Web Inspector: drag/drop over the sidebar should load an imported file in Canvas...
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Views / CanvasTabContentView.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.CanvasTabContentView = class CanvasTabContentView extends WI.ContentBrowserTabContentView
27 {
28     constructor(representedObject)
29     {
30         console.assert(!representedObject || representedObject instanceof WI.Canvas);
31
32         let tabBarItem = WI.GeneralTabBarItem.fromTabInfo(WI.CanvasTabContentView.tabInfo());
33
34         const navigationSidebarPanelConstructor = WI.CanvasSidebarPanel;
35         const detailsSidebarPanelConstructors = [WI.RecordingStateDetailsSidebarPanel, WI.RecordingTraceDetailsSidebarPanel, WI.CanvasDetailsSidebarPanel];
36         const disableBackForward = true;
37         super("canvas", ["canvas"], tabBarItem, navigationSidebarPanelConstructor, detailsSidebarPanelConstructors, disableBackForward);
38
39         this._canvasCollection = new WI.CanvasCollection;
40
41         this._canvasTreeOutline = new WI.TreeOutline;
42         this._canvasTreeOutline.addEventListener(WI.TreeOutline.Event.SelectionDidChange, this._canvasTreeOutlineSelectionDidChange, this);
43
44         this._overviewTreeElement = new WI.GeneralTreeElement("canvas-overview", WI.UIString("Overview"), null, this._canvasCollection);
45         this._canvasTreeOutline.appendChild(this._overviewTreeElement);
46
47         this._importedRecordingsTreeElement = new WI.FolderTreeElement(WI.UIString("Imported Recordings"), WI.RecordingCollection);
48         this._importedRecordingsTreeElement.hidden = true;
49         this._canvasTreeOutline.appendChild(this._importedRecordingsTreeElement);
50
51         this._recordShortcut = new WI.KeyboardShortcut(null, WI.KeyboardShortcut.Key.Space, this._handleSpace.bind(this));
52         this._recordShortcut.implicitlyPreventsDefault = false;
53         this._recordShortcut.disabled = true;
54
55         this._recordSingleFrameShortcut = new WI.KeyboardShortcut(WI.KeyboardShortcut.Modifier.Shift, WI.KeyboardShortcut.Key.Space, this._handleSpace.bind(this));
56         this._recordSingleFrameShortcut.implicitlyPreventsDefault = false;
57         this._recordSingleFrameShortcut.disabled = true;
58
59         WI.canvasManager.enable();
60     }
61
62     static tabInfo()
63     {
64         return {
65             image: "Images/Canvas.svg",
66             title: WI.UIString("Canvas"),
67         };
68     }
69
70     static isTabAllowed()
71     {
72         return !!window.CanvasAgent;
73     }
74
75     // Public
76
77     treeElementForRepresentedObject(representedObject)
78     {
79         return this._canvasTreeOutline.findTreeElement(representedObject);
80     }
81
82     get type()
83     {
84         return WI.CanvasTabContentView.Type;
85     }
86
87     get supportsSplitContentBrowser()
88     {
89         return true;
90     }
91
92     get managesNavigationSidebarPanel()
93     {
94         return true;
95     }
96
97     canShowRepresentedObject(representedObject)
98     {
99         return representedObject instanceof WI.Canvas
100             || representedObject instanceof WI.CanvasCollection
101             || representedObject instanceof WI.Recording
102             || representedObject instanceof WI.ShaderProgram;
103     }
104
105     shown()
106     {
107         super.shown();
108
109         this._recordShortcut.disabled = false;
110         this._recordSingleFrameShortcut.disabled = false;
111
112         if (!this.contentBrowser.currentContentView)
113             this.showRepresentedObject(this._canvasCollection);
114     }
115
116     hidden()
117     {
118         this._recordShortcut.disabled = true;
119         this._recordSingleFrameShortcut.disabled = true;
120
121         super.hidden();
122     }
123
124     closed()
125     {
126         WI.canvasManager.disable();
127
128         super.closed();
129     }
130
131     restoreStateFromCookie(cookie)
132     {
133         // FIXME: implement once <https://webkit.org/b/177606> is complete.
134     }
135
136     saveStateToCookie(cookie)
137     {
138         // FIXME: implement once <https://webkit.org/b/177606> is complete.
139     }
140
141     async handleFileDrop(files)
142     {
143         await WI.FileUtilities.readJSON(files, (result) => WI.canvasManager.processJSON(result));
144     }
145
146     // Protected
147
148     initialLayout()
149     {
150         super.initialLayout();
151
152         const options = {
153             suppressShowRecording: true,
154         };
155
156         for (let recording of WI.canvasManager.importedRecordings)
157             this._addRecording(recording, options);
158     }
159
160     attached()
161     {
162         super.attached();
163
164         WI.canvasManager.addEventListener(WI.CanvasManager.Event.CanvasAdded, this._handleCanvasAdded, this);
165         WI.canvasManager.addEventListener(WI.CanvasManager.Event.CanvasRemoved, this._handleCanvasRemoved, this);
166         WI.canvasManager.addEventListener(WI.CanvasManager.Event.RecordingImported, this._recordingImportedOrStopped, this);
167         WI.Canvas.addEventListener(WI.Canvas.Event.RecordingStopped, this._recordingImportedOrStopped, this);
168
169         let canvases = WI.canvasManager.canvases;
170
171         for (let canvas of this._canvasCollection) {
172             if (!canvases.includes(canvas))
173                 this._removeCanvas(canvas);
174         }
175
176         for (let canvas of canvases) {
177             if (!this._canvasCollection.has(canvas))
178                 this._addCanvas(canvas);
179         }
180     }
181
182     detached()
183     {
184         WI.Canvas.removeEventListener(null, null, this);
185         WI.canvasManager.removeEventListener(null, null, this);
186
187         super.detached();
188     }
189
190     // Private
191
192     _addCanvas(canvas)
193     {
194         this._overviewTreeElement.appendChild(new WI.CanvasTreeElement(canvas));
195         this._canvasCollection.add(canvas);
196
197         const options = {
198             suppressShowRecording: true,
199         };
200
201         for (let recording of canvas.recordingCollection)
202             this._addRecording(recording, options);
203     }
204
205     _removeCanvas(canvas)
206     {
207         let treeElement = this._canvasTreeOutline.findTreeElement(canvas);
208         console.assert(treeElement, "Missing tree element for canvas.", canvas);
209
210         const suppressNotification = true;
211         treeElement.deselect(suppressNotification);
212         this._overviewTreeElement.removeChild(treeElement);
213
214         this._canvasCollection.remove(canvas);
215
216         const options = {
217             suppressShowRecording: true,
218         };
219
220         for (let recording of canvas.recordingCollection)
221             this._addRecording(recording, options);
222
223         let currentContentView = this.contentBrowser.currentContentView;
224         if (currentContentView instanceof WI.CanvasContentView)
225             WI.showRepresentedObject(this._canvasCollection);
226         else if (currentContentView instanceof WI.RecordingContentView && canvas.recordingCollection.has(currentContentView.representedObject))
227             this.contentBrowser.updateHierarchicalPathForCurrentContentView();
228
229         let navigationSidebarPanel = this.navigationSidebarPanel;
230         if (navigationSidebarPanel instanceof WI.CanvasSidebarPanel && navigationSidebarPanel.visible)
231             navigationSidebarPanel.updateRepresentedObjects();
232     }
233
234     _addRecording(recording, options = {})
235     {
236         if (!recording.source) {
237             const subtitle = null;
238             let recordingTreeElement = new WI.GeneralTreeElement(["recording"], recording.displayName, subtitle, recording);
239             this._importedRecordingsTreeElement.hidden = false;
240             this._importedRecordingsTreeElement.appendChild(recordingTreeElement);
241         }
242
243         if (!options.suppressShowRecording)
244             this.showRepresentedObject(recording);
245     }
246
247     _handleCanvasAdded(event)
248     {
249         this._addCanvas(event.data.canvas);
250     }
251
252     _handleCanvasRemoved(event)
253     {
254         this._removeCanvas(event.data.canvas);
255     }
256
257     _canvasTreeOutlineSelectionDidChange(event)
258     {
259         let selectedElement = this._canvasTreeOutline.selectedTreeElement;
260         if (!selectedElement)
261             return;
262
263         let representedObject = selectedElement.representedObject;
264         if (!this.canShowRepresentedObject(representedObject)) {
265             console.assert(false, "Unexpected representedObject.", representedObject);
266             return;
267         }
268
269         this.showRepresentedObject(representedObject);
270     }
271
272     _recordingImportedOrStopped(event)
273     {
274         let {recording, initiatedByUser} = event.data;
275         if (!recording)
276             return;
277
278         let options = {};
279
280         // Always show imported recordings.
281         if (recording.source)
282             options.suppressShowRecording = !initiatedByUser || this.contentBrowser.currentRepresentedObjects.some((representedObject) => representedObject instanceof WI.Recording);
283
284         this._addRecording(recording, options);
285     }
286
287     _handleSpace(event)
288     {
289         if (WI.isEventTargetAnEditableField(event))
290             return;
291
292         if (!this.navigationSidebarPanel)
293             return;
294
295         let canvas = this.navigationSidebarPanel.canvas;
296         if (!canvas)
297             return;
298
299         if (canvas.recordingActive)
300             canvas.stopRecording();
301         else {
302             let singleFrame = !!event.shiftKey;
303             canvas.startRecording(singleFrame);
304         }
305
306         event.preventDefault();
307     }
308 };
309
310 WI.CanvasTabContentView.Type = "canvas";