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 31function preloadImages() 32{ 33 (new Image()).src = "Images/clearConsoleButtonGlyph.png"; 34 (new Image()).src = "Images/consoleButtonGlyph.png"; 35 (new Image()).src = "Images/dockButtonGlyph.png"; 36 (new Image()).src = "Images/enableOutlineButtonGlyph.png"; 37 (new Image()).src = "Images/enableSolidButtonGlyph.png"; 38 (new Image()).src = "Images/excludeButtonGlyph.png"; 39 (new Image()).src = "Images/focusButtonGlyph.png"; 40 (new Image()).src = "Images/largerResourcesButtonGlyph.png"; 41 (new Image()).src = "Images/nodeSearchButtonGlyph.png"; 42 (new Image()).src = "Images/pauseOnExceptionButtonGlyph.png"; 43 (new Image()).src = "Images/percentButtonGlyph.png"; 44 (new Image()).src = "Images/recordButtonGlyph.png"; 45 (new Image()).src = "Images/recordToggledButtonGlyph.png"; 46 (new Image()).src = "Images/reloadButtonGlyph.png"; 47 (new Image()).src = "Images/undockButtonGlyph.png"; 48} 49 50preloadImages(); 51 52var WebInspector = { 53 resources: {}, 54 resourceURLMap: {}, 55 cookieDomains: {}, 56 missingLocalizedStrings: {}, 57 pendingDispatches: 0, 58 59 // RegExp groups: 60 // 1 - scheme 61 // 2 - hostname 62 // 3 - ?port 63 // 4 - ?path 64 // 5 - ?fragment 65 URLRegExp: /^(http[s]?|file):\/\/([^\/:]*)(?::([\d]+))?(?:(\/[^#]*)(?:#(.*))?)?$/i, 66 67 get platform() 68 { 69 if (!("_platform" in this)) 70 this._platform = InspectorFrontendHost.platform(); 71 72 return this._platform; 73 }, 74 75 get platformFlavor() 76 { 77 if (!("_platformFlavor" in this)) 78 this._platformFlavor = this._detectPlatformFlavor(); 79 80 return this._platformFlavor; 81 }, 82 83 _detectPlatformFlavor: function() 84 { 85 const userAgent = navigator.userAgent; 86 87 if (this.platform === "windows") { 88 var match = userAgent.match(/Windows NT (\d+)\.(?:\d+)/); 89 if (match && match[1] >= 6) 90 return WebInspector.PlatformFlavor.WindowsVista; 91 return null; 92 } else if (this.platform === "mac") { 93 var match = userAgent.match(/Mac OS X\s*(?:(\d+)_(\d+))?/); 94 if (!match || match[1] != 10) 95 return WebInspector.PlatformFlavor.MacSnowLeopard; 96 switch (Number(match[2])) { 97 case 4: 98 return WebInspector.PlatformFlavor.MacTiger; 99 case 5: 100 return WebInspector.PlatformFlavor.MacLeopard; 101 case 6: 102 default: 103 return WebInspector.PlatformFlavor.MacSnowLeopard; 104 } 105 } 106 107 return null; 108 }, 109 110 get port() 111 { 112 if (!("_port" in this)) 113 this._port = InspectorFrontendHost.port(); 114 115 return this._port; 116 }, 117 118 get previousFocusElement() 119 { 120 return this._previousFocusElement; 121 }, 122 123 get currentFocusElement() 124 { 125 return this._currentFocusElement; 126 }, 127 128 set currentFocusElement(x) 129 { 130 if (this._currentFocusElement !== x) 131 this._previousFocusElement = this._currentFocusElement; 132 this._currentFocusElement = x; 133 134 if (this._currentFocusElement) { 135 this._currentFocusElement.focus(); 136 137 // Make a caret selection inside the new element if there isn't a range selection and 138 // there isn't already a caret selection inside. 139 var selection = window.getSelection(); 140 if (selection.isCollapsed && !this._currentFocusElement.isInsertionCaretInside()) { 141 var selectionRange = this._currentFocusElement.ownerDocument.createRange(); 142 selectionRange.setStart(this._currentFocusElement, 0); 143 selectionRange.setEnd(this._currentFocusElement, 0); 144 145 selection.removeAllRanges(); 146 selection.addRange(selectionRange); 147 } 148 } else if (this._previousFocusElement) 149 this._previousFocusElement.blur(); 150 }, 151 152 get currentPanel() 153 { 154 return this._currentPanel; 155 }, 156 157 set currentPanel(x) 158 { 159 if (this._currentPanel === x) 160 return; 161 162 if (this._currentPanel) 163 this._currentPanel.hide(); 164 165 this._currentPanel = x; 166 167 this.updateSearchLabel(); 168 169 if (x) { 170 x.show(); 171 172 if (this.currentQuery) { 173 if (x.performSearch) { 174 function performPanelSearch() 175 { 176 this.updateSearchMatchesCount(); 177 178 x.currentQuery = this.currentQuery; 179 x.performSearch(this.currentQuery); 180 } 181 182 // Perform the search on a timeout so the panel switches fast. 183 setTimeout(performPanelSearch.bind(this), 0); 184 } else { 185 // Update to show Not found for panels that can't be searched. 186 this.updateSearchMatchesCount(); 187 } 188 } 189 } 190 191 for (var panelName in WebInspector.panels) { 192 if (WebInspector.panels[panelName] == x) 193 InspectorBackend.storeLastActivePanel(panelName); 194 } 195 }, 196 197 _createPanels: function() 198 { 199 var hiddenPanels = (InspectorFrontendHost.hiddenPanels() || "").split(','); 200 if (hiddenPanels.indexOf("elements") === -1) 201 this.panels.elements = new WebInspector.ElementsPanel(); 202 if (hiddenPanels.indexOf("resources") === -1) 203 this.panels.resources = new WebInspector.ResourcesPanel(); 204 if (hiddenPanels.indexOf("scripts") === -1) 205 this.panels.scripts = new WebInspector.ScriptsPanel(); 206 if (hiddenPanels.indexOf("timeline") === -1) 207 this.panels.timeline = new WebInspector.TimelinePanel(); 208 if (hiddenPanels.indexOf("profiles") === -1) { 209 this.panels.profiles = new WebInspector.ProfilesPanel(); 210 this.panels.profiles.registerProfileType(new WebInspector.CPUProfileType()); 211 } 212 213 if (hiddenPanels.indexOf("storage") === -1 && hiddenPanels.indexOf("databases") === -1) 214 this.panels.storage = new WebInspector.StoragePanel(); 215 216 // FIXME: Uncomment when ready. 217 // if (hiddenPanels.indexOf("audits") === -1) 218 // this.panels.audits = new WebInspector.AuditsPanel(); 219 220 if (hiddenPanels.indexOf("console") === -1) 221 this.panels.console = new WebInspector.ConsolePanel(); 222 }, 223 224 get attached() 225 { 226 return this._attached; 227 }, 228 229 set attached(x) 230 { 231 if (this._attached === x) 232 return; 233 234 this._attached = x; 235 236 this.updateSearchLabel(); 237 238 var dockToggleButton = document.getElementById("dock-status-bar-item"); 239 var body = document.body; 240 241 if (x) { 242 InspectorFrontendHost.attach(); 243 body.removeStyleClass("detached"); 244 body.addStyleClass("attached"); 245 dockToggleButton.title = WebInspector.UIString("Undock into separate window."); 246 } else { 247 InspectorFrontendHost.detach(); 248 body.removeStyleClass("attached"); 249 body.addStyleClass("detached"); 250 dockToggleButton.title = WebInspector.UIString("Dock to main window."); 251 } 252 }, 253 254 get errors() 255 { 256 return this._errors || 0; 257 }, 258 259 set errors(x) 260 { 261 x = Math.max(x, 0); 262 263 if (this._errors === x) 264 return; 265 this._errors = x; 266 this._updateErrorAndWarningCounts(); 267 }, 268 269 get warnings() 270 { 271 return this._warnings || 0; 272 }, 273 274 set warnings(x) 275 { 276 x = Math.max(x, 0); 277 278 if (this._warnings === x) 279 return; 280 this._warnings = x; 281 this._updateErrorAndWarningCounts(); 282 }, 283 284 _updateErrorAndWarningCounts: function() 285 { 286 var errorWarningElement = document.getElementById("error-warning-count"); 287 if (!errorWarningElement) 288 return; 289 290 if (!this.errors && !this.warnings) { 291 errorWarningElement.addStyleClass("hidden"); 292 return; 293 } 294 295 errorWarningElement.removeStyleClass("hidden"); 296 297 errorWarningElement.removeChildren(); 298 299 if (this.errors) { 300 var errorElement = document.createElement("span"); 301 errorElement.id = "error-count"; 302 errorElement.textContent = this.errors; 303 errorWarningElement.appendChild(errorElement); 304 } 305 306 if (this.warnings) { 307 var warningsElement = document.createElement("span"); 308 warningsElement.id = "warning-count"; 309 warningsElement.textContent = this.warnings; 310 errorWarningElement.appendChild(warningsElement); 311 } 312 313 if (this.errors) { 314 if (this.warnings) { 315 if (this.errors == 1) { 316 if (this.warnings == 1) 317 errorWarningElement.title = WebInspector.UIString("%d error, %d warning", this.errors, this.warnings); 318 else 319 errorWarningElement.title = WebInspector.UIString("%d error, %d warnings", this.errors, this.warnings); 320 } else if (this.warnings == 1) 321 errorWarningElement.title = WebInspector.UIString("%d errors, %d warning", this.errors, this.warnings); 322 else 323 errorWarningElement.title = WebInspector.UIString("%d errors, %d warnings", this.errors, this.warnings); 324 } else if (this.errors == 1) 325 errorWarningElement.title = WebInspector.UIString("%d error", this.errors); 326 else 327 errorWarningElement.title = WebInspector.UIString("%d errors", this.errors); 328 } else if (this.warnings == 1) 329 errorWarningElement.title = WebInspector.UIString("%d warning", this.warnings); 330 else if (this.warnings) 331 errorWarningElement.title = WebInspector.UIString("%d warnings", this.warnings); 332 else 333 errorWarningElement.title = null; 334 }, 335 336 get styleChanges() 337 { 338 return this._styleChanges; 339 }, 340 341 set styleChanges(x) 342 { 343 x = Math.max(x, 0); 344 345 if (this._styleChanges === x) 346 return; 347 this._styleChanges = x; 348 this._updateChangesCount(); 349 }, 350 351 _updateChangesCount: function() 352 { 353 // TODO: Remove immediate return when enabling the Changes Panel 354 return; 355 356 var changesElement = document.getElementById("changes-count"); 357 if (!changesElement) 358 return; 359 360 if (!this.styleChanges) { 361 changesElement.addStyleClass("hidden"); 362 return; 363 } 364 365 changesElement.removeStyleClass("hidden"); 366 changesElement.removeChildren(); 367 368 if (this.styleChanges) { 369 var styleChangesElement = document.createElement("span"); 370 styleChangesElement.id = "style-changes-count"; 371 styleChangesElement.textContent = this.styleChanges; 372 changesElement.appendChild(styleChangesElement); 373 } 374 375 if (this.styleChanges) { 376 if (this.styleChanges === 1) 377 changesElement.title = WebInspector.UIString("%d style change", this.styleChanges); 378 else 379 changesElement.title = WebInspector.UIString("%d style changes", this.styleChanges); 380 } 381 }, 382 383 get hoveredDOMNode() 384 { 385 return this._hoveredDOMNode; 386 }, 387 388 set hoveredDOMNode(x) 389 { 390 if (this._hoveredDOMNode === x) 391 return; 392 393 this._hoveredDOMNode = x; 394 395 if (this._hoveredDOMNode) 396 this._updateHoverHighlightSoon(this.showingDOMNodeHighlight ? 50 : 500); 397 else 398 this._updateHoverHighlight(); 399 }, 400 401 _updateHoverHighlightSoon: function(delay) 402 { 403 if ("_updateHoverHighlightTimeout" in this) 404 clearTimeout(this._updateHoverHighlightTimeout); 405 this._updateHoverHighlightTimeout = setTimeout(this._updateHoverHighlight.bind(this), delay); 406 }, 407 408 _updateHoverHighlight: function() 409 { 410 if ("_updateHoverHighlightTimeout" in this) { 411 clearTimeout(this._updateHoverHighlightTimeout); 412 delete this._updateHoverHighlightTimeout; 413 } 414 415 if (this._hoveredDOMNode) { 416 InspectorBackend.highlightDOMNode(this._hoveredDOMNode.id); 417 this.showingDOMNodeHighlight = true; 418 } else { 419 InspectorBackend.hideDOMNodeHighlight(); 420 this.showingDOMNodeHighlight = false; 421 } 422 } 423} 424 425WebInspector.PlatformFlavor = { 426 WindowsVista: "windows-vista", 427 MacTiger: "mac-tiger", 428 MacLeopard: "mac-leopard", 429 MacSnowLeopard: "mac-snowleopard" 430} 431 432WebInspector.loaded = function() 433{ 434 InspectorBackend.setInjectedScriptSource("(" + injectedScriptConstructor + ");"); 435 436 var platform = WebInspector.platform; 437 document.body.addStyleClass("platform-" + platform); 438 var flavor = WebInspector.platformFlavor; 439 if (flavor) 440 document.body.addStyleClass("platform-" + flavor); 441 var port = WebInspector.port; 442 document.body.addStyleClass("port-" + port); 443 444 this.settings = new WebInspector.Settings(); 445 446 this.drawer = new WebInspector.Drawer(); 447 this.console = new WebInspector.ConsoleView(this.drawer); 448 // TODO: Uncomment when enabling the Changes Panel 449 // this.changes = new WebInspector.ChangesView(this.drawer); 450 // TODO: Remove class="hidden" from inspector.html on button#changes-status-bar-item 451 this.drawer.visibleView = this.console; 452 this.domAgent = new WebInspector.DOMAgent(); 453 454 this.resourceCategories = { 455 documents: new WebInspector.ResourceCategory("documents", WebInspector.UIString("Documents"), "rgb(47,102,236)"), 456 stylesheets: new WebInspector.ResourceCategory("stylesheets", WebInspector.UIString("Stylesheets"), "rgb(157,231,119)"), 457 images: new WebInspector.ResourceCategory("images", WebInspector.UIString("Images"), "rgb(164,60,255)"), 458 scripts: new WebInspector.ResourceCategory("scripts", WebInspector.UIString("Scripts"), "rgb(255,121,0)"), 459 xhr: new WebInspector.ResourceCategory("xhr", WebInspector.UIString("XHR"), "rgb(231,231,10)"), 460 fonts: new WebInspector.ResourceCategory("fonts", WebInspector.UIString("Fonts"), "rgb(255,82,62)"), 461 other: new WebInspector.ResourceCategory("other", WebInspector.UIString("Other"), "rgb(186,186,186)") 462 }; 463 464 this.panels = {}; 465 this._createPanels(); 466 467 var toolbarElement = document.getElementById("toolbar"); 468 var previousToolbarItem = toolbarElement.children[0]; 469 470 this.panelOrder = []; 471 for (var panelName in this.panels) 472 previousToolbarItem = WebInspector.addPanelToolbarIcon(toolbarElement, this.panels[panelName], previousToolbarItem); 473 474 this.Tips = { 475 ResourceNotCompressed: {id: 0, message: WebInspector.UIString("You could save bandwidth by having your web server compress this transfer with gzip or zlib.")} 476 }; 477 478 this.Warnings = { 479 IncorrectMIMEType: {id: 0, message: WebInspector.UIString("Resource interpreted as %s but transferred with MIME type %s.")} 480 }; 481 482 this.addMainEventListeners(document); 483 484 window.addEventListener("unload", this.windowUnload.bind(this), true); 485 window.addEventListener("resize", this.windowResize.bind(this), true); 486 487 document.addEventListener("focus", this.focusChanged.bind(this), true); 488 document.addEventListener("keydown", this.documentKeyDown.bind(this), false); 489 document.addEventListener("beforecopy", this.documentCanCopy.bind(this), true); 490 document.addEventListener("copy", this.documentCopy.bind(this), true); 491 document.addEventListener("contextmenu", this.contextMenuEventFired.bind(this), true); 492 493 var dockToggleButton = document.getElementById("dock-status-bar-item"); 494 dockToggleButton.addEventListener("click", this.toggleAttach.bind(this), false); 495 496 if (this.attached) 497 dockToggleButton.title = WebInspector.UIString("Undock into separate window."); 498 else 499 dockToggleButton.title = WebInspector.UIString("Dock to main window."); 500 501 var errorWarningCount = document.getElementById("error-warning-count"); 502 errorWarningCount.addEventListener("click", this.showConsole.bind(this), false); 503 this._updateErrorAndWarningCounts(); 504 505 this.styleChanges = 0; 506 // TODO: Uncomment when enabling the Changes Panel 507 // var changesElement = document.getElementById("changes-count"); 508 // changesElement.addEventListener("click", this.showChanges.bind(this), false); 509 // this._updateErrorAndWarningCounts(); 510 511 var searchField = document.getElementById("search"); 512 searchField.addEventListener("search", this.performSearch.bind(this), false); // when the search is emptied 513 searchField.addEventListener("mousedown", this._searchFieldManualFocus.bind(this), false); // when the search field is manually selected 514 searchField.addEventListener("keydown", this._searchKeyDown.bind(this), true); 515 516 toolbarElement.addEventListener("mousedown", this.toolbarDragStart, true); 517 document.getElementById("close-button-left").addEventListener("click", this.close, true); 518 document.getElementById("close-button-right").addEventListener("click", this.close, true); 519 520 InspectorFrontendHost.loaded(); 521} 522 523WebInspector.addPanelToolbarIcon = function(toolbarElement, panel, previousToolbarItem) 524{ 525 var panelToolbarItem = panel.toolbarItem; 526 this.panelOrder.push(panel); 527 panelToolbarItem.addEventListener("click", this._toolbarItemClicked.bind(this)); 528 if (previousToolbarItem) 529 toolbarElement.insertBefore(panelToolbarItem, previousToolbarItem.nextSibling); 530 else 531 toolbarElement.insertBefore(panelToolbarItem, toolbarElement.firstChild); 532 return panelToolbarItem; 533} 534 535var windowLoaded = function() 536{ 537 var localizedStringsURL = InspectorFrontendHost.localizedStringsURL(); 538 if (localizedStringsURL) { 539 var localizedStringsScriptElement = document.createElement("script"); 540 localizedStringsScriptElement.addEventListener("load", WebInspector.loaded.bind(WebInspector), false); 541 localizedStringsScriptElement.type = "text/javascript"; 542 localizedStringsScriptElement.src = localizedStringsURL; 543 document.head.appendChild(localizedStringsScriptElement); 544 } else 545 WebInspector.loaded(); 546 547 window.removeEventListener("load", windowLoaded, false); 548 delete windowLoaded; 549}; 550 551window.addEventListener("load", windowLoaded, false); 552 553WebInspector.dispatch = function() { 554 var methodName = arguments[0]; 555 var parameters = Array.prototype.slice.call(arguments, 1); 556 557 // We'd like to enforce asynchronous interaction between the inspector controller and the frontend. 558 // This is important to LayoutTests. 559 function delayDispatch() 560 { 561 WebInspector[methodName].apply(WebInspector, parameters); 562 WebInspector.pendingDispatches--; 563 } 564 WebInspector.pendingDispatches++; 565 setTimeout(delayDispatch, 0); 566} 567 568WebInspector.windowUnload = function(event) 569{ 570 InspectorFrontendHost.windowUnloading(); 571} 572 573WebInspector.windowResize = function(event) 574{ 575 if (this.currentPanel) 576 this.currentPanel.resize(); 577 this.drawer.resize(); 578} 579 580WebInspector.windowFocused = function(event) 581{ 582 // Fires after blur, so when focusing on either the main inspector 583 // or an <iframe> within the inspector we should always remove the 584 // "inactive" class. 585 if (event.target.document.nodeType === Node.DOCUMENT_NODE) 586 document.body.removeStyleClass("inactive"); 587} 588 589WebInspector.windowBlurred = function(event) 590{ 591 // Leaving the main inspector or an <iframe> within the inspector. 592 // We can add "inactive" now, and if we are moving the focus to another 593 // part of the inspector then windowFocused will correct this. 594 if (event.target.document.nodeType === Node.DOCUMENT_NODE) 595 document.body.addStyleClass("inactive"); 596} 597 598WebInspector.focusChanged = function(event) 599{ 600 this.currentFocusElement = event.target; 601} 602 603WebInspector.setAttachedWindow = function(attached) 604{ 605 this.attached = attached; 606} 607 608WebInspector.close = function(event) 609{ 610 InspectorFrontendHost.closeWindow(); 611} 612 613WebInspector.documentClick = function(event) 614{ 615 var anchor = event.target.enclosingNodeOrSelfWithNodeName("a"); 616 if (!anchor) 617 return; 618 619 // Prevent the link from navigating, since we don't do any navigation by following links normally. 620 event.preventDefault(); 621 622 function followLink() 623 { 624 // FIXME: support webkit-html-external-link links here. 625 if (WebInspector.canShowSourceLineForURL(anchor.href, anchor.preferredPanel)) { 626 if (anchor.hasStyleClass("webkit-html-external-link")) { 627 anchor.removeStyleClass("webkit-html-external-link"); 628 anchor.addStyleClass("webkit-html-resource-link"); 629 } 630 631 WebInspector.showSourceLineForURL(anchor.href, anchor.lineNumber, anchor.preferredPanel); 632 } else { 633 var profileString = WebInspector.ProfileType.URLRegExp.exec(anchor.href); 634 if (profileString) 635 WebInspector.showProfileForURL(anchor.href); 636 } 637 } 638 639 if (WebInspector.followLinkTimeout) 640 clearTimeout(WebInspector.followLinkTimeout); 641 642 if (anchor.preventFollowOnDoubleClick) { 643 // Start a timeout if this is the first click, if the timeout is canceled 644 // before it fires, then a double clicked happened or another link was clicked. 645 if (event.detail === 1) 646 WebInspector.followLinkTimeout = setTimeout(followLink, 333); 647 return; 648 } 649 650 followLink(); 651} 652 653WebInspector.documentKeyDown = function(event) 654{ 655 if (this.currentFocusElement && this.currentFocusElement.handleKeyEvent) { 656 this.currentFocusElement.handleKeyEvent(event); 657 if (event.handled) { 658 event.preventDefault(); 659 return; 660 } 661 } 662 663 if (this.currentPanel && this.currentPanel.handleShortcut) { 664 this.currentPanel.handleShortcut(event); 665 if (event.handled) { 666 event.preventDefault(); 667 return; 668 } 669 } 670 671 var isMac = WebInspector.isMac(); 672 673 switch (event.keyIdentifier) { 674 case "U+001B": // Escape key 675 event.preventDefault(); 676 if (this.drawer.fullPanel) 677 return; 678 679 this.drawer.visible = !this.drawer.visible; 680 break; 681 682 case "U+0046": // F key 683 if (isMac) 684 var isFindKey = event.metaKey && !event.ctrlKey && !event.altKey && !event.shiftKey; 685 else 686 var isFindKey = event.ctrlKey && !event.metaKey && !event.altKey && !event.shiftKey; 687 688 if (isFindKey) { 689 var searchField = document.getElementById("search"); 690 searchField.focus(); 691 searchField.select(); 692 event.preventDefault(); 693 } 694 695 break; 696 697 case "U+0047": // G key 698 if (isMac) 699 var isFindAgainKey = event.metaKey && !event.ctrlKey && !event.altKey; 700 else 701 var isFindAgainKey = event.ctrlKey && !event.metaKey && !event.altKey; 702 703 if (isFindAgainKey) { 704 if (event.shiftKey) { 705 if (this.currentPanel.jumpToPreviousSearchResult) 706 this.currentPanel.jumpToPreviousSearchResult(); 707 } else if (this.currentPanel.jumpToNextSearchResult) 708 this.currentPanel.jumpToNextSearchResult(); 709 event.preventDefault(); 710 } 711 712 break; 713 714 // Windows and Mac have two different definitions of [, so accept both. 715 case "U+005B": 716 case "U+00DB": // [ key 717 if (isMac) 718 var isRotateLeft = event.metaKey && !event.shiftKey && !event.ctrlKey && !event.altKey; 719 else 720 var isRotateLeft = event.ctrlKey && !event.shiftKey && !event.metaKey && !event.altKey; 721 722 if (isRotateLeft) { 723 var index = this.panelOrder.indexOf(this.currentPanel); 724 index = (index === 0) ? this.panelOrder.length - 1 : index - 1; 725 this.panelOrder[index].toolbarItem.click(); 726 event.preventDefault(); 727 } 728 729 break; 730 731 // Windows and Mac have two different definitions of ], so accept both. 732 case "U+005D": 733 case "U+00DD": // ] key 734 if (isMac) 735 var isRotateRight = event.metaKey && !event.shiftKey && !event.ctrlKey && !event.altKey; 736 else 737 var isRotateRight = event.ctrlKey && !event.shiftKey && !event.metaKey && !event.altKey; 738 739 if (isRotateRight) { 740 var index = this.panelOrder.indexOf(this.currentPanel); 741 index = (index + 1) % this.panelOrder.length; 742 this.panelOrder[index].toolbarItem.click(); 743 event.preventDefault(); 744 } 745 746 break; 747 748 case "U+0041": // A key 749 if (isMac) 750 var shouldShowAuditsPanel = event.metaKey && !event.shiftKey && !event.ctrlKey && event.altKey; 751 else 752 var shouldShowAuditsPanel = event.ctrlKey && !event.shiftKey && !event.metaKey && event.altKey; 753 754 if (shouldShowAuditsPanel) { 755 if (!this.panels.audits) { 756 this.panels.audits = new WebInspector.AuditsPanel(); 757 var toolbarElement = document.getElementById("toolbar"); 758 WebInspector.addPanelToolbarIcon(toolbarElement, this.panels.audits, this.panels.console.toolbarItem); 759 } 760 this.currentPanel = this.panels.audits; 761 } 762 763 break; 764 } 765} 766 767WebInspector.documentCanCopy = function(event) 768{ 769 if (this.currentPanel && this.currentPanel.handleCopyEvent) 770 event.preventDefault(); 771} 772 773WebInspector.documentCopy = function(event) 774{ 775 if (this.currentPanel && this.currentPanel.handleCopyEvent) 776 this.currentPanel.handleCopyEvent(event); 777} 778 779WebInspector.contextMenuEventFired = function(event) 780{ 781 if (event.handled || event.target.hasStyleClass("popup-glasspane")) 782 event.preventDefault(); 783} 784 785WebInspector.animateStyle = function(animations, duration, callback) 786{ 787 var interval; 788 var complete = 0; 789 790 const intervalDuration = (1000 / 30); // 30 frames per second. 791 const animationsLength = animations.length; 792 const propertyUnit = {opacity: ""}; 793 const defaultUnit = "px"; 794 795 function cubicInOut(t, b, c, d) 796 { 797 if ((t/=d/2) < 1) return c/2*t*t*t + b; 798 return c/2*((t-=2)*t*t + 2) + b; 799 } 800 801 // Pre-process animations. 802 for (var i = 0; i < animationsLength; ++i) { 803 var animation = animations[i]; 804 var element = null, start = null, end = null, key = null; 805 for (key in animation) { 806 if (key === "element") 807 element = animation[key]; 808 else if (key === "start") 809 start = animation[key]; 810 else if (key === "end") 811 end = animation[key]; 812 } 813 814 if (!element || !end) 815 continue; 816 817 if (!start) { 818 var computedStyle = element.ownerDocument.defaultView.getComputedStyle(element); 819 start = {}; 820 for (key in end) 821 start[key] = parseInt(computedStyle.getPropertyValue(key)); 822 animation.start = start; 823 } else 824 for (key in start) 825 element.style.setProperty(key, start[key] + (key in propertyUnit ? propertyUnit[key] : defaultUnit)); 826 } 827 828 function animateLoop() 829 { 830 // Advance forward. 831 complete += intervalDuration; 832 var next = complete + intervalDuration; 833 834 // Make style changes. 835 for (var i = 0; i < animationsLength; ++i) { 836 var animation = animations[i]; 837 var element = animation.element; 838 var start = animation.start; 839 var end = animation.end; 840 if (!element || !end) 841 continue; 842 843 var style = element.style; 844 for (key in end) { 845 var endValue = end[key]; 846 if (next < duration) { 847 var startValue = start[key]; 848 var newValue = cubicInOut(complete, startValue, endValue - startValue, duration); 849 style.setProperty(key, newValue + (key in propertyUnit ? propertyUnit[key] : defaultUnit)); 850 } else 851 style.setProperty(key, endValue + (key in propertyUnit ? propertyUnit[key] : defaultUnit)); 852 } 853 } 854 855 // End condition. 856 if (complete >= duration) { 857 clearInterval(interval); 858 if (callback) 859 callback(); 860 } 861 } 862 863 interval = setInterval(animateLoop, intervalDuration); 864 return interval; 865} 866 867WebInspector.updateSearchLabel = function() 868{ 869 if (!this.currentPanel) 870 return; 871 872 var newLabel = WebInspector.UIString("Search %s", this.currentPanel.toolbarItemLabel); 873 if (this.attached) 874 document.getElementById("search").setAttribute("placeholder", newLabel); 875 else { 876 document.getElementById("search").removeAttribute("placeholder"); 877 document.getElementById("search-toolbar-label").textContent = newLabel; 878 } 879} 880 881WebInspector.toggleAttach = function() 882{ 883 this.attached = !this.attached; 884 this.drawer.resize(); 885} 886 887WebInspector.toolbarDragStart = function(event) 888{ 889 if ((!WebInspector.attached && WebInspector.platformFlavor !== WebInspector.PlatformFlavor.MacLeopard && WebInspector.platformFlavor !== WebInspector.PlatformFlavor.MacSnowLeopard) || WebInspector.port == "qt") 890 return; 891 892 var target = event.target; 893 if (target.hasStyleClass("toolbar-item") && target.hasStyleClass("toggleable")) 894 return; 895 896 var toolbar = document.getElementById("toolbar"); 897 if (target !== toolbar && !target.hasStyleClass("toolbar-item")) 898 return; 899 900 toolbar.lastScreenX = event.screenX; 901 toolbar.lastScreenY = event.screenY; 902 903 WebInspector.elementDragStart(toolbar, WebInspector.toolbarDrag, WebInspector.toolbarDragEnd, event, (WebInspector.attached ? "row-resize" : "default")); 904} 905 906WebInspector.toolbarDragEnd = function(event) 907{ 908 var toolbar = document.getElementById("toolbar"); 909 910 WebInspector.elementDragEnd(event); 911 912 delete toolbar.lastScreenX; 913 delete toolbar.lastScreenY; 914} 915 916WebInspector.toolbarDrag = function(event) 917{ 918 var toolbar = document.getElementById("toolbar"); 919 920 if (WebInspector.attached) { 921 var height = window.innerHeight - (event.screenY - toolbar.lastScreenY); 922 923 InspectorFrontendHost.setAttachedWindowHeight(height); 924 } else { 925 var x = event.screenX - toolbar.lastScreenX; 926 var y = event.screenY - toolbar.lastScreenY; 927 928 // We cannot call window.moveBy here because it restricts the movement 929 // of the window at the edges. 930 InspectorFrontendHost.moveWindowBy(x, y); 931 } 932 933 toolbar.lastScreenX = event.screenX; 934 toolbar.lastScreenY = event.screenY; 935 936 event.preventDefault(); 937} 938 939WebInspector.elementDragStart = function(element, dividerDrag, elementDragEnd, event, cursor) 940{ 941 if (this._elementDraggingEventListener || this._elementEndDraggingEventListener) 942 this.elementDragEnd(event); 943 944 this._elementDraggingEventListener = dividerDrag; 945 this._elementEndDraggingEventListener = elementDragEnd; 946 947 document.addEventListener("mousemove", dividerDrag, true); 948 document.addEventListener("mouseup", elementDragEnd, true); 949 950 document.body.style.cursor = cursor; 951 952 event.preventDefault(); 953} 954 955WebInspector.elementDragEnd = function(event) 956{ 957 document.removeEventListener("mousemove", this._elementDraggingEventListener, true); 958 document.removeEventListener("mouseup", this._elementEndDraggingEventListener, true); 959 960 document.body.style.removeProperty("cursor"); 961 962 delete this._elementDraggingEventListener; 963 delete this._elementEndDraggingEventListener; 964 965 event.preventDefault(); 966} 967 968WebInspector.showConsole = function() 969{ 970 this.drawer.showView(this.console); 971} 972 973WebInspector.showChanges = function() 974{ 975 this.drawer.showView(this.changes); 976} 977 978WebInspector.showElementsPanel = function() 979{ 980 this.currentPanel = this.panels.elements; 981} 982 983WebInspector.showResourcesPanel = function() 984{ 985 this.currentPanel = this.panels.resources; 986} 987 988WebInspector.showScriptsPanel = function() 989{ 990 this.currentPanel = this.panels.scripts; 991} 992 993WebInspector.showTimelinePanel = function() 994{ 995 this.currentPanel = this.panels.timeline; 996} 997 998WebInspector.showProfilesPanel = function() 999{ 1000 this.currentPanel = this.panels.profiles; 1001} 1002 1003WebInspector.showStoragePanel = function() 1004{ 1005 this.currentPanel = this.panels.storage; 1006} 1007 1008WebInspector.showConsolePanel = function() 1009{ 1010 this.currentPanel = this.panels.console; 1011} 1012 1013WebInspector.clearConsoleMessages = function() 1014{ 1015 WebInspector.console.clearMessages(); 1016} 1017 1018WebInspector.selectDatabase = function(o) 1019{ 1020 WebInspector.showStoragePanel(); 1021 WebInspector.panels.storage.selectDatabase(o); 1022} 1023 1024WebInspector.selectDOMStorage = function(o) 1025{ 1026 WebInspector.showStoragePanel(); 1027 WebInspector.panels.storage.selectDOMStorage(o); 1028} 1029 1030WebInspector.updateResource = function(identifier, payload) 1031{ 1032 var resource = this.resources[identifier]; 1033 if (!resource) { 1034 resource = new WebInspector.Resource(identifier, payload.url); 1035 this.resources[identifier] = resource; 1036 this.resourceURLMap[resource.url] = resource; 1037 if (this.panels.resources) 1038 this.panels.resources.addResource(resource); 1039 } 1040 1041 if (payload.didRequestChange) { 1042 resource.domain = payload.host; 1043 resource.path = payload.path; 1044 resource.lastPathComponent = payload.lastPathComponent; 1045 resource.requestHeaders = payload.requestHeaders; 1046 resource.mainResource = payload.mainResource; 1047 resource.requestMethod = payload.requestMethod; 1048 resource.requestFormData = payload.requestFormData; 1049 resource.cached = payload.cached; 1050 resource.documentURL = payload.documentURL; 1051 1052 if (resource.mainResource) 1053 this.mainResource = resource; 1054 1055 var match = payload.documentURL.match(WebInspector.URLRegExp); 1056 if (match) { 1057 var protocol = match[1].toLowerCase(); 1058 if (protocol.indexOf("http") === 0 || protocol === "file") 1059 this._addCookieDomain(protocol === "file" ? "" : match[2]); 1060 } 1061 } 1062 1063 if (payload.didResponseChange) { 1064 resource.mimeType = payload.mimeType; 1065 resource.suggestedFilename = payload.suggestedFilename; 1066 resource.expectedContentLength = payload.expectedContentLength; 1067 resource.statusCode = payload.statusCode; 1068 resource.suggestedFilename = payload.suggestedFilename; 1069 resource.responseHeaders = payload.responseHeaders; 1070 } 1071 1072 if (payload.didTypeChange) { 1073 resource.type = payload.type; 1074 } 1075 1076 if (payload.didLengthChange) { 1077 resource.contentLength = payload.contentLength; 1078 } 1079 1080 if (payload.didCompletionChange) { 1081 resource.failed = payload.failed; 1082 resource.finished = payload.finished; 1083 } 1084 1085 if (payload.didTimingChange) { 1086 if (payload.startTime) 1087 resource.startTime = payload.startTime; 1088 if (payload.responseReceivedTime) 1089 resource.responseReceivedTime = payload.responseReceivedTime; 1090 if (payload.endTime) 1091 resource.endTime = payload.endTime; 1092 1093 if (payload.loadEventTime) { 1094 // This loadEventTime is for the main resource, and we want to show it 1095 // for all resources on this page. This means we want to set it as a member 1096 // of the resources panel instead of the individual resource. 1097 if (this.panels.resources) 1098 this.panels.resources.mainResourceLoadTime = payload.loadEventTime; 1099 if (this.panels.audits) 1100 this.panels.audits.mainResourceLoadTime = payload.loadEventTime; 1101 } 1102 1103 if (payload.domContentEventTime) { 1104 // This domContentEventTime is for the main resource, so it should go in 1105 // the resources panel for the same reasons as above. 1106 if (this.panels.resources) 1107 this.panels.resources.mainResourceDOMContentTime = payload.domContentEventTime; 1108 if (this.panels.audits) 1109 this.panels.audits.mainResourceDOMContentTime = payload.domContentEventTime; 1110 } 1111 } 1112} 1113 1114WebInspector.removeResource = function(identifier) 1115{ 1116 var resource = this.resources[identifier]; 1117 if (!resource) 1118 return; 1119 1120 resource.category.removeResource(resource); 1121 delete this.resourceURLMap[resource.url]; 1122 delete this.resources[identifier]; 1123 1124 if (this.panels.resources) 1125 this.panels.resources.removeResource(resource); 1126} 1127 1128WebInspector.addDatabase = function(payload) 1129{ 1130 if (!this.panels.storage) 1131 return; 1132 var database = new WebInspector.Database( 1133 payload.id, 1134 payload.domain, 1135 payload.name, 1136 payload.version); 1137 this.panels.storage.addDatabase(database); 1138} 1139 1140WebInspector._addCookieDomain = function(domain) 1141{ 1142 // Eliminate duplicate domains from the list. 1143 if (domain in this.cookieDomains) 1144 return; 1145 this.cookieDomains[domain] = true; 1146 1147 if (!this.panels.storage) 1148 return; 1149 this.panels.storage.addCookieDomain(domain); 1150} 1151 1152WebInspector.addDOMStorage = function(payload) 1153{ 1154 if (!this.panels.storage) 1155 return; 1156 var domStorage = new WebInspector.DOMStorage( 1157 payload.id, 1158 payload.host, 1159 payload.isLocalStorage); 1160 this.panels.storage.addDOMStorage(domStorage); 1161} 1162 1163WebInspector.updateDOMStorage = function(storageId) 1164{ 1165 if (!this.panels.storage) 1166 return; 1167 this.panels.storage.updateDOMStorage(storageId); 1168} 1169 1170WebInspector.resourceTrackingWasEnabled = function() 1171{ 1172 this.panels.resources.resourceTrackingWasEnabled(); 1173} 1174 1175WebInspector.resourceTrackingWasDisabled = function() 1176{ 1177 this.panels.resources.resourceTrackingWasDisabled(); 1178} 1179 1180WebInspector.attachDebuggerWhenShown = function() 1181{ 1182 this.panels.scripts.attachDebuggerWhenShown(); 1183} 1184 1185WebInspector.debuggerWasEnabled = function() 1186{ 1187 this.panels.scripts.debuggerWasEnabled(); 1188} 1189 1190WebInspector.debuggerWasDisabled = function() 1191{ 1192 this.panels.scripts.debuggerWasDisabled(); 1193} 1194 1195WebInspector.profilerWasEnabled = function() 1196{ 1197 this.panels.profiles.profilerWasEnabled(); 1198} 1199 1200WebInspector.profilerWasDisabled = function() 1201{ 1202 this.panels.profiles.profilerWasDisabled(); 1203} 1204 1205WebInspector.parsedScriptSource = function(sourceID, sourceURL, source, startingLine) 1206{ 1207 this.panels.scripts.addScript(sourceID, sourceURL, source, startingLine); 1208} 1209 1210WebInspector.failedToParseScriptSource = function(sourceURL, source, startingLine, errorLine, errorMessage) 1211{ 1212 this.panels.scripts.addScript(null, sourceURL, source, startingLine, errorLine, errorMessage); 1213} 1214 1215WebInspector.pausedScript = function(callFrames) 1216{ 1217 this.panels.scripts.debuggerPaused(callFrames); 1218} 1219 1220WebInspector.resumedScript = function() 1221{ 1222 this.panels.scripts.debuggerResumed(); 1223} 1224 1225WebInspector.populateInterface = function() 1226{ 1227 for (var panelName in this.panels) { 1228 var panel = this.panels[panelName]; 1229 if ("populateInterface" in panel) 1230 panel.populateInterface(); 1231 } 1232} 1233 1234WebInspector.reset = function() 1235{ 1236 for (var panelName in this.panels) { 1237 var panel = this.panels[panelName]; 1238 if ("reset" in panel) 1239 panel.reset(); 1240 } 1241 1242 for (var category in this.resourceCategories) 1243 this.resourceCategories[category].removeAllResources(); 1244 1245 this.resources = {}; 1246 this.resourceURLMap = {}; 1247 this.cookieDomains = {}; 1248 this.hoveredDOMNode = null; 1249 1250 delete this.mainResource; 1251 1252 this.console.clearMessages(); 1253} 1254 1255WebInspector.resourceURLChanged = function(resource, oldURL) 1256{ 1257 delete this.resourceURLMap[oldURL]; 1258 this.resourceURLMap[resource.url] = resource; 1259} 1260 1261WebInspector.didCommitLoad = function() 1262{ 1263 // Cleanup elements panel early on inspected page refresh. 1264 WebInspector.setDocument(null); 1265} 1266 1267WebInspector.updateConsoleMessageExpiredCount = function(count) 1268{ 1269 var message = String.sprintf(WebInspector.UIString("%d console messages are not shown."), count); 1270 WebInspector.console.addMessage(new WebInspector.ConsoleTextMessage(message, WebInspector.ConsoleMessage.MessageLevel.Warning)); 1271} 1272 1273WebInspector.addConsoleMessage = function(payload, opt_args) 1274{ 1275 var consoleMessage = new WebInspector.ConsoleMessage( 1276 payload.source, 1277 payload.type, 1278 payload.level, 1279 payload.line, 1280 payload.url, 1281 payload.groupLevel, 1282 payload.repeatCount); 1283 consoleMessage.setMessageBody(Array.prototype.slice.call(arguments, 1)); 1284 this.console.addMessage(consoleMessage); 1285} 1286 1287WebInspector.updateConsoleMessageRepeatCount = function(count) 1288{ 1289 this.console.updateMessageRepeatCount(count); 1290} 1291 1292WebInspector.log = function(message) 1293{ 1294 // remember 'this' for setInterval() callback 1295 var self = this; 1296 1297 // return indication if we can actually log a message 1298 function isLogAvailable() 1299 { 1300 return WebInspector.ConsoleMessage && WebInspector.ObjectProxy && self.console; 1301 } 1302 1303 // flush the queue of pending messages 1304 function flushQueue() 1305 { 1306 var queued = WebInspector.log.queued; 1307 if (!queued) 1308 return; 1309 1310 for (var i = 0; i < queued.length; ++i) 1311 logMessage(queued[i]); 1312 1313 delete WebInspector.log.queued; 1314 } 1315 1316 // flush the queue if it console is available 1317 // - this function is run on an interval 1318 function flushQueueIfAvailable() 1319 { 1320 if (!isLogAvailable()) 1321 return; 1322 1323 clearInterval(WebInspector.log.interval); 1324 delete WebInspector.log.interval; 1325 1326 flushQueue(); 1327 } 1328 1329 // actually log the message 1330 function logMessage(message) 1331 { 1332 var repeatCount = 1; 1333 if (message == WebInspector.log.lastMessage) 1334 repeatCount = WebInspector.log.repeatCount + 1; 1335 1336 WebInspector.log.lastMessage = message; 1337 WebInspector.log.repeatCount = repeatCount; 1338 1339 // ConsoleMessage expects a proxy object 1340 message = new WebInspector.ObjectProxy(null, null, [], 0, message, false); 1341 1342 // post the message 1343 var msg = new WebInspector.ConsoleMessage( 1344 WebInspector.ConsoleMessage.MessageSource.Other, 1345 WebInspector.ConsoleMessage.MessageType.Log, 1346 WebInspector.ConsoleMessage.MessageLevel.Debug, 1347 -1, 1348 null, 1349 null, 1350 repeatCount, 1351 message); 1352 1353 self.console.addMessage(msg); 1354 } 1355 1356 // if we can't log the message, queue it 1357 if (!isLogAvailable()) { 1358 if (!WebInspector.log.queued) 1359 WebInspector.log.queued = []; 1360 1361 WebInspector.log.queued.push(message); 1362 1363 if (!WebInspector.log.interval) 1364 WebInspector.log.interval = setInterval(flushQueueIfAvailable, 1000); 1365 1366 return; 1367 } 1368 1369 // flush the pending queue if any 1370 flushQueue(); 1371 1372 // log the message 1373 logMessage(message); 1374} 1375 1376WebInspector.addProfileHeader = function(profile) 1377{ 1378 this.panels.profiles.addProfileHeader(profile); 1379} 1380 1381WebInspector.setRecordingProfile = function(isProfiling) 1382{ 1383 this.panels.profiles.getProfileType(WebInspector.CPUProfileType.TypeId).setRecordingProfile(isProfiling); 1384 this.panels.profiles.updateProfileTypeButtons(); 1385} 1386 1387WebInspector.drawLoadingPieChart = function(canvas, percent) { 1388 var g = canvas.getContext("2d"); 1389 var darkColor = "rgb(122, 168, 218)"; 1390 var lightColor = "rgb(228, 241, 251)"; 1391 var cx = 8; 1392 var cy = 8; 1393 var r = 7; 1394 1395 g.beginPath(); 1396 g.arc(cx, cy, r, 0, Math.PI * 2, false); 1397 g.closePath(); 1398 1399 g.lineWidth = 1; 1400 g.strokeStyle = darkColor; 1401 g.fillStyle = lightColor; 1402 g.fill(); 1403 g.stroke(); 1404 1405 var startangle = -Math.PI / 2; 1406 var endangle = startangle + (percent * Math.PI * 2); 1407 1408 g.beginPath(); 1409 g.moveTo(cx, cy); 1410 g.arc(cx, cy, r, startangle, endangle, false); 1411 g.closePath(); 1412 1413 g.fillStyle = darkColor; 1414 g.fill(); 1415} 1416 1417WebInspector.updateFocusedNode = function(nodeId) 1418{ 1419 var node = WebInspector.domAgent.nodeForId(nodeId); 1420 if (!node) 1421 // FIXME: Should we deselect if null is passed in? 1422 return; 1423 1424 this.currentPanel = this.panels.elements; 1425 this.panels.elements.focusedDOMNode = node; 1426} 1427 1428WebInspector.displayNameForURL = function(url) 1429{ 1430 if (!url) 1431 return ""; 1432 var resource = this.resourceURLMap[url]; 1433 if (resource) 1434 return resource.displayName; 1435 return url.trimURL(WebInspector.mainResource ? WebInspector.mainResource.domain : ""); 1436} 1437 1438WebInspector.resourceForURL = function(url) 1439{ 1440 if (url in this.resourceURLMap) 1441 return this.resourceURLMap[url]; 1442 1443 // No direct match found. Search for resources that contain 1444 // a substring of the URL. 1445 for (var resourceURL in this.resourceURLMap) { 1446 if (resourceURL.hasSubstring(url)) 1447 return this.resourceURLMap[resourceURL]; 1448 } 1449 1450 return null; 1451} 1452 1453WebInspector._choosePanelToShowSourceLineForURL = function(url, preferredPanel) 1454{ 1455 preferredPanel = preferredPanel || "resources"; 1456 var panel = this.panels[preferredPanel]; 1457 if (panel && panel.canShowSourceLineForURL(url)) 1458 return panel; 1459 panel = this.panels.resources; 1460 return panel.canShowSourceLineForURL(url) ? panel : null; 1461} 1462 1463WebInspector.canShowSourceLineForURL = function(url, preferredPanel) 1464{ 1465 return !!this._choosePanelToShowSourceLineForURL(url, preferredPanel); 1466} 1467 1468WebInspector.showSourceLineForURL = function(url, line, preferredPanel) 1469{ 1470 this.currentPanel = this._choosePanelToShowSourceLineForURL(url, preferredPanel); 1471 if (!this.currentPanel) 1472 return false; 1473 this.currentPanel.showSourceLineForURL(url, line); 1474 return true; 1475} 1476 1477WebInspector.linkifyStringAsFragment = function(string) 1478{ 1479 var container = document.createDocumentFragment(); 1480 var linkStringRegEx = /(?:[a-zA-Z][a-zA-Z0-9+.-]{2,}:\/\/|www\.)[\w$\-_+*'=\|\/\\(){}[\]%@&#~,:;.!?]{2,}[\w$\-_+*=\|\/\\({%@&#~]/; 1481 1482 while (string) { 1483 var linkString = linkStringRegEx.exec(string); 1484 if (!linkString) 1485 break; 1486 1487 linkString = linkString[0]; 1488 var title = linkString; 1489 var linkIndex = string.indexOf(linkString); 1490 var nonLink = string.substring(0, linkIndex); 1491 container.appendChild(document.createTextNode(nonLink)); 1492 1493 var profileStringMatches = WebInspector.ProfileType.URLRegExp.exec(title); 1494 if (profileStringMatches) 1495 title = WebInspector.panels.profiles.displayTitleForProfileLink(profileStringMatches[2], profileStringMatches[1]); 1496 1497 var realURL = (linkString.indexOf("www.") === 0 ? "http://" + linkString : linkString); 1498 container.appendChild(WebInspector.linkifyURLAsNode(realURL, title, null, (realURL in WebInspector.resourceURLMap))); 1499 string = string.substring(linkIndex + linkString.length, string.length); 1500 } 1501 1502 if (string) 1503 container.appendChild(document.createTextNode(string)); 1504 1505 return container; 1506} 1507 1508WebInspector.showProfileForURL = function(url) 1509{ 1510 WebInspector.showProfilesPanel(); 1511 WebInspector.panels.profiles.showProfileForURL(url); 1512} 1513 1514WebInspector.linkifyURLAsNode = function(url, linkText, classes, isExternal, tooltipText) 1515{ 1516 if (!linkText) 1517 linkText = url; 1518 classes = (classes ? classes + " " : ""); 1519 classes += isExternal ? "webkit-html-external-link" : "webkit-html-resource-link"; 1520 1521 var a = document.createElement("a"); 1522 a.href = url; 1523 a.className = classes; 1524 a.title = tooltipText || url; 1525 a.target = "_blank"; 1526 a.textContent = linkText; 1527 1528 return a; 1529} 1530 1531WebInspector.linkifyURL = function(url, linkText, classes, isExternal, tooltipText) 1532{ 1533 // Use the DOM version of this function so as to avoid needing to escape attributes. 1534 // FIXME: Get rid of linkifyURL entirely. 1535 return WebInspector.linkifyURLAsNode(url, linkText, classes, isExternal, tooltipText).outerHTML; 1536} 1537 1538WebInspector.completeURL = function(baseURL, href) 1539{ 1540 var match = baseURL.match(WebInspector.URLRegExp); 1541 if (match) { 1542 var path = href; 1543 if (path.charAt(0) !== "/") { 1544 var basePath = match[4] || "/"; 1545 path = basePath.substring(0, basePath.lastIndexOf("/")) + "/" + path; 1546 } 1547 return match[1] + "://" + match[2] + (match[3] ? (":" + match[3]) : "") + path; 1548 } 1549 return null; 1550} 1551 1552WebInspector.addMainEventListeners = function(doc) 1553{ 1554 doc.defaultView.addEventListener("focus", this.windowFocused.bind(this), false); 1555 doc.defaultView.addEventListener("blur", this.windowBlurred.bind(this), false); 1556 doc.addEventListener("click", this.documentClick.bind(this), true); 1557} 1558 1559WebInspector._searchFieldManualFocus = function(event) 1560{ 1561 this.currentFocusElement = event.target; 1562 this._previousFocusElement = event.target; 1563} 1564 1565WebInspector._searchKeyDown = function(event) 1566{ 1567 // Escape Key will clear the field and clear the search results 1568 if (event.keyCode === WebInspector.KeyboardShortcut.KeyCodes.Esc) { 1569 // If focus belongs here and text is empty - nothing to do, return unhandled. 1570 if (event.target.value === "" && this.currentFocusElement === this.previousFocusElement) 1571 return; 1572 event.preventDefault(); 1573 event.stopPropagation(); 1574 // When search was selected manually and is currently blank, we'd like Esc stay unhandled 1575 // and hit console drawer handler. 1576 event.target.value = ""; 1577 1578 this.performSearch(event); 1579 this.currentFocusElement = this.previousFocusElement; 1580 if (this.currentFocusElement === event.target) 1581 this.currentFocusElement.select(); 1582 return false; 1583 } 1584 1585 if (!isEnterKey(event)) 1586 return false; 1587 1588 // Select all of the text so the user can easily type an entirely new query. 1589 event.target.select(); 1590 1591 // Only call performSearch if the Enter key was pressed. Otherwise the search 1592 // performance is poor because of searching on every key. The search field has 1593 // the incremental attribute set, so we still get incremental searches. 1594 this.performSearch(event); 1595 1596 // Call preventDefault since this was the Enter key. This prevents a "search" event 1597 // from firing for key down. This stops performSearch from being called twice in a row. 1598 event.preventDefault(); 1599} 1600 1601WebInspector.performSearch = function(event) 1602{ 1603 var query = event.target.value; 1604 var forceSearch = event.keyIdentifier === "Enter"; 1605 var isShortSearch = (query.length < 3); 1606 1607 // Clear a leftover short search flag due to a non-conflicting forced search. 1608 if (isShortSearch && this.shortSearchWasForcedByKeyEvent && this.currentQuery !== query) 1609 delete this.shortSearchWasForcedByKeyEvent; 1610 1611 // Indicate this was a forced search on a short query. 1612 if (isShortSearch && forceSearch) 1613 this.shortSearchWasForcedByKeyEvent = true; 1614 1615 if (!query || !query.length || (!forceSearch && isShortSearch)) { 1616 // Prevent clobbering a short search forced by the user. 1617 if (this.shortSearchWasForcedByKeyEvent) { 1618 delete this.shortSearchWasForcedByKeyEvent; 1619 return; 1620 } 1621 1622 delete this.currentQuery; 1623 1624 for (var panelName in this.panels) { 1625 var panel = this.panels[panelName]; 1626 if (panel.currentQuery && panel.searchCanceled) 1627 panel.searchCanceled(); 1628 delete panel.currentQuery; 1629 } 1630 1631 this.updateSearchMatchesCount(); 1632 1633 return; 1634 } 1635 1636 if (query === this.currentPanel.currentQuery && this.currentPanel.currentQuery === this.currentQuery) { 1637 // When this is the same query and a forced search, jump to the next 1638 // search result for a good user experience. 1639 if (forceSearch && this.currentPanel.jumpToNextSearchResult) 1640 this.currentPanel.jumpToNextSearchResult(); 1641 return; 1642 } 1643 1644 this.currentQuery = query; 1645 1646 this.updateSearchMatchesCount(); 1647 1648 if (!this.currentPanel.performSearch) 1649 return; 1650 1651 this.currentPanel.currentQuery = query; 1652 this.currentPanel.performSearch(query); 1653} 1654 1655WebInspector.addNodesToSearchResult = function(nodeIds) 1656{ 1657 WebInspector.panels.elements.addNodesToSearchResult(nodeIds); 1658} 1659 1660WebInspector.updateSearchMatchesCount = function(matches, panel) 1661{ 1662 if (!panel) 1663 panel = this.currentPanel; 1664 1665 panel.currentSearchMatches = matches; 1666 1667 if (panel !== this.currentPanel) 1668 return; 1669 1670 if (!this.currentPanel.currentQuery) { 1671 document.getElementById("search-results-matches").addStyleClass("hidden"); 1672 return; 1673 } 1674 1675 if (matches) { 1676 if (matches === 1) 1677 var matchesString = WebInspector.UIString("1 match"); 1678 else 1679 var matchesString = WebInspector.UIString("%d matches", matches); 1680 } else 1681 var matchesString = WebInspector.UIString("Not Found"); 1682 1683 var matchesToolbarElement = document.getElementById("search-results-matches"); 1684 matchesToolbarElement.removeStyleClass("hidden"); 1685 matchesToolbarElement.textContent = matchesString; 1686} 1687 1688WebInspector.UIString = function(string) 1689{ 1690 if (window.localizedStrings && string in window.localizedStrings) 1691 string = window.localizedStrings[string]; 1692 else { 1693 if (!(string in this.missingLocalizedStrings)) { 1694 if (!WebInspector.InspectorBackendStub) 1695 console.error("Localized string \"" + string + "\" not found."); 1696 this.missingLocalizedStrings[string] = true; 1697 } 1698 1699 if (Preferences.showMissingLocalizedStrings) 1700 string += " (not localized)"; 1701 } 1702 1703 return String.vsprintf(string, Array.prototype.slice.call(arguments, 1)); 1704} 1705 1706WebInspector.isMac = function() 1707{ 1708 if (!("_isMac" in this)) 1709 this._isMac = WebInspector.platform === "mac"; 1710 1711 return this._isMac; 1712} 1713 1714WebInspector.isBeingEdited = function(element) 1715{ 1716 return element.__editing; 1717} 1718 1719WebInspector.startEditing = function(element, committedCallback, cancelledCallback, context, multiline) 1720{ 1721 if (element.__editing) 1722 return; 1723 element.__editing = true; 1724 1725 var oldText = getContent(element); 1726 var moveDirection = ""; 1727 1728 element.addStyleClass("editing"); 1729 1730 var oldTabIndex = element.tabIndex; 1731 if (element.tabIndex < 0) 1732 element.tabIndex = 0; 1733 1734 function blurEventListener() { 1735 editingCommitted.call(element); 1736 } 1737 1738 function getContent(element) { 1739 if (element.tagName === "INPUT" && element.type === "text") 1740 return element.value; 1741 else 1742 return element.textContent; 1743 } 1744 1745 function cleanUpAfterEditing() { 1746 delete this.__editing; 1747 1748 this.removeStyleClass("editing"); 1749 this.tabIndex = oldTabIndex; 1750 this.scrollTop = 0; 1751 this.scrollLeft = 0; 1752 1753 element.removeEventListener("blur", blurEventListener, false); 1754 element.removeEventListener("keydown", keyDownEventListener, true); 1755 1756 if (element === WebInspector.currentFocusElement || element.isAncestor(WebInspector.currentFocusElement)) 1757 WebInspector.currentFocusElement = WebInspector.previousFocusElement; 1758 } 1759 1760 function editingCancelled() { 1761 if (this.tagName === "INPUT" && this.type === "text") 1762 this.value = oldText; 1763 else 1764 this.textContent = oldText; 1765 1766 cleanUpAfterEditing.call(this); 1767 1768 if (cancelledCallback) 1769 cancelledCallback(this, context); 1770 } 1771 1772 function editingCommitted() { 1773 cleanUpAfterEditing.call(this); 1774 1775 if (committedCallback) 1776 committedCallback(this, getContent(this), oldText, context, moveDirection); 1777 } 1778 1779 function keyDownEventListener(event) { 1780 var isMetaOrCtrl = WebInspector.isMac() ? 1781 event.metaKey && !event.shiftKey && !event.ctrlKey && !event.altKey : 1782 event.ctrlKey && !event.shiftKey && !event.metaKey && !event.altKey; 1783 if (isEnterKey(event) && (!multiline || isMetaOrCtrl)) { 1784 editingCommitted.call(element); 1785 event.preventDefault(); 1786 event.stopPropagation(); 1787 } else if (event.keyCode === WebInspector.KeyboardShortcut.KeyCodes.Esc) { 1788 editingCancelled.call(element); 1789 event.preventDefault(); 1790 event.stopPropagation(); 1791 } else if (event.keyIdentifier === "U+0009") // Tab key 1792 moveDirection = (event.shiftKey ? "backward" : "forward"); 1793 } 1794 1795 element.addEventListener("blur", blurEventListener, false); 1796 element.addEventListener("keydown", keyDownEventListener, true); 1797 1798 WebInspector.currentFocusElement = element; 1799} 1800 1801WebInspector._toolbarItemClicked = function(event) 1802{ 1803 var toolbarItem = event.currentTarget; 1804 this.currentPanel = toolbarItem.panel; 1805} 1806 1807// This table maps MIME types to the Resource.Types which are valid for them. 1808// The following line: 1809// "text/html": {0: 1}, 1810// means that text/html is a valid MIME type for resources that have type 1811// WebInspector.Resource.Type.Document (which has a value of 0). 1812WebInspector.MIMETypes = { 1813 "text/html": {0: true}, 1814 "text/xml": {0: true}, 1815 "text/plain": {0: true}, 1816 "application/xhtml+xml": {0: true}, 1817 "text/css": {1: true}, 1818 "text/xsl": {1: true}, 1819 "image/jpeg": {2: true}, 1820 "image/png": {2: true}, 1821 "image/gif": {2: true}, 1822 "image/bmp": {2: true}, 1823 "image/vnd.microsoft.icon": {2: true}, 1824 "image/x-icon": {2: true}, 1825 "image/x-xbitmap": {2: true}, 1826 "font/ttf": {3: true}, 1827 "font/opentype": {3: true}, 1828 "application/x-font-type1": {3: true}, 1829 "application/x-font-ttf": {3: true}, 1830 "application/x-truetype-font": {3: true}, 1831 "text/javascript": {4: true}, 1832 "text/ecmascript": {4: true}, 1833 "application/javascript": {4: true}, 1834 "application/ecmascript": {4: true}, 1835 "application/x-javascript": {4: true}, 1836 "text/javascript1.1": {4: true}, 1837 "text/javascript1.2": {4: true}, 1838 "text/javascript1.3": {4: true}, 1839 "text/jscript": {4: true}, 1840 "text/livescript": {4: true}, 1841} 1842