зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
c5963a5340
Коммит
0c4bb65e4d
|
@ -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: {},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
Загрузка…
Ссылка в новой задаче