Index: remoting/webapp/base/js/application.js |
diff --git a/remoting/webapp/base/js/application.js b/remoting/webapp/base/js/application.js |
index bcd6cf122f2d5a5471fd16926e2e740863c23869..11fec3651a3ec7ed1d6728231391202990509e11 100644 |
--- a/remoting/webapp/base/js/application.js |
+++ b/remoting/webapp/base/js/application.js |
@@ -15,10 +15,13 @@ var remoting = remoting || {}; |
/** |
* @param {Array<string>} appCapabilities Array of application capabilities. |
* @constructor |
+ * @implements {remoting.ApplicationInterface} |
*/ |
remoting.Application = function(appCapabilities) { |
- /** @private {remoting.Application.Delegate} */ |
- this.delegate_ = null; |
+ // Create global factories. |
+ remoting.ClientPlugin.factory = new remoting.DefaultClientPluginFactory(); |
+ remoting.SessionConnector.factory = |
+ new remoting.DefaultSessionConnectorFactory(); |
/** @private {Array<string>} */ |
this.appCapabilities_ = [ |
@@ -29,26 +32,23 @@ remoting.Application = function(appCapabilities) { |
// Append the app-specific capabilities. |
this.appCapabilities_.push.apply(this.appCapabilities_, appCapabilities); |
- /** @private {remoting.SessionConnector} */ |
- this.sessionConnector_ = null; |
+ /** @protected {remoting.SessionConnector} */ |
+ this.sessionConnector_ = remoting.SessionConnector.factory.createConnector( |
+ document.getElementById('client-container'), |
+ this.onConnected_.bind(this), |
+ this.onError_.bind(this), |
+ this.onConnectionFailed_.bind(this), |
+ this.appCapabilities_); |
/** @private {base.Disposable} */ |
this.sessionConnectedHooks_ = null; |
}; |
/** |
- * @param {remoting.Application.Delegate} appDelegate The delegate that |
- * contains the app-specific functionality. |
+ * @return {remoting.SessionConnector} The session connector. |
*/ |
-remoting.Application.prototype.setDelegate = function(appDelegate) { |
- this.delegate_ = appDelegate; |
-}; |
- |
-/** |
- * @return {string} Application product name to be used in UI. |
- */ |
-remoting.Application.prototype.getApplicationName = function() { |
- return this.delegate_.getApplicationName(); |
+remoting.Application.prototype.getSessionConnector = function() { |
+ return this.sessionConnector_; |
}; |
/** |
@@ -60,18 +60,33 @@ remoting.Application.prototype.hasCapability = function(capability) { |
return capabilities.indexOf(capability) != -1; |
}; |
+/* Disconnect the remoting client. */ |
+remoting.Application.prototype.disconnect = function() { |
+ if (remoting.clientSession) { |
+ remoting.clientSession.disconnect(remoting.Error.none()); |
+ console.log('Disconnected.'); |
+ } |
+}; |
+ |
+/* Public method to exit the application. */ |
+remoting.Application.prototype.quit = function() { |
+ this.exitApplication_(); |
+}; |
+ |
+/** |
+ * Close the main window when quitting the application. This should be called |
+ * by exitApplication() in the subclass. |
+ * @protected |
+ */ |
+remoting.Application.prototype.exit_ = function() { |
Jamie
2015/03/26 01:54:30
Having both exit_ and quit is confusing. Perhaps e
garykac
2015/03/26 16:38:11
Changed name to closeMainWindow_ since I like havi
|
+ chrome.app.window.current().close(); |
+}; |
+ |
/** |
* Initialize the application and register all event handlers. After this |
* is called, the app is running and waiting for user events. |
- * |
- * @return {void} Nothing. |
*/ |
remoting.Application.prototype.start = function() { |
- // Create global objects. |
- remoting.ClientPlugin.factory = new remoting.DefaultClientPluginFactory(); |
- remoting.SessionConnector.factory = |
- new remoting.DefaultSessionConnectorFactory(); |
- |
// TODO(garykac): This should be owned properly rather than living in the |
// global 'remoting' namespace. |
remoting.settings = new remoting.Settings(); |
@@ -79,17 +94,17 @@ remoting.Application.prototype.start = function() { |
remoting.initGlobalObjects(); |
remoting.initIdentity(); |
- this.delegate_.init(); |
+ this.initApplication_(); |
var that = this; |
- remoting.identity.getToken().then( |
- this.delegate_.start.bind(this.delegate_, this.getSessionConnector()) |
- ).catch(remoting.Error.handler( |
+ remoting.identity.getToken(). |
+ then(this.startApplication_.bind(this)). |
+ catch(remoting.Error.handler( |
function(/** !remoting.Error */ error) { |
if (error.hasTag(remoting.Error.Tag.CANCELLED)) { |
- that.exit(); |
+ that.exitApplication_(); |
} else { |
- that.delegate_.signInFailed(error); |
+ that.signInFailed_(error); |
} |
} |
) |
@@ -97,82 +112,19 @@ remoting.Application.prototype.start = function() { |
}; |
/** |
- * Quit the application. |
- */ |
-remoting.Application.prototype.exit = function() { |
- this.delegate_.handleExit(); |
- chrome.app.window.current().close(); |
-}; |
- |
-/** Disconnect the remoting client. */ |
-remoting.Application.prototype.disconnect = function() { |
- if (remoting.clientSession) { |
- remoting.clientSession.disconnect(remoting.Error.none()); |
- console.log('Disconnected.'); |
- } |
-}; |
- |
-/** |
* Called when a new session has been connected. |
* |
* @param {remoting.ConnectionInfo} connectionInfo |
* @return {void} Nothing. |
+ * @protected |
*/ |
-remoting.Application.prototype.onConnected = function(connectionInfo) { |
+remoting.Application.prototype.initSession_ = function(connectionInfo) { |
this.sessionConnectedHooks_ = new base.Disposables( |
new base.EventHook(connectionInfo.session(), 'stateChanged', |
this.onSessionFinished_.bind(this)), |
new base.RepeatingTimer(this.updateStatistics_.bind(this), 1000) |
); |
remoting.clipboard.startSession(); |
- |
- this.delegate_.handleConnected(connectionInfo); |
-}; |
- |
-/** |
- * Called when the current session has been disconnected. |
- * |
- * @return {void} Nothing. |
- */ |
-remoting.Application.prototype.onDisconnected = function() { |
- this.delegate_.handleDisconnected(); |
-}; |
- |
-/** |
- * Called when the current session's connection has failed. |
- * |
- * @param {!remoting.Error} error |
- * @return {void} Nothing. |
- */ |
-remoting.Application.prototype.onConnectionFailed = function(error) { |
- this.delegate_.handleConnectionFailed(this.sessionConnector_, error); |
-}; |
- |
-/** |
- * Called when an error needs to be displayed to the user. |
- * |
- * @param {!remoting.Error} errorTag The error to be localized and displayed. |
- * @return {void} Nothing. |
- */ |
-remoting.Application.prototype.onError = function(errorTag) { |
- this.delegate_.handleError(errorTag); |
-}; |
- |
-/** |
- * @return {remoting.SessionConnector} A session connector, creating a new one |
- * if necessary. |
- */ |
-remoting.Application.prototype.getSessionConnector = function() { |
- // TODO(garykac): Check if this can be initialized in the ctor. |
- if (!this.sessionConnector_) { |
- this.sessionConnector_ = remoting.SessionConnector.factory.createConnector( |
- document.getElementById('client-container'), |
- this.onConnected.bind(this), |
- this.onError.bind(this), |
- this.onConnectionFailed.bind(this), |
- this.appCapabilities_); |
- } |
- return this.sessionConnector_; |
}; |
/** |
@@ -186,7 +138,7 @@ remoting.Application.prototype.onSessionFinished_ = function(state) { |
switch (state.current) { |
case remoting.ClientSession.State.CLOSED: |
console.log('Connection closed by host'); |
- this.onDisconnected(); |
+ this.onDisconnected_(); |
break; |
case remoting.ClientSession.State.FAILED: |
var error = remoting.clientSession.getError(); |
@@ -195,14 +147,14 @@ remoting.Application.prototype.onSessionFinished_ = function(state) { |
if (error === null) { |
error = remoting.Error.unexpected(); |
} |
- this.onError(error); |
+ this.onError_(error); |
break; |
default: |
console.error('Unexpected client plugin state: ' + state.current); |
// This should only happen if the web-app and client plugin get out of |
// sync, so MISSING_PLUGIN is a suitable error. |
- this.onError(new remoting.Error(remoting.Error.Tag.MISSING_PLUGIN)); |
+ this.onError_(new remoting.Error(remoting.Error.Tag.MISSING_PLUGIN)); |
break; |
} |
@@ -219,83 +171,143 @@ remoting.Application.prototype.updateStatistics_ = function() { |
}; |
+/* |
+ * remoting.ApplicationInterface |
+ * These functions must be overridden in the subclass. |
+ */ |
+ |
+/** @return {string} */ |
+remoting.Application.prototype.getApplicationName = function() { |
+ base.debug.assert(false, "Subclass must override"); |
+}; |
+ |
+/** |
+ * @param {!remoting.Error} error |
+ * @protected |
+ */ |
+remoting.Application.prototype.signInFailed_ = function(error) { |
+ base.debug.assert(false, "Subclass must override"); |
+}; |
+ |
+/** @protected */ |
+remoting.Application.prototype.initApplication_ = function() { |
+ base.debug.assert(false, "Subclass must override"); |
+}; |
+ |
+/** |
+ * @param {string} token |
+ * @protected |
+ */ |
+remoting.Application.prototype.startApplication_ = function(token) { |
+ base.debug.assert(false, "Subclass must override"); |
+}; |
+ |
+remoting.Application.prototype.exitApplication_ = function() { |
+ base.debug.assert(false, "Subclass must override"); |
+}; |
+ |
+/** |
+ * @param {remoting.ConnectionInfo} connectionInfo |
+ * @protected |
+ */ |
+remoting.Application.prototype.onConnected_ = function(connectionInfo) { |
+ base.debug.assert(false, "Subclass must override"); |
+}; |
+ |
+/** @protected */ |
+remoting.Application.prototype.onDisconnected_ = function() { |
+ base.debug.assert(false, "Subclass must override"); |
+}; |
+ |
+/** |
+ * @param {!remoting.Error} error |
+ * @protected |
+ */ |
+remoting.Application.prototype.onConnectionFailed_ = function(error) { |
+ base.debug.assert(false, "Subclass must override"); |
+}; |
+ |
+/** |
+ * @param {!remoting.Error} error The error to be localized and displayed. |
+ * @protected |
+ */ |
+remoting.Application.prototype.onError_ = function(error) { |
+ base.debug.assert(false, "Subclass must override"); |
+}; |
+ |
+ |
/** |
+ * The interface specifies the methods that a subclass of remoting.Application |
+ * is required implement to override the default behavior. |
+ * |
* @interface |
*/ |
-remoting.Application.Delegate = function() {}; |
+remoting.ApplicationInterface = function() {}; |
+ |
+/** |
+ * @return {string} Application product name to be used in UI. |
+ */ |
+remoting.ApplicationInterface.prototype.getApplicationName = function() {}; |
+ |
+/** |
+ * Report an authentication error to the user. This is called in lieu of |
+ * startApplication() if the user cannot be authenticated or if they decline |
+ * the app permissions. |
+ * |
+ * @param {!remoting.Error} error The failure reason. |
+ */ |
+remoting.ApplicationInterface.prototype.signInFailed_ = function(error) {}; |
/** |
* Initialize the application. This is called before an OAuth token is requested |
* and should be used for tasks such as initializing the DOM, registering event |
- * handlers, etc. |
+ * handlers, etc. After this is called, the app is running and waiting for |
+ * user events. |
*/ |
-remoting.Application.Delegate.prototype.init = function() {}; |
+remoting.ApplicationInterface.prototype.initApplication_ = function() {}; |
/** |
- * Start the application. Once start() is called, the delegate can assume that |
+ * Start the application. Once startApplication() is called, we can assume that |
* the user has consented to all permissions specified in the manifest. |
* |
- * @param {remoting.SessionConnector} connector |
- * @param {string} token An OAuth access token. The delegate should not cache |
+ * @param {string} token An OAuth access token. The app should not cache |
* this token, but can assume that it will remain valid during application |
* start-up. |
*/ |
-remoting.Application.Delegate.prototype.start = function(connector, token) {}; |
- |
-/** |
- * Report an authentication error to the user. This is called in lieu of start() |
- * if the user cannot be authenticated. |
- * |
- * @param {!remoting.Error} error The failure reason. |
- */ |
-remoting.Application.Delegate.prototype.signInFailed = function(error) {}; |
+remoting.ApplicationInterface.prototype.startApplication_ = function(token) {}; |
/** |
- * @return {string} Application product name to be used in UI. |
+ * Close down the application before exiting. |
*/ |
-remoting.Application.Delegate.prototype.getApplicationName = function() {}; |
+remoting.ApplicationInterface.prototype.exitApplication_ = function() {}; |
/** |
* Called when a new session has been connected. |
* |
* @param {remoting.ConnectionInfo} connectionInfo |
- * @return {void} Nothing. |
*/ |
-remoting.Application.Delegate.prototype.handleConnected = function( |
- connectionInfo) {}; |
+remoting.ApplicationInterface.prototype.onConnected_ = |
+ function(connectionInfo) {}; |
/** |
* Called when the current session has been disconnected. |
- * |
- * @return {void} Nothing. |
*/ |
-remoting.Application.Delegate.prototype.handleDisconnected = function() {}; |
+remoting.ApplicationInterface.prototype.onDisconnected_ = function() {}; |
/** |
* Called when the current session's connection has failed. |
* |
- * @param {remoting.SessionConnector} connector |
* @param {!remoting.Error} error |
- * @return {void} Nothing. |
*/ |
-remoting.Application.Delegate.prototype.handleConnectionFailed = |
- function(connector, error) {}; |
+remoting.ApplicationInterface.prototype.onConnectionFailed_ = |
+ function(error) {}; |
/** |
* Called when an error needs to be displayed to the user. |
* |
* @param {!remoting.Error} errorTag The error to be localized and displayed. |
- * @return {void} Nothing. |
- */ |
-remoting.Application.Delegate.prototype.handleError = function(errorTag) {}; |
- |
-/** |
- * Perform any application-specific cleanup before exiting. This is called in |
- * lieu of start() if the user declines the app permissions, and will usually |
- * be called immediately prior to exiting, although delegates should not rely |
- * on this. |
*/ |
-remoting.Application.Delegate.prototype.handleExit = function() {}; |
+remoting.ApplicationInterface.prototype.onError_ = function(errorTag) {}; |
/** @type {remoting.Application} */ |