Bug 1837620 - Part 7: Keep JitScripts in a linked list to avoid iterating all BaseScr...
[gecko.git] / widget / nsBaseClipboard.cpp
blob12d9dd9081a698c0800f8a0213f25715e167be0c
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(ClipboardSetDataHelper::AsyncSetClipboardData,
19 nsIAsyncSetClipboardData)
21 ClipboardSetDataHelper::AsyncSetClipboardData::AsyncSetClipboardData(
22 int32_t aClipboardType, ClipboardSetDataHelper* aClipboard,
23 nsIAsyncSetClipboardDataCallback* aCallback)
24 : mClipboardType(aClipboardType),
25 mClipboard(aClipboard),
26 mCallback(aCallback) {
27 MOZ_ASSERT(mClipboard);
28 MOZ_ASSERT(mClipboard->IsClipboardTypeSupported(mClipboardType));
31 NS_IMETHODIMP
32 ClipboardSetDataHelper::AsyncSetClipboardData::SetData(
33 nsITransferable* aTransferable, nsIClipboardOwner* aOwner) {
34 if (!IsValid()) {
35 return NS_ERROR_FAILURE;
38 MOZ_ASSERT(mClipboard);
39 MOZ_ASSERT(mClipboard->IsClipboardTypeSupported(mClipboardType));
40 MOZ_DIAGNOSTIC_ASSERT(mClipboard->mPendingWriteRequests[mClipboardType] ==
41 this);
43 RefPtr<AsyncSetClipboardData> request =
44 std::move(mClipboard->mPendingWriteRequests[mClipboardType]);
45 nsresult rv = mClipboard->SetData(aTransferable, aOwner, mClipboardType);
46 MaybeNotifyCallback(rv);
48 return rv;
51 NS_IMETHODIMP
52 ClipboardSetDataHelper::AsyncSetClipboardData::Abort(nsresult aReason) {
53 // Note: This may be called during destructor, so it should not attempt to
54 // take a reference to mClipboard.
56 if (!IsValid() || !NS_FAILED(aReason)) {
57 return NS_ERROR_FAILURE;
60 MaybeNotifyCallback(aReason);
61 return NS_OK;
64 void ClipboardSetDataHelper::AsyncSetClipboardData::MaybeNotifyCallback(
65 nsresult aResult) {
66 // Note: This may be called during destructor, so it should not attempt to
67 // take a reference to mClipboard.
69 MOZ_ASSERT(IsValid());
70 if (nsCOMPtr<nsIAsyncSetClipboardDataCallback> callback =
71 mCallback.forget()) {
72 callback->OnComplete(aResult);
74 // Once the callback is notified, setData should not be allowed, so invalidate
75 // this request.
76 mClipboard = nullptr;
79 NS_IMPL_ISUPPORTS(ClipboardSetDataHelper, nsIClipboard)
81 ClipboardSetDataHelper::~ClipboardSetDataHelper() {
82 for (auto& request : mPendingWriteRequests) {
83 if (request) {
84 request->Abort(NS_ERROR_ABORT);
85 request = nullptr;
90 void ClipboardSetDataHelper::RejectPendingAsyncSetDataRequestIfAny(
91 int32_t aClipboardType) {
92 MOZ_ASSERT(nsIClipboard::IsClipboardTypeSupported(aClipboardType));
93 auto& request = mPendingWriteRequests[aClipboardType];
94 if (request) {
95 request->Abort(NS_ERROR_ABORT);
96 request = nullptr;
100 NS_IMETHODIMP
101 ClipboardSetDataHelper::SetData(nsITransferable* aTransferable,
102 nsIClipboardOwner* aOwner,
103 int32_t aWhichClipboard) {
104 NS_ENSURE_ARG(aTransferable);
105 if (!nsIClipboard::IsClipboardTypeSupported(aWhichClipboard)) {
106 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
109 // Reject existing pending asyncSetData request if any.
110 RejectPendingAsyncSetDataRequestIfAny(aWhichClipboard);
112 return SetNativeClipboardData(aTransferable, aOwner, aWhichClipboard);
115 NS_IMETHODIMP ClipboardSetDataHelper::AsyncSetData(
116 int32_t aWhichClipboard, nsIAsyncSetClipboardDataCallback* aCallback,
117 nsIAsyncSetClipboardData** _retval) {
118 *_retval = nullptr;
119 if (!nsIClipboard::IsClipboardTypeSupported(aWhichClipboard)) {
120 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
123 // Reject existing pending AsyncSetData request if any.
124 RejectPendingAsyncSetDataRequestIfAny(aWhichClipboard);
126 // Create a new AsyncSetClipboardData.
127 RefPtr<AsyncSetClipboardData> request =
128 mozilla::MakeRefPtr<AsyncSetClipboardData>(aWhichClipboard, this,
129 aCallback);
130 mPendingWriteRequests[aWhichClipboard] = request;
131 request.forget(_retval);
132 return NS_OK;
135 nsBaseClipboard::nsBaseClipboard(const ClipboardCapabilities& aClipboardCaps)
136 : mClipboardCaps(aClipboardCaps) {
137 using mozilla::MakeUnique;
138 // Initialize clipboard cache.
139 mCaches[kGlobalClipboard] = MakeUnique<ClipboardCache>();
140 if (mClipboardCaps.supportsSelectionClipboard()) {
141 mCaches[kSelectionClipboard] = MakeUnique<ClipboardCache>();
143 if (mClipboardCaps.supportsFindClipboard()) {
144 mCaches[kFindClipboard] = MakeUnique<ClipboardCache>();
146 if (mClipboardCaps.supportsSelectionCache()) {
147 mCaches[kSelectionCache] = MakeUnique<ClipboardCache>();
151 NS_IMPL_ISUPPORTS_INHERITED0(nsBaseClipboard, ClipboardSetDataHelper)
154 * Sets the transferable object
157 NS_IMETHODIMP nsBaseClipboard::SetData(nsITransferable* aTransferable,
158 nsIClipboardOwner* anOwner,
159 int32_t aWhichClipboard) {
160 NS_ASSERTION(aTransferable, "clipboard given a null transferable");
162 CLIPBOARD_LOG("%s: clipboard=%d", __FUNCTION__, aWhichClipboard);
164 if (!nsIClipboard::IsClipboardTypeSupported(aWhichClipboard)) {
165 CLIPBOARD_LOG("%s: clipboard %d is not supported.", __FUNCTION__,
166 aWhichClipboard);
167 return NS_ERROR_FAILURE;
170 const auto& clipboardCache = mCaches[aWhichClipboard];
171 MOZ_ASSERT(clipboardCache);
172 if (aTransferable == clipboardCache->GetTransferable() &&
173 anOwner == clipboardCache->GetClipboardOwner()) {
174 CLIPBOARD_LOG("%s: skipping update.", __FUNCTION__);
175 return NS_OK;
178 clipboardCache->Clear();
180 nsresult rv = NS_ERROR_FAILURE;
181 if (aTransferable) {
182 mIgnoreEmptyNotification = true;
183 rv = ClipboardSetDataHelper::SetData(aTransferable, anOwner,
184 aWhichClipboard);
185 mIgnoreEmptyNotification = false;
187 if (NS_FAILED(rv)) {
188 CLIPBOARD_LOG("%s: setting native clipboard data failed.", __FUNCTION__);
189 return rv;
192 auto result = GetNativeClipboardSequenceNumber(aWhichClipboard);
193 if (result.isErr()) {
194 CLIPBOARD_LOG("%s: getting native clipboard change count failed.",
195 __FUNCTION__);
196 return result.unwrapErr();
199 clipboardCache->Update(aTransferable, anOwner, result.unwrap());
200 return NS_OK;
204 * Gets the transferable object from system clipboard.
206 NS_IMETHODIMP nsBaseClipboard::GetData(nsITransferable* aTransferable,
207 int32_t aWhichClipboard) {
208 CLIPBOARD_LOG("%s: clipboard=%d", __FUNCTION__, aWhichClipboard);
210 if (!aTransferable) {
211 NS_ASSERTION(false, "clipboard given a null transferable");
212 return NS_ERROR_FAILURE;
215 if (mozilla::StaticPrefs::widget_clipboard_use_cached_data_enabled()) {
216 // If we were the last ones to put something on the navtive clipboard, then
217 // just use the cached transferable. Otherwise clear it because it isn't
218 // relevant any more.
219 if (auto* clipboardCache = GetClipboardCacheIfValid(aWhichClipboard)) {
220 MOZ_ASSERT(clipboardCache->GetTransferable());
222 // get flavor list that includes all acceptable flavors (including ones
223 // obtained through conversion)
224 nsTArray<nsCString> flavors;
225 nsresult rv = aTransferable->FlavorsTransferableCanImport(flavors);
226 if (NS_FAILED(rv)) {
227 return NS_ERROR_FAILURE;
230 for (const auto& flavor : flavors) {
231 nsCOMPtr<nsISupports> dataSupports;
232 rv = clipboardCache->GetTransferable()->GetTransferData(
233 flavor.get(), getter_AddRefs(dataSupports));
234 if (NS_SUCCEEDED(rv)) {
235 CLIPBOARD_LOG("%s: getting %s from cache.", __FUNCTION__,
236 flavor.get());
237 aTransferable->SetTransferData(flavor.get(), dataSupports);
238 // maybe try to fill in more types? Is there a point?
239 return NS_OK;
244 // at this point we can't satisfy the request from cache data so let's look
245 // for things other people put on the system clipboard
248 return GetNativeClipboardData(aTransferable, aWhichClipboard);
251 RefPtr<GenericPromise> nsBaseClipboard::AsyncGetData(
252 nsITransferable* aTransferable, int32_t aWhichClipboard) {
253 nsresult rv = GetData(aTransferable, aWhichClipboard);
254 if (NS_FAILED(rv)) {
255 return GenericPromise::CreateAndReject(rv, __func__);
258 return GenericPromise::CreateAndResolve(true, __func__);
261 NS_IMETHODIMP nsBaseClipboard::EmptyClipboard(int32_t aWhichClipboard) {
262 CLIPBOARD_LOG("%s: clipboard=%d", __FUNCTION__, aWhichClipboard);
264 if (!nsIClipboard::IsClipboardTypeSupported(aWhichClipboard)) {
265 CLIPBOARD_LOG("%s: clipboard %d is not supported.", __FUNCTION__,
266 aWhichClipboard);
267 return NS_ERROR_FAILURE;
270 EmptyNativeClipboardData(aWhichClipboard);
272 if (mIgnoreEmptyNotification) {
273 MOZ_DIAGNOSTIC_ASSERT(false, "How did we get here?");
274 return NS_OK;
277 const auto& clipboardCache = mCaches[aWhichClipboard];
278 MOZ_ASSERT(clipboardCache);
279 clipboardCache->Clear();
281 return NS_OK;
284 NS_IMETHODIMP
285 nsBaseClipboard::HasDataMatchingFlavors(const nsTArray<nsCString>& aFlavorList,
286 int32_t aWhichClipboard,
287 bool* aOutResult) {
288 CLIPBOARD_LOG("%s: clipboard=%d", __FUNCTION__, aWhichClipboard);
289 if (CLIPBOARD_LOG_ENABLED()) {
290 CLIPBOARD_LOG(" Asking for content clipboard=%i:\n", aWhichClipboard);
291 for (const auto& flavor : aFlavorList) {
292 CLIPBOARD_LOG(" MIME %s", flavor.get());
296 *aOutResult = false;
298 if (mozilla::StaticPrefs::widget_clipboard_use_cached_data_enabled()) {
299 if (auto* clipboardCache = GetClipboardCacheIfValid(aWhichClipboard)) {
300 MOZ_ASSERT(clipboardCache->GetTransferable());
302 // first see if we have data for this in our cached transferable
303 nsTArray<nsCString> transferableFlavors;
304 nsresult rv =
305 clipboardCache->GetTransferable()->FlavorsTransferableCanImport(
306 transferableFlavors);
307 if (NS_SUCCEEDED(rv)) {
308 if (CLIPBOARD_LOG_ENABLED()) {
309 CLIPBOARD_LOG(" Cached transferable types (nums %zu)\n",
310 transferableFlavors.Length());
311 for (const auto& transferableFlavor : transferableFlavors) {
312 CLIPBOARD_LOG(" MIME %s", transferableFlavor.get());
316 for (const auto& transferableFlavor : transferableFlavors) {
317 for (const auto& flavor : aFlavorList) {
318 if (transferableFlavor.Equals(flavor)) {
319 CLIPBOARD_LOG(" has %s", flavor.get());
320 *aOutResult = true;
321 return NS_OK;
329 auto resultOrError =
330 HasNativeClipboardDataMatchingFlavors(aFlavorList, aWhichClipboard);
331 if (resultOrError.isErr()) {
332 CLIPBOARD_LOG("%s: checking native clipboard data matching flavors falied.",
333 __FUNCTION__);
334 return resultOrError.unwrapErr();
337 *aOutResult = resultOrError.unwrap();
338 return NS_OK;
341 RefPtr<DataFlavorsPromise> nsBaseClipboard::AsyncHasDataMatchingFlavors(
342 const nsTArray<nsCString>& aFlavorList, int32_t aWhichClipboard) {
343 nsTArray<nsCString> results;
344 for (const auto& flavor : aFlavorList) {
345 bool hasMatchingFlavor = false;
346 nsresult rv = HasDataMatchingFlavors(AutoTArray<nsCString, 1>{flavor},
347 aWhichClipboard, &hasMatchingFlavor);
348 if (NS_SUCCEEDED(rv) && hasMatchingFlavor) {
349 results.AppendElement(flavor);
353 return DataFlavorsPromise::CreateAndResolve(std::move(results), __func__);
356 NS_IMETHODIMP
357 nsBaseClipboard::IsClipboardTypeSupported(int32_t aWhichClipboard,
358 bool* aRetval) {
359 NS_ENSURE_ARG_POINTER(aRetval);
360 switch (aWhichClipboard) {
361 case kGlobalClipboard:
362 // We always support the global clipboard.
363 *aRetval = true;
364 return NS_OK;
365 case kSelectionClipboard:
366 *aRetval = mClipboardCaps.supportsSelectionClipboard();
367 return NS_OK;
368 case kFindClipboard:
369 *aRetval = mClipboardCaps.supportsFindClipboard();
370 return NS_OK;
371 case kSelectionCache:
372 *aRetval = mClipboardCaps.supportsSelectionCache();
373 return NS_OK;
374 default:
375 *aRetval = false;
376 return NS_OK;
380 nsBaseClipboard::ClipboardCache* nsBaseClipboard::GetClipboardCacheIfValid(
381 int32_t aClipboardType) {
382 MOZ_ASSERT(nsIClipboard::IsClipboardTypeSupported(aClipboardType));
384 const mozilla::UniquePtr<ClipboardCache>& cache = mCaches[aClipboardType];
385 MOZ_ASSERT(cache);
387 if (!cache->GetTransferable()) {
388 MOZ_ASSERT(cache->GetSequenceNumber() == -1);
389 return nullptr;
392 auto changeCountOrError = GetNativeClipboardSequenceNumber(aClipboardType);
393 if (changeCountOrError.isErr()) {
394 return nullptr;
397 if (changeCountOrError.unwrap() != cache->GetSequenceNumber()) {
398 // Clipboard cache is invalid, clear it.
399 cache->Clear();
400 return nullptr;
403 return cache.get();
406 void nsBaseClipboard::ClipboardCache::Clear() {
407 if (mClipboardOwner) {
408 mClipboardOwner->LosingOwnership(mTransferable);
409 mClipboardOwner = nullptr;
411 mTransferable = nullptr;
412 mSequenceNumber = -1;