Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / remoting / webapp / wcs_sandbox_container.js
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  */
5
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  */
11
12 'use strict';
13
14 /** @suppress {duplicate} */
15 var remoting = remoting || {};
16
17 /**
18  * @param {Window} sandbox The Javascript Window object representing the
19  *     sandboxed WCS driver.
20  * @constructor
21  */
22 remoting.WcsSandboxContainer = function(sandbox) {
23   /** @private */
24   this.sandbox_ = sandbox;
25   /** @type {?function(string):void}
26     * @private */
27   this.onConnected_ = null;
28   /** @type {function(remoting.Error):void}
29     * @private */
30   this.onError_ = function(error) {};
31   /** @type {?function(string):void}
32     * @private */
33   this.onIq_ = null;
34   /** @type {Object.<number, XMLHttpRequest>}
35     * @private */
36   this.pendingXhrs_ = {};
37   /** @private */
38   this.localJid_ = '';
39
40   /** @private */
41   this.accessTokenRefreshTimerStarted_ = false;
42
43   window.addEventListener('message', this.onMessage_.bind(this), false);
44
45   if (base.isAppsV2()) {
46     var message = {
47       'command': 'proxyXhrs'
48     };
49     this.sandbox_.postMessage(message, '*');
50   }
51 };
52
53 /**
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.
58  */
59 remoting.WcsSandboxContainer.prototype.connect = function(
60     onConnected, onError) {
61   this.onError_ = onError;
62   this.ensureAccessTokenRefreshTimer_();
63   if (this.localJid_) {
64     onConnected(this.localJid_);
65   } else {
66     this.onConnected_ = onConnected;
67   }
68 };
69
70 /**
71  * @param {?function(string):void} onIq Callback invoked when an IQ stanza is
72  *     received.
73  * @return {void} Nothing.
74  */
75 remoting.WcsSandboxContainer.prototype.setOnIq = function(onIq) {
76   this.onIq_ = onIq;
77 };
78
79 /**
80  * Refreshes access token and starts a timer to update it periodically.
81  *
82  * @private
83  */
84 remoting.WcsSandboxContainer.prototype.ensureAccessTokenRefreshTimer_ =
85     function() {
86   if (this.accessTokenRefreshTimerStarted_) {
87     return;
88   }
89
90   this.refreshAccessToken_();
91   setInterval(this.refreshAccessToken_.bind(this), 60 * 1000);
92   this.accessTokenRefreshTimerStarted_ = true;
93 }
94
95 /**
96  * @private
97  * @return {void} Nothing.
98  */
99 remoting.WcsSandboxContainer.prototype.refreshAccessToken_ = function() {
100   remoting.identity.callWithToken(
101       this.setAccessToken_.bind(this), this.onError_);
102 };
103
104 /**
105  * @private
106  * @param {string} token The access token.
107  * @return {void}
108  */
109 remoting.WcsSandboxContainer.prototype.setAccessToken_ = function(token) {
110   var message = {
111     'command': 'setAccessToken',
112     'token': token
113   };
114   this.sandbox_.postMessage(message, '*');
115 };
116
117 /**
118  * @param {string} stanza The IQ stanza to send.
119  * @return {void}
120  */
121 remoting.WcsSandboxContainer.prototype.sendIq = function(stanza) {
122   var message = {
123     'command': 'sendIq',
124     'stanza': stanza
125   };
126   this.sandbox_.postMessage(message, '*');
127 };
128
129 /**
130  * Event handler to process messages from the sandbox.
131  *
132  * @param {Event} event
133  */
134 remoting.WcsSandboxContainer.prototype.onMessage_ = function(event) {
135   switch (event.data['command']) {
136
137     case 'onLocalJid':
138       /** @type {string} */
139       var localJid = event.data['localJid'];
140       if (localJid === undefined) {
141         console.error('onReady: missing localJid');
142         break;
143       }
144       this.localJid_ = localJid;
145       if (this.onConnected_) {
146         var callback = this.onConnected_;
147         this.onConnected_ = null;
148         callback(localJid);
149       }
150       break;
151
152     case 'onError':
153       /** @type {remoting.Error} */
154       var error = event.data['error'];
155       if (error === undefined) {
156         console.error('onError: missing error code');
157         break;
158       }
159       this.onError_(error);
160       break;
161
162     case 'onIq':
163       /** @type {string} */
164       var stanza = event.data['stanza'];
165       if (stanza === undefined) {
166         console.error('onIq: missing IQ stanza');
167         break;
168       }
169       if (this.onIq_) {
170         this.onIq_(stanza);
171       }
172       break;
173
174     case 'sendXhr':
175       /** @type {number} */
176       var id = event.data['id'];
177       if (id === undefined) {
178         console.error('sendXhr: missing id');
179         break;
180       }
181       /** @type {Object} */
182       var parameters = event.data['parameters'];
183       if (parameters === undefined) {
184         console.error('sendXhr: missing parameters');
185         break;
186       }
187       /** @type {string} */
188       var method = parameters['method'];
189       if (method === undefined) {
190         console.error('sendXhr: missing method');
191         break;
192       }
193       /** @type {string} */
194       var url = parameters['url'];
195       if (url === undefined) {
196         console.error('sendXhr: missing url');
197         break;
198       }
199       /** @type {string} */
200       var data = parameters['data'];
201       if (data === undefined) {
202         console.error('sendXhr: missing data');
203         break;
204       }
205       /** @type {string|undefined}*/
206       var user = parameters['user'];
207       /** @type {string|undefined}*/
208       var password = parameters['password'];
209       var xhr = new XMLHttpRequest;
210       this.pendingXhrs_[id] = xhr;
211       xhr.open(method, url, true, user, password);
212       /** @type {Object} */
213       var headers = parameters['headers'];
214       if (headers) {
215         for (var header in headers) {
216           xhr.setRequestHeader(header, headers[header]);
217         }
218       }
219       xhr.onreadystatechange = this.onReadyStateChange_.bind(this, id);
220       xhr.send(data);
221       break;
222
223     case 'abortXhr':
224       var id = event.data['id'];
225       if (id === undefined) {
226         console.error('abortXhr: missing id');
227         break;
228       }
229       var xhr = this.pendingXhrs_[id]
230       if (!xhr) {
231         // It's possible for an abort and a reply to cross each other on the
232         // IPC channel. In that case, we silently ignore the abort.
233         break;
234       }
235       xhr.abort();
236       break;
237
238     default:
239       console.error('Unexpected message:', event.data['command'], event.data);
240   }
241 };
242
243 /**
244  * Return a "copy" of an XHR object suitable for postMessage. Specifically,
245  * remove all non-serializable members such as functions.
246  *
247  * @param {XMLHttpRequest} xhr The XHR to serialize.
248  * @return {Object} A serializable version of the input.
249  */
250 function sanitizeXhr_(xhr) {
251   /** @type {Object} */
252   var result = {
253     readyState: xhr.readyState,
254     response: xhr.response,
255     responseText: xhr.responseText,
256     responseType: xhr.responseType,
257     responseXML: xhr.responseXML,
258     status: xhr.status,
259     statusText: xhr.statusText,
260     withCredentials: xhr.withCredentials
261   };
262   return result;
263 }
264
265 /**
266  * @param {number} id The unique ID of the XHR for which the state has changed.
267  * @private
268  */
269 remoting.WcsSandboxContainer.prototype.onReadyStateChange_ = function(id) {
270   var xhr = this.pendingXhrs_[id];
271   if (!xhr) {
272     // XHRs are only removed when they have completed, in which case no
273     // further callbacks should be received.
274     console.error('Unexpected callback for xhr', id);
275     return;
276   }
277   var message = {
278     'command': 'xhrStateChange',
279     'id': id,
280     'xhr': sanitizeXhr_(xhr)
281   };
282   this.sandbox_.postMessage(message, '*');
283   if (xhr.readyState == 4) {
284     delete this.pendingXhrs_[id];
285   }
286 }
287
288 /** @type {remoting.WcsSandboxContainer} */
289 remoting.wcsSandbox = null;