1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsPrintJob.h"
10 #include "nsDocShell.h"
11 #include "nsReadableUtils.h"
12 #include "nsQueryObject.h"
14 #include "mozilla/AsyncEventDispatcher.h"
15 #include "mozilla/ResultExtensions.h"
16 #include "mozilla/ComputedStyleInlines.h"
17 #include "mozilla/dom/BrowsingContext.h"
18 #include "mozilla/dom/PBrowser.h"
19 #include "mozilla/dom/Selection.h"
20 #include "mozilla/dom/ShadowRoot.h"
21 #include "mozilla/dom/CustomEvent.h"
22 #include "mozilla/dom/ContentChild.h"
23 #include "mozilla/dom/HTMLCanvasElement.h"
24 #include "mozilla/dom/ScriptSettings.h"
25 #include "mozilla/IntegerRange.h"
26 #include "mozilla/PresShell.h"
27 #include "mozilla/PresShellInlines.h"
28 #include "mozilla/StaticPrefs_print.h"
29 #include "mozilla/Telemetry.h"
30 #include "nsIBrowserChild.h"
31 #include "nsIOService.h"
32 #include "nsIScriptGlobalObject.h"
33 #include "nsIStringBundle.h"
34 #include "nsPIDOMWindow.h"
35 #include "nsPrintData.h"
36 #include "nsPrintObject.h"
37 #include "nsIDocShell.h"
39 #include "nsITextToSubURI.h"
46 #include "nsIPrintSettings.h"
47 #include "nsIPrintSettingsService.h"
48 #include "nsIPrintSession.h"
49 #include "nsGkAtoms.h"
52 static const char sPrintSettingsServiceContractID
[] =
53 "@mozilla.org/gfx/printsettings-service;1";
56 #include "nsPagePrintTimer.h"
59 #include "mozilla/dom/Document.h"
60 #include "mozilla/dom/DocumentInlines.h"
63 #include "gfxContext.h"
64 #include "mozilla/gfx/DrawEventRecorder.h"
65 #include "mozilla/layout/RemotePrintJobChild.h"
66 #include "nsISupportsUtils.h"
67 #include "nsIScriptContext.h"
68 #include "nsComponentManagerUtils.h"
69 #include "mozilla/Preferences.h"
70 #include "mozilla/PresShell.h"
73 #include "nsIDeviceContextSpec.h"
74 #include "nsDeviceContextSpecProxy.h"
75 #include "nsViewManager.h"
77 #include "nsPageSequenceFrame.h"
78 #include "nsIInterfaceRequestor.h"
79 #include "nsIInterfaceRequestorUtils.h"
80 #include "nsIWebBrowserChrome.h"
81 #include "mozilla/ReflowInput.h"
82 #include "nsIContentViewer.h"
83 #include "nsIDocumentViewerPrint.h"
85 #include "nsFocusManager.h"
87 #include "mozilla/Components.h"
88 #include "mozilla/dom/Element.h"
89 #include "mozilla/dom/HTMLFrameElement.h"
90 #include "mozilla/ServoStyleSet.h"
92 using namespace mozilla
;
93 using namespace mozilla::dom
;
95 //-----------------------------------------------------
97 #include "mozilla/Logging.h"
100 // PR_LOGGING is force to always be on (even in release builds)
101 // but we only want some of it on,
102 //#define EXTENDED_DEBUG_PRINTING
105 // this log level turns on the dumping of each document's layout info
106 #define DUMP_LAYOUT_LEVEL (static_cast<mozilla::LogLevel>(9))
109 static mozilla::LazyLogModule
gPrintingLog("printing");
111 # define PR_PL(_p1) MOZ_LOG(gPrintingLog, mozilla::LogLevel::Debug, _p1);
114 #ifdef EXTENDED_DEBUG_PRINTING
115 static uint32_t gDumpFileNameCnt
= 0;
116 static uint32_t gDumpLOFileNameCnt
= 0;
119 #define PRT_YESNO(_p) ((_p) ? "YES" : "NO")
120 static const char* gFrameTypesStr
[] = {"eDoc", "eFrame", "eIFrame",
123 // This processes the selection on aOrigDoc and creates an inverted selection on
124 // aDoc, which it then deletes. If the start or end of the inverted selection
125 // ranges occur in text nodes then an ellipsis is added.
126 static nsresult
DeleteNonSelectedNodes(Document
& aDoc
);
128 #ifdef EXTENDED_DEBUG_PRINTING
129 // Forward Declarations
130 static void DumpPrintObjectsListStart(const char* aStr
,
131 const nsTArray
<nsPrintObject
*>& aDocList
);
132 static void DumpPrintObjectsTree(nsPrintObject
* aPO
, int aLevel
= 0,
133 FILE* aFD
= nullptr);
134 static void DumpPrintObjectsTreeLayout(const UniquePtr
<nsPrintObject
>& aPO
,
135 nsDeviceContext
* aDC
, int aLevel
= 0,
136 FILE* aFD
= nullptr);
138 # define DUMP_DOC_LIST(_title) \
139 DumpPrintObjectsListStart((_title), mPrt->mPrintDocList);
140 # define DUMP_DOC_TREE DumpPrintObjectsTree(mPrt->mPrintObject.get());
141 # define DUMP_DOC_TREELAYOUT \
142 DumpPrintObjectsTreeLayout(mPrt->mPrintObject, mPrt->mPrintDC);
144 # define DUMP_DOC_LIST(_title)
145 # define DUMP_DOC_TREE
146 # define DUMP_DOC_TREELAYOUT
149 // -------------------------------------------------------
151 // -------------------------------------------------------
153 static bool HasFramesetChild(nsIContent
* aContent
) {
158 // do a breadth search across all siblings
159 for (nsIContent
* child
= aContent
->GetFirstChild(); child
;
160 child
= child
->GetNextSibling()) {
161 if (child
->IsHTMLElement(nsGkAtoms::frameset
)) {
169 static bool IsParentAFrameSet(nsIDocShell
* aParent
) {
170 // See if the incoming doc is the root document
171 if (!aParent
) return false;
173 // When it is the top level document we need to check
174 // to see if it contains a frameset. If it does, then
175 // we only want to print the doc's children and not the document itself
176 // For anything else we always print all the children and the document
177 // for example, if the doc contains an IFRAME we eant to print the child
178 // document (the IFRAME) and then the rest of the document.
180 // XXX we really need to search the frame tree, and not the content
181 // but there is no way to distinguish between IFRAMEs and FRAMEs
182 // with the GetFrameType call.
183 // Bug 53459 has been files so we can eventually distinguish
184 // between IFRAME frames and FRAME frames
185 bool isFrameSet
= false;
186 // only check to see if there is a frameset if there is
187 // NO parent doc for this doc. meaning this parent is the root doc
188 nsCOMPtr
<Document
> doc
= aParent
->GetDocument();
190 nsIContent
* rootElement
= doc
->GetRootElement();
192 isFrameSet
= HasFramesetChild(rootElement
);
199 * Build a tree of nsPrintObjects under aPO. It also appends a (depth first)
200 * flat list of all the nsPrintObjects created to aPrintData->mPrintDocList. If
201 * one of the nsPrintObject's document is the focused document, then the print
202 * object is set as aPrintData->mSelectionRoot.
203 * @param aParentPO The parent nsPrintObject to populate, must not be null.
204 * @param aFocusedDoc Document from the window that had focus when print was
206 * @param aPrintData nsPrintData for the current print, must not be null.
208 static void BuildNestedPrintObjects(const UniquePtr
<nsPrintObject
>& aParentPO
,
209 const RefPtr
<Document
>& aFocusedDoc
,
210 RefPtr
<nsPrintData
>& aPrintData
) {
211 MOZ_ASSERT(aParentPO
);
212 MOZ_ASSERT(aPrintData
);
214 // If aParentPO is for an iframe and its original document is focusedDoc then
215 // always set as the selection root.
216 if (aParentPO
->mFrameType
== eIFrame
&&
217 aParentPO
->mDocument
->GetOriginalDocument() == aFocusedDoc
) {
218 aPrintData
->mSelectionRoot
= aParentPO
.get();
219 } else if (!aPrintData
->mSelectionRoot
&& aParentPO
->HasSelection()) {
220 // If there is no focused iframe but there is a selection in one or more
221 // frames then we want to set the root nsPrintObject as the focus root so
222 // that later EnablePrintingSelectionOnly can search for and enable all
223 // nsPrintObjects containing selections.
224 aPrintData
->mSelectionRoot
= aPrintData
->mPrintObject
.get();
227 for (auto& bc
: aParentPO
->mDocShell
->GetBrowsingContext()->Children()) {
228 nsCOMPtr
<nsIDocShell
> docShell
= bc
->GetDocShell();
230 if (auto* cc
= dom::ContentChild::GetSingleton()) {
231 nsCOMPtr
<nsIPrintSettingsService
> printSettingsService
=
232 do_GetService(sPrintSettingsServiceContractID
);
233 embedding::PrintData printData
;
234 printSettingsService
->SerializeToPrintData(aPrintData
->mPrintSettings
,
236 Unused
<< cc
->SendUpdateRemotePrintSettings(bc
, printData
);
241 RefPtr
<Document
> doc
= docShell
->GetDocument();
242 MOZ_DIAGNOSTIC_ASSERT(doc
);
243 // We might find non-static documents here if the fission remoteness change
244 // hasn't happened / finished yet. In that case, just skip them, the same
245 // way we do for remote frames above.
246 MOZ_DIAGNOSTIC_ASSERT(doc
->IsStaticDocument() || doc
->IsInitialDocument());
247 if (!doc
|| !doc
->IsStaticDocument()) {
251 auto childPO
= MakeUnique
<nsPrintObject
>();
252 nsresult rv
= childPO
->InitAsNestedObject(docShell
, doc
, aParentPO
.get());
254 MOZ_ASSERT_UNREACHABLE("Init failed?");
257 aPrintData
->mPrintDocList
.AppendElement(childPO
.get());
258 BuildNestedPrintObjects(childPO
, aFocusedDoc
, aPrintData
);
259 aParentPO
->mKids
.AppendElement(std::move(childPO
));
264 * On platforms that support it, sets the printer name stored in the
265 * nsIPrintSettings to the last-used printer if a printer name is not already
267 * XXXjwatt: Why is this necessary? Can't the code that reads the printer
268 * name later "just" use the last-used printer if a name isn't specified? Then
269 * we wouldn't have this inconsistency between platforms and processes.
271 static nsresult
EnsureSettingsHasPrinterNameSet(
272 nsIPrintSettings
* aPrintSettings
) {
273 #if defined(XP_MACOSX) || defined(ANDROID)
274 // Mac doesn't support retrieving a printer list.
277 NS_ENSURE_ARG_POINTER(aPrintSettings
);
279 // See if aPrintSettings already has a printer
280 nsString printerName
;
281 nsresult rv
= aPrintSettings
->GetPrinterName(printerName
);
282 if (NS_SUCCEEDED(rv
) && !printerName
.IsEmpty()) {
286 // aPrintSettings doesn't have a printer set.
287 // Try to fetch the name of the last-used printer.
288 nsCOMPtr
<nsIPrintSettingsService
> printSettingsService
=
289 do_GetService(sPrintSettingsServiceContractID
, &rv
);
290 NS_ENSURE_SUCCESS(rv
, rv
);
292 rv
= printSettingsService
->GetLastUsedPrinterName(printerName
);
293 if (NS_SUCCEEDED(rv
) && !printerName
.IsEmpty()) {
294 rv
= aPrintSettings
->SetPrinterName(printerName
);
300 static nsresult
GetDefaultPrintSettings(nsIPrintSettings
** aSettings
) {
301 *aSettings
= nullptr;
303 nsresult rv
= NS_ERROR_FAILURE
;
304 nsCOMPtr
<nsIPrintSettingsService
> printSettingsService
=
305 do_GetService(sPrintSettingsServiceContractID
, &rv
);
306 NS_ENSURE_SUCCESS(rv
, rv
);
308 return printSettingsService
->GetDefaultPrintSettingsForPrinting(aSettings
);
311 //-------------------------------------------------------
313 NS_IMPL_ISUPPORTS(nsPrintJob
, nsIWebProgressListener
, nsISupportsWeakReference
)
315 //-------------------------------------------------------
316 nsPrintJob::nsPrintJob() = default;
318 nsPrintJob::~nsPrintJob() {
319 Destroy(); // for insurance
320 DisconnectPagePrintTimer();
323 bool nsPrintJob::CheckBeforeDestroy() const {
324 return mPrt
&& mPrt
->mPreparingForPrint
;
327 PresShell
* nsPrintJob::GetPrintPreviewPresShell() {
328 return mPrtPreview
->mPrintObject
->mPresShell
;
331 //-------------------------------------------------------
332 void nsPrintJob::Destroy() {
336 mIsDestroying
= true;
340 #ifdef NS_PRINT_PREVIEW
341 mPrtPreview
= nullptr;
343 mDocViewerPrint
= nullptr;
346 //-------------------------------------------------------
347 void nsPrintJob::DestroyPrintingData() { mPrt
= nullptr; }
349 //---------------------------------------------------------------------------------
350 //-- Section: Methods needed by the DocViewer
351 //---------------------------------------------------------------------------------
353 //--------------------------------------------------------
354 nsresult
nsPrintJob::Initialize(nsIDocumentViewerPrint
* aDocViewerPrint
,
355 nsIDocShell
* aDocShell
, Document
* aOriginalDoc
,
357 NS_ENSURE_ARG_POINTER(aDocViewerPrint
);
358 NS_ENSURE_ARG_POINTER(aDocShell
);
359 NS_ENSURE_ARG_POINTER(aOriginalDoc
);
361 mDocViewerPrint
= aDocViewerPrint
;
362 mDocShell
= do_GetWeakReference(aDocShell
);
363 mScreenDPI
= aScreenDPI
;
365 // Anything state that we need from aOriginalDoc must be fetched and stored
366 // here, since the document that the user selected to print may mutate
367 // across consecutive PrintPreview() calls.
369 Element
* root
= aOriginalDoc
->GetRootElement();
370 mDisallowSelectionPrint
=
372 root
->HasAttr(kNameSpaceID_None
, nsGkAtoms::mozdisallowselectionprint
);
374 if (nsPIDOMWindowOuter
* window
= aOriginalDoc
->GetWindow()) {
375 if (nsCOMPtr
<nsIWebBrowserChrome
> wbc
= window
->GetWebBrowserChrome()) {
376 // We only get this in order to skip opening the progress dialog when
377 // the window is modal. Once the platform code stops opening the
378 // progress dialog (bug 1558907), we can get rid of this.
379 wbc
->IsWindowModal(&mIsForModalWindow
);
386 //-------------------------------------------------------
387 nsresult
nsPrintJob::Cancel() {
388 if (mPrt
&& mPrt
->mPrintSettings
) {
389 return mPrt
->mPrintSettings
->SetIsCancelled(true);
391 return NS_ERROR_FAILURE
;
394 //-----------------------------------------------------------------
395 std::tuple
<nsPageSequenceFrame
*, int32_t>
396 nsPrintJob::GetSeqFrameAndCountSheets() const {
397 nsPrintData
* printData
= mPrtPreview
? mPrtPreview
: mPrt
;
398 if (NS_WARN_IF(!printData
)) {
402 const nsPrintObject
* po
= printData
->mPrintObject
.get();
403 if (NS_WARN_IF(!po
)) {
407 // This is sometimes incorrectly called before the pres shell has been created
408 // (bug 1141756). MOZ_DIAGNOSTIC_ASSERT so we'll still see the crash in
409 // Nightly/Aurora in case the other patch fixes this.
410 if (!po
->mPresShell
) {
411 MOZ_DIAGNOSTIC_ASSERT(
412 false, "GetSeqFrameAndCountSheets needs a non-null pres shell");
416 nsPageSequenceFrame
* seqFrame
= po
->mPresShell
->GetPageSequenceFrame();
421 // count the total number of sheets
422 return {seqFrame
, seqFrame
->PrincipalChildList().GetLength()};
424 //---------------------------------------------------------------------------------
425 //-- Done: Methods needed by the DocViewer
426 //---------------------------------------------------------------------------------
428 // Foward decl for Debug Helper Functions
429 #ifdef EXTENDED_DEBUG_PRINTING
431 static int RemoveFilesInDir(const char* aDir
);
433 static void GetDocTitleAndURL(const UniquePtr
<nsPrintObject
>& aPO
,
434 nsACString
& aDocStr
, nsACString
& aURLStr
);
435 static void DumpPrintObjectsTree(nsPrintObject
* aPO
, int aLevel
, FILE* aFD
);
436 static void DumpPrintObjectsList(const nsTArray
<nsPrintObject
*>& aDocList
);
437 static void RootFrameList(nsPresContext
* aPresContext
, FILE* out
,
438 const char* aPrefix
);
439 static void DumpViews(nsIDocShell
* aDocShell
, FILE* out
);
440 static void DumpLayoutData(const char* aTitleStr
, const char* aURLStr
,
441 nsPresContext
* aPresContext
, nsDeviceContext
* aDC
,
442 nsIFrame
* aRootFrame
, nsIDocShell
* aDocShell
,
446 //--------------------------------------------------------------------------------
448 nsresult
nsPrintJob::CommonPrint(bool aIsPrintPreview
,
449 nsIPrintSettings
* aPrintSettings
,
450 nsIWebProgressListener
* aWebProgressListener
,
451 Document
* aSourceDoc
) {
452 // Callers must hold a strong reference to |this| to ensure that we stay
453 // alive for the duration of this method, because our main owning reference
454 // (on nsDocumentViewer) might be cleared during this function (if we cause
455 // script to run and it cancels the print operation).
457 nsresult rv
= DoCommonPrint(aIsPrintPreview
, aPrintSettings
,
458 aWebProgressListener
, aSourceDoc
);
460 if (aIsPrintPreview
) {
461 mIsCreatingPrintPreview
= false;
462 SetIsPrintPreview(false);
464 SetIsPrinting(false);
466 if (rv
!= NS_ERROR_ABORT
&& rv
!= NS_ERROR_OUT_OF_MEMORY
) {
467 FirePrintingErrorEvent(rv
);
475 nsresult
nsPrintJob::DoCommonPrint(bool aIsPrintPreview
,
476 nsIPrintSettings
* aPrintSettings
,
477 nsIWebProgressListener
* aWebProgressListener
,
479 MOZ_ASSERT(aDoc
->IsStaticDocument());
483 // Grab the new instance with local variable to guarantee that it won't be
484 // deleted during this method.
485 // Note: Methods we call early below rely on mPrt being set.
486 mPrt
= new nsPrintData(aIsPrintPreview
? nsPrintData::eIsPrintPreview
487 : nsPrintData::eIsPrinting
);
488 RefPtr
<nsPrintData
> printData
= mPrt
;
490 if (aIsPrintPreview
) {
491 mIsCreatingPrintPreview
= true;
493 // Our new print preview nsPrintData is stored in mPtr until we move it
494 // to mPrtPreview once we've finish creating the print preview. We must
495 // clear mPtrPreview so that code will use mPtr until that happens.
496 mPrtPreview
= nullptr;
498 SetIsPrintPreview(true);
503 if (aWebProgressListener
) {
504 printData
->mPrintProgressListeners
.AppendObject(aWebProgressListener
);
507 // Get the document from the currently focused window.
508 RefPtr
<Document
> focusedDoc
= FindFocusedDocument(aDoc
);
510 // Get the docshell for this documentviewer
511 nsCOMPtr
<nsIDocShell
> docShell(do_QueryReferent(mDocShell
, &rv
));
512 NS_ENSURE_SUCCESS(rv
, rv
);
514 // if they don't pass in a PrintSettings, then get the Global PS
515 printData
->mPrintSettings
= aPrintSettings
;
516 if (!printData
->mPrintSettings
) {
517 MOZ_TRY(GetDefaultPrintSettings(getter_AddRefs(printData
->mPrintSettings
)));
521 nsAutoScriptBlocker scriptBlocker
;
522 printData
->mPrintObject
= MakeUnique
<nsPrintObject
>();
523 rv
= printData
->mPrintObject
->InitAsRootObject(docShell
, aDoc
,
524 mIsCreatingPrintPreview
);
525 NS_ENSURE_SUCCESS(rv
, rv
);
527 printData
->mPrintDocList
.AppendElement(printData
->mPrintObject
.get());
529 printData
->mIsParentAFrameSet
= IsParentAFrameSet(docShell
);
530 printData
->mPrintObject
->mFrameType
=
531 printData
->mIsParentAFrameSet
? eFrameSet
: eDoc
;
533 BuildNestedPrintObjects(printData
->mPrintObject
, focusedDoc
, printData
);
536 // The nsAutoScriptBlocker above will now have been destroyed, which may
537 // cause our print/print-preview operation to finish. In this case, we
538 // should immediately return an error code so that the root caller knows
539 // it shouldn't continue to do anything with this instance.
541 return NS_ERROR_FAILURE
;
544 // XXX This isn't really correct...
545 if (!printData
->mPrintObject
->mDocument
||
546 !printData
->mPrintObject
->mDocument
->GetRootElement())
547 return NS_ERROR_GFX_PRINTER_STARTDOC
;
549 MOZ_TRY(EnsureSettingsHasPrinterNameSet(printData
->mPrintSettings
));
551 printData
->mPrintSettings
->SetIsCancelled(false);
552 printData
->mPrintSettings
->GetShrinkToFit(&printData
->mShrinkToFit
);
554 // Create a print session and let the print settings know about it.
555 // Don't overwrite an existing print session.
556 // The print settings hold an nsWeakPtr to the session so it does not
557 // need to be cleared from the settings at the end of the job.
558 // XXX What lifetime does the printSession need to have?
559 nsCOMPtr
<nsIPrintSession
> printSession
;
560 if (!mIsCreatingPrintPreview
) {
561 rv
= printData
->mPrintSettings
->GetPrintSession(
562 getter_AddRefs(printSession
));
563 if (NS_FAILED(rv
) || !printSession
) {
564 printSession
= do_CreateInstance("@mozilla.org/gfx/printsession;1", &rv
);
565 NS_ENSURE_SUCCESS(rv
, rv
);
566 printData
->mPrintSettings
->SetPrintSession(printSession
);
568 RefPtr
<layout::RemotePrintJobChild
> remotePrintJob
=
569 printSession
->GetRemotePrintJob();
570 if (remotePrintJob
) {
571 // If we have a RemotePrintJob add it to the print progress listeners,
572 // so it can forward to the parent.
573 printData
->mPrintProgressListeners
.AppendElement(remotePrintJob
);
578 // Now determine how to set up the Frame print UI
579 printData
->mPrintSettings
->SetIsPrintSelectionRBEnabled(
580 !mDisallowSelectionPrint
&& printData
->mSelectionRoot
);
582 bool printingViaParent
=
583 XRE_IsContentProcess() && StaticPrefs::print_print_via_parent();
584 nsCOMPtr
<nsIDeviceContextSpec
> devspec
;
585 if (printingViaParent
) {
586 devspec
= new nsDeviceContextSpecProxy();
588 devspec
= do_CreateInstance("@mozilla.org/gfx/devicecontextspec;1", &rv
);
589 NS_ENSURE_SUCCESS(rv
, rv
);
592 bool printSilently
= false;
593 printData
->mPrintSettings
->GetPrintSilent(&printSilently
);
594 if (StaticPrefs::print_always_print_silent()) {
595 printSilently
= true;
598 if (mIsDoingPrinting
&& printSilently
) {
599 Telemetry::ScalarAdd(Telemetry::ScalarID::PRINTING_SILENT_PRINT
, 1);
602 MOZ_TRY(devspec
->Init(nullptr, printData
->mPrintSettings
,
603 mIsCreatingPrintPreview
));
605 printData
->mPrintDC
= new nsDeviceContext();
606 MOZ_TRY(printData
->mPrintDC
->InitForPrinting(devspec
));
608 MOZ_TRY(EnablePOsForPrinting());
610 if (!mIsCreatingPrintPreview
) {
611 printData
->OnStartPrinting();
613 InitPrintDocConstruction(false);
618 //---------------------------------------------------------------------------------
619 nsresult
nsPrintJob::Print(Document
* aSourceDoc
,
620 nsIPrintSettings
* aPrintSettings
,
621 nsIWebProgressListener
* aWebProgressListener
) {
622 // If we have a print preview document, use that instead of the original
623 // mDocument. That way animated images etc. get printed using the same state
624 // as in print preview.
625 RefPtr
<Document
> doc
= mPrtPreview
&& mPrtPreview
->mPrintObject
626 ? mPrtPreview
->mPrintObject
->mDocument
.get()
629 return CommonPrint(false, aPrintSettings
, aWebProgressListener
, doc
);
632 nsresult
nsPrintJob::PrintPreview(Document
* aSourceDoc
,
633 nsIPrintSettings
* aPrintSettings
,
634 nsIWebProgressListener
* aWebProgressListener
,
635 PrintPreviewResolver
&& aCallback
) {
636 // Take ownership of aCallback, otherwise a function further up the call
637 // stack will call it to signal failure (by passing zero).
638 mPrintPreviewCallback
= std::move(aCallback
);
641 CommonPrint(true, aPrintSettings
, aWebProgressListener
, aSourceDoc
);
643 if (mPrintPreviewCallback
) {
645 mPrintPreviewCallback(
646 PrintPreviewResultInfo(0, 0, false, false, false, {}));
647 mPrintPreviewCallback
= nullptr;
653 int32_t nsPrintJob::GetRawNumPages() const {
654 auto [seqFrame
, numSheets
] = GetSeqFrameAndCountSheets();
656 return seqFrame
? seqFrame
->GetRawNumPages() : 0;
659 bool nsPrintJob::GetIsEmpty() const {
660 auto [seqFrame
, numSheets
] = GetSeqFrameAndCountSheets();
667 return !seqFrame
->GetPagesInFirstSheet();
670 int32_t nsPrintJob::GetPrintPreviewNumSheets() const {
671 auto [seqFrame
, numSheets
] = GetSeqFrameAndCountSheets();
676 already_AddRefed
<nsIPrintSettings
> nsPrintJob::GetCurrentPrintSettings() {
678 return do_AddRef(mPrt
->mPrintSettings
);
681 return do_AddRef(mPrtPreview
->mPrintSettings
);
686 //-----------------------------------------------------------------
687 //-- Section: Pre-Reflow Methods
688 //-----------------------------------------------------------------
691 void nsPrintJob::GetDisplayTitleAndURL(Document
& aDoc
,
692 nsIPrintSettings
* aSettings
,
693 DocTitleDefault aTitleDefault
,
694 nsAString
& aTitle
, nsAString
& aURLStr
) {
699 aSettings
->GetTitle(aTitle
);
700 aSettings
->GetDocURL(aURLStr
);
703 if (aTitle
.IsEmpty()) {
704 aDoc
.GetTitle(aTitle
);
705 if (aTitle
.IsEmpty()) {
706 if (!aURLStr
.IsEmpty() &&
707 aTitleDefault
== DocTitleDefault::eDocURLElseFallback
) {
710 nsCOMPtr
<nsIStringBundle
> brandBundle
;
711 nsCOMPtr
<nsIStringBundleService
> svc
=
712 mozilla::components::StringBundle::Service();
714 svc
->CreateBundle("chrome://branding/locale/brand.properties",
715 getter_AddRefs(brandBundle
));
717 brandBundle
->GetStringFromName("brandShortName", aTitle
);
720 if (aTitle
.IsEmpty()) {
721 aTitle
.AssignLiteral(u
"Mozilla Document");
727 if (aURLStr
.IsEmpty()) {
728 nsIURI
* url
= aDoc
.GetDocumentURI();
733 nsCOMPtr
<nsIURI
> exposableURI
= net::nsIOService::CreateExposableURI(url
);
734 nsAutoCString urlCStr
;
735 nsresult rv
= exposableURI
->GetSpec(urlCStr
);
740 nsCOMPtr
<nsITextToSubURI
> textToSubURI
=
741 do_GetService(NS_ITEXTTOSUBURI_CONTRACTID
, &rv
);
746 textToSubURI
->UnEscapeURIForUI(urlCStr
, aURLStr
);
750 //---------------------------------------------------------------------
751 nsresult
nsPrintJob::DocumentReadyForPrinting() {
752 // Send the document to the printer...
753 nsresult rv
= SetupToPrintContent();
755 // The print job was canceled or there was a problem
756 // So remove all other documents from the print list
757 DonePrintingSheets(nullptr, rv
);
762 /** ---------------------------------------------------
763 * Cleans up when an error occurred
765 nsresult
nsPrintJob::CleanupOnFailure(nsresult aResult
, bool aIsPrinting
) {
766 PR_PL(("**** Failed %s - rv 0x%" PRIX32
,
767 aIsPrinting
? "Printing" : "Print Preview",
768 static_cast<uint32_t>(aResult
)));
771 if (mPagePrintTimer
) {
772 mPagePrintTimer
->Stop();
773 DisconnectPagePrintTimer();
777 SetIsPrinting(false);
779 SetIsPrintPreview(false);
780 mIsCreatingPrintPreview
= false;
783 /* cleanup done, let's fire-up an error dialog to notify the user
786 * When rv == NS_ERROR_ABORT, it means we want out of the
787 * print job without displaying any error messages
789 if (aResult
!= NS_ERROR_ABORT
) {
790 FirePrintingErrorEvent(aResult
);
793 FirePrintCompletionEvent();
798 //---------------------------------------------------------------------
799 void nsPrintJob::FirePrintingErrorEvent(nsresult aPrintError
) {
800 if (mPrintPreviewCallback
) {
802 mPrintPreviewCallback(
803 PrintPreviewResultInfo(0, 0, false, false, false, {}));
804 mPrintPreviewCallback
= nullptr;
807 nsCOMPtr
<nsIContentViewer
> cv
= do_QueryInterface(mDocViewerPrint
);
808 if (NS_WARN_IF(!cv
)) {
812 nsCOMPtr
<Document
> doc
= cv
->GetDocument();
813 RefPtr
<CustomEvent
> event
= NS_NewDOMCustomEvent(doc
, nullptr, nullptr);
818 if (!jsapi
.Init(event
->GetParentObject())) {
821 JSContext
* cx
= jsapi
.cx();
823 JS::Rooted
<JS::Value
> detail(
824 cx
, JS::NumberValue(static_cast<double>(aPrintError
)));
825 event
->InitCustomEvent(cx
, u
"PrintingError"_ns
, false, false, detail
);
826 event
->SetTrusted(true);
828 RefPtr
<AsyncEventDispatcher
> asyncDispatcher
=
829 new AsyncEventDispatcher(doc
, event
);
830 asyncDispatcher
->mOnlyChromeDispatch
= ChromeOnlyDispatch::eYes
;
831 asyncDispatcher
->RunDOMEventWhenSafe();
833 // Inform any progress listeners of the Error.
835 // Note that nsPrintData::DoOnStatusChange() will call some listeners.
836 // So, mPrt can be cleared or recreated.
837 RefPtr
<nsPrintData
> printData
= mPrt
;
838 printData
->DoOnStatusChange(aPrintError
);
842 //-----------------------------------------------------------------
843 //-- Section: Reflow Methods
844 //-----------------------------------------------------------------
846 nsresult
nsPrintJob::ReconstructAndReflow(bool doSetPixelScale
) {
847 if (NS_WARN_IF(!mPrt
)) {
848 return NS_ERROR_FAILURE
;
851 #if defined(XP_WIN) && defined(EXTENDED_DEBUG_PRINTING)
852 // We need to clear all the output files here
853 // because they will be re-created with second reflow of the docs
854 if (MOZ_LOG_TEST(gPrintingLog
, DUMP_LAYOUT_LEVEL
)) {
855 RemoveFilesInDir(".\\");
856 gDumpFileNameCnt
= 0;
857 gDumpLOFileNameCnt
= 0;
861 // In this loop, it's conceivable that one of our helpers might clear mPrt,
862 // while we're using it & its members! So we capture it in an owning local
863 // reference & use that instead of using mPrt directly.
864 RefPtr
<nsPrintData
> printData
= mPrt
;
865 for (uint32_t i
= 0; i
< printData
->mPrintDocList
.Length(); ++i
) {
866 nsPrintObject
* po
= printData
->mPrintDocList
.ElementAt(i
);
867 NS_ASSERTION(po
, "nsPrintObject can't be null!");
869 if (!po
->PrintingIsEnabled() || po
->mInvisible
) {
873 // When the print object has been marked as "print the document" (i.e,
874 // po->PrintingIsEnabled() is true), mPresContext and mPresShell should be
875 // non-nullptr (i.e., should've been created for the print) since they
876 // are necessary to print the document.
877 MOZ_ASSERT(po
->mPresContext
&& po
->mPresShell
,
878 "mPresContext and mPresShell shouldn't be nullptr when the "
880 "has been marked as \"print the document\"");
882 UpdateZoomRatio(po
, doSetPixelScale
);
884 po
->mPresContext
->SetPageScale(po
->mZoomRatio
);
886 // Calculate scale factor from printer to screen
887 float printDPI
= float(AppUnitsPerCSSInch()) /
888 float(printData
->mPrintDC
->AppUnitsPerDevPixel());
889 po
->mPresContext
->SetPrintPreviewScale(mScreenDPI
/ printDPI
);
891 RefPtr
<PresShell
> presShell(po
->mPresShell
);
892 if (NS_WARN_IF(presShell
->IsDestroying())) {
893 return NS_ERROR_FAILURE
;
896 presShell
->ReconstructFrames();
898 // If the printing was canceled or restarted with different data,
899 // let's stop doing this printing.
900 if (NS_WARN_IF(mPrt
!= printData
)) {
901 return NS_ERROR_FAILURE
;
904 // For all views except the first one, setup the root view.
905 // ??? Can there be multiple po for the top-level-document?
906 bool documentIsTopLevel
= true;
910 nsresult rv
= SetRootView(po
, doReturn
, documentIsTopLevel
, adjSize
);
912 MOZ_ASSERT(!documentIsTopLevel
, "How could this happen?");
914 if (NS_FAILED(rv
) || doReturn
) {
919 presShell
->FlushPendingNotifications(FlushType::Layout
);
921 if (NS_WARN_IF(presShell
->IsDestroying())) {
922 return NS_ERROR_FAILURE
;
925 // If the printing was canceled or restarted with different data,
926 // let's stop doing this printing.
927 if (NS_WARN_IF(mPrt
!= printData
)) {
928 return NS_ERROR_FAILURE
;
931 nsresult rv
= UpdateSelectionAndShrinkPrintObject(po
, documentIsTopLevel
);
932 NS_ENSURE_SUCCESS(rv
, rv
);
937 //-------------------------------------------------------
938 nsresult
nsPrintJob::SetupToPrintContent() {
939 // This method may be called while DoCommonPrint() initializes the instance
940 // when its script blocker goes out of scope. In such case, this cannot do
941 // its job as expected because some objects in mPrt have not been initialized
942 // yet but they are necessary.
943 // Note: it shouldn't be possible for mPrt->mPrintObject to be null; we check
944 // it for good measure (after we check its owner) before we start
945 // dereferencing it below.
946 if (NS_WARN_IF(!mPrt
) || NS_WARN_IF(!mPrt
->mPrintObject
)) {
947 return NS_ERROR_FAILURE
;
950 // If this is creating print preview, mPrt->mPrintObject->mPresContext and
951 // mPrt->mPrintObject->mPresShell need to be non-nullptr because this cannot
952 // initialize page sequence frame without them at end of this method since
953 // page sequence frame has already been destroyed or not been created yet.
954 if (mIsCreatingPrintPreview
&&
955 (NS_WARN_IF(!mPrt
->mPrintObject
->mPresContext
) ||
956 NS_WARN_IF(!mPrt
->mPrintObject
->mPresShell
))) {
957 return NS_ERROR_FAILURE
;
960 // If this is printing some documents (not print-previewing the documents),
961 // mPrt->mPrintObject->mPresContext and mPrt->mPrintObject->mPresShell can be
962 // nullptr only when mPrt->mPrintObject->PrintingIsEnabled() is false. E.g.,
963 // if the document has a <frameset> element and it's printing only content in
964 // a <frame> element or all <frame> elements separately.
966 (!mIsCreatingPrintPreview
&& !mPrt
->mPrintObject
->PrintingIsEnabled()) ||
967 (mPrt
->mPrintObject
->mPresContext
&& mPrt
->mPrintObject
->mPresShell
),
968 "mPresContext and mPresShell shouldn't be nullptr when printing the "
969 "document or creating print-preview");
971 bool didReconstruction
= false;
973 // This method works with mPrt->mPrintObject. So, we need to guarantee that
974 // it won't be deleted in this method. We achieve this by holding a strong
975 // local reference to mPrt, which in turn keeps mPrintObject alive.
976 RefPtr
<nsPrintData
> printData
= mPrt
;
978 // If some new content got loaded since the initial reflow rebuild
980 if (mDidLoadDataForPrinting
) {
981 nsresult rv
= ReconstructAndReflow(DoSetPixelScale());
982 if (NS_WARN_IF(NS_FAILED(rv
))) {
985 // If the printing was canceled or restarted with different data,
986 // let's stop doing this printing.
987 if (NS_WARN_IF(mPrt
!= printData
)) {
988 return NS_ERROR_FAILURE
;
990 didReconstruction
= true;
993 // Here is where we figure out if extra reflow for shrinking the content
995 // But skip this step if we are in PrintPreview
996 bool ppIsShrinkToFit
= mPrtPreview
&& mPrtPreview
->mShrinkToFit
;
997 if (printData
->mShrinkToFit
&& !ppIsShrinkToFit
) {
998 // Now look for the PO that has the smallest percent for shrink to fit
999 if (printData
->mPrintDocList
.Length() > 1 &&
1000 printData
->mPrintObject
->mFrameType
== eFrameSet
) {
1001 nsPrintObject
* smallestPO
= FindSmallestSTF();
1002 NS_ASSERTION(smallestPO
, "There must always be an XMost PO!");
1004 // Calc the shrinkage based on the entire content area
1005 printData
->mShrinkRatio
= smallestPO
->mShrinkRatio
;
1008 // Single document so use the Shrink as calculated for the PO
1009 printData
->mShrinkRatio
= printData
->mPrintObject
->mShrinkRatio
;
1012 if (printData
->mShrinkRatio
< 0.998f
) {
1013 nsresult rv
= ReconstructAndReflow(true);
1014 if (NS_WARN_IF(NS_FAILED(rv
))) {
1017 // If the printing was canceled or restarted with different data,
1018 // let's stop doing this printing.
1019 if (NS_WARN_IF(mPrt
!= printData
)) {
1020 return NS_ERROR_FAILURE
;
1022 didReconstruction
= true;
1025 if (MOZ_LOG_TEST(gPrintingLog
, LogLevel::Debug
)) {
1026 float calcRatio
= 0.0f
;
1027 if (printData
->mPrintDocList
.Length() > 1 &&
1028 printData
->mPrintObject
->mFrameType
== eFrameSet
) {
1029 nsPrintObject
* smallestPO
= FindSmallestSTF();
1030 NS_ASSERTION(smallestPO
, "There must always be an XMost PO!");
1032 // Calc the shrinkage based on the entire content area
1033 calcRatio
= smallestPO
->mShrinkRatio
;
1036 // Single document so use the Shrink as calculated for the PO
1037 calcRatio
= printData
->mPrintObject
->mShrinkRatio
;
1040 ("*******************************************************************"
1042 PR_PL(("STF Ratio is: %8.5f Effective Ratio: %8.5f Diff: %8.5f\n",
1043 printData
->mShrinkRatio
, calcRatio
,
1044 printData
->mShrinkRatio
- calcRatio
));
1046 ("*******************************************************************"
1051 // If the frames got reconstructed and reflowed the number of pages might
1053 if (didReconstruction
) {
1054 FirePrintPreviewUpdateEvent();
1055 // If the printing was canceled or restarted with different data,
1056 // let's stop doing this printing.
1057 if (NS_WARN_IF(mPrt
!= printData
)) {
1058 return NS_ERROR_FAILURE
;
1062 DUMP_DOC_LIST(("\nAfter Reflow------------------------------------------"));
1064 PR_PL(("-------------------------------------------------------\n"));
1067 CalcNumPrintablePages(printData
->mNumPrintablePages
);
1069 PR_PL(("--- Printing %d pages\n", printData
->mNumPrintablePages
));
1070 DUMP_DOC_TREELAYOUT
;
1072 // Print listener setup...
1073 printData
->OnStartPrinting();
1075 // If the printing was canceled or restarted with different data,
1076 // let's stop doing this printing.
1077 if (NS_WARN_IF(mPrt
!= printData
)) {
1078 return NS_ERROR_FAILURE
;
1081 nsAutoString fileNameStr
;
1082 // check to see if we are printing to a file
1083 if (printData
->mPrintSettings
->GetOutputDestination() ==
1084 nsIPrintSettings::kOutputDestinationFile
) {
1085 // On some platforms the BeginDocument needs to know the name of the file.
1086 printData
->mPrintSettings
->GetToFileName(fileNameStr
);
1089 nsAutoString docTitleStr
;
1090 nsAutoString docURLStr
;
1091 GetDisplayTitleAndURL(
1092 *printData
->mPrintObject
->mDocument
, printData
->mPrintSettings
,
1093 DocTitleDefault::eDocURLElseFallback
, docTitleStr
, docURLStr
);
1095 int32_t startPage
= 1;
1096 int32_t endPage
= printData
->mNumPrintablePages
;
1098 nsTArray
<int32_t> ranges
;
1099 printData
->mPrintSettings
->GetPageRanges(ranges
);
1100 for (size_t i
= 0; i
< ranges
.Length(); i
+= 2) {
1101 startPage
= std::max(1, std::min(startPage
, ranges
[i
]));
1102 endPage
= std::min(printData
->mNumPrintablePages
,
1103 std::max(endPage
, ranges
[i
+ 1]));
1106 nsresult rv
= NS_OK
;
1107 // BeginDocument may pass back a FAILURE code
1108 // i.e. On Windows, if you are printing to a file and hit "Cancel"
1109 // to the "File Name" dialog, this comes back as an error
1110 // Don't start printing when regression test are executed
1111 if (mIsDoingPrinting
) {
1112 rv
= printData
->mPrintDC
->BeginDocument(docTitleStr
, fileNameStr
, startPage
,
1116 if (mIsCreatingPrintPreview
) {
1117 // Copy docTitleStr and docURLStr to the pageSequenceFrame, to be displayed
1119 nsPageSequenceFrame
* seqFrame
=
1120 printData
->mPrintObject
->mPresShell
->GetPageSequenceFrame();
1122 seqFrame
->StartPrint(printData
->mPrintObject
->mPresContext
,
1123 printData
->mPrintSettings
, docTitleStr
, docURLStr
);
1127 PR_PL(("****************** Begin Document ************************\n"));
1129 if (NS_FAILED(rv
)) {
1130 NS_WARNING_ASSERTION(rv
== NS_ERROR_ABORT
,
1131 "Failed to begin document for printing");
1135 // This will print the docshell document
1136 // when it completes asynchronously in the DonePrintingSheets method
1137 // it will check to see if there are more docshells to be printed and
1138 // then PrintDocContent will be called again.
1140 if (mIsDoingPrinting
) {
1141 PrintDocContent(printData
->mPrintObject
, rv
); // ignore return value
1147 //-------------------------------------------------------
1148 // Recursively reflow each sub-doc and then calc
1149 // all the frame locations of the sub-docs
1150 nsresult
nsPrintJob::ReflowDocList(const UniquePtr
<nsPrintObject
>& aPO
,
1151 bool aSetPixelScale
) {
1152 NS_ENSURE_ARG_POINTER(aPO
);
1154 // Check to see if the subdocument's element has been hidden by the parent
1156 if (aPO
->mParent
&& aPO
->mParent
->mPresShell
) {
1158 aPO
->mContent
? aPO
->mContent
->GetPrimaryFrame() : nullptr;
1159 if (!frame
|| !frame
->StyleVisibility()->IsVisible()) {
1160 aPO
->EnablePrinting(false);
1161 aPO
->mInvisible
= true;
1166 UpdateZoomRatio(aPO
.get(), aSetPixelScale
);
1169 MOZ_TRY(ReflowPrintObject(aPO
));
1171 for (const UniquePtr
<nsPrintObject
>& kid
: aPO
->mKids
) {
1172 MOZ_TRY(ReflowDocList(kid
, aSetPixelScale
));
1177 void nsPrintJob::FirePrintPreviewUpdateEvent() {
1178 // Dispatch the event only while in PrintPreview. When printing, there is no
1179 // listener bound to this event and therefore no need to dispatch it.
1180 if (mCreatedForPrintPreview
&& !mIsDoingPrinting
) {
1181 nsCOMPtr
<nsIContentViewer
> cv
= do_QueryInterface(mDocViewerPrint
);
1182 (new AsyncEventDispatcher(cv
->GetDocument(), u
"printPreviewUpdate"_ns
,
1183 CanBubble::eYes
, ChromeOnlyDispatch::eYes
))
1184 ->RunDOMEventWhenSafe();
1188 nsresult
nsPrintJob::InitPrintDocConstruction(bool aHandleError
) {
1189 // Guarantee that mPrt->mPrintObject won't be deleted. It's owned by mPrt.
1190 // So, we should grab it with local variable.
1191 RefPtr
<nsPrintData
> printData
= mPrt
;
1193 if (NS_WARN_IF(!printData
)) {
1194 return NS_ERROR_FAILURE
;
1197 // Attach progressListener to catch network requests.
1198 mDidLoadDataForPrinting
= false;
1201 AutoRestore
<bool> restore
{mDoingInitialReflow
};
1202 mDoingInitialReflow
= true;
1204 nsCOMPtr
<nsIWebProgress
> webProgress
=
1205 do_QueryInterface(printData
->mPrintObject
->mDocShell
);
1206 webProgress
->AddProgressListener(static_cast<nsIWebProgressListener
*>(this),
1207 nsIWebProgress::NOTIFY_STATE_REQUEST
);
1209 MOZ_TRY(ReflowDocList(printData
->mPrintObject
, DoSetPixelScale()));
1211 FirePrintPreviewUpdateEvent();
1214 MaybeResumePrintAfterResourcesLoaded(aHandleError
);
1218 bool nsPrintJob::ShouldResumePrint() const {
1219 if (mDoingInitialReflow
) {
1222 Document
* doc
= mPrt
->mPrintObject
->mDocument
;
1224 NS_ENSURE_TRUE(doc
, true);
1225 nsCOMPtr
<nsILoadGroup
> lg
= doc
->GetDocumentLoadGroup();
1226 NS_ENSURE_TRUE(lg
, true);
1227 bool pending
= false;
1228 nsresult rv
= lg
->IsPending(&pending
);
1229 NS_ENSURE_SUCCESS(rv
, true);
1233 nsresult
nsPrintJob::MaybeResumePrintAfterResourcesLoaded(
1234 bool aCleanupOnError
) {
1235 if (!ShouldResumePrint()) {
1236 mDidLoadDataForPrinting
= true;
1239 // If Destroy() has already been called, mPtr is nullptr. Then, the instance
1240 // needs to do nothing anymore in this method.
1241 // Note: it shouldn't be possible for mPrt->mPrintObject to be null; we
1242 // just check it for good measure, as we check its owner.
1243 // Note: it shouldn't be possible for mPrt->mPrintObject->mDocShell to be
1244 // null; we just check it for good measure, as we check its owner.
1245 if (!mPrt
|| NS_WARN_IF(!mPrt
->mPrintObject
) ||
1246 NS_WARN_IF(!mPrt
->mPrintObject
->mDocShell
)) {
1247 return NS_ERROR_FAILURE
;
1250 nsCOMPtr
<nsIWebProgress
> webProgress
=
1251 do_QueryInterface(mPrt
->mPrintObject
->mDocShell
);
1253 webProgress
->RemoveProgressListener(
1254 static_cast<nsIWebProgressListener
*>(this));
1257 if (mIsDoingPrinting
) {
1258 rv
= DocumentReadyForPrinting();
1260 rv
= FinishPrintPreview();
1263 /* cleaup on failure + notify user */
1264 if (aCleanupOnError
&& NS_FAILED(rv
)) {
1265 NS_WARNING_ASSERTION(rv
== NS_ERROR_ABORT
,
1266 "nsPrintJob::ResumePrintAfterResourcesLoaded failed");
1267 CleanupOnFailure(rv
, !mIsDoingPrinting
);
1273 ////////////////////////////////////////////////////////////////////////////////
1274 // nsIWebProgressListener
1276 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP
1277 nsPrintJob::OnStateChange(nsIWebProgress
* aWebProgress
, nsIRequest
* aRequest
,
1278 uint32_t aStateFlags
, nsresult aStatus
) {
1279 if (aStateFlags
& STATE_STOP
) {
1280 // If all resources are loaded, then finish and reflow.
1281 MaybeResumePrintAfterResourcesLoaded(/* aCleanupOnError */ true);
1287 nsPrintJob::OnProgressChange(nsIWebProgress
* aWebProgress
, nsIRequest
* aRequest
,
1288 int32_t aCurSelfProgress
, int32_t aMaxSelfProgress
,
1289 int32_t aCurTotalProgress
,
1290 int32_t aMaxTotalProgress
) {
1291 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
1296 nsPrintJob::OnLocationChange(nsIWebProgress
* aWebProgress
, nsIRequest
* aRequest
,
1297 nsIURI
* aLocation
, uint32_t aFlags
) {
1298 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
1303 nsPrintJob::OnStatusChange(nsIWebProgress
* aWebProgress
, nsIRequest
* aRequest
,
1304 nsresult aStatus
, const char16_t
* aMessage
) {
1305 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
1310 nsPrintJob::OnSecurityChange(nsIWebProgress
* aWebProgress
, nsIRequest
* aRequest
,
1312 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
1317 nsPrintJob::OnContentBlockingEvent(nsIWebProgress
* aWebProgress
,
1318 nsIRequest
* aRequest
, uint32_t aEvent
) {
1319 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
1323 //-------------------------------------------------------
1325 void nsPrintJob::UpdateZoomRatio(nsPrintObject
* aPO
, bool aSetPixelScale
) {
1326 // Here is where we set the shrinkage value into the DC
1327 // and this is what actually makes it shrink
1328 if (aSetPixelScale
&& aPO
->mFrameType
!= eIFrame
) {
1330 aPO
->mZoomRatio
= mPrt
->mPrintSettings
->GetPrintSelectionOnly()
1331 ? aPO
->mShrinkRatio
- 0.005f
1332 : mPrt
->mShrinkRatio
- 0.005f
;
1333 } else if (!mPrt
->mShrinkToFit
) {
1335 mPrt
->mPrintSettings
->GetScaling(&scaling
);
1336 aPO
->mZoomRatio
= float(scaling
);
1340 nsresult
nsPrintJob::UpdateSelectionAndShrinkPrintObject(
1341 nsPrintObject
* aPO
, bool aDocumentIsTopLevel
) {
1342 PresShell
* displayPresShell
= aPO
->mDocShell
->GetPresShell();
1343 // Transfer Selection Ranges to the new Print PresShell
1344 RefPtr
<Selection
> selection
, selectionPS
;
1345 // It's okay if there is no display shell, just skip copying the selection
1346 if (displayPresShell
) {
1347 selection
= displayPresShell
->GetCurrentSelection(SelectionType::eNormal
);
1349 selectionPS
= aPO
->mPresShell
->GetCurrentSelection(SelectionType::eNormal
);
1351 // Reset all existing selection ranges that might have been added by calling
1352 // this function before.
1354 selectionPS
->RemoveAllRanges(IgnoreErrors());
1356 if (selection
&& selectionPS
) {
1357 const uint32_t rangeCount
= selection
->RangeCount();
1358 for (const uint32_t inx
: IntegerRange(rangeCount
)) {
1359 MOZ_ASSERT(selection
->RangeCount() == rangeCount
);
1360 const RefPtr
<nsRange
> range
{selection
->GetRangeAt(inx
)};
1361 selectionPS
->AddRangeAndSelectFramesAndNotifyListeners(*range
,
1366 // If we are trying to shrink the contents to fit on the page
1367 // we must first locate the "pageContent" frame
1368 // Then we walk the frame tree and look for the "xmost" frame
1369 // this is the frame where the right-hand side of the frame extends
1371 if (mPrt
->mShrinkToFit
&& aDocumentIsTopLevel
) {
1372 nsPageSequenceFrame
* pageSeqFrame
= aPO
->mPresShell
->GetPageSequenceFrame();
1373 NS_ENSURE_STATE(pageSeqFrame
);
1374 aPO
->mShrinkRatio
= pageSeqFrame
->GetSTFPercent();
1375 // Limit the shrink-to-fit scaling for some text-ish type of documents.
1376 nsAutoString contentType
;
1377 aPO
->mPresShell
->GetDocument()->GetContentType(contentType
);
1378 if (contentType
.EqualsLiteral("application/xhtml+xml") ||
1379 StringBeginsWith(contentType
, u
"text/"_ns
)) {
1380 int32_t limitPercent
=
1381 Preferences::GetInt("print.shrink-to-fit.scale-limit-percent", 20);
1382 limitPercent
= std::max(0, limitPercent
);
1383 limitPercent
= std::min(100, limitPercent
);
1384 float minShrinkRatio
= float(limitPercent
) / 100;
1385 aPO
->mShrinkRatio
= std::max(aPO
->mShrinkRatio
, minShrinkRatio
);
1391 bool nsPrintJob::DoSetPixelScale() {
1392 // This is an Optimization
1393 // If we are in PP then we already know all the shrinkage information
1394 // so just transfer it to the PrintData and we will skip the extra shrinkage
1397 // doSetPixelScale tells Reflow whether to set the shrinkage value into the DC
1398 // The first time we do not want to do this, the second time through we do
1399 bool doSetPixelScale
= false;
1400 bool ppIsShrinkToFit
= mPrtPreview
&& mPrtPreview
->mShrinkToFit
;
1401 if (ppIsShrinkToFit
) {
1402 mPrt
->mShrinkRatio
= mPrtPreview
->mShrinkRatio
;
1403 doSetPixelScale
= true;
1405 return doSetPixelScale
;
1408 nsView
* nsPrintJob::GetParentViewForRoot() {
1409 if (mIsCreatingPrintPreview
) {
1410 if (nsCOMPtr
<nsIContentViewer
> cv
= do_QueryInterface(mDocViewerPrint
)) {
1411 return cv
->FindContainerView();
1417 nsresult
nsPrintJob::SetRootView(nsPrintObject
* aPO
, bool& doReturn
,
1418 bool& documentIsTopLevel
, nsSize
& adjSize
) {
1419 bool canCreateScrollbars
= true;
1422 nsView
* parentView
= nullptr;
1426 if (aPO
->mParent
&& aPO
->mParent
->PrintingIsEnabled()) {
1428 aPO
->mContent
? aPO
->mContent
->GetPrimaryFrame() : nullptr;
1429 // Without a frame, this document can't be displayed; therefore, there is no
1430 // point to reflowing it
1432 aPO
->EnablePrinting(false);
1437 // XXX If printing supported printing document hierarchies with non-constant
1438 // zoom this would be wrong as we use the same mPrt->mPrintDC for all
1440 adjSize
= frame
->GetContentRect().Size();
1441 documentIsTopLevel
= false;
1442 // presshell exists because parent is printable
1444 // the top nsPrintObject's widget will always have scrollbars
1445 if (frame
&& frame
->IsSubDocumentFrame()) {
1446 nsView
* view
= frame
->GetView();
1447 NS_ENSURE_TRUE(view
, NS_ERROR_FAILURE
);
1448 view
= view
->GetFirstChild();
1449 NS_ENSURE_TRUE(view
, NS_ERROR_FAILURE
);
1451 canCreateScrollbars
= false;
1454 nscoord pageWidth
, pageHeight
;
1455 mPrt
->mPrintDC
->GetDeviceSurfaceDimensions(pageWidth
, pageHeight
);
1456 adjSize
= nsSize(pageWidth
, pageHeight
);
1457 documentIsTopLevel
= true;
1458 parentView
= GetParentViewForRoot();
1461 if (aPO
->mViewManager
->GetRootView()) {
1462 // Reuse the root view that is already on the root frame.
1463 rootView
= aPO
->mViewManager
->GetRootView();
1464 // Remove it from its existing parent if necessary
1465 aPO
->mViewManager
->RemoveChild(rootView
);
1466 rootView
->SetParent(parentView
);
1468 // Create a child window of the parent that is our "root view/window"
1469 nsRect tbounds
= nsRect(nsPoint(0, 0), adjSize
);
1470 rootView
= aPO
->mViewManager
->CreateView(tbounds
, parentView
);
1471 NS_ENSURE_TRUE(rootView
, NS_ERROR_OUT_OF_MEMORY
);
1474 if (mIsCreatingPrintPreview
&& documentIsTopLevel
) {
1475 aPO
->mPresContext
->SetPaginatedScrolling(canCreateScrollbars
);
1478 // Setup hierarchical relationship in view manager
1479 aPO
->mViewManager
->SetRootView(rootView
);
1484 // Reflow a nsPrintObject
1485 nsresult
nsPrintJob::ReflowPrintObject(const UniquePtr
<nsPrintObject
>& aPO
) {
1486 NS_ENSURE_STATE(aPO
);
1488 if (!aPO
->PrintingIsEnabled()) {
1492 NS_ASSERTION(!aPO
->mPresContext
, "Recreating prescontext");
1494 // Guarantee that mPrt and the objects it owns won't be deleted in this method
1495 // because it might be cleared if other modules called from here may fire
1496 // events, notifying observers and/or listeners.
1497 RefPtr
<nsPrintData
> printData
= mPrt
;
1499 // create the PresContext
1500 nsPresContext::nsPresContextType type
=
1501 mIsCreatingPrintPreview
? nsPresContext::eContext_PrintPreview
1502 : nsPresContext::eContext_Print
;
1503 const bool shouldBeRoot
=
1504 (!aPO
->mParent
|| !aPO
->mParent
->PrintingIsEnabled()) &&
1505 !GetParentViewForRoot();
1506 aPO
->mPresContext
= shouldBeRoot
? new nsRootPresContext(aPO
->mDocument
, type
)
1507 : new nsPresContext(aPO
->mDocument
, type
);
1508 aPO
->mPresContext
->SetPrintSettings(printData
->mPrintSettings
);
1510 // init it with the DC
1511 MOZ_TRY(aPO
->mPresContext
->Init(printData
->mPrintDC
));
1513 aPO
->mViewManager
= new nsViewManager();
1515 MOZ_TRY(aPO
->mViewManager
->Init(printData
->mPrintDC
));
1518 aPO
->mDocument
->CreatePresShell(aPO
->mPresContext
, aPO
->mViewManager
);
1519 if (!aPO
->mPresShell
) {
1520 return NS_ERROR_FAILURE
;
1523 // If we're printing selection then remove the nonselected nodes from our
1525 if (printData
->mPrintSettings
->GetPrintSelectionOnly()) {
1526 // If we fail to remove the nodes then we should fail to print, because if
1527 // the user was trying to print a small selection from a large document,
1528 // sending the whole document to a real printer would be very frustrating.
1529 MOZ_TRY(DeleteNonSelectedNodes(*aPO
->mDocument
));
1532 bool doReturn
= false;
1533 bool documentIsTopLevel
= false;
1536 nsresult rv
= SetRootView(aPO
.get(), doReturn
, documentIsTopLevel
, adjSize
);
1538 if (NS_FAILED(rv
) || doReturn
) {
1542 PR_PL(("In DV::ReflowPrintObject PO: %p pS: %p (%9s) Setting w,h to %d,%d\n",
1543 aPO
.get(), aPO
->mPresShell
.get(), gFrameTypesStr
[aPO
->mFrameType
],
1544 adjSize
.width
, adjSize
.height
));
1546 aPO
->mPresShell
->BeginObservingDocument();
1548 // Here, we inform nsPresContext of the page size. Note that 'adjSize' is
1549 // *usually* the page size, but we need to check. Strictly speaking, adjSize
1550 // is the *device output size*, which is really the dimensions of a "sheet"
1551 // rather than a "page" (an important distinction in an N-pages-per-sheet
1552 // scenario). For some pages-per-sheet values, the pages are orthogonal to
1553 // the sheet; we adjust for that here by swapping the width with the height.
1554 nsSize pageSize
= adjSize
;
1555 if (printData
->mPrintSettings
->HasOrthogonalSheetsAndPages()) {
1556 std::swap(pageSize
.width
, pageSize
.height
);
1559 // If the document has a specified CSS page-size, we rotate the page to
1560 // reflect this. Changing the orientation is reflected by the result of
1561 // FinishPrintPreview, so that the frontend can reflect this.
1562 // The new document has not yet been reflowed, so we have to query the
1563 // original document for any CSS page-size.
1564 if (const Maybe
<StyleOrientation
> maybeOrientation
=
1565 aPO
->mDocument
->GetPresShell()
1567 ->GetDefaultPageOrientation()) {
1568 if (maybeOrientation
.value() == StyleOrientation::Landscape
&&
1569 pageSize
.width
< pageSize
.height
) {
1570 // Paper is in portrait, CSS page size is landscape.
1571 std::swap(pageSize
.width
, pageSize
.height
);
1572 } else if (maybeOrientation
.value() == StyleOrientation::Portrait
&&
1573 pageSize
.width
> pageSize
.height
) {
1574 // Paper is in landscape, CSS page size is portrait.
1575 std::swap(pageSize
.width
, pageSize
.height
);
1578 aPO
->mPresContext
->SetPageSize(pageSize
);
1580 int32_t p2a
= aPO
->mPresContext
->DeviceContext()->AppUnitsPerDevPixel();
1581 if (documentIsTopLevel
&& mIsCreatingPrintPreview
) {
1582 if (nsCOMPtr
<nsIContentViewer
> cv
= do_QueryInterface(mDocViewerPrint
)) {
1583 // If we're print-previewing and the top level document, use the bounds
1584 // from our doc viewer. Page bounds is not what we want.
1586 cv
->GetBounds(bounds
);
1587 adjSize
= nsSize(bounds
.width
* p2a
, bounds
.height
* p2a
);
1590 aPO
->mPresContext
->SetVisibleArea(nsRect(nsPoint(), adjSize
));
1591 aPO
->mPresContext
->SetIsRootPaginatedDocument(documentIsTopLevel
);
1592 aPO
->mPresContext
->SetPageScale(aPO
->mZoomRatio
);
1593 // Calculate scale factor from printer to screen
1594 float printDPI
= float(AppUnitsPerCSSInch()) / float(p2a
);
1595 aPO
->mPresContext
->SetPrintPreviewScale(mScreenDPI
/ printDPI
);
1597 if (mIsCreatingPrintPreview
&& documentIsTopLevel
) {
1598 mDocViewerPrint
->SetPrintPreviewPresentation(
1599 aPO
->mViewManager
, aPO
->mPresContext
, aPO
->mPresShell
.get());
1602 MOZ_TRY(aPO
->mPresShell
->Initialize());
1603 NS_ASSERTION(aPO
->mPresShell
, "Presshell should still be here");
1605 // Process the reflow event Initialize posted
1606 RefPtr
<PresShell
> presShell
= aPO
->mPresShell
;
1607 presShell
->FlushPendingNotifications(FlushType::Layout
);
1609 MOZ_TRY(UpdateSelectionAndShrinkPrintObject(aPO
.get(), documentIsTopLevel
));
1611 #ifdef EXTENDED_DEBUG_PRINTING
1612 if (MOZ_LOG_TEST(gPrintingLog
, DUMP_LAYOUT_LEVEL
)) {
1613 nsAutoCString docStr
;
1614 nsAutoCString urlStr
;
1615 GetDocTitleAndURL(aPO
, docStr
, urlStr
);
1617 sprintf(filename
, "print_dump_%d.txt", gDumpFileNameCnt
++);
1618 // Dump all the frames and view to a a file
1619 FILE* fd
= fopen(filename
, "w");
1621 nsIFrame
* theRootFrame
= aPO
->mPresShell
->GetRootFrame();
1622 fprintf(fd
, "Title: %s\n", docStr
.get());
1623 fprintf(fd
, "URL: %s\n", urlStr
.get());
1624 fprintf(fd
, "--------------- Frames ----------------\n");
1625 // RefPtr<gfxContext> renderingContext =
1626 // printData->mPrintDocDC->CreateRenderingContext();
1627 RootFrameList(aPO
->mPresContext
, fd
, 0);
1628 // DumpFrames(fd, aPO->mPresContext, renderingContext, theRootFrame, 0);
1629 fprintf(fd
, "---------------------------------------\n\n");
1630 fprintf(fd
, "--------------- Views From Root Frame----------------\n");
1631 nsView
* v
= theRootFrame
->GetView();
1635 printf("View is null!\n");
1637 if (aPO
->mDocShell
) {
1638 fprintf(fd
, "--------------- All Views ----------------\n");
1639 DumpViews(aPO
->mDocShell
, fd
);
1640 fprintf(fd
, "---------------------------------------\n\n");
1650 //-------------------------------------------------------
1651 // Figure out how many documents and how many total pages we are printing
1652 void nsPrintJob::CalcNumPrintablePages(int32_t& aNumPages
) {
1654 // Count the number of printable documents
1655 // and printable pages
1656 for (uint32_t i
= 0; i
< mPrt
->mPrintDocList
.Length(); i
++) {
1657 nsPrintObject
* po
= mPrt
->mPrintDocList
.ElementAt(i
);
1658 NS_ASSERTION(po
, "nsPrintObject can't be null!");
1659 // Note: The po->mPresContext null-check below is necessary, because it's
1660 // possible po->mPresContext might never have been set. (e.g., if
1661 // PrintingIsEnabled() returns false, ReflowPrintObject bails before setting
1663 if (po
->mPresContext
&& po
->mPresContext
->IsRootPaginatedDocument()) {
1664 nsPageSequenceFrame
* seqFrame
= po
->mPresShell
->GetPageSequenceFrame();
1666 aNumPages
+= seqFrame
->PrincipalChildList().GetLength();
1672 //-----------------------------------------------------------------
1673 //-- Done: Reflow Methods
1674 //-----------------------------------------------------------------
1676 //-----------------------------------------------------------------
1677 //-- Section: Printing Methods
1678 //-----------------------------------------------------------------
1680 //-------------------------------------------------------
1681 // Called for each DocShell that needs to be printed
1682 bool nsPrintJob::PrintDocContent(const UniquePtr
<nsPrintObject
>& aPO
,
1683 nsresult
& aStatus
) {
1684 NS_ASSERTION(aPO
, "Pointer is null!");
1687 if (!aPO
->mHasBeenPrinted
&& aPO
->PrintingIsEnabled()) {
1688 aStatus
= DoPrint(aPO
);
1692 // If |aPO->mHasBeenPrinted| is true,
1693 // the kids frames are already processed in |PrintPage|.
1694 // XXX This should be removed. Since bug 1552785 it has no longer been
1695 // possible for us to have to print multiple subdocuments consecutively.
1696 if (!aPO
->mHasBeenPrinted
&& !aPO
->mInvisible
) {
1697 for (const UniquePtr
<nsPrintObject
>& po
: aPO
->mKids
) {
1698 bool printed
= PrintDocContent(po
, aStatus
);
1699 if (printed
|| NS_FAILED(aStatus
)) {
1707 // A helper struct to aid with DeleteNonSelectedNodes.
1708 struct MOZ_STACK_CLASS SelectionRangeState
{
1709 explicit SelectionRangeState(RefPtr
<Selection
> aSelection
)
1710 : mSelection(std::move(aSelection
)) {
1711 MOZ_ASSERT(mSelection
);
1712 MOZ_ASSERT(!mSelection
->RangeCount());
1715 // Selects all the nodes that are _not_ included in a given set of ranges.
1716 MOZ_CAN_RUN_SCRIPT
void SelectComplementOf(Span
<const RefPtr
<nsRange
>>);
1717 // Removes the selected ranges from the document.
1718 MOZ_CAN_RUN_SCRIPT
void RemoveSelectionFromDocument();
1726 MOZ_CAN_RUN_SCRIPT
void SelectRange(nsRange
*);
1727 MOZ_CAN_RUN_SCRIPT
void SelectNodesExcept(const Position
& aStart
,
1728 const Position
& aEnd
);
1729 MOZ_CAN_RUN_SCRIPT
void SelectNodesExceptInSubtree(const Position
& aStart
,
1730 const Position
& aEnd
);
1732 // A map from subtree root (document or shadow root) to the start position of
1733 // the non-selected content (so far).
1734 nsTHashMap
<nsPtrHashKey
<nsINode
>, Position
> mPositions
;
1736 // The selection we're adding the ranges to.
1737 const RefPtr
<Selection
> mSelection
;
1740 void SelectionRangeState::SelectComplementOf(
1741 Span
<const RefPtr
<nsRange
>> aRanges
) {
1742 for (const auto& range
: aRanges
) {
1743 auto start
= Position
{range
->GetStartContainer(), range
->StartOffset()};
1744 auto end
= Position
{range
->GetEndContainer(), range
->EndOffset()};
1745 SelectNodesExcept(start
, end
);
1749 void SelectionRangeState::SelectRange(nsRange
* aRange
) {
1750 if (aRange
&& !aRange
->Collapsed()) {
1751 mSelection
->AddRangeAndSelectFramesAndNotifyListeners(*aRange
,
1756 void SelectionRangeState::SelectNodesExcept(const Position
& aStart
,
1757 const Position
& aEnd
) {
1758 SelectNodesExceptInSubtree(aStart
, aEnd
);
1759 if (auto* shadow
= ShadowRoot::FromNode(aStart
.mNode
->SubtreeRoot())) {
1760 auto* host
= shadow
->Host();
1761 SelectNodesExcept(Position
{host
, 0}, Position
{host
, host
->GetChildCount()});
1763 MOZ_ASSERT(aStart
.mNode
->IsInUncomposedDoc());
1767 void SelectionRangeState::SelectNodesExceptInSubtree(const Position
& aStart
,
1768 const Position
& aEnd
) {
1769 static constexpr auto kEllipsis
= u
"\x2026"_ns
;
1771 nsINode
* root
= aStart
.mNode
->SubtreeRoot();
1773 mPositions
.WithEntryHandle(root
, [&](auto&& entry
) -> Position
& {
1774 return entry
.OrInsertWith([&] { return Position
{root
, 0}; });
1777 bool ellipsizedStart
= false;
1778 if (auto* text
= Text::FromNode(aStart
.mNode
)) {
1779 if (start
.mNode
!= text
&& aStart
.mOffset
&&
1780 aStart
.mOffset
< text
->Length()) {
1781 text
->InsertData(aStart
.mOffset
, kEllipsis
, IgnoreErrors());
1782 ellipsizedStart
= true;
1786 RefPtr
<nsRange
> range
= nsRange::Create(
1787 start
.mNode
, start
.mOffset
, aStart
.mNode
, aStart
.mOffset
, IgnoreErrors());
1792 // If we added an ellipsis at the start and the end position was relative to
1793 // the same node account for it here.
1794 if (ellipsizedStart
&& aStart
.mNode
== aEnd
.mNode
) {
1795 start
.mOffset
+= kEllipsis
.Length();
1798 // If the end is mid text then add an ellipsis.
1799 if (auto* text
= Text::FromNode(start
.mNode
)) {
1800 if (start
.mOffset
&& start
.mOffset
< text
->Length()) {
1801 text
->InsertData(start
.mOffset
, kEllipsis
, IgnoreErrors());
1802 start
.mOffset
+= kEllipsis
.Length();
1807 void SelectionRangeState::RemoveSelectionFromDocument() {
1808 for (auto& entry
: mPositions
) {
1809 const Position
& pos
= entry
.GetData();
1810 nsINode
* root
= entry
.GetKey();
1811 RefPtr
<nsRange
> range
= nsRange::Create(
1812 pos
.mNode
, pos
.mOffset
, root
, root
->GetChildCount(), IgnoreErrors());
1815 mSelection
->DeleteFromDocument(IgnoreErrors());
1819 * Builds the complement set of ranges and adds those to the selection.
1820 * Deletes all of the nodes contained in the complement set of ranges
1821 * leaving behind only nodes that were originally selected.
1822 * Adds ellipses to a selected node's text if text is truncated by a range.
1823 * This is used to implement the "Print Selection Only" user interface option.
1825 MOZ_CAN_RUN_SCRIPT_BOUNDARY
static nsresult
DeleteNonSelectedNodes(
1827 MOZ_ASSERT(aDoc
.IsStaticDocument());
1828 const auto* printRanges
= static_cast<nsTArray
<RefPtr
<nsRange
>>*>(
1829 aDoc
.GetProperty(nsGkAtoms::printselectionranges
));
1834 PresShell
* presShell
= aDoc
.GetPresShell();
1835 NS_ENSURE_STATE(presShell
);
1836 RefPtr
<Selection
> selection
=
1837 presShell
->GetCurrentSelection(SelectionType::eNormal
);
1838 NS_ENSURE_STATE(selection
);
1840 SelectionRangeState
state(std::move(selection
));
1841 state
.SelectComplementOf(*printRanges
);
1842 state
.RemoveSelectionFromDocument();
1846 //-------------------------------------------------------
1847 nsresult
nsPrintJob::DoPrint(const UniquePtr
<nsPrintObject
>& aPO
) {
1849 PR_PL(("**************************** %s ****************************\n",
1850 gFrameTypesStr
[aPO
->mFrameType
]));
1851 PR_PL(("****** In DV::DoPrint PO: %p \n", aPO
.get()));
1853 PresShell
* poPresShell
= aPO
->mPresShell
;
1854 nsPresContext
* poPresContext
= aPO
->mPresContext
;
1856 NS_ASSERTION(poPresContext
, "PrintObject has not been reflowed");
1857 NS_ASSERTION(poPresContext
->Type() != nsPresContext::eContext_PrintPreview
,
1858 "How did this context end up here?");
1860 // Guarantee that mPrt and the objects it owns won't be deleted in this method
1861 // because it might be cleared if other modules called from here may fire
1862 // events, notifying observers and/or listeners.
1863 RefPtr
<nsPrintData
> printData
= mPrt
;
1864 if (NS_WARN_IF(!printData
)) {
1865 return NS_ERROR_FAILURE
;
1869 // Ask the page sequence frame to print all the pages
1870 nsPageSequenceFrame
* seqFrame
= poPresShell
->GetPageSequenceFrame();
1871 MOZ_ASSERT(seqFrame
, "no page sequence frame");
1873 // We are done preparing for printing, so we can turn this off
1874 printData
->mPreparingForPrint
= false;
1876 #ifdef EXTENDED_DEBUG_PRINTING
1877 nsIFrame
* rootFrame
= poPresShell
->GetRootFrame();
1878 if (aPO
->PrintingIsEnabled()) {
1879 nsAutoCString docStr
;
1880 nsAutoCString urlStr
;
1881 GetDocTitleAndURL(aPO
, docStr
, urlStr
);
1882 DumpLayoutData(docStr
.get(), urlStr
.get(), poPresContext
,
1883 printData
->mPrintDC
, rootFrame
, aPO
->mDocShell
, nullptr);
1887 if (!printData
->mPrintSettings
) {
1888 // not sure what to do here!
1889 SetIsPrinting(false);
1890 return NS_ERROR_FAILURE
;
1893 nsAutoString docTitleStr
;
1894 nsAutoString docURLStr
;
1895 GetDisplayTitleAndURL(*aPO
->mDocument
, mPrt
->mPrintSettings
,
1896 DocTitleDefault::eFallback
, docTitleStr
, docURLStr
);
1899 SetIsPrinting(false);
1900 return NS_ERROR_FAILURE
;
1903 // For telemetry, get paper size being used; convert the dimensions to
1904 // points and ensure they reflect portrait orientation.
1905 nsIPrintSettings
* settings
= printData
->mPrintSettings
;
1906 double paperWidth
, paperHeight
;
1907 settings
->GetPaperWidth(&paperWidth
);
1908 settings
->GetPaperHeight(&paperHeight
);
1910 settings
->GetPaperSizeUnit(&sizeUnit
);
1912 case nsIPrintSettings::kPaperSizeInches
:
1914 paperHeight
*= 72.0;
1916 case nsIPrintSettings::kPaperSizeMillimeters
:
1917 paperWidth
*= 72.0 / 25.4;
1918 paperHeight
*= 72.0 / 25.4;
1921 MOZ_ASSERT_UNREACHABLE("unknown paper size unit");
1924 if (paperWidth
> paperHeight
) {
1925 std::swap(paperWidth
, paperHeight
);
1927 // Use the paper size to build a Telemetry Scalar key.
1929 key
.AppendInt(int32_t(NS_round(paperWidth
)));
1931 key
.AppendInt(int32_t(NS_round(paperHeight
)));
1932 Telemetry::ScalarAdd(Telemetry::ScalarID::PRINTING_PAPER_SIZE
, key
, 1);
1934 mPageSeqFrame
= seqFrame
;
1935 seqFrame
->StartPrint(poPresContext
, settings
, docTitleStr
, docURLStr
);
1937 // Schedule Page to Print
1938 PR_PL(("Scheduling Print of PO: %p (%s) \n", aPO
.get(),
1939 gFrameTypesStr
[aPO
->mFrameType
]));
1940 StartPagePrintTimer(aPO
);
1946 //-------------------------------------------------------
1947 bool nsPrintJob::PrePrintSheet() {
1948 NS_ASSERTION(mPageSeqFrame
.IsAlive(), "mPageSeqFrame is not alive!");
1949 NS_ASSERTION(mPrt
, "mPrt is null!");
1951 // Although these should NEVER be nullptr
1952 // This is added insurance, to make sure we don't crash in optimized builds
1953 if (!mPrt
|| !mPageSeqFrame
.IsAlive()) {
1954 return true; // means we are done preparing the sheet.
1957 // Guarantee that mPrt won't be deleted during a call of
1958 // FirePrintingErrorEvent().
1959 RefPtr
<nsPrintData
> printData
= mPrt
;
1961 // Check setting to see if someone request it be cancelled
1962 bool isCancelled
= false;
1963 printData
->mPrintSettings
->GetIsCancelled(&isCancelled
);
1964 if (isCancelled
) return true;
1966 // Ask mPageSeqFrame if the sheet is ready to be printed.
1967 // If the sheet doesn't get printed at all, the |done| will be |true|.
1969 nsPageSequenceFrame
* pageSeqFrame
= do_QueryFrame(mPageSeqFrame
.GetFrame());
1970 nsresult rv
= pageSeqFrame
->PrePrintNextSheet(mPagePrintTimer
, &done
);
1971 if (NS_FAILED(rv
)) {
1972 // ??? ::PrintSheet doesn't set |printData->mIsAborted = true| if
1973 // rv != NS_ERROR_ABORT, but I don't really understand why this should be
1974 // the right thing to do? Shouldn't |printData->mIsAborted| set to true
1975 // all the time if something went wrong?
1976 if (rv
!= NS_ERROR_ABORT
) {
1977 FirePrintingErrorEvent(rv
);
1978 printData
->mIsAborted
= true;
1985 bool nsPrintJob::PrintSheet(nsPrintObject
* aPO
, bool& aInRange
) {
1986 NS_ASSERTION(aPO
, "aPO is null!");
1987 NS_ASSERTION(mPageSeqFrame
.IsAlive(), "mPageSeqFrame is not alive!");
1988 NS_ASSERTION(mPrt
, "mPrt is null!");
1990 // XXXdholbert Nowadays, this function doesn't need to concern itself with
1991 // page ranges -- page-range handling is now handled when we reflow our
1992 // PrintedSheetFrames, and all PrintedSheetFrames are "in-range" and should
1993 // be printed. So this outparam is unconditionally true. Bug 1669815 is filed
1994 // on removing it entirely.
1997 // Although these should NEVER be nullptr
1998 // This is added insurance, to make sure we don't crash in optimized builds
1999 if (!mPrt
|| !aPO
|| !mPageSeqFrame
.IsAlive()) {
2000 FirePrintingErrorEvent(NS_ERROR_FAILURE
);
2001 return true; // means we are done printing
2004 // Guarantee that mPrt won't be deleted during a call of
2005 // nsPrintData::DoOnProgressChange() which runs some listeners,
2006 // which may clear (& might otherwise destroy).
2007 RefPtr
<nsPrintData
> printData
= mPrt
;
2009 PR_PL(("-----------------------------------\n"));
2010 PR_PL(("------ In DV::PrintSheet PO: %p (%s)\n", aPO
,
2011 gFrameTypesStr
[aPO
->mFrameType
]));
2013 // Check setting to see if someone request it be cancelled
2014 bool isCancelled
= false;
2015 printData
->mPrintSettings
->GetIsCancelled(&isCancelled
);
2016 if (isCancelled
|| printData
->mIsAborted
) {
2020 nsPageSequenceFrame
* pageSeqFrame
= do_QueryFrame(mPageSeqFrame
.GetFrame());
2021 const uint32_t sheetIdx
= pageSeqFrame
->GetCurrentSheetIdx();
2022 const uint32_t numSheets
= pageSeqFrame
->PrincipalChildList().GetLength();
2024 PR_PL(("****** Printing sheet index %d of %d sheets(s)\n", sheetIdx
,
2027 MOZ_ASSERT(numSheets
> 0, "print operations must have at least 1 sheet");
2028 MOZ_ASSERT(sheetIdx
< numSheets
,
2029 "sheetIdx shouldn't be allowed to go out of bounds");
2030 printData
->DoOnProgressChange(sheetIdx
, numSheets
, false, 0);
2031 if (NS_WARN_IF(mPrt
!= printData
)) {
2032 // If current printing is canceled or new print is started, let's return
2033 // true to notify the caller of current printing is done.
2038 // if a print job was cancelled externally, an EndPage or BeginPage may
2039 // fail and the failure is passed back here.
2040 // Returning true means we are done printing.
2042 // When rv == NS_ERROR_ABORT, it means we want out of the
2043 // print job without displaying any error messages
2044 nsresult rv
= pageSeqFrame
->PrintNextSheet();
2045 if (NS_FAILED(rv
)) {
2046 if (rv
!= NS_ERROR_ABORT
) {
2047 FirePrintingErrorEvent(rv
);
2048 printData
->mIsAborted
= true;
2053 pageSeqFrame
->DoPageEnd();
2055 // If we just printed the final sheet (the one with index "numSheets-1"),
2057 return (sheetIdx
== numSheets
- 1);
2060 void nsPrintJob::PageDone(nsresult aResult
) {
2061 MOZ_ASSERT(mIsDoingPrinting
);
2063 // mPagePrintTimer might be released during RemotePrintFinished, keep a
2064 // reference here to make sure it lives long enough.
2065 RefPtr
<nsPagePrintTimer
> timer
= mPagePrintTimer
;
2066 timer
->RemotePrintFinished();
2069 //-----------------------------------------------------------------
2070 //-- Done: Printing Methods
2071 //-----------------------------------------------------------------
2073 //-----------------------------------------------------------------
2074 //-- Section: Misc Support Methods
2075 //-----------------------------------------------------------------
2077 //---------------------------------------------------------------------
2078 void nsPrintJob::SetIsPrinting(bool aIsPrinting
) {
2079 mIsDoingPrinting
= aIsPrinting
;
2081 mHasEverPrinted
= true;
2083 if (mPrt
&& aIsPrinting
) {
2084 mPrt
->mPreparingForPrint
= true;
2088 //---------------------------------------------------------------------
2089 void nsPrintJob::SetIsPrintPreview(bool aIsPrintPreview
) {
2090 mCreatedForPrintPreview
= aIsPrintPreview
;
2092 if (mDocViewerPrint
) {
2093 mDocViewerPrint
->SetIsPrintPreview(aIsPrintPreview
);
2097 Document
* nsPrintJob::FindFocusedDocument(Document
* aDoc
) const {
2098 nsFocusManager
* fm
= nsFocusManager::GetFocusManager();
2099 NS_ENSURE_TRUE(fm
, nullptr);
2101 nsPIDOMWindowOuter
* window
= aDoc
->GetOriginalDocument()->GetWindow();
2102 NS_ENSURE_TRUE(window
, nullptr);
2104 nsCOMPtr
<nsPIDOMWindowOuter
> rootWindow
= window
->GetPrivateRoot();
2105 NS_ENSURE_TRUE(rootWindow
, nullptr);
2107 nsCOMPtr
<nsPIDOMWindowOuter
> focusedWindow
;
2108 nsFocusManager::GetFocusedDescendant(rootWindow
,
2109 nsFocusManager::eIncludeAllDescendants
,
2110 getter_AddRefs(focusedWindow
));
2111 NS_ENSURE_TRUE(focusedWindow
, nullptr);
2113 if (IsWindowsInOurSubTree(focusedWindow
)) {
2114 return focusedWindow
->GetDoc();
2120 //---------------------------------------------------------------------
2121 bool nsPrintJob::IsWindowsInOurSubTree(nsPIDOMWindowOuter
* window
) const {
2123 nsCOMPtr
<nsIDocShell
> ourDocShell(do_QueryReferent(mDocShell
));
2125 BrowsingContext
* ourBC
= ourDocShell
->GetBrowsingContext();
2126 BrowsingContext
* bc
= window
->GetBrowsingContext();
2131 bc
= bc
->GetParent();
2138 //-------------------------------------------------------
2139 bool nsPrintJob::DonePrintingSheets(nsPrintObject
* aPO
, nsresult aResult
) {
2140 // NS_ASSERTION(aPO, "Pointer is null!");
2141 PR_PL(("****** In DV::DonePrintingSheets PO: %p (%s)\n", aPO
,
2142 aPO
? gFrameTypesStr
[aPO
->mFrameType
] : ""));
2144 // If there is a pageSeqFrame, make sure there are no more printCanvas active
2145 // that might call |Notify| on the pagePrintTimer after things are cleaned up
2146 // and printing was marked as being done.
2147 if (mPageSeqFrame
.IsAlive()) {
2148 nsPageSequenceFrame
* pageSeqFrame
= do_QueryFrame(mPageSeqFrame
.GetFrame());
2149 pageSeqFrame
->ResetPrintCanvasList();
2152 // Guarantee that mPrt and mPrt->mPrintObject won't be deleted during a
2153 // call of PrintDocContent() and FirePrintCompletionEvent().
2154 RefPtr
<nsPrintData
> printData
= mPrt
;
2156 if (aPO
&& !printData
->mIsAborted
) {
2157 aPO
->mHasBeenPrinted
= true;
2159 bool didPrint
= PrintDocContent(printData
->mPrintObject
, rv
);
2160 if (NS_SUCCEEDED(rv
) && didPrint
) {
2162 ("****** In DV::DonePrintingSheets PO: %p (%s) didPrint:%s (Not Done "
2164 aPO
, gFrameTypesStr
[aPO
->mFrameType
], PRT_YESNO(didPrint
)));
2169 if (NS_SUCCEEDED(aResult
)) {
2170 FirePrintCompletionEvent();
2171 // XXX mPrt may be cleared or replaced with new instance here.
2172 // However, the following methods will clean up with new mPrt or will
2173 // do nothing due to no proper nsPrintData instance.
2176 SetIsPrinting(false);
2178 // Release reference to mPagePrintTimer; the timer object destroys itself
2179 // after this returns true
2180 DisconnectPagePrintTimer();
2185 //-------------------------------------------------------
2186 nsresult
nsPrintJob::EnablePOsForPrinting() {
2187 // Guarantee that mPrt and the objects it owns won't be deleted.
2188 RefPtr
<nsPrintData
> printData
= mPrt
;
2190 // NOTE: All POs have been "turned off" for printing
2191 // this is where we decided which POs get printed.
2193 if (!printData
|| !printData
->mPrintSettings
) {
2194 return NS_ERROR_FAILURE
;
2198 PR_PL(("********* nsPrintJob::EnablePOsForPrinting *********\n"));
2200 if (!printData
->mPrintSettings
->GetPrintSelectionOnly()) {
2201 printData
->mPrintObject
->EnablePrinting(true);
2205 // This means we are either printing a selected iframe or
2206 // we are printing the current selection.
2207 NS_ENSURE_STATE(!mDisallowSelectionPrint
&& printData
->mSelectionRoot
);
2209 // If mSelectionRoot is a selected iframe without a selection, then just
2210 // enable normally from that point.
2211 if (printData
->mSelectionRoot
->mFrameType
== eIFrame
&&
2212 !printData
->mSelectionRoot
->HasSelection()) {
2213 printData
->mSelectionRoot
->EnablePrinting(true);
2215 // Otherwise, only enable nsPrintObjects that have a selection.
2216 printData
->mSelectionRoot
->EnablePrintingSelectionOnly();
2221 //-------------------------------------------------------
2222 // Return the nsPrintObject with that is XMost (The widest frameset frame) AND
2223 // contains the XMost (widest) layout frame
2224 nsPrintObject
* nsPrintJob::FindSmallestSTF() {
2225 float smallestRatio
= 1.0f
;
2226 nsPrintObject
* smallestPO
= nullptr;
2228 for (uint32_t i
= 0; i
< mPrt
->mPrintDocList
.Length(); i
++) {
2229 nsPrintObject
* po
= mPrt
->mPrintDocList
.ElementAt(i
);
2230 NS_ASSERTION(po
, "nsPrintObject can't be null!");
2231 if (po
->mFrameType
!= eFrameSet
&& po
->mFrameType
!= eIFrame
) {
2232 if (po
->mShrinkRatio
< smallestRatio
) {
2233 smallestRatio
= po
->mShrinkRatio
;
2239 #ifdef EXTENDED_DEBUG_PRINTING
2241 printf("*PO: %p Type: %d %10.3f\n", smallestPO
, smallestPO
->mFrameType
,
2242 smallestPO
->mShrinkRatio
);
2247 //-----------------------------------------------------------------
2248 //-- Done: Misc Support Methods
2249 //-----------------------------------------------------------------
2251 //-----------------------------------------------------------------
2252 //-- Section: Finishing up or Cleaning up
2253 //-----------------------------------------------------------------
2255 //-----------------------------------------------------------------
2256 nsresult
nsPrintJob::FinishPrintPreview() {
2257 nsresult rv
= NS_OK
;
2259 #ifdef NS_PRINT_PREVIEW
2261 // If mPrt is null we've already finished with print preview. If mPrt is not
2262 // null but mIsCreatingPrintPreview is false FinishPrintPreview must have
2263 // already failed due to DocumentReadyForPrinting failing.
2264 if (!mPrt
|| !mIsCreatingPrintPreview
) {
2268 rv
= DocumentReadyForPrinting();
2270 // Note that this method may be called while the instance is being
2271 // initialized. Some methods which initialize the instance (e.g.,
2272 // DoCommonPrint) may need to stop initializing and return error if
2273 // this is called. Therefore it's important to set mIsCreatingPrintPreview
2274 // state to false here. If you need to stop setting that here, you need to
2275 // keep them being able to check whether the owner stopped using this
2277 mIsCreatingPrintPreview
= false;
2279 // mPrt may be cleared during a call of nsPrintData::OnEndPrinting()
2280 // because that method invokes some arbitrary listeners.
2281 RefPtr
<nsPrintData
> printData
= mPrt
;
2282 if (NS_FAILED(rv
)) {
2283 /* cleanup done, let's fire-up an error dialog to notify the user
2284 * what went wrong...
2286 printData
->OnEndPrinting();
2291 if (mPrintPreviewCallback
) {
2292 const bool hasSelection
=
2293 !mDisallowSelectionPrint
&& printData
->mSelectionRoot
;
2294 // Determine if there is a specified page size, and if we should set the
2295 // paper orientation to match it.
2296 const Maybe
<bool> maybeLandscape
=
2297 printData
->mPrintObject
->mPresShell
->StyleSet()
2298 ->GetDefaultPageOrientation()
2299 .map([](StyleOrientation o
) -> bool {
2300 return o
== StyleOrientation::Landscape
;
2302 mPrintPreviewCallback(PrintPreviewResultInfo(
2303 GetPrintPreviewNumSheets(), GetRawNumPages(), GetIsEmpty(),
2304 hasSelection
, hasSelection
&& printData
->mPrintObject
->HasSelection(),
2306 mPrintPreviewCallback
= nullptr;
2309 // At this point we are done preparing everything
2310 // before it is to be created
2312 printData
->OnEndPrinting();
2313 // XXX If mPrt becomes nullptr or different instance here, what should we
2316 // PrintPreview was built using the mPrt (code reuse)
2317 // then we assign it over
2318 mPrtPreview
= std::move(mPrt
);
2320 #endif // NS_PRINT_PREVIEW
2325 //-----------------------------------------------------------------
2326 //-- Done: Finishing up or Cleaning up
2327 //-----------------------------------------------------------------
2329 /*=============== Timer Related Code ======================*/
2330 nsresult
nsPrintJob::StartPagePrintTimer(const UniquePtr
<nsPrintObject
>& aPO
) {
2331 if (!mPagePrintTimer
) {
2332 // Get the delay time in between the printing of each page
2333 // this gives the user more time to press cancel
2334 int32_t printPageDelay
= mPrt
->mPrintSettings
->GetPrintPageDelay();
2336 nsCOMPtr
<nsIContentViewer
> cv
= do_QueryInterface(mDocViewerPrint
);
2337 NS_ENSURE_TRUE(cv
, NS_ERROR_FAILURE
);
2338 nsCOMPtr
<Document
> doc
= cv
->GetDocument();
2339 NS_ENSURE_TRUE(doc
, NS_ERROR_FAILURE
);
2342 new nsPagePrintTimer(this, mDocViewerPrint
, doc
, printPageDelay
);
2344 nsCOMPtr
<nsIPrintSession
> printSession
;
2346 mPrt
->mPrintSettings
->GetPrintSession(getter_AddRefs(printSession
));
2347 if (NS_SUCCEEDED(rv
) && printSession
) {
2348 RefPtr
<layout::RemotePrintJobChild
> remotePrintJob
=
2349 printSession
->GetRemotePrintJob();
2350 if (remotePrintJob
) {
2351 remotePrintJob
->SetPagePrintTimer(mPagePrintTimer
);
2352 remotePrintJob
->SetPrintJob(this);
2357 return mPagePrintTimer
->Start(aPO
.get());
2360 //---------------------------------------------------------------
2361 //-- PLEvent Notification
2362 //---------------------------------------------------------------
2363 class nsPrintCompletionEvent
: public Runnable
{
2365 explicit nsPrintCompletionEvent(nsIDocumentViewerPrint
* docViewerPrint
)
2366 : mozilla::Runnable("nsPrintCompletionEvent"),
2367 mDocViewerPrint(docViewerPrint
) {
2368 NS_ASSERTION(mDocViewerPrint
, "mDocViewerPrint is null.");
2371 NS_IMETHOD
Run() override
{
2372 if (mDocViewerPrint
) mDocViewerPrint
->OnDonePrinting();
2377 nsCOMPtr
<nsIDocumentViewerPrint
> mDocViewerPrint
;
2380 //-----------------------------------------------------------
2381 void nsPrintJob::FirePrintCompletionEvent() {
2382 MOZ_ASSERT(NS_IsMainThread());
2383 nsCOMPtr
<nsIRunnable
> event
= new nsPrintCompletionEvent(mDocViewerPrint
);
2384 nsCOMPtr
<nsIContentViewer
> cv
= do_QueryInterface(mDocViewerPrint
);
2385 NS_ENSURE_TRUE_VOID(cv
);
2386 nsCOMPtr
<Document
> doc
= cv
->GetDocument();
2387 NS_ENSURE_TRUE_VOID(doc
);
2389 NS_ENSURE_SUCCESS_VOID(doc
->Dispatch(TaskCategory::Other
, event
.forget()));
2392 void nsPrintJob::DisconnectPagePrintTimer() {
2393 if (mPagePrintTimer
) {
2394 mPagePrintTimer
->Disconnect();
2395 mPagePrintTimer
= nullptr;
2399 //---------------------------------------------------------------
2400 //---------------------------------------------------------------
2401 //-- Debug helper routines
2402 //---------------------------------------------------------------
2403 //---------------------------------------------------------------
2404 #if defined(XP_WIN) && defined(EXTENDED_DEBUG_PRINTING)
2405 # include <windows.h>
2406 # include <process.h>
2407 # include <direct.h>
2409 # define MY_FINDFIRST(a, b) FindFirstFile(a, b)
2410 # define MY_FINDNEXT(a, b) FindNextFile(a, b)
2411 # define ISDIR(a) (a.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
2412 # define MY_FINDCLOSE(a) FindClose(a)
2413 # define MY_FILENAME(a) a.cFileName
2414 # define MY_FILESIZE(a) (a.nFileSizeHigh * MAXDWORD) + a.nFileSizeLow
2416 int RemoveFilesInDir(const char* aDir
) {
2417 WIN32_FIND_DATA data_ptr
;
2420 char path
[MAX_PATH
];
2424 // Append slash to the end of the directory names if not there
2425 if (path
[strlen(path
) - 1] != '\\') strcat(path
, "\\");
2427 char findPath
[MAX_PATH
];
2428 strcpy(findPath
, path
);
2429 strcat(findPath
, "*.*");
2431 find_handle
= MY_FINDFIRST(findPath
, &data_ptr
);
2433 if (find_handle
!= INVALID_HANDLE_VALUE
) {
2435 if (ISDIR(data_ptr
) && (stricmp(MY_FILENAME(data_ptr
), ".")) &&
2436 (stricmp(MY_FILENAME(data_ptr
), ".."))) {
2438 } else if (!ISDIR(data_ptr
)) {
2439 if (!strncmp(MY_FILENAME(data_ptr
), "print_dump", 10)) {
2440 char fileName
[MAX_PATH
];
2441 strcpy(fileName
, aDir
);
2442 strcat(fileName
, "\\");
2443 strcat(fileName
, MY_FILENAME(data_ptr
));
2444 printf("Removing %s\n", fileName
);
2448 } while (MY_FINDNEXT(find_handle
, &data_ptr
));
2449 MY_FINDCLOSE(find_handle
);
2455 #ifdef EXTENDED_DEBUG_PRINTING
2457 /** ---------------------------------------------------
2458 * Dumps Frames for Printing
2460 static void RootFrameList(nsPresContext
* aPresContext
, FILE* out
,
2461 const char* aPrefix
) {
2462 if (!aPresContext
|| !out
) return;
2464 if (PresShell
* presShell
= aPresContext
->GetPresShell()) {
2465 nsIFrame
* frame
= presShell
->GetRootFrame();
2467 frame
->List(out
, aPrefix
);
2472 /** ---------------------------------------------------
2473 * Dumps Frames for Printing
2475 static void DumpFrames(FILE* out
, nsPresContext
* aPresContext
,
2476 gfxContext
* aRendContext
, nsIFrame
* aFrame
,
2478 NS_ASSERTION(out
, "Pointer is null!");
2479 NS_ASSERTION(aPresContext
, "Pointer is null!");
2480 NS_ASSERTION(aRendContext
, "Pointer is null!");
2481 NS_ASSERTION(aFrame
, "Pointer is null!");
2483 nsIFrame
* child
= aFrame
->PrincipalChildList().FirstChild();
2484 while (child
!= nullptr) {
2485 for (int32_t i
= 0; i
< aLevel
; i
++) {
2489 child
->GetFrameName(tmp
);
2490 fputs(NS_LossyConvertUTF16toASCII(tmp
).get(), out
);
2492 if (child
->IsVisibleForPainting()) {
2493 fprintf(out
, " %p %s", child
, isSelected
? "VIS" : "UVS");
2494 nsRect rect
= child
->GetRect();
2495 fprintf(out
, "[%d,%d,%d,%d] ", rect
.x
, rect
.y
, rect
.width
, rect
.height
);
2496 fprintf(out
, "v: %p ", (void*)child
->GetView());
2498 DumpFrames(out
, aPresContext
, aRendContext
, child
, aLevel
+ 1);
2499 child
= child
->GetNextSibling();
2504 /** ---------------------------------------------------
2505 * Dumps the Views from the DocShell
2507 static void DumpViews(nsIDocShell
* aDocShell
, FILE* out
) {
2508 NS_ASSERTION(aDocShell
, "Pointer is null!");
2509 NS_ASSERTION(out
, "Pointer is null!");
2511 if (nullptr != aDocShell
) {
2512 fprintf(out
, "docshell=%p \n", aDocShell
);
2513 if (PresShell
* presShell
= aDocShell
->GetPresShell()) {
2514 nsViewManager
* vm
= presShell
->GetViewManager();
2516 nsView
* root
= vm
->GetRootView();
2522 fputs("null pres shell\n", out
);
2525 // dump the views of the sub documents
2527 BrowsingContext
* bc
= nsDocShell::Cast(aDocShell
)->GetBrowsingContext();
2528 for (auto& child
: bc
->Children()) {
2529 if (auto childDS
= child
->GetDocShell()) {
2530 DumpViews(childAsShell
, out
);
2536 /** ---------------------------------------------------
2537 * Dumps the Views and Frames
2539 void DumpLayoutData(const char* aTitleStr
, const char* aURLStr
,
2540 nsPresContext
* aPresContext
, nsDeviceContext
* aDC
,
2541 nsIFrame
* aRootFrame
, nsIDocShell
* aDocShell
,
2542 FILE* aFD
= nullptr) {
2543 if (!MOZ_LOG_TEST(gPrintingLog
, DUMP_LAYOUT_LEVEL
)) {
2547 if (aPresContext
== nullptr || aDC
== nullptr) {
2551 # ifdef NS_PRINT_PREVIEW
2552 if (aPresContext
->Type() == nsPresContext::eContext_PrintPreview
) {
2557 NS_ASSERTION(aRootFrame
, "Pointer is null!");
2558 NS_ASSERTION(aDocShell
, "Pointer is null!");
2560 // Dump all the frames and view to a a file
2562 sprintf(filename
, "print_dump_layout_%d.txt", gDumpLOFileNameCnt
++);
2563 FILE* fd
= aFD
? aFD
: fopen(filename
, "w");
2565 fprintf(fd
, "Title: %s\n", aTitleStr
? aTitleStr
: "");
2566 fprintf(fd
, "URL: %s\n", aURLStr
? aURLStr
: "");
2567 fprintf(fd
, "--------------- Frames ----------------\n");
2568 fprintf(fd
, "--------------- Frames ----------------\n");
2569 // RefPtr<gfxContext> renderingContext =
2570 // aDC->CreateRenderingContext();
2571 RootFrameList(aPresContext
, fd
, "");
2572 // DumpFrames(fd, aPresContext, renderingContext, aRootFrame, 0);
2573 fprintf(fd
, "---------------------------------------\n\n");
2574 fprintf(fd
, "--------------- Views From Root Frame----------------\n");
2575 nsView
* v
= aRootFrame
->GetView();
2579 printf("View is null!\n");
2582 fprintf(fd
, "--------------- All Views ----------------\n");
2583 DumpViews(aDocShell
, fd
);
2584 fprintf(fd
, "---------------------------------------\n\n");
2586 if (aFD
== nullptr) {
2592 //-------------------------------------------------------------
2593 static void DumpPrintObjectsList(const nsTArray
<nsPrintObject
*>& aDocList
) {
2594 if (!MOZ_LOG_TEST(gPrintingLog
, DUMP_LAYOUT_LEVEL
)) {
2598 const char types
[][3] = {"DC", "FR", "IF", "FS"};
2599 PR_PL(("Doc List\n***************************************************\n"));
2601 ("T P A H PO DocShell Seq Page Root Page# "
2603 for (nsPrintObject
* po
: aDocList
) {
2604 NS_ASSERTION(po
, "nsPrintObject can't be null!");
2605 nsIFrame
* rootFrame
= nullptr;
2606 if (po
->mPresShell
) {
2607 rootFrame
= po
->mPresShell
->GetRootFrame();
2608 while (rootFrame
!= nullptr) {
2609 nsPageSequenceFrame
* sqf
= do_QueryFrame(rootFrame
);
2613 rootFrame
= rootFrame
->PrincipalChildList().FirstChild();
2617 PR_PL(("%s %d %d %p %p %p\n", types
[po
->mFrameType
],
2618 po
->PrintingIsEnabled(), po
->mHasBeenPrinted
, po
,
2619 po
->mDocShell
.get(), rootFrame
));
2623 //-------------------------------------------------------------
2624 static void DumpPrintObjectsTree(nsPrintObject
* aPO
, int aLevel
, FILE* aFD
) {
2625 if (!MOZ_LOG_TEST(gPrintingLog
, DUMP_LAYOUT_LEVEL
)) {
2629 NS_ASSERTION(aPO
, "Pointer is null!");
2631 FILE* fd
= aFD
? aFD
: stdout
;
2632 const char types
[][3] = {"DC", "FR", "IF", "FS"};
2635 "DocTree\n***************************************************\n");
2636 fprintf(fd
, "T PO DocShell Seq Page Page# Rect\n");
2638 for (const auto& po
: aPO
->mKids
) {
2639 NS_ASSERTION(po
, "nsPrintObject can't be null!");
2640 for (int32_t k
= 0; k
< aLevel
; k
++) fprintf(fd
, " ");
2641 fprintf(fd
, "%s %p %p\n", types
[po
->mFrameType
], po
.get(),
2642 po
->mDocShell
.get());
2646 //-------------------------------------------------------------
2647 static void GetDocTitleAndURL(const UniquePtr
<nsPrintObject
>& aPO
,
2648 nsACString
& aDocStr
, nsACString
& aURLStr
) {
2649 nsAutoString docTitleStr
;
2650 nsAutoString docURLStr
;
2651 GetDocumentTitleAndURL(aPO
->mDocument
, docTitleStr
, docURLStr
);
2652 CopyUTF16toUTF8(docTitleStr
, aDocStr
);
2653 CopyUTF16toUTF8(docURLStr
, aURLStr
);
2656 //-------------------------------------------------------------
2657 static void DumpPrintObjectsTreeLayout(const UniquePtr
<nsPrintObject
>& aPO
,
2658 nsDeviceContext
* aDC
, int aLevel
,
2660 if (!MOZ_LOG_TEST(gPrintingLog
, DUMP_LAYOUT_LEVEL
)) {
2664 NS_ASSERTION(aPO
, "Pointer is null!");
2665 NS_ASSERTION(aDC
, "Pointer is null!");
2667 const char types
[][3] = {"DC", "FR", "IF", "FS"};
2670 fd
= fopen("tree_layout.txt", "w");
2672 "DocTree\n***************************************************\n");
2673 fprintf(fd
, "***************************************************\n");
2674 fprintf(fd
, "T PO DocShell Seq Page Page# Rect\n");
2679 nsIFrame
* rootFrame
= nullptr;
2680 if (aPO
->mPresShell
) {
2681 rootFrame
= aPO
->mPresShell
->GetRootFrame();
2683 for (int32_t k
= 0; k
< aLevel
; k
++) fprintf(fd
, " ");
2684 fprintf(fd
, "%s %p %p\n", types
[aPO
->mFrameType
], aPO
.get(),
2685 aPO
->mDocShell
.get());
2686 if (aPO
->PrintingIsEnabled()) {
2687 nsAutoCString docStr
;
2688 nsAutoCString urlStr
;
2689 GetDocTitleAndURL(aPO
, docStr
, urlStr
);
2690 DumpLayoutData(docStr
.get(), urlStr
.get(), aPO
->mPresContext
, aDC
,
2691 rootFrame
, aPO
->mDocShell
, fd
);
2693 fprintf(fd
, "<***************************************************>\n");
2695 for (const auto& po
: aPO
->mKids
) {
2696 NS_ASSERTION(po
, "nsPrintObject can't be null!");
2697 DumpPrintObjectsTreeLayout(po
, aDC
, aLevel
+ 1, fd
);
2700 if (aLevel
== 0 && fd
) {
2705 //-------------------------------------------------------------
2706 static void DumpPrintObjectsListStart(
2707 const char* aStr
, const nsTArray
<nsPrintObject
*>& aDocList
) {
2708 if (!MOZ_LOG_TEST(gPrintingLog
, DUMP_LAYOUT_LEVEL
)) {
2712 NS_ASSERTION(aStr
, "Pointer is null!");
2714 PR_PL(("%s\n", aStr
));
2715 DumpPrintObjectsList(aDocList
);
2720 //---------------------------------------------------------------
2721 //---------------------------------------------------------------
2722 //-- End of debug helper routines
2723 //---------------------------------------------------------------