Add new certificateProvider extension API.
[chromium-blink-merge.git] / chrome / browser / resources / extensions / extension_loader.js
blob95def83d8bccb227975f4d68eb0deae6ada1a824
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.
5 cr.define('extensions', function() {
6   'use strict';
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   }
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   }
41   ExtensionLoadError.prototype = {
42     __proto__: HTMLDivElement.prototype,
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'));
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'));
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'));
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];
81       /**
82        * An array of Failures for keeping track of multiple active failures.
83        * @type {Array<Failure>}
84        * @private
85        */
86       this.failures_ = [];
88       this.querySelector('#extension-load-error-retry-button').addEventListener(
89           'click', function(e) {
90         chrome.send('extensionLoaderRetry');
91         this.remove_();
92       }.bind(this));
94       this.querySelector('#extension-load-error-give-up-button').
95           addEventListener('click', function(e) {
96         chrome.send('extensionLoaderIgnoreFailure');
97         this.remove_();
98       }.bind(this));
100       chrome.send('extensionLoaderDisplayFailures');
101     },
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     },
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     },
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;
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();
166       this.additional_.hidden = this.failures_.length == 1;
167     }
168   };
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   }
185   cr.addSingletonGetter(ExtensionLoader);
187   ExtensionLoader.prototype = {
188     /**
189      * Whether or not we are currently loading an unpacked extension.
190      * @private {boolean}
191      */
192     isLoading_: false,
194     /**
195      * Begin the sequence of loading an unpacked extension. If an error is
196      * encountered, this object will get notified via notifyFailed().
197      */
198     loadUnpacked: function() {
199       if (this.isLoading_)  // Only one running load at a time.
200         return;
201       this.isLoading_ = true;
202       chrome.developerPrivate.loadUnpacked({failQuietly: true}, function() {
203         // Check lastError to avoid the log, but don't do anything with it -
204         // error-handling is done on the C++ side.
205         var lastError = chrome.runtime.lastError;
206         this.isLoading_ = false;
207       }.bind(this));
208     },
210     /**
211      * Notify the ExtensionLoader that loading an unpacked extension failed.
212      * Add the failure to failures_ and show the ExtensionLoadError.
213      * @param {Array<Object>} failures Array of failures containing paths,
214      *     errors, and manifests.
215      */
216     notifyFailed: function(failures) {
217       this.loadError_.add_(failures);
218     },
219   };
221   /**
222    * A static forwarding function for ExtensionLoader.notifyFailed.
223    * @param {Array<Object>} failures Array of failures containing paths,
224    *     errors, and manifests.
225    * @see ExtensionLoader.notifyFailed
226    */
227   ExtensionLoader.notifyLoadFailed = function(failures) {
228     ExtensionLoader.getInstance().notifyFailed(failures);
229   };
231   return {
232     ExtensionLoader: ExtensionLoader
233   };