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 249 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 260 document().updateStyleAndLayoutIgnorePendingStylesheets(); | 260 document().updateStyleAndLayoutIgnorePendingStylesheets(); |
| 261 | 261 |
| 262 setSelectionOffsets(oldOffsets); | 262 setSelectionOffsets(oldOffsets); |
| 263 | 263 |
| 264 // No DOM update after 'compositionend'. | 264 // No DOM update after 'compositionend'. |
| 265 dispatchCompositionEndEvent(frame(), composing); | 265 dispatchCompositionEndEvent(frame(), composing); |
| 266 | 266 |
| 267 return result; | 267 return result; |
| 268 } | 268 } |
| 269 | 269 |
| 270 return replaceCompositionAndMoveCaret(composingText(), 0); | 270 return replaceCompositionAndMoveCaret(composingText(), 0, |
| 271 Vector<CompositionUnderline>()); | |
| 271 } | 272 } |
| 272 | 273 |
| 273 bool InputMethodController::commitText(const String& text, | 274 bool InputMethodController::commitText( |
| 274 int relativeCaretPosition) { | 275 const String& text, |
| 275 if (hasComposition()) | 276 const Vector<CompositionUnderline>& underlines, |
| 276 return replaceCompositionAndMoveCaret(text, relativeCaretPosition); | 277 int relativeCaretPosition) { |
| 278 if (hasComposition()) { | |
| 279 return replaceCompositionAndMoveCaret(text, relativeCaretPosition, | |
| 280 underlines); | |
| 281 } | |
| 277 | 282 |
| 278 // We should do nothing in this case, because: | 283 // We should do nothing in this case, because: |
| 279 // 1. No need to insert text when text is empty. | 284 // 1. No need to insert text when text is empty. |
| 280 // 2. Shouldn't move caret when relativeCaretPosition == 0 to avoid | 285 // 2. Shouldn't move caret when relativeCaretPosition == 0 to avoid |
| 281 // duplicate selection change event. | 286 // duplicate selection change event. |
| 282 if (!text.length() && !relativeCaretPosition) | 287 if (!text.length() && !relativeCaretPosition) { |
| 283 return false; | 288 return false; |
| 284 return insertTextAndMoveCaret(text, relativeCaretPosition); | 289 } |
| 290 | |
| 291 return insertTextAndMoveCaret(text, relativeCaretPosition, underlines); | |
| 285 } | 292 } |
| 286 | 293 |
| 287 bool InputMethodController::replaceComposition(const String& text) { | 294 bool InputMethodController::replaceComposition(const String& text) { |
| 288 if (!hasComposition()) | 295 if (!hasComposition()) |
| 289 return false; | 296 return false; |
| 290 | 297 |
| 291 // Select the text that will be deleted or replaced. | 298 // Select the text that will be deleted or replaced. |
| 292 selectComposition(); | 299 selectComposition(); |
| 293 | 300 |
| 294 if (frame().selection().isNone()) | 301 if (frame().selection().isNone()) |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 317 return true; | 324 return true; |
| 318 } | 325 } |
| 319 | 326 |
| 320 // relativeCaretPosition is relative to the end of the text. | 327 // relativeCaretPosition is relative to the end of the text. |
| 321 static int computeAbsoluteCaretPosition(size_t textStart, | 328 static int computeAbsoluteCaretPosition(size_t textStart, |
| 322 size_t textLength, | 329 size_t textLength, |
| 323 int relativeCaretPosition) { | 330 int relativeCaretPosition) { |
| 324 return textStart + textLength + relativeCaretPosition; | 331 return textStart + textLength + relativeCaretPosition; |
| 325 } | 332 } |
| 326 | 333 |
| 334 void InputMethodController::addCompositionUnderlines( | |
| 335 const Vector<CompositionUnderline>& underlines, | |
| 336 Node* baseNode, | |
| 337 unsigned baseOffset) { | |
| 338 for (const auto& underline : underlines) { | |
| 339 unsigned underlineStart = baseOffset + underline.startOffset(); | |
| 340 unsigned underlineEnd = baseOffset + underline.endOffset(); | |
| 341 EphemeralRange ephemeralLineRange = EphemeralRange( | |
| 342 Position(baseNode, underlineStart), Position(baseNode, underlineEnd)); | |
| 343 if (ephemeralLineRange.isNull()) | |
| 344 continue; | |
| 345 document().markers().addCompositionMarker( | |
| 346 ephemeralLineRange.startPosition(), ephemeralLineRange.endPosition(), | |
| 347 underline.color(), underline.thick(), underline.backgroundColor()); | |
| 348 } | |
| 349 } | |
| 350 | |
| 327 bool InputMethodController::replaceCompositionAndMoveCaret( | 351 bool InputMethodController::replaceCompositionAndMoveCaret( |
| 328 const String& text, | 352 const String& text, |
| 329 int relativeCaretPosition) { | 353 int relativeCaretPosition, |
| 354 const Vector<CompositionUnderline>& underlines) { | |
| 330 Element* rootEditableElement = frame().selection().rootEditableElement(); | 355 Element* rootEditableElement = frame().selection().rootEditableElement(); |
| 331 if (!rootEditableElement) | 356 if (!rootEditableElement) |
| 332 return false; | 357 return false; |
| 333 DCHECK(hasComposition()); | 358 DCHECK(hasComposition()); |
| 334 PlainTextRange compositionRange = | 359 PlainTextRange compositionRange = |
| 335 PlainTextRange::create(*rootEditableElement, *m_compositionRange); | 360 PlainTextRange::create(*rootEditableElement, *m_compositionRange); |
| 336 if (compositionRange.isNull()) | 361 if (compositionRange.isNull()) |
| 337 return false; | 362 return false; |
| 338 int textStart = compositionRange.start(); | 363 int textStart = compositionRange.start(); |
| 339 | 364 |
| 340 if (!replaceComposition(text)) | 365 if (!replaceComposition(text)) |
| 341 return false; | 366 return false; |
| 342 | 367 |
| 368 addCompositionUnderlines(underlines, rootEditableElement, textStart); | |
|
rlanday
2016/12/16 20:55:33
I don't think the Voice IME actually ever replaces
rlanday
2016/12/16 21:01:35
I'm going to try to add a test case to InputMethod
rlanday
2016/12/16 21:01:35
I'm going to try to add a test case to InputMethod
| |
| 369 | |
| 343 int absoluteCaretPosition = computeAbsoluteCaretPosition( | 370 int absoluteCaretPosition = computeAbsoluteCaretPosition( |
| 344 textStart, text.length(), relativeCaretPosition); | 371 textStart, text.length(), relativeCaretPosition); |
| 345 if (!moveCaret(absoluteCaretPosition)) | 372 if (!moveCaret(absoluteCaretPosition)) |
| 346 return false; | 373 return false; |
| 347 | 374 |
| 348 // No DOM update after 'compositionend'. | 375 // No DOM update after 'compositionend'. |
| 349 dispatchCompositionEndEvent(frame(), text); | 376 dispatchCompositionEndEvent(frame(), text); |
| 350 | 377 |
| 351 return true; | 378 return true; |
| 352 } | 379 } |
| 353 | 380 |
| 354 bool InputMethodController::insertText(const String& text) { | 381 bool InputMethodController::insertText(const String& text) { |
| 355 if (dispatchBeforeInputInsertText(document().focusedElement(), text) != | 382 if (dispatchBeforeInputInsertText(document().focusedElement(), text) != |
| 356 DispatchEventResult::NotCanceled) | 383 DispatchEventResult::NotCanceled) |
| 357 return false; | 384 return false; |
| 358 editor().insertText(text, 0); | 385 editor().insertText(text, 0); |
| 359 return true; | 386 return true; |
| 360 } | 387 } |
| 361 | 388 |
| 362 bool InputMethodController::insertTextAndMoveCaret(const String& text, | 389 bool InputMethodController::insertTextAndMoveCaret( |
| 363 int relativeCaretPosition) { | 390 const String& text, |
| 391 int relativeCaretPosition, | |
| 392 const Vector<CompositionUnderline>& underlines) { | |
| 364 PlainTextRange selectionRange = getSelectionOffsets(); | 393 PlainTextRange selectionRange = getSelectionOffsets(); |
| 365 if (selectionRange.isNull()) | 394 if (selectionRange.isNull()) |
| 366 return false; | 395 return false; |
| 367 int textStart = selectionRange.start(); | 396 int textStart = selectionRange.start(); |
| 368 | 397 |
| 369 if (text.length()) { | 398 if (text.length()) { |
| 399 Position base = mostForwardCaretPosition(frame().selection().base()); | |
| 400 Node* baseNode = base.anchorNode(); | |
| 401 | |
| 402 if (!baseNode) | |
|
rlanday
2016/12/16 20:55:33
Note: if I add the baseNode->isTextNode() check th
| |
| 403 return false; | |
| 404 | |
| 405 unsigned baseOffset = base.computeOffsetInContainerNode(); | |
| 406 | |
| 370 if (!insertText(text)) | 407 if (!insertText(text)) |
| 371 return false; | 408 return false; |
| 409 | |
| 410 addCompositionUnderlines(underlines, baseNode, baseOffset); | |
| 372 } | 411 } |
| 373 | 412 |
| 374 int absoluteCaretPosition = computeAbsoluteCaretPosition( | 413 int absoluteCaretPosition = computeAbsoluteCaretPosition( |
| 375 textStart, text.length(), relativeCaretPosition); | 414 textStart, text.length(), relativeCaretPosition); |
| 376 return moveCaret(absoluteCaretPosition); | 415 return moveCaret(absoluteCaretPosition); |
| 377 } | 416 } |
| 378 | 417 |
| 379 void InputMethodController::cancelComposition() { | 418 void InputMethodController::cancelComposition() { |
| 380 if (!hasComposition()) | 419 if (!hasComposition()) |
| 381 return; | 420 return; |
| (...skipping 189 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 571 // We shouldn't close typing in the middle of setComposition. | 610 // We shouldn't close typing in the middle of setComposition. |
| 572 setEditableSelectionOffsets(selectedRange, NotUserTriggered); | 611 setEditableSelectionOffsets(selectedRange, NotUserTriggered); |
| 573 | 612 |
| 574 if (underlines.isEmpty()) { | 613 if (underlines.isEmpty()) { |
| 575 document().markers().addCompositionMarker( | 614 document().markers().addCompositionMarker( |
| 576 m_compositionRange->startPosition(), m_compositionRange->endPosition(), | 615 m_compositionRange->startPosition(), m_compositionRange->endPosition(), |
| 577 Color::black, false, | 616 Color::black, false, |
| 578 LayoutTheme::theme().platformDefaultCompositionBackgroundColor()); | 617 LayoutTheme::theme().platformDefaultCompositionBackgroundColor()); |
| 579 return; | 618 return; |
| 580 } | 619 } |
| 581 for (const auto& underline : underlines) { | 620 |
| 582 unsigned underlineStart = baseOffset + underline.startOffset(); | 621 addCompositionUnderlines(underlines, baseNode, baseOffset); |
| 583 unsigned underlineEnd = baseOffset + underline.endOffset(); | |
| 584 EphemeralRange ephemeralLineRange = EphemeralRange( | |
| 585 Position(baseNode, underlineStart), Position(baseNode, underlineEnd)); | |
| 586 if (ephemeralLineRange.isNull()) | |
| 587 continue; | |
| 588 document().markers().addCompositionMarker( | |
| 589 ephemeralLineRange.startPosition(), ephemeralLineRange.endPosition(), | |
| 590 underline.color(), underline.thick(), underline.backgroundColor()); | |
| 591 } | |
| 592 } | 622 } |
| 593 | 623 |
| 594 PlainTextRange InputMethodController::createSelectionRangeForSetComposition( | 624 PlainTextRange InputMethodController::createSelectionRangeForSetComposition( |
| 595 int selectionStart, | 625 int selectionStart, |
| 596 int selectionEnd, | 626 int selectionEnd, |
| 597 size_t textLength) const { | 627 size_t textLength) const { |
| 598 const int selectionOffsetsStart = | 628 const int selectionOffsetsStart = |
| 599 static_cast<int>(getSelectionOffsets().start()); | 629 static_cast<int>(getSelectionOffsets().start()); |
| 600 const int start = selectionOffsetsStart + selectionStart; | 630 const int start = selectionOffsetsStart + selectionStart; |
| 601 const int end = selectionOffsetsStart + selectionEnd; | 631 const int end = selectionOffsetsStart + selectionEnd; |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 620 const Position start = range.startPosition(); | 650 const Position start = range.startPosition(); |
| 621 if (rootEditableElementOf(start) != editable) | 651 if (rootEditableElementOf(start) != editable) |
| 622 return; | 652 return; |
| 623 | 653 |
| 624 const Position end = range.endPosition(); | 654 const Position end = range.endPosition(); |
| 625 if (rootEditableElementOf(end) != editable) | 655 if (rootEditableElementOf(end) != editable) |
| 626 return; | 656 return; |
| 627 | 657 |
| 628 clear(); | 658 clear(); |
| 629 | 659 |
| 630 for (const auto& underline : underlines) { | 660 addCompositionUnderlines(underlines, editable, compositionStart); |
| 631 unsigned underlineStart = compositionStart + underline.startOffset(); | |
| 632 unsigned underlineEnd = compositionStart + underline.endOffset(); | |
| 633 EphemeralRange ephemeralLineRange = | |
| 634 PlainTextRange(underlineStart, underlineEnd).createRange(*editable); | |
| 635 if (ephemeralLineRange.isNull()) | |
| 636 continue; | |
| 637 document().markers().addCompositionMarker( | |
| 638 ephemeralLineRange.startPosition(), ephemeralLineRange.endPosition(), | |
| 639 underline.color(), underline.thick(), underline.backgroundColor()); | |
| 640 } | |
| 641 | 661 |
| 642 m_hasComposition = true; | 662 m_hasComposition = true; |
| 643 if (!m_compositionRange) | 663 if (!m_compositionRange) |
| 644 m_compositionRange = Range::create(document()); | 664 m_compositionRange = Range::create(document()); |
| 645 m_compositionRange->setStart(range.startPosition()); | 665 m_compositionRange->setStart(range.startPosition()); |
| 646 m_compositionRange->setEnd(range.endPosition()); | 666 m_compositionRange->setEnd(range.endPosition()); |
| 647 } | 667 } |
| 648 | 668 |
| 649 EphemeralRange InputMethodController::compositionEphemeralRange() const { | 669 EphemeralRange InputMethodController::compositionEphemeralRange() const { |
| 650 if (!hasComposition()) | 670 if (!hasComposition()) |
| (...skipping 412 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1063 frame().chromeClient().resetInputMethod(); | 1083 frame().chromeClient().resetInputMethod(); |
| 1064 } | 1084 } |
| 1065 | 1085 |
| 1066 DEFINE_TRACE(InputMethodController) { | 1086 DEFINE_TRACE(InputMethodController) { |
| 1067 visitor->trace(m_frame); | 1087 visitor->trace(m_frame); |
| 1068 visitor->trace(m_compositionRange); | 1088 visitor->trace(m_compositionRange); |
| 1069 SynchronousMutationObserver::trace(visitor); | 1089 SynchronousMutationObserver::trace(visitor); |
| 1070 } | 1090 } |
| 1071 | 1091 |
| 1072 } // namespace blink | 1092 } // namespace blink |
| OLD | NEW |