tor-browser

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

test_Sync.js (13954B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 const { setTimeout } = ChromeUtils.importESModule(
      6  "resource://gre/modules/Timer.sys.mjs"
      7 );
      8 
      9 const {
     10  AnimationFramePromise,
     11  Deferred,
     12  EventPromise,
     13  PollPromise,
     14  TimedPromise,
     15 } = ChromeUtils.importESModule("chrome://remote/content/shared/Sync.sys.mjs");
     16 
     17 const { Log } = ChromeUtils.importESModule(
     18  "resource://gre/modules/Log.sys.mjs"
     19 );
     20 
     21 /**
     22 * Mimic a DOM node for listening for events.
     23 */
     24 class MockElement {
     25  constructor() {
     26    this.capture = false;
     27    this.eventName = null;
     28    this.func = null;
     29    this.mozSystemGroup = false;
     30    this.wantUntrusted = false;
     31    this.untrusted = false;
     32  }
     33 
     34  addEventListener(name, func, options = {}) {
     35    const { capture, mozSystemGroup, wantUntrusted } = options;
     36 
     37    this.eventName = name;
     38    this.func = func;
     39    this.capture = capture ?? false;
     40    this.mozSystemGroup = mozSystemGroup ?? false;
     41    this.wantUntrusted = wantUntrusted ?? false;
     42  }
     43 
     44  click() {
     45    if (this.func) {
     46      const event = {
     47        capture: this.capture,
     48        mozSystemGroup: this.mozSystemGroup,
     49        target: this,
     50        type: this.eventName,
     51        untrusted: this.untrusted,
     52        wantUntrusted: this.wantUntrusted,
     53      };
     54      this.func(event);
     55    }
     56  }
     57 
     58  dispatchEvent() {
     59    if (this.wantUntrusted) {
     60      this.untrusted = true;
     61    }
     62    this.click();
     63  }
     64 
     65  removeEventListener() {
     66    this.capture = false;
     67    this.eventName = null;
     68    this.func = null;
     69    this.mozSystemGroup = false;
     70    this.untrusted = false;
     71    this.wantUntrusted = false;
     72  }
     73 }
     74 
     75 class MockAppender extends Log.Appender {
     76  constructor(formatter) {
     77    super(formatter);
     78    this.messages = [];
     79  }
     80 
     81  append(message) {
     82    this.doAppend(message);
     83  }
     84 
     85  doAppend(message) {
     86    this.messages.push(message);
     87  }
     88 }
     89 
     90 add_task(async function test_AnimationFramePromise() {
     91  for (const options of [
     92    undefined, // default options
     93    {}, //empty options
     94    { timeout: null }, // disabled timeout
     95    { timeout: 1234 }, // custom timeout
     96  ]) {
     97    let called = false;
     98    let win = {
     99      addEventListener(_event, _listener) {},
    100      requestAnimationFrame(callback) {
    101        called = true;
    102        callback();
    103      },
    104    };
    105    await AnimationFramePromise(win, options);
    106    ok(called);
    107  }
    108 });
    109 
    110 const mockNoopWindow = {
    111  addEventListener(_event, _listener) {},
    112  requestAnimationFrame(_callback) {},
    113 };
    114 
    115 add_task(async function test_AnimationFramePromiseDefaultTimeout() {
    116  await AnimationFramePromise(mockNoopWindow);
    117 });
    118 
    119 add_task(async function test_AnimationFramePromiseCustomTimeout() {
    120  await AnimationFramePromise(mockNoopWindow, { timeout: 10 });
    121 });
    122 
    123 add_task(async function test_AnimationFramePromiseAbortOnPageHide() {
    124  let resolvePageHideEvent;
    125 
    126  const mockWindow = {
    127    addEventListener(event, listener) {
    128      if (event === "pagehide") {
    129        resolvePageHideEvent = listener;
    130      }
    131    },
    132    removeEventListener() {},
    133    requestAnimationFrame(callback) {
    134      // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
    135      setTimeout(() => callback(), 10000);
    136    },
    137  };
    138 
    139  const trackedPromise = trackPromise(AnimationFramePromise(mockWindow));
    140 
    141  ok(trackedPromise.isPending(), "AnimationFramePromise is pending");
    142 
    143  // Simulate "pagehide" event.
    144  resolvePageHideEvent({});
    145 
    146  await trackedPromise;
    147 });
    148 
    149 add_task(async function test_AnimationFramePromiseTimeoutErrors() {
    150  // not an number or null
    151  for (const val of ["foo", true, [], {}]) {
    152    Assert.throws(
    153      () => AnimationFramePromise(mockNoopWindow, { timeout: val }),
    154      /TypeError: timeout must be a number or null/
    155    );
    156  }
    157 
    158  // not a nonnegative integer
    159  for (const val of [-1, -100000, -1.2, 1.2]) {
    160    Assert.throws(
    161      () => AnimationFramePromise(mockNoopWindow, { timeout: val }),
    162      /RangeError: timeout must be a non-negative integer/
    163    );
    164  }
    165 });
    166 
    167 add_task(async function test_DeferredPending() {
    168  const deferred = Deferred();
    169  ok(deferred.pending);
    170 
    171  deferred.resolve();
    172  await deferred.promise;
    173  ok(!deferred.pending);
    174 });
    175 
    176 add_task(async function test_DeferredRejected() {
    177  const deferred = Deferred();
    178 
    179  // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
    180  setTimeout(() => deferred.reject(new Error("foo")), 100);
    181 
    182  try {
    183    await deferred.promise;
    184    ok(false);
    185  } catch (e) {
    186    ok(!deferred.pending);
    187 
    188    ok(!deferred.fulfilled);
    189    ok(deferred.rejected);
    190    equal(e.message, "foo");
    191  }
    192 });
    193 
    194 add_task(async function test_DeferredResolved() {
    195  const deferred = Deferred();
    196  ok(deferred.pending);
    197 
    198  // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
    199  setTimeout(() => deferred.resolve("foo"), 100);
    200 
    201  const result = await deferred.promise;
    202  ok(!deferred.pending);
    203 
    204  ok(deferred.fulfilled);
    205  ok(!deferred.rejected);
    206  equal(result, "foo");
    207 });
    208 
    209 add_task(async function test_EventPromise_subjectTypes() {
    210  for (const subject of ["foo", 42, null, undefined, true, [], {}]) {
    211    Assert.throws(() => new EventPromise(subject, "click"), /TypeError/);
    212  }
    213 });
    214 
    215 add_task(async function test_EventPromise_eventNameTypes() {
    216  const element = new MockElement();
    217 
    218  for (const eventName of [42, null, undefined, true, [], {}]) {
    219    Assert.throws(() => new EventPromise(element, eventName), /TypeError/);
    220  }
    221 });
    222 
    223 add_task(async function test_EventPromise_subjectAndEventNameEvent() {
    224  const element = new MockElement();
    225 
    226  const clicked = new EventPromise(element, "click");
    227  element.click();
    228  const event = await clicked;
    229 
    230  equal(element, event.target);
    231 });
    232 
    233 add_task(async function test_EventPromise_captureTypes() {
    234  const element = new MockElement();
    235 
    236  for (const capture of [null, "foo", 42, [], {}]) {
    237    Assert.throws(
    238      () => new EventPromise(element, "click", { capture }),
    239      /TypeError/
    240    );
    241  }
    242 });
    243 
    244 add_task(async function test_EventPromise_captureEvent() {
    245  const element = new MockElement();
    246 
    247  for (const capture of [undefined, false, true]) {
    248    const expectedCapture = capture ?? false;
    249 
    250    const clicked = new EventPromise(element, "click", { capture });
    251    element.click();
    252    const event = await clicked;
    253 
    254    equal(element, event.target);
    255    equal(expectedCapture, event.capture);
    256  }
    257 });
    258 
    259 add_task(async function test_EventPromise_checkFnTypes() {
    260  const element = new MockElement();
    261 
    262  for (const checkFn of ["foo", 42, true, [], {}]) {
    263    Assert.throws(
    264      () => new EventPromise(element, "click", { checkFn }),
    265      /TypeError/
    266    );
    267  }
    268 });
    269 
    270 add_task(async function test_EventPromise_checkFnCallback() {
    271  const element = new MockElement();
    272 
    273  let count;
    274  const data = [
    275    { checkFn: null, expected_count: 0 },
    276    { checkFn: undefined, expected_count: 0 },
    277    {
    278      checkFn: () => {
    279        throw new Error("foo");
    280      },
    281      expected_count: 0,
    282    },
    283    { checkFn: () => count++ > 0, expected_count: 2 },
    284  ];
    285 
    286  for (const { checkFn, expected_count } of data) {
    287    count = 0;
    288 
    289    const clicked = new EventPromise(element, "click", { checkFn });
    290    element.click();
    291    element.click();
    292    const event = await clicked;
    293 
    294    equal(element, event.target);
    295    equal(expected_count, count);
    296  }
    297 });
    298 
    299 add_task(async function test_EventPromise_mozSystemGroupTypes() {
    300  const element = new MockElement();
    301 
    302  for (const mozSystemGroup of [null, "foo", 42, [], {}]) {
    303    Assert.throws(
    304      () => new EventPromise(element, "click", { mozSystemGroup }),
    305      /TypeError/
    306    );
    307  }
    308 });
    309 
    310 add_task(async function test_EventPromise_mozSystemGroupEvent() {
    311  const element = new MockElement();
    312 
    313  for (const mozSystemGroup of [undefined, false, true]) {
    314    const expectedMozSystemGroup = mozSystemGroup ?? false;
    315 
    316    const clicked = new EventPromise(element, "click", { mozSystemGroup });
    317    element.click();
    318    const event = await clicked;
    319 
    320    equal(element, event.target);
    321    equal(expectedMozSystemGroup, event.mozSystemGroup);
    322  }
    323 });
    324 
    325 add_task(async function test_EventPromise_wantUntrustedTypes() {
    326  const element = new MockElement();
    327 
    328  for (let wantUntrusted of [null, "foo", 42, [], {}]) {
    329    Assert.throws(
    330      () => new EventPromise(element, "click", { wantUntrusted }),
    331      /TypeError/
    332    );
    333  }
    334 });
    335 
    336 add_task(async function test_EventPromise_wantUntrustedEvent() {
    337  for (const wantUntrusted of [undefined, false, true]) {
    338    let expected_untrusted = wantUntrusted ?? false;
    339 
    340    const element = new MockElement();
    341 
    342    const clicked = new EventPromise(element, "click", { wantUntrusted });
    343    element.dispatchEvent(new CustomEvent("click", {}));
    344    const event = await clicked;
    345 
    346    equal(element, event.target);
    347    equal(expected_untrusted, event.untrusted);
    348  }
    349 });
    350 
    351 add_task(function test_executeSoon_callback() {
    352  // executeSoon() is already defined for xpcshell in head.js. As such import
    353  // our implementation into a custom namespace.
    354  let sync = ChromeUtils.importESModule(
    355    "chrome://remote/content/shared/Sync.sys.mjs"
    356  );
    357 
    358  for (let func of ["foo", null, true, [], {}]) {
    359    Assert.throws(() => sync.executeSoon(func), /TypeError/);
    360  }
    361 
    362  let a;
    363  sync.executeSoon(() => {
    364    a = 1;
    365  });
    366  executeSoon(() => equal(1, a));
    367 });
    368 
    369 add_task(function test_PollPromise_funcTypes() {
    370  for (let type of ["foo", 42, null, undefined, true, [], {}]) {
    371    Assert.throws(() => new PollPromise(type), /TypeError/);
    372  }
    373  new PollPromise(() => {});
    374  new PollPromise(function () {});
    375 });
    376 
    377 add_task(function test_PollPromise_timeoutTypes() {
    378  for (let timeout of ["foo", true, [], {}]) {
    379    Assert.throws(() => new PollPromise(() => {}, { timeout }), /TypeError/);
    380  }
    381  for (let timeout of [1.2, -1]) {
    382    Assert.throws(() => new PollPromise(() => {}, { timeout }), /RangeError/);
    383  }
    384  for (let timeout of [null, undefined, 42]) {
    385    new PollPromise(resolve => resolve(1), { timeout });
    386  }
    387 });
    388 
    389 add_task(function test_PollPromise_intervalTypes() {
    390  for (let interval of ["foo", null, true, [], {}]) {
    391    Assert.throws(() => new PollPromise(() => {}, { interval }), /TypeError/);
    392  }
    393  for (let interval of [1.2, -1]) {
    394    Assert.throws(() => new PollPromise(() => {}, { interval }), /RangeError/);
    395  }
    396  new PollPromise(() => {}, { interval: 42 });
    397 });
    398 
    399 add_task(async function test_PollPromise_retvalTypes() {
    400  for (let typ of [true, false, "foo", 42, [], {}]) {
    401    strictEqual(typ, await new PollPromise(resolve => resolve(typ)));
    402  }
    403 });
    404 
    405 add_task(async function test_PollPromise_rethrowError() {
    406  let nevals = 0;
    407  let err;
    408  try {
    409    await PollPromise(() => {
    410      ++nevals;
    411      throw new Error();
    412    });
    413  } catch (e) {
    414    err = e;
    415  }
    416  equal(1, nevals);
    417  ok(err instanceof Error);
    418 });
    419 
    420 add_task(async function test_PollPromise_noTimeout() {
    421  let nevals = 0;
    422  await new PollPromise((resolve, reject) => {
    423    ++nevals;
    424    nevals < 100 ? reject() : resolve();
    425  });
    426  equal(100, nevals);
    427 });
    428 
    429 add_task(async function test_PollPromise_zeroTimeout() {
    430  // run at least once when timeout is 0
    431  let nevals = 0;
    432  let start = new Date().getTime();
    433  await new PollPromise(
    434    (resolve, reject) => {
    435      ++nevals;
    436      reject();
    437    },
    438    { timeout: 0 }
    439  );
    440  let end = new Date().getTime();
    441  equal(1, nevals);
    442  less(end - start, 500);
    443 });
    444 
    445 add_task(async function test_PollPromise_timeoutElapse() {
    446  let nevals = 0;
    447  let start = new Date().getTime();
    448  await new PollPromise(
    449    (resolve, reject) => {
    450      ++nevals;
    451      reject();
    452    },
    453    { timeout: 100 }
    454  );
    455  let end = new Date().getTime();
    456  lessOrEqual(nevals, 11);
    457  greaterOrEqual(end - start, 100);
    458 });
    459 
    460 add_task(async function test_PollPromise_interval() {
    461  let nevals = 0;
    462  await new PollPromise(
    463    (resolve, reject) => {
    464      ++nevals;
    465      reject();
    466    },
    467    { timeout: 100, interval: 100 }
    468  );
    469  equal(2, nevals);
    470 });
    471 
    472 add_task(async function test_PollPromise_resolve() {
    473  const log = Log.repository.getLogger("RemoteAgent");
    474  const appender = new MockAppender(new Log.BasicFormatter());
    475  appender.level = Log.Level.Info;
    476  log.addAppender(appender);
    477 
    478  const errorMessage = "PollingFailed";
    479  const timeout = 100;
    480 
    481  await new PollPromise(
    482    resolve => {
    483      resolve();
    484    },
    485    { timeout, errorMessage }
    486  );
    487  Assert.equal(appender.messages.length, 0);
    488 
    489  await new PollPromise(
    490    (resolve, reject) => {
    491      reject();
    492    },
    493    { timeout, errorMessage: "PollingFailed" }
    494  );
    495  Assert.equal(appender.messages.length, 1);
    496  Assert.equal(appender.messages[0].level, Log.Level.Warn);
    497  Assert.equal(appender.messages[0].message, "PollingFailed after 100 ms");
    498 });
    499 
    500 add_task(function test_TimedPromise_funcTypes() {
    501  for (let type of ["foo", 42, null, undefined, true, [], {}]) {
    502    Assert.throws(() => new TimedPromise(type), /TypeError/);
    503  }
    504  new TimedPromise(resolve => resolve());
    505  new TimedPromise(function (resolve) {
    506    resolve();
    507  });
    508 });
    509 
    510 add_task(function test_TimedPromise_timeoutTypes() {
    511  for (let timeout of ["foo", null, true, [], {}]) {
    512    Assert.throws(
    513      () => new TimedPromise(resolve => resolve(), { timeout }),
    514      /TypeError/
    515    );
    516  }
    517  for (let timeout of [1.2, -1]) {
    518    Assert.throws(
    519      () => new TimedPromise(resolve => resolve(), { timeout }),
    520      /RangeError/
    521    );
    522  }
    523  new TimedPromise(resolve => resolve(), { timeout: 42 });
    524 });
    525 
    526 add_task(async function test_TimedPromise_errorMessage() {
    527  try {
    528    await new TimedPromise(() => {}, { timeout: 0 });
    529    ok(false, "Expected Timeout error not raised");
    530  } catch (e) {
    531    ok(
    532      e.message.includes("TimedPromise timed out after"),
    533      "Expected default error message found"
    534    );
    535  }
    536 
    537  try {
    538    await new TimedPromise(() => {}, {
    539      errorMessage: "Not found",
    540      timeout: 0,
    541    });
    542    ok(false, "Expected Timeout error not raised");
    543  } catch (e) {
    544    ok(
    545      e.message.includes("Not found after"),
    546      "Expected custom error message found"
    547    );
    548  }
    549 });