Web Inspector: Timelines: add Media timeline
authordrousso@apple.com <drousso@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 25 Nov 2018 22:11:24 +0000 (22:11 +0000)
committerdrousso@apple.com <drousso@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 25 Nov 2018 22:11:24 +0000 (22:11 +0000)
https://bugs.webkit.org/show_bug.cgi?id=191625

Reviewed by Matt Baker.

Add a new timeline to Timelines for media related events (e.g. event/fullscreen/low-power).
Mimics what is visible by using the same instrumentation points as the Network tab when
"Group Media Requests" is enabled.

* UserInterface/Models/MediaInstrument.js: Added.
(WI.MediaInstrument):
(WI.MediaInstrument.supported):
(WI.MediaInstrument.prototype.get timelineRecordType):
(WI.MediaInstrument.prototype.startInstrumentation):
(WI.MediaInstrument.prototype.stopInstrumentation):

* UserInterface/Models/MediaTimelineRecord.js: Added.
(WI.MediaTimelineRecord):
(WI.MediaTimelineRecord.prototype.get eventType):
(WI.MediaTimelineRecord.prototype.get domNode):
(WI.MediaTimelineRecord.prototype.get domEvent):
(WI.MediaTimelineRecord.prototype.get isLowPower):
(WI.MediaTimelineRecord.prototype.get displayName):
(WI.MediaTimelineRecord.prototype.saveIdentityToCookie):

* UserInterface/Views/MediaTimelineDataGridNode.js: Added.
(WI.MediaTimelineDataGridNode):
(WI.MediaTimelineDataGridNode.prototype.get records):
(WI.MediaTimelineDataGridNode.prototype.get data):
(WI.MediaTimelineDataGridNode.prototype.createCellContent):
(WI.MediaTimelineDataGridNode.prototype.iconClassNames):
(WI.MediaTimelineDataGridNode.prototype.filterableDataForColumn):

* UserInterface/Views/MediaTimelineOverviewGraph.js: Added.
(WI.MediaTimelineOverviewGraph):
(WI.MediaTimelineOverviewGraph.prototype.reset):
(WI.MediaTimelineOverviewGraph.prototype.shown):
(WI.MediaTimelineOverviewGraph.prototype.hidden):
(WI.MediaTimelineOverviewGraph.prototype.layout):
(WI.MediaTimelineOverviewGraph.prototype.updateSelectedRecord):
(WI.MediaTimelineOverviewGraph.prototype._handleRecordAdded):
* UserInterface/Views/MediaTimelineOverviewGraph.css: Added.
(.timeline-overview-graph.media > .timeline-record-bar):
(.timeline-overview-graph.media > .timeline-record-bar > .segment):

* UserInterface/Views/MediaTimelineView.js: Added.
(WI.MediaTimelineView):
(WI.MediaTimelineView.prototype.get secondsPerPixel):
(WI.MediaTimelineView.prototype.get selectionPathComponents):
(WI.MediaTimelineView.prototype.closed):
(WI.MediaTimelineView.prototype.reset):
(WI.MediaTimelineView.prototype.dataGridSortComparator.compareDOMNodes):
(WI.MediaTimelineView.prototype.dataGridSortComparator.):
(WI.MediaTimelineView.prototype.dataGridSortComparator):
(WI.MediaTimelineView.prototype.layout):
(WI.MediaTimelineView.prototype._processPendingRecords):
(WI.MediaTimelineView.prototype._handleRecordAdded):
(WI.MediaTimelineView.prototype._handleSelectionPathComponentSiblingSelected):
* UserInterface/Views/MediaTimelineView.css: Added.
(.timeline-view.media > .data-grid):

* UserInterface/Views/DOMEventsBreakdownView.js:
(WI.DOMEventsBreakdownView.prototype.layout):
* UserInterface/Views/DOMNodeEventsContentView.js:
(WI.DOMNodeEventsContentView.prototype.initialLayout):
* UserInterface/Views/NetworkDOMNodeDetailView.js:
(WI.NetworkDOMNodeDetailView):
(WI.NetworkDOMNodeDetailView.prototype.showContentViewForIdentifier):
* UserInterface/Views/NetworkTableContentView.js:
(WI.NetworkTableContentView):
(WI.NetworkTableContentView.prototype.get filterNavigationItems):
(WI.NetworkTableContentView.prototype._populateWaterfallGraph):
(WI.NetworkTableContentView.prototype._showDetailView):
(WI.NetworkTableContentView.prototype._waterfallPopoverContentForNodeEntry):
Remove passing of `startTimestamp` to `WI.DOMEventsBreakdownView`, as we should be showing
absolute timestamps for each event, not relative to the start of the recording.

* UserInterface/Views/TimelineIcons.css:
(.media-icon .icon): Added.
(.dom-event-record .icon): Added.
(.dom-event-record.fullscreen .icon): Added.
(.low-power-record .icon): Added.

* UserInterface/Controllers/TimelineManager.js:
(WI.TimelineManager.availableTimelineTypes):
(WI.TimelineManager.prototype.capturingStarted):
(WI.TimelineManager.prototype.capturingStopped):
(WI.TimelineManager.prototype._updateAutoCaptureInstruments):
(WI.TimelineManager.prototype._handleDOMNodeDidFireEvent): Added.
(WI.TimelineManager.prototype._handleDOMNodeLowPowerChanged): Added.

* UserInterface/Views/TimelineRuler.js:
(WI.TimelineRuler.prototype.clearMarkers):
(WI.TimelineRuler.prototype._handleClick):
* UserInterface/Views/TimelineOverview.js:
(WI.TimelineOverview.prototype._timelineRulerMouseClicked):
* UserInterface/Models/TimelineRecording.js:
(WI.TimelineRecording.prototype.reset):
(WI.TimelineRecording.prototype.addEventMarker):
(WI.TimelineRecording.prototype.addRecord):
(WI.TimelineRecording.prototype._keyForRecord):
Drive-by: rework the logic for "click" event pass-through to the graph underneath.
* UserInterface/Models/Instrument.js:
(WI.Instrument.createForTimelineType):

* UserInterface/Models/TimelineRecord.js:

* UserInterface/Views/TimelineTabContentView.js:
(WI.TimelineTabContentView.displayNameForTimelineType):
(WI.TimelineTabContentView.iconClassNameForTimelineType):
(WI.TimelineTabContentView.genericClassNameForTimelineType):
(WI.TimelineTabContentView.iconClassNameForRecord):
(WI.TimelineTabContentView.displayNameForRecord):
* UserInterface/Views/TimelineRecordBar.css:
(.timeline-record-bar.timeline-record-type-media > .segment): Added.

* UserInterface/Views/ContentView.js:
(WI.ContentView.createFromRepresentedObject):
* UserInterface/Views/TimelineOverviewGraph.js:
(WI.TimelineOverviewGraph.createForTimeline):

* UserInterface/Models/ScriptTimelineRecord.js:
(WI.ScriptTimelineRecord.EventType.displayName):

* UserInterface/Main.html:
* UserInterface/Test.html:
* Localizations/en.lproj/localizedStrings.js:
* UserInterface/Images/DOMEvent.svg: Added.
* UserInterface/Images/DOMEventFullscreen.svg: Added.
* UserInterface/Images/LowPower.svg: Added.
* UserInterface/Images/MediaInstrument.svg: Added.

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

31 files changed:
Source/WebInspectorUI/ChangeLog
Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js
Source/WebInspectorUI/UserInterface/Controllers/TimelineManager.js
Source/WebInspectorUI/UserInterface/Images/DOMEvent.svg [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Images/DOMEventFullscreen.svg [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Images/LowPower.svg [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Images/MediaInstrument.svg [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Main.html
Source/WebInspectorUI/UserInterface/Models/Instrument.js
Source/WebInspectorUI/UserInterface/Models/MediaInstrument.js [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Models/MediaTimelineRecord.js [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Models/ScriptTimelineRecord.js
Source/WebInspectorUI/UserInterface/Models/TimelineRecord.js
Source/WebInspectorUI/UserInterface/Models/TimelineRecording.js
Source/WebInspectorUI/UserInterface/Test.html
Source/WebInspectorUI/UserInterface/Views/ContentView.js
Source/WebInspectorUI/UserInterface/Views/DOMEventsBreakdownView.js
Source/WebInspectorUI/UserInterface/Views/DOMNodeEventsContentView.js
Source/WebInspectorUI/UserInterface/Views/MediaTimelineDataGridNode.js [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Views/MediaTimelineOverviewGraph.css [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Views/MediaTimelineOverviewGraph.js [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Views/MediaTimelineView.css [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Views/MediaTimelineView.js [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Views/NetworkDOMNodeDetailView.js
Source/WebInspectorUI/UserInterface/Views/NetworkTableContentView.js
Source/WebInspectorUI/UserInterface/Views/TimelineIcons.css
Source/WebInspectorUI/UserInterface/Views/TimelineOverview.js
Source/WebInspectorUI/UserInterface/Views/TimelineOverviewGraph.js
Source/WebInspectorUI/UserInterface/Views/TimelineRecordBar.css
Source/WebInspectorUI/UserInterface/Views/TimelineRuler.js
Source/WebInspectorUI/UserInterface/Views/TimelineTabContentView.js

index e489a9715a7fd08a52ad5741fdd99fd50cc56f70..8571477eafce3b964d0601ef691776dcc34b6da6 100644 (file)
@@ -1,3 +1,138 @@
+2018-11-25  Devin Rousso  <drousso@apple.com>
+
+        Web Inspector: Timelines: add Media timeline
+        https://bugs.webkit.org/show_bug.cgi?id=191625
+
+        Reviewed by Matt Baker.
+
+        Add a new timeline to Timelines for media related events (e.g. event/fullscreen/low-power).
+        Mimics what is visible by using the same instrumentation points as the Network tab when
+        "Group Media Requests" is enabled.
+
+        * UserInterface/Models/MediaInstrument.js: Added.
+        (WI.MediaInstrument):
+        (WI.MediaInstrument.supported):
+        (WI.MediaInstrument.prototype.get timelineRecordType):
+        (WI.MediaInstrument.prototype.startInstrumentation):
+        (WI.MediaInstrument.prototype.stopInstrumentation):
+
+        * UserInterface/Models/MediaTimelineRecord.js: Added.
+        (WI.MediaTimelineRecord):
+        (WI.MediaTimelineRecord.prototype.get eventType):
+        (WI.MediaTimelineRecord.prototype.get domNode):
+        (WI.MediaTimelineRecord.prototype.get domEvent):
+        (WI.MediaTimelineRecord.prototype.get isLowPower):
+        (WI.MediaTimelineRecord.prototype.get displayName):
+        (WI.MediaTimelineRecord.prototype.saveIdentityToCookie):
+
+        * UserInterface/Views/MediaTimelineDataGridNode.js: Added.
+        (WI.MediaTimelineDataGridNode):
+        (WI.MediaTimelineDataGridNode.prototype.get records):
+        (WI.MediaTimelineDataGridNode.prototype.get data):
+        (WI.MediaTimelineDataGridNode.prototype.createCellContent):
+        (WI.MediaTimelineDataGridNode.prototype.iconClassNames):
+        (WI.MediaTimelineDataGridNode.prototype.filterableDataForColumn):
+
+        * UserInterface/Views/MediaTimelineOverviewGraph.js: Added.
+        (WI.MediaTimelineOverviewGraph):
+        (WI.MediaTimelineOverviewGraph.prototype.reset):
+        (WI.MediaTimelineOverviewGraph.prototype.shown):
+        (WI.MediaTimelineOverviewGraph.prototype.hidden):
+        (WI.MediaTimelineOverviewGraph.prototype.layout):
+        (WI.MediaTimelineOverviewGraph.prototype.updateSelectedRecord):
+        (WI.MediaTimelineOverviewGraph.prototype._handleRecordAdded):
+        * UserInterface/Views/MediaTimelineOverviewGraph.css: Added.
+        (.timeline-overview-graph.media > .timeline-record-bar):
+        (.timeline-overview-graph.media > .timeline-record-bar > .segment):
+
+        * UserInterface/Views/MediaTimelineView.js: Added.
+        (WI.MediaTimelineView):
+        (WI.MediaTimelineView.prototype.get secondsPerPixel):
+        (WI.MediaTimelineView.prototype.get selectionPathComponents):
+        (WI.MediaTimelineView.prototype.closed):
+        (WI.MediaTimelineView.prototype.reset):
+        (WI.MediaTimelineView.prototype.dataGridSortComparator.compareDOMNodes):
+        (WI.MediaTimelineView.prototype.dataGridSortComparator.):
+        (WI.MediaTimelineView.prototype.dataGridSortComparator):
+        (WI.MediaTimelineView.prototype.layout):
+        (WI.MediaTimelineView.prototype._processPendingRecords):
+        (WI.MediaTimelineView.prototype._handleRecordAdded):
+        (WI.MediaTimelineView.prototype._handleSelectionPathComponentSiblingSelected):
+        * UserInterface/Views/MediaTimelineView.css: Added.
+        (.timeline-view.media > .data-grid):
+
+        * UserInterface/Views/DOMEventsBreakdownView.js:
+        (WI.DOMEventsBreakdownView.prototype.layout):
+        * UserInterface/Views/DOMNodeEventsContentView.js:
+        (WI.DOMNodeEventsContentView.prototype.initialLayout):
+        * UserInterface/Views/NetworkDOMNodeDetailView.js:
+        (WI.NetworkDOMNodeDetailView):
+        (WI.NetworkDOMNodeDetailView.prototype.showContentViewForIdentifier):
+        * UserInterface/Views/NetworkTableContentView.js:
+        (WI.NetworkTableContentView):
+        (WI.NetworkTableContentView.prototype.get filterNavigationItems):
+        (WI.NetworkTableContentView.prototype._populateWaterfallGraph):
+        (WI.NetworkTableContentView.prototype._showDetailView):
+        (WI.NetworkTableContentView.prototype._waterfallPopoverContentForNodeEntry):
+        Remove passing of `startTimestamp` to `WI.DOMEventsBreakdownView`, as we should be showing
+        absolute timestamps for each event, not relative to the start of the recording.
+
+        * UserInterface/Views/TimelineIcons.css:
+        (.media-icon .icon): Added.
+        (.dom-event-record .icon): Added.
+        (.dom-event-record.fullscreen .icon): Added.
+        (.low-power-record .icon): Added.
+
+        * UserInterface/Controllers/TimelineManager.js:
+        (WI.TimelineManager.availableTimelineTypes):
+        (WI.TimelineManager.prototype.capturingStarted):
+        (WI.TimelineManager.prototype.capturingStopped):
+        (WI.TimelineManager.prototype._updateAutoCaptureInstruments):
+        (WI.TimelineManager.prototype._handleDOMNodeDidFireEvent): Added.
+        (WI.TimelineManager.prototype._handleDOMNodeLowPowerChanged): Added.
+
+        * UserInterface/Views/TimelineRuler.js:
+        (WI.TimelineRuler.prototype.clearMarkers):
+        (WI.TimelineRuler.prototype._handleClick):
+        * UserInterface/Views/TimelineOverview.js:
+        (WI.TimelineOverview.prototype._timelineRulerMouseClicked):
+        * UserInterface/Models/TimelineRecording.js:
+        (WI.TimelineRecording.prototype.reset):
+        (WI.TimelineRecording.prototype.addEventMarker):
+        (WI.TimelineRecording.prototype.addRecord):
+        (WI.TimelineRecording.prototype._keyForRecord):
+        Drive-by: rework the logic for "click" event pass-through to the graph underneath.
+
+        * UserInterface/Models/Instrument.js:
+        (WI.Instrument.createForTimelineType):
+
+        * UserInterface/Models/TimelineRecord.js:
+
+        * UserInterface/Views/TimelineTabContentView.js:
+        (WI.TimelineTabContentView.displayNameForTimelineType):
+        (WI.TimelineTabContentView.iconClassNameForTimelineType):
+        (WI.TimelineTabContentView.genericClassNameForTimelineType):
+        (WI.TimelineTabContentView.iconClassNameForRecord):
+        (WI.TimelineTabContentView.displayNameForRecord):
+        * UserInterface/Views/TimelineRecordBar.css:
+        (.timeline-record-bar.timeline-record-type-media > .segment): Added.
+
+        * UserInterface/Views/ContentView.js:
+        (WI.ContentView.createFromRepresentedObject):
+        * UserInterface/Views/TimelineOverviewGraph.js:
+        (WI.TimelineOverviewGraph.createForTimeline):
+
+        * UserInterface/Models/ScriptTimelineRecord.js:
+        (WI.ScriptTimelineRecord.EventType.displayName):
+
+        * UserInterface/Main.html:
+        * UserInterface/Test.html:
+        * Localizations/en.lproj/localizedStrings.js:
+        * UserInterface/Images/DOMEvent.svg: Added.
+        * UserInterface/Images/DOMEventFullscreen.svg: Added.
+        * UserInterface/Images/LowPower.svg: Added.
+        * UserInterface/Images/MediaInstrument.svg: Added.
+
 2018-11-25  Matt Baker  <mattbaker@apple.com>
 
         Web Inspector: Remove parameters from TreeOutline SelectionDidChange event
index c9c561f2a1b87ddae214f349370f96c7f6529a7b..3e8b20e8b9164a10cfd25d941ac66fff1691afc7 100644 (file)
@@ -371,6 +371,8 @@ localizedStrings["Ensure forms have at least one input."] = "Ensure forms have a
 localizedStrings["Ensure hidden=true is not present on the document body."] = "Ensure hidden=true is not present on the document body.";
 localizedStrings["Ensure legend is first child in form."] = "Ensure legend is first child in form.";
 localizedStrings["Ensure tabindex is a number."] = "Ensure tabindex is a number.";
+localizedStrings["Entered Full-Screen Mode"] = "Entered Full-Screen Mode";
+localizedStrings["Entered Low-Power Mode"] = "Entered Low-Power Mode";
 localizedStrings["Entire Recording"] = "Entire Recording";
 localizedStrings["Error"] = "Error";
 localizedStrings["Error: "] = "Error: ";
@@ -385,6 +387,8 @@ localizedStrings["Event Listeners"] = "Event Listeners";
 localizedStrings["Events"] = "Events";
 localizedStrings["Example: “%s”"] = "Example: “%s”";
 localizedStrings["Exception with thrown value: %s"] = "Exception with thrown value: %s";
+localizedStrings["Exited Full-Screen Mode"] = "Exited Full-Screen Mode";
+localizedStrings["Exited Low-Power Mode"] = "Exited Low-Power Mode";
 localizedStrings["Expand All"] = "Expand All";
 localizedStrings["Expand columns"] = "Expand columns";
 localizedStrings["Expanded"] = "Expanded";
@@ -436,8 +440,8 @@ localizedStrings["Frames"] = "Frames";
 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["Full-Screen"] = "Full-Screen";
+localizedStrings["Full-Screen from “%s“"] = "Full-Screen from “%s“";
 localizedStrings["Function"] = "Function";
 localizedStrings["Function Name Variable"] = "Function Name Variable";
 localizedStrings["Garbage Collection"] = "Garbage Collection";
@@ -550,7 +554,7 @@ localizedStrings["Log WebSocket"] = "Log WebSocket";
 localizedStrings["Log: "] = "Log: ";
 localizedStrings["Logs"] = "Logs";
 localizedStrings["Low"] = "Low";
-localizedStrings["Low Power Mode"] = "Low Power Mode";
+localizedStrings["Low-Power Mode"] = "Low-Power Mode";
 localizedStrings["Lowest: %s"] = "Lowest: %s";
 localizedStrings["MIME Type"] = "MIME Type";
 localizedStrings["MIME Type:"] = "MIME Type:";
@@ -562,6 +566,7 @@ localizedStrings["Maximum"] = "Maximum";
 localizedStrings["Maximum Size: %s"] = "Maximum Size: %s";
 localizedStrings["Maximum maximum memory size in this recording"] = "Maximum maximum memory size in this recording";
 localizedStrings["Media"] = "Media";
+localizedStrings["Media Event"] = "Media Event";
 localizedStrings["Media Logging:"] = "Media Logging:";
 localizedStrings["Medium"] = "Medium";
 localizedStrings["Memory"] = "Memory";
index bf22adb445c17696ad56f8536d5a20a271b0c5d8..4414e731d2e6e6721f0b88a6cba0fba48bf7e240 100644 (file)
@@ -111,6 +111,11 @@ WI.TimelineManager = class TimelineManager extends WI.Object
         if (WI.HeapAllocationsInstrument.supported())
             types.push(WI.TimelineRecord.Type.HeapAllocations);
 
+        if (WI.MediaInstrument.supported()) {
+            let insertionIndex = types.indexOf(WI.TimelineRecord.Type.Layout) + 1;
+            types.insertAtIndex(WI.TimelineRecord.Type.Media, insertionIndex || types.length);
+        }
+
         return types;
     }
 
@@ -251,6 +256,9 @@ WI.TimelineManager = class TimelineManager extends WI.Object
 
         this._webTimelineScriptRecordsExpectingScriptProfilerEvents = [];
 
+        WI.DOMNode.addEventListener(WI.DOMNode.Event.DidFireEvent, this._handleDOMNodeDidFireEvent, this);
+        WI.DOMNode.addEventListener(WI.DOMNode.Event.LowPowerChanged, this._handleDOMNodeLowPowerChanged, this);
+
         this.dispatchEventToListeners(WI.TimelineManager.Event.CapturingStarted, {startTime});
     }
 
@@ -261,6 +269,8 @@ WI.TimelineManager = class TimelineManager extends WI.Object
         if (!this._isCapturing)
             return;
 
+        WI.DOMNode.removeEventListener(null, null, this);
+
         if (this._stopCapturingTimeout) {
             clearTimeout(this._stopCapturingTimeout);
             this._stopCapturingTimeout = undefined;
@@ -1067,6 +1077,7 @@ WI.TimelineManager = class TimelineManager extends WI.Object
                 case WI.TimelineRecord.Type.Network:
                 case WI.TimelineRecord.Type.RenderingFrame:
                 case WI.TimelineRecord.Type.Layout:
+                case WI.TimelineRecord.Type.Media:
                     instrumentSet.add(target.TimelineAgent.Instrument.Timeline);
                     break;
                 case WI.TimelineRecord.Type.Memory:
@@ -1078,6 +1089,30 @@ WI.TimelineManager = class TimelineManager extends WI.Object
             target.TimelineAgent.setInstruments(Array.from(instrumentSet));
         }
     }
+
+    _handleDOMNodeDidFireEvent(event)
+    {
+        console.assert(this._isCapturing);
+
+        let {domEvent} = event.data;
+
+        this._addRecord(new WI.MediaTimelineRecord(WI.MediaTimelineRecord.EventType.DOMEvent, domEvent.timestamp, {
+            domNode: event.target,
+            domEvent,
+        }));
+    }
+
+    _handleDOMNodeLowPowerChanged(event)
+    {
+        console.assert(this._isCapturing);
+
+        let {timestamp, isLowPower} = event.data;
+
+        this._addRecord(new WI.MediaTimelineRecord(WI.MediaTimelineRecord.EventType.LowPower, timestamp, {
+            domNode: event.target,
+            isLowPower,
+        }));
+    }
 };
 
 WI.TimelineManager.Event = {
diff --git a/Source/WebInspectorUI/UserInterface/Images/DOMEvent.svg b/Source/WebInspectorUI/UserInterface/Images/DOMEvent.svg
new file mode 100644 (file)
index 0000000..50a4e71
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright © 2018 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, 190, 164)" 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(101, 161, 134)" 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="rgb(101, 161, 134)" d="M 5.503906 12.742188 C 4.949219 12.742188 4.503906 12.292969 4.503906 11.742188 L 4.503906 3.792969 C 4.503906 3.242188 4.949219 2.792969 5.503906 2.792969 L 10.472656 2.792969 C 11.023438 2.792969 11.472656 3.242188 11.472656 3.792969 L 11.472656 4.882812 C 11.472656 5.4375 11.023438 5.882812 10.472656 5.882812 C 10.472656 5.882812 9.132812 5.882812 8.15625 5.882812 C 8.15625 5.992188 8.15625 5.996094 8.15625 6.101562 C 8.957031 6.101562 9.875 6.101562 9.875 6.101562 C 10.425781 6.101562 10.875 6.550781 10.875 7.101562 L 10.875 8.164062 C 10.875 8.71875 10.425781 9.164062 9.875 9.164062 C 9.875 9.164062 8.957031 9.164062 8.15625 9.164062 C 8.15625 9.382812 8.15625 9.398438 8.15625 9.613281 C 9.191406 9.613281 10.722656 9.613281 10.722656 9.613281 C 11.277344 9.613281 11.722656 10.0625 11.722656 10.613281 L 11.722656 11.742188 C 11.722656 12.292969 11.277344 12.742188 10.722656 12.742188 L 5.503906 12.742188"/>
+    <path fill="white" d="M 5.503906 11.742188 L 5.503906 3.792969 L 10.46875 3.792969 L 10.46875 4.882812 L 7.15625 4.882812 L 7.15625 7.101562 L 9.875 7.101562 L 9.875 8.164062 L 7.15625 8.164062 L 7.15625 10.613281 L 10.722656 10.613281 L 10.722656 11.742188 Z"/>
+</svg>
diff --git a/Source/WebInspectorUI/UserInterface/Images/DOMEventFullscreen.svg b/Source/WebInspectorUI/UserInterface/Images/DOMEventFullscreen.svg
new file mode 100644 (file)
index 0000000..66386e7
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright © 2018 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, 190, 164)" 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(101, 161, 134)" 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="rgb(101, 161, 134)" d="M 12 3 L 11 3 L 5 3 L 4 3 L 4 4 L 4 12 L 4 12.962999 L 4.962 12.999 L 6.962 13.075 L 8 13.115 L 8 12.076 L 8 9 L 10 9 L 11 9 L 11 8 L 11 7 L 11 6 L 10 6 L 8 6 L 11 6 L 12 6 L 12 5 L 12 4 L 12 3 Z M 11 4 L 11 5 L 7 5 L 7 7 L 10 7 L 10 8 L 7 8 L 7 12.076 L 5 12 L 5 4 L 11 4 Z"/>
+    <path fill="white" d="M 5 12 L 5 4 L 11 4 L 11 5 L 7 5 L 7 7 L 10 7 L 10 8 L 7 8 L 7 12.076 L 5 12 Z"/>
+</svg>
diff --git a/Source/WebInspectorUI/UserInterface/Images/LowPower.svg b/Source/WebInspectorUI/UserInterface/Images/LowPower.svg
new file mode 100644 (file)
index 0000000..1b6e382
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright © 2018 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, 190, 164)" 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(101, 161, 134)" 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="rgb(101, 161, 134)" d="M 5 13 L 6 13 L 11 13 L 12 13 L 12 12 L 12 10.8648649 L 12 9.86486486 L 11 9.86486486 L 8.57948709 9.86486483 L 8.57948718 4 L 8.57948718 3 L 7.57948718 3 L 6 3 L 5 3 L 5 4 L 5 12 L 5 13 Z M 5 13"/>
+    <path fill="white" d="M 6 4 L 7.57948718 4 L 7.57948718 10.8648649 L 11 10.8648649 L 11 12 L 6 12 L 6 4 Z M 6 4"/>
+</svg>
diff --git a/Source/WebInspectorUI/UserInterface/Images/MediaInstrument.svg b/Source/WebInspectorUI/UserInterface/Images/MediaInstrument.svg
new file mode 100644 (file)
index 0000000..54ac5ac
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright © 2018 Apple Inc. All rights reserved. -->
+<svg xmlns="http://www.w3.org/2000/svg" id="root" version="1.1" viewBox="0 0 16 16">
+    <circle cx="8" cy="8" r="7.5" id="Oval" stroke="rgb(101, 161, 134)" fill="rgb(148, 190, 164)"/>
+    <path d="M 9.05 7.38920455 L 12.375 5.5 L 12.375 10.5 L 9.05 8.61079545 L 9.05 9.5 C 9.05 10.0522847 8.60228475 10.5 8.05 10.5 L 4.25 10.5 C 3.69771525 10.5 3.25 10.0522847 3.25 9.5 L 3.25 6.5 C 3.25 5.94771525 3.69771525 5.5 4.25 5.5 L 8.05 5.5 C 8.60228475 5.5 9.05 5.94771525 9.05 6.5 L 9.05 7.38920455 Z" stroke="rgb(101, 161, 134)" fill="rgb(101, 161, 134)" stroke-width="2" stroke-linejoin="round"/>
+    <path d="M 9.05 7.38920455 L 12.375 5.5 L 12.375 10.5 L 9.05 8.61079545 L 9.05 9.5 C 9.05 10.0522847 8.60228475 10.5 8.05 10.5 L 4.25 10.5 C 3.69771525 10.5 3.25 10.0522847 3.25 9.5 L 3.25 6.5 C 3.25 5.94771525 3.69771525 5.5 4.25 5.5 L 8.05 5.5 C 8.60228475 5.5 9.05 5.94771525 9.05 6.5 L 9.05 7.38920455 Z" fill="white"/>
+</svg>
index af16ab19f22edbefd7454cd11e8e7f4b19af7eb3..78fc4eae55b915d5c3071900516904c06dd45664 100644 (file)
     <link rel="stylesheet" href="Views/LayoutTimelineView.css">
     <link rel="stylesheet" href="Views/LogContentView.css">
     <link rel="stylesheet" href="Views/LogIcon.css">
+    <link rel="stylesheet" href="Views/MediaTimelineOverviewGraph.css">
+    <link rel="stylesheet" href="Views/MediaTimelineView.css">
     <link rel="stylesheet" href="Views/MemoryCategoryView.css">
     <link rel="stylesheet" href="Views/MemoryTimelineOverviewGraph.css">
     <link rel="stylesheet" href="Views/MemoryTimelineView.css">
     <script src="Models/LineWidget.js"></script>
     <script src="Models/LogObject.js"></script>
     <script src="Models/LoggingChannel.js"></script>
+    <script src="Models/MediaInstrument.js"></script>
+    <script src="Models/MediaTimelineRecord.js"></script>
     <script src="Models/MemoryCategory.js"></script>
     <script src="Models/MemoryInstrument.js"></script>
     <script src="Models/MemoryPressureEvent.js"></script>
     <script src="Views/LayoutTimelineView.js"></script>
     <script src="Views/LineChart.js"></script>
     <script src="Views/LogContentView.js"></script>
+    <script src="Views/MediaTimelineDataGridNode.js"></script>
+    <script src="Views/MediaTimelineOverviewGraph.js"></script>
+    <script src="Views/MediaTimelineView.js"></script>
     <script src="Views/MemoryCategoryView.js"></script>
     <script src="Views/MemoryTimelineOverviewGraph.js"></script>
     <script src="Views/MemoryTimelineView.js"></script>
index db862c76d0e9ba7586f49c4916dc64ecdfe6c666..f12c3af6ffe1062eb611394b5e69858a5afeb933 100644 (file)
@@ -42,6 +42,8 @@ WI.Instrument = class Instrument
             return new WI.MemoryInstrument;
         case WI.TimelineRecord.Type.HeapAllocations:
             return new WI.HeapAllocationsInstrument;
+        case WI.TimelineRecord.Type.Media:
+            return new WI.MediaInstrument;
         default:
             console.error("Unknown TimelineRecord.Type: " + type);
             return null;
diff --git a/Source/WebInspectorUI/UserInterface/Models/MediaInstrument.js b/Source/WebInspectorUI/UserInterface/Models/MediaInstrument.js
new file mode 100644 (file)
index 0000000..20bf6d9
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * 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.MediaInstrument = class MediaInstrument extends WI.Instrument
+{
+    constructor()
+    {
+        super();
+
+        console.assert(WI.MediaInstrument.supported());
+    }
+
+    // Static
+
+    static supported()
+    {
+        // COMPATIBILITY (iOS 12): DOM.didFireEvent and DOM.videoLowPowerChanged did not exist.
+        return window.DOMAgent && DOMAgent.hasEvent("didFireEvent") && DOMAgent.hasEvent("videoLowPowerChanged");
+    }
+
+    // Protected
+
+    get timelineRecordType()
+    {
+        return WI.TimelineRecord.Type.Media;
+    }
+
+    startInstrumentation(initiatedByBackend)
+    {
+        // Nothing to do, media instrumentation is always happening.
+    }
+
+    stopInstrumentation(initiatedByBackend)
+    {
+        // Nothing to do, media instrumentation is always happening.
+    }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Models/MediaTimelineRecord.js b/Source/WebInspectorUI/UserInterface/Models/MediaTimelineRecord.js
new file mode 100644 (file)
index 0000000..8149c24
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * 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.MediaTimelineRecord = class MediaTimelineRecord extends WI.TimelineRecord
+{
+    constructor(eventType, timestamp, {domNode, domEvent, isLowPower} = {})
+    {
+        console.assert(Object.values(WI.MediaTimelineRecord.EventType).includes(eventType));
+
+        super(WI.TimelineRecord.Type.Media, timestamp, timestamp);
+
+        this._eventType = eventType;
+        this._domNode = domNode || null;
+        this._domEvent = domEvent || null;
+        this._isLowPower = isLowPower || false;
+    }
+
+    // Public
+
+    get eventType() { return this._eventType; }
+    get domNode() { return this._domNode; }
+    get domEvent() { return this._domEvent; }
+    get isLowPower() { return this._isLowPower; }
+
+    get displayName()
+    {
+        if (this._eventType === WI.MediaTimelineRecord.EventType.DOMEvent && this._domEvent) {
+            let eventName = this._domEvent.eventName;
+            if (eventName === "webkitfullscreenchange" && this._domEvent.data)
+                return this._domEvent.data.enabled ? WI.UIString("Entered Full-Screen Mode") : WI.UIString("Exited Full-Screen Mode");
+            return eventName;
+        }
+
+        if (this._eventType === WI.MediaTimelineRecord.EventType.LowPower)
+            return this._isLowPower ? WI.UIString("Entered Low-Power Mode") : WI.UIString("Exited Low-Power Mode");
+
+        if (this._domNode)
+            return this._domNode.displayName;
+
+        console.error("Unknown media record event type: ", this._eventType, this);
+        return WI.UIString("Media Event");
+    }
+
+    saveIdentityToCookie(cookie)
+    {
+        super.saveIdentityToCookie(cookie);
+
+        cookie["media-timeline-record-event-type"] = this._eventType;
+        if (this._domNode)
+            cookie["media-timeline-record-dom-node"] = this._domNode.path();
+        if (this._domEvent)
+            cookie["media-timeline-record-dom-event"] = this._domEvent.eventName;
+        if (this._isLowPower || (this._domEvent && this._domEvent.data && this._domEvent.data.enabled))
+            cookie["media-timeline-record-active"] = true;
+    }
+};
+
+WI.MediaTimelineRecord.EventType = {
+    DOMEvent: "dom-event",
+    LowPower: "low-power",
+};
index 50bc4ecc513a128b589f5f01082098af7f739ada..6b40ac8142bbc199ffe644d0092c1ebee6c21ee6 100644 (file)
@@ -329,11 +329,11 @@ WI.ScriptTimelineRecord.EventType.displayName = function(eventType, details, inc
         nameMap.set("webkitEditableContentChanged", "Editable Content Changed");
         nameMap.set("webkitTransitionEnd", "Transition End");
         nameMap.set("webkitaddsourcebuffer", "Add Source Buffer");
-        nameMap.set("webkitbeginfullscreen", "Begin Full Screen");
+        nameMap.set("webkitbeginfullscreen", "Begin Full-Screen");
         nameMap.set("webkitcurrentplaybacktargetiswirelesschanged", "Current Playback Target Is Wireless Changed");
-        nameMap.set("webkitendfullscreen", "End Full Screen");
-        nameMap.set("webkitfullscreenchange", "Full Screen Change");
-        nameMap.set("webkitfullscreenerror", "Full Screen Error");
+        nameMap.set("webkitendfullscreen", "End Full-Screen");
+        nameMap.set("webkitfullscreenchange", "Full-Screen Change");
+        nameMap.set("webkitfullscreenerror", "Full-Screen Error");
         nameMap.set("webkitkeyadded", "Key Added");
         nameMap.set("webkitkeyerror", "Key Error");
         nameMap.set("webkitkeymessage", "Key Message");
index fd7d5ff121fa81a26fca1f502bc4f8fdbda3834e..5c5e78b11bc2ded3828f605cb7ab0d2bbe8c17b3 100644 (file)
@@ -160,6 +160,7 @@ WI.TimelineRecord.Type = {
     RenderingFrame: "timeline-record-type-rendering-frame",
     Memory: "timeline-record-type-memory",
     HeapAllocations: "timeline-record-type-heap-allocations",
+    Media: "timeline-record-type-media",
 };
 
 WI.TimelineRecord.TypeIdentifier = "timeline-record";
index 947905c62a6012a5a161a7419ecd1d449ec129e8..fc7587e82146757248c3380293a01b9b8ad3d869 100644 (file)
@@ -127,7 +127,6 @@ WI.TimelineRecording = class TimelineRecording extends WI.Object
         console.assert(!this._readonly, "Can't reset a read-only recording.");
 
         this._sourceCodeTimelinesMap = new Map;
-        this._eventMarkers = [];
         this._startTime = NaN;
         this._endTime = NaN;
         this._discontinuities = [];
@@ -196,8 +195,6 @@ WI.TimelineRecording = class TimelineRecording extends WI.Object
         if (!this._capturing)
             return;
 
-        this._eventMarkers.push(marker);
-
         this.dispatchEventToListeners(WI.TimelineRecording.Event.MarkerAdded, {marker});
     }
 
@@ -215,7 +212,8 @@ WI.TimelineRecording = class TimelineRecording extends WI.Object
         if (record.type === WI.TimelineRecord.Type.Network
             || record.type === WI.TimelineRecord.Type.RenderingFrame
             || record.type === WI.TimelineRecord.Type.Memory
-            || record.type === WI.TimelineRecord.Type.HeapAllocations)
+            || record.type === WI.TimelineRecord.Type.HeapAllocations
+            || record.type === WI.TimelineRecord.Type.Media)
             return;
 
         if (!WI.TimelineRecording.sourceCodeTimelinesSupported())
@@ -332,7 +330,7 @@ WI.TimelineRecording = class TimelineRecording extends WI.Object
     _keyForRecord(record)
     {
         var key = record.type;
-        if (record instanceof WI.ScriptTimelineRecord || record instanceof WI.LayoutTimelineRecord)
+        if (record instanceof WI.ScriptTimelineRecord || record instanceof WI.LayoutTimelineRecord || record instanceof WI.MediaTimelineRecord)
             key += ":" + record.eventType;
         if (record instanceof WI.ScriptTimelineRecord && record.eventType === WI.ScriptTimelineRecord.EventType.EventDispatched)
             key += ":" + record.details;
index 07199a6049f5a614e5706d6bc0744646ab495950..9dd40b99e8684f2e5946694aeeadc7edc2eac10d 100644 (file)
     <script src="Models/LayoutTimelineRecord.js"></script>
     <script src="Models/LazySourceCodeLocation.js"></script>
     <script src="Models/LoggingChannel.js"></script>
+    <script src="Models/MediaInstrument.js"></script>
+    <script src="Models/MediaTimelineRecord.js"></script>
     <script src="Models/MemoryCategory.js"></script>
     <script src="Models/MemoryInstrument.js"></script>
     <script src="Models/MemoryPressureEvent.js"></script>
index 1a11e7ffcfce3c15211e44f70a8c6d5080f24a03..e7b30c955348ccee68b7f1a2da002a7684b46c9c 100644 (file)
@@ -88,6 +88,9 @@ WI.ContentView = class ContentView extends WI.View
 
             if (timelineType === WI.TimelineRecord.Type.HeapAllocations)
                 return new WI.HeapAllocationsTimelineView(representedObject, extraArguments);
+
+            if (timelineType === WI.TimelineRecord.Type.Media)
+                return new WI.MediaTimelineView(representedObject, extraArguments);
         }
 
         if (representedObject instanceof WI.Breakpoint || representedObject instanceof WI.IssueMessage) {
index 1d642e6526ae15fc5dfcedf085efad892a9a1148..e3f78deb87616ff11c63d71eca072b5a0e0b5ae1 100644 (file)
@@ -25,7 +25,7 @@
 
 WI.DOMEventsBreakdownView = class DOMEventsBreakdownView extends WI.View
 {
-    constructor(domNodeOrEvents, {includeGraph, startTimestamp} = {})
+    constructor(domNodeOrEvents, {includeGraph} = {})
     {
         console.assert(domNodeOrEvents instanceof WI.DOMNode || Array.isArray(domNodeOrEvents));
 
@@ -45,7 +45,6 @@ WI.DOMEventsBreakdownView = class DOMEventsBreakdownView extends WI.View
         }
 
         this._includeGraph = includeGraph || false;
-        this._startTimestamp = startTimestamp || 0;
 
         this._tableBodyElement = null;
 
@@ -134,16 +133,16 @@ WI.DOMEventsBreakdownView = class DOMEventsBreakdownView extends WI.View
                     fullscreenArea.style.setProperty("width", percentOfTotalTime(fullscreenRange.endTimestamp - fullscreenRange.startTimestamp) + "%");
 
                     if (fullscreenRange.originator)
-                        fullscreenArea.title = WI.UIString("Fullscreen from “%s“").format(fullscreenRange.originator.displayName);
+                        fullscreenArea.title = WI.UIString("Full-Screen from “%s“").format(fullscreenRange.originator.displayName);
                     else
-                        fullscreenArea.title = WI.UIString("Fullscreen");
+                        fullscreenArea.title = WI.UIString("Full-Screen");
                 }
 
                 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.title = WI.UIString("Low-Power Mode");
                     lowPowerArea.style.setProperty(styleAttribute, percentOfTotalTime(lowPowerRange.startTimestamp - startTimestamp) + "%");
                     lowPowerArea.style.setProperty("width", percentOfTotalTime(lowPowerRange.endTimestamp - lowPowerRange.startTimestamp) + "%");
                 }
@@ -157,7 +156,7 @@ WI.DOMEventsBreakdownView = class DOMEventsBreakdownView extends WI.View
             timeCell.classList.add("time");
 
             const higherResolution = true;
-            timeCell.textContent = Number.secondsToString(domEvent.timestamp - this._startTimestamp, higherResolution);
+            timeCell.textContent = Number.secondsToString(domEvent.timestamp, higherResolution);
 
             let originatorCell = rowElement.appendChild(document.createElement("td"));
             originatorCell.classList.add("originator");
index 1950e85add98de8e12ef13493c1d2759c02bf45d..fd978bd1ff07cbe451bf479fc6c6b18765b36f3a 100644 (file)
@@ -25,7 +25,7 @@
 
 WI.DOMNodeEventsContentView = class DOMNodeEventsContentView extends WI.ContentView
 {
-    constructor(domNode, {startTimestamp} = {})
+    constructor(domNode)
     {
         console.assert(domNode instanceof WI.DOMNode);
 
@@ -33,7 +33,6 @@ WI.DOMNodeEventsContentView = class DOMNodeEventsContentView extends WI.ContentV
         super(representedObject);
 
         this._domNode = domNode;
-        this._startTimestamp = startTimestamp || 0;
 
         this.element.classList.add("dom-node-details", "dom-events");
 
@@ -48,7 +47,6 @@ WI.DOMNodeEventsContentView = class DOMNodeEventsContentView extends WI.ContentV
 
         this._breakdownView = new WI.DOMEventsBreakdownView(this._domNode, {
             includeGraph: true,
-            startTimestamp: this._startTimestamp,
         });
         this.addSubview(this._breakdownView);
     }
diff --git a/Source/WebInspectorUI/UserInterface/Views/MediaTimelineDataGridNode.js b/Source/WebInspectorUI/UserInterface/Views/MediaTimelineDataGridNode.js
new file mode 100644 (file)
index 0000000..a18f471
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * 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.MediaTimelineDataGridNode = class MediaTimelineDataGridNode extends WI.TimelineDataGridNode
+{
+    constructor(record, graphDataSource)
+    {
+        console.assert(record instanceof WI.MediaTimelineRecord);
+
+        const includesGraph = false;
+        super(includesGraph, graphDataSource);
+
+        this._records = [record];
+    }
+
+    // Public
+
+    get records() { return this._records; }
+
+    get data()
+    {
+        if (this._cachedData)
+            return this._cachedData;
+
+        this._cachedData = super.data;
+        this._cachedData.name = this.record.displayName;
+        if (this.record.domNode)
+            this._cachedData.element = this.record.domNode;
+        this._cachedData.time = this.record.startTime - (this.graphDataSource ? this.graphDataSource.zeroTime : 0);
+        if (this.record.eventType === WI.MediaTimelineRecord.EventType.DOMEvent && this.record.domEvent.originator)
+            this._cachedData.originator = this.record.domEvent.originator;
+        return this._cachedData;
+    }
+
+    createCellContent(columnIdentifier, cell)
+    {
+        let value = this.data[columnIdentifier];
+
+        switch (columnIdentifier) {
+        case "name":
+            cell.classList.add(...this.iconClassNames());
+            return value;
+
+        case "element":
+            return value ? WI.linkifyNodeReference(value) : emDash;
+
+        case "time": {
+            const higherResolution = true;
+            return Number.secondsToString(value, higherResolution);
+        }
+
+        case "originator":
+            return value ? WI.linkifyNodeReference(value) : zeroWidthSpace;
+        }
+
+        return super.createCellContent(columnIdentifier, cell);
+    }
+
+    iconClassNames()
+    {
+        let iconClassNames = super.iconClassNames();
+        if (this.record.eventType === WI.MediaTimelineRecord.EventType.DOMEvent && this.record.domEvent.eventName === "webkitfullscreenchange")
+            iconClassNames.push("fullscreen");
+        return iconClassNames;
+    }
+
+    // Protected
+
+    filterableDataForColumn(columnIdentifier)
+    {
+        if (columnIdentifier === "element") {
+            if (this.record.domNode)
+                return this.record.domNode.displayName;
+        }
+
+        if (columnIdentifier === "originator") {
+            if (this.record.eventType === WI.MediaTimelineRecord.EventType.DOMEvent && this.record.domEvent.originator)
+                return this.record.domEvent.originator.displayName;
+        }
+
+        return super.filterableDataForColumn(columnIdentifier);
+    }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Views/MediaTimelineOverviewGraph.css b/Source/WebInspectorUI/UserInterface/Views/MediaTimelineOverviewGraph.css
new file mode 100644 (file)
index 0000000..8dddf13
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+.timeline-overview-graph.media > .timeline-record-bar {
+    margin-top: 8px;
+    height: 20px;
+}
+
+.timeline-overview-graph.media > .timeline-record-bar > .segment {
+    border-radius: 2px;
+}
diff --git a/Source/WebInspectorUI/UserInterface/Views/MediaTimelineOverviewGraph.js b/Source/WebInspectorUI/UserInterface/Views/MediaTimelineOverviewGraph.js
new file mode 100644 (file)
index 0000000..7b244ce
--- /dev/null
@@ -0,0 +1,120 @@
+
+/*
+ * 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.MediaTimelineOverviewGraph = class MediaTimelineOverviewGraph extends WI.TimelineOverviewGraph
+{
+    constructor(timeline, timelineOverview)
+    {
+        console.assert(timeline instanceof WI.Timeline);
+        console.assert(timeline.type === WI.TimelineRecord.Type.Media);
+
+        super(timelineOverview);
+
+        this._timeline = timeline;
+        this._recordBars = [];
+
+        this.element.classList.add("media");
+
+        this.reset();
+    }
+
+    // Public
+
+    reset()
+    {
+        this.element.removeChildren();
+
+        super.reset();
+    }
+
+    shown()
+    {
+        super.shown();
+
+        this._timeline.addEventListener(WI.Timeline.Event.RecordAdded, this._handleRecordAdded, this);
+    }
+
+    hidden()
+    {
+        this._timeline.removeEventListener(null, null, this);
+
+        super.hidden();
+    }
+
+    // Protected
+
+    layout()
+    {
+        if (!this.visible)
+            return;
+
+        let recordBarIndex = 0;
+
+        let createBar = (records, renderMode) => {
+            let timelineRecordBar = this._recordBars[recordBarIndex];
+            if (timelineRecordBar) {
+                timelineRecordBar.renderMode = renderMode;
+                timelineRecordBar.records = records;
+            } else
+                timelineRecordBar = this._recordBars[recordBarIndex] = new WI.TimelineRecordBar(this, records, renderMode);
+            timelineRecordBar.refresh(this);
+            if (!timelineRecordBar.element.parentNode)
+                this.element.appendChild(timelineRecordBar.element);
+            ++recordBarIndex;
+        };
+
+        WI.TimelineRecordBar.createCombinedBars(this._timeline.records, this.timelineOverview.secondsPerPixel, this, createBar);
+
+        // Remove the remaining unused TimelineRecordBars.
+        for (let i = recordBarIndex; i < this._recordBars.length; ++i) {
+            this._recordBars[i].records = null;
+            this._recordBars[i].element.remove();
+        }
+    }
+
+    updateSelectedRecord()
+    {
+        super.updateSelectedRecord();
+
+        for (let recordBar of this._recordBars) {
+            if (recordBar.records.includes(this.selectedRecord)) {
+                this.selectedRecordBar = recordBar;
+                return;
+            }
+        }
+
+        this.selectedRecordBar = null;
+    }
+
+    // Private
+
+    _handleRecordAdded(event)
+    {
+        this.needsLayout();
+    }
+};
+
+WI.MediaTimelineOverviewGraph.MaximumRowCount = 6;
diff --git a/Source/WebInspectorUI/UserInterface/Views/MediaTimelineView.css b/Source/WebInspectorUI/UserInterface/Views/MediaTimelineView.css
new file mode 100644 (file)
index 0000000..20b4e39
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+.timeline-view.media > .data-grid {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+}
diff --git a/Source/WebInspectorUI/UserInterface/Views/MediaTimelineView.js b/Source/WebInspectorUI/UserInterface/Views/MediaTimelineView.js
new file mode 100644 (file)
index 0000000..3b402dc
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ * 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.MediaTimelineView = class MediaTimelineView extends WI.TimelineView
+{
+    constructor(timeline, extraArguments)
+    {
+        console.assert(timeline instanceof WI.Timeline);
+        console.assert(timeline.type === WI.TimelineRecord.Type.Media);
+
+        super(timeline, extraArguments);
+
+        this._timelineRuler = new WI.TimelineRuler;
+
+        const columns = {
+            name: {
+                title: WI.UIString("Name"),
+                width: "10%",
+                icon: true,
+                sortable: true,
+                locked: true,
+            },
+            element: {
+                title: WI.UIString("Element"),
+                width: "10%",
+                sortable: true,
+            },
+            time: {
+                title: WI.UIString("Time"),
+                width: "10%",
+                sortable: true,
+                locked: true,
+            },
+            originator: {
+                title: WI.UIString("Originator"),
+                width: "10%",
+                sortable: true,
+                hidden: true,
+            },
+            graph: {
+                headerView: this._timelineRuler,
+                locked: true,
+            },
+        };
+
+        this._dataGrid = new WI.TimelineDataGrid(columns);
+        this._dataGrid.sortDelegate = this;
+        this._dataGrid.sortColumnIdentifier = "time";
+        this._dataGrid.sortOrder = WI.DataGrid.SortOrder.Ascending;
+        this._dataGrid.setColumnVisible("originator", false);
+        this._dataGrid.createSettings("media-timeline-view");
+        this.setupDataGrid(this._dataGrid);
+        this.addSubview(this._dataGrid);
+
+        this.element.classList.add("media");
+
+        timeline.addEventListener(WI.Timeline.Event.RecordAdded, this._handleRecordAdded, this);
+
+        this._pendingRecords = [];
+    }
+
+    // Public
+
+    get secondsPerPixel() { return this._timelineRuler.secondsPerPixel; }
+
+    get selectionPathComponents()
+    {
+        if (!this._dataGrid.selectedNode || this._dataGrid.selectedNode.hidden)
+            return null;
+
+        let pathComponent = new WI.TimelineDataGridNodePathComponent(this._dataGrid.selectedNode);
+        pathComponent.addEventListener(WI.HierarchicalPathComponent.Event.SiblingWasSelected, this._handleSelectionPathComponentSiblingSelected, this);
+        return [pathComponent];
+    }
+
+    closed()
+    {
+        this.representedObject.removeEventListener(null, null, this);
+
+        super.closed();
+    }
+
+    reset()
+    {
+        super.reset();
+
+        this._pendingRecords = [];
+    }
+
+    // TimelineDataGrid delegate
+
+    dataGridSortComparator(sortColumnIdentifier, sortDirection, node1, node2)
+    {
+        function compareDOMNodes(a, b) {
+            if (a && !b)
+                return -1;
+            if (b && !a)
+                return 1;
+            if (!a && !b)
+                return 0;
+            return a.id - b.id;
+        }
+
+        if (sortColumnIdentifier === "name") {
+            let displayName1 = node1.displayName();
+            let displayName2 = node2.displayName();
+            return displayName1.extendedLocaleCompare(displayName2) * sortDirection;
+        }
+
+        if (sortColumnIdentifier === "element")
+            return compareDOMNodes(node1.record.domNode, node2.record.domNode) * sortDirection;
+
+        if (sortColumnIdentifier === "time")
+            return (node1.record.startTime - node2.record.startTime) * sortDirection;
+
+        if (sortColumnIdentifier === "originator") {
+            function getOriginator(record) {
+                if (record.eventType !== WI.MediaTimelineRecord.EventType.DOMEvent || !record.domEvent)
+                    return null;
+                return record.domEvent.originator;
+            }
+            return compareDOMNodes(getOriginator(node1.record), getOriginator(node2.record)) * sortDirection;
+        }
+
+        return null;
+    }
+
+    // Protected
+
+    layout()
+    {
+        super.layout();
+
+        this.endTime = Math.min(this.endTime, this.currentTime);
+
+        let oldZeroTime = this._timelineRuler.zeroTime;
+        let oldStartTime = this._timelineRuler.startTime;
+        let oldEndTime = this._timelineRuler.endTime;
+
+        this._timelineRuler.zeroTime = this.zeroTime;
+        this._timelineRuler.startTime = this.startTime;
+        this._timelineRuler.endTime = this.endTime;
+
+        // We only need to refresh the graphs when the any of the times change.
+        if (this.zeroTime !== oldZeroTime || this.startTime !== oldStartTime || this.endTime !== oldEndTime) {
+            for (let dataGridNode of this._dataGrid.children)
+                dataGridNode.refreshGraph();
+        }
+
+        this._processPendingRecords();
+    }
+
+    // Private
+
+    _processPendingRecords()
+    {
+        if (!this._pendingRecords.length)
+            return;
+
+        for (let timelineRecord of this._pendingRecords) {
+            if (timelineRecord.domEvent && timelineRecord.domEvent.originator)
+                this._dataGrid.setColumnVisible("originator", true);
+
+            this._dataGrid.addRowInSortOrder(new WI.MediaTimelineDataGridNode(timelineRecord, this));
+        }
+
+        this._pendingRecords = [];
+    }
+
+    _handleRecordAdded(event)
+    {
+        let record = event.data.record;
+        console.assert(record instanceof WI.MediaTimelineRecord);
+
+        this._pendingRecords.push(record);
+
+        this.needsLayout();
+    }
+
+    _handleSelectionPathComponentSiblingSelected(event)
+    {
+        let pathComponent = event.data.pathComponent;
+        console.assert(pathComponent instanceof WI.TimelineDataGridNodePathComponent);
+
+        let dataGridNode = pathComponent.timelineDataGridNode;
+        console.assert(dataGridNode.dataGrid === this._dataGrid);
+
+        dataGridNode.revealAndSelect();
+    }
+};
index 0a24bfb5c342c3fddd406965da52d05f76b7bee0..dc87c3c040c698d282bf25b75b9132ad2e44d09b 100644 (file)
 
 WI.NetworkDOMNodeDetailView = class NetworkDOMNodeDetailView extends WI.NetworkDetailView
 {
-    constructor(domNode, delegate, {startTimestamp} = {})
+    constructor(domNode, delegate)
     {
         console.assert(domNode instanceof WI.DOMNode);
 
         super(domNode, delegate);
 
-        this._startTimestamp = startTimestamp || 0;
-
         this.element.classList.add("dom-node");
 
         this._domEventsContentView = null;
@@ -56,9 +54,7 @@ WI.NetworkDOMNodeDetailView = class NetworkDOMNodeDetailView extends WI.NetworkD
         switch (identifier) {
         case "dom-events":
             if (!this._domEventsContentView) {
-                this._domEventsContentView = new WI.DOMNodeEventsContentView(this.representedObject, {
-                    startTimestamp: this._startTimestamp,
-                });
+                this._domEventsContentView = new WI.DOMNodeEventsContentView(this.representedObject);
             }
             this._contentBrowser.showContentView(this._domEventsContentView, this._contentViewCookie);
             break;
index 1cd36460a93ea271d08f8e0117cdc0edfa52284f..0e676598548bc1d1eb062f835c777afad36a1112 100644 (file)
@@ -84,8 +84,11 @@ WI.NetworkTableContentView = class NetworkTableContentView extends WI.ContentVie
         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 Media Requests"), WI.settings.groupByDOMNode.value);
-        this._groupByDOMNodeNavigationItem.addEventListener(WI.CheckboxNavigationItem.Event.CheckedDidChange, this._handleGroupByDOMNodeCheckedDidChange, this);
+        if (WI.MediaInstrument.supported()) {
+            this._groupByDOMNodeNavigationItem = new WI.CheckboxNavigationItem("group-by-node", WI.UIString("Group Media Requests"), WI.settings.groupByDOMNode.value);
+            this._groupByDOMNodeNavigationItem.addEventListener(WI.CheckboxNavigationItem.Event.CheckedDidChange, this._handleGroupByDOMNodeCheckedDidChange, this);
+        } else
+            WI.settings.groupByDOMNode.value = false;
 
         this._urlFilterSearchText = null;
         this._urlFilterSearchRegex = null;
@@ -213,7 +216,10 @@ WI.NetworkTableContentView = class NetworkTableContentView extends WI.ContentVie
 
     get filterNavigationItems()
     {
-        return [this._urlFilterNavigationItem, this._typeFilterScopeBar, this._groupByDOMNodeNavigationItem];
+        let navigationItems = [this._urlFilterNavigationItem, this._typeFilterScopeBar];
+        if (WI.MediaInstrument.supported())
+            navigationItems.push(this._groupByDOMNodeNavigationItem);
+        return navigationItems;
     }
 
     get supportsSave()
@@ -695,9 +701,9 @@ WI.NetworkTableContentView = class NetworkTableContentView extends WI.ContentVie
 
                     let originator = fullscreenDOMEvents[i].originator || fullscreenDOMEvents[i + 1].originator;
                     if (originator)
-                        fullscreenElement.title = WI.UIString("Fullscreen from “%s“").format(originator.displayName);
+                        fullscreenElement.title = WI.UIString("Full-Screen from “%s“").format(originator.displayName);
                     else
-                        fullscreenElement.title = WI.UIString("Fullscreen");
+                        fullscreenElement.title = WI.UIString("Full-Screen");
                 }
             }
 
@@ -707,7 +713,7 @@ WI.NetworkTableContentView = class NetworkTableContentView extends WI.ContentVie
 
                 let lowPowerElement = container.appendChild(document.createElement("div"));
                 lowPowerElement.classList.add("area", "low-power");
-                lowPowerElement.title = WI.UIString("Low Power Mode");
+                lowPowerElement.title = WI.UIString("Low-Power Mode");
                 positionByStartOffset(lowPowerElement, startTimestamp);
                 setWidthForDuration(lowPowerElement, startTimestamp, endTimestamp);
             }
@@ -1313,9 +1319,7 @@ WI.NetworkTableContentView = class NetworkTableContentView extends WI.ContentVie
             if (object instanceof WI.Resource)
                 this._detailView = new WI.NetworkResourceDetailView(object, this);
             else if (object instanceof WI.DOMNode) {
-                this._detailView = new WI.NetworkDOMNodeDetailView(object, this, {
-                    startTimestamp: this._waterfallStartTime,
-                });
+                this._detailView = new WI.NetworkDOMNodeDetailView(object, this);
             }
 
             this._detailViewMap.set(object, this._detailView);
@@ -1918,9 +1922,7 @@ WI.NetworkTableContentView = class NetworkTableContentView extends WI.ContentVie
     {
         let contentElement = this._waterfallPopoverContent();
 
-        let breakdownView = new WI.DOMEventsBreakdownView(domEvents, {
-            startTimestamp: this._waterfallStartTime,
-        });
+        let breakdownView = new WI.DOMEventsBreakdownView(domEvents);
         contentElement.appendChild(breakdownView.element);
         breakdownView.updateLayout();
 
index ad7239c3480926ccff41b7958792bbde0c8c6656..8d6b2dc83413555af61fa402711fb5b51382e804 100644 (file)
     content: url(../Images/RenderingFramesInstrument.svg);
 }
 
+.media-icon .icon {
+    content: url(../Images/MediaInstrument.svg);
+}
+
 .stopwatch-icon .icon {
     content: url(../Images/Stopwatch.svg);
 }
     content: url(../Images/HeapSnapshot.svg);
 }
 
+.dom-event-record .icon {
+    content: url(../Images/DOMEvent.svg);
+}
+
+.dom-event-record.fullscreen .icon {
+    content: url(../Images/DOMEventFullscreen.svg);
+}
+
+.low-power-record .icon {
+    content: url(../Images/LowPower.svg);
+}
+
 @media (prefers-dark-interface) {
     .time-icon .icon {
         filter: invert();
index 15add52c14db316e1cd8d40d450cda5c4871e414..bb0dbe5735805d4149a16be8e7e4afaf1445a3c9 100644 (file)
@@ -685,6 +685,9 @@ WI.TimelineOverview = class TimelineOverview extends WI.View
             return;
 
         for (let overviewGraph of this._overviewGraphsByTypeMap.values()) {
+            if (!overviewGraph.visible)
+                continue;
+
             let graphRect = overviewGraph.element.getBoundingClientRect();
             if (!(event.pageX >= graphRect.left && event.pageX <= graphRect.right && event.pageY >= graphRect.top && event.pageY <= graphRect.bottom))
                 continue;
index 88c5dfdf6008c59dec00193ab7c8ea36fef447df..a0e12d696aee2013f9926be25ba3b29603606072 100644 (file)
@@ -69,6 +69,9 @@ WI.TimelineOverviewGraph = class TimelineOverviewGraph extends WI.View
         if (timelineType === WI.TimelineRecord.Type.HeapAllocations)
             return new WI.HeapAllocationsTimelineOverviewGraph(timeline, timelineOverview);
 
+        if (timelineType === WI.TimelineRecord.Type.Media)
+            return new WI.MediaTimelineOverviewGraph(timeline, timelineOverview);
+
         throw new Error("Can't make a graph for an unknown timeline.");
     }
 
index 455779959dc76b3ee8e34780aea27cf29095dbf5..7df5e227d2cc02f15085e68bb11ed5f2f49e9280 100644 (file)
@@ -126,3 +126,8 @@ body[dir=rtl] :focus .selected .timeline-record-bar.has-inactive-segment > .segm
     background-color: hsl(23, 69%, 73%);
     border-color: hsl(11, 54%, 62%);    
 }
+
+.timeline-record-bar.timeline-record-type-media > .segment {
+    background-color: hsl(143, 24%, 66%);
+    border-color: hsl(153, 24%, 51%);
+}
index c29f6f8be1057fc4a9152cc00232be889ce885f1..1090fc755bffe4ee6fc8d0fd4dac3e30633ed329 100644 (file)
@@ -368,8 +368,10 @@ WI.TimelineRuler = class TimelineRuler extends WI.View
 
     clearMarkers()
     {
-        for (let markerElement of this._markerElementMap.values())
+        for (let [marker, markerElement] of this._markerElementMap) {
+            marker.removeEventListener(null, null, this);
             markerElement.remove();
+        }
 
         this._markerElementMap.clear();
     }
@@ -728,12 +730,16 @@ WI.TimelineRuler = class TimelineRuler extends WI.View
         if (this._mouseMoved)
             return;
 
-        this.element.style.pointerEvents = "none";
-        let newTarget = document.elementFromPoint(event.pageX, event.pageY);
-        this.element.style.pointerEvents = null;
+        for (let newTarget of document.elementsFromPoint(event.pageX, event.pageY)) {
+            if (!newTarget || typeof newTarget.click !== "function")
+                continue;
+            if (this.element.contains(newTarget))
+                continue;
 
-        if (newTarget && newTarget.click)
-            newTarget.click();
+            // Clone the event to dispatch it on the new element.
+            newTarget.dispatchEvent(new event.constructor(event.type, event));
+            return;
+        }
     }
 
     _handleDoubleClick(event)
index 81cede529646fa4c7cbce674406931f73a51f717..5b57d80f51e4f642231f3ca1c324543e8ab8b7f2 100644 (file)
@@ -120,6 +120,8 @@ WI.TimelineTabContentView = class TimelineTabContentView extends WI.ContentBrows
             return WI.UIString("Memory");
         case WI.TimelineRecord.Type.HeapAllocations:
             return WI.UIString("JavaScript Allocations");
+        case WI.TimelineRecord.Type.Media:
+            return WI.UIString("Media");
         default:
             console.error("Unknown Timeline type:", timelineType);
         }
@@ -142,6 +144,8 @@ WI.TimelineTabContentView = class TimelineTabContentView extends WI.ContentBrows
             return "script-icon";
         case WI.TimelineRecord.Type.RenderingFrame:
             return "rendering-frame-icon";
+        case WI.TimelineRecord.Type.Media:
+            return "media-icon";
         default:
             console.error("Unknown Timeline type:", timelineType);
         }
@@ -164,6 +168,8 @@ WI.TimelineTabContentView = class TimelineTabContentView extends WI.ContentBrows
             return "script";
         case WI.TimelineRecord.Type.RenderingFrame:
             return "rendering-frame";
+        case WI.TimelineRecord.Type.Media:
+            return "media";
         default:
             console.error("Unknown Timeline type:", timelineType);
         }
@@ -229,6 +235,18 @@ WI.TimelineTabContentView = class TimelineTabContentView extends WI.ContentBrows
         case WI.TimelineRecord.Type.HeapAllocations:
             return "heap-snapshot-record";
 
+        case WI.TimelineRecord.Type.Media:
+            switch (timelineRecord.eventType) {
+            case WI.MediaTimelineRecord.EventType.DOMEvent:
+                return "dom-event-record";
+            case WI.MediaTimelineRecord.EventType.LowPower:
+                return "low-power-record";
+            default:
+                console.error("Unknown MediaTimelineRecord eventType: " + timelineRecord.eventType, timelineRecord);
+            }
+
+            break;
+
         case WI.TimelineRecord.Type.Memory:
             // Not used. Fall through to error just in case.
 
@@ -254,6 +272,8 @@ WI.TimelineTabContentView = class TimelineTabContentView extends WI.ContentBrows
             if (timelineRecord.heapSnapshot.title)
                 return WI.UIString("Snapshot %d \u2014 %s").format(timelineRecord.heapSnapshot.identifier, timelineRecord.heapSnapshot.title);
             return WI.UIString("Snapshot %d").format(timelineRecord.heapSnapshot.identifier);
+        case WI.TimelineRecord.Type.Media:
+            return timelineRecord.displayName;
         case WI.TimelineRecord.Type.Memory:
             // Not used. Fall through to error just in case.
         default: