1 /* Copyright 2013 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.
8 * The application side of the application/sandbox WCS interface, used by the
9 * application to exchange messages with the sandbox.
14 /** @suppress {duplicate} */
15 var remoting = remoting || {};
18 * @param {Window} sandbox The Javascript Window object representing the
19 * sandboxed WCS driver.
22 remoting.WcsSandboxContainer = function(sandbox) {
24 this.sandbox_ = sandbox;
25 /** @type {?function(string):void}
27 this.onConnected_ = null;
28 /** @type {function(remoting.Error):void}
30 this.onError_ = function(error) {};
31 /** @type {?function(string):void}
34 /** @type {Object<number, XMLHttpRequest>}
36 this.pendingXhrs_ = {};
41 this.accessTokenRefreshTimerStarted_ = false;
43 window.addEventListener('message', this.onMessage_.bind(this), false);
45 if (base.isAppsV2()) {
47 'command': 'proxyXhrs'
49 this.sandbox_.postMessage(message, '*');
54 * @param {function(string):void} onConnected Callback to be called when WCS is
55 * connected. May be called synchronously if WCS is already connected.
56 * @param {function(remoting.Error):void} onError called in case of an error.
57 * @return {void} Nothing.
59 remoting.WcsSandboxContainer.prototype.connect = function(
60 onConnected, onError) {
61 this.onError_ = onError;
62 this.ensureAccessTokenRefreshTimer_();
64 onConnected(this.localJid_);
66 this.onConnected_ = onConnected;
71 * @param {?function(string):void} onIq Callback invoked when an IQ stanza is
73 * @return {void} Nothing.
75 remoting.WcsSandboxContainer.prototype.setOnIq = function(onIq) {
80 * Refreshes access token and starts a timer to update it periodically.
84 remoting.WcsSandboxContainer.prototype.ensureAccessTokenRefreshTimer_ =
86 if (this.accessTokenRefreshTimerStarted_) {
90 this.refreshAccessToken_();
91 setInterval(this.refreshAccessToken_.bind(this), 60 * 1000);
92 this.accessTokenRefreshTimerStarted_ = true;
97 * @return {void} Nothing.
99 remoting.WcsSandboxContainer.prototype.refreshAccessToken_ = function() {
100 remoting.identity.getToken().then(
101 this.setAccessToken_.bind(this),
102 remoting.Error.handler(this.onError_));
107 * @param {string} token The access token.
110 remoting.WcsSandboxContainer.prototype.setAccessToken_ = function(token) {
112 'command': 'setAccessToken',
115 this.sandbox_.postMessage(message, '*');
119 * @param {string} stanza The IQ stanza to send.
122 remoting.WcsSandboxContainer.prototype.sendIq = function(stanza) {
127 this.sandbox_.postMessage(message, '*');
131 * Event handler to process messages from the sandbox.
133 * @param {Event} event
135 remoting.WcsSandboxContainer.prototype.onMessage_ = function(event) {
136 switch (event.data['command']) {
139 /** @type {string} */
140 var localJid = event.data['localJid'];
141 if (localJid === undefined) {
142 console.error('onReady: missing localJid');
145 this.localJid_ = localJid;
146 if (this.onConnected_) {
147 var callback = this.onConnected_;
148 this.onConnected_ = null;
154 /** @type {remoting.Error} */
155 var error = event.data['error'];
156 if (error === undefined) {
157 console.error('onError: missing error code');
160 this.onError_(error);
164 /** @type {string} */
165 var stanza = event.data['stanza'];
166 if (stanza === undefined) {
167 console.error('onIq: missing IQ stanza');
176 /** @type {number} */
177 var id = event.data['id'];
178 if (id === undefined) {
179 console.error('sendXhr: missing id');
182 /** @type {Object} */
183 var parameters = event.data['parameters'];
184 if (parameters === undefined) {
185 console.error('sendXhr: missing parameters');
188 /** @type {string} */
189 var method = parameters['method'];
190 if (method === undefined) {
191 console.error('sendXhr: missing method');
194 /** @type {string} */
195 var url = parameters['url'];
196 if (url === undefined) {
197 console.error('sendXhr: missing url');
200 /** @type {string} */
201 var data = parameters['data'];
202 if (data === undefined) {
203 console.error('sendXhr: missing data');
206 /** @type {string|undefined}*/
207 var user = parameters['user'];
208 /** @type {string|undefined}*/
209 var password = parameters['password'];
210 var xhr = new XMLHttpRequest;
211 this.pendingXhrs_[id] = xhr;
212 xhr.open(method, url, true, user, password);
213 /** @type {Object} */
214 var headers = parameters['headers'];
216 for (var header in headers) {
217 xhr.setRequestHeader(header, headers[header]);
220 xhr.onreadystatechange = this.onReadyStateChange_.bind(this, id);
225 var id = event.data['id'];
226 if (id === undefined) {
227 console.error('abortXhr: missing id');
230 var xhr = this.pendingXhrs_[id]
232 // It's possible for an abort and a reply to cross each other on the
233 // IPC channel. In that case, we silently ignore the abort.
240 console.error('Unexpected message:', event.data['command'], event.data);
245 * Return a "copy" of an XHR object suitable for postMessage. Specifically,
246 * remove all non-serializable members such as functions.
248 * @param {XMLHttpRequest} xhr The XHR to serialize.
249 * @return {Object} A serializable version of the input.
251 function sanitizeXhr_(xhr) {
252 /** @type {Object} */
254 readyState: xhr.readyState,
255 response: xhr.response,
256 responseText: xhr.responseText,
257 responseType: xhr.responseType,
258 responseXML: xhr.responseXML,
260 statusText: xhr.statusText,
261 withCredentials: xhr.withCredentials
267 * @param {number} id The unique ID of the XHR for which the state has changed.
270 remoting.WcsSandboxContainer.prototype.onReadyStateChange_ = function(id) {
271 var xhr = this.pendingXhrs_[id];
273 // XHRs are only removed when they have completed, in which case no
274 // further callbacks should be received.
275 console.error('Unexpected callback for xhr', id);
279 'command': 'xhrStateChange',
281 'xhr': sanitizeXhr_(xhr)
283 this.sandbox_.postMessage(message, '*');
284 if (xhr.readyState == 4) {
285 delete this.pendingXhrs_[id];
289 /** @type {remoting.WcsSandboxContainer} */
290 remoting.wcsSandbox = null;