Roll src/third_party/WebKit d9c6159:8139f33 (svn 201974:201975)
[chromium-blink-merge.git] / remoting / webapp / base / js / wcs_sandbox_container.js
blobc44e7b3fc655b6aad7e5f0b3331c0d85507d3386
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.
4 */
6 /**
7 * @fileoverview
8 * The application side of the application/sandbox WCS interface, used by the
9 * application to exchange messages with the sandbox.
12 'use strict';
14 /** @suppress {duplicate} */
15 var remoting = remoting || {};
17 /**
18 * @param {Window} sandbox The Javascript Window object representing the
19 * sandboxed WCS driver.
20 * @param {base.WindowMessageDispatcher} windowMessageDispatcher
21 * @constructor
22 * @implements {base.Disposable}
24 remoting.WcsSandboxContainer = function(sandbox, windowMessageDispatcher) {
25 /** @private */
26 this.sandbox_ = sandbox;
27 /** @private {?function(string):void} */
28 this.onConnected_ = null;
29 /** @private {function(!remoting.Error):void} */
30 this.onError_ = function(error) {};
31 /** @private {?function(string):void} */
32 this.onIq_ = null;
33 /** @private {Object<number, XMLHttpRequest>} */
34 this.pendingXhrs_ = {};
35 /** @private */
36 this.localJid_ = '';
38 /** @private */
39 this.accessTokenRefreshTimerStarted_ = false;
41 /** @private {base.WindowMessageDispatcher} */
42 this.windowMessageDispatcher_ = windowMessageDispatcher;
44 this.windowMessageDispatcher_.registerMessageHandler(
45 'wcs-sandbox', this.onMessage_.bind(this));
47 if (base.isAppsV2()) {
48 var message = {
49 'command': 'proxyXhrs'
51 this.sandbox_.postMessage(message, '*');
55 remoting.WcsSandboxContainer.prototype.dispose = function() {
56 this.windowMessageDispatcher_.unregisterMessageHandler('wcs-sandbox');
59 /**
60 * @param {function(string):void} onConnected Callback to be called when WCS is
61 * connected. May be called synchronously if WCS is already connected.
62 * @param {function(!remoting.Error):void} onError called in case of an error.
63 * @return {void} Nothing.
65 remoting.WcsSandboxContainer.prototype.connect = function(
66 onConnected, onError) {
67 this.onError_ = onError;
68 this.ensureAccessTokenRefreshTimer_();
69 if (this.localJid_) {
70 onConnected(this.localJid_);
71 } else {
72 this.onConnected_ = onConnected;
76 /**
77 * @param {?function(string):void} onIq Callback invoked when an IQ stanza is
78 * received.
79 * @return {void} Nothing.
81 remoting.WcsSandboxContainer.prototype.setOnIq = function(onIq) {
82 this.onIq_ = onIq;
85 /**
86 * Refreshes access token and starts a timer to update it periodically.
88 * @private
90 remoting.WcsSandboxContainer.prototype.ensureAccessTokenRefreshTimer_ =
91 function() {
92 if (this.accessTokenRefreshTimerStarted_) {
93 return;
96 this.refreshAccessToken_();
97 setInterval(this.refreshAccessToken_.bind(this), 60 * 1000);
98 this.accessTokenRefreshTimerStarted_ = true;
102 * @private
103 * @return {void} Nothing.
105 remoting.WcsSandboxContainer.prototype.refreshAccessToken_ = function() {
106 remoting.identity.getToken().then(
107 this.setAccessToken_.bind(this),
108 remoting.Error.handler(this.onError_));
112 * @private
113 * @param {string} token The access token.
114 * @return {void}
116 remoting.WcsSandboxContainer.prototype.setAccessToken_ = function(token) {
117 var message = {
118 'command': 'setAccessToken',
119 'token': token
121 this.sandbox_.postMessage(message, '*');
125 * @param {string} stanza The IQ stanza to send.
126 * @return {void}
128 remoting.WcsSandboxContainer.prototype.sendIq = function(stanza) {
129 var message = {
130 'command': 'sendIq',
131 'stanza': stanza
133 this.sandbox_.postMessage(message, '*');
137 * Event handler to process messages from the sandbox.
139 * @param {Event} event
140 * @private
142 remoting.WcsSandboxContainer.prototype.onMessage_ = function(event) {
143 console.assert(typeof event.data === 'object',
144 'Bad data type: ' + typeof event.data + '.');
145 console.assert(event.data['source'] == 'wcs-sandbox',
146 'Bad data source: ' +
147 /** @type {string} */ (event.data['source']));
149 switch (event.data['command']) {
151 case 'onLocalJid':
152 /** @type {string} */
153 var localJid = event.data['localJid'];
154 if (localJid === undefined) {
155 console.error('onReady: missing localJid');
156 break;
158 this.localJid_ = localJid;
159 if (this.onConnected_) {
160 var callback = this.onConnected_;
161 this.onConnected_ = null;
162 callback(localJid);
164 break;
166 case 'onError':
167 /** @type {!remoting.Error} */
168 var error = event.data['error'];
169 if (error === undefined) {
170 console.error('onError: missing error code');
171 break;
173 this.onError_(error);
174 break;
176 case 'onIq':
177 /** @type {string} */
178 var stanza = event.data['stanza'];
179 if (stanza === undefined) {
180 console.error('onIq: missing IQ stanza');
181 break;
183 if (this.onIq_) {
184 this.onIq_(stanza);
186 break;
188 case 'sendXhr':
189 /** @type {number} */
190 var id = event.data['id'];
191 if (id === undefined) {
192 console.error('sendXhr: missing id');
193 break;
195 /** @type {Object} */
196 var parameters = event.data['parameters'];
197 if (parameters === undefined) {
198 console.error('sendXhr: missing parameters');
199 break;
201 /** @type {string} */
202 var method = parameters['method'];
203 if (method === undefined) {
204 console.error('sendXhr: missing method');
205 break;
207 /** @type {string} */
208 var url = parameters['url'];
209 if (url === undefined) {
210 console.error('sendXhr: missing url');
211 break;
213 /** @type {string} */
214 var data = parameters['data'];
215 if (data === undefined) {
216 console.error('sendXhr: missing data');
217 break;
219 /** @type {string|undefined}*/
220 var user = parameters['user'];
221 /** @type {string|undefined}*/
222 var password = parameters['password'];
223 var xhr = new XMLHttpRequest;
224 this.pendingXhrs_[id] = xhr;
225 xhr.open(method, url, true, user, password);
226 /** @type {Object} */
227 var headers = parameters['headers'];
228 if (headers) {
229 for (var header in headers) {
230 xhr.setRequestHeader(header, headers[header]);
233 xhr.onreadystatechange = this.onReadyStateChange_.bind(this, id);
234 xhr.send(data);
235 break;
237 case 'abortXhr':
238 var id = event.data['id'];
239 if (id === undefined) {
240 console.error('abortXhr: missing id');
241 break;
243 var xhr = this.pendingXhrs_[id]
244 if (!xhr) {
245 // It's possible for an abort and a reply to cross each other on the
246 // IPC channel. In that case, we silently ignore the abort.
247 break;
249 xhr.abort();
250 break;
252 default:
253 console.error('Unexpected message:', event.data['command'], event.data);
258 * Return a "copy" of an XHR object suitable for postMessage. Specifically,
259 * remove all non-serializable members such as functions.
261 * @param {XMLHttpRequest} xhr The XHR to serialize.
262 * @return {Object} A serializable version of the input.
264 function sanitizeXhr_(xhr) {
265 /** @type {Object} */
266 var result = {
267 readyState: xhr.readyState,
268 response: xhr.response,
269 responseText: xhr.responseText,
270 responseType: xhr.responseType,
271 responseXML: xhr.responseXML,
272 status: xhr.status,
273 statusText: xhr.statusText,
274 withCredentials: xhr.withCredentials
276 return result;
280 * @param {number} id The unique ID of the XHR for which the state has changed.
281 * @private
283 remoting.WcsSandboxContainer.prototype.onReadyStateChange_ = function(id) {
284 var xhr = this.pendingXhrs_[id];
285 if (!xhr) {
286 // XHRs are only removed when they have completed, in which case no
287 // further callbacks should be received.
288 console.error('Unexpected callback for xhr', id);
289 return;
291 var message = {
292 'command': 'xhrStateChange',
293 'id': id,
294 'xhr': sanitizeXhr_(xhr)
296 this.sandbox_.postMessage(message, '*');
297 if (xhr.readyState == 4) {
298 delete this.pendingXhrs_[id];
302 /** @type {remoting.WcsSandboxContainer} */
303 remoting.wcsSandbox = null;