sku-pg
Version:
Skulpt is a Javascript implementation of Python 2.x. Python that runs in your browser!
279 lines (231 loc) • 7.83 kB
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 iframe relay tranport.
*
*/
goog.provide('goog.net.xpc.IframeRelayTransport');
goog.require('goog.dom');
goog.require('goog.events');
goog.require('goog.net.xpc');
goog.require('goog.net.xpc.Transport');
goog.require('goog.userAgent');
/**
* Iframe relay transport. Creates hidden iframes containing a document
* from the peer's origin. Data is transferred in the fragment identifier.
* Therefore the document loaded in the iframes can be served from the
* browser's cache.
*
* @param {goog.net.xpc.CrossPageChannel} channel The channel this
* transport belongs to.
* @constructor
* @extends {goog.net.xpc.Transport}
*/
goog.net.xpc.IframeRelayTransport = function(channel) {
/**
* The channel this transport belongs to.
* @type {goog.net.xpc.CrossPageChannel}
* @private
*/
this.channel_ = channel;
/**
* The URI used to relay data to the peer.
* @type {string}
* @private
*/
this.peerRelayUri_ =
this.channel_.cfg_[goog.net.xpc.CfgFields.PEER_RELAY_URI];
/**
* The id of the iframe the peer page lives in.
* @type {string}
* @private
*/
this.peerIframeId_ = this.channel_.cfg_[goog.net.xpc.CfgFields.IFRAME_ID];
if (goog.userAgent.WEBKIT) {
goog.net.xpc.IframeRelayTransport.startCleanupTimer_();
}
};
goog.inherits(goog.net.xpc.IframeRelayTransport, goog.net.xpc.Transport);
if (goog.userAgent.WEBKIT) {
/**
* Array to keep references to the relay-iframes. Used only if
* there is no way to detect when the iframes are loaded. In that
* case the relay-iframes are removed after a timeout.
* @type {Array.<Object>}
* @private
*/
goog.net.xpc.IframeRelayTransport.iframeRefs_ = [];
/**
* Interval at which iframes are destroyed.
* @type {number}
* @private
*/
goog.net.xpc.IframeRelayTransport.CLEANUP_INTERVAL_ = 1000;
/**
* Time after which a relay-iframe is destroyed.
* @private
*/
goog.net.xpc.IframeRelayTransport.IFRAME_MAX_AGE_ = 3000;
/**
* The cleanup timer id.
* @type {number}
* @private
*/
goog.net.xpc.IframeRelayTransport.cleanupTimer_ = 0;
/**
* Starts the cleanup timer.
* @private
*/
goog.net.xpc.IframeRelayTransport.startCleanupTimer_ = function() {
if (!goog.net.xpc.IframeRelayTransport.cleanupTimer_) {
goog.net.xpc.IframeRelayTransport.cleanupTimer_ = window.setTimeout(
function() { goog.net.xpc.IframeRelayTransport.cleanup_(); },
goog.net.xpc.IframeRelayTransport.CLEANUP_INTERVAL_);
}
};
/**
* Remove all relay-iframes which are older than the maximal age.
* @param {number=} opt_maxAge The maximal age in milliseconds.
* @private
*/
goog.net.xpc.IframeRelayTransport.cleanup_ = function(opt_maxAge) {
var now = goog.now();
var maxAge =
opt_maxAge || goog.net.xpc.IframeRelayTransport.IFRAME_MAX_AGE_;
while (goog.net.xpc.IframeRelayTransport.iframeRefs_.length &&
now - goog.net.xpc.IframeRelayTransport.iframeRefs_[0].timestamp >=
maxAge) {
var ifr = goog.net.xpc.IframeRelayTransport.iframeRefs_.
shift().iframeElement;
goog.dom.removeNode(ifr);
goog.net.xpc.logger.finest('iframe removed');
}
goog.net.xpc.IframeRelayTransport.cleanupTimer_ = window.setTimeout(
goog.net.xpc.IframeRelayTransport.cleanupCb_,
goog.net.xpc.IframeRelayTransport.CLEANUP_INTERVAL_);
};
/**
* Function which wraps cleanup_().
* @private
*/
goog.net.xpc.IframeRelayTransport.cleanupCb_ = function() {
goog.net.xpc.IframeRelayTransport.cleanup_();
};
}
/**
* The transport type.
* @type {number}
*/
goog.net.xpc.IframeRelayTransport.prototype.transportType =
goog.net.xpc.TransportTypes.IFRAME_RELAY;
/**
* Connects this transport.
*/
goog.net.xpc.IframeRelayTransport.prototype.connect = function() {
this.send(goog.net.xpc.TRANSPORT_SERVICE_, goog.net.xpc.SETUP);
};
/**
* Handles transport service messages (internal signalling).
* @param {string} payload The message content.
*/
goog.net.xpc.IframeRelayTransport.prototype.transportServiceHandler =
function(payload) {
if (payload == goog.net.xpc.SETUP) {
// TODO(user) Safari swallows the SETUP_ACK from the iframe to the
// container after hitting reload.
this.send(goog.net.xpc.TRANSPORT_SERVICE_, goog.net.xpc.SETUP_ACK_);
this.channel_.notifyConnected_();
}
else if (payload == goog.net.xpc.SETUP_ACK_) {
this.channel_.notifyConnected_();
}
};
/**
* Sends a message.
*
* @param {string} service Name of service this the message has to be delivered.
* @param {string} payload The message content.
*/
goog.net.xpc.IframeRelayTransport.prototype.send = function(service, payload) {
// IE requires that we create the onload attribute inline, otherwise the
// handler is not triggered
if (goog.userAgent.IE) {
var div = document.createElement('div');
div.innerHTML = '<iframe onload="this.xpcOnload()"></iframe>';
var ifr = div.childNodes[0];
div = null;
ifr.xpcOnload = goog.net.xpc.IframeRelayTransport.iframeLoadHandler_;
} else {
var ifr = document.createElement('iframe');
if (goog.userAgent.WEBKIT) {
// safari doesn't fire load-events on iframes.
// keep a reference and remove after a timeout.
goog.net.xpc.IframeRelayTransport.iframeRefs_.push({
timestamp: goog.now(),
iframeElement: ifr
});
} else {
goog.events.listen(ifr, 'load',
goog.net.xpc.IframeRelayTransport.iframeLoadHandler_);
}
}
var style = ifr.style;
style.visibility = 'hidden';
style.width = ifr.style.height = '0px';
style.position = 'absolute';
// TODO(user) Split payload in multiple parts (frames) in case we are
// in IE and the constructed URL exceeds IE's 4K-limit.
var url = this.peerRelayUri_;
url += '#' + this.channel_.name;
if (this.peerIframeId_) {
url += ',' + this.peerIframeId_;
}
url += '|' + service + ':' + encodeURIComponent(payload);
ifr.src = url;
document.body.appendChild(ifr);
goog.net.xpc.logger.finest('msg sent: ' + url);
};
/**
* The iframe load handler. Gets called as method on the iframe element.
* @private
* @this Element
*/
goog.net.xpc.IframeRelayTransport.iframeLoadHandler_ = function() {
goog.net.xpc.logger.finest('iframe-load');
goog.dom.removeNode(this);
this.xpcOnload = null;
};
/**
* Processes an incoming message.
*
* @param {string} channelName The name of the channel.
* @param {string} frame The raw frame content.
*/
window['xpcRelay'] =
function(channelName, frame) {
var pos = frame.indexOf(':');
var service = frame.substring(0, pos);
var payload = frame.substring(pos + 1);
goog.net.xpc.channels_[channelName].deliver_(service,
decodeURIComponent(payload));
};
/**
* Disposes of the transport.
*/
goog.net.xpc.IframeRelayTransport.prototype.disposeInternal = function() {
goog.net.xpc.IframeRelayTransport.superClass_.disposeInternal.call(this);
if (goog.userAgent.WEBKIT) {
goog.net.xpc.IframeRelayTransport.cleanup_(0);
}
};