tor-browser

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

fetch.https.window.js (10725B)


      1 // META: script=/common/get-host-info.sub.js
      2 // META: script=/service-workers/service-worker/resources/test-helpers.sub.js
      3 // META: script=resources/utils.js
      4 
      5 'use strict';
      6 
      7 // Covers basic functionality provided by BackgroundFetchManager.fetch().
      8 // https://wicg.github.io/background-fetch/#background-fetch-manager-fetch
      9 
     10 const wait = milliseconds =>
     11  new Promise(resolve => step_timeout(resolve, milliseconds));
     12 
     13 promise_test(async test => {
     14  // 6.3.1.9.2: If |registration|’s active worker is null, then reject promise
     15  //            with a TypeError and abort these steps.
     16  const script = 'service_workers/sw.js';
     17  const scope = 'service_workers/' + location.pathname;
     18 
     19  const serviceWorkerRegistration =
     20    await service_worker_unregister_and_register(test, script, scope);
     21 
     22  assert_equals(
     23    serviceWorkerRegistration.active, null,
     24    'There must not be an activated worker');
     25 
     26  await promise_rejects_js(
     27    test, TypeError,
     28    serviceWorkerRegistration.backgroundFetch.fetch(
     29      uniqueId(), ['resources/feature-name.txt']),
     30      'fetch() must reject on pending and installing workers');
     31 
     32 }, 'Background Fetch requires an activated Service Worker');
     33 
     34 backgroundFetchTest(async (test, backgroundFetch) => {
     35  // 6.3.1.6: If |requests| is empty, then return a promise rejected with a
     36  //          TypeError.
     37  await promise_rejects_js(
     38    test, TypeError, backgroundFetch.fetch(uniqueId(), []),
     39    'Empty sequences are treated as NULL');
     40 
     41  // 6.3.1.7.1: Let |internalRequest| be the request of the result of invoking
     42  //            the Request constructor with |request|. If this throws an
     43  //            exception, return a promise rejected with the exception.
     44  await promise_rejects_js(
     45    test, TypeError,
     46    backgroundFetch.fetch(uniqueId(), 'https://user:pass@domain/secret.txt'),
     47    'Exceptions thrown in the Request constructor are rethrown');
     48 
     49  // 6.3.1.7.2: If |internalRequest|’s mode is "no-cors", then return a
     50  //            promise rejected with a TypeError.
     51  {
     52    const request =
     53      new Request('resources/feature-name.txt', {mode: 'no-cors'});
     54 
     55    await promise_rejects_js(
     56      test, TypeError, backgroundFetch.fetch(uniqueId(), request),
     57      'Requests must not be in no-cors mode');
     58  }
     59 
     60 }, 'Argument verification is done for BackgroundFetchManager.fetch()');
     61 
     62 backgroundFetchTest(async (test, backgroundFetch) => {
     63  // 6.3.1.9.2: If |bgFetchMap[id]| exists, reject |promise| with a TypeError
     64  //            and abort these steps.
     65  return promise_rejects_js(test, TypeError, Promise.all([
     66    backgroundFetch.fetch('my-id', 'resources/feature-name.txt?1'),
     67    backgroundFetch.fetch('my-id', 'resources/feature-name.txt?2')
     68  ]));
     69 
     70 }, 'IDs must be unique among active Background Fetch registrations');
     71 
     72 backgroundFetchTest(async (test, backgroundFetch) => {
     73  const registrationId = uniqueId();
     74  const registration =
     75    await backgroundFetch.fetch(registrationId, '');
     76 
     77  assert_equals(registration.id, registrationId);
     78 
     79  const {type, eventRegistration, results} = await getMessageFromServiceWorker();
     80 
     81  assert_equals('backgroundfetchsuccess', type);
     82  assert_equals(eventRegistration.result, 'success');
     83  assert_equals(eventRegistration.failureReason, '');
     84 
     85 }, 'Empty URL is OK.');
     86 
     87 backgroundFetchTest(async (test, backgroundFetch) => {
     88  const registrationId = uniqueId();
     89  const registration =
     90    await backgroundFetch.fetch(registrationId,
     91        new Request('https://example/com', {
     92        method: 'PUT',
     93      }));
     94 
     95  assert_equals(registration.id, registrationId);
     96 
     97  const {type, eventRegistration, results} = await getMessageFromServiceWorker();
     98 
     99  assert_equals(type, 'backgroundfetchsuccess');
    100  assert_equals(eventRegistration.result, 'success');
    101  assert_equals(eventRegistration.failureReason, '');
    102 }, 'Requests with PUT method require CORS Preflight and succeed.');
    103 
    104 backgroundFetchTest(async (test, backgroundFetch) => {
    105  const registrationId = uniqueId();
    106  const registration =
    107    await backgroundFetch.fetch(registrationId,
    108        new Request('https://example/com', {
    109        method: 'POST',
    110        headers: {'Content-Type': 'text/json'}
    111      }));
    112 
    113  assert_equals(registration.id, registrationId);
    114 
    115  const {type, eventRegistration, results} = await getMessageFromServiceWorker();
    116 
    117  assert_equals(type, 'backgroundfetchsuccess');
    118  assert_equals(eventRegistration.result, 'success');
    119  assert_equals(eventRegistration.failureReason, '');
    120 }, 'Requests with text/json content type require CORS Preflight and succeed.');
    121 
    122 backgroundFetchTest(async (test, backgroundFetch) => {
    123  const registrationId = uniqueId();
    124  const registration =
    125    await backgroundFetch.fetch(registrationId, 'resources/feature-name.txt');
    126 
    127  assert_equals(registration.id, registrationId);
    128  assert_equals(registration.uploadTotal, 0);
    129  assert_equals(registration.uploaded, 0);
    130  assert_equals(registration.downloadTotal, 0);
    131  assert_equals(registration.result, '');
    132  assert_equals(registration.failureReason, '');
    133  assert_true(registration.recordsAvailable);
    134  // Skip `downloaded`, as the transfer may have started already.
    135 
    136  const {type, eventRegistration, results} = await getMessageFromServiceWorker();
    137  assert_equals('backgroundfetchsuccess', type);
    138  assert_equals(results.length, 1);
    139 
    140  assert_equals(eventRegistration.id, registration.id);
    141  assert_equals(eventRegistration.result, 'success');
    142  assert_equals(eventRegistration.failureReason, '');
    143 
    144  assert_true(results[0].url.includes('resources/feature-name.txt'));
    145  assert_equals(results[0].status, 200);
    146  assert_equals(results[0].text, 'Background Fetch');
    147 
    148 }, 'Using Background Fetch to successfully fetch a single resource');
    149 
    150 backgroundFetchTest(async (test, backgroundFetch) => {
    151  const registrationId = uniqueId();
    152  const registration =
    153    await backgroundFetch.fetch(registrationId, 'resources/feature-name.txt');
    154 
    155  assert_equals(registration.result, '');
    156  assert_equals(registration.failureReason, '');
    157 
    158  const {type, eventRegistration, results} =
    159    await getMessageFromServiceWorker();
    160  assert_equals('backgroundfetchsuccess', type);
    161 
    162  assert_equals(eventRegistration.id, registration.id);
    163  assert_equals(registration.result, 'success');
    164  assert_equals(registration.failureReason, '');
    165 
    166 }, 'Registration object gets updated values when a background fetch completes.');
    167 
    168 backgroundFetchTest(async (test, backgroundFetch) => {
    169  const registrationId = uniqueId();
    170 
    171  // Very large download total that will definitely exceed the quota.
    172  const options = {downloadTotal: Number.MAX_SAFE_INTEGER};
    173  await promise_rejects_quotaexceedederror(
    174    test, backgroundFetch.fetch(registrationId, 'resources/feature-name.txt', options),
    175    null, null
    176  );
    177 
    178 }, 'Background Fetch that exceeds the quota throws a QuotaExceededError');
    179 
    180 backgroundFetchTest(async (test, backgroundFetch) => {
    181  const registration = await backgroundFetch.fetch(
    182    'my-id', ['resources/feature-name.txt', 'resources/feature-name.txt']);
    183 
    184  const {type, eventRegistration, results} = await getMessageFromServiceWorker();
    185  assert_equals('backgroundfetchsuccess', type);
    186  assert_equals(results.length, 2);
    187 
    188  assert_equals(eventRegistration.id, registration.id);
    189  assert_equals(eventRegistration.result, 'success');
    190  assert_equals(eventRegistration.failureReason, '');
    191 
    192  for (const result of results) {
    193    assert_true(result.url.includes('resources/feature-name.txt'));
    194    assert_equals(result.status, 200);
    195    assert_equals(result.text, 'Background Fetch');
    196  }
    197 
    198 }, 'Fetches can have requests with duplicate URLs');
    199 
    200 backgroundFetchTest(async (test, backgroundFetch) => {
    201  const registrationId = uniqueId();
    202  const registration =
    203    await backgroundFetch.fetch(registrationId, 'resources/feature-name.txt');
    204  assert_true(registration.recordsAvailable);
    205 
    206  const {type, eventRegistration, results} = await getMessageFromServiceWorker();
    207  assert_equals('backgroundfetchsuccess', type);
    208  assert_equals(results.length, 1);
    209 
    210  // Wait for up to 5 seconds for the |eventRegistration|'s recordsAvailable
    211  // flag to be set to false, which happens after the successful invocation
    212  // of the ServiceWorker event has finished.
    213  for (let i = 0; i < 50; ++i) {
    214    if (!registration.recordsAvailable)
    215      break;
    216    await wait(100);
    217  }
    218 
    219  assert_false(registration.recordsAvailable);
    220 }, 'recordsAvailable is false after onbackgroundfetchsuccess finishes execution.');
    221 
    222 backgroundFetchTest(async (test, backgroundFetch) => {
    223  const registrationId = uniqueId();
    224  const registration =
    225    await backgroundFetch.fetch(registrationId, 'resources/missing-cat.txt');
    226 
    227  assert_equals(registration.id, registrationId);
    228  assert_equals(registration.result, '');
    229  assert_equals(registration.failureReason, '');
    230 
    231  const {type, eventRegistration, results} = await getMessageFromServiceWorker();
    232  assert_equals(type, 'backgroundfetchfail');
    233  assert_equals(results.length, 1);
    234  assert_true(results[0].url.includes('resources/missing-cat.txt'));
    235  assert_equals(results[0].status, 404);
    236  assert_equals(results[0].text, '');
    237 
    238  assert_equals(eventRegistration.id, registration.id);
    239  assert_equals(eventRegistration.result, 'failure');
    240  assert_equals(eventRegistration.failureReason, 'bad-status');
    241 
    242  assert_equals(registration.result, 'failure');
    243  assert_equals(registration.failureReason, 'bad-status');
    244 
    245 }, 'Using Background Fetch to fetch a non-existent resource should fail.');
    246 
    247 backgroundFetchTest(async (test, backgroundFetch) => {
    248  const registration = await backgroundFetch.fetch(
    249      'my-id',
    250      [location.origin, location.origin.replace('https', 'http')]);
    251 
    252  const {type, eventRegistration, results} = await getMessageFromServiceWorker();
    253 
    254  assert_equals('backgroundfetchfail', type);
    255  assert_equals(eventRegistration.failureReason, 'fetch-error');
    256 
    257  assert_equals(results.length, 2);
    258 
    259  const validResponse = results[0] ? results[0] : results[1];
    260  const nullResponse = !results[0] ? results[0] : results[1];
    261 
    262  assert_true(validResponse.url.includes(location.origin));
    263  assert_equals(nullResponse, null);
    264 
    265 }, 'Fetches with mixed content should fail.');
    266 
    267 backgroundFetchTest(async (test, backgroundFetch) => {
    268  const filePath = '/background-fetch/resources/feature-name.txt';
    269  const registration = await backgroundFetch.fetch(
    270    uniqueId(),
    271    `https://${get_host_info().REMOTE_HOST}${filePath}`);
    272 
    273  const {type, eventRegistration, results} = await getMessageFromServiceWorker();
    274  assert_equals(type, 'backgroundfetchfail');
    275  assert_equals(results.length, 1);
    276 
    277  assert_equals(results[0], null);
    278  assert_equals(eventRegistration.id, registration.id);
    279  assert_equals(eventRegistration.downloaded, 0);
    280 }, 'Responses failing CORS checks are not leaked');