Title: [236927] trunk
Revision
236927
Author
[email protected]
Date
2018-10-08 11:25:52 -0700 (Mon, 08 Oct 2018)

Log Message

Web Inspector: group media network entries by the node that triggered the request
https://bugs.webkit.org/show_bug.cgi?id=189606
<rdar://problem/44438527>

Reviewed by Brian Burg.

Source/_javascript_Core:

* inspector/protocol/Network.json:
Add an optional `nodeId` field to the `Initiator` object that is set it is possible to
determine which ancestor node triggered the load. It may not correspond directly to the node
with the href/src, as that url may only be used by an ancestor for loading.

Source/WebCore:

Test: http/tests/inspector/network/resource-initiatorNode.html

Add extra arguments to functions that create `ResourceRequest` objects for media resources so
that `initiatorNodeIdentifier` can be set for WebInspector frontend to use for grouping.

* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::loadResource):
* html/HTMLVideoElement.cpp:
(WebCore::HTMLVideoElement::setDisplayMode):
* loader/FrameLoader.h:
* loader/FrameLoader.cpp:
(WebCore::FrameLoader::willLoadMediaElementURL):
Handles initial (e.g. DNT) resource requests.

* loader/ImageLoader.cpp:
(ImageLoader::updateFromElement):
Handles "poster" requests.

* loader/MediaResourceLoader.cpp:
(MediaResourceLoader::requestResource):
Handles byte-range requests.

* html/track/LoadableTextTrack.cpp:
(WebCore::LoadableTextTrack::loadTimerFired):
* loader/TextTrackLoader.h:
* loader/TextTrackLoader.cpp:
(WebCore::TextTrackLoader::load):
* html/HTMLTrackElement.h:
Handles <track> (e.g. subtitle) requests.

* inspector/agents/InspectorDOMAgent.cpp:
(WebCore::InspectorDOMAgent::identifierForNode):
* inspector/InspectorInstrumentation.h:
(WebCore::InspectorInstrumentation::identifierForNode):
* inspector/InspectorInstrumentation.cpp:
(WebCore::InspectorInstrumentation::identifierForNodeImpl):
Allows callers to get a `DOM.nodeId` for the given `Node`, which is (in this patch) attached
to the `ResourceRequest` and later used by `InspectorNetworkAgent`.

* inspector/agents/InspectorNetworkAgent.h:
* inspector/agents/InspectorNetworkAgent.cpp:
(WebCore::InspectorNetworkAgent::willSendRequest):
(WebCore::InspectorNetworkAgent::didLoadResourceFromMemoryCache):
(WebCore::InspectorNetworkAgent::buildInitiatorObject):

* platform/network/ResourceRequestBase.h:
(WebCore::ResourceRequestBase::initiatorNodeIdentifier const):
(WebCore::ResourceRequestBase::setInitiatorNodeIdentifier):
* platform/network/ResourceRequestBase.cpp:
(WebCore::ResourceRequestBase::setAsIsolatedCopy):
* platform/network/cf/ResourceRequestCFNet.cpp:
(WebCore::ResourceRequest::updateFromDelegatePreservingOldProperties):
* loader/cache/CachedResourceLoader.cpp:
(WebCore::CachedResourceLoader::shouldContinueAfterNotifyingLoadedFromMemoryCache):

* inspector/InspectorCanvas.h:
* inspector/InspectorCanvas.cpp:
(WebCore::InspectorCanvas::buildObjectForCanvas):
* inspector/agents/InspectorCanvasAgent.cpp:
(WebCore::InspectorCanvasAgent::enable):
(WebCore::InspectorCanvasAgent::didCreateCanvasRenderingContext):
Don't try to push the canvas' node to the frontend, as this will create a dangling node in
`InspectorDOMAgent` if the canvas' node is detached from the DOM.

Source/WebInspectorUI:

Introduces a `WI.NavigationItem` for changing whether network entries are grouped by the
node that initiated the load (if applicable). When grouped by node, a tree-like layout of
the table cells (including expand/collapse) is used for resources that share the same
initiator node. The values for the node's cell are based on it's initated resources.

* Localizations/en.lproj/localizedStrings.js:
* UserInterface/Base/Setting.js:

* UserInterface/Controllers/DOMManager.js:
(WI.DOMManager):
(WI.DOMManager.prototype._mainResourceDidChange): Added.
Whenever the frame navigates, re-request the document so that `NetworkAgent` is able to send
valid `nodeId` for each request's `initiatorNode`. This means that the document should
always be available.

* UserInterface/Views/NetworkTableContentView.js:
(WI.NetworkTableContentView):
(WI.NetworkTableContentView.prototype.get filterNavigationItems):
(WI.NetworkTableContentView.prototype.closed):
(WI.NetworkTableContentView.prototype.reset):
(WI.NetworkTableContentView.prototype.tableSortChanged):
(WI.NetworkTableContentView.prototype.tableSelectedRowChanged):
(WI.NetworkTableContentView.prototype.tablePopulateCell):
(WI.NetworkTableContentView.prototype._populateNameCell.createIconElement): Added.
(WI.NetworkTableContentView.prototype._populateNameCell):
(WI.NetworkTableContentView.prototype._populateDomainCell.createIconAndText): Added.
(WI.NetworkTableContentView.prototype._populateDomainCell):
(WI.NetworkTableContentView.prototype._populateInitiatorCell):
(WI.NetworkTableContentView.prototype._populateTransferSizeCell):
(WI.NetworkTableContentView.prototype._generateSortComparator):
(WI.NetworkTableContentView.prototype._processPendingEntries):
(WI.NetworkTableContentView.prototype._updateEntryForResource.updateExistingEntry): Added.
(WI.NetworkTableContentView.prototype._updateEntryForResource):
(WI.NetworkTableContentView.prototype._insertResourceAndReloadTable):
(WI.NetworkTableContentView.prototype._entryForDOMNode): Added.
(WI.NetworkTableContentView.prototype._tryLinkResourceToDOMNode): Added.
(WI.NetworkTableContentView.prototype._uniqueValuesForDOMNodeEntry): Added.
(WI.NetworkTableContentView.prototype._updateFilteredEntries):
(WI.NetworkTableContentView.prototype._handleGroupByDOMNodeCheckedDidChange): Added.
* UserInterface/Views/NetworkTableContentView.css:
(.network-table .cell.dom-node.name .icon): Added.
(.network-table .cell.dom-node.name .disclosure): Added.
(body[dir=rtl] .network-table .cell.dom-node.name .disclosure): Added.
(.network-table:focus li.selected .cell.dom-node.name .disclosure): Added.
(.network-table .cell.dom-node.name .disclosure.expanded): Added.
(.network-table:focus li.selected .cell.node.name .disclosure.expanded): Added.
(.network-table .cell.grouped-by-node.name): Added.
(body[dir=ltr] .network-table .cell.grouped-by-node.name): Added.
(body[dir=rtl] .network-table .cell.grouped-by-node.name): Added.
(.network-table li:not(.selected) .cell:matches(.cache-type, .multiple)): Added.
(.network-table li.selected .cell.domain > .lock): Added.
(.network-table .cache-type): Deleted.
When two resources are added that share the same `initiatorNode`, insert a node entry into
the `WI.Table` before the first resource entry for that node (based on the current sort).
This node entry is added after the resource entries are filtered, so they won't appear in
the default entries list.

* UserInterface/Models/Resource.js:
(WI.Resource):
(WI.Resource.prototype.initiatorNode): Added.
(WI.Resource.prototype.requestedByteRange): Added.
* UserInterface/Controllers/NetworkManager.js:
(WI.NetworkManager.prototype.resourceRequestWillBeSent):
(WI.NetworkManager.prototype.resourceRequestWasServedFromMemoryCache):
(WI.NetworkManager.prototype._initiatorNodeFromPayload): Added.

* UserInterface/Images/Range.svg: Added.
* UserInterface/Views/ResourceIcons.css:
(.resource-icon.resource-type-range .icon): Added.

LayoutTests:

* http/tests/inspector/network/resource-initiatorNode-expected.txt: Added.
* http/tests/inspector/network/resource-initiatorNode.html: Added.

* inspector/canvas/requestNode.html:
Test case no longer needed since the document is always requested once it's available.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (236926 => 236927)


--- trunk/LayoutTests/ChangeLog	2018-10-08 17:51:20 UTC (rev 236926)
+++ trunk/LayoutTests/ChangeLog	2018-10-08 18:25:52 UTC (rev 236927)
@@ -1,3 +1,17 @@
+2018-10-08  Devin Rousso  <[email protected]>
+
+        Web Inspector: group media network entries by the node that triggered the request
+        https://bugs.webkit.org/show_bug.cgi?id=189606
+        <rdar://problem/44438527>
+
+        Reviewed by Brian Burg.
+
+        * http/tests/inspector/network/resource-initiatorNode-expected.txt: Added.
+        * http/tests/inspector/network/resource-initiatorNode.html: Added.
+
+        * inspector/canvas/requestNode.html:
+        Test case no longer needed since the document is always requested once it's available.
+
 2018-10-08  Andy Estes  <[email protected]>
 
         [Payment Request] Requests should be aborted after details settle when the user cancels

Added: trunk/LayoutTests/http/tests/inspector/network/resource-initiatorNode-expected.txt (0 => 236927)


--- trunk/LayoutTests/http/tests/inspector/network/resource-initiatorNode-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/http/tests/inspector/network/resource-initiatorNode-expected.txt	2018-10-08 18:25:52 UTC (rev 236927)
@@ -0,0 +1,17 @@
+Tests that Request initiatorNode is set correctly.
+
+
+
+== Running test suite: WI.Resource.initiatorNode
+-- Running test case: WI.Resource.initiatorNode.posterPNG
+PASS: Resource should have an initiatorNode
+PASS: Resource initiatorNode should match video node id
+
+-- Running test case: WI.Resource.initiatorNode.sourceMP4
+PASS: Resource should have an initiatorNode
+PASS: Resource initiatorNode should match video node id
+
+-- Running test case: WI.Resource.initiatorNode.trackVTT
+PASS: Resource should have an initiatorNode
+PASS: Resource initiatorNode should match video node id
+

Added: trunk/LayoutTests/http/tests/inspector/network/resource-initiatorNode.html (0 => 236927)


--- trunk/LayoutTests/http/tests/inspector/network/resource-initiatorNode.html	                        (rev 0)
+++ trunk/LayoutTests/http/tests/inspector/network/resource-initiatorNode.html	2018-10-08 18:25:52 UTC (rev 236927)
@@ -0,0 +1,137 @@
+<!doctype html>
+<html>
+<head>
+<script src=""
+<script>
+
+function replay() {
+    let videoElement = document.getElementById("video");
+    videoElement.pause();
+    videoElement.currentTime = 0;
+    videoElement.load();
+}
+
+function loadPoster(url) {
+    document.getElementById("video").poster = url;
+
+    replay();
+}
+
+function loadSource(url, type) {
+    let sourceElement = document.createElement("source");
+    sourceElement.type = type;
+    sourceElement.src = ""
+
+    document.getElementById("video").appendChild(sourceElement);
+
+    replay();
+}
+
+function loadTrack(url, kind) {
+    let trackElement = document.createElement("track");
+    trackElement.kind = kind;
+    trackElement.default = true;
+    trackElement.src = ""
+
+    trackElement.track.mode = "hidden";
+
+    document.getElementById("video").appendChild(trackElement);
+
+    replay();
+}
+
+function handleVideoEnded(event) {
+    TestPage.dispatchEventToFrontend("TestPage-video-ended");
+}
+
+function test()
+{
+    let suite = InspectorTest.createAsyncSuite("WI.Resource.initiatorNode");
+
+    let videoNode = null;
+
+    suite.addTestCase({
+        name: "WI.Resource.initiatorNode.posterPNG",
+        test(resolve, reject) {
+            let file = "white.png";
+
+            WI.Frame.awaitEvent(WI.Frame.Event.ResourceWasAdded)
+            .then((event) => {
+                let resource = event.data.resource;
+
+                InspectorTest.assert(resource.url.endsWith(file), `Resource should be "${file}"`);
+
+                InspectorTest.expectNotNull(resource.initiatorNode, "Resource should have an initiatorNode");
+                if (resource.initiatorNode)
+                    InspectorTest.expectEqual(resource.initiatorNode.id, videoNode.id, "Resource initiatorNode should match video node id");
+            })
+            .then(resolve, reject);
+
+            InspectorTest.evaluateInPage(`loadPoster("resources/${file}")`);
+        }
+    });
+
+    suite.addTestCase({
+        name: "WI.Resource.initiatorNode.sourceMP4",
+        test(resolve, reject) {
+            let file = "white.mp4";
+
+            WI.Frame.awaitEvent(WI.Frame.Event.ResourceWasAdded)
+            .then((event) => {
+                let resource = event.data.resource;
+
+                InspectorTest.assert(resource.url.endsWith(file), `Resource should be "${file}"`);
+
+                InspectorTest.expectNotNull(resource.initiatorNode, "Resource should have an initiatorNode");
+                if (resource.initiatorNode)
+                    InspectorTest.expectEqual(resource.initiatorNode.id, videoNode.id, "Resource initiatorNode should match video node id");
+            })
+            .then(resolve, reject);
+
+            InspectorTest.evaluateInPage(`loadSource("resources/${file}", "video/mp4")`);
+        }
+    });
+
+    suite.addTestCase({
+        name: "WI.Resource.initiatorNode.trackVTT",
+        test(resolve, reject) {
+            let file = "white.vtt";
+
+            // Loading a track causes a bunch of media controls to be loaded.
+            let listener = WI.Frame.addEventListener(WI.Frame.Event.ResourceWasAdded, (event) => {
+                let resource = event.data.resource;
+                if (!resource.url.endsWith(file))
+                    return;
+
+                InspectorTest.expectNotNull(resource.initiatorNode, "Resource should have an initiatorNode");
+                if (resource.initiatorNode)
+                    InspectorTest.expectEqual(resource.initiatorNode.id, videoNode.id, "Resource initiatorNode should match video node id");
+
+                WI.Frame.removeEventListener(WI.Frame.Event.ResourceWasAdded, listener);
+
+                resolve();
+            });
+
+            InspectorTest.evaluateInPage(`loadTrack("resources/${file}", "captions")`);
+        }
+    });
+
+    WI.domManager.requestDocument((documentNode) => {
+        WI.domManager.querySelector(documentNode.id, "video#video", (videoNodeId) => {
+            videoNode = WI.domManager.nodeForId(videoNodeId);
+            if (videoNode)
+                suite.runTestCasesAndFinish();
+            else {
+                InspectorTest.fail(`DOM node for "video#video" not found.`);
+                InspectorTest.completeTest();
+            }
+        });
+    });
+}
+</script>
+</head>
+<body _onload_="runTest()">
+    <p>Tests that Request initiatorNode is set correctly.</p>
+    <video id="video" muted autoplay></video>
+</body>
+</html>

Added: trunk/LayoutTests/http/tests/inspector/network/resources/white.mp4 (0 => 236927)


--- trunk/LayoutTests/http/tests/inspector/network/resources/white.mp4	                        (rev 0)
+++ trunk/LayoutTests/http/tests/inspector/network/resources/white.mp4	2018-10-08 18:25:52 UTC (rev 236927)
@@ -0,0 +1,15 @@
+������ftypmp42������mp41mp42isom������mdat������������	,������	������gd��\xAC\xD9AA\xFB������sY@��\xF1B\x99`������h\xEB\xEC\xB2,����\xD8\xFF\xFF\xD4\xDCE\xE9\xBD\xE6\xD9H\xB7\x96,\xD8 \xD9#\xEE\xEFx264 - core 120 r2151 a3f4407 - H.264/MPEG-4 AVC codec - Copyleft 2003-2011 - http://www.videolan.org/x264.html - options: cabac=1 ref=3 deblock=1:0:0 analyse=0x3:0x113 me=hex subme=7 psy=1 psy_rd=1.00:0.00 mixed_ref=1 me_range=16 chroma_me=1 trellis=1 8x8dct=1 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=-2 threads=12 sliced_threads=0 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=3 b_pyramid=2 b_adapt=1 b_bias=0 direct=1 weightb=1 open_gop=0 weightp=2 keyint=60 keyint_min=6 scenecut=40 intra_refresh=0 rc_lookahead=40 rc=cbr mbtree=1 bitrate=2048 ratetol=1.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4 vbv_maxrate=2048 vbv_bufsize=1228 nal_hrd=none ip_ratio=1.40 aq=1:1.00��\x80������?e\x88\x84��\xFF\xFE\xF7Է̲\xEDSo\xB6\xA8\xF7\xA2\x9E
 &_Cu%\x9B\x96\xC4\xED:\x80��\xBA
 \xF5\xB7\xDBo\xCD\xD1\xA4\xD0@����X\xD9\xF6\xD774\xE22=:\x81������	0������+A\x9A$lA\xFE֌\xB0��\xAA������	P������A\x9EBx\x82\xFF����\x93������	P������\x9EatA����\x92������	P������\x9EcjA����\x93������	0������A\x9AhI\xA8Ah\x99L/\xFF\xFE֌\xB0��\xAB������	P������+A\x9E\x86E,\xFF����\x93������	P������\x9E\xA5tA����\x93������	P������\x9E\xA7jA����\x92������	0������A\x9A\xACI\xA8Al\x99L/\xFF\xFE֌\xB0��\xAA������	P������+A\x9E\xCAE,\xFF����\x93������	P������\x9E\xE9tA����\x92������	P������\x9E\xEBjA����\x92������	0������A\x9A\xF0I\xA8Al\x99L/\xFF\xFE֌\xB0��\xAB������	P������+A\x9FE,\xFF����\x93������	P������\x9F-tA����\x93������	P������\x9F/jA����\x92������	0������A\x9B4I\xA8Al\x99L/\xFF\xFE֌\xB0��\xAA������	P������+A\x9FRE,\xFF����\x93������	P������\x9FqtA����\x92������	P������\x9FsjA����\x92������	0������A\x9BxI\xA8Al\x99L/\xFF\xFE֌\xB0��\xAB������	P������+A\x9F\x96E,
 \xFF����\x92������	P������\x9F\xB5tA����\x93������	P������\x9F\xB7jA����\x93������	0������A\x9B\xBCI\xA8Al\x99L/\xFF\xFE֌\xB0��\xAA������	P������+A\x9F\xDAE,\xFF����\x93������
 	P������\x9F\xF9tA����\x92������	P������\x9F\xFBjA����\x93������	0������A\x9B\xE0I\xA8Al\x99L/\xFF\xFE֌\xB0��\xAB������	P������+A\x9EE,\xFF����\x92������	P������\x9E=tA����\x92������	P������\x9E?jA����\x93������	0������A\x9A$I\xA8Al\x99L/\xFF\xFE֌\xB0��\xAA������	P������+A\x9EBE,\xFF����\x93������	P������\x9EatA����\x92������	P������\x9EcjA����\x93������	0������A\x9AhI\xA8Al\x99L/\xFF\xFE֌\xB0��\xAB������	P������+A\x9E\x86E,\xFF����\x93������	P������\x9E\xA5tA����\x93������	P������\x9E\xA7jA����\x92������	0������A\x9A\xACI\xA8Al\x99L/\xFF\xFE֌\xB0��\xAA������	P������+A\x9E\xCAE,\xFF����\x93������	P������\x9E\xE9tA����\x92������	P������\x9E\xEBjA����\x92������	0������A\x9A\xF0I\xA8Al\x99L/\xFF\xFE֌\xB0��\xAB������	P������+A\x9FE,\xFF����\x93������	P������\x9F-tA����\x93������	P������\x9F/jA����\x92������	0������A\x9B4I\xA8Al\x99L/\xFF\xFE֌\xB0��\xAA������	
 P������+A\x9FRE,\xFF����\x93������	P������\x9FqtA����\x92������	P������\x9FsjA����\x92������	0������A\x9BxI\xA8Al\x99L/\xFF\xFE֌\xB0��\xAB������	P������+A\x9F\x96E,\xFF����\x92������	
 P������\x9F\xB5tA����\x93������	P������\x9F\xB7jA����\x93������	0������A\x9B\xBBI\xA8Al\x99L/\xFF\xFE֌\xB0��\xAA������	P������A\x9F\xDAB\xFF����\x93������	P������\x9F\xF9i_\xFF����\x92������	������gd��\xAC\xD9AA\xFB������sY@��\xF1B\x99`������h\xEB\xEC\xB2,������?e\x88\x82��\xBF\xFE\xF7Է̲\xEDSo\xB6\xA8\xF7\xA2\x9E&_Cu%\x9B\x96\xC4\xED:\x80��\xBA\xF5\xB7\xDBo\xCD\xD1\xA4\xD0@����X\xD9\xF6\xD774\xE22=:\x81����\xBBmoov������lmvhd��������\xD7\xC0\xB0\xD9\xD7\xC0\xB0\xD9����\xE8����\xD3����������������������������������������������������������������������������������������@����������������������������������������������������������������Gtrak������\tkhd������\xD7\xC0\xB0\xD9\xD7\xC0\xB0\xD9������������������\xD3��������������������������������������������������������������������������������������������@������@������\xF0����������$edts������elst������������������\xD3������\xC8����������\xBFmdia������ mdhd��������\xD7\xC0
 \xB0\xD9\xD7\xC0\xB0\xD9����\xB8����\xD4U\xC4����������1hdlr����������������vide������������������������Core Media Video������fminf������vmhd����������������������������$dinf������dref��������������������url ����������&stbl������\xAAstsd��������������������\x9Aavc1����������������������������������������������@��\xF0��H������H��������������
 ������������������������������������������������������������������\xFF\xFF������0avcCd��\xFF\xE1��gd��\xAC\xD9AA\xFB������sY@��\xF1B\x99`��h\xEB\xEC\xB2,������btrt��������������������\x92������stts��������������������=������d����\xF8ctts��������������=������������\xC8����������\xF4������������\xC8��������������������������d����������\xF4������������\xC8��������������������������d����������\xF4������������\xC8��������������������������d����������\xF4������������\xC8��������������������������d����������\xF4������������\xC8��������������������������d����������\xF4������������\xC8��������������������������d����������\xF4������������\xC8��������������������������d����������\xF4������������\xC8��������������������������d����������\xF4������������\xC8��������������������������d����������\xF4������������\xC8��������������������������d����������\xF4������������\xC8��������������������������d����������\xF4������������\
 xC8��������������������������d����������\xF4������������\xC8��������������������������d����������\xF4������������\xC8��������������������������d����������\x90������������\xC8��������������������������\xC8������stss��������������������������=������(stsc������������������������������������������������������stsz����������������������=����J������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
 ������������������������������������������������������������������������������������������������������������������������������������������������������n������stco��������������������,��������\xDA
\ No newline at end of file

Added: trunk/LayoutTests/http/tests/inspector/network/resources/white.png


(Binary files differ)
Index: trunk/LayoutTests/http/tests/inspector/network/resources/white.png =================================================================== --- trunk/LayoutTests/http/tests/inspector/network/resources/white.png 2018-10-08 17:51:20 UTC (rev 236926) +++ trunk/LayoutTests/http/tests/inspector/network/resources/white.png 2018-10-08 18:25:52 UTC (rev 236927) Property changes on: trunk/LayoutTests/http/tests/inspector/network/resources/white.png ___________________________________________________________________

Added: svn:mime-type

+image/png \ No newline at end of property

Added: trunk/LayoutTests/http/tests/inspector/network/resources/white.vtt (0 => 236927)


--- trunk/LayoutTests/http/tests/inspector/network/resources/white.vtt	                        (rev 0)
+++ trunk/LayoutTests/http/tests/inspector/network/resources/white.vtt	2018-10-08 18:25:52 UTC (rev 236927)
@@ -0,0 +1,7 @@
+WEBVTT
+
+00:00:00.000 --> 00:00:00.999
+0.000 to 0.999
+
+00:00:01.000 --> 00:00:01.999
+1.000 to 1.999

Modified: trunk/LayoutTests/inspector/canvas/requestNode-expected.txt (236926 => 236927)


--- trunk/LayoutTests/inspector/canvas/requestNode-expected.txt	2018-10-08 17:51:20 UTC (rev 236926)
+++ trunk/LayoutTests/inspector/canvas/requestNode-expected.txt	2018-10-08 18:25:52 UTC (rev 236927)
@@ -2,11 +2,6 @@
 
 
 == Running test suite: Canvas.requestNode
--- Running test case: Canvas.requestNode.missingDocument
-PASS: The page should have three canvases.
-PASS: Should produce an error.
-Error: Document has not been requested
-
 -- Running test case: Canvas.requestNode.validCanvasId
 PASS: Canvas "CSS canvas “css”" has node with valid id.
 PASS: Canvas "CSS canvas “css”" has node with type "CANVAS".

Modified: trunk/LayoutTests/inspector/canvas/requestNode.html (236926 => 236927)


--- trunk/LayoutTests/inspector/canvas/requestNode.html	2018-10-08 17:51:20 UTC (rev 236926)
+++ trunk/LayoutTests/inspector/canvas/requestNode.html	2018-10-08 18:25:52 UTC (rev 236927)
@@ -15,25 +15,6 @@
     let suite = InspectorTest.createAsyncSuite("Canvas.requestNode");
 
     suite.addTestCase({
-        name: "Canvas.requestNode.missingDocument",
-        description: "Getting the canvas node requires that WebInspector knows about the document.",
-        test(resolve, reject) {
-            let canvases = WI.canvasManager.canvases;
-            InspectorTest.expectEqual(canvases.length, 3, "The page should have three canvases.");
-            if (!canvases.length) {
-                reject("Missing canvas.");
-                return;
-            }
-
-            CanvasAgent.requestNode(canvases[0].identifier, (error) => {
-                InspectorTest.expectThat(error, "Should produce an error.");
-                InspectorTest.log("Error: " + error);
-                resolve();
-            });
-        }
-    });
-
-    suite.addTestCase({
         name: "Canvas.requestNode.validCanvasId",
         description: "Get the node id for each canvas on the page.",
         test(resolve, reject) {

Modified: trunk/Source/_javascript_Core/ChangeLog (236926 => 236927)


--- trunk/Source/_javascript_Core/ChangeLog	2018-10-08 17:51:20 UTC (rev 236926)
+++ trunk/Source/_javascript_Core/ChangeLog	2018-10-08 18:25:52 UTC (rev 236927)
@@ -1,3 +1,16 @@
+2018-10-08  Devin Rousso  <[email protected]>
+
+        Web Inspector: group media network entries by the node that triggered the request
+        https://bugs.webkit.org/show_bug.cgi?id=189606
+        <rdar://problem/44438527>
+
+        Reviewed by Brian Burg.
+
+        * inspector/protocol/Network.json:
+        Add an optional `nodeId` field to the `Initiator` object that is set it is possible to
+        determine which ancestor node triggered the load. It may not correspond directly to the node
+        with the href/src, as that url may only be used by an ancestor for loading.
+
 2018-10-07  Yusuke Suzuki  <[email protected]>
 
         [JSC][Linux] Use non-truncated name for JIT workers in Linux

Modified: trunk/Source/_javascript_Core/inspector/protocol/Network.json (236926 => 236927)


--- trunk/Source/_javascript_Core/inspector/protocol/Network.json	2018-10-08 17:51:20 UTC (rev 236926)
+++ trunk/Source/_javascript_Core/inspector/protocol/Network.json	2018-10-08 18:25:52 UTC (rev 236927)
@@ -142,7 +142,8 @@
                 { "name": "type", "type": "string", "enum": ["parser", "script", "other"], "description": "Type of this initiator." },
                 { "name": "stackTrace", "type": "array", "items": { "$ref": "Console.CallFrame" }, "optional": true, "description": "Initiator _javascript_ stack trace, set for Script only." },
                 { "name": "url", "type": "string", "optional": true, "description": "Initiator URL, set for Parser type only." },
-                { "name": "lineNumber", "type": "number", "optional": true, "description": "Initiator line number, set for Parser type only." }
+                { "name": "lineNumber", "type": "number", "optional": true, "description": "Initiator line number, set for Parser type only." },
+                { "name": "nodeId", "$ref": "DOM.NodeId", "optional": true, "description": "Set if the load was triggered by a DOM node, in addition to the other initiator information." }
             ]
         }
     ],

Modified: trunk/Source/WebCore/ChangeLog (236926 => 236927)


--- trunk/Source/WebCore/ChangeLog	2018-10-08 17:51:20 UTC (rev 236926)
+++ trunk/Source/WebCore/ChangeLog	2018-10-08 18:25:52 UTC (rev 236927)
@@ -1,3 +1,75 @@
+2018-10-08  Devin Rousso  <[email protected]>
+
+        Web Inspector: group media network entries by the node that triggered the request
+        https://bugs.webkit.org/show_bug.cgi?id=189606
+        <rdar://problem/44438527>
+
+        Reviewed by Brian Burg.
+
+        Test: http/tests/inspector/network/resource-initiatorNode.html
+
+        Add extra arguments to functions that create `ResourceRequest` objects for media resources so
+        that `initiatorNodeIdentifier` can be set for WebInspector frontend to use for grouping.
+
+        * html/HTMLMediaElement.cpp:
+        (WebCore::HTMLMediaElement::loadResource):
+        * html/HTMLVideoElement.cpp:
+        (WebCore::HTMLVideoElement::setDisplayMode):
+        * loader/FrameLoader.h:
+        * loader/FrameLoader.cpp:
+        (WebCore::FrameLoader::willLoadMediaElementURL):
+        Handles initial (e.g. DNT) resource requests.
+
+        * loader/ImageLoader.cpp:
+        (ImageLoader::updateFromElement):
+        Handles "poster" requests.
+
+        * loader/MediaResourceLoader.cpp:
+        (MediaResourceLoader::requestResource):
+        Handles byte-range requests.
+
+        * html/track/LoadableTextTrack.cpp:
+        (WebCore::LoadableTextTrack::loadTimerFired):
+        * loader/TextTrackLoader.h:
+        * loader/TextTrackLoader.cpp:
+        (WebCore::TextTrackLoader::load):
+        * html/HTMLTrackElement.h:
+        Handles <track> (e.g. subtitle) requests.
+
+        * inspector/agents/InspectorDOMAgent.cpp:
+        (WebCore::InspectorDOMAgent::identifierForNode):
+        * inspector/InspectorInstrumentation.h:
+        (WebCore::InspectorInstrumentation::identifierForNode):
+        * inspector/InspectorInstrumentation.cpp:
+        (WebCore::InspectorInstrumentation::identifierForNodeImpl):
+        Allows callers to get a `DOM.nodeId` for the given `Node`, which is (in this patch) attached
+        to the `ResourceRequest` and later used by `InspectorNetworkAgent`.
+
+        * inspector/agents/InspectorNetworkAgent.h:
+        * inspector/agents/InspectorNetworkAgent.cpp:
+        (WebCore::InspectorNetworkAgent::willSendRequest):
+        (WebCore::InspectorNetworkAgent::didLoadResourceFromMemoryCache):
+        (WebCore::InspectorNetworkAgent::buildInitiatorObject):
+
+        * platform/network/ResourceRequestBase.h:
+        (WebCore::ResourceRequestBase::initiatorNodeIdentifier const):
+        (WebCore::ResourceRequestBase::setInitiatorNodeIdentifier):
+        * platform/network/ResourceRequestBase.cpp:
+        (WebCore::ResourceRequestBase::setAsIsolatedCopy):
+        * platform/network/cf/ResourceRequestCFNet.cpp:
+        (WebCore::ResourceRequest::updateFromDelegatePreservingOldProperties):
+        * loader/cache/CachedResourceLoader.cpp:
+        (WebCore::CachedResourceLoader::shouldContinueAfterNotifyingLoadedFromMemoryCache):
+
+        * inspector/InspectorCanvas.h:
+        * inspector/InspectorCanvas.cpp:
+        (WebCore::InspectorCanvas::buildObjectForCanvas):
+        * inspector/agents/InspectorCanvasAgent.cpp:
+        (WebCore::InspectorCanvasAgent::enable):
+        (WebCore::InspectorCanvasAgent::didCreateCanvasRenderingContext):
+        Don't try to push the canvas' node to the frontend, as this will create a dangling node in
+        `InspectorDOMAgent` if the canvas' node is detached from the DOM.
+
 2018-10-08  Andy Estes  <[email protected]>
 
         [Payment Request] Requests should be aborted after details settle when the user cancels

Modified: trunk/Source/WebCore/html/HTMLMediaElement.cpp (236926 => 236927)


--- trunk/Source/WebCore/html/HTMLMediaElement.cpp	2018-10-08 17:51:20 UTC (rev 236926)
+++ trunk/Source/WebCore/html/HTMLMediaElement.cpp	2018-10-08 18:25:52 UTC (rev 236927)
@@ -1592,7 +1592,7 @@
     }
 
     URL url = ""
-    if (!url.isEmpty() && !frame->loader().willLoadMediaElementURL(url)) {
+    if (!url.isEmpty() && !frame->loader().willLoadMediaElementURL(url, *this)) {
         mediaLoadingFailed(MediaPlayer::FormatError);
         return;
     }

Modified: trunk/Source/WebCore/html/HTMLTrackElement.h (236926 => 236927)


--- trunk/Source/WebCore/html/HTMLTrackElement.h	2018-10-08 17:51:20 UTC (rev 236926)
+++ trunk/Source/WebCore/html/HTMLTrackElement.h	2018-10-08 18:25:52 UTC (rev 236927)
@@ -58,6 +58,7 @@
     enum LoadStatus { Failure, Success };
     void didCompleteLoad(LoadStatus);
 
+    RefPtr<HTMLMediaElement> mediaElement() const;
     const AtomicString& mediaElementCrossOriginAttribute() const;
 
 private:
@@ -73,8 +74,6 @@
 
     void loadTimerFired();
 
-    RefPtr<HTMLMediaElement> mediaElement() const;
-
     // TextTrackClient
     void textTrackModeChanged(TextTrack&) final;
     void textTrackKindChanged(TextTrack&) final;

Modified: trunk/Source/WebCore/html/HTMLVideoElement.cpp (236926 => 236927)


--- trunk/Source/WebCore/html/HTMLVideoElement.cpp	2018-10-08 17:51:20 UTC (rev 236926)
+++ trunk/Source/WebCore/html/HTMLVideoElement.cpp	2018-10-08 18:25:52 UTC (rev 236927)
@@ -263,7 +263,7 @@
         bool canLoad = true;
         if (!poster.isEmpty()) {
             if (RefPtr<Frame> frame = document().frame())
-                canLoad = frame->loader().willLoadMediaElementURL(poster);
+                canLoad = frame->loader().willLoadMediaElementURL(poster, *this);
         }
         if (canLoad)
             player()->setPoster(poster);

Modified: trunk/Source/WebCore/html/track/LoadableTextTrack.cpp (236926 => 236927)


--- trunk/Source/WebCore/html/track/LoadableTextTrack.cpp	2018-10-08 17:51:20 UTC (rev 236926)
+++ trunk/Source/WebCore/html/track/LoadableTextTrack.cpp	2018-10-08 18:25:52 UTC (rev 236927)
@@ -82,7 +82,7 @@
     // mode being the state of the media element's crossorigin content attribute, the origin being the
     // origin of the media element's Document, and the default origin behaviour set to fail.
     m_loader = std::make_unique<TextTrackLoader>(static_cast<TextTrackLoaderClient&>(*this), static_cast<ScriptExecutionContext*>(&m_trackElement->document()));
-    if (!m_loader->load(m_url, m_trackElement->mediaElementCrossOriginAttribute(), m_trackElement->isInUserAgentShadowTree()))
+    if (!m_loader->load(m_url, *m_trackElement))
         m_trackElement->didCompleteLoad(HTMLTrackElement::Failure);
 }
 

Modified: trunk/Source/WebCore/inspector/InspectorCanvas.cpp (236926 => 236927)


--- trunk/Source/WebCore/inspector/InspectorCanvas.cpp	2018-10-08 17:51:20 UTC (rev 236926)
+++ trunk/Source/WebCore/inspector/InspectorCanvas.cpp	2018-10-08 18:25:52 UTC (rev 236927)
@@ -44,7 +44,6 @@
 #include "ImageBuffer.h"
 #include "ImageData.h"
 #include "InspectorDOMAgent.h"
-#include "InstrumentingAgents.h"
 #include "JSCanvasDirection.h"
 #include "JSCanvasFillRule.h"
 #include "JSCanvasLineCap.h"
@@ -217,7 +216,7 @@
     return m_bufferUsed < m_bufferLimit;
 }
 
-Ref<Inspector::Protocol::Canvas::Canvas> InspectorCanvas::buildObjectForCanvas(InstrumentingAgents& instrumentingAgents, bool captureBacktrace)
+Ref<Inspector::Protocol::Canvas::Canvas> InspectorCanvas::buildObjectForCanvas(bool captureBacktrace)
 {
     Inspector::Protocol::Canvas::ContextType contextType;
     if (is<CanvasRenderingContext2D>(m_context))
@@ -250,19 +249,8 @@
         String cssCanvasName = node->document().nameForCSSCanvasElement(*node);
         if (!cssCanvasName.isEmpty())
             canvas->setCssCanvasName(cssCanvasName);
-        else {
-            InspectorDOMAgent* domAgent = instrumentingAgents.inspectorDOMAgent();
-            int nodeId = domAgent->boundNodeId(node);
-            if (!nodeId) {
-                if (int documentNodeId = domAgent->boundNodeId(&node->document())) {
-                    ErrorString ignored;
-                    nodeId = domAgent->pushNodeToFrontend(ignored, documentNodeId, node);
-                }
-            }
 
-            if (nodeId)
-                canvas->setNodeId(nodeId);
-        }
+        // FIXME: <https://webkit.org/b/178282> Web Inspector: send a DOM node with each Canvas payload and eliminate Canvas.requestNode
     }
 
     if (is<ImageBitmapRenderingContext>(m_context)) {

Modified: trunk/Source/WebCore/inspector/InspectorCanvas.h (236926 => 236927)


--- trunk/Source/WebCore/inspector/InspectorCanvas.h	2018-10-08 17:51:20 UTC (rev 236926)
+++ trunk/Source/WebCore/inspector/InspectorCanvas.h	2018-10-08 18:25:52 UTC (rev 236927)
@@ -42,7 +42,6 @@
 class HTMLVideoElement;
 class ImageBitmap;
 class ImageData;
-class InstrumentingAgents;
 
 class InspectorCanvas final : public RefCounted<InspectorCanvas> {
 public:
@@ -75,7 +74,7 @@
     bool singleFrame() const { return m_singleFrame; }
     void setSingleFrame(bool singleFrame) { m_singleFrame = singleFrame; }
 
-    Ref<Inspector::Protocol::Canvas::Canvas> buildObjectForCanvas(InstrumentingAgents&, bool captureBacktrace);
+    Ref<Inspector::Protocol::Canvas::Canvas> buildObjectForCanvas(bool captureBacktrace);
 
 private:
     InspectorCanvas(CanvasRenderingContext&);

Modified: trunk/Source/WebCore/inspector/InspectorInstrumentation.cpp (236926 => 236927)


--- trunk/Source/WebCore/inspector/InspectorInstrumentation.cpp	2018-10-08 17:51:20 UTC (rev 236926)
+++ trunk/Source/WebCore/inspector/InspectorInstrumentation.cpp	2018-10-08 18:25:52 UTC (rev 236927)
@@ -128,6 +128,13 @@
     return false;
 }
 
+int InspectorInstrumentation::identifierForNodeImpl(InstrumentingAgents& instrumentingAgents, Node& node)
+{
+    if (InspectorDOMAgent* domAgent = instrumentingAgents.inspectorDOMAgent())
+        return domAgent->identifierForNode(node);
+    return 0;
+}
+
 void InspectorInstrumentation::willInsertDOMNodeImpl(InstrumentingAgents& instrumentingAgents, Node& parent)
 {
     if (InspectorDOMDebuggerAgent* domDebuggerAgent = instrumentingAgents.inspectorDOMDebuggerAgent())

Modified: trunk/Source/WebCore/inspector/InspectorInstrumentation.h (236926 => 236927)


--- trunk/Source/WebCore/inspector/InspectorInstrumentation.h	2018-10-08 17:51:20 UTC (rev 236926)
+++ trunk/Source/WebCore/inspector/InspectorInstrumentation.h	2018-10-08 18:25:52 UTC (rev 236927)
@@ -106,6 +106,7 @@
     static void didClearWindowObjectInWorld(Frame&, DOMWrapperWorld&);
     static bool isDebuggerPaused(Frame*);
 
+    static int identifierForNode(Node&);
     static void willInsertDOMNode(Document&, Node& parent);
     static void didInsertDOMNode(Document&, Node&);
     static void willRemoveDOMNode(Document&, Node&);
@@ -290,6 +291,7 @@
     static void didClearWindowObjectInWorldImpl(InstrumentingAgents&, Frame&, DOMWrapperWorld&);
     static bool isDebuggerPausedImpl(InstrumentingAgents&);
 
+    static int identifierForNodeImpl(InstrumentingAgents&, Node&);
     static void willInsertDOMNodeImpl(InstrumentingAgents&, Node& parent);
     static void didInsertDOMNodeImpl(InstrumentingAgents&, Node&);
     static void willRemoveDOMNodeImpl(InstrumentingAgents&, Node&);
@@ -470,6 +472,14 @@
     return false;
 }
 
+inline int InspectorInstrumentation::identifierForNode(Node& node)
+{
+    FAST_RETURN_IF_NO_FRONTENDS(0);
+    if (InstrumentingAgents* instrumentingAgents = instrumentingAgentsForDocument(node.document()))
+        return identifierForNodeImpl(*instrumentingAgents, node);
+    return 0;
+}
+
 inline void InspectorInstrumentation::willInsertDOMNode(Document& document, Node& parent)
 {
     FAST_RETURN_IF_NO_FRONTENDS(void());

Modified: trunk/Source/WebCore/inspector/agents/InspectorCanvasAgent.cpp (236926 => 236927)


--- trunk/Source/WebCore/inspector/agents/InspectorCanvasAgent.cpp	2018-10-08 17:51:20 UTC (rev 236926)
+++ trunk/Source/WebCore/inspector/agents/InspectorCanvasAgent.cpp	2018-10-08 18:25:52 UTC (rev 236927)
@@ -99,7 +99,7 @@
 
     const bool captureBacktrace = false;
     for (auto& inspectorCanvas : m_identifierToInspectorCanvas.values()) {
-        m_frontendDispatcher->canvasAdded(inspectorCanvas->buildObjectForCanvas(m_instrumentingAgents, captureBacktrace));
+        m_frontendDispatcher->canvasAdded(inspectorCanvas->buildObjectForCanvas(captureBacktrace));
 
 #if ENABLE(WEBGL)
         if (is<WebGLRenderingContextBase>(inspectorCanvas->context())) {
@@ -431,7 +431,7 @@
 
     if (m_enabled) {
         const bool captureBacktrace = true;
-        m_frontendDispatcher->canvasAdded(inspectorCanvas->buildObjectForCanvas(m_instrumentingAgents, captureBacktrace));
+        m_frontendDispatcher->canvasAdded(inspectorCanvas->buildObjectForCanvas(captureBacktrace));
     }
 
     m_identifierToInspectorCanvas.set(inspectorCanvas->identifier(), WTFMove(inspectorCanvas));

Modified: trunk/Source/WebCore/inspector/agents/InspectorDOMAgent.cpp (236926 => 236927)


--- trunk/Source/WebCore/inspector/agents/InspectorDOMAgent.cpp	2018-10-08 17:51:20 UTC (rev 236926)
+++ trunk/Source/WebCore/inspector/agents/InspectorDOMAgent.cpp	2018-10-08 18:25:52 UTC (rev 236927)
@@ -2085,6 +2085,11 @@
     m_frontendDispatcher->childNodeInserted(parentId, prevId, WTFMove(value));
 }
 
+int InspectorDOMAgent::identifierForNode(Node& node)
+{
+    return pushNodePathToFrontend(&node);
+}
+
 void InspectorDOMAgent::didInsertDOMNode(Node& node)
 {
     if (containsOnlyHTMLWhitespace(&node))

Modified: trunk/Source/WebCore/inspector/agents/InspectorDOMAgent.h (236926 => 236927)


--- trunk/Source/WebCore/inspector/agents/InspectorDOMAgent.h	2018-10-08 17:51:20 UTC (rev 236926)
+++ trunk/Source/WebCore/inspector/agents/InspectorDOMAgent.h	2018-10-08 18:25:52 UTC (rev 236927)
@@ -157,6 +157,7 @@
 
 
     // InspectorInstrumentation
+    int identifierForNode(Node&);
     void didInsertDOMNode(Node&);
     void didRemoveDOMNode(Node&);
     void willModifyDOMAttr(Element&, const AtomicString& oldValue, const AtomicString& newValue);

Modified: trunk/Source/WebCore/inspector/agents/InspectorNetworkAgent.cpp (236926 => 236927)


--- trunk/Source/WebCore/inspector/agents/InspectorNetworkAgent.cpp	2018-10-08 17:51:20 UTC (rev 236926)
+++ trunk/Source/WebCore/inspector/agents/InspectorNetworkAgent.cpp	2018-10-08 18:25:52 UTC (rev 236927)
@@ -45,6 +45,7 @@
 #include "FrameLoader.h"
 #include "HTTPHeaderMap.h"
 #include "HTTPHeaderNames.h"
+#include "InspectorDOMAgent.h"
 #include "InspectorTimelineAgent.h"
 #include "InstrumentingAgents.h"
 #include "JSExecState.h"
@@ -386,7 +387,7 @@
     auto protocolResourceType = InspectorPageAgent::resourceTypeJSON(type);
 
     Document* document = loader && loader->frame() ? loader->frame()->document() : nullptr;
-    auto initiatorObject = buildInitiatorObject(document);
+    auto initiatorObject = buildInitiatorObject(document, request);
 
     String url = "" ? loader->url().string() : request.url();
     m_frontendDispatcher->requestWillBeSent(requestId, frameId, loaderId, url, buildObjectForResourceRequest(request), sendTimestamp, walltime.secondsSinceEpoch().seconds(), initiatorObject, buildObjectForResourceResponse(redirectResponse, nullptr), type != InspectorPageAgent::OtherResource ? &protocolResourceType : nullptr, targetId.isEmpty() ? nullptr : &targetId);
@@ -566,7 +567,7 @@
 
     m_resourcesData->resourceCreated(requestId, loaderId, resource);
 
-    RefPtr<Inspector::Protocol::Network::Initiator> initiatorObject = buildInitiatorObject(loader->frame() ? loader->frame()->document() : nullptr);
+    auto initiatorObject = buildInitiatorObject(loader->frame() ? loader->frame()->document() : nullptr, resource.resourceRequest());
 
     // FIXME: It would be ideal to generate the Network.Response with the MemoryCache source
     // instead of whatever ResourceResponse::Source the CachedResources's response has.
@@ -636,7 +637,7 @@
         m_styleRecalculationInitiator = buildInitiatorObject(&document);
 }
 
-RefPtr<Inspector::Protocol::Network::Initiator> InspectorNetworkAgent::buildInitiatorObject(Document* document)
+RefPtr<Inspector::Protocol::Network::Initiator> InspectorNetworkAgent::buildInitiatorObject(Document* document, std::optional<const ResourceRequest&> resourceRequest)
 {
     // FIXME: Worker support.
     if (!isMainThread()) {
@@ -645,24 +646,38 @@
             .release();
     }
 
+    RefPtr<Inspector::Protocol::Network::Initiator> initiatorObject;
+
     Ref<ScriptCallStack> stackTrace = createScriptCallStack(JSExecState::currentState());
     if (stackTrace->size() > 0) {
-        auto initiatorObject = Inspector::Protocol::Network::Initiator::create()
+        initiatorObject = Inspector::Protocol::Network::Initiator::create()
             .setType(Inspector::Protocol::Network::Initiator::Type::Script)
             .release();
         initiatorObject->setStackTrace(stackTrace->buildInspectorArray());
-        return WTFMove(initiatorObject);
-    }
-
-    if (document && document->scriptableDocumentParser()) {
-        auto initiatorObject = Inspector::Protocol::Network::Initiator::create()
+    } else if (document && document->scriptableDocumentParser()) {
+        initiatorObject = Inspector::Protocol::Network::Initiator::create()
             .setType(Inspector::Protocol::Network::Initiator::Type::Parser)
             .release();
         initiatorObject->setUrl(document->url().string());
         initiatorObject->setLineNumber(document->scriptableDocumentParser()->textPosition().m_line.oneBasedInt());
-        return WTFMove(initiatorObject);
     }
 
+    auto domAgent = m_instrumentingAgents.inspectorDOMAgent();
+    if (domAgent && resourceRequest) {
+        if (auto inspectorInitiatorNodeIdentifier = resourceRequest->inspectorInitiatorNodeIdentifier()) {
+            if (!initiatorObject) {
+                initiatorObject = Inspector::Protocol::Network::Initiator::create()
+                    .setType(Inspector::Protocol::Network::Initiator::Type::Other)
+                    .release();
+            }
+
+            initiatorObject->setNodeId(*inspectorInitiatorNodeIdentifier);
+        }
+    }
+
+    if (initiatorObject)
+        return initiatorObject;
+
     if (m_isRecalculatingStyle && m_styleRecalculationInitiator)
         return m_styleRecalculationInitiator;
 

Modified: trunk/Source/WebCore/inspector/agents/InspectorNetworkAgent.h (236926 => 236927)


--- trunk/Source/WebCore/inspector/agents/InspectorNetworkAgent.h	2018-10-08 17:51:20 UTC (rev 236926)
+++ trunk/Source/WebCore/inspector/agents/InspectorNetworkAgent.h	2018-10-08 18:25:52 UTC (rev 236927)
@@ -132,7 +132,7 @@
 
     WebSocket* webSocketForRequestId(const String& requestId);
 
-    RefPtr<Inspector::Protocol::Network::Initiator> buildInitiatorObject(Document*);
+    RefPtr<Inspector::Protocol::Network::Initiator> buildInitiatorObject(Document*, std::optional<const ResourceRequest&> = std::nullopt);
     Ref<Inspector::Protocol::Network::ResourceTiming> buildObjectForTiming(const NetworkLoadMetrics&, ResourceLoader&);
     Ref<Inspector::Protocol::Network::Metrics> buildObjectForMetrics(const NetworkLoadMetrics&);
     RefPtr<Inspector::Protocol::Network::Response> buildObjectForResourceResponse(const ResourceResponse&, ResourceLoader*);

Modified: trunk/Source/WebCore/loader/FrameLoader.cpp (236926 => 236927)


--- trunk/Source/WebCore/loader/FrameLoader.cpp	2018-10-08 17:51:20 UTC (rev 236926)
+++ trunk/Source/WebCore/loader/FrameLoader.cpp	2018-10-08 18:25:52 UTC (rev 236927)
@@ -88,6 +88,7 @@
 #include "MemoryRelease.h"
 #include "NavigationDisabler.h"
 #include "NavigationScheduler.h"
+#include "Node.h"
 #include "Page.h"
 #include "PageCache.h"
 #include "PageTransitionEvent.h"
@@ -1644,7 +1645,7 @@
     return activeDocumentLoader()->originalRequest();
 }
 
-bool FrameLoader::willLoadMediaElementURL(URL& url)
+bool FrameLoader::willLoadMediaElementURL(URL& url, Node& initiatorNode)
 {
 #if PLATFORM(IOS)
     // MobileStore depends on the iOS 4.0 era client delegate method because webView:resource:willSendRequest:redirectResponse:fromDataSource
@@ -1654,6 +1655,7 @@
 #endif
 
     ResourceRequest request(url);
+    request.setInspectorInitiatorNodeIdentifier(InspectorInstrumentation::identifierForNode(initiatorNode));
 
     unsigned long identifier;
     ResourceError error;

Modified: trunk/Source/WebCore/loader/FrameLoader.h (236926 => 236927)


--- trunk/Source/WebCore/loader/FrameLoader.h	2018-10-08 17:51:20 UTC (rev 236926)
+++ trunk/Source/WebCore/loader/FrameLoader.h	2018-10-08 18:25:52 UTC (rev 236927)
@@ -72,6 +72,7 @@
 class HistoryItem;
 class NavigationAction;
 class NetworkingContext;
+class Node;
 class Page;
 class PolicyChecker;
 class ResourceError;
@@ -177,7 +178,7 @@
     const ResourceRequest& initialRequest() const;
     void receivedMainResourceError(const ResourceError&);
 
-    bool willLoadMediaElementURL(URL&);
+    bool willLoadMediaElementURL(URL&, Node&);
 
     void handleFallbackContent();
 

Modified: trunk/Source/WebCore/loader/ImageLoader.cpp (236926 => 236927)


--- trunk/Source/WebCore/loader/ImageLoader.cpp	2018-10-08 17:51:20 UTC (rev 236926)
+++ trunk/Source/WebCore/loader/ImageLoader.cpp	2018-10-08 18:25:52 UTC (rev 236927)
@@ -37,6 +37,7 @@
 #include "HTMLNames.h"
 #include "HTMLObjectElement.h"
 #include "HTMLParserIdioms.h"
+#include "InspectorInstrumentation.h"
 #include "Page.h"
 #include "RenderImage.h"
 #include "RenderSVGImage.h"
@@ -179,7 +180,11 @@
         options.sameOriginDataURLFlag = SameOriginDataURLFlag::Set;
 
         auto crossOriginAttribute = element().attributeWithoutSynchronization(HTMLNames::crossoriginAttr);
-        auto request = createPotentialAccessControlRequest(document.completeURL(sourceURI(attr)), document, crossOriginAttribute, WTFMove(options));
+
+        ResourceRequest resourceRequest(document.completeURL(sourceURI(attr)));
+        resourceRequest.setInspectorInitiatorNodeIdentifier(InspectorInstrumentation::identifierForNode(m_element));
+
+        auto request = createPotentialAccessControlRequest(WTFMove(resourceRequest), document, crossOriginAttribute, WTFMove(options));
         request.setInitiator(element());
 
         if (m_loadManually) {

Modified: trunk/Source/WebCore/loader/MediaResourceLoader.cpp (236926 => 236927)


--- trunk/Source/WebCore/loader/MediaResourceLoader.cpp	2018-10-08 17:51:20 UTC (rev 236926)
+++ trunk/Source/WebCore/loader/MediaResourceLoader.cpp	2018-10-08 18:25:52 UTC (rev 236927)
@@ -35,6 +35,7 @@
 #include "CrossOriginAccessControl.h"
 #include "Document.h"
 #include "HTMLMediaElement.h"
+#include "InspectorInstrumentation.h"
 #include "SecurityOrigin.h"
 #include <wtf/NeverDestroyed.h>
 
@@ -69,6 +70,10 @@
     auto cachingPolicy = options & LoadOption::DisallowCaching ? CachingPolicy::DisallowCaching : CachingPolicy::AllowCaching;
 
     request.setRequester(ResourceRequest::Requester::Media);
+
+    if (m_mediaElement)
+        request.setInspectorInitiatorNodeIdentifier(InspectorInstrumentation::identifierForNode(*m_mediaElement));
+
 #if HAVE(AVFOUNDATION_LOADER_DELEGATE) && PLATFORM(MAC)
     // FIXME: Workaround for <rdar://problem/26071607>. We are not able to do CORS checking on 304 responses because they are usually missing the headers we need.
     if (!m_crossOriginMode.isNull())

Modified: trunk/Source/WebCore/loader/TextTrackLoader.cpp (236926 => 236927)


--- trunk/Source/WebCore/loader/TextTrackLoader.cpp	2018-10-08 17:51:20 UTC (rev 236926)
+++ trunk/Source/WebCore/loader/TextTrackLoader.cpp	2018-10-08 18:25:52 UTC (rev 236927)
@@ -35,6 +35,8 @@
 #include "CachedTextTrack.h"
 #include "CrossOriginAccessControl.h"
 #include "Document.h"
+#include "HTMLTrackElement.h"
+#include "InspectorInstrumentation.h"
 #include "Logging.h"
 #include "SharedBuffer.h"
 #include "VTTCue.h"
@@ -142,7 +144,7 @@
     cancelLoad();
 }
 
-bool TextTrackLoader::load(const URL& url, const String& crossOriginMode, bool isInitiatingElementInUserAgentShadowTree)
+bool TextTrackLoader::load(const URL& url, HTMLTrackElement& element)
 {
     cancelLoad();
 
@@ -150,9 +152,14 @@
     Document& document = downcast<Document>(*m_scriptExecutionContext);
 
     ResourceLoaderOptions options = CachedResourceLoader::defaultCachedResourceOptions();
-    options.contentSecurityPolicyImposition = isInitiatingElementInUserAgentShadowTree ? ContentSecurityPolicyImposition::SkipPolicyCheck : ContentSecurityPolicyImposition::DoPolicyCheck;
+    options.contentSecurityPolicyImposition = element.isInUserAgentShadowTree() ? ContentSecurityPolicyImposition::SkipPolicyCheck : ContentSecurityPolicyImposition::DoPolicyCheck;
 
-    auto cueRequest = createPotentialAccessControlRequest(document.completeURL(url), document, crossOriginMode, WTFMove(options));
+    ResourceRequest resourceRequest(document.completeURL(url));
+
+    if (auto mediaElement = element.mediaElement())
+        resourceRequest.setInspectorInitiatorNodeIdentifier(InspectorInstrumentation::identifierForNode(*mediaElement));
+
+    auto cueRequest = createPotentialAccessControlRequest(WTFMove(resourceRequest), document, element.mediaElementCrossOriginAttribute(), WTFMove(options));
     m_resource = document.cachedResourceLoader().requestTextTrack(WTFMove(cueRequest)).value_or(nullptr);
     if (!m_resource)
         return false;

Modified: trunk/Source/WebCore/loader/TextTrackLoader.h (236926 => 236927)


--- trunk/Source/WebCore/loader/TextTrackLoader.h	2018-10-08 17:51:20 UTC (rev 236926)
+++ trunk/Source/WebCore/loader/TextTrackLoader.h	2018-10-08 18:25:52 UTC (rev 236927)
@@ -37,6 +37,7 @@
 
 class CachedTextTrack;
 class Document;
+class HTMLTrackElement;
 class TextTrackLoader;
 class ScriptExecutionContext;
 
@@ -55,8 +56,8 @@
 public:
     TextTrackLoader(TextTrackLoaderClient&, ScriptExecutionContext*);
     virtual ~TextTrackLoader();
-    
-    bool load(const URL&, const String& crossOriginMode, bool isInitiatingElementInUserAgentShadowTree);
+
+    bool load(const URL&, HTMLTrackElement&);
     void cancelLoad();
     void getNewCues(Vector<RefPtr<TextTrackCue>>& outputCues);
     void getNewRegions(Vector<RefPtr<VTTRegion>>& outputRegions);

Modified: trunk/Source/WebCore/loader/cache/CachedResourceLoader.cpp (236926 => 236927)


--- trunk/Source/WebCore/loader/cache/CachedResourceLoader.cpp	2018-10-08 17:51:20 UTC (rev 236926)
+++ trunk/Source/WebCore/loader/cache/CachedResourceLoader.cpp	2018-10-08 18:25:52 UTC (rev 236927)
@@ -600,6 +600,8 @@
 
     ResourceRequest newRequest = ResourceRequest(resource.url());
     newRequest.setInitiatorIdentifier(request.resourceRequest().initiatorIdentifier());
+    if (auto inspectorInitiatorNodeIdentifier = request.resourceRequest().inspectorInitiatorNodeIdentifier())
+        newRequest.setInspectorInitiatorNodeIdentifier(*inspectorInitiatorNodeIdentifier);
     if (request.resourceRequest().hiddenFromInspector())
         newRequest.setHiddenFromInspector(true);
     frame()->loader().loadedResourceFromMemoryCache(resource, newRequest, error);

Modified: trunk/Source/WebCore/platform/network/ResourceRequestBase.cpp (236926 => 236927)


--- trunk/Source/WebCore/platform/network/ResourceRequestBase.cpp	2018-10-08 17:51:20 UTC (rev 236926)
+++ trunk/Source/WebCore/platform/network/ResourceRequestBase.cpp	2018-10-08 18:25:52 UTC (rev 236927)
@@ -67,6 +67,9 @@
     setInitiatorIdentifier(other.initiatorIdentifier().isolatedCopy());
     setCachePartition(other.cachePartition().isolatedCopy());
 
+    if (auto inspectorInitiatorNodeIdentifier = other.inspectorInitiatorNodeIdentifier())
+        setInspectorInitiatorNodeIdentifier(*inspectorInitiatorNodeIdentifier);
+
     if (!other.isSameSiteUnspecified()) {
         setIsSameSite(other.isSameSite());
         setIsTopSite(other.isTopSite());

Modified: trunk/Source/WebCore/platform/network/ResourceRequestBase.h (236926 => 236927)


--- trunk/Source/WebCore/platform/network/ResourceRequestBase.h	2018-10-08 17:51:20 UTC (rev 236926)
+++ trunk/Source/WebCore/platform/network/ResourceRequestBase.h	2018-10-08 18:25:52 UTC (rev 236927)
@@ -172,6 +172,10 @@
     String initiatorIdentifier() const { return m_initiatorIdentifier; }
     void setInitiatorIdentifier(const String& identifier) { m_initiatorIdentifier = identifier; }
 
+    // Additional information for the Inspector to be able to identify the node that initiated this request.
+    const std::optional<int>& inspectorInitiatorNodeIdentifier() const { return m_inspectorInitiatorNodeIdentifier; }
+    void setInspectorInitiatorNodeIdentifier(int inspectorInitiatorNodeIdentifier) { m_inspectorInitiatorNodeIdentifier = inspectorInitiatorNodeIdentifier; }
+
 #if USE(SYSTEM_PREVIEW)
     WEBCORE_EXPORT bool isSystemPreview() const;
     WEBCORE_EXPORT void setSystemPreview(bool);
@@ -232,6 +236,7 @@
     SameSiteDisposition m_sameSiteDisposition { SameSiteDisposition::Unspecified };
     ResourceLoadPriority m_priority { ResourceLoadPriority::Low };
     Requester m_requester { Requester::Unspecified };
+    std::optional<int> m_inspectorInitiatorNodeIdentifier;
     bool m_allowCookies { false };
     mutable bool m_resourceRequestUpdated { false };
     mutable bool m_platformRequestUpdated { false };

Modified: trunk/Source/WebCore/platform/network/cf/ResourceRequestCFNet.cpp (236926 => 236927)


--- trunk/Source/WebCore/platform/network/cf/ResourceRequestCFNet.cpp	2018-10-08 17:51:20 UTC (rev 236926)
+++ trunk/Source/WebCore/platform/network/cf/ResourceRequestCFNet.cpp	2018-10-08 18:25:52 UTC (rev 236927)
@@ -371,6 +371,7 @@
     bool isHiddenFromInspector = hiddenFromInspector();
     auto oldRequester = requester();
     auto oldInitiatorIdentifier = initiatorIdentifier();
+    auto oldInspectorInitiatorNodeIdentifier = inspectorInitiatorNodeIdentifier();
 
     *this = delegateProvidedRequest;
 
@@ -379,6 +380,8 @@
     setHiddenFromInspector(isHiddenFromInspector);
     setRequester(oldRequester);
     setInitiatorIdentifier(oldInitiatorIdentifier);
+    if (oldInspectorInitiatorNodeIdentifier)
+        setInspectorInitiatorNodeIdentifier(*oldInspectorInitiatorNodeIdentifier);
 }
 
 bool ResourceRequest::httpPipeliningEnabled()

Modified: trunk/Source/WebInspectorUI/ChangeLog (236926 => 236927)


--- trunk/Source/WebInspectorUI/ChangeLog	2018-10-08 17:51:20 UTC (rev 236926)
+++ trunk/Source/WebInspectorUI/ChangeLog	2018-10-08 18:25:52 UTC (rev 236927)
@@ -1,5 +1,83 @@
 2018-10-08  Devin Rousso  <[email protected]>
 
+        Web Inspector: group media network entries by the node that triggered the request
+        https://bugs.webkit.org/show_bug.cgi?id=189606
+        <rdar://problem/44438527>
+
+        Reviewed by Brian Burg.
+
+        Introduces a `WI.NavigationItem` for changing whether network entries are grouped by the
+        node that initiated the load (if applicable). When grouped by node, a tree-like layout of
+        the table cells (including expand/collapse) is used for resources that share the same
+        initiator node. The values for the node's cell are based on it's initated resources.
+
+        * Localizations/en.lproj/localizedStrings.js:
+        * UserInterface/Base/Setting.js:
+
+        * UserInterface/Controllers/DOMManager.js:
+        (WI.DOMManager):
+        (WI.DOMManager.prototype._mainResourceDidChange): Added.
+        Whenever the frame navigates, re-request the document so that `NetworkAgent` is able to send
+        valid `nodeId` for each request's `initiatorNode`. This means that the document should
+        always be available.
+
+        * UserInterface/Views/NetworkTableContentView.js:
+        (WI.NetworkTableContentView):
+        (WI.NetworkTableContentView.prototype.get filterNavigationItems):
+        (WI.NetworkTableContentView.prototype.closed):
+        (WI.NetworkTableContentView.prototype.reset):
+        (WI.NetworkTableContentView.prototype.tableSortChanged):
+        (WI.NetworkTableContentView.prototype.tableSelectedRowChanged):
+        (WI.NetworkTableContentView.prototype.tablePopulateCell):
+        (WI.NetworkTableContentView.prototype._populateNameCell.createIconElement): Added.
+        (WI.NetworkTableContentView.prototype._populateNameCell):
+        (WI.NetworkTableContentView.prototype._populateDomainCell.createIconAndText): Added.
+        (WI.NetworkTableContentView.prototype._populateDomainCell):
+        (WI.NetworkTableContentView.prototype._populateInitiatorCell):
+        (WI.NetworkTableContentView.prototype._populateTransferSizeCell):
+        (WI.NetworkTableContentView.prototype._generateSortComparator):
+        (WI.NetworkTableContentView.prototype._processPendingEntries):
+        (WI.NetworkTableContentView.prototype._updateEntryForResource.updateExistingEntry): Added.
+        (WI.NetworkTableContentView.prototype._updateEntryForResource):
+        (WI.NetworkTableContentView.prototype._insertResourceAndReloadTable):
+        (WI.NetworkTableContentView.prototype._entryForDOMNode): Added.
+        (WI.NetworkTableContentView.prototype._tryLinkResourceToDOMNode): Added.
+        (WI.NetworkTableContentView.prototype._uniqueValuesForDOMNodeEntry): Added.
+        (WI.NetworkTableContentView.prototype._updateFilteredEntries):
+        (WI.NetworkTableContentView.prototype._handleGroupByDOMNodeCheckedDidChange): Added.
+        * UserInterface/Views/NetworkTableContentView.css:
+        (.network-table .cell.dom-node.name .icon): Added.
+        (.network-table .cell.dom-node.name .disclosure): Added.
+        (body[dir=rtl] .network-table .cell.dom-node.name .disclosure): Added.
+        (.network-table:focus li.selected .cell.dom-node.name .disclosure): Added.
+        (.network-table .cell.dom-node.name .disclosure.expanded): Added.
+        (.network-table:focus li.selected .cell.node.name .disclosure.expanded): Added.
+        (.network-table .cell.grouped-by-node.name): Added.
+        (body[dir=ltr] .network-table .cell.grouped-by-node.name): Added.
+        (body[dir=rtl] .network-table .cell.grouped-by-node.name): Added.
+        (.network-table li:not(.selected) .cell:matches(.cache-type, .multiple)): Added.
+        (.network-table li.selected .cell.domain > .lock): Added.
+        (.network-table .cache-type): Deleted.
+        When two resources are added that share the same `initiatorNode`, insert a node entry into
+        the `WI.Table` before the first resource entry for that node (based on the current sort).
+        This node entry is added after the resource entries are filtered, so they won't appear in
+        the default entries list.
+
+        * UserInterface/Models/Resource.js:
+        (WI.Resource):
+        (WI.Resource.prototype.initiatorNode): Added.
+        (WI.Resource.prototype.requestedByteRange): Added.
+        * UserInterface/Controllers/NetworkManager.js:
+        (WI.NetworkManager.prototype.resourceRequestWillBeSent):
+        (WI.NetworkManager.prototype.resourceRequestWasServedFromMemoryCache):
+        (WI.NetworkManager.prototype._initiatorNodeFromPayload): Added.
+
+        * UserInterface/Images/Range.svg: Added.
+        * UserInterface/Views/ResourceIcons.css:
+        (.resource-icon.resource-type-range .icon): Added.
+
+2018-10-08  Devin Rousso  <[email protected]>
+
         Web Inspector: clicking initiator link in Network Tab table doesn't automatically switch to Preview section
         https://bugs.webkit.org/show_bug.cgi?id=190286
 

Modified: trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js (236926 => 236927)


--- trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js	2018-10-08 17:51:20 UTC (rev 236926)
+++ trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js	2018-10-08 18:25:52 UTC (rev 236927)
@@ -57,6 +57,7 @@
 localizedStrings["(disk)"] = "(disk)";
 localizedStrings["(many)"] = "(many)";
 localizedStrings["(memory)"] = "(memory)";
+localizedStrings["(multiple)"] = "(multiple)";
 localizedStrings["(program)"] = "(program)";
 localizedStrings["(service worker)"] = "(service worker)";
 localizedStrings["(uninitialized)"] = "(uninitialized)";
@@ -137,6 +138,7 @@
 localizedStrings["Breakpoints disabled"] = "Breakpoints disabled";
 localizedStrings["Bubbling"] = "Bubbling";
 localizedStrings["Busy"] = "Busy";
+localizedStrings["Byte Range %s\u2013%s"] = "Byte Range %s\u2013%s";
 localizedStrings["Bytes Received"] = "Bytes Received";
 localizedStrings["Bytes Sent"] = "Bytes Sent";
 localizedStrings["CSP Hash"] = "CSP Hash";

Modified: trunk/Source/WebInspectorUI/UserInterface/Base/Setting.js (236926 => 236927)


--- trunk/Source/WebInspectorUI/UserInterface/Base/Setting.js	2018-10-08 17:51:20 UTC (rev 236926)
+++ trunk/Source/WebInspectorUI/UserInterface/Base/Setting.js	2018-10-08 18:25:52 UTC (rev 236927)
@@ -119,6 +119,7 @@
     showRulers: new WI.Setting("show-rulers", false),
     showAssertionFailuresBreakpoint: new WI.Setting("show-assertion-failures-breakpoint", true),
     showAllRequestsBreakpoint: new WI.Setting("show-all-requests-breakpoint", true),
+    groupByDOMNode: new WI.Setting("group-by-dom-node", false),
 
     // Experimental
     experimentalEnableMultiplePropertiesSelection: new WI.Setting("experimental-enable-multiple-properties-selection", false),

Modified: trunk/Source/WebInspectorUI/UserInterface/Controllers/DOMManager.js (236926 => 236927)


--- trunk/Source/WebInspectorUI/UserInterface/Controllers/DOMManager.js	2018-10-08 17:51:20 UTC (rev 236926)
+++ trunk/Source/WebInspectorUI/UserInterface/Controllers/DOMManager.js	2018-10-08 18:25:52 UTC (rev 236927)
@@ -46,6 +46,8 @@
         this._breakpointsForEventListeners = new Map;
 
         WI.Frame.addEventListener(WI.Frame.Event.MainResourceDidChange, this._mainResourceDidChange, this);
+
+        this.ensureDocument();
     }
 
     // Public
@@ -628,8 +630,12 @@
 
     _mainResourceDidChange(event)
     {
-        if (event.target.isMainFrame())
-            this._restoreSelectedNodeIsAllowed = true;
+        if (!event.target.isMainFrame())
+            return;
+
+        this._restoreSelectedNodeIsAllowed = true;
+
+        this.ensureDocument();
     }
 };
 

Modified: trunk/Source/WebInspectorUI/UserInterface/Controllers/NetworkManager.js (236926 => 236927)


--- trunk/Source/WebInspectorUI/UserInterface/Controllers/NetworkManager.js	2018-10-08 17:51:20 UTC (rev 236926)
+++ trunk/Source/WebInspectorUI/UserInterface/Controllers/NetworkManager.js	2018-10-08 18:25:52 UTC (rev 236927)
@@ -257,6 +257,7 @@
             requestSentTimestamp: elapsedTime,
             requestSentWalltime: walltime,
             initiatorSourceCodeLocation: this._initiatorSourceCodeLocationFromPayload(initiator),
+            initiatorNode: this._initiatorNodeFromPayload(initiator),
             originalRequestWillBeSentTimestamp,
         });
 
@@ -400,6 +401,7 @@
             requestMethod: "GET",
             requestSentTimestamp: elapsedTime,
             initiatorSourceCodeLocation: this._initiatorSourceCodeLocationFromPayload(initiator),
+            initiatorNode: this._initiatorNodeFromPayload(initiator),
         });
         resource.updateForResponse(cachedResourcePayload.url, response.mimeType, cachedResourcePayload.type, response.headers, response.status, response.statusText, elapsedTime, response.timing, responseSource);
         resource.increaseSize(cachedResourcePayload.bodySize, elapsedTime);
@@ -705,6 +707,11 @@
         return sourceCode.createSourceCodeLocation(lineNumber, columnNumber);
     }
 
+    _initiatorNodeFromPayload(initiatorPayload)
+    {
+        return WI.domManager.nodeForId(initiatorPayload.nodeId);
+    }
+
     _processServiceWorkerConfiguration(error, initializationPayload)
     {
         console.assert(this._waitingForMainFrameResourceTreePayload);

Added: trunk/Source/WebInspectorUI/UserInterface/Images/Range.svg (0 => 236927)


--- trunk/Source/WebInspectorUI/UserInterface/Images/Range.svg	                        (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Images/Range.svg	2018-10-08 18:25:52 UTC (rev 236927)
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright © 2013 Apple Inc. All rights reserved. -->
+<svg xmlns="http://www.w3.org/2000/svg" id="root" version="1.1" viewBox="0 0 16 16">
+    <path fill="rgb(148, 183, 219)" d="M 13 1 L 3 1 C 1.898438 1 1 1.898438 1 3 L 1 13 C 1 14.101562 1.898438 15 3 15 L 13 15 C 14.101562 15 15 14.101562 15 13 L 15 3 C 15 1.898438 14.101562 1 13 1 Z"/>
+    <path fill="rgb(106, 136, 170)" d="M 13 1 L 3 1 C 1.898438 1 1 1.898438 1 3 L 1 13 C 1 14.101562 1.898438 15 3 15 L 13 15 C 14.101562 15 15 14.101562 15 13 L 15 3 C 15 1.898438 14.101562 1 13 1 M 13 2 C 13.550781 2 14 2.449219 14 3 L 14 13 C 14 13.550781 13.550781 14 13 14 L 3 14 C 2.449219 14 2 13.550781 2 13 L 2 3 C 2 2.449219 2.449219 2 3 2 L 13 2"/>
+    <path fill="white" d="M 7 7.632812 L 7.027344 7.632812 C 8.28125 7.632812 9.0625 7.144531 9.0625 6.167969 C 9.0625 5.453125 8.347656 5.097656 7.230469 5.097656 L 7 5.097656 Z M 5 12 L 5 4 L 8.109375 4 C 9.800781 4 11 4.675781 11 6 C 11 6.496094 10.855469 6.941406 10.570312 7.347656 C 10.285156 7.753906 9.53125 8.050781 9.03125 8.265625 L 11.75 12 L 9.402344 12 L 7.34375 8.730469 L 7 8.730469 L 7 12 Z"/>
+    <path fill="rgb(113, 146, 184)" d="M 8.109375 3 L 4 3 L 4 13 L 8 13 L 8 11.644531 L 8.558594 12.53125 L 8.851562 13 L 13.714844 13 L 12.558594 11.410156 L 10.550781 8.652344 C 10.875 8.460938 11.175781 8.226562 11.386719 7.925781 C 11.792969 7.351562 12 6.703125 12 6 C 12 4.179688 10.472656 3 8.109375 3 M 7 7.632812 L 7.027344 7.632812 C 8.28125 7.632812 9.0625 7.144531 9.0625 6.167969 C 9.0625 5.453125 8.347656 5.097656 7.230469 5.097656 L 7 5.097656 L 7 7.632812 M 8.109375 4 C 9.796875 4 11 4.679688 11 6 C 11 6.492188 10.855469 6.941406 10.570312 7.347656 C 10.285156 7.753906 9.535156 8.050781 9.03125 8.265625 L 11.75 12 L 9.40625 12 L 7.34375 8.730469 L 7 8.730469 L 7 12 L 5 12 L 5 4 L 8.109375 4"/>
+</svg>

Modified: trunk/Source/WebInspectorUI/UserInterface/Models/Resource.js (236926 => 236927)


--- trunk/Source/WebInspectorUI/UserInterface/Models/Resource.js	2018-10-08 17:51:20 UTC (rev 236926)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/Resource.js	2018-10-08 18:25:52 UTC (rev 236927)
@@ -26,7 +26,7 @@
 
 WI.Resource = class Resource extends WI.SourceCode
 {
-    constructor(url, {mimeType, type, loaderIdentifier, targetId, requestIdentifier, requestMethod, requestHeaders, requestData, requestSentTimestamp, requestSentWalltime, initiatorSourceCodeLocation, originalRequestWillBeSentTimestamp} = {})
+    constructor(url, {mimeType, type, loaderIdentifier, targetId, requestIdentifier, requestMethod, requestHeaders, requestData, requestSentTimestamp, requestSentWalltime, initiatorSourceCodeLocation, initiatorNode, originalRequestWillBeSentTimestamp} = {})
     {
         super();
 
@@ -52,6 +52,7 @@
         this._responseCookies = null;
         this._parentFrame = null;
         this._initiatorSourceCodeLocation = initiatorSourceCodeLocation || null;
+        this._initiatorNode = initiatorNode || null;
         this._initiatedResources = [];
         this._originalRequestWillBeSentTimestamp = originalRequestWillBeSentTimestamp || null;
         this._requestSentTimestamp = requestSentTimestamp || NaN;
@@ -289,6 +290,7 @@
     get requestMethod() { return this._requestMethod; }
     get requestData() { return this._requestData; }
     get initiatorSourceCodeLocation() { return this._initiatorSourceCodeLocation; }
+    get initiatorNode() { return this._initiatorNode; }
     get initiatedResources() { return this._initiatedResources; }
     get originalRequestWillBeSentTimestamp() { return this._originalRequestWillBeSentTimestamp; }
     get statusCode() { return this._statusCode; }
@@ -564,6 +566,27 @@
         return !!(contentEncoding && /\b(?:gzip|deflate)\b/.test(contentEncoding));
     }
 
+    get requestedByteRange()
+    {
+        let range = this._requestHeaders.valueForCaseInsensitiveKey("Range");
+        if (!range)
+            return null;
+
+        let rangeValues = range.match(/bytes=(\d+)-(\d+)/);
+        if (!rangeValues)
+            return null;
+
+        let start = parseInt(rangeValues[1]);
+        if (isNaN(start))
+            return null;
+
+        let end = parseInt(rangeValues[2]);
+        if (isNaN(end))
+            return null;
+
+        return {start, end};
+    }
+
     get scripts()
     {
         return this._scripts || [];

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/NetworkTableContentView.css (236926 => 236927)


--- trunk/Source/WebInspectorUI/UserInterface/Views/NetworkTableContentView.css	2018-10-08 17:51:20 UTC (rev 236926)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/NetworkTableContentView.css	2018-10-08 18:25:52 UTC (rev 236927)
@@ -40,7 +40,48 @@
     cursor: pointer;
 }
 
-.network-table .cache-type {
+.network-table .cell.dom-node.name .icon {
+    content: url(../Images/DOMElement.svg);
+}
+
+.network-table .cell.dom-node.name .disclosure {
+    width: 13px;
+    height: 13px;
+    vertical-align: -2px;
+    content: url(../Images/DisclosureTriangles.svg#closed-normal);
+    background-size: 13px 13px;
+    background-repeat: no-repeat;
+}
+
+body[dir=rtl] .network-table .cell.dom-node.name .disclosure {
+    transform: scaleX(-1);
+}
+
+.network-table:focus li.selected .cell.dom-node.name .disclosure {
+    content: url(../Images/DisclosureTriangles.svg#closed-selected);
+}
+
+.network-table .cell.dom-node.name .disclosure.expanded {
+    content: url(../Images/DisclosureTriangles.svg#open-normal);
+}
+
+.network-table:focus li.selected .cell.dom-node.name .disclosure.expanded {
+    content: url(../Images/DisclosureTriangles.svg#open-selected);
+}
+
+.network-table .cell.grouped-by-node.name {
+    --item-padding-start: 19px;
+}
+
+body[dir=ltr] .network-table .cell.grouped-by-node.name {
+    padding-left: var(--item-padding-start);
+}
+
+body[dir=rtl] .network-table .cell.grouped-by-node.name {
+    padding-right: var(--item-padding-start);
+}
+
+.network-table li:not(.selected) .cell:matches(.cache-type, .multiple) {
     color: var(--text-color-gray-medium);
 }
 
@@ -55,6 +96,11 @@
     -webkit-margin-end: 5px;
 }
 
+.network-table li.selected .cell.domain > .lock {
+    /* FIXME: <https://webkit.org/b/189773> Web Inspector: create special Network waterfall for media events */
+    filter: invert();
+}
+
 body[dir=ltr] .network-table .cell.name > .status {
     float: right;
     margin-left: 4px;

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/NetworkTableContentView.js (236926 => 236927)


--- trunk/Source/WebInspectorUI/UserInterface/Views/NetworkTableContentView.js	2018-10-08 17:51:20 UTC (rev 236926)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/NetworkTableContentView.js	2018-10-08 18:25:52 UTC (rev 236927)
@@ -44,6 +44,8 @@
         this._resourceDetailView = null;
         this._resourceDetailViewMap = new Map;
 
+        this._domNodeEntries = new Map;
+
         this._waterfallStartTime = NaN;
         this._waterfallEndTime = NaN;
         this._waterfallTimelineRuler = null;
@@ -83,6 +85,9 @@
         this._typeFilterScopeBar = new WI.ScopeBar("network-type-filter-scope-bar", typeFilterScopeBarItems, typeFilterScopeBarItems[0]);
         this._typeFilterScopeBar.addEventListener(WI.ScopeBar.Event.SelectionChanged, this._typeFilterScopeBarSelectionChanged, this);
 
+        this._groupByDOMNodeNavigationItem = new WI.CheckboxNavigationItem("group-by-node", WI.UIString("Group by Node"), WI.settings.groupByDOMNode.value);
+        this._groupByDOMNodeNavigationItem.addEventListener(WI.CheckboxNavigationItem.Event.CheckedDidChange, this._handleGroupByDOMNodeCheckedDidChange, this);
+
         this._urlFilterSearchText = null;
         this._urlFilterSearchRegex = null;
         this._urlFilterIsActive = false;
@@ -201,7 +206,7 @@
 
     get filterNavigationItems()
     {
-        return [this._urlFilterNavigationItem, this._typeFilterScopeBar];
+        return [this._urlFilterNavigationItem, this._typeFilterScopeBar, this._groupByDOMNodeNavigationItem];
     }
 
     get supportsSave()
@@ -241,6 +246,8 @@
             detailView.dispose();
         this._resourceDetailViewMap.clear();
 
+        this._domNodeEntries.clear();
+
         this._hidePopover();
         this._hideResourceDetailView();
 
@@ -264,6 +271,8 @@
             detailView.dispose();
         this._resourceDetailViewMap.clear();
 
+        this._domNodeEntries.clear();
+
         this._waterfallStartTime = NaN;
         this._waterfallEndTime = NaN;
         this._updateWaterfallTimelineRuler();
@@ -320,7 +329,11 @@
 
         this._hideResourceDetailView();
 
-        this._entries = this._entries.sort(this._entriesSortComparator);
+        for (let nodeEntry of this._domNodeEntries.values())
+            nodeEntry.initiatedResourceEntries.sort(this._entriesSortComparator);
+
+        this._entries.sort(this._entriesSortComparator);
+
         this._updateFilteredEntries();
         this._table.reloadData();
     }
@@ -361,7 +374,10 @@
             return;
 
         this._selectedResource = entry.resource;
-        this._showResourceDetailView(this._selectedResource);
+        if (this._selectedResource)
+            this._showResourceDetailView(this._selectedResource);
+        else
+            this._hideResourceDetailView();
     }
 
     tablePopulateCell(table, cell, column, rowIndex)
@@ -368,8 +384,25 @@
     {
         let entry = this._filteredEntries[rowIndex];
 
-        cell.classList.toggle("error", entry.resource.hadLoadingError());
+        if (entry.resource)
+            cell.classList.toggle("error", entry.resource.hadLoadingError());
 
+        let setTextContent = (accessor) => {
+            let uniqueValues = this._uniqueValuesForDOMNodeEntry(entry, accessor);
+            if (uniqueValues) {
+                if (uniqueValues.size > 1) {
+                    cell.classList.add("multiple");
+                    cell.textContent = WI.UIString("(multiple)");
+                    return;
+                }
+
+                cell.textContent = uniqueValues.values().next().value || emDash;
+                return;
+            }
+
+            cell.textContent = accessor(entry) || emDash;
+        };
+
         switch (column.identifier) {
         case "name":
             this._populateNameCell(cell, entry);
@@ -378,45 +411,55 @@
             this._populateDomainCell(cell, entry);
             break;
         case "type":
-            cell.textContent = entry.displayType || emDash;
+            setTextContent((resourceEntry) => resourceEntry.displayType);
             break;
         case "mimeType":
-            cell.textContent = entry.mimeType || emDash;
+            setTextContent((resourceEntry) => resourceEntry.mimeType);
             break;
         case "method":
-            cell.textContent = entry.method || emDash;
+            setTextContent((resourceEntry) => resourceEntry.method);
             break;
         case "scheme":
-            cell.textContent = entry.scheme || emDash;
+            setTextContent((resourceEntry) => resourceEntry.scheme);
             break;
         case "status":
-            cell.textContent = entry.status || emDash;
+            setTextContent((resourceEntry) => resourceEntry.status);
             break;
         case "protocol":
-            cell.textContent = entry.protocol || emDash;
+            setTextContent((resourceEntry) => resourceEntry.protocol);
             break;
         case "initiator":
             this._populateInitiatorCell(cell, entry);
             break;
         case "priority":
-            cell.textContent = WI.Resource.displayNameForPriority(entry.priority) || emDash;
+            setTextContent((resourceEntry) => WI.Resource.displayNameForPriority(resourceEntry.priority));
             break;
         case "remoteAddress":
-            cell.textContent = entry.remoteAddress || emDash;
+            setTextContent((resourceEntry) => resourceEntry.remoteAddress);
             break;
         case "connectionIdentifier":
-            cell.textContent = entry.connectionIdentifier || emDash;
+            setTextContent((resourceEntry) => resourceEntry.connectionIdentifier);
             break;
-        case "resourceSize":
-            cell.textContent = isNaN(entry.resourceSize) ? emDash : Number.bytesToString(entry.resourceSize);
+        case "resourceSize": {
+            let resourceSize = entry.resourceSize;
+            let resourceEntries = entry.initiatedResourceEntries;
+            if (resourceEntries)
+                resourceSize = resourceEntries.reduce((accumulator, current) => accumulator + (current.resourceSize || 0), 0);
+            cell.textContent = isNaN(resourceSize) ? emDash : Number.bytesToString(resourceSize);
             break;
+        }
         case "transferSize":
             this._populateTransferSizeCell(cell, entry);
             break;
-        case "time":
+        case "time": {
             // FIXME: <https://webkit.org/b/176748> Web Inspector: Frontend sometimes receives resources with negative duration (responseEnd - requestStart)
-            cell.textContent = isNaN(entry.time) ? emDash : Number.secondsToString(Math.max(entry.time, 0));
+            let time = entry.time;
+            let resourceEntries = entry.initiatedResourceEntries;
+            if (resourceEntries)
+                time = resourceEntries.reduce((accumulator, current) => accumulator + (current.time || 0), 0);
+            cell.textContent = isNaN(time) ? emDash : Number.secondsToString(Math.max(time, 0));
             break;
+        }
         case "waterfall":
             this._populateWaterfallGraph(cell, entry);
             break;
@@ -431,6 +474,30 @@
     {
         console.assert(!cell.firstChild, "We expect the cell to be empty.", cell, cell.firstChild);
 
+        function createIconElement() {
+            let iconElement = cell.appendChild(document.createElement("img"));
+            iconElement.className = "icon";
+        }
+
+        let domNode = entry.domNode;
+        if (domNode) {
+            let disclosureElement = cell.appendChild(document.createElement("img"));
+            disclosureElement.classList.add("disclosure");
+            disclosureElement.classList.toggle("expanded", !!entry.expanded);
+            disclosureElement.addEventListener("click", (event) => {
+                entry.expanded = !entry.expanded;
+
+                this._updateFilteredEntries();
+                this._table.reloadData();
+            });
+
+            createIconElement();
+
+            cell.classList.add("dom-node");
+            cell.appendChild(WI.linkifyNodeReference(domNode));
+            return;
+        }
+
         let resource = entry.resource;
         if (resource.isLoading()) {
             let statusElement = cell.appendChild(document.createElement("div"));
@@ -439,14 +506,31 @@
             statusElement.appendChild(spinner.element);
         }
 
-        let iconElement = cell.appendChild(document.createElement("img"));
-        iconElement.className = "icon";
-        cell.classList.add(WI.ResourceTreeElement.ResourceIconStyleClassName, entry.resource.type);
+        createIconElement();
 
+        cell.classList.add(WI.ResourceTreeElement.ResourceIconStyleClassName);
+
         let nameElement = cell.appendChild(document.createElement("span"));
-        nameElement.textContent = entry.name;
 
-        cell.title = entry.resource.url;
+        if (WI.settings.groupByDOMNode.value && resource.initiatorNode) {
+            let nodeEntry = this._domNodeEntries.get(resource.initiatorNode);
+            if (nodeEntry.initiatedResourceEntries.length > 1) {
+                cell.classList.add("grouped-by-node");
+
+                let range = resource.requestedByteRange;
+                if (range) {
+                    cell.classList.add("resource-type-range");
+                    nameElement.textContent = WI.UIString("Byte Range %s\u2013%s").format(range.start, range.end);
+                }
+            }
+        }
+
+        if (!nameElement.textContent) {
+            cell.classList.add(resource.type);
+            nameElement.textContent = entry.name;
+        }
+
+        cell.title = resource.url;
     }
 
     _populateDomainCell(cell, entry)
@@ -453,22 +537,45 @@
     {
         console.assert(!cell.firstChild, "We expect the cell to be empty.", cell, cell.firstChild);
 
+        function createIconAndText(scheme, domain) {
+            let secure = scheme === "https" || scheme === "wss";
+            if (secure) {
+                let lockIconElement = cell.appendChild(document.createElement("img"));
+                lockIconElement.className = "lock";
+            }
+
+            cell.append(domain);
+        }
+
+        let uniqueSchemeValues = this._uniqueValuesForDOMNodeEntry(entry, (resourceEntry) => resourceEntry.scheme);
+        let uniqueDomainValues = this._uniqueValuesForDOMNodeEntry(entry, (resourceEntry) => resourceEntry.domain);
+        if (uniqueSchemeValues && uniqueDomainValues) {
+            if (uniqueSchemeValues.size > 1 || uniqueDomainValues.size > 1) {
+                cell.classList.add("multiple");
+                cell.textContent = WI.UIString("(multiple)");
+                return;
+            }
+
+            createIconAndText(uniqueSchemeValues.values().next().value, uniqueDomainValues.values().next().value);
+            return;
+        }
+
         if (!entry.domain) {
             cell.textContent = emDash;
             return;
         }
 
-        let secure = entry.scheme === "https" || entry.scheme === "wss";
-        if (secure) {
-            let lockIconElement = cell.appendChild(document.createElement("img"));
-            lockIconElement.className = "lock";
-        }
-
-        cell.append(entry.domain);
+        createIconAndText(entry.scheme, entry.domain);
     }
 
     _populateInitiatorCell(cell, entry)
     {
+        let domNode = entry.domNode;
+        if (domNode) {
+            cell.textContent = emDash;
+            return;
+        }
+
         let initiatorLocation = entry.resource.initiatorSourceCodeLocation;
         if (!initiatorLocation) {
             cell.textContent = emDash;
@@ -484,6 +591,31 @@
 
     _populateTransferSizeCell(cell, entry)
     {
+        let resourceEntries = entry.initiatedResourceEntries;
+        if (resourceEntries) {
+            if (resourceEntries.every((resourceEntry) => resourceEntry.resource.responseSource === WI.Resource.ResponseSource.MemoryCache)) {
+                cell.classList.add("cache-type");
+                cell.textContent = WI.UIString("(memory)");
+                return;
+            }
+            if (resourceEntries.every((resourceEntry) => resourceEntry.resource.responseSource === WI.Resource.ResponseSource.DiskCache)) {
+                cell.classList.add("cache-type");
+                cell.textContent = WI.UIString("(disk)");
+                return;
+            }
+            if (resourceEntries.every((resourceEntry) => resourceEntry.resource.responseSource === WI.Resource.ResponseSource.ServiceWorker)) {
+                cell.classList.add("cache-type");
+                cell.textContent = WI.UIString("(service worker)");
+                return;
+            }
+            let transferSize = resourceEntries.reduce((accumulator, current) => accumulator + (current.transferSize || 0), 0);
+            if (isNaN(transferSize))
+                cell.textContent = emDash;
+            else
+                cell.textContent = Number.bytesToString(transferSize);
+            return;
+        }
+
         let responseSource = entry.resource.responseSource;
         if (responseSource === WI.Resource.ResponseSource.MemoryCache) {
             cell.classList.add("cache-type");
@@ -510,6 +642,12 @@
     {
         cell.removeChildren();
 
+        let domNode = entry.domNode;
+        if (domNode) {
+            // FIXME: <https://webkit.org/b/189773> Web Inspector: create special Network waterfall for media events
+            return;
+        }
+
         let resource = entry.resource;
         if (!resource.hasResponse()) {
             cell.textContent = zeroWidthSpace;
@@ -667,7 +805,7 @@
 
         case "waterfall":
             // Sort by startTime number.
-            comparator = comparator = (a, b) => a.startTime - b.startTime;
+            comparator = (a, b) => a.startTime - b.startTime;
             break;
 
         default:
@@ -676,7 +814,21 @@
         }
 
         let reverseFactor = this._table.sortOrder === WI.Table.SortOrder.Ascending ? 1 : -1;
-        this._entriesSortComparator = (a, b) => reverseFactor * comparator(a, b);
+
+        // If the entry has an `initiatorNode`, use that node's "first" resource as the value of
+        // `entry`, so long as the entry being compared to doesn't have the same `initiatorNode`.
+        // This will ensure that all resource entries for a given `initiatorNode` will appear right
+        // next to each other, as they will all effectively be sorted by the first resource.
+        let substitute = (entry, other) => {
+            if (WI.settings.groupByDOMNode.value && entry.resource.initiatorNode) {
+                let nodeEntry = this._domNodeEntries.get(entry.resource.initiatorNode);
+                if (!nodeEntry.initiatedResourceEntries.includes(other))
+                    return nodeEntry.initiatedResourceEntries[0];
+            }
+            return entry;
+        };
+
+        this._entriesSortComparator = (a, b) => reverseFactor * comparator(substitute(a, b), substitute(b, a));
     }
 
     // Protected
@@ -884,8 +1036,11 @@
             return;
         }
 
-        for (let resource of this._pendingInsertions)
-            this._entries.push(this._entryForResource(resource));
+        for (let resource of this._pendingInsertions) {
+            let resourceEntry = this._entryForResource(resource);
+            this._tryLinkResourceToDOMNode(resourceEntry);
+            this._entries.push(resourceEntry);
+        }
         this._pendingInsertions = [];
 
         for (let resource of this._pendingUpdates)
@@ -952,14 +1107,20 @@
         if (index === -1)
             return;
 
+        // Don't wipe out the previous entry, as it may be used by a node entry.
+        function updateExistingEntry(existingEntry, newEntry) {
+            for (let key in newEntry)
+                existingEntry[key] = newEntry[key];
+        }
+
         let entry = this._entryForResource(resource);
-        this._entries[index] = entry;
+        updateExistingEntry(this._entries[index], entry);
 
         let rowIndex = this._rowIndexForResource(resource);
         if (rowIndex === -1)
             return;
 
-        this._filteredEntries[rowIndex] = entry;
+        updateExistingEntry(this._filteredEntries[rowIndex], entry);
     }
 
     _hidePopover()
@@ -1186,26 +1347,34 @@
             return;
         }
 
-        let entry = this._entryForResource(resource);
+        let resourceEntry = this._entryForResource(resource);
 
-        // Default sort has fast path.
-        if (this._isDefaultSort() || !this._entriesSortComparator) {
-            this._entries.push(entry);
-            if (this._passFilter(entry)) {
-                this._filteredEntries.push(entry);
+        this._tryLinkResourceToDOMNode(resourceEntry);
+
+        if (WI.settings.groupByDOMNode.value && resource.initiatorNode) {
+            if (!this._entriesSortComparator)
+                this._generateSortComparator();
+        } else if (this._isDefaultSort() || !this._entriesSortComparator) {
+            // Default sort has fast path.
+            this._entries.push(resourceEntry);
+            if (this._passFilter(resourceEntry)) {
+                this._filteredEntries.push(resourceEntry);
                 this._table.reloadDataAddedToEndOnly();
             }
             return;
         }
 
-        insertObjectIntoSortedArray(entry, this._entries, this._entriesSortComparator);
+        insertObjectIntoSortedArray(resourceEntry, this._entries, this._entriesSortComparator);
 
-        if (this._passFilter(entry)) {
-            insertObjectIntoSortedArray(entry, this._filteredEntries, this._entriesSortComparator);
+        if (this._passFilter(resourceEntry)) {
+            if (WI.settings.groupByDOMNode.value)
+                this._updateFilteredEntries();
+            else
+                insertObjectIntoSortedArray(resourceEntry, this._filteredEntries, this._entriesSortComparator);
 
             // Probably a useless optimization here, but if we only added this row to the end
             // we may avoid recreating all visible rows by saying as such.
-            if (this._filteredEntries.lastValue === entry)
+            if (this._filteredEntries.lastValue === resourceEntry)
                 this._table.reloadDataAddedToEndOnly();
             else
                 this._table.reloadData();
@@ -1240,6 +1409,47 @@
         };
     }
 
+    _entryForDOMNode(domNode)
+    {
+        return {
+            domNode,
+            initiatedResourceEntries: [],
+            expanded: true,
+        };
+    }
+
+    _tryLinkResourceToDOMNode(resourceEntry)
+    {
+        let resource = resourceEntry.resource;
+        if (!resource || !resource.initiatorNode)
+            return;
+
+        let nodeEntry = this._domNodeEntries.get(resource.initiatorNode);
+        if (!nodeEntry) {
+            nodeEntry = this._entryForDOMNode(resource.initiatorNode, Object.keys(resourceEntry));
+            this._domNodeEntries.set(resource.initiatorNode, nodeEntry);
+        }
+
+        if (!this._entriesSortComparator)
+            this._generateSortComparator();
+
+        insertObjectIntoSortedArray(resourceEntry, nodeEntry.initiatedResourceEntries, this._entriesSortComparator);
+    }
+
+    _uniqueValuesForDOMNodeEntry(nodeEntry, accessor)
+    {
+        let resourceEntries = nodeEntry.initiatedResourceEntries;
+        if (!resourceEntries)
+            return null;
+
+        return resourceEntries.reduce((accumulator, current) => {
+            let value = accessor(current);
+            if (value || typeof value === "number")
+                accumulator.add(value);
+            return accumulator;
+        }, new Set);
+    }
+
     _hasTypeFilter()
     {
         return !!this._activeTypeFilters;
@@ -1291,6 +1501,37 @@
         else
             this._filteredEntries = this._entries.slice();
 
+        if (WI.settings.groupByDOMNode.value) {
+            for (let nodeEntry of this._domNodeEntries.values()) {
+                if (nodeEntry.initiatedResourceEntries.length < 2)
+                    continue;
+
+                let firstIndex = Infinity;
+                for (let resourceEntry of nodeEntry.initiatedResourceEntries) {
+                    if (this._hasActiveFilter() && !this._passFilter(resourceEntry))
+                        continue;
+
+                    let index = this._filteredEntries.indexOf(resourceEntry);
+                    if (index >= 0 && index < firstIndex)
+                        firstIndex = index;
+                }
+
+                if (!isFinite(firstIndex))
+                    continue;
+
+                this._filteredEntries.insertAtIndex(nodeEntry, firstIndex);
+            }
+
+            this._filteredEntries = this._filteredEntries.filter((entry) => {
+                if (entry.resource && entry.resource.initiatorNode) {
+                    let nodeEntry = this._domNodeEntries.get(entry.resource.initiatorNode);
+                    if (!nodeEntry.expanded)
+                        return false;
+                }
+                return true;
+            });
+        }
+
         this._restoreSelectedRow();
 
         this._updateURLFilterActiveIndicator();
@@ -1362,6 +1603,14 @@
         this._table.reloadData();
     }
 
+    _handleGroupByDOMNodeCheckedDidChange(event)
+    {
+        WI.settings.groupByDOMNode.value = this._groupByDOMNodeNavigationItem.checked;
+
+        this._updateSortAndFilteredEntries();
+        this._table.reloadData();
+    }
+
     _urlFilterDidChange(event)
     {
         let searchQuery = this._urlFilterNavigationItem.filterBar.filters.text;

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/ResourceIcons.css (236926 => 236927)


--- trunk/Source/WebInspectorUI/UserInterface/Views/ResourceIcons.css	2018-10-08 17:51:20 UTC (rev 236926)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/ResourceIcons.css	2018-10-08 18:25:52 UTC (rev 236927)
@@ -76,6 +76,10 @@
     content: url(../Images/Beacon.svg);
 }
 
+.resource-icon.resource-type-range .icon {
+    content: url(../Images/Range.svg);
+}
+
 .large .resource-icon .icon {
     content: image-set(url(../Images/DocumentGenericLarge.png) 1x, url(../Images/[email protected]) 2x);
 }
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to