Web Inspector: implement import/export for timeline data.
authorloislo@chromium.org <loislo@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 19 Jul 2011 13:45:04 +0000 (13:45 +0000)
committerloislo@chromium.org <loislo@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 19 Jul 2011 13:45:04 +0000 (13:45 +0000)
https://bugs.webkit.org/show_bug.cgi?id=64601

Reviewed by Yury Semikhatsky.

Source/WebCore:

Test: inspector/timeline/timeline-load.html

* English.lproj/localizedStrings.js:
* inspector/InspectorFrontendHost.cpp:
(WebCore::FrontendMenuProvider::contextMenuItemSelected):
* inspector/front-end/TimelinePanel.js:
(WebInspector.TimelinePanel):
(WebInspector.TimelinePanel.prototype._createFileSelector):
(WebInspector.TimelinePanel.prototype._contextMenu):
(WebInspector.TimelinePanel.prototype._exportToFile):
(WebInspector.TimelinePanel.prototype._importFromFile):
(WebInspector.TimelinePanel.prototype._addRecordToTimeline):
(WebInspector.TimelinePanel.prototype._clearPanel):
(WebInspector.TimelineModel):
(WebInspector.TimelineModel.prototype._addRecord):
(WebInspector.TimelineModel.prototype._importNextChunk):
(WebInspector.TimelineModel.prototype._importFromFile):
(WebInspector.TimelineModel.prototype._importFromFile.onError):
(WebInspector.TimelineModel.prototype._exportToFile):
(WebInspector.TimelineModel.prototype._reset):
* inspector/front-end/utilities.js:
():

LayoutTests:

* inspector/timeline/timeline-load-expected.txt: Added.
* inspector/timeline/timeline-load.html: Added.

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

LayoutTests/ChangeLog
LayoutTests/inspector/timeline/timeline-load-expected.txt [new file with mode: 0644]
LayoutTests/inspector/timeline/timeline-load.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/English.lproj/localizedStrings.js
Source/WebCore/inspector/InspectorFrontendHost.cpp
Source/WebCore/inspector/front-end/TimelinePanel.js
Source/WebCore/inspector/front-end/utilities.js

index 5ea0c7e..4da47c0 100644 (file)
@@ -1,3 +1,13 @@
+2011-07-19  Ilya Tikhonovsky  <loislo@chromium.org>
+
+        Web Inspector: implement import/export for timeline data.
+        https://bugs.webkit.org/show_bug.cgi?id=64601
+
+        Reviewed by Yury Semikhatsky.
+
+        * inspector/timeline/timeline-load-expected.txt: Added.
+        * inspector/timeline/timeline-load.html: Added.
+
 2011-07-19  Gabor Loki  <loki@webkit.org>
 
         [Qt] Add missing expected file after r91191
diff --git a/LayoutTests/inspector/timeline/timeline-load-expected.txt b/LayoutTests/inspector/timeline/timeline-load-expected.txt
new file mode 100644 (file)
index 0000000..6e040c3
--- /dev/null
@@ -0,0 +1,4 @@
+Tests the Timeline save/load functionality.
+
+passed
+
diff --git a/LayoutTests/inspector/timeline/timeline-load.html b/LayoutTests/inspector/timeline/timeline-load.html
new file mode 100644 (file)
index 0000000..5775512
--- /dev/null
@@ -0,0 +1,51 @@
+<html>
+<head>
+<script src="../../http/tests/inspector/inspector-test.js"></script>
+<script src="timeline-test.js"></script>
+<script>
+
+function test()
+{
+    WebInspector.showPanel("timeline");
+    InspectorTest.addSniffer(WebInspector.TimelineModel.prototype, "_importNextChunk", importNextChunk);
+    InspectorFrontendHost.saveAs = saveAs;
+    var data = [
+        {"startTime":1310749854316.8408,"data":{"identifier":43,"url":"http://www.webkit.org/","requestMethod":"GET"},"type":"ResourceSendRequest","usedHeapSize":1819936,"totalHeapSize":7629120},
+        {"startTime":1310749855118.9329,"data":{"identifier":43,"statusCode":200,"mimeType":"text/html"},"children":[],"endTime":1310749855118.968,"type":"ResourceReceiveResponse","usedHeapSize":1819936,"totalHeapSize":7629120},
+        {"startTime":1310749855136.245,"data":{"identifier":44,"url":"http://www.webkit.org/css/main.css","requestMethod":"GET"},"type":"ResourceSendRequest","usedHeapSize":2367192,"totalHeapSize":7629120},
+        {"startTime":1310749855136.655,"data":{"identifier":45,"url":"http://www.webkit.org/css/green.css","requestMethod":"GET"},"type":"ResourceSendRequest","usedHeapSize":2367192,"totalHeapSize":7629120},
+        {"startTime":1310749855137.023,"data":{"identifier":46,"url":"http://www.webkit.org/css/blue.css","requestMethod":"GET"},"type":"ResourceSendRequest","usedHeapSize":2367192,"totalHeapSize":7629120},
+        {"startTime":1310749855137.342,"data":{"identifier":47,"url":"http://www.webkit.org/css/yellow.css","requestMethod":"GET"},"type":"ResourceSendRequest","usedHeapSize":2367192,"totalHeapSize":7629120},
+        {"startTime":1310749855137.656,"data":{"identifier":48,"url":"http://www.webkit.org/css/pink.css","requestMethod":"GET"},"type":"ResourceSendRequest","usedHeapSize":2367192,"totalHeapSize":7629120},
+        {"startTime":1310749855137.989,"data":{"identifier":49,"url":"http://www.webkit.org/css/purple.css","requestMethod":"GET"},"type":"ResourceSendRequest","usedHeapSize":2367192,"totalHeapSize":7629120},
+        {"startTime":1310749855138.308,"data":{"identifier":50,"url":"http://www.webkit.org/css/gray.css","requestMethod":"GET"},"type":"ResourceSendRequest","usedHeapSize":2367192,"totalHeapSize":7629120}];
+
+    WebInspector.panels.timeline._model._importNextChunk(data, 0);
+
+    function importNextChunk()
+    {
+        WebInspector.panels.timeline._model._exportToFile();
+    }
+
+    function saveAs(name, saveData)
+    {
+        saveData = JSON.parse(saveData);
+        saveData.shift(); // strip version info
+        if (JSON.stringify(data) == JSON.stringify(saveData))
+            InspectorTest.addResult("passed");
+        else
+            InspectorTest.addResult("saved data is not equal to restored");
+        InspectorTest.completeTest();
+    }
+}
+
+</script>
+</head>
+
+<body onload="runTest()">
+<p>
+Tests the Timeline save/load functionality.
+</p>
+
+</body>
+</html>
index b0f946a..226f0d7 100644 (file)
@@ -1,3 +1,33 @@
+2011-07-19  Ilya Tikhonovsky  <loislo@chromium.org>
+
+        Web Inspector: implement import/export for timeline data.
+        https://bugs.webkit.org/show_bug.cgi?id=64601
+
+        Reviewed by Yury Semikhatsky.
+
+        Test: inspector/timeline/timeline-load.html
+
+        * English.lproj/localizedStrings.js:
+        * inspector/InspectorFrontendHost.cpp:
+        (WebCore::FrontendMenuProvider::contextMenuItemSelected):
+        * inspector/front-end/TimelinePanel.js:
+        (WebInspector.TimelinePanel):
+        (WebInspector.TimelinePanel.prototype._createFileSelector):
+        (WebInspector.TimelinePanel.prototype._contextMenu):
+        (WebInspector.TimelinePanel.prototype._exportToFile):
+        (WebInspector.TimelinePanel.prototype._importFromFile):
+        (WebInspector.TimelinePanel.prototype._addRecordToTimeline):
+        (WebInspector.TimelinePanel.prototype._clearPanel):
+        (WebInspector.TimelineModel):
+        (WebInspector.TimelineModel.prototype._addRecord):
+        (WebInspector.TimelineModel.prototype._importNextChunk):
+        (WebInspector.TimelineModel.prototype._importFromFile):
+        (WebInspector.TimelineModel.prototype._importFromFile.onError):
+        (WebInspector.TimelineModel.prototype._exportToFile):
+        (WebInspector.TimelineModel.prototype._reset):
+        * inspector/front-end/utilities.js:
+        ():
+
 2011-07-19  Vsevolod Vlasov  <vsevik@chromium.org>
 
         Web Inspector: Rename agentIdentifierPrefix to processId, move out from page agent and make static.
index 62f4959..79d8e6f 100644 (file)
Binary files a/Source/WebCore/English.lproj/localizedStrings.js and b/Source/WebCore/English.lproj/localizedStrings.js differ
index 004e457..ac9dcf7 100644 (file)
@@ -47,6 +47,7 @@
 #include "Page.h"
 #include "Pasteboard.h"
 #include "ScriptFunctionCall.h"
+#include "UserGestureIndicator.h"
 
 #include <wtf/RefPtr.h>
 #include <wtf/StdLibExtras.h>
@@ -91,6 +92,7 @@ private:
     virtual void contextMenuItemSelected(ContextMenuItem* item)
     {
         if (m_frontendHost) {
+            UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture);
             int itemNumber = item->action() - ContextMenuItemBaseCustomTag;
 
             ScriptFunctionCall function(m_webInspector, "contextMenuItemSelected");
index 90ba41f..a27fe9f 100644 (file)
@@ -33,6 +33,7 @@ WebInspector.TimelinePanel = function()
     WebInspector.Panel.call(this, "timeline");
 
     this.element.appendChild(this._createTopPane());
+    this.element.addEventListener("contextmenu", this._contextMenu.bind(this), true);
     this.element.tabIndex = 0;
 
     this._sidebarBackgroundElement = document.createElement("div");
@@ -100,6 +101,9 @@ WebInspector.TimelinePanel = function()
     this._timeStampRecords = [];
     this._expandOffset = 15;
 
+    this._createFileSelector();
+    this._model = new WebInspector.TimelineModel(this);
+
     WebInspector.timelineManager.addEventListener(WebInspector.TimelineManager.EventTypes.TimelineEventRecorded, this._onTimelineEventRecorded, this);
 }
 
@@ -217,6 +221,43 @@ WebInspector.TimelinePanel.prototype = {
         this.recordsCounter.className = "timeline-records-counter";
     },
 
+    _createFileSelector: function()
+    {
+        if (this._fileSelectorElement)
+            this.element.removeChild(this._fileSelectorElement);
+
+        var fileSelectorElement = document.createElement("input");
+        fileSelectorElement.type = "file";
+        fileSelectorElement.style.opacity = 0;
+        fileSelectorElement.onchange = this._importFromFile.bind(this);
+        this.element.appendChild(fileSelectorElement);
+        this._fileSelectorElement = fileSelectorElement;
+    },
+
+    _contextMenu: function(event)
+    {
+        var contextMenu = new WebInspector.ContextMenu();
+        contextMenu.appendItem(WebInspector.UIString("&Export Timeline data\u2026"), this._exportToFile.bind(this));
+        contextMenu.appendItem(WebInspector.UIString("&Import Timeline data\u2026"), this._fileSelectorElement.click.bind(this._fileSelectorElement));
+        contextMenu.show(event);
+    },
+
+    _exportToFile: function()
+    {
+        this._model._exportToFile();
+    },
+
+    _importFromFile: function()
+    {
+        if (this.toggleTimelineButton.toggled)
+            WebInspector.timelineManager.stop();
+
+        this._clearPanel();
+
+        this._model._importFromFile(this._fileSelectorElement.files[0]);
+        this._createFileSelector();
+    },
+
     _updateRecordsCounter: function()
     {
         this.recordsCounter.textContent = WebInspector.UIString("%d of %d captured records are visible", this._rootRecord._visibleRecordsCount, this._rootRecord._allRecordsCount);
@@ -314,6 +355,7 @@ WebInspector.TimelinePanel.prototype = {
                 this._clearPanel();
             }
         }
+        this._model._addRecord(record);
         this._innerAddRecordToTimeline(record, this._rootRecord);
         this._scheduleRefresh();
     },
@@ -444,6 +486,7 @@ WebInspector.TimelinePanel.prototype = {
         this._adjustScrollPosition(0);
         this._refresh();
         this._closeRecordDetails();
+        this._model._reset();
     },
 
     show: function()
@@ -1172,3 +1215,72 @@ WebInspector.TimelineExpandableElement.prototype = {
         this._element.parentElement.removeChild(this._element);
     }
 }
+
+WebInspector.TimelineModel = function(timelinePanel)
+{
+    this._panel = timelinePanel;
+    this._records = [];
+}
+
+WebInspector.TimelineModel.prototype = {
+    _addRecord: function(record)
+    {
+        this._records.push(record);
+    },
+
+    _importNextChunk: function(data, index)
+    {
+        for (var i = 0; i < 20 && index < data.length; ++i, ++index)
+            this._panel._addRecordToTimeline(data[index]);
+
+        if (index !== data.length)
+            setTimeout(this._importNextChunk.bind(this, data, index), 0);
+    },
+
+    _importFromFile: function(file)
+    {
+        function onLoad(e)
+        {
+            var data = JSON.parse(e.target.result);
+            var version = data[0];
+            this._importNextChunk(data, 1);
+        }
+
+        function onError(e)
+        {
+            switch(e.target.error.code) {
+            case e.target.error.NOT_FOUND_ERR:
+                WebInspector.log(WebInspector.UIString('Timeline.importFromFile: File "%s" not found.', file.name));
+            break;
+            case e.target.error.NOT_READABLE_ERR:
+                WebInspector.log(WebInspector.UIString('Timeline.importFromFile: File "%s" is not readable', file.name));
+            break;
+            case e.target.error.ABORT_ERR:
+                break;
+            default:
+                WebInspector.log(WebInspector.UIString('Timeline.importFromFile: An error occurred while reading the file "%s"', file.name));
+            }
+        }
+
+        var reader = new FileReader();
+        reader.onload = onLoad.bind(this);
+        reader.onerror = onError;
+        reader.readAsText(file);
+    },
+
+    _exportToFile: function()
+    {
+        var records = ['[' + JSON.stringify(window.navigator.appVersion)];
+        for (var i = 0; i < this._records.length - 1; ++i)
+            records.push(JSON.stringify(this._records[i]));
+        records.push(JSON.stringify(this._records[this._records.length - 1]) + "]");
+
+        var now= new Date();
+        InspectorFrontendHost.saveAs("TimelineRawData-" + now.toRFC3339() + ".json", records.join(",\n"));
+    },
+
+    _reset: function()
+    {
+        this._records = [];
+    }
+}
index eff55e9..6388125 100644 (file)
@@ -688,6 +688,23 @@ Number.constrain = function(num, min, max)
     return num;
 }
 
+Date.prototype.toRFC3339 = function()
+{
+    function leadZero(x)
+    {
+        return x > 9 ? x : '0' + x
+    }
+    var offset = Math.abs(this.getTimezoneOffset());
+    var offsetString = Math.floor(offset / 60) + ':' + leadZero(offset % 60);
+    return this.getFullYear() + '-' +
+           leadZero(this.getMonth() + 1) + '-' +
+           leadZero(this.getDate()) + 'T' +
+           leadZero(this.getHours()) + ':' +
+           leadZero(this.getMinutes()) + ':' +
+           leadZero(this.getSeconds()) +
+           (!offset ? "Z" : (this.getTimezoneOffset() > 0 ? '-' : '+') + offsetString);
+}
+
 HTMLTextAreaElement.prototype.moveCursorToEnd = function()
 {
     var length = this.value.length;