Path: blob/trunk/third_party/closure/goog/messaging/bufferedchannel.js
2868 views
// Copyright 2010 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 A wrapper for asynchronous message-passing channels that buffer16* their output until both ends of the channel are connected.17*18*/1920goog.provide('goog.messaging.BufferedChannel');2122goog.require('goog.Disposable');23goog.require('goog.Timer');24goog.require('goog.events');25goog.require('goog.log');26goog.require('goog.messaging.MessageChannel');27goog.require('goog.messaging.MultiChannel');28293031/**32* Creates a new BufferedChannel, which operates like its underlying channel33* except that it buffers calls to send until it receives a message from its34* peer claiming that the peer is ready to receive. The peer is also expected35* to be a BufferedChannel, though this is not enforced.36*37* @param {!goog.messaging.MessageChannel} messageChannel The MessageChannel38* we're wrapping.39* @param {number=} opt_interval Polling interval for sending ready40* notifications to peer, in ms. Default is 50.41* @constructor42* @extends {goog.Disposable}43* @implements {goog.messaging.MessageChannel};44* @final45*/46goog.messaging.BufferedChannel = function(messageChannel, opt_interval) {47goog.Disposable.call(this);4849/**50* Buffer of messages to be sent when the channel's peer is ready.51*52* @type {Array<Object>}53* @private54*/55this.buffer_ = [];5657/**58* Channel dispatcher wrapping the underlying delegate channel.59*60* @type {!goog.messaging.MultiChannel}61* @private62*/63this.multiChannel_ = new goog.messaging.MultiChannel(messageChannel);6465/**66* Virtual channel for carrying the user's messages.67*68* @type {!goog.messaging.MessageChannel}69* @private70*/71this.userChannel_ = this.multiChannel_.createVirtualChannel(72goog.messaging.BufferedChannel.USER_CHANNEL_NAME_);7374/**75* Virtual channel for carrying control messages for BufferedChannel.76*77* @type {!goog.messaging.MessageChannel}78* @private79*/80this.controlChannel_ = this.multiChannel_.createVirtualChannel(81goog.messaging.BufferedChannel.CONTROL_CHANNEL_NAME_);8283/**84* Timer for the peer ready ping loop.85*86* @type {goog.Timer}87* @private88*/89this.timer_ = new goog.Timer(90opt_interval || goog.messaging.BufferedChannel.DEFAULT_INTERVAL_MILLIS_);9192this.timer_.start();93goog.events.listen(94this.timer_, goog.Timer.TICK, this.sendReadyPing_, false, this);9596this.controlChannel_.registerService(97goog.messaging.BufferedChannel.PEER_READY_SERVICE_NAME_,98goog.bind(this.setPeerReady_, this));99};100goog.inherits(goog.messaging.BufferedChannel, goog.Disposable);101102103/**104* Default polling interval (in ms) for setPeerReady_ notifications.105*106* @type {number}107* @const108* @private109*/110goog.messaging.BufferedChannel.DEFAULT_INTERVAL_MILLIS_ = 50;111112113/**114* The name of the private service which handles peer ready pings. The115* service registered with this name is bound to this.setPeerReady_, an internal116* part of BufferedChannel's implementation that clients should not send to117* directly.118*119* @type {string}120* @const121* @private122*/123goog.messaging.BufferedChannel.PEER_READY_SERVICE_NAME_ = 'setPeerReady_';124125126/**127* The name of the virtual channel along which user messages are sent.128*129* @type {string}130* @const131* @private132*/133goog.messaging.BufferedChannel.USER_CHANNEL_NAME_ = 'user';134135136/**137* The name of the virtual channel along which internal control messages are138* sent.139*140* @type {string}141* @const142* @private143*/144goog.messaging.BufferedChannel.CONTROL_CHANNEL_NAME_ = 'control';145146147/** @override */148goog.messaging.BufferedChannel.prototype.connect = function(opt_connectCb) {149if (opt_connectCb) {150opt_connectCb();151}152};153154155/** @override */156goog.messaging.BufferedChannel.prototype.isConnected = function() {157return true;158};159160161/**162* @return {boolean} Whether the channel's peer is ready.163*/164goog.messaging.BufferedChannel.prototype.isPeerReady = function() {165return this.peerReady_;166};167168169/**170* Logger.171*172* @type {goog.log.Logger}173* @const174* @private175*/176goog.messaging.BufferedChannel.prototype.logger_ =177goog.log.getLogger('goog.messaging.bufferedchannel');178179180/**181* Handles one tick of our peer ready notification loop. This entails sending a182* ready ping to the peer and shutting down the loop if we've received a ping183* ourselves.184*185* @private186*/187goog.messaging.BufferedChannel.prototype.sendReadyPing_ = function() {188try {189this.controlChannel_.send(190goog.messaging.BufferedChannel.PEER_READY_SERVICE_NAME_,191/* payload */ this.isPeerReady() ? '1' : '');192} catch (e) {193this.timer_.stop(); // So we don't keep calling send and re-throwing.194throw e;195}196};197198199/**200* Whether or not the peer channel is ready to receive messages.201*202* @type {boolean}203* @private204*/205goog.messaging.BufferedChannel.prototype.peerReady_;206207208/** @override */209goog.messaging.BufferedChannel.prototype.registerService = function(210serviceName, callback, opt_objectPayload) {211this.userChannel_.registerService(serviceName, callback, opt_objectPayload);212};213214215/** @override */216goog.messaging.BufferedChannel.prototype.registerDefaultService = function(217callback) {218this.userChannel_.registerDefaultService(callback);219};220221222/**223* Send a message over the channel. If the peer is not ready, the message will224* be buffered and sent once we've received a ready message from our peer.225*226* @param {string} serviceName The name of the service this message should be227* delivered to.228* @param {string|!Object} payload The value of the message. If this is an229* Object, it is serialized to JSON before sending. It's the responsibility230* of implementors of this class to perform the serialization.231* @see goog.net.xpc.BufferedChannel.send232* @override233*/234goog.messaging.BufferedChannel.prototype.send = function(serviceName, payload) {235if (this.isPeerReady()) {236this.userChannel_.send(serviceName, payload);237} else {238goog.log.fine(239goog.messaging.BufferedChannel.prototype.logger_,240'buffering message ' + serviceName);241this.buffer_.push({serviceName: serviceName, payload: payload});242}243};244245246/**247* Marks the channel's peer as ready, then sends buffered messages and nulls the248* buffer. Subsequent calls to setPeerReady_ have no effect.249*250* @param {(!Object|string)} peerKnowsWeKnowItsReady Passed by the peer to251* indicate whether it knows that we've received its ping and that it's252* ready. Non-empty if true, empty if false.253* @private254*/255goog.messaging.BufferedChannel.prototype.setPeerReady_ = function(256peerKnowsWeKnowItsReady) {257if (peerKnowsWeKnowItsReady) {258this.timer_.stop();259} else {260// Our peer doesn't know we're ready, so restart (or continue) pinging.261// Restarting may be needed if the peer iframe was reloaded after the262// connection was first established.263this.timer_.start();264}265266if (this.peerReady_) {267return;268}269this.peerReady_ = true;270// Send one last ping so that the peer knows we know it's ready.271this.sendReadyPing_();272for (var i = 0; i < this.buffer_.length; i++) {273var message = this.buffer_[i];274goog.log.fine(275goog.messaging.BufferedChannel.prototype.logger_,276'sending buffered message ' + message.serviceName);277this.userChannel_.send(message.serviceName, message.payload);278}279this.buffer_ = null;280};281282283/** @override */284goog.messaging.BufferedChannel.prototype.disposeInternal = function() {285goog.dispose(this.multiChannel_);286goog.dispose(this.timer_);287goog.messaging.BufferedChannel.base(this, 'disposeInternal');288};289290291