Source/WebInspectorUI/ChangeLog

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

Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js

@@localizedStrings["Cancel comparison"] = "Cancel comparison";
168168localizedStrings["Canvas"] = "Canvas";
169169localizedStrings["Canvas %d"] = "Canvas %d";
170170localizedStrings["Canvas %s"] = "Canvas %s";
171 localizedStrings["Canvas Overview"] = "Canvas Overview";
172171localizedStrings["Canvases"] = "Canvases";
173172localizedStrings["Cap"] = "Cap";
174173localizedStrings["Caps"] = "Caps";

@@localizedStrings["Experimental"] = "Experimental";
402401localizedStrings["Expires"] = "Expires";
403402localizedStrings["Export"] = "Export";
404403localizedStrings["Export HAR"] = "Export HAR";
 404localizedStrings["Export recording (%s)"] = "Export recording (%s)";
405405localizedStrings["Expression"] = "Expression";
406406localizedStrings["Extension Scripts"] = "Extension Scripts";
407407localizedStrings["Extensions"] = "Extensions";

@@localizedStrings["Image Size"] = "Image Size";
496496localizedStrings["Images"] = "Images";
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";
501503localizedStrings["Indent width:"] = "Indent width:";

@@localizedStrings["Stalled"] = "Stalled";
905907localizedStrings["Start Time"] = "Start Time";
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";
911913localizedStrings["Status"] = "Status";

Source/WebInspectorUI/UserInterface/Images/Canvas2D.svg

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>

Source/WebInspectorUI/UserInterface/Images/Canvas3D.svg

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>

Source/WebInspectorUI/UserInterface/Images/Recording.svg

11<?xml version="1.0" encoding="utf-8"?>
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>

Source/WebInspectorUI/UserInterface/Main.html

4646 <link rel="stylesheet" href="Views/CanvasContentView.css">
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">
5152 <link rel="stylesheet" href="Views/CheckboxNavigationItem.css">

139140 <link rel="stylesheet" href="Views/ProbeDetailsSidebarPanel.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">
144146 <link rel="stylesheet" href="Views/RecordingActionTreeElement.css">
145147 <link rel="stylesheet" href="Views/RecordingContentView.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">
151152 <link rel="stylesheet" href="Views/Resizer.css">

560561 <script src="Views/CanvasContentView.js"></script>
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>
565567 <script src="Views/CheckboxNavigationItem.js"></script>

688690 <script src="Views/ProfileNodeDataGridNode.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>
693696 <script src="Views/RadioButtonNavigationItem.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>
699701 <script src="Views/RenderingFrameTimelineDataGridNode.js"></script>

Source/WebInspectorUI/UserInterface/Models/RecordingAction.js

@@WI.RecordingAction = class RecordingAction extends WI.Object
9191 get parameters() { return this._parameters; }
9292 get swizzleTypes() { return this._payloadSwizzleTypes; }
9393 get trace() { return this._trace; }
 94
9495 get snapshot() { return this._snapshot; }
 96 set snapshot(snapshot) { this._snapshot = snapshot; }
 97
9598 get valid() { return this._valid; }
9699 get isFunction() { return this._isFunction; }
97100 get isGetter() { return this._isGetter; }

Source/WebInspectorUI/UserInterface/Views/CanvasContentView.css

2525
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
4230.content-view.canvas:not(.tab) > .preview {

4836 padding: 15px;
4937}
5038
 39.content-view.canvas:not(.tab) {
 40 display: flex;
 41 flex-direction: column;
 42}
 43
5144.content-view.canvas:not(.tab) > .preview > img {
5245 max-width: 100%;
5346 max-height: 100%;

Source/WebInspectorUI/UserInterface/Views/CanvasContentView.js

@@WI.CanvasContentView = class CanvasContentView extends WI.ContentView
3333
3434 this.element.classList.add("canvas");
3535
36  this._recordingProgressElement = null;
 36 this._progressView = null;
3737 this._previewContainerElement = null;
3838 this._previewImageElement = null;
3939 this._errorElement = null;

@@WI.CanvasContentView = class CanvasContentView extends WI.ContentView
4545 this._recordingOptionElementMap = new WeakMap;
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);
5151 this._recordButtonNavigationItem.visibilityPriority = WI.NavigationItem.VisibilityPriority.High;

@@WI.CanvasContentView = class CanvasContentView extends WI.ContentView
6666
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
7574 refresh()

@@WI.CanvasContentView = class CanvasContentView extends WI.ContentView
156155
157156 if (!this._previewImageElement) {
158157 this._previewImageElement = document.createElement("img");
 158 this._previewImageElement.addEventListener("click", this._handlePreviewImageClick.bind(this));
159159 this._previewImageElement.addEventListener("error", this._showError.bind(this));
160160 }
161161

@@WI.CanvasContentView = class CanvasContentView extends WI.ContentView
178178 this.refresh();
179179
180180 this._updateRecordNavigationItem();
 181 this._updateProgressView();
181182 }
182183
183184 attached()

@@WI.CanvasContentView = class CanvasContentView extends WI.ContentView
262263 _recordingStarted(event)
263264 {
264265 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…");
 266 this._updateProgressView();
275267 }
276268
277269 _recordingProgress(event)

@@WI.CanvasContentView = class CanvasContentView extends WI.ContentView
280272 if (canvas !== this.representedObject)
281273 return;
282274
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) + ")";
 275 this._updateProgressView(frameCount, bufferUsed);
296276 }
297277
298278 _recordingStopped(event)

@@WI.CanvasContentView = class CanvasContentView extends WI.ContentView
303283 if (canvas !== this.representedObject)
304284 return;
305285
 286 this._updateProgressView();
 287
306288 if (recording)
307289 this._addRecording(recording);
 290 }
308291
309  if (this._recordingProgressElement) {
310  this._recordingProgressElement.remove();
311  this._recordingProgressElement = null;
312  }
 292 _handlePreviewImageClick(event)
 293 {
 294 WI.showRepresentedObject(this.representedObject);
313295 }
314296
315297 _handleRecordingSelectElementChange(event)

@@WI.CanvasContentView = class CanvasContentView extends WI.ContentView
388370 this._recordButtonNavigationItem.enabled = isRecording || !WI.canvasManager.recordingCanvas;
389371 this._recordButtonNavigationItem.toggled = isRecording;
390372
 373 this._refreshButtonNavigationItem.enabled = !isRecording;
 374
391375 this.element.classList.toggle("is-recording", isRecording);
392376 }
 377
 378 _updateProgressView(frameCount, bufferUsed)
 379 {
 380 if (!this.representedObject.isRecording) {
 381 if (this._progressView && this._progressView.parentView) {
 382 this.removeSubview(this._progressView);
 383 this._progressView = null;
 384 }
 385 return;
 386 }
 387
 388 if (!this._progressView) {
 389 this._progressView = new WI.ProgressView;
 390 this._progressView.element.addEventListener("click", this._handlePreviewImageClick.bind(this));
 391 this.element.insertBefore(this._progressView.element, this._previewContainerElement);
 392 this.addSubview(this._progressView);
 393 }
 394
 395 let title;
 396 if (frameCount) {
 397 let formatString = frameCount === 1 ? WI.UIString("%d Frame") : WI.UIString("%d Frames");
 398 title = formatString.format(frameCount);
 399 } else
 400 title = WI.UIString("Waiting for frames…")
 401
 402 this._progressView.title = title;
 403 this._progressView.subtitle = bufferUsed ? Number.bytesToString(bufferUsed) : "";
 404 }
393405};

Source/WebInspectorUI/UserInterface/Views/CanvasDetailsSidebarPanel.js

@@WI.CanvasDetailsSidebarPanel = class CanvasDetailsSidebarPanel extends WI.Detail
5555
5656 this.canvas = objects.find((object) => object instanceof WI.Canvas);
5757
58  return true;
 58 return !!this.canvas;
5959 }
6060
6161 get canvas()

Source/WebInspectorUI/UserInterface/Views/CanvasOverviewContentView.css

3838 background-color: white;
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);
4343}
4444
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);
 45.content-view.canvas-overview .content-view.canvas.is-recording {
 46 border-color: red;
4847}
4948
5049.content-view.canvas-overview .content-view.canvas > :matches(header, footer) {

5958
6059.content-view.canvas-overview .content-view.canvas > header {
6160 font-size: 13px;
62  border-bottom: none;
6361}
6462
6563.content-view.canvas-overview .content-view.canvas.is-recording > header {
6664 background-color: red;
67  border-color: red;
6865}
6966
7067.content-view.canvas-overview .content-view.canvas > header > .titles,

10097.content-view.canvas-overview .content-view.canvas > header > .navigation-bar {
10198 align-items: initial;
10299 border: none;
 100 opacity: 0;
 101 transition: opacity 200ms ease-in-out;
103102}
104103
105 .content-view.canvas-overview .content-view.canvas:not(:hover, .is-recording, .selected) > header > .navigation-bar {
106  visibility: hidden;
 104.content-view.canvas-overview .content-view.canvas:matches(:hover, .is-recording) > header > .navigation-bar {
 105 opacity: 1;
 106 transition: opacity 200ms ease-in-out;
107107}
108108
109109.content-view.canvas-overview .content-view.canvas:not(.is-recording) > header > .navigation-bar > .item.record-start-stop.disabled {

119119 filter: brightness(80%);
120120}
121121
122 .content-view.canvas-overview .content-view.canvas > :matches(.progress, .preview) {
123  border-top: none;
124  border-bottom: none;
 122.content-view.canvas-overview .content-view.canvas.is-recording > .progress-view {
 123 cursor: pointer;
125124}
126125
 126.content-view.canvas-overview .content-view.canvas.is-recording > .progress-view,
127127.content-view.canvas-overview .content-view.canvas > .preview {
128  height: var(--preview-height);
129 
130  --preview-height: 280px;
 128 height: 280px;
131129}
132130
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)));
 131.content-view.canvas-overview .content-view.canvas.is-recording > .preview {
 132 display: none;
137133}
138134
139135.content-view.canvas-overview .content-view.canvas > .preview > img {
140136 border-radius: 4px;
141137 box-shadow: 1px 2px 6px rgba(0, 0, 0, 0.58);
 138 cursor: pointer;
142139}
143140
144141.content-view.canvas-overview .content-view.canvas > .preview > .message-text-view {

Source/WebInspectorUI/UserInterface/Views/CanvasOverviewContentView.js

@@WI.CanvasOverviewContentView = class CanvasOverviewContentView extends WI.Collec
4545
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;
5055 this._refreshButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._refreshPreviews, this);

@@WI.CanvasOverviewContentView = class CanvasOverviewContentView extends WI.Collec
5358 this._showGridButtonNavigationItem.activated = !!WI.settings.showImageGrid.value;
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
7863 // Public
7964
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
10670 hidden()

@@WI.CanvasOverviewContentView = class CanvasOverviewContentView extends WI.Collec
13397 super.attached();
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
143102 detached()
144103 {
145104 WI.settings.showImageGrid.removeEventListener(null, null, this);
146105
147  this.removeEventListener(null, null, this);
148 
149  for (let shortcut of this._keyboardShortcuts)
150  shortcut.disabled = true;
151 
152106 super.detached();
153107 }
154108

@@WI.CanvasOverviewContentView = class CanvasOverviewContentView extends WI.Collec
165119 canvasContentView.refresh();
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 {
207124 let hasItems = !!this.representedObject.items.size;

@@WI.CanvasOverviewContentView = class CanvasOverviewContentView extends WI.Collec
209126 this._showGridButtonNavigationItem.enabled = hasItems;
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 {
223131 WI.settings.showImageGrid.value = !this._showGridButtonNavigationItem.activated;
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;
269137 }
270138
271  _supplementalRepresentedObjectsDidChange()
272  {
273  this.dispatchEventToListeners(WI.ContentView.Event.SelectionPathComponentsDidChange);
274  }
275 
276139 _contentViewMouseEnter(event)
277140 {
278141 let contentView = WI.View.fromElement(event.target);

Source/WebInspectorUI/UserInterface/Views/CanvasSidebarPanel.css

 1/*
 2 * Copyright (C) 2018 Apple Inc. All rights reserved.
 3 *
 4 * Redistribution and use in source and binary forms, with or without
 5 * modification, are permitted provided that the following conditions
 6 * are met:
 7 * 1. Redistributions of source code must retain the above copyright
 8 * notice, this list of conditions and the following disclaimer.
 9 * 2. Redistributions in binary form must reproduce the above copyright
 10 * notice, this list of conditions and the following disclaimer in the
 11 * documentation and/or other materials provided with the distribution.
 12 *
 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 23 * THE POSSIBILITY OF SUCH DAMAGE.
 24 */
 25
 26.sidebar > .panel.navigation.canvas > .content {
 27 top: var(--navigation-bar-height);
 28}
 29
 30.sidebar > .panel.navigation.canvas > .navigation-bar > .item.record-start-stop.disabled {
 31 filter: grayscale();
 32 opacity: 0.5;
 33}
 34
 35.sidebar > .panel.navigation.canvas > .content > .tree-outline .item.canvas.canvas-2d .icon {
 36 content: url(../Images/Canvas2D.svg);
 37}
 38
 39.sidebar > .panel.navigation.canvas > .content > .tree-outline .item.canvas.webgl .icon {
 40 content: url(../Images/Canvas3D.svg);
 41}
 42
 43.sidebar > .panel.navigation.canvas > .content > .navigation-bar {
 44 border-top: 1px solid var(--border-color);
 45}
 46
 47.sidebar > .panel.navigation.canvas.has-recordings > .content > .tree-outline.canvas {
 48 max-height: 50%;
 49 overflow-y: scroll;
 50}
 51
 52.sidebar > .panel.navigation.canvas:not(.has-recordings) > .filter-bar,
 53.sidebar > .panel.navigation.canvas:not(.has-recordings) > .content > .navigation-bar {
 54 display: none;
 55}
 56
 57.sidebar > .panel.navigation.canvas > .content > .tree-outline .item.recording > .icon {
 58 content: url(../Images/Recording.svg);
 59}
 60
 61.sidebar > .panel.navigation.canvas > .content > .tree-outline .item.shader-program > .icon {
 62 content: image-set(url(../Images/DocumentGL.png) 1x, url(../Images/DocumentGL@2x.png) 2x);
 63}
 64
 65.sidebar > .panel.navigation.canvas > .content > .tree-outline .item.folder-icon > .icon {
 66 content: url(../Images/RenderingFrame.svg);
 67}
 68
 69.sidebar > .panel.navigation.canvas > .content > .tree-outline .item.folder-icon > .status {
 70 line-height: 16px;
 71}

Source/WebInspectorUI/UserInterface/Views/CanvasSidebarPanel.js

 1/*
 2 * Copyright (C) 2018 Apple Inc. All rights reserved.
 3 *
 4 * Redistribution and use in source and binary forms, with or without
 5 * modification, are permitted provided that the following conditions
 6 * are met:
 7 * 1. Redistributions of source code must retain the above copyright
 8 * notice, this list of conditions and the following disclaimer.
 9 * 2. Redistributions in binary form must reproduce the above copyright
 10 * notice, this list of conditions and the following disclaimer in the
 11 * documentation and/or other materials provided with the distribution.
 12 *
 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 23 * THE POSSIBILITY OF SUCH DAMAGE.
 24 */
 25
 26WI.CanvasSidebarPanel = class CanvasSidebarPanel extends WI.NavigationSidebarPanel
 27{
 28 constructor()
 29 {
 30 super("canvas", WI.UIString("Canvas"));
 31
 32 this._canvas = null;
 33 this._recording = null;
 34
 35 this._navigationBar = new WI.NavigationBar;
 36 this._scopeBar = null;
 37
 38 const toolTip = WI.UIString("Start recording canvas actions.\nShift-click to record a single frame.");
 39 const altToolTip = WI.UIString("Stop recording canvas actions");
 40 this._recordButtonNavigationItem = new WI.ToggleButtonNavigationItem("record-start-stop", toolTip, altToolTip, "Images/Record.svg", "Images/Stop.svg", 13, 13);
 41 this._recordButtonNavigationItem.enabled = false;
 42 this._recordButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._toggleRecording, this);
 43 this._navigationBar.addNavigationItem(this._recordButtonNavigationItem);
 44
 45 this.addSubview(this._navigationBar);
 46
 47 const suppressFiltering = true;
 48 this._canvasTreeOutline = this.createContentTreeOutline(suppressFiltering);
 49 this._canvasTreeOutline.element.classList.add("canvas");
 50
 51 this._recordingNavigationBar = new WI.NavigationBar;
 52 this.contentView.addSubview(this._recordingNavigationBar);
 53
 54 this._recordingTreeOutline = this.contentTreeOutline;
 55 this.contentView.element.appendChild(this._recordingTreeOutline.element);
 56
 57 this._canvasTreeOutline.addEventListener(WI.TreeOutline.Event.SelectionDidChange, this._treeOutlineSelectionDidChange, this);
 58 this._recordingTreeOutline.addEventListener(WI.TreeOutline.Event.SelectionDidChange, this._treeOutlineSelectionDidChange, this);
 59
 60 WI.canvasManager.addEventListener(WI.CanvasManager.Event.RecordingStarted, this._updateRecordNavigationItem, this);
 61 WI.canvasManager.addEventListener(WI.CanvasManager.Event.RecordingStopped, this._updateRecordNavigationItem, this);
 62 }
 63
 64 // Public
 65
 66 get canvas()
 67 {
 68 return this._canvas;
 69 }
 70
 71 set canvas(canvas)
 72 {
 73 if (this._canvas === canvas)
 74 return;
 75
 76 if (this._canvas)
 77 this._canvas.recordingCollection.removeEventListener(null, null, this);
 78
 79 this._canvas = canvas;
 80 if (this._canvas) {
 81 this._canvas.recordingCollection.addEventListener(WI.Collection.Event.ItemAdded, this._recordingAdded, this);
 82 this._canvas.recordingCollection.addEventListener(WI.Collection.Event.ItemRemoved, this._recordingRemoved, this);
 83 }
 84
 85 this._canvasChanged();
 86 this._updateRecordNavigationItem();
 87 this._updateRecordingScopeBar();
 88 }
 89
 90 set recording(recording)
 91 {
 92 if (recording === this._recording)
 93 return;
 94
 95 if (recording)
 96 this.canvas = recording.source;
 97
 98 this._recording = recording;
 99 this._recordingChanged();
 100 }
 101
 102 set action(action)
 103 {
 104 if (!this._recording)
 105 return;
 106
 107 let treeElement = this._recordingTreeOutline.findTreeElement(action);
 108 console.assert(treeElement, "Missing tree element for recording action.", action);
 109 if (!treeElement)
 110 return;
 111
 112 this._recording[WI.CanvasSidebarPanel.SelectedActionSymbol] = action;
 113
 114 const omitFocus = false;
 115 const selectedByUser = false;
 116 treeElement.revealAndSelect(omitFocus, selectedByUser);
 117 }
 118
 119 shown()
 120 {
 121 super.shown();
 122
 123 this.contentBrowser.addEventListener(WI.ContentBrowser.Event.CurrentRepresentedObjectsDidChange, this._currentRepresentedObjectsDidChange, this);
 124 }
 125
 126 hidden()
 127 {
 128 this.contentBrowser.removeEventListener(null, null, this);
 129
 130 super.hidden();
 131 }
 132
 133 // Protected
 134
 135 hasCustomFilters()
 136 {
 137 return true;
 138 }
 139
 140 matchTreeElementAgainstCustomFilters(treeElement)
 141 {
 142 // Keep recording frame tree elements.
 143 if (treeElement instanceof WI.FolderTreeElement)
 144 return true;
 145
 146 // Always show the Initial State tree element.
 147 if (treeElement instanceof WI.RecordingActionTreeElement && treeElement.representedObject instanceof WI.RecordingInitialStateAction)
 148 return true;
 149
 150 return super.matchTreeElementAgainstCustomFilters(treeElement);
 151 }
 152
 153 initialLayout()
 154 {
 155 super.initialLayout();
 156
 157 let filterFunction = (treeElement) => {
 158 if (!(treeElement.representedObject instanceof WI.RecordingAction))
 159 return false;
 160
 161 return treeElement.representedObject.isVisual || treeElement.representedObject instanceof WI.RecordingInitialStateAction;
 162 };
 163
 164 const activatedByDefault = false;
 165 const defaultToolTip = WI.UIString("Only show visual actions");
 166 const activatedToolTip = WI.UIString("Show all actions");
 167 this.filterBar.addFilterBarButton("recording-show-visual-only", filterFunction, activatedByDefault, defaultToolTip, activatedToolTip, "Images/Paint.svg", 15, 15);
 168 }
 169
 170 // Private
 171
 172 _recordingAdded(event)
 173 {
 174 this.recording = event.data.item;
 175
 176 this._updateRecordNavigationItem();
 177 this._updateRecordingScopeBar();
 178 }
 179
 180 _recordingRemoved(event)
 181 {
 182 let recording = event.data.item;
 183 if (recording === this.recording)
 184 this.recording = this._canvas ? this._canvas.recordingCollection.toArray().lastValue : null;
 185
 186 this._updateRecordingScopeBar();
 187 }
 188
 189 _scopeBarSelectionChanged()
 190 {
 191 let selectedScopeBarItem = this._scopeBar.selectedItems[0];
 192 this.recording = selectedScopeBarItem.__recording || null;
 193 }
 194
 195 _toggleRecording(event)
 196 {
 197 if (!this._canvas)
 198 return;
 199
 200 if (this._canvas.isRecording)
 201 WI.canvasManager.stopRecording();
 202 else if (!WI.canvasManager.recordingCanvas) {
 203 let singleFrame = event.data.nativeEvent.shiftKey;
 204 WI.canvasManager.startRecording(this._canvas, singleFrame);
 205 }
 206 }
 207
 208 _currentRepresentedObjectsDidChange(event)
 209 {
 210 let objects = this.contentBrowser.currentRepresentedObjects;
 211
 212 let canvas = objects.find((object) => object instanceof WI.Canvas);
 213 if (canvas) {
 214 this.canvas = canvas;
 215 return;
 216 }
 217
 218 let shaderProgram = objects.find((object) => object instanceof WI.ShaderProgram);
 219 if (shaderProgram) {
 220 this.canvas = shaderProgram.canvas;
 221 let treeElement = this._canvasTreeOutline.findTreeElement(shaderProgram);
 222 const omitFocus = false;
 223 const selectedByUser = false;
 224 treeElement.revealAndSelect(omitFocus, selectedByUser);
 225 return;
 226 }
 227
 228 let recording = objects.find((object) => object instanceof WI.Recording);
 229 if (recording) {
 230 this.canvas = recording.source;
 231 this.recording = recording;
 232 this.action = objects.find((object) => object instanceof WI.RecordingAction);
 233 return;
 234 }
 235
 236 this.canvas = null;
 237 }
 238
 239 _treeOutlineSelectionDidChange(event)
 240 {
 241 let treeElement = event.data.selectedElement;
 242 if (!treeElement)
 243 return;
 244
 245 if ((treeElement instanceof WI.CanvasTreeElement) || (treeElement instanceof WI.ShaderProgramTreeElement)) {
 246 this.showDefaultContentViewForTreeElement(treeElement);
 247 return;
 248 }
 249
 250 if (treeElement instanceof WI.FolderTreeElement)
 251 treeElement = treeElement.children.lastValue;
 252
 253 if (!(treeElement instanceof WI.RecordingActionTreeElement))
 254 return;
 255
 256 console.assert(this._recording, "Missing recording for action tree element.", treeElement);
 257 this._recording[WI.CanvasSidebarPanel.SelectedActionSymbol] = treeElement.representedObject;
 258
 259 let recordingContentView = this.contentBrowser.showContentViewForRepresentedObject(this._recording);
 260 if (recordingContentView)
 261 recordingContentView.updateActionIndex(treeElement.index);
 262 }
 263
 264 _canvasChanged()
 265 {
 266 this._canvasTreeOutline.removeChildren();
 267
 268 if (!this._canvas)
 269 return;
 270
 271 const showRecordings = false;
 272 let canvasTreeElement = new WI.CanvasTreeElement(this._canvas, showRecordings);
 273 canvasTreeElement.expanded = true;
 274 this._canvasTreeOutline.appendChild(canvasTreeElement);
 275
 276 const omitFocus = false;
 277 const selectedByUser = false;
 278 canvasTreeElement.revealAndSelect(omitFocus, selectedByUser);
 279
 280 if (WI.Canvas.ContextType.Canvas2D || this._canvas.contextType === WI.Canvas.ContextType.WebGL)
 281 this._recordButtonNavigationItem.enabled = true;
 282
 283 let defaultSelectedRecording = null;
 284 if (this._canvas.recordingCollection.items.size)
 285 defaultSelectedRecording = this._canvas.recordingCollection.toArray().lastValue;
 286
 287 this.recording = defaultSelectedRecording;
 288 }
 289
 290 _recordingChanged()
 291 {
 292 this._recordingTreeOutline.removeChildren();
 293
 294 if (!this._recording)
 295 return;
 296
 297 let recording = this._recording;
 298
 299 this._recording.actions.then((actions) => {
 300 if (recording !== this._recording)
 301 return;
 302
 303 this._recordingTreeOutline.element.dataset.indent = Number.countDigits(actions.length);
 304
 305 if (actions[0] instanceof WI.RecordingInitialStateAction)
 306 this._recordingTreeOutline.appendChild(new WI.RecordingActionTreeElement(actions[0], 0, this._recording.type));
 307
 308 let cumulativeActionIndex = 1;
 309 this._recording.frames.forEach((frame, frameIndex) => {
 310 let folder = new WI.FolderTreeElement(WI.UIString("Frame %d").format((frameIndex + 1).toLocaleString()));
 311 this._recordingTreeOutline.appendChild(folder);
 312
 313 for (let i = 0; i < frame.actions.length; ++i)
 314 folder.appendChild(new WI.RecordingActionTreeElement(frame.actions[i], cumulativeActionIndex + i, this._recording.type));
 315
 316 if (!isNaN(frame.duration)) {
 317 const higherResolution = true;
 318 folder.status = Number.secondsToString(frame.duration / 1000, higherResolution);
 319 }
 320
 321 if (frame.incomplete)
 322 folder.subtitle = WI.UIString("Incomplete");
 323
 324 if (this._recording.frames.length === 1)
 325 folder.expand();
 326
 327 cumulativeActionIndex += frame.actions.length;
 328 });
 329
 330 if (this._scopeBar) {
 331 let scopeBarItem = this._scopeBar.item(this._recording.displayName);
 332 console.assert(scopeBarItem, "Missing scopeBarItem for recording.", this._recording);
 333 scopeBarItem.selected = true;
 334 }
 335
 336 this.action = this._recording[WI.CanvasSidebarPanel.SelectedActionSymbol] || null;
 337 });
 338 }
 339
 340 _updateRecordNavigationItem()
 341 {
 342 if (!this._canvas || !(this._canvas.contextType === WI.Canvas.ContextType.Canvas2D || this._canvas.contextType === WI.Canvas.ContextType.WebGL)) {
 343 this._recordButtonNavigationItem.enabled = false;
 344 return;
 345 }
 346
 347 let isRecording = this._canvas.isRecording;
 348 this._recordButtonNavigationItem.enabled = isRecording || !WI.canvasManager.recordingCanvas;
 349 this._recordButtonNavigationItem.toggled = isRecording;
 350 }
 351
 352 _updateRecordingScopeBar()
 353 {
 354 if (this._scopeBar) {
 355 this._recordingNavigationBar.removeNavigationItem(this._scopeBar);
 356 this._scopeBar = null;
 357 }
 358
 359 if (!this._canvas || !this._canvas.recordingCollection.items.size) {
 360 this.element.classList.remove("has-recordings");
 361 return;
 362 }
 363
 364 let scopeBarItems = [];
 365 let selectedScopeBarItem = null;
 366 for (let recording of this._canvas.recordingCollection.items) {
 367 let scopeBarItem = new WI.ScopeBarItem(recording.displayName, recording.displayName);
 368 if (recording === this._recording)
 369 selectedScopeBarItem = scopeBarItem;
 370 scopeBarItem.__recording = recording;
 371 scopeBarItems.push(scopeBarItem);
 372 }
 373
 374 if (!selectedScopeBarItem)
 375 selectedScopeBarItem = scopeBarItems[0];
 376
 377 this._scopeBar = new WI.ScopeBar("canvas-recordinga-scope-bar", scopeBarItems, selectedScopeBarItem, true);
 378 this._scopeBar.addEventListener(WI.ScopeBar.Event.SelectionChanged, this._scopeBarSelectionChanged, this);
 379 this._recordingNavigationBar.insertNavigationItem(this._scopeBar, 0);
 380
 381 this.element.classList.add("has-recordings");
 382 }
 383};
 384
 385WI.CanvasSidebarPanel.SelectedActionSymbol = Symbol("selected-action");

Source/WebInspectorUI/UserInterface/Views/CanvasTabContentView.css

2323 * THE POSSIBILITY OF SUCH DAMAGE.
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
3538.content-view.tab.canvas .navigation-bar > .item .recording > .icon {
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}

Source/WebInspectorUI/UserInterface/Views/CanvasTabContentView.js

@@WI.CanvasTabContentView = class CanvasTabContentView extends WI.ContentBrowserTa
3131
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;
3737 super("canvas", ["canvas"], tabBarItem, navigationSidebarPanelConstructor, detailsSidebarPanelConstructors, disableBackForward);

@@WI.CanvasTabContentView = class CanvasTabContentView extends WI.ContentBrowserTa
4141 this._canvasTreeOutline = new WI.TreeOutline;
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); });
 44 this._overviewTreeElement = new WI.GeneralTreeElement("canvas-overview", WI.UIString("Overview"), null, this._canvasCollection);
 45 this._canvasTreeOutline.appendChild(this._overviewTreeElement);
5246
53  this.contentBrowser.navigationBar.insertNavigationItem(this._overviewNavigationItem, 2);
54  this.contentBrowser.navigationBar.insertNavigationItem(new WI.DividerNavigationItem, 3);
 47 this._importedRecordingsTreeElement = new WI.FolderTreeElement(WI.UIString("Imported Recordings"));
 48 this._importedRecordingsTreeElement.hidden = true;
 49 this._overviewTreeElement.appendChild(this._importedRecordingsTreeElement);
5550
56  this.navigationSidebarPanel.contentTreeOutline.addEventListener(WI.TreeOutline.Event.SelectionDidChange, this._navigationSidebarTreeOutlineSelectionChanged, this);
 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
5958 static tabInfo()

@@WI.CanvasTabContentView = class CanvasTabContentView extends WI.ContentBrowserTa
8887
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;
9494 }
9595
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.");
114  }
115 
11696 shown()
11797 {
11898 super.shown();

@@WI.CanvasTabContentView = class CanvasTabContentView extends WI.ContentBrowserTa
141121 WI.canvasManager.addEventListener(WI.CanvasManager.Event.CanvasRemoved, this._handleCanvasRemoved, this);
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));
147126

@@WI.CanvasTabContentView = class CanvasTabContentView extends WI.ContentBrowserTa
168147
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
174153 for (let recording of canvas.recordingCollection.items)

@@WI.CanvasTabContentView = class CanvasTabContentView extends WI.ContentBrowserTa
177156
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
195166 let currentContentView = this.contentBrowser.currentContentView;

@@WI.CanvasTabContentView = class CanvasTabContentView extends WI.ContentBrowserTa
214185 return;
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);
 188 if (!this.canShowRepresentedObject(representedObject)) {
 189 console.assert(false, "Unexpected representedObject.", representedObject);
229190 return;
230191 }
231192
232  console.assert(false, "Unexpected representedObject.", representedObject);
 193 this.showRepresentedObject(representedObject);
233194 }
234195
235196 _recordingImportedOrStopped(event)

@@WI.CanvasTabContentView = class CanvasTabContentView extends WI.ContentBrowserTa
243204 });
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 {
264209 if (!recording.source) {
265210 const subtitle = null;
266211 let recordingTreeElement = new WI.GeneralTreeElement(["recording"], recording.displayName, subtitle, recording);
267  this._canvasTreeOutline.appendChild(recordingTreeElement);
 212 this._importedRecordingsTreeElement.hidden = false;
 213 this._importedRecordingsTreeElement.appendChild(recordingTreeElement);
268214 }
269215
270  if (!options.suppressShowRecording) {
 216 if (!options.suppressShowRecording)
271217 this.showRepresentedObject(recording);
272  this._updateActionIndex(0, {suppressNavigationSidebarUpdate: true});
273  }
274218 }
275219
276  _recordingActionIndexChanged(event)
 220 _handleSpace(event)
277221 {
278  if (event.target !== this.contentBrowser.currentContentView)
 222 if (WI.isEventTargetAnEditableField(event))
279223 return;
280224
281  this._updateActionIndex(event.data.index);
282  }
 225 if (!this.navigationSidebarPanel)
 226 return;
283227
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  };
 228 let canvas = this.navigationSidebarPanel.canvas;
 229 if (!canvas)
 230 return;
292231
293  if (!options.suppressNavigationSidebarUpdate)
294  this.navigationSidebarPanel.updateActionIndex(index, options);
 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 }
295238
296  this.contentBrowser.currentContentView.updateActionIndex(index, options);
 239 event.preventDefault();
297240 }
298241};
299242

Source/WebInspectorUI/UserInterface/Views/CanvasTreeElement.js

2525
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
4355 // Protected

@@WI.CanvasTreeElement = class CanvasTreeElement extends WI.FolderizedTreeElement
4658 {
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();
5965 }
6066
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();
70  }
71 
7267 onpopulate()
7368 {
7469 super.onpopulate();

@@WI.CanvasTreeElement = class CanvasTreeElement extends WI.FolderizedTreeElement
8378 for (let program of this.representedObject.shaderProgramCollection.items)
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
9087 populateContextMenu(contextMenu, event)

@@WI.CanvasTreeElement = class CanvasTreeElement extends WI.FolderizedTreeElement
137134 {
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};

Source/WebInspectorUI/UserInterface/Views/CollectionContentView.js

@@WI.CollectionContentView = class CollectionContentView extends WI.ContentView
5151 return [];
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 {
5672 return this._selectionEnabled;

Source/WebInspectorUI/UserInterface/Views/ContentView.js

@@WI.ContentView = class ContentView extends WI.View
255255 return true;
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;
260262 if (representedObject instanceof WI.TimelineRecording)

Source/WebInspectorUI/UserInterface/Views/ProgressView.css

 1/*
 2 * Copyright (C) 2018 Apple Inc. All rights reserved.
 3 *
 4 * Redistribution and use in source and binary forms, with or without
 5 * modification, are permitted provided that the following conditions
 6 * are met:
 7 * 1. Redistributions of source code must retain the above copyright
 8 * notice, this list of conditions and the following disclaimer.
 9 * 2. Redistributions in binary form must reproduce the above copyright
 10 * notice, this list of conditions and the following disclaimer in the
 11 * documentation and/or other materials provided with the distribution.
 12 *
 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 23 * THE POSSIBILITY OF SUCH DAMAGE.
 24 */
 25
 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;
 33}
 34
 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 {
 44 color: var(--text-color-gray-medium);
 45}
 46
 47.progress-view > .titles > .subtitle::before {
 48 content: "\2002\2012\2002";
 49}
 50
 51.progress-view > .indeterminate-progress-spinner {
 52 flex-shrink: 0;
 53 width: 24px;
 54 height: 24px;
 55 margin: 0 auto;
 56}

Source/WebInspectorUI/UserInterface/Views/ProgressView.js

 1/*
 2 * Copyright (C) 2018 Apple Inc. All rights reserved.
 3 *
 4 * Redistribution and use in source and binary forms, with or without
 5 * modification, are permitted provided that the following conditions
 6 * are met:
 7 * 1. Redistributions of source code must retain the above copyright
 8 * notice, this list of conditions and the following disclaimer.
 9 * 2. Redistributions in binary form must reproduce the above copyright
 10 * notice, this list of conditions and the following disclaimer in the
 11 * documentation and/or other materials provided with the distribution.
 12 *
 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 23 * THE POSSIBILITY OF SUCH DAMAGE.
 24 */
 25
 26WI.ProgressView = class ProgressView extends WI.View
 27{
 28 constructor(title, subtitle)
 29 {
 30 super();
 31
 32 this.element.classList.add("progress-view");
 33
 34 this._title = title || "";
 35 this._subtitle = subtitle || "";
 36
 37 this._titlesElement = null;
 38 this._titleElement = null;
 39 this._subtitleElement = null;
 40 }
 41
 42 // Public
 43
 44 get title()
 45 {
 46 return this._title;
 47 }
 48
 49 set title(title)
 50 {
 51 title = title || "";
 52 if (this._title === title)
 53 return;
 54
 55 this._title = title;
 56 this._updateTitles();
 57 }
 58
 59 get subtitle()
 60 {
 61 return this._subtitle;
 62 }
 63
 64 set subtitle(subtitle)
 65 {
 66 subtitle = subtitle || "";
 67 if (this._subtitle === subtitle)
 68 return;
 69
 70 this._subtitle = subtitle;
 71 this._updateTitles();
 72 }
 73
 74 get visible() { return this._visible; }
 75
 76 set visible(x)
 77 {
 78 if (this._visible === x)
 79 return;
 80
 81 // FIXME: remove once <https://webkit.org/b/150741> is fixed.
 82 this._visible = x;
 83 this.element.classList.toggle("hidden", !this._visible);
 84 }
 85
 86 // Protected
 87
 88 initialLayout()
 89 {
 90 super.initialLayout();
 91
 92 this._titlesElement = this.element.appendChild(document.createElement("div"));
 93 this._titlesElement.className = "titles";
 94 this._titleElement = this._titlesElement.appendChild(document.createElement("span"));
 95 this._titleElement.className = "title";
 96 this._subtitleElement = this._titlesElement.appendChild(document.createElement("span"));
 97 this._subtitleElement.className = "subtitle";
 98
 99 let spinner = new WI.IndeterminateProgressSpinner;
 100 this.element.appendChild(spinner.element);
 101
 102 this._updateTitles();
 103 }
 104
 105 _updateTitles()
 106 {
 107 if (!this._titlesElement)
 108 return;
 109
 110 this._titleElement.textContent = this._title;
 111 this._titleElement.classList.toggle("hidden", !this._title);
 112
 113 this._subtitleElement.textContent = this._subtitle;
 114 this._subtitleElement.classList.toggle("hidden", !this._subtitle);
 115 }
 116};

Source/WebInspectorUI/UserInterface/Views/RecordingContentView.css

6767 flex: 1;
6868 justify-content: center;
6969 align-items: center;
70  position: relative;
71  width: 100%;
72  height: 100%;
7370}
7471
7572.content-view:not(.tab).recording :matches(img, canvas) {

Source/WebInspectorUI/UserInterface/Views/RecordingContentView.js

@@WI.RecordingContentView = class RecordingContentView extends WI.ContentView
3232 super(representedObject);
3333
3434 this._index = NaN;
 35 this._action = null;
3536 this._snapshots = [];
3637 this._initialContent = null;
3738 this._throttler = this.throttle(200);

@@WI.RecordingContentView = class RecordingContentView extends WI.ContentView
5354 this._showGridButtonNavigationItem.visibilityPriority = WI.NavigationItem.VisibilityPriority.Low;
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 }
5865

@@WI.RecordingContentView = class RecordingContentView extends WI.ContentView
8895 {
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 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] : [];
98112 }
99113
100  updateActionIndex(index, options = {})
 114 updateActionIndex(index)
101115 {
102116 if (!this.representedObject)
103117 return;

@@WI.RecordingContentView = class RecordingContentView extends WI.ContentView
114128 this._updateSliderValue();
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 }
122136

@@WI.RecordingContentView = class RecordingContentView extends WI.ContentView
150164
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
164170 // Protected

@@WI.RecordingContentView = class RecordingContentView extends WI.ContentView
191197
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) => {
197220 // Loading took too long and the current action index has already changed.
198221 if (index !== this._index)
199222 return;
200223
201  this._generateContentCanvas2D(index, actions, options);
 224 this._generateContentCanvas2D(index, actions);
202225 };
203226
204227 let initialState = this.representedObject.initialState;

@@WI.RecordingContentView = class RecordingContentView extends WI.ContentView
431454 }
432455
433456 applyActions(snapshot.index, this._index, () => {
434  if (options.actionCompletedCallback)
435  options.actionCompletedCallback(actions[this._index], snapshot.context);
 457 this._action = actions[this._index];
 458 this._action.snapshot = snapshot;
 459
 460 this.dispatchEventToListeners(WI.ContentView.Event.SupplementalRepresentedObjectsDidChange);
436461 });
437462
438463 this._previewContainer.appendChild(snapshot.element);
439464 this._updateImageGrid();
440465 }
441466
442  async _generateContentCanvasWebGL(index, actions, options = {})
 467 async _generateContentCanvasWebGL(index, actions)
443468 {
444469 let imageLoad = (event) => {
445470 // Loading took too long and the current action index has already changed.
446471 if (index !== this._index)
447472 return;
448473
449  this._generateContentCanvasWebGL(index, actions, options);
 474 this._generateContentCanvasWebGL(index, actions);
450475 };
451476
452477 let initialState = this.representedObject.initialState;

@@WI.RecordingContentView = class RecordingContentView extends WI.ContentView
481506 this._updateImageGrid();
482507 }
483508
484  if (options.actionCompletedCallback)
485  options.actionCompletedCallback(actions[this._index]);
 509 this.dispatchEventToListeners(WI.ContentView.Event.SupplementalRepresentedObjectsDidChange);
486510 }
487511
488512 _updateCanvasPath()

@@WI.RecordingContentView = class RecordingContentView extends WI.ContentView
545569 if (visualActionIndex !== -1)
546570 index = this.representedObject.visualActionIndexes[visualActionIndex];
547571
548  this.dispatchEventToListeners(WI.RecordingContentView.Event.RecordingActionIndexChanged, {index});
 572 this.updateActionIndex(index);
549573 }
550574};
551575
552576WI.RecordingContentView.SnapshotInterval = 5000;
553 
554 WI.RecordingContentView.Event = {
555  RecordingActionIndexChanged: "recording-action-index-changed",
556 };

Source/WebInspectorUI/UserInterface/Views/RecordingStateDetailsSidebarPanel.js

@@WI.RecordingStateDetailsSidebarPanel = class RecordingStateDetailsSidebarPanel e
4343 objects = [objects];
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
5051 set recording(recording)

@@WI.RecordingStateDetailsSidebarPanel = class RecordingStateDetailsSidebarPanel e
5960 this.contentView.removeSubview(subview);
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;
6668
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();
7375 }
7476
7577 // Private
7678
77  _generateDetailsCanvas2D(action, context, options = {})
 79 _generateDetailsCanvas2D(action)
7880 {
7981 if (!this._dataGrid) {
8082 this._dataGrid = new WI.DataGrid({

@@WI.RecordingStateDetailsSidebarPanel = class RecordingStateDetailsSidebarPanel e
8789
8890 this._dataGrid.removeChildren();
8991
 92 if (!action.snapshot)
 93 return;
 94
 95 let context = action.snapshot.context;
9096 if (!context)
9197 return;
9298

Source/WebInspectorUI/UserInterface/Views/RecordingTraceDetailsSidebarPanel.js

@@WI.RecordingTraceDetailsSidebarPanel = class RecordingTraceDetailsSidebarPanel e
4646 objects = [objects];
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
5354 set recording(recording)

@@WI.RecordingTraceDetailsSidebarPanel = class RecordingTraceDetailsSidebarPanel e
6162 this.contentView.element.removeChildren();
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;
6870

@@WI.RecordingTraceDetailsSidebarPanel = class RecordingTraceDetailsSidebarPanel e
7072
7173 this.contentView.element.removeChildren();
7274
 75 if (!this._action)
 76 return;
 77
7378 let trace = this._action.trace;
7479 this._backtraceTreeController.callFrames = trace;
7580

Source/WebInspectorUI/UserInterface/Views/ResourceIcons.css

7676 content: url(../Images/Beacon.svg);
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);
8981}