diff options
Diffstat (limited to 'docshell/base/CanonicalBrowsingContext.cpp')
-rw-r--r-- | docshell/base/CanonicalBrowsingContext.cpp | 150 |
1 files changed, 138 insertions, 12 deletions
diff --git a/docshell/base/CanonicalBrowsingContext.cpp b/docshell/base/CanonicalBrowsingContext.cpp index 84f2d2960a..4c92988c9b 100644 --- a/docshell/base/CanonicalBrowsingContext.cpp +++ b/docshell/base/CanonicalBrowsingContext.cpp @@ -6,8 +6,10 @@ #include "mozilla/dom/CanonicalBrowsingContext.h" +#include "ContentAnalysis.h" #include "ErrorList.h" #include "mozilla/CheckedInt.h" +#include "mozilla/Components.h" #include "mozilla/ErrorResult.h" #include "mozilla/EventForwards.h" #include "mozilla/AsyncEventDispatcher.h" @@ -47,6 +49,7 @@ #include "nsFrameLoader.h" #include "nsFrameLoaderOwner.h" #include "nsGlobalWindowOuter.h" +#include "nsIContentAnalysis.h" #include "nsIWebBrowserChrome.h" #include "nsIXULRuntime.h" #include "nsNetUtil.h" @@ -668,6 +671,9 @@ CanonicalBrowsingContext::ReplaceLoadingSessionHistoryEntryForLoad( using PrintPromise = CanonicalBrowsingContext::PrintPromise; #ifdef NS_PRINTING +// Clients must call StaticCloneForPrintingCreated or +// NoStaticCloneForPrintingWillBeCreated before the underlying promise can +// resolve. class PrintListenerAdapter final : public nsIWebProgressListener { public: explicit PrintListenerAdapter(PrintPromise::Private* aPromise) @@ -678,10 +684,14 @@ class PrintListenerAdapter final : public nsIWebProgressListener { // NS_DECL_NSIWEBPROGRESSLISTENER NS_IMETHOD OnStateChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest, uint32_t aStateFlags, nsresult aStatus) override { + MOZ_ASSERT(NS_IsMainThread()); if (aStateFlags & nsIWebProgressListener::STATE_STOP && aStateFlags & nsIWebProgressListener::STATE_IS_DOCUMENT && mPromise) { - mPromise->Resolve(true, __func__); - mPromise = nullptr; + mPrintJobFinished = true; + if (mHaveSetBrowsingContext) { + mPromise->Resolve(mClonedStaticBrowsingContext, __func__); + mPromise = nullptr; + } } return NS_OK; } @@ -716,10 +726,28 @@ class PrintListenerAdapter final : public nsIWebProgressListener { return NS_OK; } + void StaticCloneForPrintingCreated( + MaybeDiscardedBrowsingContext&& aClonedStaticBrowsingContext) { + MOZ_ASSERT(NS_IsMainThread()); + mClonedStaticBrowsingContext = std::move(aClonedStaticBrowsingContext); + mHaveSetBrowsingContext = true; + if (mPrintJobFinished && mPromise) { + mPromise->Resolve(mClonedStaticBrowsingContext, __func__); + mPromise = nullptr; + } + } + + void NoStaticCloneForPrintingWillBeCreated() { + StaticCloneForPrintingCreated(nullptr); + } + private: ~PrintListenerAdapter() = default; RefPtr<PrintPromise::Private> mPromise; + MaybeDiscardedBrowsingContext mClonedStaticBrowsingContext = nullptr; + bool mHaveSetBrowsingContext = false; + bool mPrintJobFinished = false; }; NS_IMPL_ISUPPORTS(PrintListenerAdapter, nsIWebProgressListener) @@ -735,7 +763,9 @@ already_AddRefed<Promise> CanonicalBrowsingContext::PrintJS( Print(aPrintSettings) ->Then( GetCurrentSerialEventTarget(), __func__, - [promise](bool) { promise->MaybeResolveWithUndefined(); }, + [promise](MaybeDiscardedBrowsingContext) { + promise->MaybeResolveWithUndefined(); + }, [promise](nsresult aResult) { promise->MaybeReject(aResult); }); return promise.forget(); } @@ -745,7 +775,72 @@ RefPtr<PrintPromise> CanonicalBrowsingContext::Print( #ifndef NS_PRINTING return PrintPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, __func__); #else +// Content analysis is not supported on non-Windows platforms. +# if defined(XP_WIN) + bool needContentAnalysis = false; + nsCOMPtr<nsIContentAnalysis> contentAnalysis = + mozilla::components::nsIContentAnalysis::Service(); + Unused << NS_WARN_IF(!contentAnalysis); + if (contentAnalysis) { + nsresult rv = contentAnalysis->GetIsActive(&needContentAnalysis); + Unused << NS_WARN_IF(NS_FAILED(rv)); + } + if (needContentAnalysis) { + auto done = MakeRefPtr<PrintPromise::Private>(__func__); + contentanalysis::ContentAnalysis::PrintToPDFToDetermineIfPrintAllowed( + this, aPrintSettings) + ->Then( + GetCurrentSerialEventTarget(), __func__, + [done, aPrintSettings = RefPtr{aPrintSettings}, + self = RefPtr{this}]( + contentanalysis::ContentAnalysis::PrintAllowedResult aResponse) + MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA mutable { + if (aResponse.mAllowed) { + self->PrintWithNoContentAnalysis( + aPrintSettings, false, + aResponse.mCachedStaticDocumentBrowsingContext) + ->ChainTo(done.forget(), __func__); + } else { + // Since we are not doing the second print in this case, + // release the clone that is no longer needed. + self->ReleaseClonedPrint( + aResponse.mCachedStaticDocumentBrowsingContext); + done->Reject(NS_ERROR_CONTENT_BLOCKED, __func__); + } + }, + [done, self = RefPtr{this}]( + contentanalysis::ContentAnalysis::PrintAllowedError + aErrorResponse) MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA { + // Since we are not doing the second print in this case, release + // the clone that is no longer needed. + self->ReleaseClonedPrint( + aErrorResponse.mCachedStaticDocumentBrowsingContext); + done->Reject(aErrorResponse.mError, __func__); + }); + return done; + } +# endif + return PrintWithNoContentAnalysis(aPrintSettings, false, nullptr); +#endif +} +void CanonicalBrowsingContext::ReleaseClonedPrint( + const MaybeDiscardedBrowsingContext& aClonedStaticBrowsingContext) { +#ifdef NS_PRINTING + auto* browserParent = GetBrowserParent(); + if (NS_WARN_IF(!browserParent)) { + return; + } + Unused << browserParent->SendDestroyPrintClone(aClonedStaticBrowsingContext); +#endif +} + +RefPtr<PrintPromise> CanonicalBrowsingContext::PrintWithNoContentAnalysis( + nsIPrintSettings* aPrintSettings, bool aForceStaticDocument, + const MaybeDiscardedBrowsingContext& aCachedStaticDocument) { +#ifndef NS_PRINTING + return PrintPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, __func__); +#else auto promise = MakeRefPtr<PrintPromise::Private>(__func__); auto listener = MakeRefPtr<PrintListenerAdapter>(promise); if (IsInProcess()) { @@ -757,12 +852,14 @@ RefPtr<PrintPromise> CanonicalBrowsingContext::Print( } ErrorResult rv; + listener->NoStaticCloneForPrintingWillBeCreated(); outerWindow->Print(aPrintSettings, /* aRemotePrintJob = */ nullptr, listener, /* aDocShellToCloneInto = */ nullptr, nsGlobalWindowOuter::IsPreview::No, nsGlobalWindowOuter::IsForWindowDotPrint::No, - /* aPrintPreviewCallback = */ nullptr, rv); + /* aPrintPreviewCallback = */ nullptr, + /* aCachedBrowsingContext = */ nullptr, rv); if (rv.Failed()) { promise->Reject(rv.StealNSResult(), __func__); } @@ -805,12 +902,31 @@ RefPtr<PrintPromise> CanonicalBrowsingContext::Print( printData.remotePrintJob() = browserParent->Manager()->SendPRemotePrintJobConstructor(remotePrintJob); - if (listener) { - remotePrintJob->RegisterListener(listener); - } + remotePrintJob->RegisterListener(listener); - if (NS_WARN_IF(!browserParent->SendPrint(this, printData))) { - promise->Reject(NS_ERROR_FAILURE, __func__); + if (!aCachedStaticDocument.IsNullOrDiscarded()) { + // There is no cloned static browsing context that + // SendPrintClonedPage() will return, so indicate this + // so listener can resolve its promise. + listener->NoStaticCloneForPrintingWillBeCreated(); + if (NS_WARN_IF(!browserParent->SendPrintClonedPage( + this, printData, aCachedStaticDocument))) { + promise->Reject(NS_ERROR_FAILURE, __func__); + } + } else { + RefPtr<PBrowserParent::PrintPromise> printPromise = + browserParent->SendPrint(this, printData, aForceStaticDocument); + printPromise->Then( + GetMainThreadSerialEventTarget(), __func__, + [listener](MaybeDiscardedBrowsingContext cachedStaticDocument) { + // promise will get resolved by the listener + listener->StaticCloneForPrintingCreated( + std::move(cachedStaticDocument)); + }, + [promise](ResponseRejectReason reason) { + NS_WARNING("SendPrint() failed"); + promise->Reject(NS_ERROR_FAILURE, __func__); + }); } return promise.forget(); #endif @@ -2245,9 +2361,19 @@ bool CanonicalBrowsingContext::SupportsLoadingInParent( return false; } } - // If the current document has a beforeunload listener, then we need to - // start the load in that process after we fire the event. - if (global->HasBeforeUnload()) { + + // If unloading the current document will cause a beforeunload listener to + // run, then we need to start the load in that process after we fire the + // event. + if (PreOrderWalkFlag([&](BrowsingContext* aBC) { + WindowContext* wc = aBC->GetCurrentWindowContext(); + if (wc && wc->HasBeforeUnload()) { + // We can stop as soon as we know at least one beforeunload listener + // exists. + return WalkFlag::Stop; + } + return WalkFlag::Next; + }) == WalkFlag::Stop) { return false; } |