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