Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(100)

Unified Diff: third_party/WebKit/Source/core/editing/InputMethodController.cpp

Issue 2530843003: Introduce InsertIncrementalTextCommand to respect existing style for composition (Closed)
Patch Set: Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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();

Powered by Google App Engine
This is Rietveld 408576698