https://bugs.webkit.org/show_bug.cgi?id=189874
<rdar://problem/
44700000>
Reviewed by Joseph Pecoraro.
Source/JavaScriptCore:
* inspector/protocol/DOM.json:
Allow `data` to be passed to the frontend with `didFireEvent`.
Source/WebCore:
Updated existing test: http/tests/inspector/dom/didFireEvent.html
* inspector/agents/InspectorDOMAgent.h:
* inspector/agents/InspectorDOMAgent.cpp:
(WebCore::EventFiredCallback::handleEvent):
(WebCore::InspectorDOMAgent::didCreateFrontendAndBackend):
(WebCore::InspectorDOMAgent::addEventListenersToNode):
(WebCore::InspectorDOMAgent::discardBindings):
(WebCore::InspectorDOMAgent::eventDidResetAfterDispatch): Added.
Prevent the same event from being sent to the frontend more than once.
* dom/Event.cpp:
(WebCore::Event::resetAfterDispatch):
* dom/Document.cpp:
(WebCore::Document::Document):
* inspector/InspectorInstrumentation.h:
(WebCore::InspectorInstrumentation::eventDidResetAfterDispatch): Added.
* inspector/InspectorInstrumentation.cpp:
(WebCore::InspectorInstrumentation::eventDidResetAfterDispatchImpl): Added.
Source/WebInspectorUI:
* Localizations/en.lproj/localizedStrings.js:
* UserInterface/Protocol/DOMObserver.js:
(WI.DOMObserver.prototype.didFireEvent):
* UserInterface/Controllers/DOMManager.js:
(WI.DOMManager.prototype.didFireEvent):
Allow `data` to be passed to the frontend with `didFireEvent`.
* UserInterface/Models/DOMNode.js:
(WI.DOMNode):
(WI.DOMNode.getFullscreenDOMEvents): Added.
(WI.DOMNode.prototype.didFireEvent):
(WI.DOMNode.prototype._handleDOMNodeDidFireEvent): Added.
(WI.DOMNode.prototype._addDOMEvent):
(WI.DOMNode.prototype._shouldListenForEventListeners): Added.
If an event is fired on an ancestor of this node, also record that event in this node's
`domEvents`, including the `originator` node.
* UserInterface/Views/NetworkTableContentView.js:
(WI.NetworkTableContentView.prototype._populateWaterfallGraph):
* UserInterface/Views/NetworkTableContentView.css:
(.network-table :not(.header) .cell.waterfall .waterfall-container > .dom-fullscreen): Added.
* UserInterface/Views/DOMEventsBreakdownView.js:
(WI.DOMEventsBreakdownView.prototype.initialLayout):
(WI.DOMEventsBreakdownView.prototype._populateTable):
* UserInterface/Views/DOMEventsBreakdownView.css:
(.dom-events-breakdown .graph > .area.fullscreen): Added.
(.dom-events-breakdown .inherited > .name, .dom-events-breakdown .inherited > .graph > .point): Added.
(.dom-events-breakdown:not(.has-inherited) .originator): Added.
LayoutTests:
* http/tests/inspector/dom/didFireEvent-expected.txt:
* http/tests/inspector/dom/didFireEvent.html:
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@237431
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
+2018-10-25 Devin Rousso <drousso@apple.com>
+
+ Web Inspector: display fullscreen enter/exit events in Timelines and Network node waterfalls
+ https://bugs.webkit.org/show_bug.cgi?id=189874
+ <rdar://problem/44700000>
+
+ Reviewed by Joseph Pecoraro.
+
+ * http/tests/inspector/dom/didFireEvent-expected.txt:
+ * http/tests/inspector/dom/didFireEvent.html:
+
2018-10-25 Alexey Proskuryakov <ap@apple.com>
https://bugs.webkit.org/show_bug.cgi?id=175597
== Running test suite: DOM.didFireEvent
--- Running test case: DOM.didFireEvent
+-- Running test case: DOM.didFireEvent.Basic
Adding video source "resources/white.mp4"...
-PASS: Should recieve a "loadstart" event.
+PASS: Should receive a "loadstart" event.
PASS: Event timestamp should be greater than 0.
+-- Running test case: DOM.didFireEvent.Fullscreen
+Entering fullscreen on #video...
+PASS: Should receive a "webkitfullscreenchange" event.
+PASS: Event timestamp should be greater than 0.
+PASS: Event should have data.
+PASS: Fullscreen should be true.
+Target: video#video
+Exiting fullscreen...
+PASS: Should receive a "webkitfullscreenchange" event.
+PASS: Event timestamp should be greater than 0.
+PASS: Event should have data.
+PASS: Fullscreen should be false.
+Target: video#video
+
+-- Running test case: DOM.didFireEvent.Inherited
+Entering fullscreen on #container...
+PASS: Should receive a "webkitfullscreenchange" event.
+PASS: Event timestamp should be greater than 0.
+PASS: Event should have data.
+PASS: Fullscreen should be true.
+Target: video#video
+Originator: div#container
+Exiting fullscreen...
+PASS: Should receive a "webkitfullscreenchange" event.
+PASS: Event timestamp should be greater than 0.
+PASS: Event should have data.
+PASS: Fullscreen should be false.
+Target: video#video
+Originator: div#container
+
document.getElementById("video").appendChild(sourceElement);
}
+function enterFullscreen(element) {
+ document.addEventListener("keydown", (event) => {
+ document.addEventListener("webkitfullscreenchange", (event) => {
+ console.assert(document.webkitFullscreenElement === element);
+
+ TestPage.dispatchEventToFrontend("TestPage-enteredFullscreen");
+ }, {once: true});
+
+ element.webkitRequestFullscreen();
+ }, {once: true});
+
+ if (window.testRunner) {
+ // DumpRenderTree changes the firstResponder to the WebInspector window when it opens.
+ // This refocuses the test page, ensuring it gets the event.
+ if (window.testRunner.setMainFrameIsFirstResponder)
+ window.testRunner.setMainFrameIsFirstResponder(true);
+
+ eventSender.keyDown(" ");
+ }
+}
+
+function exitFullscreen() {
+ document.addEventListener("webkitfullscreenchange", (event) => {
+ console.assert(!document.webkitFullscreenElement);
+
+ TestPage.dispatchEventToFrontend("TestPage-exitedFullscreen");
+ }, {once: true});
+
+ document.webkitExitFullscreen();
+}
+
function test()
{
+ InspectorTest.debug();
+
let suite = InspectorTest.createAsyncSuite("DOM.didFireEvent");
let videoNode = null;
+ function fullscreenTest(fullscreenElementId, resolve, reject) {
+ InspectorTest.awaitEvent("TestPage-exitedFullscreen")
+ .then(resolve, reject);
+
+ InspectorTest.awaitEvent("TestPage-enteredFullscreen")
+ .then((event) => {
+ InspectorTest.log("Exiting fullscreen...");
+ InspectorTest.evaluateInPage(`exitFullscreen()`).catch(reject);
+ });
+
+ let enabled = false;
+ let listener = videoNode.addEventListener(WI.DOMNode.Event.DidFireEvent, (event) => {
+ let {domEvent} = event.data;
+ if (domEvent.eventName !== "webkitfullscreenchange")
+ return;
+
+ InspectorTest.pass(`Should receive a "webkitfullscreenchange" event.`);
+ InspectorTest.expectGreaterThan(domEvent.timestamp, 0, "Event timestamp should be greater than 0.");
+ InspectorTest.expectThat(domEvent.data, "Event should have data.");
+ InspectorTest.expectNotEqual(domEvent.data.enabled, enabled, `Fullscreen should be ${!enabled}.`);
+ InspectorTest.log("Target: " + event.target.displayName);
+ if (domEvent.originator)
+ InspectorTest.log("Originator: " + domEvent.originator.displayName);
+
+ enabled = domEvent.data.enabled;
+ if (!enabled)
+ videoNode.removeEventListener(WI.DOMNode.Event.DidFireEvent, listener);
+ });
+
+ InspectorTest.log(`Entering fullscreen on #${fullscreenElementId}...`);
+ InspectorTest.evaluateInPage(`enterFullscreen(document.getElementById("${fullscreenElementId}"))`).catch(reject);
+ }
+
suite.addTestCase({
- name: "DOM.didFireEvent",
+ name: "DOM.didFireEvent.Basic",
description: "Check that HTMLMediaElement events work.",
test(resolve, reject) {
const file = "white.mp4";
if (domEvent.eventName !== "loadstart")
return;
- InspectorTest.pass(`Should recieve a "loadstart" event.`)
+ InspectorTest.pass(`Should receive a "loadstart" event.`)
InspectorTest.expectGreaterThan(domEvent.timestamp, 0, "Event timestamp should be greater than 0.");
videoNode.removeEventListener(WI.DOMNode.Event.DidFireEvent, listener);
}
});
+ suite.addTestCase({
+ name: "DOM.didFireEvent.Fullscreen",
+ description: "Check that fullscreen events work.",
+ test(resolve, reject) {
+ fullscreenTest("video", resolve, reject);
+ }
+ });
+
+ suite.addTestCase({
+ name: "DOM.didFireEvent.Inherited",
+ description: "Check that inherited events work.",
+ test(resolve, reject) {
+ fullscreenTest("container", resolve, reject);
+ }
+ });
+
WI.domManager.requestDocument((documentNode) => {
WI.domManager.querySelector(documentNode.id, "#video", (videoNodeId) => {
videoNode = WI.domManager.nodeForId(videoNodeId);
</head>
<body onload="runTest()">
<p>Tests that listeners registered by InspectorDOMAgent::addEventListenersToNode are working.</p>
- <video id="video" muted autoplay></video>
+ <div id="container">
+ <video id="video" muted autoplay></video>
+ </div>
</body>
</html>
+2018-10-25 Devin Rousso <drousso@apple.com>
+
+ Web Inspector: display fullscreen enter/exit events in Timelines and Network node waterfalls
+ https://bugs.webkit.org/show_bug.cgi?id=189874
+ <rdar://problem/44700000>
+
+ Reviewed by Joseph Pecoraro.
+
+ * inspector/protocol/DOM.json:
+ Allow `data` to be passed to the frontend with `didFireEvent`.
+
2018-10-25 Ross Kirsling <ross.kirsling@sony.com>
Cleanup: inline constexpr is redundant as constexpr implies inline
"parameters": [
{ "name": "nodeId", "$ref": "NodeId" },
{ "name": "eventName", "type": "string" },
- { "name": "timestamp", "$ref": "Network.Timestamp", "description": "Time when the event was fired" }
+ { "name": "timestamp", "$ref": "Network.Timestamp", "description": "Time when the event was fired" },
+ { "name": "data", "type": "object", "optional": true, "description": "Holds ancillary information about the event or its target." }
]
}
]
+2018-10-25 Devin Rousso <drousso@apple.com>
+
+ Web Inspector: display fullscreen enter/exit events in Timelines and Network node waterfalls
+ https://bugs.webkit.org/show_bug.cgi?id=189874
+ <rdar://problem/44700000>
+
+ Reviewed by Joseph Pecoraro.
+
+ Updated existing test: http/tests/inspector/dom/didFireEvent.html
+
+ * inspector/agents/InspectorDOMAgent.h:
+ * inspector/agents/InspectorDOMAgent.cpp:
+ (WebCore::EventFiredCallback::handleEvent):
+ (WebCore::InspectorDOMAgent::didCreateFrontendAndBackend):
+ (WebCore::InspectorDOMAgent::addEventListenersToNode):
+ (WebCore::InspectorDOMAgent::discardBindings):
+ (WebCore::InspectorDOMAgent::eventDidResetAfterDispatch): Added.
+ Prevent the same event from being sent to the frontend more than once.
+
+ * dom/Event.cpp:
+ (WebCore::Event::resetAfterDispatch):
+
+ * dom/Document.cpp:
+ (WebCore::Document::Document):
+
+ * inspector/InspectorInstrumentation.h:
+ (WebCore::InspectorInstrumentation::eventDidResetAfterDispatch): Added.
+ * inspector/InspectorInstrumentation.cpp:
+ (WebCore::InspectorInstrumentation::eventDidResetAfterDispatchImpl): Added.
+
2018-10-25 Michael Catanzaro <mcatanzaro@igalia.com>
Unreviewed, silence a -Wreturn-type warning
for (auto& nodeListAndCollectionCount : m_nodeListAndCollectionCounts)
nodeListAndCollectionCount = 0;
+
+ InspectorInstrumentation::addEventListenersToNode(*this);
}
#if ENABLE(FULLSCREEN_API)
m_eventPhase = NONE;
m_propagationStopped = false;
m_immediatePropagationStopped = false;
+
+ InspectorInstrumentation::eventDidResetAfterDispatch(*this);
}
} // namespace WebCore
timelineAgent->didDispatchEvent();
}
+void InspectorInstrumentation::eventDidResetAfterDispatchImpl(InstrumentingAgents& instrumentingAgents, const Event& event)
+{
+ if (auto* domAgent = instrumentingAgents.inspectorDOMAgent())
+ domAgent->eventDidResetAfterDispatch(event);
+}
+
InspectorInstrumentationCookie InspectorInstrumentation::willEvaluateScriptImpl(InstrumentingAgents& instrumentingAgents, Frame& frame, const String& url, int lineNumber)
{
int timelineAgentId = 0;
static void didHandleEvent(ScriptExecutionContext&);
static InspectorInstrumentationCookie willDispatchEventOnWindow(Frame*, const Event&, DOMWindow&);
static void didDispatchEventOnWindow(const InspectorInstrumentationCookie&);
+ static void eventDidResetAfterDispatch(const Event&);
static InspectorInstrumentationCookie willEvaluateScript(Frame&, const String& url, int lineNumber);
static void didEvaluateScript(const InspectorInstrumentationCookie&, Frame&);
static InspectorInstrumentationCookie willFireTimer(ScriptExecutionContext&, int timerId, bool oneShot);
static void didDispatchEventImpl(const InspectorInstrumentationCookie&);
static InspectorInstrumentationCookie willDispatchEventOnWindowImpl(InstrumentingAgents&, const Event&, DOMWindow&);
static void didDispatchEventOnWindowImpl(const InspectorInstrumentationCookie&);
+ static void eventDidResetAfterDispatchImpl(InstrumentingAgents&, const Event&);
static InspectorInstrumentationCookie willEvaluateScriptImpl(InstrumentingAgents&, Frame&, const String& url, int lineNumber);
static void didEvaluateScriptImpl(const InspectorInstrumentationCookie&, Frame&);
static InspectorInstrumentationCookie willFireTimerImpl(InstrumentingAgents&, int timerId, bool oneShot, ScriptExecutionContext&);
didDispatchEventOnWindowImpl(cookie);
}
+inline void InspectorInstrumentation::eventDidResetAfterDispatch(const Event& event)
+{
+ FAST_RETURN_IF_NO_FRONTENDS(void());
+
+ if (!is<Node>(event.target()))
+ return;
+
+ auto* node = downcast<Node>(event.target());
+ if (auto* instrumentingAgents = instrumentingAgentsForContext(node->scriptExecutionContext()))
+ return eventDidResetAfterDispatchImpl(*instrumentingAgents, event);
+}
+
inline InspectorInstrumentationCookie InspectorInstrumentation::willEvaluateScript(Frame& frame, const String& url, int lineNumber)
{
FAST_RETURN_IF_NO_FRONTENDS(InspectorInstrumentationCookie());
void handleEvent(ScriptExecutionContext&, Event& event) final
{
- if (!is<Node>(event.target()))
+ if (!is<Node>(event.target()) || m_domAgent.m_dispatchedEvents.contains(&event))
return;
auto* node = downcast<Node>(event.target());
if (!nodeId)
return;
+ m_domAgent.m_dispatchedEvents.add(&event);
+
+ RefPtr<JSON::Object> data = JSON::Object::create();
+
+ if (event.type() == eventNames().webkitfullscreenchangeEvent)
+ data->setBoolean("enabled"_s, !!node->document().webkitFullscreenElement());
+
auto timestamp = m_domAgent.m_environment.executionStopwatch()->elapsedTime().seconds();
- m_domAgent.m_frontendDispatcher->didFireEvent(nodeId, event.type(), timestamp);
+ m_domAgent.m_frontendDispatcher->didFireEvent(nodeId, event.type(), timestamp, data->size() ? WTFMove(data) : nullptr);
}
private:
m_instrumentingAgents.setInspectorDOMAgent(this);
m_document = m_pageAgent->mainFrame().document();
+ if (m_document)
+ addEventListenersToNode(*m_document);
+
for (auto* mediaElement : HTMLMediaElement::allMediaElements())
addEventListenersToNode(*mediaElement);
{
m_documentNodeToIdMap.clear();
m_idToNode.clear();
+ m_dispatchedEvents.clear();
m_eventListenerEntries.clear();
releaseDanglingNodes();
m_childrenRequested.clear();
node.addEventListener(eventName, callback.copyRef(), false);
};
- if (is<HTMLMediaElement>(node)) {
+ if (is<Document>(node))
+ createEventListener(eventNames().webkitfullscreenchangeEvent);
+ else if (is<HTMLMediaElement>(node)) {
+ createEventListener(eventNames().webkitfullscreenchangeEvent);
createEventListener(eventNames().abortEvent);
createEventListener(eventNames().canplayEvent);
createEventListener(eventNames().canplaythroughEvent);
return false;
}
+void InspectorDOMAgent::eventDidResetAfterDispatch(const Event& event)
+{
+ m_dispatchedEvents.remove(&event);
+}
+
bool InspectorDOMAgent::hasBreakpointForEventListener(EventTarget& target, const AtomicString& eventType, EventListener& listener, bool capture)
{
for (auto& inspectorEventListener : m_eventListenerEntries.values()) {
void didAddEventListener(EventTarget&);
void willRemoveEventListener(EventTarget&, const AtomicString& eventType, EventListener&, bool capture);
bool isEventListenerDisabled(EventTarget&, const AtomicString& eventType, EventListener&, bool capture);
+ void eventDidResetAfterDispatch(const Event&);
// Callbacks that don't directly correspond to an instrumentation entry point.
void setDocument(Document*);
friend class EventFiredCallback;
+ HashSet<const Event*> m_dispatchedEvents;
HashMap<int, InspectorEventListener> m_eventListenerEntries;
int m_lastEventListenerId { 1 };
};
2018-10-25 Devin Rousso <drousso@apple.com>
+ Web Inspector: display fullscreen enter/exit events in Timelines and Network node waterfalls
+ https://bugs.webkit.org/show_bug.cgi?id=189874
+ <rdar://problem/44700000>
+
+ Reviewed by Joseph Pecoraro.
+
+ * Localizations/en.lproj/localizedStrings.js:
+
+ * UserInterface/Protocol/DOMObserver.js:
+ (WI.DOMObserver.prototype.didFireEvent):
+ * UserInterface/Controllers/DOMManager.js:
+ (WI.DOMManager.prototype.didFireEvent):
+ Allow `data` to be passed to the frontend with `didFireEvent`.
+
+ * UserInterface/Models/DOMNode.js:
+ (WI.DOMNode):
+ (WI.DOMNode.getFullscreenDOMEvents): Added.
+ (WI.DOMNode.prototype.didFireEvent):
+ (WI.DOMNode.prototype._handleDOMNodeDidFireEvent): Added.
+ (WI.DOMNode.prototype._addDOMEvent):
+ (WI.DOMNode.prototype._shouldListenForEventListeners): Added.
+ If an event is fired on an ancestor of this node, also record that event in this node's
+ `domEvents`, including the `originator` node.
+
+ * UserInterface/Views/NetworkTableContentView.js:
+ (WI.NetworkTableContentView.prototype._populateWaterfallGraph):
+ * UserInterface/Views/NetworkTableContentView.css:
+ (.network-table :not(.header) .cell.waterfall .waterfall-container > .dom-fullscreen): Added.
+
+ * UserInterface/Views/DOMEventsBreakdownView.js:
+ (WI.DOMEventsBreakdownView.prototype.initialLayout):
+ (WI.DOMEventsBreakdownView.prototype._populateTable):
+ * UserInterface/Views/DOMEventsBreakdownView.css:
+ (.dom-events-breakdown .graph > .area.fullscreen): Added.
+ (.dom-events-breakdown .inherited > .name, .dom-events-breakdown .inherited > .graph > .point): Added.
+ (.dom-events-breakdown:not(.has-inherited) .originator): Added.
+
+2018-10-25 Devin Rousso <drousso@apple.com>
+
Web Inspector: Network: more aggressively snap timing blocks together
https://bugs.webkit.org/show_bug.cgi?id=190439
localizedStrings["Frames %d \u2013 %d"] = "Frames %d \u2013 %d";
localizedStrings["Full Garbage Collection"] = "Full Garbage Collection";
localizedStrings["Full URL"] = "Full URL";
+localizedStrings["Fullscreen from “%s“"] = "Fullscreen from “%s“";
localizedStrings["Function"] = "Function";
localizedStrings["Function Name Variable"] = "Function Name Variable";
localizedStrings["Garbage Collection"] = "Garbage Collection";
localizedStrings["Original"] = "Original";
localizedStrings["Original formatting"] = "Original formatting";
localizedStrings["Originally %s"] = "Originally %s";
+localizedStrings["Originator"] = "Originator";
localizedStrings["Other"] = "Other";
localizedStrings["Other Issue"] = "Other Issue";
localizedStrings["Outgoing message"] = "Outgoing message";
node.dispatchEventToListeners(WI.DOMNode.Event.EventListenersChanged);
}
- didFireEvent(nodeId, eventName, timestamp)
+ didFireEvent(nodeId, eventName, timestamp, data)
{
// Called from WI.DOMObserver.
if (!node)
return;
- node.didFireEvent(eventName, timestamp);
+ node.didFireEvent(eventName, timestamp, data);
}
// Private
}
this._domEvents = [];
+
+ if (this._shouldListenForEventListeners())
+ WI.DOMNode.addEventListener(WI.DOMNode.Event.DidFireEvent, this._handleDOMNodeDidFireEvent, this);
+ }
+
+ // Static
+
+ static getFullscreenDOMEvents(domEvents)
+ {
+ return domEvents.reduce((accumulator, current) => {
+ if (current.eventName === "webkitfullscreenchange" && current.data && (!accumulator.length || accumulator.lastValue.data.enabled !== current.data.enabled))
+ accumulator.push(current);
+ return accumulator;
+ }, []);
}
// Public
return !!this.ownerSVGElement;
}
- didFireEvent(eventName, timestamp)
+ didFireEvent(eventName, timestamp, data)
{
// Called from WI.DOMManager.
this._addDOMEvent({
eventName,
timestamp: WI.timelineManager.computeElapsedTime(timestamp),
+ data,
});
}
+ _handleDOMNodeDidFireEvent(event)
+ {
+ if (event.target === this || !event.target.isAncestor(this))
+ return;
+
+ let domEvent = Object.shallowCopy(event.data.domEvent);
+ domEvent.originator = event.target;
+
+ this._addDOMEvent(domEvent);
+ }
+
_addDOMEvent(domEvent)
{
this._domEvents.push(domEvent);
this.dispatchEventToListeners(WI.DOMNode.Event.DidFireEvent, {domEvent});
}
+ _shouldListenForEventListeners()
+ {
+ let lowerCaseName = this.localName() || this.nodeName().toLowerCase();
+ return lowerCaseName === "video" || lowerCaseName === "audio";
+ }
+
_setAttributesPayload(attrs)
{
this._attributes = [];
WI.domManager.willRemoveEventListener(nodeId);
}
- didFireEvent(nodeId, eventName, timestamp)
+ didFireEvent(nodeId, eventName, timestamp, data)
{
- WI.domManager.didFireEvent(nodeId, eventName, timestamp);
+ WI.domManager.didFireEvent(nodeId, eventName, timestamp, data);
}
};
border-radius: 50%;
}
+.dom-events-breakdown .graph > .area.fullscreen {
+ top: 0;
+ height: 100%;
+ background-color: var(--panel-background-color);
+}
+
.dom-events-breakdown .time {
text-align: end;
}
+
+.dom-events-breakdown .inherited > .name,
+.dom-events-breakdown .inherited > .graph > .point {
+ opacity: 0.5;
+}
+
+.dom-events-breakdown:not(.has-inherited) .originator {
+ display: none;
+}
timeHeadCell.classList.add("time");
timeHeadCell.textContent = WI.UIString("Time");
+ let originatorHeadCell = headRowElement.appendChild(document.createElement("th"));
+ originatorHeadCell.classList.add("originator");
+ originatorHeadCell.textContent = WI.UIString("Originator");
+
this._tableBodyElement = tableElement.appendChild(document.createElement("tbody"));
this._populateTable();
return time / totalTime * 100;
}
+ let fullscreenRanges = [];
+ let fullscreenDOMEvents = WI.DOMNode.getFullscreenDOMEvents(this._domEvents);
+ for (let fullscreenDOMEvent of fullscreenDOMEvents) {
+ let {enabled} = fullscreenDOMEvent.data;
+ if (enabled || !fullscreenRanges.length) {
+ fullscreenRanges.push({
+ startTimestamp: enabled ? fullscreenDOMEvent.timestamp : startTimestamp,
+ });
+ }
+ fullscreenRanges.lastValue.endTimestamp = (enabled && fullscreenDOMEvent === fullscreenDOMEvents.lastValue) ? endTimestamp : fullscreenDOMEvent.timestamp;
+ }
+
for (let domEvent of this._domEvents) {
let rowElement = this._tableBodyElement.appendChild(document.createElement("tr"));
let graphCell = rowElement.appendChild(document.createElement("td"));
graphCell.classList.add("graph");
+ let fullscreenRange = fullscreenRanges.find((range) => domEvent.timestamp >= range.startTimestamp && domEvent.timestamp <= range.endTimestamp);
+ if (fullscreenRange) {
+ let fullscreenArea = graphCell.appendChild(document.createElement("div"));
+ fullscreenArea.classList.add("area", "fullscreen");
+ fullscreenArea.style.setProperty(styleAttribute, percentOfTotalTime(fullscreenRange.startTimestamp - startTimestamp) + "%");
+ fullscreenArea.style.setProperty("width", percentOfTotalTime(fullscreenRange.endTimestamp - fullscreenRange.startTimestamp) + "%");
+ }
+
let graphPoint = graphCell.appendChild(document.createElement("div"));
graphPoint.classList.add("point");
graphPoint.style.setProperty(styleAttribute, `calc(${percentOfTotalTime(domEvent.timestamp - startTimestamp)}% - (var(--point-size) / 2))`);
const higherResolution = true;
timeCell.textContent = Number.secondsToString(domEvent.timestamp - this._startTimestamp, higherResolution);
+
+ let originatorCell = rowElement.appendChild(document.createElement("td"));
+ originatorCell.classList.add("originator");
+ if (domEvent.originator) {
+ originatorCell.appendChild(WI.linkifyNodeReference(domEvent.originator));
+
+ rowElement.classList.add("inherited");
+ this.element.classList.add("has-inherited");
+ }
}
}
};
overflow: hidden;
}
+.network-table :not(.header) .cell.waterfall .waterfall-container > .dom-fullscreen {
+ position: absolute;
+ top: var(--dom-fullscreen-vertical-padding);
+ height: calc(100% - (var(--dom-fullscreen-vertical-padding) * 2));
+ background-color: lightgrey;
+
+ /* Half of the vertical space above any .dom-event node */
+ --dom-fullscreen-vertical-padding: calc((50% - (var(--node-waterfall-dom-event-size) / 2)) / 2);
+}
+
.network-table .timeline-ruler > .header {
top: calc(var(--navigation-bar-height) - var(--timeline-ruler-height));
}
if (domNode) {
const domEventElementSize = 8; // Keep this in sync with `--node-waterfall-dom-event-size`.
- let groupedDOMEvents = domNode.domEvents.reduce((accumulator, current) => {
- if (!accumulator.length || (current.timestamp - accumulator.lastValue.endTimestamp) >= (domEventElementSize * secondsPerPixel)) {
- accumulator.push({
- startTimestamp: current.timestamp,
+ let groupedDOMEvents = [];
+ for (let domEvent of domNode.domEvents) {
+ if (domEvent.originator)
+ continue;
+
+ if (!groupedDOMEvents.length || (domEvent.timestamp - groupedDOMEvents.lastValue.endTimestamp) >= (domEventElementSize * secondsPerPixel)) {
+ groupedDOMEvents.push({
+ startTimestamp: domEvent.timestamp,
domEvents: [],
});
}
- accumulator.lastValue.endTimestamp = current.timestamp;
- accumulator.lastValue.domEvents.push(current);
- return accumulator;
- }, []);
+ groupedDOMEvents.lastValue.endTimestamp = domEvent.timestamp;
+ groupedDOMEvents.lastValue.domEvents.push(domEvent);
+ }
+
+ let fullscreenDOMEvents = WI.DOMNode.getFullscreenDOMEvents(domNode.domEvents);
+ if (fullscreenDOMEvents.length) {
+ if (!fullscreenDOMEvents[0].data.enabled)
+ fullscreenDOMEvents.unshift({timestamp: graphStartTime});
+
+ if (fullscreenDOMEvents.lastValue.data.enabled)
+ fullscreenDOMEvents.push({timestamp: this._waterfallEndTime});
+
+ console.assert((fullscreenDOMEvents.length % 2) === 0, "Every enter/exit of fullscreen should have a corresponding exit/enter.");
+
+ for (let i = 0; i < fullscreenDOMEvents.length; i += 2) {
+ let fullscreenElement = container.appendChild(document.createElement("div"));
+ fullscreenElement.classList.add("dom-fullscreen");
+ positionByStartOffset(fullscreenElement, fullscreenDOMEvents[i].timestamp);
+ setWidthForDuration(fullscreenElement, fullscreenDOMEvents[i].timestamp, fullscreenDOMEvents[i + 1].timestamp);
+
+ let originator = fullscreenDOMEvents[i].originator || fullscreenDOMEvents[i + 1].originator;
+ if (originator)
+ fullscreenElement.title = WI.UIString("Fullscreen from “%s“").format(originator.displayName);
+ }
+ }
let playing = false;