tor-browser

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

browser_rules_select-and-copy-styles.js (7514B)


      1 /* Any copyright is dedicated to the Public Domain.
      2 http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 // Tests that properties can be selected and copied from the rule view
      7 
      8 const osString = Services.appinfo.OS;
      9 
     10 const TEST_URI = `
     11  <style type="text/css">
     12    html {
     13      color: #000000;
     14    }
     15    span {
     16      font-variant: small-caps; color: #000000;
     17    }
     18    .nomatches {
     19      color: #ff0000;
     20    }
     21 
     22    html {
     23      body {
     24        container-type: inline-size;
     25        @container (1px < width) {
     26          #nested {
     27            background: tomato;
     28            color: gold;
     29          }
     30        }
     31      }
     32    }
     33  </style>
     34  <div id="first" style="margin: 10em;
     35    font-size: 14pt; font-family: helvetica, sans-serif; color: #AAA">
     36    <h1>Some header text</h1>
     37    <p id="salutation" style="font-size: 12pt">hi.</p>
     38    <p id="body" style="font-size: 12pt">I am a test-case. This text exists
     39    solely to provide some things to <span style="color: yellow">
     40    highlight</span> and <span style="font-weight: bold">count</span>
     41    style list-items in the box at right. If you are reading this,
     42    you should go do something else instead. Maybe read a book. Or better
     43    yet, write some test-cases for another bit of code.
     44    <span style="font-style: italic">some text</span></p>
     45    <p id="closing">more text</p>
     46    <p>even more text</p>
     47  </div>
     48  <section id=nested>Nested</section>
     49 `;
     50 
     51 add_task(async function () {
     52  await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
     53  const { inspector, view } = await openRuleView();
     54  await selectNode("div", inspector);
     55  await checkCopySelection(view);
     56  await checkSelectAll(view);
     57  await checkCopyEditorValue(view);
     58 
     59  await selectNode("#nested", inspector);
     60  await checkCopyNestedRule(view);
     61 });
     62 
     63 async function checkCopySelection(view) {
     64  info("Testing selection copy");
     65 
     66  const contentDoc = view.styleDocument;
     67  const win = view.styleWindow;
     68  const prop = contentDoc.querySelector(".ruleview-property");
     69  const values = contentDoc.querySelectorAll(
     70    ".ruleview-propertyvaluecontainer"
     71  );
     72 
     73  let range = contentDoc.createRange();
     74  range.setStart(prop, 0);
     75  range.setEnd(values[4], 2);
     76  win.getSelection().addRange(range);
     77  info("Checking that _Copy() returns the correct clipboard value");
     78 
     79  const expectedPattern =
     80    "  margin: 10em;[\\r\\n]+" +
     81    "  font-size: 14pt;[\\r\\n]+" +
     82    "  font-family: helvetica, sans-serif;[\\r\\n]+" +
     83    "  color: #AAA;[\\r\\n]+" +
     84    "}[\\r\\n]+" +
     85    "html {[\\r\\n]+" +
     86    "  color: #000000;[\\r\\n]*";
     87 
     88  const allMenuItems = openStyleContextMenuAndGetAllItems(view, prop);
     89  const menuitemCopy = allMenuItems.find(
     90    item =>
     91      item.label ===
     92      STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copy")
     93  );
     94 
     95  ok(menuitemCopy.visible, "Copy menu item is displayed as expected");
     96 
     97  try {
     98    await waitForClipboardPromise(
     99      () => menuitemCopy.click(),
    100      () => checkClipboardData(expectedPattern)
    101    );
    102  } catch (e) {
    103    failedClipboard(expectedPattern);
    104  }
    105 
    106  info("Check copying from keyboard");
    107  win.getSelection().removeRange(range);
    108  // Selecting the declaration `margin: 10em;`
    109  range = contentDoc.createRange();
    110  range.setStart(prop, 0);
    111  range.setEnd(prop, 1);
    112  win.getSelection().addRange(range);
    113 
    114  // Dispatching the copy event from the checkbox to make sure we cover Bug 1680893.
    115  const declarationCheckbox = contentDoc.querySelector(
    116    "input[type=checkbox].ruleview-enableproperty"
    117  );
    118  const copyEvent = new win.Event("copy", { bubbles: true });
    119  await waitForClipboardPromise(
    120    () => declarationCheckbox.dispatchEvent(copyEvent),
    121    () => checkClipboardData("^  margin: 10em;$")
    122  );
    123  win.getSelection().removeRange(range);
    124 }
    125 
    126 async function checkSelectAll(view) {
    127  info("Testing select-all copy");
    128 
    129  const contentDoc = view.styleDocument;
    130  const prop = contentDoc.querySelector(".ruleview-property");
    131 
    132  info(
    133    "Checking that _SelectAll() then copy returns the correct " +
    134      "clipboard value"
    135  );
    136  view.contextMenu._onSelectAll();
    137  const expectedPattern =
    138    "element {[\\r\\n]+" +
    139    "  margin: 10em;[\\r\\n]+" +
    140    "  font-size: 14pt;[\\r\\n]+" +
    141    "  font-family: helvetica, sans-serif;[\\r\\n]+" +
    142    "  color: #AAA;[\\r\\n]+" +
    143    "}[\\r\\n]+" +
    144    "html {[\\r\\n]+" +
    145    "  color: #000000;[\\r\\n]+" +
    146    "}[\\r\\n]*";
    147 
    148  const allMenuItems = openStyleContextMenuAndGetAllItems(view, prop);
    149  const menuitemCopy = allMenuItems.find(
    150    item =>
    151      item.label ===
    152      STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copy")
    153  );
    154 
    155  ok(menuitemCopy.visible, "Copy menu item is displayed as expected");
    156 
    157  try {
    158    await waitForClipboardPromise(
    159      () => menuitemCopy.click(),
    160      () => checkClipboardData(expectedPattern)
    161    );
    162  } catch (e) {
    163    failedClipboard(expectedPattern);
    164  }
    165 }
    166 
    167 async function checkCopyEditorValue(view) {
    168  info("Testing CSS property editor value copy");
    169 
    170  const ruleEditor = getRuleViewRuleEditor(view, 0);
    171  const propEditor = ruleEditor.rule.textProps[0].editor;
    172 
    173  const editor = await focusEditableField(view, propEditor.valueSpan);
    174 
    175  info(
    176    "Checking that copying a css property value editor returns the correct" +
    177      " clipboard value"
    178  );
    179 
    180  const expectedPattern = "10em";
    181 
    182  const allMenuItems = openStyleContextMenuAndGetAllItems(view, editor.input);
    183  const menuitemCopy = allMenuItems.find(
    184    item =>
    185      item.label ===
    186      STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copy")
    187  );
    188 
    189  ok(menuitemCopy.visible, "Copy menu item is displayed as expected");
    190 
    191  try {
    192    await waitForClipboardPromise(
    193      () => menuitemCopy.click(),
    194      () => checkClipboardData(expectedPattern)
    195    );
    196  } catch (e) {
    197    failedClipboard(expectedPattern);
    198  }
    199 }
    200 
    201 async function checkCopyNestedRule(view) {
    202  info("Select nested rule");
    203  const doc = view.styleDocument;
    204  const range = doc.createRange();
    205  const nestedRule = doc.querySelector(".ruleview-rule:nth-of-type(2)");
    206  range.selectNode(nestedRule);
    207  const win = view.styleWindow;
    208  win.getSelection().addRange(range);
    209 
    210  const copyEvent = new win.Event("copy", { bubbles: true });
    211  const expectedNested = `html {
    212  & body {
    213    @container (1px < width) {
    214      & #nested {
    215        background: tomato;
    216        color: gold;
    217      }
    218    }
    219  }
    220 }
    221 `;
    222 
    223  await waitForClipboardPromise(
    224    () => nestedRule.dispatchEvent(copyEvent),
    225    expectedNested
    226  );
    227 }
    228 
    229 function checkClipboardData(expectedPattern) {
    230  const actual = SpecialPowers.getClipboardData("text/plain");
    231  const expectedRegExp = new RegExp(expectedPattern, "g");
    232  return expectedRegExp.test(actual);
    233 }
    234 
    235 function failedClipboard(expectedPattern) {
    236  // Format expected text for comparison
    237  const terminator = osString == "WINNT" ? "\r\n" : "\n";
    238  expectedPattern = expectedPattern.replace(/\[\\r\\n\][+*]/g, terminator);
    239  expectedPattern = expectedPattern.replace(/\\\(/g, "(");
    240  expectedPattern = expectedPattern.replace(/\\\)/g, ")");
    241 
    242  let actual = SpecialPowers.getClipboardData("text/plain");
    243 
    244  // Trim the right hand side of our strings. This is because expectedPattern
    245  // accounts for windows sometimes adding a newline to our copied data.
    246  expectedPattern = expectedPattern.trimRight();
    247  actual = actual.trimRight();
    248 
    249  dump(
    250    "TEST-UNEXPECTED-FAIL | Clipboard text does not match expected ... " +
    251      "results (escaped for accurate comparison):\n"
    252  );
    253  info("Actual: " + escape(actual));
    254  info("Expected: " + escape(expectedPattern));
    255 }