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 * Class to communicate with the It2me Host component via Native Messaging.
10 /** @suppress {duplicate} */
11 var remoting = remoting || {};
19 * @implements {base.Disposable}
21 remoting.It2MeHostFacade = function() {
22 /** @private {number} */
25 /** @private {?chrome.runtime.Port} */
28 /** @private {string} */
29 this.accessCode_ = '';
31 /** @private {number} */
32 this.accessCodeLifetime_ = 0;
34 /** @private {string} */
37 /** @private {boolean} */
38 this.initialized_ = false;
40 /** @private {base.Disposables} */
41 this.eventHooks_ = null;
44 * @type {?function():void}
47 this.onInitialized_ = function() {};
50 * Called if Native Messaging host has failed to start.
53 this.onInitializationFailed_ = function() {};
56 * Called if the It2Me Native Messaging host sends a malformed message:
57 * missing required attributes, attributes with incorrect types, etc.
58 * @type {?function(remoting.Error):void}
61 this.onError_ = function(error) {};
64 * @type {?function(remoting.HostSession.State):void}
67 this.onStateChanged_ = function() {};
70 * @type {?function(boolean):void}
73 this.onNatPolicyChanged_ = function() {};
76 remoting.It2MeHostFacade.prototype.dispose = function() {
77 base.dispose(this.eventHooks_);
78 this.eventHooks_ = null;
80 this.port_.disconnect();
86 * Sets up connection to the Native Messaging host process and exchanges
87 * 'hello' messages. If Native Messaging is not supported or if the it2me
88 * native messaging host is not installed, onInitializationFailed is invoked.
89 * Otherwise, onInitialized is invoked.
91 * @param {function(*=):void} onInitialized Called after successful
93 * @param {function(*=):void} onInitializationFailed Called if cannot connect to
94 * the native messaging host.
97 remoting.It2MeHostFacade.prototype.initialize =
98 function(onInitialized, onInitializationFailed) {
99 this.onInitialized_ = onInitialized;
100 this.onInitializationFailed_ = onInitializationFailed;
103 this.port_ = chrome.runtime.connectNative(
104 'com.google.chrome.remote_assistance');
105 this.eventHooks_ = new base.Disposables(
106 new base.ChromeEventHook(this.port_.onMessage,
107 this.onIncomingMessage_.bind(this)),
108 new base.ChromeEventHook(this.port_.onDisconnect,
109 this.onHostDisconnect_.bind(this)));
110 this.port_.postMessage({type: 'hello'});
111 } catch (/** @type {*} */ err) {
112 console.log('Native Messaging initialization failed: ', err);
113 onInitializationFailed();
119 * @param {string} email The user's email address.
120 * @param {string} authServiceWithToken Concatenation of the auth service
121 * (e.g. oauth2) and the access token.
122 * @param {function(remoting.HostSession.State):void} onStateChanged Callback to
123 * invoke when the host state changes.
124 * @param {function(boolean):void} onNatPolicyChanged Callback to invoke when
125 * the nat traversal policy changes.
126 * @param {function(string):void} logDebugInfo Callback allowing the plugin
127 * to log messages to the debug log.
128 * @param {string} xmppServerAddress XMPP server host name (or IP address) and
130 * @param {boolean} xmppServerUseTls Whether to use TLS on connections to the
132 * @param {string} directoryBotJid XMPP JID for the remoting directory server
134 * @param {function(remoting.Error):void} onError Callback to invoke in case of
138 remoting.It2MeHostFacade.prototype.connect =
139 function(email, authServiceWithToken, onStateChanged, onNatPolicyChanged,
140 logDebugInfo, xmppServerAddress, xmppServerUseTls, directoryBotJid,
144 'remoting.It2MeHostFacade.connect() without initialization.');
145 onError(remoting.Error.UNEXPECTED);
149 this.onStateChanged_ = onStateChanged;
150 this.onNatPolicyChanged_ = onNatPolicyChanged;
151 this.onError_ = onError;
152 this.port_.postMessage({
155 authServiceWithToken: authServiceWithToken,
156 xmppServerAddress: xmppServerAddress,
157 xmppServerUseTls: xmppServerUseTls,
158 directoryBotJid: directoryBotJid
163 * Unhooks the |onStateChanged|, |onError|, |onNatPolicyChanged| and
164 * |onInitalized| callbacks. This is called when the client shuts down so that
165 * the callbacks will not be invoked on a disposed client.
169 remoting.It2MeHostFacade.prototype.unhookCallbacks = function() {
170 this.onStateChanged_ = null;
171 this.onNatPolicyChanged_ = null;
172 this.onError_ = null;
173 this.onInitialized_ = null;
179 remoting.It2MeHostFacade.prototype.disconnect = function() {
181 this.port_.postMessage({type: 'disconnect'});
187 remoting.It2MeHostFacade.prototype.initialized = function() {
188 return this.initialized_;
194 remoting.It2MeHostFacade.prototype.getAccessCode = function() {
195 return this.accessCode_;
201 remoting.It2MeHostFacade.prototype.getAccessCodeLifetime = function() {
202 return this.accessCodeLifetime_;
208 remoting.It2MeHostFacade.prototype.getClient = function() {
209 return this.clientId_;
213 * Handler for incoming messages.
215 * @param {Object} message The received message.
219 remoting.It2MeHostFacade.prototype.onIncomingMessage_ =
221 var type = getStringAttr(message, 'type');
224 case 'helloResponse':
225 var version = getStringAttr(message, 'version');
226 console.log('Host version: ', version);
227 this.initialized_ = true;
228 // A "hello" request is sent immediately after the native messaging host
229 // is started. We can proceed to the next task once we receive the
231 if (this.onInitialized_) {
232 this.onInitialized_();
236 case 'connectResponse':
237 console.log('connectResponse received');
238 // Response to the "connect" request. No action is needed until we
239 // receive the corresponding "hostStateChanged" message.
242 case 'disconnectResponse':
243 console.log('disconnectResponse received');
244 // Response to the "disconnect" request. No action is needed until we
245 // receive the corresponding "hostStateChanged" message.
248 case 'hostStateChanged':
249 var stateString = getStringAttr(message, 'state');
250 console.log('hostStateChanged received: ', stateString);
251 var state = remoting.HostSession.State.fromString(stateString);
254 case remoting.HostSession.State.RECEIVED_ACCESS_CODE:
255 var accessCode = getStringAttr(message, 'accessCode');
256 var accessCodeLifetime = getNumberAttr(message, 'accessCodeLifetime');
257 this.onReceivedAccessCode_(accessCode, accessCodeLifetime);
260 case remoting.HostSession.State.CONNECTED:
261 var client = getStringAttr(message, 'client');
262 this.onConnected_(client);
265 if (this.onStateChanged_) {
266 this.onStateChanged_(state);
270 case 'natPolicyChanged':
271 if (this.onNatPolicyChanged_) {
272 var natTraversalEnabled =
273 getBooleanAttr(message, 'natTraversalEnabled');
274 this.onNatPolicyChanged_(natTraversalEnabled);
279 console.error(getStringAttr(message, 'description'));
281 this.onError_(remoting.Error.UNEXPECTED);
286 throw 'Unexpected native message: ' + message;
291 * @param {string} accessCode
292 * @param {number} accessCodeLifetime
296 remoting.It2MeHostFacade.prototype.onReceivedAccessCode_ =
297 function(accessCode, accessCodeLifetime) {
298 this.accessCode_ = accessCode;
299 this.accessCodeLifetime_ = accessCodeLifetime;
303 * @param {string} clientId
307 remoting.It2MeHostFacade.prototype.onConnected_ = function(clientId) {
308 this.clientId_ = clientId;
315 remoting.It2MeHostFacade.prototype.onHostDisconnect_ = function() {
316 if (!this.initialized_) {
317 // If the host is disconnected before it is initialized, it probably means
318 // the host is not propertly installed (or not installed at all).
319 // E.g., if the host manifest is not present we get "Specified native
320 // messaging host not found" error. If the host manifest is present but
321 // the host binary cannot be found we get the "Native host has exited"
323 console.log('Native Messaging initialization failed: ' +
324 chrome.runtime.lastError.message);
325 this.onInitializationFailed_();
327 console.error('Native Messaging port disconnected.');
329 this.onError_(remoting.Error.UNEXPECTED);