tor-browser

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

browser_rules_variables_autocomplete.js (5687B)


      1 /* Any copyright is dedicated to the Public Domain.
      2 http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 // Test for autocomplete of CSS variables in the Rules view.
      7 
      8 const IFRAME_URL = `https://example.org/document-builder.sjs?html=${encodeURIComponent(`
      9  <style>
     10    @property --iframe {
     11      syntax: "*";
     12      inherits: true;
     13    }
     14    body {
     15      --iframe-not-registered: turquoise;
     16    }
     17 
     18    h1 {
     19      color: tomato;
     20    }
     21  </style>
     22  <h1>iframe</h1>
     23 `)}`;
     24 
     25 const TEST_URI = `https://example.org/document-builder.sjs?html=
     26 <script>
     27    CSS.registerProperty({
     28      name: "--js",
     29      syntax: "<color>",
     30      inherits: false,
     31      initialValue: "gold"
     32    });
     33  </script>
     34  <style>
     35    @property --css {
     36      syntax: "<color>";
     37      inherits: false;
     38      initial-value: tomato;
     39    }
     40 
     41    h1 {
     42      --css: red;
     43      --not-registered: blue;
     44      --nested: var(--js);
     45      --nested-with-function: color-mix(in srgb, var(--css) 50%, var(--not-registered));
     46      color: gold;
     47    }
     48  </style>
     49  <h1>Hello world</h1>
     50  <iframe src="${encodeURIComponent(IFRAME_URL)}"></iframe>`;
     51 
     52 add_task(async function () {
     53  await pushPref("layout.css.properties-and-values.enabled", true);
     54 
     55  await addTab(TEST_URI);
     56  const { inspector, view } = await openRuleView();
     57  await selectNode("h1", inspector);
     58 
     59  info("Wait for @property panel to be displayed");
     60  await waitFor(() =>
     61    view.styleDocument.querySelector("#registered-properties-container")
     62  );
     63 
     64  const topLevelVariables = [
     65    { label: "--css", postLabel: "rgb(255, 0, 0)", hasColorSwatch: true },
     66    { label: "--js", postLabel: "gold", hasColorSwatch: true },
     67    { label: "--nested", postLabel: "rgb(255, 215, 0)", hasColorSwatch: true },
     68    {
     69      label: "--nested-with-function",
     70      postLabel: "color-mix(in srgb, rgb(255, 0, 0) 50%, blue)",
     71      hasColorSwatch: true,
     72    },
     73    { label: "--not-registered", postLabel: "blue", hasColorSwatch: true },
     74  ];
     75  await checkNewPropertyCssVariableAutocomplete(view, topLevelVariables);
     76 
     77  await checkCssVariableAutocomplete(
     78    view,
     79    getTextProperty(view, 1, { color: "gold" }).editor.valueSpan,
     80    topLevelVariables
     81  );
     82 
     83  info(
     84    "Check that the list is correct when selecting a node from another document"
     85  );
     86  await selectNodeInFrames(["iframe", "h1"], inspector);
     87 
     88  const iframeVariables = [
     89    { label: "--iframe" },
     90    {
     91      label: "--iframe-not-registered",
     92      postLabel: "turquoise",
     93      hasColorSwatch: true,
     94    },
     95  ];
     96  await checkNewPropertyCssVariableAutocomplete(view, iframeVariables);
     97 
     98  await checkCssVariableAutocomplete(
     99    view,
    100    getTextProperty(view, 1, { color: "tomato" }).editor.valueSpan,
    101    iframeVariables
    102  );
    103 });
    104 
    105 async function checkNewPropertyCssVariableAutocomplete(
    106  view,
    107  expectedPopupItems
    108 ) {
    109  const ruleEditor = getRuleViewRuleEditor(view, 1);
    110  const editor = await focusNewRuleViewProperty(ruleEditor);
    111  const onPopupOpen = editor.popup.once("popup-opened");
    112  EventUtils.sendString("--");
    113  await onPopupOpen;
    114 
    115  assertEditorPopupItems(
    116    editor,
    117    // we don't display postLabel for the new property
    118    expectedPopupItems.map(item => ({ label: item.label }))
    119  );
    120 
    121  info("Close the popup");
    122  const onPopupClosed = once(editor.popup, "popup-closed");
    123  EventUtils.synthesizeKey("VK_ESCAPE", {}, view.styleWindow);
    124  await onPopupClosed;
    125 
    126  info("Close the editor");
    127  EventUtils.synthesizeKey("VK_ESCAPE", {}, view.styleWindow);
    128 }
    129 
    130 async function checkCssVariableAutocomplete(
    131  view,
    132  inplaceEditorEl,
    133  expectedPopupItems
    134 ) {
    135  const editor = await focusEditableField(view, inplaceEditorEl);
    136  await wait(500);
    137 
    138  const onCloseParenthesisAppended = editor.once("after-suggest");
    139  EventUtils.sendString("var(");
    140  await onCloseParenthesisAppended;
    141 
    142  let onRuleViewChanged = view.once("ruleview-changed");
    143  EventUtils.sendString("--");
    144  const onPopupOpen = editor.popup.once("popup-opened");
    145  view.debounce.flush();
    146  await onPopupOpen;
    147  assertEditorPopupItems(editor, expectedPopupItems);
    148  await onRuleViewChanged;
    149 
    150  info("Close the popup");
    151  const onPopupClosed = once(editor.popup, "popup-closed");
    152  EventUtils.synthesizeKey("VK_ESCAPE", {}, view.styleWindow);
    153  await onPopupClosed;
    154 
    155  info("Cancel");
    156  onRuleViewChanged = view.once("ruleview-changed");
    157  EventUtils.synthesizeKey("VK_ESCAPE", {}, view.styleWindow);
    158  await onRuleViewChanged;
    159 
    160  view.debounce.flush();
    161 }
    162 
    163 /**
    164 * Check that the popup items are the expected ones.
    165 *
    166 * @param {InplaceEditor} editor
    167 * @param {Array{Object}} expectedPopupItems
    168 */
    169 function assertEditorPopupItems(editor, expectedPopupItems) {
    170  const popupListItems = Array.from(editor.popup.list.querySelectorAll("li"));
    171  is(
    172    popupListItems.length,
    173    expectedPopupItems.length,
    174    "Popup has expected number of items"
    175  );
    176  popupListItems.forEach((li, i) => {
    177    const expected = expectedPopupItems[i];
    178    const value =
    179      (li.querySelector(".initial-value")?.textContent ?? "") +
    180      li.querySelector(".autocomplete-value").textContent;
    181    is(value, expected.label, `Popup item #${i} as expected label`);
    182 
    183    // Don't pollute test logs if we don't have the expected variable
    184    if (value !== expected.label) {
    185      return;
    186    }
    187 
    188    const postLabelEl = li.querySelector(".autocomplete-postlabel");
    189    is(
    190      li.querySelector(".autocomplete-postlabel")?.textContent,
    191      expected.postLabel,
    192      `${expected.label} has expected post label`
    193    );
    194    is(
    195      !!postLabelEl?.querySelector(".autocomplete-swatch"),
    196      !!expected.hasColorSwatch,
    197      `${expected.label} ${
    198        expected.hasColorSwatch ? "has" : "does not have"
    199      } a post label color swatch`
    200    );
    201  });
    202 }