// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. cr.define('extensions', function() { 'use strict'; /** * Construct an ExtensionLoadError around the given |div|. * @param {HTMLDivElement} div The HTML div for the extension load error. * @constructor * @extends {HTMLDivElement} */ function ExtensionLoadError(div) { div.__proto__ = ExtensionLoadError.prototype; div.init(); return div; } /** * Construct a Failure. * @param {string} filePath The path to the unpacked extension. * @param {string} error The reason the extension failed to load. * @param {ExtensionHighlight} manifest Three 'highlight' strings in * |manifest| represent three portions of the file's content to display - * the portion which is most relevant and should be emphasized * (highlight), and the parts both before and after this portion. These * may be empty. * @param {HTMLLIElement} listElement The HTML element used for displaying the * failure path for the additional failures UI. * @constructor * @extends {HTMLDivElement} */ function Failure(filePath, error, manifest, listElement) { this.path = filePath; this.error = error; this.manifest = manifest; this.listElement = listElement; } ExtensionLoadError.prototype = { __proto__: HTMLDivElement.prototype, /** * Initialize the ExtensionLoadError div. */ init: function() { /** * The element which displays the path of the extension. * @type {HTMLElement} * @private */ this.path_ = /** @type {HTMLElement} */( this.querySelector('#extension-load-error-path')); /** * The element which displays the reason the extension failed to load. * @type {HTMLElement} * @private */ this.reason_ = /** @type {HTMLElement} */( this.querySelector('#extension-load-error-reason')); /** * The element which displays the manifest code. * @type {extensions.ExtensionCode} * @private */ this.manifest_ = new extensions.ExtensionCode( this.querySelector('#extension-load-error-manifest')); /** * The element which displays information about additional errors. * @type {HTMLElement} * @private */ this.additional_ = /** @type {HTMLUListElement} */( this.querySelector('#extension-load-error-additional')); this.additional_.list = this.additional_.getElementsByTagName('ul')[0]; /** * An array of Failures for keeping track of multiple active failures. * @type {Array} * @private */ this.failures_ = []; this.querySelector('#extension-load-error-retry-button').addEventListener( 'click', function(e) { chrome.send('extensionLoaderRetry'); this.remove_(); }.bind(this)); this.querySelector('#extension-load-error-give-up-button'). addEventListener('click', function(e) { chrome.send('extensionLoaderIgnoreFailure'); this.remove_(); }.bind(this)); chrome.send('extensionLoaderDisplayFailures'); }, /** * Add a failure to failures_ array. If there is already a displayed * failure, display the additional failures element. * @param {Array} failures Array of failures containing paths, * errors, and manifests. * @private */ add_: function(failures) { // If a failure is already being displayed, unhide the last item. if (this.failures_.length > 0) this.failures_[this.failures_.length - 1].listElement.hidden = false; failures.forEach(function(failure) { var listItem = /** @type {HTMLLIElement} */( document.createElement('li')); listItem.textContent = failure.path; this.additional_.list.appendChild(listItem); this.failures_.push(new Failure(failure.path, failure.error, failure.manifest, listItem)); }.bind(this)); // Hide the last item because the UI is displaying its information. this.failures_[this.failures_.length - 1].listElement.hidden = true; this.show_(); }, /** * Remove a failure from |failures_| array. If this was the last failure, * hide the error UI. If this was the last additional failure, hide * the additional failures UI. * @private */ remove_: function() { this.additional_.list.removeChild( this.failures_[this.failures_.length - 1].listElement); this.failures_.pop(); if (this.failures_.length > 0) { this.failures_[this.failures_.length - 1].listElement.hidden = true; this.show_(); } else { this.hidden = true; } }, /** * Display the load error to the user. The last failure gets its manifest * and error displayed, while additional failures have their path names * displayed in the additional failures element. * @private */ show_: function() { assert(this.failures_.length >= 1); var failure = this.failures_[this.failures_.length - 1]; this.path_.textContent = failure.path; this.reason_.textContent = failure.error; failure.manifest.message = failure.error; this.manifest_.populate( failure.manifest, loadTimeData.getString('extensionLoadCouldNotLoadManifest')); this.hidden = false; this.manifest_.scrollToError(); this.additional_.hidden = this.failures_.length == 1; } }; /** * The ExtensionLoader is the class in charge of loading unpacked extensions. * @constructor */ function ExtensionLoader() { /** * The ExtensionLoadError to show any errors from loading an unpacked * extension. * @type {ExtensionLoadError} * @private */ this.loadError_ = new ExtensionLoadError( /** @type {HTMLDivElement} */($('extension-load-error'))); } cr.addSingletonGetter(ExtensionLoader); ExtensionLoader.prototype = { /** * Whether or not we are currently loading an unpacked extension. * @private {boolean} */ isLoading_: false, /** * Begin the sequence of loading an unpacked extension. If an error is * encountered, this object will get notified via notifyFailed(). */ loadUnpacked: function() { if (this.isLoading_) // Only one running load at a time. return; this.isLoading_ = true; chrome.developerPrivate.loadUnpacked({failQuietly: true}, function() { // Check lastError to avoid the log, but don't do anything with it - // error-handling is done on the C++ side. var lastError = chrome.runtime.lastError; this.isLoading_ = false; }.bind(this)); }, /** * Notify the ExtensionLoader that loading an unpacked extension failed. * Add the failure to failures_ and show the ExtensionLoadError. * @param {Array} failures Array of failures containing paths, * errors, and manifests. */ notifyFailed: function(failures) { this.loadError_.add_(failures); }, }; /** * A static forwarding function for ExtensionLoader.notifyFailed. * @param {Array} failures Array of failures containing paths, * errors, and manifests. * @see ExtensionLoader.notifyFailed */ ExtensionLoader.notifyLoadFailed = function(failures) { ExtensionLoader.getInstance().notifyFailed(failures); }; return { ExtensionLoader: ExtensionLoader }; });