Web Inspector: Associate Worker Resources with the Worker and not the Page
authorjoepeck@webkit.org <joepeck@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 10 Nov 2016 03:54:06 +0000 (03:54 +0000)
committerjoepeck@webkit.org <joepeck@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 10 Nov 2016 03:54:06 +0000 (03:54 +0000)
https://bugs.webkit.org/show_bug.cgi?id=164342
<rdar://problem/29075775>

Reviewed by Timothy Hatcher.

Source/JavaScriptCore:

* inspector/protocol/Network.json:
* inspector/protocol/Page.json:
Associate Resource data with a target.

Source/WebCore:

Test: inspector/worker/resources-in-worker.html

Provide a way to associate an initiator identifier with a ResourceRequest.
This will allow Web Inspector to identify who started particular resource
loads. This is important to associate Worker(...), importScript(...), and
XMLHttpRequest / Fetch loads with that specific Worker.

* platform/network/ResourceRequestBase.cpp:
(WebCore::ResourceRequestBase::setAsIsolatedCopy):
* platform/network/ResourceRequestBase.h:
(WebCore::ResourceRequestBase::initiatorIdentifier):
(WebCore::ResourceRequestBase::setInitiatorIdentifier):
Optional initiator identifier. Currently used only be Web Inspector.

* dom/ScriptExecutionContext.h:
(WebCore::ScriptExecutionContext::resourceRequestIdentifier):
Non-page execution contexts, like WorkerGlobalScope, should provide
a unique identifier that may be used to distinguish loads initiated
from within that context.

* xml/XMLHttpRequest.cpp:
(WebCore::XMLHttpRequest::createRequest):
* Modules/fetch/FetchLoader.cpp:
(WebCore::FetchLoader::start):
* Modules/fetch/FetchRequest.cpp:
(WebCore::FetchRequest::initializeWith):
XHR / Fetch loads should include the ScriptExecutionContext's
initiator identifier.

* workers/WorkerScriptLoader.cpp:
(WebCore::WorkerScriptLoader::WorkerScriptLoader):
(WebCore::WorkerScriptLoader::loadSynchronously):
(WebCore::WorkerScriptLoader::loadAsynchronously):
(WebCore::WorkerScriptLoader::createResourceRequest):
* workers/WorkerScriptLoader.h:
Provide a way to provide initiator identifier information for
Worker script loads. Currently this is `new Worker(...)` and
`importScripts(...)` resource loads.

* workers/Worker.cpp:
(WebCore::Worker::Worker):
(WebCore::Worker::create):
* workers/Worker.h:
* workers/WorkerGlobalScope.cpp:
(WebCore::WorkerGlobalScope::WorkerGlobalScope):
(WebCore::WorkerGlobalScope::importScripts):
* workers/WorkerGlobalScope.h:
Give Worker itself the unique identifier, because `new Worker(...)`
loads happen before the WorkerGlobalScript (ScriptExecutionContext)
is actually created, but we want to associate it with this Worker.

* workers/DedicatedWorkerGlobalScope.cpp:
(WebCore::DedicatedWorkerGlobalScope::create):
(WebCore::DedicatedWorkerGlobalScope::DedicatedWorkerGlobalScope):
* workers/DedicatedWorkerGlobalScope.h:
* workers/DedicatedWorkerThread.cpp:
(WebCore::DedicatedWorkerThread::DedicatedWorkerThread):
(WebCore::DedicatedWorkerThread::createWorkerGlobalScope):
* workers/DedicatedWorkerThread.h:
* workers/WorkerInspectorProxy.cpp:
(WebCore::WorkerInspectorProxy::WorkerInspectorProxy):
* workers/WorkerInspectorProxy.h:
* workers/WorkerMessagingProxy.cpp:
(WebCore::WorkerMessagingProxy::WorkerMessagingProxy):
(WebCore::WorkerMessagingProxy::startWorkerGlobalScope):
* workers/WorkerThread.cpp:
(WebCore::WorkerThreadStartupData::WorkerThreadStartupData):
(WebCore::WorkerThread::WorkerThread):
(WebCore::WorkerThread::workerThread):
* workers/WorkerThread.h:
Pass the MainThread's Worker identifier through to the WorkerGlobalScope
created on the WorkerThread. They should be the same identifier.

* inspector/InspectorNetworkAgent.cpp:
(WebCore::InspectorNetworkAgent::willSendRequest):
* inspector/InspectorPageAgent.cpp:
(WebCore::InspectorPageAgent::buildObjectForFrameTree):
Pass the initiator identifier data to the frontend. This identifier is
equivalent to a "target identifier" in the frontend. Currently the only
non-Page targets are Workers.

* loader/cache/CachedResourceLoader.cpp:
(WebCore::CachedResourceLoader::shouldContinueAfterNotifyingLoadedFromMemoryCache):
When using the memory cache we create a new resource request. Be sure
to copy over useful inspector data, like the initiator identifier,
from the original request.

* platform/network/cf/ResourceRequestCFNet.cpp:
(WebCore::ResourceRequest::updateFromDelegatePreservingOldProperties):
When rebuilding a ResourceRequest from NSURLRequest, copy over the
initiator identifier property that wouldn't otherwise have survived
the transition.

Source/WebInspectorUI:

A Target may have its own list of Resource. For example, Workers may
request any resources via XHR/Fetch. So we associate a ResourceCollection
with a Target, and ensure we show them in Web Inspector as you would expect.
At this point, Target starts acting like Frame. Target has a resourceCollection
and extraScriptsCollection just like Frame. Target has events for ResourceAdded
just like Frame.

Even though Resource loads are happening in Workers, the Network data
still comes from the Page's Network agent. The added "targetId" data
with the Resource will let us associate a Resoure with a Target.

When opening inspector after a page has loaded, the frontend loads Resources
via the Page.getResourceTree path. In this case, the frontend may be
informed of Resources for a Target that it does not know about yet. In
these cases, it sets them aside as Orphaned resources for a target. Later,
when that Target is created, it will adopt its Orphaned resources.

Places that used to listen to just Frame.Event.ResourceWasAdded should now
also listen for Target.Event.ResourceAdded to ensure it sees the resources
associated with non-page targets.

* UserInterface/Protocol/Target.js:
(WebInspector.Target):
(WebInspector.Target.prototype.get identifier):
(WebInspector.Target.prototype.get resourceCollection):
(WebInspector.Target.prototype.get extraScriptCollection):
(WebInspector.Target.prototype.addResource):
(WebInspector.Target.prototype.adoptResource):
(WebInspector.Target.prototype.addScript):
Give Target resource collections.

(WebInspector.MainTarget):
(WebInspector.MainTarget.prototype.get mainResource):
Pass through to the FrameResourceManager for the MainTarget.

(WebInspector.WorkerTarget):
(WebInspector.WorkerTarget.prototype.initialize):
Adopt orphaned resources on creation.

* UserInterface/Models/Resource.js:
(WebInspector.Resource):
(WebInspector.Resource.prototype.get target):
(WebInspector.Resource.prototype.get type):
Resource now has a Target. During creation, if there is a targetId
then we must produce a Target or null (orphaned).

(WebInspector.Resource.prototype.associateWithScript):
When associating a Resource with a Script, we can use this opportunity
to convert from an XML / Other type to Script.

* UserInterface/Models/Script.js:
(WebInspector.Script.prototype._resolveResource):
When associating Scripts with a resource we must associate resources
from within the proper Target. If it is the Main target we still use
the FrameResourceManager which keep searches across all Frames.

* UserInterface/Protocol/NetworkObserver.js:
(WebInspector.NetworkObserver.prototype.requestWillBeSent):
* UserInterface/Controllers/FrameResourceManager.js:
(WebInspector.FrameResourceManager.prototype.initialize):
(WebInspector.FrameResourceManager.prototype.frameDidNavigate):
(WebInspector.FrameResourceManager.prototype.resourceRequestWillBeSent):
(WebInspector.FrameResourceManager.prototype.resourceRequestWasServedFromMemoryCache):
(WebInspector.FrameResourceManager.prototype.resourceRequestDidReceiveResponse):
(WebInspector.FrameResourceManager.prototype.adoptOrphanedResourcesForTarget):
(WebInspector.FrameResourceManager.prototype._addNewResourceToFrameOrTarget):
(WebInspector.FrameResourceManager.prototype._addResourceToTarget):
(WebInspector.FrameResourceManager.prototype._createResource):
(WebInspector.FrameResourceManager.prototype._addFrameTreeFromFrameResourceTreePayload):
(WebInspector.FrameResourceManager.prototype._addOrphanedResource):
(WebInspector.FrameResourceManager.prototype._mainFrameDidChange):
(WebInspector.FrameResourceManager.prototype._addNewResourceToFrame): Deleted.
When creating Resources from Network events we may now have a targetId.
Once created a Resource must be associated with a Frame, Target, or orphaned.

* UserInterface/Main.html:
* UserInterface/Views/TargetTreeElement.js: Removed.
* UserInterface/Views/WorkerTreeElement.js: Added.
(WebInspector.WorkerTreeElement):
(WebInspector.WorkerTreeElement.prototype.get target):
(WebInspector.WorkerTreeElement.prototype.onexpand):
(WebInspector.WorkerTreeElement.prototype.oncollapse):
(WebInspector.WorkerTreeElement.prototype.onpopulate):
(WebInspector.WorkerTreeElement.prototype.updateSourceMapResources):
(WebInspector.WorkerTreeElement.prototype.onattach):
(WebInspector.WorkerTreeElement.prototype.compareChildTreeElements):
(WebInspector.WorkerTreeElement.prototype._handleContextMenuEvent):
(WebInspector.WorkerTreeElement.prototype._scriptAdded):
(WebInspector.WorkerTreeElement.prototype._resourceAdded):
Convert TargetTreeElement to WorkerTreeElement as that is clearer.
Behave like FrameTreeElement and populate resources on creation,
handle SourceMapResource, etc.

* UserInterface/Views/FolderizedTreeElement.js:
(WebInspector.FolderizedTreeElement.prototype.registerFolderizeSettings):
(WebInspector.FolderizedTreeElement.prototype._compareTreeElementsByMainTitle):
(WebInspector.FolderizedTreeElement.prototype._parentTreeElementForRepresentedObject):
If the display name for a folder is `null` then there is no folder,
and place such child tree elements at the top level. This will be
the case for a Worker's Script's, which we choose not to folderize.

* UserInterface/Controllers/DebuggerManager.js:
(WebInspector.DebuggerManager.prototype.scriptDidParse):
* UserInterface/Controllers/TargetManager.js:
(WebInspector.TargetManager.prototype.targetForIdentifier):

* UserInterface/Models/DefaultDashboard.js:
(WebInspector.DefaultDashboard):
* UserInterface/Controllers/TimelineManager.js:
(WebInspector.TimelineManager):
* UserInterface/Controllers/WorkerManager.js:
(WebInspector.WorkerManager.prototype.workerCreated):
* UserInterface/Views/OpenResourceDialog.js:
(WebInspector.OpenResourceDialog.prototype.didDismissDialog):
(WebInspector.OpenResourceDialog.prototype.didPresentDialog):
(WebInspector.OpenResourceDialog.prototype._addScriptsForTarget): Deleted.
(WebInspector.OpenResourceDialog.prototype._addResourcesForTarget): Added.
Ensure those that listen for Frame.Event.ResourceWasAdded now also
listen for Target.Event.ResourceAdded.

* UserInterface/Views/ContextMenuUtilities.js:
(WebInspector.appendContextMenuItemsForSourceCode):
(WebInspector.appendContextMenuItemsForResource): Deleted.
* UserInterface/Views/ResourceTimelineDataGridNode.js:
(WebInspector.ResourceTimelineDataGridNode.prototype.appendContextMenuItems):
* UserInterface/Views/ResourceTreeElement.js:
(WebInspector.ResourceTreeElement.prototype._updateTitles):
(WebInspector.ResourceTreeElement.prototype._handleContextMenuEvent):
Generalize ContextMenu helper to SourceCode so it can be used on a Script or Resource.

* UserInterface/Views/ResourceDetailsSidebarPanel.js:
(WebInspector.ResourceDetailsSidebarPanel.prototype.inspect):
When looking at a WorkerTarget's mainResource (Script) show the
Resource Details sidebar for its Resource.

* UserInterface/Views/ResourceSidebarPanel.js:
(WebInspector.ResourceSidebarPanel):
(WebInspector.ResourceSidebarPanel.prototype._scriptWasAdded):
(WebInspector.ResourceSidebarPanel.prototype._scriptsCleared):
(WebInspector.ResourceSidebarPanel.prototype._addTargetWithMainResource):
(WebInspector.ResourceSidebarPanel.prototype._targetRemoved):
(WebInspector.ResourceSidebarPanel.prototype._addScriptForNonMainTarget): Deleted.
Simplify ResourceSidebarPanel to only handle adding WorkerTreeElements,
which will do the rest of the work for their Resources/Scripts.

* UserInterface/Views/SourceCodeTreeElement.js:
(WebInspector.SourceCodeTreeElement.prototype.descendantResourceTreeElementTypeDidChange):
When we were changing the type of a resource, it would remove and re-insert.
This would collapse the parent if it was the only child in removal, and not
expand the parent when re-inserting. This ensures we re-expand.

LayoutTests:

* inspector/worker/resources-in-worker-expected.txt: Added.
* inspector/worker/resources-in-worker.html: Added.
* inspector/worker/resources/dataFetch.json: Added.
* inspector/worker/resources/dataXHR.json: Added.
* inspector/worker/resources/resource-utilities.js: Added.
(loadResourceXHR):
(loadResourceFetch):
* inspector/worker/resources/worker-resources.js: Added.
(importScript):
(onmessage):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@208520 268f45cc-cd09-0410-ab3c-d52691b4dbfc

59 files changed:
LayoutTests/ChangeLog
LayoutTests/inspector/worker/resources-in-worker-expected.txt [new file with mode: 0644]
LayoutTests/inspector/worker/resources-in-worker.html [new file with mode: 0644]
LayoutTests/inspector/worker/resources/dataFetch.json [new file with mode: 0644]
LayoutTests/inspector/worker/resources/dataXHR.json [new file with mode: 0644]
LayoutTests/inspector/worker/resources/resource-utilities.js [new file with mode: 0644]
LayoutTests/inspector/worker/resources/worker-resources.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/inspector/protocol/Network.json
Source/JavaScriptCore/inspector/protocol/Page.json
Source/WebCore/ChangeLog
Source/WebCore/Modules/fetch/FetchLoader.cpp
Source/WebCore/Modules/fetch/FetchRequest.cpp
Source/WebCore/dom/ScriptExecutionContext.h
Source/WebCore/inspector/InspectorNetworkAgent.cpp
Source/WebCore/inspector/InspectorPageAgent.cpp
Source/WebCore/loader/cache/CachedResourceLoader.cpp
Source/WebCore/platform/network/ResourceRequestBase.cpp
Source/WebCore/platform/network/ResourceRequestBase.h
Source/WebCore/platform/network/cf/ResourceRequestCFNet.cpp
Source/WebCore/workers/DedicatedWorkerGlobalScope.cpp
Source/WebCore/workers/DedicatedWorkerGlobalScope.h
Source/WebCore/workers/DedicatedWorkerThread.cpp
Source/WebCore/workers/DedicatedWorkerThread.h
Source/WebCore/workers/Worker.cpp
Source/WebCore/workers/Worker.h
Source/WebCore/workers/WorkerGlobalScope.cpp
Source/WebCore/workers/WorkerGlobalScope.h
Source/WebCore/workers/WorkerInspectorProxy.cpp
Source/WebCore/workers/WorkerInspectorProxy.h
Source/WebCore/workers/WorkerMessagingProxy.cpp
Source/WebCore/workers/WorkerScriptLoader.cpp
Source/WebCore/workers/WorkerScriptLoader.h
Source/WebCore/workers/WorkerThread.cpp
Source/WebCore/workers/WorkerThread.h
Source/WebCore/xml/XMLHttpRequest.cpp
Source/WebInspectorUI/ChangeLog
Source/WebInspectorUI/UserInterface/Controllers/DebuggerManager.js
Source/WebInspectorUI/UserInterface/Controllers/FrameResourceManager.js
Source/WebInspectorUI/UserInterface/Controllers/TargetManager.js
Source/WebInspectorUI/UserInterface/Controllers/TimelineManager.js
Source/WebInspectorUI/UserInterface/Controllers/WorkerManager.js
Source/WebInspectorUI/UserInterface/Main.html
Source/WebInspectorUI/UserInterface/Models/DefaultDashboard.js
Source/WebInspectorUI/UserInterface/Models/Resource.js
Source/WebInspectorUI/UserInterface/Models/Script.js
Source/WebInspectorUI/UserInterface/Protocol/NetworkObserver.js
Source/WebInspectorUI/UserInterface/Protocol/Target.js
Source/WebInspectorUI/UserInterface/Views/ContextMenuUtilities.js
Source/WebInspectorUI/UserInterface/Views/DebuggerSidebarPanel.js
Source/WebInspectorUI/UserInterface/Views/FolderizedTreeElement.js
Source/WebInspectorUI/UserInterface/Views/OpenResourceDialog.js
Source/WebInspectorUI/UserInterface/Views/ResourceDetailsSidebarPanel.js
Source/WebInspectorUI/UserInterface/Views/ResourceSidebarPanel.js
Source/WebInspectorUI/UserInterface/Views/ResourceTimelineDataGridNode.js
Source/WebInspectorUI/UserInterface/Views/ResourceTreeElement.js
Source/WebInspectorUI/UserInterface/Views/SourceCodeTreeElement.js
Source/WebInspectorUI/UserInterface/Views/TargetTreeElement.js [deleted file]
Source/WebInspectorUI/UserInterface/Views/WorkerTreeElement.js [new file with mode: 0644]

index f4d25af..9bdc63d 100644 (file)
@@ -1,3 +1,22 @@
+2016-11-09  Joseph Pecoraro  <pecoraro@apple.com>
+
+        Web Inspector: Associate Worker Resources with the Worker and not the Page
+        https://bugs.webkit.org/show_bug.cgi?id=164342
+        <rdar://problem/29075775>
+
+        Reviewed by Timothy Hatcher.
+
+        * inspector/worker/resources-in-worker-expected.txt: Added.
+        * inspector/worker/resources-in-worker.html: Added.
+        * inspector/worker/resources/dataFetch.json: Added.
+        * inspector/worker/resources/dataXHR.json: Added.
+        * inspector/worker/resources/resource-utilities.js: Added.
+        (loadResourceXHR):
+        (loadResourceFetch):
+        * inspector/worker/resources/worker-resources.js: Added.
+        (importScript):
+        (onmessage):
+
 2016-11-09  Brent Fulgham  <bfulgham@apple.com>
 
         Windows localStorage tests will fail until Bug 155185 is fixed.
diff --git a/LayoutTests/inspector/worker/resources-in-worker-expected.txt b/LayoutTests/inspector/worker/resources-in-worker-expected.txt
new file mode 100644 (file)
index 0000000..3c2bc41
--- /dev/null
@@ -0,0 +1,63 @@
+Test for Resources in a Worker.
+
+
+== Running test suite: Worker.Resources
+-- Running test case: Worker.Resource.Start
+PASS: Added Target should have Worker type.
+
+-- Running test case: Worker.Resource.XHR
+PASS: Worker Target should have 1 resource.
+PASS: Worker Target should dispatch ResourceAdded event.
+PASS: Resource should be XHR.
+PASS: Resource should be tied to the Worker Target.
+PASS: Resource has no parent frame.
+PASS: Worker Target should have 2 resources.
+RESOURCES:
+inspector/worker/resources/resource-utilities.js
+inspector/worker/resources/dataXHR.json
+
+-- Running test case: Main.Resource.XHR
+PASS: Worker Target should still have 2 resources.
+PASS: Frame should dispatch ResourceWasAdded event.
+PASS: Resource should be XHR.
+PASS: Resource should be tied to the Main Target.
+PASS: Resource parentFrame is the main frame.
+PASS: Worker Target should still have 2 resources.
+RESOURCES:
+inspector/worker/resources/resource-utilities.js
+inspector/worker/resources/dataXHR.json
+
+-- Running test case: Worker.Resource.Fetch
+PASS: Worker Target should have 2 resources.
+PASS: Worker Target should dispatch ResourceAdded event.
+PASS: Resource should be tied to the Worker Target.
+PASS: Resource has no parent frame.
+PASS: Worker Target should have 3 resources.
+RESOURCES:
+inspector/worker/resources/resource-utilities.js
+inspector/worker/resources/dataXHR.json
+inspector/worker/resources/dataFetch.json
+
+-- Running test case: Main.Resource.Fetch
+PASS: Worker Target should still have 3 resources.
+PASS: Frame should dispatch ResourceWasAdded event.
+PASS: Resource should be tied to the Main Target.
+PASS: Resource parentFrame is the main frame.
+PASS: Worker Target should still have 3 resources.
+RESOURCES:
+inspector/worker/resources/resource-utilities.js
+inspector/worker/resources/dataXHR.json
+inspector/worker/resources/dataFetch.json
+
+-- Running test case: Worker.Resource.ImportScript
+PASS: Worker Target should still have 3 resources.
+PASS: Worker Target should dispatch ResourceAdded event.
+PASS: Resource should be tied to the Worker Target.
+PASS: Resource has no parent frame.
+PASS: Worker Target should have 4 resources.
+RESOURCES:
+inspector/worker/resources/resource-utilities.js
+inspector/worker/resources/dataXHR.json
+inspector/worker/resources/dataFetch.json
+inspector/worker/resources/worker-import-1.js
+
diff --git a/LayoutTests/inspector/worker/resources-in-worker.html b/LayoutTests/inspector/worker/resources-in-worker.html
new file mode 100644 (file)
index 0000000..415eefe
--- /dev/null
@@ -0,0 +1,183 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../http/tests/inspector/resources/inspector-test.js"></script>
+<script src="resources/resource-utilities.js"></script>
+<script>
+let worker = null;
+
+function triggerWorkerStart() {
+    worker = new Worker("resources/worker-resources.js");
+    worker.onmessage = (event) => {
+        if (event.data === "ready")
+            TestPage.dispatchEventToFrontend("WorkerIsReady");
+    }
+}
+
+function triggerMainResourceLoadXHR() {
+    loadResourceXHR();
+}
+
+function triggerWorkerResourceLoadXHR() {
+    worker.postMessage("loadResourceXHR");
+}
+
+function triggerMainResourceLoadFetch() {
+    loadResourceFetch();
+}
+
+function triggerWorkerResourceLoadFetch() {
+    worker.postMessage("loadResourceFetch");
+}
+
+function triggerWorkerImportScript() {
+    worker.postMessage("importScript");
+}
+
+function test()
+{
+    InspectorTest.debug();
+
+    let workerTarget = null;
+    let mainTarget = WebInspector.mainTarget;
+
+    function sanitizeURL(url) {
+        return url.replace(/^.*?LayoutTests\//, "");
+    }
+
+    function dumpWorkerResources() {
+        InspectorTest.log("RESOURCES:");
+        for (let resource of workerTarget.resourceCollection.items)
+            InspectorTest.log(sanitizeURL(resource.url));
+        if (!workerTarget.resourceCollection.items.size)
+            InspectorTest.log("-- No Resources --");
+    }
+
+    let suite = InspectorTest.createAsyncSuite("Worker.Resources");
+
+    suite.addTestCase({
+        name: "Worker.Resource.Start",
+        description: "Start the worker.",
+        test(resolve, reject) {
+            InspectorTest.evaluateInPage("triggerWorkerStart()");
+
+            WebInspector.targetManager.singleFireEventListener(WebInspector.TargetManager.Event.TargetAdded, (event) => {
+                workerTarget = event.data.target;
+                InspectorTest.assert(workerTarget instanceof WebInspector.Target);
+                InspectorTest.expectEqual(workerTarget.type, WebInspector.Target.Type.Worker, "Added Target should have Worker type.");
+            });
+
+            InspectorTest.singleFireEventListener("WorkerIsReady", () => {
+                resolve();
+            });
+        }
+    });
+
+    suite.addTestCase({
+        name: "Worker.Resource.XHR",
+        description: "Worker XHRs are tied to the Worker Target.",
+        test(resolve, reject) {
+            InspectorTest.expectEqual(workerTarget.resourceCollection.items.size, 1, "Worker Target should have 1 resource.");
+            InspectorTest.evaluateInPage("triggerWorkerResourceLoadXHR()");
+            workerTarget.awaitEvent(WebInspector.Target.Event.ResourceAdded)
+                .then((event) => {
+                    let resource = event.data.resource;
+                    InspectorTest.expectThat(resource instanceof WebInspector.Resource, "Worker Target should dispatch ResourceAdded event.");
+                    InspectorTest.expectEqual(resource.type, WebInspector.Resource.Type.XHR, "Resource should be XHR.");
+                    InspectorTest.expectEqual(resource.target, workerTarget, "Resource should be tied to the Worker Target.");
+                    InspectorTest.expectNull(resource.parentFrame, "Resource has no parent frame.");
+                    InspectorTest.expectEqual(workerTarget.resourceCollection.items.size, 2, "Worker Target should have 2 resources.");
+                    dumpWorkerResources();
+                    resolve();
+                }).catch(reject);
+        }
+    });
+
+    suite.addTestCase({
+        name: "Main.Resource.XHR",
+        description: "Main XHRs are tied to the Main Target and not affect the Worker Target",
+        test(resolve, reject) {
+            InspectorTest.evaluateInPage("triggerMainResourceLoadXHR()");
+            InspectorTest.expectEqual(workerTarget.resourceCollection.items.size, 2, "Worker Target should still have 2 resources.");
+            WebInspector.Frame.awaitEvent(WebInspector.Frame.Event.ResourceWasAdded)
+                .then((event) => {
+                    let resource = event.data.resource;
+                    InspectorTest.expectThat(resource instanceof WebInspector.Resource, "Frame should dispatch ResourceWasAdded event.");
+                    InspectorTest.expectEqual(resource.type, WebInspector.Resource.Type.XHR, "Resource should be XHR.");
+                    InspectorTest.expectEqual(resource.target, mainTarget, "Resource should be tied to the Main Target.");
+                    InspectorTest.expectEqual(resource.parentFrame, WebInspector.frameResourceManager.mainFrame, "Resource parentFrame is the main frame.");
+                    InspectorTest.expectEqual(workerTarget.resourceCollection.items.size, 2, "Worker Target should still have 2 resources.");
+                    dumpWorkerResources();
+                    resolve();
+                }).catch(reject);
+        }
+    });
+
+    suite.addTestCase({
+        name: "Worker.Resource.Fetch",
+        description: "Worker fetch requests are tied to the Worker Target.",
+        test(resolve, reject) {
+            InspectorTest.expectEqual(workerTarget.resourceCollection.items.size, 2, "Worker Target should have 2 resources.");
+            InspectorTest.evaluateInPage("triggerWorkerResourceLoadFetch()");
+            workerTarget.awaitEvent(WebInspector.Target.Event.ResourceAdded)
+                .then((event) => {
+                    let resource = event.data.resource;
+                    InspectorTest.expectThat(resource instanceof WebInspector.Resource, "Worker Target should dispatch ResourceAdded event.");
+                    // FIXME: Add resource type for Fetch that is not XHR?
+                    InspectorTest.expectEqual(resource.target, workerTarget, "Resource should be tied to the Worker Target.");
+                    InspectorTest.expectNull(resource.parentFrame, "Resource has no parent frame.");
+                    InspectorTest.expectEqual(workerTarget.resourceCollection.items.size, 3, "Worker Target should have 3 resources.");
+                    dumpWorkerResources();
+                    resolve();
+                }).catch(reject);
+        }
+    });
+
+    suite.addTestCase({
+        name: "Main.Resource.Fetch",
+        description: "Main fetch requests are tied to the Main Target and not affect the Worker Target",
+        test(resolve, reject) {
+            InspectorTest.evaluateInPage("triggerMainResourceLoadFetch()");
+            InspectorTest.expectEqual(workerTarget.resourceCollection.items.size, 3, "Worker Target should still have 3 resources.");
+            WebInspector.Frame.awaitEvent(WebInspector.Frame.Event.ResourceWasAdded)
+                .then((event) => {
+                    let resource = event.data.resource;
+                    InspectorTest.expectThat(resource instanceof WebInspector.Resource, "Frame should dispatch ResourceWasAdded event.");
+                    // FIXME: Add resource type for Fetch that is not XHR?
+                    InspectorTest.expectEqual(resource.target, mainTarget, "Resource should be tied to the Main Target.");
+                    InspectorTest.expectEqual(resource.parentFrame, WebInspector.frameResourceManager.mainFrame, "Resource parentFrame is the main frame.");
+                    InspectorTest.expectEqual(workerTarget.resourceCollection.items.size, 3, "Worker Target should still have 3 resources.");
+                    dumpWorkerResources();
+                    resolve();
+                }).catch(reject);
+        }
+    });
+
+    suite.addTestCase({
+        name: "Worker.Resource.ImportScript",
+        description: "Worker imported scripts are Resources tied to the Worker Target.",
+        test(resolve, reject) {
+            InspectorTest.expectEqual(workerTarget.resourceCollection.items.size, 3, "Worker Target should still have 3 resources.");
+            InspectorTest.evaluateInPage("triggerWorkerImportScript()");
+            workerTarget.awaitEvent(WebInspector.Target.Event.ResourceAdded)
+                .then((event) => {
+                    let resource = event.data.resource;
+                    InspectorTest.expectThat(resource instanceof WebInspector.Resource, "Worker Target should dispatch ResourceAdded event.");
+                    // FIXME: <https://webkit.org/b/164425> Worker Script Loads (new Worker(...), importScripts(...)) should be classified as Scripts not Raw requests
+                    InspectorTest.expectEqual(resource.target, workerTarget, "Resource should be tied to the Worker Target.");
+                    InspectorTest.expectNull(resource.parentFrame, "Resource has no parent frame.");
+                    InspectorTest.expectEqual(workerTarget.resourceCollection.items.size, 4, "Worker Target should have 4 resources.");
+                    dumpWorkerResources();
+                    resolve();
+                }).catch(reject);
+        }
+    });
+
+    suite.runTestCasesAndFinish();
+}
+</script>
+</head>
+<body onload="runTest()">
+<p>Test for Resources in a Worker.</p>
+</body>
+</html>
diff --git a/LayoutTests/inspector/worker/resources/dataFetch.json b/LayoutTests/inspector/worker/resources/dataFetch.json
new file mode 100644 (file)
index 0000000..784c67c
--- /dev/null
@@ -0,0 +1 @@
+{"webkit":true, "type":"Fetch"}
diff --git a/LayoutTests/inspector/worker/resources/dataXHR.json b/LayoutTests/inspector/worker/resources/dataXHR.json
new file mode 100644 (file)
index 0000000..a9981b9
--- /dev/null
@@ -0,0 +1 @@
+{"webkit":true, "type":"XHR"}
diff --git a/LayoutTests/inspector/worker/resources/resource-utilities.js b/LayoutTests/inspector/worker/resources/resource-utilities.js
new file mode 100644 (file)
index 0000000..d33ef26
--- /dev/null
@@ -0,0 +1,9 @@
+function loadResourceXHR() {
+    let xhr = new XMLHttpRequest;
+    xhr.open("GET", "dataXHR.json", true);
+    xhr.send();
+}
+
+function loadResourceFetch() {
+    fetch("dataFetch.json");
+}
diff --git a/LayoutTests/inspector/worker/resources/worker-resources.js b/LayoutTests/inspector/worker/resources/worker-resources.js
new file mode 100644 (file)
index 0000000..a54d480
--- /dev/null
@@ -0,0 +1,16 @@
+importScripts("resource-utilities.js");
+
+function importScript() {
+    importScripts("worker-import-1.js");
+}
+
+onmessage = function(event) {
+    if (event.data === "loadResourceXHR")
+        loadResourceXHR();
+    else if (event.data === "loadResourceFetch")
+        loadResourceFetch();
+    else if (event.data === "importScript")
+        importScript();
+}
+
+postMessage("ready");
index f24f59b..076a06f 100644 (file)
@@ -1,3 +1,15 @@
+2016-11-09  Joseph Pecoraro  <pecoraro@apple.com>
+
+        Web Inspector: Associate Worker Resources with the Worker and not the Page
+        https://bugs.webkit.org/show_bug.cgi?id=164342
+        <rdar://problem/29075775>
+
+        Reviewed by Timothy Hatcher.
+
+        * inspector/protocol/Network.json:
+        * inspector/protocol/Page.json:
+        Associate Resource data with a target.
+
 2016-11-09  Keith Miller  <keith_miller@apple.com>
 
         jsc CLI should work with the remote inspector
index 230a8c0..1910b1e 100644 (file)
                 { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." },
                 { "name": "initiator", "$ref": "Initiator", "description": "Request initiator." },
                 { "name": "redirectResponse", "optional": true, "$ref": "Response", "description": "Redirect response data." },
-                { "name": "type", "$ref": "Page.ResourceType", "optional": true, "description": "Resource type." }
+                { "name": "type", "$ref": "Page.ResourceType", "optional": true, "description": "Resource type." },
+                { "name": "targetId", "type": "string", "optional": true, "description": "Identifier for the context of where the load originated. In general this is the target identifier. For Workers this will be the workerId." }
             ]
         },
         {
index 4ba5476..139476c 100644 (file)
@@ -38,7 +38,8 @@
                 { "name": "mimeType", "type": "string", "description": "Resource mimeType as determined by the browser." },
                 { "name": "failed", "type": "boolean", "optional": true, "description": "True if the resource failed to load." },
                 { "name": "canceled", "type": "boolean", "optional": true, "description": "True if the resource was canceled during loading." },
-                { "name": "sourceMapURL", "type": "string", "optional": true, "description": "URL of source map associated with this resource (if any)." }
+                { "name": "sourceMapURL", "type": "string", "optional": true, "description": "URL of source map associated with this resource (if any)." },
+                { "name": "targetId", "type": "string", "optional": true, "description": "Identifier for the context of where the load originated. In general this is the target identifier. For Workers this will be the workerId." }
             ]
         },
         {
index 0f65172..1be0585 100644 (file)
@@ -1,3 +1,104 @@
+2016-11-09  Joseph Pecoraro  <pecoraro@apple.com>
+
+        Web Inspector: Associate Worker Resources with the Worker and not the Page
+        https://bugs.webkit.org/show_bug.cgi?id=164342
+        <rdar://problem/29075775>
+
+        Reviewed by Timothy Hatcher.
+
+        Test: inspector/worker/resources-in-worker.html
+
+        Provide a way to associate an initiator identifier with a ResourceRequest.
+        This will allow Web Inspector to identify who started particular resource
+        loads. This is important to associate Worker(...), importScript(...), and
+        XMLHttpRequest / Fetch loads with that specific Worker.
+
+        * platform/network/ResourceRequestBase.cpp:
+        (WebCore::ResourceRequestBase::setAsIsolatedCopy):
+        * platform/network/ResourceRequestBase.h:
+        (WebCore::ResourceRequestBase::initiatorIdentifier):
+        (WebCore::ResourceRequestBase::setInitiatorIdentifier):
+        Optional initiator identifier. Currently used only be Web Inspector.
+
+        * dom/ScriptExecutionContext.h:
+        (WebCore::ScriptExecutionContext::resourceRequestIdentifier):
+        Non-page execution contexts, like WorkerGlobalScope, should provide
+        a unique identifier that may be used to distinguish loads initiated
+        from within that context.
+
+        * xml/XMLHttpRequest.cpp:
+        (WebCore::XMLHttpRequest::createRequest):
+        * Modules/fetch/FetchLoader.cpp:
+        (WebCore::FetchLoader::start):
+        * Modules/fetch/FetchRequest.cpp:
+        (WebCore::FetchRequest::initializeWith):
+        XHR / Fetch loads should include the ScriptExecutionContext's
+        initiator identifier.
+
+        * workers/WorkerScriptLoader.cpp:
+        (WebCore::WorkerScriptLoader::WorkerScriptLoader):
+        (WebCore::WorkerScriptLoader::loadSynchronously):
+        (WebCore::WorkerScriptLoader::loadAsynchronously):
+        (WebCore::WorkerScriptLoader::createResourceRequest):
+        * workers/WorkerScriptLoader.h:
+        Provide a way to provide initiator identifier information for
+        Worker script loads. Currently this is `new Worker(...)` and
+        `importScripts(...)` resource loads.
+
+        * workers/Worker.cpp:
+        (WebCore::Worker::Worker):
+        (WebCore::Worker::create):
+        * workers/Worker.h:
+        * workers/WorkerGlobalScope.cpp:
+        (WebCore::WorkerGlobalScope::WorkerGlobalScope):
+        (WebCore::WorkerGlobalScope::importScripts):
+        * workers/WorkerGlobalScope.h:
+        Give Worker itself the unique identifier, because `new Worker(...)`
+        loads happen before the WorkerGlobalScript (ScriptExecutionContext)
+        is actually created, but we want to associate it with this Worker.
+
+        * workers/DedicatedWorkerGlobalScope.cpp:
+        (WebCore::DedicatedWorkerGlobalScope::create):
+        (WebCore::DedicatedWorkerGlobalScope::DedicatedWorkerGlobalScope):
+        * workers/DedicatedWorkerGlobalScope.h:
+        * workers/DedicatedWorkerThread.cpp:
+        (WebCore::DedicatedWorkerThread::DedicatedWorkerThread):
+        (WebCore::DedicatedWorkerThread::createWorkerGlobalScope):
+        * workers/DedicatedWorkerThread.h:
+        * workers/WorkerInspectorProxy.cpp:
+        (WebCore::WorkerInspectorProxy::WorkerInspectorProxy):
+        * workers/WorkerInspectorProxy.h:
+        * workers/WorkerMessagingProxy.cpp:
+        (WebCore::WorkerMessagingProxy::WorkerMessagingProxy):
+        (WebCore::WorkerMessagingProxy::startWorkerGlobalScope):
+        * workers/WorkerThread.cpp:
+        (WebCore::WorkerThreadStartupData::WorkerThreadStartupData):
+        (WebCore::WorkerThread::WorkerThread):
+        (WebCore::WorkerThread::workerThread):
+        * workers/WorkerThread.h:
+        Pass the MainThread's Worker identifier through to the WorkerGlobalScope
+        created on the WorkerThread. They should be the same identifier.
+
+        * inspector/InspectorNetworkAgent.cpp:
+        (WebCore::InspectorNetworkAgent::willSendRequest):
+        * inspector/InspectorPageAgent.cpp:
+        (WebCore::InspectorPageAgent::buildObjectForFrameTree):
+        Pass the initiator identifier data to the frontend. This identifier is
+        equivalent to a "target identifier" in the frontend. Currently the only
+        non-Page targets are Workers.
+
+        * loader/cache/CachedResourceLoader.cpp:
+        (WebCore::CachedResourceLoader::shouldContinueAfterNotifyingLoadedFromMemoryCache):
+        When using the memory cache we create a new resource request. Be sure
+        to copy over useful inspector data, like the initiator identifier,
+        from the original request.
+
+        * platform/network/cf/ResourceRequestCFNet.cpp:
+        (WebCore::ResourceRequest::updateFromDelegatePreservingOldProperties):
+        When rebuilding a ResourceRequest from NSURLRequest, copy over the
+        initiator identifier property that wouldn't otherwise have survived
+        the transition.
+
 2016-11-09  Brady Eidson  <beidson@apple.com>
 
         IndexedDB 2.0: Clean up some exception ordering.
index 9113931..fac1bea 100644 (file)
@@ -58,6 +58,7 @@ void FetchLoader::start(ScriptExecutionContext& context, const Blob& blob)
     ThreadableBlobRegistry::registerBlobURL(context.securityOrigin(), urlForReading, blob.url());
 
     ResourceRequest request(urlForReading);
+    request.setInitiatorIdentifier(context.resourceRequestIdentifier());
     request.setHTTPMethod("GET");
 
     ThreadableLoaderOptions options;
index 5592bd7..5190017 100644 (file)
@@ -252,6 +252,7 @@ ExceptionOr<FetchHeaders&> FetchRequest::initializeWith(const String& url, const
     m_internalRequest.options.credentials = Credentials::Omit;
     m_internalRequest.referrer = ASCIILiteral("client");
     m_internalRequest.request.setURL(requestURL);
+    m_internalRequest.request.setInitiatorIdentifier(scriptExecutionContext()->resourceRequestIdentifier());
 
     return initializeOptions(init);
 }
index 60f9abb..0837c71 100644 (file)
@@ -92,6 +92,8 @@ public:
     virtual SocketProvider* socketProvider() = 0;
 #endif
 
+    virtual String resourceRequestIdentifier() const { return String(); };
+
     bool sanitizeScriptError(String& errorMessage, int& lineNumber, int& columnNumber, String& sourceURL, Deprecated::ScriptValue& error, CachedScript* = nullptr);
     void reportException(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL, JSC::Exception*, RefPtr<Inspector::ScriptCallStack>&&, CachedScript* = nullptr);
 
index 2dbad58..ff9858d 100644 (file)
@@ -309,7 +309,9 @@ void InspectorNetworkAgent::willSendRequest(unsigned long identifier, DocumentLo
     Inspector::Protocol::Page::ResourceType resourceType = InspectorPageAgent::resourceTypeJson(type);
 
     RefPtr<Inspector::Protocol::Network::Initiator> initiatorObject = buildInitiatorObject(loader.frame() ? loader.frame()->document() : nullptr);
-    m_frontendDispatcher->requestWillBeSent(requestId, m_pageAgent->frameId(loader.frame()), m_pageAgent->loaderId(&loader), loader.url().string(), buildObjectForResourceRequest(request), timestamp(), initiatorObject, buildObjectForResourceResponse(redirectResponse, nullptr), type != InspectorPageAgent::OtherResource ? &resourceType : nullptr);
+    String targetId = request.initiatorIdentifier();
+
+    m_frontendDispatcher->requestWillBeSent(requestId, m_pageAgent->frameId(loader.frame()), m_pageAgent->loaderId(&loader), loader.url().string(), buildObjectForResourceRequest(request), timestamp(), initiatorObject, buildObjectForResourceResponse(redirectResponse, nullptr), type != InspectorPageAgent::OtherResource ? &resourceType : nullptr, targetId.isEmpty() ? nullptr : &targetId);
 }
 
 void InspectorNetworkAgent::markResourceAsCached(unsigned long identifier)
index c6b2d19..2629397 100644 (file)
@@ -961,6 +961,9 @@ Ref<Inspector::Protocol::Page::FrameResourceTree> InspectorPageAgent::buildObjec
         String sourceMappingURL = InspectorPageAgent::sourceMapURLForResource(cachedResource);
         if (!sourceMappingURL.isEmpty())
             resourceObject->setSourceMapURL(sourceMappingURL);
+        String targetId = cachedResource->resourceRequest().initiatorIdentifier();
+        if (!targetId.isEmpty())
+            resourceObject->setTargetId(targetId);
         subresources->addItem(WTFMove(resourceObject));
     }
 
index e158a40..e163e8b 100644 (file)
@@ -542,6 +542,7 @@ bool CachedResourceLoader::shouldContinueAfterNotifyingLoadedFromMemoryCache(con
         return true;
 
     ResourceRequest newRequest = ResourceRequest(resource->url());
+    newRequest.setInitiatorIdentifier(request.resourceRequest().initiatorIdentifier());
     if (request.resourceRequest().hiddenFromInspector())
         newRequest.setHiddenFromInspector(true);
     frame()->loader().loadedResourceFromMemoryCache(resource, newRequest);
index 298504d..c324d8e 100644 (file)
@@ -65,6 +65,7 @@ void ResourceRequestBase::setAsIsolatedCopy(const ResourceRequest& other)
     setHTTPMethod(other.httpMethod().isolatedCopy());
     setPriority(other.priority());
     setRequester(other.requester());
+    setInitiatorIdentifier(other.initiatorIdentifier().isolatedCopy());
 
     updateResourceRequest();
     m_httpHeaderFields = other.httpHeaderFields().isolatedCopy();
index 62cb2c6..c73e76c 100644 (file)
@@ -164,6 +164,10 @@ public:
     Requester requester() const { return m_requester; }
     void setRequester(Requester requester) { m_requester = requester; }
 
+    // Who initiated the request so the Inspector can associate it with a context. E.g. a Web Worker.
+    String initiatorIdentifier() const { return m_initiatorIdentifier; }
+    void setInitiatorIdentifier(const String& identifier) { m_initiatorIdentifier = identifier; }
+
 #if !PLATFORM(COCOA)
     bool encodingRequiresPlatformData() const { return true; }
 #endif
@@ -232,6 +236,7 @@ protected:
     bool m_ignoreForRequestCount { false };
     ResourceLoadPriority m_priority { ResourceLoadPriority::Low };
     Requester m_requester { Requester::Unspecified };
+    String m_initiatorIdentifier;
 
 private:
     const ResourceRequest& asResourceRequest() const;
index d7647f4..69b8bac 100644 (file)
@@ -338,6 +338,7 @@ void ResourceRequest::updateFromDelegatePreservingOldProperties(const ResourceRe
     RefPtr<FormData> oldHTTPBody = httpBody();
     bool isHiddenFromInspector = hiddenFromInspector();
     auto oldRequester = requester();
+    auto oldInitiatorIdentifier = initiatorIdentifier();
 
     *this = delegateProvidedRequest;
 
@@ -345,6 +346,7 @@ void ResourceRequest::updateFromDelegatePreservingOldProperties(const ResourceRe
     setHTTPBody(WTFMove(oldHTTPBody));
     setHiddenFromInspector(isHiddenFromInspector);
     setRequester(oldRequester);
+    setInitiatorIdentifier(oldInitiatorIdentifier);
 }
 
 bool ResourceRequest::httpPipeliningEnabled()
index 5120d36..56c34a9 100644 (file)
 
 namespace WebCore {
 
-Ref<DedicatedWorkerGlobalScope> DedicatedWorkerGlobalScope::create(const URL& url, const String& userAgent, DedicatedWorkerThread& thread, const ContentSecurityPolicyResponseHeaders& contentSecurityPolicyResponseHeaders, bool shouldBypassMainWorldContentSecurityPolicy, RefPtr<SecurityOrigin>&& topOrigin, IDBClient::IDBConnectionProxy* connectionProxy, SocketProvider* socketProvider)
+Ref<DedicatedWorkerGlobalScope> DedicatedWorkerGlobalScope::create(const URL& url, const String& identifier, const String& userAgent, DedicatedWorkerThread& thread, const ContentSecurityPolicyResponseHeaders& contentSecurityPolicyResponseHeaders, bool shouldBypassMainWorldContentSecurityPolicy, RefPtr<SecurityOrigin>&& topOrigin, IDBClient::IDBConnectionProxy* connectionProxy, SocketProvider* socketProvider)
 {
-    auto context = adoptRef(*new DedicatedWorkerGlobalScope(url, userAgent, thread, shouldBypassMainWorldContentSecurityPolicy, WTFMove(topOrigin), connectionProxy, socketProvider));
+    auto context = adoptRef(*new DedicatedWorkerGlobalScope(url, identifier, userAgent, thread, shouldBypassMainWorldContentSecurityPolicy, WTFMove(topOrigin), connectionProxy, socketProvider));
     if (!shouldBypassMainWorldContentSecurityPolicy)
         context->applyContentSecurityPolicyResponseHeaders(contentSecurityPolicyResponseHeaders);
     return context;
 }
 
-DedicatedWorkerGlobalScope::DedicatedWorkerGlobalScope(const URL& url, const String& userAgent, DedicatedWorkerThread& thread, bool shouldBypassMainWorldContentSecurityPolicy, RefPtr<SecurityOrigin>&& topOrigin, IDBClient::IDBConnectionProxy* connectionProxy, SocketProvider* socketProvider)
-    : WorkerGlobalScope(url, userAgent, thread, shouldBypassMainWorldContentSecurityPolicy, WTFMove(topOrigin), connectionProxy, socketProvider)
+DedicatedWorkerGlobalScope::DedicatedWorkerGlobalScope(const URL& url, const String& identifier, const String& userAgent, DedicatedWorkerThread& thread, bool shouldBypassMainWorldContentSecurityPolicy, RefPtr<SecurityOrigin>&& topOrigin, IDBClient::IDBConnectionProxy* connectionProxy, SocketProvider* socketProvider)
+    : WorkerGlobalScope(url, identifier, userAgent, thread, shouldBypassMainWorldContentSecurityPolicy, WTFMove(topOrigin), connectionProxy, socketProvider)
 {
 }
 
index 30a62fd..ce54b70 100644 (file)
@@ -42,7 +42,7 @@ class SerializedScriptValue;
 
 class DedicatedWorkerGlobalScope final : public WorkerGlobalScope {
 public:
-    static Ref<DedicatedWorkerGlobalScope> create(const URL&, const String& userAgent, DedicatedWorkerThread&, const ContentSecurityPolicyResponseHeaders&, bool shouldBypassMainWorldContentSecurityPolicy, RefPtr<SecurityOrigin>&& topOrigin, IDBClient::IDBConnectionProxy*, SocketProvider*);
+    static Ref<DedicatedWorkerGlobalScope> create(const URL&, const String& identifier, const String& userAgent, DedicatedWorkerThread&, const ContentSecurityPolicyResponseHeaders&, bool shouldBypassMainWorldContentSecurityPolicy, RefPtr<SecurityOrigin>&& topOrigin, IDBClient::IDBConnectionProxy*, SocketProvider*);
     virtual ~DedicatedWorkerGlobalScope();
 
     ExceptionOr<void> postMessage(RefPtr<SerializedScriptValue>&&, Vector<RefPtr<MessagePort>>&&);
@@ -52,7 +52,7 @@ public:
 private:
     using Base = WorkerGlobalScope;
 
-    DedicatedWorkerGlobalScope(const URL&, const String& userAgent, DedicatedWorkerThread&, bool shouldBypassMainWorldContentSecurityPolicy, RefPtr<SecurityOrigin>&& topOrigin, IDBClient::IDBConnectionProxy*, SocketProvider*);
+    DedicatedWorkerGlobalScope(const URL&, const String& identifier, const String& userAgent, DedicatedWorkerThread&, bool shouldBypassMainWorldContentSecurityPolicy, RefPtr<SecurityOrigin>&& topOrigin, IDBClient::IDBConnectionProxy*, SocketProvider*);
 
     bool isDedicatedWorkerGlobalScope() const final { return true; }
     ExceptionOr<void> importScripts(const Vector<String>& urls) final;
index 790f4be..d7aba3c 100644 (file)
@@ -38,8 +38,8 @@
 
 namespace WebCore {
 
-DedicatedWorkerThread::DedicatedWorkerThread(const URL& url, const String& userAgent, const String& sourceCode, WorkerLoaderProxy& workerLoaderProxy, WorkerObjectProxy& workerObjectProxy, WorkerThreadStartMode startMode, const ContentSecurityPolicyResponseHeaders& contentSecurityPolicyResponseHeaders, bool shouldBypassMainWorldContentSecurityPolicy, const SecurityOrigin* topOrigin, IDBClient::IDBConnectionProxy* connectionProxy, SocketProvider* socketProvider, JSC::RuntimeFlags runtimeFlags)
-    : WorkerThread(url, userAgent, sourceCode, workerLoaderProxy, workerObjectProxy, startMode, contentSecurityPolicyResponseHeaders, shouldBypassMainWorldContentSecurityPolicy, topOrigin, connectionProxy, socketProvider, runtimeFlags)
+DedicatedWorkerThread::DedicatedWorkerThread(const URL& url, const String& identifier, const String& userAgent, const String& sourceCode, WorkerLoaderProxy& workerLoaderProxy, WorkerObjectProxy& workerObjectProxy, WorkerThreadStartMode startMode, const ContentSecurityPolicyResponseHeaders& contentSecurityPolicyResponseHeaders, bool shouldBypassMainWorldContentSecurityPolicy, const SecurityOrigin* topOrigin, IDBClient::IDBConnectionProxy* connectionProxy, SocketProvider* socketProvider, JSC::RuntimeFlags runtimeFlags)
+    : WorkerThread(url, identifier, userAgent, sourceCode, workerLoaderProxy, workerObjectProxy, startMode, contentSecurityPolicyResponseHeaders, shouldBypassMainWorldContentSecurityPolicy, topOrigin, connectionProxy, socketProvider, runtimeFlags)
     , m_workerObjectProxy(workerObjectProxy)
 {
 }
@@ -48,9 +48,9 @@ DedicatedWorkerThread::~DedicatedWorkerThread()
 {
 }
 
-Ref<WorkerGlobalScope> DedicatedWorkerThread::createWorkerGlobalScope(const URL& url, const String& userAgent, const ContentSecurityPolicyResponseHeaders& contentSecurityPolicyResponseHeaders, bool shouldBypassMainWorldContentSecurityPolicy, PassRefPtr<SecurityOrigin> topOrigin)
+Ref<WorkerGlobalScope> DedicatedWorkerThread::createWorkerGlobalScope(const URL& url, const String& identifier, const String& userAgent, const ContentSecurityPolicyResponseHeaders& contentSecurityPolicyResponseHeaders, bool shouldBypassMainWorldContentSecurityPolicy, PassRefPtr<SecurityOrigin> topOrigin)
 {
-    return DedicatedWorkerGlobalScope::create(url, userAgent, *this, contentSecurityPolicyResponseHeaders, shouldBypassMainWorldContentSecurityPolicy, topOrigin, idbConnectionProxy(), socketProvider());
+    return DedicatedWorkerGlobalScope::create(url, identifier, userAgent, *this, contentSecurityPolicyResponseHeaders, shouldBypassMainWorldContentSecurityPolicy, topOrigin, idbConnectionProxy(), socketProvider());
 }
 
 void DedicatedWorkerThread::runEventLoop()
index c503cdb..d0261b6 100644 (file)
@@ -49,11 +49,11 @@ public:
     WorkerObjectProxy& workerObjectProxy() const { return m_workerObjectProxy; }
 
 protected:
-    Ref<WorkerGlobalScope> createWorkerGlobalScope(const URL&, const String& userAgent, const ContentSecurityPolicyResponseHeaders&, bool shouldBypassMainWorldContentSecurityPolicy, PassRefPtr<SecurityOrigin> topOrigin) override;
+    Ref<WorkerGlobalScope> createWorkerGlobalScope(const URL&, const String& identifier, const String& userAgent, const ContentSecurityPolicyResponseHeaders&, bool shouldBypassMainWorldContentSecurityPolicy, PassRefPtr<SecurityOrigin> topOrigin) override;
     void runEventLoop() override;
 
 private:
-    DedicatedWorkerThread(const URL&, const String& userAgent, const String& sourceCode, WorkerLoaderProxy&, WorkerObjectProxy&, WorkerThreadStartMode, const ContentSecurityPolicyResponseHeaders&, bool shouldBypassMainWorldContentSecurityPolicy, const SecurityOrigin* topOrigin, IDBClient::IDBConnectionProxy*, SocketProvider*, JSC::RuntimeFlags);
+    DedicatedWorkerThread(const URL&, const String& identifier, const String& userAgent, const String& sourceCode, WorkerLoaderProxy&, WorkerObjectProxy&, WorkerThreadStartMode, const ContentSecurityPolicyResponseHeaders&, bool shouldBypassMainWorldContentSecurityPolicy, const SecurityOrigin* topOrigin, IDBClient::IDBConnectionProxy*, SocketProvider*, JSC::RuntimeFlags);
 
     WorkerObjectProxy& m_workerObjectProxy;
 };
index 15b7100..b667f25 100644 (file)
@@ -38,6 +38,7 @@
 #include "WorkerGlobalScopeProxy.h"
 #include "WorkerScriptLoader.h"
 #include "WorkerThread.h"
+#include <inspector/IdentifiersFactory.h>
 #include <wtf/HashSet.h>
 #include <wtf/MainThread.h>
 
@@ -53,6 +54,7 @@ void networkStateChanged(bool isOnLine)
 
 inline Worker::Worker(ScriptExecutionContext& context, JSC::RuntimeFlags runtimeFlags)
     : ActiveDOMObject(&context)
+    , m_identifier("worker:" + Inspector::IdentifiersFactory::createIdentifier())
     , m_contextProxy(WorkerGlobalScopeProxy::create(this))
     , m_runtimeFlags(runtimeFlags)
 {
@@ -88,7 +90,7 @@ ExceptionOr<Ref<Worker>> Worker::create(ScriptExecutionContext& context, const S
 
     worker->m_scriptLoader = WorkerScriptLoader::create();
     auto contentSecurityPolicyEnforcement = shouldBypassMainWorldContentSecurityPolicy ? ContentSecurityPolicyEnforcement::DoNotEnforce : ContentSecurityPolicyEnforcement::EnforceChildSrcDirective;
-    worker->m_scriptLoader->loadAsynchronously(&context, scriptURL.releaseReturnValue(), FetchOptions::Mode::SameOrigin, contentSecurityPolicyEnforcement, worker.ptr());
+    worker->m_scriptLoader->loadAsynchronously(&context, scriptURL.releaseReturnValue(), FetchOptions::Mode::SameOrigin, contentSecurityPolicyEnforcement, worker->m_identifier, worker.ptr());
     return WTFMove(worker);
 }
 
index 3bf31e8..12884cf 100644 (file)
@@ -52,6 +52,8 @@ public:
 
     bool hasPendingActivity() const final;
 
+    String identifier() const { return m_identifier; }
+
     ScriptExecutionContext* scriptExecutionContext() const final { return ActiveDOMObject::scriptExecutionContext(); }
 
 private:
@@ -71,6 +73,7 @@ private:
     friend void networkStateChanged(bool isOnLine);
 
     RefPtr<WorkerScriptLoader> m_scriptLoader;
+    String m_identifier;
     WorkerGlobalScopeProxy* m_contextProxy; // The proxy outlives the worker to perform thread shutdown.
     Optional<ContentSecurityPolicyResponseHeaders> m_contentSecurityPolicyResponseHeaders;
     bool m_shouldBypassMainWorldContentSecurityPolicy { false };
index 299f53b..ed2feda 100644 (file)
@@ -52,8 +52,9 @@ using namespace Inspector;
 
 namespace WebCore {
 
-WorkerGlobalScope::WorkerGlobalScope(const URL& url, const String& userAgent, WorkerThread& thread, bool shouldBypassMainWorldContentSecurityPolicy, RefPtr<SecurityOrigin>&& topOrigin, IDBClient::IDBConnectionProxy* connectionProxy, SocketProvider* socketProvider)
+WorkerGlobalScope::WorkerGlobalScope(const URL& url, const String& identifier, const String& userAgent, WorkerThread& thread, bool shouldBypassMainWorldContentSecurityPolicy, RefPtr<SecurityOrigin>&& topOrigin, IDBClient::IDBConnectionProxy* connectionProxy, SocketProvider* socketProvider)
     : m_url(url)
+    , m_identifier(identifier)
     , m_userAgent(userAgent)
     , m_thread(thread)
     , m_script(std::make_unique<WorkerScriptController>(this))
@@ -224,7 +225,7 @@ ExceptionOr<void> WorkerGlobalScope::importScripts(const Vector<String>& urls)
             return Exception { NETWORK_ERR };
 
         auto scriptLoader = WorkerScriptLoader::create();
-        scriptLoader->loadSynchronously(this, url, FetchOptions::Mode::NoCors, shouldBypassMainWorldContentSecurityPolicy ? ContentSecurityPolicyEnforcement::DoNotEnforce : ContentSecurityPolicyEnforcement::EnforceScriptSrcDirective);
+        scriptLoader->loadSynchronously(this, url, FetchOptions::Mode::NoCors, shouldBypassMainWorldContentSecurityPolicy ? ContentSecurityPolicyEnforcement::DoNotEnforce : ContentSecurityPolicyEnforcement::EnforceScriptSrcDirective, resourceRequestIdentifier());
 
         // If the fetching attempt failed, throw a NETWORK_ERR exception and abort all these steps.
         if (scriptLoader->failed())
index e92878d..df07e16 100644 (file)
@@ -100,7 +100,7 @@ public:
     Crypto& crypto();
 
 protected:
-    WorkerGlobalScope(const URL&, const String& userAgent, WorkerThread&, bool shouldBypassMainWorldContentSecurityPolicy, RefPtr<SecurityOrigin>&& topOrigin, IDBClient::IDBConnectionProxy*, SocketProvider*);
+    WorkerGlobalScope(const URL&, const String& identifier, const String& userAgent, WorkerThread&, bool shouldBypassMainWorldContentSecurityPolicy, RefPtr<SecurityOrigin>&& topOrigin, IDBClient::IDBConnectionProxy*, SocketProvider*);
 
     void applyContentSecurityPolicyResponseHeaders(const ContentSecurityPolicyResponseHeaders&);
 
@@ -123,6 +123,7 @@ private:
     void disableEval(const String& errorMessage) final;
     EventTarget* errorEventTarget() final;
     WorkerEventQueue& eventQueue() const final;
+    String resourceRequestIdentifier() const final { return m_identifier; }
 
 #if ENABLE(WEB_SOCKETS)
     SocketProvider* socketProvider() final;
@@ -145,6 +146,7 @@ private:
 #endif
 
     URL m_url;
+    String m_identifier;
     String m_userAgent;
 
     mutable RefPtr<WorkerLocation> m_location;
index 5eec1eb..ab566ca 100644 (file)
@@ -31,7 +31,6 @@
 #include "WorkerGlobalScope.h"
 #include "WorkerInspectorController.h"
 #include "WorkerRunLoop.h"
-#include <inspector/IdentifiersFactory.h>
 #include <inspector/InspectorAgentBase.h>
 #include <wtf/NeverDestroyed.h>
 
@@ -45,9 +44,9 @@ HashSet<WorkerInspectorProxy*>& WorkerInspectorProxy::allWorkerInspectorProxies(
     return proxies;
 }
 
-WorkerInspectorProxy::WorkerInspectorProxy()
+WorkerInspectorProxy::WorkerInspectorProxy(const String& identifier)
+    : m_identifier(identifier)
 {
-    m_identifier = "worker:" + IdentifiersFactory::createIdentifier();
 }
 
 WorkerInspectorProxy::~WorkerInspectorProxy()
index 4bcea2b..0c5aa4b 100644 (file)
@@ -42,7 +42,7 @@ class WorkerInspectorProxy {
     WTF_MAKE_NONCOPYABLE(WorkerInspectorProxy);
     WTF_MAKE_FAST_ALLOCATED;
 public:
-    WorkerInspectorProxy();
+    WorkerInspectorProxy(const String& identifier);
     ~WorkerInspectorProxy();
 
     // A Worker's inspector messages come in and go out through the Page's WorkerAgent.
index 84deaa6..2f4ff3f 100644 (file)
@@ -55,7 +55,7 @@ WorkerGlobalScopeProxy* WorkerGlobalScopeProxy::create(Worker* worker)
 
 WorkerMessagingProxy::WorkerMessagingProxy(Worker* workerObject)
     : m_scriptExecutionContext(workerObject->scriptExecutionContext())
-    , m_inspectorProxy(std::make_unique<WorkerInspectorProxy>())
+    , m_inspectorProxy(std::make_unique<WorkerInspectorProxy>(workerObject->identifier()))
     , m_workerObject(workerObject)
     , m_mayBeDestroyed(false)
     , m_unconfirmedMessageCount(0)
@@ -80,6 +80,7 @@ void WorkerMessagingProxy::startWorkerGlobalScope(const URL& scriptURL, const St
     ASSERT(m_scriptExecutionContext);
     Document& document = downcast<Document>(*m_scriptExecutionContext);
     WorkerThreadStartMode startMode = m_inspectorProxy->workerStartMode(*m_scriptExecutionContext.get());
+    String identifier = m_inspectorProxy->identifier();
 
 #if ENABLE(INDEXED_DATABASE)
     IDBClient::IDBConnectionProxy* proxy = document.idbConnectionProxy();
@@ -93,7 +94,7 @@ void WorkerMessagingProxy::startWorkerGlobalScope(const URL& scriptURL, const St
     SocketProvider* socketProvider = nullptr;
 #endif
 
-    RefPtr<DedicatedWorkerThread> thread = DedicatedWorkerThread::create(scriptURL, userAgent, sourceCode, *this, *this, startMode, contentSecurityPolicyResponseHeaders, shouldBypassMainWorldContentSecurityPolicy, document.topOrigin(), proxy, socketProvider, runtimeFlags);
+    RefPtr<DedicatedWorkerThread> thread = DedicatedWorkerThread::create(scriptURL, identifier, userAgent, sourceCode, *this, *this, startMode, contentSecurityPolicyResponseHeaders, shouldBypassMainWorldContentSecurityPolicy, document.topOrigin(), proxy, socketProvider, runtimeFlags);
 
     workerThreadCreated(thread);
     thread->start();
index e3f6409..b6aa267 100644 (file)
 namespace WebCore {
 
 WorkerScriptLoader::WorkerScriptLoader()
-    : m_client(nullptr)
-    , m_failed(false)
-    , m_identifier(0)
-    , m_finishing(false)
 {
 }
 
@@ -50,13 +46,13 @@ WorkerScriptLoader::~WorkerScriptLoader()
 {
 }
 
-void WorkerScriptLoader::loadSynchronously(ScriptExecutionContext* scriptExecutionContext, const URL& url, FetchOptions::Mode mode, ContentSecurityPolicyEnforcement contentSecurityPolicyEnforcement)
+void WorkerScriptLoader::loadSynchronously(ScriptExecutionContext* scriptExecutionContext, const URL& url, FetchOptions::Mode mode, ContentSecurityPolicyEnforcement contentSecurityPolicyEnforcement, const String& initiatorIdentifier)
 {
     ASSERT(scriptExecutionContext);
 
     m_url = url;
 
-    std::unique_ptr<ResourceRequest> request(createResourceRequest());
+    std::unique_ptr<ResourceRequest> request(createResourceRequest(initiatorIdentifier));
     if (!request)
         return;
 
@@ -74,7 +70,7 @@ void WorkerScriptLoader::loadSynchronously(ScriptExecutionContext* scriptExecuti
     WorkerThreadableLoader::loadResourceSynchronously(downcast<WorkerGlobalScope>(*scriptExecutionContext), WTFMove(*request), *this, options);
 }
 
-void WorkerScriptLoader::loadAsynchronously(ScriptExecutionContext* scriptExecutionContext, const URL& url, FetchOptions::Mode mode, ContentSecurityPolicyEnforcement contentSecurityPolicyEnforcement, WorkerScriptLoaderClient* client)
+void WorkerScriptLoader::loadAsynchronously(ScriptExecutionContext* scriptExecutionContext, const URL& url, FetchOptions::Mode mode, ContentSecurityPolicyEnforcement contentSecurityPolicyEnforcement, const String& initiatorIdentifier, WorkerScriptLoaderClient* client)
 {
     ASSERT(client);
     ASSERT(scriptExecutionContext);
@@ -82,7 +78,7 @@ void WorkerScriptLoader::loadAsynchronously(ScriptExecutionContext* scriptExecut
     m_client = client;
     m_url = url;
 
-    std::unique_ptr<ResourceRequest> request(createResourceRequest());
+    std::unique_ptr<ResourceRequest> request(createResourceRequest(initiatorIdentifier));
     if (!request)
         return;
 
@@ -107,10 +103,11 @@ const URL& WorkerScriptLoader::responseURL() const
     return m_responseURL;
 }
 
-std::unique_ptr<ResourceRequest> WorkerScriptLoader::createResourceRequest()
+std::unique_ptr<ResourceRequest> WorkerScriptLoader::createResourceRequest(const String& initiatorIdentifier)
 {
     auto request = std::make_unique<ResourceRequest>(m_url);
     request->setHTTPMethod(ASCIILiteral("GET"));
+    request->setInitiatorIdentifier(initiatorIdentifier);
     return request;
 }
     
index 563a9d6..77ce0fd 100644 (file)
@@ -38,7 +38,6 @@
 
 namespace WebCore {
 
-    class ResourceRequest;
     class ResourceResponse;
     class ScriptExecutionContext;
     class TextResourceDecoder;
@@ -52,8 +51,8 @@ namespace WebCore {
             return adoptRef(*new WorkerScriptLoader);
         }
 
-        void loadSynchronously(ScriptExecutionContext*, const URL&, FetchOptions::Mode, ContentSecurityPolicyEnforcement);
-        void loadAsynchronously(ScriptExecutionContext*, const URL&, FetchOptions::Mode, ContentSecurityPolicyEnforcement, WorkerScriptLoaderClient*);
+        void loadSynchronously(ScriptExecutionContext*, const URL&, FetchOptions::Mode, ContentSecurityPolicyEnforcement, const String& initiatorIdentifier);
+        void loadAsynchronously(ScriptExecutionContext*, const URL&, FetchOptions::Mode, ContentSecurityPolicyEnforcement, const String& initiatorIdentifier, WorkerScriptLoaderClient*);
 
         void notifyError();
 
@@ -63,7 +62,7 @@ namespace WebCore {
         bool failed() const { return m_failed; }
         unsigned long identifier() const { return m_identifier; }
 
-        void didReceiveResponse(unsigned long /*identifier*/, const ResourceResponse&) override;
+        void didReceiveResponse(unsigned long identifier, const ResourceResponse&) override;
         void didReceiveData(const char* data, int dataLength) override;
         void didFinishLoading(unsigned long identifier, double) override;
         void didFail(const ResourceError&) override;
@@ -74,19 +73,19 @@ namespace WebCore {
         WorkerScriptLoader();
         ~WorkerScriptLoader();
 
-        std::unique_ptr<ResourceRequest> createResourceRequest();
+        std::unique_ptr<ResourceRequest> createResourceRequest(const String& initiatorIdentifier);
         void notifyFinished();
 
-        WorkerScriptLoaderClient* m_client;
+        WorkerScriptLoaderClient* m_client { nullptr };
         RefPtr<ThreadableLoader> m_threadableLoader;
         String m_responseEncoding;        
         RefPtr<TextResourceDecoder> m_decoder;
         StringBuilder m_script;
         URL m_url;
         URL m_responseURL;
-        bool m_failed;
-        unsigned long m_identifier;
-        bool m_finishing;
+        unsigned long m_identifier { 0 };
+        bool m_failed { false };
+        bool m_finishing { false };
     };
 
 } // namespace WebCore
index 573894c..51f42f5 100644 (file)
@@ -72,9 +72,10 @@ unsigned WorkerThread::workerThreadCount()
 struct WorkerThreadStartupData {
     WTF_MAKE_NONCOPYABLE(WorkerThreadStartupData); WTF_MAKE_FAST_ALLOCATED;
 public:
-    WorkerThreadStartupData(const URL& scriptURL, const String& userAgent, const String& sourceCode, WorkerThreadStartMode, const ContentSecurityPolicyResponseHeaders&, bool shouldBypassMainWorldContentSecurityPolicy, const SecurityOrigin* topOrigin);
+    WorkerThreadStartupData(const URL& scriptURL, const String& identifier, const String& userAgent, const String& sourceCode, WorkerThreadStartMode, const ContentSecurityPolicyResponseHeaders&, bool shouldBypassMainWorldContentSecurityPolicy, const SecurityOrigin* topOrigin);
 
     URL m_scriptURL;
+    String m_identifier;
     String m_userAgent;
     String m_sourceCode;
     WorkerThreadStartMode m_startMode;
@@ -83,8 +84,9 @@ public:
     RefPtr<SecurityOrigin> m_topOrigin;
 };
 
-WorkerThreadStartupData::WorkerThreadStartupData(const URL& scriptURL, const String& userAgent, const String& sourceCode, WorkerThreadStartMode startMode, const ContentSecurityPolicyResponseHeaders& contentSecurityPolicyResponseHeaders, bool shouldBypassMainWorldContentSecurityPolicy, const SecurityOrigin* topOrigin)
+WorkerThreadStartupData::WorkerThreadStartupData(const URL& scriptURL, const String& identifier, const String& userAgent, const String& sourceCode, WorkerThreadStartMode startMode, const ContentSecurityPolicyResponseHeaders& contentSecurityPolicyResponseHeaders, bool shouldBypassMainWorldContentSecurityPolicy, const SecurityOrigin* topOrigin)
     : m_scriptURL(scriptURL.isolatedCopy())
+    , m_identifier(identifier.isolatedCopy())
     , m_userAgent(userAgent.isolatedCopy())
     , m_sourceCode(sourceCode.isolatedCopy())
     , m_startMode(startMode)
@@ -94,12 +96,12 @@ WorkerThreadStartupData::WorkerThreadStartupData(const URL& scriptURL, const Str
 {
 }
 
-WorkerThread::WorkerThread(const URL& scriptURL, const String& userAgent, const String& sourceCode, WorkerLoaderProxy& workerLoaderProxy, WorkerReportingProxy& workerReportingProxy, WorkerThreadStartMode startMode, const ContentSecurityPolicyResponseHeaders& contentSecurityPolicyResponseHeaders, bool shouldBypassMainWorldContentSecurityPolicy, const SecurityOrigin* topOrigin, IDBClient::IDBConnectionProxy* connectionProxy, SocketProvider* socketProvider, JSC::RuntimeFlags runtimeFlags)
+WorkerThread::WorkerThread(const URL& scriptURL, const String& identifier, const String& userAgent, const String& sourceCode, WorkerLoaderProxy& workerLoaderProxy, WorkerReportingProxy& workerReportingProxy, WorkerThreadStartMode startMode, const ContentSecurityPolicyResponseHeaders& contentSecurityPolicyResponseHeaders, bool shouldBypassMainWorldContentSecurityPolicy, const SecurityOrigin* topOrigin, IDBClient::IDBConnectionProxy* connectionProxy, SocketProvider* socketProvider, JSC::RuntimeFlags runtimeFlags)
     : m_threadID(0)
     , m_workerLoaderProxy(workerLoaderProxy)
     , m_workerReportingProxy(workerReportingProxy)
     , m_runtimeFlags(runtimeFlags)
-    , m_startupData(std::make_unique<WorkerThreadStartupData>(scriptURL, userAgent, sourceCode, startMode, contentSecurityPolicyResponseHeaders, shouldBypassMainWorldContentSecurityPolicy, topOrigin))
+    , m_startupData(std::make_unique<WorkerThreadStartupData>(scriptURL, identifier, userAgent, sourceCode, startMode, contentSecurityPolicyResponseHeaders, shouldBypassMainWorldContentSecurityPolicy, topOrigin))
 #if ENABLE(INDEXED_DATABASE)
     , m_idbConnectionProxy(connectionProxy)
 #endif
@@ -159,7 +161,7 @@ void WorkerThread::workerThread()
 
     {
         LockHolder lock(m_threadCreationMutex);
-        m_workerGlobalScope = createWorkerGlobalScope(m_startupData->m_scriptURL, m_startupData->m_userAgent, m_startupData->m_contentSecurityPolicyResponseHeaders, m_startupData->m_shouldBypassMainWorldContentSecurityPolicy, WTFMove(m_startupData->m_topOrigin));
+        m_workerGlobalScope = createWorkerGlobalScope(m_startupData->m_scriptURL, m_startupData->m_identifier, m_startupData->m_userAgent, m_startupData->m_contentSecurityPolicyResponseHeaders, m_startupData->m_shouldBypassMainWorldContentSecurityPolicy, WTFMove(m_startupData->m_topOrigin));
 
         if (m_runLoop.terminated()) {
             // The worker was terminated before the thread had a chance to run. Since the context didn't exist yet,
index 27f3385..ab0a81d 100644 (file)
@@ -81,10 +81,10 @@ public:
     JSC::RuntimeFlags runtimeFlags() const { return m_runtimeFlags; }
 
 protected:
-    WorkerThread(const URL&, const String& userAgent, const String& sourceCode, WorkerLoaderProxy&, WorkerReportingProxy&, WorkerThreadStartMode, const ContentSecurityPolicyResponseHeaders&, bool shouldBypassMainWorldContentSecurityPolicy, const SecurityOrigin* topOrigin, IDBClient::IDBConnectionProxy*, SocketProvider*, JSC::RuntimeFlags);
+    WorkerThread(const URL&, const String& identifier, const String& userAgent, const String& sourceCode, WorkerLoaderProxy&, WorkerReportingProxy&, WorkerThreadStartMode, const ContentSecurityPolicyResponseHeaders&, bool shouldBypassMainWorldContentSecurityPolicy, const SecurityOrigin* topOrigin, IDBClient::IDBConnectionProxy*, SocketProvider*, JSC::RuntimeFlags);
 
     // Factory method for creating a new worker context for the thread.
-    virtual Ref<WorkerGlobalScope> createWorkerGlobalScope(const URL&, const String& userAgent, const ContentSecurityPolicyResponseHeaders&, bool shouldBypassMainWorldContentSecurityPolicy, PassRefPtr<SecurityOrigin> topOrigin) = 0;
+    virtual Ref<WorkerGlobalScope> createWorkerGlobalScope(const URL&, const String& identifier, const String& userAgent, const ContentSecurityPolicyResponseHeaders&, bool shouldBypassMainWorldContentSecurityPolicy, PassRefPtr<SecurityOrigin> topOrigin) = 0;
 
     // Executes the event loop for the worker thread. Derived classes can override to perform actions before/after entering the event loop.
     virtual void runEventLoop();
index a5bb59d..509a9a9 100644 (file)
@@ -649,6 +649,7 @@ ExceptionOr<void> XMLHttpRequest::createRequest()
 
     ResourceRequest request(m_url);
     request.setRequester(ResourceRequest::Requester::XHR);
+    request.setInitiatorIdentifier(scriptExecutionContext()->resourceRequestIdentifier());
     request.setHTTPMethod(m_method);
 
     if (m_requestEntityBody) {
index 02b0051..0d2a3ab 100644 (file)
@@ -1,3 +1,162 @@
+2016-11-09  Joseph Pecoraro  <pecoraro@apple.com>
+
+        Web Inspector: Associate Worker Resources with the Worker and not the Page
+        https://bugs.webkit.org/show_bug.cgi?id=164342
+        <rdar://problem/29075775>
+
+        Reviewed by Timothy Hatcher.
+
+        A Target may have its own list of Resource. For example, Workers may
+        request any resources via XHR/Fetch. So we associate a ResourceCollection
+        with a Target, and ensure we show them in Web Inspector as you would expect.
+        At this point, Target starts acting like Frame. Target has a resourceCollection
+        and extraScriptsCollection just like Frame. Target has events for ResourceAdded
+        just like Frame.
+
+        Even though Resource loads are happening in Workers, the Network data
+        still comes from the Page's Network agent. The added "targetId" data
+        with the Resource will let us associate a Resoure with a Target.
+
+        When opening inspector after a page has loaded, the frontend loads Resources
+        via the Page.getResourceTree path. In this case, the frontend may be
+        informed of Resources for a Target that it does not know about yet. In
+        these cases, it sets them aside as Orphaned resources for a target. Later,
+        when that Target is created, it will adopt its Orphaned resources.
+
+        Places that used to listen to just Frame.Event.ResourceWasAdded should now
+        also listen for Target.Event.ResourceAdded to ensure it sees the resources
+        associated with non-page targets.
+
+        * UserInterface/Protocol/Target.js:
+        (WebInspector.Target):
+        (WebInspector.Target.prototype.get identifier):
+        (WebInspector.Target.prototype.get resourceCollection):
+        (WebInspector.Target.prototype.get extraScriptCollection):
+        (WebInspector.Target.prototype.addResource):
+        (WebInspector.Target.prototype.adoptResource):
+        (WebInspector.Target.prototype.addScript):
+        Give Target resource collections.
+
+        (WebInspector.MainTarget):
+        (WebInspector.MainTarget.prototype.get mainResource):
+        Pass through to the FrameResourceManager for the MainTarget.
+
+        (WebInspector.WorkerTarget):
+        (WebInspector.WorkerTarget.prototype.initialize):
+        Adopt orphaned resources on creation.
+
+        * UserInterface/Models/Resource.js:
+        (WebInspector.Resource):
+        (WebInspector.Resource.prototype.get target):
+        (WebInspector.Resource.prototype.get type):
+        Resource now has a Target. During creation, if there is a targetId
+        then we must produce a Target or null (orphaned).
+
+        (WebInspector.Resource.prototype.associateWithScript):
+        When associating a Resource with a Script, we can use this opportunity
+        to convert from an XML / Other type to Script.
+
+        * UserInterface/Models/Script.js:
+        (WebInspector.Script.prototype._resolveResource):
+        When associating Scripts with a resource we must associate resources
+        from within the proper Target. If it is the Main target we still use
+        the FrameResourceManager which keep searches across all Frames.
+
+        * UserInterface/Protocol/NetworkObserver.js:
+        (WebInspector.NetworkObserver.prototype.requestWillBeSent):
+        * UserInterface/Controllers/FrameResourceManager.js:
+        (WebInspector.FrameResourceManager.prototype.initialize):
+        (WebInspector.FrameResourceManager.prototype.frameDidNavigate):
+        (WebInspector.FrameResourceManager.prototype.resourceRequestWillBeSent):
+        (WebInspector.FrameResourceManager.prototype.resourceRequestWasServedFromMemoryCache):
+        (WebInspector.FrameResourceManager.prototype.resourceRequestDidReceiveResponse):
+        (WebInspector.FrameResourceManager.prototype.adoptOrphanedResourcesForTarget):
+        (WebInspector.FrameResourceManager.prototype._addNewResourceToFrameOrTarget):
+        (WebInspector.FrameResourceManager.prototype._addResourceToTarget):
+        (WebInspector.FrameResourceManager.prototype._createResource):
+        (WebInspector.FrameResourceManager.prototype._addFrameTreeFromFrameResourceTreePayload):
+        (WebInspector.FrameResourceManager.prototype._addOrphanedResource):
+        (WebInspector.FrameResourceManager.prototype._mainFrameDidChange):
+        (WebInspector.FrameResourceManager.prototype._addNewResourceToFrame): Deleted.
+        When creating Resources from Network events we may now have a targetId.
+        Once created a Resource must be associated with a Frame, Target, or orphaned.
+
+        * UserInterface/Main.html:
+        * UserInterface/Views/TargetTreeElement.js: Removed.
+        * UserInterface/Views/WorkerTreeElement.js: Added.
+        (WebInspector.WorkerTreeElement):
+        (WebInspector.WorkerTreeElement.prototype.get target):
+        (WebInspector.WorkerTreeElement.prototype.onexpand):
+        (WebInspector.WorkerTreeElement.prototype.oncollapse):
+        (WebInspector.WorkerTreeElement.prototype.onpopulate):
+        (WebInspector.WorkerTreeElement.prototype.updateSourceMapResources):
+        (WebInspector.WorkerTreeElement.prototype.onattach):
+        (WebInspector.WorkerTreeElement.prototype.compareChildTreeElements):
+        (WebInspector.WorkerTreeElement.prototype._handleContextMenuEvent):
+        (WebInspector.WorkerTreeElement.prototype._scriptAdded):
+        (WebInspector.WorkerTreeElement.prototype._resourceAdded):
+        Convert TargetTreeElement to WorkerTreeElement as that is clearer.
+        Behave like FrameTreeElement and populate resources on creation,
+        handle SourceMapResource, etc.
+
+        * UserInterface/Views/FolderizedTreeElement.js:
+        (WebInspector.FolderizedTreeElement.prototype.registerFolderizeSettings):
+        (WebInspector.FolderizedTreeElement.prototype._compareTreeElementsByMainTitle):
+        (WebInspector.FolderizedTreeElement.prototype._parentTreeElementForRepresentedObject):
+        If the display name for a folder is `null` then there is no folder,
+        and place such child tree elements at the top level. This will be
+        the case for a Worker's Script's, which we choose not to folderize.
+
+        * UserInterface/Controllers/DebuggerManager.js:
+        (WebInspector.DebuggerManager.prototype.scriptDidParse):
+        * UserInterface/Controllers/TargetManager.js:
+        (WebInspector.TargetManager.prototype.targetForIdentifier):
+
+        * UserInterface/Models/DefaultDashboard.js:
+        (WebInspector.DefaultDashboard):
+        * UserInterface/Controllers/TimelineManager.js:
+        (WebInspector.TimelineManager):
+        * UserInterface/Controllers/WorkerManager.js:
+        (WebInspector.WorkerManager.prototype.workerCreated):
+        * UserInterface/Views/OpenResourceDialog.js:
+        (WebInspector.OpenResourceDialog.prototype.didDismissDialog):
+        (WebInspector.OpenResourceDialog.prototype.didPresentDialog):
+        (WebInspector.OpenResourceDialog.prototype._addScriptsForTarget): Deleted.
+        (WebInspector.OpenResourceDialog.prototype._addResourcesForTarget): Added.
+        Ensure those that listen for Frame.Event.ResourceWasAdded now also
+        listen for Target.Event.ResourceAdded.
+
+        * UserInterface/Views/ContextMenuUtilities.js:
+        (WebInspector.appendContextMenuItemsForSourceCode):
+        (WebInspector.appendContextMenuItemsForResource): Deleted.
+        * UserInterface/Views/ResourceTimelineDataGridNode.js:
+        (WebInspector.ResourceTimelineDataGridNode.prototype.appendContextMenuItems):
+        * UserInterface/Views/ResourceTreeElement.js:
+        (WebInspector.ResourceTreeElement.prototype._updateTitles):
+        (WebInspector.ResourceTreeElement.prototype._handleContextMenuEvent):
+        Generalize ContextMenu helper to SourceCode so it can be used on a Script or Resource.
+
+        * UserInterface/Views/ResourceDetailsSidebarPanel.js:
+        (WebInspector.ResourceDetailsSidebarPanel.prototype.inspect):
+        When looking at a WorkerTarget's mainResource (Script) show the
+        Resource Details sidebar for its Resource.
+
+        * UserInterface/Views/ResourceSidebarPanel.js:
+        (WebInspector.ResourceSidebarPanel):
+        (WebInspector.ResourceSidebarPanel.prototype._scriptWasAdded):
+        (WebInspector.ResourceSidebarPanel.prototype._scriptsCleared):
+        (WebInspector.ResourceSidebarPanel.prototype._addTargetWithMainResource):
+        (WebInspector.ResourceSidebarPanel.prototype._targetRemoved):
+        (WebInspector.ResourceSidebarPanel.prototype._addScriptForNonMainTarget): Deleted.
+        Simplify ResourceSidebarPanel to only handle adding WorkerTreeElements,
+        which will do the rest of the work for their Resources/Scripts.
+
+        * UserInterface/Views/SourceCodeTreeElement.js:
+        (WebInspector.SourceCodeTreeElement.prototype.descendantResourceTreeElementTypeDidChange):
+        When we were changing the type of a resource, it would remove and re-insert.
+        This would collapse the parent if it was the only child in removal, and not
+        expand the parent when re-inserting. This ensures we re-expand.
+
 2016-11-09  Nikita Vasilyev  <nvasilyev@apple.com>
 
         Web Inspector: Settings tab sections overlap each other in docked Inspector window
index 2101b02..0ff31e4 100644 (file)
@@ -57,7 +57,7 @@ WebInspector.DebuggerManager = class DebuggerManager extends WebInspector.Object
         this._allExceptionsBreakpoint.resolved = true;
 
         this._allUncaughtExceptionsBreakpoint = new WebInspector.Breakpoint(specialBreakpointLocation, !this._allUncaughtExceptionsBreakpointEnabledSetting.value);
-        
+
         this._assertionsBreakpoint = new WebInspector.Breakpoint(specialBreakpointLocation, !this._assertionsBreakpointEnabledSetting.value);
         this._assertionsBreakpoint.resolved = true;
 
@@ -644,13 +644,20 @@ WebInspector.DebuggerManager = class DebuggerManager extends WebInspector.Object
 
         let script = new WebInspector.Script(target, scriptIdentifier, new WebInspector.TextRange(startLine, startColumn, endLine, endColumn), url, isContentScript, sourceURL, sourceMapURL);
 
-        if (!target.mainResource && target.type === WebInspector.Target.Type.Worker) {
-            if (script.url === target.name)
+        targetData.addScript(script);
+
+        if (target !== WebInspector.mainTarget && !target.mainResource) {
+            // FIXME: <https://webkit.org/b/164427> Web Inspector: WorkerTarget's mainResource should be a Resource not a Script
+            // We make the main resource of a WorkerTarget the Script instead of the Resource
+            // because the frontend may not be informed of the Resource. We should gaurantee
+            // the frontend is informed of the Resource.
+            if (script.url === target.name) {
                 target.mainResource = script;
+                if (script.resource)
+                    target.resourceCollection.remove(script.resource);
+            }
         }
 
-        targetData.addScript(script);
-
         if (isWebKitInternalScript(script.sourceURL)) {
             this._internalWebKitScripts.push(script);
             if (!WebInspector.isDebugUIEnabled())
@@ -662,6 +669,9 @@ WebInspector.DebuggerManager = class DebuggerManager extends WebInspector.Object
             return;
 
         this.dispatchEventToListeners(WebInspector.DebuggerManager.Event.ScriptAdded, {script});
+
+        if (target !== WebInspector.mainTarget && !script.isMainResource() && !script.resource)
+            target.addScript(script);
     }
 
     // Private
index 8b731d7..43d97c8 100644 (file)
@@ -48,6 +48,7 @@ WebInspector.FrameResourceManager = class FrameResourceManager extends WebInspec
         this._frameIdentifierMap = {};
         this._mainFrame = null;
         this._resourceRequestIdentifierMap = {};
+        this._orphanedResources = new Map;
 
         if (this._mainFrame !== oldMainFrame)
             this._mainFrameDidChange(oldMainFrame);
@@ -91,7 +92,7 @@ WebInspector.FrameResourceManager = class FrameResourceManager extends WebInspec
             // If the frame wasn't known before now, then the main resource was loaded instantly (about:blank, etc.)
             // Make a new resource (which will make the frame). Mark will mark it as loaded at the end too since we
             // don't expect any more events about the load finishing for these frames.
-            var frameResource = this._addNewResourceToFrame(null, framePayload.id, framePayload.loaderId, framePayload.url, null, null, null, null, null, framePayload.name, framePayload.securityOrigin);
+            var frameResource = this._addNewResourceToFrameOrTarget(null, framePayload.id, framePayload.loaderId, framePayload.url, null, null, null, null, null, framePayload.name, framePayload.securityOrigin);
             frame = frameResource.parentFrame;
             frameWasLoadedInstantly = true;
 
@@ -169,7 +170,7 @@ WebInspector.FrameResourceManager = class FrameResourceManager extends WebInspec
             this._mainFrameDidChange(oldMainFrame);
     }
 
-    resourceRequestWillBeSent(requestIdentifier, frameIdentifier, loaderIdentifier, request, type, redirectResponse, timestamp, initiator)
+    resourceRequestWillBeSent(requestIdentifier, frameIdentifier, loaderIdentifier, request, type, redirectResponse, timestamp, initiator, targetId)
     {
         // Called from WebInspector.NetworkObserver.
 
@@ -189,6 +190,7 @@ WebInspector.FrameResourceManager = class FrameResourceManager extends WebInspec
         if (resource) {
             // This is an existing request which is being redirected, update the resource.
             console.assert(redirectResponse);
+            console.assert(!targetId);
             resource.updateForRedirectResponse(request.url, request.headers, elapsedTime);
             return;
         }
@@ -196,7 +198,7 @@ WebInspector.FrameResourceManager = class FrameResourceManager extends WebInspec
         var initiatorSourceCodeLocation = this._initiatorSourceCodeLocationFromPayload(initiator);
 
         // This is a new request, make a new resource and add it to the right frame.
-        resource = this._addNewResourceToFrame(requestIdentifier, frameIdentifier, loaderIdentifier, request.url, type, request.method, request.headers, request.postData, elapsedTime, null, null, initiatorSourceCodeLocation, originalRequestWillBeSentTimestamp);
+        resource = this._addNewResourceToFrameOrTarget(requestIdentifier, frameIdentifier, loaderIdentifier, request.url, type, request.method, request.headers, request.postData, elapsedTime, null, null, initiatorSourceCodeLocation, originalRequestWillBeSentTimestamp, targetId);
 
         // Associate the resource with the requestIdentifier so it can be found in future loading events.
         this._resourceRequestIdentifierMap[requestIdentifier] = resource;
@@ -234,7 +236,7 @@ WebInspector.FrameResourceManager = class FrameResourceManager extends WebInspec
         var elapsedTime = WebInspector.timelineManager.computeElapsedTime(timestamp);
         var initiatorSourceCodeLocation = this._initiatorSourceCodeLocationFromPayload(initiator);
         var response = cachedResourcePayload.response;
-        var resource = this._addNewResourceToFrame(requestIdentifier, frameIdentifier, loaderIdentifier, cachedResourcePayload.url, cachedResourcePayload.type, "GET", null, null, elapsedTime, null, null, initiatorSourceCodeLocation);
+        var resource = this._addNewResourceToFrameOrTarget(requestIdentifier, frameIdentifier, loaderIdentifier, cachedResourcePayload.url, cachedResourcePayload.type, "GET", null, null, elapsedTime, null, null, initiatorSourceCodeLocation);
         resource.markAsCached();
         resource.updateForResponse(cachedResourcePayload.url, response.mimeType, cachedResourcePayload.type, response.headers, response.status, response.statusText, elapsedTime, response.timing);
         resource.increaseSize(cachedResourcePayload.bodySize, elapsedTime);
@@ -279,7 +281,7 @@ WebInspector.FrameResourceManager = class FrameResourceManager extends WebInspec
         // If we haven't found an existing Resource by now, then it is a resource that was loading when the inspector
         // opened and we just missed the resourceRequestWillBeSent for it. So make a new resource and add it.
         if (!resource) {
-            resource = this._addNewResourceToFrame(requestIdentifier, frameIdentifier, loaderIdentifier, response.url, type, null, response.requestHeaders, null, elapsedTime, null, null, null);
+            resource = this._addNewResourceToFrameOrTarget(requestIdentifier, frameIdentifier, loaderIdentifier, response.url, type, null, response.requestHeaders, null, elapsedTime, null, null, null);
 
             // Associate the resource with the requestIdentifier so it can be found in future loading events.
             this._resourceRequestIdentifierMap[requestIdentifier] = resource;
@@ -387,15 +389,25 @@ WebInspector.FrameResourceManager = class FrameResourceManager extends WebInspec
         return this._mainFrame.resourceForURL(url, true);
     }
 
+    adoptOrphanedResourcesForTarget(target)
+    {
+        let resources = this._orphanedResources.take(target.identifier);
+        if (!resources)
+            return;
+
+        for (let resource of resources)
+            target.adoptResource(resource);
+    }
+
     // Private
 
-    _addNewResourceToFrame(requestIdentifier, frameIdentifier, loaderIdentifier, url, type, requestMethod, requestHeaders, requestData, elapsedTime, frameName, frameSecurityOrigin, initiatorSourceCodeLocation, originalRequestWillBeSentTimestamp)
+    _addNewResourceToFrameOrTarget(requestIdentifier, frameIdentifier, loaderIdentifier, url, type, requestMethod, requestHeaders, requestData, elapsedTime, frameName, frameSecurityOrigin, initiatorSourceCodeLocation, originalRequestWillBeSentTimestamp, targetId)
     {
         console.assert(!this._waitingForMainFrameResourceTreePayload);
 
-        var resource = null;
+        let resource = null;
 
-        var frame = this.frameForIdentifier(frameIdentifier);
+        let frame = this.frameForIdentifier(frameIdentifier);
         if (frame) {
             // This is a new request for an existing frame, which might be the main resource or a new resource.
             if (frame.mainResource.url === url && frame.loaderIdentifier === loaderIdentifier)
@@ -403,12 +415,18 @@ WebInspector.FrameResourceManager = class FrameResourceManager extends WebInspec
             else if (frame.provisionalMainResource && frame.provisionalMainResource.url === url && frame.provisionalLoaderIdentifier === loaderIdentifier)
                 resource = frame.provisionalMainResource;
             else {
-                resource = new WebInspector.Resource(url, null, type, loaderIdentifier, requestIdentifier, requestMethod, requestHeaders, requestData, elapsedTime, initiatorSourceCodeLocation, originalRequestWillBeSentTimestamp);
-                this._addResourceToFrame(frame, resource);
+                resource = new WebInspector.Resource(url, null, type, loaderIdentifier, targetId, requestIdentifier, requestMethod, requestHeaders, requestData, elapsedTime, initiatorSourceCodeLocation, originalRequestWillBeSentTimestamp);
+                if (resource.target === WebInspector.mainTarget)
+                    this._addResourceToFrame(frame, resource);
+                else if (resource.target)
+                    resource.target.addResource(resource);
+                else
+                    this._addOrphanedResource(resource, targetId);
             }
         } else {
             // This is a new request for a new frame, which is always the main resource.
-            resource = new WebInspector.Resource(url, null, type, loaderIdentifier, requestIdentifier, requestMethod, requestHeaders, requestData, elapsedTime, initiatorSourceCodeLocation, originalRequestWillBeSentTimestamp);
+            console.assert(!targetId)
+            resource = new WebInspector.Resource(url, null, type, loaderIdentifier, targetId, requestIdentifier, requestMethod, requestHeaders, requestData, elapsedTime, initiatorSourceCodeLocation, originalRequestWillBeSentTimestamp);
             frame = new WebInspector.Frame(frameIdentifier, frameName, frameSecurityOrigin, loaderIdentifier, resource);
             this._frameIdentifierMap[frame.id] = frame;
 
@@ -448,6 +466,14 @@ WebInspector.FrameResourceManager = class FrameResourceManager extends WebInspec
         frame.addResource(resource);
     }
 
+    _addResourceToTarget(target, resource)
+    {
+        console.assert(target !== WebInspector.mainTarget);
+        console.assert(resource);
+
+        target.addResource(resource);
+    }
+
     _initiatorSourceCodeLocationFromPayload(initiatorPayload)
     {
         if (!initiatorPayload)
@@ -533,7 +559,7 @@ WebInspector.FrameResourceManager = class FrameResourceManager extends WebInspec
 
     _createResource(payload, framePayload)
     {
-        var resource = new WebInspector.Resource(payload.url, payload.mimeType, payload.type, framePayload.loaderId);
+        var resource = new WebInspector.Resource(payload.url, payload.mimeType, payload.type, framePayload.loaderId, payload.targetId);
 
         if (payload.sourceMapURL)
             WebInspector.sourceMapManager.downloadSourceMap(payload.sourceMapURL, resource.url, resource);
@@ -560,7 +586,12 @@ WebInspector.FrameResourceManager = class FrameResourceManager extends WebInspec
                 continue;
 
             var resource = this._createResource(resourcePayload, payload);
-            frame.addResource(resource);
+            if (resource.target === WebInspector.mainTarget)
+                frame.addResource(resource);
+            else if (resource.target)
+                resource.target.addResource(resource);
+            else
+                this._addOrphanedResource(resource, resourcePayload.targetId);
 
             if (resourcePayload.failed || resourcePayload.canceled)
                 resource.markAsFailed(resourcePayload.canceled);
@@ -573,6 +604,17 @@ WebInspector.FrameResourceManager = class FrameResourceManager extends WebInspec
         return frame;
     }
 
+    _addOrphanedResource(resource, targetId)
+    {
+        let resources = this._orphanedResources.get(targetId);
+        if (!resources) {
+            resources = [];
+            this._orphanedResources.set(targetId, resources);
+        }
+
+        resources.push(resource);
+    }
+
     _dispatchFrameWasAddedEvent(frame)
     {
         this.dispatchEventToListeners(WebInspector.FrameResourceManager.Event.FrameWasAdded, {frame});
@@ -584,6 +626,7 @@ WebInspector.FrameResourceManager = class FrameResourceManager extends WebInspec
             oldMainFrame.unmarkAsMainFrame();
         if (this._mainFrame)
             this._mainFrame.markAsMainFrame();
+
         this.dispatchEventToListeners(WebInspector.FrameResourceManager.Event.MainFrameDidChange, {oldMainFrame});
     }
 
@@ -597,5 +640,5 @@ WebInspector.FrameResourceManager = class FrameResourceManager extends WebInspec
 WebInspector.FrameResourceManager.Event = {
     FrameWasAdded: "frame-resource-manager-frame-was-added",
     FrameWasRemoved: "frame-resource-manager-frame-was-removed",
-    MainFrameDidChange: "frame-resource-manager-main-frame-did-change"
+    MainFrameDidChange: "frame-resource-manager-main-frame-did-change",
 };
index c08d217..7e987d4 100644 (file)
@@ -41,6 +41,19 @@ WebInspector.TargetManager = class TargetManager extends WebInspector.Object
         return this._targets;
     }
 
+    targetForIdentifier(targetId)
+    {
+        if (!targetId)
+            return null;
+
+        for (let target of this._targets) {
+            if (target.identifier === targetId)
+                return target;
+        }
+
+        return null;
+    }
+
     addTarget(target)
     {
         this._targets.add(target);
index 9c5925d..7400e37 100644 (file)
@@ -32,6 +32,7 @@ WebInspector.TimelineManager = class TimelineManager extends WebInspector.Object
         WebInspector.Frame.addEventListener(WebInspector.Frame.Event.ProvisionalLoadStarted, this._provisionalLoadStarted, this);
         WebInspector.Frame.addEventListener(WebInspector.Frame.Event.MainResourceDidChange, this._mainResourceDidChange, this);
         WebInspector.Frame.addEventListener(WebInspector.Frame.Event.ResourceWasAdded, this._resourceWasAdded, this);
+        WebInspector.Target.addEventListener(WebInspector.Target.Event.ResourceAdded, this._resourceWasAdded, this);
 
         WebInspector.heapManager.addEventListener(WebInspector.HeapManager.Event.GarbageCollected, this._garbageCollected, this);
         WebInspector.memoryManager.addEventListener(WebInspector.MemoryManager.Event.MemoryPressure, this._memoryPressure, this);
index 2ff0521..99cd3f6 100644 (file)
@@ -40,7 +40,7 @@ WebInspector.WorkerManager = class WorkerManager extends WebInspector.Object
     workerCreated(workerId, url)
     {
         let connection = new InspectorBackend.WorkerConnection(workerId);
-        let workerTarget = new WebInspector.WorkerTarget(url, connection);
+        let workerTarget = new WebInspector.WorkerTarget(workerId, url, connection);
         WebInspector.targetManager.addTarget(workerTarget);
 
         this._connections.set(workerId, connection);
index 27db426..f69c949 100644 (file)
     <script src="Views/StackedLineChart.js"></script>
     <script src="Views/StorageSidebarPanel.js"></script>
     <script src="Views/SyntaxHighlightingSupport.js"></script>
-    <script src="Views/TargetTreeElement.js"></script>
     <script src="Views/TextContentView.js"></script>
     <script src="Views/TextNavigationItem.js"></script>
     <script src="Views/TextResourceContentView.js"></script>
+    <script src="Views/TextToggleButtonNavigationItem.js"></script>
     <script src="Views/TimelineRecordBar.js"></script>
     <script src="Views/TimelineRecordFrame.js"></script>
     <script src="Views/TimelineRecordingContentView.js"></script>
     <script src="Views/TimelineRuler.js"></script>
     <script src="Views/TitleView.js"></script>
     <script src="Views/ToggleButtonNavigationItem.js"></script>
-    <script src="Views/TextToggleButtonNavigationItem.js"></script>
     <script src="Views/ToggleControlToolbarItem.js"></script>
     <script src="Views/Toolbar.js"></script>
     <script src="Views/TreeElementStatusButton.js"></script>
     <script src="Views/TypeTokenView.js"></script>
     <script src="Views/TypeTreeElement.js"></script>
     <script src="Views/TypeTreeView.js"></script>
+    <script src="Views/WorkerTreeElement.js"></script>
 
     <script src="Views/VisualStyleDetailsPanel.js"></script>
     <script src="Views/VisualStylePropertyEditor.js"></script>
index 283f5ff..33b8d6d 100644 (file)
@@ -37,6 +37,7 @@ WebInspector.DefaultDashboard = class DefaultDashboard extends WebInspector.Obje
 
         // Necessary events required to track load of resources.
         WebInspector.Frame.addEventListener(WebInspector.Frame.Event.ResourceWasAdded, this._resourceWasAdded, this);
+        WebInspector.Target.addEventListener(WebInspector.Target.Event.ResourceAdded, this._resourceWasAdded, this);
         WebInspector.frameResourceManager.addEventListener(WebInspector.FrameResourceManager.Event.FrameWasAdded, this._frameWasAdded, this);
 
         // Necessary events required to track console messages.
index 70cb4fe..4f3c0b7 100644 (file)
@@ -26,7 +26,7 @@
 
 WebInspector.Resource = class Resource extends WebInspector.SourceCode
 {
-    constructor(url, mimeType, type, loaderIdentifier, requestIdentifier, requestMethod, requestHeaders, requestData, requestSentTimestamp, initiatorSourceCodeLocation, originalRequestWillBeSentTimestamp)
+    constructor(url, mimeType, type, loaderIdentifier, targetId, requestIdentifier, requestMethod, requestHeaders, requestData, requestSentTimestamp, initiatorSourceCodeLocation, originalRequestWillBeSentTimestamp)
     {
         super();
 
@@ -60,6 +60,7 @@ WebInspector.Resource = class Resource extends WebInspector.SourceCode
         this._transferSize = NaN;
         this._cached = false;
         this._timingData = new WebInspector.ResourceTimingData(this);
+        this._target = targetId ? WebInspector.targetManager.targetForIdentifier(targetId) : WebInspector.mainTarget;
 
         if (this._initiatorSourceCodeLocation && this._initiatorSourceCodeLocation.sourceCode instanceof WebInspector.Resource)
             this._initiatorSourceCodeLocation.sourceCode.addInitiatedResource(this);
@@ -127,6 +128,8 @@ WebInspector.Resource = class Resource extends WebInspector.SourceCode
 
     // Public
 
+    get target() { return this._target; }
+    get type() { return this._type; }
     get timingData() { return this._timingData; }
 
     get url()
@@ -168,11 +171,6 @@ WebInspector.Resource = class Resource extends WebInspector.SourceCode
         return this._originalRequestWillBeSentTimestamp;
     }
 
-    get type()
-    {
-        return this._type;
-    }
-
     get mimeType()
     {
         return this._mimeType;
@@ -672,8 +670,8 @@ WebInspector.Resource = class Resource extends WebInspector.SourceCode
 
         this._scripts.push(script);
 
-        if (this._type === WebInspector.Resource.Type.Other) {
-            var oldType = this._type;
+        if (this._type === WebInspector.Resource.Type.Other || this._type === WebInspector.Resource.Type.XHR) {
+            let oldType = this._type;
             this._type = WebInspector.Resource.Type.Script;
             this.dispatchEventToListeners(WebInspector.Resource.Event.TypeDidChange, {oldType});
         }
index 42022c5..1871966 100644 (file)
@@ -266,36 +266,36 @@ WebInspector.Script = class Script extends WebInspector.SourceCode
         if (!this._url)
             return null;
 
-        // Only associate Scripts on the Page with Resources on the Page.
+        let resolver = WebInspector.frameResourceManager;
         if (this._target !== WebInspector.mainTarget)
-            return null;
+            resolver = this._target.resourceCollection;
 
         try {
             // Try with the Script's full URL.
-            var resource = WebInspector.frameResourceManager.resourceForURL(this.url);
+            let resource = resolver.resourceForURL(this._url);
             if (resource)
                 return resource;
 
             // Try with the Script's full decoded URL.
-            var decodedURL = decodeURI(this._url);
+            let decodedURL = decodeURI(this._url);
             if (decodedURL !== this._url) {
-                resource = WebInspector.frameResourceManager.resourceForURL(decodedURL);
+                resource = resolver.resourceForURL(decodedURL);
                 if (resource)
                     return resource;
             }
 
             // Next try removing any fragment in the original URL.
-            var urlWithoutFragment = removeURLFragment(this._url);
+            let urlWithoutFragment = removeURLFragment(this._url);
             if (urlWithoutFragment !== this._url) {
-                resource = WebInspector.frameResourceManager.resourceForURL(urlWithoutFragment);
+                resource = resolver.resourceForURL(urlWithoutFragment);
                 if (resource)
                     return resource;
             }
 
             // Finally try removing any fragment in the decoded URL.
-            var decodedURLWithoutFragment = removeURLFragment(decodedURL);
+            let decodedURLWithoutFragment = removeURLFragment(decodedURL);
             if (decodedURLWithoutFragment !== decodedURL) {
-                resource = WebInspector.frameResourceManager.resourceForURL(decodedURLWithoutFragment);
+                resource = resolver.resourceForURL(decodedURLWithoutFragment);
                 if (resource)
                     return resource;
             }
index 8adcb6e..70f2346 100644 (file)
@@ -27,9 +27,9 @@ WebInspector.NetworkObserver = class NetworkObserver
 {
     // Events defined by the "Network" domain.
 
-    requestWillBeSent(requestId, frameId, loaderId, documentURL, request, timestamp, initiator, redirectResponse, type)
+    requestWillBeSent(requestId, frameId, loaderId, documentURL, request, timestamp, initiator, redirectResponse, type, targetId)
     {
-        WebInspector.frameResourceManager.resourceRequestWillBeSent(requestId, frameId, loaderId, request, type, redirectResponse, timestamp, initiator);
+        WebInspector.frameResourceManager.resourceRequestWillBeSent(requestId, frameId, loaderId, request, type, redirectResponse, timestamp, initiator, targetId);
     }
 
     requestServedFromCache(requestId)
index c6af80b..ac77b36 100644 (file)
 
 WebInspector.Target = class Target extends WebInspector.Object
 {
-    constructor(name, type, connection)
+    constructor(identifier, name, type, connection)
     {
         super();
 
+        this._identifier = identifier;
         this._name = name;
         this._type = type;
         this._connection = connection;
         this._executionContext = null;
         this._mainResource = null;
+        this._resourceCollection = new WebInspector.ResourceCollection;
+        this._extraScriptCollection = new WebInspector.Collection(WebInspector.Collection.TypeVerifier.Script);
 
         this._connection.target = this;
 
@@ -48,13 +51,38 @@ WebInspector.Target = class Target extends WebInspector.Object
 
     // Public
 
+    get identifier() { return this._identifier; }
     get name() { return this._name; }
     get type() { return this._type; }
     get connection() { return this._connection; }
     get executionContext() { return this._executionContext; }
 
+    get resourceCollection() { return this._resourceCollection; }
+    get extraScriptCollection() { return this._extraScriptCollection; }
+
     get mainResource() { return this._mainResource; }
     set mainResource(resource) { this._mainResource = resource; }
+
+    addResource(resource)
+    {
+        this._resourceCollection.add(resource);
+
+        this.dispatchEventToListeners(WebInspector.Target.Event.ResourceAdded, {resource});
+    }
+
+    adoptResource(resource)
+    {
+        resource._target = this;
+
+        this.addResource(resource);
+    }
+
+    addScript(script)
+    {
+        this._extraScriptCollection.add(script);
+
+        this.dispatchEventToListeners(WebInspector.Target.Event.ScriptAdded, {script});
+    }
 };
 
 WebInspector.Target.Type = {
@@ -62,11 +90,16 @@ WebInspector.Target.Type = {
     Worker: Symbol("worker"),
 };
 
+WebInspector.Target.Event = {
+    ResourceAdded: "target-resource-added",
+    ScriptAdded: "target-script-added",
+};
+
 WebInspector.MainTarget = class MainTarget extends WebInspector.Target
 {
     constructor(connection)
     {
-        super("", WebInspector.Target.Type.Main, InspectorBackend.mainConnection);
+        super("", "", WebInspector.Target.Type.Main, InspectorBackend.mainConnection);
     }
 
     // Protected (Target)
@@ -78,6 +111,12 @@ WebInspector.MainTarget = class MainTarget extends WebInspector.Target
         return WebInspector.UIString("Main Context");
     }
 
+    get mainResource()
+    {
+        let mainFrame = WebInspector.frameResourceManager.mainFrame;
+        return mainFrame ? mainFrame.mainResource : null;
+    }
+
     initialize()
     {
         this._executionContext = new WebInspector.ExecutionContext(this, WebInspector.RuntimeManager.TopLevelContextExecutionIdentifier, this.displayName, true, null);
@@ -86,9 +125,9 @@ WebInspector.MainTarget = class MainTarget extends WebInspector.Target
 
 WebInspector.WorkerTarget = class WorkerTarget extends WebInspector.Target
 {
-    constructor(name, connection)
+    constructor(workerId, name, connection)
     {
-        super(name, WebInspector.Target.Type.Worker, connection);
+        super(workerId, name, WebInspector.Target.Type.Worker, connection);
     }
 
     // Protected (Target)
@@ -100,6 +139,8 @@ WebInspector.WorkerTarget = class WorkerTarget extends WebInspector.Target
 
     initialize()
     {
+        WebInspector.frameResourceManager.adoptOrphanedResourcesForTarget(this);
+
         if (this.RuntimeAgent) {
             this.RuntimeAgent.enable();
             this._executionContext = new WebInspector.ExecutionContext(this, WebInspector.RuntimeManager.TopLevelContextExecutionIdentifier, this.displayName, false, null);
index 5786f72..4d2b51a 100644 (file)
  * THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-WebInspector.appendContextMenuItemsForResource = function(contextMenu, resource)
+WebInspector.appendContextMenuItemsForSourceCode = function(contextMenu, sourceCode)
 {
-
     console.assert(contextMenu instanceof WebInspector.ContextMenu);
     if (!(contextMenu instanceof WebInspector.ContextMenu))
         return;
 
-    console.assert(resource instanceof WebInspector.Resource);
-    if (!(resource instanceof WebInspector.Resource))
+    console.assert(sourceCode instanceof WebInspector.SourceCode);
+    if (!(sourceCode instanceof WebInspector.SourceCode))
         return;
 
-    contextMenu.appendItem(WebInspector.UIString("Open in New Tab"), () => {
-        const frame = null;
-        const alwaysOpenExternally = true;
-        WebInspector.openURL(resource.url, frame, alwaysOpenExternally);
-    });
-
-    contextMenu.appendItem(WebInspector.UIString("Copy Link Address"), () => {
-        InspectorFrontendHost.copyText(resource.url);
-    });
+    if (sourceCode.url) {
+        contextMenu.appendItem(WebInspector.UIString("Open in New Tab"), () => {
+            const frame = null;
+            const alwaysOpenExternally = true;
+            WebInspector.openURL(sourceCode.url, frame, alwaysOpenExternally);
+        });
 
-    if (resource.urlComponents.scheme !== "data") {
-        contextMenu.appendItem(WebInspector.UIString("Copy as cURL"), () => {
-            resource.generateCURLCommand();
+        contextMenu.appendItem(WebInspector.UIString("Copy Link Address"), () => {
+            InspectorFrontendHost.copyText(sourceCode.url);
         });
     }
 
+    if (sourceCode instanceof WebInspector.Resource) {
+        if (sourceCode.urlComponents.scheme !== "data") {
+            contextMenu.appendItem(WebInspector.UIString("Copy as cURL"), () => {
+                sourceCode.generateCURLCommand();
+            });
+        }
+    }
+
     contextMenu.appendItem(WebInspector.UIString("Save File"), () => {
-        resource.requestContent().then(() => {
+        sourceCode.requestContent().then(() => {
             WebInspector.saveDataToFile({
-                url: resource.url,
-                content: resource.content
+                url: sourceCode.url || "",
+                content: sourceCode.content
             });
         });
     });
index dcc99a8..a9223e3 100644 (file)
@@ -33,6 +33,7 @@ WebInspector.DebuggerSidebarPanel = class DebuggerSidebarPanel extends WebInspec
 
         WebInspector.Frame.addEventListener(WebInspector.Frame.Event.MainResourceDidChange, this._mainResourceDidChange, this);
         WebInspector.Frame.addEventListener(WebInspector.Frame.Event.ResourceWasAdded, this._resourceAdded, this);
+        WebInspector.Target.addEventListener(WebInspector.Target.Event.ResourceAdded, this._resourceAdded, this);
 
         WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.BreakpointsEnabledDidChange, this._breakpointsEnabledDidChange, this);
         WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.CallFramesDidChange, this._debuggerCallFramesDidChange, this);
index b17e7e7..fd5f659 100644 (file)
@@ -54,13 +54,14 @@ WebInspector.FolderizedTreeElement = class FolderizedTreeElement extends WebInsp
     registerFolderizeSettings(type, displayName, representedObject, treeElementConstructor)
     {
         console.assert(type);
-        console.assert(displayName);
+        console.assert(displayName || displayName === null);
         console.assert(representedObject);
         console.assert(typeof treeElementConstructor === "function");
 
         let settings = {
             type,
             displayName,
+            topLevel: displayName === null,
             representedObject,
             treeElementConstructor,
         };
@@ -227,6 +228,15 @@ WebInspector.FolderizedTreeElement = class FolderizedTreeElement extends WebInsp
 
     _compareTreeElementsByMainTitle(a, b)
     {
+        // Folders before anything.
+        let aIsFolder = a instanceof WebInspector.FolderTreeElement;
+        let bIsFolder = b instanceof WebInspector.FolderTreeElement;
+        if (aIsFolder && !bIsFolder)
+            return -1;
+        if (bIsFolder && !aIsFolder)
+            return 1;
+
+        // Then sort by title.
         return a.mainTitle.localeCompare(b.mainTitle);
     }
 
@@ -290,6 +300,9 @@ WebInspector.FolderizedTreeElement = class FolderizedTreeElement extends WebInsp
             return this;
         }
 
+        if (settings.topLevel)
+            return this;
+
         var folder = this._folderTypeMap.get(settings.type);
         if (folder)
             return folder;
index d3c51ba..58a72f7 100644 (file)
@@ -122,6 +122,7 @@ WebInspector.OpenResourceDialog = class OpenResourceDialog extends WebInspector.
     {
         WebInspector.Frame.removeEventListener(WebInspector.Frame.Event.MainResourceDidChange, this._mainResourceDidChange, this);
         WebInspector.Frame.removeEventListener(WebInspector.Frame.Event.ResourceWasAdded, this._resourceWasAdded, this);
+        WebInspector.Target.removeEventListener(WebInspector.Target.Event.ResourceAdded, this._resourceWasAdded, this);
         WebInspector.debuggerManager.removeEventListener(WebInspector.DebuggerManager.Event.ScriptAdded, this._scriptAdded, this);
 
         this._queryController.reset();
@@ -131,6 +132,7 @@ WebInspector.OpenResourceDialog = class OpenResourceDialog extends WebInspector.
     {
         WebInspector.Frame.addEventListener(WebInspector.Frame.Event.MainResourceDidChange, this._mainResourceDidChange, this);
         WebInspector.Frame.addEventListener(WebInspector.Frame.Event.ResourceWasAdded, this._resourceWasAdded, this);
+        WebInspector.Target.addEventListener(WebInspector.Target.Event.ResourceAdded, this._resourceWasAdded, this);
         WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.ScriptAdded, this._scriptAdded, this);
 
         if (WebInspector.frameResourceManager.mainFrame)
@@ -138,7 +140,7 @@ WebInspector.OpenResourceDialog = class OpenResourceDialog extends WebInspector.
 
         for (let target of WebInspector.targets) {
             if (target !== WebInspector.mainTarget)
-                this._addScriptsForTarget(target);
+                this._addResourcesForTarget(target);
         }
 
         this._updateFilter();
@@ -285,12 +287,19 @@ WebInspector.OpenResourceDialog = class OpenResourceDialog extends WebInspector.
         }
     }
 
-    _addScriptsForTarget(target)
+    _addResourcesForTarget(target)
     {
         const suppressFilterUpdate = true;
 
+        this._addResource(target.mainResource);
+
+        for (let resource of target.resourceCollection.items)
+            this._addResource(resource, suppressFilterUpdate);
+
         let targetData = WebInspector.debuggerManager.dataForTarget(target);
         for (let script of targetData.scripts) {
+            if (script.resource)
+                continue;
             if (isWebKitInternalScript(script.sourceURL) || isWebInspectorConsoleEvaluationScript(script.sourceURL))
                 continue;
             this._addResource(script, suppressFilterUpdate);
@@ -313,6 +322,9 @@ WebInspector.OpenResourceDialog = class OpenResourceDialog extends WebInspector.
     _scriptAdded(event)
     {
         let script = event.data.script;
+        if (script.resource)
+            return;
+
         if (script.target === WebInspector.mainTarget)
             return;
 
index 59af495..2d4e1b0 100644 (file)
@@ -121,14 +121,21 @@ WebInspector.ResourceDetailsSidebarPanel = class ResourceDetailsSidebarPanel ext
         var resourceToInspect = null;
 
         // Iterate over the objects to find a WebInspector.Resource to inspect.
-        for (var i = 0; i < objects.length; ++i) {
-            if (objects[i] instanceof WebInspector.Resource) {
-                resourceToInspect = objects[i];
+        for (let object of objects) {
+            if (object instanceof WebInspector.Resource) {
+                resourceToInspect = object;
                 break;
             }
 
-            if (objects[i] instanceof WebInspector.Frame) {
-                resourceToInspect = objects[i].mainResource;
+            if (object instanceof WebInspector.Frame) {
+                resourceToInspect = object.mainResource;
+                break;
+            }
+
+            // FIXME: <https://webkit.org/b/164427> Web Inspector: WorkerTarget's mainResource should be a Resource not a Script
+            // If that was the case, then we could just have WorkerTreeElement contain the Resource and not a Script.
+            if (object instanceof WebInspector.Script && object.isMainResource() && object.resource) {
+                resourceToInspect = object.resource;
                 break;
             }
         }
index daf9a17..b6c3900 100644 (file)
@@ -37,7 +37,6 @@ WebInspector.ResourceSidebarPanel = class ResourceSidebarPanel extends WebInspec
         this.addSubview(this._navigationBar);
 
         this._targetTreeElementMap = new Map;
-        this._deferredTargetScripts = [];
 
         var scopeItemPrefix = "resource-sidebar-";
         var scopeBarItems = [];
@@ -287,16 +286,17 @@ WebInspector.ResourceSidebarPanel = class ResourceSidebarPanel extends WebInspec
         if (!script.url && !script.sourceURL)
             return;
 
-        // If the script URL matches a resource we can assume it is part of that resource and does not need added.
-        if (script.resource || script.dynamicallyAddedScriptElement)
-            return;
-
         // Worker script.
         if (script.target !== WebInspector.mainTarget) {
-            this._addScriptForNonMainTarget(script);
+            if (script.isMainResource())
+                this._addTargetWithMainResource(script.target)
             return;
         }
 
+        // If the script URL matches a resource we can assume it is part of that resource and does not need added.
+        if (script.resource || script.dynamicallyAddedScriptElement)
+            return;
+
         let insertIntoTopLevel = false;
         let parentFolderTreeElement = null;
 
@@ -380,7 +380,6 @@ WebInspector.ResourceSidebarPanel = class ResourceSidebarPanel extends WebInspec
             this._anonymousScriptsFolderTreeElement = null;
         }
 
-        this._deferredTargetScripts = [];
         if (this._targetTreeElementMap.size) {
             for (let treeElement of this._targetTreeElementMap)
                 treeElement.parent.removeChild(treeElement, suppressOnDeselect, suppressSelectSibling);
@@ -388,42 +387,15 @@ WebInspector.ResourceSidebarPanel = class ResourceSidebarPanel extends WebInspec
         }
     }
 
-    _addScriptForNonMainTarget(script)
-    {
-        let targetTreeElement = this._targetTreeElementMap.get(script.target);
-        if (!targetTreeElement) {
-            // Defer adding this script until we have the main resource for the Target.
-            // This can happen when opening the inspector after a page has already loaded,
-            // in those cases the scriptDidParse events are in random order.
-            if (script.isMainResource())
-                this._addTargetWithMainResource(script.target);
-            else
-                this._deferredTargetScripts.push(script);
-            return;
-        }
-
-        let scriptTreeElement = new WebInspector.ScriptTreeElement(script);
-        let index = insertionIndexForObjectInListSortedByFunction(scriptTreeElement, targetTreeElement.children, this._compareTreeElements);
-        targetTreeElement.insertChild(scriptTreeElement, index);
-    }
-
     _addTargetWithMainResource(target)
     {
         console.assert(target.type === WebInspector.Target.Type.Worker);
 
-        let targetTreeElement = new WebInspector.TargetTreeElement(target);
+        let targetTreeElement = new WebInspector.WorkerTreeElement(target);
         this._targetTreeElementMap.set(target, targetTreeElement);
 
         let index = insertionIndexForObjectInListSortedByFunction(targetTreeElement, this.contentTreeOutline.children, this._compareTreeElements);
         this.contentTreeOutline.insertChild(targetTreeElement, index);
-
-        let [deferredScriptsForThisTarget, deferredScriptsForAnotherTarget] = this._deferredTargetScripts.partition((script) => script.target === target);
-        this._deferredTargetScripts = deferredScriptsForAnotherTarget;
-        for (let script of deferredScriptsForThisTarget) {
-            let scriptTreeElement = new WebInspector.ScriptTreeElement(script);
-            let index = insertionIndexForObjectInListSortedByFunction(scriptTreeElement, targetTreeElement.children, this._compareTreeElements);
-            targetTreeElement.insertChild(scriptTreeElement, index);
-        }
     }
 
     _targetRemoved(event)
@@ -433,8 +405,6 @@ WebInspector.ResourceSidebarPanel = class ResourceSidebarPanel extends WebInspec
         let targetTreeElement = this._targetTreeElementMap.take(removedTarget);
         if (targetTreeElement)
             targetTreeElement.parent.removeChild(targetTreeElement);
-
-        this._deferredTargetScripts = this._deferredTargetScripts.filter((script) => script.target !== removedTarget);
     }
 
     _treeSelectionDidChange(event)
index 2e040df..ea3668c 100644 (file)
@@ -149,7 +149,7 @@ WebInspector.ResourceTimelineDataGridNode = class ResourceTimelineDataGridNode e
 
     appendContextMenuItems(contextMenu)
     {
-        WebInspector.appendContextMenuItemsForResource(contextMenu, this._resource);
+        WebInspector.appendContextMenuItemsForSourceCode(contextMenu, this._resource);
     }
 
     // Protected
index 89c63da..bfd2d69 100644 (file)
@@ -136,13 +136,16 @@ WebInspector.ResourceTreeElement = class ResourceTreeElement extends WebInspecto
     _updateTitles()
     {
         var frame = this._resource.parentFrame;
+        var target = this._resource.target;
+
         var isMainResource = this._resource.isMainResource();
+        var parentResourceHost = target.mainResource ? target.mainResource.urlComponents.host : null;
         if (isMainResource && frame) {
             // When the resource is a main resource, get the host from the current frame's parent frame instead of the current frame.
-            var parentResourceHost = frame.parentFrame ? frame.parentFrame.mainResource.urlComponents.host : null;
+            parentResourceHost = frame.parentFrame ? frame.parentFrame.mainResource.urlComponents.host : null;
         } else if (frame) {
             // When the resource is a normal sub-resource, get the host from the current frame's main resource.
-            var parentResourceHost = frame.mainResource.urlComponents.host;
+            parentResourceHost = frame.mainResource.urlComponents.host;
         }
 
         var urlComponents = this._resource.urlComponents;
@@ -151,7 +154,7 @@ WebInspector.ResourceTreeElement = class ResourceTreeElement extends WebInspecto
         this.mainTitle = WebInspector.displayNameForURL(this._resource.url, urlComponents);
 
         // Show the host as the subtitle if it is different from the main resource or if this is the main frame's main resource.
-        var subtitle = parentResourceHost !== urlComponents.host || frame.isMainFrame() && isMainResource ? WebInspector.displayNameForHost(urlComponents.host) : null;
+        var subtitle = parentResourceHost !== urlComponents.host || frame && frame.isMainFrame() && isMainResource ? WebInspector.displayNameForHost(urlComponents.host) : null;
         this.subtitle = this.mainTitle !== subtitle ? subtitle : null;
 
         if (oldMainTitle !== this.mainTitle)
@@ -162,7 +165,7 @@ WebInspector.ResourceTreeElement = class ResourceTreeElement extends WebInspecto
     {
         let contextMenu = WebInspector.ContextMenu.createFromEvent(event);
 
-        WebInspector.appendContextMenuItemsForResource(contextMenu, this._resource);
+        WebInspector.appendContextMenuItemsForSourceCode(contextMenu, this._resource);
     }
 
     // Private
index 95e657d..d02bcf8 100644 (file)
@@ -173,12 +173,16 @@ WebInspector.SourceCodeTreeElement = class SourceCodeTreeElement extends WebInsp
 
         console.assert(this.hasChildren);
 
-        var wasSelected = childTreeElement.selected;
+        let parentTreeElement = childTreeElement.parent;
+
+        let wasParentExpanded = parentTreeElement.expanded;
+        let wasSelected = childTreeElement.selected;
 
-        var parentTreeElement = childTreeElement.parent;
         parentTreeElement.removeChild(childTreeElement, true, true);
         parentTreeElement.insertChild(childTreeElement, insertionIndexForObjectInListSortedByFunction(childTreeElement, parentTreeElement.children, WebInspector.ResourceTreeElement.compareFolderAndResourceTreeElements));
 
+        if (wasParentExpanded)
+            parentTreeElement.expand();
         if (wasSelected)
             childTreeElement.revealAndSelect(true, false, true, true);
     }
diff --git a/Source/WebInspectorUI/UserInterface/Views/TargetTreeElement.js b/Source/WebInspectorUI/UserInterface/Views/TargetTreeElement.js
deleted file mode 100644 (file)
index 38280fb..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2016 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
- * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
- * THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-WebInspector.TargetTreeElement = class TargetTreeElement extends WebInspector.ScriptTreeElement
-{
-    constructor(target)
-    {
-        super(target.mainResource);
-
-        console.assert(target instanceof WebInspector.Target);
-        console.assert(target.type === WebInspector.Target.Type.Worker);
-        console.assert(target.mainResource instanceof WebInspector.Script);
-
-        this._target = target;
-
-        this._expandedSetting = new WebInspector.Setting("target-expanded-" + this.target.name.hash, true);
-
-        if (this._expandedSetting.value)
-            this.expand();
-    }
-
-    // Public
-
-    get target() { return this._target; }
-
-    // Protected (TreeElement)
-
-    onexpand()
-    {
-        this._expandedSetting.value = true;
-    }
-
-    oncollapse()
-    {
-        if (this.hasChildren)
-            this._expandedSetting.value = false;
-    }
-};
diff --git a/Source/WebInspectorUI/UserInterface/Views/WorkerTreeElement.js b/Source/WebInspectorUI/UserInterface/Views/WorkerTreeElement.js
new file mode 100644 (file)
index 0000000..2cca419
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// FIXME: <https://webkit.org/b/164427> Web Inspector: WorkerTarget's mainResource should be a Resource not a Script
+// When we are guaranteed a Resource and not a Script we can extend ResourceTreeElement.
+
+WebInspector.WorkerTreeElement = class WorkerTreeElement extends WebInspector.ScriptTreeElement
+{
+    constructor(target)
+    {
+        super(target.mainResource);
+
+        console.assert(target instanceof WebInspector.Target);
+        console.assert(target.type === WebInspector.Target.Type.Worker);
+        console.assert(target.mainResource instanceof WebInspector.Script);
+
+        this._target = target;
+        this._target.addEventListener(WebInspector.Target.Event.ResourceAdded, this._resourceAdded, this);
+        this._target.addEventListener(WebInspector.Target.Event.ScriptAdded, this._scriptAdded, this);
+
+        this._expandedSetting = new WebInspector.Setting("worker-expanded-" + this._target.name.hash, true);
+
+        // Scripts are top level.
+        this.registerFolderizeSettings("scripts", null, this._target.resourceCollection.resourceCollectionForType(WebInspector.Resource.Type.Script), WebInspector.ResourceTreeElement);
+        this.registerFolderizeSettings("extra-scripts", null, this._target.extraScriptCollection, WebInspector.ScriptTreeElement);
+
+        // All other resources may be folderized.
+        for (let [key, value] of Object.entries(WebInspector.Resource.Type)) {
+            if (value === WebInspector.Resource.Type.Script)
+                continue;
+            let folderName = WebInspector.Resource.displayNameForType(value, true);
+            this.registerFolderizeSettings(key, folderName, this._target.resourceCollection.resourceCollectionForType(value), WebInspector.ResourceTreeElement);
+        }
+
+        this.updateParentStatus();
+
+        if (this._expandedSetting.value)
+            this.expand();
+    }
+
+    // Public
+
+    get target() { return this._target; }
+
+    // Protected (TreeElement)
+
+    onexpand()
+    {
+        this._expandedSetting.value = true;
+    }
+
+    oncollapse()
+    {
+        if (this.hasChildren)
+            this._expandedSetting.value = false;
+    }
+
+    onpopulate()
+    {
+        if (this.children.length && !this.shouldRefreshChildren)
+            return;
+
+        this.shouldRefreshChildren = false;
+
+        this.removeChildren();
+        this.prepareToPopulate();
+
+        for (let resource of this._target.resourceCollection.items)
+            this.addChildForRepresentedObject(resource);
+
+        for (let script of this._target.extraScriptCollection.items)
+            this.addChildForRepresentedObject(script);
+
+        let sourceMaps = this._target.mainResource.sourceMaps;
+        for (let sourceMap of sourceMaps) {
+            for (let resource of sourceMap.resources)
+                this.addChildForRepresentedObject(resource);
+        }
+    }
+
+    // Overrides from SourceCodeTreeElement.
+
+    updateSourceMapResources()
+    {
+        // Handle our own SourceMapResources.
+
+        if (!this.treeOutline || !this.treeOutline.includeSourceMapResourceChildren)
+            return;
+
+        this.updateParentStatus();
+
+        if (this._target.mainResource.sourceMaps.length) {
+            this.hasChildren = true;
+            this.shouldRefreshChildren = true;
+        }
+    }
+
+    onattach()
+    {
+        // Handle our own SourceMapResources. Skip immediate superclasses.
+
+        WebInspector.GeneralTreeElement.prototype.onattach.call(this);
+
+        this.element.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this));
+    }
+
+    // Overrides from FolderizedTreeElement
+
+    compareChildTreeElements(a, b)
+    {
+        let aIsResource = a instanceof WebInspector.ResourceTreeElement;
+        let bIsResource = b instanceof WebInspector.ResourceTreeElement;
+
+        if (aIsResource && bIsResource)
+            return WebInspector.ResourceTreeElement.compareResourceTreeElements(a, b);
+
+        if (!aIsResource && !bIsResource)
+            return super.compareChildTreeElements(a, b);
+
+        return aIsResource ? 1 : -1;
+    }
+
+    // Private
+
+    _handleContextMenuEvent(event)
+    {
+        let contextMenu = WebInspector.ContextMenu.createFromEvent(event);
+
+        // FIXME: <https://webkit.org/b/164427> Web Inspector: WorkerTarget's mainResource should be a Resource not a Script
+        WebInspector.appendContextMenuItemsForSourceCode(contextMenu, this.script.resource ? this.script.resource : this.script);
+    }
+
+    _scriptAdded(event)
+    {
+        let script = event.data.script;
+        if (!script.url && !script.sourceURL)
+            return;
+
+        this.addRepresentedObjectToNewChildQueue(script);
+    }
+
+    _resourceAdded(event)
+    {
+        this.addRepresentedObjectToNewChildQueue(event.data.resource);
+    }
+};