Web Inspector: display low-power enter/exit events in Timelines and Network node waterfalls
https://bugs.webkit.org/show_bug.cgi?id=190641
<rdar://problem/45319049>
Reviewed by Joseph Pecoraro.
Source/JavaScriptCore:
* inspector/protocol/DOM.json:
Add `videoLowPowerChanged` event that is fired when `InspectorDOMAgent` is able to determine
whether a video element's low power state has changed.
Source/WebCore:
No new tests, as low power mode is indeterminate. Should not affect functionality.
* inspector/agents/InspectorDOMAgent.h:
* inspector/agents/InspectorDOMAgent.cpp:
(WebCore::InspectorDOMAgent::InspectorDOMAgent):
(WebCore::InspectorDOMAgent::addEventListenersToNode):
(WebCore::InspectorDOMAgent::mediaMetricsTimerFired): Added.
Source/WebInspectorUI:
* UserInterface/Protocol/DOMObserver.js:
(WI.DOMObserver.prototype.videoLowPowerChanged): Added.
* UserInterface/Controllers/DOMManager.js:
(WI.DOMManager.prototype.videoLowPowerChanged): Added.
* UserInterface/Models/DOMNode.js:
(WI.DOMNode):
(WI.DOMNode.prototype.get lowPowerRanges): Added.
(WI.DOMNode.prototype.videoLowPowerChanged): Added.
(WI.DOMNode.prototype.canEnterLowPowerMode): Added.
* UserInterface/Views/NetworkTableContentView.js:
(WI.NetworkTableContentView.prototype._populateDomainCell):
(WI.NetworkTableContentView.prototype._tryLinkResourceToDOMNode):
(WI.NetworkTableContentView.prototype._handleNodeLowPowerChanged): Added.
* UserInterface/Views/NetworkTableContentView.css:
(.network-table :not(.header) .cell.waterfall .waterfall-container > .area):
(.network-table :not(.header) .cell.waterfall .waterfall-container > .area.dom-fullscreen): Added.
(.network-table :not(.header) .cell.waterfall .waterfall-container > .area.low-power): Added.
(.network-table :not(.header) .cell.waterfall .waterfall-container > .dom-fullscreen): Deleted.
* UserInterface/Views/DOMNodeEventsContentView.js:
(WI.DOMNodeEventsContentView):
(WI.DOMNodeEventsContentView.prototype.initialLayout):
(WI.DOMNodeEventsContentView.prototype.closed): Deleted.
(WI.DOMNodeEventsContentView.prototype._handleDOMNodeDidFireEvent): Deleted.
* UserInterface/Views/DOMEventsBreakdownView.js:
(WI.DOMEventsBreakdownView):
(WI.DOMEventsBreakdownView.prototype.initialLayout):
(WI.DOMEventsBreakdownView.prototype.layout): Added.
(WI.DOMEventsBreakdownView.prototype._handleDOMNodeDidFireEvent): Added.
(WI.DOMEventsBreakdownView.prototype._handleDOMNodeLowPowerChanged): Added.
(WI.DOMEventsBreakdownView.prototype.addEvent): Deleted.
(WI.DOMEventsBreakdownView.prototype._populateTable.percentOfTotalTime): Deleted.
(WI.DOMEventsBreakdownView.prototype._populateTable): Deleted.
* UserInterface/Views/DOMEventsBreakdownView.css:
(.dom-events-breakdown .graph > .area): Added.
(.dom-events-breakdown .graph > .area.fullscreen):
(.dom-events-breakdown .graph > .area.low-power): Added.
* Localizations/en.lproj/localizedStrings.js:
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@237669 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index b87fe99..87fd70e 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,15 @@
+2018-10-31 Devin Rousso <drousso@apple.com>
+
+ Web Inspector: display low-power enter/exit events in Timelines and Network node waterfalls
+ https://bugs.webkit.org/show_bug.cgi?id=190641
+ <rdar://problem/45319049>
+
+ Reviewed by Joseph Pecoraro.
+
+ * inspector/protocol/DOM.json:
+ Add `videoLowPowerChanged` event that is fired when `InspectorDOMAgent` is able to determine
+ whether a video element's low power state has changed.
+
2018-10-31 Tadeu Zagallo <tzagallo@apple.com>
Adjust inlining threshold for new bytecode format
diff --git a/Source/JavaScriptCore/inspector/protocol/DOM.json b/Source/JavaScriptCore/inspector/protocol/DOM.json
index c879186..0597d5f 100644
--- a/Source/JavaScriptCore/inspector/protocol/DOM.json
+++ b/Source/JavaScriptCore/inspector/protocol/DOM.json
@@ -669,6 +669,15 @@
{ "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." }
]
+ },
+ {
+ "name": "videoLowPowerChanged",
+ "description": "Called when a video element enters/exits low power mode.",
+ "parameters": [
+ { "name": "nodeId", "$ref": "NodeId" },
+ { "name": "timestamp", "$ref": "Network.Timestamp", "description": "Time when the video element entered/exited low power mode" },
+ { "name": "isLowPower", "type": "boolean" }
+ ]
}
]
}
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 7507550..069bc15 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,19 @@
+2018-10-31 Devin Rousso <drousso@apple.com>
+
+ Web Inspector: display low-power enter/exit events in Timelines and Network node waterfalls
+ https://bugs.webkit.org/show_bug.cgi?id=190641
+ <rdar://problem/45319049>
+
+ Reviewed by Joseph Pecoraro.
+
+ No new tests, as low power mode is indeterminate. Should not affect functionality.
+
+ * inspector/agents/InspectorDOMAgent.h:
+ * inspector/agents/InspectorDOMAgent.cpp:
+ (WebCore::InspectorDOMAgent::InspectorDOMAgent):
+ (WebCore::InspectorDOMAgent::addEventListenersToNode):
+ (WebCore::InspectorDOMAgent::mediaMetricsTimerFired): Added.
+
2018-10-31 Alicia Boya García <aboya@igalia.com>
[MSE] Use tolerance when growing the coded frame group
diff --git a/Source/WebCore/inspector/agents/InspectorDOMAgent.cpp b/Source/WebCore/inspector/agents/InspectorDOMAgent.cpp
index a768580..a666311 100644
--- a/Source/WebCore/inspector/agents/InspectorDOMAgent.cpp
+++ b/Source/WebCore/inspector/agents/InspectorDOMAgent.cpp
@@ -67,6 +67,7 @@
#include "HTMLScriptElement.h"
#include "HTMLStyleElement.h"
#include "HTMLTemplateElement.h"
+#include "HTMLVideoElement.h"
#include "HitTestResult.h"
#include "InspectorClient.h"
#include "InspectorController.h"
@@ -96,6 +97,7 @@
#include "Text.h"
#include "TextNodeTraversal.h"
#include "Timer.h"
+#include "VideoPlaybackQuality.h"
#include "WebInjectedScriptManager.h"
#include "XPathResult.h"
#include "markup.h"
@@ -282,6 +284,7 @@
, m_backendDispatcher(Inspector::DOMBackendDispatcher::create(context.backendDispatcher, this))
, m_pageAgent(pageAgent)
, m_overlay(overlay)
+ , m_mediaMetricsTimer(*this, &InspectorDOMAgent::mediaMetricsTimerFired)
{
}
@@ -2178,6 +2181,9 @@
createEventListener(eventNames().timeupdateEvent);
createEventListener(eventNames().volumechangeEvent);
createEventListener(eventNames().waitingEvent);
+
+ if (!m_mediaMetricsTimer.isActive())
+ m_mediaMetricsTimer.start(0_s, 1_s / 15.);
}
}
@@ -2447,6 +2453,49 @@
return 0;
}
+void InspectorDOMAgent::mediaMetricsTimerFired()
+{
+ // FIXME: remove metrics information for any media element when it's destroyed
+
+ if (HTMLMediaElement::allMediaElements().isEmpty()) {
+ if (m_mediaMetricsTimer.isActive())
+ m_mediaMetricsTimer.stop();
+ m_mediaMetrics.clear();
+ return;
+ }
+
+ for (auto* mediaElement : HTMLMediaElement::allMediaElements()) {
+ if (!is<HTMLVideoElement>(mediaElement) || !mediaElement->isPlaying())
+ continue;
+
+ auto videoPlaybackQuality = mediaElement->getVideoPlaybackQuality();
+ unsigned displayCompositedVideoFrames = videoPlaybackQuality->displayCompositedVideoFrames();
+
+ auto iterator = m_mediaMetrics.find(mediaElement);
+ if (iterator == m_mediaMetrics.end()) {
+ m_mediaMetrics.set(mediaElement, MediaMetrics(displayCompositedVideoFrames));
+ continue;
+ }
+
+ bool isLowPower = (displayCompositedVideoFrames - iterator->value.displayCompositedFrames) > 0;
+ if (iterator->value.isLowPower != isLowPower) {
+ iterator->value.isLowPower = isLowPower;
+
+ int nodeId = pushNodePathToFrontend(mediaElement);
+ if (nodeId) {
+ auto timestamp = m_environment.executionStopwatch()->elapsedTime().seconds();
+ m_frontendDispatcher->videoLowPowerChanged(nodeId, timestamp, iterator->value.isLowPower);
+ }
+ }
+
+ iterator->value.displayCompositedFrames = displayCompositedVideoFrames;
+ }
+
+ m_mediaMetrics.removeIf([&] (auto& entry) {
+ return !HTMLMediaElement::allMediaElements().contains(entry.key);
+ });
+}
+
Node* InspectorDOMAgent::nodeForPath(const String& path)
{
// The path is of form "1,HTML,2,BODY,1,DIV"
diff --git a/Source/WebCore/inspector/agents/InspectorDOMAgent.h b/Source/WebCore/inspector/agents/InspectorDOMAgent.h
index cc5acbd..9bb63d0 100644
--- a/Source/WebCore/inspector/agents/InspectorDOMAgent.h
+++ b/Source/WebCore/inspector/agents/InspectorDOMAgent.h
@@ -31,6 +31,7 @@
#include "EventTarget.h"
#include "InspectorWebAgentBase.h"
+#include "Timer.h"
#include <JavaScriptCore/InspectorBackendDispatchers.h>
#include <JavaScriptCore/InspectorFrontendDispatchers.h>
#include <wtf/HashMap.h>
@@ -63,6 +64,7 @@
class InspectorHistory;
class InspectorOverlay;
class InspectorPageAgent;
+class HTMLMediaElement;
class HitTestResult;
class Node;
class PseudoElement;
@@ -223,6 +225,8 @@
int idForEventListener(EventTarget&, const AtomicString& eventType, EventListener&, bool capture);
private:
+ void mediaMetricsTimerFired();
+
void highlightMousedOverNode();
void setSearchingForNode(ErrorString&, bool enabled, const JSON::Object* highlightConfig);
std::unique_ptr<HighlightConfig> highlightConfigFromInspectorObject(ErrorString&, const JSON::Object* highlightInspectorObject);
@@ -284,6 +288,22 @@
bool m_suppressAttributeModifiedEvent { false };
bool m_documentRequested { false };
+ Timer m_mediaMetricsTimer;
+ struct MediaMetrics {
+ unsigned displayCompositedFrames { 0 };
+ bool isLowPower { false };
+
+ MediaMetrics() { }
+
+ MediaMetrics(unsigned displayCompositedFrames)
+ : displayCompositedFrames(displayCompositedFrames)
+ {
+ }
+ };
+
+ // The pointer key for this map should not be used for anything other than matching.
+ HashMap<HTMLMediaElement*, MediaMetrics> m_mediaMetrics;
+
struct InspectorEventListener {
int identifier { 1 };
RefPtr<EventTarget> eventTarget;
diff --git a/Source/WebInspectorUI/ChangeLog b/Source/WebInspectorUI/ChangeLog
index e95e00f..47d9ffb 100644
--- a/Source/WebInspectorUI/ChangeLog
+++ b/Source/WebInspectorUI/ChangeLog
@@ -1,5 +1,57 @@
2018-10-31 Devin Rousso <drousso@apple.com>
+ Web Inspector: display low-power enter/exit events in Timelines and Network node waterfalls
+ https://bugs.webkit.org/show_bug.cgi?id=190641
+ <rdar://problem/45319049>
+
+ Reviewed by Joseph Pecoraro.
+
+ * UserInterface/Protocol/DOMObserver.js:
+ (WI.DOMObserver.prototype.videoLowPowerChanged): Added.
+
+ * UserInterface/Controllers/DOMManager.js:
+ (WI.DOMManager.prototype.videoLowPowerChanged): Added.
+
+ * UserInterface/Models/DOMNode.js:
+ (WI.DOMNode):
+ (WI.DOMNode.prototype.get lowPowerRanges): Added.
+ (WI.DOMNode.prototype.videoLowPowerChanged): Added.
+ (WI.DOMNode.prototype.canEnterLowPowerMode): Added.
+
+ * UserInterface/Views/NetworkTableContentView.js:
+ (WI.NetworkTableContentView.prototype._populateDomainCell):
+ (WI.NetworkTableContentView.prototype._tryLinkResourceToDOMNode):
+ (WI.NetworkTableContentView.prototype._handleNodeLowPowerChanged): Added.
+ * UserInterface/Views/NetworkTableContentView.css:
+ (.network-table :not(.header) .cell.waterfall .waterfall-container > .area):
+ (.network-table :not(.header) .cell.waterfall .waterfall-container > .area.dom-fullscreen): Added.
+ (.network-table :not(.header) .cell.waterfall .waterfall-container > .area.low-power): Added.
+ (.network-table :not(.header) .cell.waterfall .waterfall-container > .dom-fullscreen): Deleted.
+
+ * UserInterface/Views/DOMNodeEventsContentView.js:
+ (WI.DOMNodeEventsContentView):
+ (WI.DOMNodeEventsContentView.prototype.initialLayout):
+ (WI.DOMNodeEventsContentView.prototype.closed): Deleted.
+ (WI.DOMNodeEventsContentView.prototype._handleDOMNodeDidFireEvent): Deleted.
+
+ * UserInterface/Views/DOMEventsBreakdownView.js:
+ (WI.DOMEventsBreakdownView):
+ (WI.DOMEventsBreakdownView.prototype.initialLayout):
+ (WI.DOMEventsBreakdownView.prototype.layout): Added.
+ (WI.DOMEventsBreakdownView.prototype._handleDOMNodeDidFireEvent): Added.
+ (WI.DOMEventsBreakdownView.prototype._handleDOMNodeLowPowerChanged): Added.
+ (WI.DOMEventsBreakdownView.prototype.addEvent): Deleted.
+ (WI.DOMEventsBreakdownView.prototype._populateTable.percentOfTotalTime): Deleted.
+ (WI.DOMEventsBreakdownView.prototype._populateTable): Deleted.
+ * UserInterface/Views/DOMEventsBreakdownView.css:
+ (.dom-events-breakdown .graph > .area): Added.
+ (.dom-events-breakdown .graph > .area.fullscreen):
+ (.dom-events-breakdown .graph > .area.low-power): Added.
+
+ * Localizations/en.lproj/localizedStrings.js:
+
+2018-10-31 Devin Rousso <drousso@apple.com>
+
Web Inspector: Audit: save imported audits across WebInspector sessions
https://bugs.webkit.org/show_bug.cgi?id=190858
<rdar://problem/45527625>
diff --git a/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js b/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js
index 8bd0720..53a35dc 100644
--- a/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js
+++ b/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js
@@ -422,6 +422,7 @@
localizedStrings["Frames %d \u2013 %d"] = "Frames %d \u2013 %d";
localizedStrings["Full Garbage Collection"] = "Full Garbage Collection";
localizedStrings["Full URL"] = "Full URL";
+localizedStrings["Fullscreen"] = "Fullscreen";
localizedStrings["Fullscreen from “%s“"] = "Fullscreen from “%s“";
localizedStrings["Function"] = "Function";
localizedStrings["Function Name Variable"] = "Function Name Variable";
@@ -535,6 +536,7 @@
localizedStrings["Log: "] = "Log: ";
localizedStrings["Logs"] = "Logs";
localizedStrings["Low"] = "Low";
+localizedStrings["Low Power Mode"] = "Low Power Mode";
localizedStrings["Lowest: %s"] = "Lowest: %s";
localizedStrings["MIME Type"] = "MIME Type";
localizedStrings["MIME Type:"] = "MIME Type:";
diff --git a/Source/WebInspectorUI/UserInterface/Controllers/DOMManager.js b/Source/WebInspectorUI/UserInterface/Controllers/DOMManager.js
index 5081a6b..8ecdf77 100644
--- a/Source/WebInspectorUI/UserInterface/Controllers/DOMManager.js
+++ b/Source/WebInspectorUI/UserInterface/Controllers/DOMManager.js
@@ -147,6 +147,17 @@
node.didFireEvent(eventName, timestamp, data);
}
+ videoLowPowerChanged(nodeId, timestamp, isLowPower)
+ {
+ // Called from WI.DOMObserver.
+
+ let node = this._idToDOMNode[nodeId];
+ if (!node)
+ return;
+
+ node.videoLowPowerChanged(timestamp, isLowPower);
+ }
+
// Private
_wrapClientCallback(callback)
diff --git a/Source/WebInspectorUI/UserInterface/Models/DOMNode.js b/Source/WebInspectorUI/UserInterface/Models/DOMNode.js
index 02d938a..8583b37 100644
--- a/Source/WebInspectorUI/UserInterface/Models/DOMNode.js
+++ b/Source/WebInspectorUI/UserInterface/Models/DOMNode.js
@@ -139,6 +139,7 @@
}
this._domEvents = [];
+ this._lowPowerRanges = [];
if (this._shouldListenForEventListeners())
WI.DOMNode.addEventListener(WI.DOMNode.Event.DidFireEvent, this._handleDOMNodeDidFireEvent, this);
@@ -158,6 +159,7 @@
// Public
get domEvents() { return this._domEvents; }
+ get lowPowerRanges() { return this._lowPowerRanges; }
get frameIdentifier()
{
@@ -726,6 +728,34 @@
});
}
+ videoLowPowerChanged(timestamp, isLowPower)
+ {
+ // Called from WI.DOMManager.
+
+ console.assert(this.canEnterLowPowerMode());
+
+ let lastValue = this._lowPowerRanges.lastValue;
+
+ if (isLowPower) {
+ console.assert(!lastValue || lastValue.endTimestamp);
+ if (!lastValue || lastValue.endTimestamp)
+ this._lowPowerRanges.push({startTimestamp: timestamp});
+ } else {
+ console.assert(!lastValue || lastValue.startTimestamp);
+ if (!lastValue)
+ this._lowPowerRanges.push({endTimestamp: timestamp});
+ else if (lastValue.startTimestamp)
+ lastValue.endTimestamp = timestamp;
+ }
+
+ this.dispatchEventToListeners(WI.DOMNode.Event.LowPowerChanged, {isLowPower, timestamp});
+ }
+
+ canEnterLowPowerMode()
+ {
+ return this.localName() === "video" || this.nodeName().toLowerCase() === "video";
+ }
+
_handleDOMNodeDidFireEvent(event)
{
if (event.target === this || !event.target.isAncestor(this))
@@ -898,6 +928,7 @@
AttributeRemoved: "dom-node-attribute-removed",
EventListenersChanged: "dom-node-event-listeners-changed",
DidFireEvent: "dom-node-did-fire-event",
+ LowPowerChanged: "dom-node-video-low-power-changed",
};
WI.DOMNode.PseudoElementType = {
diff --git a/Source/WebInspectorUI/UserInterface/Protocol/DOMObserver.js b/Source/WebInspectorUI/UserInterface/Protocol/DOMObserver.js
index 1d94b92..eeddb28 100644
--- a/Source/WebInspectorUI/UserInterface/Protocol/DOMObserver.js
+++ b/Source/WebInspectorUI/UserInterface/Protocol/DOMObserver.js
@@ -116,4 +116,9 @@
{
WI.domManager.didFireEvent(nodeId, eventName, timestamp, data);
}
+
+ videoLowPowerChanged(nodeId, timestamp, isLowPower)
+ {
+ WI.domManager.videoLowPowerChanged(nodeId, timestamp, isLowPower);
+ }
};
diff --git a/Source/WebInspectorUI/UserInterface/Views/DOMEventsBreakdownView.css b/Source/WebInspectorUI/UserInterface/Views/DOMEventsBreakdownView.css
index 91b8ec2..bc88db4 100644
--- a/Source/WebInspectorUI/UserInterface/Views/DOMEventsBreakdownView.css
+++ b/Source/WebInspectorUI/UserInterface/Views/DOMEventsBreakdownView.css
@@ -69,10 +69,17 @@
border-radius: 50%;
}
-.dom-events-breakdown .graph > .area.fullscreen {
+.dom-events-breakdown .graph > .area {
top: 0;
height: 100%;
- background-color: var(--panel-background-color);
+}
+
+.dom-events-breakdown .graph > .area.fullscreen {
+ background-color: hsla(0, 0%, 75%, 0.25);
+}
+
+.dom-events-breakdown .graph > .area.low-power {
+ background-color: hsla(83, 100%, 48%, 0.4);
}
.dom-events-breakdown .time {
diff --git a/Source/WebInspectorUI/UserInterface/Views/DOMEventsBreakdownView.js b/Source/WebInspectorUI/UserInterface/Views/DOMEventsBreakdownView.js
index 208f479..1d642e6 100644
--- a/Source/WebInspectorUI/UserInterface/Views/DOMEventsBreakdownView.js
+++ b/Source/WebInspectorUI/UserInterface/Views/DOMEventsBreakdownView.js
@@ -25,11 +25,25 @@
WI.DOMEventsBreakdownView = class DOMEventsBreakdownView extends WI.View
{
- constructor(domEvents, {includeGraph, startTimestamp} = {})
+ constructor(domNodeOrEvents, {includeGraph, startTimestamp} = {})
{
+ console.assert(domNodeOrEvents instanceof WI.DOMNode || Array.isArray(domNodeOrEvents));
+
super();
- this._domEvents = domEvents;
+ if (domNodeOrEvents instanceof WI.DOMNode) {
+ this._domNode = domNodeOrEvents;
+ this._domNode.addEventListener(WI.DOMNode.Event.DidFireEvent, this._handleDOMNodeDidFireEvent, this);
+ if (this._domNode.canEnterLowPowerMode())
+ this._domNode.addEventListener(WI.DOMNode.Event.LowPowerChanged, this._handleDOMNodeLowPowerChanged, this);
+
+ this._domEvents = null;
+ } else {
+ this._domNode = null;
+ this._domEvents = domNodeOrEvents;
+ this._lowPowerRanges = [];
+ }
+
this._includeGraph = includeGraph || false;
this._startTimestamp = startTimestamp || 0;
@@ -38,15 +52,6 @@
this.element.classList.add("dom-events-breakdown");
}
- // Public
-
- addEvent(domEvent)
- {
- this._domEvents.push(domEvent);
-
- this.soon._populateTable();
- }
-
// Protected
initialLayout()
@@ -74,18 +79,19 @@
originatorHeadCell.textContent = WI.UIString("Originator");
this._tableBodyElement = tableElement.appendChild(document.createElement("tbody"));
-
- this._populateTable();
}
- // Private
-
- _populateTable()
+ layout()
{
+ if (this.layoutReason !== WI.View.LayoutReason.Dirty)
+ return;
+
this._tableBodyElement.removeChildren();
- let startTimestamp = this._domEvents[0].timestamp;
- let endTimestamp = this._domEvents.lastValue.timestamp;
+ console.assert(this._domEvents || (this._domNode && this._domNode.domEvents));
+ let domEvents = this._domEvents || this._domNode.domEvents;
+ let startTimestamp = domEvents[0].timestamp;
+ let endTimestamp = domEvents.lastValue.timestamp;
let totalTime = endTimestamp - startTimestamp;
let styleAttribute = WI.resolvedLayoutDirection() === WI.LayoutDirection.LTR ? "left" : "right";
@@ -94,7 +100,7 @@
}
let fullscreenRanges = [];
- let fullscreenDOMEvents = WI.DOMNode.getFullscreenDOMEvents(this._domEvents);
+ let fullscreenDOMEvents = WI.DOMNode.getFullscreenDOMEvents(domEvents);
for (let fullscreenDOMEvent of fullscreenDOMEvents) {
let {enabled} = fullscreenDOMEvent.data;
if (enabled || !fullscreenRanges.length) {
@@ -103,9 +109,13 @@
});
}
fullscreenRanges.lastValue.endTimestamp = (enabled && fullscreenDOMEvent === fullscreenDOMEvents.lastValue) ? endTimestamp : fullscreenDOMEvent.timestamp;
+ if (fullscreenDOMEvent.originator)
+ fullscreenRanges.lastValue.originator = fullscreenDOMEvent.originator;
}
- for (let domEvent of this._domEvents) {
+ let lowPowerRanges = this._domNode ? this._domNode.lowPowerRanges : [];
+
+ for (let domEvent of domEvents) {
let rowElement = this._tableBodyElement.appendChild(document.createElement("tr"));
let nameCell = rowElement.appendChild(document.createElement("td"));
@@ -122,6 +132,20 @@
fullscreenArea.classList.add("area", "fullscreen");
fullscreenArea.style.setProperty(styleAttribute, percentOfTotalTime(fullscreenRange.startTimestamp - startTimestamp) + "%");
fullscreenArea.style.setProperty("width", percentOfTotalTime(fullscreenRange.endTimestamp - fullscreenRange.startTimestamp) + "%");
+
+ if (fullscreenRange.originator)
+ fullscreenArea.title = WI.UIString("Fullscreen from “%s“").format(fullscreenRange.originator.displayName);
+ else
+ fullscreenArea.title = WI.UIString("Fullscreen");
+ }
+
+ let lowPowerRange = lowPowerRanges.find((range) => domEvent.timestamp >= range.startTimestamp && domEvent.timestamp <= range.endTimestamp);
+ if (lowPowerRange) {
+ let lowPowerArea = graphCell.appendChild(document.createElement("div"));
+ lowPowerArea.classList.add("area", "low-power");
+ lowPowerArea.title = WI.UIString("Low Power Mode");
+ lowPowerArea.style.setProperty(styleAttribute, percentOfTotalTime(lowPowerRange.startTimestamp - startTimestamp) + "%");
+ lowPowerArea.style.setProperty("width", percentOfTotalTime(lowPowerRange.endTimestamp - lowPowerRange.startTimestamp) + "%");
}
let graphPoint = graphCell.appendChild(document.createElement("div"));
@@ -145,4 +169,16 @@
}
}
}
+
+ // Private
+
+ _handleDOMNodeDidFireEvent(event)
+ {
+ this.needsLayout();
+ }
+
+ _handleDOMNodeLowPowerChanged(event)
+ {
+ this.needsLayout();
+ }
};
diff --git a/Source/WebInspectorUI/UserInterface/Views/DOMNodeEventsContentView.js b/Source/WebInspectorUI/UserInterface/Views/DOMNodeEventsContentView.js
index 336a276..1950e85 100644
--- a/Source/WebInspectorUI/UserInterface/Views/DOMNodeEventsContentView.js
+++ b/Source/WebInspectorUI/UserInterface/Views/DOMNodeEventsContentView.js
@@ -46,29 +46,10 @@
{
super.initialLayout();
- this._breakdownView = new WI.DOMEventsBreakdownView(this._domNode.domEvents.slice(), {
+ this._breakdownView = new WI.DOMEventsBreakdownView(this._domNode, {
includeGraph: true,
startTimestamp: this._startTimestamp,
});
this.addSubview(this._breakdownView);
-
- this._domNode.addEventListener(WI.DOMNode.Event.DidFireEvent, this._handleDOMNodeDidFireEvent, this);
- }
-
- closed()
- {
- this._domNode.removeEventListener(null, null, this);
-
- super.closed();
- }
-
- // Private
-
- _handleDOMNodeDidFireEvent(event)
- {
- let {domEvent} = event.data;
-
- if (this._breakdownView)
- this._breakdownView.addEvent(domEvent);
}
};
diff --git a/Source/WebInspectorUI/UserInterface/Views/NetworkTableContentView.css b/Source/WebInspectorUI/UserInterface/Views/NetworkTableContentView.css
index 565233f..84e9761 100644
--- a/Source/WebInspectorUI/UserInterface/Views/NetworkTableContentView.css
+++ b/Source/WebInspectorUI/UserInterface/Views/NetworkTableContentView.css
@@ -175,6 +175,23 @@
border-top-style: solid;
}
+.network-table :not(.header) .cell.waterfall .waterfall-container > .area {
+ position: absolute;
+ top: var(--area-padding);
+ height: calc(100% - (var(--area-padding) * 2));
+
+ /* Half of the vertical space above any .dom-event node */
+ --area-padding: calc((50% - (var(--node-waterfall-dom-event-size) / 2)) / 2);
+}
+
+.network-table :not(.header) .cell.waterfall .waterfall-container > .area.dom-fullscreen {
+ background-color: hsla(0, 0%, 75%, 0.75);
+}
+
+.network-table :not(.header) .cell.waterfall .waterfall-container > .area.low-power {
+ background-color: var(--network-request-color);
+}
+
.network-table .timeline-ruler {
position: absolute;
top: 0;
@@ -182,16 +199,6 @@
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));
}
diff --git a/Source/WebInspectorUI/UserInterface/Views/NetworkTableContentView.js b/Source/WebInspectorUI/UserInterface/Views/NetworkTableContentView.js
index 83237f4..58bc0e7 100644
--- a/Source/WebInspectorUI/UserInterface/Views/NetworkTableContentView.js
+++ b/Source/WebInspectorUI/UserInterface/Views/NetworkTableContentView.js
@@ -542,7 +542,7 @@
lockIconElement.className = "lock";
}
- cell.append(domain);
+ cell.append(domain || emDash);
}
let uniqueSchemeValues = this._uniqueValuesForDOMNodeEntry(entry, (resourceEntry) => resourceEntry.scheme);
@@ -558,11 +558,6 @@
return;
}
- if (!entry.domain) {
- cell.textContent = emDash;
- return;
- }
-
createIconAndText(entry.scheme, entry.domain);
}
@@ -686,16 +681,29 @@
for (let i = 0; i < fullscreenDOMEvents.length; i += 2) {
let fullscreenElement = container.appendChild(document.createElement("div"));
- fullscreenElement.classList.add("dom-fullscreen");
+ fullscreenElement.classList.add("area", "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);
+ else
+ fullscreenElement.title = WI.UIString("Fullscreen");
}
}
+ for (let lowPowerRange of domNode.lowPowerRanges) {
+ let startTimestamp = lowPowerRange.startTimestamp || graphStartTime;
+ let endTimestamp = lowPowerRange.endTimestamp || this._waterfallEndTime;
+
+ let lowPowerElement = container.appendChild(document.createElement("div"));
+ lowPowerElement.classList.add("area", "low-power");
+ lowPowerElement.title = WI.UIString("Low Power Mode");
+ positionByStartOffset(lowPowerElement, startTimestamp);
+ setWidthForDuration(lowPowerElement, startTimestamp, endTimestamp);
+ }
+
let playing = false;
function createDOMEventLine(domEvents, startTimestamp, endTimestamp) {
@@ -1558,6 +1566,8 @@
this._domNodeEntries.set(resource.initiatorNode, nodeEntry);
resource.initiatorNode.addEventListener(WI.DOMNode.Event.DidFireEvent, this._handleNodeDidFireEvent, this);
+ if (resource.initiatorNode.canEnterLowPowerMode())
+ resource.initiatorNode.addEventListener(WI.DOMNode.Event.LowPowerChanged, this._handleNodeLowPowerChanged, this);
}
if (!this._entriesSortComparator)
@@ -1593,6 +1603,19 @@
this.needsLayout();
}
+ _handleNodeLowPowerChanged(event)
+ {
+ let domNode = event.target;
+ let {timestamp} = event.data;
+
+ this._pendingUpdates.push(domNode);
+
+ if (timestamp > this._waterfallEndTime)
+ this._waterfallEndTime = timestamp + (this._waterfallTimelineRuler.secondsPerPixel * 10);
+
+ this.needsLayout();
+ }
+
_hasTypeFilter()
{
return !!this._activeTypeFilters;