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("elements/elementsPanel.css");
42 this._splitWidget
= new WebInspector
.SplitWidget(true, true, "elementsPanelSplitViewState", 325, 325);
43 this._splitWidget
.addEventListener(WebInspector
.SplitWidget
.Events
.SidebarSizeChanged
, this._updateTreeOutlineVisibleWidth
.bind(this));
44 this._splitWidget
.show(this.element
);
46 this._searchableView
= new WebInspector
.SearchableView(this);
47 this._searchableView
.setMinimumSize(25, 19);
48 this._searchableView
.setPlaceholder(WebInspector
.UIString("Find by string, selector, or XPath"));
49 var stackElement
= this._searchableView
.element
;
51 this._contentElement
= createElement("div");
52 var crumbsContainer
= createElement("div");
53 if (Runtime
.experiments
.isEnabled("materialDesign")) {
54 this._toolbar
= this._createElementsToolbar();
55 var toolbar
= stackElement
.createChild("div", "elements-topbar hbox");
56 toolbar
.appendChild(crumbsContainer
);
57 toolbar
.appendChild(this._toolbar
.element
);
58 stackElement
.appendChild(this._contentElement
);
60 stackElement
.appendChild(this._contentElement
);
61 stackElement
.appendChild(crumbsContainer
);
64 this._elementsPanelTreeOutilneSplit
= new WebInspector
.SplitWidget(false, true, "treeOutlineAnimationTimelineWidget", 300, 300);
65 this._elementsPanelTreeOutilneSplit
.hideSidebar();
66 this._elementsPanelTreeOutilneSplit
.setMainWidget(this._searchableView
);
67 this._splitWidget
.setMainWidget(this._elementsPanelTreeOutilneSplit
);
69 this._contentElement
.id
= "elements-content";
70 // FIXME: crbug.com/425984
71 if (WebInspector
.moduleSetting("domWordWrap").get())
72 this._contentElement
.classList
.add("elements-wrap");
73 WebInspector
.moduleSetting("domWordWrap").addChangeListener(this._domWordWrapSettingChanged
.bind(this));
75 crumbsContainer
.id
= "elements-crumbs";
76 this._breadcrumbs
= new WebInspector
.ElementsBreadcrumbs();
77 this._breadcrumbs
.show(crumbsContainer
);
78 this._breadcrumbs
.addEventListener(WebInspector
.ElementsBreadcrumbs
.Events
.NodeSelected
, this._crumbNodeSelected
, this);
80 this.sidebarPanes
= {};
81 /** @type !Array<!WebInspector.ElementsSidebarViewWrapperPane> */
82 this._elementsSidebarViewWrappers
= [];
83 var sharedSidebarModel
= new WebInspector
.SharedSidebarModel();
84 this.sidebarPanes
.platformFonts
= WebInspector
.PlatformFontsWidget
.createSidebarWrapper(sharedSidebarModel
);
85 this.sidebarPanes
.styles
= new WebInspector
.StylesSidebarPane(this._showStylesSidebar
.bind(this));
87 this.sidebarPanes
.computedStyle
= WebInspector
.ComputedStyleWidget
.createSidebarWrapper(this.sidebarPanes
.styles
, sharedSidebarModel
);
89 this.sidebarPanes
.styles
.addEventListener(WebInspector
.StylesSidebarPane
.Events
.SelectorEditingStarted
, this._onEditingSelectorStarted
.bind(this));
90 this.sidebarPanes
.styles
.addEventListener(WebInspector
.StylesSidebarPane
.Events
.SelectorEditingEnded
, this._onEditingSelectorEnded
.bind(this));
92 this.sidebarPanes
.metrics
= new WebInspector
.MetricsSidebarPane();
93 this.sidebarPanes
.properties
= WebInspector
.PropertiesWidget
.createSidebarWrapper();
94 this.sidebarPanes
.domBreakpoints
= WebInspector
.domBreakpointsSidebarPane
.createProxy(this);
95 this.sidebarPanes
.eventListeners
= WebInspector
.EventListenersWidget
.createSidebarWrapper();
97 WebInspector
.dockController
.addEventListener(WebInspector
.DockController
.Events
.DockSideChanged
, this._dockSideChanged
.bind(this));
98 WebInspector
.moduleSetting("splitVerticallyWhenDockedToRight").addChangeListener(this._dockSideChanged
.bind(this));
99 this._dockSideChanged();
100 this._loadSidebarViews();
102 /** @type {!Array.<!WebInspector.ElementsTreeOutline>} */
103 this._treeOutlines
= [];
104 /** @type {!Map.<!WebInspector.DOMModel, !WebInspector.ElementsTreeOutline>} */
105 this._modelToTreeOutline
= new Map();
106 WebInspector
.targetManager
.observeTargets(this);
107 WebInspector
.moduleSetting("showUAShadowDOM").addChangeListener(this._showUAShadowDOMChanged
.bind(this));
108 WebInspector
.targetManager
.addModelListener(WebInspector
.DOMModel
, WebInspector
.DOMModel
.Events
.DocumentUpdated
, this._documentUpdatedEvent
, this);
109 WebInspector
.targetManager
.addModelListener(WebInspector
.CSSStyleModel
, WebInspector
.CSSStyleModel
.Events
.PseudoStateForced
, this._pseudoStateForced
, this);
110 WebInspector
.extensionServer
.addEventListener(WebInspector
.ExtensionServer
.Events
.SidebarPaneAdded
, this._extensionSidebarPaneAdded
, this);
113 WebInspector
.ElementsPanel
._elementsSidebarViewTitleSymbol
= Symbol("title");
115 WebInspector
.ElementsPanel
.prototype = {
117 * @return {!WebInspector.Toolbar}
119 _createElementsToolbar: function()
121 var toolbar
= new WebInspector
.ExtensibleToolbar("elements-toolbar");
122 toolbar
.element
.classList
.add("elements-toolbar");
123 this._hideElementButton
= new WebInspector
.ToolbarButton(WebInspector
.UIString("Hide element"), "visibility-toolbar-item");
124 this._hideElementButton
.setAction("elements.hide-element");
125 toolbar
.appendToolbarItem(this._hideElementButton
);
127 this._editAsHTMLButton
= new WebInspector
.ToolbarButton(WebInspector
.UIString("Edit as HTML"), "edit-toolbar-item");
128 this._editAsHTMLButton
.setAction("elements.edit-as-html");
129 toolbar
.appendToolbarItem(this._editAsHTMLButton
);
131 this._breakpointsButton
= new WebInspector
.ToolbarMenuButton(WebInspector
.UIString("Toggle breakpoints"), "breakpoint-toolbar-item", this._showBreakpointsMenu
.bind(this));
132 toolbar
.appendToolbarItem(this._breakpointsButton
);
134 this._forceElementStateButton
= new WebInspector
.ToolbarMenuButton(WebInspector
.UIString("Force element state"), "pin-toolbar-item", this._showForceElementStateMenu
.bind(this));
135 toolbar
.appendToolbarItem(this._forceElementStateButton
);
137 toolbar
.appendSeparator();
141 _toggleHideElement: function()
143 var node
= this.selectedDOMNode();
144 var treeOutline
= this._treeOutlineForNode(node
);
145 if (!node
|| !treeOutline
)
147 treeOutline
.toggleHideElement(node
);
148 this._hideElementButton
.setToggled(!this._hideElementButton
.toggled());
149 this._hideElementButton
.element
.classList
.toggle("visibility-off-toolbar-item", this._hideElementButton
.toggled());
150 this._hideElementButton
.element
.classList
.toggle("visibility-toolbar-item", !this._hideElementButton
.toggled());
153 _updateToolbarButtons: function()
155 var node
= this.selectedDOMNode();
158 var classText
= node
.getAttribute("class");
159 this._hideElementButton
.setToggled(classText
&& classText
.match(/__web-inspector-hide/));
160 this._hideElementButton
.element
.classList
.toggle("visibility-off-toolbar-item", this._hideElementButton
.toggled());
161 this._hideElementButton
.element
.classList
.toggle("visibility-toolbar-item", !this._hideElementButton
.toggled());
162 this._editAsHTMLButton
.setToggled(false);
163 this._breakpointsButton
.setEnabled(!node
.pseudoType());
164 this._forceElementStateButton
.setEnabled(node
.nodeType() === Node
.ELEMENT_NODE
&& !node
.pseudoType());
167 _toggleEditAsHTML: function()
169 var node
= this.selectedDOMNode();
170 var treeOutline
= this._treeOutlineForNode(node
);
171 if (!node
|| !treeOutline
)
173 var startEditing
= !this._editAsHTMLButton
.toggled();
174 treeOutline
.toggleEditAsHTML(node
, startEditing
, this._updateToolbarButtons
.bind(this));
175 this._editAsHTMLButton
.setToggled(startEditing
);
179 * @param {!WebInspector.ContextMenu} contextMenu
181 _showBreakpointsMenu: function(contextMenu
)
183 var node
= this.selectedDOMNode();
186 WebInspector
.domBreakpointsSidebarPane
.populateNodeContextMenu(node
, contextMenu
, false);
190 * @param {!WebInspector.ContextMenu} contextMenu
192 _showForceElementStateMenu: function(contextMenu
)
194 var node
= this.selectedDOMNode();
197 WebInspector
.ElementsTreeElement
.populateForcedPseudoStateItems(contextMenu
, node
);
200 _loadSidebarViews: function()
202 var extensions
= self
.runtime
.extensions("@WebInspector.Widget");
204 for (var i
= 0; i
< extensions
.length
; ++i
) {
205 var descriptor
= extensions
[i
].descriptor();
206 if (descriptor
["location"] !== "elements-panel")
209 var title
= WebInspector
.UIString(descriptor
["title"]);
210 extensions
[i
].instancePromise().then(addSidebarView
.bind(this, title
));
214 * @param {string} title
215 * @param {!Object} object
216 * @this {WebInspector.ElementsPanel}
218 function addSidebarView(title
, object
)
220 var widget
= /** @type {!WebInspector.Widget} */ (object
);
221 var elementsSidebarViewWrapperPane
= new WebInspector
.ElementsSidebarViewWrapperPane(title
, widget
);
222 this._elementsSidebarViewWrappers
.push(elementsSidebarViewWrapperPane
);
224 if (this.sidebarPaneView
)
225 this.sidebarPaneView
.addPane(elementsSidebarViewWrapperPane
);
229 _onEditingSelectorStarted: function()
231 for (var i
= 0; i
< this._treeOutlines
.length
; ++i
)
232 this._treeOutlines
[i
].setPickNodeMode(true);
235 _onEditingSelectorEnded: function()
237 for (var i
= 0; i
< this._treeOutlines
.length
; ++i
)
238 this._treeOutlines
[i
].setPickNodeMode(false);
243 * @param {!WebInspector.Target} target
245 targetAdded: function(target
)
247 var domModel
= WebInspector
.DOMModel
.fromTarget(target
);
250 var treeOutline
= new WebInspector
.ElementsTreeOutline(domModel
, true, true);
251 treeOutline
.setWordWrap(WebInspector
.moduleSetting("domWordWrap").get());
252 treeOutline
.wireToDOMModel();
253 treeOutline
.addEventListener(WebInspector
.ElementsTreeOutline
.Events
.SelectedNodeChanged
, this._selectedNodeChanged
, this);
254 treeOutline
.addEventListener(WebInspector
.ElementsTreeOutline
.Events
.NodePicked
, this._onNodePicked
, this);
255 treeOutline
.addEventListener(WebInspector
.ElementsTreeOutline
.Events
.ElementsTreeUpdated
, this._updateBreadcrumbIfNeeded
, this);
256 this._treeOutlines
.push(treeOutline
);
257 this._modelToTreeOutline
.set(domModel
, treeOutline
);
259 // Perform attach if necessary.
260 if (this.isShowing())
266 * @param {!WebInspector.Target} target
268 targetRemoved: function(target
)
270 var domModel
= WebInspector
.DOMModel
.fromTarget(target
);
273 var treeOutline
= this._modelToTreeOutline
.remove(domModel
);
274 treeOutline
.unwireFromDOMModel();
275 this._treeOutlines
.remove(treeOutline
);
276 treeOutline
.element
.remove();
279 _updateTreeOutlineVisibleWidth: function()
281 if (!this._treeOutlines
.length
)
284 var width
= this._splitWidget
.element
.offsetWidth
;
285 if (this._splitWidget
.isVertical())
286 width
-= this._splitWidget
.sidebarSize();
287 for (var i
= 0; i
< this._treeOutlines
.length
; ++i
) {
288 this._treeOutlines
[i
].setVisibleWidth(width
);
289 this._treeOutlines
[i
].updateSelection();
291 this._breadcrumbs
.updateSizes();
298 defaultFocusedElement: function()
300 return this._treeOutlines
.length
? this._treeOutlines
[0].element
: this.element
;
305 * @return {!WebInspector.SearchableView}
307 searchableView: function()
309 return this._searchableView
;
314 for (var i
= 0; i
< this._treeOutlines
.length
; ++i
) {
315 var treeOutline
= this._treeOutlines
[i
];
316 // Attach heavy component lazily
317 if (treeOutline
.element
.parentElement
!== this._contentElement
)
318 this._contentElement
.appendChild(treeOutline
.element
);
320 WebInspector
.Panel
.prototype.wasShown
.call(this);
321 this._breadcrumbs
.update();
323 for (var i
= 0; i
< this._treeOutlines
.length
; ++i
) {
324 var treeOutline
= this._treeOutlines
[i
];
325 treeOutline
.updateSelection();
326 treeOutline
.setVisible(true);
328 if (!treeOutline
.rootDOMNode
)
329 if (treeOutline
.domModel().existingDocument())
330 this._documentUpdated(treeOutline
.domModel(), treeOutline
.domModel().existingDocument());
332 treeOutline
.domModel().requestDocument();
339 WebInspector
.DOMModel
.hideDOMNodeHighlight();
340 for (var i
= 0; i
< this._treeOutlines
.length
; ++i
) {
341 var treeOutline
= this._treeOutlines
[i
];
342 treeOutline
.setVisible(false);
343 // Detach heavy component on hide
344 this._contentElement
.removeChild(treeOutline
.element
);
346 if (this._popoverHelper
)
347 this._popoverHelper
.hidePopover();
348 WebInspector
.Panel
.prototype.willHide
.call(this);
353 this._updateTreeOutlineVisibleWidth();
357 * @param {!WebInspector.Event} event
359 _pseudoStateForced: function(event
)
361 var node
= /** @type {!WebInspector.DOMNode} */ (event
.data
["node"]);
362 this._treeOutlineForNode(node
).updateOpenCloseTags(node
);
366 * @param {!WebInspector.Event} event
368 _onNodePicked: function(event
)
370 if (!this.sidebarPanes
.styles
.isEditingSelector())
372 this.sidebarPanes
.styles
.updateEditingSelectorForNode(/** @type {!WebInspector.DOMNode} */(event
.data
));
376 * @param {!WebInspector.Event} event
378 _selectedNodeChanged: function(event
)
380 var selectedNode
= /** @type {?WebInspector.DOMNode} */ (event
.data
);
381 for (var i
= 0; i
< this._treeOutlines
.length
; ++i
) {
382 if (!selectedNode
|| selectedNode
.domModel() !== this._treeOutlines
[i
].domModel())
383 this._treeOutlines
[i
].selectDOMNode(null);
386 if (!selectedNode
&& this._lastValidSelectedNode
)
387 this._selectedPathOnReset
= this._lastValidSelectedNode
.path();
389 this._breadcrumbs
.setSelectedNode(selectedNode
);
391 WebInspector
.context
.setFlavor(WebInspector
.DOMNode
, selectedNode
);
394 selectedNode
.setAsInspectedNode();
395 this._lastValidSelectedNode
= selectedNode
;
397 WebInspector
.notifications
.dispatchEventToListeners(WebInspector
.NotificationService
.Events
.SelectedNodeChanged
);
398 this._selectedNodeChangedForTest();
399 if (Runtime
.experiments
.isEnabled("materialDesign"))
400 this._updateToolbarButtons();
403 _selectedNodeChangedForTest: function() { },
407 delete this.currentQuery
;
411 * @param {!WebInspector.Event} event
413 _documentUpdatedEvent: function(event
)
415 this._documentUpdated(/** @type {!WebInspector.DOMModel} */ (event
.target
), /** @type {?WebInspector.DOMDocument} */ (event
.data
));
419 * @param {!WebInspector.DOMModel} domModel
420 * @param {?WebInspector.DOMDocument} inspectedRootDocument
422 _documentUpdated: function(domModel
, inspectedRootDocument
)
425 this.searchCanceled();
427 var treeOutline
= this._modelToTreeOutline
.get(domModel
);
428 treeOutline
.rootDOMNode
= inspectedRootDocument
;
430 if (!inspectedRootDocument
) {
431 if (this.isShowing())
432 domModel
.requestDocument();
436 WebInspector
.domBreakpointsSidebarPane
.restoreBreakpoints(domModel
);
439 * @this {WebInspector.ElementsPanel}
440 * @param {?WebInspector.DOMNode} candidateFocusNode
442 function selectNode(candidateFocusNode
)
444 if (!candidateFocusNode
)
445 candidateFocusNode
= inspectedRootDocument
.body
|| inspectedRootDocument
.documentElement
;
447 if (!candidateFocusNode
)
450 if (!this._pendingNodeReveal
) {
451 this.selectDOMNode(candidateFocusNode
);
452 if (treeOutline
.selectedTreeElement
)
453 treeOutline
.selectedTreeElement
.expand();
458 * @param {?DOMAgent.NodeId} nodeId
459 * @this {WebInspector.ElementsPanel}
461 function selectLastSelectedNode(nodeId
)
463 if (this.selectedDOMNode()) {
464 // Focused node has been explicitly set while reaching out for the last selected node.
467 var node
= nodeId
? domModel
.nodeForId(nodeId
) : null;
468 selectNode
.call(this, node
);
471 if (this._omitDefaultSelection
)
474 if (this._selectedPathOnReset
)
475 domModel
.pushNodeByPathToFrontend(this._selectedPathOnReset
, selectLastSelectedNode
.bind(this));
477 selectNode
.call(this, null);
478 delete this._selectedPathOnReset
;
484 searchCanceled: function()
486 delete this._searchQuery
;
487 this._hideSearchHighlights();
489 this._searchableView
.updateSearchMatchesCount(0);
491 delete this._currentSearchResultIndex
;
492 delete this._searchResults
;
494 WebInspector
.DOMModel
.cancelSearch();
499 * @param {!WebInspector.SearchableView.SearchConfig} searchConfig
500 * @param {boolean} shouldJump
501 * @param {boolean=} jumpBackwards
503 performSearch: function(searchConfig
, shouldJump
, jumpBackwards
)
505 var query
= searchConfig
.query
;
506 // Call searchCanceled since it will reset everything we need before doing a new search.
507 this.searchCanceled();
509 const whitespaceTrimmedQuery
= query
.trim();
510 if (!whitespaceTrimmedQuery
.length
)
513 this._searchQuery
= query
;
516 var domModels
= WebInspector
.DOMModel
.instances();
517 for (var domModel
of domModels
)
518 promises
.push(domModel
.performSearchPromise(whitespaceTrimmedQuery
, WebInspector
.moduleSetting("showUAShadowDOM").get()));
519 Promise
.all(promises
).then(resultCountCallback
.bind(this));
522 * @param {!Array.<number>} resultCounts
523 * @this {WebInspector.ElementsPanel}
525 function resultCountCallback(resultCounts
)
528 * @type {!Array.<{domModel: !WebInspector.DOMModel, index: number, node: (?WebInspector.DOMNode|undefined)}>}
530 this._searchResults
= [];
531 for (var i
= 0; i
< resultCounts
.length
; ++i
) {
532 var resultCount
= resultCounts
[i
];
533 for (var j
= 0; j
< resultCount
; ++j
)
534 this._searchResults
.push({domModel
: domModels
[i
], index
: j
, node
: undefined});
536 this._searchableView
.updateSearchMatchesCount(this._searchResults
.length
);
537 if (!this._searchResults
.length
)
539 this._currentSearchResultIndex
= -1;
542 this._jumpToSearchResult(jumpBackwards
? -1 : 0);
546 _domWordWrapSettingChanged: function(event
)
548 // FIXME: crbug.com/425984
549 this._contentElement
.classList
.toggle("elements-wrap", event
.data
);
550 for (var i
= 0; i
< this._treeOutlines
.length
; ++i
)
551 this._treeOutlines
[i
].setWordWrap(/** @type {boolean} */ (event
.data
));
553 var selectedNode
= this.selectedDOMNode();
557 var treeElement
= this._treeElementForNode(selectedNode
);
559 treeElement
.updateSelection(); // Recalculate selection highlight dimensions.
562 switchToAndFocus: function(node
)
564 // Reset search restore.
565 this._searchableView
.cancelSearch();
566 WebInspector
.inspectorView
.setCurrentPanel(this);
567 this.selectDOMNode(node
, true);
571 * @param {!Element} element
572 * @param {!Event} event
573 * @return {!Element|!AnchorBox|undefined}
575 _getPopoverAnchor: function(element
, event
)
577 var anchor
= element
.enclosingNodeOrSelfWithClass("webkit-html-resource-link");
578 if (!anchor
|| !anchor
.href
)
585 * @param {!Element} anchor
586 * @param {!WebInspector.Popover} popover
588 _showPopover: function(anchor
, popover
)
590 var node
= this.selectedDOMNode();
592 WebInspector
.DOMPresentationUtils
.buildImagePreviewContents(node
.target(), anchor
.href
, true, showPopover
);
595 * @param {!Element=} contents
597 function showPopover(contents
)
601 popover
.setCanShrink(false);
602 popover
.showForAnchor(contents
, anchor
);
606 _jumpToSearchResult: function(index
)
608 this._hideSearchHighlights();
609 this._currentSearchResultIndex
= (index
+ this._searchResults
.length
) % this._searchResults
.length
;
610 this._highlightCurrentSearchResult();
616 jumpToNextSearchResult: function()
618 if (!this._searchResults
)
620 this._jumpToSearchResult(this._currentSearchResultIndex
+ 1);
626 jumpToPreviousSearchResult: function()
628 if (!this._searchResults
)
630 this._jumpToSearchResult(this._currentSearchResultIndex
- 1);
637 supportsCaseSensitiveSearch: function()
646 supportsRegexSearch: function()
651 _highlightCurrentSearchResult: function()
653 var index
= this._currentSearchResultIndex
;
654 var searchResults
= this._searchResults
;
655 var searchResult
= searchResults
[index
];
657 if (searchResult
.node
=== null) {
658 this._searchableView
.updateCurrentMatchIndex(index
);
663 * @param {?WebInspector.DOMNode} node
664 * @this {WebInspector.ElementsPanel}
666 function searchCallback(node
)
668 searchResult
.node
= node
;
669 this._highlightCurrentSearchResult();
672 if (typeof searchResult
.node
=== "undefined") {
673 // No data for slot, request it.
674 searchResult
.domModel
.searchResult(searchResult
.index
, searchCallback
.bind(this));
678 this._searchableView
.updateCurrentMatchIndex(index
);
680 var treeElement
= this._treeElementForNode(searchResult
.node
);
682 treeElement
.highlightSearchResults(this._searchQuery
);
683 treeElement
.reveal();
684 var matches
= treeElement
.listItemElement
.getElementsByClassName(WebInspector
.highlightedSearchResultClassName
);
686 matches
[0].scrollIntoViewIfNeeded();
690 _hideSearchHighlights: function()
692 if (!this._searchResults
|| !this._searchResults
.length
|| this._currentSearchResultIndex
< 0)
694 var searchResult
= this._searchResults
[this._currentSearchResultIndex
];
695 if (!searchResult
.node
)
697 var treeOutline
= this._modelToTreeOutline
.get(searchResult
.node
.domModel());
698 var treeElement
= treeOutline
.findTreeElement(searchResult
.node
);
700 treeElement
.hideSearchHighlights();
704 * @return {?WebInspector.DOMNode}
706 selectedDOMNode: function()
708 for (var i
= 0; i
< this._treeOutlines
.length
; ++i
) {
709 var treeOutline
= this._treeOutlines
[i
];
710 if (treeOutline
.selectedDOMNode())
711 return treeOutline
.selectedDOMNode();
717 * @param {!WebInspector.DOMNode} node
718 * @param {boolean=} focus
720 selectDOMNode: function(node
, focus
)
722 for (var i
= 0; i
< this._treeOutlines
.length
; ++i
) {
723 var treeOutline
= this._treeOutlines
[i
];
724 if (treeOutline
.domModel() === node
.domModel())
725 treeOutline
.selectDOMNode(node
, focus
);
727 treeOutline
.selectDOMNode(null);
732 * @param {!WebInspector.Event} event
734 _updateBreadcrumbIfNeeded: function(event
)
736 var nodes
= /** @type {!Array.<!WebInspector.DOMNode>} */ (event
.data
);
737 this._breadcrumbs
.updateNodes(nodes
);
741 * @param {!WebInspector.Event} event
743 _crumbNodeSelected: function(event
)
745 var node
= /** @type {!WebInspector.DOMNode} */ (event
.data
);
746 this.selectDOMNode(node
, true);
751 * @param {!KeyboardEvent} event
753 handleShortcut: function(event
)
756 * @param {!WebInspector.ElementsTreeOutline} treeOutline
758 function handleUndoRedo(treeOutline
)
760 if (WebInspector
.KeyboardShortcut
.eventHasCtrlOrMeta(event
) && !event
.shiftKey
&& event
.keyIdentifier
=== "U+005A") { // Z key
761 treeOutline
.domModel().undo();
762 event
.handled
= true;
766 var isRedoKey
= WebInspector
.isMac() ? event
.metaKey
&& event
.shiftKey
&& event
.keyIdentifier
=== "U+005A" : // Z key
767 event
.ctrlKey
&& event
.keyIdentifier
=== "U+0059"; // Y key
769 treeOutline
.domModel().redo();
770 event
.handled
= true;
774 if (WebInspector
.isEditing() && event
.keyCode
!== WebInspector
.KeyboardShortcut
.Keys
.F2
.code
)
777 var treeOutline
= null;
778 for (var i
= 0; i
< this._treeOutlines
.length
; ++i
) {
779 if (this._treeOutlines
[i
].selectedDOMNode() === this._lastValidSelectedNode
)
780 treeOutline
= this._treeOutlines
[i
];
785 if (!treeOutline
.editing()) {
786 handleUndoRedo
.call(null, treeOutline
);
788 this.sidebarPanes
.styles
.onUndoOrRedoHappened();
793 treeOutline
.handleShortcut(event
);
797 WebInspector
.Panel
.prototype.handleShortcut
.call(this, event
);
801 * @param {?WebInspector.DOMNode} node
802 * @return {?WebInspector.ElementsTreeOutline}
804 _treeOutlineForNode: function(node
)
808 return this._modelToTreeOutline
.get(node
.domModel()) || null;
812 * @return {?WebInspector.ElementsTreeOutline}
814 _focusedTreeOutline: function()
816 for (var i
= 0; i
< this._treeOutlines
.length
; ++i
) {
817 if (this._treeOutlines
[i
].hasFocus())
818 return this._treeOutlines
[i
];
824 * @param {!WebInspector.DOMNode} node
825 * @return {?WebInspector.ElementsTreeElement}
827 _treeElementForNode: function(node
)
829 var treeOutline
= this._treeOutlineForNode(node
);
830 return /** @type {?WebInspector.ElementsTreeElement} */ (treeOutline
.findTreeElement(node
));
834 * @param {!Event} event
836 handleCopyEvent: function(event
)
838 var treeOutline
= this._focusedTreeOutline();
840 treeOutline
.handleCopyOrCutKeyboardEvent(false, event
);
844 * @param {!Event} event
846 handleCutEvent: function(event
)
848 var treeOutline
= this._focusedTreeOutline();
850 treeOutline
.handleCopyOrCutKeyboardEvent(true, event
);
854 * @param {!Event} event
856 handlePasteEvent: function(event
)
858 var treeOutline
= this._focusedTreeOutline();
860 treeOutline
.handlePasteKeyboardEvent(event
);
864 * @param {!WebInspector.DOMNode} node
865 * @return {!WebInspector.DOMNode}
867 _leaveUserAgentShadowDOM: function(node
)
869 var userAgentShadowRoot
= node
.ancestorUserAgentShadowRoot();
870 return userAgentShadowRoot
? /** @type {!WebInspector.DOMNode} */ (userAgentShadowRoot
.parentNode
) : node
;
874 * @param {!WebInspector.DOMNode} node
876 revealAndSelectNode: function(node
)
878 if (WebInspector
.inspectElementModeController
&& WebInspector
.inspectElementModeController
.enabled()) {
879 InspectorFrontendHost
.bringToFront();
880 WebInspector
.inspectElementModeController
.disable();
883 this._omitDefaultSelection
= true;
884 WebInspector
.inspectorView
.setCurrentPanel(this);
885 node
= WebInspector
.moduleSetting("showUAShadowDOM").get() ? node
: this._leaveUserAgentShadowDOM(node
);
886 node
.highlightForTwoSeconds();
887 this.selectDOMNode(node
, true);
888 delete this._omitDefaultSelection
;
890 if (!this._notFirstInspectElement
)
891 InspectorFrontendHost
.inspectElementCompleted();
892 this._notFirstInspectElement
= true;
896 * @param {!Event} event
897 * @param {!WebInspector.ContextMenu} contextMenu
898 * @param {!Object} object
900 appendApplicableItems: function(event
, contextMenu
, object
)
902 if (!(object
instanceof WebInspector
.RemoteObject
&& (/** @type {!WebInspector.RemoteObject} */ (object
)).isNode())
903 && !(object
instanceof WebInspector
.DOMNode
)
904 && !(object
instanceof WebInspector
.DeferredDOMNode
)) {
908 // Add debbuging-related actions
909 if (object
instanceof WebInspector
.DOMNode
) {
910 contextMenu
.appendSeparator();
911 WebInspector
.domBreakpointsSidebarPane
.populateNodeContextMenu(object
, contextMenu
, true);
914 // Skip adding "Reveal..." menu item for our own tree outline.
915 if (this.element
.isAncestor(/** @type {!Node} */ (event
.target
)))
917 var commandCallback
= WebInspector
.Revealer
.reveal
.bind(WebInspector
.Revealer
, object
);
919 contextMenu
.appendItem(WebInspector
.UIString
.capitalize("Reveal in Elements ^panel"), commandCallback
);
922 _sidebarContextMenuEventFired: function(event
)
924 var contextMenu
= new WebInspector
.ContextMenu(event
);
925 contextMenu
.appendApplicableItems(/** @type {!Object} */ (event
.deepElementFromPoint()));
929 _dockSideChanged: function()
931 var vertically
= WebInspector
.dockController
.isVertical() && WebInspector
.moduleSetting("splitVerticallyWhenDockedToRight").get();
932 this._splitVertically(vertically
);
935 _showUAShadowDOMChanged: function()
937 for (var i
= 0; i
< this._treeOutlines
.length
; ++i
)
938 this._treeOutlines
[i
].update();
941 _showStylesSidebar: function()
943 this.sidebarPaneView
.selectTab(this.sidebarPanes
.styles
.title());
947 * @param {boolean} vertically
949 _splitVertically: function(vertically
)
951 if (this.sidebarPaneView
&& vertically
=== !this._splitWidget
.isVertical())
954 var extensionSidebarPanes
= WebInspector
.extensionServer
.sidebarPanes();
955 if (this.sidebarPaneView
&& extensionSidebarPanes
.length
)
956 return; // We can't reparent extension iframes.
958 if (this.sidebarPaneView
) {
959 this.sidebarPaneView
.detach();
960 this._splitWidget
.uninstallResizer(this.sidebarPaneView
.headerElement());
963 this._splitWidget
.setVertical(!vertically
);
965 var computedPane
= new WebInspector
.SidebarPane(WebInspector
.UIString("Computed"));
966 computedPane
.element
.classList
.add("composite");
967 computedPane
.element
.classList
.add("fill");
969 computedPane
.element
.classList
.add("metrics-and-computed");
971 var matchedStylePanesWrapper
= new WebInspector
.VBox();
972 matchedStylePanesWrapper
.element
.classList
.add("style-panes-wrapper");
973 var computedStylePanesWrapper
= new WebInspector
.VBox();
974 computedStylePanesWrapper
.element
.classList
.add("style-panes-wrapper");
977 * @param {boolean} inComputedStyle
978 * @this {WebInspector.ElementsPanel}
980 function showMetrics(inComputedStyle
)
983 this.sidebarPanes
.metrics
.show(computedStylePanesWrapper
.element
, this.sidebarPanes
.computedStyle
.element
);
985 this.sidebarPanes
.metrics
.show(matchedStylePanesWrapper
.element
);
989 * @param {!WebInspector.Event} event
990 * @this {WebInspector.ElementsPanel}
992 function tabSelected(event
)
994 var tabId
= /** @type {string} */ (event
.data
.tabId
);
995 if (tabId
=== computedPane
.title())
996 showMetrics
.call(this, true);
997 else if (tabId
=== stylesPane
.title())
998 showMetrics
.call(this, false);
1001 this.sidebarPaneView
= new WebInspector
.SidebarTabbedPane();
1002 this.sidebarPaneView
.element
.addEventListener("contextmenu", this._sidebarContextMenuEventFired
.bind(this), false);
1003 if (this._popoverHelper
)
1004 this._popoverHelper
.hidePopover();
1005 this._popoverHelper
= new WebInspector
.PopoverHelper(this.sidebarPaneView
.element
, this._getPopoverAnchor
.bind(this), this._showPopover
.bind(this));
1006 this._popoverHelper
.setTimeout(0);
1009 this._splitWidget
.installResizer(this.sidebarPaneView
.headerElement());
1011 var compositePane
= new WebInspector
.SidebarPane(this.sidebarPanes
.styles
.title());
1012 compositePane
.element
.classList
.add("composite");
1013 compositePane
.element
.classList
.add("fill");
1015 var splitWidget
= new WebInspector
.SplitWidget(true, true, "stylesPaneSplitViewState", 215);
1016 splitWidget
.show(compositePane
.element
);
1018 splitWidget
.setMainWidget(matchedStylePanesWrapper
);
1019 splitWidget
.setSidebarWidget(computedStylePanesWrapper
);
1021 computedPane
.show(computedStylePanesWrapper
.element
);
1022 this.sidebarPaneView
.addPane(compositePane
);
1024 var stylesPane
= new WebInspector
.SidebarPane(this.sidebarPanes
.styles
.title());
1025 stylesPane
.element
.classList
.add("composite", "fill", "metrics-and-styles");
1027 matchedStylePanesWrapper
.show(stylesPane
.element
);
1028 computedStylePanesWrapper
.show(computedPane
.element
);
1030 this.sidebarPaneView
.addEventListener(WebInspector
.TabbedPane
.EventTypes
.TabSelected
, tabSelected
, this);
1032 this.sidebarPaneView
.addPane(stylesPane
);
1033 this.sidebarPaneView
.addPane(computedPane
);
1036 this.sidebarPanes
.styles
.show(matchedStylePanesWrapper
.element
);
1037 this.sidebarPanes
.computedStyle
.show(computedStylePanesWrapper
.element
);
1038 showMetrics
.call(this, vertically
);
1039 this.sidebarPanes
.platformFonts
.show(computedStylePanesWrapper
.element
);
1041 this.sidebarPaneView
.addPane(this.sidebarPanes
.eventListeners
);
1042 this.sidebarPaneView
.addPane(this.sidebarPanes
.domBreakpoints
);
1043 this.sidebarPaneView
.addPane(this.sidebarPanes
.properties
);
1045 for (var sidebarViewWrapper
of this._elementsSidebarViewWrappers
)
1046 this.sidebarPaneView
.addPane(sidebarViewWrapper
);
1048 this._extensionSidebarPanesContainer
= this.sidebarPaneView
;
1050 for (var i
= 0; i
< extensionSidebarPanes
.length
; ++i
)
1051 this._addExtensionSidebarPane(extensionSidebarPanes
[i
]);
1053 this._splitWidget
.setSidebarWidget(this.sidebarPaneView
);
1054 this.sidebarPanes
.styles
.expand();
1058 * @param {!WebInspector.Event} event
1060 _extensionSidebarPaneAdded: function(event
)
1062 var pane
= /** @type {!WebInspector.ExtensionSidebarPane} */ (event
.data
);
1063 this._addExtensionSidebarPane(pane
);
1067 * @param {!WebInspector.ExtensionSidebarPane} pane
1069 _addExtensionSidebarPane: function(pane
)
1071 if (pane
.panelName() === this.name
) {
1072 this.setHideOnDetach();
1073 this._extensionSidebarPanesContainer
.addPane(pane
);
1078 * @param {?WebInspector.Widget} widget
1080 setWidgetBelowDOM: function(widget
)
1083 this._elementsPanelTreeOutilneSplit
.setSidebarWidget(widget
);
1084 this._elementsPanelTreeOutilneSplit
.showBoth(true);
1086 this._elementsPanelTreeOutilneSplit
.hideSidebar(true);
1090 __proto__
: WebInspector
.Panel
.prototype
1095 * @implements {WebInspector.ContextMenu.Provider}
1097 WebInspector
.ElementsPanel
.ContextMenuProvider = function()
1101 WebInspector
.ElementsPanel
.ContextMenuProvider
.prototype = {
1104 * @param {!Event} event
1105 * @param {!WebInspector.ContextMenu} contextMenu
1106 * @param {!Object} target
1108 appendApplicableItems: function(event
, contextMenu
, target
)
1110 WebInspector
.ElementsPanel
.instance().appendApplicableItems(event
, contextMenu
, target
);
1116 * @implements {WebInspector.Revealer}
1118 WebInspector
.ElementsPanel
.DOMNodeRevealer = function()
1122 WebInspector
.ElementsPanel
.DOMNodeRevealer
.prototype = {
1125 * @param {!Object} node
1126 * @return {!Promise}
1128 reveal: function(node
)
1130 var panel
= WebInspector
.ElementsPanel
.instance();
1131 panel
._pendingNodeReveal
= true;
1133 return new Promise(revealPromise
);
1136 * @param {function(undefined)} resolve
1137 * @param {function(!Error)} reject
1139 function revealPromise(resolve
, reject
)
1141 if (node
instanceof WebInspector
.DOMNode
) {
1142 onNodeResolved(/** @type {!WebInspector.DOMNode} */ (node
));
1143 } else if (node
instanceof WebInspector
.DeferredDOMNode
) {
1144 (/** @type {!WebInspector.DeferredDOMNode} */ (node
)).resolve(onNodeResolved
);
1145 } else if (node
instanceof WebInspector
.RemoteObject
) {
1146 var domModel
= WebInspector
.DOMModel
.fromTarget(/** @type {!WebInspector.RemoteObject} */ (node
).target());
1148 domModel
.pushObjectAsNodeToFrontend(node
, onNodeResolved
);
1150 reject(new Error("Could not resolve a node to reveal."));
1152 reject(new Error("Can't reveal a non-node."));
1153 panel
._pendingNodeReveal
= false;
1157 * @param {?WebInspector.DOMNode} resolvedNode
1159 function onNodeResolved(resolvedNode
)
1161 panel
._pendingNodeReveal
= false;
1164 panel
.revealAndSelectNode(resolvedNode
);
1168 reject(new Error("Could not resolve node to reveal."));
1174 WebInspector
.ElementsPanel
.show = function()
1176 WebInspector
.inspectorView
.setCurrentPanel(WebInspector
.ElementsPanel
.instance());
1180 * @return {!WebInspector.ElementsPanel}
1182 WebInspector
.ElementsPanel
.instance = function()
1184 if (!WebInspector
.ElementsPanel
._instanceObject
)
1185 WebInspector
.ElementsPanel
._instanceObject
= new WebInspector
.ElementsPanel();
1186 return WebInspector
.ElementsPanel
._instanceObject
;
1191 * @implements {WebInspector.PanelFactory}
1193 WebInspector
.ElementsPanelFactory = function()
1197 WebInspector
.ElementsPanelFactory
.prototype = {
1200 * @return {!WebInspector.Panel}
1202 createPanel: function()
1204 return WebInspector
.ElementsPanel
.instance();
1210 * @implements {WebInspector.ActionDelegate}
1212 WebInspector
.ElementsActionDelegate = function() { }
1214 WebInspector
.ElementsActionDelegate
.prototype = {
1217 * @param {!WebInspector.Context} context
1218 * @param {string} actionId
1220 handleAction: function(context
, actionId
)
1222 var elementsPanel
= WebInspector
.ElementsPanel
.instance();
1223 if (actionId
=== "elements.hide-element")
1224 elementsPanel
._toggleHideElement();
1225 else if (actionId
=== "elements.edit-as-html")
1226 elementsPanel
._toggleEditAsHTML();