tor-browser

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

browser_rules_variables_unused.js (7779B)


      1 /* Any copyright is dedicated to the Public Domain.
      2   https://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 add_task(async function testHiddenUnusedVariables() {
      7  const h1Declarations = [
      8    { name: "--foo", value: "1" },
      9    { name: "--bar", value: "2" },
     10    { name: "--foobar", value: "calc( var(--foo, 3) * var(--bar, 4))" },
     11    { name: "--fallback", value: "var(--fallback-a)" },
     12    { name: "--fallback-a", value: "var(--fallback-b)" },
     13    { name: "--fallback-b", value: "var(--fallback-c)" },
     14    { name: "--fallback-c", value: "10" },
     15    { name: "--cycle-a", value: "var(--cycle-b)" },
     16    { name: "--cycle-b", value: "var(--cycle-a)" },
     17    { name: "--unused-a", value: "var(--unused-b)" },
     18    { name: "--unused-b", value: "5" },
     19    { name: "--h", value: "400px" },
     20    // Generate a good amount of variables that won't be referenced anywhere to trigger the
     21    // "hide unused" mechanism
     22    ...Array.from({ length: 10 }, (_, i) => ({
     23      name: `--unused-no-dep-${i}`,
     24      value: i.toString(),
     25    })),
     26    {
     27      name: "width",
     28      value: `calc(var(--foobar, var(--fallback)) + var(--cycle-a) + var(--unset))`,
     29    },
     30  ];
     31 
     32  // set a different rule using a variable from the first rule to check if its detected
     33  // as being used
     34  const whereH1Declarations = [
     35    // declare 9 unused variables, so they should be visible by default
     36    ...Array.from({ length: 9 }, (_, i) => ({
     37      name: `--unused-where-${i}`,
     38      value: i.toString(),
     39    })),
     40    {
     41      name: "height",
     42      // Using variable for the h1 rule
     43      value: "var(--h)",
     44    },
     45  ];
     46 
     47  const TEST_URI = `
     48  <style>
     49    h1 {
     50      ${h1Declarations
     51        .map(({ name, value }) => `${name}: ${value};`)
     52        .join("\n")}
     53    }
     54 
     55    :where(h1) {
     56      ${whereH1Declarations
     57        .map(({ name, value }) => `${name}: ${value};`)
     58        .join("\n")}
     59    }
     60  </style>
     61  <h1>Hello</h1>
     62 `;
     63 
     64  await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
     65  const { inspector, view } = await openRuleView();
     66  await selectNode("h1", inspector);
     67 
     68  info("Check that elementStyle.usedVariables has the expected data");
     69  Assert.deepEqual(Array.from(view._elementStyle.usedVariables), [
     70    // in `h1 -> width`
     71    "--foobar",
     72    // in `h1 -> width`
     73    "--fallback",
     74    // in `h1 -> width`
     75    "--cycle-a",
     76    // in `h1 -> width`, is picked up even if it's not defined
     77    "--unset",
     78    // in `:where(h1) -> height`
     79    "--h",
     80    // in `h1 -> --foobar`, which is used in `h1 -> width`
     81    "--foo",
     82    // in `h1 -> --foobar`, which is used in `h1 -> width`
     83    "--bar",
     84    // in `h1 -> --fallback`, which is used in `h1 -> width`
     85    "--fallback-a",
     86    // in `h1 -> --fallback-a`, which is used in `h1 -> --fallback`, which is used in `h1 -> width`
     87    "--fallback-b",
     88    // in `h1 -> --fallback-b`, which is used in `h1 -> --fallback-a`, which is used in `h1 -> --fallback`,
     89    // which is used in `h1 -> width`
     90    "--fallback-c",
     91    // in `h1 --cycle-a`, which is used in `h1 -> width`
     92    "--cycle-b",
     93  ]);
     94 
     95  await checkRuleViewContent(view, [
     96    {
     97      selector: "element",
     98      selectorEditable: false,
     99      declarations: [],
    100    },
    101    {
    102      selector: "h1",
    103      declarations: [
    104        { name: "--foo", value: "1" },
    105        { name: "--bar", value: "2" },
    106        { name: "--foobar", value: "calc( var(--foo, 3) * var(--bar, 4))" },
    107        { name: "--fallback", value: "var(--fallback-a)" },
    108        { name: "--fallback-a", value: "var(--fallback-b)" },
    109        { name: "--fallback-b", value: "var(--fallback-c)" },
    110        { name: "--fallback-c", value: "10" },
    111        { name: "--cycle-a", value: "var(--cycle-b)" },
    112        { name: "--cycle-b", value: "var(--cycle-a)" },
    113        // Displayed because used in `:where(h1) -> height`
    114        { name: "--h", value: "400px" },
    115        {
    116          name: "width",
    117          value: `calc(var(--foobar, var(--fallback)) + var(--cycle-a) + var(--unset))`,
    118        },
    119      ],
    120    },
    121    {
    122      selector: ":where(h1)",
    123      // All declarations are displayed, even the unused variables, because we don't
    124      // hit the threshold to trigger the "hide unused" UI
    125      declarations: whereH1Declarations,
    126    },
    127  ]);
    128 
    129  info("Check that the 'Show X unused variables button is displayed'");
    130  const showUnusedVariablesButton = getUnusedVariableButton(view, 1);
    131  ok(!!showUnusedVariablesButton, "Show unused variables button is displayed");
    132 
    133  info("Check that the button doesn't prevent the usual keyboard navigation");
    134  const h1RuleEditor = getRuleViewRuleEditor(view, 1);
    135  const whereH1RuleEditor = getRuleViewRuleEditor(view, 2);
    136 
    137  await focusNewRuleViewProperty(h1RuleEditor);
    138 
    139  EventUtils.synthesizeKey("VK_TAB", {}, view.styleWindow);
    140  is(
    141    inplaceEditor(view.styleDocument.activeElement),
    142    inplaceEditor(whereH1RuleEditor.selectorText),
    143    "Hitting Tab triggered the editor for the selector of the next rule"
    144  );
    145 
    146  EventUtils.synthesizeKey("VK_TAB", { shiftKey: true }, view.styleWindow);
    147  is(
    148    inplaceEditor(view.styleDocument.activeElement),
    149    inplaceEditor(h1RuleEditor.newPropSpan),
    150    "Hitting Shift+Tab triggered the editor for the new property"
    151  );
    152 
    153  // Blur the input to not interfere with the rest of the test
    154  const onBlur = once(view.styleDocument.activeElement, "blur");
    155  view.styleDocument.activeElement.blur();
    156  await onBlur;
    157 
    158  info(
    159    "Check that clicking the Show unused variable button does show the unused variables"
    160  );
    161  showUnusedVariablesButton.click();
    162 
    163  is(
    164    getUnusedVariableButton(view, 1),
    165    null,
    166    "Show unused variable button is not visible anymore"
    167  );
    168 
    169  await checkRuleViewContent(view, [
    170    {
    171      selector: "element",
    172      selectorEditable: false,
    173      declarations: [],
    174    },
    175    {
    176      selector: "h1",
    177      declarations: h1Declarations,
    178    },
    179    {
    180      selector: ":where(h1)",
    181      declarations: whereH1Declarations,
    182    },
    183  ]);
    184 
    185  info(
    186    "Selecting another node and select h1 back to assert the rules after a refresh"
    187  );
    188  await selectNode("body", inspector);
    189  await selectNode("h1", inspector);
    190 
    191  is(
    192    getUnusedVariableButton(view, 1),
    193    null,
    194    "Unused variable button is kept hidden after refreshing rules view"
    195  );
    196 
    197  info("Add another unused variables to the :where(h1) rule");
    198  // Sanity check
    199  ok(
    200    !getUnusedVariableButton(view, 2),
    201    "The unused variable button isn't displayed at first for :where(h1) rule"
    202  );
    203 
    204  // We shouldn't add the property via the UI, as variables added by the user in the
    205  // Rules view are always visible (see browser_rules_variables_unused_add_property.js).
    206  // We could add the property via CSSOM, but it looks like there's a bug at the moment
    207  // where properties aren't showing up (unrelated to unused variable).
    208  // So add the property via the UI, but clear the user properties so it won't be seen
    209  // as added by the user.
    210  await addProperty(view, 2, "--added-unused-where", "new-1");
    211  view.store.userProperties.clear();
    212 
    213  info(
    214    "Selecting another node and select h1 back after adding property via CSSOM"
    215  );
    216  await selectNode("body", inspector);
    217  await selectNode("h1", inspector);
    218 
    219  await checkRuleViewContent(view, [
    220    {
    221      selector: "element",
    222      selectorEditable: false,
    223      declarations: [],
    224    },
    225    {
    226      selector: "h1",
    227      declarations: h1Declarations,
    228    },
    229    {
    230      selector: ":where(h1)",
    231      // we only see the height variable, --unused-c is now hidden, as well as all the
    232      // --unused-cssom-* variables
    233      declarations: [{ name: "height", value: "var(--h)" }],
    234    },
    235  ]);
    236 
    237  is(
    238    getUnusedVariableButton(view, 2).textContent,
    239    "Show 10 unused custom CSS properties",
    240    "Unused variable button is kept hidden after refreshing rules view"
    241  );
    242 });