1 /* 2 * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. 3 * Copyright (C) 2007 Matt Lilek (pewtermoose (at) gmail.com). 4 * Copyright (C) 2009 Joseph Pecoraro 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 16 * its contributors may be used to endorse or promote products derived 17 * from this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 // Keep this ; so that concatenated version of the script worked. 32 ;(function preloadImages() 33 { 34 (new Image()).src = "Images/clearConsoleButtonGlyph.png"; 35 (new Image()).src = "Images/consoleButtonGlyph.png"; 36 (new Image()).src = "Images/dockButtonGlyph.png"; 37 (new Image()).src = "Images/enableOutlineButtonGlyph.png"; 38 (new Image()).src = "Images/enableSolidButtonGlyph.png"; 39 (new Image()).src = "Images/excludeButtonGlyph.png"; 40 (new Image()).src = "Images/focusButtonGlyph.png"; 41 (new Image()).src = "Images/largerResourcesButtonGlyph.png"; 42 (new Image()).src = "Images/nodeSearchButtonGlyph.png"; 43 (new Image()).src = "Images/pauseOnExceptionButtonGlyph.png"; 44 (new Image()).src = "Images/percentButtonGlyph.png"; 45 (new Image()).src = "Images/recordButtonGlyph.png"; 46 (new Image()).src = "Images/recordToggledButtonGlyph.png"; 47 (new Image()).src = "Images/reloadButtonGlyph.png"; 48 (new Image()).src = "Images/undockButtonGlyph.png"; 49 })(); 50 51 var WebInspector = { 52 resources: {}, 53 missingLocalizedStrings: {}, 54 pendingDispatches: 0, 55 56 get platform() 57 { 58 if (!("_platform" in this)) 59 this._platform = InspectorFrontendHost.platform(); 60 61 return this._platform; 62 }, 63 64 get platformFlavor() 65 { 66 if (!("_platformFlavor" in this)) 67 this._platformFlavor = this._detectPlatformFlavor(); 68 69 return this._platformFlavor; 70 }, 71 72 _detectPlatformFlavor: function() 73 { 74 const userAgent = navigator.userAgent; 75 76 if (this.platform === "windows") { 77 var match = userAgent.match(/Windows NT (\d+)\.(?:\d+)/); 78 if (match && match[1] >= 6) 79 return WebInspector.PlatformFlavor.WindowsVista; 80 return null; 81 } else if (this.platform === "mac") { 82 var match = userAgent.match(/Mac OS X\s*(?:(\d+)_(\d+))?/); 83 if (!match || match[1] != 10) 84 return WebInspector.PlatformFlavor.MacSnowLeopard; 85 switch (Number(match[2])) { 86 case 4: 87 return WebInspector.PlatformFlavor.MacTiger; 88 case 5: 89 return WebInspector.PlatformFlavor.MacLeopard; 90 case 6: 91 default: 92 return WebInspector.PlatformFlavor.MacSnowLeopard; 93 } 94 } 95 96 return null; 97 }, 98 99 get port() 100 { 101 if (!("_port" in this)) 102 this._port = InspectorFrontendHost.port(); 103 104 return this._port; 105 }, 106 107 get previousFocusElement() 108 { 109 return this._previousFocusElement; 110 }, 111 112 get currentFocusElement() 113 { 114 return this._currentFocusElement; 115 }, 116 117 set currentFocusElement(x) 118 { 119 if (this._currentFocusElement !== x) 120 this._previousFocusElement = this._currentFocusElement; 121 this._currentFocusElement = x; 122 123 if (this._currentFocusElement) { 124 this._currentFocusElement.focus(); 125 126 // Make a caret selection inside the new element if there isn't a range selection and 127 // there isn't already a caret selection inside. 128 var selection = window.getSelection(); 129 if (selection.isCollapsed && !this._currentFocusElement.isInsertionCaretInside()) { 130 var selectionRange = this._currentFocusElement.ownerDocument.createRange(); 131 selectionRange.setStart(this._currentFocusElement, 0); 132 selectionRange.setEnd(this._currentFocusElement, 0); 133 134 selection.removeAllRanges(); 135 selection.addRange(selectionRange); 136 } 137 } else if (this._previousFocusElement) 138 this._previousFocusElement.blur(); 139 }, 140 141 resetFocusElement: function() 142 { 143 this.currentFocusElement = null; 144 this._previousFocusElement = null; 145 }, 146 147 get currentPanel() 148 { 149 return this._currentPanel; 150 }, 151 152 set currentPanel(x) 153 { 154 if (this._currentPanel === x) 155 return; 156 157 if (this._currentPanel) 158 this._currentPanel.hide(); 159 160 this._currentPanel = x; 161 162 if (x) { 163 x.show(); 164 WebInspector.searchController.activePanelChanged(); 165 } 166 for (var panelName in WebInspector.panels) { 167 if (WebInspector.panels[panelName] === x) { 168 WebInspector.settings.lastActivePanel = panelName; 169 this._panelHistory.setPanel(panelName); 170 } 171 } 172 }, 173 174 _createPanels: function() 175 { 176 var hiddenPanels = (InspectorFrontendHost.hiddenPanels() || "").split(','); 177 if (hiddenPanels.indexOf("elements") === -1) 178 this.panels.elements = new WebInspector.ElementsPanel(); 179 if (hiddenPanels.indexOf("resources") === -1) 180 this.panels.resources = new WebInspector.ResourcesPanel(); 181 if (hiddenPanels.indexOf("network") === -1) 182 this.panels.network = new WebInspector.NetworkPanel(); 183 if (hiddenPanels.indexOf("scripts") === -1) 184 this.panels.scripts = new WebInspector.ScriptsPanel(); 185 if (hiddenPanels.indexOf("timeline") === -1) 186 this.panels.timeline = new WebInspector.TimelinePanel(); 187 if (hiddenPanels.indexOf("profiles") === -1) 188 this.panels.profiles = new WebInspector.ProfilesPanel(); 189 if (hiddenPanels.indexOf("audits") === -1) 190 this.panels.audits = new WebInspector.AuditsPanel(); 191 if (hiddenPanels.indexOf("console") === -1) 192 this.panels.console = new WebInspector.ConsolePanel(); 193 }, 194 195 get attached() 196 { 197 return this._attached; 198 }, 199 200 set attached(x) 201 { 202 if (this._attached === x) 203 return; 204 205 this._attached = x; 206 207 var dockToggleButton = document.getElementById("dock-status-bar-item"); 208 var body = document.body; 209 210 if (x) { 211 body.removeStyleClass("detached"); 212 body.addStyleClass("attached"); 213 dockToggleButton.title = WebInspector.UIString("Undock into separate window."); 214 } else { 215 body.removeStyleClass("attached"); 216 body.addStyleClass("detached"); 217 dockToggleButton.title = WebInspector.UIString("Dock to main window."); 218 } 219 220 // This may be called before onLoadedDone, hence the bulk of inspector objects may 221 // not be created yet. 222 if (WebInspector.searchController) 223 WebInspector.searchController.updateSearchLabel(); 224 }, 225 226 get errors() 227 { 228 return this._errors || 0; 229 }, 230 231 set errors(x) 232 { 233 x = Math.max(x, 0); 234 235 if (this._errors === x) 236 return; 237 this._errors = x; 238 this._updateErrorAndWarningCounts(); 239 }, 240 241 get warnings() 242 { 243 return this._warnings || 0; 244 }, 245 246 set warnings(x) 247 { 248 x = Math.max(x, 0); 249 250 if (this._warnings === x) 251 return; 252 this._warnings = x; 253 this._updateErrorAndWarningCounts(); 254 }, 255 256 _updateErrorAndWarningCounts: function() 257 { 258 var errorWarningElement = document.getElementById("error-warning-count"); 259 if (!errorWarningElement) 260 return; 261 262 if (!this.errors && !this.warnings) { 263 errorWarningElement.addStyleClass("hidden"); 264 return; 265 } 266 267 errorWarningElement.removeStyleClass("hidden"); 268 269 errorWarningElement.removeChildren(); 270 271 if (this.errors) { 272 var errorElement = document.createElement("span"); 273 errorElement.id = "error-count"; 274 errorElement.textContent = this.errors; 275 errorWarningElement.appendChild(errorElement); 276 } 277 278 if (this.warnings) { 279 var warningsElement = document.createElement("span"); 280 warningsElement.id = "warning-count"; 281 warningsElement.textContent = this.warnings; 282 errorWarningElement.appendChild(warningsElement); 283 } 284 285 if (this.errors) { 286 if (this.warnings) { 287 if (this.errors == 1) { 288 if (this.warnings == 1) 289 errorWarningElement.title = WebInspector.UIString("%d error, %d warning", this.errors, this.warnings); 290 else 291 errorWarningElement.title = WebInspector.UIString("%d error, %d warnings", this.errors, this.warnings); 292 } else if (this.warnings == 1) 293 errorWarningElement.title = WebInspector.UIString("%d errors, %d warning", this.errors, this.warnings); 294 else 295 errorWarningElement.title = WebInspector.UIString("%d errors, %d warnings", this.errors, this.warnings); 296 } else if (this.errors == 1) 297 errorWarningElement.title = WebInspector.UIString("%d error", this.errors); 298 else 299 errorWarningElement.title = WebInspector.UIString("%d errors", this.errors); 300 } else if (this.warnings == 1) 301 errorWarningElement.title = WebInspector.UIString("%d warning", this.warnings); 302 else if (this.warnings) 303 errorWarningElement.title = WebInspector.UIString("%d warnings", this.warnings); 304 else 305 errorWarningElement.title = null; 306 }, 307 308 highlightDOMNode: function(nodeId) 309 { 310 if ("_hideDOMNodeHighlightTimeout" in this) { 311 clearTimeout(this._hideDOMNodeHighlightTimeout); 312 delete this._hideDOMNodeHighlightTimeout; 313 } 314 315 if (this._highlightedDOMNodeId === nodeId) 316 return; 317 318 this._highlightedDOMNodeId = nodeId; 319 if (nodeId) 320 DOMAgent.highlightDOMNode(nodeId); 321 else 322 DOMAgent.hideDOMNodeHighlight(); 323 }, 324 325 highlightDOMNodeForTwoSeconds: function(nodeId) 326 { 327 this.highlightDOMNode(nodeId); 328 this._hideDOMNodeHighlightTimeout = setTimeout(this.highlightDOMNode.bind(this, 0), 2000); 329 }, 330 331 wireElementWithDOMNode: function(element, nodeId) 332 { 333 element.addEventListener("click", this._updateFocusedNode.bind(this, nodeId), false); 334 element.addEventListener("mouseover", this.highlightDOMNode.bind(this, nodeId), false); 335 element.addEventListener("mouseout", this.highlightDOMNode.bind(this, 0), false); 336 }, 337 338 _updateFocusedNode: function(nodeId) 339 { 340 this.currentPanel = this.panels.elements; 341 this.panels.elements.updateFocusedNode(nodeId); 342 }, 343 344 get networkResources() 345 { 346 return this.panels.network.resources; 347 }, 348 349 networkResourceById: function(id) 350 { 351 return this.panels.network.resourceById(id); 352 }, 353 354 forAllResources: function(callback) 355 { 356 WebInspector.resourceTreeModel.forAllResources(callback); 357 }, 358 359 resourceForURL: function(url) 360 { 361 return this.resourceTreeModel.resourceForURL(url); 362 }, 363 364 openLinkExternallyLabel: function() 365 { 366 return WebInspector.UIString("Open Link in New Window"); 367 } 368 } 369 370 WebInspector.PlatformFlavor = { 371 WindowsVista: "windows-vista", 372 MacTiger: "mac-tiger", 373 MacLeopard: "mac-leopard", 374 MacSnowLeopard: "mac-snowleopard" 375 }; 376 377 (function parseQueryParameters() 378 { 379 WebInspector.queryParamsObject = {}; 380 var queryParams = window.location.search; 381 if (!queryParams) 382 return; 383 var params = queryParams.substring(1).split("&"); 384 for (var i = 0; i < params.length; ++i) { 385 var pair = params[i].split("="); 386 WebInspector.queryParamsObject[pair[0]] = pair[1]; 387 } 388 })(); 389 390 WebInspector.loaded = function() 391 { 392 if ("page" in WebInspector.queryParamsObject) { 393 var page = WebInspector.queryParamsObject.page; 394 var host = "host" in WebInspector.queryParamsObject ? WebInspector.queryParamsObject.host : window.location.host; 395 WebInspector.socket = new WebSocket("ws://" + host + "/devtools/page/" + page); 396 WebInspector.socket.onmessage = function(message) { InspectorBackend.dispatch(message.data); } 397 WebInspector.socket.onerror = function(error) { console.error(error); } 398 WebInspector.socket.onopen = function() { 399 InspectorFrontendHost.sendMessageToBackend = WebInspector.socket.send.bind(WebInspector.socket); 400 InspectorFrontendHost.loaded = WebInspector.socket.send.bind(WebInspector.socket, "loaded"); 401 WebInspector.doLoadedDone(); 402 } 403 return; 404 } 405 WebInspector.doLoadedDone(); 406 } 407 408 WebInspector.doLoadedDone = function() 409 { 410 InspectorFrontendHost.loaded(); 411 412 var platform = WebInspector.platform; 413 document.body.addStyleClass("platform-" + platform); 414 var flavor = WebInspector.platformFlavor; 415 if (flavor) 416 document.body.addStyleClass("platform-" + flavor); 417 var port = WebInspector.port; 418 document.body.addStyleClass("port-" + port); 419 if (WebInspector.socket) 420 document.body.addStyleClass("remote"); 421 422 WebInspector.settings = new WebInspector.Settings(); 423 424 this._registerShortcuts(); 425 426 // set order of some sections explicitly 427 WebInspector.shortcutsHelp.section(WebInspector.UIString("Console")); 428 WebInspector.shortcutsHelp.section(WebInspector.UIString("Elements Panel")); 429 430 this.drawer = new WebInspector.Drawer(); 431 this.console = new WebInspector.ConsoleView(this.drawer); 432 this.drawer.visibleView = this.console; 433 this.networkManager = new WebInspector.NetworkManager(); 434 this.resourceTreeModel = new WebInspector.ResourceTreeModel(); 435 this.domAgent = new WebInspector.DOMAgent(); 436 437 InspectorBackend.registerDomainDispatcher("Inspector", this); 438 InspectorBackend.registerDomainDispatcher("Page", this); 439 440 this.resourceCategories = { 441 documents: new WebInspector.ResourceCategory("documents", WebInspector.UIString("Documents"), "rgb(47,102,236)"), 442 stylesheets: new WebInspector.ResourceCategory("stylesheets", WebInspector.UIString("Stylesheets"), "rgb(157,231,119)"), 443 images: new WebInspector.ResourceCategory("images", WebInspector.UIString("Images"), "rgb(164,60,255)"), 444 scripts: new WebInspector.ResourceCategory("scripts", WebInspector.UIString("Scripts"), "rgb(255,121,0)"), 445 xhr: new WebInspector.ResourceCategory("xhr", WebInspector.UIString("XHR"), "rgb(231,231,10)"), 446 fonts: new WebInspector.ResourceCategory("fonts", WebInspector.UIString("Fonts"), "rgb(255,82,62)"), 447 websockets: new WebInspector.ResourceCategory("websockets", WebInspector.UIString("WebSockets"), "rgb(186,186,186)"), // FIXME: Decide the color. 448 other: new WebInspector.ResourceCategory("other", WebInspector.UIString("Other"), "rgb(186,186,186)") 449 }; 450 451 this.cssModel = new WebInspector.CSSStyleModel(); 452 this.debuggerModel = new WebInspector.DebuggerModel(); 453 454 this.searchController = new WebInspector.SearchController(); 455 this.domBreakpointsSidebarPane = new WebInspector.DOMBreakpointsSidebarPane(); 456 457 this.panels = {}; 458 this._createPanels(); 459 this._panelHistory = new WebInspector.PanelHistory(); 460 this.toolbar = new WebInspector.Toolbar(); 461 462 this.panelOrder = []; 463 for (var panelName in this.panels) 464 this.addPanel(this.panels[panelName]); 465 466 this.Tips = { 467 ResourceNotCompressed: {id: 0, message: WebInspector.UIString("You could save bandwidth by having your web server compress this transfer with gzip or zlib.")} 468 }; 469 470 this.Warnings = { 471 IncorrectMIMEType: {id: 0, message: WebInspector.UIString("Resource interpreted as %s but transferred with MIME type %s.")} 472 }; 473 474 this.addMainEventListeners(document); 475 476 window.addEventListener("resize", this.windowResize.bind(this), true); 477 478 document.addEventListener("focus", this.focusChanged.bind(this), true); 479 document.addEventListener("keydown", this.documentKeyDown.bind(this), false); 480 document.addEventListener("beforecopy", this.documentCanCopy.bind(this), true); 481 document.addEventListener("copy", this.documentCopy.bind(this), true); 482 document.addEventListener("contextmenu", this.contextMenuEventFired.bind(this), true); 483 484 var dockToggleButton = document.getElementById("dock-status-bar-item"); 485 dockToggleButton.addEventListener("click", this.toggleAttach.bind(this), false); 486 487 if (this.attached) 488 dockToggleButton.title = WebInspector.UIString("Undock into separate window."); 489 else 490 dockToggleButton.title = WebInspector.UIString("Dock to main window."); 491 492 var errorWarningCount = document.getElementById("error-warning-count"); 493 errorWarningCount.addEventListener("click", this.showConsole.bind(this), false); 494 this._updateErrorAndWarningCounts(); 495 496 this.extensionServer.initExtensions(); 497 498 if (WebInspector.settings.monitoringXHREnabled) 499 ConsoleAgent.setMonitoringXHREnabled(true); 500 ConsoleAgent.enable(this.console.setConsoleMessageExpiredCount.bind(this.console)); 501 502 DatabaseAgent.enable(); 503 504 WebInspector.showPanel(WebInspector.settings.lastActivePanel); 505 506 function propertyNamesCallback(error, names) 507 { 508 if (!error) 509 WebInspector.cssNameCompletions = new WebInspector.CSSCompletions(names); 510 } 511 // As a DOMAgent method, this needs to happen after the frontend has loaded and the agent is available. 512 CSSAgent.getSupportedCSSProperties(propertyNamesCallback); 513 } 514 515 WebInspector.addPanel = function(panel) 516 { 517 this.panelOrder.push(panel); 518 this.toolbar.addPanel(panel); 519 } 520 521 var windowLoaded = function() 522 { 523 var localizedStringsURL = InspectorFrontendHost.localizedStringsURL(); 524 if (localizedStringsURL) { 525 var localizedStringsScriptElement = document.createElement("script"); 526 localizedStringsScriptElement.addEventListener("load", WebInspector.loaded.bind(WebInspector), false); 527 localizedStringsScriptElement.type = "text/javascript"; 528 localizedStringsScriptElement.src = localizedStringsURL; 529 document.head.appendChild(localizedStringsScriptElement); 530 } else 531 WebInspector.loaded(); 532 533 window.removeEventListener("DOMContentLoaded", windowLoaded, false); 534 delete windowLoaded; 535 }; 536 537 window.addEventListener("DOMContentLoaded", windowLoaded, false); 538 539 // We'd like to enforce asynchronous interaction between the inspector controller and the frontend. 540 // It is needed to prevent re-entering the backend code. 541 // Also, native dispatches do not guarantee setTimeouts to be serialized, so we 542 // enforce serialization using 'messagesToDispatch' queue. It is also important that JSC debugger 543 // tests require that each command was dispatch within individual timeout callback, so we don't batch them. 544 545 var messagesToDispatch = []; 546 547 WebInspector.dispatch = function(message) { 548 messagesToDispatch.push(message); 549 setTimeout(function() { 550 InspectorBackend.dispatch(messagesToDispatch.shift()); 551 }, 0); 552 } 553 554 WebInspector.dispatchMessageFromBackend = function(messageObject) 555 { 556 WebInspector.dispatch(messageObject); 557 } 558 559 WebInspector.windowResize = function(event) 560 { 561 if (this.currentPanel) 562 this.currentPanel.resize(); 563 this.drawer.resize(); 564 this.toolbar.resize(); 565 } 566 567 WebInspector.windowFocused = function(event) 568 { 569 // Fires after blur, so when focusing on either the main inspector 570 // or an <iframe> within the inspector we should always remove the 571 // "inactive" class. 572 if (event.target.document.nodeType === Node.DOCUMENT_NODE) 573 document.body.removeStyleClass("inactive"); 574 } 575 576 WebInspector.windowBlurred = function(event) 577 { 578 // Leaving the main inspector or an <iframe> within the inspector. 579 // We can add "inactive" now, and if we are moving the focus to another 580 // part of the inspector then windowFocused will correct this. 581 if (event.target.document.nodeType === Node.DOCUMENT_NODE) 582 document.body.addStyleClass("inactive"); 583 } 584 585 WebInspector.focusChanged = function(event) 586 { 587 this.currentFocusElement = event.target; 588 } 589 590 WebInspector.setAttachedWindow = function(attached) 591 { 592 this.attached = attached; 593 } 594 595 WebInspector.close = function(event) 596 { 597 if (this._isClosing) 598 return; 599 this._isClosing = true; 600 InspectorFrontendHost.closeWindow(); 601 } 602 603 WebInspector.disconnectFromBackend = function() 604 { 605 InspectorFrontendHost.disconnectFromBackend(); 606 } 607 608 WebInspector.documentClick = function(event) 609 { 610 var anchor = event.target.enclosingNodeOrSelfWithNodeName("a"); 611 if (!anchor || anchor.target === "_blank") 612 return; 613 614 // Prevent the link from navigating, since we don't do any navigation by following links normally. 615 event.preventDefault(); 616 event.stopPropagation(); 617 618 function followLink() 619 { 620 if (WebInspector._showAnchorLocation(anchor)) 621 return; 622 623 const profileMatch = WebInspector.ProfileType.URLRegExp.exec(anchor.href); 624 if (profileMatch) { 625 WebInspector.showProfileForURL(anchor.href); 626 return; 627 } 628 629 var parsedURL = anchor.href.asParsedURL(); 630 if (parsedURL && parsedURL.scheme === "webkit-link-action") { 631 if (parsedURL.host === "show-panel") { 632 var panel = parsedURL.path.substring(1); 633 if (WebInspector.panels[panel]) 634 WebInspector.showPanel(panel); 635 } 636 return; 637 } 638 639 WebInspector.showPanel("resources"); 640 } 641 642 if (WebInspector.followLinkTimeout) 643 clearTimeout(WebInspector.followLinkTimeout); 644 645 if (anchor.preventFollowOnDoubleClick) { 646 // Start a timeout if this is the first click, if the timeout is canceled 647 // before it fires, then a double clicked happened or another link was clicked. 648 if (event.detail === 1) 649 WebInspector.followLinkTimeout = setTimeout(followLink, 333); 650 return; 651 } 652 653 followLink(); 654 } 655 656 WebInspector.openResource = function(resourceURL, inResourcesPanel) 657 { 658 var resource = WebInspector.resourceForURL(resourceURL); 659 if (inResourcesPanel && resource) { 660 WebInspector.panels.resources.showResource(resource); 661 WebInspector.showPanel("resources"); 662 } else 663 PageAgent.openInInspectedWindow(resource ? resource.url : resourceURL); 664 } 665 666 WebInspector._registerShortcuts = function() 667 { 668 var shortcut = WebInspector.KeyboardShortcut; 669 var section = WebInspector.shortcutsHelp.section(WebInspector.UIString("All Panels")); 670 var keys = [ 671 shortcut.shortcutToString("]", shortcut.Modifiers.CtrlOrMeta), 672 shortcut.shortcutToString("[", shortcut.Modifiers.CtrlOrMeta) 673 ]; 674 section.addRelatedKeys(keys, WebInspector.UIString("Next/previous panel")); 675 section.addKey(shortcut.shortcutToString(shortcut.Keys.Esc), WebInspector.UIString("Toggle console")); 676 section.addKey(shortcut.shortcutToString("f", shortcut.Modifiers.CtrlOrMeta), WebInspector.UIString("Search")); 677 if (WebInspector.isMac()) { 678 keys = [ 679 shortcut.shortcutToString("g", shortcut.Modifiers.Meta), 680 shortcut.shortcutToString("g", shortcut.Modifiers.Meta | shortcut.Modifiers.Shift) 681 ]; 682 section.addRelatedKeys(keys, WebInspector.UIString("Find next/previous")); 683 } 684 } 685 686 WebInspector.documentKeyDown = function(event) 687 { 688 var isInputElement = event.target.nodeName === "INPUT"; 689 var isInEditMode = event.target.enclosingNodeOrSelfWithClass("text-prompt") || WebInspector.isEditingAnyField(); 690 const helpKey = WebInspector.isMac() ? "U+003F" : "U+00BF"; // "?" for both platforms 691 692 if (event.keyIdentifier === "F1" || 693 (event.keyIdentifier === helpKey && event.shiftKey && (!isInEditMode && !isInputElement || event.metaKey))) { 694 WebInspector.shortcutsHelp.show(); 695 event.stopPropagation(); 696 event.preventDefault(); 697 return; 698 } 699 700 if (WebInspector.isEditingAnyField()) 701 return; 702 703 if (this.currentFocusElement && this.currentFocusElement.handleKeyEvent) { 704 this.currentFocusElement.handleKeyEvent(event); 705 if (event.handled) { 706 event.preventDefault(); 707 return; 708 } 709 } 710 711 if (this.currentPanel && this.currentPanel.handleShortcut) { 712 this.currentPanel.handleShortcut(event); 713 if (event.handled) { 714 event.preventDefault(); 715 return; 716 } 717 } 718 719 WebInspector.searchController.handleShortcut(event); 720 if (event.handled) { 721 event.preventDefault(); 722 return; 723 } 724 725 var isMac = WebInspector.isMac(); 726 switch (event.keyIdentifier) { 727 case "Left": 728 var isBackKey = !isInEditMode && (isMac ? event.metaKey : event.ctrlKey); 729 if (isBackKey && this._panelHistory.canGoBack()) { 730 this._panelHistory.goBack(); 731 event.preventDefault(); 732 } 733 break; 734 735 case "Right": 736 var isForwardKey = !isInEditMode && (isMac ? event.metaKey : event.ctrlKey); 737 if (isForwardKey && this._panelHistory.canGoForward()) { 738 this._panelHistory.goForward(); 739 event.preventDefault(); 740 } 741 break; 742 743 case "U+001B": // Escape key 744 event.preventDefault(); 745 if (this.drawer.fullPanel) 746 return; 747 748 this.drawer.visible = !this.drawer.visible; 749 break; 750 751 // Windows and Mac have two different definitions of [, so accept both. 752 case "U+005B": 753 case "U+00DB": // [ key 754 if (isMac) 755 var isRotateLeft = event.metaKey && !event.shiftKey && !event.ctrlKey && !event.altKey; 756 else 757 var isRotateLeft = event.ctrlKey && !event.shiftKey && !event.metaKey && !event.altKey; 758 759 if (isRotateLeft) { 760 var index = this.panelOrder.indexOf(this.currentPanel); 761 index = (index === 0) ? this.panelOrder.length - 1 : index - 1; 762 this.panelOrder[index].toolbarItem.click(); 763 event.preventDefault(); 764 } 765 766 break; 767 768 // Windows and Mac have two different definitions of ], so accept both. 769 case "U+005D": 770 case "U+00DD": // ] key 771 if (isMac) 772 var isRotateRight = event.metaKey && !event.shiftKey && !event.ctrlKey && !event.altKey; 773 else 774 var isRotateRight = event.ctrlKey && !event.shiftKey && !event.metaKey && !event.altKey; 775 776 if (isRotateRight) { 777 var index = this.panelOrder.indexOf(this.currentPanel); 778 index = (index + 1) % this.panelOrder.length; 779 this.panelOrder[index].toolbarItem.click(); 780 event.preventDefault(); 781 } 782 783 break; 784 785 case "U+0052": // R key 786 if ((event.metaKey && isMac) || (event.ctrlKey && !isMac)) { 787 PageAgent.reloadPage(event.shiftKey); 788 event.preventDefault(); 789 } 790 break; 791 case "F5": 792 if (!isMac) 793 PageAgent.reloadPage(event.ctrlKey || event.shiftKey); 794 break; 795 } 796 } 797 798 WebInspector.documentCanCopy = function(event) 799 { 800 if (this.currentPanel && this.currentPanel.handleCopyEvent) 801 event.preventDefault(); 802 } 803 804 WebInspector.documentCopy = function(event) 805 { 806 if (this.currentPanel && this.currentPanel.handleCopyEvent) 807 this.currentPanel.handleCopyEvent(event); 808 } 809 810 WebInspector.contextMenuEventFired = function(event) 811 { 812 if (event.handled || event.target.hasStyleClass("popup-glasspane")) 813 event.preventDefault(); 814 } 815 816 WebInspector.animateStyle = function(animations, duration, callback) 817 { 818 var interval; 819 var complete = 0; 820 var hasCompleted = false; 821 822 const intervalDuration = (1000 / 30); // 30 frames per second. 823 const animationsLength = animations.length; 824 const propertyUnit = {opacity: ""}; 825 const defaultUnit = "px"; 826 827 function cubicInOut(t, b, c, d) 828 { 829 if ((t/=d/2) < 1) return c/2*t*t*t + b; 830 return c/2*((t-=2)*t*t + 2) + b; 831 } 832 833 // Pre-process animations. 834 for (var i = 0; i < animationsLength; ++i) { 835 var animation = animations[i]; 836 var element = null, start = null, end = null, key = null; 837 for (key in animation) { 838 if (key === "element") 839 element = animation[key]; 840 else if (key === "start") 841 start = animation[key]; 842 else if (key === "end") 843 end = animation[key]; 844 } 845 846 if (!element || !end) 847 continue; 848 849 if (!start) { 850 var computedStyle = element.ownerDocument.defaultView.getComputedStyle(element); 851 start = {}; 852 for (key in end) 853 start[key] = parseInt(computedStyle.getPropertyValue(key)); 854 animation.start = start; 855 } else 856 for (key in start) 857 element.style.setProperty(key, start[key] + (key in propertyUnit ? propertyUnit[key] : defaultUnit)); 858 } 859 860 function animateLoop() 861 { 862 // Advance forward. 863 complete += intervalDuration; 864 var next = complete + intervalDuration; 865 866 // Make style changes. 867 for (var i = 0; i < animationsLength; ++i) { 868 var animation = animations[i]; 869 var element = animation.element; 870 var start = animation.start; 871 var end = animation.end; 872 if (!element || !end) 873 continue; 874 875 var style = element.style; 876 for (key in end) { 877 var endValue = end[key]; 878 if (next < duration) { 879 var startValue = start[key]; 880 var newValue = cubicInOut(complete, startValue, endValue - startValue, duration); 881 style.setProperty(key, newValue + (key in propertyUnit ? propertyUnit[key] : defaultUnit)); 882 } else 883 style.setProperty(key, endValue + (key in propertyUnit ? propertyUnit[key] : defaultUnit)); 884 } 885 } 886 887 // End condition. 888 if (complete >= duration) { 889 hasCompleted = true; 890 clearInterval(interval); 891 if (callback) 892 callback(); 893 } 894 } 895 896 function forceComplete() 897 { 898 if (!hasCompleted) { 899 complete = duration; 900 animateLoop(); 901 } 902 } 903 904 function cancel() 905 { 906 hasCompleted = true; 907 clearInterval(interval); 908 } 909 910 interval = setInterval(animateLoop, intervalDuration); 911 return { 912 cancel: cancel, 913 forceComplete: forceComplete 914 }; 915 } 916 917 WebInspector.toggleAttach = function() 918 { 919 if (!this.attached) 920 InspectorFrontendHost.requestAttachWindow(); 921 else 922 InspectorFrontendHost.requestDetachWindow(); 923 } 924 925 WebInspector.elementDragStart = function(element, dividerDrag, elementDragEnd, event, cursor) 926 { 927 if (this._elementDraggingEventListener || this._elementEndDraggingEventListener) 928 this.elementDragEnd(event); 929 930 this._elementDraggingEventListener = dividerDrag; 931 this._elementEndDraggingEventListener = elementDragEnd; 932 933 document.addEventListener("mousemove", dividerDrag, true); 934 document.addEventListener("mouseup", elementDragEnd, true); 935 936 document.body.style.cursor = cursor; 937 938 event.preventDefault(); 939 } 940 941 WebInspector.elementDragEnd = function(event) 942 { 943 document.removeEventListener("mousemove", this._elementDraggingEventListener, true); 944 document.removeEventListener("mouseup", this._elementEndDraggingEventListener, true); 945 946 document.body.style.removeProperty("cursor"); 947 948 delete this._elementDraggingEventListener; 949 delete this._elementEndDraggingEventListener; 950 951 event.preventDefault(); 952 } 953 954 WebInspector.toggleSearchingForNode = function() 955 { 956 if (this.panels.elements) { 957 this.showPanel("elements"); 958 this.panels.elements.toggleSearchingForNode(); 959 } 960 } 961 962 WebInspector.showConsole = function() 963 { 964 this.drawer.showView(this.console); 965 } 966 967 WebInspector.showPanel = function(panel) 968 { 969 if (!(panel in this.panels)) 970 panel = "elements"; 971 this.currentPanel = this.panels[panel]; 972 } 973 974 WebInspector.startUserInitiatedDebugging = function() 975 { 976 this.currentPanel = this.panels.scripts; 977 WebInspector.debuggerModel.enableDebugger(); 978 } 979 980 WebInspector.domContentEventFired = function(time) 981 { 982 this.panels.audits.mainResourceDOMContentTime = time; 983 this.mainResourceDOMContentTime = time; 984 } 985 986 WebInspector.loadEventFired = function(time) 987 { 988 this.panels.audits.mainResourceLoadTime = time; 989 this.panels.resources.loadEventFired(); 990 this.mainResourceLoadTime = time; 991 } 992 993 WebInspector.searchingForNodeWasEnabled = function() 994 { 995 this.panels.elements.searchingForNodeWasEnabled(); 996 } 997 998 WebInspector.searchingForNodeWasDisabled = function() 999 { 1000 this.panels.elements.searchingForNodeWasDisabled(); 1001 } 1002 1003 WebInspector.reset = function() 1004 { 1005 this.debuggerModel.reset(); 1006 1007 for (var panelName in this.panels) { 1008 var panel = this.panels[panelName]; 1009 if ("reset" in panel) 1010 panel.reset(); 1011 } 1012 1013 this.resources = {}; 1014 this.highlightDOMNode(0); 1015 1016 this.console.clearMessages(); 1017 this.extensionServer.notifyInspectorReset(); 1018 } 1019 1020 WebInspector.bringToFront = function() 1021 { 1022 InspectorFrontendHost.bringToFront(); 1023 } 1024 1025 WebInspector.inspectedURLChanged = function(url) 1026 { 1027 InspectorFrontendHost.inspectedURLChanged(url); 1028 this.domBreakpointsSidebarPane.setInspectedURL(url); 1029 this.extensionServer.notifyInspectedURLChanged(url); 1030 } 1031 1032 WebInspector.didCreateWorker = function() 1033 { 1034 var workersPane = WebInspector.panels.scripts.sidebarPanes.workers; 1035 workersPane.addWorker.apply(workersPane, arguments); 1036 } 1037 1038 WebInspector.didDestroyWorker = function() 1039 { 1040 var workersPane = WebInspector.panels.scripts.sidebarPanes.workers; 1041 workersPane.removeWorker.apply(workersPane, arguments); 1042 } 1043 1044 WebInspector.log = function(message, messageLevel) 1045 { 1046 // remember 'this' for setInterval() callback 1047 var self = this; 1048 1049 // return indication if we can actually log a message 1050 function isLogAvailable() 1051 { 1052 return WebInspector.ConsoleMessage && WebInspector.RemoteObject && self.console; 1053 } 1054 1055 // flush the queue of pending messages 1056 function flushQueue() 1057 { 1058 var queued = WebInspector.log.queued; 1059 if (!queued) 1060 return; 1061 1062 for (var i = 0; i < queued.length; ++i) 1063 logMessage(queued[i]); 1064 1065 delete WebInspector.log.queued; 1066 } 1067 1068 // flush the queue if it console is available 1069 // - this function is run on an interval 1070 function flushQueueIfAvailable() 1071 { 1072 if (!isLogAvailable()) 1073 return; 1074 1075 clearInterval(WebInspector.log.interval); 1076 delete WebInspector.log.interval; 1077 1078 flushQueue(); 1079 } 1080 1081 // actually log the message 1082 function logMessage(message) 1083 { 1084 var repeatCount = 1; 1085 if (message == WebInspector.log.lastMessage) 1086 repeatCount = WebInspector.log.repeatCount + 1; 1087 1088 WebInspector.log.lastMessage = message; 1089 WebInspector.log.repeatCount = repeatCount; 1090 1091 // ConsoleMessage expects a proxy object 1092 message = new WebInspector.RemoteObject.fromPrimitiveValue(message); 1093 1094 // post the message 1095 var msg = new WebInspector.ConsoleMessage( 1096 WebInspector.ConsoleMessage.MessageSource.Other, 1097 WebInspector.ConsoleMessage.MessageType.Log, 1098 messageLevel || WebInspector.ConsoleMessage.MessageLevel.Debug, 1099 -1, 1100 null, 1101 repeatCount, 1102 null, 1103 [message], 1104 null); 1105 1106 self.console.addMessage(msg); 1107 } 1108 1109 // if we can't log the message, queue it 1110 if (!isLogAvailable()) { 1111 if (!WebInspector.log.queued) 1112 WebInspector.log.queued = []; 1113 1114 WebInspector.log.queued.push(message); 1115 1116 if (!WebInspector.log.interval) 1117 WebInspector.log.interval = setInterval(flushQueueIfAvailable, 1000); 1118 1119 return; 1120 } 1121 1122 // flush the pending queue if any 1123 flushQueue(); 1124 1125 // log the message 1126 logMessage(message); 1127 } 1128 1129 WebInspector.drawLoadingPieChart = function(canvas, percent) { 1130 var g = canvas.getContext("2d"); 1131 var darkColor = "rgb(122, 168, 218)"; 1132 var lightColor = "rgb(228, 241, 251)"; 1133 var cx = 8; 1134 var cy = 8; 1135 var r = 7; 1136 1137 g.beginPath(); 1138 g.arc(cx, cy, r, 0, Math.PI * 2, false); 1139 g.closePath(); 1140 1141 g.lineWidth = 1; 1142 g.strokeStyle = darkColor; 1143 g.fillStyle = lightColor; 1144 g.fill(); 1145 g.stroke(); 1146 1147 var startangle = -Math.PI / 2; 1148 var endangle = startangle + (percent * Math.PI * 2); 1149 1150 g.beginPath(); 1151 g.moveTo(cx, cy); 1152 g.arc(cx, cy, r, startangle, endangle, false); 1153 g.closePath(); 1154 1155 g.fillStyle = darkColor; 1156 g.fill(); 1157 } 1158 1159 WebInspector.inspect = function(payload, hints) 1160 { 1161 var object = WebInspector.RemoteObject.fromPayload(payload); 1162 if (object.type === "node") { 1163 // Request node from backend and focus it. 1164 object.pushNodeToFrontend(WebInspector.updateFocusedNode.bind(WebInspector), object.release.bind(object)); 1165 return; 1166 } 1167 1168 if (hints.databaseId) { 1169 WebInspector.currentPanel = WebInspector.panels.resources; 1170 WebInspector.panels.resources.selectDatabase(hints.databaseId); 1171 } else if (hints.domStorageId) { 1172 WebInspector.currentPanel = WebInspector.panels.resources; 1173 WebInspector.panels.resources.selectDOMStorage(hints.domStorageId); 1174 } 1175 1176 object.release(); 1177 } 1178 1179 WebInspector.updateFocusedNode = function(nodeId) 1180 { 1181 this._updateFocusedNode(nodeId); 1182 this.highlightDOMNodeForTwoSeconds(nodeId); 1183 } 1184 1185 WebInspector.displayNameForURL = function(url) 1186 { 1187 if (!url) 1188 return ""; 1189 1190 var resource = this.resourceForURL(url); 1191 if (resource) 1192 return resource.displayName; 1193 1194 if (!WebInspector.mainResource) 1195 return url.trimURL(""); 1196 1197 var lastPathComponent = WebInspector.mainResource.lastPathComponent; 1198 var index = WebInspector.mainResource.url.indexOf(lastPathComponent); 1199 if (index !== -1 && index + lastPathComponent.length === WebInspector.mainResource.url.length) { 1200 var baseURL = WebInspector.mainResource.url.substring(0, index); 1201 if (url.indexOf(baseURL) === 0) 1202 return url.substring(index); 1203 } 1204 1205 return url.trimURL(WebInspector.mainResource.domain); 1206 } 1207 1208 WebInspector._showAnchorLocation = function(anchor) 1209 { 1210 var preferedPanel = this.panels[anchor.getAttribute("preferred_panel") || "resources"]; 1211 if (WebInspector._showAnchorLocationInPanel(anchor, preferedPanel)) 1212 return true; 1213 if (preferedPanel !== this.panels.resources && WebInspector._showAnchorLocationInPanel(anchor, this.panels.resources)) 1214 return true; 1215 return false; 1216 } 1217 1218 WebInspector._showAnchorLocationInPanel = function(anchor, panel) 1219 { 1220 if (!panel.canShowAnchorLocation(anchor)) 1221 return false; 1222 1223 // FIXME: support webkit-html-external-link links here. 1224 if (anchor.hasStyleClass("webkit-html-external-link")) { 1225 anchor.removeStyleClass("webkit-html-external-link"); 1226 anchor.addStyleClass("webkit-html-resource-link"); 1227 } 1228 1229 this.currentPanel = panel; 1230 if (this.drawer) 1231 this.drawer.immediatelyFinishAnimation(); 1232 this.currentPanel.showAnchorLocation(anchor); 1233 return true; 1234 } 1235 1236 WebInspector.linkifyStringAsFragment = function(string) 1237 { 1238 var container = document.createDocumentFragment(); 1239 var linkStringRegEx = /(?:[a-zA-Z][a-zA-Z0-9+.-]{2,}:\/\/|www\.)[\w$\-_+*'=\|\/\\(){}[\]%@&#~,:;.!?]{2,}[\w$\-_+*=\|\/\\({%@&#~]/; 1240 var lineColumnRegEx = /:(\d+)(:(\d+))?$/; 1241 1242 while (string) { 1243 var linkString = linkStringRegEx.exec(string); 1244 if (!linkString) 1245 break; 1246 1247 linkString = linkString[0]; 1248 var title = linkString; 1249 var linkIndex = string.indexOf(linkString); 1250 var nonLink = string.substring(0, linkIndex); 1251 container.appendChild(document.createTextNode(nonLink)); 1252 1253 var profileStringMatches = WebInspector.ProfileType.URLRegExp.exec(title); 1254 if (profileStringMatches) 1255 title = WebInspector.panels.profiles.displayTitleForProfileLink(profileStringMatches[2], profileStringMatches[1]); 1256 1257 var realURL = (linkString.indexOf("www.") === 0 ? "http://" + linkString : linkString); 1258 var lineColumnMatch = lineColumnRegEx.exec(realURL); 1259 if (lineColumnMatch) 1260 realURL = realURL.substring(0, realURL.length - lineColumnMatch[0].length); 1261 1262 var hasResourceWithURL = !!WebInspector.resourceForURL(realURL); 1263 var urlNode = WebInspector.linkifyURLAsNode(realURL, title, null, hasResourceWithURL); 1264 container.appendChild(urlNode); 1265 if (lineColumnMatch) { 1266 urlNode.setAttribute("line_number", lineColumnMatch[1]); 1267 urlNode.setAttribute("preferred_panel", "scripts"); 1268 } 1269 string = string.substring(linkIndex + linkString.length, string.length); 1270 } 1271 1272 if (string) 1273 container.appendChild(document.createTextNode(string)); 1274 1275 return container; 1276 } 1277 1278 WebInspector.showProfileForURL = function(url) 1279 { 1280 WebInspector.showPanel("profiles"); 1281 WebInspector.panels.profiles.showProfileForURL(url); 1282 } 1283 1284 WebInspector.linkifyURLAsNode = function(url, linkText, classes, isExternal, tooltipText) 1285 { 1286 if (!linkText) 1287 linkText = url; 1288 classes = (classes ? classes + " " : ""); 1289 classes += isExternal ? "webkit-html-external-link" : "webkit-html-resource-link"; 1290 1291 var a = document.createElement("a"); 1292 a.href = url; 1293 a.className = classes; 1294 if (typeof tooltipText === "undefined") 1295 a.title = url; 1296 else if (typeof tooltipText !== "string" || tooltipText.length) 1297 a.title = tooltipText; 1298 a.textContent = linkText; 1299 a.style.maxWidth = "100%"; 1300 1301 return a; 1302 } 1303 1304 WebInspector.linkifyURL = function(url, linkText, classes, isExternal, tooltipText) 1305 { 1306 // Use the DOM version of this function so as to avoid needing to escape attributes. 1307 // FIXME: Get rid of linkifyURL entirely. 1308 return WebInspector.linkifyURLAsNode(url, linkText, classes, isExternal, tooltipText).outerHTML; 1309 } 1310 1311 WebInspector.linkifyResourceAsNode = function(url, preferredPanel, lineNumber, classes, tooltipText) 1312 { 1313 var linkText = WebInspector.displayNameForURL(url); 1314 if (lineNumber) 1315 linkText += ":" + lineNumber; 1316 var node = WebInspector.linkifyURLAsNode(url, linkText, classes, false, tooltipText); 1317 node.setAttribute("line_number", lineNumber); 1318 node.setAttribute("preferred_panel", preferredPanel); 1319 return node; 1320 } 1321 1322 WebInspector.resourceURLForRelatedNode = function(node, url) 1323 { 1324 if (!url || url.indexOf("://") > 0) 1325 return url; 1326 1327 for (var frameOwnerCandidate = node; frameOwnerCandidate; frameOwnerCandidate = frameOwnerCandidate.parentNode) { 1328 if (frameOwnerCandidate.documentURL) { 1329 var result = WebInspector.completeURL(frameOwnerCandidate.documentURL, url); 1330 if (result) 1331 return result; 1332 break; 1333 } 1334 } 1335 1336 // documentURL not found or has bad value 1337 var resourceURL = url; 1338 function callback(resource) 1339 { 1340 if (resource.path === url) { 1341 resourceURL = resource.url; 1342 return true; 1343 } 1344 } 1345 WebInspector.forAllResources(callback); 1346 return resourceURL; 1347 } 1348 1349 WebInspector.completeURL = function(baseURL, href) 1350 { 1351 if (href) { 1352 // Return absolute URLs as-is. 1353 var parsedHref = href.asParsedURL(); 1354 if (parsedHref && parsedHref.scheme) 1355 return href; 1356 } 1357 1358 var parsedURL = baseURL.asParsedURL(); 1359 if (parsedURL) { 1360 var path = href; 1361 if (path.charAt(0) !== "/") { 1362 var basePath = parsedURL.path; 1363 // A href of "?foo=bar" implies "basePath?foo=bar". 1364 // With "basePath?a=b" and "?foo=bar" we should get "basePath?foo=bar". 1365 var prefix; 1366 if (path.charAt(0) === "?") { 1367 var basePathCutIndex = basePath.indexOf("?"); 1368 if (basePathCutIndex !== -1) 1369 prefix = basePath.substring(0, basePathCutIndex); 1370 else 1371 prefix = basePath; 1372 } else 1373 prefix = basePath.substring(0, basePath.lastIndexOf("/")) + "/"; 1374 1375 path = prefix + path; 1376 } else if (path.length > 1 && path.charAt(1) === "/") { 1377 // href starts with "//" which is a full URL with the protocol dropped (use the baseURL protocol). 1378 return parsedURL.scheme + ":" + path; 1379 } 1380 return parsedURL.scheme + "://" + parsedURL.host + (parsedURL.port ? (":" + parsedURL.port) : "") + path; 1381 } 1382 return null; 1383 } 1384 1385 WebInspector.addMainEventListeners = function(doc) 1386 { 1387 doc.defaultView.addEventListener("focus", this.windowFocused.bind(this), false); 1388 doc.defaultView.addEventListener("blur", this.windowBlurred.bind(this), false); 1389 doc.addEventListener("click", this.documentClick.bind(this), true); 1390 } 1391 1392 WebInspector.frontendReused = function() 1393 { 1394 this.networkManager.frontendReused(); 1395 this.resourceTreeModel.frontendReused(); 1396 WebInspector.panels.network.clear(); 1397 this.reset(); 1398 } 1399 1400 WebInspector.UIString = function(string) 1401 { 1402 if (window.localizedStrings && string in window.localizedStrings) 1403 string = window.localizedStrings[string]; 1404 else { 1405 if (!(string in WebInspector.missingLocalizedStrings)) { 1406 if (!WebInspector.InspectorBackendStub) 1407 console.warn("Localized string \"" + string + "\" not found."); 1408 WebInspector.missingLocalizedStrings[string] = true; 1409 } 1410 1411 if (Preferences.showMissingLocalizedStrings) 1412 string += " (not localized)"; 1413 } 1414 1415 return String.vsprintf(string, Array.prototype.slice.call(arguments, 1)); 1416 } 1417 1418 WebInspector.formatLocalized = function(format, substitutions, formatters, initialValue, append) 1419 { 1420 return String.format(WebInspector.UIString(format), substitutions, formatters, initialValue, append); 1421 } 1422 1423 WebInspector.isMac = function() 1424 { 1425 if (!("_isMac" in this)) 1426 this._isMac = WebInspector.platform === "mac"; 1427 1428 return this._isMac; 1429 } 1430 1431 WebInspector.isBeingEdited = function(element) 1432 { 1433 return element.__editing; 1434 } 1435 1436 WebInspector.markBeingEdited = function(element, value) 1437 { 1438 if (value) { 1439 if (element.__editing) 1440 return false; 1441 element.__editing = true; 1442 WebInspector.__editingCount = (WebInspector.__editingCount || 0) + 1; 1443 } else { 1444 if (!element.__editing) 1445 return false; 1446 delete element.__editing; 1447 --WebInspector.__editingCount; 1448 } 1449 return true; 1450 } 1451 1452 WebInspector.isEditingAnyField = function() 1453 { 1454 return !!WebInspector.__editingCount; 1455 } 1456 1457 // Available config fields (all optional): 1458 // context: Object - an arbitrary context object to be passed to the commit and cancel handlers 1459 // commitHandler: Function - handles editing "commit" outcome 1460 // cancelHandler: Function - handles editing "cancel" outcome 1461 // customFinishHandler: Function - custom finish handler for the editing session (invoked on keydown) 1462 // pasteHandler: Function - handles the "paste" event, return values are the same as those for customFinishHandler 1463 // multiline: Boolean - whether the edited element is multiline 1464 WebInspector.startEditing = function(element, config) 1465 { 1466 if (!WebInspector.markBeingEdited(element, true)) 1467 return; 1468 1469 config = config || {}; 1470 var committedCallback = config.commitHandler; 1471 var cancelledCallback = config.cancelHandler; 1472 var pasteCallback = config.pasteHandler; 1473 var context = config.context; 1474 var oldText = getContent(element); 1475 var moveDirection = ""; 1476 1477 element.addStyleClass("editing"); 1478 1479 var oldTabIndex = element.tabIndex; 1480 if (element.tabIndex < 0) 1481 element.tabIndex = 0; 1482 1483 function blurEventListener() { 1484 editingCommitted.call(element); 1485 } 1486 1487 function getContent(element) { 1488 if (element.tagName === "INPUT" && element.type === "text") 1489 return element.value; 1490 else 1491 return element.textContent; 1492 } 1493 1494 function cleanUpAfterEditing() { 1495 WebInspector.markBeingEdited(element, false); 1496 1497 this.removeStyleClass("editing"); 1498 this.tabIndex = oldTabIndex; 1499 this.scrollTop = 0; 1500 this.scrollLeft = 0; 1501 1502 element.removeEventListener("blur", blurEventListener, false); 1503 element.removeEventListener("keydown", keyDownEventListener, true); 1504 if (pasteCallback) 1505 element.removeEventListener("paste", pasteEventListener, true); 1506 1507 if (element === WebInspector.currentFocusElement || element.isAncestor(WebInspector.currentFocusElement)) 1508 WebInspector.currentFocusElement = WebInspector.previousFocusElement; 1509 } 1510 1511 function editingCancelled() { 1512 if (this.tagName === "INPUT" && this.type === "text") 1513 this.value = oldText; 1514 else 1515 this.textContent = oldText; 1516 1517 cleanUpAfterEditing.call(this); 1518 1519 if (cancelledCallback) 1520 cancelledCallback(this, context); 1521 } 1522 1523 function editingCommitted() { 1524 cleanUpAfterEditing.call(this); 1525 1526 if (committedCallback) 1527 committedCallback(this, getContent(this), oldText, context, moveDirection); 1528 } 1529 1530 function defaultFinishHandler(event) 1531 { 1532 var isMetaOrCtrl = WebInspector.isMac() ? 1533 event.metaKey && !event.shiftKey && !event.ctrlKey && !event.altKey : 1534 event.ctrlKey && !event.shiftKey && !event.metaKey && !event.altKey; 1535 if (isEnterKey(event) && (event.isMetaOrCtrlForTest || !config.multiline || isMetaOrCtrl)) 1536 return "commit"; 1537 else if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code) 1538 return "cancel"; 1539 else if (event.keyIdentifier === "U+0009") // Tab key 1540 return "move-" + (event.shiftKey ? "backward" : "forward"); 1541 } 1542 1543 function handleEditingResult(result, event) 1544 { 1545 if (result === "commit") { 1546 editingCommitted.call(element); 1547 event.preventDefault(); 1548 event.stopPropagation(); 1549 } else if (result === "cancel") { 1550 editingCancelled.call(element); 1551 event.preventDefault(); 1552 event.stopPropagation(); 1553 } else if (result && result.indexOf("move-") === 0) { 1554 moveDirection = result.substring(5); 1555 if (event.keyIdentifier !== "U+0009") 1556 blurEventListener(); 1557 } 1558 } 1559 1560 function pasteEventListener(event) 1561 { 1562 var result = pasteCallback(event); 1563 handleEditingResult(result, event); 1564 } 1565 1566 function keyDownEventListener(event) 1567 { 1568 var handler = config.customFinishHandler || defaultFinishHandler; 1569 var result = handler(event); 1570 handleEditingResult(result, event); 1571 } 1572 1573 element.addEventListener("blur", blurEventListener, false); 1574 element.addEventListener("keydown", keyDownEventListener, true); 1575 if (pasteCallback) 1576 element.addEventListener("paste", pasteEventListener, true); 1577 1578 WebInspector.currentFocusElement = element; 1579 return { 1580 cancel: editingCancelled.bind(element), 1581 commit: editingCommitted.bind(element) 1582 }; 1583 } 1584 1585 WebInspector._toolbarItemClicked = function(event) 1586 { 1587 var toolbarItem = event.currentTarget; 1588 this.currentPanel = toolbarItem.panel; 1589 } 1590 1591 // This table maps MIME types to the Resource.Types which are valid for them. 1592 // The following line: 1593 // "text/html": {0: 1}, 1594 // means that text/html is a valid MIME type for resources that have type 1595 // WebInspector.Resource.Type.Document (which has a value of 0). 1596 WebInspector.MIMETypes = { 1597 "text/html": {0: true}, 1598 "text/xml": {0: true}, 1599 "text/plain": {0: true}, 1600 "application/xhtml+xml": {0: true}, 1601 "text/css": {1: true}, 1602 "text/xsl": {1: true}, 1603 "image/jpeg": {2: true}, 1604 "image/png": {2: true}, 1605 "image/gif": {2: true}, 1606 "image/bmp": {2: true}, 1607 "image/vnd.microsoft.icon": {2: true}, 1608 "image/x-icon": {2: true}, 1609 "image/x-xbitmap": {2: true}, 1610 "font/ttf": {3: true}, 1611 "font/opentype": {3: true}, 1612 "application/x-font-type1": {3: true}, 1613 "application/x-font-ttf": {3: true}, 1614 "application/x-font-woff": {3: true}, 1615 "application/x-truetype-font": {3: true}, 1616 "text/javascript": {4: true}, 1617 "text/ecmascript": {4: true}, 1618 "application/javascript": {4: true}, 1619 "application/ecmascript": {4: true}, 1620 "application/x-javascript": {4: true}, 1621 "text/javascript1.1": {4: true}, 1622 "text/javascript1.2": {4: true}, 1623 "text/javascript1.3": {4: true}, 1624 "text/jscript": {4: true}, 1625 "text/livescript": {4: true}, 1626 } 1627 1628 WebInspector.PanelHistory = function() 1629 { 1630 this._history = []; 1631 this._historyIterator = -1; 1632 } 1633 1634 WebInspector.PanelHistory.prototype = { 1635 canGoBack: function() 1636 { 1637 return this._historyIterator > 0; 1638 }, 1639 1640 goBack: function() 1641 { 1642 this._inHistory = true; 1643 WebInspector.currentPanel = WebInspector.panels[this._history[--this._historyIterator]]; 1644 delete this._inHistory; 1645 }, 1646 1647 canGoForward: function() 1648 { 1649 return this._historyIterator < this._history.length - 1; 1650 }, 1651 1652 goForward: function() 1653 { 1654 this._inHistory = true; 1655 WebInspector.currentPanel = WebInspector.panels[this._history[++this._historyIterator]]; 1656 delete this._inHistory; 1657 }, 1658 1659 setPanel: function(panelName) 1660 { 1661 if (this._inHistory) 1662 return; 1663 1664 this._history.splice(this._historyIterator + 1, this._history.length - this._historyIterator - 1); 1665 if (!this._history.length || this._history[this._history.length - 1] !== panelName) 1666 this._history.push(panelName); 1667 this._historyIterator = this._history.length - 1; 1668 } 1669 } 1670