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