OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
Sergey Ulanov
2015/01/14 19:42:43
2015
| |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 'use strict'; | |
6 | |
7 /** @suppress {duplicate} */ | |
8 var remoting = remoting || {}; | |
9 | |
10 /** | |
11 * A signal strategy encapsulating a primary and a back-up strategy. If the | |
12 * primary fails or times out, then the secondary is used. Information about | |
13 * which strategy was used, and why, is returned via |onProgressCallback|. | |
14 * | |
15 * @param {function( | |
16 * function(remoting.SignalStrategy.State) | |
17 * ):remoting.SignalStrategy} primaryFactory | |
18 * @param {function( | |
19 * function(remoting.SignalStrategy.State) | |
20 * ):remoting.SignalStrategy} secondaryFactory | |
21 * @param {function(remoting.SignalStrategy.State):void} onStateChangedCallback | |
22 * @param {function(remoting.FallbackSignalStrategy.Progress)} | |
23 * onProgressCallback | |
24 * | |
25 * @implements {remoting.SignalStrategy} | |
26 * @constructor | |
27 */ | |
28 remoting.FallbackSignalStrategy = function( | |
29 primaryFactory, secondaryFactory, | |
30 onStateChangedCallback, onProgressCallback) { | |
31 /** | |
32 * @type {remoting.SignalStrategy} | |
33 * @private | |
34 */ | |
35 this.primary_ = primaryFactory(this.onPrimaryStateChanged_.bind(this)); | |
36 | |
37 /** | |
38 * @type {remoting.SignalStrategy} | |
39 * @private | |
40 */ | |
41 this.secondary_ = secondaryFactory(this.onSecondaryStateChanged_.bind(this)); | |
42 | |
43 /** | |
44 * @type {function(remoting.SignalStrategy.State)} | |
45 * @private | |
46 */ | |
47 this.onStateChangedCallback_ = onStateChangedCallback; | |
48 | |
49 /** | |
50 * @type {function(remoting.FallbackSignalStrategy.Progress)} | |
51 * @private | |
52 */ | |
53 this.onProgressCallback_ = onProgressCallback; | |
54 | |
55 /** | |
56 * @type {?function(Element):void} | |
57 * @private | |
58 */ | |
59 this.onIncomingStanzaCallback_ = null; | |
60 | |
61 /** | |
62 * @type {number} | |
63 * @private | |
64 * @const | |
65 */ | |
66 this.PRIMARY_CONNECT_TIMEOUT_MS_ = 10 * 1000; | |
67 | |
68 /** | |
69 * @enum {string} | |
70 * @private | |
71 */ | |
72 this.State = { | |
73 NOT_CONNECTED: 'not-connected', | |
74 PRIMARY_PENDING: 'primary-pending', | |
75 PRIMARY_SUCCEEDED: 'primary-succeeded', | |
76 SECONDARY_PENDING: 'secondary-pending', | |
77 SECONDARY_SUCCEEDED: 'secondary-succeeded', | |
78 SECONDARY_FAILED: 'secondary-failed', | |
79 CLOSED: 'closed' | |
80 }; | |
81 | |
82 /** | |
83 * @type {string} | |
84 * @private | |
85 */ | |
86 this.state_ = this.State.NOT_CONNECTED; | |
87 | |
88 /** | |
89 * @type {?remoting.SignalStrategy.State} | |
90 * @private | |
91 */ | |
92 this.externalState_ = null; | |
93 | |
94 /** | |
95 * @type {string} | |
96 * @private | |
97 */ | |
98 this.server_ = ''; | |
99 | |
100 /** | |
101 * @type {string} | |
102 * @private | |
103 */ | |
104 this.username_ = ''; | |
105 | |
106 /** | |
107 * @type {string} | |
108 * @private | |
109 */ | |
110 this.authToken_ = ''; | |
111 | |
112 /** | |
113 * @type {number} | |
114 * @private | |
115 */ | |
116 this.primaryConnectTimerId_ = 0; | |
117 }; | |
118 | |
119 /** | |
120 * @enum {string} | |
121 */ | |
122 remoting.FallbackSignalStrategy.Progress = { | |
123 PRIMARY_SUCCEEDED: 'primary-succeeded', | |
124 PRIMARY_FAILED: 'primary-failed', | |
125 PRIMARY_TIMED_OUT: 'primary-timed-out', | |
126 PRIMARY_SUCCEEDED_LATE: 'primary-succeeded-late', | |
127 PRIMARY_FAILED_LATE: 'primary-failed-late', | |
128 SECONDARY_SUCCEEDED: 'secondary-succeeded', | |
129 SECONDARY_FAILED: 'secondary-failed' | |
130 }; | |
131 | |
132 remoting.FallbackSignalStrategy.prototype.dispose = function() { | |
133 this.primary_.dispose(); | |
134 this.secondary_.dispose(); | |
135 }; | |
136 | |
137 /** | |
138 * @param {?function(Element):void} onIncomingStanzaCallback Callback to call on | |
139 * incoming messages. | |
140 */ | |
141 remoting.FallbackSignalStrategy.prototype.setIncomingStanzaCallback = | |
142 function(onIncomingStanzaCallback) { | |
143 this.onIncomingStanzaCallback_ = onIncomingStanzaCallback; | |
144 if (this.state_ == this.State.PRIMARY_PENDING || | |
145 this.state_ == this.State.PRIMARY_SUCCEEDED) { | |
146 this.primary_.setIncomingStanzaCallback(onIncomingStanzaCallback); | |
147 } else if (this.state_ == this.State.SECONDARY_PENDING || | |
148 this.state_ == this.State.SECONDARY_SUCCEEDED) { | |
149 this.secondary_.setIncomingStanzaCallback(onIncomingStanzaCallback); | |
150 } | |
151 }; | |
152 | |
153 /** | |
154 * @param {string} server | |
155 * @param {string} username | |
156 * @param {string} authToken | |
157 */ | |
158 remoting.FallbackSignalStrategy.prototype.connect = | |
159 function(server, username, authToken) { | |
160 base.debug.assert(this.state_ == this.State.NOT_CONNECTED); | |
161 this.server_ = server; | |
162 this.username_ = username; | |
163 this.authToken_ = authToken; | |
164 this.state_ = this.State.PRIMARY_PENDING; | |
165 this.primary_.setIncomingStanzaCallback(this.onIncomingStanzaCallback_); | |
166 this.primary_.connect(server, username, authToken); | |
167 this.primaryConnectTimerId_ = | |
168 window.setTimeout(this.onPrimaryTimeout_.bind(this), | |
169 this.PRIMARY_CONNECT_TIMEOUT_MS_); | |
170 }; | |
171 | |
172 /** | |
173 * Sends a message. Can be called only in CONNECTED state. | |
174 * @param {string} message | |
175 */ | |
176 remoting.FallbackSignalStrategy.prototype.sendMessage = function(message) { | |
177 this.getConnectedSignalStrategy_().sendMessage(message); | |
178 }; | |
179 | |
180 /** @return {remoting.SignalStrategy.State} Current state */ | |
181 remoting.FallbackSignalStrategy.prototype.getState = function() { | |
182 return (this.externalState_ === null) | |
183 ? remoting.SignalStrategy.State.NOT_CONNECTED | |
184 : this.externalState_; | |
185 }; | |
186 | |
187 /** @return {remoting.Error} Error when in FAILED state. */ | |
188 remoting.FallbackSignalStrategy.prototype.getError = function() { | |
189 base.debug.assert(this.state_ == this.State.SECONDARY_FAILED); | |
190 base.debug.assert( | |
191 this.secondary_.getState() == remoting.SignalStrategy.State.FAILED); | |
192 return this.secondary_.getError(); | |
193 }; | |
194 | |
195 /** @return {string} Current JID when in CONNECTED state. */ | |
196 remoting.FallbackSignalStrategy.prototype.getJid = function() { | |
197 return this.getConnectedSignalStrategy_().getJid(); | |
198 }; | |
199 | |
200 /** | |
201 * @return {remoting.SignalStrategy} The active signal strategy, if the | |
202 * connection has succeeded. | |
203 * @private | |
204 */ | |
205 remoting.FallbackSignalStrategy.prototype.getConnectedSignalStrategy_ = | |
206 function() { | |
207 if (this.state_ == this.State.PRIMARY_SUCCEEDED) { | |
208 base.debug.assert( | |
209 this.primary_.getState() == remoting.SignalStrategy.State.CONNECTED); | |
210 return this.primary_; | |
211 } else if (this.state_ == this.State.SECONDARY_SUCCEEDED) { | |
212 base.debug.assert( | |
213 this.secondary_.getState() == remoting.SignalStrategy.State.CONNECTED); | |
214 return this.secondary_; | |
215 } else { | |
216 base.debug.assert( | |
217 false, | |
218 'getConnectedSignalStrategy called in unconnected state'); | |
219 return null; | |
220 } | |
221 }; | |
222 | |
223 /** | |
224 * @param {remoting.SignalStrategy.State} state | |
225 * @private | |
226 */ | |
227 remoting.FallbackSignalStrategy.prototype.onPrimaryStateChanged_ = | |
228 function(state) { | |
229 switch (state) { | |
230 case remoting.SignalStrategy.State.CONNECTED: | |
231 if (this.state_ == this.State.PRIMARY_PENDING) { | |
232 window.clearTimeout(this.primaryConnectTimerId_); | |
233 this.onProgressCallback_( | |
234 remoting.FallbackSignalStrategy.Progress.PRIMARY_SUCCEEDED); | |
235 this.state_ = this.State.PRIMARY_SUCCEEDED; | |
236 } else { | |
237 this.onProgressCallback_( | |
238 remoting.FallbackSignalStrategy.Progress.PRIMARY_SUCCEEDED_LATE); | |
239 } | |
240 break; | |
241 | |
242 case remoting.SignalStrategy.State.FAILED: | |
243 if (this.state_ == this.State.PRIMARY_PENDING) { | |
244 window.clearTimeout(this.primaryConnectTimerId_); | |
245 this.onProgressCallback_( | |
246 remoting.FallbackSignalStrategy.Progress.PRIMARY_FAILED); | |
247 this.connectSecondary_(); | |
248 } else { | |
249 this.onProgressCallback_( | |
250 remoting.FallbackSignalStrategy.Progress.PRIMARY_FAILED_LATE); | |
251 } | |
252 return; // Don't notify the external callback | |
253 | |
254 case remoting.SignalStrategy.State.CLOSED: | |
255 this.state_ = this.State.CLOSED; | |
256 break; | |
257 } | |
258 | |
259 this.notifyExternalCallback_(state); | |
260 }; | |
261 | |
262 /** | |
263 * @param {remoting.SignalStrategy.State} state | |
264 * @private | |
265 */ | |
266 remoting.FallbackSignalStrategy.prototype.onSecondaryStateChanged_ = | |
267 function(state) { | |
268 switch (state) { | |
269 case remoting.SignalStrategy.State.CONNECTED: | |
270 this.onProgressCallback_( | |
271 remoting.FallbackSignalStrategy.Progress.SECONDARY_SUCCEEDED); | |
272 this.state_ = this.State.SECONDARY_SUCCEEDED; | |
273 break; | |
274 | |
275 case remoting.SignalStrategy.State.FAILED: | |
276 this.onProgressCallback_( | |
277 remoting.FallbackSignalStrategy.Progress.SECONDARY_FAILED); | |
278 this.state_ = this.State.SECONDARY_FAILED; | |
279 break; | |
280 | |
281 case remoting.SignalStrategy.State.CLOSED: | |
282 this.state_ = this.State.CLOSED; | |
283 break; | |
284 } | |
285 | |
286 this.notifyExternalCallback_(state); | |
287 }; | |
288 | |
289 /** | |
290 * Notify the external callback of a change in state if it's consistent with | |
291 * the allowed state transitions (ie, if it represents a later stage in the | |
292 * connection process). Suppress state transitions that would violate this, | |
293 * for example a CONNECTING -> NOT_CONNECTED transition when we switch from | |
294 * the primary to the secondary signal strategy. | |
295 * | |
296 * @param {remoting.SignalStrategy.State} state | |
297 * @private | |
298 */ | |
299 remoting.FallbackSignalStrategy.prototype.notifyExternalCallback_ = | |
300 function(state) { | |
301 if (this.externalState_ === null || state > this.externalState_) { | |
302 this.externalState_ = state; | |
303 this.onStateChangedCallback_(state); | |
304 } | |
305 }; | |
306 | |
307 /** | |
308 * @private | |
309 */ | |
310 remoting.FallbackSignalStrategy.prototype.connectSecondary_ = function() { | |
311 base.debug.assert(this.state_ == this.State.PRIMARY_PENDING); | |
312 base.debug.assert(this.server_ != ''); | |
313 base.debug.assert(this.username_ != ''); | |
314 base.debug.assert(this.authToken_ != ''); | |
315 | |
316 this.state_ = this.State.SECONDARY_PENDING; | |
317 this.primary_.setIncomingStanzaCallback(null); | |
318 this.secondary_.setIncomingStanzaCallback(this.onIncomingStanzaCallback_); | |
319 this.secondary_.connect(this.server_, this.username_, this.authToken_); | |
320 }; | |
321 | |
322 /** | |
323 * @private | |
324 */ | |
325 remoting.FallbackSignalStrategy.prototype.onPrimaryTimeout_ = function() { | |
326 this.onProgressCallback_( | |
327 remoting.FallbackSignalStrategy.Progress.PRIMARY_TIMED_OUT); | |
328 this.connectSecondary_(); | |
329 }; | |
330 | |
331 /** | |
332 * @return {remoting.SignalStrategy} The primary signal strategy. | |
333 * @private | |
334 */ | |
335 remoting.FallbackSignalStrategy.prototype.getPrimaryStrategyForTesting_ = | |
336 function() { | |
337 return this.primary_; | |
338 }; | |
339 | |
340 /** | |
341 * @return {remoting.SignalStrategy} The primary signal strategy. | |
342 * @private | |
343 */ | |
344 remoting.FallbackSignalStrategy.prototype.getSecondaryStrategyForTesting_ = | |
345 function() { | |
346 return this.secondary_; | |
347 }; | |
OLD | NEW |