added Linux arm64 SDK

This commit is contained in:
Andrew Zambazos
2026-06-11 14:07:38 +12:00
parent c0395a49bd
commit cbf3f085d0
2160 changed files with 1 additions and 542 deletions
+70
View File
@@ -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;
}
+413
View File
@@ -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;
};
+134
View File
@@ -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);
}
}
+82
View File
@@ -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;
};
+91
View File
@@ -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;
}
};
+92
View File
@@ -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 = [];
}
};
+275
View File
@@ -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",
};
+107
View File
@@ -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,
]);
+58
View File
@@ -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);
},
});
+131
View File
@@ -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;
+141
View File
@@ -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);
});
}
}
+112
View File
@@ -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;
}
}
+117
View File
@@ -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")
};
+345
View File
@@ -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
+142
View File
@@ -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);
}
}
+222
View File
@@ -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",
};
+284
View File
@@ -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"}),
};
+31
View File
@@ -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,
}
};
+120
View File
@@ -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"});
+121
View File
@@ -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;
}
}
};
+263
View File
@@ -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),
};
+35
View File
@@ -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);
+108
View File
@@ -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);
}
}
+373
View File
@@ -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
+26
View File
@@ -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
+154
View File
@@ -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);
}
};