Bug 1867190 - Add prefs for PHC probablities r=glandium
[gecko.git] / toolkit / actors / PrintingChild.sys.mjs
blob4fca3ab4038b89fef2177d75c318ad02c5ada7f7
1 /* vim: set ts=2 sw=2 sts=2 et tw=80: */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 const lazy = {};
8 ChromeUtils.defineESModuleGetters(lazy, {
9   DeferredTask: "resource://gre/modules/DeferredTask.sys.mjs",
10   ReaderMode: "resource://gre/modules/ReaderMode.sys.mjs",
11   setTimeout: "resource://gre/modules/Timer.sys.mjs",
12 });
14 let gPendingPreviewsMap = new Map();
16 export class PrintingChild extends JSWindowActorChild {
17   actorCreated() {
18     // When the print preview page is loaded, the actor will change, so update
19     // the state/progress listener to the new actor.
20     let listener = gPendingPreviewsMap.get(this.browsingContext.id);
21     if (listener) {
22       listener.actor = this;
23     }
24     this.contentWindow.addEventListener("scroll", this);
25   }
27   didDestroy() {
28     this._scrollTask?.disarm();
29     this.contentWindow?.removeEventListener("scroll", this);
30   }
32   handleEvent(event) {
33     switch (event.type) {
34       case "PrintingError": {
35         let win = event.target.defaultView;
36         let wbp = win.getInterface(Ci.nsIWebBrowserPrint);
37         let nsresult = event.detail;
38         this.sendAsyncMessage("Printing:Error", {
39           isPrinting: wbp.doingPrint,
40           nsresult,
41         });
42         break;
43       }
45       case "scroll":
46         if (!this._scrollTask) {
47           this._scrollTask = new lazy.DeferredTask(
48             () => this.updateCurrentPage(),
49             16,
50             16
51           );
52         }
53         this._scrollTask.arm();
54         break;
55     }
56   }
58   receiveMessage(message) {
59     let data = message.data;
60     switch (message.name) {
61       case "Printing:Preview:Navigate": {
62         this.navigate(data.navType, data.pageNum);
63         break;
64       }
66       case "Printing:Preview:ParseDocument": {
67         return this.parseDocument(
68           data.URL,
69           Services.wm.getOuterWindowWithId(data.windowID)
70         );
71       }
72     }
74     return undefined;
75   }
77   async parseDocument(URL, contentWindow) {
78     // The document in 'contentWindow' will be simplified and the resulting nodes
79     // will be inserted into this.contentWindow.
80     let thisWindow = this.contentWindow;
82     // By using ReaderMode primitives, we parse given document and place the
83     // resulting JS object into the DOM of current browser.
84     let article;
85     try {
86       article = await lazy.ReaderMode.parseDocument(contentWindow.document);
87     } catch (ex) {
88       console.error(ex);
89     }
91     await new Promise(resolve => {
92       // We make use of a web progress listener in order to know when the content we inject
93       // into the DOM has finished rendering. If our layout engine is still painting, we
94       // will wait for MozAfterPaint event to be fired.
95       let actor = thisWindow.windowGlobalChild.getActor("Printing");
96       let webProgressListener = {
97         onStateChange(webProgress, req, flags, status) {
98           if (flags & Ci.nsIWebProgressListener.STATE_STOP) {
99             webProgress.removeProgressListener(webProgressListener);
100             let domUtils = contentWindow.windowUtils;
101             // Here we tell the parent that we have parsed the document successfully
102             // using ReaderMode primitives and we are able to enter on preview mode.
103             if (domUtils.isMozAfterPaintPending) {
104               let onPaint = function () {
105                 contentWindow.removeEventListener("MozAfterPaint", onPaint);
106                 actor.sendAsyncMessage("Printing:Preview:ReaderModeReady");
107                 resolve();
108               };
109               contentWindow.addEventListener("MozAfterPaint", onPaint);
110               // This timer is needed for when display list invalidation doesn't invalidate.
111               lazy.setTimeout(() => {
112                 contentWindow.removeEventListener("MozAfterPaint", onPaint);
113                 actor.sendAsyncMessage("Printing:Preview:ReaderModeReady");
114                 resolve();
115               }, 100);
116             } else {
117               actor.sendAsyncMessage("Printing:Preview:ReaderModeReady");
118               resolve();
119             }
120           }
121         },
123         QueryInterface: ChromeUtils.generateQI([
124           "nsIWebProgressListener",
125           "nsISupportsWeakReference",
126           "nsIObserver",
127         ]),
128       };
130       // Here we QI the docShell into a nsIWebProgress passing our web progress listener in.
131       let webProgress = thisWindow.docShell
132         .QueryInterface(Ci.nsIInterfaceRequestor)
133         .getInterface(Ci.nsIWebProgress);
134       webProgress.addProgressListener(
135         webProgressListener,
136         Ci.nsIWebProgress.NOTIFY_STATE_REQUEST
137       );
139       let document = thisWindow.document;
140       document.head.innerHTML = "";
142       // Set base URI of document. Print preview code will read this value to
143       // populate the URL field in print settings so that it doesn't show
144       // "about:blank" as its URI.
145       let headBaseElement = document.createElement("base");
146       headBaseElement.setAttribute("href", URL);
147       document.head.appendChild(headBaseElement);
149       // Create link element referencing aboutReader.css and append it to head
150       let headStyleElement = document.createElement("link");
151       headStyleElement.setAttribute("rel", "stylesheet");
152       headStyleElement.setAttribute(
153         "href",
154         "chrome://global/skin/aboutReader.css"
155       );
156       headStyleElement.setAttribute("type", "text/css");
157       document.head.appendChild(headStyleElement);
159       // Create link element referencing simplifyMode.css and append it to head
160       headStyleElement = document.createElement("link");
161       headStyleElement.setAttribute("rel", "stylesheet");
162       headStyleElement.setAttribute(
163         "href",
164         "chrome://global/content/simplifyMode.css"
165       );
166       headStyleElement.setAttribute("type", "text/css");
167       document.head.appendChild(headStyleElement);
169       document.body.innerHTML = "";
171       // Create container div (main element) and append it to body
172       let containerElement = document.createElement("div");
173       containerElement.setAttribute("class", "container");
174       document.body.appendChild(containerElement);
176       // Reader Mode might return null if there's a failure when parsing the document.
177       // We'll render the error message for the Simplify Page document when that happens.
178       if (article) {
179         // Set title of document
180         document.title = article.title;
182         // Create header div and append it to container
183         let headerElement = document.createElement("div");
184         headerElement.setAttribute("class", "reader-header");
185         headerElement.setAttribute("class", "header");
186         containerElement.appendChild(headerElement);
188         // Jam the article's title and byline into header div
189         let titleElement = document.createElement("h1");
190         titleElement.setAttribute("class", "reader-title");
191         titleElement.textContent = article.title;
192         headerElement.appendChild(titleElement);
194         let bylineElement = document.createElement("div");
195         bylineElement.setAttribute("class", "reader-credits credits");
196         bylineElement.textContent = article.byline;
197         headerElement.appendChild(bylineElement);
199         // Display header element
200         headerElement.style.display = "block";
202         // Create content div and append it to container
203         let contentElement = document.createElement("div");
204         contentElement.setAttribute("class", "content");
205         containerElement.appendChild(contentElement);
207         // Jam the article's content into content div
208         let readerContent = document.createElement("div");
209         readerContent.setAttribute("class", "moz-reader-content");
210         contentElement.appendChild(readerContent);
212         let articleUri = Services.io.newURI(article.url);
213         let parserUtils = Cc["@mozilla.org/parserutils;1"].getService(
214           Ci.nsIParserUtils
215         );
216         let contentFragment = parserUtils.parseFragment(
217           article.content,
218           Ci.nsIParserUtils.SanitizerDropForms |
219             Ci.nsIParserUtils.SanitizerAllowStyle,
220           false,
221           articleUri,
222           readerContent
223         );
225         readerContent.appendChild(contentFragment);
227         // Display reader content element
228         readerContent.style.display = "block";
229       } else {
230         const l10n = new Localization(["toolkit/about/aboutReader.ftl"], true);
231         const errorMessage = l10n.formatValueSync("about-reader-load-error");
233         document.title = errorMessage;
235         // Create reader message div and append it to body
236         let readerMessageElement = document.createElement("div");
237         readerMessageElement.setAttribute("class", "reader-message");
238         readerMessageElement.textContent = errorMessage;
239         containerElement.appendChild(readerMessageElement);
241         // Display reader message element
242         readerMessageElement.style.display = "block";
243       }
244     });
245   }
247   updateCurrentPage() {
248     let cv = this.docShell.docViewer;
249     cv.QueryInterface(Ci.nsIWebBrowserPrint);
250     this.sendAsyncMessage("Printing:Preview:CurrentPage", {
251       currentPage: cv.printPreviewCurrentPageNumber,
252     });
253   }
255   navigate(navType, pageNum) {
256     let cv = this.docShell.docViewer;
257     cv.QueryInterface(Ci.nsIWebBrowserPrint);
258     cv.printPreviewScrollToPage(navType, pageNum);
259   }