12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172 |
- /*
- * 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.
- */
- importScript("CSSNamedFlowCollectionsView.js");
- importScript("CSSNamedFlowView.js");
- importScript("EventListenersSidebarPane.js");
- importScript("MetricsSidebarPane.js");
- importScript("PropertiesSidebarPane.js");
- importScript("StylesSidebarPane.js");
- /**
- * @constructor
- * @extends {WebInspector.Panel}
- */
- WebInspector.ElementsPanel = function()
- {
- WebInspector.Panel.call(this, "elements");
- this.registerRequiredCSS("breadcrumbList.css");
- this.registerRequiredCSS("elementsPanel.css");
- this.registerRequiredCSS("textPrompt.css");
- this.setHideOnDetach();
- const initialSidebarWidth = 325;
- const minimumContentWidthPercent = 34;
- const initialSidebarHeight = 325;
- const minimumContentHeightPercent = 34;
- this.createSidebarView(this.element, WebInspector.SidebarView.SidebarPosition.End, initialSidebarWidth, initialSidebarHeight);
- this.splitView.setMinimumSidebarWidth(Preferences.minElementsSidebarWidth);
- this.splitView.setMinimumMainWidthPercent(minimumContentWidthPercent);
- this.splitView.setMinimumSidebarHeight(Preferences.minElementsSidebarHeight);
- this.splitView.setMinimumMainHeightPercent(minimumContentHeightPercent);
- this.contentElement = this.splitView.mainElement;
- this.contentElement.id = "elements-content";
- this.contentElement.addStyleClass("outline-disclosure");
- this.contentElement.addStyleClass("source-code");
- if (!WebInspector.settings.domWordWrap.get())
- this.contentElement.classList.add("nowrap");
- WebInspector.settings.domWordWrap.addChangeListener(this._domWordWrapSettingChanged.bind(this));
- this.contentElement.addEventListener("contextmenu", this._contextMenuEventFired.bind(this), true);
- this.splitView.sidebarElement.addEventListener("contextmenu", this._sidebarContextMenuEventFired.bind(this), false);
- this.treeOutline = new WebInspector.ElementsTreeOutline(true, true, false, this._populateContextMenu.bind(this), this._setPseudoClassForNodeId.bind(this));
- this.treeOutline.wireToDomAgent();
- this.treeOutline.addEventListener(WebInspector.ElementsTreeOutline.Events.SelectedNodeChanged, this._selectedNodeChanged, this);
- this.crumbsElement = document.createElement("div");
- this.crumbsElement.className = "crumbs";
- this.crumbsElement.addEventListener("mousemove", this._mouseMovedInCrumbs.bind(this), false);
- this.crumbsElement.addEventListener("mouseout", this._mouseMovedOutOfCrumbs.bind(this), false);
- this.sidebarPanes = {};
- this.sidebarPanes.computedStyle = new WebInspector.ComputedStyleSidebarPane();
- this.sidebarPanes.styles = new WebInspector.StylesSidebarPane(this.sidebarPanes.computedStyle, this._setPseudoClassForNodeId.bind(this));
- this.sidebarPanes.metrics = new WebInspector.MetricsSidebarPane();
- this.sidebarPanes.properties = new WebInspector.PropertiesSidebarPane();
- this.sidebarPanes.domBreakpoints = WebInspector.domBreakpointsSidebarPane.createProxy(this);
- this.sidebarPanes.eventListeners = new WebInspector.EventListenersSidebarPane();
- this.sidebarPanes.styles.addEventListener(WebInspector.SidebarPane.EventTypes.wasShown, this.updateStyles.bind(this, false));
- this.sidebarPanes.metrics.addEventListener(WebInspector.SidebarPane.EventTypes.wasShown, this.updateMetrics.bind(this));
- this.sidebarPanes.properties.addEventListener(WebInspector.SidebarPane.EventTypes.wasShown, this.updateProperties.bind(this));
- this.sidebarPanes.eventListeners.addEventListener(WebInspector.SidebarPane.EventTypes.wasShown, this.updateEventListeners.bind(this));
- this.sidebarPanes.styles.addEventListener("style edited", this._stylesPaneEdited, this);
- this.sidebarPanes.styles.addEventListener("style property toggled", this._stylesPaneEdited, this);
- this.sidebarPanes.metrics.addEventListener("metrics edited", this._metricsPaneEdited, this);
- WebInspector.dockController.addEventListener(WebInspector.DockController.Events.DockSideChanged, this._dockSideChanged.bind(this));
- WebInspector.settings.splitVerticallyWhenDockedToRight.addChangeListener(this._dockSideChanged.bind(this));
- this._dockSideChanged();
- this._popoverHelper = new WebInspector.PopoverHelper(this.element, this._getPopoverAnchor.bind(this), this._showPopover.bind(this));
- this._popoverHelper.setTimeout(0);
- WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.AttrModified, this._updateBreadcrumbIfNeeded, this);
- WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.AttrRemoved, this._updateBreadcrumbIfNeeded, this);
- WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.NodeRemoved, this._nodeRemoved, this);
- WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.DocumentUpdated, this._documentUpdatedEvent, this);
- WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.InspectElementRequested, this._inspectElementRequested, this);
- if (WebInspector.domAgent.existingDocument())
- this._documentUpdated(WebInspector.domAgent.existingDocument());
- }
- WebInspector.ElementsPanel.prototype = {
- statusBarItems: function()
- {
- return [this.crumbsElement];
- },
- defaultFocusedElement: function()
- {
- return this.treeOutline.element;
- },
- statusBarResized: function()
- {
- this.updateBreadcrumbSizes();
- },
- wasShown: function()
- {
- // Attach heavy component lazily
- if (this.treeOutline.element.parentElement !== this.contentElement)
- this.contentElement.appendChild(this.treeOutline.element);
- WebInspector.Panel.prototype.wasShown.call(this);
- this.updateBreadcrumb();
- this.treeOutline.updateSelection();
- this.treeOutline.setVisible(true);
- if (!this.treeOutline.rootDOMNode)
- WebInspector.domAgent.requestDocument();
- },
- willHide: function()
- {
- WebInspector.domAgent.hideDOMNodeHighlight();
- this.treeOutline.setVisible(false);
- this._popoverHelper.hidePopover();
- // Detach heavy component on hide
- this.contentElement.removeChild(this.treeOutline.element);
- WebInspector.Panel.prototype.willHide.call(this);
- },
- onResize: function()
- {
- this.treeOutline.updateSelection();
- this.updateBreadcrumbSizes();
- },
- /**
- * @param {DOMAgent.NodeId} nodeId
- * @param {string} pseudoClass
- * @param {boolean} enable
- */
- _setPseudoClassForNodeId: function(nodeId, pseudoClass, enable)
- {
- var node = WebInspector.domAgent.nodeForId(nodeId);
- if (!node)
- return;
- var pseudoClasses = node.getUserProperty(WebInspector.ElementsTreeOutline.PseudoStateDecorator.PropertyName);
- if (enable) {
- pseudoClasses = pseudoClasses || [];
- if (pseudoClasses.indexOf(pseudoClass) >= 0)
- return;
- pseudoClasses.push(pseudoClass);
- node.setUserProperty(WebInspector.ElementsTreeOutline.PseudoStateDecorator.PropertyName, pseudoClasses);
- } else {
- if (!pseudoClasses || pseudoClasses.indexOf(pseudoClass) < 0)
- return;
- pseudoClasses.remove(pseudoClass);
- if (!pseudoClasses.length)
- node.removeUserProperty(WebInspector.ElementsTreeOutline.PseudoStateDecorator.PropertyName);
- }
- this.treeOutline.updateOpenCloseTags(node);
- WebInspector.cssModel.forcePseudoState(node.id, node.getUserProperty(WebInspector.ElementsTreeOutline.PseudoStateDecorator.PropertyName));
- this._metricsPaneEdited();
- this._stylesPaneEdited();
- WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
- action: WebInspector.UserMetrics.UserActionNames.ForcedElementState,
- selector: node.appropriateSelectorFor(false),
- enabled: enable,
- state: pseudoClass
- });
- },
- _selectedNodeChanged: function()
- {
- var selectedNode = this.selectedDOMNode();
- if (!selectedNode && this._lastValidSelectedNode)
- this._selectedPathOnReset = this._lastValidSelectedNode.path();
- this.updateBreadcrumb(false);
- this._updateSidebars();
- if (selectedNode) {
- ConsoleAgent.addInspectedNode(selectedNode.id);
- this._lastValidSelectedNode = selectedNode;
- }
- WebInspector.notifications.dispatchEventToListeners(WebInspector.ElementsTreeOutline.Events.SelectedNodeChanged);
- },
- _updateSidebars: function()
- {
- for (var pane in this.sidebarPanes)
- this.sidebarPanes[pane].needsUpdate = true;
- this.updateStyles(true);
- this.updateMetrics();
- this.updateProperties();
- this.updateEventListeners();
- },
- _reset: function()
- {
- delete this.currentQuery;
- },
- _documentUpdatedEvent: function(event)
- {
- this._documentUpdated(event.data);
- },
- _documentUpdated: function(inspectedRootDocument)
- {
- this._reset();
- this.searchCanceled();
- this.treeOutline.rootDOMNode = inspectedRootDocument;
- if (!inspectedRootDocument) {
- if (this.isShowing())
- WebInspector.domAgent.requestDocument();
- return;
- }
- WebInspector.domBreakpointsSidebarPane.restoreBreakpoints();
- /**
- * @this {WebInspector.ElementsPanel}
- * @param {WebInspector.DOMNode=} candidateFocusNode
- */
- function selectNode(candidateFocusNode)
- {
- if (!candidateFocusNode)
- candidateFocusNode = inspectedRootDocument.body || inspectedRootDocument.documentElement;
- if (!candidateFocusNode)
- return;
- this.selectDOMNode(candidateFocusNode);
- if (this.treeOutline.selectedTreeElement)
- this.treeOutline.selectedTreeElement.expand();
- }
- function selectLastSelectedNode(nodeId)
- {
- if (this.selectedDOMNode()) {
- // Focused node has been explicitly set while reaching out for the last selected node.
- return;
- }
- var node = nodeId ? WebInspector.domAgent.nodeForId(nodeId) : null;
- selectNode.call(this, node);
- }
- if (this._selectedPathOnReset)
- WebInspector.domAgent.pushNodeByPathToFrontend(this._selectedPathOnReset, selectLastSelectedNode.bind(this));
- else
- selectNode.call(this);
- delete this._selectedPathOnReset;
- },
- searchCanceled: function()
- {
- delete this._searchQuery;
- this._hideSearchHighlights();
- WebInspector.searchController.updateSearchMatchesCount(0, this);
- delete this._currentSearchResultIndex;
- delete this._searchResults;
- WebInspector.domAgent.cancelSearch();
- },
- /**
- * @param {string} query
- */
- performSearch: function(query)
- {
- // Call searchCanceled since it will reset everything we need before doing a new search.
- this.searchCanceled();
- const whitespaceTrimmedQuery = query.trim();
- if (!whitespaceTrimmedQuery.length)
- return;
- this._searchQuery = query;
- /**
- * @param {number} resultCount
- */
- function resultCountCallback(resultCount)
- {
- WebInspector.searchController.updateSearchMatchesCount(resultCount, this);
- if (!resultCount)
- return;
- this._searchResults = new Array(resultCount);
- this._currentSearchResultIndex = -1;
- this.jumpToNextSearchResult();
- }
- WebInspector.domAgent.performSearch(whitespaceTrimmedQuery, resultCountCallback.bind(this));
- },
- _contextMenuEventFired: function(event)
- {
- function toggleWordWrap()
- {
- WebInspector.settings.domWordWrap.set(!WebInspector.settings.domWordWrap.get());
- }
- var contextMenu = new WebInspector.ContextMenu(event);
- this.treeOutline.populateContextMenu(contextMenu, event);
- if (WebInspector.experimentsSettings.cssRegions.isEnabled()) {
- contextMenu.appendSeparator();
- contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "CSS named flows\u2026" : "CSS Named Flows\u2026"), this._showNamedFlowCollections.bind(this));
- }
- contextMenu.appendSeparator();
- contextMenu.appendCheckboxItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Word wrap" : "Word Wrap"), toggleWordWrap.bind(this), WebInspector.settings.domWordWrap.get());
- contextMenu.show();
- },
- _showNamedFlowCollections: function()
- {
- if (!WebInspector.cssNamedFlowCollectionsView)
- WebInspector.cssNamedFlowCollectionsView = new WebInspector.CSSNamedFlowCollectionsView();
- WebInspector.cssNamedFlowCollectionsView.showInDrawer();
- },
- _domWordWrapSettingChanged: function(event)
- {
- if (event.data)
- this.contentElement.removeStyleClass("nowrap");
- else
- this.contentElement.addStyleClass("nowrap");
- var selectedNode = this.selectedDOMNode();
- if (!selectedNode)
- return;
- var treeElement = this.treeOutline.findTreeElement(selectedNode);
- if (treeElement)
- treeElement.updateSelection(); // Recalculate selection highlight dimensions.
- },
- switchToAndFocus: function(node)
- {
- // Reset search restore.
- WebInspector.searchController.cancelSearch();
- WebInspector.inspectorView.setCurrentPanel(this);
- this.selectDOMNode(node, true);
- },
- _populateContextMenu: function(contextMenu, node)
- {
- // Add debbuging-related actions
- contextMenu.appendSeparator();
- var pane = WebInspector.domBreakpointsSidebarPane;
- pane.populateNodeContextMenu(node, contextMenu);
- },
- _getPopoverAnchor: function(element)
- {
- var anchor = element.enclosingNodeOrSelfWithClass("webkit-html-resource-link");
- if (anchor) {
- if (!anchor.href)
- return null;
- var resource = WebInspector.resourceTreeModel.resourceForURL(anchor.href);
- if (!resource || resource.type !== WebInspector.resourceTypes.Image)
- return null;
- anchor.removeAttribute("title");
- }
- return anchor;
- },
-
- _loadDimensionsForNode: function(treeElement, callback)
- {
- // We get here for CSS properties, too, so bail out early for non-DOM treeElements.
- if (treeElement.treeOutline !== this.treeOutline) {
- callback();
- return;
- }
-
- var node = /** @type {WebInspector.DOMNode} */ (treeElement.representedObject);
- if (!node.nodeName() || node.nodeName().toLowerCase() !== "img") {
- callback();
- return;
- }
- WebInspector.RemoteObject.resolveNode(node, "", resolvedNode);
- function resolvedNode(object)
- {
- if (!object) {
- callback();
- return;
- }
- object.callFunctionJSON(dimensions, undefined, callback);
- object.release();
- function dimensions()
- {
- return { offsetWidth: this.offsetWidth, offsetHeight: this.offsetHeight, naturalWidth: this.naturalWidth, naturalHeight: this.naturalHeight };
- }
- }
- },
- /**
- * @param {Element} anchor
- * @param {WebInspector.Popover} popover
- */
- _showPopover: function(anchor, popover)
- {
- var listItem = anchor.enclosingNodeOrSelfWithNodeName("li");
- if (listItem && listItem.treeElement)
- this._loadDimensionsForNode(listItem.treeElement, WebInspector.DOMPresentationUtils.buildImagePreviewContents.bind(WebInspector.DOMPresentationUtils, anchor.href, true, showPopover));
- else
- WebInspector.DOMPresentationUtils.buildImagePreviewContents(anchor.href, true, showPopover);
- /**
- * @param {Element=} contents
- */
- function showPopover(contents)
- {
- if (!contents)
- return;
- popover.setCanShrink(false);
- popover.show(contents, anchor);
- }
- },
- jumpToNextSearchResult: function()
- {
- if (!this._searchResults)
- return;
- this._hideSearchHighlights();
- if (++this._currentSearchResultIndex >= this._searchResults.length)
- this._currentSearchResultIndex = 0;
- this._highlightCurrentSearchResult();
- },
- jumpToPreviousSearchResult: function()
- {
- if (!this._searchResults)
- return;
- this._hideSearchHighlights();
- if (--this._currentSearchResultIndex < 0)
- this._currentSearchResultIndex = (this._searchResults.length - 1);
- this._highlightCurrentSearchResult();
- },
- _highlightCurrentSearchResult: function()
- {
- var index = this._currentSearchResultIndex;
- var searchResults = this._searchResults;
- var searchResult = searchResults[index];
- if (searchResult === null) {
- WebInspector.searchController.updateCurrentMatchIndex(index, this);
- return;
- }
- if (typeof searchResult === "undefined") {
- // No data for slot, request it.
- function callback(node)
- {
- searchResults[index] = node || null;
- this._highlightCurrentSearchResult();
- }
- WebInspector.domAgent.searchResult(index, callback.bind(this));
- return;
- }
- WebInspector.searchController.updateCurrentMatchIndex(index, this);
- var treeElement = this.treeOutline.findTreeElement(searchResult);
- if (treeElement) {
- treeElement.highlightSearchResults(this._searchQuery);
- treeElement.reveal();
- var matches = treeElement.listItemElement.getElementsByClassName("webkit-search-result");
- if (matches.length)
- matches[0].scrollIntoViewIfNeeded();
- }
- },
- _hideSearchHighlights: function()
- {
- if (!this._searchResults)
- return;
- var searchResult = this._searchResults[this._currentSearchResultIndex];
- if (!searchResult)
- return;
- var treeElement = this.treeOutline.findTreeElement(searchResult);
- if (treeElement)
- treeElement.hideSearchHighlights();
- },
- selectedDOMNode: function()
- {
- return this.treeOutline.selectedDOMNode();
- },
- /**
- * @param {boolean=} focus
- */
- selectDOMNode: function(node, focus)
- {
- this.treeOutline.selectDOMNode(node, focus);
- },
- _nodeRemoved: function(event)
- {
- if (!this.isShowing())
- return;
- var crumbs = this.crumbsElement;
- for (var crumb = crumbs.firstChild; crumb; crumb = crumb.nextSibling) {
- if (crumb.representedObject === event.data.node) {
- this.updateBreadcrumb(true);
- return;
- }
- }
- },
- _stylesPaneEdited: function()
- {
- // Once styles are edited, the Metrics pane should be updated.
- this.sidebarPanes.metrics.needsUpdate = true;
- this.updateMetrics();
- },
- _metricsPaneEdited: function()
- {
- // Once metrics are edited, the Styles pane should be updated.
- this.sidebarPanes.styles.needsUpdate = true;
- this.updateStyles(true);
- },
- _mouseMovedInCrumbs: function(event)
- {
- var nodeUnderMouse = document.elementFromPoint(event.pageX, event.pageY);
- var crumbElement = nodeUnderMouse.enclosingNodeOrSelfWithClass("crumb");
- WebInspector.domAgent.highlightDOMNode(crumbElement ? crumbElement.representedObject.id : 0);
- if ("_mouseOutOfCrumbsTimeout" in this) {
- clearTimeout(this._mouseOutOfCrumbsTimeout);
- delete this._mouseOutOfCrumbsTimeout;
- }
- },
- _mouseMovedOutOfCrumbs: function(event)
- {
- var nodeUnderMouse = document.elementFromPoint(event.pageX, event.pageY);
- if (nodeUnderMouse && nodeUnderMouse.isDescendant(this.crumbsElement))
- return;
- WebInspector.domAgent.hideDOMNodeHighlight();
- this._mouseOutOfCrumbsTimeout = setTimeout(this.updateBreadcrumbSizes.bind(this), 1000);
- },
- _updateBreadcrumbIfNeeded: function(event)
- {
- var name = event.data.name;
- if (name !== "class" && name !== "id")
- return;
- var node = /** @type {WebInspector.DOMNode} */ (event.data.node);
- var crumbs = this.crumbsElement;
- var crumb = crumbs.firstChild;
- while (crumb) {
- if (crumb.representedObject === node) {
- this.updateBreadcrumb(true);
- break;
- }
- crumb = crumb.nextSibling;
- }
- },
- /**
- * @param {boolean=} forceUpdate
- */
- updateBreadcrumb: function(forceUpdate)
- {
- if (!this.isShowing())
- return;
- var crumbs = this.crumbsElement;
- var handled = false;
- var crumb = crumbs.firstChild;
- while (crumb) {
- if (crumb.representedObject === this.selectedDOMNode()) {
- crumb.addStyleClass("selected");
- handled = true;
- } else {
- crumb.removeStyleClass("selected");
- }
- crumb = crumb.nextSibling;
- }
- if (handled && !forceUpdate) {
- // We don't need to rebuild the crumbs, but we need to adjust sizes
- // to reflect the new focused or root node.
- this.updateBreadcrumbSizes();
- return;
- }
- crumbs.removeChildren();
- var panel = this;
- function selectCrumbFunction(event)
- {
- var crumb = event.currentTarget;
- if (crumb.hasStyleClass("collapsed")) {
- // Clicking a collapsed crumb will expose the hidden crumbs.
- if (crumb === panel.crumbsElement.firstChild) {
- // If the focused crumb is the first child, pick the farthest crumb
- // that is still hidden. This allows the user to expose every crumb.
- var currentCrumb = crumb;
- while (currentCrumb) {
- var hidden = currentCrumb.hasStyleClass("hidden");
- var collapsed = currentCrumb.hasStyleClass("collapsed");
- if (!hidden && !collapsed)
- break;
- crumb = currentCrumb;
- currentCrumb = currentCrumb.nextSibling;
- }
- }
- panel.updateBreadcrumbSizes(crumb);
- } else
- panel.selectDOMNode(crumb.representedObject, true);
- event.preventDefault();
- }
- for (var current = this.selectedDOMNode(); current; current = current.parentNode) {
- if (current.nodeType() === Node.DOCUMENT_NODE)
- continue;
- crumb = document.createElement("span");
- crumb.className = "crumb";
- crumb.representedObject = current;
- crumb.addEventListener("mousedown", selectCrumbFunction, false);
- var crumbTitle;
- switch (current.nodeType()) {
- case Node.ELEMENT_NODE:
- WebInspector.DOMPresentationUtils.decorateNodeLabel(current, crumb);
- break;
- case Node.TEXT_NODE:
- crumbTitle = WebInspector.UIString("(text)");
- break
- case Node.COMMENT_NODE:
- crumbTitle = "<!-->";
- break;
- case Node.DOCUMENT_TYPE_NODE:
- crumbTitle = "<!DOCTYPE>";
- break;
- default:
- crumbTitle = current.nodeNameInCorrectCase();
- }
- if (!crumb.childNodes.length) {
- var nameElement = document.createElement("span");
- nameElement.textContent = crumbTitle;
- crumb.appendChild(nameElement);
- crumb.title = crumbTitle;
- }
- if (current === this.selectedDOMNode())
- crumb.addStyleClass("selected");
- if (!crumbs.childNodes.length)
- crumb.addStyleClass("end");
- crumbs.appendChild(crumb);
- }
- if (crumbs.hasChildNodes())
- crumbs.lastChild.addStyleClass("start");
- this.updateBreadcrumbSizes();
- },
- /**
- * @param {Element=} focusedCrumb
- */
- updateBreadcrumbSizes: function(focusedCrumb)
- {
- if (!this.isShowing())
- return;
- if (document.body.offsetWidth <= 0) {
- // The stylesheet hasn't loaded yet or the window is closed,
- // so we can't calculate what is need. Return early.
- return;
- }
- var crumbs = this.crumbsElement;
- if (!crumbs.childNodes.length || crumbs.offsetWidth <= 0)
- return; // No crumbs, do nothing.
- // A Zero index is the right most child crumb in the breadcrumb.
- var selectedIndex = 0;
- var focusedIndex = 0;
- var selectedCrumb;
- var i = 0;
- var crumb = crumbs.firstChild;
- while (crumb) {
- // Find the selected crumb and index.
- if (!selectedCrumb && crumb.hasStyleClass("selected")) {
- selectedCrumb = crumb;
- selectedIndex = i;
- }
- // Find the focused crumb index.
- if (crumb === focusedCrumb)
- focusedIndex = i;
- // Remove any styles that affect size before
- // deciding to shorten any crumbs.
- if (crumb !== crumbs.lastChild)
- crumb.removeStyleClass("start");
- if (crumb !== crumbs.firstChild)
- crumb.removeStyleClass("end");
- crumb.removeStyleClass("compact");
- crumb.removeStyleClass("collapsed");
- crumb.removeStyleClass("hidden");
- crumb = crumb.nextSibling;
- ++i;
- }
- // Restore the start and end crumb classes in case they got removed in coalesceCollapsedCrumbs().
- // The order of the crumbs in the document is opposite of the visual order.
- crumbs.firstChild.addStyleClass("end");
- crumbs.lastChild.addStyleClass("start");
- function crumbsAreSmallerThanContainer()
- {
- var rightPadding = 20;
- var errorWarningElement = document.getElementById("error-warning-count");
- if (!WebInspector.drawer.visible && errorWarningElement)
- rightPadding += errorWarningElement.offsetWidth;
- return ((crumbs.totalOffsetLeft() + crumbs.offsetWidth + rightPadding) < window.innerWidth);
- }
- if (crumbsAreSmallerThanContainer())
- return; // No need to compact the crumbs, they all fit at full size.
- var BothSides = 0;
- var AncestorSide = -1;
- var ChildSide = 1;
- /**
- * @param {boolean=} significantCrumb
- */
- function makeCrumbsSmaller(shrinkingFunction, direction, significantCrumb)
- {
- if (!significantCrumb)
- significantCrumb = (focusedCrumb || selectedCrumb);
- if (significantCrumb === selectedCrumb)
- var significantIndex = selectedIndex;
- else if (significantCrumb === focusedCrumb)
- var significantIndex = focusedIndex;
- else {
- var significantIndex = 0;
- for (var i = 0; i < crumbs.childNodes.length; ++i) {
- if (crumbs.childNodes[i] === significantCrumb) {
- significantIndex = i;
- break;
- }
- }
- }
- function shrinkCrumbAtIndex(index)
- {
- var shrinkCrumb = crumbs.childNodes[index];
- if (shrinkCrumb && shrinkCrumb !== significantCrumb)
- shrinkingFunction(shrinkCrumb);
- if (crumbsAreSmallerThanContainer())
- return true; // No need to compact the crumbs more.
- return false;
- }
- // Shrink crumbs one at a time by applying the shrinkingFunction until the crumbs
- // fit in the container or we run out of crumbs to shrink.
- if (direction) {
- // Crumbs are shrunk on only one side (based on direction) of the signifcant crumb.
- var index = (direction > 0 ? 0 : crumbs.childNodes.length - 1);
- while (index !== significantIndex) {
- if (shrinkCrumbAtIndex(index))
- return true;
- index += (direction > 0 ? 1 : -1);
- }
- } else {
- // Crumbs are shrunk in order of descending distance from the signifcant crumb,
- // with a tie going to child crumbs.
- var startIndex = 0;
- var endIndex = crumbs.childNodes.length - 1;
- while (startIndex != significantIndex || endIndex != significantIndex) {
- var startDistance = significantIndex - startIndex;
- var endDistance = endIndex - significantIndex;
- if (startDistance >= endDistance)
- var index = startIndex++;
- else
- var index = endIndex--;
- if (shrinkCrumbAtIndex(index))
- return true;
- }
- }
- // We are not small enough yet, return false so the caller knows.
- return false;
- }
- function coalesceCollapsedCrumbs()
- {
- var crumb = crumbs.firstChild;
- var collapsedRun = false;
- var newStartNeeded = false;
- var newEndNeeded = false;
- while (crumb) {
- var hidden = crumb.hasStyleClass("hidden");
- if (!hidden) {
- var collapsed = crumb.hasStyleClass("collapsed");
- if (collapsedRun && collapsed) {
- crumb.addStyleClass("hidden");
- crumb.removeStyleClass("compact");
- crumb.removeStyleClass("collapsed");
- if (crumb.hasStyleClass("start")) {
- crumb.removeStyleClass("start");
- newStartNeeded = true;
- }
- if (crumb.hasStyleClass("end")) {
- crumb.removeStyleClass("end");
- newEndNeeded = true;
- }
- continue;
- }
- collapsedRun = collapsed;
- if (newEndNeeded) {
- newEndNeeded = false;
- crumb.addStyleClass("end");
- }
- } else
- collapsedRun = true;
- crumb = crumb.nextSibling;
- }
- if (newStartNeeded) {
- crumb = crumbs.lastChild;
- while (crumb) {
- if (!crumb.hasStyleClass("hidden")) {
- crumb.addStyleClass("start");
- break;
- }
- crumb = crumb.previousSibling;
- }
- }
- }
- function compact(crumb)
- {
- if (crumb.hasStyleClass("hidden"))
- return;
- crumb.addStyleClass("compact");
- }
- function collapse(crumb, dontCoalesce)
- {
- if (crumb.hasStyleClass("hidden"))
- return;
- crumb.addStyleClass("collapsed");
- crumb.removeStyleClass("compact");
- if (!dontCoalesce)
- coalesceCollapsedCrumbs();
- }
- if (!focusedCrumb) {
- // When not focused on a crumb we can be biased and collapse less important
- // crumbs that the user might not care much about.
- // Compact child crumbs.
- if (makeCrumbsSmaller(compact, ChildSide))
- return;
- // Collapse child crumbs.
- if (makeCrumbsSmaller(collapse, ChildSide))
- return;
- }
- // Compact ancestor crumbs, or from both sides if focused.
- if (makeCrumbsSmaller(compact, (focusedCrumb ? BothSides : AncestorSide)))
- return;
- // Collapse ancestor crumbs, or from both sides if focused.
- if (makeCrumbsSmaller(collapse, (focusedCrumb ? BothSides : AncestorSide)))
- return;
- if (!selectedCrumb)
- return;
- // Compact the selected crumb.
- compact(selectedCrumb);
- if (crumbsAreSmallerThanContainer())
- return;
- // Collapse the selected crumb as a last resort. Pass true to prevent coalescing.
- collapse(selectedCrumb, true);
- },
- /**
- * @param {boolean=} forceUpdate
- */
- updateStyles: function(forceUpdate)
- {
- var stylesSidebarPane = this.sidebarPanes.styles;
- var computedStylePane = this.sidebarPanes.computedStyle;
- if ((!stylesSidebarPane.isShowing() && !computedStylePane.isShowing()) || !stylesSidebarPane.needsUpdate)
- return;
- stylesSidebarPane.update(this.selectedDOMNode(), forceUpdate);
- stylesSidebarPane.needsUpdate = false;
- },
- updateMetrics: function()
- {
- var metricsSidebarPane = this.sidebarPanes.metrics;
- if (!metricsSidebarPane.isShowing() || !metricsSidebarPane.needsUpdate)
- return;
- metricsSidebarPane.update(this.selectedDOMNode());
- metricsSidebarPane.needsUpdate = false;
- },
- updateProperties: function()
- {
- var propertiesSidebarPane = this.sidebarPanes.properties;
- if (!propertiesSidebarPane.isShowing() || !propertiesSidebarPane.needsUpdate)
- return;
- propertiesSidebarPane.update(this.selectedDOMNode());
- propertiesSidebarPane.needsUpdate = false;
- },
- updateEventListeners: function()
- {
- var eventListenersSidebarPane = this.sidebarPanes.eventListeners;
- if (!eventListenersSidebarPane.isShowing() || !eventListenersSidebarPane.needsUpdate)
- return;
- eventListenersSidebarPane.update(this.selectedDOMNode());
- eventListenersSidebarPane.needsUpdate = false;
- },
- handleShortcut: function(event)
- {
- function handleUndoRedo()
- {
- if (WebInspector.KeyboardShortcut.eventHasCtrlOrMeta(event) && !event.shiftKey && event.keyIdentifier === "U+005A") { // Z key
- WebInspector.domAgent.undo(this._updateSidebars.bind(this));
- event.handled = true;
- return;
- }
- var isRedoKey = WebInspector.isMac() ? event.metaKey && event.shiftKey && event.keyIdentifier === "U+005A" : // Z key
- event.ctrlKey && event.keyIdentifier === "U+0059"; // Y key
- if (isRedoKey) {
- DOMAgent.redo(this._updateSidebars.bind(this));
- event.handled = true;
- }
- }
- if (!this.treeOutline.editing()) {
- handleUndoRedo.call(this);
- if (event.handled)
- return;
- }
- this.treeOutline.handleShortcut(event);
- },
- handleCopyEvent: function(event)
- {
- // Don't prevent the normal copy if the user has a selection.
- if (!window.getSelection().isCollapsed)
- return;
- event.clipboardData.clearData();
- event.preventDefault();
- this.selectedDOMNode().copyNode();
- },
- sidebarResized: function(event)
- {
- this.treeOutline.updateSelection();
- },
- _inspectElementRequested: function(event)
- {
- var node = event.data;
- this.revealAndSelectNode(node.id);
- },
- revealAndSelectNode: function(nodeId)
- {
- WebInspector.inspectorView.setCurrentPanel(this);
- var node = WebInspector.domAgent.nodeForId(nodeId);
- if (!node)
- return;
- WebInspector.domAgent.highlightDOMNodeForTwoSeconds(nodeId);
- this.selectDOMNode(node, true);
- },
- /**
- * @param {WebInspector.ContextMenu} contextMenu
- * @param {Object} target
- */
- appendApplicableItems: function(event, contextMenu, target)
- {
- if (!(target instanceof WebInspector.RemoteObject))
- return;
- var remoteObject = /** @type {WebInspector.RemoteObject} */ (target);
- if (remoteObject.subtype !== "node")
- return;
- function selectNode(nodeId)
- {
- if (nodeId)
- WebInspector.domAgent.inspectElement(nodeId);
- }
-
- function revealElement()
- {
- remoteObject.pushNodeToFrontend(selectNode);
- }
- contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Elements panel" : "Reveal in Elements Panel"), revealElement.bind(this));
- },
- _sidebarContextMenuEventFired: function(event)
- {
- var contextMenu = new WebInspector.ContextMenu(event);
- contextMenu.show();
- },
- _dockSideChanged: function()
- {
- var dockSide = WebInspector.dockController.dockSide();
- var vertically = dockSide === WebInspector.DockController.State.DockedToRight && WebInspector.settings.splitVerticallyWhenDockedToRight.get();
- this._splitVertically(vertically);
- },
- /**
- * @param {boolean} vertically
- */
- _splitVertically: function(vertically)
- {
- if (this.sidebarPaneView && vertically === !this.splitView.isVertical())
- return;
- if (this.sidebarPaneView)
- this.sidebarPaneView.detach();
- this.splitView.setVertical(!vertically);
- if (!vertically) {
- this.sidebarPaneView = new WebInspector.SidebarPaneStack();
- for (var pane in this.sidebarPanes)
- this.sidebarPaneView.addPane(this.sidebarPanes[pane]);
- } else {
- this.sidebarPaneView = new WebInspector.SidebarTabbedPane();
- var compositePane = new WebInspector.SidebarPane(this.sidebarPanes.styles.title());
- compositePane.element.addStyleClass("composite");
- compositePane.element.addStyleClass("fill");
- var expandComposite = compositePane.expand.bind(compositePane);
- var splitView = new WebInspector.SplitView(true, "StylesPaneSplitRatio", 0.5);
- splitView.show(compositePane.bodyElement);
- this.sidebarPanes.styles.show(splitView.firstElement());
- splitView.firstElement().appendChild(this.sidebarPanes.styles.titleElement);
- this.sidebarPanes.styles.setExpandCallback(expandComposite);
- this.sidebarPanes.metrics.show(splitView.secondElement());
- this.sidebarPanes.metrics.setExpandCallback(expandComposite);
- splitView.secondElement().appendChild(this.sidebarPanes.computedStyle.titleElement);
- splitView.secondElement().addStyleClass("metrics-and-computed");
- this.sidebarPanes.computedStyle.show(splitView.secondElement());
- this.sidebarPanes.computedStyle.setExpandCallback(expandComposite);
- this.sidebarPaneView.addPane(compositePane);
- this.sidebarPaneView.addPane(this.sidebarPanes.properties);
- this.sidebarPaneView.addPane(this.sidebarPanes.domBreakpoints);
- this.sidebarPaneView.addPane(this.sidebarPanes.eventListeners);
- }
- this.sidebarPaneView.show(this.splitView.sidebarElement);
- this.sidebarPanes.styles.expand();
- },
- /**
- * @param {string} id
- * @param {WebInspector.SidebarPane} pane
- */
- addExtensionSidebarPane: function(id, pane)
- {
- this.sidebarPanes[id] = pane;
- this.sidebarPaneView.addPane(pane);
- },
- __proto__: WebInspector.Panel.prototype
- }
|