Bug 1689886 - Add a test-only clipboard.read implementation with ClipboardItem. r=nika

Differential Revision: https://phabricator.services.mozilla.com/D103588
This commit is contained in:
Tom Schuster 2021-02-10 20:50:56 +00:00
Родитель c1b72d4f7a
Коммит be7f4c6ca9
6 изменённых файлов: 73 добавлений и 40 удалений

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

@ -65,27 +65,78 @@ already_AddRefed<Promise> Clipboard::ReadHelper(
RefPtr<DataTransfer> dataTransfer = new DataTransfer( RefPtr<DataTransfer> dataTransfer = new DataTransfer(
this, ePaste, /* is external */ true, nsIClipboard::kGlobalClipboard); this, ePaste, /* is external */ true, nsIClipboard::kGlobalClipboard);
RefPtr<nsPIDOMWindowInner> owner = GetOwner();
// Create a new runnable // Create a new runnable
RefPtr<nsIRunnable> r = NS_NewRunnableFunction( RefPtr<nsIRunnable> r = NS_NewRunnableFunction(
"Clipboard::Read", "Clipboard::Read", [p, dataTransfer, aClipboardReadType, owner,
[p, dataTransfer, &aSubjectPrincipal, aClipboardReadType]() { principal = RefPtr{&aSubjectPrincipal}]() {
IgnoredErrorResult ier; IgnoredErrorResult ier;
switch (aClipboardReadType) { switch (aClipboardReadType) {
case eRead: case eRead: {
MOZ_LOG(GetClipboardLog(), LogLevel::Debug, MOZ_LOG(GetClipboardLog(), LogLevel::Debug,
("Clipboard, ReadHelper, read case\n")); ("Clipboard, ReadHelper, read case\n"));
dataTransfer->FillAllExternalData(); dataTransfer->FillAllExternalData();
// If there are items on the clipboard, data transfer will contain
// those, else, data transfer will be empty and we will be resolving // Convert the DataTransferItems to ClipboardItems.
// with an empty data transfer // FIXME(bug 1691825): This is only suitable for testing!
p->MaybeResolve(dataTransfer); // A real implementation would only read from the clipboard
// in ClipboardItem::getType instead of doing it here.
nsTArray<ClipboardItem::ItemEntry> entries;
DataTransferItemList* items = dataTransfer->Items();
for (size_t i = 0; i < items->Length(); i++) {
bool found = false;
DataTransferItem* item = items->IndexedGetter(i, found);
// Only allow strings and files.
if (!found || item->Kind() == DataTransferItem::KIND_OTHER) {
continue;
}
nsAutoString type;
item->GetType(type);
if (item->Kind() == DataTransferItem::KIND_STRING) {
// We just ignore items that we can't access.
IgnoredErrorResult ignored;
nsCOMPtr<nsIVariant> data = item->Data(principal, ignored);
if (NS_WARN_IF(!data || ignored.Failed())) {
continue;
}
nsAutoString string;
if (NS_WARN_IF(NS_FAILED(data->GetAsAString(string)))) {
continue;
}
ClipboardItem::ItemEntry* entry = entries.AppendElement();
entry->mType = type;
entry->mData.SetAsString() = string;
} else {
IgnoredErrorResult ignored;
RefPtr<File> file = item->GetAsFile(*principal, ignored);
if (NS_WARN_IF(!file || ignored.Failed())) {
continue;
}
ClipboardItem::ItemEntry* entry = entries.AppendElement();
entry->mType = type;
entry->mData.SetAsBlob() = file;
}
}
nsTArray<RefPtr<ClipboardItem>> sequence;
sequence.AppendElement(MakeRefPtr<ClipboardItem>(
owner, PresentationStyle::Unspecified, std::move(entries)));
p->MaybeResolve(sequence);
break; break;
}
case eReadText: case eReadText:
MOZ_LOG(GetClipboardLog(), LogLevel::Debug, MOZ_LOG(GetClipboardLog(), LogLevel::Debug,
("Clipboard, ReadHelper, read text case\n")); ("Clipboard, ReadHelper, read text case\n"));
nsAutoString str; nsAutoString str;
dataTransfer->GetData(NS_LITERAL_STRING_FROM_CSTRING(kTextMime), dataTransfer->GetData(NS_LITERAL_STRING_FROM_CSTRING(kTextMime),
str, aSubjectPrincipal, ier); str, *principal, ier);
// Either resolve with a string extracted from data transfer item // Either resolve with a string extracted from data transfer item
// or resolve with an empty string if nothing was found // or resolve with an empty string if nothing was found
p->MaybeResolve(str); p->MaybeResolve(str);

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

@ -14,8 +14,8 @@ typedef sequence<ClipboardItem> ClipboardItems;
[SecureContext, Exposed=Window, Pref="dom.events.asyncClipboard"] [SecureContext, Exposed=Window, Pref="dom.events.asyncClipboard"]
interface Clipboard : EventTarget { interface Clipboard : EventTarget {
[Pref="dom.events.asyncClipboard.dataTransfer", Throws, NeedsSubjectPrincipal] [Pref="dom.events.asyncClipboard.clipboardItem", Throws, NeedsSubjectPrincipal]
Promise<DataTransfer> read(); Promise<ClipboardItems> read();
[Func="Clipboard::ReadTextEnabled", Throws, NeedsSubjectPrincipal] [Func="Clipboard::ReadTextEnabled", Throws, NeedsSubjectPrincipal]
Promise<DOMString> readText(); Promise<DOMString> readText();

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

@ -1805,13 +1805,7 @@
value: true value: true
mirror: always mirror: always
# Disable clipboard read() by default # Disable ClipboardItem and clipboard.read/write by default
- name: dom.events.asyncClipboard.dataTransfer
type: bool
value: false
mirror: always
# Disable ClipboardItem and clipboard.write by default
- name: dom.events.asyncClipboard.clipboardItem - name: dom.events.asyncClipboard.clipboardItem
type: bool type: bool
value: false value: false

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

@ -1 +1,2 @@
prefs: [dom.events.asyncClipboard:true, dom.events.asyncClipboard.dataTransfer:true, dom.events.asyncClipboard.clipboardItem: true, dom.events.testing.asyncClipboard:true] prefs: [dom.events.asyncClipboard:true, dom.events.asyncClipboard.clipboardItem: true,
dom.events.testing.asyncClipboard:true]

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

@ -1,4 +0,0 @@
[async-navigator-clipboard-basics.https.html]
[navigator.clipboard.read() succeeds]
expected: FAIL

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

@ -59,7 +59,6 @@ function clearClipboard() {
add_task(async function setup() { add_task(async function setup() {
await SpecialPowers.pushPrefEnv({"set": [ await SpecialPowers.pushPrefEnv({"set": [
["dom.events.asyncClipboard", true], ["dom.events.asyncClipboard", true],
["dom.events.asyncClipboard.dataTransfer", true],
["dom.events.asyncClipboard.clipboardItem", true], ["dom.events.asyncClipboard.clipboardItem", true],
]}); ]});
}); });
@ -241,8 +240,9 @@ add_task(async function test_contentscript_clipboard_permission_write() {
// Test that with enough permissions, we are allowed to use read in content script // Test that with enough permissions, we are allowed to use read in content script
add_task(async function test_contentscript_clipboard_permission_read() { add_task(async function test_contentscript_clipboard_permission_read() {
function contentScript() { function contentScript() {
clipboardRead().then(function(dt) { clipboardRead().then(async function(items) {
let s = dt.getData("text/plain"); let blob = await items[0].getType("text/plain");
let s = await blob.text();
if (s == "HELLO") { if (s == "HELLO") {
browser.test.succeed("Read promise successfully read the right thing"); browser.test.succeed("Read promise successfully read the right thing");
} else { } else {
@ -323,24 +323,15 @@ add_task(async function test_contentscript_clipboard_nocontents_readtext() {
await extension.unload(); await extension.unload();
}); });
// Test that performing read(...) when the clipboard is empty returns an empty data transfer // Test that performing read(...) when the clipboard is empty returns an empty ClipboardItem
add_task(async function test_contentscript_clipboard_nocontents_read() { add_task(async function test_contentscript_clipboard_nocontents_read() {
function contentScript() { function contentScript() {
clipboardRead().then(function(dataT) { clipboardRead().then(function(items) {
// On macOS if we clear the clipboard and read from it, there will be if (items[0].types.length) {
// no items in the data transfer object.
// On linux with e10s enabled clearing of the clipboard does not happen in
// the same way as it does on other platforms. So when we clear the clipboard
// and read from it, the data transfer object contains an item of type
// text/plain and kind string, but we can't call getAsString on it to verify
// that at least it is an empty string because the callback never gets invoked.
if (!dataT.items.length ||
(dataT.items.length == 1 && dataT.items[0].type == "text/plain" &&
dataT.items[0].kind == "string")) {
browser.test.succeed("Read promise successfully resolved");
} else {
browser.test.fail("Read read the wrong thing from clipboard, " + browser.test.fail("Read read the wrong thing from clipboard, " +
"data transfer has this many items:" + dataT.items.length); "ClipboardItem has this many entries: " + items[0].types.length);
} else {
browser.test.succeed("Read promise successfully resolved");
} }
browser.test.sendMessage("ready"); browser.test.sendMessage("ready");
}, function(err) { }, function(err) {