Web Inspector: Canvas Tab: clicking on a canvas card causes details sidebar to show...
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Views / RecordingNavigationSidebarPanel.js
1 /*
2  * Copyright (C) 2017 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 WI.RecordingNavigationSidebarPanel = class RecordingNavigationSidebarPanel extends WI.NavigationSidebarPanel
27 {
28     constructor()
29     {
30         super("recording", WI.UIString("Recording"));
31
32         this.contentTreeOutline.customIndent = true;
33         this.contentTreeOutline.registerScrollVirtualizer(this.contentView.element, 20);
34
35         this.recording = null;
36
37         this._importButton = null;
38         this._exportButton = null;
39     }
40
41     // Public
42
43     set recording(recording)
44     {
45         if (recording === this._recording)
46             return;
47
48         this.contentTreeOutline.removeChildren();
49
50         this._recording = recording;
51
52         this.updateEmptyContentPlaceholder(WI.UIString("No Recording Data"));
53
54         if (!this._recording) {
55             if (this._exportButton)
56                 this._exportButton.disabled = true;
57             return;
58         }
59
60         this._recording.actions.then((actions) => {
61             this.contentTreeOutline.element.dataset.indent = Number.countDigits(actions.length);
62
63             if (actions[0] instanceof WI.RecordingInitialStateAction)
64                 this.contentTreeOutline.appendChild(new WI.RecordingActionTreeElement(actions[0], 0, this._recording.type));
65
66             let cumulativeActionIndex = 1;
67             this._recording.frames.forEach((frame, frameIndex) => {
68                 let folder = new WI.FolderTreeElement(WI.UIString("Frame %d").format((frameIndex + 1).toLocaleString()));
69                 this.contentTreeOutline.appendChild(folder);
70
71                 for (let i = 0; i < frame.actions.length; ++i)
72                     folder.appendChild(new WI.RecordingActionTreeElement(frame.actions[i], cumulativeActionIndex + i, this._recording.type));
73
74                 if (frame.incomplete)
75                     folder.subtitle = WI.UIString("Incomplete");
76
77                 if (this._recording.frames.length === 1)
78                     folder.expand();
79
80                 cumulativeActionIndex += frame.actions.length;
81             });
82
83             this._exportButton.disabled = !actions.length;
84
85             let index = this._recording[WI.RecordingNavigationSidebarPanel.SelectedActionIndexSymbol] || 0;
86             this.updateActionIndex(index);
87         });
88     }
89
90     updateActionIndex(index, options = {})
91     {
92         if (!this._recording)
93             return;
94
95         this._recording.actions.then((actions) => {
96             let recordingAction = actions[index];
97             console.assert(recordingAction, "Invalid recording action index.", index);
98             if (!recordingAction)
99                 return;
100
101             let treeElement = this.contentTreeOutline.findTreeElement(recordingAction);
102             console.assert(treeElement, "Missing tree element for recording action.", recordingAction);
103             if (!treeElement)
104                 return;
105
106             this._recording[WI.RecordingNavigationSidebarPanel.SelectedActionIndexSymbol] = index;
107
108             const omitFocus = false;
109             const selectedByUser = false;
110             const suppressOnSelect = true;
111             const suppressOnDeselect = true;
112             treeElement.revealAndSelect(omitFocus, selectedByUser, suppressOnSelect, suppressOnDeselect);
113         });
114     }
115
116     // Protected
117
118     initialLayout()
119     {
120         super.initialLayout();
121
122         const role = "button";
123
124         const importLabel = WI.UIString("Import");
125         let importNavigationItem = new WI.NavigationItem("recording-import", role, importLabel);
126
127         this._importButton = importNavigationItem.element.appendChild(document.createElement("button"));
128         this._importButton.textContent = importLabel;
129         this._importButton.addEventListener("click", this._importNavigationItemClicked.bind(this));
130
131         const exportLabel = WI.UIString("Export");
132         let exportNavigationItem = new WI.NavigationItem("recording-export", role, exportLabel);
133
134         this._exportButton = exportNavigationItem.element.appendChild(document.createElement("button"));
135         this._exportButton.textContent = exportLabel;
136         this._exportButton.disabled = !this.contentTreeOutline.children.length;
137         this._exportButton.addEventListener("click", this._exportNavigationItemClicked.bind(this));
138
139         const element = null;
140         this.addSubview(new WI.NavigationBar(element, [importNavigationItem, exportNavigationItem]));
141
142         let filterFunction = (treeElement) => {
143             if (!(treeElement instanceof WI.RecordingActionTreeElement))
144                 return false;
145
146             return treeElement.representedObject.isVisual;
147         };
148
149         const activatedByDefault = false;
150         const defaultToolTip = WI.UIString("Only show visual actions");
151         const activatedToolTip = WI.UIString("Show all actions");
152         this.filterBar.addFilterBarButton("recording-show-visual-only", filterFunction, activatedByDefault, defaultToolTip, activatedToolTip, "Images/Paint.svg", 15, 15);
153     }
154
155     matchTreeElementAgainstCustomFilters(treeElement)
156     {
157         // Keep recording frame tree elements.
158         if (treeElement instanceof WI.FolderTreeElement)
159             return true;
160
161         return super.matchTreeElementAgainstCustomFilters(treeElement);
162     }
163
164     // Private
165
166     _importNavigationItemClicked(event)
167     {
168         WI.loadDataFromFile((data, filename) => {
169             if (!data)
170                 return;
171
172             let payload = null;
173             try {
174                 payload = JSON.parse(data);
175             } catch (e) {
176                 WI.Recording.synthesizeError(e);
177                 return;
178             }
179
180             this.dispatchEventToListeners(WI.RecordingNavigationSidebarPanel.Event.Import, {payload, filename});
181         });
182     }
183
184     _exportNavigationItemClicked(event)
185     {
186         if (!this._recording || !this.contentBrowser || !this.contentBrowser.currentContentView || !this.contentBrowser.currentContentView.supportsSave)
187             return;
188
189         const forceSaveAs = true;
190         WI.saveDataToFile(this.contentBrowser.currentContentView.saveData, forceSaveAs);
191     }
192 };
193
194 WI.RecordingNavigationSidebarPanel.SelectedActionIndexSymbol = Symbol("selected-action-index");
195
196 WI.RecordingNavigationSidebarPanel.Event = {
197     Import: "recording-navigation-sidebar-panel-import",
198 };