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