tor-browser

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

browser_dbg-pretty-print-inline-scripts.js (8633B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
      4 
      5 // Tests pretty-printing of HTML file and its inner scripts.
      6 
      7 "use strict";
      8 
      9 const TEST_FILENAME = `doc-pretty-print-inline-scripts.html`;
     10 const PRETTY_PRINTED_FILENAME = `${TEST_FILENAME}:formatted`;
     11 
     12 const BREAKABLE_LINE_HINT_CHAR = `➤`;
     13 
     14 requestLongerTimeout(2);
     15 
     16 // Import helpers for the inspector
     17 Services.scriptloader.loadSubScript(
     18  "chrome://mochitests/content/browser/devtools/client/inspector/test/shared-head.js",
     19  this
     20 );
     21 
     22 add_task(async function () {
     23  const dbg = await initDebugger(TEST_FILENAME);
     24 
     25  await selectSource(dbg, TEST_FILENAME);
     26  await togglePrettyPrint(dbg);
     27 
     28  const prettyPrintedSource = findSourceContent(dbg, PRETTY_PRINTED_FILENAME);
     29 
     30  ok(prettyPrintedSource, "Pretty-printed source exists");
     31 
     32  info("Check that the HTML file was pretty-printed as expected");
     33  const expectedPrettyHtml = getExpectedPrettyPrintedHtml();
     34  is(
     35    prettyPrintedSource.value,
     36    // ➤ is used to indicate breakable lines, we remove them to assert the actual
     37    // content of the editor.
     38    expectedPrettyHtml.replaceAll(BREAKABLE_LINE_HINT_CHAR, ""),
     39    "HTML file is pretty printed as expected"
     40  );
     41 
     42  info("Check breakable lines");
     43  const htmlLines = expectedPrettyHtml.split("\n");
     44  const expectedBreakableLines = [];
     45  htmlLines.forEach((line, index) => {
     46    if (line.startsWith(BREAKABLE_LINE_HINT_CHAR)) {
     47      // Lines in the debugger are 1-based
     48      expectedBreakableLines.push(index + 1);
     49    }
     50  });
     51 
     52  await assertBreakableLines(
     53    dbg,
     54    PRETTY_PRINTED_FILENAME,
     55    htmlLines.length,
     56    expectedBreakableLines
     57  );
     58 
     59  info("Check that console messages are pointing to pretty-printed file");
     60  const { toolbox } = dbg;
     61  await toolbox.selectTool("webconsole");
     62 
     63  const { hud } = await dbg.toolbox.getPanel("webconsole");
     64  info("Wait until messages are sourcemapped");
     65  await waitFor(
     66    () => !!findMessageByType(hud, PRETTY_PRINTED_FILENAME, ".log")
     67  );
     68 
     69  const firstMessage = await waitFor(() =>
     70    findMessageByType(hud, "User information", ".log")
     71  );
     72  const firstMessageLink = firstMessage.querySelector(".frame-link-source");
     73  is(
     74    firstMessageLink.innerText,
     75    `${PRETTY_PRINTED_FILENAME}:18:9`,
     76    "first console message has expected location"
     77  );
     78  info(
     79    "Click on the link of the first message to open the file in the debugger"
     80  );
     81  firstMessageLink.click();
     82  await waitForSelectedSource(dbg, PRETTY_PRINTED_FILENAME);
     83  ok(true, "pretty printed file was selected in debugger…");
     84  await waitForSelectedLocation(dbg, 18);
     85  ok(true, "…at the expected location");
     86 
     87  info("Go back to the console to check an other message");
     88  await toolbox.selectTool("webconsole");
     89 
     90  const secondMessage = await waitFor(() =>
     91    findMessageByType(hud, "42 yay", ".log")
     92  );
     93  const secondMessageLink = secondMessage.querySelector(".frame-link-source");
     94  is(
     95    secondMessageLink.innerText,
     96    `${PRETTY_PRINTED_FILENAME}:41:13`,
     97    "second console message has expected location"
     98  );
     99  info(
    100    "Click on the link of the second message to open the file in the debugger"
    101  );
    102  secondMessageLink.click();
    103  await waitForSelectedSource(dbg, PRETTY_PRINTED_FILENAME);
    104  ok(true, "pretty printed file was selected in debugger…");
    105  await waitForSelectedLocation(dbg, 41);
    106  ok(true, "…at the expected location");
    107 
    108  info("Check that event listener popup is pointing to pretty-printed file");
    109  const inspector = await toolbox.selectTool("inspector");
    110 
    111  const nodeFront = await getNodeFront("h1", inspector);
    112  const markupContainer = inspector.markup.getContainer(nodeFront);
    113  const evHolder = markupContainer.elt.querySelector(
    114    ".inspector-badge.interactive[data-event]"
    115  );
    116  const eventTooltip = inspector.markup.eventDetailsTooltip;
    117 
    118  info("Open event tooltip.");
    119  EventUtils.synthesizeMouseAtCenter(
    120    evHolder,
    121    {},
    122    inspector.markup.doc.defaultView
    123  );
    124  await eventTooltip.once("shown");
    125  ok(true, "event tooltip opened");
    126  // wait for filename to be sourcemapped
    127  await waitFor(() =>
    128    eventTooltip.panel
    129      .querySelector(".event-header")
    130      ?.innerText.includes(PRETTY_PRINTED_FILENAME)
    131  );
    132  const header = eventTooltip.panel.querySelector(".event-header");
    133  const headerFilename = header.querySelector(
    134    ".event-tooltip-filename"
    135  ).innerText;
    136  ok(
    137    headerFilename.endsWith(`${PRETTY_PRINTED_FILENAME}:51`),
    138    `Location in event tooltip is the pretty printed one (${headerFilename})`
    139  );
    140  info(
    141    "Check that clicking on open debugger icon selects the pretty printed file"
    142  );
    143  header.querySelector(".event-tooltip-debugger-icon").click();
    144  await waitForSelectedSource(dbg, PRETTY_PRINTED_FILENAME);
    145  ok(true, "pretty printed file was selected in debugger…");
    146  await waitForSelectedLocation(dbg, 51);
    147  ok(true, "…at the expected location");
    148 });
    149 
    150 add_task(async function prettyPrintSingleLineDataUrl() {
    151  const TEST_URL = `data:text/html,<meta charset=utf8><script>{"use strict"; globalThis.foo = function() {}}</script>`;
    152  const PRETTY_PRINTED_URL = `${TEST_URL}:formatted`;
    153  const dbg = await initDebuggerWithAbsoluteURL(TEST_URL);
    154 
    155  await selectSource(dbg, TEST_URL);
    156  await togglePrettyPrint(dbg);
    157 
    158  const prettyPrintedSource = findSourceContent(dbg, PRETTY_PRINTED_URL);
    159 
    160  ok(prettyPrintedSource, "Pretty-printed source exists");
    161 
    162  info("Check that the HTML file was pretty-printed as expected");
    163  const expectedPrettyHtml = `<meta charset=utf8><script>
    164 {
    165  'use strict';
    166  globalThis.foo = function () {
    167  }
    168 }
    169 </script>`;
    170  is(
    171    prettyPrintedSource.value,
    172    expectedPrettyHtml,
    173    "HTML file is pretty printed as expected"
    174  );
    175 });
    176 
    177 // Asserts that files which contain characters which are
    178 // encoded by two code units (surrogate pairs)
    179 add_task(async function prettyPrintHtmlWithSurrogatePairCharacters() {
    180  const TEST_URL = `doc-pretty-print-with-emojis.html`;
    181  const PRETTY_PRINTED_URL = `${TEST_URL}:formatted`;
    182  const dbg = await initDebugger(TEST_URL);
    183 
    184  await selectSource(dbg, TEST_URL);
    185  await togglePrettyPrint(dbg);
    186  const prettyPrintedSource = findSourceContent(dbg, PRETTY_PRINTED_URL);
    187  ok(prettyPrintedSource, "Pretty-printed source exists");
    188 
    189  info("Check that the HTML file was pretty-printed as expected");
    190  const expectedPrettyHtml =
    191    "<!DOCTYPE html><html><head><meta charset=\"utf-8\" /></head><body>\n🥁🤯<script>\nconsole.log('%', '🤯🤯🤯🤯🤯')\n</script>🥁</body>";
    192  is(
    193    prettyPrintedSource.value,
    194    expectedPrettyHtml,
    195    "HTML file is pretty printed as expected"
    196  );
    197 });
    198 
    199 /**
    200 * Return the expected pretty-printed HTML. Lines starting with ➤ indicate breakable
    201 * lines for easier maintenance.
    202 *
    203 * @returns {string}
    204 */
    205 function getExpectedPrettyPrintedHtml() {
    206  return `<!-- This Source Code Form is subject to the terms of the Mozilla Public
    207    - License, v. 2.0. If a copy of the MPL was not distributed with this
    208    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
    209 <!doctype html>
    210 
    211 <html>
    212  <head>
    213    <meta charset="utf-8"/>
    214    <title>Debugger test page</title>
    215    <!-- Added to check we don't handle non js scripts -->
    216    <script id="json-data" type="application/json">
    217      { "foo": "bar" }
    218    </script>
    219 
    220    <!-- the unusual formatting is wanted to check inline scripts pretty printing -->
    221    <script id="inline" type="application/javascript">
    222 ➤const userInfo = JSON.parse(document.getElementById('json-data').text);
    223 ➤console.log('User information: %o', userInfo);
    224 ➤document.addEventListener(
    225  'click',
    226  function onClick(e) {
    227 ➤    console.log('in inline script');
    228    // this is
    229    // something
    230 ➤    e.target;
    231 ➤  }
    232 );
    233 </script>
    234  </head>
    235 
    236  <body>
    237    <h1>Minified</h1>
    238    <!-- Checking that having empty inline scripts doesn't throw off pretty printing -->
    239    <script></script>
    240    <!-- Single line "minified" script -->
    241    <script id="minified" type="module">
    242 ➤for (const x of [
    243  42
    244 ]) {
    245 ➤  if (x > 0) {
    246 ➤    console.log(x, 'yay')
    247  } else {
    248 ➤    console.log(x, 'booh')
    249  }
    250 ➤}
    251 </script>
    252    <!-- Multiple line "minified" script, with content on the first line -->
    253    <script>
    254 {
    255  'use strict';
    256 ➤  document.querySelector('h1').addEventListener('mousedown', e => {
    257 ➤    console.log('mousedown on h1')
    258 ➤  })
    259 ➤}
    260 </script>
    261    <hr><script>
    262 ➤const x = {
    263  a: 1,
    264  b: 2
    265 ➤};
    266 </script><hr><!-- multiple scripts on same line --><script>
    267 ➤const y = [
    268  1,
    269  2,
    270  3
    271 ];
    272 ➤y.map(i => i * 2)
    273 </script>
    274  </body>
    275 </html>
    276 `;
    277 }