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 "mozilla/Try.h"
31 #include "nsIBrowserChild.h"
32 #include "nsIOService.h"
33 #include "nsIScriptGlobalObject.h"
34 #include "nsIStringBundle.h"
35 #include "nsPIDOMWindow.h"
36 #include "nsPrintData.h"
37 #include "nsPrintObject.h"
38 #include "nsIDocShell.h"
40 #include "nsITextToSubURI.h"
47 #include "nsIPrintSettings.h"
48 #include "nsIPrintSettingsService.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")
121 inline const char* LoggableTypeOfPO(const nsPrintObject
* aPO
) {
122 // TODO(dholbert): These strings used to represent actual enum values, but
123 // the enum type has been removed. We could replace them with more
124 // descriptive names, if anyone uses this logging and cares to do so.
125 return aPO
->mParent
? "eIFrame" : "eDoc";
128 inline const char* ShortLoggableTypeOfPO(const nsPrintObject
* aPO
) {
129 return aPO
->mParent
? "IF" : "DC";
132 // This processes the selection on aOrigDoc and creates an inverted selection on
133 // aDoc, which it then deletes. If the start or end of the inverted selection
134 // ranges occur in text nodes then an ellipsis is added.
135 static nsresult
DeleteNonSelectedNodes(Document
& aDoc
);
137 #ifdef EXTENDED_DEBUG_PRINTING
138 // Forward Declarations
139 static void DumpPrintObjectsListStart(const char* aStr
,
140 const nsTArray
<nsPrintObject
*>& aDocList
);
141 static void DumpPrintObjectsTree(nsPrintObject
* aPO
, int aLevel
= 0,
142 FILE* aFD
= nullptr);
143 static void DumpPrintObjectsTreeLayout(const UniquePtr
<nsPrintObject
>& aPO
,
144 nsDeviceContext
* aDC
, int aLevel
= 0,
145 FILE* aFD
= nullptr);
147 # define DUMP_DOC_LIST(_title) \
148 DumpPrintObjectsListStart((_title), mPrintDocList);
149 # define DUMP_DOC_TREE DumpPrintObjectsTree(mPrintObject.get());
150 # define DUMP_DOC_TREELAYOUT \
151 DumpPrintObjectsTreeLayout(mPrintObject, mPrt->mPrintDC);
153 # define DUMP_DOC_LIST(_title)
154 # define DUMP_DOC_TREE
155 # define DUMP_DOC_TREELAYOUT
158 // -------------------------------------------------------
160 // -------------------------------------------------------
163 * Build a tree of nsPrintObjects under aPO. It also appends a (depth first)
164 * flat list of all the nsPrintObjects created to mPrintDocList. If
165 * one of the nsPrintObject's document is the focused document, then the print
166 * object is set as mSelectionRoot.
167 * @param aParentPO The parent nsPrintObject to populate, must not be null.
169 void nsPrintJob::BuildNestedPrintObjects(
170 const UniquePtr
<nsPrintObject
>& aParentPO
) {
171 MOZ_ASSERT(aParentPO
);
173 // If aParentPO is for an iframe and its original document was the document
174 // that had focus then always set as the selection root.
175 if (aParentPO
->mParent
&&
176 aParentPO
->mDocument
->GetProperty(nsGkAtoms::printisfocuseddoc
)) {
177 mSelectionRoot
= aParentPO
.get();
178 } else if (!mSelectionRoot
&& aParentPO
->HasSelection()) {
179 // If there is no focused iframe but there is a selection in one or more
180 // frames then we want to set the root nsPrintObject as the focus root so
181 // that later EnablePrintingSelectionOnly can search for and enable all
182 // nsPrintObjects containing selections.
183 mSelectionRoot
= mPrintObject
.get();
186 for (auto& bc
: aParentPO
->mDocShell
->GetBrowsingContext()->Children()) {
187 nsCOMPtr
<nsIDocShell
> docShell
= bc
->GetDocShell();
189 if (auto* cc
= dom::ContentChild::GetSingleton()) {
190 nsCOMPtr
<nsIPrintSettingsService
> printSettingsService
=
191 do_GetService(sPrintSettingsServiceContractID
);
192 embedding::PrintData printData
;
193 printSettingsService
->SerializeToPrintData(mPrintSettings
, &printData
);
194 Unused
<< cc
->SendUpdateRemotePrintSettings(bc
, printData
);
199 RefPtr
<Document
> doc
= docShell
->GetDocument();
200 MOZ_DIAGNOSTIC_ASSERT(doc
);
201 // We might find non-static documents here if the fission remoteness change
202 // hasn't happened / finished yet. In that case, just skip them, the same
203 // way we do for remote frames above.
204 MOZ_DIAGNOSTIC_ASSERT(doc
->IsStaticDocument() || doc
->IsInitialDocument());
205 if (!doc
|| !doc
->IsStaticDocument()) {
209 // Note: docShell and doc are known-non-null at this point; they've been
210 // null-checked above (with null leading to 'continue' statements).
211 auto childPO
= MakeUnique
<nsPrintObject
>(*docShell
, *doc
, aParentPO
.get());
213 mPrintDocList
.AppendElement(childPO
.get());
214 BuildNestedPrintObjects(childPO
);
215 aParentPO
->mKids
.AppendElement(std::move(childPO
));
219 static nsresult
GetDefaultPrintSettings(nsIPrintSettings
** aSettings
) {
220 *aSettings
= nullptr;
222 nsresult rv
= NS_ERROR_FAILURE
;
223 nsCOMPtr
<nsIPrintSettingsService
> printSettingsService
=
224 do_GetService(sPrintSettingsServiceContractID
, &rv
);
225 NS_ENSURE_SUCCESS(rv
, rv
);
227 return printSettingsService
->GetDefaultPrintSettingsForPrinting(aSettings
);
230 //-------------------------------------------------------
232 NS_IMPL_ISUPPORTS(nsPrintJob
, nsIWebProgressListener
, nsISupportsWeakReference
)
234 //-------------------------------------------------------
235 nsPrintJob::~nsPrintJob() {
236 Destroy(); // for insurance
237 DisconnectPagePrintTimer();
240 bool nsPrintJob::CheckBeforeDestroy() const { return mPreparingForPrint
; }
242 //-------------------------------------------------------
243 void nsPrintJob::Destroy() {
247 mIsDestroying
= true;
249 DestroyPrintingData();
251 mDocViewerPrint
= nullptr;
254 //-------------------------------------------------------
255 void nsPrintJob::DestroyPrintingData() {
256 mPrintObject
= nullptr;
260 nsPrintJob::nsPrintJob(nsIDocumentViewerPrint
& aDocViewerPrint
,
261 nsIDocShell
& aDocShell
, Document
& aOriginalDoc
,
263 : mDocViewerPrint(&aDocViewerPrint
),
264 mDocShell(do_GetWeakReference(&aDocShell
)),
265 mScreenDPI(aScreenDPI
) {
266 // Any state that we need from aOriginalDoc must be fetched and stored
267 // here, since the document that the user selected to print may mutate
268 // across consecutive PrintPreview() calls.
270 Element
* root
= aOriginalDoc
.GetRootElement();
271 mDisallowSelectionPrint
=
272 root
&& root
->HasAttr(nsGkAtoms::mozdisallowselectionprint
);
275 //-----------------------------------------------------------------
276 std::tuple
<nsPageSequenceFrame
*, int32_t>
277 nsPrintJob::GetSeqFrameAndCountSheets() const {
278 if (NS_WARN_IF(!mPrt
)) {
282 const nsPrintObject
* po
= mPrintObject
.get();
283 if (NS_WARN_IF(!po
)) {
287 // This is sometimes incorrectly called before the pres shell has been created
288 // (bug 1141756). MOZ_DIAGNOSTIC_ASSERT so we'll still see the crash in
289 // Nightly/Aurora in case the other patch fixes this.
290 if (!po
->mPresShell
) {
291 MOZ_DIAGNOSTIC_ASSERT(
292 false, "GetSeqFrameAndCountSheets needs a non-null pres shell");
296 nsPageSequenceFrame
* seqFrame
= po
->mPresShell
->GetPageSequenceFrame();
301 // count the total number of sheets
302 return {seqFrame
, seqFrame
->PrincipalChildList().GetLength()};
305 // Foward decl for Debug Helper Functions
306 #ifdef EXTENDED_DEBUG_PRINTING
308 static int RemoveFilesInDir(const char* aDir
);
310 static void GetDocTitleAndURL(const UniquePtr
<nsPrintObject
>& aPO
,
311 nsACString
& aDocStr
, nsACString
& aURLStr
);
312 static void DumpPrintObjectsTree(nsPrintObject
* aPO
, int aLevel
, FILE* aFD
);
313 static void DumpPrintObjectsList(const nsTArray
<nsPrintObject
*>& aDocList
);
314 static void RootFrameList(nsPresContext
* aPresContext
, FILE* out
,
315 const char* aPrefix
);
316 static void DumpViews(nsIDocShell
* aDocShell
, FILE* out
);
317 static void DumpLayoutData(const char* aTitleStr
, const char* aURLStr
,
318 nsPresContext
* aPresContext
, nsDeviceContext
* aDC
,
319 nsIFrame
* aRootFrame
, nsIDocShell
* aDocShell
,
323 //--------------------------------------------------------------------------------
325 nsresult
nsPrintJob::CommonPrint(bool aIsPrintPreview
,
326 nsIPrintSettings
* aPrintSettings
,
327 nsIWebProgressListener
* aWebProgressListener
,
328 Document
& aSourceDoc
) {
329 // Callers must hold a strong reference to |this| to ensure that we stay
330 // alive for the duration of this method, because our main owning reference
331 // (on nsDocumentViewer) might be cleared during this function (if we cause
332 // script to run and it cancels the print operation).
334 nsresult rv
= DoCommonPrint(aIsPrintPreview
, aPrintSettings
,
335 aWebProgressListener
, aSourceDoc
);
337 if (aIsPrintPreview
) {
338 mIsCreatingPrintPreview
= false;
339 SetIsPrintPreview(false);
341 SetIsPrinting(false);
343 if (rv
!= NS_ERROR_ABORT
&& rv
!= NS_ERROR_OUT_OF_MEMORY
) {
344 FirePrintingErrorEvent(rv
);
346 DestroyPrintingData();
352 nsresult
nsPrintJob::DoCommonPrint(bool aIsPrintPreview
,
353 nsIPrintSettings
* aPrintSettings
,
354 nsIWebProgressListener
* aWebProgressListener
,
356 MOZ_ASSERT(aDoc
.IsStaticDocument());
360 // Grab the new instance with local variable to guarantee that it won't be
361 // deleted during this method.
362 // Note: Methods we call early below rely on mPrt being set.
363 mPrt
= new nsPrintData(aIsPrintPreview
? nsPrintData::eIsPrintPreview
364 : nsPrintData::eIsPrinting
);
365 RefPtr
<nsPrintData
> printData
= mPrt
;
367 if (aIsPrintPreview
) {
368 mIsCreatingPrintPreview
= true;
369 SetIsPrintPreview(true);
374 if (aWebProgressListener
) {
375 printData
->mPrintProgressListeners
.AppendObject(aWebProgressListener
);
377 if (mRemotePrintJob
) {
378 // If we have a RemotePrintJob add it to the print progress listeners,
379 // so it can forward to the parent.
380 printData
->mPrintProgressListeners
.AppendElement(mRemotePrintJob
);
383 // Get the docshell for this documentviewer
384 nsCOMPtr
<nsIDocShell
> docShell(do_QueryReferent(mDocShell
, &rv
));
385 NS_ENSURE_SUCCESS(rv
, rv
);
387 // if they don't pass in a PrintSettings, then get the Global PS
388 mPrintSettings
= aPrintSettings
;
389 if (!mPrintSettings
) {
390 MOZ_TRY(GetDefaultPrintSettings(getter_AddRefs(mPrintSettings
)));
394 nsAutoScriptBlocker scriptBlocker
;
395 // Note: docShell is implicitly non-null via do_QueryReferent necessarily
396 // having succeeded (if we got here).
397 mPrintObject
= MakeUnique
<nsPrintObject
>(*docShell
, aDoc
);
398 mPrintDocList
.AppendElement(mPrintObject
.get());
400 BuildNestedPrintObjects(mPrintObject
);
403 // The nsAutoScriptBlocker above will now have been destroyed, which may
404 // cause our print/print-preview operation to finish. In this case, we
405 // should immediately return an error code so that the root caller knows
406 // it shouldn't continue to do anything with this instance.
408 return NS_ERROR_FAILURE
;
411 // XXX This isn't really correct...
412 if (!mPrintObject
->mDocument
|| !mPrintObject
->mDocument
->GetRootElement())
413 return NS_ERROR_GFX_PRINTER_STARTDOC
;
415 mPrintSettings
->GetShrinkToFit(&mShrinkToFit
);
417 nsCOMPtr
<nsIDeviceContextSpec
> devspec
;
418 if (XRE_IsContentProcess()) {
419 devspec
= new nsDeviceContextSpecProxy(mRemotePrintJob
);
421 devspec
= do_CreateInstance("@mozilla.org/gfx/devicecontextspec;1", &rv
);
422 NS_ENSURE_SUCCESS(rv
, rv
);
425 bool printSilently
= false;
426 mPrintSettings
->GetPrintSilent(&printSilently
);
427 if (StaticPrefs::print_always_print_silent()) {
428 printSilently
= true;
431 if (mIsDoingPrinting
&& printSilently
) {
432 Telemetry::ScalarAdd(Telemetry::ScalarID::PRINTING_SILENT_PRINT
, 1);
435 MOZ_TRY(devspec
->Init(mPrintSettings
, mIsCreatingPrintPreview
));
437 printData
->mPrintDC
= new nsDeviceContext();
438 MOZ_TRY(printData
->mPrintDC
->InitForPrinting(devspec
));
440 MOZ_TRY(EnablePOsForPrinting());
442 if (!mIsCreatingPrintPreview
) {
443 printData
->OnStartPrinting();
445 InitPrintDocConstruction(false);
450 //---------------------------------------------------------------------------------
451 nsresult
nsPrintJob::Print(Document
& aDoc
, nsIPrintSettings
* aPrintSettings
,
452 RemotePrintJobChild
* aRemotePrintJob
,
453 nsIWebProgressListener
* aWebProgressListener
) {
454 mRemotePrintJob
= aRemotePrintJob
;
455 return CommonPrint(false, aPrintSettings
, aWebProgressListener
, aDoc
);
458 nsresult
nsPrintJob::PrintPreview(Document
& aDoc
,
459 nsIPrintSettings
* aPrintSettings
,
460 nsIWebProgressListener
* aWebProgressListener
,
461 PrintPreviewResolver
&& aCallback
) {
462 // Take ownership of aCallback, otherwise a function further up the call
463 // stack will call it to signal failure (by passing zero).
464 mPrintPreviewCallback
= std::move(aCallback
);
466 nsresult rv
= CommonPrint(true, aPrintSettings
, aWebProgressListener
, aDoc
);
468 if (mPrintPreviewCallback
) {
470 mPrintPreviewCallback(
471 PrintPreviewResultInfo(0, 0, false, false, false, {}, {}, {}));
472 mPrintPreviewCallback
= nullptr;
478 int32_t nsPrintJob::GetRawNumPages() const {
479 auto [seqFrame
, numSheets
] = GetSeqFrameAndCountSheets();
481 return seqFrame
? seqFrame
->GetRawNumPages() : 0;
484 bool nsPrintJob::GetIsEmpty() const {
485 auto [seqFrame
, numSheets
] = GetSeqFrameAndCountSheets();
492 return !seqFrame
->GetPagesInFirstSheet();
495 int32_t nsPrintJob::GetPrintPreviewNumSheets() const {
496 auto [seqFrame
, numSheets
] = GetSeqFrameAndCountSheets();
501 //-----------------------------------------------------------------
502 //-- Section: Pre-Reflow Methods
503 //-----------------------------------------------------------------
506 void nsPrintJob::GetDisplayTitleAndURL(Document
& aDoc
,
507 nsIPrintSettings
* aSettings
,
508 DocTitleDefault aTitleDefault
,
509 nsAString
& aTitle
, nsAString
& aURLStr
) {
514 aSettings
->GetTitle(aTitle
);
515 aSettings
->GetDocURL(aURLStr
);
518 if (aTitle
.IsEmpty()) {
519 aDoc
.GetTitle(aTitle
);
520 if (aTitle
.IsEmpty()) {
521 if (!aURLStr
.IsEmpty() &&
522 aTitleDefault
== DocTitleDefault::eDocURLElseFallback
) {
525 nsCOMPtr
<nsIStringBundle
> brandBundle
;
526 nsCOMPtr
<nsIStringBundleService
> svc
=
527 mozilla::components::StringBundle::Service();
529 svc
->CreateBundle("chrome://branding/locale/brand.properties",
530 getter_AddRefs(brandBundle
));
532 brandBundle
->GetStringFromName("brandShortName", aTitle
);
535 if (aTitle
.IsEmpty()) {
536 aTitle
.AssignLiteral(u
"Mozilla Document");
542 if (aURLStr
.IsEmpty()) {
543 nsIURI
* url
= aDoc
.GetDocumentURI();
548 nsCOMPtr
<nsIURI
> exposableURI
= net::nsIOService::CreateExposableURI(url
);
549 nsAutoCString urlCStr
;
550 nsresult rv
= exposableURI
->GetSpec(urlCStr
);
555 nsCOMPtr
<nsITextToSubURI
> textToSubURI
=
556 do_GetService(NS_ITEXTTOSUBURI_CONTRACTID
, &rv
);
561 textToSubURI
->UnEscapeURIForUI(urlCStr
, aURLStr
);
565 //---------------------------------------------------------------------
566 nsresult
nsPrintJob::DocumentReadyForPrinting() {
567 // Send the document to the printer...
568 nsresult rv
= SetupToPrintContent();
570 // The print job was canceled or there was a problem
571 // So remove all other documents from the print list
572 DonePrintingSheets(nullptr, rv
);
577 /** ---------------------------------------------------
578 * Cleans up when an error occurred
580 nsresult
nsPrintJob::CleanupOnFailure(nsresult aResult
, bool aIsPrinting
) {
581 PR_PL(("**** Failed %s - rv 0x%" PRIX32
,
582 aIsPrinting
? "Printing" : "Print Preview",
583 static_cast<uint32_t>(aResult
)));
586 if (mPagePrintTimer
) {
587 mPagePrintTimer
->Stop();
588 DisconnectPagePrintTimer();
592 SetIsPrinting(false);
594 SetIsPrintPreview(false);
595 mIsCreatingPrintPreview
= false;
598 /* cleanup done, let's fire-up an error dialog to notify the user
601 * When rv == NS_ERROR_ABORT, it means we want out of the
602 * print job without displaying any error messages
604 if (aResult
!= NS_ERROR_ABORT
) {
605 FirePrintingErrorEvent(aResult
);
608 FirePrintCompletionEvent();
613 //---------------------------------------------------------------------
614 void nsPrintJob::FirePrintingErrorEvent(nsresult aPrintError
) {
615 if (mPrintPreviewCallback
) {
617 mPrintPreviewCallback(
618 PrintPreviewResultInfo(0, 0, false, false, false, {}, {}, {}));
619 mPrintPreviewCallback
= nullptr;
622 nsCOMPtr
<nsIContentViewer
> cv
= do_QueryInterface(mDocViewerPrint
);
623 if (NS_WARN_IF(!cv
)) {
627 const RefPtr
<Document
> doc
= cv
->GetDocument();
628 const RefPtr
<CustomEvent
> event
= NS_NewDOMCustomEvent(doc
, nullptr, nullptr);
633 if (!jsapi
.Init(event
->GetParentObject())) {
636 JSContext
* cx
= jsapi
.cx();
638 JS::Rooted
<JS::Value
> detail(
639 cx
, JS::NumberValue(static_cast<double>(aPrintError
)));
640 event
->InitCustomEvent(cx
, u
"PrintingError"_ns
, false, false, detail
);
641 event
->SetTrusted(true);
643 // Event listeners in chrome shouldn't delete this.
644 AsyncEventDispatcher::RunDOMEventWhenSafe(*doc
, *event
,
645 ChromeOnlyDispatch::eYes
);
647 // Inform any progress listeners of the Error.
649 // Note that nsPrintData::DoOnStatusChange() will call some listeners.
650 // So, mPrt can be cleared or recreated.
651 RefPtr
<nsPrintData
> printData
= mPrt
;
652 printData
->DoOnStatusChange(aPrintError
);
656 //-----------------------------------------------------------------
657 //-- Section: Reflow Methods
658 //-----------------------------------------------------------------
660 nsresult
nsPrintJob::ReconstructAndReflow() {
661 if (NS_WARN_IF(!mPrt
)) {
662 return NS_ERROR_FAILURE
;
665 #if defined(XP_WIN) && defined(EXTENDED_DEBUG_PRINTING)
666 // We need to clear all the output files here
667 // because they will be re-created with second reflow of the docs
668 if (MOZ_LOG_TEST(gPrintingLog
, DUMP_LAYOUT_LEVEL
)) {
669 RemoveFilesInDir(".\\");
670 gDumpFileNameCnt
= 0;
671 gDumpLOFileNameCnt
= 0;
675 // In this loop, it's conceivable that one of our helpers might clear mPrt,
676 // while we're using it & its members! So we capture it in an owning local
677 // reference & use that instead of using mPrt directly.
678 RefPtr
<nsPrintData
> printData
= mPrt
;
679 for (nsPrintObject
* po
: mPrintDocList
) {
680 if (!po
->PrintingIsEnabled() || po
->mInvisible
) {
684 // When the print object has been marked as "print the document" (i.e,
685 // po->PrintingIsEnabled() is true), mPresContext and mPresShell should be
686 // non-nullptr (i.e., should've been created for the print) since they
687 // are necessary to print the document.
688 MOZ_ASSERT(po
->mPresContext
&& po
->mPresShell
,
689 "mPresContext and mPresShell shouldn't be nullptr when the "
691 "has been marked as \"print the document\"");
695 po
->mPresContext
->SetPageScale(po
->mZoomRatio
);
697 // Calculate scale factor from printer to screen
698 float printDPI
= float(AppUnitsPerCSSInch()) /
699 float(printData
->mPrintDC
->AppUnitsPerDevPixel());
700 po
->mPresContext
->SetPrintPreviewScale(mScreenDPI
/ printDPI
);
702 RefPtr
<PresShell
> presShell(po
->mPresShell
);
703 if (NS_WARN_IF(presShell
->IsDestroying())) {
704 return NS_ERROR_FAILURE
;
707 presShell
->ReconstructFrames();
709 // If the printing was canceled or restarted with different data,
710 // let's stop doing this printing.
711 if (NS_WARN_IF(mPrt
!= printData
)) {
712 return NS_ERROR_FAILURE
;
715 // For all views except the first one, setup the root view.
716 // ??? Can there be multiple po for the top-level-document?
717 bool documentIsTopLevel
= true;
721 nsresult rv
= SetRootView(po
, doReturn
, documentIsTopLevel
, adjSize
);
723 MOZ_ASSERT(!documentIsTopLevel
, "How could this happen?");
725 if (NS_FAILED(rv
) || doReturn
) {
730 presShell
->FlushPendingNotifications(FlushType::Layout
);
732 if (NS_WARN_IF(presShell
->IsDestroying())) {
733 return NS_ERROR_FAILURE
;
736 // If the printing was canceled or restarted with different data,
737 // let's stop doing this printing.
738 if (NS_WARN_IF(mPrt
!= printData
)) {
739 return NS_ERROR_FAILURE
;
742 nsresult rv
= UpdateSelectionAndShrinkPrintObject(po
, documentIsTopLevel
);
743 NS_ENSURE_SUCCESS(rv
, rv
);
748 //-------------------------------------------------------
749 nsresult
nsPrintJob::SetupToPrintContent() {
750 // This method may be called while DoCommonPrint() initializes the instance
751 // when its script blocker goes out of scope. In such case, this cannot do
752 // its job as expected because some objects in mPrt have not been initialized
753 // yet but they are necessary.
754 // Note: it shouldn't be possible for mPrintObject to be null; we check
755 // it for good measure (after we check its owner) before we start
756 // dereferencing it below.
757 if (NS_WARN_IF(!mPrt
) || NS_WARN_IF(!mPrintObject
)) {
758 return NS_ERROR_FAILURE
;
761 // If this is creating print preview, mPrintObject->mPresContext and
762 // mPrintObject->mPresShell need to be non-nullptr because this cannot
763 // initialize page sequence frame without them at end of this method since
764 // page sequence frame has already been destroyed or not been created yet.
765 if (mIsCreatingPrintPreview
&& (NS_WARN_IF(!mPrintObject
->mPresContext
) ||
766 NS_WARN_IF(!mPrintObject
->mPresShell
))) {
767 return NS_ERROR_FAILURE
;
770 // If this is printing some documents (not print-previewing the documents),
771 // mPrintObject->mPresContext and mPrintObject->mPresShell can be
772 // nullptr only when mPrintObject->PrintingIsEnabled() is false. E.g.,
773 // if the document has a <frameset> element and it's printing only content in
774 // a <frame> element or all <frame> elements separately.
776 (!mIsCreatingPrintPreview
&& !mPrintObject
->PrintingIsEnabled()) ||
777 (mPrintObject
->mPresContext
&& mPrintObject
->mPresShell
),
778 "mPresContext and mPresShell shouldn't be nullptr when printing the "
779 "document or creating print-preview");
781 bool didReconstruction
= false;
783 // This method works with mPrintObject. So, we need to guarantee that
784 // it won't be deleted in this method. We achieve this by holding a strong
785 // local reference to mPrt, which in turn keeps mPrintObject alive.
786 RefPtr
<nsPrintData
> printData
= mPrt
;
788 // If some new content got loaded since the initial reflow rebuild
790 if (mDidLoadDataForPrinting
) {
791 nsresult rv
= ReconstructAndReflow();
792 if (NS_WARN_IF(NS_FAILED(rv
))) {
795 // If the printing was canceled or restarted with different data,
796 // let's stop doing this printing.
797 if (NS_WARN_IF(mPrt
!= printData
)) {
798 return NS_ERROR_FAILURE
;
800 didReconstruction
= true;
803 // Here is where we figure out if extra reflow for shrinking the content
806 mShrinkToFitFactor
= mPrintObject
->mShrinkRatio
;
808 if (mShrinkToFitFactor
< 0.998f
) {
809 nsresult rv
= ReconstructAndReflow();
810 if (NS_WARN_IF(NS_FAILED(rv
))) {
813 // If the printing was canceled or restarted with different data,
814 // let's stop doing this printing.
815 if (NS_WARN_IF(mPrt
!= printData
)) {
816 return NS_ERROR_FAILURE
;
818 didReconstruction
= true;
821 if (MOZ_LOG_TEST(gPrintingLog
, LogLevel::Debug
)) {
822 float calcRatio
= mPrintObject
->mShrinkRatio
;
824 ("*******************************************************************"
826 PR_PL(("STF Ratio is: %8.5f Effective Ratio: %8.5f Diff: %8.5f\n",
827 mShrinkToFitFactor
, calcRatio
, mShrinkToFitFactor
- calcRatio
));
829 ("*******************************************************************"
834 // If the frames got reconstructed and reflowed the number of pages might
836 if (didReconstruction
) {
837 FirePrintPreviewUpdateEvent();
838 // If the printing was canceled or restarted with different data,
839 // let's stop doing this printing.
840 if (NS_WARN_IF(mPrt
!= printData
)) {
841 return NS_ERROR_FAILURE
;
845 DUMP_DOC_LIST(("\nAfter Reflow------------------------------------------"));
847 PR_PL(("-------------------------------------------------------\n"));
850 CalcNumPrintablePages(mNumPrintablePages
);
852 PR_PL(("--- Printing %d pages\n", mNumPrintablePages
));
855 // Print listener setup...
856 printData
->OnStartPrinting();
858 // If the printing was canceled or restarted with different data,
859 // let's stop doing this printing.
860 if (NS_WARN_IF(mPrt
!= printData
)) {
861 return NS_ERROR_FAILURE
;
864 nsAutoString fileNameStr
;
865 // check to see if we are printing to a file
866 if (mPrintSettings
->GetOutputDestination() ==
867 nsIPrintSettings::kOutputDestinationFile
) {
868 // On some platforms the BeginDocument needs to know the name of the file.
869 mPrintSettings
->GetToFileName(fileNameStr
);
872 nsAutoString docTitleStr
;
873 nsAutoString docURLStr
;
874 GetDisplayTitleAndURL(*mPrintObject
->mDocument
, mPrintSettings
,
875 DocTitleDefault::eDocURLElseFallback
, docTitleStr
,
878 int32_t startPage
= 1;
879 int32_t endPage
= mNumPrintablePages
;
881 nsTArray
<int32_t> ranges
;
882 mPrintSettings
->GetPageRanges(ranges
);
883 for (size_t i
= 0; i
< ranges
.Length(); i
+= 2) {
884 startPage
= std::max(1, std::min(startPage
, ranges
[i
]));
885 endPage
= std::min(mNumPrintablePages
, std::max(endPage
, ranges
[i
+ 1]));
889 // BeginDocument may pass back a FAILURE code
890 // i.e. On Windows, if you are printing to a file and hit "Cancel"
891 // to the "File Name" dialog, this comes back as an error
892 // Don't start printing when regression test are executed
893 if (mIsDoingPrinting
) {
894 rv
= printData
->mPrintDC
->BeginDocument(docTitleStr
, fileNameStr
, startPage
,
898 if (mIsCreatingPrintPreview
) {
899 // Copy docTitleStr and docURLStr to the pageSequenceFrame, to be displayed
901 nsPageSequenceFrame
* seqFrame
=
902 mPrintObject
->mPresShell
->GetPageSequenceFrame();
904 seqFrame
->StartPrint(mPrintObject
->mPresContext
, mPrintSettings
,
905 docTitleStr
, docURLStr
);
909 PR_PL(("****************** Begin Document ************************\n"));
912 NS_WARNING_ASSERTION(rv
== NS_ERROR_ABORT
,
913 "Failed to begin document for printing");
917 // This will print the docshell document
918 // when it completes asynchronously in the DonePrintingSheets method
919 // it will check to see if there are more docshells to be printed and
920 // then PrintDocContent will be called again.
922 if (mIsDoingPrinting
) {
923 // Double-check that mPrintObject is non-null, because it could have
924 // gotten cleared while running script since we last checked it.
925 if (NS_WARN_IF(!mPrintObject
)) {
926 return NS_ERROR_FAILURE
;
929 PrintDocContent(mPrintObject
, rv
); // ignore return value
935 //-------------------------------------------------------
936 // Recursively reflow each sub-doc and then calc
937 // all the frame locations of the sub-docs
938 nsresult
nsPrintJob::ReflowDocList(const UniquePtr
<nsPrintObject
>& aPO
) {
939 NS_ENSURE_ARG_POINTER(aPO
);
941 // Check to see if the subdocument's element has been hidden by the parent
943 if (aPO
->mParent
&& aPO
->mParent
->mPresShell
) {
945 aPO
->mContent
? aPO
->mContent
->GetPrimaryFrame() : nullptr;
946 if (!frame
|| !frame
->StyleVisibility()->IsVisible()) {
947 aPO
->EnablePrinting(false);
948 aPO
->mInvisible
= true;
953 UpdateZoomRatio(aPO
.get());
956 MOZ_TRY(ReflowPrintObject(aPO
));
958 for (const UniquePtr
<nsPrintObject
>& kid
: aPO
->mKids
) {
959 MOZ_TRY(ReflowDocList(kid
));
964 void nsPrintJob::FirePrintPreviewUpdateEvent() {
965 // Dispatch the event only while in PrintPreview. When printing, there is no
966 // listener bound to this event and therefore no need to dispatch it.
967 if (mCreatedForPrintPreview
&& !mIsDoingPrinting
) {
968 nsCOMPtr
<nsIContentViewer
> cv
= do_QueryInterface(mDocViewerPrint
);
969 if (Document
* document
= cv
->GetDocument()) {
970 AsyncEventDispatcher::RunDOMEventWhenSafe(
971 *document
, u
"printPreviewUpdate"_ns
, CanBubble::eYes
,
972 ChromeOnlyDispatch::eYes
);
977 nsresult
nsPrintJob::InitPrintDocConstruction(bool aHandleError
) {
978 // Guarantee that mPrintObject won't be deleted.
979 RefPtr
<nsPrintData
> printData
= mPrt
;
981 if (NS_WARN_IF(!printData
)) {
982 return NS_ERROR_FAILURE
;
985 // Attach progressListener to catch network requests.
986 mDidLoadDataForPrinting
= false;
989 AutoRestore
<bool> restore
{mDoingInitialReflow
};
990 mDoingInitialReflow
= true;
992 nsCOMPtr
<nsIWebProgress
> webProgress
=
993 do_QueryInterface(mPrintObject
->mDocShell
);
994 webProgress
->AddProgressListener(static_cast<nsIWebProgressListener
*>(this),
995 nsIWebProgress::NOTIFY_STATE_REQUEST
);
997 MOZ_TRY(ReflowDocList(mPrintObject
));
999 FirePrintPreviewUpdateEvent();
1002 MaybeResumePrintAfterResourcesLoaded(aHandleError
);
1006 bool nsPrintJob::ShouldResumePrint() const {
1007 if (mDoingInitialReflow
) {
1010 Document
* doc
= mPrintObject
->mDocument
;
1012 NS_ENSURE_TRUE(doc
, true);
1013 nsCOMPtr
<nsILoadGroup
> lg
= doc
->GetDocumentLoadGroup();
1014 NS_ENSURE_TRUE(lg
, true);
1015 bool pending
= false;
1016 nsresult rv
= lg
->IsPending(&pending
);
1017 NS_ENSURE_SUCCESS(rv
, true);
1021 nsresult
nsPrintJob::MaybeResumePrintAfterResourcesLoaded(
1022 bool aCleanupOnError
) {
1023 if (!ShouldResumePrint()) {
1024 mDidLoadDataForPrinting
= true;
1027 // If Destroy() has already been called, mPtr is nullptr. Then, the instance
1028 // needs to do nothing anymore in this method.
1029 // Note: it shouldn't be possible for mPrintObject to be null; we
1030 // just check it for good measure, as we check its owner.
1031 // Note: it shouldn't be possible for mPrintObject->mDocShell to be
1032 // null; we just check it for good measure, as we check its owner.
1033 if (!mPrt
|| NS_WARN_IF(!mPrintObject
) ||
1034 NS_WARN_IF(!mPrintObject
->mDocShell
)) {
1035 return NS_ERROR_FAILURE
;
1038 nsCOMPtr
<nsIWebProgress
> webProgress
=
1039 do_QueryInterface(mPrintObject
->mDocShell
);
1041 webProgress
->RemoveProgressListener(
1042 static_cast<nsIWebProgressListener
*>(this));
1045 if (mIsDoingPrinting
) {
1046 rv
= DocumentReadyForPrinting();
1048 rv
= FinishPrintPreview();
1051 /* cleaup on failure + notify user */
1052 if (aCleanupOnError
&& NS_FAILED(rv
)) {
1053 NS_WARNING_ASSERTION(rv
== NS_ERROR_ABORT
,
1054 "nsPrintJob::ResumePrintAfterResourcesLoaded failed");
1055 CleanupOnFailure(rv
, !mIsDoingPrinting
);
1061 ////////////////////////////////////////////////////////////////////////////////
1062 // nsIWebProgressListener
1064 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP
1065 nsPrintJob::OnStateChange(nsIWebProgress
* aWebProgress
, nsIRequest
* aRequest
,
1066 uint32_t aStateFlags
, nsresult aStatus
) {
1067 if (aStateFlags
& STATE_STOP
) {
1068 // If all resources are loaded, then finish and reflow.
1069 MaybeResumePrintAfterResourcesLoaded(/* aCleanupOnError */ true);
1075 nsPrintJob::OnProgressChange(nsIWebProgress
* aWebProgress
, nsIRequest
* aRequest
,
1076 int32_t aCurSelfProgress
, int32_t aMaxSelfProgress
,
1077 int32_t aCurTotalProgress
,
1078 int32_t aMaxTotalProgress
) {
1079 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
1084 nsPrintJob::OnLocationChange(nsIWebProgress
* aWebProgress
, nsIRequest
* aRequest
,
1085 nsIURI
* aLocation
, uint32_t aFlags
) {
1086 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
1091 nsPrintJob::OnStatusChange(nsIWebProgress
* aWebProgress
, nsIRequest
* aRequest
,
1092 nsresult aStatus
, const char16_t
* aMessage
) {
1093 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
1098 nsPrintJob::OnSecurityChange(nsIWebProgress
* aWebProgress
, nsIRequest
* aRequest
,
1100 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
1105 nsPrintJob::OnContentBlockingEvent(nsIWebProgress
* aWebProgress
,
1106 nsIRequest
* aRequest
, uint32_t aEvent
) {
1107 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
1111 //-------------------------------------------------------
1113 void nsPrintJob::UpdateZoomRatio(nsPrintObject
* aPO
) {
1114 if (!aPO
->mParent
) {
1116 aPO
->mZoomRatio
= mShrinkToFitFactor
;
1117 // If we're actually going to scale (the factor is less than 1), we
1118 // reduce the scale factor slightly to avoid the possibility of floating
1119 // point rounding error causing slight clipping of the longest lines.
1120 if (aPO
->mZoomRatio
!= 1.0f
) {
1121 aPO
->mZoomRatio
-= 0.005f
;
1125 mPrintSettings
->GetScaling(&scaling
);
1126 aPO
->mZoomRatio
= float(scaling
);
1131 nsresult
nsPrintJob::UpdateSelectionAndShrinkPrintObject(
1132 nsPrintObject
* aPO
, bool aDocumentIsTopLevel
) {
1133 PresShell
* displayPresShell
= aPO
->mDocShell
->GetPresShell();
1134 // Transfer Selection Ranges to the new Print PresShell
1135 RefPtr
<Selection
> selection
, selectionPS
;
1136 // It's okay if there is no display shell, just skip copying the selection
1137 if (displayPresShell
) {
1138 selection
= displayPresShell
->GetCurrentSelection(SelectionType::eNormal
);
1140 selectionPS
= aPO
->mPresShell
->GetCurrentSelection(SelectionType::eNormal
);
1142 // Reset all existing selection ranges that might have been added by calling
1143 // this function before.
1145 selectionPS
->RemoveAllRanges(IgnoreErrors());
1147 if (selection
&& selectionPS
) {
1148 const uint32_t rangeCount
= selection
->RangeCount();
1149 for (const uint32_t inx
: IntegerRange(rangeCount
)) {
1150 MOZ_ASSERT(selection
->RangeCount() == rangeCount
);
1151 const RefPtr
<nsRange
> range
{selection
->GetRangeAt(inx
)};
1152 selectionPS
->AddRangeAndSelectFramesAndNotifyListeners(*range
,
1157 // If we are trying to shrink the contents to fit on the page
1158 // we must first locate the "pageContent" frame
1159 // Then we walk the frame tree and look for the "xmost" frame
1160 // this is the frame where the right-hand side of the frame extends
1162 if (mShrinkToFit
&& aDocumentIsTopLevel
) {
1163 nsPageSequenceFrame
* pageSeqFrame
= aPO
->mPresShell
->GetPageSequenceFrame();
1164 NS_ENSURE_STATE(pageSeqFrame
);
1165 aPO
->mShrinkRatio
= pageSeqFrame
->GetSTFPercent();
1166 // Limit the shrink-to-fit scaling for some text-ish type of documents.
1167 nsAutoString contentType
;
1168 aPO
->mPresShell
->GetDocument()->GetContentType(contentType
);
1169 if (contentType
.EqualsLiteral("application/xhtml+xml") ||
1170 StringBeginsWith(contentType
, u
"text/"_ns
)) {
1171 int32_t limitPercent
=
1172 Preferences::GetInt("print.shrink-to-fit.scale-limit-percent", 20);
1173 limitPercent
= std::max(0, limitPercent
);
1174 limitPercent
= std::min(100, limitPercent
);
1175 float minShrinkRatio
= float(limitPercent
) / 100;
1176 aPO
->mShrinkRatio
= std::max(aPO
->mShrinkRatio
, minShrinkRatio
);
1182 nsView
* nsPrintJob::GetParentViewForRoot() {
1183 if (mIsCreatingPrintPreview
) {
1184 if (nsCOMPtr
<nsIContentViewer
> cv
= do_QueryInterface(mDocViewerPrint
)) {
1185 return cv
->FindContainerView();
1191 nsresult
nsPrintJob::SetRootView(nsPrintObject
* aPO
, bool& doReturn
,
1192 bool& documentIsTopLevel
, nsSize
& adjSize
) {
1193 bool canCreateScrollbars
= true;
1196 nsView
* parentView
= nullptr;
1200 if (aPO
->mParent
&& aPO
->mParent
->PrintingIsEnabled()) {
1202 aPO
->mContent
? aPO
->mContent
->GetPrimaryFrame() : nullptr;
1203 // Without a frame, this document can't be displayed; therefore, there is no
1204 // point to reflowing it
1206 aPO
->EnablePrinting(false);
1211 // XXX If printing supported printing document hierarchies with non-constant
1212 // zoom this would be wrong as we use the same mPrt->mPrintDC for all
1214 adjSize
= frame
->GetContentRect().Size();
1215 documentIsTopLevel
= false;
1216 // presshell exists because parent is printable
1218 // the top nsPrintObject's widget will always have scrollbars
1219 if (frame
&& frame
->IsSubDocumentFrame()) {
1220 nsView
* view
= frame
->GetView();
1221 NS_ENSURE_TRUE(view
, NS_ERROR_FAILURE
);
1222 view
= view
->GetFirstChild();
1223 NS_ENSURE_TRUE(view
, NS_ERROR_FAILURE
);
1225 canCreateScrollbars
= false;
1228 nscoord pageWidth
, pageHeight
;
1229 mPrt
->mPrintDC
->GetDeviceSurfaceDimensions(pageWidth
, pageHeight
);
1230 adjSize
= nsSize(pageWidth
, pageHeight
);
1231 documentIsTopLevel
= true;
1232 parentView
= GetParentViewForRoot();
1235 if (aPO
->mViewManager
->GetRootView()) {
1236 // Reuse the root view that is already on the root frame.
1237 rootView
= aPO
->mViewManager
->GetRootView();
1238 // Remove it from its existing parent if necessary
1239 aPO
->mViewManager
->RemoveChild(rootView
);
1240 rootView
->SetParent(parentView
);
1242 // Create a child window of the parent that is our "root view/window"
1243 nsRect tbounds
= nsRect(nsPoint(0, 0), adjSize
);
1244 rootView
= aPO
->mViewManager
->CreateView(tbounds
, parentView
);
1245 NS_ENSURE_TRUE(rootView
, NS_ERROR_OUT_OF_MEMORY
);
1248 if (mIsCreatingPrintPreview
&& documentIsTopLevel
) {
1249 aPO
->mPresContext
->SetPaginatedScrolling(canCreateScrollbars
);
1252 // Setup hierarchical relationship in view manager
1253 aPO
->mViewManager
->SetRootView(rootView
);
1258 // Reflow a nsPrintObject
1259 nsresult
nsPrintJob::ReflowPrintObject(const UniquePtr
<nsPrintObject
>& aPO
) {
1260 NS_ENSURE_STATE(aPO
);
1262 if (!aPO
->PrintingIsEnabled()) {
1266 NS_ASSERTION(!aPO
->mPresContext
, "Recreating prescontext");
1268 // Guarantee that mPrt and the objects it owns won't be deleted in this method
1269 // because it might be cleared if other modules called from here may fire
1270 // events, notifying observers and/or listeners.
1271 RefPtr
<nsPrintData
> printData
= mPrt
;
1273 // create the PresContext
1274 nsPresContext::nsPresContextType type
=
1275 mIsCreatingPrintPreview
? nsPresContext::eContext_PrintPreview
1276 : nsPresContext::eContext_Print
;
1277 const bool shouldBeRoot
=
1278 (!aPO
->mParent
|| !aPO
->mParent
->PrintingIsEnabled()) &&
1279 !GetParentViewForRoot();
1280 aPO
->mPresContext
= shouldBeRoot
? new nsRootPresContext(aPO
->mDocument
, type
)
1281 : new nsPresContext(aPO
->mDocument
, type
);
1282 aPO
->mPresContext
->SetPrintSettings(mPrintSettings
);
1284 // init it with the DC
1285 MOZ_TRY(aPO
->mPresContext
->Init(printData
->mPrintDC
));
1287 aPO
->mViewManager
= new nsViewManager();
1289 MOZ_TRY(aPO
->mViewManager
->Init(printData
->mPrintDC
));
1291 bool doReturn
= false;
1292 bool documentIsTopLevel
= false;
1295 nsresult rv
= SetRootView(aPO
.get(), doReturn
, documentIsTopLevel
, adjSize
);
1297 if (NS_FAILED(rv
) || doReturn
) {
1301 // Here, we inform nsPresContext of the page size. Note that 'adjSize' is
1302 // *usually* the page size, but we need to check. Strictly speaking, adjSize
1303 // is the *device output size*, which is really the dimensions of a "sheet"
1304 // rather than a "page" (an important distinction in an N-pages-per-sheet
1305 // scenario). For some pages-per-sheet values, the pages are orthogonal to
1306 // the sheet; we adjust for that here by swapping the width with the height.
1307 nsSize pageSize
= adjSize
;
1308 if (mPrintSettings
->HasOrthogonalSheetsAndPages()) {
1309 std::swap(pageSize
.width
, pageSize
.height
);
1311 // XXXalaskanemily: Is this actually necessary? We set it again before the
1313 aPO
->mPresContext
->SetPageSize(pageSize
);
1315 int32_t p2a
= aPO
->mPresContext
->DeviceContext()->AppUnitsPerDevPixel();
1316 if (documentIsTopLevel
&& mIsCreatingPrintPreview
) {
1317 if (nsCOMPtr
<nsIContentViewer
> cv
= do_QueryInterface(mDocViewerPrint
)) {
1318 // If we're print-previewing and the top level document, use the bounds
1319 // from our doc viewer. Page bounds is not what we want.
1321 cv
->GetBounds(bounds
);
1322 adjSize
= nsSize(bounds
.width
* p2a
, bounds
.height
* p2a
);
1325 aPO
->mPresContext
->SetIsRootPaginatedDocument(documentIsTopLevel
);
1326 aPO
->mPresContext
->SetVisibleArea(nsRect(nsPoint(), adjSize
));
1327 aPO
->mPresContext
->SetPageScale(aPO
->mZoomRatio
);
1328 // Calculate scale factor from printer to screen
1329 float printDPI
= float(AppUnitsPerCSSInch()) / float(p2a
);
1330 aPO
->mPresContext
->SetPrintPreviewScale(mScreenDPI
/ printDPI
);
1332 // Do CreatePresShell() after we setup the page size, the visible area, and
1333 // the flag |mIsRootPaginatedDocument|, to make sure we can resolve the
1334 // correct viewport size for the print preview page when notifying the media
1335 // feature values changed. See au_viewport_size_for_viewport_unit_resolution()
1336 // in media_queries.rs for more details.
1337 RefPtr
<Document
> doc
= aPO
->mDocument
;
1338 RefPtr
<nsPresContext
> presContext
= aPO
->mPresContext
;
1339 RefPtr
<nsViewManager
> viewManager
= aPO
->mViewManager
;
1341 aPO
->mPresShell
= doc
->CreatePresShell(presContext
, viewManager
);
1342 if (!aPO
->mPresShell
) {
1343 return NS_ERROR_FAILURE
;
1346 // If we're printing selection then remove the nonselected nodes from our
1348 if (mPrintSettings
->GetPrintSelectionOnly()) {
1349 // If we fail to remove the nodes then we should fail to print, because if
1350 // the user was trying to print a small selection from a large document,
1351 // sending the whole document to a real printer would be very frustrating.
1352 MOZ_TRY(DeleteNonSelectedNodes(*aPO
->mDocument
));
1355 aPO
->mPresShell
->BeginObservingDocument();
1358 ("In DV::ReflowPrintObject PO: %p pS: %p (%9s) Setting page size w,h to "
1360 aPO
.get(), aPO
->mPresShell
.get(), LoggableTypeOfPO(aPO
.get()),
1361 pageSize
.width
, pageSize
.height
));
1363 if (mIsCreatingPrintPreview
&& documentIsTopLevel
) {
1364 mDocViewerPrint
->SetPrintPreviewPresentation(
1365 aPO
->mViewManager
, aPO
->mPresContext
, aPO
->mPresShell
.get());
1368 MOZ_TRY(aPO
->mPresShell
->Initialize());
1369 NS_ASSERTION(aPO
->mPresShell
, "Presshell should still be here");
1371 RefPtr
<PresShell
> presShell
= aPO
->mPresShell
;
1373 // Get the initial page name. Even though we haven't done any page-name
1374 // fragmentation (that happens during block reflow), this will still be
1375 // valid to find the first page's name.
1376 const nsAtom
* firstPageName
= nsGkAtoms::_empty
;
1377 if (const Element
* const rootElement
= aPO
->mDocument
->GetRootElement()) {
1378 if (const nsIFrame
* const rootFrame
= rootElement
->GetPrimaryFrame()) {
1379 firstPageName
= rootFrame
->ComputePageValue();
1383 const ServoStyleSet::FirstPageSizeAndOrientation sizeAndOrientation
=
1384 presShell
->StyleSet()->GetFirstPageSizeAndOrientation(firstPageName
);
1385 if (mPrintSettings
->GetUsePageRuleSizeAsPaperSize()) {
1386 mMaybeCSSPageSize
= sizeAndOrientation
.size
;
1387 if (sizeAndOrientation
.size
) {
1388 pageSize
= sizeAndOrientation
.size
.value();
1389 aPO
->mPresContext
->SetPageSize(pageSize
);
1393 // If the document has a specified CSS page-size, we rotate the page to
1394 // reflect this. Changing the orientation is reflected by the result of
1395 // FinishPrintPreview, so that the frontend can reflect this.
1396 // The new document has not yet been reflowed, so we have to query the
1397 // original document for any CSS page-size.
1398 if (sizeAndOrientation
.orientation
) {
1399 switch (sizeAndOrientation
.orientation
.value()) {
1400 case StylePageSizeOrientation::Landscape
:
1401 if (pageSize
.width
< pageSize
.height
) {
1402 // Paper is in portrait, CSS page size is landscape.
1403 std::swap(pageSize
.width
, pageSize
.height
);
1406 case StylePageSizeOrientation::Portrait
:
1407 if (pageSize
.width
> pageSize
.height
) {
1408 // Paper is in landscape, CSS page size is portrait.
1409 std::swap(pageSize
.width
, pageSize
.height
);
1413 mMaybeCSSPageLandscape
= Some(sizeAndOrientation
.orientation
.value() ==
1414 StylePageSizeOrientation::Landscape
);
1415 aPO
->mPresContext
->SetPageSize(pageSize
);
1418 // Process the reflow event Initialize posted
1419 presShell
->FlushPendingNotifications(FlushType::Layout
);
1421 MOZ_TRY(UpdateSelectionAndShrinkPrintObject(aPO
.get(), documentIsTopLevel
));
1423 #ifdef EXTENDED_DEBUG_PRINTING
1424 if (MOZ_LOG_TEST(gPrintingLog
, DUMP_LAYOUT_LEVEL
)) {
1425 nsAutoCString docStr
;
1426 nsAutoCString urlStr
;
1427 GetDocTitleAndURL(aPO
, docStr
, urlStr
);
1429 sprintf(filename
, "print_dump_%d.txt", gDumpFileNameCnt
++);
1430 // Dump all the frames and view to a a file
1431 FILE* fd
= fopen(filename
, "w");
1433 nsIFrame
* theRootFrame
= aPO
->mPresShell
->GetRootFrame();
1434 fprintf(fd
, "Title: %s\n", docStr
.get());
1435 fprintf(fd
, "URL: %s\n", urlStr
.get());
1436 fprintf(fd
, "--------------- Frames ----------------\n");
1437 // RefPtr<gfxContext> renderingContext =
1438 // printData->mPrintDocDC->CreateRenderingContext();
1439 RootFrameList(aPO
->mPresContext
, fd
, 0);
1440 // DumpFrames(fd, aPO->mPresContext, renderingContext, theRootFrame, 0);
1441 fprintf(fd
, "---------------------------------------\n\n");
1442 fprintf(fd
, "--------------- Views From Root Frame----------------\n");
1443 nsView
* v
= theRootFrame
->GetView();
1447 printf("View is null!\n");
1449 if (aPO
->mDocShell
) {
1450 fprintf(fd
, "--------------- All Views ----------------\n");
1451 DumpViews(aPO
->mDocShell
, fd
);
1452 fprintf(fd
, "---------------------------------------\n\n");
1462 //-------------------------------------------------------
1463 // Figure out how many documents and how many total pages we are printing
1464 void nsPrintJob::CalcNumPrintablePages(int32_t& aNumPages
) {
1466 // Count the number of printable documents and printable pages
1467 for (nsPrintObject
* po
: mPrintDocList
) {
1468 // Note: The po->mPresContext null-check below is necessary, because it's
1469 // possible po->mPresContext might never have been set. (e.g., if
1470 // PrintingIsEnabled() returns false, ReflowPrintObject bails before setting
1472 if (po
->mPresContext
&& po
->mPresContext
->IsRootPaginatedDocument()) {
1473 nsPageSequenceFrame
* seqFrame
= po
->mPresShell
->GetPageSequenceFrame();
1475 aNumPages
+= seqFrame
->PrincipalChildList().GetLength();
1481 //-----------------------------------------------------------------
1482 //-- Done: Reflow Methods
1483 //-----------------------------------------------------------------
1485 //-----------------------------------------------------------------
1486 //-- Section: Printing Methods
1487 //-----------------------------------------------------------------
1489 //-------------------------------------------------------
1490 // Called for each DocShell that needs to be printed
1491 bool nsPrintJob::PrintDocContent(const UniquePtr
<nsPrintObject
>& aPO
,
1492 nsresult
& aStatus
) {
1493 NS_ASSERTION(aPO
, "Pointer is null!");
1496 if (!aPO
->mHasBeenPrinted
&& aPO
->PrintingIsEnabled()) {
1497 aStatus
= DoPrint(aPO
);
1501 // If |aPO->mHasBeenPrinted| is true,
1502 // the kids frames are already processed in |PrintPage|.
1503 // XXX This should be removed. Since bug 1552785 it has no longer been
1504 // possible for us to have to print multiple subdocuments consecutively.
1505 if (!aPO
->mHasBeenPrinted
&& !aPO
->mInvisible
) {
1506 for (const UniquePtr
<nsPrintObject
>& po
: aPO
->mKids
) {
1507 bool printed
= PrintDocContent(po
, aStatus
);
1508 if (printed
|| NS_FAILED(aStatus
)) {
1516 // A helper struct to aid with DeleteNonSelectedNodes.
1517 struct MOZ_STACK_CLASS SelectionRangeState
{
1518 explicit SelectionRangeState(RefPtr
<Selection
> aSelection
)
1519 : mSelection(std::move(aSelection
)) {
1520 MOZ_ASSERT(mSelection
);
1521 MOZ_ASSERT(!mSelection
->RangeCount());
1524 // Selects all the nodes that are _not_ included in a given set of ranges.
1525 MOZ_CAN_RUN_SCRIPT
void SelectComplementOf(Span
<const RefPtr
<nsRange
>>);
1526 // Removes the selected ranges from the document.
1527 MOZ_CAN_RUN_SCRIPT
void RemoveSelectionFromDocument();
1535 MOZ_CAN_RUN_SCRIPT
void SelectRange(nsRange
*);
1536 MOZ_CAN_RUN_SCRIPT
void SelectNodesExcept(const Position
& aStart
,
1537 const Position
& aEnd
);
1538 MOZ_CAN_RUN_SCRIPT
void SelectNodesExceptInSubtree(const Position
& aStart
,
1539 const Position
& aEnd
);
1541 // A map from subtree root (document or shadow root) to the start position of
1542 // the non-selected content (so far).
1543 nsTHashMap
<nsPtrHashKey
<nsINode
>, Position
> mPositions
;
1545 // The selection we're adding the ranges to.
1546 const RefPtr
<Selection
> mSelection
;
1549 void SelectionRangeState::SelectComplementOf(
1550 Span
<const RefPtr
<nsRange
>> aRanges
) {
1551 for (const auto& range
: aRanges
) {
1552 auto start
= Position
{range
->GetStartContainer(), range
->StartOffset()};
1553 auto end
= Position
{range
->GetEndContainer(), range
->EndOffset()};
1554 SelectNodesExcept(start
, end
);
1558 void SelectionRangeState::SelectRange(nsRange
* aRange
) {
1559 if (aRange
&& !aRange
->Collapsed()) {
1560 mSelection
->AddRangeAndSelectFramesAndNotifyListeners(*aRange
,
1565 void SelectionRangeState::SelectNodesExcept(const Position
& aStart
,
1566 const Position
& aEnd
) {
1567 SelectNodesExceptInSubtree(aStart
, aEnd
);
1568 if (auto* shadow
= ShadowRoot::FromNode(aStart
.mNode
->SubtreeRoot())) {
1569 auto* host
= shadow
->Host();
1570 SelectNodesExcept(Position
{host
, 0}, Position
{host
, host
->GetChildCount()});
1572 MOZ_ASSERT(aStart
.mNode
->IsInUncomposedDoc());
1576 void SelectionRangeState::SelectNodesExceptInSubtree(const Position
& aStart
,
1577 const Position
& aEnd
) {
1578 static constexpr auto kEllipsis
= u
"\x2026"_ns
;
1580 nsINode
* root
= aStart
.mNode
->SubtreeRoot();
1582 mPositions
.WithEntryHandle(root
, [&](auto&& entry
) -> Position
& {
1583 return entry
.OrInsertWith([&] { return Position
{root
, 0}; });
1586 bool ellipsizedStart
= false;
1587 if (auto* text
= Text::FromNode(aStart
.mNode
)) {
1588 if (start
.mNode
!= text
&& aStart
.mOffset
&&
1589 aStart
.mOffset
< text
->Length()) {
1590 text
->InsertData(aStart
.mOffset
, kEllipsis
, IgnoreErrors());
1591 ellipsizedStart
= true;
1595 RefPtr
<nsRange
> range
= nsRange::Create(
1596 start
.mNode
, start
.mOffset
, aStart
.mNode
, aStart
.mOffset
, IgnoreErrors());
1601 // If we added an ellipsis at the start and the end position was relative to
1602 // the same node account for it here.
1603 if (ellipsizedStart
&& aStart
.mNode
== aEnd
.mNode
) {
1604 start
.mOffset
+= kEllipsis
.Length();
1607 // If the end is mid text then add an ellipsis.
1608 if (auto* text
= Text::FromNode(start
.mNode
)) {
1609 if (start
.mOffset
&& start
.mOffset
< text
->Length()) {
1610 text
->InsertData(start
.mOffset
, kEllipsis
, IgnoreErrors());
1611 start
.mOffset
+= kEllipsis
.Length();
1616 void SelectionRangeState::RemoveSelectionFromDocument() {
1617 for (auto& entry
: mPositions
) {
1618 const Position
& pos
= entry
.GetData();
1619 nsINode
* root
= entry
.GetKey();
1620 RefPtr
<nsRange
> range
= nsRange::Create(
1621 pos
.mNode
, pos
.mOffset
, root
, root
->GetChildCount(), IgnoreErrors());
1624 mSelection
->DeleteFromDocument(IgnoreErrors());
1628 * Builds the complement set of ranges and adds those to the selection.
1629 * Deletes all of the nodes contained in the complement set of ranges
1630 * leaving behind only nodes that were originally selected.
1631 * Adds ellipses to a selected node's text if text is truncated by a range.
1632 * This is used to implement the "Print Selection Only" user interface option.
1634 MOZ_CAN_RUN_SCRIPT_BOUNDARY
static nsresult
DeleteNonSelectedNodes(
1636 MOZ_ASSERT(aDoc
.IsStaticDocument());
1637 const auto* printRanges
= static_cast<nsTArray
<RefPtr
<nsRange
>>*>(
1638 aDoc
.GetProperty(nsGkAtoms::printselectionranges
));
1643 PresShell
* presShell
= aDoc
.GetPresShell();
1644 NS_ENSURE_STATE(presShell
);
1645 RefPtr
<Selection
> selection
=
1646 presShell
->GetCurrentSelection(SelectionType::eNormal
);
1647 NS_ENSURE_STATE(selection
);
1649 SelectionRangeState
state(std::move(selection
));
1650 state
.SelectComplementOf(*printRanges
);
1651 state
.RemoveSelectionFromDocument();
1655 //-------------------------------------------------------
1656 nsresult
nsPrintJob::DoPrint(const UniquePtr
<nsPrintObject
>& aPO
) {
1658 PR_PL(("**************************** %s ****************************\n",
1659 LoggableTypeOfPO(aPO
.get())));
1660 PR_PL(("****** In DV::DoPrint PO: %p \n", aPO
.get()));
1662 PresShell
* poPresShell
= aPO
->mPresShell
;
1663 nsPresContext
* poPresContext
= aPO
->mPresContext
;
1665 NS_ASSERTION(poPresContext
, "PrintObject has not been reflowed");
1666 NS_ASSERTION(poPresContext
->Type() != nsPresContext::eContext_PrintPreview
,
1667 "How did this context end up here?");
1669 // Guarantee that mPrt and the objects it owns won't be deleted in this method
1670 // because it might be cleared if other modules called from here may fire
1671 // events, notifying observers and/or listeners.
1672 RefPtr
<nsPrintData
> printData
= mPrt
;
1673 if (NS_WARN_IF(!printData
)) {
1674 return NS_ERROR_FAILURE
;
1678 // Ask the page sequence frame to print all the pages
1679 nsPageSequenceFrame
* seqFrame
= poPresShell
->GetPageSequenceFrame();
1680 MOZ_ASSERT(seqFrame
, "no page sequence frame");
1682 // We are done preparing for printing, so we can turn this off
1683 mPreparingForPrint
= false;
1685 #ifdef EXTENDED_DEBUG_PRINTING
1686 nsIFrame
* rootFrame
= poPresShell
->GetRootFrame();
1687 if (aPO
->PrintingIsEnabled()) {
1688 nsAutoCString docStr
;
1689 nsAutoCString urlStr
;
1690 GetDocTitleAndURL(aPO
, docStr
, urlStr
);
1691 DumpLayoutData(docStr
.get(), urlStr
.get(), poPresContext
,
1692 printData
->mPrintDC
, rootFrame
, aPO
->mDocShell
, nullptr);
1696 if (!mPrintSettings
) {
1697 // not sure what to do here!
1698 SetIsPrinting(false);
1699 return NS_ERROR_FAILURE
;
1702 nsAutoString docTitleStr
;
1703 nsAutoString docURLStr
;
1704 GetDisplayTitleAndURL(*aPO
->mDocument
, mPrintSettings
,
1705 DocTitleDefault::eFallback
, docTitleStr
, docURLStr
);
1708 SetIsPrinting(false);
1709 return NS_ERROR_FAILURE
;
1712 // For telemetry, get paper size being used; convert the dimensions to
1713 // points and ensure they reflect portrait orientation.
1714 nsIPrintSettings
* settings
= mPrintSettings
;
1715 double paperWidth
, paperHeight
;
1716 settings
->GetPaperWidth(&paperWidth
);
1717 settings
->GetPaperHeight(&paperHeight
);
1719 settings
->GetPaperSizeUnit(&sizeUnit
);
1721 case nsIPrintSettings::kPaperSizeInches
:
1723 paperHeight
*= 72.0;
1725 case nsIPrintSettings::kPaperSizeMillimeters
:
1726 paperWidth
*= 72.0 / 25.4;
1727 paperHeight
*= 72.0 / 25.4;
1730 MOZ_ASSERT_UNREACHABLE("unknown paper size unit");
1733 if (paperWidth
> paperHeight
) {
1734 std::swap(paperWidth
, paperHeight
);
1736 // Use the paper size to build a Telemetry Scalar key.
1738 key
.AppendInt(int32_t(NS_round(paperWidth
)));
1740 key
.AppendInt(int32_t(NS_round(paperHeight
)));
1741 Telemetry::ScalarAdd(Telemetry::ScalarID::PRINTING_PAPER_SIZE
, key
, 1);
1743 mPageSeqFrame
= seqFrame
;
1744 seqFrame
->StartPrint(poPresContext
, settings
, docTitleStr
, docURLStr
);
1746 // Schedule Page to Print
1747 PR_PL(("Scheduling Print of PO: %p (%s) \n", aPO
.get(),
1748 LoggableTypeOfPO(aPO
.get())));
1749 StartPagePrintTimer(aPO
);
1755 //-------------------------------------------------------
1756 bool nsPrintJob::PrePrintSheet() {
1757 NS_ASSERTION(mPageSeqFrame
.IsAlive(), "mPageSeqFrame is not alive!");
1758 NS_ASSERTION(mPrt
, "mPrt is null!");
1760 // Although these should NEVER be nullptr
1761 // This is added insurance, to make sure we don't crash in optimized builds
1762 if (!mPrt
|| !mPageSeqFrame
.IsAlive()) {
1763 return true; // means we are done preparing the sheet.
1766 // Guarantee that mPrt won't be deleted during a call of
1767 // FirePrintingErrorEvent().
1768 RefPtr
<nsPrintData
> printData
= mPrt
;
1770 // Ask mPageSeqFrame if the sheet is ready to be printed.
1771 // If the sheet doesn't get printed at all, the |done| will be |true|.
1773 nsPageSequenceFrame
* pageSeqFrame
= do_QueryFrame(mPageSeqFrame
.GetFrame());
1774 nsresult rv
= pageSeqFrame
->PrePrintNextSheet(mPagePrintTimer
, &done
);
1775 if (NS_FAILED(rv
)) {
1776 // ??? ::PrintSheet doesn't set |printData->mIsAborted = true| if
1777 // rv != NS_ERROR_ABORT, but I don't really understand why this should be
1778 // the right thing to do? Shouldn't |printData->mIsAborted| set to true
1779 // all the time if something went wrong?
1780 if (rv
!= NS_ERROR_ABORT
) {
1781 FirePrintingErrorEvent(rv
);
1782 printData
->mIsAborted
= true;
1789 bool nsPrintJob::PrintSheet(nsPrintObject
* aPO
, bool& aInRange
) {
1790 NS_ASSERTION(aPO
, "aPO is null!");
1791 NS_ASSERTION(mPageSeqFrame
.IsAlive(), "mPageSeqFrame is not alive!");
1792 NS_ASSERTION(mPrt
, "mPrt is null!");
1794 // XXXdholbert Nowadays, this function doesn't need to concern itself with
1795 // page ranges -- page-range handling is now handled when we reflow our
1796 // PrintedSheetFrames, and all PrintedSheetFrames are "in-range" and should
1797 // be printed. So this outparam is unconditionally true. Bug 1669815 is filed
1798 // on removing it entirely.
1801 // Although these should NEVER be nullptr
1802 // This is added insurance, to make sure we don't crash in optimized builds
1803 if (!mPrt
|| !aPO
|| !mPageSeqFrame
.IsAlive()) {
1804 FirePrintingErrorEvent(NS_ERROR_FAILURE
);
1805 return true; // means we are done printing
1808 // Guarantee that mPrt won't be deleted during a call of
1809 // nsPrintData::DoOnProgressChange() which runs some listeners,
1810 // which may clear (& might otherwise destroy).
1811 RefPtr
<nsPrintData
> printData
= mPrt
;
1813 PR_PL(("-----------------------------------\n"));
1814 PR_PL(("------ In DV::PrintSheet PO: %p (%s)\n", aPO
, LoggableTypeOfPO(aPO
)));
1816 if (printData
->mIsAborted
) {
1820 nsPageSequenceFrame
* pageSeqFrame
= do_QueryFrame(mPageSeqFrame
.GetFrame());
1821 const uint32_t sheetIdx
= pageSeqFrame
->GetCurrentSheetIdx();
1822 const uint32_t numSheets
= pageSeqFrame
->PrincipalChildList().GetLength();
1824 PR_PL(("****** Printing sheet index %d of %d sheets(s)\n", sheetIdx
,
1827 MOZ_ASSERT(numSheets
> 0, "print operations must have at least 1 sheet");
1828 MOZ_ASSERT(sheetIdx
< numSheets
,
1829 "sheetIdx shouldn't be allowed to go out of bounds");
1830 printData
->DoOnProgressChange(sheetIdx
, numSheets
, false, 0);
1831 if (NS_WARN_IF(mPrt
!= printData
)) {
1832 // If current printing is canceled or new print is started, let's return
1833 // true to notify the caller of current printing is done.
1838 // if a print job was cancelled externally, an EndPage or BeginPage may
1839 // fail and the failure is passed back here.
1840 // Returning true means we are done printing.
1842 // When rv == NS_ERROR_ABORT, it means we want out of the
1843 // print job without displaying any error messages
1844 nsresult rv
= pageSeqFrame
->PrintNextSheet();
1845 if (NS_FAILED(rv
)) {
1846 if (rv
!= NS_ERROR_ABORT
) {
1847 FirePrintingErrorEvent(rv
);
1848 printData
->mIsAborted
= true;
1853 pageSeqFrame
->DoPageEnd();
1855 // If we just printed the final sheet (the one with index "numSheets-1"),
1857 return (sheetIdx
== numSheets
- 1);
1860 void nsPrintJob::PageDone(nsresult aResult
) {
1861 MOZ_ASSERT(mIsDoingPrinting
);
1863 // mPagePrintTimer might be released during RemotePrintFinished, keep a
1864 // reference here to make sure it lives long enough.
1865 RefPtr
<nsPagePrintTimer
> timer
= mPagePrintTimer
;
1866 timer
->RemotePrintFinished();
1869 //-----------------------------------------------------------------
1870 //-- Done: Printing Methods
1871 //-----------------------------------------------------------------
1873 //-----------------------------------------------------------------
1874 //-- Section: Misc Support Methods
1875 //-----------------------------------------------------------------
1877 //---------------------------------------------------------------------
1878 void nsPrintJob::SetIsPrinting(bool aIsPrinting
) {
1879 mIsDoingPrinting
= aIsPrinting
;
1881 mPreparingForPrint
= true;
1885 //---------------------------------------------------------------------
1886 void nsPrintJob::SetIsPrintPreview(bool aIsPrintPreview
) {
1887 mCreatedForPrintPreview
= aIsPrintPreview
;
1889 if (mDocViewerPrint
) {
1890 mDocViewerPrint
->SetIsPrintPreview(aIsPrintPreview
);
1894 //-------------------------------------------------------
1895 bool nsPrintJob::DonePrintingSheets(nsPrintObject
* aPO
, nsresult aResult
) {
1896 // NS_ASSERTION(aPO, "Pointer is null!");
1897 PR_PL(("****** In DV::DonePrintingSheets PO: %p (%s)\n", aPO
,
1898 aPO
? LoggableTypeOfPO(aPO
) : ""));
1900 // If there is a pageSeqFrame, make sure there are no more printCanvas active
1901 // that might call |Notify| on the pagePrintTimer after things are cleaned up
1902 // and printing was marked as being done.
1903 if (mPageSeqFrame
.IsAlive()) {
1904 nsPageSequenceFrame
* pageSeqFrame
= do_QueryFrame(mPageSeqFrame
.GetFrame());
1905 pageSeqFrame
->ResetPrintCanvasList();
1908 // Guarantee that mPrt and mPrintObject won't be deleted during a
1909 // call of PrintDocContent() and FirePrintCompletionEvent().
1910 RefPtr
<nsPrintData
> printData
= mPrt
;
1912 if (aPO
&& !printData
->mIsAborted
) {
1913 aPO
->mHasBeenPrinted
= true;
1915 bool didPrint
= PrintDocContent(mPrintObject
, rv
);
1916 if (NS_SUCCEEDED(rv
) && didPrint
) {
1918 ("****** In DV::DonePrintingSheets PO: %p (%s) didPrint:%s (Not Done "
1920 aPO
, LoggableTypeOfPO(aPO
), PRT_YESNO(didPrint
)));
1925 if (NS_SUCCEEDED(aResult
)) {
1926 FirePrintCompletionEvent();
1927 // XXX mPrt may be cleared or replaced with new instance here.
1928 // However, the following methods will clean up with new mPrt or will
1929 // do nothing due to no proper nsPrintData instance.
1932 SetIsPrinting(false);
1934 // Release reference to mPagePrintTimer; the timer object destroys itself
1935 // after this returns true
1936 DisconnectPagePrintTimer();
1941 //-------------------------------------------------------
1942 nsresult
nsPrintJob::EnablePOsForPrinting() {
1943 // Guarantee that mPrt and the objects it owns won't be deleted.
1944 RefPtr
<nsPrintData
> printData
= mPrt
;
1946 // NOTE: All POs have been "turned off" for printing
1947 // this is where we decided which POs get printed.
1949 if (!printData
|| !mPrintSettings
) {
1950 return NS_ERROR_FAILURE
;
1954 PR_PL(("********* nsPrintJob::EnablePOsForPrinting *********\n"));
1956 if (!mPrintSettings
->GetPrintSelectionOnly()) {
1957 mPrintObject
->EnablePrinting(true);
1961 // This means we are either printing a selected iframe or
1962 // we are printing the current selection.
1963 NS_ENSURE_STATE(!mDisallowSelectionPrint
&& mSelectionRoot
);
1965 // If mSelectionRoot is a selected iframe without a selection, then just
1966 // enable normally from that point.
1967 if (mSelectionRoot
->mParent
&& !mSelectionRoot
->HasSelection()) {
1968 mSelectionRoot
->EnablePrinting(true);
1970 // Otherwise, only enable nsPrintObjects that have a selection.
1971 mSelectionRoot
->EnablePrintingSelectionOnly();
1976 //-----------------------------------------------------------------
1977 //-- Done: Misc Support Methods
1978 //-----------------------------------------------------------------
1980 //-----------------------------------------------------------------
1981 //-- Section: Finishing up or Cleaning up
1982 //-----------------------------------------------------------------
1984 //-----------------------------------------------------------------
1985 nsresult
nsPrintJob::FinishPrintPreview() {
1986 nsresult rv
= NS_OK
;
1988 #ifdef NS_PRINT_PREVIEW
1990 // If mPrt is null we've already finished with print preview. If mPrt is not
1991 // null but mIsCreatingPrintPreview is false FinishPrintPreview must have
1992 // already failed due to DocumentReadyForPrinting failing.
1993 if (!mPrt
|| !mIsCreatingPrintPreview
) {
1997 rv
= DocumentReadyForPrinting();
1999 // Note that this method may be called while the instance is being
2000 // initialized. Some methods which initialize the instance (e.g.,
2001 // DoCommonPrint) may need to stop initializing and return error if
2002 // this is called. Therefore it's important to set mIsCreatingPrintPreview
2003 // state to false here. If you need to stop setting that here, you need to
2004 // keep them being able to check whether the owner stopped using this
2006 mIsCreatingPrintPreview
= false;
2008 // mPrt may be cleared during a call of nsPrintData::OnEndPrinting()
2009 // because that method invokes some arbitrary listeners.
2010 // TODO(dshin): Does any listener attach to print preview? Doesn't seem like
2011 // we call matching `OnStartPrinting()` for previews.
2012 RefPtr
<nsPrintData
> printData
= mPrt
;
2013 if (NS_FAILED(rv
)) {
2014 printData
->OnEndPrinting();
2019 if (mPrintPreviewCallback
) {
2020 const bool hasSelection
= !mDisallowSelectionPrint
&& mSelectionRoot
;
2022 Maybe
<float> pageWidth
;
2023 Maybe
<float> pageHeight
;
2024 if (mMaybeCSSPageSize
) {
2025 nsSize cssPageSize
= *mMaybeCSSPageSize
;
2026 pageWidth
= Some(float(cssPageSize
.width
) / float(AppUnitsPerCSSInch()));
2028 Some(float(cssPageSize
.height
) / float(AppUnitsPerCSSInch()));
2031 mPrintPreviewCallback(PrintPreviewResultInfo(
2032 GetPrintPreviewNumSheets(), GetRawNumPages(), GetIsEmpty(),
2033 hasSelection
, hasSelection
&& mPrintObject
->HasSelection(),
2034 mMaybeCSSPageLandscape
, pageWidth
, pageHeight
));
2035 mPrintPreviewCallback
= nullptr;
2038 // At this point we are done preparing everything
2039 // before it is to be created
2041 printData
->OnEndPrinting();
2043 #endif // NS_PRINT_PREVIEW
2048 //-----------------------------------------------------------------
2049 //-- Done: Finishing up or Cleaning up
2050 //-----------------------------------------------------------------
2052 /*=============== Timer Related Code ======================*/
2053 nsresult
nsPrintJob::StartPagePrintTimer(const UniquePtr
<nsPrintObject
>& aPO
) {
2054 if (!mPagePrintTimer
) {
2055 // Get the delay time in between the printing of each page
2056 // this gives the user more time to press cancel
2057 int32_t printPageDelay
= mPrintSettings
->GetPrintPageDelay();
2059 nsCOMPtr
<nsIContentViewer
> cv
= do_QueryInterface(mDocViewerPrint
);
2060 NS_ENSURE_TRUE(cv
, NS_ERROR_FAILURE
);
2061 nsCOMPtr
<Document
> doc
= cv
->GetDocument();
2062 NS_ENSURE_TRUE(doc
, NS_ERROR_FAILURE
);
2065 new nsPagePrintTimer(this, mDocViewerPrint
, doc
, printPageDelay
);
2067 if (mRemotePrintJob
) {
2068 mRemotePrintJob
->SetPagePrintTimer(mPagePrintTimer
);
2069 mRemotePrintJob
->SetPrintJob(this);
2073 return mPagePrintTimer
->Start(aPO
.get());
2076 //---------------------------------------------------------------
2077 //-- PLEvent Notification
2078 //---------------------------------------------------------------
2079 class nsPrintCompletionEvent
: public Runnable
{
2081 explicit nsPrintCompletionEvent(nsIDocumentViewerPrint
* docViewerPrint
)
2082 : mozilla::Runnable("nsPrintCompletionEvent"),
2083 mDocViewerPrint(docViewerPrint
) {
2084 NS_ASSERTION(mDocViewerPrint
, "mDocViewerPrint is null.");
2087 NS_IMETHOD
Run() override
{
2088 if (mDocViewerPrint
) mDocViewerPrint
->OnDonePrinting();
2093 nsCOMPtr
<nsIDocumentViewerPrint
> mDocViewerPrint
;
2096 //-----------------------------------------------------------
2097 void nsPrintJob::FirePrintCompletionEvent() {
2098 MOZ_ASSERT(NS_IsMainThread());
2099 nsCOMPtr
<nsIRunnable
> event
= new nsPrintCompletionEvent(mDocViewerPrint
);
2100 nsCOMPtr
<nsIContentViewer
> cv
= do_QueryInterface(mDocViewerPrint
);
2101 NS_ENSURE_TRUE_VOID(cv
);
2102 nsCOMPtr
<Document
> doc
= cv
->GetDocument();
2103 NS_ENSURE_TRUE_VOID(doc
);
2104 NS_ENSURE_SUCCESS_VOID(doc
->Dispatch(event
.forget()));
2107 void nsPrintJob::DisconnectPagePrintTimer() {
2108 if (mPagePrintTimer
) {
2109 mPagePrintTimer
->Disconnect();
2110 mPagePrintTimer
= nullptr;
2114 //---------------------------------------------------------------
2115 //---------------------------------------------------------------
2116 //-- Debug helper routines
2117 //---------------------------------------------------------------
2118 //---------------------------------------------------------------
2119 #if defined(XP_WIN) && defined(EXTENDED_DEBUG_PRINTING)
2120 # include <windows.h>
2121 # include <process.h>
2122 # include <direct.h>
2124 # define MY_FINDFIRST(a, b) FindFirstFile(a, b)
2125 # define MY_FINDNEXT(a, b) FindNextFile(a, b)
2126 # define ISDIR(a) (a.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
2127 # define MY_FINDCLOSE(a) FindClose(a)
2128 # define MY_FILENAME(a) a.cFileName
2129 # define MY_FILESIZE(a) (a.nFileSizeHigh * MAXDWORD) + a.nFileSizeLow
2131 int RemoveFilesInDir(const char* aDir
) {
2132 WIN32_FIND_DATA data_ptr
;
2135 char path
[MAX_PATH
];
2139 // Append slash to the end of the directory names if not there
2140 if (path
[strlen(path
) - 1] != '\\') strcat(path
, "\\");
2142 char findPath
[MAX_PATH
];
2143 strcpy(findPath
, path
);
2144 strcat(findPath
, "*.*");
2146 find_handle
= MY_FINDFIRST(findPath
, &data_ptr
);
2148 if (find_handle
!= INVALID_HANDLE_VALUE
) {
2150 if (ISDIR(data_ptr
) && (stricmp(MY_FILENAME(data_ptr
), ".")) &&
2151 (stricmp(MY_FILENAME(data_ptr
), ".."))) {
2153 } else if (!ISDIR(data_ptr
)) {
2154 if (!strncmp(MY_FILENAME(data_ptr
), "print_dump", 10)) {
2155 char fileName
[MAX_PATH
];
2156 strcpy(fileName
, aDir
);
2157 strcat(fileName
, "\\");
2158 strcat(fileName
, MY_FILENAME(data_ptr
));
2159 printf("Removing %s\n", fileName
);
2163 } while (MY_FINDNEXT(find_handle
, &data_ptr
));
2164 MY_FINDCLOSE(find_handle
);
2170 #ifdef EXTENDED_DEBUG_PRINTING
2172 /** ---------------------------------------------------
2173 * Dumps Frames for Printing
2175 static void RootFrameList(nsPresContext
* aPresContext
, FILE* out
,
2176 const char* aPrefix
) {
2177 if (!aPresContext
|| !out
) return;
2179 if (PresShell
* presShell
= aPresContext
->GetPresShell()) {
2180 nsIFrame
* frame
= presShell
->GetRootFrame();
2182 frame
->List(out
, aPrefix
);
2187 /** ---------------------------------------------------
2188 * Dumps Frames for Printing
2190 static void DumpFrames(FILE* out
, nsPresContext
* aPresContext
,
2191 gfxContext
* aRendContext
, nsIFrame
* aFrame
,
2193 NS_ASSERTION(out
, "Pointer is null!");
2194 NS_ASSERTION(aPresContext
, "Pointer is null!");
2195 NS_ASSERTION(aRendContext
, "Pointer is null!");
2196 NS_ASSERTION(aFrame
, "Pointer is null!");
2198 nsIFrame
* child
= aFrame
->PrincipalChildList().FirstChild();
2199 while (child
!= nullptr) {
2200 for (int32_t i
= 0; i
< aLevel
; i
++) {
2204 child
->GetFrameName(tmp
);
2205 fputs(NS_LossyConvertUTF16toASCII(tmp
).get(), out
);
2207 if (child
->IsVisibleForPainting()) {
2208 fprintf(out
, " %p %s", child
, isSelected
? "VIS" : "UVS");
2209 nsRect rect
= child
->GetRect();
2210 fprintf(out
, "[%d,%d,%d,%d] ", rect
.x
, rect
.y
, rect
.width
, rect
.height
);
2211 fprintf(out
, "v: %p ", (void*)child
->GetView());
2213 DumpFrames(out
, aPresContext
, aRendContext
, child
, aLevel
+ 1);
2214 child
= child
->GetNextSibling();
2219 /** ---------------------------------------------------
2220 * Dumps the Views from the DocShell
2222 static void DumpViews(nsIDocShell
* aDocShell
, FILE* out
) {
2223 NS_ASSERTION(aDocShell
, "Pointer is null!");
2224 NS_ASSERTION(out
, "Pointer is null!");
2226 if (nullptr != aDocShell
) {
2227 fprintf(out
, "docshell=%p \n", aDocShell
);
2228 if (PresShell
* presShell
= aDocShell
->GetPresShell()) {
2229 nsViewManager
* vm
= presShell
->GetViewManager();
2231 nsView
* root
= vm
->GetRootView();
2237 fputs("null pres shell\n", out
);
2240 // dump the views of the sub documents
2242 BrowsingContext
* bc
= nsDocShell::Cast(aDocShell
)->GetBrowsingContext();
2243 for (auto& child
: bc
->Children()) {
2244 if (auto childDS
= child
->GetDocShell()) {
2245 DumpViews(childAsShell
, out
);
2251 /** ---------------------------------------------------
2252 * Dumps the Views and Frames
2254 void DumpLayoutData(const char* aTitleStr
, const char* aURLStr
,
2255 nsPresContext
* aPresContext
, nsDeviceContext
* aDC
,
2256 nsIFrame
* aRootFrame
, nsIDocShell
* aDocShell
,
2257 FILE* aFD
= nullptr) {
2258 if (!MOZ_LOG_TEST(gPrintingLog
, DUMP_LAYOUT_LEVEL
)) {
2262 if (aPresContext
== nullptr || aDC
== nullptr) {
2266 # ifdef NS_PRINT_PREVIEW
2267 if (aPresContext
->Type() == nsPresContext::eContext_PrintPreview
) {
2272 NS_ASSERTION(aRootFrame
, "Pointer is null!");
2273 NS_ASSERTION(aDocShell
, "Pointer is null!");
2275 // Dump all the frames and view to a a file
2277 sprintf(filename
, "print_dump_layout_%d.txt", gDumpLOFileNameCnt
++);
2278 FILE* fd
= aFD
? aFD
: fopen(filename
, "w");
2280 fprintf(fd
, "Title: %s\n", aTitleStr
? aTitleStr
: "");
2281 fprintf(fd
, "URL: %s\n", aURLStr
? aURLStr
: "");
2282 fprintf(fd
, "--------------- Frames ----------------\n");
2283 fprintf(fd
, "--------------- Frames ----------------\n");
2284 // RefPtr<gfxContext> renderingContext =
2285 // aDC->CreateRenderingContext();
2286 RootFrameList(aPresContext
, fd
, "");
2287 // DumpFrames(fd, aPresContext, renderingContext, aRootFrame, 0);
2288 fprintf(fd
, "---------------------------------------\n\n");
2289 fprintf(fd
, "--------------- Views From Root Frame----------------\n");
2290 nsView
* v
= aRootFrame
->GetView();
2294 printf("View is null!\n");
2297 fprintf(fd
, "--------------- All Views ----------------\n");
2298 DumpViews(aDocShell
, fd
);
2299 fprintf(fd
, "---------------------------------------\n\n");
2301 if (aFD
== nullptr) {
2307 //-------------------------------------------------------------
2308 static void DumpPrintObjectsList(const nsTArray
<nsPrintObject
*>& aDocList
) {
2309 if (!MOZ_LOG_TEST(gPrintingLog
, DUMP_LAYOUT_LEVEL
)) {
2313 PR_PL(("Doc List\n***************************************************\n"));
2315 ("T P A H PO DocShell Seq Page Root Page# "
2317 for (nsPrintObject
* po
: aDocList
) {
2318 NS_ASSERTION(po
, "nsPrintObject can't be null!");
2319 nsIFrame
* rootFrame
= nullptr;
2320 if (po
->mPresShell
) {
2321 rootFrame
= po
->mPresShell
->GetRootFrame();
2322 while (rootFrame
!= nullptr) {
2323 nsPageSequenceFrame
* sqf
= do_QueryFrame(rootFrame
);
2327 rootFrame
= rootFrame
->PrincipalChildList().FirstChild();
2331 PR_PL(("%s %d %d %p %p %p\n", ShortLoggableTypeOfPO(po
),
2332 po
->PrintingIsEnabled(), po
->mHasBeenPrinted
, po
,
2333 po
->mDocShell
.get(), rootFrame
));
2337 //-------------------------------------------------------------
2338 static void DumpPrintObjectsTree(nsPrintObject
* aPO
, int aLevel
, FILE* aFD
) {
2339 if (!MOZ_LOG_TEST(gPrintingLog
, DUMP_LAYOUT_LEVEL
)) {
2343 NS_ASSERTION(aPO
, "Pointer is null!");
2345 FILE* fd
= aFD
? aFD
: stdout
;
2348 "DocTree\n***************************************************\n");
2349 fprintf(fd
, "T PO DocShell Seq Page Page# Rect\n");
2351 for (const auto& po
: aPO
->mKids
) {
2352 NS_ASSERTION(po
, "nsPrintObject can't be null!");
2353 for (int32_t k
= 0; k
< aLevel
; k
++) fprintf(fd
, " ");
2354 fprintf(fd
, "%s %p %p\n", ShortLoggableTypeOfPO(po
.get()), po
.get(),
2355 po
->mDocShell
.get());
2359 //-------------------------------------------------------------
2360 static void GetDocTitleAndURL(const UniquePtr
<nsPrintObject
>& aPO
,
2361 nsACString
& aDocStr
, nsACString
& aURLStr
) {
2362 nsAutoString docTitleStr
;
2363 nsAutoString docURLStr
;
2364 GetDocumentTitleAndURL(aPO
->mDocument
, docTitleStr
, docURLStr
);
2365 CopyUTF16toUTF8(docTitleStr
, aDocStr
);
2366 CopyUTF16toUTF8(docURLStr
, aURLStr
);
2369 //-------------------------------------------------------------
2370 static void DumpPrintObjectsTreeLayout(const UniquePtr
<nsPrintObject
>& aPO
,
2371 nsDeviceContext
* aDC
, int aLevel
,
2373 if (!MOZ_LOG_TEST(gPrintingLog
, DUMP_LAYOUT_LEVEL
)) {
2377 NS_ASSERTION(aPO
, "Pointer is null!");
2378 NS_ASSERTION(aDC
, "Pointer is null!");
2382 fd
= fopen("tree_layout.txt", "w");
2384 "DocTree\n***************************************************\n");
2385 fprintf(fd
, "***************************************************\n");
2386 fprintf(fd
, "T PO DocShell Seq Page Page# Rect\n");
2391 nsIFrame
* rootFrame
= nullptr;
2392 if (aPO
->mPresShell
) {
2393 rootFrame
= aPO
->mPresShell
->GetRootFrame();
2395 for (int32_t k
= 0; k
< aLevel
; k
++) fprintf(fd
, " ");
2396 fprintf(fd
, "%s %p %p\n", ShortLoggableTypeOfPO(aPO
.get()), aPO
.get(),
2397 aPO
->mDocShell
.get());
2398 if (aPO
->PrintingIsEnabled()) {
2399 nsAutoCString docStr
;
2400 nsAutoCString urlStr
;
2401 GetDocTitleAndURL(aPO
, docStr
, urlStr
);
2402 DumpLayoutData(docStr
.get(), urlStr
.get(), aPO
->mPresContext
, aDC
,
2403 rootFrame
, aPO
->mDocShell
, fd
);
2405 fprintf(fd
, "<***************************************************>\n");
2407 for (const auto& po
: aPO
->mKids
) {
2408 NS_ASSERTION(po
, "nsPrintObject can't be null!");
2409 DumpPrintObjectsTreeLayout(po
, aDC
, aLevel
+ 1, fd
);
2412 if (aLevel
== 0 && fd
) {
2417 //-------------------------------------------------------------
2418 static void DumpPrintObjectsListStart(
2419 const char* aStr
, const nsTArray
<nsPrintObject
*>& aDocList
) {
2420 if (!MOZ_LOG_TEST(gPrintingLog
, DUMP_LAYOUT_LEVEL
)) {
2424 NS_ASSERTION(aStr
, "Pointer is null!");
2426 PR_PL(("%s\n", aStr
));
2427 DumpPrintObjectsList(aDocList
);
2432 //---------------------------------------------------------------
2433 //---------------------------------------------------------------
2434 //-- End of debug helper routines
2435 //---------------------------------------------------------------