1 /* 2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. 3 * Copyright (C) 2008 Matt Lilek <webkit (at) mattlilek.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 importScript("Spectrum.js"); 32 importScript("DOMSyntaxHighlighter.js"); 33 importScript("ElementsTreeOutline.js"); 34 importScript("EventListenersSidebarPane.js"); 35 importScript("MetricsSidebarPane.js"); 36 importScript("OverridesView.js"); 37 importScript("PlatformFontsSidebarPane.js"); 38 importScript("PropertiesSidebarPane.js"); 39 importScript("RenderingOptionsView.js"); 40 importScript("StylesSidebarPane.js"); 41 42 /** 43 * @constructor 44 * @implements {WebInspector.Searchable} 45 * @implements {WebInspector.TargetManager.Observer} 46 * @extends {WebInspector.Panel} 47 */ 48 WebInspector.ElementsPanel = function() 49 { 50 WebInspector.Panel.call(this, "elements"); 51 this.registerRequiredCSS("breadcrumbList.css"); 52 this.registerRequiredCSS("elementsPanel.css"); 53 this.registerRequiredCSS("suggestBox.css"); 54 this.setHideOnDetach(); 55 56 this._splitView = new WebInspector.SplitView(true, true, "elementsPanelSplitViewState", 325, 325); 57 this._splitView.addEventListener(WebInspector.SplitView.Events.SidebarSizeChanged, this._updateTreeOutlineVisibleWidth.bind(this)); 58 this._splitView.show(this.element); 59 60 this._searchableView = new WebInspector.SearchableView(this); 61 this._searchableView.setMinimumSize(25, 19); 62 this._searchableView.show(this._splitView.mainElement()); 63 var stackElement = this._searchableView.element; 64 65 this.contentElement = stackElement.createChild("div"); 66 this.contentElement.id = "elements-content"; 67 this.contentElement.classList.add("outline-disclosure"); 68 this.contentElement.classList.add("source-code"); 69 if (!WebInspector.settings.domWordWrap.get()) 70 this.contentElement.classList.add("nowrap"); 71 WebInspector.settings.domWordWrap.addChangeListener(this._domWordWrapSettingChanged.bind(this)); 72 73 this.contentElement.addEventListener("contextmenu", this._contextMenuEventFired.bind(this), true); 74 this._splitView.sidebarElement().addEventListener("contextmenu", this._sidebarContextMenuEventFired.bind(this), false); 75 76 var crumbsContainer = stackElement.createChild("div"); 77 crumbsContainer.id = "elements-crumbs"; 78 this.crumbsElement = crumbsContainer.createChild("div", "crumbs"); 79 this.crumbsElement.addEventListener("mousemove", this._mouseMovedInCrumbs.bind(this), false); 80 this.crumbsElement.addEventListener("mouseout", this._mouseMovedOutOfCrumbs.bind(this), false); 81 82 this.sidebarPanes = {}; 83 this.sidebarPanes.platformFonts = new WebInspector.PlatformFontsSidebarPane(); 84 this.sidebarPanes.computedStyle = new WebInspector.ComputedStyleSidebarPane(); 85 this.sidebarPanes.styles = new WebInspector.StylesSidebarPane(this.sidebarPanes.computedStyle, this._setPseudoClassForNode.bind(this)); 86 87 this._matchedStylesFilterBoxContainer = document.createElement("div"); 88 this._matchedStylesFilterBoxContainer.className = "sidebar-pane-filter-box"; 89 this._computedStylesFilterBoxContainer = document.createElement("div"); 90 this._computedStylesFilterBoxContainer.className = "sidebar-pane-filter-box"; 91 this.sidebarPanes.styles.setFilterBoxContainers(this._matchedStylesFilterBoxContainer, this._computedStylesFilterBoxContainer); 92 93 this.sidebarPanes.metrics = new WebInspector.MetricsSidebarPane(); 94 this.sidebarPanes.properties = new WebInspector.PropertiesSidebarPane(); 95 this.sidebarPanes.domBreakpoints = WebInspector.domBreakpointsSidebarPane.createProxy(this); 96 this.sidebarPanes.eventListeners = new WebInspector.EventListenersSidebarPane(); 97 98 this.sidebarPanes.styles.addEventListener(WebInspector.SidebarPane.EventTypes.wasShown, this.updateStyles.bind(this, false)); 99 this.sidebarPanes.metrics.addEventListener(WebInspector.SidebarPane.EventTypes.wasShown, this.updateMetrics.bind(this)); 100 this.sidebarPanes.platformFonts.addEventListener(WebInspector.SidebarPane.EventTypes.wasShown, this.updatePlatformFonts.bind(this)); 101 this.sidebarPanes.properties.addEventListener(WebInspector.SidebarPane.EventTypes.wasShown, this.updateProperties.bind(this)); 102 this.sidebarPanes.eventListeners.addEventListener(WebInspector.SidebarPane.EventTypes.wasShown, this.updateEventListeners.bind(this)); 103 104 this.sidebarPanes.styles.addEventListener("style edited", this._stylesPaneEdited, this); 105 this.sidebarPanes.styles.addEventListener("style property toggled", this._stylesPaneEdited, this); 106 this.sidebarPanes.metrics.addEventListener("metrics edited", this._metricsPaneEdited, this); 107 this._extensionSidebarPanes = []; 108 109 WebInspector.dockController.addEventListener(WebInspector.DockController.Events.DockSideChanged, this._dockSideChanged.bind(this)); 110 WebInspector.settings.splitVerticallyWhenDockedToRight.addChangeListener(this._dockSideChanged.bind(this)); 111 this._dockSideChanged(); 112 113 this._popoverHelper = new WebInspector.PopoverHelper(this.element, this._getPopoverAnchor.bind(this), this._showPopover.bind(this)); 114 this._popoverHelper.setTimeout(0); 115 116 /** @type {!Array.<!WebInspector.ElementsTreeOutline>} */ 117 this._treeOutlines = []; 118 /** @type {!Map.<!WebInspector.Target, !WebInspector.ElementsTreeOutline>} */ 119 this._targetToTreeOutline = new Map(); 120 WebInspector.targetManager.observeTargets(this); 121 WebInspector.settings.showUAShadowDOM.addChangeListener(this._showUAShadowDOMChanged.bind(this)); 122 } 123 124 WebInspector.ElementsPanel.prototype = { 125 /** 126 * @param {!WebInspector.Target} target 127 */ 128 targetAdded: function(target) 129 { 130 var treeOutline = new WebInspector.ElementsTreeOutline(target, true, true, this._populateContextMenu.bind(this), this._setPseudoClassForNode.bind(this)); 131 treeOutline.wireToDOMModel(); 132 treeOutline.addEventListener(WebInspector.ElementsTreeOutline.Events.SelectedNodeChanged, this._selectedNodeChanged, this); 133 treeOutline.addEventListener(WebInspector.ElementsTreeOutline.Events.ElementsTreeUpdated, this._updateBreadcrumbIfNeeded, this); 134 this._treeOutlines.push(treeOutline); 135 this._targetToTreeOutline.put(target, treeOutline); 136 137 target.domModel.addEventListener(WebInspector.DOMModel.Events.DocumentUpdated, this._documentUpdatedEvent, this); 138 target.cssModel.addEventListener(WebInspector.CSSStyleModel.Events.ModelWasEnabled, this._updateSidebars, this); 139 140 // Perform attach if necessary. 141 if (this.isShowing()) 142 this.wasShown(); 143 }, 144 145 /** 146 * @param {!WebInspector.Target} target 147 */ 148 targetRemoved: function(target) 149 { 150 var treeOutline = this._targetToTreeOutline.get(target); 151 treeOutline.unwireFromDOMModel(); 152 this._treeOutlines.remove(treeOutline); 153 treeOutline.element.remove(); 154 155 target.domModel.removeEventListener(WebInspector.DOMModel.Events.DocumentUpdated, this._documentUpdatedEvent, this); 156 target.cssModel.removeEventListener(WebInspector.CSSStyleModel.Events.ModelWasEnabled, this._updateSidebars, this); 157 }, 158 159 /** 160 * @return {?WebInspector.ElementsTreeOutline} 161 */ 162 _firstTreeOutlineDeprecated: function() 163 { 164 return this._treeOutlines[0] || null; 165 }, 166 167 _updateTreeOutlineVisibleWidth: function() 168 { 169 if (!this._treeOutlines.length) 170 return; 171 172 var width = this._splitView.element.offsetWidth; 173 if (this._splitView.isVertical()) 174 width -= this._splitView.sidebarSize(); 175 for (var i = 0; i < this._treeOutlines.length; ++i) { 176 this._treeOutlines[i].setVisibleWidth(width); 177 this._treeOutlines[i].updateSelection(); 178 } 179 this.updateBreadcrumbSizes(); 180 }, 181 182 /** 183 * @return {!Element} 184 */ 185 defaultFocusedElement: function() 186 { 187 return this._treeOutlines.length ? this._treeOutlines[0].element : this.element; 188 }, 189 190 /** 191 * @return {!WebInspector.SearchableView} 192 */ 193 searchableView: function() 194 { 195 return this._searchableView; 196 }, 197 198 wasShown: function() 199 { 200 for (var i = 0; i < this._treeOutlines.length; ++i) { 201 var treeOutline = this._treeOutlines[i]; 202 // Attach heavy component lazily 203 if (treeOutline.element.parentElement !== this.contentElement) 204 this.contentElement.appendChild(treeOutline.element); 205 } 206 WebInspector.Panel.prototype.wasShown.call(this); 207 this.updateBreadcrumb(); 208 209 for (var i = 0; i < this._treeOutlines.length; ++i) { 210 var treeOutline = this._treeOutlines[i]; 211 treeOutline.updateSelection(); 212 treeOutline.setVisible(true); 213 214 if (!treeOutline.rootDOMNode) 215 if (treeOutline.domModel().existingDocument()) 216 this._documentUpdated(treeOutline.domModel(), treeOutline.domModel().existingDocument()); 217 else 218 treeOutline.domModel().requestDocument(); 219 } 220 221 }, 222 223 willHide: function() 224 { 225 for (var i = 0; i < this._treeOutlines.length; ++i) { 226 var treeOutline = this._treeOutlines[i]; 227 treeOutline.domModel().hideDOMNodeHighlight(); 228 treeOutline.setVisible(false); 229 // Detach heavy component on hide 230 this.contentElement.removeChild(treeOutline.element); 231 } 232 this._popoverHelper.hidePopover(); 233 WebInspector.Panel.prototype.willHide.call(this); 234 }, 235 236 onResize: function() 237 { 238 this._updateTreeOutlineVisibleWidth(); 239 }, 240 241 omitDefaultSelection: function() 242 { 243 this._omitDefaultSelection = true; 244 }, 245 246 stopOmittingDefaultSelection: function() 247 { 248 delete this._omitDefaultSelection; 249 }, 250 251 /** 252 * @param {!WebInspector.DOMNode} node 253 * @param {string} pseudoClass 254 * @param {boolean} enable 255 */ 256 _setPseudoClassForNode: function(node, pseudoClass, enable) 257 { 258 if (!node || !node.target().cssModel.forcePseudoState(node, pseudoClass, enable)) 259 return; 260 261 this._targetToTreeOutline.get(node.target()).updateOpenCloseTags(node); 262 this._metricsPaneEdited(); 263 this._stylesPaneEdited(); 264 265 WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, { 266 action: WebInspector.UserMetrics.UserActionNames.ForcedElementState, 267 selector: WebInspector.DOMPresentationUtils.fullQualifiedSelector(node, false), 268 enabled: enable, 269 state: pseudoClass 270 }); 271 }, 272 273 /** 274 * @param {!WebInspector.Event} event 275 */ 276 _selectedNodeChanged: function(event) 277 { 278 var selectedNode = /** @type {?WebInspector.DOMNode} */ (event.data); 279 for (var i = 0; i < this._treeOutlines.length; ++i) { 280 if (!selectedNode || selectedNode.domModel() !== this._treeOutlines[i].domModel()) 281 this._treeOutlines[i].selectDOMNode(null); 282 } 283 284 if (!selectedNode && this._lastValidSelectedNode) 285 this._selectedPathOnReset = this._lastValidSelectedNode.path(); 286 287 this.updateBreadcrumb(false); 288 289 this._updateSidebars(); 290 291 if (selectedNode) { 292 ConsoleAgent.addInspectedNode(selectedNode.id); 293 this._lastValidSelectedNode = selectedNode; 294 } 295 WebInspector.notifications.dispatchEventToListeners(WebInspector.NotificationService.Events.SelectedNodeChanged); 296 }, 297 298 _updateSidebars: function() 299 { 300 for (var pane in this.sidebarPanes) 301 this.sidebarPanes[pane].needsUpdate = true; 302 303 this.updateStyles(true); 304 this.updateMetrics(); 305 this.updatePlatformFonts(); 306 this.updateProperties(); 307 this.updateEventListeners(); 308 }, 309 310 _reset: function() 311 { 312 delete this.currentQuery; 313 }, 314 315 /** 316 * @param {!WebInspector.Event} event 317 */ 318 _documentUpdatedEvent: function(event) 319 { 320 this._documentUpdated(/** @type {!WebInspector.DOMModel} */ (event.target), /** @type {?WebInspector.DOMDocument} */ (event.data)); 321 }, 322 323 /** 324 * @param {!WebInspector.DOMModel} domModel 325 * @param {?WebInspector.DOMDocument} inspectedRootDocument 326 */ 327 _documentUpdated: function(domModel, inspectedRootDocument) 328 { 329 this._reset(); 330 this.searchCanceled(); 331 332 var treeOutline = this._targetToTreeOutline.get(domModel.target()); 333 treeOutline.rootDOMNode = inspectedRootDocument; 334 335 if (!inspectedRootDocument) { 336 if (this.isShowing()) 337 domModel.requestDocument(); 338 return; 339 } 340 341 WebInspector.domBreakpointsSidebarPane.restoreBreakpoints(domModel.target()); 342 343 /** 344 * @this {WebInspector.ElementsPanel} 345 * @param {?WebInspector.DOMNode} candidateFocusNode 346 */ 347 function selectNode(candidateFocusNode) 348 { 349 if (!candidateFocusNode) 350 candidateFocusNode = inspectedRootDocument.body || inspectedRootDocument.documentElement; 351 352 if (!candidateFocusNode) 353 return; 354 355 this.selectDOMNode(candidateFocusNode); 356 if (treeOutline.selectedTreeElement) 357 treeOutline.selectedTreeElement.expand(); 358 } 359 360 /** 361 * @param {?DOMAgent.NodeId} nodeId 362 * @this {WebInspector.ElementsPanel} 363 */ 364 function selectLastSelectedNode(nodeId) 365 { 366 if (this.selectedDOMNode()) { 367 // Focused node has been explicitly set while reaching out for the last selected node. 368 return; 369 } 370 var node = nodeId ? domModel.nodeForId(nodeId) : null; 371 selectNode.call(this, node); 372 } 373 374 if (this._omitDefaultSelection) 375 return; 376 377 if (this._selectedPathOnReset) 378 domModel.pushNodeByPathToFrontend(this._selectedPathOnReset, selectLastSelectedNode.bind(this)); 379 else 380 selectNode.call(this, null); 381 delete this._selectedPathOnReset; 382 }, 383 384 searchCanceled: function() 385 { 386 delete this._searchQuery; 387 this._hideSearchHighlights(); 388 389 this._searchableView.updateSearchMatchesCount(0); 390 391 delete this._currentSearchResultIndex; 392 delete this._searchResults; 393 WebInspector.domModel.cancelSearch(); 394 }, 395 396 /** 397 * @param {string} query 398 * @param {boolean} shouldJump 399 * @param {boolean=} jumpBackwards 400 */ 401 performSearch: function(query, shouldJump, jumpBackwards) 402 { 403 // Call searchCanceled since it will reset everything we need before doing a new search. 404 this.searchCanceled(); 405 406 const whitespaceTrimmedQuery = query.trim(); 407 if (!whitespaceTrimmedQuery.length) 408 return; 409 410 this._searchQuery = query; 411 412 /** 413 * @param {number} resultCount 414 * @this {WebInspector.ElementsPanel} 415 */ 416 function resultCountCallback(resultCount) 417 { 418 this._searchableView.updateSearchMatchesCount(resultCount); 419 if (!resultCount) 420 return; 421 422 this._currentSearchResultIndex = -1; 423 this._searchResults = new Array(resultCount); 424 if (shouldJump) 425 this._jumpToSearchResult(jumpBackwards ? -1 : 0); 426 } 427 WebInspector.domModel.performSearch(whitespaceTrimmedQuery, resultCountCallback.bind(this)); 428 }, 429 430 _contextMenuEventFired: function(event) 431 { 432 var contextMenu = new WebInspector.ContextMenu(event); 433 for (var i = 0; i < this._treeOutlines.length; ++i) 434 this._treeOutlines[i].populateContextMenu(contextMenu, event); 435 contextMenu.show(); 436 }, 437 438 _domWordWrapSettingChanged: function(event) 439 { 440 if (event.data) 441 this.contentElement.classList.remove("nowrap"); 442 else 443 this.contentElement.classList.add("nowrap"); 444 445 var selectedNode = this.selectedDOMNode(); 446 if (!selectedNode) 447 return; 448 449 var treeOutline = this._targetToTreeOutline.get(selectedNode.target()); 450 var treeElement = treeOutline.findTreeElement(selectedNode); 451 if (treeElement) 452 treeElement.updateSelection(); // Recalculate selection highlight dimensions. 453 }, 454 455 switchToAndFocus: function(node) 456 { 457 // Reset search restore. 458 this._searchableView.cancelSearch(); 459 WebInspector.inspectorView.setCurrentPanel(this); 460 this.selectDOMNode(node, true); 461 }, 462 463 _populateContextMenu: function(contextMenu, node) 464 { 465 // Add debbuging-related actions 466 contextMenu.appendSeparator(); 467 var pane = WebInspector.domBreakpointsSidebarPane; 468 pane.populateNodeContextMenu(node, contextMenu); 469 }, 470 471 _getPopoverAnchor: function(element) 472 { 473 var anchor = element.enclosingNodeOrSelfWithClass("webkit-html-resource-link"); 474 if (!anchor || !anchor.href) 475 return null; 476 477 var treeOutlineElement = anchor.enclosingNodeOrSelfWithClass("elements-tree-outline"); 478 if (!treeOutlineElement) 479 return null; 480 481 for (var i = 0; i < this._treeOutlines.length; ++i) { 482 if (this._treeOutlines[i].element !== treeOutlineElement) 483 continue; 484 485 var resource = this._treeOutlines[i].target().resourceTreeModel.resourceForURL(anchor.href); 486 if (!resource || resource.type !== WebInspector.resourceTypes.Image) 487 return null; 488 anchor.removeAttribute("title"); 489 return anchor; 490 } 491 return null; 492 }, 493 494 /** 495 * @param {!WebInspector.DOMNode} node 496 * @param {function()} callback 497 */ 498 _loadDimensionsForNode: function(node, callback) 499 { 500 if (!node.nodeName() || node.nodeName().toLowerCase() !== "img") { 501 callback(); 502 return; 503 } 504 505 node.resolveToObject("", resolvedNode); 506 507 function resolvedNode(object) 508 { 509 if (!object) { 510 callback(); 511 return; 512 } 513 514 object.callFunctionJSON(dimensions, undefined, callback); 515 object.release(); 516 517 /** 518 * @return {!{offsetWidth: number, offsetHeight: number, naturalWidth: number, naturalHeight: number}} 519 * @suppressReceiverCheck 520 * @this {!Element} 521 */ 522 function dimensions() 523 { 524 return { offsetWidth: this.offsetWidth, offsetHeight: this.offsetHeight, naturalWidth: this.naturalWidth, naturalHeight: this.naturalHeight }; 525 } 526 } 527 }, 528 529 /** 530 * @param {!Element} anchor 531 * @param {!WebInspector.Popover} popover 532 */ 533 _showPopover: function(anchor, popover) 534 { 535 var listItem = anchor.enclosingNodeOrSelfWithNodeName("li"); 536 // We get here for CSS properties, too. 537 if (listItem && listItem.treeElement && listItem.treeElement.treeOutline instanceof WebInspector.ElementsTreeOutline) { 538 var node = /** @type {!WebInspector.DOMNode} */ (listItem.treeElement.representedObject); 539 this._loadDimensionsForNode(node, WebInspector.DOMPresentationUtils.buildImagePreviewContents.bind(WebInspector.DOMPresentationUtils, node.target(), anchor.href, true, showPopover)); 540 } else { 541 var node = this.selectedDOMNode(); 542 if (node) 543 WebInspector.DOMPresentationUtils.buildImagePreviewContents(node.target(), anchor.href, true, showPopover); 544 } 545 546 /** 547 * @param {!Element=} contents 548 */ 549 function showPopover(contents) 550 { 551 if (!contents) 552 return; 553 popover.setCanShrink(false); 554 popover.show(contents, anchor); 555 } 556 }, 557 558 _jumpToSearchResult: function(index) 559 { 560 this._hideSearchHighlights(); 561 this._currentSearchResultIndex = (index + this._searchResults.length) % this._searchResults.length; 562 this._highlightCurrentSearchResult(); 563 }, 564 565 jumpToNextSearchResult: function() 566 { 567 if (!this._searchResults) 568 return; 569 this._jumpToSearchResult(this._currentSearchResultIndex + 1); 570 }, 571 572 jumpToPreviousSearchResult: function() 573 { 574 if (!this._searchResults) 575 return; 576 this._jumpToSearchResult(this._currentSearchResultIndex - 1); 577 }, 578 579 _highlightCurrentSearchResult: function() 580 { 581 var treeOutline = this._firstTreeOutlineDeprecated(); 582 if (!treeOutline) 583 return; 584 585 var index = this._currentSearchResultIndex; 586 var searchResults = this._searchResults; 587 var searchResult = searchResults[index]; 588 589 if (searchResult === null) { 590 this._searchableView.updateCurrentMatchIndex(index); 591 return; 592 } 593 594 /** 595 * @param {?WebInspector.DOMNode} node 596 * @this {WebInspector.ElementsPanel} 597 */ 598 function searchCallback(node) 599 { 600 searchResults[index] = node; 601 this._highlightCurrentSearchResult(); 602 } 603 604 if (typeof searchResult === "undefined") { 605 // No data for slot, request it. 606 WebInspector.domModel.searchResult(index, searchCallback.bind(this)); 607 return; 608 } 609 610 this._searchableView.updateCurrentMatchIndex(index); 611 612 var treeElement = treeOutline.findTreeElement(searchResult); 613 if (treeElement) { 614 treeElement.highlightSearchResults(this._searchQuery); 615 treeElement.reveal(); 616 var matches = treeElement.listItemElement.getElementsByClassName("highlighted-search-result"); 617 if (matches.length) 618 matches[0].scrollIntoViewIfNeeded(); 619 } 620 }, 621 622 _hideSearchHighlights: function() 623 { 624 if (!this._searchResults) 625 return; 626 var searchResult = this._searchResults[this._currentSearchResultIndex]; 627 if (!searchResult) 628 return; 629 var treeOutline = this._targetToTreeOutline.get(searchResult.target()); 630 var treeElement = treeOutline.findTreeElement(searchResult); 631 if (treeElement) 632 treeElement.hideSearchHighlights(); 633 }, 634 635 /** 636 * @return {?WebInspector.DOMNode} 637 */ 638 selectedDOMNode: function() 639 { 640 for (var i = 0; i < this._treeOutlines.length; ++i) { 641 var treeOutline = this._treeOutlines[i]; 642 if (treeOutline.selectedDOMNode()) 643 return treeOutline.selectedDOMNode(); 644 } 645 return null; 646 }, 647 648 /** 649 * @param {!WebInspector.DOMNode} node 650 * @param {boolean=} focus 651 */ 652 selectDOMNode: function(node, focus) 653 { 654 for (var i = 0; i < this._treeOutlines.length; ++i) { 655 var treeOutline = this._treeOutlines[i]; 656 if (treeOutline.target() === node.target()) 657 treeOutline.selectDOMNode(node, focus); 658 else 659 treeOutline.selectDOMNode(null); 660 } 661 }, 662 663 /** 664 * @param {!WebInspector.Event} event 665 */ 666 _updateBreadcrumbIfNeeded: function(event) 667 { 668 var nodes = /** @type {!Array.<!WebInspector.DOMNode>} */ (event.data || []); 669 if (!nodes.length) 670 return; 671 672 var crumbs = this.crumbsElement; 673 for (var crumb = crumbs.firstChild; crumb; crumb = crumb.nextSibling) { 674 if (nodes.indexOf(crumb.representedObject) !== -1) { 675 this.updateBreadcrumb(true); 676 return; 677 } 678 } 679 }, 680 681 _stylesPaneEdited: function() 682 { 683 // Once styles are edited, the Metrics pane should be updated. 684 this.sidebarPanes.metrics.needsUpdate = true; 685 this.updateMetrics(); 686 this.sidebarPanes.platformFonts.needsUpdate = true; 687 this.updatePlatformFonts(); 688 }, 689 690 _metricsPaneEdited: function() 691 { 692 // Once metrics are edited, the Styles pane should be updated. 693 this.sidebarPanes.styles.needsUpdate = true; 694 this.updateStyles(true); 695 }, 696 697 _mouseMovedInCrumbs: function(event) 698 { 699 var nodeUnderMouse = document.elementFromPoint(event.pageX, event.pageY); 700 var crumbElement = nodeUnderMouse.enclosingNodeOrSelfWithClass("crumb"); 701 var node = /** @type {?WebInspector.DOMNode} */ (crumbElement ? crumbElement.representedObject : null); 702 if (node) 703 node.highlight(); 704 }, 705 706 _mouseMovedOutOfCrumbs: function(event) 707 { 708 var nodeUnderMouse = document.elementFromPoint(event.pageX, event.pageY); 709 if (nodeUnderMouse && nodeUnderMouse.isDescendant(this.crumbsElement)) 710 return; 711 712 for (var i = 0; i < this._treeOutlines.length; ++i) 713 this._treeOutlines[i].domModel().hideDOMNodeHighlight(); 714 }, 715 716 /** 717 * @param {boolean=} forceUpdate 718 */ 719 updateBreadcrumb: function(forceUpdate) 720 { 721 if (!this.isShowing()) 722 return; 723 724 var crumbs = this.crumbsElement; 725 726 var handled = false; 727 var crumb = crumbs.firstChild; 728 while (crumb) { 729 if (crumb.representedObject === this.selectedDOMNode()) { 730 crumb.classList.add("selected"); 731 handled = true; 732 } else { 733 crumb.classList.remove("selected"); 734 } 735 736 crumb = crumb.nextSibling; 737 } 738 739 if (handled && !forceUpdate) { 740 // We don't need to rebuild the crumbs, but we need to adjust sizes 741 // to reflect the new focused or root node. 742 this.updateBreadcrumbSizes(); 743 return; 744 } 745 746 crumbs.removeChildren(); 747 748 var panel = this; 749 750 function selectCrumbFunction(event) 751 { 752 var crumb = event.currentTarget; 753 if (crumb.classList.contains("collapsed")) { 754 // Clicking a collapsed crumb will expose the hidden crumbs. 755 if (crumb === panel.crumbsElement.firstChild) { 756 // If the focused crumb is the first child, pick the farthest crumb 757 // that is still hidden. This allows the user to expose every crumb. 758 var currentCrumb = crumb; 759 while (currentCrumb) { 760 var hidden = currentCrumb.classList.contains("hidden"); 761 var collapsed = currentCrumb.classList.contains("collapsed"); 762 if (!hidden && !collapsed) 763 break; 764 crumb = currentCrumb; 765 currentCrumb = currentCrumb.nextSibling; 766 } 767 } 768 769 panel.updateBreadcrumbSizes(crumb); 770 } else 771 panel.selectDOMNode(crumb.representedObject, true); 772 773 event.preventDefault(); 774 } 775 776 for (var current = this.selectedDOMNode(); current; current = current.parentNode) { 777 if (current.nodeType() === Node.DOCUMENT_NODE) 778 continue; 779 780 crumb = document.createElement("span"); 781 crumb.className = "crumb"; 782 crumb.representedObject = current; 783 crumb.addEventListener("mousedown", selectCrumbFunction, false); 784 785 var crumbTitle = ""; 786 switch (current.nodeType()) { 787 case Node.ELEMENT_NODE: 788 if (current.pseudoType()) 789 crumbTitle = "::" + current.pseudoType(); 790 else 791 WebInspector.DOMPresentationUtils.decorateNodeLabel(current, crumb); 792 break; 793 794 case Node.TEXT_NODE: 795 crumbTitle = WebInspector.UIString("(text)"); 796 break; 797 798 case Node.COMMENT_NODE: 799 crumbTitle = "<!-->"; 800 break; 801 802 case Node.DOCUMENT_TYPE_NODE: 803 crumbTitle = "<!DOCTYPE>"; 804 break; 805 806 case Node.DOCUMENT_FRAGMENT_NODE: 807 crumbTitle = current.shadowRootType() ? "#shadow-root" : current.nodeNameInCorrectCase(); 808 break; 809 810 default: 811 crumbTitle = current.nodeNameInCorrectCase(); 812 } 813 814 if (!crumb.childNodes.length) { 815 var nameElement = document.createElement("span"); 816 nameElement.textContent = crumbTitle; 817 crumb.appendChild(nameElement); 818 crumb.title = crumbTitle; 819 } 820 821 if (current === this.selectedDOMNode()) 822 crumb.classList.add("selected"); 823 crumbs.insertBefore(crumb, crumbs.firstChild); 824 } 825 826 this.updateBreadcrumbSizes(); 827 }, 828 829 /** 830 * @param {!Element=} focusedCrumb 831 */ 832 updateBreadcrumbSizes: function(focusedCrumb) 833 { 834 if (!this.isShowing()) 835 return; 836 837 var crumbs = this.crumbsElement; 838 if (!crumbs.firstChild) 839 return; 840 841 var selectedIndex = 0; 842 var focusedIndex = 0; 843 var selectedCrumb; 844 845 // Reset crumb styles. 846 for (var i = 0; i < crumbs.childNodes.length; ++i) { 847 var crumb = crumbs.childNodes[i]; 848 // Find the selected crumb and index. 849 if (!selectedCrumb && crumb.classList.contains("selected")) { 850 selectedCrumb = crumb; 851 selectedIndex = i; 852 } 853 854 // Find the focused crumb index. 855 if (crumb === focusedCrumb) 856 focusedIndex = i; 857 858 crumb.classList.remove("compact", "collapsed", "hidden"); 859 } 860 861 // Layout 1: Measure total and normal crumb sizes 862 var contentElementWidth = this.contentElement.offsetWidth; 863 var normalSizes = []; 864 for (var i = 0; i < crumbs.childNodes.length; ++i) { 865 var crumb = crumbs.childNodes[i]; 866 normalSizes[i] = crumb.offsetWidth; 867 } 868 869 // Layout 2: Measure collapsed crumb sizes 870 var compactSizes = []; 871 for (var i = 0; i < crumbs.childNodes.length; ++i) { 872 var crumb = crumbs.childNodes[i]; 873 crumb.classList.add("compact"); 874 } 875 for (var i = 0; i < crumbs.childNodes.length; ++i) { 876 var crumb = crumbs.childNodes[i]; 877 compactSizes[i] = crumb.offsetWidth; 878 } 879 880 // Layout 3: Measure collapsed crumb size 881 crumbs.firstChild.classList.add("collapsed"); 882 var collapsedSize = crumbs.firstChild.offsetWidth; 883 884 // Clean up. 885 for (var i = 0; i < crumbs.childNodes.length; ++i) { 886 var crumb = crumbs.childNodes[i]; 887 crumb.classList.remove("compact", "collapsed"); 888 } 889 890 function crumbsAreSmallerThanContainer() 891 { 892 var totalSize = 0; 893 for (var i = 0; i < crumbs.childNodes.length; ++i) { 894 var crumb = crumbs.childNodes[i]; 895 if (crumb.classList.contains("hidden")) 896 continue; 897 if (crumb.classList.contains("collapsed")) { 898 totalSize += collapsedSize; 899 continue; 900 } 901 totalSize += crumb.classList.contains("compact") ? compactSizes[i] : normalSizes[i]; 902 } 903 const rightPadding = 10; 904 return totalSize + rightPadding < contentElementWidth; 905 } 906 907 if (crumbsAreSmallerThanContainer()) 908 return; // No need to compact the crumbs, they all fit at full size. 909 910 var BothSides = 0; 911 var AncestorSide = -1; 912 var ChildSide = 1; 913 914 /** 915 * @param {function(!Element)} shrinkingFunction 916 * @param {number} direction 917 */ 918 function makeCrumbsSmaller(shrinkingFunction, direction) 919 { 920 var significantCrumb = focusedCrumb || selectedCrumb; 921 var significantIndex = significantCrumb === selectedCrumb ? selectedIndex : focusedIndex; 922 923 function shrinkCrumbAtIndex(index) 924 { 925 var shrinkCrumb = crumbs.childNodes[index]; 926 if (shrinkCrumb && shrinkCrumb !== significantCrumb) 927 shrinkingFunction(shrinkCrumb); 928 if (crumbsAreSmallerThanContainer()) 929 return true; // No need to compact the crumbs more. 930 return false; 931 } 932 933 // Shrink crumbs one at a time by applying the shrinkingFunction until the crumbs 934 // fit in the container or we run out of crumbs to shrink. 935 if (direction) { 936 // Crumbs are shrunk on only one side (based on direction) of the signifcant crumb. 937 var index = (direction > 0 ? 0 : crumbs.childNodes.length - 1); 938 while (index !== significantIndex) { 939 if (shrinkCrumbAtIndex(index)) 940 return true; 941 index += (direction > 0 ? 1 : -1); 942 } 943 } else { 944 // Crumbs are shrunk in order of descending distance from the signifcant crumb, 945 // with a tie going to child crumbs. 946 var startIndex = 0; 947 var endIndex = crumbs.childNodes.length - 1; 948 while (startIndex != significantIndex || endIndex != significantIndex) { 949 var startDistance = significantIndex - startIndex; 950 var endDistance = endIndex - significantIndex; 951 if (startDistance >= endDistance) 952 var index = startIndex++; 953 else 954 var index = endIndex--; 955 if (shrinkCrumbAtIndex(index)) 956 return true; 957 } 958 } 959 960 // We are not small enough yet, return false so the caller knows. 961 return false; 962 } 963 964 function coalesceCollapsedCrumbs() 965 { 966 var crumb = crumbs.firstChild; 967 var collapsedRun = false; 968 var newStartNeeded = false; 969 var newEndNeeded = false; 970 while (crumb) { 971 var hidden = crumb.classList.contains("hidden"); 972 if (!hidden) { 973 var collapsed = crumb.classList.contains("collapsed"); 974 if (collapsedRun && collapsed) { 975 crumb.classList.add("hidden"); 976 crumb.classList.remove("compact"); 977 crumb.classList.remove("collapsed"); 978 979 if (crumb.classList.contains("start")) { 980 crumb.classList.remove("start"); 981 newStartNeeded = true; 982 } 983 984 if (crumb.classList.contains("end")) { 985 crumb.classList.remove("end"); 986 newEndNeeded = true; 987 } 988 989 continue; 990 } 991 992 collapsedRun = collapsed; 993 994 if (newEndNeeded) { 995 newEndNeeded = false; 996 crumb.classList.add("end"); 997 } 998 } else 999 collapsedRun = true; 1000 crumb = crumb.nextSibling; 1001 } 1002 1003 if (newStartNeeded) { 1004 crumb = crumbs.lastChild; 1005 while (crumb) { 1006 if (!crumb.classList.contains("hidden")) { 1007 crumb.classList.add("start"); 1008 break; 1009 } 1010 crumb = crumb.previousSibling; 1011 } 1012 } 1013 } 1014 1015 /** 1016 * @param {!Element} crumb 1017 */ 1018 function compact(crumb) 1019 { 1020 if (crumb.classList.contains("hidden")) 1021 return; 1022 crumb.classList.add("compact"); 1023 } 1024 1025 /** 1026 * @param {!Element} crumb 1027 * @param {boolean=} dontCoalesce 1028 */ 1029 function collapse(crumb, dontCoalesce) 1030 { 1031 if (crumb.classList.contains("hidden")) 1032 return; 1033 crumb.classList.add("collapsed"); 1034 crumb.classList.remove("compact"); 1035 if (!dontCoalesce) 1036 coalesceCollapsedCrumbs(); 1037 } 1038 1039 if (!focusedCrumb) { 1040 // When not focused on a crumb we can be biased and collapse less important 1041 // crumbs that the user might not care much about. 1042 1043 // Compact child crumbs. 1044 if (makeCrumbsSmaller(compact, ChildSide)) 1045 return; 1046 1047 // Collapse child crumbs. 1048 if (makeCrumbsSmaller(collapse, ChildSide)) 1049 return; 1050 } 1051 1052 // Compact ancestor crumbs, or from both sides if focused. 1053 if (makeCrumbsSmaller(compact, focusedCrumb ? BothSides : AncestorSide)) 1054 return; 1055 1056 // Collapse ancestor crumbs, or from both sides if focused. 1057 if (makeCrumbsSmaller(collapse, focusedCrumb ? BothSides : AncestorSide)) 1058 return; 1059 1060 if (!selectedCrumb) 1061 return; 1062 1063 // Compact the selected crumb. 1064 compact(selectedCrumb); 1065 if (crumbsAreSmallerThanContainer()) 1066 return; 1067 1068 // Collapse the selected crumb as a last resort. Pass true to prevent coalescing. 1069 collapse(selectedCrumb, true); 1070 }, 1071 1072 /** 1073 * @return {boolean} 1074 */ 1075 _cssModelEnabledForSelectedNode: function() 1076 { 1077 if (!this.selectedDOMNode()) 1078 return true; 1079 return this.selectedDOMNode().target().cssModel.isEnabled(); 1080 }, 1081 1082 /** 1083 * @param {boolean=} forceUpdate 1084 */ 1085 updateStyles: function(forceUpdate) 1086 { 1087 if (!this._cssModelEnabledForSelectedNode()) 1088 return; 1089 var stylesSidebarPane = this.sidebarPanes.styles; 1090 var computedStylePane = this.sidebarPanes.computedStyle; 1091 if ((!stylesSidebarPane.isShowing() && !computedStylePane.isShowing()) || !stylesSidebarPane.needsUpdate) 1092 return; 1093 1094 stylesSidebarPane.update(this.selectedDOMNode(), forceUpdate); 1095 stylesSidebarPane.needsUpdate = false; 1096 }, 1097 1098 updateMetrics: function() 1099 { 1100 if (!this._cssModelEnabledForSelectedNode()) 1101 return; 1102 var metricsSidebarPane = this.sidebarPanes.metrics; 1103 if (!metricsSidebarPane.isShowing() || !metricsSidebarPane.needsUpdate) 1104 return; 1105 1106 metricsSidebarPane.update(this.selectedDOMNode()); 1107 metricsSidebarPane.needsUpdate = false; 1108 }, 1109 1110 updatePlatformFonts: function() 1111 { 1112 if (!this._cssModelEnabledForSelectedNode()) 1113 return; 1114 var platformFontsSidebar = this.sidebarPanes.platformFonts; 1115 if (!platformFontsSidebar.isShowing() || !platformFontsSidebar.needsUpdate) 1116 return; 1117 1118 platformFontsSidebar.update(this.selectedDOMNode()); 1119 platformFontsSidebar.needsUpdate = false; 1120 }, 1121 1122 updateProperties: function() 1123 { 1124 var propertiesSidebarPane = this.sidebarPanes.properties; 1125 if (!propertiesSidebarPane.isShowing() || !propertiesSidebarPane.needsUpdate) 1126 return; 1127 1128 propertiesSidebarPane.update(this.selectedDOMNode()); 1129 propertiesSidebarPane.needsUpdate = false; 1130 }, 1131 1132 updateEventListeners: function() 1133 { 1134 var eventListenersSidebarPane = this.sidebarPanes.eventListeners; 1135 if (!eventListenersSidebarPane.isShowing() || !eventListenersSidebarPane.needsUpdate) 1136 return; 1137 1138 eventListenersSidebarPane.update(this.selectedDOMNode()); 1139 eventListenersSidebarPane.needsUpdate = false; 1140 }, 1141 1142 /** 1143 * @param {!KeyboardEvent} event 1144 */ 1145 handleShortcut: function(event) 1146 { 1147 /** 1148 * @this {WebInspector.ElementsPanel} 1149 */ 1150 function handleUndoRedo() 1151 { 1152 if (WebInspector.KeyboardShortcut.eventHasCtrlOrMeta(event) && !event.shiftKey && event.keyIdentifier === "U+005A") { // Z key 1153 WebInspector.domModel.undo(this._updateSidebars.bind(this)); 1154 event.handled = true; 1155 return; 1156 } 1157 1158 var isRedoKey = WebInspector.isMac() ? event.metaKey && event.shiftKey && event.keyIdentifier === "U+005A" : // Z key 1159 event.ctrlKey && event.keyIdentifier === "U+0059"; // Y key 1160 if (isRedoKey) { 1161 WebInspector.domModel.redo(this._updateSidebars.bind(this)); 1162 event.handled = true; 1163 } 1164 } 1165 1166 var treeOutline = this._firstTreeOutlineDeprecated(); 1167 if (!treeOutline) 1168 return; 1169 1170 if (!treeOutline.editing()) { 1171 handleUndoRedo.call(this); 1172 if (event.handled) 1173 return; 1174 } 1175 1176 treeOutline.handleShortcut(event); 1177 }, 1178 1179 handleCopyEvent: function(event) 1180 { 1181 var currentFocusElement = WebInspector.currentFocusElement(); 1182 if (currentFocusElement && WebInspector.isBeingEdited(currentFocusElement)) 1183 return; 1184 1185 // Don't prevent the normal copy if the user has a selection. 1186 if (!window.getSelection().isCollapsed) 1187 return; 1188 event.clipboardData.clearData(); 1189 event.preventDefault(); 1190 this.selectedDOMNode().copyNode(); 1191 }, 1192 1193 /** 1194 * @param {!WebInspector.DOMNode} node 1195 * @return {!WebInspector.DOMNode} 1196 */ 1197 _leaveUserAgentShadowDOM: function(node) 1198 { 1199 var userAgentShadowRoot = node.ancestorUserAgentShadowRoot(); 1200 return userAgentShadowRoot ? /** @type {!WebInspector.DOMNode} */ (userAgentShadowRoot.parentNode) : node; 1201 }, 1202 1203 /** 1204 * @param {!WebInspector.DOMNode} node 1205 */ 1206 revealAndSelectNode: function(node) 1207 { 1208 WebInspector.inspectorView.setCurrentPanel(this); 1209 node = WebInspector.settings.showUAShadowDOM.get() ? node : this._leaveUserAgentShadowDOM(node); 1210 node.highlightForTwoSeconds(); 1211 this.selectDOMNode(node, true); 1212 }, 1213 1214 /** 1215 * @param {!Event} event 1216 * @param {!WebInspector.ContextMenu} contextMenu 1217 * @param {!Object} object 1218 */ 1219 appendApplicableItems: function(event, contextMenu, object) 1220 { 1221 var commandCallback; 1222 if (object instanceof WebInspector.RemoteObject) { 1223 var remoteObject = /** @type {!WebInspector.RemoteObject} */ (object); 1224 if (remoteObject.isNode()) 1225 commandCallback = remoteObject.reveal.bind(remoteObject); 1226 } else if (object instanceof WebInspector.DOMNode) { 1227 var domNode = /** @type {!WebInspector.DOMNode} */ (object); 1228 commandCallback = domNode.reveal.bind(domNode); 1229 } 1230 if (!commandCallback) 1231 return; 1232 // Skip adding "Reveal..." menu item for our own tree outline. 1233 if (this.element.isAncestor(/** @type {!Node} */ (event.target))) 1234 return; 1235 contextMenu.appendItem(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Elements panel" : "Reveal in Elements Panel", commandCallback); 1236 }, 1237 1238 _sidebarContextMenuEventFired: function(event) 1239 { 1240 var contextMenu = new WebInspector.ContextMenu(event); 1241 contextMenu.show(); 1242 }, 1243 1244 _dockSideChanged: function() 1245 { 1246 var vertically = WebInspector.dockController.isVertical() && WebInspector.settings.splitVerticallyWhenDockedToRight.get(); 1247 this._splitVertically(vertically); 1248 }, 1249 1250 _showUAShadowDOMChanged: function() 1251 { 1252 for (var i = 0; i < this._treeOutlines.length; ++i) 1253 this._treeOutlines[i].update(); 1254 }, 1255 1256 /** 1257 * @param {boolean} vertically 1258 */ 1259 _splitVertically: function(vertically) 1260 { 1261 if (this.sidebarPaneView && vertically === !this._splitView.isVertical()) 1262 return; 1263 1264 if (this.sidebarPaneView) { 1265 this.sidebarPaneView.detach(); 1266 this._splitView.uninstallResizer(this.sidebarPaneView.headerElement()); 1267 } 1268 1269 this._splitView.setVertical(!vertically); 1270 1271 var computedPane = new WebInspector.SidebarPane(WebInspector.UIString("Computed")); 1272 computedPane.element.classList.add("composite"); 1273 computedPane.element.classList.add("fill"); 1274 var expandComputed = computedPane.expand.bind(computedPane); 1275 1276 computedPane.bodyElement.classList.add("metrics-and-computed"); 1277 this.sidebarPanes.computedStyle.setExpandCallback(expandComputed); 1278 1279 var matchedStylePanesWrapper = document.createElement("div"); 1280 matchedStylePanesWrapper.className = "style-panes-wrapper"; 1281 var computedStylePanesWrapper = document.createElement("div"); 1282 computedStylePanesWrapper.className = "style-panes-wrapper"; 1283 1284 /** 1285 * @param {boolean} inComputedStyle 1286 * @this {WebInspector.ElementsPanel} 1287 */ 1288 function showMetrics(inComputedStyle) 1289 { 1290 if (inComputedStyle) 1291 this.sidebarPanes.metrics.show(computedStylePanesWrapper, this.sidebarPanes.computedStyle.element); 1292 else 1293 this.sidebarPanes.metrics.show(matchedStylePanesWrapper); 1294 } 1295 1296 /** 1297 * @param {!WebInspector.Event} event 1298 * @this {WebInspector.ElementsPanel} 1299 */ 1300 function tabSelected(event) 1301 { 1302 var tabId = /** @type {string} */ (event.data.tabId); 1303 if (tabId === computedPane.title()) 1304 showMetrics.call(this, true); 1305 else if (tabId === stylesPane.title()) 1306 showMetrics.call(this, false); 1307 } 1308 1309 this.sidebarPaneView = new WebInspector.SidebarTabbedPane(); 1310 1311 if (vertically) { 1312 this._splitView.installResizer(this.sidebarPaneView.headerElement()); 1313 this.sidebarPanes.metrics.setExpandCallback(expandComputed); 1314 1315 var compositePane = new WebInspector.SidebarPane(this.sidebarPanes.styles.title()); 1316 compositePane.element.classList.add("composite"); 1317 compositePane.element.classList.add("fill"); 1318 var expandComposite = compositePane.expand.bind(compositePane); 1319 1320 var splitView = new WebInspector.SplitView(true, true, "stylesPaneSplitViewState", 0.5); 1321 splitView.show(compositePane.bodyElement); 1322 1323 splitView.mainElement().appendChild(matchedStylePanesWrapper); 1324 splitView.sidebarElement().appendChild(computedStylePanesWrapper); 1325 1326 this.sidebarPanes.styles.setExpandCallback(expandComposite); 1327 1328 computedPane.show(computedStylePanesWrapper); 1329 computedPane.setExpandCallback(expandComposite); 1330 1331 splitView.mainElement().appendChild(this._matchedStylesFilterBoxContainer); 1332 splitView.sidebarElement().appendChild(this._computedStylesFilterBoxContainer); 1333 1334 this.sidebarPaneView.addPane(compositePane); 1335 } else { 1336 var stylesPane = new WebInspector.SidebarPane(this.sidebarPanes.styles.title()); 1337 stylesPane.element.classList.add("composite"); 1338 stylesPane.element.classList.add("fill"); 1339 var expandStyles = stylesPane.expand.bind(stylesPane); 1340 stylesPane.bodyElement.classList.add("metrics-and-styles"); 1341 1342 stylesPane.bodyElement.appendChild(matchedStylePanesWrapper); 1343 computedPane.bodyElement.appendChild(computedStylePanesWrapper); 1344 1345 this.sidebarPanes.styles.setExpandCallback(expandStyles); 1346 this.sidebarPanes.metrics.setExpandCallback(expandStyles); 1347 1348 this.sidebarPaneView.addEventListener(WebInspector.TabbedPane.EventTypes.TabSelected, tabSelected, this); 1349 1350 stylesPane.bodyElement.appendChild(this._matchedStylesFilterBoxContainer); 1351 computedPane.bodyElement.appendChild(this._computedStylesFilterBoxContainer); 1352 1353 this.sidebarPaneView.addPane(stylesPane); 1354 this.sidebarPaneView.addPane(computedPane); 1355 } 1356 1357 this.sidebarPanes.styles.show(matchedStylePanesWrapper); 1358 this.sidebarPanes.computedStyle.show(computedStylePanesWrapper); 1359 matchedStylePanesWrapper.appendChild(this.sidebarPanes.styles.titleElement); 1360 showMetrics.call(this, vertically); 1361 this.sidebarPanes.platformFonts.show(computedStylePanesWrapper); 1362 1363 this.sidebarPaneView.addPane(this.sidebarPanes.eventListeners); 1364 this.sidebarPaneView.addPane(this.sidebarPanes.domBreakpoints); 1365 this.sidebarPaneView.addPane(this.sidebarPanes.properties); 1366 this._extensionSidebarPanesContainer = this.sidebarPaneView; 1367 1368 for (var i = 0; i < this._extensionSidebarPanes.length; ++i) 1369 this._extensionSidebarPanesContainer.addPane(this._extensionSidebarPanes[i]); 1370 1371 this.sidebarPaneView.show(this._splitView.sidebarElement()); 1372 this.sidebarPanes.styles.expand(); 1373 }, 1374 1375 /** 1376 * @param {string} id 1377 * @param {!WebInspector.SidebarPane} pane 1378 */ 1379 addExtensionSidebarPane: function(id, pane) 1380 { 1381 this._extensionSidebarPanes.push(pane); 1382 this._extensionSidebarPanesContainer.addPane(pane); 1383 }, 1384 1385 __proto__: WebInspector.Panel.prototype 1386 } 1387 1388 /** 1389 * @constructor 1390 * @implements {WebInspector.ContextMenu.Provider} 1391 */ 1392 WebInspector.ElementsPanel.ContextMenuProvider = function() 1393 { 1394 } 1395 1396 WebInspector.ElementsPanel.ContextMenuProvider.prototype = { 1397 /** 1398 * @param {!Event} event 1399 * @param {!WebInspector.ContextMenu} contextMenu 1400 * @param {!Object} target 1401 */ 1402 appendApplicableItems: function(event, contextMenu, target) 1403 { 1404 /** @type {!WebInspector.ElementsPanel} */ (WebInspector.inspectorView.panel("elements")).appendApplicableItems(event, contextMenu, target); 1405 } 1406 } 1407 1408 /** 1409 * @constructor 1410 * @implements {WebInspector.Revealer} 1411 */ 1412 WebInspector.ElementsPanel.DOMNodeRevealer = function() 1413 { 1414 } 1415 1416 WebInspector.ElementsPanel.DOMNodeRevealer.prototype = { 1417 /** 1418 * @param {!Object} node 1419 */ 1420 reveal: function(node) 1421 { 1422 if (WebInspector.inspectElementModeController && WebInspector.inspectElementModeController.enabled()) { 1423 InspectorFrontendHost.bringToFront(); 1424 WebInspector.inspectElementModeController.disable(); 1425 } 1426 1427 /** @type {!WebInspector.ElementsPanel} */ (WebInspector.inspectorView.panel("elements")).revealAndSelectNode(/** @type {!WebInspector.DOMNode} */ (node)); 1428 } 1429 } 1430 1431 /** 1432 * @constructor 1433 * @implements {WebInspector.Revealer} 1434 */ 1435 WebInspector.ElementsPanel.NodeRemoteObjectRevealer = function() 1436 { 1437 } 1438 1439 WebInspector.ElementsPanel.NodeRemoteObjectRevealer.prototype = { 1440 /** 1441 * @param {!Object} remoteObject 1442 */ 1443 reveal: function(remoteObject) 1444 { 1445 revealElement(/** @type {!WebInspector.RemoteObject} */ (remoteObject)); 1446 1447 /** 1448 * @param {?WebInspector.RemoteObject} remoteObject 1449 */ 1450 function revealElement(remoteObject) 1451 { 1452 if (remoteObject) 1453 remoteObject.pushNodeToFrontend(selectNode.bind(null, remoteObject)); 1454 } 1455 1456 /** 1457 * @param {?WebInspector.RemoteObject} remoteObject 1458 * @param {?WebInspector.DOMNode} node 1459 */ 1460 function selectNode(remoteObject, node) 1461 { 1462 if (node) { 1463 node.reveal(); 1464 return; 1465 } 1466 if (!remoteObject || remoteObject.description !== "#text" || !remoteObject.isNode()) 1467 return; 1468 remoteObject.callFunction(parentElement, undefined, revealElement); 1469 } 1470 1471 /** 1472 * @suppressReceiverCheck 1473 * @this {Element} 1474 */ 1475 function parentElement() 1476 { 1477 return this.parentElement; 1478 } 1479 } 1480 } 1481