home *** CD-ROM | disk | FTP | other *** search
/ Computer Active 2010 July / CA07.iso / Multimedija / QuickTimeInstaller.exe / AppleApplicationSupport.msi / WebKit.resources_inspector_ResourcesPanel.js < prev    next >
Encoding:
Text File  |  2010-03-15  |  57.9 KB  |  1,707 lines

  1. /*
  2.  * Copyright (C) 2007, 2008 Apple Inc.  All rights reserved.
  3.  * Copyright (C) 2008 Anthony Ricaud (rik24d@gmail.com)
  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.Panel.call(this);
  33.  
  34.     this.element.addStyleClass("resources");
  35.  
  36.     this.viewsContainerElement = document.createElement("div");
  37.     this.viewsContainerElement.id = "resource-views";
  38.     this.element.appendChild(this.viewsContainerElement);
  39.  
  40.     this.containerElement = document.createElement("div");
  41.     this.containerElement.id = "resources-container";
  42.     this.containerElement.addEventListener("scroll", this._updateDividersLabelBarPosition.bind(this), false);
  43.     this.element.appendChild(this.containerElement);
  44.  
  45.     this.sidebarElement = document.createElement("div");
  46.     this.sidebarElement.id = "resources-sidebar";
  47.     this.sidebarElement.className = "sidebar";
  48.     this.containerElement.appendChild(this.sidebarElement);
  49.  
  50.     this.sidebarResizeElement = document.createElement("div");
  51.     this.sidebarResizeElement.className = "sidebar-resizer-vertical";
  52.     this.sidebarResizeElement.addEventListener("mousedown", this._startSidebarDragging.bind(this), false);
  53.     this.element.appendChild(this.sidebarResizeElement);
  54.  
  55.     this.containerContentElement = document.createElement("div");
  56.     this.containerContentElement.id = "resources-container-content";
  57.     this.containerElement.appendChild(this.containerContentElement);
  58.  
  59.     this.summaryElement = document.createElement("div");
  60.     this.summaryElement.id = "resources-summary";
  61.     this.containerContentElement.appendChild(this.summaryElement);
  62.  
  63.     this.resourcesGraphsElement = document.createElement("div");
  64.     this.resourcesGraphsElement.id = "resources-graphs";
  65.     this.containerContentElement.appendChild(this.resourcesGraphsElement);
  66.  
  67.     this.dividersElement = document.createElement("div");
  68.     this.dividersElement.id = "resources-dividers";
  69.     this.containerContentElement.appendChild(this.dividersElement);
  70.  
  71.     this.dividersLabelBarElement = document.createElement("div");
  72.     this.dividersLabelBarElement.id = "resources-dividers-label-bar";
  73.     this.containerContentElement.appendChild(this.dividersLabelBarElement);
  74.  
  75.     this.summaryGraphElement = document.createElement("canvas");
  76.     this.summaryGraphElement.setAttribute("width", "450");
  77.     this.summaryGraphElement.setAttribute("height", "38");
  78.     this.summaryGraphElement.id = "resources-summary-graph";
  79.     this.summaryElement.appendChild(this.summaryGraphElement);
  80.  
  81.     this.legendElement = document.createElement("div");
  82.     this.legendElement.id = "resources-graph-legend";
  83.     this.summaryElement.appendChild(this.legendElement);
  84.  
  85.     this.sidebarTreeElement = document.createElement("ol");
  86.     this.sidebarTreeElement.className = "sidebar-tree";
  87.     this.sidebarElement.appendChild(this.sidebarTreeElement);
  88.  
  89.     this.sidebarTree = new TreeOutline(this.sidebarTreeElement);
  90.  
  91.     var timeGraphItem = new WebInspector.SidebarTreeElement("resources-time-graph-sidebar-item", WebInspector.UIString("Time"));
  92.     timeGraphItem.onselect = this._graphSelected.bind(this);
  93.  
  94.     var transferTimeCalculator = new WebInspector.ResourceTransferTimeCalculator();
  95.     var transferDurationCalculator = new WebInspector.ResourceTransferDurationCalculator();
  96.  
  97.     timeGraphItem.sortingOptions = [
  98.         { name: WebInspector.UIString("Sort by Start Time"), sortingFunction: WebInspector.ResourceSidebarTreeElement.CompareByAscendingStartTime, calculator: transferTimeCalculator },
  99.         { name: WebInspector.UIString("Sort by Response Time"), sortingFunction: WebInspector.ResourceSidebarTreeElement.CompareByAscendingResponseReceivedTime, calculator: transferTimeCalculator },
  100.         { name: WebInspector.UIString("Sort by End Time"), sortingFunction: WebInspector.ResourceSidebarTreeElement.CompareByAscendingEndTime, calculator: transferTimeCalculator },
  101.         { name: WebInspector.UIString("Sort by Duration"), sortingFunction: WebInspector.ResourceSidebarTreeElement.CompareByDescendingDuration, calculator: transferDurationCalculator },
  102.         { name: WebInspector.UIString("Sort by Latency"), sortingFunction: WebInspector.ResourceSidebarTreeElement.CompareByDescendingLatency, calculator: transferDurationCalculator },
  103.     ];
  104.  
  105.     timeGraphItem.selectedSortingOptionIndex = 1;
  106.  
  107.     var sizeGraphItem = new WebInspector.SidebarTreeElement("resources-size-graph-sidebar-item", WebInspector.UIString("Size"));
  108.     sizeGraphItem.onselect = this._graphSelected.bind(this);
  109.  
  110.     var transferSizeCalculator = new WebInspector.ResourceTransferSizeCalculator();
  111.     sizeGraphItem.sortingOptions = [
  112.         { name: WebInspector.UIString("Sort by Size"), sortingFunction: WebInspector.ResourceSidebarTreeElement.CompareByDescendingSize, calculator: transferSizeCalculator },
  113.     ];
  114.  
  115.     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(sizeGraphItem);
  122.     this.graphsTreeElement.expand();
  123.  
  124.     this.resourcesTreeElement = new WebInspector.SidebarSectionTreeElement(WebInspector.UIString("RESOURCES"), {}, true);
  125.     this.sidebarTree.appendChild(this.resourcesTreeElement);
  126.  
  127.     this.resourcesTreeElement.expand();
  128.  
  129.     var panelEnablerHeading = WebInspector.UIString("You need to enable resource tracking to use this panel.");
  130.     var panelEnablerDisclaimer = WebInspector.UIString("Enabling resource tracking will reload the page and make page loading slower.");
  131.     var panelEnablerButton = WebInspector.UIString("Enable resource tracking");
  132.  
  133.     this.panelEnablerView = new WebInspector.PanelEnablerView("resources", panelEnablerHeading, panelEnablerDisclaimer, panelEnablerButton);
  134.     this.panelEnablerView.addEventListener("enable clicked", this._enableResourceTracking, this);
  135.  
  136.     this.element.appendChild(this.panelEnablerView.element);
  137.  
  138.     this.enableToggleButton = document.createElement("button");
  139.     this.enableToggleButton.className = "enable-toggle-status-bar-item status-bar-item";
  140.     this.enableToggleButton.addEventListener("click", this._toggleResourceTracking.bind(this), false);
  141.  
  142.     this.largerResourcesButton = document.createElement("button");
  143.     this.largerResourcesButton.id = "resources-larger-resources-status-bar-item";
  144.     this.largerResourcesButton.className = "status-bar-item toggled-on";
  145.     this.largerResourcesButton.title = WebInspector.UIString("Use small resource rows.");
  146.     this.largerResourcesButton.addEventListener("click", this._toggleLargerResources.bind(this), false);
  147.  
  148.     this.sortingSelectElement = document.createElement("select");
  149.     this.sortingSelectElement.className = "status-bar-item";
  150.     this.sortingSelectElement.addEventListener("change", this._changeSortingFunction.bind(this), false);
  151.  
  152.     this.reset();
  153.  
  154.     timeGraphItem.select();
  155. }
  156.  
  157. WebInspector.ResourcesPanel.prototype = {
  158.     toolbarItemClass: "resources",
  159.  
  160.     get toolbarItemLabel()
  161.     {
  162.         return WebInspector.UIString("Resources");
  163.     },
  164.  
  165.     get statusBarItems()
  166.     {
  167.         return [this.enableToggleButton, this.largerResourcesButton, this.sortingSelectElement];
  168.     },
  169.  
  170.     show: function()
  171.     {
  172.         WebInspector.Panel.prototype.show.call(this);
  173.  
  174.         this._updateDividersLabelBarPosition();
  175.         this._updateSidebarWidth();
  176.         this.refreshIfNeeded();
  177.  
  178.         var visibleView = this.visibleView;
  179.         if (visibleView) {
  180.             visibleView.headersVisible = true;
  181.             visibleView.show(this.viewsContainerElement);
  182.         }
  183.  
  184.         // Hide any views that are visible that are not this panel's current visible view.
  185.         // This can happen when a ResourceView is visible in the Scripts panel then switched
  186.         // to the this panel.
  187.         var resourcesLength = this._resources.length;
  188.         for (var i = 0; i < resourcesLength; ++i) {
  189.             var resource = this._resources[i];
  190.             var view = resource._resourcesView;
  191.             if (!view || view === visibleView)
  192.                 continue;
  193.             view.visible = false;
  194.         }
  195.     },
  196.  
  197.     resize: function()
  198.     {
  199.         this._updateGraphDividersIfNeeded();
  200.  
  201.         var visibleView = this.visibleView;
  202.         if (visibleView && "resize" in visibleView)
  203.             visibleView.resize();
  204.     },
  205.  
  206.     get searchableViews()
  207.     {
  208.         var views = [];
  209.  
  210.         const visibleView = this.visibleView;
  211.         if (visibleView && visibleView.performSearch)
  212.             views.push(visibleView);
  213.  
  214.         var resourcesLength = this._resources.length;
  215.         for (var i = 0; i < resourcesLength; ++i) {
  216.             var resource = this._resources[i];
  217.             if (!resource._resourcesTreeElement)
  218.                 continue;
  219.             var resourceView = this.resourceViewForResource(resource);
  220.             if (!resourceView.performSearch || resourceView === visibleView)
  221.                 continue;
  222.             views.push(resourceView);
  223.         }
  224.  
  225.         return views;
  226.     },
  227.  
  228.     get searchResultsSortFunction()
  229.     {
  230.         const resourceTreeElementSortFunction = this.sortingFunction;
  231.  
  232.         function sortFuction(a, b)
  233.         {
  234.             return resourceTreeElementSortFunction(a.resource._resourcesTreeElement, b.resource._resourcesTreeElement);
  235.         }
  236.  
  237.         return sortFuction;
  238.     },
  239.  
  240.     searchMatchFound: function(view, matches)
  241.     {
  242.         view.resource._resourcesTreeElement.searchMatches = matches;
  243.     },
  244.  
  245.     searchCanceled: function(startingNewSearch)
  246.     {
  247.         WebInspector.Panel.prototype.searchCanceled.call(this, startingNewSearch);
  248.  
  249.         if (startingNewSearch || !this._resources)
  250.             return;
  251.  
  252.         for (var i = 0; i < this._resources.length; ++i) {
  253.             var resource = this._resources[i];
  254.             if (resource._resourcesTreeElement)
  255.                 resource._resourcesTreeElement.updateErrorsAndWarnings();
  256.         }
  257.     },
  258.  
  259.     performSearch: function(query)
  260.     {
  261.         for (var i = 0; i < this._resources.length; ++i) {
  262.             var resource = this._resources[i];
  263.             if (resource._resourcesTreeElement)
  264.                 resource._resourcesTreeElement.resetBubble();
  265.         }
  266.  
  267.         WebInspector.Panel.prototype.performSearch.call(this, query);
  268.     },
  269.  
  270.     get visibleView()
  271.     {
  272.         if (this.visibleResource)
  273.             return this.visibleResource._resourcesView;
  274.         return null;
  275.     },
  276.  
  277.     get calculator()
  278.     {
  279.         return this._calculator;
  280.     },
  281.  
  282.     set calculator(x)
  283.     {
  284.         if (!x || this._calculator === x)
  285.             return;
  286.  
  287.         this._calculator = x;
  288.         this._calculator.reset();
  289.  
  290.         this._staleResources = this._resources;
  291.         this.refresh();
  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.     get needsRefresh()
  306.     {
  307.         return this._needsRefresh;
  308.     },
  309.  
  310.     set needsRefresh(x)
  311.     {
  312.         if (this._needsRefresh === x)
  313.             return;
  314.  
  315.         this._needsRefresh = x;
  316.  
  317.         if (x) {
  318.             if (this.visible && !("_refreshTimeout" in this))
  319.                 this._refreshTimeout = setTimeout(this.refresh.bind(this), 500);
  320.         } else {
  321.             if ("_refreshTimeout" in this) {
  322.                 clearTimeout(this._refreshTimeout);
  323.                 delete this._refreshTimeout;
  324.             }
  325.         }
  326.     },
  327.  
  328.     refreshIfNeeded: function()
  329.     {
  330.         if (this.needsRefresh)
  331.             this.refresh();
  332.     },
  333.  
  334.     refresh: function()
  335.     {
  336.         this.needsRefresh = false;
  337.  
  338.         var staleResourcesLength = this._staleResources.length;
  339.         var boundariesChanged = false;
  340.  
  341.         for (var i = 0; i < staleResourcesLength; ++i) {
  342.             var resource = this._staleResources[i];
  343.             if (!resource._resourcesTreeElement) {
  344.                 // Create the resource tree element and graph.
  345.                 resource._resourcesTreeElement = new WebInspector.ResourceSidebarTreeElement(resource);
  346.                 resource._resourcesTreeElement._resourceGraph = new WebInspector.ResourceGraph(resource);
  347.  
  348.                 this.resourcesTreeElement.appendChild(resource._resourcesTreeElement);
  349.                 this.resourcesGraphsElement.appendChild(resource._resourcesTreeElement._resourceGraph.graphElement);
  350.             }
  351.  
  352.             resource._resourcesTreeElement.refresh();
  353.  
  354.             if (this.calculator.updateBoundaries(resource))
  355.                 boundariesChanged = true;
  356.         }
  357.  
  358.         if (boundariesChanged) {
  359.             // The boundaries changed, so all resource graphs are stale.
  360.             this._staleResources = this._resources;
  361.             staleResourcesLength = this._staleResources.length;
  362.         }
  363.  
  364.         for (var i = 0; i < staleResourcesLength; ++i)
  365.             this._staleResources[i]._resourcesTreeElement._resourceGraph.refresh(this.calculator);
  366.  
  367.         this._staleResources = [];
  368.  
  369.         this._updateGraphDividersIfNeeded();
  370.         this._sortResourcesIfNeeded();
  371.         this._updateSummaryGraph();
  372.     },
  373.  
  374.     resourceTrackingWasEnabled: function()
  375.     {
  376.         this.reset();
  377.     },
  378.  
  379.     resourceTrackingWasDisabled: function()
  380.     {
  381.         this.reset();
  382.     },
  383.  
  384.     reset: function()
  385.     {
  386.         this.closeVisibleResource();
  387.  
  388.         this.containerElement.scrollTop = 0;
  389.  
  390.         delete this.currentQuery;
  391.         this.searchCanceled();
  392.  
  393.         if (this._calculator)
  394.             this._calculator.reset();
  395.  
  396.         if (this._resources) {
  397.             var resourcesLength = this._resources.length;
  398.             for (var i = 0; i < resourcesLength; ++i) {
  399.                 var resource = this._resources[i];
  400.  
  401.                 resource.warnings = 0;
  402.                 resource.errors = 0;
  403.  
  404.                 delete resource._resourcesTreeElement;
  405.                 delete resource._resourcesView;
  406.             }
  407.         }
  408.  
  409.         this._resources = [];
  410.         this._staleResources = [];
  411.  
  412.         this.resourcesTreeElement.removeChildren();
  413.         this.viewsContainerElement.removeChildren();
  414.         this.resourcesGraphsElement.removeChildren();
  415.         this.legendElement.removeChildren();
  416.  
  417.         this._updateGraphDividersIfNeeded(true);
  418.  
  419.         this._drawSummaryGraph(); // draws an empty graph
  420.  
  421.         if (InspectorController.resourceTrackingEnabled()) {
  422.             this.enableToggleButton.title = WebInspector.UIString("Resource tracking enabled. Click to disable.");
  423.             this.enableToggleButton.addStyleClass("toggled-on");
  424.             this.largerResourcesButton.removeStyleClass("hidden");
  425.             this.sortingSelectElement.removeStyleClass("hidden");
  426.             this.panelEnablerView.visible = false;
  427.         } else {
  428.             this.enableToggleButton.title = WebInspector.UIString("Resource tracking disabled. Click to enable.");
  429.             this.enableToggleButton.removeStyleClass("toggled-on");
  430.             this.largerResourcesButton.addStyleClass("hidden");
  431.             this.sortingSelectElement.addStyleClass("hidden");
  432.             this.panelEnablerView.visible = true;
  433.         }
  434.     },
  435.  
  436.     addResource: function(resource)
  437.     {
  438.         this._resources.push(resource);
  439.         this.refreshResource(resource);
  440.     },
  441.  
  442.     removeResource: function(resource)
  443.     {
  444.         if (this.visibleView === resource._resourcesView)
  445.             this.closeVisibleResource();
  446.  
  447.         this._resources.remove(resource, true);
  448.  
  449.         if (resource._resourcesTreeElement) {
  450.             this.resourcesTreeElement.removeChild(resource._resourcesTreeElement);
  451.             this.resourcesGraphsElement.removeChild(resource._resourcesTreeElement._resourceGraph.graphElement);
  452.         }
  453.  
  454.         resource.warnings = 0;
  455.         resource.errors = 0;
  456.  
  457.         delete resource._resourcesTreeElement;
  458.         delete resource._resourcesView;
  459.  
  460.         this._adjustScrollPosition();
  461.     },
  462.  
  463.     addMessageToResource: function(resource, msg)
  464.     {
  465.         if (!resource)
  466.             return;
  467.  
  468.         switch (msg.level) {
  469.         case WebInspector.ConsoleMessage.MessageLevel.Warning:
  470.             resource.warnings += msg.repeatDelta;
  471.             break;
  472.         case WebInspector.ConsoleMessage.MessageLevel.Error:
  473.             resource.errors += msg.repeatDelta;
  474.             break;
  475.         }
  476.  
  477.         if (!this.currentQuery && resource._resourcesTreeElement)
  478.             resource._resourcesTreeElement.updateErrorsAndWarnings();
  479.  
  480.         var view = this.resourceViewForResource(resource);
  481.         if (view.addMessage)
  482.             view.addMessage(msg);
  483.     },
  484.  
  485.     clearMessages: function()
  486.     {
  487.         var resourcesLength = this._resources.length;
  488.         for (var i = 0; i < resourcesLength; ++i) {
  489.             var resource = this._resources[i];
  490.             resource.warnings = 0;
  491.             resource.errors = 0;
  492.  
  493.             if (!this.currentQuery && resource._resourcesTreeElement)
  494.                 resource._resourcesTreeElement.updateErrorsAndWarnings();
  495.  
  496.             var view = resource._resourcesView;
  497.             if (!view || !view.clearMessages)
  498.                 continue;
  499.             view.clearMessages();
  500.         }
  501.     },
  502.  
  503.     refreshResource: function(resource)
  504.     {
  505.         this._staleResources.push(resource);
  506.         this.needsRefresh = true;
  507.     },
  508.  
  509.     recreateViewForResourceIfNeeded: function(resource)
  510.     {
  511.         if (!resource || !resource._resourcesView)
  512.             return;
  513.  
  514.         var newView = this._createResourceView(resource);
  515.         if (newView.prototype === resource._resourcesView.prototype)
  516.             return;
  517.  
  518.         resource.warnings = 0;
  519.         resource.errors = 0;
  520.  
  521.         if (!this.currentQuery && resource._resourcesTreeElement)
  522.             resource._resourcesTreeElement.updateErrorsAndWarnings();
  523.  
  524.         var oldView = resource._resourcesView;
  525.  
  526.         resource._resourcesView.detach();
  527.         delete resource._resourcesView;
  528.  
  529.         resource._resourcesView = newView;
  530.  
  531.         newView.headersVisible = oldView.headersVisible;
  532.  
  533.         if (oldView.visible && oldView.element.parentNode)
  534.             newView.show(oldView.element.parentNode);
  535.     },
  536.  
  537.     showResource: function(resource, line)
  538.     {
  539.         if (!resource)
  540.             return;
  541.  
  542.         this.containerElement.addStyleClass("viewing-resource");
  543.  
  544.         if (this.visibleResource && this.visibleResource._resourcesView)
  545.             this.visibleResource._resourcesView.hide();
  546.  
  547.         var view = this.resourceViewForResource(resource);
  548.         view.headersVisible = true;
  549.         view.show(this.viewsContainerElement);
  550.  
  551.         if (line) {
  552.             if (view.revealLine)
  553.                 view.revealLine(line);
  554.             if (view.highlightLine)
  555.                 view.highlightLine(line);
  556.         }
  557.  
  558.         if (resource._resourcesTreeElement) {
  559.             resource._resourcesTreeElement.reveal();
  560.             resource._resourcesTreeElement.select(true);
  561.         }
  562.  
  563.         this.visibleResource = resource;
  564.  
  565.         this._updateSidebarWidth();
  566.     },
  567.  
  568.     showView: function(view)
  569.     {
  570.         if (!view)
  571.             return;
  572.         this.showResource(view.resource);
  573.     },
  574.  
  575.     closeVisibleResource: function()
  576.     {
  577.         this.containerElement.removeStyleClass("viewing-resource");
  578.         this._updateDividersLabelBarPosition();
  579.  
  580.         if (this.visibleResource && this.visibleResource._resourcesView)
  581.             this.visibleResource._resourcesView.hide();
  582.         delete this.visibleResource;
  583.  
  584.         if (this._lastSelectedGraphTreeElement)
  585.             this._lastSelectedGraphTreeElement.select(true);
  586.  
  587.         this._updateSidebarWidth();
  588.     },
  589.  
  590.     resourceViewForResource: function(resource)
  591.     {
  592.         if (!resource)
  593.             return null;
  594.         if (!resource._resourcesView)
  595.             resource._resourcesView = this._createResourceView(resource);
  596.         return resource._resourcesView;
  597.     },
  598.  
  599.     sourceFrameForResource: function(resource)
  600.     {
  601.         var view = this.resourceViewForResource(resource);
  602.         if (!view)
  603.             return null;
  604.  
  605.         if (!view.setupSourceFrameIfNeeded)
  606.             return null;
  607.  
  608.         // Setting up the source frame requires that we be attached.
  609.         if (!this.element.parentNode)
  610.             this.attach();
  611.  
  612.         view.setupSourceFrameIfNeeded();
  613.         return view.sourceFrame;
  614.     },
  615.  
  616.     handleKeyEvent: function(event)
  617.     {
  618.         this.sidebarTree.handleKeyEvent(event);
  619.     },
  620.  
  621.     _makeLegendElement: function(label, value, color)
  622.     {
  623.         var legendElement = document.createElement("label");
  624.         legendElement.className = "resources-graph-legend-item";
  625.  
  626.         if (color) {
  627.             var swatch = document.createElement("canvas");
  628.             swatch.className = "resources-graph-legend-swatch";
  629.             swatch.setAttribute("width", "13");
  630.             swatch.setAttribute("height", "24");
  631.  
  632.             legendElement.appendChild(swatch);
  633.  
  634.             this._drawSwatch(swatch, color);
  635.         }
  636.  
  637.         var labelElement = document.createElement("div");
  638.         labelElement.className = "resources-graph-legend-label";
  639.         legendElement.appendChild(labelElement);
  640.  
  641.         var headerElement = document.createElement("div");
  642.         var headerElement = document.createElement("div");
  643.         headerElement.className = "resources-graph-legend-header";
  644.         headerElement.textContent = label;
  645.         labelElement.appendChild(headerElement);
  646.  
  647.         var valueElement = document.createElement("div");
  648.         valueElement.className = "resources-graph-legend-value";
  649.         valueElement.textContent = value;
  650.         labelElement.appendChild(valueElement);
  651.  
  652.         return legendElement;
  653.     },
  654.  
  655.     _sortResourcesIfNeeded: function()
  656.     {
  657.         var sortedElements = [].concat(this.resourcesTreeElement.children);
  658.         sortedElements.sort(this.sortingFunction);
  659.  
  660.         var sortedElementsLength = sortedElements.length;
  661.         for (var i = 0; i < sortedElementsLength; ++i) {
  662.             var treeElement = sortedElements[i];
  663.             if (treeElement === this.resourcesTreeElement.children[i])
  664.                 continue;
  665.  
  666.             var wasSelected = treeElement.selected;
  667.             this.resourcesTreeElement.removeChild(treeElement);
  668.             this.resourcesTreeElement.insertChild(treeElement, i);
  669.             if (wasSelected)
  670.                 treeElement.select(true);
  671.  
  672.             var graphElement = treeElement._resourceGraph.graphElement;
  673.             this.resourcesGraphsElement.insertBefore(graphElement, this.resourcesGraphsElement.children[i]);
  674.         }
  675.     },
  676.  
  677.     _updateGraphDividersIfNeeded: function(force)
  678.     {
  679.         if (!this.visible) {
  680.             this.needsRefresh = true;
  681.             return;
  682.         }
  683.  
  684.         if (document.body.offsetWidth <= 0) {
  685.             // The stylesheet hasn't loaded yet or the window is closed,
  686.             // so we can't calculate what is need. Return early.
  687.             return;
  688.         }
  689.  
  690.         var dividerCount = Math.round(this.dividersElement.offsetWidth / 64);
  691.         var slice = this.calculator.boundarySpan / dividerCount;
  692.         if (!force && this._currentDividerSlice === slice)
  693.             return;
  694.  
  695.         this._currentDividerSlice = slice;
  696.  
  697.         this.dividersElement.removeChildren();
  698.         this.dividersLabelBarElement.removeChildren();
  699.  
  700.         for (var i = 1; i <= dividerCount; ++i) {
  701.             var divider = document.createElement("div");
  702.             divider.className = "resources-divider";
  703.             if (i === dividerCount)
  704.                 divider.addStyleClass("last");
  705.             divider.style.left = ((i / dividerCount) * 100) + "%";
  706.  
  707.             this.dividersElement.appendChild(divider.cloneNode());
  708.  
  709.             var label = document.createElement("div");
  710.             label.className = "resources-divider-label";
  711.             if (!isNaN(slice))
  712.                 label.textContent = this.calculator.formatValue(slice * i);
  713.             divider.appendChild(label);
  714.  
  715.             this.dividersLabelBarElement.appendChild(divider);
  716.         }
  717.     },
  718.  
  719.     _fadeOutRect: function(ctx, x, y, w, h, a1, a2)
  720.     {
  721.         ctx.save();
  722.  
  723.         var gradient = ctx.createLinearGradient(x, y, x, y + h);
  724.         gradient.addColorStop(0.0, "rgba(0, 0, 0, " + (1.0 - a1) + ")");
  725.         gradient.addColorStop(0.8, "rgba(0, 0, 0, " + (1.0 - a2) + ")");
  726.         gradient.addColorStop(1.0, "rgba(0, 0, 0, 1.0)");
  727.  
  728.         ctx.globalCompositeOperation = "destination-out";
  729.  
  730.         ctx.fillStyle = gradient;
  731.         ctx.fillRect(x, y, w, h);
  732.  
  733.         ctx.restore();
  734.     },
  735.  
  736.     _drawSwatch: function(canvas, color)
  737.     {
  738.         var ctx = canvas.getContext("2d");
  739.  
  740.         function drawSwatchSquare() {
  741.             ctx.fillStyle = color;
  742.             ctx.fillRect(0, 0, 13, 13);
  743.  
  744.             var gradient = ctx.createLinearGradient(0, 0, 13, 13);
  745.             gradient.addColorStop(0.0, "rgba(255, 255, 255, 0.2)");
  746.             gradient.addColorStop(1.0, "rgba(255, 255, 255, 0.0)");
  747.  
  748.             ctx.fillStyle = gradient;
  749.             ctx.fillRect(0, 0, 13, 13);
  750.  
  751.             gradient = ctx.createLinearGradient(13, 13, 0, 0);
  752.             gradient.addColorStop(0.0, "rgba(0, 0, 0, 0.2)");
  753.             gradient.addColorStop(1.0, "rgba(0, 0, 0, 0.0)");
  754.  
  755.             ctx.fillStyle = gradient;
  756.             ctx.fillRect(0, 0, 13, 13);
  757.  
  758.             ctx.strokeStyle = "rgba(0, 0, 0, 0.6)";
  759.             ctx.strokeRect(0.5, 0.5, 12, 12);
  760.         }
  761.  
  762.         ctx.clearRect(0, 0, 13, 24);
  763.  
  764.         drawSwatchSquare();
  765.  
  766.         ctx.save();
  767.  
  768.         ctx.translate(0, 25);
  769.         ctx.scale(1, -1);
  770.  
  771.         drawSwatchSquare();
  772.  
  773.         ctx.restore();
  774.  
  775.         this._fadeOutRect(ctx, 0, 13, 13, 13, 0.5, 0.0);
  776.     },
  777.  
  778.     _drawSummaryGraph: function(segments)
  779.     {
  780.         if (!this.summaryGraphElement)
  781.             return;
  782.  
  783.         if (!segments || !segments.length) {
  784.             segments = [{color: "white", value: 1}];
  785.             this._showingEmptySummaryGraph = true;
  786.         } else
  787.             delete this._showingEmptySummaryGraph;
  788.  
  789.         // Calculate the total of all segments.
  790.         var total = 0;
  791.         for (var i = 0; i < segments.length; ++i)
  792.             total += segments[i].value;
  793.  
  794.         // Calculate the percentage of each segment, rounded to the nearest percent.
  795.         var percents = segments.map(function(s) { return Math.max(Math.round(100 * s.value / total), 1) });
  796.  
  797.         // Calculate the total percentage.
  798.         var percentTotal = 0;
  799.         for (var i = 0; i < percents.length; ++i)
  800.             percentTotal += percents[i];
  801.  
  802.         // Make sure our percentage total is not greater-than 100, it can be greater
  803.         // if we rounded up for a few segments.
  804.         while (percentTotal > 100) {
  805.             for (var i = 0; i < percents.length && percentTotal > 100; ++i) {
  806.                 if (percents[i] > 1) {
  807.                     --percents[i];
  808.                     --percentTotal;
  809.                 }
  810.             }
  811.         }
  812.  
  813.         // Make sure our percentage total is not less-than 100, it can be less
  814.         // if we rounded down for a few segments.
  815.         while (percentTotal < 100) {
  816.             for (var i = 0; i < percents.length && percentTotal < 100; ++i) {
  817.                 ++percents[i];
  818.                 ++percentTotal;
  819.             }
  820.         }
  821.  
  822.         var ctx = this.summaryGraphElement.getContext("2d");
  823.  
  824.         var x = 0;
  825.         var y = 0;
  826.         var w = 450;
  827.         var h = 19;
  828.         var r = (h / 2);
  829.  
  830.         function drawPillShadow()
  831.         {
  832.             // This draws a line with a shadow that is offset away from the line. The line is stroked
  833.             // twice with different X shadow offsets to give more feathered edges. Later we erase the
  834.             // line with destination-out 100% transparent black, leaving only the shadow. This only
  835.             // works if nothing has been drawn into the canvas yet.
  836.  
  837.             ctx.beginPath();
  838.             ctx.moveTo(x + 4, y + h - 3 - 0.5);
  839.             ctx.lineTo(x + w - 4, y + h - 3 - 0.5);
  840.             ctx.closePath();
  841.  
  842.             ctx.save();
  843.  
  844.             ctx.shadowBlur = 2;
  845.             ctx.shadowColor = "rgba(0, 0, 0, 0.5)";
  846.             ctx.shadowOffsetX = 3;
  847.             ctx.shadowOffsetY = 5;
  848.  
  849.             ctx.strokeStyle = "white";
  850.             ctx.lineWidth = 1;
  851.  
  852.             ctx.stroke();
  853.  
  854.             ctx.shadowOffsetX = -3;
  855.  
  856.             ctx.stroke();
  857.  
  858.             ctx.restore();
  859.  
  860.             ctx.save();
  861.  
  862.             ctx.globalCompositeOperation = "destination-out";
  863.             ctx.strokeStyle = "rgba(0, 0, 0, 1)";
  864.             ctx.lineWidth = 1;
  865.  
  866.             ctx.stroke();
  867.  
  868.             ctx.restore();
  869.         }
  870.  
  871.         function drawPill()
  872.         {
  873.             // Make a rounded rect path.
  874.             ctx.beginPath();
  875.             ctx.moveTo(x, y + r);
  876.             ctx.lineTo(x, y + h - r);
  877.             ctx.quadraticCurveTo(x, y + h, x + r, y + h);
  878.             ctx.lineTo(x + w - r, y + h);
  879.             ctx.quadraticCurveTo(x + w, y + h, x + w, y + h - r);
  880.             ctx.lineTo(x + w, y + r);
  881.             ctx.quadraticCurveTo(x + w, y, x + w - r, y);
  882.             ctx.lineTo(x + r, y);
  883.             ctx.quadraticCurveTo(x, y, x, y + r);
  884.             ctx.closePath();
  885.  
  886.             // Clip to the rounded rect path.
  887.             ctx.save();
  888.             ctx.clip();
  889.  
  890.             // Fill the segments with the associated color.
  891.             var previousSegmentsWidth = 0;
  892.             for (var i = 0; i < segments.length; ++i) {
  893.                 var segmentWidth = Math.round(w * percents[i] / 100);
  894.                 ctx.fillStyle = segments[i].color;
  895.                 ctx.fillRect(x + previousSegmentsWidth, y, segmentWidth, h);
  896.                 previousSegmentsWidth += segmentWidth;
  897.             }
  898.  
  899.             // Draw the segment divider lines.
  900.             ctx.lineWidth = 1;
  901.             for (var i = 1; i < 20; ++i) {
  902.                 ctx.beginPath();
  903.                 ctx.moveTo(x + (i * Math.round(w / 20)) + 0.5, y);
  904.                 ctx.lineTo(x + (i * Math.round(w / 20)) + 0.5, y + h);
  905.                 ctx.closePath();
  906.  
  907.                 ctx.strokeStyle = "rgba(0, 0, 0, 0.2)";
  908.                 ctx.stroke();
  909.  
  910.                 ctx.beginPath();
  911.                 ctx.moveTo(x + (i * Math.round(w / 20)) + 1.5, y);
  912.                 ctx.lineTo(x + (i * Math.round(w / 20)) + 1.5, y + h);
  913.                 ctx.closePath();
  914.  
  915.                 ctx.strokeStyle = "rgba(255, 255, 255, 0.2)";
  916.                 ctx.stroke();
  917.             }
  918.  
  919.             // Draw the pill shading.
  920.             var lightGradient = ctx.createLinearGradient(x, y, x, y + (h / 1.5));
  921.             lightGradient.addColorStop(0.0, "rgba(220, 220, 220, 0.6)");
  922.             lightGradient.addColorStop(0.4, "rgba(220, 220, 220, 0.2)");
  923.             lightGradient.addColorStop(1.0, "rgba(255, 255, 255, 0.0)");
  924.  
  925.             var darkGradient = ctx.createLinearGradient(x, y + (h / 3), x, y + h);
  926.             darkGradient.addColorStop(0.0, "rgba(0, 0, 0, 0.0)");
  927.             darkGradient.addColorStop(0.8, "rgba(0, 0, 0, 0.2)");
  928.             darkGradient.addColorStop(1.0, "rgba(0, 0, 0, 0.5)");
  929.  
  930.             ctx.fillStyle = darkGradient;
  931.             ctx.fillRect(x, y, w, h);
  932.  
  933.             ctx.fillStyle = lightGradient;
  934.             ctx.fillRect(x, y, w, h);
  935.  
  936.             ctx.restore();
  937.         }
  938.  
  939.         ctx.clearRect(x, y, w, (h * 2));
  940.  
  941.         drawPillShadow();
  942.         drawPill();
  943.  
  944.         ctx.save();
  945.  
  946.         ctx.translate(0, (h * 2) + 1);
  947.         ctx.scale(1, -1);
  948.  
  949.         drawPill();
  950.  
  951.         ctx.restore();
  952.  
  953.         this._fadeOutRect(ctx, x, y + h + 1, w, h, 0.5, 0.0);
  954.     },
  955.  
  956.     _updateSummaryGraph: function()
  957.     {
  958.         var graphInfo = this.calculator.computeSummaryValues(this._resources);
  959.  
  960.         var categoryOrder = ["documents", "stylesheets", "images", "scripts", "xhr", "fonts", "other"];
  961.         var categoryColors = {documents: {r: 47, g: 102, b: 236}, stylesheets: {r: 157, g: 231, b: 119}, images: {r: 164, g: 60, b: 255}, scripts: {r: 255, g: 121, b: 0}, xhr: {r: 231, g: 231, b: 10}, fonts: {r: 255, g: 82, b: 62}, other: {r: 186, g: 186, b: 186}};
  962.         var fillSegments = [];
  963.  
  964.         this.legendElement.removeChildren();
  965.  
  966.         for (var i = 0; i < categoryOrder.length; ++i) {
  967.             var category = categoryOrder[i];
  968.             var size = graphInfo.categoryValues[category];
  969.             if (!size)
  970.                 continue;
  971.  
  972.             var color = categoryColors[category];
  973.             var colorString = "rgb(" + color.r + ", " + color.g + ", " + color.b + ")";
  974.  
  975.             var fillSegment = {color: colorString, value: size};
  976.             fillSegments.push(fillSegment);
  977.  
  978.             var legendLabel = this._makeLegendElement(WebInspector.resourceCategories[category].title, this.calculator.formatValue(size), colorString);
  979.             this.legendElement.appendChild(legendLabel);
  980.         }
  981.  
  982.         if (graphInfo.total) {
  983.             var totalLegendLabel = this._makeLegendElement(WebInspector.UIString("Total"), this.calculator.formatValue(graphInfo.total));
  984.             totalLegendLabel.addStyleClass("total");
  985.             this.legendElement.appendChild(totalLegendLabel);
  986.         }
  987.  
  988.         this._drawSummaryGraph(fillSegments);
  989.     },
  990.  
  991.     _updateDividersLabelBarPosition: function()
  992.     {
  993.         var scrollTop = this.containerElement.scrollTop;
  994.         var dividersTop = (scrollTop < this.summaryElement.offsetHeight ? this.summaryElement.offsetHeight : scrollTop);
  995.         this.dividersElement.style.top = scrollTop + "px";
  996.         this.dividersLabelBarElement.style.top = dividersTop + "px";
  997.     },
  998.  
  999.     _graphSelected: function(treeElement)
  1000.     {
  1001.         if (this._lastSelectedGraphTreeElement)
  1002.             this._lastSelectedGraphTreeElement.selectedSortingOptionIndex = this.sortingSelectElement.selectedIndex;
  1003.  
  1004.         this._lastSelectedGraphTreeElement = treeElement;
  1005.  
  1006.         this.sortingSelectElement.removeChildren();
  1007.         for (var i = 0; i < treeElement.sortingOptions.length; ++i) {
  1008.             var sortingOption = treeElement.sortingOptions[i];
  1009.             var option = document.createElement("option");
  1010.             option.label = sortingOption.name;
  1011.             option.sortingFunction = sortingOption.sortingFunction;
  1012.             option.calculator = sortingOption.calculator;
  1013.             this.sortingSelectElement.appendChild(option);
  1014.         }
  1015.  
  1016.         this.sortingSelectElement.selectedIndex = treeElement.selectedSortingOptionIndex;
  1017.         this._changeSortingFunction();
  1018.  
  1019.         this.closeVisibleResource();
  1020.         this.containerElement.scrollTop = 0;
  1021.     },
  1022.  
  1023.     _toggleLargerResources: function()
  1024.     {
  1025.         if (!this.resourcesTreeElement._childrenListNode)
  1026.             return;
  1027.  
  1028.         this.resourcesTreeElement.smallChildren = !this.resourcesTreeElement.smallChildren;
  1029.  
  1030.         if (this.resourcesTreeElement.smallChildren) {
  1031.             this.resourcesGraphsElement.addStyleClass("small");
  1032.             this.largerResourcesButton.title = WebInspector.UIString("Use large resource rows.");
  1033.             this.largerResourcesButton.removeStyleClass("toggled-on");
  1034.             this._adjustScrollPosition();
  1035.         } else {
  1036.             this.resourcesGraphsElement.removeStyleClass("small");
  1037.             this.largerResourcesButton.title = WebInspector.UIString("Use small resource rows.");
  1038.             this.largerResourcesButton.addStyleClass("toggled-on");
  1039.         }
  1040.     },
  1041.  
  1042.     _adjustScrollPosition: function()
  1043.     {
  1044.         // Prevent the container from being scrolled off the end.
  1045.         if ((this.containerElement.scrollTop + this.containerElement.offsetHeight) > this.sidebarElement.offsetHeight)
  1046.             this.containerElement.scrollTop = (this.sidebarElement.offsetHeight - this.containerElement.offsetHeight);
  1047.     },
  1048.  
  1049.     _changeSortingFunction: function()
  1050.     {
  1051.         var selectedOption = this.sortingSelectElement[this.sortingSelectElement.selectedIndex];
  1052.         this.sortingFunction = selectedOption.sortingFunction;
  1053.         this.calculator = selectedOption.calculator;
  1054.     },
  1055.  
  1056.     _createResourceView: function(resource)
  1057.     {
  1058.         switch (resource.category) {
  1059.             case WebInspector.resourceCategories.documents:
  1060.             case WebInspector.resourceCategories.stylesheets:
  1061.             case WebInspector.resourceCategories.scripts:
  1062.             case WebInspector.resourceCategories.xhr:
  1063.                 return new WebInspector.SourceView(resource);
  1064.             case WebInspector.resourceCategories.images:
  1065.                 return new WebInspector.ImageView(resource);
  1066.             case WebInspector.resourceCategories.fonts:
  1067.                 return new WebInspector.FontView(resource);
  1068.             default:
  1069.                 return new WebInspector.ResourceView(resource);
  1070.         }
  1071.     },
  1072.  
  1073.     _startSidebarDragging: function(event)
  1074.     {
  1075.         WebInspector.elementDragStart(this.sidebarResizeElement, this._sidebarDragging.bind(this), this._endSidebarDragging.bind(this), event, "col-resize");
  1076.     },
  1077.  
  1078.     _sidebarDragging: function(event)
  1079.     {
  1080.         this._updateSidebarWidth(event.pageX);
  1081.  
  1082.         event.preventDefault();
  1083.     },
  1084.  
  1085.     _endSidebarDragging: function(event)
  1086.     {
  1087.         WebInspector.elementDragEnd(event);
  1088.     },
  1089.  
  1090.     _updateSidebarWidth: function(width)
  1091.     {
  1092.         if (this.sidebarElement.offsetWidth <= 0) {
  1093.             // The stylesheet hasn't loaded yet or the window is closed,
  1094.             // so we can't calculate what is need. Return early.
  1095.             return;
  1096.         }
  1097.  
  1098.         if (!("_currentSidebarWidth" in this))
  1099.             this._currentSidebarWidth = this.sidebarElement.offsetWidth;
  1100.  
  1101.         if (typeof width === "undefined")
  1102.             width = this._currentSidebarWidth;
  1103.  
  1104.         width = Number.constrain(width, Preferences.minSidebarWidth, window.innerWidth / 2);
  1105.  
  1106.         this._currentSidebarWidth = width;
  1107.  
  1108.         if (this.visibleResource) {
  1109.             this.containerElement.style.width = width + "px";
  1110.             this.sidebarElement.style.removeProperty("width");
  1111.         } else {
  1112.             this.sidebarElement.style.width = width + "px";
  1113.             this.containerElement.style.removeProperty("width");
  1114.         }
  1115.  
  1116.         this.containerContentElement.style.left = width + "px";
  1117.         this.viewsContainerElement.style.left = width + "px";
  1118.         this.sidebarResizeElement.style.left = (width - 3) + "px";
  1119.  
  1120.         this._updateGraphDividersIfNeeded();
  1121.  
  1122.         var visibleView = this.visibleView;
  1123.         if (visibleView && "resize" in visibleView)
  1124.             visibleView.resize();
  1125.     },
  1126.  
  1127.     _enableResourceTracking: function()
  1128.     {
  1129.         if (InspectorController.resourceTrackingEnabled())
  1130.             return;
  1131.         this._toggleResourceTracking(this.panelEnablerView.alwaysEnabled);
  1132.     },
  1133.  
  1134.     _toggleResourceTracking: function(optionalAlways)
  1135.     {
  1136.         if (InspectorController.resourceTrackingEnabled()) {
  1137.             this.largerResourcesButton.visible = false;
  1138.             this.sortingSelectElement.visible = false;
  1139.             InspectorController.disableResourceTracking(true);
  1140.         } else {
  1141.             this.largerResourcesButton.visible = true;
  1142.             this.sortingSelectElement.visible = true;
  1143.             InspectorController.enableResourceTracking(!!optionalAlways);
  1144.         }
  1145.     }
  1146. }
  1147.  
  1148. WebInspector.ResourcesPanel.prototype.__proto__ = WebInspector.Panel.prototype;
  1149.  
  1150. WebInspector.ResourceCalculator = function()
  1151. {
  1152. }
  1153.  
  1154. WebInspector.ResourceCalculator.prototype = {
  1155.     computeSummaryValues: function(resources)
  1156.     {
  1157.         var total = 0;
  1158.         var categoryValues = {};
  1159.  
  1160.         var resourcesLength = resources.length;
  1161.         for (var i = 0; i < resourcesLength; ++i) {
  1162.             var resource = resources[i];
  1163.             var value = this._value(resource);
  1164.             if (typeof value === "undefined")
  1165.                 continue;
  1166.             if (!(resource.category.name in categoryValues))
  1167.                 categoryValues[resource.category.name] = 0;
  1168.             categoryValues[resource.category.name] += value;
  1169.             total += value;
  1170.         }
  1171.  
  1172.         return {categoryValues: categoryValues, total: total};
  1173.     },
  1174.  
  1175.     computeBarGraphPercentages: function(resource)
  1176.     {
  1177.         return {start: 0, middle: 0, end: (this._value(resource) / this.boundarySpan) * 100};
  1178.     },
  1179.  
  1180.     computeBarGraphLabels: function(resource)
  1181.     {
  1182.         const label = this.formatValue(this._value(resource));
  1183.         var tooltip = label;
  1184.         if (resource.cached)
  1185.             tooltip = WebInspector.UIString("%s (from cache)", tooltip);
  1186.         return {left: label, right: label, tooltip: tooltip};
  1187.     },
  1188.  
  1189.     get boundarySpan()
  1190.     {
  1191.         return this.maximumBoundary - this.minimumBoundary;
  1192.     },
  1193.  
  1194.     updateBoundaries: function(resource)
  1195.     {
  1196.         this.minimumBoundary = 0;
  1197.  
  1198.         var value = this._value(resource);
  1199.         if (typeof this.maximumBoundary === "undefined" || value > this.maximumBoundary) {
  1200.             this.maximumBoundary = value;
  1201.             return true;
  1202.         }
  1203.  
  1204.         return false;
  1205.     },
  1206.  
  1207.     reset: function()
  1208.     {
  1209.         delete this.minimumBoundary;
  1210.         delete this.maximumBoundary;
  1211.     },
  1212.  
  1213.     _value: function(resource)
  1214.     {
  1215.         return 0;
  1216.     },
  1217.  
  1218.     formatValue: function(value)
  1219.     {
  1220.         return value.toString();
  1221.     }
  1222. }
  1223.  
  1224. WebInspector.ResourceTimeCalculator = function(startAtZero)
  1225. {
  1226.     WebInspector.ResourceCalculator.call(this);
  1227.     this.startAtZero = startAtZero;
  1228. }
  1229.  
  1230. WebInspector.ResourceTimeCalculator.prototype = {
  1231.     computeSummaryValues: function(resources)
  1232.     {
  1233.         var resourcesByCategory = {};
  1234.         var resourcesLength = resources.length;
  1235.         for (var i = 0; i < resourcesLength; ++i) {
  1236.             var resource = resources[i];
  1237.             if (!(resource.category.name in resourcesByCategory))
  1238.                 resourcesByCategory[resource.category.name] = [];
  1239.             resourcesByCategory[resource.category.name].push(resource);
  1240.         }
  1241.  
  1242.         var earliestStart;
  1243.         var latestEnd;
  1244.         var categoryValues = {};
  1245.         for (var category in resourcesByCategory) {
  1246.             resourcesByCategory[category].sort(WebInspector.Resource.CompareByTime);
  1247.             categoryValues[category] = 0;
  1248.  
  1249.             var segment = {start: -1, end: -1};
  1250.  
  1251.             var categoryResources = resourcesByCategory[category];
  1252.             var resourcesLength = categoryResources.length;
  1253.             for (var i = 0; i < resourcesLength; ++i) {
  1254.                 var resource = categoryResources[i];
  1255.                 if (resource.startTime === -1 || resource.endTime === -1)
  1256.                     continue;
  1257.  
  1258.                 if (typeof earliestStart === "undefined")
  1259.                     earliestStart = resource.startTime;
  1260.                 else
  1261.                     earliestStart = Math.min(earliestStart, resource.startTime);
  1262.  
  1263.                 if (typeof latestEnd === "undefined")
  1264.                     latestEnd = resource.endTime;
  1265.                 else
  1266.                     latestEnd = Math.max(latestEnd, resource.endTime);
  1267.  
  1268.                 if (resource.startTime <= segment.end) {
  1269.                     segment.end = Math.max(segment.end, resource.endTime);
  1270.                     continue;
  1271.                 }
  1272.  
  1273.                 categoryValues[category] += segment.end - segment.start;
  1274.  
  1275.                 segment.start = resource.startTime;
  1276.                 segment.end = resource.endTime;
  1277.             }
  1278.  
  1279.             // Add the last segment
  1280.             categoryValues[category] += segment.end - segment.start;
  1281.         }
  1282.  
  1283.         return {categoryValues: categoryValues, total: latestEnd - earliestStart};
  1284.     },
  1285.  
  1286.     computeBarGraphPercentages: function(resource)
  1287.     {
  1288.         if (resource.startTime !== -1)
  1289.             var start = ((resource.startTime - this.minimumBoundary) / this.boundarySpan) * 100;
  1290.         else
  1291.             var start = 0;
  1292.  
  1293.         if (resource.responseReceivedTime !== -1)
  1294.             var middle = ((resource.responseReceivedTime - this.minimumBoundary) / this.boundarySpan) * 100;
  1295.         else
  1296.             var middle = (this.startAtZero ? start : 100);
  1297.  
  1298.         if (resource.endTime !== -1)
  1299.             var end = ((resource.endTime - this.minimumBoundary) / this.boundarySpan) * 100;
  1300.         else
  1301.             var end = (this.startAtZero ? middle : 100);
  1302.  
  1303.         if (this.startAtZero) {
  1304.             end -= start;
  1305.             middle -= start;
  1306.             start = 0;
  1307.         }
  1308.  
  1309.         return {start: start, middle: middle, end: end};
  1310.     },
  1311.  
  1312.     computeBarGraphLabels: function(resource)
  1313.     {
  1314.         var leftLabel = "";
  1315.         if (resource.latency > 0)
  1316.             leftLabel = this.formatValue(resource.latency);
  1317.  
  1318.         var rightLabel = "";
  1319.         if (resource.responseReceivedTime !== -1 && resource.endTime !== -1)
  1320.             rightLabel = this.formatValue(resource.endTime - resource.responseReceivedTime);
  1321.  
  1322.         if (leftLabel && rightLabel) {
  1323.             var total = this.formatValue(resource.duration);
  1324.             var tooltip = WebInspector.UIString("%s latency, %s download (%s total)", leftLabel, rightLabel, total);
  1325.         } else if (leftLabel)
  1326.             var tooltip = WebInspector.UIString("%s latency", leftLabel);
  1327.         else if (rightLabel)
  1328.             var tooltip = WebInspector.UIString("%s download", rightLabel);
  1329.  
  1330.         if (resource.cached)
  1331.             tooltip = WebInspector.UIString("%s (from cache)", tooltip);
  1332.  
  1333.         return {left: leftLabel, right: rightLabel, tooltip: tooltip};
  1334.     },
  1335.  
  1336.     updateBoundaries: function(resource)
  1337.     {
  1338.         var didChange = false;
  1339.  
  1340.         var lowerBound;
  1341.         if (this.startAtZero)
  1342.             lowerBound = 0;
  1343.         else
  1344.             lowerBound = this._lowerBound(resource);
  1345.  
  1346.         if (lowerBound !== -1 && (typeof this.minimumBoundary === "undefined" || lowerBound < this.minimumBoundary)) {
  1347.             this.minimumBoundary = lowerBound;
  1348.             didChange = true;
  1349.         }
  1350.  
  1351.         var upperBound = this._upperBound(resource);
  1352.         if (upperBound !== -1 && (typeof this.maximumBoundary === "undefined" || upperBound > this.maximumBoundary)) {
  1353.             this.maximumBoundary = upperBound;
  1354.             didChange = true;
  1355.         }
  1356.  
  1357.         return didChange;
  1358.     },
  1359.  
  1360.     formatValue: function(value)
  1361.     {
  1362.         return Number.secondsToString(value, WebInspector.UIString.bind(WebInspector));
  1363.     },
  1364.  
  1365.     _lowerBound: function(resource)
  1366.     {
  1367.         return 0;
  1368.     },
  1369.  
  1370.     _upperBound: function(resource)
  1371.     {
  1372.         return 0;
  1373.     },
  1374. }
  1375.  
  1376. WebInspector.ResourceTimeCalculator.prototype.__proto__ = WebInspector.ResourceCalculator.prototype;
  1377.  
  1378. WebInspector.ResourceTransferTimeCalculator = function()
  1379. {
  1380.     WebInspector.ResourceTimeCalculator.call(this, false);
  1381. }
  1382.  
  1383. WebInspector.ResourceTransferTimeCalculator.prototype = {
  1384.     formatValue: function(value)
  1385.     {
  1386.         return Number.secondsToString(value, WebInspector.UIString.bind(WebInspector));
  1387.     },
  1388.  
  1389.     _lowerBound: function(resource)
  1390.     {
  1391.         return resource.startTime;
  1392.     },
  1393.  
  1394.     _upperBound: function(resource)
  1395.     {
  1396.         return resource.endTime;
  1397.     }
  1398. }
  1399.  
  1400. WebInspector.ResourceTransferTimeCalculator.prototype.__proto__ = WebInspector.ResourceTimeCalculator.prototype;
  1401.  
  1402. WebInspector.ResourceTransferDurationCalculator = function()
  1403. {
  1404.     WebInspector.ResourceTimeCalculator.call(this, true);
  1405. }
  1406.  
  1407. WebInspector.ResourceTransferDurationCalculator.prototype = {
  1408.     formatValue: function(value)
  1409.     {
  1410.         return Number.secondsToString(value, WebInspector.UIString.bind(WebInspector));
  1411.     },
  1412.  
  1413.     _upperBound: function(resource)
  1414.     {
  1415.         return resource.duration;
  1416.     }
  1417. }
  1418.  
  1419. WebInspector.ResourceTransferDurationCalculator.prototype.__proto__ = WebInspector.ResourceTimeCalculator.prototype;
  1420.  
  1421. WebInspector.ResourceTransferSizeCalculator = function()
  1422. {
  1423.     WebInspector.ResourceCalculator.call(this);
  1424. }
  1425.  
  1426. WebInspector.ResourceTransferSizeCalculator.prototype = {
  1427.     _value: function(resource)
  1428.     {
  1429.         return resource.contentLength;
  1430.     },
  1431.  
  1432.     formatValue: function(value)
  1433.     {
  1434.         return Number.bytesToString(value, WebInspector.UIString.bind(WebInspector));
  1435.     }
  1436. }
  1437.  
  1438. WebInspector.ResourceTransferSizeCalculator.prototype.__proto__ = WebInspector.ResourceCalculator.prototype;
  1439.  
  1440. WebInspector.ResourceSidebarTreeElement = function(resource)
  1441. {
  1442.     this.resource = resource;
  1443.  
  1444.     this.createIconElement();
  1445.  
  1446.     WebInspector.SidebarTreeElement.call(this, "resource-sidebar-tree-item", "", "", resource);
  1447.  
  1448.     this.refreshTitles();
  1449. }
  1450.  
  1451. WebInspector.ResourceSidebarTreeElement.prototype = {
  1452.     onattach: function()
  1453.     {
  1454.         WebInspector.SidebarTreeElement.prototype.onattach.call(this);
  1455.  
  1456.         this._listItemNode.addStyleClass("resources-category-" + this.resource.category.name);
  1457.     },
  1458.  
  1459.     onselect: function()
  1460.     {
  1461.         WebInspector.panels.resources.showResource(this.resource);
  1462.     },
  1463.  
  1464.     get mainTitle()
  1465.     {
  1466.         return this.resource.displayName;
  1467.     },
  1468.  
  1469.     set mainTitle(x)
  1470.     {
  1471.         // Do nothing.
  1472.     },
  1473.  
  1474.     get subtitle()
  1475.     {
  1476.         var subtitle = this.resource.displayDomain;
  1477.  
  1478.         if (this.resource.path && this.resource.lastPathComponent) {
  1479.             var lastPathComponentIndex = this.resource.path.lastIndexOf("/" + this.resource.lastPathComponent);
  1480.             if (lastPathComponentIndex != -1)
  1481.                 subtitle += this.resource.path.substring(0, lastPathComponentIndex);
  1482.         }
  1483.  
  1484.         return subtitle;
  1485.     },
  1486.  
  1487.     set subtitle(x)
  1488.     {
  1489.         // Do nothing.
  1490.     },
  1491.  
  1492.     createIconElement: function()
  1493.     {
  1494.         var previousIconElement = this.iconElement;
  1495.  
  1496.         if (this.resource.category === WebInspector.resourceCategories.images) {
  1497.             var previewImage = document.createElement("img");
  1498.             previewImage.className = "image-resource-icon-preview";
  1499.             previewImage.src = this.resource.url;
  1500.  
  1501.             this.iconElement = document.createElement("div");
  1502.             this.iconElement.className = "icon";
  1503.             this.iconElement.appendChild(previewImage);
  1504.         } else {
  1505.             this.iconElement = document.createElement("img");
  1506.             this.iconElement.className = "icon";
  1507.         }
  1508.  
  1509.         if (previousIconElement)
  1510.             previousIconElement.parentNode.replaceChild(this.iconElement, previousIconElement);
  1511.     },
  1512.  
  1513.     refresh: function()
  1514.     {
  1515.         this.refreshTitles();
  1516.  
  1517.         if (!this._listItemNode.hasStyleClass("resources-category-" + this.resource.category.name)) {
  1518.             this._listItemNode.removeMatchingStyleClasses("resources-category-\\w+");
  1519.             this._listItemNode.addStyleClass("resources-category-" + this.resource.category.name);
  1520.  
  1521.             this.createIconElement();
  1522.         }
  1523.     },
  1524.  
  1525.     resetBubble: function()
  1526.     {
  1527.         this.bubbleText = "";
  1528.         this.bubbleElement.removeStyleClass("search-matches");
  1529.         this.bubbleElement.removeStyleClass("warning");
  1530.         this.bubbleElement.removeStyleClass("error");
  1531.     },
  1532.  
  1533.     set searchMatches(matches)
  1534.     {
  1535.         this.resetBubble();
  1536.  
  1537.         if (!matches)
  1538.             return;
  1539.  
  1540.         this.bubbleText = matches;
  1541.         this.bubbleElement.addStyleClass("search-matches");
  1542.     },
  1543.  
  1544.     updateErrorsAndWarnings: function()
  1545.     {
  1546.         this.resetBubble();
  1547.  
  1548.         if (this.resource.warnings || this.resource.errors)
  1549.             this.bubbleText = (this.resource.warnings + this.resource.errors);
  1550.  
  1551.         if (this.resource.warnings)
  1552.             this.bubbleElement.addStyleClass("warning");
  1553.  
  1554.         if (this.resource.errors)
  1555.             this.bubbleElement.addStyleClass("error");
  1556.     }
  1557. }
  1558.  
  1559. WebInspector.ResourceSidebarTreeElement.CompareByAscendingStartTime = function(a, b)
  1560. {
  1561.     return WebInspector.Resource.CompareByStartTime(a.resource, b.resource)
  1562.         || WebInspector.Resource.CompareByEndTime(a.resource, b.resource)
  1563.         || WebInspector.Resource.CompareByResponseReceivedTime(a.resource, b.resource);
  1564. }
  1565.  
  1566. WebInspector.ResourceSidebarTreeElement.CompareByAscendingResponseReceivedTime = function(a, b)
  1567. {
  1568.     return WebInspector.Resource.CompareByResponseReceivedTime(a.resource, b.resource)
  1569.         || WebInspector.Resource.CompareByStartTime(a.resource, b.resource)
  1570.         || WebInspector.Resource.CompareByEndTime(a.resource, b.resource);
  1571. }
  1572.  
  1573. WebInspector.ResourceSidebarTreeElement.CompareByAscendingEndTime = function(a, b)
  1574. {
  1575.     return WebInspector.Resource.CompareByEndTime(a.resource, b.resource)
  1576.         || WebInspector.Resource.CompareByStartTime(a.resource, b.resource)
  1577.         || WebInspector.Resource.CompareByResponseReceivedTime(a.resource, b.resource);
  1578. }
  1579.  
  1580. WebInspector.ResourceSidebarTreeElement.CompareByDescendingDuration = function(a, b)
  1581. {
  1582.     return -1 * WebInspector.Resource.CompareByDuration(a.resource, b.resource);
  1583. }
  1584.  
  1585. WebInspector.ResourceSidebarTreeElement.CompareByDescendingLatency = function(a, b)
  1586. {
  1587.     return -1 * WebInspector.Resource.CompareByLatency(a.resource, b.resource);
  1588. }
  1589.  
  1590. WebInspector.ResourceSidebarTreeElement.CompareByDescendingSize = function(a, b)
  1591. {
  1592.     return -1 * WebInspector.Resource.CompareBySize(a.resource, b.resource);
  1593. }
  1594.  
  1595. WebInspector.ResourceSidebarTreeElement.prototype.__proto__ = WebInspector.SidebarTreeElement.prototype;
  1596.  
  1597. WebInspector.ResourceGraph = function(resource)
  1598. {
  1599.     this.resource = resource;
  1600.  
  1601.     this._graphElement = document.createElement("div");
  1602.     this._graphElement.className = "resources-graph-side";
  1603.     this._graphElement.addEventListener("mouseover", this.refreshLabelPositions.bind(this), false);
  1604.  
  1605.     if (resource.cached)
  1606.         this._graphElement.addStyleClass("resource-cached");
  1607.  
  1608.     this._barAreaElement = document.createElement("div");
  1609.     this._barAreaElement.className = "resources-graph-bar-area hidden";
  1610.     this._graphElement.appendChild(this._barAreaElement);
  1611.  
  1612.     this._barLeftElement = document.createElement("div");
  1613.     this._barLeftElement.className = "resources-graph-bar waiting";
  1614.     this._barAreaElement.appendChild(this._barLeftElement);
  1615.  
  1616.     this._barRightElement = document.createElement("div");
  1617.     this._barRightElement.className = "resources-graph-bar";
  1618.     this._barAreaElement.appendChild(this._barRightElement);
  1619.  
  1620.     this._labelLeftElement = document.createElement("div");
  1621.     this._labelLeftElement.className = "resources-graph-label waiting";
  1622.     this._barAreaElement.appendChild(this._labelLeftElement);
  1623.  
  1624.     this._labelRightElement = document.createElement("div");
  1625.     this._labelRightElement.className = "resources-graph-label";
  1626.     this._barAreaElement.appendChild(this._labelRightElement);
  1627.  
  1628.     this._graphElement.addStyleClass("resources-category-" + resource.category.name);
  1629. }
  1630.  
  1631. WebInspector.ResourceGraph.prototype = {
  1632.     get graphElement()
  1633.     {
  1634.         return this._graphElement;
  1635.     },
  1636.  
  1637.     refreshLabelPositions: function()
  1638.     {
  1639.         this._labelLeftElement.style.removeProperty("left");
  1640.         this._labelLeftElement.style.removeProperty("right");
  1641.         this._labelLeftElement.removeStyleClass("before");
  1642.         this._labelLeftElement.removeStyleClass("hidden");
  1643.  
  1644.         this._labelRightElement.style.removeProperty("left");
  1645.         this._labelRightElement.style.removeProperty("right");
  1646.         this._labelRightElement.removeStyleClass("after");
  1647.         this._labelRightElement.removeStyleClass("hidden");
  1648.  
  1649.         const labelPadding = 10;
  1650.         const rightBarWidth = (this._barRightElement.offsetWidth - labelPadding);
  1651.         const leftBarWidth = ((this._barLeftElement.offsetWidth - this._barRightElement.offsetWidth) - labelPadding);
  1652.  
  1653.         var labelBefore = (this._labelLeftElement.offsetWidth > leftBarWidth);
  1654.         var labelAfter = (this._labelRightElement.offsetWidth > rightBarWidth);
  1655.  
  1656.         if (labelBefore) {
  1657.             if ((this._graphElement.offsetWidth * (this._percentages.start / 100)) < (this._labelLeftElement.offsetWidth + 10))
  1658.                 this._labelLeftElement.addStyleClass("hidden");
  1659.             this._labelLeftElement.style.setProperty("right", (100 - this._percentages.start) + "%");
  1660.             this._labelLeftElement.addStyleClass("before");
  1661.         } else {
  1662.             this._labelLeftElement.style.setProperty("left", this._percentages.start + "%");
  1663.             this._labelLeftElement.style.setProperty("right", (100 - this._percentages.middle) + "%");
  1664.         }
  1665.  
  1666.         if (labelAfter) {
  1667.             if ((this._graphElement.offsetWidth * ((100 - this._percentages.end) / 100)) < (this._labelRightElement.offsetWidth + 10))
  1668.                 this._labelRightElement.addStyleClass("hidden");
  1669.             this._labelRightElement.style.setProperty("left", this._percentages.end + "%");
  1670.             this._labelRightElement.addStyleClass("after");
  1671.         } else {
  1672.             this._labelRightElement.style.setProperty("left", this._percentages.middle + "%");
  1673.             this._labelRightElement.style.setProperty("right", (100 - this._percentages.end) + "%");
  1674.         }
  1675.     },
  1676.  
  1677.     refresh: function(calculator)
  1678.     {
  1679.         var percentages = calculator.computeBarGraphPercentages(this.resource);
  1680.         var labels = calculator.computeBarGraphLabels(this.resource);
  1681.  
  1682.         this._percentages = percentages;
  1683.  
  1684.         this._barAreaElement.removeStyleClass("hidden");
  1685.  
  1686.         if (!this._graphElement.hasStyleClass("resources-category-" + this.resource.category.name)) {
  1687.             this._graphElement.removeMatchingStyleClasses("resources-category-\\w+");
  1688.             this._graphElement.addStyleClass("resources-category-" + this.resource.category.name);
  1689.         }
  1690.  
  1691.         this._barLeftElement.style.setProperty("left", percentages.start + "%");
  1692.         this._barLeftElement.style.setProperty("right", (100 - percentages.end) + "%");
  1693.  
  1694.         this._barRightElement.style.setProperty("left", percentages.middle + "%");
  1695.         this._barRightElement.style.setProperty("right", (100 - percentages.end) + "%");
  1696.  
  1697.         this._labelLeftElement.textContent = labels.left;
  1698.         this._labelRightElement.textContent = labels.right;
  1699.  
  1700.         var tooltip = (labels.tooltip || "");
  1701.         this._barLeftElement.title = tooltip;
  1702.         this._labelLeftElement.title = tooltip;
  1703.         this._labelRightElement.title = tooltip;
  1704.         this._barRightElement.title = tooltip;
  1705.     }
  1706. }
  1707.