Title: [237808] trunk
Revision
237808
Author
[email protected]
Date
2018-11-05 09:34:48 -0800 (Mon, 05 Nov 2018)

Log Message

Web Inspector: show save/restore stack for recorded 2D Canvases
https://bugs.webkit.org/show_bug.cgi?id=175283
<rdar://problem/34040756>

Reviewed by Matt Baker.

Source/WebInspectorUI:

Instead of using plain objects for holding the `WI.Recording`'s state, use a model object
so that more data can be passed around. When visualizing the state, show any previously
saved states in a `WI.DetailsSection` underneath the current state. If there are no saved
states (meaning there is only the current state), don't use a `WI.DetailsSection`.

* UserInterface/Models/RecordingState.js: Added.
(WI.RecordingState):
(WI.RecordingState.fromContext):
(WI.RecordingState.async swizzleInitialState):
(WI.RecordingState.prototype.get source):
(WI.RecordingState.prototype.has):
(WI.RecordingState.prototype.get return):
(WI.RecordingState.prototype.toJSON):
(WI.RecordingState.prototype.[Symbol.iterator]):

* UserInterface/Models/Recording.js:
(WI.Recording.prototype.async _process):
(WI.Recording.prototype.async _swizzleState): Deleted.

* UserInterface/Models/RecordingAction.js:
(WI.RecordingAction.prototype.process):
(WI.RecordingAction.deriveCurrentState): Deleted.

* UserInterface/Views/RecordingContentView.js:
(WI.RecordingContentView.prototype._generateContentCanvas2D):

* UserInterface/Views/RecordingStateDetailsSidebarPanel.js:
(WI.RecordingStateDetailsSidebarPanel):
(WI.RecordingStateDetailsSidebarPanel.prototype.get scrollElement):
(WI.RecordingStateDetailsSidebarPanel.prototype.sizeDidChange): Added.
(WI.RecordingStateDetailsSidebarPanel.prototype._generateDetailsCanvas2D):
(WI.RecordingStateDetailsSidebarPanel.prototype._generateDetailsCanvas2D.isColorProperty): Deleted.
(WI.RecordingStateDetailsSidebarPanel.prototype._generateDetailsCanvas2D.createInlineSwatch): Deleted.
* UserInterface/Views/RecordingStateDetailsSidebarPanel.css:
(.sidebar > .panel.details.recording-state > .content .details-section > .header .source): Added.
(.sidebar > .panel.details.recording-state > .content .data-grid tr.modified): Added.
(.sidebar > .panel.details.recording-state > .content .data-grid tr:not(.selected).non-standard .name-column): Added.
(.sidebar > .panel.details.recording-state > .content .data-grid tr:not(.selected) .unavailable): Added.
(.sidebar > .panel.details.recording-state > .content .data-grid .inline-swatch): Added.
(.sidebar > .panel.details.recording-state > .content > .data-grid tr.modified): Deleted.
(.sidebar > .panel.details.recording-state > .content > .data-grid tr:not(.selected).non-standard): Deleted.
(.sidebar > .panel.details.recording-state > .content > .data-grid tr:not(.selected) .unavailable): Deleted.
(.sidebar > .panel.details.recording-state > .content > .data-grid .inline-swatch): Deleted.

* UserInterface/Main.html:
* UserInterface/Test.html:

* Localizations/en.lproj/localizedStrings.js:

LayoutTests:

* inspector/canvas/recording-2d.html:
* inspector/canvas/resources/recording-utilities.js:
(TestPage.registerInitializer.log):
(TestPage.registerInitializer.async logRecording):

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (237807 => 237808)


--- trunk/LayoutTests/ChangeLog	2018-11-05 17:33:54 UTC (rev 237807)
+++ trunk/LayoutTests/ChangeLog	2018-11-05 17:34:48 UTC (rev 237808)
@@ -1,3 +1,16 @@
+2018-11-05  Devin Rousso  <[email protected]>
+
+        Web Inspector: show save/restore stack for recorded 2D Canvases
+        https://bugs.webkit.org/show_bug.cgi?id=175283
+        <rdar://problem/34040756>
+
+        Reviewed by Matt Baker.
+
+        * inspector/canvas/recording-2d.html:
+        * inspector/canvas/resources/recording-utilities.js:
+        (TestPage.registerInitializer.log):
+        (TestPage.registerInitializer.async logRecording):
+
 2018-11-05  Thibault Saunier  <[email protected]>
 
         [GStreamer][WebRTC] Error out when simulcast is activated

Modified: trunk/LayoutTests/inspector/canvas/recording-2d.html (237807 => 237808)


--- trunk/LayoutTests/inspector/canvas/recording-2d.html	2018-11-05 17:33:54 UTC (rev 237807)
+++ trunk/LayoutTests/inspector/canvas/recording-2d.html	2018-11-05 17:34:48 UTC (rev 237808)
@@ -519,8 +519,8 @@
 
             async function logStates(recording) {
                 async function compare(index, expected) {
-                    let swizzledState = await recording._swizzleState(recording.initialState.states[index]);
-                    InspectorTest.expectEqual(swizzledState["fillStyle"], expected, `State ${index} should match expected fillStyle value.`)
+                    let state = await WI.RecordingState.swizzleInitialState(recording, recording.initialState.states[index]);
+                    InspectorTest.expectEqual(state.get("fillStyle"), expected, `State ${index} should match expected fillStyle value.`)
                 }
 
                 await compare(0, "#000000");

Modified: trunk/LayoutTests/inspector/canvas/resources/recording-utilities.js (237807 => 237808)


--- trunk/LayoutTests/inspector/canvas/resources/recording-utilities.js	2018-11-05 17:33:54 UTC (rev 237807)
+++ trunk/LayoutTests/inspector/canvas/resources/recording-utilities.js	2018-11-05 17:34:48 UTC (rev 237808)
@@ -1,12 +1,11 @@
 TestPage.registerInitializer(() => {
     function log(object, indent) {
-        for (let key of Object.keys(object)) {
-            let value = object[key];
+        for (let [name, value] of object) {
             if (typeof value === "string")
                 value = sanitizeURL(value);
             else if (Array.isArray(value) && value[0] instanceof DOMMatrix)
                 value[0] = [value[0].a, value[0].b, value[0].c, value[0].d, value[0].e, value[0].f];
-            InspectorTest.log(indent + key + ": " + JSON.stringify(value));
+            InspectorTest.log(indent + name + ": " + JSON.stringify(value));
         }
     }
 
@@ -14,17 +13,17 @@
         InspectorTest.log("initialState:");
 
         InspectorTest.log("  attributes:");
-        log(recording.initialState.attributes, "    ");
+        log(Object.entries(recording.initialState.attributes), "    ");
 
         let currentState = recording.initialState.states.lastValue;
         if (currentState) {
             InspectorTest.log("  current state:");
-            let swizzledState = await recording._swizzleState(currentState);
-            log(swizzledState, "    ");
+            let state = await WI.RecordingState.swizzleInitialState(recording, currentState);
+            log(state, "    ");
         }
 
         InspectorTest.log("  parameters:");
-        log(recording.initialState.parameters, "    ");
+        log(Object.entries(recording.initialState.parameters), "    ");
 
         InspectorTest.log("  content: " + JSON.stringify(recording.initialState.content));
 

Modified: trunk/Source/WebInspectorUI/ChangeLog (237807 => 237808)


--- trunk/Source/WebInspectorUI/ChangeLog	2018-11-05 17:33:54 UTC (rev 237807)
+++ trunk/Source/WebInspectorUI/ChangeLog	2018-11-05 17:34:48 UTC (rev 237808)
@@ -1,3 +1,60 @@
+2018-11-05  Devin Rousso  <[email protected]>
+
+        Web Inspector: show save/restore stack for recorded 2D Canvases
+        https://bugs.webkit.org/show_bug.cgi?id=175283
+        <rdar://problem/34040756>
+
+        Reviewed by Matt Baker.
+
+        Instead of using plain objects for holding the `WI.Recording`'s state, use a model object
+        so that more data can be passed around. When visualizing the state, show any previously
+        saved states in a `WI.DetailsSection` underneath the current state. If there are no saved
+        states (meaning there is only the current state), don't use a `WI.DetailsSection`.
+
+        * UserInterface/Models/RecordingState.js: Added.
+        (WI.RecordingState):
+        (WI.RecordingState.fromContext):
+        (WI.RecordingState.async swizzleInitialState):
+        (WI.RecordingState.prototype.get source):
+        (WI.RecordingState.prototype.has):
+        (WI.RecordingState.prototype.get return):
+        (WI.RecordingState.prototype.toJSON):
+        (WI.RecordingState.prototype.[Symbol.iterator]):
+
+        * UserInterface/Models/Recording.js:
+        (WI.Recording.prototype.async _process):
+        (WI.Recording.prototype.async _swizzleState): Deleted.
+
+        * UserInterface/Models/RecordingAction.js:
+        (WI.RecordingAction.prototype.process):
+        (WI.RecordingAction.deriveCurrentState): Deleted.
+
+        * UserInterface/Views/RecordingContentView.js:
+        (WI.RecordingContentView.prototype._generateContentCanvas2D):
+
+        * UserInterface/Views/RecordingStateDetailsSidebarPanel.js:
+        (WI.RecordingStateDetailsSidebarPanel):
+        (WI.RecordingStateDetailsSidebarPanel.prototype.get scrollElement):
+        (WI.RecordingStateDetailsSidebarPanel.prototype.sizeDidChange): Added.
+        (WI.RecordingStateDetailsSidebarPanel.prototype._generateDetailsCanvas2D):
+        (WI.RecordingStateDetailsSidebarPanel.prototype._generateDetailsCanvas2D.isColorProperty): Deleted.
+        (WI.RecordingStateDetailsSidebarPanel.prototype._generateDetailsCanvas2D.createInlineSwatch): Deleted.
+        * UserInterface/Views/RecordingStateDetailsSidebarPanel.css:
+        (.sidebar > .panel.details.recording-state > .content .details-section > .header .source): Added.
+        (.sidebar > .panel.details.recording-state > .content .data-grid tr.modified): Added.
+        (.sidebar > .panel.details.recording-state > .content .data-grid tr:not(.selected).non-standard .name-column): Added.
+        (.sidebar > .panel.details.recording-state > .content .data-grid tr:not(.selected) .unavailable): Added.
+        (.sidebar > .panel.details.recording-state > .content .data-grid .inline-swatch): Added.
+        (.sidebar > .panel.details.recording-state > .content > .data-grid tr.modified): Deleted.
+        (.sidebar > .panel.details.recording-state > .content > .data-grid tr:not(.selected).non-standard): Deleted.
+        (.sidebar > .panel.details.recording-state > .content > .data-grid tr:not(.selected) .unavailable): Deleted.
+        (.sidebar > .panel.details.recording-state > .content > .data-grid .inline-swatch): Deleted.
+
+        * UserInterface/Main.html:
+        * UserInterface/Test.html:
+
+        * Localizations/en.lproj/localizedStrings.js:
+
 2018-11-03  Devin Rousso  <[email protected]>
 
         Web Inspector: Canvas: capture changes to <canvas> that would affect the recorded context

Modified: trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js (237807 => 237808)


--- trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js	2018-11-05 17:33:54 UTC (rev 237807)
+++ trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js	2018-11-05 17:34:48 UTC (rev 237808)
@@ -54,6 +54,7 @@
 localizedStrings["%s cannot be modified"] = "%s cannot be modified";
 localizedStrings["%s delay"] = "%s delay";
 localizedStrings["%s interval"] = "%s interval";
+localizedStrings["(Action %s)"] = "(Action %s)";
 localizedStrings["(Disk)"] = "(Disk)";
 localizedStrings["(Index)"] = "(Index)";
 localizedStrings["(Memory)"] = "(Memory)";
@@ -252,6 +253,7 @@
 localizedStrings["Create Breakpoint"] = "Create Breakpoint";
 localizedStrings["Create a new tab"] = "Create a new tab";
 localizedStrings["Current"] = "Current";
+localizedStrings["Current State"] = "Current State";
 localizedStrings["Custom"] = "Custom";
 localizedStrings["DNS"] = "DNS";
 localizedStrings["DOM Content Loaded \u2014 %s"] = "DOM Content Loaded \u2014 %s";
@@ -745,9 +747,11 @@
 localizedStrings["Run %d"] = "Run %d";
 localizedStrings["Running the ā€œ%sā€œ audit"] = "Running the ā€œ%sā€œ audit";
 localizedStrings["Samples"] = "Samples";
+localizedStrings["Save %d"] = "Save %d";
 localizedStrings["Save File"] = "Save File";
 localizedStrings["Save Selected"] = "Save Selected";
 localizedStrings["Save configuration"] = "Save configuration";
+localizedStrings["Saved States"] = "Saved States";
 localizedStrings["Scheduling:"] = "Scheduling:";
 localizedStrings["Scheme"] = "Scheme";
 localizedStrings["Scope"] = "Scope";

Modified: trunk/Source/WebInspectorUI/UserInterface/Main.html (237807 => 237808)


--- trunk/Source/WebInspectorUI/UserInterface/Main.html	2018-11-05 17:33:54 UTC (rev 237807)
+++ trunk/Source/WebInspectorUI/UserInterface/Main.html	2018-11-05 17:34:48 UTC (rev 237808)
@@ -413,6 +413,7 @@
     <script src=""
     <script src=""
     <script src=""
+    <script src=""
     <script src=""
     <script src=""
     <script src=""

Modified: trunk/Source/WebInspectorUI/UserInterface/Models/Recording.js (237807 => 237808)


--- trunk/Source/WebInspectorUI/UserInterface/Models/Recording.js	2018-11-05 17:33:54 UTC (rev 237807)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/Recording.js	2018-11-05 17:34:48 UTC (rev 237808)
@@ -375,21 +375,14 @@
                 let initialContent = await WI.ImageUtilities.promisifyLoad(this._initialState.content);
                 this._processContext.drawImage(initialContent, 0, 0);
 
-                for (let state of this._initialState.states) {
-                    let swizzledState = await this._swizzleState(state);
-                    for (let [key, value] of Object.entries(swizzledState)) {
-                        try {
-                            if (WI.RecordingAction.isFunctionForType(this._type, key))
-                                this._processContext[key](...value);
-                            else
-                                this._processContext[key] = value;
-                        } catch { }
-                    }
+                for (let initialState of this._initialState.states) {
+                    let state = await WI.RecordingState.swizzleInitialState(this, initialState);
+                    state.apply(this._type, this._processContext);
 
                     // The last state represents the current state, which should not be saved.
-                    if (state !== this._initialState.states.lastValue) {
+                    if (initialState !== this._initialState.states.lastValue) {
                         this._processContext.save();
-                        this._processStates.push(WI.RecordingAction.deriveCurrentState(this._type, this._processContext));
+                        this._processStates.push(WI.RecordingState.fromContext(this._type, this._processContext));
                     }
                 }
             }
@@ -456,72 +449,6 @@
         this._processContext = null;
         this._processing = false;
     }
-
-    async _swizzleState(state)
-    {
-        let swizzledState = {};
-
-        for (let [key, value] of Object.entries(state)) {
-            // COMPATIBILITY (iOS 12.0): Recording.InitialState.states did not exist yet
-            let keyIndex = parseInt(key);
-            if (!isNaN(keyIndex))
-                key = await this.swizzle(keyIndex, WI.Recording.Swizzle.String);
-
-            switch (key) {
-            case "setTransform":
-                value = [await this.swizzle(value, WI.Recording.Swizzle.DOMMatrix)];
-                break;
-
-            case "fillStyle":
-            case "strokeStyle":
-                    let [gradient, pattern, string] = await Promise.all([
-                        this.swizzle(value, WI.Recording.Swizzle.CanvasGradient),
-                        this.swizzle(value, WI.Recording.Swizzle.CanvasPattern),
-                        this.swizzle(value, WI.Recording.Swizzle.String),
-                    ]);
-                    if (gradient && !pattern)
-                        value = gradient;
-                    else if (pattern && !gradient)
-                        value = pattern;
-                    else
-                        value = string;
-                break;
-
-            case "direction":
-            case "font":
-            case "globalCompositeOperation":
-            case "imageSmoothingQuality":
-            case "lineCap":
-            case "lineJoin":
-            case "shadowColor":
-            case "textAlign":
-            case "textBaseline":
-                value = await this.swizzle(value, WI.Recording.Swizzle.String);
-                break;
-
-            case "globalAlpha":
-            case "lineWidth":
-            case "miterLimit":
-            case "shadowOffsetX":
-            case "shadowOffsetY":
-            case "shadowBlur":
-            case "lineDashOffset":
-                value = await this.swizzle(value, WI.Recording.Swizzle.Number);
-                break;
-
-            case "setPath":
-                value = [await this.swizzle(value[0], WI.Recording.Swizzle.Path2D)];
-                break;
-            }
-
-            if (value === undefined || (Array.isArray(value) && value.includes(undefined)))
-                continue;
-
-            swizzledState[key] = value;
-        }
-
-        return swizzledState;
-    }
 };
 
 WI.Recording.Event = {

Modified: trunk/Source/WebInspectorUI/UserInterface/Models/RecordingAction.js (237807 => 237808)


--- trunk/Source/WebInspectorUI/UserInterface/Models/RecordingAction.js	2018-11-05 17:33:54 UTC (rev 237807)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/RecordingAction.js	2018-11-05 17:34:48 UTC (rev 237808)
@@ -135,52 +135,6 @@
         return null;
     }
 
-    static deriveCurrentState(type, context)
-    {
-        if (type === WI.Recording.Type.Canvas2D) {
-            let matrix = context.getTransform();
-
-            let state = {};
-
-            if (WI.ImageUtilities.supportsCanvasPathDebugging()) {
-                state.currentX = context.currentX;
-                state.currentY = context.currentY;
-            }
-
-            state.direction = context.direction;
-            state.fillStyle = context.fillStyle;
-            state.font = context.font;
-            state.globalAlpha = context.globalAlpha;
-            state.globalCompositeOperation = context.globalCompositeOperation;
-            state.imageSmoothingEnabled = context.imageSmoothingEnabled;
-            state.imageSmoothingQuality = context.imageSmoothingQuality;
-            state.lineCap = context.lineCap;
-            state.lineDash = context.getLineDash();
-            state.lineDashOffset = context.lineDashOffset;
-            state.lineJoin = context.lineJoin;
-            state.lineWidth = context.lineWidth;
-            state.miterLimit = context.miterLimit;
-            state.shadowBlur = context.shadowBlur;
-            state.shadowColor = context.shadowColor;
-            state.shadowOffsetX = context.shadowOffsetX;
-            state.shadowOffsetY = context.shadowOffsetY;
-            state.strokeStyle = context.strokeStyle;
-            state.textAlign = context.textAlign;
-            state.textBaseline = context.textBaseline;
-            state.transform = [matrix.a, matrix.b, matrix.c, matrix.d, matrix.e, matrix.f];
-            state.webkitImageSmoothingEnabled = context.webkitImageSmoothingEnabled;
-            state.webkitLineDash = context.webkitLineDash;
-            state.webkitLineDashOffset = context.webkitLineDashOffset;
-
-            if (WI.ImageUtilities.supportsCanvasPathDebugging())
-                state.setPath = [context.getPath()];
-
-            return state;
-        }
-
-        return null;
-    }
-
     static _prototypeForType(type)
     {
         if (type === WI.Recording.Type.Canvas2D)
@@ -261,7 +215,7 @@
         }
 
         if (recording.type === WI.Recording.Type.Canvas2D) {
-            let currentState = WI.RecordingAction.deriveCurrentState(recording.type, context);
+            let currentState = WI.RecordingState.fromContext(recording.type, context, {source: this});
             console.assert(currentState);
 
             if (this.name === "save")
@@ -274,10 +228,11 @@
 
             let lastState = null;
             if (lastAction) {
-                lastState = lastAction.states.lastValue;
-                for (let key in currentState) {
-                    if (!(key in lastState) || (currentState[key] !== lastState[key] && !Object.shallowEqual(currentState[key], lastState[key])))
-                        this._stateModifiers.add(key);
+                let previousState = lastAction.states.lastValue;
+                for (let [name, value] of currentState) {
+                    let previousValue = previousState.get(name);
+                    if (value !== previousValue && !Object.shallowEqual(value, previousValue))
+                        this._stateModifiers.add(name);
                 }
             }
 

Added: trunk/Source/WebInspectorUI/UserInterface/Models/RecordingState.js (0 => 237808)


--- trunk/Source/WebInspectorUI/UserInterface/Models/RecordingState.js	                        (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/RecordingState.js	2018-11-05 17:34:48 UTC (rev 237808)
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WI.RecordingState = class RecordingState
+{
+    constructor(data, {source} = {})
+    {
+        this._data = data;
+        this._source = source || null;
+    }
+
+    // Static
+
+    static fromContext(type, context, options = {})
+    {
+        if (type !== WI.Recording.Type.Canvas2D)
+            return null;
+
+        let matrix = context.getTransform();
+
+        let data = ""
+
+        if (WI.ImageUtilities.supportsCanvasPathDebugging()) {
+            data.currentX = context.currentX;
+            data.currentY = context.currentY;
+        }
+
+        data.direction = context.direction;
+        data.fillStyle = context.fillStyle;
+        data.font = context.font;
+        data.globalAlpha = context.globalAlpha;
+        data.globalCompositeOperation = context.globalCompositeOperation;
+        data.imageSmoothingEnabled = context.imageSmoothingEnabled;
+        data.imageSmoothingQuality = context.imageSmoothingQuality;
+        data.lineCap = context.lineCap;
+        data.lineDash = context.getLineDash();
+        data.lineDashOffset = context.lineDashOffset;
+        data.lineJoin = context.lineJoin;
+        data.lineWidth = context.lineWidth;
+        data.miterLimit = context.miterLimit;
+        data.shadowBlur = context.shadowBlur;
+        data.shadowColor = context.shadowColor;
+        data.shadowOffsetX = context.shadowOffsetX;
+        data.shadowOffsetY = context.shadowOffsetY;
+        data.strokeStyle = context.strokeStyle;
+        data.textAlign = context.textAlign;
+        data.textBaseline = context.textBaseline;
+        data.transform = [matrix.a, matrix.b, matrix.c, matrix.d, matrix.e, matrix.f];
+        data.webkitImageSmoothingEnabled = context.webkitImageSmoothingEnabled;
+        data.webkitLineDash = context.webkitLineDash;
+        data.webkitLineDashOffset = context.webkitLineDashOffset;
+
+        if (WI.ImageUtilities.supportsCanvasPathDebugging())
+            data.setPath = [context.getPath()];
+
+        return new WI.RecordingState(data, options);
+    }
+
+    static async swizzleInitialState(recording, initialState)
+    {
+        if (recording.type === WI.Recording.Type.Canvas2D) {
+            let swizzledState = {};
+
+            for (let [name, value] of Object.entries(initialState)) {
+                // COMPATIBILITY (iOS 12.0): Recording.InitialState.states did not exist yet
+                let nameIndex = parseInt(name);
+                if (!isNaN(nameIndex))
+                    name = await recording.swizzle(nameIndex, WI.Recording.Swizzle.String);
+
+                switch (name) {
+                case "setTransform":
+                    value = [await recording.swizzle(value, WI.Recording.Swizzle.DOMMatrix)];
+                    break;
+
+                case "fillStyle":
+                case "strokeStyle":
+                    var [gradient, pattern, string] = await Promise.all([
+                        recording.swizzle(value, WI.Recording.Swizzle.CanvasGradient),
+                        recording.swizzle(value, WI.Recording.Swizzle.CanvasPattern),
+                        recording.swizzle(value, WI.Recording.Swizzle.String),
+                    ]);
+                    if (gradient && !pattern)
+                        value = gradient;
+                    else if (pattern && !gradient)
+                        value = pattern;
+                    else
+                        value = string;
+                    break;
+
+                case "direction":
+                case "font":
+                case "globalCompositeOperation":
+                case "imageSmoothingQuality":
+                case "lineCap":
+                case "lineJoin":
+                case "shadowColor":
+                case "textAlign":
+                case "textBaseline":
+                    value = await recording.swizzle(value, WI.Recording.Swizzle.String);
+                    break;
+
+                case "globalAlpha":
+                case "lineWidth":
+                case "miterLimit":
+                case "shadowOffsetX":
+                case "shadowOffsetY":
+                case "shadowBlur":
+                case "lineDashOffset":
+                    value = await recording.swizzle(value, WI.Recording.Swizzle.Number);
+                    break;
+
+                case "setPath":
+                    value = [await recording.swizzle(value[0], WI.Recording.Swizzle.Path2D)];
+                    break;
+                }
+
+                if (value === undefined || (Array.isArray(value) && value.includes(undefined)))
+                    continue;
+
+                swizzledState[name] = value;
+            }
+
+            return new WI.RecordingState(swizzledState);
+        }
+
+        return null;
+    }
+
+    // Public
+
+    get source() { return this._source; }
+
+    has(name)
+    {
+        return name in this._data;
+    }
+
+    get(name)
+    {
+        return this._data[name];
+    }
+
+    apply(type, context)
+    {
+        for (let [name, value] of this) {
+            if (!(name in context))
+                continue;
+
+            // Skip internal state used for path debugging.
+            if (name === "currentX" || name === "currentY")
+                continue;
+
+            try {
+                if (WI.RecordingAction.isFunctionForType(type, name))
+                    context[name](...value);
+                else
+                    context[name] = value;
+            } catch { }
+        }
+    }
+
+    toJSON()
+    {
+        return this._data;
+    }
+
+    [Symbol.iterator]()
+    {
+        return Object.entries(this._data)[Symbol.iterator]();
+    }
+};

Modified: trunk/Source/WebInspectorUI/UserInterface/Test.html (237807 => 237808)


--- trunk/Source/WebInspectorUI/UserInterface/Test.html	2018-11-05 17:33:54 UTC (rev 237807)
+++ trunk/Source/WebInspectorUI/UserInterface/Test.html	2018-11-05 17:34:48 UTC (rev 237808)
@@ -174,6 +174,7 @@
     <script src=""
     <script src=""
     <script src=""
+    <script src=""
     <script src=""
     <script src=""
     <script src=""

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/RecordingContentView.js (237807 => 237808)


--- trunk/Source/WebInspectorUI/UserInterface/Views/RecordingContentView.js	2018-11-05 17:33:54 UTC (rev 237807)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/RecordingContentView.js	2018-11-05 17:34:48 UTC (rev 237808)
@@ -229,24 +229,8 @@
             }
 
             for (let state of snapshot.states) {
-                for (let name in state) {
-                    if (!(name in snapshot.context))
-                        continue;
+                state.apply(this.representedObject.type, snapshot.context);
 
-                    // Skip internal state used for path debugging.
-                    if (name === "currentX" || name === "currentY")
-                        continue;
-
-                    try {
-                        if (WI.RecordingAction.isFunctionForType(this.representedObject.type, name))
-                            snapshot.context[name](...state[name]);
-                        else
-                            snapshot.context[name] = state[name];
-                    } catch {
-                        delete state[name];
-                    }
-                }
-
                 ++saveCount;
                 snapshot.context.save();
             }

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/RecordingStateDetailsSidebarPanel.css (237807 => 237808)


--- trunk/Source/WebInspectorUI/UserInterface/Views/RecordingStateDetailsSidebarPanel.css	2018-11-05 17:33:54 UTC (rev 237807)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/RecordingStateDetailsSidebarPanel.css	2018-11-05 17:34:48 UTC (rev 237808)
@@ -27,24 +27,29 @@
     min-height: 100%;
 }
 
-.sidebar > .panel.details.recording-state > .content > .data-grid tr.modified {
+.sidebar > .panel.details.recording-state > .content .details-section > .header .source {
+    -webkit-margin-start: 4px;
+    color: var(--text-color-gray-medium);
+}
+
+.sidebar > .panel.details.recording-state > .content .data-grid tr.modified {
     background-color: var(--value-changed-highlight);
 }
 
-.sidebar > .panel.details.recording-state > .content > .data-grid tr:not(.selected).non-standard {
+.sidebar > .panel.details.recording-state > .content .data-grid tr:not(.selected).non-standard .name-column {
     opacity: 0.5;
 }
 
-.sidebar > .panel.details.recording-state > .content > .data-grid tr:not(.selected) .unavailable {
+.sidebar > .panel.details.recording-state > .content .data-grid tr:not(.selected) .unavailable {
     color: grey;
 }
 
-.sidebar > .panel.details.recording-state > .content > .data-grid .inline-swatch {
+.sidebar > .panel.details.recording-state > .content .data-grid .inline-swatch {
     vertical-align: -1px;
 }
 
 @media (prefers-dark-interface) {
-    .sidebar > .panel.details.recording-state > .content > .data-grid tr.modified {
+    .sidebar > .panel.details.recording-state > .content .data-grid tr.modified {
         color: var(--green-highlight-text-color);
     }
 }

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/RecordingStateDetailsSidebarPanel.js (237807 => 237808)


--- trunk/Source/WebInspectorUI/UserInterface/Views/RecordingStateDetailsSidebarPanel.js	2018-11-05 17:33:54 UTC (rev 237807)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/RecordingStateDetailsSidebarPanel.js	2018-11-05 17:34:48 UTC (rev 237808)
@@ -32,7 +32,7 @@
         this._recording = null;
         this._action = null;
 
-        this._dataGrid = null;
+        this._dataGrids = [];
     }
 
     // Public
@@ -78,92 +78,149 @@
 
     get scrollElement()
     {
-        return this._dataGrid.scrollContainer;
+        if (this._dataGrids.length === 1)
+            return this._dataGrids[0].scrollContainer;
+        return super.scrollElement;
     }
 
+    sizeDidChange()
+    {
+        super.sizeDidChange();
+
+        if (this._dataGrids.length === 1)
+            return;
+
+        // FIXME: <https://webkit.org/b/152269> Web Inspector: Convert DetailsSection classes to use View
+        for (let dataGrid of this._dataGrids)
+            dataGrid.sizeDidChange();
+    }
+
     // Private
 
     _generateDetailsCanvas2D(action)
     {
-        if (!this._dataGrid) {
-            this._dataGrid = new WI.DataGrid({
-                name: {title: WI.UIString("Name")},
-                value: {title: WI.UIString("Value")},
-            });
-        }
-        if (!this._dataGrid.parentView)
-            this.contentView.addSubview(this._dataGrid);
+        if (this._dataGrids.length === 1)
+            this.contentView.removeSubview(this._dataGrids[0]);
 
-        this._dataGrid.removeChildren();
+        this.contentView.element.removeChildren();
 
+        this._dataGrids = [];
+
         let currentState = action.states.lastValue;
         console.assert(currentState);
         if (!currentState)
             return;
 
-        function isColorProperty(name) {
-            return name === "fillStyle" || name === "strokeStyle" || name === "shadowColor";
-        }
+        let createStateDataGrid = (state) => {
+            let dataGrid = new WI.DataGrid({
+                name: {title: WI.UIString("Name")},
+                value: {title: WI.UIString("Value")},
+            });
+            this._dataGrids.push(dataGrid);
 
-        function createInlineSwatch(value) {
-            let color = WI.Color.fromString(value);
-            if (!color)
-                return null;
+            for (let [name, value] of state) {
+                // Skip internal state used for path debugging.
+                if (name === "setPath")
+                    continue;
 
-            const readOnly = true;
-            return new WI.InlineSwatch(WI.InlineSwatch.Type.Color, color, readOnly);
-        }
+                if (typeof value === "object") {
+                    let isGradient = value instanceof CanvasGradient;
+                    let isPattern = value instanceof CanvasPattern;
+                    if (isGradient || isPattern) {
+                        let textElement = document.createElement("span");
+                        textElement.classList.add("unavailable");
 
-        for (let name in currentState) {
-            // Skip internal state used for path debugging.
-            if (name === "setPath")
-                continue;
+                        let image = null;
+                        if (isGradient) {
+                            textElement.textContent = WI.unlocalizedString("CanvasGradient");
+                            image = WI.ImageUtilities.imageFromCanvasGradient(value, 100, 100);
+                        } else if (isPattern) {
+                            textElement.textContent = WI.unlocalizedString("CanvasPattern");
+                            image = value.__image;
+                        }
 
-            let value = currentState[name];
-            if (typeof value === "object") {
-                let isGradient = value instanceof CanvasGradient;
-                let isPattern = value instanceof CanvasPattern;
-                if (isGradient || isPattern) {
-                    let textElement = document.createElement("span");
-                    textElement.classList.add("unavailable");
+                        let fragment = document.createDocumentFragment();
+                        if (image) {
+                            let swatch = new WI.InlineSwatch(WI.InlineSwatch.Type.Image, image);
+                            fragment.appendChild(swatch.element);
+                        }
+                        fragment.appendChild(textElement);
+                        value = fragment;
+                    } else {
+                        if (value instanceof DOMMatrix)
+                            value = [value.a, value.b, value.c, value.d, value.e, value.f];
 
-                    let image = null;
-                    if (isGradient) {
-                        textElement.textContent = WI.unlocalizedString("CanvasGradient");
-                        image = WI.ImageUtilities.imageFromCanvasGradient(value, 100, 100);
-                    } else if (isPattern) {
-                        textElement.textContent = WI.unlocalizedString("CanvasPattern");
-                        image = value.__image;
+                        value = JSON.stringify(value);
                     }
+                } else if (name === "fillStyle" || name === "strokeStyle" || name === "shadowColor") {
+                    let color = WI.Color.fromString(value);
+                    const readOnly = true;
+                    let swatch = new WI.InlineSwatch(WI.InlineSwatch.Type.Color, color, readOnly);
+                    value = document.createElement("span");
+                    value.append(swatch.element, color.toString());
+                }
 
-                    let fragment = document.createDocumentFragment();
-                    if (image) {
-                        let swatch = new WI.InlineSwatch(WI.InlineSwatch.Type.Image, image);
-                        fragment.appendChild(swatch.element);
+                let classNames = [];
+                if (state === currentState && !action.isGetter && action.stateModifiers.has(name))
+                    classNames.push("modified");
+                if (name.startsWith("webkit"))
+                    classNames.push("non-standard");
+
+                const hasChildren = false;
+                dataGrid.appendChild(new WI.DataGridNode({name, value}, hasChildren, classNames));
+            }
+
+            dataGrid.updateLayoutIfNeeded();
+            return dataGrid;
+        };
+
+        let createStateSection = (state, index = NaN) => {
+            let isCurrentState = isNaN(index);
+
+            let dataGrid = createStateDataGrid(state);
+            let row = new WI.DetailsSectionDataGridRow(dataGrid);
+            let group = new WI.DetailsSectionGroup([row]);
+
+            let identifier = isCurrentState ? "recording-current-state" : `recording-saved-state-${index + 1}`;
+            const title = null;
+            const optionsElement = null;
+            let defaultCollapsedSettingValue = !isCurrentState;
+            let section = new WI.DetailsSection(identifier, title, [group], optionsElement, defaultCollapsedSettingValue);
+
+            if (isCurrentState)
+                section.title = WI.UIString("Current State");
+            else {
+                section.title = WI.UIString("Save %d").format(index + 1);
+
+                if (state.source) {
+                    let sourceIndex = this._recording.actions.indexOf(state.source);
+                    if (sourceIndex >= 0) {
+                        let sourceElement = section.titleElement.appendChild(document.createElement("span"));
+                        sourceElement.classList.add("source");
+                        sourceElement.textContent = WI.UIString("(Action %s)").format(sourceIndex);
                     }
-                    fragment.appendChild(textElement);
-                    value = fragment;
-                } else {
-                    if (value instanceof DOMMatrix)
-                        value = [value.a, value.b, value.c, value.d, value.e, value.f];
-
-                    value = JSON.stringify(value);
                 }
-            } else if (isColorProperty(name)) {
-                let swatch = createInlineSwatch(value);
-                let label = swatch.value.toString();
-                value = document.createElement("span");
-                value.append(swatch.element, label);
             }
 
-            let classNames = [];
-            if (!action.isGetter && action.stateModifiers.has(name))
-                classNames.push("modified");
-            if (name.startsWith("webkit"))
-                classNames.push("non-standard");
+            return section;
+        };
 
-            const hasChildren = false;
-            this._dataGrid.appendChild(new WI.DataGridNode({name, value}, hasChildren, classNames));
+        if (action.states.length === 1) {
+            this.contentView.addSubview(createStateDataGrid(currentState));
+            return;
         }
+
+        let currentStateSection = createStateSection(currentState);
+        this.contentView.element.appendChild(currentStateSection.element);
+
+        let savedStateSections = [];
+        for (let i = action.states.length - 2; i >= 0; --i) {
+            let savedStateSection = createStateSection(action.states[i], i);
+            savedStateSections.push(savedStateSection);
+        }
+
+        let savedStatesGroup = new WI.DetailsSectionGroup(savedStateSections);
+        let savedStatesSection = new WI.DetailsSection("recording-saved-states", WI.UIString("Saved States"), [savedStatesGroup]);
+        this.contentView.element.appendChild(savedStatesSection.element);
     }
 };
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to