diff options
Diffstat (limited to 'layout/printing')
-rw-r--r-- | layout/printing/Makefile.in | 24 | ||||
-rw-r--r-- | layout/printing/crashtests/509839-1.html | 10 | ||||
-rw-r--r-- | layout/printing/crashtests/509839-2.html | 8 | ||||
-rw-r--r-- | layout/printing/crashtests/576878.xhtml | 9 | ||||
-rw-r--r-- | layout/printing/crashtests/793844.html | 10 | ||||
-rw-r--r-- | layout/printing/crashtests/crashtests.list | 4 | ||||
-rw-r--r-- | layout/printing/moz.build | 26 | ||||
-rw-r--r-- | layout/printing/nsIPrintProgress.idl | 44 | ||||
-rw-r--r-- | layout/printing/nsIPrintProgressParams.idl | 14 | ||||
-rw-r--r-- | layout/printing/nsIPrintStatusFeedback.idl | 21 | ||||
-rw-r--r-- | layout/printing/nsPagePrintTimer.cpp | 180 | ||||
-rw-r--r-- | layout/printing/nsPagePrintTimer.h | 75 | ||||
-rw-r--r-- | layout/printing/nsPrintData.cpp | 137 | ||||
-rw-r--r-- | layout/printing/nsPrintData.h | 103 | ||||
-rw-r--r-- | layout/printing/nsPrintEngine.cpp | 4043 | ||||
-rw-r--r-- | layout/printing/nsPrintEngine.h | 314 | ||||
-rw-r--r-- | layout/printing/nsPrintObject.cpp | 111 | ||||
-rw-r--r-- | layout/printing/nsPrintObject.h | 72 | ||||
-rw-r--r-- | layout/printing/nsPrintPreviewListener.cpp | 189 | ||||
-rw-r--r-- | layout/printing/nsPrintPreviewListener.h | 47 |
20 files changed, 5441 insertions, 0 deletions
diff --git a/layout/printing/Makefile.in b/layout/printing/Makefile.in new file mode 100644 index 000000000..5422ff2f2 --- /dev/null +++ b/layout/printing/Makefile.in @@ -0,0 +1,24 @@ +# 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/. + +DEPTH = @DEPTH@ +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +LIBXUL_LIBRARY = 1 +FAIL_ON_WARNINGS = 1 + +FORCE_STATIC_LIB = 1 + +include $(topsrcdir)/config/rules.mk + +LOCAL_INCLUDES += \ + -I$(srcdir)/../base \ + -I$(srcdir)/../../content/base/src \ + $(NULL) + +DEFINES += -D_IMPL_NS_LAYOUT diff --git a/layout/printing/crashtests/509839-1.html b/layout/printing/crashtests/509839-1.html new file mode 100644 index 000000000..37d05ff96 --- /dev/null +++ b/layout/printing/crashtests/509839-1.html @@ -0,0 +1,10 @@ +<html class="reftest-print"><head> +<title>Crash [@ nsViewManager::CreateView][@ nsCSSFrameConstructor::AddFrameConstructionItemsInternal] on closing print preview with -moz-transform, position: fixed table displays</title> +</head><body> +<div style="position: fixed;"> +<div style="display: table-header-group; -moz-transform: rotate(1deg);"> +<div style="position: relative;"></div> +<div style="display: table-row;page-break-before: always;"></div> +</div> +</body> +</html> diff --git a/layout/printing/crashtests/509839-2.html b/layout/printing/crashtests/509839-2.html new file mode 100644 index 000000000..2a47a607c --- /dev/null +++ b/layout/printing/crashtests/509839-2.html @@ -0,0 +1,8 @@ +<html style="position: fixed; -moz-transform: scale(0.000001);" class="reftest-print"> +<head> +</head> +<body> +<div style="display: table-row;"></div> +<div style="display: table-row; page-break-before: right;"></div> +</body> +</html> diff --git a/layout/printing/crashtests/576878.xhtml b/layout/printing/crashtests/576878.xhtml new file mode 100644 index 000000000..d281427d9 --- /dev/null +++ b/layout/printing/crashtests/576878.xhtml @@ -0,0 +1,9 @@ +<html xmlns="http://www.w3.org/1999/xhtml" xmlns:mathml="http://www.w3.org/1998/Math/MathML" class="reftest-print"> + +<mathml:mfenced style="position: fixed;"> +<span style="position: inherit; float: right;"><span style="position:fixed;">b</span></span> +</mathml:mfenced> +<div style="page-break-before: always; "/> + +m +</html> diff --git a/layout/printing/crashtests/793844.html b/layout/printing/crashtests/793844.html new file mode 100644 index 000000000..21e163530 --- /dev/null +++ b/layout/printing/crashtests/793844.html @@ -0,0 +1,10 @@ +<html class="reftest-print"> +<head> +<title>crash in nsContentList::nsContentList on print preview</title> +</head><body> + +<iframe onload="window.location.reload()" src="data:text/html,"> +</iframe> + + +</body></html> diff --git a/layout/printing/crashtests/crashtests.list b/layout/printing/crashtests/crashtests.list new file mode 100644 index 000000000..0ec048d68 --- /dev/null +++ b/layout/printing/crashtests/crashtests.list @@ -0,0 +1,4 @@ +load 509839-1.html +load 509839-2.html +load 576878.xhtml +load 793844.html diff --git a/layout/printing/moz.build b/layout/printing/moz.build new file mode 100644 index 000000000..152a3587c --- /dev/null +++ b/layout/printing/moz.build @@ -0,0 +1,26 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +XPIDL_SOURCES += [ + 'nsIPrintProgress.idl', + 'nsIPrintProgressParams.idl', + 'nsIPrintStatusFeedback.idl', +] + +XPIDL_MODULE = 'layout_printing' + +MODULE = 'layout' + +CPP_SOURCES += [ + 'nsPagePrintTimer.cpp', + 'nsPrintData.cpp', + 'nsPrintEngine.cpp', + 'nsPrintObject.cpp', + 'nsPrintPreviewListener.cpp', +] + +LIBRARY_NAME = 'gkprinting_s' + diff --git a/layout/printing/nsIPrintProgress.idl b/layout/printing/nsIPrintProgress.idl new file mode 100644 index 000000000..fed7d0fac --- /dev/null +++ b/layout/printing/nsIPrintProgress.idl @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "nsISupports.idl" +#include "nsIWebProgressListener.idl" + +interface nsIDOMWindow; +interface nsIObserver; +interface nsIPrompt; + +[scriptable, uuid(594fd36d-5b1b-412f-a74e-ab72099a5bb2)] +interface nsIPrintProgress: nsIWebProgressListener { + + /* Open the progress dialog + you can specify parameters through an xpcom object + */ + void openProgressDialog(in nsIDOMWindow parent, + in string dialogURL, + in nsISupports parameters, + in nsIObserver openDialogObserver, + out boolean notifyOnOpen); + + /* Close the progress dialog */ + void closeProgressDialog(in boolean forceClose); + + /* Register a Web Progress Listener */ + void registerListener(in nsIWebProgressListener listener); + + /* Unregister a Web Progress Listener */ + void unregisterListener(in nsIWebProgressListener listener); + + /* This method is called after the dialog that shows the progress has been shown + */ + void doneIniting(); + + /* Retrieve the prompter, needed to display modal dialog on top of progress dialog */ + nsIPrompt getPrompter(); + + /* Indicated if the user asked to cancel the current process */ + attribute boolean processCanceledByUser; +}; + + diff --git a/layout/printing/nsIPrintProgressParams.idl b/layout/printing/nsIPrintProgressParams.idl new file mode 100644 index 000000000..294391273 --- /dev/null +++ b/layout/printing/nsIPrintProgressParams.idl @@ -0,0 +1,14 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "nsISupports.idl" + +[scriptable, uuid(CA89B55B-6FAF-4051-9645-1C03EF5108F8)] +interface nsIPrintProgressParams: nsISupports { + + /* message subject */ + attribute wstring docTitle; + attribute wstring docURL; + +}; diff --git a/layout/printing/nsIPrintStatusFeedback.idl b/layout/printing/nsIPrintStatusFeedback.idl new file mode 100644 index 000000000..331a6d4d4 --- /dev/null +++ b/layout/printing/nsIPrintStatusFeedback.idl @@ -0,0 +1,21 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "nsISupports.idl" + +interface nsIDocShell; +interface nsIDOMWindow; + +[scriptable, uuid(e439d3eb-b1ed-449c-aaf7-c693e399b16f)] +interface nsIPrintStatusFeedback : nsISupports { + + void showStatusString(in wstring status); + void startMeteors(); + void stopMeteors(); + void showProgress(in long percent); + [noscript] void setDocShell(in nsIDocShell shell, in nsIDOMWindow window); + void closeWindow(); +}; + diff --git a/layout/printing/nsPagePrintTimer.cpp b/layout/printing/nsPagePrintTimer.cpp new file mode 100644 index 000000000..39055fa5a --- /dev/null +++ b/layout/printing/nsPagePrintTimer.cpp @@ -0,0 +1,180 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "nsPagePrintTimer.h" +#include "nsIContentViewer.h" +#include "nsIServiceManager.h" +#include "nsPrintEngine.h" + +NS_IMPL_ISUPPORTS_INHERITED1(nsPagePrintTimer, nsRunnable, nsITimerCallback) + +nsPagePrintTimer::~nsPagePrintTimer() +{ + // "Destroy" the document viewer; this normally doesn't actually + // destroy it because of the IncrementDestroyRefCount call below + // XXX This is messy; the document viewer should use a single approach + // to keep itself alive during printing + nsCOMPtr<nsIContentViewer> cv(do_QueryInterface(mDocViewerPrint)); + if (cv) { + cv->Destroy(); + } +} + +nsresult +nsPagePrintTimer::StartTimer(bool aUseDelay) +{ + nsresult result; + mTimer = do_CreateInstance("@mozilla.org/timer;1", &result); + if (NS_FAILED(result)) { + NS_WARNING("unable to start the timer"); + } else { + uint32_t delay = 0; + if (aUseDelay) { + if (mFiringCount < 10) { + // Longer delay for the few first pages. + delay = mDelay + ((10 - mFiringCount) * 100); + } else { + delay = mDelay; + } + } + mTimer->InitWithCallback(this, delay, nsITimer::TYPE_ONE_SHOT); + } + return result; +} + +nsresult +nsPagePrintTimer::StartWatchDogTimer() +{ + nsresult result; + if (mWatchDogTimer) { + mWatchDogTimer->Cancel(); + } + mWatchDogTimer = do_CreateInstance("@mozilla.org/timer;1", &result); + if (NS_FAILED(result)) { + NS_WARNING("unable to start the timer"); + } else { + // Instead of just doing one timer for a long period do multiple so we + // can check if the user cancelled the printing. + mWatchDogTimer->InitWithCallback(this, WATCH_DOG_INTERVAL, + nsITimer::TYPE_ONE_SHOT); + } + return result; +} + +void +nsPagePrintTimer::StopWatchDogTimer() +{ + if (mWatchDogTimer) { + mWatchDogTimer->Cancel(); + mWatchDogTimer = nullptr; + } +} + +//nsRunnable +NS_IMETHODIMP +nsPagePrintTimer::Run() +{ + bool initNewTimer = true; + // Check to see if we are done + // inRange will be true if a page is actually printed + bool inRange; + bool donePrinting; + + // donePrinting will be true if it completed successfully or + // if the printing was cancelled + donePrinting = mPrintEngine->PrintPage(mPrintObj, inRange); + if (donePrinting) { + // now clean up print or print the next webshell + if (mPrintEngine->DonePrintingPages(mPrintObj, NS_OK)) { + initNewTimer = false; + mDone = true; + } + } + + // Note that the Stop() destroys this after the print job finishes + // (The PrintEngine stops holding a reference when DonePrintingPages + // returns true.) + Stop(); + if (initNewTimer) { + ++mFiringCount; + nsresult result = StartTimer(inRange); + if (NS_FAILED(result)) { + mDone = true; // had a failure.. we are finished.. + mPrintEngine->SetIsPrinting(false); + } + } + return NS_OK; +} + +// nsITimerCallback +NS_IMETHODIMP +nsPagePrintTimer::Notify(nsITimer *timer) +{ + // When finished there may be still pending notifications, which we can just + // ignore. + if (mDone) { + return NS_OK; + } + + // There are three things that call Notify with different values for timer: + // 1) the delay between pages (timer == mTimer) + // 2) canvasPrintState done (timer == null) + // 3) the watch dog timer (timer == mWatchDogTimer) + if (timer && timer == mWatchDogTimer) { + mWatchDogCount++; + if (mWatchDogCount > WATCH_DOG_MAX_COUNT) { + Fail(); + return NS_OK; + } + } else if(!timer) { + // Reset the counter since a mozPrintCallback has finished. + mWatchDogCount = 0; + } + + if (mDocViewerPrint) { + bool donePrePrint = mPrintEngine->PrePrintPage(); + + if (donePrePrint) { + StopWatchDogTimer(); + NS_DispatchToMainThread(this); + } else { + // Start the watch dog if we're waiting for preprint to ensure that if any + // mozPrintCallbacks take to long we error out. + StartWatchDogTimer(); + } + + } + return NS_OK; +} + +nsresult +nsPagePrintTimer::Start(nsPrintObject* aPO) +{ + mPrintObj = aPO; + mWatchDogCount = 0; + mDone = false; + return StartTimer(false); +} + + +void +nsPagePrintTimer::Stop() +{ + if (mTimer) { + mTimer->Cancel(); + mTimer = nullptr; + } + StopWatchDogTimer(); +} + +void +nsPagePrintTimer::Fail() +{ + mDone = true; + Stop(); + if (mPrintEngine) { + mPrintEngine->CleanupOnFailure(NS_OK, false); + } +} diff --git a/layout/printing/nsPagePrintTimer.h b/layout/printing/nsPagePrintTimer.h new file mode 100644 index 000000000..72371315c --- /dev/null +++ b/layout/printing/nsPagePrintTimer.h @@ -0,0 +1,75 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ +#ifndef nsPagePrintTimer_h___ +#define nsPagePrintTimer_h___ + +// Timer Includes +#include "nsITimer.h" + +#include "nsIDocumentViewerPrint.h" +#include "nsPrintObject.h" +#include "mozilla/Attributes.h" +#include "nsThreadUtils.h" + +class nsPrintEngine; + +//--------------------------------------------------- +//-- Page Timer Class +//--------------------------------------------------- +class nsPagePrintTimer MOZ_FINAL : public nsRunnable, + public nsITimerCallback +{ +public: + + NS_DECL_ISUPPORTS + + nsPagePrintTimer(nsPrintEngine* aPrintEngine, + nsIDocumentViewerPrint* aDocViewerPrint, + uint32_t aDelay) + : mPrintEngine(aPrintEngine) + , mDocViewerPrint(aDocViewerPrint) + , mDelay(aDelay) + , mFiringCount(0) + , mPrintObj(nullptr) + , mWatchDogCount(0) + , mDone(false) + { + mDocViewerPrint->IncrementDestroyRefCount(); + } + ~nsPagePrintTimer(); + + NS_DECL_NSITIMERCALLBACK + + nsresult Start(nsPrintObject* aPO); + + NS_IMETHOD Run() MOZ_OVERRIDE; + + void Stop(); + +private: + nsresult StartTimer(bool aUseDelay); + nsresult StartWatchDogTimer(); + void StopWatchDogTimer(); + void Fail(); + + nsPrintEngine* mPrintEngine; + nsCOMPtr<nsIDocumentViewerPrint> mDocViewerPrint; + nsCOMPtr<nsITimer> mTimer; + nsCOMPtr<nsITimer> mWatchDogTimer; + uint32_t mDelay; + uint32_t mFiringCount; + nsPrintObject * mPrintObj; + uint32_t mWatchDogCount; + bool mDone; + + static const uint32_t WATCH_DOG_INTERVAL = 1000; + static const uint32_t WATCH_DOG_MAX_COUNT = 10; +}; + + +nsresult +NS_NewPagePrintTimer(nsPagePrintTimer **aResult); + +#endif /* nsPagePrintTimer_h___ */ diff --git a/layout/printing/nsPrintData.cpp b/layout/printing/nsPrintData.cpp new file mode 100644 index 000000000..8f046c889 --- /dev/null +++ b/layout/printing/nsPrintData.cpp @@ -0,0 +1,137 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "nsPrintData.h" + +#include "nsIStringBundle.h" +#include "nsIServiceManager.h" +#include "nsPrintObject.h" +#include "nsPrintPreviewListener.h" +#include "nsIWebProgressListener.h" +#include "mozilla/Services.h" + +//----------------------------------------------------- +// PR LOGGING +#ifdef MOZ_LOGGING +#define FORCE_PR_LOG /* Allow logging in the release build */ +#endif + +#include "prlog.h" + +#ifdef PR_LOGGING +#define DUMP_LAYOUT_LEVEL 9 // this turns on the dumping of each doucment's layout info +static PRLogModuleInfo * +GetPrintingLog() +{ + static PRLogModuleInfo *sLog; + if (!sLog) + sLog = PR_NewLogModule("printing"); + return sLog; +} +#define PR_PL(_p1) PR_LOG(GetPrintingLog(), PR_LOG_DEBUG, _p1); +#else +#define PRT_YESNO(_p) +#define PR_PL(_p1) +#endif + +//--------------------------------------------------- +//-- nsPrintData Class Impl +//--------------------------------------------------- +nsPrintData::nsPrintData(ePrintDataType aType) : + mType(aType), mDebugFilePtr(nullptr), mPrintObject(nullptr), mSelectedPO(nullptr), + mPrintDocList(0), mIsIFrameSelected(false), + mIsParentAFrameSet(false), mOnStartSent(false), + mIsAborted(false), mPreparingForPrint(false), mDocWasToBeDestroyed(false), + mShrinkToFit(false), mPrintFrameType(nsIPrintSettings::kFramesAsIs), + mNumPrintablePages(0), mNumPagesPrinted(0), + mShrinkRatio(1.0), mOrigDCScale(1.0), mPPEventListeners(NULL), + mBrandName(nullptr) +{ + MOZ_COUNT_CTOR(nsPrintData); + nsCOMPtr<nsIStringBundle> brandBundle; + nsCOMPtr<nsIStringBundleService> svc = + mozilla::services::GetStringBundleService(); + if (svc) { + svc->CreateBundle( "chrome://branding/locale/brand.properties", getter_AddRefs( brandBundle ) ); + if (brandBundle) { + brandBundle->GetStringFromName(NS_LITERAL_STRING("brandShortName").get(), &mBrandName ); + } + } + + if (!mBrandName) { + mBrandName = ToNewUnicode(NS_LITERAL_STRING("Mozilla Document")); + } + +} + +nsPrintData::~nsPrintData() +{ + MOZ_COUNT_DTOR(nsPrintData); + // remove the event listeners + if (mPPEventListeners) { + mPPEventListeners->RemoveListeners(); + NS_RELEASE(mPPEventListeners); + } + + // Only Send an OnEndPrinting if we have started printing + if (mOnStartSent && mType != eIsPrintPreview) { + OnEndPrinting(); + } + + if (mPrintDC && !mDebugFilePtr) { + PR_PL(("****************** End Document ************************\n")); + PR_PL(("\n")); + bool isCancelled = false; + mPrintSettings->GetIsCancelled(&isCancelled); + + nsresult rv = NS_OK; + if (mType == eIsPrinting) { + if (!isCancelled && !mIsAborted) { + rv = mPrintDC->EndDocument(); + } else { + rv = mPrintDC->AbortDocument(); + } + if (NS_FAILED(rv)) { + // XXX nsPrintData::ShowPrintErrorDialog(rv); + } + } + } + + delete mPrintObject; + + if (mBrandName) { + NS_Free(mBrandName); + } +} + +void nsPrintData::OnStartPrinting() +{ + if (!mOnStartSent) { + DoOnProgressChange(0, 0, true, nsIWebProgressListener::STATE_START|nsIWebProgressListener::STATE_IS_DOCUMENT|nsIWebProgressListener::STATE_IS_NETWORK); + mOnStartSent = true; + } +} + +void nsPrintData::OnEndPrinting() +{ + DoOnProgressChange(100, 100, true, nsIWebProgressListener::STATE_STOP|nsIWebProgressListener::STATE_IS_DOCUMENT); + DoOnProgressChange(100, 100, true, nsIWebProgressListener::STATE_STOP|nsIWebProgressListener::STATE_IS_NETWORK); +} + +void +nsPrintData::DoOnProgressChange(int32_t aProgress, + int32_t aMaxProgress, + bool aDoStartStop, + int32_t aFlag) +{ + for (int32_t i=0;i<mPrintProgressListeners.Count();i++) { + nsIWebProgressListener* wpl = mPrintProgressListeners.ObjectAt(i); + wpl->OnProgressChange(nullptr, nullptr, aProgress, aMaxProgress, aProgress, aMaxProgress); + if (aDoStartStop) { + wpl->OnStateChange(nullptr, nullptr, aFlag, NS_OK); + } + } +} + diff --git a/layout/printing/nsPrintData.h b/layout/printing/nsPrintData.h new file mode 100644 index 000000000..fb4a88576 --- /dev/null +++ b/layout/printing/nsPrintData.h @@ -0,0 +1,103 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + +#ifndef nsPrintData_h___ +#define nsPrintData_h___ + +#include "mozilla/Attributes.h" + +// Interfaces +#include "nsIDOMWindow.h" +#include "nsDeviceContext.h" +#include "nsIPrintProgressParams.h" +#include "nsIPrintOptions.h" +#include "nsTArray.h" +#include "nsCOMArray.h" +#include "nsAutoPtr.h" + +// Classes +class nsPrintObject; +class nsPrintPreviewListener; +class nsIWebProgressListener; + +//------------------------------------------------------------------------ +// nsPrintData Class +// +// mPreparingForPrint - indicates that we have started Printing but +// have not gone to the timer to start printing the pages. It gets turned +// off right before we go to the timer. +// +// mDocWasToBeDestroyed - Gets set when "someone" tries to unload the document +// while we were prparing to Print. This typically happens if a user starts +// to print while a page is still loading. If they start printing and pause +// at the print dialog and then the page comes in, we then abort printing +// because the document is no longer stable. +// +//------------------------------------------------------------------------ +class nsPrintData { +public: + + typedef enum {eIsPrinting, eIsPrintPreview } ePrintDataType; + + // This enum tells indicates what the default should be for the title + // if the title from the document is null + enum eDocTitleDefault { + eDocTitleDefNone, + eDocTitleDefBlank, + eDocTitleDefURLDoc + }; + + + nsPrintData(ePrintDataType aType); + ~nsPrintData(); // non-virtual + + // Listener Helper Methods + void OnEndPrinting(); + void OnStartPrinting(); + void DoOnProgressChange(int32_t aProgress, + int32_t aMaxProgress, + bool aDoStartStop, + int32_t aFlag); + + + ePrintDataType mType; // the type of data this is (Printing or Print Preview) + nsRefPtr<nsDeviceContext> mPrintDC; + FILE *mDebugFilePtr; // a file where information can go to when printing + + nsPrintObject * mPrintObject; + nsPrintObject * mSelectedPO; + + nsCOMArray<nsIWebProgressListener> mPrintProgressListeners; + nsCOMPtr<nsIPrintProgressParams> mPrintProgressParams; + + nsCOMPtr<nsIDOMWindow> mCurrentFocusWin; // cache a pointer to the currently focused window + + nsTArray<nsPrintObject*> mPrintDocList; + bool mIsIFrameSelected; + bool mIsParentAFrameSet; + bool mOnStartSent; + bool mIsAborted; // tells us the document is being aborted + bool mPreparingForPrint; // see comments above + bool mDocWasToBeDestroyed; // see comments above + bool mShrinkToFit; + int16_t mPrintFrameType; + int32_t mNumPrintablePages; + int32_t mNumPagesPrinted; + float mShrinkRatio; + float mOrigDCScale; + + nsCOMPtr<nsIPrintSettings> mPrintSettings; + nsPrintPreviewListener* mPPEventListeners; + + PRUnichar* mBrandName; // needed as a substitute name for a document + +private: + nsPrintData() MOZ_DELETE; + nsPrintData& operator=(const nsPrintData& aOther) MOZ_DELETE; + +}; + +#endif /* nsPrintData_h___ */ + diff --git a/layout/printing/nsPrintEngine.cpp b/layout/printing/nsPrintEngine.cpp new file mode 100644 index 000000000..5593dd9ff --- /dev/null +++ b/layout/printing/nsPrintEngine.cpp @@ -0,0 +1,4043 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "nsPrintEngine.h" + +#include "nsIStringBundle.h" +#include "nsReadableUtils.h" +#include "nsCRT.h" + +#include "mozilla/Selection.h" +#include "nsIScriptGlobalObject.h" +#include "nsPIDOMWindow.h" +#include "nsIDocShell.h" +#include "nsIFrame.h" +#include "nsIURI.h" +#include "nsITextToSubURI.h" +#include "nsError.h" + +#include "nsView.h" +#include "nsAsyncDOMEvent.h" +#include <algorithm> + +// Print Options +#include "nsIPrintSettings.h" +#include "nsIPrintSettingsService.h" +#include "nsIPrintOptions.h" +#include "nsIPrintSession.h" +#include "nsGfxCIID.h" +#include "nsIServiceManager.h" +#include "nsGkAtoms.h" +#include "nsXPCOM.h" +#include "nsISupportsPrimitives.h" + +static const char sPrintSettingsServiceContractID[] = "@mozilla.org/gfx/printsettings-service;1"; + +// Printing Events +#include "nsPrintPreviewListener.h" +#include "nsThreadUtils.h" + +// Printing +#include "nsIWebBrowserPrint.h" +#include "nsIDOMHTMLFrameElement.h" +#include "nsIDOMHTMLFrameSetElement.h" +#include "nsIDOMHTMLIFrameElement.h" +#include "nsIDOMHTMLObjectElement.h" +#include "nsIDOMHTMLEmbedElement.h" + +// Print Preview +#include "imgIContainer.h" // image animation mode constants +#include "nsIWebBrowserPrint.h" // needed for PrintPreview Navigation constants + +// Print Progress +#include "nsIPrintProgress.h" +#include "nsIPrintProgressParams.h" +#include "nsIObserver.h" + +// Print error dialog +#include "nsIPrompt.h" +#include "nsIWindowWatcher.h" + +// Printing Prompts +#include "nsIPrintingPromptService.h" +static const char kPrintingPromptService[] = "@mozilla.org/embedcomp/printingprompt-service;1"; + +// Printing Timer +#include "nsPagePrintTimer.h" + +// FrameSet +#include "nsIDocument.h" + +// Focus +#include "nsISelectionController.h" + +// Misc +#include "nsISupportsUtils.h" +#include "nsIScriptContext.h" +#include "nsIDOMDocument.h" +#include "nsISelectionListener.h" +#include "nsISelectionPrivate.h" +#include "nsIDOMRange.h" +#include "nsContentCID.h" +#include "nsLayoutCID.h" +#include "nsContentUtils.h" +#include "nsIPresShell.h" +#include "nsLayoutUtils.h" +#include "mozilla/Preferences.h" + +#include "nsWidgetsCID.h" +#include "nsIDeviceContextSpec.h" +#include "nsViewManager.h" +#include "nsView.h" +#include "nsRenderingContext.h" + +#include "nsIPageSequenceFrame.h" +#include "nsIURL.h" +#include "nsIContentViewerEdit.h" +#include "nsIContentViewerFile.h" +#include "nsIMarkupDocumentViewer.h" +#include "nsIInterfaceRequestor.h" +#include "nsIInterfaceRequestorUtils.h" +#include "nsIDocShellTreeOwner.h" +#include "nsIWebBrowserChrome.h" +#include "nsIBaseWindow.h" +#include "nsILayoutHistoryState.h" +#include "nsFrameManager.h" +#include "nsGUIEvent.h" +#include "nsHTMLReflowState.h" +#include "nsIDOMHTMLAnchorElement.h" +#include "nsIDOMHTMLAreaElement.h" +#include "nsIDOMHTMLLinkElement.h" +#include "nsIDOMHTMLImageElement.h" +#include "nsIContentViewerContainer.h" +#include "nsIContentViewer.h" +#include "nsIDocumentViewerPrint.h" + +#include "nsFocusManager.h" +#include "nsRange.h" +#include "nsCDefaultURIFixup.h" +#include "nsIURIFixup.h" +#include "mozilla/dom/Element.h" +#include "nsContentList.h" + +using namespace mozilla; +using namespace mozilla::dom; + +//----------------------------------------------------- +// PR LOGGING +#ifdef MOZ_LOGGING +#define FORCE_PR_LOG /* Allow logging in the release build */ +#endif + +#include "prlog.h" + +#ifdef PR_LOGGING + +#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 + +#define DUMP_LAYOUT_LEVEL 9 // this turns on the dumping of each doucment's layout info + +static PRLogModuleInfo * +GetPrintingLog() +{ + static PRLogModuleInfo *sLog; + if (!sLog) + sLog = PR_NewLogModule("printing"); + return sLog; +} +#define PR_PL(_p1) PR_LOG(GetPrintingLog(), PR_LOG_DEBUG, _p1); + +#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"}; +static const char * gPrintFrameTypeStr[] = {"kNoFrames", "kFramesAsIs", "kSelectedFrame", "kEachFrameSep"}; +static const char * gFrameHowToEnableStr[] = {"kFrameEnableNone", "kFrameEnableAll", "kFrameEnableAsIsAndEach"}; +static const char * gPrintRangeStr[] = {"kRangeAllPages", "kRangeSpecifiedPageRange", "kRangeSelection", "kRangeFocusFrame"}; +#else +#define PRT_YESNO(_p) +#define PR_PL(_p1) +#endif + +#ifdef EXTENDED_DEBUG_PRINTING +// Forward Declarations +static void DumpPrintObjectsListStart(const char * aStr, nsTArray<nsPrintObject*> * aDocList); +static void DumpPrintObjectsTree(nsPrintObject * aPO, int aLevel= 0, FILE* aFD = nullptr); +static void DumpPrintObjectsTreeLayout(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); +#define DUMP_DOC_TREELAYOUT DumpPrintObjectsTreeLayout(mPrt->mPrintObject, mPrt->mPrintDC); +#else +#define DUMP_DOC_LIST(_title) +#define DUMP_DOC_TREE +#define DUMP_DOC_TREELAYOUT +#endif + +class nsScriptSuppressor +{ +public: + nsScriptSuppressor(nsPrintEngine* aPrintEngine) + : mPrintEngine(aPrintEngine), mSuppressed(false) {} + + ~nsScriptSuppressor() { Unsuppress(); } + + void Suppress() + { + if (mPrintEngine) { + mSuppressed = true; + mPrintEngine->TurnScriptingOn(false); + } + } + + void Unsuppress() + { + if (mPrintEngine && mSuppressed) { + mPrintEngine->TurnScriptingOn(true); + } + mSuppressed = false; + } + + void Disconnect() { mPrintEngine = nullptr; } +protected: + nsRefPtr<nsPrintEngine> mPrintEngine; + bool mSuppressed; +}; + +NS_IMPL_ISUPPORTS3(nsPrintEngine, nsIWebProgressListener, + nsISupportsWeakReference, nsIObserver) + +//--------------------------------------------------- +//-- nsPrintEngine Class Impl +//--------------------------------------------------- +nsPrintEngine::nsPrintEngine() : + mIsCreatingPrintPreview(false), + mIsDoingPrinting(false), + mIsDoingPrintPreview(false), + mProgressDialogIsShown(false), + mScreenDPI(115.0f), + mPrt(nullptr), + mPagePrintTimer(nullptr), + mPageSeqFrame(nullptr), + mPrtPreview(nullptr), + mOldPrtPreview(nullptr), + mDebugFile(nullptr), + mLoadCounter(0), + mDidLoadDataForPrinting(false), + mIsDestroying(false), + mDisallowSelectionPrint(false), + mNoMarginBoxes(false) +{ +} + +//------------------------------------------------------- +nsPrintEngine::~nsPrintEngine() +{ + Destroy(); // for insurance +} + +//------------------------------------------------------- +void nsPrintEngine::Destroy() +{ + if (mIsDestroying) { + return; + } + mIsDestroying = true; + + if (mPrt) { + delete mPrt; + mPrt = nullptr; + } + +#ifdef NS_PRINT_PREVIEW + if (mPrtPreview) { + delete mPrtPreview; + mPrtPreview = nullptr; + } + + // This is insruance + if (mOldPrtPreview) { + delete mOldPrtPreview; + mOldPrtPreview = nullptr; + } + +#endif + mDocViewerPrint = nullptr; +} + +//------------------------------------------------------- +void nsPrintEngine::DestroyPrintingData() +{ + if (mPrt) { + nsPrintData* data = mPrt; + mPrt = nullptr; + delete data; + } +} + +//--------------------------------------------------------------------------------- +//-- Section: Methods needed by the DocViewer +//--------------------------------------------------------------------------------- + +//-------------------------------------------------------- +nsresult nsPrintEngine::Initialize(nsIDocumentViewerPrint* aDocViewerPrint, + nsIWeakReference* aContainer, + nsIDocument* aDocument, + float aScreenDPI, + FILE* aDebugFile) +{ + NS_ENSURE_ARG_POINTER(aDocViewerPrint); + NS_ENSURE_ARG_POINTER(aContainer); + NS_ENSURE_ARG_POINTER(aDocument); + + mDocViewerPrint = aDocViewerPrint; + mContainer = aContainer; + mDocument = aDocument; + mScreenDPI = aScreenDPI; + + mDebugFile = aDebugFile; // ok to be NULL + + return NS_OK; +} + +//------------------------------------------------------- +bool +nsPrintEngine::CheckBeforeDestroy() +{ + if (mPrt && mPrt->mPreparingForPrint) { + mPrt->mDocWasToBeDestroyed = true; + return true; + } + return false; +} + +//------------------------------------------------------- +nsresult +nsPrintEngine::Cancelled() +{ + if (mPrt && mPrt->mPrintSettings) { + return mPrt->mPrintSettings->SetIsCancelled(true); + } + return NS_ERROR_FAILURE; +} + +//------------------------------------------------------- +// Install our event listeners on the document to prevent +// some events from being processed while in PrintPreview +// +// No return code - if this fails, there isn't much we can do +void +nsPrintEngine::InstallPrintPreviewListener() +{ + if (!mPrt->mPPEventListeners) { + nsCOMPtr<nsIDocShell> docShell = do_QueryReferent(mContainer); + nsCOMPtr<nsPIDOMWindow> win(do_GetInterface(docShell)); + if (win) { + nsCOMPtr<EventTarget> target = do_QueryInterface(win->GetFrameElementInternal()); + mPrt->mPPEventListeners = new nsPrintPreviewListener(target); + mPrt->mPPEventListeners->AddListeners(); + } + } +} + +//---------------------------------------------------------------------- +nsresult +nsPrintEngine::GetSeqFrameAndCountPagesInternal(nsPrintObject* aPO, + nsIFrame*& aSeqFrame, + int32_t& aCount) +{ + NS_ENSURE_ARG_POINTER(aPO); + + // Finds the SimplePageSequencer frame + nsIPageSequenceFrame* seqFrame = aPO->mPresShell->GetPageSequenceFrame(); + if (seqFrame) { + aSeqFrame = do_QueryFrame(seqFrame); + } else { + aSeqFrame = nullptr; + } + if (aSeqFrame == nullptr) return NS_ERROR_FAILURE; + + // first count the total number of pages + aCount = 0; + nsIFrame* pageFrame = aSeqFrame->GetFirstPrincipalChild(); + while (pageFrame != nullptr) { + aCount++; + pageFrame = pageFrame->GetNextSibling(); + } + + return NS_OK; + +} + +//----------------------------------------------------------------- +nsresult nsPrintEngine::GetSeqFrameAndCountPages(nsIFrame*& aSeqFrame, int32_t& aCount) +{ + NS_ASSERTION(mPrtPreview, "mPrtPreview can't be null!"); + return GetSeqFrameAndCountPagesInternal(mPrtPreview->mPrintObject, aSeqFrame, aCount); +} +//--------------------------------------------------------------------------------- +//-- Done: Methods needed by the DocViewer +//--------------------------------------------------------------------------------- + + +//--------------------------------------------------------------------------------- +//-- Section: nsIWebBrowserPrint +//--------------------------------------------------------------------------------- + +// Foward decl for Debug Helper Functions +#ifdef EXTENDED_DEBUG_PRINTING +static int RemoveFilesInDir(const char * aDir); +static void GetDocTitleAndURL(nsPrintObject* aPO, char *& aDocStr, char *& aURLStr); +static void DumpPrintObjectsTree(nsPrintObject * aPO, int aLevel, FILE* aFD); +static void DumpPrintObjectsList(nsTArray<nsPrintObject*> * aDocList); +static void RootFrameList(nsPresContext* aPresContext, FILE* out, int32_t aIndent); +static void DumpViews(nsIDocShell* aDocShell, FILE* out); +static void DumpLayoutData(char* aTitleStr, char* aURLStr, + nsPresContext* aPresContext, + nsDeviceContext * aDC, nsIFrame * aRootFrame, + nsIDocShell * aDocShell, FILE* aFD); +#endif + +//-------------------------------------------------------------------------------- + +nsresult +nsPrintEngine::CommonPrint(bool aIsPrintPreview, + nsIPrintSettings* aPrintSettings, + nsIWebProgressListener* aWebProgressListener, + nsIDOMDocument* aDoc) { + nsRefPtr<nsPrintEngine> kungfuDeathGrip = this; + nsresult rv = DoCommonPrint(aIsPrintPreview, aPrintSettings, + aWebProgressListener, aDoc); + if (NS_FAILED(rv)) { + if (aIsPrintPreview) { + SetIsCreatingPrintPreview(false); + SetIsPrintPreview(false); + } else { + SetIsPrinting(false); + } + if (mProgressDialogIsShown) + CloseProgressDialog(aWebProgressListener); + if (rv != NS_ERROR_ABORT && rv != NS_ERROR_OUT_OF_MEMORY) + ShowPrintErrorDialog(rv, !aIsPrintPreview); + delete mPrt; + mPrt = nullptr; + } + + return rv; +} + +nsresult +nsPrintEngine::DoCommonPrint(bool aIsPrintPreview, + nsIPrintSettings* aPrintSettings, + nsIWebProgressListener* aWebProgressListener, + nsIDOMDocument* aDoc) +{ + nsresult rv; + + 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; + + if (mIsDoingPrintPreview) { + mOldPrtPreview = mPrtPreview; + mPrtPreview = nullptr; + } + } else { + mProgressDialogIsShown = false; + } + + mPrt = new nsPrintData(aIsPrintPreview ? nsPrintData::eIsPrintPreview : + nsPrintData::eIsPrinting); + NS_ENSURE_TRUE(mPrt, NS_ERROR_OUT_OF_MEMORY); + + // if they don't pass in a PrintSettings, then get the Global PS + mPrt->mPrintSettings = aPrintSettings; + if (!mPrt->mPrintSettings) { + rv = GetGlobalPrintSettings(getter_AddRefs(mPrt->mPrintSettings)); + NS_ENSURE_SUCCESS(rv, rv); + } + + rv = CheckForPrinters(mPrt->mPrintSettings); + NS_ENSURE_SUCCESS(rv, rv); + + mPrt->mPrintSettings->SetIsCancelled(false); + mPrt->mPrintSettings->GetShrinkToFit(&mPrt->mShrinkToFit); + + // In the case the margin boxes are not printed store the print settings for + // the footer/header to be used as default print setting for follow up prints. + mPrt->mPrintSettings->SetPersistMarginBoxSettings(!mNoMarginBoxes); + + if (mNoMarginBoxes) { + // Set the footer/header to blank. + const PRUnichar* emptyString = EmptyString().get(); + mPrt->mPrintSettings->SetHeaderStrLeft(emptyString); + mPrt->mPrintSettings->SetHeaderStrCenter(emptyString); + mPrt->mPrintSettings->SetHeaderStrRight(emptyString); + mPrt->mPrintSettings->SetFooterStrLeft(emptyString); + mPrt->mPrintSettings->SetFooterStrCenter(emptyString); + mPrt->mPrintSettings->SetFooterStrRight(emptyString); + } + + if (aIsPrintPreview) { + SetIsCreatingPrintPreview(true); + SetIsPrintPreview(true); + nsCOMPtr<nsIMarkupDocumentViewer> viewer = + do_QueryInterface(mDocViewerPrint); + if (viewer) { + viewer->SetTextZoom(1.0f); + viewer->SetFullZoom(1.0f); + viewer->SetMinFontSize(0); + } + } + + // Create a print session and let the print settings know about it. + // 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; + if (!aIsPrintPreview) { + printSession = do_CreateInstance("@mozilla.org/gfx/printsession;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + mPrt->mPrintSettings->SetPrintSession(printSession); + } + + if (aWebProgressListener != nullptr) { + mPrt->mPrintProgressListeners.AppendObject(aWebProgressListener); + } + + // Get the currently focused window and cache it + // because the Print Dialog will "steal" focus and later when you try + // to get the currently focused windows it will be NULL + mPrt->mCurrentFocusWin = FindFocusedDOMWindow(); + + // Check to see if there is a "regular" selection + bool isSelection = IsThereARangeSelection(mPrt->mCurrentFocusWin); + + // Get the docshell for this documentviewer + nsCOMPtr<nsIDocShell> webContainer(do_QueryReferent(mContainer, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + { + if (aIsPrintPreview) { + nsCOMPtr<nsIContentViewer> viewer; + webContainer->GetContentViewer(getter_AddRefs(viewer)); + if (viewer && viewer->GetDocument() && viewer->GetDocument()->IsShowing()) { + viewer->GetDocument()->OnPageHide(false, nullptr); + } + } + + nsAutoScriptBlocker scriptBlocker; + mPrt->mPrintObject = new nsPrintObject(); + NS_ENSURE_TRUE(mPrt->mPrintObject, NS_ERROR_OUT_OF_MEMORY); + rv = mPrt->mPrintObject->Init(webContainer, aDoc, aIsPrintPreview); + NS_ENSURE_SUCCESS(rv, rv); + + NS_ENSURE_TRUE(mPrt->mPrintDocList.AppendElement(mPrt->mPrintObject), + NS_ERROR_OUT_OF_MEMORY); + + mPrt->mIsParentAFrameSet = IsParentAFrameSet(webContainer); + mPrt->mPrintObject->mFrameType = mPrt->mIsParentAFrameSet ? eFrameSet : eDoc; + + // Build the "tree" of PrintObjects + BuildDocTree(mPrt->mPrintObject->mDocShell, &mPrt->mPrintDocList, + mPrt->mPrintObject); + } + + if (!aIsPrintPreview) { + SetIsPrinting(true); + } + + // XXX This isn't really correct... + if (!mPrt->mPrintObject->mDocument || + !mPrt->mPrintObject->mDocument->GetRootElement()) + return NS_ERROR_GFX_PRINTER_STARTDOC; + + // Create the linkage from the sub-docs back to the content element + // in the parent document + MapContentToWebShells(mPrt->mPrintObject, mPrt->mPrintObject); + + mPrt->mIsIFrameSelected = IsThereAnIFrameSelected(webContainer, mPrt->mCurrentFocusWin, mPrt->mIsParentAFrameSet); + + // Setup print options for UI + if (mPrt->mIsParentAFrameSet) { + if (mPrt->mCurrentFocusWin) { + mPrt->mPrintSettings->SetHowToEnableFrameUI(nsIPrintSettings::kFrameEnableAll); + } else { + mPrt->mPrintSettings->SetHowToEnableFrameUI(nsIPrintSettings::kFrameEnableAsIsAndEach); + } + } else { + mPrt->mPrintSettings->SetHowToEnableFrameUI(nsIPrintSettings::kFrameEnableNone); + } + // Now determine how to set up the Frame print UI + mPrt->mPrintSettings->SetPrintOptions(nsIPrintSettings::kEnableSelectionRB, + isSelection || mPrt->mIsIFrameSelected); + + nsCOMPtr<nsIDeviceContextSpec> devspec + (do_CreateInstance("@mozilla.org/gfx/devicecontextspec;1", &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + nsScriptSuppressor scriptSuppressor(this); + if (!aIsPrintPreview) { +#ifdef DEBUG + mPrt->mDebugFilePtr = mDebugFile; +#endif + + scriptSuppressor.Suppress(); + bool printSilently; + mPrt->mPrintSettings->GetPrintSilent(&printSilently); + + // Check prefs for a default setting as to whether we should print silently + printSilently = + Preferences::GetBool("print.always_print_silent", printSilently); + + // 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 (!printSilently) { + nsCOMPtr<nsIPrintingPromptService> printPromptService(do_GetService(kPrintingPromptService)); + if (printPromptService) { + nsIDOMWindow *domWin = mDocument->GetWindow(); + NS_ENSURE_TRUE(domWin, NS_ERROR_FAILURE); + + // 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 + // + nsCOMPtr<nsIWebBrowserPrint> wbp(do_QueryInterface(mDocViewerPrint)); + rv = printPromptService->ShowPrintDialog(domWin, wbp, + mPrt->mPrintSettings); + // + // 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 (!mPrt) { + 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 (mPrt->mPrintSettings) { + // The user might have changed shrink-to-fit in the print dialog, so update our copy of its state + mPrt->mPrintSettings->GetShrinkToFit(&mPrt->mShrinkToFit); + } + } 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 { + // Call any code that requires a run of the event loop. + rv = mPrt->mPrintSettings->SetupSilentPrinting(); + } + // Check explicitly for abort because it's expected + if (rv == NS_ERROR_ABORT) + return rv; + NS_ENSURE_SUCCESS(rv, rv); + } + + rv = devspec->Init(nullptr, mPrt->mPrintSettings, aIsPrintPreview); + NS_ENSURE_SUCCESS(rv, rv); + + mPrt->mPrintDC = new nsDeviceContext(); + rv = mPrt->mPrintDC->InitForPrinting(devspec); + NS_ENSURE_SUCCESS(rv, rv); + + if (aIsPrintPreview) { + mPrt->mPrintSettings->SetPrintFrameType(nsIPrintSettings::kFramesAsIs); + + // override any UI that wants to PrintPreview any selection or page range + // we want to view every page in PrintPreview each time + mPrt->mPrintSettings->SetPrintRange(nsIPrintSettings::kRangeAllPages); + } else { + // Always check and set the print settings first and then fall back + // onto the PrintService if there isn't a PrintSettings + // + // Posiible Usage values: + // nsIPrintSettings::kUseInternalDefault + // nsIPrintSettings::kUseSettingWhenPossible + // + // NOTE: The consts are the same for PrintSettings and PrintSettings + int16_t printFrameTypeUsage = nsIPrintSettings::kUseSettingWhenPossible; + mPrt->mPrintSettings->GetPrintFrameTypeUsage(&printFrameTypeUsage); + + // Ok, see if we are going to use our value and override the default + if (printFrameTypeUsage == nsIPrintSettings::kUseSettingWhenPossible) { + // Get the Print Options/Settings PrintFrameType to see what is preferred + int16_t printFrameType = nsIPrintSettings::kEachFrameSep; + mPrt->mPrintSettings->GetPrintFrameType(&printFrameType); + + // Don't let anybody do something stupid like try to set it to + // kNoFrames when we are printing a FrameSet + if (printFrameType == nsIPrintSettings::kNoFrames) { + mPrt->mPrintFrameType = nsIPrintSettings::kEachFrameSep; + mPrt->mPrintSettings->SetPrintFrameType(mPrt->mPrintFrameType); + } else { + // First find out from the PrinService what options are available + // to us for Printing FrameSets + int16_t howToEnableFrameUI; + mPrt->mPrintSettings->GetHowToEnableFrameUI(&howToEnableFrameUI); + if (howToEnableFrameUI != nsIPrintSettings::kFrameEnableNone) { + switch (howToEnableFrameUI) { + case nsIPrintSettings::kFrameEnableAll: + mPrt->mPrintFrameType = printFrameType; + break; + + case nsIPrintSettings::kFrameEnableAsIsAndEach: + if (printFrameType != nsIPrintSettings::kSelectedFrame) { + mPrt->mPrintFrameType = printFrameType; + } else { // revert back to a good value + mPrt->mPrintFrameType = nsIPrintSettings::kEachFrameSep; + } + break; + } // switch + mPrt->mPrintSettings->SetPrintFrameType(mPrt->mPrintFrameType); + } + } + } else { + mPrt->mPrintSettings->GetPrintFrameType(&mPrt->mPrintFrameType); + } + } + + if (mPrt->mPrintFrameType == nsIPrintSettings::kEachFrameSep) { + CheckForChildFrameSets(mPrt->mPrintObject); + } + + if (NS_FAILED(EnablePOsForPrinting())) { + return NS_ERROR_FAILURE; + } + + // Attach progressListener to catch network requests. + nsCOMPtr<nsIWebProgress> webProgress = do_QueryInterface(mPrt->mPrintObject->mDocShell); + webProgress->AddProgressListener( + static_cast<nsIWebProgressListener*>(this), + nsIWebProgress::NOTIFY_STATE_REQUEST); + + mLoadCounter = 0; + mDidLoadDataForPrinting = false; + + if (aIsPrintPreview) { + bool notifyOnInit = false; + ShowPrintProgress(false, notifyOnInit); + + // Very important! Turn Off scripting + TurnScriptingOn(false); + + if (!notifyOnInit) { + InstallPrintPreviewListener(); + rv = InitPrintDocConstruction(false); + } else { + rv = NS_OK; + } + } else { + bool doNotify; + ShowPrintProgress(true, doNotify); + if (!doNotify) { + // Print listener setup... + mPrt->OnStartPrinting(); + + rv = InitPrintDocConstruction(false); + } + } + + // We will enable scripting later after printing has finished. + scriptSuppressor.Disconnect(); + + return NS_OK; +} + +//--------------------------------------------------------------------------------- +NS_IMETHODIMP +nsPrintEngine::Print(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. + nsCOMPtr<nsIDOMDocument> doc = + do_QueryInterface(mPrtPreview && mPrtPreview->mPrintObject ? + mPrtPreview->mPrintObject->mDocument : mDocument); + + return CommonPrint(false, aPrintSettings, aWebProgressListener, doc); +} + +NS_IMETHODIMP +nsPrintEngine::PrintPreview(nsIPrintSettings* aPrintSettings, + nsIDOMWindow *aChildDOMWin, + nsIWebProgressListener* aWebProgressListener) +{ + // Get the DocShell and see if it is busy + // (We can't Print Preview this document if it is still busy) + nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mContainer)); + NS_ENSURE_STATE(docShell); + + uint32_t busyFlags = nsIDocShell::BUSY_FLAGS_NONE; + if (NS_FAILED(docShell->GetBusyFlags(&busyFlags)) || + busyFlags != nsIDocShell::BUSY_FLAGS_NONE) { + CloseProgressDialog(aWebProgressListener); + ShowPrintErrorDialog(NS_ERROR_GFX_PRINTER_DOC_IS_BUSY, false); + return NS_ERROR_FAILURE; + } + + NS_ENSURE_STATE(aChildDOMWin); + nsCOMPtr<nsIDOMDocument> doc; + aChildDOMWin->GetDocument(getter_AddRefs(doc)); + NS_ENSURE_STATE(doc); + + // Document is not busy -- go ahead with the Print Preview + return CommonPrint(true, aPrintSettings, aWebProgressListener, doc); +} + +//---------------------------------------------------------------------------------- +/* readonly attribute boolean isFramesetDocument; */ +NS_IMETHODIMP +nsPrintEngine::GetIsFramesetDocument(bool *aIsFramesetDocument) +{ + nsCOMPtr<nsIDocShell> webContainer(do_QueryReferent(mContainer)); + *aIsFramesetDocument = IsParentAFrameSet(webContainer); + return NS_OK; +} + +//---------------------------------------------------------------------------------- +/* readonly attribute boolean isIFrameSelected; */ +NS_IMETHODIMP +nsPrintEngine::GetIsIFrameSelected(bool *aIsIFrameSelected) +{ + *aIsIFrameSelected = false; + + // Get the docshell for this documentviewer + nsCOMPtr<nsIDocShell> webContainer(do_QueryReferent(mContainer)); + // Get the currently focused window + nsCOMPtr<nsIDOMWindow> currentFocusWin = FindFocusedDOMWindow(); + if (currentFocusWin && webContainer) { + // Get whether the doc contains a frameset + // Also, check to see if the currently focus docshell + // is a child of this docshell + bool isParentFrameSet; + *aIsIFrameSelected = IsThereAnIFrameSelected(webContainer, currentFocusWin, isParentFrameSet); + } + return NS_OK; +} + +//---------------------------------------------------------------------------------- +/* readonly attribute boolean isRangeSelection; */ +NS_IMETHODIMP +nsPrintEngine::GetIsRangeSelection(bool *aIsRangeSelection) +{ + // Get the currently focused window + nsCOMPtr<nsIDOMWindow> currentFocusWin = FindFocusedDOMWindow(); + *aIsRangeSelection = IsThereARangeSelection(currentFocusWin); + return NS_OK; +} + +//---------------------------------------------------------------------------------- +/* readonly attribute boolean isFramesetFrameSelected; */ +NS_IMETHODIMP +nsPrintEngine::GetIsFramesetFrameSelected(bool *aIsFramesetFrameSelected) +{ + // Get the currently focused window + nsCOMPtr<nsIDOMWindow> currentFocusWin = FindFocusedDOMWindow(); + *aIsFramesetFrameSelected = currentFocusWin != nullptr; + return NS_OK; +} + +//---------------------------------------------------------------------------------- +/* readonly attribute long printPreviewNumPages; */ +NS_IMETHODIMP +nsPrintEngine::GetPrintPreviewNumPages(int32_t *aPrintPreviewNumPages) +{ + NS_ENSURE_ARG_POINTER(aPrintPreviewNumPages); + + nsPrintData* prt = nullptr; + nsIFrame* seqFrame = nullptr; + *aPrintPreviewNumPages = 0; + + // When calling this function, the FinishPrintPreview() function might not + // been called as there are still some + if (mPrtPreview) { + prt = mPrtPreview; + } else { + prt = mPrt; + } + if ((!prt) || + NS_FAILED(GetSeqFrameAndCountPagesInternal(prt->mPrintObject, seqFrame, *aPrintPreviewNumPages))) { + return NS_ERROR_FAILURE; + } + return NS_OK; +} + +//---------------------------------------------------------------------------------- +// Enumerate all the documents for their titles +NS_IMETHODIMP +nsPrintEngine::EnumerateDocumentNames(uint32_t* aCount, + PRUnichar*** aResult) +{ + NS_ENSURE_ARG(aCount); + NS_ENSURE_ARG_POINTER(aResult); + + *aCount = 0; + *aResult = nullptr; + + int32_t numDocs = mPrt->mPrintDocList.Length(); + PRUnichar** array = (PRUnichar**) nsMemory::Alloc(numDocs * sizeof(PRUnichar*)); + if (!array) + return NS_ERROR_OUT_OF_MEMORY; + + for (int32_t i=0;i<numDocs;i++) { + nsPrintObject* po = mPrt->mPrintDocList.ElementAt(i); + NS_ASSERTION(po, "nsPrintObject can't be null!"); + PRUnichar * docTitleStr; + PRUnichar * docURLStr; + GetDocumentTitleAndURL(po->mDocument, &docTitleStr, &docURLStr); + + // Use the URL if the doc is empty + if (!docTitleStr || !*docTitleStr) { + if (docURLStr && *docURLStr) { + nsMemory::Free(docTitleStr); + docTitleStr = docURLStr; + } else { + nsMemory::Free(docURLStr); + } + docURLStr = nullptr; + if (!docTitleStr || !*docTitleStr) { + CleanupDocTitleArray(array, i); + return NS_ERROR_OUT_OF_MEMORY; + } + } + array[i] = docTitleStr; + if (docURLStr) nsMemory::Free(docURLStr); + } + *aCount = numDocs; + *aResult = array; + + return NS_OK; + +} + +//---------------------------------------------------------------------------------- +/* readonly attribute nsIPrintSettings globalPrintSettings; */ +nsresult +nsPrintEngine::GetGlobalPrintSettings(nsIPrintSettings **aGlobalPrintSettings) +{ + NS_ENSURE_ARG_POINTER(aGlobalPrintSettings); + + nsresult rv = NS_ERROR_FAILURE; + nsCOMPtr<nsIPrintSettingsService> printSettingsService = + do_GetService(sPrintSettingsServiceContractID, &rv); + if (NS_SUCCEEDED(rv)) { + rv = printSettingsService->GetGlobalPrintSettings(aGlobalPrintSettings); + } + return rv; +} + +//---------------------------------------------------------------------------------- +/* readonly attribute boolean doingPrint; */ +NS_IMETHODIMP +nsPrintEngine::GetDoingPrint(bool *aDoingPrint) +{ + NS_ENSURE_ARG_POINTER(aDoingPrint); + *aDoingPrint = mIsDoingPrinting; + return NS_OK; +} + +//---------------------------------------------------------------------------------- +/* readonly attribute boolean doingPrintPreview; */ +NS_IMETHODIMP +nsPrintEngine::GetDoingPrintPreview(bool *aDoingPrintPreview) +{ + NS_ENSURE_ARG_POINTER(aDoingPrintPreview); + *aDoingPrintPreview = mIsDoingPrintPreview; + return NS_OK; +} + +//---------------------------------------------------------------------------------- +/* readonly attribute nsIPrintSettings currentPrintSettings; */ +NS_IMETHODIMP +nsPrintEngine::GetCurrentPrintSettings(nsIPrintSettings * *aCurrentPrintSettings) +{ + NS_ENSURE_ARG_POINTER(aCurrentPrintSettings); + + if (mPrt) { + *aCurrentPrintSettings = mPrt->mPrintSettings; + + } else if (mPrtPreview) { + *aCurrentPrintSettings = mPrtPreview->mPrintSettings; + + } else { + *aCurrentPrintSettings = nullptr; + } + NS_IF_ADDREF(*aCurrentPrintSettings); + return NS_OK; +} + +//----------------------------------------------------------------- +//-- Section: Pre-Reflow Methods +//----------------------------------------------------------------- + +//--------------------------------------------------------------------- +// This method checks to see if there is at least one printer defined +// and if so, it sets the first printer in the list as the default name +// in the PrintSettings which is then used for Printer Preview +nsresult +nsPrintEngine::CheckForPrinters(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 + nsXPIDLString printerName; + nsresult rv = aPrintSettings->GetPrinterName(getter_Copies(printerName)); + if (NS_SUCCEEDED(rv) && !printerName.IsEmpty()) { + return NS_OK; + } + + // aPrintSettings doesn't have a printer set. Try to fetch the default. + nsCOMPtr<nsIPrintSettingsService> printSettingsService = + do_GetService(sPrintSettingsServiceContractID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + rv = printSettingsService->GetDefaultPrinterName(getter_Copies(printerName)); + if (NS_SUCCEEDED(rv) && !printerName.IsEmpty()) { + rv = aPrintSettings->SetPrinterName(printerName.get()); + } + return rv; +#endif +} + +//---------------------------------------------------------------------- +// Set up to use the "pluggable" Print Progress Dialog +void +nsPrintEngine::ShowPrintProgress(bool aIsForPrinting, bool& aDoNotify) +{ + // 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; + + // Assume we can't do progress and then see if we can + bool showProgresssDialog = false; + + // if it is already being shown then don't bother to find out if it should be + // so skip this and leave mShowProgressDialog set to FALSE + if (!mProgressDialogIsShown) { + showProgresssDialog = Preferences::GetBool("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) { + mPrt->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) { + nsPIDOMWindow *domWin = mDocument->GetWindow(); + if (!domWin) return; + + nsCOMPtr<nsIDocShell> docShell = domWin->GetDocShell(); + if (!docShell) return; + nsCOMPtr<nsIDocShellTreeOwner> owner; + docShell->GetTreeOwner(getter_AddRefs(owner)); + nsCOMPtr<nsIWebBrowserChrome> browserChrome = do_GetInterface(owner); + if (!browserChrome) return; + bool isModal = true; + browserChrome->IsWindowModal(&isModal); + if (isModal) { + // Showing a print progress dialog when printing a modal window + // isn't supported. See bug 301560. + return; + } + + nsCOMPtr<nsIWebProgressListener> printProgressListener; + + nsCOMPtr<nsIWebBrowserPrint> wbp(do_QueryInterface(mDocViewerPrint)); + nsresult rv = printPromptService->ShowProgress(domWin, wbp, mPrt->mPrintSettings, this, aIsForPrinting, + getter_AddRefs(printProgressListener), + getter_AddRefs(mPrt->mPrintProgressParams), + &aDoNotify); + if (NS_SUCCEEDED(rv)) { + if (printProgressListener && mPrt->mPrintProgressParams) { + mPrt->mPrintProgressListeners.AppendObject(printProgressListener); + SetDocAndURLIntoProgress(mPrt->mPrintObject, mPrt->mPrintProgressParams); + } + } + } + } +} + +//--------------------------------------------------------------------- +bool +nsPrintEngine::IsThereARangeSelection(nsIDOMWindow* aDOMWin) +{ + if (mDisallowSelectionPrint) + return false; + + nsCOMPtr<nsIPresShell> presShell; + if (aDOMWin) { + nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(aDOMWin)); + presShell = window->GetDocShell()->GetPresShell(); + } + + if (!presShell) + return false; + + // check here to see if there is a range selection + // so we know whether to turn on the "Selection" radio button + Selection* selection = + presShell->GetCurrentSelection(nsISelectionController::SELECTION_NORMAL); + if (!selection) { + return false; + } + + int32_t rangeCount = selection->GetRangeCount(); + if (!rangeCount) { + return false; + } + + if (rangeCount > 1) { + return true; + } + + // check to make sure it isn't an insertion selection + return selection->GetRangeAt(0) && !selection->IsCollapsed(); +} + +//--------------------------------------------------------------------- +bool +nsPrintEngine::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<nsIDOMDocument> domDoc = do_GetInterface(aParent); + nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc); + if (doc) { + nsIContent *rootElement = doc->GetRootElement(); + if (rootElement) { + isFrameSet = HasFramesetChild(rootElement); + } + } + return isFrameSet; +} + + +//--------------------------------------------------------------------- +// Recursively build a list of sub documents to be printed +// that mirrors the document tree +void +nsPrintEngine::BuildDocTree(nsIDocShellTreeNode * aParentNode, + nsTArray<nsPrintObject*> * aDocList, + nsPrintObject * aPO) +{ + NS_ASSERTION(aParentNode, "Pointer is null!"); + NS_ASSERTION(aDocList, "Pointer is null!"); + NS_ASSERTION(aPO, "Pointer is null!"); + + int32_t childWebshellCount; + aParentNode->GetChildCount(&childWebshellCount); + if (childWebshellCount > 0) { + for (int32_t i=0;i<childWebshellCount;i++) { + nsCOMPtr<nsIDocShellTreeItem> child; + aParentNode->GetChildAt(i, getter_AddRefs(child)); + nsCOMPtr<nsIDocShell> childAsShell(do_QueryInterface(child)); + + nsCOMPtr<nsIContentViewer> viewer; + childAsShell->GetContentViewer(getter_AddRefs(viewer)); + if (viewer) { + nsCOMPtr<nsIContentViewerFile> viewerFile(do_QueryInterface(viewer)); + if (viewerFile) { + nsCOMPtr<nsIDocShell> childDocShell(do_QueryInterface(child)); + nsCOMPtr<nsIDocShellTreeNode> childNode(do_QueryInterface(child)); + nsCOMPtr<nsIDOMDocument> doc = do_GetInterface(childDocShell); + nsPrintObject * po = new nsPrintObject(); + po->mParent = aPO; + nsresult rv = po->Init(childDocShell, doc, aPO->mPrintPreview); + if (NS_FAILED(rv)) + NS_NOTREACHED("Init failed?"); + aPO->mKids.AppendElement(po); + aDocList->AppendElement(po); + BuildDocTree(childNode, aDocList, po); + } + } + } + } +} + +//--------------------------------------------------------------------- +void +nsPrintEngine::GetDocumentTitleAndURL(nsIDocument* aDoc, + PRUnichar** aTitle, + PRUnichar** aURLStr) +{ + NS_ASSERTION(aDoc, "Pointer is null!"); + NS_ASSERTION(aTitle, "Pointer is null!"); + NS_ASSERTION(aURLStr, "Pointer is null!"); + + *aTitle = nullptr; + *aURLStr = nullptr; + + nsAutoString docTitle; + nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(aDoc); + doc->GetTitle(docTitle); + if (!docTitle.IsEmpty()) { + *aTitle = ToNewUnicode(docTitle); + } + + nsIURI* url = aDoc->GetDocumentURI(); + if (!url) return; + + nsCOMPtr<nsIURIFixup> urifixup(do_GetService(NS_URIFIXUP_CONTRACTID)); + if (!urifixup) return; + + nsCOMPtr<nsIURI> exposableURI; + urifixup->CreateExposableURI(url, getter_AddRefs(exposableURI)); + + if (!exposableURI) return; + + nsAutoCString urlCStr; + exposableURI->GetSpec(urlCStr); + + nsresult rv; + nsCOMPtr<nsITextToSubURI> textToSubURI = + do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv); + if (NS_FAILED(rv)) return; + + nsAutoString unescapedURI; + rv = textToSubURI->UnEscapeURIForUI(NS_LITERAL_CSTRING("UTF-8"), + urlCStr, unescapedURI); + if (NS_FAILED(rv)) return; + + *aURLStr = ToNewUnicode(unescapedURI); +} + +//--------------------------------------------------------------------- +// The walks the PO tree and for each document it walks the content +// tree looking for any content that are sub-shells +// +// It then sets the mContent pointer in the "found" PO object back to the +// the document that contained it. +void +nsPrintEngine::MapContentToWebShells(nsPrintObject* aRootPO, + nsPrintObject* aPO) +{ + NS_ASSERTION(aRootPO, "Pointer is null!"); + NS_ASSERTION(aPO, "Pointer is null!"); + + // Recursively walk the content from the root item + // XXX Would be faster to enumerate the subdocuments, although right now + // nsIDocument doesn't expose quite what would be needed. + nsCOMPtr<nsIContentViewer> viewer; + aPO->mDocShell->GetContentViewer(getter_AddRefs(viewer)); + if (!viewer) return; + + nsCOMPtr<nsIDOMDocument> domDoc; + viewer->GetDOMDocument(getter_AddRefs(domDoc)); + nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc); + if (!doc) return; + + Element* rootElement = doc->GetRootElement(); + if (rootElement) { + MapContentForPO(aPO, rootElement); + } else { + NS_WARNING("Null root content on (sub)document."); + } + + // Continue recursively walking the chilren of this PO + for (uint32_t i=0;i<aPO->mKids.Length();i++) { + MapContentToWebShells(aRootPO, aPO->mKids[i]); + } + +} + +//------------------------------------------------------- +// A Frame's sub-doc may contain content or a FrameSet +// When it contains a FrameSet the mFrameType for the PrintObject +// is always set to an eFrame. Which is fine when printing "AsIs" +// but is incorrect when when printing "Each Frame Separately". +// When printing "Each Frame Separately" the Frame really acts like +// a frameset. +// +// This method walks the PO tree and checks to see if the PrintObject is +// an eFrame and has children that are eFrames (meaning it's a Frame containing a FrameSet) +// If so, then the mFrameType need to be changed to eFrameSet +// +// Also note: We only want to call this we are printing "Each Frame Separately" +// when printing "As Is" leave it as an eFrame +void +nsPrintEngine::CheckForChildFrameSets(nsPrintObject* aPO) +{ + NS_ASSERTION(aPO, "Pointer is null!"); + + // Continue recursively walking the chilren of this PO + bool hasChildFrames = false; + for (uint32_t i=0;i<aPO->mKids.Length();i++) { + nsPrintObject* po = aPO->mKids[i]; + if (po->mFrameType == eFrame) { + hasChildFrames = true; + CheckForChildFrameSets(po); + } + } + + if (hasChildFrames && aPO->mFrameType == eFrame) { + aPO->mFrameType = eFrameSet; + } +} + +//--------------------------------------------------------------------- +// This method is key to the entire print mechanism. +// +// This "maps" or figures out which sub-doc represents a +// given Frame or IFrame in its parent sub-doc. +// +// So the Mcontent pointer in the child sub-doc points to the +// content in the its parent document, that caused it to be printed. +// This is used later to (after reflow) to find the absolute location +// of the sub-doc on its parent's page frame so it can be +// printed in the correct location. +// +// This method recursvely "walks" the content for a document finding +// all the Frames and IFrames, then sets the "mFrameType" data member +// which tells us what type of PO we have +void +nsPrintEngine::MapContentForPO(nsPrintObject* aPO, + nsIContent* aContent) +{ + NS_PRECONDITION(aPO && aContent, "Null argument"); + + nsIDocument* doc = aContent->GetDocument(); + + NS_ASSERTION(doc, "Content without a document from a document tree?"); + + nsIDocument* subDoc = doc->GetSubDocumentFor(aContent); + + if (subDoc) { + nsCOMPtr<nsISupports> container = subDoc->GetContainer(); + nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(container)); + + if (docShell) { + nsPrintObject * po = nullptr; + int32_t cnt = aPO->mKids.Length(); + for (int32_t i=0;i<cnt;i++) { + nsPrintObject* kid = aPO->mKids.ElementAt(i); + if (kid->mDocument == subDoc) { + po = kid; + break; + } + } + + // XXX If a subdocument has no onscreen presentation, there will be no PO + // This is even if there should be a print presentation + if (po) { + + nsCOMPtr<nsIDOMHTMLFrameElement> frame(do_QueryInterface(aContent)); + // "frame" elements not in a frameset context should be treated + // as iframes + if (frame && po->mParent->mFrameType == eFrameSet) { + po->mFrameType = eFrame; + } else { + // Assume something iframe-like, i.e. iframe, object, or embed + po->mFrameType = eIFrame; + SetPrintAsIs(po, true); + NS_ASSERTION(po->mParent, "The root must be a parent"); + po->mParent->mPrintAsIs = true; + } + } + } + } + + // walk children content + for (nsIContent* child = aContent->GetFirstChild(); + child; + child = child->GetNextSibling()) { + MapContentForPO(aPO, child); + } +} + +//--------------------------------------------------------------------- +bool +nsPrintEngine::IsThereAnIFrameSelected(nsIDocShell* aDocShell, + nsIDOMWindow* aDOMWin, + bool& aIsParentFrameSet) +{ + aIsParentFrameSet = IsParentAFrameSet(aDocShell); + bool iFrameIsSelected = false; + if (mPrt && mPrt->mPrintObject) { + nsPrintObject* po = FindPrintObjectByDOMWin(mPrt->mPrintObject, aDOMWin); + iFrameIsSelected = po && po->mFrameType == eIFrame; + } else { + // First, check to see if we are a frameset + if (!aIsParentFrameSet) { + // Check to see if there is a currenlt focused frame + // if so, it means the selected frame is either the main docshell + // or an IFRAME + if (aDOMWin) { + // Get the main docshell's DOMWin to see if it matches + // the frame that is selected + nsCOMPtr<nsIDOMWindow> domWin = do_GetInterface(aDocShell); + if (domWin != aDOMWin) { + iFrameIsSelected = true; // we have a selected IFRAME + } + } + } + } + + return iFrameIsSelected; +} + +//--------------------------------------------------------------------- +// Recursively sets all the PO items to be printed +// from the given item down into the tree +void +nsPrintEngine::SetPrintPO(nsPrintObject* aPO, bool aPrint) +{ + NS_ASSERTION(aPO, "Pointer is null!"); + + // Set whether to print flag + aPO->mDontPrint = !aPrint; + + for (uint32_t i=0;i<aPO->mKids.Length();i++) { + SetPrintPO(aPO->mKids[i], aPrint); + } +} + +//--------------------------------------------------------------------- +// This will first use a Title and/or URL from the PrintSettings +// if one isn't set then it uses the one from the document +// then if not title is there we will make sure we send something back +// depending on the situation. +void +nsPrintEngine::GetDisplayTitleAndURL(nsPrintObject* aPO, + PRUnichar** aTitle, + PRUnichar** aURLStr, + eDocTitleDefault aDefType) +{ + NS_ASSERTION(aPO, "Pointer is null!"); + NS_ASSERTION(aTitle, "Pointer is null!"); + NS_ASSERTION(aURLStr, "Pointer is null!"); + + *aTitle = nullptr; + *aURLStr = nullptr; + + if (!mPrt) + return; + + // First check to see if the PrintSettings has defined an alternate title + // and use that if it did + PRUnichar * docTitleStrPS = nullptr; + PRUnichar * docURLStrPS = nullptr; + if (mPrt->mPrintSettings) { + mPrt->mPrintSettings->GetTitle(&docTitleStrPS); + mPrt->mPrintSettings->GetDocURL(&docURLStrPS); + + if (docTitleStrPS && *docTitleStrPS) { + *aTitle = docTitleStrPS; + } + + if (docURLStrPS && *docURLStrPS) { + *aURLStr = docURLStrPS; + } + + // short circut + if (docTitleStrPS && docURLStrPS) { + return; + } + } + + PRUnichar* docTitle; + PRUnichar* docUrl; + GetDocumentTitleAndURL(aPO->mDocument, &docTitle, &docUrl); + + if (docUrl) { + if (!docURLStrPS) + *aURLStr = docUrl; + else + nsMemory::Free(docUrl); + } + + if (docTitle) { + if (!docTitleStrPS) + *aTitle = docTitle; + else + nsMemory::Free(docTitle); + } else if (!docTitleStrPS) { + switch (aDefType) { + case eDocTitleDefBlank: *aTitle = ToNewUnicode(EmptyString()); + break; + + case eDocTitleDefURLDoc: + if (*aURLStr) { + *aTitle = NS_strdup(*aURLStr); + } else if (mPrt->mBrandName) { + *aTitle = NS_strdup(mPrt->mBrandName); + } + break; + case eDocTitleDefNone: + // *aTitle defaults to nullptr + break; + } + } +} + +//--------------------------------------------------------------------- +nsresult nsPrintEngine::DocumentReadyForPrinting() +{ + if (mPrt->mPrintFrameType == nsIPrintSettings::kEachFrameSep) { + CheckForChildFrameSets(mPrt->mPrintObject); + } + + // + // 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 + DonePrintingPages(nullptr, rv); + } + return rv; +} + +/** --------------------------------------------------- + * Cleans up when an error occurred + */ +nsresult nsPrintEngine::CleanupOnFailure(nsresult aResult, bool aIsPrinting) +{ + PR_PL(("**** Failed %s - rv 0x%X", aIsPrinting?"Printing":"Print Preview", aResult)); + + /* cleanup... */ + if (mPagePrintTimer) { + mPagePrintTimer->Stop(); + NS_RELEASE(mPagePrintTimer); + } + + if (aIsPrinting) { + SetIsPrinting(false); + } else { + SetIsPrintPreview(false); + SetIsCreatingPrintPreview(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) { + ShowPrintErrorDialog(aResult, aIsPrinting); + } + + FirePrintCompletionEvent(); + + return aResult; + +} + +//--------------------------------------------------------------------- +void +nsPrintEngine::ShowPrintErrorDialog(nsresult aPrintError, bool aIsPrinting) +{ + nsAutoCString stringName; + nsXPIDLString msg, title; + nsresult rv = NS_OK; + + switch(aPrintError) + { +#define ENTITY_FOR_ERROR(label) \ + case NS_ERROR_##label: stringName.AssignLiteral("PERR_" #label); break + + ENTITY_FOR_ERROR(GFX_PRINTER_NO_PRINTER_AVAILABLE); + ENTITY_FOR_ERROR(GFX_PRINTER_NAME_NOT_FOUND); + ENTITY_FOR_ERROR(GFX_PRINTER_COULD_NOT_OPEN_FILE); + ENTITY_FOR_ERROR(GFX_PRINTER_STARTDOC); + ENTITY_FOR_ERROR(GFX_PRINTER_ENDDOC); + ENTITY_FOR_ERROR(GFX_PRINTER_STARTPAGE); + ENTITY_FOR_ERROR(GFX_PRINTER_DOC_IS_BUSY); + ENTITY_FOR_ERROR(GFX_PRINTER_NO_XUL); // bug 136185 / bug 240490 + + ENTITY_FOR_ERROR(ABORT); + ENTITY_FOR_ERROR(NOT_AVAILABLE); + ENTITY_FOR_ERROR(NOT_IMPLEMENTED); + ENTITY_FOR_ERROR(OUT_OF_MEMORY); + ENTITY_FOR_ERROR(UNEXPECTED); + + default: + ENTITY_FOR_ERROR(FAILURE); + +#undef ENTITY_FOR_ERROR + } + + if (!aIsPrinting) { + // Try first with _PP suffix. + stringName.AppendLiteral("_PP"); + rv = nsContentUtils::GetLocalizedString( + nsContentUtils::ePRINTING_PROPERTIES, stringName.get(), msg); + if (NS_FAILED(rv)) { + stringName.Truncate(stringName.Length() - 3); + } + } + if (aIsPrinting || NS_FAILED(rv)) { + rv = nsContentUtils::GetLocalizedString( + nsContentUtils::ePRINTING_PROPERTIES, stringName.get(), msg); + } + if (NS_FAILED(rv)) { + return; + } + + rv = nsContentUtils::GetLocalizedString( + nsContentUtils::ePRINTING_PROPERTIES, + aIsPrinting ? "print_error_dialog_title" + : "printpreview_error_dialog_title", + title); + if (NS_FAILED(rv)) { + return; + } + + nsCOMPtr<nsIWindowWatcher> wwatch = + do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv); + if (NS_FAILED(rv)) { + return; + } + + nsCOMPtr<nsIDOMWindow> active; + wwatch->GetActiveWindow(getter_AddRefs(active)); + + nsCOMPtr<nsIPrompt> dialog; + /* |GetNewPrompter| allows that |active| is |nullptr| + * (see bug 234982 ("nsPrintEngine::ShowPrintErrorDialog() fails in many cases")) */ + wwatch->GetNewPrompter(active, getter_AddRefs(dialog)); + if (!dialog) { + return; + } + + dialog->Alert(title.get(), msg.get()); +} + +//----------------------------------------------------------------- +//-- Section: Reflow Methods +//----------------------------------------------------------------- + +nsresult +nsPrintEngine::ReconstructAndReflow(bool doSetPixelScale) +{ +#if (defined(XP_WIN) || defined(XP_OS2)) && 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 (kPrintingLogMod && kPrintingLogMod->level == DUMP_LAYOUT_LEVEL) { + RemoveFilesInDir(".\\"); + gDumpFileNameCnt = 0; + gDumpLOFileNameCnt = 0; + } +#endif + + 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->mDontPrint || po->mInvisible) { + continue; + } + + UpdateZoomRatio(po, doSetPixelScale); + + po->mPresContext->SetPageScale(po->mZoomRatio); + + // Calculate scale factor from printer to screen + float printDPI = float(mPrt->mPrintDC->AppUnitsPerCSSInch()) / + float(mPrt->mPrintDC->AppUnitsPerDevPixel()); + po->mPresContext->SetPrintPreviewScale(mScreenDPI / printDPI); + + po->mPresShell->ReconstructFrames(); + + // 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; + } + } + + po->mPresShell->FlushPendingNotifications(Flush_Layout); + + nsresult rv = UpdateSelectionAndShrinkPrintObject(po, documentIsTopLevel); + NS_ENSURE_SUCCESS(rv, rv); + } + return NS_OK; +} + +//------------------------------------------------------- +nsresult +nsPrintEngine::SetupToPrintContent() +{ + nsresult rv; + + bool didReconstruction = false; + + // If some new content got loaded since the initial reflow rebuild + // everything. + if (mDidLoadDataForPrinting) { + rv = ReconstructAndReflow(DoSetPixelScale()); + didReconstruction = true; + NS_ENSURE_SUCCESS(rv, rv); + } + + // 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 (mPrt->mShrinkToFit && !ppIsShrinkToFit) { + // Now look for the PO that has the smallest percent for shrink to fit + if (mPrt->mPrintDocList.Length() > 1 && mPrt->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 + mPrt->mShrinkRatio = smallestPO->mShrinkRatio; + } + } else { + // Single document so use the Shrink as calculated for the PO + mPrt->mShrinkRatio = mPrt->mPrintObject->mShrinkRatio; + } + + if (mPrt->mShrinkRatio < 0.998f) { + rv = ReconstructAndReflow(true); + didReconstruction = true; + NS_ENSURE_SUCCESS(rv, rv); + } + +#ifdef PR_LOGGING + float calcRatio = 0.0f; + if (mPrt->mPrintDocList.Length() > 1 && mPrt->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 = mPrt->mPrintObject->mShrinkRatio; + } + PR_PL(("**************************************************************************\n")); + PR_PL(("STF Ratio is: %8.5f Effective Ratio: %8.5f Diff: %8.5f\n", mPrt->mShrinkRatio, calcRatio, mPrt->mShrinkRatio-calcRatio)); + PR_PL(("**************************************************************************\n")); +#endif + } + + // If the frames got reconstructed and reflowed the number of pages might + // has changed. + if (didReconstruction) { + FirePrintPreviewUpdateEvent(); + } + + DUMP_DOC_LIST(("\nAfter Reflow------------------------------------------")); + PR_PL(("\n")); + PR_PL(("-------------------------------------------------------\n")); + PR_PL(("\n")); + + CalcNumPrintablePages(mPrt->mNumPrintablePages); + + PR_PL(("--- Printing %d pages\n", mPrt->mNumPrintablePages)); + DUMP_DOC_TREELAYOUT; + + // Print listener setup... + if (mPrt != nullptr) { + mPrt->OnStartPrinting(); + } + + PRUnichar* fileName = nullptr; + // check to see if we are printing to a file + bool isPrintToFile = false; + mPrt->mPrintSettings->GetPrintToFile(&isPrintToFile); + if (isPrintToFile) { + // On some platforms The BeginDocument needs to know the name of the file + // and it uses the PrintService to get it, so we need to set it into the PrintService here + mPrt->mPrintSettings->GetToFileName(&fileName); + } + + PRUnichar * docTitleStr; + PRUnichar * docURLStr; + GetDisplayTitleAndURL(mPrt->mPrintObject, &docTitleStr, &docURLStr, eDocTitleDefURLDoc); + + int32_t startPage = 1; + int32_t endPage = mPrt->mNumPrintablePages; + + int16_t printRangeType = nsIPrintSettings::kRangeAllPages; + mPrt->mPrintSettings->GetPrintRange(&printRangeType); + if (printRangeType == nsIPrintSettings::kRangeSpecifiedPageRange) { + mPrt->mPrintSettings->GetStartPageRange(&startPage); + mPrt->mPrintSettings->GetEndPageRange(&endPage); + if (endPage > mPrt->mNumPrintablePages) { + endPage = mPrt->mNumPrintablePages; + } + } + + 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 (!mPrt->mDebugFilePtr && mIsDoingPrinting) { + rv = mPrt->mPrintDC->BeginDocument(docTitleStr, fileName, startPage, endPage); + } + + if (mIsCreatingPrintPreview) { + // Print Preview -- Pass ownership of docTitleStr and docURLStr + // to the pageSequenceFrame, to be displayed in the header + nsIPageSequenceFrame *seqFrame = mPrt->mPrintObject->mPresShell->GetPageSequenceFrame(); + if (seqFrame) { + seqFrame->StartPrint(mPrt->mPrintObject->mPresContext, + mPrt->mPrintSettings, docTitleStr, docURLStr); + docTitleStr = nullptr; + docURLStr = nullptr; + } + } + if (docTitleStr) nsMemory::Free(docTitleStr); + if (docURLStr) nsMemory::Free(docURLStr); + + PR_PL(("****************** Begin Document ************************\n")); + + NS_ENSURE_SUCCESS(rv, rv); + + // This will print the docshell document + // when it completes asynchronously in the DonePrintingPages method + // it will check to see if there are more docshells to be printed and + // then PrintDocContent will be called again. + + if (mIsDoingPrinting) { + PrintDocContent(mPrt->mPrintObject, rv); // ignore return value + } + + return rv; +} + +//------------------------------------------------------- +// Recursively reflow each sub-doc and then calc +// all the frame locations of the sub-docs +nsresult +nsPrintEngine::ReflowDocList(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()) { + SetPrintPO(aPO, false); + aPO->mInvisible = true; + return NS_OK; + } + } + + UpdateZoomRatio(aPO, aSetPixelScale); + + nsresult rv; + // Reflow the PO + rv = ReflowPrintObject(aPO); + NS_ENSURE_SUCCESS(rv, rv); + + int32_t cnt = aPO->mKids.Length(); + for (int32_t i=0;i<cnt;i++) { + rv = ReflowDocList(aPO->mKids[i], aSetPixelScale); + NS_ENSURE_SUCCESS(rv, rv); + } + return NS_OK; +} + +void +nsPrintEngine::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 (mIsDoingPrintPreview && !mIsDoingPrinting) { + nsCOMPtr<nsIContentViewer> cv = do_QueryInterface(mDocViewerPrint); + (new nsAsyncDOMEvent( + cv->GetDocument(), NS_LITERAL_STRING("printPreviewUpdate"), true, true) + )->RunDOMEventWhenSafe(); + } +} + +nsresult +nsPrintEngine::InitPrintDocConstruction(bool aHandleError) +{ + nsresult rv; + rv = ReflowDocList(mPrt->mPrintObject, DoSetPixelScale()); + NS_ENSURE_SUCCESS(rv, rv); + + FirePrintPreviewUpdateEvent(); + + if (mLoadCounter == 0) { + AfterNetworkPrint(aHandleError); + } + return rv; +} + +nsresult +nsPrintEngine::AfterNetworkPrint(bool aHandleError) +{ + 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 (aHandleError && NS_FAILED(rv)) { + CleanupOnFailure(rv, !mIsDoingPrinting); + } + + return rv; +} + +//////////////////////////////////////////////////////////////////////////////// +// nsIWebProgressListener + +NS_IMETHODIMP +nsPrintEngine::OnStateChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + uint32_t aStateFlags, + nsresult aStatus) +{ + nsAutoCString name; + aRequest->GetName(name); + if (name.Equals("about:document-onload-blocker")) { + return NS_OK; + } + if (aStateFlags & STATE_START) { + nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest); + + ++mLoadCounter; + } else if (aStateFlags & STATE_STOP) { + mDidLoadDataForPrinting = true; + --mLoadCounter; + + // If all resources are loaded, then do a small timeout and if there + // are still no new requests, then another reflow. + if (mLoadCounter == 0) { + AfterNetworkPrint(true); + } + } + return NS_OK; +} + + + +NS_IMETHODIMP +nsPrintEngine::OnProgressChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + int32_t aCurSelfProgress, + int32_t aMaxSelfProgress, + int32_t aCurTotalProgress, + int32_t aMaxTotalProgress) +{ + NS_NOTREACHED("notification excluded in AddProgressListener(...)"); + return NS_OK; +} + +NS_IMETHODIMP +nsPrintEngine::OnLocationChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + nsIURI* aLocation, + uint32_t aFlags) +{ + NS_NOTREACHED("notification excluded in AddProgressListener(...)"); + return NS_OK; +} + +NS_IMETHODIMP +nsPrintEngine::OnStatusChange(nsIWebProgress *aWebProgress, + nsIRequest *aRequest, + nsresult aStatus, + const PRUnichar *aMessage) +{ + NS_NOTREACHED("notification excluded in AddProgressListener(...)"); + return NS_OK; +} + +NS_IMETHODIMP +nsPrintEngine::OnSecurityChange(nsIWebProgress *aWebProgress, + nsIRequest *aRequest, + uint32_t aState) +{ + NS_NOTREACHED("notification excluded in AddProgressListener(...)"); + return NS_OK; +} + +//------------------------------------------------------- + +void +nsPrintEngine::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) { + float ratio; + if (mPrt->mPrintFrameType == nsIPrintSettings::kFramesAsIs || mPrt->mPrintFrameType == nsIPrintSettings::kNoFrames) { + ratio = mPrt->mShrinkRatio - 0.005f; // round down + } else { + ratio = aPO->mShrinkRatio - 0.005f; // round down + } + aPO->mZoomRatio = ratio; + } else if (!mPrt->mShrinkToFit) { + double scaling; + mPrt->mPrintSettings->GetScaling(&scaling); + aPO->mZoomRatio = float(scaling); + } +} + +nsresult +nsPrintEngine::UpdateSelectionAndShrinkPrintObject(nsPrintObject* aPO, + bool aDocumentIsTopLevel) +{ + nsCOMPtr<nsIPresShell> displayShell = aPO->mDocShell->GetPresShell(); + // Transfer Selection Ranges to the new Print PresShell + nsRefPtr<Selection> selection, selectionPS; + // It's okay if there is no display shell, just skip copying the selection + if (displayShell) { + selection = displayShell->GetCurrentSelection(nsISelectionController::SELECTION_NORMAL); + } + selectionPS = aPO->mPresShell->GetCurrentSelection(nsISelectionController::SELECTION_NORMAL); + + // Reset all existing selection ranges that might have been added by calling + // this function before. + if (selectionPS) { + selectionPS->RemoveAllRanges(); + } + if (selection && selectionPS) { + int32_t cnt = selection->GetRangeCount(); + int32_t inx; + for (inx = 0; inx < cnt; ++inx) { + selectionPS->AddRange(selection->GetRangeAt(inx)); + } + } + + // 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) { + nsIPageSequenceFrame* pageSequence = aPO->mPresShell->GetPageSequenceFrame(); + NS_ENSURE_STATE(pageSequence); + pageSequence->GetSTFPercent(aPO->mShrinkRatio); + } + return NS_OK; +} + +bool +nsPrintEngine::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* +nsPrintEngine::GetParentViewForRoot() +{ + if (mIsCreatingPrintPreview) { + nsCOMPtr<nsIContentViewer> cv = do_QueryInterface(mDocViewerPrint); + if (cv) { + return cv->FindContainerView(); + } + } + return nullptr; +} + +nsresult +nsPrintEngine::SetRootView( + nsPrintObject* aPO, + bool& doReturn, + bool& documentIsTopLevel, + nsSize& adjSize +) +{ + bool canCreateScrollbars = true; + + nsView* rootView; + nsView* parentView = nullptr; + + doReturn = false; + + if (aPO->mParent && aPO->mParent->IsPrintable()) { + 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) { + SetPrintPO(aPO, 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->GetType() == nsGkAtoms::subDocumentFrame) { + 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 +nsPrintEngine::ReflowPrintObject(nsPrintObject * aPO) +{ + NS_ENSURE_STATE(aPO); + + if (!aPO->IsPrintable()) { + return NS_OK; + } + + NS_ASSERTION(!aPO->mPresContext, "Recreating prescontext"); + + // create the PresContext + nsPresContext::nsPresContextType type = + mIsCreatingPrintPreview ? nsPresContext::eContext_PrintPreview: + nsPresContext::eContext_Print; + nsView* parentView = + aPO->mParent && aPO->mParent->IsPrintable() ? nullptr : GetParentViewForRoot(); + aPO->mPresContext = parentView ? + new nsPresContext(aPO->mDocument, type) : + new nsRootPresContext(aPO->mDocument, type); + NS_ENSURE_TRUE(aPO->mPresContext, NS_ERROR_OUT_OF_MEMORY); + aPO->mPresContext->SetPrintSettings(mPrt->mPrintSettings); + + // set the presentation context to the value in the print settings + bool printBGColors; + mPrt->mPrintSettings->GetPrintBGColors(&printBGColors); + aPO->mPresContext->SetBackgroundColorDraw(printBGColors); + mPrt->mPrintSettings->GetPrintBGImages(&printBGColors); + aPO->mPresContext->SetBackgroundImageDraw(printBGColors); + + // init it with the DC + nsresult rv = aPO->mPresContext->Init(mPrt->mPrintDC); + NS_ENSURE_SUCCESS(rv, rv); + + aPO->mViewManager = new nsViewManager(); + + rv = aPO->mViewManager->Init(mPrt->mPrintDC); + NS_ENSURE_SUCCESS(rv,rv); + + nsStyleSet* styleSet; + rv = mDocViewerPrint->CreateStyleSet(aPO->mDocument, &styleSet); + NS_ENSURE_SUCCESS(rv, rv); + + aPO->mPresShell = aPO->mDocument->CreateShell(aPO->mPresContext, + aPO->mViewManager, styleSet); + if (!aPO->mPresShell) { + delete styleSet; + return NS_ERROR_FAILURE; + } + + styleSet->EndUpdate(); + + // The pres shell now owns the style set object. + + + bool doReturn = false;; + bool documentIsTopLevel = false; + nsSize adjSize; + + rv = SetRootView(aPO, 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, aPO->mPresShell.get(), + gFrameTypesStr[aPO->mFrameType], adjSize.width, adjSize.height)); + + + // This docshell stuff is weird; will go away when we stop having multiple + // presentations per document + nsCOMPtr<nsISupports> supps(do_QueryInterface(aPO->mDocShell)); + aPO->mPresContext->SetContainer(supps); + + aPO->mPresShell->BeginObservingDocument(); + + aPO->mPresContext->SetPageSize(adjSize); + aPO->mPresContext->SetIsRootPaginatedDocument(documentIsTopLevel); + aPO->mPresContext->SetPageScale(aPO->mZoomRatio); + // Calculate scale factor from printer to screen + float printDPI = float(mPrt->mPrintDC->AppUnitsPerCSSInch()) / + float(mPrt->mPrintDC->AppUnitsPerDevPixel()); + aPO->mPresContext->SetPrintPreviewScale(mScreenDPI / printDPI); + + if (mIsCreatingPrintPreview && documentIsTopLevel) { + mDocViewerPrint->SetPrintPreviewPresentation(aPO->mViewManager, + aPO->mPresContext, + aPO->mPresShell); + } + + rv = aPO->mPresShell->Initialize(adjSize.width, adjSize.height); + + NS_ENSURE_SUCCESS(rv, rv); + NS_ASSERTION(aPO->mPresShell, "Presshell should still be here"); + + // Process the reflow event Initialize posted + aPO->mPresShell->FlushPendingNotifications(Flush_Layout); + + rv = UpdateSelectionAndShrinkPrintObject(aPO, documentIsTopLevel); + NS_ENSURE_SUCCESS(rv, rv); + +#ifdef EXTENDED_DEBUG_PRINTING + if (kPrintingLogMod && kPrintingLogMod->level == DUMP_LAYOUT_LEVEL) { + char * docStr; + char * 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->FrameManager()->GetRootFrame(); + fprintf(fd, "Title: %s\n", docStr?docStr:""); + fprintf(fd, "URL: %s\n", urlStr?urlStr:""); + fprintf(fd, "--------------- Frames ----------------\n"); + nsRefPtr<nsRenderingContext> renderingContext; + mPrt->mPrintDocDC->CreateRenderingContext(*getter_AddRefs(renderingContext)); + 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 (docShell) { + fprintf(fd, "--------------- All Views ----------------\n"); + DumpViews(docShell, fd); + fprintf(fd, "---------------------------------------\n\n"); + } + fclose(fd); + } + if (docStr) nsMemory::Free(docStr); + if (urlStr) nsMemory::Free(urlStr); + } +#endif + + return NS_OK; +} + +//------------------------------------------------------- +// Figure out how many documents and how many total pages we are printing +void +nsPrintEngine::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!"); + if (po->mPresContext && po->mPresContext->IsRootPaginatedDocument()) { + nsIPageSequenceFrame* pageSequence = po->mPresShell->GetPageSequenceFrame(); + nsIFrame * seqFrame = do_QueryFrame(pageSequence); + if (seqFrame) { + nsIFrame* frame = seqFrame->GetFirstPrincipalChild(); + while (frame) { + aNumPages++; + frame = frame->GetNextSibling(); + } + } + } + } +} +//----------------------------------------------------------------- +//-- Done: Reflow Methods +//----------------------------------------------------------------- + +//----------------------------------------------------------------- +//-- Section: Printing Methods +//----------------------------------------------------------------- + +//------------------------------------------------------- +// Called for each DocShell that needs to be printed +bool +nsPrintEngine::PrintDocContent(nsPrintObject* aPO, nsresult& aStatus) +{ + NS_ASSERTION(aPO, "Pointer is null!"); + aStatus = NS_OK; + + if (!aPO->mHasBeenPrinted && aPO->IsPrintable()) { + aStatus = DoPrint(aPO); + return true; + } + + // If |aPO->mPrintAsIs| and |aPO->mHasBeenPrinted| are true, + // the kids frames are already processed in |PrintPage|. + if (!aPO->mInvisible && !(aPO->mPrintAsIs && aPO->mHasBeenPrinted)) { + for (uint32_t i=0;i<aPO->mKids.Length();i++) { + nsPrintObject* po = aPO->mKids[i]; + bool printed = PrintDocContent(po, aStatus); + if (printed || NS_FAILED(aStatus)) { + return true; + } + } + } + return false; +} + +static already_AddRefed<nsIDOMNode> +GetEqualNodeInCloneTree(nsIDOMNode* aNode, nsIDocument* aDoc) +{ + nsCOMPtr<nsIContent> content = do_QueryInterface(aNode); + // Selections in anonymous subtrees aren't supported. + if (content && content->IsInAnonymousSubtree()) { + return nullptr; + } + + nsCOMPtr<nsINode> node = do_QueryInterface(aNode); + NS_ENSURE_TRUE(node, nullptr); + + nsTArray<int32_t> indexArray; + nsINode* current = node; + NS_ENSURE_TRUE(current, nullptr); + while (current) { + nsINode* parent = current->GetParentNode(); + if (!parent) { + break; + } + int32_t index = parent->IndexOf(current); + NS_ENSURE_TRUE(index >= 0, nullptr); + indexArray.AppendElement(index); + current = parent; + } + NS_ENSURE_TRUE(current->IsNodeOfType(nsINode::eDOCUMENT), nullptr); + + current = aDoc; + for (int32_t i = indexArray.Length() - 1; i >= 0; --i) { + current = current->GetChildAt(indexArray[i]); + NS_ENSURE_TRUE(current, nullptr); + } + nsCOMPtr<nsIDOMNode> result = do_QueryInterface(current); + return result.forget(); +} + +static void +CloneRangeToSelection(nsRange* aRange, nsIDocument* aDoc, + Selection* aSelection) +{ + if (aRange->Collapsed()) { + return; + } + + nsCOMPtr<nsIDOMNode> startContainer, endContainer; + aRange->GetStartContainer(getter_AddRefs(startContainer)); + int32_t startOffset = aRange->StartOffset(); + aRange->GetEndContainer(getter_AddRefs(endContainer)); + int32_t endOffset = aRange->EndOffset(); + NS_ENSURE_TRUE_VOID(startContainer && endContainer); + + nsCOMPtr<nsIDOMNode> newStart = GetEqualNodeInCloneTree(startContainer, aDoc); + nsCOMPtr<nsIDOMNode> newEnd = GetEqualNodeInCloneTree(endContainer, aDoc); + NS_ENSURE_TRUE_VOID(newStart && newEnd); + + nsCOMPtr<nsINode> newStartNode = do_QueryInterface(newStart); + NS_ENSURE_TRUE_VOID(newStartNode); + + nsRefPtr<nsRange> range = new nsRange(newStartNode); + nsresult rv = range->SetStart(newStartNode, startOffset); + NS_ENSURE_SUCCESS_VOID(rv); + rv = range->SetEnd(newEnd, endOffset); + NS_ENSURE_SUCCESS_VOID(rv); + + aSelection->AddRange(range); +} + +static nsresult CloneSelection(nsIDocument* aOrigDoc, nsIDocument* aDoc) +{ + nsIPresShell* origShell = aOrigDoc->GetShell(); + nsIPresShell* shell = aDoc->GetShell(); + NS_ENSURE_STATE(origShell && shell); + + nsRefPtr<Selection> origSelection = + origShell->GetCurrentSelection(nsISelectionController::SELECTION_NORMAL); + nsRefPtr<Selection> selection = + shell->GetCurrentSelection(nsISelectionController::SELECTION_NORMAL); + NS_ENSURE_STATE(origSelection && selection); + + int32_t rangeCount = origSelection->GetRangeCount(); + for (int32_t i = 0; i < rangeCount; ++i) { + CloneRangeToSelection(origSelection->GetRangeAt(i), aDoc, selection); + } + return NS_OK; +} + +//------------------------------------------------------- +nsresult +nsPrintEngine::DoPrint(nsPrintObject * aPO) +{ + PR_PL(("\n")); + PR_PL(("**************************** %s ****************************\n", gFrameTypesStr[aPO->mFrameType])); + PR_PL(("****** In DV::DoPrint PO: %p \n", aPO)); + + nsIPresShell* 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?"); + + if (mPrt->mPrintProgressParams) { + SetDocAndURLIntoProgress(aPO, mPrt->mPrintProgressParams); + } + + { + int16_t printRangeType = nsIPrintSettings::kRangeAllPages; + nsresult rv; + if (mPrt->mPrintSettings != nullptr) { + mPrt->mPrintSettings->GetPrintRange(&printRangeType); + } + + // Ask the page sequence frame to print all the pages + nsIPageSequenceFrame* pageSequence = poPresShell->GetPageSequenceFrame(); + NS_ASSERTION(nullptr != pageSequence, "no page sequence frame"); + + // We are done preparing for printing, so we can turn this off + mPrt->mPreparingForPrint = false; + + // mPrt->mDebugFilePtr this is onlu non-null when compiled for debugging + if (nullptr != mPrt->mDebugFilePtr) { +#ifdef DEBUG + // output the regression test + nsIFrame* root = poPresShell->FrameManager()->GetRootFrame(); + root->DumpRegressionData(poPresContext, mPrt->mDebugFilePtr, 0); + fclose(mPrt->mDebugFilePtr); + SetIsPrinting(false); +#endif + } else { +#ifdef EXTENDED_DEBUG_PRINTING + nsIFrame* rootFrame = poPresShell->FrameManager()->GetRootFrame(); + if (aPO->IsPrintable()) { + char * docStr; + char * urlStr; + GetDocTitleAndURL(aPO, docStr, urlStr); + DumpLayoutData(docStr, urlStr, poPresContext, mPrt->mPrintDocDC, rootFrame, docShell, nullptr); + if (docStr) nsMemory::Free(docStr); + if (urlStr) nsMemory::Free(urlStr); + } +#endif + + if (!mPrt->mPrintSettings) { + // not sure what to do here! + SetIsPrinting(false); + return NS_ERROR_FAILURE; + } + + PRUnichar * docTitleStr = nullptr; + PRUnichar * docURLStr = nullptr; + + GetDisplayTitleAndURL(aPO, &docTitleStr, &docURLStr, eDocTitleDefBlank); + + if (nsIPrintSettings::kRangeSelection == printRangeType) { + CloneSelection(aPO->mDocument->GetOriginalDocument(), aPO->mDocument); + + poPresContext->SetIsRenderingOnlySelection(true); + // temporarily creating rendering context + // which is needed to find the selection frames + nsRefPtr<nsRenderingContext> rc; + mPrt->mPrintDC->CreateRenderingContext(*getter_AddRefs(rc)); + + // find the starting and ending page numbers + // via the selection + nsIFrame* startFrame; + nsIFrame* endFrame; + int32_t startPageNum; + int32_t endPageNum; + nsRect startRect; + nsRect endRect; + + nsRefPtr<Selection> selectionPS = + poPresShell->GetCurrentSelection(nsISelectionController::SELECTION_NORMAL); + + rv = GetPageRangeForSelection(poPresShell, poPresContext, *rc, selectionPS, pageSequence, + &startFrame, startPageNum, startRect, + &endFrame, endPageNum, endRect); + if (NS_SUCCEEDED(rv)) { + mPrt->mPrintSettings->SetStartPageRange(startPageNum); + mPrt->mPrintSettings->SetEndPageRange(endPageNum); + nsIntMargin marginTwips(0,0,0,0); + nsIntMargin unwrtMarginTwips(0,0,0,0); + mPrt->mPrintSettings->GetMarginInTwips(marginTwips); + mPrt->mPrintSettings->GetUnwriteableMarginInTwips(unwrtMarginTwips); + nsMargin totalMargin = poPresContext->CSSTwipsToAppUnits(marginTwips + + unwrtMarginTwips); + if (startPageNum == endPageNum) { + startRect.y -= totalMargin.top; + endRect.y -= totalMargin.top; + + // Clip out selection regions above the top of the first page + if (startRect.y < 0) { + // Reduce height to be the height of the positive-territory + // region of original rect + startRect.height = std::max(0, startRect.YMost()); + startRect.y = 0; + } + if (endRect.y < 0) { + // Reduce height to be the height of the positive-territory + // region of original rect + endRect.height = std::max(0, endRect.YMost()); + endRect.y = 0; + } + NS_ASSERTION(endRect.y >= startRect.y, + "Selection end point should be after start point"); + NS_ASSERTION(startRect.height >= 0, + "rect should have non-negative height."); + NS_ASSERTION(endRect.height >= 0, + "rect should have non-negative height."); + + nscoord selectionHgt = endRect.y + endRect.height - startRect.y; + // XXX This is temporary fix for printing more than one page of a selection + pageSequence->SetSelectionHeight(startRect.y * aPO->mZoomRatio, + selectionHgt * aPO->mZoomRatio); + + // calc total pages by getting calculating the selection's height + // and then dividing it by how page content frames will fit. + nscoord pageWidth, pageHeight; + mPrt->mPrintDC->GetDeviceSurfaceDimensions(pageWidth, pageHeight); + pageHeight -= totalMargin.top + totalMargin.bottom; + int32_t totalPages = NSToIntCeil(float(selectionHgt) * aPO->mZoomRatio / float(pageHeight)); + pageSequence->SetTotalNumPages(totalPages); + } + } + } + + nsIFrame * seqFrame = do_QueryFrame(pageSequence); + if (!seqFrame) { + SetIsPrinting(false); + if (docTitleStr) nsMemory::Free(docTitleStr); + if (docURLStr) nsMemory::Free(docURLStr); + return NS_ERROR_FAILURE; + } + + mPageSeqFrame = pageSequence; + mPageSeqFrame->StartPrint(poPresContext, mPrt->mPrintSettings, docTitleStr, docURLStr); + + // Schedule Page to Print + PR_PL(("Scheduling Print of PO: %p (%s) \n", aPO, gFrameTypesStr[aPO->mFrameType])); + StartPagePrintTimer(aPO); + } + } + + return NS_OK; +} + +//--------------------------------------------------------------------- +void +nsPrintEngine::SetDocAndURLIntoProgress(nsPrintObject* aPO, + nsIPrintProgressParams* aParams) +{ + NS_ASSERTION(aPO, "Must have vaild nsPrintObject"); + NS_ASSERTION(aParams, "Must have vaild nsIPrintProgressParams"); + + if (!aPO || !aPO->mDocShell || !aParams) { + return; + } + const uint32_t kTitleLength = 64; + + PRUnichar * docTitleStr; + PRUnichar * docURLStr; + GetDisplayTitleAndURL(aPO, &docTitleStr, &docURLStr, eDocTitleDefURLDoc); + + // Make sure the Titles & URLS don't get too long for the progress dialog + ElipseLongString(docTitleStr, kTitleLength, false); + ElipseLongString(docURLStr, kTitleLength, true); + + aParams->SetDocTitle(docTitleStr); + aParams->SetDocURL(docURLStr); + + if (docTitleStr != nullptr) nsMemory::Free(docTitleStr); + if (docURLStr != nullptr) nsMemory::Free(docURLStr); +} + +//--------------------------------------------------------------------- +void +nsPrintEngine::ElipseLongString(PRUnichar *& aStr, const uint32_t aLen, bool aDoFront) +{ + // Make sure the URLS don't get too long for the progress dialog + if (aStr && NS_strlen(aStr) > aLen) { + if (aDoFront) { + PRUnichar * ptr = &aStr[NS_strlen(aStr) - aLen + 3]; + nsAutoString newStr; + newStr.AppendLiteral("..."); + newStr += ptr; + nsMemory::Free(aStr); + aStr = ToNewUnicode(newStr); + } else { + nsAutoString newStr(aStr); + newStr.SetLength(aLen-3); + newStr.AppendLiteral("..."); + nsMemory::Free(aStr); + aStr = ToNewUnicode(newStr); + } + } +} + +static bool +DocHasPrintCallbackCanvas(nsIDocument* aDoc, void* aData) +{ + if (!aDoc) { + return true; + } + Element* root = aDoc->GetRootElement(); + if (!root) { + return true; + } + nsRefPtr<nsContentList> canvases = NS_GetContentList(root, + kNameSpaceID_XHTML, + NS_LITERAL_STRING("canvas")); + uint32_t canvasCount = canvases->Length(true); + for (uint32_t i = 0; i < canvasCount; ++i) { + nsCOMPtr<nsIDOMHTMLCanvasElement> canvas = do_QueryInterface(canvases->Item(i, false)); + nsCOMPtr<nsIPrintCallback> printCallback; + if (canvas && NS_SUCCEEDED(canvas->GetMozPrintCallback(getter_AddRefs(printCallback))) && + printCallback) { + // This subdocument has a print callback. Set result and return false to + // stop iteration. + *static_cast<bool*>(aData) = true; + return false; + } + } + return true; +} + +static bool +DocHasPrintCallbackCanvas(nsIDocument* aDoc) +{ + bool result = false; + aDoc->EnumerateSubDocuments(&DocHasPrintCallbackCanvas, static_cast<void*>(&result)); + return result; +} + +/** + * Checks to see if the document this print engine is associated with has any + * canvases that have a mozPrintCallback. + */ +bool +nsPrintEngine::HasPrintCallbackCanvas() +{ + if (!mDocument) { + return false; + } + // First check this mDocument. + bool result = false; + DocHasPrintCallbackCanvas(mDocument, static_cast<void*>(&result)); + // Also check the sub documents. + return result || DocHasPrintCallbackCanvas(mDocument); +} + +//------------------------------------------------------- +bool +nsPrintEngine::PrePrintPage() +{ + NS_ASSERTION(mPageSeqFrame, "mPageSeqFrame is null!"); + NS_ASSERTION(mPrt, "mPrt is null!"); + + // Although these should NEVER be NULL + // This is added insurance, to make sure we don't crash in optimized builds + if (!mPrt || !mPageSeqFrame) { + return true; // means we are done preparing the page. + } + + // Check setting to see if someone request it be cancelled + bool isCancelled = false; + mPrt->mPrintSettings->GetIsCancelled(&isCancelled); + if (isCancelled) + return true; + + // Ask mPageSeqFrame if the page is ready to be printed. + // If the page doesn't get printed at all, the |done| will be |true|. + bool done = false; + nsresult rv = mPageSeqFrame->PrePrintNextPage(mPagePrintTimer, &done); + if (NS_FAILED(rv)) { + // ??? ::PrintPage doesn't set |mPrt->mIsAborted = true| if rv != NS_ERROR_ABORT, + // but I don't really understand why this should be the right thing to do? + // Shouldn't |mPrt->mIsAborted| set to true all the time if something + // wents wrong? + if (rv != NS_ERROR_ABORT) { + ShowPrintErrorDialog(rv); + mPrt->mIsAborted = true; + } + done = true; + } + return done; +} + +bool +nsPrintEngine::PrintPage(nsPrintObject* aPO, + bool& aInRange) +{ + NS_ASSERTION(aPO, "aPO is null!"); + NS_ASSERTION(mPageSeqFrame, "mPageSeqFrame is null!"); + NS_ASSERTION(mPrt, "mPrt is null!"); + + // Although these should NEVER be NULL + // This is added insurance, to make sure we don't crash in optimized builds + if (!mPrt || !aPO || !mPageSeqFrame) { + ShowPrintErrorDialog(NS_ERROR_FAILURE); + return true; // means we are done printing + } + + PR_PL(("-----------------------------------\n")); + PR_PL(("------ In DV::PrintPage PO: %p (%s)\n", aPO, gFrameTypesStr[aPO->mFrameType])); + + // Check setting to see if someone request it be cancelled + bool isCancelled = false; + mPrt->mPrintSettings->GetIsCancelled(&isCancelled); + if (isCancelled || mPrt->mIsAborted) + return true; + + int32_t pageNum, numPages, endPage; + mPageSeqFrame->GetCurrentPageNum(&pageNum); + mPageSeqFrame->GetNumPages(&numPages); + + bool donePrinting; + bool isDoingPrintRange; + mPageSeqFrame->IsDoingPrintRange(&isDoingPrintRange); + if (isDoingPrintRange) { + int32_t fromPage; + int32_t toPage; + mPageSeqFrame->GetPrintRange(&fromPage, &toPage); + + if (fromPage > numPages) { + return true; + } + if (toPage > numPages) { + toPage = numPages; + } + + PR_PL(("****** Printing Page %d printing from %d to page %d\n", pageNum, fromPage, toPage)); + + donePrinting = pageNum >= toPage; + aInRange = pageNum >= fromPage && pageNum <= toPage; + endPage = (toPage - fromPage)+1; + } else { + PR_PL(("****** Printing Page %d of %d page(s)\n", pageNum, numPages)); + + donePrinting = pageNum >= numPages; + endPage = numPages; + aInRange = true; + } + + // XXX This is wrong, but the actual behavior in the presence of a print + // range sucks. + if (mPrt->mPrintFrameType == nsIPrintSettings::kEachFrameSep) + endPage = mPrt->mNumPrintablePages; + + mPrt->DoOnProgressChange(++mPrt->mNumPagesPrinted, endPage, false, 0); + + // Print the Page + // 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 = mPageSeqFrame->PrintNextPage(); + if (NS_FAILED(rv)) { + if (rv != NS_ERROR_ABORT) { + ShowPrintErrorDialog(rv); + mPrt->mIsAborted = true; + } + return true; + } + + mPageSeqFrame->DoPageEnd(); + + return donePrinting; +} + +/** --------------------------------------------------- + * Find by checking frames type + */ +nsresult +nsPrintEngine::FindSelectionBoundsWithList(nsPresContext* aPresContext, + nsRenderingContext& aRC, + nsFrameList::Enumerator& aChildFrames, + nsIFrame * aParentFrame, + nsRect& aRect, + nsIFrame *& aStartFrame, + nsRect& aStartRect, + nsIFrame *& aEndFrame, + nsRect& aEndRect) +{ + NS_ASSERTION(aPresContext, "Pointer is null!"); + NS_ASSERTION(aParentFrame, "Pointer is null!"); + + aRect += aParentFrame->GetPosition(); + for (; !aChildFrames.AtEnd(); aChildFrames.Next()) { + nsIFrame* child = aChildFrames.get(); + if (child->IsSelected() && child->IsVisibleForPainting()) { + nsRect r = child->GetRect(); + if (aStartFrame == nullptr) { + aStartFrame = child; + aStartRect.SetRect(aRect.x + r.x, aRect.y + r.y, r.width, r.height); + } else { + aEndFrame = child; + aEndRect.SetRect(aRect.x + r.x, aRect.y + r.y, r.width, r.height); + } + } + FindSelectionBounds(aPresContext, aRC, child, aRect, aStartFrame, aStartRect, aEndFrame, aEndRect); + child = child->GetNextSibling(); + } + aRect -= aParentFrame->GetPosition(); + return NS_OK; +} + +//------------------------------------------------------- +// Find the Frame that is XMost +nsresult +nsPrintEngine::FindSelectionBounds(nsPresContext* aPresContext, + nsRenderingContext& aRC, + nsIFrame * aParentFrame, + nsRect& aRect, + nsIFrame *& aStartFrame, + nsRect& aStartRect, + nsIFrame *& aEndFrame, + nsRect& aEndRect) +{ + NS_ASSERTION(aPresContext, "Pointer is null!"); + NS_ASSERTION(aParentFrame, "Pointer is null!"); + + // loop through named child lists + nsIFrame::ChildListIterator lists(aParentFrame); + for (; !lists.IsDone(); lists.Next()) { + nsFrameList::Enumerator childFrames(lists.CurrentList()); + nsresult rv = FindSelectionBoundsWithList(aPresContext, aRC, childFrames, aParentFrame, aRect, aStartFrame, aStartRect, aEndFrame, aEndRect); + NS_ENSURE_SUCCESS(rv, rv); + } + return NS_OK; +} + +/** --------------------------------------------------- + * This method finds the starting and ending page numbers + * of the selection and also returns rect for each where + * the x,y of the rect is relative to the very top of the + * frame tree (absolutely positioned) + */ +nsresult +nsPrintEngine::GetPageRangeForSelection(nsIPresShell * aPresShell, + nsPresContext* aPresContext, + nsRenderingContext& aRC, + nsISelection* aSelection, + nsIPageSequenceFrame* aPageSeqFrame, + nsIFrame** aStartFrame, + int32_t& aStartPageNum, + nsRect& aStartRect, + nsIFrame** aEndFrame, + int32_t& aEndPageNum, + nsRect& aEndRect) +{ + NS_ASSERTION(aPresShell, "Pointer is null!"); + NS_ASSERTION(aPresContext, "Pointer is null!"); + NS_ASSERTION(aSelection, "Pointer is null!"); + NS_ASSERTION(aPageSeqFrame, "Pointer is null!"); + NS_ASSERTION(aStartFrame, "Pointer is null!"); + NS_ASSERTION(aEndFrame, "Pointer is null!"); + + nsIFrame * seqFrame = do_QueryFrame(aPageSeqFrame); + if (!seqFrame) { + return NS_ERROR_FAILURE; + } + + nsIFrame * startFrame = nullptr; + nsIFrame * endFrame = nullptr; + + // start out with the sequence frame and search the entire frame tree + // capturing the starting and ending child frames of the selection + // and their rects + nsRect r = seqFrame->GetRect(); + FindSelectionBounds(aPresContext, aRC, seqFrame, r, + startFrame, aStartRect, endFrame, aEndRect); + +#ifdef DEBUG_rodsX + printf("Start Frame: %p\n", startFrame); + printf("End Frame: %p\n", endFrame); +#endif + + // initial the page numbers here + // in case we don't find and frames + aStartPageNum = -1; + aEndPageNum = -1; + + nsIFrame * startPageFrame; + nsIFrame * endPageFrame; + + // check to make sure we found a starting frame + if (startFrame != nullptr) { + // Now search up the tree to find what page the + // start/ending selections frames are on + // + // Check to see if start should be same as end if + // the end frame comes back null + if (endFrame == nullptr) { + // XXX the "GetPageFrame" step could be integrated into + // the FindSelectionBounds step, but walking up to find + // the parent of a child frame isn't expensive and it makes + // FindSelectionBounds a little easier to understand + startPageFrame = nsLayoutUtils::GetPageFrame(startFrame); + endPageFrame = startPageFrame; + aEndRect = aStartRect; + } else { + startPageFrame = nsLayoutUtils::GetPageFrame(startFrame); + endPageFrame = nsLayoutUtils::GetPageFrame(endFrame); + } + } else { + return NS_ERROR_FAILURE; + } + +#ifdef DEBUG_rodsX + printf("Start Page: %p\n", startPageFrame); + printf("End Page: %p\n", endPageFrame); + + // dump all the pages and their pointers + { + int32_t pageNum = 1; + nsIFrame* child = seqFrame->GetFirstPrincipalChild(); + while (child != nullptr) { + printf("Page: %d - %p\n", pageNum, child); + pageNum++; + child = child->GetNextSibling(); + } + } +#endif + + // Now that we have the page frames + // find out what the page numbers are for each frame + int32_t pageNum = 1; + nsIFrame* page = seqFrame->GetFirstPrincipalChild(); + while (page != nullptr) { + if (page == startPageFrame) { + aStartPageNum = pageNum; + } + if (page == endPageFrame) { + aEndPageNum = pageNum; + } + pageNum++; + page = page->GetNextSibling(); + } + +#ifdef DEBUG_rodsX + printf("Start Page No: %d\n", aStartPageNum); + printf("End Page No: %d\n", aEndPageNum); +#endif + + *aStartFrame = startPageFrame; + *aEndFrame = endPageFrame; + + return NS_OK; +} + +//----------------------------------------------------------------- +//-- Done: Printing Methods +//----------------------------------------------------------------- + + +//----------------------------------------------------------------- +//-- Section: Misc Support Methods +//----------------------------------------------------------------- + +//--------------------------------------------------------------------- +void nsPrintEngine::SetIsPrinting(bool aIsPrinting) +{ + mIsDoingPrinting = aIsPrinting; + // Calling SetIsPrinting while in print preview confuses the document viewer + // This is safe because we prevent exiting print preview while printing + if (!mIsDoingPrintPreview && mDocViewerPrint) { + mDocViewerPrint->SetIsPrinting(aIsPrinting); + } + if (mPrt && aIsPrinting) { + mPrt->mPreparingForPrint = true; + } +} + +//--------------------------------------------------------------------- +void nsPrintEngine::SetIsPrintPreview(bool aIsPrintPreview) +{ + mIsDoingPrintPreview = aIsPrintPreview; + + if (mDocViewerPrint) { + mDocViewerPrint->SetIsPrintPreview(aIsPrintPreview); + } +} + +//--------------------------------------------------------------------- +void +nsPrintEngine::CleanupDocTitleArray(PRUnichar**& aArray, int32_t& aCount) +{ + for (int32_t i = aCount - 1; i >= 0; i--) { + nsMemory::Free(aArray[i]); + } + nsMemory::Free(aArray); + aArray = NULL; + aCount = 0; +} + +//--------------------------------------------------------------------- +// static +bool nsPrintEngine::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->IsHTML(nsGkAtoms::frameset)) { + return true; + } + } + + return false; +} + + + +/** --------------------------------------------------- + * Get the Focused Frame for a documentviewer + */ +already_AddRefed<nsIDOMWindow> +nsPrintEngine::FindFocusedDOMWindow() +{ + nsIFocusManager* fm = nsFocusManager::GetFocusManager(); + NS_ENSURE_TRUE(fm, nullptr); + + nsCOMPtr<nsPIDOMWindow> window(mDocument->GetWindow()); + NS_ENSURE_TRUE(window, nullptr); + + nsCOMPtr<nsPIDOMWindow> rootWindow = window->GetPrivateRoot(); + NS_ENSURE_TRUE(rootWindow, nullptr); + + nsCOMPtr<nsPIDOMWindow> focusedWindow; + nsFocusManager::GetFocusedDescendant(rootWindow, true, + getter_AddRefs(focusedWindow)); + NS_ENSURE_TRUE(focusedWindow, nullptr); + + if (IsWindowsInOurSubTree(focusedWindow)) { + return focusedWindow.forget(); + } + + return nullptr; +} + +//--------------------------------------------------------------------- +bool +nsPrintEngine::IsWindowsInOurSubTree(nsPIDOMWindow * window) +{ + bool found = false; + + // now check to make sure it is in "our" tree of docshells + if (window) { + nsCOMPtr<nsIDocShell> docShell = window->GetDocShell(); + + if (docShell) { + // get this DocViewer docshell + nsCOMPtr<nsIDocShell> thisDVDocShell(do_QueryReferent(mContainer)); + while (!found) { + if (docShell) { + if (docShell == thisDVDocShell) { + found = true; + break; + } + } else { + break; // at top of tree + } + nsCOMPtr<nsIDocShellTreeItem> docShellItemParent; + docShell->GetSameTypeParent(getter_AddRefs(docShellItemParent)); + docShell = do_QueryInterface(docShellItemParent); + } // while + } + } // scriptobj + + return found; +} + +//------------------------------------------------------- +bool +nsPrintEngine::DonePrintingPages(nsPrintObject* aPO, nsresult aResult) +{ + //NS_ASSERTION(aPO, "Pointer is null!"); + PR_PL(("****** In DV::DonePrintingPages 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) { + mPageSeqFrame->ResetPrintCanvasList(); + } + + if (aPO && !mPrt->mIsAborted) { + aPO->mHasBeenPrinted = true; + nsresult rv; + bool didPrint = PrintDocContent(mPrt->mPrintObject, rv); + if (NS_SUCCEEDED(rv) && didPrint) { + PR_PL(("****** In DV::DonePrintingPages PO: %p (%s) didPrint:%s (Not Done Printing)\n", aPO, gFrameTypesStr[aPO->mFrameType], PRT_YESNO(didPrint))); + return false; + } + } + + if (NS_SUCCEEDED(aResult)) { + FirePrintCompletionEvent(); + } + + TurnScriptingOn(true); + SetIsPrinting(false); + + // Release reference to mPagePrintTimer; the timer object destroys itself + // after this returns true + NS_IF_RELEASE(mPagePrintTimer); + + return true; +} + +//------------------------------------------------------- +// Recursively sets the PO items to be printed "As Is" +// from the given item down into the tree +void +nsPrintEngine::SetPrintAsIs(nsPrintObject* aPO, bool aAsIs) +{ + NS_ASSERTION(aPO, "Pointer is null!"); + + aPO->mPrintAsIs = aAsIs; + for (uint32_t i=0;i<aPO->mKids.Length();i++) { + SetPrintAsIs(aPO->mKids[i], aAsIs); + } +} + +//------------------------------------------------------- +// Given a DOMWindow it recursively finds the PO object that matches +nsPrintObject* +nsPrintEngine::FindPrintObjectByDOMWin(nsPrintObject* aPO, + nsIDOMWindow* aDOMWin) +{ + NS_ASSERTION(aPO, "Pointer is null!"); + + // Often the CurFocused DOMWindow is passed in + // andit is valid for it to be null, so short circut + if (!aDOMWin) { + return nullptr; + } + + nsCOMPtr<nsIDOMDocument> domDoc; + aDOMWin->GetDocument(getter_AddRefs(domDoc)); + nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc); + if (aPO->mDocument && aPO->mDocument->GetOriginalDocument() == doc) { + return aPO; + } + + int32_t cnt = aPO->mKids.Length(); + for (int32_t i = 0; i < cnt; ++i) { + nsPrintObject* po = FindPrintObjectByDOMWin(aPO->mKids[i], aDOMWin); + if (po) { + return po; + } + } + + return nullptr; +} + +//------------------------------------------------------- +nsresult +nsPrintEngine::EnablePOsForPrinting() +{ + // NOTE: All POs have been "turned off" for printing + // this is where we decided which POs get printed. + mPrt->mSelectedPO = nullptr; + + if (mPrt->mPrintSettings == nullptr) { + return NS_ERROR_FAILURE; + } + + mPrt->mPrintFrameType = nsIPrintSettings::kNoFrames; + mPrt->mPrintSettings->GetPrintFrameType(&mPrt->mPrintFrameType); + + int16_t printHowEnable = nsIPrintSettings::kFrameEnableNone; + mPrt->mPrintSettings->GetHowToEnableFrameUI(&printHowEnable); + + int16_t printRangeType = nsIPrintSettings::kRangeAllPages; + mPrt->mPrintSettings->GetPrintRange(&printRangeType); + + PR_PL(("\n")); + PR_PL(("********* nsPrintEngine::EnablePOsForPrinting *********\n")); + PR_PL(("PrintFrameType: %s \n", gPrintFrameTypeStr[mPrt->mPrintFrameType])); + PR_PL(("HowToEnableFrameUI: %s \n", gFrameHowToEnableStr[printHowEnable])); + PR_PL(("PrintRange: %s \n", gPrintRangeStr[printRangeType])); + PR_PL(("----\n")); + + // ***** This is the ultimate override ***** + // if we are printing the selection (either an IFrame or selection range) + // then set the mPrintFrameType as if it were the selected frame + if (printRangeType == nsIPrintSettings::kRangeSelection) { + mPrt->mPrintFrameType = nsIPrintSettings::kSelectedFrame; + printHowEnable = nsIPrintSettings::kFrameEnableNone; + } + + // This tells us that the "Frame" UI has turned off, + // so therefore there are no FrameSets/Frames/IFrames to be printed + // + // This means there are not FrameSets, + // but the document could contain an IFrame + if (printHowEnable == nsIPrintSettings::kFrameEnableNone) { + + // Print all the pages or a sub range of pages + if (printRangeType == nsIPrintSettings::kRangeAllPages || + printRangeType == nsIPrintSettings::kRangeSpecifiedPageRange) { + SetPrintPO(mPrt->mPrintObject, true); + + // Set the children so they are PrinAsIs + // In this case, the children are probably IFrames + if (mPrt->mPrintObject->mKids.Length() > 0) { + for (uint32_t i=0;i<mPrt->mPrintObject->mKids.Length();i++) { + nsPrintObject* po = mPrt->mPrintObject->mKids[i]; + NS_ASSERTION(po, "nsPrintObject can't be null!"); + SetPrintAsIs(po); + } + + // ***** Another override ***** + mPrt->mPrintFrameType = nsIPrintSettings::kFramesAsIs; + } + PR_PL(("PrintFrameType: %s \n", gPrintFrameTypeStr[mPrt->mPrintFrameType])); + PR_PL(("HowToEnableFrameUI: %s \n", gFrameHowToEnableStr[printHowEnable])); + PR_PL(("PrintRange: %s \n", gPrintRangeStr[printRangeType])); + return NS_OK; + } + + // This means we are either printed a selected IFrame or + // we are printing the current selection + if (printRangeType == nsIPrintSettings::kRangeSelection) { + + // If the currentFocusDOMWin can'r be null if something is selected + if (mPrt->mCurrentFocusWin) { + // Find the selected IFrame + nsPrintObject * po = FindPrintObjectByDOMWin(mPrt->mPrintObject, mPrt->mCurrentFocusWin); + if (po != nullptr) { + mPrt->mSelectedPO = po; + // Makes sure all of its children are be printed "AsIs" + SetPrintAsIs(po); + + // Now, only enable this POs (the selected PO) and all of its children + SetPrintPO(po, true); + + // check to see if we have a range selection, + // as oppose to a insert selection + // this means if the user just clicked on the IFrame then + // there will not be a selection so we want the entire page to print + // + // XXX this is sort of a hack right here to make the page + // not try to reposition itself when printing selection + nsCOMPtr<nsIDOMWindow> domWin = + do_QueryInterface(po->mDocument->GetOriginalDocument()->GetWindow()); + if (!IsThereARangeSelection(domWin)) { + printRangeType = nsIPrintSettings::kRangeAllPages; + mPrt->mPrintSettings->SetPrintRange(printRangeType); + } + PR_PL(("PrintFrameType: %s \n", gPrintFrameTypeStr[mPrt->mPrintFrameType])); + PR_PL(("HowToEnableFrameUI: %s \n", gFrameHowToEnableStr[printHowEnable])); + PR_PL(("PrintRange: %s \n", gPrintRangeStr[printRangeType])); + return NS_OK; + } + } else { + for (uint32_t i=0;i<mPrt->mPrintDocList.Length();i++) { + nsPrintObject* po = mPrt->mPrintDocList.ElementAt(i); + NS_ASSERTION(po, "nsPrintObject can't be null!"); + nsCOMPtr<nsIDOMWindow> domWin = do_GetInterface(po->mDocShell); + if (IsThereARangeSelection(domWin)) { + mPrt->mCurrentFocusWin = domWin; + SetPrintPO(po, true); + break; + } + } + return NS_OK; + } + } + } + + // check to see if there is a selection when a FrameSet is present + if (printRangeType == nsIPrintSettings::kRangeSelection) { + // If the currentFocusDOMWin can'r be null if something is selected + if (mPrt->mCurrentFocusWin) { + // Find the selected IFrame + nsPrintObject * po = FindPrintObjectByDOMWin(mPrt->mPrintObject, mPrt->mCurrentFocusWin); + if (po != nullptr) { + mPrt->mSelectedPO = po; + // Makes sure all of its children are be printed "AsIs" + SetPrintAsIs(po); + + // Now, only enable this POs (the selected PO) and all of its children + SetPrintPO(po, true); + + // check to see if we have a range selection, + // as oppose to a insert selection + // this means if the user just clicked on the IFrame then + // there will not be a selection so we want the entire page to print + // + // XXX this is sort of a hack right here to make the page + // not try to reposition itself when printing selection + nsCOMPtr<nsIDOMWindow> domWin = + do_QueryInterface(po->mDocument->GetOriginalDocument()->GetWindow()); + if (!IsThereARangeSelection(domWin)) { + printRangeType = nsIPrintSettings::kRangeAllPages; + mPrt->mPrintSettings->SetPrintRange(printRangeType); + } + PR_PL(("PrintFrameType: %s \n", gPrintFrameTypeStr[mPrt->mPrintFrameType])); + PR_PL(("HowToEnableFrameUI: %s \n", gFrameHowToEnableStr[printHowEnable])); + PR_PL(("PrintRange: %s \n", gPrintRangeStr[printRangeType])); + return NS_OK; + } + } + } + + // If we are printing "AsIs" then sets all the POs to be printed as is + if (mPrt->mPrintFrameType == nsIPrintSettings::kFramesAsIs) { + SetPrintAsIs(mPrt->mPrintObject); + SetPrintPO(mPrt->mPrintObject, true); + return NS_OK; + } + + // If we are printing the selected Frame then + // find that PO for that selected DOMWin and set it all of its + // children to be printed + if (mPrt->mPrintFrameType == nsIPrintSettings::kSelectedFrame) { + + if ((mPrt->mIsParentAFrameSet && mPrt->mCurrentFocusWin) || mPrt->mIsIFrameSelected) { + nsPrintObject * po = FindPrintObjectByDOMWin(mPrt->mPrintObject, mPrt->mCurrentFocusWin); + if (po != nullptr) { + mPrt->mSelectedPO = po; + // NOTE: Calling this sets the "po" and + // we don't want to do this for documents that have no children, + // because then the "DoEndPage" gets called and it shouldn't + if (po->mKids.Length() > 0) { + // Makes sure that itself, and all of its children are printed "AsIs" + SetPrintAsIs(po); + } + + // Now, only enable this POs (the selected PO) and all of its children + SetPrintPO(po, true); + } + } + return NS_OK; + } + + // If we are print each subdoc separately, + // then don't print any of the FraneSet Docs + if (mPrt->mPrintFrameType == nsIPrintSettings::kEachFrameSep) { + SetPrintPO(mPrt->mPrintObject, true); + int32_t cnt = mPrt->mPrintDocList.Length(); + for (int32_t i=0;i<cnt;i++) { + nsPrintObject* po = mPrt->mPrintDocList.ElementAt(i); + NS_ASSERTION(po, "nsPrintObject can't be null!"); + if (po->mFrameType == eFrameSet) { + po->mDontPrint = true; + } + } + } + + return NS_OK; +} + +//------------------------------------------------------- +// Return the nsPrintObject with that is XMost (The widest frameset frame) AND +// contains the XMost (widest) layout frame +nsPrintObject* +nsPrintEngine::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; +} + +//------------------------------------------------------- +void +nsPrintEngine::TurnScriptingOn(bool aDoTurnOn) +{ + if (mIsDoingPrinting && aDoTurnOn && mDocViewerPrint && + mDocViewerPrint->GetIsPrintPreview()) { + // We don't want to turn scripting on if print preview is shown still after + // printing. + return; + } + + nsPrintData* prt = mPrt; +#ifdef NS_PRINT_PREVIEW + if (!prt) { + prt = mPrtPreview; + } +#endif + if (!prt) { + return; + } + + NS_ASSERTION(mDocument, "We MUST have a document."); + // First, get the script global object from the document... + + for (uint32_t i=0;i<prt->mPrintDocList.Length();i++) { + nsPrintObject* po = prt->mPrintDocList.ElementAt(i); + NS_ASSERTION(po, "nsPrintObject can't be null!"); + + nsIDocument* doc = po->mDocument; + if (!doc) { + continue; + } + + if (nsCOMPtr<nsPIDOMWindow> window = doc->GetWindow()) { + nsCOMPtr<nsIScriptGlobalObject> scriptGlobalObj = do_QueryInterface(window); + nsIScriptContext *scx = scriptGlobalObj->GetContext(); + NS_WARN_IF_FALSE(scx, "Can't get nsIScriptContext"); + nsresult propThere = NS_PROPTABLE_PROP_NOT_THERE; + doc->GetProperty(nsGkAtoms::scriptEnabledBeforePrintOrPreview, + &propThere); + if (aDoTurnOn) { + if (propThere != NS_PROPTABLE_PROP_NOT_THERE) { + doc->DeleteProperty(nsGkAtoms::scriptEnabledBeforePrintOrPreview); + if (scx) { + scx->SetScriptsEnabled(true, false); + } + window->ResumeTimeouts(false); + } + } else { + // Have to be careful, because people call us over and over again with + // aDoTurnOn == false. So don't set the property if it's already + // set, since in that case we'd set it to the wrong value. + if (propThere == NS_PROPTABLE_PROP_NOT_THERE) { + // Stash the current value of IsScriptEnabled on the document, so + // that layout code running in print preview doesn't get confused. + doc->SetProperty(nsGkAtoms::scriptEnabledBeforePrintOrPreview, + NS_INT32_TO_PTR(doc->IsScriptEnabled())); + if (scx) { + scx->SetScriptsEnabled(false, false); + } + window->SuspendTimeouts(1, false); + } + } + } + } +} + +//----------------------------------------------------------------- +//-- Done: Misc Support Methods +//----------------------------------------------------------------- + + +//----------------------------------------------------------------- +//-- Section: Finishing up or Cleaning up +//----------------------------------------------------------------- + +//----------------------------------------------------------------- +void +nsPrintEngine::CloseProgressDialog(nsIWebProgressListener* aWebProgressListener) +{ + if (aWebProgressListener) { + aWebProgressListener->OnStateChange(nullptr, nullptr, nsIWebProgressListener::STATE_STOP|nsIWebProgressListener::STATE_IS_DOCUMENT, NS_OK); + } +} + +//----------------------------------------------------------------- +nsresult +nsPrintEngine::FinishPrintPreview() +{ + nsresult rv = NS_OK; + +#ifdef NS_PRINT_PREVIEW + + if (!mPrt) { + /* we're already finished with print preview */ + return rv; + } + + rv = DocumentReadyForPrinting(); + + SetIsCreatingPrintPreview(false); + + /* cleaup on failure + notify user */ + if (NS_FAILED(rv)) { + /* cleanup done, let's fire-up an error dialog to notify the user + * what went wrong... + */ + mPrt->OnEndPrinting(); + TurnScriptingOn(true); + + return rv; + } + + // At this point we are done preparing everything + // before it is to be created + + + if (mIsDoingPrintPreview && mOldPrtPreview) { + delete mOldPrtPreview; + mOldPrtPreview = nullptr; + } + + + mPrt->OnEndPrinting(); + + // PrintPreview was built using the mPrt (code reuse) + // then we assign it over + mPrtPreview = mPrt; + mPrt = nullptr; + +#endif // NS_PRINT_PREVIEW + + return NS_OK; +} + +//----------------------------------------------------------------- +//-- Done: Finishing up or Cleaning up +//----------------------------------------------------------------- + + +/*=============== Timer Related Code ======================*/ +nsresult +nsPrintEngine::StartPagePrintTimer(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); + + nsRefPtr<nsPagePrintTimer> timer = + new nsPagePrintTimer(this, mDocViewerPrint, printPageDelay); + timer.forget(&mPagePrintTimer); + } + + return mPagePrintTimer->Start(aPO); +} + +/*=============== nsIObserver Interface ======================*/ +NS_IMETHODIMP +nsPrintEngine::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *aData) +{ + nsresult rv = NS_ERROR_FAILURE; + + rv = InitPrintDocConstruction(true); + if (!mIsDoingPrinting && mPrtPreview) { + mPrtPreview->OnEndPrinting(); + } + + return rv; + +} + +//--------------------------------------------------------------- +//-- PLEvent Notification +//--------------------------------------------------------------- +class nsPrintCompletionEvent : public nsRunnable { +public: + nsPrintCompletionEvent(nsIDocumentViewerPrint *docViewerPrint) + : mDocViewerPrint(docViewerPrint) { + NS_ASSERTION(mDocViewerPrint, "mDocViewerPrint is null."); + } + + NS_IMETHOD Run() { + if (mDocViewerPrint) + mDocViewerPrint->OnDonePrinting(); + return NS_OK; + } + +private: + nsCOMPtr<nsIDocumentViewerPrint> mDocViewerPrint; +}; + +//----------------------------------------------------------- +void +nsPrintEngine::FirePrintCompletionEvent() +{ + nsCOMPtr<nsIRunnable> event = new nsPrintCompletionEvent(mDocViewerPrint); + if (NS_FAILED(NS_DispatchToCurrentThread(event))) + NS_WARNING("failed to dispatch print completion event"); +} + +//--------------------------------------------------------------- +//--------------------------------------------------------------- +//-- Debug helper routines +//--------------------------------------------------------------- +//--------------------------------------------------------------- +#if (defined(XP_WIN) || defined(XP_OS2)) && 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, int32_t aIndent) +{ + if (!aPresContext || !out) + return; + + nsIPresShell *shell = aPresContext->GetPresShell(); + if (shell) { + nsIFrame* frame = shell->FrameManager()->GetRootFrame(); + if (frame) { + frame->List(aPresContext, out, aIndent); + } + } +} + +/** --------------------------------------------------- + * Dumps Frames for Printing + */ +static void DumpFrames(FILE* out, + nsPresContext* aPresContext, + nsRenderingContext * 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->GetFirstPrincipalChild(); + 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 (NS_SUCCEEDED(child->IsVisibleForPainting(aPresContext, *aRendContext, true, &isSelected))) { + 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); + nsIPresShell* shell = nsPrintEngine::GetPresShellFor(aDocShell); + if (shell) { + nsViewManager* vm = shell->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; + nsCOMPtr<nsIDocShellTreeNode> docShellAsNode(do_QueryInterface(aDocShell)); + docShellAsNode->GetChildCount(&n); + for (i = 0; i < n; i++) { + nsCOMPtr<nsIDocShellTreeItem> child; + docShellAsNode->GetChildAt(i, getter_AddRefs(child)); + nsCOMPtr<nsIDocShell> childAsShell(do_QueryInterface(child)); + if (childAsShell) { + DumpViews(childAsShell, out); + } + } + } +} + +/** --------------------------------------------------- + * Dumps the Views and Frames + */ +void DumpLayoutData(char* aTitleStr, + char* aURLStr, + nsPresContext* aPresContext, + nsDeviceContext * aDC, + nsIFrame * aRootFrame, + nsIDocShekk * aDocShell, + FILE* aFD = nullptr) +{ + if (!kPrintingLogMod || kPrintingLogMod->level != 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"); + nsRefPtr<nsRenderingContext> renderingContext; + aDC->CreateRenderingContext(*getter_AddRefs(renderingContext)); + RootFrameList(aPresContext, fd, 0); + //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(nsTArray<nsPrintObject*> * aDocList) +{ + if (!kPrintingLogMod || kPrintingLogMod->level != DUMP_LAYOUT_LEVEL) return; + + NS_ASSERTION(aDocList, "Pointer is null!"); + + 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")); + int32_t cnt = aDocList->Length(); + for (int32_t i=0;i<cnt;i++) { + nsPrintObject* po = aDocList->ElementAt(i); + NS_ASSERTION(po, "nsPrintObject can't be null!"); + nsIFrame* rootFrame = nullptr; + if (po->mPresShell) { + rootFrame = po->mPresShell->FrameManager()->GetRootFrame(); + while (rootFrame != nullptr) { + nsIPageSequenceFrame * sqf = do_QueryFrame(rootFrame); + if (sqf) { + break; + } + rootFrame = rootFrame->GetFirstPrincipalChild(); + } + } + + PR_PL(("%s %d %d %d %p %p %p %p %p %d %d,%d,%d,%d\n", types[po->mFrameType], + po->IsPrintable(), po->mPrintAsIs, po->mHasBeenPrinted, po, po->mDocShell.get(), po->mSeqFrame, + po->mPageFrame, rootFrame, po->mPageNum, po->mRect.x, po->mRect.y, po->mRect.width, po->mRect.height)); + } +} + +//------------------------------------------------------------- +static void DumpPrintObjectsTree(nsPrintObject * aPO, int aLevel, FILE* aFD) +{ + if (!kPrintingLogMod || kPrintingLogMod->level != 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"); + } + int32_t cnt = aPO->mKids.Length(); + for (int32_t i=0;i<cnt;i++) { + nsPrintObject* po = aPO->mKids.ElementAt(i); + NS_ASSERTION(po, "nsPrintObject can't be null!"); + for (int32_t k=0;k<aLevel;k++) fprintf(fd, " "); + fprintf(fd, "%s %p %p %p %p %d %d,%d,%d,%d\n", types[po->mFrameType], po, po->mDocShell.get(), po->mSeqFrame, + po->mPageFrame, po->mPageNum, po->mRect.x, po->mRect.y, po->mRect.width, po->mRect.height); + } +} + +//------------------------------------------------------------- +static void GetDocTitleAndURL(nsPrintObject* aPO, char *& aDocStr, char *& aURLStr) +{ + aDocStr = nullptr; + aURLStr = nullptr; + + PRUnichar * docTitleStr; + PRUnichar * docURLStr; + nsPrintEngine::GetDisplayTitleAndURL(aPO, + &docTitleStr, &docURLStr, + nsPrintEngine::eDocTitleDefURLDoc); + + if (docTitleStr) { + nsAutoString strDocTitle(docTitleStr); + aDocStr = ToNewCString(strDocTitle); + nsMemory::Free(docTitleStr); + } + + if (docURLStr) { + nsAutoString strURL(docURLStr); + aURLStr = ToNewCString(strURL); + nsMemory::Free(docURLStr); + } +} + +//------------------------------------------------------------- +static void DumpPrintObjectsTreeLayout(nsPrintObject * aPO, + nsDeviceContext * aDC, + int aLevel, FILE * aFD) +{ + if (!kPrintingLogMod || kPrintingLogMod->level != 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->FrameManager()->GetRootFrame(); + } + for (int32_t k=0;k<aLevel;k++) fprintf(fd, " "); + fprintf(fd, "%s %p %p %p %p %d %d,%d,%d,%d\n", types[aPO->mFrameType], aPO, aPO->mDocShell.get(), aPO->mSeqFrame, + aPO->mPageFrame, aPO->mPageNum, aPO->mRect.x, aPO->mRect.y, aPO->mRect.width, aPO->mRect.height); + if (aPO->IsPrintable()) { + char * docStr; + char * urlStr; + GetDocTitleAndURL(aPO, docStr, urlStr); + DumpLayoutData(docStr, urlStr, aPO->mPresContext, aDC, rootFrame, aPO->mDocShell, fd); + if (docStr) nsMemory::Free(docStr); + if (urlStr) nsMemory::Free(urlStr); + } + fprintf(fd, "<***************************************************>\n"); + + int32_t cnt = aPO->mKids.Length(); + for (int32_t i=0;i<cnt;i++) { + nsPrintObject* po = aPO->mKids.ElementAt(i); + 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, nsTArray<nsPrintObject*> * aDocList) +{ + if (!kPrintingLogMod || kPrintingLogMod->level != DUMP_LAYOUT_LEVEL) return; + + NS_ASSERTION(aStr, "Pointer is null!"); + NS_ASSERTION(aDocList, "Pointer is null!"); + + PR_PL(("%s\n", aStr)); + DumpPrintObjectsList(aDocList); +} + +#define DUMP_DOC_LIST(_title) DumpPrintObjectsListStart((_title), mPrt->mPrintDocList); +#define DUMP_DOC_TREE DumpPrintObjectsTree(mPrt->mPrintObject); +#define DUMP_DOC_TREELAYOUT DumpPrintObjectsTreeLayout(mPrt->mPrintObject, mPrt->mPrintDC); + +#else +#define DUMP_DOC_LIST(_title) +#define DUMP_DOC_TREE +#define DUMP_DOC_TREELAYOUT +#endif + +//--------------------------------------------------------------- +//--------------------------------------------------------------- +//-- End of debug helper routines +//--------------------------------------------------------------- diff --git a/layout/printing/nsPrintEngine.h b/layout/printing/nsPrintEngine.h new file mode 100644 index 000000000..2a6b07746 --- /dev/null +++ b/layout/printing/nsPrintEngine.h @@ -0,0 +1,314 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ +#ifndef nsPrintEngine_h___ +#define nsPrintEngine_h___ + +#include "mozilla/Attributes.h" + +#include "nsCOMPtr.h" + +#include "nsPrintObject.h" +#include "nsPrintData.h" +#include "nsFrameList.h" +#include "mozilla/Attributes.h" +#include "nsIWebProgress.h" +#include "mozilla/dom/HTMLCanvasElement.h" +#include "nsIWebProgressListener.h" +#include "nsWeakReference.h" + +// Interfaces +#include "nsIDOMWindow.h" +#include "nsIObserver.h" + +// Classes +class nsPagePrintTimer; +class nsIDocShellTreeNode; +class nsDeviceContext; +class nsIDocument; +class nsIDocumentViewerPrint; +class nsPrintObject; +class nsIDocShell; +class nsIPageSequenceFrame; +class nsIWeakReference; + +//------------------------------------------------------------------------ +// nsPrintEngine Class +// +//------------------------------------------------------------------------ +class nsPrintEngine MOZ_FINAL : public nsIObserver, + public nsIWebProgressListener, + public nsSupportsWeakReference +{ +public: + // nsISupports interface... + NS_DECL_ISUPPORTS + + // nsIObserver + NS_DECL_NSIOBSERVER + + NS_DECL_NSIWEBPROGRESSLISTENER + + // Old nsIWebBrowserPrint methods; not cleaned up yet + NS_IMETHOD Print(nsIPrintSettings* aPrintSettings, + nsIWebProgressListener* aWebProgressListener); + NS_IMETHOD PrintPreview(nsIPrintSettings* aPrintSettings, + nsIDOMWindow *aChildDOMWin, + nsIWebProgressListener* aWebProgressListener); + NS_IMETHOD GetIsFramesetDocument(bool *aIsFramesetDocument); + NS_IMETHOD GetIsIFrameSelected(bool *aIsIFrameSelected); + NS_IMETHOD GetIsRangeSelection(bool *aIsRangeSelection); + NS_IMETHOD GetIsFramesetFrameSelected(bool *aIsFramesetFrameSelected); + NS_IMETHOD GetPrintPreviewNumPages(int32_t *aPrintPreviewNumPages); + NS_IMETHOD EnumerateDocumentNames(uint32_t* aCount, PRUnichar*** aResult); + static nsresult GetGlobalPrintSettings(nsIPrintSettings** aPrintSettings); + NS_IMETHOD GetDoingPrint(bool *aDoingPrint); + NS_IMETHOD GetDoingPrintPreview(bool *aDoingPrintPreview); + NS_IMETHOD GetCurrentPrintSettings(nsIPrintSettings **aCurrentPrintSettings); + + + // This enum tells indicates what the default should be for the title + // if the title from the document is null + enum eDocTitleDefault { + eDocTitleDefNone, + eDocTitleDefBlank, + eDocTitleDefURLDoc + }; + + nsPrintEngine(); + ~nsPrintEngine(); + + void Destroy(); + void DestroyPrintingData(); + + nsresult Initialize(nsIDocumentViewerPrint* aDocViewerPrint, + nsIWeakReference* aContainer, + nsIDocument* aDocument, + float aScreenDPI, + FILE* aDebugFile); + + nsresult GetSeqFrameAndCountPages(nsIFrame*& aSeqFrame, int32_t& aCount); + + // + // The following three methods are used for printing... + // + nsresult DocumentReadyForPrinting(); + nsresult GetSelectionDocument(nsIDeviceContextSpec * aDevSpec, + nsIDocument ** aNewDoc); + + nsresult SetupToPrintContent(); + nsresult EnablePOsForPrinting(); + nsPrintObject* FindSmallestSTF(); + + bool PrintDocContent(nsPrintObject* aPO, nsresult& aStatus); + nsresult DoPrint(nsPrintObject * aPO); + + void SetPrintPO(nsPrintObject* aPO, bool aPrint); + + void TurnScriptingOn(bool aDoTurnOn); + bool CheckDocumentForPPCaching(); + void InstallPrintPreviewListener(); + + // nsIDocumentViewerPrint Printing Methods + bool HasPrintCallbackCanvas(); + bool PrePrintPage(); + bool PrintPage(nsPrintObject* aPOect, bool& aInRange); + bool DonePrintingPages(nsPrintObject* aPO, nsresult aResult); + + //--------------------------------------------------------------------- + void BuildDocTree(nsIDocShellTreeNode * aParentNode, + nsTArray<nsPrintObject*> * aDocList, + nsPrintObject * aPO); + nsresult ReflowDocList(nsPrintObject * aPO, bool aSetPixelScale); + + nsresult ReflowPrintObject(nsPrintObject * aPO); + + void CheckForChildFrameSets(nsPrintObject* aPO); + + void CalcNumPrintablePages(int32_t& aNumPages); + void ShowPrintProgress(bool aIsForPrinting, bool& aDoNotify); + nsresult CleanupOnFailure(nsresult aResult, bool aIsPrinting); + // If FinishPrintPreview() fails, caller may need to reset the state of the + // object, for example by calling CleanupOnFailure(). + nsresult FinishPrintPreview(); + static void CloseProgressDialog(nsIWebProgressListener* aWebProgressListener); + void SetDocAndURLIntoProgress(nsPrintObject* aPO, + nsIPrintProgressParams* aParams); + void ElipseLongString(PRUnichar *& aStr, const uint32_t aLen, bool aDoFront); + nsresult CheckForPrinters(nsIPrintSettings* aPrintSettings); + void CleanupDocTitleArray(PRUnichar**& aArray, int32_t& aCount); + + bool IsThereARangeSelection(nsIDOMWindow * aDOMWin); + + //--------------------------------------------------------------------- + + + // Timer Methods + nsresult StartPagePrintTimer(nsPrintObject* aPO); + + bool IsWindowsInOurSubTree(nsPIDOMWindow * aDOMWindow); + static bool IsParentAFrameSet(nsIDocShell * aParent); + bool IsThereAnIFrameSelected(nsIDocShell* aDocShell, + nsIDOMWindow* aDOMWin, + bool& aIsParentFrameSet); + + static nsPrintObject* FindPrintObjectByDOMWin(nsPrintObject* aParentObject, + nsIDOMWindow* aDOMWin); + + // get the currently infocus frame for the document viewer + already_AddRefed<nsIDOMWindow> FindFocusedDOMWindow(); + + //--------------------------------------------------------------------- + // Static Methods + //--------------------------------------------------------------------- + static void GetDocumentTitleAndURL(nsIDocument* aDoc, + PRUnichar** aTitle, + PRUnichar** aURLStr); + void GetDisplayTitleAndURL(nsPrintObject* aPO, + PRUnichar** aTitle, + PRUnichar** aURLStr, + eDocTitleDefault aDefType); + static void ShowPrintErrorDialog(nsresult printerror, + bool aIsPrinting = true); + + static bool HasFramesetChild(nsIContent* aContent); + + bool CheckBeforeDestroy(); + nsresult Cancelled(); + + nsIPresShell* GetPrintPreviewPresShell() {return mPrtPreview->mPrintObject->mPresShell;} + + float GetPrintPreviewScale() { return mPrtPreview->mPrintObject-> + mPresContext->GetPrintPreviewScale(); } + + static nsIPresShell* GetPresShellFor(nsIDocShell* aDocShell); + + // These calls also update the DocViewer + void SetIsPrinting(bool aIsPrinting); + bool GetIsPrinting() + { + return mIsDoingPrinting; + } + void SetIsPrintPreview(bool aIsPrintPreview); + bool GetIsPrintPreview() + { + return mIsDoingPrintPreview; + } + void SetIsCreatingPrintPreview(bool aIsCreatingPrintPreview) + { + mIsCreatingPrintPreview = aIsCreatingPrintPreview; + } + bool GetIsCreatingPrintPreview() + { + return mIsCreatingPrintPreview; + } + + void SetDisallowSelectionPrint(bool aDisallowSelectionPrint) + { + mDisallowSelectionPrint = aDisallowSelectionPrint; + } + + void SetNoMarginBoxes(bool aNoMarginBoxes) { + mNoMarginBoxes = aNoMarginBoxes; + } + +protected: + + nsresult CommonPrint(bool aIsPrintPreview, nsIPrintSettings* aPrintSettings, + nsIWebProgressListener* aWebProgressListener, + nsIDOMDocument* aDoc); + + nsresult DoCommonPrint(bool aIsPrintPreview, nsIPrintSettings* aPrintSettings, + nsIWebProgressListener* aWebProgressListener, + nsIDOMDocument* aDoc); + + void FirePrintCompletionEvent(); + static nsresult GetSeqFrameAndCountPagesInternal(nsPrintObject* aPO, + nsIFrame*& aSeqFrame, + int32_t& aCount); + + static nsresult FindSelectionBoundsWithList(nsPresContext* aPresContext, + nsRenderingContext& aRC, + nsFrameList::Enumerator& aChildFrames, + nsIFrame * aParentFrame, + nsRect& aRect, + nsIFrame *& aStartFrame, + nsRect& aStartRect, + nsIFrame *& aEndFrame, + nsRect& aEndRect); + + static nsresult FindSelectionBounds(nsPresContext* aPresContext, + nsRenderingContext& aRC, + nsIFrame * aParentFrame, + nsRect& aRect, + nsIFrame *& aStartFrame, + nsRect& aStartRect, + nsIFrame *& aEndFrame, + nsRect& aEndRect); + + static nsresult GetPageRangeForSelection(nsIPresShell * aPresShell, + nsPresContext* aPresContext, + nsRenderingContext& aRC, + nsISelection* aSelection, + nsIPageSequenceFrame* aPageSeqFrame, + nsIFrame** aStartFrame, + int32_t& aStartPageNum, + nsRect& aStartRect, + nsIFrame** aEndFrame, + int32_t& aEndPageNum, + nsRect& aEndRect); + + static void MapContentForPO(nsPrintObject* aPO, nsIContent* aContent); + + static void MapContentToWebShells(nsPrintObject* aRootPO, nsPrintObject* aPO); + + static void SetPrintAsIs(nsPrintObject* aPO, bool aAsIs = true); + + // Static member variables + bool mIsCreatingPrintPreview; + bool mIsDoingPrinting; + bool mIsDoingPrintPreview; // per DocumentViewer + bool mProgressDialogIsShown; + + nsCOMPtr<nsIDocumentViewerPrint> mDocViewerPrint; + nsWeakPtr mContainer; + float mScreenDPI; + + nsPrintData* mPrt; + nsPagePrintTimer* mPagePrintTimer; + nsIPageSequenceFrame* mPageSeqFrame; + + // Print Preview + nsPrintData* mPrtPreview; + nsPrintData* mOldPrtPreview; + + nsCOMPtr<nsIDocument> mDocument; + + FILE* mDebugFile; + + int32_t mLoadCounter; + bool mDidLoadDataForPrinting; + bool mIsDestroying; + bool mDisallowSelectionPrint; + bool mNoMarginBoxes; + + nsresult AfterNetworkPrint(bool aHandleError); + + nsresult SetRootView(nsPrintObject* aPO, + bool& aDoReturn, + bool& aDocumentIsTopLevel, + nsSize& aAdjSize); + nsView* GetParentViewForRoot(); + bool DoSetPixelScale(); + void UpdateZoomRatio(nsPrintObject* aPO, bool aSetPixelScale); + nsresult ReconstructAndReflow(bool aDoSetPixelScale); + nsresult UpdateSelectionAndShrinkPrintObject(nsPrintObject* aPO, + bool aDocumentIsTopLevel); + nsresult InitPrintDocConstruction(bool aHandleError); + void FirePrintPreviewUpdateEvent(); +private: + nsPrintEngine& operator=(const nsPrintEngine& aOther) MOZ_DELETE; +}; + +#endif /* nsPrintEngine_h___ */ diff --git a/layout/printing/nsPrintObject.cpp b/layout/printing/nsPrintObject.cpp new file mode 100644 index 000000000..6a1d5c582 --- /dev/null +++ b/layout/printing/nsPrintObject.cpp @@ -0,0 +1,111 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "nsPrintObject.h" +#include "nsIContentViewer.h" +#include "nsIDOMDocument.h" +#include "nsContentUtils.h" +#include "nsIInterfaceRequestorUtils.h" +#include "nsPIDOMWindow.h" +#include "nsGkAtoms.h" +#include "nsComponentManagerUtils.h" +#include "nsIDocShellTreeItem.h" +#include "nsIBaseWindow.h" + +//--------------------------------------------------- +//-- nsPrintObject Class Impl +//--------------------------------------------------- +nsPrintObject::nsPrintObject() : + mContent(nullptr), mFrameType(eFrame), mParent(nullptr), + mHasBeenPrinted(false), mDontPrint(true), mPrintAsIs(false), + mInvisible(false), mDidCreateDocShell(false), + mShrinkRatio(1.0), mZoomRatio(1.0) +{ + MOZ_COUNT_CTOR(nsPrintObject); +} + + +nsPrintObject::~nsPrintObject() +{ + MOZ_COUNT_DTOR(nsPrintObject); + for (uint32_t i=0;i<mKids.Length();i++) { + nsPrintObject* po = mKids[i]; + delete po; + } + + DestroyPresentation(); + if (mDidCreateDocShell && mDocShell) { + nsCOMPtr<nsIBaseWindow> baseWin(do_QueryInterface(mDocShell)); + if (baseWin) { + baseWin->Destroy(); + } + } + mDocShell = nullptr; + mTreeOwner = nullptr; // mTreeOwner must be released after mDocShell; +} + +//------------------------------------------------------------------ +nsresult +nsPrintObject::Init(nsIDocShell* aDocShell, nsIDOMDocument* aDoc, + bool aPrintPreview) +{ + mPrintPreview = aPrintPreview; + + if (mPrintPreview || mParent) { + mDocShell = aDocShell; + } else { + mTreeOwner = do_GetInterface(aDocShell); + int32_t itemType = 0; + aDocShell->GetItemType(&itemType); + // Create a container docshell for printing. + mDocShell = do_CreateInstance("@mozilla.org/docshell;1"); + NS_ENSURE_TRUE(mDocShell, NS_ERROR_OUT_OF_MEMORY); + mDidCreateDocShell = true; + mDocShell->SetItemType(itemType); + mDocShell->SetTreeOwner(mTreeOwner); + } + NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE); + + nsCOMPtr<nsIDOMDocument> dummy = do_GetInterface(mDocShell); + nsCOMPtr<nsIContentViewer> viewer; + mDocShell->GetContentViewer(getter_AddRefs(viewer)); + NS_ENSURE_STATE(viewer); + + nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDoc); + NS_ENSURE_STATE(doc); + + if (mParent) { + nsCOMPtr<nsPIDOMWindow> window = doc->GetWindow(); + if (window) { + mContent = do_QueryInterface(window->GetFrameElementInternal()); + } + mDocument = doc; + return NS_OK; + } + + mDocument = doc->CreateStaticClone(mDocShell); + nsCOMPtr<nsIDOMDocument> clonedDOMDoc = do_QueryInterface(mDocument); + NS_ENSURE_STATE(clonedDOMDoc); + + viewer->SetDOMDocument(clonedDOMDoc); + return NS_OK; +} + +//------------------------------------------------------------------ +// Resets PO by destroying the presentation +void +nsPrintObject::DestroyPresentation() +{ + if (mPresShell) { + mPresShell->EndObservingDocument(); + nsAutoScriptBlocker scriptBlocker; + nsCOMPtr<nsIPresShell> shell = mPresShell; + mPresShell = nullptr; + shell->Destroy(); + } + mPresContext = nullptr; + mViewManager = nullptr; +} + diff --git a/layout/printing/nsPrintObject.h b/layout/printing/nsPrintObject.h new file mode 100644 index 000000000..00ec73937 --- /dev/null +++ b/layout/printing/nsPrintObject.h @@ -0,0 +1,72 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ +#ifndef nsPrintObject_h___ +#define nsPrintObject_h___ + +#include "mozilla/Attributes.h" + +// Interfaces +#include "nsCOMPtr.h" +#include "nsIPresShell.h" +#include "nsStyleSet.h" +#include "nsViewManager.h" +#include "nsIDocShell.h" +#include "nsIDocShellTreeOwner.h" + +class nsIContent; +class nsIDocument; +class nsPresContext; + +// nsPrintObject Document Type +enum PrintObjectType {eDoc = 0, eFrame = 1, eIFrame = 2, eFrameSet = 3}; + +//--------------------------------------------------- +//-- nsPrintObject Class +//--------------------------------------------------- +class nsPrintObject +{ + +public: + nsPrintObject(); + ~nsPrintObject(); // non-virtual + + // Methods + nsresult Init(nsIDocShell* aDocShell, nsIDOMDocument* aDoc, + bool aPrintPreview); + + bool IsPrintable() { return !mDontPrint; } + void DestroyPresentation(); + + // Data Members + nsCOMPtr<nsIDocShell> mDocShell; + nsCOMPtr<nsIDocShellTreeOwner> mTreeOwner; + nsCOMPtr<nsIDocument> mDocument; + + nsRefPtr<nsPresContext> mPresContext; + nsCOMPtr<nsIPresShell> mPresShell; + nsRefPtr<nsViewManager> mViewManager; + + nsCOMPtr<nsIContent> mContent; + PrintObjectType mFrameType; + + nsTArray<nsPrintObject*> mKids; + nsPrintObject* mParent; + bool mHasBeenPrinted; + bool mDontPrint; + bool mPrintAsIs; + bool mInvisible; // Indicates PO is set to not visible by CSS + bool mPrintPreview; + bool mDidCreateDocShell; + float mShrinkRatio; + float mZoomRatio; + +private: + nsPrintObject& operator=(const nsPrintObject& aOther) MOZ_DELETE; +}; + + + +#endif /* nsPrintObject_h___ */ + diff --git a/layout/printing/nsPrintPreviewListener.cpp b/layout/printing/nsPrintPreviewListener.cpp new file mode 100644 index 000000000..000aad7b9 --- /dev/null +++ b/layout/printing/nsPrintPreviewListener.cpp @@ -0,0 +1,189 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * 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 "nsPrintPreviewListener.h" + +#include "mozilla/dom/Element.h" +#include "nsDOMEvent.h" +#include "nsIDOMWindow.h" +#include "nsPIDOMWindow.h" +#include "nsIDOMElement.h" +#include "nsIDOMKeyEvent.h" +#include "nsIDOMEvent.h" +#include "nsIDocument.h" +#include "nsIDocShell.h" +#include "nsPresContext.h" +#include "nsFocusManager.h" +#include "nsLiteralString.h" + +using namespace mozilla; +using namespace mozilla::dom; + +NS_IMPL_ISUPPORTS1(nsPrintPreviewListener, nsIDOMEventListener) + + +// +// nsPrintPreviewListener ctor +// +nsPrintPreviewListener::nsPrintPreviewListener(EventTarget* aTarget) + : mEventTarget(aTarget) +{ + NS_ADDREF_THIS(); +} // ctor + + +//------------------------------------------------------- +// +// AddListeners +// +// Subscribe to the events that will allow us to track various events. +// +nsresult +nsPrintPreviewListener::AddListeners() +{ + if (mEventTarget) { + mEventTarget->AddEventListener(NS_LITERAL_STRING("click"), this, true); + mEventTarget->AddEventListener(NS_LITERAL_STRING("contextmenu"), this, true); + mEventTarget->AddEventListener(NS_LITERAL_STRING("keydown"), this, true); + mEventTarget->AddEventListener(NS_LITERAL_STRING("keypress"), this, true); + mEventTarget->AddEventListener(NS_LITERAL_STRING("keyup"), this, true); + mEventTarget->AddEventListener(NS_LITERAL_STRING("mousedown"), this, true); + mEventTarget->AddEventListener(NS_LITERAL_STRING("mousemove"), this, true); + mEventTarget->AddEventListener(NS_LITERAL_STRING("mouseout"), this, true); + mEventTarget->AddEventListener(NS_LITERAL_STRING("mouseover"), this, true); + mEventTarget->AddEventListener(NS_LITERAL_STRING("mouseup"), this, true); + } + + return NS_OK; +} + + +//------------------------------------------------------- +// +// RemoveListeners +// +// Unsubscribe from all the various events that we were listening to. +// +nsresult +nsPrintPreviewListener::RemoveListeners() +{ + if (mEventTarget) { + mEventTarget->RemoveEventListener(NS_LITERAL_STRING("click"), this, true); + mEventTarget->RemoveEventListener(NS_LITERAL_STRING("contextmenu"), this, true); + mEventTarget->RemoveEventListener(NS_LITERAL_STRING("keydown"), this, true); + mEventTarget->RemoveEventListener(NS_LITERAL_STRING("keypress"), this, true); + mEventTarget->RemoveEventListener(NS_LITERAL_STRING("keyup"), this, true); + mEventTarget->RemoveEventListener(NS_LITERAL_STRING("mousedown"), this, true); + mEventTarget->RemoveEventListener(NS_LITERAL_STRING("mousemove"), this, true); + mEventTarget->RemoveEventListener(NS_LITERAL_STRING("mouseout"), this, true); + mEventTarget->RemoveEventListener(NS_LITERAL_STRING("mouseover"), this, true); + mEventTarget->RemoveEventListener(NS_LITERAL_STRING("mouseup"), this, true); + } + + return NS_OK; +} + +//------------------------------------------------------- +// +// GetActionForEvent +// +// Helper function to let certain key events through +// +enum eEventAction { + eEventAction_Tab, eEventAction_ShiftTab, + eEventAction_Propagate, eEventAction_Suppress +}; + +static eEventAction +GetActionForEvent(nsIDOMEvent* aEvent) +{ + static const uint32_t kOKKeyCodes[] = { + nsIDOMKeyEvent::DOM_VK_PAGE_UP, nsIDOMKeyEvent::DOM_VK_PAGE_DOWN, + nsIDOMKeyEvent::DOM_VK_UP, nsIDOMKeyEvent::DOM_VK_DOWN, + nsIDOMKeyEvent::DOM_VK_HOME, nsIDOMKeyEvent::DOM_VK_END + }; + + nsCOMPtr<nsIDOMKeyEvent> keyEvent(do_QueryInterface(aEvent)); + if (keyEvent) { + bool b; + keyEvent->GetAltKey(&b); + if (b) return eEventAction_Suppress; + keyEvent->GetCtrlKey(&b); + if (b) return eEventAction_Suppress; + + keyEvent->GetShiftKey(&b); + + uint32_t keyCode; + keyEvent->GetKeyCode(&keyCode); + if (keyCode == nsIDOMKeyEvent::DOM_VK_TAB) + return b ? eEventAction_ShiftTab : eEventAction_Tab; + + uint32_t charCode; + keyEvent->GetCharCode(&charCode); + if (charCode == ' ' || keyCode == nsIDOMKeyEvent::DOM_VK_SPACE) + return eEventAction_Propagate; + + if (b) return eEventAction_Suppress; + + for (uint32_t i = 0; i < sizeof(kOKKeyCodes)/sizeof(kOKKeyCodes[0]); ++i) { + if (keyCode == kOKKeyCodes[i]) { + return eEventAction_Propagate; + } + } + } + return eEventAction_Suppress; +} + +NS_IMETHODIMP +nsPrintPreviewListener::HandleEvent(nsIDOMEvent* aEvent) +{ + nsCOMPtr<nsIContent> content = do_QueryInterface( + aEvent ? aEvent->InternalDOMEvent()->GetOriginalTarget() : nullptr); + if (content && !content->IsXUL()) { + eEventAction action = ::GetActionForEvent(aEvent); + switch (action) { + case eEventAction_Tab: + case eEventAction_ShiftTab: + { + nsAutoString eventString; + aEvent->GetType(eventString); + if (eventString == NS_LITERAL_STRING("keydown")) { + // Handle tabbing explicitly here since we don't want focus ending up + // inside the content document, bug 244128. + nsIDocument* doc = content->GetCurrentDoc(); + NS_ASSERTION(doc, "no document"); + + nsIDocument* parentDoc = doc->GetParentDocument(); + NS_ASSERTION(parentDoc, "no parent document"); + + nsCOMPtr<nsIDOMWindow> win = do_QueryInterface(parentDoc->GetWindow()); + + nsIFocusManager* fm = nsFocusManager::GetFocusManager(); + if (fm && win) { + dom::Element* fromElement = parentDoc->FindContentForSubDocument(doc); + nsCOMPtr<nsIDOMElement> from = do_QueryInterface(fromElement); + + bool forward = (action == eEventAction_Tab); + nsCOMPtr<nsIDOMElement> result; + fm->MoveFocus(win, from, + forward ? nsIFocusManager::MOVEFOCUS_FORWARD : + nsIFocusManager::MOVEFOCUS_BACKWARD, + nsIFocusManager::FLAG_BYKEY, getter_AddRefs(result)); + } + } + } + // fall-through + case eEventAction_Suppress: + aEvent->StopPropagation(); + aEvent->PreventDefault(); + break; + case eEventAction_Propagate: + // intentionally empty + break; + } + } + return NS_OK; +} diff --git a/layout/printing/nsPrintPreviewListener.h b/layout/printing/nsPrintPreviewListener.h new file mode 100644 index 000000000..dbf345431 --- /dev/null +++ b/layout/printing/nsPrintPreviewListener.h @@ -0,0 +1,47 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * 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/. */ + +#ifndef nsPrintPreviewListener_h__ +#define nsPrintPreviewListener_h__ + +// Interfaces needed to be included +#include "nsIDOMEventListener.h" +#include "mozilla/dom/EventTarget.h" +// Helper Classes +#include "nsCOMPtr.h" +#include "mozilla/Attributes.h" + +// +// class nsPrintPreviewListener +// +// The class that listens to the chrome events and tells the embedding +// chrome to show context menus, as appropriate. Handles registering itself +// with the DOM with AddChromeListeners() and removing itself with +// RemoveChromeListeners(). +// +class nsPrintPreviewListener MOZ_FINAL : public nsIDOMEventListener + +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIDOMEVENTLISTENER + + nsPrintPreviewListener(mozilla::dom::EventTarget* aTarget); + + // Add/remove the relevant listeners, based on what interfaces + // the embedding chrome implements. + nsresult AddListeners(); + nsresult RemoveListeners(); + +private: + + nsCOMPtr<mozilla::dom::EventTarget> mEventTarget; + +}; // class nsPrintPreviewListener + + + +#endif /* nsPrintPreviewListener_h__ */ |