tor-browser

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

head.js (11191B)


      1 /* Any copyright is dedicated to the Public Domain.
      2 http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 // Import the inspector's head.js first (which itself imports shared-head.js).
      7 Services.scriptloader.loadSubScript(
      8  "chrome://mochitests/content/browser/devtools/client/inspector/test/head.js",
      9  this
     10 );
     11 
     12 /**
     13 * Dispatch the copy event on the given element
     14 */
     15 function fireCopyEvent(element) {
     16  const evt = element.ownerDocument.createEvent("Event");
     17  evt.initEvent("copy", true, true);
     18  element.dispatchEvent(evt);
     19 }
     20 
     21 /**
     22 * Return all the computed items in the computed view
     23 *
     24 * @param {CssComputedView} view
     25 *        The instance of the computed view panel
     26 * @returns {Array<Element>}
     27 */
     28 function getComputedViewProperties(view) {
     29  return Array.from(
     30    view.styleDocument.querySelectorAll(
     31      "#computed-container .computed-property-view"
     32    )
     33  );
     34 }
     35 
     36 /**
     37 * Get references to the name and value span nodes corresponding to a given
     38 * property name in the computed-view
     39 *
     40 * @param {CssComputedView} view
     41 *        The instance of the computed view panel
     42 * @param {string} name
     43 *        The name of the property to retrieve
     44 * @return an object {nameSpan, valueSpan}
     45 */
     46 function getComputedViewProperty(view, name) {
     47  let prop;
     48  for (const property of getComputedViewProperties(view)) {
     49    const nameSpan = property.querySelector(".computed-property-name");
     50    const valueSpan = property.querySelector(".computed-property-value");
     51 
     52    if (nameSpan.firstChild.textContent === name) {
     53      prop = { nameSpan, valueSpan };
     54      break;
     55    }
     56  }
     57  return prop;
     58 }
     59 
     60 /**
     61 * Get an instance of PropertyView from the computed-view.
     62 *
     63 * @param {CssComputedView} view
     64 *        The instance of the computed view panel
     65 * @param {string} name
     66 *        The name of the property to retrieve
     67 * @return {PropertyView}
     68 */
     69 function getComputedViewPropertyView(view, name) {
     70  return view.propertyViews.find(propertyView => propertyView.name === name);
     71 }
     72 
     73 /**
     74 * Get a reference to the matched rules element for a given property name in
     75 * the computed-view.
     76 * A matched rule element is inside the property element (<li>) itself
     77 * and is only shown when the twisty icon is expanded on the property.
     78 * It contains matched rules, with selectors, properties, values and stylesheet links.
     79 *
     80 * @param {CssComputedView} view
     81 *        The instance of the computed view panel
     82 * @param {string} name
     83 *        The name of the property to retrieve
     84 * @return {Promise} A promise that resolves to the property matched rules
     85 * container
     86 */
     87 var getComputedViewMatchedRules = async function (view, name) {
     88  let expander;
     89  let matchedRulesEl;
     90  for (const property of view.styleDocument.querySelectorAll(
     91    "#computed-container .computed-property-view"
     92  )) {
     93    const nameSpan = property.querySelector(".computed-property-name");
     94    if (nameSpan.firstChild.textContent === name) {
     95      expander = property.querySelector(".computed-expandable");
     96      matchedRulesEl = property.querySelector(".matchedselectors");
     97 
     98      break;
     99    }
    100  }
    101 
    102  if (!expander.hasAttribute("open")) {
    103    // Need to expand the property
    104    const onExpand = view.inspector.once("computed-view-property-expanded");
    105    expander.click();
    106    await onExpand;
    107 
    108    await waitFor(() => expander.hasAttribute("open"));
    109  }
    110 
    111  return matchedRulesEl;
    112 };
    113 
    114 /**
    115 * Get the text value of the property corresponding to a given name in the
    116 * computed-view
    117 *
    118 * @param {CssComputedView} view
    119 *        The instance of the computed view panel
    120 * @param {string} name
    121 *        The name of the property to retrieve
    122 * @return {string} The property value
    123 */
    124 function getComputedViewPropertyValue(view, name) {
    125  return getComputedViewProperty(view, name).valueSpan.textContent;
    126 }
    127 
    128 /**
    129 * Expand a given property, given its index in the current property list of
    130 * the computed view
    131 *
    132 * @param {CssComputedView} view
    133 *        The instance of the computed view panel
    134 * @param {number} index
    135 *        The index of the property to be expanded
    136 * @return a promise that resolves when the property has been expanded, or
    137 * rejects if the property was not found
    138 */
    139 function expandComputedViewPropertyByIndex(view, index) {
    140  info("Expanding property " + index + " in the computed view");
    141  const expandos = view.styleDocument.querySelectorAll(".computed-expandable");
    142  if (!expandos.length || !expandos[index]) {
    143    return Promise.reject();
    144  }
    145 
    146  const onExpand = view.inspector.once("computed-view-property-expanded");
    147  expandos[index].click();
    148  return onExpand;
    149 }
    150 
    151 /**
    152 * Get a rule-link from the computed-view given its index
    153 *
    154 * @param {CssComputedView} view
    155 *        The instance of the computed view panel
    156 * @param {number} index
    157 *        The index of the matched selector element
    158 * @return {DOMNode} The link at the given index, if one exists, null otherwise
    159 */
    160 function getComputedViewLinkByIndex(view, index) {
    161  const matchedSelectors = view.styleDocument.querySelectorAll(
    162    ".matchedselectors > p"
    163  );
    164  const matchedSelector = matchedSelectors[index];
    165  if (!matchedSelector) {
    166    return null;
    167  }
    168 
    169  return matchedSelector.querySelector(`.rule-link .computed-link`);
    170 }
    171 
    172 /**
    173 * Trigger the select all action in the computed view.
    174 *
    175 * @param {CssComputedView} view
    176 *        The instance of the computed view panel
    177 */
    178 function selectAllText(view) {
    179  info("Selecting all the text");
    180  view.contextMenu._onSelectAll();
    181 }
    182 
    183 /**
    184 * Select all the text, copy it, and check the content in the clipboard.
    185 *
    186 * @param {CssComputedView} view
    187 *        The instance of the computed view panel
    188 * @param {string} expectedPattern
    189 *        A regular expression used to check the content of the clipboard
    190 */
    191 async function copyAllAndCheckClipboard(view, expectedPattern) {
    192  selectAllText(view);
    193  const contentDoc = view.styleDocument;
    194  const prop = contentDoc.querySelector(
    195    "#computed-container .computed-property-view"
    196  );
    197 
    198  try {
    199    info("Trigger a copy event and wait for the clipboard content");
    200    await waitForClipboardPromise(
    201      () => fireCopyEvent(prop),
    202      () => checkClipboard(expectedPattern)
    203    );
    204  } catch (e) {
    205    failClipboardCheck(expectedPattern);
    206  }
    207 }
    208 
    209 /**
    210 * Select some text, copy it, and check the content in the clipboard.
    211 *
    212 * @param {CssComputedView} view
    213 *        The instance of the computed view panel
    214 * @param {object} positions
    215 *        The start and end positions of the text to be selected. This must be an object
    216 *        like this:
    217 *        { start: {prop: 1, offset: 0}, end: {prop: 3, offset: 5} }
    218 * @param {string} expectedPattern
    219 *        A regular expression used to check the content of the clipboard
    220 */
    221 async function copySomeTextAndCheckClipboard(view, positions, expectedPattern) {
    222  info("Testing selection copy");
    223 
    224  const contentDocument = view.styleDocument;
    225  const props = contentDocument.querySelectorAll(
    226    "#computed-container .computed-property-view"
    227  );
    228 
    229  info("Create the text selection range");
    230  const range = contentDocument.createRange();
    231  range.setStart(props[positions.start.prop], positions.start.offset);
    232  range.setEnd(props[positions.end.prop], positions.end.offset);
    233  contentDocument.defaultView.getSelection().addRange(range);
    234 
    235  try {
    236    info("Trigger a copy event and wait for the clipboard content");
    237    await waitForClipboardPromise(
    238      () => fireCopyEvent(props[0]),
    239      () => checkClipboard(expectedPattern)
    240    );
    241  } catch (e) {
    242    failClipboardCheck(expectedPattern);
    243  }
    244 }
    245 
    246 function checkClipboard(expectedPattern) {
    247  const actual = SpecialPowers.getClipboardData("text/plain");
    248  const expectedRegExp = new RegExp(expectedPattern, "g");
    249  return expectedRegExp.test(actual);
    250 }
    251 
    252 function failClipboardCheck(expectedPattern) {
    253  // Format expected text for comparison
    254  const terminator = Services.appinfo.OS == "WINNT" ? "\r\n" : "\n";
    255  expectedPattern = expectedPattern.replace(/\[\\r\\n\][+*]/g, terminator);
    256  expectedPattern = expectedPattern.replace(/\\\(/g, "(");
    257  expectedPattern = expectedPattern.replace(/\\\)/g, ")");
    258 
    259  let actual = SpecialPowers.getClipboardData("text/plain");
    260 
    261  // Trim the right hand side of our strings. This is because expectedPattern
    262  // accounts for windows sometimes adding a newline to our copied data.
    263  expectedPattern = expectedPattern.trimRight();
    264  actual = actual.trimRight();
    265 
    266  dump(
    267    "TEST-UNEXPECTED-FAIL | Clipboard text does not match expected ... " +
    268      "results (escaped for accurate comparison):\n"
    269  );
    270  info("Actual: " + escape(actual));
    271  info("Expected: " + escape(expectedPattern));
    272 }
    273 
    274 /**
    275 * Check that the given property has the expected value and the expected matched selectors
    276 *
    277 * @param {CssComputedView} view
    278 *        The instance of the computed view panel
    279 * @param {object} options
    280 * @param {string} options.property
    281 *        The property name to check
    282 * @param {string} options.expectedComputedValue
    283 *        The expected value displayed for the property
    284 * @param {object[]} options.expectedMatchedSelectors
    285 *        An array of objects describing the expected matched selectors
    286 * @param {string} options.expectedMatchedSelectors[].selector
    287 *        The selector that should be displayed at this index
    288 * @param {string} options.expectedMatchedSelectors[].value
    289 *        The value that should be displayed at this index
    290 * @param {boolean} options.expectedMatchedSelectors[].match
    291 *        Whether the selector should match the currently selected element. Defaults to true.
    292 */
    293 async function checkMatchedSelectorForProperty(
    294  view,
    295  { property, expectedComputedValue, expectedMatchedSelectors }
    296 ) {
    297  const propertyView = getComputedViewPropertyView(view, property);
    298  ok(propertyView, `found PropertyView for "${property}"`);
    299  const { valueNode } = propertyView;
    300  is(
    301    valueNode.textContent,
    302    expectedComputedValue,
    303    `Expected displayed computed value for "${property}"`
    304  );
    305 
    306  is(propertyView.hasMatchedSelectors, true, "hasMatchedSelectors is true");
    307 
    308  info("Expanding the matched selectors");
    309  propertyView.matchedExpanded = true;
    310  await propertyView.refreshMatchedSelectors();
    311 
    312  const selectorsEl =
    313    propertyView.matchedSelectorsContainer.querySelectorAll(".rule-text");
    314  is(
    315    selectorsEl.length,
    316    expectedMatchedSelectors.length,
    317    "Expected number of selectors are displayed"
    318  );
    319 
    320  selectorsEl.forEach((selectorEl, index) => {
    321    is(
    322      selectorEl.querySelector(".fix-get-selection").innerText,
    323      expectedMatchedSelectors[index].selector,
    324      `Selector #${index} is the expected one`
    325    );
    326    is(
    327      selectorEl.querySelector(".computed-other-property-value").innerText,
    328      expectedMatchedSelectors[index].value,
    329      `Selector #${index} ("${expectedMatchedSelectors[index].selector}") has the expected "${property}"`
    330    );
    331    const classToMatch = index === 0 ? "bestmatch" : "matched";
    332    const expectedMatch = expectedMatchedSelectors[index].match ?? true;
    333    is(
    334      selectorEl.classList.contains(classToMatch),
    335      expectedMatch,
    336      `Selector #${index} ("${expectedMatchedSelectors[index].selector}") element does ${expectedMatch ? "" : "not "}have a matching class`
    337    );
    338  });
    339 }