11cb0ef41Sopenharmony_ci// Flags: --no-warnings --expose-gc --expose-internals
21cb0ef41Sopenharmony_ci'use strict';
31cb0ef41Sopenharmony_ci
41cb0ef41Sopenharmony_ciconst common = require('../common');
51cb0ef41Sopenharmony_ciconst { inspect } = require('util');
61cb0ef41Sopenharmony_ci
71cb0ef41Sopenharmony_ciconst {
81cb0ef41Sopenharmony_ci  ok,
91cb0ef41Sopenharmony_ci  notStrictEqual,
101cb0ef41Sopenharmony_ci  strictEqual,
111cb0ef41Sopenharmony_ci  throws,
121cb0ef41Sopenharmony_ci} = require('assert');
131cb0ef41Sopenharmony_ci
141cb0ef41Sopenharmony_ciconst {
151cb0ef41Sopenharmony_ci  kWeakHandler,
161cb0ef41Sopenharmony_ci} = require('internal/event_target');
171cb0ef41Sopenharmony_ci
181cb0ef41Sopenharmony_ciconst { setTimeout: sleep } = require('timers/promises');
191cb0ef41Sopenharmony_ci
201cb0ef41Sopenharmony_ci{
211cb0ef41Sopenharmony_ci  // Tests that abort is fired with the correct event type on AbortControllers
221cb0ef41Sopenharmony_ci  const ac = new AbortController();
231cb0ef41Sopenharmony_ci  ok(ac.signal);
241cb0ef41Sopenharmony_ci  ac.signal.onabort = common.mustCall((event) => {
251cb0ef41Sopenharmony_ci    ok(event);
261cb0ef41Sopenharmony_ci    strictEqual(event.type, 'abort');
271cb0ef41Sopenharmony_ci  });
281cb0ef41Sopenharmony_ci  ac.signal.addEventListener('abort', common.mustCall((event) => {
291cb0ef41Sopenharmony_ci    ok(event);
301cb0ef41Sopenharmony_ci    strictEqual(event.type, 'abort');
311cb0ef41Sopenharmony_ci  }), { once: true });
321cb0ef41Sopenharmony_ci  ac.abort();
331cb0ef41Sopenharmony_ci  ac.abort();
341cb0ef41Sopenharmony_ci  ok(ac.signal.aborted);
351cb0ef41Sopenharmony_ci}
361cb0ef41Sopenharmony_ci
371cb0ef41Sopenharmony_ci{
381cb0ef41Sopenharmony_ci  // Tests that abort events are trusted
391cb0ef41Sopenharmony_ci  const ac = new AbortController();
401cb0ef41Sopenharmony_ci  ac.signal.addEventListener('abort', common.mustCall((event) => {
411cb0ef41Sopenharmony_ci    ok(event.isTrusted);
421cb0ef41Sopenharmony_ci  }));
431cb0ef41Sopenharmony_ci  ac.abort();
441cb0ef41Sopenharmony_ci}
451cb0ef41Sopenharmony_ci
461cb0ef41Sopenharmony_ci{
471cb0ef41Sopenharmony_ci  // Tests that abort events have the same `isTrusted` reference
481cb0ef41Sopenharmony_ci  const first = new AbortController();
491cb0ef41Sopenharmony_ci  const second = new AbortController();
501cb0ef41Sopenharmony_ci  let ev1, ev2;
511cb0ef41Sopenharmony_ci  const ev3 = new Event('abort');
521cb0ef41Sopenharmony_ci  first.signal.addEventListener('abort', common.mustCall((event) => {
531cb0ef41Sopenharmony_ci    ev1 = event;
541cb0ef41Sopenharmony_ci  }));
551cb0ef41Sopenharmony_ci  second.signal.addEventListener('abort', common.mustCall((event) => {
561cb0ef41Sopenharmony_ci    ev2 = event;
571cb0ef41Sopenharmony_ci  }));
581cb0ef41Sopenharmony_ci  first.abort();
591cb0ef41Sopenharmony_ci  second.abort();
601cb0ef41Sopenharmony_ci  const firstTrusted = Reflect.getOwnPropertyDescriptor(Object.getPrototypeOf(ev1), 'isTrusted').get;
611cb0ef41Sopenharmony_ci  const secondTrusted = Reflect.getOwnPropertyDescriptor(Object.getPrototypeOf(ev2), 'isTrusted').get;
621cb0ef41Sopenharmony_ci  const untrusted = Reflect.getOwnPropertyDescriptor(Object.getPrototypeOf(ev3), 'isTrusted').get;
631cb0ef41Sopenharmony_ci  strictEqual(firstTrusted, secondTrusted);
641cb0ef41Sopenharmony_ci  strictEqual(untrusted, firstTrusted);
651cb0ef41Sopenharmony_ci}
661cb0ef41Sopenharmony_ci
671cb0ef41Sopenharmony_ci{
681cb0ef41Sopenharmony_ci  // Tests that AbortSignal is impossible to construct manually
691cb0ef41Sopenharmony_ci  const ac = new AbortController();
701cb0ef41Sopenharmony_ci  throws(() => new ac.signal.constructor(), {
711cb0ef41Sopenharmony_ci    code: 'ERR_ILLEGAL_CONSTRUCTOR',
721cb0ef41Sopenharmony_ci  });
731cb0ef41Sopenharmony_ci}
741cb0ef41Sopenharmony_ci{
751cb0ef41Sopenharmony_ci  // Symbol.toStringTag
761cb0ef41Sopenharmony_ci  const toString = (o) => Object.prototype.toString.call(o);
771cb0ef41Sopenharmony_ci  const ac = new AbortController();
781cb0ef41Sopenharmony_ci  strictEqual(toString(ac), '[object AbortController]');
791cb0ef41Sopenharmony_ci  strictEqual(toString(ac.signal), '[object AbortSignal]');
801cb0ef41Sopenharmony_ci}
811cb0ef41Sopenharmony_ci
821cb0ef41Sopenharmony_ci{
831cb0ef41Sopenharmony_ci  const signal = AbortSignal.abort();
841cb0ef41Sopenharmony_ci  ok(signal.aborted);
851cb0ef41Sopenharmony_ci}
861cb0ef41Sopenharmony_ci
871cb0ef41Sopenharmony_ci{
881cb0ef41Sopenharmony_ci  // Test that AbortController properties and methods validate the receiver
891cb0ef41Sopenharmony_ci  const acSignalGet = Object.getOwnPropertyDescriptor(
901cb0ef41Sopenharmony_ci    AbortController.prototype,
911cb0ef41Sopenharmony_ci    'signal'
921cb0ef41Sopenharmony_ci  ).get;
931cb0ef41Sopenharmony_ci  const acAbort = AbortController.prototype.abort;
941cb0ef41Sopenharmony_ci
951cb0ef41Sopenharmony_ci  const goodController = new AbortController();
961cb0ef41Sopenharmony_ci  ok(acSignalGet.call(goodController));
971cb0ef41Sopenharmony_ci  acAbort.call(goodController);
981cb0ef41Sopenharmony_ci
991cb0ef41Sopenharmony_ci  const badAbortControllers = [
1001cb0ef41Sopenharmony_ci    null,
1011cb0ef41Sopenharmony_ci    undefined,
1021cb0ef41Sopenharmony_ci    0,
1031cb0ef41Sopenharmony_ci    NaN,
1041cb0ef41Sopenharmony_ci    true,
1051cb0ef41Sopenharmony_ci    'AbortController',
1061cb0ef41Sopenharmony_ci    Object.create(AbortController.prototype),
1071cb0ef41Sopenharmony_ci  ];
1081cb0ef41Sopenharmony_ci  for (const badController of badAbortControllers) {
1091cb0ef41Sopenharmony_ci    throws(
1101cb0ef41Sopenharmony_ci      () => acSignalGet.call(badController),
1111cb0ef41Sopenharmony_ci      { code: 'ERR_INVALID_THIS', name: 'TypeError' }
1121cb0ef41Sopenharmony_ci    );
1131cb0ef41Sopenharmony_ci    throws(
1141cb0ef41Sopenharmony_ci      () => acAbort.call(badController),
1151cb0ef41Sopenharmony_ci      { code: 'ERR_INVALID_THIS', name: 'TypeError' }
1161cb0ef41Sopenharmony_ci    );
1171cb0ef41Sopenharmony_ci  }
1181cb0ef41Sopenharmony_ci}
1191cb0ef41Sopenharmony_ci
1201cb0ef41Sopenharmony_ci{
1211cb0ef41Sopenharmony_ci  // Test that AbortSignal properties validate the receiver
1221cb0ef41Sopenharmony_ci  const signalAbortedGet = Object.getOwnPropertyDescriptor(
1231cb0ef41Sopenharmony_ci    AbortSignal.prototype,
1241cb0ef41Sopenharmony_ci    'aborted'
1251cb0ef41Sopenharmony_ci  ).get;
1261cb0ef41Sopenharmony_ci
1271cb0ef41Sopenharmony_ci  const goodSignal = new AbortController().signal;
1281cb0ef41Sopenharmony_ci  strictEqual(signalAbortedGet.call(goodSignal), false);
1291cb0ef41Sopenharmony_ci
1301cb0ef41Sopenharmony_ci  const badAbortSignals = [
1311cb0ef41Sopenharmony_ci    null,
1321cb0ef41Sopenharmony_ci    undefined,
1331cb0ef41Sopenharmony_ci    0,
1341cb0ef41Sopenharmony_ci    NaN,
1351cb0ef41Sopenharmony_ci    true,
1361cb0ef41Sopenharmony_ci    'AbortSignal',
1371cb0ef41Sopenharmony_ci    Object.create(AbortSignal.prototype),
1381cb0ef41Sopenharmony_ci  ];
1391cb0ef41Sopenharmony_ci  for (const badSignal of badAbortSignals) {
1401cb0ef41Sopenharmony_ci    throws(
1411cb0ef41Sopenharmony_ci      () => signalAbortedGet.call(badSignal),
1421cb0ef41Sopenharmony_ci      { code: 'ERR_INVALID_THIS', name: 'TypeError' }
1431cb0ef41Sopenharmony_ci    );
1441cb0ef41Sopenharmony_ci  }
1451cb0ef41Sopenharmony_ci}
1461cb0ef41Sopenharmony_ci
1471cb0ef41Sopenharmony_ci{
1481cb0ef41Sopenharmony_ci  const ac = new AbortController();
1491cb0ef41Sopenharmony_ci  strictEqual(inspect(ac, { depth: 1 }),
1501cb0ef41Sopenharmony_ci              'AbortController { signal: [AbortSignal] }');
1511cb0ef41Sopenharmony_ci  strictEqual(inspect(ac, { depth: null }),
1521cb0ef41Sopenharmony_ci              'AbortController { signal: AbortSignal { aborted: false } }');
1531cb0ef41Sopenharmony_ci}
1541cb0ef41Sopenharmony_ci
1551cb0ef41Sopenharmony_ci{
1561cb0ef41Sopenharmony_ci  // Test AbortSignal.reason
1571cb0ef41Sopenharmony_ci  const ac = new AbortController();
1581cb0ef41Sopenharmony_ci  ac.abort('reason');
1591cb0ef41Sopenharmony_ci  strictEqual(ac.signal.reason, 'reason');
1601cb0ef41Sopenharmony_ci}
1611cb0ef41Sopenharmony_ci
1621cb0ef41Sopenharmony_ci{
1631cb0ef41Sopenharmony_ci  // Test AbortSignal.reason
1641cb0ef41Sopenharmony_ci  const signal = AbortSignal.abort('reason');
1651cb0ef41Sopenharmony_ci  strictEqual(signal.reason, 'reason');
1661cb0ef41Sopenharmony_ci}
1671cb0ef41Sopenharmony_ci
1681cb0ef41Sopenharmony_ci{
1691cb0ef41Sopenharmony_ci  // Test AbortSignal timeout
1701cb0ef41Sopenharmony_ci  const signal = AbortSignal.timeout(10);
1711cb0ef41Sopenharmony_ci  ok(!signal.aborted);
1721cb0ef41Sopenharmony_ci  setTimeout(common.mustCall(() => {
1731cb0ef41Sopenharmony_ci    ok(signal.aborted);
1741cb0ef41Sopenharmony_ci    strictEqual(signal.reason.name, 'TimeoutError');
1751cb0ef41Sopenharmony_ci    strictEqual(signal.reason.code, 23);
1761cb0ef41Sopenharmony_ci  }), 20);
1771cb0ef41Sopenharmony_ci}
1781cb0ef41Sopenharmony_ci
1791cb0ef41Sopenharmony_ci{
1801cb0ef41Sopenharmony_ci  (async () => {
1811cb0ef41Sopenharmony_ci    // Test AbortSignal timeout doesn't prevent the signal
1821cb0ef41Sopenharmony_ci    // from being garbage collected.
1831cb0ef41Sopenharmony_ci    let ref;
1841cb0ef41Sopenharmony_ci    {
1851cb0ef41Sopenharmony_ci      ref = new globalThis.WeakRef(AbortSignal.timeout(1_200_000));
1861cb0ef41Sopenharmony_ci    }
1871cb0ef41Sopenharmony_ci
1881cb0ef41Sopenharmony_ci    await sleep(10);
1891cb0ef41Sopenharmony_ci    globalThis.gc();
1901cb0ef41Sopenharmony_ci    strictEqual(ref.deref(), undefined);
1911cb0ef41Sopenharmony_ci  })().then(common.mustCall());
1921cb0ef41Sopenharmony_ci
1931cb0ef41Sopenharmony_ci  (async () => {
1941cb0ef41Sopenharmony_ci    // Test that an AbortSignal with a timeout is not gc'd while
1951cb0ef41Sopenharmony_ci    // there is an active listener on it.
1961cb0ef41Sopenharmony_ci    let ref;
1971cb0ef41Sopenharmony_ci    function handler() {}
1981cb0ef41Sopenharmony_ci    {
1991cb0ef41Sopenharmony_ci      ref = new globalThis.WeakRef(AbortSignal.timeout(1_200_000));
2001cb0ef41Sopenharmony_ci      ref.deref().addEventListener('abort', handler);
2011cb0ef41Sopenharmony_ci    }
2021cb0ef41Sopenharmony_ci
2031cb0ef41Sopenharmony_ci    await sleep(10);
2041cb0ef41Sopenharmony_ci    globalThis.gc();
2051cb0ef41Sopenharmony_ci    notStrictEqual(ref.deref(), undefined);
2061cb0ef41Sopenharmony_ci    ok(ref.deref() instanceof AbortSignal);
2071cb0ef41Sopenharmony_ci
2081cb0ef41Sopenharmony_ci    ref.deref().removeEventListener('abort', handler);
2091cb0ef41Sopenharmony_ci
2101cb0ef41Sopenharmony_ci    await sleep(10);
2111cb0ef41Sopenharmony_ci    globalThis.gc();
2121cb0ef41Sopenharmony_ci    strictEqual(ref.deref(), undefined);
2131cb0ef41Sopenharmony_ci  })().then(common.mustCall());
2141cb0ef41Sopenharmony_ci
2151cb0ef41Sopenharmony_ci  (async () => {
2161cb0ef41Sopenharmony_ci    // If the event listener is weak, however, it should not prevent gc
2171cb0ef41Sopenharmony_ci    let ref;
2181cb0ef41Sopenharmony_ci    function handler() {}
2191cb0ef41Sopenharmony_ci    {
2201cb0ef41Sopenharmony_ci      ref = new globalThis.WeakRef(AbortSignal.timeout(1_200_000));
2211cb0ef41Sopenharmony_ci      ref.deref().addEventListener('abort', handler, { [kWeakHandler]: {} });
2221cb0ef41Sopenharmony_ci    }
2231cb0ef41Sopenharmony_ci
2241cb0ef41Sopenharmony_ci    await sleep(10);
2251cb0ef41Sopenharmony_ci    globalThis.gc();
2261cb0ef41Sopenharmony_ci    strictEqual(ref.deref(), undefined);
2271cb0ef41Sopenharmony_ci  })().then(common.mustCall());
2281cb0ef41Sopenharmony_ci
2291cb0ef41Sopenharmony_ci  // Setting a long timeout (20 minutes here) should not
2301cb0ef41Sopenharmony_ci  // keep the Node.js process open (the timer is unref'd)
2311cb0ef41Sopenharmony_ci  AbortSignal.timeout(1_200_000);
2321cb0ef41Sopenharmony_ci}
2331cb0ef41Sopenharmony_ci
2341cb0ef41Sopenharmony_ci{
2351cb0ef41Sopenharmony_ci  // Test AbortSignal.reason default
2361cb0ef41Sopenharmony_ci  const signal = AbortSignal.abort();
2371cb0ef41Sopenharmony_ci  ok(signal.reason instanceof DOMException);
2381cb0ef41Sopenharmony_ci  strictEqual(signal.reason.code, 20);
2391cb0ef41Sopenharmony_ci
2401cb0ef41Sopenharmony_ci  const ac = new AbortController();
2411cb0ef41Sopenharmony_ci  ac.abort();
2421cb0ef41Sopenharmony_ci  ok(ac.signal.reason instanceof DOMException);
2431cb0ef41Sopenharmony_ci  strictEqual(ac.signal.reason.code, 20);
2441cb0ef41Sopenharmony_ci}
2451cb0ef41Sopenharmony_ci
2461cb0ef41Sopenharmony_ci{
2471cb0ef41Sopenharmony_ci  // Test abortSignal.throwIfAborted()
2481cb0ef41Sopenharmony_ci  throws(() => AbortSignal.abort().throwIfAborted(), {
2491cb0ef41Sopenharmony_ci    code: 20,
2501cb0ef41Sopenharmony_ci    name: 'AbortError',
2511cb0ef41Sopenharmony_ci  });
2521cb0ef41Sopenharmony_ci
2531cb0ef41Sopenharmony_ci  // Does not throw because it's not aborted.
2541cb0ef41Sopenharmony_ci  const ac = new AbortController();
2551cb0ef41Sopenharmony_ci  ac.signal.throwIfAborted();
2561cb0ef41Sopenharmony_ci}
2571cb0ef41Sopenharmony_ci
2581cb0ef41Sopenharmony_ci{
2591cb0ef41Sopenharmony_ci  const originalDesc = Reflect.getOwnPropertyDescriptor(AbortSignal.prototype, 'aborted');
2601cb0ef41Sopenharmony_ci  const actualReason = new Error();
2611cb0ef41Sopenharmony_ci  Reflect.defineProperty(AbortSignal.prototype, 'aborted', { value: false });
2621cb0ef41Sopenharmony_ci  throws(() => AbortSignal.abort(actualReason).throwIfAborted(), actualReason);
2631cb0ef41Sopenharmony_ci  Reflect.defineProperty(AbortSignal.prototype, 'aborted', originalDesc);
2641cb0ef41Sopenharmony_ci}
2651cb0ef41Sopenharmony_ci
2661cb0ef41Sopenharmony_ci{
2671cb0ef41Sopenharmony_ci  const originalDesc = Reflect.getOwnPropertyDescriptor(AbortSignal.prototype, 'reason');
2681cb0ef41Sopenharmony_ci  const actualReason = new Error();
2691cb0ef41Sopenharmony_ci  const fakeExcuse = new Error();
2701cb0ef41Sopenharmony_ci  Reflect.defineProperty(AbortSignal.prototype, 'reason', { value: fakeExcuse });
2711cb0ef41Sopenharmony_ci  throws(() => AbortSignal.abort(actualReason).throwIfAborted(), actualReason);
2721cb0ef41Sopenharmony_ci  Reflect.defineProperty(AbortSignal.prototype, 'reason', originalDesc);
2731cb0ef41Sopenharmony_ci}
274