Path: blob/trunk/third_party/closure/goog/messaging/portcaller.js
2868 views
// Copyright 2011 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 The leaf node of a {@link goog.messaging.PortNetwork}. Callers16* connect to the operator, and request connections with other contexts from it.17*18*/1920goog.provide('goog.messaging.PortCaller');2122goog.require('goog.Disposable');23goog.require('goog.async.Deferred');24goog.require('goog.messaging.DeferredChannel');25goog.require('goog.messaging.PortChannel');26goog.require('goog.messaging.PortNetwork'); // interface27goog.require('goog.object');28293031/**32* The leaf node of a network.33*34* @param {!goog.messaging.MessageChannel} operatorPort The channel for35* communicating with the operator. The other side of this channel should be36* passed to {@link goog.messaging.PortOperator#addPort}. Must be either a37* {@link goog.messaging.PortChannel} or a decorator wrapping a PortChannel;38* in particular, it must be able to send and receive {@link MessagePort}s.39* @constructor40* @extends {goog.Disposable}41* @implements {goog.messaging.PortNetwork}42* @final43*/44goog.messaging.PortCaller = function(operatorPort) {45goog.messaging.PortCaller.base(this, 'constructor');4647/**48* The channel to the {@link goog.messaging.PortOperator} for this network.49*50* @type {!goog.messaging.MessageChannel}51* @private52*/53this.operatorPort_ = operatorPort;5455/**56* The collection of channels for communicating with other contexts in the57* network. Each value can contain a {@link goog.aync.Deferred} and/or a58* {@link goog.messaging.MessageChannel}.59*60* If the value contains a Deferred, then the channel is a61* {@link goog.messaging.DeferredChannel} wrapping that Deferred. The Deferred62* will be resolved with a {@link goog.messaging.PortChannel} once we receive63* the appropriate port from the operator. This is the situation when this64* caller requests a connection to another context; the DeferredChannel is65* used to queue up messages until we receive the port from the operator.66*67* If the value does not contain a Deferred, then the channel is simply a68* {@link goog.messaging.PortChannel} communicating with the given context.69* This is the situation when this context received a port for the other70* context before it was requested.71*72* If a value exists for a given key, it must contain a channel, but it73* doesn't necessarily contain a Deferred.74*75* @type {!Object<{deferred: goog.async.Deferred,76* channel: !goog.messaging.MessageChannel}>}77* @private78*/79this.connections_ = {};8081this.operatorPort_.registerService(82goog.messaging.PortNetwork.GRANT_CONNECTION_SERVICE,83goog.bind(this.connectionGranted_, this), true /* opt_json */);84};85goog.inherits(goog.messaging.PortCaller, goog.Disposable);868788/** @override */89goog.messaging.PortCaller.prototype.dial = function(name) {90if (name in this.connections_) {91return this.connections_[name].channel;92}9394this.operatorPort_.send(95goog.messaging.PortNetwork.REQUEST_CONNECTION_SERVICE, name);96var deferred = new goog.async.Deferred();97var channel = new goog.messaging.DeferredChannel(deferred);98this.connections_[name] = {deferred: deferred, channel: channel};99return channel;100};101102103/**104* Registers a connection to another context in the network. This is called when105* the operator sends us one end of a {@link MessageChannel}, either because106* this caller requested a connection with another context, or because that107* context requested a connection with this caller.108*109* It's possible that the remote context and this one request each other roughly110* concurrently. The operator doesn't keep track of which contexts have been111* connected, so it will create two separate {@link MessageChannel}s in this112* case. However, the first channel created will reach both contexts first, so113* we simply ignore all connections with a given context after the first.114*115* @param {!Object|string} message The name of the context116* being connected and the port connecting the context.117* @private118*/119goog.messaging.PortCaller.prototype.connectionGranted_ = function(message) {120var args = /** @type {{name: string, port: MessagePort}} */ (message);121var port = args['port'];122var entry = this.connections_[args['name']];123if (entry && (!entry.deferred || entry.deferred.hasFired())) {124// If two PortCallers request one another at the same time, the operator may125// send out a channel for connecting them multiple times. Since both callers126// will receive the first channel's ports first, we can safely ignore and127// close any future ports.128port.close();129} else if (!args['success']) {130throw Error(args['message']);131} else {132port.start();133var channel = new goog.messaging.PortChannel(port);134if (entry) {135entry.deferred.callback(channel);136} else {137this.connections_[args['name']] = {channel: channel, deferred: null};138}139}140};141142143/** @override */144goog.messaging.PortCaller.prototype.disposeInternal = function() {145goog.dispose(this.operatorPort_);146goog.object.forEach(this.connections_, goog.dispose);147delete this.operatorPort_;148delete this.connections_;149goog.messaging.PortCaller.base(this, 'disposeInternal');150};151152153