Commit 1c38f9fb authored by tbarzic's avatar tbarzic Committed by Commit bot

Use delegate object for chrome API calls in cws_widget_container

This would make the widget container code independent of the
chrome APIs
available/used by the extension embedding the widget.

TEST=Verified installing file handlers and file system providers works
BUG=477106

Review URL: https://codereview.chromium.org/1091943002

Cr-Commit-Position: refs/heads/master@{#327345}
parent 15525c4f
......@@ -6,10 +6,14 @@
* Manage the installation of apps.
*
* @param {string} itemId Item id to be installed.
* @param {!CWSWidgetContainer.PlatformDelegate} delegate Delegate for accessing
* Chrome platform APIs.
* @constructor
* @struct
*/
function AppInstaller(itemId) {
function AppInstaller(itemId, delegate) {
/** @private {!CWSWidgetContainer.PlatformDelegate} */
this.delegate_ = delegate;
this.itemId_ = itemId;
this.callback_ = null;
}
......@@ -43,12 +47,9 @@ AppInstaller.USER_CANCELLED_ERROR_STR_ = 'User cancelled install';
*/
AppInstaller.prototype.install = function(callback) {
this.callback_ = callback;
chrome.fileManagerPrivate.installWebstoreItem(
this.delegate_.installWebstoreItem(
this.itemId_,
false, // Shows installation prompt.
function() {
this.onInstallCompleted_(chrome.runtime.lastError);
}.bind(this));
this.onInstallCompleted_.bind(this));
};
/**
......@@ -63,8 +64,8 @@ AppInstaller.prototype.cancel = function() {
/**
* Called when the installation is completed.
*
* @param {!Object|undefined} error Undefined if the installation is success,
* otherwise an object which contains error message.
* @param {?string} error Null if the installation is success,
* otherwise error message.
* @private
*/
AppInstaller.prototype.onInstallCompleted_ = function(error) {
......@@ -72,14 +73,12 @@ AppInstaller.prototype.onInstallCompleted_ = function(error) {
return;
var installerResult = AppInstaller.Result.SUCCESS;
var errorMessage = '';
if (error) {
if (error !== null) {
installerResult =
error.message == AppInstaller.USER_CANCELLED_ERROR_STR_ ?
error == AppInstaller.USER_CANCELLED_ERROR_STR_ ?
AppInstaller.Result.CANCELLED :
AppInstaller.Result.ERROR;
errorMessage = error.message;
}
this.callback_(installerResult, errorMessage);
this.callback_(installerResult, error || '');
this.callback_ = null;
};
......@@ -9,10 +9,15 @@
* @param {string} url Share Url for an entry.
* @param {string} target Target (scheme + host + port) of the widget.
* @param {Object<string, *>} options Options to be sent to the dialog host.
* @param {!CWSWidgetContainer.PlatformDelegate} delegate Delegate for accessing
* Chrome platform APIs.
* @constructor
* @extends {cr.EventTarget}
*/
function CWSContainerClient(webView, width, height, url, target, options) {
function CWSContainerClient(webView, width, height, url, target, options,
delegate) {
/** @private {!CWSWidgetContainer.PlatformDelegate} */
this.delegate_ = delegate;
this.webView_ = webView;
this.width_ = width;
this.height_ = height;
......@@ -194,23 +199,26 @@ CWSContainerClient.prototype.postInstallSuccessMessage_ = function(itemId) {
*/
CWSContainerClient.prototype.postInitializeMessage_ = function() {
new Promise(function(fulfill, reject) {
chrome.fileManagerPrivate.getProvidingExtensions(function(items) {
if (chrome.runtime.lastError) {
reject(chrome.runtime.lastError.message);
return;
}
fulfill(items.map(function(item) {
return item.extensionId;
}));
this.delegate_.getInstalledItems(
/**
* @param {?Array.<!string>} items Installed items.
* Null on error.
*/
function(items) {
if (!items) {
reject('Failed to retrive installed items.');
return;
}
fulfill(items);
})
}).then(
}.bind(this)).then(
/**
* @param {!Array<string>} preinstalledExtensionIDs
*/
function(preinstalledExtensionIDs) {
var message = {
message: 'initialize',
hl: util.getCurrentLocaleOrDefault(),
hl: this.delegate_.strings.UI_LOCALE,
width: this.width_,
height: this.height_,
preinstalled_items: preinstalledExtensionIDs,
......
......@@ -45,10 +45,19 @@ var CWS_WIDGET_ORIGIN = 'https://clients5.google.com';
*
* @param {!HTMLDocument} document The document to contain this container.
* @param {!HTMLElement} parentNode Node to be parent for this container.
* @param {!CWSWidgetContainer.PlatformDelegate} delegate Delegate for accessing
* Chrome platform APIs.
* @param {!SuggestAppDialogState} state Static state of suggest app dialog.
* @constructor
*/
function CWSWidgetContainer(document, parentNode, state) {
function CWSWidgetContainer(document, parentNode, delegate, state) {
/** @private {!CWSWidgetContainer.PlatformDelegate} */
this.delegate_ = delegate;
/** @private {!CWSWidgetContainer.MetricsRecorder} */
this.metricsRecorder_ =
new CWSWidgetContainer.MetricsRecorder(delegate.metricsImpl);
/**
* The document that will contain the container.
* @const {!HTMLDocument}
......@@ -117,7 +126,7 @@ function CWSWidgetContainer(document, parentNode, state) {
*/
var webstoreButtonLabel = this.document_.createElement('span');
webstoreButtonLabel.classList.add('cws-widget-webstore-button-label');
webstoreButtonLabel.textContent = str('SUGGEST_DIALOG_LINK_TO_WEBSTORE');
webstoreButtonLabel.textContent = this.delegate_.strings.LINK_TO_WEBSTORE;
this.webstoreButton_.appendChild(webstoreButtonLabel);
this.webstoreButton_.addEventListener(
......@@ -208,6 +217,40 @@ function CWSWidgetContainer(document, parentNode, state) {
this.errorDialog_ = new CWSWidgetContainerErrorDialog(parentNode);
}
/**
* Strings required by the widget container.
* @typedef {{
* UI_LOCALE: string,
* LINK_TO_WEBSTORE: string,
* INSTALLATION_FAILED_MESSAGE: string
* }}
*/
CWSWidgetContainer.Strings;
/**
* Functions for reporting metrics for the widget.
* @typedef {{
* recordEnum: function(string, number, number),
* recordUserAction: function(string),
* startInterval: function(string),
* recordInterval: function(string)
* }}
*/
CWSWidgetContainer.MetricsImpl;
/**
* Type for delegate used by CWSWidgetContainer component to access Chrome
* platform APIs.
* @typedef {{
* strings: !CWSWidgetContainer.Strings,
* metricsImpl: !CWSWidgetContainer.MetricsImpl,
* installWebstoreItem: function(string, function(?string)),
* getInstalledItems: function(function(?Array<!string>)),
* requestWebstoreAccessToken: function(function(?string))
* }}
*/
CWSWidgetContainer.PlatformDelegate;
/**
* @enum {string}
* @private
......@@ -293,15 +336,16 @@ CWSWidgetContainer.prototype.createTokenGetter_ = function() {
}
// Fetch or update the access token.
chrome.fileManagerPrivate.requestWebStoreAccessToken(
this.delegate_.requestWebstoreAccessToken(
/** @param {?string} accessToken The requested token. Null on error. */
function(accessToken) {
if (chrome.runtime.lastError) {
reject('Error retriveing Web Store access token: ' +
chrome.runtime.lastError.message);
if (!accessToken) {
reject('Error retriveing Web Store access token.');
return;
}
resolve(accessToken)
});
});
}.bind(this));
};
/**
......@@ -323,8 +367,8 @@ CWSWidgetContainer.prototype.ready = function() {
return;
}
CWSWidgetContainer.Metrics.recordShowDialog();
CWSWidgetContainer.Metrics.startLoad();
this.metricsRecorder_.recordShowDialog();
this.metricsRecorder_.startLoad();
this.state_ = CWSWidgetContainer.State.GETTING_ACCESS_TOKEN;
......@@ -399,7 +443,8 @@ CWSWidgetContainer.prototype.start = function(options, webStoreUrl) {
WEBVIEW_HEIGHT,
this.widgetUrl_,
this.widgetOrigin_,
this.options_);
this.options_,
this.delegate_);
this.webviewClient_.addEventListener(CWSContainerClient.Events.LOADED,
this.onWidgetLoaded_.bind(this));
this.webviewClient_.addEventListener(CWSContainerClient.Events.LOAD_FAILED,
......@@ -447,9 +492,9 @@ CWSWidgetContainer.prototype.onWebstoreLinkKeyDown_ = function(e) {
* @private
*/
CWSWidgetContainer.prototype.onWidgetLoaded_ = function(event) {
CWSWidgetContainer.Metrics.finishLoad();
CWSWidgetContainer.Metrics.recordLoad(
CWSWidgetContainer.Metrics.LOAD.SUCCEEDED);
this.metricsRecorder_.finishLoad();
this.metricsRecorder_.recordLoad(
CWSWidgetContainer.MetricsRecorder.LOAD.SUCCEEDED);
this.state_ = CWSWidgetContainer.State.INITIALIZED;
......@@ -463,7 +508,8 @@ CWSWidgetContainer.prototype.onWidgetLoaded_ = function(event) {
* @private
*/
CWSWidgetContainer.prototype.onWidgetLoadFailed_ = function(event) {
CWSWidgetContainer.Metrics.recordLoad(CWSWidgetContainer.Metrics.LOAD.FAILED);
this.metricsRecorder_.recordLoad(
CWSWidgetContainer.MetricsRecorder.LOAD.FAILED);
this.spinnerLayerController_.setVisible(false);
this.state_ = CWSWidgetContainer.State.INITIALIZE_FAILED_CLOSING;
......@@ -489,7 +535,7 @@ CWSWidgetContainer.prototype.onInstallRequest_ = function(e) {
var itemId = e.itemId;
this.installingItemId_ = itemId;
this.appInstaller_ = new AppInstaller(itemId);
this.appInstaller_ = new AppInstaller(itemId, this.delegate_);
this.appInstaller_.install(this.onItemInstalled_.bind(this));
this.spinnerLayerController_.setVisible(true);
......@@ -531,20 +577,20 @@ CWSWidgetContainer.prototype.onItemInstalled_ = function(result, error) {
switch (result) {
case AppInstaller.Result.SUCCESS:
CWSWidgetContainer.Metrics.recordInstall(
CWSWidgetContainer.Metrics.INSTALL.SUCCEEDED);
this.metricsRecorder_.recordInstall(
CWSWidgetContainer.MetricsRecorder.INSTALL.SUCCEEDED);
// Wait for the widget webview container to dispatch INSTALL_DONE.
break;
case AppInstaller.Result.CANCELLED:
CWSWidgetContainer.Metrics.recordInstall(
CWSWidgetContainer.Metrics.INSTALL.CANCELLED);
this.metricsRecorder_.recordInstall(
CWSWidgetContainer.MetricsRecorder.INSTALL.CANCELLED);
// User cancelled the installation. Do nothing.
break;
case AppInstaller.Result.ERROR:
CWSWidgetContainer.Metrics.recordInstall(
CWSWidgetContainer.Metrics.INSTALL.FAILED);
this.metricsRecorder_.recordInstall(
CWSWidgetContainer.MetricsRecorder.INSTALL.FAILED);
this.errorDialog_.show(
str('SUGGEST_DIALOG_INSTALLATION_FAILED'),
this.delegate_.strings.INSTALLATION_FAILED_MESSAGE,
null,
null,
null);
......@@ -585,8 +631,8 @@ CWSWidgetContainer.prototype.finalizeAndGetResult = function() {
case CWSWidgetContainer.State.GETTING_ACCESS_TOKEN:
case CWSWidgetContainer.State.ACCESS_TOKEN_READY:
case CWSWidgetContainer.State.INITIALIZING:
CWSWidgetContainer.Metrics.recordLoad(
CWSWidgetContainer.Metrics.LOAD.CANCELLED);
this.metricsRecorder_.recordLoad(
CWSWidgetContainer.MetricsRecorder.LOAD.CANCELLED);
this.state_ = CWSWidgetContainer.State.CANCELED_CLOSING;
break;
case CWSWidgetContainer.State.WAITING_FOR_CONFIRMATION:
......@@ -613,26 +659,26 @@ CWSWidgetContainer.prototype.finalizeAndGetResult = function() {
switch (this.state_) {
case CWSWidgetContainer.State.INSTALLED_CLOSING:
result = CWSWidgetContainer.Result.INSTALL_SUCCESSFUL;
CWSWidgetContainer.Metrics.recordCloseDialog(
CWSWidgetContainer.Metrics.CLOSE_DIALOG.ITEM_INSTALLED);
this.metricsRecorder_.recordCloseDialog(
CWSWidgetContainer.MetricsRecorder.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);
this.metricsRecorder_.recordCloseDialog(
CWSWidgetContainer.MetricsRecorder.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);
this.metricsRecorder_.recordCloseDialog(
CWSWidgetContainer.MetricsRecorder.CLOSE_DIALOG.WEBSTORE_LINK_OPENED);
break;
default:
result = CWSWidgetContainer.Result.USER_CANCEL;
CWSWidgetContainer.Metrics.recordCloseDialog(
CWSWidgetContainer.Metrics.CLOSE_DIALOG.UNKNOWN_ERROR);
this.metricsRecorder_.recordCloseDialog(
CWSWidgetContainer.MetricsRecorder.CLOSE_DIALOG.UNKNOWN_ERROR);
}
this.state_ = CWSWidgetContainer.State.UNINITIALIZED;
......@@ -812,14 +858,19 @@ CWSWidgetContainer.SpinnerLayerController.prototype.setVisible =
/**
* Utility methods and constants to record histograms.
* @param {!CWSWidgetContainer.MetricsImpl} metricsImpl
* @constructor
*/
CWSWidgetContainer.Metrics = {};
CWSWidgetContainer.MetricsRecorder = function(metricsImpl) {
/** @private {!CWSWidgetContainer.MetricsImpl} */
this.metricsImpl_ = metricsImpl;
};
/**
* @enum {number}
* @const
*/
CWSWidgetContainer.Metrics.LOAD = {
CWSWidgetContainer.MetricsRecorder.LOAD = {
SUCCEEDED: 0,
CANCELLED: 1,
FAILED: 2,
......@@ -829,7 +880,7 @@ CWSWidgetContainer.Metrics.LOAD = {
* @enum {number}
* @const
*/
CWSWidgetContainer.Metrics.CLOSE_DIALOG = {
CWSWidgetContainer.MetricsRecorder.CLOSE_DIALOG = {
UNKNOWN_ERROR: 0,
ITEM_INSTALLED: 1,
USER_CANCELLED: 2,
......@@ -840,7 +891,7 @@ CWSWidgetContainer.Metrics.CLOSE_DIALOG = {
* @enum {number}
* @const
*/
CWSWidgetContainer.Metrics.INSTALL = {
CWSWidgetContainer.MetricsRecorder.INSTALL = {
SUCCEEDED: 0,
CANCELLED: 1,
FAILED: 2,
......@@ -848,39 +899,40 @@ CWSWidgetContainer.Metrics.INSTALL = {
/**
* @param {number} result Result of load, which must be defined in
* CWSWidgetContainer.Metrics.LOAD.
* CWSWidgetContainer.MetricsRecorder.LOAD.
*/
CWSWidgetContainer.Metrics.recordLoad = function(result) {
CWSWidgetContainer.MetricsRecorder.prototype.recordLoad = function(result) {
if (0 <= result && result < 3)
metrics.recordEnum('SuggestApps.Load', result, 3);
this.metricsImpl_.recordEnum('SuggestApps.Load', result, 3);
};
/**
* @param {number} reason Reason of closing dialog, which must be defined in
* CWSWidgetContainer.Metrics.CLOSE_DIALOG.
* CWSWidgetContainer.MetricsRecorder.CLOSE_DIALOG.
*/
CWSWidgetContainer.Metrics.recordCloseDialog = function(reason) {
CWSWidgetContainer.MetricsRecorder.prototype.recordCloseDialog = function(
reason) {
if (0 <= reason && reason < 4)
metrics.recordEnum('SuggestApps.CloseDialog', reason, 4);
this.metricsImpl_.recordEnum('SuggestApps.CloseDialog', reason, 4);
};
/**
* @param {number} result Result of installation, which must be defined in
* CWSWidgetContainer.Metrics.INSTALL.
* CWSWidgetContainer.MetricsRecorder.INSTALL.
*/
CWSWidgetContainer.Metrics.recordInstall = function(result) {
CWSWidgetContainer.MetricsRecorder.prototype.recordInstall = function(result) {
if (0 <= result && result < 3)
metrics.recordEnum('SuggestApps.Install', result, 3);
this.metricsImpl_.recordEnum('SuggestApps.Install', result, 3);
};
CWSWidgetContainer.Metrics.recordShowDialog = function() {
metrics.recordUserAction('SuggestApps.ShowDialog');
CWSWidgetContainer.MetricsRecorder.prototype.recordShowDialog = function() {
this.metricsImpl_.recordUserAction('SuggestApps.ShowDialog');
};
CWSWidgetContainer.Metrics.startLoad = function() {
metrics.startInterval('SuggestApps.LoadTime');
CWSWidgetContainer.MetricsRecorder.prototype.startLoad = function() {
this.metricsImpl_.startInterval('SuggestApps.LoadTime');
};
CWSWidgetContainer.Metrics.finishLoad = function() {
metrics.recordInterval('SuggestApps.LoadTime');
CWSWidgetContainer.MetricsRecorder.prototype.finishLoad = function() {
this.metricsImpl_.recordInterval('SuggestApps.LoadTime');
};
......@@ -33,7 +33,8 @@ function SuggestAppsDialog(parentNode, state) {
* @const {!CWSWidgetContainer}
* @private
*/
this.widget_ = new CWSWidgetContainer(this.document_, widgetRoot, state);
this.widget_ = new CWSWidgetContainer(
this.document_, widgetRoot, this.createWidgetPlatformDelegate_(), state);
this.initialFocusElement_ = this.widget_.getInitiallyFocusedElement();
......@@ -114,6 +115,71 @@ SuggestAppsDialog.prototype.showProviders = function(onDialogClosed) {
onDialogClosed);
};
/**
* Creates platform delegate for CWSWidgetContainer.
* @return {!CWSWidgetContainer.PlatformDelegate}
* @private
*/
SuggestAppsDialog.prototype.createWidgetPlatformDelegate_ = function() {
return {
strings: {
UI_LOCALE: util.getCurrentLocaleOrDefault(),
LINK_TO_WEBSTORE: str('SUGGEST_DIALOG_LINK_TO_WEBSTORE'),
INSTALLATION_FAILED_MESSAGE: str('SUGGEST_DIALOG_INSTALLATION_FAILED')
},
metricsImpl: metrics,
/**
* @param {string} itemId,
* @param {function(?string)} callback Callback argument is set to error
* message (null on success)
*/
installWebstoreItem: function(itemId, callback) {
chrome.fileManagerPrivate.installWebstoreItem(
itemId,
false /* show installation prompt */,
function() {
callback(chrome.runtime.lastError ?
chrome.runtime.lastError.message || 'UNKNOWN ERROR' : null);
});
},
/**
* @param {function(?Array.<!string>)} callback Callback
* argument is a list of installed item ids (null on error).
*/
getInstalledItems: function(callback) {
chrome.fileManagerPrivate.getProvidingExtensions(function(items) {
if (chrome.runtime.lastError) {
console.error('Failed to get installed items: ' +
chrome.runtime.lastError.message);
callback(null);
return;
}
callback(items.map(function(item) {
return item.extensionId;
}));
});
},
/**
* @param {function(?string)} callback Callback argument is the requested
* token (null on error).
*/
requestWebstoreAccessToken: function(callback) {
chrome.fileManagerPrivate.requestWebStoreAccessToken(function(token) {
if (chrome.runtime.lastError) {
console.error(chrome.runtime.lastError.message);
callback(null);
return;
}
callback(token);
});
}
};
};
/**
* Internal method to show a dialog. This should be called only from 'Suggest.
* appDialog.showXxxx()' functions.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment