tor-browser

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

browser_console_content_getters.js (17287B)


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