OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. | 2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. |
3 * Copyright (C) 2008 Matt Lilek <webkit@mattlilek.com> | 3 * Copyright (C) 2008 Matt Lilek <webkit@mattlilek.com> |
4 * Copyright (C) 2009 Joseph Pecoraro | 4 * Copyright (C) 2009 Joseph Pecoraro |
5 * | 5 * |
6 * Redistribution and use in source and binary forms, with or without | 6 * Redistribution and use in source and binary forms, with or without |
7 * modification, are permitted provided that the following conditions | 7 * modification, are permitted provided that the following conditions |
8 * are met: | 8 * are met: |
9 * | 9 * |
10 * 1. Redistributions of source code must retain the above copyright | 10 * 1. Redistributions of source code must retain the above copyright |
11 * notice, this list of conditions and the following disclaimer. | 11 * notice, this list of conditions and the following disclaimer. |
12 * 2. Redistributions in binary form must reproduce the above copyright | 12 * 2. Redistributions in binary form must reproduce the above copyright |
13 * notice, this list of conditions and the following disclaimer in the | 13 * notice, this list of conditions and the following disclaimer in the |
14 * documentation and/or other materials provided with the distribution. | 14 * documentation and/or other materials provided with the distribution. |
15 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of | 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 | 16 * its contributors may be used to endorse or promote products derived |
17 * from this software without specific prior written permission. | 17 * from this software without specific prior written permission. |
18 * | 18 * |
19 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY | 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 | 20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | 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 | 22 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | 23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | 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 | 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 | 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 | 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. | 28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
29 */ | 29 */ |
30 | |
31 /** | 30 /** |
32 * @constructor | |
33 * @implements {WebInspector.Searchable} | 31 * @implements {WebInspector.Searchable} |
34 * @implements {WebInspector.TargetManager.Observer} | 32 * @implements {WebInspector.TargetManager.Observer} |
35 * @implements {WebInspector.ViewLocationResolver} | 33 * @implements {WebInspector.ViewLocationResolver} |
36 * @extends {WebInspector.Panel} | 34 * @unrestricted |
37 */ | 35 */ |
38 WebInspector.ElementsPanel = function() | 36 WebInspector.ElementsPanel = class extends WebInspector.Panel { |
39 { | 37 constructor() { |
40 WebInspector.Panel.call(this, "elements"); | 38 super('elements'); |
41 this.registerRequiredCSS("elements/elementsPanel.css"); | 39 this.registerRequiredCSS('elements/elementsPanel.css'); |
42 | 40 |
43 this._splitWidget = new WebInspector.SplitWidget(true, true, "elementsPanelS
plitViewState", 325, 325); | 41 this._splitWidget = new WebInspector.SplitWidget(true, true, 'elementsPanelS
plitViewState', 325, 325); |
44 this._splitWidget.addEventListener(WebInspector.SplitWidget.Events.SidebarSi
zeChanged, this._updateTreeOutlineVisibleWidth.bind(this)); | 42 this._splitWidget.addEventListener( |
| 43 WebInspector.SplitWidget.Events.SidebarSizeChanged, this._updateTreeOutl
ineVisibleWidth.bind(this)); |
45 this._splitWidget.show(this.element); | 44 this._splitWidget.show(this.element); |
46 | 45 |
47 this._searchableView = new WebInspector.SearchableView(this); | 46 this._searchableView = new WebInspector.SearchableView(this); |
48 this._searchableView.setMinimumSize(25, 28); | 47 this._searchableView.setMinimumSize(25, 28); |
49 this._searchableView.setPlaceholder(WebInspector.UIString("Find by string, s
elector, or XPath")); | 48 this._searchableView.setPlaceholder(WebInspector.UIString('Find by string, s
elector, or XPath')); |
50 var stackElement = this._searchableView.element; | 49 var stackElement = this._searchableView.element; |
51 | 50 |
52 this._contentElement = createElement("div"); | 51 this._contentElement = createElement('div'); |
53 var crumbsContainer = createElement("div"); | 52 var crumbsContainer = createElement('div'); |
54 stackElement.appendChild(this._contentElement); | 53 stackElement.appendChild(this._contentElement); |
55 stackElement.appendChild(crumbsContainer); | 54 stackElement.appendChild(crumbsContainer); |
56 | 55 |
57 this._splitWidget.setMainWidget(this._searchableView); | 56 this._splitWidget.setMainWidget(this._searchableView); |
58 | 57 |
59 this._contentElement.id = "elements-content"; | 58 this._contentElement.id = 'elements-content'; |
60 // FIXME: crbug.com/425984 | 59 // FIXME: crbug.com/425984 |
61 if (WebInspector.moduleSetting("domWordWrap").get()) | 60 if (WebInspector.moduleSetting('domWordWrap').get()) |
62 this._contentElement.classList.add("elements-wrap"); | 61 this._contentElement.classList.add('elements-wrap'); |
63 WebInspector.moduleSetting("domWordWrap").addChangeListener(this._domWordWra
pSettingChanged.bind(this)); | 62 WebInspector.moduleSetting('domWordWrap').addChangeListener(this._domWordWra
pSettingChanged.bind(this)); |
64 | 63 |
65 crumbsContainer.id = "elements-crumbs"; | 64 crumbsContainer.id = 'elements-crumbs'; |
66 this._breadcrumbs = new WebInspector.ElementsBreadcrumbs(); | 65 this._breadcrumbs = new WebInspector.ElementsBreadcrumbs(); |
67 this._breadcrumbs.show(crumbsContainer); | 66 this._breadcrumbs.show(crumbsContainer); |
68 this._breadcrumbs.addEventListener(WebInspector.ElementsBreadcrumbs.Events.N
odeSelected, this._crumbNodeSelected, this); | 67 this._breadcrumbs.addEventListener( |
| 68 WebInspector.ElementsBreadcrumbs.Events.NodeSelected, this._crumbNodeSel
ected, this); |
69 | 69 |
70 this._currentToolbarPane = null; | 70 this._currentToolbarPane = null; |
71 | 71 |
72 this._stylesWidget = new WebInspector.StylesSidebarPane(); | 72 this._stylesWidget = new WebInspector.StylesSidebarPane(); |
73 this._computedStyleWidget = new WebInspector.ComputedStyleWidget(); | 73 this._computedStyleWidget = new WebInspector.ComputedStyleWidget(); |
74 this._metricsWidget = new WebInspector.MetricsSidebarPane(); | 74 this._metricsWidget = new WebInspector.MetricsSidebarPane(); |
75 | 75 |
76 this._stylesSidebarToolbar = this._createStylesSidebarToolbar(); | 76 this._stylesSidebarToolbar = this._createStylesSidebarToolbar(); |
77 | 77 |
78 WebInspector.moduleSetting("sidebarPosition").addChangeListener(this._update
SidebarPosition.bind(this)); | 78 WebInspector.moduleSetting('sidebarPosition').addChangeListener(this._update
SidebarPosition.bind(this)); |
79 this._updateSidebarPosition(); | 79 this._updateSidebarPosition(); |
80 | 80 |
81 /** @type {!Array.<!WebInspector.ElementsTreeOutline>} */ | 81 /** @type {!Array.<!WebInspector.ElementsTreeOutline>} */ |
82 this._treeOutlines = []; | 82 this._treeOutlines = []; |
83 WebInspector.targetManager.observeTargets(this); | 83 WebInspector.targetManager.observeTargets(this); |
84 WebInspector.moduleSetting("showUAShadowDOM").addChangeListener(this._showUA
ShadowDOMChanged.bind(this)); | 84 WebInspector.moduleSetting('showUAShadowDOM').addChangeListener(this._showUA
ShadowDOMChanged.bind(this)); |
85 WebInspector.targetManager.addModelListener(WebInspector.DOMModel, WebInspec
tor.DOMModel.Events.DocumentUpdated, this._documentUpdatedEvent, this); | 85 WebInspector.targetManager.addModelListener( |
86 WebInspector.extensionServer.addEventListener(WebInspector.ExtensionServer.E
vents.SidebarPaneAdded, this._extensionSidebarPaneAdded, this); | 86 WebInspector.DOMModel, WebInspector.DOMModel.Events.DocumentUpdated, thi
s._documentUpdatedEvent, this); |
87 }; | 87 WebInspector.extensionServer.addEventListener( |
88 | 88 WebInspector.ExtensionServer.Events.SidebarPaneAdded, this._extensionSid
ebarPaneAdded, this); |
89 WebInspector.ElementsPanel._elementsSidebarViewTitleSymbol = Symbol("title"); | 89 } |
90 | 90 |
91 WebInspector.ElementsPanel.prototype = { | 91 /** |
| 92 * @return {!WebInspector.ElementsPanel} |
| 93 */ |
| 94 static instance() { |
| 95 return /** @type {!WebInspector.ElementsPanel} */ (self.runtime.sharedInstan
ce(WebInspector.ElementsPanel)); |
| 96 } |
| 97 |
| 98 /** |
| 99 * @param {!WebInspector.CSSProperty} cssProperty |
| 100 */ |
| 101 _revealProperty(cssProperty) { |
| 102 return this.sidebarPaneView.showView(this._stylesViewToReveal).then(() => { |
| 103 this._stylesWidget.revealProperty(/** @type {!WebInspector.CSSProperty} */
(cssProperty)); |
| 104 }); |
| 105 } |
| 106 |
| 107 /** |
| 108 * @return {!Element} |
| 109 */ |
| 110 _createStylesSidebarToolbar() { |
| 111 var container = createElementWithClass('div', 'styles-sidebar-pane-toolbar-c
ontainer'); |
| 112 var hbox = container.createChild('div', 'hbox styles-sidebar-pane-toolbar'); |
| 113 var filterContainerElement = hbox.createChild('div', 'styles-sidebar-pane-fi
lter-box'); |
| 114 var filterInput = WebInspector.StylesSidebarPane.createPropertyFilterElement
( |
| 115 WebInspector.UIString('Filter'), hbox, this._stylesWidget.onFilterChange
d.bind(this._stylesWidget)); |
| 116 filterContainerElement.appendChild(filterInput); |
| 117 var toolbar = new WebInspector.Toolbar('styles-pane-toolbar', hbox); |
| 118 toolbar.makeToggledGray(); |
| 119 toolbar.appendLocationItems('styles-sidebarpane-toolbar'); |
| 120 var toolbarPaneContainer = container.createChild('div', 'styles-sidebar-tool
bar-pane-container'); |
| 121 this._toolbarPaneElement = createElementWithClass('div', 'styles-sidebar-too
lbar-pane'); |
| 122 toolbarPaneContainer.appendChild(this._toolbarPaneElement); |
| 123 return container; |
| 124 } |
| 125 |
| 126 /** |
| 127 * @override |
| 128 * @param {string} locationName |
| 129 * @return {?WebInspector.ViewLocation} |
| 130 */ |
| 131 resolveLocation(locationName) { |
| 132 return this.sidebarPaneView; |
| 133 } |
| 134 |
| 135 /** |
| 136 * @param {?WebInspector.Widget} widget |
| 137 * @param {!WebInspector.ToolbarToggle=} toggle |
| 138 */ |
| 139 showToolbarPane(widget, toggle) { |
| 140 if (this._pendingWidgetToggle) |
| 141 this._pendingWidgetToggle.setToggled(false); |
| 142 this._pendingWidgetToggle = toggle; |
| 143 |
| 144 if (this._animatedToolbarPane !== undefined) |
| 145 this._pendingWidget = widget; |
| 146 else |
| 147 this._startToolbarPaneAnimation(widget); |
| 148 |
| 149 if (widget && toggle) |
| 150 toggle.setToggled(true); |
| 151 } |
| 152 |
| 153 /** |
| 154 * @param {?WebInspector.Widget} widget |
| 155 */ |
| 156 _startToolbarPaneAnimation(widget) { |
| 157 if (widget === this._currentToolbarPane) |
| 158 return; |
| 159 |
| 160 if (widget && this._currentToolbarPane) { |
| 161 this._currentToolbarPane.detach(); |
| 162 widget.show(this._toolbarPaneElement); |
| 163 this._currentToolbarPane = widget; |
| 164 this._currentToolbarPane.focus(); |
| 165 return; |
| 166 } |
| 167 |
| 168 this._animatedToolbarPane = widget; |
| 169 |
| 170 if (this._currentToolbarPane) |
| 171 this._toolbarPaneElement.style.animationName = 'styles-element-state-pane-
slideout'; |
| 172 else if (widget) |
| 173 this._toolbarPaneElement.style.animationName = 'styles-element-state-pane-
slidein'; |
| 174 |
| 175 if (widget) |
| 176 widget.show(this._toolbarPaneElement); |
| 177 |
| 178 var listener = onAnimationEnd.bind(this); |
| 179 this._toolbarPaneElement.addEventListener('animationend', listener, false); |
| 180 |
92 /** | 181 /** |
93 * @param {!WebInspector.CSSProperty} cssProperty | 182 * @this {WebInspector.ElementsPanel} |
94 */ | 183 */ |
95 _revealProperty: function(cssProperty) | 184 function onAnimationEnd() { |
96 { | 185 this._toolbarPaneElement.style.removeProperty('animation-name'); |
97 return this.sidebarPaneView.showView(this._stylesViewToReveal).then(() =
> { | 186 this._toolbarPaneElement.removeEventListener('animationend', listener, fal
se); |
98 this._stylesWidget.revealProperty(/** @type {!WebInspector.CSSProper
ty} */(cssProperty)); | 187 |
99 }); | 188 if (this._currentToolbarPane) |
100 }, | 189 this._currentToolbarPane.detach(); |
| 190 |
| 191 this._currentToolbarPane = this._animatedToolbarPane; |
| 192 if (this._currentToolbarPane) |
| 193 this._currentToolbarPane.focus(); |
| 194 delete this._animatedToolbarPane; |
| 195 |
| 196 if (this._pendingWidget !== undefined) { |
| 197 this._startToolbarPaneAnimation(this._pendingWidget); |
| 198 delete this._pendingWidget; |
| 199 } |
| 200 } |
| 201 } |
| 202 |
| 203 /** |
| 204 * @override |
| 205 * @param {!WebInspector.Target} target |
| 206 */ |
| 207 targetAdded(target) { |
| 208 var domModel = WebInspector.DOMModel.fromTarget(target); |
| 209 if (!domModel) |
| 210 return; |
| 211 var treeOutline = new WebInspector.ElementsTreeOutline(domModel, true, true)
; |
| 212 treeOutline.setWordWrap(WebInspector.moduleSetting('domWordWrap').get()); |
| 213 treeOutline.wireToDOMModel(); |
| 214 treeOutline.addEventListener( |
| 215 WebInspector.ElementsTreeOutline.Events.SelectedNodeChanged, this._selec
tedNodeChanged, this); |
| 216 treeOutline.addEventListener( |
| 217 WebInspector.ElementsTreeOutline.Events.ElementsTreeUpdated, this._updat
eBreadcrumbIfNeeded, this); |
| 218 new WebInspector.ElementsTreeElementHighlighter(treeOutline); |
| 219 this._treeOutlines.push(treeOutline); |
| 220 |
| 221 // Perform attach if necessary. |
| 222 if (this.isShowing()) |
| 223 this.wasShown(); |
| 224 } |
| 225 |
| 226 /** |
| 227 * @override |
| 228 * @param {!WebInspector.Target} target |
| 229 */ |
| 230 targetRemoved(target) { |
| 231 var domModel = WebInspector.DOMModel.fromTarget(target); |
| 232 if (!domModel) |
| 233 return; |
| 234 var treeOutline = WebInspector.ElementsTreeOutline.forDOMModel(domModel); |
| 235 treeOutline.unwireFromDOMModel(); |
| 236 this._treeOutlines.remove(treeOutline); |
| 237 treeOutline.element.remove(); |
| 238 } |
| 239 |
| 240 _updateTreeOutlineVisibleWidth() { |
| 241 if (!this._treeOutlines.length) |
| 242 return; |
| 243 |
| 244 var width = this._splitWidget.element.offsetWidth; |
| 245 if (this._splitWidget.isVertical()) |
| 246 width -= this._splitWidget.sidebarSize(); |
| 247 for (var i = 0; i < this._treeOutlines.length; ++i) { |
| 248 this._treeOutlines[i].setVisibleWidth(width); |
| 249 } |
| 250 this._breadcrumbs.updateSizes(); |
| 251 } |
| 252 |
| 253 /** |
| 254 * @override |
| 255 */ |
| 256 focus() { |
| 257 if (this._treeOutlines.length) |
| 258 this._treeOutlines[0].focus(); |
| 259 } |
| 260 |
| 261 /** |
| 262 * @override |
| 263 * @return {!WebInspector.SearchableView} |
| 264 */ |
| 265 searchableView() { |
| 266 return this._searchableView; |
| 267 } |
| 268 |
| 269 /** |
| 270 * @override |
| 271 */ |
| 272 wasShown() { |
| 273 WebInspector.context.setFlavor(WebInspector.ElementsPanel, this); |
| 274 |
| 275 for (var i = 0; i < this._treeOutlines.length; ++i) { |
| 276 var treeOutline = this._treeOutlines[i]; |
| 277 // Attach heavy component lazily |
| 278 if (treeOutline.element.parentElement !== this._contentElement) |
| 279 this._contentElement.appendChild(treeOutline.element); |
| 280 } |
| 281 super.wasShown(); |
| 282 this._breadcrumbs.update(); |
| 283 |
| 284 for (var i = 0; i < this._treeOutlines.length; ++i) { |
| 285 var treeOutline = this._treeOutlines[i]; |
| 286 treeOutline.setVisible(true); |
| 287 |
| 288 if (!treeOutline.rootDOMNode) |
| 289 if (treeOutline.domModel().existingDocument()) |
| 290 this._documentUpdated(treeOutline.domModel(), treeOutline.domModel().e
xistingDocument()); |
| 291 else |
| 292 treeOutline.domModel().requestDocument(); |
| 293 } |
| 294 this.focus(); |
| 295 } |
| 296 |
| 297 /** |
| 298 * @override |
| 299 */ |
| 300 willHide() { |
| 301 WebInspector.context.setFlavor(WebInspector.ElementsPanel, null); |
| 302 |
| 303 WebInspector.DOMModel.hideDOMNodeHighlight(); |
| 304 for (var i = 0; i < this._treeOutlines.length; ++i) { |
| 305 var treeOutline = this._treeOutlines[i]; |
| 306 treeOutline.setVisible(false); |
| 307 // Detach heavy component on hide |
| 308 this._contentElement.removeChild(treeOutline.element); |
| 309 } |
| 310 if (this._popoverHelper) |
| 311 this._popoverHelper.hidePopover(); |
| 312 super.willHide(); |
| 313 } |
| 314 |
| 315 /** |
| 316 * @override |
| 317 */ |
| 318 onResize() { |
| 319 if (WebInspector.moduleSetting('sidebarPosition').get() === 'auto') |
| 320 this.element.window().requestAnimationFrame(this._updateSidebarPosition.bi
nd(this)); // Do not force layout. |
| 321 this._updateTreeOutlineVisibleWidth(); |
| 322 } |
| 323 |
| 324 /** |
| 325 * @param {!WebInspector.Event} event |
| 326 */ |
| 327 _selectedNodeChanged(event) { |
| 328 var selectedNode = /** @type {?WebInspector.DOMNode} */ (event.data.node); |
| 329 var focus = /** @type {boolean} */ (event.data.focus); |
| 330 for (var i = 0; i < this._treeOutlines.length; ++i) { |
| 331 if (!selectedNode || selectedNode.domModel() !== this._treeOutlines[i].dom
Model()) |
| 332 this._treeOutlines[i].selectDOMNode(null); |
| 333 } |
| 334 |
| 335 this._breadcrumbs.setSelectedNode(selectedNode); |
| 336 |
| 337 WebInspector.context.setFlavor(WebInspector.DOMNode, selectedNode); |
| 338 |
| 339 if (!selectedNode) |
| 340 return; |
| 341 selectedNode.setAsInspectedNode(); |
| 342 if (focus) { |
| 343 this._selectedNodeOnReset = selectedNode; |
| 344 this._hasNonDefaultSelectedNode = true; |
| 345 } |
| 346 |
| 347 var executionContexts = selectedNode.target().runtimeModel.executionContexts
(); |
| 348 var nodeFrameId = selectedNode.frameId(); |
| 349 for (var context of executionContexts) { |
| 350 if (context.frameId === nodeFrameId) { |
| 351 WebInspector.context.setFlavor(WebInspector.ExecutionContext, context); |
| 352 break; |
| 353 } |
| 354 } |
| 355 } |
| 356 |
| 357 _reset() { |
| 358 delete this.currentQuery; |
| 359 } |
| 360 |
| 361 /** |
| 362 * @param {!WebInspector.Event} event |
| 363 */ |
| 364 _documentUpdatedEvent(event) { |
| 365 this._documentUpdated( |
| 366 /** @type {!WebInspector.DOMModel} */ (event.target), /** @type {?WebIns
pector.DOMDocument} */ (event.data)); |
| 367 } |
| 368 |
| 369 /** |
| 370 * @param {!WebInspector.DOMModel} domModel |
| 371 * @param {?WebInspector.DOMDocument} inspectedRootDocument |
| 372 */ |
| 373 _documentUpdated(domModel, inspectedRootDocument) { |
| 374 this._reset(); |
| 375 this.searchCanceled(); |
| 376 |
| 377 var treeOutline = WebInspector.ElementsTreeOutline.forDOMModel(domModel); |
| 378 treeOutline.rootDOMNode = inspectedRootDocument; |
| 379 |
| 380 if (!inspectedRootDocument) { |
| 381 if (this.isShowing()) |
| 382 domModel.requestDocument(); |
| 383 return; |
| 384 } |
| 385 |
| 386 this._hasNonDefaultSelectedNode = false; |
| 387 WebInspector.domBreakpointsSidebarPane.restoreBreakpoints(inspectedRootDocum
ent); |
| 388 |
| 389 if (this._omitDefaultSelection) |
| 390 return; |
| 391 |
| 392 var savedSelectedNodeOnReset = this._selectedNodeOnReset; |
| 393 restoreNode.call(this, domModel, this._selectedNodeOnReset); |
101 | 394 |
102 /** | 395 /** |
103 * @return {!Element} | 396 * @param {!WebInspector.DOMModel} domModel |
| 397 * @param {?WebInspector.DOMNode} staleNode |
| 398 * @this {WebInspector.ElementsPanel} |
104 */ | 399 */ |
105 _createStylesSidebarToolbar: function() | 400 function restoreNode(domModel, staleNode) { |
106 { | 401 var nodePath = staleNode ? staleNode.path() : null; |
107 var container = createElementWithClass("div", "styles-sidebar-pane-toolb
ar-container"); | 402 if (!nodePath) { |
108 var hbox = container.createChild("div", "hbox styles-sidebar-pane-toolba
r"); | 403 onNodeRestored.call(this, null); |
109 var filterContainerElement = hbox.createChild("div", "styles-sidebar-pan
e-filter-box"); | 404 return; |
110 var filterInput = WebInspector.StylesSidebarPane.createPropertyFilterEle
ment(WebInspector.UIString("Filter"), hbox, this._stylesWidget.onFilterChanged.b
ind(this._stylesWidget)); | 405 } |
111 filterContainerElement.appendChild(filterInput); | 406 domModel.pushNodeByPathToFrontend(nodePath, onNodeRestored.bind(this)); |
112 var toolbar = new WebInspector.Toolbar("styles-pane-toolbar", hbox); | 407 } |
113 toolbar.makeToggledGray(); | |
114 toolbar.appendLocationItems("styles-sidebarpane-toolbar"); | |
115 var toolbarPaneContainer = container.createChild("div", "styles-sidebar-
toolbar-pane-container"); | |
116 this._toolbarPaneElement = createElementWithClass("div", "styles-sidebar
-toolbar-pane"); | |
117 toolbarPaneContainer.appendChild(this._toolbarPaneElement); | |
118 return container; | |
119 }, | |
120 | 408 |
121 /** | 409 /** |
122 * @override | 410 * @param {?DOMAgent.NodeId} restoredNodeId |
123 * @param {string} locationName | 411 * @this {WebInspector.ElementsPanel} |
124 * @return {?WebInspector.ViewLocation} | |
125 */ | 412 */ |
126 resolveLocation: function(locationName) | 413 function onNodeRestored(restoredNodeId) { |
127 { | 414 if (savedSelectedNodeOnReset !== this._selectedNodeOnReset) |
128 return this.sidebarPaneView; | 415 return; |
129 }, | 416 var node = restoredNodeId ? domModel.nodeForId(restoredNodeId) : null; |
| 417 if (!node) { |
| 418 var inspectedDocument = domModel.existingDocument(); |
| 419 node = inspectedDocument ? inspectedDocument.body || inspectedDocument.d
ocumentElement : null; |
| 420 } |
| 421 this._setDefaultSelectedNode(node); |
| 422 this._lastSelectedNodeSelectedForTest(); |
| 423 } |
| 424 } |
| 425 |
| 426 _lastSelectedNodeSelectedForTest() { |
| 427 } |
| 428 |
| 429 /** |
| 430 * @param {?WebInspector.DOMNode} node |
| 431 */ |
| 432 _setDefaultSelectedNode(node) { |
| 433 if (!node || this._hasNonDefaultSelectedNode || this._pendingNodeReveal) |
| 434 return; |
| 435 var treeOutline = WebInspector.ElementsTreeOutline.forDOMModel(node.domModel
()); |
| 436 if (!treeOutline) |
| 437 return; |
| 438 this.selectDOMNode(node); |
| 439 if (treeOutline.selectedTreeElement) |
| 440 treeOutline.selectedTreeElement.expand(); |
| 441 } |
| 442 |
| 443 /** |
| 444 * @override |
| 445 */ |
| 446 searchCanceled() { |
| 447 delete this._searchQuery; |
| 448 this._hideSearchHighlights(); |
| 449 |
| 450 this._searchableView.updateSearchMatchesCount(0); |
| 451 |
| 452 delete this._currentSearchResultIndex; |
| 453 delete this._searchResults; |
| 454 |
| 455 WebInspector.DOMModel.cancelSearch(); |
| 456 } |
| 457 |
| 458 /** |
| 459 * @override |
| 460 * @param {!WebInspector.SearchableView.SearchConfig} searchConfig |
| 461 * @param {boolean} shouldJump |
| 462 * @param {boolean=} jumpBackwards |
| 463 */ |
| 464 performSearch(searchConfig, shouldJump, jumpBackwards) { |
| 465 var query = searchConfig.query; |
| 466 // Call searchCanceled since it will reset everything we need before doing a
new search. |
| 467 this.searchCanceled(); |
| 468 |
| 469 const whitespaceTrimmedQuery = query.trim(); |
| 470 if (!whitespaceTrimmedQuery.length) |
| 471 return; |
| 472 |
| 473 this._searchQuery = query; |
| 474 |
| 475 var promises = []; |
| 476 var domModels = WebInspector.DOMModel.instances(); |
| 477 for (var domModel of domModels) |
| 478 promises.push( |
| 479 domModel.performSearchPromise(whitespaceTrimmedQuery, WebInspector.mod
uleSetting('showUAShadowDOM').get())); |
| 480 Promise.all(promises).then(resultCountCallback.bind(this)); |
130 | 481 |
131 /** | 482 /** |
132 * @param {?WebInspector.Widget} widget | 483 * @param {!Array.<number>} resultCounts |
133 * @param {!WebInspector.ToolbarToggle=} toggle | 484 * @this {WebInspector.ElementsPanel} |
134 */ | 485 */ |
135 showToolbarPane: function(widget, toggle) | 486 function resultCountCallback(resultCounts) { |
136 { | 487 /** |
137 if (this._pendingWidgetToggle) | 488 * @type {!Array.<{domModel: !WebInspector.DOMModel, index: number, node:
(?WebInspector.DOMNode|undefined)}>} |
138 this._pendingWidgetToggle.setToggled(false); | 489 */ |
139 this._pendingWidgetToggle = toggle; | 490 this._searchResults = []; |
140 | 491 for (var i = 0; i < resultCounts.length; ++i) { |
141 if (this._animatedToolbarPane !== undefined) | 492 var resultCount = resultCounts[i]; |
142 this._pendingWidget = widget; | 493 for (var j = 0; j < resultCount; ++j) |
143 else | 494 this._searchResults.push({domModel: domModels[i], index: j, node: unde
fined}); |
144 this._startToolbarPaneAnimation(widget); | 495 } |
145 | 496 this._searchableView.updateSearchMatchesCount(this._searchResults.length); |
146 if (widget && toggle) | 497 if (!this._searchResults.length) |
147 toggle.setToggled(true); | 498 return; |
148 }, | 499 this._currentSearchResultIndex = -1; |
| 500 |
| 501 if (shouldJump) |
| 502 this._jumpToSearchResult(jumpBackwards ? -1 : 0); |
| 503 } |
| 504 } |
| 505 |
| 506 _domWordWrapSettingChanged(event) { |
| 507 // FIXME: crbug.com/425984 |
| 508 this._contentElement.classList.toggle('elements-wrap', event.data); |
| 509 for (var i = 0; i < this._treeOutlines.length; ++i) |
| 510 this._treeOutlines[i].setWordWrap(/** @type {boolean} */ (event.data)); |
| 511 } |
| 512 |
| 513 switchToAndFocus(node) { |
| 514 // Reset search restore. |
| 515 this._searchableView.cancelSearch(); |
| 516 WebInspector.viewManager.showView('elements').then(() => this.selectDOMNode(
node, true)); |
| 517 } |
| 518 |
| 519 /** |
| 520 * @param {!Element} element |
| 521 * @param {!Event} event |
| 522 * @return {!Element|!AnchorBox|undefined} |
| 523 */ |
| 524 _getPopoverAnchor(element, event) { |
| 525 var anchor = element.enclosingNodeOrSelfWithClass('webkit-html-resource-link
'); |
| 526 if (!anchor || !anchor.href) |
| 527 return; |
| 528 |
| 529 return anchor; |
| 530 } |
| 531 |
| 532 /** |
| 533 * @param {!Element} anchor |
| 534 * @param {!WebInspector.Popover} popover |
| 535 */ |
| 536 _showPopover(anchor, popover) { |
| 537 var node = this.selectedDOMNode(); |
| 538 if (node) |
| 539 WebInspector.DOMPresentationUtils.buildImagePreviewContents(node.target(),
anchor.href, true, showPopover); |
149 | 540 |
150 /** | 541 /** |
151 * @param {?WebInspector.Widget} widget | 542 * @param {!Element=} contents |
152 */ | 543 */ |
153 _startToolbarPaneAnimation: function(widget) | 544 function showPopover(contents) { |
154 { | 545 if (!contents) |
155 if (widget === this._currentToolbarPane) | 546 return; |
156 return; | 547 popover.setCanShrink(false); |
157 | 548 popover.showForAnchor(contents, anchor); |
158 if (widget && this._currentToolbarPane) { | 549 } |
159 this._currentToolbarPane.detach(); | 550 } |
160 widget.show(this._toolbarPaneElement); | 551 |
161 this._currentToolbarPane = widget; | 552 _jumpToSearchResult(index) { |
162 this._currentToolbarPane.focus(); | 553 this._hideSearchHighlights(); |
163 return; | 554 this._currentSearchResultIndex = (index + this._searchResults.length) % this
._searchResults.length; |
164 } | 555 this._highlightCurrentSearchResult(); |
165 | 556 } |
166 this._animatedToolbarPane = widget; | 557 |
167 | 558 /** |
168 if (this._currentToolbarPane) | 559 * @override |
169 this._toolbarPaneElement.style.animationName = "styles-element-state
-pane-slideout"; | 560 */ |
170 else if (widget) | 561 jumpToNextSearchResult() { |
171 this._toolbarPaneElement.style.animationName = "styles-element-state
-pane-slidein"; | 562 if (!this._searchResults) |
172 | 563 return; |
173 if (widget) | 564 this._jumpToSearchResult(this._currentSearchResultIndex + 1); |
174 widget.show(this._toolbarPaneElement); | 565 } |
175 | 566 |
176 var listener = onAnimationEnd.bind(this); | 567 /** |
177 this._toolbarPaneElement.addEventListener("animationend", listener, fals
e); | 568 * @override |
178 | 569 */ |
179 /** | 570 jumpToPreviousSearchResult() { |
180 * @this {WebInspector.ElementsPanel} | 571 if (!this._searchResults) |
181 */ | 572 return; |
182 function onAnimationEnd() | 573 this._jumpToSearchResult(this._currentSearchResultIndex - 1); |
183 { | 574 } |
184 this._toolbarPaneElement.style.removeProperty("animation-name"); | 575 |
185 this._toolbarPaneElement.removeEventListener("animationend", listene
r, false); | 576 /** |
186 | 577 * @override |
187 if (this._currentToolbarPane) | 578 * @return {boolean} |
188 this._currentToolbarPane.detach(); | 579 */ |
189 | 580 supportsCaseSensitiveSearch() { |
190 this._currentToolbarPane = this._animatedToolbarPane; | 581 return false; |
191 if (this._currentToolbarPane) | 582 } |
192 this._currentToolbarPane.focus(); | 583 |
193 delete this._animatedToolbarPane; | 584 /** |
194 | 585 * @override |
195 if (this._pendingWidget !== undefined) { | 586 * @return {boolean} |
196 this._startToolbarPaneAnimation(this._pendingWidget); | 587 */ |
197 delete this._pendingWidget; | 588 supportsRegexSearch() { |
198 } | 589 return false; |
199 } | 590 } |
200 }, | 591 |
| 592 _highlightCurrentSearchResult() { |
| 593 var index = this._currentSearchResultIndex; |
| 594 var searchResults = this._searchResults; |
| 595 var searchResult = searchResults[index]; |
| 596 |
| 597 if (searchResult.node === null) { |
| 598 this._searchableView.updateCurrentMatchIndex(index); |
| 599 return; |
| 600 } |
201 | 601 |
202 /** | 602 /** |
203 * @override | 603 * @param {?WebInspector.DOMNode} node |
204 * @param {!WebInspector.Target} target | 604 * @this {WebInspector.ElementsPanel} |
205 */ | 605 */ |
206 targetAdded: function(target) | 606 function searchCallback(node) { |
207 { | 607 searchResult.node = node; |
208 var domModel = WebInspector.DOMModel.fromTarget(target); | 608 this._highlightCurrentSearchResult(); |
209 if (!domModel) | 609 } |
210 return; | 610 |
211 var treeOutline = new WebInspector.ElementsTreeOutline(domModel, true, t
rue); | 611 if (typeof searchResult.node === 'undefined') { |
212 treeOutline.setWordWrap(WebInspector.moduleSetting("domWordWrap").get())
; | 612 // No data for slot, request it. |
213 treeOutline.wireToDOMModel(); | 613 searchResult.domModel.searchResult(searchResult.index, searchCallback.bind
(this)); |
214 treeOutline.addEventListener(WebInspector.ElementsTreeOutline.Events.Sel
ectedNodeChanged, this._selectedNodeChanged, this); | 614 return; |
215 treeOutline.addEventListener(WebInspector.ElementsTreeOutline.Events.Ele
mentsTreeUpdated, this._updateBreadcrumbIfNeeded, this); | 615 } |
216 new WebInspector.ElementsTreeElementHighlighter(treeOutline); | 616 |
217 this._treeOutlines.push(treeOutline); | 617 this._searchableView.updateCurrentMatchIndex(index); |
218 | 618 |
219 // Perform attach if necessary. | 619 var treeElement = this._treeElementForNode(searchResult.node); |
220 if (this.isShowing()) | 620 if (treeElement) { |
221 this.wasShown(); | 621 treeElement.highlightSearchResults(this._searchQuery); |
222 | 622 treeElement.reveal(); |
223 }, | 623 var matches = treeElement.listItemElement.getElementsByClassName(WebInspec
tor.highlightedSearchResultClassName); |
224 | 624 if (matches.length) |
| 625 matches[0].scrollIntoViewIfNeeded(false); |
| 626 } |
| 627 } |
| 628 |
| 629 _hideSearchHighlights() { |
| 630 if (!this._searchResults || !this._searchResults.length || this._currentSear
chResultIndex < 0) |
| 631 return; |
| 632 var searchResult = this._searchResults[this._currentSearchResultIndex]; |
| 633 if (!searchResult.node) |
| 634 return; |
| 635 var treeOutline = WebInspector.ElementsTreeOutline.forDOMModel(searchResult.
node.domModel()); |
| 636 var treeElement = treeOutline.findTreeElement(searchResult.node); |
| 637 if (treeElement) |
| 638 treeElement.hideSearchHighlights(); |
| 639 } |
| 640 |
| 641 /** |
| 642 * @return {?WebInspector.DOMNode} |
| 643 */ |
| 644 selectedDOMNode() { |
| 645 for (var i = 0; i < this._treeOutlines.length; ++i) { |
| 646 var treeOutline = this._treeOutlines[i]; |
| 647 if (treeOutline.selectedDOMNode()) |
| 648 return treeOutline.selectedDOMNode(); |
| 649 } |
| 650 return null; |
| 651 } |
| 652 |
| 653 /** |
| 654 * @param {!WebInspector.DOMNode} node |
| 655 * @param {boolean=} focus |
| 656 */ |
| 657 selectDOMNode(node, focus) { |
| 658 for (var i = 0; i < this._treeOutlines.length; ++i) { |
| 659 var treeOutline = this._treeOutlines[i]; |
| 660 if (treeOutline.domModel() === node.domModel()) |
| 661 treeOutline.selectDOMNode(node, focus); |
| 662 else |
| 663 treeOutline.selectDOMNode(null); |
| 664 } |
| 665 } |
| 666 |
| 667 /** |
| 668 * @param {!WebInspector.Event} event |
| 669 */ |
| 670 _updateBreadcrumbIfNeeded(event) { |
| 671 var nodes = /** @type {!Array.<!WebInspector.DOMNode>} */ (event.data); |
| 672 this._breadcrumbs.updateNodes(nodes); |
| 673 } |
| 674 |
| 675 /** |
| 676 * @param {!WebInspector.Event} event |
| 677 */ |
| 678 _crumbNodeSelected(event) { |
| 679 var node = /** @type {!WebInspector.DOMNode} */ (event.data); |
| 680 this.selectDOMNode(node, true); |
| 681 } |
| 682 |
| 683 /** |
| 684 * @override |
| 685 * @param {!KeyboardEvent} event |
| 686 */ |
| 687 handleShortcut(event) { |
225 /** | 688 /** |
226 * @override | 689 * @param {!WebInspector.ElementsTreeOutline} treeOutline |
227 * @param {!WebInspector.Target} target | |
228 */ | 690 */ |
229 targetRemoved: function(target) | 691 function handleUndoRedo(treeOutline) { |
230 { | 692 if (WebInspector.KeyboardShortcut.eventHasCtrlOrMeta(event) && !event.shif
tKey && |
231 var domModel = WebInspector.DOMModel.fromTarget(target); | 693 (event.key === 'Z' || event.key === 'z')) { // Z key |
232 if (!domModel) | 694 treeOutline.domModel().undo(); |
233 return; | 695 event.handled = true; |
234 var treeOutline = WebInspector.ElementsTreeOutline.forDOMModel(domModel)
; | 696 return; |
235 treeOutline.unwireFromDOMModel(); | 697 } |
236 this._treeOutlines.remove(treeOutline); | 698 |
237 treeOutline.element.remove(); | 699 var isRedoKey = WebInspector.isMac() ? |
238 }, | 700 event.metaKey && event.shiftKey && (event.key === 'Z' || event.key ===
'z') : // Z key |
239 | 701 event.ctrlKey && (event.key === 'Y' || event.key === 'y');
// Y key |
240 _updateTreeOutlineVisibleWidth: function() | 702 if (isRedoKey) { |
241 { | 703 treeOutline.domModel().redo(); |
242 if (!this._treeOutlines.length) | 704 event.handled = true; |
243 return; | 705 } |
244 | 706 } |
245 var width = this._splitWidget.element.offsetWidth; | 707 |
246 if (this._splitWidget.isVertical()) | 708 if (WebInspector.isEditing() && event.keyCode !== WebInspector.KeyboardShort
cut.Keys.F2.code) |
247 width -= this._splitWidget.sidebarSize(); | 709 return; |
248 for (var i = 0; i < this._treeOutlines.length; ++i) { | 710 |
249 this._treeOutlines[i].setVisibleWidth(width); | 711 var treeOutline = null; |
250 } | 712 for (var i = 0; i < this._treeOutlines.length; ++i) { |
251 this._breadcrumbs.updateSizes(); | 713 if (this._treeOutlines[i].selectedDOMNode()) |
252 }, | 714 treeOutline = this._treeOutlines[i]; |
| 715 } |
| 716 if (!treeOutline) |
| 717 return; |
| 718 |
| 719 if (!treeOutline.editing()) { |
| 720 handleUndoRedo.call(null, treeOutline); |
| 721 if (event.handled) { |
| 722 this._stylesWidget.forceUpdate(); |
| 723 return; |
| 724 } |
| 725 } |
| 726 |
| 727 treeOutline.handleShortcut(event); |
| 728 if (event.handled) |
| 729 return; |
| 730 |
| 731 super.handleShortcut(event); |
| 732 } |
| 733 |
| 734 /** |
| 735 * @param {?WebInspector.DOMNode} node |
| 736 * @return {?WebInspector.ElementsTreeOutline} |
| 737 */ |
| 738 _treeOutlineForNode(node) { |
| 739 if (!node) |
| 740 return null; |
| 741 return WebInspector.ElementsTreeOutline.forDOMModel(node.domModel()); |
| 742 } |
| 743 |
| 744 /** |
| 745 * @param {!WebInspector.DOMNode} node |
| 746 * @return {?WebInspector.ElementsTreeElement} |
| 747 */ |
| 748 _treeElementForNode(node) { |
| 749 var treeOutline = this._treeOutlineForNode(node); |
| 750 return /** @type {?WebInspector.ElementsTreeElement} */ (treeOutline.findTre
eElement(node)); |
| 751 } |
| 752 |
| 753 /** |
| 754 * @param {!WebInspector.DOMNode} node |
| 755 * @return {!WebInspector.DOMNode} |
| 756 */ |
| 757 _leaveUserAgentShadowDOM(node) { |
| 758 var userAgentShadowRoot; |
| 759 while ((userAgentShadowRoot = node.ancestorUserAgentShadowRoot()) && userAge
ntShadowRoot.parentNode) |
| 760 node = userAgentShadowRoot.parentNode; |
| 761 return node; |
| 762 } |
| 763 |
| 764 /** |
| 765 * @param {!WebInspector.DOMNode} node |
| 766 * @return {!Promise} |
| 767 */ |
| 768 revealAndSelectNode(node) { |
| 769 if (WebInspector.inspectElementModeController && WebInspector.inspectElement
ModeController.isInInspectElementMode()) |
| 770 WebInspector.inspectElementModeController.stopInspection(); |
| 771 |
| 772 this._omitDefaultSelection = true; |
| 773 |
| 774 node = WebInspector.moduleSetting('showUAShadowDOM').get() ? node : this._le
aveUserAgentShadowDOM(node); |
| 775 node.highlightForTwoSeconds(); |
| 776 |
| 777 return WebInspector.viewManager.showView('elements').then(() => { |
| 778 this.selectDOMNode(node, true); |
| 779 delete this._omitDefaultSelection; |
| 780 |
| 781 if (!this._notFirstInspectElement) |
| 782 InspectorFrontendHost.inspectElementCompleted(); |
| 783 this._notFirstInspectElement = true; |
| 784 }); |
| 785 } |
| 786 |
| 787 _sidebarContextMenuEventFired(event) { |
| 788 var contextMenu = new WebInspector.ContextMenu(event); |
| 789 contextMenu.appendApplicableItems(/** @type {!Object} */ (event.deepElementF
romPoint())); |
| 790 contextMenu.show(); |
| 791 } |
| 792 |
| 793 _showUAShadowDOMChanged() { |
| 794 for (var i = 0; i < this._treeOutlines.length; ++i) |
| 795 this._treeOutlines[i].update(); |
| 796 } |
| 797 |
| 798 _updateSidebarPosition() { |
| 799 var horizontally; |
| 800 var position = WebInspector.moduleSetting('sidebarPosition').get(); |
| 801 if (position === 'right') |
| 802 horizontally = false; |
| 803 else if (position === 'bottom') |
| 804 horizontally = true; |
| 805 else |
| 806 horizontally = WebInspector.inspectorView.element.offsetWidth < 680; |
| 807 |
| 808 if (this.sidebarPaneView && horizontally === !this._splitWidget.isVertical()
) |
| 809 return; |
| 810 |
| 811 if (this.sidebarPaneView && this.sidebarPaneView.tabbedPane().shouldHideOnDe
tach()) |
| 812 return; // We can't reparent extension iframes. |
| 813 |
| 814 var extensionSidebarPanes = WebInspector.extensionServer.sidebarPanes(); |
| 815 if (this.sidebarPaneView) { |
| 816 this.sidebarPaneView.tabbedPane().detach(); |
| 817 this._splitWidget.uninstallResizer(this.sidebarPaneView.tabbedPane().heade
rElement()); |
| 818 } |
| 819 |
| 820 this._splitWidget.setVertical(!horizontally); |
| 821 this.showToolbarPane(null); |
| 822 |
| 823 var matchedStylesContainer = new WebInspector.VBox(); |
| 824 matchedStylesContainer.element.appendChild(this._stylesSidebarToolbar); |
| 825 var matchedStylePanesWrapper = new WebInspector.VBox(); |
| 826 matchedStylePanesWrapper.element.classList.add('style-panes-wrapper'); |
| 827 matchedStylePanesWrapper.show(matchedStylesContainer.element); |
| 828 this._stylesWidget.show(matchedStylePanesWrapper.element); |
| 829 |
| 830 var computedStylePanesWrapper = new WebInspector.VBox(); |
| 831 computedStylePanesWrapper.element.classList.add('style-panes-wrapper'); |
| 832 this._computedStyleWidget.show(computedStylePanesWrapper.element); |
253 | 833 |
254 /** | 834 /** |
255 * @override | 835 * @param {boolean} inComputedStyle |
| 836 * @this {WebInspector.ElementsPanel} |
256 */ | 837 */ |
257 focus: function() | 838 function showMetrics(inComputedStyle) { |
258 { | 839 if (inComputedStyle) |
259 if (this._treeOutlines.length) | 840 this._metricsWidget.show(computedStylePanesWrapper.element, this._comput
edStyleWidget.element); |
260 this._treeOutlines[0].focus(); | 841 else |
261 }, | 842 this._metricsWidget.show(matchedStylePanesWrapper.element); |
262 | 843 } |
263 /** | |
264 * @override | |
265 * @return {!WebInspector.SearchableView} | |
266 */ | |
267 searchableView: function() | |
268 { | |
269 return this._searchableView; | |
270 }, | |
271 | |
272 wasShown: function() | |
273 { | |
274 WebInspector.context.setFlavor(WebInspector.ElementsPanel, this); | |
275 | |
276 for (var i = 0; i < this._treeOutlines.length; ++i) { | |
277 var treeOutline = this._treeOutlines[i]; | |
278 // Attach heavy component lazily | |
279 if (treeOutline.element.parentElement !== this._contentElement) | |
280 this._contentElement.appendChild(treeOutline.element); | |
281 } | |
282 WebInspector.Panel.prototype.wasShown.call(this); | |
283 this._breadcrumbs.update(); | |
284 | |
285 for (var i = 0; i < this._treeOutlines.length; ++i) { | |
286 var treeOutline = this._treeOutlines[i]; | |
287 treeOutline.setVisible(true); | |
288 | |
289 if (!treeOutline.rootDOMNode) | |
290 if (treeOutline.domModel().existingDocument()) | |
291 this._documentUpdated(treeOutline.domModel(), treeOutline.do
mModel().existingDocument()); | |
292 else | |
293 treeOutline.domModel().requestDocument(); | |
294 } | |
295 this.focus(); | |
296 }, | |
297 | |
298 willHide: function() | |
299 { | |
300 WebInspector.context.setFlavor(WebInspector.ElementsPanel, null); | |
301 | |
302 WebInspector.DOMModel.hideDOMNodeHighlight(); | |
303 for (var i = 0; i < this._treeOutlines.length; ++i) { | |
304 var treeOutline = this._treeOutlines[i]; | |
305 treeOutline.setVisible(false); | |
306 // Detach heavy component on hide | |
307 this._contentElement.removeChild(treeOutline.element); | |
308 } | |
309 if (this._popoverHelper) | |
310 this._popoverHelper.hidePopover(); | |
311 WebInspector.Panel.prototype.willHide.call(this); | |
312 }, | |
313 | |
314 onResize: function() | |
315 { | |
316 if (WebInspector.moduleSetting("sidebarPosition").get() === "auto") | |
317 this.element.window().requestAnimationFrame(this._updateSidebarPosit
ion.bind(this)); // Do not force layout. | |
318 this._updateTreeOutlineVisibleWidth(); | |
319 }, | |
320 | 844 |
321 /** | 845 /** |
322 * @param {!WebInspector.Event} event | 846 * @param {!WebInspector.Event} event |
| 847 * @this {WebInspector.ElementsPanel} |
323 */ | 848 */ |
324 _selectedNodeChanged: function(event) | 849 function tabSelected(event) { |
325 { | 850 var tabId = /** @type {string} */ (event.data.tabId); |
326 var selectedNode = /** @type {?WebInspector.DOMNode} */ (event.data.node
); | 851 if (tabId === WebInspector.UIString('Computed')) |
327 var focus = /** @type {boolean} */ (event.data.focus); | 852 showMetrics.call(this, true); |
328 for (var i = 0; i < this._treeOutlines.length; ++i) { | 853 else if (tabId === WebInspector.UIString('Styles')) |
329 if (!selectedNode || selectedNode.domModel() !== this._treeOutlines[
i].domModel()) | 854 showMetrics.call(this, false); |
330 this._treeOutlines[i].selectDOMNode(null); | 855 } |
| 856 |
| 857 this.sidebarPaneView = |
| 858 WebInspector.viewManager.createTabbedLocation(() => WebInspector.viewMan
ager.showView('elements')); |
| 859 var tabbedPane = this.sidebarPaneView.tabbedPane(); |
| 860 tabbedPane.element.addEventListener('contextmenu', this._sidebarContextMenuE
ventFired.bind(this), false); |
| 861 if (this._popoverHelper) |
| 862 this._popoverHelper.hidePopover(); |
| 863 this._popoverHelper = new WebInspector.PopoverHelper(tabbedPane.element); |
| 864 this._popoverHelper.initializeCallbacks(this._getPopoverAnchor.bind(this), t
his._showPopover.bind(this)); |
| 865 this._popoverHelper.setTimeout(0); |
| 866 |
| 867 if (horizontally) { |
| 868 // Styles and computed are merged into a single tab. |
| 869 this._splitWidget.installResizer(tabbedPane.headerElement()); |
| 870 |
| 871 var stylesView = new WebInspector.SimpleView(WebInspector.UIString('Styles
')); |
| 872 stylesView.element.classList.add('flex-auto'); |
| 873 |
| 874 var splitWidget = new WebInspector.SplitWidget(true, true, 'stylesPaneSpli
tViewState', 215); |
| 875 splitWidget.show(stylesView.element); |
| 876 |
| 877 splitWidget.setMainWidget(matchedStylesContainer); |
| 878 splitWidget.setSidebarWidget(computedStylePanesWrapper); |
| 879 |
| 880 this.sidebarPaneView.appendView(stylesView); |
| 881 this._stylesViewToReveal = stylesView; |
| 882 } else { |
| 883 // Styles and computed are in separate tabs. |
| 884 var stylesView = new WebInspector.SimpleView(WebInspector.UIString('Styles
')); |
| 885 stylesView.element.classList.add('flex-auto', 'metrics-and-styles'); |
| 886 matchedStylesContainer.show(stylesView.element); |
| 887 |
| 888 var computedView = new WebInspector.SimpleView(WebInspector.UIString('Comp
uted')); |
| 889 computedView.element.classList.add('composite', 'fill', 'metrics-and-compu
ted'); |
| 890 computedStylePanesWrapper.show(computedView.element); |
| 891 |
| 892 tabbedPane.addEventListener(WebInspector.TabbedPane.Events.TabSelected, ta
bSelected, this); |
| 893 this.sidebarPaneView.appendView(stylesView); |
| 894 this.sidebarPaneView.appendView(computedView); |
| 895 this._stylesViewToReveal = stylesView; |
| 896 } |
| 897 |
| 898 showMetrics.call(this, horizontally); |
| 899 |
| 900 this.sidebarPaneView.appendApplicableItems('elements-sidebar'); |
| 901 for (var i = 0; i < extensionSidebarPanes.length; ++i) |
| 902 this._addExtensionSidebarPane(extensionSidebarPanes[i]); |
| 903 |
| 904 this._splitWidget.setSidebarWidget(this.sidebarPaneView.tabbedPane()); |
| 905 } |
| 906 |
| 907 /** |
| 908 * @param {!WebInspector.Event} event |
| 909 */ |
| 910 _extensionSidebarPaneAdded(event) { |
| 911 var pane = /** @type {!WebInspector.ExtensionSidebarPane} */ (event.data); |
| 912 this._addExtensionSidebarPane(pane); |
| 913 } |
| 914 |
| 915 /** |
| 916 * @param {!WebInspector.ExtensionSidebarPane} pane |
| 917 */ |
| 918 _addExtensionSidebarPane(pane) { |
| 919 if (pane.panelName() === this.name) |
| 920 this.sidebarPaneView.appendView(pane); |
| 921 } |
| 922 }; |
| 923 |
| 924 WebInspector.ElementsPanel._elementsSidebarViewTitleSymbol = Symbol('title'); |
| 925 |
| 926 /** |
| 927 * @implements {WebInspector.ContextMenu.Provider} |
| 928 * @unrestricted |
| 929 */ |
| 930 WebInspector.ElementsPanel.ContextMenuProvider = class { |
| 931 /** |
| 932 * @override |
| 933 * @param {!Event} event |
| 934 * @param {!WebInspector.ContextMenu} contextMenu |
| 935 * @param {!Object} object |
| 936 */ |
| 937 appendApplicableItems(event, contextMenu, object) { |
| 938 if (!(object instanceof WebInspector.RemoteObject && |
| 939 (/** @type {!WebInspector.RemoteObject} */ (object)).isNode()) && |
| 940 !(object instanceof WebInspector.DOMNode) && !(object instanceof WebInsp
ector.DeferredDOMNode)) { |
| 941 return; |
| 942 } |
| 943 |
| 944 // Add debbuging-related actions |
| 945 if (object instanceof WebInspector.DOMNode) { |
| 946 contextMenu.appendSeparator(); |
| 947 WebInspector.domBreakpointsSidebarPane.populateNodeContextMenu(object, con
textMenu, true); |
| 948 } |
| 949 |
| 950 // Skip adding "Reveal..." menu item for our own tree outline. |
| 951 if (WebInspector.ElementsPanel.instance().element.isAncestor(/** @type {!Nod
e} */ (event.target))) |
| 952 return; |
| 953 var commandCallback = WebInspector.Revealer.reveal.bind(WebInspector.Reveale
r, object); |
| 954 contextMenu.appendItem(WebInspector.UIString.capitalize('Reveal in Elements
^panel'), commandCallback); |
| 955 } |
| 956 }; |
| 957 |
| 958 /** |
| 959 * @implements {WebInspector.Revealer} |
| 960 * @unrestricted |
| 961 */ |
| 962 WebInspector.ElementsPanel.DOMNodeRevealer = class { |
| 963 /** |
| 964 * @override |
| 965 * @param {!Object} node |
| 966 * @return {!Promise} |
| 967 */ |
| 968 reveal(node) { |
| 969 var panel = WebInspector.ElementsPanel.instance(); |
| 970 panel._pendingNodeReveal = true; |
| 971 |
| 972 return new Promise(revealPromise); |
| 973 |
| 974 /** |
| 975 * @param {function(undefined)} resolve |
| 976 * @param {function(!Error)} reject |
| 977 */ |
| 978 function revealPromise(resolve, reject) { |
| 979 if (node instanceof WebInspector.DOMNode) { |
| 980 onNodeResolved(/** @type {!WebInspector.DOMNode} */ (node)); |
| 981 } else if (node instanceof WebInspector.DeferredDOMNode) { |
| 982 (/** @type {!WebInspector.DeferredDOMNode} */ (node)).resolve(onNodeReso
lved); |
| 983 } else if (node instanceof WebInspector.RemoteObject) { |
| 984 var domModel = WebInspector.DOMModel.fromTarget(/** @type {!WebInspector
.RemoteObject} */ (node).target()); |
| 985 if (domModel) |
| 986 domModel.pushObjectAsNodeToFrontend(node, onNodeResolved); |
| 987 else |
| 988 reject(new Error('Could not resolve a node to reveal.')); |
| 989 } else { |
| 990 reject(new Error('Can\'t reveal a non-node.')); |
| 991 panel._pendingNodeReveal = false; |
| 992 } |
| 993 |
| 994 /** |
| 995 * @param {?WebInspector.DOMNode} resolvedNode |
| 996 */ |
| 997 function onNodeResolved(resolvedNode) { |
| 998 panel._pendingNodeReveal = false; |
| 999 |
| 1000 if (resolvedNode) { |
| 1001 panel.revealAndSelectNode(resolvedNode).then(resolve); |
| 1002 return; |
331 } | 1003 } |
332 | 1004 reject(new Error('Could not resolve node to reveal.')); |
333 this._breadcrumbs.setSelectedNode(selectedNode); | 1005 } |
334 | 1006 } |
335 WebInspector.context.setFlavor(WebInspector.DOMNode, selectedNode); | 1007 } |
336 | |
337 if (!selectedNode) | |
338 return; | |
339 selectedNode.setAsInspectedNode(); | |
340 if (focus) { | |
341 this._selectedNodeOnReset = selectedNode; | |
342 this._hasNonDefaultSelectedNode = true; | |
343 } | |
344 | |
345 var executionContexts = selectedNode.target().runtimeModel.executionCont
exts(); | |
346 var nodeFrameId = selectedNode.frameId(); | |
347 for (var context of executionContexts) { | |
348 if (context.frameId === nodeFrameId) { | |
349 WebInspector.context.setFlavor(WebInspector.ExecutionContext, co
ntext); | |
350 break; | |
351 } | |
352 } | |
353 }, | |
354 | |
355 _reset: function() | |
356 { | |
357 delete this.currentQuery; | |
358 }, | |
359 | |
360 /** | |
361 * @param {!WebInspector.Event} event | |
362 */ | |
363 _documentUpdatedEvent: function(event) | |
364 { | |
365 this._documentUpdated(/** @type {!WebInspector.DOMModel} */ (event.targe
t), /** @type {?WebInspector.DOMDocument} */ (event.data)); | |
366 }, | |
367 | |
368 /** | |
369 * @param {!WebInspector.DOMModel} domModel | |
370 * @param {?WebInspector.DOMDocument} inspectedRootDocument | |
371 */ | |
372 _documentUpdated: function(domModel, inspectedRootDocument) | |
373 { | |
374 this._reset(); | |
375 this.searchCanceled(); | |
376 | |
377 var treeOutline = WebInspector.ElementsTreeOutline.forDOMModel(domModel)
; | |
378 treeOutline.rootDOMNode = inspectedRootDocument; | |
379 | |
380 if (!inspectedRootDocument) { | |
381 if (this.isShowing()) | |
382 domModel.requestDocument(); | |
383 return; | |
384 } | |
385 | |
386 this._hasNonDefaultSelectedNode = false; | |
387 WebInspector.domBreakpointsSidebarPane.restoreBreakpoints(inspectedRootD
ocument); | |
388 | |
389 if (this._omitDefaultSelection) | |
390 return; | |
391 | |
392 var savedSelectedNodeOnReset = this._selectedNodeOnReset; | |
393 restoreNode.call(this, domModel, this._selectedNodeOnReset); | |
394 | |
395 /** | |
396 * @param {!WebInspector.DOMModel} domModel | |
397 * @param {?WebInspector.DOMNode} staleNode | |
398 * @this {WebInspector.ElementsPanel} | |
399 */ | |
400 function restoreNode(domModel, staleNode) | |
401 { | |
402 var nodePath = staleNode ? staleNode.path() : null; | |
403 if (!nodePath) { | |
404 onNodeRestored.call(this, null); | |
405 return; | |
406 } | |
407 domModel.pushNodeByPathToFrontend(nodePath, onNodeRestored.bind(this
)); | |
408 } | |
409 | |
410 /** | |
411 * @param {?DOMAgent.NodeId} restoredNodeId | |
412 * @this {WebInspector.ElementsPanel} | |
413 */ | |
414 function onNodeRestored(restoredNodeId) | |
415 { | |
416 if (savedSelectedNodeOnReset !== this._selectedNodeOnReset) | |
417 return; | |
418 var node = restoredNodeId ? domModel.nodeForId(restoredNodeId) : nul
l; | |
419 if (!node) { | |
420 var inspectedDocument = domModel.existingDocument(); | |
421 node = inspectedDocument ? inspectedDocument.body || inspectedDo
cument.documentElement : null; | |
422 } | |
423 this._setDefaultSelectedNode(node); | |
424 this._lastSelectedNodeSelectedForTest(); | |
425 } | |
426 }, | |
427 | |
428 _lastSelectedNodeSelectedForTest: function() { }, | |
429 | |
430 /** | |
431 * @param {?WebInspector.DOMNode} node | |
432 */ | |
433 _setDefaultSelectedNode: function(node) | |
434 { | |
435 if (!node || this._hasNonDefaultSelectedNode || this._pendingNodeReveal) | |
436 return; | |
437 var treeOutline = WebInspector.ElementsTreeOutline.forDOMModel(node.domM
odel()); | |
438 if (!treeOutline) | |
439 return; | |
440 this.selectDOMNode(node); | |
441 if (treeOutline.selectedTreeElement) | |
442 treeOutline.selectedTreeElement.expand(); | |
443 }, | |
444 | |
445 /** | |
446 * @override | |
447 */ | |
448 searchCanceled: function() | |
449 { | |
450 delete this._searchQuery; | |
451 this._hideSearchHighlights(); | |
452 | |
453 this._searchableView.updateSearchMatchesCount(0); | |
454 | |
455 delete this._currentSearchResultIndex; | |
456 delete this._searchResults; | |
457 | |
458 WebInspector.DOMModel.cancelSearch(); | |
459 }, | |
460 | |
461 /** | |
462 * @override | |
463 * @param {!WebInspector.SearchableView.SearchConfig} searchConfig | |
464 * @param {boolean} shouldJump | |
465 * @param {boolean=} jumpBackwards | |
466 */ | |
467 performSearch: function(searchConfig, shouldJump, jumpBackwards) | |
468 { | |
469 var query = searchConfig.query; | |
470 // Call searchCanceled since it will reset everything we need before doi
ng a new search. | |
471 this.searchCanceled(); | |
472 | |
473 const whitespaceTrimmedQuery = query.trim(); | |
474 if (!whitespaceTrimmedQuery.length) | |
475 return; | |
476 | |
477 this._searchQuery = query; | |
478 | |
479 var promises = []; | |
480 var domModels = WebInspector.DOMModel.instances(); | |
481 for (var domModel of domModels) | |
482 promises.push(domModel.performSearchPromise(whitespaceTrimmedQuery,
WebInspector.moduleSetting("showUAShadowDOM").get())); | |
483 Promise.all(promises).then(resultCountCallback.bind(this)); | |
484 | |
485 /** | |
486 * @param {!Array.<number>} resultCounts | |
487 * @this {WebInspector.ElementsPanel} | |
488 */ | |
489 function resultCountCallback(resultCounts) | |
490 { | |
491 /** | |
492 * @type {!Array.<{domModel: !WebInspector.DOMModel, index: number,
node: (?WebInspector.DOMNode|undefined)}>} | |
493 */ | |
494 this._searchResults = []; | |
495 for (var i = 0; i < resultCounts.length; ++i) { | |
496 var resultCount = resultCounts[i]; | |
497 for (var j = 0; j < resultCount; ++j) | |
498 this._searchResults.push({domModel: domModels[i], index: j,
node: undefined}); | |
499 } | |
500 this._searchableView.updateSearchMatchesCount(this._searchResults.le
ngth); | |
501 if (!this._searchResults.length) | |
502 return; | |
503 this._currentSearchResultIndex = -1; | |
504 | |
505 if (shouldJump) | |
506 this._jumpToSearchResult(jumpBackwards ? -1 : 0); | |
507 } | |
508 }, | |
509 | |
510 _domWordWrapSettingChanged: function(event) | |
511 { | |
512 // FIXME: crbug.com/425984 | |
513 this._contentElement.classList.toggle("elements-wrap", event.data); | |
514 for (var i = 0; i < this._treeOutlines.length; ++i) | |
515 this._treeOutlines[i].setWordWrap(/** @type {boolean} */ (event.data
)); | |
516 }, | |
517 | |
518 switchToAndFocus: function(node) | |
519 { | |
520 // Reset search restore. | |
521 this._searchableView.cancelSearch(); | |
522 WebInspector.viewManager.showView("elements").then(() => this.selectDOMN
ode(node, true)); | |
523 }, | |
524 | |
525 /** | |
526 * @param {!Element} element | |
527 * @param {!Event} event | |
528 * @return {!Element|!AnchorBox|undefined} | |
529 */ | |
530 _getPopoverAnchor: function(element, event) | |
531 { | |
532 var anchor = element.enclosingNodeOrSelfWithClass("webkit-html-resource-
link"); | |
533 if (!anchor || !anchor.href) | |
534 return; | |
535 | |
536 return anchor; | |
537 }, | |
538 | |
539 /** | |
540 * @param {!Element} anchor | |
541 * @param {!WebInspector.Popover} popover | |
542 */ | |
543 _showPopover: function(anchor, popover) | |
544 { | |
545 var node = this.selectedDOMNode(); | |
546 if (node) | |
547 WebInspector.DOMPresentationUtils.buildImagePreviewContents(node.tar
get(), anchor.href, true, showPopover); | |
548 | |
549 /** | |
550 * @param {!Element=} contents | |
551 */ | |
552 function showPopover(contents) | |
553 { | |
554 if (!contents) | |
555 return; | |
556 popover.setCanShrink(false); | |
557 popover.showForAnchor(contents, anchor); | |
558 } | |
559 }, | |
560 | |
561 _jumpToSearchResult: function(index) | |
562 { | |
563 this._hideSearchHighlights(); | |
564 this._currentSearchResultIndex = (index + this._searchResults.length) %
this._searchResults.length; | |
565 this._highlightCurrentSearchResult(); | |
566 }, | |
567 | |
568 /** | |
569 * @override | |
570 */ | |
571 jumpToNextSearchResult: function() | |
572 { | |
573 if (!this._searchResults) | |
574 return; | |
575 this._jumpToSearchResult(this._currentSearchResultIndex + 1); | |
576 }, | |
577 | |
578 /** | |
579 * @override | |
580 */ | |
581 jumpToPreviousSearchResult: function() | |
582 { | |
583 if (!this._searchResults) | |
584 return; | |
585 this._jumpToSearchResult(this._currentSearchResultIndex - 1); | |
586 }, | |
587 | |
588 /** | |
589 * @override | |
590 * @return {boolean} | |
591 */ | |
592 supportsCaseSensitiveSearch: function() | |
593 { | |
594 return false; | |
595 }, | |
596 | |
597 /** | |
598 * @override | |
599 * @return {boolean} | |
600 */ | |
601 supportsRegexSearch: function() | |
602 { | |
603 return false; | |
604 }, | |
605 | |
606 _highlightCurrentSearchResult: function() | |
607 { | |
608 var index = this._currentSearchResultIndex; | |
609 var searchResults = this._searchResults; | |
610 var searchResult = searchResults[index]; | |
611 | |
612 if (searchResult.node === null) { | |
613 this._searchableView.updateCurrentMatchIndex(index); | |
614 return; | |
615 } | |
616 | |
617 /** | |
618 * @param {?WebInspector.DOMNode} node | |
619 * @this {WebInspector.ElementsPanel} | |
620 */ | |
621 function searchCallback(node) | |
622 { | |
623 searchResult.node = node; | |
624 this._highlightCurrentSearchResult(); | |
625 } | |
626 | |
627 if (typeof searchResult.node === "undefined") { | |
628 // No data for slot, request it. | |
629 searchResult.domModel.searchResult(searchResult.index, searchCallbac
k.bind(this)); | |
630 return; | |
631 } | |
632 | |
633 this._searchableView.updateCurrentMatchIndex(index); | |
634 | |
635 var treeElement = this._treeElementForNode(searchResult.node); | |
636 if (treeElement) { | |
637 treeElement.highlightSearchResults(this._searchQuery); | |
638 treeElement.reveal(); | |
639 var matches = treeElement.listItemElement.getElementsByClassName(Web
Inspector.highlightedSearchResultClassName); | |
640 if (matches.length) | |
641 matches[0].scrollIntoViewIfNeeded(false); | |
642 } | |
643 }, | |
644 | |
645 _hideSearchHighlights: function() | |
646 { | |
647 if (!this._searchResults || !this._searchResults.length || this._current
SearchResultIndex < 0) | |
648 return; | |
649 var searchResult = this._searchResults[this._currentSearchResultIndex]; | |
650 if (!searchResult.node) | |
651 return; | |
652 var treeOutline = WebInspector.ElementsTreeOutline.forDOMModel(searchRes
ult.node.domModel()); | |
653 var treeElement = treeOutline.findTreeElement(searchResult.node); | |
654 if (treeElement) | |
655 treeElement.hideSearchHighlights(); | |
656 }, | |
657 | |
658 /** | |
659 * @return {?WebInspector.DOMNode} | |
660 */ | |
661 selectedDOMNode: function() | |
662 { | |
663 for (var i = 0; i < this._treeOutlines.length; ++i) { | |
664 var treeOutline = this._treeOutlines[i]; | |
665 if (treeOutline.selectedDOMNode()) | |
666 return treeOutline.selectedDOMNode(); | |
667 } | |
668 return null; | |
669 }, | |
670 | |
671 /** | |
672 * @param {!WebInspector.DOMNode} node | |
673 * @param {boolean=} focus | |
674 */ | |
675 selectDOMNode: function(node, focus) | |
676 { | |
677 for (var i = 0; i < this._treeOutlines.length; ++i) { | |
678 var treeOutline = this._treeOutlines[i]; | |
679 if (treeOutline.domModel() === node.domModel()) | |
680 treeOutline.selectDOMNode(node, focus); | |
681 else | |
682 treeOutline.selectDOMNode(null); | |
683 } | |
684 }, | |
685 | |
686 /** | |
687 * @param {!WebInspector.Event} event | |
688 */ | |
689 _updateBreadcrumbIfNeeded: function(event) | |
690 { | |
691 var nodes = /** @type {!Array.<!WebInspector.DOMNode>} */ (event.data); | |
692 this._breadcrumbs.updateNodes(nodes); | |
693 }, | |
694 | |
695 /** | |
696 * @param {!WebInspector.Event} event | |
697 */ | |
698 _crumbNodeSelected: function(event) | |
699 { | |
700 var node = /** @type {!WebInspector.DOMNode} */ (event.data); | |
701 this.selectDOMNode(node, true); | |
702 }, | |
703 | |
704 /** | |
705 * @override | |
706 * @param {!KeyboardEvent} event | |
707 */ | |
708 handleShortcut: function(event) | |
709 { | |
710 /** | |
711 * @param {!WebInspector.ElementsTreeOutline} treeOutline | |
712 */ | |
713 function handleUndoRedo(treeOutline) | |
714 { | |
715 if (WebInspector.KeyboardShortcut.eventHasCtrlOrMeta(event) && !even
t.shiftKey && (event.key === "Z" || event.key === "z")) { // Z key | |
716 treeOutline.domModel().undo(); | |
717 event.handled = true; | |
718 return; | |
719 } | |
720 | |
721 var isRedoKey = WebInspector.isMac() ? event.metaKey && event.shiftK
ey && (event.key === "Z" || event.key === "z") : // Z key | |
722 event.ctrlKey && (event.key =
== "Y" || event.key === "y"); // Y key | |
723 if (isRedoKey) { | |
724 treeOutline.domModel().redo(); | |
725 event.handled = true; | |
726 } | |
727 } | |
728 | |
729 if (WebInspector.isEditing() && event.keyCode !== WebInspector.KeyboardS
hortcut.Keys.F2.code) | |
730 return; | |
731 | |
732 var treeOutline = null; | |
733 for (var i = 0; i < this._treeOutlines.length; ++i) { | |
734 if (this._treeOutlines[i].selectedDOMNode()) | |
735 treeOutline = this._treeOutlines[i]; | |
736 } | |
737 if (!treeOutline) | |
738 return; | |
739 | |
740 if (!treeOutline.editing()) { | |
741 handleUndoRedo.call(null, treeOutline); | |
742 if (event.handled) { | |
743 this._stylesWidget.forceUpdate(); | |
744 return; | |
745 } | |
746 } | |
747 | |
748 treeOutline.handleShortcut(event); | |
749 if (event.handled) | |
750 return; | |
751 | |
752 WebInspector.Panel.prototype.handleShortcut.call(this, event); | |
753 }, | |
754 | |
755 /** | |
756 * @param {?WebInspector.DOMNode} node | |
757 * @return {?WebInspector.ElementsTreeOutline} | |
758 */ | |
759 _treeOutlineForNode: function(node) | |
760 { | |
761 if (!node) | |
762 return null; | |
763 return WebInspector.ElementsTreeOutline.forDOMModel(node.domModel()); | |
764 }, | |
765 | |
766 /** | |
767 * @param {!WebInspector.DOMNode} node | |
768 * @return {?WebInspector.ElementsTreeElement} | |
769 */ | |
770 _treeElementForNode: function(node) | |
771 { | |
772 var treeOutline = this._treeOutlineForNode(node); | |
773 return /** @type {?WebInspector.ElementsTreeElement} */ (treeOutline.fin
dTreeElement(node)); | |
774 }, | |
775 | |
776 /** | |
777 * @param {!WebInspector.DOMNode} node | |
778 * @return {!WebInspector.DOMNode} | |
779 */ | |
780 _leaveUserAgentShadowDOM: function(node) | |
781 { | |
782 var userAgentShadowRoot; | |
783 while ((userAgentShadowRoot = node.ancestorUserAgentShadowRoot()) && use
rAgentShadowRoot.parentNode) | |
784 node = userAgentShadowRoot.parentNode; | |
785 return node; | |
786 }, | |
787 | |
788 /** | |
789 * @param {!WebInspector.DOMNode} node | |
790 * @return {!Promise} | |
791 */ | |
792 revealAndSelectNode: function(node) | |
793 { | |
794 if (WebInspector.inspectElementModeController && WebInspector.inspectEle
mentModeController.isInInspectElementMode()) | |
795 WebInspector.inspectElementModeController.stopInspection(); | |
796 | |
797 this._omitDefaultSelection = true; | |
798 | |
799 node = WebInspector.moduleSetting("showUAShadowDOM").get() ? node : this
._leaveUserAgentShadowDOM(node); | |
800 node.highlightForTwoSeconds(); | |
801 | |
802 return WebInspector.viewManager.showView("elements").then(() => { | |
803 this.selectDOMNode(node, true); | |
804 delete this._omitDefaultSelection; | |
805 | |
806 if (!this._notFirstInspectElement) | |
807 InspectorFrontendHost.inspectElementCompleted(); | |
808 this._notFirstInspectElement = true; | |
809 }); | |
810 }, | |
811 | |
812 _sidebarContextMenuEventFired: function(event) | |
813 { | |
814 var contextMenu = new WebInspector.ContextMenu(event); | |
815 contextMenu.appendApplicableItems(/** @type {!Object} */ (event.deepElem
entFromPoint())); | |
816 contextMenu.show(); | |
817 }, | |
818 | |
819 _showUAShadowDOMChanged: function() | |
820 { | |
821 for (var i = 0; i < this._treeOutlines.length; ++i) | |
822 this._treeOutlines[i].update(); | |
823 }, | |
824 | |
825 _updateSidebarPosition: function() | |
826 { | |
827 var horizontally; | |
828 var position = WebInspector.moduleSetting("sidebarPosition").get(); | |
829 if (position === "right") | |
830 horizontally = false; | |
831 else if (position === "bottom") | |
832 horizontally = true; | |
833 else | |
834 horizontally = WebInspector.inspectorView.element.offsetWidth < 680; | |
835 | |
836 if (this.sidebarPaneView && horizontally === !this._splitWidget.isVertic
al()) | |
837 return; | |
838 | |
839 if (this.sidebarPaneView && this.sidebarPaneView.tabbedPane().shouldHide
OnDetach()) | |
840 return; // We can't reparent extension iframes. | |
841 | |
842 var extensionSidebarPanes = WebInspector.extensionServer.sidebarPanes(); | |
843 if (this.sidebarPaneView) { | |
844 this.sidebarPaneView.tabbedPane().detach(); | |
845 this._splitWidget.uninstallResizer(this.sidebarPaneView.tabbedPane()
.headerElement()); | |
846 } | |
847 | |
848 this._splitWidget.setVertical(!horizontally); | |
849 this.showToolbarPane(null); | |
850 | |
851 var matchedStylesContainer = new WebInspector.VBox(); | |
852 matchedStylesContainer.element.appendChild(this._stylesSidebarToolbar); | |
853 var matchedStylePanesWrapper = new WebInspector.VBox(); | |
854 matchedStylePanesWrapper.element.classList.add("style-panes-wrapper"); | |
855 matchedStylePanesWrapper.show(matchedStylesContainer.element); | |
856 this._stylesWidget.show(matchedStylePanesWrapper.element); | |
857 | |
858 var computedStylePanesWrapper = new WebInspector.VBox(); | |
859 computedStylePanesWrapper.element.classList.add("style-panes-wrapper"); | |
860 this._computedStyleWidget.show(computedStylePanesWrapper.element); | |
861 | |
862 /** | |
863 * @param {boolean} inComputedStyle | |
864 * @this {WebInspector.ElementsPanel} | |
865 */ | |
866 function showMetrics(inComputedStyle) | |
867 { | |
868 if (inComputedStyle) | |
869 this._metricsWidget.show(computedStylePanesWrapper.element, this
._computedStyleWidget.element); | |
870 else | |
871 this._metricsWidget.show(matchedStylePanesWrapper.element); | |
872 } | |
873 | |
874 /** | |
875 * @param {!WebInspector.Event} event | |
876 * @this {WebInspector.ElementsPanel} | |
877 */ | |
878 function tabSelected(event) | |
879 { | |
880 var tabId = /** @type {string} */ (event.data.tabId); | |
881 if (tabId === WebInspector.UIString("Computed")) | |
882 showMetrics.call(this, true); | |
883 else if (tabId === WebInspector.UIString("Styles")) | |
884 showMetrics.call(this, false); | |
885 } | |
886 | |
887 this.sidebarPaneView = WebInspector.viewManager.createTabbedLocation(()
=> WebInspector.viewManager.showView("elements")); | |
888 var tabbedPane = this.sidebarPaneView.tabbedPane(); | |
889 tabbedPane.element.addEventListener("contextmenu", this._sidebarContextM
enuEventFired.bind(this), false); | |
890 if (this._popoverHelper) | |
891 this._popoverHelper.hidePopover(); | |
892 this._popoverHelper = new WebInspector.PopoverHelper(tabbedPane.element)
; | |
893 this._popoverHelper.initializeCallbacks(this._getPopoverAnchor.bind(this
), this._showPopover.bind(this)); | |
894 this._popoverHelper.setTimeout(0); | |
895 | |
896 if (horizontally) { | |
897 // Styles and computed are merged into a single tab. | |
898 this._splitWidget.installResizer(tabbedPane.headerElement()); | |
899 | |
900 var stylesView = new WebInspector.SimpleView(WebInspector.UIString("
Styles")); | |
901 stylesView.element.classList.add("flex-auto"); | |
902 | |
903 var splitWidget = new WebInspector.SplitWidget(true, true, "stylesPa
neSplitViewState", 215); | |
904 splitWidget.show(stylesView.element); | |
905 | |
906 splitWidget.setMainWidget(matchedStylesContainer); | |
907 splitWidget.setSidebarWidget(computedStylePanesWrapper); | |
908 | |
909 this.sidebarPaneView.appendView(stylesView); | |
910 this._stylesViewToReveal = stylesView; | |
911 } else { | |
912 // Styles and computed are in separate tabs. | |
913 var stylesView = new WebInspector.SimpleView(WebInspector.UIString("
Styles")); | |
914 stylesView.element.classList.add("flex-auto", "metrics-and-styles"); | |
915 matchedStylesContainer.show(stylesView.element); | |
916 | |
917 var computedView = new WebInspector.SimpleView(WebInspector.UIString
("Computed")); | |
918 computedView.element.classList.add("composite", "fill", "metrics-and
-computed"); | |
919 computedStylePanesWrapper.show(computedView.element); | |
920 | |
921 tabbedPane.addEventListener(WebInspector.TabbedPane.Events.TabSelect
ed, tabSelected, this); | |
922 this.sidebarPaneView.appendView(stylesView); | |
923 this.sidebarPaneView.appendView(computedView); | |
924 this._stylesViewToReveal = stylesView; | |
925 } | |
926 | |
927 showMetrics.call(this, horizontally); | |
928 | |
929 this.sidebarPaneView.appendApplicableItems("elements-sidebar"); | |
930 for (var i = 0; i < extensionSidebarPanes.length; ++i) | |
931 this._addExtensionSidebarPane(extensionSidebarPanes[i]); | |
932 | |
933 this._splitWidget.setSidebarWidget(this.sidebarPaneView.tabbedPane()); | |
934 }, | |
935 | |
936 /** | |
937 * @param {!WebInspector.Event} event | |
938 */ | |
939 _extensionSidebarPaneAdded: function(event) | |
940 { | |
941 var pane = /** @type {!WebInspector.ExtensionSidebarPane} */ (event.data
); | |
942 this._addExtensionSidebarPane(pane); | |
943 }, | |
944 | |
945 /** | |
946 * @param {!WebInspector.ExtensionSidebarPane} pane | |
947 */ | |
948 _addExtensionSidebarPane: function(pane) | |
949 { | |
950 if (pane.panelName() === this.name) | |
951 this.sidebarPaneView.appendView(pane); | |
952 }, | |
953 | |
954 __proto__: WebInspector.Panel.prototype | |
955 }; | 1008 }; |
956 | 1009 |
957 /** | 1010 /** |
958 * @constructor | 1011 * @implements {WebInspector.Revealer} |
959 * @implements {WebInspector.ContextMenu.Provider} | 1012 * @unrestricted |
960 */ | 1013 */ |
961 WebInspector.ElementsPanel.ContextMenuProvider = function() | 1014 WebInspector.ElementsPanel.CSSPropertyRevealer = class { |
962 { | 1015 /** |
| 1016 * @override |
| 1017 * @param {!Object} property |
| 1018 * @return {!Promise} |
| 1019 */ |
| 1020 reveal(property) { |
| 1021 var panel = WebInspector.ElementsPanel.instance(); |
| 1022 return panel._revealProperty(/** @type {!WebInspector.CSSProperty} */ (prope
rty)); |
| 1023 } |
963 }; | 1024 }; |
964 | 1025 |
965 WebInspector.ElementsPanel.ContextMenuProvider.prototype = { | 1026 |
966 /** | 1027 /** |
967 * @override | 1028 * @implements {WebInspector.ActionDelegate} |
968 * @param {!Event} event | 1029 * @unrestricted |
969 * @param {!WebInspector.ContextMenu} contextMenu | 1030 */ |
970 * @param {!Object} object | 1031 WebInspector.ElementsActionDelegate = class { |
971 */ | 1032 /** |
972 appendApplicableItems: function(event, contextMenu, object) | 1033 * @override |
973 { | 1034 * @param {!WebInspector.Context} context |
974 if (!(object instanceof WebInspector.RemoteObject && (/** @type {!WebIns
pector.RemoteObject} */ (object)).isNode()) | 1035 * @param {string} actionId |
975 && !(object instanceof WebInspector.DOMNode) | 1036 * @return {boolean} |
976 && !(object instanceof WebInspector.DeferredDOMNode)) { | 1037 */ |
977 return; | 1038 handleAction(context, actionId) { |
978 } | 1039 var node = WebInspector.context.flavor(WebInspector.DOMNode); |
979 | 1040 if (!node) |
980 // Add debbuging-related actions | 1041 return true; |
981 if (object instanceof WebInspector.DOMNode) { | 1042 var treeOutline = WebInspector.ElementsTreeOutline.forDOMModel(node.domModel
()); |
982 contextMenu.appendSeparator(); | 1043 if (!treeOutline) |
983 WebInspector.domBreakpointsSidebarPane.populateNodeContextMenu(objec
t, contextMenu, true); | 1044 return true; |
984 } | 1045 |
985 | 1046 switch (actionId) { |
986 // Skip adding "Reveal..." menu item for our own tree outline. | 1047 case 'elements.hide-element': |
987 if (WebInspector.ElementsPanel.instance().element.isAncestor(/** @type {
!Node} */ (event.target))) | 1048 treeOutline.toggleHideElement(node); |
988 return; | 1049 return true; |
989 var commandCallback = WebInspector.Revealer.reveal.bind(WebInspector.Rev
ealer, object); | 1050 case 'elements.edit-as-html': |
990 contextMenu.appendItem(WebInspector.UIString.capitalize("Reveal in Eleme
nts ^panel"), commandCallback); | 1051 treeOutline.toggleEditAsHTML(node); |
991 } | 1052 return true; |
| 1053 } |
| 1054 return false; |
| 1055 } |
992 }; | 1056 }; |
993 | 1057 |
994 /** | 1058 /** |
995 * @constructor | 1059 * @implements {WebInspector.DOMPresentationUtils.MarkerDecorator} |
996 * @implements {WebInspector.Revealer} | 1060 * @unrestricted |
997 */ | 1061 */ |
998 WebInspector.ElementsPanel.DOMNodeRevealer = function() { }; | 1062 WebInspector.ElementsPanel.PseudoStateMarkerDecorator = class { |
999 | 1063 /** |
1000 WebInspector.ElementsPanel.DOMNodeRevealer.prototype = { | 1064 * @override |
1001 /** | 1065 * @param {!WebInspector.DOMNode} node |
1002 * @override | 1066 * @return {?{title: string, color: string}} |
1003 * @param {!Object} node | 1067 */ |
1004 * @return {!Promise} | 1068 decorate(node) { |
1005 */ | 1069 return { |
1006 reveal: function(node) | 1070 color: 'orange', |
1007 { | 1071 title: WebInspector.UIString( |
1008 var panel = WebInspector.ElementsPanel.instance(); | 1072 'Element state: %s', ':' + WebInspector.CSSModel.fromNode(node).pseudo
State(node).join(', :')) |
1009 panel._pendingNodeReveal = true; | 1073 }; |
1010 | 1074 } |
1011 return new Promise(revealPromise); | |
1012 | |
1013 /** | |
1014 * @param {function(undefined)} resolve | |
1015 * @param {function(!Error)} reject | |
1016 */ | |
1017 function revealPromise(resolve, reject) | |
1018 { | |
1019 if (node instanceof WebInspector.DOMNode) { | |
1020 onNodeResolved(/** @type {!WebInspector.DOMNode} */ (node)); | |
1021 } else if (node instanceof WebInspector.DeferredDOMNode) { | |
1022 (/** @type {!WebInspector.DeferredDOMNode} */ (node)).resolve(on
NodeResolved); | |
1023 } else if (node instanceof WebInspector.RemoteObject) { | |
1024 var domModel = WebInspector.DOMModel.fromTarget(/** @type {!WebI
nspector.RemoteObject} */ (node).target()); | |
1025 if (domModel) | |
1026 domModel.pushObjectAsNodeToFrontend(node, onNodeResolved); | |
1027 else | |
1028 reject(new Error("Could not resolve a node to reveal.")); | |
1029 } else { | |
1030 reject(new Error("Can't reveal a non-node.")); | |
1031 panel._pendingNodeReveal = false; | |
1032 } | |
1033 | |
1034 /** | |
1035 * @param {?WebInspector.DOMNode} resolvedNode | |
1036 */ | |
1037 function onNodeResolved(resolvedNode) | |
1038 { | |
1039 panel._pendingNodeReveal = false; | |
1040 | |
1041 if (resolvedNode) { | |
1042 panel.revealAndSelectNode(resolvedNode).then(resolve); | |
1043 return; | |
1044 } | |
1045 reject(new Error("Could not resolve node to reveal.")); | |
1046 } | |
1047 } | |
1048 } | |
1049 }; | 1075 }; |
1050 | |
1051 /** | |
1052 * @constructor | |
1053 * @implements {WebInspector.Revealer} | |
1054 */ | |
1055 WebInspector.ElementsPanel.CSSPropertyRevealer = function() { }; | |
1056 | |
1057 WebInspector.ElementsPanel.CSSPropertyRevealer.prototype = { | |
1058 /** | |
1059 * @override | |
1060 * @param {!Object} property | |
1061 * @return {!Promise} | |
1062 */ | |
1063 reveal: function(property) | |
1064 { | |
1065 var panel = WebInspector.ElementsPanel.instance(); | |
1066 return panel._revealProperty(/** @type {!WebInspector.CSSProperty} */ (p
roperty)); | |
1067 } | |
1068 }; | |
1069 | |
1070 /** | |
1071 * @return {!WebInspector.ElementsPanel} | |
1072 */ | |
1073 WebInspector.ElementsPanel.instance = function() | |
1074 { | |
1075 return /** @type {!WebInspector.ElementsPanel} */ (self.runtime.sharedInstan
ce(WebInspector.ElementsPanel)); | |
1076 }; | |
1077 | |
1078 /** | |
1079 * @constructor | |
1080 * @implements {WebInspector.ActionDelegate} | |
1081 */ | |
1082 WebInspector.ElementsActionDelegate = function() { }; | |
1083 | |
1084 WebInspector.ElementsActionDelegate.prototype = { | |
1085 /** | |
1086 * @override | |
1087 * @param {!WebInspector.Context} context | |
1088 * @param {string} actionId | |
1089 * @return {boolean} | |
1090 */ | |
1091 handleAction: function(context, actionId) | |
1092 { | |
1093 var node = WebInspector.context.flavor(WebInspector.DOMNode); | |
1094 if (!node) | |
1095 return true; | |
1096 var treeOutline = WebInspector.ElementsTreeOutline.forDOMModel(node.domM
odel()); | |
1097 if (!treeOutline) | |
1098 return true; | |
1099 | |
1100 switch (actionId) { | |
1101 case "elements.hide-element": | |
1102 treeOutline.toggleHideElement(node); | |
1103 return true; | |
1104 case "elements.edit-as-html": | |
1105 treeOutline.toggleEditAsHTML(node); | |
1106 return true; | |
1107 } | |
1108 return false; | |
1109 } | |
1110 }; | |
1111 | |
1112 /** | |
1113 * @constructor | |
1114 * @implements {WebInspector.DOMPresentationUtils.MarkerDecorator} | |
1115 */ | |
1116 WebInspector.ElementsPanel.PseudoStateMarkerDecorator = function() | |
1117 { | |
1118 }; | |
1119 | |
1120 WebInspector.ElementsPanel.PseudoStateMarkerDecorator.prototype = { | |
1121 /** | |
1122 * @override | |
1123 * @param {!WebInspector.DOMNode} node | |
1124 * @return {?{title: string, color: string}} | |
1125 */ | |
1126 decorate: function(node) | |
1127 { | |
1128 return { color: "orange", title: WebInspector.UIString("Element state: %
s", ":" + WebInspector.CSSModel.fromNode(node).pseudoState(node).join(", :")) }; | |
1129 } | |
1130 }; | |
OLD | NEW |