Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
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 "nsIMarkupDocumentViewer.h"
101 #include "nsIInterfaceRequestor.h"
102 #include "nsIInterfaceRequestorUtils.h"
103 #include "nsIDocShellTreeOwner.h"
104 #include "nsIWebBrowserChrome.h"
105 #include "nsIBaseWindow.h"
106 #include "nsILayoutHistoryState.h"
107 #include "nsFrameManager.h"
108 #include "nsHTMLReflowState.h"
109 #include "nsIDOMHTMLAnchorElement.h"
110 #include "nsIDOMHTMLAreaElement.h"
111 #include "nsIDOMHTMLLinkElement.h"
112 #include "nsIDOMHTMLImageElement.h"
113 #include "nsIContentViewerContainer.h"
114 #include "nsIContentViewer.h"
115 #include "nsIDocumentViewerPrint.h"
117 #include "nsFocusManager.h"
118 #include "nsRange.h"
119 #include "nsCDefaultURIFixup.h"
120 #include "nsIURIFixup.h"
121 #include "mozilla/dom/Element.h"
122 #include "nsContentList.h"
123 #include "nsIChannel.h"
124 #include "xpcpublic.h"
126 using namespace mozilla;
127 using namespace mozilla::dom;
129 //-----------------------------------------------------
130 // PR LOGGING
131 #ifdef MOZ_LOGGING
132 #define FORCE_PR_LOG /* Allow logging in the release build */
133 #endif
135 #include "prlog.h"
137 #ifdef PR_LOGGING
139 #ifdef DEBUG
140 // PR_LOGGING is force to always be on (even in release builds)
141 // but we only want some of it on,
142 //#define EXTENDED_DEBUG_PRINTING
143 #endif
145 #define DUMP_LAYOUT_LEVEL 9 // this turns on the dumping of each doucment's layout info
147 #ifndef PR_PL
148 static PRLogModuleInfo *
149 GetPrintingLog()
150 {
151 static PRLogModuleInfo *sLog;
152 if (!sLog)
153 sLog = PR_NewLogModule("printing");
154 return sLog;
155 }
156 #define PR_PL(_p1) PR_LOG(GetPrintingLog(), PR_LOG_DEBUG, _p1);
157 #endif
159 #ifdef EXTENDED_DEBUG_PRINTING
160 static uint32_t gDumpFileNameCnt = 0;
161 static uint32_t gDumpLOFileNameCnt = 0;
162 #endif
164 #define PRT_YESNO(_p) ((_p)?"YES":"NO")
165 static const char * gFrameTypesStr[] = {"eDoc", "eFrame", "eIFrame", "eFrameSet"};
166 static const char * gPrintFrameTypeStr[] = {"kNoFrames", "kFramesAsIs", "kSelectedFrame", "kEachFrameSep"};
167 static const char * gFrameHowToEnableStr[] = {"kFrameEnableNone", "kFrameEnableAll", "kFrameEnableAsIsAndEach"};
168 static const char * gPrintRangeStr[] = {"kRangeAllPages", "kRangeSpecifiedPageRange", "kRangeSelection", "kRangeFocusFrame"};
169 #else
170 #define PRT_YESNO(_p)
171 #define PR_PL(_p1)
172 #endif
174 #ifdef EXTENDED_DEBUG_PRINTING
175 // Forward Declarations
176 static void DumpPrintObjectsListStart(const char * aStr, nsTArray<nsPrintObject*> * aDocList);
177 static void DumpPrintObjectsTree(nsPrintObject * aPO, int aLevel= 0, FILE* aFD = nullptr);
178 static void DumpPrintObjectsTreeLayout(nsPrintObject * aPO,nsDeviceContext * aDC, int aLevel= 0, FILE * aFD = nullptr);
180 #define DUMP_DOC_LIST(_title) DumpPrintObjectsListStart((_title), mPrt->mPrintDocList);
181 #define DUMP_DOC_TREE DumpPrintObjectsTree(mPrt->mPrintObject);
182 #define DUMP_DOC_TREELAYOUT DumpPrintObjectsTreeLayout(mPrt->mPrintObject, mPrt->mPrintDC);
183 #else
184 #define DUMP_DOC_LIST(_title)
185 #define DUMP_DOC_TREE
186 #define DUMP_DOC_TREELAYOUT
187 #endif
189 class nsScriptSuppressor
190 {
191 public:
192 nsScriptSuppressor(nsPrintEngine* aPrintEngine)
193 : mPrintEngine(aPrintEngine), mSuppressed(false) {}
195 ~nsScriptSuppressor() { Unsuppress(); }
197 void Suppress()
198 {
199 if (mPrintEngine) {
200 mSuppressed = true;
201 mPrintEngine->TurnScriptingOn(false);
202 }
203 }
205 void Unsuppress()
206 {
207 if (mPrintEngine && mSuppressed) {
208 mPrintEngine->TurnScriptingOn(true);
209 }
210 mSuppressed = false;
211 }
213 void Disconnect() { mPrintEngine = nullptr; }
214 protected:
215 nsRefPtr<nsPrintEngine> mPrintEngine;
216 bool mSuppressed;
217 };
219 NS_IMPL_ISUPPORTS(nsPrintEngine, nsIWebProgressListener,
220 nsISupportsWeakReference, nsIObserver)
222 //---------------------------------------------------
223 //-- nsPrintEngine Class Impl
224 //---------------------------------------------------
225 nsPrintEngine::nsPrintEngine() :
226 mIsCreatingPrintPreview(false),
227 mIsDoingPrinting(false),
228 mIsDoingPrintPreview(false),
229 mProgressDialogIsShown(false),
230 mScreenDPI(115.0f),
231 mPrt(nullptr),
232 mPagePrintTimer(nullptr),
233 mPageSeqFrame(nullptr),
234 mPrtPreview(nullptr),
235 mOldPrtPreview(nullptr),
236 mDebugFile(nullptr),
237 mLoadCounter(0),
238 mDidLoadDataForPrinting(false),
239 mIsDestroying(false),
240 mDisallowSelectionPrint(false),
241 mNoMarginBoxes(false)
242 {
243 }
245 //-------------------------------------------------------
246 nsPrintEngine::~nsPrintEngine()
247 {
248 Destroy(); // for insurance
249 }
251 //-------------------------------------------------------
252 void nsPrintEngine::Destroy()
253 {
254 if (mIsDestroying) {
255 return;
256 }
257 mIsDestroying = true;
259 if (mPrt) {
260 delete mPrt;
261 mPrt = nullptr;
262 }
264 #ifdef NS_PRINT_PREVIEW
265 if (mPrtPreview) {
266 delete mPrtPreview;
267 mPrtPreview = nullptr;
268 }
270 // This is insruance
271 if (mOldPrtPreview) {
272 delete mOldPrtPreview;
273 mOldPrtPreview = nullptr;
274 }
276 #endif
277 mDocViewerPrint = nullptr;
278 }
280 //-------------------------------------------------------
281 void nsPrintEngine::DestroyPrintingData()
282 {
283 if (mPrt) {
284 nsPrintData* data = mPrt;
285 mPrt = nullptr;
286 delete data;
287 }
288 }
290 //---------------------------------------------------------------------------------
291 //-- Section: Methods needed by the DocViewer
292 //---------------------------------------------------------------------------------
294 //--------------------------------------------------------
295 nsresult nsPrintEngine::Initialize(nsIDocumentViewerPrint* aDocViewerPrint,
296 nsIDocShell* aContainer,
297 nsIDocument* aDocument,
298 float aScreenDPI,
299 FILE* aDebugFile)
300 {
301 NS_ENSURE_ARG_POINTER(aDocViewerPrint);
302 NS_ENSURE_ARG_POINTER(aContainer);
303 NS_ENSURE_ARG_POINTER(aDocument);
305 mDocViewerPrint = aDocViewerPrint;
306 mContainer = do_GetWeakReference(aContainer);
307 mDocument = aDocument;
308 mScreenDPI = aScreenDPI;
310 mDebugFile = aDebugFile; // ok to be nullptr
312 return NS_OK;
313 }
315 //-------------------------------------------------------
316 bool
317 nsPrintEngine::CheckBeforeDestroy()
318 {
319 if (mPrt && mPrt->mPreparingForPrint) {
320 mPrt->mDocWasToBeDestroyed = true;
321 return true;
322 }
323 return false;
324 }
326 //-------------------------------------------------------
327 nsresult
328 nsPrintEngine::Cancelled()
329 {
330 if (mPrt && mPrt->mPrintSettings) {
331 return mPrt->mPrintSettings->SetIsCancelled(true);
332 }
333 return NS_ERROR_FAILURE;
334 }
336 //-------------------------------------------------------
337 // Install our event listeners on the document to prevent
338 // some events from being processed while in PrintPreview
339 //
340 // No return code - if this fails, there isn't much we can do
341 void
342 nsPrintEngine::InstallPrintPreviewListener()
343 {
344 if (!mPrt->mPPEventListeners) {
345 nsCOMPtr<nsIDocShell> docShell = do_QueryReferent(mContainer);
346 nsCOMPtr<nsPIDOMWindow> win(do_GetInterface(docShell));
347 if (win) {
348 nsCOMPtr<EventTarget> target = do_QueryInterface(win->GetFrameElementInternal());
349 mPrt->mPPEventListeners = new nsPrintPreviewListener(target);
350 mPrt->mPPEventListeners->AddListeners();
351 }
352 }
353 }
355 //----------------------------------------------------------------------
356 nsresult
357 nsPrintEngine::GetSeqFrameAndCountPagesInternal(nsPrintObject* aPO,
358 nsIFrame*& aSeqFrame,
359 int32_t& aCount)
360 {
361 NS_ENSURE_ARG_POINTER(aPO);
363 // Finds the SimplePageSequencer frame
364 nsIPageSequenceFrame* seqFrame = aPO->mPresShell->GetPageSequenceFrame();
365 aSeqFrame = do_QueryFrame(seqFrame);
366 if (!aSeqFrame) {
367 return NS_ERROR_FAILURE;
368 }
370 // first count the total number of pages
371 aCount = 0;
372 nsIFrame* pageFrame = aSeqFrame->GetFirstPrincipalChild();
373 while (pageFrame != nullptr) {
374 aCount++;
375 pageFrame = pageFrame->GetNextSibling();
376 }
378 return NS_OK;
380 }
382 //-----------------------------------------------------------------
383 nsresult nsPrintEngine::GetSeqFrameAndCountPages(nsIFrame*& aSeqFrame, int32_t& aCount)
384 {
385 NS_ASSERTION(mPrtPreview, "mPrtPreview can't be null!");
386 return GetSeqFrameAndCountPagesInternal(mPrtPreview->mPrintObject, aSeqFrame, aCount);
387 }
388 //---------------------------------------------------------------------------------
389 //-- Done: Methods needed by the DocViewer
390 //---------------------------------------------------------------------------------
393 //---------------------------------------------------------------------------------
394 //-- Section: nsIWebBrowserPrint
395 //---------------------------------------------------------------------------------
397 // Foward decl for Debug Helper Functions
398 #ifdef EXTENDED_DEBUG_PRINTING
399 static int RemoveFilesInDir(const char * aDir);
400 static void GetDocTitleAndURL(nsPrintObject* aPO, char *& aDocStr, char *& aURLStr);
401 static void DumpPrintObjectsTree(nsPrintObject * aPO, int aLevel, FILE* aFD);
402 static void DumpPrintObjectsList(nsTArray<nsPrintObject*> * aDocList);
403 static void RootFrameList(nsPresContext* aPresContext, FILE* out, int32_t aIndent);
404 static void DumpViews(nsIDocShell* aDocShell, FILE* out);
405 static void DumpLayoutData(char* aTitleStr, char* aURLStr,
406 nsPresContext* aPresContext,
407 nsDeviceContext * aDC, nsIFrame * aRootFrame,
408 nsIDocShell * aDocShell, FILE* aFD);
409 #endif
411 //--------------------------------------------------------------------------------
413 nsresult
414 nsPrintEngine::CommonPrint(bool aIsPrintPreview,
415 nsIPrintSettings* aPrintSettings,
416 nsIWebProgressListener* aWebProgressListener,
417 nsIDOMDocument* aDoc) {
418 nsRefPtr<nsPrintEngine> kungfuDeathGrip = this;
419 nsresult rv = DoCommonPrint(aIsPrintPreview, aPrintSettings,
420 aWebProgressListener, aDoc);
421 if (NS_FAILED(rv)) {
422 if (aIsPrintPreview) {
423 SetIsCreatingPrintPreview(false);
424 SetIsPrintPreview(false);
425 } else {
426 SetIsPrinting(false);
427 }
428 if (mProgressDialogIsShown)
429 CloseProgressDialog(aWebProgressListener);
430 if (rv != NS_ERROR_ABORT && rv != NS_ERROR_OUT_OF_MEMORY)
431 ShowPrintErrorDialog(rv, !aIsPrintPreview);
432 delete mPrt;
433 mPrt = nullptr;
434 }
436 return rv;
437 }
439 nsresult
440 nsPrintEngine::DoCommonPrint(bool aIsPrintPreview,
441 nsIPrintSettings* aPrintSettings,
442 nsIWebProgressListener* aWebProgressListener,
443 nsIDOMDocument* aDoc)
444 {
445 nsresult rv;
447 if (aIsPrintPreview) {
448 // The WebProgressListener can be QI'ed to nsIPrintingPromptService
449 // then that means the progress dialog is already being shown.
450 nsCOMPtr<nsIPrintingPromptService> pps(do_QueryInterface(aWebProgressListener));
451 mProgressDialogIsShown = pps != nullptr;
453 if (mIsDoingPrintPreview) {
454 mOldPrtPreview = mPrtPreview;
455 mPrtPreview = nullptr;
456 }
457 } else {
458 mProgressDialogIsShown = false;
459 }
461 mPrt = new nsPrintData(aIsPrintPreview ? nsPrintData::eIsPrintPreview :
462 nsPrintData::eIsPrinting);
463 NS_ENSURE_TRUE(mPrt, NS_ERROR_OUT_OF_MEMORY);
465 // if they don't pass in a PrintSettings, then get the Global PS
466 mPrt->mPrintSettings = aPrintSettings;
467 if (!mPrt->mPrintSettings) {
468 rv = GetGlobalPrintSettings(getter_AddRefs(mPrt->mPrintSettings));
469 NS_ENSURE_SUCCESS(rv, rv);
470 }
472 rv = CheckForPrinters(mPrt->mPrintSettings);
473 NS_ENSURE_SUCCESS(rv, rv);
475 mPrt->mPrintSettings->SetIsCancelled(false);
476 mPrt->mPrintSettings->GetShrinkToFit(&mPrt->mShrinkToFit);
478 // In the case the margin boxes are not printed store the print settings for
479 // the footer/header to be used as default print setting for follow up prints.
480 mPrt->mPrintSettings->SetPersistMarginBoxSettings(!mNoMarginBoxes);
482 if (mNoMarginBoxes) {
483 // Set the footer/header to blank.
484 const char16_t* emptyString = EmptyString().get();
485 mPrt->mPrintSettings->SetHeaderStrLeft(emptyString);
486 mPrt->mPrintSettings->SetHeaderStrCenter(emptyString);
487 mPrt->mPrintSettings->SetHeaderStrRight(emptyString);
488 mPrt->mPrintSettings->SetFooterStrLeft(emptyString);
489 mPrt->mPrintSettings->SetFooterStrCenter(emptyString);
490 mPrt->mPrintSettings->SetFooterStrRight(emptyString);
491 }
493 if (aIsPrintPreview) {
494 SetIsCreatingPrintPreview(true);
495 SetIsPrintPreview(true);
496 nsCOMPtr<nsIMarkupDocumentViewer> viewer =
497 do_QueryInterface(mDocViewerPrint);
498 if (viewer) {
499 viewer->SetTextZoom(1.0f);
500 viewer->SetFullZoom(1.0f);
501 viewer->SetMinFontSize(0);
502 }
503 }
505 // Create a print session and let the print settings know about it.
506 // The print settings hold an nsWeakPtr to the session so it does not
507 // need to be cleared from the settings at the end of the job.
508 // XXX What lifetime does the printSession need to have?
509 nsCOMPtr<nsIPrintSession> printSession;
510 if (!aIsPrintPreview) {
511 printSession = do_CreateInstance("@mozilla.org/gfx/printsession;1", &rv);
512 NS_ENSURE_SUCCESS(rv, rv);
513 mPrt->mPrintSettings->SetPrintSession(printSession);
514 }
516 if (aWebProgressListener != nullptr) {
517 mPrt->mPrintProgressListeners.AppendObject(aWebProgressListener);
518 }
520 // Get the currently focused window and cache it
521 // because the Print Dialog will "steal" focus and later when you try
522 // to get the currently focused windows it will be nullptr
523 mPrt->mCurrentFocusWin = FindFocusedDOMWindow();
525 // Check to see if there is a "regular" selection
526 bool isSelection = IsThereARangeSelection(mPrt->mCurrentFocusWin);
528 // Get the docshell for this documentviewer
529 nsCOMPtr<nsIDocShell> webContainer(do_QueryReferent(mContainer, &rv));
530 NS_ENSURE_SUCCESS(rv, rv);
532 {
533 if (aIsPrintPreview) {
534 nsCOMPtr<nsIContentViewer> viewer;
535 webContainer->GetContentViewer(getter_AddRefs(viewer));
536 if (viewer && viewer->GetDocument() && viewer->GetDocument()->IsShowing()) {
537 viewer->GetDocument()->OnPageHide(false, nullptr);
538 }
539 }
541 nsAutoScriptBlocker scriptBlocker;
542 mPrt->mPrintObject = new nsPrintObject();
543 NS_ENSURE_TRUE(mPrt->mPrintObject, NS_ERROR_OUT_OF_MEMORY);
544 rv = mPrt->mPrintObject->Init(webContainer, aDoc, aIsPrintPreview);
545 NS_ENSURE_SUCCESS(rv, rv);
547 NS_ENSURE_TRUE(mPrt->mPrintDocList.AppendElement(mPrt->mPrintObject),
548 NS_ERROR_OUT_OF_MEMORY);
550 mPrt->mIsParentAFrameSet = IsParentAFrameSet(webContainer);
551 mPrt->mPrintObject->mFrameType = mPrt->mIsParentAFrameSet ? eFrameSet : eDoc;
553 // Build the "tree" of PrintObjects
554 BuildDocTree(mPrt->mPrintObject->mDocShell, &mPrt->mPrintDocList,
555 mPrt->mPrintObject);
556 }
558 if (!aIsPrintPreview) {
559 SetIsPrinting(true);
560 }
562 // XXX This isn't really correct...
563 if (!mPrt->mPrintObject->mDocument ||
564 !mPrt->mPrintObject->mDocument->GetRootElement())
565 return NS_ERROR_GFX_PRINTER_STARTDOC;
567 // Create the linkage from the sub-docs back to the content element
568 // in the parent document
569 MapContentToWebShells(mPrt->mPrintObject, mPrt->mPrintObject);
571 mPrt->mIsIFrameSelected = IsThereAnIFrameSelected(webContainer, mPrt->mCurrentFocusWin, mPrt->mIsParentAFrameSet);
573 // Setup print options for UI
574 if (mPrt->mIsParentAFrameSet) {
575 if (mPrt->mCurrentFocusWin) {
576 mPrt->mPrintSettings->SetHowToEnableFrameUI(nsIPrintSettings::kFrameEnableAll);
577 } else {
578 mPrt->mPrintSettings->SetHowToEnableFrameUI(nsIPrintSettings::kFrameEnableAsIsAndEach);
579 }
580 } else {
581 mPrt->mPrintSettings->SetHowToEnableFrameUI(nsIPrintSettings::kFrameEnableNone);
582 }
583 // Now determine how to set up the Frame print UI
584 mPrt->mPrintSettings->SetPrintOptions(nsIPrintSettings::kEnableSelectionRB,
585 isSelection || mPrt->mIsIFrameSelected);
587 nsCOMPtr<nsIDeviceContextSpec> devspec
588 (do_CreateInstance("@mozilla.org/gfx/devicecontextspec;1", &rv));
589 NS_ENSURE_SUCCESS(rv, rv);
591 nsScriptSuppressor scriptSuppressor(this);
592 if (!aIsPrintPreview) {
593 #ifdef DEBUG
594 mPrt->mDebugFilePtr = mDebugFile;
595 #endif
597 scriptSuppressor.Suppress();
598 bool printSilently;
599 mPrt->mPrintSettings->GetPrintSilent(&printSilently);
601 // Check prefs for a default setting as to whether we should print silently
602 printSilently =
603 Preferences::GetBool("print.always_print_silent", printSilently);
605 // Ask dialog to be Print Shown via the Plugable Printing Dialog Service
606 // This service is for the Print Dialog and the Print Progress Dialog
607 // If printing silently or you can't get the service continue on
608 if (!printSilently) {
609 nsCOMPtr<nsIPrintingPromptService> printPromptService(do_GetService(kPrintingPromptService));
610 if (printPromptService) {
611 nsIDOMWindow *domWin = mDocument->GetWindow();
612 NS_ENSURE_TRUE(domWin, NS_ERROR_FAILURE);
614 // Platforms not implementing a given dialog for the service may
615 // return NS_ERROR_NOT_IMPLEMENTED or an error code.
616 //
617 // NS_ERROR_NOT_IMPLEMENTED indicates they want default behavior
618 // Any other error code means we must bail out
619 //
620 nsCOMPtr<nsIWebBrowserPrint> wbp(do_QueryInterface(mDocViewerPrint));
621 rv = printPromptService->ShowPrintDialog(domWin, wbp,
622 mPrt->mPrintSettings);
623 //
624 // ShowPrintDialog triggers an event loop which means we can't assume
625 // that the state of this->{anything} matches the state we've checked
626 // above. Including that a given {thing} is non null.
627 if (!mPrt) {
628 return NS_ERROR_FAILURE;
629 }
631 if (NS_SUCCEEDED(rv)) {
632 // since we got the dialog and it worked then make sure we
633 // are telling GFX we want to print silent
634 printSilently = true;
636 if (mPrt->mPrintSettings) {
637 // The user might have changed shrink-to-fit in the print dialog, so update our copy of its state
638 mPrt->mPrintSettings->GetShrinkToFit(&mPrt->mShrinkToFit);
639 }
640 } else if (rv == NS_ERROR_NOT_IMPLEMENTED) {
641 // This means the Dialog service was there,
642 // but they choose not to implement this dialog and
643 // are looking for default behavior from the toolkit
644 rv = NS_OK;
645 }
646 } else {
647 // No dialog service available
648 rv = NS_ERROR_NOT_IMPLEMENTED;
649 }
650 } else {
651 // Call any code that requires a run of the event loop.
652 rv = mPrt->mPrintSettings->SetupSilentPrinting();
653 }
654 // Check explicitly for abort because it's expected
655 if (rv == NS_ERROR_ABORT)
656 return rv;
657 NS_ENSURE_SUCCESS(rv, rv);
658 }
660 rv = devspec->Init(nullptr, mPrt->mPrintSettings, aIsPrintPreview);
661 NS_ENSURE_SUCCESS(rv, rv);
663 mPrt->mPrintDC = new nsDeviceContext();
664 rv = mPrt->mPrintDC->InitForPrinting(devspec);
665 NS_ENSURE_SUCCESS(rv, rv);
667 if (aIsPrintPreview) {
668 mPrt->mPrintSettings->SetPrintFrameType(nsIPrintSettings::kFramesAsIs);
670 // override any UI that wants to PrintPreview any selection or page range
671 // we want to view every page in PrintPreview each time
672 mPrt->mPrintSettings->SetPrintRange(nsIPrintSettings::kRangeAllPages);
673 } else {
674 // Always check and set the print settings first and then fall back
675 // onto the PrintService if there isn't a PrintSettings
676 //
677 // Posiible Usage values:
678 // nsIPrintSettings::kUseInternalDefault
679 // nsIPrintSettings::kUseSettingWhenPossible
680 //
681 // NOTE: The consts are the same for PrintSettings and PrintSettings
682 int16_t printFrameTypeUsage = nsIPrintSettings::kUseSettingWhenPossible;
683 mPrt->mPrintSettings->GetPrintFrameTypeUsage(&printFrameTypeUsage);
685 // Ok, see if we are going to use our value and override the default
686 if (printFrameTypeUsage == nsIPrintSettings::kUseSettingWhenPossible) {
687 // Get the Print Options/Settings PrintFrameType to see what is preferred
688 int16_t printFrameType = nsIPrintSettings::kEachFrameSep;
689 mPrt->mPrintSettings->GetPrintFrameType(&printFrameType);
691 // Don't let anybody do something stupid like try to set it to
692 // kNoFrames when we are printing a FrameSet
693 if (printFrameType == nsIPrintSettings::kNoFrames) {
694 mPrt->mPrintFrameType = nsIPrintSettings::kEachFrameSep;
695 mPrt->mPrintSettings->SetPrintFrameType(mPrt->mPrintFrameType);
696 } else {
697 // First find out from the PrinService what options are available
698 // to us for Printing FrameSets
699 int16_t howToEnableFrameUI;
700 mPrt->mPrintSettings->GetHowToEnableFrameUI(&howToEnableFrameUI);
701 if (howToEnableFrameUI != nsIPrintSettings::kFrameEnableNone) {
702 switch (howToEnableFrameUI) {
703 case nsIPrintSettings::kFrameEnableAll:
704 mPrt->mPrintFrameType = printFrameType;
705 break;
707 case nsIPrintSettings::kFrameEnableAsIsAndEach:
708 if (printFrameType != nsIPrintSettings::kSelectedFrame) {
709 mPrt->mPrintFrameType = printFrameType;
710 } else { // revert back to a good value
711 mPrt->mPrintFrameType = nsIPrintSettings::kEachFrameSep;
712 }
713 break;
714 } // switch
715 mPrt->mPrintSettings->SetPrintFrameType(mPrt->mPrintFrameType);
716 }
717 }
718 } else {
719 mPrt->mPrintSettings->GetPrintFrameType(&mPrt->mPrintFrameType);
720 }
721 }
723 if (mPrt->mPrintFrameType == nsIPrintSettings::kEachFrameSep) {
724 CheckForChildFrameSets(mPrt->mPrintObject);
725 }
727 if (NS_FAILED(EnablePOsForPrinting())) {
728 return NS_ERROR_FAILURE;
729 }
731 // Attach progressListener to catch network requests.
732 nsCOMPtr<nsIWebProgress> webProgress = do_QueryInterface(mPrt->mPrintObject->mDocShell);
733 webProgress->AddProgressListener(
734 static_cast<nsIWebProgressListener*>(this),
735 nsIWebProgress::NOTIFY_STATE_REQUEST);
737 mLoadCounter = 0;
738 mDidLoadDataForPrinting = false;
740 if (aIsPrintPreview) {
741 bool notifyOnInit = false;
742 ShowPrintProgress(false, notifyOnInit);
744 // Very important! Turn Off scripting
745 TurnScriptingOn(false);
747 if (!notifyOnInit) {
748 InstallPrintPreviewListener();
749 rv = InitPrintDocConstruction(false);
750 } else {
751 rv = NS_OK;
752 }
753 } else {
754 bool doNotify;
755 ShowPrintProgress(true, doNotify);
756 if (!doNotify) {
757 // Print listener setup...
758 mPrt->OnStartPrinting();
760 rv = InitPrintDocConstruction(false);
761 }
762 }
764 // We will enable scripting later after printing has finished.
765 scriptSuppressor.Disconnect();
767 return NS_OK;
768 }
770 //---------------------------------------------------------------------------------
771 NS_IMETHODIMP
772 nsPrintEngine::Print(nsIPrintSettings* aPrintSettings,
773 nsIWebProgressListener* aWebProgressListener)
774 {
775 // If we have a print preview document, use that instead of the original
776 // mDocument. That way animated images etc. get printed using the same state
777 // as in print preview.
778 nsCOMPtr<nsIDOMDocument> doc =
779 do_QueryInterface(mPrtPreview && mPrtPreview->mPrintObject ?
780 mPrtPreview->mPrintObject->mDocument : mDocument);
782 return CommonPrint(false, aPrintSettings, aWebProgressListener, doc);
783 }
785 NS_IMETHODIMP
786 nsPrintEngine::PrintPreview(nsIPrintSettings* aPrintSettings,
787 nsIDOMWindow *aChildDOMWin,
788 nsIWebProgressListener* aWebProgressListener)
789 {
790 // Get the DocShell and see if it is busy
791 // (We can't Print Preview this document if it is still busy)
792 nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mContainer));
793 NS_ENSURE_STATE(docShell);
795 uint32_t busyFlags = nsIDocShell::BUSY_FLAGS_NONE;
796 if (NS_FAILED(docShell->GetBusyFlags(&busyFlags)) ||
797 busyFlags != nsIDocShell::BUSY_FLAGS_NONE) {
798 CloseProgressDialog(aWebProgressListener);
799 ShowPrintErrorDialog(NS_ERROR_GFX_PRINTER_DOC_IS_BUSY, false);
800 return NS_ERROR_FAILURE;
801 }
803 NS_ENSURE_STATE(aChildDOMWin);
804 nsCOMPtr<nsIDOMDocument> doc;
805 aChildDOMWin->GetDocument(getter_AddRefs(doc));
806 NS_ENSURE_STATE(doc);
808 // Document is not busy -- go ahead with the Print Preview
809 return CommonPrint(true, aPrintSettings, aWebProgressListener, doc);
810 }
812 //----------------------------------------------------------------------------------
813 /* readonly attribute boolean isFramesetDocument; */
814 NS_IMETHODIMP
815 nsPrintEngine::GetIsFramesetDocument(bool *aIsFramesetDocument)
816 {
817 nsCOMPtr<nsIDocShell> webContainer(do_QueryReferent(mContainer));
818 *aIsFramesetDocument = IsParentAFrameSet(webContainer);
819 return NS_OK;
820 }
822 //----------------------------------------------------------------------------------
823 /* readonly attribute boolean isIFrameSelected; */
824 NS_IMETHODIMP
825 nsPrintEngine::GetIsIFrameSelected(bool *aIsIFrameSelected)
826 {
827 *aIsIFrameSelected = false;
829 // Get the docshell for this documentviewer
830 nsCOMPtr<nsIDocShell> webContainer(do_QueryReferent(mContainer));
831 // Get the currently focused window
832 nsCOMPtr<nsIDOMWindow> currentFocusWin = FindFocusedDOMWindow();
833 if (currentFocusWin && webContainer) {
834 // Get whether the doc contains a frameset
835 // Also, check to see if the currently focus docshell
836 // is a child of this docshell
837 bool isParentFrameSet;
838 *aIsIFrameSelected = IsThereAnIFrameSelected(webContainer, currentFocusWin, isParentFrameSet);
839 }
840 return NS_OK;
841 }
843 //----------------------------------------------------------------------------------
844 /* readonly attribute boolean isRangeSelection; */
845 NS_IMETHODIMP
846 nsPrintEngine::GetIsRangeSelection(bool *aIsRangeSelection)
847 {
848 // Get the currently focused window
849 nsCOMPtr<nsIDOMWindow> currentFocusWin = FindFocusedDOMWindow();
850 *aIsRangeSelection = IsThereARangeSelection(currentFocusWin);
851 return NS_OK;
852 }
854 //----------------------------------------------------------------------------------
855 /* readonly attribute boolean isFramesetFrameSelected; */
856 NS_IMETHODIMP
857 nsPrintEngine::GetIsFramesetFrameSelected(bool *aIsFramesetFrameSelected)
858 {
859 // Get the currently focused window
860 nsCOMPtr<nsIDOMWindow> currentFocusWin = FindFocusedDOMWindow();
861 *aIsFramesetFrameSelected = currentFocusWin != nullptr;
862 return NS_OK;
863 }
865 //----------------------------------------------------------------------------------
866 /* readonly attribute long printPreviewNumPages; */
867 NS_IMETHODIMP
868 nsPrintEngine::GetPrintPreviewNumPages(int32_t *aPrintPreviewNumPages)
869 {
870 NS_ENSURE_ARG_POINTER(aPrintPreviewNumPages);
872 nsPrintData* prt = nullptr;
873 nsIFrame* seqFrame = nullptr;
874 *aPrintPreviewNumPages = 0;
876 // When calling this function, the FinishPrintPreview() function might not
877 // been called as there are still some
878 if (mPrtPreview) {
879 prt = mPrtPreview;
880 } else {
881 prt = mPrt;
882 }
883 if ((!prt) ||
884 NS_FAILED(GetSeqFrameAndCountPagesInternal(prt->mPrintObject, seqFrame, *aPrintPreviewNumPages))) {
885 return NS_ERROR_FAILURE;
886 }
887 return NS_OK;
888 }
890 //----------------------------------------------------------------------------------
891 // Enumerate all the documents for their titles
892 NS_IMETHODIMP
893 nsPrintEngine::EnumerateDocumentNames(uint32_t* aCount,
894 char16_t*** aResult)
895 {
896 NS_ENSURE_ARG(aCount);
897 NS_ENSURE_ARG_POINTER(aResult);
899 *aCount = 0;
900 *aResult = nullptr;
902 int32_t numDocs = mPrt->mPrintDocList.Length();
903 char16_t** array = (char16_t**) nsMemory::Alloc(numDocs * sizeof(char16_t*));
904 if (!array)
905 return NS_ERROR_OUT_OF_MEMORY;
907 for (int32_t i=0;i<numDocs;i++) {
908 nsPrintObject* po = mPrt->mPrintDocList.ElementAt(i);
909 NS_ASSERTION(po, "nsPrintObject can't be null!");
910 nsAutoString docTitleStr;
911 nsAutoString docURLStr;
912 GetDocumentTitleAndURL(po->mDocument, docTitleStr, docURLStr);
914 // Use the URL if the doc is empty
915 if (docTitleStr.IsEmpty() && !docURLStr.IsEmpty()) {
916 docTitleStr = docURLStr;
917 }
918 array[i] = ToNewUnicode(docTitleStr);
919 }
920 *aCount = numDocs;
921 *aResult = array;
923 return NS_OK;
925 }
927 //----------------------------------------------------------------------------------
928 /* readonly attribute nsIPrintSettings globalPrintSettings; */
929 nsresult
930 nsPrintEngine::GetGlobalPrintSettings(nsIPrintSettings **aGlobalPrintSettings)
931 {
932 NS_ENSURE_ARG_POINTER(aGlobalPrintSettings);
934 nsresult rv = NS_ERROR_FAILURE;
935 nsCOMPtr<nsIPrintSettingsService> printSettingsService =
936 do_GetService(sPrintSettingsServiceContractID, &rv);
937 if (NS_SUCCEEDED(rv)) {
938 rv = printSettingsService->GetGlobalPrintSettings(aGlobalPrintSettings);
939 }
940 return rv;
941 }
943 //----------------------------------------------------------------------------------
944 /* readonly attribute boolean doingPrint; */
945 NS_IMETHODIMP
946 nsPrintEngine::GetDoingPrint(bool *aDoingPrint)
947 {
948 NS_ENSURE_ARG_POINTER(aDoingPrint);
949 *aDoingPrint = mIsDoingPrinting;
950 return NS_OK;
951 }
953 //----------------------------------------------------------------------------------
954 /* readonly attribute boolean doingPrintPreview; */
955 NS_IMETHODIMP
956 nsPrintEngine::GetDoingPrintPreview(bool *aDoingPrintPreview)
957 {
958 NS_ENSURE_ARG_POINTER(aDoingPrintPreview);
959 *aDoingPrintPreview = mIsDoingPrintPreview;
960 return NS_OK;
961 }
963 //----------------------------------------------------------------------------------
964 /* readonly attribute nsIPrintSettings currentPrintSettings; */
965 NS_IMETHODIMP
966 nsPrintEngine::GetCurrentPrintSettings(nsIPrintSettings * *aCurrentPrintSettings)
967 {
968 NS_ENSURE_ARG_POINTER(aCurrentPrintSettings);
970 if (mPrt) {
971 *aCurrentPrintSettings = mPrt->mPrintSettings;
973 } else if (mPrtPreview) {
974 *aCurrentPrintSettings = mPrtPreview->mPrintSettings;
976 } else {
977 *aCurrentPrintSettings = nullptr;
978 }
979 NS_IF_ADDREF(*aCurrentPrintSettings);
980 return NS_OK;
981 }
983 //-----------------------------------------------------------------
984 //-- Section: Pre-Reflow Methods
985 //-----------------------------------------------------------------
987 //---------------------------------------------------------------------
988 // This method checks to see if there is at least one printer defined
989 // and if so, it sets the first printer in the list as the default name
990 // in the PrintSettings which is then used for Printer Preview
991 nsresult
992 nsPrintEngine::CheckForPrinters(nsIPrintSettings* aPrintSettings)
993 {
994 #if defined(XP_MACOSX) || defined(ANDROID)
995 // Mac doesn't support retrieving a printer list.
996 return NS_OK;
997 #else
998 NS_ENSURE_ARG_POINTER(aPrintSettings);
1000 // See if aPrintSettings already has a printer
1001 nsXPIDLString printerName;
1002 nsresult rv = aPrintSettings->GetPrinterName(getter_Copies(printerName));
1003 if (NS_SUCCEEDED(rv) && !printerName.IsEmpty()) {
1004 return NS_OK;
1005 }
1007 // aPrintSettings doesn't have a printer set. Try to fetch the default.
1008 nsCOMPtr<nsIPrintSettingsService> printSettingsService =
1009 do_GetService(sPrintSettingsServiceContractID, &rv);
1010 NS_ENSURE_SUCCESS(rv, rv);
1012 rv = printSettingsService->GetDefaultPrinterName(getter_Copies(printerName));
1013 if (NS_SUCCEEDED(rv) && !printerName.IsEmpty()) {
1014 rv = aPrintSettings->SetPrinterName(printerName.get());
1015 }
1016 return rv;
1017 #endif
1018 }
1020 //----------------------------------------------------------------------
1021 // Set up to use the "pluggable" Print Progress Dialog
1022 void
1023 nsPrintEngine::ShowPrintProgress(bool aIsForPrinting, bool& aDoNotify)
1024 {
1025 // default to not notifying, that if something here goes wrong
1026 // or we aren't going to show the progress dialog we can straight into
1027 // reflowing the doc for printing.
1028 aDoNotify = false;
1030 // Assume we can't do progress and then see if we can
1031 bool showProgresssDialog = false;
1033 // if it is already being shown then don't bother to find out if it should be
1034 // so skip this and leave mShowProgressDialog set to FALSE
1035 if (!mProgressDialogIsShown) {
1036 showProgresssDialog = Preferences::GetBool("print.show_print_progress");
1037 }
1039 // Turning off the showing of Print Progress in Prefs overrides
1040 // whether the calling PS desire to have it on or off, so only check PS if
1041 // prefs says it's ok to be on.
1042 if (showProgresssDialog) {
1043 mPrt->mPrintSettings->GetShowPrintProgress(&showProgresssDialog);
1044 }
1046 // Now open the service to get the progress dialog
1047 // If we don't get a service, that's ok, then just don't show progress
1048 if (showProgresssDialog) {
1049 nsCOMPtr<nsIPrintingPromptService> printPromptService(do_GetService(kPrintingPromptService));
1050 if (printPromptService) {
1051 nsPIDOMWindow *domWin = mDocument->GetWindow();
1052 if (!domWin) return;
1054 nsCOMPtr<nsIDocShell> docShell = domWin->GetDocShell();
1055 if (!docShell) return;
1056 nsCOMPtr<nsIDocShellTreeOwner> owner;
1057 docShell->GetTreeOwner(getter_AddRefs(owner));
1058 nsCOMPtr<nsIWebBrowserChrome> browserChrome = do_GetInterface(owner);
1059 if (!browserChrome) return;
1060 bool isModal = true;
1061 browserChrome->IsWindowModal(&isModal);
1062 if (isModal) {
1063 // Showing a print progress dialog when printing a modal window
1064 // isn't supported. See bug 301560.
1065 return;
1066 }
1068 nsCOMPtr<nsIWebProgressListener> printProgressListener;
1070 nsCOMPtr<nsIWebBrowserPrint> wbp(do_QueryInterface(mDocViewerPrint));
1071 nsresult rv = printPromptService->ShowProgress(domWin, wbp, mPrt->mPrintSettings, this, aIsForPrinting,
1072 getter_AddRefs(printProgressListener),
1073 getter_AddRefs(mPrt->mPrintProgressParams),
1074 &aDoNotify);
1075 if (NS_SUCCEEDED(rv)) {
1076 if (printProgressListener && mPrt->mPrintProgressParams) {
1077 mPrt->mPrintProgressListeners.AppendObject(printProgressListener);
1078 SetDocAndURLIntoProgress(mPrt->mPrintObject, mPrt->mPrintProgressParams);
1079 }
1080 }
1081 }
1082 }
1083 }
1085 //---------------------------------------------------------------------
1086 bool
1087 nsPrintEngine::IsThereARangeSelection(nsIDOMWindow* aDOMWin)
1088 {
1089 if (mDisallowSelectionPrint)
1090 return false;
1092 nsCOMPtr<nsIPresShell> presShell;
1093 if (aDOMWin) {
1094 nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(aDOMWin));
1095 presShell = window->GetDocShell()->GetPresShell();
1096 }
1098 if (!presShell)
1099 return false;
1101 // check here to see if there is a range selection
1102 // so we know whether to turn on the "Selection" radio button
1103 Selection* selection =
1104 presShell->GetCurrentSelection(nsISelectionController::SELECTION_NORMAL);
1105 if (!selection) {
1106 return false;
1107 }
1109 int32_t rangeCount = selection->GetRangeCount();
1110 if (!rangeCount) {
1111 return false;
1112 }
1114 if (rangeCount > 1) {
1115 return true;
1116 }
1118 // check to make sure it isn't an insertion selection
1119 return selection->GetRangeAt(0) && !selection->IsCollapsed();
1120 }
1122 //---------------------------------------------------------------------
1123 bool
1124 nsPrintEngine::IsParentAFrameSet(nsIDocShell * aParent)
1125 {
1126 // See if the incoming doc is the root document
1127 if (!aParent) return false;
1129 // When it is the top level document we need to check
1130 // to see if it contains a frameset. If it does, then
1131 // we only want to print the doc's children and not the document itself
1132 // For anything else we always print all the children and the document
1133 // for example, if the doc contains an IFRAME we eant to print the child
1134 // document (the IFRAME) and then the rest of the document.
1135 //
1136 // XXX we really need to search the frame tree, and not the content
1137 // but there is no way to distinguish between IFRAMEs and FRAMEs
1138 // with the GetFrameType call.
1139 // Bug 53459 has been files so we can eventually distinguish
1140 // between IFRAME frames and FRAME frames
1141 bool isFrameSet = false;
1142 // only check to see if there is a frameset if there is
1143 // NO parent doc for this doc. meaning this parent is the root doc
1144 nsCOMPtr<nsIDOMDocument> domDoc = do_GetInterface(aParent);
1145 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
1146 if (doc) {
1147 nsIContent *rootElement = doc->GetRootElement();
1148 if (rootElement) {
1149 isFrameSet = HasFramesetChild(rootElement);
1150 }
1151 }
1152 return isFrameSet;
1153 }
1156 //---------------------------------------------------------------------
1157 // Recursively build a list of sub documents to be printed
1158 // that mirrors the document tree
1159 void
1160 nsPrintEngine::BuildDocTree(nsIDocShell * aParentNode,
1161 nsTArray<nsPrintObject*> * aDocList,
1162 nsPrintObject * aPO)
1163 {
1164 NS_ASSERTION(aParentNode, "Pointer is null!");
1165 NS_ASSERTION(aDocList, "Pointer is null!");
1166 NS_ASSERTION(aPO, "Pointer is null!");
1168 int32_t childWebshellCount;
1169 aParentNode->GetChildCount(&childWebshellCount);
1170 if (childWebshellCount > 0) {
1171 for (int32_t i=0;i<childWebshellCount;i++) {
1172 nsCOMPtr<nsIDocShellTreeItem> child;
1173 aParentNode->GetChildAt(i, getter_AddRefs(child));
1174 nsCOMPtr<nsIDocShell> childAsShell(do_QueryInterface(child));
1176 nsCOMPtr<nsIContentViewer> viewer;
1177 childAsShell->GetContentViewer(getter_AddRefs(viewer));
1178 if (viewer) {
1179 nsCOMPtr<nsIContentViewerFile> viewerFile(do_QueryInterface(viewer));
1180 if (viewerFile) {
1181 nsCOMPtr<nsIDOMDocument> doc = do_GetInterface(childAsShell);
1182 nsPrintObject * po = new nsPrintObject();
1183 po->mParent = aPO;
1184 nsresult rv = po->Init(childAsShell, doc, aPO->mPrintPreview);
1185 if (NS_FAILED(rv))
1186 NS_NOTREACHED("Init failed?");
1187 aPO->mKids.AppendElement(po);
1188 aDocList->AppendElement(po);
1189 BuildDocTree(childAsShell, aDocList, po);
1190 }
1191 }
1192 }
1193 }
1194 }
1196 //---------------------------------------------------------------------
1197 void
1198 nsPrintEngine::GetDocumentTitleAndURL(nsIDocument* aDoc,
1199 nsAString& aTitle,
1200 nsAString& aURLStr)
1201 {
1202 NS_ASSERTION(aDoc, "Pointer is null!");
1204 aTitle.Truncate();
1205 aURLStr.Truncate();
1207 nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(aDoc);
1208 doc->GetTitle(aTitle);
1210 nsIURI* url = aDoc->GetDocumentURI();
1211 if (!url) return;
1213 nsCOMPtr<nsIURIFixup> urifixup(do_GetService(NS_URIFIXUP_CONTRACTID));
1214 if (!urifixup) return;
1216 nsCOMPtr<nsIURI> exposableURI;
1217 urifixup->CreateExposableURI(url, getter_AddRefs(exposableURI));
1219 if (!exposableURI) return;
1221 nsAutoCString urlCStr;
1222 exposableURI->GetSpec(urlCStr);
1224 nsresult rv;
1225 nsCOMPtr<nsITextToSubURI> textToSubURI =
1226 do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
1227 if (NS_FAILED(rv)) return;
1229 textToSubURI->UnEscapeURIForUI(NS_LITERAL_CSTRING("UTF-8"),
1230 urlCStr, aURLStr);
1231 }
1233 //---------------------------------------------------------------------
1234 // The walks the PO tree and for each document it walks the content
1235 // tree looking for any content that are sub-shells
1236 //
1237 // It then sets the mContent pointer in the "found" PO object back to the
1238 // the document that contained it.
1239 void
1240 nsPrintEngine::MapContentToWebShells(nsPrintObject* aRootPO,
1241 nsPrintObject* aPO)
1242 {
1243 NS_ASSERTION(aRootPO, "Pointer is null!");
1244 NS_ASSERTION(aPO, "Pointer is null!");
1246 // Recursively walk the content from the root item
1247 // XXX Would be faster to enumerate the subdocuments, although right now
1248 // nsIDocument doesn't expose quite what would be needed.
1249 nsCOMPtr<nsIContentViewer> viewer;
1250 aPO->mDocShell->GetContentViewer(getter_AddRefs(viewer));
1251 if (!viewer) return;
1253 nsCOMPtr<nsIDOMDocument> domDoc;
1254 viewer->GetDOMDocument(getter_AddRefs(domDoc));
1255 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
1256 if (!doc) return;
1258 Element* rootElement = doc->GetRootElement();
1259 if (rootElement) {
1260 MapContentForPO(aPO, rootElement);
1261 } else {
1262 NS_WARNING("Null root content on (sub)document.");
1263 }
1265 // Continue recursively walking the chilren of this PO
1266 for (uint32_t i=0;i<aPO->mKids.Length();i++) {
1267 MapContentToWebShells(aRootPO, aPO->mKids[i]);
1268 }
1270 }
1272 //-------------------------------------------------------
1273 // A Frame's sub-doc may contain content or a FrameSet
1274 // When it contains a FrameSet the mFrameType for the PrintObject
1275 // is always set to an eFrame. Which is fine when printing "AsIs"
1276 // but is incorrect when when printing "Each Frame Separately".
1277 // When printing "Each Frame Separately" the Frame really acts like
1278 // a frameset.
1279 //
1280 // This method walks the PO tree and checks to see if the PrintObject is
1281 // an eFrame and has children that are eFrames (meaning it's a Frame containing a FrameSet)
1282 // If so, then the mFrameType need to be changed to eFrameSet
1283 //
1284 // Also note: We only want to call this we are printing "Each Frame Separately"
1285 // when printing "As Is" leave it as an eFrame
1286 void
1287 nsPrintEngine::CheckForChildFrameSets(nsPrintObject* aPO)
1288 {
1289 NS_ASSERTION(aPO, "Pointer is null!");
1291 // Continue recursively walking the chilren of this PO
1292 bool hasChildFrames = false;
1293 for (uint32_t i=0;i<aPO->mKids.Length();i++) {
1294 nsPrintObject* po = aPO->mKids[i];
1295 if (po->mFrameType == eFrame) {
1296 hasChildFrames = true;
1297 CheckForChildFrameSets(po);
1298 }
1299 }
1301 if (hasChildFrames && aPO->mFrameType == eFrame) {
1302 aPO->mFrameType = eFrameSet;
1303 }
1304 }
1306 //---------------------------------------------------------------------
1307 // This method is key to the entire print mechanism.
1308 //
1309 // This "maps" or figures out which sub-doc represents a
1310 // given Frame or IFrame in its parent sub-doc.
1311 //
1312 // So the Mcontent pointer in the child sub-doc points to the
1313 // content in the its parent document, that caused it to be printed.
1314 // This is used later to (after reflow) to find the absolute location
1315 // of the sub-doc on its parent's page frame so it can be
1316 // printed in the correct location.
1317 //
1318 // This method recursvely "walks" the content for a document finding
1319 // all the Frames and IFrames, then sets the "mFrameType" data member
1320 // which tells us what type of PO we have
1321 void
1322 nsPrintEngine::MapContentForPO(nsPrintObject* aPO,
1323 nsIContent* aContent)
1324 {
1325 NS_PRECONDITION(aPO && aContent, "Null argument");
1327 nsIDocument* doc = aContent->GetDocument();
1329 NS_ASSERTION(doc, "Content without a document from a document tree?");
1331 nsIDocument* subDoc = doc->GetSubDocumentFor(aContent);
1333 if (subDoc) {
1334 nsCOMPtr<nsIDocShell> docShell(subDoc->GetDocShell());
1336 if (docShell) {
1337 nsPrintObject * po = nullptr;
1338 int32_t cnt = aPO->mKids.Length();
1339 for (int32_t i=0;i<cnt;i++) {
1340 nsPrintObject* kid = aPO->mKids.ElementAt(i);
1341 if (kid->mDocument == subDoc) {
1342 po = kid;
1343 break;
1344 }
1345 }
1347 // XXX If a subdocument has no onscreen presentation, there will be no PO
1348 // This is even if there should be a print presentation
1349 if (po) {
1351 nsCOMPtr<nsIDOMHTMLFrameElement> frame(do_QueryInterface(aContent));
1352 // "frame" elements not in a frameset context should be treated
1353 // as iframes
1354 if (frame && po->mParent->mFrameType == eFrameSet) {
1355 po->mFrameType = eFrame;
1356 } else {
1357 // Assume something iframe-like, i.e. iframe, object, or embed
1358 po->mFrameType = eIFrame;
1359 SetPrintAsIs(po, true);
1360 NS_ASSERTION(po->mParent, "The root must be a parent");
1361 po->mParent->mPrintAsIs = true;
1362 }
1363 }
1364 }
1365 }
1367 // walk children content
1368 for (nsIContent* child = aContent->GetFirstChild();
1369 child;
1370 child = child->GetNextSibling()) {
1371 MapContentForPO(aPO, child);
1372 }
1373 }
1375 //---------------------------------------------------------------------
1376 bool
1377 nsPrintEngine::IsThereAnIFrameSelected(nsIDocShell* aDocShell,
1378 nsIDOMWindow* aDOMWin,
1379 bool& aIsParentFrameSet)
1380 {
1381 aIsParentFrameSet = IsParentAFrameSet(aDocShell);
1382 bool iFrameIsSelected = false;
1383 if (mPrt && mPrt->mPrintObject) {
1384 nsPrintObject* po = FindPrintObjectByDOMWin(mPrt->mPrintObject, aDOMWin);
1385 iFrameIsSelected = po && po->mFrameType == eIFrame;
1386 } else {
1387 // First, check to see if we are a frameset
1388 if (!aIsParentFrameSet) {
1389 // Check to see if there is a currenlt focused frame
1390 // if so, it means the selected frame is either the main docshell
1391 // or an IFRAME
1392 if (aDOMWin) {
1393 // Get the main docshell's DOMWin to see if it matches
1394 // the frame that is selected
1395 nsCOMPtr<nsIDOMWindow> domWin = do_GetInterface(aDocShell);
1396 if (domWin != aDOMWin) {
1397 iFrameIsSelected = true; // we have a selected IFRAME
1398 }
1399 }
1400 }
1401 }
1403 return iFrameIsSelected;
1404 }
1406 //---------------------------------------------------------------------
1407 // Recursively sets all the PO items to be printed
1408 // from the given item down into the tree
1409 void
1410 nsPrintEngine::SetPrintPO(nsPrintObject* aPO, bool aPrint)
1411 {
1412 NS_ASSERTION(aPO, "Pointer is null!");
1414 // Set whether to print flag
1415 aPO->mDontPrint = !aPrint;
1417 for (uint32_t i=0;i<aPO->mKids.Length();i++) {
1418 SetPrintPO(aPO->mKids[i], aPrint);
1419 }
1420 }
1422 //---------------------------------------------------------------------
1423 // This will first use a Title and/or URL from the PrintSettings
1424 // if one isn't set then it uses the one from the document
1425 // then if not title is there we will make sure we send something back
1426 // depending on the situation.
1427 void
1428 nsPrintEngine::GetDisplayTitleAndURL(nsPrintObject* aPO,
1429 nsAString& aTitle,
1430 nsAString& aURLStr,
1431 eDocTitleDefault aDefType)
1432 {
1433 NS_ASSERTION(aPO, "Pointer is null!");
1435 if (!mPrt)
1436 return;
1438 aTitle.Truncate();
1439 aURLStr.Truncate();
1441 // First check to see if the PrintSettings has defined an alternate title
1442 // and use that if it did
1443 if (mPrt->mPrintSettings) {
1444 char16_t * docTitleStrPS = nullptr;
1445 char16_t * docURLStrPS = nullptr;
1446 mPrt->mPrintSettings->GetTitle(&docTitleStrPS);
1447 mPrt->mPrintSettings->GetDocURL(&docURLStrPS);
1449 if (docTitleStrPS) {
1450 aTitle = docTitleStrPS;
1451 }
1453 if (docURLStrPS) {
1454 aURLStr = docURLStrPS;
1455 }
1457 nsMemory::Free(docTitleStrPS);
1458 nsMemory::Free(docURLStrPS);
1459 }
1461 nsAutoString docTitle;
1462 nsAutoString docUrl;
1463 GetDocumentTitleAndURL(aPO->mDocument, docTitle, docUrl);
1465 if (aURLStr.IsEmpty() && !docUrl.IsEmpty()) {
1466 aURLStr = docUrl;
1467 }
1469 if (aTitle.IsEmpty()) {
1470 if (!docTitle.IsEmpty()) {
1471 aTitle = docTitle;
1472 } else {
1473 if (aDefType == eDocTitleDefURLDoc) {
1474 if (!aURLStr.IsEmpty()) {
1475 aTitle = aURLStr;
1476 } else if (mPrt->mBrandName) {
1477 aTitle = mPrt->mBrandName;
1478 }
1479 }
1480 }
1481 }
1482 }
1484 //---------------------------------------------------------------------
1485 nsresult nsPrintEngine::DocumentReadyForPrinting()
1486 {
1487 if (mPrt->mPrintFrameType == nsIPrintSettings::kEachFrameSep) {
1488 CheckForChildFrameSets(mPrt->mPrintObject);
1489 }
1491 //
1492 // Send the document to the printer...
1493 //
1494 nsresult rv = SetupToPrintContent();
1495 if (NS_FAILED(rv)) {
1496 // The print job was canceled or there was a problem
1497 // So remove all other documents from the print list
1498 DonePrintingPages(nullptr, rv);
1499 }
1500 return rv;
1501 }
1503 /** ---------------------------------------------------
1504 * Cleans up when an error occurred
1505 */
1506 nsresult nsPrintEngine::CleanupOnFailure(nsresult aResult, bool aIsPrinting)
1507 {
1508 PR_PL(("**** Failed %s - rv 0x%X", aIsPrinting?"Printing":"Print Preview", aResult));
1510 /* cleanup... */
1511 if (mPagePrintTimer) {
1512 mPagePrintTimer->Stop();
1513 NS_RELEASE(mPagePrintTimer);
1514 }
1516 if (aIsPrinting) {
1517 SetIsPrinting(false);
1518 } else {
1519 SetIsPrintPreview(false);
1520 SetIsCreatingPrintPreview(false);
1521 }
1523 /* cleanup done, let's fire-up an error dialog to notify the user
1524 * what went wrong...
1525 *
1526 * When rv == NS_ERROR_ABORT, it means we want out of the
1527 * print job without displaying any error messages
1528 */
1529 if (aResult != NS_ERROR_ABORT) {
1530 ShowPrintErrorDialog(aResult, aIsPrinting);
1531 }
1533 FirePrintCompletionEvent();
1535 return aResult;
1537 }
1539 //---------------------------------------------------------------------
1540 void
1541 nsPrintEngine::ShowPrintErrorDialog(nsresult aPrintError, bool aIsPrinting)
1542 {
1543 nsAutoCString stringName;
1544 nsXPIDLString msg, title;
1545 nsresult rv = NS_OK;
1547 switch(aPrintError)
1548 {
1549 #define ENTITY_FOR_ERROR(label) \
1550 case NS_ERROR_##label: stringName.AssignLiteral("PERR_" #label); break
1552 ENTITY_FOR_ERROR(GFX_PRINTER_NO_PRINTER_AVAILABLE);
1553 ENTITY_FOR_ERROR(GFX_PRINTER_NAME_NOT_FOUND);
1554 ENTITY_FOR_ERROR(GFX_PRINTER_COULD_NOT_OPEN_FILE);
1555 ENTITY_FOR_ERROR(GFX_PRINTER_STARTDOC);
1556 ENTITY_FOR_ERROR(GFX_PRINTER_ENDDOC);
1557 ENTITY_FOR_ERROR(GFX_PRINTER_STARTPAGE);
1558 ENTITY_FOR_ERROR(GFX_PRINTER_DOC_IS_BUSY);
1560 ENTITY_FOR_ERROR(ABORT);
1561 ENTITY_FOR_ERROR(NOT_AVAILABLE);
1562 ENTITY_FOR_ERROR(NOT_IMPLEMENTED);
1563 ENTITY_FOR_ERROR(OUT_OF_MEMORY);
1564 ENTITY_FOR_ERROR(UNEXPECTED);
1566 default:
1567 ENTITY_FOR_ERROR(FAILURE);
1569 #undef ENTITY_FOR_ERROR
1570 }
1572 if (!aIsPrinting) {
1573 // Try first with _PP suffix.
1574 stringName.AppendLiteral("_PP");
1575 rv = nsContentUtils::GetLocalizedString(
1576 nsContentUtils::ePRINTING_PROPERTIES, stringName.get(), msg);
1577 if (NS_FAILED(rv)) {
1578 stringName.Truncate(stringName.Length() - 3);
1579 }
1580 }
1581 if (aIsPrinting || NS_FAILED(rv)) {
1582 rv = nsContentUtils::GetLocalizedString(
1583 nsContentUtils::ePRINTING_PROPERTIES, stringName.get(), msg);
1584 }
1585 if (NS_FAILED(rv)) {
1586 return;
1587 }
1589 rv = nsContentUtils::GetLocalizedString(
1590 nsContentUtils::ePRINTING_PROPERTIES,
1591 aIsPrinting ? "print_error_dialog_title"
1592 : "printpreview_error_dialog_title",
1593 title);
1594 if (NS_FAILED(rv)) {
1595 return;
1596 }
1598 nsCOMPtr<nsIWindowWatcher> wwatch =
1599 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
1600 if (NS_FAILED(rv)) {
1601 return;
1602 }
1604 nsCOMPtr<nsIDOMWindow> active;
1605 wwatch->GetActiveWindow(getter_AddRefs(active));
1607 nsCOMPtr<nsIPrompt> dialog;
1608 /* |GetNewPrompter| allows that |active| is |nullptr|
1609 * (see bug 234982 ("nsPrintEngine::ShowPrintErrorDialog() fails in many cases")) */
1610 wwatch->GetNewPrompter(active, getter_AddRefs(dialog));
1611 if (!dialog) {
1612 return;
1613 }
1615 dialog->Alert(title.get(), msg.get());
1616 }
1618 //-----------------------------------------------------------------
1619 //-- Section: Reflow Methods
1620 //-----------------------------------------------------------------
1622 nsresult
1623 nsPrintEngine::ReconstructAndReflow(bool doSetPixelScale)
1624 {
1625 #if defined(XP_WIN) && defined(EXTENDED_DEBUG_PRINTING)
1626 // We need to clear all the output files here
1627 // because they will be re-created with second reflow of the docs
1628 if (kPrintingLogMod && kPrintingLogMod->level == DUMP_LAYOUT_LEVEL) {
1629 RemoveFilesInDir(".\\");
1630 gDumpFileNameCnt = 0;
1631 gDumpLOFileNameCnt = 0;
1632 }
1633 #endif
1635 for (uint32_t i = 0; i < mPrt->mPrintDocList.Length(); ++i) {
1636 nsPrintObject* po = mPrt->mPrintDocList.ElementAt(i);
1637 NS_ASSERTION(po, "nsPrintObject can't be null!");
1639 if (po->mDontPrint || po->mInvisible) {
1640 continue;
1641 }
1643 UpdateZoomRatio(po, doSetPixelScale);
1645 po->mPresContext->SetPageScale(po->mZoomRatio);
1647 // Calculate scale factor from printer to screen
1648 float printDPI = float(mPrt->mPrintDC->AppUnitsPerCSSInch()) /
1649 float(mPrt->mPrintDC->AppUnitsPerDevPixel());
1650 po->mPresContext->SetPrintPreviewScale(mScreenDPI / printDPI);
1652 po->mPresShell->ReconstructFrames();
1654 // For all views except the first one, setup the root view.
1655 // ??? Can there be multiple po for the top-level-document?
1656 bool documentIsTopLevel = true;
1657 if (i != 0) {
1658 nsSize adjSize;
1659 bool doReturn;
1660 nsresult rv = SetRootView(po, doReturn, documentIsTopLevel, adjSize);
1662 MOZ_ASSERT(!documentIsTopLevel, "How could this happen?");
1664 if (NS_FAILED(rv) || doReturn) {
1665 return rv;
1666 }
1667 }
1669 po->mPresShell->FlushPendingNotifications(Flush_Layout);
1671 nsresult rv = UpdateSelectionAndShrinkPrintObject(po, documentIsTopLevel);
1672 NS_ENSURE_SUCCESS(rv, rv);
1673 }
1674 return NS_OK;
1675 }
1677 //-------------------------------------------------------
1678 nsresult
1679 nsPrintEngine::SetupToPrintContent()
1680 {
1681 nsresult rv;
1683 bool didReconstruction = false;
1685 // If some new content got loaded since the initial reflow rebuild
1686 // everything.
1687 if (mDidLoadDataForPrinting) {
1688 rv = ReconstructAndReflow(DoSetPixelScale());
1689 didReconstruction = true;
1690 NS_ENSURE_SUCCESS(rv, rv);
1691 }
1693 // Here is where we figure out if extra reflow for shrinking the content
1694 // is required.
1695 // But skip this step if we are in PrintPreview
1696 bool ppIsShrinkToFit = mPrtPreview && mPrtPreview->mShrinkToFit;
1697 if (mPrt->mShrinkToFit && !ppIsShrinkToFit) {
1698 // Now look for the PO that has the smallest percent for shrink to fit
1699 if (mPrt->mPrintDocList.Length() > 1 && mPrt->mPrintObject->mFrameType == eFrameSet) {
1700 nsPrintObject* smallestPO = FindSmallestSTF();
1701 NS_ASSERTION(smallestPO, "There must always be an XMost PO!");
1702 if (smallestPO) {
1703 // Calc the shrinkage based on the entire content area
1704 mPrt->mShrinkRatio = smallestPO->mShrinkRatio;
1705 }
1706 } else {
1707 // Single document so use the Shrink as calculated for the PO
1708 mPrt->mShrinkRatio = mPrt->mPrintObject->mShrinkRatio;
1709 }
1711 if (mPrt->mShrinkRatio < 0.998f) {
1712 rv = ReconstructAndReflow(true);
1713 didReconstruction = true;
1714 NS_ENSURE_SUCCESS(rv, rv);
1715 }
1717 #ifdef PR_LOGGING
1718 float calcRatio = 0.0f;
1719 if (mPrt->mPrintDocList.Length() > 1 && mPrt->mPrintObject->mFrameType == eFrameSet) {
1720 nsPrintObject* smallestPO = FindSmallestSTF();
1721 NS_ASSERTION(smallestPO, "There must always be an XMost PO!");
1722 if (smallestPO) {
1723 // Calc the shrinkage based on the entire content area
1724 calcRatio = smallestPO->mShrinkRatio;
1725 }
1726 } else {
1727 // Single document so use the Shrink as calculated for the PO
1728 calcRatio = mPrt->mPrintObject->mShrinkRatio;
1729 }
1730 PR_PL(("**************************************************************************\n"));
1731 PR_PL(("STF Ratio is: %8.5f Effective Ratio: %8.5f Diff: %8.5f\n", mPrt->mShrinkRatio, calcRatio, mPrt->mShrinkRatio-calcRatio));
1732 PR_PL(("**************************************************************************\n"));
1733 #endif
1734 }
1736 // If the frames got reconstructed and reflowed the number of pages might
1737 // has changed.
1738 if (didReconstruction) {
1739 FirePrintPreviewUpdateEvent();
1740 }
1742 DUMP_DOC_LIST(("\nAfter Reflow------------------------------------------"));
1743 PR_PL(("\n"));
1744 PR_PL(("-------------------------------------------------------\n"));
1745 PR_PL(("\n"));
1747 CalcNumPrintablePages(mPrt->mNumPrintablePages);
1749 PR_PL(("--- Printing %d pages\n", mPrt->mNumPrintablePages));
1750 DUMP_DOC_TREELAYOUT;
1752 // Print listener setup...
1753 if (mPrt != nullptr) {
1754 mPrt->OnStartPrinting();
1755 }
1757 char16_t* fileName = nullptr;
1758 // check to see if we are printing to a file
1759 bool isPrintToFile = false;
1760 mPrt->mPrintSettings->GetPrintToFile(&isPrintToFile);
1761 if (isPrintToFile) {
1762 // On some platforms The BeginDocument needs to know the name of the file
1763 // and it uses the PrintService to get it, so we need to set it into the PrintService here
1764 mPrt->mPrintSettings->GetToFileName(&fileName);
1765 }
1767 nsAutoString docTitleStr;
1768 nsAutoString docURLStr;
1769 GetDisplayTitleAndURL(mPrt->mPrintObject, docTitleStr, docURLStr, eDocTitleDefURLDoc);
1771 int32_t startPage = 1;
1772 int32_t endPage = mPrt->mNumPrintablePages;
1774 int16_t printRangeType = nsIPrintSettings::kRangeAllPages;
1775 mPrt->mPrintSettings->GetPrintRange(&printRangeType);
1776 if (printRangeType == nsIPrintSettings::kRangeSpecifiedPageRange) {
1777 mPrt->mPrintSettings->GetStartPageRange(&startPage);
1778 mPrt->mPrintSettings->GetEndPageRange(&endPage);
1779 if (endPage > mPrt->mNumPrintablePages) {
1780 endPage = mPrt->mNumPrintablePages;
1781 }
1782 }
1784 rv = NS_OK;
1785 // BeginDocument may pass back a FAILURE code
1786 // i.e. On Windows, if you are printing to a file and hit "Cancel"
1787 // to the "File Name" dialog, this comes back as an error
1788 // Don't start printing when regression test are executed
1789 if (!mPrt->mDebugFilePtr && mIsDoingPrinting) {
1790 rv = mPrt->mPrintDC->BeginDocument(docTitleStr, fileName, startPage, endPage);
1791 }
1793 if (mIsCreatingPrintPreview) {
1794 // Copy docTitleStr and docURLStr to the pageSequenceFrame, to be displayed
1795 // in the header
1796 nsIPageSequenceFrame *seqFrame = mPrt->mPrintObject->mPresShell->GetPageSequenceFrame();
1797 if (seqFrame) {
1798 seqFrame->StartPrint(mPrt->mPrintObject->mPresContext,
1799 mPrt->mPrintSettings, docTitleStr, docURLStr);
1800 }
1801 }
1803 PR_PL(("****************** Begin Document ************************\n"));
1805 NS_ENSURE_SUCCESS(rv, rv);
1807 // This will print the docshell document
1808 // when it completes asynchronously in the DonePrintingPages method
1809 // it will check to see if there are more docshells to be printed and
1810 // then PrintDocContent will be called again.
1812 if (mIsDoingPrinting) {
1813 PrintDocContent(mPrt->mPrintObject, rv); // ignore return value
1814 }
1816 return rv;
1817 }
1819 //-------------------------------------------------------
1820 // Recursively reflow each sub-doc and then calc
1821 // all the frame locations of the sub-docs
1822 nsresult
1823 nsPrintEngine::ReflowDocList(nsPrintObject* aPO, bool aSetPixelScale)
1824 {
1825 NS_ENSURE_ARG_POINTER(aPO);
1827 // Check to see if the subdocument's element has been hidden by the parent document
1828 if (aPO->mParent && aPO->mParent->mPresShell) {
1829 nsIFrame* frame = aPO->mContent ? aPO->mContent->GetPrimaryFrame() : nullptr;
1830 if (!frame || !frame->StyleVisibility()->IsVisible()) {
1831 SetPrintPO(aPO, false);
1832 aPO->mInvisible = true;
1833 return NS_OK;
1834 }
1835 }
1837 UpdateZoomRatio(aPO, aSetPixelScale);
1839 nsresult rv;
1840 // Reflow the PO
1841 rv = ReflowPrintObject(aPO);
1842 NS_ENSURE_SUCCESS(rv, rv);
1844 int32_t cnt = aPO->mKids.Length();
1845 for (int32_t i=0;i<cnt;i++) {
1846 rv = ReflowDocList(aPO->mKids[i], aSetPixelScale);
1847 NS_ENSURE_SUCCESS(rv, rv);
1848 }
1849 return NS_OK;
1850 }
1852 void
1853 nsPrintEngine::FirePrintPreviewUpdateEvent()
1854 {
1855 // Dispatch the event only while in PrintPreview. When printing, there is no
1856 // listener bound to this event and therefore no need to dispatch it.
1857 if (mIsDoingPrintPreview && !mIsDoingPrinting) {
1858 nsCOMPtr<nsIContentViewer> cv = do_QueryInterface(mDocViewerPrint);
1859 (new AsyncEventDispatcher(
1860 cv->GetDocument(), NS_LITERAL_STRING("printPreviewUpdate"), true, true)
1861 )->RunDOMEventWhenSafe();
1862 }
1863 }
1865 nsresult
1866 nsPrintEngine::InitPrintDocConstruction(bool aHandleError)
1867 {
1868 nsresult rv;
1869 rv = ReflowDocList(mPrt->mPrintObject, DoSetPixelScale());
1870 NS_ENSURE_SUCCESS(rv, rv);
1872 FirePrintPreviewUpdateEvent();
1874 if (mLoadCounter == 0) {
1875 AfterNetworkPrint(aHandleError);
1876 }
1877 return rv;
1878 }
1880 nsresult
1881 nsPrintEngine::AfterNetworkPrint(bool aHandleError)
1882 {
1883 nsCOMPtr<nsIWebProgress> webProgress = do_QueryInterface(mPrt->mPrintObject->mDocShell);
1885 webProgress->RemoveProgressListener(
1886 static_cast<nsIWebProgressListener*>(this));
1888 nsresult rv;
1889 if (mIsDoingPrinting) {
1890 rv = DocumentReadyForPrinting();
1891 } else {
1892 rv = FinishPrintPreview();
1893 }
1895 /* cleaup on failure + notify user */
1896 if (aHandleError && NS_FAILED(rv)) {
1897 CleanupOnFailure(rv, !mIsDoingPrinting);
1898 }
1900 return rv;
1901 }
1903 ////////////////////////////////////////////////////////////////////////////////
1904 // nsIWebProgressListener
1906 NS_IMETHODIMP
1907 nsPrintEngine::OnStateChange(nsIWebProgress* aWebProgress,
1908 nsIRequest* aRequest,
1909 uint32_t aStateFlags,
1910 nsresult aStatus)
1911 {
1912 nsAutoCString name;
1913 aRequest->GetName(name);
1914 if (name.Equals("about:document-onload-blocker")) {
1915 return NS_OK;
1916 }
1917 if (aStateFlags & STATE_START) {
1918 nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
1920 ++mLoadCounter;
1921 } else if (aStateFlags & STATE_STOP) {
1922 mDidLoadDataForPrinting = true;
1923 --mLoadCounter;
1925 // If all resources are loaded, then do a small timeout and if there
1926 // are still no new requests, then another reflow.
1927 if (mLoadCounter == 0) {
1928 AfterNetworkPrint(true);
1929 }
1930 }
1931 return NS_OK;
1932 }
1936 NS_IMETHODIMP
1937 nsPrintEngine::OnProgressChange(nsIWebProgress* aWebProgress,
1938 nsIRequest* aRequest,
1939 int32_t aCurSelfProgress,
1940 int32_t aMaxSelfProgress,
1941 int32_t aCurTotalProgress,
1942 int32_t aMaxTotalProgress)
1943 {
1944 NS_NOTREACHED("notification excluded in AddProgressListener(...)");
1945 return NS_OK;
1946 }
1948 NS_IMETHODIMP
1949 nsPrintEngine::OnLocationChange(nsIWebProgress* aWebProgress,
1950 nsIRequest* aRequest,
1951 nsIURI* aLocation,
1952 uint32_t aFlags)
1953 {
1954 NS_NOTREACHED("notification excluded in AddProgressListener(...)");
1955 return NS_OK;
1956 }
1958 NS_IMETHODIMP
1959 nsPrintEngine::OnStatusChange(nsIWebProgress *aWebProgress,
1960 nsIRequest *aRequest,
1961 nsresult aStatus,
1962 const char16_t *aMessage)
1963 {
1964 NS_NOTREACHED("notification excluded in AddProgressListener(...)");
1965 return NS_OK;
1966 }
1968 NS_IMETHODIMP
1969 nsPrintEngine::OnSecurityChange(nsIWebProgress *aWebProgress,
1970 nsIRequest *aRequest,
1971 uint32_t aState)
1972 {
1973 NS_NOTREACHED("notification excluded in AddProgressListener(...)");
1974 return NS_OK;
1975 }
1977 //-------------------------------------------------------
1979 void
1980 nsPrintEngine::UpdateZoomRatio(nsPrintObject* aPO, bool aSetPixelScale)
1981 {
1982 // Here is where we set the shrinkage value into the DC
1983 // and this is what actually makes it shrink
1984 if (aSetPixelScale && aPO->mFrameType != eIFrame) {
1985 float ratio;
1986 if (mPrt->mPrintFrameType == nsIPrintSettings::kFramesAsIs || mPrt->mPrintFrameType == nsIPrintSettings::kNoFrames) {
1987 ratio = mPrt->mShrinkRatio - 0.005f; // round down
1988 } else {
1989 ratio = aPO->mShrinkRatio - 0.005f; // round down
1990 }
1991 aPO->mZoomRatio = ratio;
1992 } else if (!mPrt->mShrinkToFit) {
1993 double scaling;
1994 mPrt->mPrintSettings->GetScaling(&scaling);
1995 aPO->mZoomRatio = float(scaling);
1996 }
1997 }
1999 nsresult
2000 nsPrintEngine::UpdateSelectionAndShrinkPrintObject(nsPrintObject* aPO,
2001 bool aDocumentIsTopLevel)
2002 {
2003 nsCOMPtr<nsIPresShell> displayShell = aPO->mDocShell->GetPresShell();
2004 // Transfer Selection Ranges to the new Print PresShell
2005 nsRefPtr<Selection> selection, selectionPS;
2006 // It's okay if there is no display shell, just skip copying the selection
2007 if (displayShell) {
2008 selection = displayShell->GetCurrentSelection(nsISelectionController::SELECTION_NORMAL);
2009 }
2010 selectionPS = aPO->mPresShell->GetCurrentSelection(nsISelectionController::SELECTION_NORMAL);
2012 // Reset all existing selection ranges that might have been added by calling
2013 // this function before.
2014 if (selectionPS) {
2015 selectionPS->RemoveAllRanges();
2016 }
2017 if (selection && selectionPS) {
2018 int32_t cnt = selection->GetRangeCount();
2019 int32_t inx;
2020 for (inx = 0; inx < cnt; ++inx) {
2021 selectionPS->AddRange(selection->GetRangeAt(inx));
2022 }
2023 }
2025 // If we are trying to shrink the contents to fit on the page
2026 // we must first locate the "pageContent" frame
2027 // Then we walk the frame tree and look for the "xmost" frame
2028 // this is the frame where the right-hand side of the frame extends
2029 // the furthest
2030 if (mPrt->mShrinkToFit && aDocumentIsTopLevel) {
2031 nsIPageSequenceFrame* pageSequence = aPO->mPresShell->GetPageSequenceFrame();
2032 NS_ENSURE_STATE(pageSequence);
2033 pageSequence->GetSTFPercent(aPO->mShrinkRatio);
2034 // Limit the shrink-to-fit scaling for some text-ish type of documents.
2035 nsAutoString contentType;
2036 aPO->mPresShell->GetDocument()->GetContentType(contentType);
2037 if (contentType.EqualsLiteral("application/xhtml+xml") ||
2038 StringBeginsWith(contentType, NS_LITERAL_STRING("text/"))) {
2039 int32_t limitPercent =
2040 Preferences::GetInt("print.shrink-to-fit.scale-limit-percent", 20);
2041 limitPercent = std::max(0, limitPercent);
2042 limitPercent = std::min(100, limitPercent);
2043 float minShrinkRatio = float(limitPercent) / 100;
2044 aPO->mShrinkRatio = std::max(aPO->mShrinkRatio, minShrinkRatio);
2045 }
2046 }
2047 return NS_OK;
2048 }
2050 bool
2051 nsPrintEngine::DoSetPixelScale()
2052 {
2053 // This is an Optimization
2054 // If we are in PP then we already know all the shrinkage information
2055 // so just transfer it to the PrintData and we will skip the extra shrinkage reflow
2056 //
2057 // doSetPixelScale tells Reflow whether to set the shrinkage value into the DC
2058 // The first time we do not want to do this, the second time through we do
2059 bool doSetPixelScale = false;
2060 bool ppIsShrinkToFit = mPrtPreview && mPrtPreview->mShrinkToFit;
2061 if (ppIsShrinkToFit) {
2062 mPrt->mShrinkRatio = mPrtPreview->mShrinkRatio;
2063 doSetPixelScale = true;
2064 }
2065 return doSetPixelScale;
2066 }
2068 nsView*
2069 nsPrintEngine::GetParentViewForRoot()
2070 {
2071 if (mIsCreatingPrintPreview) {
2072 nsCOMPtr<nsIContentViewer> cv = do_QueryInterface(mDocViewerPrint);
2073 if (cv) {
2074 return cv->FindContainerView();
2075 }
2076 }
2077 return nullptr;
2078 }
2080 nsresult
2081 nsPrintEngine::SetRootView(
2082 nsPrintObject* aPO,
2083 bool& doReturn,
2084 bool& documentIsTopLevel,
2085 nsSize& adjSize
2086 )
2087 {
2088 bool canCreateScrollbars = true;
2090 nsView* rootView;
2091 nsView* parentView = nullptr;
2093 doReturn = false;
2095 if (aPO->mParent && aPO->mParent->IsPrintable()) {
2096 nsIFrame* frame = aPO->mContent ? aPO->mContent->GetPrimaryFrame() : nullptr;
2097 // Without a frame, this document can't be displayed; therefore, there is no
2098 // point to reflowing it
2099 if (!frame) {
2100 SetPrintPO(aPO, false);
2101 doReturn = true;
2102 return NS_OK;
2103 }
2105 //XXX If printing supported printing document hierarchies with non-constant
2106 // zoom this would be wrong as we use the same mPrt->mPrintDC for all
2107 // subdocuments.
2108 adjSize = frame->GetContentRect().Size();
2109 documentIsTopLevel = false;
2110 // presshell exists because parent is printable
2112 // the top nsPrintObject's widget will always have scrollbars
2113 if (frame && frame->GetType() == nsGkAtoms::subDocumentFrame) {
2114 nsView* view = frame->GetView();
2115 NS_ENSURE_TRUE(view, NS_ERROR_FAILURE);
2116 view = view->GetFirstChild();
2117 NS_ENSURE_TRUE(view, NS_ERROR_FAILURE);
2118 parentView = view;
2119 canCreateScrollbars = false;
2120 }
2121 } else {
2122 nscoord pageWidth, pageHeight;
2123 mPrt->mPrintDC->GetDeviceSurfaceDimensions(pageWidth, pageHeight);
2124 adjSize = nsSize(pageWidth, pageHeight);
2125 documentIsTopLevel = true;
2126 parentView = GetParentViewForRoot();
2127 }
2129 if (aPO->mViewManager->GetRootView()) {
2130 // Reuse the root view that is already on the root frame.
2131 rootView = aPO->mViewManager->GetRootView();
2132 // Remove it from its existing parent if necessary
2133 aPO->mViewManager->RemoveChild(rootView);
2134 rootView->SetParent(parentView);
2135 } else {
2136 // Create a child window of the parent that is our "root view/window"
2137 nsRect tbounds = nsRect(nsPoint(0, 0), adjSize);
2138 rootView = aPO->mViewManager->CreateView(tbounds, parentView);
2139 NS_ENSURE_TRUE(rootView, NS_ERROR_OUT_OF_MEMORY);
2140 }
2142 if (mIsCreatingPrintPreview && documentIsTopLevel) {
2143 aPO->mPresContext->SetPaginatedScrolling(canCreateScrollbars);
2144 }
2146 // Setup hierarchical relationship in view manager
2147 aPO->mViewManager->SetRootView(rootView);
2149 return NS_OK;
2150 }
2152 // Reflow a nsPrintObject
2153 nsresult
2154 nsPrintEngine::ReflowPrintObject(nsPrintObject * aPO)
2155 {
2156 NS_ENSURE_STATE(aPO);
2158 if (!aPO->IsPrintable()) {
2159 return NS_OK;
2160 }
2162 NS_ASSERTION(!aPO->mPresContext, "Recreating prescontext");
2164 // create the PresContext
2165 nsPresContext::nsPresContextType type =
2166 mIsCreatingPrintPreview ? nsPresContext::eContext_PrintPreview:
2167 nsPresContext::eContext_Print;
2168 nsView* parentView =
2169 aPO->mParent && aPO->mParent->IsPrintable() ? nullptr : GetParentViewForRoot();
2170 aPO->mPresContext = parentView ?
2171 new nsPresContext(aPO->mDocument, type) :
2172 new nsRootPresContext(aPO->mDocument, type);
2173 NS_ENSURE_TRUE(aPO->mPresContext, NS_ERROR_OUT_OF_MEMORY);
2174 aPO->mPresContext->SetPrintSettings(mPrt->mPrintSettings);
2176 // set the presentation context to the value in the print settings
2177 bool printBGColors;
2178 mPrt->mPrintSettings->GetPrintBGColors(&printBGColors);
2179 aPO->mPresContext->SetBackgroundColorDraw(printBGColors);
2180 mPrt->mPrintSettings->GetPrintBGImages(&printBGColors);
2181 aPO->mPresContext->SetBackgroundImageDraw(printBGColors);
2183 // init it with the DC
2184 nsresult rv = aPO->mPresContext->Init(mPrt->mPrintDC);
2185 NS_ENSURE_SUCCESS(rv, rv);
2187 aPO->mViewManager = new nsViewManager();
2189 rv = aPO->mViewManager->Init(mPrt->mPrintDC);
2190 NS_ENSURE_SUCCESS(rv,rv);
2192 nsStyleSet* styleSet;
2193 rv = mDocViewerPrint->CreateStyleSet(aPO->mDocument, &styleSet);
2194 NS_ENSURE_SUCCESS(rv, rv);
2196 aPO->mPresShell = aPO->mDocument->CreateShell(aPO->mPresContext,
2197 aPO->mViewManager, styleSet);
2198 if (!aPO->mPresShell) {
2199 delete styleSet;
2200 return NS_ERROR_FAILURE;
2201 }
2203 styleSet->EndUpdate();
2205 // The pres shell now owns the style set object.
2208 bool doReturn = false;;
2209 bool documentIsTopLevel = false;
2210 nsSize adjSize;
2212 rv = SetRootView(aPO, doReturn, documentIsTopLevel, adjSize);
2214 if (NS_FAILED(rv) || doReturn) {
2215 return rv;
2216 }
2218 PR_PL(("In DV::ReflowPrintObject PO: %p pS: %p (%9s) Setting w,h to %d,%d\n", aPO, aPO->mPresShell.get(),
2219 gFrameTypesStr[aPO->mFrameType], adjSize.width, adjSize.height));
2222 // This docshell stuff is weird; will go away when we stop having multiple
2223 // presentations per document
2224 aPO->mPresContext->SetContainer(aPO->mDocShell);
2226 aPO->mPresShell->BeginObservingDocument();
2228 aPO->mPresContext->SetPageSize(adjSize);
2229 aPO->mPresContext->SetIsRootPaginatedDocument(documentIsTopLevel);
2230 aPO->mPresContext->SetPageScale(aPO->mZoomRatio);
2231 // Calculate scale factor from printer to screen
2232 float printDPI = float(mPrt->mPrintDC->AppUnitsPerCSSInch()) /
2233 float(mPrt->mPrintDC->AppUnitsPerDevPixel());
2234 aPO->mPresContext->SetPrintPreviewScale(mScreenDPI / printDPI);
2236 if (mIsCreatingPrintPreview && documentIsTopLevel) {
2237 mDocViewerPrint->SetPrintPreviewPresentation(aPO->mViewManager,
2238 aPO->mPresContext,
2239 aPO->mPresShell);
2240 }
2242 rv = aPO->mPresShell->Initialize(adjSize.width, adjSize.height);
2244 NS_ENSURE_SUCCESS(rv, rv);
2245 NS_ASSERTION(aPO->mPresShell, "Presshell should still be here");
2247 // Process the reflow event Initialize posted
2248 aPO->mPresShell->FlushPendingNotifications(Flush_Layout);
2250 rv = UpdateSelectionAndShrinkPrintObject(aPO, documentIsTopLevel);
2251 NS_ENSURE_SUCCESS(rv, rv);
2253 #ifdef EXTENDED_DEBUG_PRINTING
2254 if (kPrintingLogMod && kPrintingLogMod->level == DUMP_LAYOUT_LEVEL) {
2255 nsAutoCString docStr;
2256 nsAutoCString urlStr;
2257 GetDocTitleAndURL(aPO, docStr, urlStr);
2258 char filename[256];
2259 sprintf(filename, "print_dump_%d.txt", gDumpFileNameCnt++);
2260 // Dump all the frames and view to a a file
2261 FILE * fd = fopen(filename, "w");
2262 if (fd) {
2263 nsIFrame *theRootFrame =
2264 aPO->mPresShell->FrameManager()->GetRootFrame();
2265 fprintf(fd, "Title: %s\n", docStr.get());
2266 fprintf(fd, "URL: %s\n", urlStr.get());
2267 fprintf(fd, "--------------- Frames ----------------\n");
2268 nsRefPtr<nsRenderingContext> renderingContext =
2269 mPrt->mPrintDocDC->CreateRenderingContext();
2270 RootFrameList(aPO->mPresContext, fd, 0);
2271 //DumpFrames(fd, aPO->mPresContext, renderingContext, theRootFrame, 0);
2272 fprintf(fd, "---------------------------------------\n\n");
2273 fprintf(fd, "--------------- Views From Root Frame----------------\n");
2274 nsView* v = theRootFrame->GetView();
2275 if (v) {
2276 v->List(fd);
2277 } else {
2278 printf("View is null!\n");
2279 }
2280 if (docShell) {
2281 fprintf(fd, "--------------- All Views ----------------\n");
2282 DumpViews(docShell, fd);
2283 fprintf(fd, "---------------------------------------\n\n");
2284 }
2285 fclose(fd);
2286 }
2287 }
2288 #endif
2290 return NS_OK;
2291 }
2293 //-------------------------------------------------------
2294 // Figure out how many documents and how many total pages we are printing
2295 void
2296 nsPrintEngine::CalcNumPrintablePages(int32_t& aNumPages)
2297 {
2298 aNumPages = 0;
2299 // Count the number of printable documents
2300 // and printable pages
2301 for (uint32_t i=0; i<mPrt->mPrintDocList.Length(); i++) {
2302 nsPrintObject* po = mPrt->mPrintDocList.ElementAt(i);
2303 NS_ASSERTION(po, "nsPrintObject can't be null!");
2304 if (po->mPresContext && po->mPresContext->IsRootPaginatedDocument()) {
2305 nsIPageSequenceFrame* pageSequence = po->mPresShell->GetPageSequenceFrame();
2306 nsIFrame * seqFrame = do_QueryFrame(pageSequence);
2307 if (seqFrame) {
2308 nsIFrame* frame = seqFrame->GetFirstPrincipalChild();
2309 while (frame) {
2310 aNumPages++;
2311 frame = frame->GetNextSibling();
2312 }
2313 }
2314 }
2315 }
2316 }
2318 //-----------------------------------------------------------------
2319 //-- Done: Reflow Methods
2320 //-----------------------------------------------------------------
2322 //-----------------------------------------------------------------
2323 //-- Section: Printing Methods
2324 //-----------------------------------------------------------------
2326 //-------------------------------------------------------
2327 // Called for each DocShell that needs to be printed
2328 bool
2329 nsPrintEngine::PrintDocContent(nsPrintObject* aPO, nsresult& aStatus)
2330 {
2331 NS_ASSERTION(aPO, "Pointer is null!");
2332 aStatus = NS_OK;
2334 if (!aPO->mHasBeenPrinted && aPO->IsPrintable()) {
2335 aStatus = DoPrint(aPO);
2336 return true;
2337 }
2339 // If |aPO->mPrintAsIs| and |aPO->mHasBeenPrinted| are true,
2340 // the kids frames are already processed in |PrintPage|.
2341 if (!aPO->mInvisible && !(aPO->mPrintAsIs && aPO->mHasBeenPrinted)) {
2342 for (uint32_t i=0;i<aPO->mKids.Length();i++) {
2343 nsPrintObject* po = aPO->mKids[i];
2344 bool printed = PrintDocContent(po, aStatus);
2345 if (printed || NS_FAILED(aStatus)) {
2346 return true;
2347 }
2348 }
2349 }
2350 return false;
2351 }
2353 static already_AddRefed<nsIDOMNode>
2354 GetEqualNodeInCloneTree(nsIDOMNode* aNode, nsIDocument* aDoc)
2355 {
2356 nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
2357 // Selections in anonymous subtrees aren't supported.
2358 if (content && content->IsInAnonymousSubtree()) {
2359 return nullptr;
2360 }
2362 nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
2363 NS_ENSURE_TRUE(node, nullptr);
2365 nsTArray<int32_t> indexArray;
2366 nsINode* current = node;
2367 NS_ENSURE_TRUE(current, nullptr);
2368 while (current) {
2369 nsINode* parent = current->GetParentNode();
2370 if (!parent) {
2371 break;
2372 }
2373 int32_t index = parent->IndexOf(current);
2374 NS_ENSURE_TRUE(index >= 0, nullptr);
2375 indexArray.AppendElement(index);
2376 current = parent;
2377 }
2378 NS_ENSURE_TRUE(current->IsNodeOfType(nsINode::eDOCUMENT), nullptr);
2380 current = aDoc;
2381 for (int32_t i = indexArray.Length() - 1; i >= 0; --i) {
2382 current = current->GetChildAt(indexArray[i]);
2383 NS_ENSURE_TRUE(current, nullptr);
2384 }
2385 nsCOMPtr<nsIDOMNode> result = do_QueryInterface(current);
2386 return result.forget();
2387 }
2389 static void
2390 CloneRangeToSelection(nsRange* aRange, nsIDocument* aDoc,
2391 Selection* aSelection)
2392 {
2393 if (aRange->Collapsed()) {
2394 return;
2395 }
2397 nsCOMPtr<nsIDOMNode> startContainer, endContainer;
2398 aRange->GetStartContainer(getter_AddRefs(startContainer));
2399 int32_t startOffset = aRange->StartOffset();
2400 aRange->GetEndContainer(getter_AddRefs(endContainer));
2401 int32_t endOffset = aRange->EndOffset();
2402 NS_ENSURE_TRUE_VOID(startContainer && endContainer);
2404 nsCOMPtr<nsIDOMNode> newStart = GetEqualNodeInCloneTree(startContainer, aDoc);
2405 nsCOMPtr<nsIDOMNode> newEnd = GetEqualNodeInCloneTree(endContainer, aDoc);
2406 NS_ENSURE_TRUE_VOID(newStart && newEnd);
2408 nsCOMPtr<nsINode> newStartNode = do_QueryInterface(newStart);
2409 NS_ENSURE_TRUE_VOID(newStartNode);
2411 nsRefPtr<nsRange> range = new nsRange(newStartNode);
2412 nsresult rv = range->SetStart(newStartNode, startOffset);
2413 NS_ENSURE_SUCCESS_VOID(rv);
2414 rv = range->SetEnd(newEnd, endOffset);
2415 NS_ENSURE_SUCCESS_VOID(rv);
2417 aSelection->AddRange(range);
2418 }
2420 static nsresult CloneSelection(nsIDocument* aOrigDoc, nsIDocument* aDoc)
2421 {
2422 nsIPresShell* origShell = aOrigDoc->GetShell();
2423 nsIPresShell* shell = aDoc->GetShell();
2424 NS_ENSURE_STATE(origShell && shell);
2426 nsRefPtr<Selection> origSelection =
2427 origShell->GetCurrentSelection(nsISelectionController::SELECTION_NORMAL);
2428 nsRefPtr<Selection> selection =
2429 shell->GetCurrentSelection(nsISelectionController::SELECTION_NORMAL);
2430 NS_ENSURE_STATE(origSelection && selection);
2432 int32_t rangeCount = origSelection->GetRangeCount();
2433 for (int32_t i = 0; i < rangeCount; ++i) {
2434 CloneRangeToSelection(origSelection->GetRangeAt(i), aDoc, selection);
2435 }
2436 return NS_OK;
2437 }
2439 //-------------------------------------------------------
2440 nsresult
2441 nsPrintEngine::DoPrint(nsPrintObject * aPO)
2442 {
2443 PR_PL(("\n"));
2444 PR_PL(("**************************** %s ****************************\n", gFrameTypesStr[aPO->mFrameType]));
2445 PR_PL(("****** In DV::DoPrint PO: %p \n", aPO));
2447 nsIPresShell* poPresShell = aPO->mPresShell;
2448 nsPresContext* poPresContext = aPO->mPresContext;
2450 NS_ASSERTION(poPresContext, "PrintObject has not been reflowed");
2451 NS_ASSERTION(poPresContext->Type() != nsPresContext::eContext_PrintPreview,
2452 "How did this context end up here?");
2454 if (mPrt->mPrintProgressParams) {
2455 SetDocAndURLIntoProgress(aPO, mPrt->mPrintProgressParams);
2456 }
2458 {
2459 int16_t printRangeType = nsIPrintSettings::kRangeAllPages;
2460 nsresult rv;
2461 if (mPrt->mPrintSettings != nullptr) {
2462 mPrt->mPrintSettings->GetPrintRange(&printRangeType);
2463 }
2465 // Ask the page sequence frame to print all the pages
2466 nsIPageSequenceFrame* pageSequence = poPresShell->GetPageSequenceFrame();
2467 NS_ASSERTION(nullptr != pageSequence, "no page sequence frame");
2469 // We are done preparing for printing, so we can turn this off
2470 mPrt->mPreparingForPrint = false;
2472 // mPrt->mDebugFilePtr this is onlu non-null when compiled for debugging
2473 if (nullptr != mPrt->mDebugFilePtr) {
2474 #ifdef DEBUG
2475 // output the regression test
2476 nsIFrame* root = poPresShell->FrameManager()->GetRootFrame();
2477 root->DumpRegressionData(poPresContext, mPrt->mDebugFilePtr, 0);
2478 fclose(mPrt->mDebugFilePtr);
2479 SetIsPrinting(false);
2480 #endif
2481 } else {
2482 #ifdef EXTENDED_DEBUG_PRINTING
2483 nsIFrame* rootFrame = poPresShell->FrameManager()->GetRootFrame();
2484 if (aPO->IsPrintable()) {
2485 nsAutoCString docStr;
2486 nsAutoCString urlStr;
2487 GetDocTitleAndURL(aPO, docStr, urlStr);
2488 DumpLayoutData(docStr.get(), urlStr.get(), poPresContext, mPrt->mPrintDocDC, rootFrame, docShell, nullptr);
2489 }
2490 #endif
2492 if (!mPrt->mPrintSettings) {
2493 // not sure what to do here!
2494 SetIsPrinting(false);
2495 return NS_ERROR_FAILURE;
2496 }
2498 nsAutoString docTitleStr;
2499 nsAutoString docURLStr;
2500 GetDisplayTitleAndURL(aPO, docTitleStr, docURLStr, eDocTitleDefBlank);
2502 if (nsIPrintSettings::kRangeSelection == printRangeType) {
2503 CloneSelection(aPO->mDocument->GetOriginalDocument(), aPO->mDocument);
2505 poPresContext->SetIsRenderingOnlySelection(true);
2506 // temporarily creating rendering context
2507 // which is needed to find the selection frames
2508 nsRefPtr<nsRenderingContext> rc =
2509 mPrt->mPrintDC->CreateRenderingContext();
2511 // find the starting and ending page numbers
2512 // via the selection
2513 nsIFrame* startFrame;
2514 nsIFrame* endFrame;
2515 int32_t startPageNum;
2516 int32_t endPageNum;
2517 nsRect startRect;
2518 nsRect endRect;
2520 nsRefPtr<Selection> selectionPS =
2521 poPresShell->GetCurrentSelection(nsISelectionController::SELECTION_NORMAL);
2523 rv = GetPageRangeForSelection(poPresShell, poPresContext, *rc, selectionPS, pageSequence,
2524 &startFrame, startPageNum, startRect,
2525 &endFrame, endPageNum, endRect);
2526 if (NS_SUCCEEDED(rv)) {
2527 mPrt->mPrintSettings->SetStartPageRange(startPageNum);
2528 mPrt->mPrintSettings->SetEndPageRange(endPageNum);
2529 nsIntMargin marginTwips(0,0,0,0);
2530 nsIntMargin unwrtMarginTwips(0,0,0,0);
2531 mPrt->mPrintSettings->GetMarginInTwips(marginTwips);
2532 mPrt->mPrintSettings->GetUnwriteableMarginInTwips(unwrtMarginTwips);
2533 nsMargin totalMargin = poPresContext->CSSTwipsToAppUnits(marginTwips +
2534 unwrtMarginTwips);
2535 if (startPageNum == endPageNum) {
2536 startRect.y -= totalMargin.top;
2537 endRect.y -= totalMargin.top;
2539 // Clip out selection regions above the top of the first page
2540 if (startRect.y < 0) {
2541 // Reduce height to be the height of the positive-territory
2542 // region of original rect
2543 startRect.height = std::max(0, startRect.YMost());
2544 startRect.y = 0;
2545 }
2546 if (endRect.y < 0) {
2547 // Reduce height to be the height of the positive-territory
2548 // region of original rect
2549 endRect.height = std::max(0, endRect.YMost());
2550 endRect.y = 0;
2551 }
2552 NS_ASSERTION(endRect.y >= startRect.y,
2553 "Selection end point should be after start point");
2554 NS_ASSERTION(startRect.height >= 0,
2555 "rect should have non-negative height.");
2556 NS_ASSERTION(endRect.height >= 0,
2557 "rect should have non-negative height.");
2559 nscoord selectionHgt = endRect.y + endRect.height - startRect.y;
2560 // XXX This is temporary fix for printing more than one page of a selection
2561 pageSequence->SetSelectionHeight(startRect.y * aPO->mZoomRatio,
2562 selectionHgt * aPO->mZoomRatio);
2564 // calc total pages by getting calculating the selection's height
2565 // and then dividing it by how page content frames will fit.
2566 nscoord pageWidth, pageHeight;
2567 mPrt->mPrintDC->GetDeviceSurfaceDimensions(pageWidth, pageHeight);
2568 pageHeight -= totalMargin.top + totalMargin.bottom;
2569 int32_t totalPages = NSToIntCeil(float(selectionHgt) * aPO->mZoomRatio / float(pageHeight));
2570 pageSequence->SetTotalNumPages(totalPages);
2571 }
2572 }
2573 }
2575 nsIFrame * seqFrame = do_QueryFrame(pageSequence);
2576 if (!seqFrame) {
2577 SetIsPrinting(false);
2578 return NS_ERROR_FAILURE;
2579 }
2581 mPageSeqFrame = pageSequence;
2582 mPageSeqFrame->StartPrint(poPresContext, mPrt->mPrintSettings, docTitleStr, docURLStr);
2584 // Schedule Page to Print
2585 PR_PL(("Scheduling Print of PO: %p (%s) \n", aPO, gFrameTypesStr[aPO->mFrameType]));
2586 StartPagePrintTimer(aPO);
2587 }
2588 }
2590 return NS_OK;
2591 }
2593 //---------------------------------------------------------------------
2594 void
2595 nsPrintEngine::SetDocAndURLIntoProgress(nsPrintObject* aPO,
2596 nsIPrintProgressParams* aParams)
2597 {
2598 NS_ASSERTION(aPO, "Must have valid nsPrintObject");
2599 NS_ASSERTION(aParams, "Must have valid nsIPrintProgressParams");
2601 if (!aPO || !aPO->mDocShell || !aParams) {
2602 return;
2603 }
2604 const uint32_t kTitleLength = 64;
2606 nsAutoString docTitleStr;
2607 nsAutoString docURLStr;
2608 GetDisplayTitleAndURL(aPO, docTitleStr, docURLStr, eDocTitleDefURLDoc);
2610 // Make sure the Titles & URLS don't get too long for the progress dialog
2611 EllipseLongString(docTitleStr, kTitleLength, false);
2612 EllipseLongString(docURLStr, kTitleLength, true);
2614 aParams->SetDocTitle(docTitleStr.get());
2615 aParams->SetDocURL(docURLStr.get());
2616 }
2618 //---------------------------------------------------------------------
2619 void
2620 nsPrintEngine::EllipseLongString(nsAString& aStr, const uint32_t aLen, bool aDoFront)
2621 {
2622 // Make sure the URLS don't get too long for the progress dialog
2623 if (aLen >= 3 && aStr.Length() > aLen) {
2624 if (aDoFront) {
2625 nsAutoString newStr;
2626 newStr.AppendLiteral("...");
2627 newStr += Substring(aStr, aStr.Length() - (aLen - 3), aLen - 3);
2628 aStr = newStr;
2629 } else {
2630 aStr.SetLength(aLen - 3);
2631 aStr.AppendLiteral("...");
2632 }
2633 }
2634 }
2636 static bool
2637 DocHasPrintCallbackCanvas(nsIDocument* aDoc, void* aData)
2638 {
2639 if (!aDoc) {
2640 return true;
2641 }
2642 Element* root = aDoc->GetRootElement();
2643 if (!root) {
2644 return true;
2645 }
2646 nsRefPtr<nsContentList> canvases = NS_GetContentList(root,
2647 kNameSpaceID_XHTML,
2648 NS_LITERAL_STRING("canvas"));
2649 uint32_t canvasCount = canvases->Length(true);
2650 for (uint32_t i = 0; i < canvasCount; ++i) {
2651 HTMLCanvasElement* canvas = HTMLCanvasElement::FromContentOrNull(canvases->Item(i, false));
2652 if (canvas && canvas->GetMozPrintCallback()) {
2653 // This subdocument has a print callback. Set result and return false to
2654 // stop iteration.
2655 *static_cast<bool*>(aData) = true;
2656 return false;
2657 }
2658 }
2659 return true;
2660 }
2662 static bool
2663 DocHasPrintCallbackCanvas(nsIDocument* aDoc)
2664 {
2665 bool result = false;
2666 aDoc->EnumerateSubDocuments(&DocHasPrintCallbackCanvas, static_cast<void*>(&result));
2667 return result;
2668 }
2670 /**
2671 * Checks to see if the document this print engine is associated with has any
2672 * canvases that have a mozPrintCallback.
2673 */
2674 bool
2675 nsPrintEngine::HasPrintCallbackCanvas()
2676 {
2677 if (!mDocument) {
2678 return false;
2679 }
2680 // First check this mDocument.
2681 bool result = false;
2682 DocHasPrintCallbackCanvas(mDocument, static_cast<void*>(&result));
2683 // Also check the sub documents.
2684 return result || DocHasPrintCallbackCanvas(mDocument);
2685 }
2687 //-------------------------------------------------------
2688 bool
2689 nsPrintEngine::PrePrintPage()
2690 {
2691 NS_ASSERTION(mPageSeqFrame, "mPageSeqFrame is null!");
2692 NS_ASSERTION(mPrt, "mPrt is null!");
2694 // Although these should NEVER be nullptr
2695 // This is added insurance, to make sure we don't crash in optimized builds
2696 if (!mPrt || !mPageSeqFrame) {
2697 return true; // means we are done preparing the page.
2698 }
2700 // Check setting to see if someone request it be cancelled
2701 bool isCancelled = false;
2702 mPrt->mPrintSettings->GetIsCancelled(&isCancelled);
2703 if (isCancelled)
2704 return true;
2706 // Ask mPageSeqFrame if the page is ready to be printed.
2707 // If the page doesn't get printed at all, the |done| will be |true|.
2708 bool done = false;
2709 nsresult rv = mPageSeqFrame->PrePrintNextPage(mPagePrintTimer, &done);
2710 if (NS_FAILED(rv)) {
2711 // ??? ::PrintPage doesn't set |mPrt->mIsAborted = true| if rv != NS_ERROR_ABORT,
2712 // but I don't really understand why this should be the right thing to do?
2713 // Shouldn't |mPrt->mIsAborted| set to true all the time if something
2714 // wents wrong?
2715 if (rv != NS_ERROR_ABORT) {
2716 ShowPrintErrorDialog(rv);
2717 mPrt->mIsAborted = true;
2718 }
2719 done = true;
2720 }
2721 return done;
2722 }
2724 bool
2725 nsPrintEngine::PrintPage(nsPrintObject* aPO,
2726 bool& aInRange)
2727 {
2728 NS_ASSERTION(aPO, "aPO is null!");
2729 NS_ASSERTION(mPageSeqFrame, "mPageSeqFrame is null!");
2730 NS_ASSERTION(mPrt, "mPrt is null!");
2732 // Although these should NEVER be nullptr
2733 // This is added insurance, to make sure we don't crash in optimized builds
2734 if (!mPrt || !aPO || !mPageSeqFrame) {
2735 ShowPrintErrorDialog(NS_ERROR_FAILURE);
2736 return true; // means we are done printing
2737 }
2739 PR_PL(("-----------------------------------\n"));
2740 PR_PL(("------ In DV::PrintPage PO: %p (%s)\n", aPO, gFrameTypesStr[aPO->mFrameType]));
2742 // Check setting to see if someone request it be cancelled
2743 bool isCancelled = false;
2744 mPrt->mPrintSettings->GetIsCancelled(&isCancelled);
2745 if (isCancelled || mPrt->mIsAborted)
2746 return true;
2748 int32_t pageNum, numPages, endPage;
2749 mPageSeqFrame->GetCurrentPageNum(&pageNum);
2750 mPageSeqFrame->GetNumPages(&numPages);
2752 bool donePrinting;
2753 bool isDoingPrintRange;
2754 mPageSeqFrame->IsDoingPrintRange(&isDoingPrintRange);
2755 if (isDoingPrintRange) {
2756 int32_t fromPage;
2757 int32_t toPage;
2758 mPageSeqFrame->GetPrintRange(&fromPage, &toPage);
2760 if (fromPage > numPages) {
2761 return true;
2762 }
2763 if (toPage > numPages) {
2764 toPage = numPages;
2765 }
2767 PR_PL(("****** Printing Page %d printing from %d to page %d\n", pageNum, fromPage, toPage));
2769 donePrinting = pageNum >= toPage;
2770 aInRange = pageNum >= fromPage && pageNum <= toPage;
2771 endPage = (toPage - fromPage)+1;
2772 } else {
2773 PR_PL(("****** Printing Page %d of %d page(s)\n", pageNum, numPages));
2775 donePrinting = pageNum >= numPages;
2776 endPage = numPages;
2777 aInRange = true;
2778 }
2780 // XXX This is wrong, but the actual behavior in the presence of a print
2781 // range sucks.
2782 if (mPrt->mPrintFrameType == nsIPrintSettings::kEachFrameSep)
2783 endPage = mPrt->mNumPrintablePages;
2785 mPrt->DoOnProgressChange(++mPrt->mNumPagesPrinted, endPage, false, 0);
2787 // Print the Page
2788 // if a print job was cancelled externally, an EndPage or BeginPage may
2789 // fail and the failure is passed back here.
2790 // Returning true means we are done printing.
2791 //
2792 // When rv == NS_ERROR_ABORT, it means we want out of the
2793 // print job without displaying any error messages
2794 nsresult rv = mPageSeqFrame->PrintNextPage();
2795 if (NS_FAILED(rv)) {
2796 if (rv != NS_ERROR_ABORT) {
2797 ShowPrintErrorDialog(rv);
2798 mPrt->mIsAborted = true;
2799 }
2800 return true;
2801 }
2803 mPageSeqFrame->DoPageEnd();
2805 return donePrinting;
2806 }
2808 /** ---------------------------------------------------
2809 * Find by checking frames type
2810 */
2811 nsresult
2812 nsPrintEngine::FindSelectionBoundsWithList(nsPresContext* aPresContext,
2813 nsRenderingContext& aRC,
2814 nsFrameList::Enumerator& aChildFrames,
2815 nsIFrame * aParentFrame,
2816 nsRect& aRect,
2817 nsIFrame *& aStartFrame,
2818 nsRect& aStartRect,
2819 nsIFrame *& aEndFrame,
2820 nsRect& aEndRect)
2821 {
2822 NS_ASSERTION(aPresContext, "Pointer is null!");
2823 NS_ASSERTION(aParentFrame, "Pointer is null!");
2825 aRect += aParentFrame->GetPosition();
2826 for (; !aChildFrames.AtEnd(); aChildFrames.Next()) {
2827 nsIFrame* child = aChildFrames.get();
2828 if (child->IsSelected() && child->IsVisibleForPainting()) {
2829 nsRect r = child->GetRect();
2830 if (aStartFrame == nullptr) {
2831 aStartFrame = child;
2832 aStartRect.SetRect(aRect.x + r.x, aRect.y + r.y, r.width, r.height);
2833 } else {
2834 aEndFrame = child;
2835 aEndRect.SetRect(aRect.x + r.x, aRect.y + r.y, r.width, r.height);
2836 }
2837 }
2838 FindSelectionBounds(aPresContext, aRC, child, aRect, aStartFrame, aStartRect, aEndFrame, aEndRect);
2839 child = child->GetNextSibling();
2840 }
2841 aRect -= aParentFrame->GetPosition();
2842 return NS_OK;
2843 }
2845 //-------------------------------------------------------
2846 // Find the Frame that is XMost
2847 nsresult
2848 nsPrintEngine::FindSelectionBounds(nsPresContext* aPresContext,
2849 nsRenderingContext& aRC,
2850 nsIFrame * aParentFrame,
2851 nsRect& aRect,
2852 nsIFrame *& aStartFrame,
2853 nsRect& aStartRect,
2854 nsIFrame *& aEndFrame,
2855 nsRect& aEndRect)
2856 {
2857 NS_ASSERTION(aPresContext, "Pointer is null!");
2858 NS_ASSERTION(aParentFrame, "Pointer is null!");
2860 // loop through named child lists
2861 nsIFrame::ChildListIterator lists(aParentFrame);
2862 for (; !lists.IsDone(); lists.Next()) {
2863 nsFrameList::Enumerator childFrames(lists.CurrentList());
2864 nsresult rv = FindSelectionBoundsWithList(aPresContext, aRC, childFrames, aParentFrame, aRect, aStartFrame, aStartRect, aEndFrame, aEndRect);
2865 NS_ENSURE_SUCCESS(rv, rv);
2866 }
2867 return NS_OK;
2868 }
2870 /** ---------------------------------------------------
2871 * This method finds the starting and ending page numbers
2872 * of the selection and also returns rect for each where
2873 * the x,y of the rect is relative to the very top of the
2874 * frame tree (absolutely positioned)
2875 */
2876 nsresult
2877 nsPrintEngine::GetPageRangeForSelection(nsIPresShell * aPresShell,
2878 nsPresContext* aPresContext,
2879 nsRenderingContext& aRC,
2880 nsISelection* aSelection,
2881 nsIPageSequenceFrame* aPageSeqFrame,
2882 nsIFrame** aStartFrame,
2883 int32_t& aStartPageNum,
2884 nsRect& aStartRect,
2885 nsIFrame** aEndFrame,
2886 int32_t& aEndPageNum,
2887 nsRect& aEndRect)
2888 {
2889 NS_ASSERTION(aPresShell, "Pointer is null!");
2890 NS_ASSERTION(aPresContext, "Pointer is null!");
2891 NS_ASSERTION(aSelection, "Pointer is null!");
2892 NS_ASSERTION(aPageSeqFrame, "Pointer is null!");
2893 NS_ASSERTION(aStartFrame, "Pointer is null!");
2894 NS_ASSERTION(aEndFrame, "Pointer is null!");
2896 nsIFrame * seqFrame = do_QueryFrame(aPageSeqFrame);
2897 if (!seqFrame) {
2898 return NS_ERROR_FAILURE;
2899 }
2901 nsIFrame * startFrame = nullptr;
2902 nsIFrame * endFrame = nullptr;
2904 // start out with the sequence frame and search the entire frame tree
2905 // capturing the starting and ending child frames of the selection
2906 // and their rects
2907 nsRect r = seqFrame->GetRect();
2908 FindSelectionBounds(aPresContext, aRC, seqFrame, r,
2909 startFrame, aStartRect, endFrame, aEndRect);
2911 #ifdef DEBUG_rodsX
2912 printf("Start Frame: %p\n", startFrame);
2913 printf("End Frame: %p\n", endFrame);
2914 #endif
2916 // initial the page numbers here
2917 // in case we don't find and frames
2918 aStartPageNum = -1;
2919 aEndPageNum = -1;
2921 nsIFrame * startPageFrame;
2922 nsIFrame * endPageFrame;
2924 // check to make sure we found a starting frame
2925 if (startFrame != nullptr) {
2926 // Now search up the tree to find what page the
2927 // start/ending selections frames are on
2928 //
2929 // Check to see if start should be same as end if
2930 // the end frame comes back null
2931 if (endFrame == nullptr) {
2932 // XXX the "GetPageFrame" step could be integrated into
2933 // the FindSelectionBounds step, but walking up to find
2934 // the parent of a child frame isn't expensive and it makes
2935 // FindSelectionBounds a little easier to understand
2936 startPageFrame = nsLayoutUtils::GetPageFrame(startFrame);
2937 endPageFrame = startPageFrame;
2938 aEndRect = aStartRect;
2939 } else {
2940 startPageFrame = nsLayoutUtils::GetPageFrame(startFrame);
2941 endPageFrame = nsLayoutUtils::GetPageFrame(endFrame);
2942 }
2943 } else {
2944 return NS_ERROR_FAILURE;
2945 }
2947 #ifdef DEBUG_rodsX
2948 printf("Start Page: %p\n", startPageFrame);
2949 printf("End Page: %p\n", endPageFrame);
2951 // dump all the pages and their pointers
2952 {
2953 int32_t pageNum = 1;
2954 nsIFrame* child = seqFrame->GetFirstPrincipalChild();
2955 while (child != nullptr) {
2956 printf("Page: %d - %p\n", pageNum, child);
2957 pageNum++;
2958 child = child->GetNextSibling();
2959 }
2960 }
2961 #endif
2963 // Now that we have the page frames
2964 // find out what the page numbers are for each frame
2965 int32_t pageNum = 1;
2966 nsIFrame* page = seqFrame->GetFirstPrincipalChild();
2967 while (page != nullptr) {
2968 if (page == startPageFrame) {
2969 aStartPageNum = pageNum;
2970 }
2971 if (page == endPageFrame) {
2972 aEndPageNum = pageNum;
2973 }
2974 pageNum++;
2975 page = page->GetNextSibling();
2976 }
2978 #ifdef DEBUG_rodsX
2979 printf("Start Page No: %d\n", aStartPageNum);
2980 printf("End Page No: %d\n", aEndPageNum);
2981 #endif
2983 *aStartFrame = startPageFrame;
2984 *aEndFrame = endPageFrame;
2986 return NS_OK;
2987 }
2989 //-----------------------------------------------------------------
2990 //-- Done: Printing Methods
2991 //-----------------------------------------------------------------
2994 //-----------------------------------------------------------------
2995 //-- Section: Misc Support Methods
2996 //-----------------------------------------------------------------
2998 //---------------------------------------------------------------------
2999 void nsPrintEngine::SetIsPrinting(bool aIsPrinting)
3000 {
3001 mIsDoingPrinting = aIsPrinting;
3002 // Calling SetIsPrinting while in print preview confuses the document viewer
3003 // This is safe because we prevent exiting print preview while printing
3004 if (!mIsDoingPrintPreview && mDocViewerPrint) {
3005 mDocViewerPrint->SetIsPrinting(aIsPrinting);
3006 }
3007 if (mPrt && aIsPrinting) {
3008 mPrt->mPreparingForPrint = true;
3009 }
3010 }
3012 //---------------------------------------------------------------------
3013 void nsPrintEngine::SetIsPrintPreview(bool aIsPrintPreview)
3014 {
3015 mIsDoingPrintPreview = aIsPrintPreview;
3017 if (mDocViewerPrint) {
3018 mDocViewerPrint->SetIsPrintPreview(aIsPrintPreview);
3019 }
3020 }
3022 //---------------------------------------------------------------------
3023 void
3024 nsPrintEngine::CleanupDocTitleArray(char16_t**& aArray, int32_t& aCount)
3025 {
3026 for (int32_t i = aCount - 1; i >= 0; i--) {
3027 nsMemory::Free(aArray[i]);
3028 }
3029 nsMemory::Free(aArray);
3030 aArray = nullptr;
3031 aCount = 0;
3032 }
3034 //---------------------------------------------------------------------
3035 // static
3036 bool nsPrintEngine::HasFramesetChild(nsIContent* aContent)
3037 {
3038 if (!aContent) {
3039 return false;
3040 }
3042 // do a breadth search across all siblings
3043 for (nsIContent* child = aContent->GetFirstChild();
3044 child;
3045 child = child->GetNextSibling()) {
3046 if (child->IsHTML(nsGkAtoms::frameset)) {
3047 return true;
3048 }
3049 }
3051 return false;
3052 }
3056 /** ---------------------------------------------------
3057 * Get the Focused Frame for a documentviewer
3058 */
3059 already_AddRefed<nsIDOMWindow>
3060 nsPrintEngine::FindFocusedDOMWindow()
3061 {
3062 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
3063 NS_ENSURE_TRUE(fm, nullptr);
3065 nsCOMPtr<nsPIDOMWindow> window(mDocument->GetWindow());
3066 NS_ENSURE_TRUE(window, nullptr);
3068 nsCOMPtr<nsPIDOMWindow> rootWindow = window->GetPrivateRoot();
3069 NS_ENSURE_TRUE(rootWindow, nullptr);
3071 nsCOMPtr<nsPIDOMWindow> focusedWindow;
3072 nsFocusManager::GetFocusedDescendant(rootWindow, true,
3073 getter_AddRefs(focusedWindow));
3074 NS_ENSURE_TRUE(focusedWindow, nullptr);
3076 if (IsWindowsInOurSubTree(focusedWindow)) {
3077 return focusedWindow.forget();
3078 }
3080 return nullptr;
3081 }
3083 //---------------------------------------------------------------------
3084 bool
3085 nsPrintEngine::IsWindowsInOurSubTree(nsPIDOMWindow * window)
3086 {
3087 bool found = false;
3089 // now check to make sure it is in "our" tree of docshells
3090 if (window) {
3091 nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
3093 if (docShell) {
3094 // get this DocViewer docshell
3095 nsCOMPtr<nsIDocShell> thisDVDocShell(do_QueryReferent(mContainer));
3096 while (!found) {
3097 if (docShell) {
3098 if (docShell == thisDVDocShell) {
3099 found = true;
3100 break;
3101 }
3102 } else {
3103 break; // at top of tree
3104 }
3105 nsCOMPtr<nsIDocShellTreeItem> docShellItemParent;
3106 docShell->GetSameTypeParent(getter_AddRefs(docShellItemParent));
3107 docShell = do_QueryInterface(docShellItemParent);
3108 } // while
3109 }
3110 } // scriptobj
3112 return found;
3113 }
3115 //-------------------------------------------------------
3116 bool
3117 nsPrintEngine::DonePrintingPages(nsPrintObject* aPO, nsresult aResult)
3118 {
3119 //NS_ASSERTION(aPO, "Pointer is null!");
3120 PR_PL(("****** In DV::DonePrintingPages PO: %p (%s)\n", aPO, aPO?gFrameTypesStr[aPO->mFrameType]:""));
3122 // If there is a pageSeqFrame, make sure there are no more printCanvas active
3123 // that might call |Notify| on the pagePrintTimer after things are cleaned up
3124 // and printing was marked as being done.
3125 if (mPageSeqFrame) {
3126 mPageSeqFrame->ResetPrintCanvasList();
3127 }
3129 if (aPO && !mPrt->mIsAborted) {
3130 aPO->mHasBeenPrinted = true;
3131 nsresult rv;
3132 bool didPrint = PrintDocContent(mPrt->mPrintObject, rv);
3133 if (NS_SUCCEEDED(rv) && didPrint) {
3134 PR_PL(("****** In DV::DonePrintingPages PO: %p (%s) didPrint:%s (Not Done Printing)\n", aPO, gFrameTypesStr[aPO->mFrameType], PRT_YESNO(didPrint)));
3135 return false;
3136 }
3137 }
3139 if (NS_SUCCEEDED(aResult)) {
3140 FirePrintCompletionEvent();
3141 }
3143 TurnScriptingOn(true);
3144 SetIsPrinting(false);
3146 // Release reference to mPagePrintTimer; the timer object destroys itself
3147 // after this returns true
3148 NS_IF_RELEASE(mPagePrintTimer);
3150 return true;
3151 }
3153 //-------------------------------------------------------
3154 // Recursively sets the PO items to be printed "As Is"
3155 // from the given item down into the tree
3156 void
3157 nsPrintEngine::SetPrintAsIs(nsPrintObject* aPO, bool aAsIs)
3158 {
3159 NS_ASSERTION(aPO, "Pointer is null!");
3161 aPO->mPrintAsIs = aAsIs;
3162 for (uint32_t i=0;i<aPO->mKids.Length();i++) {
3163 SetPrintAsIs(aPO->mKids[i], aAsIs);
3164 }
3165 }
3167 //-------------------------------------------------------
3168 // Given a DOMWindow it recursively finds the PO object that matches
3169 nsPrintObject*
3170 nsPrintEngine::FindPrintObjectByDOMWin(nsPrintObject* aPO,
3171 nsIDOMWindow* aDOMWin)
3172 {
3173 NS_ASSERTION(aPO, "Pointer is null!");
3175 // Often the CurFocused DOMWindow is passed in
3176 // andit is valid for it to be null, so short circut
3177 if (!aDOMWin) {
3178 return nullptr;
3179 }
3181 nsCOMPtr<nsIDOMDocument> domDoc;
3182 aDOMWin->GetDocument(getter_AddRefs(domDoc));
3183 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
3184 if (aPO->mDocument && aPO->mDocument->GetOriginalDocument() == doc) {
3185 return aPO;
3186 }
3188 int32_t cnt = aPO->mKids.Length();
3189 for (int32_t i = 0; i < cnt; ++i) {
3190 nsPrintObject* po = FindPrintObjectByDOMWin(aPO->mKids[i], aDOMWin);
3191 if (po) {
3192 return po;
3193 }
3194 }
3196 return nullptr;
3197 }
3199 //-------------------------------------------------------
3200 nsresult
3201 nsPrintEngine::EnablePOsForPrinting()
3202 {
3203 // NOTE: All POs have been "turned off" for printing
3204 // this is where we decided which POs get printed.
3205 mPrt->mSelectedPO = nullptr;
3207 if (mPrt->mPrintSettings == nullptr) {
3208 return NS_ERROR_FAILURE;
3209 }
3211 mPrt->mPrintFrameType = nsIPrintSettings::kNoFrames;
3212 mPrt->mPrintSettings->GetPrintFrameType(&mPrt->mPrintFrameType);
3214 int16_t printHowEnable = nsIPrintSettings::kFrameEnableNone;
3215 mPrt->mPrintSettings->GetHowToEnableFrameUI(&printHowEnable);
3217 int16_t printRangeType = nsIPrintSettings::kRangeAllPages;
3218 mPrt->mPrintSettings->GetPrintRange(&printRangeType);
3220 PR_PL(("\n"));
3221 PR_PL(("********* nsPrintEngine::EnablePOsForPrinting *********\n"));
3222 PR_PL(("PrintFrameType: %s \n", gPrintFrameTypeStr[mPrt->mPrintFrameType]));
3223 PR_PL(("HowToEnableFrameUI: %s \n", gFrameHowToEnableStr[printHowEnable]));
3224 PR_PL(("PrintRange: %s \n", gPrintRangeStr[printRangeType]));
3225 PR_PL(("----\n"));
3227 // ***** This is the ultimate override *****
3228 // if we are printing the selection (either an IFrame or selection range)
3229 // then set the mPrintFrameType as if it were the selected frame
3230 if (printRangeType == nsIPrintSettings::kRangeSelection) {
3231 mPrt->mPrintFrameType = nsIPrintSettings::kSelectedFrame;
3232 printHowEnable = nsIPrintSettings::kFrameEnableNone;
3233 }
3235 // This tells us that the "Frame" UI has turned off,
3236 // so therefore there are no FrameSets/Frames/IFrames to be printed
3237 //
3238 // This means there are not FrameSets,
3239 // but the document could contain an IFrame
3240 if (printHowEnable == nsIPrintSettings::kFrameEnableNone) {
3242 // Print all the pages or a sub range of pages
3243 if (printRangeType == nsIPrintSettings::kRangeAllPages ||
3244 printRangeType == nsIPrintSettings::kRangeSpecifiedPageRange) {
3245 SetPrintPO(mPrt->mPrintObject, true);
3247 // Set the children so they are PrinAsIs
3248 // In this case, the children are probably IFrames
3249 if (mPrt->mPrintObject->mKids.Length() > 0) {
3250 for (uint32_t i=0;i<mPrt->mPrintObject->mKids.Length();i++) {
3251 nsPrintObject* po = mPrt->mPrintObject->mKids[i];
3252 NS_ASSERTION(po, "nsPrintObject can't be null!");
3253 SetPrintAsIs(po);
3254 }
3256 // ***** Another override *****
3257 mPrt->mPrintFrameType = nsIPrintSettings::kFramesAsIs;
3258 }
3259 PR_PL(("PrintFrameType: %s \n", gPrintFrameTypeStr[mPrt->mPrintFrameType]));
3260 PR_PL(("HowToEnableFrameUI: %s \n", gFrameHowToEnableStr[printHowEnable]));
3261 PR_PL(("PrintRange: %s \n", gPrintRangeStr[printRangeType]));
3262 return NS_OK;
3263 }
3265 // This means we are either printed a selected IFrame or
3266 // we are printing the current selection
3267 if (printRangeType == nsIPrintSettings::kRangeSelection) {
3269 // If the currentFocusDOMWin can'r be null if something is selected
3270 if (mPrt->mCurrentFocusWin) {
3271 // Find the selected IFrame
3272 nsPrintObject * po = FindPrintObjectByDOMWin(mPrt->mPrintObject, mPrt->mCurrentFocusWin);
3273 if (po != nullptr) {
3274 mPrt->mSelectedPO = po;
3275 // Makes sure all of its children are be printed "AsIs"
3276 SetPrintAsIs(po);
3278 // Now, only enable this POs (the selected PO) and all of its children
3279 SetPrintPO(po, true);
3281 // check to see if we have a range selection,
3282 // as oppose to a insert selection
3283 // this means if the user just clicked on the IFrame then
3284 // there will not be a selection so we want the entire page to print
3285 //
3286 // XXX this is sort of a hack right here to make the page
3287 // not try to reposition itself when printing selection
3288 nsCOMPtr<nsIDOMWindow> domWin =
3289 do_QueryInterface(po->mDocument->GetOriginalDocument()->GetWindow());
3290 if (!IsThereARangeSelection(domWin)) {
3291 printRangeType = nsIPrintSettings::kRangeAllPages;
3292 mPrt->mPrintSettings->SetPrintRange(printRangeType);
3293 }
3294 PR_PL(("PrintFrameType: %s \n", gPrintFrameTypeStr[mPrt->mPrintFrameType]));
3295 PR_PL(("HowToEnableFrameUI: %s \n", gFrameHowToEnableStr[printHowEnable]));
3296 PR_PL(("PrintRange: %s \n", gPrintRangeStr[printRangeType]));
3297 return NS_OK;
3298 }
3299 } else {
3300 for (uint32_t i=0;i<mPrt->mPrintDocList.Length();i++) {
3301 nsPrintObject* po = mPrt->mPrintDocList.ElementAt(i);
3302 NS_ASSERTION(po, "nsPrintObject can't be null!");
3303 nsCOMPtr<nsIDOMWindow> domWin = do_GetInterface(po->mDocShell);
3304 if (IsThereARangeSelection(domWin)) {
3305 mPrt->mCurrentFocusWin = domWin;
3306 SetPrintPO(po, true);
3307 break;
3308 }
3309 }
3310 return NS_OK;
3311 }
3312 }
3313 }
3315 // check to see if there is a selection when a FrameSet is present
3316 if (printRangeType == nsIPrintSettings::kRangeSelection) {
3317 // If the currentFocusDOMWin can'r be null if something is selected
3318 if (mPrt->mCurrentFocusWin) {
3319 // Find the selected IFrame
3320 nsPrintObject * po = FindPrintObjectByDOMWin(mPrt->mPrintObject, mPrt->mCurrentFocusWin);
3321 if (po != nullptr) {
3322 mPrt->mSelectedPO = po;
3323 // Makes sure all of its children are be printed "AsIs"
3324 SetPrintAsIs(po);
3326 // Now, only enable this POs (the selected PO) and all of its children
3327 SetPrintPO(po, true);
3329 // check to see if we have a range selection,
3330 // as oppose to a insert selection
3331 // this means if the user just clicked on the IFrame then
3332 // there will not be a selection so we want the entire page to print
3333 //
3334 // XXX this is sort of a hack right here to make the page
3335 // not try to reposition itself when printing selection
3336 nsCOMPtr<nsIDOMWindow> domWin =
3337 do_QueryInterface(po->mDocument->GetOriginalDocument()->GetWindow());
3338 if (!IsThereARangeSelection(domWin)) {
3339 printRangeType = nsIPrintSettings::kRangeAllPages;
3340 mPrt->mPrintSettings->SetPrintRange(printRangeType);
3341 }
3342 PR_PL(("PrintFrameType: %s \n", gPrintFrameTypeStr[mPrt->mPrintFrameType]));
3343 PR_PL(("HowToEnableFrameUI: %s \n", gFrameHowToEnableStr[printHowEnable]));
3344 PR_PL(("PrintRange: %s \n", gPrintRangeStr[printRangeType]));
3345 return NS_OK;
3346 }
3347 }
3348 }
3350 // If we are printing "AsIs" then sets all the POs to be printed as is
3351 if (mPrt->mPrintFrameType == nsIPrintSettings::kFramesAsIs) {
3352 SetPrintAsIs(mPrt->mPrintObject);
3353 SetPrintPO(mPrt->mPrintObject, true);
3354 return NS_OK;
3355 }
3357 // If we are printing the selected Frame then
3358 // find that PO for that selected DOMWin and set it all of its
3359 // children to be printed
3360 if (mPrt->mPrintFrameType == nsIPrintSettings::kSelectedFrame) {
3362 if ((mPrt->mIsParentAFrameSet && mPrt->mCurrentFocusWin) || mPrt->mIsIFrameSelected) {
3363 nsPrintObject * po = FindPrintObjectByDOMWin(mPrt->mPrintObject, mPrt->mCurrentFocusWin);
3364 if (po != nullptr) {
3365 mPrt->mSelectedPO = po;
3366 // NOTE: Calling this sets the "po" and
3367 // we don't want to do this for documents that have no children,
3368 // because then the "DoEndPage" gets called and it shouldn't
3369 if (po->mKids.Length() > 0) {
3370 // Makes sure that itself, and all of its children are printed "AsIs"
3371 SetPrintAsIs(po);
3372 }
3374 // Now, only enable this POs (the selected PO) and all of its children
3375 SetPrintPO(po, true);
3376 }
3377 }
3378 return NS_OK;
3379 }
3381 // If we are print each subdoc separately,
3382 // then don't print any of the FraneSet Docs
3383 if (mPrt->mPrintFrameType == nsIPrintSettings::kEachFrameSep) {
3384 SetPrintPO(mPrt->mPrintObject, true);
3385 int32_t cnt = mPrt->mPrintDocList.Length();
3386 for (int32_t i=0;i<cnt;i++) {
3387 nsPrintObject* po = mPrt->mPrintDocList.ElementAt(i);
3388 NS_ASSERTION(po, "nsPrintObject can't be null!");
3389 if (po->mFrameType == eFrameSet) {
3390 po->mDontPrint = true;
3391 }
3392 }
3393 }
3395 return NS_OK;
3396 }
3398 //-------------------------------------------------------
3399 // Return the nsPrintObject with that is XMost (The widest frameset frame) AND
3400 // contains the XMost (widest) layout frame
3401 nsPrintObject*
3402 nsPrintEngine::FindSmallestSTF()
3403 {
3404 float smallestRatio = 1.0f;
3405 nsPrintObject* smallestPO = nullptr;
3407 for (uint32_t i=0;i<mPrt->mPrintDocList.Length();i++) {
3408 nsPrintObject* po = mPrt->mPrintDocList.ElementAt(i);
3409 NS_ASSERTION(po, "nsPrintObject can't be null!");
3410 if (po->mFrameType != eFrameSet && po->mFrameType != eIFrame) {
3411 if (po->mShrinkRatio < smallestRatio) {
3412 smallestRatio = po->mShrinkRatio;
3413 smallestPO = po;
3414 }
3415 }
3416 }
3418 #ifdef EXTENDED_DEBUG_PRINTING
3419 if (smallestPO) printf("*PO: %p Type: %d %10.3f\n", smallestPO, smallestPO->mFrameType, smallestPO->mShrinkRatio);
3420 #endif
3421 return smallestPO;
3422 }
3424 //-------------------------------------------------------
3425 void
3426 nsPrintEngine::TurnScriptingOn(bool aDoTurnOn)
3427 {
3428 if (mIsDoingPrinting && aDoTurnOn && mDocViewerPrint &&
3429 mDocViewerPrint->GetIsPrintPreview()) {
3430 // We don't want to turn scripting on if print preview is shown still after
3431 // printing.
3432 return;
3433 }
3435 nsPrintData* prt = mPrt;
3436 #ifdef NS_PRINT_PREVIEW
3437 if (!prt) {
3438 prt = mPrtPreview;
3439 }
3440 #endif
3441 if (!prt) {
3442 return;
3443 }
3445 NS_ASSERTION(mDocument, "We MUST have a document.");
3446 // First, get the script global object from the document...
3448 for (uint32_t i=0;i<prt->mPrintDocList.Length();i++) {
3449 nsPrintObject* po = prt->mPrintDocList.ElementAt(i);
3450 NS_ASSERTION(po, "nsPrintObject can't be null!");
3452 nsIDocument* doc = po->mDocument;
3453 if (!doc) {
3454 continue;
3455 }
3457 if (nsCOMPtr<nsPIDOMWindow> window = doc->GetInnerWindow()) {
3458 nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(window);
3459 NS_WARN_IF_FALSE(go && go->GetGlobalJSObject(), "Can't get global");
3460 nsresult propThere = NS_PROPTABLE_PROP_NOT_THERE;
3461 doc->GetProperty(nsGkAtoms::scriptEnabledBeforePrintOrPreview,
3462 &propThere);
3463 if (aDoTurnOn) {
3464 if (propThere != NS_PROPTABLE_PROP_NOT_THERE) {
3465 doc->DeleteProperty(nsGkAtoms::scriptEnabledBeforePrintOrPreview);
3466 if (go && go->GetGlobalJSObject()) {
3467 xpc::Scriptability::Get(go->GetGlobalJSObject()).Unblock();
3468 }
3469 window->ResumeTimeouts(false);
3470 }
3471 } else {
3472 // Have to be careful, because people call us over and over again with
3473 // aDoTurnOn == false. So don't set the property if it's already
3474 // set, since in that case we'd set it to the wrong value.
3475 if (propThere == NS_PROPTABLE_PROP_NOT_THERE) {
3476 // Stash the current value of IsScriptEnabled on the document, so
3477 // that layout code running in print preview doesn't get confused.
3478 doc->SetProperty(nsGkAtoms::scriptEnabledBeforePrintOrPreview,
3479 NS_INT32_TO_PTR(doc->IsScriptEnabled()));
3480 if (go && go->GetGlobalJSObject()) {
3481 xpc::Scriptability::Get(go->GetGlobalJSObject()).Block();
3482 }
3483 window->SuspendTimeouts(1, false);
3484 }
3485 }
3486 }
3487 }
3488 }
3490 //-----------------------------------------------------------------
3491 //-- Done: Misc Support Methods
3492 //-----------------------------------------------------------------
3495 //-----------------------------------------------------------------
3496 //-- Section: Finishing up or Cleaning up
3497 //-----------------------------------------------------------------
3499 //-----------------------------------------------------------------
3500 void
3501 nsPrintEngine::CloseProgressDialog(nsIWebProgressListener* aWebProgressListener)
3502 {
3503 if (aWebProgressListener) {
3504 aWebProgressListener->OnStateChange(nullptr, nullptr, nsIWebProgressListener::STATE_STOP|nsIWebProgressListener::STATE_IS_DOCUMENT, NS_OK);
3505 }
3506 }
3508 //-----------------------------------------------------------------
3509 nsresult
3510 nsPrintEngine::FinishPrintPreview()
3511 {
3512 nsresult rv = NS_OK;
3514 #ifdef NS_PRINT_PREVIEW
3516 if (!mPrt) {
3517 /* we're already finished with print preview */
3518 return rv;
3519 }
3521 rv = DocumentReadyForPrinting();
3523 SetIsCreatingPrintPreview(false);
3525 /* cleaup on failure + notify user */
3526 if (NS_FAILED(rv)) {
3527 /* cleanup done, let's fire-up an error dialog to notify the user
3528 * what went wrong...
3529 */
3530 mPrt->OnEndPrinting();
3531 TurnScriptingOn(true);
3533 return rv;
3534 }
3536 // At this point we are done preparing everything
3537 // before it is to be created
3540 if (mIsDoingPrintPreview && mOldPrtPreview) {
3541 delete mOldPrtPreview;
3542 mOldPrtPreview = nullptr;
3543 }
3546 mPrt->OnEndPrinting();
3548 // PrintPreview was built using the mPrt (code reuse)
3549 // then we assign it over
3550 mPrtPreview = mPrt;
3551 mPrt = nullptr;
3553 #endif // NS_PRINT_PREVIEW
3555 return NS_OK;
3556 }
3558 //-----------------------------------------------------------------
3559 //-- Done: Finishing up or Cleaning up
3560 //-----------------------------------------------------------------
3563 /*=============== Timer Related Code ======================*/
3564 nsresult
3565 nsPrintEngine::StartPagePrintTimer(nsPrintObject* aPO)
3566 {
3567 if (!mPagePrintTimer) {
3568 // Get the delay time in between the printing of each page
3569 // this gives the user more time to press cancel
3570 int32_t printPageDelay = 50;
3571 mPrt->mPrintSettings->GetPrintPageDelay(&printPageDelay);
3573 nsRefPtr<nsPagePrintTimer> timer =
3574 new nsPagePrintTimer(this, mDocViewerPrint, printPageDelay);
3575 timer.forget(&mPagePrintTimer);
3576 }
3578 return mPagePrintTimer->Start(aPO);
3579 }
3581 /*=============== nsIObserver Interface ======================*/
3582 NS_IMETHODIMP
3583 nsPrintEngine::Observe(nsISupports *aSubject, const char *aTopic, const char16_t *aData)
3584 {
3585 nsresult rv = NS_ERROR_FAILURE;
3587 rv = InitPrintDocConstruction(true);
3588 if (!mIsDoingPrinting && mPrtPreview) {
3589 mPrtPreview->OnEndPrinting();
3590 }
3592 return rv;
3594 }
3596 //---------------------------------------------------------------
3597 //-- PLEvent Notification
3598 //---------------------------------------------------------------
3599 class nsPrintCompletionEvent : public nsRunnable {
3600 public:
3601 nsPrintCompletionEvent(nsIDocumentViewerPrint *docViewerPrint)
3602 : mDocViewerPrint(docViewerPrint) {
3603 NS_ASSERTION(mDocViewerPrint, "mDocViewerPrint is null.");
3604 }
3606 NS_IMETHOD Run() MOZ_OVERRIDE {
3607 if (mDocViewerPrint)
3608 mDocViewerPrint->OnDonePrinting();
3609 return NS_OK;
3610 }
3612 private:
3613 nsCOMPtr<nsIDocumentViewerPrint> mDocViewerPrint;
3614 };
3616 //-----------------------------------------------------------
3617 void
3618 nsPrintEngine::FirePrintCompletionEvent()
3619 {
3620 nsCOMPtr<nsIRunnable> event = new nsPrintCompletionEvent(mDocViewerPrint);
3621 if (NS_FAILED(NS_DispatchToCurrentThread(event)))
3622 NS_WARNING("failed to dispatch print completion event");
3623 }
3625 //---------------------------------------------------------------
3626 //---------------------------------------------------------------
3627 //-- Debug helper routines
3628 //---------------------------------------------------------------
3629 //---------------------------------------------------------------
3630 #if defined(XP_WIN) && defined(EXTENDED_DEBUG_PRINTING)
3631 #include "windows.h"
3632 #include "process.h"
3633 #include "direct.h"
3635 #define MY_FINDFIRST(a,b) FindFirstFile(a,b)
3636 #define MY_FINDNEXT(a,b) FindNextFile(a,b)
3637 #define ISDIR(a) (a.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
3638 #define MY_FINDCLOSE(a) FindClose(a)
3639 #define MY_FILENAME(a) a.cFileName
3640 #define MY_FILESIZE(a) (a.nFileSizeHigh * MAXDWORD) + a.nFileSizeLow
3642 int RemoveFilesInDir(const char * aDir)
3643 {
3644 WIN32_FIND_DATA data_ptr;
3645 HANDLE find_handle;
3647 char path[MAX_PATH];
3649 strcpy(path, aDir);
3651 // Append slash to the end of the directory names if not there
3652 if (path[strlen(path)-1] != '\\')
3653 strcat(path, "\\");
3655 char findPath[MAX_PATH];
3656 strcpy(findPath, path);
3657 strcat(findPath, "*.*");
3659 find_handle = MY_FINDFIRST(findPath, &data_ptr);
3661 if (find_handle != INVALID_HANDLE_VALUE) {
3662 do {
3663 if (ISDIR(data_ptr)
3664 && (stricmp(MY_FILENAME(data_ptr),"."))
3665 && (stricmp(MY_FILENAME(data_ptr),".."))) {
3666 // skip
3667 }
3668 else if (!ISDIR(data_ptr)) {
3669 if (!strncmp(MY_FILENAME(data_ptr), "print_dump", 10)) {
3670 char fileName[MAX_PATH];
3671 strcpy(fileName, aDir);
3672 strcat(fileName, "\\");
3673 strcat(fileName, MY_FILENAME(data_ptr));
3674 printf("Removing %s\n", fileName);
3675 remove(fileName);
3676 }
3677 }
3678 } while(MY_FINDNEXT(find_handle,&data_ptr));
3679 MY_FINDCLOSE(find_handle);
3680 }
3681 return TRUE;
3682 }
3683 #endif
3685 #ifdef EXTENDED_DEBUG_PRINTING
3687 /** ---------------------------------------------------
3688 * Dumps Frames for Printing
3689 */
3690 static void RootFrameList(nsPresContext* aPresContext, FILE* out, int32_t aIndent)
3691 {
3692 if (!aPresContext || !out)
3693 return;
3695 nsIPresShell *shell = aPresContext->GetPresShell();
3696 if (shell) {
3697 nsIFrame* frame = shell->FrameManager()->GetRootFrame();
3698 if (frame) {
3699 frame->List(aPresContext, out, aIndent);
3700 }
3701 }
3702 }
3704 /** ---------------------------------------------------
3705 * Dumps Frames for Printing
3706 */
3707 static void DumpFrames(FILE* out,
3708 nsPresContext* aPresContext,
3709 nsRenderingContext * aRendContext,
3710 nsIFrame * aFrame,
3711 int32_t aLevel)
3712 {
3713 NS_ASSERTION(out, "Pointer is null!");
3714 NS_ASSERTION(aPresContext, "Pointer is null!");
3715 NS_ASSERTION(aRendContext, "Pointer is null!");
3716 NS_ASSERTION(aFrame, "Pointer is null!");
3718 nsIFrame* child = aFrame->GetFirstPrincipalChild();
3719 while (child != nullptr) {
3720 for (int32_t i=0;i<aLevel;i++) {
3721 fprintf(out, " ");
3722 }
3723 nsAutoString tmp;
3724 child->GetFrameName(tmp);
3725 fputs(NS_LossyConvertUTF16toASCII(tmp).get(), out);
3726 bool isSelected;
3727 if (NS_SUCCEEDED(child->IsVisibleForPainting(aPresContext, *aRendContext, true, &isSelected))) {
3728 fprintf(out, " %p %s", child, isSelected?"VIS":"UVS");
3729 nsRect rect = child->GetRect();
3730 fprintf(out, "[%d,%d,%d,%d] ", rect.x, rect.y, rect.width, rect.height);
3731 fprintf(out, "v: %p ", (void*)child->GetView());
3732 fprintf(out, "\n");
3733 DumpFrames(out, aPresContext, aRendContext, child, aLevel+1);
3734 child = child->GetNextSibling();
3735 }
3736 }
3737 }
3740 /** ---------------------------------------------------
3741 * Dumps the Views from the DocShell
3742 */
3743 static void
3744 DumpViews(nsIDocShell* aDocShell, FILE* out)
3745 {
3746 NS_ASSERTION(aDocShell, "Pointer is null!");
3747 NS_ASSERTION(out, "Pointer is null!");
3749 if (nullptr != aDocShell) {
3750 fprintf(out, "docshell=%p \n", aDocShell);
3751 nsIPresShell* shell = nsPrintEngine::GetPresShellFor(aDocShell);
3752 if (shell) {
3753 nsViewManager* vm = shell->GetViewManager();
3754 if (vm) {
3755 nsView* root = vm->GetRootView();
3756 if (root) {
3757 root->List(out);
3758 }
3759 }
3760 }
3761 else {
3762 fputs("null pres shell\n", out);
3763 }
3765 // dump the views of the sub documents
3766 int32_t i, n;
3767 aDocShell->GetChildCount(&n);
3768 for (i = 0; i < n; i++) {
3769 nsCOMPtr<nsIDocShellTreeItem> child;
3770 aDocShell->GetChildAt(i, getter_AddRefs(child));
3771 nsCOMPtr<nsIDocShell> childAsShell(do_QueryInterface(child));
3772 if (childAsShell) {
3773 DumpViews(childAsShell, out);
3774 }
3775 }
3776 }
3777 }
3779 /** ---------------------------------------------------
3780 * Dumps the Views and Frames
3781 */
3782 void DumpLayoutData(char* aTitleStr,
3783 char* aURLStr,
3784 nsPresContext* aPresContext,
3785 nsDeviceContext * aDC,
3786 nsIFrame * aRootFrame,
3787 nsIDocShekk * aDocShell,
3788 FILE* aFD = nullptr)
3789 {
3790 if (!kPrintingLogMod || kPrintingLogMod->level != DUMP_LAYOUT_LEVEL) return;
3792 if (aPresContext == nullptr || aDC == nullptr) {
3793 return;
3794 }
3796 #ifdef NS_PRINT_PREVIEW
3797 if (aPresContext->Type() == nsPresContext::eContext_PrintPreview) {
3798 return;
3799 }
3800 #endif
3802 NS_ASSERTION(aRootFrame, "Pointer is null!");
3803 NS_ASSERTION(aDocShell, "Pointer is null!");
3805 // Dump all the frames and view to a a file
3806 char filename[256];
3807 sprintf(filename, "print_dump_layout_%d.txt", gDumpLOFileNameCnt++);
3808 FILE * fd = aFD?aFD:fopen(filename, "w");
3809 if (fd) {
3810 fprintf(fd, "Title: %s\n", aTitleStr?aTitleStr:"");
3811 fprintf(fd, "URL: %s\n", aURLStr?aURLStr:"");
3812 fprintf(fd, "--------------- Frames ----------------\n");
3813 fprintf(fd, "--------------- Frames ----------------\n");
3814 nsRefPtr<nsRenderingContext> renderingContext =
3815 aDC->CreateRenderingContext();
3816 RootFrameList(aPresContext, fd, 0);
3817 //DumpFrames(fd, aPresContext, renderingContext, aRootFrame, 0);
3818 fprintf(fd, "---------------------------------------\n\n");
3819 fprintf(fd, "--------------- Views From Root Frame----------------\n");
3820 nsView* v = aRootFrame->GetView();
3821 if (v) {
3822 v->List(fd);
3823 } else {
3824 printf("View is null!\n");
3825 }
3826 if (aDocShell) {
3827 fprintf(fd, "--------------- All Views ----------------\n");
3828 DumpViews(aDocShell, fd);
3829 fprintf(fd, "---------------------------------------\n\n");
3830 }
3831 if (aFD == nullptr) {
3832 fclose(fd);
3833 }
3834 }
3835 }
3837 //-------------------------------------------------------------
3838 static void DumpPrintObjectsList(nsTArray<nsPrintObject*> * aDocList)
3839 {
3840 if (!kPrintingLogMod || kPrintingLogMod->level != DUMP_LAYOUT_LEVEL) return;
3842 NS_ASSERTION(aDocList, "Pointer is null!");
3844 const char types[][3] = {"DC", "FR", "IF", "FS"};
3845 PR_PL(("Doc List\n***************************************************\n"));
3846 PR_PL(("T P A H PO DocShell Seq Page Root Page# Rect\n"));
3847 int32_t cnt = aDocList->Length();
3848 for (int32_t i=0;i<cnt;i++) {
3849 nsPrintObject* po = aDocList->ElementAt(i);
3850 NS_ASSERTION(po, "nsPrintObject can't be null!");
3851 nsIFrame* rootFrame = nullptr;
3852 if (po->mPresShell) {
3853 rootFrame = po->mPresShell->FrameManager()->GetRootFrame();
3854 while (rootFrame != nullptr) {
3855 nsIPageSequenceFrame * sqf = do_QueryFrame(rootFrame);
3856 if (sqf) {
3857 break;
3858 }
3859 rootFrame = rootFrame->GetFirstPrincipalChild();
3860 }
3861 }
3863 PR_PL(("%s %d %d %d %p %p %p %p %p %d %d,%d,%d,%d\n", types[po->mFrameType],
3864 po->IsPrintable(), po->mPrintAsIs, po->mHasBeenPrinted, po, po->mDocShell.get(), po->mSeqFrame,
3865 po->mPageFrame, rootFrame, po->mPageNum, po->mRect.x, po->mRect.y, po->mRect.width, po->mRect.height));
3866 }
3867 }
3869 //-------------------------------------------------------------
3870 static void DumpPrintObjectsTree(nsPrintObject * aPO, int aLevel, FILE* aFD)
3871 {
3872 if (!kPrintingLogMod || kPrintingLogMod->level != DUMP_LAYOUT_LEVEL) return;
3874 NS_ASSERTION(aPO, "Pointer is null!");
3876 FILE * fd = aFD?aFD:stdout;
3877 const char types[][3] = {"DC", "FR", "IF", "FS"};
3878 if (aLevel == 0) {
3879 fprintf(fd, "DocTree\n***************************************************\n");
3880 fprintf(fd, "T PO DocShell Seq Page Page# Rect\n");
3881 }
3882 int32_t cnt = aPO->mKids.Length();
3883 for (int32_t i=0;i<cnt;i++) {
3884 nsPrintObject* po = aPO->mKids.ElementAt(i);
3885 NS_ASSERTION(po, "nsPrintObject can't be null!");
3886 for (int32_t k=0;k<aLevel;k++) fprintf(fd, " ");
3887 fprintf(fd, "%s %p %p %p %p %d %d,%d,%d,%d\n", types[po->mFrameType], po, po->mDocShell.get(), po->mSeqFrame,
3888 po->mPageFrame, po->mPageNum, po->mRect.x, po->mRect.y, po->mRect.width, po->mRect.height);
3889 }
3890 }
3892 //-------------------------------------------------------------
3893 static void GetDocTitleAndURL(nsPrintObject* aPO, nsACString& aDocStr, nsACString& aURLStr)
3894 {
3895 nsAutoString docTitleStr;
3896 nsAutoString docURLStr;
3897 nsPrintEngine::GetDisplayTitleAndURL(aPO,
3898 docTitleStr, docURLStr,
3899 nsPrintEngine::eDocTitleDefURLDoc);
3900 aDocStr = NS_ConvertUTF16toUTF8(docTitleStr);
3901 aURLStr = NS_ConvertUTF16toUTF8(docURLStr);
3902 }
3904 //-------------------------------------------------------------
3905 static void DumpPrintObjectsTreeLayout(nsPrintObject * aPO,
3906 nsDeviceContext * aDC,
3907 int aLevel, FILE * aFD)
3908 {
3909 if (!kPrintingLogMod || kPrintingLogMod->level != DUMP_LAYOUT_LEVEL) return;
3911 NS_ASSERTION(aPO, "Pointer is null!");
3912 NS_ASSERTION(aDC, "Pointer is null!");
3914 const char types[][3] = {"DC", "FR", "IF", "FS"};
3915 FILE * fd = nullptr;
3916 if (aLevel == 0) {
3917 fd = fopen("tree_layout.txt", "w");
3918 fprintf(fd, "DocTree\n***************************************************\n");
3919 fprintf(fd, "***************************************************\n");
3920 fprintf(fd, "T PO DocShell Seq Page Page# Rect\n");
3921 } else {
3922 fd = aFD;
3923 }
3924 if (fd) {
3925 nsIFrame* rootFrame = nullptr;
3926 if (aPO->mPresShell) {
3927 rootFrame = aPO->mPresShell->FrameManager()->GetRootFrame();
3928 }
3929 for (int32_t k=0;k<aLevel;k++) fprintf(fd, " ");
3930 fprintf(fd, "%s %p %p %p %p %d %d,%d,%d,%d\n", types[aPO->mFrameType], aPO, aPO->mDocShell.get(), aPO->mSeqFrame,
3931 aPO->mPageFrame, aPO->mPageNum, aPO->mRect.x, aPO->mRect.y, aPO->mRect.width, aPO->mRect.height);
3932 if (aPO->IsPrintable()) {
3933 nsAutoCString docStr;
3934 nsAutoCString urlStr;
3935 GetDocTitleAndURL(aPO, docStr, urlStr);
3936 DumpLayoutData(docStr.get(), urlStr.get(), aPO->mPresContext, aDC, rootFrame, aPO->mDocShell, fd);
3937 }
3938 fprintf(fd, "<***************************************************>\n");
3940 int32_t cnt = aPO->mKids.Length();
3941 for (int32_t i=0;i<cnt;i++) {
3942 nsPrintObject* po = aPO->mKids.ElementAt(i);
3943 NS_ASSERTION(po, "nsPrintObject can't be null!");
3944 DumpPrintObjectsTreeLayout(po, aDC, aLevel+1, fd);
3945 }
3946 }
3947 if (aLevel == 0 && fd) {
3948 fclose(fd);
3949 }
3950 }
3952 //-------------------------------------------------------------
3953 static void DumpPrintObjectsListStart(const char * aStr, nsTArray<nsPrintObject*> * aDocList)
3954 {
3955 if (!kPrintingLogMod || kPrintingLogMod->level != DUMP_LAYOUT_LEVEL) return;
3957 NS_ASSERTION(aStr, "Pointer is null!");
3958 NS_ASSERTION(aDocList, "Pointer is null!");
3960 PR_PL(("%s\n", aStr));
3961 DumpPrintObjectsList(aDocList);
3962 }
3964 #define DUMP_DOC_LIST(_title) DumpPrintObjectsListStart((_title), mPrt->mPrintDocList);
3965 #define DUMP_DOC_TREE DumpPrintObjectsTree(mPrt->mPrintObject);
3966 #define DUMP_DOC_TREELAYOUT DumpPrintObjectsTreeLayout(mPrt->mPrintObject, mPrt->mPrintDC);
3968 #else
3969 #define DUMP_DOC_LIST(_title)
3970 #define DUMP_DOC_TREE
3971 #define DUMP_DOC_TREELAYOUT
3972 #endif
3974 //---------------------------------------------------------------
3975 //---------------------------------------------------------------
3976 //-- End of debug helper routines
3977 //---------------------------------------------------------------