diff --git a/browser/base/content/nsContextMenu.js b/browser/base/content/nsContextMenu.js index 9769552dce50..926b346321e4 100644 --- a/browser/base/content/nsContextMenu.js +++ b/browser/base/content/nsContextMenu.js @@ -2175,7 +2175,10 @@ class nsContextMenu { var clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService( Ci.nsIClipboardHelper ); - clipboard.copyString(addresses); + clipboard.copyString( + addresses, + this.actor.manager.browsingContext.currentWindowGlobal + ); } // Extract phone and put it on clipboard @@ -2195,7 +2198,10 @@ class nsContextMenu { var clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService( Ci.nsIClipboardHelper ); - clipboard.copyString(phone); + clipboard.copyString( + phone, + this.actor.manager.browsingContext.currentWindowGlobal + ); } copyLink() { @@ -2204,7 +2210,10 @@ class nsContextMenu { var clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService( Ci.nsIClipboardHelper ); - clipboard.copyString(linkURL); + clipboard.copyString( + linkURL, + this.actor.manager.browsingContext.currentWindowGlobal + ); } /** @@ -2220,7 +2229,10 @@ class nsContextMenu { let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService( Ci.nsIClipboardHelper ); - clipboard.copyString(strippedLinkURL); + clipboard.copyString( + strippedLinkURL, + this.actor.manager.browsingContext.currentWindowGlobal + ); } } @@ -2458,7 +2470,10 @@ class nsContextMenu { var clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService( Ci.nsIClipboardHelper ); - clipboard.copyString(this.originalMediaURL); + clipboard.copyString( + this.originalMediaURL, + this.actor.manager.browsingContext.currentWindowGlobal + ); } getImageText() { diff --git a/browser/components/aboutlogins/AboutLoginsChild.sys.mjs b/browser/components/aboutlogins/AboutLoginsChild.sys.mjs index 3fcdf77923e0..c7059d8f40e5 100644 --- a/browser/components/aboutlogins/AboutLoginsChild.sys.mjs +++ b/browser/components/aboutlogins/AboutLoginsChild.sys.mjs @@ -160,7 +160,11 @@ export class AboutLoginsChild extends JSWindowActorChild { } #aboutLoginsCopyLoginDetail(detail) { - lazy.ClipboardHelper.copyString(detail, lazy.ClipboardHelper.Sensitive); + lazy.ClipboardHelper.copyString( + detail, + this.windowContext, + lazy.ClipboardHelper.Sensitive + ); } #aboutLoginsCreateLogin(login) { diff --git a/browser/components/enterprisepolicies/Policies.sys.mjs b/browser/components/enterprisepolicies/Policies.sys.mjs index fd15c244cc4d..aa2a3d26e168 100644 --- a/browser/components/enterprisepolicies/Policies.sys.mjs +++ b/browser/components/enterprisepolicies/Policies.sys.mjs @@ -553,6 +553,7 @@ export var Policies = { ["IsPerUser", "is_per_user"], ["ShowBlockedResult", "show_blocked_result"], ["DefaultAllow", "default_allow"], + ["BypassForSameTabOperations", "bypass_for_same_tab_operations"], ]; for (let pref of boolPrefs) { if (pref[0] in param) { diff --git a/browser/components/enterprisepolicies/schemas/policies-schema.json b/browser/components/enterprisepolicies/schemas/policies-schema.json index 3c578f2c4b00..fee0e7ba5263 100644 --- a/browser/components/enterprisepolicies/schemas/policies-schema.json +++ b/browser/components/enterprisepolicies/schemas/policies-schema.json @@ -267,6 +267,9 @@ }, "DefaultAllow": { "type": "boolean" + }, + "BypassForSameTabOperations": { + "type": "boolean" } } }, diff --git a/dom/base/nsCopySupport.cpp b/dom/base/nsCopySupport.cpp index 15c0cf4cf0db..1fb9aa2d65b8 100644 --- a/dom/base/nsCopySupport.cpp +++ b/dom/base/nsCopySupport.cpp @@ -332,7 +332,8 @@ static nsresult PutToClipboard( rv = CreateTransferable(aEncodedDocumentWithContext, aDocument, transferable); NS_ENSURE_SUCCESS(rv, rv); - rv = clipboard->SetData(transferable, nullptr, aClipboardID); + rv = clipboard->SetData(transferable, nullptr, aClipboardID, + aDocument.GetWindowContext()); NS_ENSURE_SUCCESS(rv, rv); return rv; @@ -455,9 +456,9 @@ nsresult nsCopySupport::GetContents(const nsACString& aMimeType, return docEncoder->EncodeToString(outdata); } -nsresult nsCopySupport::ImageCopy(nsIImageLoadingContent* aImageElement, - nsILoadContext* aLoadContext, - int32_t aCopyFlags) { +nsresult nsCopySupport::ImageCopy( + nsIImageLoadingContent* aImageElement, nsILoadContext* aLoadContext, + int32_t aCopyFlags, mozilla::dom::WindowContext* aSettingWindowContext) { nsresult rv; nsCOMPtr imageNode = do_QueryInterface(aImageElement, &rv); @@ -527,11 +528,13 @@ nsresult nsCopySupport::ImageCopy(nsIImageLoadingContent* aImageElement, // check whether the system supports the selection clipboard or not. if (clipboard->IsClipboardTypeSupported(nsIClipboard::kSelectionClipboard)) { // put the transferable on the clipboard - rv = clipboard->SetData(trans, nullptr, nsIClipboard::kSelectionClipboard); + rv = clipboard->SetData(trans, nullptr, nsIClipboard::kSelectionClipboard, + aSettingWindowContext); NS_ENSURE_SUCCESS(rv, rv); } - return clipboard->SetData(trans, nullptr, nsIClipboard::kGlobalClipboard); + return clipboard->SetData(trans, nullptr, nsIClipboard::kGlobalClipboard, + aSettingWindowContext); } static nsresult AppendString(nsITransferable* aTransferable, @@ -928,7 +931,12 @@ bool nsCopySupport::FireClipboardEvent(EventMessage aEventMessage, NS_ENSURE_TRUE(transferable, false); // put the transferable on the clipboard - nsresult rv = clipboard->SetData(transferable, nullptr, aClipboardType); + WindowContext* settingWindowContext = nullptr; + if (aPresShell && aPresShell->GetDocument()) { + settingWindowContext = aPresShell->GetDocument()->GetWindowContext(); + } + nsresult rv = clipboard->SetData(transferable, nullptr, aClipboardType, + settingWindowContext); if (NS_FAILED(rv)) { return false; } diff --git a/dom/base/nsCopySupport.h b/dom/base/nsCopySupport.h index a6b4b5e78313..5552e1a6074e 100644 --- a/dom/base/nsCopySupport.h +++ b/dom/base/nsCopySupport.h @@ -23,6 +23,7 @@ class PresShell; namespace dom { class Document; class Selection; +class WindowContext; } // namespace dom } // namespace mozilla @@ -46,7 +47,8 @@ class nsCopySupport { mozilla::dom::Document* aDoc, nsAString& outdata); static nsresult ImageCopy(nsIImageLoadingContent* aImageElement, - nsILoadContext* aLoadContext, int32_t aCopyFlags); + nsILoadContext* aLoadContext, int32_t aCopyFlags, + mozilla::dom::WindowContext* aSettingWindowContext); // Get the selection as a transferable. // @param aSelection Can be nullptr. diff --git a/dom/events/Clipboard.cpp b/dom/events/Clipboard.cpp index b797f9396155..1e6ae0d1b9c0 100644 --- a/dom/events/Clipboard.cpp +++ b/dom/events/Clipboard.cpp @@ -731,7 +731,8 @@ already_AddRefed Clipboard::Write( RefPtr callback = MakeRefPtr(p, aData[0]); nsresult rv = clipboard->AsyncSetData(nsIClipboard::kGlobalClipboard, - callback, getter_AddRefs(request)); + owner->GetWindowContext(), callback, + getter_AddRefs(request)); if (NS_FAILED(rv)) { p->MaybeReject(rv); return p.forget(); diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 412b7e879664..d14d0035ebc5 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -3409,7 +3409,8 @@ void ContentParent::OnVarChanged(const GfxVarUpdate& aVar) { } mozilla::ipc::IPCResult ContentParent::RecvSetClipboard( - const IPCTransferable& aTransferable, const int32_t& aWhichClipboard) { + const IPCTransferable& aTransferable, const int32_t& aWhichClipboard, + const MaybeDiscarded& aRequestingWindowContext) { // aRequestingPrincipal is allowed to be nullptr here. if (!ValidatePrincipal(aTransferable.requestingPrincipal(), @@ -3434,7 +3435,12 @@ mozilla::ipc::IPCResult ContentParent::RecvSetClipboard( true /* aFilterUnknownFlavors */); NS_ENSURE_SUCCESS(rv, IPC_OK()); - clipboard->SetData(trans, nullptr, aWhichClipboard); + // OK if this is null + RefPtr window; + if (!aRequestingWindowContext.IsDiscarded()) { + window = aRequestingWindowContext.get_canonical(); + } + clipboard->SetData(trans, nullptr, aWhichClipboard, window); return IPC_OK(); } @@ -3682,10 +3688,15 @@ mozilla::ipc::IPCResult ContentParent::RecvGetClipboardDataSnapshotSync( already_AddRefed ContentParent::AllocPClipboardWriteRequestParent( - const int32_t& aClipboardType) { + const int32_t& aClipboardType, + const MaybeDiscarded& aSettingWindowContext) { + WindowContext* settingWindowContext = nullptr; + if (!aSettingWindowContext.IsDiscarded()) { + settingWindowContext = aSettingWindowContext.get(); + } RefPtr request = MakeAndAddRef(this); - request->Init(aClipboardType); + request->Init(aClipboardType, settingWindowContext); return request.forget(); } diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index 31cfa6de888a..4646dc795b37 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -949,8 +949,9 @@ class ContentParent final : public PContentParent, PBrowserParent* aBrowser, const MaybeDiscarded& aContext); - mozilla::ipc::IPCResult RecvSetClipboard(const IPCTransferable& aTransferable, - const int32_t& aWhichClipboard); + mozilla::ipc::IPCResult RecvSetClipboard( + const IPCTransferable& aTransferable, const int32_t& aWhichClipboard, + const MaybeDiscarded& aRequestingWindowContext); mozilla::ipc::IPCResult RecvGetClipboard( nsTArray&& aTypes, const int32_t& aWhichClipboard, @@ -975,7 +976,9 @@ class ContentParent final : public PContentParent, ClipboardReadRequestOrError* aRequestOrError); already_AddRefed - AllocPClipboardWriteRequestParent(const int32_t& aClipboardType); + AllocPClipboardWriteRequestParent( + const int32_t& aClipboardType, + const MaybeDiscarded& aSettingWindowContext); mozilla::ipc::IPCResult RecvGetIconForExtension(const nsACString& aFileExt, const uint32_t& aIconSize, diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index 67c4c298e496..f9c9e2ea8d9f 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -1219,7 +1219,7 @@ parent: // Places the items within dataTransfer on the clipboard. async SetClipboard(IPCTransferable aTransferable, - int32_t aWhichClipboard); + int32_t aWhichClipboard, MaybeDiscardedWindowContext aRequestingWindowContext); // Given a list of supported types, returns the clipboard data for the // first type that matches. @@ -1252,8 +1252,12 @@ parent: * and that the data will be sent over another IPC message once it is ready. * @param aClipboardType * The clipboard type defined in nsIClipboard. + * @param aSettingWindowContext + * The window context that is setting the clipboard, if any. This is used + * to possibly bypass Content Analysis if a set clipboard and get clipboard + * operation are done on the same page. */ - async PClipboardWriteRequest(int32_t aClipboardType); + async PClipboardWriteRequest(int32_t aClipboardType, MaybeDiscardedWindowContext aSettingWindowContext); sync GetIconForExtension(nsCString aFileExt, uint32_t aIconSize) returns (uint8_t[] bits); diff --git a/gfx/thebes/gfxUtils.cpp b/gfx/thebes/gfxUtils.cpp index ad3ab14f8b92..98e68ae86255 100644 --- a/gfx/thebes/gfxUtils.cpp +++ b/gfx/thebes/gfxUtils.cpp @@ -1265,7 +1265,7 @@ nsresult gfxUtils::EncodeSourceSurface(SourceSurface* aSurface, nsCOMPtr clipboard( do_GetService("@mozilla.org/widget/clipboardhelper;1", &rv)); if (clipboard) { - clipboard->CopyString(NS_ConvertASCIItoUTF16(dataURI)); + clipboard->CopyString(NS_ConvertASCIItoUTF16(dataURI), nullptr); } } return NS_OK; diff --git a/layout/base/nsDocumentViewer.cpp b/layout/base/nsDocumentViewer.cpp index 8ae0e001b87f..bbd43a830e37 100644 --- a/layout/base/nsDocumentViewer.cpp +++ b/layout/base/nsDocumentViewer.cpp @@ -2426,7 +2426,7 @@ NS_IMETHODIMP nsDocumentViewer::CopyLinkLocation() { NS_ENSURE_SUCCESS(rv, rv); // copy the href onto the clipboard - return clipboard->CopyString(locationText); + return clipboard->CopyString(locationText, mDocument->GetWindowContext()); } NS_IMETHODIMP nsDocumentViewer::CopyImage(int32_t aCopyFlags) { @@ -2436,7 +2436,8 @@ NS_IMETHODIMP nsDocumentViewer::CopyImage(int32_t aCopyFlags) { NS_ENSURE_TRUE(node, NS_ERROR_FAILURE); nsCOMPtr loadContext(mContainer); - return nsCopySupport::ImageCopy(node, loadContext, aCopyFlags); + return nsCopySupport::ImageCopy(node, loadContext, aCopyFlags, + mDocument->GetWindowContext()); } NS_IMETHODIMP nsDocumentViewer::GetCopyable(bool* aCopyable) { diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml index 0a3e72a672e5..53d8ab0b9e32 100644 --- a/modules/libpref/init/StaticPrefList.yaml +++ b/modules/libpref/init/StaticPrefList.yaml @@ -1211,6 +1211,13 @@ value: true mirror: always +# Should Firefox bypass content analysis for pastes and drags whose source +# is the same tab? +- name: browser.contentanalysis.bypass_for_same_tab_operations + type: bool + value: false + mirror: always + # Content blocking for Enhanced Tracking Protection - name: browser.contentblocking.database.enabled type: bool diff --git a/toolkit/components/contentanalysis/ContentAnalysis.cpp b/toolkit/components/contentanalysis/ContentAnalysis.cpp index 2977072984e8..58f91e2f6aa2 100644 --- a/toolkit/components/contentanalysis/ContentAnalysis.cpp +++ b/toolkit/components/contentanalysis/ContentAnalysis.cpp @@ -816,6 +816,7 @@ NS_IMETHODIMP ContentAnalysisResult::GetShouldAllowContent( ALLOW_DUE_TO_CONTENT_ANALYSIS_NOT_ACTIVE || result == NoContentAnalysisResult:: ALLOW_DUE_TO_CONTEXT_EXEMPT_FROM_CONTENT_ANALYSIS || + result == NoContentAnalysisResult::ALLOW_DUE_TO_SAME_TAB_SOURCE || result == NoContentAnalysisResult::ALLOW_DUE_TO_COULD_NOT_GET_DATA; } } else { diff --git a/toolkit/components/contentanalysis/ContentAnalysisIPCTypes.h b/toolkit/components/contentanalysis/ContentAnalysisIPCTypes.h index a5540362570c..48e650d778b3 100644 --- a/toolkit/components/contentanalysis/ContentAnalysisIPCTypes.h +++ b/toolkit/components/contentanalysis/ContentAnalysisIPCTypes.h @@ -19,6 +19,7 @@ namespace contentanalysis { enum class NoContentAnalysisResult : uint8_t { ALLOW_DUE_TO_CONTENT_ANALYSIS_NOT_ACTIVE, ALLOW_DUE_TO_CONTEXT_EXEMPT_FROM_CONTENT_ANALYSIS, + ALLOW_DUE_TO_SAME_TAB_SOURCE, ALLOW_DUE_TO_COULD_NOT_GET_DATA, DENY_DUE_TO_CANCELED, DENY_DUE_TO_INVALID_JSON_RESPONSE, diff --git a/toolkit/components/contentanalysis/tests/browser/browser_content_analysis_policies.js b/toolkit/components/contentanalysis/tests/browser/browser_content_analysis_policies.js index b226c0a37ae3..18cb685d1126 100644 --- a/toolkit/components/contentanalysis/tests/browser/browser_content_analysis_policies.js +++ b/toolkit/components/contentanalysis/tests/browser/browser_content_analysis_policies.js @@ -26,6 +26,7 @@ const kClientSignaturePref = "client_signature"; const kPerUserPref = "is_per_user"; const kShowBlockedPref = "show_blocked_result"; const kDefaultAllowPref = "default_allow"; +const kBypassForSameTabOperationsPref = "bypass_for_same_tab_operations"; const ca = Cc["@mozilla.org/contentanalysis;1"].getService( Ci.nsIContentAnalysis @@ -88,6 +89,7 @@ add_task(async function test_ca_enterprise_config() { IsPerUser: true, ShowBlockedResult: false, DefaultAllow: true, + BypassForSameTabOperations: true, }, }, }); @@ -139,6 +141,13 @@ add_task(async function test_ca_enterprise_config() { true, "default allow match" ); + is( + Services.prefs.getBoolPref( + "browser.contentanalysis." + kBypassForSameTabOperationsPref + ), + true, + "bypass for same tab operations match" + ); PoliciesPrefTracker.stop(); }); diff --git a/widget/ClipboardWriteRequestParent.cpp b/widget/ClipboardWriteRequestParent.cpp index 751befbe050a..7f49ebe2e1cd 100644 --- a/widget/ClipboardWriteRequestParent.cpp +++ b/widget/ClipboardWriteRequestParent.cpp @@ -27,7 +27,9 @@ ClipboardWriteRequestParent::ClipboardWriteRequestParent( ClipboardWriteRequestParent::~ClipboardWriteRequestParent() = default; -nsresult ClipboardWriteRequestParent::Init(const int32_t& aClipboardType) { +nsresult ClipboardWriteRequestParent::Init( + const int32_t& aClipboardType, + mozilla::dom::WindowContext* aSettingWindowContext) { nsCOMPtr clipboard(do_GetService(kCClipboardCID)); if (!clipboard) { Unused << PClipboardWriteRequestParent::Send__delete__(this, @@ -35,8 +37,9 @@ nsresult ClipboardWriteRequestParent::Init(const int32_t& aClipboardType) { return NS_ERROR_FAILURE; } - nsresult rv = clipboard->AsyncSetData(aClipboardType, this, - getter_AddRefs(mAsyncSetClipboardData)); + nsresult rv = + clipboard->AsyncSetData(aClipboardType, aSettingWindowContext, this, + getter_AddRefs(mAsyncSetClipboardData)); if (NS_FAILED(rv)) { Unused << PClipboardWriteRequestParent::Send__delete__(this, rv); return rv; diff --git a/widget/ClipboardWriteRequestParent.h b/widget/ClipboardWriteRequestParent.h index 6027661bdbe0..73dcc98192d4 100644 --- a/widget/ClipboardWriteRequestParent.h +++ b/widget/ClipboardWriteRequestParent.h @@ -27,7 +27,8 @@ class ClipboardWriteRequestParent final explicit ClipboardWriteRequestParent(ContentParent* aManager); - nsresult Init(const int32_t& aClipboardType); + nsresult Init(const int32_t& aClipboardType, + mozilla::dom::WindowContext* aSettingWindowContext); IPCResult RecvSetData(const IPCTransferable& aTransferable); IPCResult Recv__delete__(nsresult aReason); diff --git a/widget/nsBaseClipboard.cpp b/widget/nsBaseClipboard.cpp index 1a176000a965..f3525aaead69 100644 --- a/widget/nsBaseClipboard.cpp +++ b/widget/nsBaseClipboard.cpp @@ -211,9 +211,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 +249,8 @@ nsBaseClipboard::AsyncSetClipboardData::SetData(nsITransferable* aTransferable, RefPtr 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 +295,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,8 +312,8 @@ NS_IMETHODIMP nsBaseClipboard::AsyncSetData( // Create a new AsyncSetClipboardData. RefPtr request = - mozilla::MakeRefPtr(aWhichClipboard, this, - aCallback); + mozilla::MakeRefPtr( + aWhichClipboard, this, aSettingWindowContext, aCallback); mPendingWriteRequests[aWhichClipboard] = request; request.forget(_retval); return NS_OK; @@ -408,7 +412,7 @@ nsBaseClipboard::CheckClipboardContentAnalysisAsFile( void nsBaseClipboard::CheckClipboardContentAnalysis( mozilla::dom::WindowGlobalParent* aWindow, nsITransferable* aTransferable, - SafeContentAnalysisResultCallback* aResolver) { + int32_t aClipboardType, SafeContentAnalysisResultCallback* aResolver) { using namespace mozilla::contentanalysis; // Content analysis is only needed if an outside webpage has access to @@ -441,8 +445,24 @@ void nsBaseClipboard::CheckClipboardContentAnalysis( return; } - nsCOMPtr currentURI = aWindow->Canonical()->GetDocumentURI(); uint64_t innerWindowId = aWindow->InnerWindowId(); + if (mozilla::StaticPrefs:: + browser_contentanalysis_bypass_for_same_tab_operations()) { + const auto* clipboardCache = GetClipboardCacheIfValid(aClipboardType); + if (clipboardCache) { + if (clipboardCache->GetInnerWindowId().isSome() && + *(clipboardCache->GetInnerWindowId()) == innerWindowId) { + // If the same page copied this data to the clipboard (and the above + // preference is set) we can skip content analysis and immediately allow + // this. + aResolver->Callback(ContentAnalysisResult::FromNoResult( + NoContentAnalysisResult::ALLOW_DUE_TO_SAME_TAB_SOURCE)); + return; + } + } + } + + nsCOMPtr currentURI = aWindow->Canonical()->GetDocumentURI(); nsTArray flavors; rv = aTransferable->FlavorsTransferableCanExport(flavors); if (NS_WARN_IF(NS_FAILED(rv))) { @@ -482,7 +502,7 @@ void nsBaseClipboard::CheckClipboardContentAnalysis( bool nsBaseClipboard::CheckClipboardContentAnalysisSync( mozilla::dom::WindowGlobalParent* aWindow, - const nsCOMPtr& trans) { + const nsCOMPtr& trans, int32_t aClipboardType) { bool requestDone = false; RefPtr result; auto callback = mozilla::MakeRefPtr( @@ -490,7 +510,7 @@ bool nsBaseClipboard::CheckClipboardContentAnalysisSync( result = std::move(aResult); requestDone = true; }); - CheckClipboardContentAnalysis(aWindow, trans, callback); + CheckClipboardContentAnalysis(aWindow, trans, aClipboardType, callback); mozilla::SpinEventLoopUntil("CheckClipboardContentAnalysisSync"_ns, [&requestDone]() -> bool { return requestDone; }); return result->GetShouldAllowContent(); @@ -527,9 +547,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); @@ -580,7 +600,10 @@ 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; } @@ -623,7 +646,7 @@ NS_IMETHODIMP nsBaseClipboard::GetData( GetDataFromClipboardCache(aTransferable, aWhichClipboard))) { // maybe try to fill in more types? Is there a point? if (!CheckClipboardContentAnalysisSync(aWindowContext->Canonical(), - aTransferable)) { + aTransferable, aWhichClipboard)) { aTransferable->ClearAllData(); return NS_ERROR_CONTENT_BLOCKED; } @@ -638,7 +661,7 @@ NS_IMETHODIMP nsBaseClipboard::GetData( return rv; } if (!CheckClipboardContentAnalysisSync(aWindowContext->Canonical(), - aTransferable)) { + aTransferable, aWhichClipboard)) { aTransferable->ClearAllData(); return NS_ERROR_CONTENT_BLOCKED; } @@ -1203,7 +1226,7 @@ NS_IMETHODIMP nsBaseClipboard::AsyncGetClipboardData::GetData( mClipboard->CheckClipboardContentAnalysis( mRequestingWindowContext ? mRequestingWindowContext->Canonical() : nullptr, - aTransferable, contentAnalysisCallback); + aTransferable, mClipboardType, contentAnalysisCallback); return NS_OK; } @@ -1233,7 +1256,7 @@ NS_IMETHODIMP nsBaseClipboard::AsyncGetClipboardData::GetData( self->mRequestingWindowContext ? self->mRequestingWindowContext->Canonical() : nullptr, - transferable, contentAnalysisCallback); + transferable, self->mClipboardType, contentAnalysisCallback); }); return NS_OK; } diff --git a/widget/nsBaseClipboard.h b/widget/nsBaseClipboard.h index 5f834645cff8..af8f471b36f3 100644 --- a/widget/nsBaseClipboard.h +++ b/widget/nsBaseClipboard.h @@ -44,9 +44,12 @@ class nsBaseClipboard : public nsIClipboard { NS_DECL_ISUPPORTS // nsIClipboard - NS_IMETHOD SetData(nsITransferable* aTransferable, nsIClipboardOwner* aOwner, - int32_t aWhichClipboard) override final; + NS_IMETHOD SetData( + nsITransferable* aTransferable, nsIClipboardOwner* aOwner, + int32_t aWhichClipboard, + mozilla::dom::WindowContext* aWindowContext) override final; NS_IMETHOD AsyncSetData(int32_t aWhichClipboard, + mozilla::dom::WindowContext* aSettingWindowContext, nsIAsyncClipboardRequestCallback* aCallback, nsIAsyncSetClipboardData** _retval) override final; NS_IMETHOD GetData( @@ -108,6 +111,7 @@ class nsBaseClipboard : public nsIClipboard { NS_DECL_NSIASYNCSETCLIPBOARDDATA AsyncSetClipboardData(int32_t aClipboardType, nsBaseClipboard* aClipboard, + mozilla::dom::WindowContext* aRequestingWindowContext, nsIAsyncClipboardRequestCallback* aCallback); private: @@ -125,6 +129,7 @@ class nsBaseClipboard : public nsIClipboard { // NotifyCallback()) once nsBaseClipboard stops tracking us. This is // also used to indicate whether this request is valid. nsBaseClipboard* mClipboard; + RefPtr mWindowContext; // mCallback will be nullified once the callback is notified to ensure the // callback is only notified once. nsCOMPtr mCallback; @@ -174,22 +179,26 @@ class nsBaseClipboard : public nsIClipboard { */ void Clear(); void Update(nsITransferable* aTransferable, - nsIClipboardOwner* aClipboardOwner, int32_t aSequenceNumber) { + nsIClipboardOwner* aClipboardOwner, int32_t aSequenceNumber, + mozilla::Maybe aInnerWindowId) { // Clear first to notify the old clipboard owner. Clear(); mTransferable = aTransferable; mClipboardOwner = aClipboardOwner; mSequenceNumber = aSequenceNumber; + mInnerWindowId = aInnerWindowId; } nsITransferable* GetTransferable() const { return mTransferable; } nsIClipboardOwner* GetClipboardOwner() const { return mClipboardOwner; } int32_t GetSequenceNumber() const { return mSequenceNumber; } + mozilla::Maybe GetInnerWindowId() const { return mInnerWindowId; } nsresult GetData(nsITransferable* aTransferable) const; private: nsCOMPtr mTransferable; nsCOMPtr mClipboardOwner; int32_t mSequenceNumber = -1; + mozilla::Maybe mInnerWindowId; }; class SafeContentAnalysisResultCallback final @@ -245,10 +254,10 @@ class nsBaseClipboard : public nsIClipboard { int32_t aClipboardType); bool CheckClipboardContentAnalysisSync( mozilla::dom::WindowGlobalParent* aWindow, - const nsCOMPtr& trans); + const nsCOMPtr& trans, int32_t aClipboardType); void CheckClipboardContentAnalysis( mozilla::dom::WindowGlobalParent* aWindow, nsITransferable* aTransferable, - SafeContentAnalysisResultCallback* aResolver); + int32_t aClipboardType, SafeContentAnalysisResultCallback* aResolver); // - true means a content analysis request was fired // - false means there is no text data in the transferable // - NoContentAnalysisResult means there was an error diff --git a/widget/nsClipboardHelper.cpp b/widget/nsClipboardHelper.cpp index 3439adef4837..87e697a4c640 100644 --- a/widget/nsClipboardHelper.cpp +++ b/widget/nsClipboardHelper.cpp @@ -36,9 +36,10 @@ nsClipboardHelper::~nsClipboardHelper() { *****************************************************************************/ NS_IMETHODIMP -nsClipboardHelper::CopyStringToClipboard(const nsAString& aString, - int32_t aClipboardID, - SensitiveData aSensitive) { +nsClipboardHelper::CopyStringToClipboard( + const nsAString& aString, int32_t aClipboardID, + mozilla::dom::WindowContext* aSettingWindowContext, + SensitiveData aSensitive) { nsresult rv; // get the clipboard @@ -92,20 +93,22 @@ nsClipboardHelper::CopyStringToClipboard(const nsAString& aString, NS_ENSURE_SUCCESS(rv, rv); // put the transferable on the clipboard - rv = clipboard->SetData(trans, nullptr, aClipboardID); + rv = clipboard->SetData(trans, nullptr, aClipboardID, aSettingWindowContext); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } NS_IMETHODIMP -nsClipboardHelper::CopyString(const nsAString& aString, - SensitiveData aSensitive) { +nsClipboardHelper::CopyString( + const nsAString& aString, + mozilla::dom::WindowContext* aSettingWindowContext, + SensitiveData aSensitive) { nsresult rv; // copy to the global clipboard. it's bad if this fails in any way. rv = CopyStringToClipboard(aString, nsIClipboard::kGlobalClipboard, - aSensitive); + aSettingWindowContext, aSensitive); NS_ENSURE_SUCCESS(rv, rv); // unix also needs us to copy to the selection clipboard. this will @@ -117,7 +120,8 @@ nsClipboardHelper::CopyString(const nsAString& aString, // if this fails in any way other than "not being unix", we'll get // the assertion we need in CopyStringToClipboard, and we needn't // assert again here. - CopyStringToClipboard(aString, nsIClipboard::kSelectionClipboard, aSensitive); + CopyStringToClipboard(aString, nsIClipboard::kSelectionClipboard, + aSettingWindowContext, aSensitive); return NS_OK; } diff --git a/widget/nsClipboardProxy.cpp b/widget/nsClipboardProxy.cpp index 3b27d5954d46..4d0b72bf8e87 100644 --- a/widget/nsClipboardProxy.cpp +++ b/widget/nsClipboardProxy.cpp @@ -32,7 +32,8 @@ nsClipboardProxy::nsClipboardProxy() : mClipboardCaps(false, false, false) {} NS_IMETHODIMP nsClipboardProxy::SetData(nsITransferable* aTransferable, - nsIClipboardOwner* anOwner, int32_t aWhichClipboard) { + nsIClipboardOwner* anOwner, int32_t aWhichClipboard, + mozilla::dom::WindowContext* aWindowContext) { #if defined(ACCESSIBILITY) && defined(XP_WIN) a11y::Compatibility::SuppressA11yForClipboardCopy(); #endif @@ -41,17 +42,19 @@ nsClipboardProxy::SetData(nsITransferable* aTransferable, IPCTransferable ipcTransferable; nsContentUtils::TransferableToIPCTransferable(aTransferable, &ipcTransferable, false, nullptr); - child->SendSetClipboard(std::move(ipcTransferable), aWhichClipboard); + child->SendSetClipboard(std::move(ipcTransferable), aWhichClipboard, + aWindowContext); return NS_OK; } NS_IMETHODIMP nsClipboardProxy::AsyncSetData( - int32_t aWhichClipboard, nsIAsyncClipboardRequestCallback* aCallback, + int32_t aWhichClipboard, mozilla::dom::WindowContext* aSettingWindowContext, + nsIAsyncClipboardRequestCallback* aCallback, nsIAsyncSetClipboardData** _retval) { RefPtr request = MakeRefPtr(aCallback); ContentChild::GetSingleton()->SendPClipboardWriteRequestConstructor( - request, aWhichClipboard); + request, aWhichClipboard, aSettingWindowContext); request.forget(_retval); return NS_OK; } diff --git a/widget/nsIClipboard.idl b/widget/nsIClipboard.idl index a34f0f9298d0..fc1f8bf8c358 100644 --- a/widget/nsIClipboard.idl +++ b/widget/nsIClipboard.idl @@ -115,20 +115,28 @@ interface nsIClipboard : nsISupports * @param aTransferable The transferable * @param anOwner The owner of the transferable * @param aWhichClipboard Specifies the clipboard to which this operation applies. - * @result NS_Ok if no errors + * @param aSettingWindowContext [optional] + * The window context that is setting the clipboard, if any. This is used + * to possibly bypass Content Analysis if a set clipboard and get clipboard + * operation are done on the same page. + * @result NS_OK if no errors */ - void setData ( in nsITransferable aTransferable, in nsIClipboardOwner anOwner, - in long aWhichClipboard ) ; + void setData (in nsITransferable aTransferable, in nsIClipboardOwner anOwner, + in long aWhichClipboard, [optional] in WindowContext aSettingWindowContext); /** - * Requests setting data to the native clipboard. The acutal set occur + * Requests setting data to the native clipboard. The actual set occurs * when the data is provided by calling nsIAsyncSetClipboardData::setData(). * The result will be notified by nsIClipboardCallback. A new set request * will cancel any prior pending requests, if any exist. * * @param aWhichClipboard * Specifies the clipboard to which this operation applies. + * @param aSettingWindowContext [optional] + * The window context that is setting the clipboard, if any. This is used + * to possibly bypass Content Analysis if a set clipboard and get clipboard + * operation are done on the same page. * @param aCallback [optional] * The callback object that will be notified upon completion. * @return nsIAsyncSetClipboardData @@ -136,6 +144,7 @@ interface nsIClipboard : nsISupports * data is provided by calling nsIAsyncSetClipboardData::setData(). */ nsIAsyncSetClipboardData asyncSetData(in long aWhichClipboard, + [optional] in WindowContext aSettingWindowContext, [optional] in nsIAsyncClipboardRequestCallback aCallback); /** diff --git a/widget/nsIClipboardHelper.idl b/widget/nsIClipboardHelper.idl index ed4af112f167..370c652b317e 100644 --- a/widget/nsIClipboardHelper.idl +++ b/widget/nsIClipboardHelper.idl @@ -29,17 +29,29 @@ interface nsIClipboardHelper : nsISupports * @param aString, the string to copy to the clipboard * @param aClipboardID, the ID of the clipboard to copy to * (eg. kSelectionClipboard -- see nsIClipboard.idl) + * @param aSettingWindowContext + * The window context that is setting the clipboard, if any. This is used + * to possibly bypass Content Analysis if a set clipboard and get clipboard + * operation are done on the same page. * @param aSensitive, optional flag to indicate that data is sensitive, like a password. * That will exclude data from Cloud Clipboard/Clipboard History on Windows. */ void copyStringToClipboard(in AString aString, in long aClipboardID, + [optional] in WindowContext aSettingWindowContext, [optional, default(NotSensitive)] in nsIClipboardHelper_SensitiveData aSensitive); /** * copy string to (default) clipboard * * @param aString, the string to copy to the clipboard + * @param aSettingWindowContext + * The window context that is setting the clipboard, if any. This is used + * to possibly bypass Content Analysis if a set clipboard and get clipboard + * operation are done on the same page. + * @param aSensitive, optional flag to indicate that data is sensitive, like a password. + * That will exclude data from Cloud Clipboard/Clipboard History on Windows. */ void copyString(in AString aString, + [optional] in WindowContext aSettingWindowContext, [optional, default(NotSensitive)] in nsIClipboardHelper_SensitiveData aSensitive); }; diff --git a/widget/tests/file_test_clipboard_asyncSetData.js b/widget/tests/file_test_clipboard_asyncSetData.js index cceecd2c4489..5eb73f90fae5 100644 --- a/widget/tests/file_test_clipboard_asyncSetData.js +++ b/widget/tests/file_test_clipboard_asyncSetData.js @@ -21,7 +21,7 @@ clipboardTypes.forEach(function (type) { let priorResult; let priorRequest; let priorPromise = new Promise(resolve => { - priorRequest = clipboard.asyncSetData(type, { + priorRequest = clipboard.asyncSetData(type, null, { QueryInterface: SpecialPowers.ChromeUtils.generateQI([ "nsIAsyncSetClipboardDataCallback", ]), @@ -119,7 +119,7 @@ clipboardTypes.forEach(function (type) { // Create a pending asyncSetData request let result; - let request = clipboard.asyncSetData(type, rv => { + let request = clipboard.asyncSetData(type, null, rv => { result = rv; });