зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1594750 - Distinguish workers in the TargetList. r=jdescottes.
Differential Revision: https://phabricator.services.mozilla.com/D73803
This commit is contained in:
Родитель
dde23f5029
Коммит
db468890fa
|
@ -47,6 +47,14 @@ class WorkerTargetFront extends TargetMixin(
|
|||
return this.url.split("/").pop();
|
||||
}
|
||||
|
||||
get isDedicatedWorker() {
|
||||
return this.type === Ci.nsIWorkerDebugger.TYPE_DEDICATED;
|
||||
}
|
||||
|
||||
get isSharedWorker() {
|
||||
return this.type === Ci.nsIWorkerDebugger.TYPE_SHARED;
|
||||
}
|
||||
|
||||
get isServiceWorker() {
|
||||
return this.type === Ci.nsIWorkerDebugger.TYPE_SERVICE;
|
||||
}
|
||||
|
|
|
@ -63,7 +63,11 @@ class EvaluationContextSelector extends Component {
|
|||
return "resource://devtools/client/debugger/images/globe-small.svg";
|
||||
}
|
||||
|
||||
if (target.type === TARGET_TYPES.WORKER) {
|
||||
if (
|
||||
target.type === TARGET_TYPES.WORKER ||
|
||||
target.type === TARGET_TYPES.SHARED_WORKER ||
|
||||
target.type === TARGET_TYPES.SERVICE_WORKER
|
||||
) {
|
||||
return "resource://devtools/client/debugger/images/worker.svg";
|
||||
}
|
||||
|
||||
|
@ -103,12 +107,16 @@ class EvaluationContextSelector extends Component {
|
|||
let mainTarget;
|
||||
const frames = [];
|
||||
const contentProcesses = [];
|
||||
const workers = [];
|
||||
const dedicatedWorkers = [];
|
||||
const sharedWorkers = [];
|
||||
const serviceWorkers = [];
|
||||
|
||||
const dict = {
|
||||
[TARGET_TYPES.FRAME]: frames,
|
||||
[TARGET_TYPES.PROCESS]: contentProcesses,
|
||||
[TARGET_TYPES.WORKER]: workers,
|
||||
[TARGET_TYPES.WORKER]: dedicatedWorkers,
|
||||
[TARGET_TYPES.SHARED_WORKER]: sharedWorkers,
|
||||
[TARGET_TYPES.SERVICE_WORKER]: serviceWorkers,
|
||||
};
|
||||
|
||||
for (const target of targets) {
|
||||
|
@ -123,25 +131,13 @@ class EvaluationContextSelector extends Component {
|
|||
|
||||
const items = [mainTarget];
|
||||
|
||||
if (frames.length > 0) {
|
||||
items.push(
|
||||
MenuItem({ role: "menuseparator", key: "frames-separator" }),
|
||||
...frames
|
||||
);
|
||||
}
|
||||
|
||||
if (contentProcesses.length > 0) {
|
||||
items.push(
|
||||
MenuItem({ role: "menuseparator", key: "process-separator" }),
|
||||
...contentProcesses
|
||||
);
|
||||
}
|
||||
|
||||
if (workers.length > 0) {
|
||||
items.push(
|
||||
MenuItem({ role: "menuseparator", key: "worker-separator" }),
|
||||
...workers
|
||||
);
|
||||
for (const [targetType, menuItems] of Object.entries(dict)) {
|
||||
if (menuItems.length > 0) {
|
||||
items.push(
|
||||
MenuItem({ role: "menuseparator", key: `${targetType}-separator` }),
|
||||
...menuItems
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return MenuList(
|
||||
|
|
|
@ -143,14 +143,13 @@ class LegacyImplementationWorkers {
|
|||
// Listen for worker which will be created later
|
||||
const listener = this._workerListChanged.bind(this, targetFront);
|
||||
this.targetsListeners.set(targetFront, listener);
|
||||
|
||||
// If this is the browser toolbox, we have to listen from the RootFront
|
||||
// (see comment in _workerListChanged)
|
||||
if (targetFront.isParentProcess) {
|
||||
this.rootFront.on("workerListChanged", listener);
|
||||
} else {
|
||||
targetFront.on("workerListChanged", listener);
|
||||
}
|
||||
// But also process the already existing ones
|
||||
const front = targetFront.isParentProcess ? this.rootFront : targetFront;
|
||||
front.on("workerListChanged", listener);
|
||||
|
||||
// We also need to process the already existing workers
|
||||
await this._workerListChanged(targetFront);
|
||||
}
|
||||
|
||||
|
@ -169,19 +168,28 @@ class LegacyImplementationWorkers {
|
|||
this.targetsListeners.delete(targetFront);
|
||||
}
|
||||
|
||||
_supportWorkerTarget(workerTarget) {
|
||||
// subprocess workers are ignored because they take several seconds to
|
||||
// attach to when opening the browser toolbox. See bug 1594597.
|
||||
// When attaching we get the following error:
|
||||
// JavaScript error: resource://devtools/server/startup/worker.js,
|
||||
// line 37: NetworkError: WorkerDebuggerGlobalScope.loadSubScript: Failed to load worker script at resource://devtools/shared/worker/loader.js (nsresult = 0x805e0006)
|
||||
return (
|
||||
workerTarget.isDedicatedWorker &&
|
||||
!workerTarget.url.startsWith(
|
||||
"resource://gre/modules/subprocess/subprocess_worker"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
async _workerListChanged(targetFront) {
|
||||
let workers;
|
||||
// Browser Toolbox codepath
|
||||
if (targetFront.isParentProcess) {
|
||||
// Query workers from the Root Front instead of the ParentProcessTarget
|
||||
// as the ParentProcess Target filters out the workers to only show the one from the top level window,
|
||||
// whereas we expect the one from all the windows, and also the window-less ones.
|
||||
({ workers } = await this.rootFront.listWorkers());
|
||||
} else {
|
||||
// Content Toolbox codepath
|
||||
//TODO: expose SW of the page, maybe optionally?
|
||||
({ workers } = await targetFront.listWorkers());
|
||||
}
|
||||
// If we're in the Browser Toolbox, query workers from the Root Front instead of the
|
||||
// ParentProcessTarget as the ParentProcess Target filters out the workers to only
|
||||
// show the one from the top level window, whereas we expect the one from all the
|
||||
// windows, and also the window-less ones.
|
||||
// TODO: For Content Toolbox, expose SW of the page, maybe optionally?
|
||||
const front = targetFront.isParentProcess ? this.rootFront : targetFront;
|
||||
const { workers } = await front.listWorkers();
|
||||
|
||||
// Fetch the list of already existing worker targets for this process target front.
|
||||
const existingTargets = this.targetsByProcess.get(targetFront);
|
||||
|
@ -196,23 +204,20 @@ class LegacyImplementationWorkers {
|
|||
existingTargets.delete(target);
|
||||
}
|
||||
}
|
||||
const promises = workers
|
||||
// subprocess workers are ignored because they take several seconds to
|
||||
// attach to when opening the browser toolbox. See bug 1594597.
|
||||
// When attaching we get the following error:
|
||||
// JavaScript error: resource://devtools/server/startup/worker.js, line 37: NetworkError: WorkerDebuggerGlobalScope.loadSubScript: Failed to load worker script at resource://devtools/shared/worker/loader.js (nsresult = 0x805e0006)
|
||||
.filter(
|
||||
workerTarget =>
|
||||
!workerTarget.url.startsWith(
|
||||
"resource://gre/modules/subprocess/subprocess_worker"
|
||||
)
|
||||
)
|
||||
.filter(workerTarget => !existingTargets.has(workerTarget))
|
||||
.map(async workerTarget => {
|
||||
// Add the new worker targets to the local list
|
||||
existingTargets.add(workerTarget);
|
||||
await this.onTargetAvailable(workerTarget);
|
||||
});
|
||||
|
||||
const promises = [];
|
||||
for (const workerTarget of workers) {
|
||||
if (
|
||||
!this._supportWorkerTarget(workerTarget) ||
|
||||
existingTargets.has(workerTarget)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add the new worker targets to the local list
|
||||
existingTargets.add(workerTarget);
|
||||
promises.push(this.onTargetAvailable(workerTarget));
|
||||
}
|
||||
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
@ -262,6 +267,18 @@ class LegacyImplementationWorkers {
|
|||
}
|
||||
}
|
||||
|
||||
class LegacyImplementationSharedWorkers extends LegacyImplementationWorkers {
|
||||
_supportWorkerTarget(workerTarget) {
|
||||
return workerTarget.isSharedWorker;
|
||||
}
|
||||
}
|
||||
|
||||
class LegacyImplementationServiceWorkers extends LegacyImplementationWorkers {
|
||||
_supportWorkerTarget(workerTarget) {
|
||||
return workerTarget.isServiceWorker;
|
||||
}
|
||||
}
|
||||
|
||||
class TargetList {
|
||||
/**
|
||||
* This class helps managing, iterating over and listening for Targets.
|
||||
|
@ -324,6 +341,16 @@ class TargetList {
|
|||
this._onTargetAvailable,
|
||||
this._onTargetDestroyed
|
||||
),
|
||||
shared_worker: new LegacyImplementationSharedWorkers(
|
||||
this,
|
||||
this._onTargetAvailable,
|
||||
this._onTargetDestroyed
|
||||
),
|
||||
service_worker: new LegacyImplementationServiceWorkers(
|
||||
this,
|
||||
this._onTargetAvailable,
|
||||
this._onTargetDestroyed
|
||||
),
|
||||
};
|
||||
|
||||
// Public flag to allow listening for workers even if the fission pref is off
|
||||
|
@ -414,6 +441,18 @@ class TargetList {
|
|||
if (this.listenForWorkers && !types.includes(TargetList.TYPES.WORKER)) {
|
||||
types.push(TargetList.TYPES.WORKER);
|
||||
}
|
||||
if (
|
||||
this.listenForWorkers &&
|
||||
!types.includes(TargetList.TYPES.SHARED_WORKER)
|
||||
) {
|
||||
types.push(TargetList.TYPES.SHARED_WORKER);
|
||||
}
|
||||
if (
|
||||
this.listenForWorkers &&
|
||||
!types.includes(TargetList.TYPES.SERVICE_WORKER)
|
||||
) {
|
||||
types.push(TargetList.TYPES.SERVICE_WORKER);
|
||||
}
|
||||
// If no pref are set to true, nor is listenForWorkers set to true,
|
||||
// we won't listen for any additional target. Only the top level target
|
||||
// will be managed. We may still do target-switching.
|
||||
|
@ -474,14 +513,27 @@ class TargetList {
|
|||
const { typeName } = target;
|
||||
if (typeName == "browsingContextTarget") {
|
||||
return TargetList.TYPES.FRAME;
|
||||
} else if (
|
||||
}
|
||||
|
||||
if (
|
||||
typeName == "contentProcessTarget" ||
|
||||
typeName == "parentProcessTarget"
|
||||
) {
|
||||
return TargetList.TYPES.PROCESS;
|
||||
} else if (typeName == "workerTarget") {
|
||||
}
|
||||
|
||||
if (typeName == "workerTarget") {
|
||||
if (target.isSharedWorker) {
|
||||
return TargetList.TYPES.SHARED_WORKER;
|
||||
}
|
||||
|
||||
if (target.isServiceWorker) {
|
||||
return TargetList.TYPES.SERVICE_WORKER;
|
||||
}
|
||||
|
||||
return TargetList.TYPES.WORKER;
|
||||
}
|
||||
|
||||
throw new Error("Unsupported target typeName: " + typeName);
|
||||
}
|
||||
|
||||
|
@ -645,6 +697,8 @@ TargetList.TYPES = TargetList.prototype.TYPES = {
|
|||
PROCESS: "process",
|
||||
FRAME: "frame",
|
||||
WORKER: "worker",
|
||||
SHARED_WORKER: "shared_worker",
|
||||
SERVICE_WORKER: "service_worker",
|
||||
};
|
||||
TargetList.ALL_TYPES = TargetList.prototype.ALL_TYPES = Object.values(
|
||||
TargetList.TYPES
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
// Test the TargetList API around workers
|
||||
|
||||
const { TargetList } = require("devtools/shared/resources/target-list");
|
||||
const { TYPES } = TargetList;
|
||||
|
||||
const FISSION_TEST_URL = URL_ROOT_SSL + "fission_document.html";
|
||||
const CHROME_WORKER_URL = CHROME_URL_ROOT + "test_worker.js";
|
||||
|
@ -46,42 +47,78 @@ async function testBrowserWorkers(mainRoot) {
|
|||
const targetList = new TargetList(mainRoot, target);
|
||||
await targetList.startListening();
|
||||
|
||||
// Very naive sanity check against getAllTargets(worker)
|
||||
const workers = await targetList.getAllTargets(TargetList.TYPES.WORKER);
|
||||
// Very naive sanity check against getAllTargets(workerType)
|
||||
info("Check that getAllTargets returned the expected targets");
|
||||
const workers = await targetList.getAllTargets(TYPES.WORKER);
|
||||
const hasWorker = workers.find(workerTarget => {
|
||||
return workerTarget.url == CHROME_WORKER_URL + "#simple-worker";
|
||||
});
|
||||
ok(hasWorker, "retrieve the target for the worker");
|
||||
|
||||
const hasSharedWorker = workers.find(workerTarget => {
|
||||
const sharedWorkers = await targetList.getAllTargets(TYPES.SHARED_WORKER);
|
||||
const hasSharedWorker = sharedWorkers.find(workerTarget => {
|
||||
return workerTarget.url == CHROME_WORKER_URL + "#shared-worker";
|
||||
});
|
||||
ok(hasSharedWorker, "retrieve the target for the shared worker");
|
||||
|
||||
const hasServiceWorker = workers.find(workerTarget => {
|
||||
const serviceWorkers = await targetList.getAllTargets(TYPES.SERVICE_WORKER);
|
||||
const hasServiceWorker = serviceWorkers.find(workerTarget => {
|
||||
return workerTarget.url == SERVICE_WORKER_URL;
|
||||
});
|
||||
ok(hasServiceWorker, "retrieve the target for the service worker");
|
||||
|
||||
// Check that calling getAllTargets(worker) return the same target instances
|
||||
const workers2 = await targetList.getAllTargets(TargetList.TYPES.WORKER);
|
||||
info(
|
||||
"Check that calling getAllTargets again return the same target instances"
|
||||
);
|
||||
const workers2 = await targetList.getAllTargets(TYPES.WORKER);
|
||||
const sharedWorkers2 = await targetList.getAllTargets(TYPES.SHARED_WORKER);
|
||||
const serviceWorkers2 = await targetList.getAllTargets(TYPES.SERVICE_WORKER);
|
||||
is(workers2.length, workers.length, "retrieved the same number of workers");
|
||||
is(
|
||||
sharedWorkers2.length,
|
||||
sharedWorkers.length,
|
||||
"retrieved the same number of shared workers"
|
||||
);
|
||||
is(
|
||||
serviceWorkers2.length,
|
||||
serviceWorkers.length,
|
||||
"retrieved the same number of service workers"
|
||||
);
|
||||
|
||||
function sortFronts(f1, f2) {
|
||||
return f1.actorID < f2.actorID;
|
||||
}
|
||||
workers.sort(sortFronts);
|
||||
workers2.sort(sortFronts);
|
||||
sharedWorkers.sort(sortFronts);
|
||||
sharedWorkers2.sort(sortFronts);
|
||||
serviceWorkers.sort(sortFronts);
|
||||
serviceWorkers2.sort(sortFronts);
|
||||
|
||||
for (let i = 0; i < workers.length; i++) {
|
||||
is(workers[i], workers2[i], `worker ${i} targets are the same`);
|
||||
}
|
||||
for (let i = 0; i < sharedWorkers2.length; i++) {
|
||||
is(
|
||||
sharedWorkers[i],
|
||||
sharedWorkers2[i],
|
||||
`shared worker ${i} targets are the same`
|
||||
);
|
||||
}
|
||||
for (let i = 0; i < serviceWorkers2.length; i++) {
|
||||
is(
|
||||
serviceWorkers[i],
|
||||
serviceWorkers2[i],
|
||||
`service worker ${i} targets are the same`
|
||||
);
|
||||
}
|
||||
|
||||
// Assert that watchTargets will call the create callback for all existing workers
|
||||
info(
|
||||
"Check that watchTargets will call the create callback for all existing workers"
|
||||
);
|
||||
const targets = [];
|
||||
const onAvailable = async ({ type, targetFront, isTopLevel }) => {
|
||||
is(
|
||||
type,
|
||||
TargetList.TYPES.WORKER,
|
||||
ok(
|
||||
type === TYPES.WORKER ||
|
||||
type === TYPES.SHARED_WORKER ||
|
||||
type === TYPES.SERVICE_WORKER,
|
||||
"We are only notified about worker targets"
|
||||
);
|
||||
ok(
|
||||
|
@ -90,23 +127,33 @@ async function testBrowserWorkers(mainRoot) {
|
|||
);
|
||||
targets.push(targetFront);
|
||||
};
|
||||
await targetList.watchTargets([TargetList.TYPES.WORKER], onAvailable);
|
||||
await targetList.watchTargets(
|
||||
[TYPES.WORKER, TYPES.SHARED_WORKER, TYPES.SERVICE_WORKER],
|
||||
onAvailable
|
||||
);
|
||||
is(
|
||||
targets.length,
|
||||
workers.length,
|
||||
workers.length + sharedWorkers.length + serviceWorkers.length,
|
||||
"retrieved the same number of workers via watchTargets"
|
||||
);
|
||||
|
||||
workers.sort(sortFronts);
|
||||
targets.sort(sortFronts);
|
||||
for (let i = 0; i < workers.length; i++) {
|
||||
const allWorkers = workers
|
||||
.concat(sharedWorkers, serviceWorkers)
|
||||
.sort(sortFronts);
|
||||
|
||||
for (let i = 0; i < allWorkers.length; i++) {
|
||||
is(
|
||||
workers[i],
|
||||
allWorkers[i],
|
||||
targets[i],
|
||||
`worker ${i} targets are the same via watchTargets`
|
||||
);
|
||||
}
|
||||
targetList.unwatchTargets([TargetList.TYPES.WORKER], onAvailable);
|
||||
|
||||
targetList.unwatchTargets(
|
||||
[TYPES.WORKER, TYPES.SHARED_WORKER, TYPES.SERVICE_WORKER],
|
||||
onAvailable
|
||||
);
|
||||
|
||||
// Create a new worker and see if the worker target is reported
|
||||
const onWorkerCreated = new Promise(resolve => {
|
||||
|
@ -114,10 +161,10 @@ async function testBrowserWorkers(mainRoot) {
|
|||
if (targets.includes(targetFront)) {
|
||||
return;
|
||||
}
|
||||
targetList.unwatchTargets([TargetList.TYPES.WORKER], onAvailable2);
|
||||
targetList.unwatchTargets([TYPES.WORKER], onAvailable2);
|
||||
resolve(targetFront);
|
||||
};
|
||||
targetList.watchTargets([TargetList.TYPES.WORKER], onAvailable2);
|
||||
targetList.watchTargets([TYPES.WORKER], onAvailable2);
|
||||
});
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const worker2 = new Worker(CHROME_WORKER_URL + "#second");
|
||||
|
@ -130,9 +177,9 @@ async function testBrowserWorkers(mainRoot) {
|
|||
"This worker target is about the new worker"
|
||||
);
|
||||
|
||||
const workers3 = await targetList.getAllTargets(TargetList.TYPES.WORKER);
|
||||
const workers3 = await targetList.getAllTargets(TYPES.WORKER);
|
||||
const hasWorker2 = workers3.find(
|
||||
worderTarget => workerTarget.url == CHROME_WORKER_URL + "#second"
|
||||
({ url }) => url == `${CHROME_WORKER_URL}#second`
|
||||
);
|
||||
ok(hasWorker2, "retrieve the target for tab via getAllTargets");
|
||||
|
||||
|
@ -160,27 +207,27 @@ async function testTabWorkers(mainRoot, tab) {
|
|||
await targetList.startListening();
|
||||
|
||||
// Check that calling getAllTargets(workers) return the same target instances
|
||||
const workers = await targetList.getAllTargets(TargetList.TYPES.WORKER);
|
||||
const workers = await targetList.getAllTargets(TYPES.WORKER);
|
||||
is(workers.length, 1, "retrieved the worker");
|
||||
is(workers[0].url, WORKER_URL, "The first worker is the page worker");
|
||||
|
||||
// Assert that watchTargets will call the create callback for all existing workers
|
||||
const targets = [];
|
||||
const onAvailable = async ({ type, targetFront, isTopLevel }) => {
|
||||
is(
|
||||
type,
|
||||
TargetList.TYPES.WORKER,
|
||||
"We are only notified about worker targets"
|
||||
);
|
||||
is(type, TYPES.WORKER, "We are only notified about worker targets");
|
||||
ok(!isTopLevel, "The workers are never top level");
|
||||
targets.push(targetFront);
|
||||
};
|
||||
await targetList.watchTargets([TargetList.TYPES.WORKER], onAvailable);
|
||||
await targetList.watchTargets([TYPES.WORKER], onAvailable);
|
||||
is(targets.length, 1, "retrieved just the worker");
|
||||
is(targets[0], workers[0], "Got the exact same target front");
|
||||
targetList.unwatchTargets([TargetList.TYPES.WORKER], onAvailable);
|
||||
targetList.unwatchTargets([TYPES.WORKER], onAvailable);
|
||||
|
||||
targetList.stopListening();
|
||||
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
}
|
||||
|
||||
function sortFronts(f1, f2) {
|
||||
return f1.actorID < f2.actorID;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче