Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

const gTestRoot = getRootDirectory(gTestPath).replace(
);
add_task(async function test() {
await BrowserTestUtils.withNewTab(
{ gBrowser, url: gTestRoot + "file_stylesheet_change_events.html" },
async function (browser) {
await SpecialPowers.spawn(
browser,
[gTestRoot],
testApplicableStateChangeEvent
);
}
);
});
// This function runs entirely in the content process. It doesn't have access
// any free variables in this file.
async function testApplicableStateChangeEvent(testRoot) {
// We've seen the original stylesheet in the document.
// Now add a stylesheet on the fly and make sure we see it.
let doc = content.document;
doc.styleSheetChangeEventsEnabled = true;
const unexpectedContentEvent = event =>
ok(false, "Received a " + event.type + " event on content");
doc.addEventListener(
"StyleSheetApplicableStateChanged",
unexpectedContentEvent
);
doc.defaultView.addEventListener(
"StyleSheetApplicableStateChanged",
unexpectedContentEvent
);
doc.addEventListener("StyleSheetRemoved", unexpectedContentEvent);
doc.defaultView.addEventListener("StyleSheetRemoved", unexpectedContentEvent);
function shouldIgnoreEvent(e) {
// accessiblecaret.css might be reported, interfering with the test
// assertions, so let's ignore it
return (
);
}
function waitForStyleApplicableStateChanged() {
return ContentTaskUtils.waitForEvent(
docShell.chromeEventHandler,
"StyleSheetApplicableStateChanged",
true,
e => !shouldIgnoreEvent(e)
);
}
function waitForStyleSheetRemovedEvent() {
return ContentTaskUtils.waitForEvent(
docShell.chromeEventHandler,
"StyleSheetRemoved",
true,
e => !shouldIgnoreEvent(e)
);
}
function checkApplicableStateChangeEvent(event, { applicable, stylesheet }) {
is(
event.type,
"StyleSheetApplicableStateChanged",
"event.type has expected value"
);
is(event.target, doc, "event targets correct document");
is(event.stylesheet, stylesheet, "event.stylesheet has the expected value");
is(event.applicable, applicable, "event.applicable has the expected value");
}
function checkStyleSheetRemovedEvent(event, { stylesheet }) {
is(event.type, "StyleSheetRemoved", "event.type has expected value");
is(event.target, doc, "event targets correct document");
is(event.stylesheet, stylesheet, "event.stylesheet has the expected value");
}
// Updating the text content will actually create a new StyleSheet instance,
// and so we should get one event for the new instance, and another one for
// the removal of the "previous"one.
function waitForTextContentChange() {
return Promise.all([
waitForStyleSheetRemovedEvent(),
waitForStyleApplicableStateChanged(),
]);
}
let stateChanged, evt;
{
const gStyleSheet = "stylesheet_change_events.css";
info("Add <link> and wait for applicable state change event");
let linkEl = doc.createElement("link");
linkEl.setAttribute("rel", "stylesheet");
linkEl.setAttribute("type", "text/css");
linkEl.setAttribute("href", testRoot + gStyleSheet);
stateChanged = waitForStyleApplicableStateChanged();
doc.body.appendChild(linkEl);
evt = await stateChanged;
ok(true, "received dynamic style sheet applicable state change event");
checkApplicableStateChangeEvent(evt, {
stylesheet: linkEl.sheet,
applicable: true,
});
stateChanged = waitForStyleApplicableStateChanged();
linkEl.sheet.disabled = true;
evt = await stateChanged;
ok(true, "received dynamic style sheet applicable state change event");
checkApplicableStateChangeEvent(evt, {
stylesheet: linkEl.sheet,
applicable: false,
});
info("Remove stylesheet and wait for removed event");
const removedStylesheet = linkEl.sheet;
const onStyleSheetRemoved = waitForStyleSheetRemovedEvent();
doc.body.removeChild(linkEl);
const removedStyleSheetEvt = await onStyleSheetRemoved;
ok(true, "received removed sheet event");
checkStyleSheetRemovedEvent(removedStyleSheetEvt, {
stylesheet: removedStylesheet,
});
}
{
info("Add <style> node and wait for applicable state changed event");
let styleEl = doc.createElement("style");
styleEl.textContent = `body { background: tomato; }`;
stateChanged = waitForStyleApplicableStateChanged();
doc.head.appendChild(styleEl);
evt = await stateChanged;
ok(true, "received dynamic style sheet applicable state change event");
checkApplicableStateChangeEvent(evt, {
stylesheet: styleEl.sheet,
applicable: true,
});
info("Updating <style> text content");
stateChanged = waitForTextContentChange();
const inlineStyleSheetBeforeChange = styleEl.sheet;
styleEl.textContent = `body { background: gold; }`;
const [inlineRemovedEvt, inlineAddedEvt] = await stateChanged;
ok(true, "received expected style sheet events");
checkStyleSheetRemovedEvent(inlineRemovedEvt, {
stylesheet: inlineStyleSheetBeforeChange,
});
checkApplicableStateChangeEvent(inlineAddedEvt, {
stylesheet: styleEl.sheet,
applicable: true,
});
info("Remove stylesheet and wait for removed event");
const onStyleSheetRemoved = waitForStyleSheetRemovedEvent();
const removedInlineStylesheet = styleEl.sheet;
styleEl.remove();
const removedStyleSheetEvt = await onStyleSheetRemoved;
ok(true, "received removed style sheet event");
checkStyleSheetRemovedEvent(removedStyleSheetEvt, {
stylesheet: removedInlineStylesheet,
});
}
{
info(
"Create a custom element and check we get an event for its stylesheet"
);
stateChanged = waitForStyleApplicableStateChanged();
const el = doc.createElement("div");
const shadowRoot = el.attachShadow({ mode: "open" });
doc.body.appendChild(el);
shadowRoot.innerHTML = `
<span>custom</span>
<style>
span { color: salmon; }
</style>`;
evt = await stateChanged;
ok(true, "received dynamic style sheet applicable state change event");
const shadowStyleEl = shadowRoot.querySelector("style");
checkApplicableStateChangeEvent(evt, {
stylesheet: shadowStyleEl.sheet,
applicable: true,
});
info("Updating <style> text content");
stateChanged = waitForTextContentChange();
const styleSheetBeforeChange = shadowStyleEl.sheet;
shadowStyleEl.textContent = `span { color: cyan; }`;
const [removedEvt, addedEvt] = await stateChanged;
ok(true, "received expected style sheet events");
checkStyleSheetRemovedEvent(removedEvt, {
stylesheet: styleSheetBeforeChange,
});
checkApplicableStateChangeEvent(addedEvt, {
stylesheet: shadowStyleEl.sheet,
applicable: true,
});
info("Remove stylesheet and wait for removed event");
const onStyleSheetRemoved = waitForStyleSheetRemovedEvent();
const removedShadowStylesheet = shadowStyleEl.sheet;
shadowStyleEl.remove();
const removedStyleSheetEvt = await onStyleSheetRemoved;
ok(true, "received removed style sheet event");
checkStyleSheetRemovedEvent(removedStyleSheetEvt, {
stylesheet: removedShadowStylesheet,
});
}
}