/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test the ResourceCommand API around STYLESHEET and navigation (reloading, creation of new browsing context, …)
const ORG_DOC_BUILDER = "https://example.org/document-builder.sjs";
const COM_DOC_BUILDER = "https://example.com/document-builder.sjs";
// Since the order of resources is not guaranteed, we put a number in the title attribute
// of the ` +
`` +
`` +
`` +
``;
const COOP_HEADERS = "Cross-Origin-Opener-Policy:same-origin";
const TEST_URI_NEW_BROWSING_CONTEXT =
`${ORG_DOC_BUILDER}?headers=${COOP_HEADERS}` +
`&html=
top-level example.org` +
``;
add_task(async function () {
info(
"Open a new tab and check that styleSheetChangeEventsEnabled is false by default"
);
const tab = await addTab(TEST_URI);
is(
await getDocumentStyleSheetChangeEventsEnabled(tab.linkedBrowser),
false,
`styleSheetChangeEventsEnabled is false at the beginning`
);
const { client, resourceCommand, targetCommand } = await initResourceCommand(
tab
);
let availableResources = [];
await resourceCommand.watchResources([resourceCommand.TYPES.STYLESHEET], {
onAvailable: resources => {
availableResources.push(...resources);
},
});
info("Wait for all the stylesheets resources of main document and iframes");
await waitFor(() => availableResources.length === 5);
is(availableResources.length, 5, "Retrieved the expected stylesheets");
// the order of the resources is not guaranteed.
sortResourcesByExpectedOrder(availableResources);
await assertResource(availableResources[0], {
styleText: `.top-level-org{}`,
});
await assertResource(availableResources[1], {
styleText: `.frame-org-1{}`,
});
await assertResource(availableResources[2], {
styleText: `.frame-org-2{}`,
});
await assertResource(availableResources[3], {
styleText: `.frame-com-1{}`,
});
await assertResource(availableResources[4], {
styleText: `.frame-com-2{}`,
});
// clear availableResources so it's easier to test
availableResources = [];
is(
await getDocumentStyleSheetChangeEventsEnabled(tab.linkedBrowser),
true,
`styleSheetChangeEventsEnabled is true after watching stylesheets`
);
info("Navigate a remote frame to a different page");
const iframeNewUrl =
`https://example.com/document-builder.sjs?` +
`html=example.com new bc
`;
await SpecialPowers.spawn(tab.linkedBrowser, [iframeNewUrl], url => {
const { browsingContext } =
content.document.querySelector("#remote-origin-2");
return SpecialPowers.spawn(browsingContext, [url], innerUrl => {
content.document.location = innerUrl;
});
});
await waitFor(() => availableResources.length == 1);
ok(true, "We're notified about the iframe new document stylesheet");
await assertResource(availableResources[0], {
styleText: `.frame-com-new-bc{}`,
});
const iframeNewBrowsingContext = await SpecialPowers.spawn(
tab.linkedBrowser,
[],
() => content.document.querySelector("#remote-origin-2").browsingContext
);
is(
await getDocumentStyleSheetChangeEventsEnabled(iframeNewBrowsingContext),
true,
`styleSheetChangeEventsEnabled is still true after navigating the iframe`
);
// clear availableResources so it's easier to test
availableResources = [];
info("Check that styleSheetChangeEventsEnabled persist after reloading");
await reloadBrowser();
// ⚠️ When EFT is disabled, we're only getting the stylesheets for the top-level document
// and the remote frames; the same-origin iframes stylesheets are missing.
const expectedStylesheetResources = isEveryFrameTargetEnabled() ? 5 : 3;
info(
"Wait until we're notified about all the stylesheets (top-level document + iframe)"
);
await waitFor(
() => availableResources.length === expectedStylesheetResources
);
is(
availableResources.length,
expectedStylesheetResources,
"Retrieved the expected stylesheets after the page was reloaded"
);
// the order of the resources is not guaranteed.
sortResourcesByExpectedOrder(availableResources);
await assertResource(availableResources[0], {
styleText: `.top-level-org{}`,
});
if (isEveryFrameTargetEnabled()) {
await assertResource(availableResources[1], {
styleText: `.frame-org-1{}`,
});
await assertResource(availableResources[2], {
styleText: `.frame-org-2{}`,
});
await assertResource(availableResources[3], {
styleText: `.frame-com-1{}`,
});
await assertResource(availableResources[4], {
styleText: `.frame-com-new-bc{}`,
});
} else {
await assertResource(availableResources[1], {
styleText: `.frame-com-1{}`,
});
await assertResource(availableResources[2], {
styleText: `.frame-com-new-bc{}`,
});
}
is(
await getDocumentStyleSheetChangeEventsEnabled(tab.linkedBrowser),
true,
`styleSheetChangeEventsEnabled is still true on the top level document after reloading`
);
if (isEveryFrameTargetEnabled()) {
const bc = await SpecialPowers.spawn(
tab.linkedBrowser,
[],
() => content.document.querySelector("#same-origin-1").browsingContext
);
is(
await getDocumentStyleSheetChangeEventsEnabled(bc),
true,
`styleSheetChangeEventsEnabled is still true on the iframe after reloading`
);
}
// clear availableResources so it's easier to test
availableResources = [];
info(
"Check that styleSheetChangeEventsEnabled persist when navigating to a page that creates a new browsing context"
);
const previousBrowsingContextId = tab.linkedBrowser.browsingContext.id;
const onLoaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
await BrowserTestUtils.loadURIString(
tab.linkedBrowser,
TEST_URI_NEW_BROWSING_CONTEXT
);
await onLoaded;
isnot(
tab.linkedBrowser.browsingContext.id,
previousBrowsingContextId,
"A new browsing context was created"
);
info("Wait to get the stylesheet for the new document");
await waitFor(() => availableResources.length === 1);
ok(true, "We received the stylesheet for the new document");
await assertResource(availableResources[0], {
styleText: `.top-level-org-new-bc{}`,
});
is(
await getDocumentStyleSheetChangeEventsEnabled(tab.linkedBrowser),
true,
`styleSheetChangeEventsEnabled is still true after navigating to a new browsing context`
);
targetCommand.destroy();
await client.close();
});
/**
* Returns the value of the browser/browsingContext document `styleSheetChangeEventsEnabled`
* property.
*
* @param {Browser|BrowsingContext} browserOrBrowsingContext: The browser element or a
* browsing context.
* @returns {Promise}
*/
function getDocumentStyleSheetChangeEventsEnabled(browserOrBrowsingContext) {
return SpecialPowers.spawn(browserOrBrowsingContext, [], () => {
return content.document.styleSheetChangeEventsEnabled;
});
}
/**
* Sort the passed array of stylesheet resources.
*
* Since the order of resources are not guaranteed, the