home *** CD-ROM | disk | FTP | other *** search
/ Computer Active Guide 2009 July / CAG7.ISO / Internetas / SafariSetup.exe / AppleApplicationSupport.msi / WebKit.resources_inspector_ResourcesPanel.js < prev    next >
Encoding:
Text File  |  2010-06-03  |  47.1 KB  |  1,355 lines

  1. /*
  2.  * Copyright (C) 2007, 2008 Apple Inc.  All rights reserved.
  3.  * Copyright (C) 2008, 2009 Anthony Ricaud <rik@webkit.org>
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  *
  9.  * 1.  Redistributions of source code must retain the above copyright
  10.  *     notice, this list of conditions and the following disclaimer. 
  11.  * 2.  Redistributions in binary form must reproduce the above copyright
  12.  *     notice, this list of conditions and the following disclaimer in the
  13.  *     documentation and/or other materials provided with the distribution. 
  14.  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
  15.  *     its contributors may be used to endorse or promote products derived
  16.  *     from this software without specific prior written permission. 
  17.  *
  18.  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
  19.  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  20.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  21.  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
  22.  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  23.  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  24.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  25.  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  27.  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28.  */
  29.  
  30. WebInspector.ResourcesPanel = function()
  31. {
  32.     WebInspector.AbstractTimelinePanel.call(this);
  33.  
  34.     this.element.addStyleClass("resources");
  35.  
  36.     this._createPanelEnabler();
  37.  
  38.     this.viewsContainerElement = document.createElement("div");
  39.     this.viewsContainerElement.id = "resource-views";
  40.     this.element.appendChild(this.viewsContainerElement);
  41.  
  42.     this.createFilterPanel();
  43.     this.createInterface();
  44.  
  45.     this._createStatusbarButtons();
  46.  
  47.     this.reset();
  48.     this.filter(this.filterAllElement, false);
  49.     this.graphsTreeElement.children[0].select();
  50.     this._resourceTrackingEnabled = false;
  51. }
  52.  
  53. WebInspector.ResourcesPanel.prototype = {
  54.     toolbarItemClass: "resources",
  55.  
  56.     get toolbarItemLabel()
  57.     {
  58.         return WebInspector.UIString("Resources");
  59.     },
  60.  
  61.     get statusBarItems()
  62.     {
  63.         return [this.enableToggleButton.element, this.largerResourcesButton.element, this.sortingSelectElement];
  64.     },
  65.  
  66.     get categories()
  67.     {
  68.         return WebInspector.resourceCategories;
  69.     },
  70.  
  71.     createItemTreeElement: function(item)
  72.     {
  73.         return new WebInspector.ResourceSidebarTreeElement(item);
  74.     },
  75.  
  76.     createItemGraph: function(item)
  77.     {
  78.         return new WebInspector.ResourceGraph(item);
  79.     },
  80.  
  81.     isCategoryVisible: function(categoryName)
  82.     {
  83.         return (this.itemsGraphsElement.hasStyleClass("filter-all") || this.itemsGraphsElement.hasStyleClass("filter-" + categoryName.toLowerCase()));
  84.     },
  85.  
  86.     populateSidebar: function()
  87.     {
  88.         var timeGraphItem = new WebInspector.SidebarTreeElement("resources-time-graph-sidebar-item", WebInspector.UIString("Time"));
  89.         timeGraphItem.onselect = this._graphSelected.bind(this);
  90.  
  91.         var transferTimeCalculator = new WebInspector.ResourceTransferTimeCalculator();
  92.         var transferDurationCalculator = new WebInspector.ResourceTransferDurationCalculator();
  93.  
  94.         timeGraphItem.sortingOptions = [
  95.             { name: WebInspector.UIString("Sort by Start Time"), sortingFunction: WebInspector.ResourceSidebarTreeElement.CompareByAscendingStartTime, calculator: transferTimeCalculator },
  96.             { name: WebInspector.UIString("Sort by Response Time"), sortingFunction: WebInspector.ResourceSidebarTreeElement.CompareByAscendingResponseReceivedTime, calculator: transferTimeCalculator },
  97.             { name: WebInspector.UIString("Sort by End Time"), sortingFunction: WebInspector.ResourceSidebarTreeElement.CompareByAscendingEndTime, calculator: transferTimeCalculator },
  98.             { name: WebInspector.UIString("Sort by Duration"), sortingFunction: WebInspector.ResourceSidebarTreeElement.CompareByDescendingDuration, calculator: transferDurationCalculator },
  99.             { name: WebInspector.UIString("Sort by Latency"), sortingFunction: WebInspector.ResourceSidebarTreeElement.CompareByDescendingLatency, calculator: transferDurationCalculator },
  100.         ];
  101.  
  102.         timeGraphItem.isBarOpaqueAtLeft = false;
  103.         timeGraphItem.selectedSortingOptionIndex = 1;
  104.  
  105.         this.sizeGraphItem = new WebInspector.SidebarTreeElement("resources-size-graph-sidebar-item", WebInspector.UIString("Size"));
  106.         this.sizeGraphItem.onselect = this._graphSelected.bind(this);
  107.  
  108.         var transferSizeCalculator = new WebInspector.ResourceTransferSizeCalculator();
  109.         this.sizeGraphItem.sortingOptions = [
  110.             { name: WebInspector.UIString("Sort by Transfer Size"), sortingFunction: WebInspector.ResourceSidebarTreeElement.CompareByDescendingTransferSize, calculator: transferSizeCalculator },
  111.             { name: WebInspector.UIString("Sort by Size"), sortingFunction: WebInspector.ResourceSidebarTreeElement.CompareByDescendingSize, calculator: transferSizeCalculator },
  112.         ];
  113.  
  114.         this.sizeGraphItem.isBarOpaqueAtLeft = true;
  115.         this.sizeGraphItem.selectedSortingOptionIndex = 0;
  116.  
  117.         this.graphsTreeElement = new WebInspector.SidebarSectionTreeElement(WebInspector.UIString("GRAPHS"), {}, true);
  118.         this.sidebarTree.appendChild(this.graphsTreeElement);
  119.  
  120.         this.graphsTreeElement.appendChild(timeGraphItem);
  121.         this.graphsTreeElement.appendChild(this.sizeGraphItem);
  122.         this.graphsTreeElement.expand();
  123.  
  124.         this.itemsTreeElement = new WebInspector.SidebarSectionTreeElement(WebInspector.UIString("RESOURCES"), {}, true);
  125.         this.sidebarTree.appendChild(this.itemsTreeElement);
  126.  
  127.         this.itemsTreeElement.expand();
  128.     },
  129.  
  130.     get resourceTrackingEnabled()
  131.     {
  132.         return this._resourceTrackingEnabled;
  133.     },
  134.  
  135.     _createPanelEnabler: function()
  136.     {
  137.         var panelEnablerHeading = WebInspector.UIString("You need to enable resource tracking to use this panel.");
  138.         var panelEnablerDisclaimer = WebInspector.UIString("Enabling resource tracking will reload the page and make page loading slower.");
  139.         var panelEnablerButton = WebInspector.UIString("Enable resource tracking");
  140.  
  141.         this.panelEnablerView = new WebInspector.PanelEnablerView("resources", panelEnablerHeading, panelEnablerDisclaimer, panelEnablerButton);
  142.         this.panelEnablerView.addEventListener("enable clicked", this._enableResourceTracking, this);
  143.  
  144.         this.element.appendChild(this.panelEnablerView.element);
  145.  
  146.         this.enableToggleButton = new WebInspector.StatusBarButton("", "enable-toggle-status-bar-item");
  147.         this.enableToggleButton.addEventListener("click", this._toggleResourceTracking.bind(this), false);
  148.     },
  149.  
  150.     _createStatusbarButtons: function()
  151.     {
  152.         this.largerResourcesButton = new WebInspector.StatusBarButton(WebInspector.UIString("Use small resource rows."), "resources-larger-resources-status-bar-item");
  153.  
  154.         WebInspector.settings.addEventListener("loaded", this._settingsLoaded, this);
  155.         this.largerResourcesButton.addEventListener("click", this._toggleLargerResources.bind(this), false);
  156.         this.sortingSelectElement = document.createElement("select");
  157.         this.sortingSelectElement.className = "status-bar-item";
  158.         this.sortingSelectElement.addEventListener("change", this._changeSortingFunction.bind(this), false);
  159.     },
  160.  
  161.     _settingsLoaded: function()
  162.     {
  163.         this.largerResourcesButton.toggled = WebInspector.settings.resourcesLargeRows;
  164.         if (!WebInspector.settings.resourcesLargeRows)
  165.             this._setLargerResources(WebInspector.settings.resourcesLargeRows);
  166.     },
  167.  
  168.     get mainResourceLoadTime()
  169.     {
  170.         return this._mainResourceLoadTime || -1;
  171.     },
  172.     
  173.     set mainResourceLoadTime(x)
  174.     {
  175.         if (this._mainResourceLoadTime === x)
  176.             return;
  177.         
  178.         this._mainResourceLoadTime = x;
  179.         
  180.         // Update the dividers to draw the new line
  181.         this.updateGraphDividersIfNeeded(true);
  182.     },
  183.     
  184.     get mainResourceDOMContentTime()
  185.     {
  186.         return this._mainResourceDOMContentTime || -1;
  187.     },
  188.     
  189.     set mainResourceDOMContentTime(x)
  190.     {
  191.         if (this._mainResourceDOMContentTime === x)
  192.             return;
  193.         
  194.         this._mainResourceDOMContentTime = x;
  195.         
  196.         this.updateGraphDividersIfNeeded(true);
  197.     },
  198.  
  199.     show: function()
  200.     {
  201.         WebInspector.AbstractTimelinePanel.prototype.show.call(this);
  202.  
  203.         var visibleView = this.visibleView;
  204.         if (this.visibleResource) {
  205.             this.visibleView.headersVisible = true;
  206.             this.visibleView.show(this.viewsContainerElement);
  207.         } else if (visibleView)
  208.             visibleView.show();
  209.  
  210.         // Hide any views that are visible that are not this panel's current visible view.
  211.         // This can happen when a ResourceView is visible in the Scripts panel then switched
  212.         // to the this panel.
  213.         var resourcesLength = this._resources.length;
  214.         for (var i = 0; i < resourcesLength; ++i) {
  215.             var resource = this._resources[i];
  216.             var view = resource._resourcesView;
  217.             if (!view || view === visibleView)
  218.                 continue;
  219.             view.visible = false;
  220.         }
  221.     },
  222.  
  223.     get searchableViews()
  224.     {
  225.         var views = [];
  226.  
  227.         const visibleView = this.visibleView;
  228.         if (visibleView && visibleView.performSearch)
  229.             views.push(visibleView);
  230.  
  231.         var resourcesLength = this._resources.length;
  232.         for (var i = 0; i < resourcesLength; ++i) {
  233.             var resource = this._resources[i];
  234.             if (!resource._itemsTreeElement)
  235.                 continue;
  236.             var resourceView = this.resourceViewForResource(resource);
  237.             if (!resourceView.performSearch || resourceView === visibleView)
  238.                 continue;
  239.             views.push(resourceView);
  240.         }
  241.  
  242.         return views;
  243.     },
  244.  
  245.     get searchResultsSortFunction()
  246.     {
  247.         const resourceTreeElementSortFunction = this.sortingFunction;
  248.  
  249.         function sortFuction(a, b)
  250.         {
  251.             return resourceTreeElementSortFunction(a.resource._itemsTreeElement, b.resource._itemsTreeElement);
  252.         }
  253.  
  254.         return sortFuction;
  255.     },
  256.  
  257.     searchMatchFound: function(view, matches)
  258.     {
  259.         view.resource._itemsTreeElement.searchMatches = matches;
  260.     },
  261.  
  262.     searchCanceled: function(startingNewSearch)
  263.     {
  264.         WebInspector.Panel.prototype.searchCanceled.call(this, startingNewSearch);
  265.  
  266.         if (startingNewSearch || !this._resources)
  267.             return;
  268.  
  269.         for (var i = 0; i < this._resources.length; ++i) {
  270.             var resource = this._resources[i];
  271.             if (resource._itemsTreeElement)
  272.                 resource._itemsTreeElement.updateErrorsAndWarnings();
  273.         }
  274.     },
  275.  
  276.     performSearch: function(query)
  277.     {
  278.         for (var i = 0; i < this._resources.length; ++i) {
  279.             var resource = this._resources[i];
  280.             if (resource._itemsTreeElement)
  281.                 resource._itemsTreeElement.resetBubble();
  282.         }
  283.  
  284.         WebInspector.Panel.prototype.performSearch.call(this, query);
  285.     },
  286.  
  287.     get visibleView()
  288.     {
  289.         if (this.visibleResource)
  290.             return this.visibleResource._resourcesView;
  291.         return this._resourceTrackingEnabled ? null : this.panelEnablerView;
  292.     },
  293.  
  294.     get sortingFunction()
  295.     {
  296.         return this._sortingFunction;
  297.     },
  298.  
  299.     set sortingFunction(x)
  300.     {
  301.         this._sortingFunction = x;
  302.         this._sortResourcesIfNeeded();
  303.     },
  304.  
  305.     refresh: function()
  306.     {
  307.         WebInspector.AbstractTimelinePanel.prototype.refresh.call(this);
  308.  
  309.         this._sortResourcesIfNeeded();
  310.         this._updateSummaryGraph();
  311.     },
  312.  
  313.     _updateSummaryGraph: function()
  314.     {
  315.         this.summaryBar.update(this._resources);
  316.     },
  317.  
  318.     resourceTrackingWasEnabled: function()
  319.     {
  320.         this._resourceTrackingEnabled = true;
  321.         this.reset();
  322.     },
  323.  
  324.     resourceTrackingWasDisabled: function()
  325.     {
  326.         this._resourceTrackingEnabled = false;
  327.         this.reset();
  328.     },
  329.  
  330.     reset: function()
  331.     {
  332.         this.closeVisibleResource();
  333.  
  334.         delete this.currentQuery;
  335.         this.searchCanceled();
  336.  
  337.         if (this._resources) {
  338.             var resourcesLength = this._resources.length;
  339.             for (var i = 0; i < resourcesLength; ++i) {
  340.                 var resource = this._resources[i];
  341.  
  342.                 resource.warnings = 0;
  343.                 resource.errors = 0;
  344.  
  345.                 delete resource._resourcesView;
  346.             }
  347.         }
  348.  
  349.         WebInspector.AbstractTimelinePanel.prototype.reset.call(this);
  350.         
  351.         this.mainResourceLoadTime = -1;
  352.         this.mainResourceDOMContentTime = -1;
  353.  
  354.         this.viewsContainerElement.removeChildren();
  355.  
  356.         this.summaryBar.reset();
  357.  
  358.         if (this._resourceTrackingEnabled) {
  359.             this.enableToggleButton.title = WebInspector.UIString("Resource tracking enabled. Click to disable.");
  360.             this.enableToggleButton.toggled = true;
  361.             this.largerResourcesButton.visible = true;
  362.             this.sortingSelectElement.removeStyleClass("hidden");
  363.             this.panelEnablerView.visible = false;
  364.         } else {
  365.             this.enableToggleButton.title = WebInspector.UIString("Resource tracking disabled. Click to enable.");
  366.             this.enableToggleButton.toggled = false;
  367.             this.largerResourcesButton.visible = false;
  368.             this.sortingSelectElement.addStyleClass("hidden");
  369.             this.panelEnablerView.visible = true;
  370.         }
  371.     },
  372.  
  373.     addResource: function(resource)
  374.     {
  375.         this._resources.push(resource);
  376.         this.refreshResource(resource);
  377.     },
  378.  
  379.     removeResource: function(resource)
  380.     {
  381.         if (this.visibleView === resource._resourcesView)
  382.             this.closeVisibleResource();
  383.  
  384.         this.removeItem(resource);
  385.  
  386.         resource.warnings = 0;
  387.         resource.errors = 0;
  388.  
  389.         delete resource._resourcesView;
  390.     },
  391.  
  392.     addMessageToResource: function(resource, msg)
  393.     {
  394.         if (!resource)
  395.             return;
  396.  
  397.         switch (msg.level) {
  398.         case WebInspector.ConsoleMessage.MessageLevel.Warning:
  399.             resource.warnings += msg.repeatDelta;
  400.             break;
  401.         case WebInspector.ConsoleMessage.MessageLevel.Error:
  402.             resource.errors += msg.repeatDelta;
  403.             break;
  404.         }
  405.  
  406.         if (!this.currentQuery && resource._itemsTreeElement)
  407.             resource._itemsTreeElement.updateErrorsAndWarnings();
  408.  
  409.         var view = this.resourceViewForResource(resource);
  410.         if (view.addMessage)
  411.             view.addMessage(msg);
  412.     },
  413.  
  414.     clearMessages: function()
  415.     {
  416.         var resourcesLength = this._resources.length;
  417.         for (var i = 0; i < resourcesLength; ++i) {
  418.             var resource = this._resources[i];
  419.             resource.warnings = 0;
  420.             resource.errors = 0;
  421.  
  422.             if (!this.currentQuery && resource._itemsTreeElement)
  423.                 resource._itemsTreeElement.updateErrorsAndWarnings();
  424.  
  425.             var view = resource._resourcesView;
  426.             if (!view || !view.clearMessages)
  427.                 continue;
  428.             view.clearMessages();
  429.         }
  430.     },
  431.  
  432.     refreshResource: function(resource)
  433.     {
  434.         this.refreshItem(resource);
  435.     },
  436.  
  437.     recreateViewForResourceIfNeeded: function(resource)
  438.     {
  439.         if (!resource || !resource._resourcesView)
  440.             return;
  441.  
  442.         var newView = this._createResourceView(resource);
  443.         if (newView.__proto__ === resource._resourcesView.__proto__)
  444.             return;
  445.  
  446.         if (!this.currentQuery && resource._itemsTreeElement)
  447.             resource._itemsTreeElement.updateErrorsAndWarnings();
  448.  
  449.         var oldView = resource._resourcesView;
  450.         var oldViewParentNode = oldView.visible ? oldView.element.parentNode : null;
  451.  
  452.         resource._resourcesView.detach();
  453.         delete resource._resourcesView;
  454.  
  455.         resource._resourcesView = newView;
  456.  
  457.         newView.headersVisible = oldView.headersVisible;
  458.  
  459.         if (oldViewParentNode)
  460.             newView.show(oldViewParentNode);
  461.  
  462.         WebInspector.panels.scripts.viewRecreated(oldView, newView);
  463.     },
  464.  
  465.     canShowSourceLine: function(url, line)
  466.     {
  467.         return this._resourceTrackingEnabled && !!WebInspector.resourceForURL(url);
  468.     },
  469.  
  470.     showSourceLine: function(url, line)
  471.     {
  472.         this.showResource(WebInspector.resourceForURL(url), line);
  473.     },
  474.  
  475.     showResource: function(resource, line)
  476.     {
  477.         if (!resource)
  478.             return;
  479.  
  480.         this.containerElement.addStyleClass("viewing-resource");
  481.  
  482.         if (this.visibleResource && this.visibleResource._resourcesView)
  483.             this.visibleResource._resourcesView.hide();
  484.  
  485.         var view = this.resourceViewForResource(resource);
  486.         view.headersVisible = true;
  487.         view.show(this.viewsContainerElement);
  488.  
  489.         if (line) {
  490.             view.selectContentTab(true);
  491.             if (view.revealLine)
  492.                 view.revealLine(line);
  493.             if (view.highlightLine)
  494.                 view.highlightLine(line);
  495.         }
  496.  
  497.         this.revealAndSelectItem(resource);
  498.  
  499.         this.visibleResource = resource;
  500.  
  501.         this.updateSidebarWidth();
  502.     },
  503.  
  504.     showView: function(view)
  505.     {
  506.         if (!view)
  507.             return;
  508.         this.showResource(view.resource);
  509.     },
  510.  
  511.     closeVisibleResource: function()
  512.     {
  513.         this.containerElement.removeStyleClass("viewing-resource");
  514.         this._updateDividersLabelBarPosition();
  515.  
  516.         if (this.visibleResource && this.visibleResource._resourcesView)
  517.             this.visibleResource._resourcesView.hide();
  518.         delete this.visibleResource;
  519.  
  520.         if (this._lastSelectedGraphTreeElement)
  521.             this._lastSelectedGraphTreeElement.select(true);
  522.  
  523.         this.updateSidebarWidth();
  524.     },
  525.  
  526.     resourceViewForResource: function(resource)
  527.     {
  528.         if (!resource)
  529.             return null;
  530.         if (!resource._resourcesView)
  531.             resource._resourcesView = this._createResourceView(resource);
  532.         return resource._resourcesView;
  533.     },
  534.  
  535.     sourceFrameForResource: function(resource)
  536.     {
  537.         var view = this.resourceViewForResource(resource);
  538.         if (!view)
  539.             return null;
  540.  
  541.         if (!view.setupSourceFrameIfNeeded)
  542.             return null;
  543.  
  544.         // Setting up the source frame requires that we be attached.
  545.         if (!this.element.parentNode)
  546.             this.attach();
  547.  
  548.         view.setupSourceFrameIfNeeded();
  549.         return view.sourceFrame;
  550.     },
  551.  
  552.     _sortResourcesIfNeeded: function()
  553.     {
  554.         this.sortItems(this.sortingFunction);
  555.     },
  556.  
  557.     updateGraphDividersIfNeeded: function(force)
  558.     {
  559.         var proceed = WebInspector.AbstractTimelinePanel.prototype.updateGraphDividersIfNeeded.call(this, force);
  560.         
  561.         if (!proceed)
  562.             return;
  563.  
  564.         if (this.calculator.startAtZero || !this.calculator.computePercentageFromEventTime) {
  565.             // If our current sorting method starts at zero, that means it shows all
  566.             // resources starting at the same point, and so onLoad event and DOMContent
  567.             // event lines really wouldn't make much sense here, so don't render them.
  568.             // Additionally, if the calculator doesn't have the computePercentageFromEventTime
  569.             // function defined, we are probably sorting by size, and event times aren't relevant
  570.             // in this case.
  571.             return;
  572.         }
  573.  
  574.         this._timelineGrid.removeEventDividers();
  575.         if (this.mainResourceLoadTime !== -1) {
  576.             var percent = this.calculator.computePercentageFromEventTime(this.mainResourceLoadTime);
  577.  
  578.             var loadDivider = document.createElement("div");
  579.             loadDivider.className = "resources-event-divider resources-red-divider";
  580.  
  581.             var loadDividerPadding = document.createElement("div");
  582.             loadDividerPadding.className = "resources-event-divider-padding";
  583.             loadDividerPadding.style.left = percent + "%";
  584.             loadDividerPadding.title = WebInspector.UIString("Load event fired");
  585.             loadDividerPadding.appendChild(loadDivider);
  586.  
  587.             this.addEventDivider(loadDividerPadding);
  588.         }
  589.         
  590.         if (this.mainResourceDOMContentTime !== -1) {
  591.             var percent = this.calculator.computePercentageFromEventTime(this.mainResourceDOMContentTime);
  592.  
  593.             var domContentDivider = document.createElement("div");
  594.             domContentDivider.className = "resources-event-divider resources-blue-divider";
  595.             
  596.             var domContentDividerPadding = document.createElement("div");
  597.             domContentDividerPadding.className = "resources-event-divider-padding";
  598.             domContentDividerPadding.style.left = percent + "%";
  599.             domContentDividerPadding.title = WebInspector.UIString("DOMContent event fired");
  600.             domContentDividerPadding.appendChild(domContentDivider);
  601.  
  602.             this.addEventDivider(domContentDividerPadding);
  603.         }
  604.     },
  605.  
  606.     _graphSelected: function(treeElement)
  607.     {
  608.         if (this._lastSelectedGraphTreeElement)
  609.             this._lastSelectedGraphTreeElement.selectedSortingOptionIndex = this.sortingSelectElement.selectedIndex;
  610.  
  611.         this._lastSelectedGraphTreeElement = treeElement;
  612.  
  613.         this.sortingSelectElement.removeChildren();
  614.         for (var i = 0; i < treeElement.sortingOptions.length; ++i) {
  615.             var sortingOption = treeElement.sortingOptions[i];
  616.             var option = document.createElement("option");
  617.             option.label = sortingOption.name;
  618.             option.sortingFunction = sortingOption.sortingFunction;
  619.             option.calculator = sortingOption.calculator;
  620.             this.sortingSelectElement.appendChild(option);
  621.         }
  622.  
  623.         this.sortingSelectElement.selectedIndex = treeElement.selectedSortingOptionIndex;
  624.         this._changeSortingFunction();
  625.  
  626.         this.closeVisibleResource();
  627.         this.containerElement.scrollTop = 0;
  628.  
  629.         if (treeElement === this.sizeGraphItem)
  630.             this.hideEventDividers();
  631.         else
  632.             this.showEventDividers();
  633.     },
  634.  
  635.     _toggleLargerResources: function()
  636.     {
  637.         if (!this.itemsTreeElement._childrenListNode)
  638.             return;
  639.  
  640.         WebInspector.settings.resourcesLargeRows = !WebInspector.settings.resourcesLargeRows;
  641.         this._setLargerResources(this.itemsTreeElement.smallChildren);
  642.     },
  643.  
  644.     _setLargerResources: function(enabled)
  645.     {
  646.         this.largerResourcesButton.toggled = enabled;
  647.         this.itemsTreeElement.smallChildren = !enabled;
  648.         if (!enabled) {
  649.             this.itemsGraphsElement.addStyleClass("small");
  650.             this.largerResourcesButton.title = WebInspector.UIString("Use large resource rows.");
  651.             this.adjustScrollPosition();
  652.         } else {
  653.             this.itemsGraphsElement.removeStyleClass("small");
  654.             this.largerResourcesButton.title = WebInspector.UIString("Use small resource rows.");
  655.         }
  656.     },
  657.  
  658.     _changeSortingFunction: function()
  659.     {
  660.         var selectedOption = this.sortingSelectElement[this.sortingSelectElement.selectedIndex];
  661.         this.sortingFunction = selectedOption.sortingFunction;
  662.         this.calculator = this.summaryBar.calculator = selectedOption.calculator;
  663.     },
  664.  
  665.     _createResourceView: function(resource)
  666.     {
  667.         switch (resource.category) {
  668.             case WebInspector.resourceCategories.documents:
  669.             case WebInspector.resourceCategories.stylesheets:
  670.             case WebInspector.resourceCategories.scripts:
  671.             case WebInspector.resourceCategories.xhr:
  672.                 return new WebInspector.SourceView(resource);
  673.             case WebInspector.resourceCategories.images:
  674.                 return new WebInspector.ImageView(resource);
  675.             case WebInspector.resourceCategories.fonts:
  676.                 return new WebInspector.FontView(resource);
  677.             default:
  678.                 return new WebInspector.ResourceView(resource);
  679.         }
  680.     },
  681.  
  682.     setSidebarWidth: function(width)
  683.     {
  684.         if (this.visibleResource) {
  685.             this.containerElement.style.width = width + "px";
  686.             this.sidebarElement.style.removeProperty("width");
  687.         } else {
  688.             this.sidebarElement.style.width = width + "px";
  689.             this.containerElement.style.removeProperty("width");
  690.         }
  691.  
  692.         this.sidebarResizeElement.style.left = (width - 3) + "px";
  693.     },
  694.  
  695.     updateMainViewWidth: function(width)
  696.     {
  697.         this.viewsContainerElement.style.left = width + "px";
  698.  
  699.         WebInspector.AbstractTimelinePanel.prototype.updateMainViewWidth.call(this, width);
  700.         this.resize();
  701.     },
  702.  
  703.     _enableResourceTracking: function()
  704.     {
  705.         if (this._resourceTrackingEnabled)
  706.             return;
  707.         this._toggleResourceTracking(this.panelEnablerView.alwaysEnabled);
  708.     },
  709.  
  710.     _toggleResourceTracking: function(optionalAlways)
  711.     {
  712.         if (this._resourceTrackingEnabled) {
  713.             this.largerResourcesButton.visible = false;
  714.             this.sortingSelectElement.visible = false;
  715.             WebInspector.resources = {};
  716.             WebInspector.resourceURLMap = {};
  717.             InspectorBackend.disableResourceTracking(true);
  718.         } else {
  719.             this.largerResourcesButton.visible = true;
  720.             this.sortingSelectElement.visible = true;
  721.             InspectorBackend.enableResourceTracking(!!optionalAlways);
  722.         }
  723.     },
  724.  
  725.     get _resources()
  726.     {
  727.         return this.items;
  728.     },
  729.  
  730.     searchIteratesOverViews: function()
  731.     {
  732.         return true;
  733.     },
  734.  
  735.     elementsToRestoreScrollPositionsFor: function()
  736.     {
  737.         return [ this.containerElement ];
  738.     }
  739. }
  740.  
  741. WebInspector.ResourcesPanel.prototype.__proto__ = WebInspector.AbstractTimelinePanel.prototype;
  742.  
  743. WebInspector.getResourceContent = function(identifier, callback)
  744. {
  745.     InspectorBackend.getResourceContent(WebInspector.Callback.wrap(callback), identifier);
  746. }
  747.  
  748. WebInspector.didGetResourceContent = WebInspector.Callback.processCallback;
  749.  
  750. WebInspector.ResourceTimeCalculator = function(startAtZero)
  751. {
  752.     WebInspector.AbstractTimelineCalculator.call(this);
  753.     this.startAtZero = startAtZero;
  754. }
  755.  
  756. WebInspector.ResourceTimeCalculator.prototype = {
  757.     computeSummaryValues: function(resources)
  758.     {
  759.         var resourcesByCategory = {};
  760.         var resourcesLength = resources.length;
  761.         for (var i = 0; i < resourcesLength; ++i) {
  762.             var resource = resources[i];
  763.             if (!(resource.category.name in resourcesByCategory))
  764.                 resourcesByCategory[resource.category.name] = [];
  765.             resourcesByCategory[resource.category.name].push(resource);
  766.         }
  767.  
  768.         var earliestStart;
  769.         var latestEnd;
  770.         var categoryValues = {};
  771.         for (var category in resourcesByCategory) {
  772.             resourcesByCategory[category].sort(WebInspector.Resource.CompareByTime);
  773.             categoryValues[category] = 0;
  774.  
  775.             var segment = {start: -1, end: -1};
  776.  
  777.             var categoryResources = resourcesByCategory[category];
  778.             var resourcesLength = categoryResources.length;
  779.             for (var i = 0; i < resourcesLength; ++i) {
  780.                 var resource = categoryResources[i];
  781.                 if (resource.startTime === -1 || resource.endTime === -1)
  782.                     continue;
  783.  
  784.                 if (typeof earliestStart === "undefined")
  785.                     earliestStart = resource.startTime;
  786.                 else
  787.                     earliestStart = Math.min(earliestStart, resource.startTime);
  788.  
  789.                 if (typeof latestEnd === "undefined")
  790.                     latestEnd = resource.endTime;
  791.                 else
  792.                     latestEnd = Math.max(latestEnd, resource.endTime);
  793.  
  794.                 if (resource.startTime <= segment.end) {
  795.                     segment.end = Math.max(segment.end, resource.endTime);
  796.                     continue;
  797.                 }
  798.  
  799.                 categoryValues[category] += segment.end - segment.start;
  800.  
  801.                 segment.start = resource.startTime;
  802.                 segment.end = resource.endTime;
  803.             }
  804.  
  805.             // Add the last segment
  806.             categoryValues[category] += segment.end - segment.start;
  807.         }
  808.  
  809.         return {categoryValues: categoryValues, total: latestEnd - earliestStart};
  810.     },
  811.  
  812.     computeBarGraphPercentages: function(resource)
  813.     {
  814.         if (resource.startTime !== -1)
  815.             var start = ((resource.startTime - this.minimumBoundary) / this.boundarySpan) * 100;
  816.         else
  817.             var start = 0;
  818.  
  819.         if (resource.responseReceivedTime !== -1)
  820.             var middle = ((resource.responseReceivedTime - this.minimumBoundary) / this.boundarySpan) * 100;
  821.         else
  822.             var middle = (this.startAtZero ? start : 100);
  823.  
  824.         if (resource.endTime !== -1)
  825.             var end = ((resource.endTime - this.minimumBoundary) / this.boundarySpan) * 100;
  826.         else
  827.             var end = (this.startAtZero ? middle : 100);
  828.  
  829.         if (this.startAtZero) {
  830.             end -= start;
  831.             middle -= start;
  832.             start = 0;
  833.         }
  834.  
  835.         return {start: start, middle: middle, end: end};
  836.     },
  837.     
  838.     computePercentageFromEventTime: function(eventTime)
  839.     {
  840.         // This function computes a percentage in terms of the total loading time
  841.         // of a specific event. If startAtZero is set, then this is useless, and we
  842.         // want to return 0.
  843.         if (eventTime !== -1 && !this.startAtZero)
  844.             return ((eventTime - this.minimumBoundary) / this.boundarySpan) * 100;
  845.  
  846.         return 0;
  847.     },
  848.  
  849.     computeBarGraphLabels: function(resource)
  850.     {
  851.         var rightLabel = "";
  852.         if (resource.responseReceivedTime !== -1 && resource.endTime !== -1)
  853.             rightLabel = this.formatValue(resource.endTime - resource.responseReceivedTime);
  854.  
  855.         var hasLatency = resource.latency > 0;
  856.         if (hasLatency)
  857.             var leftLabel = this.formatValue(resource.latency);
  858.         else
  859.             var leftLabel = rightLabel;
  860.  
  861.         if (hasLatency && rightLabel) {
  862.             var total = this.formatValue(resource.duration);
  863.             var tooltip = WebInspector.UIString("%s latency, %s download (%s total)", leftLabel, rightLabel, total);
  864.         } else if (hasLatency)
  865.             var tooltip = WebInspector.UIString("%s latency", leftLabel);
  866.         else if (rightLabel)
  867.             var tooltip = WebInspector.UIString("%s download", rightLabel);
  868.  
  869.         if (resource.cached)
  870.             tooltip = WebInspector.UIString("%s (from cache)", tooltip);
  871.  
  872.         return {left: leftLabel, right: rightLabel, tooltip: tooltip};
  873.     },
  874.  
  875.     updateBoundaries: function(resource)
  876.     {
  877.         var didChange = false;
  878.  
  879.         var lowerBound;
  880.         if (this.startAtZero)
  881.             lowerBound = 0;
  882.         else
  883.             lowerBound = this._lowerBound(resource);
  884.  
  885.         if (lowerBound !== -1 && (typeof this.minimumBoundary === "undefined" || lowerBound < this.minimumBoundary)) {
  886.             this.minimumBoundary = lowerBound;
  887.             didChange = true;
  888.         }
  889.  
  890.         var upperBound = this._upperBound(resource);
  891.         if (upperBound !== -1 && (typeof this.maximumBoundary === "undefined" || upperBound > this.maximumBoundary)) {
  892.             this.maximumBoundary = upperBound;
  893.             didChange = true;
  894.         }
  895.  
  896.         return didChange;
  897.     },
  898.  
  899.     formatValue: function(value)
  900.     {
  901.         return Number.secondsToString(value, WebInspector.UIString.bind(WebInspector));
  902.     },
  903.  
  904.     _lowerBound: function(resource)
  905.     {
  906.         return 0;
  907.     },
  908.  
  909.     _upperBound: function(resource)
  910.     {
  911.         return 0;
  912.     }
  913. }
  914.  
  915. WebInspector.ResourceTimeCalculator.prototype.__proto__ = WebInspector.AbstractTimelineCalculator.prototype;
  916.  
  917. WebInspector.ResourceTransferTimeCalculator = function()
  918. {
  919.     WebInspector.ResourceTimeCalculator.call(this, false);
  920. }
  921.  
  922. WebInspector.ResourceTransferTimeCalculator.prototype = {
  923.     formatValue: function(value)
  924.     {
  925.         return Number.secondsToString(value, WebInspector.UIString.bind(WebInspector));
  926.     },
  927.  
  928.     _lowerBound: function(resource)
  929.     {
  930.         return resource.startTime;
  931.     },
  932.  
  933.     _upperBound: function(resource)
  934.     {
  935.         return resource.endTime;
  936.     }
  937. }
  938.  
  939. WebInspector.ResourceTransferTimeCalculator.prototype.__proto__ = WebInspector.ResourceTimeCalculator.prototype;
  940.  
  941. WebInspector.ResourceTransferDurationCalculator = function()
  942. {
  943.     WebInspector.ResourceTimeCalculator.call(this, true);
  944. }
  945.  
  946. WebInspector.ResourceTransferDurationCalculator.prototype = {
  947.     formatValue: function(value)
  948.     {
  949.         return Number.secondsToString(value, WebInspector.UIString.bind(WebInspector));
  950.     },
  951.  
  952.     _upperBound: function(resource)
  953.     {
  954.         return resource.duration;
  955.     }
  956. }
  957.  
  958. WebInspector.ResourceTransferDurationCalculator.prototype.__proto__ = WebInspector.ResourceTimeCalculator.prototype;
  959.  
  960. WebInspector.ResourceTransferSizeCalculator = function()
  961. {
  962.     WebInspector.AbstractTimelineCalculator.call(this);
  963. }
  964.  
  965. WebInspector.ResourceTransferSizeCalculator.prototype = {
  966.     computeBarGraphLabels: function(resource)
  967.     {
  968.         var networkBytes = this._networkBytes(resource);
  969.         var resourceBytes = this._value(resource);
  970.         if (networkBytes && networkBytes !== resourceBytes) {
  971.             // Transferred size is not the same as reported resource length.
  972.             var networkBytesString = this.formatValue(networkBytes);
  973.             var left = networkBytesString;
  974.             var right = this.formatValue(resourceBytes);
  975.             var tooltip = right ? WebInspector.UIString("%s (%s transferred)", right, networkBytesString) : right;
  976.         } else {
  977.             var left = this.formatValue(resourceBytes);
  978.             var right = left;
  979.             var tooltip = left;
  980.         }
  981.         if (resource.cached)
  982.             tooltip = WebInspector.UIString("%s (from cache)", tooltip);
  983.         return {left: left, right: right, tooltip: tooltip};
  984.     },
  985.  
  986.     computeBarGraphPercentages: function(item)
  987.     {
  988.         const resourceBytesAsPercent = (this._value(item) / this.boundarySpan) * 100;
  989.         const networkBytesAsPercent = this._networkBytes(item) ? (this._networkBytes(item) / this.boundarySpan) * 100 : resourceBytesAsPercent;
  990.         return {start: 0, middle: networkBytesAsPercent, end: resourceBytesAsPercent};
  991.     },
  992.  
  993.     _value: function(resource)
  994.     {
  995.         return resource.resourceSize;
  996.     },
  997.  
  998.     _networkBytes: function(resource)
  999.     {
  1000.         return resource.transferSize;
  1001.     },
  1002.  
  1003.     formatValue: function(value)
  1004.     {
  1005.         return Number.bytesToString(value, WebInspector.UIString.bind(WebInspector));
  1006.     }
  1007. }
  1008.  
  1009. WebInspector.ResourceTransferSizeCalculator.prototype.__proto__ = WebInspector.AbstractTimelineCalculator.prototype;
  1010.  
  1011. WebInspector.ResourceSidebarTreeElement = function(resource)
  1012. {
  1013.     this.resource = resource;
  1014.  
  1015.     this.createIconElement();
  1016.  
  1017.     WebInspector.SidebarTreeElement.call(this, "resource-sidebar-tree-item", "", "", resource);
  1018.  
  1019.     this.refreshTitles();
  1020. }
  1021.  
  1022. WebInspector.ResourceSidebarTreeElement.prototype = {
  1023.     onattach: function()
  1024.     {
  1025.         WebInspector.SidebarTreeElement.prototype.onattach.call(this);
  1026.  
  1027.         this._listItemNode.addStyleClass("resources-category-" + this.resource.category.name);
  1028.         this._listItemNode.draggable = true;
  1029.         
  1030.         // FIXME: should actually add handler to parent, to be resolved via
  1031.         // https://bugs.webkit.org/show_bug.cgi?id=30227
  1032.         this._listItemNode.addEventListener("dragstart", this.ondragstart.bind(this), false);
  1033.         this.updateErrorsAndWarnings();
  1034.     },
  1035.  
  1036.     onselect: function()
  1037.     {
  1038.         WebInspector.panels.resources.showResource(this.resource);
  1039.     },
  1040.     
  1041.     ondblclick: function(event)
  1042.     {
  1043.         InjectedScriptAccess.getDefault().openInInspectedWindow(this.resource.url, function() {});
  1044.     },
  1045.  
  1046.     ondragstart: function(event) {
  1047.         event.dataTransfer.setData("text/plain", this.resource.url);
  1048.         event.dataTransfer.setData("text/uri-list", this.resource.url + "\r\n");
  1049.         event.dataTransfer.effectAllowed = "copy";
  1050.         return true;
  1051.     },
  1052.  
  1053.     get mainTitle()
  1054.     {
  1055.         return this.resource.displayName;
  1056.     },
  1057.  
  1058.     set mainTitle(x)
  1059.     {
  1060.         // Do nothing.
  1061.     },
  1062.  
  1063.     get subtitle()
  1064.     {
  1065.         var subtitle = this.resource.displayDomain;
  1066.  
  1067.         if (this.resource.path && this.resource.lastPathComponent) {
  1068.             var lastPathComponentIndex = this.resource.path.lastIndexOf("/" + this.resource.lastPathComponent);
  1069.             if (lastPathComponentIndex != -1)
  1070.                 subtitle += this.resource.path.substring(0, lastPathComponentIndex);
  1071.         }
  1072.  
  1073.         return subtitle;
  1074.     },
  1075.  
  1076.     set subtitle(x)
  1077.     {
  1078.         // Do nothing.
  1079.     },
  1080.  
  1081.     get selectable()
  1082.     {
  1083.         return WebInspector.panels.resources.isCategoryVisible(this.resource.category.name);
  1084.     },
  1085.  
  1086.     createIconElement: function()
  1087.     {
  1088.         var previousIconElement = this.iconElement;
  1089.  
  1090.         if (this.resource.category === WebInspector.resourceCategories.images) {
  1091.             var previewImage = document.createElement("img");
  1092.             previewImage.className = "image-resource-icon-preview";
  1093.             previewImage.src = this.resource.url;
  1094.  
  1095.             this.iconElement = document.createElement("div");
  1096.             this.iconElement.className = "icon";
  1097.             this.iconElement.appendChild(previewImage);
  1098.         } else {
  1099.             this.iconElement = document.createElement("img");
  1100.             this.iconElement.className = "icon";
  1101.         }
  1102.  
  1103.         if (previousIconElement)
  1104.             previousIconElement.parentNode.replaceChild(this.iconElement, previousIconElement);
  1105.     },
  1106.  
  1107.     refresh: function()
  1108.     {
  1109.         this.refreshTitles();
  1110.  
  1111.         if (!this._listItemNode.hasStyleClass("resources-category-" + this.resource.category.name)) {
  1112.             this._listItemNode.removeMatchingStyleClasses("resources-category-\\w+");
  1113.             this._listItemNode.addStyleClass("resources-category-" + this.resource.category.name);
  1114.  
  1115.             this.createIconElement();
  1116.         }
  1117.  
  1118.         this.tooltip = this.resource.url;
  1119.     },
  1120.  
  1121.     resetBubble: function()
  1122.     {
  1123.         this.bubbleText = "";
  1124.         this.bubbleElement.removeStyleClass("search-matches");
  1125.         this.bubbleElement.removeStyleClass("warning");
  1126.         this.bubbleElement.removeStyleClass("error");
  1127.     },
  1128.  
  1129.     set searchMatches(matches)
  1130.     {
  1131.         this.resetBubble();
  1132.  
  1133.         if (!matches)
  1134.             return;
  1135.  
  1136.         this.bubbleText = matches;
  1137.         this.bubbleElement.addStyleClass("search-matches");
  1138.     },
  1139.  
  1140.     updateErrorsAndWarnings: function()
  1141.     {
  1142.         this.resetBubble();
  1143.  
  1144.         if (this.resource.warnings || this.resource.errors)
  1145.             this.bubbleText = (this.resource.warnings + this.resource.errors);
  1146.  
  1147.         if (this.resource.warnings)
  1148.             this.bubbleElement.addStyleClass("warning");
  1149.  
  1150.         if (this.resource.errors)
  1151.             this.bubbleElement.addStyleClass("error");
  1152.     }
  1153. }
  1154.  
  1155. WebInspector.ResourceSidebarTreeElement.CompareByAscendingStartTime = function(a, b)
  1156. {
  1157.     return WebInspector.Resource.CompareByStartTime(a.resource, b.resource)
  1158.         || WebInspector.Resource.CompareByEndTime(a.resource, b.resource)
  1159.         || WebInspector.Resource.CompareByResponseReceivedTime(a.resource, b.resource);
  1160. }
  1161.  
  1162. WebInspector.ResourceSidebarTreeElement.CompareByAscendingResponseReceivedTime = function(a, b)
  1163. {
  1164.     return WebInspector.Resource.CompareByResponseReceivedTime(a.resource, b.resource)
  1165.         || WebInspector.Resource.CompareByStartTime(a.resource, b.resource)
  1166.         || WebInspector.Resource.CompareByEndTime(a.resource, b.resource);
  1167. }
  1168.  
  1169. WebInspector.ResourceSidebarTreeElement.CompareByAscendingEndTime = function(a, b)
  1170. {
  1171.     return WebInspector.Resource.CompareByEndTime(a.resource, b.resource)
  1172.         || WebInspector.Resource.CompareByStartTime(a.resource, b.resource)
  1173.         || WebInspector.Resource.CompareByResponseReceivedTime(a.resource, b.resource);
  1174. }
  1175.  
  1176. WebInspector.ResourceSidebarTreeElement.CompareByDescendingDuration = function(a, b)
  1177. {
  1178.     return -1 * WebInspector.Resource.CompareByDuration(a.resource, b.resource);
  1179. }
  1180.  
  1181. WebInspector.ResourceSidebarTreeElement.CompareByDescendingLatency = function(a, b)
  1182. {
  1183.     return -1 * WebInspector.Resource.CompareByLatency(a.resource, b.resource);
  1184. }
  1185.  
  1186. WebInspector.ResourceSidebarTreeElement.CompareByDescendingSize = function(a, b)
  1187. {
  1188.     return -1 * WebInspector.Resource.CompareBySize(a.resource, b.resource);
  1189. }
  1190.  
  1191. WebInspector.ResourceSidebarTreeElement.CompareByDescendingTransferSize = function(a, b)
  1192. {
  1193.     return -1 * WebInspector.Resource.CompareByTransferSize(a.resource, b.resource);
  1194. }
  1195.  
  1196. WebInspector.ResourceSidebarTreeElement.prototype.__proto__ = WebInspector.SidebarTreeElement.prototype;
  1197.  
  1198. WebInspector.ResourceGraph = function(resource)
  1199. {
  1200.     this.resource = resource;
  1201.  
  1202.     this._graphElement = document.createElement("div");
  1203.     this._graphElement.className = "resources-graph-side";
  1204.     this._graphElement.addEventListener("mouseover", this.refreshLabelPositions.bind(this), false);
  1205.  
  1206.     if (resource.cached)
  1207.         this._graphElement.addStyleClass("resource-cached");
  1208.  
  1209.     this._barAreaElement = document.createElement("div");
  1210.     this._barAreaElement.className = "resources-graph-bar-area hidden";
  1211.     this._graphElement.appendChild(this._barAreaElement);
  1212.  
  1213.     this._barLeftElement = document.createElement("div");
  1214.     this._barLeftElement.className = "resources-graph-bar waiting";
  1215.     this._barAreaElement.appendChild(this._barLeftElement);
  1216.  
  1217.     this._barRightElement = document.createElement("div");
  1218.     this._barRightElement.className = "resources-graph-bar";
  1219.     this._barAreaElement.appendChild(this._barRightElement);
  1220.  
  1221.     this._labelLeftElement = document.createElement("div");
  1222.     this._labelLeftElement.className = "resources-graph-label waiting";
  1223.     this._barAreaElement.appendChild(this._labelLeftElement);
  1224.  
  1225.     this._labelRightElement = document.createElement("div");
  1226.     this._labelRightElement.className = "resources-graph-label";
  1227.     this._barAreaElement.appendChild(this._labelRightElement);
  1228.  
  1229.     this._graphElement.addStyleClass("resources-category-" + resource.category.name);
  1230. }
  1231.  
  1232. WebInspector.ResourceGraph.prototype = {
  1233.     get graphElement()
  1234.     {
  1235.         return this._graphElement;
  1236.     },
  1237.  
  1238.     refreshLabelPositions: function()
  1239.     {
  1240.         this._labelLeftElement.style.removeProperty("left");
  1241.         this._labelLeftElement.style.removeProperty("right");
  1242.         this._labelLeftElement.removeStyleClass("before");
  1243.         this._labelLeftElement.removeStyleClass("hidden");
  1244.  
  1245.         this._labelRightElement.style.removeProperty("left");
  1246.         this._labelRightElement.style.removeProperty("right");
  1247.         this._labelRightElement.removeStyleClass("after");
  1248.         this._labelRightElement.removeStyleClass("hidden");
  1249.  
  1250.         const labelPadding = 10;
  1251.         const barRightElementOffsetWidth = this._barRightElement.offsetWidth;
  1252.         const barLeftElementOffsetWidth = this._barLeftElement.offsetWidth;
  1253.  
  1254.         if (this._isBarOpaqueAtLeft) {
  1255.             var leftBarWidth = barLeftElementOffsetWidth - labelPadding;
  1256.             var rightBarWidth = (barRightElementOffsetWidth - barLeftElementOffsetWidth) - labelPadding;
  1257.         } else {
  1258.             var leftBarWidth = (barLeftElementOffsetWidth - barRightElementOffsetWidth) - labelPadding;
  1259.             var rightBarWidth = barRightElementOffsetWidth - labelPadding;
  1260.         }
  1261.  
  1262.         const labelLeftElementOffsetWidth = this._labelLeftElement.offsetWidth;
  1263.         const labelRightElementOffsetWidth = this._labelRightElement.offsetWidth;
  1264.  
  1265.         const labelBefore = (labelLeftElementOffsetWidth > leftBarWidth);
  1266.         const labelAfter = (labelRightElementOffsetWidth > rightBarWidth);
  1267.         const graphElementOffsetWidth = this._graphElement.offsetWidth;
  1268.  
  1269.         if (labelBefore && (graphElementOffsetWidth * (this._percentages.start / 100)) < (labelLeftElementOffsetWidth + 10))
  1270.             var leftHidden = true;
  1271.  
  1272.         if (labelAfter && (graphElementOffsetWidth * ((100 - this._percentages.end) / 100)) < (labelRightElementOffsetWidth + 10))
  1273.             var rightHidden = true;
  1274.  
  1275.         if (barLeftElementOffsetWidth == barRightElementOffsetWidth) {
  1276.             // The left/right label data are the same, so a before/after label can be replaced by an on-bar label.
  1277.             if (labelBefore && !labelAfter)
  1278.                 leftHidden = true;
  1279.             else if (labelAfter && !labelBefore)
  1280.                 rightHidden = true;
  1281.         }
  1282.  
  1283.         if (labelBefore) {
  1284.             if (leftHidden)
  1285.                 this._labelLeftElement.addStyleClass("hidden");
  1286.             this._labelLeftElement.style.setProperty("right", (100 - this._percentages.start) + "%");
  1287.             this._labelLeftElement.addStyleClass("before");
  1288.         } else {
  1289.             this._labelLeftElement.style.setProperty("left", this._percentages.start + "%");
  1290.             this._labelLeftElement.style.setProperty("right", (100 - this._percentages.middle) + "%");
  1291.         }
  1292.  
  1293.         if (labelAfter) {
  1294.             if (rightHidden)
  1295.                 this._labelRightElement.addStyleClass("hidden");
  1296.             this._labelRightElement.style.setProperty("left", this._percentages.end + "%");
  1297.             this._labelRightElement.addStyleClass("after");
  1298.         } else {
  1299.             this._labelRightElement.style.setProperty("left", this._percentages.middle + "%");
  1300.             this._labelRightElement.style.setProperty("right", (100 - this._percentages.end) + "%");
  1301.         }
  1302.     },
  1303.  
  1304.     refresh: function(calculator, isBarOpaqueAtLeft)
  1305.     {
  1306.         var percentages = calculator.computeBarGraphPercentages(this.resource);
  1307.         var labels = calculator.computeBarGraphLabels(this.resource);
  1308.  
  1309.         this._percentages = percentages;
  1310.  
  1311.         this._barAreaElement.removeStyleClass("hidden");
  1312.  
  1313.         if (!this._graphElement.hasStyleClass("resources-category-" + this.resource.category.name)) {
  1314.             this._graphElement.removeMatchingStyleClasses("resources-category-\\w+");
  1315.             this._graphElement.addStyleClass("resources-category-" + this.resource.category.name);
  1316.         }
  1317.  
  1318.         this._barLeftElement.style.setProperty("left", percentages.start + "%");
  1319.         this._barRightElement.style.setProperty("right", (100 - percentages.end) + "%");
  1320.  
  1321.         if (!isBarOpaqueAtLeft) {
  1322.             this._barLeftElement.style.setProperty("right", (100 - percentages.end) + "%");
  1323.             this._barRightElement.style.setProperty("left", percentages.middle + "%");
  1324.  
  1325.             if (this._isBarOpaqueAtLeft != isBarOpaqueAtLeft) {
  1326.                 this._barLeftElement.addStyleClass("waiting");
  1327.                 this._barRightElement.removeStyleClass("waiting-right");
  1328.                 this._labelLeftElement.addStyleClass("waiting");
  1329.                 this._labelRightElement.removeStyleClass("waiting-right");
  1330.             }
  1331.         } else {
  1332.             this._barLeftElement.style.setProperty("right", (100 - percentages.middle) + "%");
  1333.             this._barRightElement.style.setProperty("left", percentages.start + "%");
  1334.  
  1335.             if (this._isBarOpaqueAtLeft != isBarOpaqueAtLeft) {
  1336.                 this._barLeftElement.removeStyleClass("waiting");
  1337.                 this._barRightElement.addStyleClass("waiting-right");
  1338.                 this._labelLeftElement.removeStyleClass("waiting");
  1339.                 this._labelRightElement.addStyleClass("waiting-right");
  1340.             }
  1341.         }
  1342.  
  1343.         this._isBarOpaqueAtLeft = isBarOpaqueAtLeft;
  1344.  
  1345.         this._labelLeftElement.textContent = labels.left;
  1346.         this._labelRightElement.textContent = labels.right;
  1347.  
  1348.         var tooltip = (labels.tooltip || "");
  1349.         this._barLeftElement.title = tooltip;
  1350.         this._labelLeftElement.title = tooltip;
  1351.         this._labelRightElement.title = tooltip;
  1352.         this._barRightElement.title = tooltip;
  1353.     }
  1354. }
  1355.