123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430 |
- /*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
- *
- * 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.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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.
- */
- WebInspector.DOMTreeContentView = function(domTree)
- {
- console.assert(domTree);
- WebInspector.ContentView.call(this, domTree);
- // The navigation item for the compositing borders button.
- this._compositingBordersButtonNavigationItem = new WebInspector.ActivateButtonNavigationItem("layer-borders", WebInspector.UIString("Show compositing borders"), WebInspector.UIString("Hide compositing borders"), "Images/LayerBorders.pdf", 16, 16);
- this._compositingBordersButtonNavigationItem.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, this._toggleCompositingBorders, this);
- this._compositingBordersButtonNavigationItem.enabled = !!PageAgent.getCompositingBordersVisible;
- // The navigation item for the shadow tree toggle button.
- WebInspector.showShadowDOMSetting.addEventListener(WebInspector.Setting.Event.Changed, this._showShadowDOMSettingChanged, this);
- this._showsShadowDOMButtonNavigationItem = new WebInspector.ActivateButtonNavigationItem("shows-shadow-DOM", WebInspector.UIString("Show shadow DOM nodes"), WebInspector.UIString("Hide shadow DOM nodes"), "Images/ShadowDOM.pdf", 16, 16);
- this._showsShadowDOMButtonNavigationItem.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, this._toggleShowsShadowDOMSetting, this);
- this._showShadowDOMSettingChanged();
- this.element.classList.add(WebInspector.DOMTreeContentView.StyleClassName);
- this.element.addEventListener("click", this._mouseWasClicked.bind(this), false);
- this._domTree = domTree;
- this._domTree.addEventListener(WebInspector.DOMTree.Event.RootDOMNodeInvalidated, this._rootDOMNodeInvalidated, this);
- this._domTreeOutline = new WebInspector.DOMTreeOutline(true, true, false);
- this._domTreeOutline.addEventListener(WebInspector.DOMTreeOutline.Event.SelectedNodeChanged, this._selectedNodeDidChange, this);
- this._domTreeOutline.wireToDomAgent();
- this.element.appendChild(this._domTreeOutline.element);
- WebInspector.domTreeManager.addEventListener(WebInspector.DOMTreeManager.Event.AttributeModified, this._domNodeChanged, this);
- WebInspector.domTreeManager.addEventListener(WebInspector.DOMTreeManager.Event.AttributeRemoved, this._domNodeChanged, this);
- WebInspector.domTreeManager.addEventListener(WebInspector.DOMTreeManager.Event.CharacterDataModified, this._domNodeChanged, this);
- this._lastSelectedNodePathSetting = new WebInspector.Setting("last-selected-node-path", null);
- this._numberOfSearchResults = null;
- this._requestRootDOMNode();
- };
- WebInspector.DOMTreeContentView.StyleClassName = "dom-tree";
- WebInspector.DOMTreeContentView.prototype = {
- constructor: WebInspector.DOMTreeContentView,
- // Public
- get navigationItems()
- {
- return [this._showsShadowDOMButtonNavigationItem, this._compositingBordersButtonNavigationItem];
- },
- get domTree()
- {
- return this._domTree;
- },
- get scrollableElements()
- {
- return [this.element];
- },
- updateLayout: function()
- {
- this._domTreeOutline.updateSelection();
- },
- shown: function()
- {
- this._domTreeOutline.setVisible(true, WebInspector.isConsoleFocused());
- this._updateCompositingBordersButtonToMatchPageSettings();
- },
- hidden: function()
- {
- WebInspector.domTreeManager.hideDOMNodeHighlight();
- this._domTreeOutline.setVisible(false);
- },
- closed: function()
- {
- this._domTree.removeEventListener(null, null, this);
- WebInspector.domTreeManager.removeEventListener(null, null, this);
- this._domTreeOutline.close();
- },
- get selectionPathComponents()
- {
- var treeElement = this._domTreeOutline.selectedTreeElement;
- var pathComponents = [];
- while (treeElement && !treeElement.root) {
- // The close tag is contained within the element it closes. So skip it since we don't want to
- // show the same node twice in the hierarchy.
- if (treeElement.isCloseTag()) {
- treeElement = treeElement.parent;
- continue;
- }
- var pathComponent = new WebInspector.DOMTreeElementPathComponent(treeElement, treeElement.representedObject);
- pathComponent.addEventListener(WebInspector.HierarchicalPathComponent.Event.SiblingWasSelected, this._pathComponentSelected, this);
- pathComponents.unshift(pathComponent);
- treeElement = treeElement.parent;
- }
- return pathComponents;
- },
- selectAndRevealDOMNode: function(domNode, preventFocusChange)
- {
- this._domTreeOutline.selectDOMNode(domNode, !preventFocusChange);
- },
- handleCopyEvent: function(event)
- {
- var selectedDOMNode = this._domTreeOutline.selectedDOMNode();
- if (!selectedDOMNode)
- return;
- event.clipboardData.clearData();
- event.preventDefault();
- selectedDOMNode.copyNode();
- },
- get supportsSearch()
- {
- return true;
- },
- get numberOfSearchResults()
- {
- return this._numberOfSearchResults;
- },
- get hasPerformedSearch()
- {
- return this._numberOfSearchResults !== null;
- },
- set automaticallyRevealFirstSearchResult(reveal)
- {
- this._automaticallyRevealFirstSearchResult = reveal;
- // If we haven't shown a search result yet, reveal one now.
- if (this._automaticallyRevealFirstSearchResult && this._numberOfSearchResults > 0) {
- if (this._currentSearchResultIndex === -1)
- this.revealNextSearchResult();
- }
- },
- performSearch: function(query)
- {
- if (this._searchQuery === query)
- return;
- if (this._searchIdentifier)
- DOMAgent.discardSearchResults(this._searchIdentifier);
- this._searchQuery = query;
- this._searchIdentifier = null;
- this._numberOfSearchResults = null;
- this._currentSearchResultIndex = -1;
- function searchResultsReady(error, searchIdentifier, resultsCount)
- {
- if (error)
- return;
- this._searchIdentifier = searchIdentifier;
- this._numberOfSearchResults = resultsCount;
- this.dispatchEventToListeners(WebInspector.ContentView.Event.NumberOfSearchResultsDidChange);
- if (this._automaticallyRevealFirstSearchResult)
- this.revealNextSearchResult();
- }
- DOMAgent.performSearch(query, searchResultsReady.bind(this));
- },
- searchCleared: function()
- {
- if (this._searchIdentifier)
- DOMAgent.discardSearchResults(this._searchIdentifier);
- this._searchQuery = null;
- this._searchIdentifier = null;
- this._numberOfSearchResults = null;
- this._currentSearchResultIndex = -1;
- },
- revealPreviousSearchResult: function(changeFocus)
- {
- if (!this._numberOfSearchResults)
- return;
- if (this._currentSearchResultIndex > 0)
- --this._currentSearchResultIndex;
- else
- this._currentSearchResultIndex = this._numberOfSearchResults - 1;
- this._revealSearchResult(this._currentSearchResultIndex, changeFocus);
- },
- revealNextSearchResult: function(changeFocus)
- {
- if (!this._numberOfSearchResults)
- return;
- if (this._currentSearchResultIndex + 1 < this._numberOfSearchResults)
- ++this._currentSearchResultIndex;
- else
- this._currentSearchResultIndex = 0;
- this._revealSearchResult(this._currentSearchResultIndex, changeFocus);
- },
- // Private
- _revealSearchResult: function(index, changeFocus)
- {
- console.assert(this._searchIdentifier);
- var searchIdentifier = this._searchIdentifier;
- function revealResult(error, nodeIdentifiers)
- {
- if (error)
- return;
- // Bail if the searchIdentifier changed since we started.
- if (this._searchIdentifier !== searchIdentifier)
- return;
- console.assert(nodeIdentifiers.length === 1);
- var domNode = WebInspector.domTreeManager.nodeForId(nodeIdentifiers[0]);
- console.assert(domNode);
- if (!domNode)
- return;
- this._domTreeOutline.selectDOMNode(domNode, changeFocus);
- }
- DOMAgent.getSearchResults(this._searchIdentifier, index, index + 1, revealResult.bind(this));
- },
- _rootDOMNodeAvailable: function(rootDOMNode)
- {
- this._domTreeOutline.rootDOMNode = rootDOMNode;
- if (!rootDOMNode) {
- this._domTreeOutline.selectDOMNode(null, false);
- return;
- }
- function selectNode(lastSelectedNode)
- {
- // A selection was made while waiting for the async reply. Just bail now.
- if (this._domTreeOutline.selectedTreeElement)
- return;
- var nodeToFocus = lastSelectedNode;
- if (!nodeToFocus)
- nodeToFocus = rootDOMNode.body || rootDOMNode.documentElement;
- if (!nodeToFocus)
- return;
- this._dontSetLastSelectedNodePath = true;
- this.selectAndRevealDOMNode(nodeToFocus, WebInspector.isConsoleFocused());
- this._dontSetLastSelectedNodePath = false;
- // If this wasn't the last selected node, then expand it.
- if (!lastSelectedNode && this._domTreeOutline.selectedTreeElement)
- this._domTreeOutline.selectedTreeElement.expand();
- }
- function selectLastSelectedNode(nodeId)
- {
- selectNode.call(this, WebInspector.domTreeManager.nodeForId(nodeId));
- }
- if (this._lastSelectedNodePathSetting.value && this._lastSelectedNodePathSetting.value.path && this._lastSelectedNodePathSetting.value.url === this._domTree.frame.url.hash)
- WebInspector.domTreeManager.pushNodeByPathToFrontend(this._lastSelectedNodePathSetting.value.path, selectLastSelectedNode.bind(this));
- else
- selectNode.call(this);
- },
- _rootDOMNodeInvalidated: function(event)
- {
- this._requestRootDOMNode();
- },
- _requestRootDOMNode: function()
- {
- this._domTree.requestRootDOMNode(this._rootDOMNodeAvailable.bind(this));
- },
- _selectedNodeDidChange: function(event)
- {
- var selectedDOMNode = this._domTreeOutline.selectedDOMNode();
- if (selectedDOMNode && !this._dontSetLastSelectedNodePath)
- this._lastSelectedNodePathSetting.value = {url: this._domTree.frame.url.hash, path: selectedDOMNode.path()};
- if (selectedDOMNode)
- ConsoleAgent.addInspectedNode(selectedDOMNode.id);
- this.dispatchEventToListeners(WebInspector.ContentView.Event.SelectionPathComponentsDidChange);
- },
- _pathComponentSelected: function(event)
- {
- console.assert(event.data.pathComponent instanceof WebInspector.DOMTreeElementPathComponent);
- console.assert(event.data.pathComponent.domTreeElement instanceof WebInspector.DOMTreeElement);
- this._domTreeOutline.selectDOMNode(event.data.pathComponent.domTreeElement.representedObject, true);
- },
- _domNodeChanged: function(event)
- {
- var selectedDOMNode = this._domTreeOutline.selectedDOMNode();
- if (selectedDOMNode !== event.data.node)
- return;
- this.dispatchEventToListeners(WebInspector.ContentView.Event.SelectionPathComponentsDidChange);
- },
- _mouseWasClicked: function(event)
- {
- var anchorElement = event.target.enclosingNodeOrSelfWithNodeName("a");
- if (!anchorElement || !anchorElement.href)
- return;
- // Prevent the link from navigating, since we don't do any navigation by following links normally.
- event.preventDefault();
- event.stopPropagation();
- if (WebInspector.isBeingEdited(anchorElement)) {
- // Don't follow the link when it is being edited.
- return;
- }
- // Cancel any pending link navigation.
- if (this._followLinkTimeoutIdentifier) {
- clearTimeout(this._followLinkTimeoutIdentifier);
- delete this._followLinkTimeoutIdentifier;
- }
- // If this is a double-click (or multiple-click), return early.
- if (event.detail > 1)
- return;
- function followLink()
- {
- // Since followLink is delayed, the call to WebInspector.openURL can't look at window.event
- // to see if the command key is down like it normally would. So we need to do that check
- // before calling WebInspector.openURL.
- var alwaysOpenExternally = event ? event.metaKey : false;
- WebInspector.openURL(anchorElement.href, this._frame, alwaysOpenExternally, anchorElement.lineNumber);
- }
- // Start a timeout since this is a single click, if the timeout is canceled before it fires,
- // then a double-click happened or another link was clicked.
- // FIXME: The duration might be longer or shorter than the user's configured double click speed.
- this._followLinkTimeoutIdentifier = setTimeout(followLink.bind(this), 333);
- },
- _toggleCompositingBorders: function(event)
- {
- console.assert(PageAgent.setCompositingBordersVisible);
- var activated = !this._compositingBordersButtonNavigationItem.activated;
- this._compositingBordersButtonNavigationItem.activated = activated;
- PageAgent.setCompositingBordersVisible(activated);
- },
-
- _updateCompositingBordersButtonToMatchPageSettings: function()
- {
- if (!PageAgent.getCompositingBordersVisible)
- return;
- var button = this._compositingBordersButtonNavigationItem;
- // We need to sync with the page settings since these can be controlled
- // in a different way than just using the navigation bar button.
- PageAgent.getCompositingBordersVisible(function(error, compositingBordersVisible) {
- button.activated = error ? false : compositingBordersVisible;
- });
- },
- _showShadowDOMSettingChanged: function(event)
- {
- this._showsShadowDOMButtonNavigationItem.activated = WebInspector.showShadowDOMSetting.value;
- },
- _toggleShowsShadowDOMSetting: function(event)
- {
- WebInspector.showShadowDOMSetting.value = !WebInspector.showShadowDOMSetting.value;
- }
- };
- WebInspector.DOMTreeContentView.prototype.__proto__ = WebInspector.ContentView.prototype;
|