portcaller.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. // Copyright 2011 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /**
  15. * @fileoverview The leaf node of a {@link goog.messaging.PortNetwork}. Callers
  16. * connect to the operator, and request connections with other contexts from it.
  17. *
  18. */
  19. goog.provide('goog.messaging.PortCaller');
  20. goog.require('goog.Disposable');
  21. goog.require('goog.async.Deferred');
  22. goog.require('goog.messaging.DeferredChannel');
  23. goog.require('goog.messaging.PortChannel');
  24. goog.require('goog.messaging.PortNetwork'); // interface
  25. goog.require('goog.object');
  26. /**
  27. * The leaf node of a network.
  28. *
  29. * @param {!goog.messaging.MessageChannel} operatorPort The channel for
  30. * communicating with the operator. The other side of this channel should be
  31. * passed to {@link goog.messaging.PortOperator#addPort}. Must be either a
  32. * {@link goog.messaging.PortChannel} or a decorator wrapping a PortChannel;
  33. * in particular, it must be able to send and receive {@link MessagePort}s.
  34. * @constructor
  35. * @extends {goog.Disposable}
  36. * @implements {goog.messaging.PortNetwork}
  37. * @final
  38. */
  39. goog.messaging.PortCaller = function(operatorPort) {
  40. goog.messaging.PortCaller.base(this, 'constructor');
  41. /**
  42. * The channel to the {@link goog.messaging.PortOperator} for this network.
  43. *
  44. * @type {!goog.messaging.MessageChannel}
  45. * @private
  46. */
  47. this.operatorPort_ = operatorPort;
  48. /**
  49. * The collection of channels for communicating with other contexts in the
  50. * network. Each value can contain a {@link goog.aync.Deferred} and/or a
  51. * {@link goog.messaging.MessageChannel}.
  52. *
  53. * If the value contains a Deferred, then the channel is a
  54. * {@link goog.messaging.DeferredChannel} wrapping that Deferred. The Deferred
  55. * will be resolved with a {@link goog.messaging.PortChannel} once we receive
  56. * the appropriate port from the operator. This is the situation when this
  57. * caller requests a connection to another context; the DeferredChannel is
  58. * used to queue up messages until we receive the port from the operator.
  59. *
  60. * If the value does not contain a Deferred, then the channel is simply a
  61. * {@link goog.messaging.PortChannel} communicating with the given context.
  62. * This is the situation when this context received a port for the other
  63. * context before it was requested.
  64. *
  65. * If a value exists for a given key, it must contain a channel, but it
  66. * doesn't necessarily contain a Deferred.
  67. *
  68. * @type {!Object<{deferred: goog.async.Deferred,
  69. * channel: !goog.messaging.MessageChannel}>}
  70. * @private
  71. */
  72. this.connections_ = {};
  73. this.operatorPort_.registerService(
  74. goog.messaging.PortNetwork.GRANT_CONNECTION_SERVICE,
  75. goog.bind(this.connectionGranted_, this), true /* opt_json */);
  76. };
  77. goog.inherits(goog.messaging.PortCaller, goog.Disposable);
  78. /** @override */
  79. goog.messaging.PortCaller.prototype.dial = function(name) {
  80. if (name in this.connections_) {
  81. return this.connections_[name].channel;
  82. }
  83. this.operatorPort_.send(
  84. goog.messaging.PortNetwork.REQUEST_CONNECTION_SERVICE, name);
  85. var deferred = new goog.async.Deferred();
  86. var channel = new goog.messaging.DeferredChannel(deferred);
  87. this.connections_[name] = {deferred: deferred, channel: channel};
  88. return channel;
  89. };
  90. /**
  91. * Registers a connection to another context in the network. This is called when
  92. * the operator sends us one end of a {@link MessageChannel}, either because
  93. * this caller requested a connection with another context, or because that
  94. * context requested a connection with this caller.
  95. *
  96. * It's possible that the remote context and this one request each other roughly
  97. * concurrently. The operator doesn't keep track of which contexts have been
  98. * connected, so it will create two separate {@link MessageChannel}s in this
  99. * case. However, the first channel created will reach both contexts first, so
  100. * we simply ignore all connections with a given context after the first.
  101. *
  102. * @param {!Object|string} message The name of the context
  103. * being connected and the port connecting the context.
  104. * @private
  105. */
  106. goog.messaging.PortCaller.prototype.connectionGranted_ = function(message) {
  107. var args = /** @type {{name: string, port: MessagePort}} */ (message);
  108. var port = args['port'];
  109. var entry = this.connections_[args['name']];
  110. if (entry && (!entry.deferred || entry.deferred.hasFired())) {
  111. // If two PortCallers request one another at the same time, the operator may
  112. // send out a channel for connecting them multiple times. Since both callers
  113. // will receive the first channel's ports first, we can safely ignore and
  114. // close any future ports.
  115. port.close();
  116. } else if (!args['success']) {
  117. throw Error(args['message']);
  118. } else {
  119. port.start();
  120. var channel = new goog.messaging.PortChannel(port);
  121. if (entry) {
  122. entry.deferred.callback(channel);
  123. } else {
  124. this.connections_[args['name']] = {channel: channel, deferred: null};
  125. }
  126. }
  127. };
  128. /** @override */
  129. goog.messaging.PortCaller.prototype.disposeInternal = function() {
  130. goog.dispose(this.operatorPort_);
  131. goog.object.forEach(this.connections_, goog.dispose);
  132. delete this.operatorPort_;
  133. delete this.connections_;
  134. goog.messaging.PortCaller.base(this, 'disposeInternal');
  135. };