Home | History | Annotate | Download | only in extensions
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 cr.define('extensions', function() {
      6   'use strict';
      7 
      8   /**
      9    * Construct an ExtensionLoadError around the given |div|.
     10    * @param {HTMLDivElement} div The HTML div for the extension load error.
     11    * @constructor
     12    * @extends {HTMLDivElement}
     13    */
     14   function ExtensionLoadError(div) {
     15     div.__proto__ = ExtensionLoadError.prototype;
     16     div.init();
     17     return div;
     18   }
     19 
     20   /**
     21    * Construct a Failure.
     22    * @param {string} filePath The path to the unpacked extension.
     23    * @param {string} error The reason the extension failed to load.
     24    * @param {ExtensionHighlight} manifest Three 'highlight' strings in
     25    *     |manifest| represent three portions of the file's content to display -
     26    *     the portion which is most relevant and should be emphasized
     27    *     (highlight), and the parts both before and after this portion. These
     28    *     may be empty.
     29    * @param {HTMLLIElement} listElement The HTML element used for displaying the
     30    *     failure path for the additional failures UI.
     31    * @constructor
     32    * @extends {HTMLDivElement}
     33    */
     34   function Failure(filePath, error, manifest, listElement) {
     35     this.path = filePath;
     36     this.error = error;
     37     this.manifest = manifest;
     38     this.listElement = listElement;
     39   }
     40 
     41   ExtensionLoadError.prototype = {
     42     __proto__: HTMLDivElement.prototype,
     43 
     44     /**
     45      * Initialize the ExtensionLoadError div.
     46      */
     47     init: function() {
     48       /**
     49        * The element which displays the path of the extension.
     50        * @type {HTMLElement}
     51        * @private
     52        */
     53       this.path_ = /** @type {HTMLElement} */(
     54           this.querySelector('#extension-load-error-path'));
     55 
     56       /**
     57        * The element which displays the reason the extension failed to load.
     58        * @type {HTMLElement}
     59        * @private
     60        */
     61       this.reason_ = /** @type {HTMLElement} */(
     62           this.querySelector('#extension-load-error-reason'));
     63 
     64       /**
     65        * The element which displays the manifest code.
     66        * @type {extensions.ExtensionCode}
     67        * @private
     68        */
     69       this.manifest_ = new extensions.ExtensionCode(
     70           this.querySelector('#extension-load-error-manifest'));
     71 
     72       /**
     73        * The element which displays information about additional errors.
     74        * @type {HTMLElement}
     75        * @private
     76        */
     77       this.additional_ = /** @type {HTMLUListElement} */(
     78           this.querySelector('#extension-load-error-additional'));
     79       this.additional_.list = this.additional_.getElementsByTagName('ul')[0];
     80 
     81       /**
     82        * An array of Failures for keeping track of multiple active failures.
     83        * @type {Array.<Failure>}
     84        * @private
     85        */
     86       this.failures_ = [];
     87 
     88       this.querySelector('#extension-load-error-retry-button').addEventListener(
     89           'click', function(e) {
     90         chrome.send('extensionLoaderRetry');
     91         this.remove_();
     92       }.bind(this));
     93 
     94       this.querySelector('#extension-load-error-give-up-button').
     95           addEventListener('click', function(e) {
     96         chrome.send('extensionLoaderIgnoreFailure');
     97         this.remove_();
     98       }.bind(this));
     99 
    100       chrome.send('extensionLoaderDisplayFailures');
    101     },
    102 
    103     /**
    104      * Add a failure to failures_ array. If there is already a displayed
    105      * failure, display the additional failures element.
    106      * @param {Array.<Object>} failures Array of failures containing paths,
    107      *     errors, and manifests.
    108      * @private
    109      */
    110     add_: function(failures) {
    111       // If a failure is already being displayed, unhide the last item.
    112       if (this.failures_.length > 0)
    113         this.failures_[this.failures_.length - 1].listElement.hidden = false;
    114       failures.forEach(function(failure) {
    115         var listItem = /** @type {HTMLLIElement} */(
    116             document.createElement('li'));
    117         listItem.textContent = failure.path;
    118         this.additional_.list.appendChild(listItem);
    119         this.failures_.push(new Failure(failure.path,
    120                                         failure.error,
    121                                         failure.manifest,
    122                                         listItem));
    123       }.bind(this));
    124       // Hide the last item because the UI is displaying its information.
    125       this.failures_[this.failures_.length - 1].listElement.hidden = true;
    126       this.show_();
    127     },
    128 
    129     /**
    130      * Remove a failure from |failures_| array. If this was the last failure,
    131      * hide the error UI.  If this was the last additional failure, hide
    132      * the additional failures UI.
    133      * @private
    134      */
    135     remove_: function() {
    136       this.additional_.list.removeChild(
    137           this.failures_[this.failures_.length - 1].listElement);
    138       this.failures_.pop();
    139       if (this.failures_.length > 0) {
    140         this.failures_[this.failures_.length - 1].listElement.hidden = true;
    141         this.show_();
    142       } else {
    143         this.hidden = true;
    144       }
    145     },
    146 
    147     /**
    148      * Display the load error to the user. The last failure gets its manifest
    149      * and error displayed, while additional failures have their path names
    150      * displayed in the additional failures element.
    151      * @private
    152      */
    153     show_: function() {
    154       assert(this.failures_.length >= 1);
    155       var failure = this.failures_[this.failures_.length - 1];
    156       this.path_.textContent = failure.path;
    157       this.reason_.textContent = failure.error;
    158 
    159       failure.manifest.message = failure.error;
    160       this.manifest_.populate(
    161           failure.manifest,
    162           loadTimeData.getString('extensionLoadCouldNotLoadManifest'));
    163       this.hidden = false;
    164       this.manifest_.scrollToError();
    165 
    166       this.additional_.hidden = this.failures_.length == 1;
    167     }
    168   };
    169 
    170   /**
    171    * The ExtensionLoader is the class in charge of loading unpacked extensions.
    172    * @constructor
    173    */
    174   function ExtensionLoader() {
    175     /**
    176      * The ExtensionLoadError to show any errors from loading an unpacked
    177      * extension.
    178      * @type {ExtensionLoadError}
    179      * @private
    180      */
    181     this.loadError_ = new ExtensionLoadError(
    182         /** @type {HTMLDivElement} */($('extension-load-error')));
    183   }
    184 
    185   cr.addSingletonGetter(ExtensionLoader);
    186 
    187   ExtensionLoader.prototype = {
    188     /**
    189      * Begin the sequence of loading an unpacked extension. If an error is
    190      * encountered, this object will get notified via notifyFailed().
    191      */
    192     loadUnpacked: function() {
    193       chrome.send('extensionLoaderLoadUnpacked');
    194     },
    195 
    196     /**
    197      * Notify the ExtensionLoader that loading an unpacked extension failed.
    198      * Add the failure to failures_ and show the ExtensionLoadError.
    199      * @param {Array.<Object>} failures Array of failures containing paths,
    200      *     errors, and manifests.
    201      */
    202     notifyFailed: function(failures) {
    203       this.loadError_.add_(failures);
    204     },
    205   };
    206 
    207   /**
    208    * A static forwarding function for ExtensionLoader.notifyFailed.
    209    * @param {Array.<Object>} failures Array of failures containing paths,
    210    *     errors, and manifests.
    211    * @see ExtensionLoader.notifyFailed
    212    */
    213   ExtensionLoader.notifyLoadFailed = function(failures) {
    214     ExtensionLoader.getInstance().notifyFailed(failures);
    215   };
    216 
    217   return {
    218     ExtensionLoader: ExtensionLoader
    219   };
    220 });
    221