Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/third_party/closure/goog/messaging/bufferedchannel.js
2868 views
1
// Copyright 2010 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
/**
16
* @fileoverview A wrapper for asynchronous message-passing channels that buffer
17
* their output until both ends of the channel are connected.
18
*
19
*/
20
21
goog.provide('goog.messaging.BufferedChannel');
22
23
goog.require('goog.Disposable');
24
goog.require('goog.Timer');
25
goog.require('goog.events');
26
goog.require('goog.log');
27
goog.require('goog.messaging.MessageChannel');
28
goog.require('goog.messaging.MultiChannel');
29
30
31
32
/**
33
* Creates a new BufferedChannel, which operates like its underlying channel
34
* except that it buffers calls to send until it receives a message from its
35
* peer claiming that the peer is ready to receive. The peer is also expected
36
* to be a BufferedChannel, though this is not enforced.
37
*
38
* @param {!goog.messaging.MessageChannel} messageChannel The MessageChannel
39
* we're wrapping.
40
* @param {number=} opt_interval Polling interval for sending ready
41
* notifications to peer, in ms. Default is 50.
42
* @constructor
43
* @extends {goog.Disposable}
44
* @implements {goog.messaging.MessageChannel};
45
* @final
46
*/
47
goog.messaging.BufferedChannel = function(messageChannel, opt_interval) {
48
goog.Disposable.call(this);
49
50
/**
51
* Buffer of messages to be sent when the channel's peer is ready.
52
*
53
* @type {Array<Object>}
54
* @private
55
*/
56
this.buffer_ = [];
57
58
/**
59
* Channel dispatcher wrapping the underlying delegate channel.
60
*
61
* @type {!goog.messaging.MultiChannel}
62
* @private
63
*/
64
this.multiChannel_ = new goog.messaging.MultiChannel(messageChannel);
65
66
/**
67
* Virtual channel for carrying the user's messages.
68
*
69
* @type {!goog.messaging.MessageChannel}
70
* @private
71
*/
72
this.userChannel_ = this.multiChannel_.createVirtualChannel(
73
goog.messaging.BufferedChannel.USER_CHANNEL_NAME_);
74
75
/**
76
* Virtual channel for carrying control messages for BufferedChannel.
77
*
78
* @type {!goog.messaging.MessageChannel}
79
* @private
80
*/
81
this.controlChannel_ = this.multiChannel_.createVirtualChannel(
82
goog.messaging.BufferedChannel.CONTROL_CHANNEL_NAME_);
83
84
/**
85
* Timer for the peer ready ping loop.
86
*
87
* @type {goog.Timer}
88
* @private
89
*/
90
this.timer_ = new goog.Timer(
91
opt_interval || goog.messaging.BufferedChannel.DEFAULT_INTERVAL_MILLIS_);
92
93
this.timer_.start();
94
goog.events.listen(
95
this.timer_, goog.Timer.TICK, this.sendReadyPing_, false, this);
96
97
this.controlChannel_.registerService(
98
goog.messaging.BufferedChannel.PEER_READY_SERVICE_NAME_,
99
goog.bind(this.setPeerReady_, this));
100
};
101
goog.inherits(goog.messaging.BufferedChannel, goog.Disposable);
102
103
104
/**
105
* Default polling interval (in ms) for setPeerReady_ notifications.
106
*
107
* @type {number}
108
* @const
109
* @private
110
*/
111
goog.messaging.BufferedChannel.DEFAULT_INTERVAL_MILLIS_ = 50;
112
113
114
/**
115
* The name of the private service which handles peer ready pings. The
116
* service registered with this name is bound to this.setPeerReady_, an internal
117
* part of BufferedChannel's implementation that clients should not send to
118
* directly.
119
*
120
* @type {string}
121
* @const
122
* @private
123
*/
124
goog.messaging.BufferedChannel.PEER_READY_SERVICE_NAME_ = 'setPeerReady_';
125
126
127
/**
128
* The name of the virtual channel along which user messages are sent.
129
*
130
* @type {string}
131
* @const
132
* @private
133
*/
134
goog.messaging.BufferedChannel.USER_CHANNEL_NAME_ = 'user';
135
136
137
/**
138
* The name of the virtual channel along which internal control messages are
139
* sent.
140
*
141
* @type {string}
142
* @const
143
* @private
144
*/
145
goog.messaging.BufferedChannel.CONTROL_CHANNEL_NAME_ = 'control';
146
147
148
/** @override */
149
goog.messaging.BufferedChannel.prototype.connect = function(opt_connectCb) {
150
if (opt_connectCb) {
151
opt_connectCb();
152
}
153
};
154
155
156
/** @override */
157
goog.messaging.BufferedChannel.prototype.isConnected = function() {
158
return true;
159
};
160
161
162
/**
163
* @return {boolean} Whether the channel's peer is ready.
164
*/
165
goog.messaging.BufferedChannel.prototype.isPeerReady = function() {
166
return this.peerReady_;
167
};
168
169
170
/**
171
* Logger.
172
*
173
* @type {goog.log.Logger}
174
* @const
175
* @private
176
*/
177
goog.messaging.BufferedChannel.prototype.logger_ =
178
goog.log.getLogger('goog.messaging.bufferedchannel');
179
180
181
/**
182
* Handles one tick of our peer ready notification loop. This entails sending a
183
* ready ping to the peer and shutting down the loop if we've received a ping
184
* ourselves.
185
*
186
* @private
187
*/
188
goog.messaging.BufferedChannel.prototype.sendReadyPing_ = function() {
189
try {
190
this.controlChannel_.send(
191
goog.messaging.BufferedChannel.PEER_READY_SERVICE_NAME_,
192
/* payload */ this.isPeerReady() ? '1' : '');
193
} catch (e) {
194
this.timer_.stop(); // So we don't keep calling send and re-throwing.
195
throw e;
196
}
197
};
198
199
200
/**
201
* Whether or not the peer channel is ready to receive messages.
202
*
203
* @type {boolean}
204
* @private
205
*/
206
goog.messaging.BufferedChannel.prototype.peerReady_;
207
208
209
/** @override */
210
goog.messaging.BufferedChannel.prototype.registerService = function(
211
serviceName, callback, opt_objectPayload) {
212
this.userChannel_.registerService(serviceName, callback, opt_objectPayload);
213
};
214
215
216
/** @override */
217
goog.messaging.BufferedChannel.prototype.registerDefaultService = function(
218
callback) {
219
this.userChannel_.registerDefaultService(callback);
220
};
221
222
223
/**
224
* Send a message over the channel. If the peer is not ready, the message will
225
* be buffered and sent once we've received a ready message from our peer.
226
*
227
* @param {string} serviceName The name of the service this message should be
228
* delivered to.
229
* @param {string|!Object} payload The value of the message. If this is an
230
* Object, it is serialized to JSON before sending. It's the responsibility
231
* of implementors of this class to perform the serialization.
232
* @see goog.net.xpc.BufferedChannel.send
233
* @override
234
*/
235
goog.messaging.BufferedChannel.prototype.send = function(serviceName, payload) {
236
if (this.isPeerReady()) {
237
this.userChannel_.send(serviceName, payload);
238
} else {
239
goog.log.fine(
240
goog.messaging.BufferedChannel.prototype.logger_,
241
'buffering message ' + serviceName);
242
this.buffer_.push({serviceName: serviceName, payload: payload});
243
}
244
};
245
246
247
/**
248
* Marks the channel's peer as ready, then sends buffered messages and nulls the
249
* buffer. Subsequent calls to setPeerReady_ have no effect.
250
*
251
* @param {(!Object|string)} peerKnowsWeKnowItsReady Passed by the peer to
252
* indicate whether it knows that we've received its ping and that it's
253
* ready. Non-empty if true, empty if false.
254
* @private
255
*/
256
goog.messaging.BufferedChannel.prototype.setPeerReady_ = function(
257
peerKnowsWeKnowItsReady) {
258
if (peerKnowsWeKnowItsReady) {
259
this.timer_.stop();
260
} else {
261
// Our peer doesn't know we're ready, so restart (or continue) pinging.
262
// Restarting may be needed if the peer iframe was reloaded after the
263
// connection was first established.
264
this.timer_.start();
265
}
266
267
if (this.peerReady_) {
268
return;
269
}
270
this.peerReady_ = true;
271
// Send one last ping so that the peer knows we know it's ready.
272
this.sendReadyPing_();
273
for (var i = 0; i < this.buffer_.length; i++) {
274
var message = this.buffer_[i];
275
goog.log.fine(
276
goog.messaging.BufferedChannel.prototype.logger_,
277
'sending buffered message ' + message.serviceName);
278
this.userChannel_.send(message.serviceName, message.payload);
279
}
280
this.buffer_ = null;
281
};
282
283
284
/** @override */
285
goog.messaging.BufferedChannel.prototype.disposeInternal = function() {
286
goog.dispose(this.multiChannel_);
287
goog.dispose(this.timer_);
288
goog.messaging.BufferedChannel.base(this, 'disposeInternal');
289
};
290
291