UNPKG

@dcloudio/uni-debugger

Version:

uni-app debugger

977 lines (854 loc) 32 kB
/* * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. * Copyright (C) 2008 Matt Lilek <webkit@mattlilek.com> * Copyright (C) 2009 Joseph Pecoraro * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @implements {UI.Searchable} * @implements {SDK.SDKModelObserver<!SDK.DOMModel>} * @implements {UI.ViewLocationResolver} * @unrestricted */ Elements.ElementsPanel = class extends UI.Panel { constructor() { super('elements'); this.registerRequiredCSS('elements/elementsPanel.css'); this._splitWidget = new UI.SplitWidget(true, true, 'elementsPanelSplitViewState', 325, 325); this._splitWidget.addEventListener( UI.SplitWidget.Events.SidebarSizeChanged, this._updateTreeOutlineVisibleWidth.bind(this)); this._splitWidget.show(this.element); this._searchableView = new UI.SearchableView(this); this._searchableView.setMinimumSize(25, 28); this._searchableView.setPlaceholder(Common.UIString('Find by string, selector, or XPath')); const stackElement = this._searchableView.element; this._contentElement = createElement('div'); //fixed by xxxxxx var tipsElement = createElement('div'); var tips = '此处仅支持nvue页面查看元素。vue页面需使用v3编译模式,并且在 菜单视图-显示webview调试 中寻找手机端已经启动的页面进行调试' tipsElement.setAttribute('style', 'overflow: hidden;text-overflow: ellipsis;white-space: nowrap;flex: 0 0 27px;border-bottom: 1px solid #ccc;height: 26px;line-height: 26px;margin-top: -2px;padding-left: 15px;background-color: var(--toolbar-bg-color);'); tipsElement.setAttribute('title', tips) tipsElement.innerText = tips this._contentElement.appendChild(tipsElement); const crumbsContainer = createElement('div'); stackElement.appendChild(this._contentElement); stackElement.appendChild(crumbsContainer); this._splitWidget.setMainWidget(this._searchableView); /** @type {?Elements.ElementsPanel._splitMode} */ this._splitMode = null; this._contentElement.id = 'elements-content'; // FIXME: crbug.com/425984 if (Common.moduleSetting('domWordWrap').get()) this._contentElement.classList.add('elements-wrap'); Common.moduleSetting('domWordWrap').addChangeListener(this._domWordWrapSettingChanged.bind(this)); crumbsContainer.id = 'elements-crumbs'; this._breadcrumbs = new Elements.ElementsBreadcrumbs(); this._breadcrumbs.show(crumbsContainer); this._breadcrumbs.addEventListener(Elements.ElementsBreadcrumbs.Events.NodeSelected, this._crumbNodeSelected, this); this._stylesWidget = new Elements.StylesSidebarPane(); this._computedStyleWidget = new Elements.ComputedStyleWidget(); this._metricsWidget = new Elements.MetricsSidebarPane(); Common.moduleSetting('sidebarPosition').addChangeListener(this._updateSidebarPosition.bind(this)); this._updateSidebarPosition(); /** @type {!Array.<!Elements.ElementsTreeOutline>} */ this._treeOutlines = []; /** @type {!Map<!Elements.ElementsTreeOutline, !Element>} */ this._treeOutlineHeaders = new Map(); SDK.targetManager.observeModels(SDK.DOMModel, this); SDK.targetManager.addEventListener( SDK.TargetManager.Events.NameChanged, event => this._targetNameChanged(/** @type {!SDK.Target} */ (event.data))); Common.moduleSetting('showUAShadowDOM').addChangeListener(this._showUAShadowDOMChanged.bind(this)); SDK.targetManager.addModelListener( SDK.DOMModel, SDK.DOMModel.Events.DocumentUpdated, this._documentUpdatedEvent, this); Extensions.extensionServer.addEventListener( Extensions.ExtensionServer.Events.SidebarPaneAdded, this._extensionSidebarPaneAdded, this); } /** * @return {!Elements.ElementsPanel} */ static instance() { return /** @type {!Elements.ElementsPanel} */ (self.runtime.sharedInstance(Elements.ElementsPanel)); } /** * @param {!SDK.CSSProperty} cssProperty */ _revealProperty(cssProperty) { return this.sidebarPaneView.showView(this._stylesViewToReveal).then(() => { this._stylesWidget.revealProperty(/** @type {!SDK.CSSProperty} */ (cssProperty)); }); } /** * @override * @param {string} locationName * @return {?UI.ViewLocation} */ resolveLocation(locationName) { return this.sidebarPaneView; } /** * @param {?UI.Widget} widget * @param {?UI.ToolbarToggle} toggle */ showToolbarPane(widget, toggle) { // TODO(luoe): remove this function once its providers have an alternative way to reveal their views. this._stylesWidget.showToolbarPane(widget, toggle); } /** * @override * @param {!SDK.DOMModel} domModel */ modelAdded(domModel) { const parentModel = domModel.parentModel(); let treeOutline = parentModel ? Elements.ElementsTreeOutline.forDOMModel(parentModel) : null; if (!treeOutline) { treeOutline = new Elements.ElementsTreeOutline(true, true); treeOutline.setWordWrap(Common.moduleSetting('domWordWrap').get()); treeOutline.addEventListener( Elements.ElementsTreeOutline.Events.SelectedNodeChanged, this._selectedNodeChanged, this); treeOutline.addEventListener( Elements.ElementsTreeOutline.Events.ElementsTreeUpdated, this._updateBreadcrumbIfNeeded, this); new Elements.ElementsTreeElementHighlighter(treeOutline); this._treeOutlines.push(treeOutline); if (domModel.target().parentTarget()) { this._treeOutlineHeaders.set(treeOutline, createElementWithClass('div', 'elements-tree-header')); this._targetNameChanged(domModel.target()); } } treeOutline.wireToDOMModel(domModel); // Perform attach if necessary. if (this.isShowing()) this.wasShown(); } /** * @override * @param {!SDK.DOMModel} domModel */ modelRemoved(domModel) { const treeOutline = Elements.ElementsTreeOutline.forDOMModel(domModel); treeOutline.unwireFromDOMModel(domModel); if (domModel.parentModel()) return; this._treeOutlines.remove(treeOutline); const header = this._treeOutlineHeaders.get(treeOutline); if (header) header.remove(); this._treeOutlineHeaders.delete(treeOutline); treeOutline.element.remove(); } /** * @param {!SDK.Target} target */ _targetNameChanged(target) { const domModel = target.model(SDK.DOMModel); if (!domModel) return; const treeOutline = Elements.ElementsTreeOutline.forDOMModel(domModel); if (!treeOutline) return; const header = this._treeOutlineHeaders.get(treeOutline); if (!header) return; header.removeChildren(); header.createChild('div', 'elements-tree-header-frame').textContent = Common.UIString('Frame'); header.appendChild(Components.Linkifier.linkifyURL(target.inspectedURL(), {text: target.name()})); } _updateTreeOutlineVisibleWidth() { if (!this._treeOutlines.length) return; let width = this._splitWidget.element.offsetWidth; if (this._splitWidget.isVertical()) width -= this._splitWidget.sidebarSize(); for (let i = 0; i < this._treeOutlines.length; ++i) this._treeOutlines[i].setVisibleWidth(width); this._breadcrumbs.updateSizes(); } /** * @override */ focus() { if (this._treeOutlines.length) this._treeOutlines[0].focus(); } /** * @override * @return {!UI.SearchableView} */ searchableView() { return this._searchableView; } /** * @override */ wasShown() { UI.context.setFlavor(Elements.ElementsPanel, this); for (let i = 0; i < this._treeOutlines.length; ++i) { const treeOutline = this._treeOutlines[i]; // Attach heavy component lazily if (treeOutline.element.parentElement !== this._contentElement) { const header = this._treeOutlineHeaders.get(treeOutline); if (header) this._contentElement.appendChild(header); this._contentElement.appendChild(treeOutline.element); } } super.wasShown(); this._breadcrumbs.update(); const domModels = SDK.targetManager.models(SDK.DOMModel); for (const domModel of domModels) { if (domModel.parentModel()) continue; const treeOutline = Elements.ElementsTreeOutline.forDOMModel(domModel); treeOutline.setVisible(true); if (!treeOutline.rootDOMNode) { if (domModel.existingDocument()) { treeOutline.rootDOMNode = domModel.existingDocument(); this._documentUpdated(domModel); } else { domModel.requestDocument(); } } } } /** * @override */ willHide() { SDK.OverlayModel.hideDOMNodeHighlight(); for (let i = 0; i < this._treeOutlines.length; ++i) { const treeOutline = this._treeOutlines[i]; treeOutline.setVisible(false); // Detach heavy component on hide this._contentElement.removeChild(treeOutline.element); const header = this._treeOutlineHeaders.get(treeOutline); if (header) this._contentElement.removeChild(header); } if (this._popoverHelper) this._popoverHelper.hidePopover(); super.willHide(); } /** * @override */ onResize() { this.element.window().requestAnimationFrame(this._updateSidebarPosition.bind(this)); // Do not force layout. this._updateTreeOutlineVisibleWidth(); } /** * @param {!Common.Event} event */ _selectedNodeChanged(event) { const selectedNode = /** @type {?SDK.DOMNode} */ (event.data.node); const focus = /** @type {boolean} */ (event.data.focus); for (const treeOutline of this._treeOutlines) { if (!selectedNode || Elements.ElementsTreeOutline.forDOMModel(selectedNode.domModel()) !== treeOutline) treeOutline.selectDOMNode(null); } this._breadcrumbs.setSelectedNode(selectedNode); UI.context.setFlavor(SDK.DOMNode, selectedNode); if (!selectedNode) return; selectedNode.setAsInspectedNode(); if (focus) { this._selectedNodeOnReset = selectedNode; this._hasNonDefaultSelectedNode = true; } const executionContexts = selectedNode.domModel().runtimeModel().executionContexts(); const nodeFrameId = selectedNode.frameId(); for (const context of executionContexts) { if (context.frameId === nodeFrameId) { UI.context.setFlavor(SDK.ExecutionContext, context); break; } } } /** * @param {!Common.Event} event */ _documentUpdatedEvent(event) { const domModel = /** @type {!SDK.DOMModel} */ (event.data); this._documentUpdated(domModel); } /** * @param {!SDK.DOMModel} domModel */ _documentUpdated(domModel) { this._searchableView.resetSearch(); if (!domModel.existingDocument()) { if (this.isShowing()) domModel.requestDocument(); return; } this._hasNonDefaultSelectedNode = false; if (this._omitDefaultSelection) return; const savedSelectedNodeOnReset = this._selectedNodeOnReset; restoreNode.call(this, domModel, this._selectedNodeOnReset); /** * @param {!SDK.DOMModel} domModel * @param {?SDK.DOMNode} staleNode * @this {Elements.ElementsPanel} */ async function restoreNode(domModel, staleNode) { const nodePath = staleNode ? staleNode.path() : null; const restoredNodeId = nodePath ? await domModel.pushNodeByPathToFrontend(nodePath) : null; if (savedSelectedNodeOnReset !== this._selectedNodeOnReset) return; let node = restoredNodeId ? domModel.nodeForId(restoredNodeId) : null; if (!node) { const inspectedDocument = domModel.existingDocument(); node = inspectedDocument ? inspectedDocument.body || inspectedDocument.documentElement : null; } this._setDefaultSelectedNode(node); this._lastSelectedNodeSelectedForTest(); } } _lastSelectedNodeSelectedForTest() { } /** * @param {?SDK.DOMNode} node */ _setDefaultSelectedNode(node) { if (!node || this._hasNonDefaultSelectedNode || this._pendingNodeReveal) return; const treeOutline = Elements.ElementsTreeOutline.forDOMModel(node.domModel()); if (!treeOutline) return; this.selectDOMNode(node); if (treeOutline.selectedTreeElement) treeOutline.selectedTreeElement.expand(); } /** * @override */ searchCanceled() { delete this._searchConfig; this._hideSearchHighlights(); this._searchableView.updateSearchMatchesCount(0); delete this._currentSearchResultIndex; delete this._searchResults; SDK.DOMModel.cancelSearch(); } /** * @override * @param {!UI.SearchableView.SearchConfig} searchConfig * @param {boolean} shouldJump * @param {boolean=} jumpBackwards */ performSearch(searchConfig, shouldJump, jumpBackwards) { const query = searchConfig.query; const whitespaceTrimmedQuery = query.trim(); if (!whitespaceTrimmedQuery.length) return; if (!this._searchConfig || this._searchConfig.query !== query) this.searchCanceled(); else this._hideSearchHighlights(); this._searchConfig = searchConfig; const showUAShadowDOM = Common.moduleSetting('showUAShadowDOM').get(); const domModels = SDK.targetManager.models(SDK.DOMModel); const promises = domModels.map(domModel => domModel.performSearch(whitespaceTrimmedQuery, showUAShadowDOM)); Promise.all(promises).then(resultCountCallback.bind(this)); /** * @param {!Array.<number>} resultCounts * @this {Elements.ElementsPanel} */ function resultCountCallback(resultCounts) { /** * @type {!Array.<{domModel: !SDK.DOMModel, index: number, node: (?SDK.DOMNode|undefined)}>} */ this._searchResults = []; for (let i = 0; i < resultCounts.length; ++i) { const resultCount = resultCounts[i]; for (let j = 0; j < resultCount; ++j) this._searchResults.push({domModel: domModels[i], index: j, node: undefined}); } this._searchableView.updateSearchMatchesCount(this._searchResults.length); if (!this._searchResults.length) return; if (this._currentSearchResultIndex >= this._searchResults.length) this._currentSearchResultIndex = undefined; let index = this._currentSearchResultIndex; if (shouldJump) { if (this._currentSearchResultIndex === undefined) index = jumpBackwards ? -1 : 0; else index = jumpBackwards ? index - 1 : index + 1; this._jumpToSearchResult(index); } } } _domWordWrapSettingChanged(event) { // FIXME: crbug.com/425984 this._contentElement.classList.toggle('elements-wrap', event.data); for (let i = 0; i < this._treeOutlines.length; ++i) this._treeOutlines[i].setWordWrap(/** @type {boolean} */ (event.data)); } switchToAndFocus(node) { // Reset search restore. this._searchableView.cancelSearch(); UI.viewManager.showView('elements').then(() => this.selectDOMNode(node, true)); } /** * @param {!Event} event * @return {?UI.PopoverRequest} */ _getPopoverRequest(event) { let link = event.target; while (link && !link[Elements.ElementsTreeElement.HrefSymbol]) link = link.parentElementOrShadowHost(); if (!link) return null; return { box: link.boxInWindow(), show: async popover => { const node = this.selectedDOMNode(); if (!node) return false; const preview = await BrowserComponents.ImagePreview.build( node.domModel().target(), link[Elements.ElementsTreeElement.HrefSymbol], true); if (preview) popover.contentElement.appendChild(preview); return !!preview; } }; } _jumpToSearchResult(index) { this._currentSearchResultIndex = (index + this._searchResults.length) % this._searchResults.length; this._highlightCurrentSearchResult(); } /** * @override */ jumpToNextSearchResult() { if (!this._searchResults) return; this.performSearch(this._searchConfig, true); } /** * @override */ jumpToPreviousSearchResult() { if (!this._searchResults) return; this.performSearch(this._searchConfig, true, true); } /** * @override * @return {boolean} */ supportsCaseSensitiveSearch() { return false; } /** * @override * @return {boolean} */ supportsRegexSearch() { return false; } _highlightCurrentSearchResult() { const index = this._currentSearchResultIndex; const searchResults = this._searchResults; const searchResult = searchResults[index]; this._searchableView.updateCurrentMatchIndex(index); if (searchResult.node === null) return; if (typeof searchResult.node === 'undefined') { // No data for slot, request it. searchResult.domModel.searchResult(searchResult.index).then(node => { searchResult.node = node; this._highlightCurrentSearchResult(); }); return; } const treeElement = this._treeElementForNode(searchResult.node); searchResult.node.scrollIntoView(); if (treeElement) { treeElement.highlightSearchResults(this._searchConfig.query); treeElement.reveal(); const matches = treeElement.listItemElement.getElementsByClassName(UI.highlightedSearchResultClassName); if (matches.length) matches[0].scrollIntoViewIfNeeded(false); } } _hideSearchHighlights() { if (!this._searchResults || !this._searchResults.length || this._currentSearchResultIndex === undefined) return; const searchResult = this._searchResults[this._currentSearchResultIndex]; if (!searchResult.node) return; const treeOutline = Elements.ElementsTreeOutline.forDOMModel(searchResult.node.domModel()); const treeElement = treeOutline.findTreeElement(searchResult.node); if (treeElement) treeElement.hideSearchHighlights(); } /** * @return {?SDK.DOMNode} */ selectedDOMNode() { for (let i = 0; i < this._treeOutlines.length; ++i) { const treeOutline = this._treeOutlines[i]; if (treeOutline.selectedDOMNode()) return treeOutline.selectedDOMNode(); } return null; } /** * @param {!SDK.DOMNode} node * @param {boolean=} focus */ selectDOMNode(node, focus) { for (const treeOutline of this._treeOutlines) { const outline = Elements.ElementsTreeOutline.forDOMModel(node.domModel()); if (outline === treeOutline) treeOutline.selectDOMNode(node, focus); else treeOutline.selectDOMNode(null); } } /** * @param {!Common.Event} event */ _updateBreadcrumbIfNeeded(event) { const nodes = /** @type {!Array.<!SDK.DOMNode>} */ (event.data); this._breadcrumbs.updateNodes(nodes); } /** * @param {!Common.Event} event */ _crumbNodeSelected(event) { const node = /** @type {!SDK.DOMNode} */ (event.data); this.selectDOMNode(node, true); } /** * @param {?SDK.DOMNode} node * @return {?Elements.ElementsTreeOutline} */ _treeOutlineForNode(node) { if (!node) return null; return Elements.ElementsTreeOutline.forDOMModel(node.domModel()); } /** * @param {!SDK.DOMNode} node * @return {?Elements.ElementsTreeElement} */ _treeElementForNode(node) { const treeOutline = this._treeOutlineForNode(node); return /** @type {?Elements.ElementsTreeElement} */ (treeOutline.findTreeElement(node)); } /** * @param {!SDK.DOMNode} node * @return {!SDK.DOMNode} */ _leaveUserAgentShadowDOM(node) { let userAgentShadowRoot; while ((userAgentShadowRoot = node.ancestorUserAgentShadowRoot()) && userAgentShadowRoot.parentNode) node = userAgentShadowRoot.parentNode; return node; } /** * @param {!SDK.DOMNode} node * @param {boolean} focus * @return {!Promise} */ revealAndSelectNode(node, focus) { if (Elements.inspectElementModeController && Elements.inspectElementModeController.isInInspectElementMode()) Elements.inspectElementModeController.stopInspection(); this._omitDefaultSelection = true; node = Common.moduleSetting('showUAShadowDOM').get() ? node : this._leaveUserAgentShadowDOM(node); node.highlightForTwoSeconds(); return UI.viewManager.showView('elements', false, !focus).then(() => { this.selectDOMNode(node, focus); delete this._omitDefaultSelection; if (!this._notFirstInspectElement) { Elements.ElementsPanel._firstInspectElementNodeNameForTest = node.nodeName(); Elements.ElementsPanel._firstInspectElementCompletedForTest(); InspectorFrontendHost.inspectElementCompleted(); } this._notFirstInspectElement = true; }); } _showUAShadowDOMChanged() { for (let i = 0; i < this._treeOutlines.length; ++i) this._treeOutlines[i].update(); } _updateSidebarPosition() { if (this.sidebarPaneView && this.sidebarPaneView.tabbedPane().shouldHideOnDetach()) return; // We can't reparent extension iframes. let splitMode; const position = Common.moduleSetting('sidebarPosition').get(); if (position === 'right' || (position === 'auto' && UI.inspectorView.element.offsetWidth > 680)) splitMode = Elements.ElementsPanel._splitMode.Vertical; else if (UI.inspectorView.element.offsetWidth > 415) splitMode = Elements.ElementsPanel._splitMode.Horizontal; else splitMode = Elements.ElementsPanel._splitMode.Slim; if (this.sidebarPaneView && splitMode === this._splitMode) return; this._splitMode = splitMode; const extensionSidebarPanes = Extensions.extensionServer.sidebarPanes(); let lastSelectedTabId = null; if (this.sidebarPaneView) { lastSelectedTabId = this.sidebarPaneView.tabbedPane().selectedTabId; this.sidebarPaneView.tabbedPane().detach(); this._splitWidget.uninstallResizer(this.sidebarPaneView.tabbedPane().headerElement()); } this._splitWidget.setVertical(this._splitMode === Elements.ElementsPanel._splitMode.Vertical); this.showToolbarPane(null /* widget */, null /* toggle */); const matchedStylePanesWrapper = new UI.VBox(); matchedStylePanesWrapper.element.classList.add('style-panes-wrapper'); this._stylesWidget.show(matchedStylePanesWrapper.element); const computedStylePanesWrapper = new UI.VBox(); computedStylePanesWrapper.element.classList.add('style-panes-wrapper'); this._computedStyleWidget.show(computedStylePanesWrapper.element); /** * @param {boolean} inComputedStyle * @this {Elements.ElementsPanel} */ function showMetrics(inComputedStyle) { if (inComputedStyle) this._metricsWidget.show(computedStylePanesWrapper.element, this._computedStyleWidget.element); else this._metricsWidget.show(matchedStylePanesWrapper.element); } /** * @param {!Common.Event} event * @this {Elements.ElementsPanel} */ function tabSelected(event) { const tabId = /** @type {string} */ (event.data.tabId); if (tabId === Common.UIString('Computed')) showMetrics.call(this, true); else if (tabId === Common.UIString('Styles')) showMetrics.call(this, false); } this.sidebarPaneView = UI.viewManager.createTabbedLocation(() => UI.viewManager.showView('elements')); const tabbedPane = this.sidebarPaneView.tabbedPane(); if (this._popoverHelper) this._popoverHelper.hidePopover(); this._popoverHelper = new UI.PopoverHelper(tabbedPane.element, this._getPopoverRequest.bind(this)); this._popoverHelper.setHasPadding(true); this._popoverHelper.setTimeout(0); if (this._splitMode !== Elements.ElementsPanel._splitMode.Vertical) this._splitWidget.installResizer(tabbedPane.headerElement()); const stylesView = new UI.SimpleView(Common.UIString('Styles')); this.sidebarPaneView.appendView(stylesView); if (splitMode === Elements.ElementsPanel._splitMode.Horizontal) { // Styles and computed are merged into a single tab. stylesView.element.classList.add('flex-auto'); const splitWidget = new UI.SplitWidget(true, true, 'stylesPaneSplitViewState', 215); splitWidget.show(stylesView.element); splitWidget.setMainWidget(matchedStylePanesWrapper); splitWidget.setSidebarWidget(computedStylePanesWrapper); } else { // Styles and computed are in separate tabs. stylesView.element.classList.add('flex-auto'); matchedStylePanesWrapper.show(stylesView.element); const computedView = new UI.SimpleView(Common.UIString('Computed')); computedView.element.classList.add('composite', 'fill'); computedStylePanesWrapper.show(computedView.element); tabbedPane.addEventListener(UI.TabbedPane.Events.TabSelected, tabSelected, this); this.sidebarPaneView.appendView(computedView); } this._stylesViewToReveal = stylesView; showMetrics.call(this, this._splitMode === Elements.ElementsPanel._splitMode.Horizontal); this.sidebarPaneView.appendApplicableItems('elements-sidebar'); for (let i = 0; i < extensionSidebarPanes.length; ++i) this._addExtensionSidebarPane(extensionSidebarPanes[i]); if (lastSelectedTabId) this.sidebarPaneView.tabbedPane().selectTab(lastSelectedTabId); this._splitWidget.setSidebarWidget(this.sidebarPaneView.tabbedPane()); } /** * @param {!Common.Event} event */ _extensionSidebarPaneAdded(event) { const pane = /** @type {!Extensions.ExtensionSidebarPane} */ (event.data); this._addExtensionSidebarPane(pane); } /** * @param {!Extensions.ExtensionSidebarPane} pane */ _addExtensionSidebarPane(pane) { if (pane.panelName() === this.name) this.sidebarPaneView.appendView(pane); } }; Elements.ElementsPanel._elementsSidebarViewTitleSymbol = Symbol('title'); /** @enum {symbol} */ Elements.ElementsPanel._splitMode = { Vertical: Symbol('Vertical'), Horizontal: Symbol('Horizontal'), Slim: Symbol('Slim'), }; // Sniffed in tests. Elements.ElementsPanel._firstInspectElementCompletedForTest = function() {}; /** * @implements {UI.ContextMenu.Provider} * @unrestricted */ Elements.ElementsPanel.ContextMenuProvider = class { /** * @override * @param {!Event} event * @param {!UI.ContextMenu} contextMenu * @param {!Object} object */ appendApplicableItems(event, contextMenu, object) { if (!(object instanceof SDK.RemoteObject && (/** @type {!SDK.RemoteObject} */ (object)).isNode()) && !(object instanceof SDK.DOMNode) && !(object instanceof SDK.DeferredDOMNode)) return; // Skip adding "Reveal..." menu item for our own tree outline. if (Elements.ElementsPanel.instance().element.isAncestor(/** @type {!Node} */ (event.target))) return; const commandCallback = Common.Revealer.reveal.bind(Common.Revealer, object); contextMenu.revealSection().appendItem(Common.UIString('Reveal in Elements panel'), commandCallback); } }; /** * @implements {Common.Revealer} * @unrestricted */ Elements.ElementsPanel.DOMNodeRevealer = class { /** * @override * @param {!Object} node * @param {boolean=} omitFocus * @return {!Promise} */ reveal(node, omitFocus) { const panel = Elements.ElementsPanel.instance(); panel._pendingNodeReveal = true; return new Promise(revealPromise); /** * @param {function(undefined)} resolve * @param {function(!Error)} reject */ function revealPromise(resolve, reject) { if (node instanceof SDK.DOMNode) { onNodeResolved(/** @type {!SDK.DOMNode} */ (node)); } else if (node instanceof SDK.DeferredDOMNode) { (/** @type {!SDK.DeferredDOMNode} */ (node)).resolve(onNodeResolved); } else if (node instanceof SDK.RemoteObject) { const domModel = /** @type {!SDK.RemoteObject} */ (node).runtimeModel().target().model(SDK.DOMModel); if (domModel) domModel.pushObjectAsNodeToFrontend(node).then(onNodeResolved); else reject(new Error('Could not resolve a node to reveal.')); } else { reject(new Error('Can\'t reveal a non-node.')); panel._pendingNodeReveal = false; } /** * @param {?SDK.DOMNode} resolvedNode */ function onNodeResolved(resolvedNode) { panel._pendingNodeReveal = false; if (resolvedNode) { panel.revealAndSelectNode(resolvedNode, !omitFocus).then(resolve); return; } reject(new Error('Could not resolve node to reveal.')); } } } }; /** * @implements {Common.Revealer} * @unrestricted */ Elements.ElementsPanel.CSSPropertyRevealer = class { /** * @override * @param {!Object} property * @return {!Promise} */ reveal(property) { const panel = Elements.ElementsPanel.instance(); return panel._revealProperty(/** @type {!SDK.CSSProperty} */ (property)); } }; /** * @implements {UI.ActionDelegate} * @unrestricted */ Elements.ElementsActionDelegate = class { /** * @override * @param {!UI.Context} context * @param {string} actionId * @return {boolean} */ handleAction(context, actionId) { const node = UI.context.flavor(SDK.DOMNode); if (!node) return true; const treeOutline = Elements.ElementsTreeOutline.forDOMModel(node.domModel()); if (!treeOutline) return true; switch (actionId) { case 'elements.hide-element': treeOutline.toggleHideElement(node); return true; case 'elements.edit-as-html': treeOutline.toggleEditAsHTML(node); return true; case 'elements.undo': if (UI.isEditing()) return false; SDK.domModelUndoStack.undo(); Elements.ElementsPanel.instance()._stylesWidget.forceUpdate(); return true; case 'elements.redo': if (UI.isEditing()) return false; SDK.domModelUndoStack.redo(); Elements.ElementsPanel.instance()._stylesWidget.forceUpdate(); return true; } return false; } }; /** * @implements {Elements.MarkerDecorator} * @unrestricted */ Elements.ElementsPanel.PseudoStateMarkerDecorator = class { /** * @override * @param {!SDK.DOMNode} node * @return {?{title: string, color: string}} */ decorate(node) { return { color: 'orange', title: Common.UIString('Element state: %s', ':' + node.domModel().cssModel().pseudoState(node).join(', :')) }; } };