Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(137)

Side by Side Diff: remoting/webapp/base/js/application.js

Issue 1016373003: [Chromoting] Change Application.Delegate to proper subclass of Application. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Protect many things Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 /** 5 /**
6 * @fileoverview 6 * @fileoverview
7 * Interface abstracting the Application functionality. 7 * Interface abstracting the Application functionality.
8 */ 8 */
9 9
10 'use strict'; 10 'use strict';
11 11
12 /** @suppress {duplicate} */ 12 /** @suppress {duplicate} */
13 var remoting = remoting || {}; 13 var remoting = remoting || {};
14 14
15 /** 15 /**
16 * @param {Array<string>} appCapabilities Array of application capabilities. 16 * @param {Array<string>} appCapabilities Array of application capabilities.
17 * @constructor 17 * @constructor
18 * @implements {remoting.ApplicationInterface}
18 */ 19 */
19 remoting.Application = function(appCapabilities) { 20 remoting.Application = function(appCapabilities) {
20 /** @private {remoting.Application.Delegate} */ 21 // Create global factories.
21 this.delegate_ = null; 22 remoting.ClientPlugin.factory = new remoting.DefaultClientPluginFactory();
23 remoting.SessionConnector.factory =
24 new remoting.DefaultSessionConnectorFactory();
22 25
23 /** @private {Array<string>} */ 26 /** @private {Array<string>} */
24 this.appCapabilities_ = [ 27 this.appCapabilities_ = [
25 remoting.ClientSession.Capability.SEND_INITIAL_RESOLUTION, 28 remoting.ClientSession.Capability.SEND_INITIAL_RESOLUTION,
26 remoting.ClientSession.Capability.RATE_LIMIT_RESIZE_REQUESTS, 29 remoting.ClientSession.Capability.RATE_LIMIT_RESIZE_REQUESTS,
27 remoting.ClientSession.Capability.VIDEO_RECORDER 30 remoting.ClientSession.Capability.VIDEO_RECORDER
28 ]; 31 ];
29 // Append the app-specific capabilities. 32 // Append the app-specific capabilities.
30 this.appCapabilities_.push.apply(this.appCapabilities_, appCapabilities); 33 this.appCapabilities_.push.apply(this.appCapabilities_, appCapabilities);
31 34
32 /** @private {remoting.SessionConnector} */ 35 /** @protected {remoting.SessionConnector} */
33 this.sessionConnector_ = null; 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_);
34 42
35 /** @private {base.Disposable} */ 43 /** @private {base.Disposable} */
36 this.sessionConnectedHooks_ = null; 44 this.sessionConnectedHooks_ = null;
37 }; 45 };
38 46
39 /** 47 /**
40 * @param {remoting.Application.Delegate} appDelegate The delegate that 48 * @return {remoting.SessionConnector} The session connector.
41 * contains the app-specific functionality.
42 */ 49 */
43 remoting.Application.prototype.setDelegate = function(appDelegate) { 50 remoting.Application.prototype.getSessionConnector = function() {
44 this.delegate_ = appDelegate; 51 return this.sessionConnector_;
45 }; 52 };
46 53
47 /** 54 /**
48 * @return {string} Application product name to be used in UI.
49 */
50 remoting.Application.prototype.getApplicationName = function() {
51 return this.delegate_.getApplicationName();
52 };
53
54 /**
55 * @param {remoting.ClientSession.Capability} capability 55 * @param {remoting.ClientSession.Capability} capability
56 * @return {boolean} 56 * @return {boolean}
57 */ 57 */
58 remoting.Application.prototype.hasCapability = function(capability) { 58 remoting.Application.prototype.hasCapability = function(capability) {
59 var capabilities = this.appCapabilities_; 59 var capabilities = this.appCapabilities_;
60 return capabilities.indexOf(capability) != -1; 60 return capabilities.indexOf(capability) != -1;
61 }; 61 };
62 62
63 /* Disconnect the remoting client. */
64 remoting.Application.prototype.disconnect = function() {
65 if (remoting.clientSession) {
66 remoting.clientSession.disconnect(remoting.Error.none());
67 console.log('Disconnected.');
68 }
69 };
70
71 /* Public method to exit the application. */
72 remoting.Application.prototype.quit = function() {
73 this.exitApplication_();
74 };
75
76 /**
77 * Close the main window when quitting the application. This should be called
78 * by exitApplication() in the subclass.
79 * @protected
80 */
81 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
82 chrome.app.window.current().close();
83 };
84
63 /** 85 /**
64 * Initialize the application and register all event handlers. After this 86 * Initialize the application and register all event handlers. After this
65 * is called, the app is running and waiting for user events. 87 * is called, the app is running and waiting for user events.
66 *
67 * @return {void} Nothing.
68 */ 88 */
69 remoting.Application.prototype.start = function() { 89 remoting.Application.prototype.start = function() {
70 // Create global objects.
71 remoting.ClientPlugin.factory = new remoting.DefaultClientPluginFactory();
72 remoting.SessionConnector.factory =
73 new remoting.DefaultSessionConnectorFactory();
74
75 // TODO(garykac): This should be owned properly rather than living in the 90 // TODO(garykac): This should be owned properly rather than living in the
76 // global 'remoting' namespace. 91 // global 'remoting' namespace.
77 remoting.settings = new remoting.Settings(); 92 remoting.settings = new remoting.Settings();
78 93
79 remoting.initGlobalObjects(); 94 remoting.initGlobalObjects();
80 remoting.initIdentity(); 95 remoting.initIdentity();
81 96
82 this.delegate_.init(); 97 this.initApplication_();
83 98
84 var that = this; 99 var that = this;
85 remoting.identity.getToken().then( 100 remoting.identity.getToken().
86 this.delegate_.start.bind(this.delegate_, this.getSessionConnector()) 101 then(this.startApplication_.bind(this)).
87 ).catch(remoting.Error.handler( 102 catch(remoting.Error.handler(
88 function(/** !remoting.Error */ error) { 103 function(/** !remoting.Error */ error) {
89 if (error.hasTag(remoting.Error.Tag.CANCELLED)) { 104 if (error.hasTag(remoting.Error.Tag.CANCELLED)) {
90 that.exit(); 105 that.exitApplication_();
91 } else { 106 } else {
92 that.delegate_.signInFailed(error); 107 that.signInFailed_(error);
93 } 108 }
94 } 109 }
95 ) 110 )
96 ); 111 );
97 }; 112 };
98 113
99 /** 114 /**
100 * Quit the application.
101 */
102 remoting.Application.prototype.exit = function() {
103 this.delegate_.handleExit();
104 chrome.app.window.current().close();
105 };
106
107 /** Disconnect the remoting client. */
108 remoting.Application.prototype.disconnect = function() {
109 if (remoting.clientSession) {
110 remoting.clientSession.disconnect(remoting.Error.none());
111 console.log('Disconnected.');
112 }
113 };
114
115 /**
116 * Called when a new session has been connected. 115 * Called when a new session has been connected.
117 * 116 *
118 * @param {remoting.ConnectionInfo} connectionInfo 117 * @param {remoting.ConnectionInfo} connectionInfo
119 * @return {void} Nothing. 118 * @return {void} Nothing.
119 * @protected
120 */ 120 */
121 remoting.Application.prototype.onConnected = function(connectionInfo) { 121 remoting.Application.prototype.initSession_ = function(connectionInfo) {
122 this.sessionConnectedHooks_ = new base.Disposables( 122 this.sessionConnectedHooks_ = new base.Disposables(
123 new base.EventHook(connectionInfo.session(), 'stateChanged', 123 new base.EventHook(connectionInfo.session(), 'stateChanged',
124 this.onSessionFinished_.bind(this)), 124 this.onSessionFinished_.bind(this)),
125 new base.RepeatingTimer(this.updateStatistics_.bind(this), 1000) 125 new base.RepeatingTimer(this.updateStatistics_.bind(this), 1000)
126 ); 126 );
127 remoting.clipboard.startSession(); 127 remoting.clipboard.startSession();
128
129 this.delegate_.handleConnected(connectionInfo);
130 }; 128 };
131 129
132 /** 130 /**
133 * Called when the current session has been disconnected.
134 *
135 * @return {void} Nothing.
136 */
137 remoting.Application.prototype.onDisconnected = function() {
138 this.delegate_.handleDisconnected();
139 };
140
141 /**
142 * Called when the current session's connection has failed.
143 *
144 * @param {!remoting.Error} error
145 * @return {void} Nothing.
146 */
147 remoting.Application.prototype.onConnectionFailed = function(error) {
148 this.delegate_.handleConnectionFailed(this.sessionConnector_, error);
149 };
150
151 /**
152 * Called when an error needs to be displayed to the user.
153 *
154 * @param {!remoting.Error} errorTag The error to be localized and displayed.
155 * @return {void} Nothing.
156 */
157 remoting.Application.prototype.onError = function(errorTag) {
158 this.delegate_.handleError(errorTag);
159 };
160
161 /**
162 * @return {remoting.SessionConnector} A session connector, creating a new one
163 * if necessary.
164 */
165 remoting.Application.prototype.getSessionConnector = function() {
166 // TODO(garykac): Check if this can be initialized in the ctor.
167 if (!this.sessionConnector_) {
168 this.sessionConnector_ = remoting.SessionConnector.factory.createConnector(
169 document.getElementById('client-container'),
170 this.onConnected.bind(this),
171 this.onError.bind(this),
172 this.onConnectionFailed.bind(this),
173 this.appCapabilities_);
174 }
175 return this.sessionConnector_;
176 };
177
178 /**
179 * Callback function called when the state of the client plugin changes. The 131 * Callback function called when the state of the client plugin changes. The
180 * current and previous states are available via the |state| member variable. 132 * current and previous states are available via the |state| member variable.
181 * 133 *
182 * @param {remoting.ClientSession.StateEvent=} state 134 * @param {remoting.ClientSession.StateEvent=} state
183 * @private 135 * @private
184 */ 136 */
185 remoting.Application.prototype.onSessionFinished_ = function(state) { 137 remoting.Application.prototype.onSessionFinished_ = function(state) {
186 switch (state.current) { 138 switch (state.current) {
187 case remoting.ClientSession.State.CLOSED: 139 case remoting.ClientSession.State.CLOSED:
188 console.log('Connection closed by host'); 140 console.log('Connection closed by host');
189 this.onDisconnected(); 141 this.onDisconnected_();
190 break; 142 break;
191 case remoting.ClientSession.State.FAILED: 143 case remoting.ClientSession.State.FAILED:
192 var error = remoting.clientSession.getError(); 144 var error = remoting.clientSession.getError();
193 console.error('Client plugin reported connection failed: ' + 145 console.error('Client plugin reported connection failed: ' +
194 error.toString()); 146 error.toString());
195 if (error === null) { 147 if (error === null) {
196 error = remoting.Error.unexpected(); 148 error = remoting.Error.unexpected();
197 } 149 }
198 this.onError(error); 150 this.onError_(error);
199 break; 151 break;
200 152
201 default: 153 default:
202 console.error('Unexpected client plugin state: ' + state.current); 154 console.error('Unexpected client plugin state: ' + state.current);
203 // This should only happen if the web-app and client plugin get out of 155 // This should only happen if the web-app and client plugin get out of
204 // sync, so MISSING_PLUGIN is a suitable error. 156 // sync, so MISSING_PLUGIN is a suitable error.
205 this.onError(new remoting.Error(remoting.Error.Tag.MISSING_PLUGIN)); 157 this.onError_(new remoting.Error(remoting.Error.Tag.MISSING_PLUGIN));
206 break; 158 break;
207 } 159 }
208 160
209 base.dispose(this.sessionConnectedHooks_); 161 base.dispose(this.sessionConnectedHooks_);
210 this.sessionConnectedHooks_= null; 162 this.sessionConnectedHooks_= null;
211 this.sessionConnector_.closeSession(); 163 this.sessionConnector_.closeSession();
212 }; 164 };
213 165
214 /** @private */ 166 /** @private */
215 remoting.Application.prototype.updateStatistics_ = function() { 167 remoting.Application.prototype.updateStatistics_ = function() {
216 var perfstats = remoting.clientSession.getPerfStats(); 168 var perfstats = remoting.clientSession.getPerfStats();
217 remoting.stats.update(perfstats); 169 remoting.stats.update(perfstats);
218 remoting.clientSession.logStatistics(perfstats); 170 remoting.clientSession.logStatistics(perfstats);
219 }; 171 };
220 172
221 173
174 /*
175 * remoting.ApplicationInterface
176 * These functions must be overridden in the subclass.
177 */
178
179 /** @return {string} */
180 remoting.Application.prototype.getApplicationName = function() {
181 base.debug.assert(false, "Subclass must override");
182 };
183
184 /**
185 * @param {!remoting.Error} error
186 * @protected
187 */
188 remoting.Application.prototype.signInFailed_ = function(error) {
189 base.debug.assert(false, "Subclass must override");
190 };
191
192 /** @protected */
193 remoting.Application.prototype.initApplication_ = function() {
194 base.debug.assert(false, "Subclass must override");
195 };
196
197 /**
198 * @param {string} token
199 * @protected
200 */
201 remoting.Application.prototype.startApplication_ = function(token) {
202 base.debug.assert(false, "Subclass must override");
203 };
204
205 remoting.Application.prototype.exitApplication_ = function() {
206 base.debug.assert(false, "Subclass must override");
207 };
208
209 /**
210 * @param {remoting.ConnectionInfo} connectionInfo
211 * @protected
212 */
213 remoting.Application.prototype.onConnected_ = function(connectionInfo) {
214 base.debug.assert(false, "Subclass must override");
215 };
216
217 /** @protected */
218 remoting.Application.prototype.onDisconnected_ = function() {
219 base.debug.assert(false, "Subclass must override");
220 };
221
222 /**
223 * @param {!remoting.Error} error
224 * @protected
225 */
226 remoting.Application.prototype.onConnectionFailed_ = function(error) {
227 base.debug.assert(false, "Subclass must override");
228 };
229
230 /**
231 * @param {!remoting.Error} error The error to be localized and displayed.
232 * @protected
233 */
234 remoting.Application.prototype.onError_ = function(error) {
235 base.debug.assert(false, "Subclass must override");
236 };
237
238
222 /** 239 /**
240 * The interface specifies the methods that a subclass of remoting.Application
241 * is required implement to override the default behavior.
242 *
223 * @interface 243 * @interface
224 */ 244 */
225 remoting.Application.Delegate = function() {}; 245 remoting.ApplicationInterface = function() {};
246
247 /**
248 * @return {string} Application product name to be used in UI.
249 */
250 remoting.ApplicationInterface.prototype.getApplicationName = function() {};
251
252 /**
253 * Report an authentication error to the user. This is called in lieu of
254 * startApplication() if the user cannot be authenticated or if they decline
255 * the app permissions.
256 *
257 * @param {!remoting.Error} error The failure reason.
258 */
259 remoting.ApplicationInterface.prototype.signInFailed_ = function(error) {};
226 260
227 /** 261 /**
228 * Initialize the application. This is called before an OAuth token is requested 262 * Initialize the application. This is called before an OAuth token is requested
229 * and should be used for tasks such as initializing the DOM, registering event 263 * and should be used for tasks such as initializing the DOM, registering event
230 * handlers, etc. 264 * handlers, etc. After this is called, the app is running and waiting for
265 * user events.
231 */ 266 */
232 remoting.Application.Delegate.prototype.init = function() {}; 267 remoting.ApplicationInterface.prototype.initApplication_ = function() {};
233 268
234 /** 269 /**
235 * Start the application. Once start() is called, the delegate can assume that 270 * Start the application. Once startApplication() is called, we can assume that
236 * the user has consented to all permissions specified in the manifest. 271 * the user has consented to all permissions specified in the manifest.
237 * 272 *
238 * @param {remoting.SessionConnector} connector 273 * @param {string} token An OAuth access token. The app should not cache
239 * @param {string} token An OAuth access token. The delegate should not cache
240 * this token, but can assume that it will remain valid during application 274 * this token, but can assume that it will remain valid during application
241 * start-up. 275 * start-up.
242 */ 276 */
243 remoting.Application.Delegate.prototype.start = function(connector, token) {}; 277 remoting.ApplicationInterface.prototype.startApplication_ = function(token) {};
244 278
245 /** 279 /**
246 * Report an authentication error to the user. This is called in lieu of start() 280 * Close down the application before exiting.
247 * if the user cannot be authenticated.
248 *
249 * @param {!remoting.Error} error The failure reason.
250 */ 281 */
251 remoting.Application.Delegate.prototype.signInFailed = function(error) {}; 282 remoting.ApplicationInterface.prototype.exitApplication_ = function() {};
252
253 /**
254 * @return {string} Application product name to be used in UI.
255 */
256 remoting.Application.Delegate.prototype.getApplicationName = function() {};
257 283
258 /** 284 /**
259 * Called when a new session has been connected. 285 * Called when a new session has been connected.
260 * 286 *
261 * @param {remoting.ConnectionInfo} connectionInfo 287 * @param {remoting.ConnectionInfo} connectionInfo
262 * @return {void} Nothing.
263 */ 288 */
264 remoting.Application.Delegate.prototype.handleConnected = function( 289 remoting.ApplicationInterface.prototype.onConnected_ =
265 connectionInfo) {}; 290 function(connectionInfo) {};
266 291
267 /** 292 /**
268 * Called when the current session has been disconnected. 293 * Called when the current session has been disconnected.
269 *
270 * @return {void} Nothing.
271 */ 294 */
272 remoting.Application.Delegate.prototype.handleDisconnected = function() {}; 295 remoting.ApplicationInterface.prototype.onDisconnected_ = function() {};
273 296
274 /** 297 /**
275 * Called when the current session's connection has failed. 298 * Called when the current session's connection has failed.
276 * 299 *
277 * @param {remoting.SessionConnector} connector
278 * @param {!remoting.Error} error 300 * @param {!remoting.Error} error
279 * @return {void} Nothing.
280 */ 301 */
281 remoting.Application.Delegate.prototype.handleConnectionFailed = 302 remoting.ApplicationInterface.prototype.onConnectionFailed_ =
282 function(connector, error) {}; 303 function(error) {};
283 304
284 /** 305 /**
285 * Called when an error needs to be displayed to the user. 306 * Called when an error needs to be displayed to the user.
286 * 307 *
287 * @param {!remoting.Error} errorTag The error to be localized and displayed. 308 * @param {!remoting.Error} errorTag The error to be localized and displayed.
288 * @return {void} Nothing.
289 */ 309 */
290 remoting.Application.Delegate.prototype.handleError = function(errorTag) {}; 310 remoting.ApplicationInterface.prototype.onError_ = function(errorTag) {};
291
292 /**
293 * Perform any application-specific cleanup before exiting. This is called in
294 * lieu of start() if the user declines the app permissions, and will usually
295 * be called immediately prior to exiting, although delegates should not rely
296 * on this.
297 */
298 remoting.Application.Delegate.prototype.handleExit = function() {};
299 311
300 312
301 /** @type {remoting.Application} */ 313 /** @type {remoting.Application} */
302 remoting.app = null; 314 remoting.app = null;
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698