fune/dom/base/test/copypaste.js
Masayuki Nakano ca4a57799a Bug 1599944 - Make SimpleTest.waitForClipboard() and SimpleTest.promiseClipboardChange() relax to compare clipboard data with expected string r=smaug
Most tests which use these APIs don't want to distinguish difference of
linebreaks (i.e., CRLF vs. CR vs. LF).  And also most tests don't want to
check prefix and postfix of HTML data in clipboard on Windows.

Therefore, this patch makes them compare clipboard data with expected string
smarter.  Every linebreak in clipboard data are treated as LF.  Expected
HTML data is wrapped with `<html>`, `<body>` and comment nodes only on
Windows at comparing with clipboard data.

Differential Revision: https://phabricator.services.mozilla.com/D57963

--HG--
extra : moz-landing-system : lando
2019-12-27 14:02:35 +00:00

533 lines
16 KiB
JavaScript

/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
function modifySelection(s) {
var g = window.getSelection();
var l = g.getRangeAt(0);
var d = document.createElement("p");
d.innerHTML = s;
d.appendChild(l.cloneContents());
var e = document.createElement("div");
document.body.appendChild(e);
e.appendChild(d);
var a = document.createRange();
a.selectNode(d);
g.removeAllRanges();
g.addRange(a);
window.setTimeout(function() {
e.remove();
g.removeAllRanges();
g.addRange(l);
}, 0);
}
function getLoadContext() {
var Ci = SpecialPowers.Ci;
return SpecialPowers.wrap(window).docShell.QueryInterface(Ci.nsILoadContext);
}
async function testCopyPaste(isXHTML) {
var suppressUnicodeCheckIfHidden = !!isXHTML;
var suppressHTMLCheck = !!isXHTML;
var docShell = SpecialPowers.wrap(window).docShell;
var documentViewer = docShell.contentViewer.QueryInterface(
SpecialPowers.Ci.nsIContentViewerEdit
);
var clipboard = SpecialPowers.Services.clipboard;
var textarea = SpecialPowers.wrap(document.getElementById("input"));
async function copySelectionToClipboard(suppressUnicodeCheck) {
await SimpleTest.promiseClipboardChange(
() => true,
() => {
documentViewer.copySelection();
}
);
if (!suppressUnicodeCheck) {
ok(
clipboard.hasDataMatchingFlavors(["text/unicode"], 1),
"check text/unicode"
);
}
if (!suppressHTMLCheck) {
ok(clipboard.hasDataMatchingFlavors(["text/html"], 1), "check text/html");
}
}
function clear(node, suppressUnicodeCheck) {
textarea.blur();
var sel = window.getSelection();
sel.removeAllRanges();
}
async function copyToClipboard(node, suppressUnicodeCheck) {
clear();
var r = document.createRange();
r.selectNode(node);
window.getSelection().addRange(r);
await copySelectionToClipboard(suppressUnicodeCheck);
}
function addRange(startNode, startIndex, endNode, endIndex) {
var sel = window.getSelection();
var r = document.createRange();
r.setStart(startNode, startIndex);
r.setEnd(endNode, endIndex);
sel.addRange(r);
}
async function copyRangeToClipboard(
startNode,
startIndex,
endNode,
endIndex,
suppressUnicodeCheck
) {
clear();
addRange(startNode, startIndex, endNode, endIndex);
await copySelectionToClipboard(suppressUnicodeCheck);
}
async function copyChildrenToClipboard(id) {
clear();
window.getSelection().selectAllChildren(document.getElementById(id));
await copySelectionToClipboard();
}
function getClipboardData(mime) {
var transferable = SpecialPowers.Cc[
"@mozilla.org/widget/transferable;1"
].createInstance(SpecialPowers.Ci.nsITransferable);
transferable.init(getLoadContext());
transferable.addDataFlavor(mime);
clipboard.getData(transferable, 1);
var data = SpecialPowers.createBlankObject();
transferable.getTransferData(mime, data);
return data;
}
function testHtmlClipboardValue(mime, expected) {
// For Windows, navigator.platform returns "Win32".
var expectedValue = expected;
if (navigator.platform.includes("Win")) {
// Windows has extra content.
var expectedValue =
"<html><body>\n<!--StartFragment-->" +
expected.replace(/\n/g, "\n") +
"<!--EndFragment-->\n</body>\n</html>";
}
testClipboardValue(mime, expectedValue);
}
function testClipboardValue(mime, expected) {
if (suppressHTMLCheck && mime == "text/html") {
return null;
}
var data = SpecialPowers.wrap(getClipboardData(mime));
is(
data.value == null
? data.value
: data.value.QueryInterface(SpecialPowers.Ci.nsISupportsString).data,
expected,
mime + " value in the clipboard"
);
return data.value;
}
function testPasteText(expected) {
textarea.value = "";
textarea.focus();
textarea.editor.paste(1);
is(textarea.value, expected, "value of the textarea after the paste");
}
function testPasteHTML(id, expected) {
var contentEditable = $(id);
contentEditable.focus();
synthesizeKey("v", { accelKey: true });
is(contentEditable.innerHTML, expected, id + ".innerHtml after the paste");
}
function testSelectionToString(expected) {
is(
window
.getSelection()
.toString()
.replace(/\r\n/g, "\n"),
expected,
"Selection.toString"
);
}
function testInnerHTML(id, expected) {
var value = document.getElementById(id).innerHTML;
is(value, expected, id + ".innerHTML");
}
await copyChildrenToClipboard("draggable");
testSelectionToString("This is a draggable bit of text.");
testClipboardValue("text/unicode", "This is a draggable bit of text.");
testHtmlClipboardValue(
"text/html",
'<div id="draggable" title="title to have a long HTML line">This is a <em>draggable</em> bit of text.</div>'
);
testPasteText("This is a draggable bit of text.");
await copyChildrenToClipboard("alist");
testSelectionToString(" bla\n\n foo\n bar\n\n");
testClipboardValue("text/unicode", " bla\n\n foo\n bar\n\n");
testHtmlClipboardValue(
"text/html",
'<div id="alist">\n bla\n <ul>\n <li>foo</li>\n \n <li>bar</li>\n </ul>\n </div>'
);
testPasteText(" bla\n\n foo\n bar\n\n");
await copyChildrenToClipboard("blist");
testSelectionToString(" mozilla\n\n foo\n bar\n\n");
testClipboardValue("text/unicode", " mozilla\n\n foo\n bar\n\n");
testHtmlClipboardValue(
"text/html",
'<div id="blist">\n mozilla\n <ol>\n <li>foo</li>\n \n <li>bar</li>\n </ol>\n </div>'
);
testPasteText(" mozilla\n\n foo\n bar\n\n");
await copyChildrenToClipboard("clist");
testSelectionToString(" mzla\n\n foo\n bazzinga!\n bar\n\n");
testClipboardValue(
"text/unicode",
" mzla\n\n foo\n bazzinga!\n bar\n\n"
);
testHtmlClipboardValue(
"text/html",
'<div id="clist">\n mzla\n <ul>\n <li>foo<ul>\n <li>bazzinga!</li>\n </ul></li>\n \n <li>bar</li>\n </ul>\n </div>'
);
testPasteText(" mzla\n\n foo\n bazzinga!\n bar\n\n");
await copyChildrenToClipboard("div4");
testSelectionToString(" Tt t t ");
testClipboardValue("text/unicode", " Tt t t ");
if (isXHTML) {
testHtmlClipboardValue(
"text/html",
'<div id="div4">\n T<textarea xmlns="http://www.w3.org/1999/xhtml">t t t</textarea>\n</div>'
);
testInnerHTML(
"div4",
'\n T<textarea xmlns="http://www.w3.org/1999/xhtml">t t t</textarea>\n'
);
} else {
testHtmlClipboardValue(
"text/html",
'<div id="div4">\n T<textarea>t t t</textarea>\n</div>'
);
testInnerHTML("div4", "\n T<textarea>t t t</textarea>\n");
}
testPasteText(" Tt t t ");
await copyChildrenToClipboard("div5");
testSelectionToString(" T ");
testClipboardValue("text/unicode", " T ");
if (isXHTML) {
testHtmlClipboardValue(
"text/html",
'<div id="div5">\n T<textarea xmlns="http://www.w3.org/1999/xhtml"> </textarea>\n</div>'
);
testInnerHTML(
"div5",
'\n T<textarea xmlns="http://www.w3.org/1999/xhtml"> </textarea>\n'
);
} else {
testHtmlClipboardValue(
"text/html",
'<div id="div5">\n T<textarea> </textarea>\n</div>'
);
testInnerHTML("div5", "\n T<textarea> </textarea>\n");
}
testPasteText(" T ");
await copyRangeToClipboard(
$("div6").childNodes[0],
0,
$("div6").childNodes[1],
1,
suppressUnicodeCheckIfHidden
);
testSelectionToString("");
// START Disabled due to bug 564688
if (false) {
testClipboardValue("text/unicode", "");
testClipboardValue("text/html", "");
}
// END Disabled due to bug 564688
testInnerHTML("div6", "div6");
await copyRangeToClipboard(
$("div7").childNodes[0],
0,
$("div7").childNodes[0],
4,
suppressUnicodeCheckIfHidden
);
testSelectionToString("");
// START Disabled due to bug 564688
if (false) {
testClipboardValue("text/unicode", "");
testClipboardValue("text/html", "");
}
// END Disabled due to bug 564688
testInnerHTML("div7", "div7");
await copyRangeToClipboard(
$("div8").childNodes[0],
0,
$("div8").childNodes[0],
4,
suppressUnicodeCheckIfHidden
);
testSelectionToString("");
// START Disabled due to bug 564688
if (false) {
testClipboardValue("text/unicode", "");
testClipboardValue("text/html", "");
}
// END Disabled due to bug 564688
testInnerHTML("div8", "div8");
await copyRangeToClipboard(
$("div9").childNodes[0],
0,
$("div9").childNodes[0],
4,
suppressUnicodeCheckIfHidden
);
testSelectionToString("div9");
testClipboardValue("text/unicode", "div9");
testHtmlClipboardValue("text/html", "div9");
testInnerHTML("div9", "div9");
await copyToClipboard($("div10"), suppressUnicodeCheckIfHidden);
testSelectionToString("");
testInnerHTML("div10", "div10");
await copyToClipboard($("div10").firstChild, suppressUnicodeCheckIfHidden);
testSelectionToString("");
await copyRangeToClipboard(
$("div10").childNodes[0],
0,
$("div10").childNodes[0],
1,
suppressUnicodeCheckIfHidden
);
testSelectionToString("");
await copyRangeToClipboard(
$("div10").childNodes[1],
0,
$("div10").childNodes[1],
1,
suppressUnicodeCheckIfHidden
);
testSelectionToString("");
if (!isXHTML) {
// ============ copy/paste multi-range selection (bug 1123505)
// with text start node
var sel = window.getSelection();
sel.removeAllRanges();
var r = document.createRange();
var ul = $("ul1");
var parent = ul.parentNode;
r.setStart(parent, 0);
r.setEnd(parent.firstChild, 15); // the end of "Copy..."
sel.addRange(r);
r = document.createRange();
r.setStart(ul, 1); // before the space inside the UL
r.setEnd(parent, 2); // after the UL
sel.addRange(r);
await copySelectionToClipboard(true);
testPasteHTML("contentEditable1", "Copy1then Paste");
// with text end node
var sel = window.getSelection();
sel.removeAllRanges();
var r = document.createRange();
var ul = $("ul2");
var parent = ul.parentNode;
r.setStart(parent, 0);
r.setEnd(ul, 1); // after the space
sel.addRange(r);
r = document.createRange();
r.setStart(parent.childNodes[1], 0); // the start of "Copy..."
r.setEnd(parent, 2);
sel.addRange(r);
await copySelectionToClipboard(true);
testPasteHTML("contentEditable2", "Copy2then Paste");
// with text end node and non-empty start
var sel = window.getSelection();
sel.removeAllRanges();
var r = document.createRange();
var ul = $("ul3");
var parent = ul.parentNode;
r.setStart(parent, 0);
r.setEnd(ul, 1); // after the space
sel.addRange(r);
r = document.createRange();
r.setStart(parent.childNodes[1], 0); // the start of "Copy..."
r.setEnd(parent, 2);
sel.addRange(r);
await copySelectionToClipboard(true);
testPasteHTML(
"contentEditable3",
'<ul id="ul3"><li>\n<br></li></ul>Copy3then Paste'
);
// with elements of different depth
var sel = window.getSelection();
sel.removeAllRanges();
var r = document.createRange();
var div1 = $("div1s");
var parent = div1.parentNode;
r.setStart(parent, 0);
r.setEnd(document.getElementById("div1se1"), 1); // after the "inner" DIV
sel.addRange(r);
r = document.createRange();
r.setStart(div1.childNodes[1], 0); // the start of "after"
r.setEnd(parent, 1);
sel.addRange(r);
await copySelectionToClipboard(true);
testPasteHTML(
"contentEditable4",
'<div id="div1s"><div id="div1se1">before</div></div><div id="div1s">after</div>'
);
// with elements of different depth, and a text node at the end
var sel = window.getSelection();
sel.removeAllRanges();
var r = document.createRange();
var div1 = $("div2s");
var parent = div1.parentNode;
r.setStart(parent, 0);
r.setEnd(document.getElementById("div2se1"), 1); // after the "inner" DIV
sel.addRange(r);
r = document.createRange();
r.setStart(div1.childNodes[1], 0); // the start of "after"
r.setEnd(parent, 1);
sel.addRange(r);
await copySelectionToClipboard(true);
testPasteHTML(
"contentEditable5",
'<div id="div2s"><div id="div2se1">before</div></div><div id="div2s">after</div>'
);
// crash test for bug 1127835
var e1 = document.getElementById("1127835crash1");
var e2 = document.getElementById("1127835crash2");
var e3 = document.getElementById("1127835crash3");
var t1 = e1.childNodes[0];
var t3 = e3.childNodes[0];
var sel = window.getSelection();
sel.removeAllRanges();
var r = document.createRange();
r.setStart(t1, 1);
r.setEnd(e2, 0);
sel.addRange(r);
r = document.createRange();
r.setStart(e2, 1);
r.setEnd(t3, 0);
sel.addRange(r);
await copySelectionToClipboard(true);
testPasteHTML(
"contentEditable6",
'<span id="1127835crash1"></span><div id="1127835crash2"><div>\n</div></div><br>'
);
}
// ============ copy/paste test from/to a textarea
var val = "1\n 2\n 3";
textarea.value = val;
textarea.select();
await SimpleTest.promiseClipboardChange(textarea.value, () => {
textarea.editor.copy();
});
textarea.value = "";
textarea.editor.paste(1);
is(textarea.value, val);
textarea.value = "";
// ============ NOSCRIPT should not be copied
await copyChildrenToClipboard("div13");
testSelectionToString("__");
testClipboardValue("text/unicode", "__");
testHtmlClipboardValue("text/html", '<div id="div13">__</div>');
testPasteText("__");
// ============ converting cell boundaries to tabs in tables
await copyToClipboard($("tr1"));
testClipboardValue("text/unicode", "foo\tbar");
if (!isXHTML) {
// ============ spanning multiple rows
await copyRangeToClipboard($("tr2"), 0, $("tr3"), 0);
testClipboardValue("text/unicode", "1\t2\n3\t4\n");
testHtmlClipboardValue(
"text/html",
'<table><tbody><tr id="tr2"><tr id="tr2"><td>1</td><td>2</td></tr><tr><td>3</td><td>4</td></tr><tr id="tr3"></tr></tr></tbody></table>'
);
// ============ spanning multiple rows in multi-range selection
clear();
addRange($("tr2"), 0, $("tr2"), 2);
addRange($("tr3"), 0, $("tr3"), 2);
await copySelectionToClipboard();
testClipboardValue("text/unicode", "1\t2\n5\t6");
testHtmlClipboardValue(
"text/html",
'<table><tbody><tr id="tr2"><td>1</td><td>2</td></tr><tr id="tr3"><td>5</td><td>6</td></tr></tbody></table>'
);
}
// ============ manipulating Selection in oncopy
await copyRangeToClipboard(
$("div11").childNodes[0],
0,
$("div11").childNodes[1],
2
);
testClipboardValue("text/unicode", "Xdiv11");
testHtmlClipboardValue("text/html", "<div><p>X<span>div</span>11</p></div>");
await new Promise(resolve => {
setTimeout(resolve, 0);
});
testSelectionToString("div11");
await new Promise(resolve => {
setTimeout(resolve, 0);
});
await copyRangeToClipboard(
$("div12").childNodes[0],
0,
$("div12").childNodes[1],
2
);
testClipboardValue("text/unicode", "Xdiv12");
testHtmlClipboardValue("text/html", "<div><p>X<span>div</span>12</p></div>");
await new Promise(resolve => {
setTimeout(resolve, 0);
});
testSelectionToString("div12");
await new Promise(resolve => {
setTimeout(resolve, 0);
});
}