Bug 1669369, remove legacy actor from PrintingChild and replace with JSWindowActor, r=mstriemer

Differential Revision: https://phabricator.services.mozilla.com/D92519
This commit is contained in:
Neil Deakin 2020-10-28 19:19:52 +00:00
Родитель c75bed1584
Коммит 8773757bb3
15 изменённых файлов: 576 добавлений и 548 удалений

Просмотреть файл

@ -60,10 +60,18 @@ add_task(async function test() {
// Enter print preview
let ppBrowser = PrintPreviewListener.getPrintPreviewBrowser();
let printPreviewEntered = BrowserTestUtils.waitForMessage(
ppBrowser.messageManager,
"Printing:Preview:Entered"
const { PrintingParent } = ChromeUtils.import(
"resource://gre/actors/PrintingParent.jsm"
);
let printPreviewEntered = new Promise(resolve => {
PrintingParent.setTestListener(browserPreviewing => {
if (browserPreviewing == ppBrowser) {
PrintingParent.setTestListener(null);
resolve();
}
});
});
document.getElementById("cmd_printPreview").doCommand();
await printPreviewEntered;

Просмотреть файл

@ -6,9 +6,6 @@
var EXPORTED_SYMBOLS = ["PrintingChild"];
const { ActorChild } = ChromeUtils.import(
"resource://gre/modules/ActorChild.jsm"
);
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.defineModuleGetter(
@ -23,7 +20,20 @@ ChromeUtils.defineModuleGetter(
"resource://gre/modules/ReaderMode.jsm"
);
class PrintingChild extends ActorChild {
let gPrintPreviewInitializingInfo = null;
let gPendingPreviewsMap = new Map();
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;
}
}
// 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
@ -41,7 +51,7 @@ class PrintingChild extends ActorChild {
let win = event.target.defaultView;
let wbp = win.getInterface(Ci.nsIWebBrowserPrint);
let nsresult = event.detail;
this.mm.sendAsyncMessage("Printing:Error", {
this.sendAsyncMessage("Printing:Error", {
isPrinting: wbp.doingPrint,
nsresult,
});
@ -49,18 +59,20 @@ class PrintingChild extends ActorChild {
}
case "printPreviewUpdate": {
let info = this.printPreviewInitializingInfo;
let info = gPrintPreviewInitializingInfo;
if (!info) {
// If there is no printPreviewInitializingInfo then we did not
// If there is no gPrintPreviewInitializingInfo 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.
// by gPrintPreviewInitializingInfo.entered not being set.
if (!info.entered) {
gPendingPreviewsMap.delete(this.browsingContext.id);
info.entered = true;
this.mm.sendAsyncMessage("Printing:Preview:Entered", {
this.sendAsyncMessage("Printing:Preview:Entered", {
failed: false,
changingBrowsers: info.changingBrowsers,
});
@ -72,7 +84,7 @@ class PrintingChild extends ActorChild {
}
// Always send page count update.
this.updatePageCount(this.mm);
this.updatePageCount();
break;
}
}
@ -106,13 +118,14 @@ class PrintingChild extends ActorChild {
}
case "Printing:Preview:ParseDocument": {
this.parseDocument(
return this.parseDocument(
data.URL,
Services.wm.getOuterWindowWithId(data.windowID)
);
break;
}
}
return undefined;
}
getPrintSettings(lastUsedPrinterName) {
@ -145,169 +158,174 @@ class PrintingChild extends ActorChild {
return null;
}
parseDocument(URL, contentWindow) {
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 articlePromise = ReaderMode.parseDocument(contentWindow.document).catch(
Cu.reportError
);
articlePromise.then(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 { mm } = this;
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() {
mm.removeEventListener("MozAfterPaint", onPaint);
mm.sendAsyncMessage("Printing:Preview:ReaderModeReady");
};
contentWindow.addEventListener("MozAfterPaint", onPaint);
// This timer need when display list invalidation doesn't invalidate.
setTimeout(() => {
mm.removeEventListener("MozAfterPaint", onPaint);
mm.sendAsyncMessage("Printing:Preview:ReaderModeReady");
}, 100);
} else {
mm.sendAsyncMessage("Printing:Preview:ReaderModeReady");
}
let article;
try {
article = await ReaderMode.parseDocument(contentWindow.document);
} catch (ex) {
Cu.reportError(ex);
}
// 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");
};
contentWindow.addEventListener("MozAfterPaint", onPaint);
// This timer is needed for when display list invalidation doesn't invalidate.
setTimeout(() => {
contentWindow.removeEventListener("MozAfterPaint", onPaint);
actor.sendAsyncMessage("Printing:Preview:ReaderModeReady");
}, 100);
} else {
actor.sendAsyncMessage("Printing:Preview:ReaderModeReady");
}
},
}
},
QueryInterface: ChromeUtils.generateQI([
"nsIWebProgressListener",
"nsISupportsWeakReference",
"nsIObserver",
]),
};
QueryInterface: ChromeUtils.generateQI([
"nsIWebProgressListener",
"nsISupportsWeakReference",
"nsIObserver",
]),
};
const { content, docShell } = this.mm;
// 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
);
// 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
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("id", "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("id", "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("id", "reader-title");
titleElement.textContent = article.title;
headerElement.appendChild(titleElement);
let bylineElement = 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 = 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("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
);
content.document.head.innerHTML = "";
readerContent.appendChild(contentFragment);
// 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"
// Display reader content element
readerContent.style.display = "block";
} else {
let aboutReaderStrings = Services.strings.createBundle(
"chrome://global/locale/aboutReader.properties"
);
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"
let errorMessage = aboutReaderStrings.GetStringFromName(
"aboutReader.loadError"
);
headStyleElement.setAttribute("type", "text/css");
content.document.head.appendChild(headStyleElement);
content.document.body.innerHTML = "";
document.title = errorMessage;
// 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);
// 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);
// 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";
}
});
// Display reader message element
readerMessageElement.style.display = "block";
}
}
enterPrintPreview(
@ -349,6 +367,9 @@ class PrintingChild extends ActorChild {
printSettings.docURL = contentWindow.document.baseURI;
}
// Get this early in case the actor goes away during print preview.
let browserContextId = this.browsingContext.id;
// 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.
@ -357,37 +378,39 @@ class PrintingChild extends ActorChild {
// might be destroyed, for example the print preview window gets closed
// soon after it's opened, in such case we should just simply bail out.
if (docShell.isBeingDestroyed()) {
this.mm.sendAsyncMessage("Printing:Preview:Entered", {
this.sendAsyncMessage("Printing:Preview:Entered", {
failed: true,
});
return;
}
try {
let listener = new PrintingListener(this.mm);
let listener = new PrintingListener(this);
gPendingPreviewsMap.set(browserContextId, listener);
gPrintPreviewInitializingInfo = { changingBrowsers };
this.printPreviewInitializingInfo = { changingBrowsers };
contentWindow.printPreview(printSettings, listener, docShell);
} 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;
this.mm.sendAsyncMessage("Printing:Preview:Entered", {
gPrintPreviewInitializingInfo = null;
this.sendAsyncMessage("Printing:Preview:Entered", {
failed: true,
});
}
};
// If printPreviewInitializingInfo.entered is not set we are still in the
// If gPrintPreviewInitializingInfo.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
gPrintPreviewInitializingInfo &&
!gPrintPreviewInitializingInfo.entered
) {
this.printPreviewInitializingInfo.nextRequest = printPreviewInitialize;
gPrintPreviewInitializingInfo.nextRequest = printPreviewInitialize;
} else {
Services.tm.dispatchToMainThread(printPreviewInitialize);
}
@ -395,19 +418,21 @@ class PrintingChild extends ActorChild {
// 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.mm.sendAsyncMessage("Printing:Preview:Entered", { failed: true });
this.sendAsyncMessage("Printing:Preview:Entered", {
failed: true,
});
}
}
exitPrintPreview(glo) {
this.printPreviewInitializingInfo = null;
exitPrintPreview() {
gPrintPreviewInitializingInfo = null;
this.docShell.exitPrintPreview();
}
updatePageCount() {
let cv = this.docShell.contentViewer;
cv.QueryInterface(Ci.nsIWebBrowserPrint);
this.mm.sendAsyncMessage("Printing:Preview:UpdatePageCount", {
this.sendAsyncMessage("Printing:Preview:UpdatePageCount", {
numPages: cv.printPreviewNumPages,
totalPages: cv.rawNumPages,
});
@ -424,14 +449,14 @@ PrintingChild.prototype.QueryInterface = ChromeUtils.generateQI([
"nsIPrintingPromptService",
]);
function PrintingListener(global) {
this.global = global;
function PrintingListener(actor) {
this.actor = actor;
}
PrintingListener.prototype = {
QueryInterface: ChromeUtils.generateQI(["nsIWebProgressListener"]),
onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
this.global.sendAsyncMessage("Printing:Preview:StateChange", {
this.actor.sendAsyncMessage("Printing:Preview:StateChange", {
stateFlags: aStateFlags,
status: aStatus,
});
@ -445,7 +470,7 @@ PrintingListener.prototype = {
aCurTotalProgress,
aMaxTotalProgress
) {
this.global.sendAsyncMessage("Printing:Preview:ProgressChange", {
this.actor.sendAsyncMessage("Printing:Preview:ProgressChange", {
curSelfProgress: aCurSelfProgress,
maxSelfProgress: aMaxSelfProgress,
curTotalProgress: aCurTotalProgress,

Просмотреть файл

@ -0,0 +1,108 @@
/* 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/. */
"use strict";
var EXPORTED_SYMBOLS = ["PrintingParent"];
let gTestListener = null;
class PrintingParent extends JSWindowActorParent {
static setTestListener(listener) {
gTestListener = listener;
}
getPrintPreviewToolbar(browser) {
return browser.ownerDocument.getElementById("print-preview-toolbar");
}
receiveMessage(message) {
let browser = this.browsingContext.top.embedderElement;
let PrintUtils = browser.ownerGlobal.PrintUtils;
if (message.name == "Printing:Error") {
PrintUtils._displayPrintingError(
message.data.nsresult,
message.data.isPrinting
);
return undefined;
}
if (this.ignoreListeners) {
return undefined;
}
let listener = PrintUtils._webProgressPP.value;
let data = message.data;
switch (message.name) {
case "Printing:Preview:Entered": {
// 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
// to complete before switching the chrome UI to print preview mode,
// otherwise we have layout issues.
if (gTestListener) {
gTestListener(browser);
}
PrintUtils.printPreviewEntered(browser, message.data);
break;
}
case "Printing:Preview:ReaderModeReady": {
PrintUtils.readerModeReady(browser);
break;
}
case "Printing:Preview:UpdatePageCount": {
let toolbar = this.getPrintPreviewToolbar(browser);
toolbar.updatePageCount(message.data.totalPages);
break;
}
case "Printing:Preview:ProgressChange": {
if (!PrintUtils._webProgressPP.value) {
// We somehow didn't get a nsIWebProgressListener to be updated...
// I guess there's nothing to do.
return undefined;
}
return listener.onProgressChange(
null,
null,
data.curSelfProgress,
data.maxSelfProgress,
data.curTotalProgress,
data.maxTotalProgress
);
}
case "Printing:Preview:StateChange": {
if (!PrintUtils._webProgressPP.value) {
// We somehow didn't get a nsIWebProgressListener to be updated...
// I guess there's nothing to do.
return undefined;
}
if (data.stateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
// Strangely, the printing engine sends 2 STATE_STOP messages when
// print preview is finishing. One has the STATE_IS_DOCUMENT flag,
// the other has the STATE_IS_NETWORK flag. However, the webProgressPP
// listener stops listening once the first STATE_STOP is sent.
// Any subsequent messages result in NS_ERROR_FAILURE errors getting
// thrown. This should all get torn out once bug 1088061 is fixed.
// Enable toobar elements that we disabled during update.
let printPreviewTB = this.getPrintPreviewToolbar(browser);
printPreviewTB.disableUpdateTriggers(false);
}
return listener.onStateChange(null, null, data.stateFlags, data.status);
}
}
return undefined;
}
}

Просмотреть файл

@ -52,6 +52,7 @@ FINAL_TARGET_FILES.actors += [
"PopupBlockingChild.jsm",
"PopupBlockingParent.jsm",
"PrintingChild.jsm",
"PrintingParent.jsm",
"PurgeSessionHistoryChild.jsm",
"RemotePageChild.jsm",
"SelectChild.jsm",

Просмотреть файл

@ -125,8 +125,6 @@ customElements.define(
this.mPPBrowser = null;
this.mMessageManager = null;
this.mOnPageTextBoxChange = () => {
this.navigate(0, Number(this.mPageTextBox.value), 0);
};
@ -154,11 +152,6 @@ customElements.define(
this.mSimplifyPageToolbarSeparator.hidden = true;
}
this.mPPBrowser = aPPBrowser;
this.mMessageManager = aPPBrowser.messageManager;
this.mMessageManager.addMessageListener(
"Printing:Preview:UpdatePageCount",
this
);
this.updateToolbar();
let ltr = document.documentElement.matches(":root:-moz-locale-dir(ltr)");
@ -196,14 +189,7 @@ customElements.define(
}
destroy() {
if (this.mMessageManager) {
this.mMessageManager.removeMessageListener(
"Printing:Preview:UpdatePageCount",
this
);
delete this.mMessageManager;
delete this.mPPBrowser;
}
delete this.mPPBrowser;
}
disconnectedCallback() {
@ -276,10 +262,14 @@ customElements.define(
}
}
this.mMessageManager.sendAsyncMessage("Printing:Preview:Navigate", {
navType,
pageNum,
});
this.mPPBrowser.sendMessageToActor(
"Printing:Preview:Navigate",
{
navType,
pageNum,
},
"Printing"
);
}
print() {
@ -445,12 +435,9 @@ customElements.define(
PSSVC.savePrintSettingsToPrefs(settings, true, flags);
}
receiveMessage(message) {
if (message.name == "Printing:Preview:UpdatePageCount") {
let totalPages = message.data.totalPages;
this.mTotalPages.value = totalPages;
this.mPageTextBox.max = totalPages;
}
updatePageCount(totalPages) {
this.mTotalPages.value = totalPages;
this.mPageTextBox.max = totalPages;
}
},
{ extends: "toolbar" }

Просмотреть файл

@ -18,17 +18,11 @@
*
* To compound that, we need to support remote browsers, and that means
* kicking off the print jobs in the content process. This means we send
* messages back and forth to that process. browser-content.js contains
* the object that listens and responds to the messages that PrintUtils
* sends.
* messages back and forth to that process via the Printing actor.
*
* This also means that <xul:browser>'s that hope to use PrintUtils must have
* their type attribute set to "content".
*
* PrintUtils sends messages at different points in its implementation, but
* their documentation is consolidated here for ease-of-access.
*
*
* Messages sent:
*
* Printing:Preview:Enter
@ -39,23 +33,6 @@
*
* Printing:Preview:Exit
* This message is sent to take content out of print preview mode.
*
*
* Messages Received
*
* Printing:Preview:Entered
* 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
* to complete before switching the chrome UI to print preview mode,
* otherwise we have layout issues.
*
* Printing:Preview:StateChange, Printing:Preview:ProgressChange
* Due to a timing issue resulting in a main-process crash, we have to
* manually open the progress dialog for print preview. The progress
* dialog is opened here in PrintUtils, and then we listen for update
* messages from the child. Bug 1088061 has been filed to investigate
* other solutions.
*
*/
XPCOMUtils.defineLazyPreferenceGetter(
@ -78,15 +55,19 @@ ChromeUtils.defineModuleGetter(
"resource://gre/modules/SharedPromptUtils.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"PrintingParent",
"resource://gre/actors/PrintingParent.jsm"
);
var gFocusedElement = null;
var gPendingPrintPreviews = new Map();
var PrintUtils = {
SAVE_TO_PDF_PRINTER: "Mozilla Save to PDF",
init() {
window.messageManager.addMessageListener("Printing:Error", this);
},
get _bundle() {
delete this._bundle;
return (this._bundle = Services.strings.createBundle(
@ -423,6 +404,11 @@ var PrintUtils = {
* with aListenerObj as null iff this window is already displaying
* print preview (in which case, the previous aListenerObj passed
* to it will be used).
*
* Due to a timing issue resulting in a main-process crash, we have to
* manually open the progress dialog for print preview. The progress
* dialog is opened here in PrintUtils, and then we listen for update
* messages from the child. Bug 1558588 is about removing this.
*/
printPreview(aTrigger, aListenerObj) {
if (aTrigger) {
@ -593,61 +579,6 @@ var PrintUtils = {
);
},
receiveMessage(aMessage) {
if (aMessage.name == "Printing:Error") {
this._displayPrintingError(
aMessage.data.nsresult,
aMessage.data.isPrinting
);
return undefined;
}
// If we got here, then the message we've received must involve
// updating the print progress UI.
if (!this._webProgressPP.value) {
// We somehow didn't get a nsIWebProgressListener to be updated...
// I guess there's nothing to do.
return undefined;
}
let listener = this._webProgressPP.value;
let mm = aMessage.target.messageManager;
let data = aMessage.data;
switch (aMessage.name) {
case "Printing:Preview:ProgressChange": {
return listener.onProgressChange(
null,
null,
data.curSelfProgress,
data.maxSelfProgress,
data.curTotalProgress,
data.maxTotalProgress
);
}
case "Printing:Preview:StateChange": {
if (data.stateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
// Strangely, the printing engine sends 2 STATE_STOP messages when
// print preview is finishing. One has the STATE_IS_DOCUMENT flag,
// the other has the STATE_IS_NETWORK flag. However, the webProgressPP
// listener stops listening once the first STATE_STOP is sent.
// Any subsequent messages result in NS_ERROR_FAILURE errors getting
// thrown. This should all get torn out once bug 1088061 is fixed.
mm.removeMessageListener("Printing:Preview:StateChange", this);
mm.removeMessageListener("Printing:Preview:ProgressChange", this);
// Enable toobar elements that we disabled during update.
let printPreviewTB = document.getElementById("print-preview-toolbar");
printPreviewTB.disableUpdateTriggers(false);
}
return listener.onStateChange(null, null, data.stateFlags, data.status);
}
}
return undefined;
},
_setPrinterDefaultsForSelectedPrinter(
aPSSVC,
aPrintSettings,
@ -771,8 +702,180 @@ var PrintUtils = {
oldPPBrowser = this._currentPPBrowser;
}
this._currentPPBrowser = ppBrowser;
let mm = ppBrowser.messageManager;
let waitForPrintProgressToEnableToolbar = false;
if (this._webProgressPP.value) {
waitForPrintProgressToEnableToolbar = true;
}
gPendingPrintPreviews.set(ppBrowser, waitForPrintProgressToEnableToolbar);
// If we happen to have gotten simplify page checked, we will lazily
// instantiate a new tab that parses the original page using ReaderMode
// primitives. When it's ready, and in order to enter on preview, we send
// over a message to print preview browser passing up the simplified tab as
// reference. If not, we pass the original tab instead as content source.
if (this._shouldSimplify) {
let simplifiedBrowser = this._listener.getSimplifiedSourceBrowser();
if (!simplifiedBrowser) {
simplifiedBrowser = this._listener.createSimplifiedBrowser();
// Here, we send down a message to simplified browser in order to parse
// the original page. After we have parsed it, content will tell parent
// that the document is ready for print previewing.
simplifiedBrowser.sendMessageToActor(
"Printing:Preview:ParseDocument",
{
URL: this._originalURL,
windowID: oldPPBrowser.outerWindowID,
},
"Printing"
);
// Here we log telemetry data for when the user enters simplify mode.
this.logTelemetry("PRINT_PREVIEW_SIMPLIFY_PAGE_OPENED_COUNT");
return;
}
}
this.sendEnterPrintPreviewToChild(
ppBrowser,
this._sourceBrowser,
this._shouldSimplify,
changingPrintPreviewBrowsers
);
},
sendEnterPrintPreviewToChild(
ppBrowser,
sourceBrowser,
simplifiedMode,
changingBrowsers
) {
ppBrowser.sendMessageToActor(
"Printing:Preview:Enter",
{
browsingContextId: sourceBrowser.browsingContext.id,
simplifiedMode,
changingBrowsers,
lastUsedPrinterName: this.getLastUsedPrinterName(),
},
"Printing"
);
},
printPreviewEntered(ppBrowser, previewResult) {
let waitForPrintProgressToEnableToolbar = gPendingPrintPreviews.get(
ppBrowser
);
gPendingPrintPreviews.delete(ppBrowser);
for (let { resolve, reject } of this._onEntered) {
if (previewResult.failed) {
reject();
} else {
resolve();
}
}
this._onEntered = [];
if (previewResult.failed) {
// Something went wrong while putting the document into print preview
// mode. Bail out.
this._ppBrowsers.clear();
this._listener.onEnter();
this._listener.onExit();
return;
}
// Stash the focused element so that we can return to it after exiting
// print preview.
gFocusedElement = document.commandDispatcher.focusedElement;
let printPreviewTB = document.getElementById("print-preview-toolbar");
if (printPreviewTB) {
if (previewResult.changingBrowsers) {
printPreviewTB.destroy();
printPreviewTB.initialize(ppBrowser);
} else {
// printPreviewTB.initialize above already calls updateToolbar.
printPreviewTB.updateToolbar();
}
// If we don't have a progress listener to enable the toolbar do it now.
if (!waitForPrintProgressToEnableToolbar) {
printPreviewTB.disableUpdateTriggers(false);
}
ppBrowser.collapsed = false;
ppBrowser.focus();
return;
}
// Set the original window as an active window so any mozPrintCallbacks can
// run without delayed setTimeouts.
if (this._listener.activateBrowser) {
this._listener.activateBrowser(this._sourceBrowser);
} else {
this._sourceBrowser.docShellIsActive = true;
}
// show the toolbar after we go into print preview mode so
// that we can initialize the toolbar with total num pages
printPreviewTB = document.createXULElement("toolbar", {
is: "printpreview-toolbar",
});
printPreviewTB.setAttribute("fullscreentoolbar", true);
printPreviewTB.setAttribute("flex", "1");
printPreviewTB.id = "print-preview-toolbar";
let navToolbox = this._listener.getNavToolbox();
navToolbox.parentNode.insertBefore(printPreviewTB, navToolbox);
printPreviewTB.initialize(ppBrowser);
// The print preview processing may not have fully completed, so if we
// have a progress listener, disable the toolbar elements that can trigger
// updates and it will enable them when completed.
if (waitForPrintProgressToEnableToolbar) {
printPreviewTB.disableUpdateTriggers(true);
}
// Enable simplify page checkbox when the page is an article
if (this._sourceBrowser.isArticle) {
printPreviewTB.enableSimplifyPage();
} else {
this.logTelemetry("PRINT_PREVIEW_SIMPLIFY_PAGE_UNAVAILABLE_COUNT");
printPreviewTB.disableSimplifyPage();
}
// copy the window close handler
if (window.onclose) {
this._closeHandlerPP = window.onclose;
} else {
this._closeHandlerPP = null;
}
window.onclose = function() {
PrintUtils.exitPrintPreview();
return false;
};
// disable chrome shortcuts...
window.addEventListener("keydown", this.onKeyDownPP, true);
window.addEventListener("keypress", this.onKeyPressPP, true);
ppBrowser.collapsed = false;
ppBrowser.focus();
// on Enter PP Call back
this._listener.onEnter();
},
readerModeReady(sourceBrowser) {
let ppBrowser = this._listener.getSimplifiedPrintPreviewBrowser();
this.sendEnterPrintPreviewToChild(ppBrowser, sourceBrowser, true, true);
},
getLastUsedPrinterName() {
let PSSVC = Cc["@mozilla.org/gfx/printsettings-service;1"].getService(
Ci.nsIPrintSettingsService
);
@ -801,171 +904,12 @@ var PrintUtils = {
}
}
let sendEnterPreviewMessage = function(browser, simplified) {
mm.sendAsyncMessage("Printing:Preview:Enter", {
browsingContextId: browser.browsingContext.id,
simplifiedMode: simplified,
changingBrowsers: changingPrintPreviewBrowsers,
lastUsedPrinterName,
});
};
// If we happen to have gotten simplify page checked, we will lazily
// instantiate a new tab that parses the original page using ReaderMode
// primitives. When it's ready, and in order to enter on preview, we send
// over a message to print preview browser passing up the simplified tab as
// reference. If not, we pass the original tab instead as content source.
if (this._shouldSimplify) {
let simplifiedBrowser = this._listener.getSimplifiedSourceBrowser();
if (simplifiedBrowser) {
sendEnterPreviewMessage(simplifiedBrowser, true);
} else {
simplifiedBrowser = this._listener.createSimplifiedBrowser();
// After instantiating the simplified tab, we attach a listener as
// callback. Once we discover reader mode has been loaded, we fire
// up a message to enter on print preview.
let spMM = simplifiedBrowser.messageManager;
spMM.addMessageListener(
"Printing:Preview:ReaderModeReady",
function onReaderReady() {
spMM.removeMessageListener(
"Printing:Preview:ReaderModeReady",
onReaderReady
);
sendEnterPreviewMessage(simplifiedBrowser, true);
}
);
// Here, we send down a message to simplified browser in order to parse
// the original page. After we have parsed it, content will tell parent
// that the document is ready for print previewing.
spMM.sendAsyncMessage("Printing:Preview:ParseDocument", {
URL: this._originalURL,
windowID: oldPPBrowser.outerWindowID,
});
// Here we log telemetry data for when the user enters simplify mode.
this.logTelemetry("PRINT_PREVIEW_SIMPLIFY_PAGE_OPENED_COUNT");
}
} else {
sendEnterPreviewMessage(this._sourceBrowser, false);
}
let waitForPrintProgressToEnableToolbar = false;
if (this._webProgressPP.value) {
mm.addMessageListener("Printing:Preview:StateChange", this);
mm.addMessageListener("Printing:Preview:ProgressChange", this);
waitForPrintProgressToEnableToolbar = true;
}
let onEntered = message => {
mm.removeMessageListener("Printing:Preview:Entered", onEntered);
for (let { resolve, reject } of this._onEntered) {
if (message.data.failed) {
reject();
} else {
resolve();
}
}
this._onEntered = [];
if (message.data.failed) {
// Something went wrong while putting the document into print preview
// mode. Bail out.
this._ppBrowsers.clear();
this._listener.onEnter();
this._listener.onExit();
return;
}
// Stash the focused element so that we can return to it after exiting
// print preview.
gFocusedElement = document.commandDispatcher.focusedElement;
let printPreviewTB = document.getElementById("print-preview-toolbar");
if (printPreviewTB) {
if (message.data.changingBrowsers) {
printPreviewTB.destroy();
printPreviewTB.initialize(ppBrowser);
} else {
// printPreviewTB.initialize above already calls updateToolbar.
printPreviewTB.updateToolbar();
}
// If we don't have a progress listener to enable the toolbar do it now.
if (!waitForPrintProgressToEnableToolbar) {
printPreviewTB.disableUpdateTriggers(false);
}
ppBrowser.collapsed = false;
ppBrowser.focus();
return;
}
// Set the original window as an active window so any mozPrintCallbacks can
// run without delayed setTimeouts.
if (this._listener.activateBrowser) {
this._listener.activateBrowser(this._sourceBrowser);
} else {
this._sourceBrowser.docShellIsActive = true;
}
// show the toolbar after we go into print preview mode so
// that we can initialize the toolbar with total num pages
printPreviewTB = document.createXULElement("toolbar", {
is: "printpreview-toolbar",
});
printPreviewTB.setAttribute("fullscreentoolbar", true);
printPreviewTB.setAttribute("flex", "1");
printPreviewTB.id = "print-preview-toolbar";
let navToolbox = this._listener.getNavToolbox();
navToolbox.parentNode.insertBefore(printPreviewTB, navToolbox);
printPreviewTB.initialize(ppBrowser);
// The print preview processing may not have fully completed, so if we
// have a progress listener, disable the toolbar elements that can trigger
// updates and it will enable them when completed.
if (waitForPrintProgressToEnableToolbar) {
printPreviewTB.disableUpdateTriggers(true);
}
// Enable simplify page checkbox when the page is an article
if (this._sourceBrowser.isArticle) {
printPreviewTB.enableSimplifyPage();
} else {
this.logTelemetry("PRINT_PREVIEW_SIMPLIFY_PAGE_UNAVAILABLE_COUNT");
printPreviewTB.disableSimplifyPage();
}
// copy the window close handler
if (window.onclose) {
this._closeHandlerPP = window.onclose;
} else {
this._closeHandlerPP = null;
}
window.onclose = function() {
PrintUtils.exitPrintPreview();
return false;
};
// disable chrome shortcuts...
window.addEventListener("keydown", this.onKeyDownPP, true);
window.addEventListener("keypress", this.onKeyPressPP, true);
ppBrowser.collapsed = false;
ppBrowser.focus();
// on Enter PP Call back
this._listener.onEnter();
};
mm.addMessageListener("Printing:Preview:Entered", onEntered);
return lastUsedPrinterName;
},
exitPrintPreview() {
for (let browser of this._ppBrowsers) {
let browserMM = browser.messageManager;
browserMM.sendAsyncMessage("Printing:Preview:Exit");
browser.sendMessageToActor("Printing:Preview:Exit", {}, "Printing");
}
this._ppBrowsers.clear();
this._currentPPBrowser = null;
@ -1065,5 +1009,3 @@ var PrintUtils = {
}
},
};
PrintUtils.init();

Просмотреть файл

@ -37,10 +37,7 @@ add_task(async function pp_after_orientation_change() {
let originalTabNavigated = BrowserTestUtils.browserStopped(browserToPrint);
// Enter print preview:
let printPreviewEntered = BrowserTestUtils.waitForMessage(
ppBrowser.messageManager,
"Printing:Preview:Entered"
);
let printPreviewEntered = PrintHelper.waitForOldPrintPreview(ppBrowser);
document.getElementById("cmd_printPreview").doCommand();
await printPreviewEntered;
@ -63,10 +60,7 @@ add_task(async function pp_after_orientation_change() {
: "landscape";
let printPreviewToolbar = document.getElementById("print-preview-toolbar");
printPreviewEntered = BrowserTestUtils.waitForMessage(
ppBrowser.messageManager,
"Printing:Preview:Entered"
);
printPreviewEntered = PrintHelper.waitForOldPrintPreview(ppBrowser);
printPreviewToolbar.orient(orientToSwitchTo);
await printPreviewEntered;

Просмотреть файл

@ -23,12 +23,9 @@ add_task(async function test() {
// Enter print preview
let ppBrowser = PrintPreviewListener.getPrintPreviewBrowser();
let printPreviewEntered = BrowserTestUtils.waitForMessage(
ppBrowser.messageManager,
"Printing:Preview:Entered"
);
let ppPromise = PrintHelper.waitForOldPrintPreview(ppBrowser);
document.getElementById("cmd_printPreview").doCommand();
await printPreviewEntered;
await ppPromise;
await BrowserTestUtils.waitForCondition(
() => gInPrintPreviewMode,

Просмотреть файл

@ -41,10 +41,7 @@ add_task(async function switch_print_preview_browsers() {
// Enter print preview
let defaultPPBrowser = PrintPreviewListener.getPrintPreviewBrowser();
let defaultPPEntered = BrowserTestUtils.waitForMessage(
defaultPPBrowser.messageManager,
"Printing:Preview:Entered"
);
let defaultPPEntered = PrintHelper.waitForOldPrintPreview(defaultPPBrowser);
document.getElementById("cmd_printPreview").doCommand();
await defaultPPEntered;
@ -59,9 +56,8 @@ add_task(async function switch_print_preview_browsers() {
// Here we call simplified mode
let simplifiedPPBrowser = PrintPreviewListener.getSimplifiedPrintPreviewBrowser();
let simplifiedPPEntered = BrowserTestUtils.waitForMessage(
simplifiedPPBrowser.messageManager,
"Printing:Preview:Entered"
let simplifiedPPEntered = PrintHelper.waitForOldPrintPreview(
simplifiedPPBrowser
);
let printPreviewToolbar = document.getElementById("print-preview-toolbar");

Просмотреть файл

@ -43,10 +43,7 @@ add_task(async function switch_print_preview_browsers() {
// Enter print preview
let defaultPPBrowser = PrintPreviewListener.getPrintPreviewBrowser();
let defaultPPEntered = BrowserTestUtils.waitForMessage(
defaultPPBrowser.messageManager,
"Printing:Preview:Entered"
);
let defaultPPEntered = PrintHelper.waitForOldPrintPreview(defaultPPBrowser);
document.getElementById("cmd_printPreview").doCommand();
await defaultPPEntered;
@ -57,9 +54,8 @@ add_task(async function switch_print_preview_browsers() {
// Here we call simplified mode
let simplifiedPPBrowser = PrintPreviewListener.getSimplifiedPrintPreviewBrowser();
let simplifiedPPEntered = BrowserTestUtils.waitForMessage(
simplifiedPPBrowser.messageManager,
"Printing:Preview:Entered"
let simplifiedPPEntered = PrintHelper.waitForOldPrintPreview(
simplifiedPPBrowser
);
let printPreviewToolbar = document.getElementById("print-preview-toolbar");
@ -96,10 +92,7 @@ add_task(async function switch_print_preview_browsers() {
);
// Switch back to default print preview content
defaultPPEntered = BrowserTestUtils.waitForMessage(
defaultPPBrowser.messageManager,
"Printing:Preview:Entered"
);
defaultPPEntered = PrintHelper.waitForOldPrintPreview(defaultPPBrowser);
printPreviewToolbar.mSimplifyPageCheckbox.click();
await defaultPPEntered;

Просмотреть файл

@ -55,6 +55,23 @@ class PrintHelper {
);
}
// This is used only for the old print preview. For tests
// involving the newer UI, use waitForPreview instead.
static waitForOldPrintPreview(expectedBrowser) {
const { PrintingParent } = ChromeUtils.import(
"resource://gre/actors/PrintingParent.jsm"
);
return new Promise(resolve => {
PrintingParent.setTestListener(browser => {
if (browser == expectedBrowser) {
PrintingParent.setTestListener(null);
resolve();
}
});
});
}
constructor(sourceBrowser) {
this.sourceBrowser = sourceBrowser;
}

Просмотреть файл

@ -1,46 +0,0 @@
/* 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/. */
"use strict";
var EXPORTED_SYMBOLS = ["ActorChild"];
/**
* This should be the base class of any actor class registered via
* ActorManagerParent and implemented in the child process. It currently takes
* care of setting the `mm`, `content`, and `docShell` properties based on the
* message manager it's bound to, but may do more in the future.
*
* If Fission is being simulated, and the actor is registered as "allFrames",
* the `content` property of this class will be bound to a specific subframe.
* Otherwise, the `content` is always the top-level content tied to the `mm`.
*/
this.ActorChild = class ActorChild {
constructor(dispatcher) {
this._dispatcher = dispatcher;
this.mm = dispatcher.mm;
}
get content() {
return this._dispatcher.window;
}
get docShell() {
return this.mm.docShell;
}
addEventListener(event, options) {
this._dispatcher.addEventListener(event, this.constructor.name, options);
}
addMessageListener(msg) {
this._dispatcher.addMessageListener(msg, this.constructor.name);
}
sendAsyncMessage(msg, data = {}) {
data.frameId = this._dispatcher.frameId;
data.browsingContextId = this._dispatcher.browsingContextId;
this.mm.sendAsyncMessage(msg, data);
}
};

Просмотреть файл

@ -51,6 +51,16 @@ class Dispatcher {
constructor(mm, data) {
this.mm = mm;
// Temporarily disable to prevent an exception until this file is removed.
if (!data) {
data = {
actors: new Map(),
events: new Map(),
messages: new Map(),
observers: new Map(),
};
}
this.actors = data.actors;
this.events = data.events;
this.messages = data.messages;

Просмотреть файл

@ -365,6 +365,19 @@ let JSWINDOWACTORS = {
allFrames: true,
},
Printing: {
parent: {
moduleURI: "resource://gre/actors/PrintingParent.jsm",
},
child: {
moduleURI: "resource://gre/actors/PrintingChild.jsm",
events: {
PrintingError: { capture: true },
printPreviewUpdate: { capture: true },
},
},
},
PurgeSessionHistory: {
child: {
moduleURI: "resource://gre/actors/PurgeSessionHistoryChild.jsm",
@ -554,23 +567,7 @@ let JSWINDOWACTORS = {
* If Fission is being simulated, and an actor needs to receive events from
* sub-frames, it must use "allFrames".
*/
let LEGACY_ACTORS = {
Printing: {
child: {
module: "resource://gre/actors/PrintingChild.jsm",
events: {
PrintingError: { capture: true },
printPreviewUpdate: { capture: true },
},
messages: [
"Printing:Preview:Enter",
"Printing:Preview:Exit",
"Printing:Preview:Navigate",
"Printing:Preview:ParseDocument",
],
},
},
};
let LEGACY_ACTORS = {};
class ActorSet {
constructor(group, actorSide) {

Просмотреть файл

@ -155,7 +155,6 @@ with Files("docs/**"):
EXTRA_JS_MODULES += [
"AboutPagesUtils.jsm",
"ActorChild.jsm",
"ActorManagerChild.jsm",
"ActorManagerParent.jsm",
"AppMenuNotifications.jsm",