255 lines
7.2 KiB
JavaScript
255 lines
7.2 KiB
JavaScript
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS-IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
/**
|
|
* @fileoverview Contains the frame element method transport for cross-domain
|
|
* communication. It exploits the fact that FF lets a page in an
|
|
* iframe call a method on the iframe-element it is contained in, even if the
|
|
* containing page is from a different domain.
|
|
*
|
|
*/
|
|
|
|
|
|
goog.provide('goog.net.xpc.FrameElementMethodTransport');
|
|
|
|
goog.require('goog.net.xpc');
|
|
goog.require('goog.net.xpc.CrossPageChannelRole');
|
|
goog.require('goog.net.xpc.Transport');
|
|
|
|
|
|
|
|
/**
|
|
* Frame-element method transport.
|
|
*
|
|
* Firefox allows a document within an iframe to call methods on the
|
|
* iframe-element added by the containing document.
|
|
* NOTE(user): Tested in all FF versions starting from 1.0
|
|
*
|
|
* @param {goog.net.xpc.CrossPageChannel} channel The channel this transport
|
|
* belongs to.
|
|
* @param {goog.dom.DomHelper=} opt_domHelper The dom helper to use for finding
|
|
* the correct window.
|
|
* @constructor
|
|
* @extends {goog.net.xpc.Transport}
|
|
*/
|
|
goog.net.xpc.FrameElementMethodTransport = function(channel, opt_domHelper) {
|
|
goog.base(this, opt_domHelper);
|
|
|
|
/**
|
|
* The channel this transport belongs to.
|
|
* @type {goog.net.xpc.CrossPageChannel}
|
|
* @private
|
|
*/
|
|
this.channel_ = channel;
|
|
|
|
// To transfer messages, this transport basically uses normal function calls,
|
|
// which are synchronous. To avoid endless recursion, the delivery has to
|
|
// be artificially made asynchronous.
|
|
|
|
/**
|
|
* Array for queued messages.
|
|
* @type {Array}
|
|
* @private
|
|
*/
|
|
this.queue_ = [];
|
|
|
|
/**
|
|
* Callback function which wraps deliverQueued_.
|
|
* @type {Function}
|
|
* @private
|
|
*/
|
|
this.deliverQueuedCb_ = goog.bind(this.deliverQueued_, this);
|
|
};
|
|
goog.inherits(goog.net.xpc.FrameElementMethodTransport, goog.net.xpc.Transport);
|
|
|
|
|
|
/**
|
|
* The transport type.
|
|
* @type {number}
|
|
* @protected
|
|
* @override
|
|
*/
|
|
goog.net.xpc.FrameElementMethodTransport.prototype.transportType =
|
|
goog.net.xpc.TransportTypes.FRAME_ELEMENT_METHOD;
|
|
|
|
|
|
/**
|
|
* Flag used to enforce asynchronous messaging semantics.
|
|
* @type {boolean}
|
|
* @private
|
|
*/
|
|
goog.net.xpc.FrameElementMethodTransport.prototype.recursive_ = false;
|
|
|
|
|
|
/**
|
|
* Timer used to enforce asynchronous message delivery.
|
|
* @type {number}
|
|
* @private
|
|
*/
|
|
goog.net.xpc.FrameElementMethodTransport.prototype.timer_ = 0;
|
|
|
|
|
|
/**
|
|
* Holds the function to send messages to the peer
|
|
* (once it becomes available).
|
|
* @type {Function}
|
|
* @private
|
|
*/
|
|
goog.net.xpc.FrameElementMethodTransport.outgoing_ = null;
|
|
|
|
|
|
/**
|
|
* Connect this transport.
|
|
* @override
|
|
*/
|
|
goog.net.xpc.FrameElementMethodTransport.prototype.connect = function() {
|
|
if (this.channel_.getRole() == goog.net.xpc.CrossPageChannelRole.OUTER) {
|
|
// get shortcut to iframe-element
|
|
this.iframeElm_ = this.channel_.getIframeElement();
|
|
|
|
// add the gateway function to the iframe-element
|
|
// (to be called by the peer)
|
|
this.iframeElm_['XPC_toOuter'] = goog.bind(this.incoming_, this);
|
|
|
|
// at this point we just have to wait for a notification from the peer...
|
|
|
|
} else {
|
|
this.attemptSetup_();
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Only used from within an iframe. Attempts to attach the method
|
|
* to be used for sending messages by the containing document. Has to
|
|
* wait until the containing document has finished. Therefore calls
|
|
* itself in a timeout if not successful.
|
|
* @private
|
|
*/
|
|
goog.net.xpc.FrameElementMethodTransport.prototype.attemptSetup_ = function() {
|
|
var retry = true;
|
|
/** @preserveTry */
|
|
try {
|
|
if (!this.iframeElm_) {
|
|
// throws security exception when called too early
|
|
this.iframeElm_ = this.getWindow().frameElement;
|
|
}
|
|
// check if iframe-element and the gateway-function to the
|
|
// outer-frame are present
|
|
// TODO(user) Make sure the following code doesn't throw any exceptions
|
|
if (this.iframeElm_ && this.iframeElm_['XPC_toOuter']) {
|
|
// get a reference to the gateway function
|
|
this.outgoing_ = this.iframeElm_['XPC_toOuter'];
|
|
// attach the gateway function the other document will use
|
|
this.iframeElm_['XPC_toOuter']['XPC_toInner'] =
|
|
goog.bind(this.incoming_, this);
|
|
// stop retrying
|
|
retry = false;
|
|
// notify outer frame
|
|
this.send(goog.net.xpc.TRANSPORT_SERVICE_, goog.net.xpc.SETUP_ACK_);
|
|
// notify channel that the transport is ready
|
|
this.channel_.notifyConnected();
|
|
}
|
|
}
|
|
catch (e) {
|
|
goog.log.error(goog.net.xpc.logger,
|
|
'exception caught while attempting setup: ' + e);
|
|
}
|
|
// retry necessary?
|
|
if (retry) {
|
|
if (!this.attemptSetupCb_) {
|
|
this.attemptSetupCb_ = goog.bind(this.attemptSetup_, this);
|
|
}
|
|
this.getWindow().setTimeout(this.attemptSetupCb_, 100);
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Handles transport service messages.
|
|
* @param {string} payload The message content.
|
|
* @override
|
|
*/
|
|
goog.net.xpc.FrameElementMethodTransport.prototype.transportServiceHandler =
|
|
function(payload) {
|
|
if (this.channel_.getRole() == goog.net.xpc.CrossPageChannelRole.OUTER &&
|
|
!this.channel_.isConnected() && payload == goog.net.xpc.SETUP_ACK_) {
|
|
// get a reference to the gateway function
|
|
this.outgoing_ = this.iframeElm_['XPC_toOuter']['XPC_toInner'];
|
|
// notify the channel we're ready
|
|
this.channel_.notifyConnected();
|
|
} else {
|
|
throw Error('Got unexpected transport message.');
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Process incoming message.
|
|
* @param {string} serviceName The name of the service the message is to be
|
|
* delivered to.
|
|
* @param {string} payload The message to process.
|
|
* @private
|
|
*/
|
|
goog.net.xpc.FrameElementMethodTransport.prototype.incoming_ =
|
|
function(serviceName, payload) {
|
|
if (!this.recursive_ && this.queue_.length == 0) {
|
|
this.channel_.xpcDeliver(serviceName, payload);
|
|
}
|
|
else {
|
|
this.queue_.push({serviceName: serviceName, payload: payload});
|
|
if (this.queue_.length == 1) {
|
|
this.timer_ = this.getWindow().setTimeout(this.deliverQueuedCb_, 1);
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Delivers queued messages.
|
|
* @private
|
|
*/
|
|
goog.net.xpc.FrameElementMethodTransport.prototype.deliverQueued_ =
|
|
function() {
|
|
while (this.queue_.length) {
|
|
var msg = this.queue_.shift();
|
|
this.channel_.xpcDeliver(msg.serviceName, msg.payload);
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Send a message
|
|
* @param {string} service The name off the service the message is to be
|
|
* delivered to.
|
|
* @param {string} payload The message content.
|
|
* @override
|
|
*/
|
|
goog.net.xpc.FrameElementMethodTransport.prototype.send =
|
|
function(service, payload) {
|
|
this.recursive_ = true;
|
|
this.outgoing_(service, payload);
|
|
this.recursive_ = false;
|
|
};
|
|
|
|
|
|
/** @override */
|
|
goog.net.xpc.FrameElementMethodTransport.prototype.disposeInternal =
|
|
function() {
|
|
goog.net.xpc.FrameElementMethodTransport.superClass_.disposeInternal.call(
|
|
this);
|
|
this.outgoing_ = null;
|
|
this.iframeElm_ = null;
|
|
};
|