Home | History | Annotate | Download | only in front_end
      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.View}
     35  */
     36 WebInspector.AuditLauncherView = function(auditController)
     37 {
     38     WebInspector.View.call(this);
     39 
     40     this._auditController = auditController;
     41 
     42     this._categoryIdPrefix = "audit-category-item-";
     43     this._auditRunning = false;
     44 
     45     this.element.addStyleClass("audit-launcher-view");
     46     this.element.addStyleClass("panel-enabler-view");
     47 
     48     this._contentElement = document.createElement("div");
     49     this._contentElement.className = "audit-launcher-view-content";
     50     this.element.appendChild(this._contentElement);
     51     this._boundCategoryClickListener = this._categoryClicked.bind(this);
     52 
     53     this._resetResourceCount();
     54 
     55     this._sortedCategories = [];
     56 
     57     this._headerElement = document.createElement("h1");
     58     this._headerElement.className = "no-audits";
     59     this._headerElement.textContent = WebInspector.UIString("No audits to run");
     60     this._contentElement.appendChild(this._headerElement);
     61 
     62     WebInspector.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.RequestStarted, this._onRequestStarted, this);
     63     WebInspector.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.RequestFinished, this._onRequestFinished, this);
     64 
     65     var defaultSelectedAuditCategory = {};
     66     defaultSelectedAuditCategory[WebInspector.AuditLauncherView.AllCategoriesKey] = true;
     67     this._selectedCategoriesSetting = WebInspector.settings.createSetting("selectedAuditCategories", defaultSelectedAuditCategory);
     68 }
     69 
     70 WebInspector.AuditLauncherView.AllCategoriesKey = "__AllCategories";
     71 
     72 WebInspector.AuditLauncherView.prototype = {
     73     _resetResourceCount: function()
     74     {
     75         this._loadedResources = 0;
     76         this._totalResources = 0;
     77     },
     78 
     79     _onRequestStarted: function(event)
     80     {
     81         var request = /** @type {WebInspector.NetworkRequest} */ (event.data);
     82         // Ignore long-living WebSockets for the sake of progress indicator, as we won't be waiting them anyway.
     83         if (request.type === WebInspector.resourceTypes.WebSocket)
     84             return;
     85         ++this._totalResources;
     86         this._updateResourceProgress();
     87     },
     88 
     89     _onRequestFinished: function(event)
     90     {
     91         var request = /** @type {WebInspector.NetworkRequest} */ (event.data);
     92         // See resorceStarted for details.
     93         if (request.type === WebInspector.resourceTypes.WebSocket)
     94             return;
     95         ++this._loadedResources;
     96         this._updateResourceProgress();
     97     },
     98 
     99     /**
    100      * @param {!WebInspector.AuditCategory} category
    101      */
    102     addCategory: function(category)
    103     {
    104         if (!this._sortedCategories.length)
    105             this._createLauncherUI();
    106 
    107         var selectedCategories = this._selectedCategoriesSetting.get();
    108         var categoryElement = this._createCategoryElement(category.displayName, category.id);
    109         category._checkboxElement = categoryElement.firstChild;
    110         if (this._selectAllCheckboxElement.checked || selectedCategories[category.displayName]) {
    111             category._checkboxElement.checked = true;
    112             ++this._currentCategoriesCount;
    113         }
    114 
    115         /**
    116          * @param {!WebInspector.AuditCategory} a
    117          * @param {!WebInspector.AuditCategory} b
    118          * @return {number}
    119          */
    120         function compareCategories(a, b)
    121         {
    122             var aTitle = a.displayName || "";
    123             var bTitle = b.displayName || "";
    124             return aTitle.localeCompare(bTitle);
    125         }
    126         var insertBefore = insertionIndexForObjectInListSortedByFunction(category, this._sortedCategories, compareCategories);
    127         this._categoriesElement.insertBefore(categoryElement, this._categoriesElement.children[insertBefore]);
    128         this._sortedCategories.splice(insertBefore, 0, category);
    129         this._selectedCategoriesUpdated();
    130     },
    131 
    132     /**
    133      * @param {boolean} auditRunning
    134      */
    135     _setAuditRunning: function(auditRunning)
    136     {
    137         if (this._auditRunning === auditRunning)
    138             return;
    139         this._auditRunning = auditRunning;
    140         this._updateButton();
    141         this._toggleUIComponents(this._auditRunning);
    142         if (this._auditRunning)
    143             this._startAudit();
    144         else
    145             this._stopAudit();
    146     },
    147 
    148     _startAudit: function()
    149     {
    150         var catIds = [];
    151         for (var category = 0; category < this._sortedCategories.length; ++category) {
    152             if (this._sortedCategories[category]._checkboxElement.checked)
    153                 catIds.push(this._sortedCategories[category].id);
    154         }
    155 
    156         this._resetResourceCount();
    157         this._progressIndicator = new WebInspector.ProgressIndicator();
    158         this._buttonContainerElement.appendChild(this._progressIndicator.element);
    159         this._displayResourceLoadingProgress = true;
    160 
    161         function onAuditStarted()
    162         {
    163             this._displayResourceLoadingProgress = false;
    164         }
    165         this._auditController.initiateAudit(catIds, this._progressIndicator, this._auditPresentStateElement.checked, onAuditStarted.bind(this), this._setAuditRunning.bind(this, false));
    166     },
    167 
    168     _stopAudit: function()
    169     {
    170         this._displayResourceLoadingProgress = false;
    171         this._progressIndicator.cancel();
    172         this._progressIndicator.done();
    173         delete this._progressIndicator;
    174     },
    175 
    176     /**
    177      * @param {boolean} disable
    178      */
    179     _toggleUIComponents: function(disable)
    180     {
    181         this._selectAllCheckboxElement.disabled = disable;
    182         this._categoriesElement.disabled = disable;
    183         this._auditPresentStateElement.disabled = disable;
    184         this._auditReloadedStateElement.disabled = disable;
    185     },
    186 
    187     _launchButtonClicked: function(event)
    188     {
    189         this._setAuditRunning(!this._auditRunning);
    190     },
    191 
    192     /**
    193      * @param {boolean} checkCategories
    194      * @param {boolean=} userGesture
    195      */
    196     _selectAllClicked: function(checkCategories, userGesture)
    197     {
    198         var childNodes = this._categoriesElement.childNodes;
    199         for (var i = 0, length = childNodes.length; i < length; ++i)
    200             childNodes[i].firstChild.checked = checkCategories;
    201         this._currentCategoriesCount = checkCategories ? this._sortedCategories.length : 0;
    202         this._selectedCategoriesUpdated(userGesture);
    203     },
    204 
    205     _categoryClicked: function(event)
    206     {
    207         this._currentCategoriesCount += event.target.checked ? 1 : -1;
    208         this._selectAllCheckboxElement.checked = this._currentCategoriesCount === this._sortedCategories.length;
    209         this._selectedCategoriesUpdated(true);
    210     },
    211 
    212     /**
    213      * @param {string} title
    214      * @param {string} id
    215      */
    216     _createCategoryElement: function(title, id)
    217     {
    218         var labelElement = document.createElement("label");
    219         labelElement.id = this._categoryIdPrefix + id;
    220 
    221         var element = document.createElement("input");
    222         element.type = "checkbox";
    223         if (id !== "")
    224             element.addEventListener("click", this._boundCategoryClickListener, false);
    225         labelElement.appendChild(element);
    226         labelElement.appendChild(document.createTextNode(title));
    227         labelElement.__displayName = title;
    228 
    229         return labelElement;
    230     },
    231 
    232     _createLauncherUI: function()
    233     {
    234         this._headerElement = document.createElement("h1");
    235         this._headerElement.textContent = WebInspector.UIString("Select audits to run");
    236 
    237         for (var child = 0; child < this._contentElement.children.length; ++child)
    238             this._contentElement.removeChild(this._contentElement.children[child]);
    239 
    240         this._contentElement.appendChild(this._headerElement);
    241 
    242         function handleSelectAllClick(event)
    243         {
    244             this._selectAllClicked(event.target.checked, true);
    245         }
    246         var categoryElement = this._createCategoryElement(WebInspector.UIString("Select All"), "");
    247         categoryElement.id = "audit-launcher-selectall";
    248         this._selectAllCheckboxElement = categoryElement.firstChild;
    249         this._selectAllCheckboxElement.checked = this._selectedCategoriesSetting.get()[WebInspector.AuditLauncherView.AllCategoriesKey];
    250         this._selectAllCheckboxElement.addEventListener("click", handleSelectAllClick.bind(this), false);
    251         this._contentElement.appendChild(categoryElement);
    252 
    253         this._categoriesElement = this._contentElement.createChild("fieldset", "audit-categories-container");
    254         this._currentCategoriesCount = 0;
    255 
    256         this._contentElement.createChild("div", "flexible-space");
    257 
    258         this._buttonContainerElement = this._contentElement.createChild("div", "button-container");
    259 
    260         var labelElement = this._buttonContainerElement.createChild("label");
    261         this._auditPresentStateElement = labelElement.createChild("input");
    262         this._auditPresentStateElement.name = "audit-mode";
    263         this._auditPresentStateElement.type = "radio";
    264         this._auditPresentStateElement.checked = true;
    265         this._auditPresentStateLabelElement = document.createTextNode(WebInspector.UIString("Audit Present State"));
    266         labelElement.appendChild(this._auditPresentStateLabelElement);
    267 
    268         labelElement = this._buttonContainerElement.createChild("label");
    269         this._auditReloadedStateElement = labelElement.createChild("input");
    270         this._auditReloadedStateElement.name = "audit-mode";
    271         this._auditReloadedStateElement.type = "radio";
    272         labelElement.appendChild(document.createTextNode("Reload Page and Audit on Load"));
    273 
    274         this._launchButton = this._buttonContainerElement.createChild("button");
    275         this._launchButton.textContent = WebInspector.UIString("Run");
    276         this._launchButton.addEventListener("click", this._launchButtonClicked.bind(this), false);
    277 
    278         this._selectAllClicked(this._selectAllCheckboxElement.checked);
    279     },
    280 
    281     _updateResourceProgress: function()
    282     {
    283         if (this._displayResourceLoadingProgress)
    284             this._progressIndicator.setTitle(WebInspector.UIString("Loading (%d of %d)", this._loadedResources, this._totalResources));
    285     },
    286 
    287     /**
    288      * @param {boolean=} userGesture
    289      */
    290     _selectedCategoriesUpdated: function(userGesture)
    291     {
    292         // Save present categories only upon user gesture to clean up junk from past versions and removed extensions.
    293         // Do not remove old categories if not handling a user gesture, as there's chance categories will be added
    294         // later during start-up.
    295         var selectedCategories = userGesture ? {} : this._selectedCategoriesSetting.get();
    296         var childNodes = this._categoriesElement.childNodes;
    297         for (var i = 0, length = childNodes.length; i < length; ++i)
    298             selectedCategories[childNodes[i].__displayName] = childNodes[i].firstChild.checked;
    299         selectedCategories[WebInspector.AuditLauncherView.AllCategoriesKey] = this._selectAllCheckboxElement.checked;
    300         this._selectedCategoriesSetting.set(selectedCategories);
    301         this._updateButton();
    302     },
    303 
    304     _updateButton: function()
    305     {
    306         this._launchButton.textContent = this._auditRunning ? WebInspector.UIString("Stop") : WebInspector.UIString("Run");
    307         this._launchButton.disabled = !this._currentCategoriesCount;
    308     },
    309 
    310     __proto__: WebInspector.View.prototype
    311 }
    312