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