Home | History | Annotate | Download | only in audits
      1 /*
      2  * Copyright (C) 2011 Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 /**
     32  * @constructor
     33  * @param {!WebInspector.AuditController} auditController
     34  * @extends {WebInspector.VBox}
     35  */
     36 WebInspector.AuditLauncherView = function(auditController)
     37 {
     38     WebInspector.VBox.call(this);
     39     this.setMinimumSize(100, 25);
     40 
     41     this._auditController = auditController;
     42 
     43     this._categoryIdPrefix = "audit-category-item-";
     44     this._auditRunning = false;
     45 
     46     this.element.classList.add("audit-launcher-view");
     47     this.element.classList.add("panel-enabler-view");
     48 
     49     this._contentElement = document.createElement("div");
     50     this._contentElement.className = "audit-launcher-view-content";
     51     this.element.appendChild(this._contentElement);
     52     this._boundCategoryClickListener = this._categoryClicked.bind(this);
     53 
     54     this._resetResourceCount();
     55 
     56     this._sortedCategories = [];
     57 
     58     this._headerElement = document.createElement("h1");
     59     this._headerElement.className = "no-audits";
     60     this._headerElement.textContent = WebInspector.UIString("No audits to run");
     61     this._contentElement.appendChild(this._headerElement);
     62 
     63     WebInspector.targetManager.addModelListener(WebInspector.NetworkManager, WebInspector.NetworkManager.EventTypes.RequestStarted, this._onRequestStarted, this);
     64     WebInspector.targetManager.addModelListener(WebInspector.NetworkManager, WebInspector.NetworkManager.EventTypes.RequestFinished, this._onRequestFinished, this);
     65     WebInspector.profilingLock().addEventListener(WebInspector.Lock.Events.StateChanged, this._updateButton, this);
     66 
     67     var defaultSelectedAuditCategory = {};
     68     defaultSelectedAuditCategory[WebInspector.AuditLauncherView.AllCategoriesKey] = true;
     69     this._selectedCategoriesSetting = WebInspector.settings.createSetting("selectedAuditCategories", defaultSelectedAuditCategory);
     70 }
     71 
     72 WebInspector.AuditLauncherView.AllCategoriesKey = "__AllCategories";
     73 
     74 WebInspector.AuditLauncherView.prototype = {
     75     _resetResourceCount: function()
     76     {
     77         this._loadedResources = 0;
     78         this._totalResources = 0;
     79     },
     80 
     81     _onRequestStarted: function(event)
     82     {
     83         var request = /** @type {!WebInspector.NetworkRequest} */ (event.data);
     84         // Ignore long-living WebSockets for the sake of progress indicator, as we won't be waiting them anyway.
     85         if (request.type === WebInspector.resourceTypes.WebSocket)
     86             return;
     87         ++this._totalResources;
     88         this._updateResourceProgress();
     89     },
     90 
     91     _onRequestFinished: function(event)
     92     {
     93         var request = /** @type {!WebInspector.NetworkRequest} */ (event.data);
     94         // See resorceStarted for details.
     95         if (request.type === WebInspector.resourceTypes.WebSocket)
     96             return;
     97         ++this._loadedResources;
     98         this._updateResourceProgress();
     99     },
    100 
    101     /**
    102      * @param {!WebInspector.AuditCategory} category
    103      */
    104     addCategory: function(category)
    105     {
    106         if (!this._sortedCategories.length)
    107             this._createLauncherUI();
    108 
    109         var selectedCategories = this._selectedCategoriesSetting.get();
    110         var categoryElement = this._createCategoryElement(category.displayName, category.id);
    111         category._checkboxElement = categoryElement.firstChild;
    112         if (this._selectAllCheckboxElement.checked || selectedCategories[category.displayName]) {
    113             category._checkboxElement.checked = true;
    114             ++this._currentCategoriesCount;
    115         }
    116 
    117         /**
    118          * @param {!WebInspector.AuditCategory} a
    119          * @param {!WebInspector.AuditCategory} b
    120          * @return {number}
    121          */
    122         function compareCategories(a, b)
    123         {
    124             var aTitle = a.displayName || "";
    125             var bTitle = b.displayName || "";
    126             return aTitle.localeCompare(bTitle);
    127         }
    128         var insertBefore = insertionIndexForObjectInListSortedByFunction(category, this._sortedCategories, compareCategories);
    129         this._categoriesElement.insertBefore(categoryElement, this._categoriesElement.children[insertBefore]);
    130         this._sortedCategories.splice(insertBefore, 0, category);
    131         this._selectedCategoriesUpdated();
    132     },
    133 
    134     /**
    135      * @param {boolean} auditRunning
    136      */
    137     _setAuditRunning: function(auditRunning)
    138     {
    139         if (this._auditRunning === auditRunning)
    140             return;
    141         this._auditRunning = auditRunning;
    142         this._updateButton();
    143         this._toggleUIComponents(this._auditRunning);
    144         if (this._auditRunning) {
    145             WebInspector.profilingLock().acquire();
    146             this._startAudit();
    147         } else {
    148             this._stopAudit();
    149             WebInspector.profilingLock().release();
    150         }
    151     },
    152 
    153     _startAudit: function()
    154     {
    155         var catIds = [];
    156         for (var category = 0; category < this._sortedCategories.length; ++category) {
    157             if (this._sortedCategories[category]._checkboxElement.checked)
    158                 catIds.push(this._sortedCategories[category].id);
    159         }
    160 
    161         this._resetResourceCount();
    162         this._progressIndicator = new WebInspector.ProgressIndicator();
    163         this._buttonContainerElement.appendChild(this._progressIndicator.element);
    164         this._displayResourceLoadingProgress = true;
    165 
    166         /**
    167          * @this {WebInspector.AuditLauncherView}
    168          */
    169         function onAuditStarted()
    170         {
    171             this._displayResourceLoadingProgress = false;
    172         }
    173         this._auditController.initiateAudit(catIds, this._progressIndicator, this._auditPresentStateElement.checked, onAuditStarted.bind(this), this._setAuditRunning.bind(this, false));
    174     },
    175 
    176     _stopAudit: function()
    177     {
    178         this._displayResourceLoadingProgress = false;
    179         this._progressIndicator.cancel();
    180         this._progressIndicator.done();
    181         delete this._progressIndicator;
    182     },
    183 
    184     /**
    185      * @param {boolean} disable
    186      */
    187     _toggleUIComponents: function(disable)
    188     {
    189         this._selectAllCheckboxElement.disabled = disable;
    190         this._categoriesElement.disabled = disable;
    191         this._auditPresentStateElement.disabled = disable;
    192         this._auditReloadedStateElement.disabled = disable;
    193     },
    194 
    195     _launchButtonClicked: function(event)
    196     {
    197         this._setAuditRunning(!this._auditRunning);
    198     },
    199 
    200     _clearButtonClicked: function()
    201     {
    202         this._auditController.clearResults();
    203     },
    204 
    205     /**
    206      * @param {boolean} checkCategories
    207      * @param {boolean=} userGesture
    208      */
    209     _selectAllClicked: function(checkCategories, userGesture)
    210     {
    211         var childNodes = this._categoriesElement.childNodes;
    212         for (var i = 0, length = childNodes.length; i < length; ++i)
    213             childNodes[i].firstChild.checked = checkCategories;
    214         this._currentCategoriesCount = checkCategories ? this._sortedCategories.length : 0;
    215         this._selectedCategoriesUpdated(userGesture);
    216     },
    217 
    218     _categoryClicked: function(event)
    219     {
    220         this._currentCategoriesCount += event.target.checked ? 1 : -1;
    221         this._selectAllCheckboxElement.checked = this._currentCategoriesCount === this._sortedCategories.length;
    222         this._selectedCategoriesUpdated(true);
    223     },
    224 
    225     /**
    226      * @param {string} title
    227      * @param {string} id
    228      */
    229     _createCategoryElement: function(title, id)
    230     {
    231         var labelElement = document.createElement("label");
    232         labelElement.id = this._categoryIdPrefix + id;
    233 
    234         var element = document.createElement("input");
    235         element.type = "checkbox";
    236         if (id !== "")
    237             element.addEventListener("click", this._boundCategoryClickListener, false);
    238         labelElement.appendChild(element);
    239         labelElement.createTextChild(title);
    240         labelElement.__displayName = title;
    241 
    242         return labelElement;
    243     },
    244 
    245     _createLauncherUI: function()
    246     {
    247         this._headerElement = document.createElement("h1");
    248         this._headerElement.textContent = WebInspector.UIString("Select audits to run");
    249 
    250         for (var child = 0; child < this._contentElement.children.length; ++child)
    251             this._contentElement.removeChild(this._contentElement.children[child]);
    252 
    253         this._contentElement.appendChild(this._headerElement);
    254 
    255         /**
    256          * @param {!Event} event
    257          * @this {WebInspector.AuditLauncherView}
    258          */
    259         function handleSelectAllClick(event)
    260         {
    261             this._selectAllClicked(event.target.checked, true);
    262         }
    263         var categoryElement = this._createCategoryElement(WebInspector.UIString("Select All"), "");
    264         categoryElement.id = "audit-launcher-selectall";
    265         this._selectAllCheckboxElement = categoryElement.firstChild;
    266         this._selectAllCheckboxElement.checked = this._selectedCategoriesSetting.get()[WebInspector.AuditLauncherView.AllCategoriesKey];
    267         this._selectAllCheckboxElement.addEventListener("click", handleSelectAllClick.bind(this), false);
    268         this._contentElement.appendChild(categoryElement);
    269 
    270         this._categoriesElement = this._contentElement.createChild("fieldset", "audit-categories-container");
    271         this._currentCategoriesCount = 0;
    272 
    273         this._contentElement.createChild("div", "flexible-space");
    274 
    275         this._buttonContainerElement = this._contentElement.createChild("div", "button-container");
    276 
    277         var labelElement = this._buttonContainerElement.createChild("label");
    278         this._auditPresentStateElement = labelElement.createChild("input");
    279         this._auditPresentStateElement.name = "audit-mode";
    280         this._auditPresentStateElement.type = "radio";
    281         this._auditPresentStateElement.checked = true;
    282         this._auditPresentStateLabelElement = document.createTextNode(WebInspector.UIString("Audit Present State"));
    283         labelElement.appendChild(this._auditPresentStateLabelElement);
    284 
    285         labelElement = this._buttonContainerElement.createChild("label");
    286         this._auditReloadedStateElement = labelElement.createChild("input");
    287         this._auditReloadedStateElement.name = "audit-mode";
    288         this._auditReloadedStateElement.type = "radio";
    289         labelElement.createTextChild("Reload Page and Audit on Load");
    290 
    291         this._launchButton = this._buttonContainerElement.createChild("button", "text-button");
    292         this._launchButton.textContent = WebInspector.UIString("Run");
    293         this._launchButton.addEventListener("click", this._launchButtonClicked.bind(this), false);
    294 
    295         this._clearButton = this._buttonContainerElement.createChild("button", "text-button");
    296         this._clearButton.textContent = WebInspector.UIString("Clear");
    297         this._clearButton.addEventListener("click", this._clearButtonClicked.bind(this), false);
    298 
    299         this._selectAllClicked(this._selectAllCheckboxElement.checked);
    300     },
    301 
    302     _updateResourceProgress: function()
    303     {
    304         if (this._displayResourceLoadingProgress)
    305             this._progressIndicator.setTitle(WebInspector.UIString("Loading (%d of %d)", this._loadedResources, this._totalResources));
    306     },
    307 
    308     /**
    309      * @param {boolean=} userGesture
    310      */
    311     _selectedCategoriesUpdated: function(userGesture)
    312     {
    313         // Save present categories only upon user gesture to clean up junk from past versions and removed extensions.
    314         // Do not remove old categories if not handling a user gesture, as there's chance categories will be added
    315         // later during start-up.
    316         var selectedCategories = userGesture ? {} : this._selectedCategoriesSetting.get();
    317         var childNodes = this._categoriesElement.childNodes;
    318         for (var i = 0, length = childNodes.length; i < length; ++i)
    319             selectedCategories[childNodes[i].__displayName] = childNodes[i].firstChild.checked;
    320         selectedCategories[WebInspector.AuditLauncherView.AllCategoriesKey] = this._selectAllCheckboxElement.checked;
    321         this._selectedCategoriesSetting.set(selectedCategories);
    322         this._updateButton();
    323     },
    324 
    325     _updateButton: function()
    326     {
    327         var enable = this._auditRunning || (this._currentCategoriesCount && !WebInspector.profilingLock().isAcquired());
    328         this._launchButton.textContent = this._auditRunning ? WebInspector.UIString("Stop") : WebInspector.UIString("Run");
    329         this._launchButton.disabled = !enable;
    330         this._launchButton.title = enable ? "" : WebInspector.anotherProfilerActiveLabel();
    331     },
    332 
    333     __proto__: WebInspector.VBox.prototype
    334 }
    335