tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

observable-constructor.window.js (4674B)


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