/* * 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. */ 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"; } preloadImages(); var WebInspector = { resources: {}, resourceURLMap: {}, cookieDomains: {}, missingLocalizedStrings: {}, pendingDispatches: 0, // RegExp groups: // 1 - scheme // 2 - hostname // 3 - ?port // 4 - ?path // 5 - ?fragment URLRegExp: /^(http[s]?|file):\/\/([^\/:]*)(?::([\d]+))?(?:(\/[^#]*)(?:#(.*))?)?$/i, 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) InspectorBackend.storeLastActivePanel(panelName); } }, _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("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 (hiddenPanels.indexOf("storage") === -1 && hiddenPanels.indexOf("databases") === -1) this.panels.storage = new WebInspector.StoragePanel(); // FIXME: Uncomment when ready. // 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) { InspectorFrontendHost.attach(); body.removeStyleClass("detached"); body.addStyleClass("attached"); dockToggleButton.title = WebInspector.UIString("Undock into separate window."); } else { InspectorFrontendHost.detach(); body.removeStyleClass("attached"); body.addStyleClass("detached"); dockToggleButton.title = WebInspector.UIString("Dock to main window."); } }, 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); } }, get hoveredDOMNode() { return this._hoveredDOMNode; }, set hoveredDOMNode(x) { if (this._hoveredDOMNode === x) return; this._hoveredDOMNode = x; if (this._hoveredDOMNode) this._updateHoverHighlightSoon(this.showingDOMNodeHighlight ? 50 : 500); else this._updateHoverHighlight(); }, _updateHoverHighlightSoon: function(delay) { if ("_updateHoverHighlightTimeout" in this) clearTimeout(this._updateHoverHighlightTimeout); this._updateHoverHighlightTimeout = setTimeout(this._updateHoverHighlight.bind(this), delay); }, _updateHoverHighlight: function() { if ("_updateHoverHighlightTimeout" in this) { clearTimeout(this._updateHoverHighlightTimeout); delete this._updateHoverHighlightTimeout; } if (this._hoveredDOMNode) { InspectorBackend.highlightDOMNode(this._hoveredDOMNode.id); this.showingDOMNodeHighlight = true; } else { InspectorBackend.hideDOMNodeHighlight(); this.showingDOMNodeHighlight = false; } } } WebInspector.PlatformFlavor = { WindowsVista: "windows-vista", MacTiger: "mac-tiger", MacLeopard: "mac-leopard", MacSnowLeopard: "mac-snowleopard" } WebInspector.loaded = 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); this.settings = new WebInspector.Settings(); 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.domAgent = new WebInspector.DOMAgent(); 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)"), other: new WebInspector.ResourceCategory("other", WebInspector.UIString("Other"), "rgb(186,186,186)") }; this.panels = {}; this._createPanels(); 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("unload", this.windowUnload.bind(this), true); 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); InspectorFrontendHost.loaded(); } 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("load", windowLoaded, false); delete windowLoaded; }; window.addEventListener("load", windowLoaded, false); WebInspector.dispatch = function() { var methodName = arguments[0]; var parameters = Array.prototype.slice.call(arguments, 1); // We'd like to enforce asynchronous interaction between the inspector controller and the frontend. // This is important to LayoutTests. function delayDispatch() { WebInspector[methodName].apply(WebInspector, parameters); WebInspector.pendingDispatches--; } WebInspector.pendingDispatches++; setTimeout(delayDispatch, 0); } WebInspector.windowUnload = function(event) { InspectorFrontendHost.windowUnloading(); } 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