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/dom/BindingUtils.h"
11 #include "mozilla/dom/CanonicalBrowsingContext.h"
12 #include "mozilla/dom/Document.h"
13 #include "mozilla/dom/Promise.h"
14 #include "mozilla/dom/PromiseNativeHandler.h"
15 #include "mozilla/dom/WindowGlobalParent.h"
16 #include "mozilla/dom/WindowContext.h"
17 #include "mozilla/ErrorResult.h"
18 #include "mozilla/MoveOnlyFunction.h"
19 #include "mozilla/RefPtr.h"
20 #include "mozilla/Services.h"
21 #include "mozilla/StaticPrefs_dom.h"
22 #include "mozilla/StaticPrefs_widget.h"
23 #include "nsContentUtils.h"
24 #include "nsFocusManager.h"
25 #include "nsIClipboardOwner.h"
26 #include "nsIPromptService.h"
30 using mozilla::GenericPromise
;
31 using mozilla::LogLevel
;
32 using mozilla::UniquePtr
;
33 using mozilla::dom::BrowsingContext
;
34 using mozilla::dom::CanonicalBrowsingContext
;
35 using mozilla::dom::ClipboardCapabilities
;
36 using mozilla::dom::Document
;
38 mozilla::LazyLogModule
gWidgetClipboardLog("WidgetClipboard");
40 static const int32_t kGetAvailableFlavorsRetryCount
= 5;
44 struct ClipboardGetRequest
{
45 ClipboardGetRequest(const nsTArray
<nsCString
>& aFlavorList
,
46 nsIClipboardGetDataSnapshotCallback
* aCallback
)
47 : mFlavorList(aFlavorList
.Clone()), mCallback(aCallback
) {}
49 const nsTArray
<nsCString
> mFlavorList
;
50 const nsCOMPtr
<nsIClipboardGetDataSnapshotCallback
> mCallback
;
53 class UserConfirmationRequest final
54 : public mozilla::dom::PromiseNativeHandler
{
56 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
57 NS_DECL_CYCLE_COLLECTION_CLASS(UserConfirmationRequest
)
59 UserConfirmationRequest(nsIClipboard::ClipboardType aClipboardType
,
60 Document
* aRequestingChromeDocument
,
61 nsIPrincipal
* aRequestingPrincipal
,
62 nsBaseClipboard
* aClipboard
,
63 mozilla::dom::WindowContext
* aRequestingWindowContext
)
64 : mClipboardType(aClipboardType
),
65 mRequestingChromeDocument(aRequestingChromeDocument
),
66 mRequestingPrincipal(aRequestingPrincipal
),
67 mClipboard(aClipboard
),
68 mRequestingWindowContext(aRequestingWindowContext
) {
70 mClipboard
->nsIClipboard::IsClipboardTypeSupported(aClipboardType
));
73 void ResolvedCallback(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
74 mozilla::ErrorResult
& aRv
) override
;
76 void RejectedCallback(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
77 mozilla::ErrorResult
& aRv
) override
;
79 bool IsEqual(nsIClipboard::ClipboardType aClipboardType
,
80 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 nsIClipboard::ClipboardType
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 nsIClipboardGetDataSnapshotCallback
* 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
->GetDataSnapshotInternal(request
->mFlavorList
, mClipboardType
,
137 mRequestingWindowContext
,
142 nsTArray
<UniquePtr
<ClipboardGetRequest
>>& GetPendingClipboardGetRequests() {
143 return mPendingClipboardGetRequests
;
147 ~UserConfirmationRequest() = default;
149 const nsIClipboard::ClipboardType 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 nsIClipboard::ClipboardType aClipboardType
, nsBaseClipboard
* aClipboard
,
214 mozilla::dom::WindowContext
* aSettingWindowContext
,
215 nsIAsyncClipboardRequestCallback
* aCallback
)
216 : mClipboardType(aClipboardType
),
217 mClipboard(aClipboard
),
218 mWindowContext(aSettingWindowContext
),
219 mCallback(aCallback
) {
220 MOZ_ASSERT(mClipboard
);
222 mClipboard
->nsIClipboard::IsClipboardTypeSupported(mClipboardType
));
226 nsBaseClipboard::AsyncSetClipboardData::SetData(nsITransferable
* aTransferable
,
227 nsIClipboardOwner
* aOwner
) {
228 MOZ_CLIPBOARD_LOG("AsyncSetClipboardData::SetData (%p): clipboard=%d", this,
232 return NS_ERROR_FAILURE
;
235 if (MOZ_CLIPBOARD_LOG_ENABLED()) {
236 nsTArray
<nsCString
> flavors
;
237 if (NS_SUCCEEDED(aTransferable
->FlavorsTransferableCanImport(flavors
))) {
238 for (const auto& flavor
: flavors
) {
239 MOZ_CLIPBOARD_LOG(" MIME %s", flavor
.get());
244 MOZ_ASSERT(mClipboard
);
246 mClipboard
->nsIClipboard::IsClipboardTypeSupported(mClipboardType
));
247 MOZ_DIAGNOSTIC_ASSERT(mClipboard
->mPendingWriteRequests
[mClipboardType
] ==
250 RefPtr
<AsyncSetClipboardData
> request
=
251 std::move(mClipboard
->mPendingWriteRequests
[mClipboardType
]);
252 nsresult rv
= mClipboard
->SetData(aTransferable
, aOwner
, mClipboardType
,
254 MaybeNotifyCallback(rv
);
260 nsBaseClipboard::AsyncSetClipboardData::Abort(nsresult aReason
) {
261 // Note: This may be called during destructor, so it should not attempt to
262 // take a reference to mClipboard.
264 if (!IsValid() || !NS_FAILED(aReason
)) {
265 return NS_ERROR_FAILURE
;
268 MaybeNotifyCallback(aReason
);
272 void nsBaseClipboard::AsyncSetClipboardData::MaybeNotifyCallback(
274 // Note: This may be called during destructor, so it should not attempt to
275 // take a reference to mClipboard.
277 MOZ_ASSERT(IsValid());
278 if (nsCOMPtr
<nsIAsyncClipboardRequestCallback
> callback
=
279 mCallback
.forget()) {
280 callback
->OnComplete(aResult
);
282 // Once the callback is notified, setData should not be allowed, so invalidate
284 mClipboard
= nullptr;
287 void nsBaseClipboard::RejectPendingAsyncSetDataRequestIfAny(
288 ClipboardType aClipboardType
) {
289 MOZ_ASSERT(nsIClipboard::IsClipboardTypeSupported(aClipboardType
));
290 auto& request
= mPendingWriteRequests
[aClipboardType
];
292 request
->Abort(NS_ERROR_ABORT
);
297 NS_IMETHODIMP
nsBaseClipboard::AsyncSetData(
298 ClipboardType aWhichClipboard
,
299 mozilla::dom::WindowContext
* aSettingWindowContext
,
300 nsIAsyncClipboardRequestCallback
* aCallback
,
301 nsIAsyncSetClipboardData
** _retval
) {
302 MOZ_CLIPBOARD_LOG("%s: clipboard=%d", __FUNCTION__
, aWhichClipboard
);
305 if (!nsIClipboard::IsClipboardTypeSupported(aWhichClipboard
)) {
306 MOZ_CLIPBOARD_LOG("%s: clipboard %d is not supported.", __FUNCTION__
,
308 return NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
311 // Reject existing pending AsyncSetData request if any.
312 RejectPendingAsyncSetDataRequestIfAny(aWhichClipboard
);
314 // Create a new AsyncSetClipboardData.
315 RefPtr
<AsyncSetClipboardData
> request
=
316 mozilla::MakeRefPtr
<AsyncSetClipboardData
>(
317 aWhichClipboard
, this, aSettingWindowContext
, aCallback
);
318 mPendingWriteRequests
[aWhichClipboard
] = request
;
319 request
.forget(_retval
);
323 nsBaseClipboard::nsBaseClipboard(const ClipboardCapabilities
& aClipboardCaps
)
324 : mClipboardCaps(aClipboardCaps
) {
325 using mozilla::MakeUnique
;
326 // Initialize clipboard cache.
327 mCaches
[kGlobalClipboard
] = MakeUnique
<ClipboardCache
>();
328 if (mClipboardCaps
.supportsSelectionClipboard()) {
329 mCaches
[kSelectionClipboard
] = MakeUnique
<ClipboardCache
>();
331 if (mClipboardCaps
.supportsFindClipboard()) {
332 mCaches
[kFindClipboard
] = MakeUnique
<ClipboardCache
>();
334 if (mClipboardCaps
.supportsSelectionCache()) {
335 mCaches
[kSelectionCache
] = MakeUnique
<ClipboardCache
>();
339 nsBaseClipboard::~nsBaseClipboard() {
340 for (auto& request
: mPendingWriteRequests
) {
342 request
->Abort(NS_ERROR_ABORT
);
348 NS_IMPL_ISUPPORTS(nsBaseClipboard
, nsIClipboard
)
351 * Sets the transferable object
354 NS_IMETHODIMP
nsBaseClipboard::SetData(
355 nsITransferable
* aTransferable
, nsIClipboardOwner
* aOwner
,
356 ClipboardType aWhichClipboard
,
357 mozilla::dom::WindowContext
* aWindowContext
) {
358 NS_ASSERTION(aTransferable
, "clipboard given a null transferable");
360 MOZ_CLIPBOARD_LOG("%s: clipboard=%d", __FUNCTION__
, aWhichClipboard
);
362 if (!nsIClipboard::IsClipboardTypeSupported(aWhichClipboard
)) {
363 MOZ_CLIPBOARD_LOG("%s: clipboard %d is not supported.", __FUNCTION__
,
365 return NS_ERROR_FAILURE
;
368 if (MOZ_CLIPBOARD_LOG_ENABLED()) {
369 nsTArray
<nsCString
> flavors
;
370 if (NS_SUCCEEDED(aTransferable
->FlavorsTransferableCanImport(flavors
))) {
371 for (const auto& flavor
: flavors
) {
372 MOZ_CLIPBOARD_LOG(" MIME %s", flavor
.get());
377 const auto& clipboardCache
= mCaches
[aWhichClipboard
];
378 MOZ_ASSERT(clipboardCache
);
379 if (aTransferable
== clipboardCache
->GetTransferable() &&
380 aOwner
== clipboardCache
->GetClipboardOwner()) {
381 MOZ_CLIPBOARD_LOG("%s: skipping update.", __FUNCTION__
);
385 clipboardCache
->Clear();
387 nsresult rv
= NS_ERROR_FAILURE
;
389 mIgnoreEmptyNotification
= true;
390 // Reject existing pending asyncSetData request if any.
391 RejectPendingAsyncSetDataRequestIfAny(aWhichClipboard
);
392 rv
= SetNativeClipboardData(aTransferable
, aWhichClipboard
);
393 mIgnoreEmptyNotification
= false;
396 MOZ_CLIPBOARD_LOG("%s: setting native clipboard data failed.",
401 auto result
= GetNativeClipboardSequenceNumber(aWhichClipboard
);
402 if (result
.isErr()) {
403 MOZ_CLIPBOARD_LOG("%s: getting native clipboard change count failed.",
405 return result
.unwrapErr();
408 clipboardCache
->Update(aTransferable
, aOwner
, result
.unwrap(),
410 ? mozilla::Some(aWindowContext
->InnerWindowId())
411 : mozilla::Nothing());
415 nsresult
nsBaseClipboard::GetDataFromClipboardCache(
416 nsITransferable
* aTransferable
, ClipboardType aClipboardType
) {
417 MOZ_ASSERT(aTransferable
);
418 MOZ_ASSERT(mozilla::StaticPrefs::widget_clipboard_use_cached_data_enabled());
420 const auto* clipboardCache
= GetClipboardCacheIfValid(aClipboardType
);
421 if (!clipboardCache
) {
422 return NS_ERROR_FAILURE
;
424 return clipboardCache
->GetData(aTransferable
);
428 * Gets the transferable object from system clipboard.
430 NS_IMETHODIMP
nsBaseClipboard::GetData(
431 nsITransferable
* aTransferable
, ClipboardType aWhichClipboard
,
432 mozilla::dom::WindowContext
* aWindowContext
) {
433 MOZ_CLIPBOARD_LOG("%s: clipboard=%d", __FUNCTION__
, aWhichClipboard
);
435 if (!aTransferable
) {
436 NS_ASSERTION(false, "clipboard given a null transferable");
437 return NS_ERROR_FAILURE
;
440 if (!nsIClipboard::IsClipboardTypeSupported(aWhichClipboard
)) {
441 MOZ_CLIPBOARD_LOG("%s: clipboard %d is not supported.", __FUNCTION__
,
443 return NS_ERROR_FAILURE
;
446 if (mozilla::StaticPrefs::widget_clipboard_use_cached_data_enabled()) {
447 // If we were the last ones to put something on the native clipboard, then
448 // just use the cached transferable. Otherwise clear it because it isn't
449 // relevant any more.
451 GetDataFromClipboardCache(aTransferable
, aWhichClipboard
))) {
452 // maybe try to fill in more types? Is there a point?
453 if (!mozilla::contentanalysis::ContentAnalysis::
454 CheckClipboardContentAnalysisSync(
455 this, aWindowContext
->Canonical(), aTransferable
,
457 aTransferable
->ClearAllData();
458 return NS_ERROR_CONTENT_BLOCKED
;
463 // at this point we can't satisfy the request from cache data so let's look
464 // for things other people put on the system clipboard
466 nsresult rv
= GetNativeClipboardData(aTransferable
, aWhichClipboard
);
470 if (!mozilla::contentanalysis::ContentAnalysis::
471 CheckClipboardContentAnalysisSync(this, aWindowContext
->Canonical(),
472 aTransferable
, aWhichClipboard
)) {
473 aTransferable
->ClearAllData();
474 return NS_ERROR_CONTENT_BLOCKED
;
479 void nsBaseClipboard::MaybeRetryGetAvailableFlavors(
480 const nsTArray
<nsCString
>& aFlavorList
, ClipboardType aWhichClipboard
,
481 nsIClipboardGetDataSnapshotCallback
* aCallback
, int32_t aRetryCount
,
482 mozilla::dom::WindowContext
* aRequestingWindowContext
) {
483 // Note we have to get the clipboard sequence number first before the actual
484 // read. This is to use it to verify the clipboard data is still the one we
485 // try to read, instead of the later state.
486 auto sequenceNumberOrError
=
487 GetNativeClipboardSequenceNumber(aWhichClipboard
);
488 if (sequenceNumberOrError
.isErr()) {
489 MOZ_CLIPBOARD_LOG("%s: unable to get sequence number for clipboard %d.",
490 __FUNCTION__
, aWhichClipboard
);
491 aCallback
->OnError(sequenceNumberOrError
.unwrapErr());
495 int32_t sequenceNumber
= sequenceNumberOrError
.unwrap();
496 AsyncHasNativeClipboardDataMatchingFlavors(
497 aFlavorList
, aWhichClipboard
,
498 [self
= RefPtr
{this}, callback
= nsCOMPtr
{aCallback
}, aWhichClipboard
,
499 aRetryCount
, flavorList
= aFlavorList
.Clone(), sequenceNumber
,
500 requestingWindowContext
=
501 RefPtr
{aRequestingWindowContext
}](auto aFlavorsOrError
) {
502 if (aFlavorsOrError
.isErr()) {
504 "%s: unable to get available flavors for clipboard %d.",
505 __FUNCTION__
, aWhichClipboard
);
506 callback
->OnError(aFlavorsOrError
.unwrapErr());
510 auto sequenceNumberOrError
=
511 self
->GetNativeClipboardSequenceNumber(aWhichClipboard
);
512 if (sequenceNumberOrError
.isErr()) {
514 "%s: unable to get sequence number for clipboard %d.",
515 __FUNCTION__
, aWhichClipboard
);
516 callback
->OnError(sequenceNumberOrError
.unwrapErr());
520 if (sequenceNumber
== sequenceNumberOrError
.unwrap()) {
521 auto clipboardDataSnapshot
=
522 mozilla::MakeRefPtr
<ClipboardDataSnapshot
>(
523 aWhichClipboard
, sequenceNumber
,
524 std::move(aFlavorsOrError
.unwrap()), false, self
,
525 requestingWindowContext
);
526 callback
->OnSuccess(clipboardDataSnapshot
);
530 if (aRetryCount
> 0) {
532 "%s: clipboard=%d, ignore the data due to the sequence number "
533 "doesn't match, retry (%d) ..",
534 __FUNCTION__
, aWhichClipboard
, aRetryCount
);
535 self
->MaybeRetryGetAvailableFlavors(flavorList
, aWhichClipboard
,
536 callback
, aRetryCount
- 1,
537 requestingWindowContext
);
541 MOZ_DIAGNOSTIC_CRASH("How can this happen?!?");
542 callback
->OnError(NS_ERROR_FAILURE
);
546 NS_IMETHODIMP
nsBaseClipboard::GetDataSnapshot(
547 const nsTArray
<nsCString
>& aFlavorList
, ClipboardType aWhichClipboard
,
548 mozilla::dom::WindowContext
* aRequestingWindowContext
,
549 nsIPrincipal
* aRequestingPrincipal
,
550 nsIClipboardGetDataSnapshotCallback
* aCallback
) {
551 MOZ_CLIPBOARD_LOG("%s: clipboard=%d", __FUNCTION__
, aWhichClipboard
);
553 if (!aCallback
|| !aRequestingPrincipal
|| aFlavorList
.IsEmpty()) {
554 return NS_ERROR_INVALID_ARG
;
557 if (!nsIClipboard::IsClipboardTypeSupported(aWhichClipboard
)) {
558 MOZ_CLIPBOARD_LOG("%s: clipboard %d is not supported.", __FUNCTION__
,
560 return NS_ERROR_FAILURE
;
563 // We want to disable security check for automated tests that have the pref
564 // set to true, or extension that have clipboard read permission.
565 if (mozilla::StaticPrefs::
566 dom_events_testing_asyncClipboard_DoNotUseDirectly() ||
567 nsContentUtils::PrincipalHasPermission(*aRequestingPrincipal
,
568 nsGkAtoms::clipboardRead
)) {
569 GetDataSnapshotInternal(aFlavorList
, aWhichClipboard
,
570 aRequestingWindowContext
, aCallback
);
574 // If cache data is valid, we are the last ones to put something on the native
575 // clipboard, then check if the data is from the same-origin page,
576 if (auto* clipboardCache
= GetClipboardCacheIfValid(aWhichClipboard
)) {
577 nsCOMPtr
<nsITransferable
> trans
= clipboardCache
->GetTransferable();
580 if (nsCOMPtr
<nsIPrincipal
> principal
= trans
->GetDataPrincipal()) {
581 if (aRequestingPrincipal
->Subsumes(principal
)) {
582 MOZ_CLIPBOARD_LOG("%s: native clipboard data is from same-origin page.",
584 GetDataSnapshotInternal(aFlavorList
, aWhichClipboard
,
585 aRequestingWindowContext
, aCallback
);
591 // TODO: enable showing the "Paste" button in this case; see bug 1773681.
592 if (aRequestingPrincipal
->GetIsAddonOrExpandedAddonPrincipal()) {
593 MOZ_CLIPBOARD_LOG("%s: Addon without read permission.", __FUNCTION__
);
594 return aCallback
->OnError(NS_ERROR_FAILURE
);
597 RequestUserConfirmation(aWhichClipboard
, aFlavorList
,
598 aRequestingWindowContext
, aRequestingPrincipal
,
603 already_AddRefed
<nsIClipboardDataSnapshot
>
604 nsBaseClipboard::MaybeCreateGetRequestFromClipboardCache(
605 const nsTArray
<nsCString
>& aFlavorList
, ClipboardType aClipboardType
,
606 mozilla::dom::WindowContext
* aRequestingWindowContext
) {
607 MOZ_DIAGNOSTIC_ASSERT(nsIClipboard::IsClipboardTypeSupported(aClipboardType
));
609 if (!mozilla::StaticPrefs::widget_clipboard_use_cached_data_enabled()) {
613 // If we were the last ones to put something on the native clipboard, then
614 // just use the cached transferable. Otherwise clear it because it isn't
615 // relevant any more.
616 ClipboardCache
* clipboardCache
= GetClipboardCacheIfValid(aClipboardType
);
617 if (!clipboardCache
) {
621 nsITransferable
* cachedTransferable
= clipboardCache
->GetTransferable();
622 MOZ_ASSERT(cachedTransferable
);
624 nsTArray
<nsCString
> transferableFlavors
;
625 if (NS_FAILED(cachedTransferable
->FlavorsTransferableCanExport(
626 transferableFlavors
))) {
630 nsTArray
<nsCString
> results
;
631 for (const auto& flavor
: aFlavorList
) {
632 for (const auto& transferableFlavor
: transferableFlavors
) {
633 // XXX We need special check for image as we always put the
634 // image as "native" on the clipboard.
635 if (transferableFlavor
.Equals(flavor
) ||
636 (transferableFlavor
.Equals(kNativeImageMime
) &&
637 nsContentUtils::IsFlavorImage(flavor
))) {
638 MOZ_CLIPBOARD_LOG(" has %s", flavor
.get());
639 results
.AppendElement(flavor
);
644 // XXX Do we need to check system clipboard for the flavors that cannot
645 // be found in cache?
646 return mozilla::MakeAndAddRef
<ClipboardDataSnapshot
>(
647 aClipboardType
, clipboardCache
->GetSequenceNumber(), std::move(results
),
648 true /* aFromCache */, this, aRequestingWindowContext
);
651 void nsBaseClipboard::GetDataSnapshotInternal(
652 const nsTArray
<nsCString
>& aFlavorList
, ClipboardType aClipboardType
,
653 mozilla::dom::WindowContext
* aRequestingWindowContext
,
654 nsIClipboardGetDataSnapshotCallback
* aCallback
) {
655 MOZ_ASSERT(nsIClipboard::IsClipboardTypeSupported(aClipboardType
));
657 if (nsCOMPtr
<nsIClipboardDataSnapshot
> clipboardDataSnapshot
=
658 MaybeCreateGetRequestFromClipboardCache(aFlavorList
, aClipboardType
,
659 aRequestingWindowContext
)) {
660 aCallback
->OnSuccess(clipboardDataSnapshot
);
664 // At this point we can't satisfy the request from cache data so let's
665 // look for things other people put on the system clipboard.
666 MaybeRetryGetAvailableFlavors(aFlavorList
, aClipboardType
, aCallback
,
667 kGetAvailableFlavorsRetryCount
,
668 aRequestingWindowContext
);
671 NS_IMETHODIMP
nsBaseClipboard::GetDataSnapshotSync(
672 const nsTArray
<nsCString
>& aFlavorList
, ClipboardType aWhichClipboard
,
673 mozilla::dom::WindowContext
* aRequestingWindowContext
,
674 nsIClipboardDataSnapshot
** _retval
) {
675 MOZ_CLIPBOARD_LOG("%s: clipboard=%d", __FUNCTION__
, aWhichClipboard
);
679 if (aFlavorList
.IsEmpty()) {
680 return NS_ERROR_INVALID_ARG
;
683 if (!nsIClipboard::IsClipboardTypeSupported(aWhichClipboard
)) {
684 MOZ_CLIPBOARD_LOG("%s: clipboard %d is not supported.", __FUNCTION__
,
686 return NS_ERROR_FAILURE
;
689 if (nsCOMPtr
<nsIClipboardDataSnapshot
> clipboardDataSnapshot
=
690 MaybeCreateGetRequestFromClipboardCache(aFlavorList
, aWhichClipboard
,
691 aRequestingWindowContext
)) {
692 clipboardDataSnapshot
.forget(_retval
);
696 auto sequenceNumberOrError
=
697 GetNativeClipboardSequenceNumber(aWhichClipboard
);
698 if (sequenceNumberOrError
.isErr()) {
699 MOZ_CLIPBOARD_LOG("%s: unable to get sequence number for clipboard %d.",
700 __FUNCTION__
, aWhichClipboard
);
701 return sequenceNumberOrError
.unwrapErr();
704 nsTArray
<nsCString
> results
;
705 for (const auto& flavor
: aFlavorList
) {
706 auto resultOrError
= HasNativeClipboardDataMatchingFlavors(
707 AutoTArray
<nsCString
, 1>{flavor
}, aWhichClipboard
);
708 if (resultOrError
.isOk() && resultOrError
.unwrap()) {
709 results
.AppendElement(flavor
);
714 mozilla::MakeAndAddRef
<ClipboardDataSnapshot
>(
715 aWhichClipboard
, sequenceNumberOrError
.unwrap(), std::move(results
),
716 false /* aFromCache */, this, aRequestingWindowContext
)
721 NS_IMETHODIMP
nsBaseClipboard::EmptyClipboard(ClipboardType aWhichClipboard
) {
722 MOZ_CLIPBOARD_LOG("%s: clipboard=%d", __FUNCTION__
, aWhichClipboard
);
724 if (!nsIClipboard::IsClipboardTypeSupported(aWhichClipboard
)) {
725 MOZ_CLIPBOARD_LOG("%s: clipboard %d is not supported.", __FUNCTION__
,
727 return NS_ERROR_FAILURE
;
730 EmptyNativeClipboardData(aWhichClipboard
);
732 const auto& clipboardCache
= mCaches
[aWhichClipboard
];
733 MOZ_ASSERT(clipboardCache
);
735 if (mIgnoreEmptyNotification
) {
736 MOZ_DIAGNOSTIC_ASSERT(!clipboardCache
->GetTransferable() &&
737 !clipboardCache
->GetClipboardOwner() &&
738 clipboardCache
->GetSequenceNumber() == -1,
739 "How did we have data in clipboard cache here?");
743 clipboardCache
->Clear();
748 mozilla::Result
<nsTArray
<nsCString
>, nsresult
>
749 nsBaseClipboard::GetFlavorsFromClipboardCache(ClipboardType aClipboardType
) {
750 MOZ_ASSERT(mozilla::StaticPrefs::widget_clipboard_use_cached_data_enabled());
751 MOZ_ASSERT(nsIClipboard::IsClipboardTypeSupported(aClipboardType
));
753 const auto* clipboardCache
= GetClipboardCacheIfValid(aClipboardType
);
754 if (!clipboardCache
) {
755 return mozilla::Err(NS_ERROR_FAILURE
);
758 nsITransferable
* cachedTransferable
= clipboardCache
->GetTransferable();
759 MOZ_ASSERT(cachedTransferable
);
761 nsTArray
<nsCString
> flavors
;
762 nsresult rv
= cachedTransferable
->FlavorsTransferableCanExport(flavors
);
764 return mozilla::Err(rv
);
767 if (MOZ_CLIPBOARD_LOG_ENABLED()) {
768 MOZ_CLIPBOARD_LOG(" Cached transferable types (nums %zu)\n",
770 for (const auto& flavor
: flavors
) {
771 MOZ_CLIPBOARD_LOG(" MIME %s", flavor
.get());
775 return std::move(flavors
);
779 nsBaseClipboard::HasDataMatchingFlavors(const nsTArray
<nsCString
>& aFlavorList
,
780 ClipboardType aWhichClipboard
,
782 MOZ_CLIPBOARD_LOG("%s: clipboard=%d", __FUNCTION__
, aWhichClipboard
);
784 if (!nsIClipboard::IsClipboardTypeSupported(aWhichClipboard
)) {
785 MOZ_CLIPBOARD_LOG("%s: clipboard %d is not supported.", __FUNCTION__
,
787 return NS_ERROR_FAILURE
;
790 if (MOZ_CLIPBOARD_LOG_ENABLED()) {
791 MOZ_CLIPBOARD_LOG(" Asking for content clipboard=%i:\n",
793 for (const auto& flavor
: aFlavorList
) {
794 MOZ_CLIPBOARD_LOG(" MIME %s", flavor
.get());
800 if (mozilla::StaticPrefs::widget_clipboard_use_cached_data_enabled()) {
801 // First, check if we have valid data in our cached transferable.
802 auto flavorsOrError
= GetFlavorsFromClipboardCache(aWhichClipboard
);
803 if (flavorsOrError
.isOk()) {
804 for (const auto& transferableFlavor
: flavorsOrError
.unwrap()) {
805 for (const auto& flavor
: aFlavorList
) {
806 if (transferableFlavor
.Equals(flavor
)) {
807 MOZ_CLIPBOARD_LOG(" has %s", flavor
.get());
817 HasNativeClipboardDataMatchingFlavors(aFlavorList
, aWhichClipboard
);
818 if (resultOrError
.isErr()) {
820 "%s: checking native clipboard data matching flavors falied.",
822 return resultOrError
.unwrapErr();
825 *aOutResult
= resultOrError
.unwrap();
830 nsBaseClipboard::IsClipboardTypeSupported(ClipboardType aWhichClipboard
,
832 NS_ENSURE_ARG_POINTER(aRetval
);
833 switch (aWhichClipboard
) {
834 case kGlobalClipboard
:
835 // We always support the global clipboard.
838 case kSelectionClipboard
:
839 *aRetval
= mClipboardCaps
.supportsSelectionClipboard();
842 *aRetval
= mClipboardCaps
.supportsFindClipboard();
844 case kSelectionCache
:
845 *aRetval
= mClipboardCaps
.supportsSelectionCache();
853 void nsBaseClipboard::AsyncHasNativeClipboardDataMatchingFlavors(
854 const nsTArray
<nsCString
>& aFlavorList
, ClipboardType aWhichClipboard
,
855 HasMatchingFlavorsCallback
&& aCallback
) {
856 MOZ_DIAGNOSTIC_ASSERT(
857 nsIClipboard::IsClipboardTypeSupported(aWhichClipboard
));
860 "nsBaseClipboard::AsyncHasNativeClipboardDataMatchingFlavors: "
864 nsTArray
<nsCString
> results
;
865 for (const auto& flavor
: aFlavorList
) {
866 auto resultOrError
= HasNativeClipboardDataMatchingFlavors(
867 AutoTArray
<nsCString
, 1>{flavor
}, aWhichClipboard
);
868 if (resultOrError
.isOk() && resultOrError
.unwrap()) {
869 results
.AppendElement(flavor
);
872 aCallback(std::move(results
));
875 void nsBaseClipboard::AsyncGetNativeClipboardData(
876 nsITransferable
* aTransferable
, ClipboardType aWhichClipboard
,
877 GetDataCallback
&& aCallback
) {
878 aCallback(GetNativeClipboardData(aTransferable
, aWhichClipboard
));
881 void nsBaseClipboard::ClearClipboardCache(ClipboardType aClipboardType
) {
882 MOZ_ASSERT(nsIClipboard::IsClipboardTypeSupported(aClipboardType
));
883 const mozilla::UniquePtr
<ClipboardCache
>& cache
= mCaches
[aClipboardType
];
888 void nsBaseClipboard::RequestUserConfirmation(
889 ClipboardType aClipboardType
, const nsTArray
<nsCString
>& aFlavorList
,
890 mozilla::dom::WindowContext
* aWindowContext
,
891 nsIPrincipal
* aRequestingPrincipal
,
892 nsIClipboardGetDataSnapshotCallback
* aCallback
) {
893 MOZ_ASSERT(nsIClipboard::IsClipboardTypeSupported(aClipboardType
));
894 MOZ_ASSERT(aCallback
);
896 if (!aWindowContext
) {
897 aCallback
->OnError(NS_ERROR_FAILURE
);
901 CanonicalBrowsingContext
* cbc
=
902 CanonicalBrowsingContext::Cast(aWindowContext
->GetBrowsingContext());
905 "Should not require user confirmation when access from chrome window");
907 RefPtr
<CanonicalBrowsingContext
> chromeTop
= cbc
->TopCrossChromeBoundary();
908 Document
* chromeDoc
= chromeTop
? chromeTop
->GetDocument() : nullptr;
909 if (!chromeDoc
|| !chromeDoc
->HasFocus(mozilla::IgnoreErrors())) {
910 MOZ_CLIPBOARD_LOG("%s: reject due to not in the focused window",
912 aCallback
->OnError(NS_ERROR_FAILURE
);
916 mozilla::dom::Element
* activeElementInChromeDoc
=
917 chromeDoc
->GetActiveElement();
918 if (activeElementInChromeDoc
!= cbc
->Top()->GetEmbedderElement()) {
919 // Reject if the request is not from web content that is in the focused tab.
920 MOZ_CLIPBOARD_LOG("%s: reject due to not in the focused tab", __FUNCTION__
);
921 aCallback
->OnError(NS_ERROR_FAILURE
);
925 // If there is a pending user confirmation request, check if we could reuse
926 // it. If not, reject the request.
927 if (sUserConfirmationRequest
) {
928 if (sUserConfirmationRequest
->IsEqual(
929 aClipboardType
, chromeDoc
, aRequestingPrincipal
, aWindowContext
)) {
930 sUserConfirmationRequest
->AddClipboardGetRequest(aFlavorList
, aCallback
);
934 aCallback
->OnError(NS_ERROR_DOM_NOT_ALLOWED_ERR
);
938 nsresult rv
= NS_ERROR_FAILURE
;
939 nsCOMPtr
<nsIPromptService
> promptService
=
940 do_GetService("@mozilla.org/prompter;1", &rv
);
942 aCallback
->OnError(NS_ERROR_DOM_NOT_ALLOWED_ERR
);
946 RefPtr
<mozilla::dom::Promise
> promise
;
947 if (NS_FAILED(promptService
->ConfirmUserPaste(aWindowContext
->Canonical(),
948 getter_AddRefs(promise
)))) {
949 aCallback
->OnError(NS_ERROR_DOM_NOT_ALLOWED_ERR
);
953 sUserConfirmationRequest
= new UserConfirmationRequest(
954 aClipboardType
, chromeDoc
, aRequestingPrincipal
, this, aWindowContext
);
955 sUserConfirmationRequest
->AddClipboardGetRequest(aFlavorList
, aCallback
);
956 promise
->AppendNativeHandler(sUserConfirmationRequest
);
959 NS_IMPL_ISUPPORTS(nsBaseClipboard::ClipboardDataSnapshot
,
960 nsIClipboardDataSnapshot
)
962 nsBaseClipboard::ClipboardDataSnapshot::ClipboardDataSnapshot(
963 nsIClipboard::ClipboardType aClipboardType
, int32_t aSequenceNumber
,
964 nsTArray
<nsCString
>&& aFlavors
, bool aFromCache
,
965 nsBaseClipboard
* aClipboard
,
966 mozilla::dom::WindowContext
* aRequestingWindowContext
)
967 : mClipboardType(aClipboardType
),
968 mSequenceNumber(aSequenceNumber
),
969 mFlavors(std::move(aFlavors
)),
970 mFromCache(aFromCache
),
971 mClipboard(aClipboard
),
972 mRequestingWindowContext(aRequestingWindowContext
) {
973 MOZ_ASSERT(mClipboard
);
975 mClipboard
->nsIClipboard::IsClipboardTypeSupported(mClipboardType
));
978 NS_IMETHODIMP
nsBaseClipboard::ClipboardDataSnapshot::GetValid(
980 *aOutResult
= IsValid();
984 NS_IMETHODIMP
nsBaseClipboard::ClipboardDataSnapshot::GetFlavorList(
985 nsTArray
<nsCString
>& aFlavors
) {
986 aFlavors
.AppendElements(mFlavors
);
990 NS_IMETHODIMP
nsBaseClipboard::ClipboardDataSnapshot::GetData(
991 nsITransferable
* aTransferable
,
992 nsIAsyncClipboardRequestCallback
* aCallback
) {
993 MOZ_CLIPBOARD_LOG("ClipboardDataSnapshot::GetData: %p", this);
995 if (!aTransferable
|| !aCallback
) {
996 return NS_ERROR_INVALID_ARG
;
999 nsTArray
<nsCString
> flavors
;
1000 nsresult rv
= aTransferable
->FlavorsTransferableCanImport(flavors
);
1001 if (NS_FAILED(rv
)) {
1005 // If the requested flavor is not in the list, throw an error.
1006 for (const auto& flavor
: flavors
) {
1007 if (!mFlavors
.Contains(flavor
)) {
1008 return NS_ERROR_FAILURE
;
1013 aCallback
->OnComplete(NS_ERROR_NOT_AVAILABLE
);
1017 MOZ_ASSERT(mClipboard
);
1019 auto contentAnalysisCallback
=
1020 mozilla::MakeRefPtr
<mozilla::contentanalysis::ContentAnalysis::
1021 SafeContentAnalysisResultCallback
>(
1022 [transferable
= nsCOMPtr
{aTransferable
},
1023 callback
= nsCOMPtr
{aCallback
}](
1024 RefPtr
<nsIContentAnalysisResult
>&& aResult
) {
1025 if (aResult
->GetShouldAllowContent()) {
1026 callback
->OnComplete(NS_OK
);
1028 transferable
->ClearAllData();
1029 callback
->OnComplete(NS_ERROR_CONTENT_BLOCKED
);
1034 const auto* clipboardCache
=
1035 mClipboard
->GetClipboardCacheIfValid(mClipboardType
);
1036 // `IsValid()` above ensures we should get a valid cache and matched
1037 // sequence number here.
1038 MOZ_DIAGNOSTIC_ASSERT(clipboardCache
);
1039 MOZ_DIAGNOSTIC_ASSERT(clipboardCache
->GetSequenceNumber() ==
1041 if (NS_SUCCEEDED(clipboardCache
->GetData(aTransferable
))) {
1042 mozilla::contentanalysis::ContentAnalysis::CheckClipboardContentAnalysis(
1044 mRequestingWindowContext
? mRequestingWindowContext
->Canonical()
1046 aTransferable
, mClipboardType
, contentAnalysisCallback
);
1050 // At this point we can't satisfy the request from cache data so let's look
1051 // for things other people put on the system clipboard.
1054 // Since this is an async operation, we need to check if the data is still
1055 // valid after we get the result.
1056 mClipboard
->AsyncGetNativeClipboardData(
1057 aTransferable
, mClipboardType
,
1058 [callback
= nsCOMPtr
{aCallback
}, self
= RefPtr
{this},
1059 transferable
= nsCOMPtr
{aTransferable
},
1060 contentAnalysisCallback
=
1061 std::move(contentAnalysisCallback
)](nsresult aResult
) mutable {
1062 if (NS_FAILED(aResult
)) {
1063 callback
->OnComplete(aResult
);
1066 // `IsValid()` checks the clipboard sequence number to ensure the data
1067 // we are requesting is still valid.
1068 if (!self
->IsValid()) {
1069 callback
->OnComplete(NS_ERROR_NOT_AVAILABLE
);
1072 mozilla::contentanalysis::ContentAnalysis::
1073 CheckClipboardContentAnalysis(
1075 self
->mRequestingWindowContext
1076 ? self
->mRequestingWindowContext
->Canonical()
1078 transferable
, self
->mClipboardType
, contentAnalysisCallback
);
1083 NS_IMETHODIMP
nsBaseClipboard::ClipboardDataSnapshot::GetDataSync(
1084 nsITransferable
* aTransferable
) {
1085 MOZ_CLIPBOARD_LOG("ClipboardDataSnapshot::GetDataSync: %p", this);
1087 if (!aTransferable
) {
1088 return NS_ERROR_INVALID_ARG
;
1091 nsTArray
<nsCString
> flavors
;
1092 nsresult rv
= aTransferable
->FlavorsTransferableCanImport(flavors
);
1093 if (NS_FAILED(rv
)) {
1097 // If the requested flavor is not in the list, throw an error.
1098 for (const auto& flavor
: flavors
) {
1099 if (!mFlavors
.Contains(flavor
)) {
1100 return NS_ERROR_FAILURE
;
1105 return NS_ERROR_NOT_AVAILABLE
;
1108 MOZ_ASSERT(mClipboard
);
1111 const auto* clipboardCache
=
1112 mClipboard
->GetClipboardCacheIfValid(mClipboardType
);
1113 // `IsValid()` above ensures we should get a valid cache and matched
1114 // sequence number here.
1115 MOZ_DIAGNOSTIC_ASSERT(clipboardCache
);
1116 MOZ_DIAGNOSTIC_ASSERT(clipboardCache
->GetSequenceNumber() ==
1118 if (NS_SUCCEEDED(clipboardCache
->GetData(aTransferable
))) {
1119 bool shouldAllowContent
= mozilla::contentanalysis::ContentAnalysis::
1120 CheckClipboardContentAnalysisSync(
1122 mRequestingWindowContext
? mRequestingWindowContext
->Canonical()
1124 aTransferable
, mClipboardType
);
1125 if (shouldAllowContent
) {
1128 aTransferable
->ClearAllData();
1129 return NS_ERROR_CONTENT_BLOCKED
;
1132 // At this point we can't satisfy the request from cache data so let's look
1133 // for things other people put on the system clipboard.
1136 rv
= mClipboard
->GetNativeClipboardData(aTransferable
, mClipboardType
);
1137 if (NS_FAILED(rv
)) {
1141 bool shouldAllowContent
= mozilla::contentanalysis::ContentAnalysis::
1142 CheckClipboardContentAnalysisSync(
1144 mRequestingWindowContext
? mRequestingWindowContext
->Canonical()
1146 aTransferable
, mClipboardType
);
1147 if (shouldAllowContent
) {
1150 aTransferable
->ClearAllData();
1151 return NS_ERROR_CONTENT_BLOCKED
;
1154 bool nsBaseClipboard::ClipboardDataSnapshot::IsValid() {
1159 // If the data should from cache, check if cache is still valid or the
1160 // sequence numbers are matched.
1162 const auto* clipboardCache
=
1163 mClipboard
->GetClipboardCacheIfValid(mClipboardType
);
1164 if (!clipboardCache
) {
1165 mClipboard
= nullptr;
1169 return mSequenceNumber
== clipboardCache
->GetSequenceNumber();
1172 auto resultOrError
=
1173 mClipboard
->GetNativeClipboardSequenceNumber(mClipboardType
);
1174 if (resultOrError
.isErr()) {
1175 mClipboard
= nullptr;
1179 if (mSequenceNumber
!= resultOrError
.unwrap()) {
1180 mClipboard
= nullptr;
1187 NS_IMPL_ISUPPORTS(nsBaseClipboard::ClipboardPopulatedDataSnapshot
,
1188 nsIClipboardDataSnapshot
)
1190 nsBaseClipboard::ClipboardPopulatedDataSnapshot::ClipboardPopulatedDataSnapshot(
1191 nsITransferable
* aTransferable
)
1192 : mTransferable(aTransferable
) {
1193 MOZ_ASSERT(mTransferable
);
1194 aTransferable
->FlavorsTransferableCanExport(mFlavors
);
1197 NS_IMETHODIMP
nsBaseClipboard::ClipboardPopulatedDataSnapshot::GetValid(
1199 // Since this is a snapshot of what the clipboard data was, this is always
1205 NS_IMETHODIMP
nsBaseClipboard::ClipboardPopulatedDataSnapshot::GetFlavorList(
1206 nsTArray
<nsCString
>& aFlavors
) {
1207 aFlavors
.AppendElements(mFlavors
);
1211 NS_IMETHODIMP
nsBaseClipboard::ClipboardPopulatedDataSnapshot::GetData(
1212 nsITransferable
* aTransferable
,
1213 nsIAsyncClipboardRequestCallback
* aCallback
) {
1214 if (!aTransferable
|| !aCallback
) {
1215 return NS_ERROR_INVALID_ARG
;
1218 NS_DispatchToMainThread(NS_NewRunnableFunction(
1219 "ClipboardPopulatedDataSnapshot::GetData",
1220 [self
= RefPtr
{this}, transferable
= RefPtr
{aTransferable
},
1221 callback
= RefPtr
{aCallback
}]() {
1222 nsresult rv
= self
->GetDataSync(transferable
);
1223 callback
->OnComplete(rv
);
1229 NS_IMETHODIMP
nsBaseClipboard::ClipboardPopulatedDataSnapshot::GetDataSync(
1230 nsITransferable
* aTransferable
) {
1231 MOZ_CLIPBOARD_LOG("ClipboardPopulatedDataSnapshot::GetDataSync: %p", this);
1233 if (!aTransferable
) {
1234 return NS_ERROR_INVALID_ARG
;
1237 nsTArray
<nsCString
> flavors
;
1238 nsresult rv
= aTransferable
->FlavorsTransferableCanImport(flavors
);
1239 if (NS_FAILED(rv
)) {
1243 // If the requested flavor is not in the list, throw an error.
1244 for (const auto& flavor
: flavors
) {
1245 if (!mFlavors
.Contains(flavor
)) {
1246 return NS_ERROR_FAILURE
;
1250 // This method only fills in the data for the first flavor passed in. This
1251 // seems weird but matches the IDL documentation and behavior.
1252 if (!flavors
.IsEmpty()) {
1253 nsCOMPtr
<nsISupports
> data
;
1254 rv
= mTransferable
->GetTransferData(flavors
[0].get(), getter_AddRefs(data
));
1255 if (NS_FAILED(rv
)) {
1256 aTransferable
->ClearAllData();
1259 rv
= aTransferable
->SetTransferData(flavors
[0].get(), data
);
1260 if (NS_FAILED(rv
)) {
1261 aTransferable
->ClearAllData();
1268 mozilla::Maybe
<uint64_t> nsBaseClipboard::GetClipboardCacheInnerWindowId(
1269 ClipboardType aClipboardType
) {
1270 auto* clipboardCache
= GetClipboardCacheIfValid(aClipboardType
);
1271 return clipboardCache
? clipboardCache
->GetInnerWindowId()
1272 : mozilla::Nothing();
1275 nsBaseClipboard::ClipboardCache
* nsBaseClipboard::GetClipboardCacheIfValid(
1276 ClipboardType aClipboardType
) {
1277 MOZ_ASSERT(nsIClipboard::IsClipboardTypeSupported(aClipboardType
));
1279 const mozilla::UniquePtr
<ClipboardCache
>& cache
= mCaches
[aClipboardType
];
1282 if (!cache
->GetTransferable()) {
1283 MOZ_ASSERT(cache
->GetSequenceNumber() == -1);
1287 auto changeCountOrError
= GetNativeClipboardSequenceNumber(aClipboardType
);
1288 if (changeCountOrError
.isErr()) {
1292 if (changeCountOrError
.unwrap() != cache
->GetSequenceNumber()) {
1293 // Clipboard cache is invalid, clear it.
1301 void nsBaseClipboard::ClipboardCache::Clear() {
1302 if (mClipboardOwner
) {
1303 mClipboardOwner
->LosingOwnership(mTransferable
);
1304 mClipboardOwner
= nullptr;
1306 mTransferable
= nullptr;
1307 mSequenceNumber
= -1;
1310 nsresult
nsBaseClipboard::ClipboardCache::GetData(
1311 nsITransferable
* aTransferable
) const {
1312 MOZ_ASSERT(aTransferable
);
1313 MOZ_ASSERT(mozilla::StaticPrefs::widget_clipboard_use_cached_data_enabled());
1315 // get flavor list that includes all acceptable flavors (including ones
1316 // obtained through conversion)
1317 nsTArray
<nsCString
> flavors
;
1318 if (NS_FAILED(aTransferable
->FlavorsTransferableCanImport(flavors
))) {
1319 return NS_ERROR_FAILURE
;
1322 MOZ_ASSERT(mTransferable
);
1323 for (const auto& flavor
: flavors
) {
1324 nsCOMPtr
<nsISupports
> dataSupports
;
1325 // XXX Maybe we need special check for image as we always put the image as
1326 // "native" on the clipboard.
1327 if (NS_SUCCEEDED(mTransferable
->GetTransferData(
1328 flavor
.get(), getter_AddRefs(dataSupports
)))) {
1329 MOZ_CLIPBOARD_LOG("%s: getting %s from cache.", __FUNCTION__
,
1331 aTransferable
->SetTransferData(flavor
.get(), dataSupports
);
1332 // XXX we only read the first available type from native clipboard, so
1333 // make cache behave the same.
1338 return NS_ERROR_FAILURE
;