Separate Simple Backend creation from initialization.
[chromium-blink-merge.git] / remoting / webapp / wcs_sandbox_container.js
blob68f2fbd83fbc81bb69d3dd5707f0035af0839b0b
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.
10  */
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  * @constructor
21  */
22 remoting.WcsSandboxContainer = function(sandbox) {
23   this.sandbox_ = sandbox;
24   /** @type {?function(string):void} */
25   this.onReady_ = null;
26   /** @type {?function(remoting.Error):void} */
27   this.onError_ = null;
28   /** @type {?function(string):void} */
29   this.onIq_ = null;
30   /** @type {Object.<number, XMLHttpRequest>} */
31   this.pendingXhrs_ = {};
33   window.addEventListener('message', this.onMessage_.bind(this), false);
36 /**
37  * @param {?function(string):void} onReady Callback invoked with the client JID
38  *     when the WCS code has loaded.
39  * @return {void} Nothing.
40  */
41 remoting.WcsSandboxContainer.prototype.setOnReady = function(onReady) {
42   this.onReady_ = onReady;
45 /**
46  * @param {?function(remoting.Error):void} onError Callback invoked if the WCS
47  *     code cannot be loaded.
48  * @return {void} Nothing.
49  */
50 remoting.WcsSandboxContainer.prototype.setOnError = function(onError) {
51   this.onError_ = onError;
54 /**
55  * @param {?function(string):void} onIq Callback invoked when an IQ stanza is
56  *     received.
57  * @return {void} Nothing.
58  */
59 remoting.WcsSandboxContainer.prototype.setOnIq = function(onIq) {
60   this.onIq_ = onIq;
63 /**
64  * @param {string} token The access token.
65  * @return {void}
66  */
67 remoting.WcsSandboxContainer.prototype.setAccessToken = function(token) {
68   var message = {
69     'command': 'setAccessToken',
70     'token': token
71   };
72   this.sandbox_.postMessage(message, '*');
75 /**
76  * @param {string} stanza The IQ stanza to send.
77  * @return {void}
78  */
79 remoting.WcsSandboxContainer.prototype.sendIq = function(stanza) {
80   var message = {
81     'command': 'sendIq',
82     'stanza': stanza
83   };
84   this.sandbox_.postMessage(message, '*');
87 /**
88  * Event handler to process messages from the sandbox.
89  *
90  * @param {Event} event
91  */
92 remoting.WcsSandboxContainer.prototype.onMessage_ = function(event) {
93   switch (event.data['command']) {
95     case 'onReady':
96       /** @type {string} */
97       var clientJid = event.data['clientJid'];
98       if (clientJid === undefined) {
99         console.error('onReady: missing client JID');
100         break;
101       }
102       if (this.onReady_) {
103         this.onReady_(clientJid);
104       }
105       break;
107     case 'onError':
108       /** @type {remoting.Error} */
109       var error = event.data['error'];
110       if (error === undefined) {
111         console.error('onError: missing error code');
112         break;
113       }
114       this.onError_(error);
115       break;
117     case 'onIq':
118       /** @type {string} */
119       var stanza = event.data['stanza'];
120       if (stanza === undefined) {
121         console.error('onIq: missing IQ stanza');
122         break;
123       }
124       if (this.onIq_) {
125         this.onIq_(stanza);
126       }
127       break;
129     case 'sendXhr':
130       /** @type {number} */
131       var id = event.data['id'];
132       if (id === undefined) {
133         console.error('sendXhr: missing id');
134         break;
135       }
136       /** @type {Object} */
137       var parameters = event.data['parameters'];
138       if (parameters === undefined) {
139         console.error('sendXhr: missing parameters');
140         break;
141       }
142       /** @type {string} */
143       var method = parameters['method'];
144       if (method === undefined) {
145         console.error('sendXhr: missing method');
146         break;
147       }
148       /** @type {string} */
149       var url = parameters['url'];
150       if (url === undefined) {
151         console.error('sendXhr: missing url');
152         break;
153       }
154       /** @type {string} */
155       var data = parameters['data'];
156       if (data === undefined) {
157         console.error('sendXhr: missing data');
158         break;
159       }
160       /** @type {string|undefined}*/
161       var user = parameters['user'];
162       /** @type {string|undefined}*/
163       var password = parameters['password'];
164       var xhr = new XMLHttpRequest;
165       this.pendingXhrs_[id] = xhr;
166       xhr.open(method, url, true, user, password);
167       /** @type {Object} */
168       var headers = parameters['headers'];
169       if (headers) {
170         for (var header in headers) {
171           xhr.setRequestHeader(header, headers[header]);
172         }
173       }
174       xhr.onreadystatechange = this.onReadyStateChange_.bind(this, id);
175       xhr.send(data);
176       break;
178     case 'abortXhr':
179       var id = event.data['id'];
180       if (id === undefined) {
181         console.error('abortXhr: missing id');
182         break;
183       }
184       var xhr = this.pendingXhrs_[id]
185       if (!xhr) {
186         // It's possible for an abort and a reply to cross each other on the
187         // IPC channel. In that case, we silently ignore the abort.
188         break;
189       }
190       xhr.abort();
191       break;
193     default:
194       console.error('Unexpected message:', event.data['command'], event.data);
195   }
199  * Return a "copy" of an XHR object suitable for postMessage. Specifically,
200  * remove all non-serializable members such as functions.
202  * @param {XMLHttpRequest} xhr The XHR to serialize.
203  * @return {Object} A serializable version of the input.
204  */
205 function sanitizeXhr_(xhr) {
206   /** @type {Object} */
207   var result = {
208     readyState: xhr.readyState,
209     response: xhr.response,
210     responseText: xhr.responseText,
211     responseType: xhr.responseType,
212     responseXML: xhr.responseXML,
213     status: xhr.status,
214     statusText: xhr.statusText,
215     withCredentials: xhr.withCredentials
216   };
217   return result;
221  * @param {number} id The unique ID of the XHR for which the state has changed.
222  * @private
223  */
224 remoting.WcsSandboxContainer.prototype.onReadyStateChange_ = function(id) {
225   var xhr = this.pendingXhrs_[id];
226   if (!xhr) {
227     // XHRs are only removed when they have completed, in which case no
228     // further callbacks should be received.
229     console.error('Unexpected callback for xhr', id);
230     return;
231   }
232   var message = {
233     'command': 'xhrStateChange',
234     'id': id,
235     'xhr': sanitizeXhr_(xhr)
236   };
237   this.sandbox_.postMessage(message, '*');
238   if (xhr.readyState == 4) {
239     delete this.pendingXhrs_[id];
240   }
243 /** @type {remoting.WcsSandboxContainer} */
244 remoting.wcsSandbox = null;