Web Inspector: Timelines - Cannot export on about:blank - suggested filename containi...
authorjoepeck@webkit.org <joepeck@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 22 Mar 2019 02:33:38 +0000 (02:33 +0000)
committerjoepeck@webkit.org <joepeck@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 22 Mar 2019 02:33:38 +0000 (02:33 +0000)
https://bugs.webkit.org/show_bug.cgi?id=196109

Reviewed by Timothy Hatcher.

Source/WebInspectorUI:

* UserInterface/Base/FileUtilities.js:
(WI.FileUtilities.sanitizeFilename):
(WI.FileUtilities.inspectorURLForFilename):
New utility functions.

* UserInterface/Test.html:
Include FileUtilities.

* UserInterface/Base/Main.js:
* UserInterface/Controllers/AuditManager.js:
(WI.AuditManager.prototype.export):
* UserInterface/Debug/ProtocolTrace.js:
(WI.ProtocolTrace.prototype.get saveData):
(WI.ProtocolTrace):
* UserInterface/Views/ConsoleMessageView.js:
(WI.ConsoleMessageView.prototype._handleContextMenu):
(WI.ConsoleMessageView):
* UserInterface/Views/ContextMenuUtilities.js:
* UserInterface/Views/HeapSnapshotContentView.js:
(WI.HeapSnapshotContentView.prototype._exportSnapshot):
* UserInterface/Views/LogContentView.js:
(WI.LogContentView.prototype.get saveData):
(WI.LogContentView.prototype._handleContextMenuEvent):
* UserInterface/Views/NetworkTableContentView.js:
(WI.NetworkTableContentView.prototype._exportHAR):
* UserInterface/Views/RecordingContentView.js:
(WI.RecordingContentView.prototype._exportRecording):
(WI.RecordingContentView.prototype._exportReduction):
* UserInterface/Views/ScriptContentView.js:
(WI.ScriptContentView.prototype.get saveData):
* UserInterface/Views/ShaderProgramContentView.js:
(WI.ShaderProgramContentView.prototype.get saveData):
* UserInterface/Views/TextContentView.js:
(WI.TextContentView.prototype.get saveData):
* UserInterface/Views/TextResourceContentView.js:
(WI.TextResourceContentView.prototype.get saveData):
* UserInterface/Views/TimelineRecordingContentView.js:
(WI.TimelineRecordingContentView.prototype._exportTimelineRecording):
Update "web-inspector:///" URL building code to use the utility.

LayoutTests:

* inspector/unit-tests/file-utilities-expected.txt: Added.
* inspector/unit-tests/file-utilities.html: Added.
Tests for FileUtilities.js

* inspector/unit-tests/object-utilities.html:
* inspector/unit-tests/promise-utilities.html:

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

22 files changed:
LayoutTests/ChangeLog
LayoutTests/inspector/unit-tests/file-utilities-expected.txt [new file with mode: 0644]
LayoutTests/inspector/unit-tests/file-utilities.html [new file with mode: 0644]
LayoutTests/inspector/unit-tests/object-utilities.html
LayoutTests/inspector/unit-tests/promise-utilities.html
Source/WebInspectorUI/ChangeLog
Source/WebInspectorUI/UserInterface/Base/FileUtilities.js
Source/WebInspectorUI/UserInterface/Base/Main.js
Source/WebInspectorUI/UserInterface/Controllers/AuditManager.js
Source/WebInspectorUI/UserInterface/Debug/ProtocolTrace.js
Source/WebInspectorUI/UserInterface/Test.html
Source/WebInspectorUI/UserInterface/Views/ConsoleMessageView.js
Source/WebInspectorUI/UserInterface/Views/ContextMenuUtilities.js
Source/WebInspectorUI/UserInterface/Views/HeapSnapshotContentView.js
Source/WebInspectorUI/UserInterface/Views/LogContentView.js
Source/WebInspectorUI/UserInterface/Views/NetworkTableContentView.js
Source/WebInspectorUI/UserInterface/Views/RecordingContentView.js
Source/WebInspectorUI/UserInterface/Views/ScriptContentView.js
Source/WebInspectorUI/UserInterface/Views/ShaderProgramContentView.js
Source/WebInspectorUI/UserInterface/Views/TextContentView.js
Source/WebInspectorUI/UserInterface/Views/TextResourceContentView.js
Source/WebInspectorUI/UserInterface/Views/TimelineRecordingContentView.js

index 0b6b8ef..9178aaa 100644 (file)
@@ -1,3 +1,17 @@
+2019-03-21  Joseph Pecoraro  <pecoraro@apple.com>
+
+        Web Inspector: Timelines - Cannot export on about:blank - suggested filename containing a colon silently fails
+        https://bugs.webkit.org/show_bug.cgi?id=196109
+
+        Reviewed by Timothy Hatcher.
+
+        * inspector/unit-tests/file-utilities-expected.txt: Added.
+        * inspector/unit-tests/file-utilities.html: Added.
+        Tests for FileUtilities.js
+
+        * inspector/unit-tests/object-utilities.html:
+        * inspector/unit-tests/promise-utilities.html:
+
 2019-03-21  Chris Dumez  <cdumez@apple.com>
 
         [ Mac WK2 ] Layout Test http/tests/security/contentSecurityPolicy/block-all-mixed-content/insecure-image-in-iframe-with-enforced-and-report-policies.html is a flaky failure
diff --git a/LayoutTests/inspector/unit-tests/file-utilities-expected.txt b/LayoutTests/inspector/unit-tests/file-utilities-expected.txt
new file mode 100644 (file)
index 0000000..1e00b16
--- /dev/null
@@ -0,0 +1,16 @@
+
+== Running test suite: FileUtilities
+-- Running test case: FileUtilities.sanitizeFilename
+PASS: FileUtilities.sanitizeFilename: "" => ""
+PASS: FileUtilities.sanitizeFilename: "webkit.org-recording.json" => "webkit.org-recording.json"
+PASS: FileUtilities.sanitizeFilename: "abcABC012-_ test.tXt" => "abcABC012-_ test.tXt"
+PASS: FileUtilities.sanitizeFilename: "about:blank.json" => "about-blank.json"
+PASS: FileUtilities.sanitizeFilename: "a::::b" => "a-b"
+
+-- Running test case: FileUtilities.inspectorURLForFilename
+PASS: FileUtilities.inspectorURLForFilename: "" => "web-inspector:///"
+PASS: FileUtilities.inspectorURLForFilename: "webkit.org-recording.json" => "web-inspector:///webkit.org-recording.json"
+PASS: FileUtilities.inspectorURLForFilename: "abcABC012-_test.tXt" => "web-inspector:///abcABC012-_test.tXt"
+PASS: FileUtilities.inspectorURLForFilename: "a:b.json" => "web-inspector:///a-b.json"
+PASS: FileUtilities.inspectorURLForFilename: "a b.txt" => "web-inspector:///a%20b.txt"
+
diff --git a/LayoutTests/inspector/unit-tests/file-utilities.html b/LayoutTests/inspector/unit-tests/file-utilities.html
new file mode 100644 (file)
index 0000000..4a8a90d
--- /dev/null
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../http/tests/inspector/resources/inspector-test.js"></script>
+<script>
+function test()
+{
+    let suite = InspectorTest.createSyncSuite("FileUtilities");
+
+    suite.addTestCase({
+        name: "FileUtilities.sanitizeFilename",
+        test() {
+            function t(str, expected) {
+                expected = expected || str;
+                InspectorTest.expectEqual(WI.FileUtilities.sanitizeFilename(str), expected, `FileUtilities.sanitizeFilename: ${JSON.stringify(str)} => ${JSON.stringify(expected)}`);
+            }
+
+            t("");
+            t("webkit.org-recording.json");
+            t("abcABC012-_ test.tXt");
+            t("about:blank.json", "about-blank.json");
+            t("a::::b", "a-b");
+        }
+    });
+
+    suite.addTestCase({
+        name: "FileUtilities.inspectorURLForFilename",
+        test() {
+            function t(str, expected) {
+                expected = expected || ("web-inspector:///" + str);
+                InspectorTest.expectEqual(WI.FileUtilities.inspectorURLForFilename(str), expected, `FileUtilities.inspectorURLForFilename: ${JSON.stringify(str)} => ${JSON.stringify(expected)}`);
+            }
+
+            t("");
+            t("webkit.org-recording.json");
+            t("abcABC012-_test.tXt");
+            t("a:b.json", "web-inspector:///a-b.json");
+            t("a b.txt", "web-inspector:///a%20b.txt");
+        }
+    });
+
+    suite.runTestCasesAndFinish();
+}
+</script>
+</head>
+<body onLoad="runTest()">
+</body>
+</html>
index 7f480f4..3f1c4ed 100644 (file)
@@ -3,7 +3,6 @@
 <head>
 <script src="../../http/tests/inspector/resources/inspector-test.js"></script>
 <script>
-
 function test()
 {
     let suite = InspectorTest.createSyncSuite("ObjectUtilities");
index 2ba29a2..da9f41b 100644 (file)
@@ -3,7 +3,6 @@
 <head>
 <script src="../../http/tests/inspector/resources/inspector-test.js"></script>
 <script>
-
 function test()
 {
     let suite = InspectorTest.createAsyncSuite("Promise");
index b8a1d63..ca4aa47 100644 (file)
@@ -1,3 +1,50 @@
+2019-03-21  Joseph Pecoraro  <pecoraro@apple.com>
+
+        Web Inspector: Timelines - Cannot export on about:blank - suggested filename containing a colon silently fails
+        https://bugs.webkit.org/show_bug.cgi?id=196109
+
+        Reviewed by Timothy Hatcher.
+
+        * UserInterface/Base/FileUtilities.js:
+        (WI.FileUtilities.sanitizeFilename):
+        (WI.FileUtilities.inspectorURLForFilename):
+        New utility functions.
+
+        * UserInterface/Test.html:
+        Include FileUtilities.
+
+        * UserInterface/Base/Main.js:
+        * UserInterface/Controllers/AuditManager.js:
+        (WI.AuditManager.prototype.export):
+        * UserInterface/Debug/ProtocolTrace.js:
+        (WI.ProtocolTrace.prototype.get saveData):
+        (WI.ProtocolTrace):
+        * UserInterface/Views/ConsoleMessageView.js:
+        (WI.ConsoleMessageView.prototype._handleContextMenu):
+        (WI.ConsoleMessageView):
+        * UserInterface/Views/ContextMenuUtilities.js:
+        * UserInterface/Views/HeapSnapshotContentView.js:
+        (WI.HeapSnapshotContentView.prototype._exportSnapshot):
+        * UserInterface/Views/LogContentView.js:
+        (WI.LogContentView.prototype.get saveData):
+        (WI.LogContentView.prototype._handleContextMenuEvent):
+        * UserInterface/Views/NetworkTableContentView.js:
+        (WI.NetworkTableContentView.prototype._exportHAR):
+        * UserInterface/Views/RecordingContentView.js:
+        (WI.RecordingContentView.prototype._exportRecording):
+        (WI.RecordingContentView.prototype._exportReduction):
+        * UserInterface/Views/ScriptContentView.js:
+        (WI.ScriptContentView.prototype.get saveData):
+        * UserInterface/Views/ShaderProgramContentView.js:
+        (WI.ShaderProgramContentView.prototype.get saveData):
+        * UserInterface/Views/TextContentView.js:
+        (WI.TextContentView.prototype.get saveData):
+        * UserInterface/Views/TextResourceContentView.js:
+        (WI.TextResourceContentView.prototype.get saveData):
+        * UserInterface/Views/TimelineRecordingContentView.js:
+        (WI.TimelineRecordingContentView.prototype._exportTimelineRecording):
+        Update "web-inspector:///" URL building code to use the utility.
+
 2019-03-21  Nikita Vasilyev  <nvasilyev@apple.com>
 
         Web Inspector: Use CSS variables for text color in Computed panel
index ccc06dd..486a116 100644 (file)
@@ -38,6 +38,16 @@ WI.FileUtilities = class FileUtilities {
         return WI.UIString("Screen Shot %s-%s-%s at %s.%s.%s").format(...values);
     }
 
+    static sanitizeFilename(filename)
+    {
+        return filename.replace(/:+/g, "-");
+    }
+
+    static inspectorURLForFilename(filename)
+    {
+        return "web-inspector:///" + encodeURIComponent(FileUtilities.sanitizeFilename(filename));
+    }
+
     static save(saveData, forceSaveAs)
     {
         console.assert(saveData);
index fcf9a2e..aa67dce 100644 (file)
@@ -3078,7 +3078,7 @@ WI.archiveMainFrame = function()
 
         let mainFrame = WI.networkManager.mainFrame;
         let archiveName = mainFrame.mainResource.urlComponents.host || mainFrame.mainResource.displayName || "Archive";
-        let url = "web-inspector:///" + encodeURI(archiveName) + ".webarchive";
+        let url = WI.FileUtilities.inspectorURLForFilename(archiveName + ".webarchive");
 
         InspectorFrontendHost.save(url, data, true, true);
     });
index 1f49053..f1eba1a 100644 (file)
@@ -232,10 +232,8 @@ WI.AuditManager = class AuditManager extends WI.Object
         if (object instanceof WI.AuditTestResultBase)
             filename = WI.UIString("%s Result").format(filename);
 
-        let url = "web-inspector:///" + encodeURI(filename) + ".json";
-
         WI.FileUtilities.save({
-            url,
+            url: WI.FileUtilities.inspectorURLForFilename(filename + ".json"),
             content: JSON.stringify(object),
             forceSaveAs: true,
         });
index 628be12..a1bd9ab 100644 (file)
@@ -51,6 +51,9 @@ WI.ProtocolTrace = class ProtocolTrace
         // "Protocol Trace 2015-12-31 at 12.43.04.json".
         // When the Intl API is implemented, we can do a better job.
         let filename = WI.unlocalizedString(`Protocol Trace at ${YYYY}-${MM}-${DD} ${hh}.${mm}.${ss}.json`);
-        return {url: "web-inspector:///" + encodeURIComponent(filename), content: JSON.stringify(this._entries)};
+        return {
+            url: WI.FileUtilities.inspectorURLForFilename(filename),
+            content: JSON.stringify(this._entries),
+        };
     }
 };
index 4cd0a12..0c284a6 100644 (file)
@@ -57,6 +57,7 @@
     <script src="Base/DOMUtilities.js"></script>
     <script src="Base/EventListener.js"></script>
     <script src="Base/EventListenerSet.js"></script>
+    <script src="Base/FileUtilities.js"></script>
     <script src="Base/ImageUtilities.js"></script>
     <script src="Base/MIMETypeUtilities.js"></script>
     <script src="Base/ObjectStore.js"></script>
index 2503d97..23ac06c 100644 (file)
@@ -951,7 +951,7 @@ WI.ConsoleMessageView = class ConsoleMessageView extends WI.Object
         contextMenu.appendItem(WI.UIString("Save Image"), () => {
             const forceSaveAs = true;
             WI.FileUtilities.save({
-                url: encodeURI("web-inspector:///" + image.getAttribute("filename")),
+                url: WI.FileUtilities.inspectorURLForFilename(image.getAttribute("filename")),
                 content: parseDataURL(this._message.messageText).data,
                 base64Encoded: true,
             }, forceSaveAs);
index 6502387..f88d105 100644 (file)
@@ -253,7 +253,7 @@ WI.appendContextMenuItemsForDOMNode = function(contextMenu, domNode, options = {
                 }
 
                 WI.FileUtilities.save({
-                    url: encodeURI(`web-inspector:///${WI.FileUtilities.screenshotString()}.png`),
+                    url: WI.FileUtilities.inspectorURLForFilename(WI.FileUtilities.screenshotString() + ".png"),
                     content: parseDataURL(dataURL).data,
                     base64Encoded: true,
                 });
index dda3ac8..4009627 100644 (file)
@@ -103,9 +103,8 @@ WI.HeapSnapshotContentView = class HeapSnapshotContentView extends WI.ContentVie
             Number.zeroPad(date.getSeconds(), 2),
         ];
         let filename = WI.UIString("Heap Snapshot %s-%s-%s at %s.%s.%s").format(...values);
-        let url = "web-inspector:///" + encodeURI(filename) + ".json";
         WI.FileUtilities.save({
-            url,
+            url: WI.FileUtilities.inspectorURLForFilename(filename + ".json"),
             content: this.representedObject.snapshotStringData,
             forceSaveAs: true,
         });
index 8a62933..feebd4b 100644 (file)
@@ -265,7 +265,11 @@ WI.LogContentView = class LogContentView extends WI.ContentView
 
     get saveData()
     {
-        return {url: "web-inspector:///Console.txt", content: this._formatMessagesAsData(false), forceSaveAs: true};
+        return {
+            url: WI.FileUtilities.inspectorURLForFilename("Console.txt"),
+            content: this._formatMessagesAsData(false),
+            forceSaveAs: true,
+        };
     }
 
     handleCopyEvent(event)
@@ -495,7 +499,7 @@ WI.LogContentView = class LogContentView extends WI.ContentView
             contextMenu.appendItem(WI.UIString("Save Selected"), () => {
                 const forceSaveAs = true;
                 WI.FileUtilities.save({
-                    url: "web-inspector:///Console.txt",
+                    url: WI.FileUtilities.inspectorURLForFilename("Console.txt"),
                     content: this._formatMessagesAsData(true),
                 }, forceSaveAs);
             });
index 1dd6510..8523f0e 100644 (file)
@@ -2108,9 +2108,8 @@ WI.NetworkTableContentView = class NetworkTableContentView extends WI.ContentVie
         WI.HARBuilder.buildArchive(resources).then((har) => {
             let mainFrame = WI.networkManager.mainFrame;
             let archiveName = mainFrame.mainResource.urlComponents.host || mainFrame.mainResource.displayName || "Archive";
-            let url = "web-inspector:///" + encodeURI(archiveName) + ".har";
             WI.FileUtilities.save({
-                url,
+                url: WI.FileUtilities.inspectorURLForFilename(archiveName + ".har"),
                 content: JSON.stringify(har, null, 2),
                 forceSaveAs: true,
             });
index c4a5989..863b52b 100644 (file)
@@ -176,10 +176,9 @@ WI.RecordingContentView = class RecordingContentView extends WI.ContentView
     _exportRecording()
     {
         let filename = this.representedObject.displayName;
-        let url = "web-inspector:///" + encodeURI(filename) + ".json";
 
         WI.FileUtilities.save({
-            url,
+            url: WI.FileUtilities.inspectorURLForFilename(filename + ".json"),
             content: JSON.stringify(this.representedObject.toJSON()),
             forceSaveAs: true,
         });
@@ -193,10 +192,9 @@ WI.RecordingContentView = class RecordingContentView extends WI.ContentView
         }
 
         let filename = this.representedObject.displayName;
-        let url = "web-inspector:///" + encodeURI(filename) + ".html";
 
         WI.FileUtilities.save({
-            url,
+            url: WI.FileUtilities.inspectorURLForFilename(filename + ".html"),
             content: this.representedObject.toHTML(),
             forceSaveAs: true,
         });
index 2ef44d4..725f190 100644 (file)
@@ -154,7 +154,7 @@ WI.ScriptContentView = class ScriptContentView extends WI.ContentView
 
     get saveData()
     {
-        var url = this._script.url || "web-inspector:///" + encodeURI(this._script.displayName) + ".js";
+        let url = this._script.url || WI.FileUtilities.inspectorURLForFilename(this._script.displayName + ".js");
         return {url, content: this._textEditor.string};
     }
 
index 283ec49..7e8da82 100644 (file)
@@ -121,7 +121,7 @@ WI.ShaderProgramContentView = class ShaderProgramContentView extends WI.ContentV
             filename = WI.UIString("Fragment");
 
         return {
-            url: `web-inspector:///${filename}.glsl`,
+            url: WI.FileUtilities.inspectorURLForFilename(filename + ".glsl"),
             content: this._lastActiveEditor.string,
             forceSaveAs: true,
         };
index 0ce10cc..25709b3 100644 (file)
@@ -106,7 +106,7 @@ WI.TextContentView = class TextContentView extends WI.ContentView
 
     get saveData()
     {
-        var url = "web-inspector:///" + encodeURI(WI.UIString("Untitled")) + ".txt";
+        let url = WI.FileUtilities.inspectorURLForFilename(WI.UIString("Untitled") + ".txt");
         return {url, content: this._textEditor.string, forceSaveAs: true};
     }
 
index d4fc48e..49a9b95 100644 (file)
@@ -143,8 +143,10 @@ WI.TextResourceContentView = class TextResourceContentView extends WI.ResourceCo
 
     get saveData()
     {
-        if (this.resource instanceof WI.CSSStyleSheet)
-            return {url: "web-inspector:///InspectorStyleSheet.css", content: this._textEditor.string, forceSaveAs: true};
+        if (this.resource instanceof WI.CSSStyleSheet) {
+            let url = WI.FileUtilities.inspectorURLForFilename("InspectorStyleSheet.css");
+            return {url, content: this._textEditor.string, forceSaveAs: true};
+        }
         return {url: this.resource.url, content: this._textEditor.string};
     }
 
index bd31043..0e16973 100644 (file)
@@ -604,9 +604,9 @@ WI.TimelineRecordingContentView = class TimelineRecordingContentView extends WI.
             frameName = mainFrame.mainResource.urlComponents.host || mainFrame.mainResource.displayName;
 
         let filename = frameName ? `${frameName}-recording` : this._recording.displayName;
-        let url = "web-inspector:///" + encodeURI(filename) + ".json";
+
         WI.FileUtilities.save({
-            url,
+            url: WI.FileUtilities.inspectorURLForFilename(filename + ".json"),
             content: JSON.stringify(json),
             forceSaveAs: true,
         });