const gTestRoot = getRootDirectory(gTestPath).replace( "chrome://mochitests/content/", "http://127.0.0.1:8888/" ); 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 ( e.stylesheet?.href === "resource://content-accessible/accessiblecaret.css" ); } 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 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 `; 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