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

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

Issue 2372493002: Workaround for setComposition styling clobber (Closed)
Patch Set: Address yosin@'s review Created 4 years, 2 months 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 f8628dc7e842b61d332d1ad4b0a6870b9b25a253..30b6a09b5a296d2a92fa9be4c9928f23f4d21c85 100644
--- a/third_party/WebKit/Source/core/editing/InputMethodController.cpp
+++ b/third_party/WebKit/Source/core/editing/InputMethodController.cpp
@@ -357,6 +357,154 @@ 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;
+ }
+
yosin_UTC9 2016/10/12 06:35:37 nit: Please remove an extra blank line.
yabinh 2016/10/12 07:32:32 Done.
+ 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;
+ }
+
yosin_UTC9 2016/10/12 06:35:38 nit: Please remove an extra blank line.
yabinh 2016/10/12 07:32:32 Done.
+ return maxCommonSuffixLength;
+}
+
+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 Position& adjustedPosition = previousPositionOf(
+ nextPositionOf(position, PositionMoveType::GraphemeCluster),
+ PositionMoveType::GraphemeCluster);
+ DCHECK_EQ(position.anchorNode(), adjustedPosition.anchorNode());
+ const int diff = position.computeOffsetInContainerNode() -
yosin_UTC9 2016/10/12 06:35:37 Let's do DCHECK_GE(position.computeOffsetInContain
yabinh 2016/10/12 07:32:32 Done.
+ adjustedPosition.computeOffsetInContainerNode();
+ DCHECK_GE(diff, 0);
+ return commonPrefixLength - static_cast<size_t>(diff);
+}
+
+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 Position& adjustedPosition = nextPositionOf(
+ previousPositionOf(position, PositionMoveType::GraphemeCluster),
+ PositionMoveType::GraphemeCluster);
+ DCHECK_EQ(position.anchorNode(), adjustedPosition.anchorNode());
+ const int diff = adjustedPosition.computeOffsetInContainerNode() -
+ position.computeOffsetInContainerNode();
+ DCHECK_GE(diff, 0);
yosin_UTC9 2016/10/12 06:35:37 Ditto as computeCommonGraphemeClusterPrefixLengthF
yabinh 2016/10/12 07:32:33 Done.
+ return commonSuffixLength - static_cast<size_t>(diff);
+}
+
+void InputMethodController::setCompositionWithIncrementalText(
+ const String& text,
+ const Vector<CompositionUnderline>& underlines,
+ int selectionStart,
+ int selectionEnd) {
+ Element* editable = frame().selection().rootEditableElement();
+ if (!editable)
+ return;
+
+ 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 appending =
yosin_UTC9 2016/10/12 06:35:38 s/appending/inserting/
yabinh 2016/10/12 07:32:32 Done.
+ text.length() > commonPrefixLength + commonSuffixLength;
+ const bool subtracting =
yosin_UTC9 2016/10/12 06:35:37 s/subtracting/deleting/
yabinh 2016/10/12 07:32:32 Done.
+ composing.length() > commonPrefixLength + +commonSuffixLength;
yosin_UTC9 2016/10/12 06:35:38 nit: Remove |+| before |comonSuffixLength|.
yabinh 2016/10/12 07:32:32 Done.
+
+ if (appending || subtracting) {
+ // Select the text to be subtracted.
yosin_UTC9 2016/10/12 06:35:38 s/subtracted/deleted/
yabinh 2016/10/12 07:32:32 Done.
+ const size_t compositionStart =
+ PlainTextRange::create(*editable, compositionEphemeralRange()).start();
+ const size_t subtractionStart = compositionStart + commonPrefixLength;
+ const size_t subtractionEnd =
+ compositionStart + composing.length() - commonSuffixLength;
+ const EphemeralRange& subtractionRange =
+ PlainTextRange(subtractionStart, subtractionEnd).createRange(*editable);
+ VisibleSelection selection;
+ selection.setWithoutValidation(subtractionRange.startPosition(),
+ subtractionRange.endPosition());
+ const Document* currentDocument = frame().document();
yosin_UTC9 2016/10/12 06:35:37 s/const Document*/Document* const/
yabinh 2016/10/12 07:32:32 Done.
+ frame().selection().setSelection(selection, 0);
+ clear();
+
+ // FrameSeleciton::setSelection() can change document associate to |frame|.
+ if (*currentDocument != *frame().document())
yosin_UTC9 2016/10/12 06:35:38 Remove |*|, we don't need to de-reference for comp
yabinh 2016/10/12 07:32:32 Done.
+ return;
+ if (!currentDocument->focusedElement())
+ return;
+
+ // Append the incremental text.
+ const size_t appendingLength =
+ text.length() - commonPrefixLength - commonSuffixLength;
+ String appendingText = text.substring(commonPrefixLength, appendingLength);
yosin_UTC9 2016/10/12 06:35:37 s/String/const String&/
yabinh 2016/10/12 07:32:33 Done.
+ insertTextDuringCompositionWithEvents(frame(), appendingText,
+ TypingCommand::PreventSpellChecking,
+ TypingCommand::TextCompositionUpdate);
+
+ // Event handlers might destroy document.
+ if (!frame().document())
yosin_UTC9 2016/10/12 06:35:37 No need to checking null document. |currentDocumen
yabinh 2016/10/12 07:32:32 Done.
+ return;
+ if (*currentDocument != *frame().document())
+ 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 +517,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 +534,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 +652,21 @@ 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();
yosin_UTC9 2016/10/12 06:35:37 This code fragment should be in callers rather tha
yabinh 2016/10/12 07:32:33 Done.
+
+ const int selectionOffsetsStart =
+ static_cast<int>(getSelectionOffsets().start());
+ int start = selectionOffsetsStart + selectionStart;
yosin_UTC9 2016/10/12 06:35:37 s/int/const int/
yabinh 2016/10/12 07:32:32 Done.
+ int end = selectionOffsetsStart + selectionEnd;
yosin_UTC9 2016/10/12 06:35:37 s/int/const int/
yabinh 2016/10/12 07:32:32 Done.
+ return createRangeForSelection(start, end, textLength);
+}
+
void InputMethodController::setCompositionFromExistingText(
const Vector<CompositionUnderline>& underlines,
unsigned compositionStart,

Powered by Google App Engine
This is Rietveld 408576698