ElementsPanel.js 45 KB

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