/* Any copyright is dedicated to the Public Domain. https://creativecommons.org/publicdomain/zero/1.0/ */ const BLANK_PAGE = "data:text/html;charset=utf-8,BlankBlank page"; /** @type {import("../../../../../netwerk/test/httpserver/httpd.sys.mjs")} */ const { HttpServer } = ChromeUtils.importESModule( "resource://testing-common/httpd.sys.mjs" ); /** * Use a tagged template literal to create a page extraction actor test. This spins * up an http server that serves the markup in a new tab. The page extractor can then * be used on the page. * * @param {TemplateStringsArray} strings - The literal string parts. * @param {...any} values - The interpolated expressions. */ async function html(strings, ...values) { // Convert the arguments into markup. let markup = ""; for (let i = 0; i < strings.length; i++) { markup += strings[i]; if (i < values.length) { markup += values[i]; } } markup = `${markup}`; const { url, serverClosed } = serveOnce(markup); const tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, url, true // waitForLoad ); const actor = tab.linkedBrowser.browsingContext.currentWindowGlobal.getActor( "PageExtractor" ); return { /** * @type {PageExtractorParent} */ actor, tab, /** * Get a new page extractor, which can change when navigating pages. * * @returns {PageExtractorParent} */ getPageExtractor() { return tab.linkedBrowser.browsingContext.currentWindowGlobal.getActor( "PageExtractor" ); }, async cleanup() { info("Cleaning up"); await serverClosed; BrowserTestUtils.removeTab(tab); }, }; } /** * Start an HTTP server that serves page.html with the provided HTML. * Explicitly encode the text as UTF-8 to correctly handle characters outside Latin-1, * which the HttpServer renders incorrectly by default. * * @param {string} html * @param {number} statusCode */ function serveOnce(html, statusCode = 200) { info("Create server"); const server = new HttpServer(); const { promise, resolve } = Promise.withResolvers(); const encoder = new TextEncoder(); const htmlUtf8 = encoder.encode(html); server.registerPathHandler("/page.html", (request, response) => { info("Request received for: " + url); response.setHeader("Content-Type", "text/html; charset=utf-8"); response.setStatusLine(request.httpVersion, statusCode); const binaryOutputStream = Cc[ "@mozilla.org/binaryoutputstream;1" ].createInstance(Ci.nsIBinaryOutputStream); binaryOutputStream.setOutputStream(response.bodyOutputStream); binaryOutputStream.writeByteArray(htmlUtf8); resolve(server.stop()); }); server.start(-1); let { primaryHost, primaryPort } = server.identity; // eslint-disable-next-line @microsoft/sdl/no-insecure-url const url = `http://${primaryHost}:${primaryPort}/page.html`; info("Server listening for: " + url); return { url, serverClosed: promise }; } /** * Click the reader-mode button if the reader-mode button is available. * Fails if the reader-mode button is hidden. */ async function toggleReaderMode() { const readerButton = document.getElementById("reader-mode-button"); await BrowserTestUtils.waitForMutationCondition( readerButton, { attributes: true, attributeFilter: ["hidden"] }, () => readerButton.hidden === false ); readerButton.getAttribute("readeractive") ? info("Exiting reader mode") : info("Entering reader mode"); const readyPromise = readerButton.getAttribute("readeractive") ? BrowserTestUtils.waitForMutationCondition( readerButton, { attributes: true, attributeFilter: ["readeractive"] }, () => !readerButton.getAttribute("readeractive") ) : BrowserTestUtils.waitForContentEvent( gBrowser.selectedBrowser, "AboutReaderContentReady" ); click(readerButton, "Clicking the reader-mode button"); await readyPromise; } function click(button, message) { info(message); if (button.hidden) { throw new Error("The button was hidden when trying to click it."); } button.click(); } /** * @param {string} file */ async function openSupportFile(file) { // Support files can be served up from example.com const url_prefix = "https://example.com/browser/"; const path_prefix = "toolkit/components/pageextractor/tests/browser/"; const url = url_prefix + path_prefix + file; // Start the tab at a blank page. const tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, BLANK_PAGE, true // waitForLoad ); BrowserTestUtils.startLoadingURIString(tab.linkedBrowser, url); await BrowserTestUtils.browserLoaded( tab.linkedBrowser, /* includeSubFrames */ false, url ); async function cleanup() { if (url.endsWith(".pdf")) { // Wait for the PDFViewerApplication to be closed before removing the // tab to avoid spurious errors and potential intermittents. await SpecialPowers.spawn(tab.linkedBrowser, [], async () => { const viewer = content.wrappedJSObject.PDFViewerApplication; await viewer.testingClose(); }); } BrowserTestUtils.removeTab(tab); } return { cleanup, /** * @returns {PageExtractorParent} */ getPageExtractor() { return tab.linkedBrowser.browsingContext.currentWindowGlobal.getActor( "PageExtractor" ); }, }; }