1/* 2 * Copyright (C) 2013 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26WebInspector.NavigationSidebarPanel = function(identifier, displayName, image, keyboardShortcutKey, autoPruneOldTopLevelResourceTreeElements, autoHideToolbarItemWhenEmpty, wantsTopOverflowShadow, element, role, label) { 27 if (keyboardShortcutKey) 28 this._keyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.Control, keyboardShortcutKey, this.toggle.bind(this)); 29 30 if (this._keyboardShortcut) { 31 var showToolTip = WebInspector.UIString("Show the %s navigation sidebar (%s)").format(displayName, this._keyboardShortcut.displayName); 32 var hideToolTip = WebInspector.UIString("Hide the %s navigation sidebar (%s)").format(displayName, this._keyboardShortcut.displayName); 33 } else { 34 var showToolTip = WebInspector.UIString("Show the %s navigation sidebar").format(displayName); 35 var hideToolTip = WebInspector.UIString("Hide the %s navigation sidebar").format(displayName); 36 } 37 38 WebInspector.SidebarPanel.call(this, identifier, displayName, showToolTip, hideToolTip, image, element, role, label || displayName); 39 40 this.element.classList.add(WebInspector.NavigationSidebarPanel.StyleClassName); 41 42 this._autoHideToolbarItemWhenEmpty = autoHideToolbarItemWhenEmpty || false; 43 44 if (autoHideToolbarItemWhenEmpty) 45 this.toolbarItem.hidden = true; 46 47 this._visibleContentTreeOutlines = new Set; 48 49 this._contentElement = document.createElement("div"); 50 this._contentElement.className = WebInspector.NavigationSidebarPanel.ContentElementStyleClassName; 51 this._contentElement.addEventListener("scroll", this._updateContentOverflowShadowVisibility.bind(this)); 52 this.element.appendChild(this._contentElement); 53 54 this._contentTreeOutline = this.createContentTreeOutline(true); 55 56 this._filterBar = new WebInspector.FilterBar(); 57 this._filterBar.addEventListener(WebInspector.FilterBar.Event.TextFilterDidChange, this._textFilterDidChange, this); 58 this.element.appendChild(this._filterBar.element); 59 60 this._bottomOverflowShadowElement = document.createElement("div"); 61 this._bottomOverflowShadowElement.className = WebInspector.NavigationSidebarPanel.OverflowShadowElementStyleClassName; 62 this.element.appendChild(this._bottomOverflowShadowElement); 63 64 if (wantsTopOverflowShadow) { 65 this._topOverflowShadowElement = document.createElement("div"); 66 this._topOverflowShadowElement.classList.add(WebInspector.NavigationSidebarPanel.OverflowShadowElementStyleClassName); 67 this._topOverflowShadowElement.classList.add(WebInspector.NavigationSidebarPanel.TopOverflowShadowElementStyleClassName); 68 this.element.appendChild(this._topOverflowShadowElement); 69 } 70 71 window.addEventListener("resize", this._updateContentOverflowShadowVisibility.bind(this)); 72 73 this._filtersSetting = new WebInspector.Setting(identifier + "-navigation-sidebar-filters", {}); 74 this._filterBar.filters = this._filtersSetting.value; 75 76 this._emptyContentPlaceholderElement = document.createElement("div"); 77 this._emptyContentPlaceholderElement.className = WebInspector.NavigationSidebarPanel.EmptyContentPlaceholderElementStyleClassName; 78 79 this._emptyContentPlaceholderMessageElement = document.createElement("div"); 80 this._emptyContentPlaceholderMessageElement.className = WebInspector.NavigationSidebarPanel.EmptyContentPlaceholderMessageElementStyleClassName; 81 this._emptyContentPlaceholderElement.appendChild(this._emptyContentPlaceholderMessageElement); 82 83 this._generateStyleRulesIfNeeded(); 84 this._generateDisclosureTrianglesIfNeeded(); 85 86 if (autoPruneOldTopLevelResourceTreeElements) { 87 WebInspector.Frame.addEventListener(WebInspector.Frame.Event.MainResourceDidChange, this._checkForOldResources, this); 88 WebInspector.Frame.addEventListener(WebInspector.Frame.Event.ChildFrameWasRemoved, this._checkForOldResources, this); 89 WebInspector.Frame.addEventListener(WebInspector.Frame.Event.ResourceWasRemoved, this._checkForOldResources, this); 90 } 91}; 92 93WebInspector.NavigationSidebarPanel.StyleClassName = "navigation"; 94WebInspector.NavigationSidebarPanel.OverflowShadowElementStyleClassName = "overflow-shadow"; 95WebInspector.NavigationSidebarPanel.TopOverflowShadowElementStyleClassName = "top"; 96WebInspector.NavigationSidebarPanel.ContentElementStyleClassName = "content"; 97WebInspector.NavigationSidebarPanel.ContentTreeOutlineElementHiddenStyleClassName = "hidden"; 98WebInspector.NavigationSidebarPanel.ContentTreeOutlineElementStyleClassName = "navigation-sidebar-panel-content-tree-outline"; 99WebInspector.NavigationSidebarPanel.HideDisclosureButtonsStyleClassName = "hide-disclosure-buttons"; 100WebInspector.NavigationSidebarPanel.EmptyContentPlaceholderElementStyleClassName = "empty-content-placeholder"; 101WebInspector.NavigationSidebarPanel.EmptyContentPlaceholderMessageElementStyleClassName = "message"; 102WebInspector.NavigationSidebarPanel.DisclosureTriangleOpenCanvasIdentifier = "navigation-sidebar-panel-disclosure-triangle-open"; 103WebInspector.NavigationSidebarPanel.DisclosureTriangleClosedCanvasIdentifier = "navigation-sidebar-panel-disclosure-triangle-closed"; 104WebInspector.NavigationSidebarPanel.DisclosureTriangleNormalCanvasIdentifierSuffix = "-normal"; 105WebInspector.NavigationSidebarPanel.DisclosureTriangleSelectedCanvasIdentifierSuffix = "-selected"; 106 107WebInspector.NavigationSidebarPanel.prototype = { 108 constructor: WebInspector.NavigationSidebarPanel, 109 110 // Public 111 112 get contentElement() 113 { 114 return this._contentElement; 115 }, 116 117 get contentTreeOutlineElement() 118 { 119 return this._contentTreeOutline.element; 120 }, 121 122 get contentTreeOutline() 123 { 124 return this._contentTreeOutline; 125 }, 126 127 set contentTreeOutline(newTreeOutline) 128 { 129 console.assert(newTreeOutline); 130 if (!newTreeOutline) 131 return; 132 133 if (this._contentTreeOutline) 134 this._contentTreeOutline.element.classList.add(WebInspector.NavigationSidebarPanel.ContentTreeOutlineElementHiddenStyleClassName); 135 136 this._contentTreeOutline = newTreeOutline; 137 this._contentTreeOutline.element.classList.remove(WebInspector.NavigationSidebarPanel.ContentTreeOutlineElementHiddenStyleClassName); 138 139 this._visibleContentTreeOutlines.delete(this._contentTreeOutline); 140 this._visibleContentTreeOutlines.add(newTreeOutline); 141 142 this._updateFilter(); 143 }, 144 145 get contentTreeOutlineToAutoPrune() 146 { 147 return this._contentTreeOutline; 148 }, 149 150 get hasSelectedElement() 151 { 152 return !!this._contentTreeOutline.selectedTreeElement; 153 }, 154 155 get filterBar() 156 { 157 return this._filterBar; 158 }, 159 160 get restoringState() 161 { 162 return this._restoringState; 163 }, 164 165 createContentTreeOutline: function(dontHideByDefault, suppressFiltering) 166 { 167 var contentTreeOutlineElement = document.createElement("ol"); 168 contentTreeOutlineElement.className = WebInspector.NavigationSidebarPanel.ContentTreeOutlineElementStyleClassName; 169 if (!dontHideByDefault) 170 contentTreeOutlineElement.classList.add(WebInspector.NavigationSidebarPanel.ContentTreeOutlineElementHiddenStyleClassName); 171 this._contentElement.appendChild(contentTreeOutlineElement); 172 173 var contentTreeOutline = new TreeOutline(contentTreeOutlineElement); 174 contentTreeOutline.allowsRepeatSelection = true; 175 176 if (!suppressFiltering) { 177 contentTreeOutline.onadd = this._treeElementAddedOrChanged.bind(this); 178 contentTreeOutline.onchange = this._treeElementAddedOrChanged.bind(this); 179 contentTreeOutline.onexpand = this._treeElementExpandedOrCollapsed.bind(this); 180 contentTreeOutline.oncollapse = this._treeElementExpandedOrCollapsed.bind(this); 181 } 182 183 if (dontHideByDefault) 184 this._visibleContentTreeOutlines.add(contentTreeOutline); 185 186 return contentTreeOutline; 187 }, 188 189 treeElementForRepresentedObject: function(representedObject) 190 { 191 return this._contentTreeOutline.getCachedTreeElement(representedObject); 192 }, 193 194 showDefaultContentView: function() 195 { 196 // Implemneted by subclasses if needed to show a content view when no existing tree element is selected. 197 }, 198 199 showContentViewForCurrentSelection: function() 200 { 201 // Reselect the selected tree element to cause the content view to be shown as well. <rdar://problem/10854727> 202 var selectedTreeElement = this._contentTreeOutline.selectedTreeElement; 203 if (selectedTreeElement) 204 selectedTreeElement.select(); 205 }, 206 207 saveStateToCookie: function(cookie) 208 { 209 console.assert(cookie); 210 211 // This does not save folder selections, which lack a represented object and content view. 212 var selectedTreeElement = null; 213 this._visibleContentTreeOutlines.forEach(function(outline) { 214 if (outline.selectedTreeElement) 215 selectedTreeElement = outline.selectedTreeElement; 216 }); 217 218 if (!selectedTreeElement) 219 return; 220 221 if (this._isTreeElementWithoutRepresentedObject(selectedTreeElement)) 222 return; 223 224 var representedObject = selectedTreeElement.representedObject; 225 cookie[WebInspector.TypeIdentifierCookieKey] = representedObject.constructor.TypeIdentifier; 226 227 if (representedObject.saveIdentityToCookie) 228 representedObject.saveIdentityToCookie(cookie); 229 else 230 console.error("Error: TreeElement.representedObject is missing a saveIdentityToCookie implementation. TreeElement.constructor: %s", selectedTreeElement.constructor); 231 }, 232 233 // This can be supplemented by subclasses that admit a simpler strategy for static tree elements. 234 restoreStateFromCookie: function(cookie, relaxedMatchDelay) 235 { 236 this._pendingViewStateCookie = cookie; 237 this._restoringState = true; 238 239 // Check if any existing tree elements in any outline match the cookie. 240 this._checkOutlinesForPendingViewStateCookie(); 241 242 if (this._finalAttemptToRestoreViewStateTimeout) 243 clearTimeout(this._finalAttemptToRestoreViewStateTimeout); 244 245 function finalAttemptToRestoreViewStateFromCookie() 246 { 247 delete this._finalAttemptToRestoreViewStateTimeout; 248 249 this._checkOutlinesForPendingViewStateCookie(true); 250 251 delete this._pendingViewStateCookie; 252 delete this._restoringState; 253 } 254 255 // If the specific tree element wasn't found, we may need to wait for the resources 256 // to be registered. We try one last time (match type only) after an arbitrary amount of timeout. 257 this._finalAttemptToRestoreViewStateTimeout = setTimeout(finalAttemptToRestoreViewStateFromCookie.bind(this), relaxedMatchDelay); 258 }, 259 260 showEmptyContentPlaceholder: function(message, hideToolbarItem) 261 { 262 console.assert(message); 263 264 if (this._emptyContentPlaceholderElement.parentNode && this._emptyContentPlaceholderMessageElement.textContent === message) 265 return; 266 267 this._emptyContentPlaceholderMessageElement.textContent = message; 268 this.element.appendChild(this._emptyContentPlaceholderElement); 269 270 this._hideToolbarItemWhenEmpty = hideToolbarItem || false; 271 this._updateToolbarItemVisibility(); 272 this._updateContentOverflowShadowVisibility(); 273 }, 274 275 hideEmptyContentPlaceholder: function() 276 { 277 if (!this._emptyContentPlaceholderElement.parentNode) 278 return; 279 280 this._emptyContentPlaceholderElement.parentNode.removeChild(this._emptyContentPlaceholderElement); 281 282 this._hideToolbarItemWhenEmpty = false; 283 this._updateToolbarItemVisibility(); 284 this._updateContentOverflowShadowVisibility(); 285 }, 286 287 updateEmptyContentPlaceholder: function(message) 288 { 289 this._updateToolbarItemVisibility(); 290 291 if (!this._contentTreeOutline.children.length) { 292 // No tree elements, so no results. 293 this.showEmptyContentPlaceholder(message); 294 } else if (!this._emptyFilterResults) { 295 // There are tree elements, and not all of them are hidden by the filter. 296 this.hideEmptyContentPlaceholder(); 297 } 298 }, 299 300 updateFilter: function() 301 { 302 this._updateFilter(); 303 }, 304 305 hasCustomFilters: function() 306 { 307 // Implemented by subclasses if needed. 308 return false; 309 }, 310 311 matchTreeElementAgainstCustomFilters: function(treeElement) 312 { 313 // Implemented by subclasses if needed. 314 return true; 315 }, 316 317 applyFiltersToTreeElement: function(treeElement) 318 { 319 if (!this._filterBar.hasActiveFilters() && !this.hasCustomFilters()) { 320 // No filters, so make everything visible. 321 treeElement.hidden = false; 322 323 // If this tree element was expanded during filtering, collapse it again. 324 if (treeElement.expanded && treeElement.__wasExpandedDuringFiltering) { 325 delete treeElement.__wasExpandedDuringFiltering; 326 treeElement.collapse(); 327 } 328 329 return; 330 } 331 332 var filterableData = treeElement.filterableData || {}; 333 334 var matchedBuiltInFilters = false; 335 336 var self = this; 337 function matchTextFilter(inputs) 338 { 339 if (!inputs || !self._textFilterRegex) 340 return true; 341 342 // Convert to a single item array if needed. 343 if (!(inputs instanceof Array)) 344 inputs = [inputs]; 345 346 // Loop over all the inputs and try to match them. 347 for (var input of inputs) { 348 if (!input) 349 continue; 350 if (self._textFilterRegex.test(input)) { 351 matchedBuiltInFilters = true; 352 return true; 353 } 354 } 355 356 // No inputs matched. 357 return false; 358 } 359 360 function makeVisible() 361 { 362 // Make this element visible. 363 treeElement.hidden = false; 364 365 // Make the ancestors visible and expand them. 366 var currentAncestor = treeElement.parent; 367 while (currentAncestor && !currentAncestor.root) { 368 currentAncestor.hidden = false; 369 370 // Only expand if the built-in filters matched, not custom filters. 371 if (matchedBuiltInFilters && !currentAncestor.expanded) { 372 currentAncestor.__wasExpandedDuringFiltering = true; 373 currentAncestor.expand(); 374 } 375 376 currentAncestor = currentAncestor.parent; 377 } 378 } 379 380 if (matchTextFilter(filterableData.text) && this.matchTreeElementAgainstCustomFilters(treeElement)) { 381 // Make this element visible since it matches. 382 makeVisible(); 383 384 // If this tree element didn't match a built-in filter and was expanded earlier during filtering, collapse it again. 385 if (!matchedBuiltInFilters && treeElement.expanded && treeElement.__wasExpandedDuringFiltering) { 386 delete treeElement.__wasExpandedDuringFiltering; 387 treeElement.collapse(); 388 } 389 390 return; 391 } 392 393 // Make this element invisible since it does not match. 394 treeElement.hidden = true; 395 }, 396 397 show: function() 398 { 399 if (!this.parentSidebar) 400 return; 401 402 WebInspector.SidebarPanel.prototype.show.call(this); 403 404 this.contentTreeOutlineElement.focus(); 405 }, 406 407 shown: function() 408 { 409 WebInspector.SidebarPanel.prototype.shown.call(this); 410 411 this._updateContentOverflowShadowVisibility(); 412 413 // Force the navigation item to be visible. This makes sure it is 414 // always visible when the panel is shown. 415 this.toolbarItem.hidden = false; 416 }, 417 418 hidden: function() 419 { 420 WebInspector.SidebarPanel.prototype.hidden.call(this); 421 422 this._updateToolbarItemVisibility(); 423 }, 424 425 // Private 426 427 _updateContentOverflowShadowVisibilitySoon: function() 428 { 429 if (this._updateContentOverflowShadowVisibilityIdentifier) 430 return; 431 432 this._updateContentOverflowShadowVisibilityIdentifier = setTimeout(this._updateContentOverflowShadowVisibility.bind(this), 0); 433 }, 434 435 _updateContentOverflowShadowVisibility: function() 436 { 437 delete this._updateContentOverflowShadowVisibilityIdentifier; 438 439 var scrollHeight = this._contentElement.scrollHeight; 440 var offsetHeight = this._contentElement.offsetHeight; 441 442 if (scrollHeight < offsetHeight) { 443 if (this._topOverflowShadowElement) 444 this._topOverflowShadowElement.style.opacity = 0; 445 this._bottomOverflowShadowElement.style.opacity = 0; 446 return; 447 } 448 449 if (WebInspector.Platform.isLegacyMacOS) 450 const edgeThreshold = 10; 451 else 452 const edgeThreshold = 1; 453 454 var scrollTop = this._contentElement.scrollTop; 455 456 var topCoverage = Math.min(scrollTop, edgeThreshold); 457 var bottomCoverage = Math.max(0, (offsetHeight + scrollTop) - (scrollHeight - edgeThreshold)); 458 459 if (this._topOverflowShadowElement) 460 this._topOverflowShadowElement.style.opacity = (topCoverage / edgeThreshold).toFixed(1); 461 this._bottomOverflowShadowElement.style.opacity = (1 - (bottomCoverage / edgeThreshold)).toFixed(1); 462 }, 463 464 _updateToolbarItemVisibility: function() 465 { 466 // Hide the navigation item if requested or auto-hiding and we are not visible and we are empty. 467 var shouldHide = ((this._hideToolbarItemWhenEmpty || this._autoHideToolbarItemWhenEmpty) && !this.selected && !this._contentTreeOutline.children.length); 468 this.toolbarItem.hidden = shouldHide; 469 }, 470 471 _checkForEmptyFilterResults: function() 472 { 473 // No tree elements, so don't touch the empty content placeholder. 474 if (!this._contentTreeOutline.children.length) 475 return; 476 477 // Iterate over all the top level tree elements. If any are visible, return early. 478 var currentTreeElement = this._contentTreeOutline.children[0]; 479 while (currentTreeElement) { 480 if (!currentTreeElement.hidden) { 481 // Not hidden, so hide any empty content message. 482 this.hideEmptyContentPlaceholder(); 483 this._emptyFilterResults = false; 484 return; 485 } 486 487 currentTreeElement = currentTreeElement.nextSibling; 488 } 489 490 // All top level tree elements are hidden, so filtering hid everything. Show a message. 491 this.showEmptyContentPlaceholder(WebInspector.UIString("No Filter Results")); 492 this._emptyFilterResults = true; 493 }, 494 495 _textFilterDidChange: function() 496 { 497 this._updateFilter(); 498 }, 499 500 _updateFilter: function() 501 { 502 var filters = this._filterBar.filters; 503 this._textFilterRegex = simpleGlobStringToRegExp(filters.text, "i"); 504 this._filtersSetting.value = filters; 505 506 // Don't populate if we don't have any active filters. 507 // We only need to populate when a filter needs to reveal. 508 var dontPopulate = !this._filterBar.hasActiveFilters(); 509 510 // Update the whole tree. 511 var currentTreeElement = this._contentTreeOutline.children[0]; 512 while (currentTreeElement && !currentTreeElement.root) { 513 this.applyFiltersToTreeElement(currentTreeElement); 514 currentTreeElement = currentTreeElement.traverseNextTreeElement(false, null, dontPopulate); 515 } 516 517 this._checkForEmptyFilterResults(); 518 this._updateContentOverflowShadowVisibility(); 519 }, 520 521 _treeElementAddedOrChanged: function(treeElement) 522 { 523 // Don't populate if we don't have any active filters. 524 // We only need to populate when a filter needs to reveal. 525 var dontPopulate = !this._filterBar.hasActiveFilters(); 526 527 // Apply the filters to the tree element and its descendants. 528 var currentTreeElement = treeElement; 529 while (currentTreeElement && !currentTreeElement.root) { 530 this.applyFiltersToTreeElement(currentTreeElement); 531 currentTreeElement = currentTreeElement.traverseNextTreeElement(false, treeElement, dontPopulate); 532 } 533 534 this._checkForEmptyFilterResults(); 535 this._updateContentOverflowShadowVisibilitySoon(); 536 this._checkElementsForPendingViewStateCookie(treeElement); 537 }, 538 539 _treeElementExpandedOrCollapsed: function(treeElement) 540 { 541 this._updateContentOverflowShadowVisibility(); 542 }, 543 544 _generateStyleRulesIfNeeded: function() 545 { 546 if (WebInspector.NavigationSidebarPanel._styleElement) 547 return; 548 549 WebInspector.NavigationSidebarPanel._styleElement = document.createElement("style"); 550 551 const maximumSidebarTreeDepth = 32; 552 const baseLeftPadding = 5; // Matches the padding in NavigationSidebarPanel.css for the item class. Keep in sync. 553 const depthPadding = 10; 554 555 var styleText = ""; 556 var childrenSubstring = ""; 557 for (var i = 1; i <= maximumSidebarTreeDepth; ++i) { 558 // Keep all the elements at the same depth once the maximum is reached. 559 childrenSubstring += i === maximumSidebarTreeDepth ? " .children" : " > .children"; 560 styleText += "." + WebInspector.NavigationSidebarPanel.ContentTreeOutlineElementStyleClassName + childrenSubstring + " > .item { "; 561 styleText += "padding-left: " + (baseLeftPadding + (depthPadding * i)) + "px; }\n"; 562 } 563 564 WebInspector.NavigationSidebarPanel._styleElement.textContent = styleText; 565 566 document.head.appendChild(WebInspector.NavigationSidebarPanel._styleElement); 567 }, 568 569 _generateDisclosureTrianglesIfNeeded: function() 570 { 571 if (WebInspector.NavigationSidebarPanel._generatedDisclosureTriangles) 572 return; 573 574 // Set this early instead of in _generateDisclosureTriangle because we don't want multiple panels that are 575 // created at the same time to duplicate the work (even though it would be harmless.) 576 WebInspector.NavigationSidebarPanel._generatedDisclosureTriangles = true; 577 578 var specifications = {}; 579 580 if (WebInspector.Platform.isLegacyMacOS) { 581 specifications[WebInspector.NavigationSidebarPanel.DisclosureTriangleNormalCanvasIdentifierSuffix] = { 582 fillColor: [112, 126, 139], 583 shadowColor: [255, 255, 255, 0.8], 584 shadowOffsetX: 0, 585 shadowOffsetY: 1, 586 shadowBlur: 0 587 }; 588 589 specifications[WebInspector.NavigationSidebarPanel.DisclosureTriangleSelectedCanvasIdentifierSuffix] = { 590 fillColor: [255, 255, 255], 591 shadowColor: [61, 91, 110, 0.8], 592 shadowOffsetX: 0, 593 shadowOffsetY: 1, 594 shadowBlur: 2 595 }; 596 } else { 597 specifications[WebInspector.NavigationSidebarPanel.DisclosureTriangleNormalCanvasIdentifierSuffix] = { 598 fillColor: [140, 140, 140] 599 }; 600 601 specifications[WebInspector.NavigationSidebarPanel.DisclosureTriangleSelectedCanvasIdentifierSuffix] = { 602 fillColor: [255, 255, 255] 603 }; 604 } 605 606 generateColoredImagesForCSS("Images/DisclosureTriangleSmallOpen.svg", specifications, 13, 13, WebInspector.NavigationSidebarPanel.DisclosureTriangleOpenCanvasIdentifier); 607 generateColoredImagesForCSS("Images/DisclosureTriangleSmallClosed.svg", specifications, 13, 13, WebInspector.NavigationSidebarPanel.DisclosureTriangleClosedCanvasIdentifier); 608 }, 609 610 _checkForOldResources: function(event) 611 { 612 if (this._checkForOldResourcesTimeoutIdentifier) 613 return; 614 615 function delayedWork() 616 { 617 delete this._checkForOldResourcesTimeoutIdentifier; 618 619 var contentTreeOutline = this.contentTreeOutlineToAutoPrune; 620 621 // Check all the ResourceTreeElements at the top level to make sure their Resource still has a parentFrame in the frame hierarchy. 622 // If the parentFrame is no longer in the frame hierarchy we know it was removed due to a navigation or some other page change and 623 // we should remove the issues for that resource. 624 for (var i = contentTreeOutline.children.length - 1; i >= 0; --i) { 625 var treeElement = contentTreeOutline.children[i]; 626 if (!(treeElement instanceof WebInspector.ResourceTreeElement)) 627 continue; 628 629 var resource = treeElement.resource; 630 if (!resource.parentFrame || resource.parentFrame.isDetached()) 631 contentTreeOutline.removeChildAtIndex(i, true, true); 632 } 633 634 if (typeof this._updateEmptyContentPlaceholder === "function") 635 this._updateEmptyContentPlaceholder(); 636 } 637 638 // Check on a delay to coalesce multiple calls to _checkForOldResources. 639 this._checkForOldResourcesTimeoutIdentifier = setTimeout(delayedWork.bind(this), 0); 640 }, 641 642 _isTreeElementWithoutRepresentedObject: function(treeElement) 643 { 644 return treeElement instanceof WebInspector.FolderTreeElement 645 || treeElement instanceof WebInspector.DatabaseHostTreeElement 646 || typeof treeElement.representedObject === "string" 647 || treeElement.representedObject instanceof String; 648 }, 649 650 _checkOutlinesForPendingViewStateCookie: function(matchTypeOnly) 651 { 652 if (!this._pendingViewStateCookie) 653 return; 654 655 var visibleTreeElements = []; 656 this._visibleContentTreeOutlines.forEach(function(outline) { 657 var currentTreeElement = outline.hasChildren ? outline.children[0] : null; 658 while (currentTreeElement) { 659 visibleTreeElements.push(currentTreeElement); 660 currentTreeElement = currentTreeElement.traverseNextTreeElement(false, null, false); 661 } 662 }); 663 664 return this._checkElementsForPendingViewStateCookie(visibleTreeElements, matchTypeOnly); 665 }, 666 667 _checkElementsForPendingViewStateCookie: function(treeElements, matchTypeOnly) 668 { 669 if (!this._pendingViewStateCookie) 670 return; 671 672 var cookie = this._pendingViewStateCookie; 673 674 function treeElementMatchesCookie(treeElement) 675 { 676 if (this._isTreeElementWithoutRepresentedObject(treeElement)) 677 return false; 678 679 var representedObject = treeElement.representedObject; 680 if (!representedObject) 681 return false; 682 683 var typeIdentifier = cookie[WebInspector.TypeIdentifierCookieKey]; 684 if (typeIdentifier !== representedObject.constructor.TypeIdentifier) 685 return false; 686 687 if (matchTypeOnly) 688 return true; 689 690 var candidateObjectCookie = {}; 691 if (representedObject.saveIdentityToCookie) 692 representedObject.saveIdentityToCookie(candidateObjectCookie); 693 694 return Object.keys(candidateObjectCookie).every(function valuesMatchForKey(key) { 695 return candidateObjectCookie[key] === cookie[key]; 696 }); 697 } 698 699 if (!(treeElements instanceof Array)) 700 treeElements = [treeElements]; 701 702 var matchedElement = null; 703 treeElements.some(function(element) { 704 if (treeElementMatchesCookie.call(this, element)) { 705 matchedElement = element; 706 return true; 707 } 708 }, this); 709 710 if (matchedElement) { 711 matchedElement.revealAndSelect(true, false); 712 713 delete this._pendingViewStateCookie; 714 715 // Delay clearing the restoringState flag until the next runloop so listeners 716 // checking for it in this runloop still know state was being restored. 717 setTimeout(function() { 718 delete this._restoringState; 719 }.bind(this)); 720 721 if (this._finalAttemptToRestoreViewStateTimeout) { 722 clearTimeout(this._finalAttemptToRestoreViewStateTimeout); 723 delete this._finalAttemptToRestoreViewStateTimeout; 724 } 725 } 726 } 727}; 728 729WebInspector.NavigationSidebarPanel.prototype.__proto__ = WebInspector.SidebarPanel.prototype; 730