Diff
Modified: trunk/LayoutTests/ChangeLog (243023 => 243024)
--- trunk/LayoutTests/ChangeLog 2019-03-15 23:33:16 UTC (rev 243023)
+++ trunk/LayoutTests/ChangeLog 2019-03-15 23:45:51 UTC (rev 243024)
@@ -1,3 +1,14 @@
+2019-03-15 Joseph Pecoraro <pecor...@apple.com>
+
+ Web Inspector: Timelines - Import / Export Timeline Recordings
+ https://bugs.webkit.org/show_bug.cgi?id=195709
+ <rdar://problem/23188921>
+
+ Reviewed by Devin Rousso.
+
+ * inspector/timeline/timeline-recording-expected.txt: Added.
+ * inspector/timeline/timeline-recording.html: Added.
+
2019-03-15 Zalan Bujtas <za...@apple.com>
[ContentChangeObserver] HTMLImageElement::willRespondToMouseClickEvents returns quirk value.
Added: trunk/LayoutTests/inspector/timeline/timeline-recording-expected.txt (0 => 243024)
--- trunk/LayoutTests/inspector/timeline/timeline-recording-expected.txt (rev 0)
+++ trunk/LayoutTests/inspector/timeline/timeline-recording-expected.txt 2019-03-15 23:45:51 UTC (rev 243024)
@@ -0,0 +1,70 @@
+Tests for timeline recording.
+
+
+== Running test suite: TimelineRecording
+-- Running test case: TimelineRecording.Basic
+Loaded
+PASS: TimelineManager should not be capturing.
+PASS: TimelineRecording should not be capturing.
+PASS: TimelineRecording should not be readonly.
+PASS: TimelineRecording should not be imported.
+PASS: TimelineRecording should not have a startTime.
+PASS: TimelineRecording should not have a endTime.
+Start
+PASS: TimelineManager should be capturing.
+PASS: TimelineManager active recording should not have changed.
+PASS: TimelineRecording should be capturing.
+PASS: TimelineRecording should not be readonly.
+Reload
+Stop
+PASS: TimelineManager should not be capturing.
+PASS: TimelineRecording should not be capturing.
+PASS: TimelineRecording should not be readonly.
+PASS: TimelineRecording should not be imported.
+PASS: TimelineRecording should have a startTime.
+PASS: TimelineRecording should have a endTime.
+
+-- Running test case: TimelineRecording.prototype.exportData
+PASS: TimelineRecording should be able to export.
+PASS: TimelineRecording should be able to produce export data.
+PASS: TimelineRecording should have at least 10 Timeline Records.
+Export Data:
+{
+ "displayName": "<filtered>",
+ "startTime": "<filtered>",
+ "endTime": "<filtered>",
+ "discontinuities": [],
+ "instrumentTypes": [
+ "timeline-record-type-network",
+ "timeline-record-type-layout",
+ "timeline-record-type-script",
+ "timeline-record-type-cpu",
+ "timeline-record-type-rendering-frame"
+ ],
+ "records": "<filtered>",
+ "markers": [
+ {
+ "time": "<filtered>",
+ "type": "dom-content-event"
+ },
+ {
+ "time": "<filtered>",
+ "type": "load-event"
+ }
+ ],
+ "memoryPressureEvents": [],
+ "sampleStackTraces": [],
+ "sampleDurations": []
+}
+
+-- Running test case: TimelineRecording.import
+PASS: TimelineManager active recording is not this imported recording.
+PASS: TimelineRecording should not be capturing.
+PASS: TimelineRecording should be readonly.
+PASS: TimelineRecording should be imported.
+PASS: TimelineRecording should have a startTime.
+PASS: TimelineRecording should have a endTime.
+PASS: TimelineRecording identifier should be 999.
+Display Name:
+Imported - TEST
+
Added: trunk/LayoutTests/inspector/timeline/timeline-recording.html (0 => 243024)
--- trunk/LayoutTests/inspector/timeline/timeline-recording.html (rev 0)
+++ trunk/LayoutTests/inspector/timeline/timeline-recording.html 2019-03-15 23:45:51 UTC (rev 243024)
@@ -0,0 +1,108 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src=""
+<script>
+function test()
+{
+ let suite = InspectorTest.createAsyncSuite("TimelineRecording");
+
+ let exportData = null;
+
+ suite.addTestCase({
+ name: "TimelineRecording.Basic",
+ description: "Make a basic Timeline recording.",
+ async test() {
+ let recording = WI.timelineManager.activeRecording;
+
+ InspectorTest.log("Loaded");
+ InspectorTest.expectFalse(WI.timelineManager.isCapturing(), "TimelineManager should not be capturing.");
+ InspectorTest.expectFalse(recording.capturing, "TimelineRecording should not be capturing.");
+ InspectorTest.expectFalse(recording.readonly, "TimelineRecording should not be readonly.");
+ InspectorTest.expectFalse(recording.imported, "TimelineRecording should not be imported.");
+ InspectorTest.expectThat(isNaN(recording.startTime), "TimelineRecording should not have a startTime.");
+ InspectorTest.expectThat(isNaN(recording.endTime), "TimelineRecording should not have a endTime.");
+
+ InspectorTest.log("Start");
+ WI.timelineManager.startCapturing();
+ await WI.timelineManager.awaitEvent(WI.TimelineManager.Event.CapturingStarted);
+ InspectorTest.expectTrue(WI.timelineManager.isCapturing(), "TimelineManager should be capturing.");
+ InspectorTest.expectEqual(WI.timelineManager.activeRecording, recording, "TimelineManager active recording should not have changed.");
+ InspectorTest.expectTrue(recording.capturing, "TimelineRecording should be capturing.");
+ InspectorTest.expectFalse(recording.readonly, "TimelineRecording should not be readonly.");
+
+ InspectorTest.log("Reload");
+ await Promise.all([
+ InspectorTest.awaitEvent(FrontendTestHarness.Event.TestPageDidLoad),
+ InspectorTest.reloadPage(),
+ ]);
+
+ InspectorTest.log("Stop");
+ WI.timelineManager.stopCapturing();
+ InspectorTest.expectFalse(WI.timelineManager.isCapturing(), "TimelineManager should not be capturing.");
+ InspectorTest.expectFalse(recording.capturing, "TimelineRecording should not be capturing.");
+ InspectorTest.expectFalse(recording.readonly, "TimelineRecording should not be readonly.");
+ InspectorTest.expectFalse(recording.imported, "TimelineRecording should not be imported.");
+ InspectorTest.expectThat(!isNaN(recording.startTime), "TimelineRecording should have a startTime.");
+ InspectorTest.expectThat(!isNaN(recording.endTime), "TimelineRecording should have a endTime.");
+ }
+ });
+
+ suite.addTestCase({
+ name: "TimelineRecording.prototype.exportData",
+ description: "Test for a recording export.",
+ async test() {
+ let recording = WI.timelineManager.activeRecording;
+ InspectorTest.assert(!isNaN(recording.startTime), "FAIL: Previous test loading a recording failed.");
+ InspectorTest.assert(!isNaN(recording.endTime), "FAIL: Previous test loading a recording failed.");
+
+ InspectorTest.expectTrue(recording.canExport(), "TimelineRecording should be able to export.");
+
+ exportData = recording.exportData();
+ InspectorTest.expectThat(exportData, "TimelineRecording should be able to produce export data.");
+ InspectorTest.expectThat(exportData.records.length > 10, "TimelineRecording should have at least 10 Timeline Records.");
+
+ InspectorTest.log("Export Data:");
+ let filterKeys = new Set(["startTime", "endTime", "time", "records", "displayName"]);
+ InspectorTest.json(exportData, (key, value) => {
+ if (filterKeys.has(key))
+ return "<filtered>";
+ return value;
+ });
+ }
+ });
+
+ suite.addTestCase({
+ name: "TimelineRecording.import",
+ description: "Test for a recording import.",
+ async test() {
+ InspectorTest.assert(exportData, "FAIL: Previous test exporting a recording failed.");
+
+ // NOTE: This runs the toJSON handlers on the timeline records and other model objects,
+ // which is important because importing expects the serialized form of the objects, not
+ // actual model objects.
+ let jsonData = JSON.parse(JSON.stringify(exportData));
+
+ const identifier = 999;
+ let recording = WI.TimelineRecording.import(identifier, jsonData, "TEST");
+ InspectorTest.expectNotEqual(WI.timelineManager.activeRecording, recording, "TimelineManager active recording is not this imported recording.");
+ InspectorTest.expectFalse(recording.capturing, "TimelineRecording should not be capturing.");
+ InspectorTest.expectTrue(recording.readonly, "TimelineRecording should be readonly.");
+ InspectorTest.expectTrue(recording.imported, "TimelineRecording should be imported.");
+ InspectorTest.expectThat(!isNaN(recording.startTime), "TimelineRecording should have a startTime.");
+ InspectorTest.expectThat(!isNaN(recording.endTime), "TimelineRecording should have a endTime.");
+ InspectorTest.expectEqual(recording.identifier, identifier, `TimelineRecording identifier should be ${identifier}.`);
+
+ InspectorTest.log("Display Name:");
+ InspectorTest.log(recording.displayName);
+ }
+ });
+
+ suite.runTestCasesAndFinish();
+}
+</script>
+</head>
+<body _onload_="runTest()">
+<p>Tests for timeline recording.</p>
+</body>
+</html>
Modified: trunk/Source/WebInspectorUI/ChangeLog (243023 => 243024)
--- trunk/Source/WebInspectorUI/ChangeLog 2019-03-15 23:33:16 UTC (rev 243023)
+++ trunk/Source/WebInspectorUI/ChangeLog 2019-03-15 23:45:51 UTC (rev 243024)
@@ -1,5 +1,183 @@
2019-03-15 Joseph Pecoraro <pecor...@apple.com>
+ Web Inspector: Timelines - Import / Export Timeline Recordings
+ https://bugs.webkit.org/show_bug.cgi?id=195709
+ <rdar://problem/23188921>
+
+ Reviewed by Devin Rousso.
+
+ Timeline exporting saves TimelineRecording and TimelineOverview state.
+ The TimelineRecording includes all kinds of model objects, such as
+ records, markers, memory pressure events, etc. It also includes raw
+ protocol data, such as script profiler samples. TimelineOverview
+ includes some of the view state to restore, such as the selected
+ time range, zoom level, and selected timeline.
+
+ Timeline importing constructs a new TimelineRecording by replaying
+ the records, markers, and other events, as well as re-initializing
+ more state. To finally display the imported recording, the content
+ view will immediately initialize start/current/end times and the
+ overview will restore the view state.
+
+ * Localizations/en.lproj/localizedStrings.js:
+ New strings.
+
+ * UserInterface/Controllers/TimelineManager.js:
+ (WI.TimelineManager.synthesizeImportError):
+ (WI.TimelineManager.prototype.importRecording):
+ Import API.
+
+ (WI.TimelineManager.prototype.scriptProfilerTrackingCompleted):
+ Initialize the samples on the recording via a different path
+ so that the data can be saved for exporting.
+
+ * UserInterface/Models/TimelineRecording.js:
+ (WI.TimelineRecording):
+ (WI.TimelineRecording.import):
+ (WI.TimelineRecording.prototype.exportData):
+ (WI.TimelineRecording.prototype.get capturing):
+ (WI.TimelineRecording.prototype.get imported):
+ (WI.TimelineRecording.prototype.unloaded):
+ (WI.TimelineRecording.prototype.reset):
+ (WI.TimelineRecording.prototype.addEventMarker):
+ (WI.TimelineRecording.prototype.addRecord):
+ (WI.TimelineRecording.prototype.addMemoryPressureEvent):
+ (WI.TimelineRecording.prototype.initializeCallingContextTrees):
+ (WI.TimelineRecording.prototype.canExport):
+ Save data at the TimelineRecording level that can be used for export.
+ We only allow exporting a TimelineRecording that has started/stopped
+ at least once and is not currently capturing.
+
+ * UserInterface/Views/TimelineRecordingContentView.js:
+ (WI.TimelineRecordingContentView):
+ (WI.TimelineRecordingContentView.prototype.get navigationItems):
+ (WI.TimelineRecordingContentView.prototype.get supportsSave):
+ (WI.TimelineRecordingContentView.prototype.get saveData):
+ (WI.TimelineRecordingContentView.prototype.shown):
+ (WI.TimelineRecordingContentView.prototype._capturingStarted):
+ (WI.TimelineRecordingContentView.prototype._capturingStopped):
+ (WI.TimelineRecordingContentView.prototype._initializeImportedRecording):
+ (WI.TimelineRecordingContentView.prototype._exportTimelineRecording):
+ (WI.TimelineRecordingContentView.prototype._importButtonNavigationItemClicked):
+ (WI.TimelineRecordingContentView.prototype._recordingReset):
+ Add Import and Export buttons in the Timeline navigation bar.
+
+ * UserInterface/Views/TimelineOverview.js:
+ (WI.TimelineOverview):
+ (WI.TimelineOverview.prototype.exportData):
+ (WI.TimelineOverview.prototype._instrumentAdded):
+ (WI.TimelineOverview.prototype._recordingImported):
+ When importing a recording update the TimelineOverview state
+ soon afterwards.
+
+ * UserInterface/Models/CPUTimelineRecord.js:
+ (WI.CPUTimelineRecord.fromJSON):
+ (WI.CPUTimelineRecord.prototype.toJSON):
+ * UserInterface/Models/GarbageCollection.js:
+ (WI.GarbageCollection.fromJSON):
+ (WI.GarbageCollection.prototype.toJSON):
+ * UserInterface/Models/Geometry.js:
+ (WI.Quad.fromJSON):
+ (WI.Quad.prototype.toJSON):
+ * UserInterface/Models/HeapAllocationsTimelineRecord.js:
+ (WI.HeapAllocationsTimelineRecord.fromJSON):
+ (WI.HeapAllocationsTimelineRecord.prototype.toJSON):
+ * UserInterface/Models/LayoutTimelineRecord.js:
+ (WI.LayoutTimelineRecord.fromJSON):
+ (WI.LayoutTimelineRecord.prototype.toJSON):
+ * UserInterface/Models/MediaTimelineRecord.js:
+ (WI.MediaTimelineRecord.fromJSON):
+ (WI.MediaTimelineRecord.prototype.toJSON):
+ * UserInterface/Models/MemoryPressureEvent.js:
+ (WI.MemoryPressureEvent.fromJSON):
+ (WI.MemoryPressureEvent.prototype.toJSON):
+ * UserInterface/Models/MemoryTimelineRecord.js:
+ (WI.MemoryTimelineRecord):
+ (WI.MemoryTimelineRecord.fromJSON):
+ (WI.MemoryTimelineRecord.prototype.toJSON):
+ * UserInterface/Models/RenderingFrameTimelineRecord.js:
+ (WI.RenderingFrameTimelineRecord.fromJSON):
+ (WI.RenderingFrameTimelineRecord.prototype.toJSON):
+ * UserInterface/Models/ResourceTimelineRecord.js:
+ (WI.ResourceTimelineRecord.fromJSON):
+ (WI.ResourceTimelineRecord.prototype.toJSON):
+ * UserInterface/Models/ScriptTimelineRecord.js:
+ (WI.ScriptTimelineRecord.fromJSON):
+ (WI.ScriptTimelineRecord.prototype.toJSON):
+ * UserInterface/Models/TimelineMarker.js:
+ (WI.TimelineMarker.fromJSON):
+ (WI.TimelineMarker.prototype.toJSON):
+ (WI.TimelineMarker.prototype.get type):
+ (WI.TimelineMarker.prototype.get details):
+ (WI.TimelineMarker.prototype.set time):
+ (WI.TimelineMarker):
+ * UserInterface/Models/TimelineRecord.js:
+ (WI.TimelineRecord.fromJSON):
+ (WI.TimelineRecord.prototype.toJSON):
+ Import / Export toJSON / fromJSON implementations.
+
+ * UserInterface/Views/CPUTimelineOverviewGraph.js:
+ (WI.CPUTimelineOverviewGraph):
+ (WI.CPUTimelineOverviewGraph.prototype._cpuTimelineRecordAdded):
+ (WI.CPUTimelineOverviewGraph.prototype._processRecord):
+ * UserInterface/Views/LayoutTimelineOverviewGraph.js:
+ (WI.LayoutTimelineOverviewGraph):
+ (WI.LayoutTimelineOverviewGraph.prototype._layoutTimelineRecordAdded):
+ (WI.LayoutTimelineOverviewGraph.prototype._processRecord):
+ * UserInterface/Views/LayoutTimelineView.js:
+ (WI.LayoutTimelineView):
+ (WI.LayoutTimelineView.prototype._layoutTimelineRecordAdded):
+ (WI.LayoutTimelineView.prototype._processRecord):
+ * UserInterface/Views/MediaTimelineView.js:
+ (WI.MediaTimelineView):
+ (WI.MediaTimelineView.prototype._handleRecordAdded):
+ (WI.MediaTimelineView.prototype._processRecord):
+ * UserInterface/Views/MemoryTimelineOverviewGraph.js:
+ (WI.MemoryTimelineOverviewGraph):
+ (WI.MemoryTimelineOverviewGraph.prototype._memoryTimelineRecordAdded):
+ (WI.MemoryTimelineOverviewGraph.prototype._processRecord):
+ * UserInterface/Views/MemoryTimelineView.js:
+ (WI.MemoryTimelineView):
+ (WI.MemoryTimelineView.prototype._memoryTimelineRecordAdded):
+ (WI.MemoryTimelineView.prototype._processRecord):
+ * UserInterface/Views/NetworkTimelineOverviewGraph.js:
+ (WI.NetworkTimelineOverviewGraph):
+ (WI.NetworkTimelineOverviewGraph.prototype.reset):
+ (WI.NetworkTimelineOverviewGraph.prototype._networkTimelineRecordAdded):
+ (WI.NetworkTimelineOverviewGraph.prototype._processRecord):
+ (WI.NetworkTimelineOverviewGraph.prototype._networkTimelineRecordAdded.compareByStartTime): Deleted.
+ * UserInterface/Views/NetworkTimelineView.js:
+ (WI.NetworkTimelineView):
+ (WI.NetworkTimelineView.prototype._networkTimelineRecordAdded):
+ (WI.NetworkTimelineView.prototype._processRecord):
+ * UserInterface/Views/RenderingFrameTimelineView.js:
+ (WI.RenderingFrameTimelineView):
+ (WI.RenderingFrameTimelineView.prototype._renderingFrameTimelineRecordAdded):
+ (WI.RenderingFrameTimelineView.prototype._processRecord):
+ * UserInterface/Views/ScriptDetailsTimelineView.js:
+ (WI.ScriptDetailsTimelineView):
+ (WI.ScriptDetailsTimelineView.prototype._scriptTimelineRecordAdded):
+ (WI.ScriptDetailsTimelineView.prototype._processRecord):
+ Add common _processRecord path to each timeline OverviewGraph and TimelineView.
+ By calling this in construction we populate graphs with TimelineRecords that
+ may have already existed. This is necessary for imports, but this also fixes
+ the case where you enable a timeline that had data and it didn't show data.
+
+ * UserInterface/Views/LayoutTimelineOverviewGraph.css:
+ (.timeline-overview-graph.layout-overview > .graph-row):
+ (.timeline-overview-graph.layout-overview > .graph-row > .timeline-record-bar):
+ (.timeline-overview-graph.layout-overview > .graph-row > .timeline-record-bar > .segment):
+ (.timeline-overview-graph.layout > .graph-row): Deleted.
+ (.timeline-overview-graph.layout > .graph-row > .timeline-record-bar): Deleted.
+ (.timeline-overview-graph.layout > .graph-row > .timeline-record-bar > .segment): Deleted.
+ * UserInterface/Views/TimelineRecordBar.css:
+ (.timeline-record-bar.timeline-record-type-layout.paint > .segment,):
+ (.timeline-record-bar.timeline-record-type-layout.layout-timeline-record-paint > .segment,): Deleted.
+ We simplified some of the sub-record type enum strings. To do this we needed to change
+ "layout" to "layout-overview" to avoid a conflict.
+
+2019-03-15 Joseph Pecoraro <pecor...@apple.com>
+
Web Inspector: Network - Toggle Between Live Activity and Imported HAR resource collections
https://bugs.webkit.org/show_bug.cgi?id=195734
Modified: trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js (243023 => 243024)
--- trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js 2019-03-15 23:33:16 UTC (rev 243023)
+++ trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js 2019-03-15 23:45:51 UTC (rev 243024)
@@ -554,6 +554,7 @@
localizedStrings["Imported"] = "Imported";
localizedStrings["Imported - %s"] = "Imported - %s";
localizedStrings["Imported Recordings"] = "Imported Recordings";
+localizedStrings["Imported Timeline Recording"] = "Imported Timeline Recording";
localizedStrings["Imported \u2014 %s"] = "Imported \u2014 %s";
localizedStrings["Incomplete"] = "Incomplete";
localizedStrings["Indent width:"] = "Indent width:";
@@ -1055,6 +1056,7 @@
localizedStrings["Time to First Byte"] = "Time to First Byte";
localizedStrings["Timeline"] = "Timeline";
localizedStrings["Timeline Recording %d"] = "Timeline Recording %d";
+localizedStrings["Timeline Recording Import Error: %s"] = "Timeline Recording Import Error: %s";
localizedStrings["Timelines"] = "Timelines";
localizedStrings["Timer %d Fired"] = "Timer %d Fired";
localizedStrings["Timer %d Installed"] = "Timer %d Installed";
@@ -1189,4 +1191,5 @@
localizedStrings["unknown %s \u0022%s\u0022"] = "unknown %s \u0022%s\u0022";
localizedStrings["unsupported %s"] = "unsupported %s";
localizedStrings["unsupported HAR version"] = "unsupported HAR version";
+localizedStrings["unsupported version"] = "unsupported version";
localizedStrings["value"] = "value";
Modified: trunk/Source/WebInspectorUI/UserInterface/Base/Main.js (243023 => 243024)
--- trunk/Source/WebInspectorUI/UserInterface/Base/Main.js 2019-03-15 23:33:16 UTC (rev 243023)
+++ trunk/Source/WebInspectorUI/UserInterface/Base/Main.js 2019-03-15 23:45:51 UTC (rev 243024)
@@ -2736,7 +2736,7 @@
WI.createMessageTextView = function(message, isError)
{
- var messageElement = document.createElement("div");
+ let messageElement = document.createElement("div");
messageElement.className = "message-text-view";
if (isError)
messageElement.classList.add("error");
Modified: trunk/Source/WebInspectorUI/UserInterface/Controllers/TimelineManager.js (243023 => 243024)
--- trunk/Source/WebInspectorUI/UserInterface/Controllers/TimelineManager.js 2019-03-15 23:33:16 UTC (rev 243023)
+++ trunk/Source/WebInspectorUI/UserInterface/Controllers/TimelineManager.js 2019-03-15 23:45:51 UTC (rev 243024)
@@ -136,6 +136,21 @@
return types;
}
+ static synthesizeImportError(message)
+ {
+ message = WI.UIString("Timeline Recording Import Error: %s").format(message);
+
+ if (window.InspectorTest) {
+ console.error(message);
+ return;
+ }
+
+ let consoleMessage = new WI.ConsoleMessage(WI.mainTarget, WI.ConsoleMessage.MessageSource.Other, WI.ConsoleMessage.MessageLevel.Error, message);
+ consoleMessage.shouldRevealConsole = true;
+
+ WI.consoleLogViewController.appendConsoleMessage(consoleMessage);
+ }
+
// Public
reset()
@@ -259,6 +274,52 @@
this._activeRecording = null;
}
+ processJSON({filename, json, error})
+ {
+ if (error) {
+ WI.TimelineManager.synthesizeImportError(error);
+ return;
+ }
+
+ if (typeof json !== "object" || json === null) {
+ WI.TimelineManager.synthesizeImportError(WI.UIString("invalid JSON"));
+ return;
+ }
+
+ if (!json.recording || typeof json.recording !== "object" || !json.overview || typeof json.overview !== "object" || typeof json.version !== "number") {
+ WI.TimelineManager.synthesizeImportError(WI.UIString("invalid JSON"));
+ return;
+ }
+
+ if (json.version !== WI.TimelineRecording.SerializationVersion) {
+ WI.NetworkManager.synthesizeImportError(WI.UIString("unsupported version"));
+ return;
+ }
+
+ let recordingData = json.recording;
+ let overviewData = json.overview;
+
+ let identifier = this._nextRecordingIdentifier++;
+ let newRecording = WI.TimelineRecording.import(identifier, recordingData, filename);
+ this._recordings.push(newRecording);
+
+ this.dispatchEventToListeners(WI.TimelineManager.Event.RecordingCreated, {recording: newRecording});
+
+ if (this._isCapturing)
+ this.stopCapturing();
+
+ let oldRecording = this._activeRecording;
+ if (oldRecording) {
+ const importing = true;
+ oldRecording.unloaded(importing);
+ }
+
+ this._activeRecording = newRecording;
+
+ this.dispatchEventToListeners(WI.TimelineManager.Event.RecordingLoaded, {oldRecording});
+ this.dispatchEventToListeners(WI.TimelineManager.Event.RecordingImported, {overviewData});
+ }
+
computeElapsedTime(timestamp)
{
if (!this._activeRecording)
@@ -1026,9 +1087,6 @@
if (samples) {
let {stackTraces} = samples;
let topDownCallingContextTree = this.activeRecording.topDownCallingContextTree;
- let bottomUpCallingContextTree = this.activeRecording.bottomUpCallingContextTree;
- let topFunctionsTopDownCallingContextTree = this.activeRecording.topFunctionsTopDownCallingContextTree;
- let topFunctionsBottomUpCallingContextTree = this.activeRecording.topFunctionsBottomUpCallingContextTree;
// Calculate a per-sample duration.
let timestampIndex = 0;
@@ -1062,12 +1120,7 @@
if (timestampIndex < timestampCount)
sampleDurations.fill(defaultDuration, sampleDurationIndex);
- for (let i = 0; i < stackTraces.length; i++) {
- topDownCallingContextTree.updateTreeWithStackTrace(stackTraces[i], sampleDurations[i]);
- bottomUpCallingContextTree.updateTreeWithStackTrace(stackTraces[i], sampleDurations[i]);
- topFunctionsTopDownCallingContextTree.updateTreeWithStackTrace(stackTraces[i], sampleDurations[i]);
- topFunctionsBottomUpCallingContextTree.updateTreeWithStackTrace(stackTraces[i], sampleDurations[i]);
- }
+ this.activeRecording.initializeCallingContextTrees(stackTraces, sampleDurations);
// FIXME: This transformation should not be needed after introducing ProfileView.
// Once we eliminate ProfileNodeTreeElements and ProfileNodeDataGridNodes.
@@ -1213,6 +1266,7 @@
WI.TimelineManager.Event = {
RecordingCreated: "timeline-manager-recording-created",
RecordingLoaded: "timeline-manager-recording-loaded",
+ RecordingImported: "timeline-manager-recording-imported",
CapturingWillStart: "timeline-manager-capturing-will-start",
CapturingStarted: "timeline-manager-capturing-started",
CapturingStopped: "timeline-manager-capturing-stopped"
Modified: trunk/Source/WebInspectorUI/UserInterface/Main.html (243023 => 243024)
--- trunk/Source/WebInspectorUI/UserInterface/Main.html 2019-03-15 23:33:16 UTC (rev 243023)
+++ trunk/Source/WebInspectorUI/UserInterface/Main.html 2019-03-15 23:45:51 UTC (rev 243024)
@@ -222,6 +222,7 @@
<link rel="stylesheet" href=""
<link rel="stylesheet" href=""
<link rel="stylesheet" href=""
+ <link rel="stylesheet" href=""
<link rel="stylesheet" href=""
<link rel="stylesheet" href=""
<link rel="stylesheet" href=""
@@ -826,6 +827,7 @@
<script src=""
<script src=""
<script src=""
+ <script src=""
<script src=""
<script src=""
<script src=""
Modified: trunk/Source/WebInspectorUI/UserInterface/Models/CPUTimelineRecord.js (243023 => 243024)
--- trunk/Source/WebInspectorUI/UserInterface/Models/CPUTimelineRecord.js 2019-03-15 23:33:16 UTC (rev 243023)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/CPUTimelineRecord.js 2019-03-15 23:45:51 UTC (rev 243024)
@@ -36,9 +36,8 @@
this._timestamp = timestamp;
this._usage = usage;
+ this._threads = threads || [];
- threads = threads || [];
-
this._mainThreadUsage = 0;
this._webkitThreadUsage = 0;
this._workerThreadUsage = 0;
@@ -45,7 +44,7 @@
this._unknownThreadUsage = 0;
this._workersData = null;
- for (let thread of threads) {
+ for (let thread of this._threads) {
if (thread.type === InspectorBackend.domains.CPUProfiler.ThreadInfoType.Main) {
console.assert(!this._mainThreadUsage, "There should only be one main thread.");
this._mainThreadUsage += thread.usage;
@@ -69,6 +68,23 @@
}
}
+ // Import / Export
+
+ static fromJSON(json)
+ {
+ return new WI.CPUTimelineRecord(json);
+ }
+
+ toJSON()
+ {
+ return {
+ type: this.type,
+ timestamp: this._timestamp,
+ usage: this._usage,
+ threads: this._threads,
+ };
+ }
+
// Public
get timestamp() { return this._timestamp; }
Modified: trunk/Source/WebInspectorUI/UserInterface/Models/GarbageCollection.js (243023 => 243024)
--- trunk/Source/WebInspectorUI/UserInterface/Models/GarbageCollection.js 2019-03-15 23:33:16 UTC (rev 243023)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/GarbageCollection.js 2019-03-15 23:45:51 UTC (rev 243024)
@@ -45,6 +45,24 @@
return new WI.GarbageCollection(type, payload.startTime, payload.endTime);
}
+ // Import / Export
+
+ static fromJSON(json)
+ {
+ let {type, startTime, endTime} = json;
+ return new WI.GarbageCollection(type, startTime, endTime);
+ }
+
+ toJSON()
+ {
+ return {
+ __type: "GarbageCollection",
+ type: this.type,
+ startTime: this.startTime,
+ endTime: this.endTime,
+ };
+ }
+
// Public
get type() { return this._type; }
@@ -58,6 +76,6 @@
};
WI.GarbageCollection.Type = {
- Partial: Symbol("Partial"),
- Full: Symbol("Full")
+ Partial: "partial",
+ Full: "full",
};
Modified: trunk/Source/WebInspectorUI/UserInterface/Models/Geometry.js (243023 => 243024)
--- trunk/Source/WebInspectorUI/UserInterface/Models/Geometry.js 2019-03-15 23:33:16 UTC (rev 243023)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/Geometry.js 2019-03-15 23:45:51 UTC (rev 243024)
@@ -284,6 +284,18 @@
this.height = Math.round(Math.sqrt(Math.pow(quad[0] - quad[6], 2) + Math.pow(quad[1] - quad[7], 2)));
}
+ // Import / Export
+
+ static fromJSON(json)
+ {
+ return new WI.Quad(json);
+ }
+
+ toJSON()
+ {
+ return this.toProtocol();
+ }
+
// Public
toProtocol()
Modified: trunk/Source/WebInspectorUI/UserInterface/Models/HeapAllocationsTimelineRecord.js (243023 => 243024)
--- trunk/Source/WebInspectorUI/UserInterface/Models/HeapAllocationsTimelineRecord.js 2019-03-15 23:33:16 UTC (rev 243023)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/HeapAllocationsTimelineRecord.js 2019-03-15 23:45:51 UTC (rev 243024)
@@ -36,6 +36,36 @@
this._heapSnapshot = heapSnapshot;
}
+ // Import / Export
+
+ static fromJSON(json)
+ {
+ // NOTE: This just goes through and generates a new heap snapshot,
+ // it is not perfect but does what we want. It asynchronously creates
+ // a heap snapshot at the right time, and insert it into the active
+ // recording, which on an import should be the newly imported recording.
+ let {timestamp, title, snapshotStringData} = json;
+
+ let workerProxy = WI.HeapSnapshotWorkerProxy.singleton();
+ workerProxy.createImportedSnapshot(snapshotStringData, title, ({objectId, snapshot: serializedSnapshot}) => {
+ let snapshot = WI.HeapSnapshotProxy.deserialize(objectId, serializedSnapshot);
+ snapshot.snapshotStringData = snapshotStringData;
+ WI.timelineManager.heapSnapshotAdded(timestamp, snapshot);
+ });
+
+ return null;
+ }
+
+ toJSON()
+ {
+ return {
+ type: this.type,
+ timestamp: this._timestamp,
+ title: WI.TimelineTabContentView.displayNameForRecord(this),
+ snapshotStringData: this._heapSnapshot.snapshotStringData,
+ };
+ }
+
// Public
get timestamp() { return this._timestamp; }
Modified: trunk/Source/WebInspectorUI/UserInterface/Models/LayoutTimelineRecord.js (243023 => 243024)
--- trunk/Source/WebInspectorUI/UserInterface/Models/LayoutTimelineRecord.js 2019-03-15 23:33:16 UTC (rev 243023)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/LayoutTimelineRecord.js 2019-03-15 23:45:51 UTC (rev 243024)
@@ -61,6 +61,29 @@
}
}
+ // Import / Export
+
+ static fromJSON(json)
+ {
+ let {eventType, startTime, endTime, callFrames, sourceCodeLocation, quad} = json;
+ quad = quad ? WI.Quad.fromJSON(quad) : null;
+ return new WI.LayoutTimelineRecord(eventType, startTime, endTime, callFrames, sourceCodeLocation, quad);
+ }
+
+ toJSON()
+ {
+ // FIXME: CallFrames
+ // FIXME: SourceCodeLocation
+
+ return {
+ type: this.type,
+ eventType: this._eventType,
+ startTime: this.startTime,
+ endTime: this.endTime,
+ quad: this._quad || undefined,
+ }
+ }
+
// Public
get eventType()
@@ -97,13 +120,13 @@
};
WI.LayoutTimelineRecord.EventType = {
- InvalidateStyles: "layout-timeline-record-invalidate-styles",
- RecalculateStyles: "layout-timeline-record-recalculate-styles",
- InvalidateLayout: "layout-timeline-record-invalidate-layout",
- ForcedLayout: "layout-timeline-record-forced-layout",
- Layout: "layout-timeline-record-layout",
- Paint: "layout-timeline-record-paint",
- Composite: "layout-timeline-record-composite"
+ InvalidateStyles: "invalidate-styles",
+ RecalculateStyles: "recalculate-styles",
+ InvalidateLayout: "invalidate-layout",
+ ForcedLayout: "forced-layout",
+ Layout: "layout",
+ Paint: "paint",
+ Composite: "composite"
};
WI.LayoutTimelineRecord.TypeIdentifier = "layout-timeline-record";
Modified: trunk/Source/WebInspectorUI/UserInterface/Models/MediaTimelineRecord.js (243023 => 243024)
--- trunk/Source/WebInspectorUI/UserInterface/Models/MediaTimelineRecord.js 2019-03-15 23:33:16 UTC (rev 243023)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/MediaTimelineRecord.js 2019-03-15 23:45:51 UTC (rev 243024)
@@ -37,6 +37,27 @@
this._isLowPower = isLowPower || false;
}
+ // Import / Export
+
+ static fromJSON(json)
+ {
+ let {eventType, timestamp} = json;
+ return new WI.MediaTimelineRecord(eventType, timestamp, json);
+ }
+
+ toJSON()
+ {
+ // FIXME: DOMNode
+
+ return {
+ type: this.type,
+ eventType: this._eventType,
+ timestamp: this.startTime,
+ domEvent: this._domEvent,
+ isLowPower: this._isLowPower,
+ };
+ }
+
// Public
get eventType() { return this._eventType; }
Modified: trunk/Source/WebInspectorUI/UserInterface/Models/MemoryPressureEvent.js (243023 => 243024)
--- trunk/Source/WebInspectorUI/UserInterface/Models/MemoryPressureEvent.js 2019-03-15 23:33:16 UTC (rev 243023)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/MemoryPressureEvent.js 2019-03-15 23:45:51 UTC (rev 243024)
@@ -52,6 +52,23 @@
return new WI.MemoryPressureEvent(timestamp, severity);
}
+ // Import / Export
+
+
+ static fromJSON(json)
+ {
+ let {timestamp, severity} = json;
+ return new WI.MemoryPressureEvent(timestamp, severity);
+ }
+
+ toJSON()
+ {
+ return {
+ timestamp: this._timestamp,
+ severity: this._severity,
+ };
+ }
+
// Public
get timestamp() { return this._timestamp; }
@@ -59,6 +76,6 @@
};
WI.MemoryPressureEvent.Severity = {
- Critical: Symbol("Critical"),
- NonCritical: Symbol("NonCritical"),
+ Critical: "critical",
+ NonCritical: "non-critical",
};
Modified: trunk/Source/WebInspectorUI/UserInterface/Models/MemoryTimelineRecord.js (243023 => 243024)
--- trunk/Source/WebInspectorUI/UserInterface/Models/MemoryTimelineRecord.js 2019-03-15 23:33:16 UTC (rev 243023)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/MemoryTimelineRecord.js 2019-03-15 23:45:51 UTC (rev 243024)
@@ -34,6 +34,7 @@
this._timestamp = timestamp;
this._categories = WI.MemoryTimelineRecord.memoryCategoriesFromProtocol(categories);
+ this._exportCategories = categories;
this._totalSize = 0;
for (let {size} of categories)
@@ -79,6 +80,23 @@
];
}
+ // Import / Export
+
+ static fromJSON(json)
+ {
+ let {timestamp, categories} = json;
+ return new WI.MemoryTimelineRecord(timestamp, categories);
+ }
+
+ toJSON()
+ {
+ return {
+ type: this.type,
+ timestamp: this.startTime,
+ categories: this._exportCategories,
+ };
+ }
+
// Public
get timestamp() { return this._timestamp; }
Modified: trunk/Source/WebInspectorUI/UserInterface/Models/RenderingFrameTimelineRecord.js (243023 => 243024)
--- trunk/Source/WebInspectorUI/UserInterface/Models/RenderingFrameTimelineRecord.js 2019-03-15 23:33:16 UTC (rev 243023)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/RenderingFrameTimelineRecord.js 2019-03-15 23:45:51 UTC (rev 243024)
@@ -69,6 +69,27 @@
}
}
+ // Import / Export
+
+ static fromJSON(json)
+ {
+ let {startTime, endTime} = json;
+ let record = new WI.RenderingFrameTimelineRecord(startTime, endTime);
+ record.setupFrameIndex();
+ return record;
+ }
+
+ toJSON()
+ {
+ // FIXME: durationByTaskType data cannot be calculated if this does not have children.
+
+ return {
+ type: this.type,
+ startTime: this.startTime,
+ endTime: this.endTime,
+ };
+ }
+
// Public
get frameIndex()
Modified: trunk/Source/WebInspectorUI/UserInterface/Models/Resource.js (243023 => 243024)
--- trunk/Source/WebInspectorUI/UserInterface/Models/Resource.js 2019-03-15 23:33:16 UTC (rev 243023)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/Resource.js 2019-03-15 23:45:51 UTC (rev 243024)
@@ -66,6 +66,7 @@
this._statusText = null;
this._cached = false;
this._canceled = false;
+ this._finished = false;
this._failed = false;
this._failureReasonText = null;
this._receivedNetworkLoadMetrics = false;
Modified: trunk/Source/WebInspectorUI/UserInterface/Models/ResourceTimelineRecord.js (243023 => 243024)
--- trunk/Source/WebInspectorUI/UserInterface/Models/ResourceTimelineRecord.js 2019-03-15 23:33:16 UTC (rev 243023)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/ResourceTimelineRecord.js 2019-03-15 23:45:51 UTC (rev 243024)
@@ -33,6 +33,26 @@
this._resource.addEventListener(WI.Resource.Event.TimestampsDidChange, this._dispatchUpdatedEvent, this);
}
+ // Import / Export
+
+ static fromJSON(json)
+ {
+ let {entry, archiveStartTime} = json;
+ let localResource = WI.LocalResource.fromHAREntry(entry, archiveStartTime);
+ return new WI.ResourceTimelineRecord(localResource);
+ }
+
+ toJSON()
+ {
+ const content = "";
+
+ return {
+ type: this.type,
+ archiveStartTime: this._resource.requestSentWalltime - this.startTime,
+ entry: WI.HARBuilder.entry(this._resource, content),
+ };
+ }
+
// Public
get resource()
Modified: trunk/Source/WebInspectorUI/UserInterface/Models/ScriptTimelineRecord.js (243023 => 243024)
--- trunk/Source/WebInspectorUI/UserInterface/Models/ScriptTimelineRecord.js 2019-03-15 23:33:16 UTC (rev 243023)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/ScriptTimelineRecord.js 2019-03-15 23:45:51 UTC (rev 243024)
@@ -48,6 +48,32 @@
}
}
+ // Import / Export
+
+ static fromJSON(json)
+ {
+ let {eventType, startTime, endTime, callFrames, sourceCodeLocation, details, profilePayload} = json;
+
+ if (typeof details === "object" && details.__type === "GarbageCollection")
+ details = WI.GarbageCollection.fromJSON(details);
+
+ return new WI.ScriptTimelineRecord(eventType, startTime, endTime, callFrames, sourceCodeLocation, details, profilePayload);
+ }
+
+ toJSON()
+ {
+ // FIXME: CallFrames
+ // FIXME: SourceCodeLocation
+
+ return {
+ type: this.type,
+ eventType: this._eventType,
+ startTime: this.startTime,
+ endTime: this.endTime,
+ details: this._details,
+ };
+ }
+
// Public
get eventType()
@@ -183,20 +209,20 @@
};
WI.ScriptTimelineRecord.EventType = {
- ScriptEvaluated: "script-timeline-record-script-evaluated",
- APIScriptEvaluated: "script-timeline-record-api-script-evaluated",
- MicrotaskDispatched: "script-timeline-record-microtask-dispatched",
- EventDispatched: "script-timeline-record-event-dispatched",
- ProbeSampleRecorded: "script-timeline-record-probe-sample-recorded",
- TimerFired: "script-timeline-record-timer-fired",
- TimerInstalled: "script-timeline-record-timer-installed",
- TimerRemoved: "script-timeline-record-timer-removed",
- AnimationFrameFired: "script-timeline-record-animation-frame-fired",
- AnimationFrameRequested: "script-timeline-record-animation-frame-requested",
- AnimationFrameCanceled: "script-timeline-record-animation-frame-canceled",
- ObserverCallback: "script-timeline-record-observer-callback",
- ConsoleProfileRecorded: "script-timeline-record-console-profile-recorded",
- GarbageCollected: "script-timeline-record-garbage-collected",
+ ScriptEvaluated: "script-evaluated",
+ APIScriptEvaluated: "api-script-evaluated",
+ MicrotaskDispatched: "microtask-dispatched",
+ EventDispatched: "event-dispatched",
+ ProbeSampleRecorded: "probe-sample-recorded",
+ TimerFired: "timer-fired",
+ TimerInstalled: "timer-installed",
+ TimerRemoved: "timer-removed",
+ AnimationFrameFired: "animation-frame-fired",
+ AnimationFrameRequested: "animation-frame-requested",
+ AnimationFrameCanceled: "animation-frame-canceled",
+ ObserverCallback: "observer-callback",
+ ConsoleProfileRecorded: "console-profile-recorded",
+ GarbageCollected: "garbage-collected",
};
WI.ScriptTimelineRecord.EventType.displayName = function(eventType, details, includeDetailsInMainTitle)
Modified: trunk/Source/WebInspectorUI/UserInterface/Models/SourceMapResource.js (243023 => 243024)
--- trunk/Source/WebInspectorUI/UserInterface/Models/SourceMapResource.js 2019-03-15 23:33:16 UTC (rev 243023)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/SourceMapResource.js 2019-03-15 23:45:51 UTC (rev 243024)
@@ -74,7 +74,7 @@
return resourceURLComponents.path.substring(sourceMappingBasePathURLComponents.path.length, resourceURLComponents.length);
}
- requestContentFromBackend(callback)
+ requestContentFromBackend()
{
// Revert the markAsFinished that was done in the constructor.
this.revertMarkAsFinished();
Modified: trunk/Source/WebInspectorUI/UserInterface/Models/TimelineMarker.js (243023 => 243024)
--- trunk/Source/WebInspectorUI/UserInterface/Models/TimelineMarker.js 2019-03-15 23:33:16 UTC (rev 243023)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/TimelineMarker.js 2019-03-15 23:45:51 UTC (rev 243024)
@@ -36,8 +36,28 @@
this._details = details || null;
}
+ // Import / Export
+
+ static fromJSON(json)
+ {
+ let {time, type, details} = json;
+ return new WI.TimelineMarker(time, type, details);
+ }
+
+ toJSON()
+ {
+ return {
+ time: this._time,
+ type: this._type,
+ details: this._details || undefined,
+ };
+ }
+
// Public
+ get type() { return this._type; }
+ get details() { return this._details; }
+
get time()
{
return this._time;
@@ -56,16 +76,6 @@
this.dispatchEventToListeners(WI.TimelineMarker.Event.TimeChanged);
}
-
- get type()
- {
- return this._type;
- }
-
- get details()
- {
- return this._details;
- }
};
WI.TimelineMarker.Event = {
Modified: trunk/Source/WebInspectorUI/UserInterface/Models/TimelineRecord.js (243023 => 243024)
--- trunk/Source/WebInspectorUI/UserInterface/Models/TimelineRecord.js 2019-03-15 23:33:16 UTC (rev 243023)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/TimelineRecord.js 2019-03-15 23:45:51 UTC (rev 243024)
@@ -42,6 +42,38 @@
this._children = [];
}
+ // Import / Export
+
+ static fromJSON(json)
+ {
+ switch (json.type) {
+ case WI.TimelineRecord.Type.Network:
+ return WI.ResourceTimelineRecord.fromJSON(json);
+ case WI.TimelineRecord.Type.Layout:
+ return WI.LayoutTimelineRecord.fromJSON(json);
+ case WI.TimelineRecord.Type.Script:
+ return WI.ScriptTimelineRecord.fromJSON(json);
+ case WI.TimelineRecord.Type.RenderingFrame:
+ return WI.RenderingFrameTimelineRecord.fromJSON(json);
+ case WI.TimelineRecord.Type.CPU:
+ return WI.CPUTimelineRecord.fromJSON(json);
+ case WI.TimelineRecord.Type.Memory:
+ return WI.MemoryTimelineRecord.fromJSON(json);
+ case WI.TimelineRecord.Type.HeapAllocations:
+ return WI.HeapAllocationsTimelineRecord.fromJSON(json);
+ case WI.TimelineRecord.Type.Media:
+ return WI.MediaTimelineRecord.fromJSON(json);
+ default:
+ console.error("Unknown TimelineRecord.Type: " + json.type, json);
+ return null;
+ }
+ }
+
+ toJSON()
+ {
+ throw WI.NotImplementedError.subclassMustOverride();
+ }
+
// Public
get type()
Modified: trunk/Source/WebInspectorUI/UserInterface/Models/TimelineRecording.js (243023 => 243024)
--- trunk/Source/WebInspectorUI/UserInterface/Models/TimelineRecording.js 2019-03-15 23:33:16 UTC (rev 243023)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/TimelineRecording.js 2019-03-15 23:45:51 UTC (rev 243024)
@@ -34,7 +34,19 @@
this._displayName = displayName;
this._capturing = false;
this._readonly = false;
+ this._imported = false;
this._instruments = instruments || [];
+
+ this._startTime = NaN;
+ this._endTime = NaN;
+ this._discontinuities = null;
+
+ this._exportDataRecords = null;
+ this._exportDataMarkers = null;
+ this._exportDataMemoryPressureEvents = null;
+ this._exportDataSampleStackTraces = null;
+ this._exportDataSampleDurations = null;
+
this._topDownCallingContextTree = new WI.CallingContextTree(WI.CallingContextTree.Type.TopDown);
this._bottomUpCallingContextTree = new WI.CallingContextTree(WI.CallingContextTree.Type.BottomUp);
this._topFunctionsTopDownCallingContextTree = new WI.CallingContextTree(WI.CallingContextTree.Type.TopFunctionsTopDown);
@@ -60,6 +72,76 @@
return WI.sharedApp.debuggableType === WI.DebuggableType.Web;
}
+ // Import / Export
+
+ static import(identifier, json, displayName)
+ {
+ let {startTime, endTime, discontinuities, instrumentTypes, records, markers, memoryPressureEvents, sampleStackTraces, sampleDurations} = json;
+ let importedDisplayName = WI.UIString("Imported - %s").format(displayName);
+ let instruments = instrumentTypes.map((type) => WI.Instrument.createForTimelineType(type));
+ let recording = new WI.TimelineRecording(identifier, importedDisplayName, instruments);
+
+ recording._readonly = true;
+ recording._imported = true;
+ recording._startTime = startTime;
+ recording._endTime = endTime;
+ recording._discontinuities = discontinuities;
+
+ recording.initializeCallingContextTrees(sampleStackTraces, sampleDurations);
+
+ for (let recordJSON of records) {
+ let record = WI.TimelineRecord.fromJSON(recordJSON);
+ if (record) {
+ recording.addRecord(record);
+
+ if (record instanceof WI.ScriptTimelineRecord)
+ record.profilePayload = recording._topDownCallingContextTree.toCPUProfilePayload(record.startTime, record.endTime);
+ }
+ }
+
+ for (let memoryPressureJSON of memoryPressureEvents) {
+ let memoryPressureEvent = WI.MemoryPressureEvent.fromJSON(memoryPressureJSON);
+ if (memoryPressureEvent)
+ recording.addMemoryPressureEvent(memoryPressureEvent);
+ }
+
+ // Add markers once we've transitioned the active recording.
+ setTimeout(() => {
+ recording.__importing = true;
+
+ for (let markerJSON of markers) {
+ let marker = WI.TimelineMarker.fromJSON(markerJSON);
+ if (marker)
+ recording.addEventMarker(marker);
+ }
+
+ recording.__importing = false;
+ });
+
+ return recording;
+ }
+
+ exportData()
+ {
+ console.assert(this.canExport(), "Attempted to export a recording which should not be exportable.");
+
+ // FIXME: Overview data (sourceCodeTimelinesMap).
+ // FIXME: Record hierarchy (parent / child relationship) is lost.
+
+ return {
+ displayName: this._displayName,
+ startTime: this._startTime,
+ endTime: this._endTime,
+ discontinuities: this._discontinuities,
+ instrumentTypes: this._instruments.map((instrument) => instrument.timelineRecordType),
+ records: this._exportDataRecords,
+ markers: this._exportDataMarkers,
+ memoryPressureEvents: this._exportDataMemoryPressureEvents,
+ sampleStackTraces: this._exportDataSampleStackTraces,
+ sampleDurations: this._exportDataSampleDurations,
+ };
+ }
+
// Public
get displayName() { return this._displayName; }
@@ -66,7 +148,9 @@
get identifier() { return this._identifier; }
get timelines() { return this._timelines; }
get instruments() { return this._instruments; }
+ get capturing() { return this._capturing; }
get readonly() { return this._readonly; }
+ get imported() { return this._imported; }
get startTime() { return this._startTime; }
get endTime() { return this._endTime; }
@@ -113,9 +197,9 @@
return true;
}
- unloaded()
+ unloaded(importing)
{
- console.assert(!this.isEmpty(), "Shouldn't unload an empty recording; it should be reused instead.");
+ console.assert(importing || !this.isEmpty(), "Shouldn't unload an empty recording; it should be reused instead.");
this._readonly = true;
@@ -127,10 +211,17 @@
console.assert(!this._readonly, "Can't reset a read-only recording.");
this._sourceCodeTimelinesMap = new Map;
+
this._startTime = NaN;
this._endTime = NaN;
this._discontinuities = [];
+ this._exportDataRecords = [];
+ this._exportDataMarkers = []
+ this._exportDataMemoryPressureEvents = [];
+ this._exportDataSampleStackTraces = [];
+ this._exportDataSampleDurations = [];
+
this._topDownCallingContextTree.reset();
this._bottomUpCallingContextTree.reset();
this._topFunctionsTopDownCallingContextTree.reset();
@@ -192,7 +283,9 @@
addEventMarker(marker)
{
- if (!this._capturing)
+ this._exportDataMarkers.push(marker);
+
+ if (!this._capturing && !this.__importing)
return;
this.dispatchEventToListeners(WI.TimelineRecording.Event.MarkerAdded, {marker});
@@ -200,7 +293,9 @@
addRecord(record)
{
- var timeline = this._timelines.get(record.type);
+ this._exportDataRecords.push(record);
+
+ let timeline = this._timelines.get(record.type);
console.assert(timeline, record, this._timelines);
if (!timeline)
return;
@@ -247,6 +342,8 @@
addMemoryPressureEvent(memoryPressureEvent)
{
+ this._exportDataMemoryPressureEvents.push(memoryPressureEvent);
+
let memoryTimeline = this._timelines.get(WI.TimelineRecord.Type.Memory);
console.assert(memoryTimeline, this._timelines);
if (!memoryTimeline)
@@ -326,6 +423,30 @@
}
}
+ initializeCallingContextTrees(stackTraces, sampleDurations)
+ {
+ this._exportDataSampleStackTraces.concat(stackTraces);
+ this._exportDataSampleDurations.concat(sampleDurations);
+
+ for (let i = 0; i < stackTraces.length; i++) {
+ this._topDownCallingContextTree.updateTreeWithStackTrace(stackTraces[i], sampleDurations[i]);
+ this._bottomUpCallingContextTree.updateTreeWithStackTrace(stackTraces[i], sampleDurations[i]);
+ this._topFunctionsTopDownCallingContextTree.updateTreeWithStackTrace(stackTraces[i], sampleDurations[i]);
+ this._topFunctionsBottomUpCallingContextTree.updateTreeWithStackTrace(stackTraces[i], sampleDurations[i]);
+ }
+ }
+
+ canExport()
+ {
+ if (this._capturing)
+ return false;
+
+ if (isNaN(this._startTime))
+ return false;
+
+ return true;
+ }
+
// Private
_keyForRecord(record)
@@ -373,3 +494,5 @@
WI.TimelineRecording.isLegacy = undefined;
WI.TimelineRecording.TimestampThresholdForLegacyRecordConversion = 10000000; // Some value not near zero.
WI.TimelineRecording.TimestampThresholdForLegacyAssumedMilliseconds = 1420099200000; // Date.parse("Jan 1, 2015"). Milliseconds since epoch.
+
+WI.TimelineRecording.SerializationVersion = 1;
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/CPUTimelineOverviewGraph.js (243023 => 243024)
--- trunk/Source/WebInspectorUI/UserInterface/Views/CPUTimelineOverviewGraph.js 2019-03-15 23:33:16 UTC (rev 243023)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/CPUTimelineOverviewGraph.js 2019-03-15 23:45:51 UTC (rev 243024)
@@ -51,6 +51,9 @@
this._lastSelectedRecordInLayout = null;
this.reset();
+
+ for (let record of this._cpuTimeline.records)
+ this._processRecord(record);
}
// Static
@@ -212,8 +215,13 @@
{
let cpuTimelineRecord = event.data.record;
- this._maxUsage = Math.max(this._maxUsage, cpuTimelineRecord.usage);
+ this._processRecord(cpuTimelineRecord);
this.needsLayout();
}
+
+ _processRecord(cpuTimelineRecord)
+ {
+ this._maxUsage = Math.max(this._maxUsage, cpuTimelineRecord.usage);
+ }
};
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineView.js (243023 => 243024)
--- trunk/Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineView.js 2019-03-15 23:33:16 UTC (rev 243023)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineView.js 2019-03-15 23:45:51 UTC (rev 243024)
@@ -219,22 +219,6 @@
return components.concat(this._contentViewContainer.currentContentView.selectionPathComponents);
}
- get supportsSave()
- {
- if (this._showingSnapshotList)
- return false;
-
- if (!this._contentViewContainer.currentContentView)
- return false;
-
- return this._contentViewContainer.currentContentView.supportsSave;
- }
-
- get saveData()
- {
- return this._contentViewContainer.currentContentView.saveData;
- }
-
selectRecord(record)
{
if (record)
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotContentView.js (243023 => 243024)
--- trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotContentView.js 2019-03-15 23:33:16 UTC (rev 243023)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotContentView.js 2019-03-15 23:45:51 UTC (rev 243024)
@@ -65,16 +65,6 @@
return [];
}
- get supportsSave()
- {
- return this.representedObject instanceof WI.HeapSnapshotProxy;
- }
-
- get saveData()
- {
- return {customSaveHandler: () => { this._exportSnapshot(); }};
- }
-
shown()
{
super.shown();
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/LayoutTimelineOverviewGraph.css (243023 => 243024)
--- trunk/Source/WebInspectorUI/UserInterface/Views/LayoutTimelineOverviewGraph.css 2019-03-15 23:33:16 UTC (rev 243023)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/LayoutTimelineOverviewGraph.css 2019-03-15 23:45:51 UTC (rev 243024)
@@ -23,15 +23,15 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-.timeline-overview-graph.layout > .graph-row {
+.timeline-overview-graph.layout-overview > .graph-row {
height: 16px;
}
-.timeline-overview-graph.layout > .graph-row > .timeline-record-bar {
+.timeline-overview-graph.layout-overview > .graph-row > .timeline-record-bar {
height: 12px;
margin-top: 4px;
}
-.timeline-overview-graph.layout > .graph-row > .timeline-record-bar > .segment {
+.timeline-overview-graph.layout-overview > .graph-row > .timeline-record-bar > .segment {
border-radius: 2px;
}
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/LayoutTimelineOverviewGraph.js (243023 => 243024)
--- trunk/Source/WebInspectorUI/UserInterface/Views/LayoutTimelineOverviewGraph.js 2019-03-15 23:33:16 UTC (rev 243023)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/LayoutTimelineOverviewGraph.js 2019-03-15 23:45:51 UTC (rev 243024)
@@ -29,12 +29,15 @@
{
super(timelineOverview);
- this.element.classList.add("layout");
+ this.element.classList.add("layout-overview");
this._layoutTimeline = timeline;
this._layoutTimeline.addEventListener(WI.Timeline.Event.RecordAdded, this._layoutTimelineRecordAdded, this);
this.reset();
+
+ for (let record of this._layoutTimeline.records)
+ this._processRecord(record);
}
// Public
@@ -118,14 +121,19 @@
_layoutTimelineRecordAdded(event)
{
- var layoutTimelineRecord = event.data.record;
+ let layoutTimelineRecord = event.data.record;
console.assert(layoutTimelineRecord instanceof WI.LayoutTimelineRecord);
+ this._processRecord(layoutTimelineRecord);
+
+ this.needsLayout();
+ }
+
+ _processRecord(layoutTimelineRecord)
+ {
if (layoutTimelineRecord.eventType === WI.LayoutTimelineRecord.EventType.Paint || layoutTimelineRecord.eventType === WI.LayoutTimelineRecord.EventType.Composite)
this._timelinePaintRecordRow.records.push(layoutTimelineRecord);
else
this._timelineLayoutRecordRow.records.push(layoutTimelineRecord);
-
- this.needsLayout();
}
};
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/LayoutTimelineView.js (243023 => 243024)
--- trunk/Source/WebInspectorUI/UserInterface/Views/LayoutTimelineView.js 2019-03-15 23:33:16 UTC (rev 243023)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/LayoutTimelineView.js 2019-03-15 23:45:51 UTC (rev 243024)
@@ -98,6 +98,9 @@
timeline.addEventListener(WI.Timeline.Event.RecordAdded, this._layoutTimelineRecordAdded, this);
this._pendingRecords = [];
+
+ for (let record of timeline.records)
+ this._processRecord(record);
}
// Public
@@ -231,16 +234,21 @@
_layoutTimelineRecordAdded(event)
{
- var layoutTimelineRecord = event.data.record;
+ let layoutTimelineRecord = event.data.record;
console.assert(layoutTimelineRecord instanceof WI.LayoutTimelineRecord);
+ this._processRecord(layoutTimelineRecord);
+
+ this.needsLayout();
+ }
+
+ _processRecord(layoutTimelineRecord)
+ {
// Only add top-level records, to avoid processing child records multiple times.
if (layoutTimelineRecord.parent instanceof WI.LayoutTimelineRecord)
return;
this._pendingRecords.push(layoutTimelineRecord);
-
- this.needsLayout();
}
_updateHighlight()
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/MediaTimelineView.js (243023 => 243024)
--- trunk/Source/WebInspectorUI/UserInterface/Views/MediaTimelineView.js 2019-03-15 23:33:16 UTC (rev 243023)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/MediaTimelineView.js 2019-03-15 23:45:51 UTC (rev 243024)
@@ -79,6 +79,9 @@
timeline.addEventListener(WI.Timeline.Event.RecordAdded, this._handleRecordAdded, this);
this._pendingRecords = [];
+
+ for (let record of timeline.records)
+ this._processRecord(record);
}
// Public
@@ -191,14 +194,19 @@
_handleRecordAdded(event)
{
- let record = event.data.record;
- console.assert(record instanceof WI.MediaTimelineRecord);
+ let mediaTimelineRecord = event.data.record;
+ console.assert(mediaTimelineRecord instanceof WI.MediaTimelineRecord);
- this._pendingRecords.push(record);
+ this._processRecord(mediaTimelineRecord);
this.needsLayout();
}
+ _processRecord(mediaTimelineRecord)
+ {
+ this._pendingRecords.push(mediaTimelineRecord);
+ }
+
_handleSelectionPathComponentSiblingSelected(event)
{
let pathComponent = event.data.pathComponent;
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/MemoryTimelineOverviewGraph.js (243023 => 243024)
--- trunk/Source/WebInspectorUI/UserInterface/Views/MemoryTimelineOverviewGraph.js 2019-03-15 23:33:16 UTC (rev 243023)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/MemoryTimelineOverviewGraph.js 2019-03-15 23:45:51 UTC (rev 243024)
@@ -52,6 +52,9 @@
this._memoryPressureMarkerElements = [];
this.reset();
+
+ for (let record of this._memoryTimeline.records)
+ this._processRecord(record);
}
// Protected
@@ -244,7 +247,15 @@
_memoryTimelineRecordAdded(event)
{
let memoryTimelineRecord = event.data.record;
+ console.assert(memoryTimelineRecord instanceof WI.MemoryTimelineRecord);
+ this._processRecord(memoryTimelineRecord);
+
+ this.needsLayout();
+ }
+
+ _processRecord(memoryTimelineRecord)
+ {
this._maxSize = Math.max(this._maxSize, memoryTimelineRecord.totalSize);
if (!this._didInitializeCategories) {
@@ -254,8 +265,6 @@
types.push(category.type);
this._chart.initializeSections(types);
}
-
- this.needsLayout();
}
_memoryTimelineMemoryPressureEventAdded(event)
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/MemoryTimelineView.js (243023 => 243024)
--- trunk/Source/WebInspectorUI/UserInterface/Views/MemoryTimelineView.js 2019-03-15 23:33:16 UTC (rev 243023)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/MemoryTimelineView.js 2019-03-15 23:45:51 UTC (rev 243024)
@@ -96,6 +96,9 @@
timeline.addEventListener(WI.Timeline.Event.RecordAdded, this._memoryTimelineRecordAdded, this);
this.element.addEventListener("mousemove", this._handleGraphMouseMove.bind(this));
+
+ for (let record of timeline.records)
+ this._processRecord(record);
}
// Static
@@ -452,12 +455,17 @@
let memoryTimelineRecord = event.data.record;
console.assert(memoryTimelineRecord instanceof WI.MemoryTimelineRecord);
+ this._processRecord(memoryTimelineRecord);
+
+ if (memoryTimelineRecord.startTime >= this.startTime && memoryTimelineRecord.endTime <= this.endTime)
+ this.needsLayout();
+ }
+
+ _processRecord(memoryTimelineRecord)
+ {
if (!this._didInitializeCategories)
this._initializeCategoryViews(memoryTimelineRecord);
this._maxSize = Math.max(this._maxSize, memoryTimelineRecord.totalSize);
-
- if (memoryTimelineRecord.startTime >= this.startTime && memoryTimelineRecord.endTime <= this.endTime)
- this.needsLayout();
}
};
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/NetworkTimelineOverviewGraph.js (243023 => 243024)
--- trunk/Source/WebInspectorUI/UserInterface/Views/NetworkTimelineOverviewGraph.js 2019-03-15 23:33:16 UTC (rev 243023)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/NetworkTimelineOverviewGraph.js 2019-03-15 23:45:51 UTC (rev 243024)
@@ -36,6 +36,9 @@
timeline.addEventListener(WI.Timeline.Event.TimesUpdated, this.needsLayout, this);
this.reset();
+
+ for (let record of timeline.records)
+ this._processRecord(record);
}
// Public
@@ -47,12 +50,12 @@
this._nextDumpRow = 0;
this._timelineRecordGridRows = [];
- for (var i = 0; i < WI.NetworkTimelineOverviewGraph.MaximumRowCount; ++i)
+ for (let i = 0; i < WI.NetworkTimelineOverviewGraph.MaximumRowCount; ++i)
this._timelineRecordGridRows.push([]);
this.element.removeChildren();
- for (var rowRecords of this._timelineRecordGridRows) {
+ for (let rowRecords of this._timelineRecordGridRows) {
rowRecords.__element = document.createElement("div");
rowRecords.__element.classList.add("graph-row");
this.element.appendChild(rowRecords.__element);
@@ -106,21 +109,24 @@
_networkTimelineRecordAdded(event)
{
- var resourceTimelineRecord = event.data.record;
+ let resourceTimelineRecord = event.data.record;
console.assert(resourceTimelineRecord instanceof WI.ResourceTimelineRecord);
- function compareByStartTime(a, b)
- {
- return a.startTime - b.startTime;
- }
+ this._processRecord(resourceTimelineRecord);
+ this.needsLayout();
+ }
+
+ _processRecord(resourceTimelineRecord)
+ {
+ let compareByStartTime = (a, b) => a.startTime - b.startTime;
let minimumBarPaddingTime = WI.TimelineOverview.MinimumDurationPerPixel * (WI.TimelineRecordBar.MinimumWidthPixels + WI.TimelineRecordBar.MinimumMarginPixels);
// Try to find a row that has room and does not overlap a previous record.
- var foundRowForRecord = false;
- for (var i = 0; i < this._timelineRecordGridRows.length; ++i) {
- var rowRecords = this._timelineRecordGridRows[i];
- var lastRecord = rowRecords.lastValue;
+ let foundRowForRecord = false;
+ for (let i = 0; i < this._timelineRecordGridRows.length; ++i) {
+ let rowRecords = this._timelineRecordGridRows[i];
+ let lastRecord = rowRecords.lastValue;
if (!lastRecord || lastRecord.endTime + minimumBarPaddingTime <= resourceTimelineRecord.startTime) {
insertObjectIntoSortedArray(resourceTimelineRecord, rowRecords, compareByStartTime);
@@ -132,9 +138,9 @@
if (!foundRowForRecord) {
// Try to find a row that does not overlap a previous record's active time, but it can overlap the inactive time.
- for (var i = 0; i < this._timelineRecordGridRows.length; ++i) {
- var rowRecords = this._timelineRecordGridRows[i];
- var lastRecord = rowRecords.lastValue;
+ for (let i = 0; i < this._timelineRecordGridRows.length; ++i) {
+ let rowRecords = this._timelineRecordGridRows[i];
+ let lastRecord = rowRecords.lastValue;
console.assert(lastRecord);
if (lastRecord.activeStartTime + minimumBarPaddingTime <= resourceTimelineRecord.startTime) {
@@ -152,8 +158,6 @@
this._nextDumpRow = 0;
insertObjectIntoSortedArray(resourceTimelineRecord, this._timelineRecordGridRows[this._nextDumpRow++], compareByStartTime);
}
-
- this.needsLayout();
}
};
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/NetworkTimelineView.js (243023 => 243024)
--- trunk/Source/WebInspectorUI/UserInterface/Views/NetworkTimelineView.js 2019-03-15 23:33:16 UTC (rev 243023)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/NetworkTimelineView.js 2019-03-15 23:45:51 UTC (rev 243024)
@@ -136,6 +136,9 @@
this._pendingRecords = [];
this._resourceDataGridNodeMap = new Map;
+
+ for (let record of timeline.records)
+ this._processRecord(record);
}
// Public
@@ -267,11 +270,16 @@
_networkTimelineRecordAdded(event)
{
- var resourceTimelineRecord = event.data.record;
+ let resourceTimelineRecord = event.data.record;
console.assert(resourceTimelineRecord instanceof WI.ResourceTimelineRecord);
- this._pendingRecords.push(resourceTimelineRecord);
+ this._processRecord(resourceTimelineRecord);
this.needsLayout();
}
+
+ _processRecord(resourceTimelineRecord)
+ {
+ this._pendingRecords.push(resourceTimelineRecord);
+ }
};
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/OverviewTimelineView.js (243023 => 243024)
--- trunk/Source/WebInspectorUI/UserInterface/Views/OverviewTimelineView.js 2019-03-15 23:33:16 UTC (rev 243023)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/OverviewTimelineView.js 2019-03-15 23:45:51 UTC (rev 243024)
@@ -52,7 +52,8 @@
this._timelineRuler.addMarker(this._currentTimeMarker);
this.element.classList.add("overview");
- this.addSubview(this._dataGrid);
+ if (!this._recording.imported)
+ this.addSubview(this._dataGrid);
this._networkTimeline = recording.timelines.get(WI.TimelineRecord.Type.Network);
if (this._networkTimeline)
@@ -125,6 +126,11 @@
// Protected
+ get showsImportedRecordingMessage()
+ {
+ return true;
+ }
+
dataGridNodePathComponentSelected(event)
{
let dataGridNode = event.data.pathComponent.timelineDataGridNode;
@@ -135,6 +141,9 @@
layout()
{
+ if (this._recording.imported)
+ return;
+
let oldZeroTime = this._timelineRuler.zeroTime;
let oldStartTime = this._timelineRuler.startTime;
let oldEndTime = this._timelineRuler.endTime;
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/RenderingFrameTimelineView.js (243023 => 243024)
--- trunk/Source/WebInspectorUI/UserInterface/Views/RenderingFrameTimelineView.js 2019-03-15 23:33:16 UTC (rev 243023)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/RenderingFrameTimelineView.js 2019-03-15 23:45:51 UTC (rev 243024)
@@ -90,6 +90,9 @@
timeline.addEventListener(WI.Timeline.Event.RecordAdded, this._renderingFrameTimelineRecordAdded, this);
this._pendingRecords = [];
+
+ for (let record of timeline.records)
+ this._processRecord(record);
}
static displayNameForDurationFilter(filter)
@@ -277,15 +280,20 @@
_renderingFrameTimelineRecordAdded(event)
{
- var renderingFrameTimelineRecord = event.data.record;
+ let renderingFrameTimelineRecord = event.data.record;
console.assert(renderingFrameTimelineRecord instanceof WI.RenderingFrameTimelineRecord);
console.assert(renderingFrameTimelineRecord.children.length, "Missing child records for rendering frame.");
- this._pendingRecords.push(renderingFrameTimelineRecord);
+ this._processRecord(renderingFrameTimelineRecord);
this.needsLayout();
}
+ _processRecord(renderingFrameTimelineRecord)
+ {
+ this._pendingRecords.push(renderingFrameTimelineRecord);
+ }
+
_scopeBarSelectionDidChange()
{
this._dataGrid.filterDidChange();
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/ScriptDetailsTimelineView.js (243023 => 243024)
--- trunk/Source/WebInspectorUI/UserInterface/Views/ScriptDetailsTimelineView.js 2019-03-15 23:33:16 UTC (rev 243023)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/ScriptDetailsTimelineView.js 2019-03-15 23:45:51 UTC (rev 243024)
@@ -87,6 +87,9 @@
timeline.addEventListener(WI.Timeline.Event.Refreshed, this._scriptTimelineRecordRefreshed, this);
this._pendingRecords = [];
+
+ for (let record of timeline.records)
+ this._processRecord(record);
}
// Public
@@ -224,14 +227,19 @@
_scriptTimelineRecordAdded(event)
{
- var scriptTimelineRecord = event.data.record;
+ let scriptTimelineRecord = event.data.record;
console.assert(scriptTimelineRecord instanceof WI.ScriptTimelineRecord);
- this._pendingRecords.push(scriptTimelineRecord);
+ this._processRecord(scriptTimelineRecord);
this.needsLayout();
}
+ _processRecord(scriptTimelineRecord)
+ {
+ this._pendingRecords.push(scriptTimelineRecord);
+ }
+
_scriptTimelineRecordRefreshed(event)
{
this.needsLayout();
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/TimelineOverview.js (243023 => 243024)
--- trunk/Source/WebInspectorUI/UserInterface/Views/TimelineOverview.js 2019-03-15 23:33:16 UTC (rev 243023)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/TimelineOverview.js 2019-03-15 23:45:51 UTC (rev 243024)
@@ -116,10 +116,28 @@
this._viewModeDidChange();
+ WI.timelineManager.addEventListener(WI.TimelineManager.Event.RecordingImported, this._recordingImported, this);
WI.timelineManager.addEventListener(WI.TimelineManager.Event.CapturingStarted, this._capturingStarted, this);
WI.timelineManager.addEventListener(WI.TimelineManager.Event.CapturingStopped, this._capturingStopped, this);
}
+ // Import / Export
+
+ exportData()
+ {
+ let json = {
+ secondsPerPixel: this.secondsPerPixel,
+ scrollStartTime: this.scrollStartTime,
+ selectionStartTime: this.selectionStartTime,
+ selectionDuration: this.selectionDuration,
+ };
+
+ if (this._selectedTimeline)
+ json.selectedTimelineType = this._selectedTimeline.type;
+
+ return json;
+ }
+
// Public
get selectedTimeline()
@@ -657,6 +675,8 @@
overviewGraph.hidden();
treeElement.hidden = true;
}
+
+ this.needsLayout();
}
_instrumentRemoved(event)
@@ -1012,13 +1032,36 @@
this._editingInstrumentsDidChange();
}
- _capturingStarted()
+ _recordingImported(event)
{
+ let {overviewData} = event.data;
+
+ if (overviewData.secondsPerPixel !== undefined)
+ this.secondsPerPixel = overviewData.secondsPerPixel;
+ if (overviewData.scrollStartTime !== undefined)
+ this.scrollStartTime = overviewData.scrollStartTime;
+ if (overviewData.selectionStartTime !== undefined)
+ this.selectionStartTime = overviewData.selectionStartTime;
+ if (overviewData.selectionDuration !== undefined) {
+ if (overviewData.selectionDuration === Number.MAX_VALUE)
+ this._timelineRuler.selectEntireRange();
+ else
+ this.selectionDuration = overviewData.selectionDuration;
+ }
+ if (overviewData.selectedTimelineType !== undefined) {
+ let timeline = this._recording.timelineForRecordType(overviewData.selectedTimelineType);
+ if (timeline)
+ this.selectedTimeline = timeline;
+ }
+ }
+
+ _capturingStarted(event)
+ {
this._editInstrumentsButton.enabled = false;
this._stopEditingInstruments();
}
- _capturingStopped()
+ _capturingStopped(event)
{
this._editInstrumentsButton.enabled = true;
}
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/TimelineRecordBar.css (243023 => 243024)
--- trunk/Source/WebInspectorUI/UserInterface/Views/TimelineRecordBar.css 2019-03-15 23:33:16 UTC (rev 243023)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/TimelineRecordBar.css 2019-03-15 23:45:51 UTC (rev 243024)
@@ -111,8 +111,8 @@
}
-.timeline-record-bar.timeline-record-type-layout.layout-timeline-record-paint > .segment,
-.timeline-record-bar.timeline-record-type-layout.layout-timeline-record-composite > .segment {
+.timeline-record-bar.timeline-record-type-layout.paint > .segment,
+.timeline-record-bar.timeline-record-type-layout.composite > .segment {
background-color: hsl(76, 49%, 60%);
border-color: hsl(79, 45%, 51%);
}
@@ -122,7 +122,7 @@
border-color: hsl(273, 33%, 58%);
}
-.timeline-record-bar.timeline-record-type-script.script-timeline-record-garbage-collected > .segment {
+.timeline-record-bar.timeline-record-type-script.garbage-collected > .segment {
background-color: hsl(23, 69%, 73%);
border-color: hsl(11, 54%, 62%);
}
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/TimelineRecordingContentView.css (243023 => 243024)
--- trunk/Source/WebInspectorUI/UserInterface/Views/TimelineRecordingContentView.css 2019-03-15 23:33:16 UTC (rev 243023)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/TimelineRecordingContentView.css 2019-03-15 23:45:51 UTC (rev 243024)
@@ -59,7 +59,7 @@
--scope-bar-border-color-override: transparent;
}
-.content-view.timeline-recording > .content-browser .recording-progress {
+.content-view.timeline-recording > .content-browser :matches(.recording-progress, .recording-imported) {
position: absolute;
left: 0;
right: 0;
@@ -69,7 +69,7 @@
background-color: var(--panel-background-color-light);
}
-.content-view.timeline-recording > .content-browser .recording-progress > .status {
+.content-view.timeline-recording > .content-browser :matches(.recording-progress, .recording-imported) > .status {
margin-top: 40px;
margin-bottom: 10px;
text-align: center;
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/TimelineRecordingContentView.js (243023 => 243024)
--- trunk/Source/WebInspectorUI/UserInterface/Views/TimelineRecordingContentView.js 2019-03-15 23:33:16 UTC (rev 243023)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/TimelineRecordingContentView.js 2019-03-15 23:45:51 UTC (rev 243024)
@@ -64,6 +64,19 @@
WI.settings.timelinesAutoStop.addEventListener(WI.Setting.Event.Changed, this._handleTimelinesAutoStopSettingChanged, this);
}
+ this._exportButtonNavigationItem = new WI.ButtonNavigationItem("export", WI.UIString("Export"), "Images/Export.svg", 15, 15);
+ this._exportButtonNavigationItem.toolTip = WI.UIString("Export (%s)").format(WI.saveKeyboardShortcut.displayName);
+ this._exportButtonNavigationItem.buttonStyle = WI.ButtonNavigationItem.Style.ImageAndText;
+ this._exportButtonNavigationItem.visibilityPriority = WI.NavigationItem.VisibilityPriority.Low;
+ this._exportButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._exportButtonNavigationItemClicked, this);
+ this._exportButtonNavigationItem.enabled = false;
+
+ this._importButtonNavigationItem = new WI.ButtonNavigationItem("import", WI.UIString("Import"), "Images/Import.svg", 15, 15);
+ this._importButtonNavigationItem.toolTip = WI.UIString("Import");
+ this._importButtonNavigationItem.buttonStyle = WI.ButtonNavigationItem.Style.ImageAndText;
+ this._importButtonNavigationItem.visibilityPriority = WI.NavigationItem.VisibilityPriority.Low;
+ this._importButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._importButtonNavigationItemClicked, this);
+
this._clearTimelineNavigationItem = new WI.ButtonNavigationItem("clear-timeline", WI.UIString("Clear Timeline (%s)").format(WI.clearKeyboardShortcut.displayName), "Images/NavigationItemTrash.svg", 15, 15);
this._clearTimelineNavigationItem.visibilityPriority = WI.NavigationItem.VisibilityPriority.Low;
this._clearTimelineNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._clearTimeline, this);
@@ -74,6 +87,9 @@
this._progressView = new WI.TimelineRecordingProgressView;
this._timelineContentBrowser.addSubview(this._progressView);
+ this._importedView = new WI.TimelineRecordingImportedView;
+ this._timelineContentBrowser.addSubview(this._importedView);
+
this._timelineViewMap = new Map;
this._pathComponentMap = new Map;
@@ -109,6 +125,11 @@
this._instrumentAdded(instrument);
this.showOverviewTimelineView();
+
+ if (this._recording.imported) {
+ let {startTime, endTime} = this._recording;
+ this._updateTimes(startTime, endTime, endTime);
+ }
}
// Public
@@ -167,6 +188,9 @@
if (this._autoStopCheckboxNavigationItem)
navigationItems.push(this._autoStopCheckboxNavigationItem);
navigationItems.push(new WI.DividerNavigationItem);
+ navigationItems.push(this._importButtonNavigationItem);
+ navigationItems.push(this._exportButtonNavigationItem);
+ navigationItems.push(new WI.DividerNavigationItem);
navigationItems.push(this._clearTimelineNavigationItem);
return navigationItems;
}
@@ -179,14 +203,12 @@
get supportsSave()
{
- let currentContentView = this._timelineContentBrowser.currentContentView;
- return currentContentView && currentContentView.supportsSave;
+ return this._recording.canExport();
}
get saveData()
{
- let currentContentView = this._timelineContentBrowser.currentContentView;
- return currentContentView && currentContentView.saveData || null;
+ return {customSaveHandler: () => { this._exportTimelineRecording(); }};
}
get currentTimelineView()
@@ -200,7 +222,9 @@
this._timelineOverview.shown();
this._timelineContentBrowser.shown();
+
this._clearTimelineNavigationItem.enabled = !this._recording.readonly && !isNaN(this._recording.startTime);
+ this._exportButtonNavigationItem.enabled = this._recording.canExport();
this._currentContentViewDidChange();
@@ -292,6 +316,7 @@
this._timelineOverview.viewMode = newViewMode;
this._updateTimelineOverviewHeight();
this._updateProgressView();
+ this._updateImportedView();
this._updateFilterBar();
if (timelineView) {
@@ -497,6 +522,7 @@
if (!this._updating)
this._startUpdatingCurrentTime(startTime);
this._clearTimelineNavigationItem.enabled = !this._recording.readonly;
+ this._exportButtonNavigationItem.enabled = false;
// A discontinuity occurs when the recording is stopped and resumed at
// a future time. Capturing started signals the end of the current
@@ -518,6 +544,8 @@
this._updateTimelineViewTimes(this.currentTimelineView);
this._discontinuityStartTime = event.data.endTime || this._currentTime;
+
+ this._exportButtonNavigationItem.enabled = this._recording.canExport();
}
_debuggerPaused(event)
@@ -562,6 +590,42 @@
this._autoStopCheckboxNavigationItem.checked = WI.settings.timelinesAutoStop.value;
}
+ _exportTimelineRecording()
+ {
+ let json = {
+ version: WI.TimelineRecording.SerializationVersion,
+ recording: this._recording.exportData(),
+ overview: this._timelineOverview.exportData(),
+ };
+ if (!json.recording || !json.overview) {
+ InspectorFrontendHost.beep();
+ return;
+ }
+
+ let frameName = null;
+ let mainFrame = WI.networkManager.mainFrame;
+ if (mainFrame)
+ frameName = mainFrame.mainResource.urlComponents.host || mainFrame.mainResource.displayName;
+
+ let filename = frameName ? `${frameName}-recording` : this._recording.displayName;
+ let url = "" + encodeURI(filename) + ".json";
+ WI.FileUtilities.save({
+ url,
+ content: JSON.stringify(json),
+ forceSaveAs: true,
+ });
+ }
+
+ _exportButtonNavigationItemClicked(event)
+ {
+ this._exportTimelineRecording();
+ }
+
+ _importButtonNavigationItemClicked(event)
+ {
+ WI.FileUtilities.importJSON((result) => WI.timelineManager.processJSON(result));
+ }
+
_clearTimeline(event)
{
if (this._recording.readonly)
@@ -664,6 +728,7 @@
this._timelineOverview.reset();
this._overviewTimelineView.reset();
this._clearTimelineNavigationItem.enabled = false;
+ this._exportButtonNavigationItem.enabled = false;
}
_recordingUnloaded(event)
@@ -877,6 +942,11 @@
this._progressView.visible = isCapturing && this.currentTimelineView && !this.currentTimelineView.showsLiveRecordingData;
}
+ _updateImportedView()
+ {
+ this._importedView.visible = this._recording.imported && this.currentTimelineView && this.currentTimelineView.showsImportedRecordingMessage;
+ }
+
_updateFilterBar()
{
this._filterBarNavigationItem.hidden = !this.currentTimelineView || !this.currentTimelineView.showsFilterBar;
Copied: trunk/Source/WebInspectorUI/UserInterface/Views/TimelineRecordingImportedView.css (from rev 243022, trunk/Source/WebInspectorUI/UserInterface/Views/LayoutTimelineOverviewGraph.css) (0 => 243024)
--- trunk/Source/WebInspectorUI/UserInterface/Views/TimelineRecordingImportedView.css (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/TimelineRecordingImportedView.css 2019-03-15 23:45:51 UTC (rev 243024)
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+.content-view.timeline-recording > .content-browser .recording-imported .message-text-view > .message {
+ font-size: var(--message-text-view-large-font-size);
+ font-weight: 600;
+ letter-spacing: 0.02em;
+}
Copied: trunk/Source/WebInspectorUI/UserInterface/Views/TimelineRecordingImportedView.js (from rev 243022, trunk/Source/WebInspectorUI/UserInterface/Models/HeapAllocationsTimelineRecord.js) (0 => 243024)
--- trunk/Source/WebInspectorUI/UserInterface/Views/TimelineRecordingImportedView.js (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/TimelineRecordingImportedView.js 2019-03-15 23:45:51 UTC (rev 243024)
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2019 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.TimelineRecordingImportedView = class TimelineRecordingImportedView extends WI.View
+{
+ constructor()
+ {
+ super();
+
+ this.element.classList.add("recording-imported");
+ this.element.appendChild(WI.createMessageTextView(WI.UIString("Imported Timeline Recording")));
+ }
+
+ // Public
+
+ get visible()
+ {
+ return this._visible;
+ }
+
+ set visible(x)
+ {
+ if (this._visible === x)
+ return;
+
+ // FIXME: remove once <https://webkit.org/b/150741> is fixed.
+ this._visible = x;
+ this.element.classList.toggle("hidden", !this._visible);
+ }
+};
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/TimelineRecordingProgressView.js (243023 => 243024)
--- trunk/Source/WebInspectorUI/UserInterface/Views/TimelineRecordingProgressView.js 2019-03-15 23:33:16 UTC (rev 243023)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/TimelineRecordingProgressView.js 2019-03-15 23:45:51 UTC (rev 243024)
@@ -47,7 +47,10 @@
// Public
- get visible() { return this._visible; }
+ get visible()
+ {
+ return this._visible;
+ }
set visible(x)
{
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/TimelineView.js (243023 => 243024)
--- trunk/Source/WebInspectorUI/UserInterface/Views/TimelineView.js 2019-03-15 23:33:16 UTC (rev 243023)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/TimelineView.js 2019-03-15 23:45:51 UTC (rev 243024)
@@ -56,6 +56,12 @@
return true;
}
+ get showsImportedRecordingMessage()
+ {
+ // Implemented by sub-classes if needed.
+ return false;
+ }
+
get showsFilterBar()
{
// Implemented by sub-classes if needed.