From 36d22d82aa202bb199967e9512281e9a53db42c9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 21:33:14 +0200 Subject: Adding upstream version 115.7.0esr. Signed-off-by: Daniel Baumann --- toolkit/components/pdfjs/test/browser.ini | 59 ++++ .../pdfjs/test/browser_pdfjs_download_button.js | 125 +++++++ .../test/browser_pdfjs_editing_contextmenu.js | 373 +++++++++++++++++++++ .../pdfjs/test/browser_pdfjs_editing_telemetry.js | 96 ++++++ .../pdfjs/test/browser_pdfjs_fill_login.js | 108 ++++++ .../components/pdfjs/test/browser_pdfjs_filters.js | 81 +++++ .../components/pdfjs/test/browser_pdfjs_find.js | 137 ++++++++ .../test/browser_pdfjs_force_opening_files.js | 89 +++++ .../components/pdfjs/test/browser_pdfjs_form.js | 85 +++++ .../pdfjs/test/browser_pdfjs_fullscreen.js | 84 +++++ toolkit/components/pdfjs/test/browser_pdfjs_hcm.js | 104 ++++++ toolkit/components/pdfjs/test/browser_pdfjs_js.js | 49 +++ .../pdfjs/test/browser_pdfjs_load_telemetry.js | 51 +++ .../components/pdfjs/test/browser_pdfjs_main.js | 72 ++++ .../pdfjs/test/browser_pdfjs_minimum_font_size.js | 70 ++++ .../pdfjs/test/browser_pdfjs_navigation.js | 314 +++++++++++++++++ .../pdfjs/test/browser_pdfjs_nonpdf_filename.js | 73 ++++ .../pdfjs/test/browser_pdfjs_not_default.js | 26 ++ ...owser_pdfjs_notification_close_on_navigation.js | 43 +++ .../pdfjs/test/browser_pdfjs_octet_stream.js | 146 ++++++++ .../components/pdfjs/test/browser_pdfjs_saveas.js | 172 ++++++++++ .../pdfjs/test/browser_pdfjs_savedialog.js | 53 +++ .../browser_pdfjs_secondary_toolbar_telemetry.js | 67 ++++ .../components/pdfjs/test/browser_pdfjs_views.js | 106 ++++++ .../components/pdfjs/test/browser_pdfjs_zoom.js | 277 +++++++++++++++ .../pdfjs/test/file_pdf_download_link.html | 4 + toolkit/components/pdfjs/test/file_pdfjs_form.pdf | Bin 0 -> 8586 bytes toolkit/components/pdfjs/test/file_pdfjs_hcm.pdf | 24 ++ toolkit/components/pdfjs/test/file_pdfjs_js.pdf | Bin 0 -> 24153 bytes .../pdfjs/test/file_pdfjs_object_stream.pdf | Bin 0 -> 1568 bytes .../test/file_pdfjs_object_stream.pdf^headers^ | 2 + toolkit/components/pdfjs/test/file_pdfjs_test.pdf | Bin 0 -> 150611 bytes .../pdfjs/test/file_pdfjs_transfer_map.pdf | Bin 0 -> 62324 bytes toolkit/components/pdfjs/test/head.js | 357 ++++++++++++++++++++ toolkit/components/pdfjs/test/mochitest.ini | 4 + .../pdfjs/test/test_pdf_file_in_iframe.html | 50 +++ 36 files changed, 3301 insertions(+) create mode 100644 toolkit/components/pdfjs/test/browser.ini create mode 100644 toolkit/components/pdfjs/test/browser_pdfjs_download_button.js create mode 100644 toolkit/components/pdfjs/test/browser_pdfjs_editing_contextmenu.js create mode 100644 toolkit/components/pdfjs/test/browser_pdfjs_editing_telemetry.js create mode 100644 toolkit/components/pdfjs/test/browser_pdfjs_fill_login.js create mode 100644 toolkit/components/pdfjs/test/browser_pdfjs_filters.js create mode 100644 toolkit/components/pdfjs/test/browser_pdfjs_find.js create mode 100644 toolkit/components/pdfjs/test/browser_pdfjs_force_opening_files.js create mode 100644 toolkit/components/pdfjs/test/browser_pdfjs_form.js create mode 100644 toolkit/components/pdfjs/test/browser_pdfjs_fullscreen.js create mode 100644 toolkit/components/pdfjs/test/browser_pdfjs_hcm.js create mode 100644 toolkit/components/pdfjs/test/browser_pdfjs_js.js create mode 100644 toolkit/components/pdfjs/test/browser_pdfjs_load_telemetry.js create mode 100644 toolkit/components/pdfjs/test/browser_pdfjs_main.js create mode 100644 toolkit/components/pdfjs/test/browser_pdfjs_minimum_font_size.js create mode 100644 toolkit/components/pdfjs/test/browser_pdfjs_navigation.js create mode 100644 toolkit/components/pdfjs/test/browser_pdfjs_nonpdf_filename.js create mode 100644 toolkit/components/pdfjs/test/browser_pdfjs_not_default.js create mode 100644 toolkit/components/pdfjs/test/browser_pdfjs_notification_close_on_navigation.js create mode 100644 toolkit/components/pdfjs/test/browser_pdfjs_octet_stream.js create mode 100644 toolkit/components/pdfjs/test/browser_pdfjs_saveas.js create mode 100644 toolkit/components/pdfjs/test/browser_pdfjs_savedialog.js create mode 100644 toolkit/components/pdfjs/test/browser_pdfjs_secondary_toolbar_telemetry.js create mode 100644 toolkit/components/pdfjs/test/browser_pdfjs_views.js create mode 100644 toolkit/components/pdfjs/test/browser_pdfjs_zoom.js create mode 100644 toolkit/components/pdfjs/test/file_pdf_download_link.html create mode 100644 toolkit/components/pdfjs/test/file_pdfjs_form.pdf create mode 100644 toolkit/components/pdfjs/test/file_pdfjs_hcm.pdf create mode 100644 toolkit/components/pdfjs/test/file_pdfjs_js.pdf create mode 100644 toolkit/components/pdfjs/test/file_pdfjs_object_stream.pdf create mode 100644 toolkit/components/pdfjs/test/file_pdfjs_object_stream.pdf^headers^ create mode 100644 toolkit/components/pdfjs/test/file_pdfjs_test.pdf create mode 100644 toolkit/components/pdfjs/test/file_pdfjs_transfer_map.pdf create mode 100644 toolkit/components/pdfjs/test/head.js create mode 100644 toolkit/components/pdfjs/test/mochitest.ini create mode 100644 toolkit/components/pdfjs/test/test_pdf_file_in_iframe.html (limited to 'toolkit/components/pdfjs/test') diff --git a/toolkit/components/pdfjs/test/browser.ini b/toolkit/components/pdfjs/test/browser.ini new file mode 100644 index 0000000000..b2292464da --- /dev/null +++ b/toolkit/components/pdfjs/test/browser.ini @@ -0,0 +1,59 @@ +[DEFAULT] +support-files = + file_pdfjs_test.pdf + head.js + +[browser_pdfjs_download_button.js] +[browser_pdfjs_editing_contextmenu.js] +skip-if = headless # the headless clipboard only recognizes unicode mime type + os == "win" && os_version == "6.1" # Skip on Azure - frequent failure +[browser_pdfjs_editing_telemetry.js] +[browser_pdfjs_fill_login.js] +support-files = + file_pdfjs_form.pdf +[browser_pdfjs_filters.js] +support-files = + file_pdfjs_transfer_map.pdf +[browser_pdfjs_find.js] +support-files = + file_pdfjs_object_stream.pdf + file_pdfjs_object_stream.pdf^headers^ +[browser_pdfjs_force_opening_files.js] +[browser_pdfjs_form.js] +support-files = + file_pdfjs_form.pdf +[browser_pdfjs_fullscreen.js] +[browser_pdfjs_hcm.js] +support-files = + file_pdfjs_hcm.pdf +[browser_pdfjs_js.js] +support-files = + file_pdfjs_js.pdf +[browser_pdfjs_load_telemetry.js] +[browser_pdfjs_main.js] +[browser_pdfjs_minimum_font_size.js] +[browser_pdfjs_navigation.js] +[browser_pdfjs_nonpdf_filename.js] +support-files = + file_pdf_download_link.html +[browser_pdfjs_not_default.js] +support-files = + file_pdfjs_object_stream.pdf + file_pdfjs_object_stream.pdf^headers^ +[browser_pdfjs_notification_close_on_navigation.js] +skip-if = true # see bug 1705327 +[browser_pdfjs_octet_stream.js] +skip-if = os == 'linux' && bits == 64 # Bug 1773830 +support-files = + file_pdfjs_object_stream.pdf + file_pdfjs_object_stream.pdf^headers^ +[browser_pdfjs_saveas.js] +support-files = + !/toolkit/content/tests/browser/common/mockTransfer.js + file_pdf_download_link.html +[browser_pdfjs_savedialog.js] +skip-if = verify +[browser_pdfjs_secondary_toolbar_telemetry.js] +[browser_pdfjs_views.js] +[browser_pdfjs_zoom.js] +skip-if = (verify && debug && (os == 'win')) diff --git a/toolkit/components/pdfjs/test/browser_pdfjs_download_button.js b/toolkit/components/pdfjs/test/browser_pdfjs_download_button.js new file mode 100644 index 0000000000..c512c6529a --- /dev/null +++ b/toolkit/components/pdfjs/test/browser_pdfjs_download_button.js @@ -0,0 +1,125 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const RELATIVE_DIR = "toolkit/components/pdfjs/test/"; +const TESTROOT = "https://example.com/browser/" + RELATIVE_DIR; + +var MockFilePicker = SpecialPowers.MockFilePicker; +MockFilePicker.init(window); +MockFilePicker.returnValue = MockFilePicker.returnOK; + +var tempDir; + +async function promiseDownloadFinished(list) { + return new Promise(resolve => { + list.addView({ + onDownloadChanged(download) { + download.launchWhenSucceeded = false; + if (download.succeeded || download.error) { + list.removeView(this); + resolve(download); + } + }, + }); + }); +} + +function createPromiseForFilePicker() { + return new Promise(resolve => { + MockFilePicker.showCallback = fp => { + let destFile = tempDir.clone(); + destFile.append(fp.defaultString); + if (destFile.exists()) { + destFile.remove(false); + } + MockFilePicker.setFiles([destFile]); + MockFilePicker.filterIndex = 0; // kSaveAsType_Complete + resolve(); + }; + }); +} + +add_setup(async function () { + tempDir = createTemporarySaveDirectory(); + MockFilePicker.displayDirectory = tempDir; + + registerCleanupFunction(async function () { + MockFilePicker.cleanup(); + await cleanupDownloads(); + tempDir.remove(true); + }); +}); + +/** + * Check clicking the download button saves the file and doesn't open a new viewer + */ +add_task(async function test_downloading_pdf_nonprivate_window() { + const pdfUrl = TESTROOT + "file_pdfjs_test.pdf"; + + await SpecialPowers.pushPrefEnv({ + set: [["browser.download.always_ask_before_handling_new_types", false]], + }); + + await BrowserTestUtils.withNewTab( + { gBrowser, url: "about:blank" }, + async function (browser) { + await waitForPdfJS(browser, pdfUrl); + + const tabCount = gBrowser.tabs.length; + info(`${tabCount} tabs are open at the start of the test`); + + let downloadList = await Downloads.getList(Downloads.PUBLIC); + + let filePickerShown = createPromiseForFilePicker(); + + let downloadFinishedPromise = promiseDownloadFinished(downloadList); + + info("Clicking on the download button..."); + await SpecialPowers.spawn(browser, [], () => { + content.document.querySelector("#download").click(); + }); + info("Waiting for a filename to be picked from the file picker"); + await filePickerShown; + + await downloadFinishedPromise; + ok(true, "A download was added when we clicked download"); + + // See bug 1739348 - don't show panel for downloads that opened dialogs + ok( + !DownloadsPanel.isPanelShowing, + "The download panel did not open, since the file picker was shown already" + ); + + const allDownloads = await downloadList.getAll(); + const dl = allDownloads.find(dl => dl.source.originalUrl === pdfUrl); + ok(!!dl, "The pdf download has the correct url in source.originalUrl"); + + SpecialPowers.clipboardCopyString(""); + DownloadsCommon.copyDownloadLink(dl); + const copiedUrl = SpecialPowers.getClipboardData("text/plain"); + is(copiedUrl, pdfUrl, "The copied url must be the original one"); + + is( + gBrowser.tabs.length, + tabCount, + "No new tab was opened to view the downloaded PDF" + ); + + SpecialPowers.clipboardCopyString(""); + const downloadsLoaded = BrowserTestUtils.waitForEvent( + browser, + "InitialDownloadsLoaded", + true + ); + BrowserTestUtils.loadURIString(browser, "about:downloads"); + await downloadsLoaded; + + info("Wait for the clipboard to contain the url of the pdf"); + await SimpleTest.promiseClipboardChange(pdfUrl, () => { + goDoCommand("cmd_copy"); + }); + } + ); +}); diff --git a/toolkit/components/pdfjs/test/browser_pdfjs_editing_contextmenu.js b/toolkit/components/pdfjs/test/browser_pdfjs_editing_contextmenu.js new file mode 100644 index 0000000000..3879a9a4f2 --- /dev/null +++ b/toolkit/components/pdfjs/test/browser_pdfjs_editing_contextmenu.js @@ -0,0 +1,373 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +const RELATIVE_DIR = "toolkit/components/pdfjs/test/"; +const TESTROOT = "http://example.com/browser/" + RELATIVE_DIR; + +// This is a modified version from browser_contextmenuFillLogins.js. +async function openContextMenuAt(browser, x, y) { + const contextMenu = document.getElementById("contentAreaContextMenu"); + + const contextMenuShownPromise = BrowserTestUtils.waitForEvent( + contextMenu, + "popupshown" + ); + + // Synthesize a contextmenu event to actually open the context menu. + await BrowserTestUtils.synthesizeMouseAtPoint( + x, + y, + { + type: "contextmenu", + button: 2, + }, + browser + ); + + await contextMenuShownPromise; + return contextMenu; +} + +/** + * Open a context menu and get the pdfjs entries + * @param {Object} browser + * @param {Object} box + * @returns {Map} the pdfjs menu entries. + */ +async function getContextMenuItems(browser, box) { + return new Promise(resolve => { + setTimeout(async () => { + const { x, y, width, height } = box; + const menuitems = [ + "context-pdfjs-undo", + "context-pdfjs-redo", + "context-sep-pdfjs-redo", + "context-pdfjs-cut", + "context-pdfjs-copy", + "context-pdfjs-paste", + "context-pdfjs-delete", + "context-pdfjs-selectall", + "context-sep-pdfjs-selectall", + ]; + + await openContextMenuAt(browser, x + width / 2, y + height / 2); + const results = new Map(); + const doc = browser.ownerDocument; + for (const menuitem of menuitems) { + const item = doc.getElementById(menuitem); + results.set(menuitem, item || null); + } + + resolve(results); + }, 0); + }); +} + +/** + * Open a context menu on the element corresponding to the given selector + * and returs the pdfjs menu entries. + * @param {Object} browser + * @param {string} selector + * @returns {Map} the pdfjs menu entries. + */ +async function getContextMenuItemsOn(browser, selector) { + const box = await SpecialPowers.spawn( + browser, + [selector], + async function (selector) { + const element = content.document.querySelector(selector); + const { x, y, width, height } = element.getBoundingClientRect(); + return { x, y, width, height }; + } + ); + return getContextMenuItems(browser, box); +} + +/** + * Hide the context menu. + * @param {Object} browser + */ +async function hideContextMenu(browser) { + await new Promise(resolve => + setTimeout(async () => { + const doc = browser.ownerDocument; + const contextMenu = doc.getElementById("contentAreaContextMenu"); + + const popupHiddenPromise = BrowserTestUtils.waitForEvent( + contextMenu, + "popuphidden" + ); + contextMenu.hidePopup(); + await popupHiddenPromise; + resolve(); + }, 0) + ); +} + +async function clickOnItem(browser, items, entry) { + const editingPromise = BrowserTestUtils.waitForContentEvent( + browser, + "editingaction", + false, + null, + true + ); + const contextMenu = document.getElementById("contentAreaContextMenu"); + contextMenu.activateItem(items.get(entry)); + await editingPromise; +} + +/** + * Asserts that the enabled pdfjs menuitems are the expected ones. + * @param {Map} menuitems + * @param {Array} expected + */ +function assertMenuitems(menuitems, expected) { + Assert.deepEqual( + [...menuitems.values()] + .filter( + elmt => + !elmt.id.includes("-sep-") && + !elmt.hidden && + elmt.getAttribute("disabled") === "false" + ) + .map(elmt => elmt.id), + expected + ); +} + +// Text copy, paste, undo, redo, delete and select all in using the context +// menu. +add_task(async function test() { + let mimeService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService); + let handlerInfo = mimeService.getFromTypeAndExtension( + "application/pdf", + "pdf" + ); + + // Make sure pdf.js is the default handler. + is( + handlerInfo.alwaysAskBeforeHandling, + false, + "pdf handler defaults to always-ask is false" + ); + is( + handlerInfo.preferredAction, + Ci.nsIHandlerInfo.handleInternally, + "pdf handler defaults to internal" + ); + + info("Pref action: " + handlerInfo.preferredAction); + + await BrowserTestUtils.withNewTab( + { gBrowser, url: "about:blank" }, + async function (browser) { + SpecialPowers.clipboardCopyString(""); + + await SpecialPowers.pushPrefEnv({ + set: [["pdfjs.annotationEditorMode", 0]], + }); + + // check that PDF is opened with internal viewer + await waitForPdfJSAllLayers(browser, TESTROOT + "file_pdfjs_test.pdf", [ + [ + "annotationEditorLayer", + "annotationLayer", + "textLayer", + "canvasWrapper", + ], + ["annotationEditorLayer", "textLayer", "canvasWrapper"], + ]); + + const spanBox = await getSpanBox(browser, "and found references"); + let menuitems = await getContextMenuItems(browser, spanBox); + + // Nothing have been edited, hence the context menu doesn't contain any + // pdf entries. + Assert.ok( + [...menuitems.values()].every(elmt => elmt.hidden), + "No visible pdf menuitem" + ); + await hideContextMenu(browser); + + await enableEditor(browser, "FreeText"); + await addFreeText(browser, "hello", spanBox); + + await BrowserTestUtils.waitForCondition( + async () => (await countElements(browser, ".freeTextEditor")) !== 0 + ); + + await TestUtils.waitForTick(); + + Assert.equal(await countElements(browser, ".freeTextEditor"), 1); + + // Unselect. + await escape(browser); + + await BrowserTestUtils.waitForCondition( + async () => (await countElements(browser, ".selectedEditor")) !== 1 + ); + + Assert.equal(await countElements(browser, ".selectedEditor"), 0); + + menuitems = await getContextMenuItems(browser, spanBox); + assertMenuitems(menuitems, [ + "context-pdfjs-undo", // Last created editor is undoable + "context-pdfjs-selectall", // and selectable. + ]); + // Undo. + await clickOnItem(browser, menuitems, "context-pdfjs-undo"); + + await BrowserTestUtils.waitForCondition( + async () => (await countElements(browser, ".freeTextEditor")) !== 1 + ); + + Assert.equal( + await countElements(browser, ".freeTextEditor"), + 0, + "The FreeText editor must have been removed" + ); + + menuitems = await getContextMenuItems(browser, spanBox); + + // The editor removed thanks to "undo" is now redoable + assertMenuitems(menuitems, ["context-pdfjs-redo"]); + await clickOnItem(browser, menuitems, "context-pdfjs-redo"); + + await BrowserTestUtils.waitForCondition( + async () => (await countElements(browser, ".freeTextEditor")) !== 0 + ); + + Assert.equal( + await countElements(browser, ".freeTextEditor"), + 1, + "The FreeText editor must have been added back" + ); + + await clickOn(browser, "#pdfjs_internal_editor_0"); + menuitems = await getContextMenuItemsOn( + browser, + "#pdfjs_internal_editor_0" + ); + + assertMenuitems(menuitems, [ + "context-pdfjs-undo", + "context-pdfjs-cut", + "context-pdfjs-copy", + "context-pdfjs-delete", + "context-pdfjs-selectall", + ]); + + await clickOnItem(browser, menuitems, "context-pdfjs-cut"); + + await BrowserTestUtils.waitForCondition( + async () => (await countElements(browser, ".freeTextEditor")) !== 1 + ); + + Assert.equal( + await countElements(browser, ".freeTextEditor"), + 0, + "The FreeText editor must have been cut" + ); + + menuitems = await getContextMenuItems(browser, spanBox); + assertMenuitems(menuitems, ["context-pdfjs-undo", "context-pdfjs-paste"]); + + await clickOnItem(browser, menuitems, "context-pdfjs-paste"); + + await BrowserTestUtils.waitForCondition( + async () => (await countElements(browser, ".freeTextEditor")) !== 0 + ); + + Assert.equal( + await countElements(browser, ".freeTextEditor"), + 1, + "The FreeText editor must have been pasted" + ); + + await clickOn(browser, "#pdfjs_internal_editor_1"); + menuitems = await getContextMenuItemsOn( + browser, + "#pdfjs_internal_editor_1" + ); + + assertMenuitems(menuitems, [ + "context-pdfjs-undo", + "context-pdfjs-cut", + "context-pdfjs-copy", + "context-pdfjs-paste", + "context-pdfjs-delete", + "context-pdfjs-selectall", + ]); + + await clickOnItem(browser, menuitems, "context-pdfjs-delete"); + + await BrowserTestUtils.waitForCondition( + async () => (await countElements(browser, ".freeTextEditor")) !== 1 + ); + + Assert.equal( + await countElements(browser, ".freeTextEditor"), + 0, + "The FreeText editor must have been deleted" + ); + + menuitems = await getContextMenuItems(browser, spanBox); + await clickOnItem(browser, menuitems, "context-pdfjs-paste"); + + await BrowserTestUtils.waitForCondition( + async () => (await countElements(browser, ".freeTextEditor")) !== 0 + ); + + Assert.equal( + await countElements(browser, ".freeTextEditor"), + 1, + "The FreeText editor must have been pasted" + ); + + await clickOn(browser, "#pdfjs_internal_editor_2"); + menuitems = await getContextMenuItemsOn( + browser, + "#pdfjs_internal_editor_2" + ); + + await clickOnItem(browser, menuitems, "context-pdfjs-copy"); + + menuitems = await getContextMenuItemsOn( + browser, + "#pdfjs_internal_editor_2" + ); + await clickOnItem(browser, menuitems, "context-pdfjs-paste"); + + await BrowserTestUtils.waitForCondition( + async () => (await countElements(browser, ".freeTextEditor")) !== 1 + ); + + Assert.equal( + await countElements(browser, ".freeTextEditor"), + 2, + "The FreeText editor must have been pasted" + ); + + menuitems = await getContextMenuItems(browser, spanBox); + await clickOnItem(browser, menuitems, "context-pdfjs-selectall"); + menuitems = await getContextMenuItems(browser, spanBox); + await clickOnItem(browser, menuitems, "context-pdfjs-delete"); + + await BrowserTestUtils.waitForCondition( + async () => (await countElements(browser, ".freeTextEditor")) !== 2 + ); + + Assert.equal( + await countElements(browser, ".freeTextEditor"), + 0, + "All the FreeText editors must have been deleted" + ); + + await SpecialPowers.spawn(browser, [], async function () { + var viewer = content.wrappedJSObject.PDFViewerApplication; + await viewer.close(); + }); + } + ); +}); diff --git a/toolkit/components/pdfjs/test/browser_pdfjs_editing_telemetry.js b/toolkit/components/pdfjs/test/browser_pdfjs_editing_telemetry.js new file mode 100644 index 0000000000..45d652c2c1 --- /dev/null +++ b/toolkit/components/pdfjs/test/browser_pdfjs_editing_telemetry.js @@ -0,0 +1,96 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +const RELATIVE_DIR = "toolkit/components/pdfjs/test/"; +const TESTROOT = "http://example.com/browser/" + RELATIVE_DIR; + +// Test telemetry. +add_task(async function test() { + let mimeService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService); + let handlerInfo = mimeService.getFromTypeAndExtension( + "application/pdf", + "pdf" + ); + + // Make sure pdf.js is the default handler. + is( + handlerInfo.alwaysAskBeforeHandling, + false, + "pdf handler defaults to always-ask is false" + ); + is( + handlerInfo.preferredAction, + Ci.nsIHandlerInfo.handleInternally, + "pdf handler defaults to internal" + ); + + info("Pref action: " + handlerInfo.preferredAction); + + await BrowserTestUtils.withNewTab( + { gBrowser, url: "about:blank" }, + async function (browser) { + await SpecialPowers.pushPrefEnv({ + set: [["pdfjs.annotationEditorMode", 0]], + }); + + Services.fog.testResetFOG(); + + // check that PDF is opened with internal viewer + await waitForPdfJSAllLayers(browser, TESTROOT + "file_pdfjs_test.pdf", [ + [ + "annotationEditorLayer", + "annotationLayer", + "textLayer", + "canvasWrapper", + ], + ["annotationEditorLayer", "textLayer", "canvasWrapper"], + ]); + + let spanBox = await getSpanBox(browser, "and found references"); + + await Services.fog.testFlushAllChildren(); + Assert.equal(Glean.pdfjs.editing.freetext.testGetValue() || 0, 0); + + await enableEditor(browser, "FreeText"); + await addFreeText(browser, "hello", spanBox); + + await BrowserTestUtils.waitForCondition( + async () => (await countElements(browser, ".freeTextEditor")) !== 0 + ); + Assert.equal(await countElements(browser, ".freeTextEditor"), 1); + + await Services.fog.testFlushAllChildren(); + + Assert.equal(Glean.pdfjs.editing.freetext.testGetValue(), 1); + + spanBox = await getSpanBox(browser, "forums and ask questions"); + await addFreeText(browser, "world", spanBox); + + await BrowserTestUtils.waitForCondition( + async () => (await countElements(browser, ".freeTextEditor")) !== 1 + ); + Assert.equal(await countElements(browser, ".freeTextEditor"), 2); + + await Services.fog.testFlushAllChildren(); + + Assert.equal(Glean.pdfjs.editing.freetext.testGetValue(), 2); + + Assert.equal(Glean.pdfjs.editing.print.testGetValue() || 0, 0); + document.getElementById("cmd_print").doCommand(); + await BrowserTestUtils.waitForCondition(() => { + let preview = document.querySelector(".printPreviewBrowser"); + return preview && BrowserTestUtils.is_visible(preview); + }); + EventUtils.synthesizeKey("KEY_Escape"); + + await Services.fog.testFlushAllChildren(); + + Assert.equal(Glean.pdfjs.editing.print.testGetValue(), 1); + + await SpecialPowers.spawn(browser, [], async function () { + var viewer = content.wrappedJSObject.PDFViewerApplication; + await viewer.close(); + }); + } + ); +}); diff --git a/toolkit/components/pdfjs/test/browser_pdfjs_fill_login.js b/toolkit/components/pdfjs/test/browser_pdfjs_fill_login.js new file mode 100644 index 0000000000..b31b9b607c --- /dev/null +++ b/toolkit/components/pdfjs/test/browser_pdfjs_fill_login.js @@ -0,0 +1,108 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +const RELATIVE_DIR = "toolkit/components/pdfjs/test/"; +const TESTROOT = "http://example.com/browser/" + RELATIVE_DIR; + +// This is a modified version from browser_contextmenuFillLogins.js. +async function openContextMenuForSelector(browser, selector) { + const doc = browser.ownerDocument; + const CONTEXT_MENU = doc.getElementById("contentAreaContextMenu"); + + let contextMenuShownPromise = BrowserTestUtils.waitForEvent( + CONTEXT_MENU, + "popupshown" + ); + + let inputCoords = await SpecialPowers.spawn( + browser, + [selector], + async selector => { + let input = content.document.querySelector(selector); + input.focus(); + let inputRect = input.getBoundingClientRect(); + + // listen for the contextmenu event so we can assert on receiving it + // and examine the target + content.contextmenuPromise = new Promise(resolve => { + content.document.body.addEventListener( + "contextmenu", + event => { + info( + `Received event on target: ${event.target.nodeName}, type: ${event.target.type}` + ); + content.console.log("got contextmenu event: ", event); + resolve(event); + }, + { once: true } + ); + }); + + let coords = { + x: inputRect.x + inputRect.width / 2, + y: inputRect.y + inputRect.height / 2, + }; + return coords; + } + ); + + // add the offsets of the in the chrome window + let browserOffsets = browser.getBoundingClientRect(); + let offsetX = browserOffsets.x + inputCoords.x; + let offsetY = browserOffsets.y + inputCoords.y; + + // Synthesize a right mouse click over the input element, we have to trigger + // both events because formfill code relies on this event happening before the contextmenu + // (which it does for real user input) in order to not show the password autocomplete. + let eventDetails = { type: "mousedown", button: 2 }; + await EventUtils.synthesizeMouseAtPoint(offsetX, offsetY, eventDetails); + + // Synthesize a contextmenu event to actually open the context menu. + eventDetails = { type: "contextmenu", button: 2 }; + await EventUtils.synthesizeMouseAtPoint(offsetX, offsetY, eventDetails); + + await SpecialPowers.spawn(browser, [], async () => { + await content.contextmenuPromise; + }); + + info("waiting for contextMenuShownPromise"); + + await contextMenuShownPromise; + return CONTEXT_MENU; +} + +/** + * Ensure the fill login context menu is not shown for PDF.js forms. + */ +add_task(async function test_filllogin() { + await SpecialPowers.pushPrefEnv({ + set: [["pdfjs.renderInteractiveForms", true]], + }); + + await BrowserTestUtils.withNewTab( + { gBrowser, url: "about:blank" }, + async function (browser) { + await waitForPdfJSAnnotationLayer( + browser, + TESTROOT + "file_pdfjs_form.pdf" + ); + + let contextMenu = await openContextMenuForSelector( + browser, + "#viewerContainer input" + ); + let fillItem = contextMenu.querySelector("#fill-login"); + ok(fillItem, "fill menu item exists"); + ok(fillItem && EventUtils.isHidden(fillItem), "fill menu item is hidden"); + + let promiseHidden = BrowserTestUtils.waitForEvent( + contextMenu, + "popuphidden" + ); + info("Calling hidePopup on contextMenu"); + contextMenu.hidePopup(); + info("waiting for promiseHidden"); + await promiseHidden; + } + ); +}); diff --git a/toolkit/components/pdfjs/test/browser_pdfjs_filters.js b/toolkit/components/pdfjs/test/browser_pdfjs_filters.js new file mode 100644 index 0000000000..87d1104a84 --- /dev/null +++ b/toolkit/components/pdfjs/test/browser_pdfjs_filters.js @@ -0,0 +1,81 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +const RELATIVE_DIR = "toolkit/components/pdfjs/test/"; +const TESTROOT = "https://example.com/browser/" + RELATIVE_DIR; + +/** + * Get the number of red pixels in the canvas. + * @param {Object} browser + * @returns {Object} + */ +async function getRedPixels(browser) { + return SpecialPowers.spawn(browser, [], async function () { + const { document } = content; + const canvas = document.querySelector("canvas"); + + Assert.ok(!!canvas, "We must have a canvas"); + + const data = canvas + .getContext("2d") + .getImageData(0, 0, canvas.width, canvas.height).data; + + let redPixels = 0; + let total = 0; + + for (let i = 0, ii = data.length; i < ii; i += 4) { + const R = data[i]; + const G = data[i + 1]; + const B = data[i + 2]; + + if (R > 128 && R > 4 * G && R > 4 * B) { + redPixels += 1; + } + total += 1; + } + + return [redPixels, total]; + }); +} + +/** + * Test that the pdf has the correct color thanks to the SVG filters. + */ +add_task(async function test() { + let mimeService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService); + let handlerInfo = mimeService.getFromTypeAndExtension( + "application/pdf", + "pdf" + ); + + // Make sure pdf.js is the default handler. + is( + handlerInfo.alwaysAskBeforeHandling, + false, + "pdf handler defaults to always-ask is false" + ); + is( + handlerInfo.preferredAction, + Ci.nsIHandlerInfo.handleInternally, + "pdf handler defaults to internal" + ); + + info("Pref action: " + handlerInfo.preferredAction); + + await BrowserTestUtils.withNewTab( + { gBrowser, url: "about:blank" }, + async function (browser) { + // check that PDF is opened with internal viewer + await waitForPdfJSCanvas( + browser, + `${TESTROOT}file_pdfjs_transfer_map.pdf#zoom=100` + ); + + const [redPixels, total] = await getRedPixels(browser); + Assert.ok( + redPixels / total >= 0.1, + `Not enough red pixels: only ${redPixels} / ${total} red pixels!` + ); + } + ); +}); diff --git a/toolkit/components/pdfjs/test/browser_pdfjs_find.js b/toolkit/components/pdfjs/test/browser_pdfjs_find.js new file mode 100644 index 0000000000..abcc89b26e --- /dev/null +++ b/toolkit/components/pdfjs/test/browser_pdfjs_find.js @@ -0,0 +1,137 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TESTROOT = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content/", + "http://mochi.test:8888/" +); + +// Get a ref to the pdfs we want to open. +const OS_PDF_URL = TESTROOT + "file_pdfjs_object_stream.pdf"; +const TEST_PDF_URL = TESTROOT + "file_pdfjs_test.pdf"; + +add_task(async function test_find_octet_stream_pdf() { + await BrowserTestUtils.withNewTab(OS_PDF_URL, async browser => { + let findEls = ["cmd_find", "cmd_findAgain", "cmd_findPrevious"].map(id => + document.getElementById(id) + ); + for (let el of findEls) { + ok(!el.hasAttribute("disabled"), `${el.id} should be enabled`); + } + }); +}); + +// This code is roughly based on `promiseFindFinished` from +// `toolkit/content/tests/browser/head.js`. +function waitForFinderResult(findbar) { + return new Promise(resolve => { + let resultListener = { + onFindResult(data) { + findbar.browser.finder.removeResultListener(resultListener); + resolve(data); + }, + onCurrentSelection() {}, + onMatchesCountResult() {}, + onHighlightFinished() {}, + }; + findbar.browser.finder.addResultListener(resultListener); + }); +} + +function waitForPdfjsResult(findbar) { + // FIXME: This is a pretty sketchy way to intercept the results from pdfjs... + return new Promise(resolve => { + let oldUpdateControlState = findbar.updateControlState; + function updateControlState(result, findPrevious) { + if (result !== Ci.nsITypeAheadFind.FIND_PENDING) { + resolve({ result, findPrevious }); + if (this.updateControlState === updateControlState) { + this.updateControlState = oldUpdateControlState; + } + } + return oldUpdateControlState.call(this, result, findPrevious); + } + findbar.updateControlState = updateControlState; + }); +} + +// This code is roughly based on `promiseFindFinished` from +// `toolkit/content/tests/browser/head.js`. +async function doFind(findbar, searchText, waitFunc) { + info(`performing a find for ${searchText}`); + findbar.startFind(findbar.FIND_NORMAL); + let highlightElement = findbar.getElement("highlight"); + if (highlightElement.checked) { + highlightElement.click(); + } + await new Promise(resolve => executeSoon(resolve)); + findbar._findField.value = searchText; + let promise = waitFunc(findbar); + findbar._find(); + return promise; +} + +add_task(async function test_findbar_in_pdf() { + let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PDF_URL); + let findbar = await gBrowser.getFindBar(tab); + let findResult = await doFind(findbar, "Mozilla", waitForPdfjsResult); + is( + findResult.result, + Ci.nsITypeAheadFind.FIND_FOUND, + "The Mozilla string was found in the PDF document" + ); + BrowserTestUtils.removeTab(tab); +}); + +add_task(async function test_findbar_in_pdf_after_adopt() { + let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PDF_URL); + let newWindow = await BrowserTestUtils.openNewBrowserWindow(); + + info("adopting tab into new window"); + let newTab = newWindow.gBrowser.adoptTab(tab); + + let findbar = await newWindow.gBrowser.getFindBar(newTab); + let findResult = await doFind(findbar, "Mozilla", waitForPdfjsResult); + is( + findResult.result, + Ci.nsITypeAheadFind.FIND_FOUND, + "The Mozilla string was found in the PDF document" + ); + await BrowserTestUtils.closeWindow(newWindow); +}); + +// Make sure that performing a find in the browser continues to work after +// navigating to another page (i.e. Pdfjs disables its pdfjs interception +// listeners). +add_task(async function test_findbar_after_navigate() { + await BrowserTestUtils.withNewTab(TEST_PDF_URL, async browser => { + let tab = gBrowser.getTabForBrowser(browser); + + ok( + !gBrowser.isFindBarInitialized(tab), + "Findbar shouldn't be initialized yet" + ); + + info("navigating to a webpage"); + BrowserTestUtils.loadURIString( + browser, + "http://mochi.test:8888/document-builder.sjs?html=

hello, world!

" + ); + await BrowserTestUtils.browserLoaded(browser); + + ok( + !gBrowser.isFindBarInitialized(tab), + "Findbar still shouldn't be initialized after navigation" + ); + + let findbar = await gBrowser.getFindBar(tab); + let findResult = await doFind(findbar, "hello", waitForFinderResult); + is( + findResult.result, + Ci.nsITypeAheadFind.FIND_FOUND, + "The hello string was found in the HTML document" + ); + }); +}); diff --git a/toolkit/components/pdfjs/test/browser_pdfjs_force_opening_files.js b/toolkit/components/pdfjs/test/browser_pdfjs_force_opening_files.js new file mode 100644 index 0000000000..0490a6eec3 --- /dev/null +++ b/toolkit/components/pdfjs/test/browser_pdfjs_force_opening_files.js @@ -0,0 +1,89 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +add_task(async function test_file_opening() { + // Get a ref to the pdf we want to open. + let dirFileObj = getChromeDir(getResolvedURI(gTestPath)); + dirFileObj.append("file_pdfjs_test.pdf"); + + // Change the defaults. + var oldAction = changeMimeHandler(Ci.nsIHandlerInfo.useSystemDefault, true); + + // Test: "Open with" dialog should not come up, despite pdf.js not being + // the default - because files from disk should always use pdfjs, unless + // it is forcibly disabled. + let openedWindow = false; + let windowOpenedPromise = new Promise((resolve, reject) => { + addWindowListener( + "chrome://mozapps/content/downloads/unknownContentType.xhtml", + () => { + openedWindow = true; + resolve(); + } + ); + }); + + // Open the tab with a system principal: + var tab = BrowserTestUtils.addTab(gBrowser, dirFileObj.path); + + let pdfjsLoadedPromise = TestUtils.waitForCondition(() => { + let { contentPrincipal } = tab.linkedBrowser; + return (contentPrincipal?.URI?.spec || "").endsWith("viewer.html"); + }); + await Promise.race([pdfjsLoadedPromise, windowOpenedPromise]); + ok(!openedWindow, "Shouldn't open an unknownContentType window!"); + + BrowserTestUtils.removeTab(tab); + + // Now try opening it from the file directory: + tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + dirFileObj.parent.path + ); + pdfjsLoadedPromise = BrowserTestUtils.browserLoaded( + tab.linkedBrowser, + false, + url => url.endsWith("test.pdf") + ); + await SpecialPowers.spawn(tab.linkedBrowser, [], () => { + content.document.querySelector("a[href$='test.pdf']").click(); + }); + await Promise.race([pdfjsLoadedPromise, windowOpenedPromise]); + ok( + !openedWindow, + "Shouldn't open an unknownContentType window for PDFs from file: links!" + ); + + registerCleanupFunction(function () { + if (listenerCleanup) { + listenerCleanup(); + } + changeMimeHandler(oldAction[0], oldAction[1]); + gBrowser.removeTab(tab); + }); +}); + +let listenerCleanup; +function addWindowListener(aURL, aCallback) { + let listener = { + onOpenWindow(aXULWindow) { + info("window opened, waiting for focus"); + listenerCleanup(); + listenerCleanup = null; + + var domwindow = aXULWindow.docShell.domWindow; + waitForFocus(function () { + is( + domwindow.document.location.href, + aURL, + "should have seen the right window open" + ); + domwindow.close(); + aCallback(); + }, domwindow); + }, + onCloseWindow(aXULWindow) {}, + }; + Services.wm.addListener(listener); + listenerCleanup = () => Services.wm.removeListener(listener); +} diff --git a/toolkit/components/pdfjs/test/browser_pdfjs_form.js b/toolkit/components/pdfjs/test/browser_pdfjs_form.js new file mode 100644 index 0000000000..380c812b8b --- /dev/null +++ b/toolkit/components/pdfjs/test/browser_pdfjs_form.js @@ -0,0 +1,85 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +const RELATIVE_DIR = "toolkit/components/pdfjs/test/"; +const TESTROOT = "http://example.com/browser/" + RELATIVE_DIR; + +async function checkFormState(formsEnabled) { + ok( + content.document.querySelector("div#viewer"), + "document content has viewer UI" + ); + + let formInput = content.document.querySelector("#viewerContainer input"); + + if (formsEnabled) { + ok(formInput, "Form input available"); + } else { + ok(!formInput, "Form input not available"); + } + let viewer = content.wrappedJSObject.PDFViewerApplication; + await viewer.close(); +} + +add_task(async function test_defaults() { + // Ensure the default preference is set to the expected value. + let defaultBranch = Services.prefs.getDefaultBranch("pdfjs."); + let prefType = defaultBranch.getPrefType("annotationMode"); + let renderForms = Services.prefs.getIntPref("pdfjs.annotationMode") === 2; + + is( + prefType, + Ci.nsIPrefBranch.PREF_INT, + "The form pref is defined by default" + ); + + ok(renderForms, "Forms are enabled"); + + // Test that the forms state matches the pref. + await BrowserTestUtils.withNewTab( + { gBrowser, url: "about:blank" }, + async function (browser) { + await waitForPdfJSAnnotationLayer( + browser, + TESTROOT + "file_pdfjs_form.pdf" + ); + + await SpecialPowers.spawn(browser, [true], checkFormState); + } + ); +}); + +// Test disabling forms with pref. +add_task(async function test_disabling() { + await BrowserTestUtils.withNewTab( + { gBrowser, url: "about:blank" }, + async function (browser) { + // First, make sure they are enabled. + await SpecialPowers.pushPrefEnv({ + set: [["pdfjs.annotationMode", 2]], + }); + await waitForPdfJSAnnotationLayer( + browser, + TESTROOT + "file_pdfjs_form.pdf" + ); + await SpecialPowers.spawn( + browser, + [true /* formsEnabled */], + checkFormState + ); + // Now disable the forms. + await SpecialPowers.pushPrefEnv({ + set: [["pdfjs.annotationMode", 1]], + }); + await waitForPdfJSAnnotationLayer( + browser, + TESTROOT + "file_pdfjs_form.pdf" + ); + await SpecialPowers.spawn( + browser, + [false /* formsEnabled */], + checkFormState + ); + } + ); +}); diff --git a/toolkit/components/pdfjs/test/browser_pdfjs_fullscreen.js b/toolkit/components/pdfjs/test/browser_pdfjs_fullscreen.js new file mode 100644 index 0000000000..d53e4cfa74 --- /dev/null +++ b/toolkit/components/pdfjs/test/browser_pdfjs_fullscreen.js @@ -0,0 +1,84 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +const RELATIVE_DIR = "toolkit/components/pdfjs/test/"; +const TESTROOT = "https://example.com/browser/" + RELATIVE_DIR; + +function waitForFullScreenState(browser, state) { + return new Promise(resolve => { + let eventReceived = false; + + let observe = (subject, topic, data) => { + if (!eventReceived) { + return; + } + Services.obs.removeObserver(observe, "fullscreen-painted"); + resolve(); + }; + Services.obs.addObserver(observe, "fullscreen-painted"); + + browser.ownerGlobal.addEventListener( + `MozDOMFullscreen:${state ? "Entered" : "Exited"}`, + () => { + eventReceived = true; + }, + { once: true } + ); + }); +} + +/** + * Test that we can enter in presentation mode in using the keyboard shortcut + * without having to interact with the pdf. + */ +add_task(async function test() { + let mimeService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService); + let handlerInfo = mimeService.getFromTypeAndExtension( + "application/pdf", + "pdf" + ); + + // Make sure pdf.js is the default handler. + is( + handlerInfo.alwaysAskBeforeHandling, + false, + "pdf handler defaults to always-ask is false" + ); + is( + handlerInfo.preferredAction, + Ci.nsIHandlerInfo.handleInternally, + "pdf handler defaults to internal" + ); + + info("Pref action: " + handlerInfo.preferredAction); + + await BrowserTestUtils.withNewTab( + { gBrowser, url: "about:blank" }, + async function (browser) { + // check that PDF is opened with internal viewer + await waitForPdfJSCanvas(browser, `${TESTROOT}file_pdfjs_test.pdf`); + + const fullscreenPromise = waitForFullScreenState(browser, true); + + await SpecialPowers.spawn(browser, [], async function () { + const { ContentTaskUtils } = ChromeUtils.importESModule( + "resource://testing-common/ContentTaskUtils.sys.mjs" + ); + const EventUtils = ContentTaskUtils.getEventUtils(content); + await EventUtils.synthesizeKey( + "p", + { ctrlKey: true, altKey: true }, + content + ); + }); + + await fullscreenPromise; + ok(true, "Should be in fullscreen"); + + await SpecialPowers.spawn(browser, [], async function () { + const viewer = content.wrappedJSObject.PDFViewerApplication; + await viewer.close(); + }); + } + ); +}); diff --git a/toolkit/components/pdfjs/test/browser_pdfjs_hcm.js b/toolkit/components/pdfjs/test/browser_pdfjs_hcm.js new file mode 100644 index 0000000000..cc5f098f1e --- /dev/null +++ b/toolkit/components/pdfjs/test/browser_pdfjs_hcm.js @@ -0,0 +1,104 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +const RELATIVE_DIR = "toolkit/components/pdfjs/test/"; +const TESTROOT = "http://example.com/browser/" + RELATIVE_DIR; + +/** + * Get the first and last pixels on the drawn canvas. + * @param {Object} browser + * @returns {Object} + */ +async function getFirstLastPixels(browser) { + return SpecialPowers.spawn(browser, [], async function () { + const { document } = content; + const canvas = document.querySelector("canvas"); + + Assert.ok(!!canvas, "We must have a canvas"); + + const data = Array.from( + canvas.getContext("2d").getImageData(0, 0, canvas.width, canvas.height) + .data + ); + + return { + first: data.slice(0, 3), + last: data.slice(-4, -1), + }; + }); +} + +/** + * Test that the pdf has the correct color depending on the high contrast mode. + */ +add_task(async function test() { + let mimeService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService); + let handlerInfo = mimeService.getFromTypeAndExtension( + "application/pdf", + "pdf" + ); + + // Make sure pdf.js is the default handler. + is( + handlerInfo.alwaysAskBeforeHandling, + false, + "pdf handler defaults to always-ask is false" + ); + is( + handlerInfo.preferredAction, + Ci.nsIHandlerInfo.handleInternally, + "pdf handler defaults to internal" + ); + + info("Pref action: " + handlerInfo.preferredAction); + + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.display.document_color_use", 0], + ["browser.display.background_color", "#ff0000"], + ["browser.display.foreground_color", "#00ff00"], + ["browser.display.use_system_colors", false], + ], + }); + + await BrowserTestUtils.withNewTab( + { gBrowser, url: "about:blank" }, + async function (browser) { + // check that PDF is opened with internal viewer + await waitForPdfJSCanvas( + browser, + `${TESTROOT}file_pdfjs_hcm.pdf#zoom=800` + ); + + const { first, last } = await getFirstLastPixels(browser); + // The first pixel must be black and not green. + Assert.deepEqual(first, [0, 0, 0]); + + // The last pixel must be white and not red. + Assert.deepEqual(last, [255, 255, 255]); + } + ); + + // Enable HCM. + await SpecialPowers.pushPrefEnv({ + set: [["ui.useAccessibilityTheme", 1]], + }); + + await BrowserTestUtils.withNewTab( + { gBrowser, url: "about:blank" }, + async function (browser) { + // check that PDF is opened with internal viewer + await waitForPdfJSCanvas( + browser, + `${TESTROOT}file_pdfjs_hcm.pdf#zoom=800` + ); + + const { first, last } = await getFirstLastPixels(browser); + // The first pixel must be green. + Assert.deepEqual(first, [0, 255, 0]); + + // The last pixel must be red. + Assert.deepEqual(last, [255, 0, 0]); + } + ); +}); diff --git a/toolkit/components/pdfjs/test/browser_pdfjs_js.js b/toolkit/components/pdfjs/test/browser_pdfjs_js.js new file mode 100644 index 0000000000..67909623cd --- /dev/null +++ b/toolkit/components/pdfjs/test/browser_pdfjs_js.js @@ -0,0 +1,49 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +const RELATIVE_DIR = "toolkit/components/pdfjs/test/"; +const TESTROOT = "http://example.com/browser/" + RELATIVE_DIR; + +// Test js in pdf file. +add_task(async function test_js_sandbox() { + await BrowserTestUtils.withNewTab( + { gBrowser, url: "about:blank" }, + async function (browser) { + await SpecialPowers.pushPrefEnv({ + set: [["pdfjs.enableScripting", true]], + }); + + await Promise.all([ + waitForPdfJSAnnotationLayer(browser, TESTROOT + "file_pdfjs_js.pdf"), + waitForPdfJSSandbox(browser), + ]); + + await SpecialPowers.spawn(browser, [], async () => { + const { PdfSandbox } = ChromeUtils.importESModule( + "resource://pdf.js/PdfSandbox.sys.mjs" + ); + + let sandboxDestroyCount = 0; + const originalDestroy = PdfSandbox.prototype.destroy; + PdfSandbox.prototype.destroy = function () { + const obj = this.sandbox.eval("({})"); + originalDestroy.apply(this, arguments); + sandboxDestroyCount++; + ok(Cu.isDeadWrapper(obj), "Sandbox must have been nuked"); + }; + + const document = content.document; + const button = document.querySelector("[data-annotation-id='16R'] a"); + button.dispatchEvent(new content.Event("click")); + + const text = document.querySelector(`[data-element-id="15R"]`); + + is(text.value, "test", "Text field must containt 'test' string"); + + content.addEventListener("unload", () => { + is(sandboxDestroyCount, 1, "Sandbox must have been destroyed"); + }); + }); + } + ); +}); diff --git a/toolkit/components/pdfjs/test/browser_pdfjs_load_telemetry.js b/toolkit/components/pdfjs/test/browser_pdfjs_load_telemetry.js new file mode 100644 index 0000000000..61a4dba290 --- /dev/null +++ b/toolkit/components/pdfjs/test/browser_pdfjs_load_telemetry.js @@ -0,0 +1,51 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +const RELATIVE_DIR = "toolkit/components/pdfjs/test/"; +const TESTROOT = "https://example.com/browser/" + RELATIVE_DIR; + +// Test telemetry. +add_task(async function test() { + let mimeService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService); + let handlerInfo = mimeService.getFromTypeAndExtension( + "application/pdf", + "pdf" + ); + + // Make sure pdf.js is the default handler. + is( + handlerInfo.alwaysAskBeforeHandling, + false, + "pdf handler defaults to always-ask is false" + ); + is( + handlerInfo.preferredAction, + Ci.nsIHandlerInfo.handleInternally, + "pdf handler defaults to internal" + ); + + info("Pref action: " + handlerInfo.preferredAction); + + await BrowserTestUtils.withNewTab( + { gBrowser, url: "about:blank" }, + async function (browser) { + Services.fog.testResetFOG(); + + await Services.fog.testFlushAllChildren(); + Assert.equal(Glean.pdfjs.timeToView.testGetValue(), undefined); + Assert.equal(Glean.pdfjs.used.testGetValue() || 0, 0); + + // check that PDF is opened with internal viewer + await waitForPdfJSCanvas(browser, TESTROOT + "file_pdfjs_test.pdf"); + + await Services.fog.testFlushAllChildren(); + Assert.ok(Glean.pdfjs.timeToView.testGetValue().sum !== 0); + Assert.equal(Glean.pdfjs.used.testGetValue(), 1); + + await SpecialPowers.spawn(browser, [], async function () { + var viewer = content.wrappedJSObject.PDFViewerApplication; + await viewer.close(); + }); + } + ); +}); diff --git a/toolkit/components/pdfjs/test/browser_pdfjs_main.js b/toolkit/components/pdfjs/test/browser_pdfjs_main.js new file mode 100644 index 0000000000..c2f2122268 --- /dev/null +++ b/toolkit/components/pdfjs/test/browser_pdfjs_main.js @@ -0,0 +1,72 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +const RELATIVE_DIR = "toolkit/components/pdfjs/test/"; +const TESTROOT = "http://example.com/browser/" + RELATIVE_DIR; + +add_task(async function test() { + let mimeService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService); + let handlerInfo = mimeService.getFromTypeAndExtension( + "application/pdf", + "pdf" + ); + + // Make sure pdf.js is the default handler. + is( + handlerInfo.alwaysAskBeforeHandling, + false, + "pdf handler defaults to always-ask is false" + ); + is( + handlerInfo.preferredAction, + Ci.nsIHandlerInfo.handleInternally, + "pdf handler defaults to internal" + ); + + info("Pref action: " + handlerInfo.preferredAction); + + await BrowserTestUtils.withNewTab( + { gBrowser, url: "about:blank" }, + async function (newTabBrowser) { + await waitForPdfJS(newTabBrowser, TESTROOT + "file_pdfjs_test.pdf"); + + await SpecialPowers.spawn(newTabBrowser, [], async function () { + // Overall sanity tests + Assert.ok( + content.document.querySelector("div#viewer"), + "document content has viewer UI" + ); + + // Sidebar: open + var sidebar = content.document.querySelector("button#sidebarToggle"), + outerContainer = content.document.querySelector("div#outerContainer"); + + sidebar.click(); + Assert.ok( + outerContainer.classList.contains("sidebarOpen"), + "sidebar opens on click" + ); + + // Sidebar: close + sidebar.click(); + Assert.ok( + !outerContainer.classList.contains("sidebarOpen"), + "sidebar closes on click" + ); + + // Verify that initial page is 1 + var pgNumber = content.document.querySelector("input#pageNumber").value; + Assert.equal(parseInt(pgNumber, 10), 1, "initial page is 1"); + + // Bookmark button + var viewBookmark = content.document.querySelector("a#viewBookmark"); + viewBookmark.click(); + + Assert.ok(!!viewBookmark.href.length, "viewBookmark button has href"); + + var viewer = content.wrappedJSObject.PDFViewerApplication; + await viewer.close(); + }); + } + ); +}); diff --git a/toolkit/components/pdfjs/test/browser_pdfjs_minimum_font_size.js b/toolkit/components/pdfjs/test/browser_pdfjs_minimum_font_size.js new file mode 100644 index 0000000000..f85e0c3ec6 --- /dev/null +++ b/toolkit/components/pdfjs/test/browser_pdfjs_minimum_font_size.js @@ -0,0 +1,70 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +const RELATIVE_DIR = "toolkit/components/pdfjs/test/"; +const TESTROOT = "https://example.com/browser/" + RELATIVE_DIR; + +/** + * Test that the pdf doesn't use the minimum font size. + */ +add_task(async function test() { + let mimeService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService); + let handlerInfo = mimeService.getFromTypeAndExtension( + "application/pdf", + "pdf" + ); + + // Make sure pdf.js is the default handler. + is( + handlerInfo.alwaysAskBeforeHandling, + false, + "pdf handler defaults to always-ask is false" + ); + is( + handlerInfo.preferredAction, + Ci.nsIHandlerInfo.handleInternally, + "pdf handler defaults to internal" + ); + + info("Pref action: " + handlerInfo.preferredAction); + + await SpecialPowers.pushPrefEnv({ + set: [["font.minimum-size.x-western", 56]], + }); + + await BrowserTestUtils.withNewTab( + { gBrowser, url: "about:blank" }, + async function (browser) { + // check that PDF is opened with internal viewer + await waitForPdfJSAllLayers( + browser, + `${TESTROOT}file_pdfjs_test.pdf#zoom=10`, + [] + ); + + await SpecialPowers.spawn(browser, [true], () => { + const pageNumberInput = content.document.querySelector("#pageNumber"); + const { style } = pageNumberInput; + const baseHeight = + content.document.defaultView.getComputedStyle(pageNumberInput).height; + + style.fontSize = "1px"; + const height = + content.document.defaultView.getComputedStyle(pageNumberInput).height; + + // Changing the font size should change the height of the input + // element. If the minimum font size is used, the height won't change. + Assert.notEqual( + baseHeight, + height, + "The pdf viewer musn't use the minimum font size." + ); + }); + + await SpecialPowers.spawn(browser, [], async function () { + var viewer = content.wrappedJSObject.PDFViewerApplication; + await viewer.close(); + }); + } + ); +}); diff --git a/toolkit/components/pdfjs/test/browser_pdfjs_navigation.js b/toolkit/components/pdfjs/test/browser_pdfjs_navigation.js new file mode 100644 index 0000000000..9851150e80 --- /dev/null +++ b/toolkit/components/pdfjs/test/browser_pdfjs_navigation.js @@ -0,0 +1,314 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ +/* eslint-disable mozilla/no-arbitrary-setTimeout */ + +requestLongerTimeout(2); + +const RELATIVE_DIR = "toolkit/components/pdfjs/test/"; +const TESTROOT = "http://example.com/browser/" + RELATIVE_DIR; + +const PDF_OUTLINE_ITEMS = 17; +const TESTS = [ + { + action: { + selector: "button#next", + event: "click", + }, + expectedPage: 2, + message: "navigated to next page using NEXT button", + }, + { + action: { + selector: "button#previous", + event: "click", + }, + expectedPage: 1, + message: "navigated to previous page using PREV button", + }, + { + action: { + selector: "button#next", + event: "click", + }, + expectedPage: 2, + message: "navigated to next page using NEXT button", + }, + { + action: { + selector: "input#pageNumber", + value: 1, + event: "change", + }, + expectedPage: 1, + message: "navigated to first page using pagenumber", + }, + { + action: { + selector: "#thumbnailView a:nth-child(4)", + event: "click", + }, + expectedPage: 4, + message: "navigated to 4th page using thumbnail view", + }, + { + action: { + selector: "#thumbnailView a:nth-child(2)", + event: "click", + }, + expectedPage: 2, + message: "navigated to 2nd page using thumbnail view", + }, + { + action: { + selector: "#viewer", + event: "keydown", + keyCode: 36, + }, + expectedPage: 1, + message: "navigated to 1st page using 'home' key", + }, + { + action: { + selector: "#viewer", + event: "keydown", + keyCode: 34, + }, + expectedPage: 2, + message: "navigated to 2nd page using 'Page Down' key", + }, + { + action: { + selector: "#viewer", + event: "keydown", + keyCode: 33, + }, + expectedPage: 1, + message: "navigated to 1st page using 'Page Up' key", + }, + { + action: { + selector: "#viewer", + event: "keydown", + keyCode: 39, + }, + expectedPage: 2, + message: "navigated to 2nd page using 'right' key", + }, + { + action: { + selector: "#viewer", + event: "keydown", + keyCode: 37, + }, + expectedPage: 1, + message: "navigated to 1st page using 'left' key", + }, + { + action: { + selector: "#viewer", + event: "keydown", + keyCode: 35, + }, + expectedPage: 5, + message: "navigated to last page using 'home' key", + }, + { + action: { + selector: "#outlineView .treeItem:nth-child(1) a", + event: "click", + }, + expectedPage: 1, + message: "navigated to 1st page using outline view", + }, + { + action: { + selector: "#outlineView .treeItem:nth-child(" + PDF_OUTLINE_ITEMS + ") a", + event: "click", + }, + expectedPage: 4, + message: "navigated to 4th page using outline view", + }, + { + action: { + selector: "input#pageNumber", + value: 5, + event: "change", + }, + expectedPage: 5, + message: "navigated to 5th page using pagenumber", + }, +]; + +add_task(async function test() { + let mimeService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService); + let handlerInfo = mimeService.getFromTypeAndExtension( + "application/pdf", + "pdf" + ); + + // Make sure pdf.js is the default handler. + is( + handlerInfo.alwaysAskBeforeHandling, + false, + "pdf handler defaults to always-ask is false" + ); + is( + handlerInfo.preferredAction, + Ci.nsIHandlerInfo.handleInternally, + "pdf handler defaults to internal" + ); + + info("Pref action: " + handlerInfo.preferredAction); + + await BrowserTestUtils.withNewTab( + { gBrowser, url: "about:blank" }, + async function (newTabBrowser) { + await waitForPdfJS(newTabBrowser, TESTROOT + "file_pdfjs_test.pdf"); + + await SpecialPowers.spawn(newTabBrowser, [], async function () { + // Check if PDF is opened with internal viewer + Assert.ok( + content.document.querySelector("div#viewer"), + "document content has viewer UI" + ); + }); + + await SpecialPowers.spawn(newTabBrowser, [], contentSetUp); + + await runTests(newTabBrowser); + + await SpecialPowers.spawn(newTabBrowser, [], async function () { + let pageNumber = content.document.querySelector("input#pageNumber"); + Assert.equal( + pageNumber.value, + pageNumber.max, + "Document is left on the last page" + ); + }); + } + ); +}); + +async function contentSetUp() { + /** + * Outline Items gets appended to the document later on we have to + * wait for them before we start to navigate though document + * + * @param document + * @returns {deferred.promise|*} + */ + function waitForOutlineItems(document) { + return new Promise((resolve, reject) => { + document.addEventListener( + "outlineloaded", + function (evt) { + var outlineCount = evt.detail.outlineCount; + + if ( + document.querySelectorAll("#outlineView .treeItem").length === + outlineCount + ) { + resolve(); + } else { + reject("Unable to find outline items."); + } + }, + { once: true } + ); + }); + } + + /** + * The key navigation has to happen in page-fit, otherwise it won't scroll + * through a complete page + * + * @param document + * @returns {deferred.promise|*} + */ + function setZoomToPageFit(document) { + return new Promise(resolve => { + document.addEventListener( + "pagerendered", + function (e) { + document.querySelector("#viewer").click(); + resolve(); + }, + { once: true } + ); + + var select = document.querySelector("select#scaleSelect"); + select.selectedIndex = 2; + select.dispatchEvent(new content.Event("change")); + }); + } + + await waitForOutlineItems(content.document); + await setZoomToPageFit(content.document); +} + +/** + * As the page changes asynchronously, we have to wait for the event after + * we trigger the action so we will be at the expected page number after each action + * + * @param document + * @param window + * @param test + * @param callback + */ +async function runTests(browser) { + await SpecialPowers.spawn(browser, [TESTS], async function (contentTESTS) { + let window = content; + let document = window.document; + + for (let test of contentTESTS) { + let deferred = {}; + deferred.promise = new Promise((resolve, reject) => { + deferred.resolve = resolve; + deferred.reject = reject; + }); + + let pageNumber = document.querySelector("input#pageNumber"); + + // Add an event-listener to wait for page to change, afterwards resolve the promise + let timeout = window.setTimeout(() => deferred.reject(), 5000); + window.addEventListener("pagechanging", function pageChange() { + if (pageNumber.value == test.expectedPage) { + window.removeEventListener("pagechanging", pageChange); + window.clearTimeout(timeout); + deferred.resolve(+pageNumber.value); + } + }); + + // Get the element and trigger the action for changing the page + var el = document.querySelector(test.action.selector); + Assert.ok(el, "Element '" + test.action.selector + "' has been found"); + + // The value option is for input case + if (test.action.value) { + el.value = test.action.value; + } + + // Dispatch the event for changing the page + var ev; + if (test.action.event == "keydown") { + ev = new window.KeyboardEvent("keydown", { + bubbles: true, + cancelable: true, + view: null, + keyCode: test.action.keyCode, + charCode: 0, + }); + el.dispatchEvent(ev); + } else { + ev = new content.Event(test.action.event); + } + el.dispatchEvent(ev); + + let pgNumber = await deferred.promise; + Assert.equal(pgNumber, test.expectedPage, test.message); + } + + var viewer = content.wrappedJSObject.PDFViewerApplication; + await viewer.close(); + }); +} diff --git a/toolkit/components/pdfjs/test/browser_pdfjs_nonpdf_filename.js b/toolkit/components/pdfjs/test/browser_pdfjs_nonpdf_filename.js new file mode 100644 index 0000000000..ee08cd45d1 --- /dev/null +++ b/toolkit/components/pdfjs/test/browser_pdfjs_nonpdf_filename.js @@ -0,0 +1,73 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TESTROOT = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content/", + "https://example.com/" +); + +// The page we use to open the PDF. +const LINK_PAGE_URL = TESTROOT + "file_pdf_download_link.html"; + +add_task(async function test_filename_nonpdf_extension() { + var MockFilePicker = SpecialPowers.MockFilePicker; + MockFilePicker.init(window); + let filepickerNamePromise = new Promise(resolve => { + MockFilePicker.showCallback = function (fp) { + resolve(fp.defaultString); + return MockFilePicker.returnCancel; + }; + }); + registerCleanupFunction(() => MockFilePicker.cleanup()); + + await SpecialPowers.pushPrefEnv({ + set: [["browser.download.open_pdf_attachments_inline", true]], + }); + + await BrowserTestUtils.withNewTab( + { gBrowser, url: LINK_PAGE_URL }, + async function (browser) { + await SpecialPowers.spawn(browser, [], async () => { + let link = content.document.getElementById("custom_filename"); + let response = await content.fetch(link.href, { + method: "GET", + headers: { + "Content-Type": "application/pdf", + }, + }); + let blob = await response.blob(); + const url = content.URL.createObjectURL(blob); + link.href = url; + link.download = "Fido-2022-04-28"; + link.rel = "noopener"; + }); + + let pdfLoaded = BrowserTestUtils.waitForNewTab( + gBrowser, + url => url.startsWith("blob:"), + true + ); + await BrowserTestUtils.synthesizeMouseAtCenter( + "#custom_filename", + {}, + browser + ); + let newTab = await pdfLoaded; + + info("Clicking on the download button..."); + await SpecialPowers.spawn(newTab.linkedBrowser, [], () => { + content.document.getElementById("download").click(); + }); + info("Waiting for a filename to be picked from the file picker"); + let defaultName = await filepickerNamePromise; + is( + defaultName, + "Fido-2022-04-28.pdf", + "Should have gotten the provided filename with pdf suffixed." + ); + BrowserTestUtils.removeTab(newTab); + } + ); +}); diff --git a/toolkit/components/pdfjs/test/browser_pdfjs_not_default.js b/toolkit/components/pdfjs/test/browser_pdfjs_not_default.js new file mode 100644 index 0000000000..ea05348bc9 --- /dev/null +++ b/toolkit/components/pdfjs/test/browser_pdfjs_not_default.js @@ -0,0 +1,26 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +add_task(async function test_pdfjs_not_default() { + var oldAction = changeMimeHandler(Ci.nsIHandlerInfo.useSystemDefault, true); + let dirFileObj = getChromeDir(getResolvedURI(gTestPath)); + dirFileObj.append("file_pdfjs_test.pdf"); + + let tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + dirFileObj.path + ); + + // If we don't have the Pdfjs actor loaded, this will throw + await getPdfjsActor(); + + changeMimeHandler(oldAction[0], oldAction[1]); + + gBrowser.removeTab(tab); +}); + +function getPdfjsActor() { + let win = Services.wm.getMostRecentWindow("navigator:browser"); + let selectedBrowser = win.gBrowser.selectedBrowser; + return selectedBrowser.browsingContext.currentWindowGlobal.getActor("Pdfjs"); +} diff --git a/toolkit/components/pdfjs/test/browser_pdfjs_notification_close_on_navigation.js b/toolkit/components/pdfjs/test/browser_pdfjs_notification_close_on_navigation.js new file mode 100644 index 0000000000..ac96b4418c --- /dev/null +++ b/toolkit/components/pdfjs/test/browser_pdfjs_notification_close_on_navigation.js @@ -0,0 +1,43 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +const RELATIVE_DIR = "toolkit/components/pdfjs/test/"; +const TESTROOT = "http://example.com/browser/" + RELATIVE_DIR; + +add_task(async function test_notification_is_removed_upon_navigation() { + await BrowserTestUtils.withNewTab( + { gBrowser, url: "about:blank" }, + async function (browser) { + await waitForPdfJSAnnotationLayer( + browser, + TESTROOT + "file_pdfjs_form.pdf" + ); + + // Trigger the PDFjs fallback notification bar + await ContentTask.spawn(browser, null, async () => { + content.wrappedJSObject.PDFViewerApplication.fallback(); + }); + + let notification = await TestUtils.waitForCondition( + () => + browser && + gBrowser.getNotificationBox(browser) && + gBrowser.getNotificationBox(browser).currentNotification, + "waiting for notification" + ); + ok(notification, "A notification should be shown"); + is( + notification.getAttribute("value"), + "pdfjs-fallback", + "Notification should be pdfjs-fallback" + ); + + BrowserTestUtils.loadURIString(browser, "https://example.com"); + + await TestUtils.waitForCondition( + () => !notification.parentNode, + "notification should be removed upon navigation to example.com" + ); + } + ); +}); diff --git a/toolkit/components/pdfjs/test/browser_pdfjs_octet_stream.js b/toolkit/components/pdfjs/test/browser_pdfjs_octet_stream.js new file mode 100644 index 0000000000..d2b4fe310f --- /dev/null +++ b/toolkit/components/pdfjs/test/browser_pdfjs_octet_stream.js @@ -0,0 +1,146 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TESTROOT = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content/", + "http://mochi.test:8888/" +); + +// Get a ref to the pdf we want to open. +const PDF_URL = TESTROOT + "file_pdfjs_object_stream.pdf"; + +var gMIMEService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService); + +/** + * Check that if we open a PDF with octet-stream mimetype, it can load + * PDF.js . + */ +add_task(async function test_octet_stream_opens_pdfjs() { + await SpecialPowers.pushPrefEnv({ set: [["pdfjs.handleOctetStream", true]] }); + let handlerInfo = gMIMEService.getFromTypeAndExtension( + "application/pdf", + "pdf" + ); + + // Make sure pdf.js is the default handler. + is( + handlerInfo.alwaysAskBeforeHandling, + false, + "pdf handler defaults to always-ask is false" + ); + is( + handlerInfo.preferredAction, + Ci.nsIHandlerInfo.handleInternally, + "pdf handler defaults to internal" + ); + + await BrowserTestUtils.withNewTab( + { gBrowser, url: "about:blank" }, + async function (newTabBrowser) { + await waitForPdfJS(newTabBrowser, PDF_URL); + is(newTabBrowser.currentURI.spec, PDF_URL, "Should load pdfjs"); + } + ); +}); + +/** + * Check that if the octet-stream thing is in a frame, we don't load it inside PDF.js + */ +add_task(async function test_octet_stream_in_frame() { + await SpecialPowers.pushPrefEnv({ + set: [["pdfjs.handleOctetStream", true]], + }); + + let handlerInfo = gMIMEService.getFromTypeAndExtension( + "application/pdf", + "pdf" + ); + + // Make sure pdf.js is the default handler. + is( + handlerInfo.alwaysAskBeforeHandling, + false, + "pdf handler defaults to always-ask is false" + ); + is( + handlerInfo.preferredAction, + Ci.nsIHandlerInfo.handleInternally, + "pdf handler defaults to internal" + ); + + let downloadsPanelPromise = BrowserTestUtils.waitForEvent( + DownloadsPanel.panel, + "popupshown" + ); + + // Once downloaded, the PDF will be opened as a file:// URI in a new tab + let previewTabPromise = BrowserTestUtils.waitForNewTab( + gBrowser, + url => { + let uri = NetUtil.newURI(url); + return uri.scheme == "file" && uri.spec.endsWith(".pdf"); + }, + false, // dont wait for load + true // any tab, not just the next one + ); + + await BrowserTestUtils.withNewTab( + { gBrowser, url: `data:text/html,