Bug 1859954 - Use XP_DARWIN rather than XP_MACOS in PHC r=glandium
[gecko.git] / widget / nsBaseClipboard.cpp
blob0c420954326c146dcb17ad1c17b894572b011372
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 "mozilla/StaticPrefs_widget.h"
9 #include "nsIClipboardOwner.h"
10 #include "nsError.h"
11 #include "nsXPCOM.h"
13 using mozilla::GenericPromise;
14 using mozilla::LogLevel;
15 using mozilla::UniquePtr;
16 using mozilla::dom::ClipboardCapabilities;
18 NS_IMPL_ISUPPORTS(nsBaseClipboard::AsyncSetClipboardData,
19 nsIAsyncSetClipboardData)
21 nsBaseClipboard::AsyncSetClipboardData::AsyncSetClipboardData(
22 int32_t aClipboardType, nsBaseClipboard* aClipboard,
23 nsIAsyncSetClipboardDataCallback* aCallback)
24 : mClipboardType(aClipboardType),
25 mClipboard(aClipboard),
26 mCallback(aCallback) {
27 MOZ_ASSERT(mClipboard);
28 MOZ_ASSERT(
29 mClipboard->nsIClipboard::IsClipboardTypeSupported(mClipboardType));
32 NS_IMETHODIMP
33 nsBaseClipboard::AsyncSetClipboardData::SetData(nsITransferable* aTransferable,
34 nsIClipboardOwner* aOwner) {
35 MOZ_CLIPBOARD_LOG("AsyncSetClipboardData::SetData (%p): clipboard=%d", this,
36 mClipboardType);
38 if (!IsValid()) {
39 return NS_ERROR_FAILURE;
42 if (MOZ_CLIPBOARD_LOG_ENABLED()) {
43 nsTArray<nsCString> flavors;
44 if (NS_SUCCEEDED(aTransferable->FlavorsTransferableCanImport(flavors))) {
45 for (const auto& flavor : flavors) {
46 MOZ_CLIPBOARD_LOG(" MIME %s", flavor.get());
51 MOZ_ASSERT(mClipboard);
52 MOZ_ASSERT(
53 mClipboard->nsIClipboard::IsClipboardTypeSupported(mClipboardType));
54 MOZ_DIAGNOSTIC_ASSERT(mClipboard->mPendingWriteRequests[mClipboardType] ==
55 this);
57 RefPtr<AsyncSetClipboardData> request =
58 std::move(mClipboard->mPendingWriteRequests[mClipboardType]);
59 nsresult rv = mClipboard->SetData(aTransferable, aOwner, mClipboardType);
60 MaybeNotifyCallback(rv);
62 return rv;
65 NS_IMETHODIMP
66 nsBaseClipboard::AsyncSetClipboardData::Abort(nsresult aReason) {
67 // Note: This may be called during destructor, so it should not attempt to
68 // take a reference to mClipboard.
70 if (!IsValid() || !NS_FAILED(aReason)) {
71 return NS_ERROR_FAILURE;
74 MaybeNotifyCallback(aReason);
75 return NS_OK;
78 void nsBaseClipboard::AsyncSetClipboardData::MaybeNotifyCallback(
79 nsresult aResult) {
80 // Note: This may be called during destructor, so it should not attempt to
81 // take a reference to mClipboard.
83 MOZ_ASSERT(IsValid());
84 if (nsCOMPtr<nsIAsyncSetClipboardDataCallback> callback =
85 mCallback.forget()) {
86 callback->OnComplete(aResult);
88 // Once the callback is notified, setData should not be allowed, so invalidate
89 // this request.
90 mClipboard = nullptr;
93 void nsBaseClipboard::RejectPendingAsyncSetDataRequestIfAny(
94 int32_t aClipboardType) {
95 MOZ_ASSERT(nsIClipboard::IsClipboardTypeSupported(aClipboardType));
96 auto& request = mPendingWriteRequests[aClipboardType];
97 if (request) {
98 request->Abort(NS_ERROR_ABORT);
99 request = nullptr;
103 NS_IMETHODIMP nsBaseClipboard::AsyncSetData(
104 int32_t aWhichClipboard, nsIAsyncSetClipboardDataCallback* aCallback,
105 nsIAsyncSetClipboardData** _retval) {
106 MOZ_CLIPBOARD_LOG("%s: clipboard=%d", __FUNCTION__, aWhichClipboard);
108 *_retval = nullptr;
109 if (!nsIClipboard::IsClipboardTypeSupported(aWhichClipboard)) {
110 MOZ_CLIPBOARD_LOG("%s: clipboard %d is not supported.", __FUNCTION__,
111 aWhichClipboard);
112 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
115 // Reject existing pending AsyncSetData request if any.
116 RejectPendingAsyncSetDataRequestIfAny(aWhichClipboard);
118 // Create a new AsyncSetClipboardData.
119 RefPtr<AsyncSetClipboardData> request =
120 mozilla::MakeRefPtr<AsyncSetClipboardData>(aWhichClipboard, this,
121 aCallback);
122 mPendingWriteRequests[aWhichClipboard] = request;
123 request.forget(_retval);
124 return NS_OK;
127 nsBaseClipboard::nsBaseClipboard(const ClipboardCapabilities& aClipboardCaps)
128 : mClipboardCaps(aClipboardCaps) {
129 using mozilla::MakeUnique;
130 // Initialize clipboard cache.
131 mCaches[kGlobalClipboard] = MakeUnique<ClipboardCache>();
132 if (mClipboardCaps.supportsSelectionClipboard()) {
133 mCaches[kSelectionClipboard] = MakeUnique<ClipboardCache>();
135 if (mClipboardCaps.supportsFindClipboard()) {
136 mCaches[kFindClipboard] = MakeUnique<ClipboardCache>();
138 if (mClipboardCaps.supportsSelectionCache()) {
139 mCaches[kSelectionCache] = MakeUnique<ClipboardCache>();
143 nsBaseClipboard::~nsBaseClipboard() {
144 for (auto& request : mPendingWriteRequests) {
145 if (request) {
146 request->Abort(NS_ERROR_ABORT);
147 request = nullptr;
152 NS_IMPL_ISUPPORTS(nsBaseClipboard, nsIClipboard)
155 * Sets the transferable object
158 NS_IMETHODIMP nsBaseClipboard::SetData(nsITransferable* aTransferable,
159 nsIClipboardOwner* aOwner,
160 int32_t aWhichClipboard) {
161 NS_ASSERTION(aTransferable, "clipboard given a null transferable");
163 MOZ_CLIPBOARD_LOG("%s: clipboard=%d", __FUNCTION__, aWhichClipboard);
165 if (!nsIClipboard::IsClipboardTypeSupported(aWhichClipboard)) {
166 MOZ_CLIPBOARD_LOG("%s: clipboard %d is not supported.", __FUNCTION__,
167 aWhichClipboard);
168 return NS_ERROR_FAILURE;
171 if (MOZ_CLIPBOARD_LOG_ENABLED()) {
172 nsTArray<nsCString> flavors;
173 if (NS_SUCCEEDED(aTransferable->FlavorsTransferableCanImport(flavors))) {
174 for (const auto& flavor : flavors) {
175 MOZ_CLIPBOARD_LOG(" MIME %s", flavor.get());
180 const auto& clipboardCache = mCaches[aWhichClipboard];
181 MOZ_ASSERT(clipboardCache);
182 if (aTransferable == clipboardCache->GetTransferable() &&
183 aOwner == clipboardCache->GetClipboardOwner()) {
184 MOZ_CLIPBOARD_LOG("%s: skipping update.", __FUNCTION__);
185 return NS_OK;
188 clipboardCache->Clear();
190 nsresult rv = NS_ERROR_FAILURE;
191 if (aTransferable) {
192 mIgnoreEmptyNotification = true;
193 // Reject existing pending asyncSetData request if any.
194 RejectPendingAsyncSetDataRequestIfAny(aWhichClipboard);
195 rv = SetNativeClipboardData(aTransferable, aWhichClipboard);
196 mIgnoreEmptyNotification = false;
198 if (NS_FAILED(rv)) {
199 MOZ_CLIPBOARD_LOG("%s: setting native clipboard data failed.",
200 __FUNCTION__);
201 return rv;
204 auto result = GetNativeClipboardSequenceNumber(aWhichClipboard);
205 if (result.isErr()) {
206 MOZ_CLIPBOARD_LOG("%s: getting native clipboard change count failed.",
207 __FUNCTION__);
208 return result.unwrapErr();
211 clipboardCache->Update(aTransferable, aOwner, result.unwrap());
212 return NS_OK;
215 nsresult nsBaseClipboard::GetDataFromClipboardCache(
216 nsITransferable* aTransferable, int32_t aClipboardType) {
217 MOZ_ASSERT(aTransferable);
218 MOZ_ASSERT(nsIClipboard::IsClipboardTypeSupported(aClipboardType));
219 MOZ_ASSERT(mozilla::StaticPrefs::widget_clipboard_use_cached_data_enabled());
221 const auto* clipboardCache = GetClipboardCacheIfValid(aClipboardType);
222 if (!clipboardCache) {
223 return NS_ERROR_FAILURE;
226 nsITransferable* cachedTransferable = clipboardCache->GetTransferable();
227 MOZ_ASSERT(cachedTransferable);
229 // get flavor list that includes all acceptable flavors (including ones
230 // obtained through conversion)
231 nsTArray<nsCString> flavors;
232 if (NS_FAILED(aTransferable->FlavorsTransferableCanImport(flavors))) {
233 return NS_ERROR_FAILURE;
236 for (const auto& flavor : flavors) {
237 nsCOMPtr<nsISupports> dataSupports;
238 if (NS_SUCCEEDED(cachedTransferable->GetTransferData(
239 flavor.get(), getter_AddRefs(dataSupports)))) {
240 MOZ_CLIPBOARD_LOG("%s: getting %s from cache.", __FUNCTION__,
241 flavor.get());
242 aTransferable->SetTransferData(flavor.get(), dataSupports);
243 // maybe try to fill in more types? Is there a point?
244 return NS_OK;
248 return NS_ERROR_FAILURE;
252 * Gets the transferable object from system clipboard.
254 NS_IMETHODIMP nsBaseClipboard::GetData(nsITransferable* aTransferable,
255 int32_t aWhichClipboard) {
256 MOZ_CLIPBOARD_LOG("%s: clipboard=%d", __FUNCTION__, aWhichClipboard);
258 if (!aTransferable) {
259 NS_ASSERTION(false, "clipboard given a null transferable");
260 return NS_ERROR_FAILURE;
263 if (mozilla::StaticPrefs::widget_clipboard_use_cached_data_enabled()) {
264 // If we were the last ones to put something on the native clipboard, then
265 // just use the cached transferable. Otherwise clear it because it isn't
266 // relevant any more.
267 if (NS_SUCCEEDED(
268 GetDataFromClipboardCache(aTransferable, aWhichClipboard))) {
269 // maybe try to fill in more types? Is there a point?
270 return NS_OK;
273 // at this point we can't satisfy the request from cache data so let's look
274 // for things other people put on the system clipboard
277 return GetNativeClipboardData(aTransferable, aWhichClipboard);
280 RefPtr<GenericPromise> nsBaseClipboard::AsyncGetData(
281 nsITransferable* aTransferable, int32_t aWhichClipboard) {
282 MOZ_CLIPBOARD_LOG("%s: clipboard=%d", __FUNCTION__, aWhichClipboard);
284 if (!aTransferable) {
285 NS_ASSERTION(false, "clipboard given a null transferable");
286 return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
289 if (mozilla::StaticPrefs::widget_clipboard_use_cached_data_enabled()) {
290 // If we were the last ones to put something on the native clipboard, then
291 // just use the cached transferable. Otherwise clear it because it isn't
292 // relevant any more.
293 if (NS_SUCCEEDED(
294 GetDataFromClipboardCache(aTransferable, aWhichClipboard))) {
295 // maybe try to fill in more types? Is there a point?
296 return GenericPromise::CreateAndResolve(true, __func__);
299 // at this point we can't satisfy the request from cache data so let's look
300 // for things other people put on the system clipboard
303 RefPtr<GenericPromise::Private> dataPromise =
304 mozilla::MakeRefPtr<GenericPromise::Private>(__func__);
305 AsyncGetNativeClipboardData(aTransferable, aWhichClipboard,
306 [dataPromise](nsresult aResult) {
307 if (NS_FAILED(aResult)) {
308 dataPromise->Reject(aResult, __func__);
309 return;
312 dataPromise->Resolve(true, __func__);
314 return dataPromise.forget();
317 NS_IMETHODIMP nsBaseClipboard::EmptyClipboard(int32_t aWhichClipboard) {
318 MOZ_CLIPBOARD_LOG("%s: clipboard=%d", __FUNCTION__, aWhichClipboard);
320 if (!nsIClipboard::IsClipboardTypeSupported(aWhichClipboard)) {
321 MOZ_CLIPBOARD_LOG("%s: clipboard %d is not supported.", __FUNCTION__,
322 aWhichClipboard);
323 return NS_ERROR_FAILURE;
326 EmptyNativeClipboardData(aWhichClipboard);
328 const auto& clipboardCache = mCaches[aWhichClipboard];
329 MOZ_ASSERT(clipboardCache);
331 if (mIgnoreEmptyNotification) {
332 MOZ_DIAGNOSTIC_ASSERT(!clipboardCache->GetTransferable() &&
333 !clipboardCache->GetClipboardOwner() &&
334 clipboardCache->GetSequenceNumber() == -1,
335 "How did we have data in clipboard cache here?");
336 return NS_OK;
339 clipboardCache->Clear();
341 return NS_OK;
344 mozilla::Result<nsTArray<nsCString>, nsresult>
345 nsBaseClipboard::GetFlavorsFromClipboardCache(int32_t aClipboardType) {
346 MOZ_ASSERT(mozilla::StaticPrefs::widget_clipboard_use_cached_data_enabled());
347 MOZ_ASSERT(nsIClipboard::IsClipboardTypeSupported(aClipboardType));
349 const auto* clipboardCache = GetClipboardCacheIfValid(aClipboardType);
350 if (!clipboardCache) {
351 return mozilla::Err(NS_ERROR_FAILURE);
354 nsITransferable* cachedTransferable = clipboardCache->GetTransferable();
355 MOZ_ASSERT(cachedTransferable);
357 nsTArray<nsCString> flavors;
358 nsresult rv = cachedTransferable->FlavorsTransferableCanImport(flavors);
359 if (NS_FAILED(rv)) {
360 return mozilla::Err(rv);
363 if (MOZ_CLIPBOARD_LOG_ENABLED()) {
364 MOZ_CLIPBOARD_LOG(" Cached transferable types (nums %zu)\n",
365 flavors.Length());
366 for (const auto& flavor : flavors) {
367 MOZ_CLIPBOARD_LOG(" MIME %s", flavor.get());
371 return std::move(flavors);
374 NS_IMETHODIMP
375 nsBaseClipboard::HasDataMatchingFlavors(const nsTArray<nsCString>& aFlavorList,
376 int32_t aWhichClipboard,
377 bool* aOutResult) {
378 MOZ_CLIPBOARD_LOG("%s: clipboard=%d", __FUNCTION__, aWhichClipboard);
379 if (MOZ_CLIPBOARD_LOG_ENABLED()) {
380 MOZ_CLIPBOARD_LOG(" Asking for content clipboard=%i:\n",
381 aWhichClipboard);
382 for (const auto& flavor : aFlavorList) {
383 MOZ_CLIPBOARD_LOG(" MIME %s", flavor.get());
387 *aOutResult = false;
389 if (mozilla::StaticPrefs::widget_clipboard_use_cached_data_enabled()) {
390 // First, check if we have valid data in our cached transferable.
391 auto flavorsOrError = GetFlavorsFromClipboardCache(aWhichClipboard);
392 if (flavorsOrError.isOk()) {
393 for (const auto& transferableFlavor : flavorsOrError.unwrap()) {
394 for (const auto& flavor : aFlavorList) {
395 if (transferableFlavor.Equals(flavor)) {
396 MOZ_CLIPBOARD_LOG(" has %s", flavor.get());
397 *aOutResult = true;
398 return NS_OK;
405 auto resultOrError =
406 HasNativeClipboardDataMatchingFlavors(aFlavorList, aWhichClipboard);
407 if (resultOrError.isErr()) {
408 MOZ_CLIPBOARD_LOG(
409 "%s: checking native clipboard data matching flavors falied.",
410 __FUNCTION__);
411 return resultOrError.unwrapErr();
414 *aOutResult = resultOrError.unwrap();
415 return NS_OK;
418 RefPtr<DataFlavorsPromise> nsBaseClipboard::AsyncHasDataMatchingFlavors(
419 const nsTArray<nsCString>& aFlavorList, int32_t aWhichClipboard) {
420 MOZ_CLIPBOARD_LOG("%s: clipboard=%d", __FUNCTION__, aWhichClipboard);
421 if (MOZ_CLIPBOARD_LOG_ENABLED()) {
422 MOZ_CLIPBOARD_LOG(" Asking for content clipboard=%i:\n",
423 aWhichClipboard);
424 for (const auto& flavor : aFlavorList) {
425 MOZ_CLIPBOARD_LOG(" MIME %s", flavor.get());
429 if (mozilla::StaticPrefs::widget_clipboard_use_cached_data_enabled()) {
430 // First, check if we have valid data in our cached transferable.
431 auto flavorsOrError = GetFlavorsFromClipboardCache(aWhichClipboard);
432 if (flavorsOrError.isOk()) {
433 nsTArray<nsCString> results;
434 for (const auto& transferableFlavor : flavorsOrError.unwrap()) {
435 for (const auto& flavor : aFlavorList) {
436 // XXX We need special check for image as we always put the image as
437 // "native" on the clipboard.
438 if (transferableFlavor.Equals(flavor) ||
439 (transferableFlavor.Equals(kNativeImageMime) &&
440 nsContentUtils::IsFlavorImage(flavor))) {
441 MOZ_CLIPBOARD_LOG(" has %s", flavor.get());
442 results.AppendElement(flavor);
446 return DataFlavorsPromise::CreateAndResolve(std::move(results), __func__);
450 RefPtr<DataFlavorsPromise::Private> flavorPromise =
451 mozilla::MakeRefPtr<DataFlavorsPromise::Private>(__func__);
452 AsyncHasNativeClipboardDataMatchingFlavors(
453 aFlavorList, aWhichClipboard, [flavorPromise](auto aResultOrError) {
454 if (aResultOrError.isErr()) {
455 flavorPromise->Reject(aResultOrError.unwrapErr(), __func__);
456 return;
459 flavorPromise->Resolve(std::move(aResultOrError.unwrap()), __func__);
461 return flavorPromise.forget();
464 NS_IMETHODIMP
465 nsBaseClipboard::IsClipboardTypeSupported(int32_t aWhichClipboard,
466 bool* aRetval) {
467 NS_ENSURE_ARG_POINTER(aRetval);
468 switch (aWhichClipboard) {
469 case kGlobalClipboard:
470 // We always support the global clipboard.
471 *aRetval = true;
472 return NS_OK;
473 case kSelectionClipboard:
474 *aRetval = mClipboardCaps.supportsSelectionClipboard();
475 return NS_OK;
476 case kFindClipboard:
477 *aRetval = mClipboardCaps.supportsFindClipboard();
478 return NS_OK;
479 case kSelectionCache:
480 *aRetval = mClipboardCaps.supportsSelectionCache();
481 return NS_OK;
482 default:
483 *aRetval = false;
484 return NS_OK;
488 void nsBaseClipboard::AsyncHasNativeClipboardDataMatchingFlavors(
489 const nsTArray<nsCString>& aFlavorList, int32_t aWhichClipboard,
490 HasMatchingFlavorsCallback&& aCallback) {
491 MOZ_DIAGNOSTIC_ASSERT(
492 nsIClipboard::IsClipboardTypeSupported(aWhichClipboard));
494 MOZ_CLIPBOARD_LOG(
495 "nsBaseClipboard::AsyncHasNativeClipboardDataMatchingFlavors: "
496 "clipboard=%d",
497 aWhichClipboard);
499 nsTArray<nsCString> results;
500 for (const auto& flavor : aFlavorList) {
501 auto resultOrError = HasNativeClipboardDataMatchingFlavors(
502 AutoTArray<nsCString, 1>{flavor}, aWhichClipboard);
503 if (resultOrError.isOk() && resultOrError.unwrap()) {
504 results.AppendElement(flavor);
507 aCallback(std::move(results));
510 void nsBaseClipboard::AsyncGetNativeClipboardData(
511 nsITransferable* aTransferable, int32_t aWhichClipboard,
512 GetDataCallback&& aCallback) {
513 aCallback(GetNativeClipboardData(aTransferable, aWhichClipboard));
516 void nsBaseClipboard::ClearClipboardCache(int32_t aClipboardType) {
517 MOZ_ASSERT(nsIClipboard::IsClipboardTypeSupported(aClipboardType));
518 const mozilla::UniquePtr<ClipboardCache>& cache = mCaches[aClipboardType];
519 MOZ_ASSERT(cache);
520 cache->Clear();
523 nsBaseClipboard::ClipboardCache* nsBaseClipboard::GetClipboardCacheIfValid(
524 int32_t aClipboardType) {
525 MOZ_ASSERT(nsIClipboard::IsClipboardTypeSupported(aClipboardType));
527 const mozilla::UniquePtr<ClipboardCache>& cache = mCaches[aClipboardType];
528 MOZ_ASSERT(cache);
530 if (!cache->GetTransferable()) {
531 MOZ_ASSERT(cache->GetSequenceNumber() == -1);
532 return nullptr;
535 auto changeCountOrError = GetNativeClipboardSequenceNumber(aClipboardType);
536 if (changeCountOrError.isErr()) {
537 return nullptr;
540 if (changeCountOrError.unwrap() != cache->GetSequenceNumber()) {
541 // Clipboard cache is invalid, clear it.
542 cache->Clear();
543 return nullptr;
546 return cache.get();
549 void nsBaseClipboard::ClipboardCache::Clear() {
550 if (mClipboardOwner) {
551 mClipboardOwner->LosingOwnership(mTransferable);
552 mClipboardOwner = nullptr;
554 mTransferable = nullptr;
555 mSequenceNumber = -1;