forked from mirrors/gecko-dev
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
442 lines
16 KiB
JavaScript
442 lines
16 KiB
JavaScript
/* 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 Targets = require("resource://devtools/server/actors/targets/index.js");
|
|
|
|
const TYPES = {
|
|
CONSOLE_MESSAGE: "console-message",
|
|
CSS_CHANGE: "css-change",
|
|
CSS_MESSAGE: "css-message",
|
|
DOCUMENT_EVENT: "document-event",
|
|
ERROR_MESSAGE: "error-message",
|
|
LAST_PRIVATE_CONTEXT_EXIT: "last-private-context-exit",
|
|
NETWORK_EVENT: "network-event",
|
|
NETWORK_EVENT_STACKTRACE: "network-event-stacktrace",
|
|
PLATFORM_MESSAGE: "platform-message",
|
|
REFLOW: "reflow",
|
|
SERVER_SENT_EVENT: "server-sent-event",
|
|
SOURCE: "source",
|
|
STYLESHEET: "stylesheet",
|
|
THREAD_STATE: "thread-state",
|
|
WEBSOCKET: "websocket",
|
|
|
|
// storage types
|
|
CACHE_STORAGE: "Cache",
|
|
COOKIE: "cookies",
|
|
EXTENSION_STORAGE: "extension-storage",
|
|
INDEXED_DB: "indexed-db",
|
|
LOCAL_STORAGE: "local-storage",
|
|
SESSION_STORAGE: "session-storage",
|
|
|
|
// root types
|
|
EXTENSIONS_BGSCRIPT_STATUS: "extensions-backgroundscript-status",
|
|
};
|
|
exports.TYPES = TYPES;
|
|
|
|
// Helper dictionaries, which will contain data specific to each resource type.
|
|
// - `path` is the absolute path to the module defining the Resource Watcher class.
|
|
//
|
|
// Also see the attributes added by `augmentResourceDictionary` for each type:
|
|
// - `watchers` is a weak map which will store Resource Watchers
|
|
// (i.e. devtools/server/actors/resources/ class instances)
|
|
// keyed by target actor -or- watcher actor.
|
|
// - `WatcherClass` is a shortcut to the Resource Watcher module.
|
|
// Each module exports a Resource Watcher class.
|
|
//
|
|
// These are several dictionaries, which depend how the resource watcher classes are instantiated.
|
|
|
|
// Frame target resources are spawned via a BrowsingContext Target Actor.
|
|
// Their watcher class receives a target actor as first argument.
|
|
// They are instantiated for each observed BrowsingContext, from the content process where it runs.
|
|
// They are meant to observe all resources related to a given Browsing Context.
|
|
const FrameTargetResources = augmentResourceDictionary({
|
|
[TYPES.CACHE_STORAGE]: {
|
|
path: "devtools/server/actors/resources/storage-cache",
|
|
},
|
|
[TYPES.CONSOLE_MESSAGE]: {
|
|
path: "devtools/server/actors/resources/console-messages",
|
|
},
|
|
[TYPES.CSS_CHANGE]: {
|
|
path: "devtools/server/actors/resources/css-changes",
|
|
},
|
|
[TYPES.CSS_MESSAGE]: {
|
|
path: "devtools/server/actors/resources/css-messages",
|
|
},
|
|
[TYPES.DOCUMENT_EVENT]: {
|
|
path: "devtools/server/actors/resources/document-event",
|
|
},
|
|
[TYPES.ERROR_MESSAGE]: {
|
|
path: "devtools/server/actors/resources/error-messages",
|
|
},
|
|
[TYPES.LOCAL_STORAGE]: {
|
|
path: "devtools/server/actors/resources/storage-local-storage",
|
|
},
|
|
[TYPES.PLATFORM_MESSAGE]: {
|
|
path: "devtools/server/actors/resources/platform-messages",
|
|
},
|
|
[TYPES.SESSION_STORAGE]: {
|
|
path: "devtools/server/actors/resources/storage-session-storage",
|
|
},
|
|
[TYPES.STYLESHEET]: {
|
|
path: "devtools/server/actors/resources/stylesheets",
|
|
},
|
|
[TYPES.NETWORK_EVENT]: {
|
|
path: "devtools/server/actors/resources/network-events-content",
|
|
},
|
|
[TYPES.NETWORK_EVENT_STACKTRACE]: {
|
|
path: "devtools/server/actors/resources/network-events-stacktraces",
|
|
},
|
|
[TYPES.REFLOW]: {
|
|
path: "devtools/server/actors/resources/reflow",
|
|
},
|
|
[TYPES.SOURCE]: {
|
|
path: "devtools/server/actors/resources/sources",
|
|
},
|
|
[TYPES.THREAD_STATE]: {
|
|
path: "devtools/server/actors/resources/thread-states",
|
|
},
|
|
[TYPES.SERVER_SENT_EVENT]: {
|
|
path: "devtools/server/actors/resources/server-sent-events",
|
|
},
|
|
[TYPES.WEBSOCKET]: {
|
|
path: "devtools/server/actors/resources/websockets",
|
|
},
|
|
});
|
|
|
|
// Process target resources are spawned via a Process Target Actor.
|
|
// Their watcher class receives a process target actor as first argument.
|
|
// They are instantiated for each observed Process (parent and all content processes).
|
|
// They are meant to observe all resources related to a given process.
|
|
const ProcessTargetResources = augmentResourceDictionary({
|
|
[TYPES.CONSOLE_MESSAGE]: {
|
|
path: "devtools/server/actors/resources/console-messages",
|
|
},
|
|
[TYPES.ERROR_MESSAGE]: {
|
|
path: "devtools/server/actors/resources/error-messages",
|
|
},
|
|
[TYPES.PLATFORM_MESSAGE]: {
|
|
path: "devtools/server/actors/resources/platform-messages",
|
|
},
|
|
[TYPES.SOURCE]: {
|
|
path: "devtools/server/actors/resources/sources",
|
|
},
|
|
[TYPES.THREAD_STATE]: {
|
|
path: "devtools/server/actors/resources/thread-states",
|
|
},
|
|
});
|
|
|
|
// Worker target resources are spawned via a Worker Target Actor.
|
|
// Their watcher class receives a worker target actor as first argument.
|
|
// They are instantiated for each observed worker, from the worker thread.
|
|
// They are meant to observe all resources related to a given worker.
|
|
//
|
|
// We'll only support a few resource types in Workers (console-message, source,
|
|
// thread state, …) as error and platform messages are not supported since we need access
|
|
// to Ci, which isn't available in worker context.
|
|
// Errors are emitted from the content process main thread so the user would still get them.
|
|
const WorkerTargetResources = augmentResourceDictionary({
|
|
[TYPES.CONSOLE_MESSAGE]: {
|
|
path: "devtools/server/actors/resources/console-messages",
|
|
},
|
|
[TYPES.SOURCE]: {
|
|
path: "devtools/server/actors/resources/sources",
|
|
},
|
|
[TYPES.THREAD_STATE]: {
|
|
path: "devtools/server/actors/resources/thread-states",
|
|
},
|
|
});
|
|
|
|
// Parent process resources are spawned via the Watcher Actor.
|
|
// Their watcher class receives the watcher actor as first argument.
|
|
// They are instantiated once per watcher from the parent process.
|
|
// They are meant to observe all resources related to a given context designated by the Watcher (and its sessionContext)
|
|
// they should be observed from the parent process.
|
|
const ParentProcessResources = augmentResourceDictionary({
|
|
[TYPES.NETWORK_EVENT]: {
|
|
path: "devtools/server/actors/resources/network-events",
|
|
},
|
|
[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",
|
|
},
|
|
[TYPES.DOCUMENT_EVENT]: {
|
|
path: "devtools/server/actors/resources/parent-process-document-event",
|
|
},
|
|
[TYPES.LAST_PRIVATE_CONTEXT_EXIT]: {
|
|
path: "devtools/server/actors/resources/last-private-context-exit",
|
|
},
|
|
});
|
|
|
|
// Root resources are spawned via the Root Actor.
|
|
// Their watcher class receives the root actor as first argument.
|
|
// They are instantiated only once from the parent process.
|
|
// They are meant to observe anything easily observable from the parent process
|
|
// that isn't related to any particular context/target.
|
|
// This is especially useful when you need to observe something without having to instantiate a Watcher actor.
|
|
const RootResources = augmentResourceDictionary({
|
|
[TYPES.EXTENSIONS_BGSCRIPT_STATUS]: {
|
|
path: "devtools/server/actors/resources/extensions-backgroundscript-status",
|
|
},
|
|
});
|
|
exports.RootResources = RootResources;
|
|
|
|
function augmentResourceDictionary(dict) {
|
|
for (const resource of Object.values(dict)) {
|
|
resource.watchers = new WeakMap();
|
|
|
|
loader.lazyRequireGetter(resource, "WatcherClass", resource.path);
|
|
}
|
|
return dict;
|
|
}
|
|
|
|
/**
|
|
* For a given actor, return the related dictionary defined just before,
|
|
* that contains info about how to listen for a given resource type, from a given actor.
|
|
*
|
|
* @param Actor rootOrWatcherOrTargetActor
|
|
* Either a RootActor or WatcherActor or a TargetActor which can be listening to a resource.
|
|
*/
|
|
function getResourceTypeDictionary(rootOrWatcherOrTargetActor) {
|
|
const { typeName } = rootOrWatcherOrTargetActor;
|
|
if (typeName == "root") {
|
|
return RootResources;
|
|
}
|
|
if (typeName == "watcher") {
|
|
return ParentProcessResources;
|
|
}
|
|
const { targetType } = rootOrWatcherOrTargetActor;
|
|
return getResourceTypeDictionaryForTargetType(targetType);
|
|
}
|
|
|
|
/**
|
|
* For a targetType, return the related dictionary.
|
|
*
|
|
* @param String targetType
|
|
* A targetType string (See Targets.TYPES)
|
|
*/
|
|
function getResourceTypeDictionaryForTargetType(targetType) {
|
|
switch (targetType) {
|
|
case Targets.TYPES.FRAME:
|
|
return FrameTargetResources;
|
|
case Targets.TYPES.PROCESS:
|
|
return ProcessTargetResources;
|
|
case Targets.TYPES.WORKER:
|
|
return WorkerTargetResources;
|
|
default:
|
|
throw new Error(`Unsupported target actor typeName '${targetType}'`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* For a given actor, return the object stored in one of the previous dictionary
|
|
* that contains info about how to listen for a given resource type, from a given actor.
|
|
*
|
|
* @param Actor rootOrWatcherOrTargetActor
|
|
* Either a RootActor or WatcherActor or a TargetActor which can be listening to a resource.
|
|
* @param String resourceType
|
|
* The resource type to be observed.
|
|
*/
|
|
function getResourceTypeEntry(rootOrWatcherOrTargetActor, resourceType) {
|
|
const dict = getResourceTypeDictionary(rootOrWatcherOrTargetActor);
|
|
if (!(resourceType in dict)) {
|
|
throw new Error(
|
|
`Unsupported resource type '${resourceType}' for ${rootOrWatcherOrTargetActor.typeName}`
|
|
);
|
|
}
|
|
return dict[resourceType];
|
|
}
|
|
|
|
/**
|
|
* Start watching for a new list of resource types.
|
|
* This will also emit all already existing resources before resolving.
|
|
*
|
|
* @param Actor rootOrWatcherOrTargetActor
|
|
* Either a RootActor or WatcherActor or a TargetActor which can be listening to a resource:
|
|
* * RootActor will be used for resources observed from the parent process and aren't related to any particular
|
|
* context/descriptor. They can be observed right away when connecting to the RDP server
|
|
* without instantiating any actor other than the root actor.
|
|
* * WatcherActor will be used for resources listened from the parent process.
|
|
* * TargetActor will be used for resources listened from the content process.
|
|
* This actor:
|
|
* - defines what context to observe (browsing context, process, worker, ...)
|
|
* Via browsingContextID, windows, docShells attributes for the target actor.
|
|
* Via the `sessionContext` object for the watcher actor.
|
|
* (only for Watcher and Target actors. Root actor is context-less.)
|
|
* - exposes `notifyResources` method to be notified about all the resources updates
|
|
* This method will receive two arguments:
|
|
* - {String} updateType, which can be "available", "updated", or "destroyed"
|
|
* - {Array<Object>} resources, which will be the list of resource's forms
|
|
* or special update object for "updated" scenario.
|
|
* @param Array<String> resourceTypes
|
|
* List of all type of resource to listen to.
|
|
*/
|
|
async function watchResources(rootOrWatcherOrTargetActor, resourceTypes) {
|
|
// If we are given a target actor, filter out the resource types supported by the target.
|
|
// When using sharedData to pass types between processes, we are passing them for all target types.
|
|
const { targetType } = rootOrWatcherOrTargetActor;
|
|
// Only target actors usecase will have a target type.
|
|
// For Root and Watcher we process the `resourceTypes` list unfiltered.
|
|
if (targetType) {
|
|
resourceTypes = getResourceTypesForTargetType(resourceTypes, targetType);
|
|
}
|
|
for (const resourceType of resourceTypes) {
|
|
const { watchers, WatcherClass } = getResourceTypeEntry(
|
|
rootOrWatcherOrTargetActor,
|
|
resourceType
|
|
);
|
|
|
|
// Ignore resources we're already listening to
|
|
if (watchers.has(rootOrWatcherOrTargetActor)) {
|
|
continue;
|
|
}
|
|
|
|
// Don't watch for console messages from the worker target if worker messages are still
|
|
// being cloned to the main process, otherwise we'll get duplicated messages in the
|
|
// console output (See Bug 1778852).
|
|
if (
|
|
resourceType == TYPES.CONSOLE_MESSAGE &&
|
|
rootOrWatcherOrTargetActor.workerConsoleApiMessagesDispatchedToMainThread
|
|
) {
|
|
continue;
|
|
}
|
|
|
|
const watcher = new WatcherClass();
|
|
await watcher.watch(rootOrWatcherOrTargetActor, {
|
|
onAvailable: rootOrWatcherOrTargetActor.notifyResources.bind(
|
|
rootOrWatcherOrTargetActor,
|
|
"available"
|
|
),
|
|
onUpdated: rootOrWatcherOrTargetActor.notifyResources.bind(
|
|
rootOrWatcherOrTargetActor,
|
|
"updated"
|
|
),
|
|
onDestroyed: rootOrWatcherOrTargetActor.notifyResources.bind(
|
|
rootOrWatcherOrTargetActor,
|
|
"destroyed"
|
|
),
|
|
});
|
|
watchers.set(rootOrWatcherOrTargetActor, watcher);
|
|
}
|
|
}
|
|
exports.watchResources = watchResources;
|
|
|
|
function getParentProcessResourceTypes(resourceTypes) {
|
|
return resourceTypes.filter(resourceType => {
|
|
return resourceType in ParentProcessResources;
|
|
});
|
|
}
|
|
exports.getParentProcessResourceTypes = getParentProcessResourceTypes;
|
|
|
|
function getResourceTypesForTargetType(resourceTypes, targetType) {
|
|
const resourceDictionnary = getResourceTypeDictionaryForTargetType(
|
|
targetType
|
|
);
|
|
return resourceTypes.filter(resourceType => {
|
|
return resourceType in resourceDictionnary;
|
|
});
|
|
}
|
|
exports.getResourceTypesForTargetType = getResourceTypesForTargetType;
|
|
|
|
function hasResourceTypesForTargets(resourceTypes) {
|
|
return resourceTypes.some(resourceType => {
|
|
return resourceType in FrameTargetResources;
|
|
});
|
|
}
|
|
exports.hasResourceTypesForTargets = hasResourceTypesForTargets;
|
|
|
|
/**
|
|
* Stop watching for a list of resource types.
|
|
*
|
|
* @param Actor rootOrWatcherOrTargetActor
|
|
* The related actor, already passed to watchResources.
|
|
* @param Array<String> resourceTypes
|
|
* List of all type of resource to stop listening to.
|
|
*/
|
|
function unwatchResources(rootOrWatcherOrTargetActor, resourceTypes) {
|
|
for (const resourceType of resourceTypes) {
|
|
// Pull all info about this resource type from `Resources` global object
|
|
const { watchers } = getResourceTypeEntry(
|
|
rootOrWatcherOrTargetActor,
|
|
resourceType
|
|
);
|
|
|
|
const watcher = watchers.get(rootOrWatcherOrTargetActor);
|
|
if (watcher) {
|
|
watcher.destroy();
|
|
watchers.delete(rootOrWatcherOrTargetActor);
|
|
}
|
|
}
|
|
}
|
|
exports.unwatchResources = unwatchResources;
|
|
|
|
/**
|
|
* Clear resources for a list of resource types.
|
|
*
|
|
* @param Actor rootOrWatcherOrTargetActor
|
|
* The related actor, already passed to watchResources.
|
|
* @param Array<String> resourceTypes
|
|
* List of all type of resource to clear.
|
|
*/
|
|
function clearResources(rootOrWatcherOrTargetActor, resourceTypes) {
|
|
for (const resourceType of resourceTypes) {
|
|
const { watchers } = getResourceTypeEntry(
|
|
rootOrWatcherOrTargetActor,
|
|
resourceType
|
|
);
|
|
|
|
const watcher = watchers.get(rootOrWatcherOrTargetActor);
|
|
if (watcher && typeof watcher.clear == "function") {
|
|
watcher.clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
exports.clearResources = clearResources;
|
|
|
|
/**
|
|
* Stop watching for all watched resources on a given actor.
|
|
*
|
|
* @param Actor rootOrWatcherOrTargetActor
|
|
* The related actor, already passed to watchResources.
|
|
*/
|
|
function unwatchAllResources(rootOrWatcherOrTargetActor) {
|
|
for (const { watchers } of Object.values(
|
|
getResourceTypeDictionary(rootOrWatcherOrTargetActor)
|
|
)) {
|
|
const watcher = watchers.get(rootOrWatcherOrTargetActor);
|
|
if (watcher) {
|
|
watcher.destroy();
|
|
watchers.delete(rootOrWatcherOrTargetActor);
|
|
}
|
|
}
|
|
}
|
|
exports.unwatchAllResources = unwatchAllResources;
|
|
|
|
/**
|
|
* If we are watching for the given resource type,
|
|
* return the current ResourceWatcher instance used by this target actor
|
|
* in order to observe this resource type.
|
|
*
|
|
* @param Actor watcherOrTargetActor
|
|
* Either a WatcherActor or a TargetActor which can be listening to a resource.
|
|
* WatcherActor will be used for resources listened from the parent process,
|
|
* and TargetActor will be used for resources listened from the content process.
|
|
* @param String resourceType
|
|
* The resource type to query
|
|
* @return ResourceWatcher
|
|
* The resource watcher instance, defined in devtools/server/actors/resources/
|
|
*/
|
|
function getResourceWatcher(watcherOrTargetActor, resourceType) {
|
|
const { watchers } = getResourceTypeEntry(watcherOrTargetActor, resourceType);
|
|
|
|
return watchers.get(watcherOrTargetActor);
|
|
}
|
|
exports.getResourceWatcher = getResourceWatcher;
|