| 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 be4e5a0b6b0159918e0b43d2e0f01eeaf966235a..0647e195a117f40c611ed0c667811b0d80011ff9 100644
|
| --- a/third_party/WebKit/Source/core/editing/InputMethodController.cpp
|
| +++ b/third_party/WebKit/Source/core/editing/InputMethodController.cpp
|
| @@ -35,6 +35,8 @@
|
| #include "core/editing/Editor.h"
|
| #include "core/editing/commands/TypingCommand.h"
|
| #include "core/editing/markers/DocumentMarkerController.h"
|
| +#include "core/editing/state_machines/BackwardCodePointStateMachine.h"
|
| +#include "core/editing/state_machines/ForwardCodePointStateMachine.h"
|
| #include "core/events/CompositionEvent.h"
|
| #include "core/frame/LocalFrame.h"
|
| #include "core/html/HTMLInputElement.h"
|
| @@ -185,6 +187,75 @@ AtomicString getInputModeAttribute(Element* element) {
|
| return element->fastGetAttribute(HTMLNames::inputmodeAttr).lower();
|
| }
|
|
|
| +constexpr int invalidDeletionLength = -1;
|
| +constexpr bool isInvalidDeletionLength(const int length) {
|
| + return length == invalidDeletionLength;
|
| +}
|
| +
|
| +int calculateBeforeDeletionLengthsInCodePoints(
|
| + const String& text,
|
| + const int beforeLengthInCodePoints,
|
| + const int selectionStart) {
|
| + DCHECK_GE(beforeLengthInCodePoints, 0);
|
| + DCHECK_GE(selectionStart, 0);
|
| + DCHECK_LE(selectionStart, static_cast<int>(text.length()));
|
| +
|
| + const UChar* uText = text.characters16();
|
| + BackwardCodePointStateMachine backwardMachine;
|
| + int counter = beforeLengthInCodePoints;
|
| + int deletionStart = selectionStart;
|
| + while (counter > 0 && deletionStart > 0) {
|
| + const TextSegmentationMachineState state =
|
| + backwardMachine.feedPrecedingCodeUnit(uText[deletionStart - 1]);
|
| + // According to Android's InputConnection spec, we should do nothing if
|
| + // |text| has invalid surrogate pair in the deletion range.
|
| + if (state == TextSegmentationMachineState::Invalid)
|
| + return invalidDeletionLength;
|
| +
|
| + if (backwardMachine.atCodePointBoundary())
|
| + --counter;
|
| + --deletionStart;
|
| + }
|
| + if (!backwardMachine.atCodePointBoundary())
|
| + return invalidDeletionLength;
|
| +
|
| + const int offset = backwardMachine.getBoundaryOffset();
|
| + DCHECK_EQ(-offset, selectionStart - deletionStart);
|
| + return -offset;
|
| +}
|
| +
|
| +int calculateAfterDeletionLengthsInCodePoints(const String& text,
|
| + const int afterLengthInCodePoints,
|
| + const int selectionEnd) {
|
| + DCHECK_GE(afterLengthInCodePoints, 0);
|
| + DCHECK_GE(selectionEnd, 0);
|
| + const int length = text.length();
|
| + DCHECK_LE(selectionEnd, length);
|
| +
|
| + const UChar* uText = text.characters16();
|
| + ForwardCodePointStateMachine forwardMachine;
|
| + int counter = afterLengthInCodePoints;
|
| + int deletionEnd = selectionEnd;
|
| + while (counter > 0 && deletionEnd < length) {
|
| + const TextSegmentationMachineState state =
|
| + forwardMachine.feedFollowingCodeUnit(uText[deletionEnd]);
|
| + // According to Android's InputConnection spec, we should do nothing if
|
| + // |text| has invalid surrogate pair in the deletion range.
|
| + if (state == TextSegmentationMachineState::Invalid)
|
| + return invalidDeletionLength;
|
| +
|
| + if (forwardMachine.atCodePointBoundary())
|
| + --counter;
|
| + ++deletionEnd;
|
| + }
|
| + if (!forwardMachine.atCodePointBoundary())
|
| + return invalidDeletionLength;
|
| +
|
| + const int offset = forwardMachine.getBoundaryOffset();
|
| + DCHECK_EQ(offset, deletionEnd - selectionEnd);
|
| + return offset;
|
| +}
|
| +
|
| } // anonymous namespace
|
|
|
| InputMethodController* InputMethodController::create(LocalFrame& frame) {
|
| @@ -904,6 +975,47 @@ void InputMethodController::deleteSurroundingText(int before, int after) {
|
| setSelectionOffsets(PlainTextRange(selectionStart, selectionEnd));
|
| }
|
|
|
| +void InputMethodController::deleteSurroundingTextInCodePoints(int before,
|
| + int after) {
|
| + DCHECK_GE(before, 0);
|
| + DCHECK_GE(after, 0);
|
| + if (!editor().canEdit())
|
| + return;
|
| + const PlainTextRange selectionOffsets(getSelectionOffsets());
|
| + if (selectionOffsets.isNull())
|
| + return;
|
| + Element* const rootEditableElement =
|
| + frame().selection().rootEditableElementOrDocumentElement();
|
| + if (!rootEditableElement)
|
| + return;
|
| +
|
| + const TextIteratorBehavior& behavior =
|
| + TextIteratorBehavior::Builder()
|
| + .setEmitsObjectReplacementCharacter(true)
|
| + .build();
|
| + const String& text = plainText(
|
| + EphemeralRange::rangeOfContents(*rootEditableElement), behavior);
|
| +
|
| + // 8-bit characters are Latin-1 characters, so the deletion lengths are
|
| + // trivial.
|
| + if (text.is8Bit())
|
| + return deleteSurroundingText(before, after);
|
| +
|
| + const int selectionStart = static_cast<int>(selectionOffsets.start());
|
| + const int selectionEnd = static_cast<int>(selectionOffsets.end());
|
| +
|
| + const int beforeLength =
|
| + calculateBeforeDeletionLengthsInCodePoints(text, before, selectionStart);
|
| + if (isInvalidDeletionLength(beforeLength))
|
| + return;
|
| + const int afterLength =
|
| + calculateAfterDeletionLengthsInCodePoints(text, after, selectionEnd);
|
| + if (isInvalidDeletionLength(afterLength))
|
| + return;
|
| +
|
| + return deleteSurroundingText(beforeLength, afterLength);
|
| +}
|
| +
|
| WebTextInputInfo InputMethodController::textInputInfo() const {
|
| WebTextInputInfo info;
|
| if (!isAvailable())
|
|
|