Bug 1798548 - Add test for pasting real files. r=nika,mac-reviewers,bradwerth

Differential Revision: https://phabricator.services.mozilla.com/D160944
This commit is contained in:
Tom Schuster 2023-01-17 12:04:42 +00:00
Родитель 619dc044f1
Коммит 9e258d177c
4 изменённых файлов: 163 добавлений и 47 удалений

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

@ -1,33 +1,48 @@
// This test is used to check that pasting files removes all non-file data from
// event.clipboardData.
/* 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/. */
// Test that (real) files can be pasted into chrome/content.
// Pasting files should also hide all other data from content.
function setClipboard(path) {
const file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
file.initWithPath(path);
const trans = Cc["@mozilla.org/widget/transferable;1"].createInstance(
Ci.nsITransferable
);
trans.init(null);
trans.addDataFlavor("application/x-moz-file");
trans.setTransferData("application/x-moz-file", file);
trans.addDataFlavor("text/unicode");
const str = Cc["@mozilla.org/supports-string;1"].createInstance(
Ci.nsISupportsString
);
str.data = "Alternate";
trans.setTransferData("text/unicode", str);
// Write to clipboard.
Services.clipboard.setData(trans, null, Ci.nsIClipboard.kGlobalClipboard);
}
add_task(async function() {
await SpecialPowers.pushPrefEnv({
set: [["dom.events.dataTransfer.mozFile.enabled", true]],
});
var input = document.createElement("input");
document.documentElement.appendChild(input);
// Create a temporary file that will be pasted.
const file = await IOUtils.createUniqueFile(
PathUtils.tempDir,
"test-file.txt",
0o600
);
await IOUtils.writeUTF8(file, "Hello World!");
input.focus();
input.value = "Text";
input.select();
await new Promise((resolve, reject) => {
input.addEventListener(
"copy",
function(event) {
event.clipboardData.setData("text/plain", "Alternate");
// For this test, it doesn't matter that the file isn't actually a file.
event.clipboardData.setData("application/x-moz-file", "Sample");
event.preventDefault();
resolve();
},
{ capture: true, once: true }
);
EventUtils.synthesizeKey("c", { accelKey: true });
});
// Put the data directly onto the native clipboard to make sure
// it isn't handled internally in Gecko somehow.
setClipboard(file);
let tab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
@ -35,17 +50,27 @@ add_task(async function() {
);
let browser = tab.linkedBrowser;
await SpecialPowers.spawn(browser, [], async function(arg) {
let resultPromise = SpecialPowers.spawn(browser, [], function(arg) {
return new Promise(resolve => {
content.document.addEventListener("testresult", event => {
resolve(event.detail.result);
});
});
});
// Focus <input> in content
await SpecialPowers.spawn(browser, [], async function() {
content.document.getElementById("input").focus();
});
// Paste file into <input> in content
await BrowserTestUtils.synthesizeKey("v", { accelKey: true }, browser);
let output = await SpecialPowers.spawn(browser, [], async function(arg) {
return content.document.getElementById("output").textContent;
});
is(output, "Passed", "Paste file");
let result = await resultPromise;
is(result, PathUtils.filename(file), "Correctly pasted file in content");
var input = document.createElement("input");
document.documentElement.appendChild(input);
input.focus();
await new Promise((resolve, reject) => {
@ -55,6 +80,11 @@ add_task(async function() {
let dt = event.clipboardData;
is(dt.types.length, 3, "number of types");
ok(dt.types.includes("text/plain"), "text/plain exists in types");
ok(
dt.types.includes("application/x-moz-file"),
"application/x-moz-file exists in types"
);
is(dt.types[2], "Files", "Last type should be 'Files'");
ok(
dt.mozTypesAt(0).contains("text/plain"),
"text/plain exists in mozTypesAt"
@ -70,6 +100,23 @@ add_task(async function() {
"text/plain returned in mozGetDataAt"
);
ok(
dt.mozTypesAt(0).contains("application/x-moz-file"),
"application/x-moz-file exists in mozTypesAt"
);
let mozFile = dt.mozGetDataAt("application/x-moz-file", 0);
ok(
mozFile instanceof Ci.nsIFile,
"application/x-moz-file returned nsIFile with mozGetDataAt"
);
is(
mozFile.leafName,
PathUtils.filename(file),
"nsIFile has correct leafName"
);
resolve();
},
{ capture: true, once: true }
@ -81,4 +128,6 @@ add_task(async function() {
input.remove();
BrowserTestUtils.removeTab(tab);
await IOUtils.remove(file);
});

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

@ -1,28 +1,52 @@
<html><body>
<script>
function checkPaste(event) {
let output = document.getElementById("output");
output.textContent = checkPasteHelper(event);
}
function checkPasteHelper(event) {
let dt = event.clipboardData;
if (dt.types.length != 2)
return "Wrong number of types; got " + dt.types.length;
for (let type of dt.types) {
if (type != "Files" && type != "application/x-moz-file")
return "Invalid type for types; got" + type;
async function checkPaste(event) {
let result = null;
try {
result = await checkPasteHelper(event);
} catch (e) {
result = e.toString();
}
if (dt.getData("text/plain"))
return "text/plain found with getData";
document.dispatchEvent(new CustomEvent('testresult', {
detail: { result }
}));
}
return "Passed";
function is(a, b, msg) {
if (!Object.is(a, b)) {
throw new Error(`FAIL: expected ${b} got ${a} - ${msg}`);
}
}
async function checkPasteHelper(event) {
let dt = event.clipboardData;
is(dt.types.length, 2, "Correct number of types");
// TODO: Remove application/x-moz-file from content.
is(dt.types[0], "application/x-moz-file", "First type")
is(dt.types[1], "Files", "Last type must be Files");
is(dt.getData("text/plain"), "", "text/plain found with getData");
is(dt.getData("application/x-moz-file"), "", "application/x-moz-file found with getData");
is(dt.files.length, 1, "Correct number of files");
is(dt.files[0].name, "test-file.txt", "Correct file name");
is(dt.files[0].type, "text/plain", "Correct file type");
is(dt.items.length, 1, "Correct number of items");
is(dt.items[0].kind, "file", "Correct item kind");
is(dt.items[0].type, "text/plain", "Correct item type");
let file = dt.files[0];
is(await file.text(), "Hello World!", "Pasted file contains right text");
return file.name;
}
</script>
<input id="input" onpaste="checkPaste(event)">
<div id="output"></div>
</body></html>

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

@ -621,7 +621,9 @@ NSDictionary* nsClipboard::PasteboardDictFromTransferable(nsITransferable* aTran
if (!url || ![url absoluteString]) {
continue;
}
[pasteboardOutputDict setObject:[url absoluteString] forKey:fileUTType];
[pasteboardOutputDict setObject:[[NSString stringWithString:[url absoluteString]]
dataUsingEncoding:NSUTF8StringEncoding]
forKey:fileUTType];
} else if (flavorStr.EqualsLiteral(kFilePromiseMime)) {
NSString* urlPromise =
[UTIHelper stringFromPboardType:(NSString*)kPasteboardTypeFileURLPromise];

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

@ -300,6 +300,13 @@ nsClipboard::SetData(nsITransferable* aTransferable, nsIClipboardOwner* aOwner,
continue;
}
if (flavorStr.EqualsLiteral(kFileMime)) {
LOGCLIP(" adding text/uri-list target\n");
GdkAtom atom = gdk_atom_intern(kURIListMime, FALSE);
gtk_target_list_add(list, atom, 0, 0);
continue;
}
// Add this to our list of valid targets
LOGCLIP(" adding OTHER target %s\n", flavorStr.get());
GdkAtom atom = gdk_atom_intern(flavorStr.get(), FALSE);
@ -1210,13 +1217,47 @@ void nsClipboard::SelectionGetEvent(GtkClipboard* aClipboard,
html.AppendLiteral(kHTMLMarkupPrefix);
AppendUTF16toUTF8(ucs2string, html);
LOGCLIP(" Setting %zd bytest of %s data\n", html.Length(),
LOGCLIP(" Setting %zd bytes of %s data\n", html.Length(),
GUniquePtr<gchar>(gdk_atom_name(selectionTarget)).get());
gtk_selection_data_set(aSelectionData, selectionTarget, 8,
(const guchar*)html.get(), html.Length());
return;
}
// We put kFileMime onto the clipboard as kURIListMime.
if (selectionTarget == gdk_atom_intern(kURIListMime, FALSE)) {
LOGCLIP(" providing %s data\n", kURIListMime);
rv = trans->GetTransferData(kFileMime, getter_AddRefs(item));
if (NS_FAILED(rv) || !item) {
LOGCLIP(" failed to get %s data by GetTransferData()!\n", kFileMime);
return;
}
nsCOMPtr<nsIFile> file = do_QueryInterface(item);
if (!file) {
LOGCLIP(" failed to get nsIFile interface!");
return;
}
nsCOMPtr<nsIURI> fileURI;
rv = NS_NewFileURI(getter_AddRefs(fileURI), file);
if (NS_FAILED(rv)) {
LOGCLIP(" failed to get fileURI\n");
return;
}
nsAutoCString uri;
if (NS_FAILED(fileURI->GetSpec(uri))) {
LOGCLIP(" failed to get fileURI spec\n");
return;
}
LOGCLIP(" Setting %zd bytes of data\n", uri.Length());
gtk_selection_data_set(aSelectionData, selectionTarget, 8,
(const guchar*)uri.get(), uri.Length());
return;
}
LOGCLIP(" Try if we have anything at GetTransferData() for %s\n",
GUniquePtr<gchar>(gdk_atom_name(selectionTarget)).get());