Bug 1654684 - Add sheet-indicator and preview navigation to the print preview. r=mstriemer,fluent-reviewers,flod

Differential Revision: https://phabricator.services.mozilla.com/D96737
This commit is contained in:
Sam Foster 2020-12-09 02:59:26 +00:00
Родитель c5963a5340
Коммит 0c4bb65e4d
17 изменённых файлов: 776 добавлений и 14 удалений

Просмотреть файл

@ -1422,6 +1422,19 @@ toolbarpaletteitem > toolbaritem {
font-size: large; font-size: large;
} }
printpreview-pagination {
opacity: 0;
transition: opacity 100ms 500ms;
}
printpreview-pagination:focus-within,
.previewStack:hover > printpreview-pagination {
opacity: 1;
transition: opacity 100ms;
}
.previewStack[rendering=true] > printpreview-pagination {
opacity: 0;
}
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
.previewStack { .previewStack {
background-color: #2A2A2E; background-color: #2A2A2E;

Просмотреть файл

@ -55,6 +55,10 @@ async function testPrintPreview() {
); );
gBrowser.getTabDialogBox(gBrowser.selectedBrowser).abortAllDialogs(); gBrowser.getTabDialogBox(gBrowser.selectedBrowser).abortAllDialogs();
// Wait for the preview to go away
await BrowserTestUtils.waitForCondition(
() => !document.querySelector(".printPreviewBrowser")
);
await extension.awaitFinish("tabs.printPreview"); await extension.awaitFinish("tabs.printPreview");
} }

Просмотреть файл

@ -20,6 +20,12 @@ ChromeUtils.defineModuleGetter(
"resource://gre/modules/ReaderMode.jsm" "resource://gre/modules/ReaderMode.jsm"
); );
ChromeUtils.defineModuleGetter(
this,
"DeferredTask",
"resource://gre/modules/DeferredTask.jsm"
);
let gPrintPreviewInitializingInfo = null; let gPrintPreviewInitializingInfo = null;
let gPendingPreviewsMap = new Map(); let gPendingPreviewsMap = new Map();
@ -87,6 +93,17 @@ class PrintingChild extends JSWindowActorChild {
this.updatePageCount(); this.updatePageCount();
break; break;
} }
case "scroll":
if (!this._scrollTask) {
this._scrollTask = new DeferredTask(
() => this.updateCurrentPage(),
16,
16
);
}
this._scrollTask.arm();
break;
} }
} }
@ -420,6 +437,14 @@ class PrintingChild extends JSWindowActorChild {
}); });
} }
updateCurrentPage() {
let cv = this.docShell.contentViewer;
cv.QueryInterface(Ci.nsIWebBrowserPrint);
this.sendAsyncMessage("Printing:Preview:CurrentPage", {
currentPage: cv.printPreviewCurrentPageNumber,
});
}
navigate(navType, pageNum) { navigate(navType, pageNum) {
let cv = this.docShell.contentViewer; let cv = this.docShell.contentViewer;
cv.QueryInterface(Ci.nsIWebBrowserPrint); cv.QueryInterface(Ci.nsIWebBrowserPrint);

Просмотреть файл

@ -33,10 +33,15 @@ class PrintingParent extends JSWindowActorParent {
return undefined; return undefined;
} }
let listener = PrintUtils._webProgressPP.value; let listener = PrintUtils._webProgressPP?.value;
let data = message.data; let data = message.data;
switch (message.name) { switch (message.name) {
case "Printing:Preview:CurrentPage": {
browser.setAttribute("current-page", message.data.currentPage);
break;
}
case "Printing:Preview:Entered": { case "Printing:Preview:Entered": {
// This message is sent by the content process once it has completed // This message is sent by the content process once it has completed
// putting the content into print preview mode. We must wait for that to // putting the content into print preview mode. We must wait for that to

Просмотреть файл

@ -757,6 +757,7 @@ var PrintEventHandler = {
detail: { sheetCount, totalPages: totalPageCount }, detail: { sheetCount, totalPages: totalPageCount },
}) })
); );
this.previewBrowser.setAttribute("sheet-count", sheetCount);
this._hideRenderingIndicator(); this._hideRenderingIndicator();

Просмотреть файл

@ -0,0 +1,132 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
:host {
/* in-content/common.css variables */
--blue-50: #0a84ff;
--grey-90-a10: rgba(12, 12, 13, 0.1);
--shadow-30: 0 4px 16px var(--grey-90-a10);
--border-active-shadow: var(--blue-50);
--border-active-color: ButtonShadow;
}
:host {
display: block;
position: absolute;
bottom: 24px;
inset-inline-start: 50%;
translate: -50%;
}
:host(:-moz-locale-dir(rtl)) {
translate: 50%;
}
.container {
margin-inline: auto;
align-items: center;
display: flex;
justify-content: center;
box-shadow: var(--shadow-30);
color: var(--toolbar-color);
background-color: var(--toolbar-bgcolor);
border-radius: 6px;
border-style: none;
}
.toolbarButton,
.toolbarCenter {
align-self: stretch;
flex: 0 0 auto;
padding: var(--toolbarbutton-outer-padding);
border: none;
border-inline-end: 1px solid ThreeDShadow;
border-block: 1px solid ThreeDShadow;
color: inherit;
background-color: transparent;
}
.startItem {
border-inline-start: 1px solid ThreeDShadow;
border-start-start-radius: 6px;
border-end-start-radius: 6px;
}
.endItem {
border-start-end-radius: 6px;
border-end-end-radius: 6px;
}
.toolbarButton {
min-width: calc(2 * var(--toolbarbutton-inner-padding) + 16px);
min-height: calc(2 * var(--toolbarbutton-inner-padding) + 16px);
}
.toolbarButton::after {
content: "";
display: inline-block;
width: 16px;
height: 16px;
vertical-align: text-bottom;
text-align: center;
background-repeat: no-repeat;
background-position: center center;
background-size: 12px;
-moz-context-properties: fill, fill-opacity;
fill: var(--lwt-toolbarbutton-icon-fill, currentColor);
fill-opacity: var(--toolbarbutton-icon-fill-opacity);
}
.toolbarButton:hover {
background-color: var(--toolbarbutton-hover-background);
}
.toolbarButton:hover:active {
background-color: var(--toolbarbutton-active-background);
}
.toolbarButton::-moz-focus-inner {
border: none;
}
.toolbarButton:focus {
z-index: 1;
}
.toolbarButton:-moz-focusring {
outline: 2px solid var(--border-active-shadow);
}
.toolbarButton.startItem:-moz-focusring,
.toolbarButton.endItem:-moz-locale-dir(rtl):-moz-focusring {
-moz-outline-radius: 8px;
-moz-outline-radius-topright: 0;
-moz-outline-radius-bottomright: 0;
}
.toolbarButton.endItem:-moz-focusring,
.toolbarButton.startItem:-moz-locale-dir(rtl):-moz-focusring {
-moz-outline-radius: 8px;
-moz-outline-radius-topleft: 0;
-moz-outline-radius-bottomleft: 0;
}
.toolbarCenter {
flex-shrink: 0;
/* 3 chars + (3px border + 1px padding) on both sides */
min-width: calc(8px + 3ch);
padding: 0 32px;
display: flex;
align-items: center;
justify-content: center;
}
#navigateHome::after,
#navigateEnd::after {
background-image: url("chrome://global/skin/icons/chevron.svg");
}
#navigatePrevious::after,
#navigateNext::after {
background-image: url("chrome://global/skin/icons/arrow-left.svg");
}
#navigatePrevious:-moz-locale-dir(rtl)::after,
#navigateEnd:-moz-locale-dir(rtl)::after,
#navigateHome:-moz-locale-dir(ltr)::after,
#navigateNext:-moz-locale-dir(ltr)::after {
transform: scaleX(-1);
}

Просмотреть файл

@ -0,0 +1,178 @@
// This file is loaded into the browser window scope.
/* eslint-env mozilla/browser-window */
// -*- tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2 -*-
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
customElements.define(
"printpreview-pagination",
class PrintPreviewPagination extends HTMLElement {
static get markup() {
return `
<html:link rel="stylesheet" href="chrome://global/content/printPagination.css" />
<html:div class="container">
<html:button id="navigateHome" class="toolbarButton startItem" data-l10n-id="printpreview-homearrow-button"></html:button>
<html:button id="navigatePrevious" class="toolbarButton" data-l10n-id="printpreview-previousarrow-button"></html:button>
<html:div class="toolbarCenter"><html:span id="sheetIndicator" data-l10n-id="printpreview-sheet-of-sheets" data-l10n-args='{ "sheetNum": 1, "sheetCount": 1 }'></html:span></html:div>
<html:button id="navigateNext" class="toolbarButton" data-l10n-id="printpreview-nextarrow-button"></html:button>
<html:button id="navigateEnd" class="toolbarButton endItem" data-l10n-id="printpreview-endarrow-button"></html:button>
</html:div>
`;
}
static get defaultProperties() {
return {
currentPage: 1,
sheetCount: 1,
};
}
get previewBrowser() {
// Assuming we're a sibling of our preview browser.
return this.parentNode.querySelector(".printPreviewBrowser");
}
connectedCallback() {
MozXULElement.insertFTLIfNeeded("toolkit/printing/printPreview.ftl");
const shadowRoot = this.attachShadow({ mode: "open" });
document.l10n.connectRoot(shadowRoot);
let fragment = MozXULElement.parseXULToFragment(this.constructor.markup);
this.shadowRoot.append(fragment);
this.elements = {
sheetIndicator: shadowRoot.querySelector("#sheetIndicator"),
homeButton: shadowRoot.querySelector("#navigateHome"),
previousButton: shadowRoot.querySelector("#navigatePrevious"),
nextButton: shadowRoot.querySelector("#navigateNext"),
endButton: shadowRoot.querySelector("#navigateEnd"),
};
this.shadowRoot.addEventListener("click", this);
let knownAttrs = {
"sheet-count": "sheetCount",
"current-page": "currentPage",
};
this.mutationObserver = new MutationObserver(changes => {
let opts = {};
for (let change of changes) {
let { attributeName, target, type } = change;
if (type == "attributes" && attributeName in knownAttrs) {
opts[knownAttrs[attributeName]] = parseInt(
target.getAttribute(attributeName),
10
);
}
}
if (opts.sheetCount || opts.currentPage) {
this.update(opts);
}
});
this.mutationObserver.observe(this.previewBrowser, {
attributes: ["current-page", "sheet-count"],
});
// Initial render with some default values
// We'll be updated with real values when available
this.update(this.constructor.defaultProperties);
}
disconnectedCallback() {
document.l10n.disconnectRoot(this.shadowRoot);
this.shadowRoot.textContent = "";
this.mutationObserver?.disconnect();
delete this.mutationObserver;
}
handleEvent(event) {
if (event.type == "click" && event.button != 0) {
return;
}
event.stopPropagation();
switch (event.target) {
case this.elements.homeButton:
this.navigate(0, 0, "home");
break;
case this.elements.previousButton:
this.navigate(-1, 0, 0);
break;
case this.elements.nextButton:
this.navigate(1, 0, 0);
break;
case this.elements.endButton:
this.navigate(0, 0, "end");
break;
}
}
navigate(aDirection, aPageNum, aHomeOrEnd) {
const nsIWebBrowserPrint = Ci.nsIWebBrowserPrint;
let targetNum;
let navType;
// we use only one of aHomeOrEnd, aDirection, or aPageNum
if (aHomeOrEnd) {
// We're going to either the very first page ("home"), or the
// very last page ("end").
if (aHomeOrEnd == "home") {
targetNum = 1;
navType = nsIWebBrowserPrint.PRINTPREVIEW_HOME;
} else {
targetNum = this.sheetCount;
navType = nsIWebBrowserPrint.PRINTPREVIEW_END;
}
} else if (aPageNum) {
// We're going to a specific page (aPageNum)
targetNum = Math.min(Math.max(1, aPageNum), this.sheetCount);
navType = nsIWebBrowserPrint.PRINTPREVIEW_GOTO_PAGENUM;
} else {
// aDirection is either +1 or -1, and allows us to increment
// or decrement our currently viewed page.
targetNum = Math.min(
Math.max(1, this.currentSheet + aDirection),
this.sheetCount
);
navType = nsIWebBrowserPrint.PRINTPREVIEW_GOTO_PAGENUM;
}
// Preemptively update our own state, rather than waiting for the message from the child process
// This allows subsequent clicks of next/back to advance 1 page per click if possible
// and keeps the UI feeling more responsive
this.update({ currentPage: targetNum });
this.previewBrowser.sendMessageToActor(
"Printing:Preview:Navigate",
{
navType,
pageNum: targetNum,
},
"Printing"
);
}
update(data = {}) {
if (data.sheetCount) {
if (this.sheetCount !== data.sheetCount && this.currentSheet !== 1) {
// when sheet count changes, scroll position will get reset
this.currentSheet = 1;
}
this.sheetCount = data.sheetCount;
}
if (data.currentPage) {
this.currentSheet = data.currentPage;
}
document.l10n.setAttributes(
this.elements.sheetIndicator,
this.elements.sheetIndicator.dataset.l10nId,
{
sheetNum: this.currentSheet,
sheetCount: this.sheetCount,
}
);
}
}
);

Просмотреть файл

@ -175,6 +175,13 @@ var PrintUtils = {
true true
).firstElementChild; ).firstElementChild;
previewStack.append(browser); previewStack.append(browser);
// show the toolbar after we go into print preview mode so
// that we can initialize the toolbar with total num pages
let previewPagination = document.createElement("printpreview-pagination");
previewPagination.classList.add("printPreviewNavigation");
previewStack.append(previewPagination);
aDialogBrowser.parentElement.prepend(previewStack); aDialogBrowser.parentElement.prepend(previewStack);
return browser; return browser;
}, },

Просмотреть файл

@ -15,6 +15,7 @@ toolkit.jar:
#endif #endif
content/global/landscape.svg (content/landscape.svg) content/global/landscape.svg (content/landscape.svg)
content/global/portrait.svg (content/portrait.svg) content/global/portrait.svg (content/portrait.svg)
content/global/printPreviewPagination.js (content/printPreviewPagination.js)
content/global/printPreviewToolbar.js (content/printPreviewToolbar.js) content/global/printPreviewToolbar.js (content/printPreviewToolbar.js)
content/global/printUtils.js (content/printUtils.js) content/global/printUtils.js (content/printUtils.js)
content/global/print.js (content/print.js) content/global/print.js (content/print.js)
@ -22,3 +23,4 @@ toolkit.jar:
content/global/print.css (content/print.css) content/global/print.css (content/print.css)
content/global/toggle-group.css (content/toggle-group.css) content/global/toggle-group.css (content/toggle-group.css)
content/global/simplifyMode.css (content/simplifyMode.css) content/global/simplifyMode.css (content/simplifyMode.css)
content/global/printPagination.css (content/printPagination.css)

Просмотреть файл

@ -36,6 +36,10 @@ support-files =
support-files = support-files =
file_print.html file_print.html
[browser_preview_navigation.js]
support-files =
longerArticle.html
[browser_preview_print_simplify_non_article.js] [browser_preview_print_simplify_non_article.js]
support-files = support-files =
simplifyNonArticleSample.html simplifyNonArticleSample.html

Просмотреть файл

@ -86,10 +86,26 @@ add_task(async function testTabOrder() {
const previewBrowser = document.querySelector(".printPreviewBrowser"); const previewBrowser = document.querySelector(".printPreviewBrowser");
ok(previewBrowser, "Got the print preview browser"); ok(previewBrowser, "Got the print preview browser");
let focused = BrowserTestUtils.waitForEvent(previewBrowser, "focus");
EventUtils.synthesizeKey("KEY_Tab", { shiftKey: true }); 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; await focused;
ok(true, "Print preview focused after shift+tab"); ok(true, "Print preview focused after shift+tab through the paginator");
focused = BrowserTestUtils.waitForEvent(gNavToolbox, "focus", true); focused = BrowserTestUtils.waitForEvent(gNavToolbox, "focus", true);
EventUtils.synthesizeKey("KEY_Tab", { shiftKey: true }); EventUtils.synthesizeKey("KEY_Tab", { shiftKey: true });
@ -101,6 +117,17 @@ add_task(async function testTabOrder() {
await focused; await focused;
ok(true, "Print preview focused after tab"); 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"); focused = BrowserTestUtils.waitForEvent(printerPicker, "focus");
EventUtils.synthesizeKey("KEY_Tab"); EventUtils.synthesizeKey("KEY_Tab");
await focused; await focused;

Просмотреть файл

@ -0,0 +1,319 @@
/* 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"),
{}
);
await helper.awaitAnimationFrame();
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"),
{}
);
await helper.awaitAnimationFrame();
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.awaitAnimationFrame();
await helper.closeDialog();
}, "longerArticle.html");
}).skip(); // Bug 1681026
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"
);
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");
});

Просмотреть файл

@ -4,17 +4,19 @@ const { MockFilePicker } = SpecialPowers;
let pickerMocked = false; let pickerMocked = false;
class PrintHelper { class PrintHelper {
static async withTestPage(testFn) { static async withTestPage(testFn, pagePathname) {
await SpecialPowers.pushPrefEnv({ await SpecialPowers.pushPrefEnv({
set: [["print.tab_modal.enabled", true]], set: [["print.tab_modal.enabled", true]],
}); });
let pageUrl = pagePathname
let taskReturn = await BrowserTestUtils.withNewTab( ? this.getTestPageUrl(pagePathname)
this.defaultTestPageUrl, : this.defaultTestPageUrl;
async function(browser) { info("withTestPage: " + pageUrl);
await testFn(new PrintHelper(browser)); let taskReturn = await BrowserTestUtils.withNewTab(pageUrl, async function(
} browser
); ) {
await testFn(new PrintHelper(browser));
});
await SpecialPowers.popPrefEnv(); await SpecialPowers.popPrefEnv();
@ -27,12 +29,16 @@ class PrintHelper {
return taskReturn; return taskReturn;
} }
static get defaultTestPageUrl() { static getTestPageUrl(pathName) {
const testPath = getRootDirectory(gTestPath).replace( const testPath = getRootDirectory(gTestPath).replace(
"chrome://mochitests/content", "chrome://mochitests/content",
"http://example.com" "http://example.com"
); );
return testPath + "simplifyArticleSample.html"; return testPath + pathName;
}
static get defaultTestPageUrl() {
return this.getTestPageUrl("simplifyArticleSample.html");
} }
static createMockPaper(paperProperties = {}) { static createMockPaper(paperProperties = {}) {

Просмотреть файл

@ -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>

Просмотреть файл

@ -848,6 +848,10 @@
"printpreview-toolbar", "printpreview-toolbar",
"chrome://global/content/printPreviewToolbar.js", "chrome://global/content/printPreviewToolbar.js",
], ],
[
"printpreview-pagination",
"chrome://global/content/printPreviewPagination.js",
],
[ [
"autocomplete-input", "autocomplete-input",
"chrome://global/content/elements/autocomplete-input.js", "chrome://global/content/elements/autocomplete-input.js",

Просмотреть файл

@ -39,6 +39,11 @@ printpreview-page =
.value = Page: .value = Page:
.accesskey = a .accesskey = a
# Variables
# $sheetNum (integer) - The current sheet number
# $sheetCount (integer) - The total number of sheets to print
printpreview-sheet-of-sheets = { $sheetNum } of { $sheetCount }
## Variables ## Variables
## $percent (integer) - menuitem percent label ## $percent (integer) - menuitem percent label
## $arrow (String) - UTF-8 arrow character for navigation buttons ## $arrow (String) - UTF-8 arrow character for navigation buttons
@ -58,3 +63,11 @@ printpreview-endarrow =
.label = { $arrow } .label = { $arrow }
.tooltiptext = Last page .tooltiptext = Last page
printpreview-homearrow-button =
.title = First page
printpreview-previousarrow-button =
.title = Previous page
printpreview-nextarrow-button =
.title = Next page
printpreview-endarrow-button =
.title = Last page

Просмотреть файл

@ -362,6 +362,7 @@ let JSWINDOWACTORS = {
events: { events: {
PrintingError: { capture: true }, PrintingError: { capture: true },
printPreviewUpdate: { capture: true }, printPreviewUpdate: { capture: true },
scroll: {},
}, },
}, },
}, },