commit 920ab7c1733347ebb766e3ab38653c03e5da7f85
parent e14e162ea0a9bc1d598f99f67d46238eada88de7
Author: Alexandru Marc <amarc@mozilla.com>
Date: Wed, 5 Nov 2025 03:07:21 +0200
Revert "Bug 1650720 - Part 4: Add line break tests for copying selection; r=masayuki" for causing mochitest failures @ test_aboutmemory3.xhtml
This reverts commit 62ce647fff1bead097d8f508383c4532974ec0ca.
Revert "Bug 1650720 - Part 3: Align link break coversion for clipboard copy with other browsers; r=masayuki"
This reverts commit 3ced2a72e8f763cfb4c49c9d4d5abac0037732c1.
Revert "Bug 1650720 - Part 2: Simplify nsPlainTextSerializer::ConvertToLinesAndOutput; r=masayuki"
This reverts commit 5e63b05f5423767984fbaed39ae09b5b5c01df62.
Revert "Bug 1650720 - Part 1: Add plaintext serializer tests for linebreak; r=masayuki"
This reverts commit 21953f6384e82c6ad50f6eafd9adeecb47ef65af.
Diffstat:
8 files changed, 36 insertions(+), 395 deletions(-)
diff --git a/dom/base/nsCopySupport.cpp b/dom/base/nsCopySupport.cpp
@@ -132,7 +132,6 @@ static nsresult EncodeForTextUnicode(nsIDocumentEncoder& aEncoder,
} else {
// Redo the encoding, but this time use pretty printing.
flags = nsIDocumentEncoder::OutputSelectionOnly |
- nsIDocumentEncoder::OutputForPlainTextClipboardCopy |
nsIDocumentEncoder::OutputAbsoluteLinks |
nsIDocumentEncoder::SkipInvisibleContent |
nsIDocumentEncoder::OutputDropInvisibleBreak |
diff --git a/dom/serializers/nsPlainTextSerializer.cpp b/dom/serializers/nsPlainTextSerializer.cpp
@@ -219,8 +219,8 @@ void nsPlainTextSerializer::OutputManager::Append(const nsAString& aString) {
}
}
-void nsPlainTextSerializer::OutputManager::AppendLineBreak(bool aForceCRLF) {
- mOutput.Append(aForceCRLF ? u"\r\n"_ns : mLineBreak);
+void nsPlainTextSerializer::OutputManager::AppendLineBreak() {
+ mOutput.Append(mLineBreak);
mAtFirstColumn = true;
}
@@ -482,13 +482,6 @@ nsPlainTextSerializer::AppendText(Text* aText, int32_t aStartOffset,
TextEditor::MaskString(textstr, *aText, 0, aStartOffset);
}
- if (mSettings.HasFlag(nsIDocumentEncoder::OutputForPlainTextClipboardCopy)) {
- // XXX it would be nice if we could just use the Write() to handle the line
- // breaks for all cases (bug 1993406).
- Write(textstr);
- return rv;
- }
-
// We have to split the string across newlines
// to match parser behavior
int32_t start = 0;
@@ -1503,55 +1496,60 @@ static void ReplaceVisiblyTrailingNbsps(nsAString& aString) {
}
void nsPlainTextSerializer::ConvertToLinesAndOutput(const nsAString& aString) {
- nsAString::const_iterator iter;
- aString.BeginReading(iter);
- nsAString::const_iterator done_searching;
- aString.EndReading(done_searching);
+ const int32_t totLen = aString.Length();
+ int32_t newline{0};
// Put the mail quote "> " chars in, if appropriate.
// Have to put it in before every line.
- while (iter != done_searching) {
- nsAString::const_iterator bol = iter;
- nsAString::const_iterator newline = done_searching;
+ int32_t bol = 0;
+ while (bol < totLen) {
+ bool outputLineBreak = false;
+ bool spacesOnly = true;
// Find one of '\n' or '\r' using iterators since nsAString
// doesn't have the old FindCharInSet function.
- bool spacesOnly = true;
+ nsAString::const_iterator iter;
+ aString.BeginReading(iter);
+ nsAString::const_iterator done_searching;
+ aString.EndReading(done_searching);
+ iter.advance(bol);
+ int32_t new_newline = bol;
+ newline = kNotFound;
while (iter != done_searching) {
if ('\n' == *iter || '\r' == *iter) {
- newline = iter;
+ newline = new_newline;
break;
}
if (' ' != *iter) {
spacesOnly = false;
}
+ ++new_newline;
++iter;
}
// Done searching
nsAutoString stringpart;
- bool outputLineBreak = false;
- bool isNewLineCRLF = false;
- if (newline == done_searching) {
+ if (newline == kNotFound) {
// No new lines.
- stringpart.Assign(Substring(bol, newline));
+ stringpart.Assign(Substring(aString, bol, totLen - bol));
if (!stringpart.IsEmpty()) {
char16_t lastchar = stringpart.Last();
mInWhitespace = IsLineFeedCarriageReturnBlankOrTab(lastchar);
}
mEmptyLines = -1;
+ bol = totLen;
} else {
// There is a newline
- stringpart.Assign(Substring(bol, newline));
+ stringpart.Assign(Substring(aString, bol, newline - bol));
mInWhitespace = true;
outputLineBreak = true;
mEmptyLines = 0;
- if ('\r' == *iter++ && '\n' == *iter) {
+ bol = newline + 1;
+ if ('\r' == *iter && bol < totLen && '\n' == *++iter) {
// There was a CRLF in the input. This used to be illegal and
// stripped by the parser. Apparently not anymore. Let's skip
// over the LF.
- newline = iter++;
- isNewLineCRLF = true;
+ bol++;
}
}
@@ -1570,19 +1568,15 @@ void nsPlainTextSerializer::ConvertToLinesAndOutput(const nsAString& aString) {
mOutputManager->Append(mCurrentLine,
OutputManager::StripTrailingWhitespaces::kNo);
if (outputLineBreak) {
- if (mSettings.HasFlag(
- nsIDocumentEncoder::OutputForPlainTextClipboardCopy)) {
- // This is aligned with other browsers that they don't convert CRLF to
- // the platform line break.
- ('\n' == *newline) ? mOutputManager->AppendLineBreak(isNewLineCRLF)
- : mOutputManager->Append(u"\r"_ns);
- } else {
- mOutputManager->AppendLineBreak();
- }
+ mOutputManager->AppendLineBreak();
}
mCurrentLine.ResetContentAndIndentationHeader();
}
+
+#ifdef DEBUG_wrapping
+ printf("No wrapping: newline is %d, totLen is %d\n", newline, totLen);
+#endif
}
/**
@@ -1681,14 +1675,10 @@ void nsPlainTextSerializer::Write(const nsAString& aStr) {
continue;
}
- if (nextpos == bol &&
- !mSettings.HasFlag(
- nsIDocumentEncoder::OutputForPlainTextClipboardCopy)) {
+ if (nextpos == bol) {
// Note that we are in whitespace.
mInWhitespace = true;
offsetIntoBuffer = str.get() + nextpos;
- // XXX Why do we need to keep the very first character when compressing
- // the reset?
AddToLine(offsetIntoBuffer, 1);
bol++;
continue;
diff --git a/dom/serializers/nsPlainTextSerializer.h b/dom/serializers/nsPlainTextSerializer.h
@@ -283,12 +283,7 @@ class nsPlainTextSerializer final : public nsIContentSerializer {
void Append(const CurrentLine& aCurrentLine,
StripTrailingWhitespaces aStripTrailingWhitespaces);
- /**
- * @param aString Last character is expected to not be a line break.
- */
- void Append(const nsAString& aString);
-
- void AppendLineBreak(bool aForceCRLF = false);
+ void AppendLineBreak();
/**
* This empties the current line cache without adding a NEWLINE.
@@ -305,6 +300,11 @@ class nsPlainTextSerializer final : public nsIContentSerializer {
uint32_t GetOutputLength() const;
private:
+ /**
+ * @param aString Last character is expected to not be a line break.
+ */
+ void Append(const nsAString& aString);
+
// As defined in nsIDocumentEncoder.idl.
const int32_t mFlags;
diff --git a/dom/serializers/tests/mochitest/mochitest.toml b/dom/serializers/tests/mochitest/mochitest.toml
@@ -123,5 +123,3 @@ skip-if = [
["test_htmlcopyencoder.html"]
["test_htmlcopyencoder.xhtml"]
-
-["test_plaintext_linebreak.html"]
diff --git a/dom/serializers/tests/mochitest/test_plaintext_linebreak.html b/dom/serializers/tests/mochitest/test_plaintext_linebreak.html
@@ -1,146 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-<title>Test line breaks for plaintext serializer</title>
-<script src="/tests/SimpleTest/SimpleTest.js"></script>
-<script src="/tests/SimpleTest/EventUtils.js"></script>
-<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-</head>
-<body>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1650720">Mozilla Bug 1650720</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-</div>
-<pre id="preformatted"></pre>
-<div id="container"></div>
-<script>
-
-const de = SpecialPowers.Ci.nsIDocumentEncoder;
-const platformLineBreak = navigator.platform.indexOf("Win") == 0 ? "\r\n" : "\n";
-const CASES = [
- "\r",
- "\n",
- "\r\r",
- "\r\n",
- "\n\r",
- "\n\n",
- "\r\r\r",
- "\r\r\n",
- "\r\n\r",
- "\r\n\n",
- "\n\r\r",
- "\n\r\n",
- "\n\n\r",
- "\n\n\n",
- "\r \n",
- "\n \r",
-];
-
-function convertLineBreaksForTestResult(aText) {
- return aText.replace(/\r?\n|\r(?!\n)/g, platformLineBreak);
-}
-
-function convertLineBreaksForClipboardTestResult(aText) {
- return aText.replace(/(?<!\r)\n/g, platformLineBreak);
-}
-
-function selectAndEncode(aElement, aEncoderFlags = 0) {
- // Select the element.
- const selection = window.getSelection();
- selection.removeAllRanges();
- selection.selectAllChildren(aElement);
-
- const encoder = SpecialPowers.Cu.createHTMLCopyEncoder();
- encoder.init(document, "text/plain", de.OutputSelectionOnly | aEncoderFlags);
- encoder.setSelection(selection);
- return encoder.encodeToString();
-}
-
-CASES.forEach((lineBreaks) => {
- const text = `${lineBreaks}First${lineBreaks}Second${lineBreaks}`;
-
- add_task(async function test_pre_text() {
- info(`Testing line breaks: ${JSON.stringify(lineBreaks)}`);
-
- const pre = document.getElementById("preformatted");
- pre.textContent = text;
-
- is(selectAndEncode(pre), text.replace(/\r|\n/g, platformLineBreak),
- `Encoded data for line breaks: ${JSON.stringify(lineBreaks)}`);
- is(selectAndEncode(pre, de.OutputForPlainTextClipboardCopy), convertLineBreaksForClipboardTestResult(text),
- `Encoded data for line breaks: ${JSON.stringify(lineBreaks)} (OutputForPlainTextClipboardCopy)`);
- });
-
- add_task(async function test_pre_img_alt() {
- info(`Testing line breaks: ${JSON.stringify(lineBreaks)}`);
-
- const pre = document.getElementById("preformatted");
- const img = document.createElement("img");
- img.alt = text;
- pre.replaceChildren(img);
-
- is(selectAndEncode(pre), convertLineBreaksForTestResult(text),
- `Encoded data for line breaks: ${JSON.stringify(lineBreaks)}`);
- is(selectAndEncode(pre, de.OutputForPlainTextClipboardCopy), convertLineBreaksForClipboardTestResult(text),
- `Encoded data for line breaks: ${JSON.stringify(lineBreaks)} (OutputForPlainTextClipboardCopy)`);
- });
-
- add_task(async function test_pre_img_title() {
- info(`Testing line breaks: ${JSON.stringify(lineBreaks)}`);
-
- const pre = document.getElementById("preformatted");
- const img = document.createElement("img");
- img.title = text;
- pre.replaceChildren(img);
-
- is(selectAndEncode(pre), ` [${convertLineBreaksForTestResult(text)}] `,
- `Encoded data for line breaks: ${JSON.stringify(lineBreaks)}`);
- is(selectAndEncode(pre, de.OutputForPlainTextClipboardCopy), ` [${convertLineBreaksForClipboardTestResult(text)}] `,
- `Encoded data for line breaks: ${JSON.stringify(lineBreaks)} (OutputForPlainTextClipboardCopy)`);
- });
-
- add_task(async function test_div_text() {
- info(`Testing line breaks: ${JSON.stringify(lineBreaks)}`);
-
- const div = document.getElementById("container");
- div.textContent = text;
-
- is(selectAndEncode(div), ` First Second `,
- `Encoded data for line breaks: ${JSON.stringify(lineBreaks)}`);
- is(selectAndEncode(div, de.OutputForPlainTextClipboardCopy), ` First Second `,
- `Encoded data for line breaks: ${JSON.stringify(lineBreaks)} (OutputForPlainTextClipboardCopy)`);
- });
-
- add_task(async function test_div_img_alt() {
- info(`Testing line breaks: ${JSON.stringify(lineBreaks)}`);
-
- const div = document.getElementById("container");
- const img = document.createElement("img");
- img.alt = text;
- div.replaceChildren(img);
-
- // Our plain text serializer keep the first CR/LF.
- is(selectAndEncode(div), `${lineBreaks[0]}First Second `,
- `Encoded data for line breaks: ${JSON.stringify(lineBreaks)}`);
- is(selectAndEncode(div, de.OutputForPlainTextClipboardCopy), ` First Second `,
- `Encoded data for line breaks: ${JSON.stringify(lineBreaks)} (OutputForPlainTextClipboardCopy)`);
- });
-
- add_task(async function test_div_img_title() {
- info(`Testing line breaks: ${JSON.stringify(lineBreaks)}`);
-
- const div = document.getElementById("container");
- const img = document.createElement("img");
- img.title = text;
- div.replaceChildren(img);
-
- is(selectAndEncode(div), ` [ First Second ] `,
- `Encoded data for line breaks: ${JSON.stringify(lineBreaks)}`);
- is(selectAndEncode(div, de.OutputForPlainTextClipboardCopy), ` [ First Second ] `,
- `Encoded data for line breaks: ${JSON.stringify(lineBreaks)} (OutputForPlainTextClipboardCopy)`);
- });
-});
-
-</script>
-</body>
-</html>
diff --git a/testing/web-platform/meta/clipboard-apis/clipboard-copy-selection-line-break.https.html.ini b/testing/web-platform/meta/clipboard-apis/clipboard-copy-selection-line-break.https.html.ini
@@ -1,70 +0,0 @@
-[clipboard-copy-selection-line-break.https.html?31-last]
- [test preformatted text with line breaks: "\\n \\r"]
- expected:
- if (os == "win") or (os == "linux"): FAIL # https://bugzilla.mozilla.org/show_bug.cgi?id=1997585
-
-
-[clipboard-copy-selection-line-break.https.html?1-10]
- [test preformatted text with line breaks: "\\r"]
- expected:
- if (os == "win") or (os == "linux"): FAIL # https://bugzilla.mozilla.org/show_bug.cgi?id=1997585
-
- [test preformatted text with line breaks: "\\n"]
- expected:
- if os == "win": FAIL # https://bugzilla.mozilla.org/show_bug.cgi?id=1997585
-
- [test preformatted text with line breaks: "\\r\\r"]
- expected:
- if (os == "win") or (os == "linux"): FAIL # https://bugzilla.mozilla.org/show_bug.cgi?id=1997585
-
- [test preformatted text with line breaks: "\\r\\n"]
- expected:
- if (os == "win") or (os == "linux"): FAIL # https://bugzilla.mozilla.org/show_bug.cgi?id=1997585
-
- [test preformatted text with line breaks: "\\n\\r"]
- expected:
- if (os == "win") or (os == "linux"): FAIL # https://bugzilla.mozilla.org/show_bug.cgi?id=1997585
-
-
-[clipboard-copy-selection-line-break.https.html?11-20]
- [test preformatted text with line breaks: "\\n\\n"]
- expected:
- if os == "win": FAIL # https://bugzilla.mozilla.org/show_bug.cgi?id=1997585
-
- [test preformatted text with line breaks: "\\r\\r\\r"]
- expected:
- if (os == "win") or (os == "linux"): FAIL # https://bugzilla.mozilla.org/show_bug.cgi?id=1997585
-
- [test preformatted text with line breaks: "\\r\\r\\n"]
- expected:
- if (os == "win") or (os == "linux"): FAIL # https://bugzilla.mozilla.org/show_bug.cgi?id=1997585
-
- [test preformatted text with line breaks: "\\r\\n\\r"]
- expected:
- if (os == "win") or (os == "linux"): FAIL # https://bugzilla.mozilla.org/show_bug.cgi?id=1997585
-
- [test preformatted text with line breaks: "\\r\\n\\n"]
- expected:
- if (os == "win") or (os == "linux"): FAIL # https://bugzilla.mozilla.org/show_bug.cgi?id=1997585
-
-
-[clipboard-copy-selection-line-break.https.html?21-30]
- [test preformatted text with line breaks: "\\n\\r\\r"]
- expected:
- if (os == "win") or (os == "linux"): FAIL # https://bugzilla.mozilla.org/show_bug.cgi?id=1997585
-
- [test preformatted text with line breaks: "\\n\\r\\n"]
- expected:
- if (os == "win") or (os == "linux"): FAIL # https://bugzilla.mozilla.org/show_bug.cgi?id=1997585
-
- [test preformatted text with line breaks: "\\n\\n\\r"]
- expected:
- if (os == "win") or (os == "linux"): FAIL # https://bugzilla.mozilla.org/show_bug.cgi?id=1997585
-
- [test preformatted text with line breaks: "\\n\\n\\n"]
- expected:
- if os == "win": FAIL # https://bugzilla.mozilla.org/show_bug.cgi?id=1997585
-
- [test preformatted text with line breaks: "\\r \\n"]
- expected:
- if (os == "win") or (os == "linux"): FAIL # https://bugzilla.mozilla.org/show_bug.cgi?id=1997585
diff --git a/testing/web-platform/tests/clipboard-apis/clipboard-copy-selection-line-break.https.html b/testing/web-platform/tests/clipboard-apis/clipboard-copy-selection-line-break.https.html
@@ -1,119 +0,0 @@
-<!doctype html>
-<meta charset="utf-8">
-<meta name="timeout" content="long">
-<meta name="variant" content="?1-10">
-<meta name="variant" content="?11-20">
-<meta name="variant" content="?21-30">
-<meta name="variant" content="?31-last">
-<title>Clipboard copy selection tests - Line breaks</title>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/resources/testdriver.js"></script>
-<script src="/resources/testdriver-vendor.js"></script>
-<script src="/resources/testdriver-actions.js"></script>
-<script src="/common/subset-tests.js"></script>
-<script src="resources/user-activation.js"></script>
-<body>
-Body needed for test_driver.click()<br>
-<div id="paragraph"></div>
-<pre id="preformatted"></pre>
-<script>
-
- const CASES = [
- "\r",
- "\n",
- "\r\r",
- "\r\n",
- "\n\r",
- "\n\n",
- "\r\r\r",
- "\r\r\n",
- "\r\n\r",
- "\r\n\n",
- "\n\r\r",
- "\n\r\n",
- "\n\n\r",
- "\n\n\n",
- "\r \n",
- "\n \r",
- ];
-
- function normalizeForPrinting(s) {
- return s.replace(/\r/g, "\\r").replace(/\n/g, "\\n");
- }
-
- function normalizeForComparison(s) {
- // Convert LF to CRLF on Windows.
- return navigator.platform.includes('Win') ? s.replace(/\r?\n/g, '\r\n') : s;
- }
-
- // Permissions are required in order to invoke navigator.clipboard functions in
- // an automated test.
- async function getPermissions() {
- await tryGrantReadPermission();
- await tryGrantWritePermission();
- await waitForUserActivation();
- }
-
- async function selectTextAndCopy(id, text) {
- const element = document.getElementById(id);
- element.textContent = text;
-
- await waitForUserActivation();
-
- // Select the text.
- const selection = window.getSelection();
- selection.removeAllRanges();
- selection.selectAllChildren(element);
-
- // Copy the selection to the clipboard.
- await new Promise((resolve) => {
- document.addEventListener("copy", e => {
- assert_true(true, "selection copid to clipboard");
- resolve();
- }, { once: true });
- document.execCommand("copy");
- });
- }
-
- async function getTextFromPasteEvent() {
- return new Promise((resolve) => {
- document.addEventListener("paste", e => {
- e.preventDefault();
- resolve(e.clipboardData.getData("text/plain"));
- }, { once: true });
- sendPasteShortcutKey();
- });
- }
-
- async function getTextFromClipboardReadText() {
- await waitForUserActivation();
- return navigator.clipboard.readText();
- }
-
- CASES.forEach(testCase => {
- const text = `First${testCase}Second`;
-
- subsetTest(promise_test, async t => {
- await getPermissions();
- await selectTextAndCopy("preformatted", text);
- let clipboardText = await getTextFromPasteEvent();
- assert_equals(clipboardText, normalizeForComparison(text), "Check result from DataTransfer in paste event");
-
- clipboardText = await getTextFromClipboardReadText();
- assert_equals(clipboardText, normalizeForComparison(text), "Check result from navigator.clipboard.readText()");
- }, `test preformatted text with line breaks: "${normalizeForPrinting(testCase)}"`);
-
- subsetTest(promise_test, async t => {
- await getPermissions();
- await selectTextAndCopy("paragraph", text);
- let clipboardText = await getTextFromPasteEvent();
- assert_equals(clipboardText, `First Second`, "Check result from DataTransfer in paste event");
-
- clipboardText = await getTextFromClipboardReadText();
- assert_equals(clipboardText, `First Second`, "Check result from navigator.clipboard.readText()");
- }, `test paragraph text with line breaks: "${normalizeForPrinting(testCase)}"`);
- });
-
-</script>
-</body>
diff --git a/testing/web-platform/tests/clipboard-apis/resources/user-activation.js b/testing/web-platform/tests/clipboard-apis/resources/user-activation.js
@@ -42,14 +42,3 @@ async function tryGrantWritePermission() {
await trySetPermission("clipboard-write", "granted");
}
-async function sendPasteShortcutKey() {
- const modifier = navigator.platform.includes("Mac") ? "\uE03d" // META
- : "\uE009"; // CONTROL
- await new test_driver.Actions()
- .keyDown(modifier)
- .keyDown("v")
- .keyUp("v")
- .keyUp(modifier)
- .send();
-}
-