9bc6db40b74d5b1a51fa0c8673aeb3b99cbbc3e8
[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.ExtraScriptAdded, this._extraScriptAdded, this);
42         frame.addEventListener(WebInspector.Frame.Event.ChildFrameWasAdded, this._childFrameWasAdded, this);
43         frame.addEventListener(WebInspector.Frame.Event.ChildFrameWasRemoved, this._childFrameWasRemoved, this);
44
45         frame.domTree.addEventListener(WebInspector.DOMTree.Event.ContentFlowWasAdded, this._childContentFlowWasAdded, this);
46         frame.domTree.addEventListener(WebInspector.DOMTree.Event.ContentFlowWasRemoved, this._childContentFlowWasRemoved, this);
47         frame.domTree.addEventListener(WebInspector.DOMTree.Event.RootDOMNodeInvalidated, this._rootDOMNodeInvalidated, this);
48
49         this.shouldRefreshChildren = true;
50         this.folderSettingsKey = this._frame.url.hash;
51
52         this.registerFolderizeSettings("frames", WebInspector.UIString("Frames"),
53             (representedObject) => representedObject instanceof WebInspector.Frame,
54             () => this.frame.childFrames.length,
55             WebInspector.FrameTreeElement
56         );
57
58         this.registerFolderizeSettings("flows", WebInspector.UIString("Flows"),
59             (representedObject) => representedObject instanceof WebInspector.ContentFlow,
60             () => this.frame.domTree.flowsCount,
61             WebInspector.ContentFlowTreeElement
62         );
63
64         this.registerFolderizeSettings("extra-scripts", WebInspector.UIString("Extra Scripts"),
65             (representedObject) => representedObject instanceof WebInspector.Script && representedObject.dynamicallyAddedScriptElement,
66             () => this.frame.extraScripts.length,
67             WebInspector.ScriptTreeElement
68         );
69
70         function makeValidateCallback(resourceType) {
71             return function(representedObject) {
72                 return representedObject instanceof WebInspector.Resource && representedObject.type === resourceType;
73             };
74         }
75
76         function makeChildCountCallback(frame, resourceType) {
77             return function() {
78                 return frame.resourcesWithType(resourceType).length;
79             };
80         }
81
82         for (var key in WebInspector.Resource.Type) {
83             var value = WebInspector.Resource.Type[key];
84             var folderName = WebInspector.Resource.displayNameForType(value, true);
85             this.registerFolderizeSettings(key, folderName,
86                 makeValidateCallback(value),
87                 makeChildCountCallback(this.frame, value),
88                 WebInspector.ResourceTreeElement
89             );
90         }
91
92         this.updateParentStatus();
93     }
94
95     // Public
96
97     get frame()
98     {
99         return this._frame;
100     }
101
102     descendantResourceTreeElementTypeDidChange(resourceTreeElement, oldType)
103     {
104         // Called by descendant ResourceTreeElements.
105
106         // Add the tree element again, which will move it to the new location
107         // based on sorting and possible folder changes.
108         this._addTreeElement(resourceTreeElement);
109     }
110
111     descendantResourceTreeElementMainTitleDidChange(resourceTreeElement, oldMainTitle)
112     {
113         // Called by descendant ResourceTreeElements.
114
115         // Add the tree element again, which will move it to the new location
116         // based on sorting and possible folder changes.
117         this._addTreeElement(resourceTreeElement);
118     }
119
120     // Overrides from SourceCodeTreeElement.
121
122     updateSourceMapResources()
123     {
124         // Frames handle their own SourceMapResources.
125
126         if (!this.treeOutline || !this.treeOutline.includeSourceMapResourceChildren)
127             return;
128
129         if (!this._frame)
130             return;
131
132         this.updateParentStatus();
133
134         if (this.resource && this.resource.sourceMaps.length)
135             this.shouldRefreshChildren = true;
136     }
137
138     onattach()
139     {
140         // Immediate superclasses are skipped, since Frames handle their own SourceMapResources.
141         WebInspector.GeneralTreeElement.prototype.onattach.call(this);
142
143         this.element.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this));
144     }
145
146     // Overrides from FolderizedTreeElement (Protected).
147
148     compareChildTreeElements(a, b)
149     {
150         if (a === b)
151             return 0;
152
153         var aIsResource = a instanceof WebInspector.ResourceTreeElement;
154         var bIsResource = b instanceof WebInspector.ResourceTreeElement;
155
156         if (aIsResource && bIsResource)
157             return WebInspector.ResourceTreeElement.compareResourceTreeElements(a, b);
158
159         if (!aIsResource && !bIsResource) {
160             // When both components are not resources then default to base class comparison.
161             return super.compareChildTreeElements(a, b);
162         }
163
164         // Non-resources should appear before the resources.
165         // FIXME: There should be a better way to group the elements by their type.
166         return aIsResource ? 1 : -1;
167     }
168
169     // Overrides from TreeElement (Private).
170
171     onpopulate()
172     {
173         if (this.children.length && !this.shouldRefreshChildren)
174             return;
175         this.shouldRefreshChildren = false;
176
177         this.removeChildren();
178         this.updateParentStatus();
179         this.prepareToPopulate();
180
181         for (var i = 0; i < this._frame.childFrames.length; ++i)
182             this.addChildForRepresentedObject(this._frame.childFrames[i]);
183
184         for (var i = 0; i < this._frame.resources.length; ++i)
185             this.addChildForRepresentedObject(this._frame.resources[i]);
186
187         var sourceMaps = this.resource && this.resource.sourceMaps;
188         for (var i = 0; i < sourceMaps.length; ++i) {
189             var sourceMap = sourceMaps[i];
190             for (var j = 0; j < sourceMap.resources.length; ++j)
191                 this.addChildForRepresentedObject(sourceMap.resources[j]);
192         }
193
194         var flowMap = this._frame.domTree.flowMap;
195         for (var flowKey in flowMap)
196             this.addChildForRepresentedObject(flowMap[flowKey]);
197
198         for (let extraScript of this._frame.extraScripts) {
199             if (extraScript.sourceURL || extraScript.sourceMappingURL)
200                 this.addChildForRepresentedObject(extraScript);
201         }
202     }
203
204     onexpand()
205     {
206         this._expandedSetting.value = true;
207         this._frame.domTree.requestContentFlowList();
208     }
209
210     oncollapse()
211     {
212         // Only store the setting if we have children, since setting hasChildren to false will cause a collapse,
213         // and we only care about user triggered collapses.
214         if (this.hasChildren)
215             this._expandedSetting.value = false;
216     }
217
218     // Private
219
220     _updateExpandedSetting()
221     {
222         this._expandedSetting = new WebInspector.Setting("frame-expanded-" + this._frame.url.hash, this._frame.isMainFrame() ? true : false);
223         if (this._expandedSetting.value)
224             this.expand();
225         else
226             this.collapse();
227     }
228
229     _mainResourceDidChange(event)
230     {
231         this._updateResource(this._frame.mainResource);
232
233         this.updateParentStatus();
234         this.removeChildren();
235
236         // Change the expanded setting since the frame URL has changed. Do this before setting shouldRefreshChildren, since
237         // shouldRefreshChildren will call onpopulate if expanded is true.
238         this._updateExpandedSetting();
239
240         this.shouldRefreshChildren = true;
241     }
242
243     _resourceWasAdded(event)
244     {
245         this.addRepresentedObjectToNewChildQueue(event.data.resource);
246     }
247
248     _resourceWasRemoved(event)
249     {
250         this.removeChildForRepresentedObject(event.data.resource);
251     }
252
253     _extraScriptAdded(event)
254     {
255         let extraScript = event.data.script;
256         if (extraScript.sourceURL || extraScript.sourceMappingURL)
257             this.addRepresentedObjectToNewChildQueue(extraScript);
258     }
259
260     _childFrameWasAdded(event)
261     {
262         this.addRepresentedObjectToNewChildQueue(event.data.childFrame);
263     }
264
265     _childFrameWasRemoved(event)
266     {
267         this.removeChildForRepresentedObject(event.data.childFrame);
268     }
269
270     _childContentFlowWasAdded(event)
271     {
272         this.addRepresentedObjectToNewChildQueue(event.data.flow);
273     }
274
275     _childContentFlowWasRemoved(event)
276     {
277         this.removeChildForRepresentedObject(event.data.flow);
278     }
279
280     _rootDOMNodeInvalidated()
281     {
282         if (this.expanded)
283             this._frame.domTree.requestContentFlowList();
284     }
285 };