зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1464552: Part 3 - Split print preview helpers into separate JSM. r=felipe
MozReview-Commit-ID: 59Z0fZIf7Ym --HG-- rename : toolkit/content/browser-content.js => toolkit/modules/PrintingContent.jsm extra : rebase_source : f808b33b62efede347284952e46cd1564ddda296
This commit is contained in:
Родитель
fafa2903e4
Коммит
f28199fcc4
|
@ -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();
|
||||
|
||||
|
|
|
@ -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) {},
|
||||
};
|
|
@ -222,6 +222,7 @@ EXTRA_JS_MODULES += [
|
|||
'PermissionsUtils.jsm',
|
||||
'PopupNotifications.jsm',
|
||||
'Preferences.jsm',
|
||||
'PrintingContent.jsm',
|
||||
'PrivateBrowsingUtils.jsm',
|
||||
'ProfileAge.jsm',
|
||||
'Promise-backend.js',
|
||||
|
|
Загрузка…
Ссылка в новой задаче