Unreviewed, rolling out r182186.
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Views / FrameTreeElement.js
1 /*
2  * Copyright (C) 2013, 2015 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 WebInspector.FrameTreeElement = class FrameTreeElement extends WebInspector.ResourceTreeElement
27 {
28     constructor(frame, representedObject)
29     {
30         console.assert(frame instanceof WebInspector.Frame);
31
32         super(frame.mainResource, representedObject || frame);
33
34         this._frame = frame;
35
36         this._updateExpandedSetting();
37
38         frame.addEventListener(WebInspector.Frame.Event.MainResourceDidChange, this._mainResourceDidChange, this);
39         frame.addEventListener(WebInspector.Frame.Event.ResourceWasAdded, this._resourceWasAdded, this);
40         frame.addEventListener(WebInspector.Frame.Event.ResourceWasRemoved, this._resourceWasRemoved, this);
41         frame.addEventListener(WebInspector.Frame.Event.ChildFrameWasAdded, this._childFrameWasAdded, this);
42         frame.addEventListener(WebInspector.Frame.Event.ChildFrameWasRemoved, this._childFrameWasRemoved, this);
43
44         frame.domTree.addEventListener(WebInspector.DOMTree.Event.ContentFlowWasAdded, this._childContentFlowWasAdded, this);
45         frame.domTree.addEventListener(WebInspector.DOMTree.Event.ContentFlowWasRemoved, this._childContentFlowWasRemoved, this);
46         frame.domTree.addEventListener(WebInspector.DOMTree.Event.RootDOMNodeInvalidated, this._rootDOMNodeInvalidated, this);
47
48         if (this._frame.isMainFrame())
49             this._downloadingPage = false;
50
51         this.shouldRefreshChildren = true;
52         this.folderSettingsKey = this._frame.url.hash;
53
54         this.registerFolderizeSettings("frames", WebInspector.UIString("Frames"),
55             function(representedObject) { return representedObject instanceof WebInspector.Frame; },
56             function() { return this.frame.childFrames.length; }.bind(this),
57             WebInspector.FrameTreeElement
58         );
59
60         this.registerFolderizeSettings("flows", WebInspector.UIString("Flows"),
61             function(representedObject) { return representedObject instanceof WebInspector.ContentFlow; },
62             function() { return this.frame.domTree.flowsCount; }.bind(this),
63             WebInspector.ContentFlowTreeElement
64         );
65
66         function makeValidateCallback(resourceType) {
67             return function(representedObject) {
68                 return representedObject instanceof WebInspector.Resource && representedObject.type === resourceType;
69             };
70         }
71
72         function makeChildCountCallback(frame, resourceType) {
73             return function() {
74                 return frame.resourcesWithType(resourceType).length;
75             };
76         }
77
78         for (var key in WebInspector.Resource.Type) {
79             var value = WebInspector.Resource.Type[key];
80             var folderName = WebInspector.Resource.displayNameForType(value, true);
81             this.registerFolderizeSettings(key, folderName,
82                 makeValidateCallback(value),
83                 makeChildCountCallback(this.frame, value),
84                 WebInspector.ResourceTreeElement
85             );
86         }
87
88         this.updateParentStatus();
89     }
90
91     // Public
92
93     get frame()
94     {
95         return this._frame;
96     }
97
98     descendantResourceTreeElementTypeDidChange(resourceTreeElement, oldType)
99     {
100         // Called by descendant ResourceTreeElements.
101
102         // Add the tree element again, which will move it to the new location
103         // based on sorting and possible folder changes.
104         this._addTreeElement(resourceTreeElement);
105     }
106
107     descendantResourceTreeElementMainTitleDidChange(resourceTreeElement, oldMainTitle)
108     {
109         // Called by descendant ResourceTreeElements.
110
111         // Add the tree element again, which will move it to the new location
112         // based on sorting and possible folder changes.
113         this._addTreeElement(resourceTreeElement);
114     }
115
116     // Overrides from SourceCodeTreeElement.
117
118     updateSourceMapResources()
119     {
120         // Frames handle their own SourceMapResources.
121
122         if (!this.treeOutline || !this.treeOutline.includeSourceMapResourceChildren)
123             return;
124
125         if (!this._frame)
126             return;
127
128         this.updateParentStatus();
129
130         if (this.resource && this.resource.sourceMaps.length)
131             this.shouldRefreshChildren = true;
132     }
133
134     onattach()
135     {
136         // Immediate superclasses are skipped, since Frames handle their own SourceMapResources.
137         WebInspector.GeneralTreeElement.prototype.onattach.call(this);
138
139         if (this._frame.isMainFrame()) {
140             WebInspector.notifications.addEventListener(WebInspector.Notification.PageArchiveStarted, this._pageArchiveStarted, this);
141             WebInspector.notifications.addEventListener(WebInspector.Notification.PageArchiveEnded, this._pageArchiveEnded, this);
142         }
143     }
144
145     ondetach()
146     {
147         WebInspector.ResourceTreeElement.prototype.ondetach.call(this);
148
149         if (this._frame.isMainFrame()) {
150             WebInspector.notifications.removeEventListener(WebInspector.Notification.PageArchiveStarted, this._pageArchiveStarted, this);
151             WebInspector.notifications.removeEventListener(WebInspector.Notification.PageArchiveEnded, this._pageArchiveEnded, this);
152         }
153     }
154
155     // Overrides from FolderizedTreeElement (Protected).
156
157     compareChildTreeElements(a, b)
158     {
159         if (a === b)
160             return 0;
161
162         var aIsResource = a instanceof WebInspector.ResourceTreeElement;
163         var bIsResource = b instanceof WebInspector.ResourceTreeElement;
164
165         if (aIsResource && bIsResource)
166             return WebInspector.ResourceTreeElement.compareResourceTreeElements(a, b);
167
168         if (!aIsResource && !bIsResource) {
169             // When both components are not resources then default to base class comparison.
170             return super.compareChildTreeElements(a, b);
171         }
172
173         // Non-resources should appear before the resources.
174         // FIXME: There should be a better way to group the elements by their type.
175         return aIsResource ? 1 : -1;
176     }
177
178     // Called from ResourceTreeElement.
179
180     updateStatusForMainFrame()
181     {
182         function loadedImages()
183         {
184             if (!this._reloadButton || !this._downloadButton)
185                 return;
186
187             var fragment = document.createDocumentFragment("div");
188             fragment.appendChild(this._downloadButton.element);
189             fragment.appendChild(this._reloadButton.element);
190             this.status = fragment;
191
192             delete this._loadingMainFrameButtons;
193         }
194
195         if (this._reloadButton && this._downloadButton) {
196             loadedImages.call(this);
197             return;
198         }
199
200         if (!this._loadingMainFrameButtons) {
201             this._loadingMainFrameButtons = true;
202
203             var tooltip;
204             if (WebInspector.debuggableType === WebInspector.DebuggableType.JavaScript)
205                 tooltip = WebInspector.UIString("Restart (%s)").format(WebInspector._reloadPageKeyboardShortcut.displayName);
206             else
207                 tooltip = WebInspector.UIString("Reload page (%s)\nReload ignoring cache (%s)").format(WebInspector._reloadPageKeyboardShortcut.displayName, WebInspector._reloadPageIgnoringCacheKeyboardShortcut.displayName);
208
209             wrappedSVGDocument(platformImagePath("Reload.svg"), null, tooltip, function(element) {
210                 this._reloadButton = new WebInspector.TreeElementStatusButton(element);
211                 this._reloadButton.addEventListener(WebInspector.TreeElementStatusButton.Event.Clicked, this._reloadPageClicked, this);
212                 loadedImages.call(this);
213             }.bind(this));
214
215             wrappedSVGDocument(platformImagePath("DownloadArrow.svg"), null, WebInspector.UIString("Download Web Archive"), function(element) {
216                 this._downloadButton = new WebInspector.TreeElementStatusButton(element);
217                 this._downloadButton.addEventListener(WebInspector.TreeElementStatusButton.Event.Clicked, this._downloadButtonClicked, this);
218                 this._updateDownloadButton();
219                 loadedImages.call(this);
220             }.bind(this));
221         }
222     }
223
224     // Overrides from TreeElement (Private).
225
226     onpopulate()
227     {
228         if (this.children.length && !this.shouldRefreshChildren)
229             return;
230         this.shouldRefreshChildren = false;
231
232         this.removeChildren();
233         this.updateParentStatus();
234         this.prepareToPopulate();
235
236         for (var i = 0; i < this._frame.childFrames.length; ++i)
237             this.addChildForRepresentedObject(this._frame.childFrames[i]);
238
239         for (var i = 0; i < this._frame.resources.length; ++i)
240             this.addChildForRepresentedObject(this._frame.resources[i]);
241
242         var sourceMaps = this.resource && this.resource.sourceMaps;
243         for (var i = 0; i < sourceMaps.length; ++i) {
244             var sourceMap = sourceMaps[i];
245             for (var j = 0; j < sourceMap.resources.length; ++j)
246                 this.addChildForRepresentedObject(sourceMap.resources[j]);
247         }
248
249         var flowMap = this._frame.domTree.flowMap;
250         for (var flowKey in flowMap)
251             this.addChildForRepresentedObject(flowMap[flowKey]);
252
253     }
254
255     onexpand()
256     {
257         this._expandedSetting.value = true;
258         this._frame.domTree.requestContentFlowList();
259     }
260
261     oncollapse()
262     {
263         // Only store the setting if we have children, since setting hasChildren to false will cause a collapse,
264         // and we only care about user triggered collapses.
265         if (this.hasChildren)
266             this._expandedSetting.value = false;
267     }
268
269     // Private
270
271     _updateExpandedSetting()
272     {
273         this._expandedSetting = new WebInspector.Setting("frame-expanded-" + this._frame.url.hash, this._frame.isMainFrame() ? true : false);
274         if (this._expandedSetting.value)
275             this.expand();
276         else
277             this.collapse();
278     }
279
280     _mainResourceDidChange(event)
281     {
282         this._updateResource(this._frame.mainResource);
283
284         this.updateParentStatus();
285         this.removeChildren();
286
287         // Change the expanded setting since the frame URL has changed. Do this before setting shouldRefreshChildren, since
288         // shouldRefreshChildren will call onpopulate if expanded is true.
289         this._updateExpandedSetting();
290
291         if (this._frame.isMainFrame())
292             this._updateDownloadButton();
293
294         this.shouldRefreshChildren = true;
295     }
296
297     _resourceWasAdded(event)
298     {
299         this.addRepresentedObjectToNewChildQueue(event.data.resource);
300     }
301
302     _resourceWasRemoved(event)
303     {
304         this.removeChildForRepresentedObject(event.data.resource);
305     }
306
307     _childFrameWasAdded(event)
308     {
309         this.addRepresentedObjectToNewChildQueue(event.data.childFrame);
310     }
311
312     _childFrameWasRemoved(event)
313     {
314         this.removeChildForRepresentedObject(event.data.childFrame);
315     }
316
317     _childContentFlowWasAdded(event)
318     {
319         this.addRepresentedObjectToNewChildQueue(event.data.flow);
320     }
321
322     _childContentFlowWasRemoved(event)
323     {
324         this.removeChildForRepresentedObject(event.data.flow);
325     }
326
327     _rootDOMNodeInvalidated()
328     {
329         if (this.expanded)
330             this._frame.domTree.requestContentFlowList();
331     }
332
333     _reloadPageClicked(event)
334     {
335         // Ignore cache when the shift key is pressed.
336         PageAgent.reload(event.data.shiftKey);
337     }
338
339     _downloadButtonClicked(event)
340     {
341         WebInspector.archiveMainFrame();
342     }
343
344     _updateDownloadButton()
345     {
346         console.assert(this._frame.isMainFrame());
347         if (!this._downloadButton)
348             return;
349
350         if (!PageAgent.archive || WebInspector.debuggableType !== WebInspector.DebuggableType.Web) {
351             this._downloadButton.hidden = true;
352             return;
353         }
354
355         if (this._downloadingPage) {
356             this._downloadButton.enabled = false;
357             return;
358         }
359
360         this._downloadButton.enabled = WebInspector.canArchiveMainFrame();
361     }
362
363     _pageArchiveStarted(event)
364     {
365         this._downloadingPage = true;
366         this._updateDownloadButton();
367     }
368
369     _pageArchiveEnded(event)
370     {
371         this._downloadingPage = false;
372         this._updateDownloadButton();
373     }
374 };