summaryrefslogtreecommitdiffstats
path: root/toolkit/actors/PrintingChild.sys.mjs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--toolkit/actors/PrintingChild.sys.mjs260
1 files changed, 260 insertions, 0 deletions
diff --git a/toolkit/actors/PrintingChild.sys.mjs b/toolkit/actors/PrintingChild.sys.mjs
new file mode 100644
index 0000000000..4ebfaf9faf
--- /dev/null
+++ b/toolkit/actors/PrintingChild.sys.mjs
@@ -0,0 +1,260 @@
+/* vim: set ts=2 sw=2 sts=2 et tw=80: */
+/* 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/. */
+
+const lazy = {};
+
+ChromeUtils.defineESModuleGetters(lazy, {
+ DeferredTask: "resource://gre/modules/DeferredTask.sys.mjs",
+ ReaderMode: "resource://gre/modules/ReaderMode.sys.mjs",
+ setTimeout: "resource://gre/modules/Timer.sys.mjs",
+});
+
+let gPendingPreviewsMap = new Map();
+
+export class PrintingChild extends JSWindowActorChild {
+ actorCreated() {
+ // When the print preview page is loaded, the actor will change, so update
+ // the state/progress listener to the new actor.
+ let listener = gPendingPreviewsMap.get(this.browsingContext.id);
+ if (listener) {
+ listener.actor = this;
+ }
+ this.contentWindow.addEventListener("scroll", this);
+ }
+
+ didDestroy() {
+ this._scrollTask?.disarm();
+ this.contentWindow?.removeEventListener("scroll", this);
+ }
+
+ handleEvent(event) {
+ switch (event.type) {
+ case "PrintingError": {
+ let win = event.target.defaultView;
+ let wbp = win.getInterface(Ci.nsIWebBrowserPrint);
+ let nsresult = event.detail;
+ this.sendAsyncMessage("Printing:Error", {
+ isPrinting: wbp.doingPrint,
+ nsresult,
+ });
+ break;
+ }
+
+ case "scroll":
+ if (!this._scrollTask) {
+ this._scrollTask = new lazy.DeferredTask(
+ () => this.updateCurrentPage(),
+ 16,
+ 16
+ );
+ }
+ this._scrollTask.arm();
+ break;
+ }
+ }
+
+ receiveMessage(message) {
+ let data = message.data;
+ switch (message.name) {
+ case "Printing:Preview:Navigate": {
+ this.navigate(data.navType, data.pageNum);
+ break;
+ }
+
+ case "Printing:Preview:ParseDocument": {
+ return this.parseDocument(
+ data.URL,
+ Services.wm.getOuterWindowWithId(data.windowID)
+ );
+ }
+ }
+
+ return undefined;
+ }
+
+ async parseDocument(URL, contentWindow) {
+ // The document in 'contentWindow' will be simplified and the resulting nodes
+ // will be inserted into this.contentWindow.
+ let thisWindow = this.contentWindow;
+
+ // By using ReaderMode primitives, we parse given document and place the
+ // resulting JS object into the DOM of current browser.
+ let article;
+ try {
+ article = await lazy.ReaderMode.parseDocument(contentWindow.document);
+ } catch (ex) {
+ console.error(ex);
+ }
+
+ await new Promise(resolve => {
+ // We make use of a web progress listener in order to know when the content we inject
+ // into the DOM has finished rendering. If our layout engine is still painting, we
+ // will wait for MozAfterPaint event to be fired.
+ let actor = thisWindow.windowGlobalChild.getActor("Printing");
+ let webProgressListener = {
+ onStateChange(webProgress, req, flags, status) {
+ if (flags & Ci.nsIWebProgressListener.STATE_STOP) {
+ webProgress.removeProgressListener(webProgressListener);
+ let domUtils = contentWindow.windowUtils;
+ // Here we tell the parent that we have parsed the document successfully
+ // using ReaderMode primitives and we are able to enter on preview mode.
+ if (domUtils.isMozAfterPaintPending) {
+ let onPaint = function () {
+ contentWindow.removeEventListener("MozAfterPaint", onPaint);
+ actor.sendAsyncMessage("Printing:Preview:ReaderModeReady");
+ resolve();
+ };
+ contentWindow.addEventListener("MozAfterPaint", onPaint);
+ // This timer is needed for when display list invalidation doesn't invalidate.
+ lazy.setTimeout(() => {
+ contentWindow.removeEventListener("MozAfterPaint", onPaint);
+ actor.sendAsyncMessage("Printing:Preview:ReaderModeReady");
+ resolve();
+ }, 100);
+ } else {
+ actor.sendAsyncMessage("Printing:Preview:ReaderModeReady");
+ resolve();
+ }
+ }
+ },
+
+ QueryInterface: ChromeUtils.generateQI([
+ "nsIWebProgressListener",
+ "nsISupportsWeakReference",
+ "nsIObserver",
+ ]),
+ };
+
+ // Here we QI the docShell into a nsIWebProgress passing our web progress listener in.
+ let webProgress = thisWindow.docShell
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebProgress);
+ webProgress.addProgressListener(
+ webProgressListener,
+ Ci.nsIWebProgress.NOTIFY_STATE_REQUEST
+ );
+
+ let document = thisWindow.document;
+ document.head.innerHTML = "";
+
+ // Set base URI of document. Print preview code will read this value to
+ // populate the URL field in print settings so that it doesn't show
+ // "about:blank" as its URI.
+ let headBaseElement = document.createElement("base");
+ headBaseElement.setAttribute("href", URL);
+ document.head.appendChild(headBaseElement);
+
+ // Create link element referencing aboutReader.css and append it to head
+ let headStyleElement = document.createElement("link");
+ headStyleElement.setAttribute("rel", "stylesheet");
+ headStyleElement.setAttribute(
+ "href",
+ "chrome://global/skin/aboutReader.css"
+ );
+ headStyleElement.setAttribute("type", "text/css");
+ document.head.appendChild(headStyleElement);
+
+ // Create link element referencing simplifyMode.css and append it to head
+ headStyleElement = document.createElement("link");
+ headStyleElement.setAttribute("rel", "stylesheet");
+ headStyleElement.setAttribute(
+ "href",
+ "chrome://global/content/simplifyMode.css"
+ );
+ headStyleElement.setAttribute("type", "text/css");
+ document.head.appendChild(headStyleElement);
+
+ document.body.innerHTML = "";
+
+ // Create container div (main element) and append it to body
+ let containerElement = document.createElement("div");
+ containerElement.setAttribute("class", "container");
+ document.body.appendChild(containerElement);
+
+ // Reader Mode might return null if there's a failure when parsing the document.
+ // We'll render the error message for the Simplify Page document when that happens.
+ if (article) {
+ // Set title of document
+ document.title = article.title;
+
+ // Create header div and append it to container
+ let headerElement = document.createElement("div");
+ headerElement.setAttribute("class", "reader-header");
+ headerElement.setAttribute("class", "header");
+ containerElement.appendChild(headerElement);
+
+ // Jam the article's title and byline into header div
+ let titleElement = document.createElement("h1");
+ titleElement.setAttribute("class", "reader-title");
+ titleElement.textContent = article.title;
+ headerElement.appendChild(titleElement);
+
+ let bylineElement = document.createElement("div");
+ bylineElement.setAttribute("class", "reader-credits credits");
+ bylineElement.textContent = article.byline;
+ headerElement.appendChild(bylineElement);
+
+ // Display header element
+ headerElement.style.display = "block";
+
+ // Create content div and append it to container
+ let contentElement = document.createElement("div");
+ contentElement.setAttribute("class", "content");
+ containerElement.appendChild(contentElement);
+
+ // Jam the article's content into content div
+ let readerContent = document.createElement("div");
+ readerContent.setAttribute("class", "moz-reader-content");
+ contentElement.appendChild(readerContent);
+
+ let articleUri = Services.io.newURI(article.url);
+ let parserUtils = Cc["@mozilla.org/parserutils;1"].getService(
+ Ci.nsIParserUtils
+ );
+ let contentFragment = parserUtils.parseFragment(
+ article.content,
+ Ci.nsIParserUtils.SanitizerDropForms |
+ Ci.nsIParserUtils.SanitizerAllowStyle,
+ false,
+ articleUri,
+ readerContent
+ );
+
+ readerContent.appendChild(contentFragment);
+
+ // Display reader content element
+ readerContent.style.display = "block";
+ } else {
+ const l10n = new Localization(["toolkit/about/aboutReader.ftl"], true);
+ const errorMessage = l10n.formatValueSync("about-reader-load-error");
+
+ document.title = errorMessage;
+
+ // Create reader message div and append it to body
+ let readerMessageElement = document.createElement("div");
+ readerMessageElement.setAttribute("class", "reader-message");
+ readerMessageElement.textContent = errorMessage;
+ containerElement.appendChild(readerMessageElement);
+
+ // Display reader message element
+ readerMessageElement.style.display = "block";
+ }
+ });
+ }
+
+ updateCurrentPage() {
+ let cv = this.docShell.contentViewer;
+ cv.QueryInterface(Ci.nsIWebBrowserPrint);
+ this.sendAsyncMessage("Printing:Preview:CurrentPage", {
+ currentPage: cv.printPreviewCurrentPageNumber,
+ });
+ }
+
+ navigate(navType, pageNum) {
+ let cv = this.docShell.contentViewer;
+ cv.QueryInterface(Ci.nsIWebBrowserPrint);
+ cv.printPreviewScrollToPage(navType, pageNum);
+ }
+}