tor-browser

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

event_leak_utils.js (3082B)


      1 // Any copyright is dedicated to the Public Domain.
      2 // http://creativecommons.org/publicdomain/zero/1.0/
      3 "use strict";
      4 
      5 // This function runs a number of tests where:
      6 //
      7 //  1. An iframe is created
      8 //  2. The target callback is executed with the iframe's contentWindow as
      9 //     an argument.
     10 //  3. The iframe is destroyed and GC is forced.
     11 //  4. Verifies that the iframe's contentWindow has been GC'd.
     12 //
     13 // Different ways of destroying the iframe are checked.  Simple
     14 // remove(), destruction via bfcache, or replacement by document.open().
     15 //
     16 // Please pass a target callback that exercises the API under
     17 // test using the given window.  The callback should try to leave the
     18 // API active to increase the liklihood of provoking an API.  Any activity
     19 // should be canceled by the destruction of the window.
     20 async function checkForEventListenerLeaks(name, target) {
     21  // Test if we leak in the case where we do nothing special to
     22  // the frame before removing it from the DOM.
     23  await _eventListenerLeakStep(target, `${name} default`);
     24 
     25  // Test the case where we navigate the frame before removing it
     26  // from the DOM so that the window using the target API ends up
     27  // in bfcache.
     28  await _eventListenerLeakStep(target, `${name} bfcache`, frame => {
     29    frame.src = "about:blank";
     30    return new Promise(resolve => (frame.onload = resolve));
     31  });
     32 
     33  // Test the case where we document.open() the frame before removing
     34  // it from the DOM so that the window using the target API ends
     35  // up getting replaced.
     36  await _eventListenerLeakStep(target, `${name} document.open()`, frame => {
     37    frame.contentDocument.open();
     38    frame.contentDocument.close();
     39  });
     40 }
     41 
     42 // ----------------
     43 // Internal helpers
     44 // ----------------
     45 
     46 // Utility function to create a loaded iframe.
     47 async function _withFrame(doc, url) {
     48  let frame = doc.createElement("iframe");
     49  frame.src = url;
     50  doc.body.appendChild(frame);
     51  await new Promise(resolve => (frame.onload = resolve));
     52  return frame;
     53 }
     54 
     55 // This function defines the basic form of the test cases.  We create an
     56 // iframe, execute the target callback to manipulate the DOM, remove the frame
     57 // from the DOM, and then check to see if the frame was GC'd.  The caller
     58 // may optionally pass in a callback that will be executed with the
     59 // frame as an argument before removing it from the DOM.
     60 async function _eventListenerLeakStep(target, name, extra) {
     61  let frame = await _withFrame(document, "empty.html");
     62 
     63  await target(frame.contentWindow);
     64 
     65  let weakRef = SpecialPowers.Cu.getWeakReference(frame.contentWindow);
     66  ok(weakRef.get(), `should be able to create a weak reference - ${name}`);
     67 
     68  if (extra) {
     69    await extra(frame);
     70  }
     71 
     72  frame.remove();
     73  frame = null;
     74 
     75  // Perform many GC's to avoid intermittent delayed collection.
     76  await new Promise(resolve => SpecialPowers.exactGC(resolve));
     77  await new Promise(resolve => SpecialPowers.exactGC(resolve));
     78  await new Promise(resolve => SpecialPowers.exactGC(resolve));
     79 
     80  ok(
     81    !weakRef.get(),
     82    `iframe content window should be garbage collected - ${name}`
     83  );
     84 }