Backed out 7 changesets (bug 1942424) for causing frequent crashes. a=backout
[gecko.git] / toolkit / components / printing / content / printUtils.js
blob591c5b8469f37fa85527e4cb4a0b2c736f847db3
1 // This file is loaded into the browser window scope.
2 /* eslint-env mozilla/browser-window */
4 // -*- tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2 -*-
6 /* This Source Code Form is subject to the terms of the Mozilla Public
7 * License, v. 2.0. If a copy of the MPL was not distributed with this
8 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
10 /**
11 * PrintUtils is a utility for front-end code to trigger common print
12 * operations (printing, show print preview, show page settings).
14 * Unfortunately, likely due to inconsistencies in how different operating
15 * systems do printing natively, our XPCOM-level printing interfaces
16 * are a bit confusing and the method by which we do something basic
17 * like printing a page is quite circuitous.
19 * To compound that, we need to support remote browsers, and that means
20 * kicking off the print jobs in the content process. This means we send
21 * messages back and forth to that process via the Printing actor.
23 * This also means that <xul:browser>'s that hope to use PrintUtils must have
24 * their type attribute set to "content".
26 * Messages sent:
28 * Printing:Preview:Enter
29 * This message is sent to put content into print preview mode. We pass
30 * the content window of the browser we're showing the preview of, and
31 * the target of the message is the browser that we'll be showing the
32 * preview in.
34 * Printing:Preview:Exit
35 * This message is sent to take content out of print preview mode.
38 XPCOMUtils.defineLazyPreferenceGetter(
39 this,
40 "SHOW_PAGE_SETUP_MENU",
41 "print.show_page_setup_menu",
42 false
45 XPCOMUtils.defineLazyPreferenceGetter(
46 this,
47 "PRINT_ALWAYS_SILENT",
48 "print.always_print_silent",
49 false
52 XPCOMUtils.defineLazyPreferenceGetter(
53 this,
54 "PREFER_SYSTEM_DIALOG",
55 "print.prefer_system_dialog",
56 false
59 ChromeUtils.defineESModuleGetters(this, {
60 PromptUtils: "resource://gre/modules/PromptUtils.sys.mjs",
61 });
63 var PrintUtils = {
64 SAVE_TO_PDF_PRINTER: "Mozilla Save to PDF",
66 get _bundle() {
67 delete this._bundle;
68 return (this._bundle = Services.strings.createBundle(
69 "chrome://global/locale/printing.properties"
70 ));
73 async checkForSelection(browsingContext) {
74 try {
75 let sourceActor =
76 browsingContext.currentWindowGlobal.getActor("PrintingSelection");
77 // Need the await for the try to trigger...
78 return await sourceActor.sendQuery("PrintingSelection:HasSelection", {});
79 } catch (e) {
80 console.error(e);
82 return false;
85 /**
86 * Updates the hidden state of the "Page Setup" menu items in the File menu,
87 * depending on the value of the `print.show_page_setup_menu` pref.
88 * Note: not all platforms have a "Page Setup" menu item (or Page Setup
89 * window).
91 updatePrintSetupMenuHiddenState() {
92 let pageSetupMenuItem = document.getElementById("menu_printSetup");
93 if (pageSetupMenuItem) {
94 pageSetupMenuItem.hidden = !SHOW_PAGE_SETUP_MENU;
98 /**
99 * Shows the page setup dialog, and saves any settings changed in
100 * that dialog if print.save_print_settings is set to true.
102 * @return true on success, false on failure
104 showPageSetup() {
105 let printSettings = this.getPrintSettings();
106 // If we come directly from the Page Setup menu, the hack in
107 // _enterPrintPreview will not have been invoked to set the last used
108 // printer name. For the reasons outlined at that hack, we want that set
109 // here too.
110 let PSSVC = Cc["@mozilla.org/gfx/printsettings-service;1"].getService(
111 Ci.nsIPrintSettingsService
113 if (!PSSVC.lastUsedPrinterName) {
114 if (printSettings.printerName) {
115 PSSVC.maybeSaveLastUsedPrinterNameToPrefs(printSettings.printerName);
116 PSSVC.maybeSavePrintSettingsToPrefs(
117 printSettings,
118 Ci.nsIPrintSettings.kInitSaveAll
122 try {
123 var PRINTDIALOGSVC = Cc[
124 "@mozilla.org/widget/printdialog-service;1"
125 ].getService(Ci.nsIPrintDialogService);
126 PRINTDIALOGSVC.showPageSetupDialog(window, printSettings, null);
127 } catch (e) {
128 dump("showPageSetup " + e + "\n");
129 return false;
131 return true;
135 * This call exists in a separate method so it can be easily overridden where
136 * `gBrowser` doesn't exist (e.g. Thunderbird).
138 * @see getTabDialogBox in tabbrowser.js
140 getTabDialogBox(sourceBrowser) {
141 return gBrowser.getTabDialogBox(sourceBrowser);
144 getPreviewBrowser(sourceBrowser) {
145 let dialogBox = this.getTabDialogBox(sourceBrowser);
146 for (let dialog of dialogBox.getTabDialogManager()._dialogs) {
147 let browser = dialog._box.querySelector(".printPreviewBrowser");
148 if (browser) {
149 return browser;
152 return null;
156 * Opens the tab modal version of the print UI for the current tab.
158 * @param aBrowsingContext
159 * The BrowsingContext of the window to print.
160 * @param aExistingPreviewBrowser
161 * An existing browser created for printing from window.print().
162 * @param aPrintSelectionOnly
163 * Whether to print only the active selection of the given browsing
164 * context.
165 * @param aPrintFrameOnly
166 * Whether to print the selected frame only
167 * @return promise resolving when the dialog is open, rejected if the preview
168 * fails.
170 _openTabModalPrint(
171 aBrowsingContext,
172 aOpenWindowInfo,
173 aPrintSelectionOnly,
174 aPrintFrameOnly
176 let sourceBrowser = aBrowsingContext.top.embedderElement;
177 let previewBrowser = this.getPreviewBrowser(sourceBrowser);
178 if (previewBrowser) {
179 // Don't open another dialog if we're already printing.
181 // XXX This can be racy can't it? getPreviewBrowser looks at browser that
182 // we set up after opening the dialog. But I guess worst case we just
183 // open two dialogs so...
184 throw new Error("Tab-modal print UI already open");
187 // Create the print preview dialog.
188 let args = PromptUtils.objectToPropBag({
189 printSelectionOnly: !!aPrintSelectionOnly,
190 isArticle: sourceBrowser.isArticle,
191 printFrameOnly: !!aPrintFrameOnly,
193 let dialogBox = this.getTabDialogBox(sourceBrowser);
194 let { closedPromise, dialog } = dialogBox.open(
195 `chrome://global/content/print.html`,
196 { features: "resizable=no", sizeTo: "available" },
197 args
199 closedPromise.catch(e => {
200 console.error(e);
203 let settingsBrowser = dialog._frame;
204 let printPreview = new PrintPreview({
205 sourceBrowsingContext: aBrowsingContext,
206 settingsBrowser,
207 topBrowsingContext: aBrowsingContext.top,
208 activeBrowsingContext: aBrowsingContext,
209 openWindowInfo: aOpenWindowInfo,
210 printFrameOnly: aPrintFrameOnly,
212 // This will create the source browser in connectedCallback() if we sent
213 // openWindowInfo. Otherwise the browser will be null.
214 settingsBrowser.parentElement.insertBefore(printPreview, settingsBrowser);
215 return printPreview.sourceBrowser;
219 * Initialize a print, this will open the tab modal UI if it is enabled or
220 * defer to the native dialog/silent print.
222 * @param aBrowsingContext
223 * The BrowsingContext of the window to print.
224 * Note that the browsing context could belong to a subframe of the
225 * tab that called window.print, or similar shenanigans.
226 * @param aOptions
227 * {windowDotPrintOpenWindowInfo}
228 * Non-null if this call comes from window.print().
229 * This is the nsIOpenWindowInfo object that has to
230 * be passed down to createBrowser in order for the
231 * static clone that has been cretaed in the child
232 * process to be linked to the browser it creates
233 * in the parent process.
234 * {printSelectionOnly} Whether to print only the active selection of
235 * the given browsing context.
236 * {printFrameOnly} Whether to print the selected frame.
238 startPrintWindow(aBrowsingContext, aOptions) {
239 // At most, one of these is set.
240 let { printSelectionOnly, printFrameOnly, windowDotPrintOpenWindowInfo } =
241 aOptions || {};
243 if (
244 windowDotPrintOpenWindowInfo &&
245 !windowDotPrintOpenWindowInfo.isForWindowDotPrint
247 throw new Error("Only expect openWindowInfo for window.print()");
250 let browsingContext = aBrowsingContext;
251 if (printSelectionOnly) {
252 // Ensure that we use the window with focus/selection if the context menu
253 // (from which 'Print selection' was selected) happens to have been opened
254 // over a different frame.
255 let focusedBc = Services.focus.focusedContentBrowsingContext;
256 if (
257 focusedBc &&
258 focusedBc.top.embedderElement == browsingContext.top.embedderElement
260 browsingContext = focusedBc;
264 if (!PRINT_ALWAYS_SILENT && !PREFER_SYSTEM_DIALOG) {
265 return this._openTabModalPrint(
266 browsingContext,
267 windowDotPrintOpenWindowInfo,
268 printSelectionOnly,
269 printFrameOnly
273 const useSystemDialog = PREFER_SYSTEM_DIALOG && !PRINT_ALWAYS_SILENT;
275 let browser = null;
276 if (windowDotPrintOpenWindowInfo) {
277 // When we're called by handleStaticCloneCreatedForPrint(), we must
278 // return this browser.
279 browser = this.createParentBrowserForStaticClone(
280 browsingContext,
281 windowDotPrintOpenWindowInfo
283 browsingContext = browser.browsingContext;
286 // This code is wrapped in an async function so that we can await the async
287 // functions that it calls.
288 async function makePrintSettingsAndInvokePrint() {
289 let settings = PrintUtils.getPrintSettings(
290 /*aPrinterName*/ "",
291 /*aDefaultsOnly*/ false,
292 /*aAllowPseudoPrinter*/ !useSystemDialog
294 settings.printSelectionOnly = printSelectionOnly;
295 if (
296 settings.outputDestination ==
297 Ci.nsIPrintSettings.kOutputDestinationFile &&
298 !settings.toFileName
300 // TODO(bug 1748004): We should consider generating the file name
301 // from the document's title as we do in print.js's pickFileName
302 // (including using DownloadPaths.sanitize!).
303 // For now, the following is for consistency with the behavior
304 // prior to bug 1669149 part 3.
305 let dest = undefined;
306 try {
307 dest = Services.dirsvc.get("CurWorkD", Ci.nsIFile).path;
308 } catch (e) {}
309 if (!dest) {
310 dest = Services.dirsvc.get("Home", Ci.nsIFile).path;
312 settings.toFileName = PathUtils.join(dest, "mozilla.pdf");
315 if (useSystemDialog) {
316 const hasSelection =
317 await PrintUtils.checkForSelection(browsingContext);
319 // Prompt the user to choose a printer and make any desired print
320 // settings changes.
321 let doPrint = false;
322 try {
323 doPrint = await PrintUtils.handleSystemPrintDialog(
324 browsingContext.topChromeWindow,
325 hasSelection,
326 settings
328 if (!doPrint) {
329 return;
331 } finally {
332 // Clean up browser if we aren't going to use it.
333 if (!doPrint && browser) {
334 browser.remove();
339 // At some point we should handle the Promise that this returns (at
340 // least report rejection to telemetry).
341 browsingContext.print(settings);
344 // We need to return to the event loop before calling
345 // makePrintSettingsAndInvokePrint() if we were called for `window.print()`.
346 // That's because if that function synchronously calls `browser.remove()`
347 // or `browsingContext.print()` before we return `browser`, the nested
348 // event loop that is being spun in the content process under the
349 // OpenInternal call in nsGlobalWindowOuter::Print will still be active.
350 // In the case of `browser.remove()`, nsGlobalWindowOuter::Print would then
351 // get unhappy once OpenInternal does return since it will fail to return
352 // a BrowsingContext. In the case of `browsingContext.print()`, we would
353 // re-enter nsGlobalWindowOuter::Print under the nested event loop and
354 // printing would then fail since the outer nsGlobalWindowOuter::Print call
355 // wouldn't yet have created the static clone.
356 setTimeout(makePrintSettingsAndInvokePrint, 0);
358 return browser;
361 togglePrintPreview(aBrowsingContext) {
362 let dialogBox = this.getTabDialogBox(aBrowsingContext.top.embedderElement);
363 let dialogs = dialogBox.getTabDialogManager().dialogs;
364 let previewDialog = dialogs.find(d =>
365 d._box.querySelector(".printSettingsBrowser")
367 if (previewDialog) {
368 previewDialog.close();
369 return;
371 this.startPrintWindow(aBrowsingContext);
375 * Called when a content process has created a new BrowsingContext for a
376 * static clone of a document that is to be printed, but we do NOT yet have a
377 * CanonicalBrowsingContext counterpart in the parent process. This only
378 * happens in the following cases:
380 * - content script invoked window.print() in the content process, or:
381 * - silent printing is enabled, and UI code previously invoked
382 * startPrintWindow which called BrowsingContext.print(), and we're now
383 * being called back by the content process to parent the static clone.
385 * In the latter case we only need to create the CanonicalBrowsingContext,
386 * link it to it's content process counterpart, and inserted it into
387 * the document tree; the print in the content process has already been
388 * initiated.
390 * In the former case we additionally need to check if we should open the
391 * tab modal print UI (if not silent printing), obtain a valid
392 * nsIPrintSettings object, and tell the content process to initiate the
393 * print with this settings object.
395 handleStaticCloneCreatedForPrint(aOpenWindowInfo) {
396 let browsingContext = aOpenWindowInfo.parent;
397 if (aOpenWindowInfo.isForWindowDotPrint) {
398 return this.startPrintWindow(browsingContext, {
399 windowDotPrintOpenWindowInfo: aOpenWindowInfo,
402 return this.createParentBrowserForStaticClone(
403 browsingContext,
404 aOpenWindowInfo
408 createParentBrowserForStaticClone(aBrowsingContext, aOpenWindowInfo) {
409 // XXX This code is only called when silent printing, so we're really
410 // abusing PrintPreview here. See bug 1768020.
411 let printPreview = new PrintPreview({
412 sourceBrowsingContext: aBrowsingContext,
413 openWindowInfo: aOpenWindowInfo,
415 let browser = printPreview.createPreviewBrowser("source");
416 document.documentElement.append(browser);
417 return browser;
420 // "private" methods and members. Don't use them.
422 _getErrorCodeForNSResult(nsresult) {
423 const MSG_CODES = [
424 "GFX_PRINTER_NO_PRINTER_AVAILABLE",
425 "GFX_PRINTER_NAME_NOT_FOUND",
426 "GFX_PRINTER_COULD_NOT_OPEN_FILE",
427 "GFX_PRINTER_STARTDOC",
428 "GFX_PRINTER_ENDDOC",
429 "GFX_PRINTER_STARTPAGE",
430 "GFX_PRINTER_DOC_IS_BUSY",
431 "ABORT",
432 "NOT_AVAILABLE",
433 "NOT_IMPLEMENTED",
434 "OUT_OF_MEMORY",
435 "UNEXPECTED",
438 for (let code of MSG_CODES) {
439 let nsErrorResult = "NS_ERROR_" + code;
440 if (Cr[nsErrorResult] == nsresult) {
441 return code;
445 // PERR_FAILURE is the catch-all error message if we've gotten one that
446 // we don't recognize.
447 return "FAILURE";
450 _displayPrintingError(nsresult, isPrinting, browser) {
451 // The nsresults from a printing error are mapped to strings that have
452 // similar names to the errors themselves. For example, for error
453 // NS_ERROR_GFX_PRINTER_NO_PRINTER_AVAILABLE, the name of the string
454 // for the error message is: PERR_GFX_PRINTER_NO_PRINTER_AVAILABLE. What's
455 // more, if we're in the process of doing a print preview, it's possible
456 // that there are strings specific for print preview for these errors -
457 // if so, the names of those strings have _PP as a suffix. It's possible
458 // that no print preview specific strings exist, in which case it is fine
459 // to fall back to the original string name.
460 let msgName = "PERR_" + this._getErrorCodeForNSResult(nsresult);
461 let msg, title;
462 if (!isPrinting) {
463 // Try first with _PP suffix.
464 let ppMsgName = msgName + "_PP";
465 try {
466 msg = this._bundle.GetStringFromName(ppMsgName);
467 } catch (e) {
468 // We allow localizers to not have the print preview error string,
469 // and just fall back to the printing error string.
473 if (!msg) {
474 msg = this._bundle.GetStringFromName(msgName);
477 title = this._bundle.GetStringFromName(
478 isPrinting
479 ? "print_error_dialog_title"
480 : "printpreview_error_dialog_title"
483 Services.prompt.asyncAlert(
484 browser.browsingContext,
485 Services.prompt.MODAL_TYPE_TAB,
486 title,
490 Glean.printing.error[this._getErrorCodeForNSResult(nsresult)].add(1);
493 getPrintSettings(aPrinterName, aDefaultsOnly, aAllowPseudoPrinter = true) {
494 var printSettings;
495 try {
496 var PSSVC = Cc["@mozilla.org/gfx/printsettings-service;1"].getService(
497 Ci.nsIPrintSettingsService
500 function isValidPrinterName(aPrinterName) {
501 return (
502 aPrinterName &&
503 (aAllowPseudoPrinter ||
504 aPrinterName != PrintUtils.SAVE_TO_PDF_PRINTER)
508 // We must not try to print using an nsIPrintSettings without a printer
509 // name set.
510 const printerName = (function () {
511 if (isValidPrinterName(aPrinterName)) {
512 return aPrinterName;
514 if (isValidPrinterName(PSSVC.lastUsedPrinterName)) {
515 return PSSVC.lastUsedPrinterName;
517 return Cc["@mozilla.org/gfx/printerlist;1"].getService(
518 Ci.nsIPrinterList
519 ).systemDefaultPrinterName;
520 })();
522 printSettings = PSSVC.createNewPrintSettings();
523 printSettings.printerName = printerName;
525 // First get any defaults from the printer. We want to skip this for Save
526 // to PDF since it isn't a real printer and will throw.
527 if (printSettings.printerName != this.SAVE_TO_PDF_PRINTER) {
528 PSSVC.initPrintSettingsFromPrinter(
529 printSettings.printerName,
530 printSettings
534 if (!aDefaultsOnly) {
535 // Apply any settings that have been saved for this printer.
536 PSSVC.initPrintSettingsFromPrefs(
537 printSettings,
538 true,
539 printSettings.kInitSaveAll
542 } catch (e) {
543 console.error("PrintUtils.getPrintSettings failed:", e);
545 return printSettings;
548 // Show the system print dialog, saving modified preferences.
549 // Returns true if the user clicked print (Not cancel).
550 async handleSystemPrintDialog(aWindow, aHasSelection, aSettings) {
551 // Prompt the user to choose a printer and make any desired print
552 // settings changes.
553 try {
554 const svc = Cc["@mozilla.org/widget/printdialog-service;1"].getService(
555 Ci.nsIPrintDialogService
557 await svc.showPrintDialog(aWindow, aHasSelection, aSettings);
558 } catch (e) {
559 if (e.result == Cr.NS_ERROR_ABORT) {
560 return false;
562 throw e;
565 // Update the saved last used printer name and print settings:
566 var PSSVC = Cc["@mozilla.org/gfx/printsettings-service;1"].getService(
567 Ci.nsIPrintSettingsService
569 PSSVC.maybeSaveLastUsedPrinterNameToPrefs(aSettings.printerName);
570 PSSVC.maybeSavePrintSettingsToPrefs(
571 aSettings,
572 Ci.nsIPrintSettings.kPrintDialogPersistSettings
574 return true;
579 * This class implements a custom element that contains a nested <browser>
580 * element. When the user asks to print a document, we create an instance of
581 * this class and ask the platform code to create a static clone of the
582 * document (a snapshot that won't change due to script running, etc.) in the
583 * contained <browser> element.
585 * To display a print preview to the user, an instance of this element is added
586 * to the tab-modal print preview dialog. As the user changes print preview
587 * settings, we may actually end up with multiple instances: one for a preview
588 * of the original document, one for a preview of the focused frame, and one
589 * for the selected text.
591 * To print without displaying a print preview, an instance of this class is
592 * appended, hidden, to the end of the top-level chrome browser's document.
594 class PrintPreview extends MozElements.BaseControl {
595 constructor({
596 sourceBrowsingContext,
597 settingsBrowser,
598 topBrowsingContext,
599 activeBrowsingContext,
600 openWindowInfo,
601 printFrameOnly,
602 }) {
603 super();
604 this.sourceBrowsingContext = sourceBrowsingContext;
605 this.settingsBrowser = settingsBrowser;
606 this.topBrowsingContext = topBrowsingContext;
607 this.activeBrowsingContext = activeBrowsingContext;
608 this.openWindowInfo = openWindowInfo;
609 this.printFrameOnly = printFrameOnly;
611 this.printSelectionOnly = false;
612 this.simplifyPage = false;
613 this.sourceBrowser = null;
614 this.selectionBrowser = null;
615 this.simplifiedBrowser = null;
616 this.lastPreviewBrowser = null;
619 connectedCallback() {
620 if (this.childElementCount > 0) {
621 return;
623 this.setAttribute("flex", "1");
624 this.append(
625 MozXULElement.parseXULToFragment(`
626 <stack class="previewStack" rendering="true" flex="1" previewtype="primary">
627 <vbox class="previewRendering" flex="1">
628 <h1 class="print-pending-label" data-l10n-id="printui-loading"></h1>
629 </vbox>
630 <html:printpreview-pagination class="printPreviewNavigation"></html:printpreview-pagination>
631 </stack>
632 <html:link rel="stylesheet" href="chrome://global/content/printPreview.css"/>
635 this.stack = this.firstElementChild;
636 this.paginator = this.querySelector("printpreview-pagination");
638 if (this.openWindowInfo) {
639 // For window.print() we need a browser right away for the contents to be
640 // cloned into, create it now.
641 this.createPreviewBrowser("source");
645 disconnectedCallback() {
646 this.exitPrintPreview();
649 getSourceBrowsingContext() {
650 if (this.openWindowInfo) {
651 // If openWindowInfo is set this was for window.print() and the source
652 // contents have already been cloned into the preview browser.
653 return this.sourceBrowser.browsingContext;
655 return this.sourceBrowsingContext;
658 get currentBrowsingContext() {
659 return this.lastPreviewBrowser.browsingContext;
662 exitPrintPreview() {
663 this.sourceBrowser?.frameLoader?.exitPrintPreview();
664 this.simplifiedBrowser?.frameLoader?.exitPrintPreview();
665 this.selectionBrowser?.frameLoader?.exitPrintPreview();
667 this.textContent = "";
670 async printPreview(settings, { sourceVersion, sourceURI }) {
671 this.stack.setAttribute("rendering", true);
673 let result = await this._printPreview(settings, {
674 sourceVersion,
675 sourceURI,
678 let browser = this.lastPreviewBrowser;
679 this.stack.setAttribute("previewtype", browser.getAttribute("previewtype"));
680 browser.setAttribute("sheet-count", result.sheetCount);
681 // The view resets to the top of the document on update bug 1686737.
682 browser.setAttribute("current-page", 1);
683 this.paginator.observePreviewBrowser(browser);
684 await document.l10n.translateElements([browser]);
686 this.stack.removeAttribute("rendering");
688 return result;
691 async _printPreview(settings, { sourceVersion, sourceURI }) {
692 let printSelectionOnly = sourceVersion == "selection";
693 let simplifyPage = sourceVersion == "simplified";
694 let selectionTypeBrowser;
695 let previewBrowser;
697 // Select the existing preview browser elements, these could be null.
698 if (printSelectionOnly) {
699 selectionTypeBrowser = this.selectionBrowser;
700 previewBrowser = this.selectionBrowser;
701 } else {
702 selectionTypeBrowser = this.sourceBrowser;
703 previewBrowser = simplifyPage
704 ? this.simplifiedBrowser
705 : this.sourceBrowser;
708 settings.docURL = sourceURI;
710 if (previewBrowser) {
711 this.lastPreviewBrowser = previewBrowser;
712 if (this.openWindowInfo) {
713 // We only want to use openWindowInfo for the window.print() browser,
714 // we can get rid of it now.
715 this.openWindowInfo = null;
717 // This browser has been rendered already, just update it.
718 return previewBrowser.frameLoader.printPreview(settings, null);
721 if (!selectionTypeBrowser) {
722 // Need to create a non-simplified browser.
723 selectionTypeBrowser = this.createPreviewBrowser(
724 simplifyPage ? "source" : sourceVersion
726 let browsingContext =
727 printSelectionOnly || this.printFrameOnly
728 ? this.activeBrowsingContext
729 : this.topBrowsingContext;
730 let result = await selectionTypeBrowser.frameLoader.printPreview(
731 settings,
732 browsingContext
734 // If this isn't simplified then we're done.
735 if (!simplifyPage) {
736 this.lastPreviewBrowser = selectionTypeBrowser;
737 return result;
741 // We have the base selection/primary browser but need to simplify.
742 previewBrowser = this.createPreviewBrowser(sourceVersion);
743 await previewBrowser.browsingContext.currentWindowGlobal
744 .getActor("Printing")
745 .sendQuery("Printing:Preview:ParseDocument", {
746 URL: sourceURI,
747 windowID:
748 selectionTypeBrowser.browsingContext.currentWindowGlobal
749 .outerWindowId,
752 // We've parsed a simplified version into the preview browser. Convert that to
753 // a print preview as usual.
754 this.lastPreviewBrowser = previewBrowser;
755 return previewBrowser.frameLoader.printPreview(
756 settings,
757 previewBrowser.browsingContext
761 createPreviewBrowser(sourceVersion) {
762 let browser = document.createXULElement("browser");
763 let browsingContext =
764 sourceVersion == "selection" ||
765 this.printFrameOnly ||
766 (sourceVersion == "source" && this.openWindowInfo)
767 ? this.sourceBrowsingContext
768 : this.sourceBrowsingContext.top;
769 if (sourceVersion == "source" && this.openWindowInfo) {
770 browser.openWindowInfo = this.openWindowInfo;
771 } else {
772 let userContextId = browsingContext.originAttributes.userContextId;
773 if (userContextId) {
774 browser.setAttribute("usercontextid", userContextId);
776 browser.setAttribute(
777 "initialBrowsingContextGroupId",
778 browsingContext.group.id
781 browser.setAttribute("type", "content");
782 let remoteType = browsingContext.currentRemoteType;
783 if (remoteType) {
784 browser.setAttribute("remoteType", remoteType);
785 browser.setAttribute("remote", "true");
787 // When the print process finishes, we get closed by
788 // nsDocumentViewer::OnDonePrinting, or by the print preview code.
790 // When that happens, we should remove us from the DOM if connected.
791 browser.addEventListener("DOMWindowClose", function (e) {
792 if (this.isConnected) {
793 this.remove();
795 e.stopPropagation();
796 e.preventDefault();
799 if (this.settingsBrowser) {
800 browser.addEventListener("contextmenu", function (e) {
801 e.preventDefault();
804 browser.setAttribute("previewtype", sourceVersion);
805 browser.classList.add("printPreviewBrowser");
806 browser.setAttribute("flex", "1");
807 browser.setAttribute("printpreview", "true");
808 browser.setAttribute("nodefaultsrc", "true");
809 document.l10n.setAttributes(browser, "printui-preview-label");
811 this.stack.insertBefore(browser, this.paginator);
813 if (sourceVersion == "source") {
814 this.sourceBrowser = browser;
815 } else if (sourceVersion == "selection") {
816 this.selectionBrowser = browser;
817 } else if (sourceVersion == "simplified") {
818 this.simplifiedBrowser = browser;
820 } else {
821 browser.style.visibility = "collapse";
823 return browser;
826 customElements.define("print-preview", PrintPreview);