diff options
Diffstat (limited to '')
-rw-r--r-- | widget/nsBaseClipboard.cpp | 301 |
1 files changed, 301 insertions, 0 deletions
diff --git a/widget/nsBaseClipboard.cpp b/widget/nsBaseClipboard.cpp new file mode 100644 index 0000000000..1af81819ad --- /dev/null +++ b/widget/nsBaseClipboard.cpp @@ -0,0 +1,301 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsBaseClipboard.h" + +#include "nsIClipboardOwner.h" +#include "nsError.h" +#include "nsXPCOM.h" + +using mozilla::GenericPromise; +using mozilla::LogLevel; +using mozilla::UniquePtr; +using mozilla::dom::ClipboardCapabilities; + +NS_IMPL_ISUPPORTS(ClipboardSetDataHelper::AsyncSetClipboardData, + nsIAsyncSetClipboardData) + +ClipboardSetDataHelper::AsyncSetClipboardData::AsyncSetClipboardData( + int32_t aClipboardType, ClipboardSetDataHelper* aClipboard, + nsIAsyncSetClipboardDataCallback* aCallback) + : mClipboardType(aClipboardType), + mClipboard(aClipboard), + mCallback(aCallback) { + MOZ_ASSERT(mClipboard); + MOZ_ASSERT(mClipboard->IsClipboardTypeSupported(mClipboardType)); +} + +NS_IMETHODIMP +ClipboardSetDataHelper::AsyncSetClipboardData::SetData( + nsITransferable* aTransferable, nsIClipboardOwner* aOwner) { + if (!IsValid()) { + return NS_ERROR_FAILURE; + } + + MOZ_ASSERT(mClipboard); + MOZ_ASSERT(mClipboard->IsClipboardTypeSupported(mClipboardType)); + MOZ_DIAGNOSTIC_ASSERT(mClipboard->mPendingWriteRequests[mClipboardType] == + this); + + RefPtr<AsyncSetClipboardData> request = + std::move(mClipboard->mPendingWriteRequests[mClipboardType]); + nsresult rv = mClipboard->SetData(aTransferable, aOwner, mClipboardType); + MaybeNotifyCallback(rv); + + return rv; +} + +NS_IMETHODIMP +ClipboardSetDataHelper::AsyncSetClipboardData::Abort(nsresult aReason) { + // Note: This may be called during destructor, so it should not attempt to + // take a reference to mClipboard. + + if (!IsValid() || !NS_FAILED(aReason)) { + return NS_ERROR_FAILURE; + } + + MaybeNotifyCallback(aReason); + return NS_OK; +} + +void ClipboardSetDataHelper::AsyncSetClipboardData::MaybeNotifyCallback( + nsresult aResult) { + // Note: This may be called during destructor, so it should not attempt to + // take a reference to mClipboard. + + MOZ_ASSERT(IsValid()); + if (nsCOMPtr<nsIAsyncSetClipboardDataCallback> callback = + mCallback.forget()) { + callback->OnComplete(aResult); + } + // Once the callback is notified, setData should not be allowed, so invalidate + // this request. + mClipboard = nullptr; +} + +NS_IMPL_ISUPPORTS(ClipboardSetDataHelper, nsIClipboard) + +ClipboardSetDataHelper::~ClipboardSetDataHelper() { + for (auto& request : mPendingWriteRequests) { + if (request) { + request->Abort(NS_ERROR_ABORT); + request = nullptr; + } + } +} + +void ClipboardSetDataHelper::RejectPendingAsyncSetDataRequestIfAny( + int32_t aClipboardType) { + MOZ_ASSERT(nsIClipboard::IsClipboardTypeSupported(aClipboardType)); + auto& request = mPendingWriteRequests[aClipboardType]; + if (request) { + request->Abort(NS_ERROR_ABORT); + request = nullptr; + } +} + +NS_IMETHODIMP +ClipboardSetDataHelper::SetData(nsITransferable* aTransferable, + nsIClipboardOwner* aOwner, + int32_t aWhichClipboard) { + NS_ENSURE_ARG(aTransferable); + if (!nsIClipboard::IsClipboardTypeSupported(aWhichClipboard)) { + return NS_ERROR_DOM_NOT_SUPPORTED_ERR; + } + + // Reject existing pending asyncSetData request if any. + RejectPendingAsyncSetDataRequestIfAny(aWhichClipboard); + + return SetNativeClipboardData(aTransferable, aOwner, aWhichClipboard); +} + +NS_IMETHODIMP ClipboardSetDataHelper::AsyncSetData( + int32_t aWhichClipboard, nsIAsyncSetClipboardDataCallback* aCallback, + nsIAsyncSetClipboardData** _retval) { + *_retval = nullptr; + if (!nsIClipboard::IsClipboardTypeSupported(aWhichClipboard)) { + return NS_ERROR_DOM_NOT_SUPPORTED_ERR; + } + + // Reject existing pending AsyncSetData request if any. + RejectPendingAsyncSetDataRequestIfAny(aWhichClipboard); + + // Create a new AsyncSetClipboardData. + RefPtr<AsyncSetClipboardData> request = + mozilla::MakeRefPtr<AsyncSetClipboardData>(aWhichClipboard, this, + aCallback); + mPendingWriteRequests[aWhichClipboard] = request; + request.forget(_retval); + return NS_OK; +} + +nsBaseClipboard::nsBaseClipboard(const ClipboardCapabilities& aClipboardCaps) + : mClipboardCaps(aClipboardCaps) { + using mozilla::MakeUnique; + // Initialize clipboard cache. + mCaches[kGlobalClipboard] = MakeUnique<ClipboardCache>(); + if (mClipboardCaps.supportsSelectionClipboard()) { + mCaches[kSelectionClipboard] = MakeUnique<ClipboardCache>(); + } + if (mClipboardCaps.supportsFindClipboard()) { + mCaches[kFindClipboard] = MakeUnique<ClipboardCache>(); + } + if (mClipboardCaps.supportsSelectionCache()) { + mCaches[kSelectionCache] = MakeUnique<ClipboardCache>(); + } +} + +NS_IMPL_ISUPPORTS_INHERITED0(nsBaseClipboard, ClipboardSetDataHelper) + +/** + * Sets the transferable object + * + */ +NS_IMETHODIMP nsBaseClipboard::SetData(nsITransferable* aTransferable, + nsIClipboardOwner* anOwner, + int32_t aWhichClipboard) { + NS_ASSERTION(aTransferable, "clipboard given a null transferable"); + + CLIPBOARD_LOG("%s", __FUNCTION__); + + if (!nsIClipboard::IsClipboardTypeSupported(aWhichClipboard)) { + return NS_ERROR_FAILURE; + } + + const auto& clipboardCache = mCaches[aWhichClipboard]; + MOZ_ASSERT(clipboardCache); + if (aTransferable == clipboardCache->GetTransferable() && + anOwner == clipboardCache->GetClipboardOwner()) { + CLIPBOARD_LOG("%s: skipping update.", __FUNCTION__); + return NS_OK; + } + + mEmptyingForSetData = true; + if (NS_FAILED(EmptyClipboard(aWhichClipboard))) { + CLIPBOARD_LOG("%s: emptying clipboard failed.", __FUNCTION__); + } + mEmptyingForSetData = false; + + clipboardCache->Update(aTransferable, anOwner); + + nsresult rv = NS_ERROR_FAILURE; + if (aTransferable) { + mIgnoreEmptyNotification = true; + rv = ClipboardSetDataHelper::SetData(aTransferable, anOwner, + aWhichClipboard); + mIgnoreEmptyNotification = false; + } + if (NS_FAILED(rv)) { + CLIPBOARD_LOG("%s: setting native clipboard data failed.", __FUNCTION__); + } + + return rv; +} + +/** + * Gets the transferable object + * + */ +NS_IMETHODIMP nsBaseClipboard::GetData(nsITransferable* aTransferable, + int32_t aWhichClipboard) { + NS_ASSERTION(aTransferable, "clipboard given a null transferable"); + + CLIPBOARD_LOG("%s", __FUNCTION__); + + if (!nsIClipboard::IsClipboardTypeSupported(kSelectionClipboard) && + !nsIClipboard::IsClipboardTypeSupported(kFindClipboard) && + aWhichClipboard != kGlobalClipboard) { + return NS_ERROR_FAILURE; + } + + if (aTransferable) + return GetNativeClipboardData(aTransferable, aWhichClipboard); + + return NS_ERROR_FAILURE; +} + +RefPtr<GenericPromise> nsBaseClipboard::AsyncGetData( + nsITransferable* aTransferable, int32_t aWhichClipboard) { + nsresult rv = GetData(aTransferable, aWhichClipboard); + if (NS_FAILED(rv)) { + return GenericPromise::CreateAndReject(rv, __func__); + } + + return GenericPromise::CreateAndResolve(true, __func__); +} + +NS_IMETHODIMP nsBaseClipboard::EmptyClipboard(int32_t aWhichClipboard) { + CLIPBOARD_LOG("%s: clipboard=%i", __FUNCTION__, aWhichClipboard); + + if (!nsIClipboard::IsClipboardTypeSupported(aWhichClipboard)) { + return NS_ERROR_FAILURE; + } + + if (mIgnoreEmptyNotification) { + MOZ_DIAGNOSTIC_ASSERT(false, "How did we get here?"); + return NS_OK; + } + + const auto& clipboardCache = mCaches[aWhichClipboard]; + MOZ_ASSERT(clipboardCache); + clipboardCache->Clear(); + + return NS_OK; +} + +NS_IMETHODIMP +nsBaseClipboard::HasDataMatchingFlavors(const nsTArray<nsCString>& aFlavorList, + int32_t aWhichClipboard, + bool* outResult) { + *outResult = true; // say we always do. + return NS_OK; +} + +RefPtr<DataFlavorsPromise> nsBaseClipboard::AsyncHasDataMatchingFlavors( + const nsTArray<nsCString>& aFlavorList, int32_t aWhichClipboard) { + nsTArray<nsCString> results; + for (const auto& flavor : aFlavorList) { + bool hasMatchingFlavor = false; + nsresult rv = HasDataMatchingFlavors(AutoTArray<nsCString, 1>{flavor}, + aWhichClipboard, &hasMatchingFlavor); + if (NS_SUCCEEDED(rv) && hasMatchingFlavor) { + results.AppendElement(flavor); + } + } + + return DataFlavorsPromise::CreateAndResolve(std::move(results), __func__); +} + +NS_IMETHODIMP +nsBaseClipboard::IsClipboardTypeSupported(int32_t aWhichClipboard, + bool* aRetval) { + NS_ENSURE_ARG_POINTER(aRetval); + switch (aWhichClipboard) { + case kGlobalClipboard: + // We always support the global clipboard. + *aRetval = true; + return NS_OK; + case kSelectionClipboard: + *aRetval = mClipboardCaps.supportsSelectionClipboard(); + return NS_OK; + case kFindClipboard: + *aRetval = mClipboardCaps.supportsFindClipboard(); + return NS_OK; + case kSelectionCache: + *aRetval = mClipboardCaps.supportsSelectionCache(); + return NS_OK; + default: + *aRetval = false; + return NS_OK; + } +} + +void nsBaseClipboard::ClipboardCache::Clear() { + if (mClipboardOwner) { + mClipboardOwner->LosingOwnership(mTransferable); + mClipboardOwner = nullptr; + } + mTransferable = nullptr; +} |