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.
7 * Interface abstracting the Application functionality.
12 /** @suppress {duplicate} */
13 var remoting
= remoting
|| {};
16 * @param {Array<string>} appCapabilities Array of application capabilities.
18 * @implements {remoting.ApplicationInterface}
20 remoting
.Application = function(appCapabilities
) {
21 // Create global factories.
22 remoting
.ClientPlugin
.factory
= new remoting
.DefaultClientPluginFactory();
23 remoting
.SessionConnector
.factory
=
24 new remoting
.DefaultSessionConnectorFactory();
26 /** @private {Array<string>} */
27 this.appCapabilities_
= [
28 remoting
.ClientSession
.Capability
.SEND_INITIAL_RESOLUTION
,
29 remoting
.ClientSession
.Capability
.RATE_LIMIT_RESIZE_REQUESTS
,
30 remoting
.ClientSession
.Capability
.VIDEO_RECORDER
32 // Append the app-specific capabilities.
33 this.appCapabilities_
.push
.apply(this.appCapabilities_
, appCapabilities
);
35 /** @protected {remoting.SessionConnector} */
36 this.sessionConnector_
= remoting
.SessionConnector
.factory
.createConnector(
37 document
.getElementById('client-container'),
38 this.onConnected_
.bind(this),
39 this.onError_
.bind(this),
40 this.onConnectionFailed_
.bind(this),
41 this.appCapabilities_
);
43 /** @protected {remoting.Application.Mode} */
44 this.connectionMode_
= remoting
.Application
.Mode
.ME2ME
;
46 /** @private {base.Disposable} */
47 this.sessionConnectedHooks_
= null;
51 * The current application mode (IT2Me, Me2Me or AppRemoting).
53 * TODO(kelvinp): Remove this when Me2Me and It2Me are abstracted into its
58 remoting
.Application
.Mode
= {
65 * @return {remoting.SessionConnector} The session connector.
67 remoting
.Application
.prototype.getSessionConnector = function() {
68 return this.sessionConnector_
;
72 * Get the connection mode (Me2Me, IT2Me or AppRemoting).
74 * @return {remoting.Application.Mode}
76 remoting
.Application
.prototype.getConnectionMode = function() {
77 return this.connectionMode_
;
81 * Set the connection mode (Me2Me, IT2Me or AppRemoting).
83 * @param {remoting.Application.Mode} mode
85 remoting
.Application
.prototype.setConnectionMode = function(mode
) {
86 this.connectionMode_
= mode
;
90 * @param {remoting.ClientSession.Capability} capability
93 remoting
.Application
.prototype.hasCapability = function(capability
) {
94 var capabilities
= this.appCapabilities_
;
95 return capabilities
.indexOf(capability
) != -1;
98 /* Disconnect the remoting client. */
99 remoting
.Application
.prototype.disconnect = function() {
100 if (remoting
.clientSession
) {
101 remoting
.clientSession
.disconnect(remoting
.Error
.none());
102 console
.log('Disconnected.');
106 /* Public method to exit the application. */
107 remoting
.Application
.prototype.quit = function() {
108 this.exitApplication_();
112 * Close the main window when quitting the application. This should be called
113 * by exitApplication() in the subclass.
116 remoting
.Application
.prototype.closeMainWindow_ = function() {
117 chrome
.app
.window
.current().close();
121 * Initialize the application and register all event handlers. After this
122 * is called, the app is running and waiting for user events.
124 remoting
.Application
.prototype.start = function() {
125 // TODO(garykac): This should be owned properly rather than living in the
126 // global 'remoting' namespace.
127 remoting
.settings
= new remoting
.Settings();
129 remoting
.initGlobalObjects();
130 remoting
.initIdentity();
132 this.initApplication_();
135 remoting
.identity
.getToken().
136 then(this.startApplication_
.bind(this)).
137 catch(remoting
.Error
.handler(
138 function(/** !remoting.Error */ error
) {
139 if (error
.hasTag(remoting
.Error
.Tag
.CANCELLED
)) {
140 that
.exitApplication_();
142 that
.signInFailed_(error
);
150 * Called when a new session has been connected.
152 * @param {remoting.ConnectionInfo} connectionInfo
153 * @return {void} Nothing.
156 remoting
.Application
.prototype.initSession_ = function(connectionInfo
) {
157 this.sessionConnectedHooks_
= new base
.Disposables(
158 new base
.EventHook(connectionInfo
.session(), 'stateChanged',
159 this.onSessionFinished_
.bind(this)),
160 new base
.RepeatingTimer(this.updateStatistics_
.bind(this), 1000)
162 remoting
.clipboard
.startSession();
166 * Callback function called when the state of the client plugin changes. The
167 * current and previous states are available via the |state| member variable.
169 * @param {remoting.ClientSession.StateEvent=} state
172 remoting
.Application
.prototype.onSessionFinished_ = function(state
) {
173 switch (state
.current
) {
174 case remoting
.ClientSession
.State
.CLOSED
:
175 console
.log('Connection closed by host');
176 this.onDisconnected_();
178 case remoting
.ClientSession
.State
.FAILED
:
179 var error
= remoting
.clientSession
.getError();
180 console
.error('Client plugin reported connection failed: ' +
182 if (error
=== null) {
183 error
= remoting
.Error
.unexpected();
185 this.onError_(error
);
189 console
.error('Unexpected client plugin state: ' + state
.current
);
190 // This should only happen if the web-app and client plugin get out of
191 // sync, so MISSING_PLUGIN is a suitable error.
192 this.onError_(new remoting
.Error(remoting
.Error
.Tag
.MISSING_PLUGIN
));
196 base
.dispose(this.sessionConnectedHooks_
);
197 this.sessionConnectedHooks_
= null;
198 this.sessionConnector_
.closeSession();
202 remoting
.Application
.prototype.updateStatistics_ = function() {
203 var perfstats
= remoting
.clientSession
.getPerfStats();
204 remoting
.stats
.update(perfstats
);
205 remoting
.clientSession
.logStatistics(perfstats
);
210 * remoting.ApplicationInterface
211 * These functions must be overridden in the subclass.
214 /** @return {string} */
215 remoting
.Application
.prototype.getApplicationName = function() {
216 base
.debug
.assert(false, "Subclass must override");
220 * @param {!remoting.Error} error
223 remoting
.Application
.prototype.signInFailed_ = function(error
) {
224 base
.debug
.assert(false, "Subclass must override");
228 remoting
.Application
.prototype.initApplication_ = function() {
229 base
.debug
.assert(false, "Subclass must override");
233 * @param {string} token
236 remoting
.Application
.prototype.startApplication_ = function(token
) {
237 base
.debug
.assert(false, "Subclass must override");
241 remoting
.Application
.prototype.exitApplication_ = function() {
242 base
.debug
.assert(false, "Subclass must override");
246 * @param {remoting.ConnectionInfo} connectionInfo
249 remoting
.Application
.prototype.onConnected_ = function(connectionInfo
) {
250 base
.debug
.assert(false, "Subclass must override");
254 remoting
.Application
.prototype.onDisconnected_ = function() {
255 base
.debug
.assert(false, "Subclass must override");
259 * @param {!remoting.Error} error
262 remoting
.Application
.prototype.onConnectionFailed_ = function(error
) {
263 base
.debug
.assert(false, "Subclass must override");
267 * @param {!remoting.Error} error The error to be localized and displayed.
270 remoting
.Application
.prototype.onError_ = function(error
) {
271 base
.debug
.assert(false, "Subclass must override");
276 * The interface specifies the methods that a subclass of remoting.Application
277 * is required implement to override the default behavior.
281 remoting
.ApplicationInterface = function() {};
284 * @return {string} Application product name to be used in UI.
286 remoting
.ApplicationInterface
.prototype.getApplicationName = function() {};
289 * Report an authentication error to the user. This is called in lieu of
290 * startApplication() if the user cannot be authenticated or if they decline
291 * the app permissions.
293 * @param {!remoting.Error} error The failure reason.
295 remoting
.ApplicationInterface
.prototype.signInFailed_ = function(error
) {};
298 * Initialize the application. This is called before an OAuth token is requested
299 * and should be used for tasks such as initializing the DOM, registering event
300 * handlers, etc. After this is called, the app is running and waiting for
303 remoting
.ApplicationInterface
.prototype.initApplication_ = function() {};
306 * Start the application. Once startApplication() is called, we can assume that
307 * the user has consented to all permissions specified in the manifest.
309 * @param {string} token An OAuth access token. The app should not cache
310 * this token, but can assume that it will remain valid during application
313 remoting
.ApplicationInterface
.prototype.startApplication_ = function(token
) {};
316 * Close down the application before exiting.
318 remoting
.ApplicationInterface
.prototype.exitApplication_ = function() {};
321 * Called when a new session has been connected.
323 * @param {remoting.ConnectionInfo} connectionInfo
325 remoting
.ApplicationInterface
.prototype.onConnected_
=
326 function(connectionInfo
) {};
329 * Called when the current session has been disconnected.
331 remoting
.ApplicationInterface
.prototype.onDisconnected_ = function() {};
334 * Called when the current session's connection has failed.
336 * @param {!remoting.Error} error
338 remoting
.ApplicationInterface
.prototype.onConnectionFailed_
=
342 * Called when an error needs to be displayed to the user.
344 * @param {!remoting.Error} errorTag The error to be localized and displayed.
346 remoting
.ApplicationInterface
.prototype.onError_ = function(errorTag
) {};
349 /** @type {remoting.Application} */