Update wmts-hidpi, add nicer-api-docs
This commit is contained in:
@@ -0,0 +1,210 @@
|
||||
// Copyright 2010 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 An abstract superclass for message channels that handles the
|
||||
* repetitive details of registering and dispatching to services. This is more
|
||||
* useful for full-fledged channels than for decorators, since decorators
|
||||
* generally delegate service registering anyway.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
goog.provide('goog.messaging.AbstractChannel');
|
||||
|
||||
goog.require('goog.Disposable');
|
||||
goog.require('goog.debug');
|
||||
goog.require('goog.json');
|
||||
goog.require('goog.log');
|
||||
goog.require('goog.messaging.MessageChannel'); // interface
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates an abstract message channel.
|
||||
*
|
||||
* @constructor
|
||||
* @extends {goog.Disposable}
|
||||
* @implements {goog.messaging.MessageChannel}
|
||||
*/
|
||||
goog.messaging.AbstractChannel = function() {
|
||||
goog.base(this);
|
||||
|
||||
/**
|
||||
* The services registered for this channel.
|
||||
* @type {Object.<string, {callback: function((string|!Object)),
|
||||
objectPayload: boolean}>}
|
||||
* @private
|
||||
*/
|
||||
this.services_ = {};
|
||||
};
|
||||
goog.inherits(goog.messaging.AbstractChannel, goog.Disposable);
|
||||
|
||||
|
||||
/**
|
||||
* The default service to be run when no other services match.
|
||||
*
|
||||
* @type {?function(string, (string|!Object))}
|
||||
* @private
|
||||
*/
|
||||
goog.messaging.AbstractChannel.prototype.defaultService_;
|
||||
|
||||
|
||||
/**
|
||||
* Logger for this class.
|
||||
* @type {goog.log.Logger}
|
||||
* @protected
|
||||
*/
|
||||
goog.messaging.AbstractChannel.prototype.logger =
|
||||
goog.log.getLogger('goog.messaging.AbstractChannel');
|
||||
|
||||
|
||||
/**
|
||||
* Immediately calls opt_connectCb if given, and is otherwise a no-op. If
|
||||
* subclasses have configuration that needs to happen before the channel is
|
||||
* connected, they should override this and {@link #isConnected}.
|
||||
* @override
|
||||
*/
|
||||
goog.messaging.AbstractChannel.prototype.connect = function(opt_connectCb) {
|
||||
if (opt_connectCb) {
|
||||
opt_connectCb();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Always returns true. If subclasses have configuration that needs to happen
|
||||
* before the channel is connected, they should override this and
|
||||
* {@link #connect}.
|
||||
* @override
|
||||
*/
|
||||
goog.messaging.AbstractChannel.prototype.isConnected = function() {
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.messaging.AbstractChannel.prototype.registerService =
|
||||
function(serviceName, callback, opt_objectPayload) {
|
||||
this.services_[serviceName] = {
|
||||
callback: callback,
|
||||
objectPayload: !!opt_objectPayload
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.messaging.AbstractChannel.prototype.registerDefaultService =
|
||||
function(callback) {
|
||||
this.defaultService_ = callback;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.messaging.AbstractChannel.prototype.send = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Delivers a message to the appropriate service. This is meant to be called by
|
||||
* subclasses when they receive messages.
|
||||
*
|
||||
* This method takes into account both explicitly-registered and default
|
||||
* services, as well as making sure that JSON payloads are decoded when
|
||||
* necessary. If the subclass is capable of passing objects as payloads, those
|
||||
* objects can be passed in to this method directly. Otherwise, the (potentially
|
||||
* JSON-encoded) strings should be passed in.
|
||||
*
|
||||
* @param {string} serviceName The name of the service receiving the message.
|
||||
* @param {string|!Object} payload The contents of the message.
|
||||
* @protected
|
||||
*/
|
||||
goog.messaging.AbstractChannel.prototype.deliver = function(
|
||||
serviceName, payload) {
|
||||
var service = this.getService(serviceName, payload);
|
||||
if (!service) {
|
||||
return;
|
||||
}
|
||||
|
||||
var decodedPayload =
|
||||
this.decodePayload(serviceName, payload, service.objectPayload);
|
||||
if (goog.isDefAndNotNull(decodedPayload)) {
|
||||
service.callback(decodedPayload);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Find the service object for a given service name. If there's no service
|
||||
* explicitly registered, but there is a default service, a service object is
|
||||
* constructed for it.
|
||||
*
|
||||
* @param {string} serviceName The name of the service receiving the message.
|
||||
* @param {string|!Object} payload The contents of the message.
|
||||
* @return {?{callback: function((string|!Object)), objectPayload: boolean}} The
|
||||
* service object for the given service, or null if none was found.
|
||||
* @protected
|
||||
*/
|
||||
goog.messaging.AbstractChannel.prototype.getService = function(
|
||||
serviceName, payload) {
|
||||
var service = this.services_[serviceName];
|
||||
if (service) {
|
||||
return service;
|
||||
} else if (this.defaultService_) {
|
||||
var callback = goog.partial(this.defaultService_, serviceName);
|
||||
var objectPayload = goog.isObject(payload);
|
||||
return {callback: callback, objectPayload: objectPayload};
|
||||
}
|
||||
|
||||
goog.log.warning(this.logger, 'Unknown service name "' + serviceName + '"');
|
||||
return null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Converts the message payload into the format expected by the registered
|
||||
* service (either JSON or string).
|
||||
*
|
||||
* @param {string} serviceName The name of the service receiving the message.
|
||||
* @param {string|!Object} payload The contents of the message.
|
||||
* @param {boolean} objectPayload Whether the service expects an object or a
|
||||
* plain string.
|
||||
* @return {string|Object} The payload in the format expected by the service, or
|
||||
* null if something went wrong.
|
||||
* @protected
|
||||
*/
|
||||
goog.messaging.AbstractChannel.prototype.decodePayload = function(
|
||||
serviceName, payload, objectPayload) {
|
||||
if (objectPayload && goog.isString(payload)) {
|
||||
try {
|
||||
return goog.json.parse(payload);
|
||||
} catch (err) {
|
||||
goog.log.warning(this.logger,
|
||||
'Expected JSON payload for ' + serviceName +
|
||||
', was "' + payload + '"');
|
||||
return null;
|
||||
}
|
||||
} else if (!objectPayload && !goog.isString(payload)) {
|
||||
return goog.json.serialize(payload);
|
||||
}
|
||||
return payload;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.messaging.AbstractChannel.prototype.disposeInternal = function() {
|
||||
goog.base(this, 'disposeInternal');
|
||||
delete this.logger;
|
||||
delete this.services_;
|
||||
delete this.defaultService_;
|
||||
};
|
||||
@@ -0,0 +1,287 @@
|
||||
// Copyright 2010 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 A wrapper for asynchronous message-passing channels that buffer
|
||||
* their output until both ends of the channel are connected.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.messaging.BufferedChannel');
|
||||
|
||||
goog.require('goog.Timer');
|
||||
goog.require('goog.Uri');
|
||||
goog.require('goog.debug.Error');
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.log');
|
||||
goog.require('goog.messaging.MessageChannel');
|
||||
goog.require('goog.messaging.MultiChannel');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new BufferedChannel, which operates like its underlying channel
|
||||
* except that it buffers calls to send until it receives a message from its
|
||||
* peer claiming that the peer is ready to receive. The peer is also expected
|
||||
* to be a BufferedChannel, though this is not enforced.
|
||||
*
|
||||
* @param {!goog.messaging.MessageChannel} messageChannel The MessageChannel
|
||||
* we're wrapping.
|
||||
* @param {number=} opt_interval Polling interval for sending ready
|
||||
* notifications to peer, in ms. Default is 50.
|
||||
* @constructor
|
||||
* @extends {goog.Disposable}
|
||||
* @implements {goog.messaging.MessageChannel};
|
||||
*/
|
||||
goog.messaging.BufferedChannel = function(messageChannel, opt_interval) {
|
||||
goog.Disposable.call(this);
|
||||
|
||||
/**
|
||||
* Buffer of messages to be sent when the channel's peer is ready.
|
||||
*
|
||||
* @type {Array.<Object>}
|
||||
* @private
|
||||
*/
|
||||
this.buffer_ = [];
|
||||
|
||||
/**
|
||||
* Channel dispatcher wrapping the underlying delegate channel.
|
||||
*
|
||||
* @type {!goog.messaging.MultiChannel}
|
||||
* @private
|
||||
*/
|
||||
this.multiChannel_ = new goog.messaging.MultiChannel(messageChannel);
|
||||
|
||||
/**
|
||||
* Virtual channel for carrying the user's messages.
|
||||
*
|
||||
* @type {!goog.messaging.MessageChannel}
|
||||
* @private
|
||||
*/
|
||||
this.userChannel_ = this.multiChannel_.createVirtualChannel(
|
||||
goog.messaging.BufferedChannel.USER_CHANNEL_NAME_);
|
||||
|
||||
/**
|
||||
* Virtual channel for carrying control messages for BufferedChannel.
|
||||
*
|
||||
* @type {!goog.messaging.MessageChannel}
|
||||
* @private
|
||||
*/
|
||||
this.controlChannel_ = this.multiChannel_.createVirtualChannel(
|
||||
goog.messaging.BufferedChannel.CONTROL_CHANNEL_NAME_);
|
||||
|
||||
/**
|
||||
* Timer for the peer ready ping loop.
|
||||
*
|
||||
* @type {goog.Timer}
|
||||
* @private
|
||||
*/
|
||||
this.timer_ = new goog.Timer(
|
||||
opt_interval || goog.messaging.BufferedChannel.DEFAULT_INTERVAL_MILLIS_);
|
||||
|
||||
this.timer_.start();
|
||||
goog.events.listen(
|
||||
this.timer_, goog.Timer.TICK, this.sendReadyPing_, false, this);
|
||||
|
||||
this.controlChannel_.registerService(
|
||||
goog.messaging.BufferedChannel.PEER_READY_SERVICE_NAME_,
|
||||
goog.bind(this.setPeerReady_, this));
|
||||
};
|
||||
goog.inherits(goog.messaging.BufferedChannel, goog.Disposable);
|
||||
|
||||
|
||||
/**
|
||||
* Default polling interval (in ms) for setPeerReady_ notifications.
|
||||
*
|
||||
* @type {number}
|
||||
* @const
|
||||
* @private
|
||||
*/
|
||||
goog.messaging.BufferedChannel.DEFAULT_INTERVAL_MILLIS_ = 50;
|
||||
|
||||
|
||||
/**
|
||||
* The name of the private service which handles peer ready pings. The
|
||||
* service registered with this name is bound to this.setPeerReady_, an internal
|
||||
* part of BufferedChannel's implementation that clients should not send to
|
||||
* directly.
|
||||
*
|
||||
* @type {string}
|
||||
* @const
|
||||
* @private
|
||||
*/
|
||||
goog.messaging.BufferedChannel.PEER_READY_SERVICE_NAME_ = 'setPeerReady_';
|
||||
|
||||
|
||||
/**
|
||||
* The name of the virtual channel along which user messages are sent.
|
||||
*
|
||||
* @type {string}
|
||||
* @const
|
||||
* @private
|
||||
*/
|
||||
goog.messaging.BufferedChannel.USER_CHANNEL_NAME_ = 'user';
|
||||
|
||||
|
||||
/**
|
||||
* The name of the virtual channel along which internal control messages are
|
||||
* sent.
|
||||
*
|
||||
* @type {string}
|
||||
* @const
|
||||
* @private
|
||||
*/
|
||||
goog.messaging.BufferedChannel.CONTROL_CHANNEL_NAME_ = 'control';
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.messaging.BufferedChannel.prototype.connect = function(opt_connectCb) {
|
||||
if (opt_connectCb) {
|
||||
opt_connectCb();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.messaging.BufferedChannel.prototype.isConnected = function() {
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {boolean} Whether the channel's peer is ready.
|
||||
*/
|
||||
goog.messaging.BufferedChannel.prototype.isPeerReady = function() {
|
||||
return this.peerReady_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Logger.
|
||||
*
|
||||
* @type {goog.log.Logger}
|
||||
* @const
|
||||
* @private
|
||||
*/
|
||||
goog.messaging.BufferedChannel.prototype.logger_ = goog.log.getLogger(
|
||||
'goog.messaging.bufferedchannel');
|
||||
|
||||
|
||||
/**
|
||||
* Handles one tick of our peer ready notification loop. This entails sending a
|
||||
* ready ping to the peer and shutting down the loop if we've received a ping
|
||||
* ourselves.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
goog.messaging.BufferedChannel.prototype.sendReadyPing_ = function() {
|
||||
try {
|
||||
this.controlChannel_.send(
|
||||
goog.messaging.BufferedChannel.PEER_READY_SERVICE_NAME_,
|
||||
/* payload */ this.isPeerReady() ? '1' : '');
|
||||
} catch (e) {
|
||||
this.timer_.stop(); // So we don't keep calling send and re-throwing.
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Whether or not the peer channel is ready to receive messages.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.messaging.BufferedChannel.prototype.peerReady_;
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.messaging.BufferedChannel.prototype.registerService = function(
|
||||
serviceName, callback, opt_objectPayload) {
|
||||
this.userChannel_.registerService(serviceName, callback, opt_objectPayload);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.messaging.BufferedChannel.prototype.registerDefaultService = function(
|
||||
callback) {
|
||||
this.userChannel_.registerDefaultService(callback);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Send a message over the channel. If the peer is not ready, the message will
|
||||
* be buffered and sent once we've received a ready message from our peer.
|
||||
*
|
||||
* @param {string} serviceName The name of the service this message should be
|
||||
* delivered to.
|
||||
* @param {string|!Object} payload The value of the message. If this is an
|
||||
* Object, it is serialized to JSON before sending. It's the responsibility
|
||||
* of implementors of this class to perform the serialization.
|
||||
* @see goog.net.xpc.BufferedChannel.send
|
||||
* @override
|
||||
*/
|
||||
goog.messaging.BufferedChannel.prototype.send = function(serviceName, payload) {
|
||||
if (this.isPeerReady()) {
|
||||
this.userChannel_.send(serviceName, payload);
|
||||
} else {
|
||||
goog.log.fine(goog.messaging.BufferedChannel.prototype.logger_,
|
||||
'buffering message ' + serviceName);
|
||||
this.buffer_.push({serviceName: serviceName, payload: payload});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Marks the channel's peer as ready, then sends buffered messages and nulls the
|
||||
* buffer. Subsequent calls to setPeerReady_ have no effect.
|
||||
*
|
||||
* @param {(!Object|string)} peerKnowsWeKnowItsReady Passed by the peer to
|
||||
* indicate whether it knows that we've received its ping and that it's
|
||||
* ready. Non-empty if true, empty if false.
|
||||
* @private
|
||||
*/
|
||||
goog.messaging.BufferedChannel.prototype.setPeerReady_ = function(
|
||||
peerKnowsWeKnowItsReady) {
|
||||
if (peerKnowsWeKnowItsReady) {
|
||||
this.timer_.stop();
|
||||
} else {
|
||||
// Our peer doesn't know we're ready, so restart (or continue) pinging.
|
||||
// Restarting may be needed if the peer iframe was reloaded after the
|
||||
// connection was first established.
|
||||
this.timer_.start();
|
||||
}
|
||||
|
||||
if (this.peerReady_) {
|
||||
return;
|
||||
}
|
||||
this.peerReady_ = true;
|
||||
// Send one last ping so that the peer knows we know it's ready.
|
||||
this.sendReadyPing_();
|
||||
for (var i = 0; i < this.buffer_.length; i++) {
|
||||
var message = this.buffer_[i];
|
||||
goog.log.fine(goog.messaging.BufferedChannel.prototype.logger_,
|
||||
'sending buffered message ' + message.serviceName);
|
||||
this.userChannel_.send(message.serviceName, message.payload);
|
||||
}
|
||||
this.buffer_ = null;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.messaging.BufferedChannel.prototype.disposeInternal = function() {
|
||||
goog.dispose(this.multiChannel_);
|
||||
goog.dispose(this.timer_);
|
||||
goog.base(this, 'disposeInternal');
|
||||
};
|
||||
@@ -0,0 +1,98 @@
|
||||
// Copyright 2010 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 A MessageChannel decorator that wraps a deferred MessageChannel
|
||||
* and enqueues messages and service registrations until that channel exists.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.messaging.DeferredChannel');
|
||||
|
||||
goog.require('goog.Disposable');
|
||||
goog.require('goog.async.Deferred');
|
||||
goog.require('goog.messaging.MessageChannel'); // interface
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new DeferredChannel, which wraps a deferred MessageChannel and
|
||||
* enqueues messages to be sent once the wrapped channel is resolved.
|
||||
*
|
||||
* @param {!goog.async.Deferred} deferredChannel The underlying deferred
|
||||
* MessageChannel.
|
||||
* @constructor
|
||||
* @extends {goog.Disposable}
|
||||
* @implements {goog.messaging.MessageChannel}
|
||||
*/
|
||||
goog.messaging.DeferredChannel = function(deferredChannel) {
|
||||
goog.base(this);
|
||||
this.deferred_ = deferredChannel;
|
||||
};
|
||||
goog.inherits(goog.messaging.DeferredChannel, goog.Disposable);
|
||||
|
||||
|
||||
/**
|
||||
* Cancels the wrapped Deferred.
|
||||
*/
|
||||
goog.messaging.DeferredChannel.prototype.cancel = function() {
|
||||
this.deferred_.cancel();
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.messaging.DeferredChannel.prototype.connect = function(opt_connectCb) {
|
||||
if (opt_connectCb) {
|
||||
opt_connectCb();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.messaging.DeferredChannel.prototype.isConnected = function() {
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.messaging.DeferredChannel.prototype.registerService = function(
|
||||
serviceName, callback, opt_objectPayload) {
|
||||
this.deferred_.addCallback(function(resolved) {
|
||||
resolved.registerService(serviceName, callback, opt_objectPayload);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.messaging.DeferredChannel.prototype.registerDefaultService =
|
||||
function(callback) {
|
||||
this.deferred_.addCallback(function(resolved) {
|
||||
resolved.registerDefaultService(callback);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.messaging.DeferredChannel.prototype.send = function(serviceName, payload) {
|
||||
this.deferred_.addCallback(function(resolved) {
|
||||
resolved.send(serviceName, payload);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.messaging.DeferredChannel.prototype.disposeInternal = function() {
|
||||
this.cancel();
|
||||
goog.base(this, 'disposeInternal');
|
||||
};
|
||||
@@ -0,0 +1,131 @@
|
||||
// Copyright 2010 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 This class sends logging messages over a message channel to a
|
||||
* server on the main page that prints them using standard logging mechanisms.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.messaging.LoggerClient');
|
||||
|
||||
goog.require('goog.Disposable');
|
||||
goog.require('goog.debug');
|
||||
goog.require('goog.debug.LogManager');
|
||||
goog.require('goog.debug.Logger');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a logger client that sends messages along a message channel for the
|
||||
* remote end to log. The remote end of the channel should use a
|
||||
* {goog.messaging.LoggerServer} with the same service name.
|
||||
*
|
||||
* @param {!goog.messaging.MessageChannel} channel The channel that on which to
|
||||
* send the log messages.
|
||||
* @param {string} serviceName The name of the logging service to use.
|
||||
* @constructor
|
||||
* @extends {goog.Disposable}
|
||||
*/
|
||||
goog.messaging.LoggerClient = function(channel, serviceName) {
|
||||
if (goog.messaging.LoggerClient.instance_) {
|
||||
return goog.messaging.LoggerClient.instance_;
|
||||
}
|
||||
|
||||
goog.base(this);
|
||||
|
||||
/**
|
||||
* The channel on which to send the log messages.
|
||||
* @type {!goog.messaging.MessageChannel}
|
||||
* @private
|
||||
*/
|
||||
this.channel_ = channel;
|
||||
|
||||
/**
|
||||
* The name of the logging service to use.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.serviceName_ = serviceName;
|
||||
|
||||
/**
|
||||
* The bound handler function for handling log messages. This is kept in a
|
||||
* variable so that it can be deregistered when the logger client is disposed.
|
||||
* @type {Function}
|
||||
* @private
|
||||
*/
|
||||
this.publishHandler_ = goog.bind(this.sendLog_, this);
|
||||
goog.debug.LogManager.getRoot().addHandler(this.publishHandler_);
|
||||
|
||||
goog.messaging.LoggerClient.instance_ = this;
|
||||
};
|
||||
goog.inherits(goog.messaging.LoggerClient, goog.Disposable);
|
||||
|
||||
|
||||
/**
|
||||
* The singleton instance, if any.
|
||||
* @type {goog.messaging.LoggerClient}
|
||||
* @private
|
||||
*/
|
||||
goog.messaging.LoggerClient.instance_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Sends a log message through the channel.
|
||||
* @param {!goog.debug.LogRecord} logRecord The log message.
|
||||
* @private
|
||||
*/
|
||||
goog.messaging.LoggerClient.prototype.sendLog_ = function(logRecord) {
|
||||
var name = logRecord.getLoggerName();
|
||||
var level = logRecord.getLevel();
|
||||
var msg = logRecord.getMessage();
|
||||
var originalException = logRecord.getException();
|
||||
|
||||
var exception;
|
||||
if (originalException) {
|
||||
var normalizedException =
|
||||
goog.debug.normalizeErrorObject(originalException);
|
||||
exception = {
|
||||
'name': normalizedException.name,
|
||||
'message': normalizedException.message,
|
||||
'lineNumber': normalizedException.lineNumber,
|
||||
'fileName': normalizedException.fileName,
|
||||
// Normalized exceptions without a stack have 'stack' set to 'Not
|
||||
// available', so we check for the existance of 'stack' on the original
|
||||
// exception instead.
|
||||
'stack': originalException.stack ||
|
||||
goog.debug.getStacktrace(goog.debug.Logger.prototype.log)
|
||||
};
|
||||
|
||||
if (goog.isObject(originalException)) {
|
||||
// Add messageN to the exception in case it was added using
|
||||
// goog.debug.enhanceError.
|
||||
for (var i = 0; 'message' + i in originalException; i++) {
|
||||
exception['message' + i] = String(originalException['message' + i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.channel_.send(this.serviceName_, {
|
||||
'name': name, 'level': level.value, 'message': msg, 'exception': exception
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.messaging.LoggerClient.prototype.disposeInternal = function() {
|
||||
goog.base(this, 'disposeInternal');
|
||||
goog.debug.LogManager.getRoot().removeHandler(this.publishHandler_);
|
||||
delete this.channel_;
|
||||
goog.messaging.LoggerClient.instance_ = null;
|
||||
};
|
||||
@@ -0,0 +1,98 @@
|
||||
// Copyright 2010 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 This class listens on a message channel for logger commands and
|
||||
* logs them on the local page. This is useful when dealing with message
|
||||
* channels to contexts that don't have access to their own logging facilities.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.messaging.LoggerServer');
|
||||
|
||||
goog.require('goog.Disposable');
|
||||
goog.require('goog.log');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a logger server that logs messages on behalf of the remote end of a
|
||||
* message channel. The remote end of the channel should use a
|
||||
* {goog.messaging.LoggerClient} with the same service name.
|
||||
*
|
||||
* @param {!goog.messaging.MessageChannel} channel The channel that is sending
|
||||
* the log messages.
|
||||
* @param {string} serviceName The name of the logging service to listen for.
|
||||
* @param {string=} opt_channelName The name of this channel. Used to help
|
||||
* distinguish this client's messages.
|
||||
* @constructor
|
||||
* @extends {goog.Disposable}
|
||||
*/
|
||||
goog.messaging.LoggerServer = function(channel, serviceName, opt_channelName) {
|
||||
goog.base(this);
|
||||
|
||||
/**
|
||||
* The channel that is sending the log messages.
|
||||
* @type {!goog.messaging.MessageChannel}
|
||||
* @private
|
||||
*/
|
||||
this.channel_ = channel;
|
||||
|
||||
/**
|
||||
* The name of the logging service to listen for.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.serviceName_ = serviceName;
|
||||
|
||||
/**
|
||||
* The name of the channel.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.channelName_ = opt_channelName || 'remote logger';
|
||||
|
||||
this.channel_.registerService(
|
||||
this.serviceName_, goog.bind(this.log_, this), true /* opt_json */);
|
||||
};
|
||||
goog.inherits(goog.messaging.LoggerServer, goog.Disposable);
|
||||
|
||||
|
||||
/**
|
||||
* Handles logging messages from the client.
|
||||
* @param {!Object|string} message
|
||||
* The logging information from the client.
|
||||
* @private
|
||||
*/
|
||||
goog.messaging.LoggerServer.prototype.log_ = function(message) {
|
||||
var args =
|
||||
/**
|
||||
* @type {!{level: number, message: string,
|
||||
* name: string, exception: Object}}
|
||||
*/ (message);
|
||||
var level = goog.log.Level.getPredefinedLevelByValue(args['level']);
|
||||
if (level) {
|
||||
var msg = '[' + this.channelName_ + '] ' + args['message'];
|
||||
goog.log.getLogger(args['name'])
|
||||
.log(level, msg, args['exception']);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.messaging.LoggerServer.prototype.disposeInternal = function() {
|
||||
goog.base(this, 'disposeInternal');
|
||||
this.channel_.registerService(this.serviceName_, goog.nullFunction, true);
|
||||
delete this.channel_;
|
||||
};
|
||||
@@ -0,0 +1,116 @@
|
||||
// Copyright 2010 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 An interface for asynchronous message-passing channels.
|
||||
*
|
||||
* This interface is useful for writing code in a message-passing style that's
|
||||
* independent of the underlying communication medium. It's also useful for
|
||||
* adding decorators that wrap message channels and add extra functionality on
|
||||
* top. For example, {@link goog.messaging.BufferedChannel} enqueues messages
|
||||
* until communication is established, while {@link goog.messaging.MultiChannel}
|
||||
* splits a single underlying channel into multiple virtual ones.
|
||||
*
|
||||
* Decorators should be passed their underlying channel(s) in the constructor,
|
||||
* and should assume that those channels are already connected. Decorators are
|
||||
* responsible for disposing of the channels they wrap when the decorators
|
||||
* themselves are disposed. Decorators should also follow the APIs of the
|
||||
* individual methods listed below.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
goog.provide('goog.messaging.MessageChannel');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @interface
|
||||
*/
|
||||
goog.messaging.MessageChannel = function() {};
|
||||
|
||||
|
||||
/**
|
||||
* Initiates the channel connection. When this method is called, all the
|
||||
* information needed to connect the channel has to be available.
|
||||
*
|
||||
* Implementers should only require this method to be called if the channel
|
||||
* needs to be configured in some way between when it's created and when it
|
||||
* becomes active. Otherwise, the channel should be immediately active and this
|
||||
* method should do nothing but immediately call opt_connectCb.
|
||||
*
|
||||
* @param {Function=} opt_connectCb Called when the channel has been connected
|
||||
* and is ready to use.
|
||||
*/
|
||||
goog.messaging.MessageChannel.prototype.connect = function(opt_connectCb) {};
|
||||
|
||||
|
||||
/**
|
||||
* Gets whether the channel is connected.
|
||||
*
|
||||
* If {@link #connect} is not required for this class, this should always return
|
||||
* true. Otherwise, this should return true by the time the callback passed to
|
||||
* {@link #connect} has been called and always after that.
|
||||
*
|
||||
* @return {boolean} Whether the channel is connected.
|
||||
*/
|
||||
goog.messaging.MessageChannel.prototype.isConnected = function() {};
|
||||
|
||||
|
||||
/**
|
||||
* Registers a service to be called when a message is received.
|
||||
*
|
||||
* Implementers shouldn't impose any restrictions on the service names that may
|
||||
* be registered. If some services are needed as control codes,
|
||||
* {@link goog.messaging.MultiMessageChannel} can be used to safely split the
|
||||
* channel into "public" and "control" virtual channels.
|
||||
*
|
||||
* @param {string} serviceName The name of the service.
|
||||
* @param {function((string|!Object))} callback The callback to process the
|
||||
* incoming messages. Passed the payload. If opt_objectPayload is set, the
|
||||
* payload is decoded and passed as an object.
|
||||
* @param {boolean=} opt_objectPayload If true, incoming messages for this
|
||||
* service are expected to contain an object, and will be deserialized from
|
||||
* a string automatically if necessary. It's the responsibility of
|
||||
* implementors of this class to perform the deserialization.
|
||||
*/
|
||||
goog.messaging.MessageChannel.prototype.registerService =
|
||||
function(serviceName, callback, opt_objectPayload) {};
|
||||
|
||||
|
||||
/**
|
||||
* Registers a service to be called when a message is received that doesn't
|
||||
* match any other services.
|
||||
*
|
||||
* @param {function(string, (string|!Object))} callback The callback to process
|
||||
* the incoming messages. Passed the service name and the payload. Since
|
||||
* some channels can pass objects natively, the payload may be either an
|
||||
* object or a string.
|
||||
*/
|
||||
goog.messaging.MessageChannel.prototype.registerDefaultService =
|
||||
function(callback) {};
|
||||
|
||||
|
||||
/**
|
||||
* Sends a message over the channel.
|
||||
*
|
||||
* @param {string} serviceName The name of the service this message should be
|
||||
* delivered to.
|
||||
* @param {string|!Object} payload The value of the message. If this is an
|
||||
* Object, it is serialized to a string before sending if necessary. It's
|
||||
* the responsibility of implementors of this class to perform the
|
||||
* serialization.
|
||||
*/
|
||||
goog.messaging.MessageChannel.prototype.send =
|
||||
function(serviceName, payload) {};
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright 2010 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 Functions for manipulating message channels.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.messaging');
|
||||
|
||||
goog.require('goog.messaging.MessageChannel');
|
||||
|
||||
|
||||
/**
|
||||
* Creates a bidirectional pipe between two message channels.
|
||||
*
|
||||
* @param {goog.messaging.MessageChannel} channel1 The first channel.
|
||||
* @param {goog.messaging.MessageChannel} channel2 The second channel.
|
||||
*/
|
||||
goog.messaging.pipe = function(channel1, channel2) {
|
||||
channel1.registerDefaultService(goog.bind(channel2.send, channel2));
|
||||
channel2.registerDefaultService(goog.bind(channel1.send, channel1));
|
||||
};
|
||||
@@ -0,0 +1,302 @@
|
||||
// Copyright 2010 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 Definition of goog.messaging.MultiChannel, which uses a
|
||||
* single underlying MessageChannel to carry several independent virtual message
|
||||
* channels.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
goog.provide('goog.messaging.MultiChannel');
|
||||
goog.provide('goog.messaging.MultiChannel.VirtualChannel');
|
||||
|
||||
goog.require('goog.Disposable');
|
||||
goog.require('goog.events.EventHandler');
|
||||
goog.require('goog.log');
|
||||
goog.require('goog.messaging.MessageChannel'); // interface
|
||||
goog.require('goog.object');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new MultiChannel wrapping a single MessageChannel. The
|
||||
* underlying channel shouldn't have any other listeners registered, but it
|
||||
* should be connected.
|
||||
*
|
||||
* Note that the other side of the channel should also be connected to a
|
||||
* MultiChannel with the same number of virtual channels.
|
||||
*
|
||||
* @param {goog.messaging.MessageChannel} underlyingChannel The underlying
|
||||
* channel to use as transport for the virtual channels.
|
||||
* @constructor
|
||||
* @extends {goog.Disposable}
|
||||
*/
|
||||
goog.messaging.MultiChannel = function(underlyingChannel) {
|
||||
goog.base(this);
|
||||
|
||||
/**
|
||||
* The underlying channel across which all requests are sent.
|
||||
* @type {goog.messaging.MessageChannel}
|
||||
* @private
|
||||
*/
|
||||
this.underlyingChannel_ = underlyingChannel;
|
||||
|
||||
/**
|
||||
* All the virtual channels that are registered for this MultiChannel.
|
||||
* These are null if they've been disposed.
|
||||
* @type {Object.<?goog.messaging.MultiChannel.VirtualChannel>}
|
||||
* @private
|
||||
*/
|
||||
this.virtualChannels_ = {};
|
||||
|
||||
this.underlyingChannel_.registerDefaultService(
|
||||
goog.bind(this.handleDefault_, this));
|
||||
};
|
||||
goog.inherits(goog.messaging.MultiChannel, goog.Disposable);
|
||||
|
||||
|
||||
/**
|
||||
* Logger object for goog.messaging.MultiChannel.
|
||||
* @type {goog.log.Logger}
|
||||
* @private
|
||||
*/
|
||||
goog.messaging.MultiChannel.prototype.logger_ =
|
||||
goog.log.getLogger('goog.messaging.MultiChannel');
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new virtual channel that will communicate across the underlying
|
||||
* channel.
|
||||
* @param {string} name The name of the virtual channel. Must be unique for this
|
||||
* MultiChannel. Cannot contain colons.
|
||||
* @return {!goog.messaging.MultiChannel.VirtualChannel} The new virtual
|
||||
* channel.
|
||||
*/
|
||||
goog.messaging.MultiChannel.prototype.createVirtualChannel = function(name) {
|
||||
if (name.indexOf(':') != -1) {
|
||||
throw Error(
|
||||
'Virtual channel name "' + name + '" should not contain colons');
|
||||
}
|
||||
|
||||
if (name in this.virtualChannels_) {
|
||||
throw Error('Virtual channel "' + name + '" was already created for ' +
|
||||
'this multichannel.');
|
||||
}
|
||||
|
||||
var channel =
|
||||
new goog.messaging.MultiChannel.VirtualChannel(this, name);
|
||||
this.virtualChannels_[name] = channel;
|
||||
return channel;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles the default service for the underlying channel. This dispatches any
|
||||
* unrecognized services to the appropriate virtual channel.
|
||||
*
|
||||
* @param {string} serviceName The name of the service being called.
|
||||
* @param {string|!Object} payload The message payload.
|
||||
* @private
|
||||
*/
|
||||
goog.messaging.MultiChannel.prototype.handleDefault_ = function(
|
||||
serviceName, payload) {
|
||||
var match = serviceName.match(/^([^:]*):(.*)/);
|
||||
if (!match) {
|
||||
goog.log.warning(this.logger_,
|
||||
'Invalid service name "' + serviceName + '": no ' +
|
||||
'virtual channel specified');
|
||||
return;
|
||||
}
|
||||
|
||||
var channelName = match[1];
|
||||
serviceName = match[2];
|
||||
if (!(channelName in this.virtualChannels_)) {
|
||||
goog.log.warning(this.logger_,
|
||||
'Virtual channel "' + channelName + ' does not ' +
|
||||
'exist, but a message was received for it: "' + serviceName + '"');
|
||||
return;
|
||||
}
|
||||
|
||||
var virtualChannel = this.virtualChannels_[channelName];
|
||||
if (!virtualChannel) {
|
||||
goog.log.warning(this.logger_,
|
||||
'Virtual channel "' + channelName + ' has been ' +
|
||||
'disposed, but a message was received for it: "' + serviceName + '"');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!virtualChannel.defaultService_) {
|
||||
goog.log.warning(this.logger_,
|
||||
'Service "' + serviceName + '" is not registered ' +
|
||||
'on virtual channel "' + channelName + '"');
|
||||
return;
|
||||
}
|
||||
|
||||
virtualChannel.defaultService_(serviceName, payload);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.messaging.MultiChannel.prototype.disposeInternal = function() {
|
||||
goog.object.forEach(this.virtualChannels_, function(channel) {
|
||||
goog.dispose(channel);
|
||||
});
|
||||
goog.dispose(this.underlyingChannel_);
|
||||
delete this.virtualChannels_;
|
||||
delete this.underlyingChannel_;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A message channel that proxies its messages over another underlying channel.
|
||||
*
|
||||
* @param {goog.messaging.MultiChannel} parent The MultiChannel
|
||||
* which created this channel, and which contains the underlying
|
||||
* MessageChannel that's used as the transport.
|
||||
* @param {string} name The name of this virtual channel. Unique among the
|
||||
* virtual channels in parent.
|
||||
* @constructor
|
||||
* @implements {goog.messaging.MessageChannel}
|
||||
* @extends {goog.Disposable}
|
||||
*/
|
||||
goog.messaging.MultiChannel.VirtualChannel = function(parent, name) {
|
||||
goog.base(this);
|
||||
|
||||
/**
|
||||
* The MultiChannel containing the underlying transport channel.
|
||||
* @type {goog.messaging.MultiChannel}
|
||||
* @private
|
||||
*/
|
||||
this.parent_ = parent;
|
||||
|
||||
/**
|
||||
* The name of this virtual channel.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.name_ = name;
|
||||
};
|
||||
goog.inherits(goog.messaging.MultiChannel.VirtualChannel,
|
||||
goog.Disposable);
|
||||
|
||||
|
||||
/**
|
||||
* The default service to run if no other services match.
|
||||
* @type {?function(string, (string|!Object))}
|
||||
* @private
|
||||
*/
|
||||
goog.messaging.MultiChannel.VirtualChannel.prototype.defaultService_;
|
||||
|
||||
|
||||
/**
|
||||
* Logger object for goog.messaging.MultiChannel.VirtualChannel.
|
||||
* @type {goog.log.Logger}
|
||||
* @private
|
||||
*/
|
||||
goog.messaging.MultiChannel.VirtualChannel.prototype.logger_ =
|
||||
goog.log.getLogger(
|
||||
'goog.messaging.MultiChannel.VirtualChannel');
|
||||
|
||||
|
||||
/**
|
||||
* This is a no-op, since the underlying channel is expected to already be
|
||||
* initialized when it's passed in.
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
goog.messaging.MultiChannel.VirtualChannel.prototype.connect =
|
||||
function(opt_connectCb) {
|
||||
if (opt_connectCb) {
|
||||
opt_connectCb();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* This always returns true, since the underlying channel is expected to already
|
||||
* be initialized when it's passed in.
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
goog.messaging.MultiChannel.VirtualChannel.prototype.isConnected =
|
||||
function() {
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
goog.messaging.MultiChannel.VirtualChannel.prototype.registerService =
|
||||
function(serviceName, callback, opt_objectPayload) {
|
||||
this.parent_.underlyingChannel_.registerService(
|
||||
this.name_ + ':' + serviceName,
|
||||
goog.bind(this.doCallback_, this, callback),
|
||||
opt_objectPayload);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
goog.messaging.MultiChannel.VirtualChannel.prototype.
|
||||
registerDefaultService = function(callback) {
|
||||
this.defaultService_ = goog.bind(this.doCallback_, this, callback);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
goog.messaging.MultiChannel.VirtualChannel.prototype.send =
|
||||
function(serviceName, payload) {
|
||||
if (this.isDisposed()) {
|
||||
throw Error('#send called for disposed VirtualChannel.');
|
||||
}
|
||||
|
||||
this.parent_.underlyingChannel_.send(this.name_ + ':' + serviceName,
|
||||
payload);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Wraps a callback with a function that will log a warning and abort if it's
|
||||
* called when this channel is disposed.
|
||||
*
|
||||
* @param {function()} callback The callback to wrap.
|
||||
* @param {...*} var_args Other arguments, passed to the callback.
|
||||
* @private
|
||||
*/
|
||||
goog.messaging.MultiChannel.VirtualChannel.prototype.doCallback_ =
|
||||
function(callback, var_args) {
|
||||
if (this.isDisposed()) {
|
||||
goog.log.warning(this.logger_,
|
||||
'Virtual channel "' + this.name_ + '" received ' +
|
||||
' a message after being disposed.');
|
||||
return;
|
||||
}
|
||||
|
||||
callback.apply({}, Array.prototype.slice.call(arguments, 1));
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.messaging.MultiChannel.VirtualChannel.prototype.disposeInternal =
|
||||
function() {
|
||||
this.parent_.virtualChannels_[this.name_] = null;
|
||||
this.parent_ = null;
|
||||
};
|
||||
@@ -0,0 +1,151 @@
|
||||
// Copyright 2011 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 The leaf node of a {@link goog.messaging.PortNetwork}. Callers
|
||||
* connect to the operator, and request connections with other contexts from it.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.messaging.PortCaller');
|
||||
|
||||
goog.require('goog.Disposable');
|
||||
goog.require('goog.async.Deferred');
|
||||
goog.require('goog.messaging.DeferredChannel');
|
||||
goog.require('goog.messaging.PortChannel');
|
||||
goog.require('goog.messaging.PortNetwork'); // interface
|
||||
goog.require('goog.object');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The leaf node of a network.
|
||||
*
|
||||
* @param {!goog.messaging.MessageChannel} operatorPort The channel for
|
||||
* communicating with the operator. The other side of this channel should be
|
||||
* passed to {@link goog.messaging.PortOperator#addPort}. Must be either a
|
||||
* {@link goog.messaging.PortChannel} or a decorator wrapping a PortChannel;
|
||||
* in particular, it must be able to send and receive {@link MessagePort}s.
|
||||
* @constructor
|
||||
* @extends {goog.Disposable}
|
||||
* @implements {goog.messaging.PortNetwork}
|
||||
*/
|
||||
goog.messaging.PortCaller = function(operatorPort) {
|
||||
goog.base(this);
|
||||
|
||||
/**
|
||||
* The channel to the {@link goog.messaging.PortOperator} for this network.
|
||||
*
|
||||
* @type {!goog.messaging.MessageChannel}
|
||||
* @private
|
||||
*/
|
||||
this.operatorPort_ = operatorPort;
|
||||
|
||||
/**
|
||||
* The collection of channels for communicating with other contexts in the
|
||||
* network. Each value can contain a {@link goog.aync.Deferred} and/or a
|
||||
* {@link goog.messaging.MessageChannel}.
|
||||
*
|
||||
* If the value contains a Deferred, then the channel is a
|
||||
* {@link goog.messaging.DeferredChannel} wrapping that Deferred. The Deferred
|
||||
* will be resolved with a {@link goog.messaging.PortChannel} once we receive
|
||||
* the appropriate port from the operator. This is the situation when this
|
||||
* caller requests a connection to another context; the DeferredChannel is
|
||||
* used to queue up messages until we receive the port from the operator.
|
||||
*
|
||||
* If the value does not contain a Deferred, then the channel is simply a
|
||||
* {@link goog.messaging.PortChannel} communicating with the given context.
|
||||
* This is the situation when this context received a port for the other
|
||||
* context before it was requested.
|
||||
*
|
||||
* If a value exists for a given key, it must contain a channel, but it
|
||||
* doesn't necessarily contain a Deferred.
|
||||
*
|
||||
* @type {!Object.<{deferred: goog.async.Deferred,
|
||||
* channel: !goog.messaging.MessageChannel}>}
|
||||
* @private
|
||||
*/
|
||||
this.connections_ = {};
|
||||
|
||||
this.operatorPort_.registerService(
|
||||
goog.messaging.PortNetwork.GRANT_CONNECTION_SERVICE,
|
||||
goog.bind(this.connectionGranted_, this),
|
||||
true /* opt_json */);
|
||||
};
|
||||
goog.inherits(goog.messaging.PortCaller, goog.Disposable);
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.messaging.PortCaller.prototype.dial = function(name) {
|
||||
if (name in this.connections_) {
|
||||
return this.connections_[name].channel;
|
||||
}
|
||||
|
||||
this.operatorPort_.send(
|
||||
goog.messaging.PortNetwork.REQUEST_CONNECTION_SERVICE, name);
|
||||
var deferred = new goog.async.Deferred();
|
||||
var channel = new goog.messaging.DeferredChannel(deferred);
|
||||
this.connections_[name] = {deferred: deferred, channel: channel};
|
||||
return channel;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Registers a connection to another context in the network. This is called when
|
||||
* the operator sends us one end of a {@link MessageChannel}, either because
|
||||
* this caller requested a connection with another context, or because that
|
||||
* context requested a connection with this caller.
|
||||
*
|
||||
* It's possible that the remote context and this one request each other roughly
|
||||
* concurrently. The operator doesn't keep track of which contexts have been
|
||||
* connected, so it will create two separate {@link MessageChannel}s in this
|
||||
* case. However, the first channel created will reach both contexts first, so
|
||||
* we simply ignore all connections with a given context after the first.
|
||||
*
|
||||
* @param {!Object|string} message The name of the context
|
||||
* being connected and the port connecting the context.
|
||||
* @private
|
||||
*/
|
||||
goog.messaging.PortCaller.prototype.connectionGranted_ = function(message) {
|
||||
var args = /** @type {{name: string, port: MessagePort}} */ (message);
|
||||
var port = args['port'];
|
||||
var entry = this.connections_[args['name']];
|
||||
if (entry && (!entry.deferred || entry.deferred.hasFired())) {
|
||||
// If two PortCallers request one another at the same time, the operator may
|
||||
// send out a channel for connecting them multiple times. Since both callers
|
||||
// will receive the first channel's ports first, we can safely ignore and
|
||||
// close any future ports.
|
||||
port.close();
|
||||
} else if (!args['success']) {
|
||||
throw Error(args['message']);
|
||||
} else {
|
||||
port.start();
|
||||
var channel = new goog.messaging.PortChannel(port);
|
||||
if (entry) {
|
||||
entry.deferred.callback(channel);
|
||||
} else {
|
||||
this.connections_[args['name']] = {channel: channel, deferred: null};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.messaging.PortCaller.prototype.disposeInternal = function() {
|
||||
goog.dispose(this.operatorPort_);
|
||||
goog.object.forEach(this.connections_, goog.dispose);
|
||||
delete this.operatorPort_;
|
||||
delete this.connections_;
|
||||
goog.base(this, 'disposeInternal');
|
||||
};
|
||||
@@ -0,0 +1,401 @@
|
||||
// Copyright 2010 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 A class that wraps several types of HTML5 message-passing
|
||||
* entities ({@link MessagePort}s, {@link WebWorker}s, and {@link Window}s),
|
||||
* providing a unified interface.
|
||||
*
|
||||
* This is tested under Chrome, Safari, and Firefox. Since Firefox 3.6 has an
|
||||
* incomplete implementation of web workers, it doesn't support sending ports
|
||||
* over Window connections. IE has no web worker support at all, and so is
|
||||
* unsupported by this class.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.messaging.PortChannel');
|
||||
|
||||
goog.require('goog.Timer');
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.async.Deferred');
|
||||
goog.require('goog.debug');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.DomHelper');
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.json');
|
||||
goog.require('goog.log');
|
||||
goog.require('goog.messaging.AbstractChannel');
|
||||
goog.require('goog.messaging.DeferredChannel');
|
||||
goog.require('goog.object');
|
||||
goog.require('goog.string');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A wrapper for several types of HTML5 message-passing entities
|
||||
* ({@link MessagePort}s and {@link WebWorker}s). This class implements the
|
||||
* {@link goog.messaging.MessageChannel} interface.
|
||||
*
|
||||
* This class can be used in conjunction with other communication on the port.
|
||||
* It sets {@link goog.messaging.PortChannel.FLAG} to true on all messages it
|
||||
* sends.
|
||||
*
|
||||
* @param {!MessagePort|!WebWorker} underlyingPort The message-passing
|
||||
* entity to wrap. If this is a {@link MessagePort}, it should be started.
|
||||
* The remote end should also be wrapped in a PortChannel. This will be
|
||||
* disposed along with the PortChannel; this means terminating it if it's a
|
||||
* worker or removing it from the DOM if it's an iframe.
|
||||
* @constructor
|
||||
* @extends {goog.messaging.AbstractChannel}
|
||||
*/
|
||||
goog.messaging.PortChannel = function(underlyingPort) {
|
||||
goog.base(this);
|
||||
|
||||
/**
|
||||
* The wrapped message-passing entity.
|
||||
* @type {!MessagePort|!WebWorker}
|
||||
* @private
|
||||
*/
|
||||
this.port_ = underlyingPort;
|
||||
|
||||
/**
|
||||
* The key for the event listener.
|
||||
* @type {goog.events.Key}
|
||||
* @private
|
||||
*/
|
||||
this.listenerKey_ = goog.events.listen(
|
||||
this.port_, goog.events.EventType.MESSAGE, this.deliver_, false, this);
|
||||
};
|
||||
goog.inherits(goog.messaging.PortChannel, goog.messaging.AbstractChannel);
|
||||
|
||||
|
||||
/**
|
||||
* Create a PortChannel that communicates with a window embedded in the current
|
||||
* page (e.g. an iframe contentWindow). The code within the window should call
|
||||
* {@link forGlobalWindow} to establish the connection.
|
||||
*
|
||||
* It's possible to use this channel in conjunction with other messages to the
|
||||
* embedded window. However, only one PortChannel should be used for a given
|
||||
* window at a time.
|
||||
*
|
||||
* @param {!Window} window The window object to communicate with.
|
||||
* @param {string} peerOrigin The expected origin of the window. See
|
||||
* http://dev.w3.org/html5/postmsg/#dom-window-postmessage.
|
||||
* @param {goog.Timer=} opt_timer The timer that regulates how often the initial
|
||||
* connection message is attempted. This will be automatically disposed once
|
||||
* the connection is established, or when the connection is cancelled.
|
||||
* @return {!goog.messaging.DeferredChannel} The PortChannel. Although this is
|
||||
* not actually an instance of the PortChannel class, it will behave like
|
||||
* one in that MessagePorts may be sent across it. The DeferredChannel may
|
||||
* be cancelled before a connection is established in order to abort the
|
||||
* attempt to make a connection.
|
||||
*/
|
||||
goog.messaging.PortChannel.forEmbeddedWindow = function(
|
||||
window, peerOrigin, opt_timer) {
|
||||
var timer = opt_timer || new goog.Timer(50);
|
||||
|
||||
var disposeTimer = goog.partial(goog.dispose, timer);
|
||||
var deferred = new goog.async.Deferred(disposeTimer);
|
||||
deferred.addBoth(disposeTimer);
|
||||
|
||||
timer.start();
|
||||
// Every tick, attempt to set up a connection by sending in one end of an
|
||||
// HTML5 MessageChannel. If the inner window posts a response along a channel,
|
||||
// then we'll use that channel to create the PortChannel.
|
||||
//
|
||||
// As per http://dev.w3.org/html5/postmsg/#ports-and-garbage-collection, any
|
||||
// ports that are not ultimately used to set up the channel will be garbage
|
||||
// collected (since there are no references in this context, and the remote
|
||||
// context hasn't seen them).
|
||||
goog.events.listen(timer, goog.Timer.TICK, function() {
|
||||
var channel = new MessageChannel();
|
||||
var gotMessage = function(e) {
|
||||
channel.port1.removeEventListener(
|
||||
goog.events.EventType.MESSAGE, gotMessage, true);
|
||||
// If the connection has been cancelled, don't create the channel.
|
||||
if (!timer.isDisposed()) {
|
||||
deferred.callback(new goog.messaging.PortChannel(channel.port1));
|
||||
}
|
||||
};
|
||||
channel.port1.start();
|
||||
// Don't use goog.events because we don't want any lingering references to
|
||||
// the ports to prevent them from getting GCed. Only modern browsers support
|
||||
// these APIs anyway, so we don't need to worry about event API
|
||||
// compatibility.
|
||||
channel.port1.addEventListener(
|
||||
goog.events.EventType.MESSAGE, gotMessage, true);
|
||||
|
||||
var msg = {};
|
||||
msg[goog.messaging.PortChannel.FLAG] = true;
|
||||
window.postMessage(msg, [channel.port2], peerOrigin);
|
||||
});
|
||||
|
||||
return new goog.messaging.DeferredChannel(deferred);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Create a PortChannel that communicates with the document in which this window
|
||||
* is embedded (e.g. within an iframe). The enclosing document should call
|
||||
* {@link forEmbeddedWindow} to establish the connection.
|
||||
*
|
||||
* It's possible to use this channel in conjunction with other messages posted
|
||||
* to the global window. However, only one PortChannel should be used for the
|
||||
* global window at a time.
|
||||
*
|
||||
* @param {string} peerOrigin The expected origin of the enclosing document. See
|
||||
* http://dev.w3.org/html5/postmsg/#dom-window-postmessage.
|
||||
* @return {!goog.messaging.MessageChannel} The PortChannel. Although this may
|
||||
* not actually be an instance of the PortChannel class, it will behave like
|
||||
* one in that MessagePorts may be sent across it.
|
||||
*/
|
||||
goog.messaging.PortChannel.forGlobalWindow = function(peerOrigin) {
|
||||
var deferred = new goog.async.Deferred();
|
||||
// Wait for the external page to post a message containing the message port
|
||||
// which we'll use to set up the PortChannel. Ignore all other messages. Once
|
||||
// we receive the port, notify the other end and then set up the PortChannel.
|
||||
var key = goog.events.listen(
|
||||
window, goog.events.EventType.MESSAGE, function(e) {
|
||||
var browserEvent = e.getBrowserEvent();
|
||||
var data = browserEvent.data;
|
||||
if (!goog.isObject(data) || !data[goog.messaging.PortChannel.FLAG]) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (peerOrigin != '*' && peerOrigin != browserEvent.origin) {
|
||||
return;
|
||||
}
|
||||
|
||||
var port = browserEvent.ports[0];
|
||||
// Notify the other end of the channel that we've received our port
|
||||
port.postMessage({});
|
||||
|
||||
port.start();
|
||||
deferred.callback(new goog.messaging.PortChannel(port));
|
||||
goog.events.unlistenByKey(key);
|
||||
});
|
||||
return new goog.messaging.DeferredChannel(deferred);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The flag added to messages that are sent by a PortChannel, and are meant to
|
||||
* be handled by one on the other side.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.messaging.PortChannel.FLAG = '--goog.messaging.PortChannel';
|
||||
|
||||
|
||||
/**
|
||||
* Whether the messages sent across the channel must be JSON-serialized. This is
|
||||
* required for older versions of Webkit, which can only send string messages.
|
||||
*
|
||||
* Although Safari and Chrome have separate implementations of message passing,
|
||||
* both of them support passing objects by Webkit 533.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.messaging.PortChannel.REQUIRES_SERIALIZATION_ = goog.userAgent.WEBKIT &&
|
||||
goog.string.compareVersions(goog.userAgent.VERSION, '533') < 0;
|
||||
|
||||
|
||||
/**
|
||||
* Logger for this class.
|
||||
* @type {goog.log.Logger}
|
||||
* @protected
|
||||
* @override
|
||||
*/
|
||||
goog.messaging.PortChannel.prototype.logger =
|
||||
goog.log.getLogger('goog.messaging.PortChannel');
|
||||
|
||||
|
||||
/**
|
||||
* Sends a message over the channel.
|
||||
*
|
||||
* As an addition to the basic MessageChannel send API, PortChannels can send
|
||||
* objects that contain MessagePorts. Note that only plain Objects and Arrays,
|
||||
* not their subclasses, can contain MessagePorts.
|
||||
*
|
||||
* As per {@link http://www.w3.org/TR/html5/comms.html#clone-a-port}, once a
|
||||
* port is copied to be sent across a channel, the original port will cease
|
||||
* being able to send or receive messages.
|
||||
*
|
||||
* @override
|
||||
* @param {string} serviceName The name of the service this message should be
|
||||
* delivered to.
|
||||
* @param {string|!Object|!MessagePort} payload The value of the message. May
|
||||
* contain MessagePorts or be a MessagePort.
|
||||
*/
|
||||
goog.messaging.PortChannel.prototype.send = function(serviceName, payload) {
|
||||
var ports = [];
|
||||
payload = this.extractPorts_(ports, payload);
|
||||
var message = {'serviceName': serviceName, 'payload': payload};
|
||||
message[goog.messaging.PortChannel.FLAG] = true;
|
||||
|
||||
if (goog.messaging.PortChannel.REQUIRES_SERIALIZATION_) {
|
||||
message = goog.json.serialize(message);
|
||||
}
|
||||
|
||||
this.port_.postMessage(message, ports);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Delivers a message to the appropriate service handler. If this message isn't
|
||||
* a GearsWorkerChannel message, it's ignored and passed on to other handlers.
|
||||
*
|
||||
* @param {goog.events.Event} e The event.
|
||||
* @private
|
||||
*/
|
||||
goog.messaging.PortChannel.prototype.deliver_ = function(e) {
|
||||
var browserEvent = e.getBrowserEvent();
|
||||
var data = browserEvent.data;
|
||||
|
||||
if (goog.messaging.PortChannel.REQUIRES_SERIALIZATION_) {
|
||||
try {
|
||||
data = goog.json.parse(data);
|
||||
} catch (error) {
|
||||
// Ignore any non-JSON messages.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!goog.isObject(data) || !data[goog.messaging.PortChannel.FLAG]) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.validateMessage_(data)) {
|
||||
var serviceName = data['serviceName'];
|
||||
var payload = data['payload'];
|
||||
var service = this.getService(serviceName, payload);
|
||||
if (!service) {
|
||||
return;
|
||||
}
|
||||
|
||||
payload = this.decodePayload(
|
||||
serviceName,
|
||||
this.injectPorts_(browserEvent.ports || [], payload),
|
||||
service.objectPayload);
|
||||
if (goog.isDefAndNotNull(payload)) {
|
||||
service.callback(payload);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether the message is invalid in some way.
|
||||
*
|
||||
* @param {Object} data The contents of the message.
|
||||
* @return {boolean} True if the message is valid, false otherwise.
|
||||
* @private
|
||||
*/
|
||||
goog.messaging.PortChannel.prototype.validateMessage_ = function(data) {
|
||||
if (!('serviceName' in data)) {
|
||||
goog.log.warning(this.logger,
|
||||
'Message object doesn\'t contain service name: ' +
|
||||
goog.debug.deepExpose(data));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!('payload' in data)) {
|
||||
goog.log.warning(this.logger,
|
||||
'Message object doesn\'t contain payload: ' +
|
||||
goog.debug.deepExpose(data));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Extracts all MessagePort objects from a message to be sent into an array.
|
||||
*
|
||||
* The message ports are replaced by placeholder objects that will be replaced
|
||||
* with the ports again on the other side of the channel.
|
||||
*
|
||||
* @param {Array.<MessagePort>} ports The array that will contain ports
|
||||
* extracted from the message. Will be destructively modified. Should be
|
||||
* empty initially.
|
||||
* @param {string|!Object} message The message from which ports will be
|
||||
* extracted.
|
||||
* @return {string|!Object} The message with ports extracted.
|
||||
* @private
|
||||
*/
|
||||
goog.messaging.PortChannel.prototype.extractPorts_ = function(ports, message) {
|
||||
// Can't use instanceof here because MessagePort is undefined in workers
|
||||
if (message &&
|
||||
Object.prototype.toString.call(/** @type {!Object} */ (message)) ==
|
||||
'[object MessagePort]') {
|
||||
ports.push(message);
|
||||
return {'_port': {'type': 'real', 'index': ports.length - 1}};
|
||||
} else if (goog.isArray(message)) {
|
||||
return goog.array.map(message, goog.bind(this.extractPorts_, this, ports));
|
||||
// We want to compare the exact constructor here because we only want to
|
||||
// recurse into object literals, not native objects like Date.
|
||||
} else if (message && message.constructor == Object) {
|
||||
return goog.object.map(/** @type {Object} */ (message), function(val, key) {
|
||||
val = this.extractPorts_(ports, val);
|
||||
return key == '_port' ? {'type': 'escaped', 'val': val} : val;
|
||||
}, this);
|
||||
} else {
|
||||
return message;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Injects MessagePorts back into a message received from across the channel.
|
||||
*
|
||||
* @param {Array.<MessagePort>} ports The array of ports to be injected into the
|
||||
* message.
|
||||
* @param {string|!Object} message The message into which the ports will be
|
||||
* injected.
|
||||
* @return {string|!Object} The message with ports injected.
|
||||
* @private
|
||||
*/
|
||||
goog.messaging.PortChannel.prototype.injectPorts_ = function(ports, message) {
|
||||
if (goog.isArray(message)) {
|
||||
return goog.array.map(message, goog.bind(this.injectPorts_, this, ports));
|
||||
} else if (message && message.constructor == Object) {
|
||||
message = /** @type {!Object} */ (message);
|
||||
if (message['_port'] && message['_port']['type'] == 'real') {
|
||||
return /** @type {!MessagePort} */ (ports[message['_port']['index']]);
|
||||
}
|
||||
return goog.object.map(message, function(val, key) {
|
||||
return this.injectPorts_(ports, key == '_port' ? val['val'] : val);
|
||||
}, this);
|
||||
} else {
|
||||
return message;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.messaging.PortChannel.prototype.disposeInternal = function() {
|
||||
goog.events.unlistenByKey(this.listenerKey_);
|
||||
// Can't use instanceof here because MessagePort is undefined in workers and
|
||||
// in Firefox
|
||||
if (Object.prototype.toString.call(this.port_) == '[object MessagePort]') {
|
||||
this.port_.close();
|
||||
// Worker is undefined in workers as well as of Chrome 9
|
||||
} else if (Object.prototype.toString.call(this.port_) == '[object Worker]') {
|
||||
this.port_.terminate();
|
||||
}
|
||||
delete this.port_;
|
||||
goog.base(this, 'disposeInternal');
|
||||
};
|
||||
@@ -0,0 +1,78 @@
|
||||
// Copyright 2011 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 An interface for classes that connect a collection of HTML5
|
||||
* message-passing entities ({@link MessagePort}s, {@link Worker}s, and
|
||||
* {@link Window}s) and allow them to seamlessly communicate with one another.
|
||||
*
|
||||
* Conceptually, a PortNetwork is a collection of JS contexts, such as pages (in
|
||||
* or outside of iframes) or web workers. Each context has a unique name, and
|
||||
* each one can communicate with any of the others in the same network. This
|
||||
* communication takes place through a {@link goog.messaging.PortChannel} that
|
||||
* is retrieved via {#link goog.messaging.PortNetwork#dial}.
|
||||
*
|
||||
* One context (usually the main page) has a
|
||||
* {@link goog.messaging.PortOperator}, which is in charge of connecting each
|
||||
* context to each other context. All other contexts have
|
||||
* {@link goog.messaging.PortCaller}s which connect to the operator.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.messaging.PortNetwork');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @interface
|
||||
*/
|
||||
goog.messaging.PortNetwork = function() {};
|
||||
|
||||
|
||||
/**
|
||||
* Returns a message channel that communicates with the named context. If no
|
||||
* such port exists, an error will either be thrown immediately or after a round
|
||||
* trip with the operator, depending on whether this pool is the operator or a
|
||||
* caller.
|
||||
*
|
||||
* If context A calls dial('B') and context B calls dial('A'), the two
|
||||
* ports returned will be connected to one another.
|
||||
*
|
||||
* @param {string} name The name of the context to get.
|
||||
* @return {goog.messaging.MessageChannel} The channel communicating with the
|
||||
* given context. This is either a {@link goog.messaging.PortChannel} or a
|
||||
* decorator around a PortChannel, so it's safe to send {@link MessagePorts}
|
||||
* across it. This will be disposed along with the PortNetwork.
|
||||
*/
|
||||
goog.messaging.PortNetwork.prototype.dial = function(name) {};
|
||||
|
||||
|
||||
/**
|
||||
* The name of the service exported by the operator for creating a connection
|
||||
* between two callers.
|
||||
*
|
||||
* @type {string}
|
||||
* @const
|
||||
*/
|
||||
goog.messaging.PortNetwork.REQUEST_CONNECTION_SERVICE = 'requestConnection';
|
||||
|
||||
|
||||
/**
|
||||
* The name of the service exported by the callers for adding a connection to
|
||||
* another context.
|
||||
*
|
||||
* @type {string}
|
||||
* @const
|
||||
*/
|
||||
goog.messaging.PortNetwork.GRANT_CONNECTION_SERVICE = 'grantConnection';
|
||||
@@ -0,0 +1,197 @@
|
||||
// Copyright 2011 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 The central node of a {@link goog.messaging.PortNetwork}. The
|
||||
* operator is responsible for providing the two-way communication channels (via
|
||||
* {@link MessageChannel}s) between each pair of nodes in the network that need
|
||||
* to communicate with one another. Each network should have one and only one
|
||||
* operator.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.messaging.PortOperator');
|
||||
|
||||
goog.require('goog.Disposable');
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.log');
|
||||
goog.require('goog.messaging.PortChannel');
|
||||
goog.require('goog.messaging.PortNetwork'); // interface
|
||||
goog.require('goog.object');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The central node of a PortNetwork.
|
||||
*
|
||||
* @param {string} name The name of this node.
|
||||
* @constructor
|
||||
* @extends {goog.Disposable}
|
||||
* @implements {goog.messaging.PortNetwork}
|
||||
*/
|
||||
goog.messaging.PortOperator = function(name) {
|
||||
goog.base(this);
|
||||
|
||||
/**
|
||||
* The collection of channels for communicating with other contexts in the
|
||||
* network. These are the channels that are returned to the user, as opposed
|
||||
* to the channels used for internal network communication. This is lazily
|
||||
* populated as the user requests communication with other contexts, or other
|
||||
* contexts request communication with the operator.
|
||||
*
|
||||
* @type {!Object.<!goog.messaging.PortChannel>}
|
||||
* @private
|
||||
*/
|
||||
this.connections_ = {};
|
||||
|
||||
/**
|
||||
* The collection of channels for internal network communication with other
|
||||
* contexts. This is not lazily populated, and always contains entries for
|
||||
* each member of the network.
|
||||
*
|
||||
* @type {!Object.<!goog.messaging.MessageChannel>}
|
||||
* @private
|
||||
*/
|
||||
this.switchboard_ = {};
|
||||
|
||||
/**
|
||||
* The name of the operator context.
|
||||
*
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.name_ = name;
|
||||
};
|
||||
goog.inherits(goog.messaging.PortOperator, goog.Disposable);
|
||||
|
||||
|
||||
/**
|
||||
* The logger for PortOperator.
|
||||
* @type {goog.log.Logger}
|
||||
* @private
|
||||
*/
|
||||
goog.messaging.PortOperator.prototype.logger_ =
|
||||
goog.log.getLogger('goog.messaging.PortOperator');
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.messaging.PortOperator.prototype.dial = function(name) {
|
||||
this.connectSelfToPort_(name);
|
||||
return this.connections_[name];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds a caller to the network with the given name. This port should have no
|
||||
* services registered on it. It will be disposed along with the PortOperator.
|
||||
*
|
||||
* @param {string} name The name of the port to add.
|
||||
* @param {!goog.messaging.MessageChannel} port The port to add. Must be either
|
||||
* a {@link goog.messaging.PortChannel} or a decorator wrapping a
|
||||
* PortChannel; in particular, it must be able to send and receive
|
||||
* {@link MessagePort}s.
|
||||
*/
|
||||
goog.messaging.PortOperator.prototype.addPort = function(name, port) {
|
||||
this.switchboard_[name] = port;
|
||||
port.registerService(goog.messaging.PortNetwork.REQUEST_CONNECTION_SERVICE,
|
||||
goog.bind(this.requestConnection_, this, name));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Connects two contexts by creating a {@link MessageChannel} and sending one
|
||||
* end to one context and the other end to the other. Called when we receive a
|
||||
* request from a caller to connect it to another context (including potentially
|
||||
* the operator).
|
||||
*
|
||||
* @param {string} sourceName The name of the context requesting the connection.
|
||||
* @param {!Object|string} message The name of the context to which
|
||||
* the connection is requested.
|
||||
* @private
|
||||
*/
|
||||
goog.messaging.PortOperator.prototype.requestConnection_ = function(
|
||||
sourceName, message) {
|
||||
var requestedName = /** @type {string} */ (message);
|
||||
if (requestedName == this.name_) {
|
||||
this.connectSelfToPort_(sourceName);
|
||||
return;
|
||||
}
|
||||
|
||||
var sourceChannel = this.switchboard_[sourceName];
|
||||
var requestedChannel = this.switchboard_[requestedName];
|
||||
|
||||
goog.asserts.assert(goog.isDefAndNotNull(sourceChannel));
|
||||
if (!requestedChannel) {
|
||||
var err = 'Port "' + sourceName + '" requested a connection to port "' +
|
||||
requestedName + '", which doesn\'t exist';
|
||||
goog.log.warning(this.logger_, err);
|
||||
sourceChannel.send(goog.messaging.PortNetwork.GRANT_CONNECTION_SERVICE,
|
||||
{'success': false, 'message': err});
|
||||
return;
|
||||
}
|
||||
|
||||
var messageChannel = new MessageChannel();
|
||||
sourceChannel.send(goog.messaging.PortNetwork.GRANT_CONNECTION_SERVICE, {
|
||||
'success': true,
|
||||
'name': requestedName,
|
||||
'port': messageChannel.port1
|
||||
});
|
||||
requestedChannel.send(goog.messaging.PortNetwork.GRANT_CONNECTION_SERVICE, {
|
||||
'success': true,
|
||||
'name': sourceName,
|
||||
'port': messageChannel.port2
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Connects together the operator and a caller by creating a
|
||||
* {@link MessageChannel} and sending one end to the remote context.
|
||||
*
|
||||
* @param {string} contextName The name of the context to which to connect the
|
||||
* operator.
|
||||
* @private
|
||||
*/
|
||||
goog.messaging.PortOperator.prototype.connectSelfToPort_ = function(
|
||||
contextName) {
|
||||
if (contextName in this.connections_) {
|
||||
// We've already established a connection with this port.
|
||||
return;
|
||||
}
|
||||
|
||||
var contextChannel = this.switchboard_[contextName];
|
||||
if (!contextChannel) {
|
||||
throw Error('Port "' + contextName + '" doesn\'t exist');
|
||||
}
|
||||
|
||||
var messageChannel = new MessageChannel();
|
||||
contextChannel.send(goog.messaging.PortNetwork.GRANT_CONNECTION_SERVICE, {
|
||||
'success': true,
|
||||
'name': this.name_,
|
||||
'port': messageChannel.port1
|
||||
});
|
||||
messageChannel.port2.start();
|
||||
this.connections_[contextName] =
|
||||
new goog.messaging.PortChannel(messageChannel.port2);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.messaging.PortOperator.prototype.disposeInternal = function() {
|
||||
goog.object.forEach(this.switchboard_, goog.dispose);
|
||||
goog.object.forEach(this.connections_, goog.dispose);
|
||||
delete this.switchboard_;
|
||||
delete this.connections_;
|
||||
goog.base(this, 'disposeInternal');
|
||||
};
|
||||
@@ -0,0 +1,236 @@
|
||||
// Copyright 2012 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 Definition of goog.messaging.RespondingChannel, which wraps a
|
||||
* MessageChannel and allows the user to get the response from the services.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
goog.provide('goog.messaging.RespondingChannel');
|
||||
|
||||
goog.require('goog.Disposable');
|
||||
goog.require('goog.log');
|
||||
goog.require('goog.messaging.MessageChannel'); // interface
|
||||
goog.require('goog.messaging.MultiChannel');
|
||||
goog.require('goog.messaging.MultiChannel.VirtualChannel');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new RespondingChannel wrapping a single MessageChannel.
|
||||
* @param {goog.messaging.MessageChannel} messageChannel The messageChannel to
|
||||
* to wrap and allow for responses. This channel must not have any existing
|
||||
* services registered. All service registration must be done through the
|
||||
* {@link RespondingChannel#registerService} api instead. The other end of
|
||||
* channel must also be a RespondingChannel.
|
||||
* @constructor
|
||||
* @extends {goog.Disposable}
|
||||
*/
|
||||
goog.messaging.RespondingChannel = function(messageChannel) {
|
||||
goog.base(this);
|
||||
|
||||
/**
|
||||
* The message channel wrapped in a MultiChannel so we can send private and
|
||||
* public messages on it.
|
||||
* @type {goog.messaging.MultiChannel}
|
||||
* @private
|
||||
*/
|
||||
this.messageChannel_ = new goog.messaging.MultiChannel(messageChannel);
|
||||
|
||||
/**
|
||||
* Map of invocation signatures to function callbacks. These are used to keep
|
||||
* track of the asyncronous service invocations so the result of a service
|
||||
* call can be passed back to a callback in the calling frame.
|
||||
* @type {Object.<number, function(Object)>}
|
||||
* @private
|
||||
*/
|
||||
this.sigCallbackMap_ = {};
|
||||
|
||||
/**
|
||||
* The virtual channel to send private messages on.
|
||||
* @type {goog.messaging.MultiChannel.VirtualChannel}
|
||||
* @private
|
||||
*/
|
||||
this.privateChannel_ = this.messageChannel_.createVirtualChannel(
|
||||
goog.messaging.RespondingChannel.PRIVATE_CHANNEL_);
|
||||
|
||||
/**
|
||||
* The virtual channel to send public messages on.
|
||||
* @type {goog.messaging.MultiChannel.VirtualChannel}
|
||||
* @private
|
||||
*/
|
||||
this.publicChannel_ = this.messageChannel_.createVirtualChannel(
|
||||
goog.messaging.RespondingChannel.PUBLIC_CHANNEL_);
|
||||
|
||||
this.privateChannel_.registerService(
|
||||
goog.messaging.RespondingChannel.CALLBACK_SERVICE_,
|
||||
goog.bind(this.callbackServiceHandler_, this),
|
||||
true);
|
||||
};
|
||||
goog.inherits(goog.messaging.RespondingChannel, goog.Disposable);
|
||||
|
||||
|
||||
/**
|
||||
* The name of the method invocation callback service (used internally).
|
||||
* @type {string}
|
||||
* @const
|
||||
* @private
|
||||
*/
|
||||
goog.messaging.RespondingChannel.CALLBACK_SERVICE_ = 'mics';
|
||||
|
||||
|
||||
/**
|
||||
* The name of the channel to send private control messages on.
|
||||
* @type {string}
|
||||
* @const
|
||||
* @private
|
||||
*/
|
||||
goog.messaging.RespondingChannel.PRIVATE_CHANNEL_ = 'private';
|
||||
|
||||
|
||||
/**
|
||||
* The name of the channel to send public messages on.
|
||||
* @type {string}
|
||||
* @const
|
||||
* @private
|
||||
*/
|
||||
goog.messaging.RespondingChannel.PUBLIC_CHANNEL_ = 'public';
|
||||
|
||||
|
||||
/**
|
||||
* The next signature index to save the callback against.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.messaging.RespondingChannel.prototype.nextSignatureIndex_ = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Logger object for goog.messaging.RespondingChannel.
|
||||
* @type {goog.log.Logger}
|
||||
* @private
|
||||
*/
|
||||
goog.messaging.RespondingChannel.prototype.logger_ =
|
||||
goog.log.getLogger('goog.messaging.RespondingChannel');
|
||||
|
||||
|
||||
/**
|
||||
* Gets a random number to use for method invocation results.
|
||||
* @return {number} A unique random signature.
|
||||
* @private
|
||||
*/
|
||||
goog.messaging.RespondingChannel.prototype.getNextSignature_ = function() {
|
||||
return this.nextSignatureIndex_++;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.messaging.RespondingChannel.prototype.disposeInternal = function() {
|
||||
goog.dispose(this.messageChannel_);
|
||||
delete this.messageChannel_;
|
||||
// Note: this.publicChannel_ and this.privateChannel_ get disposed by
|
||||
// this.messageChannel_
|
||||
delete this.publicChannel_;
|
||||
delete this.privateChannel_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sends a message over the channel.
|
||||
* @param {string} serviceName The name of the service this message should be
|
||||
* delivered to.
|
||||
* @param {string|!Object} payload The value of the message. If this is an
|
||||
* Object, it is serialized to a string before sending if necessary.
|
||||
* @param {function(?Object)} callback The callback invoked with
|
||||
* the result of the service call.
|
||||
*/
|
||||
goog.messaging.RespondingChannel.prototype.send = function(
|
||||
serviceName,
|
||||
payload,
|
||||
callback) {
|
||||
|
||||
var signature = this.getNextSignature_();
|
||||
this.sigCallbackMap_[signature] = callback;
|
||||
|
||||
var message = {};
|
||||
message['signature'] = signature;
|
||||
message['data'] = payload;
|
||||
|
||||
this.publicChannel_.send(serviceName, message);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Receives the results of the peer's service results.
|
||||
* @param {!Object|string} message The results from the remote service
|
||||
* invocation.
|
||||
* @private
|
||||
*/
|
||||
goog.messaging.RespondingChannel.prototype.callbackServiceHandler_ = function(
|
||||
message) {
|
||||
|
||||
var signature = message['signature'];
|
||||
var result = message['data'];
|
||||
|
||||
if (signature in this.sigCallbackMap_) {
|
||||
var callback = /** @type {function(Object)} */ (this.sigCallbackMap_[
|
||||
signature]);
|
||||
callback(result);
|
||||
delete this.sigCallbackMap_[signature];
|
||||
} else {
|
||||
goog.log.warning(this.logger_, 'Received signature is invalid');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Registers a service to be called when a message is received.
|
||||
* @param {string} serviceName The name of the service.
|
||||
* @param {function(!Object)} callback The callback to process the
|
||||
* incoming messages. Passed the payload.
|
||||
*/
|
||||
goog.messaging.RespondingChannel.prototype.registerService = function(
|
||||
serviceName, callback) {
|
||||
this.publicChannel_.registerService(
|
||||
serviceName,
|
||||
goog.bind(this.callbackProxy_, this, callback),
|
||||
true);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A intermediary proxy for service callbacks to be invoked and return their
|
||||
* their results to the remote caller's callback.
|
||||
* @param {function((string|!Object))} callback The callback to process the
|
||||
* incoming messages. Passed the payload.
|
||||
* @param {!Object|string} message The message containing the signature and
|
||||
* the data to invoke the service callback with.
|
||||
* @private
|
||||
*/
|
||||
goog.messaging.RespondingChannel.prototype.callbackProxy_ = function(
|
||||
callback, message) {
|
||||
|
||||
var resultMessage = {};
|
||||
resultMessage['data'] = callback(message['data']);
|
||||
resultMessage['signature'] = message['signature'];
|
||||
// The callback invoked above may have disposed the channel so check if it
|
||||
// exists.
|
||||
if (this.privateChannel_) {
|
||||
this.privateChannel_.send(
|
||||
goog.messaging.RespondingChannel.CALLBACK_SERVICE_,
|
||||
resultMessage);
|
||||
}
|
||||
};
|
||||
37
nicer-api-docs/closure-library/closure/goog/messaging/testdata/portchannel_worker.js
vendored
Normal file
37
nicer-api-docs/closure-library/closure/goog/messaging/testdata/portchannel_worker.js
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright 2010 The Closure Library Authors. All Rights Reserved.
|
||||
|
||||
// Use of this source code is governed by the Apache License, Version 2.0.
|
||||
// See the COPYING file for details.
|
||||
|
||||
/**
|
||||
* @fileoverview A web worker for integration testing the PortChannel class.
|
||||
*
|
||||
* @nocompile
|
||||
*/
|
||||
|
||||
self.CLOSURE_BASE_PATH = '../../';
|
||||
importScripts('../../bootstrap/webworkers.js');
|
||||
importScripts('../../base.js');
|
||||
|
||||
// The provide is necessary to stop the jscompiler from thinking this is an
|
||||
// entry point and adding it into the manifest incorrectly.
|
||||
goog.provide('goog.messaging.testdata.portchannel_worker');
|
||||
goog.require('goog.messaging.PortChannel');
|
||||
|
||||
function registerPing(channel) {
|
||||
channel.registerService('ping', function(msg) {
|
||||
channel.send('pong', msg);
|
||||
}, true);
|
||||
}
|
||||
|
||||
function startListening() {
|
||||
var channel = new goog.messaging.PortChannel(self);
|
||||
registerPing(channel);
|
||||
|
||||
channel.registerService('addPort', function(port) {
|
||||
port.start();
|
||||
registerPing(new goog.messaging.PortChannel(port));
|
||||
}, true);
|
||||
}
|
||||
|
||||
startListening();
|
||||
32
nicer-api-docs/closure-library/closure/goog/messaging/testdata/portnetwork_worker1.js
vendored
Normal file
32
nicer-api-docs/closure-library/closure/goog/messaging/testdata/portnetwork_worker1.js
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright 2011 The Closure Library Authors. All Rights Reserved.
|
||||
|
||||
// Use of this source code is governed by the Apache License, Version 2.0.
|
||||
// See the COPYING file for details.
|
||||
|
||||
/**
|
||||
* @fileoverview A web worker for integration testing the PortPool class.
|
||||
*
|
||||
* @nocompile
|
||||
*/
|
||||
|
||||
self.CLOSURE_BASE_PATH = '../../';
|
||||
importScripts('../../bootstrap/webworkers.js');
|
||||
importScripts('../../base.js');
|
||||
|
||||
// The provide is necessary to stop the jscompiler from thinking this is an
|
||||
// entry point and adding it into the manifest incorrectly.
|
||||
goog.provide('goog.messaging.testdata.portnetwork_worker1');
|
||||
goog.require('goog.messaging.PortCaller');
|
||||
goog.require('goog.messaging.PortChannel');
|
||||
|
||||
function startListening() {
|
||||
var caller = new goog.messaging.PortCaller(
|
||||
new goog.messaging.PortChannel(self));
|
||||
|
||||
caller.dial('frame').registerService('sendToMain', function(msg) {
|
||||
msg.push('worker1');
|
||||
caller.dial('main').send('result', msg);
|
||||
}, true);
|
||||
}
|
||||
|
||||
startListening();
|
||||
32
nicer-api-docs/closure-library/closure/goog/messaging/testdata/portnetwork_worker2.js
vendored
Normal file
32
nicer-api-docs/closure-library/closure/goog/messaging/testdata/portnetwork_worker2.js
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright 2011 The Closure Library Authors. All Rights Reserved.
|
||||
|
||||
// Use of this source code is governed by the Apache License, Version 2.0.
|
||||
// See the COPYING file for details.
|
||||
|
||||
/**
|
||||
* @fileoverview A web worker for integration testing the PortPool class.
|
||||
*
|
||||
* @nocompile
|
||||
*/
|
||||
|
||||
self.CLOSURE_BASE_PATH = '../../';
|
||||
importScripts('../../bootstrap/webworkers.js');
|
||||
importScripts('../../base.js');
|
||||
|
||||
// The provide is necessary to stop the jscompiler from thinking this is an
|
||||
// entry point and adding it into the manifest incorrectly.
|
||||
goog.provide('goog.messaging.testdata.portnetwork_worker2');
|
||||
goog.require('goog.messaging.PortCaller');
|
||||
goog.require('goog.messaging.PortChannel');
|
||||
|
||||
function startListening() {
|
||||
var caller = new goog.messaging.PortCaller(
|
||||
new goog.messaging.PortChannel(self));
|
||||
|
||||
caller.dial('main').registerService('sendToFrame', function(msg) {
|
||||
msg.push('worker2');
|
||||
caller.dial('frame').send('sendToWorker1', msg);
|
||||
}, true);
|
||||
}
|
||||
|
||||
startListening();
|
||||
Reference in New Issue
Block a user