summaryrefslogtreecommitdiffstats
path: root/layout/printing
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /layout/printing
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'layout/printing')
-rw-r--r--layout/printing/PrintTranslator.cpp102
-rw-r--r--layout/printing/PrintTranslator.h178
-rw-r--r--layout/printing/crashtests/509839-1.html10
-rw-r--r--layout/printing/crashtests/509839-2.html8
-rw-r--r--layout/printing/crashtests/576878.xhtml9
-rw-r--r--layout/printing/crashtests/793844.html10
-rw-r--r--layout/printing/crashtests/crashtests.list4
-rw-r--r--layout/printing/ipc/PRemotePrintJob.ipdl58
-rw-r--r--layout/printing/ipc/RemotePrintJobChild.cpp155
-rw-r--r--layout/printing/ipc/RemotePrintJobChild.h61
-rw-r--r--layout/printing/ipc/RemotePrintJobParent.cpp244
-rw-r--r--layout/printing/ipc/RemotePrintJobParent.h84
-rw-r--r--layout/printing/moz.build37
-rw-r--r--layout/printing/nsIPrintProgress.idl43
-rw-r--r--layout/printing/nsIPrintProgressParams.idl14
-rw-r--r--layout/printing/nsIPrintStatusFeedback.idl23
-rw-r--r--layout/printing/nsPagePrintTimer.cpp224
-rw-r--r--layout/printing/nsPagePrintTimer.h93
-rw-r--r--layout/printing/nsPrintData.cpp140
-rw-r--r--layout/printing/nsPrintData.h96
-rw-r--r--layout/printing/nsPrintEngine.cpp4071
-rw-r--r--layout/printing/nsPrintEngine.h305
-rw-r--r--layout/printing/nsPrintObject.cpp113
-rw-r--r--layout/printing/nsPrintObject.h71
-rw-r--r--layout/printing/nsPrintPreviewListener.cpp212
-rw-r--r--layout/printing/nsPrintPreviewListener.h53
26 files changed, 6418 insertions, 0 deletions
diff --git a/layout/printing/PrintTranslator.cpp b/layout/printing/PrintTranslator.cpp
new file mode 100644
index 000000000..0bda505ba
--- /dev/null
+++ b/layout/printing/PrintTranslator.cpp
@@ -0,0 +1,102 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PrintTranslator.h"
+
+#include "gfxContext.h"
+#include "nsDeviceContext.h"
+#include "mozilla/gfx/RecordedEvent.h"
+#include "mozilla/gfx/RecordingTypes.h"
+#include "mozilla/UniquePtr.h"
+
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace layout {
+
+PrintTranslator::PrintTranslator(nsDeviceContext* aDeviceContext)
+ : mDeviceContext(aDeviceContext)
+{
+ RefPtr<gfxContext> context = mDeviceContext->CreateReferenceRenderingContext();
+ mBaseDT = context->GetDrawTarget();
+}
+
+bool
+PrintTranslator::TranslateRecording(std::istream& aRecording)
+{
+ uint32_t magicInt;
+ ReadElement(aRecording, magicInt);
+ if (magicInt != mozilla::gfx::kMagicInt) {
+ return false;
+ }
+
+ uint16_t majorRevision;
+ ReadElement(aRecording, majorRevision);
+ if (majorRevision != kMajorRevision) {
+ return false;
+ }
+
+ uint16_t minorRevision;
+ ReadElement(aRecording, minorRevision);
+ if (minorRevision > kMinorRevision) {
+ return false;
+ }
+
+ int32_t eventType;
+ ReadElement(aRecording, eventType);
+ while (aRecording.good()) {
+ UniquePtr<RecordedEvent> recordedEvent(
+ RecordedEvent::LoadEventFromStream(aRecording,
+ static_cast<RecordedEvent::EventType>(eventType)));
+
+ // Make sure that the whole event was read from the stream successfully.
+ if (!aRecording.good() || !recordedEvent) {
+ return false;
+ }
+
+ if (!recordedEvent->PlayEvent(this)) {
+ return false;
+ }
+
+ ReadElement(aRecording, eventType);
+ }
+
+ return true;
+}
+
+already_AddRefed<DrawTarget>
+PrintTranslator::CreateDrawTarget(ReferencePtr aRefPtr,
+ const gfx::IntSize &aSize,
+ gfx::SurfaceFormat aFormat)
+{
+ RefPtr<gfxContext> context = mDeviceContext->CreateRenderingContext();
+ if (!context) {
+ NS_WARNING("Failed to create rendering context for print.");
+ return nullptr;
+ }
+
+ RefPtr<DrawTarget> drawTarget = context->GetDrawTarget();
+ AddDrawTarget(aRefPtr, drawTarget);
+ return drawTarget.forget();
+}
+
+FontType
+PrintTranslator::GetDesiredFontType()
+{
+ switch (mBaseDT->GetBackendType()) {
+ case BackendType::DIRECT2D:
+ return FontType::DWRITE;
+ case BackendType::CAIRO:
+ return FontType::CAIRO;
+ case BackendType::SKIA:
+ return FontType::SKIA;
+ default:
+ return FontType::CAIRO;
+ }
+}
+
+} // namespace layout
+} // namespace mozilla
diff --git a/layout/printing/PrintTranslator.h b/layout/printing/PrintTranslator.h
new file mode 100644
index 000000000..92d8bbed2
--- /dev/null
+++ b/layout/printing/PrintTranslator.h
@@ -0,0 +1,178 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layout_PrintTranslator_h
+#define mozilla_layout_PrintTranslator_h
+
+#include <istream>
+
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Filters.h"
+#include "mozilla/gfx/RecordedEvent.h"
+#include "nsRefPtrHashtable.h"
+
+class nsDeviceContext;
+
+namespace mozilla {
+namespace layout {
+
+using gfx::Translator;
+using gfx::ReferencePtr;
+using gfx::DrawTarget;
+using gfx::Path;
+using gfx::SourceSurface;
+using gfx::FilterNode;
+using gfx::GradientStops;
+using gfx::ScaledFont;
+using gfx::NativeFontResource;
+
+class PrintTranslator final : public Translator
+{
+public:
+ explicit PrintTranslator(nsDeviceContext* aDeviceContext);
+
+ bool TranslateRecording(std::istream& aRecording);
+
+ DrawTarget* LookupDrawTarget(ReferencePtr aRefPtr) final
+ {
+ DrawTarget* result = mDrawTargets.GetWeak(aRefPtr);
+ MOZ_ASSERT(result);
+ return result;
+ }
+
+ Path* LookupPath(ReferencePtr aRefPtr) final
+ {
+ Path* result = mPaths.GetWeak(aRefPtr);
+ MOZ_ASSERT(result);
+ return result;
+ }
+
+ SourceSurface* LookupSourceSurface(ReferencePtr aRefPtr) final
+ {
+ SourceSurface* result = mSourceSurfaces.GetWeak(aRefPtr);
+ MOZ_ASSERT(result);
+ return result;
+ }
+
+ FilterNode* LookupFilterNode(ReferencePtr aRefPtr) final
+ {
+ FilterNode* result = mFilterNodes.GetWeak(aRefPtr);
+ MOZ_ASSERT(result);
+ return result;
+ }
+
+ GradientStops* LookupGradientStops(ReferencePtr aRefPtr) final
+ {
+ GradientStops* result = mGradientStops.GetWeak(aRefPtr);
+ MOZ_ASSERT(result);
+ return result;
+ }
+
+ ScaledFont* LookupScaledFont(ReferencePtr aRefPtr) final
+ {
+ ScaledFont* result = mScaledFonts.GetWeak(aRefPtr);
+ MOZ_ASSERT(result);
+ return result;
+ }
+
+ NativeFontResource* LookupNativeFontResource(uint64_t aKey) final
+ {
+ NativeFontResource* result = mNativeFontResources.GetWeak(aKey);
+ MOZ_ASSERT(result);
+ return result;
+ }
+
+ void AddDrawTarget(ReferencePtr aRefPtr, DrawTarget *aDT) final
+ {
+ mDrawTargets.Put(aRefPtr, aDT);
+ }
+
+ void AddPath(ReferencePtr aRefPtr, Path *aPath) final
+ {
+ mPaths.Put(aRefPtr, aPath);
+ }
+
+ void AddSourceSurface(ReferencePtr aRefPtr, SourceSurface *aSurface) final
+ {
+ mSourceSurfaces.Put(aRefPtr, aSurface);
+ }
+
+ void AddFilterNode(ReferencePtr aRefPtr, FilterNode *aFilter) final
+ {
+ mFilterNodes.Put(aRefPtr, aFilter);
+ }
+
+ void AddGradientStops(ReferencePtr aRefPtr, GradientStops *aStops) final
+ {
+ mGradientStops.Put(aRefPtr, aStops);
+ }
+
+ void AddScaledFont(ReferencePtr aRefPtr, ScaledFont *aScaledFont) final
+ {
+ mScaledFonts.Put(aRefPtr, aScaledFont);
+ }
+
+ void AddNativeFontResource(uint64_t aKey,
+ NativeFontResource *aScaledFontResouce) final
+ {
+ mNativeFontResources.Put(aKey, aScaledFontResouce);
+ }
+
+ void RemoveDrawTarget(ReferencePtr aRefPtr) final
+ {
+ mDrawTargets.Remove(aRefPtr);
+ }
+
+ void RemovePath(ReferencePtr aRefPtr) final
+ {
+ mPaths.Remove(aRefPtr);
+ }
+
+ void RemoveSourceSurface(ReferencePtr aRefPtr) final
+ {
+ mSourceSurfaces.Remove(aRefPtr);
+ }
+
+ void RemoveFilterNode(ReferencePtr aRefPtr) final
+ {
+ mFilterNodes.Remove(aRefPtr);
+ }
+
+ void RemoveGradientStops(ReferencePtr aRefPtr) final
+ {
+ mGradientStops.Remove(aRefPtr);
+ }
+
+ void RemoveScaledFont(ReferencePtr aRefPtr) final
+ {
+ mScaledFonts.Remove(aRefPtr);
+ }
+
+ already_AddRefed<DrawTarget> CreateDrawTarget(ReferencePtr aRefPtr,
+ const gfx::IntSize &aSize,
+ gfx::SurfaceFormat aFormat) final;
+
+ mozilla::gfx::DrawTarget* GetReferenceDrawTarget() final { return mBaseDT; }
+
+ mozilla::gfx::FontType GetDesiredFontType() final;
+
+private:
+ RefPtr<nsDeviceContext> mDeviceContext;
+ RefPtr<DrawTarget> mBaseDT;
+
+ nsRefPtrHashtable<nsPtrHashKey<void>, DrawTarget> mDrawTargets;
+ nsRefPtrHashtable<nsPtrHashKey<void>, Path> mPaths;
+ nsRefPtrHashtable<nsPtrHashKey<void>, SourceSurface> mSourceSurfaces;
+ nsRefPtrHashtable<nsPtrHashKey<void>, FilterNode> mFilterNodes;
+ nsRefPtrHashtable<nsPtrHashKey<void>, GradientStops> mGradientStops;
+ nsRefPtrHashtable<nsPtrHashKey<void>, ScaledFont> mScaledFonts;
+ nsRefPtrHashtable<nsUint64HashKey, NativeFontResource> mNativeFontResources;
+};
+
+} // namespace layout
+} // namespace mozilla
+
+#endif // mozilla_layout_PrintTranslator_h
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..2af8d8476
--- /dev/null
+++ b/layout/printing/crashtests/crashtests.list
@@ -0,0 +1,4 @@
+load 509839-1.html
+load 509839-2.html
+asserts-if(browserIsRemote,4) load 576878.xhtml
+load 793844.html
diff --git a/layout/printing/ipc/PRemotePrintJob.ipdl b/layout/printing/ipc/PRemotePrintJob.ipdl
new file mode 100644
index 000000000..f6c1de895
--- /dev/null
+++ b/layout/printing/ipc/PRemotePrintJob.ipdl
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PPrinting;
+
+namespace mozilla {
+namespace layout {
+
+async protocol PRemotePrintJob
+{
+ manager PPrinting;
+
+both:
+ // Tell either side to abort printing and clean up.
+ async AbortPrint(nsresult aRv);
+
+parent:
+ // Initialize the real print device with the given information.
+ async InitializePrint(nsString aDocumentTitle, nsString aPrintToFile,
+ int32_t aStartPage, int32_t aEndPage);
+
+ // Translate the stored page recording and play back the events to the real
+ // print device.
+ async ProcessPage(nsCString aPageFileName);
+
+ // This informs the real print device that we've finished, so it can trigger
+ // the actual print.
+ async FinalizePrint();
+
+ // Report a state change to listeners in the parent process.
+ async StateChange(long aStateFlags,
+ nsresult aStatus);
+
+ // Report a progress change to listeners in the parent process.
+ async ProgressChange(long aCurSelfProgress,
+ long aMaxSelfProgress,
+ long aCurTotalProgress,
+ long aMaxTotalProgress);
+
+ // Report a status change to listeners in the parent process.
+ async StatusChange(nsresult aStatus);
+
+child:
+ // Inform the child that the print has been initialized in the parent or has
+ // failed with result aRv.
+ async PrintInitializationResult(nsresult aRv);
+
+ // Inform the child that the latest page has been processed remotely.
+ async PageProcessed();
+
+ async __delete__();
+};
+
+} // namespace layout
+} // namespace mozilla
diff --git a/layout/printing/ipc/RemotePrintJobChild.cpp b/layout/printing/ipc/RemotePrintJobChild.cpp
new file mode 100644
index 000000000..ffc3c2455
--- /dev/null
+++ b/layout/printing/ipc/RemotePrintJobChild.cpp
@@ -0,0 +1,155 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "RemotePrintJobChild.h"
+
+#include "mozilla/Unused.h"
+#include "nsPagePrintTimer.h"
+#include "nsPrintEngine.h"
+
+namespace mozilla {
+namespace layout {
+
+NS_IMPL_ISUPPORTS(RemotePrintJobChild,
+ nsIWebProgressListener)
+
+RemotePrintJobChild::RemotePrintJobChild()
+{
+ MOZ_COUNT_CTOR(RemotePrintJobChild);
+}
+
+nsresult
+RemotePrintJobChild::InitializePrint(const nsString& aDocumentTitle,
+ const nsString& aPrintToFile,
+ const int32_t& aStartPage,
+ const int32_t& aEndPage)
+{
+ // Print initialization can sometimes display a dialog in the parent, so we
+ // need to spin a nested event loop until initialization completes.
+ Unused << SendInitializePrint(aDocumentTitle, aPrintToFile, aStartPage,
+ aEndPage);
+ while (!mPrintInitialized) {
+ Unused << NS_ProcessNextEvent();
+ }
+
+ return mInitializationResult;
+}
+
+bool
+RemotePrintJobChild::RecvPrintInitializationResult(const nsresult& aRv)
+{
+ mPrintInitialized = true;
+ mInitializationResult = aRv;
+ return true;
+}
+
+void
+RemotePrintJobChild::ProcessPage(const nsCString& aPageFileName)
+{
+ MOZ_ASSERT(mPagePrintTimer);
+
+ mPagePrintTimer->WaitForRemotePrint();
+ Unused << SendProcessPage(aPageFileName);
+}
+
+bool
+RemotePrintJobChild::RecvPageProcessed()
+{
+ MOZ_ASSERT(mPagePrintTimer);
+
+ mPagePrintTimer->RemotePrintFinished();
+ return true;
+}
+
+bool
+RemotePrintJobChild::RecvAbortPrint(const nsresult& aRv)
+{
+ MOZ_ASSERT(mPrintEngine);
+
+ mPrintEngine->CleanupOnFailure(aRv, true);
+ return true;
+}
+
+void
+RemotePrintJobChild::SetPagePrintTimer(nsPagePrintTimer* aPagePrintTimer)
+{
+ MOZ_ASSERT(aPagePrintTimer);
+
+ mPagePrintTimer = aPagePrintTimer;
+}
+
+void
+RemotePrintJobChild::SetPrintEngine(nsPrintEngine* aPrintEngine)
+{
+ MOZ_ASSERT(aPrintEngine);
+
+ mPrintEngine = aPrintEngine;
+}
+
+// nsIWebProgressListener
+
+NS_IMETHODIMP
+RemotePrintJobChild::OnStateChange(nsIWebProgress* aProgress,
+ nsIRequest* aRequest, uint32_t aStateFlags,
+ nsresult aStatus)
+{
+ Unused << SendStateChange(aStateFlags, aStatus);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RemotePrintJobChild::OnProgressChange(nsIWebProgress * aProgress,
+ nsIRequest * aRequest,
+ int32_t aCurSelfProgress,
+ int32_t aMaxSelfProgress,
+ int32_t aCurTotalProgress,
+ int32_t aMaxTotalProgress)
+{
+ Unused << SendProgressChange(aCurSelfProgress, aMaxSelfProgress,
+ aCurTotalProgress, aMaxTotalProgress);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RemotePrintJobChild::OnLocationChange(nsIWebProgress* aProgress,
+ nsIRequest* aRequest, nsIURI* aURI,
+ uint32_t aFlags)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RemotePrintJobChild::OnStatusChange(nsIWebProgress* aProgress,
+ nsIRequest* aRequest, nsresult aStatus,
+ const char16_t* aMessage)
+{
+ Unused << SendStatusChange(aStatus);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RemotePrintJobChild::OnSecurityChange(nsIWebProgress* aProgress,
+ nsIRequest* aRequest, uint32_t aState)
+{
+ return NS_OK;
+}
+
+// End of nsIWebProgressListener
+
+RemotePrintJobChild::~RemotePrintJobChild()
+{
+ MOZ_COUNT_DTOR(RemotePrintJobChild);
+}
+
+void
+RemotePrintJobChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+ mPagePrintTimer = nullptr;
+ mPrintEngine = nullptr;
+}
+
+} // namespace layout
+} // namespace mozilla
diff --git a/layout/printing/ipc/RemotePrintJobChild.h b/layout/printing/ipc/RemotePrintJobChild.h
new file mode 100644
index 000000000..a316815ea
--- /dev/null
+++ b/layout/printing/ipc/RemotePrintJobChild.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layout_RemotePrintJobChild_h
+#define mozilla_layout_RemotePrintJobChild_h
+
+#include "mozilla/layout/PRemotePrintJobChild.h"
+
+#include "mozilla/RefPtr.h"
+#include "nsIWebProgressListener.h"
+
+class nsPagePrintTimer;
+class nsPrintEngine;
+
+namespace mozilla {
+namespace layout {
+
+class RemotePrintJobChild final : public PRemotePrintJobChild
+ , public nsIWebProgressListener
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIWEBPROGRESSLISTENER
+
+ RemotePrintJobChild();
+
+ void ActorDestroy(ActorDestroyReason aWhy) final;
+
+ nsresult InitializePrint(const nsString& aDocumentTitle,
+ const nsString& aPrintToFile,
+ const int32_t& aStartPage,
+ const int32_t& aEndPage);
+
+ bool RecvPrintInitializationResult(const nsresult& aRv) final;
+
+ void ProcessPage(const nsCString& aPageFileName);
+
+ bool RecvPageProcessed() final;
+
+ bool RecvAbortPrint(const nsresult& aRv) final;
+
+ void SetPagePrintTimer(nsPagePrintTimer* aPagePrintTimer);
+
+ void SetPrintEngine(nsPrintEngine* aPrintEngine);
+
+private:
+ ~RemotePrintJobChild() final;
+
+ bool mPrintInitialized = false;
+ nsresult mInitializationResult = NS_OK;
+ RefPtr<nsPagePrintTimer> mPagePrintTimer;
+ RefPtr<nsPrintEngine> mPrintEngine;
+};
+
+} // namespace layout
+} // namespace mozilla
+
+#endif // mozilla_layout_RemotePrintJobChild_h
diff --git a/layout/printing/ipc/RemotePrintJobParent.cpp b/layout/printing/ipc/RemotePrintJobParent.cpp
new file mode 100644
index 000000000..2f4dbd56e
--- /dev/null
+++ b/layout/printing/ipc/RemotePrintJobParent.cpp
@@ -0,0 +1,244 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "RemotePrintJobParent.h"
+
+#include <fstream>
+
+#include "gfxContext.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Unused.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsComponentManagerUtils.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsDeviceContext.h"
+#include "nsIDeviceContextSpec.h"
+#include "nsIPrintSettings.h"
+#include "nsIWebProgressListener.h"
+#include "PrintTranslator.h"
+
+namespace mozilla {
+namespace layout {
+
+RemotePrintJobParent::RemotePrintJobParent(nsIPrintSettings* aPrintSettings)
+ : mPrintSettings(aPrintSettings)
+{
+ MOZ_COUNT_CTOR(RemotePrintJobParent);
+}
+
+bool
+RemotePrintJobParent::RecvInitializePrint(const nsString& aDocumentTitle,
+ const nsString& aPrintToFile,
+ const int32_t& aStartPage,
+ const int32_t& aEndPage)
+{
+ nsresult rv = InitializePrintDevice(aDocumentTitle, aPrintToFile, aStartPage,
+ aEndPage);
+ if (NS_FAILED(rv)) {
+ Unused << SendPrintInitializationResult(rv);
+ Unused << Send__delete__(this);
+ return true;
+ }
+
+ mPrintTranslator.reset(new PrintTranslator(mPrintDeviceContext));
+ Unused << SendPrintInitializationResult(NS_OK);
+
+ return true;
+}
+
+nsresult
+RemotePrintJobParent::InitializePrintDevice(const nsString& aDocumentTitle,
+ const nsString& aPrintToFile,
+ const int32_t& aStartPage,
+ const int32_t& aEndPage)
+{
+ nsresult rv;
+ nsCOMPtr<nsIDeviceContextSpec> deviceContextSpec =
+ do_CreateInstance("@mozilla.org/gfx/devicecontextspec;1", &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = deviceContextSpec->Init(nullptr, mPrintSettings, false);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ mPrintDeviceContext = new nsDeviceContext();
+ rv = mPrintDeviceContext->InitForPrinting(deviceContextSpec);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = mPrintDeviceContext->BeginDocument(aDocumentTitle, aPrintToFile,
+ aStartPage, aEndPage);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+bool
+RemotePrintJobParent::RecvProcessPage(const nsCString& aPageFileName)
+{
+ nsresult rv = PrintPage(aPageFileName);
+
+ if (NS_FAILED(rv)) {
+ Unused << SendAbortPrint(rv);
+ } else {
+ Unused << SendPageProcessed();
+ }
+
+ return true;
+}
+
+nsresult
+RemotePrintJobParent::PrintPage(const nsCString& aPageFileName)
+{
+ MOZ_ASSERT(mPrintDeviceContext);
+
+ nsresult rv = mPrintDeviceContext->BeginPage();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsCOMPtr<nsIFile> recordingFile;
+ rv = NS_GetSpecialDirectory(NS_APP_CONTENT_PROCESS_TEMP_DIR,
+ getter_AddRefs(recordingFile));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = recordingFile->AppendNative(aPageFileName);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsAutoCString recordingPath;
+ rv = recordingFile->GetNativePath(recordingPath);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ std::ifstream recording(recordingPath.get(), std::ifstream::binary);
+ if (!mPrintTranslator->TranslateRecording(recording)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ rv = mPrintDeviceContext->EndPage();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ recording.close();
+ rv = recordingFile->Remove(/* recursive= */ false);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+bool
+RemotePrintJobParent::RecvFinalizePrint()
+{
+ // EndDocument is sometimes called in the child even when BeginDocument has
+ // not been called. See bug 1223332.
+ if (mPrintDeviceContext) {
+ DebugOnly<nsresult> rv = mPrintDeviceContext->EndDocument();
+
+ // Too late to abort the child just log.
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EndDocument failed");
+ }
+
+
+ Unused << Send__delete__(this);
+ return true;
+}
+
+bool
+RemotePrintJobParent::RecvAbortPrint(const nsresult& aRv)
+{
+ if (mPrintDeviceContext) {
+ Unused << mPrintDeviceContext->AbortDocument();
+ }
+
+ Unused << Send__delete__(this);
+ return true;
+}
+
+bool
+RemotePrintJobParent::RecvStateChange(const long& aStateFlags,
+ const nsresult& aStatus)
+{
+ uint32_t numberOfListeners = mPrintProgressListeners.Length();
+ for (uint32_t i = 0; i < numberOfListeners; ++i) {
+ nsIWebProgressListener* listener = mPrintProgressListeners.SafeElementAt(i);
+ listener->OnStateChange(nullptr, nullptr, aStateFlags, aStatus);
+ }
+
+ return true;
+}
+
+bool
+RemotePrintJobParent::RecvProgressChange(const long& aCurSelfProgress,
+ const long& aMaxSelfProgress,
+ const long& aCurTotalProgress,
+ const long& aMaxTotalProgress)
+{
+ uint32_t numberOfListeners = mPrintProgressListeners.Length();
+ for (uint32_t i = 0; i < numberOfListeners; ++i) {
+ nsIWebProgressListener* listener = mPrintProgressListeners.SafeElementAt(i);
+ listener->OnProgressChange(nullptr, nullptr,
+ aCurSelfProgress, aMaxSelfProgress,
+ aCurTotalProgress, aMaxTotalProgress);
+ }
+
+ return true;
+}
+
+bool
+RemotePrintJobParent::RecvStatusChange(const nsresult& aStatus)
+{
+ uint32_t numberOfListeners = mPrintProgressListeners.Length();
+ for (uint32_t i = 0; i < numberOfListeners; ++i) {
+ nsIWebProgressListener* listener = mPrintProgressListeners.SafeElementAt(i);
+ listener->OnStatusChange(nullptr, nullptr, aStatus, nullptr);
+ }
+
+ return true;
+}
+
+void
+RemotePrintJobParent::RegisterListener(nsIWebProgressListener* aListener)
+{
+ MOZ_ASSERT(aListener);
+
+ mPrintProgressListeners.AppendElement(aListener);
+}
+
+already_AddRefed<nsIPrintSettings>
+RemotePrintJobParent::GetPrintSettings()
+{
+ nsCOMPtr<nsIPrintSettings> printSettings = mPrintSettings;
+ return printSettings.forget();
+}
+
+RemotePrintJobParent::~RemotePrintJobParent()
+{
+ MOZ_COUNT_DTOR(RemotePrintJobParent);
+}
+
+void
+RemotePrintJobParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+}
+
+} // namespace layout
+} // namespace mozilla
+
+
diff --git a/layout/printing/ipc/RemotePrintJobParent.h b/layout/printing/ipc/RemotePrintJobParent.h
new file mode 100644
index 000000000..a96cc7eaa
--- /dev/null
+++ b/layout/printing/ipc/RemotePrintJobParent.h
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layout_RemotePrintJobParent_h
+#define mozilla_layout_RemotePrintJobParent_h
+
+#include "mozilla/layout/PRemotePrintJobParent.h"
+
+#include "nsCOMArray.h"
+#include "nsCOMPtr.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+
+class nsDeviceContext;
+class nsIPrintSettings;
+class nsIWebProgressListener;
+class PrintTranslator;
+
+namespace mozilla {
+namespace layout {
+
+class RemotePrintJobParent final : public PRemotePrintJobParent
+{
+public:
+ explicit RemotePrintJobParent(nsIPrintSettings* aPrintSettings);
+
+ void ActorDestroy(ActorDestroyReason aWhy) final;
+
+ bool RecvInitializePrint(const nsString& aDocumentTitle,
+ const nsString& aPrintToFile,
+ const int32_t& aStartPage,
+ const int32_t& aEndPage) final;
+
+ bool RecvProcessPage(const nsCString& aPageFileName) final;
+
+ bool RecvFinalizePrint() final;
+
+ bool RecvAbortPrint(const nsresult& aRv) final;
+
+ bool RecvStateChange(const long& aStateFlags,
+ const nsresult& aStatus) final;
+
+ bool RecvProgressChange(const long& aCurSelfProgress,
+ const long& aMaxSelfProgress,
+ const long& aCurTotalProgress,
+ const long& aMaxTotalProgress) final;
+
+ bool RecvStatusChange(const nsresult& aStatus) final;
+
+ /**
+ * Register a progress listener to receive print progress updates.
+ *
+ * @param aListener the progress listener to register. Must not be null.
+ */
+ void RegisterListener(nsIWebProgressListener* aListener);
+
+ /**
+ * @return the print settings for this remote print job.
+ */
+ already_AddRefed<nsIPrintSettings> GetPrintSettings();
+
+private:
+ ~RemotePrintJobParent() final;
+
+ nsresult InitializePrintDevice(const nsString& aDocumentTitle,
+ const nsString& aPrintToFile,
+ const int32_t& aStartPage,
+ const int32_t& aEndPage);
+
+ nsresult PrintPage(const nsCString& aPageFileName);
+
+ nsCOMPtr<nsIPrintSettings> mPrintSettings;
+ RefPtr<nsDeviceContext> mPrintDeviceContext;
+ UniquePtr<PrintTranslator> mPrintTranslator;
+ nsCOMArray<nsIWebProgressListener> mPrintProgressListeners;
+};
+
+} // namespace layout
+} // namespace mozilla
+
+#endif // mozilla_layout_RemotePrintJobParent_h
diff --git a/layout/printing/moz.build b/layout/printing/moz.build
new file mode 100644
index 000000000..1a82d7cdb
--- /dev/null
+++ b/layout/printing/moz.build
@@ -0,0 +1,37 @@
+# -*- Mode: python; 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',
+]
+
+EXPORTS.mozilla.layout += [
+ 'ipc/RemotePrintJobChild.h',
+ 'ipc/RemotePrintJobParent.h',
+]
+
+XPIDL_MODULE = 'layout_printing'
+
+UNIFIED_SOURCES += [
+ 'ipc/RemotePrintJobChild.cpp',
+ 'ipc/RemotePrintJobParent.cpp',
+ 'nsPagePrintTimer.cpp',
+ 'nsPrintData.cpp',
+ 'nsPrintEngine.cpp',
+ 'nsPrintObject.cpp',
+ 'nsPrintPreviewListener.cpp',
+ 'PrintTranslator.cpp',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
+LOCAL_INCLUDES += [
+ '../base',
+ '/dom/base',
+]
diff --git a/layout/printing/nsIPrintProgress.idl b/layout/printing/nsIPrintProgress.idl
new file mode 100644
index 000000000..5d42920eb
--- /dev/null
+++ b/layout/printing/nsIPrintProgress.idl
@@ -0,0 +1,43 @@
+/* -*- 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 "nsIWebProgressListener.idl"
+
+interface mozIDOMWindowProxy;
+interface nsIObserver;
+interface nsIPrompt;
+
+[scriptable, uuid(05f4fb88-e568-4d35-b394-ce0aa3eea6fc)]
+interface nsIPrintProgress: nsIWebProgressListener {
+
+ /* Open the progress dialog
+ you can specify parameters through an xpcom object
+ */
+ void openProgressDialog(in mozIDOMWindowProxy 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..9fef4b59d
--- /dev/null
+++ b/layout/printing/nsIPrintStatusFeedback.idl
@@ -0,0 +1,23 @@
+/* -*- 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 mozIDOMWindowProxy;
+
+/**
+ * XXX This interface seems to be unimplemented and unused.
+ */
+[scriptable, uuid(8eb6ffc9-715c-487e-927c-c56139229548)]
+interface nsIPrintStatusFeedback : nsISupports {
+ void showStatusString(in wstring status);
+ void startMeteors();
+ void stopMeteors();
+ void showProgress(in long percent);
+ [noscript] void setDocShell(in nsIDocShell shell, in mozIDOMWindowProxy window);
+ void closeWindow();
+};
+
diff --git a/layout/printing/nsPagePrintTimer.cpp b/layout/printing/nsPagePrintTimer.cpp
new file mode 100644
index 000000000..b56569a9a
--- /dev/null
+++ b/layout/printing/nsPagePrintTimer.cpp
@@ -0,0 +1,224 @@
+/* -*- 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 "mozilla/Unused.h"
+#include "nsIContentViewer.h"
+#include "nsIServiceManager.h"
+#include "nsPrintEngine.h"
+
+NS_IMPL_ISUPPORTS_INHERITED(nsPagePrintTimer, mozilla::Runnable, 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 || mPrintEngine->PrintPage(mPrintObj, inRange);
+ if (donePrinting) {
+ // now clean up print or print the next webshell
+ if (!mPrintEngine || 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..
+ if (mPrintEngine) {
+ 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 four 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)
+ // 4) the waiting for remote print "timer" (timer == mWaitingForRemotePrint)
+ if (!timer) {
+ // Reset the counter since a mozPrintCallback has finished.
+ mWatchDogCount = 0;
+ } else if (timer == mTimer) {
+ // Reset the watchdog timer before the start of every page.
+ mWatchDogCount = 0;
+ mTimer = nullptr;
+ } else if (timer == mWaitingForRemotePrint) {
+ mWaitingForRemotePrint = nullptr;
+
+ // If we are still waiting for the page delay timer, don't let the
+ // notification from the remote print job trigger the next page.
+ if (mTimer) {
+ return NS_OK;
+ }
+ } else if (timer == mWatchDogTimer) {
+ mWatchDogCount++;
+ if (mWatchDogCount > WATCH_DOG_MAX_COUNT) {
+ Fail();
+ return NS_OK;
+ }
+ }
+
+ if (mDocViewerPrint) {
+ bool donePrePrint = true;
+ if (mPrintEngine) {
+ donePrePrint = mPrintEngine->PrePrintPage();
+ }
+
+ if (donePrePrint && !mWaitingForRemotePrint) {
+ 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;
+}
+
+
+void
+nsPagePrintTimer::WaitForRemotePrint()
+{
+ nsresult result;
+ mWaitingForRemotePrint = do_CreateInstance("@mozilla.org/timer;1", &result);
+ if (NS_FAILED(result)) {
+ NS_WARNING("Failed to wait for remote print, we might time-out.");
+ mWaitingForRemotePrint = nullptr;
+ }
+}
+
+void
+nsPagePrintTimer::RemotePrintFinished()
+{
+ if (!mWaitingForRemotePrint) {
+ return;
+ }
+
+ mozilla::Unused <<
+ mWaitingForRemotePrint->InitWithCallback(this, 0, nsITimer::TYPE_ONE_SHOT);
+}
+
+nsresult
+nsPagePrintTimer::Start(nsPrintObject* aPO)
+{
+ mPrintObj = aPO;
+ mDone = false;
+ return StartTimer(false);
+}
+
+
+void
+nsPagePrintTimer::Stop()
+{
+ if (mTimer) {
+ mTimer->Cancel();
+ mTimer = nullptr;
+ }
+ StopWatchDogTimer();
+}
+
+void
+nsPagePrintTimer::Fail()
+{
+ NS_WARNING("nsPagePrintTimer::Fail called");
+
+ 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..0137514da
--- /dev/null
+++ b/layout/printing/nsPagePrintTimer.h
@@ -0,0 +1,93 @@
+/* -*- 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 final : public mozilla::Runnable,
+ public nsITimerCallback
+{
+public:
+
+ NS_DECL_ISUPPORTS_INHERITED
+
+ nsPagePrintTimer(nsPrintEngine* aPrintEngine,
+ nsIDocumentViewerPrint* aDocViewerPrint,
+ uint32_t aDelay)
+ : mPrintEngine(aPrintEngine)
+ , mDocViewerPrint(aDocViewerPrint)
+ , mDelay(aDelay)
+ , mFiringCount(0)
+ , mPrintObj(nullptr)
+ , mWatchDogCount(0)
+ , mDone(false)
+ {
+ mDocViewerPrint->IncrementDestroyRefCount();
+ }
+
+ NS_DECL_NSITIMERCALLBACK
+
+ nsresult Start(nsPrintObject* aPO);
+
+ NS_IMETHOD Run() override;
+
+ void Stop();
+
+ void WaitForRemotePrint();
+ void RemotePrintFinished();
+
+ void Disconnect()
+ {
+ mPrintEngine = nullptr;
+ mPrintObj = nullptr;
+ }
+
+private:
+ ~nsPagePrintTimer();
+
+ nsresult StartTimer(bool aUseDelay);
+ nsresult StartWatchDogTimer();
+ void StopWatchDogTimer();
+ void Fail();
+
+ nsPrintEngine* mPrintEngine;
+ nsCOMPtr<nsIDocumentViewerPrint> mDocViewerPrint;
+ nsCOMPtr<nsITimer> mTimer;
+ nsCOMPtr<nsITimer> mWatchDogTimer;
+ nsCOMPtr<nsITimer> mWaitingForRemotePrint;
+ 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 =
+#ifdef DEBUG
+ // Debug builds are very slow (on Mac at least) and can need extra time
+ 30
+#else
+ 10
+#endif
+ ;
+};
+
+
+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..1914f779b
--- /dev/null
+++ b/layout/printing/nsPrintData.cpp
@@ -0,0 +1,140 @@
+/* -*- 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
+#include "mozilla/Logging.h"
+
+#define DUMP_LAYOUT_LEVEL 9 // this turns on the dumping of each doucment's layout info
+static mozilla::LazyLogModule gPrintingLog("printing");
+
+#define PR_PL(_p1) MOZ_LOG(gPrintingLog, mozilla::LogLevel::Debug, _p1);
+
+//---------------------------------------------------
+//-- 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(nullptr),
+ mBrandName(nullptr)
+{
+ 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(u"brandShortName", &mBrandName );
+ }
+ }
+
+ if (!mBrandName) {
+ mBrandName = ToNewUnicode(NS_LITERAL_STRING("Mozilla Document"));
+ }
+
+}
+
+nsPrintData::~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) {
+ 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)
+{
+ size_t numberOfListeners = mPrintProgressListeners.Length();
+ for (size_t i = 0; i < numberOfListeners; ++i) {
+ nsCOMPtr<nsIWebProgressListener> listener =
+ mPrintProgressListeners.SafeElementAt(i);
+ if (NS_WARN_IF(!listener)) {
+ continue;
+ }
+ listener->OnProgressChange(nullptr, nullptr, aProgress, aMaxProgress,
+ aProgress, aMaxProgress);
+ if (aDoStartStop) {
+ listener->OnStateChange(nullptr, nullptr, aFlag, NS_OK);
+ }
+ }
+}
+
+void
+nsPrintData::DoOnStatusChange(nsresult aStatus)
+{
+ size_t numberOfListeners = mPrintProgressListeners.Length();
+ for (size_t i = 0; i < numberOfListeners; ++i) {
+ nsCOMPtr<nsIWebProgressListener> listener =
+ mPrintProgressListeners.SafeElementAt(i);
+ if (NS_WARN_IF(!listener)) {
+ continue;
+ }
+ listener->OnStatusChange(nullptr, nullptr, aStatus, nullptr);
+ }
+}
+
diff --git a/layout/printing/nsPrintData.h b/layout/printing/nsPrintData.h
new file mode 100644
index 000000000..50f8c7a86
--- /dev/null
+++ b/layout/printing/nsPrintData.h
@@ -0,0 +1,96 @@
+/* -*- 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 "nsDeviceContext.h"
+#include "nsIPrintProgressParams.h"
+#include "nsIPrintSettings.h"
+#include "nsISupportsImpl.h"
+#include "nsTArray.h"
+#include "nsCOMArray.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;
+
+ explicit nsPrintData(ePrintDataType aType);
+
+ NS_INLINE_DECL_REFCOUNTING(nsPrintData)
+
+ // Listener Helper Methods
+ void OnEndPrinting();
+ void OnStartPrinting();
+ void DoOnProgressChange(int32_t aProgress,
+ int32_t aMaxProgress,
+ bool aDoStartStop,
+ int32_t aFlag);
+
+ void DoOnStatusChange(nsresult aStatus);
+
+
+ ePrintDataType mType; // the type of data this is (Printing or Print Preview)
+ RefPtr<nsDeviceContext> mPrintDC;
+ FILE *mDebugFilePtr; // a file where information can go to when printing
+
+ nsPrintObject * mPrintObject;
+ nsPrintObject * mSelectedPO;
+
+ nsCOMArray<nsIWebProgressListener> mPrintProgressListeners;
+ nsCOMPtr<nsIPrintProgressParams> mPrintProgressParams;
+
+ nsCOMPtr<nsPIDOMWindowOuter> 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;
+
+ char16_t* mBrandName; // needed as a substitute name for a document
+
+private:
+ nsPrintData() = delete;
+ nsPrintData& operator=(const nsPrintData& aOther) = delete;
+
+ ~nsPrintData(); // non-virtual
+};
+
+#endif /* nsPrintData_h___ */
+
diff --git a/layout/printing/nsPrintEngine.cpp b/layout/printing/nsPrintEngine.cpp
new file mode 100644
index 000000000..f2db53250
--- /dev/null
+++ b/layout/printing/nsPrintEngine.cpp
@@ -0,0 +1,4071 @@
+/* -*- 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/AsyncEventDispatcher.h"
+#include "mozilla/dom/Selection.h"
+#include "mozilla/dom/CustomEvent.h"
+#include "nsIScriptGlobalObject.h"
+#include "nsPIDOMWindow.h"
+#include "nsIDocShell.h"
+#include "nsIURI.h"
+#include "nsITextToSubURI.h"
+#include "nsError.h"
+
+#include "nsView.h"
+#include <algorithm>
+
+// Print Options
+#include "nsIPrintSettings.h"
+#include "nsIPrintSettingsService.h"
+#include "nsIPrintSession.h"
+#include "nsGfxCIID.h"
+#include "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 "mozilla/gfx/DrawEventRecorder.h"
+#include "mozilla/layout/RemotePrintJobChild.h"
+#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 "nsDeviceContextSpecProxy.h"
+#include "nsViewManager.h"
+#include "nsView.h"
+#include "nsRenderingContext.h"
+
+#include "nsIPageSequenceFrame.h"
+#include "nsIURL.h"
+#include "nsIContentViewerEdit.h"
+#include "nsIContentViewerFile.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIDocShellTreeOwner.h"
+#include "nsIWebBrowserChrome.h"
+#include "nsIBaseWindow.h"
+#include "nsILayoutHistoryState.h"
+#include "nsFrameManager.h"
+#include "mozilla/ReflowInput.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"
+#include "nsIChannel.h"
+#include "xpcpublic.h"
+#include "nsVariant.h"
+#include "mozilla/StyleSetHandle.h"
+#include "mozilla/StyleSetHandleInlines.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+//-----------------------------------------------------
+// PR LOGGING
+#include "mozilla/Logging.h"
+
+#ifdef DEBUG
+// PR_LOGGING is force to always be on (even in release builds)
+// but we only want some of it on,
+//#define EXTENDED_DEBUG_PRINTING
+#endif
+
+#define DUMP_LAYOUT_LEVEL 9 // this turns on the dumping of each doucment's layout info
+
+#ifndef PR_PL
+static mozilla::LazyLogModule gPrintingLog("printing")
+
+#define PR_PL(_p1) MOZ_LOG(gPrintingLog, mozilla::LogLevel::Debug, _p1);
+#endif
+
+#ifdef EXTENDED_DEBUG_PRINTING
+static uint32_t gDumpFileNameCnt = 0;
+static uint32_t gDumpLOFileNameCnt = 0;
+#endif
+
+#define PRT_YESNO(_p) ((_p)?"YES":"NO")
+static const char * gFrameTypesStr[] = {"eDoc", "eFrame", "eIFrame", "eFrameSet"};
+static const char * gPrintFrameTypeStr[] = {"kNoFrames", "kFramesAsIs", "kSelectedFrame", "kEachFrameSep"};
+static const char * gFrameHowToEnableStr[] = {"kFrameEnableNone", "kFrameEnableAll", "kFrameEnableAsIsAndEach"};
+static const char * gPrintRangeStr[] = {"kRangeAllPages", "kRangeSpecifiedPageRange", "kRangeSelection", "kRangeFocusFrame"};
+
+#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:
+ explicit 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:
+ RefPtr<nsPrintEngine> mPrintEngine;
+ bool mSuppressed;
+};
+
+NS_IMPL_ISUPPORTS(nsPrintEngine, nsIWebProgressListener,
+ nsISupportsWeakReference, nsIObserver)
+
+//---------------------------------------------------
+//-- nsPrintEngine Class Impl
+//---------------------------------------------------
+nsPrintEngine::nsPrintEngine()
+ : mIsCreatingPrintPreview(false)
+ , mIsDoingPrinting(false)
+ , mIsDoingPrintPreview(false)
+ , mProgressDialogIsShown(false)
+ , mScreenDPI(115.0f)
+ , mPagePrintTimer(nullptr)
+ , mDebugFile(nullptr)
+ , mLoadCounter(0)
+ , mDidLoadDataForPrinting(false)
+ , mIsDestroying(false)
+ , mDisallowSelectionPrint(false)
+{
+}
+
+//-------------------------------------------------------
+nsPrintEngine::~nsPrintEngine()
+{
+ Destroy(); // for insurance
+ DisconnectPagePrintTimer();
+}
+
+//-------------------------------------------------------
+void nsPrintEngine::Destroy()
+{
+ if (mIsDestroying) {
+ return;
+ }
+ mIsDestroying = true;
+
+ mPrt = nullptr;
+
+#ifdef NS_PRINT_PREVIEW
+ mPrtPreview = nullptr;
+ mOldPrtPreview = nullptr;
+#endif
+ mDocViewerPrint = nullptr;
+}
+
+//-------------------------------------------------------
+void nsPrintEngine::DestroyPrintingData()
+{
+ mPrt = nullptr;
+}
+
+//---------------------------------------------------------------------------------
+//-- Section: Methods needed by the DocViewer
+//---------------------------------------------------------------------------------
+
+//--------------------------------------------------------
+nsresult nsPrintEngine::Initialize(nsIDocumentViewerPrint* aDocViewerPrint,
+ nsIDocShell* 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 = do_GetWeakReference(aContainer);
+ mDocument = aDocument;
+ mScreenDPI = aScreenDPI;
+
+ mDebugFile = aDebugFile; // ok to be nullptr
+
+ 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);
+ if (!docShell) {
+ return;
+ }
+
+ if (nsPIDOMWindowOuter* win = docShell->GetWindow()) {
+ nsCOMPtr<EventTarget> target = 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);
+
+ // This is sometimes incorrectly called before the pres shell has been created
+ // (bug 1141756). MOZ_DIAGNOSTIC_ASSERT so we'll still see the crash in
+ // Nightly/Aurora in case the other patch fixes this.
+ if (!aPO->mPresShell) {
+ MOZ_DIAGNOSTIC_ASSERT(false,
+ "GetSeqFrameAndCountPages needs a non-null pres shell");
+ return NS_ERROR_FAILURE;
+ }
+
+ // Finds the SimplePageSequencer frame
+ nsIPageSequenceFrame* seqFrame = aPO->mPresShell->GetPageSequenceFrame();
+ aSeqFrame = do_QueryFrame(seqFrame);
+ if (!aSeqFrame) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // count the total number of pages
+ aCount = aSeqFrame->PrincipalChildList().GetLength();
+
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------
+nsresult nsPrintEngine::GetSeqFrameAndCountPages(nsIFrame*& aSeqFrame, int32_t& aCount)
+{
+ MOZ_ASSERT(mPrtPreview);
+ // Guarantee that mPrintPreview->mPrintObject won't be deleted during a call
+ // of GetSeqFrameAndCountPagesInternal().
+ RefPtr<nsPrintData> printDataForPrintPreview = mPrtPreview;
+ return GetSeqFrameAndCountPagesInternal(
+ printDataForPrintPreview->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) {
+ RefPtr<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) {
+ FirePrintingErrorEvent(rv);
+ }
+ 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 = Move(mPrtPreview);
+ }
+ } else {
+ mProgressDialogIsShown = false;
+ }
+
+ // Grab the new instance with local variable to guarantee that it won't be
+ // deleted during this method.
+ mPrt = new nsPrintData(aIsPrintPreview ? nsPrintData::eIsPrintPreview :
+ nsPrintData::eIsPrinting);
+ RefPtr<nsPrintData> printData = mPrt;
+
+ // if they don't pass in a PrintSettings, then get the Global PS
+ printData->mPrintSettings = aPrintSettings;
+ if (!printData->mPrintSettings) {
+ rv = GetGlobalPrintSettings(getter_AddRefs(printData->mPrintSettings));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ rv = CheckForPrinters(printData->mPrintSettings);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ printData->mPrintSettings->SetIsCancelled(false);
+ printData->mPrintSettings->GetShrinkToFit(&printData->mShrinkToFit);
+
+ if (aIsPrintPreview) {
+ SetIsCreatingPrintPreview(true);
+ SetIsPrintPreview(true);
+ nsCOMPtr<nsIContentViewer> 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.
+ // Don't overwrite an existing print session.
+ // The print settings hold an nsWeakPtr to the session so it does not
+ // need to be cleared from the settings at the end of the job.
+ // XXX What lifetime does the printSession need to have?
+ nsCOMPtr<nsIPrintSession> printSession;
+ bool remotePrintJobListening = false;
+ if (!aIsPrintPreview) {
+ rv = printData->mPrintSettings->GetPrintSession(
+ getter_AddRefs(printSession));
+ if (NS_FAILED(rv) || !printSession) {
+ printSession = do_CreateInstance("@mozilla.org/gfx/printsession;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ printData->mPrintSettings->SetPrintSession(printSession);
+ } else {
+ RefPtr<mozilla::layout::RemotePrintJobChild> remotePrintJob;
+ printSession->GetRemotePrintJob(getter_AddRefs(remotePrintJob));
+ if (NS_SUCCEEDED(rv) && remotePrintJob) {
+ // If we have a RemotePrintJob add it to the print progress listeners,
+ // so it can forward to the parent.
+ printData->mPrintProgressListeners.AppendElement(remotePrintJob);
+ remotePrintJobListening = true;
+ }
+ }
+
+ }
+
+ if (aWebProgressListener != nullptr) {
+ printData->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 nullptr
+ printData->mCurrentFocusWin = FindFocusedDOMWindow();
+
+ // Check to see if there is a "regular" selection
+ bool isSelection = IsThereARangeSelection(printData->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;
+ printData->mPrintObject = new nsPrintObject();
+ NS_ENSURE_TRUE(printData->mPrintObject, NS_ERROR_OUT_OF_MEMORY);
+ rv = printData->mPrintObject->Init(webContainer, aDoc, aIsPrintPreview);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_ENSURE_TRUE(printData->mPrintDocList.AppendElement(
+ printData->mPrintObject),
+ NS_ERROR_OUT_OF_MEMORY);
+
+ printData->mIsParentAFrameSet = IsParentAFrameSet(webContainer);
+ printData->mPrintObject->mFrameType =
+ printData->mIsParentAFrameSet ? eFrameSet : eDoc;
+
+ // Build the "tree" of PrintObjects
+ BuildDocTree(printData->mPrintObject->mDocShell, &printData->mPrintDocList,
+ printData->mPrintObject);
+ }
+
+ if (!aIsPrintPreview) {
+ SetIsPrinting(true);
+ }
+
+ // XXX This isn't really correct...
+ if (!printData->mPrintObject->mDocument ||
+ !printData->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(printData->mPrintObject, printData->mPrintObject);
+
+ printData->mIsIFrameSelected =
+ IsThereAnIFrameSelected(webContainer, printData->mCurrentFocusWin,
+ printData->mIsParentAFrameSet);
+
+ // Setup print options for UI
+ if (printData->mIsParentAFrameSet) {
+ if (printData->mCurrentFocusWin) {
+ printData->mPrintSettings->SetHowToEnableFrameUI(
+ nsIPrintSettings::kFrameEnableAll);
+ } else {
+ printData->mPrintSettings->SetHowToEnableFrameUI(
+ nsIPrintSettings::kFrameEnableAsIsAndEach);
+ }
+ } else {
+ printData->mPrintSettings->SetHowToEnableFrameUI(
+ nsIPrintSettings::kFrameEnableNone);
+ }
+ // Now determine how to set up the Frame print UI
+ printData->mPrintSettings->SetPrintOptions(
+ nsIPrintSettings::kEnableSelectionRB,
+ isSelection || printData->mIsIFrameSelected);
+
+ bool printingViaParent = XRE_IsContentProcess() &&
+ Preferences::GetBool("print.print_via_parent");
+ nsCOMPtr<nsIDeviceContextSpec> devspec;
+ if (printingViaParent) {
+ devspec = new nsDeviceContextSpecProxy();
+ } else {
+ devspec = do_CreateInstance("@mozilla.org/gfx/devicecontextspec;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ nsScriptSuppressor scriptSuppressor(this);
+ // If printing via parent we still call ShowPrintDialog even for print preview
+ // because we use that to retrieve the print settings from the printer.
+ // The dialog is not shown, but this means we don't need to access the printer
+ // driver from the child, which causes sandboxing issues.
+ if (!aIsPrintPreview || printingViaParent) {
+#ifdef DEBUG
+ printData->mDebugFilePtr = mDebugFile;
+#endif
+
+ scriptSuppressor.Suppress();
+ bool printSilently;
+ printData->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 printing via the parent then we need to confirm that the pref is set
+ // and get a remote print job, but the parent won't display a prompt.
+ if (!printSilently || printingViaParent) {
+ nsCOMPtr<nsIPrintingPromptService> printPromptService(do_GetService(kPrintingPromptService));
+ if (printPromptService) {
+ nsPIDOMWindowOuter* domWin = nullptr;
+ // We leave domWin as nullptr to indicate a call for print preview.
+ if (!aIsPrintPreview) {
+ 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,
+ printData->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 (NS_WARN_IF(mPrt != printData)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (NS_SUCCEEDED(rv)) {
+ // since we got the dialog and it worked then make sure we
+ // are telling GFX we want to print silent
+ printSilently = true;
+
+ if (printData->mPrintSettings && !aIsPrintPreview) {
+ // The user might have changed shrink-to-fit in the print dialog, so update our copy of its state
+ printData->mPrintSettings->GetShrinkToFit(&printData->mShrinkToFit);
+
+ // If we haven't already added the RemotePrintJob as a listener,
+ // add it now if there is one.
+ if (!remotePrintJobListening) {
+ RefPtr<mozilla::layout::RemotePrintJobChild> remotePrintJob;
+ printSession->GetRemotePrintJob(getter_AddRefs(remotePrintJob));
+ if (NS_SUCCEEDED(rv) && remotePrintJob) {
+ printData->mPrintProgressListeners.AppendElement(
+ remotePrintJob);
+ remotePrintJobListening = true;
+ }
+ }
+ }
+ } else if (rv == NS_ERROR_NOT_IMPLEMENTED) {
+ // This means the Dialog service was there,
+ // but they choose not to implement this dialog and
+ // are looking for default behavior from the toolkit
+ rv = NS_OK;
+ }
+ } else {
+ // No dialog service available
+ rv = NS_ERROR_NOT_IMPLEMENTED;
+ }
+ } else {
+ // Call any code that requires a run of the event loop.
+ rv = printData->mPrintSettings->SetupSilentPrinting();
+ }
+ // Check explicitly for abort because it's expected
+ if (rv == NS_ERROR_ABORT)
+ return rv;
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ rv = devspec->Init(nullptr, printData->mPrintSettings, aIsPrintPreview);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ printData->mPrintDC = new nsDeviceContext();
+ rv = printData->mPrintDC->InitForPrinting(devspec);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (aIsPrintPreview) {
+ printData->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
+ printData->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;
+ printData->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;
+ printData->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) {
+ printData->mPrintFrameType = nsIPrintSettings::kEachFrameSep;
+ printData->mPrintSettings->SetPrintFrameType(
+ printData->mPrintFrameType);
+ } else {
+ // First find out from the PrinService what options are available
+ // to us for Printing FrameSets
+ int16_t howToEnableFrameUI;
+ printData->mPrintSettings->GetHowToEnableFrameUI(&howToEnableFrameUI);
+ if (howToEnableFrameUI != nsIPrintSettings::kFrameEnableNone) {
+ switch (howToEnableFrameUI) {
+ case nsIPrintSettings::kFrameEnableAll:
+ printData->mPrintFrameType = printFrameType;
+ break;
+
+ case nsIPrintSettings::kFrameEnableAsIsAndEach:
+ if (printFrameType != nsIPrintSettings::kSelectedFrame) {
+ printData->mPrintFrameType = printFrameType;
+ } else { // revert back to a good value
+ printData->mPrintFrameType = nsIPrintSettings::kEachFrameSep;
+ }
+ break;
+ } // switch
+ printData->mPrintSettings->SetPrintFrameType(
+ printData->mPrintFrameType);
+ }
+ }
+ } else {
+ printData->mPrintSettings->GetPrintFrameType(&printData->mPrintFrameType);
+ }
+ }
+
+ if (printData->mPrintFrameType == nsIPrintSettings::kEachFrameSep) {
+ CheckForChildFrameSets(printData->mPrintObject);
+ }
+
+ if (NS_FAILED(EnablePOsForPrinting())) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Attach progressListener to catch network requests.
+ nsCOMPtr<nsIWebProgress> webProgress =
+ do_QueryInterface(printData->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...
+ printData->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,
+ mozIDOMWindowProxy* 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);
+ FirePrintingErrorEvent(NS_ERROR_GFX_PRINTER_DOC_IS_BUSY);
+ return NS_ERROR_FAILURE;
+ }
+
+ auto* window = nsPIDOMWindowOuter::From(aChildDOMWin);
+ NS_ENSURE_STATE(window);
+ nsCOMPtr<nsIDocument> doc = window->GetDoc();
+ NS_ENSURE_STATE(doc);
+ nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(doc);
+ MOZ_ASSERT(domDoc);
+
+ // Document is not busy -- go ahead with the Print Preview
+ return CommonPrint(true, aPrintSettings, aWebProgressListener, domDoc);
+}
+
+//----------------------------------------------------------------------------------
+NS_IMETHODIMP
+nsPrintEngine::GetIsFramesetDocument(bool *aIsFramesetDocument)
+{
+ nsCOMPtr<nsIDocShell> webContainer(do_QueryReferent(mContainer));
+ *aIsFramesetDocument = IsParentAFrameSet(webContainer);
+ return NS_OK;
+}
+
+//----------------------------------------------------------------------------------
+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<nsPIDOMWindowOuter> 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;
+}
+
+//----------------------------------------------------------------------------------
+NS_IMETHODIMP
+nsPrintEngine::GetIsRangeSelection(bool *aIsRangeSelection)
+{
+ // Get the currently focused window
+ nsCOMPtr<nsPIDOMWindowOuter> currentFocusWin = FindFocusedDOMWindow();
+ *aIsRangeSelection = IsThereARangeSelection(currentFocusWin);
+ return NS_OK;
+}
+
+//----------------------------------------------------------------------------------
+NS_IMETHODIMP
+nsPrintEngine::GetIsFramesetFrameSelected(bool *aIsFramesetFrameSelected)
+{
+ // Get the currently focused window
+ nsCOMPtr<nsPIDOMWindowOuter> currentFocusWin = FindFocusedDOMWindow();
+ *aIsFramesetFrameSelected = currentFocusWin != nullptr;
+ return NS_OK;
+}
+
+//----------------------------------------------------------------------------------
+NS_IMETHODIMP
+nsPrintEngine::GetPrintPreviewNumPages(int32_t *aPrintPreviewNumPages)
+{
+ NS_ENSURE_ARG_POINTER(aPrintPreviewNumPages);
+
+ nsIFrame* seqFrame = nullptr;
+ *aPrintPreviewNumPages = 0;
+
+ // When calling this function, the FinishPrintPreview() function might not
+ // been called as there are still some
+ RefPtr<nsPrintData> printData = mPrtPreview ? mPrtPreview : mPrt;
+ if (NS_WARN_IF(!printData)) {
+ return NS_ERROR_FAILURE;
+ }
+ nsresult rv =
+ GetSeqFrameAndCountPagesInternal(printData->mPrintObject, seqFrame,
+ *aPrintPreviewNumPages);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+//----------------------------------------------------------------------------------
+// Enumerate all the documents for their titles
+NS_IMETHODIMP
+nsPrintEngine::EnumerateDocumentNames(uint32_t* aCount,
+ char16_t*** aResult)
+{
+ NS_ENSURE_ARG(aCount);
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ *aCount = 0;
+ *aResult = nullptr;
+
+ int32_t numDocs = mPrt->mPrintDocList.Length();
+ char16_t** array = (char16_t**) moz_xmalloc(numDocs * sizeof(char16_t*));
+ 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!");
+ nsAutoString docTitleStr;
+ nsAutoString docURLStr;
+ GetDocumentTitleAndURL(po->mDocument, docTitleStr, docURLStr);
+
+ // Use the URL if the doc is empty
+ if (docTitleStr.IsEmpty() && !docURLStr.IsEmpty()) {
+ docTitleStr = docURLStr;
+ }
+ array[i] = ToNewUnicode(docTitleStr);
+ }
+ *aCount = numDocs;
+ *aResult = array;
+
+ return NS_OK;
+
+}
+
+//----------------------------------------------------------------------------------
+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;
+}
+
+//----------------------------------------------------------------------------------
+NS_IMETHODIMP
+nsPrintEngine::GetDoingPrint(bool *aDoingPrint)
+{
+ NS_ENSURE_ARG_POINTER(aDoingPrint);
+ *aDoingPrint = mIsDoingPrinting;
+ return NS_OK;
+}
+
+//----------------------------------------------------------------------------------
+NS_IMETHODIMP
+nsPrintEngine::GetDoingPrintPreview(bool *aDoingPrintPreview)
+{
+ NS_ENSURE_ARG_POINTER(aDoingPrintPreview);
+ *aDoingPrintPreview = mIsDoingPrintPreview;
+ return NS_OK;
+}
+
+//----------------------------------------------------------------------------------
+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");
+ }
+
+ // Guarantee that mPrt and the objects it owns won't be deleted. If this
+ // method shows a progress dialog and spins the event loop. So, mPrt may be
+ // cleared or recreated.
+ RefPtr<nsPrintData> printData = mPrt;
+
+ // Turning off the showing of Print Progress in Prefs overrides
+ // whether the calling PS desire to have it on or off, so only check PS if
+ // prefs says it's ok to be on.
+ if (showProgresssDialog) {
+ printData->mPrintSettings->GetShowPrintProgress(&showProgresssDialog);
+ }
+
+ // Now open the service to get the progress dialog
+ // If we don't get a service, that's ok, then just don't show progress
+ if (showProgresssDialog) {
+ nsCOMPtr<nsIPrintingPromptService> printPromptService(do_GetService(kPrintingPromptService));
+ if (printPromptService) {
+ nsPIDOMWindowOuter* 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, printData->mPrintSettings, this,
+ aIsForPrinting,
+ getter_AddRefs(printProgressListener),
+ getter_AddRefs(printData->mPrintProgressParams),
+ &aDoNotify);
+ if (NS_SUCCEEDED(rv)) {
+ if (printProgressListener) {
+ printData->mPrintProgressListeners.AppendObject(
+ printProgressListener);
+ }
+
+ if (printData->mPrintProgressParams) {
+ SetDocAndURLIntoProgress(printData->mPrintObject,
+ printData->mPrintProgressParams);
+ }
+ }
+ }
+ }
+}
+
+//---------------------------------------------------------------------
+bool
+nsPrintEngine::IsThereARangeSelection(nsPIDOMWindowOuter* aDOMWin)
+{
+ if (mDisallowSelectionPrint)
+ return false;
+
+ nsCOMPtr<nsIPresShell> presShell;
+ if (aDOMWin) {
+ presShell = aDOMWin->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(SelectionType::eNormal);
+ if (!selection) {
+ return false;
+ }
+
+ int32_t rangeCount = selection->RangeCount();
+ 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<nsIDocument> doc = aParent->GetDocument();
+ 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(nsIDocShell * 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<nsIDOMDocument> doc = do_GetInterface(childAsShell);
+ nsPrintObject * po = new nsPrintObject();
+ po->mParent = aPO;
+ nsresult rv = po->Init(childAsShell, doc, aPO->mPrintPreview);
+ if (NS_FAILED(rv))
+ NS_NOTREACHED("Init failed?");
+ aPO->mKids.AppendElement(po);
+ aDocList->AppendElement(po);
+ BuildDocTree(childAsShell, aDocList, po);
+ }
+ }
+ }
+ }
+}
+
+//---------------------------------------------------------------------
+void
+nsPrintEngine::GetDocumentTitleAndURL(nsIDocument* aDoc,
+ nsAString& aTitle,
+ nsAString& aURLStr)
+{
+ NS_ASSERTION(aDoc, "Pointer is null!");
+
+ aTitle.Truncate();
+ aURLStr.Truncate();
+
+ nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(aDoc);
+ doc->GetTitle(aTitle);
+
+ 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;
+ nsresult rv = exposableURI->GetSpec(urlCStr);
+ if (NS_FAILED(rv)) return;
+
+ nsCOMPtr<nsITextToSubURI> textToSubURI =
+ do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) return;
+
+ textToSubURI->UnEscapeURIForUI(NS_LITERAL_CSTRING("UTF-8"),
+ urlCStr, aURLStr);
+}
+
+//---------------------------------------------------------------------
+// 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->GetComposedDoc();
+
+ NS_ASSERTION(doc, "Content without a document from a document tree?");
+
+ nsIDocument* subDoc = doc->GetSubDocumentFor(aContent);
+
+ if (subDoc) {
+ nsCOMPtr<nsIDocShell> docShell(subDoc->GetDocShell());
+
+ 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,
+ nsPIDOMWindowOuter* 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
+ nsPIDOMWindowOuter* domWin = aDocShell ? aDocShell->GetWindow() : nullptr;
+ 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,
+ nsAString& aTitle,
+ nsAString& aURLStr,
+ eDocTitleDefault aDefType)
+{
+ NS_ASSERTION(aPO, "Pointer is null!");
+
+ if (!mPrt)
+ return;
+
+ aTitle.Truncate();
+ aURLStr.Truncate();
+
+ // First check to see if the PrintSettings has defined an alternate title
+ // and use that if it did
+ if (mPrt->mPrintSettings) {
+ char16_t * docTitleStrPS = nullptr;
+ char16_t * docURLStrPS = nullptr;
+ mPrt->mPrintSettings->GetTitle(&docTitleStrPS);
+ mPrt->mPrintSettings->GetDocURL(&docURLStrPS);
+
+ if (docTitleStrPS) {
+ aTitle = docTitleStrPS;
+ }
+
+ if (docURLStrPS) {
+ aURLStr = docURLStrPS;
+ }
+
+ free(docTitleStrPS);
+ free(docURLStrPS);
+ }
+
+ nsAutoString docTitle;
+ nsAutoString docUrl;
+ GetDocumentTitleAndURL(aPO->mDocument, docTitle, docUrl);
+
+ if (aURLStr.IsEmpty() && !docUrl.IsEmpty()) {
+ aURLStr = docUrl;
+ }
+
+ if (aTitle.IsEmpty()) {
+ if (!docTitle.IsEmpty()) {
+ aTitle = docTitle;
+ } else {
+ if (aDefType == eDocTitleDefURLDoc) {
+ if (!aURLStr.IsEmpty()) {
+ aTitle = aURLStr;
+ } else if (mPrt->mBrandName) {
+ aTitle = mPrt->mBrandName;
+ }
+ }
+ }
+ }
+}
+
+//---------------------------------------------------------------------
+nsresult nsPrintEngine::DocumentReadyForPrinting()
+{
+ if (mPrt->mPrintFrameType == nsIPrintSettings::kEachFrameSep) {
+ // Guarantee that mPrt->mPrintObject won't be deleted during a call of
+ // CheckForChildFrameSets().
+ RefPtr<nsPrintData> printData = mPrt;
+ CheckForChildFrameSets(printData->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();
+ DisconnectPagePrintTimer();
+ }
+
+ 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) {
+ FirePrintingErrorEvent(aResult);
+ }
+
+ FirePrintCompletionEvent();
+
+ return aResult;
+
+}
+
+//---------------------------------------------------------------------
+void
+nsPrintEngine::FirePrintingErrorEvent(nsresult aPrintError)
+{
+ nsCOMPtr<nsIContentViewer> cv = do_QueryInterface(mDocViewerPrint);
+ if (NS_WARN_IF(!cv)) {
+ return;
+ }
+
+ nsCOMPtr<nsIDocument> doc = cv->GetDocument();
+ RefPtr<CustomEvent> event =
+ NS_NewDOMCustomEvent(doc, nullptr, nullptr);
+
+ MOZ_ASSERT(event);
+ nsCOMPtr<nsIWritableVariant> resultVariant = new nsVariant();
+ // nsresults are Uint32_t's, but XPConnect will interpret it as a double
+ // when any JS attempts to access it, and will therefore interpret it
+ // incorrectly. We preempt this by casting and setting as a double.
+ resultVariant->SetAsDouble(static_cast<double>(aPrintError));
+
+ event->InitCustomEvent(NS_LITERAL_STRING("PrintingError"), false, false,
+ resultVariant);
+ event->SetTrusted(true);
+
+ RefPtr<AsyncEventDispatcher> asyncDispatcher =
+ new AsyncEventDispatcher(doc, event);
+ asyncDispatcher->mOnlyChromeDispatch = true;
+ asyncDispatcher->RunDOMEventWhenSafe();
+
+ // Inform any progress listeners of the Error.
+ if (mPrt) {
+ // Note that nsPrintData::DoOnStatusChange() will call some listeners.
+ // So, mPrt can be cleared or recreated.
+ RefPtr<nsPrintData> printData = mPrt;
+ printData->DoOnStatusChange(aPrintError);
+ }
+}
+
+//-----------------------------------------------------------------
+//-- Section: Reflow Methods
+//-----------------------------------------------------------------
+
+nsresult
+nsPrintEngine::ReconstructAndReflow(bool doSetPixelScale)
+{
+ if (NS_WARN_IF(!mPrt)) {
+ return NS_ERROR_FAILURE;
+ }
+
+#if defined(XP_WIN) && defined(EXTENDED_DEBUG_PRINTING)
+ // We need to clear all the output files here
+ // because they will be re-created with second reflow of the docs
+ if (kPrintingLogMod && kPrintingLogMod->level == DUMP_LAYOUT_LEVEL) {
+ RemoveFilesInDir(".\\");
+ gDumpFileNameCnt = 0;
+ gDumpLOFileNameCnt = 0;
+ }
+#endif
+
+ // In this loop, it's conceivable that one of our helpers might clear mPrt,
+ // while we're using it & its members! So we capture it in an owning local
+ // reference & use that instead of using mPrt directly.
+ RefPtr<nsPrintData> printData = mPrt;
+ for (uint32_t i = 0; i < printData->mPrintDocList.Length(); ++i) {
+ nsPrintObject* po = printData->mPrintDocList.ElementAt(i);
+ NS_ASSERTION(po, "nsPrintObject can't be null!");
+
+ if (po->mDontPrint || po->mInvisible) {
+ continue;
+ }
+
+ UpdateZoomRatio(po, doSetPixelScale);
+
+ po->mPresContext->SetPageScale(po->mZoomRatio);
+
+ // Calculate scale factor from printer to screen
+ float printDPI = float(printData->mPrintDC->AppUnitsPerCSSInch()) /
+ float(printData->mPrintDC->AppUnitsPerDevPixel());
+ po->mPresContext->SetPrintPreviewScale(mScreenDPI / printDPI);
+
+ po->mPresShell->ReconstructFrames();
+
+ // If the printing was canceled or restarted with different data,
+ // let's stop doing this printing.
+ if (NS_WARN_IF(mPrt != printData)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // For all views except the first one, setup the root view.
+ // ??? Can there be multiple po for the top-level-document?
+ bool documentIsTopLevel = true;
+ if (i != 0) {
+ nsSize adjSize;
+ bool doReturn;
+ nsresult rv = SetRootView(po, doReturn, documentIsTopLevel, adjSize);
+
+ MOZ_ASSERT(!documentIsTopLevel, "How could this happen?");
+
+ if (NS_FAILED(rv) || doReturn) {
+ return rv;
+ }
+ }
+
+ po->mPresShell->FlushPendingNotifications(Flush_Layout);
+
+ // If the printing was canceled or restarted with different data,
+ // let's stop doing this printing.
+ if (NS_WARN_IF(mPrt != printData)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult rv = UpdateSelectionAndShrinkPrintObject(po, documentIsTopLevel);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ return NS_OK;
+}
+
+//-------------------------------------------------------
+nsresult
+nsPrintEngine::SetupToPrintContent()
+{
+ if (NS_WARN_IF(!mPrt)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ bool didReconstruction = false;
+
+ // This method works with mPrt->mPrintObject. So, we need to guarantee that
+ // it won't be deleted in this method. We achieve this by holding a strong
+ // local reference to mPrt, which in turn keeps mPrintObject alive.
+ RefPtr<nsPrintData> printData = mPrt;
+
+ // If some new content got loaded since the initial reflow rebuild
+ // everything.
+ if (mDidLoadDataForPrinting) {
+ nsresult rv = ReconstructAndReflow(DoSetPixelScale());
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ // If the printing was canceled or restarted with different data,
+ // let's stop doing this printing.
+ if (NS_WARN_IF(mPrt != printData)) {
+ return NS_ERROR_FAILURE;
+ }
+ didReconstruction = true;
+ }
+
+ // Here is where we figure out if extra reflow for shrinking the content
+ // is required.
+ // But skip this step if we are in PrintPreview
+ bool ppIsShrinkToFit = mPrtPreview && mPrtPreview->mShrinkToFit;
+ if (printData->mShrinkToFit && !ppIsShrinkToFit) {
+ // Now look for the PO that has the smallest percent for shrink to fit
+ if (printData->mPrintDocList.Length() > 1 &&
+ printData->mPrintObject->mFrameType == eFrameSet) {
+ nsPrintObject* smallestPO = FindSmallestSTF();
+ NS_ASSERTION(smallestPO, "There must always be an XMost PO!");
+ if (smallestPO) {
+ // Calc the shrinkage based on the entire content area
+ printData->mShrinkRatio = smallestPO->mShrinkRatio;
+ }
+ } else {
+ // Single document so use the Shrink as calculated for the PO
+ printData->mShrinkRatio = printData->mPrintObject->mShrinkRatio;
+ }
+
+ if (printData->mShrinkRatio < 0.998f) {
+ nsresult rv = ReconstructAndReflow(true);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ // If the printing was canceled or restarted with different data,
+ // let's stop doing this printing.
+ if (NS_WARN_IF(mPrt != printData)) {
+ return NS_ERROR_FAILURE;
+ }
+ didReconstruction = true;
+ }
+
+ if (MOZ_LOG_TEST(gPrintingLog, LogLevel::Debug)) {
+ float calcRatio = 0.0f;
+ if (printData->mPrintDocList.Length() > 1 &&
+ printData->mPrintObject->mFrameType == eFrameSet) {
+ nsPrintObject* smallestPO = FindSmallestSTF();
+ NS_ASSERTION(smallestPO, "There must always be an XMost PO!");
+ if (smallestPO) {
+ // Calc the shrinkage based on the entire content area
+ calcRatio = smallestPO->mShrinkRatio;
+ }
+ } else {
+ // Single document so use the Shrink as calculated for the PO
+ calcRatio = printData->mPrintObject->mShrinkRatio;
+ }
+ PR_PL(("**************************************************************************\n"));
+ PR_PL(("STF Ratio is: %8.5f Effective Ratio: %8.5f Diff: %8.5f\n",
+ printData->mShrinkRatio, calcRatio,
+ printData->mShrinkRatio-calcRatio));
+ PR_PL(("**************************************************************************\n"));
+ }
+ }
+
+ // If the frames got reconstructed and reflowed the number of pages might
+ // has changed.
+ if (didReconstruction) {
+ FirePrintPreviewUpdateEvent();
+ // If the printing was canceled or restarted with different data,
+ // let's stop doing this printing.
+ if (NS_WARN_IF(mPrt != printData)) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ DUMP_DOC_LIST(("\nAfter Reflow------------------------------------------"));
+ PR_PL(("\n"));
+ PR_PL(("-------------------------------------------------------\n"));
+ PR_PL(("\n"));
+
+ CalcNumPrintablePages(printData->mNumPrintablePages);
+
+ PR_PL(("--- Printing %d pages\n", printData->mNumPrintablePages));
+ DUMP_DOC_TREELAYOUT;
+
+ // Print listener setup...
+ printData->OnStartPrinting();
+
+ // If the printing was canceled or restarted with different data,
+ // let's stop doing this printing.
+ if (NS_WARN_IF(mPrt != printData)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsAutoString fileNameStr;
+ // check to see if we are printing to a file
+ bool isPrintToFile = false;
+ printData->mPrintSettings->GetPrintToFile(&isPrintToFile);
+ if (isPrintToFile) {
+ // On some platforms The BeginDocument needs to know the name of the file.
+ char16_t* fileName = nullptr;
+ printData->mPrintSettings->GetToFileName(&fileName);
+ fileNameStr = fileName;
+ }
+
+ nsAutoString docTitleStr;
+ nsAutoString docURLStr;
+ GetDisplayTitleAndURL(printData->mPrintObject, docTitleStr, docURLStr,
+ eDocTitleDefURLDoc);
+
+ int32_t startPage = 1;
+ int32_t endPage = printData->mNumPrintablePages;
+
+ int16_t printRangeType = nsIPrintSettings::kRangeAllPages;
+ printData->mPrintSettings->GetPrintRange(&printRangeType);
+ if (printRangeType == nsIPrintSettings::kRangeSpecifiedPageRange) {
+ printData->mPrintSettings->GetStartPageRange(&startPage);
+ printData->mPrintSettings->GetEndPageRange(&endPage);
+ if (endPage > printData->mNumPrintablePages) {
+ endPage = printData->mNumPrintablePages;
+ }
+ }
+
+ nsresult rv = NS_OK;
+ // BeginDocument may pass back a FAILURE code
+ // i.e. On Windows, if you are printing to a file and hit "Cancel"
+ // to the "File Name" dialog, this comes back as an error
+ // Don't start printing when regression test are executed
+ if (!printData->mDebugFilePtr && mIsDoingPrinting) {
+ rv = printData->mPrintDC->BeginDocument(docTitleStr, fileNameStr, startPage,
+ endPage);
+ }
+
+ if (mIsCreatingPrintPreview) {
+ // Copy docTitleStr and docURLStr to the pageSequenceFrame, to be displayed
+ // in the header
+ nsIPageSequenceFrame* seqFrame =
+ printData->mPrintObject->mPresShell->GetPageSequenceFrame();
+ if (seqFrame) {
+ seqFrame->StartPrint(printData->mPrintObject->mPresContext,
+ printData->mPrintSettings, docTitleStr, 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(printData->mPrintObject, rv); // ignore return value
+ }
+
+ return rv;
+}
+
+//-------------------------------------------------------
+// Recursively reflow each sub-doc and then calc
+// all the frame locations of the sub-docs
+nsresult
+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 AsyncEventDispatcher(
+ cv->GetDocument(), NS_LITERAL_STRING("printPreviewUpdate"), true, true)
+ )->RunDOMEventWhenSafe();
+ }
+}
+
+nsresult
+nsPrintEngine::InitPrintDocConstruction(bool aHandleError)
+{
+ nsresult rv;
+ // Guarantee that mPrt->mPrintObject won't be deleted. It's owned by mPrt.
+ // So, we should grab it with local variable.
+ RefPtr<nsPrintData> printData = mPrt;
+ rv = ReflowDocList(printData->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)) {
+ NS_WARNING("nsPrintEngine::AfterNetworkPrint failed");
+ 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.EqualsLiteral("about:document-onload-blocker")) {
+ return NS_OK;
+ }
+ if (aStateFlags & STATE_START) {
+ ++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 char16_t *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
+ RefPtr<Selection> selection, selectionPS;
+ // It's okay if there is no display shell, just skip copying the selection
+ if (displayShell) {
+ selection = displayShell->GetCurrentSelection(SelectionType::eNormal);
+ }
+ selectionPS = aPO->mPresShell->GetCurrentSelection(SelectionType::eNormal);
+
+ // Reset all existing selection ranges that might have been added by calling
+ // this function before.
+ if (selectionPS) {
+ selectionPS->RemoveAllRanges();
+ }
+ if (selection && selectionPS) {
+ int32_t cnt = selection->RangeCount();
+ 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);
+ // Limit the shrink-to-fit scaling for some text-ish type of documents.
+ nsAutoString contentType;
+ aPO->mPresShell->GetDocument()->GetContentType(contentType);
+ if (contentType.EqualsLiteral("application/xhtml+xml") ||
+ StringBeginsWith(contentType, NS_LITERAL_STRING("text/"))) {
+ int32_t limitPercent =
+ Preferences::GetInt("print.shrink-to-fit.scale-limit-percent", 20);
+ limitPercent = std::max(0, limitPercent);
+ limitPercent = std::min(100, limitPercent);
+ float minShrinkRatio = float(limitPercent) / 100;
+ aPO->mShrinkRatio = std::max(aPO->mShrinkRatio, minShrinkRatio);
+ }
+ }
+ return NS_OK;
+}
+
+bool
+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");
+
+ // Guarantee that mPrt and the objects it owns won't be deleted in this method
+ // because it might be cleared if other modules called from here may fire
+ // events, notifying observers and/or listeners.
+ RefPtr<nsPrintData> printData = mPrt;
+
+ // create the PresContext
+ nsPresContext::nsPresContextType type =
+ mIsCreatingPrintPreview ? nsPresContext::eContext_PrintPreview:
+ nsPresContext::eContext_Print;
+ 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(printData->mPrintSettings);
+
+ // set the presentation context to the value in the print settings
+ bool printBGColors;
+ printData->mPrintSettings->GetPrintBGColors(&printBGColors);
+ aPO->mPresContext->SetBackgroundColorDraw(printBGColors);
+ printData->mPrintSettings->GetPrintBGImages(&printBGColors);
+ aPO->mPresContext->SetBackgroundImageDraw(printBGColors);
+
+ // init it with the DC
+ nsresult rv = aPO->mPresContext->Init(printData->mPrintDC);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aPO->mViewManager = new nsViewManager();
+
+ rv = aPO->mViewManager->Init(printData->mPrintDC);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ StyleSetHandle styleSet = mDocViewerPrint->CreateStyleSet(aPO->mDocument);
+
+ aPO->mPresShell = aPO->mDocument->CreateShell(aPO->mPresContext,
+ aPO->mViewManager, styleSet);
+ if (!aPO->mPresShell) {
+ styleSet->Delete();
+ 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
+ aPO->mPresContext->SetContainer(aPO->mDocShell);
+
+ 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(printData->mPrintDC->AppUnitsPerCSSInch()) /
+ float(printData->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) {
+ nsAutoCString docStr;
+ nsAutoCString urlStr;
+ GetDocTitleAndURL(aPO, docStr, urlStr);
+ char filename[256];
+ sprintf(filename, "print_dump_%d.txt", gDumpFileNameCnt++);
+ // Dump all the frames and view to a a file
+ FILE * fd = fopen(filename, "w");
+ if (fd) {
+ nsIFrame *theRootFrame =
+ aPO->mPresShell->FrameManager()->GetRootFrame();
+ fprintf(fd, "Title: %s\n", docStr.get());
+ fprintf(fd, "URL: %s\n", urlStr.get());
+ fprintf(fd, "--------------- Frames ----------------\n");
+ //RefPtr<gfxContext> renderingContext =
+ // printData->mPrintDocDC->CreateRenderingContext();
+ RootFrameList(aPO->mPresContext, fd, 0);
+ //DumpFrames(fd, aPO->mPresContext, renderingContext, theRootFrame, 0);
+ fprintf(fd, "---------------------------------------\n\n");
+ fprintf(fd, "--------------- Views From Root Frame----------------\n");
+ nsView* v = theRootFrame->GetView();
+ if (v) {
+ v->List(fd);
+ } else {
+ printf("View is null!\n");
+ }
+ if (docShell) {
+ fprintf(fd, "--------------- All Views ----------------\n");
+ DumpViews(docShell, fd);
+ fprintf(fd, "---------------------------------------\n\n");
+ }
+ fclose(fd);
+ }
+ }
+#endif
+
+ return NS_OK;
+}
+
+//-------------------------------------------------------
+// Figure out how many documents and how many total pages we are printing
+void
+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) {
+ aNumPages += seqFrame->PrincipalChildList().GetLength();
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------
+//-- 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);
+
+ RefPtr<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);
+
+ RefPtr<Selection> origSelection =
+ origShell->GetCurrentSelection(SelectionType::eNormal);
+ RefPtr<Selection> selection =
+ shell->GetCurrentSelection(SelectionType::eNormal);
+ NS_ENSURE_STATE(origSelection && selection);
+
+ int32_t rangeCount = origSelection->RangeCount();
+ 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?");
+
+ // Guarantee that mPrt and its owning objects won't be deleted in this method
+ // because it might be cleared if other modules called from here may fire
+ // events, notifying observers and/or listeners.
+ RefPtr<nsPrintData> printData = mPrt;
+
+ if (printData->mPrintProgressParams) {
+ SetDocAndURLIntoProgress(aPO, printData->mPrintProgressParams);
+ }
+
+ {
+ int16_t printRangeType = nsIPrintSettings::kRangeAllPages;
+ nsresult rv;
+ if (printData->mPrintSettings) {
+ printData->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
+ printData->mPreparingForPrint = false;
+
+ // printData->mDebugFilePtr this is onlu non-null when compiled for
+ // debugging
+ if (printData->mDebugFilePtr) {
+#ifdef DEBUG
+ // output the regression test
+ nsIFrame* root = poPresShell->FrameManager()->GetRootFrame();
+ root->DumpRegressionData(poPresContext, printData->mDebugFilePtr, 0);
+ fclose(printData->mDebugFilePtr);
+ SetIsPrinting(false);
+#endif
+ } else {
+#ifdef EXTENDED_DEBUG_PRINTING
+ nsIFrame* rootFrame = poPresShell->FrameManager()->GetRootFrame();
+ if (aPO->IsPrintable()) {
+ nsAutoCString docStr;
+ nsAutoCString urlStr;
+ GetDocTitleAndURL(aPO, docStr, urlStr);
+ DumpLayoutData(docStr.get(), urlStr.get(), poPresContext,
+ printData->mPrintDocDC, rootFrame, docShell, nullptr);
+ }
+#endif
+
+ if (!printData->mPrintSettings) {
+ // not sure what to do here!
+ SetIsPrinting(false);
+ return NS_ERROR_FAILURE;
+ }
+
+ nsAutoString docTitleStr;
+ nsAutoString docURLStr;
+ 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
+ // mPrintDC must have positive width and height for this call
+
+ // find the starting and ending page numbers
+ // via the selection
+ nsIFrame* startFrame;
+ nsIFrame* endFrame;
+ int32_t startPageNum;
+ int32_t endPageNum;
+ nsRect startRect;
+ nsRect endRect;
+
+ rv = GetPageRangeForSelection(pageSequence,
+ &startFrame, startPageNum, startRect,
+ &endFrame, endPageNum, endRect);
+ if (NS_SUCCEEDED(rv)) {
+ printData->mPrintSettings->SetStartPageRange(startPageNum);
+ printData->mPrintSettings->SetEndPageRange(endPageNum);
+ nsIntMargin marginTwips(0,0,0,0);
+ nsIntMargin unwrtMarginTwips(0,0,0,0);
+ printData->mPrintSettings->GetMarginInTwips(marginTwips);
+ printData->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;
+ printData->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);
+ return NS_ERROR_FAILURE;
+ }
+
+ mPageSeqFrame = seqFrame;
+ pageSequence->StartPrint(poPresContext, printData->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 valid nsPrintObject");
+ NS_ASSERTION(aParams, "Must have valid nsIPrintProgressParams");
+
+ if (!aPO || !aPO->mDocShell || !aParams) {
+ return;
+ }
+ const uint32_t kTitleLength = 64;
+
+ nsAutoString docTitleStr;
+ nsAutoString docURLStr;
+ GetDisplayTitleAndURL(aPO, docTitleStr, docURLStr, eDocTitleDefURLDoc);
+
+ // Make sure the Titles & URLS don't get too long for the progress dialog
+ EllipseLongString(docTitleStr, kTitleLength, false);
+ EllipseLongString(docURLStr, kTitleLength, true);
+
+ aParams->SetDocTitle(docTitleStr.get());
+ aParams->SetDocURL(docURLStr.get());
+}
+
+//---------------------------------------------------------------------
+void
+nsPrintEngine::EllipseLongString(nsAString& aStr, const uint32_t aLen, bool aDoFront)
+{
+ // Make sure the URLS don't get too long for the progress dialog
+ if (aLen >= 3 && aStr.Length() > aLen) {
+ if (aDoFront) {
+ nsAutoString newStr;
+ newStr.AppendLiteral("...");
+ newStr += Substring(aStr, aStr.Length() - (aLen - 3), aLen - 3);
+ aStr = newStr;
+ } else {
+ aStr.SetLength(aLen - 3);
+ aStr.AppendLiteral("...");
+ }
+ }
+}
+
+static bool
+DocHasPrintCallbackCanvas(nsIDocument* aDoc, void* aData)
+{
+ if (!aDoc) {
+ return true;
+ }
+ Element* root = aDoc->GetRootElement();
+ if (!root) {
+ return true;
+ }
+ RefPtr<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) {
+ HTMLCanvasElement* canvas = HTMLCanvasElement::FromContentOrNull(canvases->Item(i, false));
+ if (canvas && canvas->GetMozPrintCallback()) {
+ // 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.
+ * https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement#Properties
+ */
+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.IsAlive(), "mPageSeqFrame is not alive!");
+ NS_ASSERTION(mPrt, "mPrt is null!");
+
+ // Although these should NEVER be nullptr
+ // This is added insurance, to make sure we don't crash in optimized builds
+ if (!mPrt || !mPageSeqFrame.IsAlive()) {
+ return true; // means we are done preparing the page.
+ }
+
+ // Guarantee that mPrt won't be deleted during a call of
+ // FirePrintingErrorEvent().
+ RefPtr<nsPrintData> printData = mPrt;
+
+ // Check setting to see if someone request it be cancelled
+ bool isCancelled = false;
+ printData->mPrintSettings->GetIsCancelled(&isCancelled);
+ if (isCancelled)
+ return true;
+
+ // Ask mPageSeqFrame if the page is ready to be printed.
+ // If the page doesn't get printed at all, the |done| will be |true|.
+ bool done = false;
+ nsIPageSequenceFrame* pageSeqFrame = do_QueryFrame(mPageSeqFrame.GetFrame());
+ nsresult rv = pageSeqFrame->PrePrintNextPage(mPagePrintTimer, &done);
+ if (NS_FAILED(rv)) {
+ // ??? ::PrintPage doesn't set |printData->mIsAborted = true| if
+ // rv != NS_ERROR_ABORT, but I don't really understand why this should be
+ // the right thing to do? Shouldn't |printData->mIsAborted| set to true
+ // all the time if something went wrong?
+ if (rv != NS_ERROR_ABORT) {
+ FirePrintingErrorEvent(rv);
+ printData->mIsAborted = true;
+ }
+ done = true;
+ }
+ return done;
+}
+
+bool
+nsPrintEngine::PrintPage(nsPrintObject* aPO,
+ bool& aInRange)
+{
+ NS_ASSERTION(aPO, "aPO is null!");
+ NS_ASSERTION(mPageSeqFrame.IsAlive(), "mPageSeqFrame is not alive!");
+ NS_ASSERTION(mPrt, "mPrt is null!");
+
+ // Although these should NEVER be nullptr
+ // This is added insurance, to make sure we don't crash in optimized builds
+ if (!mPrt || !aPO || !mPageSeqFrame.IsAlive()) {
+ FirePrintingErrorEvent(NS_ERROR_FAILURE);
+ return true; // means we are done printing
+ }
+
+ // Guarantee that mPrt won't be deleted during a call of
+ // nsPrintData::DoOnProgressChange() which runs some listeners,
+ // which may clear (& might otherwise destroy).
+ RefPtr<nsPrintData> printData = mPrt;
+
+ PR_PL(("-----------------------------------\n"));
+ PR_PL(("------ In DV::PrintPage PO: %p (%s)\n", aPO, gFrameTypesStr[aPO->mFrameType]));
+
+ // Check setting to see if someone request it be cancelled
+ bool isCancelled = false;
+ printData->mPrintSettings->GetIsCancelled(&isCancelled);
+ if (isCancelled || printData->mIsAborted) {
+ return true;
+ }
+
+ int32_t pageNum, numPages, endPage;
+ nsIPageSequenceFrame* pageSeqFrame = do_QueryFrame(mPageSeqFrame.GetFrame());
+ pageSeqFrame->GetCurrentPageNum(&pageNum);
+ pageSeqFrame->GetNumPages(&numPages);
+
+ bool donePrinting;
+ bool isDoingPrintRange;
+ pageSeqFrame->IsDoingPrintRange(&isDoingPrintRange);
+ if (isDoingPrintRange) {
+ int32_t fromPage;
+ int32_t toPage;
+ pageSeqFrame->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 (printData->mPrintFrameType == nsIPrintSettings::kEachFrameSep) {
+ endPage = printData->mNumPrintablePages;
+ }
+
+ printData->DoOnProgressChange(++printData->mNumPagesPrinted,
+ endPage, false, 0);
+ if (NS_WARN_IF(mPrt != printData)) {
+ // If current printing is canceled or new print is started, let's return
+ // true to notify the caller of current printing is done.
+ return true;
+ }
+
+ // 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 = pageSeqFrame->PrintNextPage();
+ if (NS_FAILED(rv)) {
+ if (rv != NS_ERROR_ABORT) {
+ FirePrintingErrorEvent(rv);
+ printData->mIsAborted = true;
+ }
+ return true;
+ }
+
+ pageSeqFrame->DoPageEnd();
+
+ return donePrinting;
+}
+
+/** ---------------------------------------------------
+ * Find by checking frames type
+ */
+nsresult
+nsPrintEngine::FindSelectionBoundsWithList(nsFrameList::Enumerator& aChildFrames,
+ nsIFrame * aParentFrame,
+ nsRect& aRect,
+ nsIFrame *& aStartFrame,
+ nsRect& aStartRect,
+ nsIFrame *& aEndFrame,
+ nsRect& aEndRect)
+{
+ 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(child, aRect, aStartFrame, aStartRect, aEndFrame, aEndRect);
+ child = child->GetNextSibling();
+ }
+ aRect -= aParentFrame->GetPosition();
+ return NS_OK;
+}
+
+//-------------------------------------------------------
+// Find the Frame that is XMost
+nsresult
+nsPrintEngine::FindSelectionBounds(nsIFrame * aParentFrame,
+ nsRect& aRect,
+ nsIFrame *& aStartFrame,
+ nsRect& aStartRect,
+ nsIFrame *& aEndFrame,
+ nsRect& aEndRect)
+{
+ 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(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(nsIPageSequenceFrame* aPageSeqFrame,
+ nsIFrame** aStartFrame,
+ int32_t& aStartPageNum,
+ nsRect& aStartRect,
+ nsIFrame** aEndFrame,
+ int32_t& aEndPageNum,
+ nsRect& aEndRect)
+{
+ 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(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->PrincipalChildList().FirstChild();
+ 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;
+ for (nsIFrame* page : seqFrame->PrincipalChildList()) {
+ if (page == startPageFrame) {
+ aStartPageNum = pageNum;
+ }
+ if (page == endPageFrame) {
+ aEndPageNum = pageNum;
+ }
+ pageNum++;
+ }
+
+#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(char16_t**& aArray, int32_t& aCount)
+{
+ for (int32_t i = aCount - 1; i >= 0; i--) {
+ free(aArray[i]);
+ }
+ free(aArray);
+ aArray = nullptr;
+ 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->IsHTMLElement(nsGkAtoms::frameset)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+
+/** ---------------------------------------------------
+ * Get the Focused Frame for a documentviewer
+ */
+already_AddRefed<nsPIDOMWindowOuter>
+nsPrintEngine::FindFocusedDOMWindow()
+{
+ nsIFocusManager* fm = nsFocusManager::GetFocusManager();
+ NS_ENSURE_TRUE(fm, nullptr);
+
+ nsPIDOMWindowOuter* window = mDocument->GetWindow();
+ NS_ENSURE_TRUE(window, nullptr);
+
+ nsCOMPtr<nsPIDOMWindowOuter> rootWindow = window->GetPrivateRoot();
+ NS_ENSURE_TRUE(rootWindow, nullptr);
+
+ nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
+ nsFocusManager::GetFocusedDescendant(rootWindow, true,
+ getter_AddRefs(focusedWindow));
+ NS_ENSURE_TRUE(focusedWindow, nullptr);
+
+ if (IsWindowsInOurSubTree(focusedWindow)) {
+ return focusedWindow.forget();
+ }
+
+ return nullptr;
+}
+
+//---------------------------------------------------------------------
+bool
+nsPrintEngine::IsWindowsInOurSubTree(nsPIDOMWindowOuter* 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.IsAlive()) {
+ nsIPageSequenceFrame* pageSeqFrame = do_QueryFrame(mPageSeqFrame.GetFrame());
+ pageSeqFrame->ResetPrintCanvasList();
+ }
+
+ // Guarantee that mPrt and mPrt->mPrintObject won't be deleted during a
+ // call of PrintDocContent() and FirePrintCompletionEvent().
+ RefPtr<nsPrintData> printData = mPrt;
+
+ if (aPO && !printData->mIsAborted) {
+ aPO->mHasBeenPrinted = true;
+ nsresult rv;
+ bool didPrint = PrintDocContent(printData->mPrintObject, rv);
+ if (NS_SUCCEEDED(rv) && didPrint) {
+ PR_PL(("****** In DV::DonePrintingPages PO: %p (%s) didPrint:%s (Not Done Printing)\n", aPO, gFrameTypesStr[aPO->mFrameType], PRT_YESNO(didPrint)));
+ return false;
+ }
+ }
+
+ if (NS_SUCCEEDED(aResult)) {
+ FirePrintCompletionEvent();
+ // XXX mPrt may be cleared or replaced with new instance here.
+ // However, the following methods will clean up with new mPrt or will
+ // do nothing due to no proper nsPrintData instance.
+ }
+
+ TurnScriptingOn(true);
+ SetIsPrinting(false);
+
+ // Release reference to mPagePrintTimer; the timer object destroys itself
+ // after this returns true
+ DisconnectPagePrintTimer();
+
+ 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,
+ nsPIDOMWindowOuter* 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<nsIDocument> doc = aDOMWin->GetDoc();
+ 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()
+{
+ // Guarantee that mPrt and the objects it owns won't be deleted.
+ RefPtr<nsPrintData> printData = mPrt;
+
+ // NOTE: All POs have been "turned off" for printing
+ // this is where we decided which POs get printed.
+ printData->mSelectedPO = nullptr;
+
+ if (printData->mPrintSettings == nullptr) {
+ return NS_ERROR_FAILURE;
+ }
+
+ printData->mPrintFrameType = nsIPrintSettings::kNoFrames;
+ printData->mPrintSettings->GetPrintFrameType(&printData->mPrintFrameType);
+
+ int16_t printHowEnable = nsIPrintSettings::kFrameEnableNone;
+ printData->mPrintSettings->GetHowToEnableFrameUI(&printHowEnable);
+
+ int16_t printRangeType = nsIPrintSettings::kRangeAllPages;
+ printData->mPrintSettings->GetPrintRange(&printRangeType);
+
+ PR_PL(("\n"));
+ PR_PL(("********* nsPrintEngine::EnablePOsForPrinting *********\n"));
+ PR_PL(("PrintFrameType: %s \n",
+ gPrintFrameTypeStr[printData->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) {
+ printData->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(printData->mPrintObject, true);
+
+ // Set the children so they are PrinAsIs
+ // In this case, the children are probably IFrames
+ if (printData->mPrintObject->mKids.Length() > 0) {
+ for (uint32_t i = 0; i < printData->mPrintObject->mKids.Length(); i++) {
+ nsPrintObject* po = printData->mPrintObject->mKids[i];
+ NS_ASSERTION(po, "nsPrintObject can't be null!");
+ SetPrintAsIs(po);
+ }
+
+ // ***** Another override *****
+ printData->mPrintFrameType = nsIPrintSettings::kFramesAsIs;
+ }
+ PR_PL(("PrintFrameType: %s \n",
+ gPrintFrameTypeStr[printData->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 (printData->mCurrentFocusWin) {
+ // Find the selected IFrame
+ nsPrintObject* po =
+ FindPrintObjectByDOMWin(printData->mPrintObject,
+ printData->mCurrentFocusWin);
+ if (po) {
+ printData->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
+ nsPIDOMWindowOuter* domWin =
+ po->mDocument->GetOriginalDocument()->GetWindow();
+ if (!IsThereARangeSelection(domWin)) {
+ printRangeType = nsIPrintSettings::kRangeAllPages;
+ printData->mPrintSettings->SetPrintRange(printRangeType);
+ }
+ PR_PL(("PrintFrameType: %s \n",
+ gPrintFrameTypeStr[printData->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 < printData->mPrintDocList.Length(); i++) {
+ nsPrintObject* po = printData->mPrintDocList.ElementAt(i);
+ NS_ASSERTION(po, "nsPrintObject can't be null!");
+ nsCOMPtr<nsPIDOMWindowOuter> domWin = po->mDocShell->GetWindow();
+ if (IsThereARangeSelection(domWin)) {
+ printData->mCurrentFocusWin = domWin.forget();
+ 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 (printData->mCurrentFocusWin) {
+ // Find the selected IFrame
+ nsPrintObject* po =
+ FindPrintObjectByDOMWin(printData->mPrintObject,
+ printData->mCurrentFocusWin);
+ if (po) {
+ printData->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<nsPIDOMWindowOuter> domWin = po->mDocument->GetOriginalDocument()->GetWindow();
+ if (!IsThereARangeSelection(domWin)) {
+ printRangeType = nsIPrintSettings::kRangeAllPages;
+ printData->mPrintSettings->SetPrintRange(printRangeType);
+ }
+ PR_PL(("PrintFrameType: %s \n",
+ gPrintFrameTypeStr[printData->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 (printData->mPrintFrameType == nsIPrintSettings::kFramesAsIs) {
+ SetPrintAsIs(printData->mPrintObject);
+ SetPrintPO(printData->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 (printData->mPrintFrameType == nsIPrintSettings::kSelectedFrame) {
+ if ((printData->mIsParentAFrameSet && printData->mCurrentFocusWin) ||
+ printData->mIsIFrameSelected) {
+ nsPrintObject* po =
+ FindPrintObjectByDOMWin(printData->mPrintObject,
+ printData->mCurrentFocusWin);
+ if (po) {
+ printData->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 (printData->mPrintFrameType == nsIPrintSettings::kEachFrameSep) {
+ SetPrintPO(printData->mPrintObject, true);
+ int32_t cnt = printData->mPrintDocList.Length();
+ for (int32_t i=0;i<cnt;i++) {
+ nsPrintObject* po = printData->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;
+ }
+
+ // The following for loop uses nsPrintObject instances that are owned by
+ // mPrt or mPrtPreview. Therefore, this method needs to guarantee that
+ // they won't be deleted in this method.
+ RefPtr<nsPrintData> printData = mPrt ? mPrt : mPrtPreview;
+ if (!printData) {
+ return;
+ }
+
+ NS_ASSERTION(mDocument, "We MUST have a document.");
+ // First, get the script global object from the document...
+
+ for (uint32_t i = 0; i < printData->mPrintDocList.Length(); i++) {
+ nsPrintObject* po = printData->mPrintDocList.ElementAt(i);
+ MOZ_ASSERT(po);
+
+ nsIDocument* doc = po->mDocument;
+ if (!doc) {
+ continue;
+ }
+
+ if (nsCOMPtr<nsPIDOMWindowInner> window = doc->GetInnerWindow()) {
+ nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(window);
+ NS_WARNING_ASSERTION(go && go->GetGlobalJSObject(), "Can't get global");
+ 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 (go && go->GetGlobalJSObject()) {
+ xpc::Scriptability::Get(go->GetGlobalJSObject()).Unblock();
+ }
+ window->Resume();
+ }
+ } 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 (go && go->GetGlobalJSObject()) {
+ xpc::Scriptability::Get(go->GetGlobalJSObject()).Block();
+ }
+ window->Suspend();
+ }
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------
+//-- 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);
+
+ // mPrt may be cleared during a call of nsPrintData::OnEndPrinting()
+ // because that method invokes some arbitrary listeners.
+ RefPtr<nsPrintData> printData = mPrt;
+ if (NS_FAILED(rv)) {
+ /* cleanup done, let's fire-up an error dialog to notify the user
+ * what went wrong...
+ */
+ printData->OnEndPrinting();
+ // XXX mPrt may be nullptr here. So, Shouldn't TurnScriptingOn() take
+ // nsPrintData as an argument?
+ TurnScriptingOn(true);
+
+ return rv;
+ }
+
+ // At this point we are done preparing everything
+ // before it is to be created
+
+ if (mIsDoingPrintPreview && mOldPrtPreview) {
+ mOldPrtPreview = nullptr;
+ }
+
+ printData->OnEndPrinting();
+ // XXX If mPrt becomes nullptr or different instance here, what should we
+ // do?
+
+ // PrintPreview was built using the mPrt (code reuse)
+ // then we assign it over
+ mPrtPreview = Move(mPrt);
+
+#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);
+
+ RefPtr<nsPagePrintTimer> timer =
+ new nsPagePrintTimer(this, mDocViewerPrint, printPageDelay);
+ timer.forget(&mPagePrintTimer);
+
+ nsCOMPtr<nsIPrintSession> printSession;
+ nsresult rv = mPrt->mPrintSettings->GetPrintSession(getter_AddRefs(printSession));
+ if (NS_SUCCEEDED(rv) && printSession) {
+ RefPtr<mozilla::layout::RemotePrintJobChild> remotePrintJob;
+ printSession->GetRemotePrintJob(getter_AddRefs(remotePrintJob));
+ if (NS_SUCCEEDED(rv) && remotePrintJob) {
+ remotePrintJob->SetPagePrintTimer(mPagePrintTimer);
+ remotePrintJob->SetPrintEngine(this);
+ }
+ }
+ }
+
+ return mPagePrintTimer->Start(aPO);
+}
+
+/*=============== nsIObserver Interface ======================*/
+NS_IMETHODIMP
+nsPrintEngine::Observe(nsISupports *aSubject, const char *aTopic, const char16_t *aData)
+{
+ nsresult rv = NS_ERROR_FAILURE;
+
+ rv = InitPrintDocConstruction(true);
+ if (!mIsDoingPrinting && mPrtPreview) {
+ RefPtr<nsPrintData> printDataOfPrintPreview = mPrtPreview;
+ printDataOfPrintPreview->OnEndPrinting();
+ }
+
+ return rv;
+
+}
+
+//---------------------------------------------------------------
+//-- PLEvent Notification
+//---------------------------------------------------------------
+class nsPrintCompletionEvent : public Runnable {
+public:
+ explicit nsPrintCompletionEvent(nsIDocumentViewerPrint *docViewerPrint)
+ : mDocViewerPrint(docViewerPrint) {
+ NS_ASSERTION(mDocViewerPrint, "mDocViewerPrint is null.");
+ }
+
+ NS_IMETHOD Run() override {
+ 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");
+}
+
+void
+nsPrintEngine::DisconnectPagePrintTimer()
+{
+ if (mPagePrintTimer) {
+ mPagePrintTimer->Disconnect();
+ NS_RELEASE(mPagePrintTimer);
+ }
+}
+
+//---------------------------------------------------------------
+//---------------------------------------------------------------
+//-- Debug helper routines
+//---------------------------------------------------------------
+//---------------------------------------------------------------
+#if defined(XP_WIN) && defined(EXTENDED_DEBUG_PRINTING)
+#include "windows.h"
+#include "process.h"
+#include "direct.h"
+
+#define MY_FINDFIRST(a,b) FindFirstFile(a,b)
+#define MY_FINDNEXT(a,b) FindNextFile(a,b)
+#define ISDIR(a) (a.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+#define MY_FINDCLOSE(a) FindClose(a)
+#define MY_FILENAME(a) a.cFileName
+#define MY_FILESIZE(a) (a.nFileSizeHigh * MAXDWORD) + a.nFileSizeLow
+
+int RemoveFilesInDir(const char * aDir)
+{
+ WIN32_FIND_DATA data_ptr;
+ HANDLE find_handle;
+
+ char path[MAX_PATH];
+
+ strcpy(path, aDir);
+
+ // Append slash to the end of the directory names if not there
+ if (path[strlen(path)-1] != '\\')
+ strcat(path, "\\");
+
+ char findPath[MAX_PATH];
+ strcpy(findPath, path);
+ strcat(findPath, "*.*");
+
+ find_handle = MY_FINDFIRST(findPath, &data_ptr);
+
+ if (find_handle != INVALID_HANDLE_VALUE) {
+ do {
+ if (ISDIR(data_ptr)
+ && (stricmp(MY_FILENAME(data_ptr),"."))
+ && (stricmp(MY_FILENAME(data_ptr),".."))) {
+ // skip
+ }
+ else if (!ISDIR(data_ptr)) {
+ if (!strncmp(MY_FILENAME(data_ptr), "print_dump", 10)) {
+ char fileName[MAX_PATH];
+ strcpy(fileName, aDir);
+ strcat(fileName, "\\");
+ strcat(fileName, MY_FILENAME(data_ptr));
+ printf("Removing %s\n", fileName);
+ remove(fileName);
+ }
+ }
+ } while(MY_FINDNEXT(find_handle,&data_ptr));
+ MY_FINDCLOSE(find_handle);
+ }
+ return TRUE;
+}
+#endif
+
+#ifdef EXTENDED_DEBUG_PRINTING
+
+/** ---------------------------------------------------
+ * Dumps Frames for Printing
+ */
+static void RootFrameList(nsPresContext* aPresContext, FILE* out, 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->PrincipalChildList().FirstChild();
+ while (child != nullptr) {
+ for (int32_t i=0;i<aLevel;i++) {
+ fprintf(out, " ");
+ }
+ nsAutoString tmp;
+ child->GetFrameName(tmp);
+ fputs(NS_LossyConvertUTF16toASCII(tmp).get(), out);
+ bool isSelected;
+ if (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;
+ aDocShell->GetChildCount(&n);
+ for (i = 0; i < n; i++) {
+ nsCOMPtr<nsIDocShellTreeItem> child;
+ aDocShell->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");
+ //RefPtr<gfxContext> renderingContext =
+ // aDC->CreateRenderingContext();
+ 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->PrincipalChildList().FirstChild();
+ }
+ }
+
+ 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, nsACString& aDocStr, nsACString& aURLStr)
+{
+ nsAutoString docTitleStr;
+ nsAutoString docURLStr;
+ nsPrintEngine::GetDisplayTitleAndURL(aPO,
+ docTitleStr, docURLStr,
+ nsPrintEngine::eDocTitleDefURLDoc);
+ aDocStr = NS_ConvertUTF16toUTF8(docTitleStr);
+ aURLStr = NS_ConvertUTF16toUTF8(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()) {
+ nsAutoCString docStr;
+ nsAutoCString urlStr;
+ GetDocTitleAndURL(aPO, docStr, urlStr);
+ DumpLayoutData(docStr.get(), urlStr.get(), aPO->mPresContext, aDC, rootFrame, aPO->mDocShell, fd);
+ }
+ 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..643d9ee4e
--- /dev/null
+++ b/layout/printing/nsPrintEngine.h
@@ -0,0 +1,305 @@
+/* -*- 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 "mozilla/UniquePtr.h"
+
+#include "nsCOMPtr.h"
+
+#include "nsPrintObject.h"
+#include "nsPrintData.h"
+#include "nsFrameList.h"
+#include "nsIFrame.h"
+#include "nsIWebProgress.h"
+#include "mozilla/dom/HTMLCanvasElement.h"
+#include "nsIWebProgressListener.h"
+#include "nsWeakReference.h"
+
+// Interfaces
+#include "nsIObserver.h"
+
+// Classes
+class nsPagePrintTimer;
+class nsIDocShell;
+class nsIDocument;
+class nsIDocumentViewerPrint;
+class nsPrintObject;
+class nsIDocShell;
+class nsIPageSequenceFrame;
+
+//------------------------------------------------------------------------
+// nsPrintEngine Class
+//
+//------------------------------------------------------------------------
+class nsPrintEngine 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,
+ mozIDOMWindowProxy* 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, char16_t*** 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 {
+ eDocTitleDefBlank,
+ eDocTitleDefURLDoc
+ };
+
+ nsPrintEngine();
+
+ void Destroy();
+ void DestroyPrintingData();
+
+ nsresult Initialize(nsIDocumentViewerPrint* aDocViewerPrint,
+ nsIDocShell* 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(nsIDocShell * 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 EllipseLongString(nsAString& aStr, const uint32_t aLen, bool aDoFront);
+ nsresult CheckForPrinters(nsIPrintSettings* aPrintSettings);
+ void CleanupDocTitleArray(char16_t**& aArray, int32_t& aCount);
+
+ bool IsThereARangeSelection(nsPIDOMWindowOuter* aDOMWin);
+
+ void FirePrintingErrorEvent(nsresult aPrintError);
+ //---------------------------------------------------------------------
+
+
+ // Timer Methods
+ nsresult StartPagePrintTimer(nsPrintObject* aPO);
+
+ bool IsWindowsInOurSubTree(nsPIDOMWindowOuter* aDOMWindow);
+ static bool IsParentAFrameSet(nsIDocShell * aParent);
+ bool IsThereAnIFrameSelected(nsIDocShell* aDocShell,
+ nsPIDOMWindowOuter* aDOMWin,
+ bool& aIsParentFrameSet);
+
+ static nsPrintObject* FindPrintObjectByDOMWin(nsPrintObject* aParentObject,
+ nsPIDOMWindowOuter* aDOMWin);
+
+ // get the currently infocus frame for the document viewer
+ already_AddRefed<nsPIDOMWindowOuter> FindFocusedDOMWindow();
+
+ //---------------------------------------------------------------------
+ // Static Methods
+ //---------------------------------------------------------------------
+ static void GetDocumentTitleAndURL(nsIDocument* aDoc,
+ nsAString& aTitle,
+ nsAString& aURLStr);
+ void GetDisplayTitleAndURL(nsPrintObject* aPO,
+ nsAString& aTitle,
+ nsAString& aURLStr,
+ eDocTitleDefault aDefType);
+
+ 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;
+ }
+
+protected:
+ ~nsPrintEngine();
+
+ 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(nsFrameList::Enumerator& aChildFrames,
+ nsIFrame * aParentFrame,
+ nsRect& aRect,
+ nsIFrame *& aStartFrame,
+ nsRect& aStartRect,
+ nsIFrame *& aEndFrame,
+ nsRect& aEndRect);
+
+ static nsresult FindSelectionBounds(nsIFrame * aParentFrame,
+ nsRect& aRect,
+ nsIFrame *& aStartFrame,
+ nsRect& aStartRect,
+ nsIFrame *& aEndFrame,
+ nsRect& aEndRect);
+
+ static nsresult GetPageRangeForSelection(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);
+
+ void DisconnectPagePrintTimer();
+
+ // Static member variables
+ bool mIsCreatingPrintPreview;
+ bool mIsDoingPrinting;
+ bool mIsDoingPrintPreview; // per DocumentViewer
+ bool mProgressDialogIsShown;
+
+ nsCOMPtr<nsIDocumentViewerPrint> mDocViewerPrint;
+ nsWeakPtr mContainer;
+ float mScreenDPI;
+
+ // We are the primary owner of our nsPrintData member vars. These vars
+ // are refcounted so that functions (e.g. nsPrintData methods) can create
+ // temporary owning references when they need to fire a callback that
+ // could conceivably destroy this nsPrintEngine owner object and all its
+ // member-data.
+ RefPtr<nsPrintData> mPrt;
+
+ nsPagePrintTimer* mPagePrintTimer;
+ nsWeakFrame mPageSeqFrame;
+
+ // Print Preview
+ RefPtr<nsPrintData> mPrtPreview;
+ RefPtr<nsPrintData> mOldPrtPreview;
+
+ nsCOMPtr<nsIDocument> mDocument;
+
+ FILE* mDebugFile;
+
+ int32_t mLoadCounter;
+ bool mDidLoadDataForPrinting;
+ bool mIsDestroying;
+ bool mDisallowSelectionPrint;
+
+ 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) = delete;
+};
+
+#endif /* nsPrintEngine_h___ */
diff --git a/layout/printing/nsPrintObject.cpp b/layout/printing/nsPrintObject.cpp
new file mode 100644
index 000000000..9165374c2
--- /dev/null
+++ b/layout/printing/nsPrintObject.cpp
@@ -0,0 +1,113 @@
+/* -*- 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" // for nsAutoScriptBlocker
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsPIDOMWindow.h"
+#include "nsGkAtoms.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIDocShellTreeItem.h"
+#include "nsIBaseWindow.h"
+#include "nsIDocument.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);
+ // 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(aDocShell->ItemType());
+ mDocShell->SetTreeOwner(mTreeOwner);
+ }
+ NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
+
+ // Keep the document related to this docshell alive
+ nsCOMPtr<nsIDOMDocument> dummy = do_GetInterface(mDocShell);
+ mozilla::Unused << dummy;
+
+ 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<nsPIDOMWindowOuter> window = doc->GetWindow();
+ if (window) {
+ mContent = 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..c8b49f4c7
--- /dev/null
+++ b/layout/printing/nsPrintObject.h
@@ -0,0 +1,71 @@
+/* -*- 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 "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;
+
+ RefPtr<nsPresContext> mPresContext;
+ nsCOMPtr<nsIPresShell> mPresShell;
+ RefPtr<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) = delete;
+};
+
+
+
+#endif /* nsPrintObject_h___ */
+
diff --git a/layout/printing/nsPrintPreviewListener.cpp b/layout/printing/nsPrintPreviewListener.cpp
new file mode 100644
index 000000000..5edc0fb90
--- /dev/null
+++ b/layout/printing/nsPrintPreviewListener.cpp
@@ -0,0 +1,212 @@
+/* -*- 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/TextEvents.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/Event.h" // for nsIDOMEvent::InternalDOMEvent()
+#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_ISUPPORTS(nsPrintPreviewListener, nsIDOMEventListener)
+
+
+//
+// nsPrintPreviewListener ctor
+//
+nsPrintPreviewListener::nsPrintPreviewListener(EventTarget* aTarget)
+ : mEventTarget(aTarget)
+{
+ NS_ADDREF_THIS();
+} // ctor
+
+nsPrintPreviewListener::~nsPrintPreviewListener()
+{
+}
+
+//-------------------------------------------------------
+//
+// 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);
+
+ mEventTarget->AddSystemEventListener(NS_LITERAL_STRING("keydown"),
+ 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);
+
+ mEventTarget->RemoveSystemEventListener(NS_LITERAL_STRING("keydown"),
+ this, true);
+ }
+
+ return NS_OK;
+}
+
+//-------------------------------------------------------
+//
+// GetActionForEvent
+//
+// Helper function to let certain key events through
+//
+enum eEventAction {
+ eEventAction_Tab, eEventAction_ShiftTab,
+ eEventAction_Propagate, eEventAction_Suppress,
+ eEventAction_StopPropagation
+};
+
+static eEventAction
+GetActionForEvent(nsIDOMEvent* aEvent)
+{
+ WidgetKeyboardEvent* keyEvent =
+ aEvent->WidgetEventPtr()->AsKeyboardEvent();
+ if (!keyEvent) {
+ return eEventAction_Suppress;
+ }
+
+ if (keyEvent->mFlags.mInSystemGroup) {
+ NS_ASSERTION(keyEvent->mMessage == eKeyDown,
+ "Assuming we're listening only keydown event in system group");
+ return eEventAction_StopPropagation;
+ }
+
+ if (keyEvent->IsAlt() || keyEvent->IsControl() || keyEvent->IsMeta()) {
+ // Don't consume keydown event because following keypress event may be
+ // handled as access key or shortcut key.
+ return (keyEvent->mMessage == eKeyDown) ? eEventAction_StopPropagation :
+ eEventAction_Suppress;
+ }
+
+ static const uint32_t kOKKeyCodes[] = {
+ NS_VK_PAGE_UP, NS_VK_PAGE_DOWN,
+ NS_VK_UP, NS_VK_DOWN,
+ NS_VK_HOME, NS_VK_END
+ };
+
+ if (keyEvent->mKeyCode == NS_VK_TAB) {
+ return keyEvent->IsShift() ? eEventAction_ShiftTab : eEventAction_Tab;
+ }
+
+ if (keyEvent->mCharCode == ' ' || keyEvent->mKeyCode == NS_VK_SPACE) {
+ return eEventAction_Propagate;
+ }
+
+ if (keyEvent->IsShift()) {
+ return eEventAction_Suppress;
+ }
+
+ for (uint32_t i = 0; i < ArrayLength(kOKKeyCodes); ++i) {
+ if (keyEvent->mKeyCode == 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->IsXULElement()) {
+ eEventAction action = ::GetActionForEvent(aEvent);
+ switch (action) {
+ case eEventAction_Tab:
+ case eEventAction_ShiftTab:
+ {
+ nsAutoString eventString;
+ aEvent->GetType(eventString);
+ if (eventString.EqualsLiteral("keydown")) {
+ // Handle tabbing explicitly here since we don't want focus ending up
+ // inside the content document, bug 244128.
+ nsIDocument* doc = content->GetUncomposedDoc();
+ NS_ASSERTION(doc, "no document");
+
+ nsIDocument* parentDoc = doc->GetParentDocument();
+ NS_ASSERTION(parentDoc, "no parent document");
+
+ nsCOMPtr<nsPIDOMWindowOuter> win = 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));
+ }
+ }
+ }
+ MOZ_FALLTHROUGH;
+ case eEventAction_Suppress:
+ aEvent->StopPropagation();
+ aEvent->PreventDefault();
+ break;
+ case eEventAction_StopPropagation:
+ aEvent->StopPropagation();
+ 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..dddb1bed5
--- /dev/null
+++ b/layout/printing/nsPrintPreviewListener.h
@@ -0,0 +1,53 @@
+/* -*- 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"
+// Helper Classes
+#include "nsCOMPtr.h"
+#include "mozilla/Attributes.h"
+
+namespace mozilla {
+namespace dom {
+class EventTarget;
+} // namespace dom
+} // namespace mozilla
+
+//
+// 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 final : public nsIDOMEventListener
+
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIDOMEVENTLISTENER
+
+ explicit nsPrintPreviewListener(mozilla::dom::EventTarget* aTarget);
+
+ // Add/remove the relevant listeners, based on what interfaces
+ // the embedding chrome implements.
+ nsresult AddListeners();
+ nsresult RemoveListeners();
+
+private:
+ ~nsPrintPreviewListener();
+
+ nsCOMPtr<mozilla::dom::EventTarget> mEventTarget;
+
+}; // class nsPrintPreviewListener
+
+
+
+#endif /* nsPrintPreviewListener_h__ */