| /* |
| * Copyright (C) 2016 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| * THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| WI.OpenResourceDialog = class OpenResourceDialog extends WI.Dialog |
| { |
| constructor(delegate) |
| { |
| super(delegate); |
| |
| this.element.classList.add("open-resource-dialog"); |
| |
| let fieldElement = this.element.appendChild(document.createElement("div")); |
| fieldElement.classList.add("field"); |
| |
| this._inputElement = fieldElement.appendChild(document.createElement("input")); |
| this._inputElement.type = "text"; |
| this._inputElement.placeholder = WI.UIString("File or Resource"); |
| this._inputElement.spellcheck = false; |
| |
| this._clearIconElement = fieldElement.appendChild(document.createElement("img")); |
| |
| this._inputElement.addEventListener("keydown", this._handleKeydownEvent.bind(this)); |
| this._inputElement.addEventListener("keyup", this._handleKeyupEvent.bind(this)); |
| this._inputElement.addEventListener("blur", this._handleBlurEvent.bind(this)); |
| this._clearIconElement.addEventListener("mousedown", this._handleMousedownEvent.bind(this)); |
| this._clearIconElement.addEventListener("click", this._handleClickEvent.bind(this)); |
| |
| this._treeOutline = new WI.TreeOutline; |
| this._treeOutline.allowsRepeatSelection = true; |
| this._treeOutline.disclosureButtons = false; |
| this._treeOutline.large = true; |
| |
| this._treeOutline.addEventListener(WI.TreeOutline.Event.SelectionDidChange, this._treeSelectionDidChange, this); |
| this._treeOutline.element.addEventListener("focus", () => { this._inputElement.focus(); }); |
| |
| this.element.appendChild(this._treeOutline.element); |
| |
| this._queryController = new WI.ResourceQueryController; |
| this._filteredResults = []; |
| } |
| |
| // Protected |
| |
| representedObjectIsValid(value) |
| { |
| if (value instanceof WI.Script && value.anonymous) |
| return false; |
| |
| if (value instanceof WI.CSSStyleSheet && value.anonymous) |
| return false; |
| |
| return super.representedObjectIsValid(value); |
| } |
| |
| _populateResourceTreeOutline() |
| { |
| function createHighlightedTitleFragment(title, searchString, highlightTextRanges) |
| { |
| let shift = searchString.indexOf(title.toLowerCase()); |
| console.assert(shift >= 0); |
| |
| let titleFragment = document.createDocumentFragment(); |
| let lastIndex = 0; |
| for (let textRange of highlightTextRanges) { |
| let end = textRange.endColumn - shift; |
| if (end >= 0) { |
| let start = textRange.startColumn - shift; |
| if (start > lastIndex) |
| titleFragment.append(title.substring(lastIndex, start)); |
| |
| let highlightSpan = document.createElement("span"); |
| highlightSpan.classList.add("highlighted"); |
| highlightSpan.append(title.substring(start, end)); |
| titleFragment.append(highlightSpan); |
| } |
| |
| lastIndex = end; |
| } |
| |
| if (lastIndex < title.length) |
| titleFragment.append(title.substring(lastIndex, title.length)); |
| |
| return titleFragment; |
| } |
| |
| function createTreeElement(representedObject) |
| { |
| let treeElement = null; |
| |
| if (representedObject instanceof WI.SourceMapResource) |
| treeElement = new WI.SourceMapResourceTreeElement(representedObject); |
| else if (representedObject instanceof WI.Resource) |
| treeElement = new WI.ResourceTreeElement(representedObject); |
| else if (representedObject instanceof WI.Script) |
| treeElement = new WI.ScriptTreeElement(representedObject); |
| else if (representedObject instanceof WI.CSSStyleSheet) |
| treeElement = new WI.CSSStyleSheetTreeElement(representedObject); |
| |
| return treeElement; |
| } |
| |
| for (let result of this._filteredResults) { |
| let resource = result.resource; |
| if (this._treeOutline.findTreeElement(resource)) |
| continue; |
| |
| let treeElement = createTreeElement(resource); |
| if (!treeElement) |
| continue; |
| |
| treeElement.mainTitle = createHighlightedTitleFragment(resource.displayName, result.searchString, result.matchingTextRanges); |
| |
| if (resource instanceof WI.LocalResource && resource.localResourceOverride) |
| treeElement.subtitle = WI.UIString("Local Override"); |
| |
| let path = resource.urlComponents.path; |
| let lastPathComponent = resource.urlComponents.lastPathComponent; |
| if (path && lastPathComponent) { |
| let parentPath = path.substring(0, path.length - lastPathComponent.length); |
| if (parentPath.length && parentPath !== "/") |
| treeElement.titlesElement.dataset.path = parentPath; |
| } |
| |
| treeElement[WI.OpenResourceDialog.ResourceMatchCookieDataSymbol] = result.cookie; |
| this._treeOutline.appendChild(treeElement); |
| } |
| |
| if (this._treeOutline.children.length) |
| this._treeOutline.children[0].select(true, false, true); |
| } |
| |
| didDismissDialog() |
| { |
| WI.Frame.removeEventListener(WI.Frame.Event.MainResourceDidChange, this._mainResourceDidChange, this); |
| WI.Frame.removeEventListener(WI.Frame.Event.ResourceWasAdded, this._resourceWasAdded, this); |
| WI.Frame.removeEventListener(WI.Frame.Event.ResourceWasRemoved, this._resourceWasRemoved, this); |
| WI.Target.removeEventListener(WI.Target.Event.ResourceAdded, this._resourceWasAdded, this); |
| WI.debuggerManager.removeEventListener(WI.DebuggerManager.Event.ScriptAdded, this._scriptAdded, this); |
| WI.debuggerManager.removeEventListener(WI.DebuggerManager.Event.ScriptRemoved, this._scriptRemoved, this); |
| WI.cssManager.removeEventListener(WI.CSSManager.Event.StyleSheetAdded, this._handleStyleSheetAdded, this); |
| WI.cssManager.removeEventListener(WI.CSSManager.Event.StyleSheetRemoved, this._handleStyleSheetRemoved, this); |
| |
| this._queryController.reset(); |
| } |
| |
| didPresentDialog() |
| { |
| WI.Frame.addEventListener(WI.Frame.Event.MainResourceDidChange, this._mainResourceDidChange, this); |
| WI.Frame.addEventListener(WI.Frame.Event.ResourceWasAdded, this._resourceWasAdded, this); |
| WI.Frame.addEventListener(WI.Frame.Event.ResourceWasRemoved, this._resourceWasRemoved, this); |
| WI.Target.addEventListener(WI.Target.Event.ResourceAdded, this._resourceWasAdded, this); |
| WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.ScriptAdded, this._scriptAdded, this); |
| WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.ScriptRemoved, this._scriptRemoved, this); |
| WI.cssManager.addEventListener(WI.CSSManager.Event.StyleSheetAdded, this._handleStyleSheetAdded, this); |
| WI.cssManager.addEventListener(WI.CSSManager.Event.StyleSheetRemoved, this._handleStyleSheetRemoved, this); |
| |
| if (WI.networkManager.mainFrame) |
| this._addResourcesForFrame(WI.networkManager.mainFrame); |
| |
| this._addScriptsForTarget(WI.mainTarget); |
| |
| for (let target of WI.targets) { |
| if (target !== WI.mainTarget) |
| this._addResourcesForTarget(target); |
| } |
| |
| this._addLocalResourceOverrides(); |
| |
| if (WI.NetworkManager.supportsBootstrapScript()) { |
| let bootstrapScript = WI.networkManager.bootstrapScript; |
| if (bootstrapScript) { |
| const suppressFilterUpdate = true; |
| this._addResource(bootstrapScript, suppressFilterUpdate); |
| } |
| } |
| |
| for (let styleSheet of WI.cssManager.styleSheets) { |
| if (styleSheet.origin !== WI.CSSStyleSheet.Type.Author && !styleSheet.anonymous) |
| this._addResource(styleSheet); |
| } |
| |
| this._updateFilter(); |
| |
| this._inputElement.focus(); |
| this._clear(); |
| } |
| |
| // Private |
| |
| _handleKeydownEvent(event) |
| { |
| if (event.keyCode === WI.KeyboardShortcut.Key.Escape.keyCode) { |
| if (this._inputElement.value === "") { |
| this.dismiss(); |
| event.preventDefault(); |
| } else |
| this._clear(); |
| |
| event.preventDefault(); |
| } else if (event.keyCode === WI.KeyboardShortcut.Key.Enter.keyCode) { |
| if (this._treeOutline.selectedTreeElement) { |
| this.dismiss(this._treeOutline.selectedTreeElement.representedObject, this._treeOutline.selectedTreeElement[WI.OpenResourceDialog.ResourceMatchCookieDataSymbol]); |
| event.preventDefault(); |
| return; |
| } |
| |
| // ":<line>:<column>" jumps to a location for the current ContentView. |
| if (/^:\d/.test(this._inputElement.value)) { |
| let visibleContentView = WI.focusedOrVisibleContentView(); |
| let representedObject = visibleContentView ? visibleContentView.representedObject : null; |
| if (representedObject && representedObject instanceof WI.SourceCode) { |
| let [, lineNumber, columnNumber] = this._inputElement.value.split(":"); |
| lineNumber = lineNumber ? parseInt(lineNumber, 10) - 1 : 0; |
| columnNumber = columnNumber ? parseInt(columnNumber, 10) - 1 : 0; |
| this.dismiss(representedObject, {lineNumber, columnNumber}); |
| event.preventDefault(); |
| return; |
| } |
| } |
| |
| this._inputElement.select(); |
| } else if (event.keyCode === WI.KeyboardShortcut.Key.Up.keyCode || event.keyCode === WI.KeyboardShortcut.Key.Down.keyCode) { |
| let treeElement = this._treeOutline.selectedTreeElement; |
| if (!treeElement) |
| return; |
| |
| let adjacentSiblingProperty = event.keyCode === WI.KeyboardShortcut.Key.Up.keyCode ? "previousSibling" : "nextSibling"; |
| treeElement = treeElement[adjacentSiblingProperty]; |
| if (treeElement) |
| treeElement.revealAndSelect(true, false, true); |
| |
| event.preventDefault(); |
| } |
| } |
| |
| _handleKeyupEvent(event) |
| { |
| if (event.keyCode === WI.KeyboardShortcut.Key.Up.keyCode || event.keyCode === WI.KeyboardShortcut.Key.Down.keyCode) |
| return; |
| |
| this._updateFilter(); |
| } |
| |
| _handleBlurEvent(event) |
| { |
| // Prevent the dialog from being dismissed while handling a tree item click event. |
| if (event.relatedTarget === this._treeOutline.element) |
| return; |
| |
| this.dismiss(); |
| } |
| |
| _handleMousedownEvent(event) |
| { |
| this._inputElement.select(); |
| |
| // This ensures we don't get a "blur" event triggered for the text field |
| // that would cause the dialog to be dismissed. |
| event.preventDefault(); |
| } |
| |
| _handleClickEvent(event) |
| { |
| this._clear(); |
| } |
| |
| _clear() |
| { |
| this._inputElement.value = ""; |
| this._updateFilter(); |
| } |
| |
| _updateFilter() |
| { |
| this._filteredResults = []; |
| this._treeOutline.removeChildren(); |
| |
| let filterText = this._inputElement.value.trim(); |
| if (filterText) { |
| this._filteredResults = this._queryController.executeQuery(filterText); |
| this._populateResourceTreeOutline(); |
| } |
| |
| this.element.classList.toggle("non-empty", this._inputElement.value !== ""); |
| this.element.classList.toggle("has-results", this._treeOutline.children.length); |
| } |
| |
| _treeSelectionDidChange(event) |
| { |
| let treeElement = this._treeOutline.selectedTreeElement; |
| if (!treeElement) |
| return; |
| |
| if (!event.data.selectedByUser) |
| return; |
| |
| this.dismiss(treeElement.representedObject, treeElement[WI.OpenResourceDialog.ResourceMatchCookieDataSymbol]); |
| } |
| |
| _addResource(resource, suppressFilterUpdate) |
| { |
| if (!this.representedObjectIsValid(resource)) |
| return; |
| |
| // Recurse on source maps if any exist. |
| for (let sourceMap of resource.sourceMaps) { |
| for (let sourceMapResource of sourceMap.resources) |
| this._addResource(sourceMapResource, suppressFilterUpdate); |
| } |
| |
| this._queryController.addResource(resource); |
| if (suppressFilterUpdate) |
| return; |
| |
| this._updateFilter(); |
| } |
| |
| _removeResource(resource) |
| { |
| if (!this.representedObjectIsValid(resource)) |
| return; |
| |
| this._queryController.removeResource(resource); |
| |
| this._updateFilter(); |
| } |
| |
| _addResourcesForFrame(frame) |
| { |
| const suppressFilterUpdate = true; |
| |
| let frames = [frame]; |
| while (frames.length) { |
| let currentFrame = frames.shift(); |
| this._addResource(currentFrame.mainResource, suppressFilterUpdate); |
| for (let resource of currentFrame.resourceCollection) |
| this._addResource(resource, suppressFilterUpdate); |
| |
| frames.pushAll(currentFrame.childFrameCollection); |
| } |
| } |
| |
| _addResourcesForTarget(target) |
| { |
| const suppressFilterUpdate = true; |
| |
| this._addResource(target.mainResource); |
| |
| for (let resource of target.resourceCollection) |
| this._addResource(resource, suppressFilterUpdate); |
| |
| this._addScriptsForTarget(target); |
| } |
| |
| _addScriptsForTarget(target) |
| { |
| const suppressFilterUpdate = true; |
| |
| let targetData = WI.debuggerManager.dataForTarget(target); |
| for (let script of targetData.scripts) { |
| if (script.anonymous || script.resource || script.dynamicallyAddedScriptElement) |
| continue; |
| if (!WI.settings.debugShowConsoleEvaluations.value && isWebInspectorConsoleEvaluationScript(script.sourceURL)) |
| continue; |
| if (!WI.settings.engineeringShowInternalScripts.value && isWebKitInternalScript(script.sourceURL)) |
| continue; |
| this._addResource(script, suppressFilterUpdate); |
| } |
| |
| for (let script of target.extraScriptCollection) { |
| if (script.resource) |
| continue; |
| this._addResource(script, suppressFilterUpdate); |
| } |
| } |
| |
| _addLocalResourceOverrides() |
| { |
| if (!WI.NetworkManager.supportsOverridingResponses()) |
| return; |
| |
| const suppressFilterUpdate = true; |
| |
| for (let localResourceOverride of WI.networkManager.localResourceOverrides) |
| this._addResource(localResourceOverride.localResource, suppressFilterUpdate); |
| } |
| |
| _mainResourceDidChange(event) |
| { |
| if (event.target.isMainFrame()) |
| this._queryController.reset(); |
| |
| this._addResource(event.target.mainResource); |
| } |
| |
| _resourceWasAdded(event) |
| { |
| this._addResource(event.data.resource); |
| } |
| |
| _resourceWasRemoved(event) |
| { |
| this._removeResource(event.data.resource); |
| } |
| |
| _scriptAdded(event) |
| { |
| let {script} = event.data; |
| if (script.resource || script.target === WI.mainTarget) |
| return; |
| |
| this._addResource(script); |
| } |
| |
| _scriptRemoved(event) |
| { |
| let {script} = event.data; |
| if (script.resource || script.target === WI.mainTarget) |
| return; |
| |
| this._removeResource(script); |
| } |
| |
| _handleStyleSheetAdded(event) |
| { |
| let {styleSheet} = event.data; |
| if (styleSheet.origin === WI.CSSStyleSheet.Type.Author || styleSheet.anonymous) |
| return; |
| |
| this._addResource(styleSheet); |
| } |
| |
| _handleStyleSheetRemoved(event) |
| { |
| let {styleSheet} = event.data; |
| if (styleSheet.origin === WI.CSSStyleSheet.Type.Author || styleSheet.anonymous) |
| return; |
| |
| this._removeResource(styleSheet); |
| } |
| }; |
| |
| WI.OpenResourceDialog.ResourceMatchCookieDataSymbol = Symbol("open-resource-dialog-resource-match-cookie-data"); |