forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			3060 lines
		
	
	
	
		
			106 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			3060 lines
		
	
	
	
		
			106 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| #include "nsPrintJob.h"
 | |
| 
 | |
| #include "nsDebug.h"
 | |
| #include "nsDocShell.h"
 | |
| #include "nsReadableUtils.h"
 | |
| #include "nsCRT.h"
 | |
| #include "nsQueryObject.h"
 | |
| 
 | |
| #include "mozilla/AsyncEventDispatcher.h"
 | |
| #include "mozilla/ResultExtensions.h"
 | |
| #include "mozilla/ComputedStyleInlines.h"
 | |
| #include "mozilla/dom/BrowsingContext.h"
 | |
| #include "mozilla/dom/PBrowser.h"
 | |
| #include "mozilla/dom/Selection.h"
 | |
| #include "mozilla/dom/CustomEvent.h"
 | |
| #include "mozilla/dom/HTMLCanvasElement.h"
 | |
| #include "mozilla/dom/ScriptSettings.h"
 | |
| #include "mozilla/StaticPrefs_print.h"
 | |
| #include "mozilla/Telemetry.h"
 | |
| #include "nsIBrowserChild.h"
 | |
| #include "nsIOService.h"
 | |
| #include "nsIScriptGlobalObject.h"
 | |
| #include "nsIStringBundle.h"
 | |
| #include "nsPIDOMWindow.h"
 | |
| #include "nsPrintData.h"
 | |
| #include "nsPrintObject.h"
 | |
| #include "nsIDocShell.h"
 | |
| #include "nsIURI.h"
 | |
| #include "nsITextToSubURI.h"
 | |
| #include "nsError.h"
 | |
| 
 | |
| #include "nsView.h"
 | |
| #include <algorithm>
 | |
| 
 | |
| // Print Options
 | |
| #include "nsIPrintSettings.h"
 | |
| #include "nsIPrintSettingsService.h"
 | |
| #include "nsIPrintSession.h"
 | |
| #include "nsGfxCIID.h"
 | |
| #include "nsGkAtoms.h"
 | |
| #include "nsXPCOM.h"
 | |
| 
 | |
| static const char sPrintSettingsServiceContractID[] =
 | |
|     "@mozilla.org/gfx/printsettings-service;1";
 | |
| 
 | |
| #include "nsThreadUtils.h"
 | |
| 
 | |
| // Printing
 | |
| #include "nsIWebBrowserPrint.h"
 | |
| 
 | |
| // Print Preview
 | |
| 
 | |
| // Print Progress
 | |
| #include "nsIObserver.h"
 | |
| 
 | |
| // Print error dialog
 | |
| 
 | |
| // Printing Prompts
 | |
| #include "nsIPrintingPromptService.h"
 | |
| static const char kPrintingPromptService[] =
 | |
|     "@mozilla.org/embedcomp/printingprompt-service;1";
 | |
| 
 | |
| // Printing Timer
 | |
| #include "nsPagePrintTimer.h"
 | |
| 
 | |
| // FrameSet
 | |
| #include "mozilla/dom/Document.h"
 | |
| #include "mozilla/dom/DocumentInlines.h"
 | |
| 
 | |
| // Focus
 | |
| 
 | |
| // Misc
 | |
| #include "gfxContext.h"
 | |
| #include "mozilla/gfx/DrawEventRecorder.h"
 | |
| #include "mozilla/layout/RemotePrintJobChild.h"
 | |
| #include "nsISupportsUtils.h"
 | |
| #include "nsIScriptContext.h"
 | |
| #include "nsIDocumentObserver.h"
 | |
| #include "nsContentCID.h"
 | |
| #include "nsLayoutCID.h"
 | |
| #include "nsContentUtils.h"
 | |
| #include "nsLayoutUtils.h"
 | |
| #include "mozilla/Preferences.h"
 | |
| #include "mozilla/PresShell.h"
 | |
| #include "Text.h"
 | |
| 
 | |
| #include "nsWidgetsCID.h"
 | |
| #include "nsIDeviceContextSpec.h"
 | |
| #include "nsDeviceContextSpecProxy.h"
 | |
| #include "nsViewManager.h"
 | |
| 
 | |
| #include "nsPageSequenceFrame.h"
 | |
| #include "nsIInterfaceRequestor.h"
 | |
| #include "nsIInterfaceRequestorUtils.h"
 | |
| #include "nsIWebBrowserChrome.h"
 | |
| #include "nsFrameManager.h"
 | |
| #include "mozilla/ReflowInput.h"
 | |
| #include "nsIContentViewer.h"
 | |
| #include "nsIDocumentViewerPrint.h"
 | |
| 
 | |
| #include "nsFocusManager.h"
 | |
| #include "nsRange.h"
 | |
| #include "mozilla/Components.h"
 | |
| #include "mozilla/dom/Element.h"
 | |
| #include "mozilla/dom/HTMLFrameElement.h"
 | |
| #include "nsContentList.h"
 | |
| #include "xpcpublic.h"
 | |
| #include "nsVariant.h"
 | |
| #include "mozilla/ServoStyleSet.h"
 | |
| 
 | |
| using namespace mozilla;
 | |
| using namespace mozilla::dom;
 | |
| 
 | |
| //-----------------------------------------------------
 | |
| // PR LOGGING
 | |
| #include "mozilla/Logging.h"
 | |
| 
 | |
| #ifdef DEBUG
 | |
| // PR_LOGGING is force to always be on (even in release builds)
 | |
| // but we only want some of it on,
 | |
| //#define EXTENDED_DEBUG_PRINTING
 | |
| #endif
 | |
| 
 | |
| // this log level turns on the dumping of each document's layout info
 | |
| #define DUMP_LAYOUT_LEVEL (static_cast<mozilla::LogLevel>(9))
 | |
| 
 | |
| #ifndef PR_PL
 | |
| static mozilla::LazyLogModule gPrintingLog("printing");
 | |
| 
 | |
| #  define PR_PL(_p1) MOZ_LOG(gPrintingLog, mozilla::LogLevel::Debug, _p1);
 | |
| #endif
 | |
| 
 | |
| #ifdef EXTENDED_DEBUG_PRINTING
 | |
| static uint32_t gDumpFileNameCnt = 0;
 | |
| static uint32_t gDumpLOFileNameCnt = 0;
 | |
| #endif
 | |
| 
 | |
| #define PRT_YESNO(_p) ((_p) ? "YES" : "NO")
 | |
| static const char* gFrameTypesStr[] = {"eDoc", "eFrame", "eIFrame",
 | |
|                                        "eFrameSet"};
 | |
| 
 | |
| // This processes the selection on aOrigDoc and creates an inverted selection on
 | |
| // aDoc, which it then deletes. If the start or end of the inverted selection
 | |
| // ranges occur in text nodes then an ellipsis is added.
 | |
| static nsresult DeleteNonSelectedNodes(Document& aDoc);
 | |
| 
 | |
| #ifdef EXTENDED_DEBUG_PRINTING
 | |
| // Forward Declarations
 | |
| static void DumpPrintObjectsListStart(const char* aStr,
 | |
|                                       const nsTArray<nsPrintObject*>& aDocList);
 | |
| static void DumpPrintObjectsTree(nsPrintObject* aPO, int aLevel = 0,
 | |
|                                  FILE* aFD = nullptr);
 | |
| static void DumpPrintObjectsTreeLayout(const UniquePtr<nsPrintObject>& aPO,
 | |
|                                        nsDeviceContext* aDC, int aLevel = 0,
 | |
|                                        FILE* aFD = nullptr);
 | |
| 
 | |
| #  define DUMP_DOC_LIST(_title) \
 | |
|     DumpPrintObjectsListStart((_title), mPrt->mPrintDocList);
 | |
| #  define DUMP_DOC_TREE DumpPrintObjectsTree(mPrt->mPrintObject.get());
 | |
| #  define DUMP_DOC_TREELAYOUT \
 | |
|     DumpPrintObjectsTreeLayout(mPrt->mPrintObject, mPrt->mPrintDC);
 | |
| #else
 | |
| #  define DUMP_DOC_LIST(_title)
 | |
| #  define DUMP_DOC_TREE
 | |
| #  define DUMP_DOC_TREELAYOUT
 | |
| #endif
 | |
| 
 | |
| // -------------------------------------------------------
 | |
| // Helpers
 | |
| // -------------------------------------------------------
 | |
| 
 | |
| static bool HasFramesetChild(nsIContent* aContent) {
 | |
|   if (!aContent) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   // do a breadth search across all siblings
 | |
|   for (nsIContent* child = aContent->GetFirstChild(); child;
 | |
|        child = child->GetNextSibling()) {
 | |
|     if (child->IsHTMLElement(nsGkAtoms::frameset)) {
 | |
|       return true;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| static bool IsParentAFrameSet(nsIDocShell* aParent) {
 | |
|   // See if the incoming doc is the root document
 | |
|   if (!aParent) return false;
 | |
| 
 | |
|   // When it is the top level document we need to check
 | |
|   // to see if it contains a frameset. If it does, then
 | |
|   // we only want to print the doc's children and not the document itself
 | |
|   // For anything else we always print all the children and the document
 | |
|   // for example, if the doc contains an IFRAME we eant to print the child
 | |
|   // document (the IFRAME) and then the rest of the document.
 | |
|   //
 | |
|   // XXX we really need to search the frame tree, and not the content
 | |
|   // but there is no way to distinguish between IFRAMEs and FRAMEs
 | |
|   // with the GetFrameType call.
 | |
|   // Bug 53459 has been files so we can eventually distinguish
 | |
|   // between IFRAME frames and FRAME frames
 | |
|   bool isFrameSet = false;
 | |
|   // only check to see if there is a frameset if there is
 | |
|   // NO parent doc for this doc. meaning this parent is the root doc
 | |
|   nsCOMPtr<Document> doc = aParent->GetDocument();
 | |
|   if (doc) {
 | |
|     nsIContent* rootElement = doc->GetRootElement();
 | |
|     if (rootElement) {
 | |
|       isFrameSet = HasFramesetChild(rootElement);
 | |
|     }
 | |
|   }
 | |
|   return isFrameSet;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Build a tree of nsPrintObjects under aPO. It also appends a (depth first)
 | |
|  * flat list of all the nsPrintObjects created to aPrintData->mPrintDocList. If
 | |
|  * one of the nsPrintObject's document is the focused document, then the print
 | |
|  * object is set as aPrintData->mSelectionRoot.
 | |
|  * @param aParentPO The parent nsPrintObject to populate, must not be null.
 | |
|  * @param aFocusedDoc Document from the window that had focus when print was
 | |
|  *                    initiated.
 | |
|  * @param aPrintData nsPrintData for the current print, must not be null.
 | |
|  */
 | |
| static void BuildNestedPrintObjects(const UniquePtr<nsPrintObject>& aParentPO,
 | |
|                                     const RefPtr<Document>& aFocusedDoc,
 | |
|                                     RefPtr<nsPrintData>& aPrintData) {
 | |
|   MOZ_ASSERT(aParentPO);
 | |
|   MOZ_ASSERT(aPrintData);
 | |
| 
 | |
|   // If aParentPO is for an iframe and its original document is focusedDoc then
 | |
|   // always set as the selection root.
 | |
|   if (aParentPO->mFrameType == eIFrame &&
 | |
|       aParentPO->mDocument->GetOriginalDocument() == aFocusedDoc) {
 | |
|     aPrintData->mSelectionRoot = aParentPO.get();
 | |
|   } else if (!aPrintData->mSelectionRoot && aParentPO->HasSelection()) {
 | |
|     // If there is no focused iframe but there is a selection in one or more
 | |
|     // frames then we want to set the root nsPrintObject as the focus root so
 | |
|     // that later EnablePrintingSelectionOnly can search for and enable all
 | |
|     // nsPrintObjects containing selections.
 | |
|     aPrintData->mSelectionRoot = aPrintData->mPrintObject.get();
 | |
|   }
 | |
| 
 | |
|   for (auto& bc : aParentPO->mDocShell->GetBrowsingContext()->Children()) {
 | |
|     nsCOMPtr<nsIDocShell> docShell = bc->GetDocShell();
 | |
|     if (!docShell) {
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     RefPtr<Document> doc = docShell->GetDocument();
 | |
|     MOZ_DIAGNOSTIC_ASSERT(doc);
 | |
|     // We might find non-static documents here if the fission remoteness change
 | |
|     // hasn't happened / finished yet. In that case, just skip them, the same
 | |
|     // way we do for remote frames above.
 | |
|     MOZ_DIAGNOSTIC_ASSERT(doc->IsStaticDocument() || doc->IsInitialDocument());
 | |
|     if (!doc || !doc->IsStaticDocument()) {
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     auto childPO = MakeUnique<nsPrintObject>();
 | |
|     nsresult rv = childPO->InitAsNestedObject(docShell, doc, aParentPO.get());
 | |
|     if (NS_FAILED(rv)) {
 | |
|       MOZ_ASSERT_UNREACHABLE("Init failed?");
 | |
|     }
 | |
| 
 | |
|     aPrintData->mPrintDocList.AppendElement(childPO.get());
 | |
|     BuildNestedPrintObjects(childPO, aFocusedDoc, aPrintData);
 | |
|     aParentPO->mKids.AppendElement(std::move(childPO));
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * On platforms that support it, sets the printer name stored in the
 | |
|  * nsIPrintSettings to the last-used printer if a printer name is not already
 | |
|  * set.
 | |
|  * XXXjwatt: Why is this necessary? Can't the code that reads the printer
 | |
|  * name later "just" use the last-used printer if a name isn't specified? Then
 | |
|  * we wouldn't have this inconsistency between platforms and processes.
 | |
|  */
 | |
| static nsresult EnsureSettingsHasPrinterNameSet(
 | |
|     nsIPrintSettings* aPrintSettings) {
 | |
| #if defined(XP_MACOSX) || defined(ANDROID)
 | |
|   // Mac doesn't support retrieving a printer list.
 | |
|   return NS_OK;
 | |
| #else
 | |
|   NS_ENSURE_ARG_POINTER(aPrintSettings);
 | |
| 
 | |
|   // See if aPrintSettings already has a printer
 | |
|   nsString printerName;
 | |
|   nsresult rv = aPrintSettings->GetPrinterName(printerName);
 | |
|   if (NS_SUCCEEDED(rv) && !printerName.IsEmpty()) {
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   // aPrintSettings doesn't have a printer set.
 | |
|   // Try to fetch the name of the last-used printer.
 | |
|   nsCOMPtr<nsIPrintSettingsService> printSettingsService =
 | |
|       do_GetService(sPrintSettingsServiceContractID, &rv);
 | |
|   NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|   rv = printSettingsService->GetLastUsedPrinterName(printerName);
 | |
|   if (NS_SUCCEEDED(rv) && !printerName.IsEmpty()) {
 | |
|     rv = aPrintSettings->SetPrinterName(printerName);
 | |
|   }
 | |
|   return rv;
 | |
| #endif
 | |
| }
 | |
| 
 | |
| static nsresult GetDefaultPrintSettings(nsIPrintSettings** aSettings) {
 | |
|   *aSettings = nullptr;
 | |
| 
 | |
|   nsresult rv = NS_ERROR_FAILURE;
 | |
|   nsCOMPtr<nsIPrintSettingsService> printSettingsService =
 | |
|       do_GetService(sPrintSettingsServiceContractID, &rv);
 | |
|   NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|   return printSettingsService->GetDefaultPrintSettingsForPrinting(aSettings);
 | |
| }
 | |
| 
 | |
| //-------------------------------------------------------
 | |
| 
 | |
| NS_IMPL_ISUPPORTS(nsPrintJob, nsIWebProgressListener, nsISupportsWeakReference,
 | |
|                   nsIObserver)
 | |
| 
 | |
| //-------------------------------------------------------
 | |
| nsPrintJob::nsPrintJob() = default;
 | |
| 
 | |
| nsPrintJob::~nsPrintJob() {
 | |
|   Destroy();  // for insurance
 | |
|   DisconnectPagePrintTimer();
 | |
| }
 | |
| 
 | |
| bool nsPrintJob::CheckBeforeDestroy() const {
 | |
|   return mPrt && mPrt->mPreparingForPrint;
 | |
| }
 | |
| 
 | |
| PresShell* nsPrintJob::GetPrintPreviewPresShell() {
 | |
|   return mPrtPreview->mPrintObject->mPresShell;
 | |
| }
 | |
| 
 | |
| //-------------------------------------------------------
 | |
| void nsPrintJob::Destroy() {
 | |
|   if (mIsDestroying) {
 | |
|     return;
 | |
|   }
 | |
|   mIsDestroying = true;
 | |
| 
 | |
|   mPrt = nullptr;
 | |
| 
 | |
| #ifdef NS_PRINT_PREVIEW
 | |
|   mPrtPreview = nullptr;
 | |
| #endif
 | |
|   mDocViewerPrint = nullptr;
 | |
| }
 | |
| 
 | |
| //-------------------------------------------------------
 | |
| void nsPrintJob::DestroyPrintingData() { mPrt = nullptr; }
 | |
| 
 | |
| //---------------------------------------------------------------------------------
 | |
| //-- Section: Methods needed by the DocViewer
 | |
| //---------------------------------------------------------------------------------
 | |
| 
 | |
| //--------------------------------------------------------
 | |
| nsresult nsPrintJob::Initialize(nsIDocumentViewerPrint* aDocViewerPrint,
 | |
|                                 nsIDocShell* aDocShell, Document* aOriginalDoc,
 | |
|                                 float aScreenDPI) {
 | |
|   NS_ENSURE_ARG_POINTER(aDocViewerPrint);
 | |
|   NS_ENSURE_ARG_POINTER(aDocShell);
 | |
|   NS_ENSURE_ARG_POINTER(aOriginalDoc);
 | |
| 
 | |
|   mDocViewerPrint = aDocViewerPrint;
 | |
|   mDocShell = do_GetWeakReference(aDocShell);
 | |
|   mScreenDPI = aScreenDPI;
 | |
| 
 | |
|   // Anything state that we need from aOriginalDoc must be fetched and stored
 | |
|   // here, since the document that the user selected to print may mutate
 | |
|   // across consecutive PrintPreview() calls.
 | |
| 
 | |
|   Element* root = aOriginalDoc->GetRootElement();
 | |
|   mDisallowSelectionPrint =
 | |
|       root &&
 | |
|       root->HasAttr(kNameSpaceID_None, nsGkAtoms::mozdisallowselectionprint);
 | |
| 
 | |
|   if (nsPIDOMWindowOuter* window = aOriginalDoc->GetWindow()) {
 | |
|     if (nsCOMPtr<nsIWebBrowserChrome> wbc = window->GetWebBrowserChrome()) {
 | |
|       // We only get this in order to skip opening the progress dialog when
 | |
|       // the window is modal.  Once the platform code stops opening the
 | |
|       // progress dialog (bug 1558907), we can get rid of this.
 | |
|       wbc->IsWindowModal(&mIsForModalWindow);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| //-------------------------------------------------------
 | |
| nsresult nsPrintJob::Cancel() {
 | |
|   if (mPrt && mPrt->mPrintSettings) {
 | |
|     return mPrt->mPrintSettings->SetIsCancelled(true);
 | |
|   }
 | |
|   return NS_ERROR_FAILURE;
 | |
| }
 | |
| 
 | |
| //-----------------------------------------------------------------
 | |
| std::tuple<nsPageSequenceFrame*, int32_t>
 | |
| nsPrintJob::GetSeqFrameAndCountSheets() const {
 | |
|   nsPrintData* printData = mPrtPreview ? mPrtPreview : mPrt;
 | |
|   if (NS_WARN_IF(!printData)) {
 | |
|     return {nullptr, 0};
 | |
|   }
 | |
| 
 | |
|   const nsPrintObject* po = printData->mPrintObject.get();
 | |
|   if (NS_WARN_IF(!po)) {
 | |
|     return {nullptr, 0};
 | |
|   }
 | |
| 
 | |
|   // This is sometimes incorrectly called before the pres shell has been created
 | |
|   // (bug 1141756). MOZ_DIAGNOSTIC_ASSERT so we'll still see the crash in
 | |
|   // Nightly/Aurora in case the other patch fixes this.
 | |
|   if (!po->mPresShell) {
 | |
|     MOZ_DIAGNOSTIC_ASSERT(
 | |
|         false, "GetSeqFrameAndCountSheets needs a non-null pres shell");
 | |
|     return {nullptr, 0};
 | |
|   }
 | |
| 
 | |
|   nsPageSequenceFrame* seqFrame = po->mPresShell->GetPageSequenceFrame();
 | |
|   if (!seqFrame) {
 | |
|     return {nullptr, 0};
 | |
|   }
 | |
| 
 | |
|   // count the total number of sheets
 | |
|   return {seqFrame, seqFrame->PrincipalChildList().GetLength()};
 | |
| }
 | |
| //---------------------------------------------------------------------------------
 | |
| //-- Done: Methods needed by the DocViewer
 | |
| //---------------------------------------------------------------------------------
 | |
| 
 | |
| //---------------------------------------------------------------------------------
 | |
| //-- Section: nsIWebBrowserPrint
 | |
| //---------------------------------------------------------------------------------
 | |
| 
 | |
| // Foward decl for Debug Helper Functions
 | |
| #ifdef EXTENDED_DEBUG_PRINTING
 | |
| #  ifdef XP_WIN
 | |
| static int RemoveFilesInDir(const char* aDir);
 | |
| #  endif
 | |
| static void GetDocTitleAndURL(const UniquePtr<nsPrintObject>& aPO,
 | |
|                               nsACString& aDocStr, nsACString& aURLStr);
 | |
| static void DumpPrintObjectsTree(nsPrintObject* aPO, int aLevel, FILE* aFD);
 | |
| static void DumpPrintObjectsList(const nsTArray<nsPrintObject*>& aDocList);
 | |
| static void RootFrameList(nsPresContext* aPresContext, FILE* out,
 | |
|                           const char* aPrefix);
 | |
| static void DumpViews(nsIDocShell* aDocShell, FILE* out);
 | |
| static void DumpLayoutData(const char* aTitleStr, const char* aURLStr,
 | |
|                            nsPresContext* aPresContext, nsDeviceContext* aDC,
 | |
|                            nsIFrame* aRootFrame, nsIDocShell* aDocShell,
 | |
|                            FILE* aFD);
 | |
| #endif
 | |
| 
 | |
| //--------------------------------------------------------------------------------
 | |
| 
 | |
| nsresult nsPrintJob::CommonPrint(bool aIsPrintPreview,
 | |
|                                  nsIPrintSettings* aPrintSettings,
 | |
|                                  nsIWebProgressListener* aWebProgressListener,
 | |
|                                  Document* aSourceDoc) {
 | |
|   // Callers must hold a strong reference to |this| to ensure that we stay
 | |
|   // alive for the duration of this method, because our main owning reference
 | |
|   // (on nsDocumentViewer) might be cleared during this function (if we cause
 | |
|   // script to run and it cancels the print operation).
 | |
| 
 | |
|   nsresult rv = DoCommonPrint(aIsPrintPreview, aPrintSettings,
 | |
|                               aWebProgressListener, aSourceDoc);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     if (aIsPrintPreview) {
 | |
|       mIsCreatingPrintPreview = false;
 | |
|       SetIsPrintPreview(false);
 | |
|     } else {
 | |
|       SetIsPrinting(false);
 | |
|     }
 | |
|     if (mProgressDialogIsShown) CloseProgressDialog(aWebProgressListener);
 | |
|     if (rv != NS_ERROR_ABORT && rv != NS_ERROR_OUT_OF_MEMORY) {
 | |
|       FirePrintingErrorEvent(rv);
 | |
|     }
 | |
|     mPrt = nullptr;
 | |
|   }
 | |
| 
 | |
|   return rv;
 | |
| }
 | |
| 
 | |
| nsresult nsPrintJob::DoCommonPrint(bool aIsPrintPreview,
 | |
|                                    nsIPrintSettings* aPrintSettings,
 | |
|                                    nsIWebProgressListener* aWebProgressListener,
 | |
|                                    Document* aDoc) {
 | |
|   MOZ_ASSERT(aDoc->IsStaticDocument());
 | |
| 
 | |
|   nsresult rv;
 | |
| 
 | |
|   // Grab the new instance with local variable to guarantee that it won't be
 | |
|   // deleted during this method.
 | |
|   // Note: Methods we call early below rely on mPrt being set.
 | |
|   mPrt = new nsPrintData(aIsPrintPreview ? nsPrintData::eIsPrintPreview
 | |
|                                          : nsPrintData::eIsPrinting);
 | |
|   RefPtr<nsPrintData> printData = mPrt;
 | |
| 
 | |
|   if (aIsPrintPreview) {
 | |
|     // The WebProgressListener can be QI'ed to nsIPrintingPromptService
 | |
|     // then that means the progress dialog is already being shown.
 | |
|     nsCOMPtr<nsIPrintingPromptService> pps(
 | |
|         do_QueryInterface(aWebProgressListener));
 | |
|     mProgressDialogIsShown = pps != nullptr;
 | |
| 
 | |
|     mIsCreatingPrintPreview = true;
 | |
| 
 | |
|     // Our new print preview nsPrintData is stored in mPtr until we move it
 | |
|     // to mPrtPreview once we've finish creating the print preview. We must
 | |
|     // clear mPtrPreview so that code will use mPtr until that happens.
 | |
|     mPrtPreview = nullptr;
 | |
| 
 | |
|     SetIsPrintPreview(true);
 | |
|   } else {
 | |
|     mProgressDialogIsShown = false;
 | |
| 
 | |
|     SetIsPrinting(true);
 | |
|   }
 | |
| 
 | |
|   if (aWebProgressListener) {
 | |
|     printData->mPrintProgressListeners.AppendObject(aWebProgressListener);
 | |
|   }
 | |
| 
 | |
|   // Get the document from the currently focused window.
 | |
|   RefPtr<Document> focusedDoc = FindFocusedDocument(aDoc);
 | |
| 
 | |
|   // Get the docshell for this documentviewer
 | |
|   nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mDocShell, &rv));
 | |
|   NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|   {
 | |
|     nsAutoScriptBlocker scriptBlocker;
 | |
|     printData->mPrintObject = MakeUnique<nsPrintObject>();
 | |
|     rv = printData->mPrintObject->InitAsRootObject(docShell, aDoc,
 | |
|                                                    mIsCreatingPrintPreview);
 | |
|     NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|     printData->mPrintDocList.AppendElement(printData->mPrintObject.get());
 | |
| 
 | |
|     printData->mIsParentAFrameSet = IsParentAFrameSet(docShell);
 | |
|     printData->mPrintObject->mFrameType =
 | |
|         printData->mIsParentAFrameSet ? eFrameSet : eDoc;
 | |
| 
 | |
|     BuildNestedPrintObjects(printData->mPrintObject, focusedDoc, printData);
 | |
|   }
 | |
| 
 | |
|   // The nsAutoScriptBlocker above will now have been destroyed, which may
 | |
|   // cause our print/print-preview operation to finish. In this case, we
 | |
|   // should immediately return an error code so that the root caller knows
 | |
|   // it shouldn't continue to do anything with this instance.
 | |
|   if (mIsDestroying) {
 | |
|     return NS_ERROR_FAILURE;
 | |
|   }
 | |
| 
 | |
|   // XXX This isn't really correct...
 | |
|   if (!printData->mPrintObject->mDocument ||
 | |
|       !printData->mPrintObject->mDocument->GetRootElement())
 | |
|     return NS_ERROR_GFX_PRINTER_STARTDOC;
 | |
| 
 | |
|   // if they don't pass in a PrintSettings, then get the Global PS
 | |
|   printData->mPrintSettings = aPrintSettings;
 | |
|   if (!printData->mPrintSettings) {
 | |
|     MOZ_TRY(GetDefaultPrintSettings(getter_AddRefs(printData->mPrintSettings)));
 | |
|   }
 | |
| 
 | |
|   MOZ_TRY(EnsureSettingsHasPrinterNameSet(printData->mPrintSettings));
 | |
| 
 | |
|   printData->mPrintSettings->SetIsCancelled(false);
 | |
|   printData->mPrintSettings->GetShrinkToFit(&printData->mShrinkToFit);
 | |
| 
 | |
|   // Create a print session and let the print settings know about it.
 | |
|   // Don't overwrite an existing print session.
 | |
|   // The print settings hold an nsWeakPtr to the session so it does not
 | |
|   // need to be cleared from the settings at the end of the job.
 | |
|   // XXX What lifetime does the printSession need to have?
 | |
|   nsCOMPtr<nsIPrintSession> printSession;
 | |
|   bool remotePrintJobListening = false;
 | |
|   if (!mIsCreatingPrintPreview) {
 | |
|     rv = printData->mPrintSettings->GetPrintSession(
 | |
|         getter_AddRefs(printSession));
 | |
|     if (NS_FAILED(rv) || !printSession) {
 | |
|       printSession = do_CreateInstance("@mozilla.org/gfx/printsession;1", &rv);
 | |
|       NS_ENSURE_SUCCESS(rv, rv);
 | |
|       printData->mPrintSettings->SetPrintSession(printSession);
 | |
|     } else {
 | |
|       RefPtr<layout::RemotePrintJobChild> remotePrintJob =
 | |
|           printSession->GetRemotePrintJob();
 | |
|       if (remotePrintJob) {
 | |
|         // If we have a RemotePrintJob add it to the print progress listeners,
 | |
|         // so it can forward to the parent.
 | |
|         printData->mPrintProgressListeners.AppendElement(remotePrintJob);
 | |
|         remotePrintJobListening = true;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Now determine how to set up the Frame print UI
 | |
|   printData->mPrintSettings->SetIsPrintSelectionRBEnabled(
 | |
|       !mDisallowSelectionPrint && printData->mSelectionRoot);
 | |
| 
 | |
|   bool printingViaParent =
 | |
|       XRE_IsContentProcess() && Preferences::GetBool("print.print_via_parent");
 | |
|   nsCOMPtr<nsIDeviceContextSpec> devspec;
 | |
|   if (printingViaParent) {
 | |
|     devspec = new nsDeviceContextSpecProxy();
 | |
|   } else {
 | |
|     devspec = do_CreateInstance("@mozilla.org/gfx/devicecontextspec;1", &rv);
 | |
|     NS_ENSURE_SUCCESS(rv, rv);
 | |
|   }
 | |
| 
 | |
|   bool printSilently = false;
 | |
|   printData->mPrintSettings->GetPrintSilent(&printSilently);
 | |
|   if (StaticPrefs::print_always_print_silent()) {
 | |
|     printSilently = true;
 | |
|   }
 | |
| 
 | |
|   if (mIsDoingPrinting && printSilently) {
 | |
|     Telemetry::ScalarAdd(Telemetry::ScalarID::PRINTING_SILENT_PRINT, 1);
 | |
|   }
 | |
| 
 | |
|   // If printing via parent we still call ShowPrintDialog even for print preview
 | |
|   // because we use that to retrieve the print settings from the printer.
 | |
|   // The dialog is not shown, but this means we don't need to access the printer
 | |
|   // driver from the child, which causes sandboxing issues.
 | |
|   if (!mIsCreatingPrintPreview || printingViaParent) {
 | |
|     // The new print UI does not need to enter ShowPrintDialog below to spin
 | |
|     // the event loop and fetch real printer settings from the parent process,
 | |
|     // since it always passes complete print settings. (In fact, trying to
 | |
|     // fetch them from the parent can cause crashes.) Here we check for that
 | |
|     // case so that we can avoid calling ShowPrintDialog below. To err on the
 | |
|     // safe side, we exclude the old UI.
 | |
|     //
 | |
|     // TODO: We should MOZ_DIAGNOSTIC_ASSERT that GetIsInitializedFromPrinter
 | |
|     // returns true.
 | |
|     bool settingsAreComplete = false;
 | |
|     if (StaticPrefs::print_tab_modal_enabled()) {
 | |
|       printData->mPrintSettings->GetIsInitializedFromPrinter(
 | |
|           &settingsAreComplete);
 | |
|     }
 | |
| 
 | |
|     // Ask dialog to be Print Shown via the Plugable Printing Dialog Service
 | |
|     // This service is for the Print Dialog and the Print Progress Dialog
 | |
|     // If printing silently or you can't get the service continue on
 | |
|     // If printing via the parent then we need to confirm that the pref is set
 | |
|     // and get a remote print job, but the parent won't display a prompt.
 | |
|     if (!settingsAreComplete && (!printSilently || printingViaParent)) {
 | |
|       nsCOMPtr<nsIPrintingPromptService> printPromptService(
 | |
|           do_GetService(kPrintingPromptService));
 | |
|       if (printPromptService) {
 | |
|         nsPIDOMWindowOuter* domWin = nullptr;
 | |
|         // We leave domWin as nullptr to indicate a call for print preview.
 | |
|         if (!mIsCreatingPrintPreview) {
 | |
|           domWin = aDoc->GetOriginalDocument()->GetWindow();
 | |
|           NS_ENSURE_TRUE(domWin, NS_ERROR_FAILURE);
 | |
| 
 | |
|           if (!printSilently) {
 | |
|             if (mCreatedForPrintPreview) {
 | |
|               Telemetry::ScalarAdd(
 | |
|                   Telemetry::ScalarID::PRINTING_DIALOG_OPENED_VIA_PREVIEW, 1);
 | |
|             } else {
 | |
|               Telemetry::ScalarAdd(
 | |
|                   Telemetry::ScalarID::PRINTING_DIALOG_OPENED_WITHOUT_PREVIEW,
 | |
|                   1);
 | |
|             }
 | |
|           }
 | |
|         }
 | |
| 
 | |
|         // Platforms not implementing a given dialog for the service may
 | |
|         // return NS_ERROR_NOT_IMPLEMENTED or an error code.
 | |
|         //
 | |
|         // NS_ERROR_NOT_IMPLEMENTED indicates they want default behavior
 | |
|         // Any other error code means we must bail out
 | |
|         //
 | |
|         rv = printPromptService->ShowPrintDialog(domWin,
 | |
|                                                  printData->mPrintSettings);
 | |
| 
 | |
|         if (!mIsCreatingPrintPreview) {
 | |
|           if (rv == NS_ERROR_ABORT) {
 | |
|             // When printing silently we can't get here since the user doesn't
 | |
|             // have the opportunity to cancel printing.
 | |
|             if (mCreatedForPrintPreview) {
 | |
|               Telemetry::ScalarAdd(
 | |
|                   Telemetry::ScalarID::PRINTING_DIALOG_VIA_PREVIEW_CANCELLED,
 | |
|                   1);
 | |
|             } else {
 | |
|               Telemetry::ScalarAdd(
 | |
|                   Telemetry::ScalarID::
 | |
|                       PRINTING_DIALOG_WITHOUT_PREVIEW_CANCELLED,
 | |
|                   1);
 | |
|             }
 | |
|           }
 | |
|         }
 | |
| 
 | |
|         //
 | |
|         // ShowPrintDialog triggers an event loop which means we can't assume
 | |
|         // that the state of this->{anything} matches the state we've checked
 | |
|         // above. Including that a given {thing} is non null.
 | |
|         if (NS_WARN_IF(mPrt != printData)) {
 | |
|           return NS_ERROR_FAILURE;
 | |
|         }
 | |
| 
 | |
|         if (NS_SUCCEEDED(rv)) {
 | |
|           // since we got the dialog and it worked then make sure we
 | |
|           // are telling GFX we want to print silent
 | |
|           printSilently = true;
 | |
| 
 | |
|           if (printData->mPrintSettings && !mIsCreatingPrintPreview) {
 | |
|             // The user might have changed shrink-to-fit in the print dialog, so
 | |
|             // update our copy of its state
 | |
|             printData->mPrintSettings->GetShrinkToFit(&printData->mShrinkToFit);
 | |
| 
 | |
|             // If we haven't already added the RemotePrintJob as a listener,
 | |
|             // add it now if there is one.
 | |
|             if (!remotePrintJobListening) {
 | |
|               RefPtr<layout::RemotePrintJobChild> remotePrintJob =
 | |
|                   printSession->GetRemotePrintJob();
 | |
|               if (remotePrintJob) {
 | |
|                 printData->mPrintProgressListeners.AppendElement(
 | |
|                     remotePrintJob);
 | |
|                 remotePrintJobListening = true;
 | |
|               }
 | |
|             }
 | |
|           }
 | |
|         } else if (rv == NS_ERROR_NOT_IMPLEMENTED) {
 | |
|           // This means the Dialog service was there,
 | |
|           // but they choose not to implement this dialog and
 | |
|           // are looking for default behavior from the toolkit
 | |
|           rv = NS_OK;
 | |
|         }
 | |
|       } else {
 | |
|         // No dialog service available
 | |
|         rv = NS_ERROR_NOT_IMPLEMENTED;
 | |
|       }
 | |
|     } else if (printSilently && !printingViaParent) {
 | |
|       // The condition above is only so contorted in order to enter this block
 | |
|       // under the exact same circumstances as we used to, in order to
 | |
|       // minimize risk for this change which may be getting late Beta uplift.
 | |
|       // Frankly calling SetupSilentPrinting should not be necessary any more
 | |
|       // since nsDeviceContextSpecGTK::EndDocument does what we need using a
 | |
|       // Runnable instead of spinning an event loop in a risk place like here.
 | |
|       // Additionally we should never need to do this when setting up print
 | |
|       // preview, we would only need it for printing.
 | |
| 
 | |
|       // Call any code that requires a run of the event loop.
 | |
|       rv = printData->mPrintSettings->SetupSilentPrinting();
 | |
|     }
 | |
|     // Check explicitly for abort because it's expected
 | |
|     if (rv == NS_ERROR_ABORT) return rv;
 | |
|     NS_ENSURE_SUCCESS(rv, rv);
 | |
|   }
 | |
| 
 | |
|   MOZ_TRY(devspec->Init(nullptr, printData->mPrintSettings,
 | |
|                         mIsCreatingPrintPreview));
 | |
| 
 | |
|   printData->mPrintDC = new nsDeviceContext();
 | |
|   MOZ_TRY(printData->mPrintDC->InitForPrinting(devspec));
 | |
| 
 | |
|   if (XRE_IsParentProcess() && !printData->mPrintDC->IsSyncPagePrinting()) {
 | |
|     RefPtr<nsPrintJob> self(this);
 | |
|     printData->mPrintDC->RegisterPageDoneCallback(
 | |
|         [self](nsresult aResult) { self->PageDone(aResult); });
 | |
|   }
 | |
| 
 | |
|   if (!mozilla::StaticPrefs::print_tab_modal_enabled() &&
 | |
|       mIsCreatingPrintPreview) {
 | |
|     // In legacy print-preview mode, override any UI that wants to PrintPreview
 | |
|     // any selection or page range.  The legacy print-preview intends to view
 | |
|     // every page in PrintPreview each time.
 | |
|     printData->mPrintSettings->SetPageRanges({});
 | |
|   }
 | |
| 
 | |
|   MOZ_TRY(EnablePOsForPrinting());
 | |
| 
 | |
|   if (mIsCreatingPrintPreview) {
 | |
|     bool notifyOnInit = false;
 | |
|     ShowPrintProgress(false, notifyOnInit, aDoc);
 | |
| 
 | |
|     if (!notifyOnInit) {
 | |
|       rv = InitPrintDocConstruction(false);
 | |
|     } else {
 | |
|       rv = NS_OK;
 | |
|     }
 | |
|   } else {
 | |
|     bool doNotify;
 | |
|     ShowPrintProgress(true, doNotify, aDoc);
 | |
|     if (!doNotify) {
 | |
|       // Print listener setup...
 | |
|       printData->OnStartPrinting();
 | |
| 
 | |
|       rv = InitPrintDocConstruction(false);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| //---------------------------------------------------------------------------------
 | |
| nsresult nsPrintJob::Print(Document* aSourceDoc,
 | |
|                            nsIPrintSettings* aPrintSettings,
 | |
|                            nsIWebProgressListener* aWebProgressListener) {
 | |
|   // If we have a print preview document, use that instead of the original
 | |
|   // mDocument. That way animated images etc. get printed using the same state
 | |
|   // as in print preview.
 | |
|   RefPtr<Document> doc = mPrtPreview && mPrtPreview->mPrintObject
 | |
|                              ? mPrtPreview->mPrintObject->mDocument.get()
 | |
|                              : aSourceDoc;
 | |
| 
 | |
|   nsresult rv = CommonPrint(false, aPrintSettings, aWebProgressListener, doc);
 | |
| 
 | |
|   if (!aPrintSettings) {
 | |
|     // This is temporary until after bug 1602410 lands.
 | |
|     return rv;
 | |
|   }
 | |
| 
 | |
|   // Save the print settings if the user picked them.
 | |
|   // We should probably do this immediately after the user confirms their
 | |
|   // selection (that is, move this to nsPrintingPromptService::ShowPrintDialog,
 | |
|   // just after the nsIPrintDialogService::Show call returns).
 | |
|   bool printSilently;
 | |
|   aPrintSettings->GetPrintSilent(&printSilently);
 | |
|   if (!printSilently) {  // user picked settings
 | |
|     bool saveOnCancel;
 | |
|     aPrintSettings->GetSaveOnCancel(&saveOnCancel);
 | |
|     if ((rv != NS_ERROR_ABORT || saveOnCancel) &&
 | |
|         Preferences::GetBool("print.save_print_settings", false)) {
 | |
|       nsCOMPtr<nsIPrintSettingsService> printSettingsService =
 | |
|           do_GetService("@mozilla.org/gfx/printsettings-service;1");
 | |
|       printSettingsService->SavePrintSettingsToPrefs(
 | |
|           aPrintSettings, true, nsIPrintSettings::kInitSaveAll);
 | |
|       printSettingsService->SavePrintSettingsToPrefs(
 | |
|           aPrintSettings, false, nsIPrintSettings::kInitSavePrinterName);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return rv;
 | |
| }
 | |
| 
 | |
| nsresult nsPrintJob::PrintPreview(Document* aSourceDoc,
 | |
|                                   nsIPrintSettings* aPrintSettings,
 | |
|                                   nsIWebProgressListener* aWebProgressListener,
 | |
|                                   PrintPreviewResolver&& aCallback) {
 | |
|   // Take ownership of aCallback, otherwise a function further up the call
 | |
|   // stack will call it to signal failure (by passing zero).
 | |
|   mPrintPreviewCallback = std::move(aCallback);
 | |
| 
 | |
|   nsresult rv =
 | |
|       CommonPrint(true, aPrintSettings, aWebProgressListener, aSourceDoc);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     if (mPrintPreviewCallback) {
 | |
|       mPrintPreviewCallback(
 | |
|           PrintPreviewResultInfo(0, 0, false, false, false));  // signal error
 | |
|       mPrintPreviewCallback = nullptr;
 | |
|     }
 | |
|   }
 | |
|   return rv;
 | |
| }
 | |
| 
 | |
| int32_t nsPrintJob::GetRawNumPages() const {
 | |
|   auto [seqFrame, numSheets] = GetSeqFrameAndCountSheets();
 | |
|   Unused << numSheets;
 | |
|   return seqFrame ? seqFrame->GetRawNumPages() : 0;
 | |
| }
 | |
| 
 | |
| bool nsPrintJob::GetIsEmpty() const {
 | |
|   auto [seqFrame, numSheets] = GetSeqFrameAndCountSheets();
 | |
|   if (!seqFrame) {
 | |
|     return true;
 | |
|   }
 | |
|   if (numSheets > 1) {
 | |
|     return false;
 | |
|   }
 | |
|   return !seqFrame->GetPagesInFirstSheet();
 | |
| }
 | |
| 
 | |
| int32_t nsPrintJob::GetPrintPreviewNumSheets() const {
 | |
|   auto [seqFrame, numSheets] = GetSeqFrameAndCountSheets();
 | |
|   Unused << seqFrame;
 | |
|   return numSheets;
 | |
| }
 | |
| 
 | |
| already_AddRefed<nsIPrintSettings> nsPrintJob::GetCurrentPrintSettings() {
 | |
|   if (mPrt) {
 | |
|     return do_AddRef(mPrt->mPrintSettings);
 | |
|   }
 | |
|   if (mPrtPreview) {
 | |
|     return do_AddRef(mPrtPreview->mPrintSettings);
 | |
|   }
 | |
|   return nullptr;
 | |
| }
 | |
| 
 | |
| //-----------------------------------------------------------------
 | |
| //-- Section: Pre-Reflow Methods
 | |
| //-----------------------------------------------------------------
 | |
| 
 | |
| //----------------------------------------------------------------------
 | |
| // Set up to use the "pluggable" Print Progress Dialog
 | |
| void nsPrintJob::ShowPrintProgress(bool aIsForPrinting, bool& aDoNotify,
 | |
|                                    Document* aDoc) {
 | |
|   // default to not notifying, that if something here goes wrong
 | |
|   // or we aren't going to show the progress dialog we can straight into
 | |
|   // reflowing the doc for printing.
 | |
|   aDoNotify = false;
 | |
| 
 | |
|   // Guarantee that mPrt and the objects it owns won't be deleted.  If this
 | |
|   // method shows a progress dialog and spins the event loop.  So, mPrt may be
 | |
|   // cleared or recreated.
 | |
|   RefPtr<nsPrintData> printData = mPrt;
 | |
| 
 | |
|   bool showProgresssDialog =
 | |
|       !mProgressDialogIsShown && StaticPrefs::print_show_print_progress();
 | |
| 
 | |
|   // Turning off the showing of Print Progress in Prefs overrides
 | |
|   // whether the calling PS desire to have it on or off, so only check PS if
 | |
|   // prefs says it's ok to be on.
 | |
|   if (showProgresssDialog) {
 | |
|     printData->mPrintSettings->GetShowPrintProgress(&showProgresssDialog);
 | |
|   }
 | |
| 
 | |
|   // Now open the service to get the progress dialog
 | |
|   // If we don't get a service, that's ok, then just don't show progress
 | |
|   if (showProgresssDialog) {
 | |
|     nsCOMPtr<nsIPrintingPromptService> printPromptService(
 | |
|         do_GetService(kPrintingPromptService));
 | |
|     if (printPromptService) {
 | |
|       if (mIsForModalWindow) {
 | |
|         // Showing a print progress dialog when printing a modal window
 | |
|         // isn't supported. See bug 301560.
 | |
|         return;
 | |
|       }
 | |
| 
 | |
|       nsPIDOMWindowOuter* domWin = aDoc->GetOriginalDocument()->GetWindow();
 | |
|       if (!domWin) return;
 | |
| 
 | |
|       nsCOMPtr<nsIWebProgressListener> printProgressListener;
 | |
| 
 | |
|       nsresult rv = printPromptService->ShowPrintProgressDialog(
 | |
|           domWin, printData->mPrintSettings, this, aIsForPrinting,
 | |
|           getter_AddRefs(printProgressListener),
 | |
|           getter_AddRefs(printData->mPrintProgressParams), &aDoNotify);
 | |
|       if (NS_SUCCEEDED(rv)) {
 | |
|         if (printProgressListener) {
 | |
|           printData->mPrintProgressListeners.AppendObject(
 | |
|               printProgressListener);
 | |
|         }
 | |
| 
 | |
|         if (printData->mPrintProgressParams) {
 | |
|           SetURLAndTitleOnProgressParams(printData->mPrintObject,
 | |
|                                          printData->mPrintProgressParams);
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| // static
 | |
| void nsPrintJob::GetDisplayTitleAndURL(Document& aDoc,
 | |
|                                        nsIPrintSettings* aSettings,
 | |
|                                        DocTitleDefault aTitleDefault,
 | |
|                                        nsAString& aTitle, nsAString& aURLStr) {
 | |
|   aTitle.Truncate();
 | |
|   aURLStr.Truncate();
 | |
| 
 | |
|   if (aSettings) {
 | |
|     aSettings->GetTitle(aTitle);
 | |
|     aSettings->GetDocURL(aURLStr);
 | |
|   }
 | |
| 
 | |
|   if (aTitle.IsEmpty()) {
 | |
|     aDoc.GetTitle(aTitle);
 | |
|     if (aTitle.IsEmpty()) {
 | |
|       if (!aURLStr.IsEmpty() &&
 | |
|           aTitleDefault == DocTitleDefault::eDocURLElseFallback) {
 | |
|         aTitle = aURLStr;
 | |
|       } else {
 | |
|         nsCOMPtr<nsIStringBundle> brandBundle;
 | |
|         nsCOMPtr<nsIStringBundleService> svc =
 | |
|             mozilla::components::StringBundle::Service();
 | |
|         if (svc) {
 | |
|           svc->CreateBundle("chrome://branding/locale/brand.properties",
 | |
|                             getter_AddRefs(brandBundle));
 | |
|           if (brandBundle) {
 | |
|             brandBundle->GetStringFromName("brandShortName", aTitle);
 | |
|           }
 | |
|         }
 | |
|         if (aTitle.IsEmpty()) {
 | |
|           aTitle.AssignLiteral(u"Mozilla Document");
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (aURLStr.IsEmpty()) {
 | |
|     nsIURI* url = aDoc.GetDocumentURI();
 | |
|     if (!url) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     nsCOMPtr<nsIURI> exposableURI = net::nsIOService::CreateExposableURI(url);
 | |
|     nsAutoCString urlCStr;
 | |
|     nsresult rv = exposableURI->GetSpec(urlCStr);
 | |
|     if (NS_FAILED(rv)) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     nsCOMPtr<nsITextToSubURI> textToSubURI =
 | |
|         do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
 | |
|     if (NS_FAILED(rv)) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     textToSubURI->UnEscapeURIForUI(urlCStr, aURLStr);
 | |
|   }
 | |
| }
 | |
| 
 | |
| //---------------------------------------------------------------------
 | |
| nsresult nsPrintJob::DocumentReadyForPrinting() {
 | |
|   // Send the document to the printer...
 | |
|   nsresult rv = SetupToPrintContent();
 | |
|   if (NS_FAILED(rv)) {
 | |
|     // The print job was canceled or there was a problem
 | |
|     // So remove all other documents from the print list
 | |
|     DonePrintingSheets(nullptr, rv);
 | |
|   }
 | |
|   return rv;
 | |
| }
 | |
| 
 | |
| /** ---------------------------------------------------
 | |
|  *  Cleans up when an error occurred
 | |
|  */
 | |
| nsresult nsPrintJob::CleanupOnFailure(nsresult aResult, bool aIsPrinting) {
 | |
|   PR_PL(("****  Failed %s - rv 0x%" PRIX32,
 | |
|          aIsPrinting ? "Printing" : "Print Preview",
 | |
|          static_cast<uint32_t>(aResult)));
 | |
| 
 | |
|   /* cleanup... */
 | |
|   if (mPagePrintTimer) {
 | |
|     mPagePrintTimer->Stop();
 | |
|     DisconnectPagePrintTimer();
 | |
|   }
 | |
| 
 | |
|   if (aIsPrinting) {
 | |
|     SetIsPrinting(false);
 | |
|   } else {
 | |
|     SetIsPrintPreview(false);
 | |
|     mIsCreatingPrintPreview = false;
 | |
|   }
 | |
| 
 | |
|   /* cleanup done, let's fire-up an error dialog to notify the user
 | |
|    * what went wrong...
 | |
|    *
 | |
|    * When rv == NS_ERROR_ABORT, it means we want out of the
 | |
|    * print job without displaying any error messages
 | |
|    */
 | |
|   if (aResult != NS_ERROR_ABORT) {
 | |
|     FirePrintingErrorEvent(aResult);
 | |
|   }
 | |
| 
 | |
|   FirePrintCompletionEvent();
 | |
| 
 | |
|   return aResult;
 | |
| }
 | |
| 
 | |
| //---------------------------------------------------------------------
 | |
| void nsPrintJob::FirePrintingErrorEvent(nsresult aPrintError) {
 | |
|   if (mPrintPreviewCallback) {
 | |
|     mPrintPreviewCallback(
 | |
|         PrintPreviewResultInfo(0, 0, false, false, false));  // signal error
 | |
|     mPrintPreviewCallback = nullptr;
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<nsIContentViewer> cv = do_QueryInterface(mDocViewerPrint);
 | |
|   if (NS_WARN_IF(!cv)) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<Document> doc = cv->GetDocument();
 | |
|   RefPtr<CustomEvent> event = NS_NewDOMCustomEvent(doc, nullptr, nullptr);
 | |
| 
 | |
|   MOZ_ASSERT(event);
 | |
| 
 | |
|   AutoJSAPI jsapi;
 | |
|   if (!jsapi.Init(event->GetParentObject())) {
 | |
|     return;
 | |
|   }
 | |
|   JSContext* cx = jsapi.cx();
 | |
| 
 | |
|   JS::Rooted<JS::Value> detail(
 | |
|       cx, JS::NumberValue(static_cast<double>(aPrintError)));
 | |
|   event->InitCustomEvent(cx, u"PrintingError"_ns, false, false, detail);
 | |
|   event->SetTrusted(true);
 | |
| 
 | |
|   RefPtr<AsyncEventDispatcher> asyncDispatcher =
 | |
|       new AsyncEventDispatcher(doc, event);
 | |
|   asyncDispatcher->mOnlyChromeDispatch = ChromeOnlyDispatch::eYes;
 | |
|   asyncDispatcher->RunDOMEventWhenSafe();
 | |
| 
 | |
|   // Inform any progress listeners of the Error.
 | |
|   if (mPrt) {
 | |
|     // Note that nsPrintData::DoOnStatusChange() will call some listeners.
 | |
|     // So, mPrt can be cleared or recreated.
 | |
|     RefPtr<nsPrintData> printData = mPrt;
 | |
|     printData->DoOnStatusChange(aPrintError);
 | |
|   }
 | |
| }
 | |
| 
 | |
| //-----------------------------------------------------------------
 | |
| //-- Section: Reflow Methods
 | |
| //-----------------------------------------------------------------
 | |
| 
 | |
| nsresult nsPrintJob::ReconstructAndReflow(bool doSetPixelScale) {
 | |
|   if (NS_WARN_IF(!mPrt)) {
 | |
|     return NS_ERROR_FAILURE;
 | |
|   }
 | |
| 
 | |
| #if defined(XP_WIN) && defined(EXTENDED_DEBUG_PRINTING)
 | |
|   // We need to clear all the output files here
 | |
|   // because they will be re-created with second reflow of the docs
 | |
|   if (MOZ_LOG_TEST(gPrintingLog, DUMP_LAYOUT_LEVEL)) {
 | |
|     RemoveFilesInDir(".\\");
 | |
|     gDumpFileNameCnt = 0;
 | |
|     gDumpLOFileNameCnt = 0;
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   // In this loop, it's conceivable that one of our helpers might clear mPrt,
 | |
|   // while we're using it & its members!  So we capture it in an owning local
 | |
|   // reference & use that instead of using mPrt directly.
 | |
|   RefPtr<nsPrintData> printData = mPrt;
 | |
|   for (uint32_t i = 0; i < printData->mPrintDocList.Length(); ++i) {
 | |
|     nsPrintObject* po = printData->mPrintDocList.ElementAt(i);
 | |
|     NS_ASSERTION(po, "nsPrintObject can't be null!");
 | |
| 
 | |
|     if (!po->PrintingIsEnabled() || po->mInvisible) {
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     // When the print object has been marked as "print the document" (i.e,
 | |
|     // po->PrintingIsEnabled() is true), mPresContext and mPresShell should be
 | |
|     // non-nullptr (i.e., should've been created for the print) since they
 | |
|     // are necessary to print the document.
 | |
|     MOZ_ASSERT(po->mPresContext && po->mPresShell,
 | |
|                "mPresContext and mPresShell shouldn't be nullptr when the "
 | |
|                "print object "
 | |
|                "has been marked as \"print the document\"");
 | |
| 
 | |
|     UpdateZoomRatio(po, doSetPixelScale);
 | |
| 
 | |
|     po->mPresContext->SetPageScale(po->mZoomRatio);
 | |
| 
 | |
|     // Calculate scale factor from printer to screen
 | |
|     float printDPI = float(AppUnitsPerCSSInch()) /
 | |
|                      float(printData->mPrintDC->AppUnitsPerDevPixel());
 | |
|     po->mPresContext->SetPrintPreviewScale(mScreenDPI / printDPI);
 | |
| 
 | |
|     RefPtr<PresShell> presShell(po->mPresShell);
 | |
|     if (NS_WARN_IF(presShell->IsDestroying())) {
 | |
|       return NS_ERROR_FAILURE;
 | |
|     }
 | |
| 
 | |
|     presShell->ReconstructFrames();
 | |
| 
 | |
|     // If the printing was canceled or restarted with different data,
 | |
|     // let's stop doing this printing.
 | |
|     if (NS_WARN_IF(mPrt != printData)) {
 | |
|       return NS_ERROR_FAILURE;
 | |
|     }
 | |
| 
 | |
|     // For all views except the first one, setup the root view.
 | |
|     // ??? Can there be multiple po for the top-level-document?
 | |
|     bool documentIsTopLevel = true;
 | |
|     if (i != 0) {
 | |
|       nsSize adjSize;
 | |
|       bool doReturn;
 | |
|       nsresult rv = SetRootView(po, doReturn, documentIsTopLevel, adjSize);
 | |
| 
 | |
|       MOZ_ASSERT(!documentIsTopLevel, "How could this happen?");
 | |
| 
 | |
|       if (NS_FAILED(rv) || doReturn) {
 | |
|         return rv;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     presShell->FlushPendingNotifications(FlushType::Layout);
 | |
| 
 | |
|     if (NS_WARN_IF(presShell->IsDestroying())) {
 | |
|       return NS_ERROR_FAILURE;
 | |
|     }
 | |
| 
 | |
|     // If the printing was canceled or restarted with different data,
 | |
|     // let's stop doing this printing.
 | |
|     if (NS_WARN_IF(mPrt != printData)) {
 | |
|       return NS_ERROR_FAILURE;
 | |
|     }
 | |
| 
 | |
|     nsresult rv = UpdateSelectionAndShrinkPrintObject(po, documentIsTopLevel);
 | |
|     NS_ENSURE_SUCCESS(rv, rv);
 | |
|   }
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| //-------------------------------------------------------
 | |
| nsresult nsPrintJob::SetupToPrintContent() {
 | |
|   // This method may be called while DoCommonPrint() initializes the instance
 | |
|   // when its script blocker goes out of scope.  In such case, this cannot do
 | |
|   // its job as expected because some objects in mPrt have not been initialized
 | |
|   // yet but they are necessary.
 | |
|   // Note: it shouldn't be possible for mPrt->mPrintObject to be null; we check
 | |
|   // it for good measure (after we check its owner) before we start
 | |
|   // dereferencing it below.
 | |
|   if (NS_WARN_IF(!mPrt) || NS_WARN_IF(!mPrt->mPrintObject)) {
 | |
|     return NS_ERROR_FAILURE;
 | |
|   }
 | |
| 
 | |
|   // If this is creating print preview, mPrt->mPrintObject->mPresContext and
 | |
|   // mPrt->mPrintObject->mPresShell need to be non-nullptr because this cannot
 | |
|   // initialize page sequence frame without them at end of this method since
 | |
|   // page sequence frame has already been destroyed or not been created yet.
 | |
|   if (mIsCreatingPrintPreview &&
 | |
|       (NS_WARN_IF(!mPrt->mPrintObject->mPresContext) ||
 | |
|        NS_WARN_IF(!mPrt->mPrintObject->mPresShell))) {
 | |
|     return NS_ERROR_FAILURE;
 | |
|   }
 | |
| 
 | |
|   // If this is printing some documents (not print-previewing the documents),
 | |
|   // mPrt->mPrintObject->mPresContext and mPrt->mPrintObject->mPresShell can be
 | |
|   // nullptr only when mPrt->mPrintObject->PrintingIsEnabled() is false.  E.g.,
 | |
|   // if the document has a <frameset> element and it's printing only content in
 | |
|   // a <frame> element or all <frame> elements separately.
 | |
|   MOZ_ASSERT(
 | |
|       (!mIsCreatingPrintPreview && !mPrt->mPrintObject->PrintingIsEnabled()) ||
 | |
|           (mPrt->mPrintObject->mPresContext && mPrt->mPrintObject->mPresShell),
 | |
|       "mPresContext and mPresShell shouldn't be nullptr when printing the "
 | |
|       "document or creating print-preview");
 | |
| 
 | |
|   bool didReconstruction = false;
 | |
| 
 | |
|   // This method works with mPrt->mPrintObject.  So, we need to guarantee that
 | |
|   // it won't be deleted in this method.  We achieve this by holding a strong
 | |
|   // local reference to mPrt, which in turn keeps mPrintObject alive.
 | |
|   RefPtr<nsPrintData> printData = mPrt;
 | |
| 
 | |
|   // If some new content got loaded since the initial reflow rebuild
 | |
|   // everything.
 | |
|   if (mDidLoadDataForPrinting) {
 | |
|     nsresult rv = ReconstructAndReflow(DoSetPixelScale());
 | |
|     if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|       return rv;
 | |
|     }
 | |
|     // If the printing was canceled or restarted with different data,
 | |
|     // let's stop doing this printing.
 | |
|     if (NS_WARN_IF(mPrt != printData)) {
 | |
|       return NS_ERROR_FAILURE;
 | |
|     }
 | |
|     didReconstruction = true;
 | |
|   }
 | |
| 
 | |
|   // Here is where we figure out if extra reflow for shrinking the content
 | |
|   // is required.
 | |
|   // But skip this step if we are in PrintPreview
 | |
|   bool ppIsShrinkToFit = mPrtPreview && mPrtPreview->mShrinkToFit;
 | |
|   if (printData->mShrinkToFit && !ppIsShrinkToFit) {
 | |
|     // Now look for the PO that has the smallest percent for shrink to fit
 | |
|     if (printData->mPrintDocList.Length() > 1 &&
 | |
|         printData->mPrintObject->mFrameType == eFrameSet) {
 | |
|       nsPrintObject* smallestPO = FindSmallestSTF();
 | |
|       NS_ASSERTION(smallestPO, "There must always be an XMost PO!");
 | |
|       if (smallestPO) {
 | |
|         // Calc the shrinkage based on the entire content area
 | |
|         printData->mShrinkRatio = smallestPO->mShrinkRatio;
 | |
|       }
 | |
|     } else {
 | |
|       // Single document so use the Shrink as calculated for the PO
 | |
|       printData->mShrinkRatio = printData->mPrintObject->mShrinkRatio;
 | |
|     }
 | |
| 
 | |
|     if (printData->mShrinkRatio < 0.998f) {
 | |
|       nsresult rv = ReconstructAndReflow(true);
 | |
|       if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|         return rv;
 | |
|       }
 | |
|       // If the printing was canceled or restarted with different data,
 | |
|       // let's stop doing this printing.
 | |
|       if (NS_WARN_IF(mPrt != printData)) {
 | |
|         return NS_ERROR_FAILURE;
 | |
|       }
 | |
|       didReconstruction = true;
 | |
|     }
 | |
| 
 | |
|     if (MOZ_LOG_TEST(gPrintingLog, LogLevel::Debug)) {
 | |
|       float calcRatio = 0.0f;
 | |
|       if (printData->mPrintDocList.Length() > 1 &&
 | |
|           printData->mPrintObject->mFrameType == eFrameSet) {
 | |
|         nsPrintObject* smallestPO = FindSmallestSTF();
 | |
|         NS_ASSERTION(smallestPO, "There must always be an XMost PO!");
 | |
|         if (smallestPO) {
 | |
|           // Calc the shrinkage based on the entire content area
 | |
|           calcRatio = smallestPO->mShrinkRatio;
 | |
|         }
 | |
|       } else {
 | |
|         // Single document so use the Shrink as calculated for the PO
 | |
|         calcRatio = printData->mPrintObject->mShrinkRatio;
 | |
|       }
 | |
|       PR_PL(
 | |
|           ("*******************************************************************"
 | |
|            "*******\n"));
 | |
|       PR_PL(("STF Ratio is: %8.5f Effective Ratio: %8.5f Diff: %8.5f\n",
 | |
|              printData->mShrinkRatio, calcRatio,
 | |
|              printData->mShrinkRatio - calcRatio));
 | |
|       PR_PL(
 | |
|           ("*******************************************************************"
 | |
|            "*******\n"));
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // If the frames got reconstructed and reflowed the number of pages might
 | |
|   // has changed.
 | |
|   if (didReconstruction) {
 | |
|     FirePrintPreviewUpdateEvent();
 | |
|     // If the printing was canceled or restarted with different data,
 | |
|     // let's stop doing this printing.
 | |
|     if (NS_WARN_IF(mPrt != printData)) {
 | |
|       return NS_ERROR_FAILURE;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   DUMP_DOC_LIST(("\nAfter Reflow------------------------------------------"));
 | |
|   PR_PL(("\n"));
 | |
|   PR_PL(("-------------------------------------------------------\n"));
 | |
|   PR_PL(("\n"));
 | |
| 
 | |
|   CalcNumPrintablePages(printData->mNumPrintablePages);
 | |
| 
 | |
|   PR_PL(("--- Printing %d pages\n", printData->mNumPrintablePages));
 | |
|   DUMP_DOC_TREELAYOUT;
 | |
| 
 | |
|   // Print listener setup...
 | |
|   printData->OnStartPrinting();
 | |
| 
 | |
|   // If the printing was canceled or restarted with different data,
 | |
|   // let's stop doing this printing.
 | |
|   if (NS_WARN_IF(mPrt != printData)) {
 | |
|     return NS_ERROR_FAILURE;
 | |
|   }
 | |
| 
 | |
|   nsAutoString fileNameStr;
 | |
|   // check to see if we are printing to a file
 | |
|   bool isPrintToFile = false;
 | |
|   printData->mPrintSettings->GetPrintToFile(&isPrintToFile);
 | |
|   if (isPrintToFile) {
 | |
|     // On some platforms The BeginDocument needs to know the name of the file.
 | |
|     printData->mPrintSettings->GetToFileName(fileNameStr);
 | |
|   }
 | |
| 
 | |
|   nsAutoString docTitleStr;
 | |
|   nsAutoString docURLStr;
 | |
|   GetDisplayTitleAndURL(
 | |
|       *printData->mPrintObject->mDocument, printData->mPrintSettings,
 | |
|       DocTitleDefault::eDocURLElseFallback, docTitleStr, docURLStr);
 | |
| 
 | |
|   int32_t startPage = 1;
 | |
|   int32_t endPage = printData->mNumPrintablePages;
 | |
| 
 | |
|   nsTArray<int32_t> ranges;
 | |
|   printData->mPrintSettings->GetPageRanges(ranges);
 | |
|   for (size_t i = 0; i < ranges.Length(); i += 2) {
 | |
|     startPage = std::max(1, std::min(startPage, ranges[i]));
 | |
|     endPage = std::min(printData->mNumPrintablePages,
 | |
|                        std::max(endPage, ranges[i + 1]));
 | |
|   }
 | |
| 
 | |
|   nsresult rv = NS_OK;
 | |
|   // BeginDocument may pass back a FAILURE code
 | |
|   // i.e. On Windows, if you are printing to a file and hit "Cancel"
 | |
|   //      to the "File Name" dialog, this comes back as an error
 | |
|   // Don't start printing when regression test are executed
 | |
|   if (mIsDoingPrinting) {
 | |
|     rv = printData->mPrintDC->BeginDocument(docTitleStr, fileNameStr, startPage,
 | |
|                                             endPage);
 | |
|   }
 | |
| 
 | |
|   if (mIsCreatingPrintPreview) {
 | |
|     // Copy docTitleStr and docURLStr to the pageSequenceFrame, to be displayed
 | |
|     // in the header
 | |
|     nsPageSequenceFrame* seqFrame =
 | |
|         printData->mPrintObject->mPresShell->GetPageSequenceFrame();
 | |
|     if (seqFrame) {
 | |
|       seqFrame->StartPrint(printData->mPrintObject->mPresContext,
 | |
|                            printData->mPrintSettings, docTitleStr, docURLStr);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   PR_PL(("****************** Begin Document ************************\n"));
 | |
| 
 | |
|   if (NS_FAILED(rv)) {
 | |
|     NS_WARNING_ASSERTION(rv == NS_ERROR_ABORT,
 | |
|                          "Failed to begin document for printing");
 | |
|     return rv;
 | |
|   }
 | |
| 
 | |
|   // This will print the docshell document
 | |
|   // when it completes asynchronously in the DonePrintingSheets method
 | |
|   // it will check to see if there are more docshells to be printed and
 | |
|   // then PrintDocContent will be called again.
 | |
| 
 | |
|   if (mIsDoingPrinting) {
 | |
|     PrintDocContent(printData->mPrintObject, rv);  // ignore return value
 | |
|   }
 | |
| 
 | |
|   return rv;
 | |
| }
 | |
| 
 | |
| //-------------------------------------------------------
 | |
| // Recursively reflow each sub-doc and then calc
 | |
| // all the frame locations of the sub-docs
 | |
| nsresult nsPrintJob::ReflowDocList(const UniquePtr<nsPrintObject>& aPO,
 | |
|                                    bool aSetPixelScale) {
 | |
|   NS_ENSURE_ARG_POINTER(aPO);
 | |
| 
 | |
|   // Check to see if the subdocument's element has been hidden by the parent
 | |
|   // document
 | |
|   if (aPO->mParent && aPO->mParent->mPresShell) {
 | |
|     nsIFrame* frame =
 | |
|         aPO->mContent ? aPO->mContent->GetPrimaryFrame() : nullptr;
 | |
|     if (!frame || !frame->StyleVisibility()->IsVisible()) {
 | |
|       aPO->EnablePrinting(false);
 | |
|       aPO->mInvisible = true;
 | |
|       return NS_OK;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   UpdateZoomRatio(aPO.get(), aSetPixelScale);
 | |
| 
 | |
|   // Reflow the PO
 | |
|   MOZ_TRY(ReflowPrintObject(aPO));
 | |
| 
 | |
|   for (const UniquePtr<nsPrintObject>& kid : aPO->mKids) {
 | |
|     MOZ_TRY(ReflowDocList(kid, aSetPixelScale));
 | |
|   }
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| void nsPrintJob::FirePrintPreviewUpdateEvent() {
 | |
|   // Dispatch the event only while in PrintPreview. When printing, there is no
 | |
|   // listener bound to this event and therefore no need to dispatch it.
 | |
|   if (mCreatedForPrintPreview && !mIsDoingPrinting) {
 | |
|     nsCOMPtr<nsIContentViewer> cv = do_QueryInterface(mDocViewerPrint);
 | |
|     (new AsyncEventDispatcher(cv->GetDocument(), u"printPreviewUpdate"_ns,
 | |
|                               CanBubble::eYes, ChromeOnlyDispatch::eYes))
 | |
|         ->RunDOMEventWhenSafe();
 | |
|   }
 | |
| }
 | |
| 
 | |
| nsresult nsPrintJob::InitPrintDocConstruction(bool aHandleError) {
 | |
|   // Guarantee that mPrt->mPrintObject won't be deleted.  It's owned by mPrt.
 | |
|   // So, we should grab it with local variable.
 | |
|   RefPtr<nsPrintData> printData = mPrt;
 | |
| 
 | |
|   if (NS_WARN_IF(!printData)) {
 | |
|     return NS_ERROR_FAILURE;
 | |
|   }
 | |
| 
 | |
|   // Attach progressListener to catch network requests.
 | |
|   mDidLoadDataForPrinting = false;
 | |
| 
 | |
|   {
 | |
|     AutoRestore<bool> restore{mDoingInitialReflow};
 | |
|     mDoingInitialReflow = true;
 | |
| 
 | |
|     nsCOMPtr<nsIWebProgress> webProgress =
 | |
|         do_QueryInterface(printData->mPrintObject->mDocShell);
 | |
|     webProgress->AddProgressListener(static_cast<nsIWebProgressListener*>(this),
 | |
|                                      nsIWebProgress::NOTIFY_STATE_REQUEST);
 | |
| 
 | |
|     MOZ_TRY(ReflowDocList(printData->mPrintObject, DoSetPixelScale()));
 | |
| 
 | |
|     FirePrintPreviewUpdateEvent();
 | |
|   }
 | |
| 
 | |
|   MaybeResumePrintAfterResourcesLoaded(aHandleError);
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| bool nsPrintJob::ShouldResumePrint() const {
 | |
|   if (mDoingInitialReflow) {
 | |
|     return false;
 | |
|   }
 | |
|   Document* doc = mPrt->mPrintObject->mDocument;
 | |
|   MOZ_ASSERT(doc);
 | |
|   NS_ENSURE_TRUE(doc, true);
 | |
|   nsCOMPtr<nsILoadGroup> lg = doc->GetDocumentLoadGroup();
 | |
|   NS_ENSURE_TRUE(lg, true);
 | |
|   bool pending = false;
 | |
|   nsresult rv = lg->IsPending(&pending);
 | |
|   NS_ENSURE_SUCCESS(rv, true);
 | |
|   return !pending;
 | |
| }
 | |
| 
 | |
| nsresult nsPrintJob::MaybeResumePrintAfterResourcesLoaded(
 | |
|     bool aCleanupOnError) {
 | |
|   if (!ShouldResumePrint()) {
 | |
|     mDidLoadDataForPrinting = true;
 | |
|     return NS_OK;
 | |
|   }
 | |
|   // If Destroy() has already been called, mPtr is nullptr.  Then, the instance
 | |
|   // needs to do nothing anymore in this method.
 | |
|   // Note: it shouldn't be possible for mPrt->mPrintObject to be null; we
 | |
|   // just check it for good measure, as we check its owner.
 | |
|   // Note: it shouldn't be possible for mPrt->mPrintObject->mDocShell to be
 | |
|   // null; we just check it for good measure, as we check its owner.
 | |
|   if (!mPrt || NS_WARN_IF(!mPrt->mPrintObject) ||
 | |
|       NS_WARN_IF(!mPrt->mPrintObject->mDocShell)) {
 | |
|     return NS_ERROR_FAILURE;
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<nsIWebProgress> webProgress =
 | |
|       do_QueryInterface(mPrt->mPrintObject->mDocShell);
 | |
| 
 | |
|   webProgress->RemoveProgressListener(
 | |
|       static_cast<nsIWebProgressListener*>(this));
 | |
| 
 | |
|   nsresult rv;
 | |
|   if (mIsDoingPrinting) {
 | |
|     rv = DocumentReadyForPrinting();
 | |
|   } else {
 | |
|     rv = FinishPrintPreview();
 | |
|   }
 | |
| 
 | |
|   /* cleaup on failure + notify user */
 | |
|   if (aCleanupOnError && NS_FAILED(rv)) {
 | |
|     NS_WARNING_ASSERTION(rv == NS_ERROR_ABORT,
 | |
|                          "nsPrintJob::ResumePrintAfterResourcesLoaded failed");
 | |
|     CleanupOnFailure(rv, !mIsDoingPrinting);
 | |
|   }
 | |
| 
 | |
|   return rv;
 | |
| }
 | |
| 
 | |
| ////////////////////////////////////////////////////////////////////////////////
 | |
| // nsIWebProgressListener
 | |
| 
 | |
| MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP
 | |
| nsPrintJob::OnStateChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
 | |
|                           uint32_t aStateFlags, nsresult aStatus) {
 | |
|   if (aStateFlags & STATE_STOP) {
 | |
|     // If all resources are loaded, then finish and reflow.
 | |
|     MaybeResumePrintAfterResourcesLoaded(/* aCleanupOnError */ true);
 | |
|   }
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsPrintJob::OnProgressChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
 | |
|                              int32_t aCurSelfProgress, int32_t aMaxSelfProgress,
 | |
|                              int32_t aCurTotalProgress,
 | |
|                              int32_t aMaxTotalProgress) {
 | |
|   MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsPrintJob::OnLocationChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
 | |
|                              nsIURI* aLocation, uint32_t aFlags) {
 | |
|   MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsPrintJob::OnStatusChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
 | |
|                            nsresult aStatus, const char16_t* aMessage) {
 | |
|   MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsPrintJob::OnSecurityChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
 | |
|                              uint32_t aState) {
 | |
|   MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsPrintJob::OnContentBlockingEvent(nsIWebProgress* aWebProgress,
 | |
|                                    nsIRequest* aRequest, uint32_t aEvent) {
 | |
|   MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| //-------------------------------------------------------
 | |
| 
 | |
| void nsPrintJob::UpdateZoomRatio(nsPrintObject* aPO, bool aSetPixelScale) {
 | |
|   // Here is where we set the shrinkage value into the DC
 | |
|   // and this is what actually makes it shrink
 | |
|   if (aSetPixelScale && aPO->mFrameType != eIFrame) {
 | |
|     // Round down
 | |
|     aPO->mZoomRatio = mPrt->mPrintSettings->GetPrintSelectionOnly()
 | |
|                           ? aPO->mShrinkRatio - 0.005f
 | |
|                           : mPrt->mShrinkRatio - 0.005f;
 | |
|   } else if (!mPrt->mShrinkToFit) {
 | |
|     double scaling;
 | |
|     mPrt->mPrintSettings->GetScaling(&scaling);
 | |
|     aPO->mZoomRatio = float(scaling);
 | |
|   }
 | |
| }
 | |
| 
 | |
| nsresult nsPrintJob::UpdateSelectionAndShrinkPrintObject(
 | |
|     nsPrintObject* aPO, bool aDocumentIsTopLevel) {
 | |
|   PresShell* displayPresShell = aPO->mDocShell->GetPresShell();
 | |
|   // Transfer Selection Ranges to the new Print PresShell
 | |
|   RefPtr<Selection> selection, selectionPS;
 | |
|   // It's okay if there is no display shell, just skip copying the selection
 | |
|   if (displayPresShell) {
 | |
|     selection = displayPresShell->GetCurrentSelection(SelectionType::eNormal);
 | |
|   }
 | |
|   selectionPS = aPO->mPresShell->GetCurrentSelection(SelectionType::eNormal);
 | |
| 
 | |
|   // Reset all existing selection ranges that might have been added by calling
 | |
|   // this function before.
 | |
|   if (selectionPS) {
 | |
|     selectionPS->RemoveAllRanges(IgnoreErrors());
 | |
|   }
 | |
|   if (selection && selectionPS) {
 | |
|     int32_t cnt = selection->RangeCount();
 | |
|     int32_t inx;
 | |
|     for (inx = 0; inx < cnt; ++inx) {
 | |
|       const RefPtr<nsRange> range{selection->GetRangeAt(inx)};
 | |
|       selectionPS->AddRangeAndSelectFramesAndNotifyListeners(*range,
 | |
|                                                              IgnoreErrors());
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // If we are trying to shrink the contents to fit on the page
 | |
|   // we must first locate the "pageContent" frame
 | |
|   // Then we walk the frame tree and look for the "xmost" frame
 | |
|   // this is the frame where the right-hand side of the frame extends
 | |
|   // the furthest
 | |
|   if (mPrt->mShrinkToFit && aDocumentIsTopLevel) {
 | |
|     nsPageSequenceFrame* pageSeqFrame = aPO->mPresShell->GetPageSequenceFrame();
 | |
|     NS_ENSURE_STATE(pageSeqFrame);
 | |
|     aPO->mShrinkRatio = pageSeqFrame->GetSTFPercent();
 | |
|     // Limit the shrink-to-fit scaling for some text-ish type of documents.
 | |
|     nsAutoString contentType;
 | |
|     aPO->mPresShell->GetDocument()->GetContentType(contentType);
 | |
|     if (contentType.EqualsLiteral("application/xhtml+xml") ||
 | |
|         StringBeginsWith(contentType, u"text/"_ns)) {
 | |
|       int32_t limitPercent =
 | |
|           Preferences::GetInt("print.shrink-to-fit.scale-limit-percent", 20);
 | |
|       limitPercent = std::max(0, limitPercent);
 | |
|       limitPercent = std::min(100, limitPercent);
 | |
|       float minShrinkRatio = float(limitPercent) / 100;
 | |
|       aPO->mShrinkRatio = std::max(aPO->mShrinkRatio, minShrinkRatio);
 | |
|     }
 | |
|   }
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| bool nsPrintJob::DoSetPixelScale() {
 | |
|   // This is an Optimization
 | |
|   // If we are in PP then we already know all the shrinkage information
 | |
|   // so just transfer it to the PrintData and we will skip the extra shrinkage
 | |
|   // reflow
 | |
|   //
 | |
|   // doSetPixelScale tells Reflow whether to set the shrinkage value into the DC
 | |
|   // The first time we do not want to do this, the second time through we do
 | |
|   bool doSetPixelScale = false;
 | |
|   bool ppIsShrinkToFit = mPrtPreview && mPrtPreview->mShrinkToFit;
 | |
|   if (ppIsShrinkToFit) {
 | |
|     mPrt->mShrinkRatio = mPrtPreview->mShrinkRatio;
 | |
|     doSetPixelScale = true;
 | |
|   }
 | |
|   return doSetPixelScale;
 | |
| }
 | |
| 
 | |
| nsView* nsPrintJob::GetParentViewForRoot() {
 | |
|   if (mIsCreatingPrintPreview) {
 | |
|     if (nsCOMPtr<nsIContentViewer> cv = do_QueryInterface(mDocViewerPrint)) {
 | |
|       return cv->FindContainerView();
 | |
|     }
 | |
|   }
 | |
|   return nullptr;
 | |
| }
 | |
| 
 | |
| nsresult nsPrintJob::SetRootView(nsPrintObject* aPO, bool& doReturn,
 | |
|                                  bool& documentIsTopLevel, nsSize& adjSize) {
 | |
|   bool canCreateScrollbars = true;
 | |
| 
 | |
|   nsView* rootView;
 | |
|   nsView* parentView = nullptr;
 | |
| 
 | |
|   doReturn = false;
 | |
| 
 | |
|   if (aPO->mParent && aPO->mParent->PrintingIsEnabled()) {
 | |
|     nsIFrame* frame =
 | |
|         aPO->mContent ? aPO->mContent->GetPrimaryFrame() : nullptr;
 | |
|     // Without a frame, this document can't be displayed; therefore, there is no
 | |
|     // point to reflowing it
 | |
|     if (!frame) {
 | |
|       aPO->EnablePrinting(false);
 | |
|       doReturn = true;
 | |
|       return NS_OK;
 | |
|     }
 | |
| 
 | |
|     // XXX If printing supported printing document hierarchies with non-constant
 | |
|     // zoom this would be wrong as we use the same mPrt->mPrintDC for all
 | |
|     // subdocuments.
 | |
|     adjSize = frame->GetContentRect().Size();
 | |
|     documentIsTopLevel = false;
 | |
|     // presshell exists because parent is printable
 | |
| 
 | |
|     // the top nsPrintObject's widget will always have scrollbars
 | |
|     if (frame && frame->IsSubDocumentFrame()) {
 | |
|       nsView* view = frame->GetView();
 | |
|       NS_ENSURE_TRUE(view, NS_ERROR_FAILURE);
 | |
|       view = view->GetFirstChild();
 | |
|       NS_ENSURE_TRUE(view, NS_ERROR_FAILURE);
 | |
|       parentView = view;
 | |
|       canCreateScrollbars = false;
 | |
|     }
 | |
|   } else {
 | |
|     nscoord pageWidth, pageHeight;
 | |
|     mPrt->mPrintDC->GetDeviceSurfaceDimensions(pageWidth, pageHeight);
 | |
|     adjSize = nsSize(pageWidth, pageHeight);
 | |
|     documentIsTopLevel = true;
 | |
|     parentView = GetParentViewForRoot();
 | |
|   }
 | |
| 
 | |
|   if (aPO->mViewManager->GetRootView()) {
 | |
|     // Reuse the root view that is already on the root frame.
 | |
|     rootView = aPO->mViewManager->GetRootView();
 | |
|     // Remove it from its existing parent if necessary
 | |
|     aPO->mViewManager->RemoveChild(rootView);
 | |
|     rootView->SetParent(parentView);
 | |
|   } else {
 | |
|     // Create a child window of the parent that is our "root view/window"
 | |
|     nsRect tbounds = nsRect(nsPoint(0, 0), adjSize);
 | |
|     rootView = aPO->mViewManager->CreateView(tbounds, parentView);
 | |
|     NS_ENSURE_TRUE(rootView, NS_ERROR_OUT_OF_MEMORY);
 | |
|   }
 | |
| 
 | |
|   if (mIsCreatingPrintPreview && documentIsTopLevel) {
 | |
|     aPO->mPresContext->SetPaginatedScrolling(canCreateScrollbars);
 | |
|   }
 | |
| 
 | |
|   // Setup hierarchical relationship in view manager
 | |
|   aPO->mViewManager->SetRootView(rootView);
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| // Reflow a nsPrintObject
 | |
| nsresult nsPrintJob::ReflowPrintObject(const UniquePtr<nsPrintObject>& aPO) {
 | |
|   NS_ENSURE_STATE(aPO);
 | |
| 
 | |
|   if (!aPO->PrintingIsEnabled()) {
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   NS_ASSERTION(!aPO->mPresContext, "Recreating prescontext");
 | |
| 
 | |
|   // Guarantee that mPrt and the objects it owns won't be deleted in this method
 | |
|   // because it might be cleared if other modules called from here may fire
 | |
|   // events, notifying observers and/or listeners.
 | |
|   RefPtr<nsPrintData> printData = mPrt;
 | |
| 
 | |
|   // create the PresContext
 | |
|   nsPresContext::nsPresContextType type =
 | |
|       mIsCreatingPrintPreview ? nsPresContext::eContext_PrintPreview
 | |
|                               : nsPresContext::eContext_Print;
 | |
|   const bool shouldBeRoot =
 | |
|       (!aPO->mParent || !aPO->mParent->PrintingIsEnabled()) &&
 | |
|       !GetParentViewForRoot();
 | |
|   aPO->mPresContext = shouldBeRoot ? new nsRootPresContext(aPO->mDocument, type)
 | |
|                                    : new nsPresContext(aPO->mDocument, type);
 | |
|   aPO->mPresContext->SetPrintSettings(printData->mPrintSettings);
 | |
| 
 | |
|   // init it with the DC
 | |
|   MOZ_TRY(aPO->mPresContext->Init(printData->mPrintDC));
 | |
| 
 | |
|   aPO->mViewManager = new nsViewManager();
 | |
| 
 | |
|   MOZ_TRY(aPO->mViewManager->Init(printData->mPrintDC));
 | |
| 
 | |
|   aPO->mPresShell =
 | |
|       aPO->mDocument->CreatePresShell(aPO->mPresContext, aPO->mViewManager);
 | |
|   if (!aPO->mPresShell) {
 | |
|     return NS_ERROR_FAILURE;
 | |
|   }
 | |
| 
 | |
|   // If we're printing selection then remove the nonselected nodes from our
 | |
|   // cloned document.
 | |
|   if (printData->mPrintSettings->GetPrintSelectionOnly()) {
 | |
|     // If we fail to remove the nodes then we should fail to print, because if
 | |
|     // the user was trying to print a small selection from a large document,
 | |
|     // sending the whole document to a real printer would be very frustrating.
 | |
|     MOZ_TRY(DeleteNonSelectedNodes(*aPO->mDocument));
 | |
|   }
 | |
| 
 | |
|   bool doReturn = false;
 | |
|   bool documentIsTopLevel = false;
 | |
|   nsSize adjSize;
 | |
| 
 | |
|   nsresult rv = SetRootView(aPO.get(), doReturn, documentIsTopLevel, adjSize);
 | |
| 
 | |
|   if (NS_FAILED(rv) || doReturn) {
 | |
|     return rv;
 | |
|   }
 | |
| 
 | |
|   PR_PL(("In DV::ReflowPrintObject PO: %p pS: %p (%9s) Setting w,h to %d,%d\n",
 | |
|          aPO.get(), aPO->mPresShell.get(), gFrameTypesStr[aPO->mFrameType],
 | |
|          adjSize.width, adjSize.height));
 | |
| 
 | |
|   aPO->mPresShell->BeginObservingDocument();
 | |
| 
 | |
|   // Here, we inform nsPresContext of the page size. Note that 'adjSize' is
 | |
|   // *usually* the page size, but we need to check. Strictly speaking, adjSize
 | |
|   // is the *device output size*, which is really the dimensions of a "sheet"
 | |
|   // rather than a "page" (an important distinction in an N-pages-per-sheet
 | |
|   // scenario). For some pages-per-sheet values, the pages are orthogonal to
 | |
|   // the sheet; we adjust for that here by swapping the width with the height.
 | |
|   nsSize pageSize = adjSize;
 | |
|   if (printData->mPrintSettings->HasOrthogonalSheetsAndPages()) {
 | |
|     std::swap(pageSize.width, pageSize.height);
 | |
|   }
 | |
| 
 | |
|   aPO->mPresContext->SetPageSize(pageSize);
 | |
| 
 | |
|   int32_t p2a = aPO->mPresContext->DeviceContext()->AppUnitsPerDevPixel();
 | |
|   if (documentIsTopLevel && mIsCreatingPrintPreview) {
 | |
|     if (nsCOMPtr<nsIContentViewer> cv = do_QueryInterface(mDocViewerPrint)) {
 | |
|       // If we're print-previewing and the top level document, use the bounds
 | |
|       // from our doc viewer. Page bounds is not what we want.
 | |
|       nsIntRect bounds;
 | |
|       cv->GetBounds(bounds);
 | |
|       adjSize = nsSize(bounds.width * p2a, bounds.height * p2a);
 | |
|     }
 | |
|   }
 | |
|   aPO->mPresContext->SetVisibleArea(nsRect(nsPoint(), adjSize));
 | |
|   aPO->mPresContext->SetIsRootPaginatedDocument(documentIsTopLevel);
 | |
|   aPO->mPresContext->SetPageScale(aPO->mZoomRatio);
 | |
|   // Calculate scale factor from printer to screen
 | |
|   float printDPI = float(AppUnitsPerCSSInch()) / float(p2a);
 | |
|   aPO->mPresContext->SetPrintPreviewScale(mScreenDPI / printDPI);
 | |
| 
 | |
|   if (mIsCreatingPrintPreview && documentIsTopLevel) {
 | |
|     mDocViewerPrint->SetPrintPreviewPresentation(
 | |
|         aPO->mViewManager, aPO->mPresContext, aPO->mPresShell.get());
 | |
|   }
 | |
| 
 | |
|   MOZ_TRY(aPO->mPresShell->Initialize());
 | |
|   NS_ASSERTION(aPO->mPresShell, "Presshell should still be here");
 | |
| 
 | |
|   // Process the reflow event Initialize posted
 | |
|   RefPtr<PresShell> presShell = aPO->mPresShell;
 | |
|   presShell->FlushPendingNotifications(FlushType::Layout);
 | |
| 
 | |
|   MOZ_TRY(UpdateSelectionAndShrinkPrintObject(aPO.get(), documentIsTopLevel));
 | |
| 
 | |
| #ifdef EXTENDED_DEBUG_PRINTING
 | |
|   if (MOZ_LOG_TEST(gPrintingLog, DUMP_LAYOUT_LEVEL)) {
 | |
|     nsAutoCString docStr;
 | |
|     nsAutoCString urlStr;
 | |
|     GetDocTitleAndURL(aPO, docStr, urlStr);
 | |
|     char filename[256];
 | |
|     sprintf(filename, "print_dump_%d.txt", gDumpFileNameCnt++);
 | |
|     // Dump all the frames and view to a a file
 | |
|     FILE* fd = fopen(filename, "w");
 | |
|     if (fd) {
 | |
|       nsIFrame* theRootFrame = aPO->mPresShell->GetRootFrame();
 | |
|       fprintf(fd, "Title: %s\n", docStr.get());
 | |
|       fprintf(fd, "URL:   %s\n", urlStr.get());
 | |
|       fprintf(fd, "--------------- Frames ----------------\n");
 | |
|       // RefPtr<gfxContext> renderingContext =
 | |
|       //  printData->mPrintDocDC->CreateRenderingContext();
 | |
|       RootFrameList(aPO->mPresContext, fd, 0);
 | |
|       // DumpFrames(fd, aPO->mPresContext, renderingContext, theRootFrame, 0);
 | |
|       fprintf(fd, "---------------------------------------\n\n");
 | |
|       fprintf(fd, "--------------- Views From Root Frame----------------\n");
 | |
|       nsView* v = theRootFrame->GetView();
 | |
|       if (v) {
 | |
|         v->List(fd);
 | |
|       } else {
 | |
|         printf("View is null!\n");
 | |
|       }
 | |
|       if (aPO->mDocShell) {
 | |
|         fprintf(fd, "--------------- All Views ----------------\n");
 | |
|         DumpViews(aPO->mDocShell, fd);
 | |
|         fprintf(fd, "---------------------------------------\n\n");
 | |
|       }
 | |
|       fclose(fd);
 | |
|     }
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| //-------------------------------------------------------
 | |
| // Figure out how many documents and how many total pages we are printing
 | |
| void nsPrintJob::CalcNumPrintablePages(int32_t& aNumPages) {
 | |
|   aNumPages = 0;
 | |
|   // Count the number of printable documents
 | |
|   // and printable pages
 | |
|   for (uint32_t i = 0; i < mPrt->mPrintDocList.Length(); i++) {
 | |
|     nsPrintObject* po = mPrt->mPrintDocList.ElementAt(i);
 | |
|     NS_ASSERTION(po, "nsPrintObject can't be null!");
 | |
|     // Note: The po->mPresContext null-check below is necessary, because it's
 | |
|     // possible po->mPresContext might never have been set.  (e.g., if
 | |
|     // PrintingIsEnabled() returns false, ReflowPrintObject bails before setting
 | |
|     // mPresContext)
 | |
|     if (po->mPresContext && po->mPresContext->IsRootPaginatedDocument()) {
 | |
|       nsPageSequenceFrame* seqFrame = po->mPresShell->GetPageSequenceFrame();
 | |
|       if (seqFrame) {
 | |
|         aNumPages += seqFrame->PrincipalChildList().GetLength();
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| //-----------------------------------------------------------------
 | |
| //-- Done: Reflow Methods
 | |
| //-----------------------------------------------------------------
 | |
| 
 | |
| //-----------------------------------------------------------------
 | |
| //-- Section: Printing Methods
 | |
| //-----------------------------------------------------------------
 | |
| 
 | |
| //-------------------------------------------------------
 | |
| // Called for each DocShell that needs to be printed
 | |
| bool nsPrintJob::PrintDocContent(const UniquePtr<nsPrintObject>& aPO,
 | |
|                                  nsresult& aStatus) {
 | |
|   NS_ASSERTION(aPO, "Pointer is null!");
 | |
|   aStatus = NS_OK;
 | |
| 
 | |
|   if (!aPO->mHasBeenPrinted && aPO->PrintingIsEnabled()) {
 | |
|     aStatus = DoPrint(aPO);
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   // If |aPO->mHasBeenPrinted| is true,
 | |
|   // the kids frames are already processed in |PrintPage|.
 | |
|   // XXX This should be removed. Since bug 1552785 it has no longer been
 | |
|   // possible for us to have to print multiple subdocuments consecutively.
 | |
|   if (!aPO->mHasBeenPrinted && !aPO->mInvisible) {
 | |
|     for (const UniquePtr<nsPrintObject>& po : aPO->mKids) {
 | |
|       bool printed = PrintDocContent(po, aStatus);
 | |
|       if (printed || NS_FAILED(aStatus)) {
 | |
|         return true;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| // A helper struct to aid with DeleteNonSelectedNodes.
 | |
| struct MOZ_STACK_CLASS SelectionRangeState {
 | |
|   explicit SelectionRangeState(RefPtr<Selection> aSelection)
 | |
|       : mSelection(std::move(aSelection)) {
 | |
|     MOZ_ASSERT(mSelection);
 | |
|     MOZ_ASSERT(!mSelection->RangeCount());
 | |
|   }
 | |
| 
 | |
|   // Selects all the nodes that are _not_ included in a given set of ranges.
 | |
|   MOZ_CAN_RUN_SCRIPT void SelectComplementOf(Span<const RefPtr<nsRange>>);
 | |
|   // Removes the selected ranges from the document.
 | |
|   MOZ_CAN_RUN_SCRIPT void RemoveSelectionFromDocument();
 | |
| 
 | |
|  private:
 | |
|   struct Position {
 | |
|     nsINode* mNode;
 | |
|     uint32_t mOffset;
 | |
|   };
 | |
| 
 | |
|   MOZ_CAN_RUN_SCRIPT void SelectRange(nsRange*);
 | |
|   MOZ_CAN_RUN_SCRIPT void SelectNodesExcept(const Position& aStart,
 | |
|                                             const Position& aEnd);
 | |
|   MOZ_CAN_RUN_SCRIPT void SelectNodesExceptInSubtree(const Position& aStart,
 | |
|                                                      const Position& aEnd);
 | |
| 
 | |
|   // A map from subtree root (document or shadow root) to the start position of
 | |
|   // the non-selected content (so far).
 | |
|   nsTHashMap<nsPtrHashKey<nsINode>, Position> mPositions;
 | |
| 
 | |
|   // The selection we're adding the ranges to.
 | |
|   const RefPtr<Selection> mSelection;
 | |
| };
 | |
| 
 | |
| void SelectionRangeState::SelectComplementOf(
 | |
|     Span<const RefPtr<nsRange>> aRanges) {
 | |
|   for (const auto& range : aRanges) {
 | |
|     auto start = Position{range->GetStartContainer(), range->StartOffset()};
 | |
|     auto end = Position{range->GetEndContainer(), range->EndOffset()};
 | |
|     SelectNodesExcept(start, end);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void SelectionRangeState::SelectRange(nsRange* aRange) {
 | |
|   if (aRange && !aRange->Collapsed()) {
 | |
|     mSelection->AddRangeAndSelectFramesAndNotifyListeners(*aRange,
 | |
|                                                           IgnoreErrors());
 | |
|   }
 | |
| }
 | |
| 
 | |
| void SelectionRangeState::SelectNodesExcept(const Position& aStart,
 | |
|                                             const Position& aEnd) {
 | |
|   SelectNodesExceptInSubtree(aStart, aEnd);
 | |
|   if (auto* shadow = ShadowRoot::FromNode(aStart.mNode->SubtreeRoot())) {
 | |
|     auto* host = shadow->Host();
 | |
|     SelectNodesExcept(Position{host, 0}, Position{host, host->GetChildCount()});
 | |
|   } else {
 | |
|     MOZ_ASSERT(aStart.mNode->IsInUncomposedDoc());
 | |
|   }
 | |
| }
 | |
| 
 | |
| void SelectionRangeState::SelectNodesExceptInSubtree(const Position& aStart,
 | |
|                                                      const Position& aEnd) {
 | |
|   static constexpr auto kEllipsis = u"\x2026"_ns;
 | |
| 
 | |
|   nsINode* root = aStart.mNode->SubtreeRoot();
 | |
|   auto& start =
 | |
|       mPositions.WithEntryHandle(root, [&](auto&& entry) -> Position& {
 | |
|         return entry.OrInsertWith([&] { return Position{root, 0}; });
 | |
|       });
 | |
| 
 | |
|   bool ellipsizedStart = false;
 | |
|   if (auto* text = Text::FromNode(aStart.mNode)) {
 | |
|     if (start.mNode != text && aStart.mOffset &&
 | |
|         aStart.mOffset < text->Length()) {
 | |
|       text->InsertData(aStart.mOffset, kEllipsis, IgnoreErrors());
 | |
|       ellipsizedStart = true;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   RefPtr<nsRange> range = nsRange::Create(
 | |
|       start.mNode, start.mOffset, aStart.mNode, aStart.mOffset, IgnoreErrors());
 | |
|   SelectRange(range);
 | |
| 
 | |
|   start = aEnd;
 | |
| 
 | |
|   // If we added an ellipsis at the start and the end position was relative to
 | |
|   // the same node account for it here.
 | |
|   if (ellipsizedStart && aStart.mNode == aEnd.mNode) {
 | |
|     start.mOffset += kEllipsis.Length();
 | |
|   }
 | |
| 
 | |
|   // If the end is mid text then add an ellipsis.
 | |
|   if (auto* text = Text::FromNode(start.mNode)) {
 | |
|     if (start.mOffset && start.mOffset < text->Length()) {
 | |
|       text->InsertData(start.mOffset, kEllipsis, IgnoreErrors());
 | |
|       start.mOffset += kEllipsis.Length();
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void SelectionRangeState::RemoveSelectionFromDocument() {
 | |
|   for (auto& entry : mPositions) {
 | |
|     const Position& pos = entry.GetData();
 | |
|     nsINode* root = entry.GetKey();
 | |
|     RefPtr<nsRange> range = nsRange::Create(
 | |
|         pos.mNode, pos.mOffset, root, root->GetChildCount(), IgnoreErrors());
 | |
|     SelectRange(range);
 | |
|   }
 | |
|   mSelection->DeleteFromDocument(IgnoreErrors());
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Builds the complement set of ranges and adds those to the selection.
 | |
|  * Deletes all of the nodes contained in the complement set of ranges
 | |
|  * leaving behind only nodes that were originally selected.
 | |
|  * Adds ellipses to a selected node's text if text is truncated by a range.
 | |
|  * This is used to implement the "Print Selection Only" user interface option.
 | |
|  */
 | |
| MOZ_CAN_RUN_SCRIPT_BOUNDARY static nsresult DeleteNonSelectedNodes(
 | |
|     Document& aDoc) {
 | |
|   MOZ_ASSERT(aDoc.IsStaticDocument());
 | |
|   const auto* printRanges = static_cast<nsTArray<RefPtr<nsRange>>*>(
 | |
|       aDoc.GetProperty(nsGkAtoms::printselectionranges));
 | |
|   if (!printRanges) {
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   PresShell* presShell = aDoc.GetPresShell();
 | |
|   NS_ENSURE_STATE(presShell);
 | |
|   RefPtr<Selection> selection =
 | |
|       presShell->GetCurrentSelection(SelectionType::eNormal);
 | |
|   NS_ENSURE_STATE(selection);
 | |
| 
 | |
|   SelectionRangeState state(std::move(selection));
 | |
|   state.SelectComplementOf(*printRanges);
 | |
|   state.RemoveSelectionFromDocument();
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| //-------------------------------------------------------
 | |
| nsresult nsPrintJob::DoPrint(const UniquePtr<nsPrintObject>& aPO) {
 | |
|   PR_PL(("\n"));
 | |
|   PR_PL(("**************************** %s ****************************\n",
 | |
|          gFrameTypesStr[aPO->mFrameType]));
 | |
|   PR_PL(("****** In DV::DoPrint   PO: %p \n", aPO.get()));
 | |
| 
 | |
|   PresShell* poPresShell = aPO->mPresShell;
 | |
|   nsPresContext* poPresContext = aPO->mPresContext;
 | |
| 
 | |
|   NS_ASSERTION(poPresContext, "PrintObject has not been reflowed");
 | |
|   NS_ASSERTION(poPresContext->Type() != nsPresContext::eContext_PrintPreview,
 | |
|                "How did this context end up here?");
 | |
| 
 | |
|   // Guarantee that mPrt and the objects it owns won't be deleted in this method
 | |
|   // because it might be cleared if other modules called from here may fire
 | |
|   // events, notifying observers and/or listeners.
 | |
|   RefPtr<nsPrintData> printData = mPrt;
 | |
| 
 | |
|   if (printData->mPrintProgressParams) {
 | |
|     SetURLAndTitleOnProgressParams(aPO, printData->mPrintProgressParams);
 | |
|   }
 | |
| 
 | |
|   {
 | |
|     // Ask the page sequence frame to print all the pages
 | |
|     nsPageSequenceFrame* seqFrame = poPresShell->GetPageSequenceFrame();
 | |
|     MOZ_ASSERT(seqFrame, "no page sequence frame");
 | |
| 
 | |
|     // We are done preparing for printing, so we can turn this off
 | |
|     printData->mPreparingForPrint = false;
 | |
| 
 | |
| #ifdef EXTENDED_DEBUG_PRINTING
 | |
|     nsIFrame* rootFrame = poPresShell->GetRootFrame();
 | |
|     if (aPO->PrintingIsEnabled()) {
 | |
|       nsAutoCString docStr;
 | |
|       nsAutoCString urlStr;
 | |
|       GetDocTitleAndURL(aPO, docStr, urlStr);
 | |
|       DumpLayoutData(docStr.get(), urlStr.get(), poPresContext,
 | |
|                      printData->mPrintDC, rootFrame, aPO->mDocShell, nullptr);
 | |
|     }
 | |
| #endif
 | |
| 
 | |
|     if (!printData->mPrintSettings) {
 | |
|       // not sure what to do here!
 | |
|       SetIsPrinting(false);
 | |
|       return NS_ERROR_FAILURE;
 | |
|     }
 | |
| 
 | |
|     nsAutoString docTitleStr;
 | |
|     nsAutoString docURLStr;
 | |
|     GetDisplayTitleAndURL(*aPO->mDocument, mPrt->mPrintSettings,
 | |
|                           DocTitleDefault::eFallback, docTitleStr, docURLStr);
 | |
| 
 | |
|     if (!seqFrame) {
 | |
|       SetIsPrinting(false);
 | |
|       return NS_ERROR_FAILURE;
 | |
|     }
 | |
| 
 | |
|     // For telemetry, get paper size being used; convert the dimensions to
 | |
|     // points and ensure they reflect portrait orientation.
 | |
|     nsIPrintSettings* settings = printData->mPrintSettings;
 | |
|     double paperWidth, paperHeight;
 | |
|     settings->GetPaperWidth(&paperWidth);
 | |
|     settings->GetPaperHeight(&paperHeight);
 | |
|     int16_t sizeUnit;
 | |
|     settings->GetPaperSizeUnit(&sizeUnit);
 | |
|     switch (sizeUnit) {
 | |
|       case nsIPrintSettings::kPaperSizeInches:
 | |
|         paperWidth *= 72.0;
 | |
|         paperHeight *= 72.0;
 | |
|         break;
 | |
|       case nsIPrintSettings::kPaperSizeMillimeters:
 | |
|         paperWidth *= 72.0 / 25.4;
 | |
|         paperHeight *= 72.0 / 25.4;
 | |
|         break;
 | |
|       default:
 | |
|         MOZ_ASSERT_UNREACHABLE("unknown paper size unit");
 | |
|         break;
 | |
|     }
 | |
|     if (paperWidth > paperHeight) {
 | |
|       std::swap(paperWidth, paperHeight);
 | |
|     }
 | |
|     // Use the paper size to build a Telemetry Scalar key.
 | |
|     nsString key;
 | |
|     key.AppendInt(int32_t(NS_round(paperWidth)));
 | |
|     key.Append(u"x");
 | |
|     key.AppendInt(int32_t(NS_round(paperHeight)));
 | |
|     Telemetry::ScalarAdd(Telemetry::ScalarID::PRINTING_PAPER_SIZE, key, 1);
 | |
| 
 | |
|     mPageSeqFrame = seqFrame;
 | |
|     seqFrame->StartPrint(poPresContext, settings, docTitleStr, docURLStr);
 | |
| 
 | |
|     // Schedule Page to Print
 | |
|     PR_PL(("Scheduling Print of PO: %p (%s) \n", aPO.get(),
 | |
|            gFrameTypesStr[aPO->mFrameType]));
 | |
|     StartPagePrintTimer(aPO);
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| //---------------------------------------------------------------------
 | |
| void nsPrintJob::SetURLAndTitleOnProgressParams(
 | |
|     const UniquePtr<nsPrintObject>& aPO, nsIPrintProgressParams* aParams) {
 | |
|   NS_ASSERTION(aPO, "Must have valid nsPrintObject");
 | |
|   NS_ASSERTION(aParams, "Must have valid nsIPrintProgressParams");
 | |
| 
 | |
|   if (!aPO || !aPO->mDocShell || !aParams) {
 | |
|     return;
 | |
|   }
 | |
|   const uint32_t kTitleLength = 64;
 | |
| 
 | |
|   nsAutoString docTitleStr;
 | |
|   nsAutoString docURLStr;
 | |
|   GetDisplayTitleAndURL(*aPO->mDocument, mPrt->mPrintSettings,
 | |
|                         DocTitleDefault::eDocURLElseFallback, docTitleStr,
 | |
|                         docURLStr);
 | |
| 
 | |
|   // Make sure the Titles & URLS don't get too long for the progress dialog
 | |
|   EllipseLongString(docTitleStr, kTitleLength, false);
 | |
|   EllipseLongString(docURLStr, kTitleLength, true);
 | |
| 
 | |
|   aParams->SetDocTitle(docTitleStr);
 | |
|   aParams->SetDocURL(docURLStr);
 | |
| }
 | |
| 
 | |
| //---------------------------------------------------------------------
 | |
| void nsPrintJob::EllipseLongString(nsAString& aStr, const uint32_t aLen,
 | |
|                                    bool aDoFront) {
 | |
|   // Make sure the URLS don't get too long for the progress dialog
 | |
|   if (aLen >= 3 && aStr.Length() > aLen) {
 | |
|     if (aDoFront) {
 | |
|       nsAutoString newStr;
 | |
|       newStr.AppendLiteral("...");
 | |
|       newStr += Substring(aStr, aStr.Length() - (aLen - 3), aLen - 3);
 | |
|       aStr = newStr;
 | |
|     } else {
 | |
|       aStr.SetLength(aLen - 3);
 | |
|       aStr.AppendLiteral("...");
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| //-------------------------------------------------------
 | |
| bool nsPrintJob::PrePrintSheet() {
 | |
|   NS_ASSERTION(mPageSeqFrame.IsAlive(), "mPageSeqFrame is not alive!");
 | |
|   NS_ASSERTION(mPrt, "mPrt is null!");
 | |
| 
 | |
|   // Although these should NEVER be nullptr
 | |
|   // This is added insurance, to make sure we don't crash in optimized builds
 | |
|   if (!mPrt || !mPageSeqFrame.IsAlive()) {
 | |
|     return true;  // means we are done preparing the sheet.
 | |
|   }
 | |
| 
 | |
|   // Guarantee that mPrt won't be deleted during a call of
 | |
|   // FirePrintingErrorEvent().
 | |
|   RefPtr<nsPrintData> printData = mPrt;
 | |
| 
 | |
|   // Check setting to see if someone request it be cancelled
 | |
|   bool isCancelled = false;
 | |
|   printData->mPrintSettings->GetIsCancelled(&isCancelled);
 | |
|   if (isCancelled) return true;
 | |
| 
 | |
|   // Ask mPageSeqFrame if the sheet is ready to be printed.
 | |
|   // If the sheet doesn't get printed at all, the |done| will be |true|.
 | |
|   bool done = false;
 | |
|   nsPageSequenceFrame* pageSeqFrame = do_QueryFrame(mPageSeqFrame.GetFrame());
 | |
|   nsresult rv = pageSeqFrame->PrePrintNextSheet(mPagePrintTimer, &done);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     // ??? ::PrintSheet doesn't set |printData->mIsAborted = true| if
 | |
|     // rv != NS_ERROR_ABORT, but I don't really understand why this should be
 | |
|     // the right thing to do?  Shouldn't |printData->mIsAborted| set to true
 | |
|     // all the time if something went wrong?
 | |
|     if (rv != NS_ERROR_ABORT) {
 | |
|       FirePrintingErrorEvent(rv);
 | |
|       printData->mIsAborted = true;
 | |
|     }
 | |
|     done = true;
 | |
|   }
 | |
|   return done;
 | |
| }
 | |
| 
 | |
| bool nsPrintJob::PrintSheet(nsPrintObject* aPO, bool& aInRange) {
 | |
|   NS_ASSERTION(aPO, "aPO is null!");
 | |
|   NS_ASSERTION(mPageSeqFrame.IsAlive(), "mPageSeqFrame is not alive!");
 | |
|   NS_ASSERTION(mPrt, "mPrt is null!");
 | |
| 
 | |
|   // XXXdholbert Nowadays, this function doesn't need to concern itself with
 | |
|   // page ranges -- page-range handling is now handled when we reflow our
 | |
|   // PrintedSheetFrames, and all PrintedSheetFrames are "in-range" and should
 | |
|   // be printed. So this outparam is unconditionally true. Bug 1669815 is filed
 | |
|   // on removing it entirely.
 | |
|   aInRange = true;
 | |
| 
 | |
|   // Although these should NEVER be nullptr
 | |
|   // This is added insurance, to make sure we don't crash in optimized builds
 | |
|   if (!mPrt || !aPO || !mPageSeqFrame.IsAlive()) {
 | |
|     FirePrintingErrorEvent(NS_ERROR_FAILURE);
 | |
|     return true;  // means we are done printing
 | |
|   }
 | |
| 
 | |
|   // Guarantee that mPrt won't be deleted during a call of
 | |
|   // nsPrintData::DoOnProgressChange() which runs some listeners,
 | |
|   // which may clear (& might otherwise destroy).
 | |
|   RefPtr<nsPrintData> printData = mPrt;
 | |
| 
 | |
|   PR_PL(("-----------------------------------\n"));
 | |
|   PR_PL(("------ In DV::PrintSheet PO: %p (%s)\n", aPO,
 | |
|          gFrameTypesStr[aPO->mFrameType]));
 | |
| 
 | |
|   // Check setting to see if someone request it be cancelled
 | |
|   bool isCancelled = false;
 | |
|   printData->mPrintSettings->GetIsCancelled(&isCancelled);
 | |
|   if (isCancelled || printData->mIsAborted) {
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   nsPageSequenceFrame* pageSeqFrame = do_QueryFrame(mPageSeqFrame.GetFrame());
 | |
|   const uint32_t sheetIdx = pageSeqFrame->GetCurrentSheetIdx();
 | |
|   const uint32_t numSheets = pageSeqFrame->PrincipalChildList().GetLength();
 | |
| 
 | |
|   PR_PL(("****** Printing sheet index %d of %d sheets(s)\n", sheetIdx,
 | |
|          numSheets));
 | |
| 
 | |
|   MOZ_ASSERT(numSheets > 0, "print operations must have at least 1 sheet");
 | |
|   MOZ_ASSERT(sheetIdx < numSheets,
 | |
|              "sheetIdx shouldn't be allowed to go out of bounds");
 | |
|   printData->DoOnProgressChange(sheetIdx, numSheets, false, 0);
 | |
|   if (NS_WARN_IF(mPrt != printData)) {
 | |
|     // If current printing is canceled or new print is started, let's return
 | |
|     // true to notify the caller of current printing is done.
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   if (XRE_IsParentProcess() && !printData->mPrintDC->IsSyncPagePrinting()) {
 | |
|     mPagePrintTimer->WaitForRemotePrint();
 | |
|   }
 | |
| 
 | |
|   // Print the sheet
 | |
|   // if a print job was cancelled externally, an EndPage or BeginPage may
 | |
|   // fail and the failure is passed back here.
 | |
|   // Returning true means we are done printing.
 | |
|   //
 | |
|   // When rv == NS_ERROR_ABORT, it means we want out of the
 | |
|   // print job without displaying any error messages
 | |
|   nsresult rv = pageSeqFrame->PrintNextSheet();
 | |
|   if (NS_FAILED(rv)) {
 | |
|     if (rv != NS_ERROR_ABORT) {
 | |
|       FirePrintingErrorEvent(rv);
 | |
|       printData->mIsAborted = true;
 | |
|     }
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   pageSeqFrame->DoPageEnd();
 | |
| 
 | |
|   // If we just printed the final sheet (the one with index "numSheets-1"),
 | |
|   // then we're done!
 | |
|   return (sheetIdx == numSheets - 1);
 | |
| }
 | |
| 
 | |
| void nsPrintJob::PageDone(nsresult aResult) {
 | |
|   MOZ_ASSERT(mIsDoingPrinting);
 | |
| 
 | |
|   // mPagePrintTimer might be released during RemotePrintFinished, keep a
 | |
|   // reference here to make sure it lives long enough.
 | |
|   RefPtr<nsPagePrintTimer> timer = mPagePrintTimer;
 | |
|   timer->RemotePrintFinished();
 | |
| }
 | |
| 
 | |
| //-----------------------------------------------------------------
 | |
| //-- Done: Printing Methods
 | |
| //-----------------------------------------------------------------
 | |
| 
 | |
| //-----------------------------------------------------------------
 | |
| //-- Section: Misc Support Methods
 | |
| //-----------------------------------------------------------------
 | |
| 
 | |
| //---------------------------------------------------------------------
 | |
| void nsPrintJob::SetIsPrinting(bool aIsPrinting) {
 | |
|   mIsDoingPrinting = aIsPrinting;
 | |
|   if (aIsPrinting) {
 | |
|     mHasEverPrinted = true;
 | |
|   }
 | |
|   if (mPrt && aIsPrinting) {
 | |
|     mPrt->mPreparingForPrint = true;
 | |
|   }
 | |
| }
 | |
| 
 | |
| //---------------------------------------------------------------------
 | |
| void nsPrintJob::SetIsPrintPreview(bool aIsPrintPreview) {
 | |
|   mCreatedForPrintPreview = aIsPrintPreview;
 | |
| 
 | |
|   if (mDocViewerPrint) {
 | |
|     mDocViewerPrint->SetIsPrintPreview(aIsPrintPreview);
 | |
|   }
 | |
| }
 | |
| 
 | |
| Document* nsPrintJob::FindFocusedDocument(Document* aDoc) const {
 | |
|   nsFocusManager* fm = nsFocusManager::GetFocusManager();
 | |
|   NS_ENSURE_TRUE(fm, nullptr);
 | |
| 
 | |
|   nsPIDOMWindowOuter* window = aDoc->GetOriginalDocument()->GetWindow();
 | |
|   NS_ENSURE_TRUE(window, nullptr);
 | |
| 
 | |
|   nsCOMPtr<nsPIDOMWindowOuter> rootWindow = window->GetPrivateRoot();
 | |
|   NS_ENSURE_TRUE(rootWindow, nullptr);
 | |
| 
 | |
|   nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
 | |
|   nsFocusManager::GetFocusedDescendant(rootWindow,
 | |
|                                        nsFocusManager::eIncludeAllDescendants,
 | |
|                                        getter_AddRefs(focusedWindow));
 | |
|   NS_ENSURE_TRUE(focusedWindow, nullptr);
 | |
| 
 | |
|   if (IsWindowsInOurSubTree(focusedWindow)) {
 | |
|     return focusedWindow->GetDoc();
 | |
|   }
 | |
| 
 | |
|   return nullptr;
 | |
| }
 | |
| 
 | |
| //---------------------------------------------------------------------
 | |
| bool nsPrintJob::IsWindowsInOurSubTree(nsPIDOMWindowOuter* window) const {
 | |
|   if (window) {
 | |
|     nsCOMPtr<nsIDocShell> ourDocShell(do_QueryReferent(mDocShell));
 | |
|     if (ourDocShell) {
 | |
|       BrowsingContext* ourBC = ourDocShell->GetBrowsingContext();
 | |
|       BrowsingContext* bc = window->GetBrowsingContext();
 | |
|       while (bc) {
 | |
|         if (bc == ourBC) {
 | |
|           return true;
 | |
|         }
 | |
|         bc = bc->GetParent();
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| //-------------------------------------------------------
 | |
| bool nsPrintJob::DonePrintingSheets(nsPrintObject* aPO, nsresult aResult) {
 | |
|   // NS_ASSERTION(aPO, "Pointer is null!");
 | |
|   PR_PL(("****** In DV::DonePrintingSheets PO: %p (%s)\n", aPO,
 | |
|          aPO ? gFrameTypesStr[aPO->mFrameType] : ""));
 | |
| 
 | |
|   // If there is a pageSeqFrame, make sure there are no more printCanvas active
 | |
|   // that might call |Notify| on the pagePrintTimer after things are cleaned up
 | |
|   // and printing was marked as being done.
 | |
|   if (mPageSeqFrame.IsAlive()) {
 | |
|     nsPageSequenceFrame* pageSeqFrame = do_QueryFrame(mPageSeqFrame.GetFrame());
 | |
|     pageSeqFrame->ResetPrintCanvasList();
 | |
|   }
 | |
| 
 | |
|   // Guarantee that mPrt and mPrt->mPrintObject won't be deleted during a
 | |
|   // call of PrintDocContent() and FirePrintCompletionEvent().
 | |
|   RefPtr<nsPrintData> printData = mPrt;
 | |
| 
 | |
|   if (aPO && !printData->mIsAborted) {
 | |
|     aPO->mHasBeenPrinted = true;
 | |
|     nsresult rv;
 | |
|     bool didPrint = PrintDocContent(printData->mPrintObject, rv);
 | |
|     if (NS_SUCCEEDED(rv) && didPrint) {
 | |
|       PR_PL(
 | |
|           ("****** In DV::DonePrintingSheets PO: %p (%s) didPrint:%s (Not Done "
 | |
|            "Printing)\n",
 | |
|            aPO, gFrameTypesStr[aPO->mFrameType], PRT_YESNO(didPrint)));
 | |
|       return false;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (XRE_IsParentProcess() && printData->mPrintDC &&
 | |
|       !printData->mPrintDC->IsSyncPagePrinting()) {
 | |
|     printData->mPrintDC->UnregisterPageDoneCallback();
 | |
|   }
 | |
| 
 | |
|   if (NS_SUCCEEDED(aResult)) {
 | |
|     FirePrintCompletionEvent();
 | |
|     // XXX mPrt may be cleared or replaced with new instance here.
 | |
|     //     However, the following methods will clean up with new mPrt or will
 | |
|     //     do nothing due to no proper nsPrintData instance.
 | |
|   }
 | |
| 
 | |
|   SetIsPrinting(false);
 | |
| 
 | |
|   // Release reference to mPagePrintTimer; the timer object destroys itself
 | |
|   // after this returns true
 | |
|   DisconnectPagePrintTimer();
 | |
| 
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| //-------------------------------------------------------
 | |
| nsresult nsPrintJob::EnablePOsForPrinting() {
 | |
|   // Guarantee that mPrt and the objects it owns won't be deleted.
 | |
|   RefPtr<nsPrintData> printData = mPrt;
 | |
| 
 | |
|   // NOTE: All POs have been "turned off" for printing
 | |
|   // this is where we decided which POs get printed.
 | |
| 
 | |
|   if (!printData || !printData->mPrintSettings) {
 | |
|     return NS_ERROR_FAILURE;
 | |
|   }
 | |
| 
 | |
|   PR_PL(("\n"));
 | |
|   PR_PL(("********* nsPrintJob::EnablePOsForPrinting *********\n"));
 | |
| 
 | |
|   if (!printData->mPrintSettings->GetPrintSelectionOnly()) {
 | |
|     printData->mPrintObject->EnablePrinting(true);
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   // This means we are either printing a selected iframe or
 | |
|   // we are printing the current selection.
 | |
|   NS_ENSURE_STATE(!mDisallowSelectionPrint && printData->mSelectionRoot);
 | |
| 
 | |
|   // If mSelectionRoot is a selected iframe without a selection, then just
 | |
|   // enable normally from that point.
 | |
|   if (printData->mSelectionRoot->mFrameType == eIFrame &&
 | |
|       !printData->mSelectionRoot->HasSelection()) {
 | |
|     printData->mSelectionRoot->EnablePrinting(true);
 | |
|   } else {
 | |
|     // Otherwise, only enable nsPrintObjects that have a selection.
 | |
|     printData->mSelectionRoot->EnablePrintingSelectionOnly();
 | |
|   }
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| //-------------------------------------------------------
 | |
| // Return the nsPrintObject with that is XMost (The widest frameset frame) AND
 | |
| // contains the XMost (widest) layout frame
 | |
| nsPrintObject* nsPrintJob::FindSmallestSTF() {
 | |
|   float smallestRatio = 1.0f;
 | |
|   nsPrintObject* smallestPO = nullptr;
 | |
| 
 | |
|   for (uint32_t i = 0; i < mPrt->mPrintDocList.Length(); i++) {
 | |
|     nsPrintObject* po = mPrt->mPrintDocList.ElementAt(i);
 | |
|     NS_ASSERTION(po, "nsPrintObject can't be null!");
 | |
|     if (po->mFrameType != eFrameSet && po->mFrameType != eIFrame) {
 | |
|       if (po->mShrinkRatio < smallestRatio) {
 | |
|         smallestRatio = po->mShrinkRatio;
 | |
|         smallestPO = po;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
| #ifdef EXTENDED_DEBUG_PRINTING
 | |
|   if (smallestPO)
 | |
|     printf("*PO: %p  Type: %d  %10.3f\n", smallestPO, smallestPO->mFrameType,
 | |
|            smallestPO->mShrinkRatio);
 | |
| #endif
 | |
|   return smallestPO;
 | |
| }
 | |
| 
 | |
| //-----------------------------------------------------------------
 | |
| //-- Done: Misc Support Methods
 | |
| //-----------------------------------------------------------------
 | |
| 
 | |
| //-----------------------------------------------------------------
 | |
| //-- Section: Finishing up or Cleaning up
 | |
| //-----------------------------------------------------------------
 | |
| 
 | |
| //-----------------------------------------------------------------
 | |
| void nsPrintJob::CloseProgressDialog(
 | |
|     nsIWebProgressListener* aWebProgressListener) {
 | |
|   if (aWebProgressListener) {
 | |
|     aWebProgressListener->OnStateChange(
 | |
|         nullptr, nullptr,
 | |
|         nsIWebProgressListener::STATE_STOP |
 | |
|             nsIWebProgressListener::STATE_IS_DOCUMENT,
 | |
|         NS_OK);
 | |
|   }
 | |
| }
 | |
| 
 | |
| //-----------------------------------------------------------------
 | |
| nsresult nsPrintJob::FinishPrintPreview() {
 | |
|   nsresult rv = NS_OK;
 | |
| 
 | |
| #ifdef NS_PRINT_PREVIEW
 | |
| 
 | |
|   // If mPrt is null we've already finished with print preview. If mPrt is not
 | |
|   // null but mIsCreatingPrintPreview is false FinishPrintPreview must have
 | |
|   // already failed due to DocumentReadyForPrinting failing.
 | |
|   if (!mPrt || !mIsCreatingPrintPreview) {
 | |
|     return rv;
 | |
|   }
 | |
| 
 | |
|   rv = DocumentReadyForPrinting();
 | |
| 
 | |
|   // Note that this method may be called while the instance is being
 | |
|   // initialized.  Some methods which initialize the instance (e.g.,
 | |
|   // DoCommonPrint) may need to stop initializing and return error if
 | |
|   // this is called.  Therefore it's important to set mIsCreatingPrintPreview
 | |
|   // state to false here.  If you need to stop setting that here, you need to
 | |
|   // keep them being able to check whether the owner stopped using this
 | |
|   // instance.
 | |
|   mIsCreatingPrintPreview = false;
 | |
| 
 | |
|   // mPrt may be cleared during a call of nsPrintData::OnEndPrinting()
 | |
|   // because that method invokes some arbitrary listeners.
 | |
|   RefPtr<nsPrintData> printData = mPrt;
 | |
|   if (NS_FAILED(rv)) {
 | |
|     /* cleanup done, let's fire-up an error dialog to notify the user
 | |
|      * what went wrong...
 | |
|      */
 | |
|     printData->OnEndPrinting();
 | |
| 
 | |
|     return rv;
 | |
|   }
 | |
| 
 | |
|   if (mPrintPreviewCallback) {
 | |
|     const bool hasSelection =
 | |
|         !mDisallowSelectionPrint && printData->mSelectionRoot;
 | |
|     mPrintPreviewCallback(PrintPreviewResultInfo(
 | |
|         GetPrintPreviewNumSheets(), GetRawNumPages(), GetIsEmpty(),
 | |
|         hasSelection, hasSelection && printData->mPrintObject->HasSelection()));
 | |
|     mPrintPreviewCallback = nullptr;
 | |
|   }
 | |
| 
 | |
|   // At this point we are done preparing everything
 | |
|   // before it is to be created
 | |
| 
 | |
|   printData->OnEndPrinting();
 | |
|   // XXX If mPrt becomes nullptr or different instance here, what should we
 | |
|   //     do?
 | |
| 
 | |
|   // PrintPreview was built using the mPrt (code reuse)
 | |
|   // then we assign it over
 | |
|   mPrtPreview = std::move(mPrt);
 | |
| 
 | |
| #endif  // NS_PRINT_PREVIEW
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| //-----------------------------------------------------------------
 | |
| //-- Done: Finishing up or Cleaning up
 | |
| //-----------------------------------------------------------------
 | |
| 
 | |
| /*=============== Timer Related Code ======================*/
 | |
| nsresult nsPrintJob::StartPagePrintTimer(const UniquePtr<nsPrintObject>& aPO) {
 | |
|   if (!mPagePrintTimer) {
 | |
|     // Get the delay time in between the printing of each page
 | |
|     // this gives the user more time to press cancel
 | |
|     int32_t printPageDelay = 50;
 | |
|     mPrt->mPrintSettings->GetPrintPageDelay(&printPageDelay);
 | |
| 
 | |
|     nsCOMPtr<nsIContentViewer> cv = do_QueryInterface(mDocViewerPrint);
 | |
|     NS_ENSURE_TRUE(cv, NS_ERROR_FAILURE);
 | |
|     nsCOMPtr<Document> doc = cv->GetDocument();
 | |
|     NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
 | |
| 
 | |
|     mPagePrintTimer =
 | |
|         new nsPagePrintTimer(this, mDocViewerPrint, doc, printPageDelay);
 | |
| 
 | |
|     nsCOMPtr<nsIPrintSession> printSession;
 | |
|     nsresult rv =
 | |
|         mPrt->mPrintSettings->GetPrintSession(getter_AddRefs(printSession));
 | |
|     if (NS_SUCCEEDED(rv) && printSession) {
 | |
|       RefPtr<layout::RemotePrintJobChild> remotePrintJob =
 | |
|           printSession->GetRemotePrintJob();
 | |
|       if (remotePrintJob) {
 | |
|         remotePrintJob->SetPagePrintTimer(mPagePrintTimer);
 | |
|         remotePrintJob->SetPrintJob(this);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return mPagePrintTimer->Start(aPO.get());
 | |
| }
 | |
| 
 | |
| /*=============== nsIObserver Interface ======================*/
 | |
| MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP nsPrintJob::Observe(
 | |
|     nsISupports* aSubject, const char* aTopic, const char16_t* aData) {
 | |
|   // We expect to be called by nsIPrintingPromptService after we were passed to
 | |
|   // it by via the nsIPrintingPromptService::ShowPrintProgressDialog call in
 | |
|   // ShowPrintProgress.  Once it has opened the progress dialog it calls this
 | |
|   // method, passing null as the topic.
 | |
| 
 | |
|   if (aTopic) {
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   nsresult rv = InitPrintDocConstruction(true);
 | |
|   if (!mIsDoingPrinting && mPrtPreview) {
 | |
|     RefPtr<nsPrintData> printDataOfPrintPreview = mPrtPreview;
 | |
|     printDataOfPrintPreview->OnEndPrinting();
 | |
|   }
 | |
| 
 | |
|   return rv;
 | |
| }
 | |
| 
 | |
| //---------------------------------------------------------------
 | |
| //-- PLEvent Notification
 | |
| //---------------------------------------------------------------
 | |
| class nsPrintCompletionEvent : public Runnable {
 | |
|  public:
 | |
|   explicit nsPrintCompletionEvent(nsIDocumentViewerPrint* docViewerPrint)
 | |
|       : mozilla::Runnable("nsPrintCompletionEvent"),
 | |
|         mDocViewerPrint(docViewerPrint) {
 | |
|     NS_ASSERTION(mDocViewerPrint, "mDocViewerPrint is null.");
 | |
|   }
 | |
| 
 | |
|   NS_IMETHOD Run() override {
 | |
|     if (mDocViewerPrint) mDocViewerPrint->OnDonePrinting();
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   nsCOMPtr<nsIDocumentViewerPrint> mDocViewerPrint;
 | |
| };
 | |
| 
 | |
| //-----------------------------------------------------------
 | |
| void nsPrintJob::FirePrintCompletionEvent() {
 | |
|   MOZ_ASSERT(NS_IsMainThread());
 | |
|   nsCOMPtr<nsIRunnable> event = new nsPrintCompletionEvent(mDocViewerPrint);
 | |
|   nsCOMPtr<nsIContentViewer> cv = do_QueryInterface(mDocViewerPrint);
 | |
|   NS_ENSURE_TRUE_VOID(cv);
 | |
|   nsCOMPtr<Document> doc = cv->GetDocument();
 | |
|   NS_ENSURE_TRUE_VOID(doc);
 | |
| 
 | |
|   NS_ENSURE_SUCCESS_VOID(doc->Dispatch(TaskCategory::Other, event.forget()));
 | |
| }
 | |
| 
 | |
| void nsPrintJob::DisconnectPagePrintTimer() {
 | |
|   if (mPagePrintTimer) {
 | |
|     mPagePrintTimer->Disconnect();
 | |
|     mPagePrintTimer = nullptr;
 | |
|   }
 | |
| }
 | |
| 
 | |
| //---------------------------------------------------------------
 | |
| //---------------------------------------------------------------
 | |
| //-- Debug helper routines
 | |
| //---------------------------------------------------------------
 | |
| //---------------------------------------------------------------
 | |
| #if defined(XP_WIN) && defined(EXTENDED_DEBUG_PRINTING)
 | |
| #  include <windows.h>
 | |
| #  include <process.h>
 | |
| #  include <direct.h>
 | |
| 
 | |
| #  define MY_FINDFIRST(a, b) FindFirstFile(a, b)
 | |
| #  define MY_FINDNEXT(a, b) FindNextFile(a, b)
 | |
| #  define ISDIR(a) (a.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
 | |
| #  define MY_FINDCLOSE(a) FindClose(a)
 | |
| #  define MY_FILENAME(a) a.cFileName
 | |
| #  define MY_FILESIZE(a) (a.nFileSizeHigh * MAXDWORD) + a.nFileSizeLow
 | |
| 
 | |
| int RemoveFilesInDir(const char* aDir) {
 | |
|   WIN32_FIND_DATA data_ptr;
 | |
|   HANDLE find_handle;
 | |
| 
 | |
|   char path[MAX_PATH];
 | |
| 
 | |
|   strcpy(path, aDir);
 | |
| 
 | |
|   // Append slash to the end of the directory names if not there
 | |
|   if (path[strlen(path) - 1] != '\\') strcat(path, "\\");
 | |
| 
 | |
|   char findPath[MAX_PATH];
 | |
|   strcpy(findPath, path);
 | |
|   strcat(findPath, "*.*");
 | |
| 
 | |
|   find_handle = MY_FINDFIRST(findPath, &data_ptr);
 | |
| 
 | |
|   if (find_handle != INVALID_HANDLE_VALUE) {
 | |
|     do {
 | |
|       if (ISDIR(data_ptr) && (stricmp(MY_FILENAME(data_ptr), ".")) &&
 | |
|           (stricmp(MY_FILENAME(data_ptr), ".."))) {
 | |
|         // skip
 | |
|       } else if (!ISDIR(data_ptr)) {
 | |
|         if (!strncmp(MY_FILENAME(data_ptr), "print_dump", 10)) {
 | |
|           char fileName[MAX_PATH];
 | |
|           strcpy(fileName, aDir);
 | |
|           strcat(fileName, "\\");
 | |
|           strcat(fileName, MY_FILENAME(data_ptr));
 | |
|           printf("Removing %s\n", fileName);
 | |
|           remove(fileName);
 | |
|         }
 | |
|       }
 | |
|     } while (MY_FINDNEXT(find_handle, &data_ptr));
 | |
|     MY_FINDCLOSE(find_handle);
 | |
|   }
 | |
|   return TRUE;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| #ifdef EXTENDED_DEBUG_PRINTING
 | |
| 
 | |
| /** ---------------------------------------------------
 | |
|  *  Dumps Frames for Printing
 | |
|  */
 | |
| static void RootFrameList(nsPresContext* aPresContext, FILE* out,
 | |
|                           const char* aPrefix) {
 | |
|   if (!aPresContext || !out) return;
 | |
| 
 | |
|   if (PresShell* presShell = aPresContext->GetPresShell()) {
 | |
|     nsIFrame* frame = presShell->GetRootFrame();
 | |
|     if (frame) {
 | |
|       frame->List(out, aPrefix);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| /** ---------------------------------------------------
 | |
|  *  Dumps Frames for Printing
 | |
|  */
 | |
| static void DumpFrames(FILE* out, nsPresContext* aPresContext,
 | |
|                        gfxContext* aRendContext, nsIFrame* aFrame,
 | |
|                        int32_t aLevel) {
 | |
|   NS_ASSERTION(out, "Pointer is null!");
 | |
|   NS_ASSERTION(aPresContext, "Pointer is null!");
 | |
|   NS_ASSERTION(aRendContext, "Pointer is null!");
 | |
|   NS_ASSERTION(aFrame, "Pointer is null!");
 | |
| 
 | |
|   nsIFrame* child = aFrame->PrincipalChildList().FirstChild();
 | |
|   while (child != nullptr) {
 | |
|     for (int32_t i = 0; i < aLevel; i++) {
 | |
|       fprintf(out, "  ");
 | |
|     }
 | |
|     nsAutoString tmp;
 | |
|     child->GetFrameName(tmp);
 | |
|     fputs(NS_LossyConvertUTF16toASCII(tmp).get(), out);
 | |
|     bool isSelected;
 | |
|     if (child->IsVisibleForPainting()) {
 | |
|       fprintf(out, " %p %s", child, isSelected ? "VIS" : "UVS");
 | |
|       nsRect rect = child->GetRect();
 | |
|       fprintf(out, "[%d,%d,%d,%d] ", rect.x, rect.y, rect.width, rect.height);
 | |
|       fprintf(out, "v: %p ", (void*)child->GetView());
 | |
|       fprintf(out, "\n");
 | |
|       DumpFrames(out, aPresContext, aRendContext, child, aLevel + 1);
 | |
|       child = child->GetNextSibling();
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| /** ---------------------------------------------------
 | |
|  *  Dumps the Views from the DocShell
 | |
|  */
 | |
| static void DumpViews(nsIDocShell* aDocShell, FILE* out) {
 | |
|   NS_ASSERTION(aDocShell, "Pointer is null!");
 | |
|   NS_ASSERTION(out, "Pointer is null!");
 | |
| 
 | |
|   if (nullptr != aDocShell) {
 | |
|     fprintf(out, "docshell=%p \n", aDocShell);
 | |
|     if (PresShell* presShell = aDocShell->GetPresShell()) {
 | |
|       nsViewManager* vm = presShell->GetViewManager();
 | |
|       if (vm) {
 | |
|         nsView* root = vm->GetRootView();
 | |
|         if (root) {
 | |
|           root->List(out);
 | |
|         }
 | |
|       }
 | |
|     } else {
 | |
|       fputs("null pres shell\n", out);
 | |
|     }
 | |
| 
 | |
|     // dump the views of the sub documents
 | |
|     int32_t i, n;
 | |
|     BrowsingContext* bc = nsDocShell::Cast(aDocShell)->GetBrowsingContext();
 | |
|     for (auto& child : bc->Children()) {
 | |
|       if (auto childDS = child->GetDocShell()) {
 | |
|         DumpViews(childAsShell, out);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| /** ---------------------------------------------------
 | |
|  *  Dumps the Views and Frames
 | |
|  */
 | |
| void DumpLayoutData(const char* aTitleStr, const char* aURLStr,
 | |
|                     nsPresContext* aPresContext, nsDeviceContext* aDC,
 | |
|                     nsIFrame* aRootFrame, nsIDocShell* aDocShell,
 | |
|                     FILE* aFD = nullptr) {
 | |
|   if (!MOZ_LOG_TEST(gPrintingLog, DUMP_LAYOUT_LEVEL)) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (aPresContext == nullptr || aDC == nullptr) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
| #  ifdef NS_PRINT_PREVIEW
 | |
|   if (aPresContext->Type() == nsPresContext::eContext_PrintPreview) {
 | |
|     return;
 | |
|   }
 | |
| #  endif
 | |
| 
 | |
|   NS_ASSERTION(aRootFrame, "Pointer is null!");
 | |
|   NS_ASSERTION(aDocShell, "Pointer is null!");
 | |
| 
 | |
|   // Dump all the frames and view to a a file
 | |
|   char filename[256];
 | |
|   sprintf(filename, "print_dump_layout_%d.txt", gDumpLOFileNameCnt++);
 | |
|   FILE* fd = aFD ? aFD : fopen(filename, "w");
 | |
|   if (fd) {
 | |
|     fprintf(fd, "Title: %s\n", aTitleStr ? aTitleStr : "");
 | |
|     fprintf(fd, "URL:   %s\n", aURLStr ? aURLStr : "");
 | |
|     fprintf(fd, "--------------- Frames ----------------\n");
 | |
|     fprintf(fd, "--------------- Frames ----------------\n");
 | |
|     // RefPtr<gfxContext> renderingContext =
 | |
|     //  aDC->CreateRenderingContext();
 | |
|     RootFrameList(aPresContext, fd, "");
 | |
|     // DumpFrames(fd, aPresContext, renderingContext, aRootFrame, 0);
 | |
|     fprintf(fd, "---------------------------------------\n\n");
 | |
|     fprintf(fd, "--------------- Views From Root Frame----------------\n");
 | |
|     nsView* v = aRootFrame->GetView();
 | |
|     if (v) {
 | |
|       v->List(fd);
 | |
|     } else {
 | |
|       printf("View is null!\n");
 | |
|     }
 | |
|     if (aDocShell) {
 | |
|       fprintf(fd, "--------------- All Views ----------------\n");
 | |
|       DumpViews(aDocShell, fd);
 | |
|       fprintf(fd, "---------------------------------------\n\n");
 | |
|     }
 | |
|     if (aFD == nullptr) {
 | |
|       fclose(fd);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| //-------------------------------------------------------------
 | |
| static void DumpPrintObjectsList(const nsTArray<nsPrintObject*>& aDocList) {
 | |
|   if (!MOZ_LOG_TEST(gPrintingLog, DUMP_LAYOUT_LEVEL)) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   const char types[][3] = {"DC", "FR", "IF", "FS"};
 | |
|   PR_PL(("Doc List\n***************************************************\n"));
 | |
|   PR_PL(
 | |
|       ("T  P A H    PO    DocShell   Seq     Page      Root     Page#    "
 | |
|        "Rect\n"));
 | |
|   for (nsPrintObject* po : aDocList) {
 | |
|     NS_ASSERTION(po, "nsPrintObject can't be null!");
 | |
|     nsIFrame* rootFrame = nullptr;
 | |
|     if (po->mPresShell) {
 | |
|       rootFrame = po->mPresShell->GetRootFrame();
 | |
|       while (rootFrame != nullptr) {
 | |
|         nsPageSequenceFrame* sqf = do_QueryFrame(rootFrame);
 | |
|         if (sqf) {
 | |
|           break;
 | |
|         }
 | |
|         rootFrame = rootFrame->PrincipalChildList().FirstChild();
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     PR_PL(("%s %d %d %p %p %p\n", types[po->mFrameType],
 | |
|            po->PrintingIsEnabled(), po->mHasBeenPrinted, po,
 | |
|            po->mDocShell.get(), rootFrame));
 | |
|   }
 | |
| }
 | |
| 
 | |
| //-------------------------------------------------------------
 | |
| static void DumpPrintObjectsTree(nsPrintObject* aPO, int aLevel, FILE* aFD) {
 | |
|   if (!MOZ_LOG_TEST(gPrintingLog, DUMP_LAYOUT_LEVEL)) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   NS_ASSERTION(aPO, "Pointer is null!");
 | |
| 
 | |
|   FILE* fd = aFD ? aFD : stdout;
 | |
|   const char types[][3] = {"DC", "FR", "IF", "FS"};
 | |
|   if (aLevel == 0) {
 | |
|     fprintf(fd,
 | |
|             "DocTree\n***************************************************\n");
 | |
|     fprintf(fd, "T     PO    DocShell   Seq      Page     Page#    Rect\n");
 | |
|   }
 | |
|   for (const auto& po : aPO->mKids) {
 | |
|     NS_ASSERTION(po, "nsPrintObject can't be null!");
 | |
|     for (int32_t k = 0; k < aLevel; k++) fprintf(fd, "  ");
 | |
|     fprintf(fd, "%s %p %p\n", types[po->mFrameType], po.get(),
 | |
|             po->mDocShell.get());
 | |
|   }
 | |
| }
 | |
| 
 | |
| //-------------------------------------------------------------
 | |
| static void GetDocTitleAndURL(const UniquePtr<nsPrintObject>& aPO,
 | |
|                               nsACString& aDocStr, nsACString& aURLStr) {
 | |
|   nsAutoString docTitleStr;
 | |
|   nsAutoString docURLStr;
 | |
|   GetDocumentTitleAndURL(aPO->mDocument, docTitleStr, docURLStr);
 | |
|   CopyUTF16toUTF8(docTitleStr, aDocStr);
 | |
|   CopyUTF16toUTF8(docURLStr, aURLStr);
 | |
| }
 | |
| 
 | |
| //-------------------------------------------------------------
 | |
| static void DumpPrintObjectsTreeLayout(const UniquePtr<nsPrintObject>& aPO,
 | |
|                                        nsDeviceContext* aDC, int aLevel,
 | |
|                                        FILE* aFD) {
 | |
|   if (!MOZ_LOG_TEST(gPrintingLog, DUMP_LAYOUT_LEVEL)) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   NS_ASSERTION(aPO, "Pointer is null!");
 | |
|   NS_ASSERTION(aDC, "Pointer is null!");
 | |
| 
 | |
|   const char types[][3] = {"DC", "FR", "IF", "FS"};
 | |
|   FILE* fd = nullptr;
 | |
|   if (aLevel == 0) {
 | |
|     fd = fopen("tree_layout.txt", "w");
 | |
|     fprintf(fd,
 | |
|             "DocTree\n***************************************************\n");
 | |
|     fprintf(fd, "***************************************************\n");
 | |
|     fprintf(fd, "T     PO    DocShell   Seq      Page     Page#    Rect\n");
 | |
|   } else {
 | |
|     fd = aFD;
 | |
|   }
 | |
|   if (fd) {
 | |
|     nsIFrame* rootFrame = nullptr;
 | |
|     if (aPO->mPresShell) {
 | |
|       rootFrame = aPO->mPresShell->GetRootFrame();
 | |
|     }
 | |
|     for (int32_t k = 0; k < aLevel; k++) fprintf(fd, "  ");
 | |
|     fprintf(fd, "%s %p %p\n", types[aPO->mFrameType], aPO.get(),
 | |
|             aPO->mDocShell.get());
 | |
|     if (aPO->PrintingIsEnabled()) {
 | |
|       nsAutoCString docStr;
 | |
|       nsAutoCString urlStr;
 | |
|       GetDocTitleAndURL(aPO, docStr, urlStr);
 | |
|       DumpLayoutData(docStr.get(), urlStr.get(), aPO->mPresContext, aDC,
 | |
|                      rootFrame, aPO->mDocShell, fd);
 | |
|     }
 | |
|     fprintf(fd, "<***************************************************>\n");
 | |
| 
 | |
|     for (const auto& po : aPO->mKids) {
 | |
|       NS_ASSERTION(po, "nsPrintObject can't be null!");
 | |
|       DumpPrintObjectsTreeLayout(po, aDC, aLevel + 1, fd);
 | |
|     }
 | |
|   }
 | |
|   if (aLevel == 0 && fd) {
 | |
|     fclose(fd);
 | |
|   }
 | |
| }
 | |
| 
 | |
| //-------------------------------------------------------------
 | |
| static void DumpPrintObjectsListStart(
 | |
|     const char* aStr, const nsTArray<nsPrintObject*>& aDocList) {
 | |
|   if (!MOZ_LOG_TEST(gPrintingLog, DUMP_LAYOUT_LEVEL)) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   NS_ASSERTION(aStr, "Pointer is null!");
 | |
| 
 | |
|   PR_PL(("%s\n", aStr));
 | |
|   DumpPrintObjectsList(aDocList);
 | |
| }
 | |
| 
 | |
| #endif
 | |
| 
 | |
| //---------------------------------------------------------------
 | |
| //---------------------------------------------------------------
 | |
| //-- End of debug helper routines
 | |
| //---------------------------------------------------------------
 | 
