tor-browser

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

browser_script_command_execute_basic.js (31507B)


      1 /* Any copyright is dedicated to the Public Domain.
      2 http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 // Testing basic expression evaluation
      7 const {
      8  MAX_AUTOCOMPLETE_ATTEMPTS,
      9  MAX_AUTOCOMPLETIONS,
     10 } = require("resource://devtools/shared/webconsole/js-property-provider.js");
     11 const {
     12  DevToolsServer,
     13 } = require("resource://devtools/server/devtools-server.js");
     14 
     15 add_task(async () => {
     16  const tab = await addTab(`data:text/html;charset=utf-8,
     17  <!DOCTYPE html>
     18  <html dir="ltr" class="class1">
     19  <head><title>Testcase</title></head>
     20  <script>
     21    window.foobarObject = Object.create(
     22      null,
     23      Object.getOwnPropertyDescriptors({
     24        foo: 1,
     25        foobar: 2,
     26        foobaz: 3,
     27        omg: 4,
     28        omgfoo: 5,
     29        strfoo: "foobarz",
     30        omgstr: "foobarz" + "abb".repeat(${DevToolsServer.LONG_STRING_LENGTH} * 2),
     31      })
     32    );
     33 
     34    window.largeObject1 = Object.create(null);
     35    for (let i = 0; i < ${MAX_AUTOCOMPLETE_ATTEMPTS} + 1; i++) {
     36      window.largeObject1["a" + i] = i;
     37    }
     38 
     39    window.largeObject2 = Object.create(null);
     40    for (let i = 0; i < ${MAX_AUTOCOMPLETIONS} * 2; i++) {
     41      window.largeObject2["a" + i] = i;
     42    }
     43 
     44    var originalExec = RegExp.prototype.exec;
     45 
     46    var promptIterable = { [Symbol.iterator]() { return { next: prompt } } };
     47 
     48    function aliasedTest() {
     49      const aliased = "ALIASED";
     50      return [0].map(() => aliased)[0];
     51    }
     52 
     53    var testMap = new Map([[1, 1], [2, 2], [3, 3], [4, 4]]);
     54    var testSet = new Set([1, 2, 3, 4, 5]);
     55    var testProxy = new Proxy({}, { getPrototypeOf: prompt });
     56    var testArray = [1,2,3];
     57    var testInt8Array = new Int8Array([1, 2, 3]);
     58    var testArrayBuffer = testInt8Array.buffer;
     59    var testDataView = new DataView(testArrayBuffer, 2);
     60 
     61    var testCanvasContext = document.createElement("canvas").getContext("2d");
     62 
     63    var objWithNativeGetter = {};
     64    Object.defineProperty(objWithNativeGetter, "print", { get: print });
     65    Object.defineProperty(objWithNativeGetter, "Element", { get: Element });
     66    Object.defineProperty(objWithNativeGetter, "setAttribute", { get: Element.prototype.setAttribute });
     67    Object.defineProperty(objWithNativeGetter, "setClassName", { get: Object.getOwnPropertyDescriptor(Element.prototype, "className").set });
     68    Object.defineProperty(objWithNativeGetter, "requestPermission", { get: Notification.requestPermission });
     69 
     70    async function testAsync() { return 10; }
     71    async function testAsyncAwait() { await 1; return 10; }
     72    async function * testAsyncGen() { return 10; }
     73    async function * testAsyncGenAwait() { await 1; return 10; }
     74 
     75    function testFunc() {}
     76 
     77    var testLocale = new Intl.Locale("de-latn-de-u-ca-gregory-co-phonebk-hc-h23-kf-true-kn-false-nu-latn");
     78 
     79    var testFormData = new FormData();
     80    var testHeaders = new Headers();
     81    var testURLSearchParams = new URLSearchParams();
     82    var testReadableStream = new ReadableStream();
     83  </script>
     84  <body id="body1" class="class2"><h1>Body text</h1></body>
     85  </html>`);
     86 
     87  const commands = await CommandsFactory.forTab(tab);
     88  await commands.targetCommand.startListening();
     89 
     90  await doSimpleEval(commands);
     91  await doWindowEval(commands);
     92  await doEvalWithException(commands);
     93  await doEvalWithHelper(commands);
     94  await doEvalString(commands);
     95  await doEvalLongString(commands);
     96  await doEvalWithBinding(commands);
     97  await forceLexicalInit(commands);
     98  await doSimpleEagerEval(commands);
     99  await doEagerEvalWithSideEffect(commands);
    100  await doEagerEvalWithSideEffectIterator(commands);
    101  await doEagerEvalWithSideEffectMonkeyPatched(commands);
    102  await doEagerEvalESGetters(commands);
    103  await doEagerEvalDOMGetters(commands);
    104  await doEagerEvalOtherNativeGetters(commands);
    105  await doEagerEvalDOMMethods(commands);
    106  await doEagerEvalAsyncFunctions(commands);
    107 
    108  await commands.destroy();
    109 });
    110 
    111 async function doSimpleEval(commands) {
    112  info("test eval '2+2'");
    113  const response = await commands.scriptCommand.execute("2+2");
    114  checkObject(response, {
    115    input: "2+2",
    116    result: 4,
    117  });
    118 
    119  ok(!response.exception, "no eval exception");
    120  ok(!response.helperResult, "no helper result");
    121 }
    122 
    123 async function doWindowEval(commands) {
    124  info("test eval 'document'");
    125  const response = await commands.scriptCommand.execute("document");
    126  checkObject(response, {
    127    input: "document",
    128    result: {
    129      type: "object",
    130      class: "HTMLDocument",
    131      actor: /[a-z]/,
    132    },
    133  });
    134 
    135  ok(!response.exception, "no eval exception");
    136  ok(!response.helperResult, "no helper result");
    137 }
    138 
    139 async function doEvalWithException(commands) {
    140  info("test eval with exception");
    141  const response = await commands.scriptCommand.execute(
    142    "window.doTheImpossible()"
    143  );
    144  checkObject(response, {
    145    input: "window.doTheImpossible()",
    146    result: {
    147      type: "undefined",
    148    },
    149    exceptionMessage: /doTheImpossible/,
    150  });
    151 
    152  ok(response.exception, "js eval exception");
    153  ok(!response.helperResult, "no helper result");
    154 }
    155 
    156 async function doEvalWithHelper(commands) {
    157  info("test eval with helper");
    158  const response = await commands.scriptCommand.execute("clear()");
    159  checkObject(response, {
    160    input: "clear()",
    161    result: {
    162      type: "undefined",
    163    },
    164    helperResult: { type: "clearOutput" },
    165  });
    166 
    167  ok(!response.exception, "no eval exception");
    168 }
    169 
    170 async function doEvalString(commands) {
    171  const response = await commands.scriptCommand.execute(
    172    "window.foobarObject.strfoo"
    173  );
    174 
    175  checkObject(response, {
    176    input: "window.foobarObject.strfoo",
    177    result: "foobarz",
    178  });
    179 }
    180 
    181 async function doEvalLongString(commands) {
    182  const response = await commands.scriptCommand.execute(
    183    "window.foobarObject.omgstr"
    184  );
    185 
    186  const str = await SpecialPowers.spawn(
    187    gBrowser.selectedBrowser,
    188    [],
    189    function () {
    190      return content.wrappedJSObject.foobarObject.omgstr;
    191    }
    192  );
    193 
    194  const initial = str.substring(0, DevToolsServer.LONG_STRING_INITIAL_LENGTH);
    195 
    196  checkObject(response, {
    197    input: "window.foobarObject.omgstr",
    198    result: {
    199      type: "longString",
    200      initial,
    201      length: str.length,
    202    },
    203  });
    204 }
    205 
    206 async function doEvalWithBinding(commands) {
    207  const response = await commands.scriptCommand.execute("document;");
    208  const documentActor = response.result.actorID;
    209 
    210  info("running a command with _self as document using selectedObjectActor");
    211  const selectedObjectSame = await commands.scriptCommand.execute(
    212    "_self === document",
    213    {
    214      selectedObjectActor: documentActor,
    215    }
    216  );
    217  checkObject(selectedObjectSame, {
    218    result: true,
    219  });
    220 }
    221 
    222 async function forceLexicalInit(commands) {
    223  info("test that failed let/const bindings are initialized to undefined");
    224 
    225  const testData = [
    226    {
    227      stmt: "let foopie = wubbalubadubdub",
    228      vars: ["foopie"],
    229    },
    230    {
    231      stmt: "let {z, w={n}=null} = {}",
    232      vars: ["z", "w"],
    233    },
    234    {
    235      stmt: "let [a, b, c] = null",
    236      vars: ["a", "b", "c"],
    237    },
    238    {
    239      stmt: "const nein1 = rofl, nein2 = copter",
    240      vars: ["nein1", "nein2"],
    241    },
    242    {
    243      stmt: "const {ha} = null",
    244      vars: ["ha"],
    245    },
    246    {
    247      stmt: "const [haw=[lame]=null] = []",
    248      vars: ["haw"],
    249    },
    250    {
    251      stmt: "const [rawr, wat=[lame]=null] = []",
    252      vars: ["rawr", "haw"],
    253    },
    254    {
    255      stmt: "let {zzz: xyz=99, zwz: wb} = nexistepas()",
    256      vars: ["xyz", "wb"],
    257    },
    258    {
    259      stmt: "let {c3pdoh=101} = null",
    260      vars: ["c3pdoh"],
    261    },
    262    {
    263      stmt: "const {...x} = x",
    264      vars: ["x"],
    265    },
    266    {
    267      stmt: "const {xx,yy,...rest} = null",
    268      vars: ["xx", "yy", "rest"],
    269    },
    270  ];
    271 
    272  for (const data of testData) {
    273    const response = await commands.scriptCommand.execute(data.stmt);
    274    checkObject(response, {
    275      input: data.stmt,
    276      result: { type: "undefined" },
    277    });
    278    ok(response.exception, "expected exception");
    279    for (const varName of data.vars) {
    280      const response2 = await commands.scriptCommand.execute(varName);
    281      checkObject(response2, {
    282        input: varName,
    283        result: { type: "undefined" },
    284      });
    285      ok(!response2.exception, "unexpected exception");
    286    }
    287  }
    288 }
    289 
    290 async function doSimpleEagerEval(commands) {
    291  const testData = [
    292    {
    293      code: "2+2",
    294      result: 4,
    295    },
    296    {
    297      code: "(x => x * 2)(3)",
    298      result: 6,
    299    },
    300    {
    301      code: "[1, 2, 3].map(x => x * 2).join()",
    302      result: "2,4,6",
    303    },
    304    {
    305      code: `"abc".match(/a./)[0]`,
    306      result: "ab",
    307    },
    308    {
    309      code: "aliasedTest()",
    310      result: "ALIASED",
    311    },
    312    {
    313      code: "testArray.concat([4,5]).join()",
    314      result: "1,2,3,4,5",
    315    },
    316    {
    317      code: "testArray.entries().toString()",
    318      result: "[object Array Iterator]",
    319    },
    320    {
    321      code: "testArray.keys().toString()",
    322      result: "[object Array Iterator]",
    323    },
    324    {
    325      code: "testArray.values().toString()",
    326      result: "[object Array Iterator]",
    327    },
    328    {
    329      code: "testArray.every(x => x < 100)",
    330      result: true,
    331    },
    332    {
    333      code: "testArray.some(x => x > 1)",
    334      result: true,
    335    },
    336    {
    337      code: "testArray.filter(x => x % 2 == 0).join()",
    338      result: "2",
    339    },
    340    {
    341      code: "testArray.find(x => x % 2 == 0)",
    342      result: 2,
    343    },
    344    {
    345      code: "testArray.findIndex(x => x % 2 == 0)",
    346      result: 1,
    347    },
    348    {
    349      code: "[testArray].flat().join()",
    350      result: "1,2,3",
    351    },
    352    {
    353      code: "[testArray].flatMap(x => x).join()",
    354      result: "1,2,3",
    355    },
    356    {
    357      code: "testArray.forEach(x => x); testArray.join()",
    358      result: "1,2,3",
    359    },
    360    {
    361      code: "testArray.includes(1)",
    362      result: true,
    363    },
    364    {
    365      code: "testArray.lastIndexOf(1)",
    366      result: 0,
    367    },
    368    {
    369      code: "testArray.map(x => x + 1).join()",
    370      result: "2,3,4",
    371    },
    372    {
    373      code: "testArray.reduce((acc,x) => acc + x, 0)",
    374      result: 6,
    375    },
    376    {
    377      code: "testArray.reduceRight((acc,x) => acc + x, 0)",
    378      result: 6,
    379    },
    380    {
    381      code: "testArray.slice(0,1).join()",
    382      result: "1",
    383    },
    384    {
    385      code: "testArray.toReversed().join()",
    386      result: "3,2,1",
    387    },
    388    {
    389      code: "testArray.toSorted().join()",
    390      result: "1,2,3",
    391    },
    392    {
    393      code: "testArray.toSpliced(0,1).join()",
    394      result: "2,3",
    395    },
    396    {
    397      code: "testArray.with(1, 'b').join()",
    398      result: "1,b,3",
    399    },
    400 
    401    {
    402      code: "testInt8Array.entries().toString()",
    403      result: "[object Array Iterator]",
    404    },
    405    {
    406      code: "testInt8Array.keys().toString()",
    407      result: "[object Array Iterator]",
    408    },
    409    {
    410      code: "testInt8Array.values().toString()",
    411      result: "[object Array Iterator]",
    412    },
    413    {
    414      code: "testInt8Array.every(x => x < 100)",
    415      result: true,
    416    },
    417    {
    418      code: "testInt8Array.some(x => x > 1)",
    419      result: true,
    420    },
    421    {
    422      code: "testInt8Array.filter(x => x % 2 == 0).join()",
    423      result: "2",
    424    },
    425    {
    426      code: "testInt8Array.find(x => x % 2 == 0)",
    427      result: 2,
    428    },
    429    {
    430      code: "testInt8Array.findIndex(x => x % 2 == 0)",
    431      result: 1,
    432    },
    433    {
    434      code: "testInt8Array.forEach(x => x); testInt8Array.join()",
    435      result: "1,2,3",
    436    },
    437    {
    438      code: "testInt8Array.includes(1)",
    439      result: true,
    440    },
    441    {
    442      code: "testInt8Array.lastIndexOf(1)",
    443      result: 0,
    444    },
    445    {
    446      code: "testInt8Array.map(x => x + 1).join()",
    447      result: "2,3,4",
    448    },
    449    {
    450      code: "testInt8Array.reduce((acc,x) => acc + x, 0)",
    451      result: 6,
    452    },
    453    {
    454      code: "testInt8Array.reduceRight((acc,x) => acc + x, 0)",
    455      result: 6,
    456    },
    457    {
    458      code: "testInt8Array.slice(0,1).join()",
    459      result: "1",
    460    },
    461    {
    462      code: "testInt8Array.toReversed().join()",
    463      skip:
    464        typeof Reflect.getPrototypeOf(Int8Array).prototype.toReversed !==
    465        "function",
    466      result: "3,2,1",
    467    },
    468    {
    469      code: "testInt8Array.toSorted().join()",
    470      skip:
    471        typeof Reflect.getPrototypeOf(Int8Array).prototype.toSorted !==
    472        "function",
    473      result: "1,2,3",
    474    },
    475    {
    476      code: "testInt8Array.with(1, 0).join()",
    477      skip:
    478        typeof Reflect.getPrototypeOf(Int8Array).prototype.with !== "function",
    479      result: "1,0,3",
    480    },
    481  ];
    482 
    483  for (const { code, result, skip } of testData) {
    484    if (skip) {
    485      info(`Skipping evaluation of ${code}`);
    486      continue;
    487    }
    488 
    489    info(`Evaluating: ${code}`);
    490    const response = await commands.scriptCommand.execute(code, {
    491      eager: true,
    492    });
    493    checkObject(response, {
    494      input: code,
    495      result,
    496    });
    497 
    498    ok(!response.exception, "no eval exception");
    499    ok(!response.helperResult, "no helper result");
    500  }
    501 }
    502 
    503 async function doEagerEvalWithSideEffect(commands) {
    504  const testData = [
    505    // Modify environment.
    506    "var a = 10; a;",
    507 
    508    // Directly call a funtion with side effect.
    509    "prompt();",
    510 
    511    // Call a funtion with side effect inside a scripted function.
    512    "(() => { prompt(); })()",
    513 
    514    // Call a funtion with side effect from self-hosted JS function.
    515    "[1, 2, 3].map(prompt)",
    516 
    517    // Call a function with Function.prototype.call.
    518    "Function.prototype.call.bind(Function.prototype.call)(prompt);",
    519 
    520    // Call a function with Function.prototype.apply.
    521    "Function.prototype.apply.bind(Function.prototype.apply)(prompt);",
    522 
    523    // Indirectly call a function with Function.prototype.apply.
    524    "Reflect.apply(prompt, null, []);",
    525    "'aaaaaaaa'.replace(/(a)(a)(a)(a)(a)(a)(a)(a)/, prompt)",
    526 
    527    // Indirect call on obj[Symbol.iterator]().next.
    528    "Array.from(promptIterable)",
    529  ];
    530 
    531  for (const code of testData) {
    532    const response = await commands.scriptCommand.execute(code, {
    533      eager: true,
    534    });
    535    checkObject(response, {
    536      input: code,
    537      result: { type: "undefined" },
    538    });
    539 
    540    ok(!response.exception, "no eval exception");
    541    ok(!response.helperResult, "no helper result");
    542  }
    543 }
    544 
    545 async function doEagerEvalWithSideEffectIterator(commands) {
    546  // Indirect call on %ArrayIterator%.prototype.next,
    547 
    548  // Create an iterable object that reuses iterator across multiple call.
    549  let response = await commands.scriptCommand.execute(`
    550 var arr = [1, 2, 3];
    551 var iterator = arr[Symbol.iterator]();
    552 var iterable = { [Symbol.iterator]() { return iterator; } };
    553 "ok";
    554 `);
    555  checkObject(response, {
    556    result: "ok",
    557  });
    558  ok(!response.exception, "no eval exception");
    559  ok(!response.helperResult, "no helper result");
    560 
    561  const testData = [
    562    "Array.from(iterable)",
    563    "new Map(iterable)",
    564    "new Set(iterable)",
    565  ];
    566 
    567  for (const code of testData) {
    568    response = await commands.scriptCommand.execute(code, {
    569      eager: true,
    570    });
    571    checkObject(response, {
    572      input: code,
    573      result: { type: "undefined" },
    574    });
    575 
    576    ok(!response.exception, "no eval exception");
    577    ok(!response.helperResult, "no helper result");
    578  }
    579 
    580  // Verify the iterator's internal state isn't modified.
    581  response = await commands.scriptCommand.execute(`[...iterator].join(",")`);
    582  checkObject(response, {
    583    result: "1,2,3",
    584  });
    585  ok(!response.exception, "no eval exception");
    586  ok(!response.helperResult, "no helper result");
    587 }
    588 
    589 async function doEagerEvalWithSideEffectMonkeyPatched(commands) {
    590  // Patch the built-in function without eager evaluation.
    591  let response = await commands.scriptCommand.execute(
    592    `RegExp.prototype.exec = prompt; "patched"`
    593  );
    594  checkObject(response, {
    595    result: "patched",
    596  });
    597  ok(!response.exception, "no eval exception");
    598  ok(!response.helperResult, "no helper result");
    599 
    600  // Test eager evaluation, where the patched built-in is called internally.
    601  // This should be aborted.
    602  const code = `"abc".match(/a./)[0]`;
    603  response = await commands.scriptCommand.execute(code, { eager: true });
    604  checkObject(response, {
    605    input: code,
    606    result: { type: "undefined" },
    607  });
    608 
    609  ok(!response.exception, "no eval exception");
    610  ok(!response.helperResult, "no helper result");
    611 
    612  // Undo the patch without eager evaluation.
    613  response = await commands.scriptCommand.execute(
    614    `RegExp.prototype.exec = originalExec; "unpatched"`
    615  );
    616  checkObject(response, {
    617    result: "unpatched",
    618  });
    619  ok(!response.exception, "no eval exception");
    620  ok(!response.helperResult, "no helper result");
    621 
    622  // Test eager evaluation again, without the patch.
    623  // This should be evaluated.
    624  response = await commands.scriptCommand.execute(code, { eager: true });
    625  checkObject(response, {
    626    input: code,
    627    result: "ab",
    628  });
    629 }
    630 
    631 async function doEagerEvalESGetters(commands) {
    632  // [code, expectedResult]
    633  const testData = [
    634    // ArrayBuffer
    635    ["testArrayBuffer.byteLength", 3],
    636 
    637    // DataView
    638    ["testDataView.buffer === testArrayBuffer", true],
    639    ["testDataView.byteLength", 1],
    640    ["testDataView.byteOffset", 2],
    641 
    642    // Error
    643    ["typeof new Error().stack", "string"],
    644 
    645    // Function
    646    ["typeof testFunc.arguments", "object"],
    647    ["typeof testFunc.caller", "object"],
    648 
    649    // Intl.Locale
    650    ["testLocale.baseName", "de-Latn-DE"],
    651    ["testLocale.calendar", "gregory"],
    652    ["testLocale.caseFirst", ""],
    653    ["testLocale.collation", "phonebk"],
    654    ["testLocale.hourCycle", "h23"],
    655    ["testLocale.numeric", false],
    656    ["testLocale.numberingSystem", "latn"],
    657    ["testLocale.language", "de"],
    658    ["testLocale.script", "Latn"],
    659    ["testLocale.region", "DE"],
    660 
    661    // Map
    662    ["testMap.size", 4],
    663 
    664    // RegExp
    665    ["/a/.dotAll", false],
    666    ["/a/giy.flags", "giy"],
    667    ["/a/g.global", true],
    668    ["/a/g.hasIndices", false],
    669    ["/a/g.ignoreCase", false],
    670    ["/a/g.multiline", false],
    671    ["/a/g.source", "a"],
    672    ["/a/g.sticky", false],
    673    ["/a/g.unicode", false],
    674 
    675    // Set
    676    ["testSet.size", 5],
    677 
    678    // Symbol
    679    ["Symbol.iterator.description", "Symbol.iterator"],
    680 
    681    // TypedArray
    682    ["testInt8Array.buffer === testArrayBuffer", true],
    683    ["testInt8Array.byteLength", 3],
    684    ["testInt8Array.byteOffset", 0],
    685    ["testInt8Array.length", 3],
    686    ["testInt8Array[Symbol.toStringTag]", "Int8Array"],
    687  ];
    688 
    689  for (const [code, expectedResult] of testData) {
    690    const response = await commands.scriptCommand.execute(code, {
    691      eager: true,
    692    });
    693    checkObject(
    694      response,
    695      {
    696        input: code,
    697        result: expectedResult,
    698      },
    699      code
    700    );
    701 
    702    ok(!response.exception, "no eval exception");
    703    ok(!response.helperResult, "no helper result");
    704  }
    705 
    706  // Test RegExp static properties.
    707  // Run preparation code here to avoid interference with other tests,
    708  // given RegExp static properties are global state.
    709  const regexpPreparationCode = `
    710 /b(c)(d)(e)(f)(g)(h)(i)(j)(k)l/.test("abcdefghijklm")
    711 `;
    712 
    713  const prepResponse = await commands.scriptCommand.execute(
    714    regexpPreparationCode
    715  );
    716  checkObject(prepResponse, {
    717    input: regexpPreparationCode,
    718    result: true,
    719  });
    720 
    721  ok(!prepResponse.exception, "no eval exception");
    722  ok(!prepResponse.helperResult, "no helper result");
    723 
    724  const testDataRegExp = [
    725    // RegExp static
    726    ["RegExp.input", "abcdefghijklm"],
    727    ["RegExp.lastMatch", "bcdefghijkl"],
    728    ["RegExp.lastParen", "k"],
    729    ["RegExp.leftContext", "a"],
    730    ["RegExp.rightContext", "m"],
    731    ["RegExp.$1", "c"],
    732    ["RegExp.$2", "d"],
    733    ["RegExp.$3", "e"],
    734    ["RegExp.$4", "f"],
    735    ["RegExp.$5", "g"],
    736    ["RegExp.$6", "h"],
    737    ["RegExp.$7", "i"],
    738    ["RegExp.$8", "j"],
    739    ["RegExp.$9", "k"],
    740    ["RegExp.$_", "abcdefghijklm"], // input
    741    ["RegExp['$&']", "bcdefghijkl"], // lastMatch
    742    ["RegExp['$+']", "k"], // lastParen
    743    ["RegExp['$`']", "a"], // leftContext
    744    ["RegExp[`$'`]", "m"], // rightContext
    745  ];
    746 
    747  for (const [code, expectedResult] of testDataRegExp) {
    748    const response = await commands.scriptCommand.execute(code, {
    749      eager: true,
    750    });
    751    checkObject(
    752      response,
    753      {
    754        input: code,
    755        result: expectedResult,
    756      },
    757      code
    758    );
    759 
    760    ok(!response.exception, "no eval exception");
    761    ok(!response.helperResult, "no helper result");
    762  }
    763 
    764  const testDataWithSideEffect = [
    765    // get Object.prototype.__proto__
    766    //
    767    // This can invoke Proxy getPrototypeOf handler, which can be any native
    768    // function, and debugger cannot hook the call.
    769    `[].__proto__`,
    770    `testProxy.__proto__`,
    771  ];
    772 
    773  for (const code of testDataWithSideEffect) {
    774    const response = await commands.scriptCommand.execute(code, {
    775      eager: true,
    776    });
    777    checkObject(
    778      response,
    779      {
    780        input: code,
    781        result: { type: "undefined" },
    782      },
    783      code
    784    );
    785 
    786    ok(!response.exception, "no eval exception");
    787    ok(!response.helperResult, "no helper result");
    788  }
    789 }
    790 
    791 async function doEagerEvalDOMGetters(commands) {
    792  // Getters explicitly marked no-side-effect.
    793  //
    794  // [code, expectedResult]
    795  const testDataExplicit = [
    796    // DOMTokenList
    797    ["document.documentElement.classList.length", 1],
    798    ["document.documentElement.classList.value", "class1"],
    799 
    800    // Document
    801    ["document.URL.startsWith('data:')", true],
    802    ["document.documentURI.startsWith('data:')", true],
    803    ["document.compatMode", "CSS1Compat"],
    804    ["document.characterSet", "UTF-8"],
    805    ["document.charset", "UTF-8"],
    806    ["document.inputEncoding", "UTF-8"],
    807    ["document.contentType", "text/html"],
    808    ["document.doctype.constructor.name", "DocumentType"],
    809    ["document.documentElement.constructor.name", "HTMLHtmlElement"],
    810    ["document.title", "Testcase"],
    811    ["document.dir", "ltr"],
    812    ["document.body.constructor.name", "HTMLBodyElement"],
    813    ["document.head.constructor.name", "HTMLHeadElement"],
    814    ["document.images.constructor.name", "HTMLCollection"],
    815    ["document.embeds.constructor.name", "HTMLCollection"],
    816    ["document.plugins.constructor.name", "HTMLCollection"],
    817    ["document.links.constructor.name", "HTMLCollection"],
    818    ["document.forms.constructor.name", "HTMLCollection"],
    819    ["document.scripts.constructor.name", "HTMLCollection"],
    820    ["document.defaultView === window", true],
    821    ["typeof document.currentScript", "object"],
    822    ["document.anchors.constructor.name", "HTMLCollection"],
    823    ["document.applets.constructor.name", "HTMLCollection"],
    824    ["document.all.constructor.name", "HTMLAllCollection"],
    825    ["document.styleSheetSets.constructor.name", "DOMStringList"],
    826    ["typeof document.featurePolicy", "undefined"],
    827    ["typeof document.blockedNodeByClassifierCount", "undefined"],
    828    ["typeof document.blockedNodesByClassifier", "undefined"],
    829    ["typeof document.permDelegateHandler", "undefined"],
    830    ["document.children.constructor.name", "HTMLCollection"],
    831    ["document.firstElementChild === document.documentElement", true],
    832    ["document.lastElementChild === document.documentElement", true],
    833    ["document.childElementCount", 1],
    834    ["document.location.href.startsWith('data:')", true],
    835 
    836    // Element
    837    ["document.body.namespaceURI", "http://www.w3.org/1999/xhtml"],
    838    ["document.body.prefix === null", true],
    839    ["document.body.localName", "body"],
    840    ["document.body.tagName", "BODY"],
    841    ["document.body.id", "body1"],
    842    ["document.body.className", "class2"],
    843    ["document.body.classList.constructor.name", "DOMTokenList"],
    844    ["document.body.part.constructor.name", "DOMTokenList"],
    845    ["document.body.attributes.constructor.name", "NamedNodeMap"],
    846    ["document.body.innerHTML.includes('Body text')", true],
    847    ["document.body.outerHTML.includes('Body text')", true],
    848    ["document.body.previousElementSibling !== null", true],
    849    ["document.body.nextElementSibling === null", true],
    850    ["document.body.children.constructor.name", "HTMLCollection"],
    851    ["document.body.firstElementChild !== null", true],
    852    ["document.body.lastElementChild !== null", true],
    853    ["document.body.childElementCount", 1],
    854 
    855    // Node
    856    ["document.body.nodeType === Node.ELEMENT_NODE", true],
    857    ["document.body.nodeName", "BODY"],
    858    ["document.body.baseURI.startsWith('data:')", true],
    859    ["document.body.isConnected", true],
    860    ["document.body.ownerDocument === document", true],
    861    ["document.body.parentNode === document.documentElement", true],
    862    ["document.body.parentElement === document.documentElement", true],
    863    ["document.body.childNodes.constructor.name", "NodeList"],
    864    ["document.body.firstChild !== null", true],
    865    ["document.body.lastChild !== null", true],
    866    ["document.body.previousSibling !== null", true],
    867    ["document.body.nextSibling === null", true],
    868    ["document.body.nodeValue === null", true],
    869    ["document.body.textContent.includes('Body text')", true],
    870    ["typeof document.body.flattenedTreeParentNode", "undefined"],
    871    ["typeof document.body.isNativeAnonymous", "undefined"],
    872    ["typeof document.body.containingShadowRoot", "undefined"],
    873    ["typeof document.body.accessibleNode", "undefined"],
    874 
    875    // Performance
    876    ["performance.timeOrigin > 0", true],
    877    ["performance.timing.constructor.name", "PerformanceTiming"],
    878    ["performance.navigation.constructor.name", "PerformanceNavigation"],
    879    ["performance.eventCounts.constructor.name", "EventCounts"],
    880 
    881    // window
    882    ["window.window === window", true],
    883    ["window.self === window", true],
    884    ["window.document.constructor.name", "HTMLDocument"],
    885    ["window.performance.constructor.name", "Performance"],
    886    ["typeof window.browsingContext", "undefined"],
    887    ["typeof window.windowUtils", "undefined"],
    888    ["typeof window.windowGlobalChild", "undefined"],
    889    ["window.visualViewport.constructor.name", "VisualViewport"],
    890    ["typeof window.caches", "undefined"],
    891    ["window.location.href.startsWith('data:')", true],
    892  ];
    893  if (typeof Scheduler === "function") {
    894    // Scheduler is behind a pref.
    895    testDataExplicit.push(["window.scheduler.constructor.name", "Scheduler"]);
    896  }
    897 
    898  for (const [code, expectedResult] of testDataExplicit) {
    899    const response = await commands.scriptCommand.execute(code, {
    900      eager: true,
    901    });
    902    checkObject(
    903      response,
    904      {
    905        input: code,
    906        result: expectedResult,
    907      },
    908      code
    909    );
    910 
    911    ok(!response.exception, "no eval exception");
    912    ok(!response.helperResult, "no helper result");
    913  }
    914 
    915  // Getters not-explicitly marked no-side-effect.
    916  // All DOM getters are considered no-side-effect in eager evaluation context.
    917  const testDataImplicit = [
    918    // NOTE: This is not an exhaustive list.
    919    // Document
    920    [`document.implementation.constructor.name`, "DOMImplementation"],
    921    [`typeof document.domain`, "string"],
    922    [`typeof document.referrer`, "string"],
    923    [`typeof document.cookie`, "string"],
    924    [`typeof document.lastModified`, "string"],
    925    [`typeof document.readyState`, "string"],
    926    [`typeof document.designMode`, "string"],
    927    [`typeof document.onabort`, "object"],
    928 
    929    // Element
    930    [`typeof document.documentElement.scrollTop`, "number"],
    931    [`typeof document.documentElement.scrollLeft`, "number"],
    932    [`typeof document.documentElement.scrollWidth`, "number"],
    933    [`typeof document.documentElement.scrollHeight`, "number"],
    934 
    935    // Performance
    936    [`typeof performance.onresourcetimingbufferfull`, "object"],
    937 
    938    // window
    939    [`typeof window.name`, "string"],
    940    [`window.history.constructor.name`, "History"],
    941    [`window.customElements.constructor.name`, "CustomElementRegistry"],
    942    [`window.locationbar.constructor.name`, "BarProp"],
    943    [`window.menubar.constructor.name`, "BarProp"],
    944    [`typeof window.status`, "string"],
    945    [`window.closed`, false],
    946 
    947    // CanvasRenderingContext2D / CanvasCompositing
    948    [`testCanvasContext.globalAlpha`, 1],
    949  ];
    950 
    951  for (const [code, expectedResult] of testDataImplicit) {
    952    const response = await commands.scriptCommand.execute(code, {
    953      eager: true,
    954    });
    955    checkObject(
    956      response,
    957      {
    958        input: code,
    959        result: expectedResult,
    960      },
    961      code
    962    );
    963 
    964    ok(!response.exception, "no eval exception");
    965    ok(!response.helperResult, "no helper result");
    966  }
    967 }
    968 
    969 async function doEagerEvalOtherNativeGetters(commands) {
    970  // DOM getter functions are allowed to be eagerly-evaluated.
    971  // Test the situation where non-DOM-getter function is called by accessing
    972  // getter.
    973  //
    974  // "being a DOM getter" is tested by checking if the native function has
    975  // JSJitInfo and it's marked as getter.
    976  const testData = [
    977    // Has no JitInfo.
    978    "objWithNativeGetter.print",
    979    "objWithNativeGetter.Element",
    980 
    981    // Not marked as getter, but method.
    982    "objWithNativeGetter.getAttribute",
    983 
    984    // Not marked as getter, but setter.
    985    "objWithNativeGetter.setClassName",
    986 
    987    // Not marked as getter, but static method.
    988    "objWithNativeGetter.requestPermission",
    989  ];
    990 
    991  for (const code of testData) {
    992    const response = await commands.scriptCommand.execute(code, {
    993      eager: true,
    994    });
    995    checkObject(
    996      response,
    997      {
    998        input: code,
    999        result: { type: "undefined" },
   1000      },
   1001      code
   1002    );
   1003 
   1004    ok(!response.exception, "no eval exception");
   1005    ok(!response.helperResult, "no helper result");
   1006  }
   1007 }
   1008 
   1009 async function doEagerEvalDOMMethods(commands) {
   1010  // The following "values" methods share single native function with different
   1011  // JitInfo, while ReadableStream's "values" isn't side-effect free.
   1012 
   1013  // TODO: See Bug 1910717, this can be removed when we remove the pref for iterator helpers
   1014  let constructorName = "Object";
   1015  if (typeof Iterator === "function") {
   1016    constructorName = "Iterator";
   1017  }
   1018 
   1019  const testDataAllowed = [
   1020    [`testFormData.values().constructor.name`, constructorName],
   1021    [`testHeaders.values().constructor.name`, constructorName],
   1022    [`testURLSearchParams.values().constructor.name`, constructorName],
   1023  ];
   1024 
   1025  for (const [code, expectedResult] of testDataAllowed) {
   1026    const response = await commands.scriptCommand.execute(code, {
   1027      eager: true,
   1028    });
   1029    checkObject(
   1030      response,
   1031      {
   1032        input: code,
   1033        result: expectedResult,
   1034      },
   1035      code
   1036    );
   1037 
   1038    ok(!response.exception, "no eval exception");
   1039    ok(!response.helperResult, "no helper result");
   1040  }
   1041 
   1042  const testDataDisallowed = ["testReadableStream.values()"];
   1043 
   1044  for (const code of testDataDisallowed) {
   1045    const response = await commands.scriptCommand.execute(code, {
   1046      eager: true,
   1047    });
   1048    checkObject(
   1049      response,
   1050      {
   1051        input: code,
   1052        result: { type: "undefined" },
   1053      },
   1054      code
   1055    );
   1056 
   1057    ok(!response.exception, "no eval exception");
   1058    ok(!response.helperResult, "no helper result");
   1059  }
   1060 }
   1061 
   1062 async function doEagerEvalAsyncFunctions(commands) {
   1063  // [code, expectedResult]
   1064  const testData = [["typeof testAsync()", "object"]];
   1065 
   1066  for (const [code, expectedResult] of testData) {
   1067    const response = await commands.scriptCommand.execute(code, {
   1068      eager: true,
   1069    });
   1070    checkObject(
   1071      response,
   1072      {
   1073        input: code,
   1074        result: expectedResult,
   1075      },
   1076      code
   1077    );
   1078 
   1079    ok(!response.exception, "no eval exception");
   1080    ok(!response.helperResult, "no helper result");
   1081  }
   1082 
   1083  const testDataWithSideEffect = [
   1084    // await is effectful
   1085    "testAsyncAwait()",
   1086 
   1087    // initial yield is effectful
   1088    "testAsyncGen()",
   1089    "testAsyncGenAwait()",
   1090  ];
   1091 
   1092  for (const code of testDataWithSideEffect) {
   1093    const response = await commands.scriptCommand.execute(code, {
   1094      eager: true,
   1095    });
   1096    checkObject(
   1097      response,
   1098      {
   1099        input: code,
   1100        result: { type: "undefined" },
   1101      },
   1102      code
   1103    );
   1104 
   1105    ok(!response.exception, "no eval exception");
   1106    ok(!response.helperResult, "no helper result");
   1107  }
   1108 }