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