Chromium Code Reviews| Index: third_party/WebKit/Source/core/editing/InputMethodController.cpp |
| diff --git a/third_party/WebKit/Source/core/editing/InputMethodController.cpp b/third_party/WebKit/Source/core/editing/InputMethodController.cpp |
| index f8628dc7e842b61d332d1ad4b0a6870b9b25a253..2959968068fbd9ac495be07a4f8762c56e857e26 100644 |
| --- a/third_party/WebKit/Source/core/editing/InputMethodController.cpp |
| +++ b/third_party/WebKit/Source/core/editing/InputMethodController.cpp |
| @@ -357,6 +357,158 @@ void InputMethodController::cancelCompositionIfSelectionIsInvalid() { |
| frame().chromeClient().didCancelCompositionOnSelectionChange(); |
| } |
| +static size_t computeCommonPrefixLength(const String& str1, |
| + const String& str2) { |
| + size_t length1 = str1.length(); |
| + size_t length2 = str2.length(); |
| + size_t commonPrefixLength = 0; |
|
yosin_UTC9
2016/10/11 03:58:10
for-loop is better
const size_t maxCommonPrefixLe
yabinh
2016/10/11 05:07:54
Done.
|
| + |
| + while (commonPrefixLength < length1 && commonPrefixLength < length2 && |
| + str1[commonPrefixLength] == str2[commonPrefixLength]) |
| + ++commonPrefixLength; |
| + |
| + return commonPrefixLength; |
| +} |
| + |
| +static size_t computeCommonSuffixLength(const String& str1, |
| + const String& str2) { |
| + size_t length1 = str1.length(); |
| + size_t length2 = str2.length(); |
| + size_t commonSuffixLength = 0; |
| + |
| + while (commonSuffixLength < length1 && commonSuffixLength < length2 && |
|
yosin_UTC9
2016/10/11 03:58:10
for-loop is better
const size_t maxCommonPrefixLe
yabinh
2016/10/11 05:07:54
Done.
|
| + str1[length1 - commonSuffixLength - 1] == |
| + str2[length2 - commonSuffixLength - 1]) |
| + ++commonSuffixLength; |
| + |
| + return commonSuffixLength; |
| +} |
| + |
| +static size_t computeCommonGraphemeClusterPrefixLengthForSetComposition( |
| + const String& oldText, |
| + const String& newText, |
| + const Element* rootEditableElement) { |
| + size_t commonPrefixLength = computeCommonPrefixLength(oldText, newText); |
| + |
| + // For multi-code text, we should adjust it for grapheme boundary. |
|
yosin_UTC9
2016/10/11 03:58:10
s/multi-code text/grapheme cluster/
yabinh
2016/10/11 05:07:54
Done.
|
| + const EphemeralRange& range = |
| + PlainTextRange(0, commonPrefixLength).createRange(*rootEditableElement); |
| + if (range.isNull()) |
| + return 0; |
| + const Position& position = range.endPosition(); |
| + const Position& adjustedPosition = previousPositionOf( |
| + nextPositionOf(position, PositionMoveType::GraphemeCluster), |
| + PositionMoveType::GraphemeCluster); |
| + |
| + DCHECK(position.anchorNode()->isEqualNode(adjustedPosition.anchorNode())); |
|
yosin_UTC9
2016/10/11 03:58:10
Let's use DCHECK_EQ(position.anchroNode(), adjuste
yabinh
2016/10/11 05:07:54
Done.
|
| + |
| + int diff = position.computeOffsetInContainerNode() - |
|
yosin_UTC9
2016/10/11 03:58:10
Could you add DCHECK_GE(position.computeOffsetInCo
yabinh
2016/10/11 05:07:55
Done.
|
| + adjustedPosition.computeOffsetInContainerNode(); |
| + return commonPrefixLength - static_cast<size_t>(diff); |
| +} |
| + |
| +static size_t computeCommonGraphemeClusterSuffixLengthForSetComposition( |
| + const String& oldText, |
| + const String& newText, |
| + const Element* rootEditableElement) { |
| + size_t commonSuffixLength = computeCommonSuffixLength(oldText, newText); |
| + |
| + // For multi-code text, we should adjust it for grapheme boundary. |
| + const EphemeralRange& range = |
| + PlainTextRange(0, oldText.length() - commonSuffixLength) |
| + .createRange(*rootEditableElement); |
| + if (range.isNull()) |
| + return 0; |
| + const Position& position = range.endPosition(); |
| + const Position& adjustedPosition = nextPositionOf( |
| + previousPositionOf(position, PositionMoveType::GraphemeCluster), |
| + PositionMoveType::GraphemeCluster); |
| + |
| + DCHECK(position.anchorNode()->isEqualNode(adjustedPosition.anchorNode())); |
|
yosin_UTC9
2016/10/11 03:58:10
See comment in computeCommonGraphemeClusterPrefixL
yabinh
2016/10/11 05:07:54
Done.
|
| + |
| + int diff = adjustedPosition.computeOffsetInContainerNode() - |
| + position.computeOffsetInContainerNode(); |
| + return commonSuffixLength - static_cast<size_t>(diff); |
| +} |
| + |
| +void InputMethodController::setCompositionWithIncrementalText( |
| + const String& text, |
| + const Vector<CompositionUnderline>& underlines, |
| + int selectionStart, |
| + int selectionEnd) { |
| + Element* rootEditableElement = frame().selection().rootEditableElement(); |
| + if (!rootEditableElement) |
| + return; |
| + |
| + String composing = composingText(); |
| + size_t commonPrefixLength = |
| + computeCommonGraphemeClusterPrefixLengthForSetComposition( |
| + composing, text, rootEditableElement); |
| + |
| + // We should ignore common prefix when finding common suffix. |
| + size_t commonSuffixLength = |
|
yosin_UTC9
2016/10/11 03:58:10
|const size_t|?
yabinh
2016/10/11 05:07:54
Done.
|
| + computeCommonGraphemeClusterSuffixLengthForSetComposition( |
| + composing.right(composing.length() - commonPrefixLength), |
| + text.right(text.length() - commonPrefixLength), rootEditableElement); |
| + |
| + const bool appending = |
| + text.length() > commonPrefixLength + commonSuffixLength; |
| + const bool subtracting = |
| + composing.length() > commonPrefixLength + +commonSuffixLength; |
| + |
| + if (appending || subtracting) { |
| + Element* editable = frame().selection().rootEditableElement(); |
| + if (!editable) |
|
yosin_UTC9
2016/10/11 03:58:10
We've already check |editable| isn't nullptr at L4
yabinh
2016/10/11 05:07:54
Done.
|
| + return; |
| + |
| + // Select the text to be subtracted. |
| + size_t compositionStart = |
| + PlainTextRange::create(*editable, compositionEphemeralRange()).start(); |
| + size_t subtractionStart = compositionStart + commonPrefixLength; |
| + size_t subtractionEnd = |
| + compositionStart + composing.length() - commonSuffixLength; |
| + const EphemeralRange& subtractionRange = |
| + PlainTextRange(subtractionStart, subtractionEnd).createRange(*editable); |
| + VisibleSelection selection; |
| + selection.setWithoutValidation(subtractionRange.startPosition(), |
| + subtractionRange.endPosition()); |
| + frame().selection().setSelection(selection, 0); |
| + clear(); |
| + |
| + Element* target = frame().document()->focusedElement(); |
|
yosin_UTC9
2016/10/11 03:58:10
FrameSeleciton::setSelection() can change document
yabinh
2016/10/11 05:07:54
Done.
|
| + if (!target) |
| + return; |
| + |
| + // Append the incremental text. |
| + size_t appendingLength = |
| + text.length() - commonPrefixLength - commonSuffixLength; |
| + String appendingText = text.substring(commonPrefixLength, appendingLength); |
| + insertTextDuringCompositionWithEvents(frame(), appendingText, |
| + TypingCommand::PreventSpellChecking, |
| + TypingCommand::TextCompositionUpdate); |
| + |
| + // Event handlers might destroy document. |
| + if (!frame().document()) |
|
yosin_UTC9
2016/10/11 03:58:10
Similar to L478 comment.
yabinh
2016/10/11 05:07:54
Done.
|
| + return; |
| + |
| + // TODO(yosin): The use of updateStyleAndLayoutIgnorePendingStylesheets |
| + // needs to be audited. see http://crbug.com/590369 for more details. |
| + frame().document()->updateStyleAndLayoutIgnorePendingStylesheets(); |
| + |
| + // Now recreate the composition starting at its original start, and |
| + // apply the specified final selection offsets. |
| + setCompositionFromExistingText(underlines, compositionStart, |
| + compositionStart + text.length()); |
| + } |
| + |
| + selectComposition(); |
| + const PlainTextRange& selectedRange = createSelectionRangeForSetComposition( |
| + selectionStart, selectionEnd, text.length()); |
| + // We shouldn't close typing in the middle of setComposition. |
| + setEditableSelectionOffsets(selectedRange, NotUserTriggered); |
| + m_isDirty = true; |
| +} |
| + |
| void InputMethodController::setComposition( |
| const String& text, |
| const Vector<CompositionUnderline>& underlines, |
| @@ -369,6 +521,14 @@ void InputMethodController::setComposition( |
| // See https://bugs.webkit.org/show_bug.cgi?id=46868 |
| frame().document()->updateStyleAndLayoutTree(); |
| + // When the IME only wants to change a few characters at the end of the |
| + // composition, only touch those characters in order to preserve rich text |
| + // substructure. |
| + if (hasComposition() && text.length()) { |
| + return setCompositionWithIncrementalText(text, underlines, selectionStart, |
| + selectionEnd); |
| + } |
| + |
| selectComposition(); |
| if (frame().selection().isNone()) |
| @@ -378,15 +538,8 @@ void InputMethodController::setComposition( |
| if (!target) |
| return; |
| - // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets |
| - // needs to be audited. see http://crbug.com/590369 for more details. |
| - frame().document()->updateStyleAndLayoutIgnorePendingStylesheets(); |
| - |
| - int selectionOffsetsStart = static_cast<int>(getSelectionOffsets().start()); |
| - int start = selectionOffsetsStart + selectionStart; |
| - int end = selectionOffsetsStart + selectionEnd; |
| - PlainTextRange selectedRange = |
| - createRangeForSelection(start, end, text.length()); |
| + PlainTextRange selectedRange = createSelectionRangeForSetComposition( |
| + selectionStart, selectionEnd, text.length()); |
| // Dispatch an appropriate composition event to the focused node. |
| // We check the composition status and choose an appropriate composition event |
| @@ -503,6 +656,20 @@ void InputMethodController::setComposition( |
| } |
| } |
| +PlainTextRange InputMethodController::createSelectionRangeForSetComposition( |
| + int selectionStart, |
| + int selectionEnd, |
| + size_t textLength) const { |
| + // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets |
| + // needs to be audited. see http://crbug.com/590369 for more details. |
| + frame().document()->updateStyleAndLayoutIgnorePendingStylesheets(); |
| + |
| + int selectionOffsetsStart = static_cast<int>(getSelectionOffsets().start()); |
| + int start = selectionOffsetsStart + selectionStart; |
| + int end = selectionOffsetsStart + selectionEnd; |
| + return createRangeForSelection(start, end, textLength); |
| +} |
| + |
| void InputMethodController::setCompositionFromExistingText( |
| const Vector<CompositionUnderline>& underlines, |
| unsigned compositionStart, |