diff options
Diffstat (limited to 'toolkit/components/pdfjs/test')
20 files changed, 1497 insertions, 0 deletions
diff --git a/toolkit/components/pdfjs/test/.eslintrc.js b/toolkit/components/pdfjs/test/.eslintrc.js new file mode 100644 index 0000000000..1779fd7f1c --- /dev/null +++ b/toolkit/components/pdfjs/test/.eslintrc.js @@ -0,0 +1,5 @@ +"use strict"; + +module.exports = { + extends: ["plugin:mozilla/browser-test"], +}; diff --git a/toolkit/components/pdfjs/test/browser.ini b/toolkit/components/pdfjs/test/browser.ini new file mode 100644 index 0000000000..1edf4699cc --- /dev/null +++ b/toolkit/components/pdfjs/test/browser.ini @@ -0,0 +1,35 @@ +[DEFAULT] +support-files = + file_pdfjs_test.pdf + head.js + +[browser_pdfjs_fill_login.js] +support-files = + file_pdfjs_form.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_main.js] +[browser_pdfjs_navigation.js] +[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] +[browser_pdfjs_octet_stream.js] +support-files = + file_pdfjs_object_stream.pdf + file_pdfjs_object_stream.pdf^headers^ +[browser_pdfjs_savedialog.js] +skip-if = verify +[browser_pdfjs_saveas.js] +support-files = + !/toolkit/content/tests/browser/common/mockTransfer.js +[browser_pdfjs_views.js] +[browser_pdfjs_zoom.js] +skip-if = (verify && debug && (os == 'win')) 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..8d9c022705 --- /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 <browser> 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_find.js b/toolkit/components/pdfjs/test/browser_pdfjs_find.js new file mode 100644 index 0000000000..27502a2d26 --- /dev/null +++ b/toolkit/components/pdfjs/test/browser_pdfjs_find.js @@ -0,0 +1,23 @@ +/* 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"; + +add_task(async function test_find_octet_stream_pdf() { + await BrowserTestUtils.withNewTab(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`); + } + }); +}); 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..722a884e8b --- /dev/null +++ b/toolkit/components/pdfjs/test/browser_pdfjs_force_opening_files.js @@ -0,0 +1,68 @@ +/* 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!"); + + 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..f424a1ebdb --- /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("renderInteractiveForms"); + let renderForms = Services.prefs.getBoolPref("pdfjs.renderInteractiveForms"); + + is( + prefType, + Ci.nsIPrefBranch.PREF_BOOL, + "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.renderInteractiveForms", true]], + }); + await waitForPdfJSAnnotationLayer( + browser, + TESTROOT + "file_pdfjs_form.pdf" + ); + await SpecialPowers.spawn( + browser, + [true /* formsEnabled */], + checkFormState + ); + // Now disable the forms. + await SpecialPowers.pushPrefEnv({ + set: [["pdfjs.renderInteractiveForms", false]], + }); + await waitForPdfJSAnnotationLayer( + browser, + TESTROOT + "file_pdfjs_form.pdf" + ); + await SpecialPowers.spawn( + browser, + [false /* formsEnabled */], + checkFormState + ); + } + ); +}); 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..dda1615c0b --- /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_navigation.js b/toolkit/components/pdfjs/test/browser_pdfjs_navigation.js new file mode 100644 index 0000000000..7e106e745e --- /dev/null +++ b/toolkit/components/pdfjs/test/browser_pdfjs_navigation.js @@ -0,0 +1,320 @@ +/* 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 = document.createEvent("KeyboardEvent"); + ev.initKeyEvent( + "keydown", + true, + true, + null, + false, + false, + false, + false, + test.action.keyCode, + 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_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..a6d4cf0a45 --- /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.loadURI(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..bb6a228532 --- /dev/null +++ b/toolkit/components/pdfjs/test/browser_pdfjs_octet_stream.js @@ -0,0 +1,81 @@ +/* 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_downloads() { + 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 dialogPromise = BrowserTestUtils.domWindowOpenedAndLoaded(); + await BrowserTestUtils.withNewTab( + { gBrowser, url: `data:text/html,<iframe src='${PDF_URL}'>` }, + async function(newTabBrowser) { + let dialogWin = await dialogPromise; + ok(dialogWin, "Should have a dialog asking what to do."); + dialogWin.close(); // This is going to cancel the dialog. + } + ); +}); diff --git a/toolkit/components/pdfjs/test/browser_pdfjs_saveas.js b/toolkit/components/pdfjs/test/browser_pdfjs_saveas.js new file mode 100644 index 0000000000..03ee7012b9 --- /dev/null +++ b/toolkit/components/pdfjs/test/browser_pdfjs_saveas.js @@ -0,0 +1,135 @@ +/* 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 = "http://example.com/browser/" + RELATIVE_DIR; + +var MockFilePicker = SpecialPowers.MockFilePicker; +MockFilePicker.init(window); + +/* import-globals-from ../../../content/tests/browser/common/mockTransfer.js */ +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/toolkit/content/tests/browser/common/mockTransfer.js", + this +); + +function createTemporarySaveDirectory() { + var saveDir = Services.dirsvc.get("TmpD", Ci.nsIFile); + saveDir.append("testsavedir"); + if (!saveDir.exists()) { + saveDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755); + } + return saveDir; +} + +function createPromiseForTransferComplete(expectedFileName, destFile) { + return new Promise(resolve => { + MockFilePicker.showCallback = fp => { + info("Filepicker shown, checking filename"); + is(fp.defaultString, expectedFileName, "Filename should be correct."); + let fileName = fp.defaultString; + destFile.append(fileName); + + MockFilePicker.setFiles([destFile]); + MockFilePicker.filterIndex = 0; // kSaveAsType_Complete + + MockFilePicker.showCallback = null; + mockTransferCallback = function(downloadSuccess) { + ok(downloadSuccess, "File should have been downloaded successfully"); + mockTransferCallback = () => {}; + resolve(); + }; + }; + }); +} + +let tempDir = createTemporarySaveDirectory(); + +add_task(async function setup() { + mockTransferRegisterer.register(); + registerCleanupFunction(function() { + mockTransferRegisterer.unregister(); + MockFilePicker.cleanup(); + tempDir.remove(true); + }); +}); + +/** + * Check triggering "Save Page As" on a non-forms PDF opens the "Save As" dialog + * and successfully saves the file. + */ +add_task(async function test_pdf_saveas() { + await BrowserTestUtils.withNewTab( + { gBrowser, url: "about:blank" }, + async function(browser) { + await waitForPdfJS(browser, TESTROOT + "file_pdfjs_test.pdf"); + let destFile = tempDir.clone(); + MockFilePicker.displayDirectory = tempDir; + let fileSavedPromise = createPromiseForTransferComplete( + "file_pdfjs_test.pdf", + destFile + ); + saveBrowser(browser); + await fileSavedPromise; + } + ); +}); + +/** + * Check triggering "Save Page As" on a PDF with forms that has been modified + * does the following: + * 1) opens the "Save As" dialog + * 2) successfully saves the file + * 3) the new file contains the new form data + */ +add_task(async function test_pdf_saveas_forms() { + await SpecialPowers.pushPrefEnv({ + set: [["pdfjs.renderInteractiveForms", true]], + }); + let destFile = tempDir.clone(); + await BrowserTestUtils.withNewTab( + { gBrowser, url: "about:blank" }, + async function(browser) { + await waitForPdfJSAnnotationLayer( + browser, + TESTROOT + "file_pdfjs_form.pdf" + ); + // Fill in the form input field. + await SpecialPowers.spawn(browser, [], async function() { + let formInput = content.document.querySelector( + "#viewerContainer input" + ); + ok(formInput, "PDF contains text field."); + is(formInput.value, "", "Text field is empty to start."); + formInput.value = "test"; + formInput.dispatchEvent(new content.window.Event("input")); + }); + + MockFilePicker.displayDirectory = tempDir; + let fileSavedPromise = createPromiseForTransferComplete( + "file_pdfjs_form.pdf", + destFile + ); + saveBrowser(browser); + await fileSavedPromise; + } + ); + + // Now that the file has been modified and saved, load it to verify the form + // data persisted. + await BrowserTestUtils.withNewTab( + { gBrowser, url: "about:blank" }, + async function(browser) { + await waitForPdfJSAnnotationLayer(browser, NetUtil.newURI(destFile).spec); + await SpecialPowers.spawn(browser, [], async function() { + let formInput = content.document.querySelector( + "#viewerContainer input" + ); + ok(formInput, "PDF contains text field."); + is(formInput.value, "test", "Text field is filled in."); + }); + } + ); +}); diff --git a/toolkit/components/pdfjs/test/browser_pdfjs_savedialog.js b/toolkit/components/pdfjs/test/browser_pdfjs_savedialog.js new file mode 100644 index 0000000000..c59985a5cf --- /dev/null +++ b/toolkit/components/pdfjs/test/browser_pdfjs_savedialog.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; + +function test() { + var oldAction = changeMimeHandler(Ci.nsIHandlerInfo.useSystemDefault, true); + var tab = BrowserTestUtils.addTab(gBrowser, TESTROOT + "file_pdfjs_test.pdf"); + // Test: "Open with" dialog comes up when pdf.js is not selected as the default + // handler. + addWindowListener( + "chrome://mozapps/content/downloads/unknownContentType.xhtml", + finish + ); + + waitForExplicitFinish(); + registerCleanupFunction(function() { + changeMimeHandler(oldAction[0], oldAction[1]); + gBrowser.removeTab(tab); + }); +} + +function addWindowListener(aURL, aCallback) { + Services.wm.addListener({ + onOpenWindow(aXULWindow) { + info("window opened, waiting for focus"); + Services.wm.removeListener(this); + + 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) {}, + }); +} diff --git a/toolkit/components/pdfjs/test/browser_pdfjs_views.js b/toolkit/components/pdfjs/test/browser_pdfjs_views.js new file mode 100644 index 0000000000..f2d95c02a6 --- /dev/null +++ b/toolkit/components/pdfjs/test/browser_pdfjs_views.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; + +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 waitForPdfJS(browser, TESTROOT + "file_pdfjs_test.pdf"); + + await SpecialPowers.spawn(browser, [], async function() { + Assert.ok( + content.document.querySelector("div#viewer"), + "document content has viewer UI" + ); + + // open sidebar + var sidebar = content.document.querySelector("button#sidebarToggle"); + var outerContainer = content.document.querySelector( + "div#outerContainer" + ); + + sidebar.click(); + Assert.ok( + outerContainer.classList.contains("sidebarOpen"), + "sidebar opens on click" + ); + + // check that thumbnail view is open + var thumbnailView = content.document.querySelector("div#thumbnailView"); + var outlineView = content.document.querySelector("div#outlineView"); + + Assert.equal( + thumbnailView.getAttribute("class"), + null, + "Initial view is thumbnail view" + ); + Assert.equal( + outlineView.getAttribute("class"), + "hidden", + "Outline view is hidden initially" + ); + + // switch to outline view + var viewOutlineButton = content.document.querySelector( + "button#viewOutline" + ); + viewOutlineButton.click(); + + Assert.equal( + thumbnailView.getAttribute("class"), + "hidden", + "Thumbnail view is hidden when outline is selected" + ); + Assert.equal( + outlineView.getAttribute("class"), + "", + "Outline view is visible when selected" + ); + + // switch back to thumbnail view + var viewThumbnailButton = content.document.querySelector( + "button#viewThumbnail" + ); + viewThumbnailButton.click(); + + Assert.equal( + thumbnailView.getAttribute("class"), + "", + "Thumbnail view is visible when selected" + ); + Assert.equal( + outlineView.getAttribute("class"), + "hidden", + "Outline view is hidden when thumbnail is selected" + ); + + sidebar.click(); + + var viewer = content.wrappedJSObject.PDFViewerApplication; + await viewer.close(); + }); + } + ); +}); diff --git a/toolkit/components/pdfjs/test/browser_pdfjs_zoom.js b/toolkit/components/pdfjs/test/browser_pdfjs_zoom.js new file mode 100644 index 0000000000..0d55c30fb8 --- /dev/null +++ b/toolkit/components/pdfjs/test/browser_pdfjs_zoom.js @@ -0,0 +1,272 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +requestLongerTimeout(2); + +const RELATIVE_DIR = "toolkit/components/pdfjs/test/"; +const TESTROOT = "http://example.com/browser/" + RELATIVE_DIR; + +const TESTS = [ + { + action: { + selector: "button#zoomIn", + event: "click", + }, + expectedZoom: 1, // 1 - zoom in + message: "Zoomed in using the '+' (zoom in) button", + }, + + { + action: { + selector: "button#zoomOut", + event: "click", + }, + expectedZoom: -1, // -1 - zoom out + message: "Zoomed out using the '-' (zoom out) button", + }, + + { + action: { + keyboard: true, + keyCode: 61, + event: "+", + }, + expectedZoom: 1, // 1 - zoom in + message: "Zoomed in using the CTRL++ keys", + }, + + { + action: { + keyboard: true, + keyCode: 109, + event: "-", + }, + expectedZoom: -1, // -1 - zoom out + message: "Zoomed out using the CTRL+- keys", + }, + + { + action: { + selector: "select#scaleSelect", + index: 5, + event: "change", + }, + expectedZoom: -1, // -1 - zoom out + message: "Zoomed using the zoom picker", + }, +]; + +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#zoom=100" + ); + + await SpecialPowers.spawn(newTabBrowser, [TESTS], async function( + contentTESTS + ) { + let document = content.document; + + function waitForRender() { + return new Promise(resolve => { + document.addEventListener( + "pagerendered", + function onPageRendered(e) { + if (e.detail.pageNumber !== 1) { + return; + } + + document.removeEventListener( + "pagerendered", + onPageRendered, + true + ); + resolve(); + }, + true + ); + }); + } + + // check that PDF is opened with internal viewer + Assert.ok( + content.document.querySelector("div#viewer"), + "document content has viewer UI" + ); + + let initialWidth, previousWidth; + initialWidth = previousWidth = parseInt( + content.document.querySelector("div.page[data-page-number='1']").style + .width + ); + + for (let subTest of contentTESTS) { + // We zoom using an UI element + var ev; + if (subTest.action.selector) { + // Get the element and trigger the action for changing the zoom + var el = document.querySelector(subTest.action.selector); + Assert.ok( + el, + "Element '" + subTest.action.selector + "' has been found" + ); + + if (subTest.action.index) { + el.selectedIndex = subTest.action.index; + } + + // Dispatch the event for changing the zoom + ev = new content.Event(subTest.action.event); + } else { + // We zoom using keyboard + // Simulate key press + ev = new content.KeyboardEvent("keydown", { + key: subTest.action.event, + keyCode: subTest.action.keyCode, + ctrlKey: true, + }); + el = content; + } + + el.dispatchEvent(ev); + await waitForRender(); + + var pageZoomScale = content.document.querySelector( + "select#scaleSelect" + ); + + // The zoom value displayed in the zoom select + var zoomValue = + pageZoomScale.options[pageZoomScale.selectedIndex].innerHTML; + + let pageContainer = content.document.querySelector( + "div.page[data-page-number='1']" + ); + let actualWidth = parseInt(pageContainer.style.width); + + // the actual zoom of the PDF document + let computedZoomValue = + parseInt((actualWidth / initialWidth).toFixed(2) * 100) + "%"; + Assert.equal( + computedZoomValue, + zoomValue, + "Content has correct zoom" + ); + + // Check that document zooms in the expected way (in/out) + let zoom = (actualWidth - previousWidth) * subTest.expectedZoom; + Assert.ok(zoom > 0, subTest.message); + + previousWidth = actualWidth; + } + + var viewer = content.wrappedJSObject.PDFViewerApplication; + await viewer.close(); + }); + } + ); +}); + +// Performs a SpecialPowers.spawn round-trip to ensure that any setup +// that needs to be done in the content process by any pending tasks has +// a chance to complete before continuing. +function waitForRoundTrip(browser) { + return SpecialPowers.spawn(browser, [], () => {}); +} + +async function waitForRenderAndGetWidth(newTabBrowser) { + return SpecialPowers.spawn(newTabBrowser, [], async function() { + function waitForRender(document) { + return new Promise(resolve => { + document.addEventListener( + "pagerendered", + function onPageRendered(e) { + if (e.detail.pageNumber !== 1) { + return; + } + + document.removeEventListener("pagerendered", onPageRendered, true); + resolve(); + }, + true + ); + }); + } + // check that PDF is opened with internal viewer + Assert.ok( + content.document.querySelector("div#viewer"), + "document content has viewer UI" + ); + + await waitForRender(content.document); + + return parseInt( + content.document.querySelector("div.page[data-page-number='1']").style + .width + ); + }); +} + +add_task(async function test_browser_zoom() { + await BrowserTestUtils.withNewTab( + { gBrowser, url: "about:blank" }, + async function(newTabBrowser) { + await waitForPdfJS(newTabBrowser, TESTROOT + "file_pdfjs_test.pdf"); + + const initialWidth = await waitForRenderAndGetWidth(newTabBrowser); + + // Zoom in + let newWidthPromise = waitForRenderAndGetWidth(newTabBrowser); + await waitForRoundTrip(newTabBrowser); + FullZoom.enlarge(); + ok( + (await newWidthPromise) > initialWidth, + "Zoom in makes the page bigger." + ); + + // Reset + newWidthPromise = waitForRenderAndGetWidth(newTabBrowser); + await waitForRoundTrip(newTabBrowser); + FullZoom.reset(); + is(await newWidthPromise, initialWidth, "Zoom reset restores page."); + + // Zoom out + newWidthPromise = waitForRenderAndGetWidth(newTabBrowser); + await waitForRoundTrip(newTabBrowser); + FullZoom.reduce(); + ok( + (await newWidthPromise) < initialWidth, + "Zoom out makes the page smaller." + ); + + // Clean-up after the PDF viewer. + await SpecialPowers.spawn(newTabBrowser, [], function() { + const viewer = content.wrappedJSObject.PDFViewerApplication; + return viewer.close(); + }); + } + ); +}); diff --git a/toolkit/components/pdfjs/test/file_pdfjs_form.pdf b/toolkit/components/pdfjs/test/file_pdfjs_form.pdf Binary files differnew file mode 100644 index 0000000000..c6eff988a6 --- /dev/null +++ b/toolkit/components/pdfjs/test/file_pdfjs_form.pdf diff --git a/toolkit/components/pdfjs/test/file_pdfjs_object_stream.pdf b/toolkit/components/pdfjs/test/file_pdfjs_object_stream.pdf Binary files differnew file mode 100644 index 0000000000..89066463f1 --- /dev/null +++ b/toolkit/components/pdfjs/test/file_pdfjs_object_stream.pdf diff --git a/toolkit/components/pdfjs/test/file_pdfjs_object_stream.pdf^headers^ b/toolkit/components/pdfjs/test/file_pdfjs_object_stream.pdf^headers^ new file mode 100644 index 0000000000..ff8fb3126e --- /dev/null +++ b/toolkit/components/pdfjs/test/file_pdfjs_object_stream.pdf^headers^ @@ -0,0 +1,2 @@ +Content-Type: application/octet-stream + diff --git a/toolkit/components/pdfjs/test/file_pdfjs_test.pdf b/toolkit/components/pdfjs/test/file_pdfjs_test.pdf Binary files differnew file mode 100644 index 0000000000..7ad87e3c2e --- /dev/null +++ b/toolkit/components/pdfjs/test/file_pdfjs_test.pdf diff --git a/toolkit/components/pdfjs/test/head.js b/toolkit/components/pdfjs/test/head.js new file mode 100644 index 0000000000..bca608e1f0 --- /dev/null +++ b/toolkit/components/pdfjs/test/head.js @@ -0,0 +1,71 @@ +async function waitForPdfJS(browser, url) { + await SpecialPowers.pushPrefEnv({ + set: [["pdfjs.eventBusDispatchToDOM", true]], + }); + // Runs tests after all "load" event handlers have fired off + let loadPromise = BrowserTestUtils.waitForContentEvent( + browser, + "documentloaded", + false, + null, + true + ); + await SpecialPowers.spawn(browser, [url], contentUrl => { + content.location = contentUrl; + }); + return loadPromise; +} + +async function waitForPdfJSAnnotationLayer(browser, url) { + await SpecialPowers.pushPrefEnv({ + set: [["pdfjs.eventBusDispatchToDOM", true]], + }); + let loadPromise = BrowserTestUtils.waitForContentEvent( + browser, + "annotationlayerrendered", + false, + null, + true + ); + await SpecialPowers.spawn(browser, [url], contentUrl => { + content.location = contentUrl; + }); + return loadPromise; +} + +function changeMimeHandler(preferredAction, alwaysAskBeforeHandling) { + let handlerService = Cc[ + "@mozilla.org/uriloader/handler-service;1" + ].getService(Ci.nsIHandlerService); + let mimeService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService); + let handlerInfo = mimeService.getFromTypeAndExtension( + "application/pdf", + "pdf" + ); + var oldAction = [ + handlerInfo.preferredAction, + handlerInfo.alwaysAskBeforeHandling, + ]; + + // Change and save mime handler settings + handlerInfo.alwaysAskBeforeHandling = alwaysAskBeforeHandling; + handlerInfo.preferredAction = preferredAction; + handlerService.store(handlerInfo); + + // Refresh data + handlerInfo = mimeService.getFromTypeAndExtension("application/pdf", "pdf"); + + // Test: Mime handler was updated + is( + handlerInfo.alwaysAskBeforeHandling, + alwaysAskBeforeHandling, + "always-ask prompt change successful" + ); + is( + handlerInfo.preferredAction, + preferredAction, + "mime handler change successful" + ); + + return oldAction; +} |