Bug 1028485 Don't set caret position over actual inserted string at commiting composition r=ehsan

This commit is contained in:
Masayuki Nakano 2014-06-24 10:06:20 +09:00
Родитель d23f6e9b7c
Коммит f850719aaf
4 изменённых файлов: 119 добавлений и 11 удалений

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

@ -224,6 +224,17 @@ IMETextTxn::SetSelectionForRanges()
// Set caret position and selection of IME composition with TextRangeArray.
bool setCaret = false;
uint32_t countOfRanges = mRanges ? mRanges->Length() : 0;
#ifdef DEBUG
// When this sets selection (caret) offset to out of the content of
// the editor, let's crash the process only on debug build. That makes such
// bugs detectable with automated tests.
uint32_t maxOffset = UINT32_MAX;
mElement->GetLength(&maxOffset);
#endif
// The mStringToInsert may be truncated if maxlength attribute value doesn't
// allow to input all text of this composition. So, we can get actual length
// of the inserted string from it.
uint32_t insertedLength = mStringToInsert.Length();
for (uint32_t i = 0; i < countOfRanges; ++i) {
const TextRange& textRange = mRanges->ElementAt(i);
@ -232,9 +243,11 @@ IMETextTxn::SetSelectionForRanges()
if (textRange.mRangeType == NS_TEXTRANGE_CARETPOSITION) {
NS_ASSERTION(!setCaret, "The ranges already has caret position");
NS_ASSERTION(!textRange.Length(), "nsEditor doesn't support wide caret");
// NOTE: If the caret position is larger than max length of the editor
// content, this may fail.
rv = selection->Collapse(mElement, mOffset + textRange.mStartOffset);
int32_t caretOffset = static_cast<int32_t>(
mOffset + std::min(textRange.mStartOffset, insertedLength));
MOZ_ASSERT(caretOffset >= 0 &&
static_cast<uint32_t>(caretOffset) <= maxOffset);
rv = selection->Collapse(mElement, caretOffset);
setCaret = setCaret || NS_SUCCEEDED(rv);
NS_ASSERTION(setCaret, "Failed to collapse normal selection");
continue;
@ -247,8 +260,16 @@ IMETextTxn::SetSelectionForRanges()
}
nsRefPtr<nsRange> clauseRange;
rv = nsRange::CreateRange(mElement, mOffset + textRange.mStartOffset,
mElement, mOffset + textRange.mEndOffset,
int32_t startOffset = static_cast<int32_t>(
mOffset + std::min(textRange.mStartOffset, insertedLength));
MOZ_ASSERT(startOffset >= 0 &&
static_cast<uint32_t>(startOffset) <= maxOffset);
int32_t endOffset = static_cast<int32_t>(
mOffset + std::min(textRange.mEndOffset, insertedLength));
MOZ_ASSERT(endOffset >= startOffset &&
static_cast<uint32_t>(endOffset) <= maxOffset);
rv = nsRange::CreateRange(mElement, startOffset,
mElement, endOffset,
getter_AddRefs(clauseRange));
if (NS_FAILED(rv)) {
NS_WARNING("Failed to create a DOM range for a clause of composition");
@ -288,7 +309,10 @@ IMETextTxn::SetSelectionForRanges()
// If the ranges doesn't include explicit caret position, let's set the
// caret to the end of composition string.
if (!setCaret) {
rv = selection->Collapse(mElement, mOffset + mStringToInsert.Length());
int32_t caretOffset = static_cast<int32_t>(mOffset + insertedLength);
MOZ_ASSERT(caretOffset >= 0 &&
static_cast<uint32_t>(caretOffset) <= maxOffset);
rv = selection->Collapse(mElement, caretOffset);
NS_ASSERTION(NS_SUCCEEDED(rv),
"Failed to set caret at the end of composition string");
}

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

@ -19,9 +19,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1026397
<script type="application/javascript">
/** Test for Bug 1026397 **/
// Assertion fails at setting caret at committing composition string if it's cut by
// maxlength and the caret position is out of the input element value. See bug 1028485.
SimpleTest.expectAssertions(10);
SimpleTest.waitForExplicitFinish();
function runTests()

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

@ -19,8 +19,6 @@
<script class="testbody" type="application/javascript">
<![CDATA[
SimpleTest.expectAssertions(2);
SimpleTest.waitForExplicitFinish();
window.open("window_composition_text_querycontent.xul", "_blank",
"chrome,width=600,height=600");

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

@ -3443,6 +3443,95 @@ function runMaxLengthTest()
!checkSelection(1 + 0, "", kDesc, "#3-4")) {
return;
}
// The input element whose content length is already maxlength and
// the carest is at start of the content.
input.value = "X";
input.selectionStart = input.selectionEnd = 0;
// start composition
synthesizeComposition({ type: "compositionstart" });
// input characters
synthesizeComposition({ type: "compositionupdate", data: "\u9B54" });
synthesizeText(
{ "composition":
{ "string": "\u9B54",
"clauses":
[
{ "length": 1, "attr": COMPOSITION_ATTR_RAWINPUT }
]
},
"caret": { "start": 1, "length": 0 }
});
if (!checkContent("\u9B54X", kDesc, "#4-1") ||
!checkSelection(1, "", kDesc, "#4-1")) {
return;
}
// commit the composition string
synthesizeComposition({ type: "compositionupdate", data: "\u9B54" });
synthesizeText(
{ "composition":
{ "string": "\u9B54",
"clauses":
[
{ "length": 0, "attr": 0 }
]
},
"caret": { "start": 1, "length": 0 }
});
// The input text must be discarded. Then, the caret position shouldn't be
// updated from its position at compositionstart.
if (!checkContent("X", kDesc, "#4-2") ||
!checkSelection(0, "", kDesc, "#4-2")) {
return;
}
synthesizeComposition({ type: "compositionend", data: "\u9B54" });
// start composition
synthesizeComposition({ type: "compositionstart" });
// input characters
synthesizeComposition({ type: "compositionupdate", data: "\u9B54\u6CD5" });
synthesizeText(
{ "composition":
{ "string": "\u9B54\u6CD5",
"clauses":
[
{ "length": 2, "attr": COMPOSITION_ATTR_RAWINPUT }
]
},
"caret": { "start": 2, "length": 0 }
});
if (!checkContent("\u9B54\u6CD5X", kDesc, "#5-1") ||
!checkSelection(2, "", kDesc, "#5-1")) {
return;
}
// commit the composition string
synthesizeComposition({ type: "compositionupdate", data: "\u9B54\u6CD5" });
synthesizeText(
{ "composition":
{ "string": "\u9B54\u6CD5",
"clauses":
[
{ "length": 0, "attr": 0 }
]
},
"caret": { "start": 2, "length": 0 }
});
if (!checkContent("X", kDesc, "#5-2") ||
!checkSelection(0, "", kDesc, "#5-2")) {
return;
}
synthesizeComposition({ type: "compositionend", data: "\u9B54\u6CD5" });
}
function runTest()