Web Inspector: Remove FrameContentView.js
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Views / ResourceSidebarPanel.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.ResourceSidebarPanel = class ResourceSidebarPanel extends WebInspector.NavigationSidebarPanel
27 {
28     constructor(contentBrowser)
29     {
30         super("resource", WebInspector.UIString("Resources"), true);
31
32         this.contentBrowser = contentBrowser;
33
34         this.filterBar.placeholder = WebInspector.UIString("Filter Resource List");
35
36         this._waitingForInitialMainFrame = true;
37
38         WebInspector.frameResourceManager.addEventListener(WebInspector.FrameResourceManager.Event.MainFrameDidChange, this._mainFrameDidChange, this);
39
40         WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.ScriptAdded, this._scriptWasAdded, this);
41         WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.ScriptsCleared, this._scriptsCleared, this);
42
43         WebInspector.notifications.addEventListener(WebInspector.Notification.ExtraDomainsActivated, this._extraDomainsActivated, this);
44
45         this.contentTreeOutline.onselect = this._treeElementSelected.bind(this);
46         this.contentTreeOutline.includeSourceMapResourceChildren = true;
47
48         if (WebInspector.debuggableType === WebInspector.DebuggableType.JavaScript)
49             this.contentTreeOutline.element.classList.add(WebInspector.NavigationSidebarPanel.HideDisclosureButtonsStyleClassName);
50     }
51
52     // Public
53
54     showDefaultContentView()
55     {
56         if (WebInspector.frameResourceManager.mainFrame) {
57             this.contentBrowser.showContentViewForRepresentedObject(WebInspector.frameResourceManager.mainFrame);
58             return;
59         }
60
61         var firstTreeElement = this.contentTreeOutline.children[0];
62         if (firstTreeElement)
63             this.showDefaultContentViewForTreeElement(firstTreeElement);
64     }
65
66     treeElementForRepresentedObject(representedObject)
67     {
68         // A custom implementation is needed for this since the frames are populated lazily.
69
70         if (!this._mainFrameTreeElement && (representedObject instanceof WebInspector.Resource || representedObject instanceof WebInspector.Frame)) {
71             // All resources are under the main frame, so we need to return early if we don't have the main frame yet.
72             return null;
73         }
74
75         // The Frame is used as the representedObject instead of the main resource in our tree.
76         if (representedObject instanceof WebInspector.Resource && representedObject.parentFrame && representedObject.parentFrame.mainResource === representedObject)
77             representedObject = representedObject.parentFrame;
78
79         function isAncestor(ancestor, resourceOrFrame)
80         {
81             // SourceMapResources are descendants of another SourceCode object.
82             if (resourceOrFrame instanceof WebInspector.SourceMapResource) {
83                 if (resourceOrFrame.sourceMap.originalSourceCode === ancestor)
84                     return true;
85
86                 // Not a direct ancestor, so check the ancestors of the parent SourceCode object.
87                 resourceOrFrame = resourceOrFrame.sourceMap.originalSourceCode;
88             }
89
90             var currentFrame = resourceOrFrame.parentFrame;
91             while (currentFrame) {
92                 if (currentFrame === ancestor)
93                     return true;
94                 currentFrame = currentFrame.parentFrame;
95             }
96
97             return false;
98         }
99
100         function getParent(resourceOrFrame)
101         {
102             // SourceMapResources are descendants of another SourceCode object.
103             if (resourceOrFrame instanceof WebInspector.SourceMapResource)
104                 return resourceOrFrame.sourceMap.originalSourceCode;
105             return resourceOrFrame.parentFrame;
106         }
107
108         var treeElement = this.contentTreeOutline.findTreeElement(representedObject, isAncestor, getParent);
109         if (treeElement)
110             return treeElement;
111
112         // Only special case Script objects.
113         if (!(representedObject instanceof WebInspector.Script))
114             return null;
115
116         // If the Script has a URL we should have found it earlier.
117         if (representedObject.url) {
118             console.error("Didn't find a ScriptTreeElement for a Script with a URL.");
119             return null;
120         }
121
122         // Since the Script does not have a URL we consider it an 'anonymous' script. These scripts happen from calls to
123         // window.eval() or browser features like Auto Fill and Reader. They are not normally added to the sidebar, but since
124         // we have a ScriptContentView asking for the tree element we will make a ScriptTreeElement on demand and add it.
125
126         if (!this._anonymousScriptsFolderTreeElement)
127             this._anonymousScriptsFolderTreeElement = new WebInspector.FolderTreeElement(WebInspector.UIString("Anonymous Scripts"));
128
129         if (!this._anonymousScriptsFolderTreeElement.parent) {
130             var index = insertionIndexForObjectInListSortedByFunction(this._anonymousScriptsFolderTreeElement, this.contentTreeOutline.children, this._compareTreeElements);
131             this.contentTreeOutline.insertChild(this._anonymousScriptsFolderTreeElement, index);
132         }
133
134         var scriptTreeElement = new WebInspector.ScriptTreeElement(representedObject);
135         this._anonymousScriptsFolderTreeElement.appendChild(scriptTreeElement);
136
137         return scriptTreeElement;
138     }
139
140     // Private
141
142     _mainFrameDidChange(event)
143     {
144         if (this._mainFrameTreeElement) {
145             this._mainFrameTreeElement.frame.removeEventListener(WebInspector.Frame.Event.MainResourceDidChange, this._mainFrameMainResourceDidChange, this);
146             this.contentTreeOutline.removeChild(this._mainFrameTreeElement);
147             this._mainFrameTreeElement = null;
148         }
149
150         var newFrame = WebInspector.frameResourceManager.mainFrame;
151         if (newFrame) {
152             newFrame.addEventListener(WebInspector.Frame.Event.MainResourceDidChange, this._mainFrameMainResourceDidChange, this);
153             this._mainFrameTreeElement = new WebInspector.FrameTreeElement(newFrame);
154             this.contentTreeOutline.insertChild(this._mainFrameTreeElement, 0);
155
156             // Select a tree element by default. Allow onselect if we aren't showing a content view.
157             if (!this.contentTreeOutline.selectedTreeElement) {
158                 var currentContentView = this.contentBrowser.currentContentView;
159                 var treeElement = currentContentView ? this.treeElementForRepresentedObject(currentContentView.representedObject) : null;
160                 if (!treeElement)
161                     treeElement = this._mainFrameTreeElement;
162                 this.showDefaultContentViewForTreeElement(treeElement);
163             }
164         }
165
166         // The navigation path needs update when the main frame changes, since all resources are under the main frame.
167         this.contentBrowser.updateHierarchicalPathForCurrentContentView();
168
169         // We only care about the first time the main frame changes.
170         if (!this._waitingForInitialMainFrame)
171             return;
172
173         // Only if there is a main frame.
174         if (!newFrame)
175             return;
176
177         this._waitingForInitialMainFrame = false;
178     }
179
180     _mainFrameMainResourceDidChange(event)
181     {
182         this.contentBrowser.contentViewContainer.closeAllContentViews();
183
184         function delayedWork()
185         {
186             // Show the main frame since there is no content view showing.
187             // Cookie restoration will attempt to re-select the resource we were showing.
188             if (!this.contentBrowser.currentContentView) {
189                 // NOTE: This selection, during provisional loading, won't be saved, so it is
190                 // safe to do and not-clobber cookie restoration.
191                 this.showDefaultContentViewForTreeElement(this._mainFrameTreeElement);
192             }
193         }
194
195         // Delay this work because other listeners of this event might not have fired yet. So selecting the main frame
196         // before those listeners do their work might cause the content of the old page to show instead of the new page.
197         setTimeout(delayedWork.bind(this), 0);
198     }
199
200     _scriptWasAdded(event)
201     {
202         var script = event.data.script;
203
204         // We don't add scripts without URLs here. Those scripts can quickly clutter the interface and
205         // are usually more transient. They will get added if/when they need to be shown in a content view.
206         if (!script.url)
207             return;
208
209         // Exclude inspector scripts.
210         if (script.url.startsWith("__WebInspector"))
211             return;
212
213         // If the script URL matches a resource we can assume it is part of that resource and does not need added.
214         if (script.resource)
215             return;
216
217         var insertIntoTopLevel = false;
218
219         if (script.injected) {
220             if (!this._extensionScriptsFolderTreeElement)
221                 this._extensionScriptsFolderTreeElement = new WebInspector.FolderTreeElement(WebInspector.UIString("Extension Scripts"));
222             var parentFolderTreeElement = this._extensionScriptsFolderTreeElement;
223         } else {
224             if (WebInspector.debuggableType === WebInspector.DebuggableType.JavaScript && !WebInspector.hasExtraDomains)
225                 insertIntoTopLevel = true;
226             else {
227                 if (!this._extraScriptsFolderTreeElement)
228                     this._extraScriptsFolderTreeElement = new WebInspector.FolderTreeElement(WebInspector.UIString("Extra Scripts"));
229                 var parentFolderTreeElement = this._extraScriptsFolderTreeElement;
230             }
231         }
232
233         var scriptTreeElement = new WebInspector.ScriptTreeElement(script);
234
235         if (insertIntoTopLevel) {
236             var index = insertionIndexForObjectInListSortedByFunction(scriptTreeElement, this.contentTreeOutline.children, this._compareTreeElements);
237             this.contentTreeOutline.insertChild(scriptTreeElement, index);
238         } else {
239             if (!parentFolderTreeElement.parent) {
240                 var index = insertionIndexForObjectInListSortedByFunction(parentFolderTreeElement, this.contentTreeOutline.children, this._compareTreeElements);
241                 this.contentTreeOutline.insertChild(parentFolderTreeElement, index);
242             }
243
244             parentFolderTreeElement.appendChild(scriptTreeElement);
245         }
246     }
247
248     _scriptsCleared(event)
249     {
250         if (this._extensionScriptsFolderTreeElement) {
251             if (this._extensionScriptsFolderTreeElement.parent)
252                 this._extensionScriptsFolderTreeElement.parent.removeChild(this._extensionScriptsFolderTreeElement);
253             this._extensionScriptsFolderTreeElement = null;
254         }
255
256         if (this._extraScriptsFolderTreeElement) {
257             if (this._extraScriptsFolderTreeElement.parent)
258                 this._extraScriptsFolderTreeElement.parent.removeChild(this._extraScriptsFolderTreeElement);
259             this._extraScriptsFolderTreeElement = null;
260         }
261
262         if (this._anonymousScriptsFolderTreeElement) {
263             if (this._anonymousScriptsFolderTreeElement.parent)
264                 this._anonymousScriptsFolderTreeElement.parent.removeChild(this._anonymousScriptsFolderTreeElement);
265             this._anonymousScriptsFolderTreeElement = null;
266         }
267     }
268
269     _treeElementSelected(treeElement, selectedByUser)
270     {
271         if (treeElement instanceof WebInspector.FolderTreeElement)
272             return;
273
274         if (treeElement instanceof WebInspector.ResourceTreeElement || treeElement instanceof WebInspector.ScriptTreeElement) {
275             WebInspector.showRepresentedObject(treeElement.representedObject);
276             return;
277         }
278
279         console.error("Unknown tree element", treeElement);
280     }
281
282     _compareTreeElements(a, b)
283     {
284         // Always sort the main frame element first.
285         if (a instanceof WebInspector.FrameTreeElement)
286             return -1;
287         if (b instanceof WebInspector.FrameTreeElement)
288             return 1;
289
290         console.assert(a.mainTitle);
291         console.assert(b.mainTitle);
292
293         return (a.mainTitle || "").localeCompare(b.mainTitle || "");
294     }
295
296     _extraDomainsActivated()
297     {
298         if (WebInspector.debuggableType === WebInspector.DebuggableType.JavaScript)
299             this.contentTreeOutline.element.classList.remove(WebInspector.NavigationSidebarPanel.HideDisclosureButtonsStyleClassName);
300     }
301 };