Bug 1858509 add thread-safety annotations around MediaSourceDemuxer::mMonitor r=alwu
[gecko.git] / layout / printing / nsPrintJob.cpp
blobbef5fa7fb806da7c559ea8b49b13b0ec273ed6a9
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"
9 #include "nsDebug.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"
39 #include "nsIURI.h"
40 #include "nsITextToSubURI.h"
41 #include "nsError.h"
43 #include "nsView.h"
44 #include <algorithm>
46 // Print Options
47 #include "nsIPrintSettings.h"
48 #include "nsIPrintSettingsService.h"
49 #include "nsGkAtoms.h"
50 #include "nsXPCOM.h"
52 static const char sPrintSettingsServiceContractID[] =
53 "@mozilla.org/gfx/printsettings-service;1";
55 // Printing Timer
56 #include "nsPagePrintTimer.h"
58 // FrameSet
59 #include "mozilla/dom/Document.h"
60 #include "mozilla/dom/DocumentInlines.h"
62 // Misc
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"
71 #include "Text.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"
86 #include "nsRange.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 //-----------------------------------------------------
96 // PR LOGGING
97 #include "mozilla/Logging.h"
99 #ifdef DEBUG
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
103 #endif
105 // this log level turns on the dumping of each document's layout info
106 #define DUMP_LAYOUT_LEVEL (static_cast<mozilla::LogLevel>(9))
108 #ifndef PR_PL
109 static mozilla::LazyLogModule gPrintingLog("printing");
111 # define PR_PL(_p1) MOZ_LOG(gPrintingLog, mozilla::LogLevel::Debug, _p1);
112 #endif
114 #ifdef EXTENDED_DEBUG_PRINTING
115 static uint32_t gDumpFileNameCnt = 0;
116 static uint32_t gDumpLOFileNameCnt = 0;
117 #endif
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);
152 #else
153 # define DUMP_DOC_LIST(_title)
154 # define DUMP_DOC_TREE
155 # define DUMP_DOC_TREELAYOUT
156 #endif
158 // -------------------------------------------------------
159 // Helpers
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();
188 if (!docShell) {
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);
196 continue;
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()) {
206 continue;
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() {
244 if (mIsDestroying) {
245 return;
247 mIsDestroying = true;
249 DestroyPrintingData();
251 mDocViewerPrint = nullptr;
254 //-------------------------------------------------------
255 void nsPrintJob::DestroyPrintingData() {
256 mPrintObject = nullptr;
257 mPrt = nullptr;
260 nsPrintJob::nsPrintJob(nsIDocumentViewerPrint& aDocViewerPrint,
261 nsIDocShell& aDocShell, Document& aOriginalDoc,
262 float aScreenDPI)
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)) {
279 return {nullptr, 0};
282 const nsPrintObject* po = mPrintObject.get();
283 if (NS_WARN_IF(!po)) {
284 return {nullptr, 0};
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");
293 return {nullptr, 0};
296 nsPageSequenceFrame* seqFrame = po->mPresShell->GetPageSequenceFrame();
297 if (!seqFrame) {
298 return {nullptr, 0};
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
307 # ifdef XP_WIN
308 static int RemoveFilesInDir(const char* aDir);
309 # endif
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,
320 FILE* aFD);
321 #endif
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);
336 if (NS_FAILED(rv)) {
337 if (aIsPrintPreview) {
338 mIsCreatingPrintPreview = false;
339 SetIsPrintPreview(false);
340 } else {
341 SetIsPrinting(false);
343 if (rv != NS_ERROR_ABORT && rv != NS_ERROR_OUT_OF_MEMORY) {
344 FirePrintingErrorEvent(rv);
346 DestroyPrintingData();
349 return rv;
352 nsresult nsPrintJob::DoCommonPrint(bool aIsPrintPreview,
353 nsIPrintSettings* aPrintSettings,
354 nsIWebProgressListener* aWebProgressListener,
355 Document& aDoc) {
356 MOZ_ASSERT(aDoc.IsStaticDocument());
358 nsresult rv;
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);
370 } else {
371 SetIsPrinting(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.
407 if (mIsDestroying) {
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);
420 } else {
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);
447 return NS_OK;
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);
467 if (NS_FAILED(rv)) {
468 if (mPrintPreviewCallback) {
469 // signal error
470 mPrintPreviewCallback(
471 PrintPreviewResultInfo(0, 0, false, false, false, {}, {}, {}));
472 mPrintPreviewCallback = nullptr;
475 return rv;
478 int32_t nsPrintJob::GetRawNumPages() const {
479 auto [seqFrame, numSheets] = GetSeqFrameAndCountSheets();
480 Unused << numSheets;
481 return seqFrame ? seqFrame->GetRawNumPages() : 0;
484 bool nsPrintJob::GetIsEmpty() const {
485 auto [seqFrame, numSheets] = GetSeqFrameAndCountSheets();
486 if (!seqFrame) {
487 return true;
489 if (numSheets > 1) {
490 return false;
492 return !seqFrame->GetPagesInFirstSheet();
495 int32_t nsPrintJob::GetPrintPreviewNumSheets() const {
496 auto [seqFrame, numSheets] = GetSeqFrameAndCountSheets();
497 Unused << seqFrame;
498 return numSheets;
501 //-----------------------------------------------------------------
502 //-- Section: Pre-Reflow Methods
503 //-----------------------------------------------------------------
505 // static
506 void nsPrintJob::GetDisplayTitleAndURL(Document& aDoc,
507 nsIPrintSettings* aSettings,
508 DocTitleDefault aTitleDefault,
509 nsAString& aTitle, nsAString& aURLStr) {
510 aTitle.Truncate();
511 aURLStr.Truncate();
513 if (aSettings) {
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) {
523 aTitle = aURLStr;
524 } else {
525 nsCOMPtr<nsIStringBundle> brandBundle;
526 nsCOMPtr<nsIStringBundleService> svc =
527 mozilla::components::StringBundle::Service();
528 if (svc) {
529 svc->CreateBundle("chrome://branding/locale/brand.properties",
530 getter_AddRefs(brandBundle));
531 if (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();
544 if (!url) {
545 return;
548 nsCOMPtr<nsIURI> exposableURI = net::nsIOService::CreateExposableURI(url);
549 nsAutoCString urlCStr;
550 nsresult rv = exposableURI->GetSpec(urlCStr);
551 if (NS_FAILED(rv)) {
552 return;
555 nsCOMPtr<nsITextToSubURI> textToSubURI =
556 do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
557 if (NS_FAILED(rv)) {
558 return;
561 textToSubURI->UnEscapeURIForUI(urlCStr, aURLStr);
565 //---------------------------------------------------------------------
566 nsresult nsPrintJob::DocumentReadyForPrinting() {
567 // Send the document to the printer...
568 nsresult rv = SetupToPrintContent();
569 if (NS_FAILED(rv)) {
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);
574 return 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);
587 /* cleanup... */
588 if (mPagePrintTimer) {
589 mPagePrintTimer->Stop();
590 DisconnectPagePrintTimer();
593 if (aIsPrinting) {
594 SetIsPrinting(false);
595 } else {
596 SetIsPrintPreview(false);
597 mIsCreatingPrintPreview = false;
600 /* cleanup done, let's fire-up an error dialog to notify the user
601 * what went wrong...
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();
612 return aResult;
615 //---------------------------------------------------------------------
616 void nsPrintJob::FirePrintingErrorEvent(nsresult aPrintError) {
617 if (mPrintPreviewCallback) {
618 // signal error
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)) {
626 return;
629 const RefPtr<Document> doc = viewer->GetDocument();
630 const RefPtr<CustomEvent> event = NS_NewDOMCustomEvent(doc, nullptr, nullptr);
632 MOZ_ASSERT(event);
634 AutoJSAPI jsapi;
635 if (!jsapi.Init(event->GetParentObject())) {
636 return;
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.
650 if (mPrt) {
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;
675 #endif
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) {
683 continue;
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 "
692 "print object "
693 "has been marked as \"print the document\"");
695 UpdateZoomRatio(po);
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;
720 if (po->mParent) {
721 nsSize adjSize;
722 bool doReturn;
723 nsresult rv = SetRootView(po, doReturn, documentIsTopLevel, adjSize);
725 MOZ_ASSERT(!documentIsTopLevel, "How could this happen?");
727 if (NS_FAILED(rv) || doReturn) {
728 return rv;
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);
747 return NS_OK;
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.
777 MOZ_ASSERT(
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
791 // everything.
792 if (mDidLoadDataForPrinting) {
793 nsresult rv = ReconstructAndReflow();
794 if (NS_WARN_IF(NS_FAILED(rv))) {
795 return 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
806 // is required.
807 if (mShrinkToFit) {
808 mShrinkToFitFactor = mPrintObject->mShrinkRatio;
810 if (mShrinkToFitFactor < 0.998f) {
811 nsresult rv = ReconstructAndReflow();
812 if (NS_WARN_IF(NS_FAILED(rv))) {
813 return 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;
825 PR_PL(
826 ("*******************************************************************"
827 "*******\n"));
828 PR_PL(("STF Ratio is: %8.5f Effective Ratio: %8.5f Diff: %8.5f\n",
829 mShrinkToFitFactor, calcRatio, mShrinkToFitFactor - calcRatio));
830 PR_PL(
831 ("*******************************************************************"
832 "*******\n"));
836 // If the frames got reconstructed and reflowed the number of pages might
837 // has changed.
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------------------------------------------"));
848 PR_PL(("\n"));
849 PR_PL(("-------------------------------------------------------\n"));
850 PR_PL(("\n"));
852 CalcNumPrintablePages(mNumPrintablePages);
854 PR_PL(("--- Printing %d pages\n", mNumPrintablePages));
855 DUMP_DOC_TREELAYOUT;
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,
878 docURLStr);
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]));
890 nsresult rv = NS_OK;
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,
897 endPage);
900 if (mIsCreatingPrintPreview) {
901 // Copy docTitleStr and docURLStr to the pageSequenceFrame, to be displayed
902 // in the header
903 nsPageSequenceFrame* seqFrame =
904 mPrintObject->mPresShell->GetPageSequenceFrame();
905 if (seqFrame) {
906 seqFrame->StartPrint(mPrintObject->mPresContext, mPrintSettings,
907 docTitleStr, docURLStr);
911 PR_PL(("****************** Begin Document ************************\n"));
913 if (NS_FAILED(rv)) {
914 NS_WARNING_ASSERTION(rv == NS_ERROR_ABORT,
915 "Failed to begin document for printing");
916 return rv;
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
934 return rv;
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
944 // document
945 if (aPO->mParent && aPO->mParent->mPresShell) {
946 nsIFrame* frame =
947 aPO->mContent ? aPO->mContent->GetPrimaryFrame() : nullptr;
948 if (!frame || !frame->StyleVisibility()->IsVisible()) {
949 aPO->EnablePrinting(false);
950 aPO->mInvisible = true;
951 return NS_OK;
955 UpdateZoomRatio(aPO.get());
957 // Reflow the PO
958 MOZ_TRY(ReflowPrintObject(aPO));
960 for (const UniquePtr<nsPrintObject>& kid : aPO->mKids) {
961 MOZ_TRY(ReflowDocList(kid));
963 return NS_OK;
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);
1005 return NS_OK;
1008 bool nsPrintJob::ShouldResumePrint() const {
1009 if (mDoingInitialReflow) {
1010 return false;
1012 Document* doc = mPrintObject->mDocument;
1013 MOZ_ASSERT(doc);
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);
1020 return !pending;
1023 nsresult nsPrintJob::MaybeResumePrintAfterResourcesLoaded(
1024 bool aCleanupOnError) {
1025 if (!ShouldResumePrint()) {
1026 mDidLoadDataForPrinting = true;
1027 return NS_OK;
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));
1046 nsresult rv;
1047 if (mIsDoingPrinting) {
1048 rv = DocumentReadyForPrinting();
1049 } else {
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);
1060 return rv;
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);
1073 return NS_OK;
1076 NS_IMETHODIMP
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(...)");
1082 return NS_OK;
1085 NS_IMETHODIMP
1086 nsPrintJob::OnLocationChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
1087 nsIURI* aLocation, uint32_t aFlags) {
1088 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
1089 return NS_OK;
1092 NS_IMETHODIMP
1093 nsPrintJob::OnStatusChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
1094 nsresult aStatus, const char16_t* aMessage) {
1095 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
1096 return NS_OK;
1099 NS_IMETHODIMP
1100 nsPrintJob::OnSecurityChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
1101 uint32_t aState) {
1102 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
1103 return NS_OK;
1106 NS_IMETHODIMP
1107 nsPrintJob::OnContentBlockingEvent(nsIWebProgress* aWebProgress,
1108 nsIRequest* aRequest, uint32_t aEvent) {
1109 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
1110 return NS_OK;
1113 //-------------------------------------------------------
1115 void nsPrintJob::UpdateZoomRatio(nsPrintObject* aPO) {
1116 if (!aPO->mParent) {
1117 if (mShrinkToFit) {
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;
1125 } else {
1126 double scaling;
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.
1146 if (selectionPS) {
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,
1155 IgnoreErrors());
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
1163 // the furthest
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);
1181 return NS_OK;
1184 nsView* nsPrintJob::GetParentViewForRoot() {
1185 if (mIsCreatingPrintPreview) {
1186 if (nsCOMPtr<nsIDocumentViewer> viewer =
1187 do_QueryInterface(mDocViewerPrint)) {
1188 return viewer->FindContainerView();
1191 return nullptr;
1194 nsresult nsPrintJob::SetRootView(nsPrintObject* aPO, bool& doReturn,
1195 bool& documentIsTopLevel, nsSize& adjSize) {
1196 bool canCreateScrollbars = true;
1198 nsView* rootView;
1199 nsView* parentView = nullptr;
1201 doReturn = false;
1203 if (aPO->mParent && aPO->mParent->PrintingIsEnabled()) {
1204 nsIFrame* frame =
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
1208 if (!frame) {
1209 aPO->EnablePrinting(false);
1210 doReturn = true;
1211 return NS_OK;
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
1216 // subdocuments.
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);
1227 parentView = view;
1228 canCreateScrollbars = false;
1230 } else {
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);
1244 } else {
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);
1258 return NS_OK;
1261 // Reflow a nsPrintObject
1262 nsresult nsPrintJob::ReflowPrintObject(const UniquePtr<nsPrintObject>& aPO) {
1263 NS_ENSURE_STATE(aPO);
1265 if (!aPO->PrintingIsEnabled()) {
1266 return NS_OK;
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;
1296 nsSize adjSize;
1298 nsresult rv = SetRootView(aPO.get(), doReturn, documentIsTopLevel, adjSize);
1300 if (NS_FAILED(rv) || doReturn) {
1301 return rv;
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
1315 // first reflow.
1316 aPO->mPresContext->SetPageSize(pageSize);
1318 int32_t p2a = aPO->mPresContext->DeviceContext()->AppUnitsPerDevPixel();
1319 if (documentIsTopLevel && mIsCreatingPrintPreview) {
1320 if (nsCOMPtr<nsIDocumentViewer> viewer =
1321 do_QueryInterface(mDocViewerPrint)) {
1322 // If we're print-previewing and the top level document, use the bounds
1323 // from our doc viewer. Page bounds is not what we want.
1324 nsIntRect bounds;
1325 viewer->GetBounds(bounds);
1326 adjSize = nsSize(bounds.width * p2a, bounds.height * p2a);
1329 aPO->mPresContext->SetIsRootPaginatedDocument(documentIsTopLevel);
1330 aPO->mPresContext->SetVisibleArea(nsRect(nsPoint(), adjSize));
1331 aPO->mPresContext->SetPageScale(aPO->mZoomRatio);
1332 // Calculate scale factor from printer to screen
1333 float printDPI = float(AppUnitsPerCSSInch()) / float(p2a);
1334 aPO->mPresContext->SetPrintPreviewScale(mScreenDPI / printDPI);
1336 // Do CreatePresShell() after we setup the page size, the visible area, and
1337 // the flag |mIsRootPaginatedDocument|, to make sure we can resolve the
1338 // correct viewport size for the print preview page when notifying the media
1339 // feature values changed. See au_viewport_size_for_viewport_unit_resolution()
1340 // in media_queries.rs for more details.
1341 RefPtr<Document> doc = aPO->mDocument;
1342 RefPtr<nsPresContext> presContext = aPO->mPresContext;
1343 RefPtr<nsViewManager> viewManager = aPO->mViewManager;
1345 aPO->mPresShell = doc->CreatePresShell(presContext, viewManager);
1346 if (!aPO->mPresShell) {
1347 return NS_ERROR_FAILURE;
1350 // If we're printing selection then remove the nonselected nodes from our
1351 // cloned document.
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();
1361 PR_PL(
1362 ("In DV::ReflowPrintObject PO: %p pS: %p (%9s) Setting page size w,h to "
1363 "%d,%d\n",
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);
1409 break;
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);
1415 break;
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);
1432 char filename[256];
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");
1436 if (fd) {
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();
1448 if (v) {
1449 v->List(fd);
1450 } else {
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");
1458 fclose(fd);
1461 #endif
1463 return NS_OK;
1466 //-------------------------------------------------------
1467 // Figure out how many documents and how many total pages we are printing
1468 void nsPrintJob::CalcNumPrintablePages(int32_t& aNumPages) {
1469 aNumPages = 0;
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
1475 // mPresContext)
1476 if (po->mPresContext && po->mPresContext->IsRootPaginatedDocument()) {
1477 nsPageSequenceFrame* seqFrame = po->mPresShell->GetPageSequenceFrame();
1478 if (seqFrame) {
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!");
1498 aStatus = NS_OK;
1500 if (!aPO->mHasBeenPrinted && aPO->PrintingIsEnabled()) {
1501 aStatus = DoPrint(aPO);
1502 return true;
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)) {
1513 return true;
1517 return false;
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();
1533 private:
1534 struct Position {
1535 nsINode* mNode;
1536 uint32_t mOffset;
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,
1565 IgnoreErrors());
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()});
1575 } else {
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();
1585 auto& start =
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());
1601 SelectRange(range);
1603 start = aEnd;
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());
1626 SelectRange(range);
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(
1639 Document& aDoc) {
1640 MOZ_ASSERT(aDoc.IsStaticDocument());
1641 const auto* printRanges = static_cast<nsTArray<RefPtr<nsRange>>*>(
1642 aDoc.GetProperty(nsGkAtoms::printselectionranges));
1643 if (!printRanges) {
1644 return NS_OK;
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();
1656 return NS_OK;
1659 //-------------------------------------------------------
1660 nsresult nsPrintJob::DoPrint(const UniquePtr<nsPrintObject>& aPO) {
1661 PR_PL(("\n"));
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);
1698 #endif
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);
1711 if (!seqFrame) {
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);
1722 int16_t sizeUnit;
1723 settings->GetPaperSizeUnit(&sizeUnit);
1724 switch (sizeUnit) {
1725 case nsIPrintSettings::kPaperSizeInches:
1726 paperWidth *= 72.0;
1727 paperHeight *= 72.0;
1728 break;
1729 case nsIPrintSettings::kPaperSizeMillimeters:
1730 paperWidth *= 72.0 / 25.4;
1731 paperHeight *= 72.0 / 25.4;
1732 break;
1733 default:
1734 MOZ_ASSERT_UNREACHABLE("unknown paper size unit");
1735 break;
1737 if (paperWidth > paperHeight) {
1738 std::swap(paperWidth, paperHeight);
1740 // Use the paper size to build a Telemetry Scalar key.
1741 nsString key;
1742 key.AppendInt(int32_t(NS_round(paperWidth)));
1743 key.Append(u"x");
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);
1756 return NS_OK;
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|.
1776 bool done = false;
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;
1788 done = true;
1790 return done;
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) {
1814 return true;
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,
1822 numSheets));
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.
1831 return true;
1834 // Print the sheet
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;
1847 return true;
1850 pageSeqFrame->DoPageEnd();
1852 // If we just printed the final sheet (the one with index "numSheets-1"),
1853 // then we're done!
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;
1877 if (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;
1911 nsresult rv;
1912 bool didPrint = PrintDocContent(mPrintObject, rv);
1913 if (NS_SUCCEEDED(rv) && didPrint) {
1914 PR_PL(
1915 ("****** In DV::DonePrintingSheets PO: %p (%s) didPrint:%s (Not Done "
1916 "Printing)\n",
1917 aPO, LoggableTypeOfPO(aPO), PRT_YESNO(didPrint)));
1918 return false;
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();
1935 return true;
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;
1950 PR_PL(("\n"));
1951 PR_PL(("********* nsPrintJob::EnablePOsForPrinting *********\n"));
1953 if (!mPrintSettings->GetPrintSelectionOnly()) {
1954 mPrintObject->EnablePrinting(true);
1955 return NS_OK;
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);
1966 } else {
1967 // Otherwise, only enable nsPrintObjects that have a selection.
1968 mSelectionRoot->EnablePrintingSelectionOnly();
1970 return NS_OK;
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) {
1991 return rv;
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
2002 // instance.
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();
2013 return rv;
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()));
2024 pageHeight =
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
2042 return NS_OK;
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);
2061 mPagePrintTimer =
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 {
2077 public:
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();
2086 return NS_OK;
2089 private:
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;
2130 HANDLE find_handle;
2132 char path[MAX_PATH];
2134 strcpy(path, aDir);
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) {
2146 do {
2147 if (ISDIR(data_ptr) && (stricmp(MY_FILENAME(data_ptr), ".")) &&
2148 (stricmp(MY_FILENAME(data_ptr), ".."))) {
2149 // skip
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);
2157 remove(fileName);
2160 } while (MY_FINDNEXT(find_handle, &data_ptr));
2161 MY_FINDCLOSE(find_handle);
2163 return TRUE;
2165 #endif
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();
2178 if (frame) {
2179 frame->List(out, aPrefix);
2184 /** ---------------------------------------------------
2185 * Dumps Frames for Printing
2187 static void DumpFrames(FILE* out, nsPresContext* aPresContext,
2188 gfxContext* aRendContext, nsIFrame* aFrame,
2189 int32_t aLevel) {
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++) {
2198 fprintf(out, " ");
2200 nsAutoString tmp;
2201 child->GetFrameName(tmp);
2202 fputs(NS_LossyConvertUTF16toASCII(tmp).get(), out);
2203 bool isSelected;
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());
2209 fprintf(out, "\n");
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();
2227 if (vm) {
2228 nsView* root = vm->GetRootView();
2229 if (root) {
2230 root->List(out);
2233 } else {
2234 fputs("null pres shell\n", out);
2237 // dump the views of the sub documents
2238 int32_t i, n;
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)) {
2256 return;
2259 if (aPresContext == nullptr || aDC == nullptr) {
2260 return;
2263 # ifdef NS_PRINT_PREVIEW
2264 if (aPresContext->Type() == nsPresContext::eContext_PrintPreview) {
2265 return;
2267 # endif
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
2273 char filename[256];
2274 sprintf(filename, "print_dump_layout_%d.txt", gDumpLOFileNameCnt++);
2275 FILE* fd = aFD ? aFD : fopen(filename, "w");
2276 if (fd) {
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();
2288 if (v) {
2289 v->List(fd);
2290 } else {
2291 printf("View is null!\n");
2293 if (aDocShell) {
2294 fprintf(fd, "--------------- All Views ----------------\n");
2295 DumpViews(aDocShell, fd);
2296 fprintf(fd, "---------------------------------------\n\n");
2298 if (aFD == nullptr) {
2299 fclose(fd);
2304 //-------------------------------------------------------------
2305 static void DumpPrintObjectsList(const nsTArray<nsPrintObject*>& aDocList) {
2306 if (!MOZ_LOG_TEST(gPrintingLog, DUMP_LAYOUT_LEVEL)) {
2307 return;
2310 PR_PL(("Doc List\n***************************************************\n"));
2311 PR_PL(
2312 ("T P A H PO DocShell Seq Page Root Page# "
2313 "Rect\n"));
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);
2321 if (sqf) {
2322 break;
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)) {
2337 return;
2340 NS_ASSERTION(aPO, "Pointer is null!");
2342 FILE* fd = aFD ? aFD : stdout;
2343 if (aLevel == 0) {
2344 fprintf(fd,
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,
2369 FILE* aFD) {
2370 if (!MOZ_LOG_TEST(gPrintingLog, DUMP_LAYOUT_LEVEL)) {
2371 return;
2374 NS_ASSERTION(aPO, "Pointer is null!");
2375 NS_ASSERTION(aDC, "Pointer is null!");
2377 FILE* fd = nullptr;
2378 if (aLevel == 0) {
2379 fd = fopen("tree_layout.txt", "w");
2380 fprintf(fd,
2381 "DocTree\n***************************************************\n");
2382 fprintf(fd, "***************************************************\n");
2383 fprintf(fd, "T PO DocShell Seq Page Page# Rect\n");
2384 } else {
2385 fd = aFD;
2387 if (fd) {
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) {
2410 fclose(fd);
2414 //-------------------------------------------------------------
2415 static void DumpPrintObjectsListStart(
2416 const char* aStr, const nsTArray<nsPrintObject*>& aDocList) {
2417 if (!MOZ_LOG_TEST(gPrintingLog, DUMP_LAYOUT_LEVEL)) {
2418 return;
2421 NS_ASSERTION(aStr, "Pointer is null!");
2423 PR_PL(("%s\n", aStr));
2424 DumpPrintObjectsList(aDocList);
2427 #endif
2429 //---------------------------------------------------------------
2430 //---------------------------------------------------------------
2431 //-- End of debug helper routines
2432 //---------------------------------------------------------------