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.classList.add("audit-launcher-view");
     46     this.element.classList.add("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         /**
    162          * @this {WebInspector.AuditLauncherView}
    163          */
    164         function onAuditStarted()
    165         {
    166             this._displayResourceLoadingProgress = false;
    167         }
    168         this._auditController.initiateAudit(catIds, this._progressIndicator, this._auditPresentStateElement.checked, onAuditStarted.bind(this), this._setAuditRunning.bind(this, false));
    169     },
    170 
    171     _stopAudit: function()
    172     {
    173         this._displayResourceLoadingProgress = false;
    174         this._progressIndicator.cancel();
    175         this._progressIndicator.done();
    176         delete this._progressIndicator;
    177     },
    178 
    179     /**
    180      * @param {boolean} disable
    181      */
    182     _toggleUIComponents: function(disable)
    183     {
    184         this._selectAllCheckboxElement.disabled = disable;
    185         this._categoriesElement.disabled = disable;
    186         this._auditPresentStateElement.disabled = disable;
    187         this._auditReloadedStateElement.disabled = disable;
    188     },
    189 
    190     _launchButtonClicked: function(event)
    191     {
    192         this._setAuditRunning(!this._auditRunning);
    193     },
    194 
    195     _clearButtonClicked: function()
    196     {
    197         this._auditController.clearResults();
    198     },
    199 
    200     /**
    201      * @param {boolean} checkCategories
    202      * @param {boolean=} userGesture
    203      */
    204     _selectAllClicked: function(checkCategories, userGesture)
    205     {
    206         var childNodes = this._categoriesElement.childNodes;
    207         for (var i = 0, length = childNodes.length; i < length; ++i)
    208             childNodes[i].firstChild.checked = checkCategories;
    209         this._currentCategoriesCount = checkCategories ? this._sortedCategories.length : 0;
    210         this._selectedCategoriesUpdated(userGesture);
    211     },
    212 
    213     _categoryClicked: function(event)
    214     {
    215         this._currentCategoriesCount += event.target.checked ? 1 : -1;
    216         this._selectAllCheckboxElement.checked = this._currentCategoriesCount === this._sortedCategories.length;
    217         this._selectedCategoriesUpdated(true);
    218     },
    219 
    220     /**
    221      * @param {string} title
    222      * @param {string} id
    223      */
    224     _createCategoryElement: function(title, id)
    225     {
    226         var labelElement = document.createElement("label");
    227         labelElement.id = this._categoryIdPrefix + id;
    228 
    229         var element = document.createElement("input");
    230         element.type = "checkbox";
    231         if (id !== "")
    232             element.addEventListener("click", this._boundCategoryClickListener, false);
    233         labelElement.appendChild(element);
    234         labelElement.appendChild(document.createTextNode(title));
    235         labelElement.__displayName = title;
    236 
    237         return labelElement;
    238     },
    239 
    240     _createLauncherUI: function()
    241     {
    242         this._headerElement = document.createElement("h1");
    243         this._headerElement.textContent = WebInspector.UIString("Select audits to run");
    244 
    245         for (var child = 0; child < this._contentElement.children.length; ++child)
    246             this._contentElement.removeChild(this._contentElement.children[child]);
    247 
    248         this._contentElement.appendChild(this._headerElement);
    249 
    250         /**
    251          * @param {?Event} event
    252          * @this {WebInspector.AuditLauncherView}
    253          */
    254         function handleSelectAllClick(event)
    255         {
    256             this._selectAllClicked(event.target.checked, true);
    257         }
    258         var categoryElement = this._createCategoryElement(WebInspector.UIString("Select All"), "");
    259         categoryElement.id = "audit-launcher-selectall";
    260         this._selectAllCheckboxElement = categoryElement.firstChild;
    261         this._selectAllCheckboxElement.checked = this._selectedCategoriesSetting.get()[WebInspector.AuditLauncherView.AllCategoriesKey];
    262         this._selectAllCheckboxElement.addEventListener("click", handleSelectAllClick.bind(this), false);
    263         this._contentElement.appendChild(categoryElement);
    264 
    265         this._categoriesElement = this._contentElement.createChild("fieldset", "audit-categories-container");
    266         this._currentCategoriesCount = 0;
    267 
    268         this._contentElement.createChild("div", "flexible-space");
    269 
    270         this._buttonContainerElement = this._contentElement.createChild("div", "button-container");
    271 
    272         var labelElement = this._buttonContainerElement.createChild("label");
    273         this._auditPresentStateElement = labelElement.createChild("input");
    274         this._auditPresentStateElement.name = "audit-mode";
    275         this._auditPresentStateElement.type = "radio";
    276         this._auditPresentStateElement.checked = true;
    277         this._auditPresentStateLabelElement = document.createTextNode(WebInspector.UIString("Audit Present State"));
    278         labelElement.appendChild(this._auditPresentStateLabelElement);
    279 
    280         labelElement = this._buttonContainerElement.createChild("label");
    281         this._auditReloadedStateElement = labelElement.createChild("input");
    282         this._auditReloadedStateElement.name = "audit-mode";
    283         this._auditReloadedStateElement.type = "radio";
    284         labelElement.appendChild(document.createTextNode("Reload Page and Audit on Load"));
    285 
    286         this._launchButton = this._buttonContainerElement.createChild("button");
    287         this._launchButton.textContent = WebInspector.UIString("Run");
    288         this._launchButton.addEventListener("click", this._launchButtonClicked.bind(this), false);
    289 
    290         this._clearButton = this._buttonContainerElement.createChild("button");
    291         this._clearButton.textContent = WebInspector.UIString("Clear");
    292         this._clearButton.addEventListener("click", this._clearButtonClicked.bind(this), false);
    293 
    294         this._selectAllClicked(this._selectAllCheckboxElement.checked);
    295     },
    296 
    297     _updateResourceProgress: function()
    298     {
    299         if (this._displayResourceLoadingProgress)
    300             this._progressIndicator.setTitle(WebInspector.UIString("Loading (%d of %d)", this._loadedResources, this._totalResources));
    301     },
    302 
    303     /**
    304      * @param {boolean=} userGesture
    305      */
    306     _selectedCategoriesUpdated: function(userGesture)
    307     {
    308         // Save present categories only upon user gesture to clean up junk from past versions and removed extensions.
    309         // Do not remove old categories if not handling a user gesture, as there's chance categories will be added
    310         // later during start-up.
    311         var selectedCategories = userGesture ? {} : this._selectedCategoriesSetting.get();
    312         var childNodes = this._categoriesElement.childNodes;
    313         for (var i = 0, length = childNodes.length; i < length; ++i)
    314             selectedCategories[childNodes[i].__displayName] = childNodes[i].firstChild.checked;
    315         selectedCategories[WebInspector.AuditLauncherView.AllCategoriesKey] = this._selectAllCheckboxElement.checked;
    316         this._selectedCategoriesSetting.set(selectedCategories);
    317         this._updateButton();
    318     },
    319 
    320     _updateButton: function()
    321     {
    322         this._launchButton.textContent = this._auditRunning ? WebInspector.UIString("Stop") : WebInspector.UIString("Run");
    323         this._launchButton.disabled = !this._currentCategoriesCount;
    324     },
    325 
    326     __proto__: WebInspector.View.prototype
    327 }
    328