Index: third_party/google_input_tools/third_party/closure_library/closure/goog/async/nexttick.js |
diff --git a/third_party/google_input_tools/third_party/closure_library/closure/goog/async/nexttick.js b/third_party/google_input_tools/third_party/closure_library/closure/goog/async/nexttick.js |
index ad2cb9e5891857429d256b70401bb62b0d9528ee..bdbad6b804c88a92558abe22f8687b5719ddbc50 100644 |
--- a/third_party/google_input_tools/third_party/closure_library/closure/goog/async/nexttick.js |
+++ b/third_party/google_input_tools/third_party/closure_library/closure/goog/async/nexttick.js |
@@ -23,7 +23,10 @@ goog.provide('goog.async.nextTick'); |
goog.provide('goog.async.throwException'); |
goog.require('goog.debug.entryPointRegistry'); |
+goog.require('goog.dom.TagName'); |
goog.require('goog.functions'); |
+goog.require('goog.labs.userAgent.browser'); |
+goog.require('goog.labs.userAgent.engine'); |
/** |
@@ -41,8 +44,8 @@ goog.async.throwException = function(exception) { |
/** |
* Fires the provided callbacks as soon as possible after the current JS |
- * execution context. setTimeout(…, 0) always takes at least 5ms for legacy |
- * reasons. |
+ * execution context. setTimeout(…, 0) takes at least 4ms when called from |
+ * within another setTimeout(…, 0) for legacy reasons. |
* |
* This will not schedule the callback as a microtask (i.e. a task that can |
* preempt user input or networking callbacks). It is meant to emulate what |
@@ -52,19 +55,41 @@ goog.async.throwException = function(exception) { |
* @param {function(this:SCOPE)} callback Callback function to fire as soon as |
* possible. |
* @param {SCOPE=} opt_context Object in whose scope to call the listener. |
+ * @param {boolean=} opt_useSetImmediate Avoid the IE workaround that |
+ * ensures correctness at the cost of speed. See comments for details. |
* @template SCOPE |
*/ |
-goog.async.nextTick = function(callback, opt_context) { |
+goog.async.nextTick = function(callback, opt_context, opt_useSetImmediate) { |
var cb = callback; |
if (opt_context) { |
cb = goog.bind(callback, opt_context); |
} |
cb = goog.async.nextTick.wrapCallback_(cb); |
- // Introduced and currently only supported by IE10. |
- if (goog.isFunction(goog.global.setImmediate)) { |
+ // window.setImmediate was introduced and currently only supported by IE10+, |
+ // but due to a bug in the implementation it is not guaranteed that |
+ // setImmediate is faster than setTimeout nor that setImmediate N is before |
+ // setImmediate N+1. That is why we do not use the native version if |
+ // available. We do, however, call setImmediate if it is a normal function |
+ // because that indicates that it has been replaced by goog.testing.MockClock |
+ // which we do want to support. |
+ // See |
+ // http://connect.microsoft.com/IE/feedback/details/801823/setimmediate-and-messagechannel-are-broken-in-ie10 |
+ // |
+ // Note we do allow callers to also request setImmediate if they are willing |
+ // to accept the possible tradeoffs of incorrectness in exchange for speed. |
+ // The IE fallback of readystate change is much slower. |
+ if (goog.isFunction(goog.global.setImmediate) && |
+ // Opt in. |
+ (opt_useSetImmediate || |
+ // or it isn't a browser or the environment is weird |
+ !goog.global.Window || !goog.global.Window.prototype || |
+ // or something redefined setImmediate in which case we (YOLO) decide |
+ // to use it (This is so that we use the mockClock setImmediate. sigh). |
+ goog.global.Window.prototype.setImmediate != goog.global.setImmediate)) { |
goog.global.setImmediate(cb); |
return; |
} |
+ |
// Look for and cache the custom fallback version of setImmediate. |
if (!goog.async.nextTick.setImmediate_) { |
goog.async.nextTick.setImmediate_ = |
@@ -97,11 +122,14 @@ goog.async.nextTick.getSetImmediateEmulator_ = function() { |
// document.addEventListener. The latter excludes IE8 because it has a |
// synchronous postMessage implementation. |
if (typeof Channel === 'undefined' && typeof window !== 'undefined' && |
- window.postMessage && window.addEventListener) { |
+ window.postMessage && window.addEventListener && |
+ // Presto (The old pre-blink Opera engine) has problems with iframes |
+ // and contentWindow. |
+ !goog.labs.userAgent.engine.isPresto()) { |
/** @constructor */ |
Channel = function() { |
// Make an empty, invisible iframe. |
- var iframe = document.createElement('iframe'); |
+ var iframe = document.createElement(goog.dom.TagName.IFRAME); |
iframe.style.display = 'none'; |
iframe.src = ''; |
document.documentElement.appendChild(iframe); |
@@ -121,8 +149,10 @@ goog.async.nextTick.getSetImmediateEmulator_ = function() { |
'*' : win.location.protocol + '//' + win.location.host; |
var onmessage = goog.bind(function(e) { |
// Validate origin and message to make sure that this message was |
- // intended for us. |
- if (e.origin != origin && e.data != message) { |
+ // intended for us. If the origin is set to '*' (see above) only the |
+ // message needs to match since, for example, '*' != 'file://'. Allowing |
+ // the wildcard is ok, as we are not concerned with security here. |
+ if ((origin != '*' && e.origin != origin) || e.data != message) { |
return; |
} |
this['port1'].onmessage(); |
@@ -136,16 +166,24 @@ goog.async.nextTick.getSetImmediateEmulator_ = function() { |
}; |
}; |
} |
- if (typeof Channel !== 'undefined') { |
+ if (typeof Channel !== 'undefined' && |
+ (!goog.labs.userAgent.browser.isIE())) { |
+ // Exclude all of IE due to |
+ // http://codeforhire.com/2013/09/21/setimmediate-and-messagechannel-broken-on-internet-explorer-10/ |
+ // which allows starving postMessage with a busy setTimeout loop. |
+ // This currently affects IE10 and IE11 which would otherwise be able |
+ // to use the postMessage based fallbacks. |
var channel = new Channel(); |
// Use a fifo linked list to call callbacks in the right order. |
var head = {}; |
var tail = head; |
channel['port1'].onmessage = function() { |
- head = head.next; |
- var cb = head.cb; |
- head.cb = null; |
- cb(); |
+ if (goog.isDef(head.next)) { |
+ head = head.next; |
+ var cb = head.cb; |
+ head.cb = null; |
+ cb(); |
+ } |
}; |
return function(cb) { |
tail.next = { |
@@ -155,12 +193,12 @@ goog.async.nextTick.getSetImmediateEmulator_ = function() { |
channel['port2'].postMessage(0); |
}; |
} |
- // Implementation for IE6-8: Script elements fire an asynchronous |
+ // Implementation for IE6+: Script elements fire an asynchronous |
// onreadystatechange event when inserted into the DOM. |
if (typeof document !== 'undefined' && 'onreadystatechange' in |
- document.createElement('script')) { |
+ document.createElement(goog.dom.TagName.SCRIPT)) { |
return function(cb) { |
- var script = document.createElement('script'); |
+ var script = document.createElement(goog.dom.TagName.SCRIPT); |
script.onreadystatechange = function() { |
// Clean up and call the callback. |
script.onreadystatechange = null; |