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 6b6569806313de4431994a4ede65ea2287346bee..96f5ef7257165ee434fc85950ce3490e9e4558d9 100644 |
| --- a/third_party/WebKit/Source/core/editing/InputMethodController.cpp |
| +++ b/third_party/WebKit/Source/core/editing/InputMethodController.cpp |
| @@ -76,9 +76,10 @@ void dispatchCompositionEndEvent(LocalFrame& frame, const String& text) { |
| // 2. Fire 'compositionupdate' event |
| // 3. Fire TextEvent and modify DOM |
| // TODO(chongz): 4. Fire 'input' event |
| -void insertTextDuringCompositionWithEvents( |
| +void insertIncrementtalTextDuringCompositionWithEvents( |
| LocalFrame& frame, |
| - const String& text, |
| + const String& oldText, |
| + const String& newText, |
| TypingCommand::Options options, |
| TypingCommand::TextCompositionType compositionType) { |
| DCHECK(compositionType == |
| @@ -107,7 +108,8 @@ void insertTextDuringCompositionWithEvents( |
| ? InputEvent::EventCancelable::NotCancelable |
| : InputEvent::EventCancelable::IsCancelable; |
| DispatchEventResult result = dispatchBeforeInputFromComposition( |
| - target, InputEvent::InputType::InsertText, text, beforeInputCancelable); |
| + target, InputEvent::InputType::InsertText, newText, |
| + beforeInputCancelable); |
| if (beforeInputCancelable == InputEvent::EventCancelable::IsCancelable && |
| result != DispatchEventResult::NotCanceled) |
| @@ -117,22 +119,30 @@ void insertTextDuringCompositionWithEvents( |
| if (!frame.document()) |
| return; |
| - dispatchCompositionUpdateEvent(frame, text); |
| + dispatchCompositionUpdateEvent(frame, newText); |
| // 'compositionupdate' event handler may destroy document. |
| if (!frame.document()) |
| return; |
| switch (compositionType) { |
| case TypingCommand::TextCompositionType::TextCompositionUpdate: |
| - TypingCommand::insertText(*frame.document(), text, options, |
| - compositionType); |
| + TypingCommand::insertIncrementalText(*frame.document(), oldText, newText, |
| + options, compositionType); |
| break; |
| case TypingCommand::TextCompositionType::TextCompositionConfirm: |
| + // When there is previous composition, and call commitText() |
| + // (or setComposition()) with empty text, |newText| will be empty. In that |
| + // case, we should do nothing to avoid firing additional events. |
| + if (newText.length()) { |
| + TypingCommand::insertIncrementalText(*frame.document(), oldText, |
| + newText, options, compositionType); |
| + } |
| + break; |
| case TypingCommand::TextCompositionType::TextCompositionCancel: |
| // TODO(chongz): Use TypingCommand::insertText after TextEvent was |
| // removed. (Removed from spec since 2012) |
| // See TextEvent.idl. |
| - frame.eventHandler().handleTextInputEvent(text, 0, |
| + frame.eventHandler().handleTextInputEvent(newText, 0, |
| TextEventInputComposition); |
| break; |
| default: |
| @@ -171,7 +181,7 @@ InputMethodController* InputMethodController::create(LocalFrame& frame) { |
| } |
| InputMethodController::InputMethodController(LocalFrame& frame) |
| - : m_frame(&frame), m_isDirty(false), m_hasComposition(false) {} |
| + : m_frame(&frame), m_hasComposition(false) {} |
| InputMethodController::~InputMethodController() = default; |
| @@ -199,7 +209,6 @@ void InputMethodController::clear() { |
| m_compositionRange->collapse(true); |
| } |
| document().markers().removeMarkers(DocumentMarker::Composition); |
| - m_isDirty = false; |
| } |
| void InputMethodController::contextDestroyed() { |
| @@ -269,14 +278,6 @@ bool InputMethodController::replaceComposition(const String& text) { |
| if (!hasComposition()) |
| return false; |
| - // If the composition was set from existing text and didn't change, then |
| - // there's nothing to do here (and we should avoid doing anything as that |
| - // may clobber multi-node styled text). |
| - if (!m_isDirty && composingText() == text) { |
| - clear(); |
| - return true; |
| - } |
| - |
| // Select the text that will be deleted or replaced. |
| selectComposition(); |
| @@ -289,13 +290,19 @@ bool InputMethodController::replaceComposition(const String& text) { |
| // If text is empty, then delete the old composition here. If text is |
| // non-empty, InsertTextCommand::input will delete the old composition with |
| // an optimized replace operation. |
| - if (text.isEmpty()) |
| + if (text.isEmpty()) { |
| TypingCommand::deleteSelection(document(), 0); |
| + // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets |
| + // needs to be audited. see http://crbug.com/590369 for more details. |
| + document().updateStyleAndLayoutIgnorePendingStylesheets(); |
| + } |
| + |
| + const String& composing = composingText(); |
| clear(); |
| - insertTextDuringCompositionWithEvents( |
| - frame(), text, 0, |
| + insertIncrementtalTextDuringCompositionWithEvents( |
| + frame(), composing, text, 0, |
| TypingCommand::TextCompositionType::TextCompositionConfirm); |
| // Event handler might destroy document. |
| if (!isAvailable()) |
| @@ -379,8 +386,8 @@ void InputMethodController::cancelComposition() { |
| InputEvent::InputType::DeleteComposedCharacterBackward, nullAtom, |
| InputEvent::EventCancelable::NotCancelable); |
| dispatchCompositionUpdateEvent(frame(), emptyString()); |
| - insertTextDuringCompositionWithEvents( |
| - frame(), emptyString(), 0, |
| + insertIncrementtalTextDuringCompositionWithEvents( |
| + frame(), emptyString(), emptyString(), 0, |
| TypingCommand::TextCompositionType::TextCompositionCancel); |
| // Event handler might destroy document. |
| if (!isAvailable()) |
| @@ -410,28 +417,6 @@ void InputMethodController::cancelCompositionIfSelectionIsInvalid() { |
| frame().chromeClient().didCancelCompositionOnSelectionChange(); |
| } |
| -static size_t computeCommonPrefixLength(const String& str1, |
| - const String& str2) { |
| - const size_t maxCommonPrefixLength = std::min(str1.length(), str2.length()); |
| - for (size_t index = 0; index < maxCommonPrefixLength; ++index) { |
| - if (str1[index] != str2[index]) |
| - return index; |
| - } |
| - return maxCommonPrefixLength; |
| -} |
| - |
| -static size_t computeCommonSuffixLength(const String& str1, |
| - const String& str2) { |
| - const size_t length1 = str1.length(); |
| - const size_t length2 = str2.length(); |
| - const size_t maxCommonSuffixLength = std::min(length1, length2); |
| - for (size_t index = 0; index < maxCommonSuffixLength; ++index) { |
| - if (str1[length1 - index - 1] != str2[length2 - index - 1]) |
| - return index; |
| - } |
| - return maxCommonSuffixLength; |
| -} |
| - |
| // If current position is at grapheme boundary, return 0; otherwise, return the |
| // distance to its nearest left grapheme boundary. |
| static size_t computeDistanceToLeftGraphemeBoundary(const Position& position) { |
| @@ -445,23 +430,6 @@ static size_t computeDistanceToLeftGraphemeBoundary(const Position& position) { |
| adjustedPosition.computeOffsetInContainerNode()); |
| } |
| -static size_t computeCommonGraphemeClusterPrefixLengthForSetComposition( |
| - const String& oldText, |
| - const String& newText, |
| - const Element* rootEditableElement) { |
| - const size_t commonPrefixLength = computeCommonPrefixLength(oldText, newText); |
| - |
| - // For grapheme cluster, we should adjust it for grapheme boundary. |
| - const EphemeralRange& range = |
| - PlainTextRange(0, commonPrefixLength).createRange(*rootEditableElement); |
| - if (range.isNull()) |
| - return 0; |
| - const Position& position = range.endPosition(); |
| - const size_t diff = computeDistanceToLeftGraphemeBoundary(position); |
| - DCHECK_GE(commonPrefixLength, diff); |
| - return commonPrefixLength - diff; |
| -} |
| - |
| // If current position is at grapheme boundary, return 0; otherwise, return the |
| // distance to its nearest right grapheme boundary. |
| static size_t computeDistanceToRightGraphemeBoundary(const Position& position) { |
| @@ -475,108 +443,6 @@ static size_t computeDistanceToRightGraphemeBoundary(const Position& position) { |
| position.computeOffsetInContainerNode()); |
| } |
| -static size_t computeCommonGraphemeClusterSuffixLengthForSetComposition( |
| - const String& oldText, |
| - const String& newText, |
| - const Element* rootEditableElement) { |
| - const size_t commonSuffixLength = computeCommonSuffixLength(oldText, newText); |
| - |
| - // For grapheme cluster, 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 size_t diff = computeDistanceToRightGraphemeBoundary(position); |
| - DCHECK_GE(commonSuffixLength, diff); |
| - return commonSuffixLength - diff; |
| -} |
| - |
| -void InputMethodController::setCompositionWithIncrementalText( |
| - const String& text, |
| - const Vector<CompositionUnderline>& underlines, |
| - int selectionStart, |
| - int selectionEnd) { |
| - Element* editable = frame().selection().rootEditableElement(); |
| - if (!editable) |
| - return; |
| - |
| - DCHECK_LE(selectionStart, selectionEnd); |
| - String composing = composingText(); |
| - const size_t commonPrefixLength = |
| - computeCommonGraphemeClusterPrefixLengthForSetComposition(composing, text, |
| - editable); |
| - |
| - // We should ignore common prefix when finding common suffix. |
| - const size_t commonSuffixLength = |
| - computeCommonGraphemeClusterSuffixLengthForSetComposition( |
| - composing.right(composing.length() - commonPrefixLength), |
| - text.right(text.length() - commonPrefixLength), editable); |
| - |
| - const bool inserting = |
| - text.length() > commonPrefixLength + commonSuffixLength; |
| - const bool deleting = |
| - composing.length() > commonPrefixLength + commonSuffixLength; |
| - |
| - if (inserting || deleting) { |
| - // Select the text to be deleted. |
| - const size_t compositionStart = |
| - PlainTextRange::create(*editable, compositionEphemeralRange()).start(); |
| - const size_t deletionStart = compositionStart + commonPrefixLength; |
| - const size_t deletionEnd = |
| - compositionStart + composing.length() - commonSuffixLength; |
| - const EphemeralRange& deletionRange = |
| - PlainTextRange(deletionStart, deletionEnd).createRange(*editable); |
| - VisibleSelection selection; |
| - selection.setWithoutValidation(deletionRange.startPosition(), |
| - deletionRange.endPosition()); |
| - Document& currentDocument = document(); |
| - frame().selection().setSelection(selection, 0); |
| - clear(); |
| - |
| - // FrameSeleciton::setSelection() can change document associate to |frame|. |
| - if (!isAvailable() || currentDocument != document()) |
| - return; |
| - if (!currentDocument.focusedElement()) |
| - return; |
| - |
| - // Insert the incremental text. |
| - const size_t insertionLength = |
| - text.length() - commonPrefixLength - commonSuffixLength; |
| - const String& insertingText = |
| - text.substring(commonPrefixLength, insertionLength); |
| - insertTextDuringCompositionWithEvents(frame(), insertingText, |
| - TypingCommand::PreventSpellChecking, |
| - TypingCommand::TextCompositionUpdate); |
| - |
| - // Event handlers might destroy document. |
| - if (!isAvailable() || currentDocument != document()) |
| - return; |
| - |
| - // TODO(yosin): The use of updateStyleAndLayoutIgnorePendingStylesheets |
| - // needs to be audited. see http://crbug.com/590369 for more details. |
| - 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(); |
| - |
| - // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets |
| - // needs to be audited. see http://crbug.com/590369 for more details. |
| - document().updateStyleAndLayoutIgnorePendingStylesheets(); |
| - |
| - 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, |
| @@ -589,14 +455,6 @@ void InputMethodController::setComposition( |
| // See https://bugs.webkit.org/show_bug.cgi?id=46868 |
| 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()) |
| @@ -663,10 +521,11 @@ void InputMethodController::setComposition( |
| DCHECK(!text.isEmpty()); |
| + const String& composing = composingText(); |
| clear(); |
| - insertTextDuringCompositionWithEvents( |
| - frame(), text, |
| + insertIncrementtalTextDuringCompositionWithEvents( |
| + frame(), composing, text, |
| TypingCommand::SelectInsertedText | TypingCommand::PreventSpellChecking, |
| TypingCommand::TextCompositionUpdate); |
| // Event handlers might destroy document. |
| @@ -685,20 +544,15 @@ void InputMethodController::setComposition( |
| Position extent = frame().selection().extent(); |
| Node* extentNode = extent.anchorNode(); |
|
yabinh
2016/11/25 04:49:29
baseNode and extentNode could be different anchor
|
| - if (baseNode != extentNode) |
| - return; |
| unsigned extentOffset = extent.computeOffsetInContainerNode(); |
| unsigned baseOffset = base.computeOffsetInContainerNode(); |
|
yabinh
2016/11/25 04:49:29
Note that the offset is relative to its anchor nod
|
| - if (baseOffset + text.length() != extentOffset) |
| - return; |
| - m_isDirty = true; |
| m_hasComposition = true; |
| if (!m_compositionRange) |
| m_compositionRange = Range::create(document()); |
| m_compositionRange->setStart(baseNode, baseOffset); |
| - m_compositionRange->setEnd(baseNode, extentOffset); |
| + m_compositionRange->setEnd(extentNode, extentOffset); |
| if (baseNode->layoutObject()) |
| baseNode->layoutObject()->setShouldDoFullPaintInvalidation(); |