blob: e7aaed0e47a0b273c4fa2dbd5b7067018791b812 [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
_populateResourceTreeOutline()
{
function createHighlightedTitleFragment(title, highlightTextRanges)
{
let titleFragment = document.createDocumentFragment();
let lastIndex = 0;
for (let textRange of highlightTextRanges) {
if (textRange.startColumn > lastIndex)
titleFragment.append(title.substring(lastIndex, textRange.startColumn));
let highlightSpan = document.createElement("span");
highlightSpan.classList.add("highlighted");
highlightSpan.append(title.substring(textRange.startColumn, textRange.endColumn));
titleFragment.append(highlightSpan);
lastIndex = textRange.endColumn;
}
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);
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.matchingTextRanges);
treeElement[WI.OpenResourceDialog.ResourceMatchCookieDataSymbol] = result.cookie;
this._treeOutline.appendChild(treeElement);
}
if (this._treeOutline.children.length)
this._treeOutline.children[0].select(true, false, true, true);
}
didDismissDialog()
{
WI.Frame.removeEventListener(WI.Frame.Event.MainResourceDidChange, this._mainResourceDidChange, this);
WI.Frame.removeEventListener(WI.Frame.Event.ResourceWasAdded, this._resourceWasAdded, this);
WI.Target.removeEventListener(WI.Target.Event.ResourceAdded, this._resourceWasAdded, this);
WI.debuggerManager.removeEventListener(WI.DebuggerManager.Event.ScriptAdded, this._scriptAdded, 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.Target.addEventListener(WI.Target.Event.ResourceAdded, this._resourceWasAdded, this);
WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.ScriptAdded, this._scriptAdded, this);
if (WI.frameResourceManager.mainFrame)
this._addResourcesForFrame(WI.frameResourceManager.mainFrame);
for (let target of WI.targets) {
if (target !== WI.mainTarget)
this._addResourcesForTarget(target);
}
this._updateFilter();
this._inputElement.focus();
this._clear();
}
// Private
_handleKeydownEvent(event)
{
if (event.keyCode === WI.KeyboardShortcut.Key.Escape.keyCode) {
if (this._inputElement.value === "")
this.dismiss();
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, 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 = event.data.selectedElement;
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();
}
_addResourcesForFrame(frame)
{
const suppressFilterUpdate = true;
let frames = [frame];
while (frames.length) {
let currentFrame = frames.shift();
let resources = [currentFrame.mainResource].concat(Array.from(currentFrame.resourceCollection.items));
for (let resource of resources)
this._addResource(resource, suppressFilterUpdate);
frames = frames.concat(currentFrame.childFrameCollection.toArray());
}
}
_addResourcesForTarget(target)
{
const suppressFilterUpdate = true;
this._addResource(target.mainResource);
for (let resource of target.resourceCollection.items)
this._addResource(resource, suppressFilterUpdate);
let targetData = WI.debuggerManager.dataForTarget(target);
for (let script of targetData.scripts) {
if (script.resource)
continue;
if (isWebKitInternalScript(script.sourceURL) || isWebInspectorConsoleEvaluationScript(script.sourceURL))
continue;
this._addResource(script, suppressFilterUpdate);
}
}
_mainResourceDidChange(event)
{
if (event.target.isMainFrame())
this._queryController.reset();
this._addResource(event.target.mainResource);
}
_resourceWasAdded(event)
{
this._addResource(event.data.resource);
}
_scriptAdded(event)
{
let script = event.data.script;
if (script.resource)
return;
if (script.target === WI.mainTarget)
return;
this._addResource(script);
}
};
WI.OpenResourceDialog.ResourceMatchCookieDataSymbol = Symbol("open-resource-dialog-resource-match-cookie-data");