forked from mirrors/gecko-dev
Bug 1666534 - [devtools] Listen to extension storages via a server side watcher. r=devtools-reviewers,nchevobbe,jdescottes
This was the last resource type requiring to keep the old storage actor as-is. This will help drastically simplify it and move storage type code into each Resource Watcher class. Differential Revision: https://phabricator.services.mozilla.com/D166661
This commit is contained in:
parent
7539d4e3f4
commit
94e02496ef
14 changed files with 191 additions and 307 deletions
|
|
@ -315,17 +315,22 @@ class StorageUI {
|
|||
this._onResourceListAvailable = this._onResourceListAvailable.bind(this);
|
||||
|
||||
const { resourceCommand } = this._toolbox;
|
||||
|
||||
this._listenedResourceTypes = [
|
||||
// The first item in this list will be the first selected storage item
|
||||
// Tests assume Cookie -- moving cookie will break tests
|
||||
resourceCommand.TYPES.COOKIE,
|
||||
resourceCommand.TYPES.CACHE_STORAGE,
|
||||
resourceCommand.TYPES.INDEXED_DB,
|
||||
resourceCommand.TYPES.LOCAL_STORAGE,
|
||||
resourceCommand.TYPES.SESSION_STORAGE,
|
||||
];
|
||||
// EXTENSION_STORAGE is only relevant when debugging web extensions
|
||||
if (this._commands.descriptorFront.isWebExtensionDescriptor) {
|
||||
this._listenedResourceTypes.push(resourceCommand.TYPES.EXTENSION_STORAGE);
|
||||
}
|
||||
await this._toolbox.resourceCommand.watchResources(
|
||||
[
|
||||
// The first item in this list will be the first selected storage item
|
||||
// Tests assume Cookie -- moving cookie will break tests
|
||||
resourceCommand.TYPES.COOKIE,
|
||||
resourceCommand.TYPES.CACHE_STORAGE,
|
||||
resourceCommand.TYPES.EXTENSION_STORAGE,
|
||||
resourceCommand.TYPES.INDEXED_DB,
|
||||
resourceCommand.TYPES.LOCAL_STORAGE,
|
||||
resourceCommand.TYPES.SESSION_STORAGE,
|
||||
],
|
||||
this._listenedResourceTypes,
|
||||
{
|
||||
onAvailable: this._onResourceListAvailable,
|
||||
}
|
||||
|
|
@ -443,19 +448,9 @@ class StorageUI {
|
|||
this._destroyed = true;
|
||||
|
||||
const { resourceCommand } = this._toolbox;
|
||||
resourceCommand.unwatchResources(
|
||||
[
|
||||
resourceCommand.TYPES.COOKIE,
|
||||
resourceCommand.TYPES.CACHE_STORAGE,
|
||||
resourceCommand.TYPES.EXTENSION_STORAGE,
|
||||
resourceCommand.TYPES.INDEXED_DB,
|
||||
resourceCommand.TYPES.LOCAL_STORAGE,
|
||||
resourceCommand.TYPES.SESSION_STORAGE,
|
||||
],
|
||||
{
|
||||
onAvailable: this._onResourceListAvailable,
|
||||
}
|
||||
);
|
||||
resourceCommand.unwatchResources(this._listenedResourceTypes, {
|
||||
onAvailable: this._onResourceListAvailable,
|
||||
});
|
||||
|
||||
this.table.off(TableWidget.EVENTS.ROW_SELECTED, this.updateObjectSidebar);
|
||||
this.table.off(TableWidget.EVENTS.SCROLL_END, this.loadMoreItems);
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ const TYPES = {
|
|||
// storage types
|
||||
CACHE_STORAGE: "Cache",
|
||||
COOKIE: "cookies",
|
||||
EXTENSION_STORAGE: "extension-storage",
|
||||
INDEXED_DB: "indexed-db",
|
||||
LOCAL_STORAGE: "local-storage",
|
||||
SESSION_STORAGE: "session-storage",
|
||||
|
|
@ -160,6 +161,9 @@ const ParentProcessResources = augmentResourceDictionary({
|
|||
[TYPES.COOKIE]: {
|
||||
path: "devtools/server/actors/resources/storage-cookie",
|
||||
},
|
||||
[TYPES.EXTENSION_STORAGE]: {
|
||||
path: "devtools/server/actors/resources/storage-extension",
|
||||
},
|
||||
[TYPES.INDEXED_DB]: {
|
||||
path: "devtools/server/actors/resources/storage-indexed-db",
|
||||
},
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ DevToolsModules(
|
|||
"sources.js",
|
||||
"storage-cache.js",
|
||||
"storage-cookie.js",
|
||||
"storage-extension.js",
|
||||
"storage-indexed-db.js",
|
||||
"storage-local-storage.js",
|
||||
"storage-session-storage.js",
|
||||
|
|
|
|||
27
devtools/server/actors/resources/storage-extension.js
Normal file
27
devtools/server/actors/resources/storage-extension.js
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {
|
||||
TYPES: { EXTENSION_STORAGE },
|
||||
} = require("resource://devtools/server/actors/resources/index.js");
|
||||
|
||||
const ParentProcessStorage = require("resource://devtools/server/actors/resources/utils/parent-process-storage.js");
|
||||
|
||||
class ExtensionStorageWatcher extends ParentProcessStorage {
|
||||
constructor() {
|
||||
super("extensionStorage", EXTENSION_STORAGE);
|
||||
}
|
||||
async watch(watcherActor, { onAvailable }) {
|
||||
if (watcherActor.sessionContext.type != "webextension") {
|
||||
throw new Error(
|
||||
"EXTENSION_STORAGE should only be listened when debugging a webextension"
|
||||
);
|
||||
}
|
||||
return super.watch(watcherActor, { onAvailable });
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ExtensionStorageWatcher;
|
||||
|
|
@ -407,7 +407,10 @@ class StorageActorMock extends EventEmitter {
|
|||
}
|
||||
|
||||
get parentActor() {
|
||||
return { isRootActor: this.watcherActor.sessionContext.type == "all" };
|
||||
return {
|
||||
isRootActor: this.watcherActor.sessionContext.type == "all",
|
||||
addonId: this.watcherActor.sessionContext.addonId,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1488,14 +1488,6 @@ StorageActors.createActor(
|
|||
);
|
||||
|
||||
const extensionStorageHelpers = {
|
||||
unresolvedPromises: new Map(),
|
||||
// Map of addonId => onStorageChange listeners in the parent process. Each addon toolbox targets
|
||||
// a single addon, and multiple addon toolboxes could be open at the same time.
|
||||
onChangedParentListeners: new Map(),
|
||||
// Set of onStorageChange listeners in the extension child process. Each addon toolbox will create
|
||||
// a separate extensionStorage actor targeting that addon. The addonId is passed into the listener,
|
||||
// so that changes propagate only if the storage actor has a matching addonId.
|
||||
onChangedChildListeners: new Set(),
|
||||
/**
|
||||
* Editing is supported only for serializable types. Examples of unserializable
|
||||
* types include Map, Set and ArrayBuffer.
|
||||
|
|
@ -1610,172 +1602,6 @@ const extensionStorageHelpers = {
|
|||
},
|
||||
},
|
||||
},
|
||||
|
||||
// Sets the parent process message manager
|
||||
setPpmm(ppmm) {
|
||||
this.ppmm = ppmm;
|
||||
},
|
||||
|
||||
// A promise in the main process has resolved, and we need to pass the return value(s)
|
||||
// back to the child process
|
||||
backToChild(...args) {
|
||||
Services.mm.broadcastAsyncMessage(
|
||||
"debug:storage-extensionStorage-request-child",
|
||||
{
|
||||
method: "backToChild",
|
||||
args,
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
// Send a message from the main process to a listener in the child process that the
|
||||
// extension has modified storage local data
|
||||
fireStorageOnChanged({ addonId, changes }) {
|
||||
Services.mm.broadcastAsyncMessage(
|
||||
"debug:storage-extensionStorage-request-child",
|
||||
{
|
||||
addonId,
|
||||
changes,
|
||||
method: "storageOnChanged",
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
// Subscribe a listener for event notifications from the WE storage API when
|
||||
// storage local data has been changed by the extension, and keep track of the
|
||||
// listener to remove it when the debugger is being disconnected.
|
||||
subscribeOnChangedListenerInParent(addonId) {
|
||||
if (!this.onChangedParentListeners.has(addonId)) {
|
||||
const onChangedListener = changes => {
|
||||
this.fireStorageOnChanged({ addonId, changes });
|
||||
};
|
||||
ExtensionStorageIDB.addOnChangedListener(addonId, onChangedListener);
|
||||
this.onChangedParentListeners.set(addonId, onChangedListener);
|
||||
}
|
||||
},
|
||||
|
||||
// The main process does not require an extension context to select the backend
|
||||
// Bug 1542038, 1542039: Each storage area will need its own implementation, as
|
||||
// they use different storage backends.
|
||||
async setupStorageInParent(addonId) {
|
||||
const { extension } = WebExtensionPolicy.getByID(addonId);
|
||||
try {
|
||||
// Make sure the extension storage APIs have been loaded,
|
||||
// otherwise the DevTools storage panel would not be updated
|
||||
// automatically when the extension storage data is being changed
|
||||
// if the parent ext-storage.js module wasn't already loaded
|
||||
// (See Bug 1802929).
|
||||
await extension.apiManager.asyncGetAPI("storage", extension);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
const parentResult = await ExtensionStorageIDB.selectBackend({ extension });
|
||||
const result = {
|
||||
...parentResult,
|
||||
// Received as a StructuredCloneHolder, so we need to deserialize
|
||||
storagePrincipal: parentResult.storagePrincipal.deserialize(this, true),
|
||||
};
|
||||
|
||||
this.subscribeOnChangedListenerInParent(addonId);
|
||||
return this.backToChild("setupStorageInParent", result);
|
||||
},
|
||||
|
||||
onDisconnected() {
|
||||
for (const [addonId, listener] of this.onChangedParentListeners) {
|
||||
ExtensionStorageIDB.removeOnChangedListener(addonId, listener);
|
||||
}
|
||||
this.onChangedParentListeners.clear();
|
||||
},
|
||||
|
||||
// Runs in the main process. This determines what code to execute based on the message
|
||||
// received from the child process.
|
||||
async handleChildRequest(msg) {
|
||||
switch (msg.json.method) {
|
||||
case "setupStorageInParent":
|
||||
const addonId = msg.data.args[0];
|
||||
const result = await extensionStorageHelpers.setupStorageInParent(
|
||||
addonId
|
||||
);
|
||||
return result;
|
||||
default:
|
||||
console.error("ERR_DIRECTOR_PARENT_UNKNOWN_METHOD", msg.json.method);
|
||||
throw new Error("ERR_DIRECTOR_PARENT_UNKNOWN_METHOD");
|
||||
}
|
||||
},
|
||||
|
||||
// Runs in the child process. This determines what code to execute based on the message
|
||||
// received from the parent process.
|
||||
handleParentRequest(msg) {
|
||||
switch (msg.json.method) {
|
||||
case "backToChild": {
|
||||
const [func, rv] = msg.json.args;
|
||||
const resolve = this.unresolvedPromises.get(func);
|
||||
if (resolve) {
|
||||
this.unresolvedPromises.delete(func);
|
||||
resolve(rv);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "storageOnChanged": {
|
||||
const { addonId, changes } = msg.data;
|
||||
for (const listener of this.onChangedChildListeners) {
|
||||
try {
|
||||
listener({ addonId, changes });
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
// Ignore errors raised from listeners.
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
console.error("ERR_DIRECTOR_CLIENT_UNKNOWN_METHOD", msg.json.method);
|
||||
throw new Error("ERR_DIRECTOR_CLIENT_UNKNOWN_METHOD");
|
||||
}
|
||||
},
|
||||
|
||||
callParentProcessAsync(methodName, ...args) {
|
||||
const promise = new Promise(resolve => {
|
||||
this.unresolvedPromises.set(methodName, resolve);
|
||||
});
|
||||
|
||||
this.ppmm.sendAsyncMessage(
|
||||
"debug:storage-extensionStorage-request-parent",
|
||||
{
|
||||
method: methodName,
|
||||
args,
|
||||
}
|
||||
);
|
||||
|
||||
return promise;
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* E10S parent/child setup helpers
|
||||
* Add a message listener in the parent process to receive messages from the child
|
||||
* process.
|
||||
*/
|
||||
exports.setupParentProcessForExtensionStorage = function({ mm, prefix }) {
|
||||
// listen for director-script requests from the child process
|
||||
mm.addMessageListener(
|
||||
"debug:storage-extensionStorage-request-parent",
|
||||
extensionStorageHelpers.handleChildRequest
|
||||
);
|
||||
|
||||
return {
|
||||
onDisconnected: () => {
|
||||
// Although "disconnected-from-child" implies that the child is already
|
||||
// disconnected this is not the case. The disconnection takes place after
|
||||
// this method has finished. This gives us chance to clean up items within
|
||||
// the parent process e.g. observers.
|
||||
mm.removeMessageListener(
|
||||
"debug:storage-extensionStorage-request-parent",
|
||||
extensionStorageHelpers.handleChildRequest
|
||||
);
|
||||
extensionStorageHelpers.onDisconnected();
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1815,8 +1641,6 @@ StorageActors.createActor(
|
|||
|
||||
this.onStorageChange = this.onStorageChange.bind(this);
|
||||
|
||||
this.setupChildProcess();
|
||||
|
||||
this.onWindowReady = this.onWindowReady.bind(this);
|
||||
this.onWindowDestroyed = this.onWindowDestroyed.bind(this);
|
||||
this.storageActor.on("window-ready", this.onWindowReady);
|
||||
|
|
@ -1828,7 +1652,8 @@ StorageActors.createActor(
|
|||
},
|
||||
|
||||
destroy() {
|
||||
extensionStorageHelpers.onChangedChildListeners.delete(
|
||||
ExtensionStorageIDB.removeOnChangedListener(
|
||||
this.addonId,
|
||||
this.onStorageChange
|
||||
);
|
||||
|
||||
|
|
@ -1841,42 +1666,12 @@ StorageActors.createActor(
|
|||
this.storageActor = null;
|
||||
},
|
||||
|
||||
setupChildProcess() {
|
||||
const ppmm = this.conn.parentMessageManager;
|
||||
extensionStorageHelpers.setPpmm(ppmm);
|
||||
|
||||
// eslint-disable-next-line no-restricted-properties
|
||||
this.conn.setupInParent({
|
||||
module: "devtools/server/actors/storage",
|
||||
setupParent: "setupParentProcessForExtensionStorage",
|
||||
});
|
||||
|
||||
extensionStorageHelpers.onChangedChildListeners.add(this.onStorageChange);
|
||||
this.setupStorageInParent = extensionStorageHelpers.callParentProcessAsync.bind(
|
||||
extensionStorageHelpers,
|
||||
"setupStorageInParent"
|
||||
);
|
||||
|
||||
// Add a message listener in the child process to receive messages from the parent
|
||||
// process
|
||||
ppmm.addMessageListener(
|
||||
"debug:storage-extensionStorage-request-child",
|
||||
extensionStorageHelpers.handleParentRequest.bind(
|
||||
extensionStorageHelpers
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* This fires when the extension changes storage data while the storage
|
||||
* inspector is open. Ensures this.hostVsStores stays up-to-date and
|
||||
* passes the changes on to update the client.
|
||||
*/
|
||||
onStorageChange({ addonId, changes }) {
|
||||
if (addonId !== this.addonId) {
|
||||
return;
|
||||
}
|
||||
|
||||
onStorageChange(changes) {
|
||||
const host = this.extensionHostURL;
|
||||
const storeMap = this.hostVsStores.get(host);
|
||||
|
||||
|
|
@ -1922,10 +1717,32 @@ StorageActors.createActor(
|
|||
*/
|
||||
async preListStores() {
|
||||
// Ensure the actor's target is an extension and it is enabled
|
||||
if (!this.addonId || !WebExtensionPolicy.getByID(this.addonId)) {
|
||||
if (!this.addonId || !this.getExtensionPolicy()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Subscribe a listener for event notifications from the WE storage API when
|
||||
// storage local data has been changed by the extension, and keep track of the
|
||||
// listener to remove it when the debugger is being disconnected.
|
||||
ExtensionStorageIDB.addOnChangedListener(
|
||||
this.addonId,
|
||||
this.onStorageChange
|
||||
);
|
||||
try {
|
||||
// Make sure the extension storage APIs have been loaded,
|
||||
// otherwise the DevTools storage panel would not be updated
|
||||
// automatically when the extension storage data is being changed
|
||||
// if the parent ext-storage.js module wasn't already loaded
|
||||
// (See Bug 1802929).
|
||||
const { extension } = WebExtensionPolicy.getByID(this.addonId);
|
||||
await extension.apiManager.asyncGetAPI("storage", extension);
|
||||
} catch (e) {
|
||||
console.error(
|
||||
"Exception while trying to initialize webext storage API",
|
||||
e
|
||||
);
|
||||
}
|
||||
|
||||
await this.populateStoresForHost(this.extensionHostURL);
|
||||
},
|
||||
|
||||
|
|
@ -1956,7 +1773,7 @@ StorageActors.createActor(
|
|||
const storeMap = new Map();
|
||||
this.hostVsStores.set(host, storeMap);
|
||||
|
||||
const storagePrincipal = await this.getStoragePrincipal(extension.id);
|
||||
const storagePrincipal = await this.getStoragePrincipal();
|
||||
|
||||
if (!storagePrincipal) {
|
||||
// This could happen if the extension fails to be migrated to the
|
||||
|
|
@ -1982,17 +1799,20 @@ StorageActors.createActor(
|
|||
}
|
||||
},
|
||||
|
||||
async getStoragePrincipal(addonId) {
|
||||
async getStoragePrincipal() {
|
||||
const { extension } = this.getExtensionPolicy();
|
||||
const {
|
||||
backendEnabled,
|
||||
storagePrincipal,
|
||||
} = await this.setupStorageInParent(addonId);
|
||||
} = await ExtensionStorageIDB.selectBackend({ extension });
|
||||
|
||||
if (!backendEnabled) {
|
||||
// IDB backend disabled; give up.
|
||||
return null;
|
||||
}
|
||||
return storagePrincipal;
|
||||
|
||||
// Received as a StructuredCloneHolder, so we need to deserialize
|
||||
return storagePrincipal.deserialize(this, true);
|
||||
},
|
||||
|
||||
getValuesForHost(host, name) {
|
||||
|
|
|
|||
|
|
@ -188,6 +188,7 @@ function getWatcherSupportedResources(type) {
|
|||
[Resources.TYPES.CACHE_STORAGE]: true,
|
||||
[Resources.TYPES.COOKIE]: true,
|
||||
[Resources.TYPES.ERROR_MESSAGE]: true,
|
||||
[Resources.TYPES.EXTENSION_STORAGE]: true,
|
||||
[Resources.TYPES.INDEXED_DB]: true,
|
||||
[Resources.TYPES.LOCAL_STORAGE]: true,
|
||||
[Resources.TYPES.SESSION_STORAGE]: true,
|
||||
|
|
|
|||
|
|
@ -60,13 +60,13 @@ add_setup(async function setup() {
|
|||
add_task(async function test_extension_store_exists() {
|
||||
const extension = await startupExtension(getExtensionConfig());
|
||||
|
||||
const { target, extensionStorage } = await openAddonStoragePanel(
|
||||
const { commands, extensionStorage } = await openAddonStoragePanel(
|
||||
extension.id
|
||||
);
|
||||
|
||||
ok(extensionStorage, "Should have an extensionStorage store");
|
||||
|
||||
await shutdown(extension, target);
|
||||
await shutdown(extension, commands);
|
||||
});
|
||||
|
||||
add_task(
|
||||
|
|
@ -86,7 +86,7 @@ add_task(
|
|||
getExtensionConfig({ background })
|
||||
);
|
||||
|
||||
const { target, extensionStorage } = await openAddonStoragePanel(
|
||||
const { commands, extensionStorage } = await openAddonStoragePanel(
|
||||
extension.id
|
||||
);
|
||||
|
||||
|
|
@ -97,7 +97,7 @@ add_task(
|
|||
"Should have the expected extension host in the extensionStorage store"
|
||||
);
|
||||
|
||||
await shutdown(extension, target);
|
||||
await shutdown(extension, commands);
|
||||
}
|
||||
);
|
||||
|
||||
|
|
@ -118,7 +118,7 @@ add_task(async function test_panel_live_updates() {
|
|||
getExtensionConfig({ background: extensionScriptWithMessageListener })
|
||||
);
|
||||
|
||||
const { target, extensionStorage } = await openAddonStoragePanel(
|
||||
const { commands, extensionStorage } = await openAddonStoragePanel(
|
||||
extension.id
|
||||
);
|
||||
|
||||
|
|
@ -319,7 +319,7 @@ add_task(async function test_panel_live_updates() {
|
|||
"Got the expected results on populated storage.local"
|
||||
);
|
||||
|
||||
await shutdown(extension, target);
|
||||
await shutdown(extension, commands);
|
||||
});
|
||||
|
||||
/**
|
||||
|
|
@ -347,7 +347,7 @@ add_task(
|
|||
extension.sendMessage("storage-local-set", { a: 123 });
|
||||
await extension.awaitMessage("storage-local-set:done");
|
||||
|
||||
const { target, extensionStorage } = await openAddonStoragePanel(
|
||||
const { commands, extensionStorage } = await openAddonStoragePanel(
|
||||
extension.id
|
||||
);
|
||||
|
||||
|
|
@ -366,7 +366,7 @@ add_task(
|
|||
);
|
||||
|
||||
await contentPage.close();
|
||||
await shutdown(extension, target);
|
||||
await shutdown(extension, commands);
|
||||
}
|
||||
);
|
||||
|
||||
|
|
@ -396,7 +396,7 @@ add_task(async function test_panel_data_matches_extension_with_no_pages_open() {
|
|||
await extension.awaitMessage("storage-local-onChanged");
|
||||
await contentPage.close();
|
||||
|
||||
const { target, extensionStorage } = await openAddonStoragePanel(
|
||||
const { commands, extensionStorage } = await openAddonStoragePanel(
|
||||
extension.id
|
||||
);
|
||||
|
||||
|
|
@ -414,7 +414,7 @@ add_task(async function test_panel_data_matches_extension_with_no_pages_open() {
|
|||
"Got the expected results on populated storage.local"
|
||||
);
|
||||
|
||||
await shutdown(extension, target);
|
||||
await shutdown(extension, commands);
|
||||
});
|
||||
|
||||
/**
|
||||
|
|
@ -434,7 +434,7 @@ add_task(
|
|||
getExtensionConfig({ files: ext_no_bg.files })
|
||||
);
|
||||
|
||||
const { target, extensionStorage } = await openAddonStoragePanel(
|
||||
const { commands, extensionStorage } = await openAddonStoragePanel(
|
||||
extension.id
|
||||
);
|
||||
|
||||
|
|
@ -493,7 +493,7 @@ add_task(
|
|||
);
|
||||
|
||||
await contentPage.close();
|
||||
await shutdown(extension, target);
|
||||
await shutdown(extension, commands);
|
||||
}
|
||||
);
|
||||
|
||||
|
|
@ -516,7 +516,7 @@ add_task(
|
|||
|
||||
const host = await extension.awaitMessage("extension-origin");
|
||||
|
||||
const { target, extensionStorage } = await openAddonStoragePanel(
|
||||
const { commands, extensionStorage } = await openAddonStoragePanel(
|
||||
extension.id
|
||||
);
|
||||
|
||||
|
|
@ -588,7 +588,7 @@ add_task(
|
|||
);
|
||||
}
|
||||
|
||||
await shutdown(extension, target);
|
||||
await shutdown(extension, commands);
|
||||
}
|
||||
);
|
||||
|
||||
|
|
@ -610,11 +610,9 @@ add_task(
|
|||
|
||||
const host = await extension.awaitMessage("extension-origin");
|
||||
|
||||
const {
|
||||
target,
|
||||
extensionStorage,
|
||||
storageFront,
|
||||
} = await openAddonStoragePanel(extension.id);
|
||||
const { commands, extensionStorage } = await openAddonStoragePanel(
|
||||
extension.id
|
||||
);
|
||||
|
||||
const DEFAULT_VALUE = "value"; // global in devtools/server/actors/storage.js
|
||||
let items = {
|
||||
|
|
@ -624,7 +622,7 @@ add_task(
|
|||
};
|
||||
|
||||
info("Adding storage items from the extension");
|
||||
let storesUpdate = storageFront.once("stores-update");
|
||||
let storesUpdate = extensionStorage.once("single-store-update");
|
||||
extension.sendMessage("storage-local-set", items);
|
||||
await extension.awaitMessage("storage-local-set:done");
|
||||
|
||||
|
|
@ -637,13 +635,15 @@ add_task(
|
|||
[host]: ["guid_1", "guid_2", "guid_3"],
|
||||
},
|
||||
},
|
||||
changed: undefined,
|
||||
deleted: undefined,
|
||||
},
|
||||
data,
|
||||
"The change data from the storage actor's 'stores-update' event matches the changes made in the client."
|
||||
);
|
||||
|
||||
info("Waiting for panel to edit some items");
|
||||
storesUpdate = storageFront.once("stores-update");
|
||||
storesUpdate = extensionStorage.once("single-store-update");
|
||||
await extensionStorage.editItem({
|
||||
host,
|
||||
field: "value",
|
||||
|
|
@ -655,11 +655,13 @@ add_task(
|
|||
data = await storesUpdate;
|
||||
Assert.deepEqual(
|
||||
{
|
||||
added: undefined,
|
||||
changed: {
|
||||
extensionStorage: {
|
||||
[host]: ["guid_1"],
|
||||
},
|
||||
},
|
||||
deleted: undefined,
|
||||
},
|
||||
data,
|
||||
"The change data from the storage actor's 'stores-update' event matches the changes made in the client."
|
||||
|
|
@ -679,13 +681,15 @@ add_task(
|
|||
);
|
||||
|
||||
info("Waiting for panel to remove an item");
|
||||
storesUpdate = storageFront.once("stores-update");
|
||||
storesUpdate = extensionStorage.once("single-store-update");
|
||||
await extensionStorage.removeItem(host, "guid_3");
|
||||
|
||||
info("Waiting for the storage actor to emit a 'stores-update' event");
|
||||
data = await storesUpdate;
|
||||
Assert.deepEqual(
|
||||
{
|
||||
added: undefined,
|
||||
changed: undefined,
|
||||
deleted: {
|
||||
extensionStorage: {
|
||||
[host]: ["guid_3"],
|
||||
|
|
@ -709,14 +713,14 @@ add_task(
|
|||
);
|
||||
|
||||
info("Waiting for panel to remove all items");
|
||||
const storesCleared = storageFront.once("stores-cleared");
|
||||
const storesCleared = extensionStorage.once("single-store-cleared");
|
||||
await extensionStorage.removeAll(host);
|
||||
|
||||
info("Waiting for the storage actor to emit a 'stores-cleared' event");
|
||||
data = await storesCleared;
|
||||
Assert.deepEqual(
|
||||
{
|
||||
extensionStorage: {
|
||||
clearedHostsOrPaths: {
|
||||
[host]: [],
|
||||
},
|
||||
},
|
||||
|
|
@ -733,7 +737,7 @@ add_task(
|
|||
`The storage items in the extension match the items in the panel`
|
||||
);
|
||||
|
||||
await shutdown(extension, target);
|
||||
await shutdown(extension, commands);
|
||||
}
|
||||
);
|
||||
|
||||
|
|
@ -780,7 +784,7 @@ add_task(
|
|||
|
||||
await extension.awaitMessage("extension-origin");
|
||||
|
||||
const { target, extensionStorage } = await openAddonStoragePanel(
|
||||
const { commands, extensionStorage } = await openAddonStoragePanel(
|
||||
extension.id
|
||||
);
|
||||
|
||||
|
|
@ -829,7 +833,7 @@ add_task(
|
|||
Services.prefs.setBoolPref(LEAVE_STORAGE_PREF, false);
|
||||
Services.prefs.setBoolPref(LEAVE_UUID_PREF, false);
|
||||
|
||||
await shutdown(extension, target);
|
||||
await shutdown(extension, commands);
|
||||
}
|
||||
);
|
||||
|
||||
|
|
@ -884,7 +888,7 @@ add_task(async function test_panel_live_reload_for_extension_without_bg_page() {
|
|||
await contentPage.close();
|
||||
|
||||
info("Opening storage panel");
|
||||
const { target, extensionStorage } = await openAddonStoragePanel(
|
||||
const { commands, extensionStorage } = await openAddonStoragePanel(
|
||||
extension.id
|
||||
);
|
||||
|
||||
|
|
@ -915,7 +919,7 @@ add_task(async function test_panel_live_reload_for_extension_without_bg_page() {
|
|||
"Got the expected results on populated storage.local"
|
||||
);
|
||||
|
||||
await shutdown(extension, target);
|
||||
await shutdown(extension, commands);
|
||||
});
|
||||
|
||||
/**
|
||||
|
|
@ -953,7 +957,7 @@ add_task(
|
|||
const host = await extension.awaitMessage("extension-origin");
|
||||
|
||||
info("Opening storage panel");
|
||||
const { target, extensionStorage } = await openAddonStoragePanel(
|
||||
const { commands, extensionStorage } = await openAddonStoragePanel(
|
||||
extension.id
|
||||
);
|
||||
|
||||
|
|
@ -994,7 +998,7 @@ add_task(
|
|||
"Got the expected results on populated storage.local"
|
||||
);
|
||||
|
||||
await shutdown(extension, target);
|
||||
await shutdown(extension, commands);
|
||||
}
|
||||
);
|
||||
|
||||
|
|
@ -1034,7 +1038,7 @@ add_task(
|
|||
const host = await extension.awaitMessage("extension-origin");
|
||||
|
||||
info("Opening storage panel");
|
||||
const { target, extensionStorage } = await openAddonStoragePanel(
|
||||
const { commands, extensionStorage } = await openAddonStoragePanel(
|
||||
extension.id
|
||||
);
|
||||
|
||||
|
|
@ -1052,7 +1056,7 @@ add_task(
|
|||
"Got the expected results on populated storage.local"
|
||||
);
|
||||
|
||||
await shutdown(extension, target);
|
||||
await shutdown(extension, commands);
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -83,11 +83,9 @@ add_task(async function test_panel_live_reload() {
|
|||
extension.sendMessage("storage-local-set", { a: 123 });
|
||||
await extension.awaitMessage("storage-local-set:done");
|
||||
|
||||
const {
|
||||
target,
|
||||
extensionStorage,
|
||||
storageFront,
|
||||
} = await openAddonStoragePanel(extension.id);
|
||||
const { commands, extensionStorage } = await openAddonStoragePanel(
|
||||
extension.id
|
||||
);
|
||||
|
||||
manifest = {
|
||||
...manifest,
|
||||
|
|
@ -99,11 +97,13 @@ add_task(async function test_panel_live_reload() {
|
|||
// Wait for the storage front to receive an event for the storage panel refresh
|
||||
// when the extension has been reloaded.
|
||||
const promiseStoragePanelUpdated = new Promise(resolve => {
|
||||
storageFront.on("stores-update", function updateListener(updates) {
|
||||
extensionStorage.on("single-store-update", function updateListener(
|
||||
updates
|
||||
) {
|
||||
info(`Got stores-update event: ${JSON.stringify(updates)}`);
|
||||
const extStorageAdded = updates.added?.extensionStorage;
|
||||
if (host in extStorageAdded && extStorageAdded[host].length) {
|
||||
storageFront.off("stores-update", updateListener);
|
||||
extensionStorage.off("single-store-update", updateListener);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
|
|
@ -137,5 +137,5 @@ add_task(async function test_panel_live_reload() {
|
|||
"Got the expected results on populated storage.local"
|
||||
);
|
||||
|
||||
await shutdown(extension, target);
|
||||
await shutdown(extension, commands);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -20,21 +20,6 @@ const {
|
|||
CommandsFactory,
|
||||
} = require("resource://devtools/shared/commands/commands-factory.js");
|
||||
|
||||
/**
|
||||
* Set up the equivalent of an `about:debugging` toolbox for a given extension, minus
|
||||
* the toolbox.
|
||||
*
|
||||
* @param {String} id - The id for the extension to be targeted by the toolbox.
|
||||
* @return {Object} Resolves with the web extension actor front and target objects when
|
||||
* the debugger has been connected to the extension.
|
||||
*/
|
||||
async function setupExtensionDebugging(id) {
|
||||
const commands = await CommandsFactory.forAddon(id);
|
||||
const target = await commands.descriptorFront.getTarget();
|
||||
return { front: commands.descriptorFront, target };
|
||||
}
|
||||
exports.setupExtensionDebugging = setupExtensionDebugging;
|
||||
|
||||
/**
|
||||
* Loads and starts up a test extension given the provided extension configuration.
|
||||
*
|
||||
|
|
@ -52,21 +37,32 @@ async function startupExtension(extConfig) {
|
|||
exports.startupExtension = startupExtension;
|
||||
|
||||
/**
|
||||
* Initializes the extensionStorage actor for a target extension. This is effectively
|
||||
* Initializes the extensionStorage actor for a given extension. This is effectively
|
||||
* what happens when the addon storage panel is opened in the browser.
|
||||
*
|
||||
* @param {String} - id, The addon id
|
||||
* @return {Object} - Resolves with the web extension actor target and extensionStorage
|
||||
* store objects when the panel has been opened.
|
||||
* @return {Object} - Resolves with the DevTools "commands" objact and the extensionStorage
|
||||
* resource/front.
|
||||
*/
|
||||
async function openAddonStoragePanel(id) {
|
||||
const { target } = await setupExtensionDebugging(id);
|
||||
const commands = await CommandsFactory.forAddon(id);
|
||||
await commands.targetCommand.startListening();
|
||||
|
||||
const storageFront = await target.getFront("storage");
|
||||
const stores = await storageFront.listStores();
|
||||
const extensionStorage = stores.extensionStorage || null;
|
||||
// Fetch the EXTENSION_STORAGE resource.
|
||||
// Unfortunately, we can't use resourceCommand.waitForNextResource as it would destroy
|
||||
// the actor by immediately unwatching for the resource type.
|
||||
const extensionStorage = await new Promise(resolve => {
|
||||
commands.resourceCommand.watchResources(
|
||||
[commands.resourceCommand.TYPES.EXTENSION_STORAGE],
|
||||
{
|
||||
onAvailable(resources) {
|
||||
resolve(resources[0]);
|
||||
},
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return { target, extensionStorage, storageFront };
|
||||
return { commands, extensionStorage };
|
||||
}
|
||||
exports.openAddonStoragePanel = openAddonStoragePanel;
|
||||
|
||||
|
|
@ -170,11 +166,11 @@ exports.extensionScriptWithMessageListener = extensionScriptWithMessageListener;
|
|||
* Shutdown procedure common to all tasks.
|
||||
*
|
||||
* @param {Object} extension - The test extension
|
||||
* @param {Object} target - The web extension actor targeted by the DevTools client
|
||||
* @param {Object} commands - The web extension commands used by the DevTools to interact with the backend
|
||||
*/
|
||||
async function shutdown(extension, target) {
|
||||
if (target) {
|
||||
await target.destroy();
|
||||
async function shutdown(extension, commands) {
|
||||
if (commands) {
|
||||
await commands.destroy();
|
||||
}
|
||||
await extension.unload();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1372,6 +1372,11 @@ loader.lazyRequireGetter(
|
|||
ResourceCommand.TYPES.COOKIE,
|
||||
"resource://devtools/shared/commands/resource/transformers/storage-cookie.js"
|
||||
);
|
||||
loader.lazyRequireGetter(
|
||||
ResourceTransformers,
|
||||
ResourceCommand.TYPES.EXTENSION_STORAGE,
|
||||
"resource://devtools/shared/commands/resource/transformers/storage-extension.js"
|
||||
);
|
||||
loader.lazyRequireGetter(
|
||||
ResourceTransformers,
|
||||
ResourceCommand.TYPES.INDEXED_DB,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ DevToolsModules(
|
|||
"network-events.js",
|
||||
"storage-cache.js",
|
||||
"storage-cookie.js",
|
||||
"storage-extension.js",
|
||||
"storage-indexed-db.js",
|
||||
"storage-local-storage.js",
|
||||
"storage-session-storage.js",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {
|
||||
TYPES: { EXTENSION_STORAGE },
|
||||
} = require("resource://devtools/shared/commands/resource/resource-command.js");
|
||||
|
||||
const { Front, types } = require("resource://devtools/shared/protocol.js");
|
||||
|
||||
module.exports = function({ resource, watcherFront, targetFront }) {
|
||||
if (!(resource instanceof Front) && watcherFront) {
|
||||
const { innerWindowId } = resource;
|
||||
|
||||
// it's safe to instantiate the front now, so we do it.
|
||||
resource = types.getType("extensionStorage").read(resource, targetFront);
|
||||
resource.resourceType = EXTENSION_STORAGE;
|
||||
resource.resourceId = `${EXTENSION_STORAGE}-${targetFront.browsingContextID}`;
|
||||
resource.resourceKey = "extensionStorage";
|
||||
resource.innerWindowId = innerWindowId;
|
||||
}
|
||||
|
||||
return resource;
|
||||
};
|
||||
|
|
@ -227,11 +227,12 @@ const Types = (exports.__TypesForTests = [
|
|||
},
|
||||
{
|
||||
types: [
|
||||
"Cache",
|
||||
"cookies",
|
||||
"localStorage",
|
||||
"sessionStorage",
|
||||
"Cache",
|
||||
"extensionStorage",
|
||||
"indexedDB",
|
||||
"sessionStorage",
|
||||
"storage",
|
||||
],
|
||||
spec: "devtools/shared/specs/storage",
|
||||
|
|
|
|||
Loading…
Reference in a new issue