OLD | NEW |
---|---|
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 * The interface specifies the methods that a subclass of remoting.Application | |
17 * is required implement to override the default behavior. | |
18 * | |
19 * @interface | |
20 */ | |
21 remoting.ApplicationInterface = function() {}; | |
22 | |
23 /** | |
24 * @return {string} Application product name to be used in UI. | |
25 */ | |
26 remoting.ApplicationInterface.prototype.getApplicationName = function() {}; | |
27 | |
28 /** | |
29 * Report an authentication error to the user. This is called in lieu of | |
30 * startApplication() if the user cannot be authenticated or if they decline | |
31 * the app permissions. | |
32 * | |
33 * @param {!remoting.Error} error The failure reason. | |
34 */ | |
35 remoting.ApplicationInterface.prototype.signInFailed = function(error) {}; | |
36 | |
37 /** | |
38 * Initialize the application. This is called before an OAuth token is requested | |
39 * and should be used for tasks such as initializing the DOM, registering event | |
40 * handlers, etc. After this is called, the app is running and waiting for | |
41 * user events. | |
42 * | |
43 * @return {void} Nothing. | |
44 */ | |
45 remoting.ApplicationInterface.prototype.initApplication = function() {}; | |
46 | |
47 /** | |
48 * Start the application. Once startApplication() is called, we can assume that | |
49 * the user has consented to all permissions specified in the manifest. | |
50 * | |
51 * @param {string} token An OAuth access token. The app should not cache | |
52 * this token, but can assume that it will remain valid during application | |
53 * start-up. | |
54 */ | |
55 remoting.ApplicationInterface.prototype.startApplication = function(token) {}; | |
56 | |
57 | |
58 /** | |
16 * @param {Array<string>} appCapabilities Array of application capabilities. | 59 * @param {Array<string>} appCapabilities Array of application capabilities. |
17 * @constructor | 60 * @constructor |
61 * @implements {remoting.ApplicationInterface} | |
18 */ | 62 */ |
19 remoting.Application = function(appCapabilities) { | 63 remoting.Application = function(appCapabilities) { |
20 /** @private {remoting.Application.Delegate} */ | 64 // Create global objects. |
21 this.delegate_ = null; | 65 remoting.ClientPlugin.factory = new remoting.DefaultClientPluginFactory(); |
66 remoting.SessionConnector.factory = | |
67 new remoting.DefaultSessionConnectorFactory(); | |
garykac
2015/03/23 18:44:22
Moved here from start() since this is required to
Jamie
2015/03/25 20:00:39
The purpose of these factories is to allow test co
garykac
2015/03/26 01:41:57
Previously, they were setup in the start() functio
| |
22 | 68 |
23 /** @private {Array<string>} */ | 69 /** @private {Array<string>} */ |
24 this.appCapabilities_ = [ | 70 this.appCapabilities_ = [ |
25 remoting.ClientSession.Capability.SEND_INITIAL_RESOLUTION, | 71 remoting.ClientSession.Capability.SEND_INITIAL_RESOLUTION, |
26 remoting.ClientSession.Capability.RATE_LIMIT_RESIZE_REQUESTS, | 72 remoting.ClientSession.Capability.RATE_LIMIT_RESIZE_REQUESTS, |
27 remoting.ClientSession.Capability.VIDEO_RECORDER | 73 remoting.ClientSession.Capability.VIDEO_RECORDER |
28 ]; | 74 ]; |
29 // Append the app-specific capabilities. | 75 // Append the app-specific capabilities. |
30 this.appCapabilities_.push.apply(this.appCapabilities_, appCapabilities); | 76 this.appCapabilities_.push.apply(this.appCapabilities_, appCapabilities); |
31 | 77 |
32 /** @private {remoting.SessionConnector} */ | 78 /** @protected {remoting.SessionConnector} */ |
33 this.sessionConnector_ = null; | 79 this.sessionConnector_ = remoting.SessionConnector.factory.createConnector( |
garykac
2015/03/23 18:44:22
Moved here from getter.
| |
80 document.getElementById('client-container'), | |
81 this.onConnected.bind(this), | |
82 this.onError.bind(this), | |
83 this.onConnectionFailed.bind(this), | |
84 this.appCapabilities_); | |
34 | 85 |
35 /** @private {base.Disposable} */ | 86 /** @private {base.Disposable} */ |
36 this.sessionConnectedHooks_ = null; | 87 this.sessionConnectedHooks_ = null; |
37 }; | 88 }; |
38 | 89 |
39 /** | 90 /** |
40 * @param {remoting.Application.Delegate} appDelegate The delegate that | 91 * @return {remoting.SessionConnector} The session connector. |
41 * contains the app-specific functionality. | |
42 */ | 92 */ |
43 remoting.Application.prototype.setDelegate = function(appDelegate) { | 93 remoting.Application.prototype.getSessionConnector = function() { |
44 this.delegate_ = appDelegate; | 94 return this.sessionConnector_; |
45 }; | 95 }; |
46 | 96 |
47 /** | 97 /** |
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 | 98 * @param {remoting.ClientSession.Capability} capability |
56 * @return {boolean} | 99 * @return {boolean} |
57 */ | 100 */ |
58 remoting.Application.prototype.hasCapability = function(capability) { | 101 remoting.Application.prototype.hasCapability = function(capability) { |
59 var capabilities = this.appCapabilities_; | 102 var capabilities = this.appCapabilities_; |
60 return capabilities.indexOf(capability) != -1; | 103 return capabilities.indexOf(capability) != -1; |
61 }; | 104 }; |
62 | 105 |
106 | |
107 /** @return {string} */ | |
Jamie
2015/03/24 21:57:05
AFAICT, these functions are essentially boilerplat
garykac
2015/03/26 01:41:57
Done.
| |
108 remoting.Application.prototype.getApplicationName = function() { | |
109 throw "Subclass must override"; | |
Jamie
2015/03/24 21:57:05
base.assert would be more appropriate here.
garykac
2015/03/26 01:41:57
Done.
| |
110 }; | |
111 | |
112 /** @param {!remoting.Error} error */ | |
113 remoting.Application.prototype.signInFailed = function(error) { | |
114 throw "Subclass must override"; | |
115 }; | |
116 | |
117 remoting.Application.prototype.initApplication = function() { | |
118 throw "Subclass must override"; | |
119 }; | |
120 | |
121 /** @param {string} token */ | |
122 remoting.Application.prototype.startApplication = function(token) { | |
123 throw "Subclass must override"; | |
124 }; | |
125 | |
126 | |
63 /** | 127 /** |
64 * Initialize the application and register all event handlers. After this | 128 * Initialize the application and register all event handlers. After this |
65 * is called, the app is running and waiting for user events. | 129 * is called, the app is running and waiting for user events. |
66 * | 130 * |
67 * @return {void} Nothing. | 131 * @return {void} Nothing. |
68 */ | 132 */ |
69 remoting.Application.prototype.start = function() { | 133 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 | 134 // TODO(garykac): This should be owned properly rather than living in the |
76 // global 'remoting' namespace. | 135 // global 'remoting' namespace. |
77 remoting.settings = new remoting.Settings(); | 136 remoting.settings = new remoting.Settings(); |
78 | 137 |
79 remoting.initGlobalObjects(); | 138 remoting.initGlobalObjects(); |
80 remoting.initIdentity(); | 139 remoting.initIdentity(); |
81 | 140 |
82 this.delegate_.init(); | 141 this.initApplication(); |
83 | 142 |
84 var that = this; | 143 var that = this; |
85 remoting.identity.getToken().then( | 144 remoting.identity.getToken(). |
86 this.delegate_.start.bind(this.delegate_, this.getSessionConnector()) | 145 then(this.startApplication.bind(this)). |
87 ).catch(remoting.Error.handler( | 146 catch(remoting.Error.handler( |
88 function(/** !remoting.Error */ error) { | 147 function(/** !remoting.Error */ error) { |
89 if (error.hasTag(remoting.Error.Tag.CANCELLED)) { | 148 if (error.hasTag(remoting.Error.Tag.CANCELLED)) { |
90 that.exit(); | 149 that.exit(); |
91 } else { | 150 } else { |
92 that.delegate_.signInFailed(error); | 151 that.signInFailed(error); |
93 } | 152 } |
94 } | 153 } |
95 ) | 154 ) |
96 ); | 155 ); |
97 }; | 156 }; |
98 | 157 |
99 /** | 158 /** |
100 * Quit the application. | 159 * Quit the application. |
101 */ | 160 */ |
102 remoting.Application.prototype.exit = function() { | 161 remoting.Application.prototype.exit = function() { |
103 this.delegate_.handleExit(); | |
Jamie
2015/03/24 21:57:05
I'm concerned about a loss of readability here. Wi
Jamie
2015/03/25 20:00:39
As discussed off-line, please refactor so that all
garykac
2015/03/26 01:41:57
Done.
| |
104 chrome.app.window.current().close(); | 162 chrome.app.window.current().close(); |
105 }; | 163 }; |
106 | 164 |
107 /** Disconnect the remoting client. */ | 165 /** Disconnect the remoting client. */ |
108 remoting.Application.prototype.disconnect = function() { | 166 remoting.Application.prototype.disconnect = function() { |
109 if (remoting.clientSession) { | 167 if (remoting.clientSession) { |
110 remoting.clientSession.disconnect(remoting.Error.none()); | 168 remoting.clientSession.disconnect(remoting.Error.none()); |
111 console.log('Disconnected.'); | 169 console.log('Disconnected.'); |
112 } | 170 } |
113 }; | 171 }; |
114 | 172 |
115 /** | |
116 * Called when a new session has been connected. | |
117 * | |
118 * @param {remoting.ConnectionInfo} connectionInfo | |
119 * @return {void} Nothing. | |
120 */ | |
121 remoting.Application.prototype.onConnected = function(connectionInfo) { | |
garykac
2015/03/23 18:44:22
This has been moved a bit further down so that it
| |
122 this.sessionConnectedHooks_ = new base.Disposables( | |
123 new base.EventHook(connectionInfo.session(), 'stateChanged', | |
124 this.onSessionFinished_.bind(this)), | |
125 new base.RepeatingTimer(this.updateStatistics_.bind(this), 1000) | |
126 ); | |
127 remoting.clipboard.startSession(); | |
128 | |
129 this.delegate_.handleConnected(connectionInfo); | |
130 }; | |
131 | 173 |
132 /** | 174 /** |
133 * Called when the current session has been disconnected. | 175 * Called when the current session has been disconnected. |
134 * | 176 * |
135 * @return {void} Nothing. | 177 * @return {void} Nothing. |
136 */ | 178 */ |
137 remoting.Application.prototype.onDisconnected = function() { | 179 remoting.Application.prototype.onDisconnected = function() { |
Jamie
2015/03/25 20:00:39
Under the new "pure-virtual or final" model, I thi
garykac
2015/03/26 01:41:57
All of them except getApplicationName are now @pro
| |
138 this.delegate_.handleDisconnected(); | |
garykac
2015/03/23 18:44:22
These redirects are no longer needed. Subclass wil
| |
139 }; | 180 }; |
140 | 181 |
141 /** | 182 /** |
142 * Called when the current session's connection has failed. | 183 * Called when the current session's connection has failed. |
143 * | 184 * |
144 * @param {!remoting.Error} error | 185 * @param {!remoting.Error} error |
145 * @return {void} Nothing. | 186 * @return {void} Nothing. |
146 */ | 187 */ |
147 remoting.Application.prototype.onConnectionFailed = function(error) { | 188 remoting.Application.prototype.onConnectionFailed = function(error) { |
148 this.delegate_.handleConnectionFailed(this.sessionConnector_, error); | |
149 }; | 189 }; |
150 | 190 |
151 /** | 191 /** |
152 * Called when an error needs to be displayed to the user. | 192 * Called when an error needs to be displayed to the user. |
153 * | 193 * |
154 * @param {!remoting.Error} errorTag The error to be localized and displayed. | 194 * @param {!remoting.Error} errorTag The error to be localized and displayed. |
155 * @return {void} Nothing. | 195 * @return {void} Nothing. |
156 */ | 196 */ |
157 remoting.Application.prototype.onError = function(errorTag) { | 197 remoting.Application.prototype.onError = function(errorTag) { |
158 this.delegate_.handleError(errorTag); | |
159 }; | 198 }; |
160 | 199 |
161 /** | 200 /** |
162 * @return {remoting.SessionConnector} A session connector, creating a new one | 201 * Called when a new session has been connected. |
163 * if necessary. | 202 * |
203 * @param {remoting.ConnectionInfo} connectionInfo | |
204 * @return {void} Nothing. | |
164 */ | 205 */ |
165 remoting.Application.prototype.getSessionConnector = function() { | 206 remoting.Application.prototype.onConnected = function(connectionInfo) { |
166 // TODO(garykac): Check if this can be initialized in the ctor. | 207 this.sessionConnectedHooks_ = new base.Disposables( |
garykac
2015/03/23 18:44:22
Recent changes make this easy to move into the con
| |
167 if (!this.sessionConnector_) { | 208 new base.EventHook(connectionInfo.session(), 'stateChanged', |
168 this.sessionConnector_ = remoting.SessionConnector.factory.createConnector( | 209 this.onSessionFinished_.bind(this)), |
169 document.getElementById('client-container'), | 210 new base.RepeatingTimer(this.updateStatistics_.bind(this), 1000) |
170 this.onConnected.bind(this), | 211 ); |
171 this.onError.bind(this), | 212 remoting.clipboard.startSession(); |
172 this.onConnectionFailed.bind(this), | |
173 this.appCapabilities_); | |
174 } | |
175 return this.sessionConnector_; | |
176 }; | 213 }; |
177 | 214 |
178 /** | 215 /** |
179 * Callback function called when the state of the client plugin changes. The | 216 * Callback function called when the state of the client plugin changes. The |
180 * current and previous states are available via the |state| member variable. | 217 * current and previous states are available via the |state| member variable. |
181 * | 218 * |
182 * @param {remoting.ClientSession.StateEvent=} state | 219 * @param {remoting.ClientSession.StateEvent=} state |
183 * @private | 220 * @private |
184 */ | 221 */ |
185 remoting.Application.prototype.onSessionFinished_ = function(state) { | 222 remoting.Application.prototype.onSessionFinished_ = function(state) { |
(...skipping 26 matching lines...) Expand all Loading... | |
212 }; | 249 }; |
213 | 250 |
214 /** @private */ | 251 /** @private */ |
215 remoting.Application.prototype.updateStatistics_ = function() { | 252 remoting.Application.prototype.updateStatistics_ = function() { |
216 var perfstats = remoting.clientSession.getPerfStats(); | 253 var perfstats = remoting.clientSession.getPerfStats(); |
217 remoting.stats.update(perfstats); | 254 remoting.stats.update(perfstats); |
218 remoting.clientSession.logStatistics(perfstats); | 255 remoting.clientSession.logStatistics(perfstats); |
219 }; | 256 }; |
220 | 257 |
221 | 258 |
222 /** | |
223 * @interface | |
224 */ | |
225 remoting.Application.Delegate = function() {}; | |
226 | |
227 /** | |
228 * 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 | |
230 * handlers, etc. | |
231 */ | |
232 remoting.Application.Delegate.prototype.init = function() {}; | |
233 | |
234 /** | |
235 * Start the application. Once start() is called, the delegate can assume that | |
236 * the user has consented to all permissions specified in the manifest. | |
237 * | |
238 * @param {remoting.SessionConnector} connector | |
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 | |
241 * start-up. | |
242 */ | |
243 remoting.Application.Delegate.prototype.start = function(connector, token) {}; | |
244 | |
245 /** | |
246 * Report an authentication error to the user. This is called in lieu of start() | |
247 * if the user cannot be authenticated. | |
248 * | |
249 * @param {!remoting.Error} error The failure reason. | |
250 */ | |
251 remoting.Application.Delegate.prototype.signInFailed = function(error) {}; | |
252 | |
253 /** | |
254 * @return {string} Application product name to be used in UI. | |
255 */ | |
256 remoting.Application.Delegate.prototype.getApplicationName = function() {}; | |
257 | |
258 /** | |
259 * Called when a new session has been connected. | |
260 * | |
261 * @param {remoting.ConnectionInfo} connectionInfo | |
262 * @return {void} Nothing. | |
263 */ | |
264 remoting.Application.Delegate.prototype.handleConnected = function( | |
265 connectionInfo) {}; | |
266 | |
267 /** | |
268 * Called when the current session has been disconnected. | |
269 * | |
270 * @return {void} Nothing. | |
271 */ | |
272 remoting.Application.Delegate.prototype.handleDisconnected = function() {}; | |
273 | |
274 /** | |
275 * Called when the current session's connection has failed. | |
276 * | |
277 * @param {remoting.SessionConnector} connector | |
278 * @param {!remoting.Error} error | |
279 * @return {void} Nothing. | |
280 */ | |
281 remoting.Application.Delegate.prototype.handleConnectionFailed = | |
282 function(connector, error) {}; | |
283 | |
284 /** | |
285 * Called when an error needs to be displayed to the user. | |
286 * | |
287 * @param {!remoting.Error} errorTag The error to be localized and displayed. | |
288 * @return {void} Nothing. | |
289 */ | |
290 remoting.Application.Delegate.prototype.handleError = 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 | |
300 | |
301 /** @type {remoting.Application} */ | 259 /** @type {remoting.Application} */ |
302 remoting.app = null; | 260 remoting.app = null; |
OLD | NEW |