tor-browser

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

query.https.any.js (8808B)


      1 // META: title=Web Locks API: navigator.locks.query method
      2 // META: script=resources/helpers.js
      3 
      4 'use strict';
      5 
      6 // Returns an array of the modes for the locks with matching name.
      7 function modes(list, name) {
      8  return list.filter(item => item.name === name).map(item => item.mode);
      9 }
     10 // Returns an array of the clientIds for the locks with matching name.
     11 function clients(list, name) {
     12  return list.filter(item => item.name === name).map(item => item.clientId);
     13 }
     14 
     15 promise_test(async t => {
     16  const res = uniqueName(t);
     17 
     18  await navigator.locks.request(res, async lock1 => {
     19    // Attempt to request this again - should be blocked.
     20    let lock2_acquired = false;
     21    navigator.locks.request(res, lock2 => { lock2_acquired = true; });
     22 
     23    // Verify that it was blocked.
     24    await navigator.locks.request(res, {ifAvailable: true}, async lock3 => {
     25      assert_false(lock2_acquired, 'second request should be blocked');
     26      assert_equals(lock3, null, 'third request should have failed');
     27 
     28      const state = await navigator.locks.query();
     29 
     30      assert_own_property(state, 'pending', 'State has `pending` property');
     31      assert_true(Array.isArray(state.pending),
     32                  'State `pending` property is an array');
     33      const pending_info = state.pending[0];
     34      assert_own_property(pending_info, 'name',
     35                          'Pending info dictionary has `name` property');
     36      assert_own_property(pending_info, 'mode',
     37                          'Pending info dictionary has `mode` property');
     38      assert_own_property(pending_info, 'clientId',
     39                          'Pending info dictionary has `clientId` property');
     40 
     41      assert_own_property(state, 'held', 'State has `held` property');
     42      assert_true(Array.isArray(state.held),
     43                  'State `held` property is an array');
     44      const held_info = state.held[0];
     45      assert_own_property(held_info, 'name',
     46                          'Held info dictionary has `name` property');
     47      assert_own_property(held_info, 'mode',
     48                          'Held info dictionary has `mode` property');
     49      assert_own_property(held_info, 'clientId',
     50                          'Held info dictionary has `clientId` property');
     51    });
     52  });
     53 }, 'query() returns dictionaries with expected properties');
     54 
     55 
     56 
     57 promise_test(async t => {
     58  const res = uniqueName(t);
     59 
     60  await navigator.locks.request(res, async lock1 => {
     61    const state = await navigator.locks.query();
     62    assert_array_equals(modes(state.held, res), ['exclusive'],
     63                        'Held lock should appear once');
     64  });
     65 
     66  await navigator.locks.request(res, {mode: 'shared'}, async lock1 => {
     67    const state = await navigator.locks.query();
     68    assert_array_equals(modes(state.held, res), ['shared'],
     69                        'Held lock should appear once');
     70  });
     71 }, 'query() reports individual held locks');
     72 
     73 promise_test(async t => {
     74  const res1 = uniqueName(t);
     75  const res2 = uniqueName(t);
     76 
     77  await navigator.locks.request(res1, async lock1 => {
     78    await navigator.locks.request(res2, {mode: 'shared'}, async lock2 => {
     79      const state = await navigator.locks.query();
     80      assert_array_equals(modes(state.held, res1), ['exclusive'],
     81                          'Held lock should appear once');
     82      assert_array_equals(modes(state.held, res2), ['shared'],
     83                          'Held lock should appear once');
     84    });
     85  });
     86 }, 'query() reports multiple held locks');
     87 
     88 promise_test(async t => {
     89  const res = uniqueName(t);
     90 
     91  await navigator.locks.request(res, async lock1 => {
     92    // Attempt to request this again - should be blocked.
     93    let lock2_acquired = false;
     94    navigator.locks.request(res, lock2 => { lock2_acquired = true; });
     95 
     96    // Verify that it was blocked.
     97    await navigator.locks.request(res, {ifAvailable: true}, async lock3 => {
     98      assert_false(lock2_acquired, 'second request should be blocked');
     99      assert_equals(lock3, null, 'third request should have failed');
    100 
    101      const state = await navigator.locks.query();
    102      assert_array_equals(modes(state.pending, res), ['exclusive'],
    103                          'Pending lock should appear once');
    104      assert_array_equals(modes(state.held, res), ['exclusive'],
    105                          'Held lock should appear once');
    106    });
    107  });
    108 }, 'query() reports pending and held locks');
    109 
    110 promise_test(async t => {
    111  const res = uniqueName(t);
    112 
    113  await navigator.locks.request(res, {mode: 'shared'}, async lock1 => {
    114    await navigator.locks.request(res, {mode: 'shared'}, async lock2 => {
    115      const state = await navigator.locks.query();
    116      assert_array_equals(modes(state.held, res), ['shared', 'shared'],
    117                          'Held lock should appear twice');
    118    });
    119  });
    120 }, 'query() reports held shared locks with appropriate count');
    121 
    122 promise_test(async t => {
    123  const res = uniqueName(t);
    124 
    125  await navigator.locks.request(res, async lock1 => {
    126    let lock2_acquired = false, lock3_acquired = false;
    127    navigator.locks.request(res, {mode: 'shared'},
    128                            lock2 => { lock2_acquired = true; });
    129    navigator.locks.request(res, {mode: 'shared'},
    130                            lock3 => { lock3_acquired = true; });
    131 
    132    await navigator.locks.request(res, {ifAvailable: true}, async lock4 => {
    133      assert_equals(lock4, null, 'lock should not be available');
    134      assert_false(lock2_acquired, 'second attempt should be blocked');
    135      assert_false(lock3_acquired, 'third attempt should be blocked');
    136 
    137      const state = await navigator.locks.query();
    138      assert_array_equals(modes(state.held, res), ['exclusive'],
    139                          'Held lock should appear once');
    140 
    141      assert_array_equals(modes(state.pending, res), ['shared', 'shared'],
    142                          'Pending lock should appear twice');
    143    });
    144  });
    145 }, 'query() reports pending shared locks with appropriate count');
    146 
    147 promise_test(async t => {
    148  const res1 = uniqueName(t);
    149  const res2 = uniqueName(t);
    150 
    151  await navigator.locks.request(res1, async lock1 => {
    152    await navigator.locks.request(res2, async lock2 => {
    153      const state = await navigator.locks.query();
    154 
    155      const res1_clients = clients(state.held, res1);
    156      const res2_clients = clients(state.held, res2);
    157 
    158      assert_equals(res1_clients.length, 1, 'Each lock should have one holder');
    159      assert_equals(res2_clients.length, 1, 'Each lock should have one holder');
    160 
    161      assert_array_equals(res1_clients, res2_clients,
    162                          'Both locks should have same clientId');
    163    });
    164  });
    165 }, 'query() reports the same clientId for held locks from the same context');
    166 
    167 promise_test(async t => {
    168  const res = uniqueName(t);
    169 
    170  const worker = new Worker('resources/worker.js');
    171  t.add_cleanup(() => { worker.terminate(); });
    172 
    173  await postToWorkerAndWait(
    174    worker, {op: 'request', name: res, mode: 'shared'});
    175 
    176  await navigator.locks.request(res, {mode: 'shared'}, async lock => {
    177    const state = await navigator.locks.query();
    178    const res_clients = clients(state.held, res);
    179    assert_equals(res_clients.length, 2, 'Clients should have same resource');
    180    assert_not_equals(res_clients[0], res_clients[1],
    181                      'Clients should have different ids');
    182  });
    183 }, 'query() reports different ids for held locks from different contexts');
    184 
    185 promise_test(async t => {
    186  const res1 = uniqueName(t);
    187  const res2 = uniqueName(t);
    188 
    189  const worker = new Worker('resources/worker.js');
    190  t.add_cleanup(() => { worker.terminate(); });
    191 
    192  // Acquire 1 in the worker.
    193  await postToWorkerAndWait(worker, {op: 'request', name: res1})
    194 
    195  // Acquire 2 here.
    196  await new Promise(resolve => {
    197    navigator.locks.request(res2, lock => {
    198      resolve();
    199      return new Promise(() => {}); // Never released.
    200    });
    201  });
    202 
    203  // Request 2 in the worker.
    204  postToWorkerAndWait(worker, {op: 'request', name: res2});
    205  assert_true((await postToWorkerAndWait(worker, {
    206    op: 'request', name: res2, ifAvailable: true
    207  })).failed, 'Lock request should have failed');
    208 
    209  // Request 1 here.
    210  navigator.locks.request(
    211    res1, t.unreached_func('Lock should not be acquired'));
    212 
    213  // Verify that we're seeing a deadlock.
    214  const state = await navigator.locks.query();
    215  const res1_held_clients = clients(state.held, res1);
    216  const res2_held_clients = clients(state.held, res2);
    217  const res1_pending_clients = clients(state.pending, res1);
    218  const res2_pending_clients = clients(state.pending, res2);
    219 
    220  assert_equals(res1_held_clients.length, 1);
    221  assert_equals(res2_held_clients.length, 1);
    222  assert_equals(res1_pending_clients.length, 1);
    223  assert_equals(res2_pending_clients.length, 1);
    224 
    225  assert_equals(res1_held_clients[0], res2_pending_clients[0]);
    226  assert_equals(res2_held_clients[0], res1_pending_clients[0]);
    227 }, 'query() can observe a deadlock');