Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
SeleniumHQ
GitHub Repository: SeleniumHQ/Selenium
Path: blob/trunk/third_party/closure/goog/net/xpc/nixtransport.js
1865 views
1
// Copyright 2008 The Closure Library Authors. All Rights Reserved.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
// http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS-IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
/**
16
* @fileoverview Contains the NIX (Native IE XDC) method transport for
17
* cross-domain communication. It exploits the fact that Internet Explorer
18
* allows a window that is the parent of an iframe to set said iframe window's
19
* opener property to an object. This object can be a function that in turn
20
* can be used to send a message despite same-origin constraints. Note that
21
* this function, if a pure JavaScript object, opens up the possibilitiy of
22
* gaining a hold of the context of the other window and in turn, attacking
23
* it. This implementation therefore wraps the JavaScript objects used inside
24
* a VBScript class. Since VBScript objects are passed in JavaScript as a COM
25
* wrapper (like DOM objects), they are thus opaque to JavaScript
26
* (except for the interface they expose). This therefore provides a safe
27
* method of transport.
28
*
29
*
30
* Initially based on FrameElementTransport which shares some similarities
31
* to this method.
32
*/
33
34
goog.provide('goog.net.xpc.NixTransport');
35
36
goog.require('goog.log');
37
goog.require('goog.net.xpc');
38
goog.require('goog.net.xpc.CfgFields');
39
goog.require('goog.net.xpc.CrossPageChannelRole');
40
goog.require('goog.net.xpc.Transport');
41
goog.require('goog.net.xpc.TransportTypes');
42
goog.require('goog.reflect');
43
44
45
46
/**
47
* NIX method transport.
48
*
49
* NOTE(user): NIX method tested in all IE versions starting from 6.0.
50
*
51
* @param {goog.net.xpc.CrossPageChannel} channel The channel this transport
52
* belongs to.
53
* @param {goog.dom.DomHelper=} opt_domHelper The dom helper to use for finding
54
* the correct window.
55
* @constructor
56
* @extends {goog.net.xpc.Transport}
57
* @final
58
*/
59
goog.net.xpc.NixTransport = function(channel, opt_domHelper) {
60
goog.net.xpc.NixTransport.base(this, 'constructor', opt_domHelper);
61
62
/**
63
* The channel this transport belongs to.
64
* @type {goog.net.xpc.CrossPageChannel}
65
* @private
66
*/
67
this.channel_ = channel;
68
69
/**
70
* The authorization token, if any, used by this transport.
71
* @type {?string}
72
* @private
73
*/
74
this.authToken_ = channel[goog.net.xpc.CfgFields.AUTH_TOKEN] || '';
75
76
/**
77
* The authorization token, if any, that must be sent by the other party
78
* for setup to occur.
79
* @type {?string}
80
* @private
81
*/
82
this.remoteAuthToken_ =
83
channel[goog.net.xpc.CfgFields.REMOTE_AUTH_TOKEN] || '';
84
85
// Conduct the setup work for NIX in general, if need be.
86
goog.net.xpc.NixTransport.conductGlobalSetup_(this.getWindow());
87
88
// Setup aliases so that VBScript can call these methods
89
// on the transport class, even if they are renamed during
90
// compression.
91
this[goog.net.xpc.NixTransport.NIX_HANDLE_MESSAGE] = this.handleMessage_;
92
this[goog.net.xpc.NixTransport.NIX_CREATE_CHANNEL] = this.createChannel_;
93
};
94
goog.inherits(goog.net.xpc.NixTransport, goog.net.xpc.Transport);
95
96
97
// Consts for NIX. VBScript doesn't allow items to start with _ for some
98
// reason, so we need to make these names quite unique, as they will go into
99
// the global namespace.
100
101
102
/**
103
* Global name of the Wrapper VBScript class.
104
* Note that this class will be stored in the *global*
105
* namespace (i.e. window in browsers).
106
* @type {string}
107
*/
108
goog.net.xpc.NixTransport.NIX_WRAPPER = 'GCXPC____NIXVBS_wrapper';
109
110
111
/**
112
* Global name of the GetWrapper VBScript function. This
113
* constant is used by JavaScript to call this function.
114
* Note that this function will be stored in the *global*
115
* namespace (i.e. window in browsers).
116
* @type {string}
117
*/
118
goog.net.xpc.NixTransport.NIX_GET_WRAPPER = 'GCXPC____NIXVBS_get_wrapper';
119
120
121
/**
122
* The name of the handle message method used by the wrapper class
123
* when calling the transport.
124
* @type {string}
125
*/
126
goog.net.xpc.NixTransport.NIX_HANDLE_MESSAGE = 'GCXPC____NIXJS_handle_message';
127
128
129
/**
130
* The name of the create channel method used by the wrapper class
131
* when calling the transport.
132
* @type {string}
133
*/
134
goog.net.xpc.NixTransport.NIX_CREATE_CHANNEL = 'GCXPC____NIXJS_create_channel';
135
136
137
/**
138
* A "unique" identifier that is stored in the wrapper
139
* class so that the wrapper can be distinguished from
140
* other objects easily.
141
* @type {string}
142
*/
143
goog.net.xpc.NixTransport.NIX_ID_FIELD = 'GCXPC____NIXVBS_container';
144
145
146
/**
147
* Determines if the installed version of IE supports accessing window.opener
148
* after it has been set to a non-Window/null value. NIX relies on this being
149
* possible.
150
* @return {boolean} Whether window.opener behavior is compatible with NIX.
151
*/
152
goog.net.xpc.NixTransport.isNixSupported = function() {
153
var isSupported = false;
154
try {
155
var oldOpener = window.opener;
156
// The compiler complains (as it should!) if we set window.opener to
157
// something other than a window or null.
158
window.opener = /** @type {Window} */ ({});
159
isSupported = goog.reflect.canAccessProperty(window, 'opener');
160
window.opener = oldOpener;
161
} catch (e) {
162
}
163
return isSupported;
164
};
165
166
167
/**
168
* Conducts the global setup work for the NIX transport method.
169
* This function creates and then injects into the page the
170
* VBScript code necessary to create the NIX wrapper class.
171
* Note that this method can be called multiple times, as
172
* it internally checks whether the work is necessary before
173
* proceeding.
174
* @param {Window} listenWindow The window containing the affected page.
175
* @private
176
*/
177
goog.net.xpc.NixTransport.conductGlobalSetup_ = function(listenWindow) {
178
if (listenWindow['nix_setup_complete']) {
179
return;
180
}
181
182
// Inject the VBScript code needed.
183
var vbscript =
184
// We create a class to act as a wrapper for
185
// a Javascript call, to prevent a break in of
186
// the context.
187
'Class ' + goog.net.xpc.NixTransport.NIX_WRAPPER + '\n ' +
188
189
// An internal member for keeping track of the
190
// transport for which this wrapper exists.
191
'Private m_Transport\n' +
192
193
// An internal member for keeping track of the
194
// auth token associated with the context that
195
// created this wrapper. Used for validation
196
// purposes.
197
'Private m_Auth\n' +
198
199
// Method for internally setting the value
200
// of the m_Transport property. We have the
201
// isEmpty check to prevent the transport
202
// from being overridden with an illicit
203
// object by a malicious party.
204
'Public Sub SetTransport(transport)\n' +
205
'If isEmpty(m_Transport) Then\n' +
206
'Set m_Transport = transport\n' +
207
'End If\n' +
208
'End Sub\n' +
209
210
// Method for internally setting the value
211
// of the m_Auth property. We have the
212
// isEmpty check to prevent the transport
213
// from being overridden with an illicit
214
// object by a malicious party.
215
'Public Sub SetAuth(auth)\n' +
216
'If isEmpty(m_Auth) Then\n' +
217
'm_Auth = auth\n' +
218
'End If\n' +
219
'End Sub\n' +
220
221
// Returns the auth token to the gadget, so it can
222
// confirm a match before initiating the connection
223
'Public Function GetAuthToken()\n ' +
224
'GetAuthToken = m_Auth\n' +
225
'End Function\n' +
226
227
// A wrapper method which causes a
228
// message to be sent to the other context.
229
'Public Sub SendMessage(service, payload)\n ' +
230
'Call m_Transport.' + goog.net.xpc.NixTransport.NIX_HANDLE_MESSAGE +
231
'(service, payload)\n' +
232
'End Sub\n' +
233
234
// Method for setting up the inner->outer
235
// channel.
236
'Public Sub CreateChannel(channel)\n ' +
237
'Call m_Transport.' + goog.net.xpc.NixTransport.NIX_CREATE_CHANNEL +
238
'(channel)\n' +
239
'End Sub\n' +
240
241
// An empty field with a unique identifier to
242
// prevent the code from confusing this wrapper
243
// with a run-of-the-mill value found in window.opener.
244
'Public Sub ' + goog.net.xpc.NixTransport.NIX_ID_FIELD + '()\n ' +
245
'End Sub\n' +
246
'End Class\n ' +
247
248
// Function to get a reference to the wrapper.
249
'Function ' + goog.net.xpc.NixTransport.NIX_GET_WRAPPER +
250
'(transport, auth)\n' +
251
'Dim wrap\n' +
252
'Set wrap = New ' + goog.net.xpc.NixTransport.NIX_WRAPPER + '\n' +
253
'wrap.SetTransport transport\n' +
254
'wrap.SetAuth auth\n' +
255
'Set ' + goog.net.xpc.NixTransport.NIX_GET_WRAPPER + ' = wrap\n' +
256
'End Function';
257
258
try {
259
listenWindow.execScript(vbscript, 'vbscript');
260
listenWindow['nix_setup_complete'] = true;
261
} catch (e) {
262
goog.log.error(
263
goog.net.xpc.logger,
264
'exception caught while attempting global setup: ' + e);
265
}
266
};
267
268
269
/**
270
* The transport type.
271
* @type {number}
272
* @protected
273
* @override
274
*/
275
goog.net.xpc.NixTransport.prototype.transportType =
276
goog.net.xpc.TransportTypes.NIX;
277
278
279
/**
280
* Keeps track of whether the local setup has completed (i.e.
281
* the initial work towards setting the channel up has been
282
* completed for this end).
283
* @type {boolean}
284
* @private
285
*/
286
goog.net.xpc.NixTransport.prototype.localSetupCompleted_ = false;
287
288
289
/**
290
* The NIX channel used to talk to the other page. This
291
* object is in fact a reference to a VBScript class
292
* (see above) and as such, is in fact a COM wrapper.
293
* When using this object, make sure to not access methods
294
* without calling them, otherwise a COM error will be thrown.
295
* @type {Object}
296
* @private
297
*/
298
goog.net.xpc.NixTransport.prototype.nixChannel_ = null;
299
300
301
/**
302
* Connect this transport.
303
* @override
304
*/
305
goog.net.xpc.NixTransport.prototype.connect = function() {
306
if (this.channel_.getRole() == goog.net.xpc.CrossPageChannelRole.OUTER) {
307
this.attemptOuterSetup_();
308
} else {
309
this.attemptInnerSetup_();
310
}
311
};
312
313
314
/**
315
* Attempts to setup the channel from the perspective
316
* of the outer (read: container) page. This method
317
* will attempt to create a NIX wrapper for this transport
318
* and place it into the "opener" property of the inner
319
* page's window object. If it fails, it will continue
320
* to loop until it does so.
321
*
322
* @private
323
*/
324
goog.net.xpc.NixTransport.prototype.attemptOuterSetup_ = function() {
325
if (this.localSetupCompleted_) {
326
return;
327
}
328
329
// Get shortcut to iframe-element that contains the inner
330
// page.
331
var innerFrame = this.channel_.getIframeElement();
332
333
try {
334
// Attempt to place the NIX wrapper object into the inner
335
// frame's opener property.
336
var theWindow = this.getWindow();
337
var getWrapper = theWindow[goog.net.xpc.NixTransport.NIX_GET_WRAPPER];
338
innerFrame.contentWindow.opener = getWrapper(this, this.authToken_);
339
this.localSetupCompleted_ = true;
340
} catch (e) {
341
goog.log.error(
342
goog.net.xpc.logger, 'exception caught while attempting setup: ' + e);
343
}
344
345
// If the retry is necessary, reattempt this setup.
346
if (!this.localSetupCompleted_) {
347
this.getWindow().setTimeout(goog.bind(this.attemptOuterSetup_, this), 100);
348
}
349
};
350
351
352
/**
353
* Attempts to setup the channel from the perspective
354
* of the inner (read: iframe) page. This method
355
* will attempt to *read* the opener object from the
356
* page's opener property. If it succeeds, this object
357
* is saved into nixChannel_ and the channel is confirmed
358
* with the container by calling CreateChannel with an instance
359
* of a wrapper for *this* page. Note that if this method
360
* fails, it will continue to loop until it succeeds.
361
*
362
* @private
363
*/
364
goog.net.xpc.NixTransport.prototype.attemptInnerSetup_ = function() {
365
if (this.localSetupCompleted_) {
366
return;
367
}
368
369
try {
370
var opener = this.getWindow().opener;
371
372
// Ensure that the object contained inside the opener
373
// property is in fact a NIX wrapper.
374
if (opener && goog.net.xpc.NixTransport.NIX_ID_FIELD in opener) {
375
this.nixChannel_ = opener;
376
377
// Ensure that the NIX channel given to use is valid.
378
var remoteAuthToken = this.nixChannel_['GetAuthToken']();
379
380
if (remoteAuthToken != this.remoteAuthToken_) {
381
goog.log.error(
382
goog.net.xpc.logger, 'Invalid auth token from other party');
383
return;
384
}
385
386
// Complete the construction of the channel by sending our own
387
// wrapper to the container via the channel they gave us.
388
var theWindow = this.getWindow();
389
var getWrapper = theWindow[goog.net.xpc.NixTransport.NIX_GET_WRAPPER];
390
this.nixChannel_['CreateChannel'](getWrapper(this, this.authToken_));
391
392
this.localSetupCompleted_ = true;
393
394
// Notify channel that the transport is ready.
395
this.channel_.notifyConnected();
396
}
397
} catch (e) {
398
goog.log.error(
399
goog.net.xpc.logger, 'exception caught while attempting setup: ' + e);
400
return;
401
}
402
403
// If the retry is necessary, reattempt this setup.
404
if (!this.localSetupCompleted_) {
405
this.getWindow().setTimeout(goog.bind(this.attemptInnerSetup_, this), 100);
406
}
407
};
408
409
410
/**
411
* Internal method called by the inner page, via the
412
* NIX wrapper, to complete the setup of the channel.
413
*
414
* @param {Object} channel The NIX wrapper of the
415
* inner page.
416
* @private
417
*/
418
goog.net.xpc.NixTransport.prototype.createChannel_ = function(channel) {
419
// Verify that the channel is in fact a NIX wrapper.
420
if (typeof channel != 'unknown' ||
421
!(goog.net.xpc.NixTransport.NIX_ID_FIELD in channel)) {
422
goog.log.error(
423
goog.net.xpc.logger, 'Invalid NIX channel given to createChannel_');
424
}
425
426
this.nixChannel_ = channel;
427
428
// Ensure that the NIX channel given to use is valid.
429
var remoteAuthToken = this.nixChannel_['GetAuthToken']();
430
431
if (remoteAuthToken != this.remoteAuthToken_) {
432
goog.log.error(goog.net.xpc.logger, 'Invalid auth token from other party');
433
return;
434
}
435
436
// Indicate to the CrossPageChannel that the channel is setup
437
// and ready to use.
438
this.channel_.notifyConnected();
439
};
440
441
442
/**
443
* Internal method called by the other page, via the NIX wrapper,
444
* to deliver a message.
445
* @param {string} serviceName The name of the service the message is to be
446
* delivered to.
447
* @param {string} payload The message to process.
448
* @private
449
*/
450
goog.net.xpc.NixTransport.prototype.handleMessage_ = function(
451
serviceName, payload) {
452
/** @this {goog.net.xpc.NixTransport} */
453
var deliveryHandler = function() {
454
this.channel_.xpcDeliver(serviceName, payload);
455
};
456
this.getWindow().setTimeout(goog.bind(deliveryHandler, this), 1);
457
};
458
459
460
/**
461
* Sends a message.
462
* @param {string} service The name of the service the message is to be
463
* delivered to.
464
* @param {string} payload The message content.
465
* @override
466
*/
467
goog.net.xpc.NixTransport.prototype.send = function(service, payload) {
468
// Verify that the NIX channel we have is valid.
469
if (typeof(this.nixChannel_) !== 'unknown') {
470
goog.log.error(goog.net.xpc.logger, 'NIX channel not connected');
471
}
472
473
// Send the message via the NIX wrapper object.
474
this.nixChannel_['SendMessage'](service, payload);
475
};
476
477
478
/** @override */
479
goog.net.xpc.NixTransport.prototype.disposeInternal = function() {
480
goog.net.xpc.NixTransport.base(this, 'disposeInternal');
481
this.nixChannel_ = null;
482
};
483
484