diff --git a/dom/events/TextComposition.cpp b/dom/events/TextComposition.cpp index a53711c1fbc6..616547f1779d 100644 --- a/dom/events/TextComposition.cpp +++ b/dom/events/TextComposition.cpp @@ -225,7 +225,7 @@ RemoveControlCharactersFrom(nsAString& aStr, TextRangeArray* aRanges) size_t i = firstControlCharOffset; for (const char16_t* source = sourceBegin + firstControlCharOffset; source < sourceEnd; ++source) { - if (*source == '\t' || !IsControlChar(*source)) { + if (*source == '\t' || *source == '\n' || !IsControlChar(*source)) { *curDest = *source; ++curDest; ++i; diff --git a/widget/TextEventDispatcher.cpp b/widget/TextEventDispatcher.cpp index 0e4728ad6aa6..b7f519130d7d 100644 --- a/widget/TextEventDispatcher.cpp +++ b/widget/TextEventDispatcher.cpp @@ -319,9 +319,11 @@ TextEventDispatcher::CommitComposition(nsEventStatus& aStatus, } if (message == eCompositionCommit) { compositionCommitEvent.mData = *aCommitString; - // Don't send CRLF, replace it with LF here. + // Don't send CRLF nor CR, replace it with LF here. compositionCommitEvent.mData.ReplaceSubstring(NS_LITERAL_STRING("\r\n"), NS_LITERAL_STRING("\n")); + compositionCommitEvent.mData.ReplaceSubstring(NS_LITERAL_STRING("\r"), + NS_LITERAL_STRING("\n")); } rv = DispatchEvent(widget, compositionCommitEvent, aStatus); if (NS_WARN_IF(NS_FAILED(rv))) { @@ -708,8 +710,9 @@ TextEventDispatcher::PendingComposition::ReplaceNativeLineBreakers() } nsAutoString nativeString(mString); - // Don't expose CRLF to web contents, instead, use LF. + // Don't expose CRLF nor CR to web contents, instead, use LF. mString.ReplaceSubstring(NS_LITERAL_STRING("\r\n"), NS_LITERAL_STRING("\n")); + mString.ReplaceSubstring(NS_LITERAL_STRING("\r"), NS_LITERAL_STRING("\n")); // If the length isn't changed, we don't need to adjust any offset and length // of mClauses nor mCaret. diff --git a/widget/tests/window_composition_text_querycontent.xul b/widget/tests/window_composition_text_querycontent.xul index cb81f3978fd1..36bdcda15992 100644 --- a/widget/tests/window_composition_text_querycontent.xul +++ b/widget/tests/window_composition_text_querycontent.xul @@ -6006,8 +6006,6 @@ function runNativeLineBreakerTest() textarea.addEventListener("compositionend", handler, true); // '\n' in composition string shouldn't be changed. - // XXX Currently, \n isn't accepted by TextComposition. So,these test checks - // without the length of \n (bug 1339331). clearResult(); textarea.value = ""; var clauses = [ "abc\n", "def\n\ng", "hi\n", "\njkl" ]; @@ -6030,18 +6028,18 @@ function runNativeLineBreakerTest() "caret": { "start": caret.length, "length": 0 } }); - checkSelection(caret.replace(/\n/g, "").length, "", "runNativeLineBreakerTest", "#1"); - checkIMESelection("RawClause", true, 0, clauses[0].replace(/\n/g, ""), "runNativeLineBreakerTest: \\n shouldn't be replaced with any character #1"); - checkIMESelection("SelectedRawClause", true, clauses[0].replace(/\n/g, "").length, clauses[1].replace(/\n/g, ""), "runNativeLineBreakerTest: \\n shouldn't be replaced with any character #1"); - checkIMESelection("ConvertedClause", true, (clauses[0] + clauses[1]).replace(/\n/g, "").length, clauses[2].replace(/\n/g, ""), "runNativeLineBreakerTest: \\n shouldn't be replaced with any character #1"); - checkIMESelection("SelectedClause", true, (clauses[0] + clauses[1] + clauses[2]).replace(/\n/g, "").length, clauses[3].replace(/\n/g, ""), "runNativeLineBreakerTest: \\n shouldn't be replaced with any character #1"); - is(result.compositionupdate, clauses.join('').replace(/\n/g, ""), "runNativeLineBreakerTest: \\n in compositionupdate.data shouldn't be removed nor replaced with other characters #1"); - is(textarea.value, clauses.join('').replace(/\n/g, ""), "runNativeLineBreakerTest: \\n in textarea.value shouldn't be removed nor replaced with other characters #1"); + checkSelection(caret.replace(/\n/g, "\n").length + (kLFLen - 1) * 4, "", "runNativeLineBreakerTest", "#1"); + checkIMESelection("RawClause", true, 0, clauses[0].replace(/\n/g, kLF), "runNativeLineBreakerTest: \\n shouldn't be replaced with any character #1"); + checkIMESelection("SelectedRawClause", true, clauses[0].replace(/\n/g, kLF).length, clauses[1].replace(/\n/g, kLF), "runNativeLineBreakerTest: \\n shouldn't be replaced with any character #1"); + checkIMESelection("ConvertedClause", true, (clauses[0] + clauses[1]).replace(/\n/g, kLF).length, clauses[2].replace(/\n/g, kLF), "runNativeLineBreakerTest: \\n shouldn't be replaced with any character #1"); + checkIMESelection("SelectedClause", true, (clauses[0] + clauses[1] + clauses[2]).replace(/\n/g, kLF).length, clauses[3].replace(/\n/g, kLF), "runNativeLineBreakerTest: \\n shouldn't be replaced with any character #1"); + is(result.compositionupdate, clauses.join('').replace(/\n/g, "\n"), "runNativeLineBreakerTest: \\n in compositionupdate.data shouldn't be removed nor replaced with other characters #1"); + is(textarea.value, clauses.join('').replace(/\n/g, "\n"), "runNativeLineBreakerTest: \\n in textarea.value shouldn't be removed nor replaced with other characters #1"); synthesizeComposition({ type: "compositioncommit", data: clauses.join('') }); - checkSelection(clauses.join('').replace(/\n/g, "").length, "", "runNativeLineBreakerTest", "#2"); - is(result.compositionend, clauses.join('').replace(/\n/g, ""), "runNativeLineBreakerTest: \\n in compositionend.data shouldn't be removed nor replaced with other characters #2"); - is(textarea.value, clauses.join('').replace(/\n/g, ""), "runNativeLineBreakerTest: \\n in textarea.value shouldn't be removed nor replaced with other characters #2"); + checkSelection(clauses.join('').replace(/\n/g, "\n").length + (kLFLen - 1) * 5, "", "runNativeLineBreakerTest", "#2"); + is(result.compositionend, clauses.join('').replace(/\n/g, "\n"), "runNativeLineBreakerTest: \\n in compositionend.data shouldn't be removed nor replaced with other characters #2"); + is(textarea.value, clauses.join('').replace(/\n/g, "\n"), "runNativeLineBreakerTest: \\n in textarea.value shouldn't be removed nor replaced with other characters #2"); // \r\n in composition string should be replaced with \n. clearResult(); @@ -6066,18 +6064,54 @@ function runNativeLineBreakerTest() "caret": { "start": caret.length, "length": 0 } }); - checkSelection(caret.replace(/\r\n/g, "").length, "", "runNativeLineBreakerTest", "#3"); - checkIMESelection("RawClause", true, 0, clauses[0].replace(/\r\n/g, ""), "runNativeLineBreakerTest: \\r\\n should be replaced with \\n #3"); - checkIMESelection("SelectedRawClause", true, clauses[0].replace(/\r\n/g, "").length, clauses[1].replace(/\r\n/g, ""), "runNativeLineBreakerTest: \\r\\n should be replaced with \\n #3"); - checkIMESelection("ConvertedClause", true, (clauses[0] + clauses[1]).replace(/\r\n/g, "").length, clauses[2].replace(/\r\n/g, ""), "runNativeLineBreakerTest: \\r\\n should be replaced with \\n #3"); - checkIMESelection("SelectedClause", true, (clauses[0] + clauses[1] + clauses[2]).replace(/\r\n/g, "").length, clauses[3].replace(/\r\n/g, ""), "runNativeLineBreakerTest: \\r\\n should be replaced with \\n #3"); - is(result.compositionupdate, clauses.join('').replace(/\r\n/g, ""), "runNativeLineBreakerTest: \\r\\n in compositionudpate.data should be replaced with \\n #3"); - is(textarea.value, clauses.join('').replace(/\r\n/g, ""), "runNativeLineBreakerTest: \\r\\n in textarea.value should be replaced with \\n #3"); + checkSelection(caret.replace(/\r\n/g, "\n").length + (kLFLen - 1) * 4, "", "runNativeLineBreakerTest", "#3"); + checkIMESelection("RawClause", true, 0, clauses[0].replace(/\r\n/g, kLF), "runNativeLineBreakerTest: \\r\\n should be replaced with \\n #3"); + checkIMESelection("SelectedRawClause", true, clauses[0].replace(/\r\n/g, kLF).length, clauses[1].replace(/\r\n/g, kLF), "runNativeLineBreakerTest: \\r\\n should be replaced with \\n #3"); + checkIMESelection("ConvertedClause", true, (clauses[0] + clauses[1]).replace(/\r\n/g, kLF).length, clauses[2].replace(/\r\n/g, kLF), "runNativeLineBreakerTest: \\r\\n should be replaced with \\n #3"); + checkIMESelection("SelectedClause", true, (clauses[0] + clauses[1] + clauses[2]).replace(/\r\n/g, kLF).length, clauses[3].replace(/\r\n/g, kLF), "runNativeLineBreakerTest: \\r\\n should be replaced with \\n #3"); + is(result.compositionupdate, clauses.join('').replace(/\r\n/g, "\n"), "runNativeLineBreakerTest: \\r\\n in compositionudpate.data should be replaced with \\n #3"); + is(textarea.value, clauses.join('').replace(/\r\n/g, "\n"), "runNativeLineBreakerTest: \\r\\n in textarea.value should be replaced with \\n #3"); synthesizeComposition({ type: "compositioncommit", data: clauses.join('') }); - checkSelection(clauses.join('').replace(/\r\n/g, "").length, "", "runNativeLineBreakerTest", "#4"); - is(result.compositionend, clauses.join('').replace(/\r\n/g, ""), "runNativeLineBreakerTest: \\r\\n in compositionend.data should be replaced with \\n #4"); - is(textarea.value, clauses.join('').replace(/\r\n/g, ""), "runNativeLineBreakerTest: \\r\\n in textarea.value should be replaced with \\n #4"); + checkSelection(clauses.join('').replace(/\r\n/g, "\n").length + (kLFLen - 1) * 5, "", "runNativeLineBreakerTest", "#4"); + is(result.compositionend, clauses.join('').replace(/\r\n/g, "\n"), "runNativeLineBreakerTest: \\r\\n in compositionend.data should be replaced with \\n #4"); + is(textarea.value, clauses.join('').replace(/\r\n/g, "\n"), "runNativeLineBreakerTest: \\r\\n in textarea.value should be replaced with \\n #4"); + + // \r (not followed by \n) in composition string should be replaced with \n. + clearResult(); + textarea.value = ""; + clauses = [ "abc\r", "def\r\rg", "hi\r", "\rjkl" ]; + caret = clauses[0] + clauses[1] + clauses[2]; + synthesizeCompositionChange( + { "composition": + { "string": clauses.join(''), + "clauses": + [ + { "length": clauses[0].length, + "attr": COMPOSITION_ATTR_RAW_CLAUSE }, + { "length": clauses[1].length, + "attr": COMPOSITION_ATTR_SELECTED_RAW_CLAUSE }, + { "length": clauses[2].length, + "attr": COMPOSITION_ATTR_CONVERTED_CLAUSE }, + { "length": clauses[3].length, + "attr": COMPOSITION_ATTR_SELECTED_CLAUSE }, + ] + }, + "caret": { "start": caret.length, "length": 0 } + }); + + checkSelection(caret.replace(/\r/g, "\n").length + (kLFLen - 1) * 4, "", "runNativeLineBreakerTest", "#5"); + checkIMESelection("RawClause", true, 0, clauses[0].replace(/\r/g, kLF), "runNativeLineBreakerTest: \\r should be replaced with \\n #5"); + checkIMESelection("SelectedRawClause", true, clauses[0].replace(/\r/g, kLF).length, clauses[1].replace(/\r/g, kLF), "runNativeLineBreakerTest: \\r should be replaced with \\n #5"); + checkIMESelection("ConvertedClause", true, (clauses[0] + clauses[1]).replace(/\r/g, kLF).length, clauses[2].replace(/\r/g, kLF), "runNativeLineBreakerTest: \\r should be replaced with \\n #5"); + checkIMESelection("SelectedClause", true, (clauses[0] + clauses[1] + clauses[2]).replace(/\r/g, kLF).length, clauses[3].replace(/\r/g, kLF), "runNativeLineBreakerTest: \\r should be replaced with \\n #5"); + is(result.compositionupdate, clauses.join('').replace(/\r/g, "\n"), "runNativeLineBreakerTest: \\r in compositionupdate.data should be replaced with \\n #5"); + is(textarea.value, clauses.join('').replace(/\r/g, "\n"), "runNativeLineBreakerTest: \\r in textarea.value should be replaced with \\n #5"); + + synthesizeComposition({ type: "compositioncommit", data: clauses.join('') }); + checkSelection(clauses.join('').replace(/\r/g, "\n").length + (kLFLen - 1) * 5, "", "runNativeLineBreakerTest", "#6"); + is(result.compositionend, clauses.join('').replace(/\r/g, "\n"), "runNativeLineBreakerTest: \\r in compositionend.data should be replaced with \\n #6"); + is(textarea.value, clauses.join('').replace(/\r/g, "\n"), "runNativeLineBreakerTest: \\r in textarea.value should be replaced with \\n #6"); textarea.removeEventListener("compositionupdate", handler, true); textarea.removeEventListener("compositionend", handler, true); @@ -6106,7 +6140,7 @@ function runControlCharTest() textarea.value = ""; var controlChars = String.fromCharCode.apply(null, Object.keys(Array.from({length:0x20}))) + "\x7F"; - var allowedChars = "\t"; + var allowedChars = "\t\n\n"; var data = "AB" + controlChars + "CD" + controlChars + "EF"; var removedData = "AB" + allowedChars + "CD" + allowedChars + "EF"; @@ -6130,7 +6164,7 @@ function runControlCharTest() "caret": { "start": DIndex, "length": 0 } }); - checkSelection(removedDIndex, "", "runControlCharTest", "#1") + checkSelection(removedDIndex + (kLFLen - 1) * 2, "", "runControlCharTest", "#1") is(result.compositionupdate, removedData, "runControlCharTest: control characters in event.data should be removed in compositionupdate event #1"); is(textarea.value, removedData, "runControlCharTest: control characters should not appear in textarea #1"); @@ -6162,14 +6196,14 @@ function runControlCharTest() "caret": { "start": DIndex, "length": 0 } }); - checkSelection(DIndex - 1 + kLFLen, "", "runControlCharTest", "#3") + checkSelection(DIndex + (kLFLen - 1) * 2, "", "runControlCharTest", "#3") - is(result.compositionupdate, data, "runControlCharTest: control characters in event.data should not be removed in compositionupdate event #3"); + is(result.compositionupdate, data.replace(/\r/g, "\n"), "runControlCharTest: control characters in event.data should not be removed in compositionupdate event #3"); is(textarea.value, data.replace(/\r/g, "\n"), "runControlCharTest: control characters should appear in textarea #3"); synthesizeComposition({ type: "compositioncommit", data: data }); - is(result.compositionend, data, "runControlCharTest: control characters in event.data should not be removed in compositionend event #4"); + is(result.compositionend, data.replace(/\r/g, "\n"), "runControlCharTest: control characters in event.data should not be removed in compositionend event #4"); is(textarea.value, data.replace(/\r/g, "\n"), "runControlCharTest: control characters should appear in textarea #4"); SpecialPowers.clearUserPref("dom.compositionevent.allow_control_characters");