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 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
69 } | 69 } |
70 | 70 |
71 // Used to insert/replace text during composition update and confirm | 71 // Used to insert/replace text during composition update and confirm |
72 // composition. | 72 // composition. |
73 // Procedure: | 73 // Procedure: |
74 // 1. Fire 'beforeinput' event for (TODO(chongz): deleted composed text) and | 74 // 1. Fire 'beforeinput' event for (TODO(chongz): deleted composed text) and |
75 // inserted text | 75 // inserted text |
76 // 2. Fire 'compositionupdate' event | 76 // 2. Fire 'compositionupdate' event |
77 // 3. Fire TextEvent and modify DOM | 77 // 3. Fire TextEvent and modify DOM |
78 // TODO(chongz): 4. Fire 'input' event | 78 // TODO(chongz): 4. Fire 'input' event |
79 void insertTextDuringCompositionWithEvents( | 79 void insertIncrementtalTextDuringCompositionWithEvents( |
80 LocalFrame& frame, | 80 LocalFrame& frame, |
81 const String& text, | 81 const String& oldText, |
82 const String& newText, | |
82 TypingCommand::Options options, | 83 TypingCommand::Options options, |
83 TypingCommand::TextCompositionType compositionType) { | 84 TypingCommand::TextCompositionType compositionType) { |
84 DCHECK(compositionType == | 85 DCHECK(compositionType == |
85 TypingCommand::TextCompositionType::TextCompositionUpdate || | 86 TypingCommand::TextCompositionType::TextCompositionUpdate || |
86 compositionType == | 87 compositionType == |
87 TypingCommand::TextCompositionType::TextCompositionConfirm || | 88 TypingCommand::TextCompositionType::TextCompositionConfirm || |
88 compositionType == | 89 compositionType == |
89 TypingCommand::TextCompositionType::TextCompositionCancel) | 90 TypingCommand::TextCompositionType::TextCompositionCancel) |
90 << "compositionType should be TextCompositionUpdate or " | 91 << "compositionType should be TextCompositionUpdate or " |
91 "TextCompositionConfirm or TextCompositionCancel, but got " | 92 "TextCompositionConfirm or TextCompositionCancel, but got " |
92 << static_cast<int>(compositionType); | 93 << static_cast<int>(compositionType); |
93 if (!frame.document()) | 94 if (!frame.document()) |
94 return; | 95 return; |
95 | 96 |
96 Element* target = frame.document()->focusedElement(); | 97 Element* target = frame.document()->focusedElement(); |
97 if (!target) | 98 if (!target) |
98 return; | 99 return; |
99 | 100 |
100 // TODO(chongz): Fire 'beforeinput' for the composed text being | 101 // TODO(chongz): Fire 'beforeinput' for the composed text being |
101 // replaced/deleted. | 102 // replaced/deleted. |
102 | 103 |
103 // Only the last confirmed text is cancelable. | 104 // Only the last confirmed text is cancelable. |
104 InputEvent::EventCancelable beforeInputCancelable = | 105 InputEvent::EventCancelable beforeInputCancelable = |
105 (compositionType == | 106 (compositionType == |
106 TypingCommand::TextCompositionType::TextCompositionUpdate) | 107 TypingCommand::TextCompositionType::TextCompositionUpdate) |
107 ? InputEvent::EventCancelable::NotCancelable | 108 ? InputEvent::EventCancelable::NotCancelable |
108 : InputEvent::EventCancelable::IsCancelable; | 109 : InputEvent::EventCancelable::IsCancelable; |
109 DispatchEventResult result = dispatchBeforeInputFromComposition( | 110 DispatchEventResult result = dispatchBeforeInputFromComposition( |
110 target, InputEvent::InputType::InsertText, text, beforeInputCancelable); | 111 target, InputEvent::InputType::InsertText, newText, |
112 beforeInputCancelable); | |
111 | 113 |
112 if (beforeInputCancelable == InputEvent::EventCancelable::IsCancelable && | 114 if (beforeInputCancelable == InputEvent::EventCancelable::IsCancelable && |
113 result != DispatchEventResult::NotCanceled) | 115 result != DispatchEventResult::NotCanceled) |
114 return; | 116 return; |
115 | 117 |
116 // 'beforeinput' event handler may destroy document. | 118 // 'beforeinput' event handler may destroy document. |
117 if (!frame.document()) | 119 if (!frame.document()) |
118 return; | 120 return; |
119 | 121 |
120 dispatchCompositionUpdateEvent(frame, text); | 122 dispatchCompositionUpdateEvent(frame, newText); |
121 // 'compositionupdate' event handler may destroy document. | 123 // 'compositionupdate' event handler may destroy document. |
122 if (!frame.document()) | 124 if (!frame.document()) |
123 return; | 125 return; |
124 | 126 |
125 switch (compositionType) { | 127 switch (compositionType) { |
126 case TypingCommand::TextCompositionType::TextCompositionUpdate: | 128 case TypingCommand::TextCompositionType::TextCompositionUpdate: |
127 TypingCommand::insertText(*frame.document(), text, options, | 129 TypingCommand::insertIncrementalText(*frame.document(), oldText, newText, |
128 compositionType); | 130 options, compositionType); |
129 break; | 131 break; |
130 case TypingCommand::TextCompositionType::TextCompositionConfirm: | 132 case TypingCommand::TextCompositionType::TextCompositionConfirm: |
133 // When there is previous composition, and call commitText() | |
134 // (or setComposition()) with empty text, |newText| will be empty. In that | |
135 // case, we should do nothing to avoid firing additional events. | |
136 if (newText.length()) { | |
137 TypingCommand::insertIncrementalText(*frame.document(), oldText, | |
138 newText, options, compositionType); | |
139 } | |
140 break; | |
131 case TypingCommand::TextCompositionType::TextCompositionCancel: | 141 case TypingCommand::TextCompositionType::TextCompositionCancel: |
132 // TODO(chongz): Use TypingCommand::insertText after TextEvent was | 142 // TODO(chongz): Use TypingCommand::insertText after TextEvent was |
133 // removed. (Removed from spec since 2012) | 143 // removed. (Removed from spec since 2012) |
134 // See TextEvent.idl. | 144 // See TextEvent.idl. |
135 frame.eventHandler().handleTextInputEvent(text, 0, | 145 frame.eventHandler().handleTextInputEvent(newText, 0, |
136 TextEventInputComposition); | 146 TextEventInputComposition); |
137 break; | 147 break; |
138 default: | 148 default: |
139 NOTREACHED(); | 149 NOTREACHED(); |
140 } | 150 } |
141 // TODO(chongz): Fire 'input' event. | 151 // TODO(chongz): Fire 'input' event. |
142 } | 152 } |
143 | 153 |
144 AtomicString getInputModeAttribute(Element* element) { | 154 AtomicString getInputModeAttribute(Element* element) { |
145 if (!element) | 155 if (!element) |
(...skipping 18 matching lines...) Expand all Loading... | |
164 return element->fastGetAttribute(HTMLNames::inputmodeAttr).lower(); | 174 return element->fastGetAttribute(HTMLNames::inputmodeAttr).lower(); |
165 } | 175 } |
166 | 176 |
167 } // anonymous namespace | 177 } // anonymous namespace |
168 | 178 |
169 InputMethodController* InputMethodController::create(LocalFrame& frame) { | 179 InputMethodController* InputMethodController::create(LocalFrame& frame) { |
170 return new InputMethodController(frame); | 180 return new InputMethodController(frame); |
171 } | 181 } |
172 | 182 |
173 InputMethodController::InputMethodController(LocalFrame& frame) | 183 InputMethodController::InputMethodController(LocalFrame& frame) |
174 : m_frame(&frame), m_isDirty(false), m_hasComposition(false) {} | 184 : m_frame(&frame), m_hasComposition(false) {} |
175 | 185 |
176 InputMethodController::~InputMethodController() = default; | 186 InputMethodController::~InputMethodController() = default; |
177 | 187 |
178 bool InputMethodController::isAvailable() const { | 188 bool InputMethodController::isAvailable() const { |
179 return frame().document(); | 189 return frame().document(); |
180 } | 190 } |
181 | 191 |
182 Document& InputMethodController::document() const { | 192 Document& InputMethodController::document() const { |
183 DCHECK(isAvailable()); | 193 DCHECK(isAvailable()); |
184 return *frame().document(); | 194 return *frame().document(); |
185 } | 195 } |
186 | 196 |
187 bool InputMethodController::hasComposition() const { | 197 bool InputMethodController::hasComposition() const { |
188 return m_hasComposition; | 198 return m_hasComposition; |
189 } | 199 } |
190 | 200 |
191 inline Editor& InputMethodController::editor() const { | 201 inline Editor& InputMethodController::editor() const { |
192 return frame().editor(); | 202 return frame().editor(); |
193 } | 203 } |
194 | 204 |
195 void InputMethodController::clear() { | 205 void InputMethodController::clear() { |
196 m_hasComposition = false; | 206 m_hasComposition = false; |
197 if (m_compositionRange) { | 207 if (m_compositionRange) { |
198 m_compositionRange->setStart(&document(), 0); | 208 m_compositionRange->setStart(&document(), 0); |
199 m_compositionRange->collapse(true); | 209 m_compositionRange->collapse(true); |
200 } | 210 } |
201 document().markers().removeMarkers(DocumentMarker::Composition); | 211 document().markers().removeMarkers(DocumentMarker::Composition); |
202 m_isDirty = false; | |
203 } | 212 } |
204 | 213 |
205 void InputMethodController::contextDestroyed() { | 214 void InputMethodController::contextDestroyed() { |
206 clear(); | 215 clear(); |
207 m_compositionRange = nullptr; | 216 m_compositionRange = nullptr; |
208 } | 217 } |
209 | 218 |
210 void InputMethodController::documentAttached(Document* document) { | 219 void InputMethodController::documentAttached(Document* document) { |
211 DCHECK(document); | 220 DCHECK(document); |
212 setContext(document); | 221 setContext(document); |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
262 // duplicate selection change event. | 271 // duplicate selection change event. |
263 if (!text.length() && !relativeCaretPosition) | 272 if (!text.length() && !relativeCaretPosition) |
264 return false; | 273 return false; |
265 return insertTextAndMoveCaret(text, relativeCaretPosition); | 274 return insertTextAndMoveCaret(text, relativeCaretPosition); |
266 } | 275 } |
267 | 276 |
268 bool InputMethodController::replaceComposition(const String& text) { | 277 bool InputMethodController::replaceComposition(const String& text) { |
269 if (!hasComposition()) | 278 if (!hasComposition()) |
270 return false; | 279 return false; |
271 | 280 |
272 // If the composition was set from existing text and didn't change, then | |
273 // there's nothing to do here (and we should avoid doing anything as that | |
274 // may clobber multi-node styled text). | |
275 if (!m_isDirty && composingText() == text) { | |
276 clear(); | |
277 return true; | |
278 } | |
279 | |
280 // Select the text that will be deleted or replaced. | 281 // Select the text that will be deleted or replaced. |
281 selectComposition(); | 282 selectComposition(); |
282 | 283 |
283 if (frame().selection().isNone()) | 284 if (frame().selection().isNone()) |
284 return false; | 285 return false; |
285 | 286 |
286 if (!isAvailable()) | 287 if (!isAvailable()) |
287 return false; | 288 return false; |
288 | 289 |
289 // If text is empty, then delete the old composition here. If text is | 290 // If text is empty, then delete the old composition here. If text is |
290 // non-empty, InsertTextCommand::input will delete the old composition with | 291 // non-empty, InsertTextCommand::input will delete the old composition with |
291 // an optimized replace operation. | 292 // an optimized replace operation. |
292 if (text.isEmpty()) | 293 if (text.isEmpty()) { |
293 TypingCommand::deleteSelection(document(), 0); | 294 TypingCommand::deleteSelection(document(), 0); |
294 | 295 |
296 // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets | |
297 // needs to be audited. see http://crbug.com/590369 for more details. | |
298 document().updateStyleAndLayoutIgnorePendingStylesheets(); | |
299 } | |
300 | |
301 const String& composing = composingText(); | |
295 clear(); | 302 clear(); |
296 | 303 |
297 insertTextDuringCompositionWithEvents( | 304 insertIncrementtalTextDuringCompositionWithEvents( |
298 frame(), text, 0, | 305 frame(), composing, text, 0, |
299 TypingCommand::TextCompositionType::TextCompositionConfirm); | 306 TypingCommand::TextCompositionType::TextCompositionConfirm); |
300 // Event handler might destroy document. | 307 // Event handler might destroy document. |
301 if (!isAvailable()) | 308 if (!isAvailable()) |
302 return false; | 309 return false; |
303 | 310 |
304 return true; | 311 return true; |
305 } | 312 } |
306 | 313 |
307 // relativeCaretPosition is relative to the end of the text. | 314 // relativeCaretPosition is relative to the end of the text. |
308 static int computeAbsoluteCaretPosition(size_t textStart, | 315 static int computeAbsoluteCaretPosition(size_t textStart, |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
372 return; | 379 return; |
373 | 380 |
374 clear(); | 381 clear(); |
375 | 382 |
376 // TODO(chongz): Figure out which InputType should we use here. | 383 // TODO(chongz): Figure out which InputType should we use here. |
377 dispatchBeforeInputFromComposition( | 384 dispatchBeforeInputFromComposition( |
378 document().focusedElement(), | 385 document().focusedElement(), |
379 InputEvent::InputType::DeleteComposedCharacterBackward, nullAtom, | 386 InputEvent::InputType::DeleteComposedCharacterBackward, nullAtom, |
380 InputEvent::EventCancelable::NotCancelable); | 387 InputEvent::EventCancelable::NotCancelable); |
381 dispatchCompositionUpdateEvent(frame(), emptyString()); | 388 dispatchCompositionUpdateEvent(frame(), emptyString()); |
382 insertTextDuringCompositionWithEvents( | 389 insertIncrementtalTextDuringCompositionWithEvents( |
383 frame(), emptyString(), 0, | 390 frame(), emptyString(), emptyString(), 0, |
384 TypingCommand::TextCompositionType::TextCompositionCancel); | 391 TypingCommand::TextCompositionType::TextCompositionCancel); |
385 // Event handler might destroy document. | 392 // Event handler might destroy document. |
386 if (!isAvailable()) | 393 if (!isAvailable()) |
387 return; | 394 return; |
388 | 395 |
389 // An open typing command that disagrees about current selection would cause | 396 // An open typing command that disagrees about current selection would cause |
390 // issues with typing later on. | 397 // issues with typing later on. |
391 TypingCommand::closeTyping(m_frame); | 398 TypingCommand::closeTyping(m_frame); |
392 | 399 |
393 // No DOM update after 'compositionend'. | 400 // No DOM update after 'compositionend'. |
394 dispatchCompositionEndEvent(frame(), emptyString()); | 401 dispatchCompositionEndEvent(frame(), emptyString()); |
395 } | 402 } |
396 | 403 |
397 void InputMethodController::cancelCompositionIfSelectionIsInvalid() { | 404 void InputMethodController::cancelCompositionIfSelectionIsInvalid() { |
398 if (!hasComposition() || editor().preventRevealSelection()) | 405 if (!hasComposition() || editor().preventRevealSelection()) |
399 return; | 406 return; |
400 | 407 |
401 // Check if selection start and selection end are valid. | 408 // Check if selection start and selection end are valid. |
402 FrameSelection& selection = frame().selection(); | 409 FrameSelection& selection = frame().selection(); |
403 if (!selection.isNone() && !m_compositionRange->collapsed()) { | 410 if (!selection.isNone() && !m_compositionRange->collapsed()) { |
404 if (selection.start().compareTo(m_compositionRange->startPosition()) >= 0 && | 411 if (selection.start().compareTo(m_compositionRange->startPosition()) >= 0 && |
405 selection.end().compareTo(m_compositionRange->endPosition()) <= 0) | 412 selection.end().compareTo(m_compositionRange->endPosition()) <= 0) |
406 return; | 413 return; |
407 } | 414 } |
408 | 415 |
409 cancelComposition(); | 416 cancelComposition(); |
410 frame().chromeClient().didCancelCompositionOnSelectionChange(); | 417 frame().chromeClient().didCancelCompositionOnSelectionChange(); |
411 } | 418 } |
412 | 419 |
413 static size_t computeCommonPrefixLength(const String& str1, | |
414 const String& str2) { | |
415 const size_t maxCommonPrefixLength = std::min(str1.length(), str2.length()); | |
416 for (size_t index = 0; index < maxCommonPrefixLength; ++index) { | |
417 if (str1[index] != str2[index]) | |
418 return index; | |
419 } | |
420 return maxCommonPrefixLength; | |
421 } | |
422 | |
423 static size_t computeCommonSuffixLength(const String& str1, | |
424 const String& str2) { | |
425 const size_t length1 = str1.length(); | |
426 const size_t length2 = str2.length(); | |
427 const size_t maxCommonSuffixLength = std::min(length1, length2); | |
428 for (size_t index = 0; index < maxCommonSuffixLength; ++index) { | |
429 if (str1[length1 - index - 1] != str2[length2 - index - 1]) | |
430 return index; | |
431 } | |
432 return maxCommonSuffixLength; | |
433 } | |
434 | |
435 // If current position is at grapheme boundary, return 0; otherwise, return the | 420 // If current position is at grapheme boundary, return 0; otherwise, return the |
436 // distance to its nearest left grapheme boundary. | 421 // distance to its nearest left grapheme boundary. |
437 static size_t computeDistanceToLeftGraphemeBoundary(const Position& position) { | 422 static size_t computeDistanceToLeftGraphemeBoundary(const Position& position) { |
438 const Position& adjustedPosition = previousPositionOf( | 423 const Position& adjustedPosition = previousPositionOf( |
439 nextPositionOf(position, PositionMoveType::GraphemeCluster), | 424 nextPositionOf(position, PositionMoveType::GraphemeCluster), |
440 PositionMoveType::GraphemeCluster); | 425 PositionMoveType::GraphemeCluster); |
441 DCHECK_EQ(position.anchorNode(), adjustedPosition.anchorNode()); | 426 DCHECK_EQ(position.anchorNode(), adjustedPosition.anchorNode()); |
442 DCHECK_GE(position.computeOffsetInContainerNode(), | 427 DCHECK_GE(position.computeOffsetInContainerNode(), |
443 adjustedPosition.computeOffsetInContainerNode()); | 428 adjustedPosition.computeOffsetInContainerNode()); |
444 return static_cast<size_t>(position.computeOffsetInContainerNode() - | 429 return static_cast<size_t>(position.computeOffsetInContainerNode() - |
445 adjustedPosition.computeOffsetInContainerNode()); | 430 adjustedPosition.computeOffsetInContainerNode()); |
446 } | 431 } |
447 | 432 |
448 static size_t computeCommonGraphemeClusterPrefixLengthForSetComposition( | |
449 const String& oldText, | |
450 const String& newText, | |
451 const Element* rootEditableElement) { | |
452 const size_t commonPrefixLength = computeCommonPrefixLength(oldText, newText); | |
453 | |
454 // For grapheme cluster, we should adjust it for grapheme boundary. | |
455 const EphemeralRange& range = | |
456 PlainTextRange(0, commonPrefixLength).createRange(*rootEditableElement); | |
457 if (range.isNull()) | |
458 return 0; | |
459 const Position& position = range.endPosition(); | |
460 const size_t diff = computeDistanceToLeftGraphemeBoundary(position); | |
461 DCHECK_GE(commonPrefixLength, diff); | |
462 return commonPrefixLength - diff; | |
463 } | |
464 | |
465 // If current position is at grapheme boundary, return 0; otherwise, return the | 433 // If current position is at grapheme boundary, return 0; otherwise, return the |
466 // distance to its nearest right grapheme boundary. | 434 // distance to its nearest right grapheme boundary. |
467 static size_t computeDistanceToRightGraphemeBoundary(const Position& position) { | 435 static size_t computeDistanceToRightGraphemeBoundary(const Position& position) { |
468 const Position& adjustedPosition = nextPositionOf( | 436 const Position& adjustedPosition = nextPositionOf( |
469 previousPositionOf(position, PositionMoveType::GraphemeCluster), | 437 previousPositionOf(position, PositionMoveType::GraphemeCluster), |
470 PositionMoveType::GraphemeCluster); | 438 PositionMoveType::GraphemeCluster); |
471 DCHECK_EQ(position.anchorNode(), adjustedPosition.anchorNode()); | 439 DCHECK_EQ(position.anchorNode(), adjustedPosition.anchorNode()); |
472 DCHECK_GE(adjustedPosition.computeOffsetInContainerNode(), | 440 DCHECK_GE(adjustedPosition.computeOffsetInContainerNode(), |
473 position.computeOffsetInContainerNode()); | 441 position.computeOffsetInContainerNode()); |
474 return static_cast<size_t>(adjustedPosition.computeOffsetInContainerNode() - | 442 return static_cast<size_t>(adjustedPosition.computeOffsetInContainerNode() - |
475 position.computeOffsetInContainerNode()); | 443 position.computeOffsetInContainerNode()); |
476 } | 444 } |
477 | 445 |
478 static size_t computeCommonGraphemeClusterSuffixLengthForSetComposition( | |
479 const String& oldText, | |
480 const String& newText, | |
481 const Element* rootEditableElement) { | |
482 const size_t commonSuffixLength = computeCommonSuffixLength(oldText, newText); | |
483 | |
484 // For grapheme cluster, we should adjust it for grapheme boundary. | |
485 const EphemeralRange& range = | |
486 PlainTextRange(0, oldText.length() - commonSuffixLength) | |
487 .createRange(*rootEditableElement); | |
488 if (range.isNull()) | |
489 return 0; | |
490 const Position& position = range.endPosition(); | |
491 const size_t diff = computeDistanceToRightGraphemeBoundary(position); | |
492 DCHECK_GE(commonSuffixLength, diff); | |
493 return commonSuffixLength - diff; | |
494 } | |
495 | |
496 void InputMethodController::setCompositionWithIncrementalText( | |
497 const String& text, | |
498 const Vector<CompositionUnderline>& underlines, | |
499 int selectionStart, | |
500 int selectionEnd) { | |
501 Element* editable = frame().selection().rootEditableElement(); | |
502 if (!editable) | |
503 return; | |
504 | |
505 DCHECK_LE(selectionStart, selectionEnd); | |
506 String composing = composingText(); | |
507 const size_t commonPrefixLength = | |
508 computeCommonGraphemeClusterPrefixLengthForSetComposition(composing, text, | |
509 editable); | |
510 | |
511 // We should ignore common prefix when finding common suffix. | |
512 const size_t commonSuffixLength = | |
513 computeCommonGraphemeClusterSuffixLengthForSetComposition( | |
514 composing.right(composing.length() - commonPrefixLength), | |
515 text.right(text.length() - commonPrefixLength), editable); | |
516 | |
517 const bool inserting = | |
518 text.length() > commonPrefixLength + commonSuffixLength; | |
519 const bool deleting = | |
520 composing.length() > commonPrefixLength + commonSuffixLength; | |
521 | |
522 if (inserting || deleting) { | |
523 // Select the text to be deleted. | |
524 const size_t compositionStart = | |
525 PlainTextRange::create(*editable, compositionEphemeralRange()).start(); | |
526 const size_t deletionStart = compositionStart + commonPrefixLength; | |
527 const size_t deletionEnd = | |
528 compositionStart + composing.length() - commonSuffixLength; | |
529 const EphemeralRange& deletionRange = | |
530 PlainTextRange(deletionStart, deletionEnd).createRange(*editable); | |
531 VisibleSelection selection; | |
532 selection.setWithoutValidation(deletionRange.startPosition(), | |
533 deletionRange.endPosition()); | |
534 Document& currentDocument = document(); | |
535 frame().selection().setSelection(selection, 0); | |
536 clear(); | |
537 | |
538 // FrameSeleciton::setSelection() can change document associate to |frame|. | |
539 if (!isAvailable() || currentDocument != document()) | |
540 return; | |
541 if (!currentDocument.focusedElement()) | |
542 return; | |
543 | |
544 // Insert the incremental text. | |
545 const size_t insertionLength = | |
546 text.length() - commonPrefixLength - commonSuffixLength; | |
547 const String& insertingText = | |
548 text.substring(commonPrefixLength, insertionLength); | |
549 insertTextDuringCompositionWithEvents(frame(), insertingText, | |
550 TypingCommand::PreventSpellChecking, | |
551 TypingCommand::TextCompositionUpdate); | |
552 | |
553 // Event handlers might destroy document. | |
554 if (!isAvailable() || currentDocument != document()) | |
555 return; | |
556 | |
557 // TODO(yosin): The use of updateStyleAndLayoutIgnorePendingStylesheets | |
558 // needs to be audited. see http://crbug.com/590369 for more details. | |
559 document().updateStyleAndLayoutIgnorePendingStylesheets(); | |
560 | |
561 // Now recreate the composition starting at its original start, and | |
562 // apply the specified final selection offsets. | |
563 setCompositionFromExistingText(underlines, compositionStart, | |
564 compositionStart + text.length()); | |
565 } | |
566 | |
567 selectComposition(); | |
568 | |
569 // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets | |
570 // needs to be audited. see http://crbug.com/590369 for more details. | |
571 document().updateStyleAndLayoutIgnorePendingStylesheets(); | |
572 | |
573 const PlainTextRange& selectedRange = createSelectionRangeForSetComposition( | |
574 selectionStart, selectionEnd, text.length()); | |
575 // We shouldn't close typing in the middle of setComposition. | |
576 setEditableSelectionOffsets(selectedRange, NotUserTriggered); | |
577 m_isDirty = true; | |
578 } | |
579 | |
580 void InputMethodController::setComposition( | 446 void InputMethodController::setComposition( |
581 const String& text, | 447 const String& text, |
582 const Vector<CompositionUnderline>& underlines, | 448 const Vector<CompositionUnderline>& underlines, |
583 int selectionStart, | 449 int selectionStart, |
584 int selectionEnd) { | 450 int selectionEnd) { |
585 Editor::RevealSelectionScope revealSelectionScope(&editor()); | 451 Editor::RevealSelectionScope revealSelectionScope(&editor()); |
586 | 452 |
587 // Updates styles before setting selection for composition to prevent | 453 // Updates styles before setting selection for composition to prevent |
588 // inserting the previous composition text into text nodes oddly. | 454 // inserting the previous composition text into text nodes oddly. |
589 // See https://bugs.webkit.org/show_bug.cgi?id=46868 | 455 // See https://bugs.webkit.org/show_bug.cgi?id=46868 |
590 document().updateStyleAndLayoutTree(); | 456 document().updateStyleAndLayoutTree(); |
591 | 457 |
592 // When the IME only wants to change a few characters at the end of the | |
593 // composition, only touch those characters in order to preserve rich text | |
594 // substructure. | |
595 if (hasComposition() && text.length()) { | |
596 return setCompositionWithIncrementalText(text, underlines, selectionStart, | |
597 selectionEnd); | |
598 } | |
599 | |
600 selectComposition(); | 458 selectComposition(); |
601 | 459 |
602 if (frame().selection().isNone()) | 460 if (frame().selection().isNone()) |
603 return; | 461 return; |
604 | 462 |
605 Element* target = document().focusedElement(); | 463 Element* target = document().focusedElement(); |
606 if (!target) | 464 if (!target) |
607 return; | 465 return; |
608 | 466 |
609 // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets | 467 // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
656 if (!hasComposition()) { | 514 if (!hasComposition()) { |
657 target->dispatchEvent( | 515 target->dispatchEvent( |
658 CompositionEvent::create(EventTypeNames::compositionstart, | 516 CompositionEvent::create(EventTypeNames::compositionstart, |
659 frame().domWindow(), frame().selectedText())); | 517 frame().domWindow(), frame().selectedText())); |
660 if (!isAvailable()) | 518 if (!isAvailable()) |
661 return; | 519 return; |
662 } | 520 } |
663 | 521 |
664 DCHECK(!text.isEmpty()); | 522 DCHECK(!text.isEmpty()); |
665 | 523 |
524 const String& composing = composingText(); | |
666 clear(); | 525 clear(); |
667 | 526 |
668 insertTextDuringCompositionWithEvents( | 527 insertIncrementtalTextDuringCompositionWithEvents( |
669 frame(), text, | 528 frame(), composing, text, |
670 TypingCommand::SelectInsertedText | TypingCommand::PreventSpellChecking, | 529 TypingCommand::SelectInsertedText | TypingCommand::PreventSpellChecking, |
671 TypingCommand::TextCompositionUpdate); | 530 TypingCommand::TextCompositionUpdate); |
672 // Event handlers might destroy document. | 531 // Event handlers might destroy document. |
673 if (!isAvailable()) | 532 if (!isAvailable()) |
674 return; | 533 return; |
675 | 534 |
676 // TODO(yosin): The use of updateStyleAndLayoutIgnorePendingStylesheets | 535 // TODO(yosin): The use of updateStyleAndLayoutIgnorePendingStylesheets |
677 // needs to be audited. see http://crbug.com/590369 for more details. | 536 // needs to be audited. see http://crbug.com/590369 for more details. |
678 document().updateStyleAndLayoutIgnorePendingStylesheets(); | 537 document().updateStyleAndLayoutIgnorePendingStylesheets(); |
679 | 538 |
680 // Find out what node has the composition now. | 539 // Find out what node has the composition now. |
681 Position base = mostForwardCaretPosition(frame().selection().base()); | 540 Position base = mostForwardCaretPosition(frame().selection().base()); |
682 Node* baseNode = base.anchorNode(); | 541 Node* baseNode = base.anchorNode(); |
683 if (!baseNode || !baseNode->isTextNode()) | 542 if (!baseNode || !baseNode->isTextNode()) |
684 return; | 543 return; |
685 | 544 |
686 Position extent = frame().selection().extent(); | 545 Position extent = frame().selection().extent(); |
687 Node* extentNode = extent.anchorNode(); | 546 Node* extentNode = extent.anchorNode(); |
yabinh
2016/11/25 04:49:29
baseNode and extentNode could be different anchor
| |
688 if (baseNode != extentNode) | |
689 return; | |
690 | 547 |
691 unsigned extentOffset = extent.computeOffsetInContainerNode(); | 548 unsigned extentOffset = extent.computeOffsetInContainerNode(); |
692 unsigned baseOffset = base.computeOffsetInContainerNode(); | 549 unsigned baseOffset = base.computeOffsetInContainerNode(); |
yabinh
2016/11/25 04:49:29
Note that the offset is relative to its anchor nod
| |
693 if (baseOffset + text.length() != extentOffset) | |
694 return; | |
695 | 550 |
696 m_isDirty = true; | |
697 m_hasComposition = true; | 551 m_hasComposition = true; |
698 if (!m_compositionRange) | 552 if (!m_compositionRange) |
699 m_compositionRange = Range::create(document()); | 553 m_compositionRange = Range::create(document()); |
700 m_compositionRange->setStart(baseNode, baseOffset); | 554 m_compositionRange->setStart(baseNode, baseOffset); |
701 m_compositionRange->setEnd(baseNode, extentOffset); | 555 m_compositionRange->setEnd(extentNode, extentOffset); |
702 | 556 |
703 if (baseNode->layoutObject()) | 557 if (baseNode->layoutObject()) |
704 baseNode->layoutObject()->setShouldDoFullPaintInvalidation(); | 558 baseNode->layoutObject()->setShouldDoFullPaintInvalidation(); |
705 | 559 |
706 // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets | 560 // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets |
707 // needs to be audited. see http://crbug.com/590369 for more details. | 561 // needs to be audited. see http://crbug.com/590369 for more details. |
708 document().updateStyleAndLayoutIgnorePendingStylesheets(); | 562 document().updateStyleAndLayoutIgnorePendingStylesheets(); |
709 | 563 |
710 // We shouldn't close typing in the middle of setComposition. | 564 // We shouldn't close typing in the middle of setComposition. |
711 setEditableSelectionOffsets(selectedRange, NotUserTriggered); | 565 setEditableSelectionOffsets(selectedRange, NotUserTriggered); |
(...skipping 481 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1193 return WebTextInputTypeNone; | 1047 return WebTextInputTypeNone; |
1194 } | 1048 } |
1195 | 1049 |
1196 DEFINE_TRACE(InputMethodController) { | 1050 DEFINE_TRACE(InputMethodController) { |
1197 visitor->trace(m_frame); | 1051 visitor->trace(m_frame); |
1198 visitor->trace(m_compositionRange); | 1052 visitor->trace(m_compositionRange); |
1199 SynchronousMutationObserver::trace(visitor); | 1053 SynchronousMutationObserver::trace(visitor); |
1200 } | 1054 } |
1201 | 1055 |
1202 } // namespace blink | 1056 } // namespace blink |
OLD | NEW |