Changeset 228301 in webkit


Ignore:
Timestamp:
Feb 8, 2018 4:52:28 PM (6 years ago)
Author:
Matt Baker
Message:

Web Inspector: add listing of Canvases/Programs/Recordings to the NavigationSidebar
https://bugs.webkit.org/show_bug.cgi?id=178744
<rdar://problem/35374379>

Reviewed by Devin Rousso.

  • Localizations/en.lproj/localizedStrings.js:
  • UserInterface/Images/Canvas2D.svg:
  • UserInterface/Images/Canvas3D.svg:
  • UserInterface/Images/Recording.svg:

Update canvas icons to be monochrome. Simplified the recording icon.

  • UserInterface/Main.html:
  • UserInterface/Models/RecordingAction.js:

(WI.RecordingAction.prototype.get state):
(WI.RecordingAction.prototype.set state):
Allow (2D) snapshot state to be associated with the action. Used by
RecordingActionDetailsSidebarPanel to retrieve the snapshot state.

  • UserInterface/Views/CanvasContentView.css:

(.content-view.canvas:not(.tab)):
(.content-view.canvas:not(.tab) > .progress): Deleted.
(.content-view.canvas:not(.tab) > .progress > .frame-count): Deleted.

  • UserInterface/Views/CanvasContentView.js:

(WI.CanvasContentView):
(WI.CanvasContentView.prototype.get navigationItems):
(WI.CanvasContentView.prototype.layout):
(WI.CanvasContentView.prototype.shown):
(WI.CanvasContentView.prototype._recordingStarted):
(WI.CanvasContentView.prototype._recordingProgress):
(WI.CanvasContentView.prototype._recordingStopped):
(WI.CanvasContentView.prototype._updateRecordNavigationItem):
(WI.CanvasContentView.prototype._updateProgressView):
Replace progress UI with a reusable ProgressView class.
When in the overview, clicking the CanvasContentView shows a dedicated
CanvasContentView for inspecting shaders and recordings. This behavior
is controlled by CollectionContentView, so we need to prevent it when
clicking inside the header and footer elements, which contain clickable UI.

  • UserInterface/Views/CanvasDetailsSidebarPanel.js:

(WI.CanvasDetailsSidebarPanel.prototype.inspect):

  • UserInterface/Views/CanvasOverviewContentView.css:

(.content-view.canvas-overview .content-view.canvas):
(.content-view.canvas-overview .content-view.canvas.is-recording):
(.content-view.canvas-overview .content-view.canvas > :matches(header, footer)):
(.content-view.canvas-overview .content-view.canvas > header):
(.content-view.canvas-overview .content-view.canvas.is-recording > header):
(.content-view.canvas-overview .content-view.canvas > header > .navigation-bar):
(.content-view.canvas-overview .content-view.canvas:matches(:hover, .is-recording) > header > .navigation-bar):
(.content-view.canvas-overview .content-view.canvas.is-recording > .progress-view,):
(.content-view.canvas-overview .content-view.canvas.is-recording > .preview):
(.content-view.canvas-overview .content-view.canvas > :matches(header, .progress, .preview, footer)): Deleted.
(.content-view.canvas-overview .content-view.canvas.selected > :matches(.progress, .preview, footer),): Deleted.
(.content-view.canvas-overview .content-view.canvas:not(:hover, .is-recording, .selected) > header > .navigation-bar): Deleted.
(.content-view.canvas-overview .content-view.canvas > :matches(.progress, .preview)): Deleted.
(.content-view.canvas-overview .content-view.canvas > .preview): Deleted.
(.content-view.canvas-overview .content-view.canvas > .progress ~ .preview): Deleted.
Clean up styles, and remove selection styles as canvases are no longer selectable in the overview.

  • UserInterface/Views/CanvasOverviewContentView.js:

(WI.CanvasOverviewContentView):
(WI.CanvasOverviewContentView.prototype.get navigationItems):
(WI.CanvasOverviewContentView.prototype.attached):
(WI.CanvasOverviewContentView.prototype.detached):
(WI.CanvasOverviewContentView.prototype.get selectionPathComponents): Deleted.
(WI.CanvasOverviewContentView.prototype._changeSelectedItemVertically): Deleted.
(WI.CanvasOverviewContentView.prototype._changeSelectedItemHorizontally): Deleted.
(WI.CanvasOverviewContentView.prototype._selectionPathComponentsChanged): Deleted.
(WI.CanvasOverviewContentView.prototype._handleUp): Deleted.
(WI.CanvasOverviewContentView.prototype._handleRight): Deleted.
(WI.CanvasOverviewContentView.prototype._handleDown): Deleted.
(WI.CanvasOverviewContentView.prototype._handleLeft): Deleted.
(WI.CanvasOverviewContentView.prototype._handleSpace): Deleted.
(WI.CanvasOverviewContentView.prototype._supplementalRepresentedObjectsDidChange): Deleted.
Disable canvas selection. Remove logic for supplemental represented objects,
path components, and selection keyboard shortcuts.

  • UserInterface/Views/CanvasSidebarPanel.css: Added.

(.sidebar > .panel.navigation.canvas > .content):
(.sidebar > .panel.navigation.canvas > .navigation-bar > .item.record-start-stop.disabled):
(.sidebar > .panel.navigation.canvas > .content > .tree-outline .item.canvas.canvas-2d .icon):
(.sidebar > .panel.navigation.canvas > .content > .tree-outline .item.canvas.webgl .icon):
(.sidebar > .panel.navigation.canvas > .content > .navigation-bar):
(.sidebar > .panel.navigation.canvas.has-recordings > .content > .tree-outline.canvas):
(.sidebar > .panel.navigation.canvas:not(.has-recordings) > .filter-bar,):
(.sidebar > .panel.navigation.canvas > .content > .tree-outline .item.recording > .icon):
(.sidebar > .panel.navigation.canvas > .content > .tree-outline .item.shader-program > .icon):
(.sidebar > .panel.navigation.canvas > .content > .tree-outline .item.folder-icon > .icon):
(.sidebar > .panel.navigation.canvas > .content > .tree-outline .item.folder-icon > .status):

  • UserInterface/Views/CanvasSidebarPanel.js: Added.

(WI.CanvasSidebarPanel):
(WI.CanvasSidebarPanel.prototype.get canvas):
(WI.CanvasSidebarPanel.prototype.set canvas):
(WI.CanvasSidebarPanel.prototype.set recording):
(WI.CanvasSidebarPanel.prototype.set action):
(WI.CanvasSidebarPanel.prototype.shown):
(WI.CanvasSidebarPanel.prototype.hidden):
(WI.CanvasSidebarPanel.prototype.hasCustomFilters):
(WI.CanvasSidebarPanel.prototype.matchTreeElementAgainstCustomFilters):
(WI.CanvasSidebarPanel.prototype.initialLayout):
(WI.CanvasSidebarPanel.prototype._recordingAdded):
(WI.CanvasSidebarPanel.prototype._recordingRemoved):
(WI.CanvasSidebarPanel.prototype._scopeBarSelectionChanged):
(WI.CanvasSidebarPanel.prototype._toggleRecording):
(WI.CanvasSidebarPanel.prototype._currentRepresentedObjectsDidChange):
(WI.CanvasSidebarPanel.prototype._treeOutlineSelectionDidChange):
(WI.CanvasSidebarPanel.prototype._canvasChanged):
(WI.CanvasSidebarPanel.prototype._recordingChanged):
(WI.CanvasSidebarPanel.prototype._updateRecordNavigationItem):
(WI.CanvasSidebarPanel.prototype._updateRecordingScopeBar):
Add new navigation sidebar, split into two sections. The upper section
contains a tree with a single element for the current canvas, and child
elements for any shader programs. The maximum height of this section is 50%
of the sidebar's height. The lower section contains a tree for the selected
recording, and a scope bar for choosing between recordings.

  • UserInterface/Views/CanvasTabContentView.css:

(.content-view.tab.canvas .navigation-bar > .item .canvas-overview .icon):
(.content-view.tab.canvas .navigation-bar > .item .canvas.canvas-2d .icon):
(.content-view.tab.canvas .navigation-bar > .item .canvas.webgl .icon):
(.content-view.tab.canvas .navigation-bar > .item .shader-program > .icon):
(.content-view.tab.canvas .navigation-bar > .item > .hierarchical-path-component > .icon): Deleted.
(.content-view.tab.canvas .navigation-bar > .item .canvas .icon): Deleted.

  • UserInterface/Views/CanvasTabContentView.js:

(WI.CanvasTabContentView):
(WI.CanvasTabContentView.prototype.canShowRepresentedObject):
(WI.CanvasTabContentView.prototype.attached):
(WI.CanvasTabContentView.prototype._addCanvas):
(WI.CanvasTabContentView.prototype._removeCanvas):
(WI.CanvasTabContentView.prototype._canvasTreeOutlineSelectionDidChange):
(WI.CanvasTabContentView.prototype._recordingAdded):
(WI.CanvasTabContentView.prototype._handleSpace):
(WI.CanvasTabContentView.prototype.showRepresentedObject): Deleted.
(WI.CanvasTabContentView.prototype._navigationSidebarTreeOutlineSelectionChanged): Deleted.
(WI.CanvasTabContentView.prototype._recordingActionIndexChanged): Deleted.
(WI.CanvasTabContentView.prototype._updateActionIndex): Deleted.
The canvas tab now maintains a tree outline of all canvases, with an
"Overview" element as the root. The Overview element is always the first
item of content browser's hierarchical path.

  • UserInterface/Views/CanvasTreeElement.js:

(WI.CanvasTreeElement.createRecordingTreeElement):
(WI.CanvasTreeElement):
(WI.CanvasTreeElement.prototype.onattach):
(WI.CanvasTreeElement.prototype.onpopulate):
(WI.CanvasTreeElement.prototype._updateStatus):
(WI.CanvasTreeElement.prototype.ondetach): Deleted.
Make it possible to not show recordings under the Canvas element.
Create isRecording status element (spinner).

  • UserInterface/Views/CollectionContentView.js:

(WI.CollectionContentView.prototype.shown):
(WI.CollectionContentView.prototype.hidden):
Child ContentViews need to be updated when the collection's visibility changes.

  • UserInterface/Views/ContentView.js:

(WI.ContentView.isViewable):

  • UserInterface/Views/ProgressView.css: Added.

(.progress-view):
(.progress-view > .titles):
(.progress-view > .titles > .title):
(.progress-view > .titles > .subtitle):
(.progress-view > .titles > .subtitle::before):
(.progress-view > .indeterminate-progress-spinner):

  • UserInterface/Views/ProgressView.js: Added.

(WI.ProgressView):
(WI.ProgressView.prototype.get title):
(WI.ProgressView.prototype.set title):
(WI.ProgressView.prototype.get subtitle):
(WI.ProgressView.prototype.set subtitle):
(WI.ProgressView.prototype.get visible):
(WI.ProgressView.prototype.set visible):
(WI.ProgressView.prototype.initialLayout):
(WI.ProgressView.prototype._updateTitles):
New view class (not a ContentView) for showing a generic progress message,
with a title, subtitle, and progress spinner.

  • UserInterface/Views/RecordingContentView.css:

(.content-view:not(.tab).recording > .preview-container):
Remove unnecessary styles.

  • UserInterface/Views/RecordingContentView.js:

(WI.RecordingContentView):
(WI.RecordingContentView.prototype.get navigationItems):
(WI.RecordingContentView.prototype.get supplementalRepresentedObjects):
(WI.RecordingContentView.prototype.updateActionIndex):
(WI.RecordingContentView.prototype.get saveData):
(WI.RecordingContentView.prototype._exportRecording):
Relocate the recording export logic and UI.
(WI.RecordingContentView.prototype.async._generateContentCanvas2D):
(WI.RecordingContentView.prototype.async._generateContentCanvasWebGL):
(WI.RecordingContentView.prototype._sliderChanged):
Refactor logic for notifying the rest of the UI of changes to the action slider.
The selected action is now exposed as a supplemental represented object, and a
corresponding SupplementalRepresentedObjectsDidChange event.

  • UserInterface/Views/RecordingStateDetailsSidebarPanel.js:

(WI.RecordingStateDetailsSidebarPanel.prototype.inspect):
(WI.RecordingStateDetailsSidebarPanel.prototype.set action):
(WI.RecordingStateDetailsSidebarPanel.prototype._generateDetailsCanvas2D):
(WI.RecordingStateDetailsSidebarPanel):
(WI.RecordingStateDetailsSidebarPanel.prototype.updateAction): Deleted.

  • UserInterface/Views/RecordingTraceDetailsSidebarPanel.js:

(WI.RecordingTraceDetailsSidebarPanel.prototype.inspect):
(WI.RecordingTraceDetailsSidebarPanel.prototype.set action):
(WI.RecordingTraceDetailsSidebarPanel):
(WI.RecordingTraceDetailsSidebarPanel.prototype.updateAction): Deleted.
Now that the selected action is exposed to the UI as a supplemental
represented object, details sidebars can be more decoupled from the
canvas tab, and be notified of changes to the selection via inspect().

  • UserInterface/Views/ResourceIcons.css:

(.canvas > .icon): Deleted.
(.shader-program .icon): Deleted.

Location:
trunk/Source/WebInspectorUI
Files:
3 added
22 edited
1 copied

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebInspectorUI/ChangeLog

    r228296 r228301  
     12018-02-08  Matt Baker  <mattbaker@apple.com>
     2
     3        Web Inspector: add listing of Canvases/Programs/Recordings to the NavigationSidebar
     4        https://bugs.webkit.org/show_bug.cgi?id=178744
     5        <rdar://problem/35374379>
     6
     7        Reviewed by Devin Rousso.
     8
     9        * Localizations/en.lproj/localizedStrings.js:
     10
     11        * UserInterface/Images/Canvas2D.svg:
     12        * UserInterface/Images/Canvas3D.svg:
     13        * UserInterface/Images/Recording.svg:
     14        Update canvas icons to be monochrome. Simplified the recording icon.
     15
     16        * UserInterface/Main.html:
     17
     18        * UserInterface/Models/RecordingAction.js:
     19        (WI.RecordingAction.prototype.get state):
     20        (WI.RecordingAction.prototype.set state):
     21        Allow (2D) snapshot state to be associated with the action. Used by
     22        RecordingActionDetailsSidebarPanel to retrieve the snapshot state.
     23
     24        * UserInterface/Views/CanvasContentView.css:
     25        (.content-view.canvas:not(.tab)):
     26        (.content-view.canvas:not(.tab) > .progress): Deleted.
     27        (.content-view.canvas:not(.tab) > .progress > .frame-count): Deleted.
     28
     29        * UserInterface/Views/CanvasContentView.js:
     30        (WI.CanvasContentView):
     31        (WI.CanvasContentView.prototype.get navigationItems):
     32        (WI.CanvasContentView.prototype.layout):
     33        (WI.CanvasContentView.prototype.shown):
     34        (WI.CanvasContentView.prototype._recordingStarted):
     35        (WI.CanvasContentView.prototype._recordingProgress):
     36        (WI.CanvasContentView.prototype._recordingStopped):
     37        (WI.CanvasContentView.prototype._updateRecordNavigationItem):
     38        (WI.CanvasContentView.prototype._updateProgressView):
     39        Replace progress UI with a reusable ProgressView class.
     40        When in the overview, clicking the CanvasContentView shows a dedicated
     41        CanvasContentView for inspecting shaders and recordings. This behavior
     42        is controlled by CollectionContentView, so we need to prevent it when
     43        clicking inside the header and footer elements, which contain clickable UI.
     44
     45        * UserInterface/Views/CanvasDetailsSidebarPanel.js:
     46        (WI.CanvasDetailsSidebarPanel.prototype.inspect):
     47
     48        * UserInterface/Views/CanvasOverviewContentView.css:
     49        (.content-view.canvas-overview .content-view.canvas):
     50        (.content-view.canvas-overview .content-view.canvas.is-recording):
     51        (.content-view.canvas-overview .content-view.canvas > :matches(header, footer)):
     52        (.content-view.canvas-overview .content-view.canvas > header):
     53        (.content-view.canvas-overview .content-view.canvas.is-recording > header):
     54        (.content-view.canvas-overview .content-view.canvas > header > .navigation-bar):
     55        (.content-view.canvas-overview .content-view.canvas:matches(:hover, .is-recording) > header > .navigation-bar):
     56        (.content-view.canvas-overview .content-view.canvas.is-recording > .progress-view,):
     57        (.content-view.canvas-overview .content-view.canvas.is-recording > .preview):
     58        (.content-view.canvas-overview .content-view.canvas > :matches(header, .progress, .preview, footer)): Deleted.
     59        (.content-view.canvas-overview .content-view.canvas.selected > :matches(.progress, .preview, footer),): Deleted.
     60        (.content-view.canvas-overview .content-view.canvas:not(:hover, .is-recording, .selected) > header > .navigation-bar): Deleted.
     61        (.content-view.canvas-overview .content-view.canvas > :matches(.progress, .preview)): Deleted.
     62        (.content-view.canvas-overview .content-view.canvas > .preview): Deleted.
     63        (.content-view.canvas-overview .content-view.canvas > .progress ~ .preview): Deleted.
     64        Clean up styles, and remove selection styles as canvases are no longer selectable in the overview.
     65
     66        * UserInterface/Views/CanvasOverviewContentView.js:
     67        (WI.CanvasOverviewContentView):
     68        (WI.CanvasOverviewContentView.prototype.get navigationItems):
     69        (WI.CanvasOverviewContentView.prototype.attached):
     70        (WI.CanvasOverviewContentView.prototype.detached):
     71        (WI.CanvasOverviewContentView.prototype.get selectionPathComponents): Deleted.
     72        (WI.CanvasOverviewContentView.prototype._changeSelectedItemVertically): Deleted.
     73        (WI.CanvasOverviewContentView.prototype._changeSelectedItemHorizontally): Deleted.
     74        (WI.CanvasOverviewContentView.prototype._selectionPathComponentsChanged): Deleted.
     75        (WI.CanvasOverviewContentView.prototype._handleUp): Deleted.
     76        (WI.CanvasOverviewContentView.prototype._handleRight): Deleted.
     77        (WI.CanvasOverviewContentView.prototype._handleDown): Deleted.
     78        (WI.CanvasOverviewContentView.prototype._handleLeft): Deleted.
     79        (WI.CanvasOverviewContentView.prototype._handleSpace): Deleted.
     80        (WI.CanvasOverviewContentView.prototype._supplementalRepresentedObjectsDidChange): Deleted.
     81        Disable canvas selection. Remove logic for supplemental represented objects,
     82        path components, and selection keyboard shortcuts.
     83
     84        * UserInterface/Views/CanvasSidebarPanel.css: Added.
     85        (.sidebar > .panel.navigation.canvas > .content):
     86        (.sidebar > .panel.navigation.canvas > .navigation-bar > .item.record-start-stop.disabled):
     87        (.sidebar > .panel.navigation.canvas > .content > .tree-outline .item.canvas.canvas-2d .icon):
     88        (.sidebar > .panel.navigation.canvas > .content > .tree-outline .item.canvas.webgl .icon):
     89        (.sidebar > .panel.navigation.canvas > .content > .navigation-bar):
     90        (.sidebar > .panel.navigation.canvas.has-recordings > .content > .tree-outline.canvas):
     91        (.sidebar > .panel.navigation.canvas:not(.has-recordings) > .filter-bar,):
     92        (.sidebar > .panel.navigation.canvas > .content > .tree-outline .item.recording > .icon):
     93        (.sidebar > .panel.navigation.canvas > .content > .tree-outline .item.shader-program > .icon):
     94        (.sidebar > .panel.navigation.canvas > .content > .tree-outline .item.folder-icon > .icon):
     95        (.sidebar > .panel.navigation.canvas > .content > .tree-outline .item.folder-icon > .status):
     96
     97        * UserInterface/Views/CanvasSidebarPanel.js: Added.
     98        (WI.CanvasSidebarPanel):
     99        (WI.CanvasSidebarPanel.prototype.get canvas):
     100        (WI.CanvasSidebarPanel.prototype.set canvas):
     101        (WI.CanvasSidebarPanel.prototype.set recording):
     102        (WI.CanvasSidebarPanel.prototype.set action):
     103        (WI.CanvasSidebarPanel.prototype.shown):
     104        (WI.CanvasSidebarPanel.prototype.hidden):
     105        (WI.CanvasSidebarPanel.prototype.hasCustomFilters):
     106        (WI.CanvasSidebarPanel.prototype.matchTreeElementAgainstCustomFilters):
     107        (WI.CanvasSidebarPanel.prototype.initialLayout):
     108        (WI.CanvasSidebarPanel.prototype._recordingAdded):
     109        (WI.CanvasSidebarPanel.prototype._recordingRemoved):
     110        (WI.CanvasSidebarPanel.prototype._scopeBarSelectionChanged):
     111        (WI.CanvasSidebarPanel.prototype._toggleRecording):
     112        (WI.CanvasSidebarPanel.prototype._currentRepresentedObjectsDidChange):
     113        (WI.CanvasSidebarPanel.prototype._treeOutlineSelectionDidChange):
     114        (WI.CanvasSidebarPanel.prototype._canvasChanged):
     115        (WI.CanvasSidebarPanel.prototype._recordingChanged):
     116        (WI.CanvasSidebarPanel.prototype._updateRecordNavigationItem):
     117        (WI.CanvasSidebarPanel.prototype._updateRecordingScopeBar):
     118        Add new navigation sidebar, split into two sections. The upper section
     119        contains a tree with a single element for the current canvas, and child
     120        elements for any shader programs. The maximum height of this section is 50%
     121        of the sidebar's height. The lower section contains a tree for the selected
     122        recording, and a scope bar for choosing between recordings.
     123
     124        * UserInterface/Views/CanvasTabContentView.css:
     125        (.content-view.tab.canvas .navigation-bar > .item .canvas-overview .icon):
     126        (.content-view.tab.canvas .navigation-bar > .item .canvas.canvas-2d .icon):
     127        (.content-view.tab.canvas .navigation-bar > .item .canvas.webgl .icon):
     128        (.content-view.tab.canvas .navigation-bar > .item .shader-program > .icon):
     129        (.content-view.tab.canvas .navigation-bar > .item > .hierarchical-path-component > .icon): Deleted.
     130        (.content-view.tab.canvas .navigation-bar > .item .canvas .icon): Deleted.
     131
     132        * UserInterface/Views/CanvasTabContentView.js:
     133        (WI.CanvasTabContentView):
     134        (WI.CanvasTabContentView.prototype.canShowRepresentedObject):
     135        (WI.CanvasTabContentView.prototype.attached):
     136        (WI.CanvasTabContentView.prototype._addCanvas):
     137        (WI.CanvasTabContentView.prototype._removeCanvas):
     138        (WI.CanvasTabContentView.prototype._canvasTreeOutlineSelectionDidChange):
     139        (WI.CanvasTabContentView.prototype._recordingAdded):
     140        (WI.CanvasTabContentView.prototype._handleSpace):
     141        (WI.CanvasTabContentView.prototype.showRepresentedObject): Deleted.
     142        (WI.CanvasTabContentView.prototype._navigationSidebarTreeOutlineSelectionChanged): Deleted.
     143        (WI.CanvasTabContentView.prototype._recordingActionIndexChanged): Deleted.
     144        (WI.CanvasTabContentView.prototype._updateActionIndex): Deleted.
     145        The canvas tab now maintains a tree outline of all canvases, with an
     146        "Overview" element as the root. The Overview element is always the first
     147        item of content browser's hierarchical path.
     148
     149        * UserInterface/Views/CanvasTreeElement.js:
     150        (WI.CanvasTreeElement.createRecordingTreeElement):
     151        (WI.CanvasTreeElement):
     152        (WI.CanvasTreeElement.prototype.onattach):
     153        (WI.CanvasTreeElement.prototype.onpopulate):
     154        (WI.CanvasTreeElement.prototype._updateStatus):
     155        (WI.CanvasTreeElement.prototype.ondetach): Deleted.
     156        Make it possible to not show recordings under the Canvas element.
     157        Create `isRecording` status element (spinner).
     158
     159        * UserInterface/Views/CollectionContentView.js:
     160        (WI.CollectionContentView.prototype.shown):
     161        (WI.CollectionContentView.prototype.hidden):
     162        Child ContentViews need to be updated when the collection's visibility changes.
     163
     164        * UserInterface/Views/ContentView.js:
     165        (WI.ContentView.isViewable):
     166
     167        * UserInterface/Views/ProgressView.css: Added.
     168        (.progress-view):
     169        (.progress-view > .titles):
     170        (.progress-view > .titles > .title):
     171        (.progress-view > .titles > .subtitle):
     172        (.progress-view > .titles > .subtitle::before):
     173        (.progress-view > .indeterminate-progress-spinner):
     174
     175        * UserInterface/Views/ProgressView.js: Added.
     176        (WI.ProgressView):
     177        (WI.ProgressView.prototype.get title):
     178        (WI.ProgressView.prototype.set title):
     179        (WI.ProgressView.prototype.get subtitle):
     180        (WI.ProgressView.prototype.set subtitle):
     181        (WI.ProgressView.prototype.get visible):
     182        (WI.ProgressView.prototype.set visible):
     183        (WI.ProgressView.prototype.initialLayout):
     184        (WI.ProgressView.prototype._updateTitles):
     185        New view class (not a ContentView) for showing a generic progress message,
     186        with a title, subtitle, and progress spinner.
     187
     188        * UserInterface/Views/RecordingContentView.css:
     189        (.content-view:not(.tab).recording > .preview-container):
     190        Remove unnecessary styles.
     191
     192        * UserInterface/Views/RecordingContentView.js:
     193        (WI.RecordingContentView):
     194        (WI.RecordingContentView.prototype.get navigationItems):
     195        (WI.RecordingContentView.prototype.get supplementalRepresentedObjects):
     196        (WI.RecordingContentView.prototype.updateActionIndex):
     197        (WI.RecordingContentView.prototype.get saveData):
     198        (WI.RecordingContentView.prototype._exportRecording):
     199        Relocate the recording export logic and UI.
     200        (WI.RecordingContentView.prototype.async._generateContentCanvas2D):
     201        (WI.RecordingContentView.prototype.async._generateContentCanvasWebGL):
     202        (WI.RecordingContentView.prototype._sliderChanged):
     203        Refactor logic for notifying the rest of the UI of changes to the action slider.
     204        The selected action is now exposed as a supplemental represented object, and a
     205        corresponding SupplementalRepresentedObjectsDidChange event.
     206
     207        * UserInterface/Views/RecordingStateDetailsSidebarPanel.js:
     208        (WI.RecordingStateDetailsSidebarPanel.prototype.inspect):
     209        (WI.RecordingStateDetailsSidebarPanel.prototype.set action):
     210        (WI.RecordingStateDetailsSidebarPanel.prototype._generateDetailsCanvas2D):
     211        (WI.RecordingStateDetailsSidebarPanel):
     212        (WI.RecordingStateDetailsSidebarPanel.prototype.updateAction): Deleted.
     213
     214        * UserInterface/Views/RecordingTraceDetailsSidebarPanel.js:
     215        (WI.RecordingTraceDetailsSidebarPanel.prototype.inspect):
     216        (WI.RecordingTraceDetailsSidebarPanel.prototype.set action):
     217        (WI.RecordingTraceDetailsSidebarPanel):
     218        (WI.RecordingTraceDetailsSidebarPanel.prototype.updateAction): Deleted.
     219        Now that the selected action is exposed to the UI as a supplemental
     220        represented object, details sidebars can be more decoupled from the
     221        canvas tab, and be notified of changes to the selection via `inspect()`.
     222
     223        * UserInterface/Views/ResourceIcons.css:
     224        (.canvas > .icon): Deleted.
     225        (.shader-program .icon): Deleted.
     226
    12272018-02-08  Nikita Vasilyev  <nvasilyev@apple.com>
    2228
  • trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js

    r228215 r228301  
    169169localizedStrings["Canvas %d"] = "Canvas %d";
    170170localizedStrings["Canvas %s"] = "Canvas %s";
    171 localizedStrings["Canvas Overview"] = "Canvas Overview";
    172171localizedStrings["Canvases"] = "Canvases";
    173172localizedStrings["Cap"] = "Cap";
     
    403402localizedStrings["Export"] = "Export";
    404403localizedStrings["Export HAR"] = "Export HAR";
     404localizedStrings["Export recording (%s)"] = "Export recording (%s)";
    405405localizedStrings["Expression"] = "Expression";
    406406localizedStrings["Extension Scripts"] = "Extension Scripts";
     
    497497localizedStrings["Immediate Pause Requested"] = "Immediate Pause Requested";
    498498localizedStrings["Import"] = "Import";
     499localizedStrings["Import recording from file"] = "Import recording from file";
     500localizedStrings["Imported Recordings"] = "Imported Recordings";
    499501localizedStrings["Incomplete"] = "Incomplete";
    500502localizedStrings["Indent"] = "Indent";
     
    906908localizedStrings["Start element selection (%s)"] = "Start element selection (%s)";
    907909localizedStrings["Start recording (%s)\nCreate new recording (%s)"] = "Start recording (%s)\nCreate new recording (%s)";
    908 localizedStrings["Start recording canvas actions. Shift-click to record a single frame."] = "Start recording canvas actions. Shift-click to record a single frame.";
     910localizedStrings["Start recording canvas actions.\nShift-click to record a single frame."] = "Start recording canvas actions.\nShift-click to record a single frame.";
    909911localizedStrings["Start to Finish"] = "Start to Finish";
    910912localizedStrings["State"] = "State";
  • trunk/Source/WebInspectorUI/UserInterface/Images/Canvas2D.svg

    r219180 r228301  
    11<?xml version="1.0" encoding="utf-8"?>
    22<!-- Copyright © 2017 Apple Inc. All rights reserved. -->
    3 <svg viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg">
    4     <polygon fill="rgb(242, 97, 97)" stroke="white" stroke-width="0.5" points="9,5.226497308103743 14,13.88675134594813 4,13.88675134594813" />
    5     <circle fill="rgb(97, 242, 97)" stroke="white" stroke-width="0.5" cx="10" cy="5" r="4" />
    6     <rect fill="rgb(92, 140, 229)" stroke="white" stroke-width="0.5" x="1.5" y="3" width="8" height="8" />
     3<svg xmlns="http://www.w3.org/2000/svg" id="root" version="1.1" viewBox="0 0 16 16">
     4    <rect x="1.5" y="1.5" width="13" height="13" rx="1.5" ry="1.5" fill="#fff" stroke="#4d4d4d"/>
     5    <path d="M11,9,9,11,6,8,3,11v1.3a.7.7,0,0,0,.7.7h8.6a.7.7,0,0,0,.7-.7V11Z" fill="#666"/>
     6    <circle cx="10.5" cy="5.5" r="1.75" fill="#666"/>
    77</svg>
  • trunk/Source/WebInspectorUI/UserInterface/Images/Canvas3D.svg

    r219180 r228301  
    11<?xml version="1.0" encoding="utf-8"?>
    22<!-- Copyright © 2017 Apple Inc. All rights reserved. -->
    3 <svg viewBox="0 0 14 16" version="1.1" xmlns="http://www.w3.org/2000/svg">
    4     <path fill="rgb(92, 140, 229)" stroke="white" stroke-width="0.5" stroke-linejoin="round" stroke-linecap="square" d="M 0.5 4.29857047 L 7.09714095 7.59714095 L 7.09714095 15.0189245 L 0.5 11.720354 L 0.5 4.29857047 Z" />
    5     <path fill="rgb(242, 97, 97)" stroke="white" stroke-width="0.5" stroke-linejoin="round" stroke-linecap="square" d="M 13.697 4.299 L 7.097 7.597 L 7.097 15.019 L 13.697 11.72 L 13.697 4.299 Z"/>
    6     <path fill="rgb(97, 242, 97)" stroke="white" stroke-width="0.5" stroke-linejoin="round" d="M 0.5 4.29857047 L 7.09714095 1 L 13.6908901 4.29857047 L 7.09714095 7.59714095 L 0.5 4.29857047 Z"/>
     3<svg xmlns="http://www.w3.org/2000/svg" id="root" version="1.1" viewBox="0 0 16 16">
     4    <rect x="1.5" y="1.5" width="13" height="13" rx="1.5" ry="1.5" fill="#fff" stroke="#4d4d4d"/>
     5    <polygon points="12.109 10.361 8.451 12.469 8 12.731 7.549 12.469 3.891 10.361 3.891 5.63 4.207 5.45 7.982 3.269 11.775 5.45 12.1 5.639 12.1 6.315 12.109 10.361" fill="#ccc" stroke="#666"/>
     6    <path d="M12.1,5.639,8,8,3.891,5.63M8,8v4.731" fill="none" stroke="#666"/>
    77</svg>
  • trunk/Source/WebInspectorUI/UserInterface/Images/Recording.svg

    r223918 r228301  
    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 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"/>
     4    <circle cx="4" cy="8" r="2.5" fill="none" stroke="rgb(26, 26, 26)" stroke-width="1.2"/>
     5    <circle cx="12" cy="8" r="2.5" fill="none" stroke="rgb(26, 26, 26)" stroke-width="1.2"/>
     6    <line x1="4" y1="10.5" x2="12" y2="10.5" fill="none" stroke="rgb(26, 26, 26)"/>
    67</svg>
  • trunk/Source/WebInspectorUI/UserInterface/Main.html

    r228024 r228301  
    4747    <link rel="stylesheet" href="Views/CanvasDetailsSidebarPanel.css">
    4848    <link rel="stylesheet" href="Views/CanvasOverviewContentView.css">
     49    <link rel="stylesheet" href="Views/CanvasSidebarPanel.css">
    4950    <link rel="stylesheet" href="Views/CanvasTabContentView.css">
    5051    <link rel="stylesheet" href="Views/ChartDetailsSectionRow.css">
     
    140141    <link rel="stylesheet" href="Views/ProbeSetDataGrid.css">
    141142    <link rel="stylesheet" href="Views/ProfileView.css">
     143    <link rel="stylesheet" href="Views/ProgressView.css">
    142144    <link rel="stylesheet" href="Views/QuickConsole.css">
    143145    <link rel="stylesheet" href="Views/RadioButtonNavigationItem.css">
     
    146148    <link rel="stylesheet" href="Views/RecordingStateDetailsSidebarPanel.css">
    147149    <link rel="stylesheet" href="Views/RecordingTraceDetailsSidebarPanel.css">
    148     <link rel="stylesheet" href="Views/RecordingNavigationSidebarPanel.css">
    149150    <link rel="stylesheet" href="Views/RenderingFrameTimelineOverviewGraph.css">
    150151    <link rel="stylesheet" href="Views/RenderingFrameTimelineView.css">
     
    561562    <script src="Views/CanvasDetailsSidebarPanel.js"></script>
    562563    <script src="Views/CanvasOverviewContentView.js"></script>
     564    <script src="Views/CanvasSidebarPanel.js"></script>
    563565    <script src="Views/CanvasTreeElement.js"></script>
    564566    <script src="Views/ChartDetailsSectionRow.js"></script>
     
    689691    <script src="Views/ProfileNodeTreeElement.js"></script>
    690692    <script src="Views/ProfileView.js"></script>
     693    <script src="Views/ProgressView.js"></script>
    691694    <script src="Views/QuickConsole.js"></script>
    692695    <script src="Views/QuickConsoleNavigationBar.js"></script>
     
    694697    <script src="Views/RecordingActionTreeElement.js"></script>
    695698    <script src="Views/RecordingContentView.js"></script>
    696     <script src="Views/RecordingNavigationSidebarPanel.js"></script>
    697699    <script src="Views/RecordingStateDetailsSidebarPanel.js"></script>
    698700    <script src="Views/RecordingTraceDetailsSidebarPanel.js"></script>
  • trunk/Source/WebInspectorUI/UserInterface/Models/RecordingAction.js

    r225602 r228301  
    9999    get hasVisibleEffect() { return this._hasVisibleEffect; }
    100100    get stateModifiers() { return this._stateModifiers; }
     101
     102    get state() { return this._state; }
     103    set state(state) { this._state = state; }
    101104
    102105    markInvalid()
  • trunk/Source/WebInspectorUI/UserInterface/Views/CanvasContentView.css

    r224726 r228301  
    2626.content-view.canvas:not(.tab) {
    2727    background-color: hsl(0, 0%, 90%);
    28 
    29     --progress-padding: 8px;
    30 }
    31 
    32 .content-view.canvas:not(.tab) > .progress {
    33     padding: var(--progress-padding) 0;
    34     text-align: center;
    35     color: var(--text-color-gray-medium);
    36 }
    37 
    38 .content-view.canvas:not(.tab) > .progress > .frame-count {
    39     color: var(--text-color-gray-dark);
    4028}
    4129
     
    4735    height: 100%;
    4836    padding: 15px;
     37}
     38
     39.content-view.canvas:not(.tab) {
     40    display: flex;
     41    flex-direction: column;
    4942}
    5043
  • trunk/Source/WebInspectorUI/UserInterface/Views/CanvasContentView.js

    r227243 r228301  
    3434        this.element.classList.add("canvas");
    3535
    36         this._recordingProgressElement = null;
     36        this._progressView = null;
    3737        this._previewContainerElement = null;
    3838        this._previewImageElement = null;
     
    4646
    4747        if (this.representedObject.contextType === WI.Canvas.ContextType.Canvas2D || this.representedObject.contextType === WI.Canvas.ContextType.WebGL) {
    48             const toolTip = WI.UIString("Start recording canvas actions. Shift-click to record a single frame.");
     48            const toolTip = WI.UIString("Start recording canvas actions.\nShift-click to record a single frame.");
    4949            const altToolTip = WI.UIString("Stop recording canvas actions");
    5050            this._recordButtonNavigationItem = new WI.ToggleButtonNavigationItem("record-start-stop", toolTip, altToolTip, "Images/Record.svg", "Images/Stop.svg", 13, 13);
     
    6767    get navigationItems()
    6868    {
    69         let navigationItems = [this._refreshButtonNavigationItem, this._showGridButtonNavigationItem];
    70         if (this._recordButtonNavigationItem)
    71             navigationItems.unshift(this._recordButtonNavigationItem);
    72         return navigationItems;
     69        // The toggle recording NavigationItem isn't added to the ContentBrowser's NavigationBar.
     70        // It's added to the "quick access" NavigationBar shown when hovering the canvas in the overview.
     71        return [this._refreshButtonNavigationItem, this._showGridButtonNavigationItem];
    7372    }
    7473
     
    9897
    9998        let header = this.element.appendChild(document.createElement("header"));
     99        header.addEventListener("click", (event) => { event.stopPropagation(); });
     100
    100101        let titles = header.appendChild(document.createElement("div"));
    101102        titles.className = "titles";
     
    121122
    122123        let footer = this.element.appendChild(document.createElement("footer"));
     124        footer.addEventListener("click", (event) => { event.stopPropagation(); });
    123125
    124126        this._recordingSelectContainer = footer.appendChild(document.createElement("div"));
     
    179181
    180182        this._updateRecordNavigationItem();
     183        this._updateProgressView();
    181184    }
    182185
     
    263266    {
    264267        this._updateRecordNavigationItem();
    265 
    266         if (!this.representedObject.isRecording)
    267             return;
    268 
    269         if (!this._recordingProgressElement) {
    270             this._recordingProgressElement = this._previewContainerElement.insertAdjacentElement("beforebegin", document.createElement("div"));
    271             this._recordingProgressElement.className = "progress";
    272         }
    273 
    274         this._recordingProgressElement.textContent = WI.UIString("Waiting for frames…");
     268        this._updateProgressView();
    275269    }
    276270
     
    281275            return;
    282276
    283         this._recordingProgressElement.removeChildren();
    284 
    285         let frameCountElement = this._recordingProgressElement.appendChild(document.createElement("span"));
    286         frameCountElement.className = "frame-count";
    287 
    288         let frameString = frameCount === 1 ? WI.UIString("%d Frame") : WI.UIString("%d Frames");
    289         frameCountElement.textContent = frameString.format(frameCount);
    290 
    291         this._recordingProgressElement.append(" ");
    292 
    293         let bufferUsedElement = this._recordingProgressElement.appendChild(document.createElement("span"));
    294         bufferUsedElement.className = "buffer-used";
    295         bufferUsedElement.textContent = "(" + Number.bytesToString(bufferUsed) + ")";
     277        this._updateProgressView(frameCount, bufferUsed);
    296278    }
    297279
     
    304286            return;
    305287
     288        this._updateProgressView();
     289
    306290        if (recording)
    307291            this._addRecording(recording);
    308 
    309         if (this._recordingProgressElement) {
    310             this._recordingProgressElement.remove();
    311             this._recordingProgressElement = null;
    312         }
    313292    }
    314293
     
    389368        this._recordButtonNavigationItem.toggled = isRecording;
    390369
     370        this._refreshButtonNavigationItem.enabled = !isRecording;
     371
    391372        this.element.classList.toggle("is-recording", isRecording);
    392373    }
     374
     375    _updateProgressView(frameCount, bufferUsed)
     376    {
     377        if (!this.representedObject.isRecording) {
     378            if (this._progressView && this._progressView.parentView) {
     379                this.removeSubview(this._progressView);
     380                this._progressView = null;
     381            }
     382            return;
     383        }
     384
     385        if (!this._progressView) {
     386            this._progressView = new WI.ProgressView;
     387            this.element.insertBefore(this._progressView.element, this._previewContainerElement);
     388            this.addSubview(this._progressView);
     389        }
     390
     391        let title;
     392        if (frameCount) {
     393            let formatString = frameCount === 1 ? WI.UIString("%d Frame") : WI.UIString("%d Frames");
     394            title = formatString.format(frameCount);
     395        } else
     396            title = WI.UIString("Waiting for frames…")
     397
     398        this._progressView.title = title;
     399        this._progressView.subtitle = bufferUsed ? Number.bytesToString(bufferUsed) : "";
     400    }
    393401};
  • trunk/Source/WebInspectorUI/UserInterface/Views/CanvasDetailsSidebarPanel.js

    r225487 r228301  
    5656        this.canvas = objects.find((object) => object instanceof WI.Canvas);
    5757
    58         return true;
     58        return !!this.canvas;
    5959    }
    6060
  • trunk/Source/WebInspectorUI/UserInterface/Views/CanvasOverviewContentView.css

    r227155 r228301  
    3939}
    4040
    41 .content-view.canvas-overview .content-view.canvas > :matches(header, .progress, .preview, footer) {
     41.content-view.canvas-overview .content-view.canvas {
    4242    border: 1px solid var(--border-color);
     43    cursor: pointer;
    4344}
    4445
    45 .content-view.canvas-overview .content-view.canvas.selected > :matches(.progress, .preview, footer),
    46 .content-view.canvas-overview .content-view.canvas.selected:not(.is-recording) > header {
    47     border-color: var(--selected-background-color);
     46.content-view.canvas-overview .content-view.canvas.is-recording {
     47    border-color: red;
    4848}
    4949
     
    5656    padding: 0 6px;
    5757    height: var(--navigation-bar-height);
     58    cursor: default;
    5859}
    5960
    6061.content-view.canvas-overview .content-view.canvas > header {
    6162    font-size: 13px;
    62     border-bottom: none;
    6363}
    6464
    6565.content-view.canvas-overview .content-view.canvas.is-recording > header {
    6666    background-color: red;
    67     border-color: red;
    6867}
    6968
     
    101100    align-items: initial;
    102101    border: none;
     102    opacity: 0;
     103    transition: opacity 200ms ease-in-out;
    103104}
    104105
    105 .content-view.canvas-overview .content-view.canvas:not(:hover, .is-recording, .selected) > header > .navigation-bar {
    106     visibility: hidden;
     106.content-view.canvas-overview .content-view.canvas:matches(:hover, .is-recording) > header > .navigation-bar {
     107    opacity: 1;
     108    transition: opacity 200ms ease-in-out;
    107109}
    108110
     
    120122}
    121123
    122 .content-view.canvas-overview .content-view.canvas > :matches(.progress, .preview) {
    123     border-top: none;
    124     border-bottom: none;
     124.content-view.canvas-overview .content-view.canvas.is-recording > .progress-view,
     125.content-view.canvas-overview .content-view.canvas > .preview {
     126    height: 280px;
    125127}
    126128
    127 .content-view.canvas-overview .content-view.canvas > .preview {
    128     height: var(--preview-height);
    129 
    130     --preview-height: 280px;
    131 }
    132 
    133 .content-view.canvas-overview .content-view.canvas > .progress ~ .preview {
    134     /* Keep the height of each CanvasContentView constant by subtracting the padding-top, */
    135     /* padding-bottom, and text-height (1em) from the previously set height. */
    136     height: calc(var(--preview-height) - 1em - (2 * var(--progress-padding)));
     129.content-view.canvas-overview .content-view.canvas.is-recording > .preview {
     130    display: none;
    137131}
    138132
  • trunk/Source/WebInspectorUI/UserInterface/Views/CanvasOverviewContentView.js

    r227008 r228301  
    4646        this.element.classList.add("canvas-overview");
    4747
     48        this._importButtonNavigationItem = new WI.ButtonNavigationItem("import-recording", WI.UIString("Import"), "Images/Import.svg", 15, 15);
     49        this._importButtonNavigationItem.toolTip = WI.UIString("Import recording from file");
     50        this._importButtonNavigationItem.buttonStyle = WI.ButtonNavigationItem.Style.ImageAndText;
     51        this._importButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, () => { WI.canvasManager.importRecording(); });
     52
    4853        this._refreshButtonNavigationItem = new WI.ButtonNavigationItem("refresh-all", WI.UIString("Refresh all"), "Images/ReloadFull.svg", 13, 13);
    4954        this._refreshButtonNavigationItem.enabled = false;
     
    5459        this._showGridButtonNavigationItem.enabled = false;
    5560        this._showGridButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._showGridButtonClicked, this);
    56 
    57         this.selectionEnabled = true;
    58 
    59         this._keyboardShortcuts = [
    60             new WI.KeyboardShortcut(null, WI.KeyboardShortcut.Key.Up, this._handleUp.bind(this)),
    61             new WI.KeyboardShortcut(null, WI.KeyboardShortcut.Key.Right, this._handleRight.bind(this)),
    62             new WI.KeyboardShortcut(null, WI.KeyboardShortcut.Key.Down, this._handleDown.bind(this)),
    63             new WI.KeyboardShortcut(null, WI.KeyboardShortcut.Key.Left, this._handleLeft.bind(this)),
    64         ];
    65 
    66         let recordShortcut = new WI.KeyboardShortcut(null, WI.KeyboardShortcut.Key.Space, this._handleSpace.bind(this));
    67         recordShortcut.implicitlyPreventsDefault = false;
    68         this._keyboardShortcuts.push(recordShortcut);
    69 
    70         let recordSingleFrameShortcut = new WI.KeyboardShortcut(WI.KeyboardShortcut.Modifier.Shift, WI.KeyboardShortcut.Key.Space, this._handleSpace.bind(this));
    71         recordSingleFrameShortcut.implicitlyPreventsDefault = false;
    72         this._keyboardShortcuts.push(recordSingleFrameShortcut);
    73 
    74         for (let shortcut of this._keyboardShortcuts)
    75             shortcut.disabled = true;
    7661    }
    7762
     
    8065    get navigationItems()
    8166    {
    82         return [this._refreshButtonNavigationItem, this._showGridButtonNavigationItem];
    83     }
    84 
    85     get selectionPathComponents()
    86     {
    87         let components = [];
    88 
    89         if (this.supplementalRepresentedObjects.length) {
    90             let [canvas] = this.supplementalRepresentedObjects;
    91             let tabContentView = WI.tabBrowser.selectedTabContentView;
    92             if (tabContentView) {
    93                 let treeElement = tabContentView.treeElementForRepresentedObject(canvas);
    94                 console.assert(treeElement);
    95                 if (treeElement) {
    96                     let pathComponent = new WI.GeneralTreeElementPathComponent(treeElement);
    97                     pathComponent.addEventListener(WI.HierarchicalPathComponent.Event.SiblingWasSelected, this._selectionPathComponentsChanged, this);
    98                     components.push(pathComponent);
    99                 }
    100             }
    101         }
    102 
    103         return components;
     67        return [this._importButtonNavigationItem, new WI.DividerNavigationItem, this._refreshButtonNavigationItem, this._showGridButtonNavigationItem];
    10468    }
    10569
     
    13498
    13599        WI.settings.showImageGrid.addEventListener(WI.Setting.Event.Changed, this._updateShowImageGrid, this);
    136 
    137         this.addEventListener(WI.ContentView.Event.SupplementalRepresentedObjectsDidChange, this._supplementalRepresentedObjectsDidChange, this);
    138 
    139         for (let shortcut of this._keyboardShortcuts)
    140             shortcut.disabled = false;
    141100    }
    142101
     
    144103    {
    145104        WI.settings.showImageGrid.removeEventListener(null, null, this);
    146 
    147         this.removeEventListener(null, null, this);
    148 
    149         for (let shortcut of this._keyboardShortcuts)
    150             shortcut.disabled = true;
    151105
    152106        super.detached();
     
    166120    }
    167121
    168     _changeSelectedItemVertically(shift)
    169     {
    170         let itemElementWidth = this.element.firstElementChild.offsetWidth + (2 * this._itemMargin);
    171         let itemsPerRow = Math.floor(this.element.offsetWidth / itemElementWidth);
    172 
    173         let items = Array.from(this.representedObject.items);
    174         let index = items.indexOf(this._selectedItem);
    175         if (index === -1)
    176             index = shift < 0 ? items.length + 1 : itemsPerRow;
    177 
    178         index += shift * itemsPerRow;
    179         if (index < 0)
    180             index = items.length + index;
    181 
    182         this.setSelectedItem(items[index % items.length]);
    183     }
    184 
    185     _changeSelectedItemHorizontally(shift)
    186     {
    187         let itemElementWidth = this.element.firstElementChild.offsetWidth + (2 * this._itemMargin);
    188         let itemsPerRow = Math.floor(this.element.offsetWidth / itemElementWidth);
    189 
    190         let items = Array.from(this.representedObject.items);
    191         let index = items.indexOf(this._selectedItem);
    192         if (index === -1)
    193             index = shift >= 0 ? itemsPerRow - 1 : 0;
    194 
    195         let selectedRow = Math.floor(index / itemsPerRow);
    196         index += shift;
    197         if (index < selectedRow * itemsPerRow)
    198             index += itemsPerRow;
    199         else if (index >= (selectedRow + 1) * itemsPerRow)
    200             index -= itemsPerRow;
    201 
    202         this.setSelectedItem(items[index]);
    203     }
    204 
    205122    _updateNavigationItems()
    206123    {
     
    210127    }
    211128
    212     _selectionPathComponentsChanged(event)
    213     {
    214         let pathComponent = event.data.pathComponent;
    215         if (pathComponent.representedObject instanceof WI.Canvas)
    216             this.setSelectedItem(pathComponent.representedObject);
    217         else if (pathComponent.representedObject instanceof WI.Recording)
    218             WI.showRepresentedObject(pathComponent.representedObject);
    219     }
    220 
    221129    _showGridButtonClicked(event)
    222130    {
     
    224132    }
    225133
    226     _handleUp(event)
    227     {
    228         this._changeSelectedItemVertically(-1);
    229     }
    230 
    231     _handleRight(event)
    232     {
    233         let shift = WI.resolvedLayoutDirection() === WI.LayoutDirection.RTL ? -1 : 1;
    234         this._changeSelectedItemHorizontally(shift);
    235     }
    236 
    237     _handleDown(event)
    238     {
    239         this._changeSelectedItemVertically(1);
    240     }
    241 
    242     _handleLeft(event)
    243     {
    244         let shift = WI.resolvedLayoutDirection() === WI.LayoutDirection.RTL ? 1 : -1;
    245         this._changeSelectedItemHorizontally(shift);
    246     }
    247 
    248     _handleSpace(event)
    249     {
    250         if (WI.isEventTargetAnEditableField(event))
    251             return;
    252 
    253         if (!this._selectedItem)
    254             return;
    255 
    256         if (this._selectedItem.isRecording)
    257             WI.canvasManager.stopRecording();
    258         else if (!WI.canvasManager.recordingCanvas) {
    259             let singleFrame = !!event.shiftKey;
    260             WI.canvasManager.startRecording(this._selectedItem, singleFrame);
    261         }
    262 
    263         event.preventDefault();
    264     }
    265 
    266134    _updateShowImageGrid()
    267135    {
    268136        this._showGridButtonNavigationItem.activated = !!WI.settings.showImageGrid.value;
    269     }
    270 
    271     _supplementalRepresentedObjectsDidChange()
    272     {
    273         this.dispatchEventToListeners(WI.ContentView.Event.SelectionPathComponentsDidChange);
    274137    }
    275138
  • trunk/Source/WebInspectorUI/UserInterface/Views/CanvasTabContentView.css

    r223918 r228301  
    2424 */
    2525
    26 .content-view.tab.canvas .navigation-bar > .item > .hierarchical-path-component > .icon {
    27     opacity: 0.7;
     26.content-view.tab.canvas .navigation-bar > .item .canvas-overview .icon {
     27    content: url(../Images/CanvasOverview.svg);
    2828}
    2929
    30 /* FIXME: this can be removed once <https://webkit.org/b/177606> is complete. */
    31 .content-view.tab.canvas .navigation-bar > .item .canvas .icon {
    32     content: url(../Images/Canvas.svg);
     30.content-view.tab.canvas .navigation-bar > .item .canvas.canvas-2d .icon {
     31    content: url(../Images/Canvas2D.svg);
     32}
     33
     34.content-view.tab.canvas .navigation-bar > .item .canvas.webgl .icon {
     35    content: url(../Images/Canvas3D.svg);
    3336}
    3437
     
    3639    content: url(../Images/Recording.svg);
    3740}
     41
     42.content-view.tab.canvas .navigation-bar > .item .shader-program > .icon {
     43    content: image-set(url(../Images/DocumentGL.png) 1x, url(../Images/DocumentGL@2x.png) 2x);
     44}
  • trunk/Source/WebInspectorUI/UserInterface/Views/CanvasTabContentView.js

    r228024 r228301  
    3232        let tabBarItem = WI.GeneralTabBarItem.fromTabInfo(WI.CanvasTabContentView.tabInfo());
    3333
    34         const navigationSidebarPanelConstructor = WI.RecordingNavigationSidebarPanel;
     34        const navigationSidebarPanelConstructor = WI.CanvasSidebarPanel;
    3535        const detailsSidebarPanelConstructors = [WI.RecordingStateDetailsSidebarPanel, WI.RecordingTraceDetailsSidebarPanel, WI.CanvasDetailsSidebarPanel];
    3636        const disableBackForward = true;
     
    4242        this._canvasTreeOutline.addEventListener(WI.TreeOutline.Event.SelectionDidChange, this._canvasTreeOutlineSelectionDidChange, this);
    4343
    44         const leftArrow = "Images/BackForwardArrows.svg#left-arrow-mask";
    45         const rightArrow = "Images/BackForwardArrows.svg#right-arrow-mask";
    46         let isRTL = WI.resolvedLayoutDirection() === WI.LayoutDirection.RTL;
    47         let backButtonImage = isRTL ? rightArrow : leftArrow;
    48         this._overviewNavigationItem = new WI.ButtonNavigationItem("canvas-overview", WI.UIString("Canvas Overview"), backButtonImage, 8, 13);
    49         this._overviewNavigationItem.hidden = true;
    50         this._overviewNavigationItem.visibilityPriority = WI.NavigationItem.VisibilityPriority.High;
    51         this._overviewNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, () => { this.showRepresentedObject(this._canvasCollection); });
    52 
    53         this.contentBrowser.navigationBar.insertNavigationItem(this._overviewNavigationItem, 2);
    54         this.contentBrowser.navigationBar.insertNavigationItem(new WI.DividerNavigationItem, 3);
    55 
    56         this.navigationSidebarPanel.contentTreeOutline.addEventListener(WI.TreeOutline.Event.SelectionDidChange, this._navigationSidebarTreeOutlineSelectionChanged, this);
     44        this._overviewTreeElement = new WI.GeneralTreeElement("canvas-overview", WI.UIString("Overview"), null, this._canvasCollection);
     45        this._canvasTreeOutline.appendChild(this._overviewTreeElement);
     46
     47        this._importedRecordingsTreeElement = new WI.FolderTreeElement(WI.UIString("Imported Recordings"));
     48        this._importedRecordingsTreeElement.hidden = true;
     49        this._overviewTreeElement.appendChild(this._importedRecordingsTreeElement);
     50
     51        this._recordShortcut = new WI.KeyboardShortcut(null, WI.KeyboardShortcut.Key.Space, this._handleSpace.bind(this));
     52        this._recordShortcut.implicitlyPreventsDefault = false;
     53
     54        this._recordSingleFrameShortcut = new WI.KeyboardShortcut(WI.KeyboardShortcut.Modifier.Shift, WI.KeyboardShortcut.Key.Space, this._handleSpace.bind(this));
     55        this._recordSingleFrameShortcut.implicitlyPreventsDefault = false;
    5756    }
    5857
     
    8988    canShowRepresentedObject(representedObject)
    9089    {
    91         return representedObject instanceof WI.CanvasCollection
     90        return representedObject instanceof WI.Canvas
     91            || representedObject instanceof WI.CanvasCollection
    9292            || representedObject instanceof WI.Recording
    9393            || representedObject instanceof WI.ShaderProgram;
    94     }
    95 
    96     showRepresentedObject(representedObject, cookie)
    97     {
    98         super.showRepresentedObject(representedObject, cookie);
    99 
    100         this.navigationSidebarPanel.recording = null;
    101 
    102         if (representedObject instanceof WI.CanvasCollection || representedObject instanceof WI.ShaderProgram) {
    103             this._overviewNavigationItem.hidden = true;
    104             return;
    105         }
    106 
    107         if (representedObject instanceof WI.Recording) {
    108             this._overviewNavigationItem.hidden = false;
    109             this.navigationSidebarPanel.recording = representedObject;
    110             return;
    111         }
    112 
    113         console.assert(false, "Should not be reached.");
    11494    }
    11595
     
    142122        WI.canvasManager.addEventListener(WI.CanvasManager.Event.RecordingImported, this._recordingImportedOrStopped, this);
    143123        WI.canvasManager.addEventListener(WI.CanvasManager.Event.RecordingStopped, this._recordingImportedOrStopped, this);
    144         WI.RecordingContentView.addEventListener(WI.RecordingContentView.Event.RecordingActionIndexChanged, this._recordingActionIndexChanged, this);
    145124
    146125        let canvases = new Set(Array.from(this._canvasCollection.items).concat(WI.canvasManager.canvases));
     
    169148    _addCanvas(canvas)
    170149    {
    171         this._canvasTreeOutline.appendChild(new WI.CanvasTreeElement(canvas));
     150        this._overviewTreeElement.appendChild(new WI.CanvasTreeElement(canvas));
    172151        this._canvasCollection.add(canvas);
    173152
     
    178157    _removeCanvas(canvas)
    179158    {
    180         // Move all existing recordings for the removed canvas to be imported recordings, as the
    181         // recording's source is no longer valid.
    182         for (let recording of canvas.recordingCollection.items) {
    183             recording.source = null;
    184             recording.createDisplayName();
    185 
    186             const subtitle = null;
    187             this._canvasTreeOutline.appendChild(new WI.GeneralTreeElement(["recording"], recording.displayName, subtitle, recording));
    188         }
     159        // FIXME: Create tree elements/cards for recordings belonging to the removed canvas.
    189160
    190161        let treeElement = this._canvasTreeOutline.findTreeElement(canvas);
    191162        console.assert(treeElement, "Missing tree element for canvas.", canvas);
    192         this._canvasTreeOutline.removeChild(treeElement);
     163        this._overviewTreeElement.removeChild(treeElement);
    193164        this._canvasCollection.remove(canvas);
    194165
     
    215186
    216187        let representedObject = selectedElement.representedObject;
    217 
    218         if (this.canShowRepresentedObject(representedObject)) {
    219             this.showRepresentedObject(representedObject);
    220 
    221             if (representedObject instanceof WI.Recording)
    222                 this._updateActionIndex(0);
    223             return;
    224         }
    225 
    226         if (representedObject instanceof WI.Canvas) {
    227             this.showRepresentedObject(this._canvasCollection);
    228             this.contentBrowser.currentContentView.setSelectedItem(representedObject);
    229             return;
    230         }
    231 
    232         console.assert(false, "Unexpected representedObject.", representedObject);
     188        if (!this.canShowRepresentedObject(representedObject)) {
     189            console.assert(false, "Unexpected representedObject.", representedObject);
     190            return;
     191        }
     192
     193        this.showRepresentedObject(representedObject);
    233194    }
    234195
     
    244205    }
    245206
    246     _navigationSidebarTreeOutlineSelectionChanged(event)
    247     {
    248         if (!event.data.selectedElement)
    249             return;
    250 
    251         let recordingContentView = this.contentBrowser.currentContentView;
    252         if (!(recordingContentView instanceof WI.RecordingContentView))
    253             return;
    254 
    255         let selectedTreeElement = event.data.selectedElement;
    256         if (selectedTreeElement instanceof WI.FolderTreeElement)
    257             selectedTreeElement = selectedTreeElement.children.lastValue;
    258 
    259         this._updateActionIndex(selectedTreeElement.index, {suppressNavigationSidebarUpdate: true});
    260     }
    261 
    262207    _recordingAdded(recording, options = {})
    263208    {
     
    265210            const subtitle = null;
    266211            let recordingTreeElement = new WI.GeneralTreeElement(["recording"], recording.displayName, subtitle, recording);
    267             this._canvasTreeOutline.appendChild(recordingTreeElement);
    268         }
    269 
    270         if (!options.suppressShowRecording) {
     212            this._importedRecordingsTreeElement.hidden = false;
     213            this._importedRecordingsTreeElement.appendChild(recordingTreeElement);
     214        }
     215
     216        if (!options.suppressShowRecording)
    271217            this.showRepresentedObject(recording);
    272             this._updateActionIndex(0, {suppressNavigationSidebarUpdate: true});
    273         }
    274     }
    275 
    276     _recordingActionIndexChanged(event)
    277     {
    278         if (event.target !== this.contentBrowser.currentContentView)
    279             return;
    280 
    281         this._updateActionIndex(event.data.index);
    282     }
    283 
    284     _updateActionIndex(index, options = {})
    285     {
    286         options.actionCompletedCallback = (action, context) => {
    287             for (let detailsSidebarPanel of this.detailsSidebarPanels) {
    288                 if (detailsSidebarPanel.updateAction)
    289                     detailsSidebarPanel.updateAction(action, context, options);
    290             }
    291         };
    292 
    293         if (!options.suppressNavigationSidebarUpdate)
    294             this.navigationSidebarPanel.updateActionIndex(index, options);
    295 
    296         this.contentBrowser.currentContentView.updateActionIndex(index, options);
     218    }
     219
     220    _handleSpace(event)
     221    {
     222        if (WI.isEventTargetAnEditableField(event))
     223            return;
     224
     225        if (!this.navigationSidebarPanel)
     226            return;
     227
     228        let canvas = this.navigationSidebarPanel.canvas;
     229        if (!canvas)
     230            return;
     231
     232        if (canvas.isRecording)
     233            WI.canvasManager.stopRecording();
     234        else if (!WI.canvasManager.recordingCanvas) {
     235            let singleFrame = !!event.shiftKey;
     236            WI.canvasManager.startRecording(canvas, singleFrame);
     237        }
     238
     239        event.preventDefault();
    297240    }
    298241};
  • trunk/Source/WebInspectorUI/UserInterface/Views/CanvasTreeElement.js

    r224200 r228301  
    2626WI.CanvasTreeElement = class CanvasTreeElement extends WI.FolderizedTreeElement
    2727{
    28     constructor(representedObject)
     28    constructor(representedObject, showRecordings = true)
    2929    {
    3030        console.assert(representedObject instanceof WI.Canvas);
    3131
    32         const subtitle = null;
     32        let subtitle = WI.Canvas.displayNameForContextType(representedObject.contextType);
    3333        super(["canvas", representedObject.contextType], representedObject.displayName, subtitle, representedObject);
    3434
    3535        this.registerFolderizeSettings("shader-programs", WI.UIString("Shader Programs"), this.representedObject.shaderProgramCollection, WI.ShaderProgramTreeElement);
    3636
    37         function createRecordingTreeElement(recording) {
    38             return new WI.GeneralTreeElement(["recording"], recording.displayName, subtitle, recording);
     37        WI.canvasManager.addEventListener(WI.CanvasManager.Event.RecordingStarted, this._updateStatus, this);
     38        WI.canvasManager.addEventListener(WI.CanvasManager.Event.RecordingStopped, this._updateStatus, this);
     39
     40        this.representedObject.shaderProgramCollection.addEventListener(WI.Collection.Event.ItemAdded, this._handleItemAdded, this);
     41        this.representedObject.shaderProgramCollection.addEventListener(WI.Collection.Event.ItemRemoved, this._handleItemRemoved, this);
     42
     43        this._showRecordings = showRecordings;
     44        if (this._showRecordings) {
     45            function createRecordingTreeElement(recording) {
     46                return new WI.GeneralTreeElement(["recording"], recording.displayName, null, recording);
     47            }
     48            this.registerFolderizeSettings("recordings", WI.UIString("Recordings"), this.representedObject.recordingCollection, createRecordingTreeElement);
     49
     50            this.representedObject.recordingCollection.addEventListener(WI.Collection.Event.ItemAdded, this._handleItemAdded, this);
     51            this.representedObject.recordingCollection.addEventListener(WI.Collection.Event.ItemRemoved, this._handleItemRemoved, this);
    3952        }
    40         this.registerFolderizeSettings("recordings", WI.UIString("Recordings"), this.representedObject.recordingCollection, createRecordingTreeElement);
    4153    }
    4254
     
    4759        super.onattach();
    4860
    49         this.representedObject.shaderProgramCollection.addEventListener(WI.Collection.Event.ItemAdded, this._handleItemAdded, this);
    50         this.representedObject.shaderProgramCollection.addEventListener(WI.Collection.Event.ItemRemoved, this._handleItemRemoved, this);
    51 
    52         this.representedObject.recordingCollection.addEventListener(WI.Collection.Event.ItemAdded, this._handleItemAdded, this);
    53         this.representedObject.recordingCollection.addEventListener(WI.Collection.Event.ItemRemoved, this._handleItemRemoved, this);
    54 
    5561        this.element.addEventListener("mouseover", this._handleMouseOver.bind(this));
    5662        this.element.addEventListener("mouseout", this._handleMouseOut.bind(this));
    5763
    5864        this.onpopulate();
    59     }
    60 
    61     ondetach()
    62     {
    63         this.representedObject.shaderProgramCollection.removeEventListener(WI.Collection.Event.ItemAdded, this._handleItemAdded, this);
    64         this.representedObject.shaderProgramCollection.removeEventListener(WI.Collection.Event.ItemRemoved, this._handleItemRemoved, this);
    65 
    66         this.representedObject.recordingCollection.removeEventListener(WI.Collection.Event.ItemAdded, this._handleItemAdded, this);
    67         this.representedObject.recordingCollection.removeEventListener(WI.Collection.Event.ItemRemoved, this._handleItemRemoved, this);
    68 
    69         super.ondetach();
    7065    }
    7166
     
    8479            this.addChildForRepresentedObject(program);
    8580
    86         for (let recording of this.representedObject.recordingCollection.items)
    87             this.addChildForRepresentedObject(recording);
     81        if (this._showRecordings) {
     82            for (let recording of this.representedObject.recordingCollection.items)
     83                this.addChildForRepresentedObject(recording);
     84        }
    8885    }
    8986
     
    138135        WI.domTreeManager.hideDOMNodeHighlight();
    139136    }
     137
     138    _updateStatus()
     139    {
     140        if (this.representedObject.isRecording) {
     141            if (!this.status || !this.status[WI.CanvasTreeElement.SpinnerSymbol]) {
     142                let spinner = new WI.IndeterminateProgressSpinner;
     143                this.status = spinner.element;
     144                this.status[WI.CanvasTreeElement.SpinnerSymbol] = true;
     145            }
     146        } else {
     147            if (this.status && this.status[WI.CanvasTreeElement.SpinnerSymbol])
     148                this.status = "";
     149        }
     150    }
    140151};
  • trunk/Source/WebInspectorUI/UserInterface/Views/CollectionContentView.js

    r225487 r228301  
    5252    }
    5353
     54    shown()
     55    {
     56        super.shown();
     57
     58        for (let contentView of this._contentViewMap.values())
     59            contentView.shown();
     60    }
     61
     62    hidden()
     63    {
     64        for (let contentView of this._contentViewMap.values())
     65            contentView.hidden();
     66
     67        super.hidden();
     68    }
     69
    5470    get selectionEnabled()
    5571    {
  • trunk/Source/WebInspectorUI/UserInterface/Views/ContentView.js

    r226415 r228301  
    256256        if (representedObject instanceof WI.Canvas)
    257257            return true;
     258        if (representedObject instanceof WI.CanvasCollection)
     259            return true;
    258260        if (representedObject instanceof WI.ShaderProgram)
    259261            return true;
  • trunk/Source/WebInspectorUI/UserInterface/Views/ProgressView.css

    r228300 r228301  
    11/*
    2  * Copyright (C) 2017 Apple Inc. All rights reserved.
     2 * Copyright (C) 2018 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    2424 */
    2525
    26 .content-view.canvas:not(.tab) {
    27     background-color: hsl(0, 0%, 90%);
    28 
    29     --progress-padding: 8px;
     26.progress-view {
     27    display: flex;
     28    flex-shrink: 0;
     29    flex-direction: column;
     30    justify-content: center;
     31    font-size: var(--message-text-view-font-size);
     32    text-align: center;
    3033}
    3134
    32 .content-view.canvas:not(.tab) > .progress {
    33     padding: var(--progress-padding) 0;
    34     text-align: center;
     35.progress-view > .titles {
     36    padding: 15px 0;
     37}
     38
     39.progress-view > .titles > .title {
     40    color: var(--text-color-gray-dark);
     41}
     42
     43.progress-view > .titles > .subtitle {
    3544    color: var(--text-color-gray-medium);
    3645}
    3746
    38 .content-view.canvas:not(.tab) > .progress > .frame-count {
    39     color: var(--text-color-gray-dark);
     47.progress-view > .titles > .subtitle::before {
     48    content: "\2002\2012\2002";
    4049}
    4150
    42 .content-view.canvas:not(.tab) > .preview {
    43     display: flex;
    44     justify-content: center;
    45     align-items: center;
    46     width: 100%;
    47     height: 100%;
    48     padding: 15px;
     51.progress-view > .indeterminate-progress-spinner {
     52    flex-shrink: 0;
     53    width: 24px;
     54    height: 24px;
     55    margin: 0 auto;
    4956}
    50 
    51 .content-view.canvas:not(.tab) > .preview > img {
    52     max-width: 100%;
    53     max-height: 100%;
    54 }
    55 
    56 .content-view.canvas:not(.tab) > :matches(header, footer) {
    57     display: none;
    58 }
    59 
    60 .navigation-bar > .item.canvas-record.disabled {
    61     filter: grayscale();
    62     opacity: 0.5;
    63 }
  • trunk/Source/WebInspectorUI/UserInterface/Views/RecordingContentView.css

    r226077 r228301  
    6868    justify-content: center;
    6969    align-items: center;
    70     position: relative;
    71     width: 100%;
    72     height: 100%;
    7370}
    7471
  • trunk/Source/WebInspectorUI/UserInterface/Views/RecordingContentView.js

    r226755 r228301  
    3333
    3434        this._index = NaN;
     35        this._action = null;
    3536        this._snapshots = [];
    3637        this._initialContent = null;
     
    5455            this._showGridButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._showGridButtonClicked, this);
    5556            this._showGridButtonNavigationItem.activated = !!WI.settings.showImageGrid.value;
     57
     58            this._exportButtonNavigationItem = new WI.ButtonNavigationItem("export-recording", WI.UIString("Export"), "Images/Export.svg", 15, 15);
     59            this._exportButtonNavigationItem.toolTip = WI.UIString("Export recording (%s)").format(WI.saveKeyboardShortcut.displayName);
     60            this._exportButtonNavigationItem.buttonStyle = WI.ButtonNavigationItem.Style.ImageAndText;
     61            this._exportButtonNavigationItem.visibilityPriority = WI.NavigationItem.VisibilityPriority.High;
     62            this._exportButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, () => { this._exportRecording(); });
    5663        }
    5764    }
     
    8996        let isCanvas2D = this.representedObject.type === WI.Recording.Type.Canvas2D;
    9097        let isCanvasWebGL = this.representedObject.type === WI.Recording.Type.CanvasWebGL;
    91         if (isCanvas2D || isCanvasWebGL) {
    92             let navigationItems = [this._showGridButtonNavigationItem];
    93             if (isCanvas2D && WI.RecordingContentView.supportsCanvasPathDebugging())
    94                 navigationItems.unshift(this._showPathButtonNavigationItem);
    95             return navigationItems;
    96         }
    97         return [];
    98     }
    99 
    100     updateActionIndex(index, options = {})
     98        if (!isCanvas2D && !isCanvasWebGL)
     99            return [];
     100
     101        let navigationItems = [this._exportButtonNavigationItem, new WI.DividerNavigationItem];
     102        if (isCanvas2D && WI.RecordingContentView.supportsCanvasPathDebugging())
     103            navigationItems.push(this._showPathButtonNavigationItem);
     104
     105        navigationItems.push(this._showGridButtonNavigationItem);
     106        return navigationItems;
     107    }
     108
     109    get supplementalRepresentedObjects()
     110    {
     111        return this._action ? [this._action] : [];
     112    }
     113
     114    updateActionIndex(index)
    101115    {
    102116        if (!this.representedObject)
     
    115129
    116130            if (this.representedObject.type === WI.Recording.Type.Canvas2D)
    117                 this._throttler._generateContentCanvas2D(index, actions, options);
     131                this._throttler._generateContentCanvas2D(index, actions);
    118132            else if (this.representedObject.type === WI.Recording.Type.CanvasWebGL)
    119                 this._throttler._generateContentCanvasWebGL(index, actions, options);
     133                this._throttler._generateContentCanvasWebGL(index, actions);
    120134        });
    121135    }
     
    151165    get saveData()
    152166    {
    153         let filename = this.representedObject.displayName;
    154         if (!filename.endsWith(".json"))
    155             filename += ".json";
    156 
    157         return {
    158             url: "web-inspector:///" + encodeURI(filename),
    159             content: JSON.stringify(this.representedObject.toJSON()),
    160             forceSaveAs: true,
    161         };
     167        return {customSaveHandler: () => { this._exportRecording(); }};
    162168    }
    163169
     
    192198    // Private
    193199
    194     async _generateContentCanvas2D(index, actions, options = {})
     200    _exportRecording()
     201    {
     202        if (!this.representedObject) {
     203            InspectorFrontendHost.beep();
     204            return;
     205        }
     206
     207        let filename = this.representedObject.displayName;
     208        let url = "web-inspector:///" + encodeURI(filename) + ".json";
     209
     210        WI.saveDataToFile({
     211            url,
     212            content: JSON.stringify(this.representedObject.toJSON()),
     213            forceSaveAs: true,
     214        });
     215    }
     216
     217    async _generateContentCanvas2D(index, actions)
    195218    {
    196219        let imageLoad = (event) => {
     
    199222                return;
    200223
    201             this._generateContentCanvas2D(index, actions, options);
     224            this._generateContentCanvas2D(index, actions);
    202225        };
    203226
     
    227250            for (let name in snapshot.state) {
    228251                if (!(name in snapshot.context))
     252                    continue;
     253
     254                // Skip internal state used for path debugging.
     255                if (name === "currentX" || name === "currentY")
    229256                    continue;
    230257
     
    305332                this._pathContext.canvas.remove();
    306333
    307             callback();
     334            let state = {
     335                currentX: snapshot.context.currentX,
     336                currentY: snapshot.context.currentY,
     337                direction: snapshot.context.direction,
     338                fillStyle: snapshot.context.fillStyle,
     339                font: snapshot.context.font,
     340                globalAlpha: snapshot.context.globalAlpha,
     341                globalCompositeOperation: snapshot.context.globalCompositeOperation,
     342                imageSmoothingEnabled: snapshot.context.imageSmoothingEnabled,
     343                imageSmoothingQuality: snapshot.context.imageSmoothingQuality,
     344                lineCap: snapshot.context.lineCap,
     345                lineDash: snapshot.context.getLineDash(),
     346                lineDashOffset: snapshot.context.lineDashOffset,
     347                lineJoin: snapshot.context.lineJoin,
     348                lineWidth: snapshot.context.lineWidth,
     349                miterLimit: snapshot.context.miterLimit,
     350                shadowBlur: snapshot.context.shadowBlur,
     351                shadowColor: snapshot.context.shadowColor,
     352                shadowOffsetX: snapshot.context.shadowOffsetX,
     353                shadowOffsetY: snapshot.context.shadowOffsetY,
     354                strokeStyle: snapshot.context.strokeStyle,
     355                textAlign: snapshot.context.textAlign,
     356                textBaseline: snapshot.context.textBaseline,
     357                transform: snapshot.context.getTransform(),
     358                webkitImageSmoothingEnabled: snapshot.context.webkitImageSmoothingEnabled,
     359                webkitLineDash: snapshot.context.webkitLineDash,
     360                webkitLineDashOffset: snapshot.context.webkitLineDashOffset,
     361            };
     362
     363            if (WI.RecordingContentView.supportsCanvasPathDebugging())
     364                state.setPath = [snapshot.context.getPath()];
    308365
    309366            snapshot.context.restore();
    310367            while (saveCount-- > 0)
    311368                snapshot.context.restore();
     369
     370            return state;
    312371        };
    313372
     
    385444            }
    386445
    387             applyActions(startIndex, snapshot.index - 1, () => {
    388                 snapshot.state = {
    389                     direction: snapshot.context.direction,
    390                     fillStyle: snapshot.context.fillStyle,
    391                     font: snapshot.context.font,
    392                     globalAlpha: snapshot.context.globalAlpha,
    393                     globalCompositeOperation: snapshot.context.globalCompositeOperation,
    394                     imageSmoothingEnabled: snapshot.context.imageSmoothingEnabled,
    395                     imageSmoothingQuality: snapshot.context.imageSmoothingQuality,
    396                     lineCap: snapshot.context.lineCap,
    397                     lineDashOffset: snapshot.context.lineDashOffset,
    398                     lineJoin: snapshot.context.lineJoin,
    399                     lineWidth: snapshot.context.lineWidth,
    400                     miterLimit: snapshot.context.miterLimit,
    401                     setLineDash: [snapshot.context.getLineDash()],
    402                     setTransform: [snapshot.context.getTransform()],
    403                     shadowBlur: snapshot.context.shadowBlur,
    404                     shadowColor: snapshot.context.shadowColor,
    405                     shadowOffsetX: snapshot.context.shadowOffsetX,
    406                     shadowOffsetY: snapshot.context.shadowOffsetY,
    407                     strokeStyle: snapshot.context.strokeStyle,
    408                     textAlign: snapshot.context.textAlign,
    409                     textBaseline: snapshot.context.textBaseline,
    410                     webkitImageSmoothingEnabled: snapshot.context.webkitImageSmoothingEnabled,
    411                     webkitLineDash: snapshot.context.webkitLineDash,
    412                     webkitLineDashOffset: snapshot.context.webkitLineDashOffset,
    413                 };
    414 
    415                 if (WI.RecordingContentView.supportsCanvasPathDebugging())
    416                     snapshot.state.setPath = [snapshot.context.getPath()];
    417             });
     446            snapshot.state = applyActions(startIndex, snapshot.index - 1);
    418447
    419448            snapshot.content = new Image;
     
    431460        }
    432461
    433         applyActions(snapshot.index, this._index, () => {
    434             if (options.actionCompletedCallback)
    435                 options.actionCompletedCallback(actions[this._index], snapshot.context);
    436         });
     462        this._action = actions[this._index];
     463
     464        let state = applyActions(snapshot.index, this._index);
     465        console.assert(!this._action.state || Object.shallowEqual(this._action.state, state));
     466        if (!this._action.state)
     467            this._action.state = state;
     468
     469        this.dispatchEventToListeners(WI.ContentView.Event.SupplementalRepresentedObjectsDidChange);
    437470
    438471        this._previewContainer.appendChild(snapshot.element);
     
    440473    }
    441474
    442     async _generateContentCanvasWebGL(index, actions, options = {})
     475    async _generateContentCanvasWebGL(index, actions)
    443476    {
    444477        let imageLoad = (event) => {
     
    447480                return;
    448481
    449             this._generateContentCanvasWebGL(index, actions, options);
     482            this._generateContentCanvasWebGL(index, actions);
    450483        };
    451484
     
    482515        }
    483516
    484         if (options.actionCompletedCallback)
    485             options.actionCompletedCallback(actions[this._index]);
     517        this.dispatchEventToListeners(WI.ContentView.Event.SupplementalRepresentedObjectsDidChange);
    486518    }
    487519
     
    546578            index = this.representedObject.visualActionIndexes[visualActionIndex];
    547579
    548         this.dispatchEventToListeners(WI.RecordingContentView.Event.RecordingActionIndexChanged, {index});
     580        this.updateActionIndex(index);
    549581    }
    550582};
    551583
    552584WI.RecordingContentView.SnapshotInterval = 5000;
    553 
    554 WI.RecordingContentView.Event = {
    555     RecordingActionIndexChanged: "recording-action-index-changed",
    556 };
  • trunk/Source/WebInspectorUI/UserInterface/Views/RecordingStateDetailsSidebarPanel.js

    r224433 r228301  
    4444
    4545        this.recording = objects.find((object) => object instanceof WI.Recording && object.type === WI.Recording.Type.Canvas2D);
     46        this.action = objects.find((object) => object instanceof WI.RecordingAction);
    4647
    47         return !!this._recording;
     48        return this._recording && this._action;
    4849    }
    4950
     
    6061    }
    6162
    62     updateAction(action, context, options = {})
     63    set action(action)
    6364    {
     65        console.assert(!action || action instanceof WI.RecordingAction);
    6466        if (!this._recording || action === this._action)
    6567            return;
     
    6769        this._action = action;
    6870
    69         if (this._recording.type === WI.Recording.Type.Canvas2D)
    70             this._generateDetailsCanvas2D(action, context, options);
     71        if (this._action && this._recording.type === WI.Recording.Type.Canvas2D)
     72            this._generateDetailsCanvas2D(this._action);
    7173
    7274        this.updateLayoutIfNeeded();
     
    7577    // Private
    7678
    77     _generateDetailsCanvas2D(action, context, options = {})
     79    _generateDetailsCanvas2D(action)
    7880    {
    7981        if (!this._dataGrid) {
     
    8890        this._dataGrid.removeChildren();
    8991
    90         if (!context)
     92        console.assert(action.state);
     93        if (!action.state)
    9194            return;
    92 
    93         let state = {};
    94 
    95         if (WI.RecordingContentView.supportsCanvasPathDebugging()) {
    96             state.currentX = context.currentX;
    97             state.currentY = context.currentY;
    98         }
    99 
    100         state.direction = context.direction;
    101         state.fillStyle = context.fillStyle;
    102         state.font = context.font;
    103         state.globalAlpha = context.globalAlpha;
    104         state.globalCompositeOperation = context.globalCompositeOperation;
    105         state.imageSmoothingEnabled = context.imageSmoothingEnabled;
    106         state.imageSmoothingQuality = context.imageSmoothingQuality;
    107         state.lineCap = context.lineCap;
    108         state.lineDash = context.getLineDash();
    109         state.lineDashOffset = context.lineDashOffset;
    110         state.lineJoin = context.lineJoin;
    111         state.lineWidth = context.lineWidth;
    112         state.miterLimit = context.miterLimit;
    113         state.shadowBlur = context.shadowBlur;
    114         state.shadowColor = context.shadowColor;
    115         state.shadowOffsetX = context.shadowOffsetX;
    116         state.shadowOffsetY = context.shadowOffsetY;
    117         state.strokeStyle = context.strokeStyle;
    118         state.textAlign = context.textAlign;
    119         state.textBaseline = context.textBaseline;
    120         state.transform = context.getTransform();
    121         state.webkitImageSmoothingEnabled = context.webkitImageSmoothingEnabled;
    122         state.webkitLineDash = context.webkitLineDash;
    123         state.webkitLineDashOffset = context.webkitLineDashOffset;
    12495
    12596        function isColorProperty(name) {
     
    136107        }
    137108
    138         for (let name in state) {
    139             let value = state[name];
     109        for (let name in action.state) {
     110            // Skip internal state used for path debugging.
     111            if (name === "setPath")
     112                continue;
     113
     114            let value = action.state[name];
    140115            if (typeof value === "object") {
    141116                let isGradient = value instanceof CanvasGradient;
  • trunk/Source/WebInspectorUI/UserInterface/Views/RecordingTraceDetailsSidebarPanel.js

    r225261 r228301  
    4747
    4848        this.recording = objects.find((object) => object instanceof WI.Recording);
     49        this.action = objects.find((object) => object instanceof WI.RecordingAction);
    4950
    50         return !!this._recording;
     51        return this._recording && this._action;
    5152    }
    5253
     
    6263    }
    6364
    64     updateAction(action, context, options = {})
     65    set action(action)
    6566    {
     67        console.assert(!action || action instanceof WI.RecordingAction);
    6668        if (!this._recording || action === this._action)
    6769            return;
     
    7072
    7173        this.contentView.element.removeChildren();
     74
     75        if (!this._action)
     76            return;
    7277
    7378        let trace = this._action.trace;
  • trunk/Source/WebInspectorUI/UserInterface/Views/ResourceIcons.css

    r224975 r228301  
    7777}
    7878
    79 .canvas > .icon {
    80     content: url(../Images/Canvas.svg);
    81 }
    82 
    83 .shader-program .icon {
    84     content: image-set(url(../Images/DocumentGL.png) 1x, url(../Images/DocumentGL@2x.png) 2x);
    85 }
    86 
    8779.large .resource-icon .icon {
    8880    content: image-set(url(../Images/DocumentGenericLarge.png) 1x, url(../Images/DocumentGenericLarge@2x.png) 2x);
Note: See TracChangeset for help on using the changeset viewer.