Index: ui/file_manager/file_manager/foreground/js/cws_widget_container.js |
diff --git a/ui/file_manager/file_manager/foreground/js/cws_widget_container.js b/ui/file_manager/file_manager/foreground/js/cws_widget_container.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..af5307173286fd82539cd53b9db74f81e151b856 |
--- /dev/null |
+++ b/ui/file_manager/file_manager/foreground/js/cws_widget_container.js |
@@ -0,0 +1,691 @@ |
+// Copyright 2015 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. |
+ |
+/** |
+ * CWSWidgetContainer contains a Chrome Web Store widget that displays list of |
+ * apps that satisfy certain constraints (e.g. fileHandler apps that can handle |
+ * files with specific file extension or MIME type) and enables the user to |
+ * install apps directly from it. |
+ * CWSWidgetContainer implements client side of the widget, which handles |
+ * widget loading and app installation. |
+ */ |
+ |
+/** |
+ * The width of the widget (in pixels) |
+ * @type {number} |
+ * @const |
+ */ |
+var WEBVIEW_WIDTH = 735; |
+ |
+/** |
+ * The height of the widget (in pixels). |
+ * @type {number} |
+ * @const |
+ */ |
+var WEBVIEW_HEIGHT = 480; |
+ |
+/** |
+ * The URL of the widget showing suggested apps. |
+ * @type {string} |
+ * @const |
+ */ |
+var CWS_WIDGET_URL = |
+ 'https://clients5.google.com/webstore/wall/cros-widget-container'; |
+ |
+/** |
+ * The origin of the widget. |
+ * @type {string} |
+ * @const |
+ */ |
+var CWS_WIDGET_ORIGIN = 'https://clients5.google.com'; |
+ |
+/** |
+ * Creates the widget container element in DOM tree. |
+ * |
+ * @param {!HTMLDocument} document The document to contain this container. |
+ * @param {!HTMLElement} parentNode Node to be parent for this container. |
+ * @param {!SuggestAppDialogState} state Static state of suggest app dialog. |
+ * @constructor |
+ */ |
+function CWSWidgetContainer(document, parentNode, state) { |
+ /** |
+ * The document that will contain the container. |
+ * @const {!HTMLDocument} |
+ * @private |
+ */ |
+ this.document_ = document; |
+ |
+ /** |
+ * The element containing the widget webview. |
+ * @type {!Element} |
+ * @private |
+ */ |
+ this.webviewContainer_ = document.createElement('div'); |
+ this.webviewContainer_.id = 'webview-container'; |
+ this.webviewContainer_.style.width = WEBVIEW_WIDTH + 'px'; |
+ this.webviewContainer_.style.height = WEBVIEW_HEIGHT + 'px'; |
+ parentNode.appendChild(this.webviewContainer_); |
+ |
+ /** |
+ * Element showing spinner layout in place of Web Store widget. |
+ * @type {!Element} |
+ */ |
+ var spinnerLayer = document.createElement('div'); |
+ spinnerLayer.className = 'spinner-layer'; |
+ this.webviewContainer_.appendChild(spinnerLayer); |
+ |
+ /** |
+ * The widget container's button strip. |
+ * @type {!Element} |
+ */ |
+ var buttons = document.createElement('div'); |
+ buttons.id = 'buttons'; |
+ parentNode.appendChild(buttons); |
+ |
+ /** |
+ * Button that opens the Webstore URL. |
+ * @const {!Element} |
+ * @private |
+ */ |
+ this.webstoreButton_ = document.createElement('div'); |
+ this.webstoreButton_.hidden = true; |
+ this.webstoreButton_.id = 'webstore-button'; |
+ this.webstoreButton_.setAttribute('role', 'button'); |
+ this.webstoreButton_.tabIndex = 0; |
+ |
+ /** |
+ * Icon for the Webstore button. |
+ * @type {!Element} |
+ */ |
+ var webstoreButtonIcon = this.document_.createElement('span'); |
+ webstoreButtonIcon.id = 'webstore-button-icon'; |
+ this.webstoreButton_.appendChild(webstoreButtonIcon); |
+ |
+ /** |
+ * The label for the Webstore button. |
+ * @type {!Element} |
+ */ |
+ var webstoreButtonLabel = this.document_.createElement('span'); |
+ webstoreButtonLabel.id = 'webstore-button-label'; |
+ webstoreButtonLabel.textContent = str('SUGGEST_DIALOG_LINK_TO_WEBSTORE'); |
+ this.webstoreButton_.appendChild(webstoreButtonLabel); |
+ |
+ this.webstoreButton_.addEventListener( |
+ 'click', this.onWebstoreLinkActivated_.bind(this)); |
+ this.webstoreButton_.addEventListener( |
+ 'keydown', this.onWebstoreLinkKeyDown_.bind(this)); |
+ |
+ buttons.appendChild(this.webstoreButton_); |
+ |
+ /** |
+ * The webview element containing the Chrome Web Store widget. |
+ * @type {?WebView} |
+ * @private |
+ */ |
+ this.webview_ = null; |
+ |
+ /** |
+ * The Chrome Web Store widget URL. |
+ * @const {string} |
+ * @private |
+ */ |
+ this.widgetUrl_ = state.overrideCwsContainerUrlForTest || CWS_WIDGET_URL; |
+ |
+ /** |
+ * The Chrome Web Store widget origin. |
+ * @const {string} |
+ * @private |
+ */ |
+ this.widgetOrigin_ = state.overrideCwsContainerOriginForTest || |
+ CWS_WIDGET_ORIGIN; |
+ |
+ /** |
+ * Map of options for the widget. |
+ * @type {?Object.<string, *>} |
+ * @private |
+ */ |
+ this.options_ = null; |
+ |
+ /** |
+ * The ID of the item being installed. Null if no items are being installed. |
+ * @type {?string} |
+ * @private |
+ */ |
+ this.installingItemId_ = null; |
+ |
+ /** |
+ * The ID of the the installed item. Null if no item was installed. |
+ * @type {?string} |
+ * @private |
+ */ |
+ this.installedItemId_ = null; |
+ |
+ /** |
+ * The current widget state. |
+ * @type {CWSWidgetContainer.State} |
+ * @private |
+ */ |
+ this.state_ = CWSWidgetContainer.State.UNINITIALIZED; |
+ |
+ /** |
+ * The Chrome Web Store access token to be used when communicating with the |
+ * Chrome Web Store widget. |
+ * @type {?string} |
+ * @private |
+ */ |
+ this.accessToken_ = null; |
+ |
+ /** |
+ * Called when the Chrome Web Store widget is done. It resolves the promise |
+ * returned by {@code this.start()}. |
+ * @type {?function(CWSWidgetContainer.ResolveReason)} |
+ * @private |
+ */ |
+ this.resolveStart_ = null; |
+ |
+ /** |
+ * Promise for retriving {@code this.accessToken_}. |
+ * @type {Promise.<string>} |
+ * @private |
+ */ |
+ this.tokenGetter_ = this.createTokenGetter_(); |
+} |
+ |
+/** |
+ * @enum {string} |
+ * @private |
+ */ |
+CWSWidgetContainer.State = { |
+ UNINITIALIZED: 'CWSWidgetContainer.State.UNINITIALIZED', |
+ GETTING_ACCESS_TOKEN: 'CWSWidgetContainer.State.GETTING_ACCESS_TOKEN', |
+ ACCESS_TOKEN_READY: 'CWSWidgetContainer.State.ACCESS_TOKEN_READY', |
+ INITIALIZING: 'CWSWidgetContainer.State.INITIALIZING', |
+ INITIALIZE_FAILED_CLOSING: |
+ 'CWSWidgetContainer.State.INITIALIZE_FAILED_CLOSING', |
+ INITIALIZED: 'CWSWidgetContainer.State.INITIALIZED', |
+ INSTALLING: 'CWSWidgetContainer.State.INSTALLING', |
+ INSTALLED_CLOSING: 'CWSWidgetContainer.State.INSTALLED_CLOSING', |
+ OPENING_WEBSTORE_CLOSING: 'CWSWidgetContainer.State.OPENING_WEBSTORE_CLOSING', |
+ CANCELED_CLOSING: 'CWSWidgetContainer.State.CANCELED_CLOSING' |
+}; |
+Object.freeze(CWSWidgetContainer.State); |
+ |
+/** |
+ * @enum {string} |
+ * @const |
+ */ |
+CWSWidgetContainer.Result = { |
+ /** Install is done. The install app should be opened. */ |
+ INSTALL_SUCCESSFUL: 'CWSWidgetContainer.Result.INSTALL_SUCCESSFUL', |
+ /** User cancelled the suggest app dialog. No message should be shown. */ |
+ USER_CANCEL: 'CWSWidgetContainer.Result.USER_CANCEL', |
+ /** User clicked the link to web store so the dialog is closed. */ |
+ WEBSTORE_LINK_OPENED: 'CWSWidgetContainer.Result.WEBSTORE_LINK_OPENED', |
+ /** Failed to load the widget. Error message should be shown. */ |
+ FAILED: 'CWSWidgetContainer.Result.FAILED' |
+}; |
+Object.freeze(CWSWidgetContainer.Result); |
+ |
+/** |
+ * The reason due to which the container is resolving {@code this.start} |
+ * promise. |
+ * @enum {string} |
+ */ |
+CWSWidgetContainer.ResolveReason = { |
+ /** The widget container ended up in its final state. */ |
+ DONE: 'CWSWidgetContainer.ResolveReason.DONE', |
+ /** The widget container is being reset. */ |
+ RESET: 'CWSWidgetContainer.CloserReason.RESET' |
+}; |
+Object.freeze(CWSWidgetContainer.ResolveReason); |
+ |
+/** |
+ * @return {!Element} The element that should be focused initially. |
+ */ |
+CWSWidgetContainer.prototype.getInitiallyFocusedElement = function() { |
+ return this.webviewContainer_; |
+}; |
+ |
+/** |
+ * Injects headers into the passed request. |
+ * |
+ * @param {!Object} e Request event. |
+ * @return {!BlockingResponse} Modified headers. |
+ * @private |
+ */ |
+CWSWidgetContainer.prototype.authorizeRequest_ = function(e) { |
+ e.requestHeaders.push({ |
+ name: 'Authorization', |
+ value: 'Bearer ' + this.accessToken_ |
+ }); |
+ return /** @type {!BlockingResponse}*/ ({requestHeaders: e.requestHeaders}); |
+}; |
+ |
+/** |
+ * Retrieves the authorize token. |
+ * @return {Promise.<string>} The promise with the retrived access token. |
+ * @private |
+ */ |
+CWSWidgetContainer.prototype.createTokenGetter_ = function() { |
+ return new Promise(function(resolve, reject) { |
+ if (window.IN_TEST) { |
+ // In test, use a dummy string as token. This must be a non-empty string. |
+ resolve('DUMMY_ACCESS_TOKEN_FOR_TEST'); |
+ return; |
+ } |
+ |
+ // Fetch or update the access token. |
+ chrome.fileManagerPrivate.requestWebStoreAccessToken( |
+ function(accessToken) { |
+ if (chrome.runtime.lastError) { |
+ reject('Error retriveing Web Store access token: ' + |
+ chrome.runtime.lastError.message); |
+ } |
+ resolve(accessToken) |
+ }); |
+ }); |
+}; |
+ |
+/** |
+ * Ensures that the widget container is in the state where it can properly |
+ * handle showing the Chrome Web Store webview. |
+ * @return {Promise} Resolved when the container is ready to be used. |
+ */ |
+CWSWidgetContainer.prototype.ready = function() { |
+ return new Promise(function(resolve, reject) { |
+ if (this.state_ !== CWSWidgetContainer.State.UNINITIALIZED) { |
+ reject('Invalid state.'); |
+ return; |
+ } |
+ |
+ CWSWidgetContainer.Metrics.recordShowDialog(); |
+ CWSWidgetContainer.Metrics.startLoad(); |
+ |
+ this.state_ = CWSWidgetContainer.State.GETTING_ACCESS_TOKEN; |
+ |
+ this.tokenGetter_.then(function(accessToken) { |
+ this.state_ = CWSWidgetContainer.State.ACCESS_TOKEN_READY; |
+ this.accessToken_ = accessToken; |
+ resolve(); |
+ }.bind(this), function(error) { |
+ this.state_ = CWSWidgetContainer.State.UNINITIALIZED; |
+ reject('Failed to get Web Store access token: ' + error); |
+ }.bind(this)); |
+ }.bind(this)); |
+}; |
+ |
+/** |
+ * Initializes and starts loading the Chrome Web Store widget webview. |
+ * Must not be called before {@code this.ready()} is resolved. |
+ * |
+ * @param {!Object<string, *>} options Map of options for the dialog. |
+ * @param {?string} webStoreUrl Url for more results. Null if not supported. |
+ * @return {Promise.<CWSWidgetContainer.ResolveReason>} Resolved when app |
+ * installation is done, or the installation is cancelled. |
+ */ |
+CWSWidgetContainer.prototype.start = function(options, webStoreUrl) { |
+ return new Promise(function(resolve, reject) { |
+ if (this.state_ !== CWSWidgetContainer.State.ACCESS_TOKEN_READY) { |
+ this.state_ = CWSWidgetContainer.State.INITIALIZE_FAILED_CLOSING; |
+ reject('Invalid state in |start|.'); |
+ return; |
+ } |
+ |
+ if (!this.accessToken_) { |
+ this.state_ = CWSWidgetContainer.State.INITIALIZE_FAILED_CLOSING; |
+ reject('No access token.'); |
+ return; |
+ } |
+ |
+ this.resolveStart_ = resolve; |
+ |
+ this.state_ = CWSWidgetContainer.State.INITIALIZING; |
+ |
+ this.webStoreUrl_ = webStoreUrl; |
+ this.options_ = options; |
+ |
+ this.webstoreButton_.hidden = (webStoreUrl === null); |
+ |
+ this.webview_ = |
+ /** @type {!WebView} */(this.document_.createElement('webview')); |
+ this.webview_.id = 'cws-widget'; |
+ this.webview_.partition = 'persist:cwswidgets'; |
+ this.webview_.style.width = WEBVIEW_WIDTH + 'px'; |
+ this.webview_.style.height = WEBVIEW_HEIGHT + 'px'; |
+ this.webview_.request.onBeforeSendHeaders.addListener( |
+ this.authorizeRequest_.bind(this), |
+ /** @type {!RequestFilter}*/ ({urls: [this.widgetOrigin_ + '/*']}), |
+ ['blocking', 'requestHeaders']); |
+ this.webview_.addEventListener('newwindow', function(event) { |
+ event = /** @type {NewWindowEvent} */ (event); |
+ // Discard the window object and reopen in an external window. |
+ event.window.discard(); |
+ window.open(event.targetUrl); |
+ event.preventDefault(); |
+ }); |
+ this.webviewContainer_.appendChild(this.webview_); |
+ |
+ this.webviewContainer_.classList.add('show-spinner'); |
+ |
+ this.webviewClient_ = new CWSContainerClient( |
+ this.webview_, |
+ WEBVIEW_WIDTH, |
+ WEBVIEW_HEIGHT, |
+ this.widgetUrl_, |
+ this.widgetOrigin_, |
+ this.options_); |
+ this.webviewClient_.addEventListener(CWSContainerClient.Events.LOADED, |
+ this.onWidgetLoaded_.bind(this)); |
+ this.webviewClient_.addEventListener(CWSContainerClient.Events.LOAD_FAILED, |
+ this.onWidgetLoadFailed_.bind(this)); |
+ this.webviewClient_.addEventListener( |
+ CWSContainerClient.Events.REQUEST_INSTALL, |
+ this.onInstallRequest_.bind(this)); |
+ this.webviewClient_.load(); |
+ }.bind(this)); |
+}; |
+ |
+/** |
+ * Called when the 'See more...' button is activated. It opens |
+ * {@code this.webstoreUrl_}. |
+ * @param {Event} e The event that activated the link. Either mouse click or |
+ * key down event. |
+ * @private |
+ */ |
+CWSWidgetContainer.prototype.onWebstoreLinkActivated_ = function(e) { |
+ if (!this.webStoreUrl_) |
+ return; |
+ util.visitURL(this.webStoreUrl_); |
+ this.state_ = CWSWidgetContainer.State.OPENING_WEBSTORE_CLOSING; |
+ this.reportDone_(); |
+}; |
+ |
+/** |
+ * Key down event handler for webstore button element. If the key is enter, it |
+ * activates the button. |
+ * @param {Event} e The event |
+ * @private |
+ */ |
+CWSWidgetContainer.prototype.onWebstoreLinkKeyDown_ = function(e) { |
+ if (e.keyCode !== 13 /* Enter */) |
+ return; |
+ this.onWebstoreLinkActivated_(e); |
+}; |
+ |
+/** |
+ * Called when the widget is loaded successfully. |
+ * @param {Event} event Event. |
+ * @private |
+ */ |
+CWSWidgetContainer.prototype.onWidgetLoaded_ = function(event) { |
+ CWSWidgetContainer.Metrics.finishLoad(); |
+ CWSWidgetContainer.Metrics.recordLoad( |
+ CWSWidgetContainer.Metrics.LOAD.SUCCEEDED); |
+ |
+ this.webviewContainer_.classList.remove('show-spinner'); |
+ this.state_ = CWSWidgetContainer.State.INITIALIZED; |
+ |
+ this.webview_.focus(); |
+}; |
+ |
+/** |
+ * Called when the widget is failed to load. |
+ * @param {Event} event Event. |
+ * @private |
+ */ |
+CWSWidgetContainer.prototype.onWidgetLoadFailed_ = function(event) { |
+ CWSWidgetContainer.Metrics.recordLoad(CWSWidgetContainer.Metrics.LOAD.FAILED); |
+ |
+ this.webviewContainer_.classList.remove('show-spinner'); |
+ this.state_ = CWSWidgetContainer.State.INITIALIZE_FAILED_CLOSING; |
+ this.reportDone_(); |
+}; |
+ |
+/** |
+ * Called when the connection status is changed to offline. |
+ */ |
+CWSWidgetContainer.prototype.onConnectionLost = function() { |
+ if (this.state_ !== CWSWidgetContainer.State.UNINITIALIZED) { |
+ this.state_ = CWSWidgetContainer.State.INITIALIZE_FAILED_CLOSING; |
+ this.reportDone_(); |
+ } |
+}; |
+ |
+/** |
+ * Called when receiving the install request from the webview client. |
+ * @param {Event} e Event. |
+ * @private |
+ */ |
+CWSWidgetContainer.prototype.onInstallRequest_ = function(e) { |
+ var itemId = e.itemId; |
+ this.installingItemId_ = itemId; |
+ |
+ this.appInstaller_ = new AppInstaller(itemId); |
+ this.appInstaller_.install(this.onInstallCompleted_.bind(this)); |
+ |
+ this.webviewContainer_.classList.add('show-spinner'); |
+ this.state_ = CWSWidgetContainer.State.INSTALLING; |
+}; |
+ |
+/** |
+ * Called when the installation is completed from the app installer. |
+ * @param {AppInstaller.Result} result Result of the installation. |
+ * @param {string} error Detail of the error. |
+ * @private |
+ */ |
+CWSWidgetContainer.prototype.onInstallCompleted_ = function(result, error) { |
+ var success = (result === AppInstaller.Result.SUCCESS); |
+ |
+ this.webviewContainer_.classList.remove('show-spinner'); |
+ this.state_ = success ? |
+ CWSWidgetContainer.State.INSTALLED_CLOSING : |
+ CWSWidgetContainer.State.INITIALIZED; // Back to normal state. |
+ this.webviewClient_.onInstallCompleted(success, this.installingItemId_); |
+ this.installedItemId_ = this.installingItemId_; |
+ this.installingItemId_ = null; |
+ |
+ switch (result) { |
+ case AppInstaller.Result.SUCCESS: |
+ CWSWidgetContainer.Metrics.recordInstall( |
+ CWSWidgetContainer.Metrics.INSTALL.SUCCEEDED); |
+ this.reportDone_(); |
+ break; |
+ case AppInstaller.Result.CANCELLED: |
+ CWSWidgetContainer.Metrics.recordInstall( |
+ CWSWidgetContainer.Metrics.INSTALL.CANCELLED); |
+ // User cancelled the installation. Do nothing. |
+ break; |
+ case AppInstaller.Result.ERROR: |
+ CWSWidgetContainer.Metrics.recordInstall( |
+ CWSWidgetContainer.Metrics.INSTALL.FAILED); |
+ // TODO(tbarzic): Remove dialog showing call from this class. |
+ fileManager.ui.errorDialog.show( |
+ str('SUGGEST_DIALOG_INSTALLATION_FAILED'), |
+ null, |
+ null, |
+ null); |
+ break; |
+ } |
+}; |
+ |
+/** |
+ * Resolves the promise returned by {@code this.start} when widget is done with |
+ * installing apps. |
+ * @private |
+ */ |
+CWSWidgetContainer.prototype.reportDone_ = function() { |
+ if (this.resolveStart_) |
+ this.resolveStart_(CWSWidgetContainer.ResolveReason.DONE); |
+ this.resolveStart_ = null; |
+}; |
+ |
+/** |
+ * Finalizes the widget container state and returns the final app instalation |
+ * result. The widget should not be used after calling this. If called before |
+ * promise returned by {@code this.start} is resolved, the reported result will |
+ * be as if the widget was cancelled. |
+ * @return {{result: CWSWidgetContainer.Result, installedItemId: ?string}} |
+ */ |
+CWSWidgetContainer.prototype.finalizeAndGetResult = function() { |
+ switch (this.state_) { |
+ case CWSWidgetContainer.State.INSTALLING: |
+ // Install is being aborted. Send the failure result. |
+ // Cancels the install. |
+ if (this.webviewClient_) |
+ this.webviewClient_.onInstallCompleted(false, this.installingItemId_); |
+ this.installingItemId_ = null; |
+ |
+ // Assumes closing the dialog as canceling the install. |
+ this.state_ = CWSWidgetContainer.State.CANCELED_CLOSING; |
+ break; |
+ case CWSWidgetContainer.State.GETTING_ACCESS_TOKEN: |
+ case CWSWidgetContainer.State.ACCESS_TOKEN_READY: |
+ case CWSWidgetContainer.State.INITIALIZING: |
+ CWSWidgetContainer.Metrics.recordLoad( |
+ CWSWidgetContainer.Metrics.LOAD.CANCELLED); |
+ this.state_ = CWSWidgetContainer.State.CANCELED_CLOSING; |
+ break; |
+ case CWSWidgetContainer.State.INSTALLED_CLOSING: |
+ case CWSWidgetContainer.State.INITIALIZE_FAILED_CLOSING: |
+ case CWSWidgetContainer.State.OPENING_WEBSTORE_CLOSING: |
+ // Do nothing. |
+ break; |
+ case CWSWidgetContainer.State.INITIALIZED: |
+ this.state_ = CWSWidgetContainer.State.CANCELED_CLOSING; |
+ break; |
+ default: |
+ this.state_ = CWSWidgetContainer.State.CANCELED_CLOSING; |
+ console.error('Invalid state.'); |
+ } |
+ |
+ var result; |
+ switch (this.state_) { |
+ case CWSWidgetContainer.State.INSTALLED_CLOSING: |
+ result = CWSWidgetContainer.Result.INSTALL_SUCCESSFUL; |
+ CWSWidgetContainer.Metrics.recordCloseDialog( |
+ CWSWidgetContainer.Metrics.CLOSE_DIALOG.ITEM_INSTALLED); |
+ break; |
+ case CWSWidgetContainer.State.INITIALIZE_FAILED_CLOSING: |
+ result = CWSWidgetContainer.Result.FAILED; |
+ break; |
+ case CWSWidgetContainer.State.CANCELED_CLOSING: |
+ result = CWSWidgetContainer.Result.USER_CANCEL; |
+ CWSWidgetContainer.Metrics.recordCloseDialog( |
+ CWSWidgetContainer.Metrics.CLOSE_DIALOG.USER_CANCELLED); |
+ break; |
+ case CWSWidgetContainer.State.OPENING_WEBSTORE_CLOSING: |
+ result = CWSWidgetContainer.Result.WEBSTORE_LINK_OPENED; |
+ CWSWidgetContainer.Metrics.recordCloseDialog( |
+ CWSWidgetContainer.Metrics.CLOSE_DIALOG.WEBSTORE_LINK_OPENED); |
+ break; |
+ default: |
+ result = CWSWidgetContainer.Result.USER_CANCEL; |
+ CWSWidgetContainer.Metrics.recordCloseDialog( |
+ CWSWidgetContainer.Metrics.CLOSE_DIALOG.UNKNOWN_ERROR); |
+ } |
+ |
+ this.state_ = CWSWidgetContainer.State.UNINITIALIZED; |
+ |
+ this.reset_(); |
+ |
+ return {result: result, installedItemId: this.installedItemId_}; |
+}; |
+ |
+/** |
+ * Resets the widget. |
+ * @private |
+ */ |
+CWSWidgetContainer.prototype.reset_ = function () { |
+ if (this.state_ !== CWSWidgetContainer.State.UNINITIALIZED) |
+ console.error('Widget reset before its state was finalized.'); |
+ |
+ if (this.resolveStart_) { |
+ this.resolveStart_(CWSWidgetContainer.ResolveReason.RESET); |
+ this.resolveStart_ = null; |
+ } |
+ |
+ if (this.webviewClient_) { |
+ this.webviewClient_.dispose(); |
+ this.webviewClient_ = null; |
+ } |
+ |
+ if (this.webview_) |
+ this.webviewContainer_.removeChild(this.webview_); |
+ this.options_ = null; |
+}; |
+ |
+/** |
+ * Utility methods and constants to record histograms. |
+ */ |
+CWSWidgetContainer.Metrics = {}; |
+ |
+/** |
+ * @enum {number} |
+ * @const |
+ */ |
+CWSWidgetContainer.Metrics.LOAD = { |
+ SUCCEEDED: 0, |
+ CANCELLED: 1, |
+ FAILED: 2, |
+}; |
+ |
+/** |
+ * @enum {number} |
+ * @const |
+ */ |
+CWSWidgetContainer.Metrics.CLOSE_DIALOG = { |
+ UNKNOWN_ERROR: 0, |
+ ITEM_INSTALLED: 1, |
+ USER_CANCELLED: 2, |
+ WEBSTORE_LINK_OPENED: 3, |
+}; |
+ |
+/** |
+ * @enum {number} |
+ * @const |
+ */ |
+CWSWidgetContainer.Metrics.INSTALL = { |
+ SUCCEEDED: 0, |
+ CANCELLED: 1, |
+ FAILED: 2, |
+}; |
+ |
+/** |
+ * @param {number} result Result of load, which must be defined in |
+ * CWSWidgetContainer.Metrics.LOAD. |
+ */ |
+CWSWidgetContainer.Metrics.recordLoad = function(result) { |
+ if (0 <= result && result < 3) |
+ metrics.recordEnum('SuggestApps.Load', result, 3); |
+}; |
+ |
+/** |
+ * @param {number} reason Reason of closing dialog, which must be defined in |
+ * CWSWidgetContainer.Metrics.CLOSE_DIALOG. |
+ */ |
+CWSWidgetContainer.Metrics.recordCloseDialog = function(reason) { |
+ if (0 <= reason && reason < 4) |
+ metrics.recordEnum('SuggestApps.CloseDialog', reason, 4); |
+}; |
+ |
+/** |
+ * @param {number} result Result of installation, which must be defined in |
+ * CWSWidgetContainer.Metrics.INSTALL. |
+ */ |
+CWSWidgetContainer.Metrics.recordInstall = function(result) { |
+ if (0 <= result && result < 3) |
+ metrics.recordEnum('SuggestApps.Install', result, 3); |
+}; |
+ |
+CWSWidgetContainer.Metrics.recordShowDialog = function() { |
+ metrics.recordUserAction('SuggestApps.ShowDialog'); |
+}; |
+ |
+CWSWidgetContainer.Metrics.startLoad = function() { |
+ metrics.startInterval('SuggestApps.LoadTime'); |
+}; |
+ |
+CWSWidgetContainer.Metrics.finishLoad = function() { |
+ metrics.recordInterval('SuggestApps.LoadTime'); |
+}; |