tor-browser

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

browser_stylesheet_change_events.js (7492B)


      1 const gTestRoot = getRootDirectory(gTestPath).replace(
      2  "chrome://mochitests/content/",
      3  "http://127.0.0.1:8888/"
      4 );
      5 
      6 add_task(async function test() {
      7  await BrowserTestUtils.withNewTab(
      8    { gBrowser, url: gTestRoot + "file_stylesheet_change_events.html" },
      9    async function (browser) {
     10      await SpecialPowers.spawn(
     11        browser,
     12        [gTestRoot],
     13        testApplicableStateChangeEvent
     14      );
     15    }
     16  );
     17 });
     18 
     19 // This function runs entirely in the content process. It doesn't have access
     20 // any free variables in this file.
     21 async function testApplicableStateChangeEvent(testRoot) {
     22  // We've seen the original stylesheet in the document.
     23  // Now add a stylesheet on the fly and make sure we see it.
     24  let doc = content.document;
     25  doc.styleSheetChangeEventsEnabled = true;
     26 
     27  const unexpectedContentEvent = event =>
     28    ok(false, "Received a " + event.type + " event on content");
     29  doc.addEventListener(
     30    "StyleSheetApplicableStateChanged",
     31    unexpectedContentEvent
     32  );
     33  doc.defaultView.addEventListener(
     34    "StyleSheetApplicableStateChanged",
     35    unexpectedContentEvent
     36  );
     37  doc.addEventListener("StyleSheetRemoved", unexpectedContentEvent);
     38  doc.defaultView.addEventListener("StyleSheetRemoved", unexpectedContentEvent);
     39 
     40  function shouldIgnoreEvent(e) {
     41    // accessiblecaret.css might be reported, interfering with the test
     42    // assertions, so let's ignore it
     43    return (
     44      e.stylesheet?.href === "resource://gre-resources/accessiblecaret.css"
     45    );
     46  }
     47 
     48  function waitForStyleApplicableStateChanged() {
     49    return ContentTaskUtils.waitForEvent(
     50      docShell.chromeEventHandler,
     51      "StyleSheetApplicableStateChanged",
     52      true,
     53      e => !shouldIgnoreEvent(e)
     54    );
     55  }
     56 
     57  function waitForStyleSheetRemovedEvent() {
     58    return ContentTaskUtils.waitForEvent(
     59      docShell.chromeEventHandler,
     60      "StyleSheetRemoved",
     61      true,
     62      e => !shouldIgnoreEvent(e)
     63    );
     64  }
     65 
     66  function checkApplicableStateChangeEvent(event, { applicable, stylesheet }) {
     67    is(
     68      event.type,
     69      "StyleSheetApplicableStateChanged",
     70      "event.type has expected value"
     71    );
     72    is(event.target, doc, "event targets correct document");
     73    is(event.stylesheet, stylesheet, "event.stylesheet has the expected value");
     74    is(event.applicable, applicable, "event.applicable has the expected value");
     75  }
     76 
     77  function checkStyleSheetRemovedEvent(event, { stylesheet }) {
     78    is(event.type, "StyleSheetRemoved", "event.type has expected value");
     79    is(event.target, doc, "event targets correct document");
     80    is(event.stylesheet, stylesheet, "event.stylesheet has the expected value");
     81  }
     82 
     83  // Updating the text content will actually create a new StyleSheet instance,
     84  // and so we should get one event for the new instance, and another one for
     85  // the removal of the "previous"one.
     86  function waitForTextContentChange() {
     87    return Promise.all([
     88      waitForStyleSheetRemovedEvent(),
     89      waitForStyleApplicableStateChanged(),
     90    ]);
     91  }
     92 
     93  let stateChanged, evt;
     94 
     95  {
     96    const gStyleSheet = "stylesheet_change_events.css";
     97 
     98    info("Add <link> and wait for applicable state change event");
     99    let linkEl = doc.createElement("link");
    100    linkEl.setAttribute("rel", "stylesheet");
    101    linkEl.setAttribute("type", "text/css");
    102    linkEl.setAttribute("href", testRoot + gStyleSheet);
    103 
    104    stateChanged = waitForStyleApplicableStateChanged();
    105    doc.body.appendChild(linkEl);
    106    evt = await stateChanged;
    107 
    108    ok(true, "received dynamic style sheet applicable state change event");
    109    checkApplicableStateChangeEvent(evt, {
    110      stylesheet: linkEl.sheet,
    111      applicable: true,
    112    });
    113 
    114    stateChanged = waitForStyleApplicableStateChanged();
    115    linkEl.sheet.disabled = true;
    116    evt = await stateChanged;
    117 
    118    ok(true, "received dynamic style sheet applicable state change event");
    119    checkApplicableStateChangeEvent(evt, {
    120      stylesheet: linkEl.sheet,
    121      applicable: false,
    122    });
    123 
    124    info("Remove stylesheet and wait for removed event");
    125    const removedStylesheet = linkEl.sheet;
    126    const onStyleSheetRemoved = waitForStyleSheetRemovedEvent();
    127    doc.body.removeChild(linkEl);
    128    const removedStyleSheetEvt = await onStyleSheetRemoved;
    129 
    130    ok(true, "received removed sheet event");
    131    checkStyleSheetRemovedEvent(removedStyleSheetEvt, {
    132      stylesheet: removedStylesheet,
    133    });
    134  }
    135 
    136  {
    137    info("Add <style> node and wait for applicable state changed event");
    138    let styleEl = doc.createElement("style");
    139    styleEl.textContent = `body { background: tomato; }`;
    140 
    141    stateChanged = waitForStyleApplicableStateChanged();
    142    doc.head.appendChild(styleEl);
    143    evt = await stateChanged;
    144 
    145    ok(true, "received dynamic style sheet applicable state change event");
    146    checkApplicableStateChangeEvent(evt, {
    147      stylesheet: styleEl.sheet,
    148      applicable: true,
    149    });
    150 
    151    info("Updating <style> text content");
    152    stateChanged = waitForTextContentChange();
    153    const inlineStyleSheetBeforeChange = styleEl.sheet;
    154 
    155    styleEl.textContent = `body { background: gold; }`;
    156    const [inlineRemovedEvt, inlineAddedEvt] = await stateChanged;
    157 
    158    ok(true, "received expected style sheet events");
    159    checkStyleSheetRemovedEvent(inlineRemovedEvt, {
    160      stylesheet: inlineStyleSheetBeforeChange,
    161    });
    162    checkApplicableStateChangeEvent(inlineAddedEvt, {
    163      stylesheet: styleEl.sheet,
    164      applicable: true,
    165    });
    166 
    167    info("Remove stylesheet and wait for removed event");
    168    const onStyleSheetRemoved = waitForStyleSheetRemovedEvent();
    169 
    170    const removedInlineStylesheet = styleEl.sheet;
    171    styleEl.remove();
    172    const removedStyleSheetEvt = await onStyleSheetRemoved;
    173 
    174    ok(true, "received removed style sheet event");
    175    checkStyleSheetRemovedEvent(removedStyleSheetEvt, {
    176      stylesheet: removedInlineStylesheet,
    177    });
    178  }
    179 
    180  {
    181    info(
    182      "Create a custom element and check we get an event for its stylesheet"
    183    );
    184    stateChanged = waitForStyleApplicableStateChanged();
    185    const el = doc.createElement("div");
    186    const shadowRoot = el.attachShadow({ mode: "open" });
    187    doc.body.appendChild(el);
    188    shadowRoot.innerHTML = `
    189        <span>custom</span>
    190        <style>
    191          span { color: salmon; }
    192        </style>`;
    193    evt = await stateChanged;
    194 
    195    ok(true, "received dynamic style sheet applicable state change event");
    196    const shadowStyleEl = shadowRoot.querySelector("style");
    197    checkApplicableStateChangeEvent(evt, {
    198      stylesheet: shadowStyleEl.sheet,
    199      applicable: true,
    200    });
    201 
    202    info("Updating <style> text content");
    203    stateChanged = waitForTextContentChange();
    204    const styleSheetBeforeChange = shadowStyleEl.sheet;
    205    shadowStyleEl.textContent = `span { color: cyan; }`;
    206    const [removedEvt, addedEvt] = await stateChanged;
    207 
    208    ok(true, "received expected style sheet events");
    209    checkStyleSheetRemovedEvent(removedEvt, {
    210      stylesheet: styleSheetBeforeChange,
    211    });
    212    checkApplicableStateChangeEvent(addedEvt, {
    213      stylesheet: shadowStyleEl.sheet,
    214      applicable: true,
    215    });
    216 
    217    info("Remove stylesheet and wait for removed event");
    218    const onStyleSheetRemoved = waitForStyleSheetRemovedEvent();
    219    const removedShadowStylesheet = shadowStyleEl.sheet;
    220    shadowStyleEl.remove();
    221    const removedStyleSheetEvt = await onStyleSheetRemoved;
    222    ok(true, "received removed style sheet event");
    223    checkStyleSheetRemovedEvent(removedStyleSheetEvt, {
    224      stylesheet: removedShadowStylesheet,
    225    });
    226  }
    227 }