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 "nsIDocumentViewer.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
)));
584 PROFILER_MARKER_TEXT("PrintJob", LAYOUT_Printing
, MarkerStack::Capture(),
585 "nsPrintJob::CleanupOnFailure"_ns
);
588 if (mPagePrintTimer
) {
589 mPagePrintTimer
->Stop();
590 DisconnectPagePrintTimer();
594 SetIsPrinting(false);
596 SetIsPrintPreview(false);
597 mIsCreatingPrintPreview
= false;
600 /* cleanup done, let's fire-up an error dialog to notify the user
603 * When rv == NS_ERROR_ABORT, it means we want out of the
604 * print job without displaying any error messages
606 if (aResult
!= NS_ERROR_ABORT
) {
607 FirePrintingErrorEvent(aResult
);
610 FirePrintCompletionEvent();
615 //---------------------------------------------------------------------
616 void nsPrintJob::FirePrintingErrorEvent(nsresult aPrintError
) {
617 if (mPrintPreviewCallback
) {
619 mPrintPreviewCallback(
620 PrintPreviewResultInfo(0, 0, false, false, false, {}, {}, {}));
621 mPrintPreviewCallback
= nullptr;
624 nsCOMPtr
<nsIDocumentViewer
> viewer
= do_QueryInterface(mDocViewerPrint
);
625 if (NS_WARN_IF(!viewer
)) {
629 const RefPtr
<Document
> doc
= viewer
->GetDocument();
630 const RefPtr
<CustomEvent
> event
= NS_NewDOMCustomEvent(doc
, nullptr, nullptr);
635 if (!jsapi
.Init(event
->GetParentObject())) {
638 JSContext
* cx
= jsapi
.cx();
640 JS::Rooted
<JS::Value
> detail(
641 cx
, JS::NumberValue(static_cast<double>(aPrintError
)));
642 event
->InitCustomEvent(cx
, u
"PrintingError"_ns
, false, false, detail
);
643 event
->SetTrusted(true);
645 // Event listeners in chrome shouldn't delete this.
646 AsyncEventDispatcher::RunDOMEventWhenSafe(*doc
, *event
,
647 ChromeOnlyDispatch::eYes
);
649 // Inform any progress listeners of the Error.
651 // Note that nsPrintData::DoOnStatusChange() will call some listeners.
652 // So, mPrt can be cleared or recreated.
653 RefPtr
<nsPrintData
> printData
= mPrt
;
654 printData
->DoOnStatusChange(aPrintError
);
658 //-----------------------------------------------------------------
659 //-- Section: Reflow Methods
660 //-----------------------------------------------------------------
662 nsresult
nsPrintJob::ReconstructAndReflow() {
663 if (NS_WARN_IF(!mPrt
)) {
664 return NS_ERROR_FAILURE
;
667 #if defined(XP_WIN) && defined(EXTENDED_DEBUG_PRINTING)
668 // We need to clear all the output files here
669 // because they will be re-created with second reflow of the docs
670 if (MOZ_LOG_TEST(gPrintingLog
, DUMP_LAYOUT_LEVEL
)) {
671 RemoveFilesInDir(".\\");
672 gDumpFileNameCnt
= 0;
673 gDumpLOFileNameCnt
= 0;
677 // In this loop, it's conceivable that one of our helpers might clear mPrt,
678 // while we're using it & its members! So we capture it in an owning local
679 // reference & use that instead of using mPrt directly.
680 RefPtr
<nsPrintData
> printData
= mPrt
;
681 for (nsPrintObject
* po
: mPrintDocList
) {
682 if (!po
->PrintingIsEnabled() || po
->mInvisible
) {
686 // When the print object has been marked as "print the document" (i.e,
687 // po->PrintingIsEnabled() is true), mPresContext and mPresShell should be
688 // non-nullptr (i.e., should've been created for the print) since they
689 // are necessary to print the document.
690 MOZ_ASSERT(po
->mPresContext
&& po
->mPresShell
,
691 "mPresContext and mPresShell shouldn't be nullptr when the "
693 "has been marked as \"print the document\"");
697 po
->mPresContext
->SetPageScale(po
->mZoomRatio
);
699 // Calculate scale factor from printer to screen
700 float printDPI
= float(AppUnitsPerCSSInch()) /
701 float(printData
->mPrintDC
->AppUnitsPerDevPixel());
702 po
->mPresContext
->SetPrintPreviewScale(mScreenDPI
/ printDPI
);
704 RefPtr
<PresShell
> presShell(po
->mPresShell
);
705 if (NS_WARN_IF(presShell
->IsDestroying())) {
706 return NS_ERROR_FAILURE
;
709 presShell
->ReconstructFrames();
711 // If the printing was canceled or restarted with different data,
712 // let's stop doing this printing.
713 if (NS_WARN_IF(mPrt
!= printData
)) {
714 return NS_ERROR_FAILURE
;
717 // For all views except the first one, setup the root view.
718 // ??? Can there be multiple po for the top-level-document?
719 bool documentIsTopLevel
= true;
723 nsresult rv
= SetRootView(po
, doReturn
, documentIsTopLevel
, adjSize
);
725 MOZ_ASSERT(!documentIsTopLevel
, "How could this happen?");
727 if (NS_FAILED(rv
) || doReturn
) {
732 presShell
->FlushPendingNotifications(FlushType::Layout
);
734 if (NS_WARN_IF(presShell
->IsDestroying())) {
735 return NS_ERROR_FAILURE
;
738 // If the printing was canceled or restarted with different data,
739 // let's stop doing this printing.
740 if (NS_WARN_IF(mPrt
!= printData
)) {
741 return NS_ERROR_FAILURE
;
744 nsresult rv
= UpdateSelectionAndShrinkPrintObject(po
, documentIsTopLevel
);
745 NS_ENSURE_SUCCESS(rv
, rv
);
750 //-------------------------------------------------------
751 nsresult
nsPrintJob::SetupToPrintContent() {
752 // This method may be called while DoCommonPrint() initializes the instance
753 // when its script blocker goes out of scope. In such case, this cannot do
754 // its job as expected because some objects in mPrt have not been initialized
755 // yet but they are necessary.
756 // Note: it shouldn't be possible for mPrintObject to be null; we check
757 // it for good measure (after we check its owner) before we start
758 // dereferencing it below.
759 if (NS_WARN_IF(!mPrt
) || NS_WARN_IF(!mPrintObject
)) {
760 return NS_ERROR_FAILURE
;
763 // If this is creating print preview, mPrintObject->mPresContext and
764 // mPrintObject->mPresShell need to be non-nullptr because this cannot
765 // initialize page sequence frame without them at end of this method since
766 // page sequence frame has already been destroyed or not been created yet.
767 if (mIsCreatingPrintPreview
&& (NS_WARN_IF(!mPrintObject
->mPresContext
) ||
768 NS_WARN_IF(!mPrintObject
->mPresShell
))) {
769 return NS_ERROR_FAILURE
;
772 // If this is printing some documents (not print-previewing the documents),
773 // mPrintObject->mPresContext and mPrintObject->mPresShell can be
774 // nullptr only when mPrintObject->PrintingIsEnabled() is false. E.g.,
775 // if the document has a <frameset> element and it's printing only content in
776 // a <frame> element or all <frame> elements separately.
778 (!mIsCreatingPrintPreview
&& !mPrintObject
->PrintingIsEnabled()) ||
779 (mPrintObject
->mPresContext
&& mPrintObject
->mPresShell
),
780 "mPresContext and mPresShell shouldn't be nullptr when printing the "
781 "document or creating print-preview");
783 bool didReconstruction
= false;
785 // This method works with mPrintObject. So, we need to guarantee that
786 // it won't be deleted in this method. We achieve this by holding a strong
787 // local reference to mPrt, which in turn keeps mPrintObject alive.
788 RefPtr
<nsPrintData
> printData
= mPrt
;
790 // If some new content got loaded since the initial reflow rebuild
792 if (mDidLoadDataForPrinting
) {
793 nsresult rv
= ReconstructAndReflow();
794 if (NS_WARN_IF(NS_FAILED(rv
))) {
797 // If the printing was canceled or restarted with different data,
798 // let's stop doing this printing.
799 if (NS_WARN_IF(mPrt
!= printData
)) {
800 return NS_ERROR_FAILURE
;
802 didReconstruction
= true;
805 // Here is where we figure out if extra reflow for shrinking the content
808 mShrinkToFitFactor
= mPrintObject
->mShrinkRatio
;
810 if (mShrinkToFitFactor
< 0.998f
) {
811 nsresult rv
= ReconstructAndReflow();
812 if (NS_WARN_IF(NS_FAILED(rv
))) {
815 // If the printing was canceled or restarted with different data,
816 // let's stop doing this printing.
817 if (NS_WARN_IF(mPrt
!= printData
)) {
818 return NS_ERROR_FAILURE
;
820 didReconstruction
= true;
823 if (MOZ_LOG_TEST(gPrintingLog
, LogLevel::Debug
)) {
824 float calcRatio
= mPrintObject
->mShrinkRatio
;
826 ("*******************************************************************"
828 PR_PL(("STF Ratio is: %8.5f Effective Ratio: %8.5f Diff: %8.5f\n",
829 mShrinkToFitFactor
, calcRatio
, mShrinkToFitFactor
- calcRatio
));
831 ("*******************************************************************"
836 // If the frames got reconstructed and reflowed the number of pages might
838 if (didReconstruction
) {
839 FirePrintPreviewUpdateEvent();
840 // If the printing was canceled or restarted with different data,
841 // let's stop doing this printing.
842 if (NS_WARN_IF(mPrt
!= printData
)) {
843 return NS_ERROR_FAILURE
;
847 DUMP_DOC_LIST(("\nAfter Reflow------------------------------------------"));
849 PR_PL(("-------------------------------------------------------\n"));
852 CalcNumPrintablePages(mNumPrintablePages
);
854 PR_PL(("--- Printing %d pages\n", mNumPrintablePages
));
857 // Print listener setup...
858 printData
->OnStartPrinting();
860 // If the printing was canceled or restarted with different data,
861 // let's stop doing this printing.
862 if (NS_WARN_IF(mPrt
!= printData
)) {
863 return NS_ERROR_FAILURE
;
866 nsAutoString fileNameStr
;
867 // check to see if we are printing to a file
868 if (mPrintSettings
->GetOutputDestination() ==
869 nsIPrintSettings::kOutputDestinationFile
) {
870 // On some platforms the BeginDocument needs to know the name of the file.
871 mPrintSettings
->GetToFileName(fileNameStr
);
874 nsAutoString docTitleStr
;
875 nsAutoString docURLStr
;
876 GetDisplayTitleAndURL(*mPrintObject
->mDocument
, mPrintSettings
,
877 DocTitleDefault::eDocURLElseFallback
, docTitleStr
,
880 int32_t startPage
= 1;
881 int32_t endPage
= mNumPrintablePages
;
883 nsTArray
<int32_t> ranges
;
884 mPrintSettings
->GetPageRanges(ranges
);
885 for (size_t i
= 0; i
< ranges
.Length(); i
+= 2) {
886 startPage
= std::max(1, std::min(startPage
, ranges
[i
]));
887 endPage
= std::min(mNumPrintablePages
, std::max(endPage
, ranges
[i
+ 1]));
891 // BeginDocument may pass back a FAILURE code
892 // i.e. On Windows, if you are printing to a file and hit "Cancel"
893 // to the "File Name" dialog, this comes back as an error
894 // Don't start printing when regression test are executed
895 if (mIsDoingPrinting
) {
896 rv
= printData
->mPrintDC
->BeginDocument(docTitleStr
, fileNameStr
, startPage
,
900 if (mIsCreatingPrintPreview
) {
901 // Copy docTitleStr and docURLStr to the pageSequenceFrame, to be displayed
903 nsPageSequenceFrame
* seqFrame
=
904 mPrintObject
->mPresShell
->GetPageSequenceFrame();
906 seqFrame
->StartPrint(mPrintObject
->mPresContext
, mPrintSettings
,
907 docTitleStr
, docURLStr
);
911 PR_PL(("****************** Begin Document ************************\n"));
914 NS_WARNING_ASSERTION(rv
== NS_ERROR_ABORT
,
915 "Failed to begin document for printing");
919 // This will print the docshell document
920 // when it completes asynchronously in the DonePrintingSheets method
921 // it will check to see if there are more docshells to be printed and
922 // then PrintDocContent will be called again.
924 if (mIsDoingPrinting
) {
925 // Double-check that mPrintObject is non-null, because it could have
926 // gotten cleared while running script since we last checked it.
927 if (NS_WARN_IF(!mPrintObject
)) {
928 return NS_ERROR_FAILURE
;
931 PrintDocContent(mPrintObject
, rv
); // ignore return value
937 //-------------------------------------------------------
938 // Recursively reflow each sub-doc and then calc
939 // all the frame locations of the sub-docs
940 nsresult
nsPrintJob::ReflowDocList(const UniquePtr
<nsPrintObject
>& aPO
) {
941 NS_ENSURE_ARG_POINTER(aPO
);
943 // Check to see if the subdocument's element has been hidden by the parent
945 if (aPO
->mParent
&& aPO
->mParent
->mPresShell
) {
947 aPO
->mContent
? aPO
->mContent
->GetPrimaryFrame() : nullptr;
948 if (!frame
|| !frame
->StyleVisibility()->IsVisible()) {
949 aPO
->EnablePrinting(false);
950 aPO
->mInvisible
= true;
955 UpdateZoomRatio(aPO
.get());
958 MOZ_TRY(ReflowPrintObject(aPO
));
960 for (const UniquePtr
<nsPrintObject
>& kid
: aPO
->mKids
) {
961 MOZ_TRY(ReflowDocList(kid
));
966 void nsPrintJob::FirePrintPreviewUpdateEvent() {
967 // Dispatch the event only while in PrintPreview. When printing, there is no
968 // listener bound to this event and therefore no need to dispatch it.
969 if (mCreatedForPrintPreview
&& !mIsDoingPrinting
) {
970 nsCOMPtr
<nsIDocumentViewer
> viewer
= do_QueryInterface(mDocViewerPrint
);
971 if (Document
* document
= viewer
->GetDocument()) {
972 AsyncEventDispatcher::RunDOMEventWhenSafe(
973 *document
, u
"printPreviewUpdate"_ns
, CanBubble::eYes
,
974 ChromeOnlyDispatch::eYes
);
979 nsresult
nsPrintJob::InitPrintDocConstruction(bool aHandleError
) {
980 // Guarantee that mPrintObject won't be deleted.
981 RefPtr
<nsPrintData
> printData
= mPrt
;
983 if (NS_WARN_IF(!printData
)) {
984 return NS_ERROR_FAILURE
;
987 // Attach progressListener to catch network requests.
988 mDidLoadDataForPrinting
= false;
991 AutoRestore
<bool> restore
{mDoingInitialReflow
};
992 mDoingInitialReflow
= true;
994 nsCOMPtr
<nsIWebProgress
> webProgress
=
995 do_QueryInterface(mPrintObject
->mDocShell
);
996 webProgress
->AddProgressListener(static_cast<nsIWebProgressListener
*>(this),
997 nsIWebProgress::NOTIFY_STATE_REQUEST
);
999 MOZ_TRY(ReflowDocList(mPrintObject
));
1001 FirePrintPreviewUpdateEvent();
1004 MaybeResumePrintAfterResourcesLoaded(aHandleError
);
1008 bool nsPrintJob::ShouldResumePrint() const {
1009 if (mDoingInitialReflow
) {
1012 Document
* doc
= mPrintObject
->mDocument
;
1014 NS_ENSURE_TRUE(doc
, true);
1015 nsCOMPtr
<nsILoadGroup
> lg
= doc
->GetDocumentLoadGroup();
1016 NS_ENSURE_TRUE(lg
, true);
1017 bool pending
= false;
1018 nsresult rv
= lg
->IsPending(&pending
);
1019 NS_ENSURE_SUCCESS(rv
, true);
1023 nsresult
nsPrintJob::MaybeResumePrintAfterResourcesLoaded(
1024 bool aCleanupOnError
) {
1025 if (!ShouldResumePrint()) {
1026 mDidLoadDataForPrinting
= true;
1029 // If Destroy() has already been called, mPtr is nullptr. Then, the instance
1030 // needs to do nothing anymore in this method.
1031 // Note: it shouldn't be possible for mPrintObject to be null; we
1032 // just check it for good measure, as we check its owner.
1033 // Note: it shouldn't be possible for mPrintObject->mDocShell to be
1034 // null; we just check it for good measure, as we check its owner.
1035 if (!mPrt
|| NS_WARN_IF(!mPrintObject
) ||
1036 NS_WARN_IF(!mPrintObject
->mDocShell
)) {
1037 return NS_ERROR_FAILURE
;
1040 nsCOMPtr
<nsIWebProgress
> webProgress
=
1041 do_QueryInterface(mPrintObject
->mDocShell
);
1043 webProgress
->RemoveProgressListener(
1044 static_cast<nsIWebProgressListener
*>(this));
1047 if (mIsDoingPrinting
) {
1048 rv
= DocumentReadyForPrinting();
1050 rv
= FinishPrintPreview();
1053 /* cleaup on failure + notify user */
1054 if (aCleanupOnError
&& NS_FAILED(rv
)) {
1055 NS_WARNING_ASSERTION(rv
== NS_ERROR_ABORT
,
1056 "nsPrintJob::ResumePrintAfterResourcesLoaded failed");
1057 CleanupOnFailure(rv
, !mIsDoingPrinting
);
1063 ////////////////////////////////////////////////////////////////////////////////
1064 // nsIWebProgressListener
1066 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP
1067 nsPrintJob::OnStateChange(nsIWebProgress
* aWebProgress
, nsIRequest
* aRequest
,
1068 uint32_t aStateFlags
, nsresult aStatus
) {
1069 if (aStateFlags
& STATE_STOP
) {
1070 // If all resources are loaded, then finish and reflow.
1071 MaybeResumePrintAfterResourcesLoaded(/* aCleanupOnError */ true);
1077 nsPrintJob::OnProgressChange(nsIWebProgress
* aWebProgress
, nsIRequest
* aRequest
,
1078 int32_t aCurSelfProgress
, int32_t aMaxSelfProgress
,
1079 int32_t aCurTotalProgress
,
1080 int32_t aMaxTotalProgress
) {
1081 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
1086 nsPrintJob::OnLocationChange(nsIWebProgress
* aWebProgress
, nsIRequest
* aRequest
,
1087 nsIURI
* aLocation
, uint32_t aFlags
) {
1088 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
1093 nsPrintJob::OnStatusChange(nsIWebProgress
* aWebProgress
, nsIRequest
* aRequest
,
1094 nsresult aStatus
, const char16_t
* aMessage
) {
1095 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
1100 nsPrintJob::OnSecurityChange(nsIWebProgress
* aWebProgress
, nsIRequest
* aRequest
,
1102 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
1107 nsPrintJob::OnContentBlockingEvent(nsIWebProgress
* aWebProgress
,
1108 nsIRequest
* aRequest
, uint32_t aEvent
) {
1109 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
1113 //-------------------------------------------------------
1115 void nsPrintJob::UpdateZoomRatio(nsPrintObject
* aPO
) {
1116 if (!aPO
->mParent
) {
1118 aPO
->mZoomRatio
= mShrinkToFitFactor
;
1119 // If we're actually going to scale (the factor is less than 1), we
1120 // reduce the scale factor slightly to avoid the possibility of floating
1121 // point rounding error causing slight clipping of the longest lines.
1122 if (aPO
->mZoomRatio
!= 1.0f
) {
1123 aPO
->mZoomRatio
-= 0.005f
;
1127 mPrintSettings
->GetScaling(&scaling
);
1128 aPO
->mZoomRatio
= float(scaling
);
1133 nsresult
nsPrintJob::UpdateSelectionAndShrinkPrintObject(
1134 nsPrintObject
* aPO
, bool aDocumentIsTopLevel
) {
1135 PresShell
* displayPresShell
= aPO
->mDocShell
->GetPresShell();
1136 // Transfer Selection Ranges to the new Print PresShell
1137 RefPtr
<Selection
> selection
, selectionPS
;
1138 // It's okay if there is no display shell, just skip copying the selection
1139 if (displayPresShell
) {
1140 selection
= displayPresShell
->GetCurrentSelection(SelectionType::eNormal
);
1142 selectionPS
= aPO
->mPresShell
->GetCurrentSelection(SelectionType::eNormal
);
1144 // Reset all existing selection ranges that might have been added by calling
1145 // this function before.
1147 selectionPS
->RemoveAllRanges(IgnoreErrors());
1149 if (selection
&& selectionPS
) {
1150 const uint32_t rangeCount
= selection
->RangeCount();
1151 for (const uint32_t inx
: IntegerRange(rangeCount
)) {
1152 MOZ_ASSERT(selection
->RangeCount() == rangeCount
);
1153 const RefPtr
<nsRange
> range
{selection
->GetRangeAt(inx
)};
1154 selectionPS
->AddRangeAndSelectFramesAndNotifyListeners(*range
,
1159 // If we are trying to shrink the contents to fit on the page
1160 // we must first locate the "pageContent" frame
1161 // Then we walk the frame tree and look for the "xmost" frame
1162 // this is the frame where the right-hand side of the frame extends
1164 if (mShrinkToFit
&& aDocumentIsTopLevel
) {
1165 nsPageSequenceFrame
* pageSeqFrame
= aPO
->mPresShell
->GetPageSequenceFrame();
1166 NS_ENSURE_STATE(pageSeqFrame
);
1167 aPO
->mShrinkRatio
= pageSeqFrame
->GetSTFPercent();
1168 // Limit the shrink-to-fit scaling for some text-ish type of documents.
1169 nsAutoString contentType
;
1170 aPO
->mPresShell
->GetDocument()->GetContentType(contentType
);
1171 if (contentType
.EqualsLiteral("application/xhtml+xml") ||
1172 StringBeginsWith(contentType
, u
"text/"_ns
)) {
1173 int32_t limitPercent
=
1174 Preferences::GetInt("print.shrink-to-fit.scale-limit-percent", 20);
1175 limitPercent
= std::max(0, limitPercent
);
1176 limitPercent
= std::min(100, limitPercent
);
1177 float minShrinkRatio
= float(limitPercent
) / 100;
1178 aPO
->mShrinkRatio
= std::max(aPO
->mShrinkRatio
, minShrinkRatio
);
1184 nsView
* nsPrintJob::GetParentViewForRoot() {
1185 if (mIsCreatingPrintPreview
) {
1186 if (nsCOMPtr
<nsIDocumentViewer
> viewer
=
1187 do_QueryInterface(mDocViewerPrint
)) {
1188 return viewer
->FindContainerView();
1194 nsresult
nsPrintJob::SetRootView(nsPrintObject
* aPO
, bool& doReturn
,
1195 bool& documentIsTopLevel
, nsSize
& adjSize
) {
1196 bool canCreateScrollbars
= true;
1199 nsView
* parentView
= nullptr;
1203 if (aPO
->mParent
&& aPO
->mParent
->PrintingIsEnabled()) {
1205 aPO
->mContent
? aPO
->mContent
->GetPrimaryFrame() : nullptr;
1206 // Without a frame, this document can't be displayed; therefore, there is no
1207 // point to reflowing it
1209 aPO
->EnablePrinting(false);
1214 // XXX If printing supported printing document hierarchies with non-constant
1215 // zoom this would be wrong as we use the same mPrt->mPrintDC for all
1217 adjSize
= frame
->GetContentRect().Size();
1218 documentIsTopLevel
= false;
1219 // presshell exists because parent is printable
1221 // the top nsPrintObject's widget will always have scrollbars
1222 if (frame
&& frame
->IsSubDocumentFrame()) {
1223 nsView
* view
= frame
->GetView();
1224 NS_ENSURE_TRUE(view
, NS_ERROR_FAILURE
);
1225 view
= view
->GetFirstChild();
1226 NS_ENSURE_TRUE(view
, NS_ERROR_FAILURE
);
1228 canCreateScrollbars
= false;
1231 nscoord pageWidth
, pageHeight
;
1232 mPrt
->mPrintDC
->GetDeviceSurfaceDimensions(pageWidth
, pageHeight
);
1233 adjSize
= nsSize(pageWidth
, pageHeight
);
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.
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 // Get the initial page name. Even though we haven't done any page-name
1378 // fragmentation (that happens during block reflow), this will still be
1379 // valid to find the first page's name.
1380 const nsAtom
* firstPageName
= nsGkAtoms::_empty
;
1381 if (const Element
* const rootElement
= aPO
->mDocument
->GetRootElement()) {
1382 if (const nsIFrame
* const rootFrame
= rootElement
->GetPrimaryFrame()) {
1383 firstPageName
= rootFrame
->ComputePageValue();
1387 const ServoStyleSet::FirstPageSizeAndOrientation sizeAndOrientation
=
1388 presShell
->StyleSet()->GetFirstPageSizeAndOrientation(firstPageName
);
1389 if (mPrintSettings
->GetUsePageRuleSizeAsPaperSize()) {
1390 mMaybeCSSPageSize
= sizeAndOrientation
.size
;
1391 if (sizeAndOrientation
.size
) {
1392 pageSize
= sizeAndOrientation
.size
.value();
1393 aPO
->mPresContext
->SetPageSize(pageSize
);
1397 // If the document has a specified CSS page-size, we rotate the page to
1398 // reflect this. Changing the orientation is reflected by the result of
1399 // FinishPrintPreview, so that the frontend can reflect this.
1400 // The new document has not yet been reflowed, so we have to query the
1401 // original document for any CSS page-size.
1402 if (sizeAndOrientation
.orientation
) {
1403 switch (sizeAndOrientation
.orientation
.value()) {
1404 case StylePageSizeOrientation::Landscape
:
1405 if (pageSize
.width
< pageSize
.height
) {
1406 // Paper is in portrait, CSS page size is landscape.
1407 std::swap(pageSize
.width
, pageSize
.height
);
1410 case StylePageSizeOrientation::Portrait
:
1411 if (pageSize
.width
> pageSize
.height
) {
1412 // Paper is in landscape, CSS page size is portrait.
1413 std::swap(pageSize
.width
, pageSize
.height
);
1417 mMaybeCSSPageLandscape
= Some(sizeAndOrientation
.orientation
.value() ==
1418 StylePageSizeOrientation::Landscape
);
1419 aPO
->mPresContext
->SetPageSize(pageSize
);
1422 // Process the reflow event Initialize posted
1423 presShell
->FlushPendingNotifications(FlushType::Layout
);
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
->GetStartContainer(), range
->StartOffset()};
1557 auto end
= Position
{range
->GetEndContainer(), range
->EndOffset()};
1558 SelectNodesExcept(start
, end
);
1562 void SelectionRangeState::SelectRange(nsRange
* aRange
) {
1563 if (aRange
&& !aRange
->Collapsed()) {
1564 mSelection
->AddRangeAndSelectFramesAndNotifyListeners(*aRange
,
1569 void SelectionRangeState::SelectNodesExcept(const Position
& aStart
,
1570 const Position
& aEnd
) {
1571 SelectNodesExceptInSubtree(aStart
, aEnd
);
1572 if (auto* shadow
= ShadowRoot::FromNode(aStart
.mNode
->SubtreeRoot())) {
1573 auto* host
= shadow
->Host();
1574 SelectNodesExcept(Position
{host
, 0}, Position
{host
, host
->GetChildCount()});
1576 MOZ_ASSERT(aStart
.mNode
->IsInUncomposedDoc());
1580 void SelectionRangeState::SelectNodesExceptInSubtree(const Position
& aStart
,
1581 const Position
& aEnd
) {
1582 static constexpr auto kEllipsis
= u
"\x2026"_ns
;
1584 nsINode
* root
= aStart
.mNode
->SubtreeRoot();
1586 mPositions
.WithEntryHandle(root
, [&](auto&& entry
) -> Position
& {
1587 return entry
.OrInsertWith([&] { return Position
{root
, 0}; });
1590 bool ellipsizedStart
= false;
1591 if (auto* text
= Text::FromNode(aStart
.mNode
)) {
1592 if (start
.mNode
!= text
&& aStart
.mOffset
&&
1593 aStart
.mOffset
< text
->Length()) {
1594 text
->InsertData(aStart
.mOffset
, kEllipsis
, IgnoreErrors());
1595 ellipsizedStart
= true;
1599 RefPtr
<nsRange
> range
= nsRange::Create(
1600 start
.mNode
, start
.mOffset
, aStart
.mNode
, aStart
.mOffset
, IgnoreErrors());
1605 // If we added an ellipsis at the start and the end position was relative to
1606 // the same node account for it here.
1607 if (ellipsizedStart
&& aStart
.mNode
== aEnd
.mNode
) {
1608 start
.mOffset
+= kEllipsis
.Length();
1611 // If the end is mid text then add an ellipsis.
1612 if (auto* text
= Text::FromNode(start
.mNode
)) {
1613 if (start
.mOffset
&& start
.mOffset
< text
->Length()) {
1614 text
->InsertData(start
.mOffset
, kEllipsis
, IgnoreErrors());
1615 start
.mOffset
+= kEllipsis
.Length();
1620 void SelectionRangeState::RemoveSelectionFromDocument() {
1621 for (auto& entry
: mPositions
) {
1622 const Position
& pos
= entry
.GetData();
1623 nsINode
* root
= entry
.GetKey();
1624 RefPtr
<nsRange
> range
= nsRange::Create(
1625 pos
.mNode
, pos
.mOffset
, root
, root
->GetChildCount(), IgnoreErrors());
1628 mSelection
->DeleteFromDocument(IgnoreErrors());
1632 * Builds the complement set of ranges and adds those to the selection.
1633 * Deletes all of the nodes contained in the complement set of ranges
1634 * leaving behind only nodes that were originally selected.
1635 * Adds ellipses to a selected node's text if text is truncated by a range.
1636 * This is used to implement the "Print Selection Only" user interface option.
1638 MOZ_CAN_RUN_SCRIPT_BOUNDARY
static nsresult
DeleteNonSelectedNodes(
1640 MOZ_ASSERT(aDoc
.IsStaticDocument());
1641 const auto* printRanges
= static_cast<nsTArray
<RefPtr
<nsRange
>>*>(
1642 aDoc
.GetProperty(nsGkAtoms::printselectionranges
));
1647 PresShell
* presShell
= aDoc
.GetPresShell();
1648 NS_ENSURE_STATE(presShell
);
1649 RefPtr
<Selection
> selection
=
1650 presShell
->GetCurrentSelection(SelectionType::eNormal
);
1651 NS_ENSURE_STATE(selection
);
1653 SelectionRangeState
state(std::move(selection
));
1654 state
.SelectComplementOf(*printRanges
);
1655 state
.RemoveSelectionFromDocument();
1659 //-------------------------------------------------------
1660 nsresult
nsPrintJob::DoPrint(const UniquePtr
<nsPrintObject
>& aPO
) {
1662 PR_PL(("**************************** %s ****************************\n",
1663 LoggableTypeOfPO(aPO
.get())));
1664 PR_PL(("****** In DV::DoPrint PO: %p \n", aPO
.get()));
1666 PresShell
* poPresShell
= aPO
->mPresShell
;
1667 nsPresContext
* poPresContext
= aPO
->mPresContext
;
1669 NS_ASSERTION(poPresContext
, "PrintObject has not been reflowed");
1670 NS_ASSERTION(poPresContext
->Type() != nsPresContext::eContext_PrintPreview
,
1671 "How did this context end up here?");
1673 // Guarantee that mPrt and the objects it owns won't be deleted in this method
1674 // because it might be cleared if other modules called from here may fire
1675 // events, notifying observers and/or listeners.
1676 RefPtr
<nsPrintData
> printData
= mPrt
;
1677 if (NS_WARN_IF(!printData
)) {
1678 return NS_ERROR_FAILURE
;
1682 // Ask the page sequence frame to print all the pages
1683 nsPageSequenceFrame
* seqFrame
= poPresShell
->GetPageSequenceFrame();
1684 MOZ_ASSERT(seqFrame
, "no page sequence frame");
1686 // We are done preparing for printing, so we can turn this off
1687 mPreparingForPrint
= false;
1689 #ifdef EXTENDED_DEBUG_PRINTING
1690 nsIFrame
* rootFrame
= poPresShell
->GetRootFrame();
1691 if (aPO
->PrintingIsEnabled()) {
1692 nsAutoCString docStr
;
1693 nsAutoCString urlStr
;
1694 GetDocTitleAndURL(aPO
, docStr
, urlStr
);
1695 DumpLayoutData(docStr
.get(), urlStr
.get(), poPresContext
,
1696 printData
->mPrintDC
, rootFrame
, aPO
->mDocShell
, nullptr);
1700 if (!mPrintSettings
) {
1701 // not sure what to do here!
1702 SetIsPrinting(false);
1703 return NS_ERROR_FAILURE
;
1706 nsAutoString docTitleStr
;
1707 nsAutoString docURLStr
;
1708 GetDisplayTitleAndURL(*aPO
->mDocument
, mPrintSettings
,
1709 DocTitleDefault::eFallback
, docTitleStr
, docURLStr
);
1712 SetIsPrinting(false);
1713 return NS_ERROR_FAILURE
;
1716 // For telemetry, get paper size being used; convert the dimensions to
1717 // points and ensure they reflect portrait orientation.
1718 nsIPrintSettings
* settings
= mPrintSettings
;
1719 double paperWidth
, paperHeight
;
1720 settings
->GetPaperWidth(&paperWidth
);
1721 settings
->GetPaperHeight(&paperHeight
);
1723 settings
->GetPaperSizeUnit(&sizeUnit
);
1725 case nsIPrintSettings::kPaperSizeInches
:
1727 paperHeight
*= 72.0;
1729 case nsIPrintSettings::kPaperSizeMillimeters
:
1730 paperWidth
*= 72.0 / 25.4;
1731 paperHeight
*= 72.0 / 25.4;
1734 MOZ_ASSERT_UNREACHABLE("unknown paper size unit");
1737 if (paperWidth
> paperHeight
) {
1738 std::swap(paperWidth
, paperHeight
);
1740 // Use the paper size to build a Telemetry Scalar key.
1742 key
.AppendInt(int32_t(NS_round(paperWidth
)));
1744 key
.AppendInt(int32_t(NS_round(paperHeight
)));
1745 Telemetry::ScalarAdd(Telemetry::ScalarID::PRINTING_PAPER_SIZE
, key
, 1);
1747 mPageSeqFrame
= seqFrame
;
1748 seqFrame
->StartPrint(poPresContext
, settings
, docTitleStr
, docURLStr
);
1750 // Schedule Page to Print
1751 PR_PL(("Scheduling Print of PO: %p (%s) \n", aPO
.get(),
1752 LoggableTypeOfPO(aPO
.get())));
1753 StartPagePrintTimer(aPO
);
1759 //-------------------------------------------------------
1760 bool nsPrintJob::PrePrintSheet() {
1761 NS_ASSERTION(mPageSeqFrame
.IsAlive(), "mPageSeqFrame is not alive!");
1762 NS_ASSERTION(mPrt
, "mPrt is null!");
1764 // Although these should NEVER be nullptr
1765 // This is added insurance, to make sure we don't crash in optimized builds
1766 if (!mPrt
|| !mPageSeqFrame
.IsAlive()) {
1767 return true; // means we are done preparing the sheet.
1770 // Guarantee that mPrt won't be deleted during a call of
1771 // FirePrintingErrorEvent().
1772 RefPtr
<nsPrintData
> printData
= mPrt
;
1774 // Ask mPageSeqFrame if the sheet is ready to be printed.
1775 // If the sheet doesn't get printed at all, the |done| will be |true|.
1777 nsPageSequenceFrame
* pageSeqFrame
= do_QueryFrame(mPageSeqFrame
.GetFrame());
1778 nsresult rv
= pageSeqFrame
->PrePrintNextSheet(mPagePrintTimer
, &done
);
1779 if (NS_FAILED(rv
)) {
1780 // ??? ::PrintSheet doesn't set |printData->mIsAborted = true| if
1781 // rv != NS_ERROR_ABORT, but I don't really understand why this should be
1782 // the right thing to do? Shouldn't |printData->mIsAborted| set to true
1783 // all the time if something went wrong?
1784 if (rv
!= NS_ERROR_ABORT
) {
1785 FirePrintingErrorEvent(rv
);
1786 printData
->mIsAborted
= true;
1793 bool nsPrintJob::PrintSheet(nsPrintObject
* aPO
) {
1794 NS_ASSERTION(aPO
, "aPO is null!");
1795 NS_ASSERTION(mPageSeqFrame
.IsAlive(), "mPageSeqFrame is not alive!");
1796 NS_ASSERTION(mPrt
, "mPrt is null!");
1798 // Although these should NEVER be nullptr
1799 // This is added insurance, to make sure we don't crash in optimized builds
1800 if (!mPrt
|| !aPO
|| !mPageSeqFrame
.IsAlive()) {
1801 FirePrintingErrorEvent(NS_ERROR_FAILURE
);
1802 return true; // means we are done printing
1805 // Guarantee that mPrt won't be deleted during a call of
1806 // nsPrintData::DoOnProgressChange() which runs some listeners,
1807 // which may clear (& might otherwise destroy).
1808 RefPtr
<nsPrintData
> printData
= mPrt
;
1810 PR_PL(("-----------------------------------\n"));
1811 PR_PL(("------ In DV::PrintSheet PO: %p (%s)\n", aPO
, LoggableTypeOfPO(aPO
)));
1813 if (printData
->mIsAborted
) {
1817 nsPageSequenceFrame
* pageSeqFrame
= do_QueryFrame(mPageSeqFrame
.GetFrame());
1818 const uint32_t sheetIdx
= pageSeqFrame
->GetCurrentSheetIdx();
1819 const uint32_t numSheets
= pageSeqFrame
->PrincipalChildList().GetLength();
1821 PR_PL(("****** Printing sheet index %d of %d sheets(s)\n", sheetIdx
,
1824 MOZ_ASSERT(numSheets
> 0, "print operations must have at least 1 sheet");
1825 MOZ_ASSERT(sheetIdx
< numSheets
,
1826 "sheetIdx shouldn't be allowed to go out of bounds");
1827 printData
->DoOnProgressChange(sheetIdx
, numSheets
, false, 0);
1828 if (NS_WARN_IF(mPrt
!= printData
)) {
1829 // If current printing is canceled or new print is started, let's return
1830 // true to notify the caller of current printing is done.
1835 // if a print job was cancelled externally, an EndPage or BeginPage may
1836 // fail and the failure is passed back here.
1837 // Returning true means we are done printing.
1839 // When rv == NS_ERROR_ABORT, it means we want out of the
1840 // print job without displaying any error messages
1841 nsresult rv
= pageSeqFrame
->PrintNextSheet();
1842 if (NS_FAILED(rv
)) {
1843 if (rv
!= NS_ERROR_ABORT
) {
1844 FirePrintingErrorEvent(rv
);
1845 printData
->mIsAborted
= true;
1850 pageSeqFrame
->DoPageEnd();
1852 // If we just printed the final sheet (the one with index "numSheets-1"),
1854 return (sheetIdx
== numSheets
- 1);
1857 void nsPrintJob::PageDone(nsresult aResult
) {
1858 MOZ_ASSERT(mIsDoingPrinting
);
1860 // mPagePrintTimer might be released during RemotePrintFinished, keep a
1861 // reference here to make sure it lives long enough.
1862 RefPtr
<nsPagePrintTimer
> timer
= mPagePrintTimer
;
1863 timer
->RemotePrintFinished();
1866 //-----------------------------------------------------------------
1867 //-- Done: Printing Methods
1868 //-----------------------------------------------------------------
1870 //-----------------------------------------------------------------
1871 //-- Section: Misc Support Methods
1872 //-----------------------------------------------------------------
1874 //---------------------------------------------------------------------
1875 void nsPrintJob::SetIsPrinting(bool aIsPrinting
) {
1876 mIsDoingPrinting
= aIsPrinting
;
1878 mPreparingForPrint
= true;
1882 //---------------------------------------------------------------------
1883 void nsPrintJob::SetIsPrintPreview(bool aIsPrintPreview
) {
1884 mCreatedForPrintPreview
= aIsPrintPreview
;
1886 if (mDocViewerPrint
) {
1887 mDocViewerPrint
->SetIsPrintPreview(aIsPrintPreview
);
1891 //-------------------------------------------------------
1892 bool nsPrintJob::DonePrintingSheets(nsPrintObject
* aPO
, nsresult aResult
) {
1893 // NS_ASSERTION(aPO, "Pointer is null!");
1894 PR_PL(("****** In DV::DonePrintingSheets PO: %p (%s)\n", aPO
,
1895 aPO
? LoggableTypeOfPO(aPO
) : ""));
1897 // If there is a pageSeqFrame, make sure there are no more printCanvas active
1898 // that might call |Notify| on the pagePrintTimer after things are cleaned up
1899 // and printing was marked as being done.
1900 if (mPageSeqFrame
.IsAlive()) {
1901 nsPageSequenceFrame
* pageSeqFrame
= do_QueryFrame(mPageSeqFrame
.GetFrame());
1902 pageSeqFrame
->ResetPrintCanvasList();
1905 // Guarantee that mPrt and mPrintObject won't be deleted during a
1906 // call of PrintDocContent() and FirePrintCompletionEvent().
1907 RefPtr
<nsPrintData
> printData
= mPrt
;
1909 if (aPO
&& !printData
->mIsAborted
) {
1910 aPO
->mHasBeenPrinted
= true;
1912 bool didPrint
= PrintDocContent(mPrintObject
, rv
);
1913 if (NS_SUCCEEDED(rv
) && didPrint
) {
1915 ("****** In DV::DonePrintingSheets PO: %p (%s) didPrint:%s (Not Done "
1917 aPO
, LoggableTypeOfPO(aPO
), PRT_YESNO(didPrint
)));
1922 if (NS_SUCCEEDED(aResult
)) {
1923 FirePrintCompletionEvent();
1924 // XXX mPrt may be cleared or replaced with new instance here.
1925 // However, the following methods will clean up with new mPrt or will
1926 // do nothing due to no proper nsPrintData instance.
1929 SetIsPrinting(false);
1931 // Release reference to mPagePrintTimer; the timer object destroys itself
1932 // after this returns true
1933 DisconnectPagePrintTimer();
1938 //-------------------------------------------------------
1939 nsresult
nsPrintJob::EnablePOsForPrinting() {
1940 // Guarantee that mPrt and the objects it owns won't be deleted.
1941 RefPtr
<nsPrintData
> printData
= mPrt
;
1943 // NOTE: All POs have been "turned off" for printing
1944 // this is where we decided which POs get printed.
1946 if (!printData
|| !mPrintSettings
) {
1947 return NS_ERROR_FAILURE
;
1951 PR_PL(("********* nsPrintJob::EnablePOsForPrinting *********\n"));
1953 if (!mPrintSettings
->GetPrintSelectionOnly()) {
1954 mPrintObject
->EnablePrinting(true);
1958 // This means we are either printing a selected iframe or
1959 // we are printing the current selection.
1960 NS_ENSURE_STATE(!mDisallowSelectionPrint
&& mSelectionRoot
);
1962 // If mSelectionRoot is a selected iframe without a selection, then just
1963 // enable normally from that point.
1964 if (mSelectionRoot
->mParent
&& !mSelectionRoot
->HasSelection()) {
1965 mSelectionRoot
->EnablePrinting(true);
1967 // Otherwise, only enable nsPrintObjects that have a selection.
1968 mSelectionRoot
->EnablePrintingSelectionOnly();
1973 //-----------------------------------------------------------------
1974 //-- Done: Misc Support Methods
1975 //-----------------------------------------------------------------
1977 //-----------------------------------------------------------------
1978 //-- Section: Finishing up or Cleaning up
1979 //-----------------------------------------------------------------
1981 //-----------------------------------------------------------------
1982 nsresult
nsPrintJob::FinishPrintPreview() {
1983 nsresult rv
= NS_OK
;
1985 #ifdef NS_PRINT_PREVIEW
1987 // If mPrt is null we've already finished with print preview. If mPrt is not
1988 // null but mIsCreatingPrintPreview is false FinishPrintPreview must have
1989 // already failed due to DocumentReadyForPrinting failing.
1990 if (!mPrt
|| !mIsCreatingPrintPreview
) {
1994 rv
= DocumentReadyForPrinting();
1996 // Note that this method may be called while the instance is being
1997 // initialized. Some methods which initialize the instance (e.g.,
1998 // DoCommonPrint) may need to stop initializing and return error if
1999 // this is called. Therefore it's important to set mIsCreatingPrintPreview
2000 // state to false here. If you need to stop setting that here, you need to
2001 // keep them being able to check whether the owner stopped using this
2003 mIsCreatingPrintPreview
= false;
2005 // mPrt may be cleared during a call of nsPrintData::OnEndPrinting()
2006 // because that method invokes some arbitrary listeners.
2007 // TODO(dshin): Does any listener attach to print preview? Doesn't seem like
2008 // we call matching `OnStartPrinting()` for previews.
2009 RefPtr
<nsPrintData
> printData
= mPrt
;
2010 if (NS_FAILED(rv
)) {
2011 printData
->OnEndPrinting();
2016 if (mPrintPreviewCallback
) {
2017 const bool hasSelection
= !mDisallowSelectionPrint
&& mSelectionRoot
;
2019 Maybe
<float> pageWidth
;
2020 Maybe
<float> pageHeight
;
2021 if (mMaybeCSSPageSize
) {
2022 nsSize cssPageSize
= *mMaybeCSSPageSize
;
2023 pageWidth
= Some(float(cssPageSize
.width
) / float(AppUnitsPerCSSInch()));
2025 Some(float(cssPageSize
.height
) / float(AppUnitsPerCSSInch()));
2028 mPrintPreviewCallback(PrintPreviewResultInfo(
2029 GetPrintPreviewNumSheets(), GetRawNumPages(), GetIsEmpty(),
2030 hasSelection
, hasSelection
&& mPrintObject
->HasSelection(),
2031 mMaybeCSSPageLandscape
, pageWidth
, pageHeight
));
2032 mPrintPreviewCallback
= nullptr;
2035 // At this point we are done preparing everything
2036 // before it is to be created
2038 printData
->OnEndPrinting();
2040 #endif // NS_PRINT_PREVIEW
2045 //-----------------------------------------------------------------
2046 //-- Done: Finishing up or Cleaning up
2047 //-----------------------------------------------------------------
2049 /*=============== Timer Related Code ======================*/
2050 nsresult
nsPrintJob::StartPagePrintTimer(const UniquePtr
<nsPrintObject
>& aPO
) {
2051 if (!mPagePrintTimer
) {
2052 // Get the delay time in between the printing of each page
2053 // this gives the user more time to press cancel
2054 int32_t printPageDelay
= mPrintSettings
->GetPrintPageDelay();
2056 nsCOMPtr
<nsIDocumentViewer
> viewer
= do_QueryInterface(mDocViewerPrint
);
2057 NS_ENSURE_TRUE(viewer
, NS_ERROR_FAILURE
);
2058 nsCOMPtr
<Document
> doc
= viewer
->GetDocument();
2059 NS_ENSURE_TRUE(doc
, NS_ERROR_FAILURE
);
2062 new nsPagePrintTimer(this, mDocViewerPrint
, doc
, printPageDelay
);
2064 if (mRemotePrintJob
) {
2065 mRemotePrintJob
->SetPagePrintTimer(mPagePrintTimer
);
2066 mRemotePrintJob
->SetPrintJob(this);
2070 return mPagePrintTimer
->Start(aPO
.get());
2073 //---------------------------------------------------------------
2074 //-- PLEvent Notification
2075 //---------------------------------------------------------------
2076 class nsPrintCompletionEvent
: public Runnable
{
2078 explicit nsPrintCompletionEvent(nsIDocumentViewerPrint
* docViewerPrint
)
2079 : mozilla::Runnable("nsPrintCompletionEvent"),
2080 mDocViewerPrint(docViewerPrint
) {
2081 NS_ASSERTION(mDocViewerPrint
, "mDocViewerPrint is null.");
2084 NS_IMETHOD
Run() override
{
2085 if (mDocViewerPrint
) mDocViewerPrint
->OnDonePrinting();
2090 nsCOMPtr
<nsIDocumentViewerPrint
> mDocViewerPrint
;
2093 //-----------------------------------------------------------
2094 void nsPrintJob::FirePrintCompletionEvent() {
2095 MOZ_ASSERT(NS_IsMainThread());
2096 nsCOMPtr
<nsIRunnable
> event
= new nsPrintCompletionEvent(mDocViewerPrint
);
2097 nsCOMPtr
<nsIDocumentViewer
> viewer
= do_QueryInterface(mDocViewerPrint
);
2098 NS_ENSURE_TRUE_VOID(viewer
);
2099 nsCOMPtr
<Document
> doc
= viewer
->GetDocument();
2100 NS_ENSURE_TRUE_VOID(doc
);
2101 NS_ENSURE_SUCCESS_VOID(doc
->Dispatch(event
.forget()));
2104 void nsPrintJob::DisconnectPagePrintTimer() {
2105 if (mPagePrintTimer
) {
2106 mPagePrintTimer
->Disconnect();
2107 mPagePrintTimer
= nullptr;
2111 //---------------------------------------------------------------
2112 //---------------------------------------------------------------
2113 //-- Debug helper routines
2114 //---------------------------------------------------------------
2115 //---------------------------------------------------------------
2116 #if defined(XP_WIN) && defined(EXTENDED_DEBUG_PRINTING)
2117 # include <windows.h>
2118 # include <process.h>
2119 # include <direct.h>
2121 # define MY_FINDFIRST(a, b) FindFirstFile(a, b)
2122 # define MY_FINDNEXT(a, b) FindNextFile(a, b)
2123 # define ISDIR(a) (a.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
2124 # define MY_FINDCLOSE(a) FindClose(a)
2125 # define MY_FILENAME(a) a.cFileName
2126 # define MY_FILESIZE(a) (a.nFileSizeHigh * MAXDWORD) + a.nFileSizeLow
2128 int RemoveFilesInDir(const char* aDir
) {
2129 WIN32_FIND_DATA data_ptr
;
2132 char path
[MAX_PATH
];
2136 // Append slash to the end of the directory names if not there
2137 if (path
[strlen(path
) - 1] != '\\') strcat(path
, "\\");
2139 char findPath
[MAX_PATH
];
2140 strcpy(findPath
, path
);
2141 strcat(findPath
, "*.*");
2143 find_handle
= MY_FINDFIRST(findPath
, &data_ptr
);
2145 if (find_handle
!= INVALID_HANDLE_VALUE
) {
2147 if (ISDIR(data_ptr
) && (stricmp(MY_FILENAME(data_ptr
), ".")) &&
2148 (stricmp(MY_FILENAME(data_ptr
), ".."))) {
2150 } else if (!ISDIR(data_ptr
)) {
2151 if (!strncmp(MY_FILENAME(data_ptr
), "print_dump", 10)) {
2152 char fileName
[MAX_PATH
];
2153 strcpy(fileName
, aDir
);
2154 strcat(fileName
, "\\");
2155 strcat(fileName
, MY_FILENAME(data_ptr
));
2156 printf("Removing %s\n", fileName
);
2160 } while (MY_FINDNEXT(find_handle
, &data_ptr
));
2161 MY_FINDCLOSE(find_handle
);
2167 #ifdef EXTENDED_DEBUG_PRINTING
2169 /** ---------------------------------------------------
2170 * Dumps Frames for Printing
2172 static void RootFrameList(nsPresContext
* aPresContext
, FILE* out
,
2173 const char* aPrefix
) {
2174 if (!aPresContext
|| !out
) return;
2176 if (PresShell
* presShell
= aPresContext
->GetPresShell()) {
2177 nsIFrame
* frame
= presShell
->GetRootFrame();
2179 frame
->List(out
, aPrefix
);
2184 /** ---------------------------------------------------
2185 * Dumps Frames for Printing
2187 static void DumpFrames(FILE* out
, nsPresContext
* aPresContext
,
2188 gfxContext
* aRendContext
, nsIFrame
* aFrame
,
2190 NS_ASSERTION(out
, "Pointer is null!");
2191 NS_ASSERTION(aPresContext
, "Pointer is null!");
2192 NS_ASSERTION(aRendContext
, "Pointer is null!");
2193 NS_ASSERTION(aFrame
, "Pointer is null!");
2195 nsIFrame
* child
= aFrame
->PrincipalChildList().FirstChild();
2196 while (child
!= nullptr) {
2197 for (int32_t i
= 0; i
< aLevel
; i
++) {
2201 child
->GetFrameName(tmp
);
2202 fputs(NS_LossyConvertUTF16toASCII(tmp
).get(), out
);
2204 if (child
->IsVisibleForPainting()) {
2205 fprintf(out
, " %p %s", child
, isSelected
? "VIS" : "UVS");
2206 nsRect rect
= child
->GetRect();
2207 fprintf(out
, "[%d,%d,%d,%d] ", rect
.x
, rect
.y
, rect
.width
, rect
.height
);
2208 fprintf(out
, "v: %p ", (void*)child
->GetView());
2210 DumpFrames(out
, aPresContext
, aRendContext
, child
, aLevel
+ 1);
2211 child
= child
->GetNextSibling();
2216 /** ---------------------------------------------------
2217 * Dumps the Views from the DocShell
2219 static void DumpViews(nsIDocShell
* aDocShell
, FILE* out
) {
2220 NS_ASSERTION(aDocShell
, "Pointer is null!");
2221 NS_ASSERTION(out
, "Pointer is null!");
2223 if (nullptr != aDocShell
) {
2224 fprintf(out
, "docshell=%p \n", aDocShell
);
2225 if (PresShell
* presShell
= aDocShell
->GetPresShell()) {
2226 nsViewManager
* vm
= presShell
->GetViewManager();
2228 nsView
* root
= vm
->GetRootView();
2234 fputs("null pres shell\n", out
);
2237 // dump the views of the sub documents
2239 BrowsingContext
* bc
= nsDocShell::Cast(aDocShell
)->GetBrowsingContext();
2240 for (auto& child
: bc
->Children()) {
2241 if (auto childDS
= child
->GetDocShell()) {
2242 DumpViews(childAsShell
, out
);
2248 /** ---------------------------------------------------
2249 * Dumps the Views and Frames
2251 void DumpLayoutData(const char* aTitleStr
, const char* aURLStr
,
2252 nsPresContext
* aPresContext
, nsDeviceContext
* aDC
,
2253 nsIFrame
* aRootFrame
, nsIDocShell
* aDocShell
,
2254 FILE* aFD
= nullptr) {
2255 if (!MOZ_LOG_TEST(gPrintingLog
, DUMP_LAYOUT_LEVEL
)) {
2259 if (aPresContext
== nullptr || aDC
== nullptr) {
2263 # ifdef NS_PRINT_PREVIEW
2264 if (aPresContext
->Type() == nsPresContext::eContext_PrintPreview
) {
2269 NS_ASSERTION(aRootFrame
, "Pointer is null!");
2270 NS_ASSERTION(aDocShell
, "Pointer is null!");
2272 // Dump all the frames and view to a a file
2274 sprintf(filename
, "print_dump_layout_%d.txt", gDumpLOFileNameCnt
++);
2275 FILE* fd
= aFD
? aFD
: fopen(filename
, "w");
2277 fprintf(fd
, "Title: %s\n", aTitleStr
? aTitleStr
: "");
2278 fprintf(fd
, "URL: %s\n", aURLStr
? aURLStr
: "");
2279 fprintf(fd
, "--------------- Frames ----------------\n");
2280 fprintf(fd
, "--------------- Frames ----------------\n");
2281 // RefPtr<gfxContext> renderingContext =
2282 // aDC->CreateRenderingContext();
2283 RootFrameList(aPresContext
, fd
, "");
2284 // DumpFrames(fd, aPresContext, renderingContext, aRootFrame, 0);
2285 fprintf(fd
, "---------------------------------------\n\n");
2286 fprintf(fd
, "--------------- Views From Root Frame----------------\n");
2287 nsView
* v
= aRootFrame
->GetView();
2291 printf("View is null!\n");
2294 fprintf(fd
, "--------------- All Views ----------------\n");
2295 DumpViews(aDocShell
, fd
);
2296 fprintf(fd
, "---------------------------------------\n\n");
2298 if (aFD
== nullptr) {
2304 //-------------------------------------------------------------
2305 static void DumpPrintObjectsList(const nsTArray
<nsPrintObject
*>& aDocList
) {
2306 if (!MOZ_LOG_TEST(gPrintingLog
, DUMP_LAYOUT_LEVEL
)) {
2310 PR_PL(("Doc List\n***************************************************\n"));
2312 ("T P A H PO DocShell Seq Page Root Page# "
2314 for (nsPrintObject
* po
: aDocList
) {
2315 NS_ASSERTION(po
, "nsPrintObject can't be null!");
2316 nsIFrame
* rootFrame
= nullptr;
2317 if (po
->mPresShell
) {
2318 rootFrame
= po
->mPresShell
->GetRootFrame();
2319 while (rootFrame
!= nullptr) {
2320 nsPageSequenceFrame
* sqf
= do_QueryFrame(rootFrame
);
2324 rootFrame
= rootFrame
->PrincipalChildList().FirstChild();
2328 PR_PL(("%s %d %d %p %p %p\n", ShortLoggableTypeOfPO(po
),
2329 po
->PrintingIsEnabled(), po
->mHasBeenPrinted
, po
,
2330 po
->mDocShell
.get(), rootFrame
));
2334 //-------------------------------------------------------------
2335 static void DumpPrintObjectsTree(nsPrintObject
* aPO
, int aLevel
, FILE* aFD
) {
2336 if (!MOZ_LOG_TEST(gPrintingLog
, DUMP_LAYOUT_LEVEL
)) {
2340 NS_ASSERTION(aPO
, "Pointer is null!");
2342 FILE* fd
= aFD
? aFD
: stdout
;
2345 "DocTree\n***************************************************\n");
2346 fprintf(fd
, "T PO DocShell Seq Page Page# Rect\n");
2348 for (const auto& po
: aPO
->mKids
) {
2349 NS_ASSERTION(po
, "nsPrintObject can't be null!");
2350 for (int32_t k
= 0; k
< aLevel
; k
++) fprintf(fd
, " ");
2351 fprintf(fd
, "%s %p %p\n", ShortLoggableTypeOfPO(po
.get()), po
.get(),
2352 po
->mDocShell
.get());
2356 //-------------------------------------------------------------
2357 static void GetDocTitleAndURL(const UniquePtr
<nsPrintObject
>& aPO
,
2358 nsACString
& aDocStr
, nsACString
& aURLStr
) {
2359 nsAutoString docTitleStr
;
2360 nsAutoString docURLStr
;
2361 GetDocumentTitleAndURL(aPO
->mDocument
, docTitleStr
, docURLStr
);
2362 CopyUTF16toUTF8(docTitleStr
, aDocStr
);
2363 CopyUTF16toUTF8(docURLStr
, aURLStr
);
2366 //-------------------------------------------------------------
2367 static void DumpPrintObjectsTreeLayout(const UniquePtr
<nsPrintObject
>& aPO
,
2368 nsDeviceContext
* aDC
, int aLevel
,
2370 if (!MOZ_LOG_TEST(gPrintingLog
, DUMP_LAYOUT_LEVEL
)) {
2374 NS_ASSERTION(aPO
, "Pointer is null!");
2375 NS_ASSERTION(aDC
, "Pointer is null!");
2379 fd
= fopen("tree_layout.txt", "w");
2381 "DocTree\n***************************************************\n");
2382 fprintf(fd
, "***************************************************\n");
2383 fprintf(fd
, "T PO DocShell Seq Page Page# Rect\n");
2388 nsIFrame
* rootFrame
= nullptr;
2389 if (aPO
->mPresShell
) {
2390 rootFrame
= aPO
->mPresShell
->GetRootFrame();
2392 for (int32_t k
= 0; k
< aLevel
; k
++) fprintf(fd
, " ");
2393 fprintf(fd
, "%s %p %p\n", ShortLoggableTypeOfPO(aPO
.get()), aPO
.get(),
2394 aPO
->mDocShell
.get());
2395 if (aPO
->PrintingIsEnabled()) {
2396 nsAutoCString docStr
;
2397 nsAutoCString urlStr
;
2398 GetDocTitleAndURL(aPO
, docStr
, urlStr
);
2399 DumpLayoutData(docStr
.get(), urlStr
.get(), aPO
->mPresContext
, aDC
,
2400 rootFrame
, aPO
->mDocShell
, fd
);
2402 fprintf(fd
, "<***************************************************>\n");
2404 for (const auto& po
: aPO
->mKids
) {
2405 NS_ASSERTION(po
, "nsPrintObject can't be null!");
2406 DumpPrintObjectsTreeLayout(po
, aDC
, aLevel
+ 1, fd
);
2409 if (aLevel
== 0 && fd
) {
2414 //-------------------------------------------------------------
2415 static void DumpPrintObjectsListStart(
2416 const char* aStr
, const nsTArray
<nsPrintObject
*>& aDocList
) {
2417 if (!MOZ_LOG_TEST(gPrintingLog
, DUMP_LAYOUT_LEVEL
)) {
2421 NS_ASSERTION(aStr
, "Pointer is null!");
2423 PR_PL(("%s\n", aStr
));
2424 DumpPrintObjectsList(aDocList
);
2429 //---------------------------------------------------------------
2430 //---------------------------------------------------------------
2431 //-- End of debug helper routines
2432 //---------------------------------------------------------------