blob: 1f4f06481ea5216f0aaae22620b8c1e304b84327 [file] [log] [blame]
/*
* 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");