Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/third_party/closure/goog/async/nexttick.js
2868 views
1
// Copyright 2013 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 Provides a function to schedule running a function as soon
17
* as possible after the current JS execution stops and yields to the event
18
* loop.
19
*
20
*/
21
22
goog.provide('goog.async.nextTick');
23
goog.provide('goog.async.throwException');
24
25
goog.require('goog.debug.entryPointRegistry');
26
goog.require('goog.dom.TagName');
27
goog.require('goog.functions');
28
goog.require('goog.labs.userAgent.browser');
29
goog.require('goog.labs.userAgent.engine');
30
31
32
/**
33
* Throw an item without interrupting the current execution context. For
34
* example, if processing a group of items in a loop, sometimes it is useful
35
* to report an error while still allowing the rest of the batch to be
36
* processed.
37
* @param {*} exception
38
*/
39
goog.async.throwException = function(exception) {
40
// Each throw needs to be in its own context.
41
goog.global.setTimeout(function() { throw exception; }, 0);
42
};
43
44
45
/**
46
* Fires the provided callbacks as soon as possible after the current JS
47
* execution context. setTimeout(…, 0) takes at least 4ms when called from
48
* within another setTimeout(…, 0) for legacy reasons.
49
*
50
* This will not schedule the callback as a microtask (i.e. a task that can
51
* preempt user input or networking callbacks). It is meant to emulate what
52
* setTimeout(_, 0) would do if it were not throttled. If you desire microtask
53
* behavior, use {@see goog.Promise} instead.
54
*
55
* @param {function(this:SCOPE)} callback Callback function to fire as soon as
56
* possible.
57
* @param {SCOPE=} opt_context Object in whose scope to call the listener.
58
* @param {boolean=} opt_useSetImmediate Avoid the IE workaround that
59
* ensures correctness at the cost of speed. See comments for details.
60
* @template SCOPE
61
*/
62
goog.async.nextTick = function(callback, opt_context, opt_useSetImmediate) {
63
var cb = callback;
64
if (opt_context) {
65
cb = goog.bind(callback, opt_context);
66
}
67
cb = goog.async.nextTick.wrapCallback_(cb);
68
// Note we do allow callers to also request setImmediate if they are willing
69
// to accept the possible tradeoffs of incorrectness in exchange for speed.
70
// The IE fallback of readystate change is much slower. See useSetImmediate_
71
// for details.
72
if (goog.isFunction(goog.global.setImmediate) &&
73
(opt_useSetImmediate || goog.async.nextTick.useSetImmediate_())) {
74
goog.global.setImmediate(cb);
75
return;
76
}
77
78
// Look for and cache the custom fallback version of setImmediate.
79
if (!goog.async.nextTick.setImmediate_) {
80
goog.async.nextTick.setImmediate_ =
81
goog.async.nextTick.getSetImmediateEmulator_();
82
}
83
goog.async.nextTick.setImmediate_(cb);
84
};
85
86
87
/**
88
* Returns whether should use setImmediate implementation currently on window.
89
*
90
* window.setImmediate was introduced and currently only supported by IE10+,
91
* but due to a bug in the implementation it is not guaranteed that
92
* setImmediate is faster than setTimeout nor that setImmediate N is before
93
* setImmediate N+1. That is why we do not use the native version if
94
* available. We do, however, call setImmediate if it is a non-native function
95
* because that indicates that it has been replaced by goog.testing.MockClock
96
* which we do want to support.
97
* See
98
* http://connect.microsoft.com/IE/feedback/details/801823/setimmediate-and-messagechannel-are-broken-in-ie10
99
*
100
* @return {boolean} Whether to use the implementation of setImmediate defined
101
* on Window.
102
* @private
103
*/
104
goog.async.nextTick.useSetImmediate_ = function() {
105
// Not a browser environment.
106
if (!goog.global.Window || !goog.global.Window.prototype) {
107
return true;
108
}
109
110
// MS Edge has window.setImmediate natively, but it's not on Window.prototype.
111
// Also, there's no clean way to detect if the goog.global.setImmediate has
112
// been replaced by mockClock as its replacement also shows up as "[native
113
// code]" when using toString. Therefore, just always use
114
// goog.global.setImmediate for Edge. It's unclear if it suffers the same
115
// issues as IE10/11, but based on
116
// https://dev.modern.ie/testdrive/demos/setimmediatesorting/
117
// it seems they've been working to ensure it's WAI.
118
if (goog.labs.userAgent.browser.isEdge() ||
119
goog.global.Window.prototype.setImmediate != goog.global.setImmediate) {
120
// Something redefined setImmediate in which case we decide to use it (This
121
// is so that we use the mockClock setImmediate).
122
return true;
123
}
124
125
return false;
126
};
127
128
129
/**
130
* Cache for the setImmediate implementation.
131
* @type {function(function())}
132
* @private
133
*/
134
goog.async.nextTick.setImmediate_;
135
136
137
/**
138
* Determines the best possible implementation to run a function as soon as
139
* the JS event loop is idle.
140
* @return {function(function())} The "setImmediate" implementation.
141
* @private
142
*/
143
goog.async.nextTick.getSetImmediateEmulator_ = function() {
144
// Create a private message channel and use it to postMessage empty messages
145
// to ourselves.
146
/** @type {!Function|undefined} */
147
var Channel = goog.global['MessageChannel'];
148
// If MessageChannel is not available and we are in a browser, implement
149
// an iframe based polyfill in browsers that have postMessage and
150
// document.addEventListener. The latter excludes IE8 because it has a
151
// synchronous postMessage implementation.
152
if (typeof Channel === 'undefined' && typeof window !== 'undefined' &&
153
window.postMessage && window.addEventListener &&
154
// Presto (The old pre-blink Opera engine) has problems with iframes
155
// and contentWindow.
156
!goog.labs.userAgent.engine.isPresto()) {
157
/** @constructor */
158
Channel = function() {
159
// Make an empty, invisible iframe.
160
var iframe = /** @type {!HTMLIFrameElement} */ (
161
document.createElement(String(goog.dom.TagName.IFRAME)));
162
iframe.style.display = 'none';
163
iframe.src = '';
164
document.documentElement.appendChild(iframe);
165
var win = iframe.contentWindow;
166
var doc = win.document;
167
doc.open();
168
doc.write('');
169
doc.close();
170
// Do not post anything sensitive over this channel, as the workaround for
171
// pages with file: origin could allow that information to be modified or
172
// intercepted.
173
var message = 'callImmediate' + Math.random();
174
// The same origin policy rejects attempts to postMessage from file: urls
175
// unless the origin is '*'.
176
// TODO(b/16335441): Use '*' origin for data: and other similar protocols.
177
var origin = win.location.protocol == 'file:' ?
178
'*' :
179
win.location.protocol + '//' + win.location.host;
180
var onmessage = goog.bind(function(e) {
181
// Validate origin and message to make sure that this message was
182
// intended for us. If the origin is set to '*' (see above) only the
183
// message needs to match since, for example, '*' != 'file://'. Allowing
184
// the wildcard is ok, as we are not concerned with security here.
185
if ((origin != '*' && e.origin != origin) || e.data != message) {
186
return;
187
}
188
this['port1'].onmessage();
189
}, this);
190
win.addEventListener('message', onmessage, false);
191
this['port1'] = {};
192
this['port2'] = {
193
postMessage: function() { win.postMessage(message, origin); }
194
};
195
};
196
}
197
if (typeof Channel !== 'undefined' && !goog.labs.userAgent.browser.isIE()) {
198
// Exclude all of IE due to
199
// http://codeforhire.com/2013/09/21/setimmediate-and-messagechannel-broken-on-internet-explorer-10/
200
// which allows starving postMessage with a busy setTimeout loop.
201
// This currently affects IE10 and IE11 which would otherwise be able
202
// to use the postMessage based fallbacks.
203
var channel = new Channel();
204
// Use a fifo linked list to call callbacks in the right order.
205
var head = {};
206
var tail = head;
207
channel['port1'].onmessage = function() {
208
if (goog.isDef(head.next)) {
209
head = head.next;
210
var cb = head.cb;
211
head.cb = null;
212
cb();
213
}
214
};
215
return function(cb) {
216
tail.next = {cb: cb};
217
tail = tail.next;
218
channel['port2'].postMessage(0);
219
};
220
}
221
// Implementation for IE6 to IE10: Script elements fire an asynchronous
222
// onreadystatechange event when inserted into the DOM.
223
if (typeof document !== 'undefined' &&
224
'onreadystatechange' in
225
document.createElement(String(goog.dom.TagName.SCRIPT))) {
226
return function(cb) {
227
var script = document.createElement(String(goog.dom.TagName.SCRIPT));
228
script.onreadystatechange = function() {
229
// Clean up and call the callback.
230
script.onreadystatechange = null;
231
script.parentNode.removeChild(script);
232
script = null;
233
cb();
234
cb = null;
235
};
236
document.documentElement.appendChild(script);
237
};
238
}
239
// Fall back to setTimeout with 0. In browsers this creates a delay of 5ms
240
// or more.
241
// NOTE(user): This fallback is used for IE11.
242
return function(cb) {
243
goog.global.setTimeout(/** @type {function()} */ (cb), 0);
244
};
245
};
246
247
248
/**
249
* Helper function that is overrided to protect callbacks with entry point
250
* monitor if the application monitors entry points.
251
* @param {function()} callback Callback function to fire as soon as possible.
252
* @return {function()} The wrapped callback.
253
* @private
254
*/
255
goog.async.nextTick.wrapCallback_ = goog.functions.identity;
256
257
258
// Register the callback function as an entry point, so that it can be
259
// monitored for exception handling, etc. This has to be done in this file
260
// since it requires special code to handle all browsers.
261
goog.debug.entryPointRegistry.register(
262
/**
263
* @param {function(!Function): !Function} transformer The transforming
264
* function.
265
*/
266
function(transformer) { goog.async.nextTick.wrapCallback_ = transformer; });
267
268