Skip to content

script: Feature Flag WebCrypto crate - #47198

Merged
jschwe merged 4 commits into
servo:mainfrom
Narfinger:webcrypto-flag
Aug 17, 2026
Merged

jschwe merged 4 commits into
servo:mainfrom
Narfinger:webcrypto-flag

Conversation

@Narfinger

@Narfinger Narfinger commented Aug 12, 2026 •

Copy link
Copy Markdown
Contributor

Feature flagging WebCrypto. This saves us 2MB in production-stripped for embedders who do not need this functionality.

Testing: Compilation is the test for default feature. Manually tested with feature disabled.
Part of #47183

@Narfinger
Narfinger marked this pull request as ready for review August 12, 2026 13:55
@Narfinger
Narfinger requested a review from gterzian as a code owner August 12, 2026 13:55
@servo-highfive servo-highfive added the S-awaiting-review There is new code that needs to be reviewed. label Aug 12, 2026
@servo-highfive servo-highfive added S-awaiting-review There is new code that needs to be reviewed. and removed S-awaiting-review There is new code that needs to be reviewed. labels Aug 12, 2026
@servo-highfive servo-highfive added the S-needs-rebase There are merge conflict errors. label Aug 12, 2026

@jschwe jschwe left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of guarding crypto, could we guard just Crypto.subtle? get randome values and randomUUID are probably quite a lot more common, and would still leave us with all the binary size win, since they don't depnd on any of the crates.

@TimvdLippe

Copy link
Copy Markdown
Contributor

After WebGPU, I think WebCrypto is a great next candidate to extract out of script. That should hopefully make feature gating also easier.

@Narfinger

Copy link
Copy Markdown
Contributor Author

Instead of guarding crypto, could we guard just Crypto.subtle? get randome values and randomUUID are probably quite a lot more common, and would still leave us with all the binary size win, since they don't depnd on any of the crates.

Crypto and CryptoKey depend on subtlecrypto heavily. It would just lead to more feature flags everywhere.

@servo-highfive servo-highfive added S-needs-rebase There are merge conflict errors. and removed S-needs-rebase There are merge conflict errors. labels Aug 13, 2026
Signed-off-by: Narfinger <Narfinger@users.noreply.github.com>
Signed-off-by: Narfinger <Narfinger@users.noreply.github.com>
@servo-highfive servo-highfive removed the S-needs-rebase There are merge conflict errors. label Aug 13, 2026
Comment thread components/script/dom/window/window.rs Resolved
Signed-off-by: Narfinger <Narfinger@users.noreply.github.com>
Signed-off-by: Narfinger <Narfinger@users.noreply.github.com>
@servo-highfive servo-highfive removed the S-awaiting-review There is new code that needs to be reviewed. label Aug 17, 2026
@Narfinger
Narfinger requested a review from jschwe August 17, 2026 09:34
@jschwe
jschwe added this pull request to the merge queue Aug 17, 2026
@servo-highfive servo-highfive added the S-awaiting-merge The PR is in the process of compiling and running tests on the automated CI. label Aug 17, 2026
Merged via the queue into servo:main with commit 723dd93 Aug 17, 2026
33 checks passed
@servo-highfive servo-highfive removed the S-awaiting-merge The PR is in the process of compiling and running tests on the automated CI. label Aug 17, 2026
@Narfinger
Narfinger deleted the webcrypto-flag branch August 18, 2026 07:14
jschwe pushed a commit to openharmony-rs/servo-ohos that referenced this pull request Aug 31, 2026
…ndexeddb

Add an OpenHarmony RDB backend behind the `ohos-rdb-backend` feature: OHOS
builds can use the system database through ohos-rdb-sys C FFI instead of the
bundled sqlite3. The sqlite modules keep their upstream logic; the RDB
implementation lives in new files with its own copy of the schema and CRUD, a
deliberate duplication that keeps rebases trivial, wired in through a small set
of cfg hooks. The FFI wrappers free their native handles on drop. A cursor
borrows its transaction, so the compiler rejects reads after commit. Strings
with interior NUL bytes are stored as blobs and read back unchanged.

Backend selection is a feature choice rather than a target choice, so a plain
`./mach build --ohos` keeps the upstream SQLite behaviour and no configuration
fails to compile:

  ./mach build --ohos                        # SQLite, as before
  ./mach build --ohos --features ohos-rdb-backend
                                             # both, chosen at runtime
  ./mach build --ohos --no-default-features \
    --features "base,ohos-rdb-backend,servo-allocator/use-jemalloc"
                                             # RDB only

Dropping sqlite is what makes the third configuration worth having: when
first measured (base 79e2c7a) the stripped aarch64 release
`libservoshell.so` went from 127,736,736 to 125,736,080 bytes, 1.91 MiB
less. Since the 2026-08-29 sync this no longer holds: upstream fb04f8f
(servo#43819, on-disk HTTP cache) makes rusqlite an unconditional dependency of
components/net, so the RDB-only configuration still links sqlite. The saving
needs remeasuring, and the configuration is only worth keeping if servo#43819's
sqlite use becomes optional.

With both backends compiled in, `storage_ohos_rdb_backend_enabled` (default
true) picks one per store as it is opened, so a single binary can A/B them
without reinstalling. The two backends keep disjoint on-disk stores — sqlite
files versus RDB's nested `rdb/<store>` — so flipping the pref switches to the
other backend's data, which starts out empty. That is intended for A/B
comparison; there is no migration between them.

Deliberate difference from the SQLite twin: RDB delivers the callbacks of a
batch after its commit, so the whole batch lands or errors together (SQLite
reports each operation in autocommit). Schema operations join that batch
transaction and follow the same rule. A second deliberate difference: after a
failed webstorage load on OHOS the session's final save is skipped, so a
partially loaded state cannot overwrite the store.

On a HarmonyOS phone the two legs of the pref agree exactly. One signed HAP,
`storage_ohos_rdb_backend_enabled` flipped between runs, over the ten WPT
storage tests the backend touches: 1188 subtests pass and 37 fail on each
side, test for test, and the failures are the ones tests/wpt/meta already
expects. The stores confirm the switch rather than assuming it — with the pref
on the app writes only `rdb/reg.rdb` and `rdb/webstorage.rdb`, with it off only
`reg.sqlite` and `webstorage.sqlite`.

One RDB limitation is worth recording. Opening a second client_storage registry
fails (14800001 / 14800011) about two runs in three, on DAYU200 and PLR-AL00
alike, when the process is not an application: `new_storage_threads` with no
`config_dir` falls back to `tempfile::tempdir()`, which outside an application
sandbox is `/data/local/tmp`. Neither half alone is enough — a single registry
under `/data/local/tmp` is reliable, and so are two registries inside the
application, which a phone confirmed over ten launches of the default
configuration and five of `--temporary-storage`, the latter being the same
two-registry shape that fails in a bare process. Servo always passes the
application's cache dir, so it never meets the condition;
`test_new_storage_threads_create_independent_groups`, which does, is ignored
when the RDB backend is compiled in.

Validation: Parallel twin of the sqlite backend — revalidate on every rebase even when it merges cleanly, since upstream storage changes never conflict with the RDB engine files. (1) `./mach check --ohos -- -p servo-storage --all-targets --features ohos-rdb-backend` must pass warning-free, as must the plain and the `--no-default-features --features ohos-rdb-backend` configurations; a new `AsyncOperation`/engine-trait variant fails here loudly and must be implemented in the RDB twins, not wildcarded. (2) Diff upstream changes since the previous base with `git log -p <old-base>..<new-base> -- components/storage` and mirror any sqlite-engine semantics changes into `engines/ohos_rdb.rs` / `webstorage/engines/ohos_rdb.rs` / `client_storage_ohos_rdb.rs`. (3) Host `./mach test-unit -p servo-storage` pins the sqlite reference behaviour. (4) On a device, run the same suite against both backends with `CARGO_TARGET_AARCH64_UNKNOWN_LINUX_OHOS_RUNNER=ohos-test-runner cargo nextest run -p servo-storage --target aarch64-unknown-linux-ohos [--no-default-features --features ohos-rdb-backend]` (raise nextest's slow-timeout: every test is pushed to the device), then the same-HAP A/B: build servoshell with `--features ohos-rdb-backend` (flavor and signing per the device: `harmonyos`/`hos` for a phone whose UDID is in the profile, default/OpenHarmony for the DAYU200), install it, and run `./mach test-wpt --ohos --release --pref dom_indexeddb_enabled=true --pref storage_ohos_rdb_backend_enabled=<bool> --log-wptreport <leg>.json <subset>` (the `servo_ohos` wptrunner product from the `wip-wpt-ohos` runner commit) once per pref value with `bm clean -n org.servo.servo -d` before each leg. Subset = all of `webstorage/` plus `IndexedDB/{idbobjectstore_put,idbobjectstore_get,idbobjectstore_delete,idbobjectstore_createIndex,idbindex_get,idbcursor_continue_objectstore,keypath,key_valid,transaction-abort-generator-revert,upgrade-transaction-lifecycle-committed,idbfactory_open,idbfactory_deleteDatabase}.any.{html,worker.html}` (78 tests). Compare the two wptreports subtest by subtest: no subtest may differ (crash/timeout-truncated tests are re-run in isolation on both legs), and the app data dir must contain only `*.rdb` stores after the true leg and only `*.sqlite` after the false leg (a lone `reg.rdb` from a pref-less launch is expected).
AI-assisted: Yes (agent session; implementation and on-device testing)
AI-assisted: Yes (agent session; 2026-08-29 rebase conflict resolution -- components/servo/Cargo.toml and components/storage/Cargo.toml resolved to the upstream side with only the fork's `ohos-rdb-backend` feature and optional-dependency markers re-added: upstream's `no-wgl = ["dep:mozangle", ...]` (servo#47198) is kept and `ohos-rdb-backend = ["storage/ohos-rdb-backend"]` re-added after it, and servo#43819 moved rusqlite's `bundled` feature onto the workspace dependency, so components/storage now carries only `rusqlite = { workspace = true, optional = true }`. Validation step (2): of the three upstream commits touching components/storage, only 7bea9ed (`ImmutableOrigin::ascii_serialization` now returns `Cow<'_, str>`) reaches the RDB twins, and it is a type change with no behaviour change; mirrored into `client_storage_shared.rs`, whose `create_a_storage_shelf` now hands the `Cow` to `push_text` by deref instead of the gone `as_str()`; `indexeddb/engines/shared.rs` already coerced, and no other twin site serializes an origin. fb04f8f touches only the Cargo.toml line above and 39f05ac only sqlite-side destructuring, so no semantics were mirrored into `engines/ohos_rdb.rs`, `webstorage/engines/ohos_rdb.rs` or `client_storage_ohos_rdb.rs`. Note: fb04f8f gave components/net an unconditional rusqlite dependency, so the 1.91 MiB saving quoted above for the RDB-only configuration no longer holds and needs remeasuring)
Signed-off-by: Jonathan Schwender <jschwender@posteo.net>
@delan delan mentioned this pull request Sep 7, 2026
RichardTjokroutomo pushed a commit to RichardTjokroutomo/servo that referenced this pull request Oct 6, 2026
Feature flagging WebCrypto. This saves us 2MB in production-stripped for
embedders who do not need this functionality.

Testing: Compilation is the test for default feature. Manually tested
with feature disabled.
Part of servo#47183

---------

Signed-off-by: Narfinger <Narfinger@users.noreply.github.com>
jschwe pushed a commit to openharmony-rs/servo-ohos that referenced this pull request Oct 9, 2026
…ndexeddb

Add an OpenHarmony RDB backend behind the `ohos-rdb-backend` feature: OHOS
builds can use the system database through ohos-rdb-sys C FFI instead of the
bundled sqlite3. The sqlite modules keep their upstream logic; the RDB
implementation lives in new files with its own copy of the schema and CRUD, a
deliberate duplication that keeps rebases trivial, wired in through a small set
of cfg hooks. The FFI wrappers free their native handles on drop. A cursor
borrows its transaction, so the compiler rejects reads after commit. Strings
with interior NUL bytes are stored as blobs and read back unchanged.

Backend selection is a feature choice rather than a target choice, so a plain
`./mach build --ohos` keeps the upstream SQLite behaviour and no configuration
fails to compile:

  ./mach build --ohos                        # SQLite, as before
  ./mach build --ohos --features ohos-rdb-backend
                                             # both, chosen at runtime
  ./mach build --ohos --no-default-features \
    --features "base,ohos-rdb-backend,servo-allocator/use-jemalloc"
                                             # RDB only

Dropping sqlite is what makes the third configuration worth having: when
first measured (base 79e2c7a) the stripped aarch64 release
`libservoshell.so` went from 127,736,736 to 125,736,080 bytes, 1.91 MiB
less. Since the 2026-08-29 sync this no longer holds: upstream fb04f8f
(servo#43819, on-disk HTTP cache) makes rusqlite an unconditional dependency of
components/net, so the RDB-only configuration still links sqlite. The saving
needs remeasuring, and the configuration is only worth keeping if servo#43819's
sqlite use becomes optional.

With both backends compiled in, `storage_ohos_rdb_backend_enabled` (default
true) picks one per store as it is opened, so a single binary can A/B them
without reinstalling. The two backends keep disjoint on-disk stores — sqlite
files versus RDB's nested `rdb/<store>` — so flipping the pref switches to the
other backend's data, which starts out empty. That is intended for A/B
comparison; there is no migration between them.

Deliberate difference from the SQLite twin: RDB delivers the callbacks of a
batch after its commit, so the whole batch lands or errors together (SQLite
reports each operation in autocommit). Schema operations join that batch
transaction and follow the same rule. A second deliberate difference: after a
failed webstorage load on OHOS the session's final save is skipped, so a
partially loaded state cannot overwrite the store.

On a HarmonyOS phone the two legs of the pref agree exactly. One signed HAP,
`storage_ohos_rdb_backend_enabled` flipped between runs, over the ten WPT
storage tests the backend touches: 1188 subtests pass and 37 fail on each
side, test for test, and the failures are the ones tests/wpt/meta already
expects. The stores confirm the switch rather than assuming it — with the pref
on the app writes only `rdb/reg.rdb` and `rdb/webstorage.rdb`, with it off only
`reg.sqlite` and `webstorage.sqlite`.

One RDB limitation is worth recording. Opening a second client_storage registry
fails (14800001 / 14800011) about two runs in three, on DAYU200 and PLR-AL00
alike, when the process is not an application: `new_storage_threads` with no
`config_dir` falls back to `tempfile::tempdir()`, which outside an application
sandbox is `/data/local/tmp`. Neither half alone is enough — a single registry
under `/data/local/tmp` is reliable, and so are two registries inside the
application, which a phone confirmed over ten launches of the default
configuration and five of `--temporary-storage`, the latter being the same
two-registry shape that fails in a bare process. Servo always passes the
application's cache dir, so it never meets the condition;
`test_new_storage_threads_create_independent_groups`, which does, is ignored
when the RDB backend is compiled in.

The RDB copies of the IndexedDB and client-storage schemas are checked
against the SQLite ones by `tests/schema_parity.rs`, a host test that builds
both in an in-memory SQLite database and compares tables, columns, keys,
foreign keys and indexes, so an upstream schema change that is not mirrored
fails on the host instead of diverging on devices. The webstorage `data`
tables differ on purpose and are not compared.

Validation: Parallel twin of the sqlite backend — revalidate on every rebase even when it merges cleanly, since upstream storage changes never conflict with the RDB engine files. (1) `./mach check --ohos -- -p servo-storage --all-targets --features ohos-rdb-backend` must pass warning-free, as must the plain and the `--no-default-features --features ohos-rdb-backend` configurations; a new `AsyncOperation`/engine-trait variant fails here loudly and must be implemented in the RDB twins, not wildcarded. (2) Diff upstream changes since the previous base with `git log -p <old-base>..<new-base> -- components/storage` and mirror any sqlite-engine semantics changes into `engines/ohos_rdb.rs` / `webstorage/engines/ohos_rdb.rs` / `client_storage_ohos_rdb.rs`. (3) Host `./mach test-unit -p servo-storage` pins the sqlite reference behaviour. (4) On a device, run the same suite against both backends with `CARGO_TARGET_AARCH64_UNKNOWN_LINUX_OHOS_RUNNER=ohos-test-runner cargo nextest run -p servo-storage --target aarch64-unknown-linux-ohos [--no-default-features --features ohos-rdb-backend]` (raise nextest's slow-timeout: every test is pushed to the device), then the same-HAP A/B: build servoshell with `--features ohos-rdb-backend` (flavor and signing per the device: `harmonyos`/`hos` for a phone whose UDID is in the profile, default/OpenHarmony for the DAYU200), install it, and run `./mach test-wpt --ohos --release --no-headless --ohos-device <serial> --pref dom_indexeddb_enabled=true --pref storage_ohos_rdb_backend_enabled=<bool> --log-wptreport <leg>.json <subset>` (the `servo_ohos` wptrunner product of the `wpt-ohos` runner branch, cherry-picked onto the stack) once per pref value with `bm clean -n org.servo.servo -d` before each leg. Subset = all of `webstorage/` plus `IndexedDB/{idbobjectstore_put,idbobjectstore_get,idbobjectstore_delete,idbobjectstore_createIndex,idbindex_get,idbcursor_continue_objectstore,keypath,key_valid,transaction-abort-generator-revert,upgrade-transaction-lifecycle-committed,idbfactory_open,idbfactory_deleteDatabase}.any.{html,worker.html}` (78 tests). Compare the two wptreports subtest by subtest: no subtest may differ (crash/timeout-truncated tests are re-run in isolation on both legs), and the app data dir must contain only `*.rdb` stores after the true leg and only `*.sqlite` after the false leg (a lone `reg.rdb` from the runner's pref-less first launch and the HTTP cache's `cache.sqlite3` are expected). (5) Host `cargo nextest run -p servo-storage schema_parity` must pass; it fails when an upstream schema change is not mirrored into `indexeddb/engines/shared.rs` or `client_storage_shared.rs`. `./mach clippy --locked -- -- --deny warnings` must pass.
AI-assisted: Yes (agent session; implementation and on-device testing)
AI-assisted: Yes (agent session; 2026-08-29 rebase conflict resolution -- components/servo/Cargo.toml and components/storage/Cargo.toml resolved to the upstream side with only the fork's `ohos-rdb-backend` feature and optional-dependency markers re-added: upstream's `no-wgl = ["dep:mozangle", ...]` (servo#47198) is kept and `ohos-rdb-backend = ["storage/ohos-rdb-backend"]` re-added after it, and servo#43819 moved rusqlite's `bundled` feature onto the workspace dependency, so components/storage now carries only `rusqlite = { workspace = true, optional = true }`. Validation step (2): of the three upstream commits touching components/storage, only 7bea9ed (`ImmutableOrigin::ascii_serialization` now returns `Cow<'_, str>`) reaches the RDB twins, and it is a type change with no behaviour change; mirrored into `client_storage_shared.rs`, whose `create_a_storage_shelf` now hands the `Cow` to `push_text` by deref instead of the gone `as_str()`; `indexeddb/engines/shared.rs` already coerced, and no other twin site serializes an origin. fb04f8f touches only the Cargo.toml line above and 39f05ac only sqlite-side destructuring, so no semantics were mirrored into `engines/ohos_rdb.rs`, `webstorage/engines/ohos_rdb.rs` or `client_storage_ohos_rdb.rs`. Note: fb04f8f gave components/net an unconditional rusqlite dependency, so the 1.91 MiB saving quoted above for the RDB-only configuration no longer holds and needs remeasuring)
AI-assisted: Yes (agent session; 2026-10-08 sync -- conflict resolution in Cargo.toml, components/servo/Cargo.toml and components/storage/Cargo.toml: upstream's 0.7.0 versions with the fork's `default-features = false` re-added, upstream's new `multiprocess` default kept in servo's `default` and servoshell's `base` group, the fork's `[lints.rust]` table dropped for upstream's `[lints] workspace = true`; mirrored servo#47936 (index names unique per object store) into the RDB schema copy, adapted the tests to servo#48037's `GenericCallback::new`, added tests/schema_parity.rs and Validation step (5), updated step (4) for the current WPT runner, folded the clippy `duplicate_mod` squash commit; Cargo.lock regenerated from upstream's so that `cargo metadata --locked` passes at this commit)
Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
jschwe pushed a commit to openharmony-rs/servo-ohos that referenced this pull request Oct 9, 2026
…ndexeddb

Add an OpenHarmony RDB backend behind the `ohos-rdb-backend` feature: OHOS
builds can use the system database through ohos-rdb-sys C FFI instead of the
bundled sqlite3. The sqlite modules keep their upstream logic; the RDB
implementation lives in new files with its own copy of the schema and CRUD, a
deliberate duplication that keeps rebases trivial, wired in through a small set
of cfg hooks. The FFI wrappers free their native handles on drop. A cursor
borrows its transaction, so the compiler rejects reads after commit. Strings
with interior NUL bytes are stored as blobs and read back unchanged.

Backend selection is a feature choice rather than a target choice, so a plain
`./mach build --ohos` keeps the upstream SQLite behaviour and no configuration
fails to compile:

  ./mach build --ohos                        # SQLite, as before
  ./mach build --ohos --features ohos-rdb-backend
                                             # both, chosen at runtime
  ./mach build --ohos --no-default-features \
    --features "base,ohos-rdb-backend,servo-allocator/use-jemalloc"
                                             # RDB only

Dropping sqlite is what makes the third configuration worth having: when
first measured (base 79e2c7a) the stripped aarch64 release
`libservoshell.so` went from 127,736,736 to 125,736,080 bytes, 1.91 MiB
less. Since the 2026-08-29 sync this no longer holds: upstream fb04f8f
(servo#43819, on-disk HTTP cache) makes rusqlite an unconditional dependency of
components/net, so the RDB-only configuration still links sqlite. The saving
needs remeasuring, and the configuration is only worth keeping if servo#43819's
sqlite use becomes optional.

With both backends compiled in, `storage_ohos_rdb_backend_enabled` (default
true) picks one per store as it is opened, so a single binary can A/B them
without reinstalling. The two backends keep disjoint on-disk stores — sqlite
files versus RDB's nested `rdb/<store>` — so flipping the pref switches to the
other backend's data, which starts out empty. That is intended for A/B
comparison; there is no migration between them.

Deliberate difference from the SQLite twin: RDB delivers the callbacks of a
batch after its commit, so the whole batch lands or errors together (SQLite
reports each operation in autocommit). Schema operations join that batch
transaction and follow the same rule. A second deliberate difference: after a
failed webstorage load on OHOS the session's final save is skipped, so a
partially loaded state cannot overwrite the store.

On a HarmonyOS phone the two legs of the pref agree exactly. One signed HAP,
`storage_ohos_rdb_backend_enabled` flipped between runs, over the ten WPT
storage tests the backend touches: 1188 subtests pass and 37 fail on each
side, test for test, and the failures are the ones tests/wpt/meta already
expects. The stores confirm the switch rather than assuming it — with the pref
on the app writes only `rdb/reg.rdb` and `rdb/webstorage.rdb`, with it off only
`reg.sqlite` and `webstorage.sqlite`.

One RDB limitation is worth recording. Opening a second client_storage registry
fails (14800001 / 14800011) about two runs in three, on DAYU200 and PLR-AL00
alike, when the process is not an application: `new_storage_threads` with no
`config_dir` falls back to `tempfile::tempdir()`, which outside an application
sandbox is `/data/local/tmp`. Neither half alone is enough — a single registry
under `/data/local/tmp` is reliable, and so are two registries inside the
application, which a phone confirmed over ten launches of the default
configuration and five of `--temporary-storage`, the latter being the same
two-registry shape that fails in a bare process. Servo always passes the
application's cache dir, so it never meets the condition;
`test_new_storage_threads_create_independent_groups`, which does, is ignored
when the RDB backend is compiled in.

The RDB copies of the IndexedDB and client-storage schemas are checked
against the SQLite ones by `tests/schema_parity.rs`, a host test that builds
both in an in-memory SQLite database and compares tables, columns, keys,
foreign keys and indexes, so an upstream schema change that is not mirrored
fails on the host instead of diverging on devices. The webstorage `data`
tables differ on purpose and are not compared.

Validation: Parallel twin of the sqlite backend — revalidate on every rebase even when it merges cleanly, since upstream storage changes never conflict with the RDB engine files. (1) `./mach check --ohos -- -p servo-storage --all-targets --features ohos-rdb-backend` must pass warning-free, as must the plain and the `--no-default-features --features ohos-rdb-backend` configurations; a new `AsyncOperation`/engine-trait variant fails here loudly and must be implemented in the RDB twins, not wildcarded. (2) Diff upstream changes since the previous base with `git log -p <old-base>..<new-base> -- components/storage` and mirror any sqlite-engine semantics changes into `engines/ohos_rdb.rs` / `webstorage/engines/ohos_rdb.rs` / `client_storage_ohos_rdb.rs`. (3) Host `./mach test-unit -p servo-storage` pins the sqlite reference behaviour. (4) On a device, run the same suite against both backends with `CARGO_TARGET_AARCH64_UNKNOWN_LINUX_OHOS_RUNNER=ohos-test-runner cargo nextest run -p servo-storage --target aarch64-unknown-linux-ohos [--no-default-features --features ohos-rdb-backend]` (raise nextest's slow-timeout: every test is pushed to the device), then the same-HAP A/B: build servoshell with `--features ohos-rdb-backend` (flavor and signing per the device: `harmonyos`/`hos` for a phone whose UDID is in the profile, default/OpenHarmony for the DAYU200), install it, and run `./mach test-wpt --ohos --release --no-headless --ohos-device <serial> --pref dom_indexeddb_enabled=true --pref storage_ohos_rdb_backend_enabled=<bool> --log-wptreport <leg>.json <subset>` (the `servo_ohos` wptrunner product of the `wpt-ohos` runner branch, cherry-picked onto the stack) once per pref value with `bm clean -n org.servo.servo -d` before each leg. Subset = all of `webstorage/` plus `IndexedDB/{idbobjectstore_put,idbobjectstore_get,idbobjectstore_delete,idbobjectstore_createIndex,idbindex_get,idbcursor_continue_objectstore,keypath,key_valid,transaction-abort-generator-revert,upgrade-transaction-lifecycle-committed,idbfactory_open,idbfactory_deleteDatabase}.any.{html,worker.html}` (78 tests). Compare the two wptreports subtest by subtest: no subtest may differ (crash/timeout-truncated tests are re-run in isolation on both legs), and the app data dir must contain only `*.rdb` stores after the true leg and only `*.sqlite` after the false leg (a lone `reg.rdb` from the runner's pref-less first launch and the HTTP cache's `cache.sqlite3` are expected). (5) Host `cargo nextest run -p servo-storage schema_parity` must pass; it fails when an upstream schema change is not mirrored into `indexeddb/engines/shared.rs` or `client_storage_shared.rs`. `./mach clippy --locked -- -- --deny warnings` must pass.
AI-assisted: Yes (agent session; implementation and on-device testing)
AI-assisted: Yes (agent session; 2026-08-29 rebase conflict resolution -- components/servo/Cargo.toml and components/storage/Cargo.toml resolved to the upstream side with only the fork's `ohos-rdb-backend` feature and optional-dependency markers re-added: upstream's `no-wgl = ["dep:mozangle", ...]` (servo#47198) is kept and `ohos-rdb-backend = ["storage/ohos-rdb-backend"]` re-added after it, and servo#43819 moved rusqlite's `bundled` feature onto the workspace dependency, so components/storage now carries only `rusqlite = { workspace = true, optional = true }`. Validation step (2): of the three upstream commits touching components/storage, only 7bea9ed (`ImmutableOrigin::ascii_serialization` now returns `Cow<'_, str>`) reaches the RDB twins, and it is a type change with no behaviour change; mirrored into `client_storage_shared.rs`, whose `create_a_storage_shelf` now hands the `Cow` to `push_text` by deref instead of the gone `as_str()`; `indexeddb/engines/shared.rs` already coerced, and no other twin site serializes an origin. fb04f8f touches only the Cargo.toml line above and 39f05ac only sqlite-side destructuring, so no semantics were mirrored into `engines/ohos_rdb.rs`, `webstorage/engines/ohos_rdb.rs` or `client_storage_ohos_rdb.rs`. Note: fb04f8f gave components/net an unconditional rusqlite dependency, so the 1.91 MiB saving quoted above for the RDB-only configuration no longer holds and needs remeasuring)
AI-assisted: Yes (agent session; 2026-10-08 sync -- conflict resolution in Cargo.toml, components/servo/Cargo.toml and components/storage/Cargo.toml: upstream's 0.7.0 versions with the fork's `default-features = false` re-added, upstream's new `multiprocess` default kept in servo's `default` and servoshell's `base` group, the fork's `[lints.rust]` table dropped for upstream's `[lints] workspace = true`; mirrored servo#47936 (index names unique per object store) into the RDB schema copy, adapted the tests to servo#48037's `GenericCallback::new`, added tests/schema_parity.rs and Validation step (5), updated step (4) for the current WPT runner, folded the clippy `duplicate_mod` squash commit; Cargo.lock regenerated from upstream's so that `cargo metadata --locked` passes at this commit)
Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
jschwe pushed a commit to openharmony-rs/servo-ohos that referenced this pull request Oct 13, 2026
…ndexeddb

Add an OpenHarmony RDB backend behind the `ohos-rdb-backend` feature: OHOS
builds can use the system database through ohos-rdb-sys C FFI instead of the
bundled sqlite3. The sqlite modules keep their upstream logic; the RDB
implementation lives in new files with its own copy of the schema and CRUD, a
deliberate duplication that keeps rebases trivial, wired in through a small set
of cfg hooks. The FFI wrappers free their native handles on drop. A cursor
borrows its transaction, so the compiler rejects reads after commit. Strings
with interior NUL bytes are stored as blobs and read back unchanged.

Backend selection is a feature choice rather than a target choice, so a plain
`./mach build --ohos` keeps the upstream SQLite behaviour and no configuration
fails to compile:

  ./mach build --ohos                        # SQLite, as before
  ./mach build --ohos --features ohos-rdb-backend
                                             # both, chosen at runtime
  ./mach build --ohos --no-default-features \
    --features "base,ohos-rdb-backend,servo-allocator/use-jemalloc"
                                             # RDB only

Dropping sqlite is what makes the third configuration worth having: when
first measured (base 79e2c7a) the stripped aarch64 release
`libservoshell.so` went from 127,736,736 to 125,736,080 bytes, 1.91 MiB
less. Since the 2026-08-29 sync this no longer holds: upstream fb04f8f
(servo#43819, on-disk HTTP cache) makes rusqlite an unconditional dependency of
components/net, so the RDB-only configuration still links sqlite. The saving
needs remeasuring, and the configuration is only worth keeping if servo#43819's
sqlite use becomes optional.

With both backends compiled in, `storage_ohos_rdb_backend_enabled` (default
true) picks one per store as it is opened, so a single binary can A/B them
without reinstalling. The two backends keep disjoint on-disk stores — sqlite
files versus RDB's nested `rdb/<store>` — so flipping the pref switches to the
other backend's data, which starts out empty. That is intended for A/B
comparison; there is no migration between them.

Deliberate difference from the SQLite twin: RDB delivers the callbacks of a
batch after its commit, so the whole batch lands or errors together (SQLite
reports each operation in autocommit). Schema operations join that batch
transaction and follow the same rule. A second deliberate difference: after a
failed webstorage load on OHOS the session's final save is skipped, so a
partially loaded state cannot overwrite the store.

On a HarmonyOS phone the two legs of the pref agree exactly. One signed HAP,
`storage_ohos_rdb_backend_enabled` flipped between runs, over the ten WPT
storage tests the backend touches: 1188 subtests pass and 37 fail on each
side, test for test, and the failures are the ones tests/wpt/meta already
expects. The stores confirm the switch rather than assuming it — with the pref
on the app writes only `rdb/reg.rdb` and `rdb/webstorage.rdb`, with it off only
`reg.sqlite` and `webstorage.sqlite`.

One RDB limitation is worth recording. Opening a second client_storage registry
fails (14800001 / 14800011) about two runs in three, on DAYU200 and PLR-AL00
alike, when the process is not an application: `new_storage_threads` with no
`config_dir` falls back to `tempfile::tempdir()`, which outside an application
sandbox is `/data/local/tmp`. Neither half alone is enough — a single registry
under `/data/local/tmp` is reliable, and so are two registries inside the
application, which a phone confirmed over ten launches of the default
configuration and five of `--temporary-storage`, the latter being the same
two-registry shape that fails in a bare process. Servo always passes the
application's cache dir, so it never meets the condition;
`test_new_storage_threads_create_independent_groups`, which does, is ignored
when the RDB backend is compiled in.

The RDB copies of the IndexedDB and client-storage schemas are checked
against the SQLite ones by `tests/schema_parity.rs`, a host test that builds
both in an in-memory SQLite database and compares tables, columns, keys,
foreign keys and indexes, so an upstream schema change that is not mirrored
fails on the host instead of diverging on devices. The webstorage `data`
tables differ on purpose and are not compared.

Validation: Parallel twin of the sqlite backend — revalidate on every rebase even when it merges cleanly, since upstream storage changes never conflict with the RDB engine files. (1) `./mach check --ohos -- -p servo-storage --all-targets --features ohos-rdb-backend` must pass warning-free, as must the plain and the `--no-default-features --features ohos-rdb-backend` configurations; a new `AsyncOperation`/engine-trait variant fails here loudly and must be implemented in the RDB twins, not wildcarded. (2) Diff upstream changes since the previous base with `git log -p <old-base>..<new-base> -- components/storage` and mirror any sqlite-engine semantics changes into `engines/ohos_rdb.rs` / `webstorage/engines/ohos_rdb.rs` / `client_storage_ohos_rdb.rs`. (3) Host `./mach test-unit -p servo-storage` pins the sqlite reference behaviour. (4) On a device, run the same suite against both backends with `CARGO_TARGET_AARCH64_UNKNOWN_LINUX_OHOS_RUNNER=ohos-test-runner cargo nextest run -p servo-storage --target aarch64-unknown-linux-ohos [--no-default-features --features ohos-rdb-backend]` (raise nextest's slow-timeout: every test is pushed to the device), then the same-HAP A/B: build servoshell with `--features ohos-rdb-backend` (flavor and signing per the device: `harmonyos`/`hos` for a phone whose UDID is in the profile, default/OpenHarmony for the DAYU200), install it, and run `./mach test-wpt --ohos --release --no-headless --ohos-device <serial> --pref dom_indexeddb_enabled=true --pref storage_ohos_rdb_backend_enabled=<bool> --log-wptreport <leg>.json <subset>` (the `servo_ohos` wptrunner product of the `wpt-ohos` runner branch, cherry-picked onto the stack) once per pref value with `bm clean -n org.servo.servo -d` before each leg. Subset = all of `webstorage/` plus `IndexedDB/{idbobjectstore_put,idbobjectstore_get,idbobjectstore_delete,idbobjectstore_createIndex,idbindex_get,idbcursor_continue_objectstore,keypath,key_valid,transaction-abort-generator-revert,upgrade-transaction-lifecycle-committed,idbfactory_open,idbfactory_deleteDatabase}.any.{html,worker.html}` (78 tests). Compare the two wptreports subtest by subtest: no subtest may differ (crash/timeout-truncated tests are re-run in isolation on both legs), and the app data dir must contain only `*.rdb` stores after the true leg and only `*.sqlite` after the false leg (a lone `reg.rdb` from the runner's pref-less first launch and the HTTP cache's `cache.sqlite3` are expected). (5) Host `cargo nextest run -p servo-storage schema_parity` must pass; it fails when an upstream schema change is not mirrored into `indexeddb/engines/shared.rs` or `client_storage_shared.rs`. `./mach clippy --locked -- -- --deny warnings` must pass.
AI-assisted: Yes (agent session; implementation and on-device testing)
AI-assisted: Yes (agent session; 2026-08-29 rebase conflict resolution -- components/servo/Cargo.toml and components/storage/Cargo.toml resolved to the upstream side with only the fork's `ohos-rdb-backend` feature and optional-dependency markers re-added: upstream's `no-wgl = ["dep:mozangle", ...]` (servo#47198) is kept and `ohos-rdb-backend = ["storage/ohos-rdb-backend"]` re-added after it, and servo#43819 moved rusqlite's `bundled` feature onto the workspace dependency, so components/storage now carries only `rusqlite = { workspace = true, optional = true }`. Validation step (2): of the three upstream commits touching components/storage, only 7bea9ed (`ImmutableOrigin::ascii_serialization` now returns `Cow<'_, str>`) reaches the RDB twins, and it is a type change with no behaviour change; mirrored into `client_storage_shared.rs`, whose `create_a_storage_shelf` now hands the `Cow` to `push_text` by deref instead of the gone `as_str()`; `indexeddb/engines/shared.rs` already coerced, and no other twin site serializes an origin. fb04f8f touches only the Cargo.toml line above and 39f05ac only sqlite-side destructuring, so no semantics were mirrored into `engines/ohos_rdb.rs`, `webstorage/engines/ohos_rdb.rs` or `client_storage_ohos_rdb.rs`. Note: fb04f8f gave components/net an unconditional rusqlite dependency, so the 1.91 MiB saving quoted above for the RDB-only configuration no longer holds and needs remeasuring)
AI-assisted: Yes (agent session; 2026-10-08 sync -- conflict resolution in Cargo.toml, components/servo/Cargo.toml and components/storage/Cargo.toml: upstream's 0.7.0 versions with the fork's `default-features = false` re-added, upstream's new `multiprocess` default kept in servo's `default` and servoshell's `base` group, the fork's `[lints.rust]` table dropped for upstream's `[lints] workspace = true`; mirrored servo#47936 (index names unique per object store) into the RDB schema copy, adapted the tests to servo#48037's `GenericCallback::new`, added tests/schema_parity.rs and Validation step (5), updated step (4) for the current WPT runner, folded the clippy `duplicate_mod` squash commit; Cargo.lock regenerated from upstream's so that `cargo metadata --locked` passes at this commit)
AI-assisted: Yes (agent session; 2026-10-13 sync -- upstream servo#47255 added the default_tooling feature group: kept it in servo's default set and servoshell's base group next to the storage backend features)
Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
jschwe pushed a commit to openharmony-rs/servo-ohos that referenced this pull request Oct 22, 2026
…ndexeddb

Add an OpenHarmony RDB backend behind the `ohos-rdb-backend` feature: OHOS
builds can use the system database through ohos-rdb-sys C FFI instead of the
bundled sqlite3. The sqlite modules keep their upstream logic; the RDB
implementation lives in new files with its own copy of the schema and CRUD, a
deliberate duplication that keeps rebases trivial, wired in through a small set
of cfg hooks. The FFI wrappers free their native handles on drop. A cursor
borrows its transaction, so the compiler rejects reads after commit. Strings
with interior NUL bytes are stored as blobs and read back unchanged.

Backend selection is a feature choice rather than a target choice, so a plain
`./mach build --ohos` keeps the upstream SQLite behaviour and no configuration
fails to compile:

  ./mach build --ohos                        # SQLite, as before
  ./mach build --ohos --features ohos-rdb-backend
                                             # both, chosen at runtime
  ./mach build --ohos --no-default-features \
    --features "base,ohos-rdb-backend"
                                             # RDB only

Dropping sqlite is what makes the third configuration worth having: when
first measured (base 79e2c7a) the stripped aarch64 release
`libservoshell.so` went from 127,736,736 to 125,736,080 bytes, 1.91 MiB
less. Since the 2026-08-29 sync this no longer holds: upstream fb04f8f
(servo#43819, on-disk HTTP cache) makes rusqlite an unconditional dependency of
components/net, so the RDB-only configuration still links sqlite. The saving
needs remeasuring, and the configuration is only worth keeping if servo#43819's
sqlite use becomes optional.

With both backends compiled in, `storage_ohos_rdb_backend_enabled` (default
true) picks one per store as it is opened, so a single binary can A/B them
without reinstalling. The two backends keep disjoint on-disk stores — sqlite
files versus RDB's nested `rdb/<store>` — so flipping the pref switches to the
other backend's data, which starts out empty. That is intended for A/B
comparison; there is no migration between them.

Deliberate difference from the SQLite twin: RDB delivers the callbacks of a
batch after its commit, so the whole batch lands or errors together (SQLite
reports each operation in autocommit). Schema operations join that batch
transaction and follow the same rule. A second deliberate difference: after a
failed webstorage load on OHOS the session's final save is skipped, so a
partially loaded state cannot overwrite the store.

On a HarmonyOS phone the two legs of the pref agree exactly. One signed HAP,
`storage_ohos_rdb_backend_enabled` flipped between runs, over the ten WPT
storage tests the backend touches: 1188 subtests pass and 37 fail on each
side, test for test, and the failures are the ones tests/wpt/meta already
expects. The stores confirm the switch rather than assuming it — with the pref
on the app writes only `rdb/reg.rdb` and `rdb/webstorage.rdb`, with it off only
`reg.sqlite` and `webstorage.sqlite`.

One RDB limitation is worth recording. Opening a second client_storage registry
fails (14800001 / 14800011) about two runs in three, on DAYU200 and PLR-AL00
alike, when the process is not an application: `new_storage_threads` with no
`config_dir` falls back to `tempfile::tempdir()`, which outside an application
sandbox is `/data/local/tmp`. Neither half alone is enough — a single registry
under `/data/local/tmp` is reliable, and so are two registries inside the
application, which a phone confirmed over ten launches of the default
configuration and five of `--temporary-storage`, the latter being the same
two-registry shape that fails in a bare process. Servo always passes the
application's cache dir, so it never meets the condition;
`test_new_storage_threads_create_independent_groups`, which does, is ignored
when the RDB backend is compiled in.

The RDB copies of the IndexedDB and client-storage schemas are checked
against the SQLite ones by `tests/schema_parity.rs`, a host test that builds
both in an in-memory SQLite database and compares tables, columns, keys,
foreign keys and indexes, so an upstream schema change that is not mirrored
fails on the host instead of diverging on devices. The webstorage `data`
tables differ on purpose and are not compared.

Validation: Parallel twin of the sqlite backend — revalidate on every rebase even when it merges cleanly, since upstream storage changes never conflict with the RDB engine files. (1) `./mach check --ohos -- -p servo-storage --all-targets --features ohos-rdb-backend` must pass warning-free, as must the plain and the `--no-default-features --features ohos-rdb-backend` configurations; a new `AsyncOperation`/engine-trait variant fails here loudly and must be implemented in the RDB twins, not wildcarded. (2) Diff upstream changes since the previous base with `git log -p <old-base>..<new-base> -- components/storage` and mirror any sqlite-engine semantics changes into `engines/ohos_rdb.rs` / `webstorage/engines/ohos_rdb.rs` / `client_storage_ohos_rdb.rs`. (3) Host `./mach test-unit -p servo-storage` pins the sqlite reference behaviour. (4) On a device, run the same suite against both backends with `CARGO_TARGET_AARCH64_UNKNOWN_LINUX_OHOS_RUNNER=ohos-test-runner cargo nextest run -p servo-storage --target aarch64-unknown-linux-ohos [--no-default-features --features ohos-rdb-backend]` (raise nextest's slow-timeout: every test is pushed to the device), then the same-HAP A/B: build servoshell with `--features ohos-rdb-backend` (flavor and signing per the device: `harmonyos`/`hos` for a phone whose UDID is in the profile, default/OpenHarmony for the DAYU200), install it, and run `./mach test-wpt --ohos --release --no-headless --ohos-device <serial> --pref dom_indexeddb_enabled=true --pref storage_ohos_rdb_backend_enabled=<bool> --log-wptreport <leg>.json <subset>` (the `servo_ohos` wptrunner product of the `wpt-ohos` runner branch, cherry-picked onto the stack) once per pref value with `bm clean -n org.servo.servo -d` before each leg. Subset = all of `webstorage/` plus `IndexedDB/{idbobjectstore_put,idbobjectstore_get,idbobjectstore_delete,idbobjectstore_createIndex,idbindex_get,idbcursor_continue_objectstore,keypath,key_valid,transaction-abort-generator-revert,upgrade-transaction-lifecycle-committed,idbfactory_open,idbfactory_deleteDatabase}.any.{html,worker.html}` (78 tests). Compare the two wptreports subtest by subtest: no subtest may differ (crash/timeout-truncated tests are re-run in isolation on both legs), and the app data dir must contain only `*.rdb` stores after the true leg and only `*.sqlite` after the false leg (a lone `reg.rdb` from the runner's pref-less first launch and the HTTP cache's `cache.sqlite3` are expected). (5) Host `cargo nextest run -p servo-storage schema_parity` must pass; it fails when an upstream schema change is not mirrored into `indexeddb/engines/shared.rs` or `client_storage_shared.rs`. `./mach clippy --locked -- -- --deny warnings` must pass.
AI-assisted: Yes (agent session; implementation and on-device testing)
AI-assisted: Yes (agent session; 2026-08-29 rebase conflict resolution -- components/servo/Cargo.toml and components/storage/Cargo.toml resolved to the upstream side with only the fork's `ohos-rdb-backend` feature and optional-dependency markers re-added: upstream's `no-wgl = ["dep:mozangle", ...]` (servo#47198) is kept and `ohos-rdb-backend = ["storage/ohos-rdb-backend"]` re-added after it, and servo#43819 moved rusqlite's `bundled` feature onto the workspace dependency, so components/storage now carries only `rusqlite = { workspace = true, optional = true }`. Validation step (2): of the three upstream commits touching components/storage, only 7bea9ed (`ImmutableOrigin::ascii_serialization` now returns `Cow<'_, str>`) reaches the RDB twins, and it is a type change with no behaviour change; mirrored into `client_storage_shared.rs`, whose `create_a_storage_shelf` now hands the `Cow` to `push_text` by deref instead of the gone `as_str()`; `indexeddb/engines/shared.rs` already coerced, and no other twin site serializes an origin. fb04f8f touches only the Cargo.toml line above and 39f05ac only sqlite-side destructuring, so no semantics were mirrored into `engines/ohos_rdb.rs`, `webstorage/engines/ohos_rdb.rs` or `client_storage_ohos_rdb.rs`. Note: fb04f8f gave components/net an unconditional rusqlite dependency, so the 1.91 MiB saving quoted above for the RDB-only configuration no longer holds and needs remeasuring)
AI-assisted: Yes (agent session; 2026-10-08 sync -- conflict resolution in Cargo.toml, components/servo/Cargo.toml and components/storage/Cargo.toml: upstream's 0.7.0 versions with the fork's `default-features = false` re-added, upstream's new `multiprocess` default kept in servo's `default` and servoshell's `base` group, the fork's `[lints.rust]` table dropped for upstream's `[lints] workspace = true`; mirrored servo#47936 (index names unique per object store) into the RDB schema copy, adapted the tests to servo#48037's `GenericCallback::new`, added tests/schema_parity.rs and Validation step (5), updated step (4) for the current WPT runner, folded the clippy `duplicate_mod` squash commit; Cargo.lock regenerated from upstream's so that `cargo metadata --locked` passes at this commit)
AI-assisted: Yes (agent session; 2026-10-13 sync -- upstream servo#47255 added the default_tooling feature group: kept it in servo's default set and servoshell's base group next to the storage backend features)
AI-assisted: Yes (agent session; 2026-10-22 sync -- upstream servo#48934 and servo#49065 renamed servoshell's `default_without_allocator` group to `default_base` (which mach selects with `--no-default-features` for dummy-media builds) and moved jemalloc into it: `default_base` = `base` + `sqlite-backend`, and `base` takes upstream's list including `jemalloc`, so the RDB-only command above no longer names the allocator; servo's `default` merged next to upstream's blank line)
Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
jschwe pushed a commit to openharmony-rs/servo-ohos that referenced this pull request Oct 30, 2026
…ndexeddb

Add an OpenHarmony RDB backend behind the `ohos-rdb-backend` feature: OHOS
builds can use the system database through ohos-rdb-sys C FFI instead of the
bundled sqlite3. The sqlite modules keep their upstream logic; the RDB
implementation lives in new files with its own copy of the schema and CRUD, a
deliberate duplication that keeps rebases trivial, wired in through a small set
of cfg hooks. The FFI wrappers free their native handles on drop. A cursor
borrows its transaction, so the compiler rejects reads after commit. Strings
with interior NUL bytes are stored as blobs and read back unchanged.

Backend selection is a feature choice rather than a target choice, so a plain
`./mach build --ohos` keeps the upstream SQLite behaviour and no configuration
fails to compile:

  ./mach build --ohos                        # SQLite, as before
  ./mach build --ohos --features ohos-rdb-backend
                                             # both, chosen at runtime
  ./mach build --ohos --no-default-features \
    --features "base,ohos-rdb-backend"
                                             # RDB only

Dropping sqlite is what makes the third configuration worth having: when
first measured (base 79e2c7a) the stripped aarch64 release
`libservoshell.so` went from 127,736,736 to 125,736,080 bytes, 1.91 MiB
less. Since the 2026-08-29 sync this no longer holds: upstream fb04f8f
(servo#43819, on-disk HTTP cache) makes rusqlite an unconditional dependency of
components/net, so the RDB-only configuration still links sqlite. The saving
needs remeasuring, and the configuration is only worth keeping if servo#43819's
sqlite use becomes optional.

With both backends compiled in, `storage_ohos_rdb_backend_enabled` (default
true) picks one per store as it is opened, so a single binary can A/B them
without reinstalling. The two backends keep disjoint on-disk stores — sqlite
files versus RDB's nested `rdb/<store>` — so flipping the pref switches to the
other backend's data, which starts out empty. That is intended for A/B
comparison; there is no migration between them.

Deliberate difference from the SQLite twin: RDB delivers the callbacks of a
batch after its commit, so the whole batch lands or errors together (SQLite
reports each operation in autocommit). Schema operations join that batch
transaction and follow the same rule. A second deliberate difference: after a
failed webstorage load on OHOS the session's final save is skipped, so a
partially loaded state cannot overwrite the store.

On a HarmonyOS phone the two legs of the pref agree exactly. One signed HAP,
`storage_ohos_rdb_backend_enabled` flipped between runs, over the ten WPT
storage tests the backend touches: 1188 subtests pass and 37 fail on each
side, test for test, and the failures are the ones tests/wpt/meta already
expects. The stores confirm the switch rather than assuming it — with the pref
on the app writes only `rdb/reg.rdb` and `rdb/webstorage.rdb`, with it off only
`reg.sqlite` and `webstorage.sqlite`.

One RDB limitation is worth recording. Opening a second client_storage registry
fails (14800001 / 14800011) about two runs in three, on DAYU200 and PLR-AL00
alike, when the process is not an application: `new_storage_threads` with no
`config_dir` falls back to `tempfile::tempdir()`, which outside an application
sandbox is `/data/local/tmp`. Neither half alone is enough — a single registry
under `/data/local/tmp` is reliable, and so are two registries inside the
application, which a phone confirmed over ten launches of the default
configuration and five of `--temporary-storage`, the latter being the same
two-registry shape that fails in a bare process. Servo always passes the
application's cache dir, so it never meets the condition;
`test_new_storage_threads_create_independent_groups`, which does, is ignored
when the RDB backend is compiled in.

The RDB copies of the IndexedDB and client-storage schemas are checked
against the SQLite ones by `tests/schema_parity.rs`, a host test that builds
both in an in-memory SQLite database and compares tables, columns, keys,
foreign keys and indexes, so an upstream schema change that is not mirrored
fails on the host instead of diverging on devices. The webstorage `data`
tables differ on purpose and are not compared.

Validation: Parallel twin of the sqlite backend — revalidate on every rebase even when it merges cleanly, since upstream storage changes never conflict with the RDB engine files. (1) `./mach check --ohos -- -p servo-storage --all-targets --features ohos-rdb-backend` must pass warning-free, as must the plain and the `--no-default-features --features ohos-rdb-backend` configurations; a new `AsyncOperation`/engine-trait variant fails here loudly and must be implemented in the RDB twins, not wildcarded. (2) Diff upstream changes since the previous base with `git log -p <old-base>..<new-base> -- components/storage` and mirror any sqlite-engine semantics changes into `engines/ohos_rdb.rs` / `webstorage/engines/ohos_rdb.rs` / `client_storage_ohos_rdb.rs`. (3) Host `./mach test-unit -p servo-storage` pins the sqlite reference behaviour. (4) On a device, run the same suite against both backends with `CARGO_TARGET_AARCH64_UNKNOWN_LINUX_OHOS_RUNNER=ohos-test-runner cargo nextest run -p servo-storage --target aarch64-unknown-linux-ohos [--no-default-features --features ohos-rdb-backend]` (raise nextest's slow-timeout: every test is pushed to the device), then the same-HAP A/B: build servoshell with `--features ohos-rdb-backend` (flavor and signing per the device: `harmonyos`/`hos` for a phone whose UDID is in the profile, default/OpenHarmony for the DAYU200), install it, and run `./mach test-wpt --ohos --release --no-headless --ohos-device <serial> --pref dom_indexeddb_enabled=true --pref storage_ohos_rdb_backend_enabled=<bool> --log-wptreport <leg>.json <subset>` (the `servo_ohos` wptrunner product of the `wpt-ohos` runner branch, cherry-picked onto the stack) once per pref value with `bm clean -n org.servo.servo -d` before each leg. Subset = all of `webstorage/` plus `IndexedDB/{idbobjectstore_put,idbobjectstore_get,idbobjectstore_delete,idbobjectstore_createIndex,idbindex_get,idbcursor_continue_objectstore,keypath,key_valid,transaction-abort-generator-revert,upgrade-transaction-lifecycle-committed,idbfactory_open,idbfactory_deleteDatabase}.any.{html,worker.html}` (78 tests). Compare the two wptreports subtest by subtest: no subtest may differ (crash/timeout-truncated tests are re-run in isolation on both legs), and the app data dir must contain only `*.rdb` stores after the true leg and only `*.sqlite` after the false leg (a lone `reg.rdb` from the runner's pref-less first launch and the HTTP cache's `cache.sqlite3` are expected). (5) Host `cargo nextest run -p servo-storage schema_parity` must pass; it fails when an upstream schema change is not mirrored into `indexeddb/engines/shared.rs` or `client_storage_shared.rs`. `./mach clippy --locked -- -- --deny warnings` must pass.
AI-assisted: Yes (agent session; implementation and on-device testing)
AI-assisted: Yes (agent session; 2026-08-29 rebase conflict resolution -- components/servo/Cargo.toml and components/storage/Cargo.toml resolved to the upstream side with only the fork's `ohos-rdb-backend` feature and optional-dependency markers re-added: upstream's `no-wgl = ["dep:mozangle", ...]` (servo#47198) is kept and `ohos-rdb-backend = ["storage/ohos-rdb-backend"]` re-added after it, and servo#43819 moved rusqlite's `bundled` feature onto the workspace dependency, so components/storage now carries only `rusqlite = { workspace = true, optional = true }`. Validation step (2): of the three upstream commits touching components/storage, only 7bea9ed (`ImmutableOrigin::ascii_serialization` now returns `Cow<'_, str>`) reaches the RDB twins, and it is a type change with no behaviour change; mirrored into `client_storage_shared.rs`, whose `create_a_storage_shelf` now hands the `Cow` to `push_text` by deref instead of the gone `as_str()`; `indexeddb/engines/shared.rs` already coerced, and no other twin site serializes an origin. fb04f8f touches only the Cargo.toml line above and 39f05ac only sqlite-side destructuring, so no semantics were mirrored into `engines/ohos_rdb.rs`, `webstorage/engines/ohos_rdb.rs` or `client_storage_ohos_rdb.rs`. Note: fb04f8f gave components/net an unconditional rusqlite dependency, so the 1.91 MiB saving quoted above for the RDB-only configuration no longer holds and needs remeasuring)
AI-assisted: Yes (agent session; 2026-10-08 sync -- conflict resolution in Cargo.toml, components/servo/Cargo.toml and components/storage/Cargo.toml: upstream's 0.7.0 versions with the fork's `default-features = false` re-added, upstream's new `multiprocess` default kept in servo's `default` and servoshell's `base` group, the fork's `[lints.rust]` table dropped for upstream's `[lints] workspace = true`; mirrored servo#47936 (index names unique per object store) into the RDB schema copy, adapted the tests to servo#48037's `GenericCallback::new`, added tests/schema_parity.rs and Validation step (5), updated step (4) for the current WPT runner, folded the clippy `duplicate_mod` squash commit; Cargo.lock regenerated from upstream's so that `cargo metadata --locked` passes at this commit)
AI-assisted: Yes (agent session; 2026-10-13 sync -- upstream servo#47255 added the default_tooling feature group: kept it in servo's default set and servoshell's base group next to the storage backend features)
AI-assisted: Yes (agent session; 2026-10-22 sync -- upstream servo#48934 and servo#49065 renamed servoshell's `default_without_allocator` group to `default_base` (which mach selects with `--no-default-features` for dummy-media builds) and moved jemalloc into it: `default_base` = `base` + `sqlite-backend`, and `base` takes upstream's list including `jemalloc`, so the RDB-only command above no longer names the allocator; servo's `default` merged next to upstream's blank line)
Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants