tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit f244579d668e32b2c76ad6fb880ec50aabfc253b
parent 942ef4cb8ece456c791c9757b5d762ed6495bced
Author: Edgar Chen <echen@mozilla.com>
Date:   Wed,  7 Jan 2026 13:21:31 +0000

Bug 1953174 - Part 2: Stop always including common ancestor when serializing HTML data for clipboard copy; r=masayuki DONTBUILD

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

Diffstat:
Mdom/base/test/test_bug166235.html | 15+++++++++------
Mdom/base/test/test_bug333064.html | 6+++++-
Mdom/base/test/test_bug574596.html | 11+++++++++--
Mdom/base/test/test_pasting_svg_image.html | 9+++++++--
Mdom/events/test/test_dragstart.html | 8+++++++-
Mdom/serializers/nsDocumentEncoder.cpp | 3++-
Mdom/serializers/tests/mochitest/copypaste.js | 49++++++++++++++++++++++++++++++++++++++++---------
Mdom/serializers/tests/mochitest/test_bug116083.html | 6+++++-
Mdom/serializers/tests/mochitest/test_htmlcopyencoder.html | 23++++++++++++++++++-----
Mdom/serializers/tests/mochitest/test_htmlcopyencoder_common_ancestor.html | 25++++++++++++++++++++-----
Mdom/serializers/tests/mochitest/test_htmlcopyencoder_list.html | 56+++++++++++++++++++++++++++++++++++++++++++-------------
Mdom/serializers/tests/mochitest/test_htmlcopyencoder_table.html | 12++++++++++--
Meditor/libeditor/tests/test_paste_no_formatting.html | 7++++++-
Mmodules/libpref/init/StaticPrefList.yaml | 6++++++
Dtesting/web-platform/meta/editing/whitespaces/chrome-compat/insert-or-paste-image.tentative.html.ini | 41-----------------------------------------
15 files changed, 187 insertions(+), 90 deletions(-)

diff --git a/dom/base/test/test_bug166235.html b/dom/base/test/test_bug166235.html @@ -101,13 +101,16 @@ var originalStrings = [ ]; // expected results for clipboard text/html +const includeCommonAncestor = SpecialPowers.getBoolPref( + "dom.serializer.includeCommonAncestor.enabled" +); var clipboardHTML = [ - '<p id=\"test0\">This text should be copied.</p>', - '<p id=\"test1\">This text should be copied.</p>', - '<p id=\"test2\">This<span style=\"user-select: text\"> text should</span> be copied.</p>', - '<p id=\"test3\">This text should be copied.</p>', - '<p id=\"test4\">This<span style=\"user-select: text\"> text should</span> be copied.</p>', - '<p id=\"test5\">This<span style=\"user-select: all\"> text should</span> be copied.</p>', + `${includeCommonAncestor ? "<p id=\"test0\">" : ""}This text should be copied.${includeCommonAncestor ? "</p>" : ""}`, + `${includeCommonAncestor ? "<p id=\"test1\">" : ""}This text should be copied.${includeCommonAncestor ? "</p>" : ""}`, + `${includeCommonAncestor ? "<p id=\"test2\">" : ""}This<span style=\"user-select: text\"> text should</span> be copied.${includeCommonAncestor ? "</p>" : ""}`, + `${includeCommonAncestor ? "<p id=\"test3\">" : ""}This text should be copied.${includeCommonAncestor ? "</p>" : ""}`, + `${includeCommonAncestor ? "<p id=\"test4\">" : ""}This<span style=\"user-select: text\"> text should</span> be copied.${includeCommonAncestor ? "</p>" : ""}`, + `${includeCommonAncestor ? "<p id=\"test5\">" : ""}This<span style=\"user-select: all\"> text should</span> be copied.${includeCommonAncestor ? "</p>" : ""}`, ]; // expected results for clipboard text/plain diff --git a/dom/base/test/test_bug333064.html b/dom/base/test/test_bug333064.html @@ -38,7 +38,11 @@ SimpleTest.waitForFocus(function() { function compare(value) { // Make sure we got the HTML flavour we asked for and that our // string is included without additional spaces. - return value.includes("korean-text") && value.includes("안".repeat(160)); + return ( + (SpecialPowers.getBoolPref("dom.serializer.includeCommonAncestor.enabled") + ? value.includes("korean-text") + : !value.includes("korean-text")) && value.includes("안".repeat(160)) + ); }, function setup() { synthesizeKey("C", {accelKey: true}); diff --git a/dom/base/test/test_bug574596.html b/dom/base/test/test_bug574596.html @@ -29,6 +29,9 @@ function ignoreFunc(actualData, expectedData) { return true; } +const includeCommonAncestor = SpecialPowers.getBoolPref( + "dom.serializer.includeCommonAncestor.enabled" +); var dragLinkText = [[ { type:"text/x-moz-url", data:"", eqTest:ignoreFunc }, { type:"text/x-moz-url-data", data:"http://www.mozilla.org/" }, @@ -36,7 +39,9 @@ var dragLinkText = [[ { type:"text/uri-list", data:"http://www.mozilla.org/" }, { type:"text/_moz_htmlcontext", data:"", eqTest:ignoreFunc }, { type:"text/_moz_htmlinfo", data:"", eqTest:ignoreFunc }, - { type:"text/html", data:'<div id="link1"><a href="http://www.mozilla.org/">link1</a></div>' }, + { type:"text/html", data:`${includeCommonAncestor ? '<div id="link1">' : ''}` + + `<a href="http://www.mozilla.org/">link1</a>` + + `${includeCommonAncestor ? '</div>' : ''}` }, { type:"text/plain", data:"http://www.mozilla.org/" } ]]; @@ -71,7 +76,9 @@ async function runTest() { // if (result) dumpTransfer(result,dragLinkText); dragLinkText[0][2].data = "link2"; - dragLinkText[0][6].data = '<div id="link2"><a href="http://www.mozilla.org/">link2</a></div>' + dragLinkText[0][6].data = `${includeCommonAncestor ? '<div id="link2">' : ''}` + + `<a href="http://www.mozilla.org/">link2</a>` + + `${includeCommonAncestor ? '</div>' : ''}`; var result = await synthesizePlainDragAndCancel( { srcElement: $('link2').firstChild, diff --git a/dom/base/test/test_pasting_svg_image.html b/dom/base/test/test_pasting_svg_image.html @@ -9,6 +9,9 @@ <script> const kPasteTargetId = "pasteTarget"; const kTestContentId = "testContent"; + const kIncludeCommonAncestor = SpecialPowers.getBoolPref( + "dom.serializer.includeCommonAncestor.enabled" + ); function selectSVG() { const testContent = document.getElementById(kTestContentId); @@ -19,7 +22,8 @@ function validatorFn(aData) { const testContent = document.getElementById(kTestContentId); - let expectedData = testContent.outerHTML; + let expectedData = kIncludeCommonAncestor ? testContent.outerHTML + : testContent.innerHTML; if (navigator.platform.includes(kPlatformWindows)) { expectedData = kTextHtmlPrefixClipboardDataWindows + expectedData + kTextHtmlSuffixClipboardDataWindows; @@ -58,7 +62,8 @@ // ids. const expectedPastedInnerHTML = SimpleTest.stripLinebreaksAndWhitespaceAfterTags( - document.getElementById(kTestContentId).outerHTML); + kIncludeCommonAncestor ? document.getElementById(kTestContentId).outerHTML + : document.getElementById(kTestContentId).innerHTML); const pasteTargetElement = document.getElementById(kPasteTargetId); await pasteTo(pasteTargetElement); diff --git a/dom/events/test/test_dragstart.html b/dom/events/test/test_dragstart.html @@ -159,8 +159,14 @@ function doDragStartSelection(event) ok(Array.isArray(types), "initial types is an Array"); checkTypes(dt, ["text/_moz_htmlcontext", "text/_moz_htmlinfo", "text/html", "text/plain"], 0, "initial selection"); + const includeCommonAncestor = SpecialPowers.getBoolPref( + "dom.serializer.includeCommonAncestor.enabled" + ); is(dt.getData("text/plain"), "This is a draggable bit of text.", "initial selection text/plain"); - is(dt.getData("text/html"), "<div id=\"draggable\" ondragstart=\"doDragStartSelection(event)\">This is a <em>draggable</em> bit of text.</div>", + is(dt.getData("text/html"), + `${includeCommonAncestor ? "<div id=\"draggable\" ondragstart=\"doDragStartSelection(event)\">" : ""}` + + `This is a <em>draggable</em> bit of text.` + + `${includeCommonAncestor ? "</div>" : ""}`, "initial selection text/html"); // text/plain and Text are available for compatibility. They retrieve the diff --git a/dom/serializers/nsDocumentEncoder.cpp b/dom/serializers/nsDocumentEncoder.cpp @@ -1895,7 +1895,8 @@ nsresult nsHTMLCopyEncoder::PromoteRange(nsRange* inRange) { // if both range endpoints are at the common ancestor, check for possible // inclusion of ancestors - if (opStartNode == common && opEndNode == common) { + if (StaticPrefs::dom_serializer_includeCommonAncestor_enabled() && + opStartNode == common && opEndNode == common) { rv = PromoteAncestorChain(address_of(opStartNode), &opStartOffset, &opEndOffset); NS_ENSURE_SUCCESS(rv, rv); diff --git a/dom/serializers/tests/mochitest/copypaste.js b/dom/serializers/tests/mochitest/copypaste.js @@ -158,12 +158,18 @@ async function testCopyPaste(isXHTML) { is(value, expected, id + ".innerHTML"); } + const includeCommonAncestor = SpecialPowers.getBoolPref( + "dom.serializer.includeCommonAncestor.enabled" + ); + await copyChildrenToClipboard("draggable"); testSelectionToString("This is a draggable bit of text."); testClipboardValue("text/plain", "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>' + `${includeCommonAncestor ? '<div id="draggable" title="title to have a long HTML line">' : ""}` + + `This is a <em>draggable</em> bit of text.` + + `${includeCommonAncestor ? "</div>" : ""}` ); testPasteText("This is a draggable bit of text."); @@ -172,7 +178,9 @@ async function testCopyPaste(isXHTML) { testClipboardValue("text/plain", " 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>' + `${includeCommonAncestor ? '<div id="alist">' : ""}` + + `\n bla\n <ul>\n <li>foo</li>\n \n <li>bar</li>\n </ul>\n ` + + `${includeCommonAncestor ? "</div>" : ""}` ); testPasteText(" bla\n\n foo\n bar\n\n"); @@ -181,7 +189,9 @@ async function testCopyPaste(isXHTML) { testClipboardValue("text/plain", " 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>' + `${includeCommonAncestor ? '<div id="blist">' : ""}` + + `\n mozilla\n <ol>\n <li>foo</li>\n \n <li>bar</li>\n </ol>\n ` + + `${includeCommonAncestor ? "</div>" : ""}` ); testPasteText(" mozilla\n\n foo\n bar\n\n"); @@ -193,7 +203,9 @@ async function testCopyPaste(isXHTML) { ); 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>' + `${includeCommonAncestor ? '<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 ` + + `${includeCommonAncestor ? "</div>" : ""}` ); testPasteText(" mzla\n\n foo\n bazzinga!\n bar\n\n"); @@ -212,7 +224,9 @@ async function testCopyPaste(isXHTML) { } else { testHtmlClipboardValue( "text/html", - '<div id="div4">\n T<textarea>t t t</textarea>\n</div>' + `${includeCommonAncestor ? '<div id="div4">' : ""}` + + `\n T<textarea>t t t</textarea>\n` + + `${includeCommonAncestor ? "</div>" : ""}` ); testInnerHTML("div4", "\n T<textarea>t t t</textarea>\n"); } @@ -233,7 +247,9 @@ async function testCopyPaste(isXHTML) { } else { testHtmlClipboardValue( "text/html", - '<div id="div5">\n T<textarea> </textarea>\n</div>' + `${includeCommonAncestor ? '<div id="div5">' : ""}` + + `\n T<textarea> </textarea>\n` + + `${includeCommonAncestor ? "</div>" : ""}` ); testInnerHTML("div5", "\n T<textarea> </textarea>\n"); } @@ -458,7 +474,12 @@ async function testCopyPaste(isXHTML) { await copyChildrenToClipboard("div13"); testSelectionToString("__"); testClipboardValue("text/plain", "__"); - testHtmlClipboardValue("text/html", '<div id="div13">__</div>'); + testHtmlClipboardValue( + "text/html", + `${includeCommonAncestor ? '<div id="div13">' : ""}` + + `__` + + `${includeCommonAncestor ? "</div>" : ""}` + ); testPasteText("__"); // ============ converting cell boundaries to tabs in tables @@ -498,7 +519,12 @@ async function testCopyPaste(isXHTML) { 2 ); testClipboardValue("text/plain", "Xdiv11"); - testHtmlClipboardValue("text/html", "<div><p>X<span>div</span>11</p></div>"); + testHtmlClipboardValue( + "text/html", + `${includeCommonAncestor ? "<div>" : ""}` + + `<p>X<span>div</span>11</p>` + + `${includeCommonAncestor ? "</div>" : ""}` + ); await new Promise(resolve => { setTimeout(resolve, 0); @@ -516,7 +542,12 @@ async function testCopyPaste(isXHTML) { ); testClipboardValue("text/plain", "Xdiv12"); - testHtmlClipboardValue("text/html", "<div><p>X<span>div</span>12</p></div>"); + testHtmlClipboardValue( + "text/html", + `${includeCommonAncestor ? "<div>" : ""}` + + `<p>X<span>div</span>12</p>` + + `${includeCommonAncestor ? "</div>" : ""}` + ); await new Promise(resolve => { setTimeout(resolve, 0); }); diff --git a/dom/serializers/tests/mochitest/test_bug116083.html b/dom/serializers/tests/mochitest/test_bug116083.html @@ -48,7 +48,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=116083 <div data-result="foo&#10;bar" contenteditable><div>foo<br>bar</div></div> <div data-result="foo&#10;bar&#10;"><div>foo<br>bar<br></div></div> <div data-result="foo&#10;bar" contenteditable><div>foo<br>bar<br></div></div> -<div data-result="&#10;foo bar&#10;">foo bar</div> +<div data-result="foo bar">foo bar</div> </div> <script type="application/javascript"> @@ -98,6 +98,10 @@ function nextTest() { var expected = div.hasAttribute("data-result") ? div.getAttribute("data-result") : div.textContent; + if (!div.nextElementSibling && + SpecialPowers.getBoolPref("dom.serializer.includeCommonAncestor.enabled")) { + expected = `\n${expected}\n`; + } SimpleTest.waitForClipboard(expected, function() { synthesizeKey("C", {accelKey: true}); diff --git a/dom/serializers/tests/mochitest/test_htmlcopyencoder.html b/dom/serializers/tests/mochitest/test_htmlcopyencoder.html @@ -22,6 +22,9 @@ function testHtmlCopyEncoder () { var node = document.getElementById('draggable'); + const includeCommonAncestor = SpecialPowers.getBoolPref( + "dom.serializer.includeCommonAncestor.enabled" + ); // in the following tests, we must use the OutputLFLineBreak flag, to avoid // to have the default line break of the platform in the result, so the test @@ -46,21 +49,27 @@ function testHtmlCopyEncoder () { encoder.init(document, "text/html", de.OutputLFLineBreak | de.OutputSelectionOnly); encoder.setSelection(select); out = encoder.encodeToString(); - expected = "<div style=\"display: none\">\n\n<div id=\"draggable\" ondragstart=\"doDragStartSelection(event)\">This is a <em>draggable</em> bit of text.</div>\n\n</div>"; + expected = `${includeCommonAncestor ? "<div style=\"display: none\">\n\n<div id=\"draggable\" ondragstart=\"doDragStartSelection(event)\">" : ""}` + + `This is a <em>draggable</em> bit of text.` + + `${includeCommonAncestor ? "</div>\n\n</div>" : ""}`; is(out, expected, "test selection"); encoder.init(document, "text/html", de.OutputLFLineBreak | de.OutputAbsoluteLinks | de.OutputEncodeHTMLEntities | de.OutputSelectionOnly | de.OutputRaw); encoder.setSelection(select); var outContext = {value:''}, outInfo = {value:''}; out = encoder.encodeToStringWithContext(outContext, outInfo); - expected = "<div style=\"display: none\">\n\n<div id=\"draggable\" ondragstart=\"doDragStartSelection(event)\">This is a <em>draggable</em> bit of text.</div>\n\n</div>"; + expected = `${includeCommonAncestor ? "<div style=\"display: none\">\n\n<div id=\"draggable\" ondragstart=\"doDragStartSelection(event)\">" : ""}` + + `This is a <em>draggable</em> bit of text.` + + `${includeCommonAncestor ? "</div>\n\n</div>" : ""}`; is(out, expected, "test encodeToStringWithContext with selection "); node.nextSibling.data="\nfoo bar\n"; encoder.init(document, "text/html", de.OutputLFLineBreak | de.OutputSelectionOnly); encoder.setSelection(select); out = encoder.encodeToString(); - expected = "<div id=\"draggable\" ondragstart=\"doDragStartSelection(event)\">This is a <em>draggable</em> bit of text.</div>"; + expected = `${includeCommonAncestor ? "<div id=\"draggable\" ondragstart=\"doDragStartSelection(event)\">" : ""}` + + `This is a <em>draggable</em> bit of text.` + + `${includeCommonAncestor ? "</div>" : ""}`; is(out, expected, "test selection with additional data"); node = document.getElementById('aList'); @@ -71,7 +80,9 @@ function testHtmlCopyEncoder () { encoder.init(document, "text/html", de.OutputLFLineBreak | de.OutputSelectionOnly); encoder.setSelection(select); out = encoder.encodeToString(); - expected = '<ol id=\"aList\">\n <li>Lorem ipsum dolor</li>\n <li>sit amet, <strong>consectetuer</strong> </li>\n <li>adipiscing elit</li>\n <li>Nam eu sapien. Sed viverra lacus. Donec quis ipsum. Nunc cursus aliquet lectus. Nunc vitae eros. Class</li>\n <li>aptent taciti</li>\n</ol>'; + expected = `${includeCommonAncestor ? "<ol id=\"aList\">" : ""}` + + `\n <li>Lorem ipsum dolor</li>\n <li>sit amet, <strong>consectetuer</strong> </li>\n <li>adipiscing elit</li>\n <li>Nam eu sapien. Sed viverra lacus. Donec quis ipsum. Nunc cursus aliquet lectus. Nunc vitae eros. Class</li>\n <li>aptent taciti</li>\n` + + `${includeCommonAncestor ? "</ol>" : ""}`; is(out, expected, "test list selection"); encoder.init(document, "text/html", de.OutputLFLineBreak | de.OutputSelectionOnly); @@ -151,7 +162,9 @@ function testHtmlCopyEncoder () { encoder.init(document, "text/html", de.OutputLFLineBreak | de.OutputSelectionOnly); encoder.setSelection(select); out = encoder.encodeToString(); - expected = '<ol id=\"aList\">\n <li value=\"8\">Lorem ipsum dolor</li>\n <li>sit amet, <strong>consectetuer</strong> </li>\n <li>adipiscing elit</li>\n <li>Nam eu sapien. Sed viverra lacus. Donec quis ipsum. Nunc cursus aliquet lectus. Nunc vitae eros. Class</li>\n <li>aptent taciti</li>\n</ol>'; + expected = `${includeCommonAncestor ? "<ol id=\"aList\">" : ""}` + + `\n <li value=\"8\">Lorem ipsum dolor</li>\n <li>sit amet, <strong>consectetuer</strong> </li>\n <li>adipiscing elit</li>\n <li>Nam eu sapien. Sed viverra lacus. Donec quis ipsum. Nunc cursus aliquet lectus. Nunc vitae eros. Class</li>\n <li>aptent taciti</li>\n` + + `${includeCommonAncestor ? "</ol>" : ""}`; is(out, expected, "test list selection with a value on a LI"); //test Bug 436703 diff --git a/dom/serializers/tests/mochitest/test_htmlcopyencoder_common_ancestor.html b/dom/serializers/tests/mochitest/test_htmlcopyencoder_common_ancestor.html @@ -29,6 +29,10 @@ const ALWAYS_INCLUDE_ELEMENTS = [ "strong", "tt", "u", "var" ]; +const alwaysIncludeCommonAncestor = SpecialPowers.getBoolPref( + "dom.serializer.includeCommonAncestor.enabled" +); + const de = SpecialPowers.Ci.nsIDocumentEncoder; const encoder = SpecialPowers.Cu.createHTMLCopyEncoder(); function testSelectAndCopy(aNode, aInclude) { @@ -57,9 +61,10 @@ function testSelectAndCopy(aNode, aInclude) { let htmlContext = { value: '' }; let htmlInfo = { value: '' }; is(encoder.encodeToStringWithContext(htmlContext, htmlInfo), - `<div id="container">${innerHTML}</div>`, + alwaysIncludeCommonAncestor ? `<div id="container">${innerHTML}</div>` : innerHTML, `Check serialized output`); - is(htmlContext.value, `<html><body></body></html>`, + is(htmlContext.value, + alwaysIncludeCommonAncestor ? `<html><body></body></html>` : `<html><body><div id="container"></div></body></html>`, `Check serialized context`); is(htmlInfo.value, `0,0`, `Check serialized info`); }); @@ -78,12 +83,22 @@ function testSelectAndCopy(aNode, aInclude) { encoder.init(document, "text/html", 0); encoder.setSelection(selection); + let expectedResult; + if (alwaysIncludeCommonAncestor) { + expectedResult = `<div id="container">${innerHTML}</div>`; + } else if (aInclude) { + expectedResult = innerHTML; + } else { + expectedResult = target.textContent; + } + let htmlContext = { value: '' }; let htmlInfo = { value: '' }; - is(encoder.encodeToStringWithContext(htmlContext, htmlInfo), - `<div id="container">${innerHTML}</div>`, + is(encoder.encodeToStringWithContext(htmlContext, htmlInfo), expectedResult, `Check serialized output`); - is(htmlContext.value, `<html><body></body></html>`, + is(htmlContext.value, + alwaysIncludeCommonAncestor ? `<html><body></body></html>` + : `<html><body><div id="container"><${aNode} id="target"></${aNode}></div></body></html>`, `Check serialized context`); is(htmlInfo.value, `0,0`, `Check serialized info`); }); diff --git a/dom/serializers/tests/mochitest/test_htmlcopyencoder_list.html b/dom/serializers/tests/mochitest/test_htmlcopyencoder_list.html @@ -27,6 +27,10 @@ /* global describe, it, beforeEach */ +const alwaysIncludeCommonAncestor = SpecialPowers.getBoolPref( + "dom.serializer.includeCommonAncestor.enabled" +); + const TESTS = [ { selector: `ul li`, @@ -34,32 +38,58 @@ const TESTS = [ expectedContext: `<html><body><div id="container"><ul></ul></div></body></html>`, }, { selector: `ul li span`, - expectedResult: `<li><span>Item2</span></li>`, - expectedContext: `<html><body><div id="container"><ul></ul></div></body></html>`, + expectedResult: `${alwaysIncludeCommonAncestor ? `<li>` : ``}` + + `<span>Item2</span>` + + `${alwaysIncludeCommonAncestor ? `</li>` : ``}`, + expectedContext: `<html><body><div id="container"><ul>` + + `${alwaysIncludeCommonAncestor ? `` : `<li></li>`}` + + `</ul></div></body></html>`, }, { selector: `ul li div`, - expectedResult: `<li><div>Item3</div></li>`, - expectedContext: `<html><body><div id="container"><ul></ul></div></body></html>`, + expectedResult: `${alwaysIncludeCommonAncestor ? `<li>` : ``}` + + `<div>Item3</div>` + + `${alwaysIncludeCommonAncestor ? `</li>` : ``}`, + expectedContext: `<html><body><div id="container"><ul>` + + `${alwaysIncludeCommonAncestor ? `` : `<li></li>`}` + + `</ul></div></body></html>`, }, { selector: `ul ol li`, - expectedResult: `<li><ol><li>SubItem1</li></ol></li>`, - expectedContext: `<html><body><div id="container"><ul></ul></div></body></html>`, + expectedResult: `${alwaysIncludeCommonAncestor ? `<li><ol>` : ``}` + + `<li>SubItem1</li>` + + `${alwaysIncludeCommonAncestor ? `</ol></li>` : ``}`, + expectedContext: `<html><body><div id="container"><ul>` + + `${alwaysIncludeCommonAncestor ? `` : `<li><ol></ol></li>`}` + + `</ul></div></body></html>`, }, { selector: `ul ol li span`, - expectedResult: `<li><span>SubItem2</span></li>`, - expectedContext: `<html><body><div id="container"><ul><li><ol></ol></li></ul></div></body></html>`, + expectedResult: `${alwaysIncludeCommonAncestor ? `<li>` : ``}` + + `<span>SubItem2</span>` + + `${alwaysIncludeCommonAncestor ? `</li>` : ``}`, + expectedContext: `<html><body><div id="container"><ul><li><ol>` + + `${alwaysIncludeCommonAncestor ? `` : `<li></li>`}` + + `</ol></li></ul></div></body></html>`, }, { selector: `ul ol li div`, - expectedResult: `<li><div>SubItem3</div></li>`, - expectedContext: `<html><body><div id="container"><ul><li><ol></ol></li></ul></div></body></html>`, + expectedResult: `${alwaysIncludeCommonAncestor ? `<li>` : ``}` + + `<div>SubItem3</div>` + + `${alwaysIncludeCommonAncestor ? `</li>` : ``}`, + expectedContext: `<html><body><div id="container"><ul><li><ol>` + + `${alwaysIncludeCommonAncestor ? `` : `<li value="2"></li>`}` + + `</ol></li></ul></div></body></html>`, }, { selector: `ul span li`, expectedResult: `<span><li>ItemInSpan1</li></span>`, - expectedContext: `<html><body><div id="container"><ul></ul></div></body></html>`, + expectedContext: `<html><body><div id="container"><ul>` + + `${alwaysIncludeCommonAncestor ? `` : `<span></span>`}` + + `</ul></div></body></html>`, }, { selector: `ul div span`, - expectedResult: `<div><li><span>ItemInDiv1</span></li></div>`, - expectedContext: `<html><body><div id="container"><ul></ul></div></body></html>`, + expectedResult: `${alwaysIncludeCommonAncestor ? `<div><li>` : ``}` + + `<span>ItemInDiv1</span>` + + `${alwaysIncludeCommonAncestor ? `</li></div>` : ``}`, + expectedContext: `<html><body><div id="container"><ul>` + + `${alwaysIncludeCommonAncestor ? `` : `<div><li></li></div>`}` + + `</ul></div></body></html>`, }, ]; diff --git a/dom/serializers/tests/mochitest/test_htmlcopyencoder_table.html b/dom/serializers/tests/mochitest/test_htmlcopyencoder_table.html @@ -31,6 +31,10 @@ /* global describe, it, beforeEach */ +const alwaysIncludeCommonAncestor = SpecialPowers.getBoolPref( + "dom.serializer.includeCommonAncestor.enabled" +); + const TESTS = [ { selector: `caption`, @@ -38,8 +42,12 @@ const TESTS = [ expectedContext: `<html><body><div id="container"><table></table></div></body></html>`, }, { selector: `caption span`, - expectedResult: `<table><caption><span>Title1</span></caption></table>`, - expectedContext: `<html><body><div id="container"><table></table></div></body></html>`, + expectedResult: `${alwaysIncludeCommonAncestor ? `<table><caption>` : ``}` + + `<span>Title1</span>` + + `${alwaysIncludeCommonAncestor ? `</caption></table>` : ``}`, + expectedContext: `<html><body><div id="container"><table>` + + `${alwaysIncludeCommonAncestor ? `` : `<caption></caption>`}` + + `</table></div></body></html>`, }, { selector: `thead th`, expectedResult: `<table><thead><tr><th>First</th></tr></thead></table>`, diff --git a/editor/libeditor/tests/test_paste_no_formatting.html b/editor/libeditor/tests/test_paste_no_formatting.html @@ -17,8 +17,13 @@ <div id="source">Some <b>Bold</b> Text</div> <script> +const includeCommonAncestor = SpecialPowers.getBoolPref( + "dom.serializer.includeCommonAncestor.enabled" +); const expectedText = "Some Bold Text"; -const expectedHTML = "<div id=\"source\">Some <b>Bold</b> Text</div>"; +const expectedHTML = `${includeCommonAncestor ? "<div id=\"source\">" : ""}` + + `Some <b>Bold</b> Text` + + `${includeCommonAncestor ? "</div>" : ""}`; const htmlPrefix = navigator.platform.includes("Win") ? "<html><body>\n<!--StartFragment-->" diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml @@ -5974,6 +5974,12 @@ value: "" mirror: never +# Whether to include common ancestor when serializing HTML data for clipboard copy. +- name: dom.serializer.includeCommonAncestor.enabled + type: bool + value: false + mirror: always + #--------------------------------------------------------------------------- # Prefs starting with "editor" #--------------------------------------------------------------------------- diff --git a/testing/web-platform/meta/editing/whitespaces/chrome-compat/insert-or-paste-image.tentative.html.ini b/testing/web-platform/meta/editing/whitespaces/chrome-compat/insert-or-paste-image.tentative.html.ini @@ -1,41 +0,0 @@ -[insert-or-paste-image.tentative.html?paste-image] - [Pasting an <img> when "[\]a&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;b"] - expected: FAIL - - [Pasting an <img> when "a[\]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;b"] - expected: FAIL - - [Pasting an <img> when "a&nbsp;[\]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;b"] - expected: FAIL - - [Pasting an <img> when "a&nbsp;&nbsp;[\]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;b"] - expected: FAIL - - [Pasting an <img> when "a&nbsp;&nbsp;&nbsp;[\]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;b"] - expected: FAIL - - [Pasting an <img> when "a&nbsp;&nbsp;&nbsp;&nbsp;[\]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;b"] - expected: FAIL - - [Pasting an <img> when "a&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[\]&nbsp;&nbsp;&nbsp;&nbsp;b"] - expected: FAIL - - [Pasting an <img> when "a&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[\]&nbsp;&nbsp;&nbsp;b"] - expected: FAIL - - [Pasting an <img> when "a&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[\]&nbsp;&nbsp;b"] - expected: FAIL - - [Pasting an <img> when "a&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[\]&nbsp;b"] - expected: FAIL - - [Pasting an <img> when "a&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[\]b"] - expected: FAIL - - [Pasting an <img> when "a&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;b[\]"] - expected: FAIL - - -[insert-or-paste-image.tentative.html?execCommand-insertImage] - -[insert-or-paste-image.tentative.html?execCommand-insertHTML]