Skip to content

Commit

Permalink
Web Inspector doesn't show nested workers
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=255402
<rdar://problem/108322385>

Reviewed by Alexey Proskuryakov.

This allows for sub-`Worker` to appear in Web Inspector in the same way as the parent `Worker`.

* Source/JavaScriptCore/inspector/protocol/Worker.json:
* Source/WebCore/inspector/InspectorController.cpp:
(WebCore::InspectorController::createLazyAgents):
* Source/WebCore/inspector/WorkerInspectorController.cpp:
(WebCore::WorkerInspectorController::createLazyAgents):
* Source/WebCore/inspector/agents/InspectorWorkerAgent.h:
* Source/WebCore/inspector/agents/InspectorWorkerAgent.cpp:
(WebCore::InspectorWorkerAgent::InspectorWorkerAgent):
(WebCore::InspectorWorkerAgent::enable):
(WebCore::InspectorWorkerAgent::connectToAllWorkerInspectorProxiesForPage): Deleted.
* Source/WebCore/inspector/agents/page/PageWorkerAgent.h: Added.
* Source/WebCore/inspector/agents/page/PageWorkerAgent.cpp: Added.
(WebCore::PageWorkerAgent::PageWorkerAgent):
(WebCore::PageWorkerAgent::connectToAllWorkerInspectorProxies):
* Source/WebCore/inspector/agents/worker/WorkerWorkerAgent.h: Added.
* Source/WebCore/inspector/agents/worker/WorkerWorkerAgent.cpp: Added.
(WebCore::WorkerWorkerAgent::WorkerWorkerAgent):
(WebCore::WorkerWorkerAgent::connectToAllWorkerInspectorProxies):
Add support for the `Worker` inspector protocol domain inside `"worker"` inspector targets.

* Source/WebCore/bindings/js/WorkerModuleScriptLoader.h:
* Source/WebCore/bindings/js/WorkerModuleScriptLoader.cpp:
(WebCore::WorkerModuleScriptLoader::load):
(WebCore::WorkerModuleScriptLoader::notifyFinished):
* Source/WebCore/fileapi/FileReaderLoader.h:
* Source/WebCore/fileapi/FileReaderLoader.cpp:
(WebCore::FileReaderLoader::didReceiveResponse):
(WebCore::FileReaderLoader::didFinishLoading):
(WebCore::FileReaderLoader::didFail):
* Source/WebCore/inspector/agents/InspectorNetworkAgent.cpp:
(WebCore::InspectorThreadableLoaderClient::didReceiveResponse):
(WebCore::InspectorThreadableLoaderClient::didFinishLoading):
(WebCore::InspectorThreadableLoaderClient::didFail):
* Source/WebCore/loader/DocumentThreadableLoader.cpp:
(WebCore::DocumentThreadableLoader::cancel):
(WebCore::DocumentThreadableLoader::didReceiveResponse):
(WebCore::DocumentThreadableLoader::didFinishLoading):
(WebCore::DocumentThreadableLoader::didFail):
(WebCore::DocumentThreadableLoader::preflightFailure):
(WebCore::DocumentThreadableLoader::logErrorAndFail):
* Source/WebCore/loader/ThreadableLoaderClient.h:
(WebCore::ThreadableLoaderClient::didReceiveResponse):
(WebCore::ThreadableLoaderClient::didFinishLoading):
(WebCore::ThreadableLoaderClient::didFail):
* Source/WebCore/loader/ThreadableLoaderClientWrapper.h:
(WebCore::ThreadableLoaderClientWrapper::didReceiveResponse):
(WebCore::ThreadableLoaderClientWrapper::didFinishLoading):
(WebCore::ThreadableLoaderClientWrapper::didFail):
(WebCore::ThreadableLoaderClientWrapper::didReceiveAuthenticationCancellation): Deleted.
* Source/WebCore/loader/WorkerThreadableLoader.h:
* Source/WebCore/loader/WorkerThreadableLoader.cpp:
(WebCore::WorkerThreadableLoader::MainThreadBridge::cancel):
(WebCore::WorkerThreadableLoader::MainThreadBridge::didReceiveResponse):
(WebCore::WorkerThreadableLoader::MainThreadBridge::didReceiveData):
(WebCore::WorkerThreadableLoader::MainThreadBridge::didFinishLoading):
(WebCore::WorkerThreadableLoader::MainThreadBridge::didFail):
* Source/WebCore/Modules/fetch/FetchLoader.h:
* Source/WebCore/Modules/fetch/FetchLoader.cpp:
(WebCore::FetchLoader::didReceiveResponse):
(WebCore::FetchLoader::didFinishLoading):
(WebCore::FetchLoader::didFail):
* Source/WebCore/Modules/notifications/NotificationResourcesLoader.h:
* Source/WebCore/Modules/notifications/NotificationResourcesLoader.cpp:
(WebCore::NotificationResourcesLoader::ResourceLoader::didReceiveResponse):
(WebCore::NotificationResourcesLoader::ResourceLoader::didFinishLoading):
(WebCore::NotificationResourcesLoader::ResourceLoader::didFail):
* Source/WebCore/page/EventSource.h:
* Source/WebCore/page/EventSource.cpp:
(WebCore::EventSource::didReceiveResponse):
(WebCore::EventSource::didFinishLoading):
(WebCore::EventSource::didFail):
* Source/WebCore/workers/Worker.h:
* Source/WebCore/workers/Worker.cpp:
(WebCore::Worker::didReceiveResponse):
(WebCore::Worker::notifyFinished):
* Source/WebCore/workers/WorkerFontLoadRequest.h:
* Source/WebCore/workers/WorkerFontLoadRequest.cpp:
(WebCore::WorkerFontLoadRequest::didReceiveResponse):
(WebCore::WorkerFontLoadRequest::didFinishLoading):
(WebCore::WorkerFontLoadRequest::didFail):
* Source/WebCore/workers/WorkerScriptLoader.h:
* Source/WebCore/workers/WorkerScriptLoader.cpp:
(WebCore::WorkerScriptLoader::didReceiveResponse):
(WebCore::WorkerScriptLoader::didFinishLoading):
(WebCore::WorkerScriptLoader::didFail):
(WebCore::WorkerScriptLoader::notifyError):
(WebCore::WorkerScriptLoader::notifyFinished):
* Source/WebCore/workers/WorkerScriptLoaderClient.h:
* Source/WebCore/workers/service/ServiceWorkerJob.h:
* Source/WebCore/workers/service/ServiceWorkerJob.cpp:
(WebCore::ServiceWorkerJob::didReceiveResponse):
(WebCore::ServiceWorkerJob::notifyFinished):
* Source/WebCore/workers/shared/SharedWorkerScriptLoader.h:
* Source/WebCore/workers/shared/SharedWorkerScriptLoader.cpp:
(WebCore::SharedWorkerScriptLoader::didReceiveResponse):
(WebCore::SharedWorkerScriptLoader::notifyFinished):
* Source/WebCore/xml/XMLHttpRequest.h:
* Source/WebCore/xml/XMLHttpRequest.cpp:
(WebCore::XMLHttpRequest::didFail):
(WebCore::XMLHttpRequest::didFinishLoading):
(WebCore::XMLHttpRequest::didReceiveResponse):
Pass along the toplevel `ScriptExecutionContextIdentifier` so that clients can use that to get the toplevel context and do things with it (e.g. "notify the `Document` that this load has finished").
This is used by Web Inspector because `Worker` do not have an `InspectorNetworkAgent`, meaning that any `InspectorInstrumentation` that pipe through to it need to be done on the same thread as the `Document`.

* Source/WebCore/workers/WorkerGlobalScope.cpp:
(WebCore::WorkerGlobalScope::importScripts):
Remove redundant `InspectorInstrumentation` call.

* Source/WebInspectorUI/UserInterface/Controllers/WorkerManager.js:
(WI.WorkerManager):
(WI.WorkerManager.prototype.workerCreated):
(WI.WorkerManager.prototype.workerTerminated):
(WI.WorkerManager.prototype.dispatchMessageFromWorker):
Keep a multimap of sub-`Worker` for each `Worker`.

* Source/WebInspectorUI/UserInterface/Controllers/DebuggerManager.js:
(WI.DebuggerManager.prototype.scriptDidParse):
* Source/WebInspectorUI/UserInterface/Models/Script.js:
(WI.Script.prototype.couldBeMainResource): Added.
(WI.Script.prototype._resolveResource):
`InspectorInstrumentation::didReceiveScriptResponse` is not always called in time before `Network.responseReceived` due to the fact that the `Worker` was created on another thread (i.e. sub-`Worker`), the `WI.Script` was loaded from memory cache, or some other reason. As such, we cannot rely on `WI.Resource.Type.Script` being the only way to associate a `WI.Resource` with a `WI.Script`.  In the sub-`Worker` scenario, do a second pass to see if any `WI.Resource` with the same URL happen to have a script MIME type (even though they're marked as some other type) and associate with that.

* Source/WebInspectorUI/UserInterface/Views/QuickConsole.js:
(WI.QuickConsole):
(WI.QuickConsole.prototype.closed):
(WI.QuickConsole.prototype._populateActiveExecutionContextNavigationItemContextMenu):
(WI.QuickConsole.prototype._handleTargetAdded): Added.
Show sub-`Worker` underneath and indented from the parent `Worker`.
Also refresh the execution context picker when new targets (e.g. `Worker`) are added.

* Source/WebInspectorUI/UserInterface/Views/SourcesNavigationSidebarPanel.js:
(WI.SourcesNavigationSidebarPanel.prototype._handleTreeSelectionDidChange):
Only use the associated `WI.Resource` when showing a `WI.Script` if it's not the main resource. Otherwise, things like `WI.WorkerTreeElement` that use the `WI.Script` as the `representedObject` won't be found when searching for a `WI.TreeElement` matching the `representedObject`.

* Source/WebCore/inspector/InspectorInstrumentation.h:
* Source/WebCore/inspector/InspectorInstrumentation.cpp:
(WebCore::InspectorInstrumentation::instrumentingAgents):
(WebCore::InspectorInstrumentation::willSendRequest):
(WebCore::InspectorInstrumentation::didReceiveResourceResponse):
(WebCore::InspectorInstrumentation::didReceiveData):
(WebCore::InspectorInstrumentation::didFinishLoading):
(WebCore::InspectorInstrumentation::didFailLoading):
(WebCore::InspectorInstrumentation::didReceiveScriptResponse):
Drive-by: Mark instrumentation only used by `ServiceWorker` as such.

* Source/WebCore/Sources.txt:
* Source/WebCore/WebCore.xcodeproj/project.pbxproj:

* LayoutTests/inspector/worker/resources/subworker-manager.js: Added.
* LayoutTests/inspector/worker/resources/worker-utilities.js:
(TestPage.registerInitializer.awaitTarget): Added.
* LayoutTests/inspector/worker/console-basic-subworker.html: Added.
* LayoutTests/inspector/worker/console-basic-subworker-expected.txt: Added.
* LayoutTests/inspector/worker/debugger-pause-subworker.html: Added.
* LayoutTests/inspector/worker/debugger-pause-subworker-expected.txt: Added.
* LayoutTests/inspector/worker/resources-in-subworker.html: Added.
* LayoutTests/inspector/worker/resources-in-subworker-expected.txt: Added.
* LayoutTests/inspector/worker/runtime-basic-subworker.html: Added.
* LayoutTests/inspector/worker/runtime-basic-subworker-expected.txt: Added.
* LayoutTests/platform/mac-site-isolation/TestExpectations:

Canonical link: https://commits.webkit.org/279793@main
  • Loading branch information
dcrousso committed Jun 7, 2024
1 parent 6b87830 commit b5d3f9b
Show file tree
Hide file tree
Showing 59 changed files with 1,349 additions and 228 deletions.
167 changes: 167 additions & 0 deletions LayoutTests/inspector/worker/console-basic-subworker-expected.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
Test for ConsoleAgent in a Subworker.


== Running test suite: Subworker.Console.basic
-- Running test case: Subworker.Console.log
PASS: ConsoleMessage parameter RemoteObjects should be from the right Worker target.
{
"_source": "console-api",
"_level": "log",
"_messageText": "log!",
"_type": "log",
"_url": "inspector/worker/resources/worker-console.js",
"_line": 4,
"_column": 20,
"_sourceCodeLocation": "<filtered>",
"_repeatCount": 1,
"_parameters": [
{
"_type": "string",
"_description": "log!",
"_value": "log!"
},
{
"_type": "object",
"_subtype": "array",
"_objectId": "<filtered>",
"_description": "Array",
"_size": 4,
"_preview": {
"_type": "object",
"_subtype": "array",
"_description": "Array",
"_lossless": false,
"_overflow": false,
"_size": 4,
"_properties": [
{
"_name": "0",
"_type": "object",
"_value": "DedicatedWorkerGlobalScope"
},
{
"_name": "1",
"_type": "object",
"_value": "WorkerLocation"
},
{
"_name": "2",
"_type": "number",
"_value": "123"
},
{
"_name": "3",
"_type": "symbol",
"_value": "Symbol()"
}
],
"_entries": null
}
}
],
"_stackTrace": "<filtered>",
"_request": null,
"_timestamp": "<filtered>"
}

-- Running test case: Subworker.Console.warn
{
"_source": "console-api",
"_level": "warning",
"_messageText": "warning!",
"_type": "log",
"_url": "inspector/worker/resources/worker-console.js",
"_line": 7,
"_column": 21,
"_sourceCodeLocation": "<filtered>",
"_repeatCount": 1,
"_parameters": [
{
"_type": "string",
"_description": "warning!",
"_value": "warning!"
}
],
"_stackTrace": "<filtered>",
"_request": null,
"_timestamp": "<filtered>"
}

-- Running test case: Subworker.Console.error
{
"_source": "console-api",
"_level": "error",
"_messageText": "error!",
"_type": "log",
"_url": "inspector/worker/resources/worker-console.js",
"_line": 10,
"_column": 22,
"_sourceCodeLocation": "<filtered>",
"_repeatCount": 1,
"_parameters": [
{
"_type": "string",
"_description": "error!",
"_value": "error!"
}
],
"_stackTrace": "<filtered>",
"_request": null,
"_timestamp": "<filtered>"
}

-- Running test case: Subworker.Console.assert
{
"_source": "console-api",
"_level": "error",
"_messageText": "Assertion Failure",
"_type": "assert",
"_url": "inspector/worker/resources/worker-console.js",
"_line": 14,
"_column": 23,
"_sourceCodeLocation": "<filtered>",
"_repeatCount": 1,
"_parameters": [
{
"_type": "string",
"_description": "Assertion Failure",
"_value": "Assertion Failure"
}
],
"_stackTrace": "<filtered>",
"_request": null,
"_timestamp": "<filtered>"
}

-- Running test case: Subworker.Console.time
{
"_source": "console-api",
"_level": "debug",
"_messageText": "name: <filtered>ms",
"_type": "timing",
"_url": "inspector/worker/resources/worker-console.js",
"_line": 18,
"_column": 24,
"_sourceCodeLocation": "<filtered>",
"_repeatCount": 1,
"_stackTrace": "<filtered>",
"_request": null,
"_timestamp": "<filtered>"
}

-- Running test case: Subworker.Console.count
{
"_source": "console-api",
"_level": "debug",
"_messageText": "default: 1",
"_type": "log",
"_url": "inspector/worker/resources/worker-console.js",
"_line": 21,
"_column": 22,
"_sourceCodeLocation": "<filtered>",
"_repeatCount": 1,
"_stackTrace": "<filtered>",
"_request": null,
"_timestamp": "<filtered>"
}

101 changes: 101 additions & 0 deletions LayoutTests/inspector/worker/console-basic-subworker.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<!DOCTYPE html>
<html>
<head>
<script src="../../http/tests/inspector/resources/inspector-test.js"></script>
<script src="resources/worker-utilities.js"></script>
<script>
let worker = new Worker("resources/subworker-manager.js");
worker.postMessage({url: "worker-console.js", data: null});

function triggerConsoleMethodInSubworker(method) {
worker.postMessage({url: "worker-console.js", data: method});
}

async function test()
{
function consoleMessageJSONFilter(key, value) {
if (key === "_target" || key === "_hasChildren" || key === "_listeners")
return undefined;
if (key === "_objectId" || key === "_stackTrace" || key === "_sourceCodeLocation" || key === "_timestamp")
return "<filtered>";
if (key === "_url")
return sanitizeURL(value);
return value;
}

let mainTarget = WI.mainTarget;
let subworkerTarget = await window.awaitTarget((target) => target instanceof WI.WorkerTarget && target.displayName.endsWith("worker-console.js"));

let suite = InspectorTest.createAsyncSuite("Subworker.Console.basic");

function addConsoleTestCase({name, method, description, validate, preprocess}) {
suite.addTestCase({
name,
description,
async test() {
let [messageAddedEvent] = await Promise.all([
WI.consoleManager.awaitEvent(WI.ConsoleManager.Event.MessageAdded),
InspectorTest.evaluateInPage(`triggerConsoleMethodInSubworker("${method}")`),
]);

let {message} = messageAddedEvent.data;
InspectorTest.assert(message instanceof WI.ConsoleMessage);
if (validate)
validate(message);
if (preprocess)
preprocess(message);
InspectorTest.log(JSON.stringify(message, consoleMessageJSONFilter, " "));
},
});
}

addConsoleTestCase({
name: "Subworker.Console.log",
description: "console.log with multiple values should work.",
method: "log",
validate(message) {
InspectorTest.expectEqual(message.parameters[1].target, subworkerTarget, "ConsoleMessage parameter RemoteObjects should be from the right Worker target.");
}
});

addConsoleTestCase({
name: "Subworker.Console.warn",
description: "console.warn should produce a warning message.",
method: "warn",
});

addConsoleTestCase({
name: "Subworker.Console.error",
description: "console.error should produce an error message.",
method: "error",
});

addConsoleTestCase({
name: "Subworker.Console.assert",
description: "console.assert should produce an assertion message.",
method: "assert",
});

addConsoleTestCase({
name: "Subworker.Console.time",
description: "console.time/timeEnd should produce a timing message.",
method: "time",
preprocess(message) {
message._messageText = message._messageText.replace(/[0-9.]+/, "<filtered>");
}
});

addConsoleTestCase({
name: "Subworker.Console.count",
description: "console.count should produce a count message.",
method: "count",
});

suite.runTestCasesAndFinish();
}
</script>
</head>
<body onload="runTest()">
<p>Test for ConsoleAgent in a Subworker.</p>
</body>
</html>
73 changes: 73 additions & 0 deletions LayoutTests/inspector/worker/debugger-pause-subworker-expected.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
CONSOLE MESSAGE: TypeError: undefined is not an object (evaluating '[].x.x')
Ensure we can pause in Subworkers.


== Running test suite: Subworker.Debugger.Pause
-- Running test case: Subworker.Debugger.Pause.DebuggerStatement
Triggering pause...
PASS: Should be paused in a Subworker CallFrame.
PASS: Pause reason should be a debugger statement.
PAUSE AT triggerDebuggerStatement:3:5
0 function triggerDebuggerStatement() {
1 let before = 1;
-> 2 |debugger;
3 let after = 2;
4 }
5

Resuming...



-- Running test case: Subworker.Debugger.Pause.Breakpoint
Triggering pause...
PASS: Should be paused in a Subworker CallFrame.
PASS: Pause reason should be a breakpoint.
PAUSE AT triggerBreakpoint:9:5
5
6 function triggerBreakpoint() {
7 let alpha = 1;
-> 8 |let beta = 2; // BREAKPOINT
9 let gamma = 3;
10 }
11

Resuming...



-- Running test case: Subworker.Debugger.Pause.Exception
Triggering pause...
PASS: Should be paused in a Subworker CallFrame.
PASS: Pause reason should be an exception.
PAUSE AT triggerException:14:9
10 }
11
12 function triggerException() {
-> 13 [].x|.x;
14 }
15
16 function triggerAssertion() {

Resuming...


Uncaught exception in test page: TypeError: undefined is not an object (evaluating '[].x.x') [worker-debugger-pause.js:14]

-- Running test case: SubWorker.Debugger.Pause.Assert
Triggering pause...
PASS: Should be paused in a Subworker CallFrame.
PASS: Pause reason should be an exception.
PAUSE AT triggerAssertion:18:19
14 }
15
16 function triggerAssertion() {
-> 17 console.assert|(false, "Assertion");
18 }
19
20 onmessage = function(event) {

Resuming...



Loading

0 comments on commit b5d3f9b

Please sign in to comment.