diff --git a/toolkit/content/browser-content.js b/toolkit/content/browser-content.js index 6e4a1be8c583..b5c8262b91bb 100644 --- a/toolkit/content/browser-content.js +++ b/toolkit/content/browser-content.js @@ -4,19 +4,20 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* eslint-env mozilla/frame-script */ +/* eslint no-unused-vars: ["error", {args: "none"}] */ /* global sendAsyncMessage */ ChromeUtils.import("resource://gre/modules/Services.jsm"); ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm"); -ChromeUtils.defineModuleGetter(this, "ReaderMode", - "resource://gre/modules/ReaderMode.jsm"); ChromeUtils.defineModuleGetter(this, "BrowserUtils", "resource://gre/modules/BrowserUtils.jsm"); ChromeUtils.defineModuleGetter(this, "SelectContentHelper", "resource://gre/modules/SelectContentHelper.jsm"); ChromeUtils.defineModuleGetter(this, "FindContent", "resource://gre/modules/FindContent.jsm"); +ChromeUtils.defineModuleGetter(this, "PrintingContent", + "resource://gre/modules/PrintingContent.jsm"); ChromeUtils.defineModuleGetter(this, "RemoteFinder", "resource://gre/modules/RemoteFinder.jsm"); @@ -484,22 +485,7 @@ var PopupBlocking = { }; PopupBlocking.init(); -XPCOMUtils.defineLazyGetter(this, "console", () => { - // Set up console.* for frame scripts. - let Console = ChromeUtils.import("resource://gre/modules/Console.jsm", {}); - return new Console.ConsoleAPI(); -}); - var Printing = { - // Bug 1088061: nsPrintJob's DoCommonPrint currently expects the - // progress listener passed to it to QI to an nsIPrintingPromptService - // in order to know that a printing progress dialog has been shown. That's - // really all the interface is used for, hence the fact that I don't actually - // implement the interface here. Bug 1088061 has been filed to remove - // this hackery. - QueryInterface: ChromeUtils.generateQI([Ci.nsIWebProgressListener, - Ci.nsIPrintingPromptService]), - MESSAGES: [ "Printing:Preview:Enter", "Printing:Preview:Exit", @@ -514,394 +500,13 @@ var Printing = { addEventListener("printPreviewUpdate", this, true); }, - get shouldSavePrintSettings() { - return Services.prefs.getBoolPref("print.use_global_printsettings") && - Services.prefs.getBoolPref("print.save_print_settings"); - }, - - printPreviewInitializingInfo: null, - handleEvent(event) { - switch (event.type) { - case "PrintingError": { - let win = event.target.defaultView; - let wbp = win.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebBrowserPrint); - let nsresult = event.detail; - sendAsyncMessage("Printing:Error", { - isPrinting: wbp.doingPrint, - nsresult, - }); - break; - } - - case "printPreviewUpdate": { - let info = this.printPreviewInitializingInfo; - if (!info) { - // If there is no printPreviewInitializingInfo then we did not - // initiate the preview so ignore this event. - return; - } - - // Only send Printing:Preview:Entered message on first update, indicated - // by printPreviewInitializingInfo.entered not being set. - if (!info.entered) { - info.entered = true; - sendAsyncMessage("Printing:Preview:Entered", { - failed: false, - changingBrowsers: info.changingBrowsers - }); - - // If we have another request waiting, dispatch it now. - if (info.nextRequest) { - Services.tm.dispatchToMainThread(info.nextRequest); - } - } - - // Always send page count update. - this.updatePageCount(); - break; - } - } + return PrintingContent.handleEvent(global, event); }, receiveMessage(message) { - let data = message.data; - switch (message.name) { - case "Printing:Preview:Enter": { - this.enterPrintPreview(Services.wm.getOuterWindowWithId(data.windowID), data.simplifiedMode, data.changingBrowsers, data.defaultPrinterName); - break; - } - - case "Printing:Preview:Exit": { - this.exitPrintPreview(); - break; - } - - case "Printing:Preview:Navigate": { - this.navigate(data.navType, data.pageNum); - break; - } - - case "Printing:Preview:ParseDocument": { - this.parseDocument(data.URL, Services.wm.getOuterWindowWithId(data.windowID)); - break; - } - - case "Printing:Print": { - this.print(Services.wm.getOuterWindowWithId(data.windowID), data.simplifiedMode, data.defaultPrinterName); - break; - } - } + return PrintingContent.receiveMessage(global, message); }, - - getPrintSettings(defaultPrinterName) { - try { - let PSSVC = Cc["@mozilla.org/gfx/printsettings-service;1"] - .getService(Ci.nsIPrintSettingsService); - - let printSettings = PSSVC.globalPrintSettings; - if (!printSettings.printerName) { - printSettings.printerName = defaultPrinterName; - } - // First get any defaults from the printer - PSSVC.initPrintSettingsFromPrinter(printSettings.printerName, - printSettings); - // now augment them with any values from last time - PSSVC.initPrintSettingsFromPrefs(printSettings, true, - printSettings.kInitSaveAll); - - return printSettings; - } catch (e) { - Cu.reportError(e); - } - - return null; - }, - - parseDocument(URL, contentWindow) { - // By using ReaderMode primitives, we parse given document and place the - // resulting JS object into the DOM of current browser. - let articlePromise = ReaderMode.parseDocument(contentWindow.document).catch(Cu.reportError); - articlePromise.then(function(article) { - // 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 webProgressListener = { - onStateChange(webProgress, req, flags, status) { - if (flags & Ci.nsIWebProgressListener.STATE_STOP) { - webProgress.removeProgressListener(webProgressListener); - let domUtils = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils); - // 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() { - removeEventListener("MozAfterPaint", onPaint); - sendAsyncMessage("Printing:Preview:ReaderModeReady"); - }; - contentWindow.addEventListener("MozAfterPaint", onPaint); - // This timer need when display list invalidation doesn't invalidate. - setTimeout(() => { - removeEventListener("MozAfterPaint", onPaint); - sendAsyncMessage("Printing:Preview:ReaderModeReady"); - }, 100); - } else { - sendAsyncMessage("Printing:Preview:ReaderModeReady"); - } - } - }, - - QueryInterface: ChromeUtils.generateQI([ - Ci.nsIWebProgressListener, - Ci.nsISupportsWeakReference, - Ci.nsIObserver, - ]), - }; - - // Here we QI the docShell into a nsIWebProgress passing our web progress listener in. - let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebProgress); - webProgress.addProgressListener(webProgressListener, Ci.nsIWebProgress.NOTIFY_STATE_REQUEST); - - content.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 = content.document.createElement("base"); - headBaseElement.setAttribute("href", URL); - content.document.head.appendChild(headBaseElement); - - // Create link element referencing aboutReader.css and append it to head - let headStyleElement = content.document.createElement("link"); - headStyleElement.setAttribute("rel", "stylesheet"); - headStyleElement.setAttribute("href", "chrome://global/skin/aboutReader.css"); - headStyleElement.setAttribute("type", "text/css"); - content.document.head.appendChild(headStyleElement); - - // Create link element referencing simplifyMode.css and append it to head - headStyleElement = content.document.createElement("link"); - headStyleElement.setAttribute("rel", "stylesheet"); - headStyleElement.setAttribute("href", "chrome://global/content/simplifyMode.css"); - headStyleElement.setAttribute("type", "text/css"); - content.document.head.appendChild(headStyleElement); - - content.document.body.innerHTML = ""; - - // Create container div (main element) and append it to body - let containerElement = content.document.createElement("div"); - containerElement.setAttribute("id", "container"); - content.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 - content.document.title = article.title; - - // Create header div and append it to container - let headerElement = content.document.createElement("div"); - headerElement.setAttribute("id", "reader-header"); - headerElement.setAttribute("class", "header"); - containerElement.appendChild(headerElement); - - // Jam the article's title and byline into header div - let titleElement = content.document.createElement("h1"); - titleElement.setAttribute("id", "reader-title"); - titleElement.textContent = article.title; - headerElement.appendChild(titleElement); - - let bylineElement = content.document.createElement("div"); - bylineElement.setAttribute("id", "reader-credits"); - bylineElement.setAttribute("class", "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 = content.document.createElement("div"); - contentElement.setAttribute("class", "content"); - containerElement.appendChild(contentElement); - - // Jam the article's content into content div - let readerContent = content.document.createElement("div"); - readerContent.setAttribute("id", "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 { - let aboutReaderStrings = Services.strings.createBundle("chrome://global/locale/aboutReader.properties"); - let errorMessage = aboutReaderStrings.GetStringFromName("aboutReader.loadError"); - - content.document.title = errorMessage; - - // Create reader message div and append it to body - let readerMessageElement = content.document.createElement("div"); - readerMessageElement.setAttribute("class", "reader-message"); - readerMessageElement.textContent = errorMessage; - containerElement.appendChild(readerMessageElement); - - // Display reader message element - readerMessageElement.style.display = "block"; - } - }); - }, - - enterPrintPreview(contentWindow, simplifiedMode, changingBrowsers, defaultPrinterName) { - try { - let printSettings = this.getPrintSettings(defaultPrinterName); - - // If we happen to be on simplified mode, we need to set docURL in order - // to generate header/footer content correctly, since simplified tab has - // "about:blank" as its URI. - if (printSettings && simplifiedMode) - printSettings.docURL = contentWindow.document.baseURI; - - // The print preview docshell will be in a different TabGroup, so - // printPreviewInitialize must be run in a separate runnable to avoid - // touching a different TabGroup in our own runnable. - let printPreviewInitialize = () => { - try { - this.printPreviewInitializingInfo = { changingBrowsers }; - docShell.printPreview.printPreview(printSettings, contentWindow, this); - } catch (error) { - // This might fail if we, for example, attempt to print a XUL document. - // In that case, we inform the parent to bail out of print preview. - Cu.reportError(error); - this.printPreviewInitializingInfo = null; - sendAsyncMessage("Printing:Preview:Entered", { failed: true }); - } - }; - - // If printPreviewInitializingInfo.entered is not set we are still in the - // initial setup of a previous preview request. We delay this one until - // that has finished because running them at the same time will almost - // certainly cause failures. - if (this.printPreviewInitializingInfo && - !this.printPreviewInitializingInfo.entered) { - this.printPreviewInitializingInfo.nextRequest = printPreviewInitialize; - } else { - Services.tm.dispatchToMainThread(printPreviewInitialize); - } - } catch (error) { - // This might fail if we, for example, attempt to print a XUL document. - // In that case, we inform the parent to bail out of print preview. - Cu.reportError(error); - sendAsyncMessage("Printing:Preview:Entered", { failed: true }); - } - }, - - exitPrintPreview() { - this.printPreviewInitializingInfo = null; - docShell.printPreview.exitPrintPreview(); - }, - - print(contentWindow, simplifiedMode, defaultPrinterName) { - let printSettings = this.getPrintSettings(defaultPrinterName); - - // If we happen to be on simplified mode, we need to set docURL in order - // to generate header/footer content correctly, since simplified tab has - // "about:blank" as its URI. - if (printSettings && simplifiedMode) { - printSettings.docURL = contentWindow.document.baseURI; - } - - try { - let print = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebBrowserPrint); - - if (print.doingPrintPreview) { - this.logKeyedTelemetry("PRINT_DIALOG_OPENED_COUNT", "FROM_PREVIEW"); - } else { - this.logKeyedTelemetry("PRINT_DIALOG_OPENED_COUNT", "FROM_PAGE"); - } - - print.print(printSettings, null); - - if (print.doingPrintPreview) { - if (simplifiedMode) { - this.logKeyedTelemetry("PRINT_COUNT", "SIMPLIFIED"); - } else { - this.logKeyedTelemetry("PRINT_COUNT", "WITH_PREVIEW"); - } - } else { - this.logKeyedTelemetry("PRINT_COUNT", "WITHOUT_PREVIEW"); - } - } catch (e) { - // Pressing cancel is expressed as an NS_ERROR_ABORT return value, - // causing an exception to be thrown which we catch here. - if (e.result != Cr.NS_ERROR_ABORT) { - Cu.reportError(`In Printing:Print:Done handler, got unexpected rv - ${e.result}.`); - sendAsyncMessage("Printing:Error", { - isPrinting: true, - nsresult: e.result, - }); - } - } - - if (this.shouldSavePrintSettings) { - let PSSVC = Cc["@mozilla.org/gfx/printsettings-service;1"] - .getService(Ci.nsIPrintSettingsService); - - PSSVC.savePrintSettingsToPrefs(printSettings, true, - printSettings.kInitSaveAll); - PSSVC.savePrintSettingsToPrefs(printSettings, false, - printSettings.kInitSavePrinterName); - } - }, - - logKeyedTelemetry(id, key) { - let histogram = Services.telemetry.getKeyedHistogramById(id); - histogram.add(key); - }, - - updatePageCount() { - let numPages = docShell.printPreview.printPreviewNumPages; - sendAsyncMessage("Printing:Preview:UpdatePageCount", { - numPages, - }); - }, - - navigate(navType, pageNum) { - docShell.printPreview.printPreviewNavigate(navType, pageNum); - }, - - /* nsIWebProgressListener for print preview */ - - onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) { - sendAsyncMessage("Printing:Preview:StateChange", { - stateFlags: aStateFlags, - status: aStatus, - }); - }, - - onProgressChange(aWebProgress, aRequest, aCurSelfProgress, - aMaxSelfProgress, aCurTotalProgress, - aMaxTotalProgress) { - sendAsyncMessage("Printing:Preview:ProgressChange", { - curSelfProgress: aCurSelfProgress, - maxSelfProgress: aMaxSelfProgress, - curTotalProgress: aCurTotalProgress, - maxTotalProgress: aMaxTotalProgress, - }); - }, - - onLocationChange(aWebProgress, aRequest, aLocation, aFlags) {}, - onStatusChange(aWebProgress, aRequest, aStatus, aMessage) {}, - onSecurityChange(aWebProgress, aRequest, aState) {}, }; Printing.init(); diff --git a/toolkit/modules/PrintingContent.jsm b/toolkit/modules/PrintingContent.jsm new file mode 100644 index 000000000000..f6c8fe72c142 --- /dev/null +++ b/toolkit/modules/PrintingContent.jsm @@ -0,0 +1,428 @@ +/* -*- 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/. */ + +var EXPORTED_SYMBOLS = ["PrintingContent"]; + +ChromeUtils.import("resource://gre/modules/Services.jsm"); +ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm"); + +ChromeUtils.defineModuleGetter(this, "ReaderMode", + "resource://gre/modules/ReaderMode.jsm"); + +var PrintingContent = { + // Bug 1088061: nsPrintJob's DoCommonPrint currently expects the + // progress listener passed to it to QI to an nsIPrintingPromptService + // in order to know that a printing progress dialog has been shown. That's + // really all the interface is used for, hence the fact that I don't actually + // implement the interface here. Bug 1088061 has been filed to remove + // this hackery. + QueryInterface: ChromeUtils.generateQI([Ci.nsIPrintingPromptService]), + + get shouldSavePrintSettings() { + return Services.prefs.getBoolPref("print.use_global_printsettings") && + Services.prefs.getBoolPref("print.save_print_settings"); + }, + + printPreviewInitializingInfo: null, + + handleEvent(global, event) { + switch (event.type) { + case "PrintingError": { + let win = event.target.defaultView; + let wbp = win.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebBrowserPrint); + let nsresult = event.detail; + global.sendAsyncMessage("Printing:Error", { + isPrinting: wbp.doingPrint, + nsresult, + }); + break; + } + + case "printPreviewUpdate": { + let info = this.printPreviewInitializingInfo; + if (!info) { + // If there is no printPreviewInitializingInfo then we did not + // initiate the preview so ignore this event. + return; + } + + // Only send Printing:Preview:Entered message on first update, indicated + // by printPreviewInitializingInfo.entered not being set. + if (!info.entered) { + info.entered = true; + global.sendAsyncMessage("Printing:Preview:Entered", { + failed: false, + changingBrowsers: info.changingBrowsers + }); + + // If we have another request waiting, dispatch it now. + if (info.nextRequest) { + Services.tm.dispatchToMainThread(info.nextRequest); + } + } + + // Always send page count update. + this.updatePageCount(global); + break; + } + } + }, + + receiveMessage(global, message) { + let data = message.data; + switch (message.name) { + case "Printing:Preview:Enter": { + this.enterPrintPreview(global, + Services.wm.getOuterWindowWithId(data.windowID), + data.simplifiedMode, + data.changingBrowsers, + data.defaultPrinterName); + break; + } + + case "Printing:Preview:Exit": { + this.exitPrintPreview(global); + break; + } + + case "Printing:Preview:Navigate": { + this.navigate(global, data.navType, data.pageNum); + break; + } + + case "Printing:Preview:ParseDocument": { + this.parseDocument(global, data.URL, Services.wm.getOuterWindowWithId(data.windowID)); + break; + } + + case "Printing:Print": { + this.print(global, + Services.wm.getOuterWindowWithId(data.windowID), + data.simplifiedMode, + data.defaultPrinterName); + break; + } + } + }, + + getPrintSettings(defaultPrinterName) { + try { + let PSSVC = Cc["@mozilla.org/gfx/printsettings-service;1"] + .getService(Ci.nsIPrintSettingsService); + + let printSettings = PSSVC.globalPrintSettings; + if (!printSettings.printerName) { + printSettings.printerName = defaultPrinterName; + } + // First get any defaults from the printer + PSSVC.initPrintSettingsFromPrinter(printSettings.printerName, + printSettings); + // now augment them with any values from last time + PSSVC.initPrintSettingsFromPrefs(printSettings, true, + printSettings.kInitSaveAll); + + return printSettings; + } catch (e) { + Cu.reportError(e); + } + + return null; + }, + + parseDocument(global, URL, contentWindow) { + // By using ReaderMode primitives, we parse given document and place the + // resulting JS object into the DOM of current browser. + let articlePromise = ReaderMode.parseDocument(contentWindow.document).catch(Cu.reportError); + articlePromise.then(function(article) { + // 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 webProgressListener = { + onStateChange(webProgress, req, flags, status) { + if (flags & Ci.nsIWebProgressListener.STATE_STOP) { + webProgress.removeProgressListener(webProgressListener); + let domUtils = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils); + // 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() { + global.removeEventListener("MozAfterPaint", onPaint); + global.sendAsyncMessage("Printing:Preview:ReaderModeReady"); + }; + contentWindow.addEventListener("MozAfterPaint", onPaint); + // This timer need when display list invalidation doesn't invalidate. + global.setTimeout(() => { + global.removeEventListener("MozAfterPaint", onPaint); + global.sendAsyncMessage("Printing:Preview:ReaderModeReady"); + }, 100); + } else { + global.sendAsyncMessage("Printing:Preview:ReaderModeReady"); + } + } + }, + + QueryInterface: ChromeUtils.generateQI([ + Ci.nsIWebProgressListener, + Ci.nsISupportsWeakReference, + Ci.nsIObserver, + ]), + }; + + const {content, docShell} = global; + + // Here we QI the docShell into a nsIWebProgress passing our web progress listener in. + let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebProgress); + webProgress.addProgressListener(webProgressListener, Ci.nsIWebProgress.NOTIFY_STATE_REQUEST); + + content.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 = content.document.createElement("base"); + headBaseElement.setAttribute("href", URL); + content.document.head.appendChild(headBaseElement); + + // Create link element referencing aboutReader.css and append it to head + let headStyleElement = content.document.createElement("link"); + headStyleElement.setAttribute("rel", "stylesheet"); + headStyleElement.setAttribute("href", "chrome://global/skin/aboutReader.css"); + headStyleElement.setAttribute("type", "text/css"); + content.document.head.appendChild(headStyleElement); + + // Create link element referencing simplifyMode.css and append it to head + headStyleElement = content.document.createElement("link"); + headStyleElement.setAttribute("rel", "stylesheet"); + headStyleElement.setAttribute("href", "chrome://global/content/simplifyMode.css"); + headStyleElement.setAttribute("type", "text/css"); + content.document.head.appendChild(headStyleElement); + + content.document.body.innerHTML = ""; + + // Create container div (main element) and append it to body + let containerElement = content.document.createElement("div"); + containerElement.setAttribute("id", "container"); + content.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 + content.document.title = article.title; + + // Create header div and append it to container + let headerElement = content.document.createElement("div"); + headerElement.setAttribute("id", "reader-header"); + headerElement.setAttribute("class", "header"); + containerElement.appendChild(headerElement); + + // Jam the article's title and byline into header div + let titleElement = content.document.createElement("h1"); + titleElement.setAttribute("id", "reader-title"); + titleElement.textContent = article.title; + headerElement.appendChild(titleElement); + + let bylineElement = content.document.createElement("div"); + bylineElement.setAttribute("id", "reader-credits"); + bylineElement.setAttribute("class", "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 = content.document.createElement("div"); + contentElement.setAttribute("class", "content"); + containerElement.appendChild(contentElement); + + // Jam the article's content into content div + let readerContent = content.document.createElement("div"); + readerContent.setAttribute("id", "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 { + let aboutReaderStrings = Services.strings.createBundle("chrome://global/locale/aboutReader.properties"); + let errorMessage = aboutReaderStrings.GetStringFromName("aboutReader.loadError"); + + content.document.title = errorMessage; + + // Create reader message div and append it to body + let readerMessageElement = content.document.createElement("div"); + readerMessageElement.setAttribute("class", "reader-message"); + readerMessageElement.textContent = errorMessage; + containerElement.appendChild(readerMessageElement); + + // Display reader message element + readerMessageElement.style.display = "block"; + } + }); + }, + + enterPrintPreview(global, contentWindow, simplifiedMode, changingBrowsers, defaultPrinterName) { + const {docShell} = global; + try { + let printSettings = this.getPrintSettings(defaultPrinterName); + + // If we happen to be on simplified mode, we need to set docURL in order + // to generate header/footer content correctly, since simplified tab has + // "about:blank" as its URI. + if (printSettings && simplifiedMode) + printSettings.docURL = contentWindow.document.baseURI; + + // The print preview docshell will be in a different TabGroup, so + // printPreviewInitialize must be run in a separate runnable to avoid + // touching a different TabGroup in our own runnable. + let printPreviewInitialize = () => { + try { + let listener = new PrintingListener(global); + + this.printPreviewInitializingInfo = { changingBrowsers }; + docShell.printPreview.printPreview(printSettings, contentWindow, listener); + } catch (error) { + // This might fail if we, for example, attempt to print a XUL document. + // In that case, we inform the parent to bail out of print preview. + Cu.reportError(error); + this.printPreviewInitializingInfo = null; + global.sendAsyncMessage("Printing:Preview:Entered", { failed: true }); + } + }; + + // If printPreviewInitializingInfo.entered is not set we are still in the + // initial setup of a previous preview request. We delay this one until + // that has finished because running them at the same time will almost + // certainly cause failures. + if (this.printPreviewInitializingInfo && + !this.printPreviewInitializingInfo.entered) { + this.printPreviewInitializingInfo.nextRequest = printPreviewInitialize; + } else { + Services.tm.dispatchToMainThread(printPreviewInitialize); + } + } catch (error) { + // This might fail if we, for example, attempt to print a XUL document. + // In that case, we inform the parent to bail out of print preview. + Cu.reportError(error); + global.sendAsyncMessage("Printing:Preview:Entered", { failed: true }); + } + }, + + exitPrintPreview(global) { + this.printPreviewInitializingInfo = null; + global.docShell.printPreview.exitPrintPreview(); + }, + + print(global, contentWindow, simplifiedMode, defaultPrinterName) { + let printSettings = this.getPrintSettings(defaultPrinterName); + + // If we happen to be on simplified mode, we need to set docURL in order + // to generate header/footer content correctly, since simplified tab has + // "about:blank" as its URI. + if (printSettings && simplifiedMode) { + printSettings.docURL = contentWindow.document.baseURI; + } + + try { + let print = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebBrowserPrint); + + if (print.doingPrintPreview) { + this.logKeyedTelemetry("PRINT_DIALOG_OPENED_COUNT", "FROM_PREVIEW"); + } else { + this.logKeyedTelemetry("PRINT_DIALOG_OPENED_COUNT", "FROM_PAGE"); + } + + print.print(printSettings, null); + + if (print.doingPrintPreview) { + if (simplifiedMode) { + this.logKeyedTelemetry("PRINT_COUNT", "SIMPLIFIED"); + } else { + this.logKeyedTelemetry("PRINT_COUNT", "WITH_PREVIEW"); + } + } else { + this.logKeyedTelemetry("PRINT_COUNT", "WITHOUT_PREVIEW"); + } + } catch (e) { + // Pressing cancel is expressed as an NS_ERROR_ABORT return value, + // causing an exception to be thrown which we catch here. + if (e.result != Cr.NS_ERROR_ABORT) { + Cu.reportError(`In Printing:Print:Done handler, got unexpected rv + ${e.result}.`); + global.sendAsyncMessage("Printing:Error", { + isPrinting: true, + nsresult: e.result, + }); + } + } + + if (this.shouldSavePrintSettings) { + let PSSVC = Cc["@mozilla.org/gfx/printsettings-service;1"] + .getService(Ci.nsIPrintSettingsService); + + PSSVC.savePrintSettingsToPrefs(printSettings, true, + printSettings.kInitSaveAll); + PSSVC.savePrintSettingsToPrefs(printSettings, false, + printSettings.kInitSavePrinterName); + } + }, + + logKeyedTelemetry(id, key) { + let histogram = Services.telemetry.getKeyedHistogramById(id); + histogram.add(key); + }, + + updatePageCount(global) { + let numPages = global.docShell.printPreview.printPreviewNumPages; + global.sendAsyncMessage("Printing:Preview:UpdatePageCount", { + numPages, + }); + }, + + navigate(global, navType, pageNum) { + global.docShell.printPreview.printPreviewNavigate(navType, pageNum); + }, +}; + +function PrintingListener(global) { + this.global = global; +} +PrintingListener.prototype = { + QueryInterface: ChromeUtils.generateQI([Ci.nsIWebProgressListener]), + + onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) { + this.global.sendAsyncMessage("Printing:Preview:StateChange", { + stateFlags: aStateFlags, + status: aStatus, + }); + }, + + onProgressChange(aWebProgress, aRequest, aCurSelfProgress, + aMaxSelfProgress, aCurTotalProgress, + aMaxTotalProgress) { + this.global.sendAsyncMessage("Printing:Preview:ProgressChange", { + curSelfProgress: aCurSelfProgress, + maxSelfProgress: aMaxSelfProgress, + curTotalProgress: aCurTotalProgress, + maxTotalProgress: aMaxTotalProgress, + }); + }, + + onLocationChange(aWebProgress, aRequest, aLocation, aFlags) {}, + onStatusChange(aWebProgress, aRequest, aStatus, aMessage) {}, + onSecurityChange(aWebProgress, aRequest, aState) {}, +}; diff --git a/toolkit/modules/moz.build b/toolkit/modules/moz.build index 6c9af5908b0e..c6ce392ee4e1 100644 --- a/toolkit/modules/moz.build +++ b/toolkit/modules/moz.build @@ -222,6 +222,7 @@ EXTRA_JS_MODULES += [ 'PermissionsUtils.jsm', 'PopupNotifications.jsm', 'Preferences.jsm', + 'PrintingContent.jsm', 'PrivateBrowsingUtils.jsm', 'ProfileAge.jsm', 'Promise-backend.js',