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

Side by Side Diff: third_party/WebKit/Source/core/editing/InputMethodController.cpp

Issue 2530843003: Introduce InsertIncrementalTextCommand to respect existing style for composition (Closed)
Patch Set: Add a test for commitText() Created 4 years 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 unified diff | Download patch
OLDNEW
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
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,
chongz 2016/11/28 16:01:04 Is it possible to utilize |FrameSelection::selecte
yabinh 2016/11/29 02:15:21 Here, oldText refers to the previous composing tex
yabinh 2016/12/02 08:41:55 It turns out to be possible after a workaround. Se
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;
chongz 2016/11/28 16:01:04 We cannot simply remove 'textInput' event due to i
yosin_UTC9 2016/11/29 02:10:16 Could you split use counter for 'textInput' into n
chongz 2016/11/29 22:20:58 Adding foolip@ for more input as he is the owner o
foolip 2016/11/30 13:05:28 I added a TODO in TextEvent.idl and a use counter,
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
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 && !m_compositionRange->collapsed() && 198 return m_hasComposition && !m_compositionRange->collapsed() &&
189 m_compositionRange->isConnected(); 199 m_compositionRange->isConnected();
190 } 200 }
191 201
192 inline Editor& InputMethodController::editor() const { 202 inline Editor& InputMethodController::editor() const {
193 return frame().editor(); 203 return frame().editor();
194 } 204 }
195 205
196 void InputMethodController::clear() { 206 void InputMethodController::clear() {
197 m_hasComposition = false; 207 m_hasComposition = false;
198 if (m_compositionRange) { 208 if (m_compositionRange) {
199 m_compositionRange->setStart(&document(), 0); 209 m_compositionRange->setStart(&document(), 0);
200 m_compositionRange->collapse(true); 210 m_compositionRange->collapse(true);
201 } 211 }
202 document().markers().removeMarkers(DocumentMarker::Composition); 212 document().markers().removeMarkers(DocumentMarker::Composition);
203 m_isDirty = false;
204 } 213 }
205 214
206 void InputMethodController::contextDestroyed() { 215 void InputMethodController::contextDestroyed() {
207 clear(); 216 clear();
208 m_compositionRange = nullptr; 217 m_compositionRange = nullptr;
209 } 218 }
210 219
211 void InputMethodController::documentAttached(Document* document) { 220 void InputMethodController::documentAttached(Document* document) {
212 DCHECK(document); 221 DCHECK(document);
213 setContext(document); 222 setContext(document);
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
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 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
373 return; 380 return;
374 381
375 clear(); 382 clear();
376 383
377 // TODO(chongz): Figure out which InputType should we use here. 384 // TODO(chongz): Figure out which InputType should we use here.
378 dispatchBeforeInputFromComposition( 385 dispatchBeforeInputFromComposition(
379 document().focusedElement(), 386 document().focusedElement(),
380 InputEvent::InputType::DeleteComposedCharacterBackward, nullAtom, 387 InputEvent::InputType::DeleteComposedCharacterBackward, nullAtom,
381 InputEvent::EventCancelable::NotCancelable); 388 InputEvent::EventCancelable::NotCancelable);
382 dispatchCompositionUpdateEvent(frame(), emptyString()); 389 dispatchCompositionUpdateEvent(frame(), emptyString());
383 insertTextDuringCompositionWithEvents( 390 insertIncrementtalTextDuringCompositionWithEvents(
384 frame(), emptyString(), 0, 391 frame(), emptyString(), emptyString(), 0,
385 TypingCommand::TextCompositionType::TextCompositionCancel); 392 TypingCommand::TextCompositionType::TextCompositionCancel);
386 // Event handler might destroy document. 393 // Event handler might destroy document.
387 if (!isAvailable()) 394 if (!isAvailable())
388 return; 395 return;
389 396
390 // An open typing command that disagrees about current selection would cause 397 // An open typing command that disagrees about current selection would cause
391 // issues with typing later on. 398 // issues with typing later on.
392 TypingCommand::closeTyping(m_frame); 399 TypingCommand::closeTyping(m_frame);
393 400
394 // No DOM update after 'compositionend'. 401 // No DOM update after 'compositionend'.
395 dispatchCompositionEndEvent(frame(), emptyString()); 402 dispatchCompositionEndEvent(frame(), emptyString());
396 } 403 }
397 404
398 void InputMethodController::cancelCompositionIfSelectionIsInvalid() { 405 void InputMethodController::cancelCompositionIfSelectionIsInvalid() {
399 if (!hasComposition() || editor().preventRevealSelection()) 406 if (!hasComposition() || editor().preventRevealSelection())
400 return; 407 return;
401 408
402 // Check if selection start and selection end are valid. 409 // Check if selection start and selection end are valid.
403 FrameSelection& selection = frame().selection(); 410 FrameSelection& selection = frame().selection();
404 if (!selection.isNone() && !m_compositionRange->collapsed()) { 411 if (!selection.isNone() && !m_compositionRange->collapsed()) {
405 if (selection.start().compareTo(m_compositionRange->startPosition()) >= 0 && 412 if (selection.start().compareTo(m_compositionRange->startPosition()) >= 0 &&
406 selection.end().compareTo(m_compositionRange->endPosition()) <= 0) 413 selection.end().compareTo(m_compositionRange->endPosition()) <= 0)
407 return; 414 return;
408 } 415 }
409 416
410 cancelComposition(); 417 cancelComposition();
411 frame().chromeClient().didCancelCompositionOnSelectionChange(); 418 frame().chromeClient().didCancelCompositionOnSelectionChange();
412 } 419 }
413 420
414 static size_t computeCommonPrefixLength(const String& str1,
415 const String& str2) {
416 const size_t maxCommonPrefixLength = std::min(str1.length(), str2.length());
417 for (size_t index = 0; index < maxCommonPrefixLength; ++index) {
418 if (str1[index] != str2[index])
419 return index;
420 }
421 return maxCommonPrefixLength;
422 }
423
424 static size_t computeCommonSuffixLength(const String& str1,
425 const String& str2) {
426 const size_t length1 = str1.length();
427 const size_t length2 = str2.length();
428 const size_t maxCommonSuffixLength = std::min(length1, length2);
429 for (size_t index = 0; index < maxCommonSuffixLength; ++index) {
430 if (str1[length1 - index - 1] != str2[length2 - index - 1])
431 return index;
432 }
433 return maxCommonSuffixLength;
434 }
435
436 // If current position is at grapheme boundary, return 0; otherwise, return the 421 // If current position is at grapheme boundary, return 0; otherwise, return the
437 // distance to its nearest left grapheme boundary. 422 // distance to its nearest left grapheme boundary.
438 static size_t computeDistanceToLeftGraphemeBoundary(const Position& position) { 423 static size_t computeDistanceToLeftGraphemeBoundary(const Position& position) {
439 const Position& adjustedPosition = previousPositionOf( 424 const Position& adjustedPosition = previousPositionOf(
440 nextPositionOf(position, PositionMoveType::GraphemeCluster), 425 nextPositionOf(position, PositionMoveType::GraphemeCluster),
441 PositionMoveType::GraphemeCluster); 426 PositionMoveType::GraphemeCluster);
442 DCHECK_EQ(position.anchorNode(), adjustedPosition.anchorNode()); 427 DCHECK_EQ(position.anchorNode(), adjustedPosition.anchorNode());
443 DCHECK_GE(position.computeOffsetInContainerNode(), 428 DCHECK_GE(position.computeOffsetInContainerNode(),
444 adjustedPosition.computeOffsetInContainerNode()); 429 adjustedPosition.computeOffsetInContainerNode());
445 return static_cast<size_t>(position.computeOffsetInContainerNode() - 430 return static_cast<size_t>(position.computeOffsetInContainerNode() -
446 adjustedPosition.computeOffsetInContainerNode()); 431 adjustedPosition.computeOffsetInContainerNode());
447 } 432 }
448 433
449 static size_t computeCommonGraphemeClusterPrefixLengthForSetComposition(
450 const String& oldText,
451 const String& newText,
452 const Element* rootEditableElement) {
453 const size_t commonPrefixLength = computeCommonPrefixLength(oldText, newText);
454
455 // For grapheme cluster, we should adjust it for grapheme boundary.
456 const EphemeralRange& range =
457 PlainTextRange(0, commonPrefixLength).createRange(*rootEditableElement);
458 if (range.isNull())
459 return 0;
460 const Position& position = range.endPosition();
461 const size_t diff = computeDistanceToLeftGraphemeBoundary(position);
462 DCHECK_GE(commonPrefixLength, diff);
463 return commonPrefixLength - diff;
464 }
465
466 // If current position is at grapheme boundary, return 0; otherwise, return the 434 // If current position is at grapheme boundary, return 0; otherwise, return the
467 // distance to its nearest right grapheme boundary. 435 // distance to its nearest right grapheme boundary.
468 static size_t computeDistanceToRightGraphemeBoundary(const Position& position) { 436 static size_t computeDistanceToRightGraphemeBoundary(const Position& position) {
469 const Position& adjustedPosition = nextPositionOf( 437 const Position& adjustedPosition = nextPositionOf(
470 previousPositionOf(position, PositionMoveType::GraphemeCluster), 438 previousPositionOf(position, PositionMoveType::GraphemeCluster),
471 PositionMoveType::GraphemeCluster); 439 PositionMoveType::GraphemeCluster);
472 DCHECK_EQ(position.anchorNode(), adjustedPosition.anchorNode()); 440 DCHECK_EQ(position.anchorNode(), adjustedPosition.anchorNode());
473 DCHECK_GE(adjustedPosition.computeOffsetInContainerNode(), 441 DCHECK_GE(adjustedPosition.computeOffsetInContainerNode(),
474 position.computeOffsetInContainerNode()); 442 position.computeOffsetInContainerNode());
475 return static_cast<size_t>(adjustedPosition.computeOffsetInContainerNode() - 443 return static_cast<size_t>(adjustedPosition.computeOffsetInContainerNode() -
476 position.computeOffsetInContainerNode()); 444 position.computeOffsetInContainerNode());
477 } 445 }
478 446
479 static size_t computeCommonGraphemeClusterSuffixLengthForSetComposition(
480 const String& oldText,
481 const String& newText,
482 const Element* rootEditableElement) {
483 const size_t commonSuffixLength = computeCommonSuffixLength(oldText, newText);
484
485 // For grapheme cluster, we should adjust it for grapheme boundary.
486 const EphemeralRange& range =
487 PlainTextRange(0, oldText.length() - commonSuffixLength)
488 .createRange(*rootEditableElement);
489 if (range.isNull())
490 return 0;
491 const Position& position = range.endPosition();
492 const size_t diff = computeDistanceToRightGraphemeBoundary(position);
493 DCHECK_GE(commonSuffixLength, diff);
494 return commonSuffixLength - diff;
495 }
496
497 void InputMethodController::setCompositionWithIncrementalText(
498 const String& text,
499 const Vector<CompositionUnderline>& underlines,
500 int selectionStart,
501 int selectionEnd) {
502 Element* editable = frame().selection().rootEditableElement();
503 if (!editable)
504 return;
505
506 DCHECK_LE(selectionStart, selectionEnd);
507 String composing = composingText();
508 const size_t commonPrefixLength =
509 computeCommonGraphemeClusterPrefixLengthForSetComposition(composing, text,
510 editable);
511
512 // We should ignore common prefix when finding common suffix.
513 const size_t commonSuffixLength =
514 computeCommonGraphemeClusterSuffixLengthForSetComposition(
515 composing.right(composing.length() - commonPrefixLength),
516 text.right(text.length() - commonPrefixLength), editable);
517
518 const bool inserting =
519 text.length() > commonPrefixLength + commonSuffixLength;
520 const bool deleting =
521 composing.length() > commonPrefixLength + commonSuffixLength;
522
523 if (inserting || deleting) {
524 // Select the text to be deleted.
525 const size_t compositionStart =
526 PlainTextRange::create(*editable, compositionEphemeralRange()).start();
527 const size_t deletionStart = compositionStart + commonPrefixLength;
528 const size_t deletionEnd =
529 compositionStart + composing.length() - commonSuffixLength;
530 const EphemeralRange& deletionRange =
531 PlainTextRange(deletionStart, deletionEnd).createRange(*editable);
532 Document& currentDocument = document();
533 frame().selection().setSelection(
534 SelectionInDOMTree::Builder().setBaseAndExtent(deletionRange).build(),
535 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( 447 void InputMethodController::setComposition(
581 const String& text, 448 const String& text,
582 const Vector<CompositionUnderline>& underlines, 449 const Vector<CompositionUnderline>& underlines,
583 int selectionStart, 450 int selectionStart,
584 int selectionEnd) { 451 int selectionEnd) {
585 Editor::RevealSelectionScope revealSelectionScope(&editor()); 452 Editor::RevealSelectionScope revealSelectionScope(&editor());
586 453
587 // Updates styles before setting selection for composition to prevent 454 // Updates styles before setting selection for composition to prevent
588 // inserting the previous composition text into text nodes oddly. 455 // inserting the previous composition text into text nodes oddly.
589 // See https://bugs.webkit.org/show_bug.cgi?id=46868 456 // See https://bugs.webkit.org/show_bug.cgi?id=46868
590 document().updateStyleAndLayoutTree(); 457 document().updateStyleAndLayoutTree();
591 458
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(); 459 selectComposition();
601 460
602 if (frame().selection().isNone()) 461 if (frame().selection().isNone())
603 return; 462 return;
604 463
605 Element* target = document().focusedElement(); 464 Element* target = document().focusedElement();
606 if (!target) 465 if (!target)
607 return; 466 return;
608 467
609 // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets 468 // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
656 if (!hasComposition()) { 515 if (!hasComposition()) {
657 target->dispatchEvent( 516 target->dispatchEvent(
658 CompositionEvent::create(EventTypeNames::compositionstart, 517 CompositionEvent::create(EventTypeNames::compositionstart,
659 frame().domWindow(), frame().selectedText())); 518 frame().domWindow(), frame().selectedText()));
660 if (!isAvailable()) 519 if (!isAvailable())
661 return; 520 return;
662 } 521 }
663 522
664 DCHECK(!text.isEmpty()); 523 DCHECK(!text.isEmpty());
665 524
525 const String& composing = composingText();
666 clear(); 526 clear();
667 527
668 insertTextDuringCompositionWithEvents( 528 insertIncrementtalTextDuringCompositionWithEvents(
669 frame(), text, 529 frame(), composing, text,
670 TypingCommand::SelectInsertedText | TypingCommand::PreventSpellChecking, 530 TypingCommand::SelectInsertedText | TypingCommand::PreventSpellChecking,
671 TypingCommand::TextCompositionUpdate); 531 TypingCommand::TextCompositionUpdate);
672 // Event handlers might destroy document. 532 // Event handlers might destroy document.
673 if (!isAvailable()) 533 if (!isAvailable())
674 return; 534 return;
675 535
676 // TODO(yosin): The use of updateStyleAndLayoutIgnorePendingStylesheets 536 // TODO(yosin): The use of updateStyleAndLayoutIgnorePendingStylesheets
677 // needs to be audited. see http://crbug.com/590369 for more details. 537 // needs to be audited. see http://crbug.com/590369 for more details.
678 document().updateStyleAndLayoutIgnorePendingStylesheets(); 538 document().updateStyleAndLayoutIgnorePendingStylesheets();
679 539
680 // Find out what node has the composition now. 540 // Find out what node has the composition now.
681 Position base = mostForwardCaretPosition(frame().selection().base()); 541 Position base = mostForwardCaretPosition(frame().selection().base());
682 Node* baseNode = base.anchorNode(); 542 Node* baseNode = base.anchorNode();
683 if (!baseNode || !baseNode->isTextNode()) 543 if (!baseNode || !baseNode->isTextNode())
684 return; 544 return;
685 545
686 Position extent = frame().selection().extent(); 546 Position extent = frame().selection().extent();
687 Node* extentNode = extent.anchorNode(); 547 Node* extentNode = extent.anchorNode();
688 if (baseNode != extentNode)
689 return;
690 548
691 unsigned extentOffset = extent.computeOffsetInContainerNode(); 549 unsigned extentOffset = extent.computeOffsetInContainerNode();
692 unsigned baseOffset = base.computeOffsetInContainerNode(); 550 unsigned baseOffset = base.computeOffsetInContainerNode();
693 if (baseOffset + text.length() != extentOffset)
694 return;
695 551
696 m_isDirty = true;
697 m_hasComposition = true; 552 m_hasComposition = true;
698 if (!m_compositionRange) 553 if (!m_compositionRange)
699 m_compositionRange = Range::create(document()); 554 m_compositionRange = Range::create(document());
700 m_compositionRange->setStart(baseNode, baseOffset); 555 m_compositionRange->setStart(baseNode, baseOffset);
701 m_compositionRange->setEnd(baseNode, extentOffset); 556 m_compositionRange->setEnd(extentNode, extentOffset);
702 557
703 if (baseNode->layoutObject()) 558 if (baseNode->layoutObject())
704 baseNode->layoutObject()->setShouldDoFullPaintInvalidation(); 559 baseNode->layoutObject()->setShouldDoFullPaintInvalidation();
705 560
706 // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets 561 // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets
707 // needs to be audited. see http://crbug.com/590369 for more details. 562 // needs to be audited. see http://crbug.com/590369 for more details.
708 document().updateStyleAndLayoutIgnorePendingStylesheets(); 563 document().updateStyleAndLayoutIgnorePendingStylesheets();
709 564
710 // We shouldn't close typing in the middle of setComposition. 565 // We shouldn't close typing in the middle of setComposition.
711 setEditableSelectionOffsets(selectedRange, NotUserTriggered); 566 setEditableSelectionOffsets(selectedRange, NotUserTriggered);
(...skipping 481 matching lines...) Expand 10 before | Expand all | Expand 10 after
1193 return WebTextInputTypeNone; 1048 return WebTextInputTypeNone;
1194 } 1049 }
1195 1050
1196 DEFINE_TRACE(InputMethodController) { 1051 DEFINE_TRACE(InputMethodController) {
1197 visitor->trace(m_frame); 1052 visitor->trace(m_frame);
1198 visitor->trace(m_compositionRange); 1053 visitor->trace(m_compositionRange);
1199 SynchronousMutationObserver::trace(visitor); 1054 SynchronousMutationObserver::trace(visitor);
1200 } 1055 }
1201 1056
1202 } // namespace blink 1057 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698