Added MacOS SDK
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.BlobUtilities = class BlobUtilities {
|
||||
static blobForContent(content, base64Encoded, mimeType)
|
||||
{
|
||||
if (base64Encoded)
|
||||
return BlobUtilities.decodeBase64ToBlob(content, mimeType);
|
||||
return BlobUtilities.textToBlob(content, mimeType);
|
||||
}
|
||||
|
||||
static decodeBase64ToBlob(base64Data, mimeType)
|
||||
{
|
||||
mimeType = mimeType || "";
|
||||
|
||||
const sliceSize = 1024;
|
||||
let byteCharacters = atob(base64Data);
|
||||
let bytesLength = byteCharacters.length;
|
||||
let slicesCount = Math.ceil(bytesLength / sliceSize);
|
||||
let byteArrays = new Array(slicesCount);
|
||||
|
||||
for (let sliceIndex = 0; sliceIndex < slicesCount; ++sliceIndex) {
|
||||
let begin = sliceIndex * sliceSize;
|
||||
let end = Math.min(begin + sliceSize, bytesLength);
|
||||
|
||||
let bytes = new Array(end - begin);
|
||||
for (let offset = begin, i = 0; offset < end; ++i, ++offset)
|
||||
bytes[i] = byteCharacters[offset].charCodeAt(0);
|
||||
|
||||
byteArrays[sliceIndex] = new Uint8Array(bytes);
|
||||
}
|
||||
|
||||
return new Blob(byteArrays, {type: mimeType});
|
||||
}
|
||||
|
||||
static textToBlob(text, mimeType)
|
||||
{
|
||||
return new Blob([text], {type: mimeType});
|
||||
}
|
||||
|
||||
static blobAsText(blob, callback)
|
||||
{
|
||||
console.assert(blob instanceof Blob);
|
||||
let fileReader = new FileReader;
|
||||
fileReader.addEventListener("loadend", () => { callback(fileReader.result); });
|
||||
fileReader.readAsText(blob);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,379 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Igalia S.L.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
if (!window.InspectorFrontendHost) {
|
||||
WI.BrowserInspectorFrontendHost = class BrowserInspectorFrontendHost {
|
||||
|
||||
constructor()
|
||||
{
|
||||
this._pendingMessages = null;
|
||||
this._socket = null;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get supportsShowCertificate()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
get isRemote()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
get inspectionLevel()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
get debuggableInfo()
|
||||
{
|
||||
return {
|
||||
debuggableType: "web-page",
|
||||
targetPlatformName: undefined,
|
||||
targetBuildVersion: undefined,
|
||||
targetProductVersion: undefined,
|
||||
targetIsSimulator: false,
|
||||
};
|
||||
}
|
||||
|
||||
get platform()
|
||||
{
|
||||
const match = navigator.platform.match(/mac|win|linux/i);
|
||||
if (match) {
|
||||
const platform = match[0].toLowerCase();
|
||||
if (platform == "win")
|
||||
return "windows";
|
||||
return platform;
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
get platformVersionName()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
get supportsDiagnosticLogging()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
get supportsWebExtensions()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
connect()
|
||||
{
|
||||
const queryParams = parseQueryString(window.location.search.substring(1));
|
||||
let url = "ws" in queryParams ? "ws://" + queryParams.ws : null;
|
||||
if (!url)
|
||||
return;
|
||||
|
||||
const socket = new WebSocket(url);
|
||||
socket.addEventListener("message", message => InspectorBackend.dispatch(message.data));
|
||||
socket.addEventListener("error", console.error);
|
||||
socket.addEventListener("open", () => { this._socket = socket; });
|
||||
socket.addEventListener("close", () => {
|
||||
this._socket = null;
|
||||
window.close();
|
||||
});
|
||||
}
|
||||
|
||||
loaded()
|
||||
{
|
||||
WI.updateVisibilityState(true);
|
||||
}
|
||||
|
||||
closeWindow()
|
||||
{
|
||||
this._windowVisible = false;
|
||||
}
|
||||
|
||||
reopen()
|
||||
{
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
reset()
|
||||
{
|
||||
this.reopen();
|
||||
}
|
||||
|
||||
bringToFront()
|
||||
{
|
||||
this._windowVisible = true;
|
||||
}
|
||||
|
||||
inspectedURLChanged(title)
|
||||
{
|
||||
document.title = title;
|
||||
}
|
||||
|
||||
showCertificate(certificate)
|
||||
{
|
||||
throw "unimplemented";
|
||||
}
|
||||
|
||||
setZoomFactor(zoom)
|
||||
{
|
||||
}
|
||||
|
||||
zoomFactor()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
setForcedAppearance(appearance)
|
||||
{
|
||||
}
|
||||
|
||||
userInterfaceLayoutDirection()
|
||||
{
|
||||
return "ltr";
|
||||
}
|
||||
|
||||
supportsDockSide(side)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
requestDockSide(side)
|
||||
{
|
||||
throw "unimplemented";
|
||||
}
|
||||
|
||||
setAttachedWindowHeight(height)
|
||||
{
|
||||
}
|
||||
|
||||
setAttachedWindowWidth(width)
|
||||
{
|
||||
}
|
||||
|
||||
setSheetRect(x, y, width, height)
|
||||
{
|
||||
}
|
||||
|
||||
startWindowDrag()
|
||||
{
|
||||
}
|
||||
|
||||
moveWindowBy(x, y)
|
||||
{
|
||||
}
|
||||
|
||||
copyText(text)
|
||||
{
|
||||
this.killText(text, false, true);
|
||||
}
|
||||
|
||||
killText(text, shouldPrependToKillRing, shouldStartNewSequence)
|
||||
{
|
||||
// FIXME: restore focus to previously focused element.
|
||||
let textarea = document.createElement("textarea");
|
||||
document.body.appendChild(textarea);
|
||||
|
||||
if (shouldStartNewSequence) {
|
||||
textarea.textContent = text;
|
||||
} else {
|
||||
textarea.select();
|
||||
if (!document.execCommand("paste"))
|
||||
console.error("BrowserInspectorFrontendHost.killText: could not paste from clipboard");
|
||||
|
||||
if (shouldPrependToKillRing)
|
||||
textarea.textContent = text + textarea.textContent;
|
||||
else
|
||||
textarea.textContent = textarea.textContent + text;
|
||||
}
|
||||
|
||||
textarea.select();
|
||||
|
||||
if (!document.execCommand("copy"))
|
||||
console.error("BrowserInspectorFrontendHost.copyText: could not copy to clipboard");
|
||||
|
||||
document.body.removeChild(textarea);
|
||||
}
|
||||
|
||||
openURLExternally(url)
|
||||
{
|
||||
window.open(url, "_blank");
|
||||
}
|
||||
|
||||
canSave(saveMode)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
save(saveDatas, forceSaveAs)
|
||||
{
|
||||
// FIXME: Create a Blob from the content, get an object URL, open it to trigger a download.
|
||||
throw "unimplemented";
|
||||
}
|
||||
|
||||
canLoad()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
load(path)
|
||||
{
|
||||
throw "unimplemented";
|
||||
}
|
||||
|
||||
getPath(file)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
canPickColorFromScreen()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
pickColorFromScreen()
|
||||
{
|
||||
throw "unimplemented";
|
||||
}
|
||||
|
||||
revealFileExternally(path)
|
||||
{
|
||||
}
|
||||
|
||||
getCurrentX(context)
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
getCurrentY(context)
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
setPath(context, path2d)
|
||||
{
|
||||
console.log("setPath", context, path2d);
|
||||
}
|
||||
|
||||
showContextMenu(event, items)
|
||||
{
|
||||
this._contextMenu = WI.SoftContextMenu(items);
|
||||
this._contextMenu.show(event);
|
||||
}
|
||||
|
||||
dispatchEventAsContextMenuEvent(event)
|
||||
{
|
||||
if (this._contextMenu)
|
||||
this._contextMenu.show(event);
|
||||
}
|
||||
|
||||
sendMessageToBackend(message)
|
||||
{
|
||||
if (!this._socket) {
|
||||
if (!this._pendingMessages)
|
||||
this._pendingMessages = [];
|
||||
this._pendingMessages.push(message);
|
||||
} else {
|
||||
this._sendPendingMessagesToBackendIfNeeded();
|
||||
this._socket.send(message);
|
||||
}
|
||||
}
|
||||
|
||||
unbufferedLog(message)
|
||||
{
|
||||
console.log(message);
|
||||
}
|
||||
|
||||
isUnderTest()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
beep()
|
||||
{
|
||||
// FIXME: Implement using Audio/AudioContext.
|
||||
}
|
||||
|
||||
inspectInspector()
|
||||
{
|
||||
throw "unimplemented";
|
||||
}
|
||||
|
||||
isBeingInspected()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
setAllowsInspectingInspector(allow)
|
||||
{
|
||||
}
|
||||
|
||||
logDiagnosticEvent(eventName, content)
|
||||
{
|
||||
throw "unimplemented";
|
||||
}
|
||||
|
||||
didShowExtensionTab(extensionID, extensionTabID, extensionFrame)
|
||||
{
|
||||
throw "unimplemented";
|
||||
}
|
||||
|
||||
didHideExtensionTab(extensionID, extensionTabID)
|
||||
{
|
||||
throw "unimplemented";
|
||||
}
|
||||
|
||||
didNavigateExtensionTab(extensionID, extensionTabID, newURL)
|
||||
{
|
||||
throw "unimplemented";
|
||||
}
|
||||
|
||||
inspectedPageDidNavigate(newURL)
|
||||
{
|
||||
throw "unimplemented";
|
||||
}
|
||||
|
||||
evaluateScriptInExtensionTab(extensionFrame, scriptSource)
|
||||
{
|
||||
throw "unimplemented";
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_sendPendingMessagesToBackendIfNeeded()
|
||||
{
|
||||
if (this._pendingMessages) {
|
||||
this._pendingMessages.forEach(message => this._socket.send(message));
|
||||
this._pendingMessages = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
InspectorFrontendHost = new WI.BrowserInspectorFrontendHost();
|
||||
|
||||
WI.dontLocalizeUserInterface = true;
|
||||
}
|
||||
@@ -0,0 +1,413 @@
|
||||
/*
|
||||
* Copyright (C) 2011 Google Inc. All rights reserved.
|
||||
* Copyright (C) 2007, 2008, 2013 Apple Inc. All rights reserved.
|
||||
* Copyright (C) 2008 Matt Lilek <webkit@mattlilek.com>
|
||||
* Copyright (C) 2009 Joseph Pecoraro
|
||||
*
|
||||
* 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.
|
||||
* 3. Neither the name of Apple Inc. ("Apple") nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE 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 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.roleSelectorForNode = function(node)
|
||||
{
|
||||
// This is proposed syntax for CSS 4 computed role selector :role(foo) and subject to change.
|
||||
// See http://lists.w3.org/Archives/Public/www-style/2013Jul/0104.html
|
||||
var title = "";
|
||||
var role = node.computedRole();
|
||||
if (role)
|
||||
title = ":role(" + role + ")";
|
||||
return title;
|
||||
};
|
||||
|
||||
WI.linkifyAccessibilityNodeReference = function(node)
|
||||
{
|
||||
if (!node)
|
||||
return null;
|
||||
// Same as linkifyNodeReference except the link text has the classnames removed...
|
||||
// ...for list brevity, and both text and title have roleSelectorForNode appended.
|
||||
var link = WI.linkifyNodeReference(node);
|
||||
var tagIdSelector = link.title;
|
||||
var classSelectorIndex = tagIdSelector.indexOf(".");
|
||||
if (classSelectorIndex > -1)
|
||||
tagIdSelector = tagIdSelector.substring(0, classSelectorIndex);
|
||||
var roleSelector = WI.roleSelectorForNode(node);
|
||||
link.textContent = tagIdSelector + roleSelector;
|
||||
link.title += roleSelector;
|
||||
return link;
|
||||
};
|
||||
|
||||
WI.linkifyStyleable = function(styleable)
|
||||
{
|
||||
console.assert(styleable instanceof WI.DOMStyleable, styleable);
|
||||
let displayName = styleable.displayName;
|
||||
let link = document.createElement("span");
|
||||
link.append(displayName);
|
||||
return WI.linkifyNodeReferenceElement(styleable.node, link, {displayName});
|
||||
};
|
||||
|
||||
WI.linkifyNodeReference = function(node, options = {})
|
||||
{
|
||||
let displayName = node.displayName;
|
||||
if (!isNaN(options.maxLength))
|
||||
displayName = displayName.truncate(options.maxLength);
|
||||
|
||||
let link = document.createElement("span");
|
||||
link.append(displayName);
|
||||
return WI.linkifyNodeReferenceElement(node, link, {...options, displayName});
|
||||
};
|
||||
|
||||
WI.linkifyNodeReferenceElement = function(node, element, options = {})
|
||||
{
|
||||
element.setAttribute("role", "link");
|
||||
element.title = options.displayName || node.displayName;
|
||||
|
||||
let nodeType = node.nodeType();
|
||||
if (!options.ignoreClick && (nodeType !== Node.DOCUMENT_NODE || node.parentNode) && nodeType !== Node.TEXT_NODE)
|
||||
element.classList.add("node-link");
|
||||
|
||||
WI.bindInteractionsForNodeToElement(node, element, options);
|
||||
|
||||
return element;
|
||||
};
|
||||
|
||||
WI.bindInteractionsForNodeToElement = function(node, element, options = {}) {
|
||||
if (!options.ignoreClick) {
|
||||
element.addEventListener("click", (event) => {
|
||||
WI.domManager.inspectElement(node.id, {
|
||||
initiatorHint: WI.TabBrowser.TabNavigationInitiator.LinkClick,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
element.addEventListener("mouseover", (event) => {
|
||||
node.highlight();
|
||||
});
|
||||
|
||||
element.addEventListener("mouseout", (event) => {
|
||||
WI.domManager.hideDOMNodeHighlight();
|
||||
});
|
||||
|
||||
element.addEventListener("contextmenu", (event) => {
|
||||
let contextMenu = WI.ContextMenu.createFromEvent(event);
|
||||
WI.appendContextMenuItemsForDOMNode(contextMenu, node, options);
|
||||
});
|
||||
};
|
||||
|
||||
function createSVGElement(tagName)
|
||||
{
|
||||
return document.createElementNS("http://www.w3.org/2000/svg", tagName);
|
||||
}
|
||||
|
||||
WI.cssPath = function(node, options = {})
|
||||
{
|
||||
console.assert(node instanceof WI.DOMNode, "Expected a DOMNode.");
|
||||
if (node.nodeType() !== Node.ELEMENT_NODE)
|
||||
return "";
|
||||
|
||||
let suffix = "";
|
||||
if (node.isPseudoElement()) {
|
||||
suffix = "::" + node.pseudoType();
|
||||
node = node.parentNode;
|
||||
}
|
||||
|
||||
let components = [];
|
||||
while (node) {
|
||||
let component = WI.cssPathComponent(node, options);
|
||||
if (!component)
|
||||
break;
|
||||
components.push(component);
|
||||
if (component.done)
|
||||
break;
|
||||
node = node.parentNode;
|
||||
}
|
||||
|
||||
components.reverse();
|
||||
return components.map((x) => x.value).join(" > ") + suffix;
|
||||
};
|
||||
|
||||
WI.cssPathComponent = function(node, options = {})
|
||||
{
|
||||
console.assert(node instanceof WI.DOMNode, "Expected a DOMNode.");
|
||||
console.assert(!node.isPseudoElement());
|
||||
if (node.nodeType() !== Node.ELEMENT_NODE)
|
||||
return null;
|
||||
|
||||
let nodeName = node.nodeNameInCorrectCase();
|
||||
|
||||
// Root node does not have siblings.
|
||||
if (!node.parentNode || node.parentNode.nodeType() === Node.DOCUMENT_NODE)
|
||||
return {value: nodeName, done: true};
|
||||
|
||||
if (options.full) {
|
||||
function getUniqueAttributes(domNode) {
|
||||
let uniqueAttributes = new Map;
|
||||
for (let attribute of domNode.attributes()) {
|
||||
let values = [attribute.value];
|
||||
if (attribute.name === "id" || attribute.name === "class")
|
||||
values = attribute.value.split(/\s+/);
|
||||
uniqueAttributes.set(attribute.name, new Set(values));
|
||||
}
|
||||
return uniqueAttributes;
|
||||
}
|
||||
|
||||
let nodeIndex = 0;
|
||||
let needsNthChild = false;
|
||||
let uniqueAttributes = getUniqueAttributes(node);
|
||||
node.parentNode.children.forEach((child, i) => {
|
||||
if (child.nodeType() !== Node.ELEMENT_NODE)
|
||||
return;
|
||||
|
||||
if (child === node) {
|
||||
nodeIndex = i;
|
||||
return;
|
||||
}
|
||||
|
||||
if (needsNthChild || child.nodeNameInCorrectCase() !== nodeName)
|
||||
return;
|
||||
|
||||
let childUniqueAttributes = getUniqueAttributes(child);
|
||||
let subsetCount = 0;
|
||||
for (let [name, values] of uniqueAttributes) {
|
||||
let childValues = childUniqueAttributes.get(name);
|
||||
if (childValues && values.size <= childValues.size && values.isSubsetOf(childValues))
|
||||
++subsetCount;
|
||||
}
|
||||
|
||||
if (subsetCount === uniqueAttributes.size)
|
||||
needsNthChild = true;
|
||||
});
|
||||
|
||||
function selectorForAttribute(values, prefix = "", shouldCSSEscape = false) {
|
||||
if (!values || !values.size)
|
||||
return "";
|
||||
values = Array.from(values);
|
||||
values = values.filter((value) => value && value.length);
|
||||
if (!values.length)
|
||||
return "";
|
||||
values = values.map((value) => shouldCSSEscape ? CSS.escape(value) : value.escapeCharacters("\""));
|
||||
return prefix + values.join(prefix);
|
||||
}
|
||||
|
||||
let selector = nodeName;
|
||||
selector += selectorForAttribute(uniqueAttributes.get("id"), "#", true);
|
||||
selector += selectorForAttribute(uniqueAttributes.get("class"), ".", true);
|
||||
for (let [attribute, values] of uniqueAttributes) {
|
||||
if (attribute !== "id" && attribute !== "class")
|
||||
selector += `[${attribute}="${selectorForAttribute(values)}"]`;
|
||||
}
|
||||
|
||||
if (needsNthChild)
|
||||
selector += `:nth-child(${nodeIndex + 1})`;
|
||||
|
||||
return {value: selector, done: false};
|
||||
}
|
||||
|
||||
let lowerNodeName = node.nodeName().toLowerCase();
|
||||
|
||||
// html, head, and body are unique nodes.
|
||||
if (lowerNodeName === "body" || lowerNodeName === "head" || lowerNodeName === "html")
|
||||
return {value: nodeName, done: true};
|
||||
|
||||
// #id is unique.
|
||||
let id = node.getAttribute("id");
|
||||
if (id)
|
||||
return {value: node.escapedIdSelector, done: true};
|
||||
|
||||
// Find uniqueness among siblings.
|
||||
// - look for a unique className
|
||||
// - look for a unique tagName
|
||||
// - fallback to nth-child()
|
||||
|
||||
function classNames(node) {
|
||||
let classAttribute = node.getAttribute("class");
|
||||
return classAttribute ? classAttribute.trim().split(/\s+/) : [];
|
||||
}
|
||||
|
||||
let nthChildIndex = -1;
|
||||
let hasUniqueTagName = true;
|
||||
let uniqueClasses = new Set(classNames(node));
|
||||
|
||||
let siblings = node.parentNode.children;
|
||||
let elementIndex = 0;
|
||||
for (let sibling of siblings) {
|
||||
if (sibling.nodeType() !== Node.ELEMENT_NODE)
|
||||
continue;
|
||||
|
||||
elementIndex++;
|
||||
if (sibling === node) {
|
||||
nthChildIndex = elementIndex;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sibling.nodeNameInCorrectCase() === nodeName)
|
||||
hasUniqueTagName = false;
|
||||
|
||||
if (uniqueClasses.size) {
|
||||
let siblingClassNames = classNames(sibling);
|
||||
for (let className of siblingClassNames)
|
||||
uniqueClasses.delete(className);
|
||||
}
|
||||
}
|
||||
|
||||
let selector = nodeName;
|
||||
if (lowerNodeName === "input" && node.getAttribute("type") && !uniqueClasses.size)
|
||||
selector += `[type="${node.getAttribute("type")}"]`;
|
||||
if (!hasUniqueTagName) {
|
||||
if (uniqueClasses.size)
|
||||
selector += node.escapedClassSelector;
|
||||
else
|
||||
selector += `:nth-child(${nthChildIndex})`;
|
||||
}
|
||||
|
||||
return {value: selector, done: false};
|
||||
};
|
||||
|
||||
WI.xpath = function(node)
|
||||
{
|
||||
console.assert(node instanceof WI.DOMNode, "Expected a DOMNode.");
|
||||
|
||||
if (node.nodeType() === Node.DOCUMENT_NODE)
|
||||
return "/";
|
||||
|
||||
let components = [];
|
||||
while (node) {
|
||||
let component = WI.xpathComponent(node);
|
||||
if (!component)
|
||||
break;
|
||||
components.push(component);
|
||||
if (component.done)
|
||||
break;
|
||||
node = node.parentNode;
|
||||
}
|
||||
|
||||
components.reverse();
|
||||
|
||||
let prefix = components.length && components[0].done ? "" : "/";
|
||||
return prefix + components.map((x) => x.value).join("/");
|
||||
};
|
||||
|
||||
WI.xpathComponent = function(node)
|
||||
{
|
||||
console.assert(node instanceof WI.DOMNode, "Expected a DOMNode.");
|
||||
|
||||
let index = WI.xpathIndex(node);
|
||||
if (index === -1)
|
||||
return null;
|
||||
|
||||
let value;
|
||||
|
||||
switch (node.nodeType()) {
|
||||
case Node.DOCUMENT_NODE:
|
||||
return {value: "", done: true};
|
||||
case Node.ELEMENT_NODE:
|
||||
var id = node.getAttribute("id");
|
||||
if (id)
|
||||
return {value: `//*[@id="${id}"]`, done: true};
|
||||
value = node.localName();
|
||||
break;
|
||||
case Node.ATTRIBUTE_NODE:
|
||||
value = `@${node.nodeName()}`;
|
||||
break;
|
||||
case Node.TEXT_NODE:
|
||||
case Node.CDATA_SECTION_NODE:
|
||||
value = "text()";
|
||||
break;
|
||||
case Node.COMMENT_NODE:
|
||||
value = "comment()";
|
||||
break;
|
||||
case Node.PROCESSING_INSTRUCTION_NODE:
|
||||
value = "processing-instruction()";
|
||||
break;
|
||||
default:
|
||||
value = "";
|
||||
break;
|
||||
}
|
||||
|
||||
if (index > 0)
|
||||
value += `[${index}]`;
|
||||
|
||||
return {value, done: false};
|
||||
};
|
||||
|
||||
WI.xpathIndex = function(node)
|
||||
{
|
||||
// Root node.
|
||||
if (!node.parentNode)
|
||||
return 0;
|
||||
|
||||
// No siblings.
|
||||
let siblings = node.parentNode.children;
|
||||
if (siblings.length <= 1)
|
||||
return 0;
|
||||
|
||||
// Find uniqueness among siblings.
|
||||
// - look for a unique localName
|
||||
// - fallback to index
|
||||
|
||||
function isSimiliarNode(a, b) {
|
||||
if (a === b)
|
||||
return true;
|
||||
|
||||
let aType = a.nodeType();
|
||||
let bType = b.nodeType();
|
||||
|
||||
if (aType === Node.ELEMENT_NODE && bType === Node.ELEMENT_NODE)
|
||||
return a.localName() === b.localName();
|
||||
|
||||
// XPath CDATA and text() are the same.
|
||||
if (aType === Node.CDATA_SECTION_NODE)
|
||||
return aType === Node.TEXT_NODE;
|
||||
if (bType === Node.CDATA_SECTION_NODE)
|
||||
return bType === Node.TEXT_NODE;
|
||||
|
||||
return aType === bType;
|
||||
}
|
||||
|
||||
let unique = true;
|
||||
let xPathIndex = -1;
|
||||
|
||||
let xPathIndexCounter = 1; // XPath indices start at 1.
|
||||
for (let sibling of siblings) {
|
||||
if (!isSimiliarNode(node, sibling))
|
||||
continue;
|
||||
|
||||
if (node === sibling) {
|
||||
xPathIndex = xPathIndexCounter;
|
||||
if (!unique)
|
||||
return xPathIndex;
|
||||
} else {
|
||||
unique = false;
|
||||
if (xPathIndex !== -1)
|
||||
return xPathIndex;
|
||||
}
|
||||
|
||||
xPathIndexCounter++;
|
||||
}
|
||||
|
||||
if (unique)
|
||||
return 0;
|
||||
|
||||
console.assert(xPathIndex > 0, "Should have found the node.");
|
||||
return xPathIndex;
|
||||
};
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.
|
||||
*/
|
||||
|
||||
// Debouncer wraps a function and continues to delay its invocation as long as
|
||||
// clients continue to delay its firing. The most recent delay call overrides
|
||||
// previous calls. Delays may be timeouts, animation frames, or microtasks.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// let debouncer = new Debouncer(() => { this.refresh() });
|
||||
// element.addEventListener("keydown", (event) => { debouncer.delayForTime(100); });
|
||||
//
|
||||
// Will ensure `refresh` will not happen until no keyevent has happened in 100ms:
|
||||
//
|
||||
// 0ms 100ms 200ms 300ms 400ms
|
||||
// time: |-------------|-------------|-------------|-------------|
|
||||
// delay: ^ ^ ^ ^ ^ ^ ^ ^
|
||||
// refreshes: * (1)
|
||||
//
|
||||
// When the wrapped function is actually called, it will be given the most recent set of arguments.
|
||||
|
||||
class Debouncer
|
||||
{
|
||||
constructor(callback)
|
||||
{
|
||||
console.assert(typeof callback === "function");
|
||||
|
||||
this._callback = callback;
|
||||
this._lastArguments = [];
|
||||
|
||||
this._timeoutIdentifier = undefined;
|
||||
this._animationFrameIdentifier = undefined;
|
||||
this._promiseIdentifier = undefined;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
force()
|
||||
{
|
||||
this._lastArguments = arguments;
|
||||
this._execute();
|
||||
}
|
||||
|
||||
delayForTime(time, ...args)
|
||||
{
|
||||
console.assert(time >= 0);
|
||||
|
||||
this.cancel();
|
||||
|
||||
this._lastArguments = args;
|
||||
|
||||
this._timeoutIdentifier = setTimeout(() => {
|
||||
this._execute();
|
||||
}, time);
|
||||
}
|
||||
|
||||
delayForFrame()
|
||||
{
|
||||
this.cancel();
|
||||
|
||||
this._lastArguments = arguments;
|
||||
|
||||
this._animationFrameIdentifier = requestAnimationFrame(() => {
|
||||
this._execute();
|
||||
});
|
||||
}
|
||||
|
||||
delayForMicrotask()
|
||||
{
|
||||
this.cancel();
|
||||
|
||||
this._lastArguments = arguments;
|
||||
|
||||
let promiseIdentifier = Symbol("next-microtask");
|
||||
|
||||
this._promiseIdentifier = promiseIdentifier;
|
||||
|
||||
queueMicrotask(() => {
|
||||
if (this._promiseIdentifier === promiseIdentifier)
|
||||
this._execute();
|
||||
});
|
||||
}
|
||||
|
||||
cancel()
|
||||
{
|
||||
this._lastArguments = [];
|
||||
|
||||
if (this._timeoutIdentifier) {
|
||||
clearTimeout(this._timeoutIdentifier);
|
||||
this._timeoutIdentifier = undefined;
|
||||
}
|
||||
|
||||
if (this._animationFrameIdentifier) {
|
||||
cancelAnimationFrame(this._animationFrameIdentifier);
|
||||
this._animationFrameIdentifier = undefined;
|
||||
}
|
||||
|
||||
if (this._promiseIdentifier)
|
||||
this._promiseIdentifier = undefined;
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_execute()
|
||||
{
|
||||
let args = this._lastArguments;
|
||||
|
||||
this.cancel();
|
||||
|
||||
this._callback.apply(undefined, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.DebuggableType = {
|
||||
ITML: "itml",
|
||||
JavaScript: "javascript",
|
||||
Page: "page",
|
||||
ServiceWorker: "service-worker",
|
||||
WebPage: "web-page",
|
||||
};
|
||||
|
||||
WI.DebuggableType.fromString = function(type) {
|
||||
switch (type) {
|
||||
case "itml":
|
||||
return WI.DebuggableType.ITML;
|
||||
case "javascript":
|
||||
return WI.DebuggableType.JavaScript;
|
||||
case "page":
|
||||
return WI.DebuggableType.Page;
|
||||
case "service-worker":
|
||||
return WI.DebuggableType.ServiceWorker;
|
||||
case "web-page":
|
||||
return WI.DebuggableType.WebPage;
|
||||
}
|
||||
|
||||
console.assert(false, "Unknown debuggable type", type);
|
||||
return null;
|
||||
};
|
||||
|
||||
WI.DebuggableType.supportedTargetTypes = function(debuggableType) {
|
||||
let targetTypes = new Set;
|
||||
|
||||
switch (debuggableType) {
|
||||
case WI.DebuggableType.ITML:
|
||||
targetTypes.add(WI.TargetType.ITML);
|
||||
break;
|
||||
|
||||
case WI.DebuggableType.JavaScript:
|
||||
targetTypes.add(WI.TargetType.JavaScript);
|
||||
break;
|
||||
|
||||
case WI.DebuggableType.Page:
|
||||
targetTypes.add(WI.TargetType.Page);
|
||||
targetTypes.add(WI.TargetType.Worker);
|
||||
break;
|
||||
|
||||
case WI.DebuggableType.ServiceWorker:
|
||||
targetTypes.add(WI.TargetType.ServiceWorker);
|
||||
break;
|
||||
|
||||
case WI.DebuggableType.WebPage:
|
||||
targetTypes.add(WI.TargetType.Page);
|
||||
targetTypes.add(WI.TargetType.WebPage);
|
||||
targetTypes.add(WI.TargetType.Worker);
|
||||
break;
|
||||
}
|
||||
|
||||
console.assert(targetTypes.size, "Unknown debuggable type", debuggableType);
|
||||
return targetTypes;
|
||||
};
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
|
||||
* Copyright (C) 2013, 2014 University of Washington. 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. ``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
|
||||
* 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.EventListener = class EventListener
|
||||
{
|
||||
constructor(thisObject, fireOnce)
|
||||
{
|
||||
this._thisObject = thisObject;
|
||||
this._emitter = null;
|
||||
this._callback = null;
|
||||
this._fireOnce = fireOnce;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
connect(emitter, type, callback, usesCapture)
|
||||
{
|
||||
console.assert(!this._emitter && !this._callback, "EventListener already bound to a callback.", this);
|
||||
console.assert(emitter, `Missing event emitter for event: ${type}.`);
|
||||
console.assert(type, "Missing event type.");
|
||||
console.assert(callback, `Missing callback for event: ${type}.`);
|
||||
var emitterIsValid = emitter && (emitter instanceof WI.Object || emitter instanceof Node || (typeof emitter.addEventListener === "function"));
|
||||
console.assert(emitterIsValid, "Event emitter ", emitter, ` (type: ${type}) is null or does not implement Node or WI.Object.`);
|
||||
|
||||
if (!emitterIsValid || !type || !callback)
|
||||
return;
|
||||
|
||||
this._emitter = emitter;
|
||||
this._type = type;
|
||||
this._usesCapture = !!usesCapture;
|
||||
|
||||
if (emitter instanceof Node)
|
||||
callback = callback.bind(this._thisObject);
|
||||
|
||||
if (this._fireOnce) {
|
||||
var listener = this;
|
||||
this._callback = function() {
|
||||
listener.disconnect();
|
||||
callback.apply(this, arguments);
|
||||
};
|
||||
} else
|
||||
this._callback = callback;
|
||||
|
||||
if (this._emitter instanceof Node)
|
||||
this._emitter.addEventListener(this._type, this._callback, this._usesCapture);
|
||||
else
|
||||
this._emitter.addEventListener(this._type, this._callback, this._thisObject);
|
||||
}
|
||||
|
||||
disconnect()
|
||||
{
|
||||
console.assert(this._emitter && this._callback, "EventListener is not bound to a callback.", this);
|
||||
|
||||
if (!this._emitter || !this._callback)
|
||||
return;
|
||||
|
||||
if (this._emitter instanceof Node)
|
||||
this._emitter.removeEventListener(this._type, this._callback, this._usesCapture);
|
||||
else
|
||||
this._emitter.removeEventListener(this._type, this._callback, this._thisObject);
|
||||
|
||||
if (this._fireOnce)
|
||||
delete this._thisObject;
|
||||
delete this._emitter;
|
||||
delete this._type;
|
||||
delete this._callback;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
|
||||
* Copyright (C) 2013, 2014 University of Washington. 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. ``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
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// This class supports adding and removing many listeners at once.
|
||||
// Add DOM or Inspector event listeners to the set using `register()`.
|
||||
// Use `install()` and `uninstall()` to enable or disable all listeners
|
||||
// in the set at once.
|
||||
|
||||
WI.EventListenerSet = class EventListenerSet
|
||||
{
|
||||
constructor(defaultThisObject, name)
|
||||
{
|
||||
this.name = name;
|
||||
this._defaultThisObject = defaultThisObject;
|
||||
|
||||
this._listeners = [];
|
||||
this._installed = false;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
register(emitter, type, callback, thisObject, usesCapture)
|
||||
{
|
||||
console.assert(emitter, `Missing event emitter for event: ${type}.`);
|
||||
console.assert(type, "Missing event type.");
|
||||
console.assert(callback, `Missing callback for event: ${type}.`);
|
||||
var emitterIsValid = emitter && (emitter instanceof WI.Object || emitter instanceof Node || (typeof emitter.addEventListener === "function"));
|
||||
console.assert(emitterIsValid, "Event emitter ", emitter, ` (type: ${type}) is null or does not implement Node or WI.Object.`);
|
||||
|
||||
if (!emitterIsValid || !type || !callback)
|
||||
return;
|
||||
|
||||
this._listeners.push({listener: new WI.EventListener(thisObject || this._defaultThisObject), emitter, type, callback, usesCapture});
|
||||
}
|
||||
|
||||
unregister()
|
||||
{
|
||||
if (this._installed)
|
||||
this.uninstall();
|
||||
this._listeners = [];
|
||||
}
|
||||
|
||||
install()
|
||||
{
|
||||
console.assert(!this._installed, "Already installed listener group: " + this.name);
|
||||
if (this._installed)
|
||||
return;
|
||||
|
||||
this._installed = true;
|
||||
|
||||
for (var data of this._listeners)
|
||||
data.listener.connect(data.emitter, data.type, data.callback, data.usesCapture);
|
||||
}
|
||||
|
||||
uninstall(unregisterListeners)
|
||||
{
|
||||
console.assert(this._installed, "Trying to uninstall listener group " + this.name + ", but it isn't installed.");
|
||||
if (!this._installed)
|
||||
return;
|
||||
|
||||
this._installed = false;
|
||||
|
||||
for (var data of this._listeners)
|
||||
data.listener.disconnect();
|
||||
|
||||
if (unregisterListeners)
|
||||
this._listeners = [];
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.FileUtilities = class FileUtilities {
|
||||
static screenshotString()
|
||||
{
|
||||
let date = new Date;
|
||||
let values = [
|
||||
date.getFullYear(),
|
||||
Number.zeroPad(date.getMonth() + 1, 2),
|
||||
Number.zeroPad(date.getDate(), 2),
|
||||
Number.zeroPad(date.getHours(), 2),
|
||||
Number.zeroPad(date.getMinutes(), 2),
|
||||
Number.zeroPad(date.getSeconds(), 2),
|
||||
];
|
||||
return WI.UIString("Screen Shot %s-%s-%s at %s.%s.%s").format(...values);
|
||||
}
|
||||
|
||||
static sanitizeFilename(filename)
|
||||
{
|
||||
return filename.replace(/:+/g, "-");
|
||||
}
|
||||
|
||||
static inspectorURLForFilename(filename)
|
||||
{
|
||||
return "web-inspector:///" + encodeURIComponent(FileUtilities.sanitizeFilename(filename));
|
||||
}
|
||||
|
||||
static canSave(saveMode)
|
||||
{
|
||||
console.assert(Object.values(WI.FileUtilities.SaveMode).includes(saveMode), saveMode);
|
||||
return InspectorFrontendHost.canSave(saveMode);
|
||||
}
|
||||
|
||||
static async save(saveMode, fileVariants, forceSaveAs)
|
||||
{
|
||||
console.assert(WI.FileUtilities.canSave(saveMode), saveMode);
|
||||
|
||||
console.assert(fileVariants);
|
||||
if (!fileVariants) {
|
||||
InspectorFrontendHost.beep();
|
||||
return;
|
||||
}
|
||||
|
||||
let isFileVariantsMode = saveMode === WI.FileUtilities.SaveMode.FileVariants;
|
||||
if (isFileVariantsMode)
|
||||
forceSaveAs = true;
|
||||
|
||||
if (typeof fileVariants.customSaveHandler === "function") {
|
||||
fileVariants.customSaveHandler(forceSaveAs);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isFileVariantsMode && !Array.isArray(fileVariants))
|
||||
fileVariants = [fileVariants];
|
||||
|
||||
console.assert(Array.isArray(fileVariants), fileVariants);
|
||||
if (!Array.isArray(fileVariants)) {
|
||||
InspectorFrontendHost.beep();
|
||||
return;
|
||||
}
|
||||
|
||||
let promises = fileVariants.map((fileVariant) => {
|
||||
let content = fileVariant.content;
|
||||
console.assert(content, fileVariant);
|
||||
if (!content)
|
||||
return null;
|
||||
|
||||
let displayType = fileVariant.displayType || "";
|
||||
console.assert(!isFileVariantsMode || fileVariant.displayType, fileVariant);
|
||||
if (!fileVariant.displayType && isFileVariantsMode)
|
||||
return null;
|
||||
|
||||
let suggestedName = fileVariant.suggestedName;
|
||||
if (!suggestedName) {
|
||||
let url = fileVariant.url || "";
|
||||
suggestedName = parseURL(url).lastPathComponent;
|
||||
if (!suggestedName) {
|
||||
suggestedName = WI.UIString("Untitled");
|
||||
let dataURLTypeMatch = /^data:([^;]+)/.exec(url);
|
||||
if (dataURLTypeMatch) {
|
||||
let fileExtension = WI.fileExtensionForMIMEType(dataURLTypeMatch[1]);
|
||||
if (fileExtension)
|
||||
suggestedName += "." + fileExtension;
|
||||
}
|
||||
}
|
||||
}
|
||||
let url = WI.FileUtilities.inspectorURLForFilename(suggestedName);
|
||||
|
||||
if (typeof content === "string") {
|
||||
return Promise.resolve({
|
||||
displayType,
|
||||
url,
|
||||
content,
|
||||
base64Encoded: !!fileVariant.base64Encoded,
|
||||
});
|
||||
}
|
||||
|
||||
let wrappedPromise = new WI.WrappedPromise;
|
||||
let fileReader = new FileReader;
|
||||
fileReader.addEventListener("loadend", () => {
|
||||
wrappedPromise.resolve({
|
||||
displayType,
|
||||
url,
|
||||
content: parseDataURL(fileReader.result).data,
|
||||
base64Encoded: true,
|
||||
});
|
||||
});
|
||||
fileReader.readAsDataURL(content);
|
||||
return wrappedPromise.promise;
|
||||
});
|
||||
if (promises.includes(null)) {
|
||||
InspectorFrontendHost.beep();
|
||||
return;
|
||||
}
|
||||
|
||||
let saveDatas = await Promise.all(promises);
|
||||
|
||||
console.assert(isFileVariantsMode || saveDatas.length === 1, saveDatas);
|
||||
console.assert(!isFileVariantsMode || new Set(saveDatas.map((saveData) => saveData.displayType)).size === saveDatas.length, saveDatas);
|
||||
console.assert(!isFileVariantsMode || new Set(saveDatas.map((saveData) => WI.urlWithoutExtension(saveData.url))).size === 1, saveDatas);
|
||||
|
||||
InspectorFrontendHost.save(saveDatas, !!forceSaveAs);
|
||||
}
|
||||
|
||||
static import(callback, {multiple} = {})
|
||||
{
|
||||
let inputElement = document.createElement("input");
|
||||
inputElement.type = "file";
|
||||
inputElement.value = null;
|
||||
inputElement.multiple = !!multiple;
|
||||
inputElement.addEventListener("change", (event) => {
|
||||
callback(inputElement.files);
|
||||
});
|
||||
|
||||
inputElement.click();
|
||||
|
||||
// Cache the last used import element so that it doesn't get GCd while the native file
|
||||
// picker is shown, which would prevent the "change" event listener from firing.
|
||||
FileUtilities.importInputElement = inputElement;
|
||||
}
|
||||
|
||||
static importText(callback, options = {})
|
||||
{
|
||||
FileUtilities.import((files) => {
|
||||
FileUtilities.readText(files, callback);
|
||||
}, options);
|
||||
}
|
||||
|
||||
static importJSON(callback, options = {})
|
||||
{
|
||||
FileUtilities.import((files) => {
|
||||
FileUtilities.readJSON(files, callback);
|
||||
}, options);
|
||||
}
|
||||
|
||||
static importData(callback, options = {})
|
||||
{
|
||||
FileUtilities.import((files) => {
|
||||
FileUtilities.readData(files, callback);
|
||||
}, options);
|
||||
}
|
||||
|
||||
static async readText(fileOrList, callback)
|
||||
{
|
||||
await FileUtilities._read(fileOrList, async (file, result) => {
|
||||
await new Promise((resolve, reject) => {
|
||||
let reader = new FileReader;
|
||||
reader.addEventListener("loadend", (event) => {
|
||||
result.text = reader.result;
|
||||
resolve(event);
|
||||
});
|
||||
reader.addEventListener("error", reject);
|
||||
reader.readAsText(file);
|
||||
});
|
||||
}, callback);
|
||||
}
|
||||
|
||||
static async readJSON(fileOrList, callback)
|
||||
{
|
||||
await WI.FileUtilities.readText(fileOrList, async (result) => {
|
||||
if (result.text && !result.error) {
|
||||
try {
|
||||
result.json = JSON.parse(result.text);
|
||||
} catch (e) {
|
||||
result.error = e;
|
||||
}
|
||||
}
|
||||
|
||||
await callback(result);
|
||||
});
|
||||
}
|
||||
|
||||
static async readData(fileOrList, callback)
|
||||
{
|
||||
await FileUtilities._read(fileOrList, async (file, result) => {
|
||||
await new Promise((resolve, reject) => {
|
||||
let reader = new FileReader;
|
||||
reader.addEventListener("loadend", (event) => {
|
||||
let {mimeType, base64, data} = parseDataURL(reader.result);
|
||||
|
||||
// In case no mime type was determined, try to derive one from the file extension.
|
||||
if (!mimeType || mimeType === "text/plain") {
|
||||
let extension = WI.fileExtensionForFilename(result.filename);
|
||||
if (extension)
|
||||
mimeType = WI.mimeTypeForFileExtension(extension);
|
||||
}
|
||||
|
||||
result.mimeType = mimeType;
|
||||
result.base64Encoded = base64;
|
||||
result.content = data;
|
||||
|
||||
resolve(event);
|
||||
});
|
||||
reader.addEventListener("error", reject);
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
}, callback);
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
static async _read(fileOrList, operation, callback)
|
||||
{
|
||||
console.assert(fileOrList instanceof File || fileOrList instanceof FileList);
|
||||
|
||||
let files = [];
|
||||
if (fileOrList instanceof File)
|
||||
files.push(fileOrList);
|
||||
else if (fileOrList instanceof FileList)
|
||||
files = Array.from(fileOrList);
|
||||
|
||||
for (let file of files) {
|
||||
let result = {
|
||||
filename: file.name,
|
||||
};
|
||||
|
||||
try {
|
||||
await operation(file, result);
|
||||
} catch (e) {
|
||||
result.error = e;
|
||||
}
|
||||
|
||||
await callback(result);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Keep in sync with `InspectorFrontendClient::SaveMode` and `InspectorFrontendHost::SaveMode`.
|
||||
WI.FileUtilities.SaveMode = {
|
||||
SingleFile: "single-file",
|
||||
FileVariants: "file-variants",
|
||||
};
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.HTTPUtilities = class HTTPUtilities {
|
||||
static statusTextForStatusCode(code)
|
||||
{
|
||||
console.assert(typeof code === "number");
|
||||
|
||||
switch (code) {
|
||||
case 0: return "OK";
|
||||
case 100: return "Continue";
|
||||
case 101: return "Switching Protocols";
|
||||
case 200: return "OK";
|
||||
case 201: return "Created";
|
||||
case 202: return "Accepted";
|
||||
case 203: return "Non-Authoritative Information";
|
||||
case 204: return "No Content";
|
||||
case 205: return "Reset Content";
|
||||
case 206: return "Partial Content";
|
||||
case 207: return "Multi-Status";
|
||||
case 300: return "Multiple Choices";
|
||||
case 301: return "Moved Permanently";
|
||||
case 302: return "Found";
|
||||
case 303: return "See Other";
|
||||
case 304: return "Not Modified";
|
||||
case 305: return "Use Proxy";
|
||||
case 307: return "Temporary Redirect";
|
||||
case 308: return "Permanent Redirect";
|
||||
case 400: return "Bad Request";
|
||||
case 401: return "Unauthorized";
|
||||
case 402: return "Payment Required";
|
||||
case 403: return "Forbidden";
|
||||
case 404: return "Not Found";
|
||||
case 405: return "Method Not Allowed";
|
||||
case 406: return "Not Acceptable";
|
||||
case 407: return "Proxy Authentication Required";
|
||||
case 408: return "Request Time-out";
|
||||
case 409: return "Conflict";
|
||||
case 410: return "Gone";
|
||||
case 411: return "Length Required";
|
||||
case 412: return "Precondition Failed";
|
||||
case 413: return "Request Entity Too Large";
|
||||
case 414: return "Request-URI Too Large";
|
||||
case 415: return "Unsupported Media Type";
|
||||
case 416: return "Requested range not satisfiable";
|
||||
case 417: return "Expectation Failed";
|
||||
case 500: return "Internal Server Error";
|
||||
case 501: return "Not Implemented";
|
||||
case 502: return "Bad Gateway";
|
||||
case 503: return "Service Unavailable";
|
||||
case 504: return "Gateway Time-out";
|
||||
case 505: return "HTTP Version not supported";
|
||||
}
|
||||
|
||||
if (code < 200)
|
||||
return "Continue";
|
||||
if (code < 300)
|
||||
return "OK";
|
||||
if (code < 400)
|
||||
return "Multiple Choices";
|
||||
if (code < 500)
|
||||
return "Bad Request";
|
||||
|
||||
return "Internal Server Error";
|
||||
}
|
||||
};
|
||||
|
||||
WI.HTTPUtilities.RequestMethod = {
|
||||
CONNECT: "CONNECT",
|
||||
DELETE: "DELETE",
|
||||
GET: "GET",
|
||||
HEAD: "HEAD",
|
||||
OPTIONS: "OPTIONS",
|
||||
PATCH: "PATCH",
|
||||
POST: "POST",
|
||||
PUT: "PUT",
|
||||
TRACE: "TRACE",
|
||||
};
|
||||
|
||||
WI.HTTPUtilities.RequestMethodsWithBody = new Set([
|
||||
WI.HTTPUtilities.RequestMethod.DELETE,
|
||||
WI.HTTPUtilities.RequestMethod.PATCH,
|
||||
WI.HTTPUtilities.RequestMethod.POST,
|
||||
WI.HTTPUtilities.RequestMethod.PUT,
|
||||
]);
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.
|
||||
*/
|
||||
|
||||
// The following should only be used for response local override file mapping.
|
||||
|
||||
Object.defineProperty(File.prototype, "getPath", {
|
||||
value() {
|
||||
return InspectorFrontendHost.getPath(this);
|
||||
},
|
||||
});
|
||||
|
||||
// The following should only be used for rendering (and interacting with) canvas 2D recordings.
|
||||
|
||||
Object.defineProperty(CanvasRenderingContext2D.prototype, "currentX", {
|
||||
get() {
|
||||
return InspectorFrontendHost.getCurrentX(this);
|
||||
},
|
||||
});
|
||||
|
||||
Object.defineProperty(CanvasRenderingContext2D.prototype, "currentY", {
|
||||
get() {
|
||||
return InspectorFrontendHost.getCurrentY(this);
|
||||
},
|
||||
});
|
||||
|
||||
Object.defineProperty(CanvasRenderingContext2D.prototype, "getPath", {
|
||||
value() {
|
||||
return InspectorFrontendHost.getPath(this);
|
||||
},
|
||||
});
|
||||
|
||||
Object.defineProperty(CanvasRenderingContext2D.prototype, "setPath", {
|
||||
value(path) {
|
||||
return InspectorFrontendHost.setPath(this, path);
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright (C) 2013, 2015 Apple Inc. All rights reserved.
|
||||
* Copyright (C) 2017 Devin Rousso <webkit@devinrousso.com>. 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.ImageUtilities = class ImageUtilities {
|
||||
static useSVGSymbol(url, className, title)
|
||||
{
|
||||
const svgNamespace = "http://www.w3.org/2000/svg";
|
||||
const xlinkNamespace = "http://www.w3.org/1999/xlink";
|
||||
|
||||
let svgElement = document.createElementNS(svgNamespace, "svg");
|
||||
svgElement.style.width = "100%";
|
||||
svgElement.style.height = "100%";
|
||||
|
||||
// URL must contain a fragment reference to a graphical element, like a symbol. If none is given
|
||||
// append #root which all of our SVGs have on the top level <svg> element.
|
||||
if (!url.includes("#"))
|
||||
url += "#root";
|
||||
|
||||
let useElement = document.createElementNS(svgNamespace, "use");
|
||||
useElement.setAttributeNS(xlinkNamespace, "xlink:href", url);
|
||||
svgElement.appendChild(useElement);
|
||||
|
||||
let wrapper = document.createElement("div");
|
||||
wrapper.appendChild(svgElement);
|
||||
|
||||
if (className)
|
||||
wrapper.className = className;
|
||||
if (title)
|
||||
wrapper.title = title;
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
static promisifyLoad(src)
|
||||
{
|
||||
return new Promise((resolve, reject) => {
|
||||
let image = new Image;
|
||||
let resolveWithImage = () => { resolve(image); };
|
||||
image.addEventListener("load", resolveWithImage);
|
||||
image.addEventListener("error", resolveWithImage);
|
||||
image.src = src;
|
||||
});
|
||||
}
|
||||
|
||||
static scratchCanvasContext2D(callback)
|
||||
{
|
||||
if (!WI.ImageUtilities._scratchContext2D)
|
||||
WI.ImageUtilities._scratchContext2D = document.createElement("canvas").getContext("2d");
|
||||
|
||||
let context = WI.ImageUtilities._scratchContext2D;
|
||||
|
||||
context.clearRect(0, 0, context.canvas.width, context.canvas.height);
|
||||
context.save();
|
||||
callback(context);
|
||||
context.restore();
|
||||
}
|
||||
|
||||
static imageFromImageBitmap(data)
|
||||
{
|
||||
console.assert(data instanceof ImageBitmap);
|
||||
|
||||
let image = null;
|
||||
WI.ImageUtilities.scratchCanvasContext2D((context) => {
|
||||
context.canvas.width = data.width;
|
||||
context.canvas.height = data.height;
|
||||
context.drawImage(data, 0, 0);
|
||||
|
||||
image = new Image;
|
||||
image.src = context.canvas.toDataURL();
|
||||
});
|
||||
return image;
|
||||
}
|
||||
|
||||
static imageFromImageData(data)
|
||||
{
|
||||
console.assert(data instanceof ImageData);
|
||||
|
||||
let image = null;
|
||||
WI.ImageUtilities.scratchCanvasContext2D((context) => {
|
||||
context.canvas.width = data.width;
|
||||
context.canvas.height = data.height;
|
||||
context.putImageData(data, 0, 0);
|
||||
|
||||
image = new Image;
|
||||
image.src = context.canvas.toDataURL();
|
||||
});
|
||||
return image;
|
||||
}
|
||||
|
||||
static imageFromCanvasGradient(gradient, width, height)
|
||||
{
|
||||
console.assert(gradient instanceof CanvasGradient);
|
||||
|
||||
let image = null;
|
||||
WI.ImageUtilities.scratchCanvasContext2D((context) => {
|
||||
context.canvas.width = width;
|
||||
context.canvas.height = height;
|
||||
context.fillStyle = gradient;
|
||||
context.fillRect(0, 0, width, height);
|
||||
|
||||
image = new Image;
|
||||
image.src = context.canvas.toDataURL();
|
||||
});
|
||||
return image;
|
||||
}
|
||||
};
|
||||
|
||||
WI.ImageUtilities._scratchContext2D = null;
|
||||
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.
|
||||
*/
|
||||
|
||||
class IterableWeakSet
|
||||
{
|
||||
constructor(items = [])
|
||||
{
|
||||
this._wrappers = new Set;
|
||||
this._wrapperForItem = new WeakMap;
|
||||
|
||||
for (let item of items)
|
||||
this.add(item);
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get size()
|
||||
{
|
||||
let size = 0;
|
||||
for (let wrapper of this._wrappers) {
|
||||
if (wrapper.deref())
|
||||
++size;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
has(item)
|
||||
{
|
||||
let result = this._wrapperForItem.has(item);
|
||||
console.assert(Array.from(this._wrappers).some((wrapper) => wrapper.deref() === item) === result, this, item);
|
||||
return result;
|
||||
}
|
||||
|
||||
add(item)
|
||||
{
|
||||
console.assert(typeof item === "object", item);
|
||||
console.assert(item !== null, item);
|
||||
|
||||
if (this.has(item))
|
||||
return;
|
||||
|
||||
let wrapper = new WeakRef(item);
|
||||
this._wrappers.add(wrapper);
|
||||
this._wrapperForItem.set(item, wrapper);
|
||||
this._finalizationRegistry.register(item, {weakThis: new WeakRef(this), wrapper}, wrapper);
|
||||
}
|
||||
|
||||
delete(item)
|
||||
{
|
||||
return !!this.take(item);
|
||||
}
|
||||
|
||||
take(item)
|
||||
{
|
||||
let wrapper = this._wrapperForItem.get(item);
|
||||
if (!wrapper)
|
||||
return undefined;
|
||||
|
||||
let itemDeleted = this._wrapperForItem.delete(item);
|
||||
console.assert(itemDeleted, this, item);
|
||||
|
||||
let wrapperDeleted = this._wrappers.delete(wrapper);
|
||||
console.assert(wrapperDeleted, this, item);
|
||||
|
||||
this._finalizationRegistry.unregister(wrapper);
|
||||
|
||||
console.assert(wrapper.deref() === item, this, item);
|
||||
return item;
|
||||
}
|
||||
|
||||
clear()
|
||||
{
|
||||
for (let wrapper of this._wrappers) {
|
||||
this._wrapperForItem.delete(wrapper);
|
||||
this._finalizationRegistry.unregister(wrapper);
|
||||
}
|
||||
|
||||
this._wrappers.clear();
|
||||
}
|
||||
|
||||
keys()
|
||||
{
|
||||
return this.values();
|
||||
}
|
||||
|
||||
*values()
|
||||
{
|
||||
for (let wrapper of this._wrappers) {
|
||||
let item = wrapper.deref();
|
||||
console.assert(!item === !this._wrapperForItem.has(item), this, item);
|
||||
if (item)
|
||||
yield item;
|
||||
}
|
||||
}
|
||||
|
||||
[Symbol.iterator]()
|
||||
{
|
||||
return this.values();
|
||||
}
|
||||
|
||||
copy()
|
||||
{
|
||||
return new IterableWeakSet(this.toJSON());
|
||||
}
|
||||
|
||||
toJSON()
|
||||
{
|
||||
return Array.from(this);
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
get _finalizationRegistry()
|
||||
{
|
||||
return IterableWeakSet._finalizationRegistry ??= new FinalizationRegistry(function(heldValue) {
|
||||
heldValue.weakThis.deref()?._wrappers.delete(heldValue.wrapper);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
class LinkedList
|
||||
{
|
||||
constructor()
|
||||
{
|
||||
this.head = new LinkedListNode;
|
||||
this.head.next = this.head.prev = this.head;
|
||||
this.length = 0;
|
||||
}
|
||||
|
||||
clear()
|
||||
{
|
||||
this.head.next = this.head.prev = this.head;
|
||||
this.length = 0;
|
||||
}
|
||||
|
||||
get last()
|
||||
{
|
||||
return this.head.prev;
|
||||
}
|
||||
|
||||
push(item)
|
||||
{
|
||||
let newNode = new LinkedListNode(item);
|
||||
let last = this.last;
|
||||
let head = this.head;
|
||||
|
||||
last.next = newNode;
|
||||
newNode.next = head;
|
||||
head.prev = newNode;
|
||||
newNode.prev = last;
|
||||
|
||||
this.length++;
|
||||
|
||||
return newNode;
|
||||
}
|
||||
|
||||
remove(node)
|
||||
{
|
||||
if (!node)
|
||||
return false;
|
||||
|
||||
node.prev.next = node.next;
|
||||
node.next.prev = node.prev;
|
||||
|
||||
this.length--;
|
||||
return true;
|
||||
}
|
||||
|
||||
forEach(callback)
|
||||
{
|
||||
let node = this.head;
|
||||
for (let i = 0, length = this.length; i < length; i++) {
|
||||
node = node.next;
|
||||
let returnValue = callback(node.value, i);
|
||||
if (returnValue === false)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
toArray()
|
||||
{
|
||||
let node = this.head;
|
||||
let i = this.length;
|
||||
let result = new Array(i);
|
||||
while (i--) {
|
||||
node = node.prev;
|
||||
result[i] = node.value;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
toJSON()
|
||||
{
|
||||
return this.toArray();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class LinkedListNode
|
||||
{
|
||||
constructor(value)
|
||||
{
|
||||
this.value = value;
|
||||
this.prev = null;
|
||||
this.next = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
class ListMultimap
|
||||
{
|
||||
constructor()
|
||||
{
|
||||
this._insertionOrderedEntries = new LinkedList;
|
||||
this._keyMap = new Map;
|
||||
}
|
||||
|
||||
get size()
|
||||
{
|
||||
return this._insertionOrderedEntries.length;
|
||||
}
|
||||
|
||||
add(key, value)
|
||||
{
|
||||
let nodeMap = this._keyMap.get(key);
|
||||
if (!nodeMap) {
|
||||
nodeMap = new Map;
|
||||
this._keyMap.set(key, nodeMap);
|
||||
}
|
||||
|
||||
let node = nodeMap.get(value);
|
||||
if (!node) {
|
||||
node = this._insertionOrderedEntries.push([key, value]);
|
||||
nodeMap.set(value, node);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
delete(key, value)
|
||||
{
|
||||
let nodeMap = this._keyMap.get(key);
|
||||
if (!nodeMap)
|
||||
return false;
|
||||
|
||||
let node = nodeMap.get(value);
|
||||
if (!node)
|
||||
return false;
|
||||
|
||||
nodeMap.delete(value);
|
||||
this._insertionOrderedEntries.remove(node);
|
||||
return true;
|
||||
}
|
||||
|
||||
deleteAll(key)
|
||||
{
|
||||
let nodeMap = this._keyMap.get(key);
|
||||
if (!nodeMap)
|
||||
return false;
|
||||
|
||||
let list = this._insertionOrderedEntries;
|
||||
let didDelete = false;
|
||||
nodeMap.forEach(function(node) {
|
||||
list.remove(node);
|
||||
didDelete = true;
|
||||
});
|
||||
|
||||
this._keyMap.delete(key);
|
||||
return didDelete;
|
||||
}
|
||||
|
||||
has(key, value)
|
||||
{
|
||||
let nodeMap = this._keyMap.get(key);
|
||||
if (!nodeMap)
|
||||
return false;
|
||||
|
||||
return nodeMap.has(value);
|
||||
}
|
||||
|
||||
clear()
|
||||
{
|
||||
this._keyMap = new Map;
|
||||
this._insertionOrderedEntries = new LinkedList;
|
||||
}
|
||||
|
||||
forEach(callback)
|
||||
{
|
||||
this._insertionOrderedEntries.forEach(callback);
|
||||
}
|
||||
|
||||
toArray()
|
||||
{
|
||||
return this._insertionOrderedEntries.toArray();
|
||||
}
|
||||
|
||||
toJSON()
|
||||
{
|
||||
return this.toArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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.
|
||||
*/
|
||||
|
||||
(function() {
|
||||
if (WI.dontLocalizeUserInterface)
|
||||
return;
|
||||
|
||||
let localizedStringsURL = InspectorFrontendHost.localizedStringsURL;
|
||||
console.assert(localizedStringsURL);
|
||||
if (localizedStringsURL)
|
||||
document.write("<script src=\"" + localizedStringsURL + "\"></script>");
|
||||
})();
|
||||
|
||||
WI.unlocalizedString = function(string)
|
||||
{
|
||||
// Intentionally do nothing, since this is for engineering builds
|
||||
// (such as in Debug UI) or in text that is standardized in English.
|
||||
// For example, CSS property names and values are never localized.
|
||||
return string;
|
||||
};
|
||||
|
||||
WI.UIString = function(string, key, comment)
|
||||
{
|
||||
"use strict";
|
||||
|
||||
if (WI.dontLocalizeUserInterface)
|
||||
return string;
|
||||
|
||||
// UIString(string, comment)
|
||||
if (arguments.length === 2) {
|
||||
comment = key;
|
||||
key = undefined;
|
||||
}
|
||||
|
||||
key = key || string;
|
||||
|
||||
if (window.localizedStrings && key in window.localizedStrings)
|
||||
return window.localizedStrings[key];
|
||||
|
||||
if (!window.localizedStrings)
|
||||
console.error(`Attempted to load localized string "${key}" before localizedStrings was initialized.`, comment);
|
||||
|
||||
if (!this._missingLocalizedStrings)
|
||||
this._missingLocalizedStrings = {};
|
||||
|
||||
if (!(key in this._missingLocalizedStrings)) {
|
||||
console.error(`Localized string "${key}" was not found.`, comment);
|
||||
this._missingLocalizedStrings[key] = true;
|
||||
}
|
||||
|
||||
return "LOCALIZED STRING NOT FOUND";
|
||||
};
|
||||
|
||||
WI.repeatedUIString = {};
|
||||
|
||||
WI.repeatedUIString.timelineRecordLayout = function() {
|
||||
return WI.UIString("Layout", "Layout @ Timeline record", "Layout phase timeline records");
|
||||
};
|
||||
|
||||
WI.repeatedUIString.timelineRecordPaint = function() {
|
||||
return WI.UIString("Paint", "Paint @ Timeline record", "Paint (render) phase timeline records");
|
||||
};
|
||||
|
||||
WI.repeatedUIString.timelineRecordComposite = function() {
|
||||
return WI.UIString("Composite", "Composite @ Timeline record", "Composite phase timeline records, where graphic layers are combined");
|
||||
};
|
||||
|
||||
WI.repeatedUIString.debuggerStatements = function() {
|
||||
return WI.UIString("Debugger Statements", "Debugger Statements @ JavaScript Breakpoint", "Break (pause) on debugger statements");
|
||||
};
|
||||
|
||||
WI.repeatedUIString.allExceptions = function() {
|
||||
return WI.UIString("All Exceptions", "All Exceptions @ JavaScript Breakpoint", "Break (pause) on all exceptions");
|
||||
};
|
||||
|
||||
WI.repeatedUIString.uncaughtExceptions = function() {
|
||||
return WI.UIString("Uncaught Exceptions", "Uncaught Exceptions @ JavaScript Breakpoint", "Break (pause) on uncaught (unhandled) exceptions");
|
||||
};
|
||||
|
||||
WI.repeatedUIString.assertionFailures = function() {
|
||||
return WI.UIString("Assertion Failures", "Assertion Failures @ JavaScript Breakpoint", "Break (pause) when console.assert() fails");
|
||||
};
|
||||
|
||||
WI.repeatedUIString.allMicrotasks = function() {
|
||||
return WI.UIString("All Microtasks", "All Microtasks @ JavaScript Breakpoint", "Break (pause) on all microtasks");
|
||||
};
|
||||
|
||||
WI.repeatedUIString.allAnimationFrames = function() {
|
||||
return WI.UIString("All Animation Frames", "All Animation Frames @ Event Breakpoint", "Break (pause) on All animation frames");
|
||||
};
|
||||
|
||||
WI.repeatedUIString.allIntervals = function() {
|
||||
return WI.UIString("All Intervals", "All Intervals @ Event Breakpoint", "Break (pause) on all intervals");
|
||||
};
|
||||
|
||||
WI.repeatedUIString.allEvents = function() {
|
||||
return WI.UIString("All Events", "All Events @ Event Breakpoint", "Break (pause) on all events");
|
||||
};
|
||||
|
||||
WI.repeatedUIString.allTimeouts = function() {
|
||||
return WI.UIString("All Timeouts", "All Timeouts @ Event Breakpoint", "Break (pause) on all timeouts");
|
||||
};
|
||||
|
||||
WI.repeatedUIString.allRequests = function() {
|
||||
return WI.UIString("All Requests", "A submenu item of 'Break on' that breaks (pauses) before all network requests");
|
||||
};
|
||||
|
||||
WI.repeatedUIString.fetch = function() {
|
||||
return WI.UIString("Fetch", "Resource loaded via 'fetch' method");
|
||||
};
|
||||
|
||||
WI.repeatedUIString.revealInDOMTree = function() {
|
||||
return WI.UIString("Reveal in DOM Tree", "Open Elements tab and select this node in DOM tree");
|
||||
};
|
||||
|
||||
WI.repeatedUIString.showTransparencyGridTooltip = function() {
|
||||
return WI.UIString("Show transparency grid", "Show transparency grid (tooltip)", "Tooltip for showing the checkered transparency grid under images and canvases")
|
||||
};
|
||||
@@ -0,0 +1,345 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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.fileExtensionForFilename = function(filename)
|
||||
{
|
||||
if (!filename)
|
||||
return null;
|
||||
|
||||
let index = filename.lastIndexOf(".");
|
||||
if (index === -1)
|
||||
return null;
|
||||
|
||||
if (index === filename.length - 1)
|
||||
return null;
|
||||
|
||||
return filename.substr(index + 1);
|
||||
};
|
||||
|
||||
WI.fileExtensionForURL = function(url)
|
||||
{
|
||||
let lastPathComponent = parseURL(url).lastPathComponent;
|
||||
return WI.fileExtensionForFilename(lastPathComponent);
|
||||
};
|
||||
|
||||
WI.mimeTypeForFileExtension = function(extension)
|
||||
{
|
||||
const extensionToMIMEType = {
|
||||
// Document types.
|
||||
"html": "text/html",
|
||||
"xhtml": "application/xhtml+xml",
|
||||
"xml": "text/xml",
|
||||
|
||||
// Script types.
|
||||
"js": "text/javascript",
|
||||
"mjs": "text/javascript",
|
||||
"json": "application/json",
|
||||
"clj": "text/x-clojure",
|
||||
"coffee": "text/x-coffeescript",
|
||||
"ls": "text/x-livescript",
|
||||
"ts": "text/typescript",
|
||||
"ps": "application/postscript",
|
||||
"jsx": "text/jsx",
|
||||
|
||||
// Stylesheet types.
|
||||
"css": "text/css",
|
||||
"less": "text/x-less",
|
||||
"sass": "text/x-sass",
|
||||
"scss": "text/x-scss",
|
||||
|
||||
// Image types.
|
||||
"avif": "image/avif",
|
||||
"bmp": "image/bmp",
|
||||
"gif": "image/gif",
|
||||
"ico": "image/x-icon",
|
||||
"jp2": "image/jp2",
|
||||
"jpeg": "image/jpeg",
|
||||
"jpg": "image/jpeg",
|
||||
"jxl": "image/jxl",
|
||||
"pdf": "application/pdf",
|
||||
"png": "image/png",
|
||||
"tif": "image/tiff",
|
||||
"tiff": "image/tiff",
|
||||
"webp": "image/webp",
|
||||
"xbm": "image/x-xbitmap",
|
||||
|
||||
"ogx": "application/ogg",
|
||||
"ogg": "audio/ogg",
|
||||
"oga": "audio/ogg",
|
||||
"ogv": "video/ogg",
|
||||
|
||||
// Annodex
|
||||
"anx": "application/annodex",
|
||||
"axa": "audio/annodex",
|
||||
"axv": "video/annodex",
|
||||
"spx": "audio/speex",
|
||||
|
||||
// WebM
|
||||
"webm": "video/webm",
|
||||
|
||||
// MPEG
|
||||
"m1a": "audio/mpeg",
|
||||
"m2a": "audio/mpeg",
|
||||
"mpg": "video/mpeg",
|
||||
"m15": "video/mpeg",
|
||||
"m1s": "video/mpeg",
|
||||
"m1v": "video/mpeg",
|
||||
"m75": "video/mpeg",
|
||||
"mpa": "video/mpeg",
|
||||
"mpeg": "video/mpeg",
|
||||
"mpm": "video/mpeg",
|
||||
"mpv": "video/mpeg",
|
||||
|
||||
// MPEG playlist
|
||||
"m3u8": "application/x-mpegurl",
|
||||
"m3url": "audio/x-mpegurl",
|
||||
"m3u": "audio/x-mpegurl",
|
||||
|
||||
// MPEG-4
|
||||
"m4v": "video/x-m4v",
|
||||
"m4a": "audio/x-m4a",
|
||||
"m4b": "audio/x-m4b",
|
||||
"m4p": "audio/x-m4p",
|
||||
|
||||
// MP3
|
||||
"mp3": "audio/mp3",
|
||||
|
||||
// MPEG-2
|
||||
"mp2": "video/x-mpeg2",
|
||||
"vob": "video/mpeg2",
|
||||
"mod": "video/mpeg2",
|
||||
"m2ts": "video/m2ts",
|
||||
"m2t": "video/x-m2ts",
|
||||
|
||||
// 3GP/3GP2
|
||||
"3gpp": "audio/3gpp",
|
||||
"3g2": "audio/3gpp2",
|
||||
"amc": "application/x-mpeg",
|
||||
|
||||
// AAC
|
||||
"aac": "audio/aac",
|
||||
"adts": "audio/aac",
|
||||
"m4r": "audio/x-aac",
|
||||
|
||||
// CoreAudio File
|
||||
"caf": "audio/x-caf",
|
||||
"gsm": "audio/x-gsm",
|
||||
|
||||
// ADPCM
|
||||
"wav": "audio/x-wav",
|
||||
|
||||
// Text Track
|
||||
"vtt": "text/vtt",
|
||||
|
||||
// Font
|
||||
"woff": "font/woff",
|
||||
"woff2": "font/woff2",
|
||||
"otf": "font/otf",
|
||||
"ttf": "font/ttf",
|
||||
"sfnt": "font/sfnt",
|
||||
|
||||
// Miscellaneous types.
|
||||
"svg": "image/svg+xml",
|
||||
"txt": "text/plain",
|
||||
"xsl": "text/xsl"
|
||||
};
|
||||
|
||||
return extensionToMIMEType[extension] || null;
|
||||
};
|
||||
|
||||
WI.fileExtensionForMIMEType = function(mimeType)
|
||||
{
|
||||
if (!mimeType)
|
||||
return null;
|
||||
|
||||
const mimeTypeToExtension = {
|
||||
// Document types.
|
||||
"text/html": "html",
|
||||
"application/xhtml+xml": "xhtml",
|
||||
"application/xml": "xml",
|
||||
"text/xml": "xml",
|
||||
|
||||
// Script types.
|
||||
"application/ecmascript": "js",
|
||||
"application/javascript": "js",
|
||||
"application/x-ecmascript": "js",
|
||||
"application/x-javascript": "js",
|
||||
"text/ecmascript": "js",
|
||||
"text/javascript": "js",
|
||||
"text/javascript1.0": "js",
|
||||
"text/javascript1.1": "js",
|
||||
"text/javascript1.2": "js",
|
||||
"text/javascript1.3": "js",
|
||||
"text/javascript1.4": "js",
|
||||
"text/javascript1.5": "js",
|
||||
"text/jscript": "js",
|
||||
"text/x-ecmascript": "js",
|
||||
"text/x-javascript": "js",
|
||||
"application/json": "json",
|
||||
"text/x-clojure": "clj",
|
||||
"text/x-coffeescript": "coffee",
|
||||
"text/livescript": "ls",
|
||||
"text/x-livescript": "ls",
|
||||
"text/typescript": "ts",
|
||||
"application/postscript": "ps",
|
||||
"text/jsx": "jsx",
|
||||
|
||||
// Stylesheet types.
|
||||
"text/css": "css",
|
||||
"text/x-less": "less",
|
||||
"text/x-sass": "sass",
|
||||
"text/x-scss": "scss",
|
||||
|
||||
// Image types.
|
||||
"image/avif": "avif",
|
||||
"image/bmp": "bmp",
|
||||
"image/gif": "gif",
|
||||
"image/vnd.microsoft.icon": "ico",
|
||||
"image/x-icon": "ico",
|
||||
"image/jp2": "jp2",
|
||||
"image/jpeg": "jpg",
|
||||
"image/jxl": "jxl",
|
||||
"application/pdf": "pdf",
|
||||
"text/pdf": "pdf",
|
||||
"image/png": "png",
|
||||
"image/tiff": "tiff",
|
||||
"image/webp": "webp",
|
||||
"image/x-xbitmap": "xbm",
|
||||
|
||||
// Ogg
|
||||
"application/ogg": "ogx",
|
||||
"audio/ogg": "ogg",
|
||||
|
||||
// Annodex
|
||||
"application/annodex": "anx",
|
||||
"audio/annodex": "axa",
|
||||
"video/annodex": "axv",
|
||||
"audio/speex": "spx",
|
||||
|
||||
// WebM
|
||||
"video/webm": "webm",
|
||||
"audio/webm": "webm",
|
||||
|
||||
// MPEG
|
||||
"video/mpeg": "mpeg",
|
||||
|
||||
// MPEG playlist
|
||||
"application/vnd.apple.mpegurl": "m3u8",
|
||||
"application/mpegurl": "m3u8",
|
||||
"application/x-mpegurl": "m3u8",
|
||||
"audio/mpegurl": "m3u",
|
||||
"audio/x-mpegurl": "m3u",
|
||||
|
||||
// MPEG-4
|
||||
"video/x-m4v": "m4v",
|
||||
"audio/x-m4a": "m4a",
|
||||
"audio/x-m4b": "m4b",
|
||||
"audio/x-m4p": "m4p",
|
||||
"audio/mp4": "m4a",
|
||||
|
||||
// MP3
|
||||
"audio/mp3": "mp3",
|
||||
"audio/x-mp3": "mp3",
|
||||
"audio/x-mpeg": "mp3",
|
||||
|
||||
// MPEG-2
|
||||
"video/x-mpeg2": "mp2",
|
||||
"video/mpeg2": "vob",
|
||||
"video/m2ts": "m2ts",
|
||||
"video/x-m2ts": "m2t",
|
||||
|
||||
// 3GP/3GP2
|
||||
"audio/3gpp": "3gpp",
|
||||
"audio/3gpp2": "3g2",
|
||||
"application/x-mpeg": "amc",
|
||||
|
||||
// AAC
|
||||
"audio/aac": "aac",
|
||||
"audio/x-aac": "m4r",
|
||||
|
||||
// CoreAudio File
|
||||
"audio/x-caf": "caf",
|
||||
"audio/x-gsm": "gsm",
|
||||
|
||||
// ADPCM
|
||||
"audio/x-wav": "wav",
|
||||
"audio/vnd.wave": "wav",
|
||||
|
||||
// Text Track
|
||||
"text/vtt": "vtt",
|
||||
|
||||
// Font
|
||||
"font/woff": "woff",
|
||||
"font/woff2": "woff2",
|
||||
"font/otf": "otf",
|
||||
"font/ttf": "ttf",
|
||||
"font/sfnt": "sfnt",
|
||||
|
||||
// Miscellaneous types.
|
||||
"image/svg+xml": "svg",
|
||||
"text/plain": "txt",
|
||||
"text/xsl": "xsl",
|
||||
};
|
||||
|
||||
let extension = mimeTypeToExtension[mimeType];
|
||||
if (extension)
|
||||
return extension;
|
||||
|
||||
if (mimeType.endsWith("+json"))
|
||||
return "json";
|
||||
if (mimeType.endsWith("+xml"))
|
||||
return "xml";
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
WI.shouldTreatMIMETypeAsText = function(mimeType)
|
||||
{
|
||||
if (!mimeType)
|
||||
return false;
|
||||
|
||||
if (mimeType.startsWith("text/"))
|
||||
return true;
|
||||
|
||||
if (mimeType.endsWith("+json") || mimeType.endsWith("+xml"))
|
||||
return true;
|
||||
|
||||
let extension = WI.fileExtensionForMIMEType(mimeType);
|
||||
if (extension === "xml")
|
||||
return true;
|
||||
|
||||
// Various script and JSON mime types.
|
||||
if (extension === "js" || extension === "json")
|
||||
return true;
|
||||
|
||||
// Various media text mime types.
|
||||
if (extension === "m3u8" || extension === "m3u")
|
||||
return true;
|
||||
|
||||
if (mimeType.startsWith("application/"))
|
||||
return mimeType.endsWith("script") || mimeType.endsWith("json") || mimeType.endsWith("xml");
|
||||
|
||||
return false;
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright (C) 2018-2020 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.
|
||||
*/
|
||||
|
||||
class Multimap
|
||||
{
|
||||
constructor(items = [])
|
||||
{
|
||||
this._map = new Map;
|
||||
|
||||
for (let [key, value] of items)
|
||||
this.add(key, value);
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get size()
|
||||
{
|
||||
return this._map.size;
|
||||
}
|
||||
|
||||
has(key, value)
|
||||
{
|
||||
let valueSet = this._map.get(key);
|
||||
if (!valueSet)
|
||||
return false;
|
||||
return value === undefined || valueSet.has(value);
|
||||
}
|
||||
|
||||
get(key)
|
||||
{
|
||||
return this._map.get(key);
|
||||
}
|
||||
|
||||
add(key, value)
|
||||
{
|
||||
let valueSet = this._map.get(key);
|
||||
if (!valueSet) {
|
||||
valueSet = new Set;
|
||||
this._map.set(key, valueSet);
|
||||
}
|
||||
valueSet.add(value);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
delete(key, value)
|
||||
{
|
||||
// Allow an entire key to be removed by not passing a value.
|
||||
if (arguments.length === 1)
|
||||
return this._map.delete(key);
|
||||
|
||||
let valueSet = this._map.get(key);
|
||||
if (!valueSet)
|
||||
return false;
|
||||
|
||||
let deleted = valueSet.delete(value);
|
||||
|
||||
if (!valueSet.size)
|
||||
this._map.delete(key);
|
||||
|
||||
return deleted;
|
||||
}
|
||||
|
||||
take(key, value)
|
||||
{
|
||||
// Allow an entire key to be removed by not passing a value.
|
||||
if (arguments.length === 1)
|
||||
return this._map.take(key);
|
||||
|
||||
let valueSet = this._map.get(key);
|
||||
if (!valueSet)
|
||||
return undefined;
|
||||
|
||||
let result = valueSet.take(value);
|
||||
if (!valueSet.size)
|
||||
this._map.delete(key);
|
||||
return result;
|
||||
}
|
||||
|
||||
clear()
|
||||
{
|
||||
this._map.clear();
|
||||
}
|
||||
|
||||
keys()
|
||||
{
|
||||
return this._map.keys();
|
||||
}
|
||||
|
||||
*values()
|
||||
{
|
||||
for (let valueSet of this._map.values()) {
|
||||
for (let value of valueSet)
|
||||
yield value;
|
||||
}
|
||||
}
|
||||
|
||||
sets()
|
||||
{
|
||||
return this._map.entries();
|
||||
}
|
||||
|
||||
*[Symbol.iterator]()
|
||||
{
|
||||
for (let [key, valueSet] of this._map) {
|
||||
for (let value of valueSet)
|
||||
yield [key, value];
|
||||
}
|
||||
}
|
||||
|
||||
copy()
|
||||
{
|
||||
return new Multimap(this.toJSON());
|
||||
}
|
||||
|
||||
toJSON()
|
||||
{
|
||||
return Array.from(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* Copyright (C) 2008, 2013 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. ``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
|
||||
* 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.Object = class WebInspectorObject
|
||||
{
|
||||
constructor()
|
||||
{
|
||||
this._listeners = null;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static addEventListener(eventType, listener, thisObject)
|
||||
{
|
||||
console.assert(typeof eventType === "string", this, eventType, listener, thisObject);
|
||||
console.assert(typeof listener === "function", this, eventType, listener, thisObject);
|
||||
console.assert(typeof thisObject === "object" || window.InspectorTest || window.ProtocolTest, this, eventType, listener, thisObject);
|
||||
|
||||
thisObject ??= this;
|
||||
|
||||
let data = {
|
||||
listener,
|
||||
thisObjectWeakRef: new WeakRef(thisObject),
|
||||
};
|
||||
|
||||
WI.Object._listenerThisObjectFinalizationRegistry.register(thisObject, {eventTargetWeakRef: new WeakRef(this), eventType, data}, data);
|
||||
|
||||
this._listeners ??= new Multimap;
|
||||
this._listeners.add(eventType, data);
|
||||
|
||||
console.assert(Array.from(this._listeners.get(eventType)).filter((item) => item.listener === listener && item.thisObjectWeakRef.deref() === thisObject).length === 1, this, eventType, listener, thisObject);
|
||||
|
||||
return listener;
|
||||
}
|
||||
|
||||
static singleFireEventListener(eventType, listener, thisObject)
|
||||
{
|
||||
let eventTargetWeakRef = new WeakRef(this);
|
||||
return this.addEventListener(eventType, function wrappedCallback() {
|
||||
eventTargetWeakRef.deref()?.removeEventListener(eventType, wrappedCallback, this);
|
||||
listener.apply(this, arguments);
|
||||
}, thisObject);
|
||||
}
|
||||
|
||||
static awaitEvent(eventType, thisObject)
|
||||
{
|
||||
return new Promise((resolve, reject) => {
|
||||
this.singleFireEventListener(eventType, resolve, thisObject);
|
||||
});
|
||||
}
|
||||
|
||||
static removeEventListener(eventType, listener, thisObject)
|
||||
{
|
||||
console.assert(this._listeners, this, eventType, listener, thisObject);
|
||||
console.assert(typeof eventType === "string", this, eventType, listener, thisObject);
|
||||
console.assert(typeof listener === "function", this, eventType, listener, thisObject);
|
||||
console.assert(typeof thisObject === "object" || window.InspectorTest || window.ProtocolTest, this, eventType, listener, thisObject);
|
||||
|
||||
if (!this._listeners)
|
||||
return;
|
||||
|
||||
thisObject ??= this;
|
||||
|
||||
let listenersForEventType = this._listeners.get(eventType);
|
||||
console.assert(listenersForEventType, this, eventType, listener, thisObject);
|
||||
if (!listenersForEventType)
|
||||
return;
|
||||
|
||||
let didDelete = false;
|
||||
for (let data of listenersForEventType) {
|
||||
let unwrapped = data.thisObjectWeakRef.deref();
|
||||
if (!unwrapped || unwrapped !== thisObject || data.listener !== listener)
|
||||
continue;
|
||||
|
||||
if (this._listeners.delete(eventType, data))
|
||||
didDelete = true;
|
||||
WI.Object._listenerThisObjectFinalizationRegistry.unregister(data);
|
||||
}
|
||||
console.assert(didDelete, this, eventType, listener, thisObject);
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
addEventListener() { return WI.Object.addEventListener.apply(this, arguments); }
|
||||
singleFireEventListener() { return WI.Object.singleFireEventListener.apply(this, arguments); }
|
||||
awaitEvent() { return WI.Object.awaitEvent.apply(this, arguments); }
|
||||
removeEventListener() { return WI.Object.removeEventListener.apply(this, arguments); }
|
||||
|
||||
dispatchEventToListeners(eventType, eventData)
|
||||
{
|
||||
let event = new WI.Event(this, eventType, eventData);
|
||||
|
||||
function dispatch(object)
|
||||
{
|
||||
if (!object || event._stoppedPropagation)
|
||||
return;
|
||||
|
||||
let listeners = object._listeners;
|
||||
if (!listeners || !object.hasOwnProperty("_listeners") || !listeners.size)
|
||||
return;
|
||||
|
||||
let listenersForEventType = listeners.get(eventType);
|
||||
if (!listenersForEventType)
|
||||
return;
|
||||
|
||||
// Copy the set of listeners so we don't have to worry about mutating while iterating.
|
||||
for (let data of Array.from(listenersForEventType)) {
|
||||
let unwrapped = data.thisObjectWeakRef.deref();
|
||||
if (!unwrapped)
|
||||
continue;
|
||||
|
||||
data.listener.call(unwrapped, event);
|
||||
|
||||
if (event._stoppedPropagation)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Dispatch to listeners of this specific object.
|
||||
dispatch(this);
|
||||
|
||||
// Allow propagation again so listeners on the constructor always have a crack at the event.
|
||||
event._stoppedPropagation = false;
|
||||
|
||||
// Dispatch to listeners on all constructors up the prototype chain, including the immediate constructor.
|
||||
let constructor = this.constructor;
|
||||
while (constructor) {
|
||||
dispatch(constructor);
|
||||
|
||||
if (!constructor.prototype.__proto__)
|
||||
break;
|
||||
|
||||
constructor = constructor.prototype.__proto__.constructor;
|
||||
}
|
||||
|
||||
return event.defaultPrevented;
|
||||
}
|
||||
|
||||
// Test
|
||||
|
||||
static hasEventListeners(eventType)
|
||||
{
|
||||
console.assert(window.InspectorTest || window.ProtocolTest);
|
||||
return this._listeners?.has(eventType);
|
||||
}
|
||||
|
||||
static activelyListeningObjectsWithPrototype(proto)
|
||||
{
|
||||
console.assert(window.InspectorTest || window.ProtocolTest);
|
||||
let results = new Set;
|
||||
if (this._listeners) {
|
||||
for (let data of this._listeners.values()) {
|
||||
let unwrapped = data.thisObjectWeakRef.deref();
|
||||
if (unwrapped instanceof proto)
|
||||
results.add(unwrapped);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
hasEventListeners() { return WI.Object.hasEventListeners.apply(this, arguments); }
|
||||
activelyListeningObjectsWithPrototype() { return WI.Object.activelyListeningObjectsWithPrototype.apply(this, arguments); }
|
||||
};
|
||||
|
||||
WI.Object._listenerThisObjectFinalizationRegistry = new FinalizationRegistry((heldValue) => {
|
||||
heldValue.eventTargetWeakRef.deref()?._listeners.delete(heldValue.eventType, heldValue.data);
|
||||
});
|
||||
|
||||
WI.Event = class Event
|
||||
{
|
||||
constructor(target, type, data)
|
||||
{
|
||||
this.target = target;
|
||||
this.type = type;
|
||||
this.data = data;
|
||||
this.defaultPrevented = false;
|
||||
this._stoppedPropagation = false;
|
||||
}
|
||||
|
||||
stopPropagation()
|
||||
{
|
||||
this._stoppedPropagation = true;
|
||||
}
|
||||
|
||||
preventDefault()
|
||||
{
|
||||
this.defaultPrevented = true;
|
||||
}
|
||||
};
|
||||
|
||||
WI.notifications = new WI.Object;
|
||||
|
||||
WI.Notification = {
|
||||
GlobalModifierKeysDidChange: "global-modifiers-did-change",
|
||||
PageArchiveStarted: "page-archive-started",
|
||||
PageArchiveEnded: "page-archive-ended",
|
||||
ExtraDomainsActivated: "extra-domains-activated", // COMPATIBILITY (iOS 14.0): Inspector.activateExtraDomains was removed in favor of a declared debuggable type
|
||||
VisibilityStateDidChange: "visibility-state-did-change",
|
||||
TransitionPageTarget: "transition-page-target",
|
||||
};
|
||||
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.ObjectStore = class ObjectStore
|
||||
{
|
||||
constructor(name, options = {})
|
||||
{
|
||||
this._name = name;
|
||||
this._options = options;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static supported()
|
||||
{
|
||||
return (!window.InspectorTest || WI.ObjectStore.__testObjectStore) && window.indexedDB;
|
||||
}
|
||||
|
||||
static async reset()
|
||||
{
|
||||
if (WI.ObjectStore._database)
|
||||
WI.ObjectStore._database.close();
|
||||
|
||||
await window.indexedDB.deleteDatabase(ObjectStore._databaseName);
|
||||
}
|
||||
|
||||
static get _databaseName()
|
||||
{
|
||||
let inspectionLevel = InspectorFrontendHost ? InspectorFrontendHost.inspectionLevel : 1;
|
||||
let levelString = (inspectionLevel > 1) ? "-" + inspectionLevel : "";
|
||||
return "com.apple.WebInspector" + levelString;
|
||||
}
|
||||
|
||||
static _open(callback)
|
||||
{
|
||||
if (WI.ObjectStore._database) {
|
||||
callback(WI.ObjectStore._database);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Array.isArray(WI.ObjectStore._databaseCallbacks)) {
|
||||
WI.ObjectStore._databaseCallbacks.push(callback);
|
||||
return;
|
||||
}
|
||||
|
||||
WI.ObjectStore._databaseCallbacks = [callback];
|
||||
|
||||
const version = 8; // Increment this for every edit to `WI.objectStores`.
|
||||
|
||||
let databaseRequest = window.indexedDB.open(WI.ObjectStore._databaseName, version);
|
||||
databaseRequest.addEventListener("upgradeneeded", (event) => {
|
||||
let database = databaseRequest.result;
|
||||
|
||||
let objectStores = Object.values(WI.objectStores);
|
||||
if (WI.ObjectStore.__testObjectStore)
|
||||
objectStores.push(WI.ObjectStore.__testObjectStore);
|
||||
|
||||
let existingNames = new Set;
|
||||
for (let objectStore of objectStores) {
|
||||
if (!database.objectStoreNames.contains(objectStore._name))
|
||||
database.createObjectStore(objectStore._name, objectStore._options);
|
||||
|
||||
existingNames.add(objectStore._name);
|
||||
}
|
||||
|
||||
for (let objectStoreName of database.objectStoreNames) {
|
||||
if (!existingNames.has(objectStoreName))
|
||||
database.deleteObjectStore(objectStoreName);
|
||||
}
|
||||
});
|
||||
databaseRequest.addEventListener("success", (successEvent) => {
|
||||
WI.ObjectStore._database = databaseRequest.result;
|
||||
WI.ObjectStore._database.addEventListener("close", (closeEvent) => {
|
||||
WI.ObjectStore._database = null;
|
||||
});
|
||||
|
||||
for (let databaseCallback of WI.ObjectStore._databaseCallbacks)
|
||||
databaseCallback(WI.ObjectStore._database);
|
||||
|
||||
WI.ObjectStore._databaseCallbacks = null;
|
||||
});
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get keyPath()
|
||||
{
|
||||
return (this._options || {}).keyPath;
|
||||
}
|
||||
|
||||
associateObject(object, key, value)
|
||||
{
|
||||
if (typeof value === "object")
|
||||
value = this._resolveKeyPath(value, key).value;
|
||||
|
||||
let resolved = this._resolveKeyPath(object, key);
|
||||
resolved.object[resolved.key] = value;
|
||||
}
|
||||
|
||||
async get(...args)
|
||||
{
|
||||
if (!WI.ObjectStore.supported())
|
||||
return undefined;
|
||||
|
||||
return this._operation("readonly", (objectStore) => objectStore.get(...args));
|
||||
}
|
||||
|
||||
async getAll(...args)
|
||||
{
|
||||
if (!WI.ObjectStore.supported())
|
||||
return [];
|
||||
|
||||
return this._operation("readonly", (objectStore) => objectStore.getAll(...args));
|
||||
}
|
||||
|
||||
async getAllKeys(...args)
|
||||
{
|
||||
if (!WI.ObjectStore.supported())
|
||||
return [];
|
||||
|
||||
return this._operation("readonly", (objectStore) => objectStore.getAllKeys(...args));
|
||||
}
|
||||
|
||||
async put(...args)
|
||||
{
|
||||
if (!WI.ObjectStore.supported())
|
||||
return undefined;
|
||||
|
||||
return this._operation("readwrite", (objectStore) => objectStore.put(...args));
|
||||
}
|
||||
|
||||
async putObject(object, ...args)
|
||||
{
|
||||
if (!WI.ObjectStore.supported())
|
||||
return undefined;
|
||||
|
||||
console.assert(typeof object.toJSON === "function", "ObjectStore cannot store an object without JSON serialization", object.constructor.name);
|
||||
let result = await this.put(object.toJSON(WI.ObjectStore.toJSONSymbol), ...args);
|
||||
this.associateObject(object, args[0], result);
|
||||
return result;
|
||||
}
|
||||
|
||||
async delete(...args)
|
||||
{
|
||||
if (!WI.ObjectStore.supported())
|
||||
return undefined;
|
||||
|
||||
return this._operation("readwrite", (objectStore) => objectStore.delete(...args));
|
||||
}
|
||||
|
||||
async deleteObject(object, ...args)
|
||||
{
|
||||
if (!WI.ObjectStore.supported())
|
||||
return undefined;
|
||||
|
||||
return this.delete(this._resolveKeyPath(object).value, ...args);
|
||||
}
|
||||
|
||||
async clear(...args)
|
||||
{
|
||||
if (!WI.ObjectStore.supported())
|
||||
return undefined;
|
||||
|
||||
return this._operation("readwrite", (objectStore) => objectStore.clear(...args));
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_resolveKeyPath(object, keyPath)
|
||||
{
|
||||
keyPath = keyPath || this._options.keyPath || "";
|
||||
|
||||
let parts = keyPath.split(".");
|
||||
let key = parts.splice(-1, 1);
|
||||
while (parts.length) {
|
||||
if (!object.hasOwnProperty(parts[0]))
|
||||
break;
|
||||
object = object[parts.shift()];
|
||||
}
|
||||
|
||||
if (parts.length)
|
||||
key = parts.join(".") + "." + key;
|
||||
|
||||
return {
|
||||
object,
|
||||
key,
|
||||
value: object[key],
|
||||
};
|
||||
}
|
||||
|
||||
async _operation(mode, func)
|
||||
{
|
||||
// IndexedDB transactions will auto-close if there are no active operations at the end of a
|
||||
// microtask, so we need to do everything using event listeners instead of promises.
|
||||
return new Promise((resolve, reject) => {
|
||||
WI.ObjectStore._open((database) => {
|
||||
let transaction = database.transaction([this._name], mode);
|
||||
let objectStore = transaction.objectStore(this._name);
|
||||
let request = null;
|
||||
|
||||
try {
|
||||
request = func(objectStore);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
return;
|
||||
}
|
||||
|
||||
function listener(event) {
|
||||
transaction.removeEventListener("complete", listener);
|
||||
transaction.removeEventListener("error", listener);
|
||||
request.removeEventListener("success", listener);
|
||||
request.removeEventListener("error", listener);
|
||||
|
||||
if (request.error) {
|
||||
reject(request.error);
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(request.result);
|
||||
}
|
||||
transaction.addEventListener("complete", listener, {once: true});
|
||||
transaction.addEventListener("error", listener, {once: true});
|
||||
request.addEventListener("success", listener, {once: true});
|
||||
request.addEventListener("error", listener, {once: true});
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
WI.ObjectStore._database = null;
|
||||
WI.ObjectStore._databaseCallbacks = null;
|
||||
|
||||
WI.ObjectStore.toJSONSymbol = Symbol("ObjectStore-toJSON");
|
||||
|
||||
// Be sure to update the `version` above when making changes.
|
||||
WI.objectStores = {
|
||||
// Version 1
|
||||
audits: new WI.ObjectStore("audit-manager-tests", {keyPath: "__id", autoIncrement: true}),
|
||||
|
||||
// Version 2
|
||||
breakpoints: new WI.ObjectStore("debugger-breakpoints", {keyPath: "__id"}),
|
||||
|
||||
// Version 3
|
||||
domBreakpoints: new WI.ObjectStore("dom-debugger-dom-breakpoints", {keyPath: "__id"}),
|
||||
eventBreakpoints: new WI.ObjectStore("dom-debugger-event-breakpoints", {keyPath: "__id"}),
|
||||
urlBreakpoints: new WI.ObjectStore("dom-debugger-url-breakpoints", {keyPath: "__id"}),
|
||||
|
||||
// Version 4
|
||||
localResourceOverrides: new WI.ObjectStore("local-resource-overrides", {keyPath: "__id"}),
|
||||
|
||||
// Version 5
|
||||
general: new WI.ObjectStore("general"),
|
||||
|
||||
// Version 6
|
||||
cssPropertyNameCounts: new WI.ObjectStore("css-property-name-counts"),
|
||||
|
||||
// Version 7
|
||||
symbolicBreakpoints: new WI.ObjectStore("debugger-symbolic-breakpoints", {keyPath: "__id"}),
|
||||
|
||||
// Version 8
|
||||
consoleSnippets: new WI.ObjectStore("console-snippets", {keyPath: "__id"}),
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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.Platform = {
|
||||
name: InspectorFrontendHost.platform,
|
||||
version: {
|
||||
name: InspectorFrontendHost.platformVersionName,
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.ReferencePage = class ReferencePage {
|
||||
constructor(page, {topic} = {})
|
||||
{
|
||||
console.assert(page instanceof WI.ReferencePage || typeof page === "string", page);
|
||||
console.assert(!(page instanceof WI.ReferencePage && page._page instanceof WI.ReferencePage), page);
|
||||
console.assert(!(page instanceof WI.ReferencePage && page.topic), page);
|
||||
console.assert(!topic || typeof topic === "string", topic);
|
||||
|
||||
if (page instanceof WI.ReferencePage)
|
||||
page = page.page;
|
||||
this._page = page;
|
||||
|
||||
this._topic = topic || "";
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get page() { return this._page; }
|
||||
get topic() { return this._topic; }
|
||||
|
||||
createLinkElement()
|
||||
{
|
||||
let url = "https://webkit.org/web-inspector/" + this._page + "/";
|
||||
if (this._topic)
|
||||
url += "#" + this._topic;
|
||||
|
||||
let wrapper = document.createElement("span");
|
||||
wrapper.className = "reference-page-link-container";
|
||||
|
||||
let link = wrapper.appendChild(document.createElement("a"));
|
||||
link.className = "reference-page-link";
|
||||
link.href = link.title = url;
|
||||
link.textContent = "?";
|
||||
link.addEventListener("click", (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
WI.openURL(link.href, {alwaysOpenExternally: true});
|
||||
});
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
};
|
||||
|
||||
WI.ReferencePage.AuditTab = new WI.ReferencePage("audit-tab");
|
||||
WI.ReferencePage.AuditTab.AuditResults = new WI.ReferencePage(WI.ReferencePage.AuditTab, {topic: "audit-results"});
|
||||
WI.ReferencePage.AuditTab.CreatingAudits = new WI.ReferencePage(WI.ReferencePage.AuditTab, {topic: "creating-audits"});
|
||||
WI.ReferencePage.AuditTab.EditingAudits = new WI.ReferencePage(WI.ReferencePage.AuditTab, {topic: "editing-audits"});
|
||||
WI.ReferencePage.AuditTab.RunningAudits = new WI.ReferencePage(WI.ReferencePage.AuditTab, {topic: "running-audits"});
|
||||
|
||||
WI.ReferencePage.DOMBreakpoints = new WI.ReferencePage("dom-breakpoints");
|
||||
WI.ReferencePage.DOMBreakpoints.Configuration = new WI.ReferencePage(WI.ReferencePage.DOMBreakpoints, {topic: "configuration"});
|
||||
|
||||
WI.ReferencePage.DeviceSettings = new WI.ReferencePage("device-settings");
|
||||
WI.ReferencePage.DeviceSettings.Configuration = new WI.ReferencePage(WI.ReferencePage.DeviceSettings, {topic: "configuration"});
|
||||
|
||||
WI.ReferencePage.ElementsTab = new WI.ReferencePage("elements-tab");
|
||||
WI.ReferencePage.ElementsTab.DOMTree = new WI.ReferencePage(WI.ReferencePage.ElementsTab, {topic: "dom-tree"});
|
||||
|
||||
WI.ReferencePage.EventBreakpoints = new WI.ReferencePage("event-breakpoints");
|
||||
WI.ReferencePage.EventBreakpoints.Configuration = new WI.ReferencePage(WI.ReferencePage.EventBreakpoints, {topic: "configuration"});
|
||||
|
||||
WI.ReferencePage.JavaScriptBreakpoints = new WI.ReferencePage("javascript-breakpoints");
|
||||
WI.ReferencePage.JavaScriptBreakpoints.Configuration = new WI.ReferencePage(WI.ReferencePage.JavaScriptBreakpoints, {topic: "configuration"});
|
||||
|
||||
WI.ReferencePage.LayersTab = new WI.ReferencePage("layers-tab");
|
||||
|
||||
WI.ReferencePage.LocalOverrides = new WI.ReferencePage("local-overrides");
|
||||
WI.ReferencePage.LocalOverrides.ConfiguringLocalOverrides = new WI.ReferencePage(WI.ReferencePage.LocalOverrides, {topic: "configuring-local-overrides"});
|
||||
|
||||
WI.ReferencePage.NetworkTab = new WI.ReferencePage("network-tab");
|
||||
WI.ReferencePage.NetworkTab.CookiesPane = new WI.ReferencePage(WI.ReferencePage.NetworkTab, {topic: "cookies-pane"});
|
||||
WI.ReferencePage.NetworkTab.HeadersPane = new WI.ReferencePage(WI.ReferencePage.NetworkTab, {topic: "headers-pane"});
|
||||
WI.ReferencePage.NetworkTab.PreviewPane = new WI.ReferencePage(WI.ReferencePage.NetworkTab, {topic: "preview-pane"});
|
||||
WI.ReferencePage.NetworkTab.SecurityPane = new WI.ReferencePage(WI.ReferencePage.NetworkTab, {topic: "security-pane"});
|
||||
WI.ReferencePage.NetworkTab.SizesPane = new WI.ReferencePage(WI.ReferencePage.NetworkTab, {topic: "sizes-pane"});
|
||||
WI.ReferencePage.NetworkTab.TimingPane = new WI.ReferencePage(WI.ReferencePage.NetworkTab, {topic: "timing-pane"});
|
||||
|
||||
WI.ReferencePage.SymbolicBreakpoints = new WI.ReferencePage("symbolic-breakpoints");
|
||||
WI.ReferencePage.SymbolicBreakpoints.Configuration = new WI.ReferencePage(WI.ReferencePage.SymbolicBreakpoints, {topic: "configuration"});
|
||||
|
||||
WI.ReferencePage.TimelinesTab = new WI.ReferencePage("timelines-tab");
|
||||
WI.ReferencePage.TimelinesTab.CPUTimeline = new WI.ReferencePage(WI.ReferencePage.TimelinesTab, {topic: "cpu-timeline"});
|
||||
WI.ReferencePage.TimelinesTab.EventsView = new WI.ReferencePage(WI.ReferencePage.TimelinesTab, {topic: "events-view"});
|
||||
WI.ReferencePage.TimelinesTab.FramesView = new WI.ReferencePage(WI.ReferencePage.TimelinesTab, {topic: "frames-view"});
|
||||
WI.ReferencePage.TimelinesTab.JavaScriptAllocationsTimeline = new WI.ReferencePage(WI.ReferencePage.TimelinesTab, {topic: "javascript-allocations-timeline"});
|
||||
WI.ReferencePage.TimelinesTab.JavaScriptAndEventsTimeline = new WI.ReferencePage(WI.ReferencePage.TimelinesTab, {topic: "javascript-events-timeline"});
|
||||
WI.ReferencePage.TimelinesTab.LayoutAndRenderingTimeline = new WI.ReferencePage(WI.ReferencePage.TimelinesTab, {topic: "layout-rendering-timeline"});
|
||||
WI.ReferencePage.TimelinesTab.MediaAndAnimationsTimeline = new WI.ReferencePage(WI.ReferencePage.TimelinesTab, {topic: "media-animations-timeline"});
|
||||
WI.ReferencePage.TimelinesTab.MemoryTimeline = new WI.ReferencePage(WI.ReferencePage.TimelinesTab, {topic: "memory-timeline"});
|
||||
WI.ReferencePage.TimelinesTab.NetworkRequestsTimeline = new WI.ReferencePage(WI.ReferencePage.TimelinesTab, {topic: "network-timeline"});
|
||||
WI.ReferencePage.TimelinesTab.ScreenshotsTimeline = new WI.ReferencePage(WI.ReferencePage.TimelinesTab, {topic: "screenshots-timeline"});
|
||||
|
||||
WI.ReferencePage.URLBreakpoints = new WI.ReferencePage("url-breakpoints");
|
||||
WI.ReferencePage.URLBreakpoints.Configuration = new WI.ReferencePage(WI.ReferencePage.URLBreakpoints, {topic: "configuration"});
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.SearchUtilities = class SearchUtilities {
|
||||
static get defaultSettings()
|
||||
{
|
||||
return {
|
||||
caseSensitive: WI.settings.searchCaseSensitive,
|
||||
regularExpression: WI.settings.searchRegularExpression,
|
||||
};
|
||||
}
|
||||
|
||||
static createSettings(namePrefix)
|
||||
{
|
||||
let settings = {};
|
||||
for (let [key, defaultSetting] of Object.entries(WI.SearchUtilities.defaultSettings)) {
|
||||
let setting = new WI.Setting(namePrefix + "-" + defaultSetting.name, defaultSetting.value);
|
||||
defaultSetting.addEventListener(WI.Setting.Event.Changed, function(event) {
|
||||
this.value = defaultSetting.value;
|
||||
}, setting);
|
||||
settings[key] = setting;
|
||||
}
|
||||
return settings;
|
||||
}
|
||||
|
||||
static searchRegExpForString(query, settings = {})
|
||||
{
|
||||
return WI.SearchUtilities._regExpForString(query, settings, {global: true});
|
||||
}
|
||||
|
||||
static filterRegExpForString(query, settings = {})
|
||||
{
|
||||
return WI.SearchUtilities._regExpForString(query, settings);
|
||||
}
|
||||
|
||||
static createSettingsButton(settings)
|
||||
{
|
||||
console.assert(!isEmptyObject(settings));
|
||||
|
||||
let button = document.createElement("button");
|
||||
button.classList.add("search-settings");
|
||||
button.tabIndex = -1;
|
||||
WI.addMouseDownContextMenuHandlers(button, (contextMenu) => {
|
||||
if (settings.caseSensitive) {
|
||||
contextMenu.appendCheckboxItem(WI.UIString("Case Sensitive", "Case Sensitive @ Context Menu", "Context menu label for whether searches should be case sensitive."), () => {
|
||||
settings.caseSensitive.value = !settings.caseSensitive.value;
|
||||
}, settings.caseSensitive.value);
|
||||
}
|
||||
|
||||
if (settings.regularExpression) {
|
||||
contextMenu.appendCheckboxItem(WI.UIString("Regular Expression", "Regular Expression @ Context Menu", "Context menu label for whether searches should be treated as regular expressions."), () => {
|
||||
settings.regularExpression.value = !settings.regularExpression.value;
|
||||
}, settings.regularExpression.value);
|
||||
}
|
||||
});
|
||||
|
||||
button.appendChild(WI.ImageUtilities.useSVGSymbol("Images/Gear.svg", "glyph"));
|
||||
|
||||
function toggleActive() {
|
||||
button.classList.toggle("active", Object.values(settings).some((setting) => !!setting.value));
|
||||
}
|
||||
settings.caseSensitive.addEventListener(WI.Setting.Event.Changed, toggleActive, button);
|
||||
settings.regularExpression.addEventListener(WI.Setting.Event.Changed, toggleActive, button);
|
||||
toggleActive();
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
static _regExpForString(query, settings = {}, options = {})
|
||||
{
|
||||
function checkSetting(setting) {
|
||||
return setting instanceof WI.Setting ? setting.value : !!setting;
|
||||
}
|
||||
|
||||
console.assert((typeof query === "string" && query) || query instanceof RegExp);
|
||||
|
||||
if (!checkSetting(settings.regularExpression)) {
|
||||
try {
|
||||
query = simpleGlobStringToRegExp(String(query));
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
console.assert((typeof query === "string" && query) || query instanceof RegExp);
|
||||
|
||||
let flags = "";
|
||||
if (options.global)
|
||||
flags += "g"
|
||||
if (!checkSetting(settings.caseSensitive))
|
||||
flags += "i";
|
||||
|
||||
try {
|
||||
return new RegExp(query, flags);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* Copyright (C) 2009 Google Inc. All rights reserved.
|
||||
* Copyright (C) 2013 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:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * 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.
|
||||
* * Neither the name of Google Inc. nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
|
||||
* OWNER OR 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.Setting = class Setting extends WI.Object
|
||||
{
|
||||
constructor(name, defaultValue)
|
||||
{
|
||||
super();
|
||||
|
||||
this._name = name;
|
||||
|
||||
this._defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static migrateValue(key)
|
||||
{
|
||||
let localStorageKey = WI.Setting._localStorageKeyPrefix + key;
|
||||
|
||||
let value = undefined;
|
||||
if (!window.InspectorTest && window.localStorage) {
|
||||
let item = window.localStorage.getItem(localStorageKey);
|
||||
if (item !== null) {
|
||||
try {
|
||||
value = JSON.parse(item);
|
||||
} catch { }
|
||||
|
||||
window.localStorage.removeItem(localStorageKey);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
static reset()
|
||||
{
|
||||
let prefix = WI.Setting._localStorageKeyPrefix;
|
||||
|
||||
let keysToRemove = [];
|
||||
for (let i = 0; i < window.localStorage.length; ++i) {
|
||||
let key = window.localStorage.key(i);
|
||||
if (key.startsWith(prefix))
|
||||
keysToRemove.push(key);
|
||||
}
|
||||
|
||||
for (let key of keysToRemove)
|
||||
window.localStorage.removeItem(key);
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get name() { return this._name; }
|
||||
get defaultValue() { return this._defaultValue; }
|
||||
|
||||
get value()
|
||||
{
|
||||
if ("_value" in this)
|
||||
return this._value;
|
||||
|
||||
// Make a copy of the default value so changes to object values don't modify the default value.
|
||||
this._value = JSON.parse(JSON.stringify(this._defaultValue));
|
||||
|
||||
if (!window.InspectorTest && window.localStorage) {
|
||||
let key = WI.Setting._localStorageKeyPrefix + this._name;
|
||||
let item = window.localStorage.getItem(key);
|
||||
if (item !== null) {
|
||||
try {
|
||||
this._value = JSON.parse(item);
|
||||
} catch {
|
||||
window.localStorage.removeItem(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this._value;
|
||||
}
|
||||
|
||||
set value(value)
|
||||
{
|
||||
if (this._value === value)
|
||||
return;
|
||||
|
||||
this._value = value;
|
||||
|
||||
this.save();
|
||||
}
|
||||
|
||||
save()
|
||||
{
|
||||
if (!window.InspectorTest && window.localStorage) {
|
||||
let key = WI.Setting._localStorageKeyPrefix + this._name;
|
||||
try {
|
||||
if (Object.shallowEqual(this._value, this._defaultValue))
|
||||
window.localStorage.removeItem(key);
|
||||
else
|
||||
window.localStorage.setItem(key, JSON.stringify(this._value));
|
||||
} catch {
|
||||
console.error("Error saving setting with name: " + this._name);
|
||||
}
|
||||
}
|
||||
|
||||
this.dispatchEventToListeners(WI.Setting.Event.Changed, this._value, {name: this._name});
|
||||
}
|
||||
|
||||
reset()
|
||||
{
|
||||
// Make a copy of the default value so changes to object values don't modify the default value.
|
||||
this.value = JSON.parse(JSON.stringify(this._defaultValue));
|
||||
}
|
||||
};
|
||||
|
||||
WI.Setting._localStorageKeyPrefix = (function() {
|
||||
let inspectionLevel = InspectorFrontendHost ? InspectorFrontendHost.inspectionLevel : 1;
|
||||
let levelString = inspectionLevel > 1 ? "-" + inspectionLevel : "";
|
||||
return `com.apple.WebInspector${levelString}.`;
|
||||
})();
|
||||
|
||||
WI.Setting.isFirstLaunch = !!window.InspectorTest || (window.localStorage && Object.keys(window.localStorage).every((key) => !key.startsWith(WI.Setting._localStorageKeyPrefix)));
|
||||
|
||||
WI.Setting.Event = {
|
||||
Changed: "setting-changed"
|
||||
};
|
||||
|
||||
WI.EngineeringSetting = class EngineeringSetting extends WI.Setting
|
||||
{
|
||||
get value()
|
||||
{
|
||||
if (WI.engineeringSettingsAllowed())
|
||||
return super.value;
|
||||
return this.defaultValue;
|
||||
}
|
||||
|
||||
set value(value)
|
||||
{
|
||||
console.assert(WI.engineeringSettingsAllowed());
|
||||
if (WI.engineeringSettingsAllowed())
|
||||
super.value = value;
|
||||
}
|
||||
};
|
||||
|
||||
WI.DebugSetting = class DebugSetting extends WI.Setting
|
||||
{
|
||||
get value()
|
||||
{
|
||||
if (WI.isDebugUIEnabled())
|
||||
return super.value;
|
||||
return this.defaultValue;
|
||||
}
|
||||
|
||||
set value(value)
|
||||
{
|
||||
console.assert(WI.isDebugUIEnabled());
|
||||
if (WI.isDebugUIEnabled())
|
||||
super.value = value;
|
||||
}
|
||||
};
|
||||
|
||||
WI.settings = {
|
||||
blackboxBreakpointEvaluations: new WI.Setting("blackbox-breakpoint-evaluations", true),
|
||||
canvasRecordingAutoCaptureEnabled: new WI.Setting("canvas-recording-auto-capture-enabled", false),
|
||||
canvasRecordingAutoCaptureFrameCount: new WI.Setting("canvas-recording-auto-capture-frame-count", 1),
|
||||
consoleAutoExpandTrace: new WI.Setting("console-auto-expand-trace", true),
|
||||
consoleSavedResultAlias: new WI.Setting("console-saved-result-alias", ""),
|
||||
cssChangesPerNode: new WI.Setting("css-changes-per-node", false),
|
||||
clearLogOnNavigate: new WI.Setting("clear-log-on-navigate", true),
|
||||
clearNetworkOnNavigate: new WI.Setting("clear-network-on-navigate", true),
|
||||
cpuTimelineThreadDetailsExpanded: new WI.Setting("cpu-timeline-thread-details-expanded", false),
|
||||
domTreeDeemphasizesNodesThatAreNotRendered: new WI.Setting("dom-tree-deemphasizes-nodes-that-are-not-rendered", true),
|
||||
emulateInUserGesture: new WI.Setting("emulate-in-user-gesture", false),
|
||||
enableControlFlowProfiler: new WI.Setting("enable-control-flow-profiler", false),
|
||||
enableElementsTabIndependentStylesDetailsSidebarPanel: new WI.Setting("elements-tab-independent-styles-details-panel", true),
|
||||
enableLineWrapping: new WI.Setting("enable-line-wrapping", true),
|
||||
flexOverlayShowOrderNumbers: new WI.Setting("flex-overlay-show-order-numbers", false),
|
||||
frontendAppearance: new WI.Setting("frontend-appearance", "system"),
|
||||
gridOverlayShowAreaNames: new WI.Setting("grid-overlay-show-area-names", false),
|
||||
gridOverlayShowExtendedGridLines: new WI.Setting("grid-overlay-show-extended-grid-lines", false),
|
||||
gridOverlayShowLineNames: new WI.Setting("grid-overlay-show-line-names", false),
|
||||
gridOverlayShowLineNumbers: new WI.Setting("grid-overlay-show-line-numbers", true),
|
||||
gridOverlayShowTrackSizes: new WI.Setting("grid-overlay-show-track-sizes", true),
|
||||
groupMediaRequestsByDOMNode: new WI.Setting("group-media-requests-by-dom-node", WI.Setting.migrateValue("group-by-dom-node") || false),
|
||||
indentUnit: new WI.Setting("indent-unit", 4),
|
||||
indentWithTabs: new WI.Setting("indent-with-tabs", false),
|
||||
resourceCachingDisabled: new WI.Setting("disable-resource-caching", false),
|
||||
searchCaseSensitive: new WI.Setting("search-case-sensitive", false),
|
||||
searchFromSelection: new WI.Setting("search-from-selection", false),
|
||||
searchRegularExpression: new WI.Setting("search-regular-expression", false),
|
||||
selectedNetworkDetailContentViewIdentifier: new WI.Setting("network-detail-content-view-identifier", "preview"),
|
||||
sourceMapsEnabled: new WI.Setting("source-maps-enabled", true),
|
||||
showConsoleMessageTimestamps: new WI.Setting("show-console-message-timestamps", false),
|
||||
showCSSPropertySyntaxInDocumentationPopover: new WI.Setting("show-css-property-syntax-in-documentation-popover", false),
|
||||
showCanvasPath: new WI.Setting("show-canvas-path", false),
|
||||
showImageGrid: new WI.Setting("show-image-grid", true),
|
||||
showInvisibleCharacters: new WI.Setting("show-invisible-characters", !!WI.Setting.migrateValue("show-invalid-characters")),
|
||||
showJavaScriptTypeInformation: new WI.Setting("show-javascript-type-information", false),
|
||||
showRulers: new WI.Setting("show-rulers", false),
|
||||
showRulersDuringElementSelection: new WI.Setting("show-rulers-during-element-selection", true),
|
||||
showScopeChainOnPause: new WI.Setting("show-scope-chain-sidebar", true),
|
||||
showWhitespaceCharacters: new WI.Setting("show-whitespace-characters", false),
|
||||
tabSize: new WI.Setting("tab-size", 4),
|
||||
timelinesAutoStop: new WI.Setting("timelines-auto-stop", true),
|
||||
timelineOverviewGroupBySourceCode: new WI.Setting("timeline-overview-group-by-source-code", true),
|
||||
zoomFactor: new WI.Setting("zoom-factor", 1),
|
||||
|
||||
// Experimental
|
||||
experimentalEnableStylesJumpToEffective: new WI.Setting("experimental-styles-jump-to-effective", false),
|
||||
experimentalEnableStylesJumpToVariableDeclaration: new WI.Setting("experimental-styles-jump-to-variable-declaration", false),
|
||||
experimentalAllowInspectingInspector: new WI.Setting("experimental-allow-inspecting-inspector", false),
|
||||
experimentalCSSSortPropertyNameAutocompletionByUsage: new WI.Setting("experimental-css-sort-property-name-autocompletion-by-usage", true),
|
||||
experimentalEnableNetworkEmulatedCondition: new WI.Setting("experimental-enable-network-emulated-condition", false),
|
||||
|
||||
// Protocol
|
||||
protocolLogAsText: new WI.Setting("protocol-log-as-text", false),
|
||||
protocolAutoLogMessages: new WI.Setting("protocol-auto-log-messages", false),
|
||||
protocolAutoLogTimeStats: new WI.Setting("protocol-auto-log-time-stats", false),
|
||||
protocolFilterMultiplexingBackendMessages: new WI.Setting("protocol-filter-multiplexing-backend-messages", true),
|
||||
|
||||
// Engineering
|
||||
engineeringShowInternalExecutionContexts: new WI.EngineeringSetting("engineering-show-internal-execution-contexts", false),
|
||||
engineeringShowInternalScripts: new WI.EngineeringSetting("engineering-show-internal-scripts", false),
|
||||
engineeringPauseForInternalScripts: new WI.EngineeringSetting("engineering-pause-for-internal-scripts", false),
|
||||
engineeringShowInternalObjectsInHeapSnapshot: new WI.EngineeringSetting("engineering-show-internal-objects-in-heap-snapshot", false),
|
||||
engineeringShowPrivateSymbolsInHeapSnapshot: new WI.EngineeringSetting("engineering-show-private-symbols-in-heap-snapshot", false),
|
||||
engineeringAllowEditingUserAgentShadowTrees: new WI.EngineeringSetting("engineering-allow-editing-user-agent-shadow-trees", false),
|
||||
|
||||
// Debug
|
||||
debugShowConsoleEvaluations: new WI.DebugSetting("debug-show-console-evaluations", false),
|
||||
debugOutlineFocusedElement: new WI.DebugSetting("debug-outline-focused-element", false),
|
||||
debugEnableLayoutFlashing: new WI.DebugSetting("debug-enable-layout-flashing", false),
|
||||
debugEnableStyleEditingDebugMode: new WI.DebugSetting("debug-enable-style-editing-debug-mode", false),
|
||||
debugEnableUncaughtExceptionReporter: new WI.DebugSetting("debug-enable-uncaught-exception-reporter", true),
|
||||
debugEnableDiagnosticLogging: new WI.DebugSetting("debug-enable-diagnostic-logging", true),
|
||||
debugAutoLogDiagnosticEvents: new WI.DebugSetting("debug-auto-log-diagnostic-events", false),
|
||||
debugLayoutDirection: new WI.DebugSetting("debug-layout-direction-override", "system"),
|
||||
debugShowMockWebExtensionTab: new WI.DebugSetting("debug-show-mock-web-extension-tab", false),
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.TargetType = {
|
||||
ITML: "itml",
|
||||
JavaScript: "javascript",
|
||||
Page: "page",
|
||||
ServiceWorker: "service-worker",
|
||||
WebPage: "web-page",
|
||||
Worker: "worker",
|
||||
};
|
||||
|
||||
WI.TargetType.all = Object.values(WI.TargetType);
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.
|
||||
*/
|
||||
|
||||
// Throttler wraps a function and ensures it doesn't get called more than once every `delay` ms.
|
||||
// The first fire will always trigger synchronous, and subsequent calls that need to be throttled
|
||||
// will happen later asynchronously.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// let throttler = new Throttler(250, () => { this.refresh() });
|
||||
// element.addEventListener("keydown", (event) => { throttler.fire(); });
|
||||
//
|
||||
// Will ensure `refresh` happens no more than once every 250ms no matter how often `fire` is called:
|
||||
//
|
||||
// 0ms 250ms 500ms
|
||||
// time: |---------------------------|---------------------------|
|
||||
// fire: ^ ^ ^ ^ ^ ^ ^ ^
|
||||
// refreshes: * (1) * (2) * (3)
|
||||
//
|
||||
// When the wrapped function is actually called, it will be given the most recent set of arguments.
|
||||
|
||||
class Throttler
|
||||
{
|
||||
constructor(callback, delay)
|
||||
{
|
||||
console.assert(typeof callback === "function");
|
||||
console.assert(delay >= 0);
|
||||
|
||||
this._callback = callback;
|
||||
this._delay = delay;
|
||||
|
||||
this._lastArguments = [];
|
||||
|
||||
this._timeoutIdentifier = undefined;
|
||||
this._lastFireTime = -this._delay;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
force()
|
||||
{
|
||||
this._lastArguments = arguments;
|
||||
this._execute();
|
||||
}
|
||||
|
||||
fire()
|
||||
{
|
||||
this._lastArguments = arguments;
|
||||
|
||||
let remaining = this._delay - (Date.now() - this._lastFireTime);
|
||||
if (remaining <= 0) {
|
||||
this._execute();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._timeoutIdentifier)
|
||||
return;
|
||||
|
||||
this._timeoutIdentifier = setTimeout(() => {
|
||||
this._execute();
|
||||
}, remaining);
|
||||
}
|
||||
|
||||
cancel()
|
||||
{
|
||||
this._lastArguments = [];
|
||||
|
||||
if (this._timeoutIdentifier) {
|
||||
clearTimeout(this._timeoutIdentifier);
|
||||
this._timeoutIdentifier = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_execute()
|
||||
{
|
||||
this._lastFireTime = Date.now();
|
||||
|
||||
let args = this._lastArguments;
|
||||
|
||||
this.cancel();
|
||||
|
||||
this._callback.apply(undefined, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,373 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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.
|
||||
*/
|
||||
|
||||
function removeURLFragment(url)
|
||||
{
|
||||
var hashIndex = url.indexOf("#");
|
||||
if (hashIndex >= 0)
|
||||
return url.substring(0, hashIndex);
|
||||
return url;
|
||||
}
|
||||
|
||||
function relativePath(path, basePath)
|
||||
{
|
||||
console.assert(path.charAt(0) === "/");
|
||||
console.assert(basePath.charAt(0) === "/");
|
||||
|
||||
var pathComponents = path.split("/");
|
||||
var baseComponents = basePath.replace(/\/$/, "").split("/");
|
||||
var finalComponents = [];
|
||||
|
||||
var index = 1;
|
||||
for (; index < pathComponents.length && index < baseComponents.length; ++index) {
|
||||
if (pathComponents[index] !== baseComponents[index])
|
||||
break;
|
||||
}
|
||||
|
||||
for (var i = index; i < baseComponents.length; ++i)
|
||||
finalComponents.push("..");
|
||||
|
||||
for (var i = index; i < pathComponents.length; ++i)
|
||||
finalComponents.push(pathComponents[i]);
|
||||
|
||||
return finalComponents.join("/");
|
||||
}
|
||||
|
||||
function parseSecurityOrigin(securityOrigin)
|
||||
{
|
||||
securityOrigin = securityOrigin ? securityOrigin.trim() : "";
|
||||
|
||||
let match = securityOrigin.match(/^(?<scheme>[^:]+):\/\/(?<host>[^\/:]*)(?::(?<port>[\d]+))?$/i);
|
||||
if (!match)
|
||||
return {scheme: null, host: null, port: null};
|
||||
|
||||
let scheme = match.groups.scheme.toLowerCase();
|
||||
let host = match.groups.host.toLowerCase();
|
||||
let port = Number(match.groups.port) || null;
|
||||
|
||||
return {scheme, host, port};
|
||||
}
|
||||
|
||||
function parseDataURL(url)
|
||||
{
|
||||
if (!url.startsWith("data:"))
|
||||
return null;
|
||||
|
||||
// data:[<media type>][;charset=<character set>][;base64],<data>
|
||||
let match = url.match(/^data:(?<mime>[^;,]*)?(?:;charset=(?<charset>[^;,]*?))?(?<base64>;base64)?,(?<data>.*)$/);
|
||||
if (!match)
|
||||
return null;
|
||||
|
||||
let scheme = "data";
|
||||
let mimeType = match.groups.mime || "text/plain";
|
||||
let charset = match.groups.charset || "US-ASCII";
|
||||
let base64 = !!match.groups.base64;
|
||||
let data = decodeURIComponent(match.groups.data);
|
||||
|
||||
return {scheme, mimeType, charset, base64, data};
|
||||
}
|
||||
|
||||
function parseURL(url)
|
||||
{
|
||||
let result = {
|
||||
scheme: null,
|
||||
userinfo: null,
|
||||
host: null,
|
||||
port: null,
|
||||
origin: null,
|
||||
path: null,
|
||||
queryString: null,
|
||||
fragment: null,
|
||||
lastPathComponent: null,
|
||||
};
|
||||
|
||||
// dataURLs should be handled by `parseDataURL`.
|
||||
if (url && url.startsWith("data:")) {
|
||||
result.scheme = "data";
|
||||
return result;
|
||||
}
|
||||
|
||||
// Internal sourceURLs will fail in URL constructor anyways.
|
||||
if (isWebKitInternalScript(url))
|
||||
return result;
|
||||
|
||||
let parsed = null;
|
||||
try {
|
||||
parsed = new URL(url);
|
||||
} catch {
|
||||
return result;
|
||||
}
|
||||
|
||||
result.scheme = parsed.protocol.slice(0, -1); // remove trailing ":"
|
||||
|
||||
if (parsed.username)
|
||||
result.userinfo = parsed.username;
|
||||
if (parsed.password)
|
||||
result.userinfo = (result.userinfo || "") + ":" + parsed.password;
|
||||
|
||||
if (parsed.hostname)
|
||||
result.host = parsed.hostname;
|
||||
|
||||
if (parsed.port)
|
||||
result.port = Number(parsed.port);
|
||||
|
||||
if (parsed.origin && parsed.origin !== "null")
|
||||
result.origin = parsed.origin;
|
||||
else if (result.scheme && result.host) {
|
||||
result.origin = result.scheme + "://" + result.host;
|
||||
if (result.port)
|
||||
result.origin += ":" + result.port;
|
||||
}
|
||||
|
||||
if (parsed.pathname)
|
||||
result.path = parsed.pathname;
|
||||
|
||||
if (parsed.search)
|
||||
result.queryString = parsed.search.substring(1); // remove leading "?"
|
||||
|
||||
if (parsed.hash)
|
||||
result.fragment = parsed.hash.substring(1); // remove leading "#"
|
||||
|
||||
// Find last path component.
|
||||
if (result.path && result.path !== "/") {
|
||||
// Skip the trailing slash if there is one.
|
||||
let endOffset = result.path.endsWith("/") ? 1 : 0;
|
||||
let lastSlashIndex = result.path.lastIndexOf("/", result.path.length - 1 - endOffset);
|
||||
if (lastSlashIndex !== -1)
|
||||
result.lastPathComponent = result.path.substring(lastSlashIndex + 1, result.path.length - endOffset);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function absoluteURL(partialURL, baseURL)
|
||||
{
|
||||
partialURL = partialURL ? partialURL.trim() : "";
|
||||
|
||||
// Return data and javascript URLs as-is.
|
||||
if (partialURL.startsWith("data:") || partialURL.startsWith("javascript:") || partialURL.startsWith("mailto:"))
|
||||
return partialURL;
|
||||
|
||||
// If the URL has a scheme it is already a full URL, so return it.
|
||||
if (parseURL(partialURL).scheme)
|
||||
return partialURL;
|
||||
|
||||
// If there is no partial URL, just return the base URL.
|
||||
if (!partialURL)
|
||||
return baseURL || null;
|
||||
|
||||
var baseURLComponents = parseURL(baseURL);
|
||||
|
||||
// The base URL needs to be an absolute URL. Return null if it isn't.
|
||||
if (!baseURLComponents.scheme)
|
||||
return null;
|
||||
|
||||
// A URL that starts with "//" is a full URL without the scheme. Use the base URL scheme.
|
||||
if (partialURL[0] === "/" && partialURL[1] === "/")
|
||||
return baseURLComponents.scheme + ":" + partialURL;
|
||||
|
||||
// The path can be null for URLs that have just a scheme and host (like "http://apple.com"). So make the path be "/".
|
||||
if (!baseURLComponents.path)
|
||||
baseURLComponents.path = "/";
|
||||
|
||||
// Generate the base URL prefix that is used in the rest of the cases.
|
||||
var baseURLPrefix = baseURLComponents.scheme + "://" + baseURLComponents.host + (baseURLComponents.port ? (":" + baseURLComponents.port) : "");
|
||||
|
||||
// A URL that starts with "?" is just a query string that gets applied to the base URL (replacing the base URL query string and fragment).
|
||||
if (partialURL[0] === "?")
|
||||
return baseURLPrefix + baseURLComponents.path + partialURL;
|
||||
|
||||
// A URL that starts with "/" is an absolute path that gets applied to the base URL (replacing the base URL path, query string and fragment).
|
||||
if (partialURL[0] === "/")
|
||||
return baseURLPrefix + resolveDotsInPath(partialURL);
|
||||
|
||||
// A URL that starts with "#" is just a fragment that gets applied to the base URL (replacing the base URL fragment, maintaining the query string).
|
||||
if (partialURL[0] === "#") {
|
||||
let queryStringComponent = baseURLComponents.queryString ? "?" + baseURLComponents.queryString : "";
|
||||
return baseURLPrefix + baseURLComponents.path + queryStringComponent + partialURL;
|
||||
}
|
||||
|
||||
// Generate the base path that is used in the final case by removing everything after the last "/" from the base URL's path.
|
||||
var basePath = baseURLComponents.path.substring(0, baseURLComponents.path.lastIndexOf("/")) + "/";
|
||||
return baseURLPrefix + resolveDotsInPath(basePath + partialURL);
|
||||
}
|
||||
|
||||
function parseQueryString(queryString, arrayResult)
|
||||
{
|
||||
if (!queryString)
|
||||
return arrayResult ? [] : {};
|
||||
|
||||
function decode(string)
|
||||
{
|
||||
try {
|
||||
// Replace "+" with " " then decode percent encoded values.
|
||||
return decodeURIComponent(string.replace(/\+/g, " "));
|
||||
} catch {
|
||||
return string;
|
||||
}
|
||||
}
|
||||
|
||||
var parameters = arrayResult ? [] : {};
|
||||
for (let parameterString of queryString.split("&")) {
|
||||
let index = parameterString.indexOf("=");
|
||||
if (index === -1)
|
||||
index = parameterString.length;
|
||||
|
||||
let name = decode(parameterString.substring(0, index));
|
||||
let value = decode(parameterString.substring(index + 1));
|
||||
|
||||
if (arrayResult)
|
||||
parameters.push({name, value});
|
||||
else
|
||||
parameters[name] = value;
|
||||
}
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
WI.displayNameForURL = function(url, urlComponents, options = {})
|
||||
{
|
||||
if (url.startsWith("data:"))
|
||||
return WI.truncateURL(url);
|
||||
|
||||
if (!urlComponents)
|
||||
urlComponents = parseURL(url);
|
||||
|
||||
var displayName;
|
||||
try {
|
||||
displayName = decodeURIComponent(urlComponents.lastPathComponent || "");
|
||||
} catch {
|
||||
displayName = urlComponents.lastPathComponent;
|
||||
}
|
||||
|
||||
if (options.allowDirectoryAsName && (urlComponents.path === "/" || (displayName && urlComponents.path.endsWith(displayName + "/"))))
|
||||
displayName = "/";
|
||||
|
||||
return displayName || WI.displayNameForHost(urlComponents.host) || url;
|
||||
};
|
||||
|
||||
WI.truncateURL = function(url, multiline = false, dataURIMaxSize = 6)
|
||||
{
|
||||
if (!url.startsWith("data:"))
|
||||
return url;
|
||||
|
||||
const dataIndex = url.indexOf(",") + 1;
|
||||
let header = url.slice(0, dataIndex);
|
||||
if (multiline)
|
||||
header += "\n";
|
||||
|
||||
const data = url.slice(dataIndex);
|
||||
if (data.length < dataURIMaxSize)
|
||||
return header + data;
|
||||
|
||||
const firstChunk = data.slice(0, Math.ceil(dataURIMaxSize / 2));
|
||||
const ellipsis = "\u2026";
|
||||
const middleChunk = multiline ? `\n${ellipsis}\n` : ellipsis;
|
||||
const lastChunk = data.slice(-Math.floor(dataURIMaxSize / 2));
|
||||
return header + firstChunk + middleChunk + lastChunk;
|
||||
};
|
||||
|
||||
WI.urlWithoutExtension = function(urlString)
|
||||
{
|
||||
let url = null;
|
||||
try {
|
||||
url = new URL(urlString);
|
||||
} catch { }
|
||||
if (!url)
|
||||
return urlString;
|
||||
|
||||
let firstDotInLastPathComponentIndex = url.pathname.indexOf(".", url.pathname.lastIndexOf("/"));
|
||||
if (firstDotInLastPathComponentIndex !== -1)
|
||||
url.pathname = url.pathname.substring(0, firstDotInLastPathComponentIndex);
|
||||
|
||||
url.search = "";
|
||||
url.hash = "";
|
||||
return url.toString();
|
||||
};
|
||||
|
||||
WI.urlWithoutFragment = function(urlString)
|
||||
{
|
||||
try {
|
||||
let url = new URL(urlString);
|
||||
if (url.hash) {
|
||||
url.hash = "";
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
// URL.toString with an empty hash leaves the hash symbol, so we strip it.
|
||||
let result = url.toString();
|
||||
if (result.endsWith("#"))
|
||||
return result.substring(0, result.length - 1);
|
||||
|
||||
return result;
|
||||
} catch { }
|
||||
|
||||
return urlString;
|
||||
};
|
||||
|
||||
WI.displayNameForHost = function(host)
|
||||
{
|
||||
let extensionName = WI.browserManager.extensionNameForId(host);
|
||||
if (extensionName)
|
||||
return extensionName;
|
||||
|
||||
// FIXME <rdar://problem/11237413>: This should decode punycode hostnames.
|
||||
|
||||
return host;
|
||||
};
|
||||
|
||||
// https://tools.ietf.org/html/rfc7540#section-8.1.2.3
|
||||
WI.h2Authority = function(components)
|
||||
{
|
||||
let {scheme, userinfo, host, port} = components;
|
||||
let result = host || "";
|
||||
|
||||
// The authority MUST NOT include the deprecated "userinfo"
|
||||
// subcomponent for "http" or "https" schemed URIs.
|
||||
if (userinfo && (scheme !== "http" && scheme !== "https"))
|
||||
result = userinfo + "@" + result;
|
||||
if (port)
|
||||
result += ":" + port;
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
// https://tools.ietf.org/html/rfc7540#section-8.1.2.3
|
||||
WI.h2Path = function(components)
|
||||
{
|
||||
let {scheme, path, queryString} = components;
|
||||
let result = path || "";
|
||||
|
||||
// The ":path" pseudo-header field includes the path and query parts
|
||||
// of the target URI. [...] This pseudo-header field MUST NOT be empty
|
||||
// for "http" or "https" URIs; "http" or "https" URIs that do not contain
|
||||
// a path component MUST include a value of '/'.
|
||||
if (!path && (scheme === "http" || scheme === "https"))
|
||||
result = "/";
|
||||
if (queryString)
|
||||
result += "?" + queryString;
|
||||
|
||||
return result;
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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.
|
||||
*/
|
||||
|
||||
var WI = {}; // Namespace
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* 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.YieldableTask = class YieldableTask
|
||||
{
|
||||
constructor(delegate, items, options = {})
|
||||
{
|
||||
let {workInterval, idleInterval} = options;
|
||||
console.assert(!workInterval || workInterval > 0, workInterval);
|
||||
console.assert(!idleInterval || idleInterval > 0, idleInterval);
|
||||
|
||||
console.assert(delegate && typeof delegate.yieldableTaskWillProcessItem === "function", "Delegate provide an implementation of method 'yieldableTaskWillProcessItem'.");
|
||||
|
||||
console.assert(items instanceof Object && Symbol.iterator in items, "Argument `items` must subclass Object and be iterable.", items);
|
||||
|
||||
// Milliseconds to run before the task should yield.
|
||||
this._workInterval = workInterval || 10;
|
||||
// Milliseconds to idle before asynchronously resuming the task.
|
||||
this._idleInterval = idleInterval || 0;
|
||||
|
||||
this._delegate = delegate;
|
||||
|
||||
this._items = items;
|
||||
this._idleTimeoutIdentifier = undefined;
|
||||
this._processing = false;
|
||||
this._processing = false;
|
||||
this._cancelled = false;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get processing() { return this._processing; }
|
||||
get cancelled() { return this._cancelled; }
|
||||
|
||||
get idleInterval() { return this._idleInterval; }
|
||||
get workInterval() { return this._workInterval; }
|
||||
|
||||
start()
|
||||
{
|
||||
console.assert(!this._processing);
|
||||
if (this._processing)
|
||||
return;
|
||||
|
||||
console.assert(!this._cancelled);
|
||||
if (this._cancelled)
|
||||
return;
|
||||
|
||||
function* createIteratorForProcessingItems()
|
||||
{
|
||||
let startTime = Date.now();
|
||||
let processedItems = [];
|
||||
|
||||
for (let item of this._items) {
|
||||
if (this._cancelled)
|
||||
break;
|
||||
|
||||
this._delegate.yieldableTaskWillProcessItem(this, item);
|
||||
processedItems.push(item);
|
||||
|
||||
// Calling out to the delegate may cause the task to be cancelled.
|
||||
if (this._cancelled)
|
||||
break;
|
||||
|
||||
let elapsedTime = Date.now() - startTime;
|
||||
if (elapsedTime > this._workInterval) {
|
||||
let returnedItems = processedItems.slice();
|
||||
processedItems = [];
|
||||
this._willYield(returnedItems, elapsedTime);
|
||||
|
||||
yield;
|
||||
|
||||
startTime = Date.now();
|
||||
}
|
||||
}
|
||||
|
||||
// The task sends a fake yield notification to the delegate so that
|
||||
// the delegate receives notification of all processed items before finishing.
|
||||
if (processedItems.length)
|
||||
this._willYield(processedItems, Date.now() - startTime);
|
||||
}
|
||||
|
||||
this._processing = true;
|
||||
this._pendingItemsIterator = createIteratorForProcessingItems.call(this);
|
||||
this._processPendingItems();
|
||||
}
|
||||
|
||||
cancel()
|
||||
{
|
||||
if (!this._processing)
|
||||
return;
|
||||
|
||||
this._cancelled = true;
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_processPendingItems()
|
||||
{
|
||||
console.assert(this._processing);
|
||||
|
||||
if (this._cancelled)
|
||||
return;
|
||||
|
||||
if (!this._pendingItemsIterator.next().done) {
|
||||
this._idleTimeoutIdentifier = setTimeout(() => { this._processPendingItems(); }, this._idleInterval);
|
||||
return;
|
||||
}
|
||||
|
||||
this._didFinish();
|
||||
}
|
||||
|
||||
_willYield(processedItems, elapsedTime)
|
||||
{
|
||||
if (typeof this._delegate.yieldableTaskDidYield === "function")
|
||||
this._delegate.yieldableTaskDidYield(this, processedItems, elapsedTime);
|
||||
}
|
||||
|
||||
_didFinish()
|
||||
{
|
||||
this._processing = false;
|
||||
this._pendingItemsIterator = null;
|
||||
|
||||
if (this._idleTimeoutIdentifier) {
|
||||
clearTimeout(this._idleTimeoutIdentifier);
|
||||
this._idleTimeoutIdentifier = undefined;
|
||||
}
|
||||
|
||||
if (typeof this._delegate.yieldableTaskDidFinish === "function")
|
||||
this._delegate.yieldableTaskDidFinish(this);
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user