1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsBaseClipboard.h"
8 #include "ContentAnalysis.h"
9 #include "mozilla/Components.h"
10 #include "mozilla/contentanalysis/ContentAnalysisIPCTypes.h"
11 #include "mozilla/dom/BindingUtils.h"
12 #include "mozilla/dom/CanonicalBrowsingContext.h"
13 #include "mozilla/dom/Document.h"
14 #include "mozilla/dom/Promise.h"
15 #include "mozilla/dom/PromiseNativeHandler.h"
16 #include "mozilla/dom/WindowGlobalParent.h"
17 #include "mozilla/dom/WindowContext.h"
18 #include "mozilla/ErrorResult.h"
19 #include "mozilla/MoveOnlyFunction.h"
20 #include "mozilla/RefPtr.h"
21 #include "mozilla/Services.h"
22 #include "mozilla/SpinEventLoopUntil.h"
23 #include "mozilla/StaticPrefs_dom.h"
24 #include "mozilla/StaticPrefs_widget.h"
25 #include "nsContentUtils.h"
26 #include "nsFocusManager.h"
27 #include "nsIClipboardOwner.h"
28 #include "nsIPromptService.h"
29 #include "nsISupportsPrimitives.h"
33 using mozilla::GenericPromise
;
34 using mozilla::LogLevel
;
35 using mozilla::UniquePtr
;
36 using mozilla::dom::BrowsingContext
;
37 using mozilla::dom::CanonicalBrowsingContext
;
38 using mozilla::dom::ClipboardCapabilities
;
39 using mozilla::dom::Document
;
41 static const int32_t kGetAvailableFlavorsRetryCount
= 5;
45 struct ClipboardGetRequest
{
46 ClipboardGetRequest(const nsTArray
<nsCString
>& aFlavorList
,
47 nsIAsyncClipboardGetCallback
* aCallback
)
48 : mFlavorList(aFlavorList
.Clone()), mCallback(aCallback
) {}
50 const nsTArray
<nsCString
> mFlavorList
;
51 const nsCOMPtr
<nsIAsyncClipboardGetCallback
> mCallback
;
54 class UserConfirmationRequest final
55 : public mozilla::dom::PromiseNativeHandler
{
57 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
58 NS_DECL_CYCLE_COLLECTION_CLASS(UserConfirmationRequest
)
60 UserConfirmationRequest(int32_t aClipboardType
,
61 Document
* aRequestingChromeDocument
,
62 nsIPrincipal
* aRequestingPrincipal
,
63 nsBaseClipboard
* aClipboard
,
64 mozilla::dom::WindowContext
* aRequestingWindowContext
)
65 : mClipboardType(aClipboardType
),
66 mRequestingChromeDocument(aRequestingChromeDocument
),
67 mRequestingPrincipal(aRequestingPrincipal
),
68 mClipboard(aClipboard
),
69 mRequestingWindowContext(aRequestingWindowContext
) {
71 mClipboard
->nsIClipboard::IsClipboardTypeSupported(aClipboardType
));
74 void ResolvedCallback(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
75 mozilla::ErrorResult
& aRv
) override
;
77 void RejectedCallback(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
78 mozilla::ErrorResult
& aRv
) override
;
80 bool IsEqual(int32_t aClipboardType
, Document
* aRequestingChromeDocument
,
81 nsIPrincipal
* aRequestingPrincipal
,
82 mozilla::dom::WindowContext
* aRequestingWindowContext
) const {
83 if (!(ClipboardType() == aClipboardType
&&
84 RequestingChromeDocument() == aRequestingChromeDocument
&&
85 RequestingPrincipal()->Equals(aRequestingPrincipal
) &&
86 (mRequestingWindowContext
&& aRequestingWindowContext
))) {
89 // Only check requesting window contexts if content analysis is active
90 nsCOMPtr
<nsIContentAnalysis
> contentAnalysis
=
91 mozilla::components::nsIContentAnalysis::Service();
92 if (!contentAnalysis
) {
96 bool contentAnalysisIsActive
;
97 nsresult rv
= contentAnalysis
->GetIsActive(&contentAnalysisIsActive
);
98 if (MOZ_LIKELY(NS_FAILED(rv
) || !contentAnalysisIsActive
)) {
101 return mRequestingWindowContext
->Id() == aRequestingWindowContext
->Id();
104 int32_t ClipboardType() const { return mClipboardType
; }
106 Document
* RequestingChromeDocument() const {
107 return mRequestingChromeDocument
;
110 nsIPrincipal
* RequestingPrincipal() const { return mRequestingPrincipal
; }
112 void AddClipboardGetRequest(const nsTArray
<nsCString
>& aFlavorList
,
113 nsIAsyncClipboardGetCallback
* aCallback
) {
114 MOZ_ASSERT(!aFlavorList
.IsEmpty());
115 MOZ_ASSERT(aCallback
);
116 mPendingClipboardGetRequests
.AppendElement(
117 mozilla::MakeUnique
<ClipboardGetRequest
>(aFlavorList
, aCallback
));
120 void RejectPendingClipboardGetRequests(nsresult aError
) {
121 MOZ_ASSERT(NS_FAILED(aError
));
122 auto requests
= std::move(mPendingClipboardGetRequests
);
123 for (const auto& request
: requests
) {
125 MOZ_ASSERT(request
->mCallback
);
126 request
->mCallback
->OnError(aError
);
130 void ProcessPendingClipboardGetRequests() {
131 auto requests
= std::move(mPendingClipboardGetRequests
);
132 for (const auto& request
: requests
) {
134 MOZ_ASSERT(!request
->mFlavorList
.IsEmpty());
135 MOZ_ASSERT(request
->mCallback
);
136 mClipboard
->AsyncGetDataInternal(request
->mFlavorList
, mClipboardType
,
137 mRequestingWindowContext
,
142 nsTArray
<UniquePtr
<ClipboardGetRequest
>>& GetPendingClipboardGetRequests() {
143 return mPendingClipboardGetRequests
;
147 ~UserConfirmationRequest() = default;
149 const int32_t mClipboardType
;
150 RefPtr
<Document
> mRequestingChromeDocument
;
151 const nsCOMPtr
<nsIPrincipal
> mRequestingPrincipal
;
152 const RefPtr
<nsBaseClipboard
> mClipboard
;
153 const RefPtr
<mozilla::dom::WindowContext
> mRequestingWindowContext
;
154 // Track the pending read requests that wait for user confirmation.
155 nsTArray
<UniquePtr
<ClipboardGetRequest
>> mPendingClipboardGetRequests
;
158 NS_IMPL_CYCLE_COLLECTION(UserConfirmationRequest
, mRequestingChromeDocument
)
160 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(UserConfirmationRequest
)
161 NS_INTERFACE_MAP_ENTRY(nsISupports
)
164 NS_IMPL_CYCLE_COLLECTING_ADDREF(UserConfirmationRequest
)
165 NS_IMPL_CYCLE_COLLECTING_RELEASE(UserConfirmationRequest
)
167 static mozilla::StaticRefPtr
<UserConfirmationRequest
> sUserConfirmationRequest
;
169 void UserConfirmationRequest::ResolvedCallback(JSContext
* aCx
,
170 JS::Handle
<JS::Value
> aValue
,
171 mozilla::ErrorResult
& aRv
) {
172 MOZ_DIAGNOSTIC_ASSERT(sUserConfirmationRequest
== this);
173 sUserConfirmationRequest
= nullptr;
175 JS::Rooted
<JSObject
*> detailObj(aCx
, &aValue
.toObject());
176 nsCOMPtr
<nsIPropertyBag2
> propBag
;
177 nsresult rv
= mozilla::dom::UnwrapArg
<nsIPropertyBag2
>(
178 aCx
, detailObj
, getter_AddRefs(propBag
));
180 RejectPendingClipboardGetRequests(rv
);
185 rv
= propBag
->GetPropertyAsBool(u
"ok"_ns
, &result
);
187 RejectPendingClipboardGetRequests(rv
);
192 RejectPendingClipboardGetRequests(NS_ERROR_DOM_NOT_ALLOWED_ERR
);
196 ProcessPendingClipboardGetRequests();
199 void UserConfirmationRequest::RejectedCallback(JSContext
* aCx
,
200 JS::Handle
<JS::Value
> aValue
,
201 mozilla::ErrorResult
& aRv
) {
202 MOZ_DIAGNOSTIC_ASSERT(sUserConfirmationRequest
== this);
203 sUserConfirmationRequest
= nullptr;
204 RejectPendingClipboardGetRequests(NS_ERROR_FAILURE
);
209 NS_IMPL_ISUPPORTS(nsBaseClipboard::AsyncSetClipboardData
,
210 nsIAsyncSetClipboardData
)
212 nsBaseClipboard::AsyncSetClipboardData::AsyncSetClipboardData(
213 int32_t aClipboardType
, nsBaseClipboard
* aClipboard
,
214 nsIAsyncClipboardRequestCallback
* aCallback
)
215 : mClipboardType(aClipboardType
),
216 mClipboard(aClipboard
),
217 mCallback(aCallback
) {
218 MOZ_ASSERT(mClipboard
);
220 mClipboard
->nsIClipboard::IsClipboardTypeSupported(mClipboardType
));
224 nsBaseClipboard::AsyncSetClipboardData::SetData(nsITransferable
* aTransferable
,
225 nsIClipboardOwner
* aOwner
) {
226 MOZ_CLIPBOARD_LOG("AsyncSetClipboardData::SetData (%p): clipboard=%d", this,
230 return NS_ERROR_FAILURE
;
233 if (MOZ_CLIPBOARD_LOG_ENABLED()) {
234 nsTArray
<nsCString
> flavors
;
235 if (NS_SUCCEEDED(aTransferable
->FlavorsTransferableCanImport(flavors
))) {
236 for (const auto& flavor
: flavors
) {
237 MOZ_CLIPBOARD_LOG(" MIME %s", flavor
.get());
242 MOZ_ASSERT(mClipboard
);
244 mClipboard
->nsIClipboard::IsClipboardTypeSupported(mClipboardType
));
245 MOZ_DIAGNOSTIC_ASSERT(mClipboard
->mPendingWriteRequests
[mClipboardType
] ==
248 RefPtr
<AsyncSetClipboardData
> request
=
249 std::move(mClipboard
->mPendingWriteRequests
[mClipboardType
]);
250 nsresult rv
= mClipboard
->SetData(aTransferable
, aOwner
, mClipboardType
);
251 MaybeNotifyCallback(rv
);
257 nsBaseClipboard::AsyncSetClipboardData::Abort(nsresult aReason
) {
258 // Note: This may be called during destructor, so it should not attempt to
259 // take a reference to mClipboard.
261 if (!IsValid() || !NS_FAILED(aReason
)) {
262 return NS_ERROR_FAILURE
;
265 MaybeNotifyCallback(aReason
);
269 void nsBaseClipboard::AsyncSetClipboardData::MaybeNotifyCallback(
271 // Note: This may be called during destructor, so it should not attempt to
272 // take a reference to mClipboard.
274 MOZ_ASSERT(IsValid());
275 if (nsCOMPtr
<nsIAsyncClipboardRequestCallback
> callback
=
276 mCallback
.forget()) {
277 callback
->OnComplete(aResult
);
279 // Once the callback is notified, setData should not be allowed, so invalidate
281 mClipboard
= nullptr;
284 void nsBaseClipboard::RejectPendingAsyncSetDataRequestIfAny(
285 int32_t aClipboardType
) {
286 MOZ_ASSERT(nsIClipboard::IsClipboardTypeSupported(aClipboardType
));
287 auto& request
= mPendingWriteRequests
[aClipboardType
];
289 request
->Abort(NS_ERROR_ABORT
);
294 NS_IMETHODIMP
nsBaseClipboard::AsyncSetData(
295 int32_t aWhichClipboard
, nsIAsyncClipboardRequestCallback
* aCallback
,
296 nsIAsyncSetClipboardData
** _retval
) {
297 MOZ_CLIPBOARD_LOG("%s: clipboard=%d", __FUNCTION__
, aWhichClipboard
);
300 if (!nsIClipboard::IsClipboardTypeSupported(aWhichClipboard
)) {
301 MOZ_CLIPBOARD_LOG("%s: clipboard %d is not supported.", __FUNCTION__
,
303 return NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
306 // Reject existing pending AsyncSetData request if any.
307 RejectPendingAsyncSetDataRequestIfAny(aWhichClipboard
);
309 // Create a new AsyncSetClipboardData.
310 RefPtr
<AsyncSetClipboardData
> request
=
311 mozilla::MakeRefPtr
<AsyncSetClipboardData
>(aWhichClipboard
, this,
313 mPendingWriteRequests
[aWhichClipboard
] = request
;
314 request
.forget(_retval
);
319 class SafeContentAnalysisResultCallback final
320 : public nsIContentAnalysisCallback
{
322 explicit SafeContentAnalysisResultCallback(
323 std::function
<void(RefPtr
<nsIContentAnalysisResult
>&&)> aResolver
)
324 : mResolver(std::move(aResolver
)) {}
325 void Callback(RefPtr
<nsIContentAnalysisResult
>&& aResult
) {
326 MOZ_ASSERT(mResolver
, "Called SafeContentAnalysisResultCallback twice!");
327 if (auto resolver
= std::move(mResolver
)) {
328 resolver(std::move(aResult
));
332 NS_IMETHODIMP
ContentResult(nsIContentAnalysisResponse
* aResponse
) override
{
333 using namespace mozilla::contentanalysis
;
334 RefPtr
<ContentAnalysisResult
> result
=
335 ContentAnalysisResult::FromContentAnalysisResponse(aResponse
);
340 NS_IMETHODIMP
Error(nsresult aError
) override
{
341 using namespace mozilla::contentanalysis
;
342 Callback(ContentAnalysisResult::FromNoResult(
343 NoContentAnalysisResult::ERROR_OTHER
));
347 NS_DECL_THREADSAFE_ISUPPORTS
349 // Private destructor to force this to be allocated in a RefPtr, which is
350 // necessary for safe usage.
351 ~SafeContentAnalysisResultCallback() {
352 MOZ_ASSERT(!mResolver
, "SafeContentAnalysisResultCallback never called!");
354 mozilla::MoveOnlyFunction
<void(RefPtr
<nsIContentAnalysisResult
>&&)> mResolver
;
356 NS_IMPL_ISUPPORTS(SafeContentAnalysisResultCallback
,
357 nsIContentAnalysisCallback
);
361 // - true means a content analysis request was fired
362 // - false means there is no text data in the transferable
363 // - NoContentAnalysisResult means there was an error
364 static mozilla::Result
<bool, mozilla::contentanalysis::NoContentAnalysisResult
>
365 CheckClipboardContentAnalysisAsText(
366 uint64_t aInnerWindowId
, SafeContentAnalysisResultCallback
* aResolver
,
367 nsIURI
* aDocumentURI
, nsIContentAnalysis
* aContentAnalysis
,
368 nsITransferable
* aTextTrans
) {
369 using namespace mozilla::contentanalysis
;
371 nsCOMPtr
<nsISupports
> transferData
;
372 if (NS_FAILED(aTextTrans
->GetTransferData(kTextMime
,
373 getter_AddRefs(transferData
)))) {
376 nsCOMPtr
<nsISupportsString
> textData
= do_QueryInterface(transferData
);
377 if (MOZ_UNLIKELY(!textData
)) {
381 if (NS_FAILED(textData
->GetData(text
))) {
382 return mozilla::Err(NoContentAnalysisResult::ERROR_OTHER
);
384 RefPtr
<mozilla::dom::WindowGlobalParent
> window
=
385 mozilla::dom::WindowGlobalParent::GetByInnerWindowId(aInnerWindowId
);
387 // The window has gone away in the meantime
388 return mozilla::Err(NoContentAnalysisResult::ERROR_OTHER
);
390 nsCOMPtr
<nsIContentAnalysisRequest
> contentAnalysisRequest
=
391 new ContentAnalysisRequest(
392 nsIContentAnalysisRequest::AnalysisType::eBulkDataEntry
,
393 std::move(text
), false, EmptyCString(), aDocumentURI
,
394 nsIContentAnalysisRequest::OperationType::eClipboard
, window
);
395 nsresult rv
= aContentAnalysis
->AnalyzeContentRequestCallback(
396 contentAnalysisRequest
, /* aAutoAcknowledge */ true, aResolver
);
398 return mozilla::Err(NoContentAnalysisResult::ERROR_OTHER
);
404 // - true means a content analysis request was fired
405 // - false means there is no file data in the transferable
406 // - NoContentAnalysisResult means there was an error
407 static mozilla::Result
<bool, mozilla::contentanalysis::NoContentAnalysisResult
>
408 CheckClipboardContentAnalysisAsFile(
409 uint64_t aInnerWindowId
, SafeContentAnalysisResultCallback
* aResolver
,
410 nsIURI
* aDocumentURI
, nsIContentAnalysis
* aContentAnalysis
,
411 nsITransferable
* aFileTrans
) {
412 using namespace mozilla::contentanalysis
;
414 nsCOMPtr
<nsISupports
> transferData
;
416 aFileTrans
->GetTransferData(kFileMime
, getter_AddRefs(transferData
));
418 if (NS_SUCCEEDED(rv
)) {
419 if (nsCOMPtr
<nsIFile
> file
= do_QueryInterface(transferData
)) {
420 rv
= file
->GetPath(filePath
);
422 MOZ_ASSERT_UNREACHABLE("clipboard data had kFileMime but no nsIFile!");
423 return mozilla::Err(NoContentAnalysisResult::ERROR_OTHER
);
426 if (NS_FAILED(rv
) || filePath
.IsEmpty()) {
429 RefPtr
<mozilla::dom::WindowGlobalParent
> window
=
430 mozilla::dom::WindowGlobalParent::GetByInnerWindowId(aInnerWindowId
);
432 // The window has gone away in the meantime
433 return mozilla::Err(NoContentAnalysisResult::ERROR_OTHER
);
435 // Let the content analysis code calculate the digest
436 nsCOMPtr
<nsIContentAnalysisRequest
> contentAnalysisRequest
=
437 new ContentAnalysisRequest(
438 nsIContentAnalysisRequest::AnalysisType::eBulkDataEntry
,
439 std::move(filePath
), true, EmptyCString(), aDocumentURI
,
440 nsIContentAnalysisRequest::OperationType::eCustomDisplayString
,
442 rv
= aContentAnalysis
->AnalyzeContentRequestCallback(
443 contentAnalysisRequest
,
444 /* aAutoAcknowledge */ true, aResolver
);
446 return mozilla::Err(NoContentAnalysisResult::ERROR_OTHER
);
451 static void CheckClipboardContentAnalysis(
452 mozilla::dom::WindowGlobalParent
* aWindow
, nsITransferable
* aTransferable
,
453 SafeContentAnalysisResultCallback
* aResolver
) {
454 using namespace mozilla::contentanalysis
;
456 // Content analysis is only needed if an outside webpage has access to
457 // the data. So, skip content analysis if there is:
458 // - no associated window (for example, scripted clipboard read by system
460 // - the window is a chrome docshell
461 // - the window is being rendered in the parent process (for example,
462 // about:support and the like)
463 if (!aWindow
|| aWindow
->GetBrowsingContext()->IsChrome() ||
464 aWindow
->IsInProcess()) {
465 aResolver
->Callback(ContentAnalysisResult::FromNoResult(
466 NoContentAnalysisResult::CONTEXT_EXEMPT_FROM_CONTENT_ANALYSIS
));
469 nsCOMPtr
<nsIContentAnalysis
> contentAnalysis
=
470 mozilla::components::nsIContentAnalysis::Service();
471 if (!contentAnalysis
) {
472 aResolver
->Callback(ContentAnalysisResult::FromNoResult(
473 NoContentAnalysisResult::ERROR_OTHER
));
477 bool contentAnalysisIsActive
;
478 nsresult rv
= contentAnalysis
->GetIsActive(&contentAnalysisIsActive
);
479 if (MOZ_LIKELY(NS_FAILED(rv
) || !contentAnalysisIsActive
)) {
480 aResolver
->Callback(ContentAnalysisResult::FromNoResult(
481 NoContentAnalysisResult::CONTENT_ANALYSIS_NOT_ACTIVE
));
485 nsCOMPtr
<nsIURI
> currentURI
= aWindow
->Canonical()->GetDocumentURI();
486 uint64_t innerWindowId
= aWindow
->InnerWindowId();
487 nsTArray
<nsCString
> flavors
;
488 rv
= aTransferable
->FlavorsTransferableCanExport(flavors
);
489 if (NS_WARN_IF(NS_FAILED(rv
))) {
490 aResolver
->Callback(ContentAnalysisResult::FromNoResult(
491 NoContentAnalysisResult::ERROR_OTHER
));
494 bool keepChecking
= true;
495 if (flavors
.Contains(kFileMime
)) {
496 auto fileResult
= CheckClipboardContentAnalysisAsFile(
497 innerWindowId
, aResolver
, currentURI
, contentAnalysis
, aTransferable
);
499 if (fileResult
.isErr()) {
501 ContentAnalysisResult::FromNoResult(fileResult
.unwrapErr()));
504 keepChecking
= !fileResult
.unwrap();
507 // Failed to get the clipboard data as a file, so try as text
508 auto textResult
= CheckClipboardContentAnalysisAsText(
509 innerWindowId
, aResolver
, currentURI
, contentAnalysis
, aTransferable
);
510 if (textResult
.isErr()) {
512 ContentAnalysisResult::FromNoResult(textResult
.unwrapErr()));
515 if (!textResult
.unwrap()) {
516 // Couldn't get file or text data from this
517 aResolver
->Callback(ContentAnalysisResult::FromNoResult(
518 NoContentAnalysisResult::ERROR_COULD_NOT_GET_DATA
));
524 static bool CheckClipboardContentAnalysisSync(
525 mozilla::dom::WindowGlobalParent
* aWindow
,
526 const nsCOMPtr
<nsITransferable
>& trans
) {
527 bool requestDone
= false;
528 RefPtr
<nsIContentAnalysisResult
> result
;
529 auto callback
= mozilla::MakeRefPtr
<SafeContentAnalysisResultCallback
>(
530 [&requestDone
, &result
](RefPtr
<nsIContentAnalysisResult
>&& aResult
) {
531 result
= std::move(aResult
);
534 CheckClipboardContentAnalysis(aWindow
, trans
, callback
);
535 mozilla::SpinEventLoopUntil("CheckClipboardContentAnalysisSync"_ns
,
536 [&requestDone
]() -> bool { return requestDone
; });
537 return result
->GetShouldAllowContent();
540 nsBaseClipboard::nsBaseClipboard(const ClipboardCapabilities
& aClipboardCaps
)
541 : mClipboardCaps(aClipboardCaps
) {
542 using mozilla::MakeUnique
;
543 // Initialize clipboard cache.
544 mCaches
[kGlobalClipboard
] = MakeUnique
<ClipboardCache
>();
545 if (mClipboardCaps
.supportsSelectionClipboard()) {
546 mCaches
[kSelectionClipboard
] = MakeUnique
<ClipboardCache
>();
548 if (mClipboardCaps
.supportsFindClipboard()) {
549 mCaches
[kFindClipboard
] = MakeUnique
<ClipboardCache
>();
551 if (mClipboardCaps
.supportsSelectionCache()) {
552 mCaches
[kSelectionCache
] = MakeUnique
<ClipboardCache
>();
556 nsBaseClipboard::~nsBaseClipboard() {
557 for (auto& request
: mPendingWriteRequests
) {
559 request
->Abort(NS_ERROR_ABORT
);
565 NS_IMPL_ISUPPORTS(nsBaseClipboard
, nsIClipboard
)
568 * Sets the transferable object
571 NS_IMETHODIMP
nsBaseClipboard::SetData(nsITransferable
* aTransferable
,
572 nsIClipboardOwner
* aOwner
,
573 int32_t aWhichClipboard
) {
574 NS_ASSERTION(aTransferable
, "clipboard given a null transferable");
576 MOZ_CLIPBOARD_LOG("%s: clipboard=%d", __FUNCTION__
, aWhichClipboard
);
578 if (!nsIClipboard::IsClipboardTypeSupported(aWhichClipboard
)) {
579 MOZ_CLIPBOARD_LOG("%s: clipboard %d is not supported.", __FUNCTION__
,
581 return NS_ERROR_FAILURE
;
584 if (MOZ_CLIPBOARD_LOG_ENABLED()) {
585 nsTArray
<nsCString
> flavors
;
586 if (NS_SUCCEEDED(aTransferable
->FlavorsTransferableCanImport(flavors
))) {
587 for (const auto& flavor
: flavors
) {
588 MOZ_CLIPBOARD_LOG(" MIME %s", flavor
.get());
593 const auto& clipboardCache
= mCaches
[aWhichClipboard
];
594 MOZ_ASSERT(clipboardCache
);
595 if (aTransferable
== clipboardCache
->GetTransferable() &&
596 aOwner
== clipboardCache
->GetClipboardOwner()) {
597 MOZ_CLIPBOARD_LOG("%s: skipping update.", __FUNCTION__
);
601 clipboardCache
->Clear();
603 nsresult rv
= NS_ERROR_FAILURE
;
605 mIgnoreEmptyNotification
= true;
606 // Reject existing pending asyncSetData request if any.
607 RejectPendingAsyncSetDataRequestIfAny(aWhichClipboard
);
608 rv
= SetNativeClipboardData(aTransferable
, aWhichClipboard
);
609 mIgnoreEmptyNotification
= false;
612 MOZ_CLIPBOARD_LOG("%s: setting native clipboard data failed.",
617 auto result
= GetNativeClipboardSequenceNumber(aWhichClipboard
);
618 if (result
.isErr()) {
619 MOZ_CLIPBOARD_LOG("%s: getting native clipboard change count failed.",
621 return result
.unwrapErr();
624 clipboardCache
->Update(aTransferable
, aOwner
, result
.unwrap());
628 nsresult
nsBaseClipboard::GetDataFromClipboardCache(
629 nsITransferable
* aTransferable
, int32_t aClipboardType
) {
630 MOZ_ASSERT(aTransferable
);
631 MOZ_ASSERT(nsIClipboard::IsClipboardTypeSupported(aClipboardType
));
632 MOZ_ASSERT(mozilla::StaticPrefs::widget_clipboard_use_cached_data_enabled());
634 const auto* clipboardCache
= GetClipboardCacheIfValid(aClipboardType
);
635 if (!clipboardCache
) {
636 return NS_ERROR_FAILURE
;
639 return clipboardCache
->GetData(aTransferable
);
643 * Gets the transferable object from system clipboard.
645 NS_IMETHODIMP
nsBaseClipboard::GetData(
646 nsITransferable
* aTransferable
, int32_t aWhichClipboard
,
647 mozilla::dom::WindowContext
* aWindowContext
) {
648 MOZ_CLIPBOARD_LOG("%s: clipboard=%d", __FUNCTION__
, aWhichClipboard
);
650 if (!aTransferable
) {
651 NS_ASSERTION(false, "clipboard given a null transferable");
652 return NS_ERROR_FAILURE
;
655 if (!nsIClipboard::IsClipboardTypeSupported(aWhichClipboard
)) {
656 MOZ_CLIPBOARD_LOG("%s: clipboard %d is not supported.", __FUNCTION__
,
658 return NS_ERROR_FAILURE
;
661 if (mozilla::StaticPrefs::widget_clipboard_use_cached_data_enabled()) {
662 // If we were the last ones to put something on the native clipboard, then
663 // just use the cached transferable. Otherwise clear it because it isn't
664 // relevant any more.
666 GetDataFromClipboardCache(aTransferable
, aWhichClipboard
))) {
667 // maybe try to fill in more types? Is there a point?
668 if (!CheckClipboardContentAnalysisSync(aWindowContext
->Canonical(),
670 aTransferable
->ClearAllData();
671 return NS_ERROR_CONTENT_BLOCKED
;
676 // at this point we can't satisfy the request from cache data so let's look
677 // for things other people put on the system clipboard
679 nsresult rv
= GetNativeClipboardData(aTransferable
, aWhichClipboard
);
683 if (!CheckClipboardContentAnalysisSync(aWindowContext
->Canonical(),
685 aTransferable
->ClearAllData();
686 return NS_ERROR_CONTENT_BLOCKED
;
691 void nsBaseClipboard::MaybeRetryGetAvailableFlavors(
692 const nsTArray
<nsCString
>& aFlavorList
, int32_t aWhichClipboard
,
693 nsIAsyncClipboardGetCallback
* aCallback
, int32_t aRetryCount
,
694 mozilla::dom::WindowContext
* aRequestingWindowContext
) {
695 // Note we have to get the clipboard sequence number first before the actual
696 // read. This is to use it to verify the clipboard data is still the one we
697 // try to read, instead of the later state.
698 auto sequenceNumberOrError
=
699 GetNativeClipboardSequenceNumber(aWhichClipboard
);
700 if (sequenceNumberOrError
.isErr()) {
701 MOZ_CLIPBOARD_LOG("%s: unable to get sequence number for clipboard %d.",
702 __FUNCTION__
, aWhichClipboard
);
703 aCallback
->OnError(sequenceNumberOrError
.unwrapErr());
707 int32_t sequenceNumber
= sequenceNumberOrError
.unwrap();
708 AsyncHasNativeClipboardDataMatchingFlavors(
709 aFlavorList
, aWhichClipboard
,
710 [self
= RefPtr
{this}, callback
= nsCOMPtr
{aCallback
}, aWhichClipboard
,
711 aRetryCount
, flavorList
= aFlavorList
.Clone(), sequenceNumber
,
712 requestingWindowContext
=
713 RefPtr
{aRequestingWindowContext
}](auto aFlavorsOrError
) {
714 if (aFlavorsOrError
.isErr()) {
716 "%s: unable to get available flavors for clipboard %d.",
717 __FUNCTION__
, aWhichClipboard
);
718 callback
->OnError(aFlavorsOrError
.unwrapErr());
722 auto sequenceNumberOrError
=
723 self
->GetNativeClipboardSequenceNumber(aWhichClipboard
);
724 if (sequenceNumberOrError
.isErr()) {
726 "%s: unable to get sequence number for clipboard %d.",
727 __FUNCTION__
, aWhichClipboard
);
728 callback
->OnError(sequenceNumberOrError
.unwrapErr());
732 if (sequenceNumber
== sequenceNumberOrError
.unwrap()) {
733 auto asyncGetClipboardData
=
734 mozilla::MakeRefPtr
<AsyncGetClipboardData
>(
735 aWhichClipboard
, sequenceNumber
,
736 std::move(aFlavorsOrError
.unwrap()), false, self
,
737 requestingWindowContext
);
738 callback
->OnSuccess(asyncGetClipboardData
);
742 if (aRetryCount
> 0) {
744 "%s: clipboard=%d, ignore the data due to the sequence number "
745 "doesn't match, retry (%d) ..",
746 __FUNCTION__
, aWhichClipboard
, aRetryCount
);
747 self
->MaybeRetryGetAvailableFlavors(flavorList
, aWhichClipboard
,
748 callback
, aRetryCount
- 1,
749 requestingWindowContext
);
753 MOZ_DIAGNOSTIC_ASSERT(false, "How can this happen?!?");
754 callback
->OnError(NS_ERROR_FAILURE
);
758 NS_IMETHODIMP
nsBaseClipboard::AsyncGetData(
759 const nsTArray
<nsCString
>& aFlavorList
, int32_t aWhichClipboard
,
760 mozilla::dom::WindowContext
* aRequestingWindowContext
,
761 nsIPrincipal
* aRequestingPrincipal
,
762 nsIAsyncClipboardGetCallback
* aCallback
) {
763 MOZ_CLIPBOARD_LOG("%s: clipboard=%d", __FUNCTION__
, aWhichClipboard
);
765 if (!aCallback
|| !aRequestingPrincipal
|| aFlavorList
.IsEmpty()) {
766 return NS_ERROR_INVALID_ARG
;
769 if (!nsIClipboard::IsClipboardTypeSupported(aWhichClipboard
)) {
770 MOZ_CLIPBOARD_LOG("%s: clipboard %d is not supported.", __FUNCTION__
,
772 return NS_ERROR_FAILURE
;
775 // We want to disable security check for automated tests that have the pref
776 // set to true, or extension that have clipboard read permission.
777 if (mozilla::StaticPrefs::
778 dom_events_testing_asyncClipboard_DoNotUseDirectly() ||
779 nsContentUtils::PrincipalHasPermission(*aRequestingPrincipal
,
780 nsGkAtoms::clipboardRead
)) {
781 AsyncGetDataInternal(aFlavorList
, aWhichClipboard
, aRequestingWindowContext
,
786 // If cache data is valid, we are the last ones to put something on the native
787 // clipboard, then check if the data is from the same-origin page,
788 if (auto* clipboardCache
= GetClipboardCacheIfValid(aWhichClipboard
)) {
789 nsCOMPtr
<nsITransferable
> trans
= clipboardCache
->GetTransferable();
792 if (nsCOMPtr
<nsIPrincipal
> principal
= trans
->GetRequestingPrincipal()) {
793 if (aRequestingPrincipal
->Subsumes(principal
)) {
794 MOZ_CLIPBOARD_LOG("%s: native clipboard data is from same-origin page.",
796 AsyncGetDataInternal(aFlavorList
, aWhichClipboard
,
797 aRequestingWindowContext
, aCallback
);
803 // TODO: enable showing the "Paste" button in this case; see bug 1773681.
804 if (aRequestingPrincipal
->GetIsAddonOrExpandedAddonPrincipal()) {
805 MOZ_CLIPBOARD_LOG("%s: Addon without read permission.", __FUNCTION__
);
806 return aCallback
->OnError(NS_ERROR_FAILURE
);
809 RequestUserConfirmation(aWhichClipboard
, aFlavorList
,
810 aRequestingWindowContext
, aRequestingPrincipal
,
815 void nsBaseClipboard::AsyncGetDataInternal(
816 const nsTArray
<nsCString
>& aFlavorList
, int32_t aClipboardType
,
817 mozilla::dom::WindowContext
* aRequestingWindowContext
,
818 nsIAsyncClipboardGetCallback
* aCallback
) {
819 MOZ_ASSERT(nsIClipboard::IsClipboardTypeSupported(aClipboardType
));
821 if (mozilla::StaticPrefs::widget_clipboard_use_cached_data_enabled()) {
822 // If we were the last ones to put something on the native clipboard, then
823 // just use the cached transferable. Otherwise clear it because it isn't
824 // relevant any more.
825 if (auto* clipboardCache
= GetClipboardCacheIfValid(aClipboardType
)) {
826 nsITransferable
* cachedTransferable
= clipboardCache
->GetTransferable();
827 MOZ_ASSERT(cachedTransferable
);
829 nsTArray
<nsCString
> transferableFlavors
;
830 if (NS_SUCCEEDED(cachedTransferable
->FlavorsTransferableCanExport(
831 transferableFlavors
))) {
832 nsTArray
<nsCString
> results
;
833 for (const auto& transferableFlavor
: transferableFlavors
) {
834 for (const auto& flavor
: aFlavorList
) {
835 // XXX We need special check for image as we always put the
836 // image as "native" on the clipboard.
837 if (transferableFlavor
.Equals(flavor
) ||
838 (transferableFlavor
.Equals(kNativeImageMime
) &&
839 nsContentUtils::IsFlavorImage(flavor
))) {
840 MOZ_CLIPBOARD_LOG(" has %s", flavor
.get());
841 results
.AppendElement(flavor
);
846 // XXX Do we need to check system clipboard for the flavors that cannot
847 // be found in cache?
848 auto asyncGetClipboardData
= mozilla::MakeRefPtr
<AsyncGetClipboardData
>(
849 aClipboardType
, clipboardCache
->GetSequenceNumber(),
850 std::move(results
), true, this, aRequestingWindowContext
);
851 aCallback
->OnSuccess(asyncGetClipboardData
);
856 // At this point we can't satisfy the request from cache data so let's look
857 // for things other people put on the system clipboard.
860 MaybeRetryGetAvailableFlavors(aFlavorList
, aClipboardType
, aCallback
,
861 kGetAvailableFlavorsRetryCount
,
862 aRequestingWindowContext
);
865 NS_IMETHODIMP
nsBaseClipboard::EmptyClipboard(int32_t aWhichClipboard
) {
866 MOZ_CLIPBOARD_LOG("%s: clipboard=%d", __FUNCTION__
, aWhichClipboard
);
868 if (!nsIClipboard::IsClipboardTypeSupported(aWhichClipboard
)) {
869 MOZ_CLIPBOARD_LOG("%s: clipboard %d is not supported.", __FUNCTION__
,
871 return NS_ERROR_FAILURE
;
874 EmptyNativeClipboardData(aWhichClipboard
);
876 const auto& clipboardCache
= mCaches
[aWhichClipboard
];
877 MOZ_ASSERT(clipboardCache
);
879 if (mIgnoreEmptyNotification
) {
880 MOZ_DIAGNOSTIC_ASSERT(!clipboardCache
->GetTransferable() &&
881 !clipboardCache
->GetClipboardOwner() &&
882 clipboardCache
->GetSequenceNumber() == -1,
883 "How did we have data in clipboard cache here?");
887 clipboardCache
->Clear();
892 mozilla::Result
<nsTArray
<nsCString
>, nsresult
>
893 nsBaseClipboard::GetFlavorsFromClipboardCache(int32_t aClipboardType
) {
894 MOZ_ASSERT(mozilla::StaticPrefs::widget_clipboard_use_cached_data_enabled());
895 MOZ_ASSERT(nsIClipboard::IsClipboardTypeSupported(aClipboardType
));
897 const auto* clipboardCache
= GetClipboardCacheIfValid(aClipboardType
);
898 if (!clipboardCache
) {
899 return mozilla::Err(NS_ERROR_FAILURE
);
902 nsITransferable
* cachedTransferable
= clipboardCache
->GetTransferable();
903 MOZ_ASSERT(cachedTransferable
);
905 nsTArray
<nsCString
> flavors
;
906 nsresult rv
= cachedTransferable
->FlavorsTransferableCanExport(flavors
);
908 return mozilla::Err(rv
);
911 if (MOZ_CLIPBOARD_LOG_ENABLED()) {
912 MOZ_CLIPBOARD_LOG(" Cached transferable types (nums %zu)\n",
914 for (const auto& flavor
: flavors
) {
915 MOZ_CLIPBOARD_LOG(" MIME %s", flavor
.get());
919 return std::move(flavors
);
923 nsBaseClipboard::HasDataMatchingFlavors(const nsTArray
<nsCString
>& aFlavorList
,
924 int32_t aWhichClipboard
,
926 MOZ_CLIPBOARD_LOG("%s: clipboard=%d", __FUNCTION__
, aWhichClipboard
);
927 if (MOZ_CLIPBOARD_LOG_ENABLED()) {
928 MOZ_CLIPBOARD_LOG(" Asking for content clipboard=%i:\n",
930 for (const auto& flavor
: aFlavorList
) {
931 MOZ_CLIPBOARD_LOG(" MIME %s", flavor
.get());
937 if (mozilla::StaticPrefs::widget_clipboard_use_cached_data_enabled()) {
938 // First, check if we have valid data in our cached transferable.
939 auto flavorsOrError
= GetFlavorsFromClipboardCache(aWhichClipboard
);
940 if (flavorsOrError
.isOk()) {
941 for (const auto& transferableFlavor
: flavorsOrError
.unwrap()) {
942 for (const auto& flavor
: aFlavorList
) {
943 if (transferableFlavor
.Equals(flavor
)) {
944 MOZ_CLIPBOARD_LOG(" has %s", flavor
.get());
954 HasNativeClipboardDataMatchingFlavors(aFlavorList
, aWhichClipboard
);
955 if (resultOrError
.isErr()) {
957 "%s: checking native clipboard data matching flavors falied.",
959 return resultOrError
.unwrapErr();
962 *aOutResult
= resultOrError
.unwrap();
967 nsBaseClipboard::IsClipboardTypeSupported(int32_t aWhichClipboard
,
969 NS_ENSURE_ARG_POINTER(aRetval
);
970 switch (aWhichClipboard
) {
971 case kGlobalClipboard
:
972 // We always support the global clipboard.
975 case kSelectionClipboard
:
976 *aRetval
= mClipboardCaps
.supportsSelectionClipboard();
979 *aRetval
= mClipboardCaps
.supportsFindClipboard();
981 case kSelectionCache
:
982 *aRetval
= mClipboardCaps
.supportsSelectionCache();
990 void nsBaseClipboard::AsyncHasNativeClipboardDataMatchingFlavors(
991 const nsTArray
<nsCString
>& aFlavorList
, int32_t aWhichClipboard
,
992 HasMatchingFlavorsCallback
&& aCallback
) {
993 MOZ_DIAGNOSTIC_ASSERT(
994 nsIClipboard::IsClipboardTypeSupported(aWhichClipboard
));
997 "nsBaseClipboard::AsyncHasNativeClipboardDataMatchingFlavors: "
1001 nsTArray
<nsCString
> results
;
1002 for (const auto& flavor
: aFlavorList
) {
1003 auto resultOrError
= HasNativeClipboardDataMatchingFlavors(
1004 AutoTArray
<nsCString
, 1>{flavor
}, aWhichClipboard
);
1005 if (resultOrError
.isOk() && resultOrError
.unwrap()) {
1006 results
.AppendElement(flavor
);
1009 aCallback(std::move(results
));
1012 void nsBaseClipboard::AsyncGetNativeClipboardData(
1013 nsITransferable
* aTransferable
, int32_t aWhichClipboard
,
1014 GetDataCallback
&& aCallback
) {
1015 aCallback(GetNativeClipboardData(aTransferable
, aWhichClipboard
));
1018 void nsBaseClipboard::ClearClipboardCache(int32_t aClipboardType
) {
1019 MOZ_ASSERT(nsIClipboard::IsClipboardTypeSupported(aClipboardType
));
1020 const mozilla::UniquePtr
<ClipboardCache
>& cache
= mCaches
[aClipboardType
];
1025 void nsBaseClipboard::RequestUserConfirmation(
1026 int32_t aClipboardType
, const nsTArray
<nsCString
>& aFlavorList
,
1027 mozilla::dom::WindowContext
* aWindowContext
,
1028 nsIPrincipal
* aRequestingPrincipal
,
1029 nsIAsyncClipboardGetCallback
* aCallback
) {
1030 MOZ_ASSERT(nsIClipboard::IsClipboardTypeSupported(aClipboardType
));
1031 MOZ_ASSERT(aCallback
);
1033 if (!aWindowContext
) {
1034 aCallback
->OnError(NS_ERROR_FAILURE
);
1038 CanonicalBrowsingContext
* cbc
=
1039 CanonicalBrowsingContext::Cast(aWindowContext
->GetBrowsingContext());
1042 "Should not require user confirmation when access from chrome window");
1044 RefPtr
<CanonicalBrowsingContext
> chromeTop
= cbc
->TopCrossChromeBoundary();
1045 Document
* chromeDoc
= chromeTop
? chromeTop
->GetDocument() : nullptr;
1046 if (!chromeDoc
|| !chromeDoc
->HasFocus(mozilla::IgnoreErrors())) {
1047 MOZ_CLIPBOARD_LOG("%s: reject due to not in the focused window",
1049 aCallback
->OnError(NS_ERROR_FAILURE
);
1053 mozilla::dom::Element
* activeElementInChromeDoc
=
1054 chromeDoc
->GetActiveElement();
1055 if (activeElementInChromeDoc
!= cbc
->Top()->GetEmbedderElement()) {
1056 // Reject if the request is not from web content that is in the focused tab.
1057 MOZ_CLIPBOARD_LOG("%s: reject due to not in the focused tab", __FUNCTION__
);
1058 aCallback
->OnError(NS_ERROR_FAILURE
);
1062 // If there is a pending user confirmation request, check if we could reuse
1063 // it. If not, reject the request.
1064 if (sUserConfirmationRequest
) {
1065 if (sUserConfirmationRequest
->IsEqual(
1066 aClipboardType
, chromeDoc
, aRequestingPrincipal
, aWindowContext
)) {
1067 sUserConfirmationRequest
->AddClipboardGetRequest(aFlavorList
, aCallback
);
1071 aCallback
->OnError(NS_ERROR_DOM_NOT_ALLOWED_ERR
);
1075 nsresult rv
= NS_ERROR_FAILURE
;
1076 nsCOMPtr
<nsIPromptService
> promptService
=
1077 do_GetService("@mozilla.org/prompter;1", &rv
);
1078 if (NS_FAILED(rv
)) {
1079 aCallback
->OnError(NS_ERROR_DOM_NOT_ALLOWED_ERR
);
1083 RefPtr
<mozilla::dom::Promise
> promise
;
1084 if (NS_FAILED(promptService
->ConfirmUserPaste(aWindowContext
->Canonical(),
1085 getter_AddRefs(promise
)))) {
1086 aCallback
->OnError(NS_ERROR_DOM_NOT_ALLOWED_ERR
);
1090 sUserConfirmationRequest
= new UserConfirmationRequest(
1091 aClipboardType
, chromeDoc
, aRequestingPrincipal
, this, aWindowContext
);
1092 sUserConfirmationRequest
->AddClipboardGetRequest(aFlavorList
, aCallback
);
1093 promise
->AppendNativeHandler(sUserConfirmationRequest
);
1096 NS_IMPL_ISUPPORTS(nsBaseClipboard::AsyncGetClipboardData
,
1097 nsIAsyncGetClipboardData
)
1099 nsBaseClipboard::AsyncGetClipboardData::AsyncGetClipboardData(
1100 int32_t aClipboardType
, int32_t aSequenceNumber
,
1101 nsTArray
<nsCString
>&& aFlavors
, bool aFromCache
,
1102 nsBaseClipboard
* aClipboard
,
1103 mozilla::dom::WindowContext
* aRequestingWindowContext
)
1104 : mClipboardType(aClipboardType
),
1105 mSequenceNumber(aSequenceNumber
),
1106 mFlavors(std::move(aFlavors
)),
1107 mFromCache(aFromCache
),
1108 mClipboard(aClipboard
),
1109 mRequestingWindowContext(aRequestingWindowContext
) {
1110 MOZ_ASSERT(mClipboard
);
1112 mClipboard
->nsIClipboard::IsClipboardTypeSupported(mClipboardType
));
1115 NS_IMETHODIMP
nsBaseClipboard::AsyncGetClipboardData::GetValid(
1117 *aOutResult
= IsValid();
1121 NS_IMETHODIMP
nsBaseClipboard::AsyncGetClipboardData::GetFlavorList(
1122 nsTArray
<nsCString
>& aFlavors
) {
1123 aFlavors
.AppendElements(mFlavors
);
1127 NS_IMETHODIMP
nsBaseClipboard::AsyncGetClipboardData::GetData(
1128 nsITransferable
* aTransferable
,
1129 nsIAsyncClipboardRequestCallback
* aCallback
) {
1130 MOZ_CLIPBOARD_LOG("AsyncGetClipboardData::GetData: %p", this);
1132 if (!aTransferable
|| !aCallback
) {
1133 return NS_ERROR_INVALID_ARG
;
1136 nsTArray
<nsCString
> flavors
;
1137 nsresult rv
= aTransferable
->FlavorsTransferableCanImport(flavors
);
1138 if (NS_FAILED(rv
)) {
1142 // If the requested flavor is not in the list, throw an error.
1143 for (const auto& flavor
: flavors
) {
1144 if (!mFlavors
.Contains(flavor
)) {
1145 return NS_ERROR_FAILURE
;
1150 aCallback
->OnComplete(NS_ERROR_FAILURE
);
1154 MOZ_ASSERT(mClipboard
);
1156 auto contentAnalysisCallback
=
1157 mozilla::MakeRefPtr
<SafeContentAnalysisResultCallback
>(
1158 [transferable
= nsCOMPtr
{aTransferable
},
1159 callback
= nsCOMPtr
{aCallback
}](
1160 RefPtr
<nsIContentAnalysisResult
>&& aResult
) {
1161 if (aResult
->GetShouldAllowContent()) {
1162 callback
->OnComplete(NS_OK
);
1164 transferable
->ClearAllData();
1165 callback
->OnComplete(NS_ERROR_CONTENT_BLOCKED
);
1170 const auto* clipboardCache
=
1171 mClipboard
->GetClipboardCacheIfValid(mClipboardType
);
1172 // `IsValid()` above ensures we should get a valid cache and matched
1173 // sequence number here.
1174 MOZ_DIAGNOSTIC_ASSERT(clipboardCache
);
1175 MOZ_DIAGNOSTIC_ASSERT(clipboardCache
->GetSequenceNumber() ==
1177 if (NS_SUCCEEDED(clipboardCache
->GetData(aTransferable
))) {
1178 CheckClipboardContentAnalysis(mRequestingWindowContext
1179 ? mRequestingWindowContext
->Canonical()
1181 aTransferable
, contentAnalysisCallback
);
1185 // At this point we can't satisfy the request from cache data so let's look
1186 // for things other people put on the system clipboard.
1189 // Since this is an async operation, we need to check if the data is still
1190 // valid after we get the result.
1191 mClipboard
->AsyncGetNativeClipboardData(
1192 aTransferable
, mClipboardType
,
1193 [callback
= nsCOMPtr
{aCallback
}, self
= RefPtr
{this},
1194 transferable
= nsCOMPtr
{aTransferable
},
1195 contentAnalysisCallback
=
1196 std::move(contentAnalysisCallback
)](nsresult aResult
) mutable {
1197 if (NS_FAILED(aResult
)) {
1198 callback
->OnComplete(aResult
);
1201 // `IsValid()` checks the clipboard sequence number to ensure the data
1202 // we are requesting is still valid.
1203 if (!self
->IsValid()) {
1204 callback
->OnComplete(NS_ERROR_FAILURE
);
1207 CheckClipboardContentAnalysis(
1208 self
->mRequestingWindowContext
1209 ? self
->mRequestingWindowContext
->Canonical()
1211 transferable
, contentAnalysisCallback
);
1216 bool nsBaseClipboard::AsyncGetClipboardData::IsValid() {
1221 // If the data should from cache, check if cache is still valid or the
1222 // sequence numbers are matched.
1224 const auto* clipboardCache
=
1225 mClipboard
->GetClipboardCacheIfValid(mClipboardType
);
1226 if (!clipboardCache
) {
1227 mClipboard
= nullptr;
1231 return mSequenceNumber
== clipboardCache
->GetSequenceNumber();
1234 auto resultOrError
=
1235 mClipboard
->GetNativeClipboardSequenceNumber(mClipboardType
);
1236 if (resultOrError
.isErr()) {
1237 mClipboard
= nullptr;
1241 if (mSequenceNumber
!= resultOrError
.unwrap()) {
1242 mClipboard
= nullptr;
1249 nsBaseClipboard::ClipboardCache
* nsBaseClipboard::GetClipboardCacheIfValid(
1250 int32_t aClipboardType
) {
1251 MOZ_ASSERT(nsIClipboard::IsClipboardTypeSupported(aClipboardType
));
1253 const mozilla::UniquePtr
<ClipboardCache
>& cache
= mCaches
[aClipboardType
];
1256 if (!cache
->GetTransferable()) {
1257 MOZ_ASSERT(cache
->GetSequenceNumber() == -1);
1261 auto changeCountOrError
= GetNativeClipboardSequenceNumber(aClipboardType
);
1262 if (changeCountOrError
.isErr()) {
1266 if (changeCountOrError
.unwrap() != cache
->GetSequenceNumber()) {
1267 // Clipboard cache is invalid, clear it.
1275 void nsBaseClipboard::ClipboardCache::Clear() {
1276 if (mClipboardOwner
) {
1277 mClipboardOwner
->LosingOwnership(mTransferable
);
1278 mClipboardOwner
= nullptr;
1280 mTransferable
= nullptr;
1281 mSequenceNumber
= -1;
1284 nsresult
nsBaseClipboard::ClipboardCache::GetData(
1285 nsITransferable
* aTransferable
) const {
1286 MOZ_ASSERT(aTransferable
);
1287 MOZ_ASSERT(mozilla::StaticPrefs::widget_clipboard_use_cached_data_enabled());
1289 // get flavor list that includes all acceptable flavors (including ones
1290 // obtained through conversion)
1291 nsTArray
<nsCString
> flavors
;
1292 if (NS_FAILED(aTransferable
->FlavorsTransferableCanImport(flavors
))) {
1293 return NS_ERROR_FAILURE
;
1296 MOZ_ASSERT(mTransferable
);
1297 for (const auto& flavor
: flavors
) {
1298 nsCOMPtr
<nsISupports
> dataSupports
;
1299 // XXX Maybe we need special check for image as we always put the image as
1300 // "native" on the clipboard.
1301 if (NS_SUCCEEDED(mTransferable
->GetTransferData(
1302 flavor
.get(), getter_AddRefs(dataSupports
)))) {
1303 MOZ_CLIPBOARD_LOG("%s: getting %s from cache.", __FUNCTION__
,
1305 aTransferable
->SetTransferData(flavor
.get(), dataSupports
);
1306 // XXX we only read the first available type from native clipboard, so
1307 // make cache behave the same.
1312 return NS_ERROR_FAILURE
;