Path: blob/trunk/third_party/closure/goog/net/xpc/frameelementmethodtransport.js
1865 views
// Copyright 2007 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 frame element method transport for cross-domain16* communication. It exploits the fact that FF lets a page in an17* iframe call a method on the iframe-element it is contained in, even if the18* containing page is from a different domain.19*20*/212223goog.provide('goog.net.xpc.FrameElementMethodTransport');2425goog.require('goog.log');26goog.require('goog.net.xpc');27goog.require('goog.net.xpc.CrossPageChannelRole');28goog.require('goog.net.xpc.Transport');29goog.require('goog.net.xpc.TransportTypes');30313233/**34* Frame-element method transport.35*36* Firefox allows a document within an iframe to call methods on the37* iframe-element added by the containing document.38* NOTE(user): Tested in all FF versions starting from 1.039*40* @param {goog.net.xpc.CrossPageChannel} channel The channel this transport41* belongs to.42* @param {goog.dom.DomHelper=} opt_domHelper The dom helper to use for finding43* the correct window.44* @constructor45* @extends {goog.net.xpc.Transport}46* @final47*/48goog.net.xpc.FrameElementMethodTransport = function(channel, opt_domHelper) {49goog.net.xpc.FrameElementMethodTransport.base(50this, 'constructor', opt_domHelper);5152/**53* The channel this transport belongs to.54* @type {goog.net.xpc.CrossPageChannel}55* @private56*/57this.channel_ = channel;5859// To transfer messages, this transport basically uses normal function calls,60// which are synchronous. To avoid endless recursion, the delivery has to61// be artificially made asynchronous.6263/**64* Array for queued messages.65* @type {Array<{serviceName: string, payload: string}>}66* @private67*/68this.queue_ = [];6970/**71* Callback function which wraps deliverQueued_.72* @type {Function}73* @private74*/75this.deliverQueuedCb_ = goog.bind(this.deliverQueued_, this);76};77goog.inherits(goog.net.xpc.FrameElementMethodTransport, goog.net.xpc.Transport);787980/**81* The transport type.82* @type {number}83* @protected84* @override85*/86goog.net.xpc.FrameElementMethodTransport.prototype.transportType =87goog.net.xpc.TransportTypes.FRAME_ELEMENT_METHOD;888990/** @private {!Function|undefined} */91goog.net.xpc.FrameElementMethodTransport.prototype.attemptSetupCb_;929394/** @private */95goog.net.xpc.FrameElementMethodTransport.prototype.outgoing_;969798/** @private */99goog.net.xpc.FrameElementMethodTransport.prototype.iframeElm_;100101102/**103* Flag used to enforce asynchronous messaging semantics.104* @type {boolean}105* @private106*/107goog.net.xpc.FrameElementMethodTransport.prototype.recursive_ = false;108109110/**111* Holds the function to send messages to the peer112* (once it becomes available).113* @type {Function}114* @private115*/116goog.net.xpc.FrameElementMethodTransport.outgoing_ = null;117118119/**120* Connect this transport.121* @override122*/123goog.net.xpc.FrameElementMethodTransport.prototype.connect = function() {124if (this.channel_.getRole() == goog.net.xpc.CrossPageChannelRole.OUTER) {125// get shortcut to iframe-element126this.iframeElm_ = this.channel_.getIframeElement();127128// add the gateway function to the iframe-element129// (to be called by the peer)130this.iframeElm_['XPC_toOuter'] = goog.bind(this.incoming_, this);131132// at this point we just have to wait for a notification from the peer...133134} else {135this.attemptSetup_();136}137};138139140/**141* Only used from within an iframe. Attempts to attach the method142* to be used for sending messages by the containing document. Has to143* wait until the containing document has finished. Therefore calls144* itself in a timeout if not successful.145* @private146*/147goog.net.xpc.FrameElementMethodTransport.prototype.attemptSetup_ = function() {148var retry = true;149150try {151if (!this.iframeElm_) {152// throws security exception when called too early153this.iframeElm_ = this.getWindow().frameElement;154}155// check if iframe-element and the gateway-function to the156// outer-frame are present157// TODO(user) Make sure the following code doesn't throw any exceptions158if (this.iframeElm_ && this.iframeElm_['XPC_toOuter']) {159// get a reference to the gateway function160this.outgoing_ = this.iframeElm_['XPC_toOuter'];161// attach the gateway function the other document will use162this.iframeElm_['XPC_toOuter']['XPC_toInner'] =163goog.bind(this.incoming_, this);164// stop retrying165retry = false;166// notify outer frame167this.send(goog.net.xpc.TRANSPORT_SERVICE_, goog.net.xpc.SETUP_ACK_);168// notify channel that the transport is ready169this.channel_.notifyConnected();170}171} catch (e) {172goog.log.error(173goog.net.xpc.logger, 'exception caught while attempting setup: ' + e);174}175// retry necessary?176if (retry) {177if (!this.attemptSetupCb_) {178this.attemptSetupCb_ = goog.bind(this.attemptSetup_, this);179}180this.getWindow().setTimeout(this.attemptSetupCb_, 100);181}182};183184185/**186* Handles transport service messages.187* @param {string} payload The message content.188* @override189*/190goog.net.xpc.FrameElementMethodTransport.prototype.transportServiceHandler =191function(payload) {192if (this.channel_.getRole() == goog.net.xpc.CrossPageChannelRole.OUTER &&193!this.channel_.isConnected() && payload == goog.net.xpc.SETUP_ACK_) {194// get a reference to the gateway function195this.outgoing_ = this.iframeElm_['XPC_toOuter']['XPC_toInner'];196// notify the channel we're ready197this.channel_.notifyConnected();198} else {199throw Error('Got unexpected transport message.');200}201};202203204/**205* Process incoming message.206* @param {string} serviceName The name of the service the message is to be207* delivered to.208* @param {string} payload The message to process.209* @private210*/211goog.net.xpc.FrameElementMethodTransport.prototype.incoming_ = function(212serviceName, payload) {213if (!this.recursive_ && this.queue_.length == 0) {214this.channel_.xpcDeliver(serviceName, payload);215} else {216this.queue_.push({serviceName: serviceName, payload: payload});217if (this.queue_.length == 1) {218this.getWindow().setTimeout(this.deliverQueuedCb_, 1);219}220}221};222223224/**225* Delivers queued messages.226* @private227*/228goog.net.xpc.FrameElementMethodTransport.prototype.deliverQueued_ = function() {229while (this.queue_.length) {230var msg = this.queue_.shift();231this.channel_.xpcDeliver(msg.serviceName, msg.payload);232}233};234235236/**237* Send a message238* @param {string} service The name off the service the message is to be239* delivered to.240* @param {string} payload The message content.241* @override242*/243goog.net.xpc.FrameElementMethodTransport.prototype.send = function(244service, payload) {245this.recursive_ = true;246this.outgoing_(service, payload);247this.recursive_ = false;248};249250251/** @override */252goog.net.xpc.FrameElementMethodTransport.prototype.disposeInternal =253function() {254goog.net.xpc.FrameElementMethodTransport.superClass_.disposeInternal.call(255this);256this.outgoing_ = null;257this.iframeElm_ = null;258};259260261