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 "nsIClipboardOwner.h"
12 using mozilla::GenericPromise
;
13 using mozilla::LogLevel
;
14 using mozilla::UniquePtr
;
15 using mozilla::dom::ClipboardCapabilities
;
17 NS_IMPL_ISUPPORTS(ClipboardSetDataHelper::AsyncSetClipboardData
,
18 nsIAsyncSetClipboardData
)
20 ClipboardSetDataHelper::AsyncSetClipboardData::AsyncSetClipboardData(
21 int32_t aClipboardType
, ClipboardSetDataHelper
* aClipboard
,
22 nsIAsyncSetClipboardDataCallback
* aCallback
)
23 : mClipboardType(aClipboardType
),
24 mClipboard(aClipboard
),
25 mCallback(aCallback
) {
26 MOZ_ASSERT(mClipboard
);
27 MOZ_ASSERT(mClipboard
->IsClipboardTypeSupported(mClipboardType
));
31 ClipboardSetDataHelper::AsyncSetClipboardData::SetData(
32 nsITransferable
* aTransferable
, nsIClipboardOwner
* aOwner
) {
34 return NS_ERROR_FAILURE
;
37 MOZ_ASSERT(mClipboard
);
38 MOZ_ASSERT(mClipboard
->IsClipboardTypeSupported(mClipboardType
));
39 MOZ_DIAGNOSTIC_ASSERT(mClipboard
->mPendingWriteRequests
[mClipboardType
] ==
42 RefPtr
<AsyncSetClipboardData
> request
=
43 std::move(mClipboard
->mPendingWriteRequests
[mClipboardType
]);
44 nsresult rv
= mClipboard
->SetData(aTransferable
, aOwner
, mClipboardType
);
45 MaybeNotifyCallback(rv
);
51 ClipboardSetDataHelper::AsyncSetClipboardData::Abort(nsresult aReason
) {
52 // Note: This may be called during destructor, so it should not attempt to
53 // take a reference to mClipboard.
55 if (!IsValid() || !NS_FAILED(aReason
)) {
56 return NS_ERROR_FAILURE
;
59 MaybeNotifyCallback(aReason
);
63 void ClipboardSetDataHelper::AsyncSetClipboardData::MaybeNotifyCallback(
65 // Note: This may be called during destructor, so it should not attempt to
66 // take a reference to mClipboard.
68 MOZ_ASSERT(IsValid());
69 if (nsCOMPtr
<nsIAsyncSetClipboardDataCallback
> callback
=
71 callback
->OnComplete(aResult
);
73 // Once the callback is notified, setData should not be allowed, so invalidate
78 NS_IMPL_ISUPPORTS(ClipboardSetDataHelper
, nsIClipboard
)
80 ClipboardSetDataHelper::~ClipboardSetDataHelper() {
81 for (auto& request
: mPendingWriteRequests
) {
83 request
->Abort(NS_ERROR_ABORT
);
89 void ClipboardSetDataHelper::RejectPendingAsyncSetDataRequestIfAny(
90 int32_t aClipboardType
) {
91 MOZ_ASSERT(nsIClipboard::IsClipboardTypeSupported(aClipboardType
));
92 auto& request
= mPendingWriteRequests
[aClipboardType
];
94 request
->Abort(NS_ERROR_ABORT
);
100 ClipboardSetDataHelper::SetData(nsITransferable
* aTransferable
,
101 nsIClipboardOwner
* aOwner
,
102 int32_t aWhichClipboard
) {
103 NS_ENSURE_ARG(aTransferable
);
104 if (!nsIClipboard::IsClipboardTypeSupported(aWhichClipboard
)) {
105 return NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
108 // Reject existing pending asyncSetData request if any.
109 RejectPendingAsyncSetDataRequestIfAny(aWhichClipboard
);
111 return SetNativeClipboardData(aTransferable
, aOwner
, aWhichClipboard
);
114 NS_IMETHODIMP
ClipboardSetDataHelper::AsyncSetData(
115 int32_t aWhichClipboard
, nsIAsyncSetClipboardDataCallback
* aCallback
,
116 nsIAsyncSetClipboardData
** _retval
) {
118 if (!nsIClipboard::IsClipboardTypeSupported(aWhichClipboard
)) {
119 return NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
122 // Reject existing pending AsyncSetData request if any.
123 RejectPendingAsyncSetDataRequestIfAny(aWhichClipboard
);
125 // Create a new AsyncSetClipboardData.
126 RefPtr
<AsyncSetClipboardData
> request
=
127 mozilla::MakeRefPtr
<AsyncSetClipboardData
>(aWhichClipboard
, this,
129 mPendingWriteRequests
[aWhichClipboard
] = request
;
130 request
.forget(_retval
);
134 nsBaseClipboard::nsBaseClipboard(const ClipboardCapabilities
& aClipboardCaps
)
135 : mClipboardCaps(aClipboardCaps
) {
136 using mozilla::MakeUnique
;
137 // Initialize clipboard cache.
138 mCaches
[kGlobalClipboard
] = MakeUnique
<ClipboardCache
>();
139 if (mClipboardCaps
.supportsSelectionClipboard()) {
140 mCaches
[kSelectionClipboard
] = MakeUnique
<ClipboardCache
>();
142 if (mClipboardCaps
.supportsFindClipboard()) {
143 mCaches
[kFindClipboard
] = MakeUnique
<ClipboardCache
>();
145 if (mClipboardCaps
.supportsSelectionCache()) {
146 mCaches
[kSelectionCache
] = MakeUnique
<ClipboardCache
>();
150 NS_IMPL_ISUPPORTS_INHERITED0(nsBaseClipboard
, ClipboardSetDataHelper
)
153 * Sets the transferable object
156 NS_IMETHODIMP
nsBaseClipboard::SetData(nsITransferable
* aTransferable
,
157 nsIClipboardOwner
* anOwner
,
158 int32_t aWhichClipboard
) {
159 NS_ASSERTION(aTransferable
, "clipboard given a null transferable");
161 CLIPBOARD_LOG("%s", __FUNCTION__
);
163 if (!nsIClipboard::IsClipboardTypeSupported(aWhichClipboard
)) {
164 return NS_ERROR_FAILURE
;
167 const auto& clipboardCache
= mCaches
[aWhichClipboard
];
168 MOZ_ASSERT(clipboardCache
);
169 if (aTransferable
== clipboardCache
->GetTransferable() &&
170 anOwner
== clipboardCache
->GetClipboardOwner()) {
171 CLIPBOARD_LOG("%s: skipping update.", __FUNCTION__
);
175 mEmptyingForSetData
= true;
176 if (NS_FAILED(EmptyClipboard(aWhichClipboard
))) {
177 CLIPBOARD_LOG("%s: emptying clipboard failed.", __FUNCTION__
);
179 mEmptyingForSetData
= false;
181 clipboardCache
->Update(aTransferable
, anOwner
);
183 nsresult rv
= NS_ERROR_FAILURE
;
185 mIgnoreEmptyNotification
= true;
186 rv
= ClipboardSetDataHelper::SetData(aTransferable
, anOwner
,
188 mIgnoreEmptyNotification
= false;
191 CLIPBOARD_LOG("%s: setting native clipboard data failed.", __FUNCTION__
);
198 * Gets the transferable object
201 NS_IMETHODIMP
nsBaseClipboard::GetData(nsITransferable
* aTransferable
,
202 int32_t aWhichClipboard
) {
203 NS_ASSERTION(aTransferable
, "clipboard given a null transferable");
205 CLIPBOARD_LOG("%s", __FUNCTION__
);
207 if (!nsIClipboard::IsClipboardTypeSupported(kSelectionClipboard
) &&
208 !nsIClipboard::IsClipboardTypeSupported(kFindClipboard
) &&
209 aWhichClipboard
!= kGlobalClipboard
) {
210 return NS_ERROR_FAILURE
;
214 return GetNativeClipboardData(aTransferable
, aWhichClipboard
);
216 return NS_ERROR_FAILURE
;
219 RefPtr
<GenericPromise
> nsBaseClipboard::AsyncGetData(
220 nsITransferable
* aTransferable
, int32_t aWhichClipboard
) {
221 nsresult rv
= GetData(aTransferable
, aWhichClipboard
);
223 return GenericPromise::CreateAndReject(rv
, __func__
);
226 return GenericPromise::CreateAndResolve(true, __func__
);
229 NS_IMETHODIMP
nsBaseClipboard::EmptyClipboard(int32_t aWhichClipboard
) {
230 CLIPBOARD_LOG("%s: clipboard=%i", __FUNCTION__
, aWhichClipboard
);
232 if (!nsIClipboard::IsClipboardTypeSupported(aWhichClipboard
)) {
233 return NS_ERROR_FAILURE
;
236 if (mIgnoreEmptyNotification
) {
237 MOZ_DIAGNOSTIC_ASSERT(false, "How did we get here?");
241 const auto& clipboardCache
= mCaches
[aWhichClipboard
];
242 MOZ_ASSERT(clipboardCache
);
243 clipboardCache
->Clear();
249 nsBaseClipboard::HasDataMatchingFlavors(const nsTArray
<nsCString
>& aFlavorList
,
250 int32_t aWhichClipboard
,
252 *outResult
= true; // say we always do.
256 RefPtr
<DataFlavorsPromise
> nsBaseClipboard::AsyncHasDataMatchingFlavors(
257 const nsTArray
<nsCString
>& aFlavorList
, int32_t aWhichClipboard
) {
258 nsTArray
<nsCString
> results
;
259 for (const auto& flavor
: aFlavorList
) {
260 bool hasMatchingFlavor
= false;
261 nsresult rv
= HasDataMatchingFlavors(AutoTArray
<nsCString
, 1>{flavor
},
262 aWhichClipboard
, &hasMatchingFlavor
);
263 if (NS_SUCCEEDED(rv
) && hasMatchingFlavor
) {
264 results
.AppendElement(flavor
);
268 return DataFlavorsPromise::CreateAndResolve(std::move(results
), __func__
);
272 nsBaseClipboard::IsClipboardTypeSupported(int32_t aWhichClipboard
,
274 NS_ENSURE_ARG_POINTER(aRetval
);
275 switch (aWhichClipboard
) {
276 case kGlobalClipboard
:
277 // We always support the global clipboard.
280 case kSelectionClipboard
:
281 *aRetval
= mClipboardCaps
.supportsSelectionClipboard();
284 *aRetval
= mClipboardCaps
.supportsFindClipboard();
286 case kSelectionCache
:
287 *aRetval
= mClipboardCaps
.supportsSelectionCache();
295 void nsBaseClipboard::ClipboardCache::Clear() {
296 if (mClipboardOwner
) {
297 mClipboardOwner
->LosingOwnership(mTransferable
);
298 mClipboardOwner
= nullptr;
300 mTransferable
= nullptr;