WebKit Bugzilla
Attachment 350168 Details for
Bug 189773
: Web Inspector: create special Network waterfall for media events
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
[Patch] WIP
189773.diff (text/plain), 34.47 KB, created by
Devin Rousso
on 2018-09-19 21:40:59 PDT
(
hide
)
Description:
[Patch] WIP
Filename:
MIME Type:
Creator:
Devin Rousso
Created:
2018-09-19 21:40:59 PDT
Size:
34.47 KB
patch
obsolete
>diff --git a/Source/JavaScriptCore/inspector/protocol/DOM.json b/Source/JavaScriptCore/inspector/protocol/DOM.json >index 810b3c650ef..8e239a8d127 100644 >--- a/Source/JavaScriptCore/inspector/protocol/DOM.json >+++ b/Source/JavaScriptCore/inspector/protocol/DOM.json >@@ -659,6 +659,15 @@ > "parameters": [ > { "name": "nodeId", "$ref": "NodeId" } > ] >+ }, >+ { >+ "name": "willFireEventListeners", >+ "description": "Called right before an event is fired.", >+ "parameters": [ >+ { "name": "nodeId", "$ref": "NodeId" }, >+ { "name": "type", "type": "string" }, >+ { "name": "timestamp", "$ref": "Network.Timestamp", "description": "Time when the event was created" } >+ ] > } > ] > } >diff --git a/Source/WebCore/dom/EventContext.cpp b/Source/WebCore/dom/EventContext.cpp >index b07ba6c55ff..e4cde9fbfaf 100644 >--- a/Source/WebCore/dom/EventContext.cpp >+++ b/Source/WebCore/dom/EventContext.cpp >@@ -30,6 +30,7 @@ > > #include "Document.h" > #include "FocusEvent.h" >+#include "InspectorInstrumentation.h" > #include "MouseEvent.h" > #include "TouchEvent.h" > >@@ -52,8 +53,10 @@ void EventContext::handleLocalEvents(Event& event, EventInvokePhase phase) const > // FIXME: Consider merging handleLocalEvents and fireEventListeners. > if (m_node) > m_node->handleLocalEvents(event, phase); >- else >+ else { >+ InspectorInstrumentation::willFireEventListeners(event, phase); > m_currentTarget->fireEventListeners(event, phase); >+ } > } > > bool EventContext::isMouseOrFocusEventContext() const >diff --git a/Source/WebCore/dom/EventPath.cpp b/Source/WebCore/dom/EventPath.cpp >index b74897a2b59..c1f5850f84d 100644 >--- a/Source/WebCore/dom/EventPath.cpp >+++ b/Source/WebCore/dom/EventPath.cpp >@@ -26,6 +26,7 @@ > #include "EventContext.h" > #include "EventNames.h" > #include "HTMLSlotElement.h" >+#include "InspectorInstrumentation.h" > #include "Node.h" > #include "PseudoElement.h" > #include "ShadowRoot.h" >@@ -49,6 +50,8 @@ void WindowEventContext::handleLocalEvents(Event& event, EventInvokePhase phase) > { > event.setTarget(m_target.get()); > event.setCurrentTarget(m_currentTarget.get()); >+ >+ InspectorInstrumentation::willFireEventListeners(event, phase); > m_currentTarget->fireEventListeners(event, phase); > } > >diff --git a/Source/WebCore/dom/EventTarget.cpp b/Source/WebCore/dom/EventTarget.cpp >index 3b9eec8ac40..51aa3f032f0 100644 >--- a/Source/WebCore/dom/EventTarget.cpp >+++ b/Source/WebCore/dom/EventTarget.cpp >@@ -191,8 +191,13 @@ void EventTarget::dispatchEvent(Event& event) > event.setCurrentTarget(this); > event.setEventPhase(Event::AT_TARGET); > event.resetBeforeDispatch(); >+ >+ InspectorInstrumentation::willFireEventListeners(event, EventInvokePhase::Capturing); > fireEventListeners(event, EventInvokePhase::Capturing); >+ >+ InspectorInstrumentation::willFireEventListeners(event, EventInvokePhase::Bubbling); > fireEventListeners(event, EventInvokePhase::Bubbling); >+ > event.resetAfterDispatch(); > } > >diff --git a/Source/WebCore/dom/Node.cpp b/Source/WebCore/dom/Node.cpp >index 1fbd6705350..7d94c33af72 100644 >--- a/Source/WebCore/dom/Node.cpp >+++ b/Source/WebCore/dom/Node.cpp >@@ -50,6 +50,7 @@ > #include "HTMLStyleElement.h" > #include "InputEvent.h" > #include "InspectorController.h" >+#include "InspectorInstrumentation.h" > #include "KeyboardEvent.h" > #include "Logging.h" > #include "MutationEvent.h" >@@ -2309,6 +2310,8 @@ void Node::notifyMutationObserversNodeWillDetach() > > void Node::handleLocalEvents(Event& event, EventInvokePhase phase) > { >+ InspectorInstrumentation::willFireEventListeners(event, phase); >+ > if (!hasEventTargetData()) > return; > >diff --git a/Source/WebCore/inspector/InspectorInstrumentation.cpp b/Source/WebCore/inspector/InspectorInstrumentation.cpp >index 61a7c739559..709511469e3 100644 >--- a/Source/WebCore/inspector/InspectorInstrumentation.cpp >+++ b/Source/WebCore/inspector/InspectorInstrumentation.cpp >@@ -332,6 +332,12 @@ bool InspectorInstrumentation::isEventListenerDisabledImpl(InstrumentingAgents& > return false; > } > >+void InspectorInstrumentation::willFireEventListenersImpl(InstrumentingAgents& instrumentingAgents, const Event& event, EventTarget::EventInvokePhase phase) >+{ >+ if (auto* domAgent = instrumentingAgents.inspectorDOMAgent()) >+ domAgent->willFireEventListeners(event, phase); >+} >+ > void InspectorInstrumentation::didPostMessageImpl(InstrumentingAgents& instrumentingAgents, const TimerBase& timer, JSC::ExecState& state) > { > if (PageDebuggerAgent* pageDebuggerAgent = instrumentingAgents.pageDebuggerAgent()) >diff --git a/Source/WebCore/inspector/InspectorInstrumentation.h b/Source/WebCore/inspector/InspectorInstrumentation.h >index 926c97d63c3..09c47b93c5e 100644 >--- a/Source/WebCore/inspector/InspectorInstrumentation.h >+++ b/Source/WebCore/inspector/InspectorInstrumentation.h >@@ -150,6 +150,7 @@ public: > static void didAddEventListener(EventTarget&, const AtomicString& eventType, EventListener&, bool capture); > static void willRemoveEventListener(EventTarget&, const AtomicString& eventType, EventListener&, bool capture); > static bool isEventListenerDisabled(EventTarget&, const AtomicString& eventType, EventListener&, bool capture); >+ static void willFireEventListeners(const Event&, EventTarget::EventInvokePhase); > static InspectorInstrumentationCookie willDispatchEvent(Document&, const Event&, bool hasEventListeners); > static void didDispatchEvent(const InspectorInstrumentationCookie&); > static void willHandleEvent(ScriptExecutionContext&, const Event&, const RegisteredEventListener&); >@@ -335,6 +336,7 @@ private: > static void didAddEventListenerImpl(InstrumentingAgents&, EventTarget&, const AtomicString& eventType, EventListener&, bool capture); > static void willRemoveEventListenerImpl(InstrumentingAgents&, EventTarget&, const AtomicString& eventType, EventListener&, bool capture); > static bool isEventListenerDisabledImpl(InstrumentingAgents&, EventTarget&, const AtomicString& eventType, EventListener&, bool capture); >+ static void willFireEventListenersImpl(InstrumentingAgents&, const Event&, EventTarget::EventInvokePhase); > static InspectorInstrumentationCookie willDispatchEventImpl(InstrumentingAgents&, Document&, const Event&, bool hasEventListeners); > static void willHandleEventImpl(InstrumentingAgents&, const Event&, const RegisteredEventListener&); > static void didHandleEventImpl(InstrumentingAgents&); >@@ -713,6 +715,13 @@ inline bool InspectorInstrumentation::isEventListenerDisabled(EventTarget& targe > return false; > } > >+inline void InspectorInstrumentation::willFireEventListeners(const Event& event, EventTarget::EventInvokePhase phase) >+{ >+ FAST_RETURN_IF_NO_FRONTENDS(void()); >+ if (auto* instrumentingAgents = instrumentingAgentsForContext(event.target()->scriptExecutionContext())) >+ willFireEventListenersImpl(*instrumentingAgents, event, phase); >+} >+ > inline void InspectorInstrumentation::didPostMessage(Frame& frame, TimerBase& timer, JSC::ExecState& state) > { > FAST_RETURN_IF_NO_FRONTENDS(void()); >diff --git a/Source/WebCore/inspector/agents/InspectorDOMAgent.cpp b/Source/WebCore/inspector/agents/InspectorDOMAgent.cpp >index 0c7f72ba529..7d83173bd27 100644 >--- a/Source/WebCore/inspector/agents/InspectorDOMAgent.cpp >+++ b/Source/WebCore/inspector/agents/InspectorDOMAgent.cpp >@@ -61,6 +61,7 @@ > #include "FrameTree.h" > #include "HTMLElement.h" > #include "HTMLFrameOwnerElement.h" >+#include "HTMLMediaElement.h" > #include "HTMLNames.h" > #include "HTMLParserIdioms.h" > #include "HTMLScriptElement.h" >@@ -2333,6 +2334,48 @@ bool InspectorDOMAgent::isEventListenerDisabled(EventTarget& target, const Atomi > return false; > } > >+void InspectorDOMAgent::willFireEventListeners(const Event& event, EventTarget::EventInvokePhase phase) >+{ >+ if (phase != EventTarget::EventInvokePhase::Capturing) >+ return; >+ >+ if (!is<Node>(event.currentTarget())) >+ return; >+ >+ auto node = downcast<Node>(event.currentTarget()); >+ if (!is<HTMLMediaElement>(node)) >+ return; >+ >+ if (event.type() != eventNames().abortEvent >+ && event.type() != eventNames().canplayEvent >+ && event.type() != eventNames().canplaythroughEvent >+ && event.type() != eventNames().durationchangeEvent >+ && event.type() != eventNames().emptiedEvent >+ && event.type() != eventNames().endedEvent >+ && event.type() != eventNames().errorEvent >+ && event.type() != eventNames().loadeddataEvent >+ && event.type() != eventNames().loadedmetadataEvent >+ && event.type() != eventNames().loadstartEvent >+ && event.type() != eventNames().pauseEvent >+ && event.type() != eventNames().playEvent >+ && event.type() != eventNames().playingEvent >+ && event.type() != eventNames().ratechangeEvent >+ && event.type() != eventNames().seekedEvent >+ && event.type() != eventNames().seekingEvent >+ && event.type() != eventNames().stalledEvent >+ && event.type() != eventNames().suspendEvent >+ && event.type() != eventNames().timeupdateEvent >+ && event.type() != eventNames().volumechangeEvent >+ && event.type() != eventNames().waitingEvent) >+ return; >+ >+ int nodeId = boundNodeId(node); >+ if (!nodeId) >+ return; >+ >+ m_frontendDispatcher->willFireEventListeners(nodeId, event.type(), timestamp()); >+} >+ > bool InspectorDOMAgent::hasBreakpointForEventListener(EventTarget& target, const AtomicString& eventType, EventListener& listener, bool capture) > { > for (auto& inspectorEventListener : m_eventListenerEntries.values()) { >@@ -2455,4 +2498,9 @@ JSC::JSValue InspectorDOMAgent::nodeAsScriptValue(JSC::ExecState& state, Node* n > return toJS(&state, deprecatedGlobalObjectForPrototype(&state), BindingSecurity::checkSecurityForNode(state, node)); > } > >+double InspectorDOMAgent::timestamp() >+{ >+ return m_environment.executionStopwatch()->elapsedTime().seconds(); >+} >+ > } // namespace WebCore >diff --git a/Source/WebCore/inspector/agents/InspectorDOMAgent.h b/Source/WebCore/inspector/agents/InspectorDOMAgent.h >index cd1f5a1ebb3..bb25ab0c653 100644 >--- a/Source/WebCore/inspector/agents/InspectorDOMAgent.h >+++ b/Source/WebCore/inspector/agents/InspectorDOMAgent.h >@@ -176,6 +176,7 @@ public: > void didAddEventListener(EventTarget&); > void willRemoveEventListener(EventTarget&, const AtomicString& eventType, EventListener&, bool capture); > bool isEventListenerDisabled(EventTarget&, const AtomicString& eventType, EventListener&, bool capture); >+ void willFireEventListeners(const Event&, EventTarget::EventInvokePhase); > > // Callbacks that don't directly correspond to an instrumentation entry point. > void setDocument(Document*); >@@ -251,6 +252,8 @@ private: > > void innerHighlightQuad(std::unique_ptr<FloatQuad>, const JSON::Object* color, const JSON::Object* outlineColor, const bool* usePageCoordinates); > >+ double timestamp(); >+ > Inspector::InjectedScriptManager& m_injectedScriptManager; > std::unique_ptr<Inspector::DOMFrontendDispatcher> m_frontendDispatcher; > RefPtr<Inspector::DOMBackendDispatcher> m_backendDispatcher; >diff --git a/Source/WebCore/page/DOMWindow.cpp b/Source/WebCore/page/DOMWindow.cpp >index 90a88d36e13..6344c5a895e 100644 >--- a/Source/WebCore/page/DOMWindow.cpp >+++ b/Source/WebCore/page/DOMWindow.cpp >@@ -2053,8 +2053,13 @@ void DOMWindow::dispatchEvent(Event& event, EventTarget* target) > event.resetBeforeDispatch(); > auto cookie = InspectorInstrumentation::willDispatchEventOnWindow(frame(), event, *this); > // FIXME: We should use EventDispatcher everywhere. >+ >+ InspectorInstrumentation::willFireEventListeners(event, EventInvokePhase::Capturing); > fireEventListeners(event, EventInvokePhase::Capturing); >+ >+ InspectorInstrumentation::willFireEventListeners(event, EventInvokePhase::Bubbling); > fireEventListeners(event, EventInvokePhase::Bubbling); >+ > InspectorInstrumentation::didDispatchEventOnWindow(cookie); > event.resetAfterDispatch(); > } >diff --git a/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js b/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js >index 6431de7a965..825070b04f2 100644 >--- a/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js >+++ b/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js >@@ -983,6 +983,8 @@ localizedStrings["This object is referenced by internal objects"] = "This object > localizedStrings["This property needs a value.\nClick to open autocomplete."] = "This property needs a value.\nClick to open autocomplete."; > localizedStrings["This text resource could benefit from compression"] = "This text resource could benefit from compression"; > localizedStrings["Time"] = "Time"; >+localizedStrings["Time (Absolute)"] = "Time (Absolute)"; >+localizedStrings["Time (Relative)"] = "Time (Relative)"; > localizedStrings["Time to First Byte"] = "Time to First Byte"; > localizedStrings["Timeline"] = "Timeline"; > localizedStrings["Timeline Recording %d"] = "Timeline Recording %d"; >@@ -1102,6 +1104,7 @@ localizedStrings["toggle"] = "toggle"; > localizedStrings["unsupported version."] = "unsupported version."; > localizedStrings["value"] = "value"; > localizedStrings["â%sâ Event Fired"] = "â%sâ Event Fired"; >+localizedStrings["â%sâ Event Fired"] = "â%sâ Event Fired"; > localizedStrings["â%sâ Profile Recorded"] = "â%sâ Profile Recorded"; > localizedStrings["â%sâ is invalid."] = "â%sâ is invalid."; > localizedStrings["â%sâ threw an error."] = "â%sâ threw an error."; >diff --git a/Source/WebInspectorUI/UserInterface/Controllers/DOMTreeManager.js b/Source/WebInspectorUI/UserInterface/Controllers/DOMTreeManager.js >index 72b484cf5ca..f0750c153a8 100644 >--- a/Source/WebInspectorUI/UserInterface/Controllers/DOMTreeManager.js >+++ b/Source/WebInspectorUI/UserInterface/Controllers/DOMTreeManager.js >@@ -122,6 +122,17 @@ WI.DOMTreeManager = class DOMTreeManager extends WI.Object > node.dispatchEventToListeners(WI.DOMNode.Event.EventListenersChanged); > } > >+ willFireEventListeners(nodeId, type, timestamp) >+ { >+ // Called from WI.DOMObserver. >+ >+ let node = this._idToDOMNode[nodeId]; >+ if (!node) >+ return; >+ >+ node.willFireEventListeners(type, timestamp); >+ } >+ > // Private > > _wrapClientCallback(callback) >diff --git a/Source/WebInspectorUI/UserInterface/Main.html b/Source/WebInspectorUI/UserInterface/Main.html >index e56ce4a050b..002db6e9973 100644 >--- a/Source/WebInspectorUI/UserInterface/Main.html >+++ b/Source/WebInspectorUI/UserInterface/Main.html >@@ -130,6 +130,7 @@ > <link rel="stylesheet" href="Views/NetworkTimelineOverviewGraph.css"> > <link rel="stylesheet" href="Views/NetworkTimelineView.css"> > <link rel="stylesheet" href="Views/NewTabContentView.css"> >+ <link rel="stylesheet" href="Views/NodeEventsBreakdownView.css"> > <link rel="stylesheet" href="Views/ObjectPreviewView.css"> > <link rel="stylesheet" href="Views/ObjectTreeArrayIndexTreeElement.css"> > <link rel="stylesheet" href="Views/ObjectTreeMapEntryTreeElement.css"> >@@ -688,6 +689,7 @@ > <script src="Views/NetworkTableContentView.js"></script> > <script src="Views/NetworkTimelineOverviewGraph.js"></script> > <script src="Views/NetworkTimelineView.js"></script> >+ <script src="Views/NodeEventsBreakdownView.js"></script> > <script src="Views/ObjectPreviewView.js"></script> > <script src="Views/ObjectPropertiesDetailSectionRow.js"></script> > <script src="Views/ObjectTreeArrayIndexTreeElement.js"></script> >diff --git a/Source/WebInspectorUI/UserInterface/Models/DOMNode.js b/Source/WebInspectorUI/UserInterface/Models/DOMNode.js >index a56aaddc9c6..ecd41ad923e 100644 >--- a/Source/WebInspectorUI/UserInterface/Models/DOMNode.js >+++ b/Source/WebInspectorUI/UserInterface/Models/DOMNode.js >@@ -137,10 +137,14 @@ WI.DOMNode = class DOMNode extends WI.Object > this.name = payload.name; > this.value = payload.value; > } >+ >+ this._events = []; > } > > // Public > >+ get events() { return this._events; } >+ > get frameIdentifier() > { > return this._frameIdentifier || this.ownerDocument.frameIdentifier; >@@ -697,6 +701,18 @@ WI.DOMNode = class DOMNode extends WI.Object > return !!this.ownerSVGElement; > } > >+ willFireEventListeners(type, timestamp) >+ { >+ // Called from WI.DOMTreeManager. >+ >+ let data = { >+ type, >+ timestamp: WI.timelineManager.computeElapsedTime(timestamp), >+ }; >+ this._events.push(data); >+ this.dispatchEventToListeners(WI.DOMNode.Event.WillFireEventListeners, data); >+ } >+ > _setAttributesPayload(attrs) > { > this._attributes = []; >@@ -844,6 +860,7 @@ WI.DOMNode.Event = { > AttributeModified: "dom-node-attribute-modified", > AttributeRemoved: "dom-node-attribute-removed", > EventListenersChanged: "dom-node-event-listeners-changed", >+ WillFireEventListeners: "dom-node-will-fire-event-listeners", > }; > > WI.DOMNode.PseudoElementType = { >diff --git a/Source/WebInspectorUI/UserInterface/Protocol/DOMObserver.js b/Source/WebInspectorUI/UserInterface/Protocol/DOMObserver.js >index 06c786d5deb..8e4c9178bfa 100644 >--- a/Source/WebInspectorUI/UserInterface/Protocol/DOMObserver.js >+++ b/Source/WebInspectorUI/UserInterface/Protocol/DOMObserver.js >@@ -111,4 +111,9 @@ WI.DOMObserver = class DOMObserver > { > WI.domTreeManager.willRemoveEventListener(nodeId); > } >+ >+ willFireEventListeners(nodeId, type, timestamp) >+ { >+ WI.domTreeManager.willFireEventListeners(nodeId, type, timestamp); >+ } > }; >diff --git a/Source/WebInspectorUI/UserInterface/Views/NetworkTableContentView.css b/Source/WebInspectorUI/UserInterface/Views/NetworkTableContentView.css >index 9fe337f256e..4903dc3695f 100644 >--- a/Source/WebInspectorUI/UserInterface/Views/NetworkTableContentView.css >+++ b/Source/WebInspectorUI/UserInterface/Views/NetworkTableContentView.css >@@ -150,6 +150,17 @@ body[dir=rtl] .network-table .cell.name > .status { > bottom: 0; > } > >+.network-table :not(.header) .cell.waterfall .waterfall-container > .event { >+ position: absolute; >+ top: calc(50% - (var(--node-waterfall-event-size) / 2)); >+ width: var(--node-waterfall-event-size); >+ height: var(--node-waterfall-event-size); >+ background-color: var(--selected-background-color); >+ border-radius: 50%; >+ >+ --node-waterfall-event-size: 8px; >+} >+ > .network-table .timeline-ruler { > position: absolute; > top: 0; >@@ -210,3 +221,7 @@ body[dir=rtl] .network-table .cell.name > .status { > .waterfall .block.response { > background-color: var(--network-response-color); > } >+ >+.popover.waterfall-popover { >+ --popover-background-color: white; >+} >diff --git a/Source/WebInspectorUI/UserInterface/Views/NetworkTableContentView.js b/Source/WebInspectorUI/UserInterface/Views/NetworkTableContentView.js >index 675181d14a7..ace80f82e19 100644 >--- a/Source/WebInspectorUI/UserInterface/Views/NetworkTableContentView.js >+++ b/Source/WebInspectorUI/UserInterface/Views/NetworkTableContentView.js >@@ -638,9 +638,30 @@ WI.NetworkTableContentView = class NetworkTableContentView extends WI.ContentVie > { > cell.removeChildren(); > >+ let container = cell.appendChild(document.createElement("div")); >+ container.className = "waterfall-container"; >+ >+ let graphStartTime = this._waterfallTimelineRuler.startTime; >+ let secondsPerPixel = this._waterfallTimelineRuler.secondsPerPixel; >+ >+ function positionByStartOffset(element, timestamp) { >+ let styleAttribute = WI.resolvedLayoutDirection() === WI.LayoutDirection.LTR ? "left" : "right"; >+ element.style.setProperty(styleAttribute, ((timestamp - graphStartTime) / secondsPerPixel) + "px"); >+ } >+ > let node = entry.node; > if (node) { >- // FIXME: create special timelines for node entries >+ for (let {type, timestamp} of node.events) { >+ let eventElement = container.appendChild(document.createElement("div")); >+ eventElement.classList.add("event", type); >+ eventElement.title = WI.UIString("â%sâ Event Fired").format(type); >+ positionByStartOffset(eventElement, timestamp); >+ eventElement.addEventListener("mousedown", (event) => { >+ if (event.button !== 0 || event.ctrlKey) >+ return; >+ this._handleNodeEntryMousedownWaterfall(eventElement, entry, timestamp); >+ }); >+ } > return; > } > >@@ -656,7 +677,6 @@ WI.NetworkTableContentView = class NetworkTableContentView extends WI.ContentVie > return; > } > >- let graphStartTime = this._waterfallTimelineRuler.startTime; > if (responseEnd < graphStartTime) { > cell.textContent = zeroWidthSpace; > return; >@@ -668,18 +688,11 @@ WI.NetworkTableContentView = class NetworkTableContentView extends WI.ContentVie > return; > } > >- let secondsPerPixel = this._waterfallTimelineRuler.secondsPerPixel; >- >- let container = cell.appendChild(document.createElement("div")); >- container.className = "waterfall-container"; >- > function appendBlock(startTime, endTime, className) { >- let startOffset = (startTime - graphStartTime) / secondsPerPixel; > let width = (endTime - startTime) / secondsPerPixel; > let block = container.appendChild(document.createElement("div")); > block.classList.add("block", className); >- let styleAttribute = WI.resolvedLayoutDirection() === WI.LayoutDirection.LTR ? "left" : "right"; >- block.style[styleAttribute] = startOffset + "px"; >+ positionByStartOffset(block, startTime); > block.style.width = width + "px"; > return block; > } >@@ -690,7 +703,7 @@ WI.NetworkTableContentView = class NetworkTableContentView extends WI.ContentVie > mouseBlock.addEventListener("mousedown", (event) => { > if (event.button !== 0 || event.ctrlKey) > return; >- this._handleMousedownWaterfall(mouseBlock, entry, event); >+ this._handleResourceEntryMousedownWaterfall(mouseBlock, entry); > }); > > // Super small visualization. >@@ -1037,8 +1050,10 @@ WI.NetworkTableContentView = class NetworkTableContentView extends WI.ContentVie > } > this._pendingInsertions = []; > >- for (let resource of this._pendingUpdates) >- this._updateEntryForResource(resource); >+ for (let updateObject of this._pendingUpdates) { >+ if (updateObject instanceof WI.Resource) >+ this._updateEntryForResource(updateObject); >+ } > this._pendingUpdates = []; > > this._pendingFilter = false; >@@ -1423,6 +1438,8 @@ WI.NetworkTableContentView = class NetworkTableContentView extends WI.ContentVie > if (!nodeEntry) { > nodeEntry = this._entryForNode(resource.initiatorNode, Object.keys(resourceEntry)); > this._nodeEntries.set(resource.initiatorNode, nodeEntry); >+ >+ resource.initiatorNode.addEventListener(WI.DOMNode.Event.WillFireEventListeners, this._handleNodeWillFireEventListeners, this); > } > > if (!this._entriesSortComparator) >@@ -1445,6 +1462,17 @@ WI.NetworkTableContentView = class NetworkTableContentView extends WI.ContentVie > }, new Set); > } > >+ _handleNodeWillFireEventListeners(event) >+ { >+ let {timestamp} = event.data; >+ this._pendingUpdates.push(event.target); >+ >+ if (timestamp > this._waterfallEndTime) >+ this._waterfallEndTime = timestamp + (this._waterfallTimelineRuler.secondsPerPixel * 10); >+ >+ this.needsLayout(); >+ } >+ > _hasTypeFilter() > { > return !!this._activeTypeFilters; >@@ -1683,11 +1711,18 @@ WI.NetworkTableContentView = class NetworkTableContentView extends WI.ContentVie > }).catch(handlePromiseException); > } > >- _waterfallPopoverContentForResource(resource) >+ _waterfallPopoverContent() > { > let contentElement = document.createElement("div"); >- contentElement.className = "waterfall-popover-content"; >+ contentElement.classList.add("waterfall-popover-content"); >+ return contentElement; >+ } > >+ _waterfallPopoverContentForResourceEntry(resourceEntry) >+ { >+ let contentElement = this._waterfallPopoverContent(); >+ >+ let resource = resourceEntry.resource; > if (!resource.hasResponse() || !resource.timingData.startTime || !resource.timingData.responseEnd) { > contentElement.textContent = WI.UIString("Resource has no timing data"); > return contentElement; >@@ -1700,7 +1735,36 @@ WI.NetworkTableContentView = class NetworkTableContentView extends WI.ContentVie > return contentElement; > } > >- _handleMousedownWaterfall(mouseBlock, entry, event) >+ _waterfallPopoverContentForNodeEntry(nodeEntry, timestamp) >+ { >+ let contentElement = this._waterfallPopoverContent(); >+ >+ let rangeToHighlight = [ >+ timestamp - (this._waterfallTimelineRuler.secondsPerPixel * 5), >+ timestamp + (this._waterfallTimelineRuler.secondsPerPixel * 5), >+ ]; >+ >+ let initiatedResources = nodeEntry.initiatedResourceEntries.map((entry) => entry.resource); >+ let breakdownView = new WI.NodeEventsBreakdownView(nodeEntry.node, initiatedResources, this._waterfallStartTime, {rangeToHighlight}); >+ contentElement.appendChild(breakdownView.element); >+ breakdownView.updateLayout(); >+ >+ return contentElement; >+ } >+ >+ _handleResourceEntryMousedownWaterfall(targetElement, resourceEntry) >+ { >+ let popoverContentElement = this._waterfallPopoverContentForResourceEntry(resourceEntry); >+ this._handleMousedownWaterfall(targetElement, popoverContentElement); >+ } >+ >+ _handleNodeEntryMousedownWaterfall(targetElement, nodeEntry, timestamp) >+ { >+ let popoverContentElement = this._waterfallPopoverContentForNodeEntry(nodeEntry, timestamp); >+ this._handleMousedownWaterfall(targetElement, popoverContentElement); >+ } >+ >+ _handleMousedownWaterfall(targetElement, popoverContentElement) > { > if (!this._waterfallPopover) { > this._waterfallPopover = new WI.Popover; >@@ -1711,20 +1775,7 @@ WI.NetworkTableContentView = class NetworkTableContentView extends WI.ContentVie > return; > > let calculateTargetFrame = () => { >- let rowIndex = this._rowIndexForResource(entry.resource); >- let cell = this._table.cellForRowAndColumn(rowIndex, this._waterfallColumn); >- if (!cell) { >- this._waterfallPopover.dismiss(); >- return null; >- } >- >- let mouseBlock = cell.querySelector(".block.mouse-tracking"); >- if (!mouseBlock) { >- this._waterfallPopover.dismiss(); >- return null; >- } >- >- return WI.Rect.rectFromClientRect(mouseBlock.getBoundingClientRect()); >+ return WI.Rect.rectFromClientRect(targetElement.getBoundingClientRect()); > }; > > let targetFrame = calculateTargetFrame(); >@@ -1741,7 +1792,6 @@ WI.NetworkTableContentView = class NetworkTableContentView extends WI.ContentVie > this._waterfallPopover.present(bounds, preferredEdges); > }; > >- let popoverContentElement = this._waterfallPopoverContentForResource(entry.resource); > this._waterfallPopover.presentNewContentWithFrame(popoverContentElement, targetFrame, preferredEdges); > } > >diff --git a/Source/WebInspectorUI/UserInterface/Views/NodeEventsBreakdownView.css b/Source/WebInspectorUI/UserInterface/Views/NodeEventsBreakdownView.css >new file mode 100644 >index 00000000000..d56067e1b28 >--- /dev/null >+++ b/Source/WebInspectorUI/UserInterface/Views/NodeEventsBreakdownView.css >@@ -0,0 +1,52 @@ >+/* >+ * Copyright (C) 2018 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' >+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, >+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS >+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR >+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF >+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS >+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN >+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) >+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF >+ * THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+.waterfall-popover-content .node-events-breakdown { >+ margin: 5px; >+ -webkit-user-select: text; >+} >+ >+.waterfall-popover-content .node-events-breakdown table { >+ width: 100%; >+ border-collapse: collapse; >+} >+ >+.waterfall-popover-content .node-events-breakdown tr.highlight { >+ color: white; >+ background-color: var(--selected-background-color); >+} >+ >+.waterfall-popover-content .node-events-breakdown tr:first-child { >+ border-bottom: 1px solid var(--border-color); >+} >+ >+.waterfall-popover-content .node-events-breakdown tr > :matches(th, td) { >+ padding: 2px 4px; >+ text-align: start; >+} >+ >+.waterfall-popover-content .node-events-breakdown td.time { >+ text-align: end; >+} >diff --git a/Source/WebInspectorUI/UserInterface/Views/NodeEventsBreakdownView.js b/Source/WebInspectorUI/UserInterface/Views/NodeEventsBreakdownView.js >new file mode 100644 >index 00000000000..68462c42183 >--- /dev/null >+++ b/Source/WebInspectorUI/UserInterface/Views/NodeEventsBreakdownView.js >@@ -0,0 +1,91 @@ >+/* >+ * Copyright (C) 2018 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' >+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, >+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS >+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR >+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF >+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS >+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN >+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) >+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF >+ * THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+WI.NodeEventsBreakdownView = class NodeEventsBreakdownView extends WI.View >+{ >+ constructor(node, initiatedResources, startTime, {rangeToHighlight}) >+ { >+ super(); >+ >+ console.assert(node instanceof WI.DOMNode); >+ console.assert(node.events.length, "Node should have at least one event."); >+ >+ this._node = node; >+ this._initiatedResources = initiatedResources; >+ this._startTime = startTime; >+ this._rangeToHighlight = rangeToHighlight || []; >+ >+ this.element.classList.add("node-events-breakdown"); >+ } >+ >+ // Protected >+ >+ initialLayout() >+ { >+ super.initialLayout(); >+ >+ let firstResourceTimestamp = this._initiatedResources.reduce((min, item) => Math.min(min, item.firstTimestamp), this._initiatedResources[0].firstTimestamp); >+ >+ let tableElement = this.element.appendChild(document.createElement("table")); >+ >+ let headerElement = tableElement.appendChild(document.createElement("tr")); >+ >+ let eventHeader = headerElement.appendChild(document.createElement("th")); >+ eventHeader.textContent = WI.UIString("Event"); >+ >+ let relativeTimeHeader = headerElement.appendChild(document.createElement("th")); >+ relativeTimeHeader.classList.add("time"); >+ relativeTimeHeader.textContent = WI.UIString("Time (Relative)"); >+ >+ let absoluteTimeHeader = headerElement.appendChild(document.createElement("th")); >+ absoluteTimeHeader.classList.add("time"); >+ absoluteTimeHeader.textContent = WI.UIString("Time (Absolute)"); >+ >+ for (let {type, timestamp} of this._node.events) { >+ let rowElement = tableElement.appendChild(document.createElement("tr")); >+ if (timestamp >= this._rangeToHighlight[0] && timestamp <= this._rangeToHighlight[1]) >+ rowElement.classList.add("highlight"); >+ >+ let eventTypeCell = rowElement.appendChild(document.createElement("td")); >+ eventTypeCell.textContent = type; >+ >+ rowElement.appendChild(this._createTimeCell(timestamp - firstResourceTimestamp)); >+ rowElement.appendChild(this._createTimeCell(timestamp - this._startTime)); >+ } >+ } >+ >+ // Private >+ >+ _createTimeCell(timestamp) >+ { >+ let cell = document.createElement("td"); >+ cell.classList.add("time"); >+ >+ const higherResolution = true; >+ cell.textContent = Number.secondsToString(timestamp, higherResolution); >+ >+ return cell; >+ } >+}; >diff --git a/Source/WebInspectorUI/UserInterface/Views/ResourceTimingBreakdownView.css b/Source/WebInspectorUI/UserInterface/Views/ResourceTimingBreakdownView.css >index 4ab440ef1ed..959dbaed57f 100644 >--- a/Source/WebInspectorUI/UserInterface/Views/ResourceTimingBreakdownView.css >+++ b/Source/WebInspectorUI/UserInterface/Views/ResourceTimingBreakdownView.css >@@ -23,10 +23,6 @@ > * THE POSSIBILITY OF SUCH DAMAGE. > */ > >-.popover.waterfall-popover { >- --popover-background-color: white; >-} >- > .waterfall-popover-content .resource-timing-breakdown { > margin: 5px; > -webkit-user-select: text;
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 189773
:
350168
|
350793
|
350795
|
350797
|
350798
|
351811
|
351856
|
352002
|
352006