1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
|
async function loadIframeAndReturnContentWindow() {
// Create and attach an iframe.
const iframe = document.createElement('iframe');
const iframeLoadPromise = new Promise((resolve, reject) => {
iframe.onload = resolve;
iframe.onerror = reject;
});
document.body.append(iframe);
await iframeLoadPromise;
return iframe.contentWindow;
}
promise_test(async t => {
// Hang this off of the main document's global, so the child can easily reach
// it.
window.results = [];
const contentWin = await loadIframeAndReturnContentWindow();
contentWin.eval(`
// Get a reference to the parent result array before we detach and lose
// access to the parent.
const parentResults = parent.results;
const source = new Observable((subscriber) => {
parentResults.push("subscribe");
// Detach the iframe and push a value to the subscriber/Observer.
window.frameElement.remove();
parentResults.push("detached");
subscriber.next("next");
subscriber.complete();
subscriber.error("error");
});
source.subscribe({
next: v => {
// Should never run.
parentResults.push(v);
},
complete: () => {
// Should never run.
parentResults.push("complete");
},
erorr: e => {
// Should never run.
parentResults.push(e);
}
});
`);
assert_array_equals(results, ["subscribe", "detached"]);
}, "No observer handlers can be invoked in detached document");
promise_test(async t => {
const contentWin = await loadIframeAndReturnContentWindow();
// Set a global error handler on the iframe document's window, and verify that
// it is never called (because the thing triggering the error happens when the
// document is detached, and "reporting the exception" relies on an attached
// document).
contentWin.addEventListener("error",
t.unreached_func("Error should not be called"), { once: true });
contentWin.eval(`
const source = new Observable((subscriber) => {
// Detach the iframe and push an error, which would normally "report the
// exception", since this subscriber did not specify an error handler.
window.frameElement.remove();
subscriber.error("this is an error that should not be reported");
});
source.subscribe();
`);
}, "Subscriber.error() does not \"report the exception\" even when an " +
"`error()` handler is not present, when it is invoked in a detached document");
promise_test(async t => {
// Make this available off the global so the child can reach it.
window.results = [];
const contentWin = await loadIframeAndReturnContentWindow();
// Set a global error handler on the iframe document's window, and verify that
// it is never called (because the thing triggering the error happens when the
// document is detached, and "reporting the exception" relies on an attached
// document).
contentWin.addEventListener("error",
t.unreached_func("Error should not be called"), { once: true });
contentWin.eval(`
const parentResults = parent.results;
const source = new Observable((subscriber) => {
// This should never run.
parentResults.push('subscribe');
});
// Detach the iframe and try to subscribe.
window.frameElement.remove();
parentResults.push('detached');
source.subscribe();
`);
assert_array_equals(results, ["detached"], "Subscribe callback is never invoked");
}, "Cannot subscribe to an Observable in a detached document");
promise_test(async t => {
// Make this available off the global so the child can reach it.
window.results = [];
const contentWin = await loadIframeAndReturnContentWindow();
contentWin.eval(`
const parentResults = parent.results;
const event_target = new EventTarget();
// Set up two event listeners, both of which will mutate |parentResults|:
// 1. A traditional event listener
// 2. An observable
event_target.addEventListener('customevent', e => parentResults.push(e));
const source = event_target.when('customevent');
source.subscribe(e => parentResults.push(e));
// Detach the iframe and fire an event at the event target. The parent will
// confirm that the observable's next handler did not get invoked, because
// the window is detached.
const event = new Event('customevent');
window.frameElement.remove();
parentResults.push('detached');
event_target.dispatchEvent(event);
`);
assert_array_equals(results, ["detached"], "Subscribe callback is never invoked");
}, "Observable from EventTarget does not get notified for events in detached documents");
|