2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
3 * Copyright (C) 2008 Matt Lilek <webkit@mattlilek.com>
4 * Copyright (C) 2009 Joseph Pecoraro
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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.
33 * @implements {WebInspector.Searchable}
34 * @implements {WebInspector.TargetManager.Observer}
35 * @extends {WebInspector.Panel}
37 WebInspector.ElementsPanel = function()
39 WebInspector.Panel.call(this, "elements");
40 this.registerRequiredCSS("elementsPanel.css");
41 this.setHideOnDetach();
43 this._splitView = new WebInspector.SplitView(true, true, "elementsPanelSplitViewState", 325, 325);
44 this._splitView.addEventListener(WebInspector.SplitView.Events.SidebarSizeChanged, this._updateTreeOutlineVisibleWidth.bind(this));
45 this._splitView.show(this.element);
47 this._searchableView = new WebInspector.SearchableView(this);
48 this._searchableView.setMinimumSize(25, 19);
49 this._searchableView.show(this._splitView.mainElement());
50 var stackElement = this._searchableView.element;
52 this.contentElement = stackElement.createChild("div");
53 this.contentElement.id = "elements-content";
54 this.contentElement.classList.add("outline-disclosure");
55 this.contentElement.classList.add("source-code");
56 if (!WebInspector.settings.domWordWrap.get())
57 this.contentElement.classList.add("nowrap");
58 WebInspector.settings.domWordWrap.addChangeListener(this._domWordWrapSettingChanged.bind(this));
60 this.contentElement.addEventListener("contextmenu", this._contextMenuEventFired.bind(this), true);
61 this._splitView.sidebarElement().addEventListener("contextmenu", this._sidebarContextMenuEventFired.bind(this), false);
63 var crumbsContainer = stackElement.createChild("div");
64 crumbsContainer.id = "elements-crumbs";
65 this.crumbsElement = crumbsContainer.createChild("div", "crumbs");
66 this.crumbsElement.addEventListener("mousemove", this._mouseMovedInCrumbs.bind(this), false);
67 this.crumbsElement.addEventListener("mouseout", this._mouseMovedOutOfCrumbs.bind(this), false);
69 this.sidebarPanes = {};
70 this.sidebarPanes.platformFonts = new WebInspector.PlatformFontsSidebarPane();
71 this.sidebarPanes.computedStyle = new WebInspector.ComputedStyleSidebarPane();
72 this.sidebarPanes.styles = new WebInspector.StylesSidebarPane(this.sidebarPanes.computedStyle, this._setPseudoClassForNode.bind(this));
74 this._matchedStylesFilterBoxContainer = document.createElement("div");
75 this._matchedStylesFilterBoxContainer.className = "sidebar-pane-filter-box";
76 this._computedStylesFilterBoxContainer = document.createElement("div");
77 this._computedStylesFilterBoxContainer.className = "sidebar-pane-filter-box";
78 this.sidebarPanes.styles.setFilterBoxContainers(this._matchedStylesFilterBoxContainer, this._computedStylesFilterBoxContainer);
80 this.sidebarPanes.metrics = new WebInspector.MetricsSidebarPane();
81 this.sidebarPanes.properties = new WebInspector.PropertiesSidebarPane();
82 this.sidebarPanes.domBreakpoints = WebInspector.domBreakpointsSidebarPane.createProxy(this);
83 this.sidebarPanes.eventListeners = new WebInspector.EventListenersSidebarPane();
85 this.sidebarPanes.styles.addEventListener(WebInspector.SidebarPane.EventTypes.wasShown, this.updateStyles.bind(this, false));
86 this.sidebarPanes.metrics.addEventListener(WebInspector.SidebarPane.EventTypes.wasShown, this.updateMetrics.bind(this));
87 this.sidebarPanes.platformFonts.addEventListener(WebInspector.SidebarPane.EventTypes.wasShown, this.updatePlatformFonts.bind(this));
88 this.sidebarPanes.properties.addEventListener(WebInspector.SidebarPane.EventTypes.wasShown, this.updateProperties.bind(this));
89 this.sidebarPanes.eventListeners.addEventListener(WebInspector.SidebarPane.EventTypes.wasShown, this.updateEventListeners.bind(this));
91 this.sidebarPanes.styles.addEventListener("style edited", this._stylesPaneEdited, this);
92 this.sidebarPanes.styles.addEventListener("style property toggled", this._stylesPaneEdited, this);
93 this.sidebarPanes.metrics.addEventListener("metrics edited", this._metricsPaneEdited, this);
94 this._extensionSidebarPanes = [];
96 WebInspector.dockController.addEventListener(WebInspector.DockController.Events.DockSideChanged, this._dockSideChanged.bind(this));
97 WebInspector.settings.splitVerticallyWhenDockedToRight.addChangeListener(this._dockSideChanged.bind(this));
98 this._dockSideChanged();
100 this._popoverHelper = new WebInspector.PopoverHelper(this.element, this._getPopoverAnchor.bind(this), this._showPopover.bind(this));
101 this._popoverHelper.setTimeout(0);
103 /** @type {!Array.<!WebInspector.ElementsTreeOutline>} */
104 this._treeOutlines = [];
105 /** @type {!Map.<!WebInspector.Target, !WebInspector.ElementsTreeOutline>} */
106 this._targetToTreeOutline = new Map();
107 WebInspector.targetManager.observeTargets(this);
108 WebInspector.settings.showUAShadowDOM.addChangeListener(this._showUAShadowDOMChanged.bind(this));
109 WebInspector.targetManager.addModelListener(WebInspector.DOMModel, WebInspector.DOMModel.Events.DocumentUpdated, this._documentUpdatedEvent, this);
110 WebInspector.targetManager.addModelListener(WebInspector.DOMModel, WebInspector.CSSStyleModel.Events.ModelWasEnabled, this._updateSidebars, this);
113 WebInspector.ElementsPanel.prototype = {
115 * @param {!WebInspector.Target} target
117 targetAdded: function(target)
119 var treeOutline = new WebInspector.ElementsTreeOutline(target, true, true, this._populateContextMenu.bind(this), this._setPseudoClassForNode.bind(this));
120 treeOutline.wireToDOMModel();
121 treeOutline.addEventListener(WebInspector.ElementsTreeOutline.Events.SelectedNodeChanged, this._selectedNodeChanged, this);
122 treeOutline.addEventListener(WebInspector.ElementsTreeOutline.Events.ElementsTreeUpdated, this._updateBreadcrumbIfNeeded, this);
123 this._treeOutlines.push(treeOutline);
124 this._targetToTreeOutline.put(target, treeOutline);
126 // Perform attach if necessary.
127 if (this.isShowing())
132 * @param {!WebInspector.Target} target
134 targetRemoved: function(target)
136 var treeOutline = this._targetToTreeOutline.remove(target);
137 treeOutline.unwireFromDOMModel();
138 this._treeOutlines.remove(treeOutline);
139 treeOutline.element.remove();
143 * @return {?WebInspector.ElementsTreeOutline}
145 _firstTreeOutlineDeprecated: function()
147 return this._treeOutlines[0] || null;
150 _updateTreeOutlineVisibleWidth: function()
152 if (!this._treeOutlines.length)
155 var width = this._splitView.element.offsetWidth;
156 if (this._splitView.isVertical())
157 width -= this._splitView.sidebarSize();
158 for (var i = 0; i < this._treeOutlines.length; ++i) {
159 this._treeOutlines[i].setVisibleWidth(width);
160 this._treeOutlines[i].updateSelection();
162 this.updateBreadcrumbSizes();
168 defaultFocusedElement: function()
170 return this._treeOutlines.length ? this._treeOutlines[0].element : this.element;
174 * @return {!WebInspector.SearchableView}
176 searchableView: function()
178 return this._searchableView;
183 for (var i = 0; i < this._treeOutlines.length; ++i) {
184 var treeOutline = this._treeOutlines[i];
185 // Attach heavy component lazily
186 if (treeOutline.element.parentElement !== this.contentElement)
187 this.contentElement.appendChild(treeOutline.element);
189 WebInspector.Panel.prototype.wasShown.call(this);
190 this.updateBreadcrumb();
192 for (var i = 0; i < this._treeOutlines.length; ++i) {
193 var treeOutline = this._treeOutlines[i];
194 treeOutline.updateSelection();
195 treeOutline.setVisible(true);
197 if (!treeOutline.rootDOMNode)
198 if (treeOutline.domModel().existingDocument())
199 this._documentUpdated(treeOutline.domModel(), treeOutline.domModel().existingDocument());
201 treeOutline.domModel().requestDocument();
208 for (var i = 0; i < this._treeOutlines.length; ++i) {
209 var treeOutline = this._treeOutlines[i];
210 treeOutline.domModel().hideDOMNodeHighlight();
211 treeOutline.setVisible(false);
212 // Detach heavy component on hide
213 this.contentElement.removeChild(treeOutline.element);
215 this._popoverHelper.hidePopover();
216 WebInspector.Panel.prototype.willHide.call(this);
221 this._updateTreeOutlineVisibleWidth();
224 omitDefaultSelection: function()
226 this._omitDefaultSelection = true;
229 stopOmittingDefaultSelection: function()
231 delete this._omitDefaultSelection;
235 * @param {!WebInspector.DOMNode} node
236 * @param {string} pseudoClass
237 * @param {boolean} enable
239 _setPseudoClassForNode: function(node, pseudoClass, enable)
241 if (!node || !node.target().cssModel.forcePseudoState(node, pseudoClass, enable))
244 this._treeOutlineForNode(node).updateOpenCloseTags(node);
245 this._metricsPaneEdited();
246 this._stylesPaneEdited();
248 WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
249 action: WebInspector.UserMetrics.UserActionNames.ForcedElementState,
250 selector: WebInspector.DOMPresentationUtils.fullQualifiedSelector(node, false),
257 * @param {!WebInspector.Event} event
259 _selectedNodeChanged: function(event)
261 var selectedNode = /** @type {?WebInspector.DOMNode} */ (event.data);
262 for (var i = 0; i < this._treeOutlines.length; ++i) {
263 if (!selectedNode || selectedNode.domModel() !== this._treeOutlines[i].domModel())
264 this._treeOutlines[i].selectDOMNode(null);
267 if (!selectedNode && this._lastValidSelectedNode)
268 this._selectedPathOnReset = this._lastValidSelectedNode.path();
270 this.updateBreadcrumb(false);
272 this._updateSidebars();
275 ConsoleAgent.addInspectedNode(selectedNode.id);
276 this._lastValidSelectedNode = selectedNode;
278 WebInspector.notifications.dispatchEventToListeners(WebInspector.NotificationService.Events.SelectedNodeChanged);
281 _updateSidebars: function()
283 for (var pane in this.sidebarPanes)
284 this.sidebarPanes[pane].needsUpdate = true;
286 this.updateStyles(true);
287 this.updateMetrics();
288 this.updatePlatformFonts();
289 this.updateProperties();
290 this.updateEventListeners();
295 delete this.currentQuery;
299 * @param {!WebInspector.Event} event
301 _documentUpdatedEvent: function(event)
303 this._documentUpdated(/** @type {!WebInspector.DOMModel} */ (event.target), /** @type {?WebInspector.DOMDocument} */ (event.data));
307 * @param {!WebInspector.DOMModel} domModel
308 * @param {?WebInspector.DOMDocument} inspectedRootDocument
310 _documentUpdated: function(domModel, inspectedRootDocument)
313 this.searchCanceled();
315 var treeOutline = this._targetToTreeOutline.get(domModel.target());
316 treeOutline.rootDOMNode = inspectedRootDocument;
318 if (!inspectedRootDocument) {
319 if (this.isShowing())
320 domModel.requestDocument();
324 WebInspector.domBreakpointsSidebarPane.restoreBreakpoints(domModel.target());
327 * @this {WebInspector.ElementsPanel}
328 * @param {?WebInspector.DOMNode} candidateFocusNode
330 function selectNode(candidateFocusNode)
332 if (!candidateFocusNode)
333 candidateFocusNode = inspectedRootDocument.body || inspectedRootDocument.documentElement;
335 if (!candidateFocusNode)
338 this.selectDOMNode(candidateFocusNode);
339 if (treeOutline.selectedTreeElement)
340 treeOutline.selectedTreeElement.expand();
344 * @param {?DOMAgent.NodeId} nodeId
345 * @this {WebInspector.ElementsPanel}
347 function selectLastSelectedNode(nodeId)
349 if (this.selectedDOMNode()) {
350 // Focused node has been explicitly set while reaching out for the last selected node.
353 var node = nodeId ? domModel.nodeForId(nodeId) : null;
354 selectNode.call(this, node);
357 if (this._omitDefaultSelection)
360 if (this._selectedPathOnReset)
361 domModel.pushNodeByPathToFrontend(this._selectedPathOnReset, selectLastSelectedNode.bind(this));
363 selectNode.call(this, null);
364 delete this._selectedPathOnReset;
367 searchCanceled: function()
369 delete this._searchQuery;
370 this._hideSearchHighlights();
372 this._searchableView.updateSearchMatchesCount(0);
374 delete this._currentSearchResultIndex;
375 delete this._searchResults;
377 var targets = WebInspector.targetManager.targets();
378 for (var i = 0; i < targets.length; ++i)
379 targets[i].domModel.cancelSearch();
383 * @param {string} query
384 * @param {boolean} shouldJump
385 * @param {boolean=} jumpBackwards
387 performSearch: function(query, shouldJump, jumpBackwards)
389 // Call searchCanceled since it will reset everything we need before doing a new search.
390 this.searchCanceled();
392 const whitespaceTrimmedQuery = query.trim();
393 if (!whitespaceTrimmedQuery.length)
396 this._searchQuery = query;
398 var targets = WebInspector.targetManager.targets();
400 for (var i = 0; i < targets.length; ++i)
401 promises.push(targets[i].domModel.performSearchPromise(whitespaceTrimmedQuery, WebInspector.settings.showUAShadowDOM.get()));
402 Promise.all(promises).then(resultCountCallback.bind(this));
405 * @param {!Array.<number>} resultCounts
406 * @this {WebInspector.ElementsPanel}
408 function resultCountCallback(resultCounts)
411 * @type {!Array.<{target: !WebInspector.Target, index: number, node: (?WebInspector.DOMNode|undefined)}>}
413 this._searchResults = [];
414 for (var i = 0; i < resultCounts.length; ++i) {
415 var resultCount = resultCounts[i];
416 for (var j = 0; j < resultCount; ++j)
417 this._searchResults.push({target: targets[i], index: j, node: undefined});
419 this._searchableView.updateSearchMatchesCount(this._searchResults.length);
420 if (!this._searchResults.length)
422 this._currentSearchResultIndex = -1;
425 this._jumpToSearchResult(jumpBackwards ? -1 : 0);
429 _contextMenuEventFired: function(event)
431 var contextMenu = new WebInspector.ContextMenu(event);
432 for (var i = 0; i < this._treeOutlines.length; ++i)
433 this._treeOutlines[i].populateContextMenu(contextMenu, event);
437 _domWordWrapSettingChanged: function(event)
440 this.contentElement.classList.remove("nowrap");
442 this.contentElement.classList.add("nowrap");
444 var selectedNode = this.selectedDOMNode();
448 var treeElement = this._treeElementForNode(selectedNode);
450 treeElement.updateSelection(); // Recalculate selection highlight dimensions.
453 switchToAndFocus: function(node)
455 // Reset search restore.
456 this._searchableView.cancelSearch();
457 WebInspector.inspectorView.setCurrentPanel(this);
458 this.selectDOMNode(node, true);
461 _populateContextMenu: function(contextMenu, node)
463 // Add debbuging-related actions
464 contextMenu.appendSeparator();
465 var pane = WebInspector.domBreakpointsSidebarPane;
466 pane.populateNodeContextMenu(node, contextMenu);
470 * @param {!Element} element
471 * @param {!Event} event
472 * @return {!Element|!AnchorBox|undefined}
474 _getPopoverAnchor: function(element, event)
476 var anchor = element.enclosingNodeOrSelfWithClass("webkit-html-resource-link");
477 if (!anchor || !anchor.href)
480 var treeOutlineElement = anchor.enclosingNodeOrSelfWithClass("elements-tree-outline");
481 if (!treeOutlineElement)
484 for (var i = 0; i < this._treeOutlines.length; ++i) {
485 if (this._treeOutlines[i].element !== treeOutlineElement)
488 var resource = this._treeOutlines[i].target().resourceTreeModel.resourceForURL(anchor.href);
489 if (!resource || resource.type !== WebInspector.resourceTypes.Image)
491 anchor.removeAttribute("title");
497 * @param {!WebInspector.DOMNode} node
498 * @param {function()} callback
500 _loadDimensionsForNode: function(node, callback)
502 if (!node.nodeName() || node.nodeName().toLowerCase() !== "img") {
507 node.resolveToObject("", resolvedNode);
509 function resolvedNode(object)
516 object.callFunctionJSON(dimensions, undefined, callback);
520 * @return {!{offsetWidth: number, offsetHeight: number, naturalWidth: number, naturalHeight: number}}
521 * @suppressReceiverCheck
524 function dimensions()
526 return { offsetWidth: this.offsetWidth, offsetHeight: this.offsetHeight, naturalWidth: this.naturalWidth, naturalHeight: this.naturalHeight };
532 * @param {!Element} anchor
533 * @param {!WebInspector.Popover} popover
535 _showPopover: function(anchor, popover)
537 var listItem = anchor.enclosingNodeOrSelfWithNodeName("li");
538 // We get here for CSS properties, too.
539 if (listItem && listItem.treeElement && listItem.treeElement.treeOutline instanceof WebInspector.ElementsTreeOutline) {
540 var node = /** @type {!WebInspector.DOMNode} */ (listItem.treeElement.representedObject);
541 this._loadDimensionsForNode(node, WebInspector.DOMPresentationUtils.buildImagePreviewContents.bind(WebInspector.DOMPresentationUtils, node.target(), anchor.href, true, showPopover));
543 var node = this.selectedDOMNode();
545 WebInspector.DOMPresentationUtils.buildImagePreviewContents(node.target(), anchor.href, true, showPopover);
549 * @param {!Element=} contents
551 function showPopover(contents)
555 popover.setCanShrink(false);
556 popover.show(contents, anchor);
560 _jumpToSearchResult: function(index)
562 this._hideSearchHighlights();
563 this._currentSearchResultIndex = (index + this._searchResults.length) % this._searchResults.length;
564 this._highlightCurrentSearchResult();
567 jumpToNextSearchResult: function()
569 if (!this._searchResults)
571 this._jumpToSearchResult(this._currentSearchResultIndex + 1);
574 jumpToPreviousSearchResult: function()
576 if (!this._searchResults)
578 this._jumpToSearchResult(this._currentSearchResultIndex - 1);
581 _highlightCurrentSearchResult: function()
583 var index = this._currentSearchResultIndex;
584 var searchResults = this._searchResults;
585 var searchResult = searchResults[index];
587 if (searchResult.node === null) {
588 this._searchableView.updateCurrentMatchIndex(index);
593 * @param {?WebInspector.DOMNode} node
594 * @this {WebInspector.ElementsPanel}
596 function searchCallback(node)
598 searchResult.node = node;
599 this._highlightCurrentSearchResult();
602 if (typeof searchResult.node === "undefined") {
603 // No data for slot, request it.
604 searchResult.target.domModel.searchResult(searchResult.index, searchCallback.bind(this));
608 this._searchableView.updateCurrentMatchIndex(index);
610 var treeElement = this._treeElementForNode(searchResult.node);
612 treeElement.highlightSearchResults(this._searchQuery);
613 treeElement.reveal();
614 var matches = treeElement.listItemElement.getElementsByClassName("highlighted-search-result");
616 matches[0].scrollIntoViewIfNeeded();
620 _hideSearchHighlights: function()
622 if (!this._searchResults || !this._searchResults.length || this._currentSearchResultIndex < 0)
624 var searchResult = this._searchResults[this._currentSearchResultIndex];
625 if (!searchResult.node)
627 var treeOutline = this._targetToTreeOutline.get(searchResult.target);
628 var treeElement = treeOutline.findTreeElement(searchResult.node);
630 treeElement.hideSearchHighlights();
634 * @return {?WebInspector.DOMNode}
636 selectedDOMNode: function()
638 for (var i = 0; i < this._treeOutlines.length; ++i) {
639 var treeOutline = this._treeOutlines[i];
640 if (treeOutline.selectedDOMNode())
641 return treeOutline.selectedDOMNode();
647 * @param {!WebInspector.DOMNode} node
648 * @param {boolean=} focus
650 selectDOMNode: function(node, focus)
652 for (var i = 0; i < this._treeOutlines.length; ++i) {
653 var treeOutline = this._treeOutlines[i];
654 if (treeOutline.target() === node.target())
655 treeOutline.selectDOMNode(node, focus);
657 treeOutline.selectDOMNode(null);
662 * @param {!WebInspector.Event} event
664 _updateBreadcrumbIfNeeded: function(event)
666 var nodes = /** @type {!Array.<!WebInspector.DOMNode>} */ (event.data || []);
670 var crumbs = this.crumbsElement;
671 for (var crumb = crumbs.firstChild; crumb; crumb = crumb.nextSibling) {
672 if (nodes.indexOf(crumb.representedObject) !== -1) {
673 this.updateBreadcrumb(true);
679 _stylesPaneEdited: function()
681 // Once styles are edited, the Metrics pane should be updated.
682 this.sidebarPanes.metrics.needsUpdate = true;
683 this.updateMetrics();
684 this.sidebarPanes.platformFonts.needsUpdate = true;
685 this.updatePlatformFonts();
688 _metricsPaneEdited: function()
690 // Once metrics are edited, the Styles pane should be updated.
691 this.sidebarPanes.styles.needsUpdate = true;
692 this.updateStyles(true);
695 _mouseMovedInCrumbs: function(event)
697 var nodeUnderMouse = document.elementFromPoint(event.pageX, event.pageY);
698 var crumbElement = nodeUnderMouse.enclosingNodeOrSelfWithClass("crumb");
699 var node = /** @type {?WebInspector.DOMNode} */ (crumbElement ? crumbElement.representedObject : null);
704 _mouseMovedOutOfCrumbs: function(event)
706 var nodeUnderMouse = document.elementFromPoint(event.pageX, event.pageY);
707 if (nodeUnderMouse && nodeUnderMouse.isDescendant(this.crumbsElement))
710 for (var i = 0; i < this._treeOutlines.length; ++i)
711 this._treeOutlines[i].domModel().hideDOMNodeHighlight();
715 * @param {boolean=} forceUpdate
717 updateBreadcrumb: function(forceUpdate)
719 if (!this.isShowing())
722 var crumbs = this.crumbsElement;
725 var crumb = crumbs.firstChild;
727 if (crumb.representedObject === this.selectedDOMNode()) {
728 crumb.classList.add("selected");
731 crumb.classList.remove("selected");
734 crumb = crumb.nextSibling;
737 if (handled && !forceUpdate) {
738 // We don't need to rebuild the crumbs, but we need to adjust sizes
739 // to reflect the new focused or root node.
740 this.updateBreadcrumbSizes();
744 crumbs.removeChildren();
748 function selectCrumbFunction(event)
750 var crumb = event.currentTarget;
751 if (crumb.classList.contains("collapsed")) {
752 // Clicking a collapsed crumb will expose the hidden crumbs.
753 if (crumb === panel.crumbsElement.firstChild) {
754 // If the focused crumb is the first child, pick the farthest crumb
755 // that is still hidden. This allows the user to expose every crumb.
756 var currentCrumb = crumb;
757 while (currentCrumb) {
758 var hidden = currentCrumb.classList.contains("hidden");
759 var collapsed = currentCrumb.classList.contains("collapsed");
760 if (!hidden && !collapsed)
762 crumb = currentCrumb;
763 currentCrumb = currentCrumb.nextSibling;
767 panel.updateBreadcrumbSizes(crumb);
769 panel.selectDOMNode(crumb.representedObject, true);
771 event.preventDefault();
774 for (var current = this.selectedDOMNode(); current; current = current.parentNode) {
775 if (current.nodeType() === Node.DOCUMENT_NODE)
778 crumb = document.createElement("span");
779 crumb.className = "crumb";
780 crumb.representedObject = current;
781 crumb.addEventListener("mousedown", selectCrumbFunction, false);
784 switch (current.nodeType()) {
785 case Node.ELEMENT_NODE:
786 if (current.pseudoType())
787 crumbTitle = "::" + current.pseudoType();
789 WebInspector.DOMPresentationUtils.decorateNodeLabel(current, crumb);
793 crumbTitle = WebInspector.UIString("(text)");
796 case Node.COMMENT_NODE:
797 crumbTitle = "<!-->";
800 case Node.DOCUMENT_TYPE_NODE:
801 crumbTitle = "<!DOCTYPE>";
804 case Node.DOCUMENT_FRAGMENT_NODE:
805 crumbTitle = current.shadowRootType() ? "#shadow-root" : current.nodeNameInCorrectCase();
809 crumbTitle = current.nodeNameInCorrectCase();
812 if (!crumb.childNodes.length) {
813 var nameElement = document.createElement("span");
814 nameElement.textContent = crumbTitle;
815 crumb.appendChild(nameElement);
816 crumb.title = crumbTitle;
819 if (current === this.selectedDOMNode())
820 crumb.classList.add("selected");
821 crumbs.insertBefore(crumb, crumbs.firstChild);
824 this.updateBreadcrumbSizes();
828 * @param {!Element=} focusedCrumb
830 updateBreadcrumbSizes: function(focusedCrumb)
832 if (!this.isShowing())
835 var crumbs = this.crumbsElement;
836 if (!crumbs.firstChild)
839 var selectedIndex = 0;
840 var focusedIndex = 0;
843 // Reset crumb styles.
844 for (var i = 0; i < crumbs.childNodes.length; ++i) {
845 var crumb = crumbs.childNodes[i];
846 // Find the selected crumb and index.
847 if (!selectedCrumb && crumb.classList.contains("selected")) {
848 selectedCrumb = crumb;
852 // Find the focused crumb index.
853 if (crumb === focusedCrumb)
856 crumb.classList.remove("compact", "collapsed", "hidden");
859 // Layout 1: Measure total and normal crumb sizes
860 var contentElementWidth = this.contentElement.offsetWidth;
861 var normalSizes = [];
862 for (var i = 0; i < crumbs.childNodes.length; ++i) {
863 var crumb = crumbs.childNodes[i];
864 normalSizes[i] = crumb.offsetWidth;
867 // Layout 2: Measure collapsed crumb sizes
868 var compactSizes = [];
869 for (var i = 0; i < crumbs.childNodes.length; ++i) {
870 var crumb = crumbs.childNodes[i];
871 crumb.classList.add("compact");
873 for (var i = 0; i < crumbs.childNodes.length; ++i) {
874 var crumb = crumbs.childNodes[i];
875 compactSizes[i] = crumb.offsetWidth;
878 // Layout 3: Measure collapsed crumb size
879 crumbs.firstChild.classList.add("collapsed");
880 var collapsedSize = crumbs.firstChild.offsetWidth;
883 for (var i = 0; i < crumbs.childNodes.length; ++i) {
884 var crumb = crumbs.childNodes[i];
885 crumb.classList.remove("compact", "collapsed");
888 function crumbsAreSmallerThanContainer()
891 for (var i = 0; i < crumbs.childNodes.length; ++i) {
892 var crumb = crumbs.childNodes[i];
893 if (crumb.classList.contains("hidden"))
895 if (crumb.classList.contains("collapsed")) {
896 totalSize += collapsedSize;
899 totalSize += crumb.classList.contains("compact") ? compactSizes[i] : normalSizes[i];
901 const rightPadding = 10;
902 return totalSize + rightPadding < contentElementWidth;
905 if (crumbsAreSmallerThanContainer())
906 return; // No need to compact the crumbs, they all fit at full size.
909 var AncestorSide = -1;
913 * @param {function(!Element)} shrinkingFunction
914 * @param {number} direction
916 function makeCrumbsSmaller(shrinkingFunction, direction)
918 var significantCrumb = focusedCrumb || selectedCrumb;
919 var significantIndex = significantCrumb === selectedCrumb ? selectedIndex : focusedIndex;
921 function shrinkCrumbAtIndex(index)
923 var shrinkCrumb = crumbs.childNodes[index];
924 if (shrinkCrumb && shrinkCrumb !== significantCrumb)
925 shrinkingFunction(shrinkCrumb);
926 if (crumbsAreSmallerThanContainer())
927 return true; // No need to compact the crumbs more.
931 // Shrink crumbs one at a time by applying the shrinkingFunction until the crumbs
932 // fit in the container or we run out of crumbs to shrink.
934 // Crumbs are shrunk on only one side (based on direction) of the signifcant crumb.
935 var index = (direction > 0 ? 0 : crumbs.childNodes.length - 1);
936 while (index !== significantIndex) {
937 if (shrinkCrumbAtIndex(index))
939 index += (direction > 0 ? 1 : -1);
942 // Crumbs are shrunk in order of descending distance from the signifcant crumb,
943 // with a tie going to child crumbs.
945 var endIndex = crumbs.childNodes.length - 1;
946 while (startIndex != significantIndex || endIndex != significantIndex) {
947 var startDistance = significantIndex - startIndex;
948 var endDistance = endIndex - significantIndex;
949 if (startDistance >= endDistance)
950 var index = startIndex++;
952 var index = endIndex--;
953 if (shrinkCrumbAtIndex(index))
958 // We are not small enough yet, return false so the caller knows.
962 function coalesceCollapsedCrumbs()
964 var crumb = crumbs.firstChild;
965 var collapsedRun = false;
966 var newStartNeeded = false;
967 var newEndNeeded = false;
969 var hidden = crumb.classList.contains("hidden");
971 var collapsed = crumb.classList.contains("collapsed");
972 if (collapsedRun && collapsed) {
973 crumb.classList.add("hidden");
974 crumb.classList.remove("compact");
975 crumb.classList.remove("collapsed");
977 if (crumb.classList.contains("start")) {
978 crumb.classList.remove("start");
979 newStartNeeded = true;
982 if (crumb.classList.contains("end")) {
983 crumb.classList.remove("end");
990 collapsedRun = collapsed;
993 newEndNeeded = false;
994 crumb.classList.add("end");
998 crumb = crumb.nextSibling;
1001 if (newStartNeeded) {
1002 crumb = crumbs.lastChild;
1004 if (!crumb.classList.contains("hidden")) {
1005 crumb.classList.add("start");
1008 crumb = crumb.previousSibling;
1014 * @param {!Element} crumb
1016 function compact(crumb)
1018 if (crumb.classList.contains("hidden"))
1020 crumb.classList.add("compact");
1024 * @param {!Element} crumb
1025 * @param {boolean=} dontCoalesce
1027 function collapse(crumb, dontCoalesce)
1029 if (crumb.classList.contains("hidden"))
1031 crumb.classList.add("collapsed");
1032 crumb.classList.remove("compact");
1034 coalesceCollapsedCrumbs();
1037 if (!focusedCrumb) {
1038 // When not focused on a crumb we can be biased and collapse less important
1039 // crumbs that the user might not care much about.
1041 // Compact child crumbs.
1042 if (makeCrumbsSmaller(compact, ChildSide))
1045 // Collapse child crumbs.
1046 if (makeCrumbsSmaller(collapse, ChildSide))
1050 // Compact ancestor crumbs, or from both sides if focused.
1051 if (makeCrumbsSmaller(compact, focusedCrumb ? BothSides : AncestorSide))
1054 // Collapse ancestor crumbs, or from both sides if focused.
1055 if (makeCrumbsSmaller(collapse, focusedCrumb ? BothSides : AncestorSide))
1061 // Compact the selected crumb.
1062 compact(selectedCrumb);
1063 if (crumbsAreSmallerThanContainer())
1066 // Collapse the selected crumb as a last resort. Pass true to prevent coalescing.
1067 collapse(selectedCrumb, true);
1073 _cssModelEnabledForSelectedNode: function()
1075 if (!this.selectedDOMNode())
1077 return this.selectedDOMNode().target().cssModel.isEnabled();
1081 * @param {boolean=} forceUpdate
1083 updateStyles: function(forceUpdate)
1085 if (!this._cssModelEnabledForSelectedNode())
1087 var stylesSidebarPane = this.sidebarPanes.styles;
1088 var computedStylePane = this.sidebarPanes.computedStyle;
1089 if ((!stylesSidebarPane.isShowing() && !computedStylePane.isShowing()) || !stylesSidebarPane.needsUpdate)
1092 stylesSidebarPane.update(this.selectedDOMNode(), forceUpdate);
1093 stylesSidebarPane.needsUpdate = false;
1096 updateMetrics: function()
1098 if (!this._cssModelEnabledForSelectedNode())
1100 var metricsSidebarPane = this.sidebarPanes.metrics;
1101 if (!metricsSidebarPane.isShowing() || !metricsSidebarPane.needsUpdate)
1104 metricsSidebarPane.update(this.selectedDOMNode());
1105 metricsSidebarPane.needsUpdate = false;
1108 updatePlatformFonts: function()
1110 if (!this._cssModelEnabledForSelectedNode())
1112 var platformFontsSidebar = this.sidebarPanes.platformFonts;
1113 if (!platformFontsSidebar.isShowing() || !platformFontsSidebar.needsUpdate)
1116 platformFontsSidebar.update(this.selectedDOMNode());
1117 platformFontsSidebar.needsUpdate = false;
1120 updateProperties: function()
1122 var propertiesSidebarPane = this.sidebarPanes.properties;
1123 if (!propertiesSidebarPane.isShowing() || !propertiesSidebarPane.needsUpdate)
1126 propertiesSidebarPane.update(this.selectedDOMNode());
1127 propertiesSidebarPane.needsUpdate = false;
1130 updateEventListeners: function()
1132 var eventListenersSidebarPane = this.sidebarPanes.eventListeners;
1133 if (!eventListenersSidebarPane.isShowing() || !eventListenersSidebarPane.needsUpdate)
1136 eventListenersSidebarPane.update(this.selectedDOMNode());
1137 eventListenersSidebarPane.needsUpdate = false;
1141 * @param {!KeyboardEvent} event
1143 handleShortcut: function(event)
1146 * @param {!WebInspector.ElementsTreeOutline} treeOutline
1147 * @this {WebInspector.ElementsPanel}
1149 function handleUndoRedo(treeOutline)
1151 if (WebInspector.KeyboardShortcut.eventHasCtrlOrMeta(event) && !event.shiftKey && event.keyIdentifier === "U+005A") { // Z key
1152 treeOutline.target().domModel.undo(this._updateSidebars.bind(this));
1153 event.handled = true;
1157 var isRedoKey = WebInspector.isMac() ? event.metaKey && event.shiftKey && event.keyIdentifier === "U+005A" : // Z key
1158 event.ctrlKey && event.keyIdentifier === "U+0059"; // Y key
1160 treeOutline.target().domModel.redo(this._updateSidebars.bind(this));
1161 event.handled = true;
1165 var element = event.target.enclosingNodeOrSelfWithClass("elements-tree-outline");
1168 var treeOutline = null;
1169 for (var i = 0; i < this._treeOutlines.length; ++i) {
1170 if (this._treeOutlines[i].element === element)
1171 treeOutline = this._treeOutlines[i];
1176 if (!treeOutline.editing()) {
1177 handleUndoRedo.call(this, treeOutline);
1182 treeOutline.handleShortcut(event);
1186 * @param {?WebInspector.DOMNode} node
1187 * @return {?WebInspector.ElementsTreeOutline}
1189 _treeOutlineForNode: function(node)
1193 return this._targetToTreeOutline.get(node.target()) || null;
1197 * @param {!WebInspector.DOMNode} node
1198 * @return {?WebInspector.ElementsTreeElement}
1200 _treeElementForNode: function(node)
1202 var treeOutline = this._treeOutlineForNode(node);
1203 return /** @type {?WebInspector.ElementsTreeElement} */ (treeOutline.findTreeElement(node));
1207 * @param {!Event} event
1209 handleCopyEvent: function(event)
1211 if (!WebInspector.currentFocusElement().enclosingNodeOrSelfWithClass("elements-tree-outline"))
1213 var treeOutline = this._treeOutlineForNode(this.selectedDOMNode());
1215 treeOutline.handleCopyOrCutKeyboardEvent(false, event);
1219 * @param {!Event} event
1221 handleCutEvent: function(event)
1223 var treeOutline = this._treeOutlineForNode(this.selectedDOMNode());
1225 treeOutline.handleCopyOrCutKeyboardEvent(true, event);
1229 * @param {!Event} event
1231 handlePasteEvent: function(event)
1233 var treeOutline = this._treeOutlineForNode(this.selectedDOMNode());
1235 treeOutline.handlePasteKeyboardEvent(event);
1239 * @param {!WebInspector.DOMNode} node
1240 * @return {!WebInspector.DOMNode}
1242 _leaveUserAgentShadowDOM: function(node)
1244 var userAgentShadowRoot = node.ancestorUserAgentShadowRoot();
1245 return userAgentShadowRoot ? /** @type {!WebInspector.DOMNode} */ (userAgentShadowRoot.parentNode) : node;
1249 * @param {!WebInspector.DOMNode} node
1251 revealAndSelectNode: function(node)
1253 WebInspector.inspectorView.setCurrentPanel(this);
1254 node = WebInspector.settings.showUAShadowDOM.get() ? node : this._leaveUserAgentShadowDOM(node);
1255 node.highlightForTwoSeconds();
1256 this.selectDOMNode(node, true);
1260 * @param {!Event} event
1261 * @param {!WebInspector.ContextMenu} contextMenu
1262 * @param {!Object} object
1264 appendApplicableItems: function(event, contextMenu, object)
1266 if (!(object instanceof WebInspector.RemoteObject && (/** @type {!WebInspector.RemoteObject} */ (object)).isNode())
1267 && !(object instanceof WebInspector.DOMNode)
1268 && !(object instanceof WebInspector.DeferredDOMNode)) {
1271 // Skip adding "Reveal..." menu item for our own tree outline.
1272 if (this.element.isAncestor(/** @type {!Node} */ (event.target)))
1274 var commandCallback = WebInspector.Revealer.reveal.bind(WebInspector.Revealer, object);
1275 contextMenu.appendItem(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Elements panel" : "Reveal in Elements Panel", commandCallback);
1278 _sidebarContextMenuEventFired: function(event)
1280 var contextMenu = new WebInspector.ContextMenu(event);
1284 _dockSideChanged: function()
1286 var vertically = WebInspector.dockController.isVertical() && WebInspector.settings.splitVerticallyWhenDockedToRight.get();
1287 this._splitVertically(vertically);
1290 _showUAShadowDOMChanged: function()
1292 for (var i = 0; i < this._treeOutlines.length; ++i)
1293 this._treeOutlines[i].update();
1297 * @param {boolean} vertically
1299 _splitVertically: function(vertically)
1301 if (this.sidebarPaneView && vertically === !this._splitView.isVertical())
1304 if (this.sidebarPaneView) {
1305 this.sidebarPaneView.detach();
1306 this._splitView.uninstallResizer(this.sidebarPaneView.headerElement());
1309 this._splitView.setVertical(!vertically);
1311 var computedPane = new WebInspector.SidebarPane(WebInspector.UIString("Computed"));
1312 computedPane.element.classList.add("composite");
1313 computedPane.element.classList.add("fill");
1314 var expandComputed = computedPane.expand.bind(computedPane);
1316 computedPane.bodyElement.classList.add("metrics-and-computed");
1317 this.sidebarPanes.computedStyle.setExpandCallback(expandComputed);
1319 var matchedStylePanesWrapper = document.createElement("div");
1320 matchedStylePanesWrapper.className = "style-panes-wrapper";
1321 var computedStylePanesWrapper = document.createElement("div");
1322 computedStylePanesWrapper.className = "style-panes-wrapper";
1325 * @param {boolean} inComputedStyle
1326 * @this {WebInspector.ElementsPanel}
1328 function showMetrics(inComputedStyle)
1330 if (inComputedStyle)
1331 this.sidebarPanes.metrics.show(computedStylePanesWrapper, this.sidebarPanes.computedStyle.element);
1333 this.sidebarPanes.metrics.show(matchedStylePanesWrapper);
1337 * @param {!WebInspector.Event} event
1338 * @this {WebInspector.ElementsPanel}
1340 function tabSelected(event)
1342 var tabId = /** @type {string} */ (event.data.tabId);
1343 if (tabId === computedPane.title())
1344 showMetrics.call(this, true);
1345 else if (tabId === stylesPane.title())
1346 showMetrics.call(this, false);
1349 this.sidebarPaneView = new WebInspector.SidebarTabbedPane();
1352 this._splitView.installResizer(this.sidebarPaneView.headerElement());
1353 this.sidebarPanes.metrics.setExpandCallback(expandComputed);
1355 var compositePane = new WebInspector.SidebarPane(this.sidebarPanes.styles.title());
1356 compositePane.element.classList.add("composite");
1357 compositePane.element.classList.add("fill");
1358 var expandComposite = compositePane.expand.bind(compositePane);
1360 var splitView = new WebInspector.SplitView(true, true, "stylesPaneSplitViewState", 0.5);
1361 splitView.show(compositePane.bodyElement);
1363 splitView.mainElement().appendChild(matchedStylePanesWrapper);
1364 splitView.sidebarElement().appendChild(computedStylePanesWrapper);
1366 this.sidebarPanes.styles.setExpandCallback(expandComposite);
1368 computedPane.show(computedStylePanesWrapper);
1369 computedPane.setExpandCallback(expandComposite);
1371 splitView.mainElement().appendChild(this._matchedStylesFilterBoxContainer);
1372 splitView.sidebarElement().appendChild(this._computedStylesFilterBoxContainer);
1374 this.sidebarPaneView.addPane(compositePane);
1376 var stylesPane = new WebInspector.SidebarPane(this.sidebarPanes.styles.title());
1377 stylesPane.element.classList.add("composite");
1378 stylesPane.element.classList.add("fill");
1379 var expandStyles = stylesPane.expand.bind(stylesPane);
1380 stylesPane.bodyElement.classList.add("metrics-and-styles");
1382 stylesPane.bodyElement.appendChild(matchedStylePanesWrapper);
1383 computedPane.bodyElement.appendChild(computedStylePanesWrapper);
1385 this.sidebarPanes.styles.setExpandCallback(expandStyles);
1386 this.sidebarPanes.metrics.setExpandCallback(expandStyles);
1388 this.sidebarPaneView.addEventListener(WebInspector.TabbedPane.EventTypes.TabSelected, tabSelected, this);
1390 stylesPane.bodyElement.appendChild(this._matchedStylesFilterBoxContainer);
1391 computedPane.bodyElement.appendChild(this._computedStylesFilterBoxContainer);
1393 this.sidebarPaneView.addPane(stylesPane);
1394 this.sidebarPaneView.addPane(computedPane);
1397 this.sidebarPanes.styles.show(matchedStylePanesWrapper);
1398 this.sidebarPanes.computedStyle.show(computedStylePanesWrapper);
1399 matchedStylePanesWrapper.appendChild(this.sidebarPanes.styles.titleElement);
1400 showMetrics.call(this, vertically);
1401 this.sidebarPanes.platformFonts.show(computedStylePanesWrapper);
1403 this.sidebarPaneView.addPane(this.sidebarPanes.eventListeners);
1404 this.sidebarPaneView.addPane(this.sidebarPanes.domBreakpoints);
1405 this.sidebarPaneView.addPane(this.sidebarPanes.properties);
1406 this._extensionSidebarPanesContainer = this.sidebarPaneView;
1408 for (var i = 0; i < this._extensionSidebarPanes.length; ++i)
1409 this._extensionSidebarPanesContainer.addPane(this._extensionSidebarPanes[i]);
1411 this.sidebarPaneView.show(this._splitView.sidebarElement());
1412 this.sidebarPanes.styles.expand();
1416 * @param {string} id
1417 * @param {!WebInspector.SidebarPane} pane
1419 addExtensionSidebarPane: function(id, pane)
1421 this._extensionSidebarPanes.push(pane);
1422 this._extensionSidebarPanesContainer.addPane(pane);
1425 __proto__: WebInspector.Panel.prototype
1430 * @implements {WebInspector.ContextMenu.Provider}
1432 WebInspector.ElementsPanel.ContextMenuProvider = function()
1436 WebInspector.ElementsPanel.ContextMenuProvider.prototype = {
1438 * @param {!Event} event
1439 * @param {!WebInspector.ContextMenu} contextMenu
1440 * @param {!Object} target
1442 appendApplicableItems: function(event, contextMenu, target)
1444 /** @type {!WebInspector.ElementsPanel} */ (WebInspector.inspectorView.panel("elements")).appendApplicableItems(event, contextMenu, target);
1450 * @implements {WebInspector.Revealer}
1452 WebInspector.ElementsPanel.DOMNodeRevealer = function()
1456 WebInspector.ElementsPanel.DOMNodeRevealer.prototype = {
1458 * @param {!Object} node
1460 reveal: function(node)
1462 if (WebInspector.inspectElementModeController && WebInspector.inspectElementModeController.enabled()) {
1463 InspectorFrontendHost.bringToFront();
1464 WebInspector.inspectElementModeController.disable();
1467 var panel = /** @type {!WebInspector.ElementsPanel} */ (WebInspector.inspectorView.panel("elements"));
1468 if (node instanceof WebInspector.DOMNode)
1469 panel.revealAndSelectNode(/** @type {!WebInspector.DOMNode} */ (node));
1470 else if (node instanceof WebInspector.DeferredDOMNode)
1471 (/** @type {!WebInspector.DeferredDOMNode} */ (node)).resolve(onNodeResolved);
1474 * @param {?WebInspector.DOMNode} resolvedNode
1476 function onNodeResolved(resolvedNode)
1479 panel.revealAndSelectNode(resolvedNode);
1486 * @implements {WebInspector.Revealer}
1488 WebInspector.ElementsPanel.NodeRemoteObjectRevealer = function()
1492 WebInspector.ElementsPanel.NodeRemoteObjectRevealer.prototype = {
1494 * @param {!Object} remoteObject
1496 reveal: function(remoteObject)
1498 revealElement(/** @type {!WebInspector.RemoteObject} */ (remoteObject));
1501 * @param {?WebInspector.RemoteObject} remoteObject
1503 function revealElement(remoteObject)
1506 remoteObject.pushNodeToFrontend(selectNode.bind(null, remoteObject));
1510 * @param {?WebInspector.RemoteObject} remoteObject
1511 * @param {?WebInspector.DOMNode} node
1513 function selectNode(remoteObject, node)
1516 WebInspector.Revealer.reveal(node);
1519 if (!remoteObject || remoteObject.description !== "#text" || !remoteObject.isNode())
1521 remoteObject.callFunction(parentElement, undefined, revealElement);
1525 * @suppressReceiverCheck
1528 function parentElement()
1530 return this.parentElement;
1538 WebInspector.ElementsPanel.NodeRemoteObjectInspector = function()
1542 WebInspector.ElementsPanel.NodeRemoteObjectInspector.prototype = {
1544 * @param {!Object} object
1546 inspectNodeObject: function(object)
1548 var remoteObject = /** @type {!WebInspector.RemoteObject} */ (object);
1549 if (!remoteObject.isNode()) {
1550 remoteObject.release();
1553 var elementsPanel = /** @type {!WebInspector.ElementsPanel} */ (WebInspector.inspectorView.panel("elements"));
1554 revealElement(remoteObject);
1557 * @param {?WebInspector.RemoteObject} remoteObject
1559 function revealElement(remoteObject)
1563 remoteObject.pushNodeToFrontend(selectNode.bind(null, remoteObject));
1564 elementsPanel.omitDefaultSelection();
1565 WebInspector.inspectorView.setCurrentPanel(elementsPanel);
1569 * @param {!WebInspector.RemoteObject} remoteObject
1570 * @param {?WebInspector.DOMNode} node
1572 function selectNode(remoteObject, node)
1574 elementsPanel.stopOmittingDefaultSelection();
1576 WebInspector.Revealer.reveal(node);
1577 if (!WebInspector._notFirstInspectElement && !WebInspector.inspectorView.drawerVisible())
1578 InspectorFrontendHost.inspectElementCompleted();
1579 WebInspector._notFirstInspectElement = true;
1580 remoteObject.release();
1583 if (remoteObject.description !== "#text" || !remoteObject.isNode()) {
1584 remoteObject.release();
1587 remoteObject.callFunction(parentElement, undefined, revealElement);
1591 * @suppressReceiverCheck
1594 function parentElement()
1596 return this.parentElement;