Bug 1339331 TextEventDispatcher should replace \r in composition string with \n and TextComposition should allow to input \n with composition events r=m_kato

According to ATOK's behavior, IME may send different line breaker from its platform's standard.  Therefore, we should treat \r as \n too.

Additionally, currently, TextComposition doesn't allow to input \n with composition.  However, this was added for preventing to see odd control characters as boxes with code point.  Therefore, we should allow \n for IMEs. (It was allowed, this limitation is unexpected when I reviewed the patch to reject control characters in TextComposition.)

MozReview-Commit-ID: DzGSMgp89Av

--HG--
extra : rebase_source : 0644e5941a080583af8701546111fbf46ec646ec
This commit is contained in:
Masayuki Nakano 2017-03-16 16:26:43 +09:00
Родитель 0865aae1e0
Коммит c038f800f7
3 изменённых файлов: 67 добавлений и 30 удалений

Просмотреть файл

@ -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;

Просмотреть файл

@ -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.

Просмотреть файл

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