diff --git a/lib/utils/nexttick-browser.js b/lib/utils/nexttick-browser.js index eaecedc8f..73a0ae0a9 100644 --- a/lib/utils/nexttick-browser.js +++ b/lib/utils/nexttick-browser.js @@ -1,9 +1,255 @@ /*! - * nexttick.js - setimmediate for bcoin - * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). + * nexttick.js - nexttick for bcoin + * Copyright (c) 2017, Christopher Jeffrey (MIT License). * https://github.com/bcoin-org/bcoin + * + * Parts of this software are based on setimmediate. + * + * Copyright (c) 2012 Barnesandnoble.com, llc, Donavon West, and Domenic Denicola + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the 'Software'), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. */ 'use strict'; -module.exports = require('../../vendor/setimmediate'); +var global = (function() { + if (this) + return this; + + if (typeof window !== 'undefined') + return window; + + if (typeof self !== 'undefined') + return self; + + if (typeof global !== 'undefined') + return global; + + throw new Error('No global defined.'); +})(); + +var document = global.document; +var nextHandle = 1; +var taskMap = {}; +var running = false; +var nextTick; + +/* + * Task Runner + */ + +function addTask(handler) { + if (typeof handler !== 'function') + throw new Error('callback must be a function.'); + + taskMap[nextHandle] = handler; + + return nextHandle++; +} + +function runTask(handle) { + var task; + + if (running) { + setTimeout(function() { + runTask(handle); + }, 1); + return; + } + + task = taskMap[handle]; + + if (task) { + running = true; + try { + task(); + } finally { + delete taskMap[handle]; + running = false; + } + } +} + +/* + * Set Immediate Implementation + */ + +function hasSetImmediate() { + return typeof global.setImmediate === 'function'; +} + +function installSetImmediate() { + return function nextTick(handler) { + setImmediate(handler); + }; +} + +/* + * Next Tick Implementation + */ + +function hasNextTick() { + // Don't get fooled by browserify. + return ({}).toString.call(global.process) === '[object process]'; +} + +function installNextTick() { + return process.nextTick; +} + +/* + * Post Message Implementation + */ + +function hasPostMessage() { + var isAsync = false; + var onMessage; + + // Be sure to exclude web workers. + if (global.postMessage && !global.importScripts) { + isAsync = true; + onMessage = global.onmessage; + global.onmessage = function() { + isAsync = false; + }; + global.postMessage('', '*'); + global.onmessage = onMessage; + } + + return isAsync; +} + +function installPostMessage() { + var prefix = 'nextTick' + Math.random(); + var onMessage; + + onMessage = function(event) { + if (event.source === global + && typeof event.data === 'string' + && event.data.indexOf(prefix) === 0) { + runTask(+event.data.slice(prefix.length)); + } + }; + + if (global.addEventListener) + global.addEventListener('message', onMessage, false); + else + global.attachEvent('onmessage', onMessage); + + return function nextTick(handler) { + var handle = addTask(handler); + global.postMessage(prefix + handle, '*'); + }; +} + +/* + * Message Channel Implementation + */ + +function hasMessageChannel() { + return typeof global.MessageChannel === 'function'; +} + +function installMessageChannel() { + var channel = new MessageChannel(); + + channel.port1.onmessage = function(event) { + runTask(event.data); + }; + + return function nextTick(handler) { + var handle = addTask(handler); + channel.port2.postMessage(handle); + }; +} + +/* + * Ready State Change Implementation + */ + +function hasReadyState() { + return document && ('onreadystatechange' in document.createElement('script')); +} + +function installReadyState() { + var html = document.documentElement; + + return function nextTick(handler) { + var handle = addTask(handler); + var script = document.createElement('script'); + + script.onreadystatechange = function() { + runTask(handle); + script.onreadystatechange = null; + html.removeChild(script); + script = null; + }; + + html.appendChild(script); + }; +} + +/* + * Set Timeout Implementation + */ + +function hasSetTimeout() { + return typeof global.setTimeout === 'function'; +} + +function installSetTimeout() { + return function nextTick(handler) { + var handle = addTask(handler); + setTimeout(function() { + runTask(handle); + }, 1); + }; +} + +/* + * Install + */ + +if (hasSetImmediate()) { + // `setImmediate` is already available. + nextTick = installSetImmediate(); +} else if (hasNextTick()) { + // For Node.js before 0.9. + nextTick = installNextTick(); +} else if (hasPostMessage()) { + // For non-IE10 modern browsers. + nextTick = installPostMessage(); +} else if (hasMessageChannel()) { + // For web workers, where supported. + nextTick = installMessageChannel(); +} else if (hasReadyState()) { + // For IE 6–8. + nextTick = installReadyState(); +} else if (hasSetTimeout()) { + // For older browsers. + nextTick = installSetTimeout(); +} else { + throw new Error('nextTick not supported.'); +} + +/* + * Expose + */ + +module.exports = nextTick; diff --git a/lib/utils/nexttick.js b/lib/utils/nexttick.js index 0533c002c..c45d4b921 100644 --- a/lib/utils/nexttick.js +++ b/lib/utils/nexttick.js @@ -6,6 +6,6 @@ 'use strict'; -module.exports = typeof setImmediate !== 'function' - ? process.nextTick - : setImmediate; +module.exports = function nextTick(handler) { + setImmediate(handler); +}; diff --git a/vendor/setimmediate.js b/vendor/setimmediate.js deleted file mode 100644 index ddd87f56c..000000000 --- a/vendor/setimmediate.js +++ /dev/null @@ -1,208 +0,0 @@ -/*! - * Copyright (c) 2012 Barnesandnoble.com, llc, Donavon West, and Domenic Denicola - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -(function(root, undefined) { - "use strict"; - - var global; - - if (typeof window !== "undefined") - global = window; - else if (typeof self !== "undefined") - global = self; - else - global = root; - - if (!global) - throw new Error("Global not found."); - - function install() { - if (global.setImmediate) { - return global.setImmediate; - } - - var nextHandle = 1; // Spec says greater than zero - var tasksByHandle = {}; - var currentlyRunningATask = false; - var doc = global.document; - var setImmediate; - - function addFromSetImmediateArguments(args) { - tasksByHandle[nextHandle] = partiallyApplied.apply(undefined, args); - return nextHandle++; - } - - // This function accepts the same arguments as setImmediate, but - // returns a function that requires no arguments. - function partiallyApplied(handler) { - var args = [].slice.call(arguments, 1); - return function() { - if (typeof handler === "function") { - handler.apply(undefined, args); - } else { - (new Function("" + handler))(); - } - }; - } - - function runIfPresent(handle) { - // From the spec: "Wait until any invocations of this algorithm started before this one have completed." - // So if we're currently running a task, we'll need to delay this invocation. - if (currentlyRunningATask) { - // Delay by doing a setTimeout. setImmediate was tried instead, but in Firefox 7 it generated a - // "too much recursion" error. - setTimeout(partiallyApplied(runIfPresent, handle), 0); - } else { - var task = tasksByHandle[handle]; - if (task) { - currentlyRunningATask = true; - try { - task(); - } finally { - clearImmediate(handle); - currentlyRunningATask = false; - } - } - } - } - - function clearImmediate(handle) { - delete tasksByHandle[handle]; - } - - function installNextTickImplementation() { - setImmediate = function() { - var handle = addFromSetImmediateArguments(arguments); - process.nextTick(partiallyApplied(runIfPresent, handle)); - return handle; - }; - } - - function canUsePostMessage() { - // The test against `importScripts` prevents this implementation from being installed inside a web worker, - // where `global.postMessage` means something completely different and can't be used for this purpose. - if (global.postMessage && !global.importScripts) { - var postMessageIsAsynchronous = true; - var oldOnMessage = global.onmessage; - global.onmessage = function() { - postMessageIsAsynchronous = false; - }; - global.postMessage("", "*"); - global.onmessage = oldOnMessage; - return postMessageIsAsynchronous; - } - } - - function installPostMessageImplementation() { - // Installs an event handler on `global` for the `message` event: see - // * https://developer.mozilla.org/en/DOM/window.postMessage - // * http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html#crossDocumentMessages - - var messagePrefix = "setImmediate$" + Math.random() + "$"; - var onGlobalMessage = function(event) { - if (event.source === global && - typeof event.data === "string" && - event.data.indexOf(messagePrefix) === 0) { - runIfPresent(+event.data.slice(messagePrefix.length)); - } - }; - - if (global.addEventListener) { - global.addEventListener("message", onGlobalMessage, false); - } else { - global.attachEvent("onmessage", onGlobalMessage); - } - - setImmediate = function() { - var handle = addFromSetImmediateArguments(arguments); - global.postMessage(messagePrefix + handle, "*"); - return handle; - }; - } - - function installMessageChannelImplementation() { - var channel = new MessageChannel(); - channel.port1.onmessage = function(event) { - var handle = event.data; - runIfPresent(handle); - }; - - setImmediate = function() { - var handle = addFromSetImmediateArguments(arguments); - channel.port2.postMessage(handle); - return handle; - }; - } - - function installReadyStateChangeImplementation() { - var html = doc.documentElement; - setImmediate = function() { - var handle = addFromSetImmediateArguments(arguments); - // Create a