Bug 1688279 - Support strings as data in ClipboardItem r=nika

Differential Revision: https://phabricator.services.mozilla.com/D102791
This commit is contained in:
Tom Schuster 2021-01-29 22:33:55 +00:00
Родитель 71ce0cfc2a
Коммит 0f69816f22
6 изменённых файлов: 63 добавлений и 18 удалений

Просмотреть файл

@ -22,6 +22,7 @@
#include "nsComponentManagerUtils.h"
#include "nsContentUtils.h"
#include "nsServiceManagerUtils.h"
#include "nsStringStream.h"
#include "nsITransferable.h"
#include "nsArrayUtils.h"
#include "nsNetUtil.h"
@ -160,17 +161,22 @@ NS_IMPL_ISUPPORTS0(BlobTextHandler)
RefPtr<NativeEntryPromise> GetStringNativeEntry(
const ClipboardItem::ItemEntry& entry) {
RefPtr<BlobTextHandler> handler = new BlobTextHandler(entry.mType);
if (entry.mData.IsString()) {
RefPtr<nsVariantCC> variant = new nsVariantCC();
variant->SetAsAString(entry.mData.GetAsString());
NativeEntry native(entry.mType, variant);
return NativeEntryPromise::CreateAndResolve(native, __func__);
}
RefPtr<BlobTextHandler> handler = new BlobTextHandler(entry.mType);
IgnoredErrorResult ignored;
RefPtr<Promise> promise = entry.mBlob->Text(ignored);
RefPtr<Promise> promise = entry.mData.GetAsBlob()->Text(ignored);
if (ignored.Failed()) {
CopyableErrorResult rv;
rv.ThrowUnknownError("Unable to read blob as text");
return NativeEntryPromise::CreateAndReject(rv, __func__);
}
promise->AppendNativeHandler(handler);
return handler->Promise();
}
@ -209,9 +215,15 @@ NS_IMPL_ISUPPORTS(ImageDecodeCallback, imgIContainerCallback)
RefPtr<NativeEntryPromise> GetImageNativeEntry(
const ClipboardItem::ItemEntry& entry) {
if (entry.mData.IsString()) {
CopyableErrorResult rv;
rv.ThrowTypeError("Can not use string for image data");
return NativeEntryPromise::CreateAndReject(rv, __func__);
}
IgnoredErrorResult ignored;
nsCOMPtr<nsIInputStream> stream;
entry.mBlob->CreateInputStream(getter_AddRefs(stream), ignored);
entry.mData.GetAsBlob()->CreateInputStream(getter_AddRefs(stream), ignored);
if (ignored.Failed()) {
CopyableErrorResult rv;
rv.ThrowUnknownError("Unable to read blob as image");
@ -219,11 +231,9 @@ RefPtr<NativeEntryPromise> GetImageNativeEntry(
}
RefPtr<ImageDecodeCallback> callback = new ImageDecodeCallback();
nsCOMPtr<imgITools> imgtool = do_CreateInstance("@mozilla.org/image/tools;1");
imgtool->DecodeImageAsync(stream, NS_ConvertUTF16toUTF8(entry.mType),
callback, GetMainThreadSerialEventTarget());
return callback->Promise();
}
@ -363,8 +373,7 @@ already_AddRefed<Promise> Clipboard::WriteText(const nsAString& aData,
nsTArray<ClipboardItem::ItemEntry> items;
ClipboardItem::ItemEntry* entry = items.AppendElement();
entry->mType = NS_LITERAL_STRING_FROM_CSTRING(kTextMime);
entry->mBlob = Blob::CreateStringBlob(
GetOwnerGlobal(), NS_ConvertUTF16toUTF8(aData), entry->mType);
entry->mData.SetAsString() = aData;
nsTArray<OwningNonNull<ClipboardItem>> sequence;
RefPtr<ClipboardItem> item = new ClipboardItem(

Просмотреть файл

@ -13,11 +13,11 @@ namespace mozilla::dom {
void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
ClipboardItem::ItemEntry& aField,
const char* aName, uint32_t aFlags = 0) {
ImplCycleCollectionTraverse(aCallback, aField.mBlob, aName, aFlags);
ImplCycleCollectionTraverse(aCallback, aField.mData, aName, aFlags);
}
void ImplCycleCollectionUnlink(ClipboardItem::ItemEntry& aField) {
ImplCycleCollectionUnlink(aField.mBlob);
ImplCycleCollectionUnlink(aField.mData);
}
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ClipboardItem, mOwner, mItems)
@ -34,7 +34,7 @@ ClipboardItem::ClipboardItem(nsISupports* aOwner,
// static
already_AddRefed<ClipboardItem> ClipboardItem::Constructor(
const GlobalObject& aGlobal,
const Record<nsString, OwningNonNull<Blob>>& aItems,
const Record<nsString, OwningStringOrBlob>& aItems,
const ClipboardItemOptions& aOptions, ErrorResult& aRv) {
if (aItems.Entries().IsEmpty()) {
aRv.ThrowTypeError("At least one entry required");
@ -45,7 +45,7 @@ already_AddRefed<ClipboardItem> ClipboardItem::Constructor(
for (const auto& entry : aItems.Entries()) {
ItemEntry* item = items.AppendElement();
item->mType = entry.mKey;
item->mBlob = entry.mValue;
item->mData = entry.mValue;
}
RefPtr<ClipboardItem> item = new ClipboardItem(
@ -69,7 +69,13 @@ already_AddRefed<Promise> ClipboardItem::GetType(const nsAString& aType,
for (const auto& item : mItems) {
if (item.mType == aType) {
p->MaybeResolve(item.mBlob);
if (item.mData.IsBlob()) {
p->MaybeResolve(item.mData);
} else {
NS_ConvertUTF16toUTF8 string(item.mData.GetAsString());
RefPtr<Blob> blob = Blob::CreateStringBlob(global, string, item.mType);
p->MaybeResolve(blob);
}
return p.forget();
}
}

Просмотреть файл

@ -23,7 +23,7 @@ class ClipboardItem final : public nsWrapperCache {
public:
struct ItemEntry {
nsString mType;
RefPtr<Blob> mBlob;
OwningStringOrBlob mData;
};
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(ClipboardItem)
@ -34,7 +34,7 @@ class ClipboardItem final : public nsWrapperCache {
static already_AddRefed<ClipboardItem> Constructor(
const GlobalObject& aGlobal,
const Record<nsString, OwningNonNull<Blob>>& aItems,
const Record<nsString, OwningStringOrBlob>& aItems,
const ClipboardItemOptions& aOptions, ErrorResult& aRv);
dom::PresentationStyle PresentationStyle() const {

Просмотреть файл

@ -26,15 +26,15 @@ interface Clipboard : EventTarget {
Promise<void> writeText(DOMString data);
};
// typedef (DOMString or Blob) ClipboardItemDataType;
typedef (DOMString or Blob) ClipboardItemDataType;
// typedef Promise<ClipboardItemDataType> ClipboardItemData;
// callback ClipboardItemDelayedCallback = ClipboardItemData ();
[SecureContext, Exposed=Window, Pref="dom.events.asyncClipboard.clipboardItem"]
interface ClipboardItem {
// Note: The spec uses ClipboardItemData instead of Blob.
// Note: The spec uses Promise<ClipboardItemDataType>.
[Throws]
constructor(record<DOMString, Blob> items,
constructor(record<DOMString, ClipboardItemDataType> items,
optional ClipboardItemOptions options = {});
// static ClipboardItem createDelayed(

Просмотреть файл

@ -46,6 +46,11 @@ promise_test(async t => {
navigator.clipboard.writeText());
}, 'navigator.clipboard.writeText() fails (expect DOMString)');
promise_test(async () => {
const item = new ClipboardItem({'text/plain': 'test'});
await navigator.clipboard.write([item]);
}, 'navigator.clipboard.write({string : DOMString}) succeeds');
promise_test(async () => {
const fetched = await fetch('/clipboard-apis/resources/greenbox.png');
const image = await fetched.blob();
@ -64,6 +69,11 @@ promise_test(async() => {
await navigator.clipboard.write([item]);
}, 'navigator.clipboard.write([text + png] succeeds');
promise_test(async t => {
const item = new ClipboardItem({'image/png': 'not an image'});
await promise_rejects_js(t, TypeError, navigator.clipboard.write([item]));
}, 'navigator.clipboard.write(image/png DOMString) fails');
promise_test(async () => {
const result = await navigator.clipboard.read();
assert_true(result instanceof Object);

Просмотреть файл

@ -89,4 +89,24 @@ promise_test(async t => {
promise_rejects_dom(t, "NotFoundError", item.getType('type not in item'));
promise_rejects_dom(t, "NotFoundError", item.getType('text/plain:subtype'));
}, "getType(DOMString type) rejects correctly when querying for missing type");
promise_test(async () => {
const item =
new ClipboardItem({'text/plain': 'abc', 'not a/real type': 'xxx'});
const blob = await item.getType('text/plain');
assert_equals(blob.type, 'text/plain');
const text = await (new Response(blob)).text();
assert_equals(text, 'abc');
}, "getType(DOMString valid type) converts DOMString to Blob");
promise_test(async () => {
const item =
new ClipboardItem({'text/plain': 'abc', 'not a/real type': 'xxx'});
const blob = await item.getType('not a/real type');
assert_equals(blob.type, 'not a/real type');
const text = await (new Response(blob)).text();
assert_equals(text, 'xxx');
}, "getType(DOMString invalid type) converts DOMString to Blob");
</script>