Several tests introspect workers through the nsIWorkerDebuggerManager -- by
enumerating it, matching a worker by its (relative) script URL, or checking
process placement. With the parent-process RemoteWorkerDebugger on by default a
worker registers its debugger in the parent process, which reports the absolute
script URL, registers asynchronously over IPC, and (for content workers) is no
longer visible in the content-process manager. These tests assume the local
WorkerDebugger's synchronous, content-process, relative-URL behaviour and fail.
Pin dom.worker.remoteDebugger.enabled to false in these tests so they keep using
the local WorkerDebugger. These checks are debugger-location-dependent: the
legacy worker-introspection path still needs to be modernized to the
RemoteWorkerDebugger model, and each pin (or test) should be removed once the
local WorkerDebugger mechanism is removed:
- devtools/client/shared/test/browser_dbg_listworkers.js
- devtools/shared/webconsole/test/chrome/test_console_worker.html
- devtools/shared/webconsole/test/chrome/test_jsterm_autocomplete.html
- dom/workers/test/test_shutdownCheck.xhtml
- dom/workers/test/browser_privilegedmozilla_remoteworker.js
- dom/workers/test/browser_serviceworker_fetch_new_process.js
- dom/base/crashtests/eventSource_invalid_scheme_worker_shutdown.html
- toolkit/components/extensions/test/browser/browser_ext_background_serviceworker.js
The extension service-worker xpcshell helper (head_service_worker.js) is instead
updated to watch the parent-process WorkerDebuggerManager when the pref is on, so
test_ext_background_service_worker keeps working through the remote debugger
rather than being pinned.
dom_worker_helper.js matches a debugger by absolute or relative script URL (the
parent reports the absolute URL), and test_WorkerDebugger.xhtml only asserts
nsIWorkerDebugger.window for the local debugger, since a remote worker has no
window in the parent process.
Run the worker tests that exercise worker debugging under both pref values, so
both the local WorkerDebugger and the parent-process RemoteWorkerDebugger stay
covered on CI regardless of the channel default. dom_worker_helper.js gains an
addTaskWithBothWorkerDebuggers helper, and each of the following runs once with
dom.worker.remoteDebugger.enabled=false and once with it true:
- the WorkerDebugger* / WorkerDebuggerGlobalScope* chrome mochitests (except
test_WorkerDebugger_frozen.xhtml)
- dom/workers/test/browser_WorkerDebugger{,_waiting}.initialize.js
- devtools browser_worker_tracer.js and browser_target_command_tab_workers.js
- the WebDriver BiDi browser_WorkerListener_{chromeWorker,serviceWorker,
sharedWorker}.js tests
A few worker tests keep global state that does not reset cleanly between the two
runs -- browser_target_command_browser_workers.js enumerates every worker in the
browser and its parent-process workers linger, test_WorkerDebugger_frozen.xhtml
manipulates frozen/suspended state, and browser_WorkerListener.js drives multiple
tabs -- so they keep running at the channel default rather than twice.
Add tests covering the parent-process worker debugging that the
RemoteWorkerDebugger enables:
- browser_parent_worker_nested.js: a chrome worker and the nested worker it
spawns both register through the parent-process RemoteWorkerDebugger, and the
nested worker's debugger URL is resolved to an absolute URL.
- browser_resources_thread_states_parent_worker.js: THREAD_STATE resources let
DevTools interactively pause and resume a chrome worker via main-process
(Browser Toolbox) commands.
Differential Revision: https://phabricator.services.mozilla.com/D306652
162 lines
6.4 KiB
HTML
162 lines
6.4 KiB
HTML
<?xml version="1.0"?>
|
|
<!--
|
|
Any copyright is dedicated to the Public Domain.
|
|
http://creativecommons.org/publicdomain/zero/1.0/
|
|
-->
|
|
<window title="Test for WorkerDebugger"
|
|
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
|
|
|
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
|
|
<script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
|
|
<script type="application/javascript" src="dom_worker_helper.js"/>
|
|
|
|
<script type="application/javascript">
|
|
<![CDATA[
|
|
|
|
const WORKER_URL = "WorkerDebugger_worker.js";
|
|
const CHILD_WORKER_URL = "WorkerDebugger_childWorker.js";
|
|
const SHARED_WORKER_URL = "WorkerDebugger_sharedWorker.js";
|
|
|
|
addTaskWithBothWorkerDebuggers(
|
|
async function runTest() {
|
|
info("Create a top-level chrome worker that creates a non-top-level " +
|
|
"content worker and wait for their debuggers to be registered.");
|
|
let promise = waitForMultiple([
|
|
waitForRegister(WORKER_URL),
|
|
waitForRegister(CHILD_WORKER_URL)
|
|
]);
|
|
worker = new ChromeWorker(WORKER_URL, { name: "worker name" });
|
|
let [dbg, childDbg] = await promise;
|
|
|
|
info("Check that the top-level chrome worker debugger has the " +
|
|
"correct properties.");
|
|
is(dbg.isChrome, true,
|
|
"Chrome worker debugger should be chrome.");
|
|
is(dbg.parent, null,
|
|
"Top-level debugger should not have parent.");
|
|
is(dbg.type, Ci.nsIWorkerDebugger.TYPE_DEDICATED,
|
|
"Chrome worker debugger should be dedicated.");
|
|
if (dbg.isRemote) {
|
|
// A remote worker debugger lives in the parent process and has no
|
|
// window object there; consumers use windowIDs instead (bug 1944240).
|
|
is(dbg.window, null,
|
|
"Remote top-level worker debugger has no window in the parent.");
|
|
} else {
|
|
is(dbg.window, window,
|
|
"Top-level dedicated worker debugger should have window.");
|
|
}
|
|
is(dbg.name, "worker name",
|
|
"Top-level worker name is exposed via name attribute.");
|
|
|
|
info("Check that the non-top-level content worker debugger has the " +
|
|
"correct properties.");
|
|
is(childDbg.isChrome, false,
|
|
"Content worker debugger should be content.");
|
|
is(childDbg.parent, dbg,
|
|
"Non-top-level worker debugger should have parent.");
|
|
is(childDbg.type, Ci.nsIWorkerDebugger.TYPE_DEDICATED,
|
|
"Content worker debugger should be dedicated.");
|
|
if (childDbg.isRemote) {
|
|
is(childDbg.window, null,
|
|
"Remote non-top-level worker debugger has no window in the parent.");
|
|
} else {
|
|
is(childDbg.window, window,
|
|
"Non-top-level worker debugger should have window.");
|
|
}
|
|
is(childDbg.name, "",
|
|
"Non-top-level worker doesn't have a custom name");
|
|
|
|
info("Terminate the top-level chrome worker and the non-top-level " +
|
|
"content worker, and wait for their debuggers to be " +
|
|
"unregistered and closed.");
|
|
promise = waitForMultiple([
|
|
waitForUnregister(CHILD_WORKER_URL),
|
|
waitForDebuggerClose(childDbg),
|
|
waitForUnregister(WORKER_URL),
|
|
waitForDebuggerClose(dbg),
|
|
]);
|
|
worker.terminate();
|
|
await promise;
|
|
|
|
info("Create a shared worker and wait for its debugger to be " +
|
|
"registered");
|
|
promise = waitForRegister(SHARED_WORKER_URL);
|
|
worker = new SharedWorker(SHARED_WORKER_URL);
|
|
let sharedDbg = await promise;
|
|
|
|
info("Check that the shared worker debugger has the correct " +
|
|
"properties.");
|
|
is(sharedDbg.isChrome, false,
|
|
"Shared worker debugger should be content.");
|
|
is(sharedDbg.parent, null,
|
|
"Shared worker debugger should not have parent.");
|
|
is(sharedDbg.type, Ci.nsIWorkerDebugger.TYPE_SHARED,
|
|
"Shared worker debugger should be shared.");
|
|
is(sharedDbg.window, null,
|
|
"Shared worker debugger should not have window.");
|
|
|
|
info("Create a shared worker with the same URL and check that its " +
|
|
"debugger is not registered again.");
|
|
let listener = {
|
|
onRegistered () {
|
|
ok(false,
|
|
"Shared worker debugger should not be registered again.");
|
|
},
|
|
};
|
|
wdm.addListener(listener);
|
|
|
|
let secondWorker = new SharedWorker(SHARED_WORKER_URL);
|
|
|
|
info("Send a message to the shared worker to tell it to close " +
|
|
"itself, and wait for its debugger to be closed.");
|
|
promise = waitForMultiple([
|
|
waitForUnregister(SHARED_WORKER_URL),
|
|
waitForDebuggerClose(sharedDbg)
|
|
]);
|
|
secondWorker.port.start();
|
|
secondWorker.port.postMessage("close");
|
|
await promise;
|
|
worker = null;
|
|
secondWorker = null;
|
|
|
|
info("Create a SharedWorker again for the infinite loop test.")
|
|
promise = waitForRegister(SHARED_WORKER_URL);
|
|
// Give it an explicit name so we don't reuse the above SharedWorker.
|
|
worker = new SharedWorker(SHARED_WORKER_URL, "loopy");
|
|
sharedDbg = await promise;
|
|
|
|
info("Send a message to the shared worker to tell it to close " +
|
|
"itself, then loop forever, and wait for its debugger to be closed.");
|
|
promise = waitForMultiple([
|
|
waitForUnregister(SHARED_WORKER_URL),
|
|
waitForDebuggerClose(sharedDbg)
|
|
]);
|
|
|
|
// When the closing process begins, we schedule a timer to terminate
|
|
// the worker in case it's in an infinite loop, which is exactly what
|
|
// we do in this test. The default delay is 30 seconds. This test
|
|
// previously waited 15 seconds for reasons that were poorly justified.
|
|
// We now set it to 100ms because we just want to make sure that the
|
|
// timeout mechanism to force cancellation from the parent properly
|
|
// works (as long as the parent thread isn't blocked).
|
|
await SpecialPowers.pushPrefEnv({"set": [[ "dom.worker.canceling.timeoutMilliseconds", 100 ]]});
|
|
|
|
worker.port.start();
|
|
worker.port.postMessage("close_loop");
|
|
await promise;
|
|
|
|
wdm.removeListener(listener);
|
|
}
|
|
);
|
|
|
|
]]>
|
|
</script>
|
|
|
|
<body xmlns="http://www.w3.org/1999/xhtml">
|
|
<p id="display"></p>
|
|
<div id="content" style="display:none;"></div>
|
|
<pre id="test"></pre>
|
|
</body>
|
|
<label id="test-result"/>
|
|
</window>
|