2 * Copyright (C) 2013, 2015 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
26 WebInspector.ResourceSidebarPanel = class ResourceSidebarPanel extends WebInspector.NavigationSidebarPanel
28 constructor(contentBrowser)
30 super("resource", WebInspector.UIString("Resources"), true);
32 this.contentBrowser = contentBrowser;
34 this.filterBar.placeholder = WebInspector.UIString("Filter Resource List");
36 this._waitingForInitialMainFrame = true;
38 WebInspector.frameResourceManager.addEventListener(WebInspector.FrameResourceManager.Event.MainFrameDidChange, this._mainFrameDidChange, this);
40 WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.ScriptAdded, this._scriptWasAdded, this);
41 WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.ScriptsCleared, this._scriptsCleared, this);
43 WebInspector.notifications.addEventListener(WebInspector.Notification.ExtraDomainsActivated, this._extraDomainsActivated, this);
45 this.contentTreeOutline.onselect = this._treeElementSelected.bind(this);
46 this.contentTreeOutline.includeSourceMapResourceChildren = true;
48 if (WebInspector.debuggableType === WebInspector.DebuggableType.JavaScript)
49 this.contentTreeOutline.element.classList.add(WebInspector.NavigationSidebarPanel.HideDisclosureButtonsStyleClassName);
54 showDefaultContentView()
56 if (WebInspector.frameResourceManager.mainFrame) {
57 this.contentBrowser.showContentViewForRepresentedObject(WebInspector.frameResourceManager.mainFrame);
61 var firstTreeElement = this.contentTreeOutline.children[0];
63 this.showDefaultContentViewForTreeElement(firstTreeElement);
66 treeElementForRepresentedObject(representedObject)
68 // A custom implementation is needed for this since the frames are populated lazily.
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.
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;
79 function isAncestor(ancestor, resourceOrFrame)
81 // SourceMapResources are descendants of another SourceCode object.
82 if (resourceOrFrame instanceof WebInspector.SourceMapResource) {
83 if (resourceOrFrame.sourceMap.originalSourceCode === ancestor)
86 // Not a direct ancestor, so check the ancestors of the parent SourceCode object.
87 resourceOrFrame = resourceOrFrame.sourceMap.originalSourceCode;
90 var currentFrame = resourceOrFrame.parentFrame;
91 while (currentFrame) {
92 if (currentFrame === ancestor)
94 currentFrame = currentFrame.parentFrame;
100 function getParent(resourceOrFrame)
102 // SourceMapResources are descendants of another SourceCode object.
103 if (resourceOrFrame instanceof WebInspector.SourceMapResource)
104 return resourceOrFrame.sourceMap.originalSourceCode;
105 return resourceOrFrame.parentFrame;
108 var treeElement = this.contentTreeOutline.findTreeElement(representedObject, isAncestor, getParent);
112 // Only special case Script objects.
113 if (!(representedObject instanceof WebInspector.Script))
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.");
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.
126 if (!this._anonymousScriptsFolderTreeElement)
127 this._anonymousScriptsFolderTreeElement = new WebInspector.FolderTreeElement(WebInspector.UIString("Anonymous Scripts"));
129 if (!this._anonymousScriptsFolderTreeElement.parent) {
130 var index = insertionIndexForObjectInListSortedByFunction(this._anonymousScriptsFolderTreeElement, this.contentTreeOutline.children, this._compareTreeElements);
131 this.contentTreeOutline.insertChild(this._anonymousScriptsFolderTreeElement, index);
134 var scriptTreeElement = new WebInspector.ScriptTreeElement(representedObject);
135 this._anonymousScriptsFolderTreeElement.appendChild(scriptTreeElement);
137 return scriptTreeElement;
142 _mainFrameDidChange(event)
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;
150 var newFrame = WebInspector.frameResourceManager.mainFrame;
152 newFrame.addEventListener(WebInspector.Frame.Event.MainResourceDidChange, this._mainFrameMainResourceDidChange, this);
153 this._mainFrameTreeElement = new WebInspector.FrameTreeElement(newFrame);
154 this.contentTreeOutline.insertChild(this._mainFrameTreeElement, 0);
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;
161 treeElement = this._mainFrameTreeElement;
162 this.showDefaultContentViewForTreeElement(treeElement);
166 // The navigation path needs update when the main frame changes, since all resources are under the main frame.
167 this.contentBrowser.updateHierarchicalPathForCurrentContentView();
169 // We only care about the first time the main frame changes.
170 if (!this._waitingForInitialMainFrame)
173 // Only if there is a main frame.
177 this._waitingForInitialMainFrame = false;
180 _mainFrameMainResourceDidChange(event)
182 this.contentBrowser.contentViewContainer.closeAllContentViews();
184 function delayedWork()
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);
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);
200 _scriptWasAdded(event)
202 var script = event.data.script;
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.
209 // Exclude inspector scripts.
210 if (script.url.startsWith("__WebInspector"))
213 // If the script URL matches a resource we can assume it is part of that resource and does not need added.
217 var insertIntoTopLevel = false;
219 if (script.injected) {
220 if (!this._extensionScriptsFolderTreeElement)
221 this._extensionScriptsFolderTreeElement = new WebInspector.FolderTreeElement(WebInspector.UIString("Extension Scripts"));
222 var parentFolderTreeElement = this._extensionScriptsFolderTreeElement;
224 if (WebInspector.debuggableType === WebInspector.DebuggableType.JavaScript && !WebInspector.hasExtraDomains)
225 insertIntoTopLevel = true;
227 if (!this._extraScriptsFolderTreeElement)
228 this._extraScriptsFolderTreeElement = new WebInspector.FolderTreeElement(WebInspector.UIString("Extra Scripts"));
229 var parentFolderTreeElement = this._extraScriptsFolderTreeElement;
233 var scriptTreeElement = new WebInspector.ScriptTreeElement(script);
235 if (insertIntoTopLevel) {
236 var index = insertionIndexForObjectInListSortedByFunction(scriptTreeElement, this.contentTreeOutline.children, this._compareTreeElements);
237 this.contentTreeOutline.insertChild(scriptTreeElement, index);
239 if (!parentFolderTreeElement.parent) {
240 var index = insertionIndexForObjectInListSortedByFunction(parentFolderTreeElement, this.contentTreeOutline.children, this._compareTreeElements);
241 this.contentTreeOutline.insertChild(parentFolderTreeElement, index);
244 parentFolderTreeElement.appendChild(scriptTreeElement);
248 _scriptsCleared(event)
250 if (this._extensionScriptsFolderTreeElement) {
251 if (this._extensionScriptsFolderTreeElement.parent)
252 this._extensionScriptsFolderTreeElement.parent.removeChild(this._extensionScriptsFolderTreeElement);
253 this._extensionScriptsFolderTreeElement = null;
256 if (this._extraScriptsFolderTreeElement) {
257 if (this._extraScriptsFolderTreeElement.parent)
258 this._extraScriptsFolderTreeElement.parent.removeChild(this._extraScriptsFolderTreeElement);
259 this._extraScriptsFolderTreeElement = null;
262 if (this._anonymousScriptsFolderTreeElement) {
263 if (this._anonymousScriptsFolderTreeElement.parent)
264 this._anonymousScriptsFolderTreeElement.parent.removeChild(this._anonymousScriptsFolderTreeElement);
265 this._anonymousScriptsFolderTreeElement = null;
269 _treeElementSelected(treeElement, selectedByUser)
271 if (treeElement instanceof WebInspector.FolderTreeElement)
274 if (treeElement instanceof WebInspector.ResourceTreeElement || treeElement instanceof WebInspector.ScriptTreeElement) {
275 WebInspector.showRepresentedObject(treeElement.representedObject);
279 console.error("Unknown tree element", treeElement);
282 _compareTreeElements(a, b)
284 // Always sort the main frame element first.
285 if (a instanceof WebInspector.FrameTreeElement)
287 if (b instanceof WebInspector.FrameTreeElement)
290 console.assert(a.mainTitle);
291 console.assert(b.mainTitle);
293 return (a.mainTitle || "").localeCompare(b.mainTitle || "");
296 _extraDomainsActivated()
298 if (WebInspector.debuggableType === WebInspector.DebuggableType.JavaScript)
299 this.contentTreeOutline.element.classList.remove(WebInspector.NavigationSidebarPanel.HideDisclosureButtonsStyleClassName);