Index: LayoutTests/http/tests/dom/resources/promise-rejection-events.js |
diff --git a/LayoutTests/http/tests/dom/resources/promise-rejection-events.js b/LayoutTests/http/tests/dom/resources/promise-rejection-events.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..8c1555588ae4e1d7c0c84647d32ecdd92e118744 |
--- /dev/null |
+++ b/LayoutTests/http/tests/dom/resources/promise-rejection-events.js |
@@ -0,0 +1,668 @@ |
+'use strict'; |
+ |
+if (self.importScripts) { |
+ if ('ServiceWorkerGlobalScope' in self && self instanceof ServiceWorkerGlobalScope) { |
+ importScripts('../../serviceworker/resources/worker-testharness.js'); |
+ } else { |
+ importScripts('../../resources/testharness.js'); |
+ } |
+} |
+ |
+// |
+// Straightforward unhandledrejection tests |
+// |
+async_test(function(t) { |
+ var e = new Error(); |
+ var p; |
+ |
+ onUnhandledSucceed(t, e, function() { return p; }); |
+ |
+ p = Promise.reject(e); |
+}, 'unhandledrejection: from Promise.reject'); |
+ |
+async_test(function(t) { |
+ var e = new Error(); |
+ var p; |
+ |
+ onUnhandledSucceed(t, e, function() { return p; }); |
+ |
+ p = new Promise(function(_, reject) { |
+ reject(e); |
+ }); |
+}, 'unhandledrejection: from a synchronous rejection in new Promise'); |
+ |
+async_test(function(t) { |
+ var e = new Error(); |
+ var p; |
+ |
+ onUnhandledSucceed(t, e, function() { return p; }); |
+ |
+ p = new Promise(function(_, reject) { |
+ postMessageTask(function() { |
+ reject(e); |
+ }); |
+ }); |
+}, 'unhandledrejection: from a task-delayed rejection'); |
+ |
+async_test(function(t) { |
+ var e = new Error(); |
+ var p; |
+ |
+ onUnhandledSucceed(t, e, function() { return p; }); |
+ |
+ p = new Promise(function(_, reject) { |
+ setTimeout(function() { |
+ reject(e); |
+ }, 1); |
+ }); |
+}, 'unhandledrejection: from a setTimeout-delayed rejection'); |
+ |
+async_test(function(t) { |
+ var e = new Error(); |
+ var e2 = new Error(); |
+ var promise2; |
+ |
+ onUnhandledSucceed(t, e2, function() { return promise2; }); |
+ |
+ var unreached = t.unreached_func('promise should not be fulfilled'); |
+ promise2 = Promise.reject(e).then(unreached, function(reason) { |
+ t.step(function() { |
+ assert_equals(reason, e); |
+ }); |
+ throw e2; |
+ }); |
+}, 'unhandledrejection: from a throw in a rejection handler chained off of Promise.reject'); |
+ |
+async_test(function(t) { |
+ var e = new Error(); |
+ var e2 = new Error(); |
+ var promise2; |
+ |
+ onUnhandledSucceed(t, e2, function() { return promise2; }); |
+ |
+ var unreached = t.unreached_func('promise should not be fulfilled'); |
+ promise2 = new Promise(function(_, reject) { |
+ setTimeout(function() { |
+ reject(e); |
+ }, 1); |
+ }).then(unreached, function(reason) { |
+ t.step(function() { |
+ assert_equals(reason, e); |
+ }); |
+ throw e2; |
+ }); |
+}, 'unhandledrejection: from a throw in a rejection handler chained off of a setTimeout-delayed rejection'); |
+ |
+async_test(function(t) { |
+ var e = new Error(); |
+ var e2 = new Error(); |
+ var promise2; |
+ |
+ onUnhandledSucceed(t, e2, function() { return promise2; }); |
+ |
+ var promise = new Promise(function(_, reject) { |
+ setTimeout(function() { |
+ reject(e); |
+ mutationObserverMicrotask(function() { |
+ var unreached = t.unreached_func('promise should not be fulfilled'); |
+ promise2 = promise.then(unreached, function(reason) { |
+ t.step(function() { |
+ assert_equals(reason, e); |
+ }); |
+ throw e2; |
+ }); |
+ }); |
+ }, 1); |
+ }); |
+}, 'unhandledrejection: from a throw in a rejection handler attached one microtask after a setTimeout-delayed rejection'); |
+ |
+async_test(function(t) { |
+ var e = new Error(); |
+ var p; |
+ |
+ onUnhandledSucceed(t, e, function() { return p; }); |
+ |
+ p = Promise.resolve().then(function() { |
+ return Promise.reject(e); |
+ }); |
+}, 'unhandledrejection: from returning a Promise.reject-created rejection in a fulfillment handler'); |
+ |
+async_test(function(t) { |
+ var e = new Error(); |
+ var p; |
+ |
+ onUnhandledSucceed(t, e, function() { return p; }); |
+ |
+ p = Promise.resolve().then(function() { |
+ throw e; |
+ }); |
+}, 'unhandledrejection: from a throw in a fulfillment handler'); |
+ |
+async_test(function(t) { |
+ var e = new Error(); |
+ var p; |
+ |
+ onUnhandledSucceed(t, e, function() { return p; }); |
+ |
+ p = Promise.resolve().then(function() { |
+ return new Promise(function(_, reject) { |
+ setTimeout(function() { |
+ reject(e); |
+ }, 1); |
+ }); |
+ }); |
+}, 'unhandledrejection: from returning a setTimeout-delayed rejection in a fulfillment handler'); |
+ |
+async_test(function(t) { |
+ var e = new Error(); |
+ var p; |
+ |
+ onUnhandledSucceed(t, e, function() { return p; }); |
+ |
+ p = Promise.all([Promise.reject(e)]); |
+}, 'unhandledrejection: from Promise.reject, indirected through Promise.all'); |
+ |
+// |
+// Negative unhandledrejection/rejectionhandled tests with immediate attachment |
+// |
+ |
+async_test(function(t) { |
+ var e = new Error(); |
+ var p; |
+ |
+ onUnhandledFail(t, function() { return p; }); |
+ |
+ var unreached = t.unreached_func('promise should not be fulfilled'); |
+ p = Promise.reject(e).then(unreached, function() {}); |
+}, 'no unhandledrejection/rejectionhandled: rejection handler attached synchronously to a promise from Promise.reject'); |
+ |
+async_test(function(t) { |
+ var e = new Error(); |
+ var p; |
+ |
+ onUnhandledFail(t, function() { return p; }); |
+ |
+ var unreached = t.unreached_func('promise should not be fulfilled'); |
+ p = Promise.all([Promise.reject(e)]).then(unreached, function() {}); |
+}, 'no unhandledrejection/rejectionhandled: rejection handler attached synchronously to a promise from ' + |
+ 'Promise.reject, indirecting through Promise.all'); |
+ |
+async_test(function(t) { |
+ var e = new Error(); |
+ var p; |
+ |
+ onUnhandledFail(t, function() { return p; }); |
+ |
+ var unreached = t.unreached_func('promise should not be fulfilled'); |
+ p = new Promise(function(_, reject) { |
+ reject(e); |
+ }).then(unreached, function() {}); |
+}, 'no unhandledrejection/rejectionhandled: rejection handler attached synchronously to a synchronously-rejected ' + |
+ 'promise created with new Promise'); |
+ |
+async_test(function(t) { |
+ var e = new Error(); |
+ var p; |
+ |
+ onUnhandledFail(t, function() { return p; }); |
+ |
+ var unreached = t.unreached_func('promise should not be fulfilled'); |
+ p = Promise.resolve().then(function() { |
+ throw e; |
+ }).then(unreached, function(reason) { |
+ t.step(function() { |
+ assert_equals(reason, e); |
+ }); |
+ }); |
+}, 'no unhandledrejection/rejectionhandled: rejection handler attached synchronously to a promise created from ' + |
+ 'throwing in a fulfillment handler'); |
+ |
+async_test(function(t) { |
+ var e = new Error(); |
+ var p; |
+ |
+ onUnhandledFail(t, function() { return p; }); |
+ |
+ var unreached = t.unreached_func('promise should not be fulfilled'); |
+ p = Promise.resolve().then(function() { |
+ return Promise.reject(e); |
+ }).then(unreached, function(reason) { |
+ t.step(function() { |
+ assert_equals(reason, e); |
+ }); |
+ }); |
+}, 'no unhandledrejection/rejectionhandled: rejection handler attached synchronously to a promise created from ' + |
+ 'returning a Promise.reject-created promise in a fulfillment handler'); |
+ |
+async_test(function(t) { |
+ var e = new Error(); |
+ var p; |
+ |
+ onUnhandledFail(t, function() { return p; }); |
+ |
+ var unreached = t.unreached_func('promise should not be fulfilled'); |
+ p = Promise.resolve().then(function() { |
+ return new Promise(function(_, reject) { |
+ setTimeout(function() { |
+ reject(e); |
+ }, 1); |
+ }); |
+ }).then(unreached, function(reason) { |
+ t.step(function() { |
+ assert_equals(reason, e); |
+ }); |
+ }); |
+}, 'no unhandledrejection/rejectionhandled: rejection handler attached synchronously to a promise created from ' + |
+ 'returning a setTimeout-delayed rejection in a fulfillment handler'); |
+ |
+async_test(function(t) { |
+ var e = new Error(); |
+ var p; |
+ |
+ onUnhandledFail(t, function() { return p; }); |
+ |
+ postMessageTask(function() { |
+ p = Promise.resolve().then(function() { |
+ return Promise.reject(e); |
+ }) |
+ .catch(function() {}); |
+ }); |
+}, 'no unhandledrejection/rejectionhandled: all inside a queued task, a rejection handler attached synchronously to ' + |
+ 'a promise created from returning a Promise.reject-created promise in a fulfillment handler'); |
+ |
+// |
+// Negative unhandledrejection/rejectionhandled tests with delayed attachment |
+// |
+ |
+async_test(function(t) { |
+ var e = new Error(); |
+ var p; |
+ |
+ onUnhandledFail(t, function() { return p; }); |
+ |
+ p = Promise.reject(e); |
+ mutationObserverMicrotask(function() { |
+ var unreached = t.unreached_func('promise should not be fulfilled'); |
+ p.then(unreached, function() {}); |
+ }); |
+}, 'delayed handling: a microtask delay before attaching a handler prevents both events (Promise.reject-created ' + |
+ 'promise)'); |
+ |
+async_test(function(t) { |
+ var e = new Error(); |
+ var p; |
+ |
+ onUnhandledFail(t, function() { return p; }); |
+ |
+ p = new Promise(function(_, reject) { |
+ reject(e); |
+ }); |
+ mutationObserverMicrotask(function() { |
+ var unreached = t.unreached_func('promise should not be fulfilled'); |
+ p.then(unreached, function() {}); |
+ }); |
+}, 'delayed handling: a microtask delay before attaching a handler prevents both events (immediately-rejected new ' + |
+ 'Promise-created promise)'); |
+ |
+async_test(function(t) { |
+ var e = new Error(); |
+ var p1; |
+ var p2; |
+ |
+ onUnhandledFail(t, function() { return p1; }); |
+ onUnhandledFail(t, function() { return p2; }); |
+ |
+ p1 = new Promise(function(_, reject) { |
+ mutationObserverMicrotask(function() { |
+ reject(e); |
+ }); |
+ }); |
+ p2 = Promise.all([p1]); |
+ mutationObserverMicrotask(function() { |
+ var unreached = t.unreached_func('promise should not be fulfilled'); |
+ p2.then(unreached, function() {}); |
+ }); |
+}, 'delayed handling: a microtask delay before attaching the handler, and before rejecting the promise, indirected ' + |
+ 'through Promise.all'); |
+ |
+// |
+// Negative unhandledrejection/rejectionhandled tests with nested-microtask-delayed attachment |
+// |
+ |
+async_test(function(t) { |
+ var e = new Error(); |
+ var p; |
+ |
+ onUnhandledFail(t, function() { return p; }); |
+ |
+ p = Promise.reject(e); |
+ mutationObserverMicrotask(function() { |
+ Promise.resolve().then(function() { |
+ mutationObserverMicrotask(function() { |
+ Promise.resolve().then(function() { |
+ p.catch(function() {}); |
+ }); |
+ }); |
+ }); |
+ }); |
+}, 'microtask nesting: attaching a handler inside a combination of mutationObserverMicrotask + promise microtasks'); |
+ |
+async_test(function(t) { |
+ var e = new Error(); |
+ var p; |
+ |
+ onUnhandledFail(t, function() { return p; }); |
+ |
+ postMessageTask(function() { |
+ p = Promise.reject(e); |
+ mutationObserverMicrotask(function() { |
+ Promise.resolve().then(function() { |
+ mutationObserverMicrotask(function() { |
+ Promise.resolve().then(function() { |
+ p.catch(function() {}); |
+ }); |
+ }); |
+ }); |
+ }); |
+ }); |
+}, 'microtask nesting: attaching a handler inside a combination of mutationObserverMicrotask + promise microtasks, ' + |
+ 'all inside a postMessageTask'); |
+ |
+async_test(function(t) { |
+ var e = new Error(); |
+ var p; |
+ |
+ onUnhandledFail(t, function() { return p; }); |
+ |
+ setTimeout(function() { |
+ p = Promise.reject(e); |
+ mutationObserverMicrotask(function() { |
+ Promise.resolve().then(function() { |
+ mutationObserverMicrotask(function() { |
+ Promise.resolve().then(function() { |
+ p.catch(function() {}); |
+ }); |
+ }); |
+ }); |
+ }); |
+ }, 0); |
+}, 'microtask nesting: attaching a handler inside a combination of mutationObserverMicrotask + promise microtasks, ' + |
+ 'all inside a setTimeout'); |
+ |
+async_test(function(t) { |
+ var e = new Error(); |
+ var p; |
+ |
+ onUnhandledFail(t, function() { return p; }); |
+ |
+ p = Promise.reject(e); |
+ Promise.resolve().then(function() { |
+ mutationObserverMicrotask(function() { |
+ Promise.resolve().then(function() { |
+ mutationObserverMicrotask(function() { |
+ p.catch(function() {}); |
+ }); |
+ }); |
+ }); |
+ }); |
+}, 'microtask nesting: attaching a handler inside a combination of promise microtasks + mutationObserverMicrotask'); |
+ |
+async_test(function(t) { |
+ var e = new Error(); |
+ var p; |
+ |
+ onUnhandledFail(t, function() { return p; }); |
+ |
+ postMessageTask(function() { |
+ p = Promise.reject(e); |
+ Promise.resolve().then(function() { |
+ mutationObserverMicrotask(function() { |
+ Promise.resolve().then(function() { |
+ mutationObserverMicrotask(function() { |
+ p.catch(function() {}); |
+ }); |
+ }); |
+ }); |
+ }); |
+ }); |
+}, 'microtask nesting: attaching a handler inside a combination of promise microtasks + mutationObserverMicrotask, ' + |
+ 'all inside a postMessageTask'); |
+ |
+async_test(function(t) { |
+ var e = new Error(); |
+ var p; |
+ |
+ onUnhandledFail(t, function() { return p; }); |
+ |
+ setTimeout(function() { |
+ p = Promise.reject(e); |
+ Promise.resolve().then(function() { |
+ mutationObserverMicrotask(function() { |
+ Promise.resolve().then(function() { |
+ mutationObserverMicrotask(function() { |
+ p.catch(function() {}); |
+ }); |
+ }); |
+ }); |
+ }); |
+ }, 0); |
+}, 'microtask nesting: attaching a handler inside a combination of promise microtasks + mutationObserverMicrotask, ' + |
+ 'all inside a setTimeout'); |
+ |
+// |
+// Positive unhandledrejection/rejectionhandled tests with delayed attachment |
+// |
+ |
+async_test(function(t) { |
+ var e = new Error(); |
+ var p; |
+ |
+ onUnhandledSucceed(t, e, function() { return p; }); |
+ |
+ var _reject; |
+ p = new Promise(function(_, reject) { |
+ _reject = reject; |
+ }); |
+ _reject(e); |
+ postMessageTask(function() { |
+ var unreached = t.unreached_func('promise should not be fulfilled'); |
+ p.then(unreached, function() {}); |
+ }); |
+}, 'delayed handling: a task delay before attaching a handler does not prevent unhandledrejection'); |
+ |
+async_test(function(t) { |
+ var unhandledPromises = []; |
+ var unhandledReasons = []; |
+ var e = new Error(); |
+ var p; |
+ |
+ var unhandled = function(ev) { |
+ if (ev.promise === p) { |
+ t.step(function() { |
+ unhandledPromises.push(ev.promise); |
+ unhandledReasons.push(ev.reason); |
+ }); |
+ } |
+ }; |
+ var handled = function(ev) { |
+ if (ev.promise === p) { |
+ t.step(function() { |
+ assert_array_equals(unhandledPromises, [p]); |
+ assert_array_equals(unhandledReasons, [e]); |
+ assert_equals(ev.promise, p); |
+ assert_equals(ev.reason, e); |
+ }); |
+ } |
+ }; |
+ addEventListener('unhandledrejection', unhandled); |
+ addEventListener('rejectionhandled', handled); |
+ ensureCleanup(t, unhandled, handled); |
+ |
+ p = new Promise(function() { |
+ throw e; |
+ }); |
+ setTimeout(function() { |
+ var unreached = t.unreached_func('promise should not be fulfilled'); |
+ p.then(unreached, function(reason) { |
+ assert_equals(reason, e); |
+ setTimeout(function() { t.done(); }, 10); |
+ }); |
+ }, 10); |
+}, 'delayed handling: delaying handling by setTimeout(,10) will cause both events to fire'); |
+ |
+async_test(function(t) { |
+ var e = new Error(); |
+ var p; |
+ |
+ onUnhandledSucceed(t, e, function() { return p; }); |
+ |
+ p = Promise.reject(e); |
+ postMessageTask(function() { |
+ Promise.resolve().then(function() { |
+ p.catch(function() {}); |
+ }); |
+ }); |
+}, 'delayed handling: postMessageTask after promise creation/rejection, plus promise microtasks, is too late to ' + |
+ 'attach a rejection handler'); |
+ |
+async_test(function(t) { |
+ var e = new Error(); |
+ var p; |
+ |
+ onUnhandledSucceed(t, e, function() { return p; }); |
+ postMessageTask(function() { |
+ Promise.resolve().then(function() { |
+ Promise.resolve().then(function() { |
+ Promise.resolve().then(function() { |
+ Promise.resolve().then(function() { |
+ p.catch(function() {}); |
+ }); |
+ }); |
+ }); |
+ }); |
+ }); |
+ p = Promise.reject(e); |
+}, 'delayed handling: postMessageTask before promise creation/rejection, plus many promise microtasks, is too late ' + |
+ 'to attach a rejection handler'); |
+ |
+async_test(function(t) { |
+ var e = new Error(); |
+ var p; |
+ |
+ onUnhandledSucceed(t, e, function() { return p; }); |
+ |
+ p = Promise.reject(e); |
+ postMessageTask(function() { |
+ Promise.resolve().then(function() { |
+ Promise.resolve().then(function() { |
+ Promise.resolve().then(function() { |
+ Promise.resolve().then(function() { |
+ p.catch(function() {}); |
+ }); |
+ }); |
+ }); |
+ }); |
+ }); |
+}, 'delayed handling: postMessageTask after promise creation/rejection, plus many promise microtasks, is too late ' + |
+ 'to attach a rejection handler'); |
+ |
+// |
+// Miscellaneous tests about integration with the rest of the platform |
+// |
+ |
+async_test(function(t) { |
+ var e = new Error(); |
+ var l = function(ev) { |
+ var order = []; |
+ mutationObserverMicrotask(function() { |
+ order.push(1); |
+ }); |
+ setTimeout(function() { |
+ order.push(2); |
+ t.step(function() { |
+ assert_array_equals(order, [1, 2]); |
+ }); |
+ t.done(); |
+ }, 1); |
+ }; |
+ addEventListener('unhandledrejection', l); |
+ ensureCleanup(t, l); |
+ Promise.reject(e); |
+}, 'mutationObserverMicrotask vs. postMessageTask ordering is not disturbed inside unhandledrejection events'); |
+ |
+// |
+// HELPERS |
+// |
+ |
+function postMessageTask(f) { |
+ if ('document' in self) { |
+ var l = function() { |
+ removeEventListener('message', l); |
+ f(); |
+ }; |
+ addEventListener('message', l); |
+ postMessage('abusingpostmessageforfunandprofit', '*'); |
+ } else { |
+ var channel = new MessageChannel(); |
+ channel.port1.onmessage = function() { channel.port1.close(); f(); }; |
+ channel.port2.postMessage('abusingpostmessageforfunandprofit'); |
+ channel.port2.close(); |
+ } |
+} |
+ |
+function mutationObserverMicrotask(f) { |
+ if ('document' in self) { |
+ var observer = new MutationObserver(function() { f(); }); |
+ var node = document.createTextNode(''); |
+ observer.observe(node, { characterData: true }); |
+ node.data = 'foo'; |
+ } else { |
+ // We don't have mutation observers on workers, so just post a promise-based |
+ // microtask. |
+ Promise.resolve().then(function() { f(); }); |
+ } |
+} |
+ |
+function onUnhandledSucceed(t, expectedReason, expectedPromiseGetter) { |
+ var l = function(ev) { |
+ if (ev.promise === expectedPromiseGetter()) { |
+ t.step(function() { |
+ assert_equals(ev.reason, expectedReason); |
+ assert_equals(ev.promise, expectedPromiseGetter()); |
+ }); |
+ t.done(); |
+ } |
+ }; |
+ addEventListener('unhandledrejection', l); |
+ ensureCleanup(t, l); |
+} |
+ |
+function onUnhandledFail(t, expectedPromiseGetter) { |
+ var unhandled = function(evt) { |
+ if (evt.promise === expectedPromiseGetter()) { |
+ t.unreached_func('unhandledrejection event is not supposed to be triggered'); |
+ } |
+ }; |
+ var handled = function(evt) { |
+ if (evt.promise === expectedPromiseGetter()) { |
+ t.unreached_func('rejectionhandled event is not supposed to be triggered'); |
+ } |
+ }; |
+ addEventListener('unhandledrejection', unhandled); |
+ addEventListener('rejectionhandled', handled); |
+ ensureCleanup(t, unhandled, handled); |
+ setTimeout(function() { |
+ t.done(); |
+ }, 10); |
+} |
+ |
+function ensureCleanup(t, unhandled, handled) { |
+ t.add_cleanup(function() { |
+ if (unhandled) |
+ removeEventListener('unhandledrejection', unhandled); |
+ if (handled) |
+ removeEventListener('rejectionhandled', handled); |
+ }); |
+} |
+ |
+done(); |