Changeset 224367 in webkit


Ignore:
Timestamp:
Nov 2, 2017 5:54:35 PM (6 years ago)
Author:
webkit@devinrousso.com
Message:

Web Inspector: Canvas: recording parameters that include an Image should show an InlineSwatch (2D canvas)
https://bugs.webkit.org/show_bug.cgi?id=177032

Reviewed by Brian Burg.

  • Localizations/en.lproj/localizedStrings.js:
  • UserInterface/Test.html:
  • UserInterface/Base/ImageUtilities.js:

(WI.scratchCanvasContext2D):
Creates a static canvas that can be used for scratch purposes, such as converting ImageData
to an Image, or rendering the result of a CanvasGradient. The canvas is reset after each
call of the function so it can be reused by other callers.

(WI.imageFromImageData):
(WI.imageFromCanvasGradient):
Utility functions for getting a dataURL image of ImageData/CanvasGradient after drawing it
onto WI.scratchCanvasContext2D.

  • UserInterface/Models/Recording.js:

(WI.Recording.prototype.async.swizzle):
Set the Image used to create a CanvasPattern as an expando property for later use.

  • UserInterface/Models/RecordingAction.js:

(WI.RecordingAction.prototype.getImageParameters):

  • UserInterface/Views/RecordingActionTreeElement.js:

(WI.RecordingActionTreeElement._generateDOM):
Add Image InlineSwatch elements for parameters that are viewable as an image. For ImageData
and CanvasGradient, WI.scratchCanvasContext2D is used to draw the ImageData/CanvasGradient
and get a dataURL to be used as the image.

(WI.RecordingActionTreeElement._createSwatchForColorParameters):
Drive-by: add checks that the parameter is not a CanvasGradient or CanvasPattern.

  • UserInterface/Views/RecordingStateDetailsSidebarPanel.js:

(WI.RecordingStateDetailsSidebarPanel.prototype._generateDetailsCanvas2D):
Add Image InlineSwatch elements for fillStyle/strokeStyle when the value is a CanvasGradient
or a CanvasPattern.

  • UserInterface/Views/InlineSwatch.js:

(WI.InlineSwatch):
(WI.InlineSwatch.prototype._updateSwatch):
(WI.InlineSwatch.prototype._swatchElementClicked):

  • UserInterface/Views/InlineSwatch.css:

(.inline-swatch):
(.inline-swatch:matches(.color, .gradient)):
(.inline-swatch:matches(.bezier, .spring, .variable)):
(.inline-swatch:not(.read-only):matches(.bezier, .spring, .variable):hover):
(.inline-swatch:not(.read-only):matches(.bezier, .spring, .variable):active):
(.inline-swatch:not(.read-only):active > span):
(.inline-swatch:matches(.bezier, .spring, .variable) > span):
(.inline-swatch.image > span):
(.inline-swatch:matches(.bezier, .spring, .variable):hover): Deleted.
(.inline-swatch:matches(.bezier, .spring, .variable):active): Deleted.
(.inline-swatch:active > span): Deleted.
(.inline-swatch:matches(.bezier, .spring) > span): Deleted.
Add Image type for showing previews of images.
Drive-by: simplify some styles.

  • UserInterface/Views/RecordingActionTreeElement.css:

(.item.action > .titles .parameters > .inline-swatch):
(.tree-outline:matches(:focus, .force-focus) .item.action > .titles .parameters > .inline-swatch): Deleted.
Drive-by: remove the :focus requirement, as it would cause the InlineSwatch to shift when
the user clicked out of the WebInspector window.

Location:
trunk/Source/WebInspectorUI
Files:
11 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebInspectorUI/ChangeLog

    r224357 r224367  
     12017-11-02  Devin Rousso  <webkit@devinrousso.com>
     2
     3        Web Inspector: Canvas: recording parameters that include an Image should show an InlineSwatch (2D canvas)
     4        https://bugs.webkit.org/show_bug.cgi?id=177032
     5
     6        Reviewed by Brian Burg.
     7
     8        * Localizations/en.lproj/localizedStrings.js:
     9
     10        * UserInterface/Test.html:
     11        * UserInterface/Base/ImageUtilities.js:
     12        (WI.scratchCanvasContext2D):
     13        Creates a static canvas that can be used for scratch purposes, such as converting ImageData
     14        to an Image, or rendering the result of a CanvasGradient. The canvas is reset after each
     15        call of the function so it can be reused by other callers.
     16
     17        (WI.imageFromImageData):
     18        (WI.imageFromCanvasGradient):
     19        Utility functions for getting a dataURL image of ImageData/CanvasGradient after drawing it
     20        onto WI.scratchCanvasContext2D.
     21
     22        * UserInterface/Models/Recording.js:
     23        (WI.Recording.prototype.async.swizzle):
     24        Set the Image used to create a CanvasPattern as an expando property for later use.
     25
     26        * UserInterface/Models/RecordingAction.js:
     27        (WI.RecordingAction.prototype.getImageParameters):
     28
     29        * UserInterface/Views/RecordingActionTreeElement.js:
     30        (WI.RecordingActionTreeElement._generateDOM):
     31        Add Image InlineSwatch elements for parameters that are viewable as an image. For ImageData
     32        and CanvasGradient, WI.scratchCanvasContext2D is used to draw the ImageData/CanvasGradient
     33        and get a dataURL to be used as the image.
     34
     35        (WI.RecordingActionTreeElement._createSwatchForColorParameters):
     36        Drive-by: add checks that the parameter is not a CanvasGradient or CanvasPattern.
     37
     38        * UserInterface/Views/RecordingStateDetailsSidebarPanel.js:
     39        (WI.RecordingStateDetailsSidebarPanel.prototype._generateDetailsCanvas2D):
     40        Add Image InlineSwatch elements for fillStyle/strokeStyle when the value is a CanvasGradient
     41        or a CanvasPattern.
     42
     43        * UserInterface/Views/InlineSwatch.js:
     44        (WI.InlineSwatch):
     45        (WI.InlineSwatch.prototype._updateSwatch):
     46        (WI.InlineSwatch.prototype._swatchElementClicked):
     47        * UserInterface/Views/InlineSwatch.css:
     48        (.inline-swatch):
     49        (.inline-swatch:matches(.color, .gradient)):
     50        (.inline-swatch:matches(.bezier, .spring, .variable)):
     51        (.inline-swatch:not(.read-only):matches(.bezier, .spring, .variable):hover):
     52        (.inline-swatch:not(.read-only):matches(.bezier, .spring, .variable):active):
     53        (.inline-swatch:not(.read-only):active > span):
     54        (.inline-swatch:matches(.bezier, .spring, .variable) > span):
     55        (.inline-swatch.image > span):
     56        (.inline-swatch:matches(.bezier, .spring, .variable):hover): Deleted.
     57        (.inline-swatch:matches(.bezier, .spring, .variable):active): Deleted.
     58        (.inline-swatch:active > span): Deleted.
     59        (.inline-swatch:matches(.bezier, .spring) > span): Deleted.
     60        Add Image type for showing previews of images.
     61        Drive-by: simplify some styles.
     62
     63        * UserInterface/Views/RecordingActionTreeElement.css:
     64        (.item.action > .titles .parameters > .inline-swatch):
     65        (.tree-outline:matches(:focus, .force-focus) .item.action > .titles .parameters > .inline-swatch): Deleted.
     66        Drive-by: remove the :focus requirement, as it would cause the InlineSwatch to shift when
     67        the user clicked out of the WebInspector window.
     68
    1692017-11-02  Joseph Pecoraro  <pecoraro@apple.com>
    270
  • trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js

    r224357 r224367  
    996996localizedStrings["Vertex Shader"] = "Vertex Shader";
    997997localizedStrings["Vertical"] = "Vertical";
     998localizedStrings["View Image"] = "View Image";
    998999localizedStrings["View Recordings... (%d)"] = "View Recordings... (%d)";
    9991000localizedStrings["View variable value"] = "View variable value";
  • trunk/Source/WebInspectorUI/UserInterface/Base/ImageUtilities.js

    r191740 r224367  
    5252    return wrapper;
    5353}
     54
     55(function() {
     56    let canvas = null;
     57    let context = null;
     58
     59    WI.scratchCanvasContext2D = function(callback) {
     60        if (!canvas)
     61            canvas = document.createElement("canvas");
     62
     63        if (!context)
     64            context = canvas.getContext("2d");
     65
     66        context.clearRect(0, 0, canvas.width, canvas.height);
     67        context.save();
     68        callback(context);
     69        context.restore();
     70    };
     71})();
     72
     73WI.imageFromImageData = function(data) {
     74    console.assert(data instanceof ImageData);
     75
     76    let image = null;
     77    WI.scratchCanvasContext2D((context) => {
     78        context.canvas.width = data.width;
     79        context.canvas.height = data.height;
     80        context.putImageData(data, 0, 0);
     81
     82        image = new Image;
     83        image.src = context.canvas.toDataURL();
     84    });
     85    return image;
     86};
     87
     88WI.imageFromCanvasGradient = function(gradient, width, height) {
     89    console.assert(gradient instanceof CanvasGradient);
     90
     91    let image = null;
     92    WI.scratchCanvasContext2D((context) => {
     93        context.canvas.width = width;
     94        context.canvas.height = height;
     95        context.fillStyle = gradient;
     96        context.fillRect(0, 0, width, height);
     97
     98        image = new Image;
     99        image.src = context.canvas.toDataURL();
     100    });
     101    return image;
     102};
  • trunk/Source/WebInspectorUI/UserInterface/Models/Recording.js

    r223918 r224367  
    270270                    var gradientType = await this.swizzle(data[0], WI.Recording.Swizzle.String);
    271271
    272                     var context = document.createElement("canvas").getContext("2d");
    273                     this._swizzle[index][type] = gradientType === "radial-gradient" ? context.createRadialGradient(...data[1]) : context.createLinearGradient(...data[1]);
     272                    WI.scratchCanvasContext2D((context) => {
     273                        this._swizzle[index][type] = gradientType === "radial-gradient" ? context.createRadialGradient(...data[1]) : context.createLinearGradient(...data[1]);
     274                    });
    274275
    275276                    for (let stop of data[2]) {
     
    285286                    ]);
    286287
    287                     var context = document.createElement("canvas").getContext("2d");
    288                     this._swizzle[index][type] = context.createPattern(image, repeat);
     288                    WI.scratchCanvasContext2D((context) => {
     289                        this._swizzle[index][type] = context.createPattern(image, repeat);
     290                        this._swizzle[index][type].__image = image;
     291                    });
    289292                    break;
    290293                }
  • trunk/Source/WebInspectorUI/UserInterface/Models/RecordingAction.js

    r223335 r224367  
    234234        case "setShadow":
    235235            return this._parameters.slice(3);
     236        }
     237
     238        return [];
     239    }
     240
     241    getImageParameters()
     242    {
     243        switch (this._name) {
     244        // 2D
     245        case "createImageData":
     246        case "createPattern":
     247        case "drawImage":
     248        case "fillStyle":
     249        case "putImageData":
     250        case "strokeStyle":
     251        // 2D (non-standard)
     252        case "drawImageFromRect":
     253        case "webkitPutImageDataHD":
     254            return this._parameters.slice(0, 1);
    236255        }
    237256
  • trunk/Source/WebInspectorUI/UserInterface/Test.html

    r223952 r224367  
    5353    <script src="Base/EventListener.js"></script>
    5454    <script src="Base/EventListenerSet.js"></script>
     55    <script src="Base/ImageUtilities.js"></script>
    5556    <script src="Base/MIMETypeUtilities.js"></script>
    5657    <script src="Base/TextUtilities.js"></script>
  • trunk/Source/WebInspectorUI/UserInterface/Views/InlineSwatch.css

    r222102 r224367  
    3232    margin-right: 3px;
    3333    vertical-align: -2px;
     34    background-color: white;
     35    border-radius: 2px;
     36    overflow: hidden;
     37    cursor: default;
     38}
     39
     40.inline-swatch:matches(.color, .gradient) {
    3441    /* Make a checkered background for transparent colors to show against. */
    3542    background-image: linear-gradient(to bottom, hsl(0, 0%, 80%), hsl(0, 0%, 80%)),
    3643                      linear-gradient(to bottom, hsl(0, 0%, 80%), hsl(0, 0%, 80%));
    37     background-color: white;
    3844    background-size: 50%;
    3945    background-position: top left, bottom right;
    4046    background-repeat: no-repeat;
    41     border-radius: 2px;
    42     overflow: hidden;
    43     cursor: default;
    4447}
    4548
     
    5457.inline-swatch:matches(.bezier, .spring, .variable) {
    5558    margin-right: 2px;
    56     background: none;
    5759}
    5860
    59 .inline-swatch:matches(.bezier, .spring, .variable):hover {
     61.inline-swatch:not(.read-only):matches(.bezier, .spring, .variable):hover {
    6062    filter: brightness(0.9);
    6163}
    6264
    63 .inline-swatch:matches(.bezier, .spring, .variable):active {
     65.inline-swatch:not(.read-only):matches(.bezier, .spring, .variable):active {
    6466    filter: brightness(0.8);
    6567}
     
    8991}
    9092
    91 .inline-swatch:active > span {
     93.inline-swatch:not(.read-only):active > span {
    9294    border-color: hsl(0, 0%, 25%);
    9395}
    9496
    95 .inline-swatch:matches(.bezier, .spring) > span {
     97.inline-swatch:matches(.bezier, .spring, .variable) > span {
    9698    display: none;
     99}
     100
     101.inline-swatch.image > span {
     102    background-size: cover;
     103    background-repeat: no-repeat;
    97104}
    98105
  • trunk/Source/WebInspectorUI/UserInterface/Views/InlineSwatch.js

    r222102 r224367  
    5757                this._swatchElement.title = WI.UIString("View variable value");
    5858                break;
     59            case WI.InlineSwatch.Type.Image:
     60                this._swatchElement.title = WI.UIString("View Image");
     61                break;
    5962            default:
    6063                WI.reportInternalError(`Unknown InlineSwatch type "${type}"`);
     
    132135        if (this._type === WI.InlineSwatch.Type.Color || this._type === WI.InlineSwatch.Type.Gradient)
    133136            this._swatchInnerElement.style.background = this._value ? this._value.toString() : null;
     137        else if (this._type === WI.InlineSwatch.Type.Image)
     138            this._swatchInnerElement.style.setProperty("background-image", `url(${this._value.src})`);
    134139
    135140        if (!dontFireEvents)
     
    161166
    162167        this._valueEditor = null;
    163         if (this._type === WI.InlineSwatch.Type.Color) {
     168        switch (this._type) {
     169        case WI.InlineSwatch.Type.Color:
    164170            this._valueEditor = new WI.ColorPicker;
    165171            this._valueEditor.addEventListener(WI.ColorPicker.Event.ColorChanged, this._valueEditorValueDidChange, this);
    166172            this._valueEditor.addEventListener(WI.ColorPicker.Event.FormatChanged, (event) => popover.update());
    167         } else if (this._type === WI.InlineSwatch.Type.Gradient) {
     173            break;
     174
     175        case WI.InlineSwatch.Type.Gradient:
    168176            this._valueEditor = new WI.GradientEditor;
    169177            this._valueEditor.addEventListener(WI.GradientEditor.Event.GradientChanged, this._valueEditorValueDidChange, this);
    170178            this._valueEditor.addEventListener(WI.GradientEditor.Event.ColorPickerToggled, (event) => popover.update());
    171         } else if (this._type === WI.InlineSwatch.Type.Bezier) {
     179            break;
     180
     181        case WI.InlineSwatch.Type.Bezier:
    172182            this._valueEditor = new WI.BezierEditor;
    173183            this._valueEditor.addEventListener(WI.BezierEditor.Event.BezierChanged, this._valueEditorValueDidChange, this);
    174         } else if (this._type === WI.InlineSwatch.Type.Spring) {
     184            break;
     185
     186        case WI.InlineSwatch.Type.Spring:
    175187            this._valueEditor = new WI.SpringEditor;
    176188            this._valueEditor.addEventListener(WI.SpringEditor.Event.SpringChanged, this._valueEditorValueDidChange, this);
    177         } else if (this._type === WI.InlineSwatch.Type.Variable) {
     189            break;
     190
     191        case WI.InlineSwatch.Type.Variable:
    178192            this._valueEditor = {};
    179193
     
    188202                popover.update();
    189203            });
     204            break;
     205
     206        case WI.InlineSwatch.Type.Image:
     207            this._valueEditor = {};
     208            this._valueEditor.element = document.createElement("img");
     209            this._valueEditor.element.src = this._value.src;
     210            this._valueEditor.element.classList.add("show-grid");
     211            this._valueEditor.element.style.setProperty("max-width", "50vw");
     212            this._valueEditor.element.style.setProperty("max-height", "50vh");
     213            break;
    190214        }
    191215
     
    202226
    203227        let value = this._value || this._fallbackValue();
    204         if (this._type === WI.InlineSwatch.Type.Color)
     228        switch (this._type) {
     229        case WI.InlineSwatch.Type.Color:
    205230            this._valueEditor.color = value;
    206         else if (this._type === WI.InlineSwatch.Type.Gradient)
     231            break;
     232
     233        case WI.InlineSwatch.Type.Gradient:
    207234            this._valueEditor.gradient = value;
    208         else if (this._type === WI.InlineSwatch.Type.Bezier)
     235            break;
     236
     237        case WI.InlineSwatch.Type.Bezier:
    209238            this._valueEditor.bezier = value;
    210         else if (this._type === WI.InlineSwatch.Type.Spring)
     239            break;
     240
     241        case WI.InlineSwatch.Type.Spring:
    211242            this._valueEditor.spring = value;
    212         else if (this._type === WI.InlineSwatch.Type.Variable)
     243            break;
     244
     245        case WI.InlineSwatch.Type.Variable:
    213246            this._valueEditor.codeMirror.setValue(value);
     247            break;
     248        }
    214249    }
    215250
     
    334369    Spring: "inline-swatch-type-spring",
    335370    Variable: "inline-swatch-type-variable",
     371    Image: "inline-swatch-type-image",
    336372};
    337373
  • trunk/Source/WebInspectorUI/UserInterface/Views/RecordingActionTreeElement.css

    r223918 r224367  
    7171}
    7272
    73 .tree-outline:matches(:focus, .force-focus) .item.action > .titles .parameters > .inline-swatch {
    74     vertical-align: -1px;
    75 }
    76 
    7773.tree-outline[data-indent="1"] .item.action::before,
    7874.tree-outline[data-indent="2"] .item.action::before {
     
    130126.item.action:not(.selected) > .titles .parameter.swizzled {
    131127    color: var(--text-color-gray-medium);
     128}
     129
     130.item.action > .titles .parameters > .inline-swatch {
     131    vertical-align: -1px;
    132132}
    133133
  • trunk/Source/WebInspectorUI/UserInterface/Views/RecordingActionTreeElement.js

    r223335 r224367  
    132132        if (colorParameters.length) {
    133133            let swatch = WI.RecordingActionTreeElement._createSwatchForColorParameters(colorParameters);
    134             let insertionIndex = recordingAction.parameters.indexOf(colorParameters[0]);
    135             let parameterElement = parametersContainer.children[insertionIndex];
    136             parametersContainer.insertBefore(swatch.element, parameterElement);
    137 
    138             if (recordingAction.swizzleTypes[insertionIndex] === WI.Recording.Swizzle.String) {
    139                 parameterElement.textContent = swatch.value.toString();
    140                 parameterElement.classList.add("color");
     134            if (swatch) {
     135                let insertionIndex = recordingAction.parameters.indexOf(colorParameters[0]);
     136                let parameterElement = parametersContainer.children[insertionIndex];
     137                parametersContainer.insertBefore(swatch.element, parameterElement);
     138
     139                if (recordingAction.swizzleTypes[insertionIndex] === WI.Recording.Swizzle.String) {
     140                    parameterElement.textContent = swatch.value.toString();
     141                    parameterElement.classList.add("color");
     142                }
     143            }
     144        }
     145
     146        let imageParameters = recordingAction.getImageParameters();
     147        let isImage = imageParameters[0] instanceof HTMLImageElement;
     148        let isImageData = imageParameters[0] instanceof ImageData;
     149        let isCanvasGradient = imageParameters[0] instanceof CanvasGradient;
     150        let isCanvasPattern = imageParameters[0] instanceof CanvasPattern;
     151        if (imageParameters.length && (isImage || isImageData || isCanvasGradient || isCanvasPattern)) {
     152            let image = imageParameters[0];
     153
     154            if (isImageData)
     155                image = WI.imageFromImageData(image);
     156            else if (isCanvasGradient)
     157                image = WI.imageFromCanvasGradient(image, 100, 100);
     158            else if (isCanvasPattern)
     159                image = image.__image;
     160
     161            if (image) {
     162                let swatch = new WI.InlineSwatch(WI.InlineSwatch.Type.Image, image);
     163                let insertionIndex = recordingAction.parameters.indexOf(imageParameters[0]);
     164                let parameterElement = parametersContainer.children[insertionIndex];
     165                parametersContainer.insertBefore(swatch.element, parameterElement);
    141166            }
    142167        }
     
    155180            if (typeof parameters[0] === "string")
    156181                color = WI.Color.fromString(parameters[0]);
    157             else
     182            else if (!isNaN(parameters[0]))
    158183                rgb = WI.Color.normalized2rgb(parameters[0], parameters[0], parameters[0]);
    159184            break;
     
    173198
    174199        if (!color) {
     200            if (rgb.length !== 3)
     201                return null;
     202
    175203            let alpha = parameters.length === 1 ? 1 : parameters.lastValue;
    176204            color = new WI.Color(WI.Color.Format.RGBA, [...rgb, alpha]);
  • trunk/Source/WebInspectorUI/UserInterface/Views/RecordingStateDetailsSidebarPanel.js

    r223991 r224367  
    142142                let isPattern = value instanceof CanvasPattern;
    143143                if (isGradient || isPattern) {
    144                     value = document.createElement("span");
    145                     value.classList.add("unavailable");
    146                     if (isGradient)
    147                         value.textContent = WI.unlocalizedString("Gradient");
    148                     else if (isPattern)
    149                         value.textContent = WI.unlocalizedString("Pattern");
     144                    let textElement = document.createElement("span");
     145                    textElement.classList.add("unavailable");
     146
     147                    let image = null;
     148                    if (isGradient) {
     149                        textElement.textContent = WI.unlocalizedString("CanvasGradient");
     150                        image = WI.imageFromCanvasGradient(value, 100, 100);
     151                    } else if (isPattern) {
     152                        textElement.textContent = WI.unlocalizedString("CanvasPattern");
     153                        image = value.__image;
     154                    }
     155
     156                    let fragment = document.createDocumentFragment();
     157                    if (image) {
     158                        let swatch = new WI.InlineSwatch(WI.InlineSwatch.Type.Image, image);
     159                        fragment.appendChild(swatch.element);
     160                    }
     161                    fragment.appendChild(textElement);
     162                    value = fragment;
    150163                } else {
    151164                    if (value instanceof DOMMatrix)
Note: See TracChangeset for help on using the changeset viewer.