diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
commit | 43a97878ce14b72f0981164f87f2e35e14151312 (patch) | |
tree | 620249daf56c0258faa40cbdcf9cfba06de2a846 /dom/events/ClipboardItem.cpp | |
parent | Initial commit. (diff) | |
download | firefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip |
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/events/ClipboardItem.cpp')
-rw-r--r-- | dom/events/ClipboardItem.cpp | 245 |
1 files changed, 245 insertions, 0 deletions
diff --git a/dom/events/ClipboardItem.cpp b/dom/events/ClipboardItem.cpp new file mode 100644 index 0000000000..62d90aef8f --- /dev/null +++ b/dom/events/ClipboardItem.cpp @@ -0,0 +1,245 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "mozilla/dom/ClipboardItem.h" + +#include "mozilla/dom/Promise.h" +#include "mozilla/dom/Record.h" +#include "nsComponentManagerUtils.h" +#include "nsIClipboard.h" +#include "nsIInputStream.h" +#include "nsISupportsPrimitives.h" +#include "nsNetUtil.h" +#include "nsServiceManagerUtils.h" + +namespace mozilla::dom { + +NS_IMPL_CYCLE_COLLECTION(ClipboardItem::ItemEntry, mData, + mPendingGetTypeRequests) + +ClipboardItem::ItemEntry::ItemEntry(const nsAString& aType, + const nsACString& aFormat) + : mType(aType), mFormat(aFormat) { + // XXX https://bugzilla.mozilla.org/show_bug.cgi?id=1776879. + // In most of cases, the mType and mFormat are the same, execpt for plain + // text. We expose it as "text/plain" to the web, but we use "text/unicode" + // internally to retrieve from system clipboard. + MOZ_ASSERT_IF( + !mType.Equals(NS_ConvertUTF8toUTF16(mFormat)), + mType.EqualsLiteral(kTextMime) && mFormat.EqualsLiteral(kUnicodeMime)); +} + +void ClipboardItem::ItemEntry::SetData(already_AddRefed<Blob>&& aBlob) { + // XXX maybe we could consider adding a method to check whether the union + // object is uninitialized or initialized. + MOZ_DIAGNOSTIC_ASSERT(!mData.IsString() && !mData.IsBlob(), + "Data should be uninitialized."); + MOZ_DIAGNOSTIC_ASSERT( + !mLoadingPromise.Exists(), + "Should not be in the process of loading data from clipboard."); + mData.SetAsBlob() = std::move(aBlob); +} + +void ClipboardItem::ItemEntry::LoadData(nsIGlobalObject& aGlobal, + nsITransferable& aTransferable) { + // XXX maybe we could consider adding a method to check whether the union + // object is uninitialized or initialized. + MOZ_DIAGNOSTIC_ASSERT(!mData.IsString() && !mData.IsBlob(), + "Data should be uninitialized."); + MOZ_DIAGNOSTIC_ASSERT( + !mLoadingPromise.Exists(), + "Should not be in the process of loading data from clipboard."); + + nsresult rv; + nsCOMPtr<nsIClipboard> clipboard( + do_GetService("@mozilla.org/widget/clipboard;1", &rv)); + if (NS_FAILED(rv)) { + return; + } + + nsCOMPtr<nsITransferable> trans(&aTransferable); + clipboard->AsyncGetData(trans, nsIClipboard::kGlobalClipboard) + ->Then( + GetMainThreadSerialEventTarget(), __func__, + /* resolved */ + [self = RefPtr{this}, global = RefPtr{&aGlobal}, trans]() { + self->mLoadingPromise.Complete(); + + nsCOMPtr<nsISupports> data; + nsresult rv = trans->GetTransferData(self->Format().get(), + getter_AddRefs(data)); + if (NS_WARN_IF(NS_FAILED(rv))) { + self->RejectPendingGetTypePromises(rv); + return; + } + + RefPtr<Blob> blob; + if (nsCOMPtr<nsISupportsString> supportsstr = + do_QueryInterface(data)) { + nsAutoString str; + supportsstr->GetData(str); + + blob = Blob::CreateStringBlob(global, NS_ConvertUTF16toUTF8(str), + self->Type()); + } else if (nsCOMPtr<nsIInputStream> istream = + do_QueryInterface(data)) { + uint64_t available; + void* data = nullptr; + nsresult rv = + NS_ReadInputStreamToBuffer(istream, &data, -1, &available); + if (NS_WARN_IF(NS_FAILED(rv))) { + self->RejectPendingGetTypePromises(rv); + return; + } + + blob = + Blob::CreateMemoryBlob(global, data, available, self->Type()); + } else if (nsCOMPtr<nsISupportsCString> supportscstr = + do_QueryInterface(data)) { + nsAutoCString str; + supportscstr->GetData(str); + + blob = Blob::CreateStringBlob(global, str, self->Type()); + } + + if (!blob) { + self->RejectPendingGetTypePromises(NS_ERROR_DOM_DATA_ERR); + return; + } + + self->ResolvePendingGetTypePromises(*blob); + self->SetData(blob.forget()); + }, + /* rejected */ + [self = RefPtr{this}](nsresult rv) { + self->mLoadingPromise.Complete(); + self->RejectPendingGetTypePromises(rv); + }) + ->Track(mLoadingPromise); +} + +void ClipboardItem::ItemEntry::ReactPromise(nsIGlobalObject& aGlobal, + Promise& aPromise) { + // Still loading data from system clipboard. + if (mLoadingPromise.Exists()) { + mPendingGetTypeRequests.AppendElement(&aPromise); + return; + } + + if (mData.IsBlob()) { + aPromise.MaybeResolve(mData); + return; + } + + // XXX This is for the case that data is from ClipboardItem constructor, + // maybe we should also load that into a Blob earlier. But Safari returns + // different `Blob` instances for each `getTypes` call if the string is from + // ClipboardItem constructor, which is more like our current setup. + if (mData.IsString()) { + if (RefPtr<Blob> blob = Blob::CreateStringBlob( + &aGlobal, NS_ConvertUTF16toUTF8(mData.GetAsString()), mType)) { + aPromise.MaybeResolve(blob); + return; + } + } + + aPromise.MaybeRejectWithDataError("The data for type '"_ns + + NS_ConvertUTF16toUTF8(mType) + + "' was not found"_ns); +} + +void ClipboardItem::ItemEntry::RejectPendingGetTypePromises(nsresult rv) { + MOZ_ASSERT(!mLoadingPromise.Exists(), + "Should not be in the process of loading data from clipboard."); + nsTArray<RefPtr<Promise>> promises = std::move(mPendingGetTypeRequests); + for (auto& promise : promises) { + promise->MaybeReject(rv); + } +} + +void ClipboardItem::ItemEntry::ResolvePendingGetTypePromises(Blob& aBlob) { + MOZ_ASSERT(!mLoadingPromise.Exists(), + "Should not be in the process of loading data from clipboard."); + nsTArray<RefPtr<Promise>> promises = std::move(mPendingGetTypeRequests); + for (auto& promise : promises) { + promise->MaybeResolve(&aBlob); + } +} + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ClipboardItem, mOwner, mItems) + +ClipboardItem::ClipboardItem(nsISupports* aOwner, + const dom::PresentationStyle aPresentationStyle, + nsTArray<RefPtr<ItemEntry>>&& aItems) + : mOwner(aOwner), + mPresentationStyle(aPresentationStyle), + mItems(std::move(aItems)) {} + +// static +already_AddRefed<ClipboardItem> ClipboardItem::Constructor( + const GlobalObject& aGlobal, + const Record<nsString, OwningStringOrBlob>& aItems, + const ClipboardItemOptions& aOptions, ErrorResult& aRv) { + if (aItems.Entries().IsEmpty()) { + aRv.ThrowTypeError("At least one entry required"); + return nullptr; + } + + nsTArray<RefPtr<ItemEntry>> items; + for (const auto& entry : aItems.Entries()) { + nsAutoCString format = entry.mKey.EqualsLiteral(kTextMime) + ? nsAutoCString(kUnicodeMime) + : NS_ConvertUTF16toUTF8(entry.mKey); + items.AppendElement( + MakeRefPtr<ItemEntry>(entry.mKey, format, entry.mValue)); + } + + RefPtr<ClipboardItem> item = MakeRefPtr<ClipboardItem>( + aGlobal.GetAsSupports(), aOptions.mPresentationStyle, std::move(items)); + return item.forget(); +} + +void ClipboardItem::GetTypes(nsTArray<nsString>& aTypes) const { + for (const auto& item : mItems) { + aTypes.AppendElement(item->Type()); + } +} + +already_AddRefed<Promise> ClipboardItem::GetType(const nsAString& aType, + ErrorResult& aRv) { + nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject()); + RefPtr<Promise> p = Promise::Create(global, aRv); + if (aRv.Failed()) { + return nullptr; + } + + for (auto& item : mItems) { + MOZ_ASSERT(item); + + const nsAString& type = item->Type(); + if (type == aType) { + nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject()); + if (NS_WARN_IF(!global)) { + p->MaybeReject(NS_ERROR_UNEXPECTED); + return p.forget(); + } + + item->ReactPromise(*global, *p); + return p.forget(); + } + } + + p->MaybeRejectWithNotFoundError( + "The type '"_ns + NS_ConvertUTF16toUTF8(aType) + "' was not found"_ns); + return p.forget(); +} + +JSObject* ClipboardItem::WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) { + return mozilla::dom::ClipboardItem_Binding::Wrap(aCx, this, aGivenProto); +} + +} // namespace mozilla::dom |