Changeset 223918 in webkit


Ignore:
Timestamp:
Oct 24, 2017 1:47:11 PM (7 years ago)
Author:
webkit@devinrousso.com
Message:

Web Inspector: Show recordings in CanvasTabContentView
https://bugs.webkit.org/show_bug.cgi?id=177606
<rdar://problem/34715819>

Reviewed by Brian Burg.

Original patch by Matt Baker <Matt Baker>.

This patch folds canvas recordings into the new Canvas tab, which now
supports showing both CanvasCollections and Recordings.

When viewing recordings, a back button is shown at the start of the
navigation bar, allowing the user to return to the overview. It has been
styled with a back arrow, to make its function as clear as possible.
Like other navigation path components, the item for the recording can
clicked to select another recording taken from the same canvas.

The recording action scrubber has been moved from the content browser's
navigation bar to a more prominent location in the recording content view.
Selecting a frame tree element in the navigation sidebar no longer selects
the last action for that frame. This was changed to prevent the scrubber
position from behaving non-monotonically when selecting actions in sequential
order.

While this patch retains support for importing recordings, the feature
is not polished. Currently it is not possible to return to an imported
recording after leaving closing the view. In the future we may want to
consider including a navigation sidebar panel for the overview, which
could list canvas recordings (and eventually shaders).

  • .eslintrc:

Drive-by: remove duplicate key.

  • Localizations/en.lproj/localizedStrings.js:
  • UserInterface/Base/Main.js:

(WI.contentLoaded):
(WI.tabContentViewClassForRepresentedObject):

  • UserInterface/Main.html:
  • UserInterface/Views/RecordingTabContentView.js: Removed.

Remove RecordingTabContentView.

  • UserInterface/Images/Recording.svg:

New recording icon, used for tree elements and canvas preview UI.

  • UserInterface/Models/Recording.js:

(WI.Recording):
(WI.Recording.prototype.get visualActionIndexes):
Visual action indexes should be computed by the recording.

  • UserInterface/Views/CanvasContentView.js:

(WI.CanvasContentView):
(WI.CanvasContentView.prototype.refresh):
(WI.CanvasContentView.prototype.initialLayout):
(WI.CanvasContentView.prototype.attached):
(WI.CanvasContentView.prototype._recordingStopped):
(WI.CanvasContentView.prototype._handleRecordingSelectElementChange):
New UI for showing CanvasContentView as a CollectionView item. Includes
recordings button and select for choosing a recording to view.

  • UserInterface/Views/CanvasOverviewContentView.css:

(.content-view.canvas-overview .content-view.canvas > header):
Switch to 13px, which is more frequently used.

(.content-view.canvas-overview .content-view.canvas > header .subtitle::before):
Switch to literal em dash. Surrounding spaces were ignored when using
the backslash-escaped character.

(.content-view.canvas-overview .content-view.canvas:not(:hover, .is-recording, .selected) > header > .navigation-bar):
(.content-view.canvas-overview .content-view.canvas > footer > .recordings):
(.content-view.canvas-overview .content-view.canvas > footer > .recordings::before):
(.content-view.canvas-overview .content-view.canvas > footer > .recordings > select):
(.content-view.canvas-overview .content-view.canvas > footer .recordings > select:focus):
(.content-view.canvas-overview .content-view.canvas > footer > .flexible-space):
(.popover-content > .tree-outline .item.recording > .icon):
(.popover-content > .tree-outline .item.recording:hover):
(.popover-content > .tree-outline .item.recording:hover > .icon):
(.content-view.canvas-overview .content-view.canvas:not(:hover, .is-recording) > header > .navigation-bar): Deleted.
(.content-view.canvas-overview .content-view.canvas > footer): Deleted.
New styles for the recording picker.

  • UserInterface/Views/CanvasOverviewContentView.js:

(WI.CanvasOverviewContentView):
(WI.CanvasOverviewContentView.prototype.get selectionPathComponents):
(WI.CanvasOverviewContentView.prototype.contentViewAdded):
(WI.CanvasOverviewContentView.prototype.contentViewRemoved):
(WI.CanvasOverviewContentView.prototype._supplementalRepresentedObjectsDidChange):
(WI.CanvasOverviewContentView.prototype._selectedPathComponentChanged): Deleted.
(WI.CanvasOverviewContentView.prototype._supplementalRepresentedObjectsDidChange.createCanvasPathComponent): Deleted.
Canvas tree elements are now managed by CanvasTabContentView, which is
now responsible for maintaining the tree of canvas objects. For now the
tree holds canvases and recordings, but will eventually include shader programs.

  • UserInterface/Views/CanvasTabContentView.css:

(.content-view.tab.canvas .navigation-bar > .item .recording > .icon):
(.content-view.tab.canvas .navigation-bar > .item > .canvas-overview > .icon): Deleted.
Add styles for recording path components.

  • UserInterface/Views/CanvasTabContentView.js:

(WI.CanvasTabContentView):
(WI.CanvasTabContentView.prototype.treeElementForRepresentedObject):
(WI.CanvasTabContentView.prototype.canShowRepresentedObject):
(WI.CanvasTabContentView.prototype.showRepresentedObject):
(WI.CanvasTabContentView.prototype.shown):
(WI.CanvasTabContentView.prototype.restoreStateFromCookie):
(WI.CanvasTabContentView.prototype.attached):
(WI.CanvasTabContentView.prototype.detached):
(WI.CanvasTabContentView.prototype._canvasAdded):
(WI.CanvasTabContentView.prototype._canvasRemoved):
(WI.CanvasTabContentView.prototype._canvasTreeOutlineSelectionDidChange):
(WI.CanvasTabContentView.prototype._recordingStopped):
(WI.CanvasTabContentView.prototype._navigationSidebarImport):
(WI.CanvasTabContentView.prototype._navigationSidebarTreeOutlineSelectionChanged):
(WI.CanvasTabContentView.prototype._recordingAdded):
(WI.CanvasTabContentView.prototype._recordingActionIndexChanged):
(WI.CanvasTabContentView.prototype._updateActionIndex):
(WI.CanvasTabContentView.prototype.restoreFromCookie): Deleted.
(WI.CanvasTabContentView.prototype._overviewPathComponentClicked): Deleted.

  • UserInterface/Views/ContentView.js:

(WI.ContentView.createFromRepresentedObject):

  • UserInterface/Views/RecordingActionTreeElement.css:

(body:not(.window-inactive, .window-docked-inactive) .tree-outline:matches(:focus, .force-focus) .item.action.selected > .titles .parameter.swizzled,):
(body:not(.window-inactive, .window-docked-inactive) :matches(:focus, .force-focus) .item.action.selected > .titles .parameter.swizzled,): Deleted.

  • UserInterface/Views/RecordingContentView.css:

(.content-view:not(.tab).recording):
(.content-view:not(.tab).recording > header):
(.content-view:not(.tab).recording > header > .slider-container):
(.content-view:not(.tab).recording > header > .slider-container > input[type=range]):
(.content-view:not(.tab).recording > header > .slider-container > input[type=range]::-webkit-slider-runnable-track):
(.content-view:not(.tab).recording > header > .slider-container > input[type=range]::-webkit-slider-thumb):
(.content-view:not(.tab).recording > .preview-container):

  • UserInterface/Views/RecordingContentView.js:

(WI.RecordingContentView):
(WI.RecordingContentView.prototype.updateActionIndex):
(WI.RecordingContentView.prototype.initialLayout):
(WI.RecordingContentView.prototype.async._generateContentCanvas2D):
(WI.RecordingContentView.prototype._updateSliderValue):
(WI.RecordingContentView.prototype._sliderChanged):
(WI.RecordingContentView.prototype.get supplementalRepresentedObjects): Deleted.

  • UserInterface/Views/RecordingNavigationSidebarPanel.js:

(WI.RecordingNavigationSidebarPanel.prototype.set recording):
(WI.RecordingNavigationSidebarPanel.prototype.updateActionIndex):

  • UserInterface/Views/ResourceIcons.css:

(.canvas .icon):
(.canvas.canvas-2d .icon): Deleted.
(.canvas:matches(.webgl, .webgl2, .webgpu) .icon): Deleted.

Location:
trunk/Source/WebInspectorUI
Files:
1 deleted
18 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebInspectorUI/.eslintrc

    r223308 r223918  
    7979        "TestHarness": true,
    8080        "TestSuite": true,
    81         "WI": true,
    8281
    8382        // Externals
  • trunk/Source/WebInspectorUI/ChangeLog

    r223914 r223918  
     12017-10-24  Devin Rousso  <webkit@devinrousso.com>
     2
     3        Web Inspector: Show recordings in CanvasTabContentView
     4        https://bugs.webkit.org/show_bug.cgi?id=177606
     5        <rdar://problem/34715819>
     6
     7        Reviewed by Brian Burg.
     8
     9        Original patch by Matt Baker <mattbaker@apple.com>.
     10
     11        This patch folds canvas recordings into the new Canvas tab, which now
     12        supports showing both CanvasCollections and Recordings.
     13
     14        When viewing recordings, a back button is shown at the start of the
     15        navigation bar, allowing the user to return to the overview. It has been
     16        styled with a back arrow, to make its function as clear as possible.
     17        Like other navigation path components, the item for the recording can
     18        clicked to select another recording taken from the same canvas.
     19
     20        The recording action scrubber has been moved from the content browser's
     21        navigation bar to a more prominent location in the recording content view.
     22        Selecting a frame tree element in the navigation sidebar no longer selects
     23        the last action for that frame. This was changed to prevent the scrubber
     24        position from behaving non-monotonically when selecting actions in sequential
     25        order.
     26
     27        While this patch retains support for importing recordings, the feature
     28        is not polished. Currently it is not possible to return to an imported
     29        recording after leaving closing the view. In the future we may want to
     30        consider including a navigation sidebar panel for the overview, which
     31        could list canvas recordings (and eventually shaders).
     32
     33        * .eslintrc:
     34        Drive-by: remove duplicate key.
     35
     36        * Localizations/en.lproj/localizedStrings.js:
     37        * UserInterface/Base/Main.js:
     38        (WI.contentLoaded):
     39        (WI.tabContentViewClassForRepresentedObject):
     40        * UserInterface/Main.html:
     41        * UserInterface/Views/RecordingTabContentView.js: Removed.
     42        Remove RecordingTabContentView.
     43
     44        * UserInterface/Images/Recording.svg:
     45        New recording icon, used for tree elements and canvas preview UI.
     46
     47        * UserInterface/Models/Recording.js:
     48        (WI.Recording):
     49        (WI.Recording.prototype.get visualActionIndexes):
     50        Visual action indexes should be computed by the recording.
     51
     52        * UserInterface/Views/CanvasContentView.js:
     53        (WI.CanvasContentView):
     54        (WI.CanvasContentView.prototype.refresh):
     55        (WI.CanvasContentView.prototype.initialLayout):
     56        (WI.CanvasContentView.prototype.attached):
     57        (WI.CanvasContentView.prototype._recordingStopped):
     58        (WI.CanvasContentView.prototype._handleRecordingSelectElementChange):
     59        New UI for showing CanvasContentView as a CollectionView item. Includes
     60        recordings button and select for choosing a recording to view.
     61
     62        * UserInterface/Views/CanvasOverviewContentView.css:
     63        (.content-view.canvas-overview .content-view.canvas > header):
     64        Switch to 13px, which is more frequently used.
     65
     66        (.content-view.canvas-overview .content-view.canvas > header .subtitle::before):
     67        Switch to literal em dash. Surrounding spaces were ignored when using
     68        the backslash-escaped character.
     69
     70        (.content-view.canvas-overview .content-view.canvas:not(:hover, .is-recording, .selected) > header > .navigation-bar):
     71        (.content-view.canvas-overview .content-view.canvas > footer > .recordings):
     72        (.content-view.canvas-overview .content-view.canvas > footer > .recordings::before):
     73        (.content-view.canvas-overview .content-view.canvas > footer > .recordings > select):
     74        (.content-view.canvas-overview .content-view.canvas > footer .recordings > select:focus):
     75        (.content-view.canvas-overview .content-view.canvas > footer > .flexible-space):
     76        (.popover-content > .tree-outline .item.recording > .icon):
     77        (.popover-content > .tree-outline .item.recording:hover):
     78        (.popover-content > .tree-outline .item.recording:hover > .icon):
     79        (.content-view.canvas-overview .content-view.canvas:not(:hover, .is-recording) > header > .navigation-bar): Deleted.
     80        (.content-view.canvas-overview .content-view.canvas > footer): Deleted.
     81        New styles for the recording picker.
     82
     83        * UserInterface/Views/CanvasOverviewContentView.js:
     84        (WI.CanvasOverviewContentView):
     85        (WI.CanvasOverviewContentView.prototype.get selectionPathComponents):
     86        (WI.CanvasOverviewContentView.prototype.contentViewAdded):
     87        (WI.CanvasOverviewContentView.prototype.contentViewRemoved):
     88        (WI.CanvasOverviewContentView.prototype._supplementalRepresentedObjectsDidChange):
     89        (WI.CanvasOverviewContentView.prototype._selectedPathComponentChanged): Deleted.
     90        (WI.CanvasOverviewContentView.prototype._supplementalRepresentedObjectsDidChange.createCanvasPathComponent): Deleted.
     91        Canvas tree elements are now managed by CanvasTabContentView, which is
     92        now responsible for maintaining the tree of canvas objects. For now the
     93        tree holds canvases and recordings, but will eventually include shader programs.
     94
     95        * UserInterface/Views/CanvasTabContentView.css:
     96        (.content-view.tab.canvas .navigation-bar > .item .recording > .icon):
     97        (.content-view.tab.canvas .navigation-bar > .item > .canvas-overview > .icon): Deleted.
     98        Add styles for recording path components.
     99
     100        * UserInterface/Views/CanvasTabContentView.js:
     101        (WI.CanvasTabContentView):
     102        (WI.CanvasTabContentView.prototype.treeElementForRepresentedObject):
     103        (WI.CanvasTabContentView.prototype.canShowRepresentedObject):
     104        (WI.CanvasTabContentView.prototype.showRepresentedObject):
     105        (WI.CanvasTabContentView.prototype.shown):
     106        (WI.CanvasTabContentView.prototype.restoreStateFromCookie):
     107        (WI.CanvasTabContentView.prototype.attached):
     108        (WI.CanvasTabContentView.prototype.detached):
     109        (WI.CanvasTabContentView.prototype._canvasAdded):
     110        (WI.CanvasTabContentView.prototype._canvasRemoved):
     111        (WI.CanvasTabContentView.prototype._canvasTreeOutlineSelectionDidChange):
     112        (WI.CanvasTabContentView.prototype._recordingStopped):
     113        (WI.CanvasTabContentView.prototype._navigationSidebarImport):
     114        (WI.CanvasTabContentView.prototype._navigationSidebarTreeOutlineSelectionChanged):
     115        (WI.CanvasTabContentView.prototype._recordingAdded):
     116        (WI.CanvasTabContentView.prototype._recordingActionIndexChanged):
     117        (WI.CanvasTabContentView.prototype._updateActionIndex):
     118        (WI.CanvasTabContentView.prototype.restoreFromCookie): Deleted.
     119        (WI.CanvasTabContentView.prototype._overviewPathComponentClicked): Deleted.
     120
     121        * UserInterface/Views/ContentView.js:
     122        (WI.ContentView.createFromRepresentedObject):
     123
     124        * UserInterface/Views/RecordingActionTreeElement.css:
     125        (body:not(.window-inactive, .window-docked-inactive) .tree-outline:matches(:focus, .force-focus) .item.action.selected > .titles .parameter.swizzled,):
     126        (body:not(.window-inactive, .window-docked-inactive) :matches(:focus, .force-focus) .item.action.selected > .titles .parameter.swizzled,): Deleted.
     127
     128        * UserInterface/Views/RecordingContentView.css:
     129        (.content-view:not(.tab).recording):
     130        (.content-view:not(.tab).recording > header):
     131        (.content-view:not(.tab).recording > header > .slider-container):
     132        (.content-view:not(.tab).recording > header > .slider-container > input[type=range]):
     133        (.content-view:not(.tab).recording > header > .slider-container > input[type=range]::-webkit-slider-runnable-track):
     134        (.content-view:not(.tab).recording > header > .slider-container > input[type=range]::-webkit-slider-thumb):
     135        (.content-view:not(.tab).recording > .preview-container):
     136
     137        * UserInterface/Views/RecordingContentView.js:
     138        (WI.RecordingContentView):
     139        (WI.RecordingContentView.prototype.updateActionIndex):
     140        (WI.RecordingContentView.prototype.initialLayout):
     141        (WI.RecordingContentView.prototype.async._generateContentCanvas2D):
     142        (WI.RecordingContentView.prototype._updateSliderValue):
     143        (WI.RecordingContentView.prototype._sliderChanged):
     144        (WI.RecordingContentView.prototype.get supplementalRepresentedObjects): Deleted.
     145
     146        * UserInterface/Views/RecordingNavigationSidebarPanel.js:
     147        (WI.RecordingNavigationSidebarPanel.prototype.set recording):
     148        (WI.RecordingNavigationSidebarPanel.prototype.updateActionIndex):
     149
     150        * UserInterface/Views/ResourceIcons.css:
     151        (.canvas .icon):
     152        (.canvas.canvas-2d .icon): Deleted.
     153        (.canvas:matches(.webgl, .webgl2, .webgpu) .icon): Deleted.
     154
    11552017-10-24  Ross Kirsling  <ross.kirsling@sony.com>
    2156
  • trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js

    r223806 r223918  
    3232localizedStrings["%d fps"] = "%d fps";
    3333localizedStrings["%d matches"] = "%d matches";
     34localizedStrings["%d of %d"] = "%d of %d";
    3435localizedStrings["%dpx"] = "%dpx";
    3536localizedStrings["%dpx²"] = "%dpx²";
     
    394395localizedStrings["Expires"] = "Expires";
    395396localizedStrings["Export"] = "Export";
     397localizedStrings["Export HAR"] = "Export HAR";
    396398localizedStrings["Expression"] = "Expression";
    397399localizedStrings["Extension Scripts"] = "Extension Scripts";
     
    987989localizedStrings["Vertex Shader"] = "Vertex Shader";
    988990localizedStrings["Vertical"] = "Vertical";
     991localizedStrings["View Recordings... (%d)"] = "View Recordings... (%d)";
    989992localizedStrings["View variable value"] = "View variable value";
    990993localizedStrings["Visibility"] = "Visibility";
  • trunk/Source/WebInspectorUI/UserInterface/Base/Main.js

    r223778 r223918  
    434434        WI.NetworkTabContentView,
    435435        WI.NewTabContentView,
    436         WI.RecordingTabContentView,
    437436        WI.ResourcesTabContentView,
    438437        WI.SearchTabContentView,
     
    11031102
    11041103    if (representedObject instanceof WI.Recording)
    1105         return WI.RecordingTabContentView;
     1104        return WI.CanvasTabContentView;
    11061105
    11071106    return null;
  • trunk/Source/WebInspectorUI/UserInterface/Images/Recording.svg

    r220114 r223918  
    22<!-- Copyright © 2017 Apple Inc. All rights reserved. -->
    33<svg xmlns="http://www.w3.org/2000/svg" id="root" version="1.1" viewBox="0 0 16 16">
    4     <path fill="currentColor" d="M 13 1 L 3 1 C 1.898438 1 1 1.898438 1 3 L 1 13 C 1 14.101562 1.898438 15 3 15 L 13 15 C 14.101562 15 15 14.101562 15 13 L 15 3 C 15 1.898438 14.101562 1 13 1 M 13 2 C 13.550781 2 14 2.449219 14 3 L 14 13 C 14 13.550781 13.550781 14 13 14 L 3 14 C 2.449219 14 2 13.550781 2 13 L 2 3 C 2 2.449219 2.449219 2 3 2 L 13 2"/>
    5     <rect fill="currentColor" x="4.25" y="4.5" width="7.5" height="1"/>
    6     <rect fill="currentColor" x="4.25" y="6.5" width="7.5" height="1"/>
    7     <rect fill="currentColor" x="4.25" y="8.5" width="7.5" height="1"/>
    8     <rect fill="currentColor" x="4.25" y="10.5" width="7.5" height="1"/>
     4    <path d="M 6.5 8 A 1.5 1.5 0 1 1 5 6.5 1.5 1.5 0 0 1 6.5 8 Z M 5 9.5 h 6 m 0 -3 A 1.5 1.5 0 1 0 12.5 8 1.5 1.5 0 0 0 11 6.5 Z" fill="none" stroke="currentColor"/>
     5    <path d="M1.5 3.729 v 8.542 a 1.127 1.127 0 0 0 1 1.22 h 11 a 1.127 1.127 0 0 0 1 -1.22 V 3.729 a 1.127 1.127 0 0 0 -1 -1.22 H 2.5 A 1.127 1.127 0 0 0 1.5 3.729 Z" fill="none" stroke="currentColor"/>
    96</svg>
  • trunk/Source/WebInspectorUI/UserInterface/Main.html

    r223856 r223918  
    514514    <script src="Views/ElementsTabContentView.js"></script>
    515515    <script src="Views/LayersTabContentView.js"></script>
    516     <script src="Views/RecordingTabContentView.js"></script>
    517516    <script src="Views/ResourceTreeElement.js"></script>
    518517    <script src="Views/ResourcesTabContentView.js"></script>
  • trunk/Source/WebInspectorUI/UserInterface/Models/Recording.js

    r223335 r223918  
    3636
    3737        this._swizzle = [];
     38        this._visualActionIndexes = [];
    3839        this._source = null;
    3940
    4041        let actions = [new WI.RecordingInitialStateAction].concat(...this._frames.map((frame) => frame.actions));
    4142        this._actions = Promise.all(actions.map((action) => action.swizzle(this))).then(() => {
    42             for (let action of actions) {
     43            actions.forEach((action, index) => {
    4344                if (!action.valid)
    44                     continue;
     45                    return;
    4546
    4647                let prototype = null;
     
    5960                    }
    6061                }
    61             }
     62
     63                if (action.isVisual)
     64                    this._visualActionIndexes.push(index);
     65            });
    6266
    6367            return actions;
     
    170174    get frames() { return this._frames; }
    171175    get data() { return this._data; }
     176    get visualActionIndexes() { return this._visualActionIndexes; }
    172177
    173178    get actions() { return this._actions; }
  • trunk/Source/WebInspectorUI/UserInterface/Views/CanvasContentView.js

    r223011 r223918  
    4242        this._pixelSizeElement = null;
    4343        this._canvasNode = null;
     44        this._recordingOptionElementMap = new WeakMap;
    4445
    4546        if (this.representedObject.contextType === WI.Canvas.ContextType.Canvas2D || this.representedObject.contextType === WI.Canvas.ContextType.WebGL) {
     
    7778        this.representedObject.requestContent().then((content) => {
    7879            this._pendingContent = content;
     80            if (!this._pendingContent) {
     81                console.error("Canvas content not available.", this.representedObject);
     82                return;
     83            }
     84
    7985            this.needsLayout();
    8086        })
     
    103109
    104110        let navigationBar = new WI.NavigationBar;
    105         if (this._recordButtonNavigationItem) {
     111        if (this._recordButtonNavigationItem)
    106112            navigationBar.addNavigationItem(this._recordButtonNavigationItem);
    107             navigationBar.addNavigationItem(new WI.DividerNavigationItem);
    108         }
     113
    109114        navigationBar.addNavigationItem(this._refreshButtonNavigationItem);
    110115
     
    115120
    116121        let footer = this.element.appendChild(document.createElement("footer"));
     122
     123        this._recordingSelectContainer = footer.appendChild(document.createElement("div"));
     124        this._recordingSelectContainer.classList.add("recordings", "hidden");
     125
     126        this._recordingSelectText = this._recordingSelectContainer.appendChild(document.createElement("span"));
     127
     128        this._recordingSelectElement = this._recordingSelectContainer.appendChild(document.createElement("select"));
     129        this._recordingSelectElement.addEventListener("change", this._handleRecordingSelectElementChange.bind(this));
     130
     131        let flexibleSpaceElement = footer.appendChild(document.createElement("div"));
     132        flexibleSpaceElement.className = "flexible-space";
     133
    117134        let metrics = footer.appendChild(document.createElement("div"));
    118135        this._pixelSizeElement = metrics.appendChild(document.createElement("span"));
     
    180197
    181198        WI.settings.showImageGrid.addEventListener(WI.Setting.Event.Changed, this._updateImageGrid, this);
     199
     200        if (this.didInitialLayout)
     201            this._recordingSelectElement.selectedIndex = -1;
    182202    }
    183203
     
    230250        this._updateRecordNavigationItem();
    231251
    232         if (event.data.canvas !== this.representedObject)
    233             return;
    234 
    235         if (!event.data.recording) {
    236             console.error("Missing recording.");
    237             return;
    238         }
     252        let {canvas, recording} = event.data;
     253        if (canvas !== this.representedObject || !recording)
     254            return;
     255
     256        const subtitle = null;
     257        let recordingTreeElement = new WI.GeneralTreeElement(["recording"], recording.displayName, subtitle, recording);
     258        recordingTreeElement.tooltip = ""; // Tree element tooltips aren't needed in a popover.
     259
     260        let optionElement = this._recordingSelectElement.appendChild(document.createElement("option"));
     261        optionElement.textContent = recording.displayName;
     262
     263        this._recordingOptionElementMap.set(optionElement, recording);
     264
     265        let recordingCount = this._recordingSelectElement.options.length;
     266        this._recordingSelectText.textContent = WI.UIString("View Recordings... (%d)").format(recordingCount);
     267        this._recordingSelectContainer.classList.remove("hidden");
    239268
    240269        WI.showRepresentedObject(event.data.recording);
     270    }
     271
     272    _handleRecordingSelectElementChange(event)
     273    {
     274        let selectedOption = this._recordingSelectElement.options[this._recordingSelectElement.selectedIndex];
     275        console.assert(selectedOption, "An option should have been selected.");
     276        if (!selectedOption)
     277            return;
     278
     279        let representedObject = this._recordingOptionElementMap.get(selectedOption);
     280        console.assert(representedObject, "Missing map entry for option.");
     281        if (!representedObject)
     282            return;
     283
     284        WI.showRepresentedObject(representedObject);
    241285    }
    242286
  • trunk/Source/WebInspectorUI/UserInterface/Views/CanvasOverviewContentView.css

    r223011 r223918  
    5353
    5454.content-view.canvas-overview .content-view.canvas > header {
    55     font-size: 14px;
     55    font-size: 13px;
    5656}
    5757
     
    7575
    7676.content-view.canvas-overview .content-view.canvas > header .subtitle::before {
    77     content: " \2014 ";
     77    content: " ";
    7878}
    7979
     
    9595}
    9696
    97 .content-view.canvas-overview .content-view.canvas:not(:hover, .is-recording) > header > .navigation-bar {
     97.content-view.canvas-overview .content-view.canvas:not(:hover, .is-recording, .selected) > header > .navigation-bar {
    9898    visibility: hidden;
    9999}
     
    130130}
    131131
    132 .content-view.canvas-overview .content-view.canvas > footer {
    133     /* FIXME: this can be removed once <https://webkit.org/b/177606> is complete.*/
    134     justify-content: flex-end;
     132.content-view.canvas-overview .content-view.canvas > footer > .recordings {
     133    position: absolute;
     134    display: flex;
     135    align-items: center;
     136}
     137
     138.content-view.canvas-overview .content-view.canvas > footer > .recordings::before {
     139    display: inline-block;
     140    width: 16px;
     141    margin-top: 3px;
     142    -webkit-padding-end: 4px;
     143    content: url(../Images/Recording.svg);
     144}
     145
     146.content-view.canvas-overview .content-view.canvas > footer > .recordings > select {
     147    position: absolute;
     148    top: 0;
     149    right: 0;
     150    bottom: 0;
     151    left: 0;
     152    margin: 0;
     153    padding: 0;
     154    border: none;
     155    opacity: 0;
     156    -webkit-appearance: none;
     157}
     158
     159.content-view.canvas-overview .content-view.canvas > footer .recordings > select:focus {
     160    -webkit-margin-start: 11px;
     161}
     162
     163.content-view.canvas-overview .content-view.canvas > footer > .flexible-space {
     164    flex: 1;
    135165}
    136166
     
    138168    -webkit-padding-start: 4px;
    139169}
     170
     171.popover-content > .tree-outline .item.recording > .icon {
     172    content: url(../Images/Recording.svg);
     173}
     174
     175.popover-content > .tree-outline .item.recording:hover {
     176    color: var(--selected-foreground-color);
     177    background-color: var(--selected-background-color);
     178    border-radius: 3px;
     179}
     180
     181.popover-content > .tree-outline .item.recording:hover > .icon {
     182    filter: invert();
     183}
  • trunk/Source/WebInspectorUI/UserInterface/Views/CanvasOverviewContentView.js

    r223011 r223918  
    3434        this.element.classList.add("canvas-overview");
    3535
    36         this._canvasTreeOutline = new WI.TreeOutline;
    37 
    3836        this._refreshButtonNavigationItem = new WI.ButtonNavigationItem("refresh-all", WI.UIString("Refresh all"), "Images/ReloadFull.svg", 13, 13);
    3937        this._refreshButtonNavigationItem.disabled = true;
     
    4442        this._showGridButtonNavigationItem.disabled = true;
    4543        this._showGridButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._showGridButtonClicked, this);
    46 
    47         this._selectedCanvasPathComponent = null;
    4844
    4945        this.selectionEnabled = true;
     
    6056    {
    6157        let components = [];
    62         let canvas = this.supplementalRepresentedObjects[0];
    63         if (canvas) {
    64             let treeElement = this._canvasTreeOutline.findTreeElement(canvas);
    65             console.assert(treeElement);
    66             if (treeElement) {
    67                 let pathComponent = new WI.GeneralTreeElementPathComponent(treeElement, canvas);
    68                 pathComponent.addEventListener(WI.HierarchicalPathComponent.Event.SiblingWasSelected, this._selectedPathComponentChanged, this);
    69                 components.push(pathComponent);
     58
     59        if (this.supplementalRepresentedObjects.length) {
     60            let [canvas] = this.supplementalRepresentedObjects;
     61            let tabContentView = WI.tabBrowser.selectedTabContentView;
     62            if (tabContentView) {
     63                let treeElement = tabContentView.treeElementForRepresentedObject(canvas);
     64                console.assert(treeElement);
     65                if (treeElement) {
     66                    let pathComponent = new WI.GeneralTreeElementPathComponent(treeElement);
     67                    pathComponent.addEventListener(WI.HierarchicalPathComponent.Event.SiblingWasSelected, this._selectionPathComponentsChanged, this);
     68                    components.push(pathComponent);
     69                }
    7070            }
    7171        }
     
    8585    contentViewAdded(contentView)
    8686    {
    87         let canvas = contentView.representedObject;
    88         let treeElement = this._canvasTreeOutline.findTreeElement(canvas);
    89         console.assert(!treeElement, "Already added tree element for canvas.", canvas);
    90         if (treeElement)
    91             return;
    92 
    93         treeElement = new WI.CanvasTreeElement(canvas);
    94         this._canvasTreeOutline.appendChild(treeElement);
    95 
    9687        contentView.element.addEventListener("mouseenter", this._contentViewMouseEnter);
    9788        contentView.element.addEventListener("mouseleave", this._contentViewMouseLeave);
     
    10091    contentViewRemoved(contentView)
    10192    {
    102         let canvas = contentView.representedObject;
    103         let treeElement = this._canvasTreeOutline.findTreeElement(canvas);
    104         console.assert(treeElement, "Missing tree element for canvas.", canvas);
    105         if (!treeElement)
    106             return;
    107 
    108         this._canvasTreeOutline.removeChild(treeElement);
    109 
    11093        contentView.element.removeEventListener("mouseenter", this._contentViewMouseEnter);
    11194        contentView.element.removeEventListener("mouseleave", this._contentViewMouseLeave);
     
    138121    }
    139122
    140     _selectedPathComponentChanged(event)
     123    _selectionPathComponentsChanged(event)
    141124    {
    142125        let pathComponent = event.data.pathComponent;
     
    152135    _supplementalRepresentedObjectsDidChange()
    153136    {
    154         function createCanvasPathComponent(canvas) {
    155             return new WI.HierarchicalPathComponent(canvas.displayName, "canvas", canvas);
    156         }
    157 
    158         let currentCanvas = this.supplementalRepresentedObjects[0];
    159         if (currentCanvas)
    160             this._selectedCanvasPathComponent = createCanvasPathComponent(currentCanvas);
    161         else
    162             this._selectedCanvasPathComponent = null;
    163 
    164137        this.dispatchEventToListeners(WI.ContentView.Event.SelectionPathComponentsDidChange);
    165138    }
  • trunk/Source/WebInspectorUI/UserInterface/Views/CanvasTabContentView.css

    r223011 r223918  
    2828}
    2929
    30 .content-view.tab.canvas .navigation-bar > .item > .canvas-overview > .icon {
    31     content: url(../Images/CanvasOverview.svg);
    32 }
    33 
    3430/* FIXME: this can be removed once <https://webkit.org/b/177606> is complete. */
    3531.content-view.tab.canvas .navigation-bar > .item .canvas .icon {
    3632    content: url(../Images/Canvas.svg);
    3733}
     34
     35.content-view.tab.canvas .navigation-bar > .item .recording > .icon {
     36    content: url(../Images/Recording.svg);
     37}
  • trunk/Source/WebInspectorUI/UserInterface/Views/CanvasTabContentView.js

    r223011 r223918  
    3333        let tabBarItem = new WI.GeneralTabBarItem(image, title);
    3434
    35         const navigationSidebarPanelConstructor = null;
     35        const navigationSidebarPanelConstructor = WI.RecordingNavigationSidebarPanel;
    3636        const detailsSidebarPanelConstructors = [WI.RecordingStateDetailsSidebarPanel, WI.RecordingTraceDetailsSidebarPanel, WI.CanvasDetailsSidebarPanel];
    3737        const disableBackForward = true;
    3838        super("canvas", ["canvas"], tabBarItem, navigationSidebarPanelConstructor, detailsSidebarPanelConstructors, disableBackForward);
    3939
    40         this._overviewPathComponent = new WI.HierarchicalPathComponent(WI.UIString("Canvas Overview"), "canvas-overview");
    41         this._overviewPathComponent.addEventListener(WI.HierarchicalPathComponent.Event.Clicked, this._overviewPathComponentClicked, this);
    42 
    4340        this._canvasCollection = null;
    44         this._canvasOverviewContentView = null;
     41
     42        this._canvasTreeOutline = new WI.TreeOutline;
     43        this._canvasTreeOutline.addEventListener(WI.TreeOutline.Event.SelectionDidChange, this._canvasTreeOutlineSelectionDidChange, this);
     44
     45        const leftArrow = "Images/BackForwardArrows.svg#left-arrow-mask";
     46        const rightArrow = "Images/BackForwardArrows.svg#right-arrow-mask";
     47        let isRTL = WI.resolvedLayoutDirection() === WI.LayoutDirection.RTL;
     48        let backButtonImage = isRTL ? rightArrow : leftArrow;
     49        this._overviewNavigationItem = new WI.ButtonNavigationItem("canvas-overview", WI.UIString("Canvas Overview"), backButtonImage, 8, 13);
     50        this._overviewNavigationItem.hidden = true;
     51        this._overviewNavigationItem.visibilityPriority = WI.NavigationItem.VisibilityPriority.High;
     52        this._overviewNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, () => { this.showRepresentedObject(this._canvasCollection); });
     53
     54        this.contentBrowser.navigationBar.insertNavigationItem(this._overviewNavigationItem, 2);
     55        this.contentBrowser.navigationBar.insertNavigationItem(new WI.DividerNavigationItem, 3);
     56
     57        this.navigationSidebarPanel.addEventListener(WI.RecordingNavigationSidebarPanel.Event.Import, this._navigationSidebarImport, this);
     58        this.navigationSidebarPanel.contentTreeOutline.addEventListener(WI.TreeOutline.Event.SelectionDidChange, this._navigationSidebarTreeOutlineSelectionChanged, this);
    4559    }
    4660
     
    6175    // Public
    6276
     77    treeElementForRepresentedObject(representedObject)
     78    {
     79        return this._canvasTreeOutline.findTreeElement(representedObject);
     80    }
     81
    6382    get type()
    6483    {
     
    7392    canShowRepresentedObject(representedObject)
    7493    {
    75         return representedObject instanceof WI.CanvasCollection;
     94        return representedObject instanceof WI.CanvasCollection || representedObject instanceof WI.Recording;
     95    }
     96
     97    showRepresentedObject(representedObject, cookie)
     98    {
     99        super.showRepresentedObject(representedObject, cookie);
     100
     101        this.navigationSidebarPanel.recording = null;
     102
     103        if (representedObject instanceof WI.CanvasCollection) {
     104            this._overviewNavigationItem.hidden = true;
     105            WI.navigationSidebar.collapsed = true;
     106            return;
     107        }
     108
     109        if (representedObject instanceof WI.Recording) {
     110            this._overviewNavigationItem.hidden = false;
     111            representedObject.actions.then((actions) => {
     112                this.navigationSidebarPanel.recording = representedObject;
     113                WI.navigationSidebar.collapsed = false;
     114            });
     115            return;
     116        }
     117
     118        console.assert(false, "Should not be reached.");
    76119    }
    77120
     
    80123        super.shown();
    81124
    82         if (this.contentBrowser.currentContentView)
    83             return;
    84 
    85         this._canvasOverviewContentView = new WI.CanvasOverviewContentView(this._canvasCollection);
    86         this.contentBrowser.showContentView(this._canvasOverviewContentView);
    87     }
    88 
    89     treeElementForRepresentedObject(representedObject)
    90     {
    91         if (!this._overviewTreeElement) {
    92             const title = WI.UIString("Canvas Overview");
    93             this._overviewTreeElement = new WI.GeneralTreeElement(["canvas-overview"], title, null, representedObject);
    94         }
    95 
    96         return this._overviewTreeElement;
    97     }
    98 
    99     restoreFromCookie(cookie)
     125        if (!this.contentBrowser.currentContentView)
     126            this.showRepresentedObject(this._canvasCollection);
     127    }
     128
     129    restoreStateFromCookie(cookie)
    100130    {
    101131        // FIXME: implement once <https://webkit.org/b/177606> is complete.
     
    115145        WI.canvasManager.addEventListener(WI.CanvasManager.Event.CanvasWasAdded, this._canvasAdded, this);
    116146        WI.canvasManager.addEventListener(WI.CanvasManager.Event.CanvasWasRemoved, this._canvasRemoved, this);
     147        WI.canvasManager.addEventListener(WI.CanvasManager.Event.RecordingStopped, this._recordingStopped, this);
    117148        WI.Frame.addEventListener(WI.Frame.Event.MainResourceDidChange, this._mainResourceDidChange, this);
     149        WI.RecordingContentView.addEventListener(WI.RecordingContentView.Event.RecordingActionIndexChanged, this._recordingActionIndexChanged, this);
    118150
    119151        this._canvasCollection = new WI.CanvasCollection(WI.canvasManager.canvases);
     152
     153        for (let canvas of this._canvasCollection.items)
     154            this._canvasTreeOutline.appendChild(new WI.CanvasTreeElement(canvas));
    120155    }
    121156
     
    124159        WI.canvasManager.removeEventListener(null, null, this);
    125160        WI.Frame.removeEventListener(null, null, this);
     161        WI.RecordingContentView.removeEventListener(null, null, this);
    126162
    127163        this._canvasCollection = null;
    128164
     165        this._canvasTreeOutline.removeChildren();
     166
    129167        super.detached();
    130168    }
     
    135173    {
    136174        let canvas = event.data.canvas;
     175        this._canvasTreeOutline.appendChild(new WI.CanvasTreeElement(canvas));
    137176        this._canvasCollection.add(canvas);
    138177    }
     
    141180    {
    142181        let canvas = event.data.canvas;
     182        let treeElement = this._canvasTreeOutline.findTreeElement(canvas);
     183        console.assert(treeElement, "Missing tree element for canvas.", canvas);
     184        this._canvasTreeOutline.removeChild(treeElement);
    143185        this._canvasCollection.remove(canvas);
    144186    }
    145187
    146     _overviewPathComponentClicked(event)
    147     {
    148         console.assert(this._canvasOverviewContentView);
    149         this.contentBrowser.showContentView(this._canvasOverviewContentView);
     188    _canvasTreeOutlineSelectionDidChange(event)
     189    {
     190        let selectedElement = event.data.selectedElement;
     191        if (!selectedElement)
     192            return;
     193
     194        let representedObject = selectedElement.representedObject;
     195
     196        if (this.canShowRepresentedObject(representedObject)) {
     197            this.showRepresentedObject(representedObject);
     198            this._updateActionIndex(0);
     199            return;
     200        }
     201
     202        if (representedObject instanceof WI.Canvas) {
     203            this.showRepresentedObject(this._canvasCollection);
     204            this.contentBrowser.currentContentView.setSelectedItem(representedObject);
     205            return;
     206        }
     207
     208        console.assert(false, "Unexpected representedObject.", representedObject);
    150209    }
    151210
     
    157216        this._canvasCollection.clear();
    158217    }
     218
     219    _recordingStopped(event)
     220    {
     221        let recording = event.data.recording;
     222        if (!recording) {
     223            // FIXME: <https://webkit.org/b/178185> Web Inspector: Canvas tab: show detailed status during canvas recording
     224            return;
     225        }
     226
     227        this._recordingAdded(recording);
     228    }
     229
     230    _navigationSidebarImport(event)
     231    {
     232        let {filename, payload} = event.data;
     233        let recording = WI.Recording.fromPayload(payload);
     234        if (!recording) {
     235            WI.Recording.synthesizeError(WI.UIString("unsupported version."));
     236            return;
     237        }
     238
     239        let extensionStart = filename.lastIndexOf(".");
     240        if (extensionStart !== -1)
     241            filename = filename.substring(0, extensionStart);
     242
     243        recording.createDisplayName(filename);
     244
     245        this._recordingAdded(recording);
     246    }
     247
     248    _navigationSidebarTreeOutlineSelectionChanged(event)
     249    {
     250        if (!event.data.selectedElement)
     251            return;
     252
     253        let selectedTreeElement = event.data.selectedElement;
     254        if (selectedTreeElement instanceof WI.FolderTreeElement)
     255            return;
     256
     257        let recordingContentView = this.contentBrowser.currentContentView;
     258        if (!(recordingContentView instanceof WI.RecordingContentView))
     259            return;
     260
     261        this._updateActionIndex(selectedTreeElement.index, {suppressNavigationSidebarUpdate: true});
     262    }
     263
     264    _recordingAdded(recording)
     265    {
     266        const subtitle = null;
     267        let recordingTreeElement = new WI.GeneralTreeElement(["recording"], recording.displayName, subtitle, recording);
     268
     269        if (recording.source) {
     270            let canvasTreeElement = this._canvasTreeOutline.findTreeElement(recording.source);
     271            console.assert(canvasTreeElement, "Missing tree element for canvas.", recording.source);
     272            if (canvasTreeElement)
     273                canvasTreeElement.appendChild(recordingTreeElement);
     274        } else
     275            this._canvasTreeOutline.appendChild(recordingTreeElement);
     276
     277        this.showRepresentedObject(recording);
     278        this._updateActionIndex(0, {suppressNavigationSidebarUpdate: true});
     279    }
     280
     281    _recordingActionIndexChanged(event)
     282    {
     283        if (event.target !== this.contentBrowser.currentContentView)
     284            return;
     285
     286        this._updateActionIndex(event.data.index);
     287    }
     288
     289    _updateActionIndex(index, options = {})
     290    {
     291        options.actionCompletedCallback = (action, context) => {
     292            for (let detailsSidebarPanel of this.detailsSidebarPanels) {
     293                if (detailsSidebarPanel.updateAction)
     294                    detailsSidebarPanel.updateAction(action, context, options);
     295            }
     296        };
     297
     298        if (!options.suppressNavigationSidebarUpdate)
     299            this.navigationSidebarPanel.updateActionIndex(index, options);
     300
     301        this.contentBrowser.currentContentView.updateActionIndex(index, options);
     302    }
    159303};
    160304
  • trunk/Source/WebInspectorUI/UserInterface/Views/ContentView.js

    r222599 r223918  
    6161            return new WI.CanvasContentView(representedObject, extraArguments);
    6262
     63        if (representedObject instanceof WI.CanvasCollection)
     64            return new WI.CanvasOverviewContentView(representedObject, extraArguments);
     65
    6366        if (representedObject instanceof WI.ShaderProgram)
    6467            return new WI.ShaderProgramContentView(representedObject, extraArguments);
  • trunk/Source/WebInspectorUI/UserInterface/Views/RecordingActionTreeElement.css

    r223694 r223918  
    6666}
    6767
    68 body:not(.window-inactive, .window-docked-inactive) :matches(:focus, .force-focus) .item.action.selected > .titles .parameter.swizzled,
    69 body:not(.window-inactive, .window-docked-inactive) :matches(:focus, .force-focus) .item.action.selected::before {
    70     color: var(--console-secondary-text-color);
     68body:not(.window-inactive, .window-docked-inactive) .tree-outline:matches(:focus, .force-focus) .item.action.selected > .titles .parameter.swizzled,
     69body:not(.window-inactive, .window-docked-inactive) .tree-outline:matches(:focus, .force-focus) .item.action.selected::before {
     70    color: var(--selected-secondary-text-color);
    7171}
    7272
  • trunk/Source/WebInspectorUI/UserInterface/Views/RecordingContentView.css

    r221232 r223918  
    2525
    2626.content-view:not(.tab).recording {
    27     padding: 15px;
     27    display: flex;
     28    flex-direction: column;
     29    padding: 10px;
    2830    background-color: hsl(0, 0%, 90%);
     31}
     32
     33.content-view:not(.tab).recording > header {
     34    margin-bottom: 10px;
     35}
     36
     37.content-view:not(.tab).recording > header > .slider-container {
     38    display: flex;
     39    padding: 8px;
     40    background-color: white;
     41    border: 1px solid var(--border-color);
     42    border-radius: 4px;
     43}
     44
     45.content-view:not(.tab).recording > header > .slider-container > input[type=range] {
     46    flex: 1;
     47    -webkit-margin-start: 10px;
     48}
     49
     50.content-view:not(.tab).recording > header > .slider-container > input[type=range]::-webkit-slider-runnable-track {
     51    height: 2px;
     52    background-color: var(--border-color);
     53    border-radius: 1px;
     54}
     55
     56.content-view:not(.tab).recording > header > .slider-container > input[type=range]::-webkit-slider-thumb {
     57    margin-top: -6px;
    2958}
    3059
    3160.content-view:not(.tab).recording > .preview-container {
    3261    display: flex;
     62    flex: 1;
     63    justify-content: center;
    3364    align-items: center;
    34     justify-content: center;
    3565    position: relative;
    3666    width: 100%;
  • trunk/Source/WebInspectorUI/UserInterface/Views/RecordingContentView.js

    r223335 r223918  
    5454            this._showGridButtonNavigationItem.activated = !!WI.settings.showImageGrid.value;
    5555        }
    56 
    57         this._previewContainer = this.element.appendChild(document.createElement("div"));
    58         this._previewContainer.classList.add("preview-container");
    59 
    60         this._messageElement = this.element.appendChild(document.createElement("div"));
    61         this._messageElement.classList.add("message");
    6256    }
    6357
     
    108102            return;
    109103
     104        if (this._index === index)
     105            return;
     106
    110107        this.representedObject.actions.then((actions) => {
    111108            console.assert(index >= 0 && index < actions.length);
    112             if (index < 0 || index >= actions.length || index === this._index)
     109            if (index < 0 || index >= actions.length)
    113110                return;
    114111
    115112            this._index = index;
     113            this._updateSliderValue();
    116114
    117115            if (this.representedObject.type === WI.Recording.Type.Canvas2D)
     
    137135    }
    138136
    139     get supplementalRepresentedObjects()
    140     {
    141         let supplementalRepresentedObjects = super.supplementalRepresentedObjects;
    142         if (this.representedObject)
    143             supplementalRepresentedObjects.push(this.representedObject);
    144         return supplementalRepresentedObjects;
    145     }
    146137
    147138    get supportsSave()
     
    161152            forceSaveAs: true,
    162153        };
     154    }
     155
     156    // Protected
     157
     158    initialLayout()
     159    {
     160        let previewHeader = this.element.appendChild(document.createElement("header"));
     161
     162        let sliderContainer = previewHeader.appendChild(document.createElement("div"));
     163        sliderContainer.className = "slider-container hidden";
     164
     165        this._previewContainer = this.element.appendChild(document.createElement("div"));
     166        this._previewContainer.className = "preview-container";
     167
     168        this._sliderValueElement = sliderContainer.appendChild(document.createElement("div"));
     169        this._sliderValueElement.className = "slider-value";
     170
     171        this._sliderElement = sliderContainer.appendChild(document.createElement("input"));
     172        this._sliderElement.addEventListener("input", this._sliderChanged.bind(this));
     173        this._sliderElement.type = "range";
     174        this._sliderElement.min = 0;
     175        this._sliderElement.max = 0;
     176
     177        this.representedObject.actions.then(() => {
     178            sliderContainer.classList.remove("hidden");
     179            this._sliderElement.max = this.representedObject.visualActionIndexes.length;
     180            this._updateSliderValue();
     181        });
    163182    }
    164183
     
    397416
    398417        this._previewContainer.removeChildren();
    399         this._messageElement.removeChildren();
    400418
    401419        if (showCanvasPath) {
     
    482500    }
    483501
     502    _updateSliderValue()
     503    {
     504        if (!this._sliderElement)
     505            return;
     506
     507        let visualActionIndexes = this.representedObject.visualActionIndexes;
     508        let visualActionIndex = 0;
     509        if (this._index > 0) {
     510            while (visualActionIndex < visualActionIndexes.length && visualActionIndexes[visualActionIndex] <= this._index)
     511                visualActionIndex++;
     512        }
     513
     514        this._sliderElement.value = visualActionIndex;
     515        this._sliderValueElement.textContent = WI.UIString("%d of %d").format(visualActionIndex, visualActionIndexes.length);
     516    }
     517
    484518    _showPathButtonClicked(event)
    485519    {
     
    495529        this._updateImageGrid();
    496530    }
     531
     532    _sliderChanged()
     533    {
     534        let index = 0;
     535
     536        let visualActionIndex = parseInt(this._sliderElement.value) - 1;
     537        if (visualActionIndex !== -1)
     538            index = this.representedObject.visualActionIndexes[visualActionIndex];
     539
     540        this.dispatchEventToListeners(WI.RecordingContentView.Event.RecordingActionIndexChanged, {index});
     541    }
    497542};
    498543
    499544WI.RecordingContentView.SnapshotInterval = 5000;
     545
     546WI.RecordingContentView.Event = {
     547    RecordingActionIndexChanged: "recording-action-index-changed",
     548};
  • trunk/Source/WebInspectorUI/UserInterface/Views/RecordingNavigationSidebarPanel.js

    r223322 r223918  
    8989
    9090            this._exportButton.disabled = !actions.length;
     91
     92            let index = this._recording[WI.RecordingNavigationSidebarPanel.SelectedActionIndexSymbol] || 0;
     93            this.updateActionIndex(index);
    9194        });
    9295    }
     
    98101
    99102        this._recording.actions.then((actions) => {
    100             console.assert(index >= 0 && index < actions.length);
    101             if (index < 0 || index >= actions.length)
    102                 return;
    103 
    104             let treeOutline = this.contentTreeOutline;
    105             if (!(actions[0] instanceof WI.RecordingInitialStateAction) || index) {
    106                 treeOutline = treeOutline.children[0];
    107                 while (index > treeOutline.children.length) {
    108                     index -= treeOutline.children.length;
    109                     treeOutline = treeOutline.nextSibling;
    110                 }
    111 
    112                 // Must subtract one from the final result since the initial state is considered index 0.
    113                 --index;
    114             }
    115 
    116             let treeElementToSelect = treeOutline.children[index];
    117             if (treeElementToSelect.parent && !treeElementToSelect.parent.expanded)
    118                 treeElementToSelect = treeElementToSelect.parent;
     103            let recordingAction = actions[index];
     104            console.assert(recordingAction, "Invalid recording action index.", index);
     105            if (!recordingAction)
     106                return;
     107
     108            let treeElement = this.contentTreeOutline.findTreeElement(recordingAction);
     109            console.assert(treeElement, "Missing tree element for recording action.", recordingAction);
     110            if (!treeElement)
     111                return;
     112
     113            this._recording[WI.RecordingNavigationSidebarPanel.SelectedActionIndexSymbol] = index;
    119114
    120115            const omitFocus = false;
    121116            const selectedByUser = false;
    122             let suppressOnSelect = !(treeElementToSelect instanceof WI.FolderTreeElement);
     117            const suppressOnSelect = true;
    123118            const suppressOnDeselect = true;
    124             treeElementToSelect.revealAndSelect(omitFocus, selectedByUser, suppressOnSelect, suppressOnDeselect);
     119            treeElement.revealAndSelect(omitFocus, selectedByUser, suppressOnSelect, suppressOnDeselect);
    125120        });
    126121    }
     
    204199};
    205200
     201WI.RecordingNavigationSidebarPanel.SelectedActionIndexSymbol = Symbol("selected-action-index");
     202
    206203WI.RecordingNavigationSidebarPanel.Event = {
    207204    Import: "recording-navigation-sidebar-panel-import",
  • trunk/Source/WebInspectorUI/UserInterface/Views/ResourceIcons.css

    r222739 r223918  
    7777}
    7878
    79 .canvas.canvas-2d .icon {
    80     content: url(../Images/Canvas2D.svg);
    81 }
    82 
    83 .canvas:matches(.webgl, .webgl2, .webgpu) .icon {
    84     content: url(../Images/Canvas3D.svg);
     79.canvas .icon {
     80    content: url(../Images/Canvas.svg);
    8581}
    8682
Note: See TracChangeset for help on using the changeset viewer.