tor-browser

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

browser_webconsole_object_inspector_getters.js (17948B)


      1 /* Any copyright is dedicated to the Public Domain.
      2 * http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 // Check evaluating and expanding getters in the console.
      7 const TEST_URI =
      8  "data:text/html;charset=utf8,<!DOCTYPE html><h1>Object Inspector on Getters</h1>";
      9 const { ELLIPSIS } = require("resource://devtools/shared/l10n.js");
     10 
     11 add_task(async function () {
     12  const hud = await openNewTabAndConsole(TEST_URI);
     13 
     14  const LONGSTRING = "ab ".repeat(1e5);
     15 
     16  await SpecialPowers.spawn(
     17    gBrowser.selectedBrowser,
     18    [LONGSTRING],
     19    function (longString) {
     20      content.wrappedJSObject.console.log(
     21        "oi-test",
     22        Object.create(
     23          null,
     24          Object.getOwnPropertyDescriptors({
     25            get myStringGetter() {
     26              return "hello";
     27            },
     28            get myNumberGetter() {
     29              return 123;
     30            },
     31            get myUndefinedGetter() {
     32              return undefined;
     33            },
     34            get myNullGetter() {
     35              return null;
     36            },
     37            get myZeroGetter() {
     38              return 0;
     39            },
     40            get myEmptyStringGetter() {
     41              return "";
     42            },
     43            get myFalseGetter() {
     44              return false;
     45            },
     46            get myTrueGetter() {
     47              return true;
     48            },
     49            get myObjectGetter() {
     50              return { foo: "bar" };
     51            },
     52            get myArrayGetter() {
     53              return Array.from({ length: 1000 }, (_, i) => i);
     54            },
     55            get myMapGetter() {
     56              return new Map([["foo", { bar: "baz" }]]);
     57            },
     58            get myProxyGetter() {
     59              const handler = {
     60                get(target, name) {
     61                  return name in target ? target[name] : 37;
     62                },
     63              };
     64              return new Proxy({ a: 1 }, handler);
     65            },
     66            get myThrowingGetter() {
     67              throw new Error("myError");
     68            },
     69            get myLongStringGetter() {
     70              return longString;
     71            },
     72            get ["hyphen-getter"]() {
     73              return "---";
     74            },
     75            get [`"quoted-getter"`]() {
     76              return "quoted";
     77            },
     78            get [`"'\``]() {
     79              return "quoted2";
     80            },
     81          })
     82        )
     83      );
     84    }
     85  );
     86 
     87  const node = await waitFor(() => findConsoleAPIMessage(hud, "oi-test"));
     88  const oi = node.querySelector(".tree");
     89 
     90  await expandObjectInspectorNode(oi.querySelector(".tree-node"));
     91 
     92  await testStringGetter(oi);
     93  await testNumberGetter(oi);
     94  await testUndefinedGetter(oi);
     95  await testNullGetter(oi);
     96  await testZeroGetter(oi);
     97  await testEmptyStringGetter(oi);
     98  await testFalseGetter(oi);
     99  await testTrueGetter(oi);
    100  await testObjectGetter(oi);
    101  await testArrayGetter(oi);
    102  await testMapGetter(oi);
    103  await testProxyGetter(oi);
    104  await testThrowingGetter(oi);
    105  await testLongStringGetter(oi, LONGSTRING);
    106  await testHypgenGetter(oi);
    107  await testQuotedGetters(oi);
    108 });
    109 
    110 async function testStringGetter(oi) {
    111  let node = findObjectInspectorNode(oi, "myStringGetter");
    112  is(
    113    isObjectInspectorNodeExpandable(node),
    114    false,
    115    "The node can't be expanded"
    116  );
    117  const invokeButton = getObjectInspectorInvokeGetterButton(node);
    118  ok(invokeButton, "There is an invoke button as expected");
    119 
    120  invokeButton.click();
    121  await waitFor(
    122    () =>
    123      !getObjectInspectorInvokeGetterButton(
    124        findObjectInspectorNode(oi, "myStringGetter")
    125      )
    126  );
    127 
    128  node = findObjectInspectorNode(oi, "myStringGetter");
    129  ok(
    130    node.textContent.includes(`myStringGetter: "hello"`),
    131    "String getter now has the expected text content"
    132  );
    133  is(
    134    isObjectInspectorNodeExpandable(node),
    135    false,
    136    "The node can't be expanded"
    137  );
    138 }
    139 
    140 async function testNumberGetter(oi) {
    141  let node = findObjectInspectorNode(oi, "myNumberGetter");
    142  is(
    143    isObjectInspectorNodeExpandable(node),
    144    false,
    145    "The node can't be expanded"
    146  );
    147  const invokeButton = getObjectInspectorInvokeGetterButton(node);
    148  ok(invokeButton, "There is an invoke button as expected");
    149 
    150  invokeButton.click();
    151  await waitFor(
    152    () =>
    153      !getObjectInspectorInvokeGetterButton(
    154        findObjectInspectorNode(oi, "myNumberGetter")
    155      )
    156  );
    157 
    158  node = findObjectInspectorNode(oi, "myNumberGetter");
    159  ok(
    160    node.textContent.includes(`myNumberGetter: 123`),
    161    "Number getter now has the expected text content"
    162  );
    163  is(
    164    isObjectInspectorNodeExpandable(node),
    165    false,
    166    "The node can't be expanded"
    167  );
    168 }
    169 
    170 async function testUndefinedGetter(oi) {
    171  let node = findObjectInspectorNode(oi, "myUndefinedGetter");
    172  is(
    173    isObjectInspectorNodeExpandable(node),
    174    false,
    175    "The node can't be expanded"
    176  );
    177  const invokeButton = getObjectInspectorInvokeGetterButton(node);
    178  ok(invokeButton, "There is an invoke button as expected");
    179 
    180  invokeButton.click();
    181  await waitFor(
    182    () =>
    183      !getObjectInspectorInvokeGetterButton(
    184        findObjectInspectorNode(oi, "myUndefinedGetter")
    185      )
    186  );
    187 
    188  node = findObjectInspectorNode(oi, "myUndefinedGetter");
    189  ok(
    190    node.textContent.includes(`myUndefinedGetter: undefined`),
    191    "undefined getter now has the expected text content"
    192  );
    193  is(
    194    isObjectInspectorNodeExpandable(node),
    195    false,
    196    "The node can't be expanded"
    197  );
    198 }
    199 
    200 async function testNullGetter(oi) {
    201  let node = findObjectInspectorNode(oi, "myNullGetter");
    202  is(
    203    isObjectInspectorNodeExpandable(node),
    204    false,
    205    "The node can't be expanded"
    206  );
    207  const invokeButton = getObjectInspectorInvokeGetterButton(node);
    208  ok(invokeButton, "There is an invoke button as expected");
    209 
    210  invokeButton.click();
    211  await waitFor(
    212    () =>
    213      !getObjectInspectorInvokeGetterButton(
    214        findObjectInspectorNode(oi, "myNullGetter")
    215      )
    216  );
    217 
    218  node = findObjectInspectorNode(oi, "myNullGetter");
    219  ok(
    220    node.textContent.includes(`myNullGetter: null`),
    221    "null getter now has the expected text content"
    222  );
    223  is(
    224    isObjectInspectorNodeExpandable(node),
    225    false,
    226    "The node can't be expanded"
    227  );
    228 }
    229 
    230 async function testZeroGetter(oi) {
    231  let node = findObjectInspectorNode(oi, "myZeroGetter");
    232  is(
    233    isObjectInspectorNodeExpandable(node),
    234    false,
    235    "The node can't be expanded"
    236  );
    237  const invokeButton = getObjectInspectorInvokeGetterButton(node);
    238  ok(invokeButton, "There is an invoke button as expected");
    239 
    240  invokeButton.click();
    241  await waitFor(
    242    () =>
    243      !getObjectInspectorInvokeGetterButton(
    244        findObjectInspectorNode(oi, "myZeroGetter")
    245      )
    246  );
    247 
    248  node = findObjectInspectorNode(oi, "myZeroGetter");
    249  ok(
    250    node.textContent.includes(`myZeroGetter: 0`),
    251    "0 getter now has the expected text content"
    252  );
    253  is(
    254    isObjectInspectorNodeExpandable(node),
    255    false,
    256    "The node can't be expanded"
    257  );
    258 }
    259 
    260 async function testEmptyStringGetter(oi) {
    261  let node = findObjectInspectorNode(oi, "myEmptyStringGetter");
    262  is(
    263    isObjectInspectorNodeExpandable(node),
    264    false,
    265    "The node can't be expanded"
    266  );
    267  const invokeButton = getObjectInspectorInvokeGetterButton(node);
    268  ok(invokeButton, "There is an invoke button as expected");
    269 
    270  invokeButton.click();
    271  await waitFor(
    272    () =>
    273      !getObjectInspectorInvokeGetterButton(
    274        findObjectInspectorNode(oi, "myEmptyStringGetter")
    275      )
    276  );
    277 
    278  node = findObjectInspectorNode(oi, "myEmptyStringGetter");
    279  ok(
    280    node.textContent.includes(`myEmptyStringGetter: ""`),
    281    "empty string getter now has the expected text content"
    282  );
    283  is(
    284    isObjectInspectorNodeExpandable(node),
    285    false,
    286    "The node can't be expanded"
    287  );
    288 }
    289 
    290 async function testFalseGetter(oi) {
    291  let node = findObjectInspectorNode(oi, "myFalseGetter");
    292  is(
    293    isObjectInspectorNodeExpandable(node),
    294    false,
    295    "The node can't be expanded"
    296  );
    297  const invokeButton = getObjectInspectorInvokeGetterButton(node);
    298  ok(invokeButton, "There is an invoke button as expected");
    299 
    300  invokeButton.click();
    301  await waitFor(
    302    () =>
    303      !getObjectInspectorInvokeGetterButton(
    304        findObjectInspectorNode(oi, "myFalseGetter")
    305      )
    306  );
    307 
    308  node = findObjectInspectorNode(oi, "myFalseGetter");
    309  ok(
    310    node.textContent.includes(`myFalseGetter: false`),
    311    "false getter now has the expected text content"
    312  );
    313  is(
    314    isObjectInspectorNodeExpandable(node),
    315    false,
    316    "The node can't be expanded"
    317  );
    318 }
    319 
    320 async function testTrueGetter(oi) {
    321  let node = findObjectInspectorNode(oi, "myTrueGetter");
    322  is(
    323    isObjectInspectorNodeExpandable(node),
    324    false,
    325    "The node can't be expanded"
    326  );
    327  const invokeButton = getObjectInspectorInvokeGetterButton(node);
    328  ok(invokeButton, "There is an invoke button as expected");
    329 
    330  invokeButton.click();
    331  await waitFor(
    332    () =>
    333      !getObjectInspectorInvokeGetterButton(
    334        findObjectInspectorNode(oi, "myTrueGetter")
    335      )
    336  );
    337 
    338  node = findObjectInspectorNode(oi, "myTrueGetter");
    339  ok(
    340    node.textContent.includes(`myTrueGetter: true`),
    341    "false getter now has the expected text content"
    342  );
    343  is(
    344    isObjectInspectorNodeExpandable(node),
    345    false,
    346    "The node can't be expanded"
    347  );
    348 }
    349 
    350 async function testObjectGetter(oi) {
    351  let node = findObjectInspectorNode(oi, "myObjectGetter");
    352  is(
    353    isObjectInspectorNodeExpandable(node),
    354    false,
    355    "The node can't be expanded"
    356  );
    357  const invokeButton = getObjectInspectorInvokeGetterButton(node);
    358  ok(invokeButton, "There is an invoke button as expected");
    359 
    360  invokeButton.click();
    361  await waitFor(
    362    () =>
    363      !getObjectInspectorInvokeGetterButton(
    364        findObjectInspectorNode(oi, "myObjectGetter")
    365      )
    366  );
    367 
    368  node = findObjectInspectorNode(oi, "myObjectGetter");
    369  ok(
    370    node.textContent.includes(`myObjectGetter: Object { foo: "bar" }`),
    371    "object getter now has the expected text content"
    372  );
    373  is(isObjectInspectorNodeExpandable(node), true, "The node can be expanded");
    374 
    375  await expandObjectInspectorNode(node);
    376  checkChildren(node, [`foo: "bar"`, `<prototype>`]);
    377 }
    378 
    379 async function testArrayGetter(oi) {
    380  let node = findObjectInspectorNode(oi, "myArrayGetter");
    381  is(
    382    isObjectInspectorNodeExpandable(node),
    383    false,
    384    "The node can't be expanded"
    385  );
    386  const invokeButton = getObjectInspectorInvokeGetterButton(node);
    387  ok(invokeButton, "There is an invoke button as expected");
    388 
    389  invokeButton.click();
    390  await waitFor(
    391    () =>
    392      !getObjectInspectorInvokeGetterButton(
    393        findObjectInspectorNode(oi, "myArrayGetter")
    394      )
    395  );
    396 
    397  node = findObjectInspectorNode(oi, "myArrayGetter");
    398  ok(
    399    node.textContent.includes(
    400      `myArrayGetter: Array(1000) [ 0, 1, 2, ${ELLIPSIS} ]`
    401    ),
    402    "Array getter now has the expected text content - "
    403  );
    404  is(isObjectInspectorNodeExpandable(node), true, "The node can be expanded");
    405 
    406  await expandObjectInspectorNode(node);
    407  const children = getObjectInspectorChildrenNodes(node);
    408 
    409  const firstBucket = children[0];
    410  ok(firstBucket.textContent.includes(`[0${ELLIPSIS}99]`), "Array has buckets");
    411 
    412  is(
    413    isObjectInspectorNodeExpandable(firstBucket),
    414    true,
    415    "The bucket can be expanded"
    416  );
    417  await expandObjectInspectorNode(firstBucket);
    418  checkChildren(
    419    firstBucket,
    420    Array.from({ length: 100 }, (_, i) => `${i}: ${i}`)
    421  );
    422 }
    423 
    424 async function testMapGetter(oi) {
    425  let node = findObjectInspectorNode(oi, "myMapGetter");
    426  is(
    427    isObjectInspectorNodeExpandable(node),
    428    false,
    429    "The node can't be expanded"
    430  );
    431  const invokeButton = getObjectInspectorInvokeGetterButton(node);
    432  ok(invokeButton, "There is an invoke button as expected");
    433 
    434  invokeButton.click();
    435  await waitFor(
    436    () =>
    437      !getObjectInspectorInvokeGetterButton(
    438        findObjectInspectorNode(oi, "myMapGetter")
    439      )
    440  );
    441 
    442  node = findObjectInspectorNode(oi, "myMapGetter");
    443  ok(
    444    node.textContent.includes(`myMapGetter: Map`),
    445    "map getter now has the expected text content"
    446  );
    447  is(isObjectInspectorNodeExpandable(node), true, "The node can be expanded");
    448 
    449  await expandObjectInspectorNode(node);
    450  checkChildren(node, [`size`, `<entries>`, `<prototype>`]);
    451 
    452  const entriesNode = findObjectInspectorNode(oi, "<entries>");
    453  await expandObjectInspectorNode(entriesNode);
    454  checkChildren(entriesNode, [`foo → Object { bar: "baz" }`]);
    455 
    456  const entryNode = getObjectInspectorChildrenNodes(entriesNode)[0];
    457  await expandObjectInspectorNode(entryNode);
    458  checkChildren(entryNode, [`<key>: "foo"`, `<value>: Object { bar: "baz" }`]);
    459 }
    460 
    461 async function testProxyGetter(oi) {
    462  let node = findObjectInspectorNode(oi, "myProxyGetter");
    463  is(
    464    isObjectInspectorNodeExpandable(node),
    465    false,
    466    "The node can't be expanded"
    467  );
    468  const invokeButton = getObjectInspectorInvokeGetterButton(node);
    469  ok(invokeButton, "There is an invoke button as expected");
    470 
    471  invokeButton.click();
    472  await waitFor(
    473    () =>
    474      !getObjectInspectorInvokeGetterButton(
    475        findObjectInspectorNode(oi, "myProxyGetter")
    476      )
    477  );
    478 
    479  node = findObjectInspectorNode(oi, "myProxyGetter");
    480  ok(
    481    node.textContent.includes(`myProxyGetter: Proxy`),
    482    "proxy getter now has the expected text content"
    483  );
    484  is(isObjectInspectorNodeExpandable(node), true, "The node can be expanded");
    485 
    486  await expandObjectInspectorNode(node);
    487  checkChildren(node, [`<target>`, `<handler>`]);
    488 
    489  const targetNode = findObjectInspectorNode(oi, "<target>");
    490  await expandObjectInspectorNode(targetNode);
    491  checkChildren(targetNode, [`a: 1`, `<prototype>`]);
    492 
    493  const handlerNode = findObjectInspectorNode(oi, "<handler>");
    494  await expandObjectInspectorNode(handlerNode);
    495  checkChildren(handlerNode, [`get:`, `<prototype>`]);
    496 }
    497 
    498 async function testThrowingGetter(oi) {
    499  let node = findObjectInspectorNode(oi, "myThrowingGetter");
    500  is(
    501    isObjectInspectorNodeExpandable(node),
    502    false,
    503    "The node can't be expanded"
    504  );
    505  const invokeButton = getObjectInspectorInvokeGetterButton(node);
    506  ok(invokeButton, "There is an invoke button as expected");
    507 
    508  invokeButton.click();
    509  await waitFor(
    510    () =>
    511      !getObjectInspectorInvokeGetterButton(
    512        findObjectInspectorNode(oi, "myThrowingGetter")
    513      )
    514  );
    515 
    516  node = findObjectInspectorNode(oi, "myThrowingGetter");
    517  ok(
    518    node.textContent.includes(`myThrowingGetter: Error`),
    519    "throwing getter does show the error"
    520  );
    521  is(isObjectInspectorNodeExpandable(node), true, "The node can be expanded");
    522 
    523  await expandObjectInspectorNode(node);
    524  await waitFor(() => !!getObjectInspectorChildrenNodes(node).length);
    525  checkChildren(node, [
    526    `columnNumber`,
    527    `fileName`,
    528    `lineNumber`,
    529    `message`,
    530    `stack`,
    531    `<prototype>`,
    532  ]);
    533 }
    534 
    535 async function testLongStringGetter(oi, longString) {
    536  const getLongStringNode = () =>
    537    findObjectInspectorNode(oi, "myLongStringGetter");
    538  const node = getLongStringNode();
    539  is(
    540    isObjectInspectorNodeExpandable(node),
    541    false,
    542    "The node can't be expanded"
    543  );
    544  const invokeButton = getObjectInspectorInvokeGetterButton(node);
    545  ok(invokeButton, "There is an invoke button as expected");
    546 
    547  invokeButton.click();
    548  await waitFor(() =>
    549    getLongStringNode().textContent.includes(`myLongStringGetter: "ab ab`)
    550  );
    551  ok(true, "longstring getter shows the initial text");
    552  is(
    553    isObjectInspectorNodeExpandable(getLongStringNode()),
    554    true,
    555    "The node can be expanded"
    556  );
    557 
    558  await expandObjectInspectorNode(getLongStringNode());
    559  await waitFor(() =>
    560    getLongStringNode().textContent.includes(
    561      `myLongStringGetter: "${longString}"`
    562    )
    563  );
    564  ok(true, "the longstring was expanded");
    565 }
    566 
    567 async function testHypgenGetter(oi) {
    568  const findHyphenGetterNode = () =>
    569    findObjectInspectorNode(oi, `"hyphen-getter"`);
    570  let node = findHyphenGetterNode();
    571 
    572  is(
    573    isObjectInspectorNodeExpandable(node),
    574    false,
    575    "The node can't be expanded"
    576  );
    577  const invokeButton = getObjectInspectorInvokeGetterButton(node);
    578  ok(invokeButton, "There is an invoke button as expected");
    579 
    580  invokeButton.click();
    581  await waitFor(
    582    () => !getObjectInspectorInvokeGetterButton(findHyphenGetterNode())
    583  );
    584 
    585  node = findHyphenGetterNode();
    586  ok(
    587    node.textContent.includes(`"hyphen-getter": "---"`),
    588    "Node now has the expected text content"
    589  );
    590  is(
    591    isObjectInspectorNodeExpandable(node),
    592    false,
    593    "The node can't be expanded"
    594  );
    595 }
    596 
    597 async function testQuotedGetters(oi) {
    598  const nodes = [
    599    {
    600      name: `'"quoted-getter"'`,
    601      expected: `"quoted"`,
    602      expandable: false,
    603    },
    604    {
    605      name: `"\\"'\`"`,
    606      expected: `"quoted2"`,
    607      expandable: false,
    608    },
    609  ];
    610 
    611  for (const { name, expected, expandable } of nodes) {
    612    await testGetter(oi, name, expected, expandable);
    613  }
    614 }
    615 
    616 async function testGetter(oi, propertyName, expectedResult, resultExpandable) {
    617  info(`Check «${propertyName}» getter`);
    618  const findNode = () => findObjectInspectorNode(oi, propertyName);
    619 
    620  let node = findNode();
    621  is(
    622    isObjectInspectorNodeExpandable(node),
    623    false,
    624    ${propertyName}» can't be expanded`
    625  );
    626  getObjectInspectorInvokeGetterButton(node).click();
    627  await waitFor(() => !getObjectInspectorInvokeGetterButton(findNode()));
    628 
    629  node = findNode();
    630  ok(
    631    node.textContent.includes(`${propertyName}: ${expectedResult}`),
    632    ${propertyName}» now has the expected text content («${expectedResult}»)`
    633  );
    634  is(
    635    isObjectInspectorNodeExpandable(node),
    636    resultExpandable,
    637    ${propertyName}» ${resultExpandable ? "now can" : "can't"} be expanded`
    638  );
    639 }
    640 
    641 function checkChildren(node, expectedChildren) {
    642  const children = getObjectInspectorChildrenNodes(node);
    643  is(
    644    children.length,
    645    expectedChildren.length,
    646    "There is the expected number of children"
    647  );
    648  children.forEach((child, index) => {
    649    ok(
    650      child.textContent.includes(expectedChildren[index]),
    651      `Expected "${child.textContent}" to include "${expectedChildren[index]}"`
    652    );
    653  });
    654 }