mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-07 18:04:46 +00:00
Bug 1688279 - Support strings as data in ClipboardItem r=nika
Differential Revision: https://phabricator.services.mozilla.com/D102791
This commit is contained in:
parent
71ce0cfc2a
commit
0f69816f22
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user