summaryrefslogtreecommitdiffstats
path: root/toolkit/components/printing/tests
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /toolkit/components/printing/tests
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/printing/tests')
-rw-r--r--toolkit/components/printing/tests/.eslintrc.js5
-rw-r--r--toolkit/components/printing/tests/browser.ini66
-rw-r--r--toolkit/components/printing/tests/browser_cancel_close_print.js28
-rw-r--r--toolkit/components/printing/tests/browser_destination_change.js152
-rw-r--r--toolkit/components/printing/tests/browser_empty_paper_sizes.js97
-rw-r--r--toolkit/components/printing/tests/browser_modal_print.js247
-rw-r--r--toolkit/components/printing/tests/browser_modal_resize.js155
-rw-r--r--toolkit/components/printing/tests/browser_page_change_print_original.js88
-rw-r--r--toolkit/components/printing/tests/browser_pdf_hidden_settings.js39
-rw-r--r--toolkit/components/printing/tests/browser_pdf_printer_settings.js116
-rw-r--r--toolkit/components/printing/tests/browser_preview_in_container.js60
-rw-r--r--toolkit/components/printing/tests/browser_preview_navigation.js424
-rw-r--r--toolkit/components/printing/tests/browser_preview_print_coop.js40
-rw-r--r--toolkit/components/printing/tests/browser_preview_print_simplify_non_article.js102
-rw-r--r--toolkit/components/printing/tests/browser_preview_switch_print_selected.js126
-rw-r--r--toolkit/components/printing/tests/browser_print_bcg_id_overflow.js61
-rw-r--r--toolkit/components/printing/tests/browser_print_context_menu.js68
-rw-r--r--toolkit/components/printing/tests/browser_print_copies.js41
-rw-r--r--toolkit/components/printing/tests/browser_print_duplex.js174
-rw-r--r--toolkit/components/printing/tests/browser_print_in_container.js38
-rw-r--r--toolkit/components/printing/tests/browser_print_margins.js802
-rw-r--r--toolkit/components/printing/tests/browser_print_page_range.js471
-rw-r--r--toolkit/components/printing/tests/browser_print_paper_sizes.js120
-rw-r--r--toolkit/components/printing/tests/browser_print_scaling.js46
-rw-r--r--toolkit/components/printing/tests/browser_print_selection.js160
-rw-r--r--toolkit/components/printing/tests/browser_sheet_count.js228
-rw-r--r--toolkit/components/printing/tests/browser_system_dialog_subdialog_hidden.js105
-rw-r--r--toolkit/components/printing/tests/browser_ui_labels.js23
-rw-r--r--toolkit/components/printing/tests/browser_window_print.js189
-rw-r--r--toolkit/components/printing/tests/file_coop_header.html6
-rw-r--r--toolkit/components/printing/tests/file_coop_header.html^headers^1
-rw-r--r--toolkit/components/printing/tests/file_page_change_print_original_1.html8
-rw-r--r--toolkit/components/printing/tests/file_page_change_print_original_2.html1
-rw-r--r--toolkit/components/printing/tests/file_pdf.pdf12
-rw-r--r--toolkit/components/printing/tests/file_print.html5
-rw-r--r--toolkit/components/printing/tests/file_window_print.html29
-rw-r--r--toolkit/components/printing/tests/file_window_print_delayed_during_load.html13
-rw-r--r--toolkit/components/printing/tests/file_window_print_sandboxed_iframe.html7
-rw-r--r--toolkit/components/printing/tests/head.js444
-rw-r--r--toolkit/components/printing/tests/longerArticle.html21
-rw-r--r--toolkit/components/printing/tests/simplifyArticleSample.html16
-rw-r--r--toolkit/components/printing/tests/simplifyNonArticleSample.html9
42 files changed, 4843 insertions, 0 deletions
diff --git a/toolkit/components/printing/tests/.eslintrc.js b/toolkit/components/printing/tests/.eslintrc.js
new file mode 100644
index 0000000000..03ec23d8d0
--- /dev/null
+++ b/toolkit/components/printing/tests/.eslintrc.js
@@ -0,0 +1,5 @@
+"use strict";
+
+module.exports = {
+ extends: ["plugin:mozilla/chrome-test", "plugin:mozilla/browser-test"],
+};
diff --git a/toolkit/components/printing/tests/browser.ini b/toolkit/components/printing/tests/browser.ini
new file mode 100644
index 0000000000..8a1a61cc94
--- /dev/null
+++ b/toolkit/components/printing/tests/browser.ini
@@ -0,0 +1,66 @@
+[DEFAULT]
+support-files =
+ head.js
+ simplifyArticleSample.html
+
+[browser_cancel_close_print.js]
+[browser_destination_change.js]
+[browser_empty_paper_sizes.js]
+
+[browser_modal_print.js]
+
+[browser_modal_resize.js]
+
+[browser_page_change_print_original.js]
+support-files =
+ file_page_change_print_original_1.html
+ file_page_change_print_original_2.html
+skip-if = os == "mac"
+
+[browser_pdf_hidden_settings.js]
+support-files =
+ file_pdf.pdf
+[browser_print_copies.js]
+[browser_print_paper_sizes.js]
+[browser_pdf_printer_settings.js]
+[browser_print_bcg_id_overflow.js]
+[browser_print_context_menu.js]
+[browser_print_duplex.js]
+skip-if = (verify && (os == 'mac')) # bug 1675609
+[browser_print_margins.js]
+[browser_print_selection.js]
+[browser_print_page_range.js]
+[browser_print_scaling.js]
+[browser_sheet_count.js]
+[browser_ui_labels.js]
+[browser_window_print.js]
+support-files =
+ file_window_print.html
+ file_window_print_delayed_during_load.html
+ file_window_print_sandboxed_iframe.html
+
+[browser_preview_in_container.js]
+support-files =
+ file_print.html
+
+[browser_preview_navigation.js]
+support-files =
+ longerArticle.html
+
+[browser_preview_print_simplify_non_article.js]
+support-files =
+ simplifyNonArticleSample.html
+skip-if = os == "mac" || (verify && (os == 'win' || os == 'linux'))
+
+[browser_preview_print_coop.js]
+support-files =
+ file_coop_header.html
+ file_coop_header.html^headers^
+
+[browser_preview_switch_print_selected.js]
+skip-if = os == "mac" || (verify && !debug && (os == 'linux'))
+
+[browser_print_in_container.js]
+skip-if = tsan # Bug 1683730
+
+[browser_system_dialog_subdialog_hidden.js]
diff --git a/toolkit/components/printing/tests/browser_cancel_close_print.js b/toolkit/components/printing/tests/browser_cancel_close_print.js
new file mode 100644
index 0000000000..7fc311a33f
--- /dev/null
+++ b/toolkit/components/printing/tests/browser_cancel_close_print.js
@@ -0,0 +1,28 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function testCloseWhilePrinting() {
+ await PrintHelper.withTestPage(async helper => {
+ await helper.startPrint();
+ await helper.setupMockPrint();
+ helper.mockFilePicker("output.pdf");
+
+ await helper.withClosingFn(async () => {
+ let cancelButton = helper.get("cancel-button");
+ is(
+ helper.doc.l10n.getAttributes(cancelButton).id,
+ "printui-cancel-button",
+ "The cancel button is using the 'cancel' string"
+ );
+ EventUtils.sendKey("return", helper.win);
+ is(
+ helper.doc.l10n.getAttributes(cancelButton).id,
+ "printui-close-button",
+ "The cancel button is using the 'close' string"
+ );
+ helper.resolvePrint();
+ });
+ });
+});
diff --git a/toolkit/components/printing/tests/browser_destination_change.js b/toolkit/components/printing/tests/browser_destination_change.js
new file mode 100644
index 0000000000..a9f1dd0f82
--- /dev/null
+++ b/toolkit/components/printing/tests/browser_destination_change.js
@@ -0,0 +1,152 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+let pdfPrinterName = "Mozilla Save to PDF";
+let fastPrinterName = "Fast";
+let slowPrinterName = "Slow";
+
+async function setupPrinters(helper) {
+ helper.addMockPrinter(fastPrinterName);
+
+ let resolvePrinterInfo;
+ helper.addMockPrinter({
+ name: slowPrinterName,
+ printerInfoPromise: new Promise(resolve => {
+ resolvePrinterInfo = resolve;
+ }),
+ });
+
+ await SpecialPowers.pushPrefEnv({
+ set: [["print.printer_Slow.print_orientation", 1]],
+ });
+
+ return resolvePrinterInfo;
+}
+
+async function changeDestination(helper, dir) {
+ let picker = helper.get("printer-picker");
+ let changed = BrowserTestUtils.waitForEvent(picker, "change");
+ picker.focus();
+ EventUtils.sendKey("space", helper.win);
+ EventUtils.sendKey(dir, helper.win);
+ EventUtils.sendKey("return", helper.win);
+ await changed;
+}
+
+function assertFormEnabled(form) {
+ for (let element of form.elements) {
+ if (element.hasAttribute("disallowed")) {
+ ok(element.disabled, `${element.id} is disallowed`);
+ } else {
+ ok(!element.disabled, `${element.id} is enabled`);
+ }
+ }
+}
+
+function assertFormDisabled(form) {
+ for (let element of form.elements) {
+ if (element.id == "printer-picker" || element.id == "cancel-button") {
+ ok(!element.disabled, `${element.id} is enabled`);
+ } else {
+ ok(element.disabled, `${element.id} is disabled`);
+ }
+ }
+}
+
+add_task(async function testSlowDestinationChange() {
+ await PrintHelper.withTestPage(async helper => {
+ let resolvePrinterInfo = await setupPrinters(helper);
+ await helper.startPrint();
+
+ let destinationPicker = helper.get("printer-picker");
+ let printForm = helper.get("print");
+
+ info("Changing to fast printer should change settings");
+ await helper.assertSettingsChanged(
+ { printerName: pdfPrinterName, orientation: 0 },
+ { printerName: fastPrinterName, orientation: 0 },
+ async () => {
+ await changeDestination(helper, "down");
+ is(destinationPicker.value, fastPrinterName, "Fast printer selected");
+ // Wait one frame so the print settings promises resolve.
+ await helper.awaitAnimationFrame();
+ assertFormEnabled(printForm);
+ }
+ );
+
+ info("Changing to slow printer should not change settings yet");
+ await helper.assertSettingsNotChanged(
+ { printerName: fastPrinterName, orientation: 0 },
+ async () => {
+ await changeDestination(helper, "down");
+ is(destinationPicker.value, slowPrinterName, "Slow printer selected");
+ // Wait one frame, since the settings are blocked on resolvePrinterInfo
+ // the settings shouldn't change.
+ await helper.awaitAnimationFrame();
+ assertFormDisabled(printForm);
+ }
+ );
+
+ await helper.assertSettingsChanged(
+ { printerName: fastPrinterName, orientation: 0 },
+ { printerName: slowPrinterName, orientation: 1 },
+ async () => {
+ resolvePrinterInfo();
+ await helper.waitForSettingsEvent();
+ assertFormEnabled(printForm);
+ }
+ );
+
+ await helper.closeDialog();
+ });
+});
+
+add_task(async function testSwitchAwayFromSlowDestination() {
+ await PrintHelper.withTestPage(async helper => {
+ let resolvePrinterInfo = await setupPrinters(helper);
+ await helper.startPrint();
+
+ let destinationPicker = helper.get("printer-picker");
+ let printForm = helper.get("print");
+
+ // Load the fast printer.
+ await helper.waitForSettingsEvent(async () => {
+ await changeDestination(helper, "down");
+ });
+ await helper.awaitAnimationFrame();
+ assertFormEnabled(printForm);
+
+ // "Load" the slow printer.
+ await changeDestination(helper, "down");
+ is(destinationPicker.value, slowPrinterName, "Slow printer selected");
+ // Wait an animation frame, since there's no settings event.
+ await helper.awaitAnimationFrame();
+ assertFormDisabled(printForm);
+
+ // Switch back to the fast printer.
+ await helper.waitForSettingsEvent(async () => {
+ await changeDestination(helper, "up");
+ });
+ helper.assertSettingsMatch({
+ printerName: fastPrinterName,
+ orientation: 0,
+ });
+
+ await helper.awaitAnimationFrame();
+ assertFormEnabled(printForm);
+
+ // Let the slow printer settings resolve, the orientation shouldn't change.
+ resolvePrinterInfo();
+ // Wait so the settings event can trigger, if this case isn't handled.
+ await helper.awaitAnimationFrame();
+ helper.assertSettingsMatch({
+ printerName: fastPrinterName,
+ orientation: 0,
+ });
+ assertFormEnabled(printForm);
+
+ await helper.closeDialog();
+ });
+});
diff --git a/toolkit/components/printing/tests/browser_empty_paper_sizes.js b/toolkit/components/printing/tests/browser_empty_paper_sizes.js
new file mode 100644
index 0000000000..8fcf3c3c66
--- /dev/null
+++ b/toolkit/components/printing/tests/browser_empty_paper_sizes.js
@@ -0,0 +1,97 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function testSanityCheckPaperList() {
+ const mockPrinterName = "Fake Printer";
+ await PrintHelper.withTestPage(async helper => {
+ let paperList = [
+ PrintHelper.createMockPaper({
+ id: "regular",
+ name: "Regular Paper",
+ }),
+ PrintHelper.createMockPaper({
+ id: "large",
+ name: "Large Size",
+ width: 720,
+ height: 1224,
+ }),
+ ];
+ helper.addMockPrinter({ name: mockPrinterName, paperList });
+ await helper.startPrint();
+ await helper.dispatchSettingsChange({ printerName: mockPrinterName });
+ await helper.awaitAnimationFrame();
+
+ is(
+ helper.settings.printerName,
+ mockPrinterName,
+ "The Fake Printer is current printer"
+ );
+ is(
+ Object.values(helper.win.PrintSettingsViewProxy.availablePaperSizes)
+ .length,
+ 2,
+ "There are 2 paper sizes"
+ );
+ ok(
+ helper.win.PrintSettingsViewProxy.availablePaperSizes.regular,
+ "'regular' paper size is available"
+ );
+ ok(
+ helper.win.PrintSettingsViewProxy.availablePaperSizes.large,
+ "'large' paper size is available"
+ );
+ });
+});
+
+add_task(async function testEmptyPaperListGetsFallbackPaperSizes() {
+ const mockPrinterName = "Fake Printer";
+ await PrintHelper.withTestPage(async helper => {
+ helper.addMockPrinter(mockPrinterName);
+ await helper.startPrint();
+
+ is(
+ Object.values(helper.win.PrintSettingsViewProxy.availablePrinters).length,
+ 2,
+ "There are 2 available printers"
+ );
+ ok(
+ helper.win.PrintSettingsViewProxy.availablePrinters[mockPrinterName],
+ "The Fake Printer is one of our availablePrinters"
+ );
+
+ await helper.dispatchSettingsChange({ printerName: mockPrinterName });
+ await helper.awaitAnimationFrame();
+
+ is(
+ helper.settings.printerName,
+ mockPrinterName,
+ "The Fake Printer is current printer"
+ );
+ is(
+ helper.get("printer-picker").value,
+ mockPrinterName,
+ "The Fake Printer is selected"
+ );
+
+ let printerList = Cc["@mozilla.org/gfx/printerlist;1"].createInstance(
+ Ci.nsIPrinterList
+ );
+ let fallbackPaperList = await printerList.fallbackPaperList;
+ let paperPickerSizes = Array.from(
+ helper.get("paper-size-picker").options
+ ).map(o => o.value);
+ for (let paper of fallbackPaperList) {
+ ok(
+ helper.win.PrintSettingsViewProxy.availablePaperSizes[paper.id],
+ "Fallback paper size: " + paper.id + " is available"
+ );
+ ok(
+ paperPickerSizes.includes(paper.id),
+ "There is a paper size options for " + paper.id
+ );
+ }
+ await helper.closeDialog();
+ });
+});
diff --git a/toolkit/components/printing/tests/browser_modal_print.js b/toolkit/components/printing/tests/browser_modal_print.js
new file mode 100644
index 0000000000..a3cbf95d65
--- /dev/null
+++ b/toolkit/components/printing/tests/browser_modal_print.js
@@ -0,0 +1,247 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function assertExpectedPrintPage(helper) {
+ is(
+ helper.sourceURI,
+ PrintHelper.defaultTestPageUrl,
+ "The URL of the browser is the one we expect"
+ );
+}
+
+add_task(async function testModalPrintDialog() {
+ await PrintHelper.withTestPage(async helper => {
+ helper.assertDialogClosed();
+
+ await helper.startPrint();
+
+ helper.assertDialogOpen();
+
+ // Check that we're printing the right page.
+ assertExpectedPrintPage(helper);
+
+ // Close the dialog with Escape.
+ await helper.withClosingFn(() => {
+ EventUtils.synthesizeKey("VK_ESCAPE", {}, helper.win);
+ });
+
+ helper.assertDialogClosed();
+ });
+});
+
+add_task(async function testPrintMultiple() {
+ await PrintHelper.withTestPage(async helper => {
+ helper.assertDialogClosed();
+
+ // First print as usual.
+ await helper.startPrint();
+ helper.assertDialogOpen();
+ assertExpectedPrintPage(helper);
+
+ // Trigger the command a few more times, verify the overlay still exists.
+ await helper.startPrint();
+ helper.assertDialogOpen();
+ await helper.startPrint();
+ helper.assertDialogOpen();
+ await helper.startPrint();
+ helper.assertDialogOpen();
+
+ // Verify it's still the correct page.
+ assertExpectedPrintPage(helper);
+
+ // Make sure we clean up, ideally this would be handled by the helper.
+ await helper.closeDialog();
+ });
+});
+
+add_task(async function testCancelButton() {
+ await PrintHelper.withTestPage(async helper => {
+ helper.assertDialogClosed();
+ await helper.startPrint();
+ helper.assertDialogOpen();
+
+ let cancelButton = helper.doc.querySelector("button[name=cancel]");
+ ok(cancelButton, "Got the cancel button");
+ await helper.withClosingFn(() =>
+ EventUtils.synthesizeMouseAtCenter(cancelButton, {}, helper.win)
+ );
+
+ helper.assertDialogClosed();
+ });
+});
+
+add_task(async function testTabOrder() {
+ await PrintHelper.withTestPage(async helper => {
+ helper.assertDialogClosed();
+ await helper.startPrint();
+ helper.assertDialogOpen();
+
+ const printerPicker = helper.doc.getElementById("printer-picker");
+ is(
+ helper.doc.activeElement,
+ printerPicker,
+ "Initial focus on printer picker"
+ );
+
+ const previewBrowser = document.querySelector(".printPreviewBrowser");
+ ok(previewBrowser, "Got the print preview browser");
+
+ let focused;
+ let navigationShadowRoot = document.querySelector(".printPreviewNavigation")
+ .shadowRoot;
+ for (let buttonId of [
+ "navigateEnd",
+ "navigateNext",
+ "navigatePrevious",
+ "navigateHome",
+ ]) {
+ let button = navigationShadowRoot.getElementById(buttonId);
+ focused = BrowserTestUtils.waitForEvent(button, "focus");
+ await EventUtils.synthesizeKey("KEY_Tab", { shiftKey: true });
+ await focused;
+ }
+
+ focused = BrowserTestUtils.waitForEvent(previewBrowser, "focus");
+ await EventUtils.synthesizeKey("KEY_Tab", { shiftKey: true });
+ await focused;
+ ok(true, "Print preview focused after shift+tab through the paginator");
+
+ focused = BrowserTestUtils.waitForEvent(gNavToolbox, "focus", true);
+ EventUtils.synthesizeKey("KEY_Tab", { shiftKey: true });
+ await focused;
+ ok(true, "Toolbox focused after shift+tab");
+
+ focused = BrowserTestUtils.waitForEvent(previewBrowser, "focus");
+ EventUtils.synthesizeKey("KEY_Tab");
+ await focused;
+ ok(true, "Print preview focused after tab");
+
+ for (let buttonId of [
+ "navigateHome",
+ "navigatePrevious",
+ "navigateNext",
+ "navigateEnd",
+ ]) {
+ let button = navigationShadowRoot.getElementById(buttonId);
+ focused = BrowserTestUtils.waitForEvent(button, "focus");
+ await EventUtils.synthesizeKey("KEY_Tab");
+ await focused;
+ }
+ focused = BrowserTestUtils.waitForEvent(printerPicker, "focus");
+ EventUtils.synthesizeKey("KEY_Tab");
+ await focused;
+ ok(true, "Printer picker focused after tab");
+
+ const lastButtonName = AppConstants.platform == "win" ? "cancel" : "print";
+ const lastButton = helper.doc.querySelector(
+ `button[name=${lastButtonName}]`
+ );
+ focused = BrowserTestUtils.waitForEvent(lastButton, "focus");
+ lastButton.focus();
+ await focused;
+ ok(true, "Last button focused");
+
+ focused = BrowserTestUtils.waitForEvent(gNavToolbox, "focus", true);
+ EventUtils.synthesizeKey("KEY_Tab");
+ await focused;
+ ok(true, "Toolbox focused after tab");
+
+ focused = BrowserTestUtils.waitForEvent(lastButton, "focus");
+ EventUtils.synthesizeKey("KEY_Tab", { shiftKey: true });
+ await focused;
+ ok(true, "Cancel button focused after shift+tab");
+
+ await helper.withClosingFn(() => {
+ EventUtils.synthesizeKey("VK_ESCAPE", {});
+ });
+
+ helper.assertDialogClosed();
+ });
+});
+
+async function testPrintWithEnter(testFn, filename) {
+ await PrintHelper.withTestPage(async helper => {
+ await helper.startPrint();
+
+ let file = helper.mockFilePicker(filename);
+ await testFn(helper);
+ await helper.assertPrintToFile(file, () => {
+ EventUtils.sendKey("return", helper.win);
+ const cancelButton = helper.doc.querySelector(`button[name="cancel"]`);
+ ok(!cancelButton.disabled, "Cancel button is not disabled");
+ const printButton = helper.doc.querySelector(`button[name="print"]`);
+ ok(printButton.disabled, "Print button is disabled");
+ });
+ });
+}
+
+add_task(async function testEnterAfterLoadPrints() {
+ info("Test print without moving focus");
+ await testPrintWithEnter(() => {}, "print_initial_focus.pdf");
+});
+
+add_task(async function testEnterPrintsFromPageRangeSelect() {
+ info("Test print from page range select");
+ await testPrintWithEnter(helper => {
+ let pageRangePicker = helper.get("range-picker");
+ pageRangePicker.focus();
+ is(
+ helper.doc.activeElement,
+ pageRangePicker,
+ "Page range select is focused"
+ );
+ }, "print_page_range_select.pdf");
+});
+
+add_task(async function testEnterPrintsFromOrientation() {
+ info("Test print on Enter from focused orientation input");
+ await testPrintWithEnter(helper => {
+ let portrait = helper.get("portrait");
+ portrait.focus();
+ is(helper.doc.activeElement, portrait, "Portrait is focused");
+ }, "print_orientation_focused.pdf");
+});
+
+add_task(async function testPrintOnNewWindowDoesntClose() {
+ info("Test that printing doesn't close a window with a single tab");
+ await SpecialPowers.pushPrefEnv({
+ set: [["print.tab_modal.enabled", true]],
+ });
+ let win = await BrowserTestUtils.openNewBrowserWindow();
+
+ await PrintHelper.withTestPage(async helper => {
+ await helper.startPrint();
+ let file = helper.mockFilePicker("print_new_window_close.pdf");
+ await helper.assertPrintToFile(file, () => {
+ EventUtils.sendKey("return", helper.win);
+ });
+ });
+ ok(!win.closed, "Shouldn't be closed");
+ await BrowserTestUtils.closeWindow(win);
+ await SpecialPowers.popPrefEnv();
+});
+
+add_task(async function testPrintProgressIndicator() {
+ await PrintHelper.withTestPage(async helper => {
+ await helper.startPrint();
+
+ helper.setupMockPrint();
+
+ let progressIndicator = helper.get("print-progress");
+ ok(progressIndicator.hidden, "Progress indicator is hidden");
+
+ let indicatorShown = BrowserTestUtils.waitForAttributeRemoval(
+ "hidden",
+ progressIndicator
+ );
+ helper.click(helper.get("print-button"));
+ await indicatorShown;
+
+ ok(!progressIndicator.hidden, "Progress indicator is shown on print start");
+
+ await helper.withClosingFn(async () => {
+ await helper.resolvePrint();
+ });
+ });
+});
diff --git a/toolkit/components/printing/tests/browser_modal_resize.js b/toolkit/components/printing/tests/browser_modal_resize.js
new file mode 100644
index 0000000000..4c0ac31b18
--- /dev/null
+++ b/toolkit/components/printing/tests/browser_modal_resize.js
@@ -0,0 +1,155 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function waitForAnimationFrames() {
+ // Wait for 2 animation frames in hopes it's actually done resizing.
+ return new Promise(resolve =>
+ window.requestAnimationFrame(() => window.requestAnimationFrame(resolve))
+ );
+}
+
+async function mouseMoveAndWait(elem) {
+ let mouseMovePromise = BrowserTestUtils.waitForEvent(elem, "mousemove");
+ EventUtils.synthesizeMouseAtCenter(elem, { type: "mousemove" });
+ await mouseMovePromise;
+ await TestUtils.waitForTick();
+}
+
+function closeEnough(actual, expected) {
+ return expected - 1 < actual && actual < expected + 1;
+}
+
+async function resizeWindow(x, y) {
+ window.innerWidth = x;
+ window.innerHeight = y;
+
+ await waitForAnimationFrames();
+
+ await TestUtils.waitForCondition(
+ () => {
+ info(`window is ${window.innerWidth} x ${window.innerHeight}`);
+ if (
+ closeEnough(window.innerWidth, x) &&
+ closeEnough(window.innerHeight, y)
+ ) {
+ return true;
+ }
+ window.innerWidth = x;
+ window.innerHeight = y;
+ return false;
+ },
+ `Wait for ${x}x${y}`,
+ 250
+ );
+}
+
+async function waitForExpectedSize(helper, x, y) {
+ // Wait a few frames, this is generally enough for the resize to happen.
+ await waitForAnimationFrames();
+
+ let isExpectedSize = () => {
+ let box = helper.dialog._box;
+ info(`Dialog is ${box.clientWidth}x${box.clientHeight}`);
+ if (closeEnough(box.clientWidth, x) && closeEnough(box.clientHeight, y)) {
+ // Make sure there's an assertion so the test passes.
+ ok(true, `${box.clientWidth} close enough to ${x}`);
+ ok(true, `${box.clientHeight} close enough to ${y}`);
+ return true;
+ }
+ return false;
+ };
+
+ if (isExpectedSize()) {
+ // We can stop now if we hit the expected size.
+ return;
+ }
+
+ // In verify and debug runs sometimes this takes longer than expected,
+ // fallback to the slow method.
+ await TestUtils.waitForCondition(isExpectedSize, `Wait for ${x}x${y}`);
+}
+
+async function checkPreviewNavigationVisibility(expected) {
+ function isHidden(elem) {
+ // BTU.is_hidden can't handle shadow DOM elements
+ return !elem.getBoundingClientRect().height;
+ }
+
+ let previewStack = document.querySelector(".previewStack");
+ let paginationElem = document.querySelector(".printPreviewNavigation");
+ // move the mouse to a known position, then back to the preview to show the paginator
+ await mouseMoveAndWait(gURLBar.textbox);
+ await mouseMoveAndWait(previewStack);
+
+ ok(
+ BrowserTestUtils.is_visible(paginationElem),
+ "The preview pagination toolbar is visible"
+ );
+ for (let [id, visible] of Object.entries(expected)) {
+ let elem = paginationElem.shadowRoot.querySelector(`#${id}`);
+ if (visible) {
+ ok(!isHidden(elem), `Navigation element ${id} is visible`);
+ } else {
+ ok(isHidden(elem), `Navigation element ${id} is hidden`);
+ }
+ }
+}
+
+add_task(async function testResizing() {
+ await PrintHelper.withTestPage(async helper => {
+ let { innerWidth, innerHeight } = window;
+
+ await resizeWindow(500, 400);
+
+ await helper.startPrint();
+
+ let chromeHeight = window.windowUtils.getBoundsWithoutFlushing(
+ document.getElementById("browser")
+ ).top;
+
+ let initialWidth = 500 - 8;
+ let initialHeight = 400 - 16 - chromeHeight + 5;
+
+ await waitForExpectedSize(helper, initialWidth, initialHeight);
+
+ // check the preview pagination state for this window size
+ await checkPreviewNavigationVisibility({
+ navigateHome: false,
+ navigatePrevious: false,
+ navigateNext: false,
+ navigateEnd: false,
+ sheetIndicator: true,
+ });
+
+ await resizeWindow(600, 500);
+
+ await checkPreviewNavigationVisibility({
+ navigateHome: true,
+ navigatePrevious: true,
+ navigateNext: true,
+ navigateEnd: true,
+ sheetIndicator: true,
+ });
+
+ // 100 wider for window, add back the old 4px padding, it's now 16px * 2.
+ let updatedWidth = initialWidth + 100 + 8 - 32;
+ await waitForExpectedSize(helper, updatedWidth, initialHeight + 100);
+
+ await resizeWindow(1100, 900);
+
+ await waitForExpectedSize(helper, 1000, 650);
+
+ await checkPreviewNavigationVisibility({
+ navigateHome: true,
+ navigatePrevious: true,
+ navigateNext: true,
+ navigateEnd: true,
+ sheetIndicator: true,
+ });
+
+ await helper.closeDialog();
+
+ await resizeWindow(innerWidth, innerHeight);
+ });
+});
diff --git a/toolkit/components/printing/tests/browser_page_change_print_original.js b/toolkit/components/printing/tests/browser_page_change_print_original.js
new file mode 100644
index 0000000000..4b85820466
--- /dev/null
+++ b/toolkit/components/printing/tests/browser_page_change_print_original.js
@@ -0,0 +1,88 @@
+// This file spawns content tasks.
+/* global content */
+
+const TEST_PATH = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "http://example.com"
+);
+
+/**
+ * Verify that if the page contents change after print preview is initialized,
+ * and we re-initialize print preview (e.g. by changing page orientation),
+ * we still show (and will therefore print) the original contents.
+ */
+add_task(async function pp_after_orientation_change() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["print.tab_modal.enabled", false]],
+ });
+
+ const URI = TEST_PATH + "file_page_change_print_original_1.html";
+ // Can only do something if we have a print preview UI:
+ if (AppConstants.platform != "win" && AppConstants.platform != "linux") {
+ ok(true, "Can't test if there's no print preview.");
+ return;
+ }
+
+ // Ensure we get a browserStopped for this browser
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ URI,
+ false,
+ true
+ );
+ let browserToPrint = tab.linkedBrowser;
+ let ppBrowser = PrintPreviewListener.getPrintPreviewBrowser();
+
+ // Get a promise now that resolves when the original tab's location changes.
+ let originalTabNavigated = BrowserTestUtils.browserStopped(browserToPrint);
+
+ // Enter print preview:
+ let printPreviewEntered = PrintHelper.waitForOldPrintPreview(ppBrowser);
+ document.getElementById("cmd_printPreview").doCommand();
+ await printPreviewEntered;
+
+ // Assert that we are showing the original page
+ await SpecialPowers.spawn(ppBrowser, [], async function() {
+ is(
+ content.document.body.textContent.trim(),
+ "INITIAL PAGE",
+ "Should have initial page print previewed."
+ );
+ });
+
+ await originalTabNavigated;
+
+ // Change orientation and wait for print preview to re-enter:
+ let orient = PrintUtils.getPrintSettings().orientation;
+ let orientToSwitchTo =
+ orient != Ci.nsIPrintSettings.kPortraitOrientation
+ ? "portrait"
+ : "landscape";
+ let printPreviewToolbar = document.getElementById("print-preview-toolbar");
+
+ printPreviewEntered = PrintHelper.waitForOldPrintPreview(ppBrowser);
+ printPreviewToolbar.orient(orientToSwitchTo);
+ await printPreviewEntered;
+
+ // Check that we're still showing the original page.
+ await SpecialPowers.spawn(ppBrowser, [], async function() {
+ is(
+ content.document.body.textContent.trim(),
+ "INITIAL PAGE",
+ "Should still have initial page print previewed."
+ );
+ });
+
+ // Check that the other tab is definitely showing the new page:
+ await SpecialPowers.spawn(browserToPrint, [], async function() {
+ is(
+ content.document.body.textContent.trim(),
+ "REPLACED PAGE!",
+ "Original page should have changed."
+ );
+ });
+
+ PrintUtils.exitPrintPreview();
+
+ BrowserTestUtils.removeTab(tab);
+});
diff --git a/toolkit/components/printing/tests/browser_pdf_hidden_settings.js b/toolkit/components/printing/tests/browser_pdf_hidden_settings.js
new file mode 100644
index 0000000000..93c965fd94
--- /dev/null
+++ b/toolkit/components/printing/tests/browser_pdf_hidden_settings.js
@@ -0,0 +1,39 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const hiddenPdfIds = [
+ "margins",
+ "backgrounds",
+ "headers-footers",
+ "more-settings-options",
+];
+
+async function checkElements({ removed, file, testName }) {
+ await PrintHelper.withTestPage(async helper => {
+ await helper.startPrint();
+
+ for (let id of hiddenPdfIds) {
+ is(
+ !helper.get(id),
+ removed,
+ `${id} is ${removed ? "" : "not "}removed (${testName})`
+ );
+ }
+
+ await helper.closeDialog();
+ }, file);
+}
+
+add_task(async function testSettingsShownForNonPdf() {
+ await checkElements({ removed: false, testName: "non-pdf" });
+});
+
+add_task(async function testSettingsHiddenForPdf() {
+ await checkElements({
+ removed: true,
+ file: "file_pdf.pdf",
+ testName: "pdf",
+ });
+});
diff --git a/toolkit/components/printing/tests/browser_pdf_printer_settings.js b/toolkit/components/printing/tests/browser_pdf_printer_settings.js
new file mode 100644
index 0000000000..7ebabc9d01
--- /dev/null
+++ b/toolkit/components/printing/tests/browser_pdf_printer_settings.js
@@ -0,0 +1,116 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function testPDFPrinterSettings() {
+ await PrintHelper.withTestPage(async helper => {
+ // Set some bad prefs
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["print.print_to_file", false],
+ ["print.print_in_color", false],
+ ["print.printer_Mozilla_Save_to_PDF.print_to_file", false],
+ ["print.printer_Mozilla_Save_to_PDF.print_in_color", false],
+ ],
+ });
+
+ await helper.startPrint();
+ await helper.awaitAnimationFrame();
+
+ // Verify we end up with sane settings
+ let { settings } = helper;
+
+ ok(
+ settings.printToFile,
+ "Check the current settings have a truthy printToFile for the PDF printer"
+ );
+ ok(
+ settings.printInColor,
+ "Check the current settings have a truthy printInColor for the PDF printer"
+ );
+ is(
+ settings.outputFormat,
+ Ci.nsIPrintSettings.kOutputFormatPDF,
+ "The PDF printer has the correct outputFormat"
+ );
+
+ await helper.closeDialog();
+ await SpecialPowers.popPrefEnv();
+ });
+});
+
+add_task(async function testPDFCancel() {
+ await PrintHelper.withTestPage(async helper => {
+ await helper.startPrint();
+ helper.mockFilePickerCancel();
+ let form = helper.doc.querySelector("#print");
+
+ // retrieve all elements other than cancel button
+ let elements = [];
+ for (let element of form.elements) {
+ if (element.name != "cancel") {
+ elements.push(element);
+ }
+ }
+ let getDisabledStates = () => elements.map(el => el.disabled);
+ let initialDisabledStates = getDisabledStates();
+
+ ok(
+ initialDisabledStates.some(disabled => !disabled),
+ "At least one enabled form element before submitting"
+ );
+ let getShownDisabledStates = new Promise(resolve => {
+ MockFilePicker.showCallback = () => resolve(getDisabledStates());
+ });
+
+ EventUtils.sendKey("return", helper.win);
+
+ let shownDisabledStates = await getShownDisabledStates;
+ ok(shownDisabledStates, "Got disabled states while shown");
+ ok(
+ shownDisabledStates.every(disabled => disabled),
+ "All elements were disabled when showing picker"
+ );
+ let cancelButton = helper.doc.querySelector(`button[name="cancel"]`);
+ ok(!cancelButton.disabled, "Cancel button is still enabled");
+
+ let saveButton = form.querySelector("#print-button");
+ await BrowserTestUtils.waitForAttributeRemoval("disabled", saveButton);
+ helper.assertDialogOpen();
+
+ is(
+ getDisabledStates().every(
+ (disabledState, index) => disabledState === initialDisabledStates[index]
+ ),
+ true,
+ "Previous disabled states match after returning to preview"
+ );
+
+ // Close the dialog with Escape.
+ await helper.withClosingFn(() => {
+ EventUtils.synthesizeKey("VK_ESCAPE", {}, helper.win);
+ });
+
+ helper.assertDialogClosed();
+ });
+});
+
+add_task(async function testPDFFile() {
+ await PrintHelper.withTestPage(async helper => {
+ await helper.startPrint();
+
+ helper.mockFilePicker("pdfFile.pdf");
+ let filePath = PathUtils.join(
+ Services.dirsvc.get("TmpD", Ci.nsIFile).path,
+ "pdfFile.pdf"
+ );
+
+ await helper.withClosingFn(() => {
+ EventUtils.sendKey("return", helper.win);
+ });
+
+ is(await IOUtils.exists(filePath), true, "Saved pdf file exists");
+ ok(await IOUtils.read(filePath), "Saved pdf file is not empty");
+ });
+});
diff --git a/toolkit/components/printing/tests/browser_preview_in_container.js b/toolkit/components/printing/tests/browser_preview_in_container.js
new file mode 100644
index 0000000000..21a0d1e6b1
--- /dev/null
+++ b/toolkit/components/printing/tests/browser_preview_in_container.js
@@ -0,0 +1,60 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_PATH = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+);
+
+async function runTest() {
+ is(
+ document.querySelector(".printPreviewBrowser"),
+ null,
+ "There shouldn't be any print preview browser"
+ );
+
+ gBrowser.selectedTab = await BrowserTestUtils.addTab(
+ gBrowser,
+ `${TEST_PATH}file_print.html`,
+ { userContextId: 1 }
+ );
+
+ // Wait for window.print() to run and ensure we're showing the preview...
+ await BrowserTestUtils.waitForCondition(
+ () => !!document.querySelector(".printPreviewBrowser")
+ );
+
+ let previewBrowser = document.querySelector(".printPreviewBrowser");
+ let contentFound = await SpecialPowers.spawn(previewBrowser, [], () => {
+ return !!content.document.getElementById("printed");
+ });
+ ok(contentFound, "We should find the preview content.");
+ BrowserTestUtils.removeTab(gBrowser.selectedTab);
+}
+
+add_task(async function test_in_container() {
+ // window.print() only shows print preview when print.tab_modal.enabled is
+ // true.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["print.tab_modal.enabled", true],
+ ["privacy.firstparty.isolate", false],
+ ],
+ });
+
+ await runTest();
+});
+
+add_task(async function test_with_fpi() {
+ // window.print() only shows print preview when print.tab_modal.enabled is
+ // true.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["print.tab_modal.enabled", true],
+ ["privacy.firstparty.isolate", true],
+ ],
+ });
+ await runTest();
+});
diff --git a/toolkit/components/printing/tests/browser_preview_navigation.js b/toolkit/components/printing/tests/browser_preview_navigation.js
new file mode 100644
index 0000000000..d90d95416c
--- /dev/null
+++ b/toolkit/components/printing/tests/browser_preview_navigation.js
@@ -0,0 +1,424 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+function compare10nArgs(elem, expectedValues) {
+ let l10nArgs = elem.ownerDocument.l10n.getAttributes(elem).args;
+ for (let [name, value] of Object.entries(expectedValues)) {
+ if (value !== l10nArgs[name]) {
+ info(
+ `compare10nArgs, expected ${name}: ${value}, actual: ${l10nArgs[name]}`
+ );
+ return false;
+ }
+ }
+ return true;
+}
+
+async function waitForPageStatusUpdate(elem, expected, message) {
+ await TestUtils.waitForCondition(
+ () => compare10nArgs(elem, expected),
+ message
+ );
+}
+
+async function waitUntilVisible(elem, visible = true) {
+ await TestUtils.waitForCondition(
+ () =>
+ BrowserTestUtils.is_visible(elem) &&
+ getComputedStyle(elem).opacity == "1",
+ "Waiting for element to be visible and have opacity:1"
+ );
+}
+
+async function waitUntilTransparent(elem) {
+ // Note that is_visible considers a fully transparent element "visible"
+ await TestUtils.waitForCondition(
+ () => getComputedStyle(elem).opacity == "0",
+ "Waiting for element to be have opacity:0"
+ );
+}
+
+async function mouseMoveAndWait(elem) {
+ let mouseMovePromise = BrowserTestUtils.waitForEvent(elem, "mousemove");
+ EventUtils.synthesizeMouseAtCenter(elem, { type: "mousemove" });
+ await mouseMovePromise;
+ await TestUtils.waitForTick();
+}
+
+add_task(async function testToolbarVisibility() {
+ // move the mouse to a known position
+ await mouseMoveAndWait(gURLBar.textbox);
+
+ await PrintHelper.withTestPage(async helper => {
+ await helper.startPrint();
+
+ let paginationElem = document.querySelector(".printPreviewNavigation");
+ let previewStack = document.querySelector(".previewStack");
+
+ // The toolbar has 0 opacity until we hover or focus it
+ is(getComputedStyle(paginationElem).opacity, "0", "Initially transparent");
+
+ let visiblePromise = waitUntilVisible(paginationElem);
+ paginationElem.shadowRoot.querySelector("#navigateEnd").focus();
+ await visiblePromise;
+ is(
+ getComputedStyle(paginationElem).opacity,
+ "1",
+ "Opaque with button focused"
+ );
+
+ await EventUtils.synthesizeKey("KEY_Tab", {});
+ await waitUntilTransparent(paginationElem);
+ is(getComputedStyle(paginationElem).opacity, "0", "Returns to transparent");
+
+ visiblePromise = waitUntilVisible(paginationElem);
+ info("Waiting for mousemove event, and for the toolbar to become opaque");
+ await mouseMoveAndWait(previewStack);
+ await visiblePromise;
+ is(getComputedStyle(paginationElem).opacity, "1", "Opaque toolbar");
+
+ // put the mouse back where it won't interfere with later tests
+ await mouseMoveAndWait(gURLBar.textbox);
+ await helper.closeDialog();
+ });
+});
+
+add_task(async function testPreviewSheetCount() {
+ await PrintHelper.withTestPage(async helper => {
+ await helper.startPrint();
+
+ let paginationElem = document.querySelector(".printPreviewNavigation");
+ let paginationSheetIndicator = paginationElem.shadowRoot.querySelector(
+ "#sheetIndicator"
+ );
+
+ // We have to wait for the first _updatePrintPreview to get the sheet count
+ await waitForPageStatusUpdate(
+ paginationSheetIndicator,
+ { sheetNum: 1, sheetCount: 3 },
+ "Paginator indicates the correct number of sheets"
+ );
+
+ // then switch to page range 1-1 and verify page count changes
+ await helper.dispatchSettingsChange({
+ pageRanges: ["1", "1"],
+ });
+ await waitForPageStatusUpdate(
+ paginationSheetIndicator,
+ { sheetNum: 1, sheetCount: 1 },
+ "Indicates the updated number of sheets"
+ );
+
+ await helper.closeDialog();
+ }, "longerArticle.html");
+});
+
+add_task(async function testPreviewScroll() {
+ await PrintHelper.withTestPage(async helper => {
+ await helper.startPrint();
+
+ let paginationElem = document.querySelector(".printPreviewNavigation");
+ let paginationSheetIndicator = paginationElem.shadowRoot.querySelector(
+ "#sheetIndicator"
+ );
+ // Wait for the first _updatePrintPreview before interacting with the preview
+ await waitForPageStatusUpdate(
+ paginationSheetIndicator,
+ { sheetNum: 1, sheetCount: 3 },
+ "Paginator indicates the correct number of sheets"
+ );
+ let previewBrowser = PrintUtils.getPreviewBrowser();
+
+ // scroll down the document
+ // and verify the indicator is updated correctly
+ await SpecialPowers.spawn(previewBrowser, [], async function() {
+ const { ContentTaskUtils } = ChromeUtils.import(
+ "resource://testing-common/ContentTaskUtils.jsm"
+ );
+ const EventUtils = ContentTaskUtils.getEventUtils(content);
+ content.focus();
+ EventUtils.synthesizeKey("VK_PAGE_DOWN", {}, content);
+ });
+ await waitForPageStatusUpdate(
+ paginationSheetIndicator,
+ { sheetNum: 2, sheetCount: 3 },
+ "Indicator updates on scroll"
+ );
+
+ // move focus before closing the dialog
+ helper.get("cancel-button").focus();
+ await helper.closeDialog();
+ }, "longerArticle.html");
+});
+
+add_task(async function testPreviewNavigationCommands() {
+ await PrintHelper.withTestPage(async helper => {
+ await helper.startPrint();
+
+ let paginationElem = document.querySelector(".printPreviewNavigation");
+ let paginationSheetIndicator = paginationElem.shadowRoot.querySelector(
+ "#sheetIndicator"
+ );
+ // Wait for the first _updatePrintPreview before interacting with the preview
+ await waitForPageStatusUpdate(
+ paginationSheetIndicator,
+ { sheetNum: 1, sheetCount: 3 },
+ "Paginator indicates the correct number of sheets"
+ );
+
+ // click the navigation buttons
+ // and verify the indicator is updated correctly
+ EventUtils.synthesizeMouseAtCenter(
+ paginationElem.shadowRoot.querySelector("#navigateNext"),
+ {}
+ );
+ await waitForPageStatusUpdate(
+ paginationSheetIndicator,
+ { sheetNum: 2, sheetCount: 3 },
+ "Indicator updates on navigation"
+ );
+
+ EventUtils.synthesizeMouseAtCenter(
+ paginationElem.shadowRoot.querySelector("#navigatePrevious"),
+ {}
+ );
+ await waitForPageStatusUpdate(
+ paginationSheetIndicator,
+ { sheetNum: 1, sheetCount: 3 },
+ "Indicator updates on navigation to previous"
+ );
+
+ EventUtils.synthesizeMouseAtCenter(
+ paginationElem.shadowRoot.querySelector("#navigateEnd"),
+ {}
+ );
+ await waitForPageStatusUpdate(
+ paginationSheetIndicator,
+ { sheetNum: 3, sheetCount: 3 },
+ "Indicator updates on navigation to end"
+ );
+
+ EventUtils.synthesizeMouseAtCenter(
+ paginationElem.shadowRoot.querySelector("#navigateHome"),
+ {}
+ );
+ await waitForPageStatusUpdate(
+ paginationSheetIndicator,
+ { sheetNum: 1, sheetCount: 3 },
+ "Indicator updates on navigation to start"
+ );
+
+ // Test rapid clicks on the navigation buttons
+ EventUtils.synthesizeMouseAtCenter(
+ paginationElem.shadowRoot.querySelector("#navigateNext"),
+ {}
+ );
+ EventUtils.synthesizeMouseAtCenter(
+ paginationElem.shadowRoot.querySelector("#navigateNext"),
+ {}
+ );
+ await waitForPageStatusUpdate(
+ paginationSheetIndicator,
+ { sheetNum: 3, sheetCount: 3 },
+ "2 successive 'next' clicks correctly update the sheet indicator"
+ );
+
+ EventUtils.synthesizeMouseAtCenter(
+ paginationElem.shadowRoot.querySelector("#navigatePrevious"),
+ {}
+ );
+ EventUtils.synthesizeMouseAtCenter(
+ paginationElem.shadowRoot.querySelector("#navigatePrevious"),
+ {}
+ );
+ await waitForPageStatusUpdate(
+ paginationSheetIndicator,
+ { sheetNum: 1, sheetCount: 3 },
+ "2 successive 'previous' clicks correctly update the sheet indicator"
+ );
+
+ // move focus before closing the dialog
+ helper.get("cancel-button").focus();
+ await helper.closeDialog();
+ }, "longerArticle.html");
+});
+
+add_task(async function testMultiplePreviewNavigation() {
+ await PrintHelper.withTestPage(async helper => {
+ await helper.startPrint();
+ const tab1 = gBrowser.selectedTab;
+ let sheetIndicator = document
+ .querySelector(".printPreviewNavigation")
+ .shadowRoot.querySelector("#sheetIndicator");
+
+ await waitForPageStatusUpdate(
+ sheetIndicator,
+ { sheetNum: 1, sheetCount: 3 },
+ "Indicator has the correct initial sheetCount"
+ );
+
+ const tab2 = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ PrintHelper.defaultTestPageUrl
+ );
+ let helper2 = new PrintHelper(tab2.linkedBrowser);
+ await helper2.startPrint();
+
+ let [previewBrowser1, previewBrowser2] = document.querySelectorAll(
+ ".printPreviewBrowser[previewtype='primary']"
+ );
+ ok(previewBrowser1 && previewBrowser2, "There are 2 preview browsers");
+
+ let [toolbar1, toolbar2] = document.querySelectorAll(
+ ".printPreviewNavigation"
+ );
+ ok(toolbar1 && toolbar2, "There are 2 preview navigation toolbars");
+ is(
+ toolbar1.previewBrowser,
+ previewBrowser1,
+ "toolbar1 has the correct previewBrowser"
+ );
+ sheetIndicator = toolbar1.shadowRoot.querySelector("#sheetIndicator");
+ ok(
+ compare10nArgs(sheetIndicator, { sheetNum: 1, sheetCount: 3 }),
+ "First toolbar has the correct content"
+ );
+
+ is(
+ toolbar2.previewBrowser,
+ previewBrowser2,
+ "toolbar2 has the correct previewBrowser"
+ );
+ sheetIndicator = toolbar2.shadowRoot.querySelector("#sheetIndicator");
+ ok(
+ compare10nArgs(sheetIndicator, { sheetNum: 1, sheetCount: 1 }),
+ "2nd toolbar has the correct content"
+ );
+
+ // Switch back to the first tab and ensure the correct preview navigation is updated when clicked
+ await BrowserTestUtils.switchTab(gBrowser, tab1);
+ sheetIndicator = toolbar1.shadowRoot.querySelector("#sheetIndicator");
+
+ EventUtils.synthesizeMouseAtCenter(
+ toolbar1.shadowRoot.querySelector("#navigateNext"),
+ {}
+ );
+ await waitForPageStatusUpdate(
+ sheetIndicator,
+ { sheetNum: 2, sheetCount: 3 },
+ "Indicator updates on navigation"
+ );
+
+ gBrowser.removeTab(tab2);
+ }, "longerArticle.html");
+});
+
+add_task(async function testPreviewNavigationSelection() {
+ await PrintHelper.withTestPage(async helper => {
+ await SpecialPowers.spawn(helper.sourceBrowser, [], async function() {
+ let element = content.document.querySelector("#page-2");
+ content.window.getSelection().selectAllChildren(element);
+ });
+
+ await helper.startPrint();
+
+ let paginationElem = document.querySelector(".printPreviewNavigation");
+ let paginationSheetIndicator = paginationElem.shadowRoot.querySelector(
+ "#sheetIndicator"
+ );
+ // Wait for the first _updatePrintPreview before interacting with the preview
+ await waitForPageStatusUpdate(
+ paginationSheetIndicator,
+ { sheetNum: 1, sheetCount: 3 },
+ "Paginator indicates the correct number of sheets"
+ );
+
+ // click a navigation button
+ // and verify the indicator is updated correctly
+ EventUtils.synthesizeMouseAtCenter(
+ paginationElem.shadowRoot.querySelector("#navigateNext"),
+ {}
+ );
+ await waitForPageStatusUpdate(
+ paginationSheetIndicator,
+ { sheetNum: 2, sheetCount: 3 },
+ "Indicator updates on navigation"
+ );
+
+ await helper.openMoreSettings();
+ let printSelect = helper.get("print-selection-container");
+ await helper.waitForPreview(() => helper.click(printSelect));
+
+ // Wait for the first _updatePrintPreview before interacting with the preview
+ await waitForPageStatusUpdate(
+ paginationSheetIndicator,
+ { sheetNum: 1, sheetCount: 2 },
+ "Paginator indicates the correct number of sheets"
+ );
+
+ // click a navigation button
+ // and verify the indicator is updated correctly
+ EventUtils.synthesizeMouseAtCenter(
+ paginationElem.shadowRoot.querySelector("#navigateNext"),
+ {}
+ );
+ await waitForPageStatusUpdate(
+ paginationSheetIndicator,
+ { sheetNum: 2, sheetCount: 2 },
+ "Indicator updates on navigation"
+ );
+
+ // move focus before closing the dialog
+ helper.get("cancel-button").focus();
+ await helper.closeDialog();
+ }, "longerArticle.html");
+});
+
+add_task(async function testPaginatorAfterSettingsUpdate() {
+ const mockPrinterName = "Fake Printer";
+ await PrintHelper.withTestPage(async helper => {
+ helper.addMockPrinter(mockPrinterName);
+ await helper.startPrint();
+
+ let paginationElem = document.querySelector(".printPreviewNavigation");
+ let paginationSheetIndicator = paginationElem.shadowRoot.querySelector(
+ "#sheetIndicator"
+ );
+ // Wait for the first _updatePrintPreview before interacting with the preview
+ await waitForPageStatusUpdate(
+ paginationSheetIndicator,
+ { sheetNum: 1, sheetCount: 3 },
+ "Paginator indicates the correct number of sheets"
+ );
+
+ // click the navigation buttons
+ // and verify the indicator is updated correctly
+ EventUtils.synthesizeMouseAtCenter(
+ paginationElem.shadowRoot.querySelector("#navigateNext"),
+ {}
+ );
+ await waitForPageStatusUpdate(
+ paginationSheetIndicator,
+ { sheetNum: 2, sheetCount: 3 },
+ "Indicator updates on navigation"
+ );
+
+ // Select a new printer
+ await helper.dispatchSettingsChange({ printerName: mockPrinterName });
+ await waitForPageStatusUpdate(
+ paginationSheetIndicator,
+ { sheetNum: 1, sheetCount: 3 },
+ "Indicator updates on navigation"
+ );
+ ok(
+ compare10nArgs(paginationSheetIndicator, { sheetNum: 1, sheetCount: 3 }),
+ "Sheet indicator has correct value"
+ );
+
+ // move focus before closing the dialog
+ helper.get("cancel-button").focus();
+ await helper.closeDialog();
+ }, "longerArticle.html");
+});
diff --git a/toolkit/components/printing/tests/browser_preview_print_coop.js b/toolkit/components/printing/tests/browser_preview_print_coop.js
new file mode 100644
index 0000000000..b635ca82a7
--- /dev/null
+++ b/toolkit/components/printing/tests/browser_preview_print_coop.js
@@ -0,0 +1,40 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_PATH = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+);
+
+/**
+ * Verify if the page with a COOP header can be used for printing preview.
+ */
+add_task(async function test() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["print.tab_modal.enabled", false]],
+ });
+
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ `${TEST_PATH}file_coop_header.html`
+ );
+
+ // Enter print preview
+ let ppBrowser = PrintPreviewListener.getPrintPreviewBrowser();
+ let ppPromise = PrintHelper.waitForOldPrintPreview(ppBrowser);
+ document.getElementById("cmd_printPreview").doCommand();
+ await ppPromise;
+
+ await BrowserTestUtils.waitForCondition(
+ () => gInPrintPreviewMode,
+ "Should be in print preview mode now."
+ );
+
+ ok(true, "We did not crash.");
+
+ PrintUtils.exitPrintPreview();
+
+ BrowserTestUtils.removeTab(tab);
+});
diff --git a/toolkit/components/printing/tests/browser_preview_print_simplify_non_article.js b/toolkit/components/printing/tests/browser_preview_print_simplify_non_article.js
new file mode 100644
index 0000000000..681e53f8b9
--- /dev/null
+++ b/toolkit/components/printing/tests/browser_preview_print_simplify_non_article.js
@@ -0,0 +1,102 @@
+/**
+ * Verify if we recover from parsing errors of Reader Mode when
+ * Simplify Page checkbox is checked.
+ */
+
+const TEST_PATH = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "http://example.com"
+);
+
+add_task(async function set_simplify_and_reader_pref() {
+ // Ensure we have the simplify page preference set
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["print.tab_modal.enabled", false],
+ ["print.use_simplify_page", true],
+ ["reader.parse-on-load.enabled", true],
+ ],
+ });
+});
+
+add_task(async function switch_print_preview_browsers() {
+ let url = TEST_PATH + "simplifyNonArticleSample.html";
+
+ // Can only do something if we have a print preview UI:
+ if (AppConstants.platform != "win" && AppConstants.platform != "linux") {
+ ok(false, "Can't test if there's no print preview.");
+ return;
+ }
+
+ // Ensure we get a browserStopped for this browser
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ url,
+ false,
+ true
+ );
+
+ // Trick browser to think loaded tab has isArticle property set as true
+ tab.linkedBrowser.isArticle = true;
+
+ // Enter print preview
+ let defaultPPBrowser = PrintPreviewListener.getPrintPreviewBrowser();
+ let defaultPPEntered = PrintHelper.waitForOldPrintPreview(defaultPPBrowser);
+ document.getElementById("cmd_printPreview").doCommand();
+ await defaultPPEntered;
+
+ // Assert that we are showing the initial content on default print preview browser
+ await SpecialPowers.spawn(defaultPPBrowser, [], async function() {
+ is(
+ content.document.title,
+ "Non article title",
+ "Should have initial content."
+ );
+ });
+
+ // Here we call simplified mode
+ let simplifiedPPBrowser = PrintPreviewListener.getSimplifiedPrintPreviewBrowser();
+ let simplifiedPPEntered = PrintHelper.waitForOldPrintPreview(
+ simplifiedPPBrowser
+ );
+ let printPreviewToolbar = document.getElementById("print-preview-toolbar");
+
+ // Wait for simplify page option enablement
+ await BrowserTestUtils.waitForCondition(() => {
+ return !printPreviewToolbar.mSimplifyPageCheckbox.disabled;
+ });
+
+ printPreviewToolbar.mSimplifyPageCheckbox.click();
+ await simplifiedPPEntered;
+
+ // Assert that simplify page option is checked
+ is(
+ printPreviewToolbar.mSimplifyPageCheckbox.checked,
+ true,
+ "Should have simplify page option checked"
+ );
+
+ // Assert that we are showing recovery content on simplified print preview browser
+ await SpecialPowers.spawn(simplifiedPPBrowser, [], async function() {
+ await ContentTaskUtils.waitForCondition(
+ () => content.document.title === "Failed to load article from page",
+ "Simplified document title should be updated with recovery title."
+ );
+ });
+
+ // Assert that we are selecting simplified print preview browser, and not default one
+ is(
+ gBrowser.selectedTab.linkedBrowser,
+ simplifiedPPBrowser,
+ "Should have simplified print preview browser selected"
+ );
+ isnot(
+ gBrowser.selectedTab.linkedBrowser,
+ defaultPPBrowser,
+ "Should not have default print preview browser selected"
+ );
+
+ PrintUtils.exitPrintPreview();
+
+ await BrowserTestUtils.removeTab(tab);
+});
diff --git a/toolkit/components/printing/tests/browser_preview_switch_print_selected.js b/toolkit/components/printing/tests/browser_preview_switch_print_selected.js
new file mode 100644
index 0000000000..e2771a9e3c
--- /dev/null
+++ b/toolkit/components/printing/tests/browser_preview_switch_print_selected.js
@@ -0,0 +1,126 @@
+/**
+ * Verify if we correctly switch print preview browsers based on whether
+ * Simplify Page checkbox is checked.
+ */
+
+const TEST_PATH = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "http://example.com"
+);
+
+add_task(async function set_simplify_and_reader_pref() {
+ // Ensure we have the simplify page preference set
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["print.tab_modal.enabled", false],
+ ["print.use_simplify_page", true],
+ ["reader.parse-on-load.enabled", true],
+ ],
+ });
+});
+
+add_task(async function switch_print_preview_browsers() {
+ let url = TEST_PATH + "simplifyArticleSample.html";
+
+ // Can only do something if we have a print preview UI:
+ if (AppConstants.platform != "win" && AppConstants.platform != "linux") {
+ ok(false, "Can't test if there's no print preview.");
+ return;
+ }
+
+ // Ensure we get a browserStopped for this browser
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ url,
+ false,
+ true
+ );
+
+ // Wait for Reader Mode to parse and set property of loaded tab
+ await BrowserTestUtils.waitForCondition(() => {
+ return tab.linkedBrowser.isArticle;
+ });
+
+ // Enter print preview
+ let defaultPPBrowser = PrintPreviewListener.getPrintPreviewBrowser();
+ let defaultPPEntered = PrintHelper.waitForOldPrintPreview(defaultPPBrowser);
+ document.getElementById("cmd_printPreview").doCommand();
+ await defaultPPEntered;
+
+ // Assert that we are showing the initial content on default print preview browser
+ await SpecialPowers.spawn(defaultPPBrowser, [], async function() {
+ is(content.document.title, "Article title", "Should have initial content.");
+ });
+
+ // Here we call simplified mode
+ let simplifiedPPBrowser = PrintPreviewListener.getSimplifiedPrintPreviewBrowser();
+ let simplifiedPPEntered = PrintHelper.waitForOldPrintPreview(
+ simplifiedPPBrowser
+ );
+ let printPreviewToolbar = document.getElementById("print-preview-toolbar");
+
+ // Wait for simplify page option enablement
+ await BrowserTestUtils.waitForCondition(() => {
+ return !printPreviewToolbar.mSimplifyPageCheckbox.disabled;
+ });
+
+ printPreviewToolbar.mSimplifyPageCheckbox.click();
+ await simplifiedPPEntered;
+
+ // Assert that simplify page option is checked
+ is(
+ printPreviewToolbar.mSimplifyPageCheckbox.checked,
+ true,
+ "Should have simplify page option checked"
+ );
+
+ // Assert that we are showing custom content on simplified print preview browser
+ await SpecialPowers.spawn(simplifiedPPBrowser, [], async function() {
+ is(content.document.title, "Article title", "Should have custom content.");
+ });
+
+ // Assert that we are selecting simplified print preview browser, and not default one
+ is(
+ gBrowser.selectedTab.linkedBrowser,
+ simplifiedPPBrowser,
+ "Should have simplified print preview browser selected"
+ );
+ isnot(
+ gBrowser.selectedTab.linkedBrowser,
+ defaultPPBrowser,
+ "Should not have default print preview browser selected"
+ );
+
+ // Switch back to default print preview content
+ defaultPPEntered = PrintHelper.waitForOldPrintPreview(defaultPPBrowser);
+ printPreviewToolbar.mSimplifyPageCheckbox.click();
+ await defaultPPEntered;
+
+ // Assert that simplify page option is not checked
+ isnot(
+ printPreviewToolbar.mSimplifyPageCheckbox.checked,
+ true,
+ "Should not have simplify page option checked"
+ );
+
+ // Assert that we are showing the initial content on default print preview browser
+ await SpecialPowers.spawn(defaultPPBrowser, [], async function() {
+ is(content.document.title, "Article title", "Should have initial content.");
+ });
+
+ // Assert that we are selecting default print preview browser, and not simplified one
+ is(
+ gBrowser.selectedTab.linkedBrowser,
+ defaultPPBrowser,
+ "Should have default print preview browser selected"
+ );
+ isnot(
+ gBrowser.selectedTab.linkedBrowser,
+ simplifiedPPBrowser,
+ "Should not have simplified print preview browser selected"
+ );
+
+ PrintUtils.exitPrintPreview();
+
+ BrowserTestUtils.removeTab(tab);
+});
diff --git a/toolkit/components/printing/tests/browser_print_bcg_id_overflow.js b/toolkit/components/printing/tests/browser_print_bcg_id_overflow.js
new file mode 100644
index 0000000000..a86db21c8b
--- /dev/null
+++ b/toolkit/components/printing/tests/browser_print_bcg_id_overflow.js
@@ -0,0 +1,61 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_PATH = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+);
+
+// The actual uri we open doesn't really matter.
+const OPENED_URI = PrintHelper.defaultTestPageUrl;
+
+// Test for bug 1669554:
+//
+// This opens a rel=noopener window in a content process, which causes us to
+// create a browsing context with an id that likely overflows an int32_t, which
+// caused us to fail to parse the initialBrowsingContextGroupId attribute
+// (causing us to potentially clone in the wrong process, etc).
+const OPEN_NOOPENER_WINDOW = `
+ <a rel="noopener" target="_blank" href="${OPENED_URI}">Open the window</a>
+`;
+
+add_task(async function test_bc_id_overflow() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["print.tab_modal.enabled", true]],
+ });
+
+ is(document.querySelector(".printPreviewBrowser"), null);
+
+ await BrowserTestUtils.withNewTab(
+ `data:text/html,` + encodeURIComponent(OPEN_NOOPENER_WINDOW),
+ async function(browser) {
+ let tabOpenedPromise = BrowserTestUtils.waitForNewTab(
+ gBrowser,
+ OPENED_URI,
+ /* waitForLoad = */ true
+ );
+ await BrowserTestUtils.synthesizeMouse("a", 0, 0, {}, browser);
+ let tab = await tabOpenedPromise;
+ let helper = new PrintHelper(tab.linkedBrowser);
+ await helper.startPrint();
+ helper.assertDialogOpen();
+
+ let previewBrowser = document.querySelector(".printPreviewBrowser");
+ is(typeof previewBrowser.browsingContext.group.id, "number", "Sanity");
+ is(
+ previewBrowser.browsingContext.group.id,
+ tab.linkedBrowser.browsingContext.group.id,
+ "Group ids should match: " + tab.linkedBrowser.browsingContext.group.id
+ );
+ is(
+ previewBrowser.browsingContext.group,
+ tab.linkedBrowser.browsingContext.group,
+ "Groups should match"
+ );
+ await helper.closeDialog();
+ await BrowserTestUtils.removeTab(tab);
+ }
+ );
+});
diff --git a/toolkit/components/printing/tests/browser_print_context_menu.js b/toolkit/components/printing/tests/browser_print_context_menu.js
new file mode 100644
index 0000000000..023d6eb794
--- /dev/null
+++ b/toolkit/components/printing/tests/browser_print_context_menu.js
@@ -0,0 +1,68 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const frameSource = `<a href="about:mozilla">Inner frame</a>`;
+const source = `<html><h1>Top level text</h1><iframe srcdoc='${frameSource}' id="f"></iframe></html>`;
+
+add_task(async function testPrintFrame() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["print.tab_modal.enabled", true]],
+ });
+
+ let url = `data:text/html,${source}`;
+ await BrowserTestUtils.withNewTab({ gBrowser, url }, async function(browser) {
+ let contentAreaContextMenuPopup = document.getElementById(
+ "contentAreaContextMenu"
+ );
+ let popupShownPromise = BrowserTestUtils.waitForEvent(
+ contentAreaContextMenuPopup,
+ "popupshown"
+ );
+ await BrowserTestUtils.synthesizeMouseAtCenter(
+ "#f",
+ { type: "contextmenu", button: 2 },
+ gBrowser.selectedBrowser
+ );
+ await popupShownPromise;
+
+ let frameContextMenu = document.getElementById("frame");
+ popupShownPromise = BrowserTestUtils.waitForEvent(
+ frameContextMenu,
+ "popupshown"
+ );
+ EventUtils.synthesizeMouseAtCenter(frameContextMenu, {});
+ await popupShownPromise;
+
+ let popupHiddenPromise = BrowserTestUtils.waitForEvent(
+ frameContextMenu,
+ "popuphidden"
+ );
+ let item = document.getElementById("context-printframe");
+ EventUtils.synthesizeMouseAtCenter(item, {});
+ await popupHiddenPromise;
+
+ await BrowserTestUtils.waitForCondition(
+ () => !!document.querySelector(".printPreviewBrowser")
+ );
+
+ let previewBrowser = document.querySelector(
+ ".printPreviewBrowser[previewtype='primary']"
+ );
+ let helper = new PrintHelper(browser);
+
+ let textContent = await TestUtils.waitForCondition(() =>
+ SpecialPowers.spawn(previewBrowser, [], function() {
+ return content.document.body.textContent;
+ })
+ );
+
+ is(textContent, "Inner frame", "Correct content loaded");
+ is(
+ helper.win.PrintEventHandler.printFrameOnly,
+ true,
+ "Print frame only is true"
+ );
+ PrintHelper.resetPrintPrefs();
+ });
+});
diff --git a/toolkit/components/printing/tests/browser_print_copies.js b/toolkit/components/printing/tests/browser_print_copies.js
new file mode 100644
index 0000000000..083dd6e5ee
--- /dev/null
+++ b/toolkit/components/printing/tests/browser_print_copies.js
@@ -0,0 +1,41 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function testCopyError() {
+ await PrintHelper.withTestPage(async helper => {
+ helper.addMockPrinter("A printer");
+ await SpecialPowers.pushPrefEnv({
+ set: [["print_printer", "A printer"]],
+ });
+
+ await helper.startPrint();
+
+ let copyInput = helper.get("copies-count");
+ let destinationPicker = helper.get("printer-picker");
+
+ await helper.assertSettingsChanged(
+ { numCopies: 1 },
+ { numCopies: 10000 },
+ async () => {
+ helper.text(copyInput, "10000000000");
+
+ // Initially, the copies will be more than the max.
+ is(copyInput.checkValidity(), false, "Copy count is invalid");
+ is(
+ destinationPicker.disabled,
+ false,
+ "Destination picker is still enabled"
+ );
+
+ // When the settings event happens, copies resolves to max.
+ await helper.waitForSettingsEvent();
+ is(copyInput.value, "10000", "Copies gets set to max value");
+ is(copyInput.checkValidity(), true, "Copy count is valid again");
+ }
+ );
+
+ await helper.closeDialog();
+ });
+});
diff --git a/toolkit/components/printing/tests/browser_print_duplex.js b/toolkit/components/printing/tests/browser_print_duplex.js
new file mode 100644
index 0000000000..5fa59f4672
--- /dev/null
+++ b/toolkit/components/printing/tests/browser_print_duplex.js
@@ -0,0 +1,174 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function testPDFPrinterIsNonDuplex() {
+ await PrintHelper.withTestPage(async helper => {
+ await helper.startPrint();
+ await helper.openMoreSettings();
+
+ is(
+ helper.settings.printerName,
+ "Mozilla Save to PDF",
+ "Mozilla Save to PDF is the current printer."
+ );
+
+ const duplexSection = helper.get("two-sided-printing");
+ ok(
+ duplexSection.hidden,
+ "The two-sided printing section should be hidden when the printer does not support duplex."
+ );
+
+ helper.assertSettingsMatch({ duplex: Ci.nsIPrintSettings.kSimplex });
+
+ await helper.closeDialog();
+ });
+});
+
+add_task(async function testToggleDuplexWithPortraitOrientation() {
+ const mockPrinterName = "DuplexWithPortrait";
+ await PrintHelper.withTestPage(async helper => {
+ const printer = helper.addMockPrinter(mockPrinterName);
+ printer.supportsDuplex = Promise.resolve(true);
+
+ await helper.startPrint();
+ await helper.dispatchSettingsChange({ printerName: mockPrinterName });
+ await helper.awaitAnimationFrame();
+ await helper.openMoreSettings();
+
+ is(
+ helper.settings.printerName,
+ mockPrinterName,
+ "The Fake Printer is current printer"
+ );
+
+ const duplexSection = helper.get("two-sided-printing");
+ ok(
+ !duplexSection.hidden,
+ "The two-sided printing section should not be hidden when the printer supports duplex."
+ );
+
+ helper.assertSettingsMatch({
+ orientation: Ci.nsIPrintSettings.kPortraitOrientation,
+ duplex: Ci.nsIPrintSettings.kSimplex,
+ });
+
+ await helper.click(helper.get("duplex-enabled"));
+ helper.assertSettingsMatch({
+ orientation: Ci.nsIPrintSettings.kPortraitOrientation,
+ duplex: Ci.nsIPrintSettings.kDuplexHorizontal,
+ });
+
+ await helper.click(helper.get("duplex-enabled"));
+ helper.assertSettingsMatch({
+ orientation: Ci.nsIPrintSettings.kPortraitOrientation,
+ duplex: Ci.nsIPrintSettings.kSimplex,
+ });
+
+ await helper.closeDialog();
+ });
+});
+
+add_task(async function testToggleDuplexWithLandscapeOrientation() {
+ const mockPrinterName = "DuplexWithLandscape";
+ await PrintHelper.withTestPage(async helper => {
+ const printer = helper.addMockPrinter(mockPrinterName);
+ printer.supportsDuplex = Promise.resolve(true);
+
+ await helper.startPrint();
+ await helper.dispatchSettingsChange({ printerName: mockPrinterName });
+ await helper.awaitAnimationFrame();
+ await helper.openMoreSettings();
+
+ is(
+ helper.settings.printerName,
+ mockPrinterName,
+ "The Fake Printer is current printer"
+ );
+
+ const duplexSection = helper.get("two-sided-printing");
+ ok(
+ !duplexSection.hidden,
+ "The two-sided printing section should not be hidden when the printer supports duplex."
+ );
+
+ await helper.assertSettingsMatch({
+ orientation: Ci.nsIPrintSettings.kPortraitOrientation,
+ duplex: Ci.nsIPrintSettings.kSimplex,
+ });
+
+ await helper.dispatchSettingsChange({ orientation: 1 });
+ await helper.awaitAnimationFrame();
+ await helper.assertSettingsMatch({
+ orientation: Ci.nsIPrintSettings.kLandscapeOrientation,
+ duplex: Ci.nsIPrintSettings.kSimplex,
+ });
+
+ await helper.click(helper.get("duplex-enabled"));
+ await helper.assertSettingsMatch({
+ orientation: Ci.nsIPrintSettings.kLandscapeOrientation,
+ duplex: Ci.nsIPrintSettings.kDuplexHorizontal,
+ });
+
+ await helper.click(helper.get("duplex-enabled"));
+ await helper.assertSettingsMatch({
+ orientation: Ci.nsIPrintSettings.kLandscapeOrientation,
+ duplex: Ci.nsIPrintSettings.kSimplex,
+ });
+
+ await helper.closeDialog();
+ });
+});
+
+add_task(async function testSwitchOrientationWithDuplexEnabled() {
+ const mockPrinterName = "ToggleOrientationPrinter";
+ await PrintHelper.withTestPage(async helper => {
+ const printer = helper.addMockPrinter(mockPrinterName);
+ printer.supportsDuplex = Promise.resolve(true);
+
+ await helper.startPrint();
+ await helper.dispatchSettingsChange({ printerName: mockPrinterName });
+ await helper.awaitAnimationFrame();
+ await helper.openMoreSettings();
+
+ is(
+ helper.settings.printerName,
+ mockPrinterName,
+ "The Fake Printer is current printer"
+ );
+
+ const duplexSection = helper.get("two-sided-printing");
+ ok(
+ !duplexSection.hidden,
+ "The two-sided printing section should not be hidden when the printer supports duplex."
+ );
+
+ await helper.assertSettingsMatch({
+ orientation: Ci.nsIPrintSettings.kPortraitOrientation,
+ duplex: Ci.nsIPrintSettings.kSimplex,
+ });
+
+ await helper.click(helper.get("duplex-enabled"));
+ await helper.assertSettingsMatch({
+ orientation: Ci.nsIPrintSettings.kPortraitOrientation,
+ duplex: Ci.nsIPrintSettings.kDuplexHorizontal,
+ });
+
+ await helper.dispatchSettingsChange({ orientation: 1 });
+ await helper.awaitAnimationFrame();
+ await helper.assertSettingsMatch({
+ orientation: Ci.nsIPrintSettings.kLandscapeOrientation,
+ duplex: Ci.nsIPrintSettings.kDuplexHorizontal,
+ });
+
+ await helper.dispatchSettingsChange({ orientation: 0 });
+ await helper.awaitAnimationFrame();
+ await helper.assertSettingsMatch({
+ orientation: Ci.nsIPrintSettings.kPortraitOrientation,
+ duplex: Ci.nsIPrintSettings.kDuplexHorizontal,
+ });
+
+ await helper.closeDialog();
+ });
+});
diff --git a/toolkit/components/printing/tests/browser_print_in_container.js b/toolkit/components/printing/tests/browser_print_in_container.js
new file mode 100644
index 0000000000..243b07eaa8
--- /dev/null
+++ b/toolkit/components/printing/tests/browser_print_in_container.js
@@ -0,0 +1,38 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_PATH = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+);
+
+add_task(async function test() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["print.tab_modal.enabled", true]],
+ });
+
+ let tab = await BrowserTestUtils.switchTab(gBrowser, function() {
+ gBrowser.selectedTab = BrowserTestUtils.addTab(
+ gBrowser,
+ `${TEST_PATH}simplifyArticleSample.html`,
+ { userContextId: 1 }
+ );
+ });
+
+ const helper = new PrintHelper(tab.linkedBrowser);
+
+ helper.assertDialogClosed();
+ await helper.startPrint();
+ helper.assertDialogOpen();
+
+ let file = helper.mockFilePicker("browser_print_in_container.pdf");
+ await helper.assertPrintToFile(file, () => {
+ helper.click(helper.get("print-button"));
+ });
+
+ ok(true, "We did not crash.");
+
+ BrowserTestUtils.removeTab(tab);
+});
diff --git a/toolkit/components/printing/tests/browser_print_margins.js b/toolkit/components/printing/tests/browser_print_margins.js
new file mode 100644
index 0000000000..23ab1c81f7
--- /dev/null
+++ b/toolkit/components/printing/tests/browser_print_margins.js
@@ -0,0 +1,802 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+function changeDefaultToCustom(helper) {
+ let marginSelect = helper.get("margins-picker");
+ marginSelect.focus();
+ marginSelect.scrollIntoView({ block: "center" });
+ EventUtils.sendKey("space", helper.win);
+ EventUtils.sendKey("down", helper.win);
+ EventUtils.sendKey("down", helper.win);
+ EventUtils.sendKey("down", helper.win);
+ EventUtils.sendKey("return", helper.win);
+}
+
+function changeCustomToDefault(helper) {
+ let marginSelect = helper.get("margins-picker");
+ marginSelect.focus();
+ EventUtils.sendKey("space", helper.win);
+ EventUtils.sendKey("up", helper.win);
+ EventUtils.sendKey("up", helper.win);
+ EventUtils.sendKey("up", helper.win);
+ EventUtils.sendKey("return", helper.win);
+}
+
+function changeCustomToNone(helper) {
+ let marginSelect = helper.get("margins-picker");
+ marginSelect.focus();
+ EventUtils.sendKey("space", helper.win);
+ EventUtils.sendKey("up", helper.win);
+ EventUtils.sendKey("return", helper.win);
+}
+
+function assertPendingMarginsUpdate(helper) {
+ ok(
+ Object.keys(helper.win.PrintEventHandler._delayedChanges).length,
+ "At least one delayed task is added"
+ );
+ ok(
+ helper.win.PrintEventHandler._delayedSettingsChangeTask.isArmed,
+ "The update task is armed"
+ );
+}
+
+function assertNoPendingMarginsUpdate(helper) {
+ ok(
+ !helper.win.PrintEventHandler._delayedSettingsChangeTask.isArmed,
+ "The update task isn't armed"
+ );
+}
+
+async function setupLetterPaper() {
+ const INCHES_PER_POINT = 1 / 72;
+ const printerList = Cc["@mozilla.org/gfx/printerlist;1"].createInstance(
+ Ci.nsIPrinterList
+ );
+ let fallbackPaperList = await printerList.fallbackPaperList;
+ let paper = fallbackPaperList.find(
+ paper =>
+ paper.width * INCHES_PER_POINT == 8.5 &&
+ paper.height * INCHES_PER_POINT == 11
+ );
+ ok(paper, "Found a paper");
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["print.printer_Mozilla_Save_to_PDF.print_paper_id", paper.id.toString()],
+ ["print.printer_Mozilla_Save_to_PDF.print_paper_size_unit", 0],
+ [
+ "print.printer_Mozilla_Save_to_PDF.print_paper_width",
+ (paper.width * INCHES_PER_POINT).toString(),
+ ],
+ [
+ "print.printer_Mozilla_Save_to_PDF.print_paper_height",
+ (paper.height * INCHES_PER_POINT).toString(),
+ ],
+ ],
+ });
+}
+
+add_task(async function testPresetMargins() {
+ await PrintHelper.withTestPage(async helper => {
+ await helper.startPrint();
+ await helper.openMoreSettings();
+
+ await helper.assertSettingsChanged(
+ { marginTop: 0.5, marginRight: 0.5, marginBottom: 0.5, marginLeft: 0.5 },
+ { marginTop: 0.25, marginRight: 1, marginBottom: 2, marginLeft: 0.75 },
+ async () => {
+ let marginSelect = helper.get("margins-picker");
+ let customMargins = helper.get("custom-margins");
+
+ ok(customMargins.hidden, "Custom margins are hidden");
+ is(marginSelect.value, "default", "Default margins set");
+
+ this.changeDefaultToCustom(helper);
+
+ is(marginSelect.value, "custom", "Custom margins are now set");
+ ok(!customMargins.hidden, "Custom margins are present");
+ // Check that values are initialized to correct values
+ is(
+ helper.get("custom-margin-top").value,
+ "0.50",
+ "Top margin placeholder is correct"
+ );
+ is(
+ helper.get("custom-margin-right").value,
+ "0.50",
+ "Right margin placeholder is correct"
+ );
+ is(
+ helper.get("custom-margin-bottom").value,
+ "0.50",
+ "Bottom margin placeholder is correct"
+ );
+ is(
+ helper.get("custom-margin-left").value,
+ "0.50",
+ "Left margin placeholder is correct"
+ );
+
+ await helper.awaitAnimationFrame();
+
+ await helper.text(helper.get("custom-margin-top"), "0.25");
+ await helper.text(helper.get("custom-margin-right"), "1");
+ await helper.text(helper.get("custom-margin-bottom"), "2");
+ await helper.text(helper.get("custom-margin-left"), "0.75");
+
+ assertPendingMarginsUpdate(helper);
+
+ // Wait for the preview to update, the margin options delay updates by
+ // INPUT_DELAY_MS, which is 500ms.
+ await helper.waitForSettingsEvent();
+ }
+ );
+ await helper.closeDialog();
+ });
+});
+
+add_task(async function testHeightError() {
+ await PrintHelper.withTestPage(async helper => {
+ await helper.startPrint();
+ await helper.openMoreSettings();
+ this.changeDefaultToCustom(helper);
+
+ await helper.assertSettingsNotChanged(
+ { marginTop: 0.5, marginRight: 0.5, marginBottom: 0.5, marginLeft: 0.5 },
+ async () => {
+ let marginError = helper.get("error-invalid-margin");
+ ok(marginError.hidden, "Margin error is hidden");
+
+ await helper.text(helper.get("custom-margin-top"), "20");
+ await BrowserTestUtils.waitForAttributeRemoval("hidden", marginError);
+
+ ok(!marginError.hidden, "Margin error is showing");
+ assertNoPendingMarginsUpdate(helper);
+ }
+ );
+ await helper.closeDialog();
+ });
+});
+
+add_task(async function testWidthError() {
+ await PrintHelper.withTestPage(async helper => {
+ await helper.startPrint();
+ await helper.openMoreSettings();
+ this.changeDefaultToCustom(helper);
+
+ await helper.assertSettingsNotChanged(
+ { marginTop: 0.5, marginRight: 0.5, marginBottom: 0.5, marginLeft: 0.5 },
+ async () => {
+ let marginError = helper.get("error-invalid-margin");
+ ok(marginError.hidden, "Margin error is hidden");
+
+ await helper.text(helper.get("custom-margin-right"), "20");
+ await BrowserTestUtils.waitForAttributeRemoval("hidden", marginError);
+
+ ok(!marginError.hidden, "Margin error is showing");
+ assertNoPendingMarginsUpdate(helper);
+ }
+ );
+ await helper.closeDialog();
+ });
+});
+
+add_task(async function testInvalidMarginsReset() {
+ await PrintHelper.withTestPage(async helper => {
+ await helper.startPrint();
+ await helper.openMoreSettings();
+ this.changeDefaultToCustom(helper);
+ let marginError = helper.get("error-invalid-margin");
+
+ await helper.assertSettingsNotChanged(
+ { marginTop: 0.5, marginRight: 0.5, marginBottom: 0.5, marginLeft: 0.5 },
+ async () => {
+ ok(marginError.hidden, "Margin error is hidden");
+
+ await helper.awaitAnimationFrame();
+ await helper.text(helper.get("custom-margin-top"), "20");
+ await helper.text(helper.get("custom-margin-right"), "20");
+ assertNoPendingMarginsUpdate(helper);
+ await BrowserTestUtils.waitForAttributeRemoval("hidden", marginError);
+ }
+ );
+
+ this.changeCustomToDefault(helper);
+ assertNoPendingMarginsUpdate(helper);
+ await BrowserTestUtils.waitForCondition(
+ () => marginError.hidden,
+ "Wait for margin error to be hidden"
+ );
+ this.changeDefaultToCustom(helper);
+ helper.assertSettingsMatch({
+ marginTop: 0.5,
+ marginRight: 0.5,
+ marginBottom: 0.5,
+ marginLeft: 0.5,
+ });
+
+ is(
+ helper.get("margins-picker").value,
+ "custom",
+ "The custom option is selected"
+ );
+ is(
+ helper.get("custom-margin-top").value,
+ "0.50",
+ "Top margin placeholder is correct"
+ );
+ is(
+ helper.get("custom-margin-right").value,
+ "0.50",
+ "Right margin placeholder is correct"
+ );
+ is(
+ helper.get("custom-margin-bottom").value,
+ "0.50",
+ "Bottom margin placeholder is correct"
+ );
+ is(
+ helper.get("custom-margin-left").value,
+ "0.50",
+ "Left margin placeholder is correct"
+ );
+ await BrowserTestUtils.waitForCondition(
+ () => marginError.hidden,
+ "Wait for margin error to be hidden"
+ );
+ await helper.closeDialog();
+ });
+});
+
+add_task(async function testChangeInvalidToValidUpdate() {
+ await PrintHelper.withTestPage(async helper => {
+ await setupLetterPaper();
+ await helper.startPrint();
+ await helper.openMoreSettings();
+ this.changeDefaultToCustom(helper);
+ await helper.awaitAnimationFrame();
+ let marginError = helper.get("error-invalid-margin");
+
+ await helper.text(helper.get("custom-margin-bottom"), "11");
+ assertNoPendingMarginsUpdate(helper);
+ await BrowserTestUtils.waitForAttributeRemoval("hidden", marginError);
+ ok(!marginError.hidden, "Margin error is showing");
+ helper.assertSettingsMatch({
+ marginTop: 0.5,
+ marginRight: 0.5,
+ marginBottom: 0.5,
+ marginLeft: 0.5,
+ paperId: "na_letter",
+ });
+
+ await helper.text(helper.get("custom-margin-top"), "1");
+ assertNoPendingMarginsUpdate(helper);
+ helper.assertSettingsMatch({
+ marginTop: 0.5,
+ marginRight: 0.5,
+ marginBottom: 0.5,
+ marginLeft: 0.5,
+ });
+ ok(!marginError.hidden, "Margin error is showing");
+
+ await helper.assertSettingsChanged(
+ { marginTop: 0.5, marginRight: 0.5, marginBottom: 0.5, marginLeft: 0.5 },
+ { marginTop: 1, marginRight: 0.5, marginBottom: 1, marginLeft: 0.5 },
+ async () => {
+ await helper.text(helper.get("custom-margin-bottom"), "1");
+
+ assertPendingMarginsUpdate(helper);
+
+ // Wait for the preview to update, the margin options delay updates by
+ // INPUT_DELAY_MS, which is 500ms.
+ await helper.waitForSettingsEvent();
+ ok(marginError.hidden, "Margin error is hidden");
+ }
+ );
+ });
+});
+
+add_task(async function testChangeInvalidCanRevalidate() {
+ await PrintHelper.withTestPage(async helper => {
+ await setupLetterPaper();
+ await helper.startPrint();
+ await helper.openMoreSettings();
+ this.changeDefaultToCustom(helper);
+ await helper.awaitAnimationFrame();
+ let marginError = helper.get("error-invalid-margin");
+
+ await helper.assertSettingsChanged(
+ { marginTop: 0.5, marginRight: 0.5, marginBottom: 0.5, marginLeft: 0.5 },
+ { marginTop: 5, marginRight: 0.5, marginBottom: 3, marginLeft: 0.5 },
+ async () => {
+ await helper.text(helper.get("custom-margin-top"), "5");
+ await helper.text(helper.get("custom-margin-bottom"), "3");
+ assertPendingMarginsUpdate(helper);
+
+ // Wait for the preview to update, the margin options delay updates by
+ // INPUT_DELAY_MS, which is 500ms.
+ await helper.waitForSettingsEvent();
+ ok(marginError.hidden, "Margin error is hidden");
+ }
+ );
+
+ await helper.text(helper.get("custom-margin-top"), "9");
+ assertNoPendingMarginsUpdate(helper);
+ await BrowserTestUtils.waitForAttributeRemoval("hidden", marginError);
+ ok(!marginError.hidden, "Margin error is showing");
+ helper.assertSettingsMatch({
+ marginTop: 5,
+ marginRight: 0.5,
+ marginBottom: 3,
+ marginLeft: 0.5,
+ paperId: "na_letter",
+ });
+
+ await helper.assertSettingsChanged(
+ { marginTop: 5, marginRight: 0.5, marginBottom: 3, marginLeft: 0.5 },
+ { marginTop: 9, marginRight: 0.5, marginBottom: 2, marginLeft: 0.5 },
+ async () => {
+ await helper.text(helper.get("custom-margin-bottom"), "2");
+ assertPendingMarginsUpdate(helper);
+
+ // Wait for the preview to update, the margin options delay updates by
+ // INPUT_DELAY_MS, which is 500ms.
+ await helper.waitForSettingsEvent();
+ ok(marginError.hidden, "Margin error is hidden");
+ }
+ );
+ });
+});
+
+add_task(async function testCustomMarginsPersist() {
+ await PrintHelper.withTestPage(async helper => {
+ await helper.startPrint();
+ await helper.openMoreSettings();
+
+ await helper.assertSettingsChanged(
+ { marginTop: 0.5, marginRight: 0.5, marginBottom: 0.5, marginLeft: 0.5 },
+ { marginTop: 0.25, marginRight: 1, marginBottom: 2, marginLeft: 0 },
+ async () => {
+ this.changeDefaultToCustom(helper);
+ await helper.awaitAnimationFrame();
+
+ await helper.text(helper.get("custom-margin-top"), "0.25");
+ await helper.text(helper.get("custom-margin-right"), "1");
+ await helper.text(helper.get("custom-margin-bottom"), "2");
+ await helper.text(helper.get("custom-margin-left"), "0");
+
+ assertPendingMarginsUpdate(helper);
+
+ // Wait for the preview to update, the margin options delay updates by
+ // INPUT_DELAY_MS, which is 500ms.
+ await helper.waitForSettingsEvent();
+ }
+ );
+
+ await helper.closeDialog();
+
+ await helper.startPrint();
+ await helper.openMoreSettings();
+
+ helper.assertSettingsMatch({
+ marginTop: 0.25,
+ marginRight: 1,
+ marginBottom: 2,
+ marginLeft: 0,
+ });
+
+ is(
+ helper.get("margins-picker").value,
+ "custom",
+ "The custom option is selected"
+ );
+ is(
+ helper.get("custom-margin-top").value,
+ "0.25",
+ "Top margin placeholder is correct"
+ );
+ is(
+ helper.get("custom-margin-right").value,
+ "1.00",
+ "Right margin placeholder is correct"
+ );
+ is(
+ helper.get("custom-margin-bottom").value,
+ "2.00",
+ "Bottom margin placeholder is correct"
+ );
+ is(
+ helper.get("custom-margin-left").value,
+ "0.00",
+ "Left margin placeholder is correct"
+ );
+ await helper.assertSettingsChanged(
+ { marginTop: 0.25, marginRight: 1, marginBottom: 2, marginLeft: 0 },
+ { marginTop: 0.5, marginRight: 0.5, marginBottom: 0.5, marginLeft: 0.5 },
+ async () => {
+ await helper.awaitAnimationFrame();
+
+ await helper.text(helper.get("custom-margin-top"), "0.50");
+ await helper.text(helper.get("custom-margin-right"), "0.50");
+ await helper.text(helper.get("custom-margin-bottom"), "0.50");
+ await helper.text(helper.get("custom-margin-left"), "0.50");
+
+ assertPendingMarginsUpdate(helper);
+
+ // Wait for the preview to update, the margin options delay updates by
+ // INPUT_DELAY_MS, which is 500ms.
+ await helper.waitForSettingsEvent();
+ }
+ );
+ await helper.closeDialog();
+ });
+});
+
+add_task(async function testChangingBetweenMargins() {
+ await PrintHelper.withTestPage(async helper => {
+ await SpecialPowers.pushPrefEnv({
+ set: [["print.printer_Mozilla_Save_to_PDF.print_margin_left", "1"]],
+ });
+
+ await helper.startPrint();
+ await helper.openMoreSettings();
+
+ let marginsPicker = helper.get("margins-picker");
+ is(marginsPicker.value, "custom", "First margin is custom");
+
+ helper.assertSettingsMatch({
+ marginTop: 0.5,
+ marginBottom: 0.5,
+ marginLeft: 1,
+ marginRight: 0.5,
+ });
+
+ info("Switch to Default margins");
+ await helper.assertSettingsChanged(
+ { marginLeft: 1 },
+ { marginLeft: 0.5 },
+ async () => {
+ let settingsChanged = helper.waitForSettingsEvent();
+ changeCustomToDefault(helper);
+ await settingsChanged;
+ }
+ );
+
+ is(marginsPicker.value, "default", "Default preset selected");
+
+ info("Switching back to Custom, should restore old margins");
+ await helper.assertSettingsChanged(
+ { marginLeft: 0.5 },
+ { marginLeft: 1 },
+ async () => {
+ let settingsChanged = helper.waitForSettingsEvent();
+ changeDefaultToCustom(helper);
+ await settingsChanged;
+ }
+ );
+
+ is(marginsPicker.value, "custom", "Custom is now selected");
+
+ info("Switching back to Default, should restore 0.5");
+ await helper.assertSettingsChanged(
+ { marginLeft: 1 },
+ { marginLeft: 0.5 },
+ async () => {
+ let settingsChanged = helper.waitForSettingsEvent();
+ changeCustomToDefault(helper);
+ await settingsChanged;
+ }
+ );
+
+ is(marginsPicker.value, "default", "Default preset is selected again");
+ });
+});
+
+add_task(async function testChangeHonoredInPrint() {
+ const mockPrinterName = "Fake Printer";
+ await PrintHelper.withTestPage(async helper => {
+ helper.addMockPrinter(mockPrinterName);
+ await helper.startPrint();
+ await helper.setupMockPrint();
+
+ helper.mockFilePicker("changedMargin.pdf");
+
+ await helper.openMoreSettings();
+ helper.assertSettingsMatch({ marginRight: 0.5 });
+ this.changeDefaultToCustom(helper);
+
+ await helper.withClosingFn(async () => {
+ await helper.text(helper.get("custom-margin-right"), "1");
+ EventUtils.sendKey("return", helper.win);
+ helper.resolvePrint();
+ });
+ helper.assertPrintedWithSettings({ marginRight: 1 });
+ });
+});
+
+add_task(async function testInvalidPrefValueHeight() {
+ await PrintHelper.withTestPage(async helper => {
+ // Set some bad prefs
+ await SpecialPowers.pushPrefEnv({
+ set: [["print.printer_Mozilla_Save_to_PDF.print_margin_top", "-1"]],
+ });
+ await helper.startPrint();
+ helper.assertSettingsMatch({
+ marginTop: 0.5,
+ marginRight: 0.5,
+ marginBottom: 0.5,
+ marginLeft: 0.5,
+ });
+ await helper.closeDialog();
+ });
+});
+
+add_task(async function testInvalidPrefValueWidth() {
+ await PrintHelper.withTestPage(async helper => {
+ // Set some bad prefs
+ await SpecialPowers.pushPrefEnv({
+ set: [["print.printer_Mozilla_Save_to_PDF.print_margin_left", "-1"]],
+ });
+ await helper.startPrint();
+ helper.assertSettingsMatch({
+ marginTop: 0.5,
+ marginRight: 0.5,
+ marginBottom: 0.5,
+ marginLeft: 0.5,
+ });
+ await helper.closeDialog();
+ });
+});
+
+add_task(async function testInvalidMarginStartup() {
+ await PrintHelper.withTestPage(async helper => {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["print.printer_Mozilla_Save_to_PDF.print_margin_right", "4"],
+ ["print.printer_Mozilla_Save_to_PDF.print_margin_left", "5"],
+ ],
+ });
+ await setupLetterPaper();
+ await helper.startPrint();
+ helper.assertSettingsMatch({
+ paperId: "na_letter",
+ marginLeft: 0.5,
+ marginRight: 0.5,
+ });
+ helper.closeDialog();
+ });
+});
+
+add_task(async function testRevalidateSwitchToNone() {
+ await PrintHelper.withTestPage(async helper => {
+ await setupLetterPaper();
+ await helper.startPrint();
+ await helper.openMoreSettings();
+ this.changeDefaultToCustom(helper);
+ await helper.awaitAnimationFrame();
+
+ await helper.text(helper.get("custom-margin-bottom"), "6");
+ await helper.text(helper.get("custom-margin-top"), "6");
+ assertNoPendingMarginsUpdate(helper);
+ helper.assertSettingsMatch({
+ marginTop: 0.5,
+ marginRight: 0.5,
+ marginBottom: 0.5,
+ marginLeft: 0.5,
+ paperId: "na_letter",
+ });
+
+ await helper.assertSettingsChanged(
+ { marginTop: 0.5, marginRight: 0.5, marginBottom: 0.5, marginLeft: 0.5 },
+ { marginTop: 6, marginRight: 0.5, marginBottom: 3, marginLeft: 0.5 },
+ async () => {
+ await helper.text(helper.get("custom-margin-bottom"), "3");
+ assertPendingMarginsUpdate(helper);
+
+ // Wait for the preview to update, the margin options delay updates by
+ // INPUT_DELAY_MS, which is 500ms.
+ await helper.waitForSettingsEvent();
+ }
+ );
+
+ await helper.assertSettingsChanged(
+ { marginTop: 6, marginRight: 0.5, marginBottom: 3, marginLeft: 0.5 },
+ { marginTop: 0, marginRight: 0, marginBottom: 0, marginLeft: 0 },
+ async () => {
+ this.changeCustomToNone(helper);
+ is(
+ helper.get("margins-picker").value,
+ "none",
+ "No margins are now set"
+ );
+
+ // Wait for the preview to update, the margin options delay updates by
+ // INPUT_DELAY_MS, which is 500ms.
+ await helper.waitForSettingsEvent();
+ }
+ );
+ });
+});
+
+add_task(async function testInvalidMarginResetAfterDestinationChange() {
+ const mockPrinterName = "Fake Printer";
+ await PrintHelper.withTestPage(async helper => {
+ helper.addMockPrinter(mockPrinterName);
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["print.printer_Fake_Printer.print_paper_id", "na_letter"],
+ ["print.printer_Fake_Printer.print_paper_size_unit", 0],
+ ["print.printer_Fake_Printer.print_paper_width", "8.5"],
+ ["print.printer_Fake_Printer.print_paper_height", "11"],
+ ],
+ });
+ await helper.startPrint();
+
+ let destinationPicker = helper.get("printer-picker");
+
+ await helper.openMoreSettings();
+ changeDefaultToCustom(helper);
+ await helper.awaitAnimationFrame();
+
+ let marginError = helper.get("error-invalid-margin");
+
+ await helper.assertSettingsNotChanged(
+ { marginTop: 0.5, marginRight: 0.5, marginBottom: 0.5, marginLeft: 0.5 },
+ async () => {
+ ok(marginError.hidden, "Margin error is hidden");
+
+ await helper.text(helper.get("custom-margin-top"), "20");
+ await BrowserTestUtils.waitForAttributeRemoval("hidden", marginError);
+
+ ok(!marginError.hidden, "Margin error is showing");
+ assertNoPendingMarginsUpdate(helper);
+ }
+ );
+
+ is(destinationPicker.disabled, false, "Destination picker is enabled");
+
+ helper.dispatchSettingsChange({ printerName: mockPrinterName });
+ await BrowserTestUtils.waitForCondition(
+ () => marginError.hidden,
+ "Wait for margin error to be hidden"
+ );
+
+ helper.assertSettingsMatch({
+ marginTop: 0.5,
+ marginRight: 0.5,
+ marginBottom: 0.5,
+ marginLeft: 0.5,
+ });
+
+ await helper.closeDialog();
+ });
+});
+
+add_task(async function testRevalidateCustomMarginsAfterPaperChanges() {
+ await PrintHelper.withTestPage(async helper => {
+ await helper.startPrint();
+ helper.dispatchSettingsChange({ paperId: "iso_a3" });
+ await helper.openMoreSettings();
+ this.changeDefaultToCustom(helper);
+ await helper.awaitAnimationFrame();
+ let marginError = helper.get("error-invalid-margin");
+
+ await helper.assertSettingsChanged(
+ { marginTop: 0.5, marginRight: 0.5, marginBottom: 0.5, marginLeft: 0.5 },
+ { marginTop: 5, marginRight: 5, marginBottom: 5, marginLeft: 5 },
+ async () => {
+ await helper.text(helper.get("custom-margin-top"), "5");
+ await helper.text(helper.get("custom-margin-bottom"), "5");
+ await helper.text(helper.get("custom-margin-right"), "5");
+ await helper.text(helper.get("custom-margin-left"), "5");
+ assertPendingMarginsUpdate(helper);
+
+ // Wait for the preview to update, the margin options delay updates by
+ // INPUT_DELAY_MS, which is 500ms.
+ await helper.waitForSettingsEvent();
+ ok(marginError.hidden, "Margin error is hidden");
+ }
+ );
+
+ await helper.assertSettingsChanged(
+ { marginTop: 5, marginRight: 5, marginBottom: 5, marginLeft: 5 },
+ { marginTop: 0.5, marginRight: 0.5, marginBottom: 0.5, marginLeft: 0.5 },
+ async () => {
+ helper.dispatchSettingsChange({ paperId: "iso_a5" });
+
+ // Wait for the preview to update, the margin options delay updates by
+ // INPUT_DELAY_MS, which is 500ms.
+ await helper.waitForSettingsEvent();
+ ok(marginError.hidden, "Margin error is hidden");
+ }
+ );
+ });
+});
+
+add_task(async function testRevalidateCustomMarginsAfterOrientationChanges() {
+ await PrintHelper.withTestPage(async helper => {
+ await setupLetterPaper();
+ await helper.startPrint();
+ await helper.openMoreSettings();
+ this.changeDefaultToCustom(helper);
+ await helper.awaitAnimationFrame();
+ let marginError = helper.get("error-invalid-margin");
+
+ await helper.assertSettingsChanged(
+ { marginTop: 0.5, marginRight: 0.5, marginBottom: 0.5, marginLeft: 0.5 },
+ { marginTop: 5, marginRight: 0.5, marginBottom: 5, marginLeft: 0.5 },
+ async () => {
+ await helper.text(helper.get("custom-margin-top"), "5");
+ await helper.text(helper.get("custom-margin-bottom"), "5");
+ assertPendingMarginsUpdate(helper);
+
+ // Wait for the preview to update, the margin options delay updates by
+ // INPUT_DELAY_MS, which is 500ms.
+ await helper.waitForSettingsEvent();
+ ok(marginError.hidden, "Margin error is hidden");
+ }
+ );
+
+ await helper.assertSettingsChanged(
+ { marginTop: 5, marginRight: 0.5, marginBottom: 5, marginLeft: 0.5 },
+ { marginTop: 0.5, marginRight: 0.5, marginBottom: 0.5, marginLeft: 0.5 },
+ async () => {
+ helper.dispatchSettingsChange({ orientation: 1 });
+ await helper.waitForSettingsEvent();
+ ok(marginError.hidden, "Margin error is hidden");
+ }
+ );
+ });
+});
+
+add_task(async function testResetMarginPersists() {
+ await PrintHelper.withTestPage(async helper => {
+ await setupLetterPaper();
+ await helper.startPrint();
+
+ await helper.openMoreSettings();
+ this.changeDefaultToCustom(helper);
+ await helper.awaitAnimationFrame();
+ let marginError = helper.get("error-invalid-margin");
+
+ await helper.assertSettingsChanged(
+ { marginTop: 0.5, marginRight: 0.5, marginBottom: 0.5, marginLeft: 0.5 },
+ { marginTop: 0.5, marginRight: 4, marginBottom: 0.5, marginLeft: 4.5 },
+ async () => {
+ await helper.text(helper.get("custom-margin-right"), "4");
+ await helper.text(helper.get("custom-margin-left"), "4.5");
+
+ // Wait for the preview to update, the margin options delay updates by
+ // INPUT_DELAY_MS, which is 500ms.
+ await helper.waitForSettingsEvent();
+ ok(marginError.hidden, "Margin error is hidden");
+ }
+ );
+
+ await helper.assertSettingsChanged(
+ { marginTop: 0.5, marginRight: 4, marginBottom: 0.5, marginLeft: 4.5 },
+ { marginTop: 0.5, marginRight: 0.5, marginBottom: 0.5, marginLeft: 0.5 },
+ async () => {
+ helper.dispatchSettingsChange({ paperId: "iso_a4" });
+
+ // Wait for the preview to update, the margin options delay updates by
+ // INPUT_DELAY_MS, which is 500ms.
+ await helper.waitForSettingsEvent();
+ ok(marginError.hidden, "Margin error is hidden");
+ }
+ );
+
+ await helper.assertSettingsNotChanged(
+ { marginTop: 0.5, marginRight: 0.5, marginBottom: 0.5, marginLeft: 0.5 },
+ async () => {
+ helper.dispatchSettingsChange({ paperId: "iso_a5" });
+ await helper.waitForSettingsEvent();
+ ok(marginError.hidden, "Margin error is hidden");
+ }
+ );
+
+ await helper.closeDialog();
+ });
+});
diff --git a/toolkit/components/printing/tests/browser_print_page_range.js b/toolkit/components/printing/tests/browser_print_page_range.js
new file mode 100644
index 0000000000..d5d64262c0
--- /dev/null
+++ b/toolkit/components/printing/tests/browser_print_page_range.js
@@ -0,0 +1,471 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+function changeAllToCustom(helper) {
+ let rangeSelect = helper.get("range-picker");
+ rangeSelect.focus();
+ rangeSelect.scrollIntoView({ block: "center" });
+ EventUtils.sendKey("space", helper.win);
+ EventUtils.sendKey("down", helper.win);
+ EventUtils.sendKey("return", helper.win);
+}
+
+function changeCustomToAll(helper) {
+ let rangeSelect = helper.get("range-picker");
+ rangeSelect.focus();
+ rangeSelect.scrollIntoView({ block: "center" });
+ EventUtils.sendKey("space", helper.win);
+ EventUtils.sendKey("up", helper.win);
+ EventUtils.sendKey("return", helper.win);
+}
+
+function getSheetCount(helper) {
+ return helper.doc.l10n.getAttributes(helper.get("sheet-count")).args
+ .sheetCount;
+}
+
+add_task(async function testRangeResetAfterScale() {
+ const mockPrinterName = "Fake Printer";
+ await PrintHelper.withTestPage(async helper => {
+ helper.addMockPrinter(mockPrinterName);
+ await helper.startPrint();
+ await helper.setupMockPrint();
+
+ helper.mockFilePicker("changeRangeFromScale.pdf");
+ changeAllToCustom(helper);
+
+ await helper.openMoreSettings();
+ let scaleRadio = helper.get("percent-scale-choice");
+ await helper.waitForPreview(() => helper.click(scaleRadio));
+ let percentScale = helper.get("percent-scale");
+ await helper.waitForPreview(() => helper.text(percentScale, "200"));
+
+ let customRange = helper.get("custom-range");
+ let rangeError = helper.get("error-invalid-range");
+ await helper.waitForPreview(() => {
+ helper.text(customRange, "3");
+ });
+
+ ok(rangeError.hidden, "Range error is hidden");
+
+ await helper.text(percentScale, "10");
+ EventUtils.sendKey("return", helper.win);
+
+ await BrowserTestUtils.waitForAttributeRemoval("hidden", rangeError);
+ ok(!rangeError.hidden, "Range error is showing");
+ await helper.closeDialog();
+ });
+});
+
+add_task(async function testRangeResetAfterPaperSize() {
+ await PrintHelper.withTestPage(async helper => {
+ await helper.startPrint();
+ await helper.waitForPreview(() =>
+ helper.dispatchSettingsChange({ paperId: "iso_a5" })
+ );
+ await helper.setupMockPrint();
+
+ await helper.openMoreSettings();
+ let scaleRadio = helper.get("percent-scale-choice");
+ await helper.waitForPreview(() => helper.click(scaleRadio));
+ let percentScale = helper.get("percent-scale");
+ await helper.waitForPreview(() => helper.text(percentScale, "200"));
+
+ let customRange = helper.get("custom-range");
+ changeAllToCustom(helper);
+ await BrowserTestUtils.waitForAttributeRemoval("hidden", customRange);
+
+ let rangeError = helper.get("error-invalid-range");
+ await helper.waitForPreview(() => {
+ helper.text(customRange, "6");
+ });
+
+ ok(rangeError.hidden, "Range error is hidden");
+
+ helper.dispatchSettingsChange({ paperId: "iso_a3" });
+ await BrowserTestUtils.waitForCondition(
+ () => helper.get("paper-size-picker").value == "iso_a3",
+ "Wait for paper size select to update"
+ );
+ EventUtils.sendKey("return", helper.win);
+
+ await BrowserTestUtils.waitForAttributeRemoval("hidden", rangeError);
+ ok(!rangeError.hidden, "Range error is showing");
+ await helper.closeDialog();
+ });
+});
+
+add_task(async function testInvalidRangeResetAfterDestinationChange() {
+ const mockPrinterName = "Fake Printer";
+ await PrintHelper.withTestPage(async helper => {
+ helper.addMockPrinter(mockPrinterName);
+ await helper.startPrint();
+
+ let destinationPicker = helper.get("printer-picker");
+ let customPageRange = helper.get("custom-range");
+
+ await helper.assertSettingsNotChanged({ pageRanges: [] }, async () => {
+ changeAllToCustom(helper);
+ });
+ let rangeError = helper.get("error-invalid-range");
+
+ await helper.assertSettingsNotChanged({ pageRanges: [] }, async () => {
+ ok(rangeError.hidden, "Range error is hidden");
+ await helper.text(customPageRange, "9");
+ await BrowserTestUtils.waitForAttributeRemoval("hidden", rangeError);
+ ok(!rangeError.hidden, "Range error is showing");
+ });
+
+ is(destinationPicker.disabled, false, "Destination picker is enabled");
+
+ // Select a new printer
+ helper.dispatchSettingsChange({ printerName: mockPrinterName });
+ await BrowserTestUtils.waitForCondition(
+ () => rangeError.hidden,
+ "Wait for range error to be hidden"
+ );
+ is(customPageRange.value, "", "Page range has reset");
+ await helper.closeDialog();
+ });
+});
+
+add_task(async function testPageRangeSets() {
+ await PrintHelper.withTestPage(async helper => {
+ await helper.startPrint();
+
+ let customRange = helper.get("custom-range");
+ let pageRangeInput = helper.get("page-range-input");
+ let invalidError = helper.get("error-invalid-range");
+ let invalidOverflowError = helper.get("error-invalid-start-range-overflow");
+
+ ok(customRange.hidden, "Custom range input is hidden");
+
+ changeAllToCustom(helper);
+ await BrowserTestUtils.waitForAttributeRemoval("hidden", customRange);
+
+ ok(!customRange.hidden, "Custom range is showing");
+
+ // We need to set the input to something to ensure we do not return early
+ // out of our validation function
+ helper.text(helper.get("custom-range"), ",");
+
+ let validStrings = {
+ "1": [1, 1],
+ "1,": [1, 1],
+ "2": [2, 2],
+ "1-2": [1, 2],
+ "1,2": [1, 2],
+ "1,2,": [1, 2],
+ "2,1": [1, 2],
+ "1,3": [1, 1, 3, 3],
+ "1-1,3": [1, 1, 3, 3],
+ "1,3-3": [1, 1, 3, 3],
+ "10-33": [10, 33],
+ "1-": [1, 50],
+ "-": [],
+ "-20": [1, 20],
+ "-,1": [],
+ "-1,1-": [],
+ "-1,1-2": [1, 2],
+ ",9": [9, 9],
+ ",": [],
+ "1,2,1,20,5": [1, 2, 5, 5, 20, 20],
+ "1-17,4,12-19": [1, 19],
+ "43-46,42,47-": [42, 50],
+ };
+
+ for (let [str, expected] of Object.entries(validStrings)) {
+ pageRangeInput._validateRangeInput(str, 50);
+ let pageRanges = pageRangeInput.formatPageRange();
+
+ is(
+ expected.every((page, index) => page === pageRanges[index]),
+ true,
+ `Expected page range for "${str}" matches "${expected}"`
+ );
+
+ ok(invalidError.hidden, "Generic error message is hidden");
+ ok(invalidOverflowError.hidden, "Start overflow error message is hidden");
+ }
+
+ let invalidStrings = ["51", "1,51", "1-51", "4-1", "--", "0", "-90"];
+
+ for (let str of invalidStrings) {
+ pageRangeInput._validateRangeInput(str, 50);
+ is(pageRangeInput._pagesSet.size, 0, `There are no pages in the set`);
+ ok(!pageRangeInput.validity, "Input is invalid");
+ }
+
+ await helper.closeDialog();
+ });
+});
+
+add_task(async function testRangeError() {
+ await PrintHelper.withTestPage(async helper => {
+ await helper.startPrint();
+
+ changeAllToCustom(helper);
+
+ let invalidError = helper.get("error-invalid-range");
+ let invalidOverflowError = helper.get("error-invalid-start-range-overflow");
+
+ ok(invalidError.hidden, "Generic error message is hidden");
+ ok(invalidOverflowError.hidden, "Start overflow error message is hidden");
+
+ helper.text(helper.get("custom-range"), "4");
+
+ await BrowserTestUtils.waitForAttributeRemoval("hidden", invalidError);
+
+ ok(!invalidError.hidden, "Generic error message is showing");
+ ok(invalidOverflowError.hidden, "Start overflow error message is hidden");
+
+ await helper.closeDialog();
+ });
+});
+
+add_task(async function testStartOverflowRangeError() {
+ await PrintHelper.withTestPage(async helper => {
+ await helper.startPrint();
+
+ changeAllToCustom(helper);
+
+ await helper.openMoreSettings();
+ let scaleRadio = helper.get("percent-scale-choice");
+ await helper.waitForPreview(() => helper.click(scaleRadio));
+ let percentScale = helper.get("percent-scale");
+ await helper.waitForPreview(() => helper.text(percentScale, "200"));
+
+ let invalidError = helper.get("error-invalid-range");
+ let invalidOverflowError = helper.get("error-invalid-start-range-overflow");
+
+ ok(invalidError.hidden, "Generic error message is hidden");
+ ok(invalidOverflowError.hidden, "Start overflow error message is hidden");
+
+ helper.text(helper.get("custom-range"), "2-1");
+
+ await BrowserTestUtils.waitForAttributeRemoval(
+ "hidden",
+ invalidOverflowError
+ );
+
+ ok(invalidError.hidden, "Generic error message is hidden");
+ ok(!invalidOverflowError.hidden, "Start overflow error message is showing");
+
+ await helper.closeDialog();
+ });
+});
+
+add_task(async function testErrorClearedAfterSwitchingToAll() {
+ await PrintHelper.withTestPage(async helper => {
+ await helper.startPrint();
+
+ changeAllToCustom(helper);
+
+ let customRange = helper.get("custom-range");
+ let rangeError = helper.get("error-invalid-range");
+ ok(rangeError.hidden, "Generic error message is hidden");
+
+ helper.text(customRange, "3");
+
+ await BrowserTestUtils.waitForAttributeRemoval("hidden", rangeError);
+ ok(!rangeError.hidden, "Generic error message is showing");
+
+ changeCustomToAll(helper);
+
+ await BrowserTestUtils.waitForCondition(
+ () => rangeError.hidden,
+ "Wait for range error to be hidden"
+ );
+ ok(customRange.hidden, "Custom range is hidden");
+ await helper.closeDialog();
+ });
+});
+
+add_task(async function testPageCountChangeNoRangeNoRerender() {
+ await PrintHelper.withTestPage(async helper => {
+ let customPrinter = "A printer";
+ helper.addMockPrinter(customPrinter);
+
+ await helper.startPrint();
+
+ await helper.assertSettingsChanged(
+ { printerName: "Mozilla Save to PDF" },
+ { printerName: customPrinter },
+ async () => {
+ let destinationPicker = helper.get("printer-picker");
+ destinationPicker.focus();
+ await Promise.all([
+ helper.waitForPreview(() =>
+ helper.dispatchSettingsChange({ printerName: customPrinter })
+ ),
+ helper.waitForSettingsEvent(),
+ ]);
+ }
+ );
+
+ // Change a setting that will change the number of pages. Since pageRanges
+ // is set to "all" then there shouldn't be a re-render because of it.
+ let previewUpdateCount = 0;
+ ok(!helper.hasPendingPreview, "No preview is pending");
+ helper.doc.addEventListener("preview-updated", () => previewUpdateCount++);
+
+ // Ensure the sheet count will change.
+ let initialSheetCount = getSheetCount(helper);
+
+ await helper.assertSettingsChanged(
+ { marginLeft: 0.5, marginRight: 0.5 },
+ { marginLeft: 3, marginRight: 3 },
+ async () => {
+ await Promise.all([
+ helper.waitForPreview(() =>
+ helper.dispatchSettingsChange({ marginLeft: 3, marginRight: 3 })
+ ),
+ BrowserTestUtils.waitForEvent(helper.doc, "page-count"),
+ helper.waitForSettingsEvent(),
+ ]);
+ }
+ );
+
+ let newSheetCount = getSheetCount(helper);
+ ok(
+ initialSheetCount < newSheetCount,
+ `There are more sheets now ${initialSheetCount} < ${newSheetCount}`
+ );
+
+ ok(!helper.hasPendingPreview, "No preview is pending");
+ is(previewUpdateCount, 1, "Only one preview update fired");
+
+ await helper.closeDialog();
+ });
+});
+
+add_task(async function testPageCountChangeRangeNoRerender() {
+ await PrintHelper.withTestPage(async helper => {
+ let customPrinter = "A printer";
+ helper.addMockPrinter(customPrinter);
+
+ await helper.startPrint();
+
+ await helper.assertSettingsChanged(
+ { printerName: "Mozilla Save to PDF", pageRanges: [] },
+ { printerName: customPrinter, pageRanges: [1, 1] },
+ async () => {
+ let destinationPicker = helper.get("printer-picker");
+ destinationPicker.focus();
+ await Promise.all([
+ helper.waitForPreview(() =>
+ helper.dispatchSettingsChange({ printerName: customPrinter })
+ ),
+ helper.waitForSettingsEvent(),
+ ]);
+
+ await helper.waitForPreview(async () => {
+ changeAllToCustom(helper);
+ helper.text(helper.get("custom-range"), "1");
+ });
+ }
+ );
+
+ // Change a setting that will change the number of pages. Since pageRanges
+ // is set to a page that is in the new range, there shouldn't be a re-render.
+ let previewUpdateCount = 0;
+ ok(!helper.hasPendingPreview, "No preview is pending");
+ helper.doc.addEventListener("preview-updated", () => previewUpdateCount++);
+
+ await helper.assertSettingsChanged(
+ { marginLeft: 0.5, marginRight: 0.5 },
+ { marginLeft: 3, marginRight: 3 },
+ async () => {
+ await Promise.all([
+ helper.waitForPreview(() =>
+ helper.dispatchSettingsChange({ marginLeft: 3, marginRight: 3 })
+ ),
+ BrowserTestUtils.waitForEvent(helper.doc, "page-count"),
+ helper.waitForSettingsEvent(),
+ ]);
+ }
+ );
+
+ let newSheetCount = getSheetCount(helper);
+ is(newSheetCount, 1, "There's still only one sheet");
+
+ ok(!helper.hasPendingPreview, "No preview is pending");
+ is(previewUpdateCount, 1, "Only one preview update fired");
+
+ await helper.closeDialog();
+ });
+});
+
+add_task(async function testPageCountChangeRangeRerender() {
+ await PrintHelper.withTestPage(async helper => {
+ let customPrinter = "A printer";
+ helper.addMockPrinter(customPrinter);
+
+ await helper.startPrint();
+
+ await helper.assertSettingsChanged(
+ { printerName: "Mozilla Save to PDF", pageRanges: [] },
+ { printerName: customPrinter, pageRanges: [1, 1] },
+ async () => {
+ let destinationPicker = helper.get("printer-picker");
+ destinationPicker.focus();
+ await Promise.all([
+ helper.waitForPreview(() =>
+ helper.dispatchSettingsChange({ printerName: customPrinter })
+ ),
+ helper.waitForSettingsEvent(),
+ ]);
+
+ await helper.waitForPreview(async () => {
+ changeAllToCustom(helper);
+ helper.text(helper.get("custom-range"), "1-");
+ });
+ }
+ );
+
+ // Change a setting that will change the number of pages. Since pageRanges
+ // is from 1-N the calculated page range will need to be updated.
+ let previewUpdateCount = 0;
+ ok(!helper.hasPendingPreview, "No preview is pending");
+ helper.doc.addEventListener("preview-updated", () => previewUpdateCount++);
+ let renderedTwice = BrowserTestUtils.waitForCondition(
+ () => previewUpdateCount == 2
+ );
+
+ // Ensure the sheet count will change.
+ let initialSheetCount = getSheetCount(helper);
+
+ await helper.assertSettingsChanged(
+ { marginLeft: 0.5, marginRight: 0.5 },
+ { marginLeft: 3, marginRight: 3 },
+ async () => {
+ await Promise.all([
+ helper.waitForPreview(() =>
+ helper.dispatchSettingsChange({ marginLeft: 3, marginRight: 3 })
+ ),
+ BrowserTestUtils.waitForEvent(helper.doc, "page-count"),
+ helper.waitForSettingsEvent(),
+ ]);
+ await renderedTwice;
+ }
+ );
+
+ let newSheetCount = getSheetCount(helper);
+ ok(
+ initialSheetCount < newSheetCount,
+ `There are more sheets now ${initialSheetCount} < ${newSheetCount}`
+ );
+ Assert.deepEqual(
+ helper.viewSettings.pageRanges,
+ [1, newSheetCount],
+ "The new range is the updated full page range"
+ );
+
+ ok(!helper.hasPendingPreview, "No preview is pending");
+ is(previewUpdateCount, 2, "Preview updated again to show new page range");
+
+ await helper.closeDialog();
+ });
+});
diff --git a/toolkit/components/printing/tests/browser_print_paper_sizes.js b/toolkit/components/printing/tests/browser_print_paper_sizes.js
new file mode 100644
index 0000000000..6f81a2a0b4
--- /dev/null
+++ b/toolkit/components/printing/tests/browser_print_paper_sizes.js
@@ -0,0 +1,120 @@
+"use strict";
+
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+async function selectPaperOptionWithValue(helper, value) {
+ let paperSelect = helper.get("paper-size-picker");
+ paperSelect.dispatchSettingsChange({
+ paperId: value,
+ });
+ await helper.awaitAnimationFrame();
+}
+
+add_task(async function testBadPaperSizeUnitCorrection() {
+ await PrintHelper.withTestPage(async helper => {
+ // Set prefs to select a non-default paper size
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["print.printer_Mozilla_Save_to_PDF.print_paper_id", "na_letter"],
+ // paperSizeUnit is a bogus value, but the dimensions are correct for inches
+ ["print.printer_Mozilla_Save_to_PDF.print_paper_size_unit", 99],
+ ["print.printer_Mozilla_Save_to_PDF.print_paper_height", "11.0"],
+ ["print.printer_Mozilla_Save_to_PDF.print_paper_width", "8.50"],
+ ],
+ });
+ await helper.startPrint();
+
+ let paperSelect = helper.get("paper-size-picker");
+ is(paperSelect.value, "na_letter", "The expected paper size is selected");
+ is(
+ helper.viewSettings.paperId,
+ "na_letter",
+ "The settings have the expected paperId"
+ );
+ is(
+ helper.viewSettings.paperSizeUnit,
+ helper.settings.kPaperSizeInches,
+ "Check paperSizeUnit"
+ );
+ is(helper.viewSettings.paperWidth.toFixed(1), "8.5", "Check paperWidth");
+ is(helper.viewSettings.paperHeight.toFixed(1), "11.0", "Check paperHeight");
+
+ await selectPaperOptionWithValue(helper, "iso_a3");
+ is(paperSelect.value, "iso_a3", "The expected paper size is selected");
+ is(
+ helper.viewSettings.paperId,
+ "iso_a3",
+ "The settings have the expected paperId"
+ );
+ is(
+ helper.viewSettings.paperSizeUnit,
+ helper.settings.kPaperSizeInches,
+ "Check paperSizeUnit"
+ );
+ is(helper.viewSettings.paperWidth.toFixed(1), "11.7", "Check paperWidth");
+ is(helper.viewSettings.paperHeight.toFixed(1), "16.5", "Check paperHeight");
+
+ await SpecialPowers.popPrefEnv();
+ await helper.closeDialog();
+ });
+});
+
+add_task(async function testMismatchedPaperSizeUnitCorrection() {
+ await PrintHelper.withTestPage(async helper => {
+ // Set prefs to select a non-default paper size
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["print.printer_Mozilla_Save_to_PDF.print_paper_id", "na_ledger"],
+ // paperSizeUnit is millimeters, but the dimensions are correct for inches
+ ["print.printer_Mozilla_Save_to_PDF.print_paper_size_unit", 1],
+ ["print.printer_Mozilla_Save_to_PDF.print_paper_width", "11.0"],
+ ["print.printer_Mozilla_Save_to_PDF.print_paper_height", "17.0"],
+ ],
+ });
+ await helper.startPrint();
+
+ let paperSelect = helper.get("paper-size-picker");
+ is(paperSelect.value, "na_ledger", "The expected paper size is selected");
+
+ // We expect to honor the paperSizeUnit, and convert paperWidth/Height to that unit
+ is(
+ helper.viewSettings.paperId,
+ "na_ledger",
+ "The settings have the expected paperId"
+ );
+ is(
+ helper.viewSettings.paperSizeUnit,
+ helper.settings.kPaperSizeMillimeters,
+ "Check paperSizeUnit"
+ );
+ is(helper.viewSettings.paperWidth.toFixed(1), "279.4", "Check paperWidth");
+ is(
+ helper.viewSettings.paperHeight.toFixed(1),
+ "431.8",
+ "Check paperHeight"
+ );
+
+ await selectPaperOptionWithValue(helper, "iso_a3");
+ is(paperSelect.value, "iso_a3", "The expected paper size is selected");
+ is(
+ helper.viewSettings.paperId,
+ "iso_a3",
+ "The settings have the expected paperId"
+ );
+ is(
+ helper.viewSettings.paperSizeUnit,
+ helper.settings.kPaperSizeMillimeters,
+ "Check paperSizeUnit"
+ );
+ is(helper.viewSettings.paperWidth.toFixed(1), "297.0", "Check paperWidth");
+ is(
+ helper.viewSettings.paperHeight.toFixed(1),
+ "420.0",
+ "Check paperHeight"
+ );
+
+ await SpecialPowers.popPrefEnv();
+ await helper.closeDialog();
+ });
+});
diff --git a/toolkit/components/printing/tests/browser_print_scaling.js b/toolkit/components/printing/tests/browser_print_scaling.js
new file mode 100644
index 0000000000..85b8a68449
--- /dev/null
+++ b/toolkit/components/printing/tests/browser_print_scaling.js
@@ -0,0 +1,46 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function testInvalidScaleResetAfterDestinationChange() {
+ const mockPrinterName = "Fake Printer";
+ await PrintHelper.withTestPage(async helper => {
+ helper.addMockPrinter(mockPrinterName);
+ await helper.startPrint();
+
+ let destinationPicker = helper.get("printer-picker");
+
+ await helper.openMoreSettings();
+ let scaleRadio = helper.get("percent-scale-choice");
+ await helper.assertSettingsChanged(
+ { shrinkToFit: true },
+ { shrinkToFit: false },
+ async () => {
+ await helper.waitForPreview(() => helper.click(scaleRadio));
+ }
+ );
+ let percentScale = helper.get("percent-scale");
+
+ let scaleError = helper.get("error-invalid-scale");
+
+ await helper.assertSettingsNotChanged({ scaling: 1 }, async () => {
+ ok(scaleError.hidden, "Scale error is hidden");
+ await helper.text(percentScale, "9");
+ await BrowserTestUtils.waitForAttributeRemoval("hidden", scaleError);
+ ok(!scaleError.hidden, "Scale error is showing");
+ });
+
+ is(destinationPicker.disabled, false, "Destination picker is enabled");
+
+ // Select a new printer
+ await helper.dispatchSettingsChange({ printerName: mockPrinterName });
+ await BrowserTestUtils.waitForCondition(
+ () => scaleError.hidden,
+ "Wait for scale error to be hidden"
+ );
+ is(percentScale.value, "100", "Scale has reset to 100");
+
+ await helper.closeDialog();
+ });
+});
diff --git a/toolkit/components/printing/tests/browser_print_selection.js b/toolkit/components/printing/tests/browser_print_selection.js
new file mode 100644
index 0000000000..fddc599788
--- /dev/null
+++ b/toolkit/components/printing/tests/browser_print_selection.js
@@ -0,0 +1,160 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const frameSource =
+ "<a href='about:mozilla'>some text</a><a id='other' href='about:about'>other text</a>";
+const sources = [
+ `<html><iframe id="f" srcdoc="${frameSource}"></iframe></html>`,
+ `<html><iframe id="f" src="https://example.com/document-builder.sjs?html=${frameSource}"></iframe></html>`,
+];
+
+async function getPreviewText(previewBrowser) {
+ return SpecialPowers.spawn(previewBrowser, [], function() {
+ return content.document.body.textContent;
+ });
+}
+
+add_task(async function print_selection() {
+ let i = 0;
+ for (let source of sources) {
+ // Testing the native print dialog is much harder.
+ // Note we need to do this from here since resetPrintPrefs() below clears
+ // out the pref.
+ await SpecialPowers.pushPrefEnv({
+ set: [["print.tab_modal.enabled", true]],
+ });
+
+ is(
+ document.querySelector(".printPreviewBrowser"),
+ null,
+ "There shouldn't be any print preview browser"
+ );
+
+ await BrowserTestUtils.withNewTab(
+ "data:text/html," + source,
+ async function(browser) {
+ let frameBC = browser.browsingContext.children[0];
+ await SpecialPowers.spawn(frameBC, [], () => {
+ let element = content.document.getElementById("other");
+ content.focus();
+ content.getSelection().selectAllChildren(element);
+ });
+
+ let helper = new PrintHelper(browser);
+
+ // If you change this, change nsContextMenu.printSelection() too.
+ PrintUtils.startPrintWindow("tests", frameBC, {
+ printSelectionOnly: true,
+ });
+
+ await BrowserTestUtils.waitForCondition(
+ () => !!document.querySelector(".printPreviewBrowser")
+ );
+
+ let previewBrowser = document.querySelector(
+ ".printPreviewBrowser[previewtype='selection']"
+ );
+ let previewText = () => getPreviewText(previewBrowser);
+ // The preview process is async, wait for it to not be empty.
+ let textContent = await TestUtils.waitForCondition(previewText);
+ is(textContent, "other text", "Correct content loaded");
+
+ let printSelect = document
+ .querySelector(".printSettingsBrowser")
+ .contentDocument.querySelector("#print-selection-enabled");
+ ok(!printSelect.hidden, "Print selection checkbox is shown");
+ ok(printSelect.checked, "Print selection checkbox is checked");
+
+ let file = helper.mockFilePicker(`browser_print_selection-${i++}.pdf`);
+ await helper.assertPrintToFile(file, () => {
+ helper.click(helper.get("print-button"));
+ });
+ PrintHelper.resetPrintPrefs();
+ }
+ );
+ }
+});
+
+add_task(async function no_print_selection() {
+ // Ensures the print selection checkbox is hidden if nothing is selected
+ await PrintHelper.withTestPage(async helper => {
+ await helper.startPrint();
+ await helper.openMoreSettings();
+
+ let printSelect = helper.get("print-selection-container");
+ ok(printSelect.hidden, "Print selection checkbox is hidden");
+ await helper.closeDialog();
+ });
+});
+
+add_task(async function print_selection_switch() {
+ await PrintHelper.withTestPage(async helper => {
+ await SpecialPowers.spawn(helper.sourceBrowser, [], async function() {
+ let element = content.document.querySelector("h1");
+ content.window.getSelection().selectAllChildren(element);
+ });
+
+ await helper.startPrint();
+ await helper.openMoreSettings();
+ let printSelect = helper.get("print-selection-container");
+ ok(!printSelect.checked, "Print selection checkbox is not checked");
+
+ let selectionBrowser = document.querySelector(
+ ".printPreviewBrowser[previewtype='selection']"
+ );
+ let primaryBrowser = document.querySelector(
+ ".printPreviewBrowser[previewtype='primary']"
+ );
+
+ let selectedText = "Article title";
+ let fullText = await getPreviewText(primaryBrowser);
+
+ function getCurrentBrowser(previewType) {
+ let browser =
+ previewType == "selection" ? selectionBrowser : primaryBrowser;
+ is(
+ browser.parentElement.getAttribute("previewtype"),
+ previewType,
+ "Expected browser is showing"
+ );
+ return browser;
+ }
+
+ helper.assertSettingsMatch({
+ printSelectionOnly: false,
+ });
+
+ is(
+ selectionBrowser.parentElement.getAttribute("previewtype"),
+ "primary",
+ "Print selection browser is not shown"
+ );
+
+ await helper.assertSettingsChanged(
+ { printSelectionOnly: false },
+ { printSelectionOnly: true },
+ async () => {
+ await helper.waitForPreview(() => helper.click(printSelect));
+ let text = await getPreviewText(getCurrentBrowser("selection"));
+ is(text, selectedText, "Correct content loaded");
+ }
+ );
+
+ await helper.assertSettingsChanged(
+ { printSelectionOnly: true },
+ { printSelectionOnly: false },
+ async () => {
+ await helper.waitForPreview(() => helper.click(printSelect));
+ let previewType = selectionBrowser.parentElement.getAttribute(
+ "previewtype"
+ );
+ is(previewType, "primary", "Print selection browser is not shown");
+ let text = await getPreviewText(getCurrentBrowser(previewType));
+ is(text, fullText, "Correct content loaded");
+ }
+ );
+
+ await helper.closeDialog();
+ });
+});
diff --git a/toolkit/components/printing/tests/browser_sheet_count.js b/toolkit/components/printing/tests/browser_sheet_count.js
new file mode 100644
index 0000000000..f53ba5ba80
--- /dev/null
+++ b/toolkit/components/printing/tests/browser_sheet_count.js
@@ -0,0 +1,228 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+function getSheetCount(el) {
+ return el.ownerDocument.l10n.getAttributes(el).args.sheetCount;
+}
+
+add_task(async function testSheetCount() {
+ await PrintHelper.withTestPage(async helper => {
+ await helper.startPrint();
+
+ let sheetCount = helper.get("sheet-count");
+ let { id } = helper.doc.l10n.getAttributes(sheetCount);
+ is(id, "printui-sheets-count", "The l10n id is correct");
+ let initialSheetCount = getSheetCount(sheetCount);
+ ok(initialSheetCount >= 1, "There is an initial sheet count");
+
+ await helper.openMoreSettings();
+
+ let scaleRadio = helper.get("percent-scale-choice");
+ await helper.waitForPreview(() => helper.click(scaleRadio));
+
+ let percentScale = helper.get("percent-scale");
+ await helper.waitForPreview(() => helper.text(percentScale, "200"));
+
+ let zoomedSheetCount = getSheetCount(sheetCount);
+ ok(zoomedSheetCount > initialSheetCount, "The sheet count increased");
+
+ // Since we're using the Save to PDF printer, the numCopies element should
+ // be hidden and its value ignored.
+ let numCopies = helper.get("copies-count");
+ ok(BrowserTestUtils.is_hidden(numCopies), "numCopies element is hidden");
+ helper.dispatchSettingsChange({
+ numCopies: 4,
+ });
+ is(
+ getSheetCount(sheetCount),
+ zoomedSheetCount,
+ "numCopies is ignored for Save to PDF printer"
+ );
+
+ is(helper.viewSettings.numCopies, 1, "numCopies is 1 in viewSettings");
+
+ // We don't have any "real" printers set up for testing yet, so insert a modified
+ // copy of the PDF printer which pretends to be real, and switch to that
+ // to triggers the component to update.
+ let realPrinterName = "My real printer";
+ let pdfPrinterInfo =
+ helper.win.PrintSettingsViewProxy.availablePrinters[
+ PrintUtils.SAVE_TO_PDF_PRINTER
+ ];
+ let mockPrinterInfo = Object.assign({}, pdfPrinterInfo, {});
+ mockPrinterInfo.settings = pdfPrinterInfo.settings.clone();
+ mockPrinterInfo.settings.outputFormat =
+ Ci.nsIPrintSettings.kOutputFormatNative;
+ mockPrinterInfo.settings.printerName = realPrinterName;
+
+ helper.win.PrintSettingsViewProxy.availablePrinters[
+ realPrinterName
+ ] = mockPrinterInfo;
+ await helper.dispatchSettingsChange({
+ printerName: realPrinterName,
+ });
+ await helper.awaitAnimationFrame();
+
+ let { settings, viewSettings } = helper;
+
+ is(
+ settings.printerName,
+ realPrinterName,
+ "Sanity check the current settings have the new printerName"
+ );
+ is(
+ settings.outputFormat,
+ Ci.nsIPrintSettings.kOutputFormatNative,
+ "The new printer has the correct outputFormat"
+ );
+ is(viewSettings.numCopies, 4, "numCopies is 4 in viewSettings");
+
+ // numCopies is now visible and sheetCount is multiplied by numCopies.
+ ok(BrowserTestUtils.is_visible(numCopies), "numCopies element is visible");
+ is(numCopies.value, "4", "numCopies displays the correct value");
+ is(
+ getSheetCount(sheetCount),
+ zoomedSheetCount * 4,
+ "numCopies is used when using a non-PDF printer"
+ );
+
+ await helper.closeDialog();
+ });
+});
+
+add_task(async function testSheetCountPageRange() {
+ await PrintHelper.withTestPage(async helper => {
+ await helper.startPrint();
+ await helper.waitForPreview(() =>
+ helper.dispatchSettingsChange({
+ shrinkToFit: false,
+ scaling: 2,
+ })
+ );
+
+ let sheetCount = helper.get("sheet-count");
+ await BrowserTestUtils.waitForCondition(
+ () => getSheetCount(sheetCount) != 1,
+ "Wait for sheet count to update"
+ );
+ let sheets = getSheetCount(sheetCount);
+ ok(sheets >= 3, "There are at least 3 pages");
+
+ // Set page range to 2-3, sheet count should be 2.
+ await helper.waitForPreview(() =>
+ helper.dispatchSettingsChange({
+ pageRanges: [2, 3],
+ })
+ );
+
+ sheets = getSheetCount(sheetCount);
+ is(sheets, 2, "There are now only 2 pages shown");
+ });
+});
+
+add_task(async function testPagesPerSheetCount() {
+ await PrintHelper.withTestPage(async helper => {
+ let mockPrinterName = "A real printer!";
+ helper.addMockPrinter(mockPrinterName);
+
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["print.pages_per_sheet.enabled", true],
+ ["print_printer", mockPrinterName],
+ ],
+ });
+
+ await helper.startPrint();
+
+ await helper.waitForPreview(() =>
+ helper.dispatchSettingsChange({
+ shrinkToFit: false,
+ scaling: 2,
+ })
+ );
+
+ let sheetCount = helper.get("sheet-count");
+ await BrowserTestUtils.waitForCondition(
+ () => getSheetCount(sheetCount) != 1,
+ "Wait for sheet count to update"
+ );
+ let sheets = getSheetCount(sheetCount);
+
+ ok(sheets > 1, "There are multiple pages");
+
+ await helper.openMoreSettings();
+ let pagesPerSheet = helper.get("pages-per-sheet-picker");
+ ok(BrowserTestUtils.is_visible(pagesPerSheet), "Pages per sheet is shown");
+ pagesPerSheet.focus();
+ EventUtils.sendKey("space", helper.win);
+ for (let i = 0; i < 7; i++) {
+ EventUtils.sendKey("down", helper.win);
+ if (pagesPerSheet.value == 16) {
+ break;
+ }
+ }
+ await helper.waitForPreview(() => EventUtils.sendKey("return", helper.win));
+
+ sheets = getSheetCount(sheetCount);
+ is(sheets, 1, "There's only one sheet now");
+
+ await helper.waitForSettingsEvent(() =>
+ helper.dispatchSettingsChange({ numCopies: 5 })
+ );
+
+ sheets = getSheetCount(sheetCount);
+ is(sheets, 5, "Copies are handled with pages per sheet correctly");
+
+ await helper.closeDialog();
+ });
+});
+
+add_task(async function testPagesPerSheetPref() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["print.pages_per_sheet.enabled", false]],
+ });
+
+ await PrintHelper.withTestPage(async helper => {
+ await helper.startPrint();
+
+ ok(
+ BrowserTestUtils.is_hidden(helper.get("pages-per-sheet")),
+ "Pages per sheet is hidden"
+ );
+
+ await helper.closeDialog();
+ });
+});
+
+add_task(async function testUpdateCopiesNoPreviewUpdate() {
+ const mockPrinterName = "Fake Printer";
+ await PrintHelper.withTestPage(async helper => {
+ helper.addMockPrinter(mockPrinterName);
+ await helper.startPrint();
+
+ await helper.waitForSettingsEvent(() =>
+ helper.dispatchSettingsChange({ numCopies: 5 })
+ );
+
+ ok(
+ !helper.win.PrintEventHandler._updatePrintPreviewTask.isArmed,
+ "Preview Task is not armed"
+ );
+
+ await helper.waitForPreview(() =>
+ helper.dispatchSettingsChange({ printerName: mockPrinterName })
+ );
+
+ await helper.waitForSettingsEvent(() =>
+ helper.dispatchSettingsChange({ numCopies: 2 })
+ );
+ ok(
+ !helper.win.PrintEventHandler._updatePrintPreviewTask.isArmed,
+ "Preview Task is not armed"
+ );
+
+ await helper.closeDialog();
+ });
+});
diff --git a/toolkit/components/printing/tests/browser_system_dialog_subdialog_hidden.js b/toolkit/components/printing/tests/browser_system_dialog_subdialog_hidden.js
new file mode 100644
index 0000000000..d5b8b5cfe6
--- /dev/null
+++ b/toolkit/components/printing/tests/browser_system_dialog_subdialog_hidden.js
@@ -0,0 +1,105 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+add_task(async function testSystemDialogLinkState() {
+ await PrintHelper.withTestPage(async helper => {
+ await helper.startPrint();
+
+ is(
+ helper.get("printer-picker").options.length,
+ 1,
+ "Only the Save to PDF printer is available"
+ );
+
+ let systemLink = helper.get("open-dialog-link");
+ if (AppConstants.platform == "win") {
+ ok(
+ BrowserTestUtils.is_hidden(systemLink),
+ "Link is hidden on Windows with no extra printers"
+ );
+ } else {
+ ok(
+ BrowserTestUtils.is_visible(systemLink),
+ "Link is visible on Linux/macOS"
+ );
+ }
+ });
+});
+
+add_task(async function testModalPrintDialog() {
+ await PrintHelper.withTestPage(async helper => {
+ helper.addMockPrinter("A printer");
+ await SpecialPowers.pushPrefEnv({
+ set: [["print_printer", "A printer"]],
+ });
+
+ await helper.startPrint();
+
+ helper.assertDialogOpen();
+
+ helper.assertSettingsMatch({ printerName: "A printer" });
+ await helper.setupMockPrint();
+
+ helper.click(helper.get("open-dialog-link"));
+
+ helper.assertDialogHidden();
+
+ await helper.withClosingFn(() => {
+ helper.resolveShowSystemDialog();
+ helper.resolvePrint();
+ });
+
+ helper.assertDialogClosed();
+ });
+});
+
+add_task(async function testModalPrintDialogCancelled() {
+ await PrintHelper.withTestPage(async helper => {
+ helper.addMockPrinter("A printer");
+ await SpecialPowers.pushPrefEnv({
+ set: [["print_printer", "A printer"]],
+ });
+
+ await helper.startPrint();
+
+ helper.assertDialogOpen();
+
+ helper.assertSettingsMatch({ printerName: "A printer" });
+ await helper.setupMockPrint();
+
+ helper.click(helper.get("open-dialog-link"));
+
+ helper.assertDialogHidden();
+
+ await helper.withClosingFn(() => {
+ helper.rejectShowSystemDialog();
+ });
+
+ helper.assertDialogClosed();
+ });
+});
+
+add_task(async function testPrintDoesNotWaitForPreview() {
+ await PrintHelper.withTestPage(async helper => {
+ helper.addMockPrinter("A printer");
+ await SpecialPowers.pushPrefEnv({
+ set: [["print_printer", "A printer"]],
+ });
+
+ await helper.startPrint({ waitFor: "loadComplete" });
+ await helper.awaitAnimationFrame();
+
+ helper.mockFilePicker("print_does_not_wait_for_preview.pdf");
+ await helper.setupMockPrint();
+ helper.click(helper.get("open-dialog-link"));
+
+ helper.assertDialogHidden();
+ await helper.withClosingFn(() => {
+ helper.resolveShowSystemDialog();
+ helper.resolvePrint();
+ });
+
+ helper.assertDialogClosed();
+ });
+});
diff --git a/toolkit/components/printing/tests/browser_ui_labels.js b/toolkit/components/printing/tests/browser_ui_labels.js
new file mode 100644
index 0000000000..a107bac4d1
--- /dev/null
+++ b/toolkit/components/printing/tests/browser_ui_labels.js
@@ -0,0 +1,23 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function test_FormFieldLabels() {
+ await PrintHelper.withTestPage(async helper => {
+ await helper.startPrint();
+
+ let fields = Array.from(helper.get("print").elements);
+ for (let field of fields) {
+ if (field.localName == "button") {
+ continue;
+ }
+ ok(
+ field.labels.length ||
+ field.hasAttribute("aria-label") ||
+ field.hasAttribute("aria-labelledby"),
+ `Field ${field.localName}#${field.id} should be labelled`
+ );
+ }
+ });
+});
diff --git a/toolkit/components/printing/tests/browser_window_print.js b/toolkit/components/printing/tests/browser_window_print.js
new file mode 100644
index 0000000000..49a4485ef1
--- /dev/null
+++ b/toolkit/components/printing/tests/browser_window_print.js
@@ -0,0 +1,189 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_PATH = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+);
+
+add_task(async function test_print_blocks() {
+ // window.print() only shows print preview when print.tab_modal.enabled is
+ // true.
+ await SpecialPowers.pushPrefEnv({
+ set: [["print.tab_modal.enabled", true]],
+ });
+
+ is(
+ document.querySelector(".printPreviewBrowser"),
+ null,
+ "There shouldn't be any print preview browser"
+ );
+
+ await BrowserTestUtils.withNewTab(
+ `${TEST_PATH}file_window_print.html`,
+ async function(browser) {
+ info(
+ "Waiting for the first window.print() to run and ensure we're showing the preview..."
+ );
+
+ await BrowserTestUtils.waitForCondition(
+ () => !!document.querySelector(".printPreviewBrowser")
+ );
+
+ {
+ let [before, afterFirst] = await SpecialPowers.spawn(
+ browser,
+ [],
+ () => {
+ return [
+ !!content.document.getElementById("before-print"),
+ !!content.document.getElementById("after-first-print"),
+ ];
+ }
+ );
+
+ ok(before, "Content before printing should be in the DOM");
+ ok(!afterFirst, "Shouldn't have returned yet from window.print()");
+ }
+
+ gBrowser.getTabDialogBox(browser).abortAllDialogs();
+
+ await BrowserTestUtils.waitForCondition(
+ () => !!document.querySelector(".printPreviewBrowser")
+ );
+
+ {
+ let [before, afterFirst, afterSecond] = await SpecialPowers.spawn(
+ browser,
+ [],
+ () => {
+ return [
+ !!content.document.getElementById("before-print"),
+ !!content.document.getElementById("after-first-print"),
+ !!content.document.getElementById("after-second-print"),
+ ];
+ }
+ );
+
+ ok(before, "Content before printing should be in the DOM");
+ ok(afterFirst, "Should be in the second print already");
+ ok(afterSecond, "Shouldn't have blocked if we have mozPrintCallbacks");
+ }
+ }
+ );
+});
+
+add_task(async function test_print_delayed_during_load() {
+ // window.print() only shows print preview when print.tab_modal.enabled is
+ // true.
+ await SpecialPowers.pushPrefEnv({
+ set: [["print.tab_modal.enabled", true]],
+ });
+
+ is(
+ document.querySelector(".printPreviewBrowser"),
+ null,
+ "There shouldn't be any print preview browser"
+ );
+
+ await BrowserTestUtils.withNewTab(
+ `${TEST_PATH}file_window_print_delayed_during_load.html`,
+ async function(browser) {
+ info(
+ "Waiting for the first window.print() to run and ensure we're showing the preview..."
+ );
+
+ await BrowserTestUtils.waitForCondition(
+ () => !!document.querySelector(".printPreviewBrowser")
+ );
+
+ // The print dialog is open, should be open after onload.
+ {
+ let duringLoad = await SpecialPowers.spawn(browser, [], () => {
+ return !!content.document.getElementById("added-during-load");
+ });
+ ok(duringLoad, "Print should've been delayed");
+ }
+
+ gBrowser.getTabDialogBox(browser).abortAllDialogs();
+
+ is(typeof browser.isConnected, "boolean");
+ await BrowserTestUtils.waitForCondition(() => !browser.isConnected);
+ ok(true, "Tab should've been closed after printing");
+ }
+ );
+});
+
+add_task(async function test_print_on_sandboxed_frame() {
+ // window.print() only shows print preview when print.tab_modal.enabled is
+ // true.
+ await SpecialPowers.pushPrefEnv({
+ set: [["print.tab_modal.enabled", true]],
+ });
+
+ is(
+ document.querySelector(".printPreviewBrowser"),
+ null,
+ "There shouldn't be any print preview browser"
+ );
+
+ await BrowserTestUtils.withNewTab(
+ `${TEST_PATH}file_window_print_sandboxed_iframe.html`,
+ async function(browser) {
+ info(
+ "Waiting for the first window.print() to run and ensure we're showing the preview..."
+ );
+
+ await BrowserTestUtils.waitForCondition(
+ () => !!document.querySelector(".printPreviewBrowser")
+ );
+
+ isnot(
+ document.querySelector(".printPreviewBrowser"),
+ null,
+ "Should open the print preview correctly"
+ );
+ gBrowser.getTabDialogBox(browser).abortAllDialogs();
+ }
+ );
+});
+
+add_task(async function test_focused_browsing_context() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["print.tab_modal.enabled", true]],
+ });
+
+ await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ `${TEST_PATH}longerArticle.html`
+ );
+
+ let tabCount = gBrowser.tabs.length;
+ document.getElementById("cmd_newNavigatorTab").doCommand();
+ await TestUtils.waitForCondition(() => gBrowser.tabs.length == tabCount + 1);
+ let newTabBrowser = gBrowser.selectedBrowser;
+ is(newTabBrowser.documentURI.spec, "about:newtab", "newtab is loaded");
+
+ let menuButton = document.getElementById("PanelUI-menu-button");
+ menuButton.click();
+ await BrowserTestUtils.waitForEvent(window.PanelUI.mainView, "ViewShown");
+ document.getElementById("appMenu-print-button").click();
+
+ let dialog = await TestUtils.waitForCondition(
+ () =>
+ gBrowser
+ .getTabDialogBox(newTabBrowser)
+ .getTabDialogManager()
+ ._dialogs.find(dlg => dlg._box.querySelector(".printSettingsBrowser")),
+ "Wait for dialog"
+ );
+ await dialog._dialogReady;
+ ok(dialog, "Dialog is available");
+ await dialog._frame.contentWindow._initialized;
+ await dialog.close();
+
+ BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ BrowserTestUtils.removeTab(gBrowser.selectedTab);
+});
diff --git a/toolkit/components/printing/tests/file_coop_header.html b/toolkit/components/printing/tests/file_coop_header.html
new file mode 100644
index 0000000000..22eb518ba0
--- /dev/null
+++ b/toolkit/components/printing/tests/file_coop_header.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+ <body>
+ <p>Hello world</p>
+ </body>
+</html>
diff --git a/toolkit/components/printing/tests/file_coop_header.html^headers^ b/toolkit/components/printing/tests/file_coop_header.html^headers^
new file mode 100644
index 0000000000..46ad58d83b
--- /dev/null
+++ b/toolkit/components/printing/tests/file_coop_header.html^headers^
@@ -0,0 +1 @@
+Cross-Origin-Opener-Policy: same-origin
diff --git a/toolkit/components/printing/tests/file_page_change_print_original_1.html b/toolkit/components/printing/tests/file_page_change_print_original_1.html
new file mode 100644
index 0000000000..c567b746c9
--- /dev/null
+++ b/toolkit/components/printing/tests/file_page_change_print_original_1.html
@@ -0,0 +1,8 @@
+<script>
+window.onafterprint = function() {
+ setTimeout(function() {
+ window.location = "file_page_change_print_original_2.html";
+ }, 0);
+};
+</script>
+<pre>INITIAL PAGE</pre>
diff --git a/toolkit/components/printing/tests/file_page_change_print_original_2.html b/toolkit/components/printing/tests/file_page_change_print_original_2.html
new file mode 100644
index 0000000000..44f33281c9
--- /dev/null
+++ b/toolkit/components/printing/tests/file_page_change_print_original_2.html
@@ -0,0 +1 @@
+REPLACED PAGE!
diff --git a/toolkit/components/printing/tests/file_pdf.pdf b/toolkit/components/printing/tests/file_pdf.pdf
new file mode 100644
index 0000000000..593558f9a4
--- /dev/null
+++ b/toolkit/components/printing/tests/file_pdf.pdf
@@ -0,0 +1,12 @@
+%PDF-1.0
+1 0 obj<</Type/Catalog/Pages 2 0 R>>endobj 2 0 obj<</Type/Pages/Kids[3 0 R]/Count 1>>endobj 3 0 obj<</Type/Page/MediaBox[0 0 3 3]>>endobj
+xref
+0 4
+0000000000 65535 f
+0000000010 00000 n
+0000000053 00000 n
+0000000102 00000 n
+trailer<</Size 4/Root 1 0 R>>
+startxref
+149
+%EOF \ No newline at end of file
diff --git a/toolkit/components/printing/tests/file_print.html b/toolkit/components/printing/tests/file_print.html
new file mode 100644
index 0000000000..be7de08dbd
--- /dev/null
+++ b/toolkit/components/printing/tests/file_print.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<script>
+ window.print();
+</script>
+<p id="printed">I should be printed</p>
diff --git a/toolkit/components/printing/tests/file_window_print.html b/toolkit/components/printing/tests/file_window_print.html
new file mode 100644
index 0000000000..6b18a04cca
--- /dev/null
+++ b/toolkit/components/printing/tests/file_window_print.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<div id="before-print">Before print</div>
+<canvas id="canvas" width="100" height="100"></canvas>
+<script>
+ onload = function() {
+ // window.print() is special until after the load event is finished firing.
+ setTimeout(function() {
+ // This fires a timer which would trigger a navigation and prevent the
+ // test from completing if it happens during window.print().
+ let meta = document.createElement("meta");
+ meta.setAttribute("http-equiv", "refresh");
+ meta.setAttribute("content", "0; url=/unlikely-to-be-found");
+ document.head.appendChild(meta);
+ // This one should block until we're done printing, and block the
+ // navigation too.
+ window.print();
+ meta.remove();
+ document.body.insertAdjacentHTML('beforeend', `<div id="after-first-print">After first print</div>`);
+
+ let canvas = document.getElementById("canvas");
+ canvas.mozPrintCallback = function() {};
+
+ // This one shouldn't, because the print callbacks need to run.
+ window.print();
+
+ document.body.insertAdjacentHTML('beforeend', `<div id="after-second-print">After second print</div>`);
+ }, 0);
+ }
+</script>
diff --git a/toolkit/components/printing/tests/file_window_print_delayed_during_load.html b/toolkit/components/printing/tests/file_window_print_delayed_during_load.html
new file mode 100644
index 0000000000..4ac5ed9a8a
--- /dev/null
+++ b/toolkit/components/printing/tests/file_window_print_delayed_during_load.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<body>
+<script>
+ onload = function() {
+ let div = document.createElement("div");
+ div.id = "added-during-load";
+ div.innerHTML = "I should be printed";
+ document.body.appendChild(div);
+ };
+
+ window.print(); // This should be delayed until after load.
+ window.close(); // So should this.
+</script>
diff --git a/toolkit/components/printing/tests/file_window_print_sandboxed_iframe.html b/toolkit/components/printing/tests/file_window_print_sandboxed_iframe.html
new file mode 100644
index 0000000000..a8e832932c
--- /dev/null
+++ b/toolkit/components/printing/tests/file_window_print_sandboxed_iframe.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<iframe sandbox="allow-same-origin allow-scripts allow-modals" src="about:blank" width="0" height="0"></iframe>
+<script>
+ onload = function() {
+ document.querySelector("iframe").contentWindow.print();
+ };
+</script>
diff --git a/toolkit/components/printing/tests/head.js b/toolkit/components/printing/tests/head.js
new file mode 100644
index 0000000000..2629e7d780
--- /dev/null
+++ b/toolkit/components/printing/tests/head.js
@@ -0,0 +1,444 @@
+const PRINT_DOCUMENT_URI = "chrome://global/content/print.html";
+const { MockFilePicker } = SpecialPowers;
+
+let pickerMocked = false;
+
+class PrintHelper {
+ static async withTestPage(testFn, pagePathname) {
+ await SpecialPowers.pushPrefEnv({
+ set: [["print.tab_modal.enabled", true]],
+ });
+
+ let pageUrl = pagePathname
+ ? this.getTestPageUrl(pagePathname)
+ : this.defaultTestPageUrl;
+ info("withTestPage: " + pageUrl);
+ let isPdf = pageUrl.endsWith(".pdf");
+
+ if (isPdf) {
+ await SpecialPowers.pushPrefEnv({
+ set: [["pdfjs.eventBusDispatchToDOM", true]],
+ });
+ }
+
+ let taskReturn = await BrowserTestUtils.withNewTab(
+ isPdf ? "about:blank" : pageUrl,
+ async function(browser) {
+ if (isPdf) {
+ let loaded = BrowserTestUtils.waitForContentEvent(
+ browser,
+ "documentloaded",
+ false,
+ null,
+ true
+ );
+ await SpecialPowers.spawn(browser, [pageUrl], contentUrl => {
+ content.location = contentUrl;
+ });
+ await loaded;
+ }
+ await testFn(new PrintHelper(browser));
+ }
+ );
+
+ await SpecialPowers.popPrefEnv();
+ if (isPdf) {
+ await SpecialPowers.popPrefEnv();
+ }
+
+ // Reset all of the other printing prefs to their default.
+ this.resetPrintPrefs();
+ return taskReturn;
+ }
+
+ static resetPrintPrefs() {
+ for (let name of Services.prefs.getChildList("print.")) {
+ Services.prefs.clearUserPref(name);
+ }
+ Services.prefs.clearUserPref("print_printer");
+ }
+
+ static getTestPageUrl(pathName) {
+ const testPath = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "http://example.com"
+ );
+ return testPath + pathName;
+ }
+
+ static get defaultTestPageUrl() {
+ return this.getTestPageUrl("simplifyArticleSample.html");
+ }
+
+ static createMockPaper(paperProperties = {}) {
+ return Object.assign(
+ {
+ id: "regular",
+ name: "Regular Size",
+ width: 612,
+ height: 792,
+ unwriteableMargin: {
+ marginTop: 0.1,
+ marginBottom: 0.1,
+ marginLeft: 0.1,
+ marginRight: 0.1,
+ QueryInterface: ChromeUtils.generateQI([Ci.nsIPaperMargin]),
+ },
+ QueryInterface: ChromeUtils.generateQI([Ci.nsIPaper]),
+ },
+ paperProperties
+ );
+ }
+
+ // This is used only for the old print preview. For tests
+ // involving the newer UI, use waitForPreview instead.
+ static waitForOldPrintPreview(expectedBrowser) {
+ const { PrintingParent } = ChromeUtils.import(
+ "resource://gre/actors/PrintingParent.jsm"
+ );
+
+ return new Promise(resolve => {
+ PrintingParent.setTestListener(browser => {
+ if (browser == expectedBrowser) {
+ PrintingParent.setTestListener(null);
+ resolve();
+ }
+ });
+ });
+ }
+
+ constructor(sourceBrowser) {
+ this.sourceBrowser = sourceBrowser;
+ }
+
+ async startPrint(condition = {}) {
+ this.sourceBrowser.ownerGlobal.document
+ .getElementById("cmd_print")
+ .doCommand();
+ let dialog = await TestUtils.waitForCondition(
+ () => this.dialog,
+ "Wait for dialog"
+ );
+ await dialog._dialogReady;
+
+ if (Object.keys(condition).length === 0) {
+ await this.win._initialized;
+ } else if (condition.waitFor == "loadComplete") {
+ await BrowserTestUtils.waitForAttributeRemoval("loading", document.body);
+ }
+ }
+
+ beforeInit(initFn) {
+ // Run a function when the print.html document is created,
+ // but before its init is called from the domcontentloaded handler
+ TestUtils.topicObserved("document-element-inserted", doc => {
+ return (
+ doc.nodePrincipal.isSystemPrincipal &&
+ doc.contentType == "text/html" &&
+ doc.URL.startsWith("chrome://global/content/print.html")
+ );
+ }).then(([doc]) => {
+ doc.addEventListener("DOMContentLoaded", () => {
+ initFn(doc.ownerGlobal);
+ });
+ });
+ }
+
+ async withClosingFn(closeFn) {
+ let { dialog } = this;
+ await closeFn();
+ if (this.dialog) {
+ await TestUtils.waitForCondition(
+ () => !this.dialog,
+ "Wait for dialog to close"
+ );
+ }
+ await dialog._closingPromise;
+ }
+
+ resetSettings() {
+ this.win.PrintEventHandler.settings = this.win.PrintEventHandler.defaultSettings;
+ this.win.PrintEventHandler.saveSettingsToPrefs(
+ this.win.PrintEventHandler.kInitSaveAll
+ );
+ }
+
+ async closeDialog() {
+ this.resetSettings();
+ await this.withClosingFn(() => this.dialog.close());
+ }
+
+ assertDialogClosed() {
+ is(this._dialogs.length, 0, "There are no print dialogs");
+ }
+
+ assertDialogOpen() {
+ is(this._dialogs.length, 1, "There is one print dialog");
+ ok(BrowserTestUtils.is_visible(this.dialog._box), "The dialog is visible");
+ }
+
+ assertDialogHidden() {
+ is(this._dialogs.length, 1, "There is one print dialog");
+ ok(BrowserTestUtils.is_hidden(this.dialog._box), "The dialog is hidden");
+ ok(
+ this.dialog._box.getBoundingClientRect().width > 0,
+ "The dialog should still have boxes"
+ );
+ }
+
+ async assertPrintToFile(file, testFn) {
+ ok(!file.exists(), "File does not exist before printing");
+ await this.withClosingFn(testFn);
+ await TestUtils.waitForCondition(
+ () => file.exists() && file.fileSize > 0,
+ "Wait for target file to get created",
+ 50
+ );
+ ok(file.exists(), "Created target file");
+
+ await TestUtils.waitForCondition(
+ () => file.fileSize > 0,
+ "Wait for the print progress to run",
+ 50
+ );
+
+ ok(file.fileSize > 0, "Target file not empty");
+ }
+
+ setupMockPrint() {
+ if (this.resolveShowSystemDialog) {
+ throw new Error("Print already mocked");
+ }
+
+ // Create some Promises that we can resolve/reject from the test.
+ let showSystemDialogPromise = new Promise((resolve, reject) => {
+ this.resolveShowSystemDialog = resolve;
+ this.rejectShowSystemDialog = () => {
+ reject(Components.Exception("", Cr.NS_ERROR_ABORT));
+ };
+ });
+ let printPromise = new Promise((resolve, reject) => {
+ this.resolvePrint = resolve;
+ this.rejectPrint = reject;
+ });
+
+ // Mock PrintEventHandler with our Promises.
+ this.win.PrintEventHandler._showPrintDialog = () => showSystemDialogPromise;
+ this.win.PrintEventHandler._doPrint = (bc, settings) => {
+ this._printedSettings = settings;
+ return printPromise;
+ };
+ }
+
+ addMockPrinter(opts = {}) {
+ if (typeof opts == "string") {
+ opts = { name: opts };
+ }
+ let {
+ name = "Mock Printer",
+ paperList = [],
+ printerInfoPromise = Promise.resolve(),
+ } = opts;
+ let PSSVC = Cc["@mozilla.org/gfx/printsettings-service;1"].getService(
+ Ci.nsIPrintSettingsService
+ );
+
+ let defaultSettings = PSSVC.newPrintSettings;
+ defaultSettings.printerName = name;
+ defaultSettings.toFileName = "";
+ defaultSettings.outputFormat = Ci.nsIPrintSettings.kOutputFormatNative;
+ defaultSettings.printToFile = false;
+
+ let printer = {
+ name,
+ supportsColor: Promise.resolve(true),
+ supportsMonochrome: Promise.resolve(true),
+ printerInfo: printerInfoPromise.then(() => ({
+ paperList,
+ defaultSettings,
+ QueryInterface: ChromeUtils.generateQI([Ci.nsIPrinterInfo]),
+ })),
+ QueryInterface: ChromeUtils.generateQI([Ci.nsIPrinter]),
+ };
+
+ if (!this._mockPrinters) {
+ this._mockPrinters = [printer];
+ this.beforeInit(win => (win._mockPrinters = this._mockPrinters));
+ } else {
+ this._mockPrinters.push(printer);
+ }
+ return printer;
+ }
+
+ get _tabDialogBox() {
+ return this.sourceBrowser.ownerGlobal.gBrowser.getTabDialogBox(
+ this.sourceBrowser
+ );
+ }
+
+ get _tabDialogBoxManager() {
+ return this._tabDialogBox.getTabDialogManager();
+ }
+
+ get _dialogs() {
+ return this._tabDialogBox.getTabDialogManager()._dialogs;
+ }
+
+ get dialog() {
+ return this._dialogs.find(dlg =>
+ dlg._box.querySelector(".printSettingsBrowser")
+ );
+ }
+
+ get _printBrowser() {
+ return this.dialog._frame;
+ }
+
+ get doc() {
+ return this._printBrowser.contentDocument;
+ }
+
+ get win() {
+ return this._printBrowser.contentWindow;
+ }
+
+ get(id) {
+ return this.doc.getElementById(id);
+ }
+
+ get sourceURI() {
+ return this.win.PrintEventHandler.originalSourceCurrentURI;
+ }
+
+ async waitForPreview(changeFn) {
+ changeFn();
+ await BrowserTestUtils.waitForEvent(this.doc, "preview-updated");
+ }
+
+ async waitForSettingsEvent(changeFn) {
+ let changed = BrowserTestUtils.waitForEvent(this.doc, "print-settings");
+ await changeFn?.();
+ await BrowserTestUtils.waitForCondition(
+ () => !this.win.PrintEventHandler._delayedSettingsChangeTask.isArmed,
+ "Wait for all delayed tasks to execute"
+ );
+ await changed;
+ }
+
+ click(el, { scroll = true } = {}) {
+ if (scroll) {
+ el.scrollIntoView();
+ }
+ ok(BrowserTestUtils.is_visible(el), "Element must be visible to click");
+ EventUtils.synthesizeMouseAtCenter(el, {}, this.win);
+ }
+
+ text(el, text) {
+ this.click(el);
+ el.value = "";
+ EventUtils.sendString(text, this.win);
+ }
+
+ async openMoreSettings(options) {
+ let details = this.get("more-settings");
+ if (!details.open) {
+ this.click(details.firstElementChild, options);
+ }
+ await this.awaitAnimationFrame();
+ }
+
+ dispatchSettingsChange(settings) {
+ this.doc.dispatchEvent(
+ new CustomEvent("update-print-settings", {
+ detail: settings,
+ })
+ );
+ }
+
+ get settings() {
+ return this.win.PrintEventHandler.settings;
+ }
+
+ get viewSettings() {
+ return this.win.PrintEventHandler.viewSettings;
+ }
+
+ _assertMatches(a, b, msg) {
+ if (Array.isArray(a)) {
+ is(a.length, b.length, msg);
+ for (let i = 0; i < a.length; ++i) {
+ this._assertMatches(a[i], b[i], msg);
+ }
+ return;
+ }
+ is(a, b, msg);
+ }
+
+ assertSettingsMatch(expected) {
+ let { settings } = this;
+ for (let [setting, value] of Object.entries(expected)) {
+ this._assertMatches(settings[setting], value, `${setting} matches`);
+ }
+ }
+
+ assertPrintedWithSettings(expected) {
+ ok(this._printedSettings, "Printed settings have been recorded");
+ for (let [setting, value] of Object.entries(expected)) {
+ this._assertMatches(
+ this._printedSettings[setting],
+ value,
+ `${setting} matches printed setting`
+ );
+ }
+ }
+
+ async assertSettingsChanged(from, to, changeFn) {
+ is(
+ Object.keys(from).length,
+ Object.keys(to).length,
+ "Got the same number of settings to check"
+ );
+ ok(
+ Object.keys(from).every(s => s in to),
+ "Checking the same setting names"
+ );
+ this.assertSettingsMatch(from);
+ await changeFn();
+ this.assertSettingsMatch(to);
+ }
+
+ async assertSettingsNotChanged(settings, changeFn) {
+ await this.assertSettingsChanged(settings, settings, changeFn);
+ }
+
+ awaitAnimationFrame() {
+ return new Promise(resolve => this.win.requestAnimationFrame(resolve));
+ }
+
+ mockFilePickerCancel() {
+ if (!pickerMocked) {
+ pickerMocked = true;
+ MockFilePicker.init(window);
+ registerCleanupFunction(() => MockFilePicker.cleanup());
+ }
+ MockFilePicker.returnValue = MockFilePicker.returnCancel;
+ }
+
+ mockFilePicker(filename) {
+ if (!pickerMocked) {
+ pickerMocked = true;
+ MockFilePicker.init(window);
+ registerCleanupFunction(() => MockFilePicker.cleanup());
+ }
+ MockFilePicker.returnValue = MockFilePicker.returnOK;
+ let file = Services.dirsvc.get("TmpD", Ci.nsIFile);
+ file.append(filename);
+ registerCleanupFunction(() => {
+ if (file.exists()) {
+ file.remove(false);
+ }
+ });
+ MockFilePicker.setFiles([file]);
+ return file;
+ }
+}
diff --git a/toolkit/components/printing/tests/longerArticle.html b/toolkit/components/printing/tests/longerArticle.html
new file mode 100644
index 0000000000..4109e55959
--- /dev/null
+++ b/toolkit/components/printing/tests/longerArticle.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <style>
+ @media print {
+ #page-2 {
+ page-break-before: always;
+ }
+ #page-3 {
+ page-break-before: always;
+ }
+ }
+ </style>
+ </head>
+ <body>
+ <h1 id="page-1">Page 1</h1>
+ <h1 id="page-2">Page 2</h1>
+ <h1 id="page-3">Page 3</h1>
+ </body>
+</html>
diff --git a/toolkit/components/printing/tests/simplifyArticleSample.html b/toolkit/components/printing/tests/simplifyArticleSample.html
new file mode 100644
index 0000000000..70b172cf63
--- /dev/null
+++ b/toolkit/components/printing/tests/simplifyArticleSample.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Article title</title>
+<meta name="description" content="This is the article description." />
+</head>
+<body>
+<header>Site header</header>
+<div>
+<h1>Article title</h1>
+<h2 class="author">by Jane Doe</h2>
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Donec et mollis dolor. Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam tincidunt congue enim, ut porta lorem lacinia consectetur. Donec ut libero sed arcu vehicula ultricies a non tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ut gravida lorem. Ut turpis felis, pulvinar a semper sed, adipiscing id dolor. Pellentesque auctor nisi id magna consequat sagittis. Curabitur dapibus enim sit amet elit pharetra tincidunt feugiat nisl imperdiet. Ut convallis libero in urna ultrices accumsan. Donec sed odio eros. Donec viverra mi quis quam pulvinar at malesuada arcu rhoncus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetu</p>
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Donec et mollis dolor. Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam tincidunt congue enim, ut porta lorem lacinia consectetur. Donec ut libero sed arcu vehicula ultricies a non tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ut gravida lorem. Ut turpis felis, pulvinar a semper sed, adipiscing id dolor. Pellentesque auctor nisi id magna consequat sagittis. Curabitur dapibus enim sit amet elit pharetra tincidunt feugiat nisl imperdiet. Ut convallis libero in urna ultrices accumsan. Donec sed odio eros. Donec viverra mi quis quam pulvinar at malesuada arcu rhoncus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetu</p>
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Donec et mollis dolor. Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam tincidunt congue enim, ut porta lorem lacinia consectetur. Donec ut libero sed arcu vehicula ultricies a non tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ut gravida lorem. Ut turpis felis, pulvinar a semper sed, adipiscing id dolor. Pellentesque auctor nisi id magna consequat sagittis. Curabitur dapibus enim sit amet elit pharetra tincidunt feugiat nisl imperdiet. Ut convallis libero in urna ultrices accumsan. Donec sed odio eros. Donec viverra mi quis quam pulvinar at malesuada arcu rhoncus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetu</p>
+</body>
+</html>
diff --git a/toolkit/components/printing/tests/simplifyNonArticleSample.html b/toolkit/components/printing/tests/simplifyNonArticleSample.html
new file mode 100644
index 0000000000..e216af3c1f
--- /dev/null
+++ b/toolkit/components/printing/tests/simplifyNonArticleSample.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Non article title</title>
+<meta name="description" content="This is the non-article description." />
+</head>
+<body>
+</body>
+</html>