Home | History | Annotate | Download | only in front_end
      1 /*
      2  * Copyright (C) 2007, 2008 Apple Inc.  All rights reserved.
      3  * Copyright (C) 2008 Matt Lilek <webkit (at) mattlilek.com>
      4  * Copyright (C) 2009 Joseph Pecoraro
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  *
     10  * 1.  Redistributions of source code must retain the above copyright
     11  *     notice, this list of conditions and the following disclaimer.
     12  * 2.  Redistributions in binary form must reproduce the above copyright
     13  *     notice, this list of conditions and the following disclaimer in the
     14  *     documentation and/or other materials provided with the distribution.
     15  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
     16  *     its contributors may be used to endorse or promote products derived
     17  *     from this software without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
     20  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     22  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
     23  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     26  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 importScript("CSSNamedFlowCollectionsView.js");
     32 importScript("CSSNamedFlowView.js");
     33 importScript("EventListenersSidebarPane.js");
     34 importScript("MetricsSidebarPane.js");
     35 importScript("PropertiesSidebarPane.js");
     36 importScript("StylesSidebarPane.js");
     37 
     38 /**
     39  * @constructor
     40  * @extends {WebInspector.Panel}
     41  */
     42 WebInspector.ElementsPanel = function()
     43 {
     44     WebInspector.Panel.call(this, "elements");
     45     this.registerRequiredCSS("breadcrumbList.css");
     46     this.registerRequiredCSS("elementsPanel.css");
     47     this.registerRequiredCSS("textPrompt.css");
     48     this.setHideOnDetach();
     49 
     50     const initialSidebarWidth = 325;
     51     const minimumContentWidthPercent = 0.34;
     52     const initialSidebarHeight = 325;
     53     const minimumContentHeightPercent = 0.34;
     54     this.createSidebarView(this.element, WebInspector.SidebarView.SidebarPosition.End, initialSidebarWidth, initialSidebarHeight);
     55     this.splitView.setSidebarElementConstraints(Preferences.minElementsSidebarWidth, Preferences.minElementsSidebarHeight);
     56     this.splitView.setMainElementConstraints(minimumContentWidthPercent, minimumContentHeightPercent);
     57 
     58     this.contentElement = this.splitView.mainElement;
     59     this.contentElement.id = "elements-content";
     60     this.contentElement.addStyleClass("outline-disclosure");
     61     this.contentElement.addStyleClass("source-code");
     62     if (!WebInspector.settings.domWordWrap.get())
     63         this.contentElement.classList.add("nowrap");
     64     WebInspector.settings.domWordWrap.addChangeListener(this._domWordWrapSettingChanged.bind(this));
     65 
     66     this.contentElement.addEventListener("contextmenu", this._contextMenuEventFired.bind(this), true);
     67     this.splitView.sidebarElement.addEventListener("contextmenu", this._sidebarContextMenuEventFired.bind(this), false);
     68 
     69     this.treeOutline = new WebInspector.ElementsTreeOutline(true, true, false, this._populateContextMenu.bind(this), this._setPseudoClassForNodeId.bind(this));
     70     this.treeOutline.wireToDomAgent();
     71 
     72     this.treeOutline.addEventListener(WebInspector.ElementsTreeOutline.Events.SelectedNodeChanged, this._selectedNodeChanged, this);
     73 
     74     this.crumbsElement = document.createElement("div");
     75     this.crumbsElement.className = "crumbs";
     76     this.crumbsElement.addEventListener("mousemove", this._mouseMovedInCrumbs.bind(this), false);
     77     this.crumbsElement.addEventListener("mouseout", this._mouseMovedOutOfCrumbs.bind(this), false);
     78 
     79     this.sidebarPanes = {};
     80     this.sidebarPanes.computedStyle = new WebInspector.ComputedStyleSidebarPane();
     81     this.sidebarPanes.styles = new WebInspector.StylesSidebarPane(this.sidebarPanes.computedStyle, this._setPseudoClassForNodeId.bind(this));
     82     this.sidebarPanes.metrics = new WebInspector.MetricsSidebarPane();
     83     this.sidebarPanes.properties = new WebInspector.PropertiesSidebarPane();
     84     this.sidebarPanes.domBreakpoints = WebInspector.domBreakpointsSidebarPane.createProxy(this);
     85     this.sidebarPanes.eventListeners = new WebInspector.EventListenersSidebarPane();
     86 
     87     this.sidebarPanes.styles.addEventListener(WebInspector.SidebarPane.EventTypes.wasShown, this.updateStyles.bind(this, false));
     88     this.sidebarPanes.metrics.addEventListener(WebInspector.SidebarPane.EventTypes.wasShown, this.updateMetrics.bind(this));
     89     this.sidebarPanes.properties.addEventListener(WebInspector.SidebarPane.EventTypes.wasShown, this.updateProperties.bind(this));
     90     this.sidebarPanes.eventListeners.addEventListener(WebInspector.SidebarPane.EventTypes.wasShown, this.updateEventListeners.bind(this));
     91 
     92     this.sidebarPanes.styles.addEventListener("style edited", this._stylesPaneEdited, this);
     93     this.sidebarPanes.styles.addEventListener("style property toggled", this._stylesPaneEdited, this);
     94     this.sidebarPanes.metrics.addEventListener("metrics edited", this._metricsPaneEdited, this);
     95 
     96     WebInspector.dockController.addEventListener(WebInspector.DockController.Events.DockSideChanged, this._dockSideChanged.bind(this));
     97     WebInspector.settings.splitVerticallyWhenDockedToRight.addChangeListener(this._dockSideChanged.bind(this));
     98     this._dockSideChanged();
     99 
    100     this._popoverHelper = new WebInspector.PopoverHelper(this.element, this._getPopoverAnchor.bind(this), this._showPopover.bind(this));
    101     this._popoverHelper.setTimeout(0);
    102 
    103     WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.AttrModified, this._updateBreadcrumbIfNeeded, this);
    104     WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.AttrRemoved, this._updateBreadcrumbIfNeeded, this);
    105     WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.NodeRemoved, this._nodeRemoved, this);
    106     WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.DocumentUpdated, this._documentUpdatedEvent, this);
    107     WebInspector.settings.showShadowDOM.addChangeListener(this._showShadowDOMChanged.bind(this));
    108 
    109     if (WebInspector.domAgent.existingDocument())
    110         this._documentUpdated(WebInspector.domAgent.existingDocument());
    111 }
    112 
    113 WebInspector.ElementsPanel.prototype = {
    114     get statusBarItems()
    115     {
    116         return [this.crumbsElement];
    117     },
    118 
    119     defaultFocusedElement: function()
    120     {
    121         return this.treeOutline.element;
    122     },
    123 
    124     statusBarResized: function()
    125     {
    126         this.updateBreadcrumbSizes();
    127     },
    128 
    129     wasShown: function()
    130     {
    131         // Attach heavy component lazily
    132         if (this.treeOutline.element.parentElement !== this.contentElement)
    133             this.contentElement.appendChild(this.treeOutline.element);
    134 
    135         WebInspector.Panel.prototype.wasShown.call(this);
    136 
    137         this.updateBreadcrumb();
    138         this.treeOutline.updateSelection();
    139         this.treeOutline.setVisible(true);
    140 
    141         if (!this.treeOutline.rootDOMNode)
    142             WebInspector.domAgent.requestDocument();
    143     },
    144 
    145     willHide: function()
    146     {
    147         WebInspector.domAgent.hideDOMNodeHighlight();
    148         this.treeOutline.setVisible(false);
    149         this._popoverHelper.hidePopover();
    150 
    151         // Detach heavy component on hide
    152         this.contentElement.removeChild(this.treeOutline.element);
    153 
    154         WebInspector.Panel.prototype.willHide.call(this);
    155     },
    156 
    157     onResize: function()
    158     {
    159         this.treeOutline.updateSelection();
    160         this.updateBreadcrumbSizes();
    161     },
    162 
    163     /**
    164      * @param {DOMAgent.NodeId} nodeId
    165      * @param {string} pseudoClass
    166      * @param {boolean} enable
    167      */
    168     _setPseudoClassForNodeId: function(nodeId, pseudoClass, enable)
    169     {
    170         var node = WebInspector.domAgent.nodeForId(nodeId);
    171         if (!node)
    172             return;
    173 
    174         var pseudoClasses = node.getUserProperty(WebInspector.ElementsTreeOutline.PseudoStateDecorator.PropertyName);
    175         if (enable) {
    176             pseudoClasses = pseudoClasses || [];
    177             if (pseudoClasses.indexOf(pseudoClass) >= 0)
    178                 return;
    179             pseudoClasses.push(pseudoClass);
    180             node.setUserProperty(WebInspector.ElementsTreeOutline.PseudoStateDecorator.PropertyName, pseudoClasses);
    181         } else {
    182             if (!pseudoClasses || pseudoClasses.indexOf(pseudoClass) < 0)
    183                 return;
    184             pseudoClasses.remove(pseudoClass);
    185             if (!pseudoClasses.length)
    186                 node.removeUserProperty(WebInspector.ElementsTreeOutline.PseudoStateDecorator.PropertyName);
    187         }
    188 
    189         this.treeOutline.updateOpenCloseTags(node);
    190         WebInspector.cssModel.forcePseudoState(node.id, node.getUserProperty(WebInspector.ElementsTreeOutline.PseudoStateDecorator.PropertyName));
    191         this._metricsPaneEdited();
    192         this._stylesPaneEdited();
    193 
    194         WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
    195             action: WebInspector.UserMetrics.UserActionNames.ForcedElementState,
    196             selector: node.appropriateSelectorFor(false),
    197             enabled: enable,
    198             state: pseudoClass
    199         });
    200     },
    201 
    202     _selectedNodeChanged: function()
    203     {
    204         var selectedNode = this.selectedDOMNode();
    205         if (!selectedNode && this._lastValidSelectedNode)
    206             this._selectedPathOnReset = this._lastValidSelectedNode.path();
    207 
    208         this.updateBreadcrumb(false);
    209 
    210         this._updateSidebars();
    211 
    212         if (selectedNode) {
    213             ConsoleAgent.addInspectedNode(selectedNode.id);
    214             this._lastValidSelectedNode = selectedNode;
    215         }
    216         WebInspector.notifications.dispatchEventToListeners(WebInspector.ElementsTreeOutline.Events.SelectedNodeChanged);
    217     },
    218 
    219     _updateSidebars: function()
    220     {
    221         for (var pane in this.sidebarPanes)
    222            this.sidebarPanes[pane].needsUpdate = true;
    223 
    224         this.updateStyles(true);
    225         this.updateMetrics();
    226         this.updateProperties();
    227         this.updateEventListeners();
    228     },
    229 
    230     _reset: function()
    231     {
    232         delete this.currentQuery;
    233     },
    234 
    235     _documentUpdatedEvent: function(event)
    236     {
    237         this._documentUpdated(event.data);
    238     },
    239 
    240     _documentUpdated: function(inspectedRootDocument)
    241     {
    242         this._reset();
    243         this.searchCanceled();
    244 
    245         this.treeOutline.rootDOMNode = inspectedRootDocument;
    246 
    247         if (!inspectedRootDocument) {
    248             if (this.isShowing())
    249                 WebInspector.domAgent.requestDocument();
    250             return;
    251         }
    252 
    253         WebInspector.domBreakpointsSidebarPane.restoreBreakpoints();
    254 
    255         /**
    256          * @this {WebInspector.ElementsPanel}
    257          * @param {WebInspector.DOMNode=} candidateFocusNode
    258          */
    259         function selectNode(candidateFocusNode)
    260         {
    261             if (!candidateFocusNode)
    262                 candidateFocusNode = inspectedRootDocument.body || inspectedRootDocument.documentElement;
    263 
    264             if (!candidateFocusNode)
    265                 return;
    266 
    267             this.selectDOMNode(candidateFocusNode);
    268             if (this.treeOutline.selectedTreeElement)
    269                 this.treeOutline.selectedTreeElement.expand();
    270         }
    271 
    272         /**
    273          * @param {?DOMAgent.NodeId} nodeId
    274          */
    275         function selectLastSelectedNode(nodeId)
    276         {
    277             if (this.selectedDOMNode()) {
    278                 // Focused node has been explicitly set while reaching out for the last selected node.
    279                 return;
    280             }
    281             var node = nodeId ? WebInspector.domAgent.nodeForId(nodeId) : null;
    282             selectNode.call(this, node);
    283         }
    284 
    285         if (this._selectedPathOnReset)
    286             WebInspector.domAgent.pushNodeByPathToFrontend(this._selectedPathOnReset, selectLastSelectedNode.bind(this));
    287         else
    288             selectNode.call(this);
    289         delete this._selectedPathOnReset;
    290     },
    291 
    292     searchCanceled: function()
    293     {
    294         delete this._searchQuery;
    295         this._hideSearchHighlights();
    296 
    297         WebInspector.searchController.updateSearchMatchesCount(0, this);
    298 
    299         delete this._currentSearchResultIndex;
    300         delete this._searchResults;
    301         WebInspector.domAgent.cancelSearch();
    302     },
    303 
    304     /**
    305      * @param {string} query
    306      * @param {boolean} shouldJump
    307      */
    308     performSearch: function(query, shouldJump)
    309     {
    310         // Call searchCanceled since it will reset everything we need before doing a new search.
    311         this.searchCanceled();
    312 
    313         const whitespaceTrimmedQuery = query.trim();
    314         if (!whitespaceTrimmedQuery.length)
    315             return;
    316 
    317         this._searchQuery = query;
    318 
    319         /**
    320          * @param {number} resultCount
    321          */
    322         function resultCountCallback(resultCount)
    323         {
    324             WebInspector.searchController.updateSearchMatchesCount(resultCount, this);
    325             if (!resultCount)
    326                 return;
    327 
    328             this._searchResults = new Array(resultCount);
    329             this._currentSearchResultIndex = -1;
    330             if (shouldJump)
    331                 this.jumpToNextSearchResult();
    332         }
    333         WebInspector.domAgent.performSearch(whitespaceTrimmedQuery, resultCountCallback.bind(this));
    334     },
    335 
    336     _contextMenuEventFired: function(event)
    337     {
    338         function toggleWordWrap()
    339         {
    340             WebInspector.settings.domWordWrap.set(!WebInspector.settings.domWordWrap.get());
    341         }
    342 
    343         var contextMenu = new WebInspector.ContextMenu(event);
    344         this.treeOutline.populateContextMenu(contextMenu, event);
    345 
    346         if (WebInspector.experimentsSettings.cssRegions.isEnabled()) {
    347             contextMenu.appendSeparator();
    348             contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "CSS named flows\u2026" : "CSS Named Flows\u2026"), this._showNamedFlowCollections.bind(this));
    349         }
    350 
    351         contextMenu.appendSeparator();
    352         contextMenu.appendCheckboxItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Word wrap" : "Word Wrap"), toggleWordWrap.bind(this), WebInspector.settings.domWordWrap.get());
    353 
    354         contextMenu.show();
    355     },
    356 
    357     _showNamedFlowCollections: function()
    358     {
    359         if (!WebInspector.cssNamedFlowCollectionsView)
    360             WebInspector.cssNamedFlowCollectionsView = new WebInspector.CSSNamedFlowCollectionsView();
    361         WebInspector.cssNamedFlowCollectionsView.showInDrawer();
    362     },
    363 
    364     _domWordWrapSettingChanged: function(event)
    365     {
    366         if (event.data)
    367             this.contentElement.removeStyleClass("nowrap");
    368         else
    369             this.contentElement.addStyleClass("nowrap");
    370 
    371         var selectedNode = this.selectedDOMNode();
    372         if (!selectedNode)
    373             return;
    374 
    375         var treeElement = this.treeOutline.findTreeElement(selectedNode);
    376         if (treeElement)
    377             treeElement.updateSelection(); // Recalculate selection highlight dimensions.
    378     },
    379 
    380     switchToAndFocus: function(node)
    381     {
    382         // Reset search restore.
    383         WebInspector.searchController.cancelSearch();
    384         WebInspector.inspectorView.setCurrentPanel(this);
    385         this.selectDOMNode(node, true);
    386     },
    387 
    388     _populateContextMenu: function(contextMenu, node)
    389     {
    390         // Add debbuging-related actions
    391         contextMenu.appendSeparator();
    392         var pane = WebInspector.domBreakpointsSidebarPane;
    393         pane.populateNodeContextMenu(node, contextMenu);
    394     },
    395 
    396     _getPopoverAnchor: function(element)
    397     {
    398         var anchor = element.enclosingNodeOrSelfWithClass("webkit-html-resource-link");
    399         if (anchor) {
    400             if (!anchor.href)
    401                 return null;
    402 
    403             var resource = WebInspector.resourceTreeModel.resourceForURL(anchor.href);
    404             if (!resource || resource.type !== WebInspector.resourceTypes.Image)
    405                 return null;
    406 
    407             anchor.removeAttribute("title");
    408         }
    409         return anchor;
    410     },
    411 
    412     _loadDimensionsForNode: function(treeElement, callback)
    413     {
    414         // We get here for CSS properties, too, so bail out early for non-DOM treeElements.
    415         if (treeElement.treeOutline !== this.treeOutline) {
    416             callback();
    417             return;
    418         }
    419 
    420         var node = /** @type {WebInspector.DOMNode} */ (treeElement.representedObject);
    421 
    422         if (!node.nodeName() || node.nodeName().toLowerCase() !== "img") {
    423             callback();
    424             return;
    425         }
    426 
    427         WebInspector.RemoteObject.resolveNode(node, "", resolvedNode);
    428 
    429         function resolvedNode(object)
    430         {
    431             if (!object) {
    432                 callback();
    433                 return;
    434             }
    435 
    436             object.callFunctionJSON(dimensions, undefined, callback);
    437             object.release();
    438 
    439             function dimensions()
    440             {
    441                 return { offsetWidth: this.offsetWidth, offsetHeight: this.offsetHeight, naturalWidth: this.naturalWidth, naturalHeight: this.naturalHeight };
    442             }
    443         }
    444     },
    445 
    446     /**
    447      * @param {Element} anchor
    448      * @param {WebInspector.Popover} popover
    449      */
    450     _showPopover: function(anchor, popover)
    451     {
    452         var listItem = anchor.enclosingNodeOrSelfWithNodeName("li");
    453         if (listItem && listItem.treeElement)
    454             this._loadDimensionsForNode(listItem.treeElement, WebInspector.DOMPresentationUtils.buildImagePreviewContents.bind(WebInspector.DOMPresentationUtils, anchor.href, true, showPopover));
    455         else
    456             WebInspector.DOMPresentationUtils.buildImagePreviewContents(anchor.href, true, showPopover);
    457 
    458         /**
    459          * @param {Element=} contents
    460          */
    461         function showPopover(contents)
    462         {
    463             if (!contents)
    464                 return;
    465             popover.setCanShrink(false);
    466             popover.show(contents, anchor);
    467         }
    468     },
    469 
    470     jumpToNextSearchResult: function()
    471     {
    472         if (!this._searchResults)
    473             return;
    474 
    475         this._hideSearchHighlights();
    476         if (++this._currentSearchResultIndex >= this._searchResults.length)
    477             this._currentSearchResultIndex = 0;
    478 
    479         this._highlightCurrentSearchResult();
    480     },
    481 
    482     jumpToPreviousSearchResult: function()
    483     {
    484         if (!this._searchResults)
    485             return;
    486 
    487         this._hideSearchHighlights();
    488         if (--this._currentSearchResultIndex < 0)
    489             this._currentSearchResultIndex = (this._searchResults.length - 1);
    490 
    491         this._highlightCurrentSearchResult();
    492     },
    493 
    494     _highlightCurrentSearchResult: function()
    495     {
    496         var index = this._currentSearchResultIndex;
    497         var searchResults = this._searchResults;
    498         var searchResult = searchResults[index];
    499 
    500         if (searchResult === null) {
    501             WebInspector.searchController.updateCurrentMatchIndex(index, this);
    502             return;
    503         }
    504 
    505         if (typeof searchResult === "undefined") {
    506             // No data for slot, request it.
    507             function callback(node)
    508             {
    509                 searchResults[index] = node || null;
    510                 this._highlightCurrentSearchResult();
    511             }
    512             WebInspector.domAgent.searchResult(index, callback.bind(this));
    513             return;
    514         }
    515 
    516         WebInspector.searchController.updateCurrentMatchIndex(index, this);
    517 
    518         var treeElement = this.treeOutline.findTreeElement(searchResult);
    519         if (treeElement) {
    520             treeElement.highlightSearchResults(this._searchQuery);
    521             treeElement.reveal();
    522             var matches = treeElement.listItemElement.getElementsByClassName("highlighted-search-result");
    523             if (matches.length)
    524                 matches[0].scrollIntoViewIfNeeded();
    525         }
    526     },
    527 
    528     _hideSearchHighlights: function()
    529     {
    530         if (!this._searchResults)
    531             return;
    532         var searchResult = this._searchResults[this._currentSearchResultIndex];
    533         if (!searchResult)
    534             return;
    535         var treeElement = this.treeOutline.findTreeElement(searchResult);
    536         if (treeElement)
    537             treeElement.hideSearchHighlights();
    538     },
    539 
    540     selectedDOMNode: function()
    541     {
    542         return this.treeOutline.selectedDOMNode();
    543     },
    544 
    545     /**
    546      * @param {boolean=} focus
    547      */
    548     selectDOMNode: function(node, focus)
    549     {
    550         this.treeOutline.selectDOMNode(node, focus);
    551     },
    552 
    553     _nodeRemoved: function(event)
    554     {
    555         if (!this.isShowing())
    556             return;
    557 
    558         var crumbs = this.crumbsElement;
    559         for (var crumb = crumbs.firstChild; crumb; crumb = crumb.nextSibling) {
    560             if (crumb.representedObject === event.data.node) {
    561                 this.updateBreadcrumb(true);
    562                 return;
    563             }
    564         }
    565     },
    566 
    567     _stylesPaneEdited: function()
    568     {
    569         // Once styles are edited, the Metrics pane should be updated.
    570         this.sidebarPanes.metrics.needsUpdate = true;
    571         this.updateMetrics();
    572     },
    573 
    574     _metricsPaneEdited: function()
    575     {
    576         // Once metrics are edited, the Styles pane should be updated.
    577         this.sidebarPanes.styles.needsUpdate = true;
    578         this.updateStyles(true);
    579     },
    580 
    581     _mouseMovedInCrumbs: function(event)
    582     {
    583         var nodeUnderMouse = document.elementFromPoint(event.pageX, event.pageY);
    584         var crumbElement = nodeUnderMouse.enclosingNodeOrSelfWithClass("crumb");
    585 
    586         WebInspector.domAgent.highlightDOMNode(crumbElement ? crumbElement.representedObject.id : 0);
    587 
    588         if ("_mouseOutOfCrumbsTimeout" in this) {
    589             clearTimeout(this._mouseOutOfCrumbsTimeout);
    590             delete this._mouseOutOfCrumbsTimeout;
    591         }
    592     },
    593 
    594     _mouseMovedOutOfCrumbs: function(event)
    595     {
    596         var nodeUnderMouse = document.elementFromPoint(event.pageX, event.pageY);
    597         if (nodeUnderMouse && nodeUnderMouse.isDescendant(this.crumbsElement))
    598             return;
    599 
    600         WebInspector.domAgent.hideDOMNodeHighlight();
    601 
    602         this._mouseOutOfCrumbsTimeout = setTimeout(this.updateBreadcrumbSizes.bind(this), 1000);
    603     },
    604 
    605     _updateBreadcrumbIfNeeded: function(event)
    606     {
    607         var name = event.data.name;
    608         if (name !== "class" && name !== "id")
    609             return;
    610 
    611         var node = /** @type {WebInspector.DOMNode} */ (event.data.node);
    612         var crumbs = this.crumbsElement;
    613         var crumb = crumbs.firstChild;
    614         while (crumb) {
    615             if (crumb.representedObject === node) {
    616                 this.updateBreadcrumb(true);
    617                 break;
    618             }
    619             crumb = crumb.nextSibling;
    620         }
    621     },
    622 
    623     /**
    624      * @param {boolean=} forceUpdate
    625      */
    626     updateBreadcrumb: function(forceUpdate)
    627     {
    628         if (!this.isShowing())
    629             return;
    630 
    631         var crumbs = this.crumbsElement;
    632 
    633         var handled = false;
    634         var crumb = crumbs.firstChild;
    635         while (crumb) {
    636             if (crumb.representedObject === this.selectedDOMNode()) {
    637                 crumb.addStyleClass("selected");
    638                 handled = true;
    639             } else {
    640                 crumb.removeStyleClass("selected");
    641             }
    642 
    643             crumb = crumb.nextSibling;
    644         }
    645 
    646         if (handled && !forceUpdate) {
    647             // We don't need to rebuild the crumbs, but we need to adjust sizes
    648             // to reflect the new focused or root node.
    649             this.updateBreadcrumbSizes();
    650             return;
    651         }
    652 
    653         crumbs.removeChildren();
    654 
    655         var panel = this;
    656 
    657         function selectCrumbFunction(event)
    658         {
    659             var crumb = event.currentTarget;
    660             if (crumb.hasStyleClass("collapsed")) {
    661                 // Clicking a collapsed crumb will expose the hidden crumbs.
    662                 if (crumb === panel.crumbsElement.firstChild) {
    663                     // If the focused crumb is the first child, pick the farthest crumb
    664                     // that is still hidden. This allows the user to expose every crumb.
    665                     var currentCrumb = crumb;
    666                     while (currentCrumb) {
    667                         var hidden = currentCrumb.hasStyleClass("hidden");
    668                         var collapsed = currentCrumb.hasStyleClass("collapsed");
    669                         if (!hidden && !collapsed)
    670                             break;
    671                         crumb = currentCrumb;
    672                         currentCrumb = currentCrumb.nextSibling;
    673                     }
    674                 }
    675 
    676                 panel.updateBreadcrumbSizes(crumb);
    677             } else
    678                 panel.selectDOMNode(crumb.representedObject, true);
    679 
    680             event.preventDefault();
    681         }
    682 
    683         for (var current = this.selectedDOMNode(); current; current = current.parentNode) {
    684             if (current.nodeType() === Node.DOCUMENT_NODE)
    685                 continue;
    686 
    687             crumb = document.createElement("span");
    688             crumb.className = "crumb";
    689             crumb.representedObject = current;
    690             crumb.addEventListener("mousedown", selectCrumbFunction, false);
    691 
    692             var crumbTitle;
    693             switch (current.nodeType()) {
    694                 case Node.ELEMENT_NODE:
    695                     WebInspector.DOMPresentationUtils.decorateNodeLabel(current, crumb);
    696                     break;
    697 
    698                 case Node.TEXT_NODE:
    699                     crumbTitle = WebInspector.UIString("(text)");
    700                     break
    701 
    702                 case Node.COMMENT_NODE:
    703                     crumbTitle = "<!-->";
    704                     break;
    705 
    706                 case Node.DOCUMENT_TYPE_NODE:
    707                     crumbTitle = "<!DOCTYPE>";
    708                     break;
    709 
    710                 default:
    711                     crumbTitle = current.nodeNameInCorrectCase();
    712             }
    713 
    714             if (!crumb.childNodes.length) {
    715                 var nameElement = document.createElement("span");
    716                 nameElement.textContent = crumbTitle;
    717                 crumb.appendChild(nameElement);
    718                 crumb.title = crumbTitle;
    719             }
    720 
    721             if (current === this.selectedDOMNode())
    722                 crumb.addStyleClass("selected");
    723             if (!crumbs.childNodes.length)
    724                 crumb.addStyleClass("end");
    725 
    726             crumbs.appendChild(crumb);
    727         }
    728 
    729         if (crumbs.hasChildNodes())
    730             crumbs.lastChild.addStyleClass("start");
    731 
    732         this.updateBreadcrumbSizes();
    733     },
    734 
    735     /**
    736      * @param {Element=} focusedCrumb
    737      */
    738     updateBreadcrumbSizes: function(focusedCrumb)
    739     {
    740         if (!this.isShowing())
    741             return;
    742 
    743         if (document.body.offsetWidth <= 0) {
    744             // The stylesheet hasn't loaded yet or the window is closed,
    745             // so we can't calculate what is need. Return early.
    746             return;
    747         }
    748 
    749         var crumbs = this.crumbsElement;
    750         if (!crumbs.childNodes.length || crumbs.offsetWidth <= 0)
    751             return; // No crumbs, do nothing.
    752 
    753         // A Zero index is the right most child crumb in the breadcrumb.
    754         var selectedIndex = 0;
    755         var focusedIndex = 0;
    756         var selectedCrumb;
    757 
    758         var i = 0;
    759         var crumb = crumbs.firstChild;
    760         while (crumb) {
    761             // Find the selected crumb and index.
    762             if (!selectedCrumb && crumb.hasStyleClass("selected")) {
    763                 selectedCrumb = crumb;
    764                 selectedIndex = i;
    765             }
    766 
    767             // Find the focused crumb index.
    768             if (crumb === focusedCrumb)
    769                 focusedIndex = i;
    770 
    771             // Remove any styles that affect size before
    772             // deciding to shorten any crumbs.
    773             if (crumb !== crumbs.lastChild)
    774                 crumb.removeStyleClass("start");
    775             if (crumb !== crumbs.firstChild)
    776                 crumb.removeStyleClass("end");
    777 
    778             crumb.removeStyleClass("compact");
    779             crumb.removeStyleClass("collapsed");
    780             crumb.removeStyleClass("hidden");
    781 
    782             crumb = crumb.nextSibling;
    783             ++i;
    784         }
    785 
    786         // Restore the start and end crumb classes in case they got removed in coalesceCollapsedCrumbs().
    787         // The order of the crumbs in the document is opposite of the visual order.
    788         crumbs.firstChild.addStyleClass("end");
    789         crumbs.lastChild.addStyleClass("start");
    790 
    791         var rightPadding = 20;
    792         var crumbsTotalOffsetLeft = crumbs.totalOffsetLeft();
    793         var windowInnerWidth = window.innerWidth;
    794         var errorWarningElement = document.getElementById("error-warning-count");
    795         if (!WebInspector.drawer.visible) {
    796             if (errorWarningElement)
    797                 rightPadding += errorWarningElement.offsetWidth;
    798             rightPadding += WebInspector.settingsController.statusBarItem.offsetWidth;
    799         }
    800 
    801         function crumbsAreSmallerThanContainer()
    802         {
    803             return (crumbsTotalOffsetLeft + crumbs.offsetWidth + rightPadding) < windowInnerWidth;
    804         }
    805 
    806         if (crumbsAreSmallerThanContainer())
    807             return; // No need to compact the crumbs, they all fit at full size.
    808 
    809         var BothSides = 0;
    810         var AncestorSide = -1;
    811         var ChildSide = 1;
    812 
    813         /**
    814          * @param {boolean=} significantCrumb
    815          */
    816         function makeCrumbsSmaller(shrinkingFunction, direction, significantCrumb)
    817         {
    818             if (!significantCrumb)
    819                 significantCrumb = (focusedCrumb || selectedCrumb);
    820 
    821             if (significantCrumb === selectedCrumb)
    822                 var significantIndex = selectedIndex;
    823             else if (significantCrumb === focusedCrumb)
    824                 var significantIndex = focusedIndex;
    825             else {
    826                 var significantIndex = 0;
    827                 for (var i = 0; i < crumbs.childNodes.length; ++i) {
    828                     if (crumbs.childNodes[i] === significantCrumb) {
    829                         significantIndex = i;
    830                         break;
    831                     }
    832                 }
    833             }
    834 
    835             function shrinkCrumbAtIndex(index)
    836             {
    837                 var shrinkCrumb = crumbs.childNodes[index];
    838                 if (shrinkCrumb && shrinkCrumb !== significantCrumb)
    839                     shrinkingFunction(shrinkCrumb);
    840                 if (crumbsAreSmallerThanContainer())
    841                     return true; // No need to compact the crumbs more.
    842                 return false;
    843             }
    844 
    845             // Shrink crumbs one at a time by applying the shrinkingFunction until the crumbs
    846             // fit in the container or we run out of crumbs to shrink.
    847             if (direction) {
    848                 // Crumbs are shrunk on only one side (based on direction) of the signifcant crumb.
    849                 var index = (direction > 0 ? 0 : crumbs.childNodes.length - 1);
    850                 while (index !== significantIndex) {
    851                     if (shrinkCrumbAtIndex(index))
    852                         return true;
    853                     index += (direction > 0 ? 1 : -1);
    854                 }
    855             } else {
    856                 // Crumbs are shrunk in order of descending distance from the signifcant crumb,
    857                 // with a tie going to child crumbs.
    858                 var startIndex = 0;
    859                 var endIndex = crumbs.childNodes.length - 1;
    860                 while (startIndex != significantIndex || endIndex != significantIndex) {
    861                     var startDistance = significantIndex - startIndex;
    862                     var endDistance = endIndex - significantIndex;
    863                     if (startDistance >= endDistance)
    864                         var index = startIndex++;
    865                     else
    866                         var index = endIndex--;
    867                     if (shrinkCrumbAtIndex(index))
    868                         return true;
    869                 }
    870             }
    871 
    872             // We are not small enough yet, return false so the caller knows.
    873             return false;
    874         }
    875 
    876         function coalesceCollapsedCrumbs()
    877         {
    878             var crumb = crumbs.firstChild;
    879             var collapsedRun = false;
    880             var newStartNeeded = false;
    881             var newEndNeeded = false;
    882             while (crumb) {
    883                 var hidden = crumb.hasStyleClass("hidden");
    884                 if (!hidden) {
    885                     var collapsed = crumb.hasStyleClass("collapsed");
    886                     if (collapsedRun && collapsed) {
    887                         crumb.addStyleClass("hidden");
    888                         crumb.removeStyleClass("compact");
    889                         crumb.removeStyleClass("collapsed");
    890 
    891                         if (crumb.hasStyleClass("start")) {
    892                             crumb.removeStyleClass("start");
    893                             newStartNeeded = true;
    894                         }
    895 
    896                         if (crumb.hasStyleClass("end")) {
    897                             crumb.removeStyleClass("end");
    898                             newEndNeeded = true;
    899                         }
    900 
    901                         continue;
    902                     }
    903 
    904                     collapsedRun = collapsed;
    905 
    906                     if (newEndNeeded) {
    907                         newEndNeeded = false;
    908                         crumb.addStyleClass("end");
    909                     }
    910                 } else
    911                     collapsedRun = true;
    912                 crumb = crumb.nextSibling;
    913             }
    914 
    915             if (newStartNeeded) {
    916                 crumb = crumbs.lastChild;
    917                 while (crumb) {
    918                     if (!crumb.hasStyleClass("hidden")) {
    919                         crumb.addStyleClass("start");
    920                         break;
    921                     }
    922                     crumb = crumb.previousSibling;
    923                 }
    924             }
    925         }
    926 
    927         function compact(crumb)
    928         {
    929             if (crumb.hasStyleClass("hidden"))
    930                 return;
    931             crumb.addStyleClass("compact");
    932         }
    933 
    934         function collapse(crumb, dontCoalesce)
    935         {
    936             if (crumb.hasStyleClass("hidden"))
    937                 return;
    938             crumb.addStyleClass("collapsed");
    939             crumb.removeStyleClass("compact");
    940             if (!dontCoalesce)
    941                 coalesceCollapsedCrumbs();
    942         }
    943 
    944         if (!focusedCrumb) {
    945             // When not focused on a crumb we can be biased and collapse less important
    946             // crumbs that the user might not care much about.
    947 
    948             // Compact child crumbs.
    949             if (makeCrumbsSmaller(compact, ChildSide))
    950                 return;
    951 
    952             // Collapse child crumbs.
    953             if (makeCrumbsSmaller(collapse, ChildSide))
    954                 return;
    955         }
    956 
    957         // Compact ancestor crumbs, or from both sides if focused.
    958         if (makeCrumbsSmaller(compact, (focusedCrumb ? BothSides : AncestorSide)))
    959             return;
    960 
    961         // Collapse ancestor crumbs, or from both sides if focused.
    962         if (makeCrumbsSmaller(collapse, (focusedCrumb ? BothSides : AncestorSide)))
    963             return;
    964 
    965         if (!selectedCrumb)
    966             return;
    967 
    968         // Compact the selected crumb.
    969         compact(selectedCrumb);
    970         if (crumbsAreSmallerThanContainer())
    971             return;
    972 
    973         // Collapse the selected crumb as a last resort. Pass true to prevent coalescing.
    974         collapse(selectedCrumb, true);
    975     },
    976 
    977     /**
    978      * @param {boolean=} forceUpdate
    979      */
    980     updateStyles: function(forceUpdate)
    981     {
    982         var stylesSidebarPane = this.sidebarPanes.styles;
    983         var computedStylePane = this.sidebarPanes.computedStyle;
    984         if ((!stylesSidebarPane.isShowing() && !computedStylePane.isShowing()) || !stylesSidebarPane.needsUpdate)
    985             return;
    986 
    987         stylesSidebarPane.update(this.selectedDOMNode(), forceUpdate);
    988         stylesSidebarPane.needsUpdate = false;
    989     },
    990 
    991     updateMetrics: function()
    992     {
    993         var metricsSidebarPane = this.sidebarPanes.metrics;
    994         if (!metricsSidebarPane.isShowing() || !metricsSidebarPane.needsUpdate)
    995             return;
    996 
    997         metricsSidebarPane.update(this.selectedDOMNode());
    998         metricsSidebarPane.needsUpdate = false;
    999     },
   1000 
   1001     updateProperties: function()
   1002     {
   1003         var propertiesSidebarPane = this.sidebarPanes.properties;
   1004         if (!propertiesSidebarPane.isShowing() || !propertiesSidebarPane.needsUpdate)
   1005             return;
   1006 
   1007         propertiesSidebarPane.update(this.selectedDOMNode());
   1008         propertiesSidebarPane.needsUpdate = false;
   1009     },
   1010 
   1011     updateEventListeners: function()
   1012     {
   1013         var eventListenersSidebarPane = this.sidebarPanes.eventListeners;
   1014         if (!eventListenersSidebarPane.isShowing() || !eventListenersSidebarPane.needsUpdate)
   1015             return;
   1016 
   1017         eventListenersSidebarPane.update(this.selectedDOMNode());
   1018         eventListenersSidebarPane.needsUpdate = false;
   1019     },
   1020 
   1021     handleShortcut: function(event)
   1022     {
   1023         function handleUndoRedo()
   1024         {
   1025             if (WebInspector.KeyboardShortcut.eventHasCtrlOrMeta(event) && !event.shiftKey && event.keyIdentifier === "U+005A") { // Z key
   1026                 WebInspector.domAgent.undo(this._updateSidebars.bind(this));
   1027                 event.handled = true;
   1028                 return;
   1029             }
   1030 
   1031             var isRedoKey = WebInspector.isMac() ? event.metaKey && event.shiftKey && event.keyIdentifier === "U+005A" : // Z key
   1032                                                    event.ctrlKey && event.keyIdentifier === "U+0059"; // Y key
   1033             if (isRedoKey) {
   1034                 DOMAgent.redo(this._updateSidebars.bind(this));
   1035                 event.handled = true;
   1036             }
   1037         }
   1038 
   1039         if (!this.treeOutline.editing()) {
   1040             handleUndoRedo.call(this);
   1041             if (event.handled)
   1042                 return;
   1043         }
   1044 
   1045         this.treeOutline.handleShortcut(event);
   1046     },
   1047 
   1048     handleCopyEvent: function(event)
   1049     {
   1050         var currentFocusElement = WebInspector.currentFocusElement();
   1051         if (currentFocusElement && WebInspector.isBeingEdited(currentFocusElement))
   1052             return;
   1053 
   1054         // Don't prevent the normal copy if the user has a selection.
   1055         if (!window.getSelection().isCollapsed)
   1056             return;
   1057         event.clipboardData.clearData();
   1058         event.preventDefault();
   1059         this.selectedDOMNode().copyNode();
   1060     },
   1061 
   1062     sidebarResized: function(event)
   1063     {
   1064         this.treeOutline.updateSelection();
   1065     },
   1066 
   1067     revealAndSelectNode: function(nodeId)
   1068     {
   1069         WebInspector.inspectorView.setCurrentPanel(this);
   1070 
   1071         var node = WebInspector.domAgent.nodeForId(nodeId);
   1072         if (!node)
   1073             return;
   1074 
   1075         while (!WebInspector.ElementsTreeOutline.showShadowDOM() && node && node.isInShadowTree())
   1076             node = node.parentNode;
   1077 
   1078         WebInspector.domAgent.highlightDOMNodeForTwoSeconds(nodeId);
   1079         this.selectDOMNode(node, true);
   1080     },
   1081 
   1082     /**
   1083      * @param {WebInspector.ContextMenu} contextMenu
   1084      * @param {Object} target
   1085      */
   1086     appendApplicableItems: function(event, contextMenu, target)
   1087     {
   1088         if (!(target instanceof WebInspector.RemoteObject))
   1089             return;
   1090         var remoteObject = /** @type {WebInspector.RemoteObject} */ (target);
   1091         if (remoteObject.subtype !== "node")
   1092             return;
   1093 
   1094         function selectNode(nodeId)
   1095         {
   1096             if (nodeId)
   1097                 WebInspector.domAgent.inspectElement(nodeId);
   1098         }
   1099 
   1100         function revealElement()
   1101         {
   1102             remoteObject.pushNodeToFrontend(selectNode);
   1103         }
   1104 
   1105         contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Elements panel" : "Reveal in Elements Panel"), revealElement.bind(this));
   1106     },
   1107 
   1108     _sidebarContextMenuEventFired: function(event)
   1109     {
   1110         var contextMenu = new WebInspector.ContextMenu(event);
   1111         contextMenu.show();
   1112     },
   1113 
   1114     _dockSideChanged: function()
   1115     {
   1116         var dockSide = WebInspector.dockController.dockSide();
   1117         var vertically = dockSide === WebInspector.DockController.State.DockedToRight && WebInspector.settings.splitVerticallyWhenDockedToRight.get();
   1118         this._splitVertically(vertically);
   1119     },
   1120 
   1121     _showShadowDOMChanged: function()
   1122     {
   1123         this.treeOutline.update();
   1124     },
   1125 
   1126     /**
   1127      * @param {boolean} vertically
   1128      */
   1129     _splitVertically: function(vertically)
   1130     {
   1131         if (this.sidebarPaneView && vertically === !this.splitView.isVertical())
   1132             return;
   1133 
   1134         if (this.sidebarPaneView)
   1135             this.sidebarPaneView.detach();
   1136 
   1137         this.splitView.setVertical(!vertically);
   1138 
   1139         var computedPane = new WebInspector.SidebarPane(WebInspector.UIString("Computed"));
   1140         computedPane.element.addStyleClass("composite");
   1141         computedPane.element.addStyleClass("fill");
   1142         var expandComputed = computedPane.expand.bind(computedPane);
   1143 
   1144         computedPane.bodyElement.appendChild(this.sidebarPanes.computedStyle.titleElement);
   1145         computedPane.bodyElement.addStyleClass("metrics-and-computed");
   1146         this.sidebarPanes.computedStyle.show(computedPane.bodyElement);
   1147         this.sidebarPanes.computedStyle.setExpandCallback(expandComputed);
   1148 
   1149         if (vertically) {
   1150             this.sidebarPanes.metrics.show(computedPane.bodyElement, this.sidebarPanes.computedStyle.element);
   1151             this.sidebarPanes.metrics.setExpandCallback(expandComputed);
   1152 
   1153             this.sidebarPaneView = new WebInspector.SidebarTabbedPane();
   1154 
   1155             var compositePane = new WebInspector.SidebarPane(this.sidebarPanes.styles.title());
   1156             compositePane.element.addStyleClass("composite");
   1157             compositePane.element.addStyleClass("fill");
   1158             var expandComposite = compositePane.expand.bind(compositePane);
   1159 
   1160             var splitView = new WebInspector.SplitView(true, "StylesPaneSplitRatio", 0.5);
   1161             splitView.show(compositePane.bodyElement);
   1162 
   1163             this.sidebarPanes.styles.show(splitView.firstElement());
   1164             splitView.firstElement().appendChild(this.sidebarPanes.styles.titleElement);
   1165             this.sidebarPanes.styles.setExpandCallback(expandComposite);
   1166 
   1167             computedPane.show(splitView.secondElement());
   1168             computedPane.setExpandCallback(expandComposite);
   1169 
   1170             this.sidebarPaneView.addPane(compositePane);
   1171             this.sidebarPaneView.addPane(this.sidebarPanes.properties);
   1172             this.sidebarPaneView.addPane(this.sidebarPanes.domBreakpoints);
   1173             this.sidebarPaneView.addPane(this.sidebarPanes.eventListeners);
   1174         } else {
   1175             this.sidebarPaneView = new WebInspector.SidebarTabbedPane();
   1176 
   1177             var stylesPane = new WebInspector.SidebarPane(this.sidebarPanes.styles.title());
   1178             stylesPane.element.addStyleClass("composite");
   1179             stylesPane.element.addStyleClass("fill");
   1180             var expandStyles = stylesPane.expand.bind(stylesPane);
   1181             stylesPane.bodyElement.addStyleClass("metrics-and-styles");
   1182             this.sidebarPanes.styles.show(stylesPane.bodyElement);
   1183             this.sidebarPanes.styles.setExpandCallback(expandStyles);
   1184             this.sidebarPanes.metrics.setExpandCallback(expandStyles);
   1185             stylesPane.bodyElement.appendChild(this.sidebarPanes.styles.titleElement);
   1186 
   1187             /**
   1188              * @param {WebInspector.SidebarPane} pane
   1189              * @param {Element=} beforeElement
   1190              */
   1191             function showMetrics(pane, beforeElement)
   1192             {
   1193                 this.sidebarPanes.metrics.show(pane.bodyElement, beforeElement);
   1194             }
   1195 
   1196             /**
   1197              * @param {WebInspector.Event} event
   1198              */
   1199             function tabSelected(event)
   1200             {
   1201                 var tabId = /** @type {string} */ (event.data.tabId);
   1202                 if (tabId === computedPane.title())
   1203                     showMetrics.call(this, computedPane, this.sidebarPanes.computedStyle.element);
   1204                 if (tabId === stylesPane.title())
   1205                     showMetrics.call(this, stylesPane);
   1206             }
   1207 
   1208             this.sidebarPaneView.addEventListener(WebInspector.TabbedPane.EventTypes.TabSelected, tabSelected, this);
   1209 
   1210             showMetrics.call(this, stylesPane);
   1211             this.sidebarPaneView.addPane(stylesPane);
   1212             this.sidebarPaneView.addPane(computedPane);
   1213 
   1214             this.sidebarPaneView.addPane(this.sidebarPanes.eventListeners);
   1215             this.sidebarPaneView.addPane(this.sidebarPanes.domBreakpoints);
   1216             this.sidebarPaneView.addPane(this.sidebarPanes.properties);
   1217         }
   1218 
   1219         this.sidebarPaneView.show(this.splitView.sidebarElement);
   1220         this.sidebarPanes.styles.expand();
   1221     },
   1222 
   1223     /**
   1224      * @param {string} id
   1225      * @param {WebInspector.SidebarPane} pane
   1226      */
   1227     addExtensionSidebarPane: function(id, pane)
   1228     {
   1229         this.sidebarPanes[id] = pane;
   1230         this.sidebarPaneView.addPane(pane);
   1231     },
   1232 
   1233     __proto__: WebInspector.Panel.prototype
   1234 }
   1235