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/DocumentTimeline.h"
24 #include "mozilla/dom/HTMLCanvasElement.h"
25 #include "mozilla/dom/ScriptSettings.h"
26 #include "mozilla/IntegerRange.h"
27 #include "mozilla/PresShell.h"
28 #include "mozilla/PresShellInlines.h"
29 #include "mozilla/StaticPrefs_print.h"
30 #include "mozilla/glean/PrintingMetrics.h"
31 #include "mozilla/Try.h"
32 #include "nsIBrowserChild.h"
33 #include "nsIOService.h"
34 #include "nsIScriptGlobalObject.h"
35 #include "nsIStringBundle.h"
36 #include "nsPIDOMWindow.h"
37 #include "nsPrintData.h"
38 #include "nsPrintObject.h"
39 #include "nsIDocShell.h"
41 #include "nsITextToSubURI.h"
48 #include "nsIPrintSettings.h"
49 #include "nsIPrintSettingsService.h"
50 #include "nsGkAtoms.h"
53 static const char sPrintSettingsServiceContractID
[] =
54 "@mozilla.org/gfx/printsettings-service;1";
57 #include "nsPagePrintTimer.h"
60 #include "mozilla/dom/Document.h"
61 #include "mozilla/dom/DocumentInlines.h"
64 #include "gfxContext.h"
65 #include "mozilla/gfx/DrawEventRecorder.h"
66 #include "mozilla/layout/RemotePrintJobChild.h"
67 #include "nsISupportsUtils.h"
68 #include "nsIScriptContext.h"
69 #include "nsComponentManagerUtils.h"
70 #include "mozilla/Preferences.h"
71 #include "mozilla/PresShell.h"
74 #include "nsIDeviceContextSpec.h"
75 #include "nsDeviceContextSpecProxy.h"
76 #include "nsViewManager.h"
78 #include "nsPageSequenceFrame.h"
79 #include "nsIInterfaceRequestor.h"
80 #include "nsIInterfaceRequestorUtils.h"
81 #include "nsIWebBrowserChrome.h"
82 #include "mozilla/ReflowInput.h"
83 #include "nsIDocumentViewer.h"
84 #include "nsIDocumentViewerPrint.h"
86 #include "nsFocusManager.h"
88 #include "mozilla/Components.h"
89 #include "mozilla/dom/Element.h"
90 #include "mozilla/dom/HTMLFrameElement.h"
91 #include "mozilla/ServoStyleSet.h"
93 using namespace mozilla
;
94 using namespace mozilla::dom
;
96 //-----------------------------------------------------
98 #include "mozilla/Logging.h"
101 // PR_LOGGING is force to always be on (even in release builds)
102 // but we only want some of it on,
103 // #define EXTENDED_DEBUG_PRINTING
106 // this log level turns on the dumping of each document's layout info
107 #define DUMP_LAYOUT_LEVEL (static_cast<mozilla::LogLevel>(9))
110 static mozilla::LazyLogModule
gPrintingLog("printing");
112 # define PR_PL(_p1) MOZ_LOG(gPrintingLog, mozilla::LogLevel::Debug, _p1);
115 #ifdef EXTENDED_DEBUG_PRINTING
116 static uint32_t gDumpFileNameCnt
= 0;
117 static uint32_t gDumpLOFileNameCnt
= 0;
120 #define PRT_YESNO(_p) ((_p) ? "YES" : "NO")
122 inline const char* LoggableTypeOfPO(const nsPrintObject
* aPO
) {
123 // TODO(dholbert): These strings used to represent actual enum values, but
124 // the enum type has been removed. We could replace them with more
125 // descriptive names, if anyone uses this logging and cares to do so.
126 return aPO
->mParent
? "eIFrame" : "eDoc";
129 inline const char* ShortLoggableTypeOfPO(const nsPrintObject
* aPO
) {
130 return aPO
->mParent
? "IF" : "DC";
133 // This processes the selection on aOrigDoc and creates an inverted selection on
134 // aDoc, which it then deletes. If the start or end of the inverted selection
135 // ranges occur in text nodes then an ellipsis is added.
136 static nsresult
DeleteNonSelectedNodes(Document
& aDoc
);
138 #ifdef EXTENDED_DEBUG_PRINTING
139 // Forward Declarations
140 static void DumpPrintObjectsListStart(const char* aStr
,
141 const nsTArray
<nsPrintObject
*>& aDocList
);
142 static void DumpPrintObjectsTree(nsPrintObject
* aPO
, int aLevel
= 0,
143 FILE* aFD
= nullptr);
144 static void DumpPrintObjectsTreeLayout(const UniquePtr
<nsPrintObject
>& aPO
,
145 nsDeviceContext
* aDC
, int aLevel
= 0,
146 FILE* aFD
= nullptr);
148 # define DUMP_DOC_LIST(_title) \
149 DumpPrintObjectsListStart((_title), mPrintDocList);
150 # define DUMP_DOC_TREE DumpPrintObjectsTree(mPrintObject.get());
151 # define DUMP_DOC_TREELAYOUT \
152 DumpPrintObjectsTreeLayout(mPrintObject, mPrt->mPrintDC);
154 # define DUMP_DOC_LIST(_title)
155 # define DUMP_DOC_TREE
156 # define DUMP_DOC_TREELAYOUT
159 // -------------------------------------------------------
161 // -------------------------------------------------------
164 * Build a tree of nsPrintObjects under aPO. It also appends a (depth first)
165 * flat list of all the nsPrintObjects created to mPrintDocList. If
166 * one of the nsPrintObject's document is the focused document, then the print
167 * object is set as mSelectionRoot.
168 * @param aParentPO The parent nsPrintObject to populate, must not be null.
170 void nsPrintJob::BuildNestedPrintObjects(
171 const UniquePtr
<nsPrintObject
>& aParentPO
) {
172 MOZ_ASSERT(aParentPO
);
174 // If aParentPO is for an iframe and its original document was the document
175 // that had focus then always set as the selection root.
176 if (aParentPO
->mParent
&&
177 aParentPO
->mDocument
->GetProperty(nsGkAtoms::printisfocuseddoc
)) {
178 mSelectionRoot
= aParentPO
.get();
179 } else if (!mSelectionRoot
&& aParentPO
->HasSelection()) {
180 // If there is no focused iframe but there is a selection in one or more
181 // frames then we want to set the root nsPrintObject as the focus root so
182 // that later EnablePrintingSelectionOnly can search for and enable all
183 // nsPrintObjects containing selections.
184 mSelectionRoot
= mPrintObject
.get();
187 for (auto& bc
: aParentPO
->mDocShell
->GetBrowsingContext()->Children()) {
188 nsCOMPtr
<nsIDocShell
> docShell
= bc
->GetDocShell();
190 if (auto* cc
= dom::ContentChild::GetSingleton()) {
191 nsCOMPtr
<nsIPrintSettingsService
> printSettingsService
=
192 do_GetService(sPrintSettingsServiceContractID
);
193 embedding::PrintData printData
;
194 printSettingsService
->SerializeToPrintData(mPrintSettings
, &printData
);
195 Unused
<< cc
->SendUpdateRemotePrintSettings(bc
, printData
);
200 RefPtr
<Document
> doc
= docShell
->GetDocument();
201 MOZ_DIAGNOSTIC_ASSERT(doc
);
202 // We might find non-static documents here if the fission remoteness change
203 // hasn't happened / finished yet. In that case, just skip them, the same
204 // way we do for remote frames above.
205 MOZ_DIAGNOSTIC_ASSERT(doc
->IsStaticDocument() || doc
->IsInitialDocument());
206 if (!doc
|| !doc
->IsStaticDocument()) {
210 // Note: docShell and doc are known-non-null at this point; they've been
211 // null-checked above (with null leading to 'continue' statements).
212 auto childPO
= MakeUnique
<nsPrintObject
>(*docShell
, *doc
, aParentPO
.get());
214 mPrintDocList
.AppendElement(childPO
.get());
215 BuildNestedPrintObjects(childPO
);
216 aParentPO
->mKids
.AppendElement(std::move(childPO
));
220 static nsresult
GetDefaultPrintSettings(nsIPrintSettings
** aSettings
) {
221 *aSettings
= nullptr;
223 nsresult rv
= NS_ERROR_FAILURE
;
224 nsCOMPtr
<nsIPrintSettingsService
> printSettingsService
=
225 do_GetService(sPrintSettingsServiceContractID
, &rv
);
226 NS_ENSURE_SUCCESS(rv
, rv
);
228 return printSettingsService
->GetDefaultPrintSettingsForPrinting(aSettings
);
231 //-------------------------------------------------------
233 NS_IMPL_ISUPPORTS(nsPrintJob
, nsIWebProgressListener
, nsISupportsWeakReference
)
235 //-------------------------------------------------------
236 nsPrintJob::~nsPrintJob() {
237 Destroy(); // for insurance
238 DisconnectPagePrintTimer();
241 bool nsPrintJob::CheckBeforeDestroy() const { return mPreparingForPrint
; }
243 //-------------------------------------------------------
244 void nsPrintJob::Destroy() {
248 mIsDestroying
= true;
250 DestroyPrintingData();
252 mDocViewerPrint
= nullptr;
255 //-------------------------------------------------------
256 void nsPrintJob::DestroyPrintingData() {
257 mPrintObject
= nullptr;
261 nsPrintJob::nsPrintJob(nsIDocumentViewerPrint
& aDocViewerPrint
,
262 nsIDocShell
& aDocShell
, Document
& aOriginalDoc
,
264 : mDocViewerPrint(&aDocViewerPrint
),
265 mDocShell(do_GetWeakReference(&aDocShell
)),
266 mScreenDPI(aScreenDPI
) {
267 // Any state that we need from aOriginalDoc must be fetched and stored
268 // here, since the document that the user selected to print may mutate
269 // across consecutive PrintPreview() calls.
271 Element
* root
= aOriginalDoc
.GetRootElement();
272 mDisallowSelectionPrint
=
273 root
&& root
->HasAttr(nsGkAtoms::mozdisallowselectionprint
);
276 //-----------------------------------------------------------------
277 std::tuple
<nsPageSequenceFrame
*, int32_t>
278 nsPrintJob::GetSeqFrameAndCountSheets() const {
279 if (NS_WARN_IF(!mPrt
)) {
283 const nsPrintObject
* po
= mPrintObject
.get();
284 if (NS_WARN_IF(!po
)) {
288 // This is sometimes incorrectly called before the pres shell has been created
289 // (bug 1141756). MOZ_DIAGNOSTIC_ASSERT so we'll still see the crash in
290 // Nightly/Aurora in case the other patch fixes this.
291 if (!po
->mPresShell
) {
292 MOZ_DIAGNOSTIC_ASSERT(
293 false, "GetSeqFrameAndCountSheets needs a non-null pres shell");
297 nsPageSequenceFrame
* seqFrame
= po
->mPresShell
->GetPageSequenceFrame();
302 // count the total number of sheets
303 return {seqFrame
, seqFrame
->PrincipalChildList().GetLength()};
306 // Foward decl for Debug Helper Functions
307 #ifdef EXTENDED_DEBUG_PRINTING
309 static int RemoveFilesInDir(const char* aDir
);
311 static void GetDocTitleAndURL(const UniquePtr
<nsPrintObject
>& aPO
,
312 nsACString
& aDocStr
, nsACString
& aURLStr
);
313 static void DumpPrintObjectsTree(nsPrintObject
* aPO
, int aLevel
, FILE* aFD
);
314 static void DumpPrintObjectsList(const nsTArray
<nsPrintObject
*>& aDocList
);
315 static void RootFrameList(nsPresContext
* aPresContext
, FILE* out
,
316 const char* aPrefix
);
317 static void DumpViews(nsIDocShell
* aDocShell
, FILE* out
);
318 static void DumpLayoutData(const char* aTitleStr
, const char* aURLStr
,
319 nsPresContext
* aPresContext
, nsDeviceContext
* aDC
,
320 nsIFrame
* aRootFrame
, nsIDocShell
* aDocShell
,
324 //--------------------------------------------------------------------------------
326 nsresult
nsPrintJob::CommonPrint(bool aIsPrintPreview
,
327 nsIPrintSettings
* aPrintSettings
,
328 nsIWebProgressListener
* aWebProgressListener
,
329 Document
& aSourceDoc
) {
330 // Callers must hold a strong reference to |this| to ensure that we stay
331 // alive for the duration of this method, because our main owning reference
332 // (on nsDocumentViewer) might be cleared during this function (if we cause
333 // script to run and it cancels the print operation).
335 nsresult rv
= DoCommonPrint(aIsPrintPreview
, aPrintSettings
,
336 aWebProgressListener
, aSourceDoc
);
338 if (aIsPrintPreview
) {
339 mIsCreatingPrintPreview
= false;
340 SetIsPrintPreview(false);
342 SetIsPrinting(false);
344 if (rv
!= NS_ERROR_ABORT
&& rv
!= NS_ERROR_OUT_OF_MEMORY
) {
345 FirePrintingErrorEvent(rv
);
347 DestroyPrintingData();
353 nsresult
nsPrintJob::DoCommonPrint(bool aIsPrintPreview
,
354 nsIPrintSettings
* aPrintSettings
,
355 nsIWebProgressListener
* aWebProgressListener
,
357 MOZ_ASSERT(aDoc
.IsStaticDocument());
361 // Grab the new instance with local variable to guarantee that it won't be
362 // deleted during this method.
363 // Note: Methods we call early below rely on mPrt being set.
364 mPrt
= new nsPrintData(aIsPrintPreview
? nsPrintData::eIsPrintPreview
365 : nsPrintData::eIsPrinting
);
366 RefPtr
<nsPrintData
> printData
= mPrt
;
368 if (aIsPrintPreview
) {
369 mIsCreatingPrintPreview
= true;
370 SetIsPrintPreview(true);
375 if (aWebProgressListener
) {
376 printData
->mPrintProgressListeners
.AppendObject(aWebProgressListener
);
378 if (mRemotePrintJob
) {
379 // If we have a RemotePrintJob add it to the print progress listeners,
380 // so it can forward to the parent.
381 printData
->mPrintProgressListeners
.AppendElement(mRemotePrintJob
);
384 // Get the docshell for this documentviewer
385 nsCOMPtr
<nsIDocShell
> docShell(do_QueryReferent(mDocShell
, &rv
));
386 NS_ENSURE_SUCCESS(rv
, rv
);
388 // if they don't pass in a PrintSettings, then get the Global PS
389 mPrintSettings
= aPrintSettings
;
390 if (!mPrintSettings
) {
391 MOZ_TRY(GetDefaultPrintSettings(getter_AddRefs(mPrintSettings
)));
395 nsAutoScriptBlocker scriptBlocker
;
396 // Note: docShell is implicitly non-null via do_QueryReferent necessarily
397 // having succeeded (if we got here).
398 mPrintObject
= MakeUnique
<nsPrintObject
>(*docShell
, aDoc
);
399 mPrintDocList
.AppendElement(mPrintObject
.get());
401 BuildNestedPrintObjects(mPrintObject
);
404 // The nsAutoScriptBlocker above will now have been destroyed, which may
405 // cause our print/print-preview operation to finish. In this case, we
406 // should immediately return an error code so that the root caller knows
407 // it shouldn't continue to do anything with this instance.
409 return NS_ERROR_FAILURE
;
412 // XXX This isn't really correct...
413 if (!mPrintObject
->mDocument
|| !mPrintObject
->mDocument
->GetRootElement()) {
414 return NS_ERROR_GFX_PRINTER_STARTDOC
;
417 mPrintSettings
->GetShrinkToFit(&mShrinkToFit
);
419 nsCOMPtr
<nsIDeviceContextSpec
> devspec
;
420 if (XRE_IsContentProcess()) {
421 devspec
= new nsDeviceContextSpecProxy(mRemotePrintJob
);
423 devspec
= do_CreateInstance("@mozilla.org/gfx/devicecontextspec;1", &rv
);
424 NS_ENSURE_SUCCESS(rv
, rv
);
427 bool printSilently
= false;
428 mPrintSettings
->GetPrintSilent(&printSilently
);
429 if (StaticPrefs::print_always_print_silent()) {
430 printSilently
= true;
433 if (mIsDoingPrinting
&& printSilently
) {
434 glean::printing::silent_print
.Add(1);
437 MOZ_TRY(devspec
->Init(mPrintSettings
, mIsCreatingPrintPreview
));
439 printData
->mPrintDC
= new nsDeviceContext();
440 MOZ_TRY(printData
->mPrintDC
->InitForPrinting(devspec
));
442 MOZ_TRY(EnablePOsForPrinting());
444 if (!mIsCreatingPrintPreview
) {
445 printData
->OnStartPrinting();
447 InitPrintDocConstruction(false);
452 //---------------------------------------------------------------------------------
453 nsresult
nsPrintJob::Print(Document
& aDoc
, nsIPrintSettings
* aPrintSettings
,
454 RemotePrintJobChild
* aRemotePrintJob
,
455 nsIWebProgressListener
* aWebProgressListener
) {
456 mRemotePrintJob
= aRemotePrintJob
;
457 return CommonPrint(false, aPrintSettings
, aWebProgressListener
, aDoc
);
460 nsresult
nsPrintJob::PrintPreview(Document
& aDoc
,
461 nsIPrintSettings
* aPrintSettings
,
462 nsIWebProgressListener
* aWebProgressListener
,
463 PrintPreviewResolver
&& aCallback
) {
464 // Take ownership of aCallback, otherwise a function further up the call
465 // stack will call it to signal failure (by passing zero).
466 mPrintPreviewCallback
= std::move(aCallback
);
468 nsresult rv
= CommonPrint(true, aPrintSettings
, aWebProgressListener
, aDoc
);
470 if (mPrintPreviewCallback
) {
472 mPrintPreviewCallback(
473 PrintPreviewResultInfo(0, 0, false, false, false, {}, {}, {}));
474 mPrintPreviewCallback
= nullptr;
480 int32_t nsPrintJob::GetRawNumPages() const {
481 auto [seqFrame
, numSheets
] = GetSeqFrameAndCountSheets();
483 return seqFrame
? seqFrame
->GetRawNumPages() : 0;
486 bool nsPrintJob::GetIsEmpty() const {
487 auto [seqFrame
, numSheets
] = GetSeqFrameAndCountSheets();
494 return !seqFrame
->GetPagesInFirstSheet();
497 int32_t nsPrintJob::GetPrintPreviewNumSheets() const {
498 auto [seqFrame
, numSheets
] = GetSeqFrameAndCountSheets();
503 //-----------------------------------------------------------------
504 //-- Section: Pre-Reflow Methods
505 //-----------------------------------------------------------------
508 void nsPrintJob::GetDisplayTitleAndURL(Document
& aDoc
,
509 nsIPrintSettings
* aSettings
,
510 DocTitleDefault aTitleDefault
,
511 nsAString
& aTitle
, nsAString
& aURLStr
) {
516 aSettings
->GetTitle(aTitle
);
517 aSettings
->GetDocURL(aURLStr
);
520 if (aTitle
.IsEmpty()) {
521 aDoc
.GetTitle(aTitle
);
522 if (aTitle
.IsEmpty()) {
523 if (!aURLStr
.IsEmpty() &&
524 aTitleDefault
== DocTitleDefault::eDocURLElseFallback
) {
527 nsCOMPtr
<nsIStringBundle
> brandBundle
;
528 nsCOMPtr
<nsIStringBundleService
> svc
=
529 mozilla::components::StringBundle::Service();
531 svc
->CreateBundle("chrome://branding/locale/brand.properties",
532 getter_AddRefs(brandBundle
));
534 brandBundle
->GetStringFromName("brandShortName", aTitle
);
537 if (aTitle
.IsEmpty()) {
538 aTitle
.AssignLiteral(u
"Mozilla Document");
544 if (aURLStr
.IsEmpty()) {
545 nsIURI
* url
= aDoc
.GetDocumentURI();
550 nsCOMPtr
<nsIURI
> exposableURI
= net::nsIOService::CreateExposableURI(url
);
551 nsAutoCString urlCStr
;
552 nsresult rv
= exposableURI
->GetSpec(urlCStr
);
557 nsCOMPtr
<nsITextToSubURI
> textToSubURI
=
558 do_GetService(NS_ITEXTTOSUBURI_CONTRACTID
, &rv
);
563 textToSubURI
->UnEscapeURIForUI(urlCStr
, aURLStr
);
567 //---------------------------------------------------------------------
568 nsresult
nsPrintJob::DocumentReadyForPrinting() {
569 // Send the document to the printer...
570 nsresult rv
= SetupToPrintContent();
572 // The print job was canceled or there was a problem
573 // So remove all other documents from the print list
574 DonePrintingSheets(nullptr, rv
);
579 /** ---------------------------------------------------
580 * Cleans up when an error occurred
582 nsresult
nsPrintJob::CleanupOnFailure(nsresult aResult
, bool aIsPrinting
) {
583 PR_PL(("**** Failed %s - rv 0x%" PRIX32
,
584 aIsPrinting
? "Printing" : "Print Preview",
585 static_cast<uint32_t>(aResult
)));
586 PROFILER_MARKER_TEXT("PrintJob", LAYOUT_Printing
, MarkerStack::Capture(),
587 "nsPrintJob::CleanupOnFailure"_ns
);
590 if (mPagePrintTimer
) {
591 mPagePrintTimer
->Stop();
592 DisconnectPagePrintTimer();
596 SetIsPrinting(false);
598 SetIsPrintPreview(false);
599 mIsCreatingPrintPreview
= false;
602 /* cleanup done, let's fire-up an error dialog to notify the user
605 * When rv == NS_ERROR_ABORT, it means we want out of the
606 * print job without displaying any error messages
608 if (aResult
!= NS_ERROR_ABORT
) {
609 FirePrintingErrorEvent(aResult
);
612 FirePrintCompletionEvent();
617 //---------------------------------------------------------------------
618 void nsPrintJob::FirePrintingErrorEvent(nsresult aPrintError
) {
619 if (mPrintPreviewCallback
) {
621 mPrintPreviewCallback(
622 PrintPreviewResultInfo(0, 0, false, false, false, {}, {}, {}));
623 mPrintPreviewCallback
= nullptr;
626 nsCOMPtr
<nsIDocumentViewer
> viewer
= do_QueryInterface(mDocViewerPrint
);
627 if (NS_WARN_IF(!viewer
)) {
631 const RefPtr
<Document
> doc
= viewer
->GetDocument();
632 const RefPtr
<CustomEvent
> event
= NS_NewDOMCustomEvent(doc
, nullptr, nullptr);
637 if (!jsapi
.Init(event
->GetParentObject())) {
640 JSContext
* cx
= jsapi
.cx();
642 JS::Rooted
<JS::Value
> detail(
643 cx
, JS::NumberValue(static_cast<double>(aPrintError
)));
644 event
->InitCustomEvent(cx
, u
"PrintingError"_ns
, false, false, detail
);
645 event
->SetTrusted(true);
647 // Event listeners in chrome shouldn't delete this.
648 AsyncEventDispatcher::RunDOMEventWhenSafe(*doc
, *event
,
649 ChromeOnlyDispatch::eYes
);
651 // Inform any progress listeners of the Error.
653 // Note that nsPrintData::DoOnStatusChange() will call some listeners.
654 // So, mPrt can be cleared or recreated.
655 RefPtr
<nsPrintData
> printData
= mPrt
;
656 printData
->DoOnStatusChange(aPrintError
);
660 //-----------------------------------------------------------------
661 //-- Section: Reflow Methods
662 //-----------------------------------------------------------------
664 nsresult
nsPrintJob::ReconstructAndReflow() {
665 if (NS_WARN_IF(!mPrt
)) {
666 return NS_ERROR_FAILURE
;
669 #if defined(XP_WIN) && defined(EXTENDED_DEBUG_PRINTING)
670 // We need to clear all the output files here
671 // because they will be re-created with second reflow of the docs
672 if (MOZ_LOG_TEST(gPrintingLog
, DUMP_LAYOUT_LEVEL
)) {
673 RemoveFilesInDir(".\\");
674 gDumpFileNameCnt
= 0;
675 gDumpLOFileNameCnt
= 0;
679 // In this loop, it's conceivable that one of our helpers might clear mPrt,
680 // while we're using it & its members! So we capture it in an owning local
681 // reference & use that instead of using mPrt directly.
682 RefPtr
<nsPrintData
> printData
= mPrt
;
683 for (nsPrintObject
* po
: mPrintDocList
) {
684 if (!po
->PrintingIsEnabled() || po
->mInvisible
) {
688 // When the print object has been marked as "print the document" (i.e,
689 // po->PrintingIsEnabled() is true), mPresContext and mPresShell should be
690 // non-nullptr (i.e., should've been created for the print) since they
691 // are necessary to print the document.
692 MOZ_ASSERT(po
->mPresContext
&& po
->mPresShell
,
693 "mPresContext and mPresShell shouldn't be nullptr when the "
695 "has been marked as \"print the document\"");
699 po
->mPresContext
->SetPageScale(po
->mZoomRatio
);
701 // Calculate scale factor from printer to screen
702 float printDPI
= float(AppUnitsPerCSSInch()) /
703 float(printData
->mPrintDC
->AppUnitsPerDevPixel());
704 po
->mPresContext
->SetPrintPreviewScale(mScreenDPI
/ printDPI
);
706 RefPtr
<PresShell
> presShell(po
->mPresShell
);
707 if (NS_WARN_IF(presShell
->IsDestroying())) {
708 return NS_ERROR_FAILURE
;
711 presShell
->ReconstructFrames();
713 // If the printing was canceled or restarted with different data,
714 // let's stop doing this printing.
715 if (NS_WARN_IF(mPrt
!= printData
)) {
716 return NS_ERROR_FAILURE
;
719 // For all views except the first one, setup the root view.
720 // ??? Can there be multiple po for the top-level-document?
721 bool documentIsTopLevel
= true;
725 nsresult rv
= SetRootView(po
, doReturn
, documentIsTopLevel
, adjSize
);
727 MOZ_ASSERT(!documentIsTopLevel
, "How could this happen?");
729 if (NS_FAILED(rv
) || doReturn
) {
734 presShell
->FlushPendingNotifications(FlushType::Layout
);
736 if (NS_WARN_IF(presShell
->IsDestroying())) {
737 return NS_ERROR_FAILURE
;
740 // If the printing was canceled or restarted with different data,
741 // let's stop doing this printing.
742 if (NS_WARN_IF(mPrt
!= printData
)) {
743 return NS_ERROR_FAILURE
;
746 po
->mDocument
->UpdateRemoteFrameEffects();
747 MOZ_TRY(UpdateSelectionAndShrinkPrintObject(po
, documentIsTopLevel
));
752 //-------------------------------------------------------
753 nsresult
nsPrintJob::SetupToPrintContent() {
754 // This method may be called while DoCommonPrint() initializes the instance
755 // when its script blocker goes out of scope. In such case, this cannot do
756 // its job as expected because some objects in mPrt have not been initialized
757 // yet but they are necessary.
758 // Note: it shouldn't be possible for mPrintObject to be null; we check
759 // it for good measure (after we check its owner) before we start
760 // dereferencing it below.
761 if (NS_WARN_IF(!mPrt
) || NS_WARN_IF(!mPrintObject
)) {
762 return NS_ERROR_FAILURE
;
765 // If this is creating print preview, mPrintObject->mPresContext and
766 // mPrintObject->mPresShell need to be non-nullptr because this cannot
767 // initialize page sequence frame without them at end of this method since
768 // page sequence frame has already been destroyed or not been created yet.
769 if (mIsCreatingPrintPreview
&& (NS_WARN_IF(!mPrintObject
->mPresContext
) ||
770 NS_WARN_IF(!mPrintObject
->mPresShell
))) {
771 return NS_ERROR_FAILURE
;
774 // If this is printing some documents (not print-previewing the documents),
775 // mPrintObject->mPresContext and mPrintObject->mPresShell can be
776 // nullptr only when mPrintObject->PrintingIsEnabled() is false. E.g.,
777 // if the document has a <frameset> element and it's printing only content in
778 // a <frame> element or all <frame> elements separately.
780 (!mIsCreatingPrintPreview
&& !mPrintObject
->PrintingIsEnabled()) ||
781 (mPrintObject
->mPresContext
&& mPrintObject
->mPresShell
),
782 "mPresContext and mPresShell shouldn't be nullptr when printing the "
783 "document or creating print-preview");
785 bool didReconstruction
= false;
787 // This method works with mPrintObject. So, we need to guarantee that
788 // it won't be deleted in this method. We achieve this by holding a strong
789 // local reference to mPrt, which in turn keeps mPrintObject alive.
790 RefPtr
<nsPrintData
> printData
= mPrt
;
792 // If some new content got loaded since the initial reflow rebuild
794 if (mDidLoadDataForPrinting
) {
795 nsresult rv
= ReconstructAndReflow();
796 if (NS_WARN_IF(NS_FAILED(rv
))) {
799 // If the printing was canceled or restarted with different data,
800 // let's stop doing this printing.
801 if (NS_WARN_IF(mPrt
!= printData
)) {
802 return NS_ERROR_FAILURE
;
804 didReconstruction
= true;
807 // Here is where we figure out if extra reflow for shrinking the content
810 mShrinkToFitFactor
= mPrintObject
->mShrinkRatio
;
812 if (mShrinkToFitFactor
< 0.998f
) {
813 nsresult rv
= ReconstructAndReflow();
814 if (NS_WARN_IF(NS_FAILED(rv
))) {
817 // If the printing was canceled or restarted with different data,
818 // let's stop doing this printing.
819 if (NS_WARN_IF(mPrt
!= printData
)) {
820 return NS_ERROR_FAILURE
;
822 didReconstruction
= true;
825 if (MOZ_LOG_TEST(gPrintingLog
, LogLevel::Debug
)) {
826 float calcRatio
= mPrintObject
->mShrinkRatio
;
828 ("*******************************************************************"
830 PR_PL(("STF Ratio is: %8.5f Effective Ratio: %8.5f Diff: %8.5f\n",
831 mShrinkToFitFactor
, calcRatio
, mShrinkToFitFactor
- calcRatio
));
833 ("*******************************************************************"
838 // If the frames got reconstructed and reflowed the number of pages might
840 if (didReconstruction
) {
841 FirePrintPreviewUpdateEvent();
842 // If the printing was canceled or restarted with different data,
843 // let's stop doing this printing.
844 if (NS_WARN_IF(mPrt
!= printData
)) {
845 return NS_ERROR_FAILURE
;
849 DUMP_DOC_LIST(("\nAfter Reflow------------------------------------------"));
851 PR_PL(("-------------------------------------------------------\n"));
854 CalcNumPrintablePages(mNumPrintablePages
);
856 PR_PL(("--- Printing %d pages\n", mNumPrintablePages
));
859 // Print listener setup...
860 printData
->OnStartPrinting();
862 // If the printing was canceled or restarted with different data,
863 // let's stop doing this printing.
864 if (NS_WARN_IF(mPrt
!= printData
)) {
865 return NS_ERROR_FAILURE
;
868 nsAutoString fileNameStr
;
869 // check to see if we are printing to a file
870 if (mPrintSettings
->GetOutputDestination() ==
871 nsIPrintSettings::kOutputDestinationFile
) {
872 // On some platforms the BeginDocument needs to know the name of the file.
873 mPrintSettings
->GetToFileName(fileNameStr
);
876 nsAutoString docTitleStr
;
877 nsAutoString docURLStr
;
878 GetDisplayTitleAndURL(*mPrintObject
->mDocument
, mPrintSettings
,
879 DocTitleDefault::eDocURLElseFallback
, docTitleStr
,
882 int32_t startPage
= 1;
883 int32_t endPage
= mNumPrintablePages
;
885 nsTArray
<int32_t> ranges
;
886 mPrintSettings
->GetPageRanges(ranges
);
887 for (size_t i
= 0; i
< ranges
.Length(); i
+= 2) {
888 startPage
= std::max(1, std::min(startPage
, ranges
[i
]));
889 endPage
= std::min(mNumPrintablePages
, std::max(endPage
, ranges
[i
+ 1]));
893 // BeginDocument may pass back a FAILURE code
894 // i.e. On Windows, if you are printing to a file and hit "Cancel"
895 // to the "File Name" dialog, this comes back as an error
896 // Don't start printing when regression test are executed
897 if (mIsDoingPrinting
) {
898 rv
= printData
->mPrintDC
->BeginDocument(docTitleStr
, fileNameStr
, startPage
,
902 if (mIsCreatingPrintPreview
) {
903 // Copy docTitleStr and docURLStr to the pageSequenceFrame, to be displayed
905 nsPageSequenceFrame
* seqFrame
=
906 mPrintObject
->mPresShell
->GetPageSequenceFrame();
908 seqFrame
->StartPrint(mPrintObject
->mPresContext
, mPrintSettings
,
909 docTitleStr
, docURLStr
);
913 PR_PL(("****************** Begin Document ************************\n"));
916 NS_WARNING_ASSERTION(rv
== NS_ERROR_ABORT
,
917 "Failed to begin document for printing");
921 // This will print the docshell document
922 // when it completes asynchronously in the DonePrintingSheets method
923 // it will check to see if there are more docshells to be printed and
924 // then PrintDocContent will be called again.
926 if (mIsDoingPrinting
) {
927 // Double-check that mPrintObject is non-null, because it could have
928 // gotten cleared while running script since we last checked it.
929 if (NS_WARN_IF(!mPrintObject
)) {
930 return NS_ERROR_FAILURE
;
933 PrintDocContent(mPrintObject
, rv
); // ignore return value
939 //-------------------------------------------------------
940 // Recursively reflow each sub-doc and then calc
941 // all the frame locations of the sub-docs
942 nsresult
nsPrintJob::ReflowDocList(const UniquePtr
<nsPrintObject
>& aPO
) {
943 NS_ENSURE_ARG_POINTER(aPO
);
945 // Check to see if the subdocument's element has been hidden by the parent
947 if (aPO
->mParent
&& aPO
->mParent
->mPresShell
) {
949 aPO
->mContent
? aPO
->mContent
->GetPrimaryFrame() : nullptr;
950 if (!frame
|| !frame
->StyleVisibility()->IsVisible()) {
951 aPO
->EnablePrinting(false);
952 aPO
->mInvisible
= true;
957 UpdateZoomRatio(aPO
.get());
960 MOZ_TRY(ReflowPrintObject(aPO
));
962 for (const UniquePtr
<nsPrintObject
>& kid
: aPO
->mKids
) {
963 MOZ_TRY(ReflowDocList(kid
));
968 void nsPrintJob::FirePrintPreviewUpdateEvent() {
969 // Dispatch the event only while in PrintPreview. When printing, there is no
970 // listener bound to this event and therefore no need to dispatch it.
971 if (mCreatedForPrintPreview
&& !mIsDoingPrinting
) {
972 nsCOMPtr
<nsIDocumentViewer
> viewer
= do_QueryInterface(mDocViewerPrint
);
973 if (Document
* document
= viewer
->GetDocument()) {
974 AsyncEventDispatcher::RunDOMEventWhenSafe(
975 *document
, u
"printPreviewUpdate"_ns
, CanBubble::eYes
,
976 ChromeOnlyDispatch::eYes
);
981 nsresult
nsPrintJob::InitPrintDocConstruction(bool aHandleError
) {
982 // Guarantee that mPrintObject won't be deleted.
983 RefPtr
<nsPrintData
> printData
= mPrt
;
985 if (NS_WARN_IF(!printData
)) {
986 return NS_ERROR_FAILURE
;
989 // Attach progressListener to catch network requests.
990 mDidLoadDataForPrinting
= false;
993 AutoRestore
<bool> restore
{mDoingInitialReflow
};
994 mDoingInitialReflow
= true;
996 nsCOMPtr
<nsIWebProgress
> webProgress
=
997 do_QueryInterface(mPrintObject
->mDocShell
);
998 webProgress
->AddProgressListener(static_cast<nsIWebProgressListener
*>(this),
999 nsIWebProgress::NOTIFY_STATE_REQUEST
);
1001 MOZ_TRY(ReflowDocList(mPrintObject
));
1003 FirePrintPreviewUpdateEvent();
1006 MaybeResumePrintAfterResourcesLoaded(aHandleError
);
1010 bool nsPrintJob::ShouldResumePrint() const {
1011 if (mDoingInitialReflow
) {
1014 Document
* doc
= mPrintObject
->mDocument
;
1016 NS_ENSURE_TRUE(doc
, true);
1017 nsCOMPtr
<nsILoadGroup
> lg
= doc
->GetDocumentLoadGroup();
1018 NS_ENSURE_TRUE(lg
, true);
1019 bool pending
= false;
1020 nsresult rv
= lg
->IsPending(&pending
);
1021 NS_ENSURE_SUCCESS(rv
, true);
1025 nsresult
nsPrintJob::MaybeResumePrintAfterResourcesLoaded(
1026 bool aCleanupOnError
) {
1027 if (!ShouldResumePrint()) {
1028 mDidLoadDataForPrinting
= true;
1031 // If Destroy() has already been called, mPtr is nullptr. Then, the instance
1032 // needs to do nothing anymore in this method.
1033 // Note: it shouldn't be possible for mPrintObject to be null; we
1034 // just check it for good measure, as we check its owner.
1035 // Note: it shouldn't be possible for mPrintObject->mDocShell to be
1036 // null; we just check it for good measure, as we check its owner.
1037 if (!mPrt
|| NS_WARN_IF(!mPrintObject
) ||
1038 NS_WARN_IF(!mPrintObject
->mDocShell
)) {
1039 return NS_ERROR_FAILURE
;
1042 nsCOMPtr
<nsIWebProgress
> webProgress
=
1043 do_QueryInterface(mPrintObject
->mDocShell
);
1045 webProgress
->RemoveProgressListener(
1046 static_cast<nsIWebProgressListener
*>(this));
1049 if (mIsDoingPrinting
) {
1050 rv
= DocumentReadyForPrinting();
1052 rv
= FinishPrintPreview();
1055 /* cleaup on failure + notify user */
1056 if (aCleanupOnError
&& NS_FAILED(rv
)) {
1057 NS_WARNING_ASSERTION(rv
== NS_ERROR_ABORT
,
1058 "nsPrintJob::ResumePrintAfterResourcesLoaded failed");
1059 CleanupOnFailure(rv
, !mIsDoingPrinting
);
1065 ////////////////////////////////////////////////////////////////////////////////
1066 // nsIWebProgressListener
1068 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP
1069 nsPrintJob::OnStateChange(nsIWebProgress
* aWebProgress
, nsIRequest
* aRequest
,
1070 uint32_t aStateFlags
, nsresult aStatus
) {
1071 if (aStateFlags
& STATE_STOP
) {
1072 // If all resources are loaded, then finish and reflow.
1073 MaybeResumePrintAfterResourcesLoaded(/* aCleanupOnError */ true);
1079 nsPrintJob::OnProgressChange(nsIWebProgress
* aWebProgress
, nsIRequest
* aRequest
,
1080 int32_t aCurSelfProgress
, int32_t aMaxSelfProgress
,
1081 int32_t aCurTotalProgress
,
1082 int32_t aMaxTotalProgress
) {
1083 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
1088 nsPrintJob::OnLocationChange(nsIWebProgress
* aWebProgress
, nsIRequest
* aRequest
,
1089 nsIURI
* aLocation
, uint32_t aFlags
) {
1090 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
1095 nsPrintJob::OnStatusChange(nsIWebProgress
* aWebProgress
, nsIRequest
* aRequest
,
1096 nsresult aStatus
, const char16_t
* aMessage
) {
1097 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
1102 nsPrintJob::OnSecurityChange(nsIWebProgress
* aWebProgress
, nsIRequest
* aRequest
,
1104 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
1109 nsPrintJob::OnContentBlockingEvent(nsIWebProgress
* aWebProgress
,
1110 nsIRequest
* aRequest
, uint32_t aEvent
) {
1111 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
1115 //-------------------------------------------------------
1117 void nsPrintJob::UpdateZoomRatio(nsPrintObject
* aPO
) {
1118 if (!aPO
->mParent
) {
1120 aPO
->mZoomRatio
= mShrinkToFitFactor
;
1121 // If we're actually going to scale (the factor is less than 1), we
1122 // reduce the scale factor slightly to avoid the possibility of floating
1123 // point rounding error causing slight clipping of the longest lines.
1124 if (aPO
->mZoomRatio
!= 1.0f
) {
1125 aPO
->mZoomRatio
-= 0.005f
;
1129 mPrintSettings
->GetScaling(&scaling
);
1130 aPO
->mZoomRatio
= float(scaling
);
1135 nsresult
nsPrintJob::UpdateSelectionAndShrinkPrintObject(
1136 nsPrintObject
* aPO
, bool aDocumentIsTopLevel
) {
1137 PresShell
* displayPresShell
= aPO
->mDocShell
->GetPresShell();
1138 // Transfer Selection Ranges to the new Print PresShell
1139 RefPtr
<Selection
> selection
, selectionPS
;
1140 // It's okay if there is no display shell, just skip copying the selection
1141 if (displayPresShell
) {
1142 selection
= displayPresShell
->GetCurrentSelection(SelectionType::eNormal
);
1144 selectionPS
= aPO
->mPresShell
->GetCurrentSelection(SelectionType::eNormal
);
1146 // Reset all existing selection ranges that might have been added by calling
1147 // this function before.
1149 selectionPS
->RemoveAllRanges(IgnoreErrors());
1151 if (selection
&& selectionPS
) {
1152 const uint32_t rangeCount
= selection
->RangeCount();
1153 for (const uint32_t inx
: IntegerRange(rangeCount
)) {
1154 MOZ_ASSERT(selection
->RangeCount() == rangeCount
);
1155 const RefPtr
<nsRange
> range
{selection
->GetRangeAt(inx
)};
1156 selectionPS
->AddRangeAndSelectFramesAndNotifyListeners(*range
,
1161 // If we are trying to shrink the contents to fit on the page
1162 // we must first locate the "pageContent" frame
1163 // Then we walk the frame tree and look for the "xmost" frame
1164 // this is the frame where the right-hand side of the frame extends
1166 if (mShrinkToFit
&& aDocumentIsTopLevel
) {
1167 nsPageSequenceFrame
* pageSeqFrame
= aPO
->mPresShell
->GetPageSequenceFrame();
1168 NS_ENSURE_STATE(pageSeqFrame
);
1169 aPO
->mShrinkRatio
= pageSeqFrame
->GetSTFPercent();
1170 // Limit the shrink-to-fit scaling for some text-ish type of documents.
1171 nsAutoString contentType
;
1172 aPO
->mPresShell
->GetDocument()->GetContentType(contentType
);
1173 if (contentType
.EqualsLiteral("application/xhtml+xml") ||
1174 StringBeginsWith(contentType
, u
"text/"_ns
)) {
1175 int32_t limitPercent
=
1176 Preferences::GetInt("print.shrink-to-fit.scale-limit-percent", 20);
1177 limitPercent
= std::max(0, limitPercent
);
1178 limitPercent
= std::min(100, limitPercent
);
1179 float minShrinkRatio
= float(limitPercent
) / 100;
1180 aPO
->mShrinkRatio
= std::max(aPO
->mShrinkRatio
, minShrinkRatio
);
1186 nsView
* nsPrintJob::GetParentViewForRoot() {
1187 if (mIsCreatingPrintPreview
) {
1188 if (nsCOMPtr
<nsIDocumentViewer
> viewer
=
1189 do_QueryInterface(mDocViewerPrint
)) {
1190 return viewer
->FindContainerView();
1196 nsresult
nsPrintJob::SetRootView(nsPrintObject
* aPO
, bool& doReturn
,
1197 bool& documentIsTopLevel
, nsSize
& adjSize
) {
1198 bool canCreateScrollbars
= true;
1201 nsView
* parentView
= nullptr;
1205 if (aPO
->mParent
&& aPO
->mParent
->PrintingIsEnabled()) {
1207 aPO
->mContent
? aPO
->mContent
->GetPrimaryFrame() : nullptr;
1208 // Without a frame, this document can't be displayed; therefore, there is no
1209 // point to reflowing it
1211 aPO
->EnablePrinting(false);
1216 // XXX If printing supported printing document hierarchies with non-constant
1217 // zoom this would be wrong as we use the same mPrt->mPrintDC for all
1219 adjSize
= frame
->GetContentRect().Size();
1220 documentIsTopLevel
= false;
1221 // presshell exists because parent is printable
1223 // the top nsPrintObject's widget will always have scrollbars
1224 if (frame
&& frame
->IsSubDocumentFrame()) {
1225 nsView
* view
= frame
->GetView();
1226 NS_ENSURE_TRUE(view
, NS_ERROR_FAILURE
);
1227 view
= view
->GetFirstChild();
1228 NS_ENSURE_TRUE(view
, NS_ERROR_FAILURE
);
1230 canCreateScrollbars
= false;
1233 adjSize
= mPrt
->mPrintDC
->GetDeviceSurfaceDimensions();
1234 documentIsTopLevel
= true;
1235 parentView
= GetParentViewForRoot();
1238 if (aPO
->mViewManager
->GetRootView()) {
1239 // Reuse the root view that is already on the root frame.
1240 rootView
= aPO
->mViewManager
->GetRootView();
1241 // Remove it from its existing parent if necessary
1242 aPO
->mViewManager
->RemoveChild(rootView
);
1243 rootView
->SetParent(parentView
);
1245 // Create a child window of the parent that is our "root view/window"
1246 nsRect tbounds
= nsRect(nsPoint(0, 0), adjSize
);
1247 rootView
= aPO
->mViewManager
->CreateView(tbounds
, parentView
);
1248 NS_ENSURE_TRUE(rootView
, NS_ERROR_OUT_OF_MEMORY
);
1251 if (mIsCreatingPrintPreview
&& documentIsTopLevel
) {
1252 aPO
->mPresContext
->SetPaginatedScrolling(canCreateScrollbars
);
1255 // Setup hierarchical relationship in view manager
1256 aPO
->mViewManager
->SetRootView(rootView
);
1261 // Reflow a nsPrintObject
1262 nsresult
nsPrintJob::ReflowPrintObject(const UniquePtr
<nsPrintObject
>& aPO
) {
1263 NS_ENSURE_STATE(aPO
);
1265 if (!aPO
->PrintingIsEnabled()) {
1269 NS_ASSERTION(!aPO
->mPresContext
, "Recreating prescontext");
1271 // Guarantee that mPrt and the objects it owns won't be deleted in this method
1272 // because it might be cleared if other modules called from here may fire
1273 // events, notifying observers and/or listeners.
1274 RefPtr
<nsPrintData
> printData
= mPrt
;
1276 // create the PresContext
1277 nsPresContext::nsPresContextType type
=
1278 mIsCreatingPrintPreview
? nsPresContext::eContext_PrintPreview
1279 : nsPresContext::eContext_Print
;
1280 const bool shouldBeRoot
=
1281 (!aPO
->mParent
|| !aPO
->mParent
->PrintingIsEnabled()) &&
1282 !GetParentViewForRoot();
1283 aPO
->mPresContext
= shouldBeRoot
? new nsRootPresContext(aPO
->mDocument
, type
)
1284 : new nsPresContext(aPO
->mDocument
, type
);
1285 aPO
->mPresContext
->SetPrintSettings(mPrintSettings
);
1287 // init it with the DC
1288 MOZ_TRY(aPO
->mPresContext
->Init(printData
->mPrintDC
));
1290 aPO
->mViewManager
= new nsViewManager();
1292 MOZ_TRY(aPO
->mViewManager
->Init(printData
->mPrintDC
));
1294 bool doReturn
= false;
1295 bool documentIsTopLevel
= false;
1298 nsresult rv
= SetRootView(aPO
.get(), doReturn
, documentIsTopLevel
, adjSize
);
1300 if (NS_FAILED(rv
) || doReturn
) {
1304 // Here, we inform nsPresContext of the page size. Note that 'adjSize' is
1305 // *usually* the page size, but we need to check. Strictly speaking, adjSize
1306 // is the *device output size*, which is really the dimensions of a "sheet"
1307 // rather than a "page" (an important distinction in an N-pages-per-sheet
1308 // scenario). For some pages-per-sheet values, the pages are orthogonal to
1309 // the sheet; we adjust for that here by swapping the width with the height.
1310 nsSize pageSize
= adjSize
;
1311 if (mPrintSettings
->HasOrthogonalPagesPerSheet()) {
1312 std::swap(pageSize
.width
, pageSize
.height
);
1314 // XXXalaskanemily: Is this actually necessary? We set it again before the
1316 aPO
->mPresContext
->SetPageSize(pageSize
);
1318 int32_t p2a
= aPO
->mPresContext
->DeviceContext()->AppUnitsPerDevPixel();
1319 if (documentIsTopLevel
&& mIsCreatingPrintPreview
) {
1320 if (nsCOMPtr
<nsIDocumentViewer
> viewer
=
1321 do_QueryInterface(mDocViewerPrint
)) {
1322 // If we're print-previewing and the top level document, use the bounds
1323 // from our doc viewer. Page bounds is not what we want.
1324 LayoutDeviceIntRect bounds
;
1325 viewer
->GetBounds(bounds
);
1326 adjSize
= nsSize(bounds
.width
* p2a
, bounds
.height
* p2a
);
1329 aPO
->mPresContext
->SetIsRootPaginatedDocument(documentIsTopLevel
);
1330 aPO
->mPresContext
->SetVisibleArea(nsRect(nsPoint(), adjSize
));
1331 aPO
->mPresContext
->SetPageScale(aPO
->mZoomRatio
);
1332 // Calculate scale factor from printer to screen
1333 float printDPI
= float(AppUnitsPerCSSInch()) / float(p2a
);
1334 aPO
->mPresContext
->SetPrintPreviewScale(mScreenDPI
/ printDPI
);
1336 // Do CreatePresShell() after we setup the page size, the visible area, and
1337 // the flag |mIsRootPaginatedDocument|, to make sure we can resolve the
1338 // correct viewport size for the print preview page when notifying the media
1339 // feature values changed. See au_viewport_size_for_viewport_unit_resolution()
1340 // in media_queries.rs for more details.
1341 RefPtr
<Document
> doc
= aPO
->mDocument
;
1342 RefPtr
<nsPresContext
> presContext
= aPO
->mPresContext
;
1343 RefPtr
<nsViewManager
> viewManager
= aPO
->mViewManager
;
1345 aPO
->mPresShell
= doc
->CreatePresShell(presContext
, viewManager
);
1346 if (!aPO
->mPresShell
) {
1347 return NS_ERROR_FAILURE
;
1350 // If we're printing selection then remove the nonselected nodes from our
1352 if (mPrintSettings
->GetPrintSelectionOnly()) {
1353 // If we fail to remove the nodes then we should fail to print, because if
1354 // the user was trying to print a small selection from a large document,
1355 // sending the whole document to a real printer would be very frustrating.
1356 MOZ_TRY(DeleteNonSelectedNodes(*aPO
->mDocument
));
1359 aPO
->mPresShell
->BeginObservingDocument();
1362 ("In DV::ReflowPrintObject PO: %p pS: %p (%9s) Setting page size w,h to "
1364 aPO
.get(), aPO
->mPresShell
.get(), LoggableTypeOfPO(aPO
.get()),
1365 pageSize
.width
, pageSize
.height
));
1367 if (mIsCreatingPrintPreview
&& documentIsTopLevel
) {
1368 mDocViewerPrint
->SetPrintPreviewPresentation(
1369 aPO
->mViewManager
, aPO
->mPresContext
, aPO
->mPresShell
.get());
1372 MOZ_TRY(aPO
->mPresShell
->Initialize());
1373 NS_ASSERTION(aPO
->mPresShell
, "Presshell should still be here");
1375 RefPtr
<PresShell
> presShell
= aPO
->mPresShell
;
1377 const ServoStyleSet::PageSizeAndOrientation sizeAndOrientation
=
1378 presShell
->StyleSet()->GetDefaultPageSizeAndOrientation();
1379 // XXX Should we enable this for known save-to-PDF pseudo-printers once
1380 // bug 1826301 is fixed?
1381 if (mPrintSettings
->GetOutputFormat() ==
1382 nsIPrintSettings::kOutputFormatPDF
&&
1384 print_save_as_pdf_use_page_rule_size_as_paper_size_enabled()) {
1385 mMaybeCSSPageSize
= sizeAndOrientation
.size
;
1386 if (sizeAndOrientation
.size
) {
1387 pageSize
= sizeAndOrientation
.size
.value();
1388 aPO
->mPresContext
->SetPageSize(pageSize
);
1392 // If the document has a specified CSS page-size, we rotate the page to
1393 // reflect this. Changing the orientation is reflected by the result of
1394 // FinishPrintPreview, so that the frontend can reflect this.
1395 // The new document has not yet been reflowed, so we have to query the
1396 // original document for any CSS page-size.
1397 if (sizeAndOrientation
.orientation
) {
1398 switch (sizeAndOrientation
.orientation
.value()) {
1399 case StylePageSizeOrientation::Landscape
:
1400 if (pageSize
.width
< pageSize
.height
) {
1401 // Paper is in portrait, CSS page size is landscape.
1402 std::swap(pageSize
.width
, pageSize
.height
);
1405 case StylePageSizeOrientation::Portrait
:
1406 if (pageSize
.width
> pageSize
.height
) {
1407 // Paper is in landscape, CSS page size is portrait.
1408 std::swap(pageSize
.width
, pageSize
.height
);
1412 mMaybeCSSPageLandscape
= Some(sizeAndOrientation
.orientation
.value() ==
1413 StylePageSizeOrientation::Landscape
);
1414 aPO
->mPresContext
->SetPageSize(pageSize
);
1417 // Make sure animations are active.
1418 for (DocumentTimeline
* tl
: aPO
->mDocument
->Timelines()) {
1419 tl
->TriggerAllPendingAnimationsNow();
1421 // Process the reflow event Initialize posted
1422 presShell
->FlushPendingNotifications(FlushType::Layout
);
1423 aPO
->mDocument
->UpdateRemoteFrameEffects();
1425 MOZ_TRY(UpdateSelectionAndShrinkPrintObject(aPO
.get(), documentIsTopLevel
));
1427 #ifdef EXTENDED_DEBUG_PRINTING
1428 if (MOZ_LOG_TEST(gPrintingLog
, DUMP_LAYOUT_LEVEL
)) {
1429 nsAutoCString docStr
;
1430 nsAutoCString urlStr
;
1431 GetDocTitleAndURL(aPO
, docStr
, urlStr
);
1433 sprintf(filename
, "print_dump_%d.txt", gDumpFileNameCnt
++);
1434 // Dump all the frames and view to a a file
1435 FILE* fd
= fopen(filename
, "w");
1437 nsIFrame
* theRootFrame
= aPO
->mPresShell
->GetRootFrame();
1438 fprintf(fd
, "Title: %s\n", docStr
.get());
1439 fprintf(fd
, "URL: %s\n", urlStr
.get());
1440 fprintf(fd
, "--------------- Frames ----------------\n");
1441 // RefPtr<gfxContext> renderingContext =
1442 // printData->mPrintDocDC->CreateRenderingContext();
1443 RootFrameList(aPO
->mPresContext
, fd
, 0);
1444 // DumpFrames(fd, aPO->mPresContext, renderingContext, theRootFrame, 0);
1445 fprintf(fd
, "---------------------------------------\n\n");
1446 fprintf(fd
, "--------------- Views From Root Frame----------------\n");
1447 nsView
* v
= theRootFrame
->GetView();
1451 printf("View is null!\n");
1453 if (aPO
->mDocShell
) {
1454 fprintf(fd
, "--------------- All Views ----------------\n");
1455 DumpViews(aPO
->mDocShell
, fd
);
1456 fprintf(fd
, "---------------------------------------\n\n");
1466 //-------------------------------------------------------
1467 // Figure out how many documents and how many total pages we are printing
1468 void nsPrintJob::CalcNumPrintablePages(int32_t& aNumPages
) {
1470 // Count the number of printable documents and printable pages
1471 for (nsPrintObject
* po
: mPrintDocList
) {
1472 // Note: The po->mPresContext null-check below is necessary, because it's
1473 // possible po->mPresContext might never have been set. (e.g., if
1474 // PrintingIsEnabled() returns false, ReflowPrintObject bails before setting
1476 if (po
->mPresContext
&& po
->mPresContext
->IsRootPaginatedDocument()) {
1477 nsPageSequenceFrame
* seqFrame
= po
->mPresShell
->GetPageSequenceFrame();
1479 aNumPages
+= seqFrame
->PrincipalChildList().GetLength();
1485 //-----------------------------------------------------------------
1486 //-- Done: Reflow Methods
1487 //-----------------------------------------------------------------
1489 //-----------------------------------------------------------------
1490 //-- Section: Printing Methods
1491 //-----------------------------------------------------------------
1493 //-------------------------------------------------------
1494 // Called for each DocShell that needs to be printed
1495 bool nsPrintJob::PrintDocContent(const UniquePtr
<nsPrintObject
>& aPO
,
1496 nsresult
& aStatus
) {
1497 NS_ASSERTION(aPO
, "Pointer is null!");
1500 if (!aPO
->mHasBeenPrinted
&& aPO
->PrintingIsEnabled()) {
1501 aStatus
= DoPrint(aPO
);
1505 // If |aPO->mHasBeenPrinted| is true,
1506 // the kids frames are already processed in |PrintPage|.
1507 // XXX This should be removed. Since bug 1552785 it has no longer been
1508 // possible for us to have to print multiple subdocuments consecutively.
1509 if (!aPO
->mHasBeenPrinted
&& !aPO
->mInvisible
) {
1510 for (const UniquePtr
<nsPrintObject
>& po
: aPO
->mKids
) {
1511 bool printed
= PrintDocContent(po
, aStatus
);
1512 if (printed
|| NS_FAILED(aStatus
)) {
1520 // A helper struct to aid with DeleteNonSelectedNodes.
1521 struct MOZ_STACK_CLASS SelectionRangeState
{
1522 explicit SelectionRangeState(RefPtr
<Selection
> aSelection
)
1523 : mSelection(std::move(aSelection
)) {
1524 MOZ_ASSERT(mSelection
);
1525 MOZ_ASSERT(!mSelection
->RangeCount());
1528 // Selects all the nodes that are _not_ included in a given set of ranges.
1529 MOZ_CAN_RUN_SCRIPT
void SelectComplementOf(Span
<const RefPtr
<nsRange
>>);
1530 // Removes the selected ranges from the document.
1531 MOZ_CAN_RUN_SCRIPT
void RemoveSelectionFromDocument();
1539 MOZ_CAN_RUN_SCRIPT
void SelectRange(nsRange
*);
1540 MOZ_CAN_RUN_SCRIPT
void SelectNodesExcept(const Position
& aStart
,
1541 const Position
& aEnd
);
1542 MOZ_CAN_RUN_SCRIPT
void SelectNodesExceptInSubtree(const Position
& aStart
,
1543 const Position
& aEnd
);
1545 // A map from subtree root (document or shadow root) to the start position of
1546 // the non-selected content (so far).
1547 nsTHashMap
<nsPtrHashKey
<nsINode
>, Position
> mPositions
;
1549 // The selection we're adding the ranges to.
1550 const RefPtr
<Selection
> mSelection
;
1553 void SelectionRangeState::SelectComplementOf(
1554 Span
<const RefPtr
<nsRange
>> aRanges
) {
1555 for (const auto& range
: aRanges
) {
1556 auto start
= Position
{range
->GetMayCrossShadowBoundaryStartContainer(),
1557 range
->MayCrossShadowBoundaryStartOffset()};
1558 auto end
= Position
{range
->GetMayCrossShadowBoundaryEndContainer(),
1559 range
->MayCrossShadowBoundaryEndOffset()};
1560 SelectNodesExcept(start
, end
);
1564 void SelectionRangeState::SelectRange(nsRange
* aRange
) {
1565 if (aRange
&& !aRange
->AreNormalRangeAndCrossShadowBoundaryRangeCollapsed()) {
1566 mSelection
->AddRangeAndSelectFramesAndNotifyListeners(*aRange
,
1571 void SelectionRangeState::SelectNodesExcept(const Position
& aStart
,
1572 const Position
& aEnd
) {
1573 SelectNodesExceptInSubtree(aStart
, aEnd
);
1574 if (!StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) {
1575 if (auto* shadow
= ShadowRoot::FromNode(aStart
.mNode
->SubtreeRoot())) {
1576 auto* host
= shadow
->Host();
1577 // Can't just select other nodes except the host, because other nodes that
1578 // are not in this particular shadow tree could also be selected
1579 SelectNodesExcept(Position
{host
, 0},
1580 Position
{host
, host
->GetChildCount()});
1582 MOZ_ASSERT(aStart
.mNode
->IsInUncomposedDoc());
1587 void SelectionRangeState::SelectNodesExceptInSubtree(const Position
& aStart
,
1588 const Position
& aEnd
) {
1589 static constexpr auto kEllipsis
= u
"\x2026"_ns
;
1591 // Finish https://bugzilla.mozilla.org/show_bug.cgi?id=1903871 once the pref
1592 // is shipped, so that we only need one position.
1593 nsINode
* root
= StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()
1594 ? aStart
.mNode
->OwnerDoc()
1595 : aStart
.mNode
->SubtreeRoot();
1597 mPositions
.WithEntryHandle(root
, [&](auto&& entry
) -> Position
& {
1598 return entry
.OrInsertWith([&] { return Position
{root
, 0}; });
1601 bool ellipsizedStart
= false;
1602 if (auto* text
= Text::FromNode(aStart
.mNode
)) {
1603 if (start
.mNode
!= text
&& aStart
.mOffset
&&
1604 aStart
.mOffset
< text
->Length()) {
1605 text
->InsertData(aStart
.mOffset
, kEllipsis
, IgnoreErrors());
1606 ellipsizedStart
= true;
1610 RefPtr
<nsRange
> range
= nsRange::Create(
1611 start
.mNode
, start
.mOffset
, aStart
.mNode
, aStart
.mOffset
, IgnoreErrors());
1616 // If we added an ellipsis at the start and the end position was relative to
1617 // the same node account for it here.
1618 if (ellipsizedStart
&& aStart
.mNode
== aEnd
.mNode
) {
1619 start
.mOffset
+= kEllipsis
.Length();
1622 // If the end is mid text then add an ellipsis.
1623 if (auto* text
= Text::FromNode(start
.mNode
)) {
1624 if (start
.mOffset
&& start
.mOffset
< text
->Length()) {
1625 text
->InsertData(start
.mOffset
, kEllipsis
, IgnoreErrors());
1626 start
.mOffset
+= kEllipsis
.Length();
1631 void SelectionRangeState::RemoveSelectionFromDocument() {
1632 for (auto& entry
: mPositions
) {
1633 const Position
& pos
= entry
.GetData();
1634 nsINode
* root
= entry
.GetKey();
1635 RefPtr
<nsRange
> range
= nsRange::Create(
1636 pos
.mNode
, pos
.mOffset
, root
, root
->GetChildCount(), IgnoreErrors());
1639 mSelection
->DeleteFromDocument(IgnoreErrors());
1643 * Builds the complement set of ranges and adds those to the selection.
1644 * Deletes all of the nodes contained in the complement set of ranges
1645 * leaving behind only nodes that were originally selected.
1646 * Adds ellipses to a selected node's text if text is truncated by a range.
1647 * This is used to implement the "Print Selection Only" user interface option.
1649 MOZ_CAN_RUN_SCRIPT_BOUNDARY
static nsresult
DeleteNonSelectedNodes(
1651 MOZ_ASSERT(aDoc
.IsStaticDocument());
1652 const auto* printRanges
= static_cast<nsTArray
<RefPtr
<nsRange
>>*>(
1653 aDoc
.GetProperty(nsGkAtoms::printselectionranges
));
1658 PresShell
* presShell
= aDoc
.GetPresShell();
1659 NS_ENSURE_STATE(presShell
);
1660 RefPtr
<Selection
> selection
=
1661 presShell
->GetCurrentSelection(SelectionType::eNormal
);
1662 NS_ENSURE_STATE(selection
);
1664 SelectionRangeState
state(std::move(selection
));
1665 state
.SelectComplementOf(*printRanges
);
1666 state
.RemoveSelectionFromDocument();
1670 //-------------------------------------------------------
1671 nsresult
nsPrintJob::DoPrint(const UniquePtr
<nsPrintObject
>& aPO
) {
1673 PR_PL(("**************************** %s ****************************\n",
1674 LoggableTypeOfPO(aPO
.get())));
1675 PR_PL(("****** In DV::DoPrint PO: %p \n", aPO
.get()));
1677 PresShell
* poPresShell
= aPO
->mPresShell
;
1678 nsPresContext
* poPresContext
= aPO
->mPresContext
;
1680 NS_ASSERTION(poPresContext
, "PrintObject has not been reflowed");
1681 NS_ASSERTION(poPresContext
->Type() != nsPresContext::eContext_PrintPreview
,
1682 "How did this context end up here?");
1684 // Guarantee that mPrt and the objects it owns won't be deleted in this method
1685 // because it might be cleared if other modules called from here may fire
1686 // events, notifying observers and/or listeners.
1687 RefPtr
<nsPrintData
> printData
= mPrt
;
1688 if (NS_WARN_IF(!printData
)) {
1689 return NS_ERROR_FAILURE
;
1693 // Ask the page sequence frame to print all the pages
1694 nsPageSequenceFrame
* seqFrame
= poPresShell
->GetPageSequenceFrame();
1695 MOZ_ASSERT(seqFrame
, "no page sequence frame");
1697 // We are done preparing for printing, so we can turn this off
1698 mPreparingForPrint
= false;
1700 #ifdef EXTENDED_DEBUG_PRINTING
1701 nsIFrame
* rootFrame
= poPresShell
->GetRootFrame();
1702 if (aPO
->PrintingIsEnabled()) {
1703 nsAutoCString docStr
;
1704 nsAutoCString urlStr
;
1705 GetDocTitleAndURL(aPO
, docStr
, urlStr
);
1706 DumpLayoutData(docStr
.get(), urlStr
.get(), poPresContext
,
1707 printData
->mPrintDC
, rootFrame
, aPO
->mDocShell
, nullptr);
1711 if (!mPrintSettings
) {
1712 // not sure what to do here!
1713 SetIsPrinting(false);
1714 return NS_ERROR_FAILURE
;
1717 nsAutoString docTitleStr
;
1718 nsAutoString docURLStr
;
1719 GetDisplayTitleAndURL(*aPO
->mDocument
, mPrintSettings
,
1720 DocTitleDefault::eFallback
, docTitleStr
, docURLStr
);
1723 SetIsPrinting(false);
1724 return NS_ERROR_FAILURE
;
1727 mPageSeqFrame
= seqFrame
;
1728 seqFrame
->StartPrint(poPresContext
, mPrintSettings
, docTitleStr
, docURLStr
);
1730 // Schedule Page to Print
1731 PR_PL(("Scheduling Print of PO: %p (%s) \n", aPO
.get(),
1732 LoggableTypeOfPO(aPO
.get())));
1733 StartPagePrintTimer(aPO
);
1739 //-------------------------------------------------------
1740 bool nsPrintJob::PrePrintSheet() {
1741 NS_ASSERTION(mPageSeqFrame
.IsAlive(), "mPageSeqFrame is not alive!");
1742 NS_ASSERTION(mPrt
, "mPrt is null!");
1744 // Although these should NEVER be nullptr
1745 // This is added insurance, to make sure we don't crash in optimized builds
1746 if (!mPrt
|| !mPageSeqFrame
.IsAlive()) {
1747 return true; // means we are done preparing the sheet.
1750 // Guarantee that mPrt won't be deleted during a call of
1751 // FirePrintingErrorEvent().
1752 RefPtr
<nsPrintData
> printData
= mPrt
;
1754 // Ask mPageSeqFrame if the sheet is ready to be printed.
1755 // If the sheet doesn't get printed at all, the |done| will be |true|.
1757 nsPageSequenceFrame
* pageSeqFrame
= do_QueryFrame(mPageSeqFrame
.GetFrame());
1758 nsresult rv
= pageSeqFrame
->PrePrintNextSheet(mPagePrintTimer
, &done
);
1759 if (NS_FAILED(rv
)) {
1760 // ??? ::PrintSheet doesn't set |printData->mIsAborted = true| if
1761 // rv != NS_ERROR_ABORT, but I don't really understand why this should be
1762 // the right thing to do? Shouldn't |printData->mIsAborted| set to true
1763 // all the time if something went wrong?
1764 if (rv
!= NS_ERROR_ABORT
) {
1765 FirePrintingErrorEvent(rv
);
1766 printData
->mIsAborted
= true;
1773 bool nsPrintJob::PrintSheet(nsPrintObject
* aPO
) {
1774 NS_ASSERTION(aPO
, "aPO is null!");
1775 NS_ASSERTION(mPageSeqFrame
.IsAlive(), "mPageSeqFrame is not alive!");
1776 NS_ASSERTION(mPrt
, "mPrt is null!");
1778 // Although these should NEVER be nullptr
1779 // This is added insurance, to make sure we don't crash in optimized builds
1780 if (!mPrt
|| !aPO
|| !mPageSeqFrame
.IsAlive()) {
1781 FirePrintingErrorEvent(NS_ERROR_FAILURE
);
1782 return true; // means we are done printing
1785 // Guarantee that mPrt won't be deleted during a call of
1786 // nsPrintData::DoOnProgressChange() which runs some listeners,
1787 // which may clear (& might otherwise destroy).
1788 RefPtr
<nsPrintData
> printData
= mPrt
;
1790 PR_PL(("-----------------------------------\n"));
1791 PR_PL(("------ In DV::PrintSheet PO: %p (%s)\n", aPO
, LoggableTypeOfPO(aPO
)));
1793 if (printData
->mIsAborted
) {
1797 nsPageSequenceFrame
* pageSeqFrame
= do_QueryFrame(mPageSeqFrame
.GetFrame());
1798 const uint32_t sheetIdx
= pageSeqFrame
->GetCurrentSheetIdx();
1799 const uint32_t numSheets
= pageSeqFrame
->PrincipalChildList().GetLength();
1801 PR_PL(("****** Printing sheet index %d of %d sheets(s)\n", sheetIdx
,
1804 MOZ_ASSERT(numSheets
> 0, "print operations must have at least 1 sheet");
1805 MOZ_ASSERT(sheetIdx
< numSheets
,
1806 "sheetIdx shouldn't be allowed to go out of bounds");
1807 printData
->DoOnProgressChange(sheetIdx
, numSheets
, false, 0);
1808 if (NS_WARN_IF(mPrt
!= printData
)) {
1809 // If current printing is canceled or new print is started, let's return
1810 // true to notify the caller of current printing is done.
1815 // if a print job was cancelled externally, an EndPage or BeginPage may
1816 // fail and the failure is passed back here.
1817 // Returning true means we are done printing.
1819 // When rv == NS_ERROR_ABORT, it means we want out of the
1820 // print job without displaying any error messages
1821 nsresult rv
= pageSeqFrame
->PrintNextSheet();
1822 if (NS_FAILED(rv
)) {
1823 if (rv
!= NS_ERROR_ABORT
) {
1824 FirePrintingErrorEvent(rv
);
1825 printData
->mIsAborted
= true;
1830 pageSeqFrame
->DoPageEnd();
1832 // If we just printed the final sheet (the one with index "numSheets-1"),
1834 return (sheetIdx
== numSheets
- 1);
1837 void nsPrintJob::PageDone(nsresult aResult
) {
1838 MOZ_ASSERT(mIsDoingPrinting
);
1840 // mPagePrintTimer might be released during RemotePrintFinished, keep a
1841 // reference here to make sure it lives long enough.
1842 RefPtr
<nsPagePrintTimer
> timer
= mPagePrintTimer
;
1843 timer
->RemotePrintFinished();
1846 //-----------------------------------------------------------------
1847 //-- Done: Printing Methods
1848 //-----------------------------------------------------------------
1850 //-----------------------------------------------------------------
1851 //-- Section: Misc Support Methods
1852 //-----------------------------------------------------------------
1854 //---------------------------------------------------------------------
1855 void nsPrintJob::SetIsPrinting(bool aIsPrinting
) {
1856 mIsDoingPrinting
= aIsPrinting
;
1858 mPreparingForPrint
= true;
1862 //---------------------------------------------------------------------
1863 void nsPrintJob::SetIsPrintPreview(bool aIsPrintPreview
) {
1864 mCreatedForPrintPreview
= aIsPrintPreview
;
1866 if (mDocViewerPrint
) {
1867 mDocViewerPrint
->SetIsPrintPreview(aIsPrintPreview
);
1871 //-------------------------------------------------------
1872 bool nsPrintJob::DonePrintingSheets(nsPrintObject
* aPO
, nsresult aResult
) {
1873 // NS_ASSERTION(aPO, "Pointer is null!");
1874 PR_PL(("****** In DV::DonePrintingSheets PO: %p (%s)\n", aPO
,
1875 aPO
? LoggableTypeOfPO(aPO
) : ""));
1877 // If there is a pageSeqFrame, make sure there are no more printCanvas active
1878 // that might call |Notify| on the pagePrintTimer after things are cleaned up
1879 // and printing was marked as being done.
1880 if (mPageSeqFrame
.IsAlive()) {
1881 nsPageSequenceFrame
* pageSeqFrame
= do_QueryFrame(mPageSeqFrame
.GetFrame());
1882 pageSeqFrame
->ResetPrintCanvasList();
1885 // Guarantee that mPrt and mPrintObject won't be deleted during a
1886 // call of PrintDocContent() and FirePrintCompletionEvent().
1887 RefPtr
<nsPrintData
> printData
= mPrt
;
1889 if (aPO
&& !printData
->mIsAborted
) {
1890 aPO
->mHasBeenPrinted
= true;
1892 bool didPrint
= PrintDocContent(mPrintObject
, rv
);
1893 if (NS_SUCCEEDED(rv
) && didPrint
) {
1895 ("****** In DV::DonePrintingSheets PO: %p (%s) didPrint:%s (Not Done "
1897 aPO
, LoggableTypeOfPO(aPO
), PRT_YESNO(didPrint
)));
1902 if (NS_SUCCEEDED(aResult
)) {
1903 FirePrintCompletionEvent();
1904 // XXX mPrt may be cleared or replaced with new instance here.
1905 // However, the following methods will clean up with new mPrt or will
1906 // do nothing due to no proper nsPrintData instance.
1909 SetIsPrinting(false);
1911 // Release reference to mPagePrintTimer; the timer object destroys itself
1912 // after this returns true
1913 DisconnectPagePrintTimer();
1918 //-------------------------------------------------------
1919 nsresult
nsPrintJob::EnablePOsForPrinting() {
1920 // Guarantee that mPrt and the objects it owns won't be deleted.
1921 RefPtr
<nsPrintData
> printData
= mPrt
;
1923 // NOTE: All POs have been "turned off" for printing
1924 // this is where we decided which POs get printed.
1926 if (!printData
|| !mPrintSettings
) {
1927 return NS_ERROR_FAILURE
;
1931 PR_PL(("********* nsPrintJob::EnablePOsForPrinting *********\n"));
1933 if (!mPrintSettings
->GetPrintSelectionOnly()) {
1934 mPrintObject
->EnablePrinting(true);
1938 // This means we are either printing a selected iframe or
1939 // we are printing the current selection.
1940 NS_ENSURE_STATE(!mDisallowSelectionPrint
&& mSelectionRoot
);
1942 // If mSelectionRoot is a selected iframe without a selection, then just
1943 // enable normally from that point.
1944 if (mSelectionRoot
->mParent
&& !mSelectionRoot
->HasSelection()) {
1945 mSelectionRoot
->EnablePrinting(true);
1947 // Otherwise, only enable nsPrintObjects that have a selection.
1948 mSelectionRoot
->EnablePrintingSelectionOnly();
1953 //-----------------------------------------------------------------
1954 //-- Done: Misc Support Methods
1955 //-----------------------------------------------------------------
1957 //-----------------------------------------------------------------
1958 //-- Section: Finishing up or Cleaning up
1959 //-----------------------------------------------------------------
1961 //-----------------------------------------------------------------
1962 nsresult
nsPrintJob::FinishPrintPreview() {
1963 nsresult rv
= NS_OK
;
1965 #ifdef NS_PRINT_PREVIEW
1967 // If mPrt is null we've already finished with print preview. If mPrt is not
1968 // null but mIsCreatingPrintPreview is false FinishPrintPreview must have
1969 // already failed due to DocumentReadyForPrinting failing.
1970 if (!mPrt
|| !mIsCreatingPrintPreview
) {
1974 rv
= DocumentReadyForPrinting();
1976 // Note that this method may be called while the instance is being
1977 // initialized. Some methods which initialize the instance (e.g.,
1978 // DoCommonPrint) may need to stop initializing and return error if
1979 // this is called. Therefore it's important to set mIsCreatingPrintPreview
1980 // state to false here. If you need to stop setting that here, you need to
1981 // keep them being able to check whether the owner stopped using this
1983 mIsCreatingPrintPreview
= false;
1985 // mPrt may be cleared during a call of nsPrintData::OnEndPrinting()
1986 // because that method invokes some arbitrary listeners.
1987 // TODO(dshin): Does any listener attach to print preview? Doesn't seem like
1988 // we call matching `OnStartPrinting()` for previews.
1989 RefPtr
<nsPrintData
> printData
= mPrt
;
1990 if (NS_FAILED(rv
)) {
1991 printData
->OnEndPrinting();
1996 if (mPrintPreviewCallback
) {
1997 const bool hasSelection
= !mDisallowSelectionPrint
&& mSelectionRoot
;
1999 Maybe
<float> pageWidth
;
2000 Maybe
<float> pageHeight
;
2001 if (mMaybeCSSPageSize
) {
2002 nsSize cssPageSize
= *mMaybeCSSPageSize
;
2003 pageWidth
= Some(float(cssPageSize
.width
) / float(AppUnitsPerCSSInch()));
2005 Some(float(cssPageSize
.height
) / float(AppUnitsPerCSSInch()));
2008 mPrintPreviewCallback(PrintPreviewResultInfo(
2009 GetPrintPreviewNumSheets(), GetRawNumPages(), GetIsEmpty(),
2010 hasSelection
, hasSelection
&& mPrintObject
->HasSelection(),
2011 mMaybeCSSPageLandscape
, pageWidth
, pageHeight
));
2012 mPrintPreviewCallback
= nullptr;
2015 // At this point we are done preparing everything
2016 // before it is to be created
2018 printData
->OnEndPrinting();
2020 #endif // NS_PRINT_PREVIEW
2025 //-----------------------------------------------------------------
2026 //-- Done: Finishing up or Cleaning up
2027 //-----------------------------------------------------------------
2029 /*=============== Timer Related Code ======================*/
2030 nsresult
nsPrintJob::StartPagePrintTimer(const UniquePtr
<nsPrintObject
>& aPO
) {
2031 if (!mPagePrintTimer
) {
2032 // Get the delay time in between the printing of each page
2033 // this gives the user more time to press cancel
2034 int32_t printPageDelay
= mPrintSettings
->GetPrintPageDelay();
2036 nsCOMPtr
<nsIDocumentViewer
> viewer
= do_QueryInterface(mDocViewerPrint
);
2037 NS_ENSURE_TRUE(viewer
, NS_ERROR_FAILURE
);
2038 nsCOMPtr
<Document
> doc
= viewer
->GetDocument();
2039 NS_ENSURE_TRUE(doc
, NS_ERROR_FAILURE
);
2042 new nsPagePrintTimer(this, mDocViewerPrint
, doc
, printPageDelay
);
2044 if (mRemotePrintJob
) {
2045 mRemotePrintJob
->SetPagePrintTimer(mPagePrintTimer
);
2046 mRemotePrintJob
->SetPrintJob(this);
2050 return mPagePrintTimer
->Start(aPO
.get());
2053 //---------------------------------------------------------------
2054 //-- PLEvent Notification
2055 //---------------------------------------------------------------
2056 class nsPrintCompletionEvent
: public Runnable
{
2058 explicit nsPrintCompletionEvent(nsIDocumentViewerPrint
* docViewerPrint
)
2059 : mozilla::Runnable("nsPrintCompletionEvent"),
2060 mDocViewerPrint(docViewerPrint
) {
2061 NS_ASSERTION(mDocViewerPrint
, "mDocViewerPrint is null.");
2064 NS_IMETHOD
Run() override
{
2065 if (mDocViewerPrint
) {
2066 mDocViewerPrint
->OnDonePrinting();
2072 nsCOMPtr
<nsIDocumentViewerPrint
> mDocViewerPrint
;
2075 //-----------------------------------------------------------
2076 void nsPrintJob::FirePrintCompletionEvent() {
2077 MOZ_ASSERT(NS_IsMainThread());
2078 nsCOMPtr
<nsIRunnable
> event
= new nsPrintCompletionEvent(mDocViewerPrint
);
2079 nsCOMPtr
<nsIDocumentViewer
> viewer
= do_QueryInterface(mDocViewerPrint
);
2080 NS_ENSURE_TRUE_VOID(viewer
);
2081 nsCOMPtr
<Document
> doc
= viewer
->GetDocument();
2082 NS_ENSURE_TRUE_VOID(doc
);
2083 NS_ENSURE_SUCCESS_VOID(doc
->Dispatch(event
.forget()));
2086 void nsPrintJob::DisconnectPagePrintTimer() {
2087 if (mPagePrintTimer
) {
2088 mPagePrintTimer
->Disconnect();
2089 mPagePrintTimer
= nullptr;
2093 //---------------------------------------------------------------
2094 //---------------------------------------------------------------
2095 //-- Debug helper routines
2096 //---------------------------------------------------------------
2097 //---------------------------------------------------------------
2098 #if defined(XP_WIN) && defined(EXTENDED_DEBUG_PRINTING)
2099 # include <windows.h>
2100 # include <process.h>
2101 # include <direct.h>
2103 # define MY_FINDFIRST(a, b) FindFirstFile(a, b)
2104 # define MY_FINDNEXT(a, b) FindNextFile(a, b)
2105 # define ISDIR(a) (a.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
2106 # define MY_FINDCLOSE(a) FindClose(a)
2107 # define MY_FILENAME(a) a.cFileName
2108 # define MY_FILESIZE(a) (a.nFileSizeHigh * MAXDWORD) + a.nFileSizeLow
2110 int RemoveFilesInDir(const char* aDir
) {
2111 WIN32_FIND_DATA data_ptr
;
2114 char path
[MAX_PATH
];
2118 // Append slash to the end of the directory names if not there
2119 if (path
[strlen(path
) - 1] != '\\') strcat(path
, "\\");
2121 char findPath
[MAX_PATH
];
2122 strcpy(findPath
, path
);
2123 strcat(findPath
, "*.*");
2125 find_handle
= MY_FINDFIRST(findPath
, &data_ptr
);
2127 if (find_handle
!= INVALID_HANDLE_VALUE
) {
2129 if (ISDIR(data_ptr
) && (stricmp(MY_FILENAME(data_ptr
), ".")) &&
2130 (stricmp(MY_FILENAME(data_ptr
), ".."))) {
2132 } else if (!ISDIR(data_ptr
)) {
2133 if (!strncmp(MY_FILENAME(data_ptr
), "print_dump", 10)) {
2134 char fileName
[MAX_PATH
];
2135 strcpy(fileName
, aDir
);
2136 strcat(fileName
, "\\");
2137 strcat(fileName
, MY_FILENAME(data_ptr
));
2138 printf("Removing %s\n", fileName
);
2142 } while (MY_FINDNEXT(find_handle
, &data_ptr
));
2143 MY_FINDCLOSE(find_handle
);
2149 #ifdef EXTENDED_DEBUG_PRINTING
2151 /** ---------------------------------------------------
2152 * Dumps Frames for Printing
2154 static void RootFrameList(nsPresContext
* aPresContext
, FILE* out
,
2155 const char* aPrefix
) {
2156 if (!aPresContext
|| !out
) return;
2158 if (PresShell
* presShell
= aPresContext
->GetPresShell()) {
2159 nsIFrame
* frame
= presShell
->GetRootFrame();
2161 frame
->List(out
, aPrefix
);
2166 /** ---------------------------------------------------
2167 * Dumps Frames for Printing
2169 static void DumpFrames(FILE* out
, nsPresContext
* aPresContext
,
2170 gfxContext
* aRendContext
, nsIFrame
* aFrame
,
2172 NS_ASSERTION(out
, "Pointer is null!");
2173 NS_ASSERTION(aPresContext
, "Pointer is null!");
2174 NS_ASSERTION(aRendContext
, "Pointer is null!");
2175 NS_ASSERTION(aFrame
, "Pointer is null!");
2177 nsIFrame
* child
= aFrame
->PrincipalChildList().FirstChild();
2178 while (child
!= nullptr) {
2179 for (int32_t i
= 0; i
< aLevel
; i
++) {
2183 child
->GetFrameName(tmp
);
2184 fputs(NS_LossyConvertUTF16toASCII(tmp
).get(), out
);
2186 if (child
->IsVisibleForPainting()) {
2187 fprintf(out
, " %p %s", child
, isSelected
? "VIS" : "UVS");
2188 nsRect rect
= child
->GetRect();
2189 fprintf(out
, "[%d,%d,%d,%d] ", rect
.x
, rect
.y
, rect
.width
, rect
.height
);
2190 fprintf(out
, "v: %p ", (void*)child
->GetView());
2192 DumpFrames(out
, aPresContext
, aRendContext
, child
, aLevel
+ 1);
2193 child
= child
->GetNextSibling();
2198 /** ---------------------------------------------------
2199 * Dumps the Views from the DocShell
2201 static void DumpViews(nsIDocShell
* aDocShell
, FILE* out
) {
2202 NS_ASSERTION(aDocShell
, "Pointer is null!");
2203 NS_ASSERTION(out
, "Pointer is null!");
2205 if (nullptr != aDocShell
) {
2206 fprintf(out
, "docshell=%p \n", aDocShell
);
2207 if (PresShell
* presShell
= aDocShell
->GetPresShell()) {
2208 nsViewManager
* vm
= presShell
->GetViewManager();
2210 nsView
* root
= vm
->GetRootView();
2216 fputs("null pres shell\n", out
);
2219 // dump the views of the sub documents
2221 BrowsingContext
* bc
= nsDocShell::Cast(aDocShell
)->GetBrowsingContext();
2222 for (auto& child
: bc
->Children()) {
2223 if (auto childDS
= child
->GetDocShell()) {
2224 DumpViews(childAsShell
, out
);
2230 /** ---------------------------------------------------
2231 * Dumps the Views and Frames
2233 void DumpLayoutData(const char* aTitleStr
, const char* aURLStr
,
2234 nsPresContext
* aPresContext
, nsDeviceContext
* aDC
,
2235 nsIFrame
* aRootFrame
, nsIDocShell
* aDocShell
,
2236 FILE* aFD
= nullptr) {
2237 if (!MOZ_LOG_TEST(gPrintingLog
, DUMP_LAYOUT_LEVEL
)) {
2241 if (aPresContext
== nullptr || aDC
== nullptr) {
2245 # ifdef NS_PRINT_PREVIEW
2246 if (aPresContext
->Type() == nsPresContext::eContext_PrintPreview
) {
2251 NS_ASSERTION(aRootFrame
, "Pointer is null!");
2252 NS_ASSERTION(aDocShell
, "Pointer is null!");
2254 // Dump all the frames and view to a a file
2256 sprintf(filename
, "print_dump_layout_%d.txt", gDumpLOFileNameCnt
++);
2257 FILE* fd
= aFD
? aFD
: fopen(filename
, "w");
2259 fprintf(fd
, "Title: %s\n", aTitleStr
? aTitleStr
: "");
2260 fprintf(fd
, "URL: %s\n", aURLStr
? aURLStr
: "");
2261 fprintf(fd
, "--------------- Frames ----------------\n");
2262 fprintf(fd
, "--------------- Frames ----------------\n");
2263 // RefPtr<gfxContext> renderingContext =
2264 // aDC->CreateRenderingContext();
2265 RootFrameList(aPresContext
, fd
, "");
2266 // DumpFrames(fd, aPresContext, renderingContext, aRootFrame, 0);
2267 fprintf(fd
, "---------------------------------------\n\n");
2268 fprintf(fd
, "--------------- Views From Root Frame----------------\n");
2269 nsView
* v
= aRootFrame
->GetView();
2273 printf("View is null!\n");
2276 fprintf(fd
, "--------------- All Views ----------------\n");
2277 DumpViews(aDocShell
, fd
);
2278 fprintf(fd
, "---------------------------------------\n\n");
2280 if (aFD
== nullptr) {
2286 //-------------------------------------------------------------
2287 static void DumpPrintObjectsList(const nsTArray
<nsPrintObject
*>& aDocList
) {
2288 if (!MOZ_LOG_TEST(gPrintingLog
, DUMP_LAYOUT_LEVEL
)) {
2292 PR_PL(("Doc List\n***************************************************\n"));
2294 ("T P A H PO DocShell Seq Page Root Page# "
2296 for (nsPrintObject
* po
: aDocList
) {
2297 NS_ASSERTION(po
, "nsPrintObject can't be null!");
2298 nsIFrame
* rootFrame
= nullptr;
2299 if (po
->mPresShell
) {
2300 rootFrame
= po
->mPresShell
->GetRootFrame();
2301 while (rootFrame
!= nullptr) {
2302 nsPageSequenceFrame
* sqf
= do_QueryFrame(rootFrame
);
2306 rootFrame
= rootFrame
->PrincipalChildList().FirstChild();
2310 PR_PL(("%s %d %d %p %p %p\n", ShortLoggableTypeOfPO(po
),
2311 po
->PrintingIsEnabled(), po
->mHasBeenPrinted
, po
,
2312 po
->mDocShell
.get(), rootFrame
));
2316 //-------------------------------------------------------------
2317 static void DumpPrintObjectsTree(nsPrintObject
* aPO
, int aLevel
, FILE* aFD
) {
2318 if (!MOZ_LOG_TEST(gPrintingLog
, DUMP_LAYOUT_LEVEL
)) {
2322 NS_ASSERTION(aPO
, "Pointer is null!");
2324 FILE* fd
= aFD
? aFD
: stdout
;
2327 "DocTree\n***************************************************\n");
2328 fprintf(fd
, "T PO DocShell Seq Page Page# Rect\n");
2330 for (const auto& po
: aPO
->mKids
) {
2331 NS_ASSERTION(po
, "nsPrintObject can't be null!");
2332 for (int32_t k
= 0; k
< aLevel
; k
++) fprintf(fd
, " ");
2333 fprintf(fd
, "%s %p %p\n", ShortLoggableTypeOfPO(po
.get()), po
.get(),
2334 po
->mDocShell
.get());
2338 //-------------------------------------------------------------
2339 static void GetDocTitleAndURL(const UniquePtr
<nsPrintObject
>& aPO
,
2340 nsACString
& aDocStr
, nsACString
& aURLStr
) {
2341 nsAutoString docTitleStr
;
2342 nsAutoString docURLStr
;
2343 GetDocumentTitleAndURL(aPO
->mDocument
, docTitleStr
, docURLStr
);
2344 CopyUTF16toUTF8(docTitleStr
, aDocStr
);
2345 CopyUTF16toUTF8(docURLStr
, aURLStr
);
2348 //-------------------------------------------------------------
2349 static void DumpPrintObjectsTreeLayout(const UniquePtr
<nsPrintObject
>& aPO
,
2350 nsDeviceContext
* aDC
, int aLevel
,
2352 if (!MOZ_LOG_TEST(gPrintingLog
, DUMP_LAYOUT_LEVEL
)) {
2356 NS_ASSERTION(aPO
, "Pointer is null!");
2357 NS_ASSERTION(aDC
, "Pointer is null!");
2361 fd
= fopen("tree_layout.txt", "w");
2363 "DocTree\n***************************************************\n");
2364 fprintf(fd
, "***************************************************\n");
2365 fprintf(fd
, "T PO DocShell Seq Page Page# Rect\n");
2370 nsIFrame
* rootFrame
= nullptr;
2371 if (aPO
->mPresShell
) {
2372 rootFrame
= aPO
->mPresShell
->GetRootFrame();
2374 for (int32_t k
= 0; k
< aLevel
; k
++) fprintf(fd
, " ");
2375 fprintf(fd
, "%s %p %p\n", ShortLoggableTypeOfPO(aPO
.get()), aPO
.get(),
2376 aPO
->mDocShell
.get());
2377 if (aPO
->PrintingIsEnabled()) {
2378 nsAutoCString docStr
;
2379 nsAutoCString urlStr
;
2380 GetDocTitleAndURL(aPO
, docStr
, urlStr
);
2381 DumpLayoutData(docStr
.get(), urlStr
.get(), aPO
->mPresContext
, aDC
,
2382 rootFrame
, aPO
->mDocShell
, fd
);
2384 fprintf(fd
, "<***************************************************>\n");
2386 for (const auto& po
: aPO
->mKids
) {
2387 NS_ASSERTION(po
, "nsPrintObject can't be null!");
2388 DumpPrintObjectsTreeLayout(po
, aDC
, aLevel
+ 1, fd
);
2391 if (aLevel
== 0 && fd
) {
2396 //-------------------------------------------------------------
2397 static void DumpPrintObjectsListStart(
2398 const char* aStr
, const nsTArray
<nsPrintObject
*>& aDocList
) {
2399 if (!MOZ_LOG_TEST(gPrintingLog
, DUMP_LAYOUT_LEVEL
)) {
2403 NS_ASSERTION(aStr
, "Pointer is null!");
2405 PR_PL(("%s\n", aStr
));
2406 DumpPrintObjectsList(aDocList
);
2411 //---------------------------------------------------------------
2412 //---------------------------------------------------------------
2413 //-- End of debug helper routines
2414 //---------------------------------------------------------------