From c777f6f9ed154ee54f01dc52fd912dc1fb97e4ea Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Thu, 30 Apr 2020 08:16:26 +0000 Subject: [PATCH] Bug 1634257 - expose wipe_all to mozIExtensionStorageArea. r=lina Differential Revision: https://phabricator.services.mozilla.com/D73191 --- .cargo/config.in | 2 +- Cargo.lock | 14 +++--- services/sync/golden_gate/Cargo.toml | 4 +- .../rust/webext-storage/.cargo-checksum.json | 2 +- third_party/rust/webext-storage/src/api.rs | 48 +++++++++++++++++++ third_party/rust/webext-storage/src/lib.rs | 2 + third_party/rust/webext-storage/src/store.rs | 9 ++++ .../storage/mozIExtensionStorageArea.idl | 5 ++ .../storage/webext_storage_bridge/Cargo.toml | 2 +- .../storage/webext_storage_bridge/src/area.rs | 12 ++++- .../storage/webext_storage_bridge/src/op.rs | 7 +++ .../test/xpcshell/test_StorageSyncService.js | 24 ++++++++++ toolkit/library/rust/shared/Cargo.toml | 2 +- 13 files changed, 119 insertions(+), 14 deletions(-) diff --git a/.cargo/config.in b/.cargo/config.in index 285690328adc..9de42af90700 100644 --- a/.cargo/config.in +++ b/.cargo/config.in @@ -20,7 +20,7 @@ tag = "v0.2.4" [source."https://github.com/mozilla/application-services"] git = "https://github.com/mozilla/application-services" replace-with = "vendored-sources" -rev = "775fc33603d722b75ff3579a5f2c52dbd4715b45" +rev = "d8a50bb0b010ab7289fe2d2864cc3eb9687a52e7" [source."https://github.com/mozilla-spidermonkey/jsparagus"] git = "https://github.com/mozilla-spidermonkey/jsparagus" diff --git a/Cargo.lock b/Cargo.lock index 14db614862ea..0bd76335c122 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1242,7 +1242,7 @@ checksum = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3" [[package]] name = "error-support" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=775fc33603d722b75ff3579a5f2c52dbd4715b45#775fc33603d722b75ff3579a5f2c52dbd4715b45" +source = "git+https://github.com/mozilla/application-services?rev=d8a50bb0b010ab7289fe2d2864cc3eb9687a52e7#d8a50bb0b010ab7289fe2d2864cc3eb9687a52e7" dependencies = [ "failure", ] @@ -2180,7 +2180,7 @@ dependencies = [ [[package]] name = "interrupt-support" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=775fc33603d722b75ff3579a5f2c52dbd4715b45#775fc33603d722b75ff3579a5f2c52dbd4715b45" +source = "git+https://github.com/mozilla/application-services?rev=d8a50bb0b010ab7289fe2d2864cc3eb9687a52e7#d8a50bb0b010ab7289fe2d2864cc3eb9687a52e7" [[package]] name = "intl-memoizer" @@ -3115,7 +3115,7 @@ dependencies = [ [[package]] name = "nss_build_common" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=775fc33603d722b75ff3579a5f2c52dbd4715b45#775fc33603d722b75ff3579a5f2c52dbd4715b45" +source = "git+https://github.com/mozilla/application-services?rev=d8a50bb0b010ab7289fe2d2864cc3eb9687a52e7#d8a50bb0b010ab7289fe2d2864cc3eb9687a52e7" [[package]] name = "nsstring" @@ -4245,7 +4245,7 @@ dependencies = [ [[package]] name = "sql-support" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=775fc33603d722b75ff3579a5f2c52dbd4715b45#775fc33603d722b75ff3579a5f2c52dbd4715b45" +source = "git+https://github.com/mozilla/application-services?rev=d8a50bb0b010ab7289fe2d2864cc3eb9687a52e7#d8a50bb0b010ab7289fe2d2864cc3eb9687a52e7" dependencies = [ "ffi-support", "interrupt-support", @@ -4442,7 +4442,7 @@ dependencies = [ [[package]] name = "sync-guid" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=775fc33603d722b75ff3579a5f2c52dbd4715b45#775fc33603d722b75ff3579a5f2c52dbd4715b45" +source = "git+https://github.com/mozilla/application-services?rev=d8a50bb0b010ab7289fe2d2864cc3eb9687a52e7#d8a50bb0b010ab7289fe2d2864cc3eb9687a52e7" dependencies = [ "base64 0.12.0", "rand", @@ -4453,7 +4453,7 @@ dependencies = [ [[package]] name = "sync15-traits" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=775fc33603d722b75ff3579a5f2c52dbd4715b45#775fc33603d722b75ff3579a5f2c52dbd4715b45" +source = "git+https://github.com/mozilla/application-services?rev=d8a50bb0b010ab7289fe2d2864cc3eb9687a52e7#d8a50bb0b010ab7289fe2d2864cc3eb9687a52e7" dependencies = [ "failure", "ffi-support", @@ -5168,7 +5168,7 @@ dependencies = [ [[package]] name = "webext-storage" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=775fc33603d722b75ff3579a5f2c52dbd4715b45#775fc33603d722b75ff3579a5f2c52dbd4715b45" +source = "git+https://github.com/mozilla/application-services?rev=d8a50bb0b010ab7289fe2d2864cc3eb9687a52e7#d8a50bb0b010ab7289fe2d2864cc3eb9687a52e7" dependencies = [ "error-support", "failure", diff --git a/services/sync/golden_gate/Cargo.toml b/services/sync/golden_gate/Cargo.toml index 4e593a5a6295..100ac16fed4c 100644 --- a/services/sync/golden_gate/Cargo.toml +++ b/services/sync/golden_gate/Cargo.toml @@ -8,13 +8,13 @@ edition = "2018" [dependencies] atomic_refcell = "0.1" cstr = "0.1" -interrupt-support = { git = "https://github.com/mozilla/application-services", rev = "775fc33603d722b75ff3579a5f2c52dbd4715b45" } +interrupt-support = { git = "https://github.com/mozilla/application-services", rev = "d8a50bb0b010ab7289fe2d2864cc3eb9687a52e7" } log = "0.4" moz_task = { path = "../../../xpcom/rust/moz_task" } nserror = { path = "../../../xpcom/rust/nserror" } nsstring = { path = "../../../xpcom/rust/nsstring" } storage_variant = { path = "../../../storage/variant" } -sync15-traits = { git = "https://github.com/mozilla/application-services", rev = "775fc33603d722b75ff3579a5f2c52dbd4715b45" } +sync15-traits = { git = "https://github.com/mozilla/application-services", rev = "d8a50bb0b010ab7289fe2d2864cc3eb9687a52e7" } xpcom = { path = "../../../xpcom/rust/xpcom" } [dependencies.thin-vec] diff --git a/third_party/rust/webext-storage/.cargo-checksum.json b/third_party/rust/webext-storage/.cargo-checksum.json index 3065be7a93b9..cc8c37b8629f 100644 --- a/third_party/rust/webext-storage/.cargo-checksum.json +++ b/third_party/rust/webext-storage/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"23ed53b7db21b1015cbb1deafe950f80068691dfe7f2256e9f9237a94910a4d8","README.md":"1fd617294339930ee1ad5172377648b268cce0216fc3971facbfe7c6839e9ab1","build.rs":"2b827a62155a3d724cdb4c198270ea467439e537403f82fa873321ac55a69a63","sql/create_schema.sql":"d50b22cb17fc5d4e2aa4d001e853bd2f67eb3ffdbb1ac29013067dceacaec80e","src/api.rs":"5413ccbf382cb13993ed31cfc62dd948b5c40404a6c513deed4238c5f8bc2bd5","src/db.rs":"8933ce788e1044aa654eb0ed177596e327057dd49001287d3fbfcf57cc6bb728","src/error.rs":"86ba215ec5a889d1ccca9dcd141e42f75914744a4803598ccf3894da4a7f7475","src/lib.rs":"14dfeb708dfa78c916d85818fc21af24cbdc05748c8d21c2f7d82a22a4f5cc14","src/schema.rs":"ac475285fb065fdf639d71e34d4e12663eb0650eefe3e151d223eb4fe3a667ef","src/store.rs":"83ab13bd6bb9fcc60705441580acd26b06030ad6599de1b306eee7541b742e89"},"package":null} \ No newline at end of file +{"files":{"Cargo.toml":"23ed53b7db21b1015cbb1deafe950f80068691dfe7f2256e9f9237a94910a4d8","README.md":"1fd617294339930ee1ad5172377648b268cce0216fc3971facbfe7c6839e9ab1","build.rs":"2b827a62155a3d724cdb4c198270ea467439e537403f82fa873321ac55a69a63","sql/create_schema.sql":"d50b22cb17fc5d4e2aa4d001e853bd2f67eb3ffdbb1ac29013067dceacaec80e","src/api.rs":"578862ecc35d1bfa0c96329702cc7ffd743d749a170d808a729f91f584b33b3d","src/db.rs":"8933ce788e1044aa654eb0ed177596e327057dd49001287d3fbfcf57cc6bb728","src/error.rs":"86ba215ec5a889d1ccca9dcd141e42f75914744a4803598ccf3894da4a7f7475","src/lib.rs":"79685854649ffd0c1664f67cd0264f86c93f49e974de217a94a005b9d9d1c39d","src/schema.rs":"ac475285fb065fdf639d71e34d4e12663eb0650eefe3e151d223eb4fe3a667ef","src/store.rs":"686003abb14ffd1ef06f8a09a74c463ffaaf8e283ae7b92809122806e7085013"},"package":null} \ No newline at end of file diff --git a/third_party/rust/webext-storage/src/api.rs b/third_party/rust/webext-storage/src/api.rs index 21424eb39d97..8117383c1109 100644 --- a/third_party/rust/webext-storage/src/api.rs +++ b/third_party/rust/webext-storage/src/api.rs @@ -253,6 +253,21 @@ pub fn clear(tx: &Transaction<'_>, ext_id: &str) -> Result { Ok(result) } +/// While this API isn't available to extensions, Firefox wants a way to wipe +/// all data for all addons but not sync the deletions. We also don't report +/// the changes caused by the deletion. +/// That means that after doing this, the next sync is likely to drag some data +/// back in - which is fine. +/// This is much like what the sync support for other components calls a "wipe", +/// so we name it similarly. +pub fn wipe_all(tx: &Transaction<'_>) -> Result<()> { + // We assume the meta table is only used by sync. + tx.execute_batch( + "DELETE FROM storage_sync_data; DELETE FROM storage_sync_mirror; DELETE FROM meta;", + )?; + Ok(()) +} + // TODO - get_bytes_in_use() #[cfg(test)] @@ -488,4 +503,37 @@ mod tests { }; Ok(()) } + + fn query_count(conn: &Connection, table: &str) -> u32 { + conn.query_row_and_then( + &format!("SELECT COUNT(*) FROM {};", table), + rusqlite::NO_PARAMS, + |row| row.get::<_, u32>(0), + ) + .expect("should work") + } + + #[test] + fn test_wipe() -> Result<()> { + use crate::db::put_meta; + + let mut db = new_mem_db(); + let tx = db.transaction()?; + set(&tx, "ext-a", json!({ "x": "y" }))?; + set(&tx, "ext-b", json!({ "y": "x" }))?; + put_meta(&tx, "meta", &"meta-meta".to_string())?; + tx.execute( + "INSERT INTO storage_sync_mirror (guid, ext_id, data) + VALUES ('guid', 'ext-a', null)", + rusqlite::NO_PARAMS, + )?; + assert_eq!(query_count(&tx, "storage_sync_data"), 2); + assert_eq!(query_count(&tx, "storage_sync_mirror"), 1); + assert_eq!(query_count(&tx, "meta"), 1); + wipe_all(&tx)?; + assert_eq!(query_count(&tx, "storage_sync_data"), 0); + assert_eq!(query_count(&tx, "storage_sync_mirror"), 0); + assert_eq!(query_count(&tx, "meta"), 0); + Ok(()) + } } diff --git a/third_party/rust/webext-storage/src/lib.rs b/third_party/rust/webext-storage/src/lib.rs index 1497600c94fc..cec6a9ff9369 100644 --- a/third_party/rust/webext-storage/src/lib.rs +++ b/third_party/rust/webext-storage/src/lib.rs @@ -22,5 +22,7 @@ pub fn delme_demo_usage() -> error::Result<()> { store.get("ext-id", json!({}))?; store.remove("ext-id", json!({}))?; store.clear("ext-id")?; + // and it might even... + store.wipe_all()?; Ok(()) } diff --git a/third_party/rust/webext-storage/src/store.rs b/third_party/rust/webext-storage/src/store.rs index e84cfc5a6c9e..0cc561d524e7 100644 --- a/third_party/rust/webext-storage/src/store.rs +++ b/third_party/rust/webext-storage/src/store.rs @@ -93,6 +93,15 @@ impl Store { Ok(result) } + /// Wipe all local data without syncing or returning any information about + /// the deletion. + pub fn wipe_all(&self) -> Result<()> { + let tx = self.db.unchecked_transaction()?; + api::wipe_all(&tx)?; + tx.commit()?; + Ok(()) + } + /// Closes the store and its database connection. See the docs for /// `StorageDb::close` for more details on when this can fail. pub fn close(self) -> result::Result<(), (Store, Error)> { diff --git a/toolkit/components/extensions/storage/mozIExtensionStorageArea.idl b/toolkit/components/extensions/storage/mozIExtensionStorageArea.idl index a2460ea87218..5fac0527033a 100644 --- a/toolkit/components/extensions/storage/mozIExtensionStorageArea.idl +++ b/toolkit/components/extensions/storage/mozIExtensionStorageArea.idl @@ -41,6 +41,11 @@ interface mozIExtensionStorageArea : nsISupports { // method will be called with all removed key-value pairs. void clear(in AUTF8String extensionId, in mozIExtensionStorageCallback callback); + + // Wipes all data from the storage area for all extensions. The deletion + // will not be synced, and no callback facility is provided for notification + // of the deletions. + void wipeAll(in mozIExtensionStorageCallback callback); }; // A configurable storage area has additional methods for setting up and tearing diff --git a/toolkit/components/extensions/storage/webext_storage_bridge/Cargo.toml b/toolkit/components/extensions/storage/webext_storage_bridge/Cargo.toml index 2a186524ef67..f28687700411 100644 --- a/toolkit/components/extensions/storage/webext_storage_bridge/Cargo.toml +++ b/toolkit/components/extensions/storage/webext_storage_bridge/Cargo.toml @@ -16,4 +16,4 @@ xpcom = { path = "../../../../../xpcom/rust/xpcom" } serde = "1" serde_json = "1" storage_variant = { path = "../../../../../storage/variant" } -webext-storage = { git = "https://github.com/mozilla/application-services", rev = "775fc33603d722b75ff3579a5f2c52dbd4715b45" } +webext-storage = { git = "https://github.com/mozilla/application-services", rev = "d8a50bb0b010ab7289fe2d2864cc3eb9687a52e7" } diff --git a/toolkit/components/extensions/storage/webext_storage_bridge/src/area.rs b/toolkit/components/extensions/storage/webext_storage_bridge/src/area.rs index f820b3ba9d59..3a04ad8ceda8 100644 --- a/toolkit/components/extensions/storage/webext_storage_bridge/src/area.rs +++ b/toolkit/components/extensions/storage/webext_storage_bridge/src/area.rs @@ -176,7 +176,7 @@ impl StorageSyncArea { callback: *const mozIExtensionStorageCallback ) ); - /// Removes all keys and values. + /// Removes all keys and values for the specified extension. fn clear(&self, ext_id: &nsACString, callback: &mozIExtensionStorageCallback) -> Result<()> { self.dispatch( StorageOp::Clear { @@ -186,6 +186,16 @@ impl StorageSyncArea { ) } + xpcom_method!( + wipe_all => WipeAll( + callback: *const mozIExtensionStorageCallback + ) + ); + /// Removes all keys and values for all extensions. + fn wipe_all(&self, callback: &mozIExtensionStorageCallback) -> Result<()> { + self.dispatch(StorageOp::WipeAll, callback) + } + xpcom_method!(teardown => Teardown(callback: *const mozIExtensionStorageCallback)); /// Tears down the storage area, closing the backing database connection. fn teardown(&self, callback: &mozIExtensionStorageCallback) -> Result<()> { diff --git a/toolkit/components/extensions/storage/webext_storage_bridge/src/op.rs b/toolkit/components/extensions/storage/webext_storage_bridge/src/op.rs index c6c13b11e5c9..4c2b9707d539 100644 --- a/toolkit/components/extensions/storage/webext_storage_bridge/src/op.rs +++ b/toolkit/components/extensions/storage/webext_storage_bridge/src/op.rs @@ -34,6 +34,8 @@ pub enum StorageOp { Remove { ext_id: String, keys: JsonValue }, /// Clear all keys and values for an extension. Clear { ext_id: String }, + /// Clear all keys and values for all extensions. + WipeAll, } impl StorageOp { @@ -45,6 +47,7 @@ impl StorageOp { StorageOp::Set { .. } => "webext_storage::set", StorageOp::Remove { .. } => "webext_storage::remove", StorageOp::Clear { .. } => "webext_storage::clear", + StorageOp::WipeAll => "webext_storage::wipe_all", } } } @@ -143,6 +146,10 @@ impl StorageTask { StorageOp::Clear { ext_id } => { StorageResult::with_changes(self.store()?.get()?.clear(&ext_id)?) } + StorageOp::WipeAll => { + self.store()?.get()?.wipe_all()?; + Ok(StorageResult::default()) + } }?) } } diff --git a/toolkit/components/extensions/test/xpcshell/test_StorageSyncService.js b/toolkit/components/extensions/test/xpcshell/test_StorageSyncService.js index 22e135f53291..bae2fdb1ad8c 100644 --- a/toolkit/components/extensions/test/xpcshell/test_StorageSyncService.js +++ b/toolkit/components/extensions/test/xpcshell/test_StorageSyncService.js @@ -89,5 +89,29 @@ add_task( "`get` without a key should return all values" ); } + + { + await promisify( + service.set, + "ext-2", + JSON.stringify({ + hi: "hola! 👋", + }) + ); + await promisify(service.clear, "ext-1"); + let { value: allValues } = await promisify(service.get, "ext-1", "null"); + deepEqual(allValues, {}, "clear removed ext-1"); + + let { value: allValues2 } = await promisify(service.get, "ext-2", "null"); + deepEqual(allValues2, { hi: "hola! 👋" }, "clear didn't remove ext-2"); + + await promisify(service.wipeAll); + + deepEqual( + (await promisify(service.get, "ext-2", "null")).value, + {}, + "wipeAll removed ext-2" + ); + } } ); diff --git a/toolkit/library/rust/shared/Cargo.toml b/toolkit/library/rust/shared/Cargo.toml index 5f43b2a006fd..4c6703710336 100644 --- a/toolkit/library/rust/shared/Cargo.toml +++ b/toolkit/library/rust/shared/Cargo.toml @@ -54,7 +54,7 @@ unic-langid = { version = "0.8", features = ["likelysubtags"] } unic-langid-ffi = { path = "../../../../intl/locale/rust/unic-langid-ffi" } fluent-langneg = { version = "0.12.1", features = ["cldr"] } fluent-langneg-ffi = { path = "../../../../intl/locale/rust/fluent-langneg-ffi" } -webext-storage = { git = "https://github.com/mozilla/application-services", rev = "775fc33603d722b75ff3579a5f2c52dbd4715b45", optional = true } +webext-storage = { git = "https://github.com/mozilla/application-services", rev = "d8a50bb0b010ab7289fe2d2864cc3eb9687a52e7", optional = true } golden_gate = { path = "../../../../services/sync/golden_gate", optional = true } # Note: `modern_sqlite` means rusqlite's bindings file be for a sqlite with