mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-01-19 01:10:22 +00:00
Bug 1657105 - Allow listening for resources from both parent and content processes. r=jdescottes,nchevobbe
Differential Revision: https://phabricator.services.mozilla.com/D86046
This commit is contained in:
parent
9f9a8d9b3e
commit
3441a8b394
@ -4,6 +4,8 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
const Targets = require("devtools/server/actors/targets/index");
|
||||
|
||||
const TYPES = {
|
||||
CONSOLE_MESSAGE: "console-message",
|
||||
CSS_CHANGE: "css-change",
|
||||
@ -14,17 +16,16 @@ const TYPES = {
|
||||
};
|
||||
exports.TYPES = TYPES;
|
||||
|
||||
// Helper dictionary, which will contain all data specific to each resource type.
|
||||
// Helper dictionaries, which will contain data specific to each resource type.
|
||||
// - `path` is the absolute path to the module defining the Resource Watcher class,
|
||||
// - `parentProcessResource` is an optional boolean to be set to true for resources to be
|
||||
// watched from the parent process.
|
||||
// Also see the following for loop which will add new attributes for each type:
|
||||
// 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.
|
||||
const Resources = {
|
||||
// These lists are specific for the parent process and each target type.
|
||||
const FrameTargetResources = augmentResourceDictionary({
|
||||
[TYPES.CONSOLE_MESSAGE]: {
|
||||
path: "devtools/server/actors/resources/console-messages",
|
||||
},
|
||||
@ -43,12 +44,56 @@ const Resources = {
|
||||
[TYPES.PLATFORM_MESSAGE]: {
|
||||
path: "devtools/server/actors/resources/platform-messages",
|
||||
},
|
||||
};
|
||||
});
|
||||
const ParentProcessResources = augmentResourceDictionary({});
|
||||
|
||||
for (const resource of Object.values(Resources)) {
|
||||
resource.watchers = new WeakMap();
|
||||
function augmentResourceDictionary(dict) {
|
||||
for (const resource of Object.values(dict)) {
|
||||
resource.watchers = new WeakMap();
|
||||
|
||||
loader.lazyRequireGetter(resource, "WatcherClass", resource.path);
|
||||
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 watcherOrTargetActor
|
||||
* Either a WatcherActor or a TargetActor which can be listening to a resource.
|
||||
*/
|
||||
function getResourceTypeDictionary(watcherOrTargetActor) {
|
||||
const { typeName } = watcherOrTargetActor;
|
||||
if (typeName == "watcher") {
|
||||
return ParentProcessResources;
|
||||
}
|
||||
const { targetType } = watcherOrTargetActor;
|
||||
switch (targetType) {
|
||||
case "frame":
|
||||
return FrameTargetResources;
|
||||
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 watcherOrTargetActor
|
||||
* Either a WatcherActor or a TargetActor which can be listening to a resource.
|
||||
* @param String resourceType
|
||||
* The resource type to be observed.
|
||||
*/
|
||||
function getResourceTypeEntry(watcherOrTargetActor, resourceType) {
|
||||
const dict = getResourceTypeDictionary(watcherOrTargetActor);
|
||||
if (!(resourceType in dict)) {
|
||||
throw new Error(
|
||||
`Unsupported resource type '${resourceType}' for ${watcherOrTargetActor.typeName}`
|
||||
);
|
||||
}
|
||||
return dict[resourceType];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -68,12 +113,17 @@ for (const resource of Object.values(Resources)) {
|
||||
* List of all type of resource to listen to.
|
||||
*/
|
||||
function watchResources(watcherOrTargetActor, 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 } = watcherOrTargetActor;
|
||||
if (targetType) {
|
||||
resourceTypes = getResourceTypesForTargetType(resourceTypes, targetType);
|
||||
}
|
||||
for (const resourceType of resourceTypes) {
|
||||
if (!(resourceType in Resources)) {
|
||||
throw new Error(`Unsupported resource type '${resourceType}'`);
|
||||
}
|
||||
// Pull all info about this resource type from `Resources` global object
|
||||
const { watchers, WatcherClass } = Resources[resourceType];
|
||||
const { watchers, WatcherClass } = getResourceTypeEntry(
|
||||
watcherOrTargetActor,
|
||||
resourceType
|
||||
);
|
||||
|
||||
// Ignore resources we're already listening to
|
||||
if (watchers.has(watcherOrTargetActor)) {
|
||||
@ -88,52 +138,31 @@ function watchResources(watcherOrTargetActor, resourceTypes) {
|
||||
watchers.set(watcherOrTargetActor, watcher);
|
||||
}
|
||||
}
|
||||
exports.watchResources = watchResources;
|
||||
|
||||
function getParentProcessResourceTypes(resourceTypes) {
|
||||
return resourceTypes.filter(resourceType => {
|
||||
if (!(resourceType in Resources)) {
|
||||
throw new Error(`Unsupported resource type '${resourceType}'`);
|
||||
}
|
||||
return !!Resources[resourceType].parentProcessResource;
|
||||
return resourceType in ParentProcessResources;
|
||||
});
|
||||
}
|
||||
function getContentProcessResourceTypes(resourceTypes) {
|
||||
return resourceTypes.filter(resourceType => {
|
||||
if (!(resourceType in Resources)) {
|
||||
throw new Error(`Unsupported resource type '${resourceType}'`);
|
||||
}
|
||||
return !Resources[resourceType].parentProcessResource;
|
||||
exports.getParentProcessResourceTypes = getParentProcessResourceTypes;
|
||||
|
||||
function getResourceTypesForTargetType(resourceTypes, targetType) {
|
||||
if (targetType == Targets.TYPES.FRAME) {
|
||||
return resourceTypes.filter(resourceType => {
|
||||
return resourceType in FrameTargetResources;
|
||||
});
|
||||
}
|
||||
throw new Error(`Unsupported target type ${targetType}`);
|
||||
}
|
||||
exports.getResourceTypesForTargetType = getResourceTypesForTargetType;
|
||||
|
||||
function hasResourceTypesForTargets(resourceTypes) {
|
||||
return resourceTypes.some(resourceType => {
|
||||
return resourceType in FrameTargetResources;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* See `watchResources` jsdoc.
|
||||
*
|
||||
* This one is to be called from the target process/thread.
|
||||
* This is called by DevToolsFrameChild.
|
||||
*/
|
||||
function watchTargetResources(targetActor, resourceTypes) {
|
||||
const contentProcessTypes = getContentProcessResourceTypes(resourceTypes);
|
||||
watchResources(targetActor, contentProcessTypes);
|
||||
}
|
||||
exports.watchTargetResources = watchTargetResources;
|
||||
|
||||
/**
|
||||
* See `watchResources` jsdoc.
|
||||
* This one is to be called from the parent process.
|
||||
* This is called by WatcherActor.
|
||||
*
|
||||
* @return Array<String>
|
||||
* List of all resource types that aren't watched from the parent process.
|
||||
*/
|
||||
function watchParentProcessResources(watcherActor, resourceTypes) {
|
||||
const parentProcessTypes = getParentProcessResourceTypes(resourceTypes);
|
||||
watchResources(watcherActor, parentProcessTypes);
|
||||
|
||||
return resourceTypes.filter(
|
||||
resource => !parentProcessTypes.includes(resource)
|
||||
);
|
||||
}
|
||||
exports.watchParentProcessResources = watchParentProcessResources;
|
||||
exports.hasResourceTypesForTargets = hasResourceTypesForTargets;
|
||||
|
||||
/**
|
||||
* Stop watching for a list of resource types.
|
||||
@ -145,11 +174,11 @@ exports.watchParentProcessResources = watchParentProcessResources;
|
||||
*/
|
||||
function unwatchResources(watcherOrTargetActor, resourceTypes) {
|
||||
for (const resourceType of resourceTypes) {
|
||||
if (!(resourceType in Resources)) {
|
||||
throw new Error(`Unsupported resource type '${resourceType}'`);
|
||||
}
|
||||
// Pull all info about this resource type from `Resources` global object
|
||||
const { watchers } = Resources[resourceType];
|
||||
const { watchers } = getResourceTypeEntry(
|
||||
watcherOrTargetActor,
|
||||
resourceType
|
||||
);
|
||||
|
||||
const watcher = watchers.get(watcherOrTargetActor);
|
||||
if (watcher) {
|
||||
@ -158,44 +187,18 @@ function unwatchResources(watcherOrTargetActor, resourceTypes) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See `unwatchResources` jsdoc.
|
||||
* This one is to be called from the target process/thread.
|
||||
* This is called by DevToolsFrameChild.
|
||||
*/
|
||||
function unwatchTargetResources(targetActor, resourceTypes) {
|
||||
const contentProcessTypes = getContentProcessResourceTypes(resourceTypes);
|
||||
unwatchResources(targetActor, contentProcessTypes);
|
||||
}
|
||||
exports.unwatchTargetResources = unwatchTargetResources;
|
||||
|
||||
/**
|
||||
* See `unwatchResources` jsdoc.
|
||||
* This one is to be called from the parent process.
|
||||
* This is called by WatcherActor.
|
||||
*
|
||||
* @return Array<String>
|
||||
* List of all resource types that aren't watched from the parent process.
|
||||
*/
|
||||
function unwatchParentProcessResources(watcherActor, resourceTypes) {
|
||||
const parentProcessTypes = getParentProcessResourceTypes(resourceTypes);
|
||||
unwatchResources(watcherActor, parentProcessTypes);
|
||||
|
||||
return resourceTypes.filter(
|
||||
resource => !parentProcessTypes.includes(resource)
|
||||
);
|
||||
}
|
||||
exports.unwatchParentProcessResources = unwatchParentProcessResources;
|
||||
exports.unwatchResources = unwatchResources;
|
||||
|
||||
/**
|
||||
* Stop watching for all watched resources on a given actor.
|
||||
*
|
||||
* @param Actor watcherOrTargetActor
|
||||
* The related actor, already passed to watchTargetResources or watchParentProcessResources.
|
||||
* The related actor, already passed to watchResources.
|
||||
*/
|
||||
function unwatchAllTargetResources(watcherOrTargetActor) {
|
||||
for (const { watchers } of Object.values(Resources)) {
|
||||
for (const { watchers } of Object.values(
|
||||
getResourceTypeDictionary(watcherOrTargetActor)
|
||||
)) {
|
||||
const watcher = watchers.get(watcherOrTargetActor);
|
||||
if (watcher) {
|
||||
watcher.destroy();
|
||||
@ -220,11 +223,7 @@ exports.unwatchAllTargetResources = unwatchAllTargetResources;
|
||||
* The resource watcher instance, defined in devtools/server/actors/resources/
|
||||
*/
|
||||
function getResourceWatcher(watcherOrTargetActor, resourceType) {
|
||||
if (!(resourceType in Resources)) {
|
||||
throw new Error(`Unsupported resource type '${resourceType}'`);
|
||||
}
|
||||
// Pull the watchers Map for this resource type from `Resources` global object
|
||||
const { watchers } = Resources[resourceType];
|
||||
const { watchers } = getResourceTypeEntry(watcherOrTargetActor, resourceType);
|
||||
|
||||
return watchers.get(watcherOrTargetActor);
|
||||
}
|
||||
|
@ -315,11 +315,11 @@ const browsingContextTargetPrototype = {
|
||||
* which is a JSM and doesn't have a reference to a DevTools Loader.
|
||||
*/
|
||||
watchTargetResources(resourceTypes) {
|
||||
return Resources.watchTargetResources(this, resourceTypes);
|
||||
return Resources.watchResources(this, resourceTypes);
|
||||
},
|
||||
|
||||
unwatchTargetResources(resourceTypes) {
|
||||
return Resources.unwatchTargetResources(this, resourceTypes);
|
||||
return Resources.unwatchResources(this, resourceTypes);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -233,17 +233,19 @@ exports.WatcherActor = protocol.ActorClassWithSpec(watcherSpec, {
|
||||
async watchResources(resourceTypes) {
|
||||
// First process resources which have to be listened from the parent process
|
||||
// (the watcher actor always runs in the parent process)
|
||||
const contentProcessResourceTypes = Resources.watchParentProcessResources(
|
||||
Resources.watchResources(
|
||||
this,
|
||||
resourceTypes
|
||||
Resources.getParentProcessResourceTypes(resourceTypes)
|
||||
);
|
||||
|
||||
// Bail out early if all resources were watched from parent process
|
||||
if (contentProcessResourceTypes.length == 0) {
|
||||
// Bail out early if all resources were watched from parent process.
|
||||
// In this scenario, we do not need to update these resource types in the WatcherRegistry
|
||||
// as targets do not care about them.
|
||||
if (!Resources.hasResourceTypesForTargets(resourceTypes)) {
|
||||
return;
|
||||
}
|
||||
|
||||
WatcherRegistry.watchResources(this, contentProcessResourceTypes);
|
||||
WatcherRegistry.watchResources(this, resourceTypes);
|
||||
|
||||
// Fetch resources from all existing targets
|
||||
for (const targetType in TARGET_HELPERS) {
|
||||
@ -255,10 +257,14 @@ exports.WatcherActor = protocol.ActorClassWithSpec(watcherSpec, {
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
const targetResourceTypes = Resources.getResourceTypesForTargetType(
|
||||
resourceTypes,
|
||||
targetType
|
||||
);
|
||||
const targetHelperModule = TARGET_HELPERS[targetType];
|
||||
await targetHelperModule.watchResources({
|
||||
watcher: this,
|
||||
resourceTypes: contentProcessResourceTypes,
|
||||
resourceTypes: targetResourceTypes,
|
||||
});
|
||||
}
|
||||
|
||||
@ -281,11 +287,15 @@ exports.WatcherActor = protocol.ActorClassWithSpec(watcherSpec, {
|
||||
* We will eventually get rid of this code once all targets are properly supported by
|
||||
* the Watcher Actor and we have target helpers for all of them.
|
||||
*/
|
||||
const frameResourceTypes = Resources.getResourceTypesForTargetType(
|
||||
resourceTypes,
|
||||
Targets.TYPES.FRAME
|
||||
);
|
||||
const targetActor = this.browserElement
|
||||
? TargetActorRegistry.getTargetActor(this.browserId)
|
||||
: TargetActorRegistry.getParentProcessTargetActor();
|
||||
if (targetActor) {
|
||||
await targetActor.watchTargetResources(resourceTypes);
|
||||
await targetActor.watchTargetResources(frameResourceTypes);
|
||||
}
|
||||
},
|
||||
|
||||
@ -298,21 +308,21 @@ exports.WatcherActor = protocol.ActorClassWithSpec(watcherSpec, {
|
||||
unwatchResources(resourceTypes) {
|
||||
// First process resources which are listened from the parent process
|
||||
// (the watcher actor always runs in the parent process)
|
||||
const contentProcessResourceTypes = Resources.unwatchParentProcessResources(
|
||||
Resources.unwatchResources(
|
||||
this,
|
||||
resourceTypes
|
||||
Resources.getParentProcessResourceTypes(resourceTypes)
|
||||
);
|
||||
|
||||
// Bail out early if all resources were watched from parent process.
|
||||
// These parent process resource types won't be saved in the WatcherRegistry because content processes
|
||||
// do not need to know about them.
|
||||
if (contentProcessResourceTypes.length == 0) {
|
||||
// Bail out early if all resources were all watched from parent process.
|
||||
// In this scenario, we do not need to update these resource types in the WatcherRegistry
|
||||
// as targets do not care about them.
|
||||
if (!Resources.hasResourceTypesForTargets(resourceTypes)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isWatchingResources = WatcherRegistry.unwatchResources(
|
||||
this,
|
||||
contentProcessResourceTypes
|
||||
resourceTypes
|
||||
);
|
||||
if (!isWatchingResources) {
|
||||
return;
|
||||
@ -330,20 +340,28 @@ exports.WatcherActor = protocol.ActorClassWithSpec(watcherSpec, {
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
const targetResourceTypes = Resources.getResourceTypesForTargetType(
|
||||
resourceTypes,
|
||||
targetType
|
||||
);
|
||||
const targetHelperModule = TARGET_HELPERS[targetType];
|
||||
targetHelperModule.unwatchResources({
|
||||
watcher: this,
|
||||
resourceTypes: contentProcessResourceTypes,
|
||||
resourceTypes: targetResourceTypes,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// See comment in watchResources.
|
||||
const frameResourceTypes = Resources.getResourceTypesForTargetType(
|
||||
resourceTypes,
|
||||
Targets.TYPES.FRAME
|
||||
);
|
||||
const targetActor = this.browserElement
|
||||
? TargetActorRegistry.getTargetActor(this.browserId)
|
||||
: TargetActorRegistry.getParentProcessTargetActor();
|
||||
if (targetActor) {
|
||||
targetActor.unwatchTargetResources(contentProcessResourceTypes);
|
||||
targetActor.unwatchTargetResources(frameResourceTypes);
|
||||
}
|
||||
|
||||
// Unregister the JS Window Actor if there is no more DevTools code observing any target/resource
|
||||
|
@ -23,12 +23,10 @@ add_task(
|
||||
};
|
||||
// But both tabs and processes will be going through the ConsoleMessages module
|
||||
// We force watching for console message first,
|
||||
Resources.watchTargetResources(targetActor, [
|
||||
Resources.TYPES.CONSOLE_MESSAGE,
|
||||
]);
|
||||
Resources.watchResources(targetActor, [Resources.TYPES.CONSOLE_MESSAGE]);
|
||||
// And then listen for resource RDP event.
|
||||
// Bug 1646677: But we should probably migrate this test to ResourceWatcher so that
|
||||
// we don't have to hack the server side via Resource.watchTargetResources call.
|
||||
// we don't have to hack the server side via Resource.watchResources call.
|
||||
targetActor.on("resource-available-form", resources => {
|
||||
if (resources[0].resourceType == Resources.TYPES.CONSOLE_MESSAGE) {
|
||||
lastMessage = resources[0].message;
|
||||
|
@ -23,12 +23,10 @@ add_task(
|
||||
};
|
||||
// But both tabs and processes will be going through the ConsoleMessages module
|
||||
// We force watching for console message first,
|
||||
Resources.watchTargetResources(targetActor, [
|
||||
Resources.TYPES.CONSOLE_MESSAGE,
|
||||
]);
|
||||
Resources.watchResources(targetActor, [Resources.TYPES.CONSOLE_MESSAGE]);
|
||||
// And then listen for resource RDP event.
|
||||
// Bug 1646677: But we should probably migrate this test to ResourceWatcher so that
|
||||
// we don't have to hack the server side via Resource.watchTargetResources call.
|
||||
// we don't have to hack the server side via Resource.watchResources call.
|
||||
targetActor.on("resource-available-form", resources => {
|
||||
if (resources[0].resourceType == Resources.TYPES.CONSOLE_MESSAGE) {
|
||||
lastMessage = resources[0].message;
|
||||
|
@ -23,12 +23,10 @@ add_task(
|
||||
};
|
||||
// But both tabs and processes will be going through the ConsoleMessages module
|
||||
// We force watching for console message first,
|
||||
Resources.watchTargetResources(targetActor, [
|
||||
Resources.TYPES.CONSOLE_MESSAGE,
|
||||
]);
|
||||
Resources.watchResources(targetActor, [Resources.TYPES.CONSOLE_MESSAGE]);
|
||||
// And then listen for resource RDP event.
|
||||
// Bug 1646677: But we should probably migrate this test to ResourceWatcher so that
|
||||
// we don't have to hack the server side via Resource.watchTargetResources call.
|
||||
// we don't have to hack the server side via Resource.watchResources call.
|
||||
targetActor.on("resource-available-form", resources => {
|
||||
if (resources[0].resourceType == Resources.TYPES.CONSOLE_MESSAGE) {
|
||||
lastMessage = resources[0].message;
|
||||
|
Loading…
x
Reference in New Issue
Block a user