tor-browser

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

browser_markup_search_01.js (12051B)


      1 /* Any copyright is dedicated to the Public Domain.
      2 http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 // Test that searching for nodes using the selector-search input expands and
      7 // selects the right nodes in the markup-view, even when those nodes are deeply
      8 // nested (and therefore not attached yet when the markup-view is initialized).
      9 
     10 const TEST_URL = URL_ROOT + "doc_markup_search.html";
     11 const DEVTOOLS_SEARCH_HIGHLIGHT_NAME = "devtools-search";
     12 
     13 add_task(async function () {
     14  const { inspector } = await openInspectorForURL(TEST_URL);
     15 
     16  let container = await getContainerForSelector("em", inspector, true);
     17  ok(!container, "The <em> tag isn't present yet in the markup-view");
     18 
     19  // Searching for the innermost element first makes sure that the inspector
     20  // back-end is able to attach the resulting node to the tree it knows at the
     21  // moment. When the inspector is started, the <body> is the default selected
     22  // node, and only the parents up to the ROOT are known, and its direct
     23  // children.
     24  info("searching for the innermost child: <em>");
     25  await searchInMarkupView(inspector, "em");
     26 
     27  container = await getContainerForSelector("em", inspector);
     28  ok(container, "The <em> tag is now imported in the markup-view");
     29 
     30  let nodeFront = await getNodeFront("em", inspector);
     31  is(
     32    inspector.selection.nodeFront,
     33    nodeFront,
     34    "The <em> tag is the currently selected node"
     35  );
     36  ok(
     37    inspector.markup.win.CSS.highlights.has(DEVTOOLS_SEARCH_HIGHLIGHT_NAME),
     38    `"${DEVTOOLS_SEARCH_HIGHLIGHT_NAME}" CSS highlight does exist`
     39  );
     40 
     41  checkHighlightedSearchResults(inspector, [
     42    // Opening tag
     43    "em",
     44    // Closing tag
     45    "em",
     46  ]);
     47 
     48  info("searching for other nodes too");
     49  for (const node of ["span", "li", "ul"]) {
     50    await searchInMarkupView(inspector, node);
     51 
     52    nodeFront = await getNodeFront(node, inspector);
     53    is(
     54      inspector.selection.nodeFront,
     55      nodeFront,
     56      "The <" + node + "> tag is the currently selected node"
     57    );
     58    // We still get 2 Ranges on those items: even if only the opening tag is visible (because
     59    // the elements are expanded to show their children), a closing tag is actually
     60    // rendered and hidden in CSS.
     61    checkHighlightedSearchResults(inspector, [node, node]);
     62  }
     63 
     64  await searchInMarkupView(inspector, "BUTT");
     65  is(
     66    inspector.selection.nodeFront,
     67    await getNodeFront(".Buttons", inspector),
     68    "The section.Buttons element is selected"
     69  );
     70  // Selected node markup: <section class="Buttons">
     71  checkHighlightedSearchResults(inspector, ["Butt"]);
     72 
     73  await searchInMarkupView(inspector, "BUT");
     74  is(
     75    inspector.selection.nodeFront,
     76    await getNodeFront(".Buttons", inspector),
     77    "The section.Buttons element is selected"
     78  );
     79  checkHighlightedSearchResults(inspector, ["But"]);
     80 
     81  let onSearchResult = inspector.search.once("search-result");
     82  inspector.searchNextButton.click();
     83  info("Waiting for results");
     84  await onSearchResult;
     85 
     86  is(
     87    inspector.selection.nodeFront,
     88    await getNodeFront(`button[type="button"]`, inspector),
     89    `The button[type="button"] element is selected`
     90  );
     91  // Selected node markup: <button type="button" class="Button">OK</button>
     92  checkHighlightedSearchResults(inspector, [
     93    // opening tag (`<button`)
     94    "but",
     95    // class attribute (`class="Button"`)
     96    // Attributes are re-ordered in the markup view, that's wy this is coming before
     97    // the result for the type attribute.
     98    "But",
     99    // type attribute (`type="button"`)
    100    "but",
    101    // closing tag (`</button`)
    102    "but",
    103  ]);
    104 
    105  onSearchResult = inspector.search.once("search-result");
    106  inspector.searchNextButton.click();
    107  info("Waiting for results");
    108  await onSearchResult;
    109 
    110  is(
    111    inspector.selection.nodeFront,
    112    await getNodeFront(`section.Buttons > p`, inspector),
    113    `The p element is selected`
    114  );
    115  // Selected node markup: <p>Click the button</p>
    116  checkHighlightedSearchResults(inspector, ["but"]);
    117 
    118  const onSearchCleared = inspector.once("search-cleared");
    119  inspector.searchClearButton.click();
    120  info("Waiting for search to clear");
    121  await onSearchCleared;
    122 
    123  checkHighlightedSearchResults(inspector, []);
    124 
    125  await searchInMarkupView(inspector, "TALLTOPMATCH");
    126  const talltopNodeFront = await getNodeFront("section.talltop", inspector);
    127  const talltopNodeFrontChildren =
    128    await inspector.walker.children(talltopNodeFront);
    129  is(
    130    inspector.selection.nodeFront,
    131    talltopNodeFrontChildren.nodes[0],
    132    `The section.talltop text node is selected`
    133  );
    134  checkHighlightedSearchResults(inspector, ["TALLTOPMATCH"]);
    135 
    136  await searchInMarkupView(inspector, "TALLBOTTOMMATCH");
    137  const tallbottomNodeFront = await getNodeFront(
    138    "section.tallbottom",
    139    inspector
    140  );
    141  const tallbottomNodeFrontChildren =
    142    await inspector.walker.children(tallbottomNodeFront);
    143  is(
    144    inspector.selection.nodeFront,
    145    tallbottomNodeFrontChildren.nodes[0],
    146    `The section.tallbottom text node is selected`
    147  );
    148  checkHighlightedSearchResults(inspector, ["TALLBOTTOMMATCH"]);
    149 
    150  await searchInMarkupView(inspector, "OVERFLOWSMATCH");
    151  const overflowsNodeFront = await getNodeFront("section.overflows", inspector);
    152  const overflowsNodeFrontChildren =
    153    await inspector.walker.children(overflowsNodeFront);
    154  is(
    155    inspector.selection.nodeFront,
    156    overflowsNodeFrontChildren.nodes[0],
    157    "The section.overflows text node is selected"
    158  );
    159  checkHighlightedSearchResults(inspector, ["OVERFLOWSMATCH"]);
    160 
    161  info(
    162    "Check that matching node with non-visible search result are still being scrolled to"
    163  );
    164  // Scroll to top to make sure the node isn't in view at first
    165  const markupViewContainer = inspector.markup.win.document.documentElement;
    166  markupViewContainer.scrollTop = 0;
    167  markupViewContainer.scrollLeft = 0;
    168 
    169  const croppedAttributeContainer = await getContainerForSelector(
    170    "section#cropped-attribute",
    171    inspector
    172  );
    173  let croppedAttributeContainerRect =
    174    croppedAttributeContainer.elt.getBoundingClientRect();
    175 
    176  ok(
    177    croppedAttributeContainerRect.y < 0 ||
    178      croppedAttributeContainerRect.y > markupViewContainer.clientHeight,
    179    "section#cropped-attribute container is not into view before searching for a match in its attributes"
    180  );
    181 
    182  await searchInMarkupView(inspector, "croppedvalue");
    183  is(
    184    inspector.selection.nodeFront,
    185    await getNodeFront("section#cropped-attribute", inspector),
    186    "The section#cropped-attribute element is selected"
    187  );
    188  checkHighlightedSearchResults(inspector, []);
    189  // Check that node visible after it was selected
    190  croppedAttributeContainerRect =
    191    croppedAttributeContainer.elt.getBoundingClientRect();
    192 
    193  Assert.greaterOrEqual(
    194    croppedAttributeContainerRect.y,
    195    0,
    196    `Node with cropped attributes is not above visible viewport`
    197  );
    198  Assert.less(
    199    croppedAttributeContainerRect.y,
    200    markupViewContainer.clientHeight,
    201    `Node with cropped attributes is not below visible viewport`
    202  );
    203 
    204  // Sanity check to make sure the markup view does overflow in both axes. We need to
    205  // wait after the search is done as their text node is only revealed when cycling through
    206  // search results.
    207  Assert.greater(
    208    markupViewContainer.scrollHeight,
    209    markupViewContainer.clientHeight,
    210    "Markup view overflows vertically"
    211  );
    212  Assert.greater(
    213    markupViewContainer.scrollWidth,
    214    markupViewContainer.clientWidth,
    215    "Markup view overflows horizontally"
    216  );
    217 
    218  info("Search for pseudo elements");
    219 
    220  await searchInMarkupView(inspector, "::before");
    221  is(
    222    inspector.selection.nodeFront.displayName,
    223    "::before",
    224    "The ::before element is selected"
    225  );
    226  checkHighlightedSearchResults(inspector, ["::before"]);
    227 
    228  await searchInMarkupView(inspector, "::after");
    229  is(
    230    inspector.selection.nodeFront.displayName,
    231    "::after",
    232    "The ::after element is selected"
    233  );
    234  checkHighlightedSearchResults(inspector, ["::after"]);
    235 
    236  await searchInMarkupView(inspector, "::marker");
    237  is(
    238    inspector.selection.nodeFront.displayName,
    239    "::marker",
    240    "The ::marker element is selected"
    241  );
    242  checkHighlightedSearchResults(inspector, ["::marker"]);
    243 
    244  await searchInMarkupView(inspector, "::backdrop");
    245  is(
    246    inspector.selection.nodeFront.displayName,
    247    "::backdrop",
    248    "The ::backdrop element is selected"
    249  );
    250  checkHighlightedSearchResults(inspector, ["::backdrop"]);
    251 
    252  // Search by the `content` declaration of the ::before and ::after pseudo elements
    253  await searchInMarkupView(inspector, "my_before_text");
    254  is(
    255    inspector.selection.nodeFront.displayName,
    256    "::before",
    257    "The ::before element is selected"
    258  );
    259  // no highlighting as the `content` text isn't displayed in the markup view
    260  checkHighlightedSearchResults(inspector, []);
    261 
    262  await searchInMarkupView(inspector, "my_after_text");
    263  is(
    264    inspector.selection.nodeFront.displayName,
    265    "::after",
    266    "The ::after element is selected"
    267  );
    268  // no highlighting as the `content` text isn't displayed in the markup view
    269  checkHighlightedSearchResults(inspector, []);
    270 
    271  info("Search for view-transition pseudo elements");
    272  // Trigger the view transition
    273  const onMarkupMutation = inspector.once("markupmutation");
    274  await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async () => {
    275    const document = content.document;
    276    content.testTransition = document.startViewTransition(() => {
    277      document.querySelector(".pseudos").replaceChildren("updated");
    278    });
    279    await content.testTransition.ready;
    280    await content.testTransition.updateCallbackDone;
    281  });
    282  await onMarkupMutation;
    283 
    284  await searchInMarkupView(inspector, "::view-transition");
    285  is(
    286    inspector.selection.nodeFront.displayName,
    287    "::view-transition",
    288    "The ::view-transition element is selected"
    289  );
    290  checkHighlightedSearchResults(inspector, ["::view-transition"]);
    291 
    292  await searchInMarkupView(inspector, "::view-transition-old(root)");
    293  is(
    294    inspector.selection.nodeFront.displayName,
    295    "::view-transition-old(root)",
    296    "The ::view-transition-old(root) element is selected"
    297  );
    298  checkHighlightedSearchResults(inspector, ["::view-transition-old(root)"]);
    299 
    300  await searchInMarkupView(inspector, "::view-transition-new(custom)");
    301  is(
    302    inspector.selection.nodeFront.displayName,
    303    "::view-transition-new(custom)",
    304    "The ::view-transition-new(custom) element is selected"
    305  );
    306  checkHighlightedSearchResults(inspector, ["::view-transition-new(custom)"]);
    307 
    308  // Cancel transition
    309  await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async () => {
    310    content.testTransition.skipTransition();
    311    delete content.testTransition;
    312  });
    313 });
    314 
    315 function checkHighlightedSearchResults(inspector, expectedHighlights) {
    316  const searchInputValue = getMarkupViewSearchInput(inspector).value;
    317 
    318  info(`Checking highlights for "${searchInputValue}" search`);
    319  const devtoolsHighlights = [
    320    ...inspector.markup.win.CSS.highlights
    321      .get(DEVTOOLS_SEARCH_HIGHLIGHT_NAME)
    322      .values(),
    323  ];
    324  Assert.deepEqual(
    325    devtoolsHighlights.map(range => range.toString()),
    326    expectedHighlights,
    327    `Got expected highlights for "${searchInputValue}"`
    328  );
    329 
    330  if (expectedHighlights.length) {
    331    const markupViewContainer = inspector.markup.win.document.documentElement;
    332    info(
    333      `Check that we scrolled so the first highlighted range for "${searchInputValue}" is visible`
    334    );
    335    const [rect] = devtoolsHighlights[0].getClientRects();
    336    const { x, y } = rect;
    337 
    338    Assert.greaterOrEqual(
    339      y,
    340      0,
    341      `First "${searchInputValue}" match not above visible viewport`
    342    );
    343    Assert.less(
    344      y,
    345      markupViewContainer.clientHeight,
    346      `First "${searchInputValue}" match not below visible viewport`
    347    );
    348    Assert.greaterOrEqual(
    349      x,
    350      0,
    351      `First "${searchInputValue}" match not before the "left border" of the visible viewport`
    352    );
    353    Assert.less(
    354      x,
    355      markupViewContainer.clientWidth,
    356      `First "${searchInputValue}" match not after the "right border" of the visible viewport`
    357    );
    358  }
    359 }