summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/dom/observable/tentative/observable-constructor.window.js
blob: d2b597c819054f2b4c186d24f1e908bf5d46d2a2 (plain)
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");