Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright (C) 2006, 2007, 2008, 2011 Apple Inc. All rights reserved. | 2 * Copyright (C) 2006, 2007, 2008, 2011 Apple Inc. All rights reserved. |
| 3 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) | 3 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) |
| 4 * | 4 * |
| 5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
| 6 * modification, are permitted provided that the following conditions | 6 * modification, are permitted provided that the following conditions |
| 7 * are met: | 7 * are met: |
| 8 * 1. Redistributions of source code must retain the above copyright | 8 * 1. Redistributions of source code must retain the above copyright |
| 9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
| 10 * 2. Redistributions in binary form must reproduce the above copyright | 10 * 2. Redistributions in binary form must reproduce the above copyright |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 28 | 28 |
| 29 #include "core/InputModeNames.h" | 29 #include "core/InputModeNames.h" |
| 30 #include "core/InputTypeNames.h" | 30 #include "core/InputTypeNames.h" |
| 31 #include "core/dom/Document.h" | 31 #include "core/dom/Document.h" |
| 32 #include "core/dom/Element.h" | 32 #include "core/dom/Element.h" |
| 33 #include "core/dom/Text.h" | 33 #include "core/dom/Text.h" |
| 34 #include "core/editing/EditingUtilities.h" | 34 #include "core/editing/EditingUtilities.h" |
| 35 #include "core/editing/Editor.h" | 35 #include "core/editing/Editor.h" |
| 36 #include "core/editing/commands/TypingCommand.h" | 36 #include "core/editing/commands/TypingCommand.h" |
| 37 #include "core/editing/markers/DocumentMarkerController.h" | 37 #include "core/editing/markers/DocumentMarkerController.h" |
| 38 #include "core/editing/state_machines/BackwardCodePointStateMachine.h" | |
| 39 #include "core/editing/state_machines/ForwardCodePointStateMachine.h" | |
| 38 #include "core/events/CompositionEvent.h" | 40 #include "core/events/CompositionEvent.h" |
| 39 #include "core/frame/LocalFrame.h" | 41 #include "core/frame/LocalFrame.h" |
| 40 #include "core/html/HTMLInputElement.h" | 42 #include "core/html/HTMLInputElement.h" |
| 41 #include "core/html/HTMLTextAreaElement.h" | 43 #include "core/html/HTMLTextAreaElement.h" |
| 42 #include "core/input/EventHandler.h" | 44 #include "core/input/EventHandler.h" |
| 43 #include "core/layout/LayoutObject.h" | 45 #include "core/layout/LayoutObject.h" |
| 44 #include "core/layout/LayoutTheme.h" | 46 #include "core/layout/LayoutTheme.h" |
| 45 #include "core/page/ChromeClient.h" | 47 #include "core/page/ChromeClient.h" |
| 46 | 48 |
| 47 namespace blink { | 49 namespace blink { |
| (...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 178 } | 180 } |
| 179 | 181 |
| 180 if (!queryAttribute) | 182 if (!queryAttribute) |
| 181 return AtomicString(); | 183 return AtomicString(); |
| 182 | 184 |
| 183 // TODO(dtapuska): We may wish to restrict this to a yet to be proposed | 185 // TODO(dtapuska): We may wish to restrict this to a yet to be proposed |
| 184 // <contenteditable> or <richtext> element Mozilla discussed at TPAC 2016. | 186 // <contenteditable> or <richtext> element Mozilla discussed at TPAC 2016. |
| 185 return element->fastGetAttribute(HTMLNames::inputmodeAttr).lower(); | 187 return element->fastGetAttribute(HTMLNames::inputmodeAttr).lower(); |
| 186 } | 188 } |
| 187 | 189 |
| 190 constexpr int invalidDeletionLength = -1; | |
| 191 constexpr bool isInvalidDeletionLength(const int length) { | |
| 192 return length == invalidDeletionLength; | |
| 193 } | |
| 194 | |
| 195 int calculateBeforeDeletionLengthsInCodePoints( | |
| 196 const UChar* uText, | |
|
yosin_UTC9
2017/02/20 05:53:59
Do we know length of |uText|?
We should make sure
yabinh
2017/02/20 07:46:45
Done.
| |
| 197 const int beforeLengthInCodePoints, | |
|
yosin_UTC9
2017/02/20 05:53:59
Let's add DCHECK_GE(beforeLengthInCodePoints, 0)
yabinh
2017/02/20 07:46:45
Done.
| |
| 198 const int selectionStart) { | |
|
yosin_UTC9
2017/02/20 05:53:59
Let's add DCHECK_GE(selectionStart, 0)
yabinh
2017/02/20 07:46:45
Done.
| |
| 199 BackwardCodePointStateMachine backwardMachine; | |
| 200 int counter = beforeLengthInCodePoints; | |
| 201 int deletionStart = selectionStart; | |
| 202 while (counter > 0 && deletionStart > 0) { | |
| 203 const TextSegmentationMachineState state = | |
| 204 backwardMachine.feedPrecedingCodeUnit(uText[deletionStart - 1]); | |
| 205 // According to Android's InputConnection spec, we should do nothing if | |
| 206 // |text| has invalid surrogate pair in the deletion range. | |
| 207 if (state == TextSegmentationMachineState::Invalid) | |
| 208 return invalidDeletionLength; | |
| 209 | |
| 210 if (backwardMachine.atCodePointBoundary()) | |
| 211 --counter; | |
| 212 --deletionStart; | |
| 213 } | |
| 214 if (!backwardMachine.atCodePointBoundary()) | |
| 215 return invalidDeletionLength; | |
| 216 | |
| 217 const int offset = backwardMachine.getBoundaryOffset(); | |
| 218 DCHECK_EQ(-offset, selectionStart - deletionStart); | |
| 219 return -offset; | |
| 220 } | |
| 221 | |
| 222 int calculateAfterDeletionLengthsInCodePoints(const UChar* uText, | |
| 223 const int afterLengthInCodePoints, | |
|
yosin_UTC9
2017/02/20 05:53:59
Let's add DCHECK_GE( afterLengthInCodePoints, 0)
yabinh
2017/02/20 07:46:45
Done.
| |
| 224 const int selectionEnd, | |
|
yosin_UTC9
2017/02/20 05:53:59
Let's add DCHECK_GE(selectionEnd, 0)
yabinh
2017/02/20 07:46:45
Done.
| |
| 225 const int length) { | |
|
yosin_UTC9
2017/02/20 05:53:59
Let's add DCHECK_GE(length, 0) or DCHECK_GE(length
yabinh
2017/02/20 07:46:45
Done.
| |
| 226 ForwardCodePointStateMachine forwardMachine; | |
| 227 int counter = afterLengthInCodePoints; | |
| 228 int deletionEnd = selectionEnd; | |
| 229 while (counter > 0 && deletionEnd < length) { | |
| 230 const TextSegmentationMachineState state = | |
| 231 forwardMachine.feedFollowingCodeUnit(uText[deletionEnd]); | |
|
yosin_UTC9
2017/02/20 05:53:59
Do we know length of |uText|?
We should make sure
yabinh
2017/02/20 07:46:45
Done.
| |
| 232 // According to Android's InputConnection spec, we should do nothing if | |
| 233 // |text| has invalid surrogate pair in the deletion range. | |
| 234 if (state == TextSegmentationMachineState::Invalid) | |
| 235 return invalidDeletionLength; | |
| 236 | |
| 237 if (forwardMachine.atCodePointBoundary()) | |
| 238 --counter; | |
| 239 ++deletionEnd; | |
| 240 } | |
| 241 if (!forwardMachine.atCodePointBoundary()) | |
| 242 return invalidDeletionLength; | |
| 243 | |
| 244 const int offset = forwardMachine.getBoundaryOffset(); | |
| 245 DCHECK_EQ(offset, deletionEnd - selectionEnd); | |
| 246 return offset; | |
| 247 } | |
| 248 | |
| 188 } // anonymous namespace | 249 } // anonymous namespace |
| 189 | 250 |
| 190 InputMethodController* InputMethodController::create(LocalFrame& frame) { | 251 InputMethodController* InputMethodController::create(LocalFrame& frame) { |
| 191 return new InputMethodController(frame); | 252 return new InputMethodController(frame); |
| 192 } | 253 } |
| 193 | 254 |
| 194 InputMethodController::InputMethodController(LocalFrame& frame) | 255 InputMethodController::InputMethodController(LocalFrame& frame) |
| 195 : m_frame(&frame), m_hasComposition(false) {} | 256 : m_frame(&frame), m_hasComposition(false) {} |
| 196 | 257 |
| 197 InputMethodController::~InputMethodController() = default; | 258 InputMethodController::~InputMethodController() = default; |
| (...skipping 662 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 860 const size_t diff = computeDistanceToRightGraphemeBoundary(position); | 921 const size_t diff = computeDistanceToRightGraphemeBoundary(position); |
| 861 const int adjustedEnd = end + static_cast<int>(diff); | 922 const int adjustedEnd = end + static_cast<int>(diff); |
| 862 if (!setSelectionOffsets(PlainTextRange(selectionEnd, adjustedEnd))) | 923 if (!setSelectionOffsets(PlainTextRange(selectionEnd, adjustedEnd))) |
| 863 return; | 924 return; |
| 864 TypingCommand::deleteSelection(document()); | 925 TypingCommand::deleteSelection(document()); |
| 865 } | 926 } |
| 866 | 927 |
| 867 setSelectionOffsets(PlainTextRange(selectionStart, selectionEnd)); | 928 setSelectionOffsets(PlainTextRange(selectionStart, selectionEnd)); |
| 868 } | 929 } |
| 869 | 930 |
| 931 void InputMethodController::deleteSurroundingTextInCodePoints(int before, | |
| 932 int after) { | |
| 933 DCHECK_GE(before, 0); | |
| 934 DCHECK_GE(after, 0); | |
| 935 if (!editor().canEdit()) | |
| 936 return; | |
| 937 const PlainTextRange selectionOffsets(getSelectionOffsets()); | |
| 938 if (selectionOffsets.isNull()) | |
| 939 return; | |
| 940 Element* const rootEditableElement = | |
| 941 frame().selection().rootEditableElement(); | |
| 942 if (!rootEditableElement) | |
| 943 return; | |
| 944 | |
| 945 const TextIteratorBehavior& behavior = | |
| 946 TextIteratorBehavior::Builder() | |
| 947 .setEmitsObjectReplacementCharacter(true) | |
| 948 .build(); | |
| 949 const String& text = plainText( | |
| 950 EphemeralRange::rangeOfContents(*rootEditableElement), behavior); | |
| 951 | |
| 952 // UTF-8 is only used to encode Latin-1 characters, so the deletion lengths | |
| 953 // are trivial. | |
| 954 if (text.is8Bit()) | |
| 955 return deleteSurroundingText(before, after); | |
| 956 | |
| 957 const int selectionStart = static_cast<int>(selectionOffsets.start()); | |
| 958 const int selectionEnd = static_cast<int>(selectionOffsets.end()); | |
| 959 | |
| 960 const UChar* uText = text.characters16(); | |
| 961 const int beforeLength = | |
| 962 calculateBeforeDeletionLengthsInCodePoints(uText, before, selectionStart); | |
|
yosin_UTC9
2017/02/20 05:53:59
It seems it is better to pass |text| for bounds ch
yabinh
2017/02/20 06:28:23
The length of |uText| == the length of |text|.
For
| |
| 963 if (isInvalidDeletionLength(beforeLength)) | |
| 964 return; | |
| 965 const int afterLength = calculateAfterDeletionLengthsInCodePoints( | |
| 966 uText, after, selectionEnd, text.length()); | |
| 967 if (isInvalidDeletionLength(afterLength)) | |
| 968 return; | |
| 969 | |
| 970 return deleteSurroundingText(beforeLength, afterLength); | |
| 971 } | |
| 972 | |
| 870 WebTextInputInfo InputMethodController::textInputInfo() const { | 973 WebTextInputInfo InputMethodController::textInputInfo() const { |
| 871 WebTextInputInfo info; | 974 WebTextInputInfo info; |
| 872 if (!isAvailable()) | 975 if (!isAvailable()) |
| 873 return info; | 976 return info; |
| 874 | 977 |
| 875 if (!frame().selection().isAvailable()) { | 978 if (!frame().selection().isAvailable()) { |
| 876 // plugins/mouse-capture-inside-shadow.html reaches here. | 979 // plugins/mouse-capture-inside-shadow.html reaches here. |
| 877 return info; | 980 return info; |
| 878 } | 981 } |
| 879 Element* element = frame().selection().rootEditableElement(); | 982 Element* element = frame().selection().rootEditableElement(); |
| (...skipping 202 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1082 finishComposingText(DoNotKeepSelection); | 1185 finishComposingText(DoNotKeepSelection); |
| 1083 } | 1186 } |
| 1084 | 1187 |
| 1085 DEFINE_TRACE(InputMethodController) { | 1188 DEFINE_TRACE(InputMethodController) { |
| 1086 visitor->trace(m_frame); | 1189 visitor->trace(m_frame); |
| 1087 visitor->trace(m_compositionRange); | 1190 visitor->trace(m_compositionRange); |
| 1088 SynchronousMutationObserver::trace(visitor); | 1191 SynchronousMutationObserver::trace(visitor); |
| 1089 } | 1192 } |
| 1090 | 1193 |
| 1091 } // namespace blink | 1194 } // namespace blink |
| OLD | NEW |