Path: blob/trunk/third_party/closure/goog/net/xpc/nixtransport.js
1865 views
// Copyright 2008 The Closure Library Authors. All Rights Reserved.1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5//6// http://www.apache.org/licenses/LICENSE-2.07//8// Unless required by applicable law or agreed to in writing, software9// distributed under the License is distributed on an "AS-IS" BASIS,10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.11// See the License for the specific language governing permissions and12// limitations under the License.1314/**15* @fileoverview Contains the NIX (Native IE XDC) method transport for16* cross-domain communication. It exploits the fact that Internet Explorer17* allows a window that is the parent of an iframe to set said iframe window's18* opener property to an object. This object can be a function that in turn19* can be used to send a message despite same-origin constraints. Note that20* this function, if a pure JavaScript object, opens up the possibilitiy of21* gaining a hold of the context of the other window and in turn, attacking22* it. This implementation therefore wraps the JavaScript objects used inside23* a VBScript class. Since VBScript objects are passed in JavaScript as a COM24* wrapper (like DOM objects), they are thus opaque to JavaScript25* (except for the interface they expose). This therefore provides a safe26* method of transport.27*28*29* Initially based on FrameElementTransport which shares some similarities30* to this method.31*/3233goog.provide('goog.net.xpc.NixTransport');3435goog.require('goog.log');36goog.require('goog.net.xpc');37goog.require('goog.net.xpc.CfgFields');38goog.require('goog.net.xpc.CrossPageChannelRole');39goog.require('goog.net.xpc.Transport');40goog.require('goog.net.xpc.TransportTypes');41goog.require('goog.reflect');42434445/**46* NIX method transport.47*48* NOTE(user): NIX method tested in all IE versions starting from 6.0.49*50* @param {goog.net.xpc.CrossPageChannel} channel The channel this transport51* belongs to.52* @param {goog.dom.DomHelper=} opt_domHelper The dom helper to use for finding53* the correct window.54* @constructor55* @extends {goog.net.xpc.Transport}56* @final57*/58goog.net.xpc.NixTransport = function(channel, opt_domHelper) {59goog.net.xpc.NixTransport.base(this, 'constructor', opt_domHelper);6061/**62* The channel this transport belongs to.63* @type {goog.net.xpc.CrossPageChannel}64* @private65*/66this.channel_ = channel;6768/**69* The authorization token, if any, used by this transport.70* @type {?string}71* @private72*/73this.authToken_ = channel[goog.net.xpc.CfgFields.AUTH_TOKEN] || '';7475/**76* The authorization token, if any, that must be sent by the other party77* for setup to occur.78* @type {?string}79* @private80*/81this.remoteAuthToken_ =82channel[goog.net.xpc.CfgFields.REMOTE_AUTH_TOKEN] || '';8384// Conduct the setup work for NIX in general, if need be.85goog.net.xpc.NixTransport.conductGlobalSetup_(this.getWindow());8687// Setup aliases so that VBScript can call these methods88// on the transport class, even if they are renamed during89// compression.90this[goog.net.xpc.NixTransport.NIX_HANDLE_MESSAGE] = this.handleMessage_;91this[goog.net.xpc.NixTransport.NIX_CREATE_CHANNEL] = this.createChannel_;92};93goog.inherits(goog.net.xpc.NixTransport, goog.net.xpc.Transport);949596// Consts for NIX. VBScript doesn't allow items to start with _ for some97// reason, so we need to make these names quite unique, as they will go into98// the global namespace.99100101/**102* Global name of the Wrapper VBScript class.103* Note that this class will be stored in the *global*104* namespace (i.e. window in browsers).105* @type {string}106*/107goog.net.xpc.NixTransport.NIX_WRAPPER = 'GCXPC____NIXVBS_wrapper';108109110/**111* Global name of the GetWrapper VBScript function. This112* constant is used by JavaScript to call this function.113* Note that this function will be stored in the *global*114* namespace (i.e. window in browsers).115* @type {string}116*/117goog.net.xpc.NixTransport.NIX_GET_WRAPPER = 'GCXPC____NIXVBS_get_wrapper';118119120/**121* The name of the handle message method used by the wrapper class122* when calling the transport.123* @type {string}124*/125goog.net.xpc.NixTransport.NIX_HANDLE_MESSAGE = 'GCXPC____NIXJS_handle_message';126127128/**129* The name of the create channel method used by the wrapper class130* when calling the transport.131* @type {string}132*/133goog.net.xpc.NixTransport.NIX_CREATE_CHANNEL = 'GCXPC____NIXJS_create_channel';134135136/**137* A "unique" identifier that is stored in the wrapper138* class so that the wrapper can be distinguished from139* other objects easily.140* @type {string}141*/142goog.net.xpc.NixTransport.NIX_ID_FIELD = 'GCXPC____NIXVBS_container';143144145/**146* Determines if the installed version of IE supports accessing window.opener147* after it has been set to a non-Window/null value. NIX relies on this being148* possible.149* @return {boolean} Whether window.opener behavior is compatible with NIX.150*/151goog.net.xpc.NixTransport.isNixSupported = function() {152var isSupported = false;153try {154var oldOpener = window.opener;155// The compiler complains (as it should!) if we set window.opener to156// something other than a window or null.157window.opener = /** @type {Window} */ ({});158isSupported = goog.reflect.canAccessProperty(window, 'opener');159window.opener = oldOpener;160} catch (e) {161}162return isSupported;163};164165166/**167* Conducts the global setup work for the NIX transport method.168* This function creates and then injects into the page the169* VBScript code necessary to create the NIX wrapper class.170* Note that this method can be called multiple times, as171* it internally checks whether the work is necessary before172* proceeding.173* @param {Window} listenWindow The window containing the affected page.174* @private175*/176goog.net.xpc.NixTransport.conductGlobalSetup_ = function(listenWindow) {177if (listenWindow['nix_setup_complete']) {178return;179}180181// Inject the VBScript code needed.182var vbscript =183// We create a class to act as a wrapper for184// a Javascript call, to prevent a break in of185// the context.186'Class ' + goog.net.xpc.NixTransport.NIX_WRAPPER + '\n ' +187188// An internal member for keeping track of the189// transport for which this wrapper exists.190'Private m_Transport\n' +191192// An internal member for keeping track of the193// auth token associated with the context that194// created this wrapper. Used for validation195// purposes.196'Private m_Auth\n' +197198// Method for internally setting the value199// of the m_Transport property. We have the200// isEmpty check to prevent the transport201// from being overridden with an illicit202// object by a malicious party.203'Public Sub SetTransport(transport)\n' +204'If isEmpty(m_Transport) Then\n' +205'Set m_Transport = transport\n' +206'End If\n' +207'End Sub\n' +208209// Method for internally setting the value210// of the m_Auth property. We have the211// isEmpty check to prevent the transport212// from being overridden with an illicit213// object by a malicious party.214'Public Sub SetAuth(auth)\n' +215'If isEmpty(m_Auth) Then\n' +216'm_Auth = auth\n' +217'End If\n' +218'End Sub\n' +219220// Returns the auth token to the gadget, so it can221// confirm a match before initiating the connection222'Public Function GetAuthToken()\n ' +223'GetAuthToken = m_Auth\n' +224'End Function\n' +225226// A wrapper method which causes a227// message to be sent to the other context.228'Public Sub SendMessage(service, payload)\n ' +229'Call m_Transport.' + goog.net.xpc.NixTransport.NIX_HANDLE_MESSAGE +230'(service, payload)\n' +231'End Sub\n' +232233// Method for setting up the inner->outer234// channel.235'Public Sub CreateChannel(channel)\n ' +236'Call m_Transport.' + goog.net.xpc.NixTransport.NIX_CREATE_CHANNEL +237'(channel)\n' +238'End Sub\n' +239240// An empty field with a unique identifier to241// prevent the code from confusing this wrapper242// with a run-of-the-mill value found in window.opener.243'Public Sub ' + goog.net.xpc.NixTransport.NIX_ID_FIELD + '()\n ' +244'End Sub\n' +245'End Class\n ' +246247// Function to get a reference to the wrapper.248'Function ' + goog.net.xpc.NixTransport.NIX_GET_WRAPPER +249'(transport, auth)\n' +250'Dim wrap\n' +251'Set wrap = New ' + goog.net.xpc.NixTransport.NIX_WRAPPER + '\n' +252'wrap.SetTransport transport\n' +253'wrap.SetAuth auth\n' +254'Set ' + goog.net.xpc.NixTransport.NIX_GET_WRAPPER + ' = wrap\n' +255'End Function';256257try {258listenWindow.execScript(vbscript, 'vbscript');259listenWindow['nix_setup_complete'] = true;260} catch (e) {261goog.log.error(262goog.net.xpc.logger,263'exception caught while attempting global setup: ' + e);264}265};266267268/**269* The transport type.270* @type {number}271* @protected272* @override273*/274goog.net.xpc.NixTransport.prototype.transportType =275goog.net.xpc.TransportTypes.NIX;276277278/**279* Keeps track of whether the local setup has completed (i.e.280* the initial work towards setting the channel up has been281* completed for this end).282* @type {boolean}283* @private284*/285goog.net.xpc.NixTransport.prototype.localSetupCompleted_ = false;286287288/**289* The NIX channel used to talk to the other page. This290* object is in fact a reference to a VBScript class291* (see above) and as such, is in fact a COM wrapper.292* When using this object, make sure to not access methods293* without calling them, otherwise a COM error will be thrown.294* @type {Object}295* @private296*/297goog.net.xpc.NixTransport.prototype.nixChannel_ = null;298299300/**301* Connect this transport.302* @override303*/304goog.net.xpc.NixTransport.prototype.connect = function() {305if (this.channel_.getRole() == goog.net.xpc.CrossPageChannelRole.OUTER) {306this.attemptOuterSetup_();307} else {308this.attemptInnerSetup_();309}310};311312313/**314* Attempts to setup the channel from the perspective315* of the outer (read: container) page. This method316* will attempt to create a NIX wrapper for this transport317* and place it into the "opener" property of the inner318* page's window object. If it fails, it will continue319* to loop until it does so.320*321* @private322*/323goog.net.xpc.NixTransport.prototype.attemptOuterSetup_ = function() {324if (this.localSetupCompleted_) {325return;326}327328// Get shortcut to iframe-element that contains the inner329// page.330var innerFrame = this.channel_.getIframeElement();331332try {333// Attempt to place the NIX wrapper object into the inner334// frame's opener property.335var theWindow = this.getWindow();336var getWrapper = theWindow[goog.net.xpc.NixTransport.NIX_GET_WRAPPER];337innerFrame.contentWindow.opener = getWrapper(this, this.authToken_);338this.localSetupCompleted_ = true;339} catch (e) {340goog.log.error(341goog.net.xpc.logger, 'exception caught while attempting setup: ' + e);342}343344// If the retry is necessary, reattempt this setup.345if (!this.localSetupCompleted_) {346this.getWindow().setTimeout(goog.bind(this.attemptOuterSetup_, this), 100);347}348};349350351/**352* Attempts to setup the channel from the perspective353* of the inner (read: iframe) page. This method354* will attempt to *read* the opener object from the355* page's opener property. If it succeeds, this object356* is saved into nixChannel_ and the channel is confirmed357* with the container by calling CreateChannel with an instance358* of a wrapper for *this* page. Note that if this method359* fails, it will continue to loop until it succeeds.360*361* @private362*/363goog.net.xpc.NixTransport.prototype.attemptInnerSetup_ = function() {364if (this.localSetupCompleted_) {365return;366}367368try {369var opener = this.getWindow().opener;370371// Ensure that the object contained inside the opener372// property is in fact a NIX wrapper.373if (opener && goog.net.xpc.NixTransport.NIX_ID_FIELD in opener) {374this.nixChannel_ = opener;375376// Ensure that the NIX channel given to use is valid.377var remoteAuthToken = this.nixChannel_['GetAuthToken']();378379if (remoteAuthToken != this.remoteAuthToken_) {380goog.log.error(381goog.net.xpc.logger, 'Invalid auth token from other party');382return;383}384385// Complete the construction of the channel by sending our own386// wrapper to the container via the channel they gave us.387var theWindow = this.getWindow();388var getWrapper = theWindow[goog.net.xpc.NixTransport.NIX_GET_WRAPPER];389this.nixChannel_['CreateChannel'](getWrapper(this, this.authToken_));390391this.localSetupCompleted_ = true;392393// Notify channel that the transport is ready.394this.channel_.notifyConnected();395}396} catch (e) {397goog.log.error(398goog.net.xpc.logger, 'exception caught while attempting setup: ' + e);399return;400}401402// If the retry is necessary, reattempt this setup.403if (!this.localSetupCompleted_) {404this.getWindow().setTimeout(goog.bind(this.attemptInnerSetup_, this), 100);405}406};407408409/**410* Internal method called by the inner page, via the411* NIX wrapper, to complete the setup of the channel.412*413* @param {Object} channel The NIX wrapper of the414* inner page.415* @private416*/417goog.net.xpc.NixTransport.prototype.createChannel_ = function(channel) {418// Verify that the channel is in fact a NIX wrapper.419if (typeof channel != 'unknown' ||420!(goog.net.xpc.NixTransport.NIX_ID_FIELD in channel)) {421goog.log.error(422goog.net.xpc.logger, 'Invalid NIX channel given to createChannel_');423}424425this.nixChannel_ = channel;426427// Ensure that the NIX channel given to use is valid.428var remoteAuthToken = this.nixChannel_['GetAuthToken']();429430if (remoteAuthToken != this.remoteAuthToken_) {431goog.log.error(goog.net.xpc.logger, 'Invalid auth token from other party');432return;433}434435// Indicate to the CrossPageChannel that the channel is setup436// and ready to use.437this.channel_.notifyConnected();438};439440441/**442* Internal method called by the other page, via the NIX wrapper,443* to deliver a message.444* @param {string} serviceName The name of the service the message is to be445* delivered to.446* @param {string} payload The message to process.447* @private448*/449goog.net.xpc.NixTransport.prototype.handleMessage_ = function(450serviceName, payload) {451/** @this {goog.net.xpc.NixTransport} */452var deliveryHandler = function() {453this.channel_.xpcDeliver(serviceName, payload);454};455this.getWindow().setTimeout(goog.bind(deliveryHandler, this), 1);456};457458459/**460* Sends a message.461* @param {string} service The name of the service the message is to be462* delivered to.463* @param {string} payload The message content.464* @override465*/466goog.net.xpc.NixTransport.prototype.send = function(service, payload) {467// Verify that the NIX channel we have is valid.468if (typeof(this.nixChannel_) !== 'unknown') {469goog.log.error(goog.net.xpc.logger, 'NIX channel not connected');470}471472// Send the message via the NIX wrapper object.473this.nixChannel_['SendMessage'](service, payload);474};475476477/** @override */478goog.net.xpc.NixTransport.prototype.disposeInternal = function() {479goog.net.xpc.NixTransport.base(this, 'disposeInternal');480this.nixChannel_ = null;481};482483484