diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /layout/printing | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | uxp-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz |
Add m-esr52 at 52.6.0
Diffstat (limited to 'layout/printing')
26 files changed, 6418 insertions, 0 deletions
diff --git a/layout/printing/PrintTranslator.cpp b/layout/printing/PrintTranslator.cpp new file mode 100644 index 0000000000..0bda505bae --- /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 0000000000..92d8bbed2b --- /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 0000000000..37d05ff96c --- /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 0000000000..2a47a607c2 --- /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 0000000000..d281427d98 --- /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 0000000000..21e163530a --- /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 0000000000..2af8d84766 --- /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 0000000000..f6c1de8957 --- /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 0000000000..ffc3c24553 --- /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 0000000000..a316815ea2 --- /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 0000000000..2f4dbd56e4 --- /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 0000000000..a96cc7eaac --- /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 0000000000..1a82d7cdb3 --- /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 0000000000..5d42920eb0 --- /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 0000000000..2943912731 --- /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 0000000000..9fef4b59df --- /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 0000000000..b56569a9a9 --- /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 0000000000..0137514da9 --- /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 0000000000..1914f779b3 --- /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 0000000000..50f8c7a867 --- /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 0000000000..f2db53250e --- /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 0000000000..643d9ee4ec --- /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 0000000000..9165374c25 --- /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 0000000000..c8b49f4c7f --- /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 0000000000..5edc0fb903 --- /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 0000000000..dddb1bed5a --- /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__ */ |