/* 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 = kTextHtmlPrefixClipboardDataWindows + expected.replace(/\n/g, "\n") + kTextHtmlSuffixClipboardDataWindows; } 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", '
This is a draggable bit of text.
' ); 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", '
\n bla\n \n
' ); 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", '
\n mozilla\n
    \n
  1. foo
  2. \n \n
  3. bar
  4. \n
\n
' ); 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", '
\n mzla\n \n
' ); 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", '
\n T\n
' ); testInnerHTML( "div4", '\n T\n' ); } else { testHtmlClipboardValue( "text/html", '
\n T\n
' ); testInnerHTML("div4", "\n T\n"); } testPasteText(" Tt t t "); await copyChildrenToClipboard("div5"); testSelectionToString(" T "); testClipboardValue("text/unicode", " T "); if (isXHTML) { testHtmlClipboardValue( "text/html", '
\n T\n
' ); testInnerHTML( "div5", '\n T\n' ); } else { testHtmlClipboardValue( "text/html", '
\n T\n
' ); testInnerHTML("div5", "\n T\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", '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", '
before
after
' ); // 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", '
before
after
' ); // 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", '
\n

' ); } // ============ 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", '
__
'); 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", '
12
34
' ); // ============ 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", '
12
56
' ); } // ============ manipulating Selection in oncopy await copyRangeToClipboard( $("div11").childNodes[0], 0, $("div11").childNodes[1], 2 ); testClipboardValue("text/unicode", "Xdiv11"); testHtmlClipboardValue("text/html", "

Xdiv11

"); 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", "

Xdiv12

"); await new Promise(resolve => { setTimeout(resolve, 0); }); testSelectionToString("div12"); await new Promise(resolve => { setTimeout(resolve, 0); }); if (!isXHTML) { // ============ copy from ruby const ruby1 = $("ruby1"); const ruby1Container = ruby1.parentNode; // Ruby annotation is included when selecting inside ruby. await copyRangeToClipboard(ruby1, 0, ruby1, 6); testClipboardValue("text/unicode", "aabb(AABB)"); // Ruby annotation is ignored when selecting across ruby. await copyRangeToClipboard(ruby1Container, 0, ruby1Container, 3); testClipboardValue("text/unicode", "XaabbY"); // ... unless converter.html2txt.always_include_ruby is set await SpecialPowers.pushPrefEnv({ set: [["converter.html2txt.always_include_ruby", true]], }); await copyRangeToClipboard(ruby1Container, 0, ruby1Container, 3); testClipboardValue("text/unicode", "Xaabb(AABB)Y"); await SpecialPowers.popPrefEnv(); } }