Web Inspector: improve invalid Audit/Recording JSON error messages
authordrousso@apple.com <drousso@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 25 Jan 2019 09:27:50 +0000 (09:27 +0000)
committerdrousso@apple.com <drousso@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 25 Jan 2019 09:27:50 +0000 (09:27 +0000)
https://bugs.webkit.org/show_bug.cgi?id=193476
<rdar://problem/47303659>

Reviewed by Joseph Pecoraro.

Source/WebInspectorUI:

* UserInterface/Models/AuditTestBase.js:
(WI.AuditTestBase):
* UserInterface/Models/AuditTestCase.js:
(WI.AuditTestCase.async fromPayload):
* UserInterface/Models/AuditTestGroup.js:
(WI.AuditTestGroup.async fromPayload):
* UserInterface/Models/AuditTestCaseResult.js:
(WI.AuditTestCaseResult.async fromPayload.checkArray):
(WI.AuditTestCaseResult.async fromPayload):
* UserInterface/Models/AuditTestGroupResult.js:
(WI.AuditTestGroupResult.async fromPayload):
* UserInterface/Controllers/AuditManager.js:
(WI.AuditManager.synthesizeWarning): Added.
(WI.AuditManager.synthesizeError):
(WI.AuditManager.prototype.async processJSON):

* UserInterface/Models/Recording.js:
(WI.Recording.fromPayload):
(WI.Recording.synthesizeWarning): Added.
(WI.Recording.synthesizeError):
* UserInterface/Models/RecordingFrame.js:
(WI.RecordingFrame.fromPayload):
* UserInterface/Models/RecordingAction.js:
(WI.RecordingAction.fromPayload):
(WI.RecordingAction.prototype.async swizzle):
(WI.RecordingAction.prototype.apply):
* UserInterface/Controllers/CanvasManager.js:
(WI.CanvasManager.prototype.processJSON):

* Localizations/en.lproj/localizedStrings.js:

LayoutTests:

* inspector/model/auditTestCase.html:
* inspector/model/auditTestCase-expected.txt:
* inspector/model/auditTestCaseResult-expected.txt:
* inspector/model/auditTestGroup.html:
* inspector/model/auditTestGroup-expected.txt:
* inspector/model/auditTestGroupResult-expected.txt:
* inspector/model/recording-expected.txt:

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

20 files changed:
LayoutTests/ChangeLog
LayoutTests/inspector/model/auditTestCase-expected.txt
LayoutTests/inspector/model/auditTestCase.html
LayoutTests/inspector/model/auditTestCaseResult-expected.txt
LayoutTests/inspector/model/auditTestGroup-expected.txt
LayoutTests/inspector/model/auditTestGroup.html
LayoutTests/inspector/model/auditTestGroupResult-expected.txt
LayoutTests/inspector/model/recording-expected.txt
Source/WebInspectorUI/ChangeLog
Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js
Source/WebInspectorUI/UserInterface/Controllers/AuditManager.js
Source/WebInspectorUI/UserInterface/Controllers/CanvasManager.js
Source/WebInspectorUI/UserInterface/Models/AuditTestBase.js
Source/WebInspectorUI/UserInterface/Models/AuditTestCase.js
Source/WebInspectorUI/UserInterface/Models/AuditTestCaseResult.js
Source/WebInspectorUI/UserInterface/Models/AuditTestGroup.js
Source/WebInspectorUI/UserInterface/Models/AuditTestGroupResult.js
Source/WebInspectorUI/UserInterface/Models/Recording.js
Source/WebInspectorUI/UserInterface/Models/RecordingAction.js
Source/WebInspectorUI/UserInterface/Models/RecordingFrame.js

index 5ad33a9..16337f3 100644 (file)
@@ -1,3 +1,19 @@
+2019-01-25  Devin Rousso  <drousso@apple.com>
+
+        Web Inspector: improve invalid Audit/Recording JSON error messages
+        https://bugs.webkit.org/show_bug.cgi?id=193476
+        <rdar://problem/47303659>
+
+        Reviewed by Joseph Pecoraro.
+
+        * inspector/model/auditTestCase.html:
+        * inspector/model/auditTestCase-expected.txt:
+        * inspector/model/auditTestCaseResult-expected.txt:
+        * inspector/model/auditTestGroup.html:
+        * inspector/model/auditTestGroup-expected.txt:
+        * inspector/model/auditTestGroupResult-expected.txt:
+        * inspector/model/recording-expected.txt:
+
 2019-01-24  Devin Rousso  <drousso@apple.com>
 
         Web Inspector: Audit: add supports key to test/group for compatibility
index 782db83..2d20ab4 100644 (file)
@@ -22,9 +22,12 @@ null
 }
 
 -- Running test case: AuditTestCase.fromPayload.validWithInvalidOptionals
+WARN: Audit Warning: "validWithInvalidOptionals test name" has a non-string "description" value
+WARN: Audit Warning: "validWithInvalidOptionals test name" is too new to run in this Web Inspector
 {
   "type": "test-case",
   "name": "validWithInvalidOptionals test name",
+  "supports": 2,
   "test": "validWithInvalidOptionals test function"
 }
 
@@ -33,6 +36,7 @@ null
   "type": "test-case",
   "name": "validWithValidOptionals test name",
   "description": "validWithValidOptionals test description",
+  "supports": 0,
   "test": "validWithValidOptionals test function"
 }
 
index 829d01d..ebcf879 100644 (file)
@@ -52,6 +52,7 @@ function test()
                 type: WI.AuditTestCase.TypeIdentifier,
                 name: "validWithInvalidOptionals test name",
                 description: null,
+                supports: WI.AuditTestBase.Version + 1,
                 test: "validWithInvalidOptionals test function",
             },
         },
@@ -61,6 +62,7 @@ function test()
                 type: WI.AuditTestCase.TypeIdentifier,
                 name: "validWithValidOptionals test name",
                 description: "validWithValidOptionals test description",
+                supports: WI.AuditTestBase.Version - 1,
                 test: "validWithValidOptionals test function",
             },
         },
index 814ad23..4e33ae2 100644 (file)
@@ -22,6 +22,9 @@ null
 }
 
 -- Running test case: AuditTestCaseResult.fromPayload.validWithInvalidOptionals
+WARN: Audit Warning: "validWithInvalidOptionals test result name" has a non-object "data" value
+WARN: Audit Warning: "validWithInvalidOptionals test result name" has a non-object "metadata" value
+WARN: Audit Warning: "validWithInvalidOptionals test result name" has a non-string "description" value
 {
   "type": "test-case-result",
   "name": "validWithInvalidOptionals test result name",
@@ -29,6 +32,10 @@ null
 }
 
 -- Running test case: AuditTestCaseResult.fromPayload.validWithInvalidSubOptionals
+WARN: Audit Warning: "validWithInvalidSubOptionals test result name" has a non-object "metadata.startTimestamp" value
+WARN: Audit Warning: "validWithInvalidSubOptionals test result name" has a non-object "metadata.asyncTimestamp" value
+WARN: Audit Warning: "validWithInvalidSubOptionals test result name" has a non-object "metadata.endTimestamp" value
+WARN: Audit Warning: "validWithInvalidSubOptionals test result name" has a non-object "metadata.url" value
 {
   "type": "test-case-result",
   "name": "validWithInvalidSubOptionals test result name",
index 99b3935..5e2629e 100644 (file)
@@ -34,13 +34,19 @@ null
 }
 
 -- Running test case: AuditTestGroup.fromPayload.validWithInvalidOptionals
+WARN: Audit Warning: "validWithInvalidOptionals test name" has a non-string "description" value
+WARN: Audit Warning: "validWithInvalidOptionals test name" is too new to run in this Web Inspector
+WARN: Audit Warning: "validWithInvalidOptionals group name" has a non-string "description" value
+WARN: Audit Warning: "validWithInvalidOptionals group name" is too new to run in this Web Inspector
 {
   "type": "test-group",
   "name": "validWithInvalidOptionals group name",
+  "supports": 2,
   "tests": [
     {
       "type": "test-case",
       "name": "validWithInvalidOptionals test name",
+      "supports": 3,
       "test": "validWithInvalidOptionals test function"
     }
   ]
@@ -51,11 +57,13 @@ null
   "type": "test-group",
   "name": "validWithValidOptionals group name",
   "description": "validWithValidOptionals group description",
+  "supports": 0,
   "tests": [
     {
       "type": "test-case",
       "name": "validWithValidOptionals test name",
       "description": "validWithValidOptionals test description",
+      "supports": -1,
       "test": "validWithValidOptionals test function"
     }
   ]
@@ -66,16 +74,19 @@ null
   "type": "test-group",
   "name": "validNested group name",
   "description": "validNested group description",
+  "supports": 0,
   "tests": [
     {
       "type": "test-group",
       "name": "validNested nested group name",
       "description": "validNested nested group description",
+      "supports": -1,
       "tests": [
         {
           "type": "test-case",
           "name": "validNested nested test name",
           "description": "validNested nested test description",
+          "supports": -2,
           "test": "validNested nested test function"
         }
       ]
@@ -84,6 +95,7 @@ null
       "type": "test-case",
       "name": "validNested test name",
       "description": "validNested test description",
+      "supports": -3,
       "test": "validNested test function"
     }
   ]
index bdfa290..dc2193f 100644 (file)
@@ -76,11 +76,13 @@ function test()
                 type: WI.AuditTestGroup.TypeIdentifier,
                 name: "validWithInvalidOptionals group name",
                 description: null,
+                supports: WI.AuditTestBase.Version + 1,
                 tests: [
                     {
                         type: WI.AuditTestCase.TypeIdentifier,
                         name: "validWithInvalidOptionals test name",
                         description: null,
+                        supports: WI.AuditTestBase.Version + 2,
                         test: "validWithInvalidOptionals test function",
                     },
                 ],
@@ -92,11 +94,13 @@ function test()
                 type: WI.AuditTestGroup.TypeIdentifier,
                 name: "validWithValidOptionals group name",
                 description: "validWithValidOptionals group description",
+                supports: WI.AuditTestBase.Version - 1,
                 tests: [
                     {
                         type: WI.AuditTestCase.TypeIdentifier,
                         name: "validWithValidOptionals test name",
                         description: "validWithValidOptionals test description",
+                        supports: WI.AuditTestBase.Version - 2,
                         test: "validWithValidOptionals test function",
                     },
                 ],
@@ -108,16 +112,19 @@ function test()
                 type: WI.AuditTestGroup.TypeIdentifier,
                 name: "validNested group name",
                 description: "validNested group description",
+                supports: WI.AuditTestBase.Version - 1,
                 tests: [
                     {
                         type: WI.AuditTestGroup.TypeIdentifier,
                         name: "validNested nested group name",
                         description: "validNested nested group description",
+                        supports: WI.AuditTestBase.Version - 2,
                         tests: [
                             {
                                 type: WI.AuditTestCase.TypeIdentifier,
                                 name: "validNested nested test name",
                                 description: "validNested nested test description",
+                                supports: WI.AuditTestBase.Version - 3,
                                 test: "validNested nested test function",
                             },
                         ],
@@ -126,6 +133,7 @@ function test()
                         type: WI.AuditTestCase.TypeIdentifier,
                         name: "validNested test name",
                         description: "validNested test description",
+                        supports: WI.AuditTestBase.Version - 4,
                         test: "validNested test function",
                     },
                 ],
index 1b732d9..0aa2df7 100644 (file)
@@ -34,6 +34,10 @@ null
 }
 
 -- Running test case: AuditTestGroupResult.fromPayload.validWithInvalidOptionals
+WARN: Audit Warning: "validWithInvalidOptionals test result name" has a non-object "data" value
+WARN: Audit Warning: "validWithInvalidOptionals test result name" has a non-object "metadata" value
+WARN: Audit Warning: "validWithInvalidOptionals test result name" has a non-string "description" value
+WARN: Audit Warning: "validWithInvalidOptionals group result name" has a non-string "description" value
 {
   "type": "test-group-result",
   "name": "validWithInvalidOptionals group result name",
index 8ccec82..f437abd 100644 (file)
@@ -9,12 +9,19 @@ null
 null
 
 -- Running test case: Recording.fromPayload.emptyObject
+ERROR: Recording Error: non-number version
 null
 
 -- Running test case: Recording.fromPayload.invalidTopLevelMembers
+ERROR: Recording Error: non-number version
 null
 
 -- Running test case: Recording.fromPayload.invalidSubMembers
+WARN: Recording Warning: unknown type "test"
+WARN: Recording Warning: non-object initialState.attributes
+WARN: Recording Warning: non-array initialState.states
+WARN: Recording Warning: non-array initialState.attributes
+WARN: Recording Warning: non-string initialState.content
 {
   "version": 1,
   "type": "test",
@@ -30,6 +37,8 @@ null
 }
 
 -- Running test case: Recording.fromPayload.invalidFrame
+WARN: Recording Warning: unknown type "test"
+WARN: Recording Warning: non-array actions
 {
   "version": 1,
   "type": "test",
@@ -58,6 +67,7 @@ null
 }
 
 -- Running test case: Recording.fromPayload.invalidAction
+WARN: Recording Warning: unknown type "test"
 {
   "version": 1,
   "type": "test",
@@ -95,6 +105,11 @@ null
 }
 
 -- Running test case: Recording.fromPayload.invalidActionMembers
+WARN: Recording Warning: unknown type "test"
+WARN: Recording Warning: non-number name
+WARN: Recording Warning: non-array parameters
+WARN: Recording Warning: non-array swizzleTypes
+WARN: Recording Warning: non-number trace
 {
   "version": 1,
   "type": "test",
@@ -116,7 +131,7 @@ null
     {
       "actions": [
         [
-          null,
+          -1,
           [],
           [],
           []
@@ -132,6 +147,7 @@ null
 }
 
 -- Running test case: Recording.fromPayload.valid
+WARN: Recording Warning: unknown type "test"
 {
   "version": 1,
   "type": "test",
index 775a5ee..1aadbde 100644 (file)
@@ -1,3 +1,42 @@
+2019-01-25  Devin Rousso  <drousso@apple.com>
+
+        Web Inspector: improve invalid Audit/Recording JSON error messages
+        https://bugs.webkit.org/show_bug.cgi?id=193476
+        <rdar://problem/47303659>
+
+        Reviewed by Joseph Pecoraro.
+
+        * UserInterface/Models/AuditTestBase.js:
+        (WI.AuditTestBase):
+        * UserInterface/Models/AuditTestCase.js:
+        (WI.AuditTestCase.async fromPayload):
+        * UserInterface/Models/AuditTestGroup.js:
+        (WI.AuditTestGroup.async fromPayload):
+        * UserInterface/Models/AuditTestCaseResult.js:
+        (WI.AuditTestCaseResult.async fromPayload.checkArray):
+        (WI.AuditTestCaseResult.async fromPayload):
+        * UserInterface/Models/AuditTestGroupResult.js:
+        (WI.AuditTestGroupResult.async fromPayload):
+        * UserInterface/Controllers/AuditManager.js:
+        (WI.AuditManager.synthesizeWarning): Added.
+        (WI.AuditManager.synthesizeError):
+        (WI.AuditManager.prototype.async processJSON):
+
+        * UserInterface/Models/Recording.js:
+        (WI.Recording.fromPayload):
+        (WI.Recording.synthesizeWarning): Added.
+        (WI.Recording.synthesizeError):
+        * UserInterface/Models/RecordingFrame.js:
+        (WI.RecordingFrame.fromPayload):
+        * UserInterface/Models/RecordingAction.js:
+        (WI.RecordingAction.fromPayload):
+        (WI.RecordingAction.prototype.async swizzle):
+        (WI.RecordingAction.prototype.apply):
+        * UserInterface/Controllers/CanvasManager.js:
+        (WI.CanvasManager.prototype.processJSON):
+
+        * Localizations/en.lproj/localizedStrings.js:
+
 2019-01-24  Devin Rousso  <drousso@apple.com>
 
         Web Inspector: Audit: add supports key to test/group for compatibility
index 0fce9f7..14150cd 100644 (file)
@@ -131,7 +131,8 @@ localizedStrings["Attribute"] = "Attribute";
 localizedStrings["Attribute Modified"] = "Attribute Modified";
 localizedStrings["Attributes"] = "Attributes";
 localizedStrings["Audit"] = "Audit";
-localizedStrings["Audit error: %s"] = "Audit error: %s";
+localizedStrings["Audit Error: %s"] = "Audit Error: %s";
+localizedStrings["Audit Warning: %s"] = "Audit Warning: %s";
 localizedStrings["Audit:"] = "Audit:";
 localizedStrings["Audits"] = "Audits";
 localizedStrings["Author Stylesheet"] = "Author Stylesheet";
@@ -726,8 +727,9 @@ localizedStrings["Record first %s frame"] = "Record first %s frame";
 localizedStrings["Record first %s frames"] = "Record first %s frames";
 localizedStrings["Recording"] = "Recording";
 localizedStrings["Recording %d"] = "Recording %d";
+localizedStrings["Recording Error: %s"] = "Recording Error: %s";
 localizedStrings["Recording Timeline Data"] = "Recording Timeline Data";
-localizedStrings["Recording error: %s"] = "Recording error: %s";
+localizedStrings["Recording Warning: %s"] = "Recording Warning: %s";
 localizedStrings["Recordings"] = "Recordings";
 localizedStrings["Redirect Response"] = "Redirect Response";
 localizedStrings["Redirects"] = "Redirects";
@@ -1054,18 +1056,30 @@ localizedStrings["XHRs"] = "XHRs";
 localizedStrings["XPath"] = "XPath";
 localizedStrings["Yes"] = "Yes";
 localizedStrings["Zoom:"] = "Zoom:";
-localizedStrings["\u0022%s\u0022 is invalid."] = "\u0022%s\u0022 is invalid.";
+localizedStrings["\u0022%s\u0022 has a non-array \u0022%s\u0022 value"] = "\u0022%s\u0022 has a non-array \u0022%s\u0022 value";
+localizedStrings["\u0022%s\u0022 has a non-number \u0022%s\u0022 value"] = "\u0022%s\u0022 has a non-number \u0022%s\u0022 value";
+localizedStrings["\u0022%s\u0022 has a non-object \u0022%s\u0022 value"] = "\u0022%s\u0022 has a non-object \u0022%s\u0022 value";
+localizedStrings["\u0022%s\u0022 has a non-string \u0022%s\u0022 value"] = "\u0022%s\u0022 has a non-string \u0022%s\u0022 value";
+localizedStrings["\u0022%s\u0022 has an invalid \u0022%s\u0022 value"] = "\u0022%s\u0022 has an invalid \u0022%s\u0022 value";
+localizedStrings["\u0022%s\u0022 is not valid for %s"] = "\u0022%s\u0022 is not valid for %s";
+localizedStrings["\u0022%s\u0022 is too new to run in this Web Inspector"] = "\u0022%s\u0022 is too new to run in this Web Inspector";
+localizedStrings["\u0022%s\u0022 is too new to run on this inspected page"] = "\u0022%s\u0022 is too new to run on this inspected page";
 localizedStrings["\u0022%s\u0022 must be a %s"] = "\u0022%s\u0022 must be a %s";
 localizedStrings["\u0022%s\u0022 must be an %s"] = "\u0022%s\u0022 must be an %s";
-localizedStrings["\u0022%s\u0022 threw an error."] = "\u0022%s\u0022 threw an error.";
+localizedStrings["\u0022%s\u0022 threw an error"] = "\u0022%s\u0022 threw an error";
 localizedStrings["\u201C%s\u201D Event Fired"] = "\u201C%s\u201D Event Fired";
 localizedStrings["\u201C%s\u201D Profile Recorded"] = "\u201C%s\u201D Profile Recorded";
 localizedStrings["computed"] = "computed";
 localizedStrings["default"] = "default";
 localizedStrings["for changes to take effect"] = "for changes to take effect";
-localizedStrings["invalid JSON."] = "invalid JSON.";
+localizedStrings["invalid JSON"] = "invalid JSON";
 localizedStrings["key"] = "key";
 localizedStrings["line "] = "line ";
+localizedStrings["non-array %s"] = "non-array %s";
+localizedStrings["non-integer %s"] = "non-integer %s";
+localizedStrings["non-number %s"] = "non-number %s";
+localizedStrings["non-object %s"] = "non-object %s";
+localizedStrings["non-string %s"] = "non-string %s";
 localizedStrings["originally %s"] = "originally %s";
 localizedStrings["popup"] = "popup";
 localizedStrings["popup, toggle"] = "popup, toggle";
@@ -1074,5 +1088,6 @@ localizedStrings["spaces"] = "spaces";
 localizedStrings["time before stopping"] = "time before stopping";
 localizedStrings["times before stopping"] = "times before stopping";
 localizedStrings["toggle"] = "toggle";
-localizedStrings["unsupported version."] = "unsupported version.";
+localizedStrings["unknown %s \u0022%s\u0022"] = "unknown %s \u0022%s\u0022";
+localizedStrings["unsupported %s"] = "unsupported %s";
 localizedStrings["value"] = "value";
index 3732c55..45a789d 100644 (file)
@@ -40,9 +40,33 @@ WI.AuditManager = class AuditManager extends WI.Object
         WI.Frame.addEventListener(WI.Frame.Event.MainResourceDidChange, this._handleFrameMainResourceDidChange, this);
     }
 
+    // Static
+
+    static synthesizeWarning(message)
+    {
+        message = WI.UIString("Audit Warning: %s").format(message);
+
+        if (window.InspectorTest) {
+            console.warn(message);
+            return;
+        }
+
+        let consoleMessage = new WI.ConsoleMessage(WI.mainTarget, WI.ConsoleMessage.MessageSource.Other, WI.ConsoleMessage.MessageLevel.Warning, message);
+        consoleMessage.shouldRevealConsole = true;
+
+        WI.consoleLogViewController.appendConsoleMessage(consoleMessage);
+    }
+
     static synthesizeError(message)
     {
-        let consoleMessage = new WI.ConsoleMessage(WI.mainTarget, WI.ConsoleMessage.MessageSource.Other, WI.ConsoleMessage.MessageLevel.Error, WI.UIString("Audit error: %s").format(message));
+        message = WI.UIString("Audit 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);
@@ -169,15 +193,21 @@ WI.AuditManager = class AuditManager extends WI.Object
             return;
         }
 
-        let object = await WI.AuditTestGroup.fromPayload(json) || await WI.AuditTestCase.fromPayload(json);
-        if (!object) {
-            object = await WI.AuditTestGroupResult.fromPayload(json) || await WI.AuditTestCaseResult.fromPayload(json);
-            if (!object) {
-                WI.AuditManager.synthesizeError(WI.UIString("invalid JSON."));
-                return;
-            }
+        if (typeof json !== "object" || json === null) {
+            WI.AuditManager.synthesizeError(WI.UIString("invalid JSON"));
+            return;
         }
 
+        if (json.type !== WI.AuditTestCase.TypeIdentifier || json.type !== WI.AuditTestGroup.TypeIdentifier
+            || json.type !== WI.AuditTestCaseResult.TypeIdentifier || json.type !== WI.AuditTestGroupResult.TypeIdentifier) {
+            WI.AuditManager.synthesizeError(WI.UIString("unknown %s \u0022%s\u0022").format(WI.unlocalizedString("type"), json.type));
+            return;
+        }
+
+        let object = await WI.AuditTestGroup.fromPayload(json) || await WI.AuditTestCase.fromPayload(json) || await WI.AuditTestGroupResult.fromPayload(json) || await WI.AuditTestCaseResult.fromPayload(json);
+        if (!object)
+            return;
+
         if (object instanceof WI.AuditTestBase) {
             this._addTest(object);
             WI.objectStores.audits.addObject(object);
index 317815d..a9a09df 100644 (file)
@@ -74,12 +74,15 @@ WI.CanvasManager = class CanvasManager extends WI.Object
             return;
         }
 
-        let recording = WI.Recording.fromPayload(json);
-        if (!recording) {
-            WI.Recording.synthesizeError(WI.UIString("unsupported version."));
+        if (typeof json !== "object" || json === null) {
+            WI.Recording.synthesizeError(WI.UIString("invalid JSON"));
             return;
         }
 
+        let recording = WI.Recording.fromPayload(json);
+        if (!recording)
+            return;
+
         let extensionStart = filename.lastIndexOf(".");
         if (extensionStart !== -1)
             filename = filename.substring(0, extensionStart);
index 7133550..0a2b5ca 100644 (file)
@@ -43,10 +43,13 @@ WI.AuditTestBase = class AuditTestBase extends WI.Object
 
         this._supported = true;
         if (typeof this._supports === "number") {
-            if (this._supports > WI.AuditTestBase.Version)
+            if (this._supports > WI.AuditTestBase.Version) {
+                WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 is too new to run in this Web Inspector").format(this.name));
                 this._supported = false;
-            else if (InspectorBackend.domains.Audit && this._supports > InspectorBackend.domains.Audit.VERSION)
+            } else if (InspectorBackend.domains.Audit && this._supports > InspectorBackend.domains.Audit.VERSION) {
+                WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 is too new to run on this inspected page").format(this.name));
                 this._supported = false;
+            }
         }
 
         if (!this.supported)
index 6c36765..85f1954 100644 (file)
@@ -41,26 +41,35 @@ WI.AuditTestCase = class AuditTestCase extends WI.AuditTestBase
         if (typeof payload !== "object" || payload === null)
             return null;
 
-        let {type, name, test, description, supports, disabled} = payload;
-
-        if (type !== WI.AuditTestCase.TypeIdentifier)
+        if (payload.type !== WI.AuditTestCase.TypeIdentifier)
             return null;
 
-        if (typeof name !== "string")
+        if (typeof payload.name !== "string") {
+            WI.AuditManager.synthesizeError(WI.UIString("\u0022%s\u0022 has a non-string \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("name")));
             return null;
+        }
 
-        if (typeof test !== "string")
+        if (typeof payload.test !== "string") {
+            WI.AuditManager.synthesizeError(WI.UIString("\u0022%s\u0022 has a non-string \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("test")));
             return null;
+        }
 
         let options = {};
-        if (typeof description === "string")
-            options.description = description;
-        if (typeof supports === "number")
-            options.supports = supports;
-        if (typeof disabled === "boolean")
-            options.disabled = disabled;
-
-        return new WI.AuditTestCase(name, test, options);
+
+        if (typeof payload.description === "string")
+            options.description = payload.description;
+        else if ("description" in payload)
+            WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 has a non-string \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("description")));
+
+        if (typeof payload.supports === "number")
+            options.supports = payload.supports;
+        else if ("supports" in payload)
+            WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 has a non-number \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("supports")));
+
+        if (typeof payload.disabled === "boolean")
+            options.disabled = payload.disabled;
+
+        return new WI.AuditTestCase(payload.name, payload.test, options);
     }
 
     // Public
index 23dd9cd..47e7b0b 100644 (file)
@@ -48,52 +48,94 @@ WI.AuditTestCaseResult = class AuditTestCaseResult extends WI.AuditTestResultBas
         if (typeof payload !== "object" || payload === null)
             return null;
 
-        let {type, name, description, level, data, metadata} = payload;
-
-        if (type !== WI.AuditTestCaseResult.TypeIdentifier)
+        if (payload.type !== WI.AuditTestCaseResult.TypeIdentifier)
             return null;
 
-        if (typeof name !== "string")
+        if (typeof payload.name !== "string") {
+            WI.AuditManager.synthesizeError(WI.UIString("\u0022%s\u0022 has a non-string \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("name")));
             return null;
+        }
 
-        if (!Object.values(WI.AuditTestCaseResult.Level).includes(level))
+        if (!Object.values(WI.AuditTestCaseResult.Level).includes(payload.level)) {
+            WI.AuditManager.synthesizeError(WI.UIString("\u0022%s\u0022 has an invalid \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("level")));
             return null;
+        }
 
-        if (typeof data !== "object" || data === null)
-            data = {};
-        else {
+        if (typeof payload.data !== "object" || payload.data === null) {
+            if ("data" in payload)
+                WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 has a non-object \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("data")));
+            payload.data = {};
+        } else {
             function checkArray(key) {
-                if (!data[key])
+                if (!payload.data[key])
                     return;
 
-                if (!Array.isArray(data[key]))
-                    data[key] = [];
+                if (!Array.isArray(payload.data[key])) {
+                    WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 has a non-array \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("data.%s").format(key)));
+                    payload.data[key] = [];
+                }
 
-                data[key] = data[key].filter((item) => typeof item === "string");
+                payload.data[key] = payload.data[key].filter((item) => typeof item === "string");
             }
             checkArray("domNodes");
             checkArray("domAttributes");
             checkArray("errors");
         }
 
-        if (typeof metadata !== "object" || metadata === null)
-            metadata = {};
-        else {
-            metadata.startTimestamp = typeof metadata.startTimestamp === "string" ? new Date(metadata.startTimestamp) : null;
-            metadata.asyncTimestamp = typeof metadata.asyncTimestamp === "string" ? new Date(metadata.asyncTimestamp) : null;
-            metadata.endTimestamp = typeof metadata.endTimestamp === "string" ? new Date(metadata.endTimestamp) : null;
-            metadata.url = typeof metadata.url === "string" ? metadata.url : null;
+        if (typeof payload.metadata !== "object" || payload.metadata === null) {
+            if ("metadata" in payload)
+                WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 has a non-object \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("metadata")));
+
+            payload.metadata = {};
+        } else {
+            if (typeof payload.metadata.startTimestamp === "string")
+                payload.metadata.startTimestamp = new Date(payload.metadata.startTimestamp);
+            else {
+                if ("startTimestamp" in payload.metadata)
+                    WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 has a non-object \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("metadata.startTimestamp")));
+
+                payload.metadata.startTimestamp = null;
+            }
+
+            if (typeof payload.metadata.asyncTimestamp === "string")
+                payload.metadata.asyncTimestamp = new Date(payload.metadata.asyncTimestamp);
+            else {
+                if ("asyncTimestamp" in payload.metadata)
+                    WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 has a non-object \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("metadata.asyncTimestamp")));
+
+                payload.metadata.asyncTimestamp = null;
+            }
+
+            if (typeof payload.metadata.endTimestamp === "string")
+                payload.metadata.endTimestamp = new Date(payload.metadata.endTimestamp);
+            else {
+                if ("endTimestamp" in payload.metadata)
+                    WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 has a non-object \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("metadata.endTimestamp")));
+
+                payload.metadata.endTimestamp = null;
+            }
+
+            if (typeof payload.metadata.url !== "string") {
+                if ("url" in payload.metadata)
+                    WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 has a non-object \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("metadata.url")));
+
+                payload.metadata.url = null;
+            }
         }
 
         let options = {};
-        if (typeof description === "string")
-            options.description = description;
-        if (!isEmptyObject(data)) {
+
+        if (typeof payload.description === "string")
+            options.description = payload.description;
+        else if ("description" in payload)
+            WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 has a non-string \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("description")));
+
+        if (!isEmptyObject(payload.data)) {
             options.data = {};
-            if (data.domNodes && data.domNodes.length) {
-                if (window.DOMAgent && (!metadata.url || metadata.url === WI.networkManager.mainFrame.url)) {
+            if (payload.data.domNodes && payload.data.domNodes.length) {
+                if (window.DOMAgent && (!payload.metadata.url || payload.metadata.url === WI.networkManager.mainFrame.url)) {
                     let documentNode = await new Promise((resolve) => WI.domManager.requestDocument(resolve));
-                    options.resolvedDOMNodes = await Promise.all(data.domNodes.map(async (domNodeString) => {
+                    options.resolvedDOMNodes = await Promise.all(payload.data.domNodes.map(async (domNodeString) => {
                         let nodeId = 0;
                         try {
                             nodeId = await WI.domManager.querySelector(documentNode, domNodeString);
@@ -102,26 +144,27 @@ WI.AuditTestCaseResult = class AuditTestCaseResult extends WI.AuditTestResultBas
                     }));
                 }
 
-                options.data.domNodes = data.domNodes;
+                options.data.domNodes = payload.data.domNodes;
             }
-            if (data.domAttributes && data.domAttributes.length)
-                options.data.domAttributes = data.domAttributes;
-            if (data.errors && data.errors.length)
-                options.data.errors = data.errors;
+            if (payload.data.domAttributes && payload.data.domAttributes.length)
+                options.data.domAttributes = payload.data.domAttributes;
+            if (payload.data.errors && payload.data.errors.length)
+                options.data.errors = payload.data.errors;
         }
-        if (!isEmptyObject(metadata)) {
+
+        if (!isEmptyObject(payload.metadata)) {
             options.metadata = {};
-            if (metadata.startTimestamp && !isNaN(metadata.startTimestamp))
-                options.metadata.startTimestamp = metadata.startTimestamp;
-            if (metadata.asyncTimestamp && !isNaN(metadata.asyncTimestamp))
-                options.metadata.asyncTimestamp = metadata.asyncTimestamp;
-            if (metadata.endTimestamp && !isNaN(metadata.endTimestamp))
-                options.metadata.endTimestamp = metadata.endTimestamp;
-            if (metadata.url)
-                options.metadata.url = metadata.url;
+            if (payload.metadata.startTimestamp && !isNaN(payload.metadata.startTimestamp))
+                options.metadata.startTimestamp = payload.metadata.startTimestamp;
+            if (payload.metadata.asyncTimestamp && !isNaN(payload.metadata.asyncTimestamp))
+                options.metadata.asyncTimestamp = payload.metadata.asyncTimestamp;
+            if (payload.metadata.endTimestamp && !isNaN(payload.metadata.endTimestamp))
+                options.metadata.endTimestamp = payload.metadata.endTimestamp;
+            if (payload.metadata.url)
+                options.metadata.url = payload.metadata.url;
         }
 
-        return new WI.AuditTestCaseResult(name, level, options);
+        return new WI.AuditTestCaseResult(payload.name, payload.level, options);
     }
 
     // Public
index cda7d4e..ef38078 100644 (file)
@@ -66,18 +66,20 @@ WI.AuditTestGroup = class AuditTestGroup extends WI.AuditTestBase
         if (typeof payload !== "object" || payload === null)
             return null;
 
-        let {type, name, tests, description, supports, disabled} = payload;
-
-        if (type !== WI.AuditTestGroup.TypeIdentifier)
+        if (payload.type !== WI.AuditTestGroup.TypeIdentifier)
             return null;
 
-        if (typeof name !== "string")
+        if (typeof payload.name !== "string") {
+            WI.AuditManager.synthesizeError(WI.UIString("\u0022%s\u0022 has a non-string \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("name")));
             return null;
+        }
 
-        if (!Array.isArray(tests))
+        if (!Array.isArray(payload.tests)) {
+            WI.AuditManager.synthesizeError(WI.UIString("\u0022%s\u0022 has a non-array \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("tests")));
             return null;
+        }
 
-        tests = await Promise.all(tests.map(async (test) => {
+        let tests = await Promise.all(payload.tests.map(async (test) => {
             let testCase = await WI.AuditTestCase.fromPayload(test);
             if (testCase)
                 return testCase;
@@ -93,14 +95,21 @@ WI.AuditTestGroup = class AuditTestGroup extends WI.AuditTestBase
             return null;
 
         let options = {};
-        if (typeof description === "string")
-            options.description = description;
-        if (typeof supports === "number")
-            options.supports = supports;
-        if (typeof disabled === "boolean")
-            options.disabled = disabled;
-
-        return new WI.AuditTestGroup(name, tests, options);
+
+        if (typeof payload.description === "string")
+            options.description = payload.description;
+        else if ("description" in payload)
+            WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 has a non-string \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("description")));
+
+        if (typeof payload.supports === "number")
+            options.supports = payload.supports;
+        else if ("supports" in payload)
+            WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 has a non-number \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("supports")));
+
+        if (typeof payload.disabled === "boolean")
+            options.disabled = payload.disabled;
+
+        return new WI.AuditTestGroup(payload.name, tests, options);
     }
 
     // Public
index d03cd1c..397d3f9 100644 (file)
@@ -41,18 +41,20 @@ WI.AuditTestGroupResult = class AuditTestGroupResult extends WI.AuditTestResultB
         if (typeof payload !== "object" || payload === null)
             return null;
 
-        let {type, name, description, results} = payload;
-
-        if (type !== WI.AuditTestGroupResult.TypeIdentifier)
+        if (payload.type !== WI.AuditTestGroupResult.TypeIdentifier)
             return null;
 
-        if (typeof name !== "string")
+        if (typeof payload.name !== "string") {
+            WI.AuditManager.synthesizeError(WI.UIString("\u0022%s\u0022 has a non-string \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("name")));
             return null;
+        }
 
-        if (!Array.isArray(results))
+        if (!Array.isArray(payload.results)) {
+            WI.AuditManager.synthesizeError(WI.UIString("\u0022%s\u0022 has a non-array \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("results")));
             return null;
+        }
 
-        results = await Promise.all(results.map(async (test) => {
+        let results = await Promise.all(payload.results.map(async (test) => {
             let testCaseResult = await WI.AuditTestCaseResult.fromPayload(test);
             if (testCaseResult)
                 return testCaseResult;
@@ -68,10 +70,13 @@ WI.AuditTestGroupResult = class AuditTestGroupResult extends WI.AuditTestResultB
             return null;
 
         let options = {};
-        if (typeof description === "string")
-            options.description = description;
 
-        return new WI.AuditTestGroupResult(name, results, options);
+        if (typeof payload.description === "string")
+            options.description = payload.description;
+        else if ("description" in payload)
+            WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 has a non-string \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("description")));
+
+        return new WI.AuditTestGroupResult(payload.name, results, options);
     }
 
     // Public
index e53f7b0..4354c2b 100644 (file)
@@ -51,8 +51,20 @@ WI.Recording = class Recording extends WI.Object
         if (typeof payload !== "object" || payload === null)
             return null;
 
-        if (isNaN(payload.version) || payload.version <= 0 || payload.version > WI.Recording.Version)
+        if (typeof payload.version !== "number") {
+            WI.Recording.synthesizeError(WI.UIString("non-number %s").format(WI.unlocalizedString("version")));
             return null;
+        }
+
+        if (payload.version < 1 || payload.version > WI.Recording.Version) {
+            WI.Recording.synthesizeError(WI.UIString("unsupported %s").format(WI.unlocalizedString("version")));
+            return null;
+        }
+
+        if (parseInt(payload.version) !== payload.version) {
+            WI.Recording.synthesizeWarning(WI.UIString("non-integer %s").format(WI.unlocalizedString("version")));
+            payload.version = parseInt(payload.version);
+        }
 
         let type = null;
         switch (payload.type) {
@@ -66,15 +78,29 @@ WI.Recording = class Recording extends WI.Object
             type = WI.Recording.Type.CanvasWebGL;
             break;
         default:
+            WI.Recording.synthesizeWarning(WI.UIString("unknown %s \u0022%s\u0022").format(WI.unlocalizedString("type"), payload.type));
             type = String(payload.type);
             break;
         }
 
-        if (typeof payload.initialState !== "object" || payload.initialState === null)
+        if (typeof payload.initialState !== "object" || payload.initialState === null) {
+            if ("initialState" in payload)
+                WI.Recording.synthesizeWarning(WI.UIString("non-object %s").format(WI.unlocalizedString("initialState")));
+
             payload.initialState = {};
-        if (typeof payload.initialState.attributes !== "object" || payload.initialState.attributes === null)
+        }
+
+        if (typeof payload.initialState.attributes !== "object" || payload.initialState.attributes === null) {
+            if ("attributes" in payload.initialState)
+                WI.Recording.synthesizeWarning(WI.UIString("non-object %s").format(WI.unlocalizedString("initialState.attributes")));
+
             payload.initialState.attributes = {};
+        }
+
         if (!Array.isArray(payload.initialState.states) || payload.initialState.states.some((item) => typeof item !== "object" || item === null)) {
+            if ("states" in payload.initialState)
+                WI.Recording.synthesizeWarning(WI.UIString("non-array %s").format(WI.unlocalizedString("initialState.states")));
+
             payload.initialState.states = [];
 
             // COMPATIBILITY (iOS 12.0): Recording.InitialState.states did not exist yet
@@ -84,16 +110,34 @@ WI.Recording = class Recording extends WI.Object
                     payload.initialState.states.push(state);
             }
         }
-        if (!Array.isArray(payload.initialState.parameters))
+
+        if (!Array.isArray(payload.initialState.parameters)) {
+            if ("parameters" in payload.initialState)
+                WI.Recording.synthesizeWarning(WI.UIString("non-array %s").format(WI.unlocalizedString("initialState.attributes")));
+
             payload.initialState.parameters = [];
-        if (typeof payload.initialState.content !== "string")
+        }
+
+        if (typeof payload.initialState.content !== "string") {
+            if ("content" in payload.initialState)
+                WI.Recording.synthesizeWarning(WI.UIString("non-string %s").format(WI.unlocalizedString("initialState.content")));
+
             payload.initialState.content = "";
+        }
+
+        if (!Array.isArray(payload.frames)) {
+            if ("frames" in payload)
+                WI.Recording.synthesizeWarning(WI.UIString("non-array %s").format(WI.unlocalizedString("frames")));
 
-        if (!Array.isArray(payload.frames))
             payload.frames = [];
+        }
+
+        if (!Array.isArray(payload.data)) {
+            if ("data" in payload)
+                WI.Recording.synthesizeWarning(WI.UIString("non-array %s").format(WI.unlocalizedString("data")));
 
-        if (!Array.isArray(payload.data))
             payload.data = [];
+        }
 
         if (!frames)
             frames = payload.frames.map(WI.RecordingFrame.fromPayload)
@@ -150,12 +194,31 @@ WI.Recording = class Recording extends WI.Object
         }
     }
 
+    static synthesizeWarning(message)
+    {
+        message = WI.UIString("Recording Warning: %s").format(message);
+
+        if (window.InspectorTest) {
+            console.warn(message);
+            return;
+        }
+
+        let consoleMessage = new WI.ConsoleMessage(WI.mainTarget, WI.ConsoleMessage.MessageSource.Other, WI.ConsoleMessage.MessageLevel.Warning, message);
+        consoleMessage.shouldRevealConsole = true;
+
+        WI.consoleLogViewController.appendConsoleMessage(consoleMessage);
+    }
+
     static synthesizeError(message)
     {
-        const target = WI.mainTarget;
-        const source = WI.ConsoleMessage.MessageSource.Other;
-        const level = WI.ConsoleMessage.MessageLevel.Error;
-        let consoleMessage = new WI.ConsoleMessage(target, source, level, WI.UIString("Recording error: %s").format(message));
+        message = WI.UIString("Recording 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);
index 534255b..c5717c7 100644 (file)
@@ -63,23 +63,43 @@ WI.RecordingAction = class RecordingAction extends WI.Object
         if (!Array.isArray(payload))
             payload = [];
 
-        if (isNaN(payload[0]))
+        if (typeof payload[0] !== "number") {
+            if (payload.length > 0)
+                WI.Recording.synthesizeWarning(WI.UIString("non-number %s").format(WI.unlocalizedString("name")));
+
             payload[0] = -1;
+        }
+
+        if (!Array.isArray(payload[1])) {
+            if (payload.length > 1)
+                WI.Recording.synthesizeWarning(WI.UIString("non-array %s").format(WI.unlocalizedString("parameters")));
 
-        if (!Array.isArray(payload[1]))
             payload[1] = [];
+        }
+
+        if (!Array.isArray(payload[2])) {
+            if (payload.length > 2)
+                WI.Recording.synthesizeWarning(WI.UIString("non-array %s").format(WI.unlocalizedString("swizzleTypes")));
 
-        if (!Array.isArray(payload[2]))
             payload[2] = [];
+        }
 
-        if (isNaN(payload[3]) || (!payload[3] && payload[3] !== 0)) {
+        if (typeof payload[3] !== "number" || isNaN(payload[3]) || (!payload[3] && payload[3] !== 0)) {
             // COMPATIBILITY (iOS 12.1): "trace" was sent as an array of call frames instead of a single call stack
-            if (!Array.isArray(payload[3]))
+            if (!Array.isArray(payload[3])) {
+                if (payload.length > 3)
+                    WI.Recording.synthesizeWarning(WI.UIString("non-number %s").format(WI.unlocalizedString("trace")));
+
                 payload[3] = [];
+            }
         }
 
-        if (payload.length >= 5 && isNaN(payload[4]))
+        if (typeof payload[4] !== "number" || isNaN(payload[4])) {
+            if (payload.length > 4)
+                WI.Recording.synthesizeWarning(WI.UIString("non-number %s").format(WI.unlocalizedString("snapshot")));
+
             payload[4] = -1;
+        }
 
         return new WI.RecordingAction(...payload);
     }
@@ -310,7 +330,7 @@ WI.RecordingAction = class RecordingAction extends WI.Object
                 if (prototype && !(name in prototype)) {
                     this.markInvalid();
 
-                    WI.Recording.synthesizeError(WI.UIString("\u0022%s\u0022 is invalid.").format(name));
+                    WI.Recording.synthesizeWarning(WI.UIString("\u0022%s\u0022 is not valid for %s").format(name, prototype.constructor.name));
                 }
             }
         }
@@ -360,7 +380,7 @@ WI.RecordingAction = class RecordingAction extends WI.Object
         } catch {
             this.markInvalid();
 
-            WI.Recording.synthesizeError(WI.UIString("\u0022%s\u0022 threw an error.").format(this._name));
+            WI.Recording.synthesizeWarning(WI.UIString("\u0022%s\u0022 threw an error").format(this._name));
         }
     }
 
index 6a2a620..b6ab23a 100644 (file)
@@ -39,8 +39,12 @@ WI.RecordingFrame = class RecordingFrame
         if (typeof payload !== "object" || payload === null)
             payload = {};
 
-        if (!Array.isArray(payload.actions))
+        if (!Array.isArray(payload.actions)) {
+            if ("actions" in payload)
+                WI.Recording.synthesizeWarning(WI.UIString("non-array %s").format(WI.unlocalizedString("actions")));
+
             payload.actions = [];
+        }
 
         let actions = payload.actions.map(WI.RecordingAction.fromPayload);
         return new WI.RecordingFrame(actions, {