summaryrefslogtreecommitdiff
path: root/Source/WebInspectorUI/UserInterface/Base
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebInspectorUI/UserInterface/Base
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/WebInspectorUI/UserInterface/Base')
-rw-r--r--Source/WebInspectorUI/UserInterface/Base/DOMUtilities.js314
-rw-r--r--Source/WebInspectorUI/UserInterface/Base/EventListener.js90
-rw-r--r--Source/WebInspectorUI/UserInterface/Base/EventListenerSet.js91
-rw-r--r--Source/WebInspectorUI/UserInterface/Base/ImageUtilities.js53
-rw-r--r--Source/WebInspectorUI/UserInterface/Base/InspectorFrontendHostStub.js200
-rw-r--r--Source/WebInspectorUI/UserInterface/Base/LinkedList.js112
-rw-r--r--Source/WebInspectorUI/UserInterface/Base/ListMultimap.js117
-rw-r--r--Source/WebInspectorUI/UserInterface/Base/LoadLocalizedStrings.js34
-rw-r--r--Source/WebInspectorUI/UserInterface/Base/MIMETypeUtilities.js122
-rw-r--r--Source/WebInspectorUI/UserInterface/Base/Main.js2693
-rw-r--r--Source/WebInspectorUI/UserInterface/Base/Object.js229
-rw-r--r--Source/WebInspectorUI/UserInterface/Base/Platform.js61
-rw-r--r--Source/WebInspectorUI/UserInterface/Base/Setting.js118
-rw-r--r--Source/WebInspectorUI/UserInterface/Base/URLUtilities.js265
-rw-r--r--Source/WebInspectorUI/UserInterface/Base/Utilities.js1509
-rw-r--r--Source/WebInspectorUI/UserInterface/Base/WebInspector.js26
-rw-r--r--Source/WebInspectorUI/UserInterface/Base/YieldableTask.js156
17 files changed, 6190 insertions, 0 deletions
diff --git a/Source/WebInspectorUI/UserInterface/Base/DOMUtilities.js b/Source/WebInspectorUI/UserInterface/Base/DOMUtilities.js
new file mode 100644
index 000000000..ba423aa31
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Base/DOMUtilities.js
@@ -0,0 +1,314 @@
+/*
+ * 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.
+ */
+
+WebInspector.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;
+};
+
+WebInspector.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 = WebInspector.linkifyNodeReference(node);
+ var tagIdSelector = link.title;
+ var classSelectorIndex = tagIdSelector.indexOf(".");
+ if (classSelectorIndex > -1)
+ tagIdSelector = tagIdSelector.substring(0, classSelectorIndex);
+ var roleSelector = WebInspector.roleSelectorForNode(node);
+ link.textContent = tagIdSelector + roleSelector;
+ link.title += roleSelector;
+ return link;
+};
+
+WebInspector.linkifyNodeReference = function(node, maxLength)
+{
+ let displayName = node.displayName;
+ if (!isNaN(maxLength))
+ displayName = displayName.truncate(maxLength);
+
+ let link = document.createElement("span");
+ link.append(displayName);
+ link.setAttribute("role", "link");
+
+ link.title = displayName;
+
+ let nodeType = node.nodeType();
+ if ((nodeType !== Node.DOCUMENT_NODE || node.parentNode) && nodeType !== Node.TEXT_NODE)
+ link.className = "node-link";
+
+ link.addEventListener("click", WebInspector.domTreeManager.inspectElement.bind(WebInspector.domTreeManager, node.id));
+ link.addEventListener("mouseover", WebInspector.domTreeManager.highlightDOMNode.bind(WebInspector.domTreeManager, node.id, "all"));
+ link.addEventListener("mouseout", WebInspector.domTreeManager.hideDOMNodeHighlight.bind(WebInspector.domTreeManager));
+
+ return link;
+};
+
+function createSVGElement(tagName)
+{
+ return document.createElementNS("http://www.w3.org/2000/svg", tagName);
+}
+
+WebInspector.cssPath = function(node)
+{
+ console.assert(node instanceof WebInspector.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 = WebInspector.cssPathComponent(node);
+ if (!component)
+ break;
+ components.push(component);
+ if (component.done)
+ break;
+ node = node.parentNode;
+ }
+
+ components.reverse();
+ return components.map((x) => x.value).join(" > ") + suffix;
+};
+
+WebInspector.cssPathComponent = function(node)
+{
+ console.assert(node instanceof WebInspector.DOMNode, "Expected a DOMNode.");
+ console.assert(!node.isPseudoElement());
+ if (node.nodeType() !== Node.ELEMENT_NODE)
+ return null;
+
+ let nodeName = node.nodeNameInCorrectCase();
+ 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};
+
+ // Root node does not have siblings.
+ if (!node.parentNode || node.parentNode.nodeType() === Node.DOCUMENT_NODE)
+ return {value: nodeName, 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};
+};
+
+WebInspector.xpath = function(node)
+{
+ console.assert(node instanceof WebInspector.DOMNode, "Expected a DOMNode.");
+
+ if (node.nodeType() === Node.DOCUMENT_NODE)
+ return "/";
+
+ let components = [];
+ while (node) {
+ let component = WebInspector.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("/");
+};
+
+WebInspector.xpathComponent = function(node)
+{
+ console.assert(node instanceof WebInspector.DOMNode, "Expected a DOMNode.");
+
+ let index = WebInspector.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};
+};
+
+WebInspector.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;
+};
diff --git a/Source/WebInspectorUI/UserInterface/Base/EventListener.js b/Source/WebInspectorUI/UserInterface/Base/EventListener.js
new file mode 100644
index 000000000..b009f887d
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Base/EventListener.js
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+
+WebInspector.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(callback, "Missing callback for event: " + type);
+ console.assert(emitter, "Missing event emitter for event: " + type);
+ var emitterIsValid = emitter && (emitter instanceof WebInspector.Object || emitter instanceof Node || (typeof emitter.addEventListener === "function"));
+ console.assert(emitterIsValid, "Event emitter ", emitter, " (type:" + type + ") is null or does not implement Node or WebInspector.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;
+ }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Base/EventListenerSet.js b/Source/WebInspectorUI/UserInterface/Base/EventListenerSet.js
new file mode 100644
index 000000000..bc392c430
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Base/EventListenerSet.js
@@ -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.
+ */
+
+// 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.
+
+WebInspector.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(callback, "Missing callback for event: " + type);
+ console.assert(type, "Tried to register listener for unknown event: " + type);
+ var emitterIsValid = emitter && (emitter instanceof WebInspector.Object || emitter instanceof Node || (typeof emitter.addEventListener === "function"));
+ console.assert(emitterIsValid, "Event emitter ", emitter, " (type:" + type + ") is null or does not implement Node or WebInspector.Object!");
+
+ if (!emitterIsValid || !type || !callback)
+ return;
+
+ this._listeners.push({listener: new WebInspector.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 = [];
+ }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Base/ImageUtilities.js b/Source/WebInspectorUI/UserInterface/Base/ImageUtilities.js
new file mode 100644
index 000000000..f04831881
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Base/ImageUtilities.js
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2013, 2015 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 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;
+}
diff --git a/Source/WebInspectorUI/UserInterface/Base/InspectorFrontendHostStub.js b/Source/WebInspectorUI/UserInterface/Base/InspectorFrontendHostStub.js
new file mode 100644
index 000000000..505e9d4af
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Base/InspectorFrontendHostStub.js
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ * Copyright (C) 2013 Seokju Kwon (seokju.kwon@gmail.com)
+ * 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.
+ */
+
+if (!window.InspectorFrontendHost) {
+ WebInspector.InspectorFrontendHostStub = function()
+ {
+ };
+
+ WebInspector.InspectorFrontendHostStub.prototype = {
+ // Public
+
+ initializeWebSocket: function(url)
+ {
+ var socket = new WebSocket(url);
+ socket.addEventListener("open", socketReady.bind(this));
+
+ function socketReady()
+ {
+ this._socket = socket;
+
+ this._socket.addEventListener("message", function(message) { InspectorBackend.dispatch(message.data); });
+ this._socket.addEventListener("error", function(error) { console.error(error); });
+
+ this._sendPendingMessagesToBackendIfNeeded();
+ }
+ },
+
+ bringToFront: function()
+ {
+ this._windowVisible = true;
+ },
+
+ closeWindow: function()
+ {
+ this._windowVisible = false;
+ },
+
+ userInterfaceLayoutDirection: function()
+ {
+ return "ltr";
+ },
+
+ requestSetDockSide: function(side)
+ {
+ InspectorFrontendAPI.setDockSide(side);
+ },
+
+ setAttachedWindowHeight: function(height)
+ {
+ },
+
+ setAttachedWindowWidth: function(width)
+ {
+ },
+
+ startWindowDrag: function()
+ {
+ },
+
+ moveWindowBy: function(x, y)
+ {
+ },
+
+ loaded: function()
+ {
+ },
+
+ localizedStringsURL: function()
+ {
+ return undefined;
+ },
+
+ backendCommandsURL: function()
+ {
+ return undefined;
+ },
+
+ debuggableType: function()
+ {
+ return "web";
+ },
+
+ inspectionLevel: function()
+ {
+ return 1;
+ },
+
+ inspectedURLChanged: function(title)
+ {
+ document.title = title;
+ },
+
+ copyText: function(text)
+ {
+ this._textToCopy = text;
+ if (!document.execCommand("copy"))
+ console.error("Clipboard access is denied");
+ },
+
+ killText: function(text, shouldStartNewSequence)
+ {
+ },
+
+ openInNewTab: function(url)
+ {
+ window.open(url, "_blank");
+ },
+
+ save: function(url, content, base64Encoded, forceSaveAs)
+ {
+ },
+
+ sendMessageToBackend: function(message)
+ {
+ if (!this._socket) {
+ if (!this._pendingMessages)
+ this._pendingMessages = [];
+ this._pendingMessages.push(message);
+ return;
+ }
+
+ this._sendPendingMessagesToBackendIfNeeded();
+
+ this._socket.send(message);
+ },
+
+ platform: function()
+ {
+ return (navigator.platform.match(/mac|win|linux/i) || ["other"])[0].toLowerCase();
+ },
+
+ beep: function()
+ {
+ },
+
+ showContextMenu: function(event, menuObject)
+ {
+ },
+
+ unbufferedLog: function()
+ {
+ console.log.apply(console, arguments);
+ },
+
+ setZoomFactor: function(zoom)
+ {
+ },
+
+ zoomFactor: function()
+ {
+ return 1;
+ },
+
+ // Private
+
+ _sendPendingMessagesToBackendIfNeeded: function()
+ {
+ if (!this._pendingMessages)
+ return;
+
+ for (var i = 0; i < this._pendingMessages.length; ++i)
+ this._socket.send(this._pendingMessages[i]);
+
+ delete this._pendingMessages;
+ }
+ };
+
+ InspectorFrontendHost = new WebInspector.InspectorFrontendHostStub;
+
+ WebInspector.dontLocalizeUserInterface = true;
+}
diff --git a/Source/WebInspectorUI/UserInterface/Base/LinkedList.js b/Source/WebInspectorUI/UserInterface/Base/LinkedList.js
new file mode 100644
index 000000000..f9978af42
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Base/LinkedList.js
@@ -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;
+ }
+}
diff --git a/Source/WebInspectorUI/UserInterface/Base/ListMultimap.js b/Source/WebInspectorUI/UserInterface/Base/ListMultimap.js
new file mode 100644
index 000000000..a415fb68f
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Base/ListMultimap.js
@@ -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();
+ }
+}
diff --git a/Source/WebInspectorUI/UserInterface/Base/LoadLocalizedStrings.js b/Source/WebInspectorUI/UserInterface/Base/LoadLocalizedStrings.js
new file mode 100644
index 000000000..84c6e08fe
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Base/LoadLocalizedStrings.js
@@ -0,0 +1,34 @@
+/*
+ * 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 (WebInspector.dontLocalizeUserInterface)
+ return;
+
+ var localizedStringsURL = InspectorFrontendHost.localizedStringsURL();
+ console.assert(localizedStringsURL);
+ if (localizedStringsURL)
+ document.write("<script src=\"" + localizedStringsURL + "\"></script>");
+})();
diff --git a/Source/WebInspectorUI/UserInterface/Base/MIMETypeUtilities.js b/Source/WebInspectorUI/UserInterface/Base/MIMETypeUtilities.js
new file mode 100644
index 000000000..42bced52e
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Base/MIMETypeUtilities.js
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+
+WebInspector.fileExtensionForURL = function(url)
+{
+ var lastPathComponent = parseURL(url).lastPathComponent;
+ if (!lastPathComponent)
+ return "";
+
+ var index = lastPathComponent.indexOf(".");
+ if (index === -1)
+ return "";
+
+ return lastPathComponent.substr(index + 1);
+};
+
+WebInspector.mimeTypeForFileExtension = function(extension)
+{
+ const extensionToMIMEType = {
+ // Document types.
+ "html": "text/html",
+ "xhtml": "application/xhtml+xml",
+ "xml": "text/xml",
+
+ // Script types.
+ "js": "text/javascript",
+ "json": "application/json",
+ "clj": "text/x-clojure",
+ "coffee": "text/x-coffeescript",
+ "ls": "text/x-livescript",
+ "ts": "text/typescript",
+
+ // Stylesheet types.
+ "css": "text/css",
+ "less": "text/x-less",
+ "sass": "text/x-sass",
+ "scss": "text/x-scss",
+
+ // Image types.
+ "bmp": "image/bmp",
+ "gif": "image/gif",
+ "jpeg": "image/jpeg",
+ "jpg": "image/jpeg",
+ "pdf": "application/pdf",
+ "png": "image/png",
+ "tif": "image/tiff",
+ "tiff": "image/tiff",
+
+ // Font types and Media types are ignored for now.
+
+ // Miscellaneous types.
+ "svg": "image/svg+xml",
+ "txt": "text/plain",
+ "xsl": "text/xsl"
+ };
+
+ return extensionToMIMEType[extension] || null;
+};
+
+WebInspector.fileExtensionForMIMEType = function(mimeType)
+{
+ const mimeTypeToExtension = {
+ // Document types.
+ "text/html": "html",
+ "application/xhtml+xml": "xhtml",
+ "text/xml": "xml",
+
+ // Script types.
+ "text/javascript": "js",
+ "application/json": "json",
+ "text/x-clojure": "clj",
+ "text/x-coffeescript": "coffee",
+ "text/x-livescript": "ls",
+ "text/typescript": "ts",
+
+ // Stylesheet types.
+ "text/css": "css",
+ "text/x-less": "less",
+ "text/x-sass": "sass",
+ "text/x-scss": "scss",
+
+ // Image types.
+ "image/bmp": "bmp",
+ "image/gif": "gif",
+ "image/jpeg": "jpg",
+ "application/pdf": "pdf",
+ "image/png": "png",
+ "image/tiff": "tiff",
+
+ // Font types and Media types are ignored for now.
+
+ // Miscellaneous types.
+ "image/svg+xml": "svg",
+ "text/plain": "txt",
+ "text/xsl": "xsl",
+ };
+
+ let extension = mimeTypeToExtension[mimeType];
+ return extension ? `.${extension}` : null;
+};
diff --git a/Source/WebInspectorUI/UserInterface/Base/Main.js b/Source/WebInspectorUI/UserInterface/Base/Main.js
new file mode 100644
index 000000000..11495f4ee
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Base/Main.js
@@ -0,0 +1,2693 @@
+/*
+ * Copyright (C) 2013-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.
+ */
+
+WebInspector.ContentViewCookieType = {
+ ApplicationCache: "application-cache",
+ CookieStorage: "cookie-storage",
+ Database: "database",
+ DatabaseTable: "database-table",
+ DOMStorage: "dom-storage",
+ Resource: "resource", // includes Frame too.
+ Timelines: "timelines"
+};
+
+WebInspector.DebuggableType = {
+ Web: "web",
+ JavaScript: "javascript"
+};
+
+WebInspector.SelectedSidebarPanelCookieKey = "selected-sidebar-panel";
+WebInspector.TypeIdentifierCookieKey = "represented-object-type";
+
+WebInspector.StateRestorationType = {
+ Load: "state-restoration-load",
+ Navigation: "state-restoration-navigation",
+ Delayed: "state-restoration-delayed",
+};
+
+WebInspector.LayoutDirection = {
+ System: "system",
+ LTR: "ltr",
+ RTL: "rtl",
+};
+
+WebInspector.loaded = function()
+{
+ // Initialize WebSocket to communication.
+ this._initializeWebSocketIfNeeded();
+
+ this.debuggableType = InspectorFrontendHost.debuggableType() === "web" ? WebInspector.DebuggableType.Web : WebInspector.DebuggableType.JavaScript;
+ this.hasExtraDomains = false;
+
+ // Register observers for events from the InspectorBackend.
+ if (InspectorBackend.registerInspectorDispatcher)
+ InspectorBackend.registerInspectorDispatcher(new WebInspector.InspectorObserver);
+ if (InspectorBackend.registerPageDispatcher)
+ InspectorBackend.registerPageDispatcher(new WebInspector.PageObserver);
+ if (InspectorBackend.registerConsoleDispatcher)
+ InspectorBackend.registerConsoleDispatcher(new WebInspector.ConsoleObserver);
+ if (InspectorBackend.registerNetworkDispatcher)
+ InspectorBackend.registerNetworkDispatcher(new WebInspector.NetworkObserver);
+ if (InspectorBackend.registerDOMDispatcher)
+ InspectorBackend.registerDOMDispatcher(new WebInspector.DOMObserver);
+ if (InspectorBackend.registerDebuggerDispatcher)
+ InspectorBackend.registerDebuggerDispatcher(new WebInspector.DebuggerObserver);
+ if (InspectorBackend.registerHeapDispatcher)
+ InspectorBackend.registerHeapDispatcher(new WebInspector.HeapObserver);
+ if (InspectorBackend.registerMemoryDispatcher)
+ InspectorBackend.registerMemoryDispatcher(new WebInspector.MemoryObserver);
+ if (InspectorBackend.registerDatabaseDispatcher)
+ InspectorBackend.registerDatabaseDispatcher(new WebInspector.DatabaseObserver);
+ if (InspectorBackend.registerDOMStorageDispatcher)
+ InspectorBackend.registerDOMStorageDispatcher(new WebInspector.DOMStorageObserver);
+ if (InspectorBackend.registerApplicationCacheDispatcher)
+ InspectorBackend.registerApplicationCacheDispatcher(new WebInspector.ApplicationCacheObserver);
+ if (InspectorBackend.registerScriptProfilerDispatcher)
+ InspectorBackend.registerScriptProfilerDispatcher(new WebInspector.ScriptProfilerObserver);
+ if (InspectorBackend.registerTimelineDispatcher)
+ InspectorBackend.registerTimelineDispatcher(new WebInspector.TimelineObserver);
+ if (InspectorBackend.registerCSSDispatcher)
+ InspectorBackend.registerCSSDispatcher(new WebInspector.CSSObserver);
+ if (InspectorBackend.registerLayerTreeDispatcher)
+ InspectorBackend.registerLayerTreeDispatcher(new WebInspector.LayerTreeObserver);
+ if (InspectorBackend.registerRuntimeDispatcher)
+ InspectorBackend.registerRuntimeDispatcher(new WebInspector.RuntimeObserver);
+ if (InspectorBackend.registerWorkerDispatcher)
+ InspectorBackend.registerWorkerDispatcher(new WebInspector.WorkerObserver);
+ if (InspectorBackend.registerReplayDispatcher)
+ InspectorBackend.registerReplayDispatcher(new WebInspector.ReplayObserver);
+
+ // Main backend target.
+ WebInspector.mainTarget = new WebInspector.MainTarget;
+
+ // Enable agents.
+ InspectorAgent.enable();
+
+ // Perform one-time tasks.
+ WebInspector.CSSCompletions.requestCSSCompletions();
+
+ // Listen for the ProvisionalLoadStarted event before registering for events so our code gets called before any managers or sidebars.
+ // This lets us save a state cookie before any managers or sidebars do any resets that would affect state (namely TimelineManager).
+ WebInspector.Frame.addEventListener(WebInspector.Frame.Event.ProvisionalLoadStarted, this._provisionalLoadStarted, this);
+
+ // Create the singleton managers next, before the user interface elements, so the user interface can register
+ // as event listeners on these managers.
+ this.targetManager = new WebInspector.TargetManager;
+ this.branchManager = new WebInspector.BranchManager;
+ this.frameResourceManager = new WebInspector.FrameResourceManager;
+ this.storageManager = new WebInspector.StorageManager;
+ this.domTreeManager = new WebInspector.DOMTreeManager;
+ this.cssStyleManager = new WebInspector.CSSStyleManager;
+ this.logManager = new WebInspector.LogManager;
+ this.issueManager = new WebInspector.IssueManager;
+ this.analyzerManager = new WebInspector.AnalyzerManager;
+ this.runtimeManager = new WebInspector.RuntimeManager;
+ this.heapManager = new WebInspector.HeapManager;
+ this.memoryManager = new WebInspector.MemoryManager;
+ this.applicationCacheManager = new WebInspector.ApplicationCacheManager;
+ this.timelineManager = new WebInspector.TimelineManager;
+ this.debuggerManager = new WebInspector.DebuggerManager;
+ this.sourceMapManager = new WebInspector.SourceMapManager;
+ this.layerTreeManager = new WebInspector.LayerTreeManager;
+ this.dashboardManager = new WebInspector.DashboardManager;
+ this.probeManager = new WebInspector.ProbeManager;
+ this.workerManager = new WebInspector.WorkerManager;
+ this.replayManager = new WebInspector.ReplayManager;
+
+ // Enable the Console Agent after creating the singleton managers.
+ ConsoleAgent.enable();
+
+ // Tell the backend we are initialized after all our initialization messages have been sent.
+ setTimeout(function() {
+ // COMPATIBILITY (iOS 8): Inspector.initialized did not exist yet.
+ if (InspectorAgent.initialized)
+ InspectorAgent.initialized();
+ }, 0);
+
+ // Register for events.
+ this.replayManager.addEventListener(WebInspector.ReplayManager.Event.CaptureStarted, this._captureDidStart, this);
+ this.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.Paused, this._debuggerDidPause, this);
+ this.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.Resumed, this._debuggerDidResume, this);
+ this.domTreeManager.addEventListener(WebInspector.DOMTreeManager.Event.InspectModeStateChanged, this._inspectModeStateChanged, this);
+ this.domTreeManager.addEventListener(WebInspector.DOMTreeManager.Event.DOMNodeWasInspected, this._domNodeWasInspected, this);
+ this.storageManager.addEventListener(WebInspector.StorageManager.Event.DOMStorageObjectWasInspected, this._storageWasInspected, this);
+ this.storageManager.addEventListener(WebInspector.StorageManager.Event.DatabaseWasInspected, this._storageWasInspected, this);
+ this.frameResourceManager.addEventListener(WebInspector.FrameResourceManager.Event.MainFrameDidChange, this._mainFrameDidChange, this);
+ this.frameResourceManager.addEventListener(WebInspector.FrameResourceManager.Event.FrameWasAdded, this._frameWasAdded, this);
+
+ WebInspector.Frame.addEventListener(WebInspector.Frame.Event.MainResourceDidChange, this._mainResourceDidChange, this);
+
+ document.addEventListener("DOMContentLoaded", this.contentLoaded.bind(this));
+
+ // Create settings.
+ this._showingSplitConsoleSetting = new WebInspector.Setting("showing-split-console", false);
+ this._splitConsoleHeightSetting = new WebInspector.Setting("split-console-height", 150);
+
+ this._openTabsSetting = new WebInspector.Setting("open-tab-types", ["elements", "network", "resources", "timeline", "debugger", "storage", "console"]);
+ this._selectedTabIndexSetting = new WebInspector.Setting("selected-tab-index", 0);
+
+ this.showShadowDOMSetting = new WebInspector.Setting("show-shadow-dom", false);
+ this.showReplayInterfaceSetting = new WebInspector.Setting("show-web-replay", false);
+
+ // COMPATIBILITY (iOS 8): Page.enableTypeProfiler did not exist.
+ this.showJavaScriptTypeInformationSetting = new WebInspector.Setting("show-javascript-type-information", false);
+ this.showJavaScriptTypeInformationSetting.addEventListener(WebInspector.Setting.Event.Changed, this._showJavaScriptTypeInformationSettingChanged, this);
+ if (this.showJavaScriptTypeInformationSetting.value && window.RuntimeAgent && RuntimeAgent.enableTypeProfiler)
+ RuntimeAgent.enableTypeProfiler();
+
+ this.enableControlFlowProfilerSetting = new WebInspector.Setting("enable-control-flow-profiler", false);
+ this.enableControlFlowProfilerSetting.addEventListener(WebInspector.Setting.Event.Changed, this._enableControlFlowProfilerSettingChanged, this);
+ if (this.enableControlFlowProfilerSetting.value && window.RuntimeAgent && RuntimeAgent.enableControlFlowProfiler)
+ RuntimeAgent.enableControlFlowProfiler();
+
+ // COMPATIBILITY (iOS 8): Page.setShowPaintRects did not exist.
+ this.showPaintRectsSetting = new WebInspector.Setting("show-paint-rects", false);
+ if (this.showPaintRectsSetting.value && window.PageAgent && PageAgent.setShowPaintRects)
+ PageAgent.setShowPaintRects(true);
+
+ this.showPrintStylesSetting = new WebInspector.Setting("show-print-styles", false);
+ if (this.showPrintStylesSetting.value && window.PageAgent)
+ PageAgent.setEmulatedMedia("print");
+
+ this.setZoomFactor(WebInspector.settings.zoomFactor.value);
+
+ this.mouseCoords = {
+ x: 0,
+ y: 0
+ };
+
+ this.visible = false;
+
+ this._windowKeydownListeners = [];
+};
+
+WebInspector.contentLoaded = function()
+{
+ // If there was an uncaught exception earlier during loading, then
+ // abort loading more content. We could be in an inconsistent state.
+ if (window.__uncaughtExceptions)
+ return;
+
+ // Register for global events.
+ document.addEventListener("beforecopy", this._beforecopy.bind(this));
+ document.addEventListener("copy", this._copy.bind(this));
+
+ document.addEventListener("click", this._mouseWasClicked.bind(this));
+ document.addEventListener("dragover", this._dragOver.bind(this));
+ document.addEventListener("focus", WebInspector._focusChanged.bind(this), true);
+
+ window.addEventListener("focus", this._windowFocused.bind(this));
+ window.addEventListener("blur", this._windowBlurred.bind(this));
+ window.addEventListener("resize", this._windowResized.bind(this));
+ window.addEventListener("keydown", this._windowKeyDown.bind(this));
+ window.addEventListener("keyup", this._windowKeyUp.bind(this));
+ window.addEventListener("mousedown", this._mouseDown.bind(this), true);
+ window.addEventListener("mousemove", this._mouseMoved.bind(this), true);
+ window.addEventListener("pagehide", this._pageHidden.bind(this));
+ window.addEventListener("contextmenu", this._contextMenuRequested.bind(this));
+
+ // Add platform style classes so the UI can be tweaked per-platform.
+ document.body.classList.add(WebInspector.Platform.name + "-platform");
+ if (WebInspector.Platform.isNightlyBuild)
+ document.body.classList.add("nightly-build");
+
+ if (WebInspector.Platform.name === "mac") {
+ document.body.classList.add(WebInspector.Platform.version.name);
+
+ if (WebInspector.Platform.version.release >= 11)
+ document.body.classList.add("latest-mac");
+ else
+ document.body.classList.add("legacy-mac");
+ }
+
+ document.body.classList.add(this.debuggableType);
+ document.body.setAttribute("dir", this.resolvedLayoutDirection());
+
+ function setTabSize() {
+ document.body.style.tabSize = WebInspector.settings.tabSize.value;
+ }
+ WebInspector.settings.tabSize.addEventListener(WebInspector.Setting.Event.Changed, setTabSize);
+ setTabSize();
+
+ function setInvalidCharacterClassName() {
+ document.body.classList.toggle("show-invalid-characters", WebInspector.settings.showInvalidCharacters.value);
+ }
+ WebInspector.settings.showInvalidCharacters.addEventListener(WebInspector.Setting.Event.Changed, setInvalidCharacterClassName);
+ setInvalidCharacterClassName();
+
+ function setWhitespaceCharacterClassName() {
+ document.body.classList.toggle("show-whitespace-characters", WebInspector.settings.showWhitespaceCharacters.value);
+ }
+ WebInspector.settings.showWhitespaceCharacters.addEventListener(WebInspector.Setting.Event.Changed, setWhitespaceCharacterClassName);
+ setWhitespaceCharacterClassName();
+
+ this.settingsTabContentView = new WebInspector.SettingsTabContentView;
+
+ this._settingsKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl, WebInspector.KeyboardShortcut.Key.Comma, this._showSettingsTab.bind(this));
+
+ // Create the user interface elements.
+ this.toolbar = new WebInspector.Toolbar(document.getElementById("toolbar"), null, true);
+ this.toolbar.displayMode = WebInspector.Toolbar.DisplayMode.IconOnly;
+ this.toolbar.sizeMode = WebInspector.Toolbar.SizeMode.Small;
+
+ this.tabBar = new WebInspector.TabBar(document.getElementById("tab-bar"));
+ this.tabBar.addEventListener(WebInspector.TabBar.Event.OpenDefaultTab, this._openDefaultTab, this);
+
+ this._contentElement = document.getElementById("content");
+ this._contentElement.setAttribute("role", "main");
+ this._contentElement.setAttribute("aria-label", WebInspector.UIString("Content"));
+
+ const disableBackForward = true;
+ const disableFindBanner = false;
+ this.splitContentBrowser = new WebInspector.ContentBrowser(document.getElementById("split-content-browser"), this, disableBackForward, disableFindBanner);
+ this.splitContentBrowser.navigationBar.element.addEventListener("mousedown", this._consoleResizerMouseDown.bind(this));
+
+ this.clearKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl, "K", this._clear.bind(this));
+
+ this.quickConsole = new WebInspector.QuickConsole(document.getElementById("quick-console"));
+ this.quickConsole.addEventListener(WebInspector.QuickConsole.Event.DidResize, this._quickConsoleDidResize, this);
+
+ this._consoleRepresentedObject = new WebInspector.LogObject;
+ this._consoleTreeElement = new WebInspector.LogTreeElement(this._consoleRepresentedObject);
+ this.consoleContentView = WebInspector.splitContentBrowser.contentViewForRepresentedObject(this._consoleRepresentedObject);
+ this.consoleLogViewController = this.consoleContentView.logViewController;
+ this.breakpointPopoverController = new WebInspector.BreakpointPopoverController;
+
+ // FIXME: The sidebars should be flipped in RTL languages.
+ this.navigationSidebar = new WebInspector.Sidebar(document.getElementById("navigation-sidebar"), WebInspector.Sidebar.Sides.Left);
+ this.navigationSidebar.addEventListener(WebInspector.Sidebar.Event.WidthDidChange, this._sidebarWidthDidChange, this);
+
+ this.detailsSidebar = new WebInspector.Sidebar(document.getElementById("details-sidebar"), WebInspector.Sidebar.Sides.Right, null, null, WebInspector.UIString("Details"), true);
+ this.detailsSidebar.addEventListener(WebInspector.Sidebar.Event.WidthDidChange, this._sidebarWidthDidChange, this);
+
+ this.searchKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl | WebInspector.KeyboardShortcut.Modifier.Shift, "F", this._focusSearchField.bind(this));
+ this._findKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl, "F", this._find.bind(this));
+ this._saveKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl, "S", this._save.bind(this));
+ this._saveAsKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.Shift | WebInspector.KeyboardShortcut.Modifier.CommandOrControl, "S", this._saveAs.bind(this));
+
+ this.openResourceKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl | WebInspector.KeyboardShortcut.Modifier.Shift, "O", this._showOpenResourceDialog.bind(this));
+ new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl, "P", this._showOpenResourceDialog.bind(this));
+
+ this.navigationSidebarKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl | WebInspector.KeyboardShortcut.Modifier.Shift, "0", this.toggleNavigationSidebar.bind(this));
+ this.detailsSidebarKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl | WebInspector.KeyboardShortcut.Modifier.Option, "0", this.toggleDetailsSidebar.bind(this));
+
+ let boundIncreaseZoom = this._increaseZoom.bind(this);
+ let boundDecreaseZoom = this._decreaseZoom.bind(this);
+ this._increaseZoomKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl, WebInspector.KeyboardShortcut.Key.Plus, boundIncreaseZoom);
+ this._decreaseZoomKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl, WebInspector.KeyboardShortcut.Key.Minus, boundDecreaseZoom);
+ this._increaseZoomKeyboardShortcut2 = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl | WebInspector.KeyboardShortcut.Modifier.Shift, WebInspector.KeyboardShortcut.Key.Plus, boundIncreaseZoom);
+ this._decreaseZoomKeyboardShortcut2 = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl | WebInspector.KeyboardShortcut.Modifier.Shift, WebInspector.KeyboardShortcut.Key.Minus, boundDecreaseZoom);
+ this._resetZoomKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl, "0", this._resetZoom.bind(this));
+
+ this._showTabAtIndexKeyboardShortcuts = [1, 2, 3, 4, 5, 6, 7, 8, 9].map((i) => new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl | WebInspector.KeyboardShortcut.Modifier.Option, `${i}`, this._showTabAtIndex.bind(this, i)));
+ this._openNewTabKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl | WebInspector.KeyboardShortcut.Modifier.Option, "T", this.showNewTabTab.bind(this));
+
+ this.tabBrowser = new WebInspector.TabBrowser(document.getElementById("tab-browser"), this.tabBar, this.navigationSidebar, this.detailsSidebar);
+ this.tabBrowser.addEventListener(WebInspector.TabBrowser.Event.SelectedTabContentViewDidChange, this._tabBrowserSelectedTabContentViewDidChange, this);
+
+ this._reloadPageKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl, "R", this._reloadPage.bind(this));
+ this._reloadPageIgnoringCacheKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl | WebInspector.KeyboardShortcut.Modifier.Shift, "R", this._reloadPageIgnoringCache.bind(this));
+ this._reloadPageKeyboardShortcut.implicitlyPreventsDefault = this._reloadPageIgnoringCacheKeyboardShortcut.implicitlyPreventsDefault = false;
+
+ this._consoleTabKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.Option | WebInspector.KeyboardShortcut.Modifier.CommandOrControl, "C", this._showConsoleTab.bind(this));
+ this._quickConsoleKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.Control, WebInspector.KeyboardShortcut.Key.Apostrophe, this._focusConsolePrompt.bind(this));
+
+ this._inspectModeKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl | WebInspector.KeyboardShortcut.Modifier.Shift, "C", this._toggleInspectMode.bind(this));
+
+ this._undoKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl, "Z", this._undoKeyboardShortcut.bind(this));
+ this._redoKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl | WebInspector.KeyboardShortcut.Modifier.Shift, "Z", this._redoKeyboardShortcut.bind(this));
+ this._undoKeyboardShortcut.implicitlyPreventsDefault = this._redoKeyboardShortcut.implicitlyPreventsDefault = false;
+
+ this.toggleBreakpointsKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl, "Y", this.debuggerToggleBreakpoints.bind(this));
+ this.pauseOrResumeKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.Control | WebInspector.KeyboardShortcut.Modifier.CommandOrControl, "Y", this.debuggerPauseResumeToggle.bind(this));
+ this.stepOverKeyboardShortcut = new WebInspector.KeyboardShortcut(null, WebInspector.KeyboardShortcut.Key.F6, this.debuggerStepOver.bind(this));
+ this.stepIntoKeyboardShortcut = new WebInspector.KeyboardShortcut(null, WebInspector.KeyboardShortcut.Key.F7, this.debuggerStepInto.bind(this));
+ this.stepOutKeyboardShortcut = new WebInspector.KeyboardShortcut(null, WebInspector.KeyboardShortcut.Key.F8, this.debuggerStepOut.bind(this));
+
+ this.pauseOrResumeAlternateKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl, WebInspector.KeyboardShortcut.Key.Backslash, this.debuggerPauseResumeToggle.bind(this));
+ this.stepOverAlternateKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl, WebInspector.KeyboardShortcut.Key.SingleQuote, this.debuggerStepOver.bind(this));
+ this.stepIntoAlternateKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl, WebInspector.KeyboardShortcut.Key.Semicolon, this.debuggerStepInto.bind(this));
+ this.stepOutAlternateKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.Shift | WebInspector.KeyboardShortcut.Modifier.CommandOrControl, WebInspector.KeyboardShortcut.Key.Semicolon, this.debuggerStepOut.bind(this));
+
+ this._closeToolbarButton = new WebInspector.ControlToolbarItem("dock-close", WebInspector.UIString("Close"), "Images/Close.svg", 16, 14);
+ this._closeToolbarButton.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, this.close, this);
+
+ this._undockToolbarButton = new WebInspector.ButtonToolbarItem("undock", WebInspector.UIString("Detach into separate window"), null, "Images/Undock.svg");
+ this._undockToolbarButton.element.classList.add(WebInspector.Popover.IgnoreAutoDismissClassName);
+ this._undockToolbarButton.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, this._undock, this);
+
+ let dockImage = WebInspector.resolvedLayoutDirection() === WebInspector.LayoutDirection.RTL ? "Images/DockLeft.svg" : "Images/DockRight.svg";
+ this._dockToSideToolbarButton = new WebInspector.ButtonToolbarItem("dock-right", WebInspector.UIString("Dock to side of window"), null, dockImage);
+ this._dockToSideToolbarButton.element.classList.add(WebInspector.Popover.IgnoreAutoDismissClassName);
+
+ let dockToSideCallback = WebInspector.resolvedLayoutDirection() === WebInspector.LayoutDirection.RTL ? this._dockLeft : this._dockRight;
+ this._dockToSideToolbarButton.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, dockToSideCallback, this);
+
+ this._dockBottomToolbarButton = new WebInspector.ButtonToolbarItem("dock-bottom", WebInspector.UIString("Dock to bottom of window"), null, "Images/DockBottom.svg");
+ this._dockBottomToolbarButton.element.classList.add(WebInspector.Popover.IgnoreAutoDismissClassName);
+ this._dockBottomToolbarButton.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, this._dockBottom, this);
+
+ this._togglePreviousDockConfigurationKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl | WebInspector.KeyboardShortcut.Modifier.Shift, "D", this._togglePreviousDockConfiguration.bind(this));
+
+ var toolTip;
+ if (WebInspector.debuggableType === WebInspector.DebuggableType.JavaScript)
+ toolTip = WebInspector.UIString("Restart (%s)").format(this._reloadPageKeyboardShortcut.displayName);
+ else
+ toolTip = WebInspector.UIString("Reload page (%s)\nReload ignoring cache (%s)").format(this._reloadPageKeyboardShortcut.displayName, this._reloadPageIgnoringCacheKeyboardShortcut.displayName);
+
+ this._reloadToolbarButton = new WebInspector.ButtonToolbarItem("reload", toolTip, null, "Images/ReloadToolbar.svg");
+ this._reloadToolbarButton.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, this._reloadPageClicked, this);
+
+ this._downloadToolbarButton = new WebInspector.ButtonToolbarItem("download", WebInspector.UIString("Download Web Archive"), null, "Images/DownloadArrow.svg");
+ this._downloadToolbarButton.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, this._downloadWebArchive, this);
+
+ this._updateReloadToolbarButton();
+ this._updateDownloadToolbarButton();
+
+ // The toolbar button for node inspection.
+ if (this.debuggableType === WebInspector.DebuggableType.Web) {
+ var toolTip = WebInspector.UIString("Start element selection (%s)").format(WebInspector._inspectModeKeyboardShortcut.displayName);
+ var activatedToolTip = WebInspector.UIString("Stop element selection (%s)").format(WebInspector._inspectModeKeyboardShortcut.displayName);
+ this._inspectModeToolbarButton = new WebInspector.ActivateButtonToolbarItem("inspect", toolTip, activatedToolTip, null, "Images/Crosshair.svg");
+ this._inspectModeToolbarButton.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, this._toggleInspectMode, this);
+ }
+
+ this._dashboardContainer = new WebInspector.DashboardContainerView;
+ this._dashboardContainer.showDashboardViewForRepresentedObject(this.dashboardManager.dashboards.default);
+
+ this._searchToolbarItem = new WebInspector.SearchBar("inspector-search", WebInspector.UIString("Search"), null, true);
+ this._searchToolbarItem.addEventListener(WebInspector.SearchBar.Event.TextChanged, this._searchTextDidChange, this);
+
+ this.toolbar.addToolbarItem(this._closeToolbarButton, WebInspector.Toolbar.Section.Control);
+
+ this.toolbar.addToolbarItem(this._undockToolbarButton, WebInspector.Toolbar.Section.Left);
+ this.toolbar.addToolbarItem(this._dockToSideToolbarButton, WebInspector.Toolbar.Section.Left);
+ this.toolbar.addToolbarItem(this._dockBottomToolbarButton, WebInspector.Toolbar.Section.Left);
+
+ this.toolbar.addToolbarItem(this._reloadToolbarButton, WebInspector.Toolbar.Section.CenterLeft);
+ this.toolbar.addToolbarItem(this._downloadToolbarButton, WebInspector.Toolbar.Section.CenterLeft);
+
+ this.toolbar.addToolbarItem(this._dashboardContainer.toolbarItem, WebInspector.Toolbar.Section.Center);
+
+ if (this._inspectModeToolbarButton)
+ this.toolbar.addToolbarItem(this._inspectModeToolbarButton, WebInspector.Toolbar.Section.CenterRight);
+
+ this.toolbar.addToolbarItem(this._searchToolbarItem, WebInspector.Toolbar.Section.Right);
+
+ this.resourceDetailsSidebarPanel = new WebInspector.ResourceDetailsSidebarPanel;
+ this.domNodeDetailsSidebarPanel = new WebInspector.DOMNodeDetailsSidebarPanel;
+ this.cssStyleDetailsSidebarPanel = new WebInspector.CSSStyleDetailsSidebarPanel;
+ this.applicationCacheDetailsSidebarPanel = new WebInspector.ApplicationCacheDetailsSidebarPanel;
+ this.indexedDatabaseDetailsSidebarPanel = new WebInspector.IndexedDatabaseDetailsSidebarPanel;
+ this.scopeChainDetailsSidebarPanel = new WebInspector.ScopeChainDetailsSidebarPanel;
+ this.probeDetailsSidebarPanel = new WebInspector.ProbeDetailsSidebarPanel;
+
+ if (window.LayerTreeAgent)
+ this.layerTreeDetailsSidebarPanel = new WebInspector.LayerTreeDetailsSidebarPanel;
+
+ this.modifierKeys = {altKey: false, metaKey: false, shiftKey: false};
+
+ let dockedResizerElement = document.getElementById("docked-resizer");
+ dockedResizerElement.classList.add(WebInspector.Popover.IgnoreAutoDismissClassName);
+ dockedResizerElement.addEventListener("mousedown", this._dockedResizerMouseDown.bind(this));
+
+ this._dockingAvailable = false;
+
+ this._updateDockNavigationItems();
+ this._setupViewHierarchy();
+
+ // These tabs are always available for selecting, modulo isTabAllowed().
+ // Other tabs may be engineering-only or toggled at runtime if incomplete.
+ let productionTabClasses = [
+ WebInspector.ConsoleTabContentView,
+ WebInspector.DebuggerTabContentView,
+ WebInspector.ElementsTabContentView,
+ WebInspector.NetworkTabContentView,
+ WebInspector.NewTabContentView,
+ WebInspector.ResourcesTabContentView,
+ WebInspector.SearchTabContentView,
+ WebInspector.SettingsTabContentView,
+ WebInspector.StorageTabContentView,
+ WebInspector.TimelineTabContentView,
+ ];
+
+ this._knownTabClassesByType = new Map;
+ // Set tab classes directly. The public API triggers other updates and
+ // notifications that won't work or have no listeners at this point.
+ for (let tabClass of productionTabClasses)
+ this._knownTabClassesByType.set(tabClass.Type, tabClass);
+
+ this._pendingOpenTabs = [];
+
+ // Previously we may have stored duplicates in this setting. Avoid creating duplicate tabs.
+ let openTabTypes = this._openTabsSetting.value;
+ let seenTabTypes = new Set;
+
+ for (let i = 0; i < openTabTypes.length; ++i) {
+ let tabType = openTabTypes[i];
+
+ if (seenTabTypes.has(tabType))
+ continue;
+ seenTabTypes.add(tabType);
+
+ if (!this.isTabTypeAllowed(tabType)) {
+ this._pendingOpenTabs.push({tabType, index: i});
+ continue;
+ }
+
+ let tabContentView = this._createTabContentViewForType(tabType);
+ if (!tabContentView)
+ continue;
+ this.tabBrowser.addTabForContentView(tabContentView, true);
+ }
+
+ this._restoreCookieForOpenTabs(WebInspector.StateRestorationType.Load);
+
+ this.tabBar.selectedTabBarItem = this._selectedTabIndexSetting.value;
+
+ if (!this.tabBar.selectedTabBarItem)
+ this.tabBar.selectedTabBarItem = 0;
+
+ if (!this.tabBar.normalTabCount)
+ this.showNewTabTab();
+
+ // Listen to the events after restoring the saved tabs to avoid recursion.
+ this.tabBar.addEventListener(WebInspector.TabBar.Event.TabBarItemAdded, this._rememberOpenTabs, this);
+ this.tabBar.addEventListener(WebInspector.TabBar.Event.TabBarItemRemoved, this._rememberOpenTabs, this);
+ this.tabBar.addEventListener(WebInspector.TabBar.Event.TabBarItemsReordered, this._rememberOpenTabs, this);
+
+ // Signal that the frontend is now ready to receive messages.
+ InspectorFrontendAPI.loadCompleted();
+
+ // Tell the InspectorFrontendHost we loaded, which causes the window to display
+ // and pending InspectorFrontendAPI commands to be sent.
+ InspectorFrontendHost.loaded();
+
+ this._updateSplitConsoleHeight(this._splitConsoleHeightSetting.value);
+
+ if (this._showingSplitConsoleSetting.value)
+ this.showSplitConsole();
+
+ // Store this on the window in case the WebInspector global gets corrupted.
+ window.__frontendCompletedLoad = true;
+
+ if (this.runBootstrapOperations)
+ this.runBootstrapOperations();
+};
+
+WebInspector.isTabTypeAllowed = function(tabType)
+{
+ let tabClass = this._knownTabClassesByType.get(tabType);
+ if (!tabClass)
+ return false;
+
+ return tabClass.isTabAllowed();
+};
+
+WebInspector.knownTabClasses = function()
+{
+ return new Set(this._knownTabClassesByType.values());
+};
+
+WebInspector._showOpenResourceDialog = function()
+{
+ if (!this._openResourceDialog)
+ this._openResourceDialog = new WebInspector.OpenResourceDialog(this);
+
+ if (this._openResourceDialog.visible)
+ return;
+
+ this._openResourceDialog.present(this._contentElement);
+};
+
+WebInspector._createTabContentViewForType = function(tabType)
+{
+ let tabClass = this._knownTabClassesByType.get(tabType);
+ if (!tabClass) {
+ console.error("Unknown tab type", tabType);
+ return null;
+ }
+
+ console.assert(WebInspector.TabContentView.isPrototypeOf(tabClass));
+ return new tabClass;
+};
+
+WebInspector._rememberOpenTabs = function()
+{
+ let seenTabTypes = new Set;
+ let openTabs = [];
+
+ for (let tabBarItem of this.tabBar.tabBarItems) {
+ let tabContentView = tabBarItem.representedObject;
+ if (!(tabContentView instanceof WebInspector.TabContentView))
+ continue;
+ if (!tabContentView.constructor.shouldSaveTab())
+ continue;
+ console.assert(tabContentView.type, "Tab type can't be null, undefined, or empty string", tabContentView.type, tabContentView);
+ openTabs.push(tabContentView.type);
+ seenTabTypes.add(tabContentView.type);
+ }
+
+ // Keep currently unsupported tabs in the setting at their previous index.
+ for (let {tabType, index} of this._pendingOpenTabs) {
+ if (seenTabTypes.has(tabType))
+ continue;
+ openTabs.insertAtIndex(tabType, index);
+ seenTabTypes.add(tabType);
+ }
+
+ this._openTabsSetting.value = openTabs;
+};
+
+WebInspector._openDefaultTab = function(event)
+{
+ this.showNewTabTab();
+};
+
+WebInspector._showSettingsTab = function(event)
+{
+ this.tabBrowser.showTabForContentView(this.settingsTabContentView);
+};
+
+WebInspector._tryToRestorePendingTabs = function()
+{
+ let stillPendingOpenTabs = [];
+ for (let {tabType, index} of this._pendingOpenTabs) {
+ if (!this.isTabTypeAllowed(tabType)) {
+ stillPendingOpenTabs.push({tabType, index});
+ continue;
+ }
+
+ let tabContentView = this._createTabContentViewForType(tabType);
+ if (!tabContentView)
+ continue;
+
+ this.tabBrowser.addTabForContentView(tabContentView, true, index);
+
+ tabContentView.restoreStateFromCookie(WebInspector.StateRestorationType.Load);
+ }
+
+ this._pendingOpenTabs = stillPendingOpenTabs;
+
+ this.tabBrowser.tabBar.updateNewTabTabBarItemState();
+};
+
+WebInspector.showNewTabTab = function(shouldAnimate)
+{
+ if (!this.isNewTabWithTypeAllowed(WebInspector.NewTabContentView.Type))
+ return;
+
+ let tabContentView = this.tabBrowser.bestTabContentViewForClass(WebInspector.NewTabContentView);
+ if (!tabContentView)
+ tabContentView = new WebInspector.NewTabContentView;
+ this.tabBrowser.showTabForContentView(tabContentView, !shouldAnimate);
+};
+
+WebInspector.isNewTabWithTypeAllowed = function(tabType)
+{
+ let tabClass = this._knownTabClassesByType.get(tabType);
+ if (!tabClass || !tabClass.isTabAllowed())
+ return false;
+
+ // Only allow one tab per class for now.
+ for (let tabBarItem of this.tabBar.tabBarItems) {
+ let tabContentView = tabBarItem.representedObject;
+ if (!(tabContentView instanceof WebInspector.TabContentView))
+ continue;
+ if (tabContentView.constructor === tabClass)
+ return false;
+ }
+
+ if (tabClass === WebInspector.NewTabContentView) {
+ let allTabs = Array.from(this.knownTabClasses());
+ let addableTabs = allTabs.filter((tabClass) => !tabClass.isEphemeral());
+ let canMakeNewTab = addableTabs.some((tabClass) => WebInspector.isNewTabWithTypeAllowed(tabClass.Type));
+ return canMakeNewTab;
+ }
+
+ return true;
+};
+
+WebInspector.createNewTabWithType = function(tabType, options = {})
+{
+ console.assert(this.isNewTabWithTypeAllowed(tabType));
+
+ let {referencedView, shouldReplaceTab, shouldShowNewTab} = options;
+ console.assert(!referencedView || referencedView instanceof WebInspector.TabContentView, referencedView);
+ console.assert(!shouldReplaceTab || referencedView, "Must provide a reference view to replace a tab.");
+
+ let tabContentView = this._createTabContentViewForType(tabType);
+ const suppressAnimations = true;
+ let insertionIndex = referencedView ? this.tabBar.tabBarItems.indexOf(referencedView.tabBarItem) : undefined;
+ this.tabBrowser.addTabForContentView(tabContentView, suppressAnimations, insertionIndex);
+
+ if (shouldReplaceTab)
+ this.tabBrowser.closeTabForContentView(referencedView, suppressAnimations);
+
+ if (shouldShowNewTab)
+ this.tabBrowser.showTabForContentView(tabContentView);
+};
+
+WebInspector.registerTabClass = function(tabClass)
+{
+ console.assert(WebInspector.TabContentView.isPrototypeOf(tabClass));
+ if (!WebInspector.TabContentView.isPrototypeOf(tabClass))
+ return;
+
+ if (this._knownTabClassesByType.has(tabClass.Type))
+ return;
+
+ this._knownTabClassesByType.set(tabClass.Type, tabClass);
+
+ this._tryToRestorePendingTabs();
+ this.notifications.dispatchEventToListeners(WebInspector.Notification.TabTypesChanged);
+};
+
+WebInspector.activateExtraDomains = function(domains)
+{
+ this.hasExtraDomains = true;
+
+ for (var domain of domains) {
+ var agent = InspectorBackend.activateDomain(domain);
+ if (agent.enable)
+ agent.enable();
+ }
+
+ this.notifications.dispatchEventToListeners(WebInspector.Notification.ExtraDomainsActivated, {"domains": domains});
+
+ WebInspector.CSSCompletions.requestCSSCompletions();
+
+ this._updateReloadToolbarButton();
+ this._updateDownloadToolbarButton();
+ this._tryToRestorePendingTabs();
+};
+
+WebInspector.contentBrowserTreeElementForRepresentedObject = function(contentBrowser, representedObject)
+{
+ // The console does not have a sidebar, so return a tree element here so something is shown.
+ if (representedObject === this._consoleRepresentedObject)
+ return this._consoleTreeElement;
+ return null;
+};
+
+WebInspector.updateWindowTitle = function()
+{
+ var mainFrame = this.frameResourceManager.mainFrame;
+ if (!mainFrame)
+ return;
+
+ var urlComponents = mainFrame.mainResource.urlComponents;
+
+ var lastPathComponent;
+ try {
+ lastPathComponent = decodeURIComponent(urlComponents.lastPathComponent || "");
+ } catch (e) {
+ lastPathComponent = urlComponents.lastPathComponent;
+ }
+
+ // Build a title based on the URL components.
+ if (urlComponents.host && lastPathComponent)
+ var title = this.displayNameForHost(urlComponents.host) + " \u2014 " + lastPathComponent;
+ else if (urlComponents.host)
+ var title = this.displayNameForHost(urlComponents.host);
+ else if (lastPathComponent)
+ var title = lastPathComponent;
+ else
+ var title = mainFrame.url;
+
+ // The name "inspectedURLChanged" sounds like the whole URL is required, however this is only
+ // used for updating the window title and it can be any string.
+ InspectorFrontendHost.inspectedURLChanged(title);
+};
+
+WebInspector.updateDockingAvailability = function(available)
+{
+ this._dockingAvailable = available;
+
+ this._updateDockNavigationItems();
+};
+
+WebInspector.updateDockedState = function(side)
+{
+ if (this._dockConfiguration === side)
+ return;
+
+ this._previousDockConfiguration = this._dockConfiguration;
+
+ if (!this._previousDockConfiguration) {
+ if (side === WebInspector.DockConfiguration.Right || side === WebInspector.DockConfiguration.Left)
+ this._previousDockConfiguration = WebInspector.DockConfiguration.Bottom;
+ else
+ this._previousDockConfiguration = WebInspector.resolvedLayoutDirection() === WebInspector.LayoutDirection.RTL ? WebInspector.DockConfiguration.Left : WebInspector.DockConfiguration.Right;
+ }
+
+ this._dockConfiguration = side;
+
+ this.docked = side !== WebInspector.DockConfiguration.Undocked;
+
+ this._ignoreToolbarModeDidChangeEvents = true;
+
+ if (side === WebInspector.DockConfiguration.Bottom) {
+ document.body.classList.add("docked", WebInspector.DockConfiguration.Bottom);
+ document.body.classList.remove("window-inactive", WebInspector.DockConfiguration.Right, WebInspector.DockConfiguration.Left);
+ } else if (side === WebInspector.DockConfiguration.Right) {
+ document.body.classList.add("docked", WebInspector.DockConfiguration.Right);
+ document.body.classList.remove("window-inactive", WebInspector.DockConfiguration.Bottom, WebInspector.DockConfiguration.Left);
+ } else if (side === WebInspector.DockConfiguration.Left) {
+ document.body.classList.add("docked", WebInspector.DockConfiguration.Left);
+ document.body.classList.remove("window-inactive", WebInspector.DockConfiguration.Bottom, WebInspector.DockConfiguration.Right);
+ } else
+ document.body.classList.remove("docked", WebInspector.DockConfiguration.Right, WebInspector.DockConfiguration.Left, WebInspector.DockConfiguration.Bottom);
+
+ this._ignoreToolbarModeDidChangeEvents = false;
+
+ this._updateDockNavigationItems();
+
+ if (!this.dockedConfigurationSupportsSplitContentBrowser() && !this.doesCurrentTabSupportSplitContentBrowser())
+ this.hideSplitConsole();
+};
+
+WebInspector.updateVisibilityState = function(visible)
+{
+ this.visible = visible;
+ this.notifications.dispatchEventToListeners(WebInspector.Notification.VisibilityStateDidChange);
+};
+
+WebInspector.handlePossibleLinkClick = function(event, frame, alwaysOpenExternally)
+{
+ var anchorElement = event.target.enclosingNodeOrSelfWithNodeName("a");
+ if (!anchorElement || !anchorElement.href)
+ return false;
+
+ if (WebInspector.isBeingEdited(anchorElement)) {
+ // Don't follow the link when it is being edited.
+ return false;
+ }
+
+ // Prevent the link from navigating, since we don't do any navigation by following links normally.
+ event.preventDefault();
+ event.stopPropagation();
+
+ this.openURL(anchorElement.href, frame, false, anchorElement.lineNumber);
+
+ return true;
+};
+
+WebInspector.openURL = function(url, frame, alwaysOpenExternally, lineNumber)
+{
+ console.assert(url);
+ if (!url)
+ return;
+
+ console.assert(typeof lineNumber === "undefined" || typeof lineNumber === "number", "lineNumber should be a number.");
+
+ // If alwaysOpenExternally is not defined, base it off the command/meta key for the current event.
+ if (alwaysOpenExternally === undefined || alwaysOpenExternally === null)
+ alwaysOpenExternally = window.event ? window.event.metaKey : false;
+
+ if (alwaysOpenExternally) {
+ InspectorFrontendHost.openInNewTab(url);
+ return;
+ }
+
+ var searchChildFrames = false;
+ if (!frame) {
+ frame = this.frameResourceManager.mainFrame;
+ searchChildFrames = true;
+ }
+
+ console.assert(frame);
+
+ // WebInspector.Frame.resourceForURL does not check the main resource, only sub-resources. So check both.
+ let simplifiedURL = removeURLFragment(url);
+ var resource = frame.url === simplifiedURL ? frame.mainResource : frame.resourceForURL(simplifiedURL, searchChildFrames);
+ if (resource) {
+ var position = new WebInspector.SourceCodePosition(lineNumber, 0);
+ this.showSourceCode(resource, position);
+ return;
+ }
+
+ InspectorFrontendHost.openInNewTab(url);
+};
+
+WebInspector.close = function()
+{
+ if (this._isClosing)
+ return;
+
+ this._isClosing = true;
+
+ InspectorFrontendHost.closeWindow();
+};
+
+WebInspector.saveDataToFile = function(saveData, forceSaveAs)
+{
+ console.assert(saveData);
+ if (!saveData)
+ return;
+
+ if (typeof saveData.customSaveHandler === "function") {
+ saveData.customSaveHandler(forceSaveAs);
+ return;
+ }
+
+ console.assert(saveData.url);
+ console.assert(saveData.content);
+ if (!saveData.url || !saveData.content)
+ return;
+
+ let suggestedName = parseURL(saveData.url).lastPathComponent;
+ if (!suggestedName) {
+ suggestedName = WebInspector.UIString("Untitled");
+ let dataURLTypeMatch = /^data:([^;]+)/.exec(saveData.url);
+ if (dataURLTypeMatch)
+ suggestedName += WebInspector.fileExtensionForMIMEType(dataURLTypeMatch[1]) || "";
+ }
+
+ if (typeof saveData.content === "string") {
+ const base64Encoded = false;
+ InspectorFrontendHost.save(suggestedName, saveData.content, base64Encoded, forceSaveAs || saveData.forceSaveAs);
+ return;
+ }
+
+ let fileReader = new FileReader;
+ fileReader.readAsDataURL(saveData.content);
+ fileReader.addEventListener("loadend", () => {
+ let dataURLComponents = parseDataURL(fileReader.result);
+
+ const base64Encoded = true;
+ InspectorFrontendHost.save(suggestedName, dataURLComponents.data, base64Encoded, forceSaveAs || saveData.forceSaveAs);
+ });
+};
+
+WebInspector.isConsoleFocused = function()
+{
+ return this.quickConsole.prompt.focused;
+};
+
+WebInspector.isShowingSplitConsole = function()
+{
+ return !this.splitContentBrowser.element.classList.contains("hidden");
+};
+
+WebInspector.dockedConfigurationSupportsSplitContentBrowser = function()
+{
+ return this._dockConfiguration !== WebInspector.DockConfiguration.Bottom;
+};
+
+WebInspector.doesCurrentTabSupportSplitContentBrowser = function()
+{
+ var currentContentView = this.tabBrowser.selectedTabContentView;
+ return !currentContentView || currentContentView.supportsSplitContentBrowser;
+};
+
+WebInspector.toggleSplitConsole = function()
+{
+ if (!this.doesCurrentTabSupportSplitContentBrowser()) {
+ this.showConsoleTab();
+ return;
+ }
+
+ if (this.isShowingSplitConsole())
+ this.hideSplitConsole();
+ else
+ this.showSplitConsole();
+};
+
+WebInspector.showSplitConsole = function()
+{
+ if (!this.doesCurrentTabSupportSplitContentBrowser()) {
+ this.showConsoleTab();
+ return;
+ }
+
+ this.splitContentBrowser.element.classList.remove("hidden");
+
+ this._showingSplitConsoleSetting.value = true;
+
+ if (this.splitContentBrowser.currentContentView !== this.consoleContentView) {
+ // Be sure to close the view in the tab content browser before showing it in the
+ // split content browser. We can only show a content view in one browser at a time.
+ if (this.consoleContentView.parentContainer)
+ this.consoleContentView.parentContainer.closeContentView(this.consoleContentView);
+ this.splitContentBrowser.showContentView(this.consoleContentView);
+ } else {
+ // This causes the view to know it was shown and focus the prompt.
+ this.splitContentBrowser.shown();
+ }
+
+ this.quickConsole.consoleLogVisibilityChanged(true);
+};
+
+WebInspector.hideSplitConsole = function()
+{
+ if (!this.isShowingSplitConsole())
+ return;
+
+ this.splitContentBrowser.element.classList.add("hidden");
+
+ this._showingSplitConsoleSetting.value = false;
+
+ // This causes the view to know it was hidden.
+ this.splitContentBrowser.hidden();
+
+ this.quickConsole.consoleLogVisibilityChanged(false);
+};
+
+WebInspector.showConsoleTab = function(requestedScope)
+{
+ requestedScope = requestedScope || WebInspector.LogContentView.Scopes.All;
+
+ this.hideSplitConsole();
+
+ this.consoleContentView.scopeBar.item(requestedScope).selected = true;
+
+ this.showRepresentedObject(this._consoleRepresentedObject);
+
+ console.assert(this.isShowingConsoleTab());
+};
+
+WebInspector.isShowingConsoleTab = function()
+{
+ return this.tabBrowser.selectedTabContentView instanceof WebInspector.ConsoleTabContentView;
+};
+
+WebInspector.showElementsTab = function()
+{
+ var tabContentView = this.tabBrowser.bestTabContentViewForClass(WebInspector.ElementsTabContentView);
+ if (!tabContentView)
+ tabContentView = new WebInspector.ElementsTabContentView;
+ this.tabBrowser.showTabForContentView(tabContentView);
+};
+
+WebInspector.showDebuggerTab = function(breakpointToSelect)
+{
+ var tabContentView = this.tabBrowser.bestTabContentViewForClass(WebInspector.DebuggerTabContentView);
+ if (!tabContentView)
+ tabContentView = new WebInspector.DebuggerTabContentView;
+
+ if (breakpointToSelect instanceof WebInspector.Breakpoint)
+ tabContentView.revealAndSelectBreakpoint(breakpointToSelect);
+
+ this.tabBrowser.showTabForContentView(tabContentView);
+};
+
+WebInspector.isShowingDebuggerTab = function()
+{
+ return this.tabBrowser.selectedTabContentView instanceof WebInspector.DebuggerTabContentView;
+};
+
+WebInspector.showResourcesTab = function()
+{
+ var tabContentView = this.tabBrowser.bestTabContentViewForClass(WebInspector.ResourcesTabContentView);
+ if (!tabContentView)
+ tabContentView = new WebInspector.ResourcesTabContentView;
+ this.tabBrowser.showTabForContentView(tabContentView);
+};
+
+WebInspector.showStorageTab = function()
+{
+ var tabContentView = this.tabBrowser.bestTabContentViewForClass(WebInspector.StorageTabContentView);
+ if (!tabContentView)
+ tabContentView = new WebInspector.StorageTabContentView;
+ this.tabBrowser.showTabForContentView(tabContentView);
+};
+
+WebInspector.showNetworkTab = function()
+{
+ var tabContentView = this.tabBrowser.bestTabContentViewForClass(WebInspector.NetworkTabContentView);
+ if (!tabContentView)
+ tabContentView = new WebInspector.NetworkTabContentView;
+ this.tabBrowser.showTabForContentView(tabContentView);
+};
+
+WebInspector.showTimelineTab = function()
+{
+ var tabContentView = this.tabBrowser.bestTabContentViewForClass(WebInspector.TimelineTabContentView);
+ if (!tabContentView)
+ tabContentView = new WebInspector.TimelineTabContentView;
+ this.tabBrowser.showTabForContentView(tabContentView);
+};
+
+WebInspector.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;
+};
+
+WebInspector.UIString = function(string, vararg)
+{
+ if (WebInspector.dontLocalizeUserInterface)
+ return string;
+
+ if (window.localizedStrings && string in window.localizedStrings)
+ return window.localizedStrings[string];
+
+ if (!this._missingLocalizedStrings)
+ this._missingLocalizedStrings = {};
+
+ if (!(string in this._missingLocalizedStrings)) {
+ console.error("Localized string \"" + string + "\" was not found.");
+ this._missingLocalizedStrings[string] = true;
+ }
+
+ return "LOCALIZED STRING NOT FOUND";
+};
+
+WebInspector.indentString = function()
+{
+ if (WebInspector.settings.indentWithTabs.value)
+ return "\t";
+ return " ".repeat(WebInspector.settings.indentUnit.value);
+};
+
+WebInspector.restoreFocusFromElement = function(element)
+{
+ if (element && element.isSelfOrAncestor(this.currentFocusElement))
+ this.previousFocusElement.focus();
+};
+
+WebInspector.toggleNavigationSidebar = function(event)
+{
+ if (!this.navigationSidebar.collapsed || !this.navigationSidebar.sidebarPanels.length) {
+ this.navigationSidebar.collapsed = true;
+ return;
+ }
+
+ if (!this.navigationSidebar.selectedSidebarPanel)
+ this.navigationSidebar.selectedSidebarPanel = this.navigationSidebar.sidebarPanels[0];
+ this.navigationSidebar.collapsed = false;
+};
+
+WebInspector.toggleDetailsSidebar = function(event)
+{
+ if (!this.detailsSidebar.collapsed || !this.detailsSidebar.sidebarPanels.length) {
+ this.detailsSidebar.collapsed = true;
+ return;
+ }
+
+ if (!this.detailsSidebar.selectedSidebarPanel)
+ this.detailsSidebar.selectedSidebarPanel = this.detailsSidebar.sidebarPanels[0];
+ this.detailsSidebar.collapsed = false;
+};
+
+WebInspector.tabContentViewClassForRepresentedObject = function(representedObject)
+{
+ if (representedObject instanceof WebInspector.DOMTree)
+ return WebInspector.ElementsTabContentView;
+
+ if (representedObject instanceof WebInspector.TimelineRecording)
+ return WebInspector.TimelineTabContentView;
+
+ // We only support one console tab right now. So this isn't an instanceof check.
+ if (representedObject === this._consoleRepresentedObject)
+ return WebInspector.ConsoleTabContentView;
+
+ if (WebInspector.debuggerManager.paused) {
+ if (representedObject instanceof WebInspector.Script)
+ return WebInspector.DebuggerTabContentView;
+
+ if (representedObject instanceof WebInspector.Resource && (representedObject.type === WebInspector.Resource.Type.Document || representedObject.type === WebInspector.Resource.Type.Script))
+ return WebInspector.DebuggerTabContentView;
+ }
+
+ if (representedObject instanceof WebInspector.Frame || representedObject instanceof WebInspector.Resource || representedObject instanceof WebInspector.Script)
+ return WebInspector.ResourcesTabContentView;
+
+ // FIXME: Move Content Flows to the Elements tab?
+ if (representedObject instanceof WebInspector.ContentFlow)
+ return WebInspector.ResourcesTabContentView;
+
+ // FIXME: Move these to a Storage tab.
+ if (representedObject instanceof WebInspector.DOMStorageObject || representedObject instanceof WebInspector.CookieStorageObject ||
+ representedObject instanceof WebInspector.DatabaseTableObject || representedObject instanceof WebInspector.DatabaseObject ||
+ representedObject instanceof WebInspector.ApplicationCacheFrame || representedObject instanceof WebInspector.IndexedDatabaseObjectStore ||
+ representedObject instanceof WebInspector.IndexedDatabaseObjectStoreIndex)
+ return WebInspector.ResourcesTabContentView;
+
+ if (representedObject instanceof WebInspector.Collection)
+ return WebInspector.CollectionContentView;
+
+ return null;
+};
+
+WebInspector.tabContentViewForRepresentedObject = function(representedObject, options = {})
+{
+ let tabContentView = this.tabBrowser.bestTabContentViewForRepresentedObject(representedObject, options);
+ if (tabContentView)
+ return tabContentView;
+
+ var tabContentViewClass = this.tabContentViewClassForRepresentedObject(representedObject);
+ if (!tabContentViewClass) {
+ console.error("Unknown representedObject, couldn't create TabContentView.", representedObject);
+ return null;
+ }
+
+ tabContentView = new tabContentViewClass;
+
+ this.tabBrowser.addTabForContentView(tabContentView);
+
+ return tabContentView;
+};
+
+WebInspector.showRepresentedObject = function(representedObject, cookie, options = {})
+{
+ let tabContentView = this.tabContentViewForRepresentedObject(representedObject, options);
+ console.assert(tabContentView);
+ if (!tabContentView)
+ return;
+
+ this.tabBrowser.showTabForContentView(tabContentView);
+ tabContentView.showRepresentedObject(representedObject, cookie);
+};
+
+WebInspector.showMainFrameDOMTree = function(nodeToSelect, options = {})
+{
+ console.assert(WebInspector.frameResourceManager.mainFrame);
+ if (!WebInspector.frameResourceManager.mainFrame)
+ return;
+ this.showRepresentedObject(WebInspector.frameResourceManager.mainFrame.domTree, {nodeToSelect}, options);
+};
+
+WebInspector.showSourceCodeForFrame = function(frameIdentifier, options = {})
+{
+ var frame = WebInspector.frameResourceManager.frameForIdentifier(frameIdentifier);
+ if (!frame) {
+ this._frameIdentifierToShowSourceCodeWhenAvailable = frameIdentifier;
+ return;
+ }
+
+ this._frameIdentifierToShowSourceCodeWhenAvailable = undefined;
+
+ this.showRepresentedObject(frame, null, options);
+};
+
+WebInspector.showSourceCode = function(sourceCode, options = {})
+{
+ const positionToReveal = options.positionToReveal;
+
+ console.assert(!positionToReveal || positionToReveal instanceof WebInspector.SourceCodePosition, positionToReveal);
+ var representedObject = sourceCode;
+
+ if (representedObject instanceof WebInspector.Script) {
+ // A script represented by a resource should always show the resource.
+ representedObject = representedObject.resource || representedObject;
+ }
+
+ var cookie = positionToReveal ? {lineNumber: positionToReveal.lineNumber, columnNumber: positionToReveal.columnNumber} : {};
+ this.showRepresentedObject(representedObject, cookie, options);
+};
+
+WebInspector.showSourceCodeLocation = function(sourceCodeLocation, options = {})
+{
+ this.showSourceCode(sourceCodeLocation.displaySourceCode, Object.shallowMerge(options, {
+ positionToReveal: sourceCodeLocation.displayPosition()
+ }));
+};
+
+WebInspector.showOriginalUnformattedSourceCodeLocation = function(sourceCodeLocation, options = {})
+{
+ this.showSourceCode(sourceCodeLocation.sourceCode, Object.shallowMerge(options, {
+ positionToReveal: sourceCodeLocation.position(),
+ forceUnformatted: true
+ }));
+};
+
+WebInspector.showOriginalOrFormattedSourceCodeLocation = function(sourceCodeLocation, options = {})
+{
+ this.showSourceCode(sourceCodeLocation.sourceCode, Object.shallowMerge(options, {
+ positionToReveal: sourceCodeLocation.formattedPosition()
+ }));
+};
+
+WebInspector.showOriginalOrFormattedSourceCodeTextRange = function(sourceCodeTextRange, options = {})
+{
+ var textRangeToSelect = sourceCodeTextRange.formattedTextRange;
+ this.showSourceCode(sourceCodeTextRange.sourceCode, Object.shallowMerge(options, {
+ positionToReveal: textRangeToSelect.startPosition(),
+ textRangeToSelect
+ }));
+};
+
+WebInspector.showResourceRequest = function(resource, options = {})
+{
+ this.showRepresentedObject(resource, {[WebInspector.ResourceClusterContentView.ContentViewIdentifierCookieKey]: WebInspector.ResourceClusterContentView.RequestIdentifier}, options);
+};
+
+WebInspector.debuggerToggleBreakpoints = function(event)
+{
+ WebInspector.debuggerManager.breakpointsEnabled = !WebInspector.debuggerManager.breakpointsEnabled;
+};
+
+WebInspector.debuggerPauseResumeToggle = function(event)
+{
+ if (WebInspector.debuggerManager.paused)
+ WebInspector.debuggerManager.resume();
+ else
+ WebInspector.debuggerManager.pause();
+};
+
+WebInspector.debuggerStepOver = function(event)
+{
+ WebInspector.debuggerManager.stepOver();
+};
+
+WebInspector.debuggerStepInto = function(event)
+{
+ WebInspector.debuggerManager.stepInto();
+};
+
+WebInspector.debuggerStepOut = function(event)
+{
+ WebInspector.debuggerManager.stepOut();
+};
+
+WebInspector._searchTextDidChange = function(event)
+{
+ var tabContentView = this.tabBrowser.bestTabContentViewForClass(WebInspector.SearchTabContentView);
+ if (!tabContentView)
+ tabContentView = new WebInspector.SearchTabContentView;
+
+ var searchQuery = this._searchToolbarItem.text;
+ this._searchToolbarItem.text = "";
+
+ this.tabBrowser.showTabForContentView(tabContentView);
+
+ tabContentView.performSearch(searchQuery);
+};
+
+WebInspector._focusSearchField = function(event)
+{
+ if (this.tabBrowser.selectedTabContentView instanceof WebInspector.SearchTabContentView) {
+ this.tabBrowser.selectedTabContentView.focusSearchField();
+ return;
+ }
+
+ this._searchToolbarItem.focus();
+};
+
+WebInspector._focusChanged = function(event)
+{
+ // Make a caret selection inside the focused element if there isn't a range selection and there isn't already
+ // a caret selection inside. This is needed (at least) to remove caret from console when focus is moved.
+ // The selection change should not apply to text fields and text areas either.
+
+ if (WebInspector.isEventTargetAnEditableField(event)) {
+ // Still update the currentFocusElement if inside of a CodeMirror editor.
+ var codeMirrorEditorElement = event.target.enclosingNodeOrSelfWithClass("CodeMirror");
+ if (codeMirrorEditorElement && codeMirrorEditorElement !== this.currentFocusElement) {
+ this.previousFocusElement = this.currentFocusElement;
+ this.currentFocusElement = codeMirrorEditorElement;
+ }
+ return;
+ }
+
+ var selection = window.getSelection();
+ if (!selection.isCollapsed)
+ return;
+
+ var element = event.target;
+
+ if (element !== this.currentFocusElement) {
+ this.previousFocusElement = this.currentFocusElement;
+ this.currentFocusElement = element;
+ }
+
+ if (element.isInsertionCaretInside())
+ return;
+
+ var selectionRange = element.ownerDocument.createRange();
+ selectionRange.setStart(element, 0);
+ selectionRange.setEnd(element, 0);
+
+ selection.removeAllRanges();
+ selection.addRange(selectionRange);
+};
+
+WebInspector._mouseWasClicked = function(event)
+{
+ this.handlePossibleLinkClick(event);
+};
+
+WebInspector._dragOver = function(event)
+{
+ // Do nothing if another event listener handled the event already.
+ if (event.defaultPrevented)
+ return;
+
+ // Allow dropping into editable areas.
+ if (WebInspector.isEventTargetAnEditableField(event))
+ return;
+
+ // Prevent the drop from being accepted.
+ event.dataTransfer.dropEffect = "none";
+ event.preventDefault();
+};
+
+WebInspector._captureDidStart = function(event)
+{
+ this._dashboardContainer.showDashboardViewForRepresentedObject(this.dashboardManager.dashboards.replay);
+};
+
+WebInspector._debuggerDidPause = function(event)
+{
+ // FIXME: <webkit.org/b/###> Web Inspector: Preference for Auto Showing Scope Chain sidebar on pause
+ this.showDebuggerTab();
+
+ this._dashboardContainer.showDashboardViewForRepresentedObject(this.dashboardManager.dashboards.debugger);
+
+ InspectorFrontendHost.bringToFront();
+};
+
+WebInspector._debuggerDidResume = function(event)
+{
+ this._dashboardContainer.closeDashboardViewForRepresentedObject(this.dashboardManager.dashboards.debugger);
+};
+
+WebInspector._frameWasAdded = function(event)
+{
+ if (!this._frameIdentifierToShowSourceCodeWhenAvailable)
+ return;
+
+ var frame = event.data.frame;
+ if (frame.id !== this._frameIdentifierToShowSourceCodeWhenAvailable)
+ return;
+
+ function delayedWork()
+ {
+ this.showSourceCodeForFrame(frame.id);
+ }
+
+ // Delay showing the frame since FrameWasAdded is called before MainFrameChanged.
+ // Calling showSourceCodeForFrame before MainFrameChanged will show the frame then close it.
+ setTimeout(delayedWork.bind(this));
+};
+
+WebInspector._mainFrameDidChange = function(event)
+{
+ this._updateDownloadToolbarButton();
+
+ this.updateWindowTitle();
+};
+
+WebInspector._mainResourceDidChange = function(event)
+{
+ if (!event.target.isMainFrame())
+ return;
+
+ this._inProvisionalLoad = false;
+
+ // Run cookie restoration after we are sure all of the Tabs and NavigationSidebarPanels
+ // have updated with respect to the main resource change.
+ setTimeout(this._restoreCookieForOpenTabs.bind(this, WebInspector.StateRestorationType.Navigation));
+
+ this._updateDownloadToolbarButton();
+
+ this.updateWindowTitle();
+};
+
+WebInspector._provisionalLoadStarted = function(event)
+{
+ if (!event.target.isMainFrame())
+ return;
+
+ this._saveCookieForOpenTabs();
+
+ this._inProvisionalLoad = true;
+};
+
+WebInspector._restoreCookieForOpenTabs = function(restorationType)
+{
+ for (var tabBarItem of this.tabBar.tabBarItems) {
+ var tabContentView = tabBarItem.representedObject;
+ if (!(tabContentView instanceof WebInspector.TabContentView))
+ continue;
+ tabContentView.restoreStateFromCookie(restorationType);
+ }
+};
+
+WebInspector._saveCookieForOpenTabs = function()
+{
+ for (var tabBarItem of this.tabBar.tabBarItems) {
+ var tabContentView = tabBarItem.representedObject;
+ if (!(tabContentView instanceof WebInspector.TabContentView))
+ continue;
+ tabContentView.saveStateToCookie();
+ }
+};
+
+WebInspector._windowFocused = function(event)
+{
+ if (event.target.document.nodeType !== Node.DOCUMENT_NODE)
+ return;
+
+ // FIXME: We should use the :window-inactive pseudo class once https://webkit.org/b/38927 is fixed.
+ document.body.classList.remove(this.docked ? "window-docked-inactive" : "window-inactive");
+};
+
+WebInspector._windowBlurred = function(event)
+{
+ if (event.target.document.nodeType !== Node.DOCUMENT_NODE)
+ return;
+
+ // FIXME: We should use the :window-inactive pseudo class once https://webkit.org/b/38927 is fixed.
+ document.body.classList.add(this.docked ? "window-docked-inactive" : "window-inactive");
+};
+
+WebInspector._windowResized = function(event)
+{
+ this.toolbar.updateLayout(WebInspector.View.LayoutReason.Resize);
+ this.tabBar.updateLayout(WebInspector.View.LayoutReason.Resize);
+ this._tabBrowserSizeDidChange();
+};
+
+WebInspector._updateModifierKeys = function(event)
+{
+ var didChange = this.modifierKeys.altKey !== event.altKey || this.modifierKeys.metaKey !== event.metaKey || this.modifierKeys.shiftKey !== event.shiftKey;
+
+ this.modifierKeys = {altKey: event.altKey, metaKey: event.metaKey, shiftKey: event.shiftKey};
+
+ if (didChange)
+ this.notifications.dispatchEventToListeners(WebInspector.Notification.GlobalModifierKeysDidChange, event);
+};
+
+WebInspector._windowKeyDown = function(event)
+{
+ this._updateModifierKeys(event);
+};
+
+WebInspector._windowKeyUp = function(event)
+{
+ this._updateModifierKeys(event);
+};
+
+WebInspector._mouseDown = function(event)
+{
+ if (this.toolbar.element.isSelfOrAncestor(event.target))
+ this._toolbarMouseDown(event);
+};
+
+WebInspector._mouseMoved = function(event)
+{
+ this._updateModifierKeys(event);
+ this.mouseCoords = {
+ x: event.pageX,
+ y: event.pageY
+ };
+};
+
+WebInspector._pageHidden = function(event)
+{
+ this._saveCookieForOpenTabs();
+};
+
+WebInspector._contextMenuRequested = function(event)
+{
+ let proposedContextMenu;
+
+ // This is setting is only defined in engineering builds.
+ if (WebInspector.isDebugUIEnabled()) {
+ proposedContextMenu = WebInspector.ContextMenu.createFromEvent(event);
+ proposedContextMenu.appendSeparator();
+ proposedContextMenu.appendItem(WebInspector.unlocalizedString("Reload Web Inspector"), () => {
+ window.location.reload();
+ });
+
+ let protocolSubMenu = proposedContextMenu.appendSubMenuItem(WebInspector.unlocalizedString("Protocol Debugging"), null, false);
+ let isCapturingTraffic = InspectorBackend.activeTracer instanceof WebInspector.CapturingProtocolTracer;
+
+ protocolSubMenu.appendCheckboxItem(WebInspector.unlocalizedString("Capture Trace"), () => {
+ if (isCapturingTraffic)
+ InspectorBackend.activeTracer = null;
+ else
+ InspectorBackend.activeTracer = new WebInspector.CapturingProtocolTracer;
+ }, isCapturingTraffic);
+
+ protocolSubMenu.appendSeparator();
+
+ protocolSubMenu.appendItem(WebInspector.unlocalizedString("Export Trace\u2026"), () => {
+ const forceSaveAs = true;
+ WebInspector.saveDataToFile(InspectorBackend.activeTracer.trace.saveData, forceSaveAs);
+ }, !isCapturingTraffic);
+ } else {
+ const onlyExisting = true;
+ proposedContextMenu = WebInspector.ContextMenu.createFromEvent(event, onlyExisting);
+ }
+
+ if (proposedContextMenu)
+ proposedContextMenu.show();
+};
+
+WebInspector.isDebugUIEnabled = function()
+{
+ return WebInspector.showDebugUISetting && WebInspector.showDebugUISetting.value;
+};
+
+WebInspector._undock = function(event)
+{
+ InspectorFrontendHost.requestSetDockSide(WebInspector.DockConfiguration.Undocked);
+};
+
+WebInspector._dockBottom = function(event)
+{
+ InspectorFrontendHost.requestSetDockSide(WebInspector.DockConfiguration.Bottom);
+};
+
+WebInspector._dockRight = function(event)
+{
+ InspectorFrontendHost.requestSetDockSide(WebInspector.DockConfiguration.Right);
+};
+
+WebInspector._dockLeft = function(event)
+{
+ InspectorFrontendHost.requestSetDockSide(WebInspector.DockConfiguration.Left);
+};
+
+WebInspector._togglePreviousDockConfiguration = function(event)
+{
+ InspectorFrontendHost.requestSetDockSide(this._previousDockConfiguration);
+};
+
+WebInspector._updateDockNavigationItems = function()
+{
+ if (this._dockingAvailable || this.docked) {
+ this._closeToolbarButton.hidden = !this.docked;
+ this._undockToolbarButton.hidden = this._dockConfiguration === WebInspector.DockConfiguration.Undocked;
+ this._dockBottomToolbarButton.hidden = this._dockConfiguration === WebInspector.DockConfiguration.Bottom;
+ this._dockToSideToolbarButton.hidden = this._dockConfiguration === WebInspector.DockConfiguration.Right || this._dockConfiguration === WebInspector.DockConfiguration.Left;
+ } else {
+ this._closeToolbarButton.hidden = true;
+ this._undockToolbarButton.hidden = true;
+ this._dockBottomToolbarButton.hidden = true;
+ this._dockToSideToolbarButton.hidden = true;
+ }
+};
+
+WebInspector._tabBrowserSizeDidChange = function()
+{
+ this.tabBrowser.updateLayout(WebInspector.View.LayoutReason.Resize);
+ this.splitContentBrowser.updateLayout(WebInspector.View.LayoutReason.Resize);
+ this.quickConsole.updateLayout(WebInspector.View.LayoutReason.Resize);
+};
+
+WebInspector._quickConsoleDidResize = function(event)
+{
+ this.tabBrowser.updateLayout(WebInspector.View.LayoutReason.Resize);
+};
+
+WebInspector._sidebarWidthDidChange = function(event)
+{
+ this._tabBrowserSizeDidChange();
+};
+
+WebInspector._setupViewHierarchy = function()
+{
+ let rootView = WebInspector.View.rootView();
+ rootView.addSubview(this.toolbar);
+ rootView.addSubview(this.tabBar);
+ rootView.addSubview(this.navigationSidebar);
+ rootView.addSubview(this.tabBrowser);
+ rootView.addSubview(this.splitContentBrowser);
+ rootView.addSubview(this.quickConsole);
+ rootView.addSubview(this.detailsSidebar);
+};
+
+WebInspector._tabBrowserSelectedTabContentViewDidChange = function(event)
+{
+ if (this.tabBar.selectedTabBarItem && this.tabBar.selectedTabBarItem.representedObject.constructor.shouldSaveTab())
+ this._selectedTabIndexSetting.value = this.tabBar.tabBarItems.indexOf(this.tabBar.selectedTabBarItem);
+
+ if (!this.doesCurrentTabSupportSplitContentBrowser())
+ this.hideSplitConsole();
+
+ if (!this.isShowingSplitConsole())
+ this.quickConsole.consoleLogVisibilityChanged(this.isShowingConsoleTab());
+};
+
+WebInspector._initializeWebSocketIfNeeded = function()
+{
+ if (!InspectorFrontendHost.initializeWebSocket)
+ return;
+
+ var queryParams = parseLocationQueryParameters();
+
+ if ("ws" in queryParams)
+ var url = "ws://" + queryParams.ws;
+ else if ("page" in queryParams) {
+ var page = queryParams.page;
+ var host = "host" in queryParams ? queryParams.host : window.location.host;
+ var url = "ws://" + host + "/devtools/page/" + page;
+ }
+
+ if (!url)
+ return;
+
+ InspectorFrontendHost.initializeWebSocket(url);
+};
+
+WebInspector._updateSplitConsoleHeight = function(height)
+{
+ const minimumHeight = 64;
+ const maximumHeight = window.innerHeight * 0.55;
+
+ height = Math.max(minimumHeight, Math.min(height, maximumHeight));
+
+ this.splitContentBrowser.element.style.height = height + "px";
+};
+
+WebInspector._consoleResizerMouseDown = function(event)
+{
+ if (event.button !== 0 || event.ctrlKey)
+ return;
+
+ // Only start dragging if the target is one of the elements that we expect.
+ if (!event.target.classList.contains("navigation-bar") && !event.target.classList.contains("flexible-space"))
+ return;
+
+ var resizerElement = event.target;
+ var mouseOffset = resizerElement.offsetHeight - (event.pageY - resizerElement.totalOffsetTop);
+
+ function dockedResizerDrag(event)
+ {
+ if (event.button !== 0)
+ return;
+
+ var height = window.innerHeight - event.pageY - mouseOffset;
+
+ this._splitConsoleHeightSetting.value = height;
+
+ this._updateSplitConsoleHeight(height);
+
+ this.quickConsole.dispatchEventToListeners(WebInspector.QuickConsole.Event.DidResize);
+ }
+
+ function dockedResizerDragEnd(event)
+ {
+ if (event.button !== 0)
+ return;
+
+ this.elementDragEnd(event);
+ }
+
+ this.elementDragStart(resizerElement, dockedResizerDrag.bind(this), dockedResizerDragEnd.bind(this), event, "row-resize");
+};
+
+WebInspector._toolbarMouseDown = function(event)
+{
+ if (event.ctrlKey)
+ return;
+
+ if (this._dockConfiguration === WebInspector.DockConfiguration.Right || this._dockConfiguration === WebInspector.DockConfiguration.Left)
+ return;
+
+ if (this.docked)
+ this._dockedResizerMouseDown(event);
+ else
+ this._moveWindowMouseDown(event);
+};
+
+WebInspector._dockedResizerMouseDown = function(event)
+{
+ if (event.button !== 0 || event.ctrlKey)
+ return;
+
+ if (!this.docked)
+ return;
+
+ // Only start dragging if the target is one of the elements that we expect.
+ if (event.target.id !== "docked-resizer" && !event.target.classList.contains("toolbar") &&
+ !event.target.classList.contains("flexible-space") && !event.target.classList.contains("item-section"))
+ return;
+
+ event[WebInspector.Popover.EventPreventDismissSymbol] = true;
+
+ let windowProperty = this._dockConfiguration === WebInspector.DockConfiguration.Bottom ? "innerHeight" : "innerWidth";
+ let eventScreenProperty = this._dockConfiguration === WebInspector.DockConfiguration.Bottom ? "screenY" : "screenX";
+ let eventClientProperty = this._dockConfiguration === WebInspector.DockConfiguration.Bottom ? "clientY" : "clientX";
+
+ var resizerElement = event.target;
+ var firstClientPosition = event[eventClientProperty];
+ var lastScreenPosition = event[eventScreenProperty];
+
+ function dockedResizerDrag(event)
+ {
+ if (event.button !== 0)
+ return;
+
+ var position = event[eventScreenProperty];
+ var delta = position - lastScreenPosition;
+ var clientPosition = event[eventClientProperty];
+
+ lastScreenPosition = position;
+
+ if (this._dockConfiguration === WebInspector.DockConfiguration.Left) {
+ // If the mouse is travelling rightward but is positioned left of the resizer, ignore the event.
+ if (delta > 0 && clientPosition < firstClientPosition)
+ return;
+
+ // If the mouse is travelling leftward but is positioned to the right of the resizer, ignore the event.
+ if (delta < 0 && clientPosition > window[windowProperty])
+ return;
+
+ // We later subtract the delta from the current position, but since the inspected view and inspector view
+ // are flipped when docked to left, we want dragging to have the opposite effect from docked to right.
+ delta *= -1;
+ } else {
+ // If the mouse is travelling downward/rightward but is positioned above/left of the resizer, ignore the event.
+ if (delta > 0 && clientPosition < firstClientPosition)
+ return;
+
+ // If the mouse is travelling upward/leftward but is positioned below/right of the resizer, ignore the event.
+ if (delta < 0 && clientPosition > firstClientPosition)
+ return;
+ }
+
+ let dimension = Math.max(0, window[windowProperty] - delta);
+ // If zoomed in/out, there be greater/fewer document pixels shown, but the inspector's
+ // width or height should be the same in device pixels regardless of the document zoom.
+ dimension *= this.getZoomFactor();
+
+ if (this._dockConfiguration === WebInspector.DockConfiguration.Bottom)
+ InspectorFrontendHost.setAttachedWindowHeight(dimension);
+ else
+ InspectorFrontendHost.setAttachedWindowWidth(dimension);
+ }
+
+ function dockedResizerDragEnd(event)
+ {
+ if (event.button !== 0)
+ return;
+
+ WebInspector.elementDragEnd(event);
+ }
+
+ WebInspector.elementDragStart(resizerElement, dockedResizerDrag.bind(this), dockedResizerDragEnd.bind(this), event, this._dockConfiguration === WebInspector.DockConfiguration.Bottom ? "row-resize" : "col-resize");
+};
+
+WebInspector._moveWindowMouseDown = function(event)
+{
+ console.assert(!this.docked);
+
+ if (event.button !== 0 || event.ctrlKey)
+ return;
+
+ // Only start dragging if the target is one of the elements that we expect.
+ if (!event.target.classList.contains("toolbar") && !event.target.classList.contains("flexible-space") &&
+ !event.target.classList.contains("item-section"))
+ return;
+
+ event[WebInspector.Popover.EventPreventDismissSymbol] = true;
+
+ if (WebInspector.Platform.name === "mac") {
+ // New Mac releases can start a window drag.
+ if (WebInspector.Platform.version.release >= 11) {
+ InspectorFrontendHost.startWindowDrag();
+ event.preventDefault();
+ return;
+ }
+
+ // Ignore dragging on the top of the toolbar on Mac if the system handles it.
+ if (WebInspector.Platform.version.release === 10) {
+ const windowDragHandledTitleBarHeight = 22;
+ if (event.pageY < windowDragHandledTitleBarHeight) {
+ event.preventDefault();
+ return;
+ }
+ }
+ }
+
+ var lastScreenX = event.screenX;
+ var lastScreenY = event.screenY;
+
+ function toolbarDrag(event)
+ {
+ if (event.button !== 0)
+ return;
+
+ var x = event.screenX - lastScreenX;
+ var y = event.screenY - lastScreenY;
+
+ InspectorFrontendHost.moveWindowBy(x, y);
+
+ lastScreenX = event.screenX;
+ lastScreenY = event.screenY;
+ }
+
+ function toolbarDragEnd(event)
+ {
+ if (event.button !== 0)
+ return;
+
+ WebInspector.elementDragEnd(event);
+ }
+
+ WebInspector.elementDragStart(event.target, toolbarDrag, toolbarDragEnd, event, "default");
+};
+
+WebInspector._storageWasInspected = function(event)
+{
+ this.showStorageTab();
+};
+
+WebInspector._domNodeWasInspected = function(event)
+{
+ this.domTreeManager.highlightDOMNodeForTwoSeconds(event.data.node.id);
+
+ InspectorFrontendHost.bringToFront();
+
+ this.showElementsTab();
+ this.showMainFrameDOMTree(event.data.node);
+};
+
+WebInspector._inspectModeStateChanged = function(event)
+{
+ this._inspectModeToolbarButton.activated = this.domTreeManager.inspectModeEnabled;
+};
+
+WebInspector._toggleInspectMode = function(event)
+{
+ this.domTreeManager.inspectModeEnabled = !this.domTreeManager.inspectModeEnabled;
+};
+
+WebInspector._downloadWebArchive = function(event)
+{
+ this.archiveMainFrame();
+};
+
+WebInspector._reloadPage = function(event)
+{
+ if (!window.PageAgent)
+ return;
+
+ PageAgent.reload();
+ event.preventDefault();
+};
+
+WebInspector._reloadPageClicked = function(event)
+{
+ // Ignore cache when the shift key is pressed.
+ PageAgent.reload(window.event ? window.event.shiftKey : false);
+};
+
+WebInspector._reloadPageIgnoringCache = function(event)
+{
+ if (!window.PageAgent)
+ return;
+
+ PageAgent.reload(true);
+ event.preventDefault();
+};
+
+WebInspector._updateReloadToolbarButton = function()
+{
+ if (!window.PageAgent) {
+ this._reloadToolbarButton.hidden = true;
+ return;
+ }
+
+ this._reloadToolbarButton.hidden = false;
+};
+
+WebInspector._updateDownloadToolbarButton = function()
+{
+ // COMPATIBILITY (iOS 7): Page.archive did not exist yet.
+ if (!window.PageAgent || !PageAgent.archive || this.debuggableType !== WebInspector.DebuggableType.Web) {
+ this._downloadToolbarButton.hidden = true;
+ return;
+ }
+
+ if (this._downloadingPage) {
+ this._downloadToolbarButton.enabled = false;
+ return;
+ }
+
+ this._downloadToolbarButton.enabled = this.canArchiveMainFrame();
+};
+
+WebInspector._toggleInspectMode = function(event)
+{
+ this.domTreeManager.inspectModeEnabled = !this.domTreeManager.inspectModeEnabled;
+};
+
+WebInspector._showConsoleTab = function(event)
+{
+ this.showConsoleTab();
+};
+
+WebInspector._focusConsolePrompt = function(event)
+{
+ this.quickConsole.prompt.focus();
+};
+
+WebInspector._focusedContentBrowser = function()
+{
+ if (this.tabBrowser.element.isSelfOrAncestor(this.currentFocusElement) || document.activeElement === document.body) {
+ var tabContentView = this.tabBrowser.selectedTabContentView;
+ if (tabContentView instanceof WebInspector.ContentBrowserTabContentView)
+ return tabContentView.contentBrowser;
+ return null;
+ }
+
+ if (this.splitContentBrowser.element.isSelfOrAncestor(this.currentFocusElement)
+ || (WebInspector.isShowingSplitConsole() && this.quickConsole.element.isSelfOrAncestor(this.currentFocusElement)))
+ return this.splitContentBrowser;
+
+ return null;
+};
+
+WebInspector._focusedContentView = function()
+{
+ if (this.tabBrowser.element.isSelfOrAncestor(this.currentFocusElement) || document.activeElement === document.body) {
+ var tabContentView = this.tabBrowser.selectedTabContentView;
+ if (tabContentView instanceof WebInspector.ContentBrowserTabContentView)
+ return tabContentView.contentBrowser.currentContentView;
+ return tabContentView;
+ }
+
+ if (this.splitContentBrowser.element.isSelfOrAncestor(this.currentFocusElement)
+ || (WebInspector.isShowingSplitConsole() && this.quickConsole.element.isSelfOrAncestor(this.currentFocusElement)))
+ return this.splitContentBrowser.currentContentView;
+
+ return null;
+};
+
+WebInspector._focusedOrVisibleContentBrowser = function()
+{
+ let focusedContentBrowser = this._focusedContentBrowser();
+ if (focusedContentBrowser)
+ return focusedContentBrowser;
+
+ var tabContentView = this.tabBrowser.selectedTabContentView;
+ if (tabContentView instanceof WebInspector.ContentBrowserTabContentView)
+ return tabContentView.contentBrowser;
+
+ return null;
+};
+
+WebInspector.focusedOrVisibleContentView = function()
+{
+ let focusedContentView = this._focusedContentView();
+ if (focusedContentView)
+ return focusedContentView;
+
+ var tabContentView = this.tabBrowser.selectedTabContentView;
+ if (tabContentView instanceof WebInspector.ContentBrowserTabContentView)
+ return tabContentView.contentBrowser.currentContentView;
+ return tabContentView;
+};
+
+WebInspector._beforecopy = function(event)
+{
+ var selection = window.getSelection();
+
+ // If there is no selection, see if the focused element or focused ContentView can handle the copy event.
+ if (selection.isCollapsed && !WebInspector.isEventTargetAnEditableField(event)) {
+ var focusedCopyHandler = this.currentFocusElement && this.currentFocusElement.copyHandler;
+ if (focusedCopyHandler && typeof focusedCopyHandler.handleBeforeCopyEvent === "function") {
+ focusedCopyHandler.handleBeforeCopyEvent(event);
+ if (event.defaultPrevented)
+ return;
+ }
+
+ var focusedContentView = this._focusedContentView();
+ if (focusedContentView && typeof focusedContentView.handleCopyEvent === "function") {
+ event.preventDefault();
+ return;
+ }
+
+ return;
+ }
+
+ if (selection.isCollapsed)
+ return;
+
+ // Say we can handle it (by preventing default) to remove word break characters.
+ event.preventDefault();
+};
+
+WebInspector._find = function(event)
+{
+ let contentBrowser = this._focusedOrVisibleContentBrowser();
+ if (!contentBrowser)
+ return;
+
+ contentBrowser.showFindBanner();
+};
+
+WebInspector._save = function(event)
+{
+ var contentView = this.focusedOrVisibleContentView();
+ if (!contentView || !contentView.supportsSave)
+ return;
+
+ WebInspector.saveDataToFile(contentView.saveData);
+};
+
+WebInspector._saveAs = function(event)
+{
+ var contentView = this.focusedOrVisibleContentView();
+ if (!contentView || !contentView.supportsSave)
+ return;
+
+ WebInspector.saveDataToFile(contentView.saveData, true);
+};
+
+WebInspector._clear = function(event)
+{
+ let contentView = this.focusedOrVisibleContentView();
+ if (!contentView || typeof contentView.handleClearShortcut !== "function") {
+ // If the current content view is unable to handle this event, clear the console to reset
+ // the dashboard counters.
+ this.logManager.requestClearMessages();
+ return;
+ }
+
+ contentView.handleClearShortcut(event);
+};
+
+WebInspector._copy = function(event)
+{
+ var selection = window.getSelection();
+
+ // If there is no selection, pass the copy event on to the focused element or focused ContentView.
+ if (selection.isCollapsed && !WebInspector.isEventTargetAnEditableField(event)) {
+ var focusedCopyHandler = this.currentFocusElement && this.currentFocusElement.copyHandler;
+ if (focusedCopyHandler && typeof focusedCopyHandler.handleCopyEvent === "function") {
+ focusedCopyHandler.handleCopyEvent(event);
+ if (event.defaultPrevented)
+ return;
+ }
+
+ var focusedContentView = this._focusedContentView();
+ if (focusedContentView && typeof focusedContentView.handleCopyEvent === "function") {
+ focusedContentView.handleCopyEvent(event);
+ return;
+ }
+
+ return;
+ }
+
+ if (selection.isCollapsed)
+ return;
+
+ // Remove word break characters from the selection before putting it on the pasteboard.
+ var selectionString = selection.toString().removeWordBreakCharacters();
+ event.clipboardData.setData("text/plain", selectionString);
+ event.preventDefault();
+};
+
+WebInspector._increaseZoom = function(event)
+{
+ const epsilon = 0.0001;
+ const maximumZoom = 2.4;
+ let currentZoom = this.getZoomFactor();
+ if (currentZoom + epsilon >= maximumZoom) {
+ InspectorFrontendHost.beep();
+ return;
+ }
+
+ this.setZoomFactor(Math.min(maximumZoom, currentZoom + 0.2));
+};
+
+WebInspector._decreaseZoom = function(event)
+{
+ const epsilon = 0.0001;
+ const minimumZoom = 0.6;
+ let currentZoom = this.getZoomFactor();
+ if (currentZoom - epsilon <= minimumZoom) {
+ InspectorFrontendHost.beep();
+ return;
+ }
+
+ this.setZoomFactor(Math.max(minimumZoom, currentZoom - 0.2));
+};
+
+WebInspector._resetZoom = function(event)
+{
+ this.setZoomFactor(1);
+};
+
+WebInspector.getZoomFactor = function()
+{
+ return WebInspector.settings.zoomFactor.value;
+};
+
+WebInspector.setZoomFactor = function(factor)
+{
+ InspectorFrontendHost.setZoomFactor(factor);
+ // Round-trip through the frontend host API in case the requested factor is not used.
+ WebInspector.settings.zoomFactor.value = InspectorFrontendHost.zoomFactor();
+};
+
+WebInspector.resolvedLayoutDirection = function()
+{
+ let layoutDirection = WebInspector.settings.layoutDirection.value;
+ if (layoutDirection === WebInspector.LayoutDirection.System)
+ layoutDirection = InspectorFrontendHost.userInterfaceLayoutDirection();
+
+ return layoutDirection;
+}
+
+WebInspector.setLayoutDirection = function(value)
+{
+ if (!Object.values(WebInspector.LayoutDirection).includes(value))
+ WebInspector.reportInternalError("Unknown layout direction requested: " + value);
+
+ if (value === WebInspector.settings.layoutDirection.value)
+ return;
+
+ WebInspector.settings.layoutDirection.value = value;
+
+ if (value === WebInspector.LayoutDirection.RTL && this._dockConfiguration === WebInspector.DockConfiguration.Right)
+ this._dockLeft();
+
+ if (value === WebInspector.LayoutDirection.LTR && this._dockConfiguration === WebInspector.DockConfiguration.Left)
+ this._dockRight();
+
+ window.location.reload();
+};
+
+WebInspector._showTabAtIndex = function(i, event)
+{
+ if (i <= WebInspector.tabBar.tabBarItems.length)
+ WebInspector.tabBar.selectedTabBarItem = i - 1;
+};
+
+WebInspector._showJavaScriptTypeInformationSettingChanged = function(event)
+{
+ if (this.showJavaScriptTypeInformationSetting.value) {
+ for (let target of WebInspector.targets)
+ target.RuntimeAgent.enableTypeProfiler();
+ } else {
+ for (let target of WebInspector.targets)
+ target.RuntimeAgent.disableTypeProfiler();
+ }
+};
+
+WebInspector._enableControlFlowProfilerSettingChanged = function(event)
+{
+ if (this.enableControlFlowProfilerSetting.value) {
+ for (let target of WebInspector.targets)
+ target.RuntimeAgent.enableControlFlowProfiler();
+ } else {
+ for (let target of WebInspector.targets)
+ target.RuntimeAgent.disableControlFlowProfiler();
+ }
+};
+
+WebInspector.elementDragStart = function(element, dividerDrag, elementDragEnd, event, cursor, eventTarget)
+{
+ if (WebInspector._elementDraggingEventListener || WebInspector._elementEndDraggingEventListener)
+ WebInspector.elementDragEnd(event);
+
+ if (element) {
+ // Install glass pane
+ if (WebInspector._elementDraggingGlassPane)
+ WebInspector._elementDraggingGlassPane.remove();
+
+ var glassPane = document.createElement("div");
+ glassPane.style.cssText = "position:absolute;top:0;bottom:0;left:0;right:0;opacity:0;z-index:1";
+ glassPane.id = "glass-pane-for-drag";
+ element.ownerDocument.body.appendChild(glassPane);
+ WebInspector._elementDraggingGlassPane = glassPane;
+ }
+
+ WebInspector._elementDraggingEventListener = dividerDrag;
+ WebInspector._elementEndDraggingEventListener = elementDragEnd;
+
+ var targetDocument = event.target.ownerDocument;
+
+ WebInspector._elementDraggingEventTarget = eventTarget || targetDocument;
+ WebInspector._elementDraggingEventTarget.addEventListener("mousemove", dividerDrag, true);
+ WebInspector._elementDraggingEventTarget.addEventListener("mouseup", elementDragEnd, true);
+
+ targetDocument.body.style.cursor = cursor;
+
+ event.preventDefault();
+};
+
+WebInspector.elementDragEnd = function(event)
+{
+ WebInspector._elementDraggingEventTarget.removeEventListener("mousemove", WebInspector._elementDraggingEventListener, true);
+ WebInspector._elementDraggingEventTarget.removeEventListener("mouseup", WebInspector._elementEndDraggingEventListener, true);
+
+ event.target.ownerDocument.body.style.removeProperty("cursor");
+
+ if (WebInspector._elementDraggingGlassPane)
+ WebInspector._elementDraggingGlassPane.remove();
+
+ delete WebInspector._elementDraggingGlassPane;
+ delete WebInspector._elementDraggingEventTarget;
+ delete WebInspector._elementDraggingEventListener;
+ delete WebInspector._elementEndDraggingEventListener;
+
+ event.preventDefault();
+};
+
+WebInspector.createMessageTextView = function(message, isError)
+{
+ var messageElement = document.createElement("div");
+ messageElement.className = "message-text-view";
+ if (isError)
+ messageElement.classList.add("error");
+
+ messageElement.textContent = message;
+
+ return messageElement;
+};
+
+WebInspector.createGoToArrowButton = function()
+{
+ var button = document.createElement("button");
+ button.addEventListener("mousedown", (event) => { event.stopPropagation(); }, true);
+ button.className = "go-to-arrow";
+ button.tabIndex = -1;
+ return button;
+};
+
+WebInspector.createSourceCodeLocationLink = function(sourceCodeLocation, dontFloat, useGoToArrowButton)
+{
+ console.assert(sourceCodeLocation);
+ if (!sourceCodeLocation)
+ return null;
+
+ var linkElement = document.createElement("a");
+ linkElement.className = "go-to-link";
+ WebInspector.linkifyElement(linkElement, sourceCodeLocation);
+ sourceCodeLocation.populateLiveDisplayLocationTooltip(linkElement);
+
+ if (useGoToArrowButton)
+ linkElement.appendChild(WebInspector.createGoToArrowButton());
+ else
+ sourceCodeLocation.populateLiveDisplayLocationString(linkElement, "textContent");
+
+ if (dontFloat)
+ linkElement.classList.add("dont-float");
+
+ return linkElement;
+};
+
+WebInspector.linkifyLocation = function(url, lineNumber, columnNumber, className)
+{
+ var sourceCode = WebInspector.sourceCodeForURL(url);
+
+ if (!sourceCode) {
+ var anchor = document.createElement("a");
+ anchor.href = url;
+ anchor.lineNumber = lineNumber;
+ if (className)
+ anchor.className = className;
+ anchor.append(WebInspector.displayNameForURL(url) + ":" + lineNumber);
+ return anchor;
+ }
+
+ var sourceCodeLocation = sourceCode.createSourceCodeLocation(lineNumber, columnNumber);
+ var linkElement = WebInspector.createSourceCodeLocationLink(sourceCodeLocation, true);
+ if (className)
+ linkElement.classList.add(className);
+ return linkElement;
+};
+
+WebInspector.linkifyElement = function(linkElement, sourceCodeLocation) {
+ console.assert(sourceCodeLocation);
+
+ function showSourceCodeLocation(event)
+ {
+ event.stopPropagation();
+ event.preventDefault();
+
+ if (event.metaKey)
+ this.showOriginalUnformattedSourceCodeLocation(sourceCodeLocation);
+ else
+ this.showSourceCodeLocation(sourceCodeLocation);
+ }
+
+ linkElement.addEventListener("click", showSourceCodeLocation.bind(this));
+};
+
+WebInspector.sourceCodeForURL = function(url)
+{
+ var sourceCode = WebInspector.frameResourceManager.resourceForURL(url);
+ if (!sourceCode) {
+ sourceCode = WebInspector.debuggerManager.scriptsForURL(url, WebInspector.assumingMainTarget())[0];
+ if (sourceCode)
+ sourceCode = sourceCode.resource || sourceCode;
+ }
+ return sourceCode || null;
+};
+
+WebInspector.linkifyURLAsNode = function(url, linkText, classes)
+{
+ if (!linkText)
+ linkText = url;
+
+ classes = (classes ? classes + " " : "");
+
+ var a = document.createElement("a");
+ a.href = url;
+ a.className = classes;
+
+ a.textContent = linkText;
+ a.style.maxWidth = "100%";
+
+ return a;
+};
+
+WebInspector.linkifyStringAsFragmentWithCustomLinkifier = function(string, linkifier)
+{
+ var container = document.createDocumentFragment();
+ var linkStringRegEx = /(?:[a-zA-Z][a-zA-Z0-9+.-]{2,}:\/\/|www\.)[\w$\-_+*'=\|\/\\(){}[\]%@&#~,:;.!?]{2,}[\w$\-_+*=\|\/\\({%@&#~]/;
+ var lineColumnRegEx = /:(\d+)(:(\d+))?$/;
+
+ while (string) {
+ var linkString = linkStringRegEx.exec(string);
+ if (!linkString)
+ break;
+
+ linkString = linkString[0];
+ var linkIndex = string.indexOf(linkString);
+ var nonLink = string.substring(0, linkIndex);
+ container.append(nonLink);
+
+ if (linkString.startsWith("data:") || linkString.startsWith("javascript:") || linkString.startsWith("mailto:")) {
+ container.append(linkString);
+ string = string.substring(linkIndex + linkString.length, string.length);
+ continue;
+ }
+
+ var title = linkString;
+ var realURL = linkString.startsWith("www.") ? "http://" + linkString : linkString;
+ var lineColumnMatch = lineColumnRegEx.exec(realURL);
+ if (lineColumnMatch)
+ realURL = realURL.substring(0, realURL.length - lineColumnMatch[0].length);
+
+ var lineNumber;
+ if (lineColumnMatch)
+ lineNumber = parseInt(lineColumnMatch[1]) - 1;
+
+ var linkNode = linkifier(title, realURL, lineNumber);
+ container.appendChild(linkNode);
+ string = string.substring(linkIndex + linkString.length, string.length);
+ }
+
+ if (string)
+ container.append(string);
+
+ return container;
+};
+
+WebInspector.linkifyStringAsFragment = function(string)
+{
+ function linkifier(title, url, lineNumber)
+ {
+ var urlNode = WebInspector.linkifyURLAsNode(url, title, undefined);
+ if (lineNumber !== undefined)
+ urlNode.lineNumber = lineNumber;
+
+ return urlNode;
+ }
+
+ return WebInspector.linkifyStringAsFragmentWithCustomLinkifier(string, linkifier);
+};
+
+WebInspector.createResourceLink = function(resource, className)
+{
+ function handleClick(event)
+ {
+ event.stopPropagation();
+ event.preventDefault();
+
+ WebInspector.showRepresentedObject(resource);
+ }
+
+ let linkNode = document.createElement("a");
+ linkNode.classList.add("resource-link", className);
+ linkNode.title = resource.url;
+ linkNode.textContent = (resource.urlComponents.lastPathComponent || resource.url).insertWordBreakCharacters();
+ linkNode.addEventListener("click", handleClick.bind(this));
+ return linkNode;
+};
+
+WebInspector._undoKeyboardShortcut = function(event)
+{
+ if (!this.isEditingAnyField() && !this.isEventTargetAnEditableField(event)) {
+ this.undo();
+ event.preventDefault();
+ }
+};
+
+WebInspector._redoKeyboardShortcut = function(event)
+{
+ if (!this.isEditingAnyField() && !this.isEventTargetAnEditableField(event)) {
+ this.redo();
+ event.preventDefault();
+ }
+};
+
+WebInspector.undo = function()
+{
+ DOMAgent.undo();
+};
+
+WebInspector.redo = function()
+{
+ DOMAgent.redo();
+};
+
+WebInspector.highlightRangesWithStyleClass = function(element, resultRanges, styleClass, changes)
+{
+ changes = changes || [];
+ var highlightNodes = [];
+ var lineText = element.textContent;
+ var ownerDocument = element.ownerDocument;
+ var textNodeSnapshot = ownerDocument.evaluate(".//text()", element, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
+
+ var snapshotLength = textNodeSnapshot.snapshotLength;
+ if (snapshotLength === 0)
+ return highlightNodes;
+
+ var nodeRanges = [];
+ var rangeEndOffset = 0;
+ for (var i = 0; i < snapshotLength; ++i) {
+ var range = {};
+ range.offset = rangeEndOffset;
+ range.length = textNodeSnapshot.snapshotItem(i).textContent.length;
+ rangeEndOffset = range.offset + range.length;
+ nodeRanges.push(range);
+ }
+
+ var startIndex = 0;
+ for (var i = 0; i < resultRanges.length; ++i) {
+ var startOffset = resultRanges[i].offset;
+ var endOffset = startOffset + resultRanges[i].length;
+
+ while (startIndex < snapshotLength && nodeRanges[startIndex].offset + nodeRanges[startIndex].length <= startOffset)
+ startIndex++;
+ var endIndex = startIndex;
+ while (endIndex < snapshotLength && nodeRanges[endIndex].offset + nodeRanges[endIndex].length < endOffset)
+ endIndex++;
+ if (endIndex === snapshotLength)
+ break;
+
+ var highlightNode = ownerDocument.createElement("span");
+ highlightNode.className = styleClass;
+ highlightNode.textContent = lineText.substring(startOffset, endOffset);
+
+ var lastTextNode = textNodeSnapshot.snapshotItem(endIndex);
+ var lastText = lastTextNode.textContent;
+ lastTextNode.textContent = lastText.substring(endOffset - nodeRanges[endIndex].offset);
+ changes.push({node: lastTextNode, type: "changed", oldText: lastText, newText: lastTextNode.textContent});
+
+ if (startIndex === endIndex) {
+ lastTextNode.parentElement.insertBefore(highlightNode, lastTextNode);
+ changes.push({node: highlightNode, type: "added", nextSibling: lastTextNode, parent: lastTextNode.parentElement});
+ highlightNodes.push(highlightNode);
+
+ var prefixNode = ownerDocument.createTextNode(lastText.substring(0, startOffset - nodeRanges[startIndex].offset));
+ lastTextNode.parentElement.insertBefore(prefixNode, highlightNode);
+ changes.push({node: prefixNode, type: "added", nextSibling: highlightNode, parent: lastTextNode.parentElement});
+ } else {
+ var firstTextNode = textNodeSnapshot.snapshotItem(startIndex);
+ var firstText = firstTextNode.textContent;
+ var anchorElement = firstTextNode.nextSibling;
+
+ firstTextNode.parentElement.insertBefore(highlightNode, anchorElement);
+ changes.push({node: highlightNode, type: "added", nextSibling: anchorElement, parent: firstTextNode.parentElement});
+ highlightNodes.push(highlightNode);
+
+ firstTextNode.textContent = firstText.substring(0, startOffset - nodeRanges[startIndex].offset);
+ changes.push({node: firstTextNode, type: "changed", oldText: firstText, newText: firstTextNode.textContent});
+
+ for (var j = startIndex + 1; j < endIndex; j++) {
+ var textNode = textNodeSnapshot.snapshotItem(j);
+ var text = textNode.textContent;
+ textNode.textContent = "";
+ changes.push({node: textNode, type: "changed", oldText: text, newText: textNode.textContent});
+ }
+ }
+ startIndex = endIndex;
+ nodeRanges[startIndex].offset = endOffset;
+ nodeRanges[startIndex].length = lastTextNode.textContent.length;
+
+ }
+ return highlightNodes;
+};
+
+WebInspector.revertDomChanges = function(domChanges)
+{
+ for (var i = domChanges.length - 1; i >= 0; --i) {
+ var entry = domChanges[i];
+ switch (entry.type) {
+ case "added":
+ entry.node.remove();
+ break;
+ case "changed":
+ entry.node.textContent = entry.oldText;
+ break;
+ }
+ }
+};
+
+WebInspector.archiveMainFrame = function()
+{
+ this._downloadingPage = true;
+ this._updateDownloadToolbarButton();
+
+ PageAgent.archive((error, data) => {
+ this._downloadingPage = false;
+ this._updateDownloadToolbarButton();
+
+ if (error)
+ return;
+
+ let mainFrame = WebInspector.frameResourceManager.mainFrame;
+ let archiveName = mainFrame.mainResource.urlComponents.host || mainFrame.mainResource.displayName || "Archive";
+ let url = "web-inspector:///" + encodeURI(archiveName) + ".webarchive";
+
+ InspectorFrontendHost.save(url, data, true, true);
+ });
+};
+
+WebInspector.canArchiveMainFrame = function()
+{
+ // COMPATIBILITY (iOS 7): Page.archive did not exist yet.
+ if (!PageAgent.archive || this.debuggableType !== WebInspector.DebuggableType.Web)
+ return false;
+
+ if (!WebInspector.frameResourceManager.mainFrame || !WebInspector.frameResourceManager.mainFrame.mainResource)
+ return false;
+
+ return WebInspector.Resource.typeFromMIMEType(WebInspector.frameResourceManager.mainFrame.mainResource.mimeType) === WebInspector.Resource.Type.Document;
+};
+
+WebInspector.addWindowKeydownListener = function(listener)
+{
+ if (typeof listener.handleKeydownEvent !== "function")
+ return;
+
+ this._windowKeydownListeners.push(listener);
+
+ this._updateWindowKeydownListener();
+};
+
+WebInspector.removeWindowKeydownListener = function(listener)
+{
+ this._windowKeydownListeners.remove(listener);
+
+ this._updateWindowKeydownListener();
+};
+
+WebInspector._updateWindowKeydownListener = function()
+{
+ if (this._windowKeydownListeners.length === 1)
+ window.addEventListener("keydown", WebInspector._sharedWindowKeydownListener, true);
+ else if (!this._windowKeydownListeners.length)
+ window.removeEventListener("keydown", WebInspector._sharedWindowKeydownListener, true);
+};
+
+WebInspector._sharedWindowKeydownListener = function(event)
+{
+ for (var i = WebInspector._windowKeydownListeners.length - 1; i >= 0; --i) {
+ if (WebInspector._windowKeydownListeners[i].handleKeydownEvent(event)) {
+ event.stopImmediatePropagation();
+ event.preventDefault();
+ break;
+ }
+ }
+};
+
+WebInspector.reportInternalError = function(errorOrString, details={})
+{
+ // The 'details' object includes additional information from the caller as free-form string keys and values.
+ // Each key and value will be shown in the uncaught exception reporter, console error message, or in
+ // a pre-filled bug report generated for this internal error.
+
+ let error = (errorOrString instanceof Error) ? errorOrString : new Error(errorOrString);
+ error.details = details;
+
+ // The error will be displayed in the Uncaught Exception Reporter sheet if DebugUI is enabled.
+ if (WebInspector.isDebugUIEnabled()) {
+ // This assert allows us to stop the debugger at an internal exception. It doesn't re-throw
+ // exceptions because the original exception would be lost through window.onerror.
+ // This workaround can be removed once <https://webkit.org/b/158192> is fixed.
+ console.assert(false, "An internal exception was thrown.", error);
+ handleInternalException(error);
+ } else
+ console.error(error);
+};
+
+Object.defineProperty(WebInspector, "targets",
+{
+ get() { return this.targetManager.targets; }
+});
+
+// Many places assume the main target because they cannot yet be
+// used by reached by Worker debugging. Eventually, once all
+// Worker domains have been implemented, all of these must be
+// handled properly.
+WebInspector.assumingMainTarget = function()
+{
+ return WebInspector.mainTarget;
+};
+
+// OpenResourceDialog delegate
+
+WebInspector.dialogWasDismissed = function(dialog)
+{
+ let representedObject = dialog.representedObject;
+ if (!representedObject)
+ return;
+
+ WebInspector.showRepresentedObject(representedObject, dialog.cookie);
+};
+
+WebInspector.DockConfiguration = {
+ Right: "right",
+ Left: "left",
+ Bottom: "bottom",
+ Undocked: "undocked",
+};
diff --git a/Source/WebInspectorUI/UserInterface/Base/Object.js b/Source/WebInspectorUI/UserInterface/Base/Object.js
new file mode 100644
index 000000000..2a91983af
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Base/Object.js
@@ -0,0 +1,229 @@
+/*
+ * 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.
+ */
+
+WebInspector.Object = class WebInspectorObject
+{
+ constructor()
+ {
+ this._listeners = null;
+ }
+
+ // Static
+
+ static addEventListener(eventType, listener, thisObject)
+ {
+ thisObject = thisObject || null;
+
+ console.assert(eventType, "Object.addEventListener: invalid event type ", eventType, "(listener: ", listener, "thisObject: ", thisObject, ")");
+ if (!eventType)
+ return null;
+
+ console.assert(listener, "Object.addEventListener: invalid listener ", listener, "(event type: ", eventType, "thisObject: ", thisObject, ")");
+ if (!listener)
+ return null;
+
+ if (!this._listeners)
+ this._listeners = new Map();
+
+ let listenersTable = this._listeners.get(eventType);
+ if (!listenersTable) {
+ listenersTable = new ListMultimap();
+ this._listeners.set(eventType, listenersTable);
+ }
+
+ listenersTable.add(thisObject, listener);
+ return listener;
+ }
+
+ static singleFireEventListener(eventType, listener, thisObject)
+ {
+ let wrappedCallback = function() {
+ this.removeEventListener(eventType, wrappedCallback, null);
+ listener.apply(thisObject, arguments);
+ }.bind(this);
+
+ this.addEventListener(eventType, wrappedCallback, null);
+ return wrappedCallback;
+ }
+
+ static removeEventListener(eventType, listener, thisObject)
+ {
+ eventType = eventType || null;
+ listener = listener || null;
+ thisObject = thisObject || null;
+
+ if (!this._listeners)
+ return;
+
+ if (thisObject && !eventType) {
+ this._listeners.forEach(function(listenersTable) {
+ let listenerPairs = listenersTable.toArray();
+ for (let i = 0, length = listenerPairs.length; i < length; ++i) {
+ let existingThisObject = listenerPairs[i][0];
+ if (existingThisObject === thisObject)
+ listenersTable.deleteAll(existingThisObject);
+ }
+ });
+
+ return;
+ }
+
+ let listenersTable = this._listeners.get(eventType);
+ if (!listenersTable || listenersTable.size === 0)
+ return;
+
+ let didDelete = listenersTable.delete(thisObject, listener);
+ console.assert(didDelete, "removeEventListener cannot remove " + eventType.toString() + " because it doesn't exist.");
+ }
+
+ static awaitEvent(eventType)
+ {
+ let wrapper = new WebInspector.WrappedPromise;
+ this.singleFireEventListener(eventType, (event) => wrapper.resolve(event));
+ return wrapper.promise;
+ }
+
+ // Only used by tests.
+ static hasEventListeners(eventType)
+ {
+ if (!this._listeners)
+ return false;
+
+ let listenersTable = this._listeners.get(eventType);
+ return listenersTable && listenersTable.size > 0;
+ }
+
+ // This should only be used within regression tests to detect leaks.
+ static retainedObjectsWithPrototype(proto)
+ {
+ let results = new Set;
+
+ if (this._listeners) {
+ this._listeners.forEach(function(listenersTable, eventType) {
+ listenersTable.forEach(function(pair) {
+ let thisObject = pair[0];
+ if (thisObject instanceof proto)
+ results.add(thisObject);
+ });
+ });
+ }
+
+ return results;
+ }
+
+ // Public
+
+ addEventListener() { return WebInspector.Object.addEventListener.apply(this, arguments); }
+ singleFireEventListener() { return WebInspector.Object.singleFireEventListener.apply(this, arguments); }
+ removeEventListener() { return WebInspector.Object.removeEventListener.apply(this, arguments); }
+ awaitEvent() { return WebInspector.Object.awaitEvent.apply(this, arguments); }
+ hasEventListeners() { return WebInspector.Object.hasEventListeners.apply(this, arguments); }
+ retainedObjectsWithPrototype() { return WebInspector.Object.retainedObjectsWithPrototype.apply(this, arguments); }
+
+ dispatchEventToListeners(eventType, eventData)
+ {
+ let event = new WebInspector.Event(this, eventType, eventData);
+
+ function dispatch(object)
+ {
+ if (!object || event._stoppedPropagation)
+ return;
+
+ let listenerTypesMap = object._listeners;
+ if (!listenerTypesMap || !object.hasOwnProperty("_listeners"))
+ return;
+
+ console.assert(listenerTypesMap instanceof Map);
+
+ let listenersTable = listenerTypesMap.get(eventType);
+ if (!listenersTable)
+ return;
+
+ // Make a copy with slice so mutations during the loop doesn't affect us.
+ let listeners = listenersTable.toArray();
+
+ // Iterate over the listeners and call them. Stop if stopPropagation is called.
+ for (let i = 0, length = listeners.length; i < length; ++i) {
+ let [thisObject, listener] = listeners[i];
+ listener.call(thisObject, 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;
+ }
+};
+
+WebInspector.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;
+ }
+};
+
+WebInspector.notifications = new WebInspector.Object;
+
+WebInspector.Notification = {
+ GlobalModifierKeysDidChange: "global-modifiers-did-change",
+ PageArchiveStarted: "page-archive-started",
+ PageArchiveEnded: "page-archive-ended",
+ ExtraDomainsActivated: "extra-domains-activated",
+ TabTypesChanged: "tab-types-changed",
+ DebugUIEnabledDidChange: "debug-ui-enabled-did-change",
+ VisibilityStateDidChange: "visibility-state-did-change",
+};
diff --git a/Source/WebInspectorUI/UserInterface/Base/Platform.js b/Source/WebInspectorUI/UserInterface/Base/Platform.js
new file mode 100644
index 000000000..65764ec7d
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Base/Platform.js
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+WebInspector.Platform = {
+ name: InspectorFrontendHost.platform(),
+ isNightlyBuild: false,
+ version: {
+ base: 0,
+ release: 0,
+ name: ""
+ }
+};
+
+(function () {
+ // Check for a nightly build by looking for a plus in the version number and a small number of stylesheets (indicating combined resources).
+ var versionMatch = / AppleWebKit\/([^ ]+)/.exec(navigator.userAgent);
+ if (versionMatch && versionMatch[1].indexOf("+") !== -1 && document.styleSheets.length < 10)
+ WebInspector.Platform.isNightlyBuild = true;
+
+ var osVersionMatch = / Mac OS X (\d+)_(\d+)/.exec(navigator.appVersion);
+ if (osVersionMatch && osVersionMatch[1] === "10") {
+ WebInspector.Platform.version.base = 10;
+ WebInspector.Platform.version.release = parseInt(osVersionMatch[2]);
+ switch (osVersionMatch[2]) {
+ case "12":
+ WebInspector.Platform.version.name = "sierra";
+ break;
+ case "11":
+ WebInspector.Platform.version.name = "el-capitan";
+ break;
+ case "10":
+ WebInspector.Platform.version.name = "yosemite";
+ break;
+ default:
+ WebInspector.Platform.version.name = "unknown-mac";
+ break;
+ }
+ }
+})();
diff --git a/Source/WebInspectorUI/UserInterface/Base/Setting.js b/Source/WebInspectorUI/UserInterface/Base/Setting.js
new file mode 100644
index 000000000..257524e3f
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Base/Setting.js
@@ -0,0 +1,118 @@
+/*
+ * 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.
+ */
+
+WebInspector.Setting = class Setting extends WebInspector.Object
+{
+ constructor(name, defaultValue)
+ {
+ super();
+
+ this._name = name;
+
+ let inspectionLevel = InspectorFrontendHost ? InspectorFrontendHost.inspectionLevel() : 1;
+ let levelString = inspectionLevel > 1 ? "-" + inspectionLevel : "";
+ this._localStorageKey = `com.apple.WebInspector${levelString}.${name}`;
+ this._defaultValue = defaultValue;
+ }
+
+ // Public
+
+ get name()
+ {
+ return this._name;
+ }
+
+ 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 && this._localStorageKey in window.localStorage) {
+ try {
+ this._value = JSON.parse(window.localStorage[this._localStorageKey]);
+ } catch (e) {
+ delete window.localStorage[this._localStorageKey];
+ }
+ }
+
+ return this._value;
+ }
+
+ set value(value)
+ {
+ if (this._value === value)
+ return;
+
+ this._value = value;
+
+ if (!window.InspectorTest && window.localStorage) {
+ try {
+ // Use Object.shallowEqual to properly compare objects.
+ if (Object.shallowEqual(this._value, this._defaultValue))
+ delete window.localStorage[this._localStorageKey];
+ else
+ window.localStorage[this._localStorageKey] = JSON.stringify(this._value);
+ } catch (e) {
+ console.error("Error saving setting with name: " + this._name);
+ }
+ }
+
+ this.dispatchEventToListeners(WebInspector.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));
+ }
+};
+
+WebInspector.Setting.Event = {
+ Changed: "setting-changed"
+};
+
+WebInspector.settings = {
+ enableLineWrapping: new WebInspector.Setting("enable-line-wrapping", false),
+ indentUnit: new WebInspector.Setting("indent-unit", 4),
+ tabSize: new WebInspector.Setting("tab-size", 4),
+ indentWithTabs: new WebInspector.Setting("indent-with-tabs", false),
+ showWhitespaceCharacters: new WebInspector.Setting("show-whitespace-characters", false),
+ showInvalidCharacters: new WebInspector.Setting("show-invalid-characters", false),
+ clearLogOnNavigate: new WebInspector.Setting("clear-log-on-navigate", true),
+ clearNetworkOnNavigate: new WebInspector.Setting("clear-network-on-navigate", true),
+ zoomFactor: new WebInspector.Setting("zoom-factor", 1),
+ // FIXME: change initial value to 'system' once we are happy with RTL support.
+ // This will cause Web Inspector to use the system user interface layout direction.
+ layoutDirection: new WebInspector.Setting("layout-direction", "ltr"),
+};
diff --git a/Source/WebInspectorUI/UserInterface/Base/URLUtilities.js b/Source/WebInspectorUI/UserInterface/Base/URLUtilities.js
new file mode 100644
index 000000000..e3935f504
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Base/URLUtilities.js
@@ -0,0 +1,265 @@
+/*
+ * 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.
+ */
+
+// FIXME: <https://webkit.org/b/165155> Web Inspector: Use URL constructor to better handle all kinds of URLs
+
+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() : "";
+
+ var match = securityOrigin.match(/^([^:]+):\/\/([^\/:]*)(?::([\d]+))?$/i);
+ if (!match)
+ return {scheme: null, host: null, port: null};
+
+ var scheme = match[1].toLowerCase();
+ var host = match[2].toLowerCase();
+ var port = Number(match[3]) || 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:([^;,]*)?(?:;charset=([^;,]*?))?(;base64)?,(.*)$/);
+ if (!match)
+ return null;
+
+ let scheme = "data";
+ let mimeType = match[1] || "text/plain";
+ let charset = match[2] || "US-ASCII";
+ let base64 = !!match[3];
+ let data = decodeURIComponent(match[4]);
+
+ return {scheme, mimeType, charset, base64, data};
+}
+
+function parseURL(url)
+{
+ url = url ? url.trim() : "";
+
+ if (url.startsWith("data:"))
+ return {scheme: "data", host: null, port: null, path: null, queryString: null, fragment: null, lastPathComponent: null};
+
+ var match = url.match(/^([^\/:]+):\/\/([^\/#:]*)(?::([\d]+))?(?:(\/[^#]*)?(?:#(.*))?)?$/i);
+ if (!match)
+ return {scheme: null, host: null, port: null, path: null, queryString: null, fragment: null, lastPathComponent: null};
+
+ var scheme = match[1].toLowerCase();
+ var host = match[2].toLowerCase();
+ var port = Number(match[3]) || null;
+ var wholePath = match[4] || null;
+ var fragment = match[5] || null;
+ var path = wholePath;
+ var queryString = null;
+
+ // Split the path and the query string.
+ if (wholePath) {
+ var indexOfQuery = wholePath.indexOf("?");
+ if (indexOfQuery !== -1) {
+ path = wholePath.substring(0, indexOfQuery);
+ queryString = wholePath.substring(indexOfQuery + 1);
+ }
+ path = resolveDotsInPath(path);
+ }
+
+ // Find last path component.
+ var lastPathComponent = null;
+ if (path && path !== "/") {
+ // Skip the trailing slash if there is one.
+ var endOffset = path[path.length - 1] === "/" ? 1 : 0;
+ var lastSlashIndex = path.lastIndexOf("/", path.length - 1 - endOffset);
+ if (lastSlashIndex !== -1)
+ lastPathComponent = path.substring(lastSlashIndex + 1, path.length - endOffset);
+ }
+
+ return {scheme, host, port, path, queryString, fragment, lastPathComponent};
+}
+
+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 parseLocationQueryParameters(arrayResult)
+{
+ // The first character is always the "?".
+ return parseQueryString(window.location.search.substring(1), arrayResult);
+}
+
+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 (e) {
+ return string;
+ }
+ }
+
+ var parameters = arrayResult ? [] : {};
+ var parameterStrings = queryString.split("&");
+ for (var i = 0; i < parameterStrings.length; ++i) {
+ var pair = parameterStrings[i].split("=").map(decode);
+ if (arrayResult)
+ parameters.push({name: pair[0], value: pair[1]});
+ else
+ parameters[pair[0]] = pair[1];
+ }
+
+ return parameters;
+}
+
+WebInspector.displayNameForURL = function(url, urlComponents)
+{
+ if (url.startsWith("data:"))
+ return WebInspector.truncateURL(url);
+
+ if (!urlComponents)
+ urlComponents = parseURL(url);
+
+ var displayName;
+ try {
+ displayName = decodeURIComponent(urlComponents.lastPathComponent || "");
+ } catch (e) {
+ displayName = urlComponents.lastPathComponent;
+ }
+
+ return displayName || WebInspector.displayNameForHost(urlComponents.host) || url;
+};
+
+WebInspector.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;
+};
+
+WebInspector.displayNameForHost = function(host)
+{
+ // FIXME <rdar://problem/11237413>: This should decode punycode hostnames.
+ return host;
+};
diff --git a/Source/WebInspectorUI/UserInterface/Base/Utilities.js b/Source/WebInspectorUI/UserInterface/Base/Utilities.js
new file mode 100644
index 000000000..06c2f1cb5
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Base/Utilities.js
@@ -0,0 +1,1509 @@
+/*
+ * 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 emDash = "\u2014";
+var enDash = "\u2013";
+var figureDash = "\u2012";
+var ellipsis = "\u2026";
+
+Object.defineProperty(Object, "shallowCopy",
+{
+ value: function(object)
+ {
+ // Make a new object and copy all the key/values. The values are not copied.
+ var copy = {};
+ var keys = Object.keys(object);
+ for (var i = 0; i < keys.length; ++i)
+ copy[keys[i]] = object[keys[i]];
+ return copy;
+ }
+});
+
+Object.defineProperty(Object, "shallowEqual",
+{
+ value: function(a, b)
+ {
+ // Checks if two objects have the same top-level properties.
+
+ // Only objects can proceed.
+ if (!(a instanceof Object) || !(b instanceof Object))
+ return false;
+
+ // Check for strict equality in case they are the same object.
+ if (a === b)
+ return true;
+
+ // Use an optimized version of shallowEqual for arrays.
+ if (Array.isArray(a) && Array.isArray(b))
+ return Array.shallowEqual(a, b);
+
+ if (a.constructor !== b.constructor)
+ return false;
+
+ var aKeys = Object.keys(a);
+ var bKeys = Object.keys(b);
+
+ // Check that each object has the same number of keys.
+ if (aKeys.length !== bKeys.length)
+ return false;
+
+ // Check if all the keys and their values are equal.
+ for (var i = 0; i < aKeys.length; ++i) {
+ // Check that b has the same key as a.
+ if (!(aKeys[i] in b))
+ return false;
+
+ // Check that the values are strict equal since this is only
+ // a shallow check, not a recursive one.
+ if (a[aKeys[i]] !== b[aKeys[i]])
+ return false;
+ }
+
+ return true;
+ }
+});
+
+Object.defineProperty(Object, "shallowMerge",
+{
+ value(a, b)
+ {
+ let result = Object.shallowCopy(a);
+ let keys = Object.keys(b);
+ for (let i = 0; i < keys.length; ++i) {
+ console.assert(!result.hasOwnProperty(keys[i]) || result[keys[i]] === b[keys[i]], keys[i]);
+ result[keys[i]] = b[keys[i]];
+ }
+ return result;
+ }
+});
+
+Object.defineProperty(Object.prototype, "valueForCaseInsensitiveKey",
+{
+ value: function(key)
+ {
+ if (this.hasOwnProperty(key))
+ return this[key];
+
+ var lowerCaseKey = key.toLowerCase();
+ for (var currentKey in this) {
+ if (currentKey.toLowerCase() === lowerCaseKey)
+ return this[currentKey];
+ }
+
+ return undefined;
+ }
+});
+
+Object.defineProperty(Map, "fromObject",
+{
+ value: function(object)
+ {
+ let map = new Map;
+ for (let key in object)
+ map.set(key, object[key]);
+ return map;
+ }
+});
+
+Object.defineProperty(Map.prototype, "take",
+{
+ value: function(key)
+ {
+ var deletedValue = this.get(key);
+ this.delete(key);
+ return deletedValue;
+ }
+});
+
+Object.defineProperty(Node.prototype, "enclosingNodeOrSelfWithClass",
+{
+ value: function(className)
+ {
+ for (var node = this; node && node !== this.ownerDocument; node = node.parentNode)
+ if (node.nodeType === Node.ELEMENT_NODE && node.classList.contains(className))
+ return node;
+ return null;
+ }
+});
+
+Object.defineProperty(Node.prototype, "enclosingNodeOrSelfWithNodeNameInArray",
+{
+ value: function(nameArray)
+ {
+ var lowerCaseNameArray = nameArray.map(function(name) { return name.toLowerCase(); });
+ for (var node = this; node && node !== this.ownerDocument; node = node.parentNode) {
+ for (var i = 0; i < nameArray.length; ++i) {
+ if (node.nodeName.toLowerCase() === lowerCaseNameArray[i])
+ return node;
+ }
+ }
+
+ return null;
+ }
+});
+
+Object.defineProperty(Node.prototype, "enclosingNodeOrSelfWithNodeName",
+{
+ value: function(nodeName)
+ {
+ return this.enclosingNodeOrSelfWithNodeNameInArray([nodeName]);
+ }
+});
+
+Object.defineProperty(Node.prototype, "isAncestor",
+{
+ value: function(node)
+ {
+ if (!node)
+ return false;
+
+ var currentNode = node.parentNode;
+ while (currentNode) {
+ if (this === currentNode)
+ return true;
+ currentNode = currentNode.parentNode;
+ }
+
+ return false;
+ }
+});
+
+Object.defineProperty(Node.prototype, "isDescendant",
+{
+ value: function(descendant)
+ {
+ return !!descendant && descendant.isAncestor(this);
+ }
+});
+
+
+Object.defineProperty(Node.prototype, "isSelfOrAncestor",
+{
+ value: function(node)
+ {
+ return !!node && (node === this || this.isAncestor(node));
+ }
+});
+
+
+Object.defineProperty(Node.prototype, "isSelfOrDescendant",
+{
+ value: function(node)
+ {
+ return !!node && (node === this || this.isDescendant(node));
+ }
+});
+
+Object.defineProperty(Node.prototype, "traverseNextNode",
+{
+ value: function(stayWithin)
+ {
+ var node = this.firstChild;
+ if (node)
+ return node;
+
+ if (stayWithin && this === stayWithin)
+ return null;
+
+ node = this.nextSibling;
+ if (node)
+ return node;
+
+ node = this;
+ while (node && !node.nextSibling && (!stayWithin || !node.parentNode || node.parentNode !== stayWithin))
+ node = node.parentNode;
+ if (!node)
+ return null;
+
+ return node.nextSibling;
+ }
+});
+
+Object.defineProperty(Node.prototype, "traversePreviousNode",
+{
+ value: function(stayWithin)
+ {
+ if (stayWithin && this === stayWithin)
+ return null;
+ var node = this.previousSibling;
+ while (node && node.lastChild)
+ node = node.lastChild;
+ if (node)
+ return node;
+ return this.parentNode;
+ }
+});
+
+
+Object.defineProperty(Node.prototype, "rangeOfWord",
+{
+ value: function(offset, stopCharacters, stayWithinNode, direction)
+ {
+ var startNode;
+ var startOffset = 0;
+ var endNode;
+ var endOffset = 0;
+
+ if (!stayWithinNode)
+ stayWithinNode = this;
+
+ if (!direction || direction === "backward" || direction === "both") {
+ var node = this;
+ while (node) {
+ if (node === stayWithinNode) {
+ if (!startNode)
+ startNode = stayWithinNode;
+ break;
+ }
+
+ if (node.nodeType === Node.TEXT_NODE) {
+ var start = (node === this ? (offset - 1) : (node.nodeValue.length - 1));
+ for (var i = start; i >= 0; --i) {
+ if (stopCharacters.indexOf(node.nodeValue[i]) !== -1) {
+ startNode = node;
+ startOffset = i + 1;
+ break;
+ }
+ }
+ }
+
+ if (startNode)
+ break;
+
+ node = node.traversePreviousNode(stayWithinNode);
+ }
+
+ if (!startNode) {
+ startNode = stayWithinNode;
+ startOffset = 0;
+ }
+ } else {
+ startNode = this;
+ startOffset = offset;
+ }
+
+ if (!direction || direction === "forward" || direction === "both") {
+ node = this;
+ while (node) {
+ if (node === stayWithinNode) {
+ if (!endNode)
+ endNode = stayWithinNode;
+ break;
+ }
+
+ if (node.nodeType === Node.TEXT_NODE) {
+ var start = (node === this ? offset : 0);
+ for (var i = start; i < node.nodeValue.length; ++i) {
+ if (stopCharacters.indexOf(node.nodeValue[i]) !== -1) {
+ endNode = node;
+ endOffset = i;
+ break;
+ }
+ }
+ }
+
+ if (endNode)
+ break;
+
+ node = node.traverseNextNode(stayWithinNode);
+ }
+
+ if (!endNode) {
+ endNode = stayWithinNode;
+ endOffset = stayWithinNode.nodeType === Node.TEXT_NODE ? stayWithinNode.nodeValue.length : stayWithinNode.childNodes.length;
+ }
+ } else {
+ endNode = this;
+ endOffset = offset;
+ }
+
+ var result = this.ownerDocument.createRange();
+ result.setStart(startNode, startOffset);
+ result.setEnd(endNode, endOffset);
+
+ return result;
+
+ }
+});
+
+Object.defineProperty(Element.prototype, "realOffsetWidth",
+{
+ get: function()
+ {
+ return this.getBoundingClientRect().width;
+ }
+});
+
+Object.defineProperty(Element.prototype, "realOffsetHeight",
+{
+ get: function()
+ {
+ return this.getBoundingClientRect().height;
+ }
+});
+
+Object.defineProperty(Element.prototype, "totalOffsetLeft",
+{
+ get: function()
+ {
+ return this.getBoundingClientRect().left;
+ }
+});
+
+Object.defineProperty(Element.prototype, "totalOffsetTop",
+{
+ get: function()
+ {
+ return this.getBoundingClientRect().top;
+ }
+});
+
+Object.defineProperty(Element.prototype, "removeChildren",
+{
+ value: function()
+ {
+ // This has been tested to be the fastest removal method.
+ if (this.firstChild)
+ this.textContent = "";
+ }
+});
+
+Object.defineProperty(Element.prototype, "isInsertionCaretInside",
+{
+ value: function()
+ {
+ var selection = window.getSelection();
+ if (!selection.rangeCount || !selection.isCollapsed)
+ return false;
+ var selectionRange = selection.getRangeAt(0);
+ return selectionRange.startContainer === this || selectionRange.startContainer.isDescendant(this);
+ }
+});
+
+Object.defineProperty(Element.prototype, "removeMatchingStyleClasses",
+{
+ value: function(classNameRegex)
+ {
+ var regex = new RegExp("(^|\\s+)" + classNameRegex + "($|\\s+)");
+ if (regex.test(this.className))
+ this.className = this.className.replace(regex, " ");
+ }
+});
+
+Object.defineProperty(Element.prototype, "createChild",
+{
+ value: function(elementName, className)
+ {
+ var element = this.ownerDocument.createElement(elementName);
+ if (className)
+ element.className = className;
+ this.appendChild(element);
+ return element;
+ }
+});
+
+Object.defineProperty(Element.prototype, "isScrolledToBottom",
+{
+ value: function()
+ {
+ // This code works only for 0-width border
+ return this.scrollTop + this.clientHeight === this.scrollHeight;
+ }
+});
+
+Object.defineProperty(Element.prototype, "recalculateStyles",
+{
+ value: function()
+ {
+ this.ownerDocument.defaultView.getComputedStyle(this);
+ }
+});
+
+Object.defineProperty(DocumentFragment.prototype, "createChild",
+{
+ value: Element.prototype.createChild
+});
+
+Object.defineProperty(Array, "shallowEqual",
+{
+ value: function(a, b)
+ {
+ if (!Array.isArray(a) || !Array.isArray(b))
+ return false;
+
+ if (a === b)
+ return true;
+
+ let length = a.length;
+
+ if (length !== b.length)
+ return false;
+
+ for (let i = 0; i < length; ++i) {
+ if (a[i] === b[i])
+ continue;
+
+ if (!Object.shallowEqual(a[i], b[i]))
+ return false;
+ }
+
+ return true;
+ }
+});
+
+Object.defineProperty(Array.prototype, "lastValue",
+{
+ get: function()
+ {
+ if (!this.length)
+ return undefined;
+ return this[this.length - 1];
+ }
+});
+
+Object.defineProperty(Array.prototype, "remove",
+{
+ value: function(value, onlyFirst)
+ {
+ for (var i = this.length - 1; i >= 0; --i) {
+ if (this[i] === value) {
+ this.splice(i, 1);
+ if (onlyFirst)
+ return;
+ }
+ }
+ }
+});
+
+Object.defineProperty(Array.prototype, "toggleIncludes",
+{
+ value: function(value, force)
+ {
+ let exists = this.includes(value);
+ if (exists === !!force)
+ return;
+
+ if (exists)
+ this.remove(value);
+ else
+ this.push(value);
+ }
+});
+
+Object.defineProperty(Array.prototype, "insertAtIndex",
+{
+ value: function(value, index)
+ {
+ this.splice(index, 0, value);
+ }
+});
+
+Object.defineProperty(Array.prototype, "keySet",
+{
+ value: function()
+ {
+ let keys = Object.create(null);
+ for (var i = 0; i < this.length; ++i)
+ keys[this[i]] = true;
+ return keys;
+ }
+});
+
+Object.defineProperty(Array.prototype, "partition",
+{
+ value: function(callback)
+ {
+ let positive = [];
+ let negative = [];
+ for (let i = 0; i < this.length; ++i) {
+ let value = this[i];
+ if (callback(value))
+ positive.push(value);
+ else
+ negative.push(value);
+ }
+ return [positive, negative];
+ }
+});
+
+Object.defineProperty(String.prototype, "isLowerCase",
+{
+ value: function()
+ {
+ return String(this) === this.toLowerCase();
+ }
+});
+
+Object.defineProperty(String.prototype, "isUpperCase",
+{
+ value: function()
+ {
+ return String(this) === this.toUpperCase();
+ }
+});
+
+Object.defineProperty(String.prototype, "trimMiddle",
+{
+ value: function(maxLength)
+ {
+ if (this.length <= maxLength)
+ return this;
+ var leftHalf = maxLength >> 1;
+ var rightHalf = maxLength - leftHalf - 1;
+ return this.substr(0, leftHalf) + ellipsis + this.substr(this.length - rightHalf, rightHalf);
+ }
+});
+
+Object.defineProperty(String.prototype, "trimEnd",
+{
+ value: function(maxLength)
+ {
+ if (this.length <= maxLength)
+ return this;
+ return this.substr(0, maxLength - 1) + ellipsis;
+ }
+});
+
+Object.defineProperty(String.prototype, "truncate",
+{
+ value: function(maxLength)
+ {
+ "use strict";
+
+ if (this.length <= maxLength)
+ return this;
+
+ let clipped = this.slice(0, maxLength);
+ let indexOfLastWhitespace = clipped.search(/\s\S*$/);
+ if (indexOfLastWhitespace > Math.floor(maxLength / 2))
+ clipped = clipped.slice(0, indexOfLastWhitespace - 1);
+
+ return clipped + ellipsis;
+ }
+});
+
+Object.defineProperty(String.prototype, "collapseWhitespace",
+{
+ value: function()
+ {
+ return this.replace(/[\s\xA0]+/g, " ");
+ }
+});
+
+Object.defineProperty(String.prototype, "removeWhitespace",
+{
+ value: function()
+ {
+ return this.replace(/[\s\xA0]+/g, "");
+ }
+});
+
+Object.defineProperty(String.prototype, "escapeCharacters",
+{
+ value: function(chars)
+ {
+ var foundChar = false;
+ for (var i = 0; i < chars.length; ++i) {
+ if (this.indexOf(chars.charAt(i)) !== -1) {
+ foundChar = true;
+ break;
+ }
+ }
+
+ if (!foundChar)
+ return this;
+
+ var result = "";
+ for (var i = 0; i < this.length; ++i) {
+ if (chars.indexOf(this.charAt(i)) !== -1)
+ result += "\\";
+ result += this.charAt(i);
+ }
+
+ return result;
+ }
+});
+
+Object.defineProperty(String.prototype, "escapeForRegExp",
+{
+ value: function()
+ {
+ return this.escapeCharacters("^[]{}()\\.$*+?|");
+ }
+});
+
+Object.defineProperty(String.prototype, "capitalize",
+{
+ value: function()
+ {
+ return this.charAt(0).toUpperCase() + this.slice(1);
+ }
+});
+
+Object.defineProperty(String, "tokenizeFormatString",
+{
+ value: function(format)
+ {
+ var tokens = [];
+ var substitutionIndex = 0;
+
+ function addStringToken(str)
+ {
+ tokens.push({type: "string", value: str});
+ }
+
+ function addSpecifierToken(specifier, precision, substitutionIndex)
+ {
+ tokens.push({type: "specifier", specifier, precision, substitutionIndex});
+ }
+
+ var index = 0;
+ for (var precentIndex = format.indexOf("%", index); precentIndex !== -1; precentIndex = format.indexOf("%", index)) {
+ addStringToken(format.substring(index, precentIndex));
+ index = precentIndex + 1;
+
+ if (format[index] === "%") {
+ addStringToken("%");
+ ++index;
+ continue;
+ }
+
+ if (!isNaN(format[index])) {
+ // The first character is a number, it might be a substitution index.
+ var number = parseInt(format.substring(index), 10);
+ while (!isNaN(format[index]))
+ ++index;
+
+ // If the number is greater than zero and ends with a "$",
+ // then this is a substitution index.
+ if (number > 0 && format[index] === "$") {
+ substitutionIndex = (number - 1);
+ ++index;
+ }
+ }
+
+ const defaultPrecision = 6;
+
+ let precision = defaultPrecision;
+ if (format[index] === ".") {
+ // This is a precision specifier. If no digit follows the ".",
+ // then use the default precision of six digits (ISO C99 specification).
+ ++index;
+
+ precision = parseInt(format.substring(index), 10);
+ if (isNaN(precision))
+ precision = defaultPrecision;
+
+ while (!isNaN(format[index]))
+ ++index;
+ }
+
+ addSpecifierToken(format[index], precision, substitutionIndex);
+
+ ++substitutionIndex;
+ ++index;
+ }
+
+ addStringToken(format.substring(index));
+
+ return tokens;
+ }
+});
+
+Object.defineProperty(String.prototype, "hash",
+{
+ get: function()
+ {
+ // Matches the wtf/Hasher.h (SuperFastHash) algorithm.
+
+ // Arbitrary start value to avoid mapping all 0's to all 0's.
+ const stringHashingStartValue = 0x9e3779b9;
+
+ var result = stringHashingStartValue;
+ var pendingCharacter = null;
+ for (var i = 0; i < this.length; ++i) {
+ var currentCharacter = this[i].charCodeAt(0);
+ if (pendingCharacter === null) {
+ pendingCharacter = currentCharacter;
+ continue;
+ }
+
+ result += pendingCharacter;
+ result = (result << 16) ^ ((currentCharacter << 11) ^ result);
+ result += result >> 11;
+
+ pendingCharacter = null;
+ }
+
+ // Handle the last character in odd length strings.
+ if (pendingCharacter !== null) {
+ result += pendingCharacter;
+ result ^= result << 11;
+ result += result >> 17;
+ }
+
+ // Force "avalanching" of final 31 bits.
+ result ^= result << 3;
+ result += result >> 5;
+ result ^= result << 2;
+ result += result >> 15;
+ result ^= result << 10;
+
+ // Prevent 0 and negative results.
+ return (0xffffffff + result + 1).toString(36);
+ }
+});
+
+Object.defineProperty(String, "standardFormatters",
+{
+ value: {
+ d: function(substitution)
+ {
+ return parseInt(substitution);
+ },
+
+ f: function(substitution, token)
+ {
+ let value = parseFloat(substitution);
+ if (isNaN(value))
+ return NaN;
+
+ let options = {
+ minimumFractionDigits: token.precision,
+ maximumFractionDigits: token.precision,
+ useGrouping: false
+ };
+ return value.toLocaleString(undefined, options);
+ },
+
+ s: function(substitution)
+ {
+ return substitution;
+ }
+ }
+});
+
+Object.defineProperty(String, "format",
+{
+ value: function(format, substitutions, formatters, initialValue, append)
+ {
+ if (!format || !substitutions || !substitutions.length)
+ return {formattedResult: append(initialValue, format), unusedSubstitutions: substitutions};
+
+ function prettyFunctionName()
+ {
+ return "String.format(\"" + format + "\", \"" + Array.from(substitutions).join("\", \"") + "\")";
+ }
+
+ function warn(msg)
+ {
+ console.warn(prettyFunctionName() + ": " + msg);
+ }
+
+ function error(msg)
+ {
+ console.error(prettyFunctionName() + ": " + msg);
+ }
+
+ var result = initialValue;
+ var tokens = String.tokenizeFormatString(format);
+ var usedSubstitutionIndexes = {};
+
+ for (var i = 0; i < tokens.length; ++i) {
+ var token = tokens[i];
+
+ if (token.type === "string") {
+ result = append(result, token.value);
+ continue;
+ }
+
+ if (token.type !== "specifier") {
+ error("Unknown token type \"" + token.type + "\" found.");
+ continue;
+ }
+
+ if (token.substitutionIndex >= substitutions.length) {
+ // If there are not enough substitutions for the current substitutionIndex
+ // just output the format specifier literally and move on.
+ error("not enough substitution arguments. Had " + substitutions.length + " but needed " + (token.substitutionIndex + 1) + ", so substitution was skipped.");
+ result = append(result, "%" + (token.precision > -1 ? token.precision : "") + token.specifier);
+ continue;
+ }
+
+ usedSubstitutionIndexes[token.substitutionIndex] = true;
+
+ if (!(token.specifier in formatters)) {
+ // Encountered an unsupported format character, treat as a string.
+ warn("unsupported format character \u201C" + token.specifier + "\u201D. Treating as a string.");
+ result = append(result, substitutions[token.substitutionIndex]);
+ continue;
+ }
+
+ result = append(result, formatters[token.specifier](substitutions[token.substitutionIndex], token));
+ }
+
+ var unusedSubstitutions = [];
+ for (var i = 0; i < substitutions.length; ++i) {
+ if (i in usedSubstitutionIndexes)
+ continue;
+ unusedSubstitutions.push(substitutions[i]);
+ }
+
+ return {formattedResult: result, unusedSubstitutions};
+ }
+});
+
+Object.defineProperty(String.prototype, "format",
+{
+ value: function()
+ {
+ return String.format(this, arguments, String.standardFormatters, "", function(a, b) { return a + b; }).formattedResult;
+ }
+});
+
+Object.defineProperty(String.prototype, "insertWordBreakCharacters",
+{
+ value: function()
+ {
+ // Add zero width spaces after characters that are good to break after.
+ // Otherwise a string with no spaces will not break and overflow its container.
+ // This is mainly used on URL strings, so the characters are tailored for URLs.
+ return this.replace(/([\/;:\)\]\}&?])/g, "$1\u200b");
+ }
+});
+
+Object.defineProperty(String.prototype, "removeWordBreakCharacters",
+{
+ value: function()
+ {
+ // Undoes what insertWordBreakCharacters did.
+ return this.replace(/\u200b/g, "");
+ }
+});
+
+Object.defineProperty(String.prototype, "getMatchingIndexes",
+{
+ value: function(needle)
+ {
+ var indexesOfNeedle = [];
+ var index = this.indexOf(needle);
+
+ while (index >= 0) {
+ indexesOfNeedle.push(index);
+ index = this.indexOf(needle, index + 1);
+ }
+
+ return indexesOfNeedle;
+ }
+});
+
+Object.defineProperty(String.prototype, "levenshteinDistance",
+{
+ value: function(s)
+ {
+ var m = this.length;
+ var n = s.length;
+ var d = new Array(m + 1);
+
+ for (var i = 0; i <= m; ++i) {
+ d[i] = new Array(n + 1);
+ d[i][0] = i;
+ }
+
+ for (var j = 0; j <= n; ++j)
+ d[0][j] = j;
+
+ for (var j = 1; j <= n; ++j) {
+ for (var i = 1; i <= m; ++i) {
+ if (this[i - 1] === s[j - 1])
+ d[i][j] = d[i - 1][j - 1];
+ else {
+ var deletion = d[i - 1][j] + 1;
+ var insertion = d[i][j - 1] + 1;
+ var substitution = d[i - 1][j - 1] + 1;
+ d[i][j] = Math.min(deletion, insertion, substitution);
+ }
+ }
+ }
+
+ return d[m][n];
+ }
+});
+
+Object.defineProperty(String.prototype, "toCamelCase",
+{
+ value: function()
+ {
+ return this.toLowerCase().replace(/[^\w]+(\w)/g, (match, group) => group.toUpperCase());
+ }
+});
+
+Object.defineProperty(String.prototype, "hasMatchingEscapedQuotes",
+{
+ value: function()
+ {
+ return /^\"(?:[^\"\\]|\\.)*\"$/.test(this) || /^\'(?:[^\'\\]|\\.)*\'$/.test(this);
+ }
+});
+
+Object.defineProperty(Math, "roundTo",
+{
+ value: function(num, step)
+ {
+ return Math.round(num / step) * step;
+ }
+});
+
+Object.defineProperty(Number, "constrain",
+{
+ value: function(num, min, max)
+ {
+ if (max < min)
+ return min;
+
+ if (num < min)
+ num = min;
+ else if (num > max)
+ num = max;
+ return num;
+ }
+});
+
+Object.defineProperty(Number, "percentageString",
+{
+ value: function(fraction, precision = 1)
+ {
+ console.assert(fraction >= 0 && fraction <= 1);
+ return fraction.toLocaleString(undefined, {minimumFractionDigits: precision, style: "percent"});
+ }
+});
+
+Object.defineProperty(Number, "secondsToMillisecondsString",
+{
+ value: function(seconds, higherResolution)
+ {
+ let ms = seconds * 1000;
+
+ if (higherResolution)
+ return WebInspector.UIString("%.2fms").format(ms);
+ return WebInspector.UIString("%.1fms").format(ms);
+ }
+});
+
+Object.defineProperty(Number, "secondsToString",
+{
+ value: function(seconds, higherResolution)
+ {
+ let ms = seconds * 1000;
+ if (!ms)
+ return WebInspector.UIString("%.0fms").format(0);
+
+ if (Math.abs(ms) < 10) {
+ if (higherResolution)
+ return WebInspector.UIString("%.3fms").format(ms);
+ return WebInspector.UIString("%.2fms").format(ms);
+ }
+
+ if (Math.abs(ms) < 100) {
+ if (higherResolution)
+ return WebInspector.UIString("%.2fms").format(ms);
+ return WebInspector.UIString("%.1fms").format(ms);
+ }
+
+ if (Math.abs(ms) < 1000) {
+ if (higherResolution)
+ return WebInspector.UIString("%.1fms").format(ms);
+ return WebInspector.UIString("%.0fms").format(ms);
+ }
+
+ // Do not go over seconds when in high resolution mode.
+ if (higherResolution || Math.abs(seconds) < 60)
+ return WebInspector.UIString("%.2fs").format(seconds);
+
+ let minutes = seconds / 60;
+ if (Math.abs(minutes) < 60)
+ return WebInspector.UIString("%.1fmin").format(minutes);
+
+ let hours = minutes / 60;
+ if (Math.abs(hours) < 24)
+ return WebInspector.UIString("%.1fhrs").format(hours);
+
+ let days = hours / 24;
+ return WebInspector.UIString("%.1f days").format(days);
+ }
+});
+
+Object.defineProperty(Number, "bytesToString",
+{
+ value: function(bytes, higherResolution)
+ {
+ if (higherResolution === undefined)
+ higherResolution = true;
+
+ if (Math.abs(bytes) < 1024)
+ return WebInspector.UIString("%.0f B").format(bytes);
+
+ let kilobytes = bytes / 1024;
+ if (Math.abs(kilobytes) < 1024) {
+ if (higherResolution || Math.abs(kilobytes) < 10)
+ return WebInspector.UIString("%.2f KB").format(kilobytes);
+ return WebInspector.UIString("%.1f KB").format(kilobytes);
+ }
+
+ let megabytes = kilobytes / 1024;
+ if (higherResolution || Math.abs(megabytes) < 10)
+ return WebInspector.UIString("%.2f MB").format(megabytes);
+ return WebInspector.UIString("%.1f MB").format(megabytes);
+ }
+});
+
+Object.defineProperty(Number, "abbreviate",
+{
+ value: function(num)
+ {
+ if (num < 1000)
+ return num;
+
+ if (num < 1000000)
+ return WebInspector.UIString("%.1fK").format(Math.round(num / 100) / 10);
+
+ if (num < 1000000000)
+ return WebInspector.UIString("%.1fM").format(Math.round(num / 100000) / 10);
+
+ return WebInspector.UIString("%.1fB").format(Math.round(num / 100000000) / 10);
+ }
+});
+
+Object.defineProperty(Number.prototype, "maxDecimals",
+{
+ value(decimals)
+ {
+ let power = 10 ** decimals;
+ return Math.round(this * power) / power;
+ }
+});
+
+Object.defineProperty(Uint32Array, "isLittleEndian",
+{
+ value: function()
+ {
+ if ("_isLittleEndian" in this)
+ return this._isLittleEndian;
+
+ var buffer = new ArrayBuffer(4);
+ var longData = new Uint32Array(buffer);
+ var data = new Uint8Array(buffer);
+
+ longData[0] = 0x0a0b0c0d;
+
+ this._isLittleEndian = data[0] === 0x0d && data[1] === 0x0c && data[2] === 0x0b && data[3] === 0x0a;
+
+ return this._isLittleEndian;
+ }
+});
+
+function isEmptyObject(object)
+{
+ for (var property in object)
+ return false;
+ return true;
+}
+
+function isEnterKey(event)
+{
+ // Check if this is an IME event.
+ return event.keyCode !== 229 && event.keyIdentifier === "Enter";
+}
+
+function resolveDotsInPath(path)
+{
+ if (!path)
+ return path;
+
+ if (path.indexOf("./") === -1)
+ return path;
+
+ console.assert(path.charAt(0) === "/");
+
+ var result = [];
+
+ var components = path.split("/");
+ for (var i = 0; i < components.length; ++i) {
+ var component = components[i];
+
+ // Skip over "./".
+ if (component === ".")
+ continue;
+
+ // Rewind one component for "../".
+ if (component === "..") {
+ if (result.length === 1)
+ continue;
+ result.pop();
+ continue;
+ }
+
+ result.push(component);
+ }
+
+ return result.join("/");
+}
+
+function parseMIMEType(fullMimeType)
+{
+ if (!fullMimeType)
+ return {type: fullMimeType, boundary: null, encoding: null};
+
+ var typeParts = fullMimeType.split(/\s*;\s*/);
+ console.assert(typeParts.length >= 1);
+
+ var type = typeParts[0];
+ var boundary = null;
+ var encoding = null;
+
+ for (var i = 1; i < typeParts.length; ++i) {
+ var subparts = typeParts[i].split(/\s*=\s*/);
+ if (subparts.length !== 2)
+ continue;
+
+ if (subparts[0].toLowerCase() === "boundary")
+ boundary = subparts[1];
+ else if (subparts[0].toLowerCase() === "charset")
+ encoding = subparts[1].replace("^\"|\"$", ""); // Trim quotes.
+ }
+
+ return {type, boundary: boundary || null, encoding: encoding || null};
+}
+
+function simpleGlobStringToRegExp(globString, regExpFlags)
+{
+ // Only supports "*" globs.
+
+ if (!globString)
+ return null;
+
+ // Escape everything from String.prototype.escapeForRegExp except "*".
+ var regexString = globString.escapeCharacters("^[]{}()\\.$+?|");
+
+ // Unescape all doubly escaped backslashes in front of escaped asterisks.
+ // So "\\*" will become "\*" again, undoing escapeCharacters escaping of "\".
+ // This makes "\*" match a literal "*" instead of using the "*" for globbing.
+ regexString = regexString.replace(/\\\\\*/g, "\\*");
+
+ // The following regex doesn't match an asterisk that has a backslash in front.
+ // It also catches consecutive asterisks so they collapse down when replaced.
+ var unescapedAsteriskRegex = /(^|[^\\])\*+/g;
+ if (unescapedAsteriskRegex.test(globString)) {
+ // Replace all unescaped asterisks with ".*".
+ regexString = regexString.replace(unescapedAsteriskRegex, "$1.*");
+
+ // Match edge boundaries when there is an asterisk to better meet the expectations
+ // of the user. When someone types "*.js" they don't expect "foo.json" to match. They
+ // would only expect that if they type "*.js*". We use \b (instead of ^ and $) to allow
+ // matches inside paths or URLs, so "ba*.js" will match "foo/bar.js" but not "boo/bbar.js".
+ // When there isn't an asterisk the regexString is just a substring search.
+ regexString = "\\b" + regexString + "\\b";
+ }
+
+ return new RegExp(regexString, regExpFlags);
+}
+
+Object.defineProperty(Array.prototype, "lowerBound",
+{
+ // Return index of the leftmost element that is equal or greater
+ // than the specimen object. If there's no such element (i.e. all
+ // elements are smaller than the specimen) returns array.length.
+ // The function works for sorted array.
+ value: function(object, comparator)
+ {
+ function defaultComparator(a, b)
+ {
+ return a - b;
+ }
+ comparator = comparator || defaultComparator;
+ var l = 0;
+ var r = this.length;
+ while (l < r) {
+ var m = (l + r) >> 1;
+ if (comparator(object, this[m]) > 0)
+ l = m + 1;
+ else
+ r = m;
+ }
+ return r;
+ }
+});
+
+Object.defineProperty(Array.prototype, "upperBound",
+{
+ // Return index of the leftmost element that is greater
+ // than the specimen object. If there's no such element (i.e. all
+ // elements are smaller than the specimen) returns array.length.
+ // The function works for sorted array.
+ value: function(object, comparator)
+ {
+ function defaultComparator(a, b)
+ {
+ return a - b;
+ }
+ comparator = comparator || defaultComparator;
+ var l = 0;
+ var r = this.length;
+ while (l < r) {
+ var m = (l + r) >> 1;
+ if (comparator(object, this[m]) >= 0)
+ l = m + 1;
+ else
+ r = m;
+ }
+ return r;
+ }
+});
+
+Object.defineProperty(Array.prototype, "binaryIndexOf",
+{
+ value: function(value, comparator)
+ {
+ var index = this.lowerBound(value, comparator);
+ return index < this.length && comparator(value, this[index]) === 0 ? index : -1;
+ }
+});
+
+(function() {
+ // The `debounce` function lets you call any function on an object with a delay
+ // and if the function keeps getting called, the delay gets reset. Since `debounce`
+ // returns a Proxy, you can cache it and call multiple functions with the same delay.
+
+ // Use: object.debounce(200).foo("Argument 1", "Argument 2")
+ // Note: The last call's arguments get used for the delayed call.
+
+ const debounceTimeoutSymbol = Symbol("debounce-timeout");
+ const debounceSoonProxySymbol = Symbol("debounce-soon-proxy");
+
+ Object.defineProperty(Object.prototype, "soon",
+ {
+ get: function()
+ {
+ if (!this[debounceSoonProxySymbol])
+ this[debounceSoonProxySymbol] = this.debounce(0);
+ return this[debounceSoonProxySymbol];
+ }
+ });
+
+ Object.defineProperty(Object.prototype, "debounce",
+ {
+ value: function(delay)
+ {
+ console.assert(delay >= 0);
+
+ return new Proxy(this, {
+ get(target, property, receiver) {
+ return (...args) => {
+ let original = target[property];
+ console.assert(typeof original === "function");
+
+ if (original[debounceTimeoutSymbol])
+ clearTimeout(original[debounceTimeoutSymbol]);
+
+ let performWork = () => {
+ original[debounceTimeoutSymbol] = undefined;
+ original.apply(target, args);
+ };
+
+ original[debounceTimeoutSymbol] = setTimeout(performWork, delay);
+ };
+ }
+ });
+ }
+ });
+
+ Object.defineProperty(Function.prototype, "cancelDebounce",
+ {
+ value: function()
+ {
+ if (!this[debounceTimeoutSymbol])
+ return;
+
+ clearTimeout(this[debounceTimeoutSymbol]);
+ this[debounceTimeoutSymbol] = undefined;
+ }
+ });
+
+ const requestAnimationFrameSymbol = Symbol("peform-on-animation-frame");
+ const requestAnimationFrameProxySymbol = Symbol("perform-on-animation-frame-proxy");
+
+ Object.defineProperty(Object.prototype, "onNextFrame",
+ {
+ get: function()
+ {
+ if (!this[requestAnimationFrameProxySymbol]) {
+ this[requestAnimationFrameProxySymbol] = new Proxy(this, {
+ get(target, property, receiver) {
+ return (...args) => {
+ let original = target[property];
+ console.assert(typeof original === "function");
+
+ if (original[requestAnimationFrameSymbol])
+ return;
+
+ let performWork = () => {
+ original[requestAnimationFrameSymbol] = undefined;
+ original.apply(target, args);
+ };
+
+ original[requestAnimationFrameSymbol] = requestAnimationFrame(performWork);
+ };
+ }
+ });
+ }
+
+ return this[requestAnimationFrameProxySymbol];
+ }
+ });
+})();
+
+function appendWebInspectorSourceURL(string)
+{
+ if (string.includes("//# sourceURL"))
+ return string;
+ return "\n//# sourceURL=__WebInspectorInternal__\n" + string;
+}
+
+function appendWebInspectorConsoleEvaluationSourceURL(string)
+{
+ if (string.includes("//# sourceURL"))
+ return string;
+ return "\n//# sourceURL=__WebInspectorConsoleEvaluation__\n" + string;
+}
+
+function isWebInspectorInternalScript(url)
+{
+ return url === "__WebInspectorInternal__";
+}
+
+function isWebInspectorConsoleEvaluationScript(url)
+{
+ return url === "__WebInspectorConsoleEvaluation__";
+}
+
+function isWebKitInjectedScript(url)
+{
+ return url && url.startsWith("__InjectedScript_") && url.endsWith(".js");
+}
+
+function isWebKitInternalScript(url)
+{
+ if (isWebInspectorConsoleEvaluationScript(url))
+ return false;
+
+ if (isWebKitInjectedScript(url))
+ return true;
+
+ return url && url.startsWith("__Web") && url.endsWith("__");
+}
+
+function isFunctionStringNativeCode(str)
+{
+ return str.endsWith("{\n [native code]\n}");
+}
+
+function isTextLikelyMinified(content)
+{
+ const autoFormatMaxCharactersToCheck = 5000;
+ const autoFormatWhitespaceRatio = 0.2;
+
+ let whitespaceScore = 0;
+ let size = Math.min(autoFormatMaxCharactersToCheck, content.length);
+
+ for (let i = 0; i < size; i++) {
+ let char = content[i];
+
+ if (char === " ")
+ whitespaceScore++;
+ else if (char === "\t")
+ whitespaceScore += 4;
+ else if (char === "\n")
+ whitespaceScore += 8;
+ }
+
+ let ratio = whitespaceScore / size;
+ return ratio < autoFormatWhitespaceRatio;
+}
+
+function doubleQuotedString(str)
+{
+ return "\"" + str.replace(/\\/g, "\\\\").replace(/"/g, "\\\"") + "\"";
+}
+
+function insertionIndexForObjectInListSortedByFunction(object, list, comparator, insertionIndexAfter)
+{
+ if (insertionIndexAfter) {
+ return list.upperBound(object, comparator);
+ } else {
+ return list.lowerBound(object, comparator);
+ }
+}
+
+function insertObjectIntoSortedArray(object, array, comparator)
+{
+ array.splice(insertionIndexForObjectInListSortedByFunction(object, array, comparator), 0, object);
+}
+
+function decodeBase64ToBlob(base64Data, mimeType)
+{
+ mimeType = mimeType || '';
+
+ const sliceSize = 1024;
+ var byteCharacters = atob(base64Data);
+ var bytesLength = byteCharacters.length;
+ var slicesCount = Math.ceil(bytesLength / sliceSize);
+ var byteArrays = new Array(slicesCount);
+
+ for (var sliceIndex = 0; sliceIndex < slicesCount; ++sliceIndex) {
+ var begin = sliceIndex * sliceSize;
+ var end = Math.min(begin + sliceSize, bytesLength);
+
+ var bytes = new Array(end - begin);
+ for (var 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});
+}
+
+// FIXME: This can be removed when WEB_TIMING is enabled for all platforms.
+function timestamp()
+{
+ return window.performance ? performance.now() : Date.now();
+}
+
+if (!window.handlePromiseException) {
+ window.handlePromiseException = function handlePromiseException(error)
+ {
+ console.error("Uncaught exception in Promise", error);
+ };
+}
diff --git a/Source/WebInspectorUI/UserInterface/Base/WebInspector.js b/Source/WebInspectorUI/UserInterface/Base/WebInspector.js
new file mode 100644
index 000000000..15460bb44
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Base/WebInspector.js
@@ -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 WebInspector = {}; // Namespace
diff --git a/Source/WebInspectorUI/UserInterface/Base/YieldableTask.js b/Source/WebInspectorUI/UserInterface/Base/YieldableTask.js
new file mode 100644
index 000000000..e7ceb5119
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Base/YieldableTask.js
@@ -0,0 +1,156 @@
+/*
+ * 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.
+ */
+
+WebInspector.YieldableTask = class YieldableTask extends WebInspector.Object
+{
+ constructor(delegate, items, options={})
+ {
+ super();
+
+ 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);
+ }
+};
+