tor-browser

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

test_weakmaps.html (9069B)


      1 <!DOCTYPE HTML>
      2 <html>
      3 <!--
      4 https://bugzilla.mozilla.org/show_bug.cgi?id=668855
      5 -->
      6 <head>
      7  <meta charset="utf-8">
      8  <title>Test Cross-Compartment DOM WeakMaps</title>
      9  <script src="/tests/SimpleTest/SimpleTest.js"></script>
     10  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
     11 <script type="application/javascript">
     12  /** Test for Bug 668855 **/
     13 
     14  SimpleTest.waitForExplicitFinish();
     15 
     16 // We wait to run this until the load event because it needs to access an element.
     17 function go() {
     18 
     19  /* Create a weak reference, with a single-element weak map. */
     20  let make_weak_ref = function (obj) {
     21    let m = new WeakMap;
     22    m.set(obj, {});
     23    return m;
     24  };
     25 
     26  /* Check to see if a weak reference is dead. */
     27  let weak_ref_dead = function (r) {
     28    return SpecialPowers.nondeterministicGetWeakMapKeys(r).length == 0;
     29  }
     30 
     31  /* Deterministically grab an arbitrary DOM element. */
     32  let get_live_dom = function () {
     33    let elems = document.getElementsByTagName("a");
     34    return elems[0];
     35  };
     36 
     37 
     38  /* Test case from bug 653248, adapted into a standard test.
     39 
     40  This is a dead cycle involving a DOM edge, so the cycle collector can free it.  Keys and
     41  values reachable only from XPConnect must be marked gray for this to work, and the cycle collector
     42  must know the proper structure of the heap.
     43 
     44  */
     45  let make_gray_loop_through = function (key) {
     46    let map = new WeakMap;
     47    let div = document.createElement("div");
     48    let obj = {m:map, k:key};
     49    div.addEventListener("foo", function() {
     50      // Entrain |obj| by referencing it from a closure attached to the
     51      // element. The code below doesn't matter (it won't run). Just pull a
     52      // reference to obj.
     53      obj.k = 1;
     54      obj.m = "bar";
     55    });
     56    map.set(key, div);
     57    return make_weak_ref(map);
     58  };
     59 
     60  let make_gray_loop_through_shape = function () {
     61    let key = Symbol();
     62    let map = new WeakMap;
     63    let div = document.createElement("div");
     64    let obj = {m:map};
     65    obj[key] = 1;
     66    div.addEventListener("foo", function() {
     67      // Entrain |obj| by referencing it from a closure attached to the
     68      // element. The code below doesn't matter (it won't run). Just pull a
     69      // reference to obj.
     70      obj.k = 1;
     71      obj.m = "bar";
     72    });
     73    map.set(key, div);
     74    return make_weak_ref(map);
     75  };
     76 
     77  let weakref_through_object = make_gray_loop_through({});
     78  let weakref_through_symbol = make_gray_loop_through(Symbol());
     79  let weakref_through_shape = make_gray_loop_through_shape();
     80 
     81  /* Combinations of live and dead gray maps/keys. */
     82  let basic_weak_ref = null;
     83  let basic_map_weak_ref = null;
     84  let black_map = new WeakMap;
     85  let black_key = {};
     86 
     87  let basic_unit_tests = function () {
     88    let live_dom = get_live_dom();
     89    let dead_dom = document.createElement("div");
     90    let live_map = new WeakMap;
     91    let dead_map = new WeakMap;
     92    let live_key = {};
     93    let dead_key = {};
     94 
     95    // put the live/dead maps/keys into the appropriate DOM elements
     96    live_dom.basic_unit_tests = {m:live_map, k:live_key};
     97 
     98    let obj = {m:dead_map, k:dead_key};
     99    // dead_dom.hook = {m:dead_map, k:dead_key};
    100    dead_dom.addEventListener("foo", function() {
    101      // The code below doesn't matter (it won't run). Just pull a
    102      // reference to obj.
    103      obj.m = 1;
    104      obj.k = "2";
    105    });
    106 
    107    // Create a dead value, and a weak ref to it.
    108    // The loop keeps dead_dom alive unless the CC is smart enough to kill it.
    109    let dead_val = {loop:dead_dom};
    110    basic_weak_ref = make_weak_ref(dead_val);
    111    basic_map_weak_ref = make_weak_ref(dead_map);
    112 
    113    // set up the actual entries.  most will die.
    114    live_map.set(live_key, {my_key:'live_live'});
    115    live_map.set(dead_key, dead_val);
    116    live_map.set(black_key, {my_key:'live_black'});
    117 
    118    dead_map.set(live_key, dead_val);
    119    dead_map.set(dead_key, dead_val);
    120    dead_map.set(black_key, dead_val);
    121 
    122    black_map.set(live_key, {my_key:'black_live'});
    123    black_map.set(dead_key, dead_val);
    124    black_map.set(black_key, {my_key:'black_black'});
    125 
    126  };
    127 
    128  basic_unit_tests();
    129 
    130 
    131  let check_basic_unit = function () {
    132    let live_dom = get_live_dom();
    133    let live_map = live_dom.basic_unit_tests.m;
    134    let live_key = live_dom.basic_unit_tests.k;
    135 
    136    // check the dead elements
    137    ok(weak_ref_dead(basic_weak_ref), "Dead value was kept alive.");
    138    ok(weak_ref_dead(basic_map_weak_ref), "Dead map was kept alive.");
    139 
    140    // check the live gray map
    141    is(live_map.get(live_key).my_key, 'live_live',
    142      "Live key should have the same value in live map.");
    143    is(live_map.get(black_key).my_key, 'live_black',
    144      "Black key should have the same value in live map.");
    145    is(SpecialPowers.nondeterministicGetWeakMapKeys(live_map).length, 2,
    146      "Live map should have two entries.");
    147 
    148    // check the live black map
    149    is(black_map.get(live_key).my_key, 'black_live',
    150      "Live key should have the same value in black map.");
    151    is(black_map.get(black_key).my_key, 'black_black',
    152      "Black key should have the same value in black map.");
    153    is(SpecialPowers.nondeterministicGetWeakMapKeys(black_map).length, 2,
    154      "Black map should have two entries.");
    155 
    156  };
    157 
    158 
    159  /* live gray chained weak map entries, involving the cycle collector. */
    160  let chainm = new WeakMap;
    161  let num_chains = 5;
    162 
    163  let nested_cc_maps = function () {
    164    let dom = get_live_dom();
    165    for(let i = 0; i < num_chains; i++) {
    166      let k = {count:i};
    167      dom.key = k;
    168      dom0 = document.createElement("div");
    169      chainm.set(k, {d:dom0});
    170      dom = document.createElement("div");
    171      dom0.appendChild(dom);
    172    };
    173  };
    174 
    175  let check_nested_cc_maps = function () {
    176    let dom = get_live_dom();
    177    let all_ok = true;
    178    for(let i = 0; i < num_chains; i++) {
    179      let k = dom.key;
    180      all_ok = all_ok && k.count == i;
    181      dom = chainm.get(k).d.firstChild;
    182    };
    183    ok(all_ok, "Count was invalid on a key in chained weak map entries.");
    184  };
    185 
    186  nested_cc_maps();
    187 
    188 
    189  /* black weak map, chained garbage cycle involving DOM */
    190  let garbage_map = new WeakMap;
    191 
    192  let chained_garbage_maps = function () {
    193    let dom0 = document.createElement("div");
    194    let dom = dom0;
    195    for(let i = 0; i < num_chains; i++) {
    196      let k = {};
    197      dom.key = k;
    198      let new_dom = document.createElement("div");
    199      garbage_map.set(k, {val_child:new_dom});
    200      dom = document.createElement("div");
    201      new_dom.appendChild(dom);
    202    };
    203    // tie the knot
    204    dom.appendChild(dom0);
    205  };
    206 
    207  chained_garbage_maps();
    208 
    209 
    210  /* black weak map, chained garbage cycle involving DOM, XPCWN keys */
    211  let wn_garbage_map = new WeakMap;
    212 
    213  let wn_chained_garbage_maps = function () {
    214    let dom0 = document.createElement("div");
    215    let dom = dom0;
    216    for(let i = 0; i < num_chains; i++) {
    217      let new_dom = document.createElement("div");
    218      wn_garbage_map.set(dom, {wn_val_child:new_dom});
    219      dom = document.createElement("div");
    220      new_dom.appendChild(dom);
    221    };
    222    // tie the knot
    223    dom.appendChild(dom0);
    224  };
    225 
    226  wn_chained_garbage_maps();
    227 
    228 
    229  /* The cycle collector shouldn't remove a live wrapped native key. */
    230 
    231  let wn_live_map = new WeakMap;
    232 
    233  let make_live_map = function () {
    234    let live = get_live_dom();
    235    wn_live_map.set(live, {});
    236    ok(wn_live_map.has(get_live_dom()), "Live map should have live DOM node before GC.");
    237  }
    238 
    239  make_live_map();
    240 
    241  // We're out of ideas for unpreservable natives, now that just about
    242  // everything is on webidl, so just don't test those.
    243 
    244  SpecialPowers.forceGC();
    245 
    246  // GC on its own can't collect these cycles.
    247  ok(!weak_ref_dead(weakref_through_object),
    248     "Garbage gray cycle through object should be alive.");
    249  ok(!weak_ref_dead(weakref_through_symbol),
    250  "Garbage gray cycle through symbol should be alive.");
    251  ok(!weak_ref_dead(weakref_through_shape),
    252     "Garbage gray cycle through shape should be alive.");
    253 
    254  SpecialPowers.forceCC();
    255  SpecialPowers.forceGC();
    256 
    257  ok(weak_ref_dead(weakref_through_object),
    258     "Garbage gray cycle through object should be collected.");
    259  ok(weak_ref_dead(weakref_through_symbol),
    260  "Garbage gray cycle through symbol should be collected.");
    261  ok(weak_ref_dead(weakref_through_shape),
    262     "Garbage gray cycle through shape should be collected.");
    263 
    264  check_nested_cc_maps();
    265 
    266  is(SpecialPowers.nondeterministicGetWeakMapKeys(garbage_map).length, 0,
    267     "Chained garbage weak map entries should not leak.");
    268 
    269  check_basic_unit();
    270 
    271  // fixed by Bug 680937
    272  is(SpecialPowers.nondeterministicGetWeakMapKeys(wn_garbage_map).length, 0,
    273     "Chained garbage WN weak map entries should not leak.");
    274 
    275  // fixed by Bug 680937
    276  is(SpecialPowers.nondeterministicGetWeakMapKeys(wn_live_map).length, 1,
    277     "Live weak map wrapped native key should not be removed.");
    278 
    279  ok(wn_live_map.has(get_live_dom()), "Live map should have live dom.");
    280 
    281  SimpleTest.finish();
    282 }
    283  </script>
    284 </head>
    285 <div></div>
    286 <div id="mydivname"></div>
    287 <body onload="go()";>
    288 <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=668855" target="_blank">Mozilla Bug 668855</a>
    289 <p id="display"></p>
    290 </body>
    291 </html>