Bumping manifests a=b2g-bump
[gecko.git] / layout / printing / nsPrintEngine.cpp
blob463efc0317371fb3001cf37dcd71a22e042ac1a2
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsPrintEngine.h"
8 #include "nsIStringBundle.h"
9 #include "nsReadableUtils.h"
10 #include "nsCRT.h"
12 #include "mozilla/AsyncEventDispatcher.h"
13 #include "mozilla/dom/Selection.h"
14 #include "nsIScriptGlobalObject.h"
15 #include "nsPIDOMWindow.h"
16 #include "nsIDocShell.h"
17 #include "nsIFrame.h"
18 #include "nsIURI.h"
19 #include "nsITextToSubURI.h"
20 #include "nsError.h"
22 #include "nsView.h"
23 #include <algorithm>
25 // Print Options
26 #include "nsIPrintSettings.h"
27 #include "nsIPrintSettingsService.h"
28 #include "nsIPrintOptions.h"
29 #include "nsIPrintSession.h"
30 #include "nsGfxCIID.h"
31 #include "nsIServiceManager.h"
32 #include "nsGkAtoms.h"
33 #include "nsXPCOM.h"
34 #include "nsISupportsPrimitives.h"
36 static const char sPrintSettingsServiceContractID[] = "@mozilla.org/gfx/printsettings-service;1";
38 // Printing Events
39 #include "nsPrintPreviewListener.h"
40 #include "nsThreadUtils.h"
42 // Printing
43 #include "nsIWebBrowserPrint.h"
44 #include "nsIDOMHTMLFrameElement.h"
45 #include "nsIDOMHTMLFrameSetElement.h"
46 #include "nsIDOMHTMLIFrameElement.h"
47 #include "nsIDOMHTMLObjectElement.h"
48 #include "nsIDOMHTMLEmbedElement.h"
50 // Print Preview
51 #include "imgIContainer.h" // image animation mode constants
52 #include "nsIWebBrowserPrint.h" // needed for PrintPreview Navigation constants
54 // Print Progress
55 #include "nsIPrintProgress.h"
56 #include "nsIPrintProgressParams.h"
57 #include "nsIObserver.h"
59 // Print error dialog
60 #include "nsIPrompt.h"
61 #include "nsIWindowWatcher.h"
63 // Printing Prompts
64 #include "nsIPrintingPromptService.h"
65 static const char kPrintingPromptService[] = "@mozilla.org/embedcomp/printingprompt-service;1";
67 // Printing Timer
68 #include "nsPagePrintTimer.h"
70 // FrameSet
71 #include "nsIDocument.h"
73 // Focus
74 #include "nsISelectionController.h"
76 // Misc
77 #include "nsISupportsUtils.h"
78 #include "nsIScriptContext.h"
79 #include "nsIDOMDocument.h"
80 #include "nsISelectionListener.h"
81 #include "nsISelectionPrivate.h"
82 #include "nsIDOMRange.h"
83 #include "nsContentCID.h"
84 #include "nsLayoutCID.h"
85 #include "nsContentUtils.h"
86 #include "nsIPresShell.h"
87 #include "nsLayoutUtils.h"
88 #include "mozilla/Preferences.h"
90 #include "nsWidgetsCID.h"
91 #include "nsIDeviceContextSpec.h"
92 #include "nsViewManager.h"
93 #include "nsView.h"
94 #include "nsRenderingContext.h"
96 #include "nsIPageSequenceFrame.h"
97 #include "nsIURL.h"
98 #include "nsIContentViewerEdit.h"
99 #include "nsIContentViewerFile.h"
100 #include "nsIInterfaceRequestor.h"
101 #include "nsIInterfaceRequestorUtils.h"
102 #include "nsIDocShellTreeOwner.h"
103 #include "nsIWebBrowserChrome.h"
104 #include "nsIBaseWindow.h"
105 #include "nsILayoutHistoryState.h"
106 #include "nsFrameManager.h"
107 #include "nsHTMLReflowState.h"
108 #include "nsIDOMHTMLAnchorElement.h"
109 #include "nsIDOMHTMLAreaElement.h"
110 #include "nsIDOMHTMLLinkElement.h"
111 #include "nsIDOMHTMLImageElement.h"
112 #include "nsIContentViewerContainer.h"
113 #include "nsIContentViewer.h"
114 #include "nsIDocumentViewerPrint.h"
116 #include "nsFocusManager.h"
117 #include "nsRange.h"
118 #include "nsCDefaultURIFixup.h"
119 #include "nsIURIFixup.h"
120 #include "mozilla/dom/Element.h"
121 #include "nsContentList.h"
122 #include "nsIChannel.h"
123 #include "xpcpublic.h"
125 using namespace mozilla;
126 using namespace mozilla::dom;
128 //-----------------------------------------------------
129 // PR LOGGING
130 #ifdef MOZ_LOGGING
131 #define FORCE_PR_LOG /* Allow logging in the release build */
132 #endif
134 #include "prlog.h"
136 #ifdef PR_LOGGING
138 #ifdef DEBUG
139 // PR_LOGGING is force to always be on (even in release builds)
140 // but we only want some of it on,
141 //#define EXTENDED_DEBUG_PRINTING
142 #endif
144 #define DUMP_LAYOUT_LEVEL 9 // this turns on the dumping of each doucment's layout info
146 #ifndef PR_PL
147 static PRLogModuleInfo *
148 GetPrintingLog()
150 static PRLogModuleInfo *sLog;
151 if (!sLog)
152 sLog = PR_NewLogModule("printing");
153 return sLog;
155 #define PR_PL(_p1) PR_LOG(GetPrintingLog(), PR_LOG_DEBUG, _p1);
156 #endif
158 #ifdef EXTENDED_DEBUG_PRINTING
159 static uint32_t gDumpFileNameCnt = 0;
160 static uint32_t gDumpLOFileNameCnt = 0;
161 #endif
163 #define PRT_YESNO(_p) ((_p)?"YES":"NO")
164 static const char * gFrameTypesStr[] = {"eDoc", "eFrame", "eIFrame", "eFrameSet"};
165 static const char * gPrintFrameTypeStr[] = {"kNoFrames", "kFramesAsIs", "kSelectedFrame", "kEachFrameSep"};
166 static const char * gFrameHowToEnableStr[] = {"kFrameEnableNone", "kFrameEnableAll", "kFrameEnableAsIsAndEach"};
167 static const char * gPrintRangeStr[] = {"kRangeAllPages", "kRangeSpecifiedPageRange", "kRangeSelection", "kRangeFocusFrame"};
168 #else
169 #define PRT_YESNO(_p)
170 #define PR_PL(_p1)
171 #endif
173 #ifdef EXTENDED_DEBUG_PRINTING
174 // Forward Declarations
175 static void DumpPrintObjectsListStart(const char * aStr, nsTArray<nsPrintObject*> * aDocList);
176 static void DumpPrintObjectsTree(nsPrintObject * aPO, int aLevel= 0, FILE* aFD = nullptr);
177 static void DumpPrintObjectsTreeLayout(nsPrintObject * aPO,nsDeviceContext * aDC, int aLevel= 0, FILE * aFD = nullptr);
179 #define DUMP_DOC_LIST(_title) DumpPrintObjectsListStart((_title), mPrt->mPrintDocList);
180 #define DUMP_DOC_TREE DumpPrintObjectsTree(mPrt->mPrintObject);
181 #define DUMP_DOC_TREELAYOUT DumpPrintObjectsTreeLayout(mPrt->mPrintObject, mPrt->mPrintDC);
182 #else
183 #define DUMP_DOC_LIST(_title)
184 #define DUMP_DOC_TREE
185 #define DUMP_DOC_TREELAYOUT
186 #endif
188 class nsScriptSuppressor
190 public:
191 explicit nsScriptSuppressor(nsPrintEngine* aPrintEngine)
192 : mPrintEngine(aPrintEngine), mSuppressed(false) {}
194 ~nsScriptSuppressor() { Unsuppress(); }
196 void Suppress()
198 if (mPrintEngine) {
199 mSuppressed = true;
200 mPrintEngine->TurnScriptingOn(false);
204 void Unsuppress()
206 if (mPrintEngine && mSuppressed) {
207 mPrintEngine->TurnScriptingOn(true);
209 mSuppressed = false;
212 void Disconnect() { mPrintEngine = nullptr; }
213 protected:
214 nsRefPtr<nsPrintEngine> mPrintEngine;
215 bool mSuppressed;
218 NS_IMPL_ISUPPORTS(nsPrintEngine, nsIWebProgressListener,
219 nsISupportsWeakReference, nsIObserver)
221 //---------------------------------------------------
222 //-- nsPrintEngine Class Impl
223 //---------------------------------------------------
224 nsPrintEngine::nsPrintEngine() :
225 mIsCreatingPrintPreview(false),
226 mIsDoingPrinting(false),
227 mIsDoingPrintPreview(false),
228 mProgressDialogIsShown(false),
229 mScreenDPI(115.0f),
230 mPrt(nullptr),
231 mPagePrintTimer(nullptr),
232 mPageSeqFrame(nullptr),
233 mPrtPreview(nullptr),
234 mOldPrtPreview(nullptr),
235 mDebugFile(nullptr),
236 mLoadCounter(0),
237 mDidLoadDataForPrinting(false),
238 mIsDestroying(false),
239 mDisallowSelectionPrint(false),
240 mNoMarginBoxes(false)
244 //-------------------------------------------------------
245 nsPrintEngine::~nsPrintEngine()
247 Destroy(); // for insurance
250 //-------------------------------------------------------
251 void nsPrintEngine::Destroy()
253 if (mIsDestroying) {
254 return;
256 mIsDestroying = true;
258 if (mPrt) {
259 delete mPrt;
260 mPrt = nullptr;
263 #ifdef NS_PRINT_PREVIEW
264 if (mPrtPreview) {
265 delete mPrtPreview;
266 mPrtPreview = nullptr;
269 // This is insruance
270 if (mOldPrtPreview) {
271 delete mOldPrtPreview;
272 mOldPrtPreview = nullptr;
275 #endif
276 mDocViewerPrint = nullptr;
279 //-------------------------------------------------------
280 void nsPrintEngine::DestroyPrintingData()
282 if (mPrt) {
283 nsPrintData* data = mPrt;
284 mPrt = nullptr;
285 delete data;
289 //---------------------------------------------------------------------------------
290 //-- Section: Methods needed by the DocViewer
291 //---------------------------------------------------------------------------------
293 //--------------------------------------------------------
294 nsresult nsPrintEngine::Initialize(nsIDocumentViewerPrint* aDocViewerPrint,
295 nsIDocShell* aContainer,
296 nsIDocument* aDocument,
297 float aScreenDPI,
298 FILE* aDebugFile)
300 NS_ENSURE_ARG_POINTER(aDocViewerPrint);
301 NS_ENSURE_ARG_POINTER(aContainer);
302 NS_ENSURE_ARG_POINTER(aDocument);
304 mDocViewerPrint = aDocViewerPrint;
305 mContainer = do_GetWeakReference(aContainer);
306 mDocument = aDocument;
307 mScreenDPI = aScreenDPI;
309 mDebugFile = aDebugFile; // ok to be nullptr
311 return NS_OK;
314 //-------------------------------------------------------
315 bool
316 nsPrintEngine::CheckBeforeDestroy()
318 if (mPrt && mPrt->mPreparingForPrint) {
319 mPrt->mDocWasToBeDestroyed = true;
320 return true;
322 return false;
325 //-------------------------------------------------------
326 nsresult
327 nsPrintEngine::Cancelled()
329 if (mPrt && mPrt->mPrintSettings) {
330 return mPrt->mPrintSettings->SetIsCancelled(true);
332 return NS_ERROR_FAILURE;
335 //-------------------------------------------------------
336 // Install our event listeners on the document to prevent
337 // some events from being processed while in PrintPreview
339 // No return code - if this fails, there isn't much we can do
340 void
341 nsPrintEngine::InstallPrintPreviewListener()
343 if (!mPrt->mPPEventListeners) {
344 nsCOMPtr<nsIDocShell> docShell = do_QueryReferent(mContainer);
345 if (!docShell) {
346 return;
349 nsCOMPtr<nsPIDOMWindow> win(docShell->GetWindow());
350 if (win) {
351 nsCOMPtr<EventTarget> target = win->GetFrameElementInternal();
352 mPrt->mPPEventListeners = new nsPrintPreviewListener(target);
353 mPrt->mPPEventListeners->AddListeners();
358 //----------------------------------------------------------------------
359 nsresult
360 nsPrintEngine::GetSeqFrameAndCountPagesInternal(nsPrintObject* aPO,
361 nsIFrame*& aSeqFrame,
362 int32_t& aCount)
364 NS_ENSURE_ARG_POINTER(aPO);
366 // Finds the SimplePageSequencer frame
367 nsIPageSequenceFrame* seqFrame = aPO->mPresShell->GetPageSequenceFrame();
368 aSeqFrame = do_QueryFrame(seqFrame);
369 if (!aSeqFrame) {
370 return NS_ERROR_FAILURE;
373 // first count the total number of pages
374 aCount = 0;
375 nsIFrame* pageFrame = aSeqFrame->GetFirstPrincipalChild();
376 while (pageFrame != nullptr) {
377 aCount++;
378 pageFrame = pageFrame->GetNextSibling();
381 return NS_OK;
385 //-----------------------------------------------------------------
386 nsresult nsPrintEngine::GetSeqFrameAndCountPages(nsIFrame*& aSeqFrame, int32_t& aCount)
388 NS_ASSERTION(mPrtPreview, "mPrtPreview can't be null!");
389 return GetSeqFrameAndCountPagesInternal(mPrtPreview->mPrintObject, aSeqFrame, aCount);
391 //---------------------------------------------------------------------------------
392 //-- Done: Methods needed by the DocViewer
393 //---------------------------------------------------------------------------------
396 //---------------------------------------------------------------------------------
397 //-- Section: nsIWebBrowserPrint
398 //---------------------------------------------------------------------------------
400 // Foward decl for Debug Helper Functions
401 #ifdef EXTENDED_DEBUG_PRINTING
402 static int RemoveFilesInDir(const char * aDir);
403 static void GetDocTitleAndURL(nsPrintObject* aPO, char *& aDocStr, char *& aURLStr);
404 static void DumpPrintObjectsTree(nsPrintObject * aPO, int aLevel, FILE* aFD);
405 static void DumpPrintObjectsList(nsTArray<nsPrintObject*> * aDocList);
406 static void RootFrameList(nsPresContext* aPresContext, FILE* out, int32_t aIndent);
407 static void DumpViews(nsIDocShell* aDocShell, FILE* out);
408 static void DumpLayoutData(char* aTitleStr, char* aURLStr,
409 nsPresContext* aPresContext,
410 nsDeviceContext * aDC, nsIFrame * aRootFrame,
411 nsIDocShell * aDocShell, FILE* aFD);
412 #endif
414 //--------------------------------------------------------------------------------
416 nsresult
417 nsPrintEngine::CommonPrint(bool aIsPrintPreview,
418 nsIPrintSettings* aPrintSettings,
419 nsIWebProgressListener* aWebProgressListener,
420 nsIDOMDocument* aDoc) {
421 nsRefPtr<nsPrintEngine> kungfuDeathGrip = this;
422 nsresult rv = DoCommonPrint(aIsPrintPreview, aPrintSettings,
423 aWebProgressListener, aDoc);
424 if (NS_FAILED(rv)) {
425 if (aIsPrintPreview) {
426 SetIsCreatingPrintPreview(false);
427 SetIsPrintPreview(false);
428 } else {
429 SetIsPrinting(false);
431 if (mProgressDialogIsShown)
432 CloseProgressDialog(aWebProgressListener);
433 if (rv != NS_ERROR_ABORT && rv != NS_ERROR_OUT_OF_MEMORY)
434 ShowPrintErrorDialog(rv, !aIsPrintPreview);
435 delete mPrt;
436 mPrt = nullptr;
439 return rv;
442 nsresult
443 nsPrintEngine::DoCommonPrint(bool aIsPrintPreview,
444 nsIPrintSettings* aPrintSettings,
445 nsIWebProgressListener* aWebProgressListener,
446 nsIDOMDocument* aDoc)
448 nsresult rv;
450 if (aIsPrintPreview) {
451 // The WebProgressListener can be QI'ed to nsIPrintingPromptService
452 // then that means the progress dialog is already being shown.
453 nsCOMPtr<nsIPrintingPromptService> pps(do_QueryInterface(aWebProgressListener));
454 mProgressDialogIsShown = pps != nullptr;
456 if (mIsDoingPrintPreview) {
457 mOldPrtPreview = mPrtPreview;
458 mPrtPreview = nullptr;
460 } else {
461 mProgressDialogIsShown = false;
464 mPrt = new nsPrintData(aIsPrintPreview ? nsPrintData::eIsPrintPreview :
465 nsPrintData::eIsPrinting);
466 NS_ENSURE_TRUE(mPrt, NS_ERROR_OUT_OF_MEMORY);
468 // if they don't pass in a PrintSettings, then get the Global PS
469 mPrt->mPrintSettings = aPrintSettings;
470 if (!mPrt->mPrintSettings) {
471 rv = GetGlobalPrintSettings(getter_AddRefs(mPrt->mPrintSettings));
472 NS_ENSURE_SUCCESS(rv, rv);
475 rv = CheckForPrinters(mPrt->mPrintSettings);
476 NS_ENSURE_SUCCESS(rv, rv);
478 mPrt->mPrintSettings->SetIsCancelled(false);
479 mPrt->mPrintSettings->GetShrinkToFit(&mPrt->mShrinkToFit);
481 // In the case the margin boxes are not printed store the print settings for
482 // the footer/header to be used as default print setting for follow up prints.
483 mPrt->mPrintSettings->SetPersistMarginBoxSettings(!mNoMarginBoxes);
485 if (mNoMarginBoxes) {
486 // Set the footer/header to blank.
487 const char16_t* emptyString = EmptyString().get();
488 mPrt->mPrintSettings->SetHeaderStrLeft(emptyString);
489 mPrt->mPrintSettings->SetHeaderStrCenter(emptyString);
490 mPrt->mPrintSettings->SetHeaderStrRight(emptyString);
491 mPrt->mPrintSettings->SetFooterStrLeft(emptyString);
492 mPrt->mPrintSettings->SetFooterStrCenter(emptyString);
493 mPrt->mPrintSettings->SetFooterStrRight(emptyString);
496 if (aIsPrintPreview) {
497 SetIsCreatingPrintPreview(true);
498 SetIsPrintPreview(true);
499 nsCOMPtr<nsIContentViewer> viewer =
500 do_QueryInterface(mDocViewerPrint);
501 if (viewer) {
502 viewer->SetTextZoom(1.0f);
503 viewer->SetFullZoom(1.0f);
504 viewer->SetMinFontSize(0);
508 // Create a print session and let the print settings know about it.
509 // The print settings hold an nsWeakPtr to the session so it does not
510 // need to be cleared from the settings at the end of the job.
511 // XXX What lifetime does the printSession need to have?
512 nsCOMPtr<nsIPrintSession> printSession;
513 if (!aIsPrintPreview) {
514 printSession = do_CreateInstance("@mozilla.org/gfx/printsession;1", &rv);
515 NS_ENSURE_SUCCESS(rv, rv);
516 mPrt->mPrintSettings->SetPrintSession(printSession);
519 if (aWebProgressListener != nullptr) {
520 mPrt->mPrintProgressListeners.AppendObject(aWebProgressListener);
523 // Get the currently focused window and cache it
524 // because the Print Dialog will "steal" focus and later when you try
525 // to get the currently focused windows it will be nullptr
526 mPrt->mCurrentFocusWin = FindFocusedDOMWindow();
528 // Check to see if there is a "regular" selection
529 bool isSelection = IsThereARangeSelection(mPrt->mCurrentFocusWin);
531 // Get the docshell for this documentviewer
532 nsCOMPtr<nsIDocShell> webContainer(do_QueryReferent(mContainer, &rv));
533 NS_ENSURE_SUCCESS(rv, rv);
536 if (aIsPrintPreview) {
537 nsCOMPtr<nsIContentViewer> viewer;
538 webContainer->GetContentViewer(getter_AddRefs(viewer));
539 if (viewer && viewer->GetDocument() && viewer->GetDocument()->IsShowing()) {
540 viewer->GetDocument()->OnPageHide(false, nullptr);
544 nsAutoScriptBlocker scriptBlocker;
545 mPrt->mPrintObject = new nsPrintObject();
546 NS_ENSURE_TRUE(mPrt->mPrintObject, NS_ERROR_OUT_OF_MEMORY);
547 rv = mPrt->mPrintObject->Init(webContainer, aDoc, aIsPrintPreview);
548 NS_ENSURE_SUCCESS(rv, rv);
550 NS_ENSURE_TRUE(mPrt->mPrintDocList.AppendElement(mPrt->mPrintObject),
551 NS_ERROR_OUT_OF_MEMORY);
553 mPrt->mIsParentAFrameSet = IsParentAFrameSet(webContainer);
554 mPrt->mPrintObject->mFrameType = mPrt->mIsParentAFrameSet ? eFrameSet : eDoc;
556 // Build the "tree" of PrintObjects
557 BuildDocTree(mPrt->mPrintObject->mDocShell, &mPrt->mPrintDocList,
558 mPrt->mPrintObject);
561 if (!aIsPrintPreview) {
562 SetIsPrinting(true);
565 // XXX This isn't really correct...
566 if (!mPrt->mPrintObject->mDocument ||
567 !mPrt->mPrintObject->mDocument->GetRootElement())
568 return NS_ERROR_GFX_PRINTER_STARTDOC;
570 // Create the linkage from the sub-docs back to the content element
571 // in the parent document
572 MapContentToWebShells(mPrt->mPrintObject, mPrt->mPrintObject);
574 mPrt->mIsIFrameSelected = IsThereAnIFrameSelected(webContainer, mPrt->mCurrentFocusWin, mPrt->mIsParentAFrameSet);
576 // Setup print options for UI
577 if (mPrt->mIsParentAFrameSet) {
578 if (mPrt->mCurrentFocusWin) {
579 mPrt->mPrintSettings->SetHowToEnableFrameUI(nsIPrintSettings::kFrameEnableAll);
580 } else {
581 mPrt->mPrintSettings->SetHowToEnableFrameUI(nsIPrintSettings::kFrameEnableAsIsAndEach);
583 } else {
584 mPrt->mPrintSettings->SetHowToEnableFrameUI(nsIPrintSettings::kFrameEnableNone);
586 // Now determine how to set up the Frame print UI
587 mPrt->mPrintSettings->SetPrintOptions(nsIPrintSettings::kEnableSelectionRB,
588 isSelection || mPrt->mIsIFrameSelected);
590 nsCOMPtr<nsIDeviceContextSpec> devspec
591 (do_CreateInstance("@mozilla.org/gfx/devicecontextspec;1", &rv));
592 NS_ENSURE_SUCCESS(rv, rv);
594 nsScriptSuppressor scriptSuppressor(this);
595 if (!aIsPrintPreview) {
596 #ifdef DEBUG
597 mPrt->mDebugFilePtr = mDebugFile;
598 #endif
600 scriptSuppressor.Suppress();
601 bool printSilently;
602 mPrt->mPrintSettings->GetPrintSilent(&printSilently);
604 // Check prefs for a default setting as to whether we should print silently
605 printSilently =
606 Preferences::GetBool("print.always_print_silent", printSilently);
608 // Ask dialog to be Print Shown via the Plugable Printing Dialog Service
609 // This service is for the Print Dialog and the Print Progress Dialog
610 // If printing silently or you can't get the service continue on
611 if (!printSilently) {
612 nsCOMPtr<nsIPrintingPromptService> printPromptService(do_GetService(kPrintingPromptService));
613 if (printPromptService) {
614 nsIDOMWindow *domWin = mDocument->GetWindow();
615 NS_ENSURE_TRUE(domWin, NS_ERROR_FAILURE);
617 // Platforms not implementing a given dialog for the service may
618 // return NS_ERROR_NOT_IMPLEMENTED or an error code.
620 // NS_ERROR_NOT_IMPLEMENTED indicates they want default behavior
621 // Any other error code means we must bail out
623 nsCOMPtr<nsIWebBrowserPrint> wbp(do_QueryInterface(mDocViewerPrint));
624 rv = printPromptService->ShowPrintDialog(domWin, wbp,
625 mPrt->mPrintSettings);
627 // ShowPrintDialog triggers an event loop which means we can't assume
628 // that the state of this->{anything} matches the state we've checked
629 // above. Including that a given {thing} is non null.
630 if (!mPrt) {
631 return NS_ERROR_FAILURE;
634 if (NS_SUCCEEDED(rv)) {
635 // since we got the dialog and it worked then make sure we
636 // are telling GFX we want to print silent
637 printSilently = true;
639 if (mPrt->mPrintSettings) {
640 // The user might have changed shrink-to-fit in the print dialog, so update our copy of its state
641 mPrt->mPrintSettings->GetShrinkToFit(&mPrt->mShrinkToFit);
643 } else if (rv == NS_ERROR_NOT_IMPLEMENTED) {
644 // This means the Dialog service was there,
645 // but they choose not to implement this dialog and
646 // are looking for default behavior from the toolkit
647 rv = NS_OK;
649 } else {
650 // No dialog service available
651 rv = NS_ERROR_NOT_IMPLEMENTED;
653 } else {
654 // Call any code that requires a run of the event loop.
655 rv = mPrt->mPrintSettings->SetupSilentPrinting();
657 // Check explicitly for abort because it's expected
658 if (rv == NS_ERROR_ABORT)
659 return rv;
660 NS_ENSURE_SUCCESS(rv, rv);
663 rv = devspec->Init(nullptr, mPrt->mPrintSettings, aIsPrintPreview);
664 NS_ENSURE_SUCCESS(rv, rv);
666 mPrt->mPrintDC = new nsDeviceContext();
667 rv = mPrt->mPrintDC->InitForPrinting(devspec);
668 NS_ENSURE_SUCCESS(rv, rv);
670 if (aIsPrintPreview) {
671 mPrt->mPrintSettings->SetPrintFrameType(nsIPrintSettings::kFramesAsIs);
673 // override any UI that wants to PrintPreview any selection or page range
674 // we want to view every page in PrintPreview each time
675 mPrt->mPrintSettings->SetPrintRange(nsIPrintSettings::kRangeAllPages);
676 } else {
677 // Always check and set the print settings first and then fall back
678 // onto the PrintService if there isn't a PrintSettings
680 // Posiible Usage values:
681 // nsIPrintSettings::kUseInternalDefault
682 // nsIPrintSettings::kUseSettingWhenPossible
684 // NOTE: The consts are the same for PrintSettings and PrintSettings
685 int16_t printFrameTypeUsage = nsIPrintSettings::kUseSettingWhenPossible;
686 mPrt->mPrintSettings->GetPrintFrameTypeUsage(&printFrameTypeUsage);
688 // Ok, see if we are going to use our value and override the default
689 if (printFrameTypeUsage == nsIPrintSettings::kUseSettingWhenPossible) {
690 // Get the Print Options/Settings PrintFrameType to see what is preferred
691 int16_t printFrameType = nsIPrintSettings::kEachFrameSep;
692 mPrt->mPrintSettings->GetPrintFrameType(&printFrameType);
694 // Don't let anybody do something stupid like try to set it to
695 // kNoFrames when we are printing a FrameSet
696 if (printFrameType == nsIPrintSettings::kNoFrames) {
697 mPrt->mPrintFrameType = nsIPrintSettings::kEachFrameSep;
698 mPrt->mPrintSettings->SetPrintFrameType(mPrt->mPrintFrameType);
699 } else {
700 // First find out from the PrinService what options are available
701 // to us for Printing FrameSets
702 int16_t howToEnableFrameUI;
703 mPrt->mPrintSettings->GetHowToEnableFrameUI(&howToEnableFrameUI);
704 if (howToEnableFrameUI != nsIPrintSettings::kFrameEnableNone) {
705 switch (howToEnableFrameUI) {
706 case nsIPrintSettings::kFrameEnableAll:
707 mPrt->mPrintFrameType = printFrameType;
708 break;
710 case nsIPrintSettings::kFrameEnableAsIsAndEach:
711 if (printFrameType != nsIPrintSettings::kSelectedFrame) {
712 mPrt->mPrintFrameType = printFrameType;
713 } else { // revert back to a good value
714 mPrt->mPrintFrameType = nsIPrintSettings::kEachFrameSep;
716 break;
717 } // switch
718 mPrt->mPrintSettings->SetPrintFrameType(mPrt->mPrintFrameType);
721 } else {
722 mPrt->mPrintSettings->GetPrintFrameType(&mPrt->mPrintFrameType);
726 if (mPrt->mPrintFrameType == nsIPrintSettings::kEachFrameSep) {
727 CheckForChildFrameSets(mPrt->mPrintObject);
730 if (NS_FAILED(EnablePOsForPrinting())) {
731 return NS_ERROR_FAILURE;
734 // Attach progressListener to catch network requests.
735 nsCOMPtr<nsIWebProgress> webProgress = do_QueryInterface(mPrt->mPrintObject->mDocShell);
736 webProgress->AddProgressListener(
737 static_cast<nsIWebProgressListener*>(this),
738 nsIWebProgress::NOTIFY_STATE_REQUEST);
740 mLoadCounter = 0;
741 mDidLoadDataForPrinting = false;
743 if (aIsPrintPreview) {
744 bool notifyOnInit = false;
745 ShowPrintProgress(false, notifyOnInit);
747 // Very important! Turn Off scripting
748 TurnScriptingOn(false);
750 if (!notifyOnInit) {
751 InstallPrintPreviewListener();
752 rv = InitPrintDocConstruction(false);
753 } else {
754 rv = NS_OK;
756 } else {
757 bool doNotify;
758 ShowPrintProgress(true, doNotify);
759 if (!doNotify) {
760 // Print listener setup...
761 mPrt->OnStartPrinting();
763 rv = InitPrintDocConstruction(false);
767 // We will enable scripting later after printing has finished.
768 scriptSuppressor.Disconnect();
770 return NS_OK;
773 //---------------------------------------------------------------------------------
774 NS_IMETHODIMP
775 nsPrintEngine::Print(nsIPrintSettings* aPrintSettings,
776 nsIWebProgressListener* aWebProgressListener)
778 // If we have a print preview document, use that instead of the original
779 // mDocument. That way animated images etc. get printed using the same state
780 // as in print preview.
781 nsCOMPtr<nsIDOMDocument> doc =
782 do_QueryInterface(mPrtPreview && mPrtPreview->mPrintObject ?
783 mPrtPreview->mPrintObject->mDocument : mDocument);
785 return CommonPrint(false, aPrintSettings, aWebProgressListener, doc);
788 NS_IMETHODIMP
789 nsPrintEngine::PrintPreview(nsIPrintSettings* aPrintSettings,
790 nsIDOMWindow *aChildDOMWin,
791 nsIWebProgressListener* aWebProgressListener)
793 // Get the DocShell and see if it is busy
794 // (We can't Print Preview this document if it is still busy)
795 nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mContainer));
796 NS_ENSURE_STATE(docShell);
798 uint32_t busyFlags = nsIDocShell::BUSY_FLAGS_NONE;
799 if (NS_FAILED(docShell->GetBusyFlags(&busyFlags)) ||
800 busyFlags != nsIDocShell::BUSY_FLAGS_NONE) {
801 CloseProgressDialog(aWebProgressListener);
802 ShowPrintErrorDialog(NS_ERROR_GFX_PRINTER_DOC_IS_BUSY, false);
803 return NS_ERROR_FAILURE;
806 NS_ENSURE_STATE(aChildDOMWin);
807 nsCOMPtr<nsIDOMDocument> doc;
808 aChildDOMWin->GetDocument(getter_AddRefs(doc));
809 NS_ENSURE_STATE(doc);
811 // Document is not busy -- go ahead with the Print Preview
812 return CommonPrint(true, aPrintSettings, aWebProgressListener, doc);
815 //----------------------------------------------------------------------------------
816 /* readonly attribute boolean isFramesetDocument; */
817 NS_IMETHODIMP
818 nsPrintEngine::GetIsFramesetDocument(bool *aIsFramesetDocument)
820 nsCOMPtr<nsIDocShell> webContainer(do_QueryReferent(mContainer));
821 *aIsFramesetDocument = IsParentAFrameSet(webContainer);
822 return NS_OK;
825 //----------------------------------------------------------------------------------
826 /* readonly attribute boolean isIFrameSelected; */
827 NS_IMETHODIMP
828 nsPrintEngine::GetIsIFrameSelected(bool *aIsIFrameSelected)
830 *aIsIFrameSelected = false;
832 // Get the docshell for this documentviewer
833 nsCOMPtr<nsIDocShell> webContainer(do_QueryReferent(mContainer));
834 // Get the currently focused window
835 nsCOMPtr<nsIDOMWindow> currentFocusWin = FindFocusedDOMWindow();
836 if (currentFocusWin && webContainer) {
837 // Get whether the doc contains a frameset
838 // Also, check to see if the currently focus docshell
839 // is a child of this docshell
840 bool isParentFrameSet;
841 *aIsIFrameSelected = IsThereAnIFrameSelected(webContainer, currentFocusWin, isParentFrameSet);
843 return NS_OK;
846 //----------------------------------------------------------------------------------
847 /* readonly attribute boolean isRangeSelection; */
848 NS_IMETHODIMP
849 nsPrintEngine::GetIsRangeSelection(bool *aIsRangeSelection)
851 // Get the currently focused window
852 nsCOMPtr<nsIDOMWindow> currentFocusWin = FindFocusedDOMWindow();
853 *aIsRangeSelection = IsThereARangeSelection(currentFocusWin);
854 return NS_OK;
857 //----------------------------------------------------------------------------------
858 /* readonly attribute boolean isFramesetFrameSelected; */
859 NS_IMETHODIMP
860 nsPrintEngine::GetIsFramesetFrameSelected(bool *aIsFramesetFrameSelected)
862 // Get the currently focused window
863 nsCOMPtr<nsIDOMWindow> currentFocusWin = FindFocusedDOMWindow();
864 *aIsFramesetFrameSelected = currentFocusWin != nullptr;
865 return NS_OK;
868 //----------------------------------------------------------------------------------
869 /* readonly attribute long printPreviewNumPages; */
870 NS_IMETHODIMP
871 nsPrintEngine::GetPrintPreviewNumPages(int32_t *aPrintPreviewNumPages)
873 NS_ENSURE_ARG_POINTER(aPrintPreviewNumPages);
875 nsPrintData* prt = nullptr;
876 nsIFrame* seqFrame = nullptr;
877 *aPrintPreviewNumPages = 0;
879 // When calling this function, the FinishPrintPreview() function might not
880 // been called as there are still some
881 if (mPrtPreview) {
882 prt = mPrtPreview;
883 } else {
884 prt = mPrt;
886 if ((!prt) ||
887 NS_FAILED(GetSeqFrameAndCountPagesInternal(prt->mPrintObject, seqFrame, *aPrintPreviewNumPages))) {
888 return NS_ERROR_FAILURE;
890 return NS_OK;
893 //----------------------------------------------------------------------------------
894 // Enumerate all the documents for their titles
895 NS_IMETHODIMP
896 nsPrintEngine::EnumerateDocumentNames(uint32_t* aCount,
897 char16_t*** aResult)
899 NS_ENSURE_ARG(aCount);
900 NS_ENSURE_ARG_POINTER(aResult);
902 *aCount = 0;
903 *aResult = nullptr;
905 int32_t numDocs = mPrt->mPrintDocList.Length();
906 char16_t** array = (char16_t**) nsMemory::Alloc(numDocs * sizeof(char16_t*));
907 if (!array)
908 return NS_ERROR_OUT_OF_MEMORY;
910 for (int32_t i=0;i<numDocs;i++) {
911 nsPrintObject* po = mPrt->mPrintDocList.ElementAt(i);
912 NS_ASSERTION(po, "nsPrintObject can't be null!");
913 nsAutoString docTitleStr;
914 nsAutoString docURLStr;
915 GetDocumentTitleAndURL(po->mDocument, docTitleStr, docURLStr);
917 // Use the URL if the doc is empty
918 if (docTitleStr.IsEmpty() && !docURLStr.IsEmpty()) {
919 docTitleStr = docURLStr;
921 array[i] = ToNewUnicode(docTitleStr);
923 *aCount = numDocs;
924 *aResult = array;
926 return NS_OK;
930 //----------------------------------------------------------------------------------
931 /* readonly attribute nsIPrintSettings globalPrintSettings; */
932 nsresult
933 nsPrintEngine::GetGlobalPrintSettings(nsIPrintSettings **aGlobalPrintSettings)
935 NS_ENSURE_ARG_POINTER(aGlobalPrintSettings);
937 nsresult rv = NS_ERROR_FAILURE;
938 nsCOMPtr<nsIPrintSettingsService> printSettingsService =
939 do_GetService(sPrintSettingsServiceContractID, &rv);
940 if (NS_SUCCEEDED(rv)) {
941 rv = printSettingsService->GetGlobalPrintSettings(aGlobalPrintSettings);
943 return rv;
946 //----------------------------------------------------------------------------------
947 /* readonly attribute boolean doingPrint; */
948 NS_IMETHODIMP
949 nsPrintEngine::GetDoingPrint(bool *aDoingPrint)
951 NS_ENSURE_ARG_POINTER(aDoingPrint);
952 *aDoingPrint = mIsDoingPrinting;
953 return NS_OK;
956 //----------------------------------------------------------------------------------
957 /* readonly attribute boolean doingPrintPreview; */
958 NS_IMETHODIMP
959 nsPrintEngine::GetDoingPrintPreview(bool *aDoingPrintPreview)
961 NS_ENSURE_ARG_POINTER(aDoingPrintPreview);
962 *aDoingPrintPreview = mIsDoingPrintPreview;
963 return NS_OK;
966 //----------------------------------------------------------------------------------
967 /* readonly attribute nsIPrintSettings currentPrintSettings; */
968 NS_IMETHODIMP
969 nsPrintEngine::GetCurrentPrintSettings(nsIPrintSettings * *aCurrentPrintSettings)
971 NS_ENSURE_ARG_POINTER(aCurrentPrintSettings);
973 if (mPrt) {
974 *aCurrentPrintSettings = mPrt->mPrintSettings;
976 } else if (mPrtPreview) {
977 *aCurrentPrintSettings = mPrtPreview->mPrintSettings;
979 } else {
980 *aCurrentPrintSettings = nullptr;
982 NS_IF_ADDREF(*aCurrentPrintSettings);
983 return NS_OK;
986 //-----------------------------------------------------------------
987 //-- Section: Pre-Reflow Methods
988 //-----------------------------------------------------------------
990 //---------------------------------------------------------------------
991 // This method checks to see if there is at least one printer defined
992 // and if so, it sets the first printer in the list as the default name
993 // in the PrintSettings which is then used for Printer Preview
994 nsresult
995 nsPrintEngine::CheckForPrinters(nsIPrintSettings* aPrintSettings)
997 #if defined(XP_MACOSX) || defined(ANDROID)
998 // Mac doesn't support retrieving a printer list.
999 return NS_OK;
1000 #else
1001 NS_ENSURE_ARG_POINTER(aPrintSettings);
1003 // See if aPrintSettings already has a printer
1004 nsXPIDLString printerName;
1005 nsresult rv = aPrintSettings->GetPrinterName(getter_Copies(printerName));
1006 if (NS_SUCCEEDED(rv) && !printerName.IsEmpty()) {
1007 return NS_OK;
1010 // aPrintSettings doesn't have a printer set. Try to fetch the default.
1011 nsCOMPtr<nsIPrintSettingsService> printSettingsService =
1012 do_GetService(sPrintSettingsServiceContractID, &rv);
1013 NS_ENSURE_SUCCESS(rv, rv);
1015 rv = printSettingsService->GetDefaultPrinterName(getter_Copies(printerName));
1016 if (NS_SUCCEEDED(rv) && !printerName.IsEmpty()) {
1017 rv = aPrintSettings->SetPrinterName(printerName.get());
1019 return rv;
1020 #endif
1023 //----------------------------------------------------------------------
1024 // Set up to use the "pluggable" Print Progress Dialog
1025 void
1026 nsPrintEngine::ShowPrintProgress(bool aIsForPrinting, bool& aDoNotify)
1028 // default to not notifying, that if something here goes wrong
1029 // or we aren't going to show the progress dialog we can straight into
1030 // reflowing the doc for printing.
1031 aDoNotify = false;
1033 // Assume we can't do progress and then see if we can
1034 bool showProgresssDialog = false;
1036 // if it is already being shown then don't bother to find out if it should be
1037 // so skip this and leave mShowProgressDialog set to FALSE
1038 if (!mProgressDialogIsShown) {
1039 showProgresssDialog = Preferences::GetBool("print.show_print_progress");
1042 // Turning off the showing of Print Progress in Prefs overrides
1043 // whether the calling PS desire to have it on or off, so only check PS if
1044 // prefs says it's ok to be on.
1045 if (showProgresssDialog) {
1046 mPrt->mPrintSettings->GetShowPrintProgress(&showProgresssDialog);
1049 // Now open the service to get the progress dialog
1050 // If we don't get a service, that's ok, then just don't show progress
1051 if (showProgresssDialog) {
1052 nsCOMPtr<nsIPrintingPromptService> printPromptService(do_GetService(kPrintingPromptService));
1053 if (printPromptService) {
1054 nsPIDOMWindow *domWin = mDocument->GetWindow();
1055 if (!domWin) return;
1057 nsCOMPtr<nsIDocShell> docShell = domWin->GetDocShell();
1058 if (!docShell) return;
1059 nsCOMPtr<nsIDocShellTreeOwner> owner;
1060 docShell->GetTreeOwner(getter_AddRefs(owner));
1061 nsCOMPtr<nsIWebBrowserChrome> browserChrome = do_GetInterface(owner);
1062 if (!browserChrome) return;
1063 bool isModal = true;
1064 browserChrome->IsWindowModal(&isModal);
1065 if (isModal) {
1066 // Showing a print progress dialog when printing a modal window
1067 // isn't supported. See bug 301560.
1068 return;
1071 nsCOMPtr<nsIWebProgressListener> printProgressListener;
1073 nsCOMPtr<nsIWebBrowserPrint> wbp(do_QueryInterface(mDocViewerPrint));
1074 nsresult rv = printPromptService->ShowProgress(domWin, wbp, mPrt->mPrintSettings, this, aIsForPrinting,
1075 getter_AddRefs(printProgressListener),
1076 getter_AddRefs(mPrt->mPrintProgressParams),
1077 &aDoNotify);
1078 if (NS_SUCCEEDED(rv)) {
1079 if (printProgressListener && mPrt->mPrintProgressParams) {
1080 mPrt->mPrintProgressListeners.AppendObject(printProgressListener);
1081 SetDocAndURLIntoProgress(mPrt->mPrintObject, mPrt->mPrintProgressParams);
1088 //---------------------------------------------------------------------
1089 bool
1090 nsPrintEngine::IsThereARangeSelection(nsIDOMWindow* aDOMWin)
1092 if (mDisallowSelectionPrint)
1093 return false;
1095 nsCOMPtr<nsIPresShell> presShell;
1096 if (aDOMWin) {
1097 nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(aDOMWin));
1098 presShell = window->GetDocShell()->GetPresShell();
1101 if (!presShell)
1102 return false;
1104 // check here to see if there is a range selection
1105 // so we know whether to turn on the "Selection" radio button
1106 Selection* selection =
1107 presShell->GetCurrentSelection(nsISelectionController::SELECTION_NORMAL);
1108 if (!selection) {
1109 return false;
1112 int32_t rangeCount = selection->GetRangeCount();
1113 if (!rangeCount) {
1114 return false;
1117 if (rangeCount > 1) {
1118 return true;
1121 // check to make sure it isn't an insertion selection
1122 return selection->GetRangeAt(0) && !selection->IsCollapsed();
1125 //---------------------------------------------------------------------
1126 bool
1127 nsPrintEngine::IsParentAFrameSet(nsIDocShell * aParent)
1129 // See if the incoming doc is the root document
1130 if (!aParent) return false;
1132 // When it is the top level document we need to check
1133 // to see if it contains a frameset. If it does, then
1134 // we only want to print the doc's children and not the document itself
1135 // For anything else we always print all the children and the document
1136 // for example, if the doc contains an IFRAME we eant to print the child
1137 // document (the IFRAME) and then the rest of the document.
1139 // XXX we really need to search the frame tree, and not the content
1140 // but there is no way to distinguish between IFRAMEs and FRAMEs
1141 // with the GetFrameType call.
1142 // Bug 53459 has been files so we can eventually distinguish
1143 // between IFRAME frames and FRAME frames
1144 bool isFrameSet = false;
1145 // only check to see if there is a frameset if there is
1146 // NO parent doc for this doc. meaning this parent is the root doc
1147 nsCOMPtr<nsIDocument> doc = aParent->GetDocument();
1148 if (doc) {
1149 nsIContent *rootElement = doc->GetRootElement();
1150 if (rootElement) {
1151 isFrameSet = HasFramesetChild(rootElement);
1154 return isFrameSet;
1158 //---------------------------------------------------------------------
1159 // Recursively build a list of sub documents to be printed
1160 // that mirrors the document tree
1161 void
1162 nsPrintEngine::BuildDocTree(nsIDocShell * aParentNode,
1163 nsTArray<nsPrintObject*> * aDocList,
1164 nsPrintObject * aPO)
1166 NS_ASSERTION(aParentNode, "Pointer is null!");
1167 NS_ASSERTION(aDocList, "Pointer is null!");
1168 NS_ASSERTION(aPO, "Pointer is null!");
1170 int32_t childWebshellCount;
1171 aParentNode->GetChildCount(&childWebshellCount);
1172 if (childWebshellCount > 0) {
1173 for (int32_t i=0;i<childWebshellCount;i++) {
1174 nsCOMPtr<nsIDocShellTreeItem> child;
1175 aParentNode->GetChildAt(i, getter_AddRefs(child));
1176 nsCOMPtr<nsIDocShell> childAsShell(do_QueryInterface(child));
1178 nsCOMPtr<nsIContentViewer> viewer;
1179 childAsShell->GetContentViewer(getter_AddRefs(viewer));
1180 if (viewer) {
1181 nsCOMPtr<nsIContentViewerFile> viewerFile(do_QueryInterface(viewer));
1182 if (viewerFile) {
1183 nsCOMPtr<nsIDOMDocument> doc = do_GetInterface(childAsShell);
1184 nsPrintObject * po = new nsPrintObject();
1185 po->mParent = aPO;
1186 nsresult rv = po->Init(childAsShell, doc, aPO->mPrintPreview);
1187 if (NS_FAILED(rv))
1188 NS_NOTREACHED("Init failed?");
1189 aPO->mKids.AppendElement(po);
1190 aDocList->AppendElement(po);
1191 BuildDocTree(childAsShell, aDocList, po);
1198 //---------------------------------------------------------------------
1199 void
1200 nsPrintEngine::GetDocumentTitleAndURL(nsIDocument* aDoc,
1201 nsAString& aTitle,
1202 nsAString& aURLStr)
1204 NS_ASSERTION(aDoc, "Pointer is null!");
1206 aTitle.Truncate();
1207 aURLStr.Truncate();
1209 nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(aDoc);
1210 doc->GetTitle(aTitle);
1212 nsIURI* url = aDoc->GetDocumentURI();
1213 if (!url) return;
1215 nsCOMPtr<nsIURIFixup> urifixup(do_GetService(NS_URIFIXUP_CONTRACTID));
1216 if (!urifixup) return;
1218 nsCOMPtr<nsIURI> exposableURI;
1219 urifixup->CreateExposableURI(url, getter_AddRefs(exposableURI));
1221 if (!exposableURI) return;
1223 nsAutoCString urlCStr;
1224 exposableURI->GetSpec(urlCStr);
1226 nsresult rv;
1227 nsCOMPtr<nsITextToSubURI> textToSubURI =
1228 do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
1229 if (NS_FAILED(rv)) return;
1231 textToSubURI->UnEscapeURIForUI(NS_LITERAL_CSTRING("UTF-8"),
1232 urlCStr, aURLStr);
1235 //---------------------------------------------------------------------
1236 // The walks the PO tree and for each document it walks the content
1237 // tree looking for any content that are sub-shells
1239 // It then sets the mContent pointer in the "found" PO object back to the
1240 // the document that contained it.
1241 void
1242 nsPrintEngine::MapContentToWebShells(nsPrintObject* aRootPO,
1243 nsPrintObject* aPO)
1245 NS_ASSERTION(aRootPO, "Pointer is null!");
1246 NS_ASSERTION(aPO, "Pointer is null!");
1248 // Recursively walk the content from the root item
1249 // XXX Would be faster to enumerate the subdocuments, although right now
1250 // nsIDocument doesn't expose quite what would be needed.
1251 nsCOMPtr<nsIContentViewer> viewer;
1252 aPO->mDocShell->GetContentViewer(getter_AddRefs(viewer));
1253 if (!viewer) return;
1255 nsCOMPtr<nsIDOMDocument> domDoc;
1256 viewer->GetDOMDocument(getter_AddRefs(domDoc));
1257 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
1258 if (!doc) return;
1260 Element* rootElement = doc->GetRootElement();
1261 if (rootElement) {
1262 MapContentForPO(aPO, rootElement);
1263 } else {
1264 NS_WARNING("Null root content on (sub)document.");
1267 // Continue recursively walking the chilren of this PO
1268 for (uint32_t i=0;i<aPO->mKids.Length();i++) {
1269 MapContentToWebShells(aRootPO, aPO->mKids[i]);
1274 //-------------------------------------------------------
1275 // A Frame's sub-doc may contain content or a FrameSet
1276 // When it contains a FrameSet the mFrameType for the PrintObject
1277 // is always set to an eFrame. Which is fine when printing "AsIs"
1278 // but is incorrect when when printing "Each Frame Separately".
1279 // When printing "Each Frame Separately" the Frame really acts like
1280 // a frameset.
1282 // This method walks the PO tree and checks to see if the PrintObject is
1283 // an eFrame and has children that are eFrames (meaning it's a Frame containing a FrameSet)
1284 // If so, then the mFrameType need to be changed to eFrameSet
1286 // Also note: We only want to call this we are printing "Each Frame Separately"
1287 // when printing "As Is" leave it as an eFrame
1288 void
1289 nsPrintEngine::CheckForChildFrameSets(nsPrintObject* aPO)
1291 NS_ASSERTION(aPO, "Pointer is null!");
1293 // Continue recursively walking the chilren of this PO
1294 bool hasChildFrames = false;
1295 for (uint32_t i=0;i<aPO->mKids.Length();i++) {
1296 nsPrintObject* po = aPO->mKids[i];
1297 if (po->mFrameType == eFrame) {
1298 hasChildFrames = true;
1299 CheckForChildFrameSets(po);
1303 if (hasChildFrames && aPO->mFrameType == eFrame) {
1304 aPO->mFrameType = eFrameSet;
1308 //---------------------------------------------------------------------
1309 // This method is key to the entire print mechanism.
1311 // This "maps" or figures out which sub-doc represents a
1312 // given Frame or IFrame in its parent sub-doc.
1314 // So the Mcontent pointer in the child sub-doc points to the
1315 // content in the its parent document, that caused it to be printed.
1316 // This is used later to (after reflow) to find the absolute location
1317 // of the sub-doc on its parent's page frame so it can be
1318 // printed in the correct location.
1320 // This method recursvely "walks" the content for a document finding
1321 // all the Frames and IFrames, then sets the "mFrameType" data member
1322 // which tells us what type of PO we have
1323 void
1324 nsPrintEngine::MapContentForPO(nsPrintObject* aPO,
1325 nsIContent* aContent)
1327 NS_PRECONDITION(aPO && aContent, "Null argument");
1329 nsIDocument* doc = aContent->GetComposedDoc();
1331 NS_ASSERTION(doc, "Content without a document from a document tree?");
1333 nsIDocument* subDoc = doc->GetSubDocumentFor(aContent);
1335 if (subDoc) {
1336 nsCOMPtr<nsIDocShell> docShell(subDoc->GetDocShell());
1338 if (docShell) {
1339 nsPrintObject * po = nullptr;
1340 int32_t cnt = aPO->mKids.Length();
1341 for (int32_t i=0;i<cnt;i++) {
1342 nsPrintObject* kid = aPO->mKids.ElementAt(i);
1343 if (kid->mDocument == subDoc) {
1344 po = kid;
1345 break;
1349 // XXX If a subdocument has no onscreen presentation, there will be no PO
1350 // This is even if there should be a print presentation
1351 if (po) {
1353 nsCOMPtr<nsIDOMHTMLFrameElement> frame(do_QueryInterface(aContent));
1354 // "frame" elements not in a frameset context should be treated
1355 // as iframes
1356 if (frame && po->mParent->mFrameType == eFrameSet) {
1357 po->mFrameType = eFrame;
1358 } else {
1359 // Assume something iframe-like, i.e. iframe, object, or embed
1360 po->mFrameType = eIFrame;
1361 SetPrintAsIs(po, true);
1362 NS_ASSERTION(po->mParent, "The root must be a parent");
1363 po->mParent->mPrintAsIs = true;
1369 // walk children content
1370 for (nsIContent* child = aContent->GetFirstChild();
1371 child;
1372 child = child->GetNextSibling()) {
1373 MapContentForPO(aPO, child);
1377 //---------------------------------------------------------------------
1378 bool
1379 nsPrintEngine::IsThereAnIFrameSelected(nsIDocShell* aDocShell,
1380 nsIDOMWindow* aDOMWin,
1381 bool& aIsParentFrameSet)
1383 aIsParentFrameSet = IsParentAFrameSet(aDocShell);
1384 bool iFrameIsSelected = false;
1385 if (mPrt && mPrt->mPrintObject) {
1386 nsPrintObject* po = FindPrintObjectByDOMWin(mPrt->mPrintObject, aDOMWin);
1387 iFrameIsSelected = po && po->mFrameType == eIFrame;
1388 } else {
1389 // First, check to see if we are a frameset
1390 if (!aIsParentFrameSet) {
1391 // Check to see if there is a currenlt focused frame
1392 // if so, it means the selected frame is either the main docshell
1393 // or an IFRAME
1394 if (aDOMWin) {
1395 // Get the main docshell's DOMWin to see if it matches
1396 // the frame that is selected
1397 nsCOMPtr<nsIDOMWindow> domWin =
1398 aDocShell ? aDocShell->GetWindow() : nullptr;
1399 if (domWin != aDOMWin) {
1400 iFrameIsSelected = true; // we have a selected IFRAME
1406 return iFrameIsSelected;
1409 //---------------------------------------------------------------------
1410 // Recursively sets all the PO items to be printed
1411 // from the given item down into the tree
1412 void
1413 nsPrintEngine::SetPrintPO(nsPrintObject* aPO, bool aPrint)
1415 NS_ASSERTION(aPO, "Pointer is null!");
1417 // Set whether to print flag
1418 aPO->mDontPrint = !aPrint;
1420 for (uint32_t i=0;i<aPO->mKids.Length();i++) {
1421 SetPrintPO(aPO->mKids[i], aPrint);
1425 //---------------------------------------------------------------------
1426 // This will first use a Title and/or URL from the PrintSettings
1427 // if one isn't set then it uses the one from the document
1428 // then if not title is there we will make sure we send something back
1429 // depending on the situation.
1430 void
1431 nsPrintEngine::GetDisplayTitleAndURL(nsPrintObject* aPO,
1432 nsAString& aTitle,
1433 nsAString& aURLStr,
1434 eDocTitleDefault aDefType)
1436 NS_ASSERTION(aPO, "Pointer is null!");
1438 if (!mPrt)
1439 return;
1441 aTitle.Truncate();
1442 aURLStr.Truncate();
1444 // First check to see if the PrintSettings has defined an alternate title
1445 // and use that if it did
1446 if (mPrt->mPrintSettings) {
1447 char16_t * docTitleStrPS = nullptr;
1448 char16_t * docURLStrPS = nullptr;
1449 mPrt->mPrintSettings->GetTitle(&docTitleStrPS);
1450 mPrt->mPrintSettings->GetDocURL(&docURLStrPS);
1452 if (docTitleStrPS) {
1453 aTitle = docTitleStrPS;
1456 if (docURLStrPS) {
1457 aURLStr = docURLStrPS;
1460 nsMemory::Free(docTitleStrPS);
1461 nsMemory::Free(docURLStrPS);
1464 nsAutoString docTitle;
1465 nsAutoString docUrl;
1466 GetDocumentTitleAndURL(aPO->mDocument, docTitle, docUrl);
1468 if (aURLStr.IsEmpty() && !docUrl.IsEmpty()) {
1469 aURLStr = docUrl;
1472 if (aTitle.IsEmpty()) {
1473 if (!docTitle.IsEmpty()) {
1474 aTitle = docTitle;
1475 } else {
1476 if (aDefType == eDocTitleDefURLDoc) {
1477 if (!aURLStr.IsEmpty()) {
1478 aTitle = aURLStr;
1479 } else if (mPrt->mBrandName) {
1480 aTitle = mPrt->mBrandName;
1487 //---------------------------------------------------------------------
1488 nsresult nsPrintEngine::DocumentReadyForPrinting()
1490 if (mPrt->mPrintFrameType == nsIPrintSettings::kEachFrameSep) {
1491 CheckForChildFrameSets(mPrt->mPrintObject);
1495 // Send the document to the printer...
1497 nsresult rv = SetupToPrintContent();
1498 if (NS_FAILED(rv)) {
1499 // The print job was canceled or there was a problem
1500 // So remove all other documents from the print list
1501 DonePrintingPages(nullptr, rv);
1503 return rv;
1506 /** ---------------------------------------------------
1507 * Cleans up when an error occurred
1509 nsresult nsPrintEngine::CleanupOnFailure(nsresult aResult, bool aIsPrinting)
1511 PR_PL(("**** Failed %s - rv 0x%X", aIsPrinting?"Printing":"Print Preview", aResult));
1513 /* cleanup... */
1514 if (mPagePrintTimer) {
1515 mPagePrintTimer->Stop();
1516 NS_RELEASE(mPagePrintTimer);
1519 if (aIsPrinting) {
1520 SetIsPrinting(false);
1521 } else {
1522 SetIsPrintPreview(false);
1523 SetIsCreatingPrintPreview(false);
1526 /* cleanup done, let's fire-up an error dialog to notify the user
1527 * what went wrong...
1529 * When rv == NS_ERROR_ABORT, it means we want out of the
1530 * print job without displaying any error messages
1532 if (aResult != NS_ERROR_ABORT) {
1533 ShowPrintErrorDialog(aResult, aIsPrinting);
1536 FirePrintCompletionEvent();
1538 return aResult;
1542 //---------------------------------------------------------------------
1543 void
1544 nsPrintEngine::ShowPrintErrorDialog(nsresult aPrintError, bool aIsPrinting)
1546 nsAutoCString stringName;
1547 nsXPIDLString msg, title;
1548 nsresult rv = NS_OK;
1550 switch(aPrintError)
1552 #define ENTITY_FOR_ERROR(label) \
1553 case NS_ERROR_##label: stringName.AssignLiteral("PERR_" #label); break
1555 ENTITY_FOR_ERROR(GFX_PRINTER_NO_PRINTER_AVAILABLE);
1556 ENTITY_FOR_ERROR(GFX_PRINTER_NAME_NOT_FOUND);
1557 ENTITY_FOR_ERROR(GFX_PRINTER_COULD_NOT_OPEN_FILE);
1558 ENTITY_FOR_ERROR(GFX_PRINTER_STARTDOC);
1559 ENTITY_FOR_ERROR(GFX_PRINTER_ENDDOC);
1560 ENTITY_FOR_ERROR(GFX_PRINTER_STARTPAGE);
1561 ENTITY_FOR_ERROR(GFX_PRINTER_DOC_IS_BUSY);
1563 ENTITY_FOR_ERROR(ABORT);
1564 ENTITY_FOR_ERROR(NOT_AVAILABLE);
1565 ENTITY_FOR_ERROR(NOT_IMPLEMENTED);
1566 ENTITY_FOR_ERROR(OUT_OF_MEMORY);
1567 ENTITY_FOR_ERROR(UNEXPECTED);
1569 default:
1570 ENTITY_FOR_ERROR(FAILURE);
1572 #undef ENTITY_FOR_ERROR
1575 if (!aIsPrinting) {
1576 // Try first with _PP suffix.
1577 stringName.AppendLiteral("_PP");
1578 rv = nsContentUtils::GetLocalizedString(
1579 nsContentUtils::ePRINTING_PROPERTIES, stringName.get(), msg);
1580 if (NS_FAILED(rv)) {
1581 stringName.Truncate(stringName.Length() - 3);
1584 if (aIsPrinting || NS_FAILED(rv)) {
1585 rv = nsContentUtils::GetLocalizedString(
1586 nsContentUtils::ePRINTING_PROPERTIES, stringName.get(), msg);
1588 if (NS_FAILED(rv)) {
1589 return;
1592 rv = nsContentUtils::GetLocalizedString(
1593 nsContentUtils::ePRINTING_PROPERTIES,
1594 aIsPrinting ? "print_error_dialog_title"
1595 : "printpreview_error_dialog_title",
1596 title);
1597 if (NS_FAILED(rv)) {
1598 return;
1601 nsCOMPtr<nsIWindowWatcher> wwatch =
1602 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
1603 if (NS_FAILED(rv)) {
1604 return;
1607 nsCOMPtr<nsIDOMWindow> active;
1608 wwatch->GetActiveWindow(getter_AddRefs(active));
1610 nsCOMPtr<nsIPrompt> dialog;
1611 /* |GetNewPrompter| allows that |active| is |nullptr|
1612 * (see bug 234982 ("nsPrintEngine::ShowPrintErrorDialog() fails in many cases")) */
1613 wwatch->GetNewPrompter(active, getter_AddRefs(dialog));
1614 if (!dialog) {
1615 return;
1618 dialog->Alert(title.get(), msg.get());
1621 //-----------------------------------------------------------------
1622 //-- Section: Reflow Methods
1623 //-----------------------------------------------------------------
1625 nsresult
1626 nsPrintEngine::ReconstructAndReflow(bool doSetPixelScale)
1628 #if defined(XP_WIN) && defined(EXTENDED_DEBUG_PRINTING)
1629 // We need to clear all the output files here
1630 // because they will be re-created with second reflow of the docs
1631 if (kPrintingLogMod && kPrintingLogMod->level == DUMP_LAYOUT_LEVEL) {
1632 RemoveFilesInDir(".\\");
1633 gDumpFileNameCnt = 0;
1634 gDumpLOFileNameCnt = 0;
1636 #endif
1638 for (uint32_t i = 0; i < mPrt->mPrintDocList.Length(); ++i) {
1639 nsPrintObject* po = mPrt->mPrintDocList.ElementAt(i);
1640 NS_ASSERTION(po, "nsPrintObject can't be null!");
1642 if (po->mDontPrint || po->mInvisible) {
1643 continue;
1646 UpdateZoomRatio(po, doSetPixelScale);
1648 po->mPresContext->SetPageScale(po->mZoomRatio);
1650 // Calculate scale factor from printer to screen
1651 float printDPI = float(mPrt->mPrintDC->AppUnitsPerCSSInch()) /
1652 float(mPrt->mPrintDC->AppUnitsPerDevPixel());
1653 po->mPresContext->SetPrintPreviewScale(mScreenDPI / printDPI);
1655 po->mPresShell->ReconstructFrames();
1657 // For all views except the first one, setup the root view.
1658 // ??? Can there be multiple po for the top-level-document?
1659 bool documentIsTopLevel = true;
1660 if (i != 0) {
1661 nsSize adjSize;
1662 bool doReturn;
1663 nsresult rv = SetRootView(po, doReturn, documentIsTopLevel, adjSize);
1665 MOZ_ASSERT(!documentIsTopLevel, "How could this happen?");
1667 if (NS_FAILED(rv) || doReturn) {
1668 return rv;
1672 po->mPresShell->FlushPendingNotifications(Flush_Layout);
1674 nsresult rv = UpdateSelectionAndShrinkPrintObject(po, documentIsTopLevel);
1675 NS_ENSURE_SUCCESS(rv, rv);
1677 return NS_OK;
1680 //-------------------------------------------------------
1681 nsresult
1682 nsPrintEngine::SetupToPrintContent()
1684 nsresult rv;
1686 bool didReconstruction = false;
1688 // If some new content got loaded since the initial reflow rebuild
1689 // everything.
1690 if (mDidLoadDataForPrinting) {
1691 rv = ReconstructAndReflow(DoSetPixelScale());
1692 didReconstruction = true;
1693 NS_ENSURE_SUCCESS(rv, rv);
1696 // Here is where we figure out if extra reflow for shrinking the content
1697 // is required.
1698 // But skip this step if we are in PrintPreview
1699 bool ppIsShrinkToFit = mPrtPreview && mPrtPreview->mShrinkToFit;
1700 if (mPrt->mShrinkToFit && !ppIsShrinkToFit) {
1701 // Now look for the PO that has the smallest percent for shrink to fit
1702 if (mPrt->mPrintDocList.Length() > 1 && mPrt->mPrintObject->mFrameType == eFrameSet) {
1703 nsPrintObject* smallestPO = FindSmallestSTF();
1704 NS_ASSERTION(smallestPO, "There must always be an XMost PO!");
1705 if (smallestPO) {
1706 // Calc the shrinkage based on the entire content area
1707 mPrt->mShrinkRatio = smallestPO->mShrinkRatio;
1709 } else {
1710 // Single document so use the Shrink as calculated for the PO
1711 mPrt->mShrinkRatio = mPrt->mPrintObject->mShrinkRatio;
1714 if (mPrt->mShrinkRatio < 0.998f) {
1715 rv = ReconstructAndReflow(true);
1716 didReconstruction = true;
1717 NS_ENSURE_SUCCESS(rv, rv);
1720 #ifdef PR_LOGGING
1721 float calcRatio = 0.0f;
1722 if (mPrt->mPrintDocList.Length() > 1 && mPrt->mPrintObject->mFrameType == eFrameSet) {
1723 nsPrintObject* smallestPO = FindSmallestSTF();
1724 NS_ASSERTION(smallestPO, "There must always be an XMost PO!");
1725 if (smallestPO) {
1726 // Calc the shrinkage based on the entire content area
1727 calcRatio = smallestPO->mShrinkRatio;
1729 } else {
1730 // Single document so use the Shrink as calculated for the PO
1731 calcRatio = mPrt->mPrintObject->mShrinkRatio;
1733 PR_PL(("**************************************************************************\n"));
1734 PR_PL(("STF Ratio is: %8.5f Effective Ratio: %8.5f Diff: %8.5f\n", mPrt->mShrinkRatio, calcRatio, mPrt->mShrinkRatio-calcRatio));
1735 PR_PL(("**************************************************************************\n"));
1736 #endif
1739 // If the frames got reconstructed and reflowed the number of pages might
1740 // has changed.
1741 if (didReconstruction) {
1742 FirePrintPreviewUpdateEvent();
1745 DUMP_DOC_LIST(("\nAfter Reflow------------------------------------------"));
1746 PR_PL(("\n"));
1747 PR_PL(("-------------------------------------------------------\n"));
1748 PR_PL(("\n"));
1750 CalcNumPrintablePages(mPrt->mNumPrintablePages);
1752 PR_PL(("--- Printing %d pages\n", mPrt->mNumPrintablePages));
1753 DUMP_DOC_TREELAYOUT;
1755 // Print listener setup...
1756 if (mPrt != nullptr) {
1757 mPrt->OnStartPrinting();
1760 char16_t* fileName = nullptr;
1761 // check to see if we are printing to a file
1762 bool isPrintToFile = false;
1763 mPrt->mPrintSettings->GetPrintToFile(&isPrintToFile);
1764 if (isPrintToFile) {
1765 // On some platforms The BeginDocument needs to know the name of the file
1766 // and it uses the PrintService to get it, so we need to set it into the PrintService here
1767 mPrt->mPrintSettings->GetToFileName(&fileName);
1770 nsAutoString docTitleStr;
1771 nsAutoString docURLStr;
1772 GetDisplayTitleAndURL(mPrt->mPrintObject, docTitleStr, docURLStr, eDocTitleDefURLDoc);
1774 int32_t startPage = 1;
1775 int32_t endPage = mPrt->mNumPrintablePages;
1777 int16_t printRangeType = nsIPrintSettings::kRangeAllPages;
1778 mPrt->mPrintSettings->GetPrintRange(&printRangeType);
1779 if (printRangeType == nsIPrintSettings::kRangeSpecifiedPageRange) {
1780 mPrt->mPrintSettings->GetStartPageRange(&startPage);
1781 mPrt->mPrintSettings->GetEndPageRange(&endPage);
1782 if (endPage > mPrt->mNumPrintablePages) {
1783 endPage = mPrt->mNumPrintablePages;
1787 rv = NS_OK;
1788 // BeginDocument may pass back a FAILURE code
1789 // i.e. On Windows, if you are printing to a file and hit "Cancel"
1790 // to the "File Name" dialog, this comes back as an error
1791 // Don't start printing when regression test are executed
1792 if (!mPrt->mDebugFilePtr && mIsDoingPrinting) {
1793 rv = mPrt->mPrintDC->BeginDocument(docTitleStr, fileName, startPage, endPage);
1796 if (mIsCreatingPrintPreview) {
1797 // Copy docTitleStr and docURLStr to the pageSequenceFrame, to be displayed
1798 // in the header
1799 nsIPageSequenceFrame *seqFrame = mPrt->mPrintObject->mPresShell->GetPageSequenceFrame();
1800 if (seqFrame) {
1801 seqFrame->StartPrint(mPrt->mPrintObject->mPresContext,
1802 mPrt->mPrintSettings, docTitleStr, docURLStr);
1806 PR_PL(("****************** Begin Document ************************\n"));
1808 NS_ENSURE_SUCCESS(rv, rv);
1810 // This will print the docshell document
1811 // when it completes asynchronously in the DonePrintingPages method
1812 // it will check to see if there are more docshells to be printed and
1813 // then PrintDocContent will be called again.
1815 if (mIsDoingPrinting) {
1816 PrintDocContent(mPrt->mPrintObject, rv); // ignore return value
1819 return rv;
1822 //-------------------------------------------------------
1823 // Recursively reflow each sub-doc and then calc
1824 // all the frame locations of the sub-docs
1825 nsresult
1826 nsPrintEngine::ReflowDocList(nsPrintObject* aPO, bool aSetPixelScale)
1828 NS_ENSURE_ARG_POINTER(aPO);
1830 // Check to see if the subdocument's element has been hidden by the parent document
1831 if (aPO->mParent && aPO->mParent->mPresShell) {
1832 nsIFrame* frame = aPO->mContent ? aPO->mContent->GetPrimaryFrame() : nullptr;
1833 if (!frame || !frame->StyleVisibility()->IsVisible()) {
1834 SetPrintPO(aPO, false);
1835 aPO->mInvisible = true;
1836 return NS_OK;
1840 UpdateZoomRatio(aPO, aSetPixelScale);
1842 nsresult rv;
1843 // Reflow the PO
1844 rv = ReflowPrintObject(aPO);
1845 NS_ENSURE_SUCCESS(rv, rv);
1847 int32_t cnt = aPO->mKids.Length();
1848 for (int32_t i=0;i<cnt;i++) {
1849 rv = ReflowDocList(aPO->mKids[i], aSetPixelScale);
1850 NS_ENSURE_SUCCESS(rv, rv);
1852 return NS_OK;
1855 void
1856 nsPrintEngine::FirePrintPreviewUpdateEvent()
1858 // Dispatch the event only while in PrintPreview. When printing, there is no
1859 // listener bound to this event and therefore no need to dispatch it.
1860 if (mIsDoingPrintPreview && !mIsDoingPrinting) {
1861 nsCOMPtr<nsIContentViewer> cv = do_QueryInterface(mDocViewerPrint);
1862 (new AsyncEventDispatcher(
1863 cv->GetDocument(), NS_LITERAL_STRING("printPreviewUpdate"), true, true)
1864 )->RunDOMEventWhenSafe();
1868 nsresult
1869 nsPrintEngine::InitPrintDocConstruction(bool aHandleError)
1871 nsresult rv;
1872 rv = ReflowDocList(mPrt->mPrintObject, DoSetPixelScale());
1873 NS_ENSURE_SUCCESS(rv, rv);
1875 FirePrintPreviewUpdateEvent();
1877 if (mLoadCounter == 0) {
1878 AfterNetworkPrint(aHandleError);
1880 return rv;
1883 nsresult
1884 nsPrintEngine::AfterNetworkPrint(bool aHandleError)
1886 nsCOMPtr<nsIWebProgress> webProgress = do_QueryInterface(mPrt->mPrintObject->mDocShell);
1888 webProgress->RemoveProgressListener(
1889 static_cast<nsIWebProgressListener*>(this));
1891 nsresult rv;
1892 if (mIsDoingPrinting) {
1893 rv = DocumentReadyForPrinting();
1894 } else {
1895 rv = FinishPrintPreview();
1898 /* cleaup on failure + notify user */
1899 if (aHandleError && NS_FAILED(rv)) {
1900 CleanupOnFailure(rv, !mIsDoingPrinting);
1903 return rv;
1906 ////////////////////////////////////////////////////////////////////////////////
1907 // nsIWebProgressListener
1909 NS_IMETHODIMP
1910 nsPrintEngine::OnStateChange(nsIWebProgress* aWebProgress,
1911 nsIRequest* aRequest,
1912 uint32_t aStateFlags,
1913 nsresult aStatus)
1915 nsAutoCString name;
1916 aRequest->GetName(name);
1917 if (name.EqualsLiteral("about:document-onload-blocker")) {
1918 return NS_OK;
1920 if (aStateFlags & STATE_START) {
1921 nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
1923 ++mLoadCounter;
1924 } else if (aStateFlags & STATE_STOP) {
1925 mDidLoadDataForPrinting = true;
1926 --mLoadCounter;
1928 // If all resources are loaded, then do a small timeout and if there
1929 // are still no new requests, then another reflow.
1930 if (mLoadCounter == 0) {
1931 AfterNetworkPrint(true);
1934 return NS_OK;
1939 NS_IMETHODIMP
1940 nsPrintEngine::OnProgressChange(nsIWebProgress* aWebProgress,
1941 nsIRequest* aRequest,
1942 int32_t aCurSelfProgress,
1943 int32_t aMaxSelfProgress,
1944 int32_t aCurTotalProgress,
1945 int32_t aMaxTotalProgress)
1947 NS_NOTREACHED("notification excluded in AddProgressListener(...)");
1948 return NS_OK;
1951 NS_IMETHODIMP
1952 nsPrintEngine::OnLocationChange(nsIWebProgress* aWebProgress,
1953 nsIRequest* aRequest,
1954 nsIURI* aLocation,
1955 uint32_t aFlags)
1957 NS_NOTREACHED("notification excluded in AddProgressListener(...)");
1958 return NS_OK;
1961 NS_IMETHODIMP
1962 nsPrintEngine::OnStatusChange(nsIWebProgress *aWebProgress,
1963 nsIRequest *aRequest,
1964 nsresult aStatus,
1965 const char16_t *aMessage)
1967 NS_NOTREACHED("notification excluded in AddProgressListener(...)");
1968 return NS_OK;
1971 NS_IMETHODIMP
1972 nsPrintEngine::OnSecurityChange(nsIWebProgress *aWebProgress,
1973 nsIRequest *aRequest,
1974 uint32_t aState)
1976 NS_NOTREACHED("notification excluded in AddProgressListener(...)");
1977 return NS_OK;
1980 //-------------------------------------------------------
1982 void
1983 nsPrintEngine::UpdateZoomRatio(nsPrintObject* aPO, bool aSetPixelScale)
1985 // Here is where we set the shrinkage value into the DC
1986 // and this is what actually makes it shrink
1987 if (aSetPixelScale && aPO->mFrameType != eIFrame) {
1988 float ratio;
1989 if (mPrt->mPrintFrameType == nsIPrintSettings::kFramesAsIs || mPrt->mPrintFrameType == nsIPrintSettings::kNoFrames) {
1990 ratio = mPrt->mShrinkRatio - 0.005f; // round down
1991 } else {
1992 ratio = aPO->mShrinkRatio - 0.005f; // round down
1994 aPO->mZoomRatio = ratio;
1995 } else if (!mPrt->mShrinkToFit) {
1996 double scaling;
1997 mPrt->mPrintSettings->GetScaling(&scaling);
1998 aPO->mZoomRatio = float(scaling);
2002 nsresult
2003 nsPrintEngine::UpdateSelectionAndShrinkPrintObject(nsPrintObject* aPO,
2004 bool aDocumentIsTopLevel)
2006 nsCOMPtr<nsIPresShell> displayShell = aPO->mDocShell->GetPresShell();
2007 // Transfer Selection Ranges to the new Print PresShell
2008 nsRefPtr<Selection> selection, selectionPS;
2009 // It's okay if there is no display shell, just skip copying the selection
2010 if (displayShell) {
2011 selection = displayShell->GetCurrentSelection(nsISelectionController::SELECTION_NORMAL);
2013 selectionPS = aPO->mPresShell->GetCurrentSelection(nsISelectionController::SELECTION_NORMAL);
2015 // Reset all existing selection ranges that might have been added by calling
2016 // this function before.
2017 if (selectionPS) {
2018 selectionPS->RemoveAllRanges();
2020 if (selection && selectionPS) {
2021 int32_t cnt = selection->GetRangeCount();
2022 int32_t inx;
2023 for (inx = 0; inx < cnt; ++inx) {
2024 selectionPS->AddRange(selection->GetRangeAt(inx));
2028 // If we are trying to shrink the contents to fit on the page
2029 // we must first locate the "pageContent" frame
2030 // Then we walk the frame tree and look for the "xmost" frame
2031 // this is the frame where the right-hand side of the frame extends
2032 // the furthest
2033 if (mPrt->mShrinkToFit && aDocumentIsTopLevel) {
2034 nsIPageSequenceFrame* pageSequence = aPO->mPresShell->GetPageSequenceFrame();
2035 NS_ENSURE_STATE(pageSequence);
2036 pageSequence->GetSTFPercent(aPO->mShrinkRatio);
2037 // Limit the shrink-to-fit scaling for some text-ish type of documents.
2038 nsAutoString contentType;
2039 aPO->mPresShell->GetDocument()->GetContentType(contentType);
2040 if (contentType.EqualsLiteral("application/xhtml+xml") ||
2041 StringBeginsWith(contentType, NS_LITERAL_STRING("text/"))) {
2042 int32_t limitPercent =
2043 Preferences::GetInt("print.shrink-to-fit.scale-limit-percent", 20);
2044 limitPercent = std::max(0, limitPercent);
2045 limitPercent = std::min(100, limitPercent);
2046 float minShrinkRatio = float(limitPercent) / 100;
2047 aPO->mShrinkRatio = std::max(aPO->mShrinkRatio, minShrinkRatio);
2050 return NS_OK;
2053 bool
2054 nsPrintEngine::DoSetPixelScale()
2056 // This is an Optimization
2057 // If we are in PP then we already know all the shrinkage information
2058 // so just transfer it to the PrintData and we will skip the extra shrinkage reflow
2060 // doSetPixelScale tells Reflow whether to set the shrinkage value into the DC
2061 // The first time we do not want to do this, the second time through we do
2062 bool doSetPixelScale = false;
2063 bool ppIsShrinkToFit = mPrtPreview && mPrtPreview->mShrinkToFit;
2064 if (ppIsShrinkToFit) {
2065 mPrt->mShrinkRatio = mPrtPreview->mShrinkRatio;
2066 doSetPixelScale = true;
2068 return doSetPixelScale;
2071 nsView*
2072 nsPrintEngine::GetParentViewForRoot()
2074 if (mIsCreatingPrintPreview) {
2075 nsCOMPtr<nsIContentViewer> cv = do_QueryInterface(mDocViewerPrint);
2076 if (cv) {
2077 return cv->FindContainerView();
2080 return nullptr;
2083 nsresult
2084 nsPrintEngine::SetRootView(
2085 nsPrintObject* aPO,
2086 bool& doReturn,
2087 bool& documentIsTopLevel,
2088 nsSize& adjSize
2091 bool canCreateScrollbars = true;
2093 nsView* rootView;
2094 nsView* parentView = nullptr;
2096 doReturn = false;
2098 if (aPO->mParent && aPO->mParent->IsPrintable()) {
2099 nsIFrame* frame = aPO->mContent ? aPO->mContent->GetPrimaryFrame() : nullptr;
2100 // Without a frame, this document can't be displayed; therefore, there is no
2101 // point to reflowing it
2102 if (!frame) {
2103 SetPrintPO(aPO, false);
2104 doReturn = true;
2105 return NS_OK;
2108 //XXX If printing supported printing document hierarchies with non-constant
2109 // zoom this would be wrong as we use the same mPrt->mPrintDC for all
2110 // subdocuments.
2111 adjSize = frame->GetContentRect().Size();
2112 documentIsTopLevel = false;
2113 // presshell exists because parent is printable
2115 // the top nsPrintObject's widget will always have scrollbars
2116 if (frame && frame->GetType() == nsGkAtoms::subDocumentFrame) {
2117 nsView* view = frame->GetView();
2118 NS_ENSURE_TRUE(view, NS_ERROR_FAILURE);
2119 view = view->GetFirstChild();
2120 NS_ENSURE_TRUE(view, NS_ERROR_FAILURE);
2121 parentView = view;
2122 canCreateScrollbars = false;
2124 } else {
2125 nscoord pageWidth, pageHeight;
2126 mPrt->mPrintDC->GetDeviceSurfaceDimensions(pageWidth, pageHeight);
2127 adjSize = nsSize(pageWidth, pageHeight);
2128 documentIsTopLevel = true;
2129 parentView = GetParentViewForRoot();
2132 if (aPO->mViewManager->GetRootView()) {
2133 // Reuse the root view that is already on the root frame.
2134 rootView = aPO->mViewManager->GetRootView();
2135 // Remove it from its existing parent if necessary
2136 aPO->mViewManager->RemoveChild(rootView);
2137 rootView->SetParent(parentView);
2138 } else {
2139 // Create a child window of the parent that is our "root view/window"
2140 nsRect tbounds = nsRect(nsPoint(0, 0), adjSize);
2141 rootView = aPO->mViewManager->CreateView(tbounds, parentView);
2142 NS_ENSURE_TRUE(rootView, NS_ERROR_OUT_OF_MEMORY);
2145 if (mIsCreatingPrintPreview && documentIsTopLevel) {
2146 aPO->mPresContext->SetPaginatedScrolling(canCreateScrollbars);
2149 // Setup hierarchical relationship in view manager
2150 aPO->mViewManager->SetRootView(rootView);
2152 return NS_OK;
2155 // Reflow a nsPrintObject
2156 nsresult
2157 nsPrintEngine::ReflowPrintObject(nsPrintObject * aPO)
2159 NS_ENSURE_STATE(aPO);
2161 if (!aPO->IsPrintable()) {
2162 return NS_OK;
2165 NS_ASSERTION(!aPO->mPresContext, "Recreating prescontext");
2167 // create the PresContext
2168 nsPresContext::nsPresContextType type =
2169 mIsCreatingPrintPreview ? nsPresContext::eContext_PrintPreview:
2170 nsPresContext::eContext_Print;
2171 nsView* parentView =
2172 aPO->mParent && aPO->mParent->IsPrintable() ? nullptr : GetParentViewForRoot();
2173 aPO->mPresContext = parentView ?
2174 new nsPresContext(aPO->mDocument, type) :
2175 new nsRootPresContext(aPO->mDocument, type);
2176 NS_ENSURE_TRUE(aPO->mPresContext, NS_ERROR_OUT_OF_MEMORY);
2177 aPO->mPresContext->SetPrintSettings(mPrt->mPrintSettings);
2179 // set the presentation context to the value in the print settings
2180 bool printBGColors;
2181 mPrt->mPrintSettings->GetPrintBGColors(&printBGColors);
2182 aPO->mPresContext->SetBackgroundColorDraw(printBGColors);
2183 mPrt->mPrintSettings->GetPrintBGImages(&printBGColors);
2184 aPO->mPresContext->SetBackgroundImageDraw(printBGColors);
2186 // init it with the DC
2187 nsresult rv = aPO->mPresContext->Init(mPrt->mPrintDC);
2188 NS_ENSURE_SUCCESS(rv, rv);
2190 aPO->mViewManager = new nsViewManager();
2192 rv = aPO->mViewManager->Init(mPrt->mPrintDC);
2193 NS_ENSURE_SUCCESS(rv,rv);
2195 nsStyleSet* styleSet;
2196 rv = mDocViewerPrint->CreateStyleSet(aPO->mDocument, &styleSet);
2197 NS_ENSURE_SUCCESS(rv, rv);
2199 aPO->mPresShell = aPO->mDocument->CreateShell(aPO->mPresContext,
2200 aPO->mViewManager, styleSet);
2201 if (!aPO->mPresShell) {
2202 delete styleSet;
2203 return NS_ERROR_FAILURE;
2206 styleSet->EndUpdate();
2208 // The pres shell now owns the style set object.
2211 bool doReturn = false;;
2212 bool documentIsTopLevel = false;
2213 nsSize adjSize;
2215 rv = SetRootView(aPO, doReturn, documentIsTopLevel, adjSize);
2217 if (NS_FAILED(rv) || doReturn) {
2218 return rv;
2221 PR_PL(("In DV::ReflowPrintObject PO: %p pS: %p (%9s) Setting w,h to %d,%d\n", aPO, aPO->mPresShell.get(),
2222 gFrameTypesStr[aPO->mFrameType], adjSize.width, adjSize.height));
2225 // This docshell stuff is weird; will go away when we stop having multiple
2226 // presentations per document
2227 aPO->mPresContext->SetContainer(aPO->mDocShell);
2229 aPO->mPresShell->BeginObservingDocument();
2231 aPO->mPresContext->SetPageSize(adjSize);
2232 aPO->mPresContext->SetIsRootPaginatedDocument(documentIsTopLevel);
2233 aPO->mPresContext->SetPageScale(aPO->mZoomRatio);
2234 // Calculate scale factor from printer to screen
2235 float printDPI = float(mPrt->mPrintDC->AppUnitsPerCSSInch()) /
2236 float(mPrt->mPrintDC->AppUnitsPerDevPixel());
2237 aPO->mPresContext->SetPrintPreviewScale(mScreenDPI / printDPI);
2239 if (mIsCreatingPrintPreview && documentIsTopLevel) {
2240 mDocViewerPrint->SetPrintPreviewPresentation(aPO->mViewManager,
2241 aPO->mPresContext,
2242 aPO->mPresShell);
2245 rv = aPO->mPresShell->Initialize(adjSize.width, adjSize.height);
2247 NS_ENSURE_SUCCESS(rv, rv);
2248 NS_ASSERTION(aPO->mPresShell, "Presshell should still be here");
2250 // Process the reflow event Initialize posted
2251 aPO->mPresShell->FlushPendingNotifications(Flush_Layout);
2253 rv = UpdateSelectionAndShrinkPrintObject(aPO, documentIsTopLevel);
2254 NS_ENSURE_SUCCESS(rv, rv);
2256 #ifdef EXTENDED_DEBUG_PRINTING
2257 if (kPrintingLogMod && kPrintingLogMod->level == DUMP_LAYOUT_LEVEL) {
2258 nsAutoCString docStr;
2259 nsAutoCString urlStr;
2260 GetDocTitleAndURL(aPO, docStr, urlStr);
2261 char filename[256];
2262 sprintf(filename, "print_dump_%d.txt", gDumpFileNameCnt++);
2263 // Dump all the frames and view to a a file
2264 FILE * fd = fopen(filename, "w");
2265 if (fd) {
2266 nsIFrame *theRootFrame =
2267 aPO->mPresShell->FrameManager()->GetRootFrame();
2268 fprintf(fd, "Title: %s\n", docStr.get());
2269 fprintf(fd, "URL: %s\n", urlStr.get());
2270 fprintf(fd, "--------------- Frames ----------------\n");
2271 nsRefPtr<nsRenderingContext> renderingContext =
2272 mPrt->mPrintDocDC->CreateRenderingContext();
2273 RootFrameList(aPO->mPresContext, fd, 0);
2274 //DumpFrames(fd, aPO->mPresContext, renderingContext, theRootFrame, 0);
2275 fprintf(fd, "---------------------------------------\n\n");
2276 fprintf(fd, "--------------- Views From Root Frame----------------\n");
2277 nsView* v = theRootFrame->GetView();
2278 if (v) {
2279 v->List(fd);
2280 } else {
2281 printf("View is null!\n");
2283 if (docShell) {
2284 fprintf(fd, "--------------- All Views ----------------\n");
2285 DumpViews(docShell, fd);
2286 fprintf(fd, "---------------------------------------\n\n");
2288 fclose(fd);
2291 #endif
2293 return NS_OK;
2296 //-------------------------------------------------------
2297 // Figure out how many documents and how many total pages we are printing
2298 void
2299 nsPrintEngine::CalcNumPrintablePages(int32_t& aNumPages)
2301 aNumPages = 0;
2302 // Count the number of printable documents
2303 // and printable pages
2304 for (uint32_t i=0; i<mPrt->mPrintDocList.Length(); i++) {
2305 nsPrintObject* po = mPrt->mPrintDocList.ElementAt(i);
2306 NS_ASSERTION(po, "nsPrintObject can't be null!");
2307 if (po->mPresContext && po->mPresContext->IsRootPaginatedDocument()) {
2308 nsIPageSequenceFrame* pageSequence = po->mPresShell->GetPageSequenceFrame();
2309 nsIFrame * seqFrame = do_QueryFrame(pageSequence);
2310 if (seqFrame) {
2311 nsIFrame* frame = seqFrame->GetFirstPrincipalChild();
2312 while (frame) {
2313 aNumPages++;
2314 frame = frame->GetNextSibling();
2321 //-----------------------------------------------------------------
2322 //-- Done: Reflow Methods
2323 //-----------------------------------------------------------------
2325 //-----------------------------------------------------------------
2326 //-- Section: Printing Methods
2327 //-----------------------------------------------------------------
2329 //-------------------------------------------------------
2330 // Called for each DocShell that needs to be printed
2331 bool
2332 nsPrintEngine::PrintDocContent(nsPrintObject* aPO, nsresult& aStatus)
2334 NS_ASSERTION(aPO, "Pointer is null!");
2335 aStatus = NS_OK;
2337 if (!aPO->mHasBeenPrinted && aPO->IsPrintable()) {
2338 aStatus = DoPrint(aPO);
2339 return true;
2342 // If |aPO->mPrintAsIs| and |aPO->mHasBeenPrinted| are true,
2343 // the kids frames are already processed in |PrintPage|.
2344 if (!aPO->mInvisible && !(aPO->mPrintAsIs && aPO->mHasBeenPrinted)) {
2345 for (uint32_t i=0;i<aPO->mKids.Length();i++) {
2346 nsPrintObject* po = aPO->mKids[i];
2347 bool printed = PrintDocContent(po, aStatus);
2348 if (printed || NS_FAILED(aStatus)) {
2349 return true;
2353 return false;
2356 static already_AddRefed<nsIDOMNode>
2357 GetEqualNodeInCloneTree(nsIDOMNode* aNode, nsIDocument* aDoc)
2359 nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
2360 // Selections in anonymous subtrees aren't supported.
2361 if (content && content->IsInAnonymousSubtree()) {
2362 return nullptr;
2365 nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
2366 NS_ENSURE_TRUE(node, nullptr);
2368 nsTArray<int32_t> indexArray;
2369 nsINode* current = node;
2370 NS_ENSURE_TRUE(current, nullptr);
2371 while (current) {
2372 nsINode* parent = current->GetParentNode();
2373 if (!parent) {
2374 break;
2376 int32_t index = parent->IndexOf(current);
2377 NS_ENSURE_TRUE(index >= 0, nullptr);
2378 indexArray.AppendElement(index);
2379 current = parent;
2381 NS_ENSURE_TRUE(current->IsNodeOfType(nsINode::eDOCUMENT), nullptr);
2383 current = aDoc;
2384 for (int32_t i = indexArray.Length() - 1; i >= 0; --i) {
2385 current = current->GetChildAt(indexArray[i]);
2386 NS_ENSURE_TRUE(current, nullptr);
2388 nsCOMPtr<nsIDOMNode> result = do_QueryInterface(current);
2389 return result.forget();
2392 static void
2393 CloneRangeToSelection(nsRange* aRange, nsIDocument* aDoc,
2394 Selection* aSelection)
2396 if (aRange->Collapsed()) {
2397 return;
2400 nsCOMPtr<nsIDOMNode> startContainer, endContainer;
2401 aRange->GetStartContainer(getter_AddRefs(startContainer));
2402 int32_t startOffset = aRange->StartOffset();
2403 aRange->GetEndContainer(getter_AddRefs(endContainer));
2404 int32_t endOffset = aRange->EndOffset();
2405 NS_ENSURE_TRUE_VOID(startContainer && endContainer);
2407 nsCOMPtr<nsIDOMNode> newStart = GetEqualNodeInCloneTree(startContainer, aDoc);
2408 nsCOMPtr<nsIDOMNode> newEnd = GetEqualNodeInCloneTree(endContainer, aDoc);
2409 NS_ENSURE_TRUE_VOID(newStart && newEnd);
2411 nsCOMPtr<nsINode> newStartNode = do_QueryInterface(newStart);
2412 NS_ENSURE_TRUE_VOID(newStartNode);
2414 nsRefPtr<nsRange> range = new nsRange(newStartNode);
2415 nsresult rv = range->SetStart(newStartNode, startOffset);
2416 NS_ENSURE_SUCCESS_VOID(rv);
2417 rv = range->SetEnd(newEnd, endOffset);
2418 NS_ENSURE_SUCCESS_VOID(rv);
2420 aSelection->AddRange(range);
2423 static nsresult CloneSelection(nsIDocument* aOrigDoc, nsIDocument* aDoc)
2425 nsIPresShell* origShell = aOrigDoc->GetShell();
2426 nsIPresShell* shell = aDoc->GetShell();
2427 NS_ENSURE_STATE(origShell && shell);
2429 nsRefPtr<Selection> origSelection =
2430 origShell->GetCurrentSelection(nsISelectionController::SELECTION_NORMAL);
2431 nsRefPtr<Selection> selection =
2432 shell->GetCurrentSelection(nsISelectionController::SELECTION_NORMAL);
2433 NS_ENSURE_STATE(origSelection && selection);
2435 int32_t rangeCount = origSelection->GetRangeCount();
2436 for (int32_t i = 0; i < rangeCount; ++i) {
2437 CloneRangeToSelection(origSelection->GetRangeAt(i), aDoc, selection);
2439 return NS_OK;
2442 //-------------------------------------------------------
2443 nsresult
2444 nsPrintEngine::DoPrint(nsPrintObject * aPO)
2446 PR_PL(("\n"));
2447 PR_PL(("**************************** %s ****************************\n", gFrameTypesStr[aPO->mFrameType]));
2448 PR_PL(("****** In DV::DoPrint PO: %p \n", aPO));
2450 nsIPresShell* poPresShell = aPO->mPresShell;
2451 nsPresContext* poPresContext = aPO->mPresContext;
2453 NS_ASSERTION(poPresContext, "PrintObject has not been reflowed");
2454 NS_ASSERTION(poPresContext->Type() != nsPresContext::eContext_PrintPreview,
2455 "How did this context end up here?");
2457 if (mPrt->mPrintProgressParams) {
2458 SetDocAndURLIntoProgress(aPO, mPrt->mPrintProgressParams);
2462 int16_t printRangeType = nsIPrintSettings::kRangeAllPages;
2463 nsresult rv;
2464 if (mPrt->mPrintSettings != nullptr) {
2465 mPrt->mPrintSettings->GetPrintRange(&printRangeType);
2468 // Ask the page sequence frame to print all the pages
2469 nsIPageSequenceFrame* pageSequence = poPresShell->GetPageSequenceFrame();
2470 NS_ASSERTION(nullptr != pageSequence, "no page sequence frame");
2472 // We are done preparing for printing, so we can turn this off
2473 mPrt->mPreparingForPrint = false;
2475 // mPrt->mDebugFilePtr this is onlu non-null when compiled for debugging
2476 if (nullptr != mPrt->mDebugFilePtr) {
2477 #ifdef DEBUG
2478 // output the regression test
2479 nsIFrame* root = poPresShell->FrameManager()->GetRootFrame();
2480 root->DumpRegressionData(poPresContext, mPrt->mDebugFilePtr, 0);
2481 fclose(mPrt->mDebugFilePtr);
2482 SetIsPrinting(false);
2483 #endif
2484 } else {
2485 #ifdef EXTENDED_DEBUG_PRINTING
2486 nsIFrame* rootFrame = poPresShell->FrameManager()->GetRootFrame();
2487 if (aPO->IsPrintable()) {
2488 nsAutoCString docStr;
2489 nsAutoCString urlStr;
2490 GetDocTitleAndURL(aPO, docStr, urlStr);
2491 DumpLayoutData(docStr.get(), urlStr.get(), poPresContext, mPrt->mPrintDocDC, rootFrame, docShell, nullptr);
2493 #endif
2495 if (!mPrt->mPrintSettings) {
2496 // not sure what to do here!
2497 SetIsPrinting(false);
2498 return NS_ERROR_FAILURE;
2501 nsAutoString docTitleStr;
2502 nsAutoString docURLStr;
2503 GetDisplayTitleAndURL(aPO, docTitleStr, docURLStr, eDocTitleDefBlank);
2505 if (nsIPrintSettings::kRangeSelection == printRangeType) {
2506 CloneSelection(aPO->mDocument->GetOriginalDocument(), aPO->mDocument);
2508 poPresContext->SetIsRenderingOnlySelection(true);
2509 // temporarily creating rendering context
2510 // which is needed to find the selection frames
2511 nsRefPtr<nsRenderingContext> rc =
2512 mPrt->mPrintDC->CreateRenderingContext();
2514 // find the starting and ending page numbers
2515 // via the selection
2516 nsIFrame* startFrame;
2517 nsIFrame* endFrame;
2518 int32_t startPageNum;
2519 int32_t endPageNum;
2520 nsRect startRect;
2521 nsRect endRect;
2523 nsRefPtr<Selection> selectionPS =
2524 poPresShell->GetCurrentSelection(nsISelectionController::SELECTION_NORMAL);
2526 rv = GetPageRangeForSelection(poPresShell, poPresContext, *rc, selectionPS, pageSequence,
2527 &startFrame, startPageNum, startRect,
2528 &endFrame, endPageNum, endRect);
2529 if (NS_SUCCEEDED(rv)) {
2530 mPrt->mPrintSettings->SetStartPageRange(startPageNum);
2531 mPrt->mPrintSettings->SetEndPageRange(endPageNum);
2532 nsIntMargin marginTwips(0,0,0,0);
2533 nsIntMargin unwrtMarginTwips(0,0,0,0);
2534 mPrt->mPrintSettings->GetMarginInTwips(marginTwips);
2535 mPrt->mPrintSettings->GetUnwriteableMarginInTwips(unwrtMarginTwips);
2536 nsMargin totalMargin = poPresContext->CSSTwipsToAppUnits(marginTwips +
2537 unwrtMarginTwips);
2538 if (startPageNum == endPageNum) {
2539 startRect.y -= totalMargin.top;
2540 endRect.y -= totalMargin.top;
2542 // Clip out selection regions above the top of the first page
2543 if (startRect.y < 0) {
2544 // Reduce height to be the height of the positive-territory
2545 // region of original rect
2546 startRect.height = std::max(0, startRect.YMost());
2547 startRect.y = 0;
2549 if (endRect.y < 0) {
2550 // Reduce height to be the height of the positive-territory
2551 // region of original rect
2552 endRect.height = std::max(0, endRect.YMost());
2553 endRect.y = 0;
2555 NS_ASSERTION(endRect.y >= startRect.y,
2556 "Selection end point should be after start point");
2557 NS_ASSERTION(startRect.height >= 0,
2558 "rect should have non-negative height.");
2559 NS_ASSERTION(endRect.height >= 0,
2560 "rect should have non-negative height.");
2562 nscoord selectionHgt = endRect.y + endRect.height - startRect.y;
2563 // XXX This is temporary fix for printing more than one page of a selection
2564 pageSequence->SetSelectionHeight(startRect.y * aPO->mZoomRatio,
2565 selectionHgt * aPO->mZoomRatio);
2567 // calc total pages by getting calculating the selection's height
2568 // and then dividing it by how page content frames will fit.
2569 nscoord pageWidth, pageHeight;
2570 mPrt->mPrintDC->GetDeviceSurfaceDimensions(pageWidth, pageHeight);
2571 pageHeight -= totalMargin.top + totalMargin.bottom;
2572 int32_t totalPages = NSToIntCeil(float(selectionHgt) * aPO->mZoomRatio / float(pageHeight));
2573 pageSequence->SetTotalNumPages(totalPages);
2578 nsIFrame * seqFrame = do_QueryFrame(pageSequence);
2579 if (!seqFrame) {
2580 SetIsPrinting(false);
2581 return NS_ERROR_FAILURE;
2584 mPageSeqFrame = pageSequence;
2585 mPageSeqFrame->StartPrint(poPresContext, mPrt->mPrintSettings, docTitleStr, docURLStr);
2587 // Schedule Page to Print
2588 PR_PL(("Scheduling Print of PO: %p (%s) \n", aPO, gFrameTypesStr[aPO->mFrameType]));
2589 StartPagePrintTimer(aPO);
2593 return NS_OK;
2596 //---------------------------------------------------------------------
2597 void
2598 nsPrintEngine::SetDocAndURLIntoProgress(nsPrintObject* aPO,
2599 nsIPrintProgressParams* aParams)
2601 NS_ASSERTION(aPO, "Must have valid nsPrintObject");
2602 NS_ASSERTION(aParams, "Must have valid nsIPrintProgressParams");
2604 if (!aPO || !aPO->mDocShell || !aParams) {
2605 return;
2607 const uint32_t kTitleLength = 64;
2609 nsAutoString docTitleStr;
2610 nsAutoString docURLStr;
2611 GetDisplayTitleAndURL(aPO, docTitleStr, docURLStr, eDocTitleDefURLDoc);
2613 // Make sure the Titles & URLS don't get too long for the progress dialog
2614 EllipseLongString(docTitleStr, kTitleLength, false);
2615 EllipseLongString(docURLStr, kTitleLength, true);
2617 aParams->SetDocTitle(docTitleStr.get());
2618 aParams->SetDocURL(docURLStr.get());
2621 //---------------------------------------------------------------------
2622 void
2623 nsPrintEngine::EllipseLongString(nsAString& aStr, const uint32_t aLen, bool aDoFront)
2625 // Make sure the URLS don't get too long for the progress dialog
2626 if (aLen >= 3 && aStr.Length() > aLen) {
2627 if (aDoFront) {
2628 nsAutoString newStr;
2629 newStr.AppendLiteral("...");
2630 newStr += Substring(aStr, aStr.Length() - (aLen - 3), aLen - 3);
2631 aStr = newStr;
2632 } else {
2633 aStr.SetLength(aLen - 3);
2634 aStr.AppendLiteral("...");
2639 static bool
2640 DocHasPrintCallbackCanvas(nsIDocument* aDoc, void* aData)
2642 if (!aDoc) {
2643 return true;
2645 Element* root = aDoc->GetRootElement();
2646 if (!root) {
2647 return true;
2649 nsRefPtr<nsContentList> canvases = NS_GetContentList(root,
2650 kNameSpaceID_XHTML,
2651 NS_LITERAL_STRING("canvas"));
2652 uint32_t canvasCount = canvases->Length(true);
2653 for (uint32_t i = 0; i < canvasCount; ++i) {
2654 HTMLCanvasElement* canvas = HTMLCanvasElement::FromContentOrNull(canvases->Item(i, false));
2655 if (canvas && canvas->GetMozPrintCallback()) {
2656 // This subdocument has a print callback. Set result and return false to
2657 // stop iteration.
2658 *static_cast<bool*>(aData) = true;
2659 return false;
2662 return true;
2665 static bool
2666 DocHasPrintCallbackCanvas(nsIDocument* aDoc)
2668 bool result = false;
2669 aDoc->EnumerateSubDocuments(&DocHasPrintCallbackCanvas, static_cast<void*>(&result));
2670 return result;
2674 * Checks to see if the document this print engine is associated with has any
2675 * canvases that have a mozPrintCallback.
2677 bool
2678 nsPrintEngine::HasPrintCallbackCanvas()
2680 if (!mDocument) {
2681 return false;
2683 // First check this mDocument.
2684 bool result = false;
2685 DocHasPrintCallbackCanvas(mDocument, static_cast<void*>(&result));
2686 // Also check the sub documents.
2687 return result || DocHasPrintCallbackCanvas(mDocument);
2690 //-------------------------------------------------------
2691 bool
2692 nsPrintEngine::PrePrintPage()
2694 NS_ASSERTION(mPageSeqFrame, "mPageSeqFrame is null!");
2695 NS_ASSERTION(mPrt, "mPrt is null!");
2697 // Although these should NEVER be nullptr
2698 // This is added insurance, to make sure we don't crash in optimized builds
2699 if (!mPrt || !mPageSeqFrame) {
2700 return true; // means we are done preparing the page.
2703 // Check setting to see if someone request it be cancelled
2704 bool isCancelled = false;
2705 mPrt->mPrintSettings->GetIsCancelled(&isCancelled);
2706 if (isCancelled)
2707 return true;
2709 // Ask mPageSeqFrame if the page is ready to be printed.
2710 // If the page doesn't get printed at all, the |done| will be |true|.
2711 bool done = false;
2712 nsresult rv = mPageSeqFrame->PrePrintNextPage(mPagePrintTimer, &done);
2713 if (NS_FAILED(rv)) {
2714 // ??? ::PrintPage doesn't set |mPrt->mIsAborted = true| if rv != NS_ERROR_ABORT,
2715 // but I don't really understand why this should be the right thing to do?
2716 // Shouldn't |mPrt->mIsAborted| set to true all the time if something
2717 // wents wrong?
2718 if (rv != NS_ERROR_ABORT) {
2719 ShowPrintErrorDialog(rv);
2720 mPrt->mIsAborted = true;
2722 done = true;
2724 return done;
2727 bool
2728 nsPrintEngine::PrintPage(nsPrintObject* aPO,
2729 bool& aInRange)
2731 NS_ASSERTION(aPO, "aPO is null!");
2732 NS_ASSERTION(mPageSeqFrame, "mPageSeqFrame is null!");
2733 NS_ASSERTION(mPrt, "mPrt is null!");
2735 // Although these should NEVER be nullptr
2736 // This is added insurance, to make sure we don't crash in optimized builds
2737 if (!mPrt || !aPO || !mPageSeqFrame) {
2738 ShowPrintErrorDialog(NS_ERROR_FAILURE);
2739 return true; // means we are done printing
2742 PR_PL(("-----------------------------------\n"));
2743 PR_PL(("------ In DV::PrintPage PO: %p (%s)\n", aPO, gFrameTypesStr[aPO->mFrameType]));
2745 // Check setting to see if someone request it be cancelled
2746 bool isCancelled = false;
2747 mPrt->mPrintSettings->GetIsCancelled(&isCancelled);
2748 if (isCancelled || mPrt->mIsAborted)
2749 return true;
2751 int32_t pageNum, numPages, endPage;
2752 mPageSeqFrame->GetCurrentPageNum(&pageNum);
2753 mPageSeqFrame->GetNumPages(&numPages);
2755 bool donePrinting;
2756 bool isDoingPrintRange;
2757 mPageSeqFrame->IsDoingPrintRange(&isDoingPrintRange);
2758 if (isDoingPrintRange) {
2759 int32_t fromPage;
2760 int32_t toPage;
2761 mPageSeqFrame->GetPrintRange(&fromPage, &toPage);
2763 if (fromPage > numPages) {
2764 return true;
2766 if (toPage > numPages) {
2767 toPage = numPages;
2770 PR_PL(("****** Printing Page %d printing from %d to page %d\n", pageNum, fromPage, toPage));
2772 donePrinting = pageNum >= toPage;
2773 aInRange = pageNum >= fromPage && pageNum <= toPage;
2774 endPage = (toPage - fromPage)+1;
2775 } else {
2776 PR_PL(("****** Printing Page %d of %d page(s)\n", pageNum, numPages));
2778 donePrinting = pageNum >= numPages;
2779 endPage = numPages;
2780 aInRange = true;
2783 // XXX This is wrong, but the actual behavior in the presence of a print
2784 // range sucks.
2785 if (mPrt->mPrintFrameType == nsIPrintSettings::kEachFrameSep)
2786 endPage = mPrt->mNumPrintablePages;
2788 mPrt->DoOnProgressChange(++mPrt->mNumPagesPrinted, endPage, false, 0);
2790 // Print the Page
2791 // if a print job was cancelled externally, an EndPage or BeginPage may
2792 // fail and the failure is passed back here.
2793 // Returning true means we are done printing.
2795 // When rv == NS_ERROR_ABORT, it means we want out of the
2796 // print job without displaying any error messages
2797 nsresult rv = mPageSeqFrame->PrintNextPage();
2798 if (NS_FAILED(rv)) {
2799 if (rv != NS_ERROR_ABORT) {
2800 ShowPrintErrorDialog(rv);
2801 mPrt->mIsAborted = true;
2803 return true;
2806 mPageSeqFrame->DoPageEnd();
2808 return donePrinting;
2811 /** ---------------------------------------------------
2812 * Find by checking frames type
2814 nsresult
2815 nsPrintEngine::FindSelectionBoundsWithList(nsPresContext* aPresContext,
2816 nsRenderingContext& aRC,
2817 nsFrameList::Enumerator& aChildFrames,
2818 nsIFrame * aParentFrame,
2819 nsRect& aRect,
2820 nsIFrame *& aStartFrame,
2821 nsRect& aStartRect,
2822 nsIFrame *& aEndFrame,
2823 nsRect& aEndRect)
2825 NS_ASSERTION(aPresContext, "Pointer is null!");
2826 NS_ASSERTION(aParentFrame, "Pointer is null!");
2828 aRect += aParentFrame->GetPosition();
2829 for (; !aChildFrames.AtEnd(); aChildFrames.Next()) {
2830 nsIFrame* child = aChildFrames.get();
2831 if (child->IsSelected() && child->IsVisibleForPainting()) {
2832 nsRect r = child->GetRect();
2833 if (aStartFrame == nullptr) {
2834 aStartFrame = child;
2835 aStartRect.SetRect(aRect.x + r.x, aRect.y + r.y, r.width, r.height);
2836 } else {
2837 aEndFrame = child;
2838 aEndRect.SetRect(aRect.x + r.x, aRect.y + r.y, r.width, r.height);
2841 FindSelectionBounds(aPresContext, aRC, child, aRect, aStartFrame, aStartRect, aEndFrame, aEndRect);
2842 child = child->GetNextSibling();
2844 aRect -= aParentFrame->GetPosition();
2845 return NS_OK;
2848 //-------------------------------------------------------
2849 // Find the Frame that is XMost
2850 nsresult
2851 nsPrintEngine::FindSelectionBounds(nsPresContext* aPresContext,
2852 nsRenderingContext& aRC,
2853 nsIFrame * aParentFrame,
2854 nsRect& aRect,
2855 nsIFrame *& aStartFrame,
2856 nsRect& aStartRect,
2857 nsIFrame *& aEndFrame,
2858 nsRect& aEndRect)
2860 NS_ASSERTION(aPresContext, "Pointer is null!");
2861 NS_ASSERTION(aParentFrame, "Pointer is null!");
2863 // loop through named child lists
2864 nsIFrame::ChildListIterator lists(aParentFrame);
2865 for (; !lists.IsDone(); lists.Next()) {
2866 nsFrameList::Enumerator childFrames(lists.CurrentList());
2867 nsresult rv = FindSelectionBoundsWithList(aPresContext, aRC, childFrames, aParentFrame, aRect, aStartFrame, aStartRect, aEndFrame, aEndRect);
2868 NS_ENSURE_SUCCESS(rv, rv);
2870 return NS_OK;
2873 /** ---------------------------------------------------
2874 * This method finds the starting and ending page numbers
2875 * of the selection and also returns rect for each where
2876 * the x,y of the rect is relative to the very top of the
2877 * frame tree (absolutely positioned)
2879 nsresult
2880 nsPrintEngine::GetPageRangeForSelection(nsIPresShell * aPresShell,
2881 nsPresContext* aPresContext,
2882 nsRenderingContext& aRC,
2883 nsISelection* aSelection,
2884 nsIPageSequenceFrame* aPageSeqFrame,
2885 nsIFrame** aStartFrame,
2886 int32_t& aStartPageNum,
2887 nsRect& aStartRect,
2888 nsIFrame** aEndFrame,
2889 int32_t& aEndPageNum,
2890 nsRect& aEndRect)
2892 NS_ASSERTION(aPresShell, "Pointer is null!");
2893 NS_ASSERTION(aPresContext, "Pointer is null!");
2894 NS_ASSERTION(aSelection, "Pointer is null!");
2895 NS_ASSERTION(aPageSeqFrame, "Pointer is null!");
2896 NS_ASSERTION(aStartFrame, "Pointer is null!");
2897 NS_ASSERTION(aEndFrame, "Pointer is null!");
2899 nsIFrame * seqFrame = do_QueryFrame(aPageSeqFrame);
2900 if (!seqFrame) {
2901 return NS_ERROR_FAILURE;
2904 nsIFrame * startFrame = nullptr;
2905 nsIFrame * endFrame = nullptr;
2907 // start out with the sequence frame and search the entire frame tree
2908 // capturing the starting and ending child frames of the selection
2909 // and their rects
2910 nsRect r = seqFrame->GetRect();
2911 FindSelectionBounds(aPresContext, aRC, seqFrame, r,
2912 startFrame, aStartRect, endFrame, aEndRect);
2914 #ifdef DEBUG_rodsX
2915 printf("Start Frame: %p\n", startFrame);
2916 printf("End Frame: %p\n", endFrame);
2917 #endif
2919 // initial the page numbers here
2920 // in case we don't find and frames
2921 aStartPageNum = -1;
2922 aEndPageNum = -1;
2924 nsIFrame * startPageFrame;
2925 nsIFrame * endPageFrame;
2927 // check to make sure we found a starting frame
2928 if (startFrame != nullptr) {
2929 // Now search up the tree to find what page the
2930 // start/ending selections frames are on
2932 // Check to see if start should be same as end if
2933 // the end frame comes back null
2934 if (endFrame == nullptr) {
2935 // XXX the "GetPageFrame" step could be integrated into
2936 // the FindSelectionBounds step, but walking up to find
2937 // the parent of a child frame isn't expensive and it makes
2938 // FindSelectionBounds a little easier to understand
2939 startPageFrame = nsLayoutUtils::GetPageFrame(startFrame);
2940 endPageFrame = startPageFrame;
2941 aEndRect = aStartRect;
2942 } else {
2943 startPageFrame = nsLayoutUtils::GetPageFrame(startFrame);
2944 endPageFrame = nsLayoutUtils::GetPageFrame(endFrame);
2946 } else {
2947 return NS_ERROR_FAILURE;
2950 #ifdef DEBUG_rodsX
2951 printf("Start Page: %p\n", startPageFrame);
2952 printf("End Page: %p\n", endPageFrame);
2954 // dump all the pages and their pointers
2956 int32_t pageNum = 1;
2957 nsIFrame* child = seqFrame->GetFirstPrincipalChild();
2958 while (child != nullptr) {
2959 printf("Page: %d - %p\n", pageNum, child);
2960 pageNum++;
2961 child = child->GetNextSibling();
2964 #endif
2966 // Now that we have the page frames
2967 // find out what the page numbers are for each frame
2968 int32_t pageNum = 1;
2969 nsIFrame* page = seqFrame->GetFirstPrincipalChild();
2970 while (page != nullptr) {
2971 if (page == startPageFrame) {
2972 aStartPageNum = pageNum;
2974 if (page == endPageFrame) {
2975 aEndPageNum = pageNum;
2977 pageNum++;
2978 page = page->GetNextSibling();
2981 #ifdef DEBUG_rodsX
2982 printf("Start Page No: %d\n", aStartPageNum);
2983 printf("End Page No: %d\n", aEndPageNum);
2984 #endif
2986 *aStartFrame = startPageFrame;
2987 *aEndFrame = endPageFrame;
2989 return NS_OK;
2992 //-----------------------------------------------------------------
2993 //-- Done: Printing Methods
2994 //-----------------------------------------------------------------
2997 //-----------------------------------------------------------------
2998 //-- Section: Misc Support Methods
2999 //-----------------------------------------------------------------
3001 //---------------------------------------------------------------------
3002 void nsPrintEngine::SetIsPrinting(bool aIsPrinting)
3004 mIsDoingPrinting = aIsPrinting;
3005 // Calling SetIsPrinting while in print preview confuses the document viewer
3006 // This is safe because we prevent exiting print preview while printing
3007 if (!mIsDoingPrintPreview && mDocViewerPrint) {
3008 mDocViewerPrint->SetIsPrinting(aIsPrinting);
3010 if (mPrt && aIsPrinting) {
3011 mPrt->mPreparingForPrint = true;
3015 //---------------------------------------------------------------------
3016 void nsPrintEngine::SetIsPrintPreview(bool aIsPrintPreview)
3018 mIsDoingPrintPreview = aIsPrintPreview;
3020 if (mDocViewerPrint) {
3021 mDocViewerPrint->SetIsPrintPreview(aIsPrintPreview);
3025 //---------------------------------------------------------------------
3026 void
3027 nsPrintEngine::CleanupDocTitleArray(char16_t**& aArray, int32_t& aCount)
3029 for (int32_t i = aCount - 1; i >= 0; i--) {
3030 nsMemory::Free(aArray[i]);
3032 nsMemory::Free(aArray);
3033 aArray = nullptr;
3034 aCount = 0;
3037 //---------------------------------------------------------------------
3038 // static
3039 bool nsPrintEngine::HasFramesetChild(nsIContent* aContent)
3041 if (!aContent) {
3042 return false;
3045 // do a breadth search across all siblings
3046 for (nsIContent* child = aContent->GetFirstChild();
3047 child;
3048 child = child->GetNextSibling()) {
3049 if (child->IsHTML(nsGkAtoms::frameset)) {
3050 return true;
3054 return false;
3059 /** ---------------------------------------------------
3060 * Get the Focused Frame for a documentviewer
3062 already_AddRefed<nsIDOMWindow>
3063 nsPrintEngine::FindFocusedDOMWindow()
3065 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
3066 NS_ENSURE_TRUE(fm, nullptr);
3068 nsCOMPtr<nsPIDOMWindow> window(mDocument->GetWindow());
3069 NS_ENSURE_TRUE(window, nullptr);
3071 nsCOMPtr<nsPIDOMWindow> rootWindow = window->GetPrivateRoot();
3072 NS_ENSURE_TRUE(rootWindow, nullptr);
3074 nsCOMPtr<nsPIDOMWindow> focusedWindow;
3075 nsFocusManager::GetFocusedDescendant(rootWindow, true,
3076 getter_AddRefs(focusedWindow));
3077 NS_ENSURE_TRUE(focusedWindow, nullptr);
3079 if (IsWindowsInOurSubTree(focusedWindow)) {
3080 return focusedWindow.forget();
3083 return nullptr;
3086 //---------------------------------------------------------------------
3087 bool
3088 nsPrintEngine::IsWindowsInOurSubTree(nsPIDOMWindow * window)
3090 bool found = false;
3092 // now check to make sure it is in "our" tree of docshells
3093 if (window) {
3094 nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
3096 if (docShell) {
3097 // get this DocViewer docshell
3098 nsCOMPtr<nsIDocShell> thisDVDocShell(do_QueryReferent(mContainer));
3099 while (!found) {
3100 if (docShell) {
3101 if (docShell == thisDVDocShell) {
3102 found = true;
3103 break;
3105 } else {
3106 break; // at top of tree
3108 nsCOMPtr<nsIDocShellTreeItem> docShellItemParent;
3109 docShell->GetSameTypeParent(getter_AddRefs(docShellItemParent));
3110 docShell = do_QueryInterface(docShellItemParent);
3111 } // while
3113 } // scriptobj
3115 return found;
3118 //-------------------------------------------------------
3119 bool
3120 nsPrintEngine::DonePrintingPages(nsPrintObject* aPO, nsresult aResult)
3122 //NS_ASSERTION(aPO, "Pointer is null!");
3123 PR_PL(("****** In DV::DonePrintingPages PO: %p (%s)\n", aPO, aPO?gFrameTypesStr[aPO->mFrameType]:""));
3125 // If there is a pageSeqFrame, make sure there are no more printCanvas active
3126 // that might call |Notify| on the pagePrintTimer after things are cleaned up
3127 // and printing was marked as being done.
3128 if (mPageSeqFrame) {
3129 mPageSeqFrame->ResetPrintCanvasList();
3132 if (aPO && !mPrt->mIsAborted) {
3133 aPO->mHasBeenPrinted = true;
3134 nsresult rv;
3135 bool didPrint = PrintDocContent(mPrt->mPrintObject, rv);
3136 if (NS_SUCCEEDED(rv) && didPrint) {
3137 PR_PL(("****** In DV::DonePrintingPages PO: %p (%s) didPrint:%s (Not Done Printing)\n", aPO, gFrameTypesStr[aPO->mFrameType], PRT_YESNO(didPrint)));
3138 return false;
3142 if (NS_SUCCEEDED(aResult)) {
3143 FirePrintCompletionEvent();
3146 TurnScriptingOn(true);
3147 SetIsPrinting(false);
3149 // Release reference to mPagePrintTimer; the timer object destroys itself
3150 // after this returns true
3151 NS_IF_RELEASE(mPagePrintTimer);
3153 return true;
3156 //-------------------------------------------------------
3157 // Recursively sets the PO items to be printed "As Is"
3158 // from the given item down into the tree
3159 void
3160 nsPrintEngine::SetPrintAsIs(nsPrintObject* aPO, bool aAsIs)
3162 NS_ASSERTION(aPO, "Pointer is null!");
3164 aPO->mPrintAsIs = aAsIs;
3165 for (uint32_t i=0;i<aPO->mKids.Length();i++) {
3166 SetPrintAsIs(aPO->mKids[i], aAsIs);
3170 //-------------------------------------------------------
3171 // Given a DOMWindow it recursively finds the PO object that matches
3172 nsPrintObject*
3173 nsPrintEngine::FindPrintObjectByDOMWin(nsPrintObject* aPO,
3174 nsIDOMWindow* aDOMWin)
3176 NS_ASSERTION(aPO, "Pointer is null!");
3178 // Often the CurFocused DOMWindow is passed in
3179 // andit is valid for it to be null, so short circut
3180 if (!aDOMWin) {
3181 return nullptr;
3184 nsCOMPtr<nsIDOMDocument> domDoc;
3185 aDOMWin->GetDocument(getter_AddRefs(domDoc));
3186 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
3187 if (aPO->mDocument && aPO->mDocument->GetOriginalDocument() == doc) {
3188 return aPO;
3191 int32_t cnt = aPO->mKids.Length();
3192 for (int32_t i = 0; i < cnt; ++i) {
3193 nsPrintObject* po = FindPrintObjectByDOMWin(aPO->mKids[i], aDOMWin);
3194 if (po) {
3195 return po;
3199 return nullptr;
3202 //-------------------------------------------------------
3203 nsresult
3204 nsPrintEngine::EnablePOsForPrinting()
3206 // NOTE: All POs have been "turned off" for printing
3207 // this is where we decided which POs get printed.
3208 mPrt->mSelectedPO = nullptr;
3210 if (mPrt->mPrintSettings == nullptr) {
3211 return NS_ERROR_FAILURE;
3214 mPrt->mPrintFrameType = nsIPrintSettings::kNoFrames;
3215 mPrt->mPrintSettings->GetPrintFrameType(&mPrt->mPrintFrameType);
3217 int16_t printHowEnable = nsIPrintSettings::kFrameEnableNone;
3218 mPrt->mPrintSettings->GetHowToEnableFrameUI(&printHowEnable);
3220 int16_t printRangeType = nsIPrintSettings::kRangeAllPages;
3221 mPrt->mPrintSettings->GetPrintRange(&printRangeType);
3223 PR_PL(("\n"));
3224 PR_PL(("********* nsPrintEngine::EnablePOsForPrinting *********\n"));
3225 PR_PL(("PrintFrameType: %s \n", gPrintFrameTypeStr[mPrt->mPrintFrameType]));
3226 PR_PL(("HowToEnableFrameUI: %s \n", gFrameHowToEnableStr[printHowEnable]));
3227 PR_PL(("PrintRange: %s \n", gPrintRangeStr[printRangeType]));
3228 PR_PL(("----\n"));
3230 // ***** This is the ultimate override *****
3231 // if we are printing the selection (either an IFrame or selection range)
3232 // then set the mPrintFrameType as if it were the selected frame
3233 if (printRangeType == nsIPrintSettings::kRangeSelection) {
3234 mPrt->mPrintFrameType = nsIPrintSettings::kSelectedFrame;
3235 printHowEnable = nsIPrintSettings::kFrameEnableNone;
3238 // This tells us that the "Frame" UI has turned off,
3239 // so therefore there are no FrameSets/Frames/IFrames to be printed
3241 // This means there are not FrameSets,
3242 // but the document could contain an IFrame
3243 if (printHowEnable == nsIPrintSettings::kFrameEnableNone) {
3245 // Print all the pages or a sub range of pages
3246 if (printRangeType == nsIPrintSettings::kRangeAllPages ||
3247 printRangeType == nsIPrintSettings::kRangeSpecifiedPageRange) {
3248 SetPrintPO(mPrt->mPrintObject, true);
3250 // Set the children so they are PrinAsIs
3251 // In this case, the children are probably IFrames
3252 if (mPrt->mPrintObject->mKids.Length() > 0) {
3253 for (uint32_t i=0;i<mPrt->mPrintObject->mKids.Length();i++) {
3254 nsPrintObject* po = mPrt->mPrintObject->mKids[i];
3255 NS_ASSERTION(po, "nsPrintObject can't be null!");
3256 SetPrintAsIs(po);
3259 // ***** Another override *****
3260 mPrt->mPrintFrameType = nsIPrintSettings::kFramesAsIs;
3262 PR_PL(("PrintFrameType: %s \n", gPrintFrameTypeStr[mPrt->mPrintFrameType]));
3263 PR_PL(("HowToEnableFrameUI: %s \n", gFrameHowToEnableStr[printHowEnable]));
3264 PR_PL(("PrintRange: %s \n", gPrintRangeStr[printRangeType]));
3265 return NS_OK;
3268 // This means we are either printed a selected IFrame or
3269 // we are printing the current selection
3270 if (printRangeType == nsIPrintSettings::kRangeSelection) {
3272 // If the currentFocusDOMWin can'r be null if something is selected
3273 if (mPrt->mCurrentFocusWin) {
3274 // Find the selected IFrame
3275 nsPrintObject * po = FindPrintObjectByDOMWin(mPrt->mPrintObject, mPrt->mCurrentFocusWin);
3276 if (po != nullptr) {
3277 mPrt->mSelectedPO = po;
3278 // Makes sure all of its children are be printed "AsIs"
3279 SetPrintAsIs(po);
3281 // Now, only enable this POs (the selected PO) and all of its children
3282 SetPrintPO(po, true);
3284 // check to see if we have a range selection,
3285 // as oppose to a insert selection
3286 // this means if the user just clicked on the IFrame then
3287 // there will not be a selection so we want the entire page to print
3289 // XXX this is sort of a hack right here to make the page
3290 // not try to reposition itself when printing selection
3291 nsCOMPtr<nsIDOMWindow> domWin =
3292 do_QueryInterface(po->mDocument->GetOriginalDocument()->GetWindow());
3293 if (!IsThereARangeSelection(domWin)) {
3294 printRangeType = nsIPrintSettings::kRangeAllPages;
3295 mPrt->mPrintSettings->SetPrintRange(printRangeType);
3297 PR_PL(("PrintFrameType: %s \n", gPrintFrameTypeStr[mPrt->mPrintFrameType]));
3298 PR_PL(("HowToEnableFrameUI: %s \n", gFrameHowToEnableStr[printHowEnable]));
3299 PR_PL(("PrintRange: %s \n", gPrintRangeStr[printRangeType]));
3300 return NS_OK;
3302 } else {
3303 for (uint32_t i=0;i<mPrt->mPrintDocList.Length();i++) {
3304 nsPrintObject* po = mPrt->mPrintDocList.ElementAt(i);
3305 NS_ASSERTION(po, "nsPrintObject can't be null!");
3306 nsCOMPtr<nsIDOMWindow> domWin = po->mDocShell->GetWindow();
3307 if (IsThereARangeSelection(domWin)) {
3308 mPrt->mCurrentFocusWin = domWin;
3309 SetPrintPO(po, true);
3310 break;
3313 return NS_OK;
3318 // check to see if there is a selection when a FrameSet is present
3319 if (printRangeType == nsIPrintSettings::kRangeSelection) {
3320 // If the currentFocusDOMWin can'r be null if something is selected
3321 if (mPrt->mCurrentFocusWin) {
3322 // Find the selected IFrame
3323 nsPrintObject * po = FindPrintObjectByDOMWin(mPrt->mPrintObject, mPrt->mCurrentFocusWin);
3324 if (po != nullptr) {
3325 mPrt->mSelectedPO = po;
3326 // Makes sure all of its children are be printed "AsIs"
3327 SetPrintAsIs(po);
3329 // Now, only enable this POs (the selected PO) and all of its children
3330 SetPrintPO(po, true);
3332 // check to see if we have a range selection,
3333 // as oppose to a insert selection
3334 // this means if the user just clicked on the IFrame then
3335 // there will not be a selection so we want the entire page to print
3337 // XXX this is sort of a hack right here to make the page
3338 // not try to reposition itself when printing selection
3339 nsCOMPtr<nsIDOMWindow> domWin =
3340 do_QueryInterface(po->mDocument->GetOriginalDocument()->GetWindow());
3341 if (!IsThereARangeSelection(domWin)) {
3342 printRangeType = nsIPrintSettings::kRangeAllPages;
3343 mPrt->mPrintSettings->SetPrintRange(printRangeType);
3345 PR_PL(("PrintFrameType: %s \n", gPrintFrameTypeStr[mPrt->mPrintFrameType]));
3346 PR_PL(("HowToEnableFrameUI: %s \n", gFrameHowToEnableStr[printHowEnable]));
3347 PR_PL(("PrintRange: %s \n", gPrintRangeStr[printRangeType]));
3348 return NS_OK;
3353 // If we are printing "AsIs" then sets all the POs to be printed as is
3354 if (mPrt->mPrintFrameType == nsIPrintSettings::kFramesAsIs) {
3355 SetPrintAsIs(mPrt->mPrintObject);
3356 SetPrintPO(mPrt->mPrintObject, true);
3357 return NS_OK;
3360 // If we are printing the selected Frame then
3361 // find that PO for that selected DOMWin and set it all of its
3362 // children to be printed
3363 if (mPrt->mPrintFrameType == nsIPrintSettings::kSelectedFrame) {
3365 if ((mPrt->mIsParentAFrameSet && mPrt->mCurrentFocusWin) || mPrt->mIsIFrameSelected) {
3366 nsPrintObject * po = FindPrintObjectByDOMWin(mPrt->mPrintObject, mPrt->mCurrentFocusWin);
3367 if (po != nullptr) {
3368 mPrt->mSelectedPO = po;
3369 // NOTE: Calling this sets the "po" and
3370 // we don't want to do this for documents that have no children,
3371 // because then the "DoEndPage" gets called and it shouldn't
3372 if (po->mKids.Length() > 0) {
3373 // Makes sure that itself, and all of its children are printed "AsIs"
3374 SetPrintAsIs(po);
3377 // Now, only enable this POs (the selected PO) and all of its children
3378 SetPrintPO(po, true);
3381 return NS_OK;
3384 // If we are print each subdoc separately,
3385 // then don't print any of the FraneSet Docs
3386 if (mPrt->mPrintFrameType == nsIPrintSettings::kEachFrameSep) {
3387 SetPrintPO(mPrt->mPrintObject, true);
3388 int32_t cnt = mPrt->mPrintDocList.Length();
3389 for (int32_t i=0;i<cnt;i++) {
3390 nsPrintObject* po = mPrt->mPrintDocList.ElementAt(i);
3391 NS_ASSERTION(po, "nsPrintObject can't be null!");
3392 if (po->mFrameType == eFrameSet) {
3393 po->mDontPrint = true;
3398 return NS_OK;
3401 //-------------------------------------------------------
3402 // Return the nsPrintObject with that is XMost (The widest frameset frame) AND
3403 // contains the XMost (widest) layout frame
3404 nsPrintObject*
3405 nsPrintEngine::FindSmallestSTF()
3407 float smallestRatio = 1.0f;
3408 nsPrintObject* smallestPO = nullptr;
3410 for (uint32_t i=0;i<mPrt->mPrintDocList.Length();i++) {
3411 nsPrintObject* po = mPrt->mPrintDocList.ElementAt(i);
3412 NS_ASSERTION(po, "nsPrintObject can't be null!");
3413 if (po->mFrameType != eFrameSet && po->mFrameType != eIFrame) {
3414 if (po->mShrinkRatio < smallestRatio) {
3415 smallestRatio = po->mShrinkRatio;
3416 smallestPO = po;
3421 #ifdef EXTENDED_DEBUG_PRINTING
3422 if (smallestPO) printf("*PO: %p Type: %d %10.3f\n", smallestPO, smallestPO->mFrameType, smallestPO->mShrinkRatio);
3423 #endif
3424 return smallestPO;
3427 //-------------------------------------------------------
3428 void
3429 nsPrintEngine::TurnScriptingOn(bool aDoTurnOn)
3431 if (mIsDoingPrinting && aDoTurnOn && mDocViewerPrint &&
3432 mDocViewerPrint->GetIsPrintPreview()) {
3433 // We don't want to turn scripting on if print preview is shown still after
3434 // printing.
3435 return;
3438 nsPrintData* prt = mPrt;
3439 #ifdef NS_PRINT_PREVIEW
3440 if (!prt) {
3441 prt = mPrtPreview;
3443 #endif
3444 if (!prt) {
3445 return;
3448 NS_ASSERTION(mDocument, "We MUST have a document.");
3449 // First, get the script global object from the document...
3451 for (uint32_t i=0;i<prt->mPrintDocList.Length();i++) {
3452 nsPrintObject* po = prt->mPrintDocList.ElementAt(i);
3453 NS_ASSERTION(po, "nsPrintObject can't be null!");
3455 nsIDocument* doc = po->mDocument;
3456 if (!doc) {
3457 continue;
3460 if (nsCOMPtr<nsPIDOMWindow> window = doc->GetInnerWindow()) {
3461 nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(window);
3462 NS_WARN_IF_FALSE(go && go->GetGlobalJSObject(), "Can't get global");
3463 nsresult propThere = NS_PROPTABLE_PROP_NOT_THERE;
3464 doc->GetProperty(nsGkAtoms::scriptEnabledBeforePrintOrPreview,
3465 &propThere);
3466 if (aDoTurnOn) {
3467 if (propThere != NS_PROPTABLE_PROP_NOT_THERE) {
3468 doc->DeleteProperty(nsGkAtoms::scriptEnabledBeforePrintOrPreview);
3469 if (go && go->GetGlobalJSObject()) {
3470 xpc::Scriptability::Get(go->GetGlobalJSObject()).Unblock();
3472 window->ResumeTimeouts(false);
3474 } else {
3475 // Have to be careful, because people call us over and over again with
3476 // aDoTurnOn == false. So don't set the property if it's already
3477 // set, since in that case we'd set it to the wrong value.
3478 if (propThere == NS_PROPTABLE_PROP_NOT_THERE) {
3479 // Stash the current value of IsScriptEnabled on the document, so
3480 // that layout code running in print preview doesn't get confused.
3481 doc->SetProperty(nsGkAtoms::scriptEnabledBeforePrintOrPreview,
3482 NS_INT32_TO_PTR(doc->IsScriptEnabled()));
3483 if (go && go->GetGlobalJSObject()) {
3484 xpc::Scriptability::Get(go->GetGlobalJSObject()).Block();
3486 window->SuspendTimeouts(1, false);
3493 //-----------------------------------------------------------------
3494 //-- Done: Misc Support Methods
3495 //-----------------------------------------------------------------
3498 //-----------------------------------------------------------------
3499 //-- Section: Finishing up or Cleaning up
3500 //-----------------------------------------------------------------
3502 //-----------------------------------------------------------------
3503 void
3504 nsPrintEngine::CloseProgressDialog(nsIWebProgressListener* aWebProgressListener)
3506 if (aWebProgressListener) {
3507 aWebProgressListener->OnStateChange(nullptr, nullptr, nsIWebProgressListener::STATE_STOP|nsIWebProgressListener::STATE_IS_DOCUMENT, NS_OK);
3511 //-----------------------------------------------------------------
3512 nsresult
3513 nsPrintEngine::FinishPrintPreview()
3515 nsresult rv = NS_OK;
3517 #ifdef NS_PRINT_PREVIEW
3519 if (!mPrt) {
3520 /* we're already finished with print preview */
3521 return rv;
3524 rv = DocumentReadyForPrinting();
3526 SetIsCreatingPrintPreview(false);
3528 /* cleaup on failure + notify user */
3529 if (NS_FAILED(rv)) {
3530 /* cleanup done, let's fire-up an error dialog to notify the user
3531 * what went wrong...
3533 mPrt->OnEndPrinting();
3534 TurnScriptingOn(true);
3536 return rv;
3539 // At this point we are done preparing everything
3540 // before it is to be created
3543 if (mIsDoingPrintPreview && mOldPrtPreview) {
3544 delete mOldPrtPreview;
3545 mOldPrtPreview = nullptr;
3549 mPrt->OnEndPrinting();
3551 // PrintPreview was built using the mPrt (code reuse)
3552 // then we assign it over
3553 mPrtPreview = mPrt;
3554 mPrt = nullptr;
3556 #endif // NS_PRINT_PREVIEW
3558 return NS_OK;
3561 //-----------------------------------------------------------------
3562 //-- Done: Finishing up or Cleaning up
3563 //-----------------------------------------------------------------
3566 /*=============== Timer Related Code ======================*/
3567 nsresult
3568 nsPrintEngine::StartPagePrintTimer(nsPrintObject* aPO)
3570 if (!mPagePrintTimer) {
3571 // Get the delay time in between the printing of each page
3572 // this gives the user more time to press cancel
3573 int32_t printPageDelay = 50;
3574 mPrt->mPrintSettings->GetPrintPageDelay(&printPageDelay);
3576 nsRefPtr<nsPagePrintTimer> timer =
3577 new nsPagePrintTimer(this, mDocViewerPrint, printPageDelay);
3578 timer.forget(&mPagePrintTimer);
3581 return mPagePrintTimer->Start(aPO);
3584 /*=============== nsIObserver Interface ======================*/
3585 NS_IMETHODIMP
3586 nsPrintEngine::Observe(nsISupports *aSubject, const char *aTopic, const char16_t *aData)
3588 nsresult rv = NS_ERROR_FAILURE;
3590 rv = InitPrintDocConstruction(true);
3591 if (!mIsDoingPrinting && mPrtPreview) {
3592 mPrtPreview->OnEndPrinting();
3595 return rv;
3599 //---------------------------------------------------------------
3600 //-- PLEvent Notification
3601 //---------------------------------------------------------------
3602 class nsPrintCompletionEvent : public nsRunnable {
3603 public:
3604 explicit nsPrintCompletionEvent(nsIDocumentViewerPrint *docViewerPrint)
3605 : mDocViewerPrint(docViewerPrint) {
3606 NS_ASSERTION(mDocViewerPrint, "mDocViewerPrint is null.");
3609 NS_IMETHOD Run() MOZ_OVERRIDE {
3610 if (mDocViewerPrint)
3611 mDocViewerPrint->OnDonePrinting();
3612 return NS_OK;
3615 private:
3616 nsCOMPtr<nsIDocumentViewerPrint> mDocViewerPrint;
3619 //-----------------------------------------------------------
3620 void
3621 nsPrintEngine::FirePrintCompletionEvent()
3623 nsCOMPtr<nsIRunnable> event = new nsPrintCompletionEvent(mDocViewerPrint);
3624 if (NS_FAILED(NS_DispatchToCurrentThread(event)))
3625 NS_WARNING("failed to dispatch print completion event");
3628 //---------------------------------------------------------------
3629 //---------------------------------------------------------------
3630 //-- Debug helper routines
3631 //---------------------------------------------------------------
3632 //---------------------------------------------------------------
3633 #if defined(XP_WIN) && defined(EXTENDED_DEBUG_PRINTING)
3634 #include "windows.h"
3635 #include "process.h"
3636 #include "direct.h"
3638 #define MY_FINDFIRST(a,b) FindFirstFile(a,b)
3639 #define MY_FINDNEXT(a,b) FindNextFile(a,b)
3640 #define ISDIR(a) (a.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
3641 #define MY_FINDCLOSE(a) FindClose(a)
3642 #define MY_FILENAME(a) a.cFileName
3643 #define MY_FILESIZE(a) (a.nFileSizeHigh * MAXDWORD) + a.nFileSizeLow
3645 int RemoveFilesInDir(const char * aDir)
3647 WIN32_FIND_DATA data_ptr;
3648 HANDLE find_handle;
3650 char path[MAX_PATH];
3652 strcpy(path, aDir);
3654 // Append slash to the end of the directory names if not there
3655 if (path[strlen(path)-1] != '\\')
3656 strcat(path, "\\");
3658 char findPath[MAX_PATH];
3659 strcpy(findPath, path);
3660 strcat(findPath, "*.*");
3662 find_handle = MY_FINDFIRST(findPath, &data_ptr);
3664 if (find_handle != INVALID_HANDLE_VALUE) {
3665 do {
3666 if (ISDIR(data_ptr)
3667 && (stricmp(MY_FILENAME(data_ptr),"."))
3668 && (stricmp(MY_FILENAME(data_ptr),".."))) {
3669 // skip
3671 else if (!ISDIR(data_ptr)) {
3672 if (!strncmp(MY_FILENAME(data_ptr), "print_dump", 10)) {
3673 char fileName[MAX_PATH];
3674 strcpy(fileName, aDir);
3675 strcat(fileName, "\\");
3676 strcat(fileName, MY_FILENAME(data_ptr));
3677 printf("Removing %s\n", fileName);
3678 remove(fileName);
3681 } while(MY_FINDNEXT(find_handle,&data_ptr));
3682 MY_FINDCLOSE(find_handle);
3684 return TRUE;
3686 #endif
3688 #ifdef EXTENDED_DEBUG_PRINTING
3690 /** ---------------------------------------------------
3691 * Dumps Frames for Printing
3693 static void RootFrameList(nsPresContext* aPresContext, FILE* out, int32_t aIndent)
3695 if (!aPresContext || !out)
3696 return;
3698 nsIPresShell *shell = aPresContext->GetPresShell();
3699 if (shell) {
3700 nsIFrame* frame = shell->FrameManager()->GetRootFrame();
3701 if (frame) {
3702 frame->List(aPresContext, out, aIndent);
3707 /** ---------------------------------------------------
3708 * Dumps Frames for Printing
3710 static void DumpFrames(FILE* out,
3711 nsPresContext* aPresContext,
3712 nsRenderingContext * aRendContext,
3713 nsIFrame * aFrame,
3714 int32_t aLevel)
3716 NS_ASSERTION(out, "Pointer is null!");
3717 NS_ASSERTION(aPresContext, "Pointer is null!");
3718 NS_ASSERTION(aRendContext, "Pointer is null!");
3719 NS_ASSERTION(aFrame, "Pointer is null!");
3721 nsIFrame* child = aFrame->GetFirstPrincipalChild();
3722 while (child != nullptr) {
3723 for (int32_t i=0;i<aLevel;i++) {
3724 fprintf(out, " ");
3726 nsAutoString tmp;
3727 child->GetFrameName(tmp);
3728 fputs(NS_LossyConvertUTF16toASCII(tmp).get(), out);
3729 bool isSelected;
3730 if (NS_SUCCEEDED(child->IsVisibleForPainting(aPresContext, *aRendContext, true, &isSelected))) {
3731 fprintf(out, " %p %s", child, isSelected?"VIS":"UVS");
3732 nsRect rect = child->GetRect();
3733 fprintf(out, "[%d,%d,%d,%d] ", rect.x, rect.y, rect.width, rect.height);
3734 fprintf(out, "v: %p ", (void*)child->GetView());
3735 fprintf(out, "\n");
3736 DumpFrames(out, aPresContext, aRendContext, child, aLevel+1);
3737 child = child->GetNextSibling();
3743 /** ---------------------------------------------------
3744 * Dumps the Views from the DocShell
3746 static void
3747 DumpViews(nsIDocShell* aDocShell, FILE* out)
3749 NS_ASSERTION(aDocShell, "Pointer is null!");
3750 NS_ASSERTION(out, "Pointer is null!");
3752 if (nullptr != aDocShell) {
3753 fprintf(out, "docshell=%p \n", aDocShell);
3754 nsIPresShell* shell = nsPrintEngine::GetPresShellFor(aDocShell);
3755 if (shell) {
3756 nsViewManager* vm = shell->GetViewManager();
3757 if (vm) {
3758 nsView* root = vm->GetRootView();
3759 if (root) {
3760 root->List(out);
3764 else {
3765 fputs("null pres shell\n", out);
3768 // dump the views of the sub documents
3769 int32_t i, n;
3770 aDocShell->GetChildCount(&n);
3771 for (i = 0; i < n; i++) {
3772 nsCOMPtr<nsIDocShellTreeItem> child;
3773 aDocShell->GetChildAt(i, getter_AddRefs(child));
3774 nsCOMPtr<nsIDocShell> childAsShell(do_QueryInterface(child));
3775 if (childAsShell) {
3776 DumpViews(childAsShell, out);
3782 /** ---------------------------------------------------
3783 * Dumps the Views and Frames
3785 void DumpLayoutData(char* aTitleStr,
3786 char* aURLStr,
3787 nsPresContext* aPresContext,
3788 nsDeviceContext * aDC,
3789 nsIFrame * aRootFrame,
3790 nsIDocShekk * aDocShell,
3791 FILE* aFD = nullptr)
3793 if (!kPrintingLogMod || kPrintingLogMod->level != DUMP_LAYOUT_LEVEL) return;
3795 if (aPresContext == nullptr || aDC == nullptr) {
3796 return;
3799 #ifdef NS_PRINT_PREVIEW
3800 if (aPresContext->Type() == nsPresContext::eContext_PrintPreview) {
3801 return;
3803 #endif
3805 NS_ASSERTION(aRootFrame, "Pointer is null!");
3806 NS_ASSERTION(aDocShell, "Pointer is null!");
3808 // Dump all the frames and view to a a file
3809 char filename[256];
3810 sprintf(filename, "print_dump_layout_%d.txt", gDumpLOFileNameCnt++);
3811 FILE * fd = aFD?aFD:fopen(filename, "w");
3812 if (fd) {
3813 fprintf(fd, "Title: %s\n", aTitleStr?aTitleStr:"");
3814 fprintf(fd, "URL: %s\n", aURLStr?aURLStr:"");
3815 fprintf(fd, "--------------- Frames ----------------\n");
3816 fprintf(fd, "--------------- Frames ----------------\n");
3817 nsRefPtr<nsRenderingContext> renderingContext =
3818 aDC->CreateRenderingContext();
3819 RootFrameList(aPresContext, fd, 0);
3820 //DumpFrames(fd, aPresContext, renderingContext, aRootFrame, 0);
3821 fprintf(fd, "---------------------------------------\n\n");
3822 fprintf(fd, "--------------- Views From Root Frame----------------\n");
3823 nsView* v = aRootFrame->GetView();
3824 if (v) {
3825 v->List(fd);
3826 } else {
3827 printf("View is null!\n");
3829 if (aDocShell) {
3830 fprintf(fd, "--------------- All Views ----------------\n");
3831 DumpViews(aDocShell, fd);
3832 fprintf(fd, "---------------------------------------\n\n");
3834 if (aFD == nullptr) {
3835 fclose(fd);
3840 //-------------------------------------------------------------
3841 static void DumpPrintObjectsList(nsTArray<nsPrintObject*> * aDocList)
3843 if (!kPrintingLogMod || kPrintingLogMod->level != DUMP_LAYOUT_LEVEL) return;
3845 NS_ASSERTION(aDocList, "Pointer is null!");
3847 const char types[][3] = {"DC", "FR", "IF", "FS"};
3848 PR_PL(("Doc List\n***************************************************\n"));
3849 PR_PL(("T P A H PO DocShell Seq Page Root Page# Rect\n"));
3850 int32_t cnt = aDocList->Length();
3851 for (int32_t i=0;i<cnt;i++) {
3852 nsPrintObject* po = aDocList->ElementAt(i);
3853 NS_ASSERTION(po, "nsPrintObject can't be null!");
3854 nsIFrame* rootFrame = nullptr;
3855 if (po->mPresShell) {
3856 rootFrame = po->mPresShell->FrameManager()->GetRootFrame();
3857 while (rootFrame != nullptr) {
3858 nsIPageSequenceFrame * sqf = do_QueryFrame(rootFrame);
3859 if (sqf) {
3860 break;
3862 rootFrame = rootFrame->GetFirstPrincipalChild();
3866 PR_PL(("%s %d %d %d %p %p %p %p %p %d %d,%d,%d,%d\n", types[po->mFrameType],
3867 po->IsPrintable(), po->mPrintAsIs, po->mHasBeenPrinted, po, po->mDocShell.get(), po->mSeqFrame,
3868 po->mPageFrame, rootFrame, po->mPageNum, po->mRect.x, po->mRect.y, po->mRect.width, po->mRect.height));
3872 //-------------------------------------------------------------
3873 static void DumpPrintObjectsTree(nsPrintObject * aPO, int aLevel, FILE* aFD)
3875 if (!kPrintingLogMod || kPrintingLogMod->level != DUMP_LAYOUT_LEVEL) return;
3877 NS_ASSERTION(aPO, "Pointer is null!");
3879 FILE * fd = aFD?aFD:stdout;
3880 const char types[][3] = {"DC", "FR", "IF", "FS"};
3881 if (aLevel == 0) {
3882 fprintf(fd, "DocTree\n***************************************************\n");
3883 fprintf(fd, "T PO DocShell Seq Page Page# Rect\n");
3885 int32_t cnt = aPO->mKids.Length();
3886 for (int32_t i=0;i<cnt;i++) {
3887 nsPrintObject* po = aPO->mKids.ElementAt(i);
3888 NS_ASSERTION(po, "nsPrintObject can't be null!");
3889 for (int32_t k=0;k<aLevel;k++) fprintf(fd, " ");
3890 fprintf(fd, "%s %p %p %p %p %d %d,%d,%d,%d\n", types[po->mFrameType], po, po->mDocShell.get(), po->mSeqFrame,
3891 po->mPageFrame, po->mPageNum, po->mRect.x, po->mRect.y, po->mRect.width, po->mRect.height);
3895 //-------------------------------------------------------------
3896 static void GetDocTitleAndURL(nsPrintObject* aPO, nsACString& aDocStr, nsACString& aURLStr)
3898 nsAutoString docTitleStr;
3899 nsAutoString docURLStr;
3900 nsPrintEngine::GetDisplayTitleAndURL(aPO,
3901 docTitleStr, docURLStr,
3902 nsPrintEngine::eDocTitleDefURLDoc);
3903 aDocStr = NS_ConvertUTF16toUTF8(docTitleStr);
3904 aURLStr = NS_ConvertUTF16toUTF8(docURLStr);
3907 //-------------------------------------------------------------
3908 static void DumpPrintObjectsTreeLayout(nsPrintObject * aPO,
3909 nsDeviceContext * aDC,
3910 int aLevel, FILE * aFD)
3912 if (!kPrintingLogMod || kPrintingLogMod->level != DUMP_LAYOUT_LEVEL) return;
3914 NS_ASSERTION(aPO, "Pointer is null!");
3915 NS_ASSERTION(aDC, "Pointer is null!");
3917 const char types[][3] = {"DC", "FR", "IF", "FS"};
3918 FILE * fd = nullptr;
3919 if (aLevel == 0) {
3920 fd = fopen("tree_layout.txt", "w");
3921 fprintf(fd, "DocTree\n***************************************************\n");
3922 fprintf(fd, "***************************************************\n");
3923 fprintf(fd, "T PO DocShell Seq Page Page# Rect\n");
3924 } else {
3925 fd = aFD;
3927 if (fd) {
3928 nsIFrame* rootFrame = nullptr;
3929 if (aPO->mPresShell) {
3930 rootFrame = aPO->mPresShell->FrameManager()->GetRootFrame();
3932 for (int32_t k=0;k<aLevel;k++) fprintf(fd, " ");
3933 fprintf(fd, "%s %p %p %p %p %d %d,%d,%d,%d\n", types[aPO->mFrameType], aPO, aPO->mDocShell.get(), aPO->mSeqFrame,
3934 aPO->mPageFrame, aPO->mPageNum, aPO->mRect.x, aPO->mRect.y, aPO->mRect.width, aPO->mRect.height);
3935 if (aPO->IsPrintable()) {
3936 nsAutoCString docStr;
3937 nsAutoCString urlStr;
3938 GetDocTitleAndURL(aPO, docStr, urlStr);
3939 DumpLayoutData(docStr.get(), urlStr.get(), aPO->mPresContext, aDC, rootFrame, aPO->mDocShell, fd);
3941 fprintf(fd, "<***************************************************>\n");
3943 int32_t cnt = aPO->mKids.Length();
3944 for (int32_t i=0;i<cnt;i++) {
3945 nsPrintObject* po = aPO->mKids.ElementAt(i);
3946 NS_ASSERTION(po, "nsPrintObject can't be null!");
3947 DumpPrintObjectsTreeLayout(po, aDC, aLevel+1, fd);
3950 if (aLevel == 0 && fd) {
3951 fclose(fd);
3955 //-------------------------------------------------------------
3956 static void DumpPrintObjectsListStart(const char * aStr, nsTArray<nsPrintObject*> * aDocList)
3958 if (!kPrintingLogMod || kPrintingLogMod->level != DUMP_LAYOUT_LEVEL) return;
3960 NS_ASSERTION(aStr, "Pointer is null!");
3961 NS_ASSERTION(aDocList, "Pointer is null!");
3963 PR_PL(("%s\n", aStr));
3964 DumpPrintObjectsList(aDocList);
3967 #define DUMP_DOC_LIST(_title) DumpPrintObjectsListStart((_title), mPrt->mPrintDocList);
3968 #define DUMP_DOC_TREE DumpPrintObjectsTree(mPrt->mPrintObject);
3969 #define DUMP_DOC_TREELAYOUT DumpPrintObjectsTreeLayout(mPrt->mPrintObject, mPrt->mPrintDC);
3971 #else
3972 #define DUMP_DOC_LIST(_title)
3973 #define DUMP_DOC_TREE
3974 #define DUMP_DOC_TREELAYOUT
3975 #endif
3977 //---------------------------------------------------------------
3978 //---------------------------------------------------------------
3979 //-- End of debug helper routines
3980 //---------------------------------------------------------------