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.
20 * @param {base.WindowMessageDispatcher} windowMessageDispatcher
22 * @implements {base.Disposable}
24 remoting
.WcsSandboxContainer = function(sandbox
, windowMessageDispatcher
) {
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} */
33 /** @private {Object<number, XMLHttpRequest>} */
34 this.pendingXhrs_
= {};
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()) {
49 'command': 'proxyXhrs'
51 this.sandbox_
.postMessage(message
, '*');
55 remoting
.WcsSandboxContainer
.prototype.dispose = function() {
56 this.windowMessageDispatcher_
.unregisterMessageHandler('wcs-sandbox');
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_();
70 onConnected(this.localJid_
);
72 this.onConnected_
= onConnected
;
77 * @param {?function(string):void} onIq Callback invoked when an IQ stanza is
79 * @return {void} Nothing.
81 remoting
.WcsSandboxContainer
.prototype.setOnIq = function(onIq
) {
86 * Refreshes access token and starts a timer to update it periodically.
90 remoting
.WcsSandboxContainer
.prototype.ensureAccessTokenRefreshTimer_
=
92 if (this.accessTokenRefreshTimerStarted_
) {
96 this.refreshAccessToken_();
97 setInterval(this.refreshAccessToken_
.bind(this), 60 * 1000);
98 this.accessTokenRefreshTimerStarted_
= true;
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_
));
113 * @param {string} token The access token.
116 remoting
.WcsSandboxContainer
.prototype.setAccessToken_ = function(token
) {
118 'command': 'setAccessToken',
121 this.sandbox_
.postMessage(message
, '*');
125 * @param {string} stanza The IQ stanza to send.
128 remoting
.WcsSandboxContainer
.prototype.sendIq = function(stanza
) {
133 this.sandbox_
.postMessage(message
, '*');
137 * Event handler to process messages from the sandbox.
139 * @param {Event} event
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']) {
152 /** @type {string} */
153 var localJid
= event
.data
['localJid'];
154 if (localJid
=== undefined) {
155 console
.error('onReady: missing localJid');
158 this.localJid_
= localJid
;
159 if (this.onConnected_
) {
160 var callback
= this.onConnected_
;
161 this.onConnected_
= null;
167 /** @type {!remoting.Error} */
168 var error
= event
.data
['error'];
169 if (error
=== undefined) {
170 console
.error('onError: missing error code');
173 this.onError_(error
);
177 /** @type {string} */
178 var stanza
= event
.data
['stanza'];
179 if (stanza
=== undefined) {
180 console
.error('onIq: missing IQ stanza');
189 /** @type {number} */
190 var id
= event
.data
['id'];
191 if (id
=== undefined) {
192 console
.error('sendXhr: missing id');
195 /** @type {Object} */
196 var parameters
= event
.data
['parameters'];
197 if (parameters
=== undefined) {
198 console
.error('sendXhr: missing parameters');
201 /** @type {string} */
202 var method
= parameters
['method'];
203 if (method
=== undefined) {
204 console
.error('sendXhr: missing method');
207 /** @type {string} */
208 var url
= parameters
['url'];
209 if (url
=== undefined) {
210 console
.error('sendXhr: missing url');
213 /** @type {string} */
214 var data
= parameters
['data'];
215 if (data
=== undefined) {
216 console
.error('sendXhr: missing data');
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'];
229 for (var header
in headers
) {
230 xhr
.setRequestHeader(header
, headers
[header
]);
233 xhr
.onreadystatechange
= this.onReadyStateChange_
.bind(this, id
);
238 var id
= event
.data
['id'];
239 if (id
=== undefined) {
240 console
.error('abortXhr: missing id');
243 var xhr
= this.pendingXhrs_
[id
]
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.
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} */
267 readyState
: xhr
.readyState
,
268 response
: xhr
.response
,
269 responseText
: xhr
.responseText
,
270 responseType
: xhr
.responseType
,
271 responseXML
: xhr
.responseXML
,
273 statusText
: xhr
.statusText
,
274 withCredentials
: xhr
.withCredentials
280 * @param {number} id The unique ID of the XHR for which the state has changed.
283 remoting
.WcsSandboxContainer
.prototype.onReadyStateChange_ = function(id
) {
284 var xhr
= this.pendingXhrs_
[id
];
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
);
292 'command': 'xhrStateChange',
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;