layout/printing/nsPrintEngine.cpp

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

     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;
  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());
  1016   return rv;
  1017 #endif
  1020 //----------------------------------------------------------------------
  1021 // Set up to use the "pluggable" Print Progress Dialog
  1022 void
  1023 nsPrintEngine::ShowPrintProgress(bool aIsForPrinting, bool& aDoNotify)
  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");
  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);
  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;
  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);
  1085 //---------------------------------------------------------------------
  1086 bool
  1087 nsPrintEngine::IsThereARangeSelection(nsIDOMWindow* aDOMWin)
  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();
  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;
  1109   int32_t rangeCount = selection->GetRangeCount();
  1110   if (!rangeCount) {
  1111     return false;
  1114   if (rangeCount > 1) {
  1115     return true;
  1118   // check to make sure it isn't an insertion selection
  1119   return selection->GetRangeAt(0) && !selection->IsCollapsed();
  1122 //---------------------------------------------------------------------
  1123 bool
  1124 nsPrintEngine::IsParentAFrameSet(nsIDocShell * aParent)
  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);
  1152   return isFrameSet;
  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)
  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);
  1196 //---------------------------------------------------------------------
  1197 void
  1198 nsPrintEngine::GetDocumentTitleAndURL(nsIDocument* aDoc,
  1199                                       nsAString&   aTitle,
  1200                                       nsAString&   aURLStr)
  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);
  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)
  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.");
  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]);
  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)
  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);
  1301   if (hasChildFrames && aPO->mFrameType == eFrame) {
  1302     aPO->mFrameType = eFrameSet;
  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)
  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;
  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;
  1367   // walk children content
  1368   for (nsIContent* child = aContent->GetFirstChild();
  1369        child;
  1370        child = child->GetNextSibling()) {
  1371     MapContentForPO(aPO, child);
  1375 //---------------------------------------------------------------------
  1376 bool
  1377 nsPrintEngine::IsThereAnIFrameSelected(nsIDocShell* aDocShell,
  1378                                        nsIDOMWindow* aDOMWin,
  1379                                        bool& aIsParentFrameSet)
  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
  1403   return iFrameIsSelected;
  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)
  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);
  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)
  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;
  1453     if (docURLStrPS) {
  1454       aURLStr = docURLStrPS;
  1457     nsMemory::Free(docTitleStrPS);
  1458     nsMemory::Free(docURLStrPS);
  1461   nsAutoString docTitle;
  1462   nsAutoString docUrl;
  1463   GetDocumentTitleAndURL(aPO->mDocument, docTitle, docUrl);
  1465   if (aURLStr.IsEmpty() && !docUrl.IsEmpty()) {
  1466     aURLStr = docUrl;
  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;
  1484 //---------------------------------------------------------------------
  1485 nsresult nsPrintEngine::DocumentReadyForPrinting()
  1487   if (mPrt->mPrintFrameType == nsIPrintSettings::kEachFrameSep) {
  1488     CheckForChildFrameSets(mPrt->mPrintObject);
  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);
  1500   return rv;
  1503 /** ---------------------------------------------------
  1504  *  Cleans up when an error occurred
  1505  */
  1506 nsresult nsPrintEngine::CleanupOnFailure(nsresult aResult, bool aIsPrinting)
  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);
  1516   if (aIsPrinting) {
  1517     SetIsPrinting(false);
  1518   } else {
  1519     SetIsPrintPreview(false);
  1520     SetIsCreatingPrintPreview(false);
  1523   /* cleanup done, let's fire-up an error dialog to notify the user
  1524    * what went wrong... 
  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);
  1533   FirePrintCompletionEvent();
  1535   return aResult;
  1539 //---------------------------------------------------------------------
  1540 void
  1541 nsPrintEngine::ShowPrintErrorDialog(nsresult aPrintError, bool aIsPrinting)
  1543   nsAutoCString stringName;
  1544   nsXPIDLString msg, title;
  1545   nsresult rv = NS_OK;
  1547   switch(aPrintError)
  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
  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);
  1581   if (aIsPrinting || NS_FAILED(rv)) {
  1582     rv = nsContentUtils::GetLocalizedString(
  1583              nsContentUtils::ePRINTING_PROPERTIES, stringName.get(), msg);
  1585   if (NS_FAILED(rv)) {
  1586     return;
  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;
  1598   nsCOMPtr<nsIWindowWatcher> wwatch =
  1599     do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
  1600   if (NS_FAILED(rv)) {
  1601     return;
  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;
  1615   dialog->Alert(title.get(), msg.get());
  1618 //-----------------------------------------------------------------
  1619 //-- Section: Reflow Methods
  1620 //-----------------------------------------------------------------
  1622 nsresult
  1623 nsPrintEngine::ReconstructAndReflow(bool doSetPixelScale)
  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;
  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;
  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; 
  1669     po->mPresShell->FlushPendingNotifications(Flush_Layout);
  1671     nsresult rv = UpdateSelectionAndShrinkPrintObject(po, documentIsTopLevel);
  1672     NS_ENSURE_SUCCESS(rv, rv);
  1674   return NS_OK;
  1677 //-------------------------------------------------------
  1678 nsresult
  1679 nsPrintEngine::SetupToPrintContent()
  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);
  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;
  1706     } else {
  1707       // Single document so use the Shrink as calculated for the PO
  1708       mPrt->mShrinkRatio = mPrt->mPrintObject->mShrinkRatio;
  1711     if (mPrt->mShrinkRatio < 0.998f) {
  1712       rv = ReconstructAndReflow(true);
  1713       didReconstruction = true;
  1714       NS_ENSURE_SUCCESS(rv, rv);
  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;
  1726     } else {
  1727       // Single document so use the Shrink as calculated for the PO
  1728       calcRatio = mPrt->mPrintObject->mShrinkRatio;
  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
  1736   // If the frames got reconstructed and reflowed the number of pages might
  1737   // has changed.
  1738   if (didReconstruction) {
  1739     FirePrintPreviewUpdateEvent();
  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();    
  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);
  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;
  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);
  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);
  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
  1816   return rv;
  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)
  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;
  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);
  1849   return NS_OK;
  1852 void
  1853 nsPrintEngine::FirePrintPreviewUpdateEvent()
  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();
  1865 nsresult
  1866 nsPrintEngine::InitPrintDocConstruction(bool aHandleError)
  1868   nsresult rv;
  1869   rv = ReflowDocList(mPrt->mPrintObject, DoSetPixelScale());
  1870   NS_ENSURE_SUCCESS(rv, rv);
  1872   FirePrintPreviewUpdateEvent();
  1874   if (mLoadCounter == 0) {
  1875     AfterNetworkPrint(aHandleError);
  1877   return rv;
  1880 nsresult
  1881 nsPrintEngine::AfterNetworkPrint(bool aHandleError)
  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();
  1895   /* cleaup on failure + notify user */
  1896   if (aHandleError && NS_FAILED(rv)) {
  1897     CleanupOnFailure(rv, !mIsDoingPrinting);
  1900   return rv;
  1903 ////////////////////////////////////////////////////////////////////////////////
  1904 // nsIWebProgressListener
  1906 NS_IMETHODIMP
  1907 nsPrintEngine::OnStateChange(nsIWebProgress* aWebProgress,
  1908                              nsIRequest* aRequest,
  1909                              uint32_t aStateFlags,
  1910                              nsresult aStatus)
  1912   nsAutoCString name;
  1913   aRequest->GetName(name);
  1914   if (name.Equals("about:document-onload-blocker")) {
  1915     return NS_OK;
  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);
  1931   return NS_OK;
  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)
  1944   NS_NOTREACHED("notification excluded in AddProgressListener(...)");
  1945   return NS_OK;
  1948 NS_IMETHODIMP
  1949 nsPrintEngine::OnLocationChange(nsIWebProgress* aWebProgress,
  1950                                 nsIRequest* aRequest,
  1951                                 nsIURI* aLocation,
  1952                                 uint32_t aFlags)
  1954   NS_NOTREACHED("notification excluded in AddProgressListener(...)");
  1955   return NS_OK;
  1958 NS_IMETHODIMP
  1959 nsPrintEngine::OnStatusChange(nsIWebProgress *aWebProgress,
  1960                               nsIRequest *aRequest,
  1961                               nsresult aStatus,
  1962                               const char16_t *aMessage)
  1964   NS_NOTREACHED("notification excluded in AddProgressListener(...)");
  1965   return NS_OK;
  1968 NS_IMETHODIMP
  1969 nsPrintEngine::OnSecurityChange(nsIWebProgress *aWebProgress,
  1970                                   nsIRequest *aRequest,
  1971                                   uint32_t aState)
  1973   NS_NOTREACHED("notification excluded in AddProgressListener(...)");
  1974   return NS_OK;
  1977 //-------------------------------------------------------
  1979 void
  1980 nsPrintEngine::UpdateZoomRatio(nsPrintObject* aPO, bool aSetPixelScale)
  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
  1991     aPO->mZoomRatio = ratio;
  1992   } else if (!mPrt->mShrinkToFit) {
  1993     double scaling;
  1994     mPrt->mPrintSettings->GetScaling(&scaling);
  1995     aPO->mZoomRatio = float(scaling);
  1999 nsresult
  2000 nsPrintEngine::UpdateSelectionAndShrinkPrintObject(nsPrintObject* aPO,
  2001                                                    bool aDocumentIsTopLevel)
  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);
  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();
  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));
  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);
  2047   return NS_OK;
  2050 bool
  2051 nsPrintEngine::DoSetPixelScale()
  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;
  2065   return doSetPixelScale;
  2068 nsView*
  2069 nsPrintEngine::GetParentViewForRoot()
  2071   if (mIsCreatingPrintPreview) {
  2072     nsCOMPtr<nsIContentViewer> cv = do_QueryInterface(mDocViewerPrint);
  2073     if (cv) {
  2074       return cv->FindContainerView();
  2077   return nullptr;
  2080 nsresult
  2081 nsPrintEngine::SetRootView(
  2082     nsPrintObject* aPO, 
  2083     bool& doReturn, 
  2084     bool& documentIsTopLevel, 
  2085     nsSize& adjSize
  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;
  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;
  2121   } else {
  2122     nscoord pageWidth, pageHeight;
  2123     mPrt->mPrintDC->GetDeviceSurfaceDimensions(pageWidth, pageHeight);
  2124     adjSize = nsSize(pageWidth, pageHeight);
  2125     documentIsTopLevel = true;
  2126     parentView = GetParentViewForRoot();
  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);
  2142   if (mIsCreatingPrintPreview && documentIsTopLevel) {
  2143     aPO->mPresContext->SetPaginatedScrolling(canCreateScrollbars);
  2146   // Setup hierarchical relationship in view manager
  2147   aPO->mViewManager->SetRootView(rootView);
  2149   return NS_OK;
  2152 // Reflow a nsPrintObject
  2153 nsresult
  2154 nsPrintEngine::ReflowPrintObject(nsPrintObject * aPO)
  2156   NS_ENSURE_STATE(aPO);
  2158   if (!aPO->IsPrintable()) {
  2159     return NS_OK;
  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;
  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; 
  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);
  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");
  2280         if (docShell) {
  2281           fprintf(fd, "--------------- All Views ----------------\n");
  2282           DumpViews(docShell, fd);
  2283           fprintf(fd, "---------------------------------------\n\n");
  2285         fclose(fd);
  2288 #endif
  2290   return NS_OK;
  2293 //-------------------------------------------------------
  2294 // Figure out how many documents and how many total pages we are printing
  2295 void
  2296 nsPrintEngine::CalcNumPrintablePages(int32_t& aNumPages)
  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();
  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)
  2331   NS_ASSERTION(aPO, "Pointer is null!");
  2332   aStatus = NS_OK;
  2334   if (!aPO->mHasBeenPrinted && aPO->IsPrintable()) {
  2335     aStatus = DoPrint(aPO);
  2336     return true;
  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;
  2350   return false;
  2353 static already_AddRefed<nsIDOMNode>
  2354 GetEqualNodeInCloneTree(nsIDOMNode* aNode, nsIDocument* aDoc)
  2356   nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
  2357   // Selections in anonymous subtrees aren't supported.
  2358   if (content && content->IsInAnonymousSubtree()) {
  2359     return nullptr;
  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;
  2373     int32_t index = parent->IndexOf(current);
  2374     NS_ENSURE_TRUE(index >= 0, nullptr);
  2375     indexArray.AppendElement(index);
  2376     current = parent;
  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);
  2385   nsCOMPtr<nsIDOMNode> result = do_QueryInterface(current);
  2386   return result.forget();
  2389 static void
  2390 CloneRangeToSelection(nsRange* aRange, nsIDocument* aDoc,
  2391                       Selection* aSelection)
  2393   if (aRange->Collapsed()) {
  2394     return;
  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);
  2420 static nsresult CloneSelection(nsIDocument* aOrigDoc, nsIDocument* aDoc)
  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);
  2436   return NS_OK;
  2439 //-------------------------------------------------------
  2440 nsresult
  2441 nsPrintEngine::DoPrint(nsPrintObject * aPO)
  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);
  2459     int16_t printRangeType = nsIPrintSettings::kRangeAllPages;
  2460     nsresult rv;
  2461     if (mPrt->mPrintSettings != nullptr) {
  2462       mPrt->mPrintSettings->GetPrintRange(&printRangeType);
  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);
  2490 #endif
  2492       if (!mPrt->mPrintSettings) {
  2493         // not sure what to do here!
  2494         SetIsPrinting(false);
  2495         return NS_ERROR_FAILURE;
  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;
  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;
  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);
  2575       nsIFrame * seqFrame = do_QueryFrame(pageSequence);
  2576       if (!seqFrame) {
  2577         SetIsPrinting(false);
  2578         return NS_ERROR_FAILURE;
  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);
  2590   return NS_OK;
  2593 //---------------------------------------------------------------------
  2594 void
  2595 nsPrintEngine::SetDocAndURLIntoProgress(nsPrintObject* aPO,
  2596                                         nsIPrintProgressParams* aParams)
  2598   NS_ASSERTION(aPO, "Must have valid nsPrintObject");
  2599   NS_ASSERTION(aParams, "Must have valid nsIPrintProgressParams");
  2601   if (!aPO || !aPO->mDocShell || !aParams) {
  2602     return;
  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());
  2618 //---------------------------------------------------------------------
  2619 void
  2620 nsPrintEngine::EllipseLongString(nsAString& aStr, const uint32_t aLen, bool aDoFront)
  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("...");
  2636 static bool
  2637 DocHasPrintCallbackCanvas(nsIDocument* aDoc, void* aData)
  2639   if (!aDoc) {
  2640     return true;
  2642   Element* root = aDoc->GetRootElement();
  2643   if (!root) {
  2644     return true;
  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;
  2659   return true;
  2662 static bool
  2663 DocHasPrintCallbackCanvas(nsIDocument* aDoc)
  2665   bool result = false;
  2666   aDoc->EnumerateSubDocuments(&DocHasPrintCallbackCanvas, static_cast<void*>(&result));
  2667   return result;
  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()
  2677   if (!mDocument) {
  2678     return false;
  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);
  2687 //-------------------------------------------------------
  2688 bool
  2689 nsPrintEngine::PrePrintPage()
  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.
  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;
  2719     done = true;
  2721   return done;
  2724 bool
  2725 nsPrintEngine::PrintPage(nsPrintObject*    aPO,
  2726                          bool&           aInRange)
  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
  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;
  2763     if (toPage > numPages) {
  2764       toPage = numPages;
  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;
  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;
  2800     return true;
  2803   mPageSeqFrame->DoPageEnd();
  2805   return donePrinting;
  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)
  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);
  2838     FindSelectionBounds(aPresContext, aRC, child, aRect, aStartFrame, aStartRect, aEndFrame, aEndRect);
  2839     child = child->GetNextSibling();
  2841   aRect -= aParentFrame->GetPosition();
  2842   return NS_OK;
  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)
  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);
  2867   return NS_OK;
  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)
  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;
  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);
  2943   } else {
  2944     return NS_ERROR_FAILURE;
  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
  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();
  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;
  2971     if (page == endPageFrame) {
  2972       aEndPageNum = pageNum;
  2974     pageNum++;
  2975     page = page->GetNextSibling();
  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;
  2989 //-----------------------------------------------------------------
  2990 //-- Done: Printing Methods
  2991 //-----------------------------------------------------------------
  2994 //-----------------------------------------------------------------
  2995 //-- Section: Misc Support Methods
  2996 //-----------------------------------------------------------------
  2998 //---------------------------------------------------------------------
  2999 void nsPrintEngine::SetIsPrinting(bool aIsPrinting)
  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);
  3007   if (mPrt && aIsPrinting) {
  3008     mPrt->mPreparingForPrint = true;
  3012 //---------------------------------------------------------------------
  3013 void nsPrintEngine::SetIsPrintPreview(bool aIsPrintPreview) 
  3015   mIsDoingPrintPreview = aIsPrintPreview; 
  3017   if (mDocViewerPrint) {
  3018     mDocViewerPrint->SetIsPrintPreview(aIsPrintPreview);
  3022 //---------------------------------------------------------------------
  3023 void
  3024 nsPrintEngine::CleanupDocTitleArray(char16_t**& aArray, int32_t& aCount)
  3026   for (int32_t i = aCount - 1; i >= 0; i--) {
  3027     nsMemory::Free(aArray[i]);
  3029   nsMemory::Free(aArray);
  3030   aArray = nullptr;
  3031   aCount = 0;
  3034 //---------------------------------------------------------------------
  3035 // static
  3036 bool nsPrintEngine::HasFramesetChild(nsIContent* aContent)
  3038   if (!aContent) {
  3039     return false;
  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;
  3051   return false;
  3056 /** ---------------------------------------------------
  3057  *  Get the Focused Frame for a documentviewer
  3058  */
  3059 already_AddRefed<nsIDOMWindow>
  3060 nsPrintEngine::FindFocusedDOMWindow()
  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();
  3080   return nullptr;
  3083 //---------------------------------------------------------------------
  3084 bool
  3085 nsPrintEngine::IsWindowsInOurSubTree(nsPIDOMWindow * window)
  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;
  3102         } else {
  3103           break; // at top of tree
  3105         nsCOMPtr<nsIDocShellTreeItem> docShellItemParent;
  3106         docShell->GetSameTypeParent(getter_AddRefs(docShellItemParent));
  3107         docShell = do_QueryInterface(docShellItemParent);
  3108       } // while
  3110   } // scriptobj
  3112   return found;
  3115 //-------------------------------------------------------
  3116 bool
  3117 nsPrintEngine::DonePrintingPages(nsPrintObject* aPO, nsresult aResult)
  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();
  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;
  3139   if (NS_SUCCEEDED(aResult)) {
  3140     FirePrintCompletionEvent();
  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;
  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)
  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);
  3167 //-------------------------------------------------------
  3168 // Given a DOMWindow it recursively finds the PO object that matches
  3169 nsPrintObject*
  3170 nsPrintEngine::FindPrintObjectByDOMWin(nsPrintObject* aPO,
  3171                                        nsIDOMWindow* aDOMWin)
  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;
  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;
  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;
  3196   return nullptr;
  3199 //-------------------------------------------------------
  3200 nsresult
  3201 nsPrintEngine::EnablePOsForPrinting()
  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;
  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;
  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);
  3256         // ***** Another override *****
  3257         mPrt->mPrintFrameType = nsIPrintSettings::kFramesAsIs;
  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;
  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);
  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;
  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;
  3310         return NS_OK;
  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);
  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;
  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;
  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);
  3374         // Now, only enable this POs (the selected PO) and all of its children
  3375         SetPrintPO(po, true);
  3378     return NS_OK;
  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;
  3395   return NS_OK;
  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()
  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;
  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;
  3424 //-------------------------------------------------------
  3425 void
  3426 nsPrintEngine::TurnScriptingOn(bool aDoTurnOn)
  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;
  3435   nsPrintData* prt = mPrt;
  3436 #ifdef NS_PRINT_PREVIEW
  3437   if (!prt) {
  3438     prt = mPrtPreview;
  3440 #endif
  3441   if (!prt) {
  3442     return;
  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;
  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();
  3469           window->ResumeTimeouts(false);
  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();
  3483           window->SuspendTimeouts(1, false);
  3490 //-----------------------------------------------------------------
  3491 //-- Done: Misc Support Methods
  3492 //-----------------------------------------------------------------
  3495 //-----------------------------------------------------------------
  3496 //-- Section: Finishing up or Cleaning up
  3497 //-----------------------------------------------------------------
  3499 //-----------------------------------------------------------------
  3500 void
  3501 nsPrintEngine::CloseProgressDialog(nsIWebProgressListener* aWebProgressListener)
  3503   if (aWebProgressListener) {
  3504     aWebProgressListener->OnStateChange(nullptr, nullptr, nsIWebProgressListener::STATE_STOP|nsIWebProgressListener::STATE_IS_DOCUMENT, NS_OK);
  3508 //-----------------------------------------------------------------
  3509 nsresult
  3510 nsPrintEngine::FinishPrintPreview()
  3512   nsresult rv = NS_OK;
  3514 #ifdef NS_PRINT_PREVIEW
  3516   if (!mPrt) {
  3517     /* we're already finished with print preview */
  3518     return rv;
  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;
  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;
  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;
  3558 //-----------------------------------------------------------------
  3559 //-- Done: Finishing up or Cleaning up
  3560 //-----------------------------------------------------------------
  3563 /*=============== Timer Related Code ======================*/
  3564 nsresult
  3565 nsPrintEngine::StartPagePrintTimer(nsPrintObject* aPO)
  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);
  3578   return mPagePrintTimer->Start(aPO);
  3581 /*=============== nsIObserver Interface ======================*/
  3582 NS_IMETHODIMP 
  3583 nsPrintEngine::Observe(nsISupports *aSubject, const char *aTopic, const char16_t *aData)
  3585   nsresult rv = NS_ERROR_FAILURE;
  3587   rv = InitPrintDocConstruction(true);
  3588   if (!mIsDoingPrinting && mPrtPreview) {
  3589       mPrtPreview->OnEndPrinting();
  3592   return rv;
  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.");
  3606   NS_IMETHOD Run() MOZ_OVERRIDE {
  3607     if (mDocViewerPrint)
  3608       mDocViewerPrint->OnDonePrinting();
  3609     return NS_OK;
  3612 private:
  3613   nsCOMPtr<nsIDocumentViewerPrint> mDocViewerPrint;
  3614 };
  3616 //-----------------------------------------------------------
  3617 void
  3618 nsPrintEngine::FirePrintCompletionEvent()
  3620   nsCOMPtr<nsIRunnable> event = new nsPrintCompletionEvent(mDocViewerPrint);
  3621   if (NS_FAILED(NS_DispatchToCurrentThread(event)))
  3622     NS_WARNING("failed to dispatch print completion event");
  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)
  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
  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);
  3678 		} while(MY_FINDNEXT(find_handle,&data_ptr));
  3679 		MY_FINDCLOSE(find_handle);
  3681 	return TRUE;
  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)
  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);
  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)
  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, "  ");
  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();
  3740 /** ---------------------------------------------------
  3741  *  Dumps the Views from the DocShell
  3742  */
  3743 static void
  3744 DumpViews(nsIDocShell* aDocShell, FILE* out)
  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);
  3761     else {
  3762       fputs("null pres shell\n", out);
  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);
  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)
  3790   if (!kPrintingLogMod || kPrintingLogMod->level != DUMP_LAYOUT_LEVEL) return;
  3792   if (aPresContext == nullptr || aDC == nullptr) {
  3793     return;
  3796 #ifdef NS_PRINT_PREVIEW
  3797   if (aPresContext->Type() == nsPresContext::eContext_PrintPreview) {
  3798     return;
  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");
  3826     if (aDocShell) {
  3827       fprintf(fd, "--------------- All Views ----------------\n");
  3828       DumpViews(aDocShell, fd);
  3829       fprintf(fd, "---------------------------------------\n\n");
  3831     if (aFD == nullptr) {
  3832       fclose(fd);
  3837 //-------------------------------------------------------------
  3838 static void DumpPrintObjectsList(nsTArray<nsPrintObject*> * aDocList)
  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;
  3859         rootFrame = rootFrame->GetFirstPrincipalChild();
  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));
  3869 //-------------------------------------------------------------
  3870 static void DumpPrintObjectsTree(nsPrintObject * aPO, int aLevel, FILE* aFD)
  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");
  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);
  3892 //-------------------------------------------------------------
  3893 static void GetDocTitleAndURL(nsPrintObject* aPO, nsACString& aDocStr, nsACString& aURLStr)
  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);
  3904 //-------------------------------------------------------------
  3905 static void DumpPrintObjectsTreeLayout(nsPrintObject * aPO,
  3906                                        nsDeviceContext * aDC,
  3907                                        int aLevel, FILE * aFD)
  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;
  3924   if (fd) {
  3925     nsIFrame* rootFrame = nullptr;
  3926     if (aPO->mPresShell) {
  3927       rootFrame = aPO->mPresShell->FrameManager()->GetRootFrame();
  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);
  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);
  3947   if (aLevel == 0 && fd) {
  3948     fclose(fd);
  3952 //-------------------------------------------------------------
  3953 static void DumpPrintObjectsListStart(const char * aStr, nsTArray<nsPrintObject*> * aDocList)
  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);
  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 //---------------------------------------------------------------

mercurial