OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 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. | |
4 | |
5 /** | |
6 * @fileoverview | |
7 * Class that wraps low-level details of interacting with the client plugin. | |
8 * | |
9 * This abstracts a <embed> element and controls the plugin which does | |
10 * the actual remoting work. It also handles differences between | |
11 * client plugins versions when it is necessary. | |
12 */ | |
13 | |
14 'use strict'; | |
15 | |
16 /** @suppress {duplicate} */ | |
17 var remoting = remoting || {}; | |
18 | |
19 /** | |
20 * @param {remoting.ViewerPlugin} plugin The plugin embed element. | |
21 * @constructor | |
22 * @implements {remoting.ClientPlugin} | |
23 */ | |
24 remoting.ClientPluginAsync = function(plugin) { | |
25 this.plugin = plugin; | |
26 | |
27 this.desktopWidth = 0; | |
28 this.desktopHeight = 0; | |
29 this.desktopXDpi = 96; | |
30 this.desktopYDpi = 96; | |
31 | |
32 /** @param {string} iq The Iq stanza received from the host. */ | |
33 this.onOutgoingIqHandler = function (iq) {}; | |
34 /** @param {string} message Log message. */ | |
35 this.onDebugMessageHandler = function (message) {}; | |
36 /** | |
37 * @param {number} state The connection state. | |
38 * @param {number} error The error code, if any. | |
39 */ | |
40 this.onConnectionStatusUpdateHandler = function(state, error) {}; | |
41 /** @param {boolean} ready Connection ready state. */ | |
42 this.onConnectionReadyHandler = function(ready) {}; | |
43 /** | |
44 * @param {string} tokenUrl Token-request URL, received from the host. | |
45 * @param {string} hostPublicKey Public key for the host. | |
46 * @param {string} scope OAuth scope to request the token for. | |
47 */ | |
48 this.fetchThirdPartyTokenHandler = function( | |
49 tokenUrl, hostPublicKey, scope) {}; | |
50 this.onDesktopSizeUpdateHandler = function () {}; | |
51 /** @param {!Array.<string>} capabilities The negotiated capabilities. */ | |
52 this.onSetCapabilitiesHandler = function (capabilities) {}; | |
53 this.fetchPinHandler = function (supportsPairing) {}; | |
54 | |
55 /** @type {number} */ | |
56 this.pluginApiVersion_ = -1; | |
57 /** @type {Array.<string>} */ | |
58 this.pluginApiFeatures_ = []; | |
59 /** @type {number} */ | |
60 this.pluginApiMinVersion_ = -1; | |
61 /** @type {!Array.<string>} */ | |
62 this.capabilities_ = []; | |
63 /** @type {boolean} */ | |
64 this.helloReceived_ = false; | |
65 /** @type {function(boolean)|null} */ | |
66 this.onInitializedCallback_ = null; | |
67 /** @type {function(string, string):void} */ | |
68 this.onPairingComplete_ = function(clientId, sharedSecret) {}; | |
69 /** @type {remoting.ClientSession.PerfStats} */ | |
70 this.perfStats_ = new remoting.ClientSession.PerfStats(); | |
71 | |
72 /** @type {remoting.ClientPluginAsync} */ | |
73 var that = this; | |
74 /** @param {Event} event Message event from the plugin. */ | |
75 this.plugin.addEventListener('message', function(event) { | |
76 that.handleMessage_(event.data); | |
77 }, false); | |
78 window.setTimeout(this.showPluginForClickToPlay_.bind(this), 500); | |
79 }; | |
80 | |
81 /** | |
82 * Chromoting session API version (for this javascript). | |
83 * This is compared with the plugin API version to verify that they are | |
84 * compatible. | |
85 * | |
86 * @const | |
87 * @private | |
88 */ | |
89 remoting.ClientPluginAsync.prototype.API_VERSION_ = 6; | |
90 | |
91 /** | |
92 * The oldest API version that we support. | |
93 * This will differ from the |API_VERSION_| if we maintain backward | |
94 * compatibility with older API versions. | |
95 * | |
96 * @const | |
97 * @private | |
98 */ | |
99 remoting.ClientPluginAsync.prototype.API_MIN_VERSION_ = 5; | |
100 | |
101 /** | |
102 * @param {string} messageStr Message from the plugin. | |
103 */ | |
104 remoting.ClientPluginAsync.prototype.handleMessage_ = function(messageStr) { | |
105 var message = /** @type {{method:string, data:Object.<string,string>}} */ | |
106 jsonParseSafe(messageStr); | |
107 | |
108 if (!message || !('method' in message) || !('data' in message)) { | |
109 console.error('Received invalid message from the plugin: ' + messageStr); | |
110 return; | |
111 } | |
112 | |
113 /** | |
114 * Splits a string into a list of words delimited by spaces. | |
115 * @param {string} str String that should be split. | |
116 * @return {!Array.<string>} List of words. | |
117 */ | |
118 var tokenize = function(str) { | |
119 /** @type {Array.<string>} */ | |
120 var tokens = str.match(/\S+/g); | |
121 return tokens ? tokens : []; | |
122 }; | |
123 | |
124 if (message.method == 'hello') { | |
125 // Reset the size in case we had to enlarge it to support click-to-play. | |
126 this.plugin.width = 0; | |
127 this.plugin.height = 0; | |
128 if (typeof message.data['apiVersion'] != 'number' || | |
129 typeof message.data['apiMinVersion'] != 'number') { | |
130 console.error('Received invalid hello message: ' + messageStr); | |
131 return; | |
132 } | |
133 this.pluginApiVersion_ = /** @type {number} */ message.data['apiVersion']; | |
134 | |
135 if (this.pluginApiVersion_ >= 7) { | |
136 if (typeof message.data['apiFeatures'] != 'string') { | |
137 console.error('Received invalid hello message: ' + messageStr); | |
138 return; | |
139 } | |
140 this.pluginApiFeatures_ = | |
141 /** @type {Array.<string>} */ tokenize(message.data['apiFeatures']); | |
142 | |
143 // Negotiate capabilities. | |
144 | |
145 /** @type {!Array.<string>} */ | |
146 var requestedCapabilities = []; | |
147 if ('requestedCapabilities' in message.data) { | |
148 if (typeof message.data['requestedCapabilities'] != 'string') { | |
149 console.error('Received invalid hello message: ' + messageStr); | |
150 return; | |
151 } | |
152 requestedCapabilities = tokenize(message.data['requestedCapabilities']); | |
153 } | |
154 | |
155 /** @type {!Array.<string>} */ | |
156 var supportedCapabilities = []; | |
157 if ('supportedCapabilities' in message.data) { | |
158 if (typeof message.data['supportedCapabilities'] != 'string') { | |
159 console.error('Received invalid hello message: ' + messageStr); | |
160 return; | |
161 } | |
162 supportedCapabilities = tokenize(message.data['supportedCapabilities']); | |
163 } | |
164 | |
165 // At the moment the webapp does not recognize any of | |
166 // 'requestedCapabilities' capabilities (so they all should be disabled) | |
167 // and do not care about any of 'supportedCapabilities' capabilities (so | |
168 // they all can be enabled). | |
169 this.capabilities_ = supportedCapabilities; | |
170 | |
171 // Let the host know that the webapp can be requested to always send | |
172 // the client's dimensions. | |
173 this.capabilities_.push( | |
174 remoting.ClientSession.Capability.SEND_INITIAL_RESOLUTION); | |
175 | |
176 // Let the host know that we're interested in knowing whether or not | |
177 // it rate-limits desktop-resize requests. | |
178 this.capabilities_.push( | |
179 remoting.ClientSession.Capability.RATE_LIMIT_RESIZE_REQUESTS); | |
180 } else if (this.pluginApiVersion_ >= 6) { | |
181 this.pluginApiFeatures_ = ['highQualityScaling', 'injectKeyEvent']; | |
182 } else { | |
183 this.pluginApiFeatures_ = ['highQualityScaling']; | |
184 } | |
185 this.pluginApiMinVersion_ = | |
186 /** @type {number} */ message.data['apiMinVersion']; | |
187 this.helloReceived_ = true; | |
188 if (this.onInitializedCallback_ != null) { | |
189 this.onInitializedCallback_(true); | |
190 this.onInitializedCallback_ = null; | |
191 } | |
192 } else if (message.method == 'sendOutgoingIq') { | |
193 if (typeof message.data['iq'] != 'string') { | |
194 console.error('Received invalid sendOutgoingIq message: ' + messageStr); | |
195 return; | |
196 } | |
197 this.onOutgoingIqHandler(message.data['iq']); | |
198 } else if (message.method == 'logDebugMessage') { | |
199 if (typeof message.data['message'] != 'string') { | |
200 console.error('Received invalid logDebugMessage message: ' + messageStr); | |
201 return; | |
202 } | |
203 this.onDebugMessageHandler(message.data['message']); | |
204 } else if (message.method == 'onConnectionStatus') { | |
205 if (typeof message.data['state'] != 'string' || | |
206 !remoting.ClientSession.State.hasOwnProperty(message.data['state']) || | |
207 typeof message.data['error'] != 'string') { | |
208 console.error('Received invalid onConnectionState message: ' + | |
209 messageStr); | |
210 return; | |
211 } | |
212 | |
213 /** @type {remoting.ClientSession.State} */ | |
214 var state = remoting.ClientSession.State[message.data['state']]; | |
215 var error; | |
216 if (remoting.ClientSession.ConnectionError.hasOwnProperty( | |
217 message.data['error'])) { | |
218 error = /** @type {remoting.ClientSession.ConnectionError} */ | |
219 remoting.ClientSession.ConnectionError[message.data['error']]; | |
220 } else { | |
221 error = remoting.ClientSession.ConnectionError.UNKNOWN; | |
222 } | |
223 | |
224 this.onConnectionStatusUpdateHandler(state, error); | |
225 } else if (message.method == 'onDesktopSize') { | |
226 if (typeof message.data['width'] != 'number' || | |
227 typeof message.data['height'] != 'number') { | |
228 console.error('Received invalid onDesktopSize message: ' + messageStr); | |
229 return; | |
230 } | |
231 this.desktopWidth = /** @type {number} */ message.data['width']; | |
232 this.desktopHeight = /** @type {number} */ message.data['height']; | |
233 this.desktopXDpi = (typeof message.data['x_dpi'] == 'number') ? | |
234 /** @type {number} */ (message.data['x_dpi']) : 96; | |
235 this.desktopYDpi = (typeof message.data['y_dpi'] == 'number') ? | |
236 /** @type {number} */ (message.data['y_dpi']) : 96; | |
237 this.onDesktopSizeUpdateHandler(); | |
238 } else if (message.method == 'onPerfStats') { | |
239 if (typeof message.data['videoBandwidth'] != 'number' || | |
240 typeof message.data['videoFrameRate'] != 'number' || | |
241 typeof message.data['captureLatency'] != 'number' || | |
242 typeof message.data['encodeLatency'] != 'number' || | |
243 typeof message.data['decodeLatency'] != 'number' || | |
244 typeof message.data['renderLatency'] != 'number' || | |
245 typeof message.data['roundtripLatency'] != 'number') { | |
246 console.error('Received incorrect onPerfStats message: ' + messageStr); | |
247 return; | |
248 } | |
249 this.perfStats_ = | |
250 /** @type {remoting.ClientSession.PerfStats} */ message.data; | |
251 } else if (message.method == 'injectClipboardItem') { | |
252 if (typeof message.data['mimeType'] != 'string' || | |
253 typeof message.data['item'] != 'string') { | |
254 console.error('Received incorrect injectClipboardItem message.'); | |
255 return; | |
256 } | |
257 if (remoting.clipboard) { | |
258 remoting.clipboard.fromHost(message.data['mimeType'], | |
259 message.data['item']); | |
260 } | |
261 } else if (message.method == 'onFirstFrameReceived') { | |
262 if (remoting.clientSession) { | |
263 remoting.clientSession.onFirstFrameReceived(); | |
264 } | |
265 } else if (message.method == 'onConnectionReady') { | |
266 if (typeof message.data['ready'] != 'boolean') { | |
267 console.error('Received incorrect onConnectionReady message.'); | |
268 return; | |
269 } | |
270 var ready = /** @type {boolean} */ message.data['ready']; | |
271 this.onConnectionReadyHandler(ready); | |
272 } else if (message.method == 'fetchPin') { | |
273 // The pairingSupported value in the dictionary indicates whether both | |
274 // client and host support pairing. If the client doesn't support pairing, | |
275 // then the value won't be there at all, so give it a default of false. | |
276 /** @type {boolean} */ | |
277 var pairingSupported = false; | |
278 if ('pairingSupported' in message.data) { | |
279 pairingSupported = | |
280 /** @type {boolean} */ message.data['pairingSupported']; | |
281 if (typeof pairingSupported != 'boolean') { | |
282 console.error('Received incorrect fetchPin message.'); | |
283 return; | |
284 } | |
285 } | |
286 this.fetchPinHandler(pairingSupported); | |
287 } else if (message.method == 'setCapabilities') { | |
288 if (typeof message.data['capabilities'] != 'string') { | |
289 console.error('Received incorrect setCapabilities message.'); | |
290 return; | |
291 } | |
292 | |
293 /** @type {!Array.<string>} */ | |
294 var capabilities = tokenize(message.data['capabilities']); | |
295 this.onSetCapabilitiesHandler(capabilities); | |
296 } else if (message.method == 'fetchThirdPartyToken') { | |
297 if (typeof message.data['tokenUrl'] != 'string' || | |
298 typeof message.data['hostPublicKey'] != 'string' || | |
299 typeof message.data['scope'] != 'string') { | |
300 console.error('Received incorrect fetchThirdPartyToken message.'); | |
301 return; | |
302 } | |
303 var tokenUrl = /** @type {string} */ message.data['tokenUrl']; | |
304 var hostPublicKey = | |
305 /** @type {string} */ message.data['hostPublicKey']; | |
306 var scope = /** @type {string} */ message.data['scope']; | |
307 this.fetchThirdPartyTokenHandler(tokenUrl, hostPublicKey, scope); | |
308 } else if (message.method == 'pairingResponse') { | |
309 var clientId = /** @type {string} */ message.data['clientId']; | |
310 var sharedSecret = /** @type {string} */ message.data['sharedSecret']; | |
311 if (typeof clientId != 'string' || typeof sharedSecret != 'string') { | |
312 console.error('Received incorrect pairingResponse message.'); | |
313 return; | |
314 } | |
315 this.onPairingComplete_(clientId, sharedSecret); | |
316 } else if (message.method == 'extensionMessage') { | |
317 if (typeof(message.data['type']) != 'string' || | |
318 typeof(message.data['data']) != 'string') { | |
319 console.error('Invalid extension message:', message.data); | |
320 return; | |
321 } | |
322 switch (message.data['type']) { | |
323 case 'test-echo-reply': | |
324 console.log('Got echo reply: ' + message.data['data']); | |
325 break; | |
326 default: | |
327 console.log('Unexpected message received: ' + | |
328 message.data['type'] + ': ' + message.data['data']); | |
329 } | |
330 } | |
331 }; | |
332 | |
333 /** | |
334 * Deletes the plugin. | |
335 */ | |
336 remoting.ClientPluginAsync.prototype.cleanup = function() { | |
337 this.plugin.parentNode.removeChild(this.plugin); | |
338 }; | |
339 | |
340 /** | |
341 * @return {HTMLEmbedElement} HTML element that correspods to the plugin. | |
342 */ | |
343 remoting.ClientPluginAsync.prototype.element = function() { | |
344 return this.plugin; | |
345 }; | |
346 | |
347 /** | |
348 * @param {function(boolean): void} onDone | |
349 */ | |
350 remoting.ClientPluginAsync.prototype.initialize = function(onDone) { | |
351 if (this.helloReceived_) { | |
352 onDone(true); | |
353 } else { | |
354 this.onInitializedCallback_ = onDone; | |
355 } | |
356 }; | |
357 | |
358 /** | |
359 * @return {boolean} True if the plugin and web-app versions are compatible. | |
360 */ | |
361 remoting.ClientPluginAsync.prototype.isSupportedVersion = function() { | |
362 if (!this.helloReceived_) { | |
363 console.error( | |
364 "isSupportedVersion() is called before the plugin is initialized."); | |
365 return false; | |
366 } | |
367 return this.API_VERSION_ >= this.pluginApiMinVersion_ && | |
368 this.pluginApiVersion_ >= this.API_MIN_VERSION_; | |
369 }; | |
370 | |
371 /** | |
372 * @param {remoting.ClientPlugin.Feature} feature The feature to test for. | |
373 * @return {boolean} True if the plugin supports the named feature. | |
374 */ | |
375 remoting.ClientPluginAsync.prototype.hasFeature = function(feature) { | |
376 if (!this.helloReceived_) { | |
377 console.error( | |
378 "hasFeature() is called before the plugin is initialized."); | |
379 return false; | |
380 } | |
381 return this.pluginApiFeatures_.indexOf(feature) > -1; | |
382 }; | |
383 | |
384 /** | |
385 * @return {boolean} True if the plugin supports the injectKeyEvent API. | |
386 */ | |
387 remoting.ClientPluginAsync.prototype.isInjectKeyEventSupported = function() { | |
388 return this.pluginApiVersion_ >= 6; | |
389 }; | |
390 | |
391 /** | |
392 * @param {string} iq Incoming IQ stanza. | |
393 */ | |
394 remoting.ClientPluginAsync.prototype.onIncomingIq = function(iq) { | |
395 if (this.plugin && this.plugin.postMessage) { | |
396 this.plugin.postMessage(JSON.stringify( | |
397 { method: 'incomingIq', data: { iq: iq } })); | |
398 } else { | |
399 // plugin.onIq may not be set after the plugin has been shut | |
400 // down. Particularly this happens when we receive response to | |
401 // session-terminate stanza. | |
402 console.warn('plugin.onIq is not set so dropping incoming message.'); | |
403 } | |
404 }; | |
405 | |
406 /** | |
407 * @param {string} hostJid The jid of the host to connect to. | |
408 * @param {string} hostPublicKey The base64 encoded version of the host's | |
409 * public key. | |
410 * @param {string} localJid Local jid. | |
411 * @param {string} sharedSecret The access code for IT2Me or the PIN | |
412 * for Me2Me. | |
413 * @param {string} authenticationMethods Comma-separated list of | |
414 * authentication methods the client should attempt to use. | |
415 * @param {string} authenticationTag A host-specific tag to mix into | |
416 * authentication hashes. | |
417 * @param {string} clientPairingId For paired Me2Me connections, the | |
418 * pairing id for this client, as issued by the host. | |
419 * @param {string} clientPairedSecret For paired Me2Me connections, the | |
420 * paired secret for this client, as issued by the host. | |
421 */ | |
422 remoting.ClientPluginAsync.prototype.connect = function( | |
423 hostJid, hostPublicKey, localJid, sharedSecret, | |
424 authenticationMethods, authenticationTag, | |
425 clientPairingId, clientPairedSecret) { | |
426 this.plugin.postMessage(JSON.stringify( | |
427 { method: 'connect', data: { | |
428 hostJid: hostJid, | |
429 hostPublicKey: hostPublicKey, | |
430 localJid: localJid, | |
431 sharedSecret: sharedSecret, | |
432 authenticationMethods: authenticationMethods, | |
433 authenticationTag: authenticationTag, | |
434 capabilities: this.capabilities_.join(" "), | |
435 clientPairingId: clientPairingId, | |
436 clientPairedSecret: clientPairedSecret | |
437 } | |
438 })); | |
439 }; | |
440 | |
441 /** | |
442 * Release all currently pressed keys. | |
443 */ | |
444 remoting.ClientPluginAsync.prototype.releaseAllKeys = function() { | |
445 this.plugin.postMessage(JSON.stringify( | |
446 { method: 'releaseAllKeys', data: {} })); | |
447 }; | |
448 | |
449 /** | |
450 * Send a key event to the host. | |
451 * | |
452 * @param {number} usbKeycode The USB-style code of the key to inject. | |
453 * @param {boolean} pressed True to inject a key press, False for a release. | |
454 */ | |
455 remoting.ClientPluginAsync.prototype.injectKeyEvent = | |
456 function(usbKeycode, pressed) { | |
457 this.plugin.postMessage(JSON.stringify( | |
458 { method: 'injectKeyEvent', data: { | |
459 'usbKeycode': usbKeycode, | |
460 'pressed': pressed} | |
461 })); | |
462 }; | |
463 | |
464 /** | |
465 * Remap one USB keycode to another in all subsequent key events. | |
466 * | |
467 * @param {number} fromKeycode The USB-style code of the key to remap. | |
468 * @param {number} toKeycode The USB-style code to remap the key to. | |
469 */ | |
470 remoting.ClientPluginAsync.prototype.remapKey = | |
471 function(fromKeycode, toKeycode) { | |
472 this.plugin.postMessage(JSON.stringify( | |
473 { method: 'remapKey', data: { | |
474 'fromKeycode': fromKeycode, | |
475 'toKeycode': toKeycode} | |
476 })); | |
477 }; | |
478 | |
479 /** | |
480 * Enable/disable redirection of the specified key to the web-app. | |
481 * | |
482 * @param {number} keycode The USB-style code of the key. | |
483 * @param {Boolean} trap True to enable trapping, False to disable. | |
484 */ | |
485 remoting.ClientPluginAsync.prototype.trapKey = function(keycode, trap) { | |
486 this.plugin.postMessage(JSON.stringify( | |
487 { method: 'trapKey', data: { | |
488 'keycode': keycode, | |
489 'trap': trap} | |
490 })); | |
491 }; | |
492 | |
493 /** | |
494 * Returns an associative array with a set of stats for this connecton. | |
495 * | |
496 * @return {remoting.ClientSession.PerfStats} The connection statistics. | |
497 */ | |
498 remoting.ClientPluginAsync.prototype.getPerfStats = function() { | |
499 return this.perfStats_; | |
500 }; | |
501 | |
502 /** | |
503 * Sends a clipboard item to the host. | |
504 * | |
505 * @param {string} mimeType The MIME type of the clipboard item. | |
506 * @param {string} item The clipboard item. | |
507 */ | |
508 remoting.ClientPluginAsync.prototype.sendClipboardItem = | |
509 function(mimeType, item) { | |
510 if (!this.hasFeature(remoting.ClientPlugin.Feature.SEND_CLIPBOARD_ITEM)) | |
511 return; | |
512 this.plugin.postMessage(JSON.stringify( | |
513 { method: 'sendClipboardItem', | |
514 data: { mimeType: mimeType, item: item }})); | |
515 }; | |
516 | |
517 /** | |
518 * Notifies the host that the client has the specified size and pixel density. | |
519 * | |
520 * @param {number} width The available client width in DIPs. | |
521 * @param {number} height The available client height in DIPs. | |
522 * @param {number} device_scale The number of device pixels per DIP. | |
523 */ | |
524 remoting.ClientPluginAsync.prototype.notifyClientResolution = | |
525 function(width, height, device_scale) { | |
526 if (this.hasFeature(remoting.ClientPlugin.Feature.NOTIFY_CLIENT_RESOLUTION)) { | |
527 var dpi = Math.floor(device_scale * 96); | |
528 this.plugin.postMessage(JSON.stringify( | |
529 { method: 'notifyClientResolution', | |
530 data: { width: Math.floor(width * device_scale), | |
531 height: Math.floor(height * device_scale), | |
532 x_dpi: dpi, y_dpi: dpi }})); | |
533 } | |
534 }; | |
535 | |
536 /** | |
537 * Requests that the host pause or resume sending video updates. | |
538 * | |
539 * @param {boolean} pause True to suspend video updates, false otherwise. | |
540 */ | |
541 remoting.ClientPluginAsync.prototype.pauseVideo = | |
542 function(pause) { | |
543 if (!this.hasFeature(remoting.ClientPlugin.Feature.PAUSE_VIDEO)) | |
544 return; | |
545 this.plugin.postMessage(JSON.stringify( | |
546 { method: 'pauseVideo', data: { pause: pause }})); | |
547 }; | |
548 | |
549 /** | |
550 * Requests that the host pause or resume sending audio updates. | |
551 * | |
552 * @param {boolean} pause True to suspend audio updates, false otherwise. | |
553 */ | |
554 remoting.ClientPluginAsync.prototype.pauseAudio = | |
555 function(pause) { | |
556 if (!this.hasFeature(remoting.ClientPlugin.Feature.PAUSE_AUDIO)) | |
557 return; | |
558 this.plugin.postMessage(JSON.stringify( | |
559 { method: 'pauseAudio', data: { pause: pause }})); | |
560 }; | |
561 | |
562 /** | |
563 * Called when a PIN is obtained from the user. | |
564 * | |
565 * @param {string} pin The PIN. | |
566 */ | |
567 remoting.ClientPluginAsync.prototype.onPinFetched = | |
568 function(pin) { | |
569 if (!this.hasFeature(remoting.ClientPlugin.Feature.ASYNC_PIN)) { | |
570 return; | |
571 } | |
572 this.plugin.postMessage(JSON.stringify( | |
573 { method: 'onPinFetched', data: { pin: pin }})); | |
574 }; | |
575 | |
576 /** | |
577 * Tells the plugin to ask for the PIN asynchronously. | |
578 */ | |
579 remoting.ClientPluginAsync.prototype.useAsyncPinDialog = | |
580 function() { | |
581 if (!this.hasFeature(remoting.ClientPlugin.Feature.ASYNC_PIN)) { | |
582 return; | |
583 } | |
584 this.plugin.postMessage(JSON.stringify( | |
585 { method: 'useAsyncPinDialog', data: {} })); | |
586 }; | |
587 | |
588 /** | |
589 * Sets the third party authentication token and shared secret. | |
590 * | |
591 * @param {string} token The token received from the token URL. | |
592 * @param {string} sharedSecret Shared secret received from the token URL. | |
593 */ | |
594 remoting.ClientPluginAsync.prototype.onThirdPartyTokenFetched = function( | |
595 token, sharedSecret) { | |
596 this.plugin.postMessage(JSON.stringify( | |
597 { method: 'onThirdPartyTokenFetched', | |
598 data: { token: token, sharedSecret: sharedSecret}})); | |
599 }; | |
600 | |
601 /** | |
602 * Request pairing with the host for PIN-less authentication. | |
603 * | |
604 * @param {string} clientName The human-readable name of the client. | |
605 * @param {function(string, string):void} onDone, Callback to receive the | |
606 * client id and shared secret when they are available. | |
607 */ | |
608 remoting.ClientPluginAsync.prototype.requestPairing = | |
609 function(clientName, onDone) { | |
610 if (!this.hasFeature(remoting.ClientPlugin.Feature.PINLESS_AUTH)) { | |
611 return; | |
612 } | |
613 this.onPairingComplete_ = onDone; | |
614 this.plugin.postMessage(JSON.stringify( | |
615 { method: 'requestPairing', data: { clientName: clientName } })); | |
616 }; | |
617 | |
618 /** | |
619 * Send an extension message to the host. | |
620 * | |
621 * @param {string} type The message type. | |
622 * @param {Object} message The message payload. | |
623 */ | |
624 remoting.ClientPluginAsync.prototype.sendClientMessage = | |
625 function(type, message) { | |
626 if (!this.hasFeature(remoting.ClientPlugin.Feature.EXTENSION_MESSAGE)) { | |
627 return; | |
628 } | |
629 this.plugin.postMessage(JSON.stringify( | |
630 { method: 'extensionMessage', | |
631 data: { type: type, data: JSON.stringify(message) } })); | |
632 | |
633 }; | |
634 | |
635 /** | |
636 * If we haven't yet received a "hello" message from the plugin, change its | |
637 * size so that the user can confirm it if click-to-play is enabled, or can | |
638 * see the "this plugin is disabled" message if it is actually disabled. | |
639 * @private | |
640 */ | |
641 remoting.ClientPluginAsync.prototype.showPluginForClickToPlay_ = function() { | |
642 if (!this.helloReceived_) { | |
643 var width = 200; | |
644 var height = 200; | |
645 this.plugin.width = width; | |
646 this.plugin.height = height; | |
647 // Center the plugin just underneath the "Connnecting..." dialog. | |
648 var parentNode = this.plugin.parentNode; | |
649 var dialog = document.getElementById('client-dialog'); | |
650 var dialogRect = dialog.getBoundingClientRect(); | |
651 parentNode.style.top = (dialogRect.bottom + 16) + 'px'; | |
652 parentNode.style.left = (window.innerWidth - width) / 2 + 'px'; | |
653 } | |
654 }; | |
OLD | NEW |