summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/inspector/front-end/inspector.js
diff options
context:
space:
mode:
authorSteve Block <steveblock@google.com>2011-05-06 11:45:16 +0100
committerSteve Block <steveblock@google.com>2011-05-12 13:44:10 +0100
commitcad810f21b803229eb11403f9209855525a25d57 (patch)
tree29a6fd0279be608e0fe9ffe9841f722f0f4e4269 /Source/WebCore/inspector/front-end/inspector.js
parent121b0cf4517156d0ac5111caf9830c51b69bae8f (diff)
downloadexternal_webkit-cad810f21b803229eb11403f9209855525a25d57.zip
external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.gz
external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.bz2
Merge WebKit at r75315: Initial merge by git.
Change-Id: I570314b346ce101c935ed22a626b48c2af266b84
Diffstat (limited to 'Source/WebCore/inspector/front-end/inspector.js')
-rw-r--r--Source/WebCore/inspector/front-end/inspector.js1972
1 files changed, 1972 insertions, 0 deletions
diff --git a/Source/WebCore/inspector/front-end/inspector.js b/Source/WebCore/inspector/front-end/inspector.js
new file mode 100644
index 0000000..33a75d7
--- /dev/null
+++ b/Source/WebCore/inspector/front-end/inspector.js
@@ -0,0 +1,1972 @@
+/*
+ * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2007 Matt Lilek (pewtermoose@gmail.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 Computer, 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.
+ */
+
+// Keep this ; so that concatenated version of the script worked.
+;(function preloadImages()
+{
+ (new Image()).src = "Images/clearConsoleButtonGlyph.png";
+ (new Image()).src = "Images/consoleButtonGlyph.png";
+ (new Image()).src = "Images/dockButtonGlyph.png";
+ (new Image()).src = "Images/enableOutlineButtonGlyph.png";
+ (new Image()).src = "Images/enableSolidButtonGlyph.png";
+ (new Image()).src = "Images/excludeButtonGlyph.png";
+ (new Image()).src = "Images/focusButtonGlyph.png";
+ (new Image()).src = "Images/largerResourcesButtonGlyph.png";
+ (new Image()).src = "Images/nodeSearchButtonGlyph.png";
+ (new Image()).src = "Images/pauseOnExceptionButtonGlyph.png";
+ (new Image()).src = "Images/percentButtonGlyph.png";
+ (new Image()).src = "Images/recordButtonGlyph.png";
+ (new Image()).src = "Images/recordToggledButtonGlyph.png";
+ (new Image()).src = "Images/reloadButtonGlyph.png";
+ (new Image()).src = "Images/undockButtonGlyph.png";
+})();
+
+var WebInspector = {
+ resources: {},
+ missingLocalizedStrings: {},
+ pendingDispatches: 0,
+
+ get platform()
+ {
+ if (!("_platform" in this))
+ this._platform = InspectorFrontendHost.platform();
+
+ return this._platform;
+ },
+
+ get platformFlavor()
+ {
+ if (!("_platformFlavor" in this))
+ this._platformFlavor = this._detectPlatformFlavor();
+
+ return this._platformFlavor;
+ },
+
+ _detectPlatformFlavor: function()
+ {
+ const userAgent = navigator.userAgent;
+
+ if (this.platform === "windows") {
+ var match = userAgent.match(/Windows NT (\d+)\.(?:\d+)/);
+ if (match && match[1] >= 6)
+ return WebInspector.PlatformFlavor.WindowsVista;
+ return null;
+ } else if (this.platform === "mac") {
+ var match = userAgent.match(/Mac OS X\s*(?:(\d+)_(\d+))?/);
+ if (!match || match[1] != 10)
+ return WebInspector.PlatformFlavor.MacSnowLeopard;
+ switch (Number(match[2])) {
+ case 4:
+ return WebInspector.PlatformFlavor.MacTiger;
+ case 5:
+ return WebInspector.PlatformFlavor.MacLeopard;
+ case 6:
+ default:
+ return WebInspector.PlatformFlavor.MacSnowLeopard;
+ }
+ }
+
+ return null;
+ },
+
+ get port()
+ {
+ if (!("_port" in this))
+ this._port = InspectorFrontendHost.port();
+
+ return this._port;
+ },
+
+ get previousFocusElement()
+ {
+ return this._previousFocusElement;
+ },
+
+ get currentFocusElement()
+ {
+ return this._currentFocusElement;
+ },
+
+ set currentFocusElement(x)
+ {
+ if (this._currentFocusElement !== x)
+ this._previousFocusElement = this._currentFocusElement;
+ this._currentFocusElement = x;
+
+ if (this._currentFocusElement) {
+ this._currentFocusElement.focus();
+
+ // Make a caret selection inside the new element if there isn't a range selection and
+ // there isn't already a caret selection inside.
+ var selection = window.getSelection();
+ if (selection.isCollapsed && !this._currentFocusElement.isInsertionCaretInside()) {
+ var selectionRange = this._currentFocusElement.ownerDocument.createRange();
+ selectionRange.setStart(this._currentFocusElement, 0);
+ selectionRange.setEnd(this._currentFocusElement, 0);
+
+ selection.removeAllRanges();
+ selection.addRange(selectionRange);
+ }
+ } else if (this._previousFocusElement)
+ this._previousFocusElement.blur();
+ },
+
+ get currentPanel()
+ {
+ return this._currentPanel;
+ },
+
+ set currentPanel(x)
+ {
+ if (this._currentPanel === x)
+ return;
+
+ if (this._currentPanel)
+ this._currentPanel.hide();
+
+ this._currentPanel = x;
+
+ this.updateSearchLabel();
+
+ if (x) {
+ x.show();
+
+ if (this.currentQuery) {
+ if (x.performSearch) {
+ function performPanelSearch()
+ {
+ this.updateSearchMatchesCount();
+
+ x.currentQuery = this.currentQuery;
+ x.performSearch(this.currentQuery);
+ }
+
+ // Perform the search on a timeout so the panel switches fast.
+ setTimeout(performPanelSearch.bind(this), 0);
+ } else {
+ // Update to show Not found for panels that can't be searched.
+ this.updateSearchMatchesCount();
+ }
+ }
+ }
+
+ for (var panelName in WebInspector.panels) {
+ if (WebInspector.panels[panelName] === x) {
+ WebInspector.settings.lastActivePanel = panelName;
+ this._panelHistory.setPanel(panelName);
+ }
+ }
+ },
+
+ createJSBreakpointsSidebarPane: function()
+ {
+ var pane = new WebInspector.BreakpointsSidebarPane(WebInspector.UIString("Breakpoints"));
+ function breakpointAdded(event)
+ {
+ pane.addBreakpointItem(new WebInspector.BreakpointItem(event.data));
+ }
+ WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.BreakpointAdded, breakpointAdded);
+ return pane;
+ },
+
+ createDOMBreakpointsSidebarPane: function()
+ {
+ var pane = new WebInspector.BreakpointsSidebarPane(WebInspector.UIString("DOM Breakpoints"));
+ function breakpointAdded(event)
+ {
+ pane.addBreakpointItem(new WebInspector.BreakpointItem(event.data));
+ }
+ WebInspector.breakpointManager.addEventListener(WebInspector.BreakpointManager.Events.DOMBreakpointAdded, breakpointAdded);
+ return pane;
+ },
+
+ createXHRBreakpointsSidebarPane: function()
+ {
+ var pane = new WebInspector.XHRBreakpointsSidebarPane();
+ function breakpointAdded(event)
+ {
+ pane.addBreakpointItem(new WebInspector.BreakpointItem(event.data));
+ }
+ WebInspector.breakpointManager.addEventListener(WebInspector.BreakpointManager.Events.XHRBreakpointAdded, breakpointAdded);
+ return pane;
+ },
+
+ _createPanels: function()
+ {
+ var hiddenPanels = (InspectorFrontendHost.hiddenPanels() || "").split(',');
+ if (hiddenPanels.indexOf("elements") === -1)
+ this.panels.elements = new WebInspector.ElementsPanel();
+ if (hiddenPanels.indexOf("resources") === -1)
+ this.panels.resources = new WebInspector.ResourcesPanel();
+ if (hiddenPanels.indexOf("network") === -1)
+ this.panels.network = new WebInspector.NetworkPanel();
+ if (hiddenPanels.indexOf("scripts") === -1)
+ this.panels.scripts = new WebInspector.ScriptsPanel();
+ if (hiddenPanels.indexOf("timeline") === -1)
+ this.panels.timeline = new WebInspector.TimelinePanel();
+ if (hiddenPanels.indexOf("profiles") === -1) {
+ this.panels.profiles = new WebInspector.ProfilesPanel();
+ this.panels.profiles.registerProfileType(new WebInspector.CPUProfileType());
+ if (Preferences.heapProfilerPresent)
+ this.panels.profiles.registerProfileType(new WebInspector.HeapSnapshotProfileType());
+ }
+ if (hiddenPanels.indexOf("audits") === -1)
+ this.panels.audits = new WebInspector.AuditsPanel();
+ if (hiddenPanels.indexOf("console") === -1)
+ this.panels.console = new WebInspector.ConsolePanel();
+ },
+
+ get attached()
+ {
+ return this._attached;
+ },
+
+ set attached(x)
+ {
+ if (this._attached === x)
+ return;
+
+ this._attached = x;
+
+ this.updateSearchLabel();
+
+ var dockToggleButton = document.getElementById("dock-status-bar-item");
+ var body = document.body;
+
+ if (x) {
+ body.removeStyleClass("detached");
+ body.addStyleClass("attached");
+ dockToggleButton.title = WebInspector.UIString("Undock into separate window.");
+ } else {
+ body.removeStyleClass("attached");
+ body.addStyleClass("detached");
+ dockToggleButton.title = WebInspector.UIString("Dock to main window.");
+ }
+ if (this.drawer)
+ this.drawer.resize();
+ },
+
+ get errors()
+ {
+ return this._errors || 0;
+ },
+
+ set errors(x)
+ {
+ x = Math.max(x, 0);
+
+ if (this._errors === x)
+ return;
+ this._errors = x;
+ this._updateErrorAndWarningCounts();
+ },
+
+ get warnings()
+ {
+ return this._warnings || 0;
+ },
+
+ set warnings(x)
+ {
+ x = Math.max(x, 0);
+
+ if (this._warnings === x)
+ return;
+ this._warnings = x;
+ this._updateErrorAndWarningCounts();
+ },
+
+ _updateErrorAndWarningCounts: function()
+ {
+ var errorWarningElement = document.getElementById("error-warning-count");
+ if (!errorWarningElement)
+ return;
+
+ if (!this.errors && !this.warnings) {
+ errorWarningElement.addStyleClass("hidden");
+ return;
+ }
+
+ errorWarningElement.removeStyleClass("hidden");
+
+ errorWarningElement.removeChildren();
+
+ if (this.errors) {
+ var errorElement = document.createElement("span");
+ errorElement.id = "error-count";
+ errorElement.textContent = this.errors;
+ errorWarningElement.appendChild(errorElement);
+ }
+
+ if (this.warnings) {
+ var warningsElement = document.createElement("span");
+ warningsElement.id = "warning-count";
+ warningsElement.textContent = this.warnings;
+ errorWarningElement.appendChild(warningsElement);
+ }
+
+ if (this.errors) {
+ if (this.warnings) {
+ if (this.errors == 1) {
+ if (this.warnings == 1)
+ errorWarningElement.title = WebInspector.UIString("%d error, %d warning", this.errors, this.warnings);
+ else
+ errorWarningElement.title = WebInspector.UIString("%d error, %d warnings", this.errors, this.warnings);
+ } else if (this.warnings == 1)
+ errorWarningElement.title = WebInspector.UIString("%d errors, %d warning", this.errors, this.warnings);
+ else
+ errorWarningElement.title = WebInspector.UIString("%d errors, %d warnings", this.errors, this.warnings);
+ } else if (this.errors == 1)
+ errorWarningElement.title = WebInspector.UIString("%d error", this.errors);
+ else
+ errorWarningElement.title = WebInspector.UIString("%d errors", this.errors);
+ } else if (this.warnings == 1)
+ errorWarningElement.title = WebInspector.UIString("%d warning", this.warnings);
+ else if (this.warnings)
+ errorWarningElement.title = WebInspector.UIString("%d warnings", this.warnings);
+ else
+ errorWarningElement.title = null;
+ },
+
+ get styleChanges()
+ {
+ return this._styleChanges;
+ },
+
+ set styleChanges(x)
+ {
+ x = Math.max(x, 0);
+
+ if (this._styleChanges === x)
+ return;
+ this._styleChanges = x;
+ this._updateChangesCount();
+ },
+
+ _updateChangesCount: function()
+ {
+ // TODO: Remove immediate return when enabling the Changes Panel
+ return;
+
+ var changesElement = document.getElementById("changes-count");
+ if (!changesElement)
+ return;
+
+ if (!this.styleChanges) {
+ changesElement.addStyleClass("hidden");
+ return;
+ }
+
+ changesElement.removeStyleClass("hidden");
+ changesElement.removeChildren();
+
+ if (this.styleChanges) {
+ var styleChangesElement = document.createElement("span");
+ styleChangesElement.id = "style-changes-count";
+ styleChangesElement.textContent = this.styleChanges;
+ changesElement.appendChild(styleChangesElement);
+ }
+
+ if (this.styleChanges) {
+ if (this.styleChanges === 1)
+ changesElement.title = WebInspector.UIString("%d style change", this.styleChanges);
+ else
+ changesElement.title = WebInspector.UIString("%d style changes", this.styleChanges);
+ }
+ },
+
+ highlightDOMNode: function(nodeId)
+ {
+ if ("_hideDOMNodeHighlightTimeout" in this) {
+ clearTimeout(this._hideDOMNodeHighlightTimeout);
+ delete this._hideDOMNodeHighlightTimeout;
+ }
+
+ if (this._highlightedDOMNodeId === nodeId)
+ return;
+
+ this._highlightedDOMNodeId = nodeId;
+ if (nodeId)
+ InspectorBackend.highlightDOMNode(nodeId);
+ else
+ InspectorBackend.hideDOMNodeHighlight();
+ },
+
+ highlightDOMNodeForTwoSeconds: function(nodeId)
+ {
+ this.highlightDOMNode(nodeId);
+ this._hideDOMNodeHighlightTimeout = setTimeout(this.highlightDOMNode.bind(this, 0), 2000);
+ },
+
+ wireElementWithDOMNode: function(element, nodeId)
+ {
+ element.addEventListener("click", this._updateFocusedNode.bind(this, nodeId), false);
+ element.addEventListener("mouseover", this.highlightDOMNode.bind(this, nodeId), false);
+ element.addEventListener("mouseout", this.highlightDOMNode.bind(this, 0), false);
+ },
+
+ _updateFocusedNode: function(nodeId)
+ {
+ this.currentPanel = this.panels.elements;
+ this.panels.elements.updateFocusedNode(nodeId);
+ },
+
+ get networkResources()
+ {
+ return this.panels.network.resources;
+ },
+
+ forAllResources: function(callback)
+ {
+ WebInspector.resourceTreeModel.forAllResources(callback);
+ },
+
+ resourceForURL: function(url)
+ {
+ return this.resourceTreeModel.resourceForURL(url);
+ }
+}
+
+WebInspector.PlatformFlavor = {
+ WindowsVista: "windows-vista",
+ MacTiger: "mac-tiger",
+ MacLeopard: "mac-leopard",
+ MacSnowLeopard: "mac-snowleopard"
+};
+
+(function parseQueryParameters()
+{
+ WebInspector.queryParamsObject = {};
+ var queryParams = window.location.search;
+ if (!queryParams)
+ return;
+ var params = queryParams.substring(1).split("&");
+ for (var i = 0; i < params.length; ++i) {
+ var pair = params[i].split("=");
+ WebInspector.queryParamsObject[pair[0]] = pair[1];
+ }
+})();
+
+WebInspector.loaded = function()
+{
+ if ("page" in WebInspector.queryParamsObject) {
+ WebInspector.socket = new WebSocket("ws://" + window.location.host + "/devtools/page/" + WebInspector.queryParamsObject.page);
+ WebInspector.socket.onmessage = function(message) { InspectorBackend.dispatch(message.data); }
+ WebInspector.socket.onerror = function(error) { console.error(error); }
+ WebInspector.socket.onopen = function() {
+ InspectorFrontendHost.sendMessageToBackend = WebInspector.socket.send.bind(WebInspector.socket);
+ InspectorFrontendHost.loaded = WebInspector.socket.send.bind(WebInspector.socket, "loaded");
+ WebInspector.doLoadedDone();
+ }
+ return;
+ }
+ WebInspector.doLoadedDone();
+}
+
+WebInspector.doLoadedDone = function()
+{
+ InspectorBackend.setInjectedScriptSource("(" + injectedScriptConstructor + ");");
+
+ var platform = WebInspector.platform;
+ document.body.addStyleClass("platform-" + platform);
+ var flavor = WebInspector.platformFlavor;
+ if (flavor)
+ document.body.addStyleClass("platform-" + flavor);
+ var port = WebInspector.port;
+ document.body.addStyleClass("port-" + port);
+
+ InspectorFrontendHost.loaded();
+ WebInspector.settings = new WebInspector.Settings();
+
+ this._registerShortcuts();
+
+ // set order of some sections explicitly
+ WebInspector.shortcutsHelp.section(WebInspector.UIString("Console"));
+ WebInspector.shortcutsHelp.section(WebInspector.UIString("Elements Panel"));
+
+ this.drawer = new WebInspector.Drawer();
+ this.console = new WebInspector.ConsoleView(this.drawer);
+ // TODO: Uncomment when enabling the Changes Panel
+ // this.changes = new WebInspector.ChangesView(this.drawer);
+ // TODO: Remove class="hidden" from inspector.html on button#changes-status-bar-item
+ this.drawer.visibleView = this.console;
+ this.resourceTreeModel = new WebInspector.ResourceTreeModel();
+ this.networkManager = new WebInspector.NetworkManager(this.resourceTreeModel);
+ this.domAgent = new WebInspector.DOMAgent();
+
+ InspectorBackend.registerDomainDispatcher("Inspector", this);
+
+ this.resourceCategories = {
+ documents: new WebInspector.ResourceCategory("documents", WebInspector.UIString("Documents"), "rgb(47,102,236)"),
+ stylesheets: new WebInspector.ResourceCategory("stylesheets", WebInspector.UIString("Stylesheets"), "rgb(157,231,119)"),
+ images: new WebInspector.ResourceCategory("images", WebInspector.UIString("Images"), "rgb(164,60,255)"),
+ scripts: new WebInspector.ResourceCategory("scripts", WebInspector.UIString("Scripts"), "rgb(255,121,0)"),
+ xhr: new WebInspector.ResourceCategory("xhr", WebInspector.UIString("XHR"), "rgb(231,231,10)"),
+ fonts: new WebInspector.ResourceCategory("fonts", WebInspector.UIString("Fonts"), "rgb(255,82,62)"),
+ websockets: new WebInspector.ResourceCategory("websockets", WebInspector.UIString("WebSocket"), "rgb(186,186,186)"), // FIXME: Decide the color.
+ other: new WebInspector.ResourceCategory("other", WebInspector.UIString("Other"), "rgb(186,186,186)")
+ };
+
+ this.cssModel = new WebInspector.CSSStyleModel();
+ this.debuggerModel = new WebInspector.DebuggerModel();
+
+ this.breakpointManager = new WebInspector.BreakpointManager();
+
+ this.panels = {};
+ this._createPanels();
+ this._panelHistory = new WebInspector.PanelHistory();
+
+ var toolbarElement = document.getElementById("toolbar");
+ var previousToolbarItem = toolbarElement.children[0];
+
+ this.panelOrder = [];
+ for (var panelName in this.panels)
+ previousToolbarItem = WebInspector.addPanelToolbarIcon(toolbarElement, this.panels[panelName], previousToolbarItem);
+
+ this.Tips = {
+ ResourceNotCompressed: {id: 0, message: WebInspector.UIString("You could save bandwidth by having your web server compress this transfer with gzip or zlib.")}
+ };
+
+ this.Warnings = {
+ IncorrectMIMEType: {id: 0, message: WebInspector.UIString("Resource interpreted as %s but transferred with MIME type %s.")}
+ };
+
+ this.addMainEventListeners(document);
+
+ window.addEventListener("resize", this.windowResize.bind(this), true);
+
+ document.addEventListener("focus", this.focusChanged.bind(this), true);
+ document.addEventListener("keydown", this.documentKeyDown.bind(this), false);
+ document.addEventListener("beforecopy", this.documentCanCopy.bind(this), true);
+ document.addEventListener("copy", this.documentCopy.bind(this), true);
+ document.addEventListener("contextmenu", this.contextMenuEventFired.bind(this), true);
+
+ var dockToggleButton = document.getElementById("dock-status-bar-item");
+ dockToggleButton.addEventListener("click", this.toggleAttach.bind(this), false);
+
+ if (this.attached)
+ dockToggleButton.title = WebInspector.UIString("Undock into separate window.");
+ else
+ dockToggleButton.title = WebInspector.UIString("Dock to main window.");
+
+ var errorWarningCount = document.getElementById("error-warning-count");
+ errorWarningCount.addEventListener("click", this.showConsole.bind(this), false);
+ this._updateErrorAndWarningCounts();
+
+ this.styleChanges = 0;
+ // TODO: Uncomment when enabling the Changes Panel
+ // var changesElement = document.getElementById("changes-count");
+ // changesElement.addEventListener("click", this.showChanges.bind(this), false);
+ // this._updateErrorAndWarningCounts();
+
+ var searchField = document.getElementById("search");
+ searchField.addEventListener("search", this.performSearch.bind(this), false); // when the search is emptied
+ searchField.addEventListener("mousedown", this._searchFieldManualFocus.bind(this), false); // when the search field is manually selected
+ searchField.addEventListener("keydown", this._searchKeyDown.bind(this), true);
+
+ toolbarElement.addEventListener("mousedown", this.toolbarDragStart, true);
+ document.getElementById("close-button-left").addEventListener("click", this.close, true);
+ document.getElementById("close-button-right").addEventListener("click", this.close, true);
+
+ this.extensionServer.initExtensions();
+
+ function populateInspectorState(inspectorState)
+ {
+ WebInspector.monitoringXHREnabled = inspectorState.monitoringXHREnabled;
+ if ("pauseOnExceptionsState" in inspectorState)
+ WebInspector.panels.scripts.updatePauseOnExceptionsState(inspectorState.pauseOnExceptionsState);
+ }
+ InspectorBackend.getInspectorState(populateInspectorState);
+
+ function onPopulateScriptObjects()
+ {
+ if (!WebInspector.currentPanel)
+ WebInspector.showPanel(WebInspector.settings.lastActivePanel);
+ }
+ InspectorBackend.populateScriptObjects(onPopulateScriptObjects);
+
+ InspectorBackend.setConsoleMessagesEnabled(true);
+
+ function propertyNamesCallback(names)
+ {
+ WebInspector.cssNameCompletions = new WebInspector.CSSCompletions(names);
+ }
+
+ // As a DOMAgent method, this needs to happen after the frontend has loaded and the agent is available.
+ InspectorBackend.getSupportedCSSProperties(propertyNamesCallback);
+}
+
+WebInspector.addPanelToolbarIcon = function(toolbarElement, panel, previousToolbarItem)
+{
+ var panelToolbarItem = panel.toolbarItem;
+ this.panelOrder.push(panel);
+ panelToolbarItem.addEventListener("click", this._toolbarItemClicked.bind(this));
+ if (previousToolbarItem)
+ toolbarElement.insertBefore(panelToolbarItem, previousToolbarItem.nextSibling);
+ else
+ toolbarElement.insertBefore(panelToolbarItem, toolbarElement.firstChild);
+ return panelToolbarItem;
+}
+
+var windowLoaded = function()
+{
+ var localizedStringsURL = InspectorFrontendHost.localizedStringsURL();
+ if (localizedStringsURL) {
+ var localizedStringsScriptElement = document.createElement("script");
+ localizedStringsScriptElement.addEventListener("load", WebInspector.loaded.bind(WebInspector), false);
+ localizedStringsScriptElement.type = "text/javascript";
+ localizedStringsScriptElement.src = localizedStringsURL;
+ document.head.appendChild(localizedStringsScriptElement);
+ } else
+ WebInspector.loaded();
+
+ window.removeEventListener("DOMContentLoaded", windowLoaded, false);
+ delete windowLoaded;
+};
+
+window.addEventListener("DOMContentLoaded", windowLoaded, false);
+
+WebInspector.dispatch = function(message) {
+ // We'd like to enforce asynchronous interaction between the inspector controller and the frontend.
+ // This is important to LayoutTests.
+ function delayDispatch()
+ {
+ InspectorBackend.dispatch(message);
+ WebInspector.pendingDispatches--;
+ }
+ WebInspector.pendingDispatches++;
+ setTimeout(delayDispatch, 0);
+}
+
+WebInspector.dispatchMessageFromBackend = function(messageObject)
+{
+ WebInspector.dispatch(messageObject);
+}
+
+WebInspector.windowResize = function(event)
+{
+ if (this.currentPanel)
+ this.currentPanel.resize();
+ this.drawer.resize();
+}
+
+WebInspector.windowFocused = function(event)
+{
+ // Fires after blur, so when focusing on either the main inspector
+ // or an <iframe> within the inspector we should always remove the
+ // "inactive" class.
+ if (event.target.document.nodeType === Node.DOCUMENT_NODE)
+ document.body.removeStyleClass("inactive");
+}
+
+WebInspector.windowBlurred = function(event)
+{
+ // Leaving the main inspector or an <iframe> within the inspector.
+ // We can add "inactive" now, and if we are moving the focus to another
+ // part of the inspector then windowFocused will correct this.
+ if (event.target.document.nodeType === Node.DOCUMENT_NODE)
+ document.body.addStyleClass("inactive");
+}
+
+WebInspector.focusChanged = function(event)
+{
+ this.currentFocusElement = event.target;
+}
+
+WebInspector.setAttachedWindow = function(attached)
+{
+ this.attached = attached;
+}
+
+WebInspector.close = function(event)
+{
+ if (this._isClosing)
+ return;
+ this._isClosing = true;
+ InspectorFrontendHost.closeWindow();
+}
+
+WebInspector.disconnectFromBackend = function()
+{
+ InspectorFrontendHost.disconnectFromBackend();
+}
+
+WebInspector.documentClick = function(event)
+{
+ var anchor = event.target.enclosingNodeOrSelfWithNodeName("a");
+ if (!anchor || anchor.target === "_blank")
+ return;
+
+ // Prevent the link from navigating, since we don't do any navigation by following links normally.
+ event.preventDefault();
+ event.stopPropagation();
+
+ function followLink()
+ {
+ // FIXME: support webkit-html-external-link links here.
+ if (WebInspector.canShowSourceLine(anchor.href, anchor.getAttribute("line_number"), anchor.getAttribute("preferred_panel"))) {
+ if (anchor.hasStyleClass("webkit-html-external-link")) {
+ anchor.removeStyleClass("webkit-html-external-link");
+ anchor.addStyleClass("webkit-html-resource-link");
+ }
+
+ WebInspector.showSourceLine(anchor.href, anchor.getAttribute("line_number"), anchor.getAttribute("preferred_panel"));
+ return;
+ }
+
+ const profileMatch = WebInspector.ProfileType.URLRegExp.exec(anchor.href);
+ if (profileMatch) {
+ WebInspector.showProfileForURL(anchor.href);
+ return;
+ }
+
+ var parsedURL = anchor.href.asParsedURL();
+ if (parsedURL && parsedURL.scheme === "webkit-link-action") {
+ if (parsedURL.host === "show-panel") {
+ var panel = parsedURL.path.substring(1);
+ if (WebInspector.panels[panel])
+ WebInspector.showPanel(panel);
+ }
+ return;
+ }
+
+ WebInspector.showPanel("resources");
+ }
+
+ if (WebInspector.followLinkTimeout)
+ clearTimeout(WebInspector.followLinkTimeout);
+
+ if (anchor.preventFollowOnDoubleClick) {
+ // Start a timeout if this is the first click, if the timeout is canceled
+ // before it fires, then a double clicked happened or another link was clicked.
+ if (event.detail === 1)
+ WebInspector.followLinkTimeout = setTimeout(followLink, 333);
+ return;
+ }
+
+ followLink();
+}
+
+WebInspector.openResource = function(resourceURL, inResourcesPanel)
+{
+ var resource = WebInspector.resourceForURL(resourceURL);
+ if (inResourcesPanel && resource) {
+ WebInspector.panels.resources.showResource(resource);
+ WebInspector.showPanel("resources");
+ } else
+ InspectorBackend.openInInspectedWindow(resource ? resource.url : resourceURL);
+}
+
+WebInspector._registerShortcuts = function()
+{
+ var shortcut = WebInspector.KeyboardShortcut;
+ var section = WebInspector.shortcutsHelp.section(WebInspector.UIString("All Panels"));
+ var keys = [
+ shortcut.shortcutToString("]", shortcut.Modifiers.CtrlOrMeta),
+ shortcut.shortcutToString("[", shortcut.Modifiers.CtrlOrMeta)
+ ];
+ section.addRelatedKeys(keys, WebInspector.UIString("Next/previous panel"));
+ section.addKey(shortcut.shortcutToString(shortcut.Keys.Esc), WebInspector.UIString("Toggle console"));
+ section.addKey(shortcut.shortcutToString("f", shortcut.Modifiers.CtrlOrMeta), WebInspector.UIString("Search"));
+ if (WebInspector.isMac()) {
+ keys = [
+ shortcut.shortcutToString("g", shortcut.Modifiers.Meta),
+ shortcut.shortcutToString("g", shortcut.Modifiers.Meta | shortcut.Modifiers.Shift)
+ ];
+ section.addRelatedKeys(keys, WebInspector.UIString("Find next/previous"));
+ }
+}
+
+WebInspector.documentKeyDown = function(event)
+{
+ var isInputElement = event.target.nodeName === "INPUT";
+ var isInEditMode = event.target.enclosingNodeOrSelfWithClass("text-prompt") || WebInspector.isEditingAnyField();
+ const helpKey = WebInspector.isMac() ? "U+003F" : "U+00BF"; // "?" for both platforms
+
+ if (event.keyIdentifier === "F1" ||
+ (event.keyIdentifier === helpKey && event.shiftKey && (!isInEditMode && !isInputElement || event.metaKey))) {
+ WebInspector.shortcutsHelp.show();
+ event.stopPropagation();
+ event.preventDefault();
+ return;
+ }
+
+ if (WebInspector.isEditingAnyField())
+ return;
+
+ if (this.currentFocusElement && this.currentFocusElement.handleKeyEvent) {
+ this.currentFocusElement.handleKeyEvent(event);
+ if (event.handled) {
+ event.preventDefault();
+ return;
+ }
+ }
+
+ if (this.currentPanel && this.currentPanel.handleShortcut) {
+ this.currentPanel.handleShortcut(event);
+ if (event.handled) {
+ event.preventDefault();
+ return;
+ }
+ }
+
+ var isMac = WebInspector.isMac();
+ switch (event.keyIdentifier) {
+ case "Left":
+ var isBackKey = !isInEditMode && (isMac ? event.metaKey : event.ctrlKey);
+ if (isBackKey && this._panelHistory.canGoBack()) {
+ this._panelHistory.goBack();
+ event.preventDefault();
+ }
+ break;
+
+ case "Right":
+ var isForwardKey = !isInEditMode && (isMac ? event.metaKey : event.ctrlKey);
+ if (isForwardKey && this._panelHistory.canGoForward()) {
+ this._panelHistory.goForward();
+ event.preventDefault();
+ }
+ break;
+
+ case "U+001B": // Escape key
+ event.preventDefault();
+ if (this.drawer.fullPanel)
+ return;
+
+ this.drawer.visible = !this.drawer.visible;
+ break;
+
+ case "U+0046": // F key
+ if (isMac)
+ var isFindKey = event.metaKey && !event.ctrlKey && !event.altKey && !event.shiftKey;
+ else
+ var isFindKey = event.ctrlKey && !event.metaKey && !event.altKey && !event.shiftKey;
+
+ if (isFindKey) {
+ WebInspector.focusSearchField();
+ event.preventDefault();
+ }
+ break;
+
+ case "F3":
+ if (!isMac) {
+ WebInspector.focusSearchField();
+ event.preventDefault();
+ }
+ break;
+
+ case "U+0047": // G key
+ if (isMac && event.metaKey && !event.ctrlKey && !event.altKey) {
+ if (event.shiftKey) {
+ if (this.currentPanel.jumpToPreviousSearchResult)
+ this.currentPanel.jumpToPreviousSearchResult();
+ } else if (this.currentPanel.jumpToNextSearchResult)
+ this.currentPanel.jumpToNextSearchResult();
+ event.preventDefault();
+ }
+ break;
+
+ // Windows and Mac have two different definitions of [, so accept both.
+ case "U+005B":
+ case "U+00DB": // [ key
+ if (isMac)
+ var isRotateLeft = event.metaKey && !event.shiftKey && !event.ctrlKey && !event.altKey;
+ else
+ var isRotateLeft = event.ctrlKey && !event.shiftKey && !event.metaKey && !event.altKey;
+
+ if (isRotateLeft) {
+ var index = this.panelOrder.indexOf(this.currentPanel);
+ index = (index === 0) ? this.panelOrder.length - 1 : index - 1;
+ this.panelOrder[index].toolbarItem.click();
+ event.preventDefault();
+ }
+
+ break;
+
+ // Windows and Mac have two different definitions of ], so accept both.
+ case "U+005D":
+ case "U+00DD": // ] key
+ if (isMac)
+ var isRotateRight = event.metaKey && !event.shiftKey && !event.ctrlKey && !event.altKey;
+ else
+ var isRotateRight = event.ctrlKey && !event.shiftKey && !event.metaKey && !event.altKey;
+
+ if (isRotateRight) {
+ var index = this.panelOrder.indexOf(this.currentPanel);
+ index = (index + 1) % this.panelOrder.length;
+ this.panelOrder[index].toolbarItem.click();
+ event.preventDefault();
+ }
+
+ break;
+
+ case "U+0052": // R key
+ if ((event.metaKey && isMac) || (event.ctrlKey && !isMac)) {
+ InspectorBackend.reloadPage();
+ event.preventDefault();
+ }
+ break;
+ case "F5":
+ if (!isMac)
+ InspectorBackend.reloadPage();
+ break;
+ }
+}
+
+WebInspector.documentCanCopy = function(event)
+{
+ if (this.currentPanel && this.currentPanel.handleCopyEvent)
+ event.preventDefault();
+}
+
+WebInspector.documentCopy = function(event)
+{
+ if (this.currentPanel && this.currentPanel.handleCopyEvent)
+ this.currentPanel.handleCopyEvent(event);
+}
+
+WebInspector.contextMenuEventFired = function(event)
+{
+ if (event.handled || event.target.hasStyleClass("popup-glasspane"))
+ event.preventDefault();
+}
+
+WebInspector.animateStyle = function(animations, duration, callback)
+{
+ var interval;
+ var complete = 0;
+
+ const intervalDuration = (1000 / 30); // 30 frames per second.
+ const animationsLength = animations.length;
+ const propertyUnit = {opacity: ""};
+ const defaultUnit = "px";
+
+ function cubicInOut(t, b, c, d)
+ {
+ if ((t/=d/2) < 1) return c/2*t*t*t + b;
+ return c/2*((t-=2)*t*t + 2) + b;
+ }
+
+ // Pre-process animations.
+ for (var i = 0; i < animationsLength; ++i) {
+ var animation = animations[i];
+ var element = null, start = null, end = null, key = null;
+ for (key in animation) {
+ if (key === "element")
+ element = animation[key];
+ else if (key === "start")
+ start = animation[key];
+ else if (key === "end")
+ end = animation[key];
+ }
+
+ if (!element || !end)
+ continue;
+
+ if (!start) {
+ var computedStyle = element.ownerDocument.defaultView.getComputedStyle(element);
+ start = {};
+ for (key in end)
+ start[key] = parseInt(computedStyle.getPropertyValue(key));
+ animation.start = start;
+ } else
+ for (key in start)
+ element.style.setProperty(key, start[key] + (key in propertyUnit ? propertyUnit[key] : defaultUnit));
+ }
+
+ function animateLoop()
+ {
+ // Advance forward.
+ complete += intervalDuration;
+ var next = complete + intervalDuration;
+
+ // Make style changes.
+ for (var i = 0; i < animationsLength; ++i) {
+ var animation = animations[i];
+ var element = animation.element;
+ var start = animation.start;
+ var end = animation.end;
+ if (!element || !end)
+ continue;
+
+ var style = element.style;
+ for (key in end) {
+ var endValue = end[key];
+ if (next < duration) {
+ var startValue = start[key];
+ var newValue = cubicInOut(complete, startValue, endValue - startValue, duration);
+ style.setProperty(key, newValue + (key in propertyUnit ? propertyUnit[key] : defaultUnit));
+ } else
+ style.setProperty(key, endValue + (key in propertyUnit ? propertyUnit[key] : defaultUnit));
+ }
+ }
+
+ // End condition.
+ if (complete >= duration) {
+ clearInterval(interval);
+ if (callback)
+ callback();
+ }
+ }
+
+ interval = setInterval(animateLoop, intervalDuration);
+ return interval;
+}
+
+WebInspector.updateSearchLabel = function()
+{
+ if (!this.currentPanel)
+ return;
+
+ var newLabel = WebInspector.UIString("Search %s", this.currentPanel.toolbarItemLabel);
+ if (this.attached)
+ document.getElementById("search").setAttribute("placeholder", newLabel);
+ else {
+ document.getElementById("search").removeAttribute("placeholder");
+ document.getElementById("search-toolbar-label").textContent = newLabel;
+ }
+}
+
+WebInspector.focusSearchField = function()
+{
+ var searchField = document.getElementById("search");
+ searchField.focus();
+ searchField.select();
+}
+
+WebInspector.toggleAttach = function()
+{
+ if (!this.attached)
+ InspectorFrontendHost.requestAttachWindow();
+ else
+ InspectorFrontendHost.requestDetachWindow();
+}
+
+WebInspector.toolbarDragStart = function(event)
+{
+ if ((!WebInspector.attached && WebInspector.platformFlavor !== WebInspector.PlatformFlavor.MacLeopard && WebInspector.platformFlavor !== WebInspector.PlatformFlavor.MacSnowLeopard) || WebInspector.port == "qt")
+ return;
+
+ var target = event.target;
+ if (target.hasStyleClass("toolbar-item") && target.hasStyleClass("toggleable"))
+ return;
+
+ var toolbar = document.getElementById("toolbar");
+ if (target !== toolbar && !target.hasStyleClass("toolbar-item"))
+ return;
+
+ toolbar.lastScreenX = event.screenX;
+ toolbar.lastScreenY = event.screenY;
+
+ WebInspector.elementDragStart(toolbar, WebInspector.toolbarDrag, WebInspector.toolbarDragEnd, event, (WebInspector.attached ? "row-resize" : "default"));
+}
+
+WebInspector.toolbarDragEnd = function(event)
+{
+ var toolbar = document.getElementById("toolbar");
+
+ WebInspector.elementDragEnd(event);
+
+ delete toolbar.lastScreenX;
+ delete toolbar.lastScreenY;
+}
+
+WebInspector.toolbarDrag = function(event)
+{
+ var toolbar = document.getElementById("toolbar");
+
+ if (WebInspector.attached) {
+ var height = window.innerHeight - (event.screenY - toolbar.lastScreenY);
+
+ InspectorFrontendHost.setAttachedWindowHeight(height);
+ } else {
+ var x = event.screenX - toolbar.lastScreenX;
+ var y = event.screenY - toolbar.lastScreenY;
+
+ // We cannot call window.moveBy here because it restricts the movement
+ // of the window at the edges.
+ InspectorFrontendHost.moveWindowBy(x, y);
+ }
+
+ toolbar.lastScreenX = event.screenX;
+ toolbar.lastScreenY = event.screenY;
+
+ event.preventDefault();
+}
+
+WebInspector.elementDragStart = function(element, dividerDrag, elementDragEnd, event, cursor)
+{
+ if (this._elementDraggingEventListener || this._elementEndDraggingEventListener)
+ this.elementDragEnd(event);
+
+ this._elementDraggingEventListener = dividerDrag;
+ this._elementEndDraggingEventListener = elementDragEnd;
+
+ document.addEventListener("mousemove", dividerDrag, true);
+ document.addEventListener("mouseup", elementDragEnd, true);
+
+ document.body.style.cursor = cursor;
+
+ event.preventDefault();
+}
+
+WebInspector.elementDragEnd = function(event)
+{
+ document.removeEventListener("mousemove", this._elementDraggingEventListener, true);
+ document.removeEventListener("mouseup", this._elementEndDraggingEventListener, true);
+
+ document.body.style.removeProperty("cursor");
+
+ delete this._elementDraggingEventListener;
+ delete this._elementEndDraggingEventListener;
+
+ event.preventDefault();
+}
+
+WebInspector.toggleSearchingForNode = function()
+{
+ if (this.panels.elements) {
+ this.showPanel("elements");
+ this.panels.elements.toggleSearchingForNode();
+ }
+}
+
+WebInspector.showConsole = function()
+{
+ this.drawer.showView(this.console);
+}
+
+WebInspector.showChanges = function()
+{
+ this.drawer.showView(this.changes);
+}
+
+WebInspector.showPanel = function(panel)
+{
+ if (!(panel in this.panels))
+ panel = "elements";
+ this.currentPanel = this.panels[panel];
+}
+
+WebInspector.consoleMessagesCleared = function()
+{
+ WebInspector.console.clearMessages();
+}
+
+WebInspector.domContentEventFired = function(time)
+{
+ this.panels.audits.mainResourceDOMContentTime = time;
+ if (this.panels.network)
+ this.panels.network.mainResourceDOMContentTime = time;
+ this.extensionServer.notifyPageDOMContentLoaded((time - WebInspector.mainResource.startTime) * 1000);
+ this.mainResourceDOMContentTime = time;
+}
+
+WebInspector.loadEventFired = function(time)
+{
+ this.panels.audits.mainResourceLoadTime = time;
+ if (this.panels.network)
+ this.panels.network.mainResourceLoadTime = time;
+ this.extensionServer.notifyPageLoaded((time - WebInspector.mainResource.startTime) * 1000);
+ this.mainResourceLoadTime = time;
+}
+
+WebInspector.searchingForNodeWasEnabled = function()
+{
+ this.panels.elements.searchingForNodeWasEnabled();
+}
+
+WebInspector.searchingForNodeWasDisabled = function()
+{
+ this.panels.elements.searchingForNodeWasDisabled();
+}
+
+WebInspector.reset = function()
+{
+ this.debuggerModel.reset();
+
+ for (var panelName in this.panels) {
+ var panel = this.panels[panelName];
+ if ("reset" in panel)
+ panel.reset();
+ }
+
+ this.resources = {};
+ this.highlightDOMNode(0);
+
+ this.console.clearMessages();
+ this.extensionServer.notifyInspectorReset();
+}
+
+WebInspector.bringToFront = function()
+{
+ InspectorFrontendHost.bringToFront();
+}
+
+WebInspector.inspectedURLChanged = function(url)
+{
+ InspectorFrontendHost.inspectedURLChanged(url);
+ this.settings.inspectedURLChanged(url);
+ this.extensionServer.notifyInspectedURLChanged();
+}
+
+WebInspector.updateConsoleMessageExpiredCount = function(count)
+{
+ var message = String.sprintf(WebInspector.UIString("%d console messages are not shown."), count);
+ WebInspector.console.addMessage(WebInspector.ConsoleMessage.createTextMessage(message, WebInspector.ConsoleMessage.MessageLevel.Warning));
+}
+
+WebInspector.addConsoleMessage = function(payload)
+{
+ var consoleMessage = new WebInspector.ConsoleMessage(
+ payload.source,
+ payload.type,
+ payload.level,
+ payload.line,
+ payload.url,
+ payload.repeatCount,
+ payload.message,
+ payload.parameters,
+ payload.stackTrace,
+ payload.requestId);
+ this.console.addMessage(consoleMessage);
+}
+
+WebInspector.updateConsoleMessageRepeatCount = function(count)
+{
+ this.console.updateMessageRepeatCount(count);
+}
+
+WebInspector.log = function(message, messageLevel)
+{
+ // remember 'this' for setInterval() callback
+ var self = this;
+
+ // return indication if we can actually log a message
+ function isLogAvailable()
+ {
+ return WebInspector.ConsoleMessage && WebInspector.RemoteObject && self.console;
+ }
+
+ // flush the queue of pending messages
+ function flushQueue()
+ {
+ var queued = WebInspector.log.queued;
+ if (!queued)
+ return;
+
+ for (var i = 0; i < queued.length; ++i)
+ logMessage(queued[i]);
+
+ delete WebInspector.log.queued;
+ }
+
+ // flush the queue if it console is available
+ // - this function is run on an interval
+ function flushQueueIfAvailable()
+ {
+ if (!isLogAvailable())
+ return;
+
+ clearInterval(WebInspector.log.interval);
+ delete WebInspector.log.interval;
+
+ flushQueue();
+ }
+
+ // actually log the message
+ function logMessage(message)
+ {
+ var repeatCount = 1;
+ if (message == WebInspector.log.lastMessage)
+ repeatCount = WebInspector.log.repeatCount + 1;
+
+ WebInspector.log.lastMessage = message;
+ WebInspector.log.repeatCount = repeatCount;
+
+ // ConsoleMessage expects a proxy object
+ message = new WebInspector.RemoteObject.fromPrimitiveValue(message);
+
+ // post the message
+ var msg = new WebInspector.ConsoleMessage(
+ WebInspector.ConsoleMessage.MessageSource.Other,
+ WebInspector.ConsoleMessage.MessageType.Log,
+ messageLevel || WebInspector.ConsoleMessage.MessageLevel.Debug,
+ -1,
+ null,
+ repeatCount,
+ null,
+ [message],
+ null);
+
+ self.console.addMessage(msg);
+ }
+
+ // if we can't log the message, queue it
+ if (!isLogAvailable()) {
+ if (!WebInspector.log.queued)
+ WebInspector.log.queued = [];
+
+ WebInspector.log.queued.push(message);
+
+ if (!WebInspector.log.interval)
+ WebInspector.log.interval = setInterval(flushQueueIfAvailable, 1000);
+
+ return;
+ }
+
+ // flush the pending queue if any
+ flushQueue();
+
+ // log the message
+ logMessage(message);
+}
+
+WebInspector.drawLoadingPieChart = function(canvas, percent) {
+ var g = canvas.getContext("2d");
+ var darkColor = "rgb(122, 168, 218)";
+ var lightColor = "rgb(228, 241, 251)";
+ var cx = 8;
+ var cy = 8;
+ var r = 7;
+
+ g.beginPath();
+ g.arc(cx, cy, r, 0, Math.PI * 2, false);
+ g.closePath();
+
+ g.lineWidth = 1;
+ g.strokeStyle = darkColor;
+ g.fillStyle = lightColor;
+ g.fill();
+ g.stroke();
+
+ var startangle = -Math.PI / 2;
+ var endangle = startangle + (percent * Math.PI * 2);
+
+ g.beginPath();
+ g.moveTo(cx, cy);
+ g.arc(cx, cy, r, startangle, endangle, false);
+ g.closePath();
+
+ g.fillStyle = darkColor;
+ g.fill();
+}
+
+WebInspector.updateFocusedNode = function(nodeId)
+{
+ this._updateFocusedNode(nodeId);
+ this.highlightDOMNodeForTwoSeconds(nodeId);
+}
+
+WebInspector.displayNameForURL = function(url)
+{
+ if (!url)
+ return "";
+
+ var resource = this.resourceForURL(url);
+ if (resource)
+ return resource.displayName;
+
+ if (!WebInspector.mainResource)
+ return url.trimURL("");
+
+ var lastPathComponent = WebInspector.mainResource.lastPathComponent;
+ var index = WebInspector.mainResource.url.indexOf(lastPathComponent);
+ if (index !== -1 && index + lastPathComponent.length === WebInspector.mainResource.url.length) {
+ var baseURL = WebInspector.mainResource.url.substring(0, index);
+ if (url.indexOf(baseURL) === 0)
+ return url.substring(index);
+ }
+
+ return url.trimURL(WebInspector.mainResource.domain);
+}
+
+WebInspector._choosePanelToShowSourceLine = function(url, line, preferredPanel)
+{
+ preferredPanel = preferredPanel || "resources";
+
+ var panel = this.panels[preferredPanel];
+ if (panel && panel.canShowSourceLine(url, line))
+ return panel;
+ panel = this.panels.resources;
+ return panel.canShowSourceLine(url, line) ? panel : null;
+}
+
+WebInspector.canShowSourceLine = function(url, line, preferredPanel)
+{
+ return !!this._choosePanelToShowSourceLine(url, line, preferredPanel);
+}
+
+WebInspector.showSourceLine = function(url, line, preferredPanel)
+{
+ this.currentPanel = this._choosePanelToShowSourceLine(url, line, preferredPanel);
+ if (!this.currentPanel)
+ return false;
+ this.currentPanel.showSourceLine(url, line);
+ return true;
+}
+
+WebInspector.linkifyStringAsFragment = function(string)
+{
+ 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 title = linkString;
+ var linkIndex = string.indexOf(linkString);
+ var nonLink = string.substring(0, linkIndex);
+ container.appendChild(document.createTextNode(nonLink));
+
+ var profileStringMatches = WebInspector.ProfileType.URLRegExp.exec(title);
+ if (profileStringMatches)
+ title = WebInspector.panels.profiles.displayTitleForProfileLink(profileStringMatches[2], profileStringMatches[1]);
+
+ var realURL = (linkString.indexOf("www.") === 0 ? "http://" + linkString : linkString);
+ var lineColumnMatch = lineColumnRegEx.exec(realURL);
+ if (lineColumnMatch)
+ realURL = realURL.substring(0, realURL.length - lineColumnMatch[0].length);
+
+ var hasResourceWithURL = !!WebInspector.resourceForURL(realURL);
+ var urlNode = WebInspector.linkifyURLAsNode(realURL, title, null, hasResourceWithURL);
+ container.appendChild(urlNode);
+ if (lineColumnMatch) {
+ urlNode.setAttribute("line_number", lineColumnMatch[1]);
+ urlNode.setAttribute("preferred_panel", "scripts");
+ }
+ string = string.substring(linkIndex + linkString.length, string.length);
+ }
+
+ if (string)
+ container.appendChild(document.createTextNode(string));
+
+ return container;
+}
+
+WebInspector.showProfileForURL = function(url)
+{
+ WebInspector.showPanel("profiles");
+ WebInspector.panels.profiles.showProfileForURL(url);
+}
+
+WebInspector.linkifyURLAsNode = function(url, linkText, classes, isExternal, tooltipText)
+{
+ if (!linkText)
+ linkText = url;
+ classes = (classes ? classes + " " : "");
+ classes += isExternal ? "webkit-html-external-link" : "webkit-html-resource-link";
+
+ var a = document.createElement("a");
+ a.href = url;
+ a.className = classes;
+ if (typeof tooltipText === "undefined")
+ a.title = url;
+ else if (typeof tooltipText !== "string" || tooltipText.length)
+ a.title = tooltipText;
+ a.textContent = linkText;
+
+ return a;
+}
+
+WebInspector.linkifyURL = function(url, linkText, classes, isExternal, tooltipText)
+{
+ // Use the DOM version of this function so as to avoid needing to escape attributes.
+ // FIXME: Get rid of linkifyURL entirely.
+ return WebInspector.linkifyURLAsNode(url, linkText, classes, isExternal, tooltipText).outerHTML;
+}
+
+WebInspector.linkifyResourceAsNode = function(url, preferredPanel, lineNumber, classes, tooltipText)
+{
+ var linkText = WebInspector.displayNameForURL(url);
+ if (lineNumber)
+ linkText += ":" + lineNumber;
+ var node = WebInspector.linkifyURLAsNode(url, linkText, classes, false, tooltipText);
+ node.setAttribute("line_number", lineNumber);
+ node.setAttribute("preferred_panel", preferredPanel);
+ return node;
+}
+
+WebInspector.resourceURLForRelatedNode = function(node, url)
+{
+ if (!url || url.indexOf("://") > 0)
+ return url;
+
+ for (var frameOwnerCandidate = node; frameOwnerCandidate; frameOwnerCandidate = frameOwnerCandidate.parentNode) {
+ if (frameOwnerCandidate.documentURL) {
+ var result = WebInspector.completeURL(frameOwnerCandidate.documentURL, url);
+ if (result)
+ return result;
+ break;
+ }
+ }
+
+ // documentURL not found or has bad value
+ var resourceURL = url;
+ function callback(resource)
+ {
+ if (resource.path === url) {
+ resourceURL = resource.url;
+ return true;
+ }
+ }
+ WebInspector.forAllResources(callback);
+ return resourceURL;
+}
+
+WebInspector.completeURL = function(baseURL, href)
+{
+ if (href) {
+ // Return absolute URLs as-is.
+ var parsedHref = href.asParsedURL();
+ if (parsedHref && parsedHref.scheme)
+ return href;
+ }
+
+ var parsedURL = baseURL.asParsedURL();
+ if (parsedURL) {
+ var path = href;
+ if (path.charAt(0) !== "/") {
+ var basePath = parsedURL.path;
+ path = basePath.substring(0, basePath.lastIndexOf("/")) + "/" + path;
+ } else if (path.length > 1 && path.charAt(1) === "/") {
+ // href starts with "//" which is a full URL with the protocol dropped (use the baseURL protocol).
+ return parsedURL.scheme + ":" + path;
+ }
+ return parsedURL.scheme + "://" + parsedURL.host + (parsedURL.port ? (":" + parsedURL.port) : "") + path;
+ }
+ return null;
+}
+
+WebInspector.addMainEventListeners = function(doc)
+{
+ doc.defaultView.addEventListener("focus", this.windowFocused.bind(this), false);
+ doc.defaultView.addEventListener("blur", this.windowBlurred.bind(this), false);
+ doc.addEventListener("click", this.documentClick.bind(this), true);
+}
+
+WebInspector._searchFieldManualFocus = function(event)
+{
+ this.currentFocusElement = event.target;
+ this._previousFocusElement = event.target;
+}
+
+WebInspector._searchKeyDown = function(event)
+{
+ // Escape Key will clear the field and clear the search results
+ if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code) {
+ // If focus belongs here and text is empty - nothing to do, return unhandled.
+ if (event.target.value === "" && this.currentFocusElement === this.previousFocusElement)
+ return;
+ event.preventDefault();
+ event.stopPropagation();
+ // When search was selected manually and is currently blank, we'd like Esc stay unhandled
+ // and hit console drawer handler.
+ event.target.value = "";
+
+ this.performSearch(event);
+ this.currentFocusElement = this.previousFocusElement;
+ if (this.currentFocusElement === event.target)
+ this.currentFocusElement.select();
+ return false;
+ }
+
+ if (!isEnterKey(event))
+ return false;
+
+ // Select all of the text so the user can easily type an entirely new query.
+ event.target.select();
+
+ // Only call performSearch if the Enter key was pressed. Otherwise the search
+ // performance is poor because of searching on every key. The search field has
+ // the incremental attribute set, so we still get incremental searches.
+ this.performSearch(event);
+
+ // Call preventDefault since this was the Enter key. This prevents a "search" event
+ // from firing for key down. This stops performSearch from being called twice in a row.
+ event.preventDefault();
+}
+
+WebInspector.performSearch = function(event)
+{
+ var forceSearch = event.keyIdentifier === "Enter";
+ this.doPerformSearch(event.target.value, forceSearch, event.shiftKey, false);
+}
+
+WebInspector.doPerformSearch = function(query, forceSearch, isBackwardSearch, repeatSearch)
+{
+ var isShortSearch = (query.length < 3);
+
+ // Clear a leftover short search flag due to a non-conflicting forced search.
+ if (isShortSearch && this.shortSearchWasForcedByKeyEvent && this.currentQuery !== query)
+ delete this.shortSearchWasForcedByKeyEvent;
+
+ // Indicate this was a forced search on a short query.
+ if (isShortSearch && forceSearch)
+ this.shortSearchWasForcedByKeyEvent = true;
+
+ if (!query || !query.length || (!forceSearch && isShortSearch)) {
+ // Prevent clobbering a short search forced by the user.
+ if (this.shortSearchWasForcedByKeyEvent) {
+ delete this.shortSearchWasForcedByKeyEvent;
+ return;
+ }
+
+ delete this.currentQuery;
+
+ for (var panelName in this.panels) {
+ var panel = this.panels[panelName];
+ var hadCurrentQuery = !!panel.currentQuery;
+ delete panel.currentQuery;
+ if (hadCurrentQuery && panel.searchCanceled)
+ panel.searchCanceled();
+ }
+
+ this.updateSearchMatchesCount();
+
+ return;
+ }
+
+ if (!repeatSearch && query === this.currentPanel.currentQuery && this.currentPanel.currentQuery === this.currentQuery) {
+ // When this is the same query and a forced search, jump to the next
+ // search result for a good user experience.
+ if (forceSearch) {
+ if (!isBackwardSearch && this.currentPanel.jumpToNextSearchResult)
+ this.currentPanel.jumpToNextSearchResult();
+ else if (isBackwardSearch && this.currentPanel.jumpToPreviousSearchResult)
+ this.currentPanel.jumpToPreviousSearchResult();
+ }
+ return;
+ }
+
+ this.currentQuery = query;
+
+ this.updateSearchMatchesCount();
+
+ if (!this.currentPanel.performSearch)
+ return;
+
+ this.currentPanel.currentQuery = query;
+ this.currentPanel.performSearch(query);
+}
+
+WebInspector.addNodesToSearchResult = function(nodeIds)
+{
+ WebInspector.panels.elements.addNodesToSearchResult(nodeIds);
+}
+
+WebInspector.updateSearchMatchesCount = function(matches, panel)
+{
+ if (!panel)
+ panel = this.currentPanel;
+
+ panel.currentSearchMatches = matches;
+
+ if (panel !== this.currentPanel)
+ return;
+
+ if (!this.currentPanel.currentQuery) {
+ document.getElementById("search-results-matches").addStyleClass("hidden");
+ return;
+ }
+
+ if (matches) {
+ if (matches === 1)
+ var matchesString = WebInspector.UIString("1 match");
+ else
+ var matchesString = WebInspector.UIString("%d matches", matches);
+ } else
+ var matchesString = WebInspector.UIString("Not Found");
+
+ var matchesToolbarElement = document.getElementById("search-results-matches");
+ matchesToolbarElement.removeStyleClass("hidden");
+ matchesToolbarElement.textContent = matchesString;
+}
+
+WebInspector.UIString = function(string)
+{
+ if (window.localizedStrings && string in window.localizedStrings)
+ string = window.localizedStrings[string];
+ else {
+ if (!(string in WebInspector.missingLocalizedStrings)) {
+ if (!WebInspector.InspectorBackendStub)
+ console.error("Localized string \"" + string + "\" not found.");
+ WebInspector.missingLocalizedStrings[string] = true;
+ }
+
+ if (Preferences.showMissingLocalizedStrings)
+ string += " (not localized)";
+ }
+
+ return String.vsprintf(string, Array.prototype.slice.call(arguments, 1));
+}
+
+WebInspector.formatLocalized = function(format, substitutions, formatters, initialValue, append)
+{
+ return String.format(WebInspector.UIString(format), substitutions, formatters, initialValue, append);
+}
+
+WebInspector.isMac = function()
+{
+ if (!("_isMac" in this))
+ this._isMac = WebInspector.platform === "mac";
+
+ return this._isMac;
+}
+
+WebInspector.isBeingEdited = function(element)
+{
+ return element.__editing;
+}
+
+WebInspector.isEditingAnyField = function()
+{
+ return this.__editing;
+}
+
+// Available config fields (all optional):
+// context: Object - an arbitrary context object to be passed to the commit and cancel handlers
+// commitHandler: Function - handles editing "commit" outcome
+// cancelHandler: Function - handles editing "cancel" outcome
+// customFinishHandler: Function - custom finish handler for the editing session (invoked on keydown)
+// pasteHandler: Function - handles the "paste" event, return values are the same as those for customFinishHandler
+// multiline: Boolean - whether the edited element is multiline
+WebInspector.startEditing = function(element, config)
+{
+ if (element.__editing)
+ return;
+ element.__editing = true;
+ WebInspector.__editing = true;
+
+ config = config || {};
+ var committedCallback = config.commitHandler;
+ var cancelledCallback = config.cancelHandler;
+ var pasteCallback = config.pasteHandler;
+ var context = config.context;
+ var oldText = getContent(element);
+ var moveDirection = "";
+
+ element.addStyleClass("editing");
+
+ var oldTabIndex = element.tabIndex;
+ if (element.tabIndex < 0)
+ element.tabIndex = 0;
+
+ function blurEventListener() {
+ editingCommitted.call(element);
+ }
+
+ function getContent(element) {
+ if (element.tagName === "INPUT" && element.type === "text")
+ return element.value;
+ else
+ return element.textContent;
+ }
+
+ function cleanUpAfterEditing() {
+ delete this.__editing;
+ delete WebInspector.__editing;
+
+ this.removeStyleClass("editing");
+ this.tabIndex = oldTabIndex;
+ this.scrollTop = 0;
+ this.scrollLeft = 0;
+
+ element.removeEventListener("blur", blurEventListener, false);
+ element.removeEventListener("keydown", keyDownEventListener, true);
+ if (pasteCallback)
+ element.removeEventListener("paste", pasteEventListener, true);
+
+ if (element === WebInspector.currentFocusElement || element.isAncestor(WebInspector.currentFocusElement))
+ WebInspector.currentFocusElement = WebInspector.previousFocusElement;
+ }
+
+ function editingCancelled() {
+ if (this.tagName === "INPUT" && this.type === "text")
+ this.value = oldText;
+ else
+ this.textContent = oldText;
+
+ cleanUpAfterEditing.call(this);
+
+ if (cancelledCallback)
+ cancelledCallback(this, context);
+ }
+
+ function editingCommitted() {
+ cleanUpAfterEditing.call(this);
+
+ if (committedCallback)
+ committedCallback(this, getContent(this), oldText, context, moveDirection);
+ }
+
+ function defaultFinishHandler(event)
+ {
+ var isMetaOrCtrl = WebInspector.isMac() ?
+ event.metaKey && !event.shiftKey && !event.ctrlKey && !event.altKey :
+ event.ctrlKey && !event.shiftKey && !event.metaKey && !event.altKey;
+ if (isEnterKey(event) && (!config.multiline || isMetaOrCtrl))
+ return "commit";
+ else if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code)
+ return "cancel";
+ else if (event.keyIdentifier === "U+0009") // Tab key
+ return "move-" + (event.shiftKey ? "backward" : "forward");
+ }
+
+ function handleEditingResult(result, event)
+ {
+ if (result === "commit") {
+ editingCommitted.call(element);
+ event.preventDefault();
+ event.stopPropagation();
+ } else if (result === "cancel") {
+ editingCancelled.call(element);
+ event.preventDefault();
+ event.stopPropagation();
+ } else if (result && result.indexOf("move-") === 0) {
+ moveDirection = result.substring(5);
+ if (event.keyIdentifier !== "U+0009")
+ blurEventListener();
+ }
+ }
+
+ function pasteEventListener(event)
+ {
+ var result = pasteCallback(event);
+ handleEditingResult(result, event);
+ }
+
+ function keyDownEventListener(event)
+ {
+ var handler = config.customFinishHandler || defaultFinishHandler;
+ var result = handler(event);
+ handleEditingResult(result, event);
+ }
+
+ element.addEventListener("blur", blurEventListener, false);
+ element.addEventListener("keydown", keyDownEventListener, true);
+ if (pasteCallback)
+ element.addEventListener("paste", pasteEventListener, true);
+
+ WebInspector.currentFocusElement = element;
+ return {
+ cancel: editingCancelled.bind(element),
+ commit: editingCommitted.bind(element)
+ };
+}
+
+WebInspector._toolbarItemClicked = function(event)
+{
+ var toolbarItem = event.currentTarget;
+ this.currentPanel = toolbarItem.panel;
+}
+
+// This table maps MIME types to the Resource.Types which are valid for them.
+// The following line:
+// "text/html": {0: 1},
+// means that text/html is a valid MIME type for resources that have type
+// WebInspector.Resource.Type.Document (which has a value of 0).
+WebInspector.MIMETypes = {
+ "text/html": {0: true},
+ "text/xml": {0: true},
+ "text/plain": {0: true},
+ "application/xhtml+xml": {0: true},
+ "text/css": {1: true},
+ "text/xsl": {1: true},
+ "image/jpeg": {2: true},
+ "image/png": {2: true},
+ "image/gif": {2: true},
+ "image/bmp": {2: true},
+ "image/vnd.microsoft.icon": {2: true},
+ "image/x-icon": {2: true},
+ "image/x-xbitmap": {2: true},
+ "font/ttf": {3: true},
+ "font/opentype": {3: true},
+ "application/x-font-type1": {3: true},
+ "application/x-font-ttf": {3: true},
+ "application/x-truetype-font": {3: true},
+ "text/javascript": {4: true},
+ "text/ecmascript": {4: true},
+ "application/javascript": {4: true},
+ "application/ecmascript": {4: true},
+ "application/x-javascript": {4: true},
+ "text/javascript1.1": {4: true},
+ "text/javascript1.2": {4: true},
+ "text/javascript1.3": {4: true},
+ "text/jscript": {4: true},
+ "text/livescript": {4: true},
+}
+
+WebInspector.PanelHistory = function()
+{
+ this._history = [];
+ this._historyIterator = -1;
+}
+
+WebInspector.PanelHistory.prototype = {
+ canGoBack: function()
+ {
+ return this._historyIterator > 0;
+ },
+
+ goBack: function()
+ {
+ this._inHistory = true;
+ WebInspector.currentPanel = WebInspector.panels[this._history[--this._historyIterator]];
+ delete this._inHistory;
+ },
+
+ canGoForward: function()
+ {
+ return this._historyIterator < this._history.length - 1;
+ },
+
+ goForward: function()
+ {
+ this._inHistory = true;
+ WebInspector.currentPanel = WebInspector.panels[this._history[++this._historyIterator]];
+ delete this._inHistory;
+ },
+
+ setPanel: function(panelName)
+ {
+ if (this._inHistory)
+ return;
+
+ this._history.splice(this._historyIterator + 1, this._history.length - this._historyIterator - 1);
+ if (!this._history.length || this._history[this._history.length - 1] !== panelName)
+ this._history.push(panelName);
+ this._historyIterator = this._history.length - 1;
+ }
+}