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.on('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");
|