diff options
Diffstat (limited to 'widget/nsBaseClipboard.cpp')
-rw-r--r-- | widget/nsBaseClipboard.cpp | 302 |
1 files changed, 46 insertions, 256 deletions
diff --git a/widget/nsBaseClipboard.cpp b/widget/nsBaseClipboard.cpp index 9ffc53b4c2..1eb2e8e9e0 100644 --- a/widget/nsBaseClipboard.cpp +++ b/widget/nsBaseClipboard.cpp @@ -7,7 +7,6 @@ #include "ContentAnalysis.h" #include "mozilla/Components.h" -#include "mozilla/contentanalysis/ContentAnalysisIPCTypes.h" #include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/CanonicalBrowsingContext.h" #include "mozilla/dom/Document.h" @@ -19,14 +18,12 @@ #include "mozilla/MoveOnlyFunction.h" #include "mozilla/RefPtr.h" #include "mozilla/Services.h" -#include "mozilla/SpinEventLoopUntil.h" #include "mozilla/StaticPrefs_dom.h" #include "mozilla/StaticPrefs_widget.h" #include "nsContentUtils.h" #include "nsFocusManager.h" #include "nsIClipboardOwner.h" #include "nsIPromptService.h" -#include "nsISupportsPrimitives.h" #include "nsError.h" #include "nsXPCOM.h" @@ -38,6 +35,8 @@ using mozilla::dom::CanonicalBrowsingContext; using mozilla::dom::ClipboardCapabilities; using mozilla::dom::Document; +mozilla::LazyLogModule gWidgetClipboardLog("WidgetClipboard"); + static const int32_t kGetAvailableFlavorsRetryCount = 5; namespace { @@ -211,9 +210,11 @@ NS_IMPL_ISUPPORTS(nsBaseClipboard::AsyncSetClipboardData, nsBaseClipboard::AsyncSetClipboardData::AsyncSetClipboardData( int32_t aClipboardType, nsBaseClipboard* aClipboard, + mozilla::dom::WindowContext* aSettingWindowContext, nsIAsyncClipboardRequestCallback* aCallback) : mClipboardType(aClipboardType), mClipboard(aClipboard), + mWindowContext(aSettingWindowContext), mCallback(aCallback) { MOZ_ASSERT(mClipboard); MOZ_ASSERT( @@ -247,7 +248,8 @@ nsBaseClipboard::AsyncSetClipboardData::SetData(nsITransferable* aTransferable, RefPtr<AsyncSetClipboardData> request = std::move(mClipboard->mPendingWriteRequests[mClipboardType]); - nsresult rv = mClipboard->SetData(aTransferable, aOwner, mClipboardType); + nsresult rv = mClipboard->SetData(aTransferable, aOwner, mClipboardType, + mWindowContext); MaybeNotifyCallback(rv); return rv; @@ -292,7 +294,8 @@ void nsBaseClipboard::RejectPendingAsyncSetDataRequestIfAny( } NS_IMETHODIMP nsBaseClipboard::AsyncSetData( - int32_t aWhichClipboard, nsIAsyncClipboardRequestCallback* aCallback, + int32_t aWhichClipboard, mozilla::dom::WindowContext* aSettingWindowContext, + nsIAsyncClipboardRequestCallback* aCallback, nsIAsyncSetClipboardData** _retval) { MOZ_CLIPBOARD_LOG("%s: clipboard=%d", __FUNCTION__, aWhichClipboard); @@ -308,241 +311,13 @@ NS_IMETHODIMP nsBaseClipboard::AsyncSetData( // Create a new AsyncSetClipboardData. RefPtr<AsyncSetClipboardData> request = - mozilla::MakeRefPtr<AsyncSetClipboardData>(aWhichClipboard, this, - aCallback); + mozilla::MakeRefPtr<AsyncSetClipboardData>( + aWhichClipboard, this, aSettingWindowContext, aCallback); mPendingWriteRequests[aWhichClipboard] = request; request.forget(_retval); return NS_OK; } -namespace { -class SafeContentAnalysisResultCallback final - : public nsIContentAnalysisCallback { - public: - explicit SafeContentAnalysisResultCallback( - std::function<void(RefPtr<nsIContentAnalysisResult>&&)> aResolver) - : mResolver(std::move(aResolver)) {} - void Callback(RefPtr<nsIContentAnalysisResult>&& aResult) { - MOZ_ASSERT(mResolver, "Called SafeContentAnalysisResultCallback twice!"); - if (auto resolver = std::move(mResolver)) { - resolver(std::move(aResult)); - } - } - - NS_IMETHODIMP ContentResult(nsIContentAnalysisResponse* aResponse) override { - using namespace mozilla::contentanalysis; - RefPtr<ContentAnalysisResult> result = - ContentAnalysisResult::FromContentAnalysisResponse(aResponse); - Callback(result); - return NS_OK; - } - - NS_IMETHODIMP Error(nsresult aError) override { - using namespace mozilla::contentanalysis; - Callback(ContentAnalysisResult::FromNoResult( - NoContentAnalysisResult::DENY_DUE_TO_OTHER_ERROR)); - return NS_OK; - } - - NS_DECL_THREADSAFE_ISUPPORTS - private: - // Private destructor to force this to be allocated in a RefPtr, which is - // necessary for safe usage. - ~SafeContentAnalysisResultCallback() { - MOZ_ASSERT(!mResolver, "SafeContentAnalysisResultCallback never called!"); - } - mozilla::MoveOnlyFunction<void(RefPtr<nsIContentAnalysisResult>&&)> mResolver; -}; -NS_IMPL_ISUPPORTS(SafeContentAnalysisResultCallback, - nsIContentAnalysisCallback); -} // namespace - -// Returning: -// - true means a content analysis request was fired -// - false means there is no text data in the transferable -// - NoContentAnalysisResult means there was an error -static mozilla::Result<bool, mozilla::contentanalysis::NoContentAnalysisResult> -CheckClipboardContentAnalysisAsText( - uint64_t aInnerWindowId, SafeContentAnalysisResultCallback* aResolver, - nsIURI* aDocumentURI, nsIContentAnalysis* aContentAnalysis, - nsITransferable* aTextTrans) { - using namespace mozilla::contentanalysis; - - nsCOMPtr<nsISupports> transferData; - if (NS_FAILED(aTextTrans->GetTransferData(kTextMime, - getter_AddRefs(transferData)))) { - return false; - } - nsCOMPtr<nsISupportsString> textData = do_QueryInterface(transferData); - if (MOZ_UNLIKELY(!textData)) { - return false; - } - nsString text; - if (NS_FAILED(textData->GetData(text))) { - return mozilla::Err(NoContentAnalysisResult::DENY_DUE_TO_OTHER_ERROR); - } - if (text.IsEmpty()) { - // Content Analysis doesn't expect to analyze an empty string. - // Just approve it. - return true; - } - RefPtr<mozilla::dom::WindowGlobalParent> window = - mozilla::dom::WindowGlobalParent::GetByInnerWindowId(aInnerWindowId); - if (!window) { - // The window has gone away in the meantime - return mozilla::Err(NoContentAnalysisResult::DENY_DUE_TO_OTHER_ERROR); - } - nsCOMPtr<nsIContentAnalysisRequest> contentAnalysisRequest = - new ContentAnalysisRequest( - nsIContentAnalysisRequest::AnalysisType::eBulkDataEntry, - std::move(text), false, EmptyCString(), aDocumentURI, - nsIContentAnalysisRequest::OperationType::eClipboard, window); - nsresult rv = aContentAnalysis->AnalyzeContentRequestCallback( - contentAnalysisRequest, /* aAutoAcknowledge */ true, aResolver); - if (NS_FAILED(rv)) { - return mozilla::Err(NoContentAnalysisResult::DENY_DUE_TO_OTHER_ERROR); - } - return true; -} - -// Returning: -// - true means a content analysis request was fired -// - false means there is no file data in the transferable -// - NoContentAnalysisResult means there was an error -static mozilla::Result<bool, mozilla::contentanalysis::NoContentAnalysisResult> -CheckClipboardContentAnalysisAsFile( - uint64_t aInnerWindowId, SafeContentAnalysisResultCallback* aResolver, - nsIURI* aDocumentURI, nsIContentAnalysis* aContentAnalysis, - nsITransferable* aFileTrans) { - using namespace mozilla::contentanalysis; - - nsCOMPtr<nsISupports> transferData; - nsresult rv = - aFileTrans->GetTransferData(kFileMime, getter_AddRefs(transferData)); - nsString filePath; - if (NS_SUCCEEDED(rv)) { - if (nsCOMPtr<nsIFile> file = do_QueryInterface(transferData)) { - rv = file->GetPath(filePath); - } else { - MOZ_ASSERT_UNREACHABLE("clipboard data had kFileMime but no nsIFile!"); - return mozilla::Err(NoContentAnalysisResult::DENY_DUE_TO_OTHER_ERROR); - } - } - if (NS_FAILED(rv) || filePath.IsEmpty()) { - return false; - } - RefPtr<mozilla::dom::WindowGlobalParent> window = - mozilla::dom::WindowGlobalParent::GetByInnerWindowId(aInnerWindowId); - if (!window) { - // The window has gone away in the meantime - return mozilla::Err(NoContentAnalysisResult::DENY_DUE_TO_OTHER_ERROR); - } - // Let the content analysis code calculate the digest - nsCOMPtr<nsIContentAnalysisRequest> contentAnalysisRequest = - new ContentAnalysisRequest( - nsIContentAnalysisRequest::AnalysisType::eBulkDataEntry, - std::move(filePath), true, EmptyCString(), aDocumentURI, - nsIContentAnalysisRequest::OperationType::eCustomDisplayString, - window); - rv = aContentAnalysis->AnalyzeContentRequestCallback( - contentAnalysisRequest, - /* aAutoAcknowledge */ true, aResolver); - if (NS_FAILED(rv)) { - return mozilla::Err(NoContentAnalysisResult::DENY_DUE_TO_OTHER_ERROR); - } - return true; -} - -static void CheckClipboardContentAnalysis( - mozilla::dom::WindowGlobalParent* aWindow, nsITransferable* aTransferable, - SafeContentAnalysisResultCallback* aResolver) { - using namespace mozilla::contentanalysis; - - // Content analysis is only needed if an outside webpage has access to - // the data. So, skip content analysis if there is: - // - no associated window (for example, scripted clipboard read by system - // code) - // - the window is a chrome docshell - // - the window is being rendered in the parent process (for example, - // about:support and the like) - if (!aWindow || aWindow->GetBrowsingContext()->IsChrome() || - aWindow->IsInProcess()) { - aResolver->Callback(ContentAnalysisResult::FromNoResult( - NoContentAnalysisResult:: - ALLOW_DUE_TO_CONTEXT_EXEMPT_FROM_CONTENT_ANALYSIS)); - return; - } - nsCOMPtr<nsIContentAnalysis> contentAnalysis = - mozilla::components::nsIContentAnalysis::Service(); - if (!contentAnalysis) { - aResolver->Callback(ContentAnalysisResult::FromNoResult( - NoContentAnalysisResult::DENY_DUE_TO_OTHER_ERROR)); - return; - } - - bool contentAnalysisIsActive; - nsresult rv = contentAnalysis->GetIsActive(&contentAnalysisIsActive); - if (MOZ_LIKELY(NS_FAILED(rv) || !contentAnalysisIsActive)) { - aResolver->Callback(ContentAnalysisResult::FromNoResult( - NoContentAnalysisResult::ALLOW_DUE_TO_CONTENT_ANALYSIS_NOT_ACTIVE)); - return; - } - - nsCOMPtr<nsIURI> currentURI = aWindow->Canonical()->GetDocumentURI(); - uint64_t innerWindowId = aWindow->InnerWindowId(); - nsTArray<nsCString> flavors; - rv = aTransferable->FlavorsTransferableCanExport(flavors); - if (NS_WARN_IF(NS_FAILED(rv))) { - aResolver->Callback(ContentAnalysisResult::FromNoResult( - NoContentAnalysisResult::DENY_DUE_TO_OTHER_ERROR)); - return; - } - bool keepChecking = true; - if (flavors.Contains(kFileMime)) { - auto fileResult = CheckClipboardContentAnalysisAsFile( - innerWindowId, aResolver, currentURI, contentAnalysis, aTransferable); - - if (fileResult.isErr()) { - aResolver->Callback( - ContentAnalysisResult::FromNoResult(fileResult.unwrapErr())); - return; - } - keepChecking = !fileResult.unwrap(); - } - if (keepChecking) { - // Failed to get the clipboard data as a file, so try as text - auto textResult = CheckClipboardContentAnalysisAsText( - innerWindowId, aResolver, currentURI, contentAnalysis, aTransferable); - if (textResult.isErr()) { - aResolver->Callback( - ContentAnalysisResult::FromNoResult(textResult.unwrapErr())); - return; - } - if (!textResult.unwrap()) { - // Couldn't get file or text data from this - aResolver->Callback(ContentAnalysisResult::FromNoResult( - NoContentAnalysisResult::ALLOW_DUE_TO_COULD_NOT_GET_DATA)); - return; - } - } -} - -static bool CheckClipboardContentAnalysisSync( - mozilla::dom::WindowGlobalParent* aWindow, - const nsCOMPtr<nsITransferable>& trans) { - bool requestDone = false; - RefPtr<nsIContentAnalysisResult> result; - auto callback = mozilla::MakeRefPtr<SafeContentAnalysisResultCallback>( - [&requestDone, &result](RefPtr<nsIContentAnalysisResult>&& aResult) { - result = std::move(aResult); - requestDone = true; - }); - CheckClipboardContentAnalysis(aWindow, trans, callback); - mozilla::SpinEventLoopUntil("CheckClipboardContentAnalysisSync"_ns, - [&requestDone]() -> bool { return requestDone; }); - return result->GetShouldAllowContent(); -} - nsBaseClipboard::nsBaseClipboard(const ClipboardCapabilities& aClipboardCaps) : mClipboardCaps(aClipboardCaps) { using mozilla::MakeUnique; @@ -574,9 +349,9 @@ NS_IMPL_ISUPPORTS(nsBaseClipboard, nsIClipboard) * Sets the transferable object * */ -NS_IMETHODIMP nsBaseClipboard::SetData(nsITransferable* aTransferable, - nsIClipboardOwner* aOwner, - int32_t aWhichClipboard) { +NS_IMETHODIMP nsBaseClipboard::SetData( + nsITransferable* aTransferable, nsIClipboardOwner* aOwner, + int32_t aWhichClipboard, mozilla::dom::WindowContext* aWindowContext) { NS_ASSERTION(aTransferable, "clipboard given a null transferable"); MOZ_CLIPBOARD_LOG("%s: clipboard=%d", __FUNCTION__, aWhichClipboard); @@ -627,21 +402,22 @@ NS_IMETHODIMP nsBaseClipboard::SetData(nsITransferable* aTransferable, return result.unwrapErr(); } - clipboardCache->Update(aTransferable, aOwner, result.unwrap()); + clipboardCache->Update(aTransferable, aOwner, result.unwrap(), + aWindowContext + ? mozilla::Some(aWindowContext->InnerWindowId()) + : mozilla::Nothing()); return NS_OK; } nsresult nsBaseClipboard::GetDataFromClipboardCache( nsITransferable* aTransferable, int32_t aClipboardType) { MOZ_ASSERT(aTransferable); - MOZ_ASSERT(nsIClipboard::IsClipboardTypeSupported(aClipboardType)); MOZ_ASSERT(mozilla::StaticPrefs::widget_clipboard_use_cached_data_enabled()); const auto* clipboardCache = GetClipboardCacheIfValid(aClipboardType); if (!clipboardCache) { return NS_ERROR_FAILURE; } - return clipboardCache->GetData(aTransferable); } @@ -671,8 +447,10 @@ NS_IMETHODIMP nsBaseClipboard::GetData( if (NS_SUCCEEDED( GetDataFromClipboardCache(aTransferable, aWhichClipboard))) { // maybe try to fill in more types? Is there a point? - if (!CheckClipboardContentAnalysisSync(aWindowContext->Canonical(), - aTransferable)) { + if (!mozilla::contentanalysis::ContentAnalysis:: + CheckClipboardContentAnalysisSync( + this, aWindowContext->Canonical(), aTransferable, + aWhichClipboard)) { aTransferable->ClearAllData(); return NS_ERROR_CONTENT_BLOCKED; } @@ -686,8 +464,9 @@ NS_IMETHODIMP nsBaseClipboard::GetData( if (NS_FAILED(rv)) { return rv; } - if (!CheckClipboardContentAnalysisSync(aWindowContext->Canonical(), - aTransferable)) { + if (!mozilla::contentanalysis::ContentAnalysis:: + CheckClipboardContentAnalysisSync(this, aWindowContext->Canonical(), + aTransferable, aWhichClipboard)) { aTransferable->ClearAllData(); return NS_ERROR_CONTENT_BLOCKED; } @@ -795,7 +574,7 @@ NS_IMETHODIMP nsBaseClipboard::AsyncGetData( nsCOMPtr<nsITransferable> trans = clipboardCache->GetTransferable(); MOZ_ASSERT(trans); - if (nsCOMPtr<nsIPrincipal> principal = trans->GetRequestingPrincipal()) { + if (nsCOMPtr<nsIPrincipal> principal = trans->GetDataPrincipal()) { if (aRequestingPrincipal->Subsumes(principal)) { MOZ_CLIPBOARD_LOG("%s: native clipboard data is from same-origin page.", __FUNCTION__); @@ -1228,7 +1007,8 @@ NS_IMETHODIMP nsBaseClipboard::AsyncGetClipboardData::GetData( MOZ_ASSERT(mClipboard); auto contentAnalysisCallback = - mozilla::MakeRefPtr<SafeContentAnalysisResultCallback>( + mozilla::MakeRefPtr<mozilla::contentanalysis::ContentAnalysis:: + SafeContentAnalysisResultCallback>( [transferable = nsCOMPtr{aTransferable}, callback = nsCOMPtr{aCallback}]( RefPtr<nsIContentAnalysisResult>&& aResult) { @@ -1249,10 +1029,11 @@ NS_IMETHODIMP nsBaseClipboard::AsyncGetClipboardData::GetData( MOZ_DIAGNOSTIC_ASSERT(clipboardCache->GetSequenceNumber() == mSequenceNumber); if (NS_SUCCEEDED(clipboardCache->GetData(aTransferable))) { - CheckClipboardContentAnalysis(mRequestingWindowContext - ? mRequestingWindowContext->Canonical() - : nullptr, - aTransferable, contentAnalysisCallback); + mozilla::contentanalysis::ContentAnalysis::CheckClipboardContentAnalysis( + mClipboard, + mRequestingWindowContext ? mRequestingWindowContext->Canonical() + : nullptr, + aTransferable, mClipboardType, contentAnalysisCallback); return NS_OK; } @@ -1278,11 +1059,13 @@ NS_IMETHODIMP nsBaseClipboard::AsyncGetClipboardData::GetData( callback->OnComplete(NS_ERROR_FAILURE); return; } - CheckClipboardContentAnalysis( - self->mRequestingWindowContext - ? self->mRequestingWindowContext->Canonical() - : nullptr, - transferable, contentAnalysisCallback); + mozilla::contentanalysis::ContentAnalysis:: + CheckClipboardContentAnalysis( + self->mClipboard, + self->mRequestingWindowContext + ? self->mRequestingWindowContext->Canonical() + : nullptr, + transferable, self->mClipboardType, contentAnalysisCallback); }); return NS_OK; } @@ -1320,6 +1103,13 @@ bool nsBaseClipboard::AsyncGetClipboardData::IsValid() { return true; } +mozilla::Maybe<uint64_t> nsBaseClipboard::GetClipboardCacheInnerWindowId( + int32_t aClipboardType) { + auto* clipboardCache = GetClipboardCacheIfValid(aClipboardType); + return clipboardCache ? clipboardCache->GetInnerWindowId() + : mozilla::Nothing(); +} + nsBaseClipboard::ClipboardCache* nsBaseClipboard::GetClipboardCacheIfValid( int32_t aClipboardType) { MOZ_ASSERT(nsIClipboard::IsClipboardTypeSupported(aClipboardType)); |