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

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

Issue 2372493002: Workaround for setComposition styling clobber (Closed)
Patch Set: Address yosin@'s review Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View 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 339 matching lines...) Expand 10 before | Expand all | Expand 10 after
350 if (!selection.isNone() && !m_compositionRange->collapsed()) { 350 if (!selection.isNone() && !m_compositionRange->collapsed()) {
351 if (selection.start().compareTo(m_compositionRange->startPosition()) >= 0 && 351 if (selection.start().compareTo(m_compositionRange->startPosition()) >= 0 &&
352 selection.end().compareTo(m_compositionRange->endPosition()) <= 0) 352 selection.end().compareTo(m_compositionRange->endPosition()) <= 0)
353 return; 353 return;
354 } 354 }
355 355
356 cancelComposition(); 356 cancelComposition();
357 frame().chromeClient().didCancelCompositionOnSelectionChange(); 357 frame().chromeClient().didCancelCompositionOnSelectionChange();
358 } 358 }
359 359
360 static size_t computeCommonPrefixLength(const String& str1,
361 const String& str2) {
362 const size_t maxCommonPrefixLength = std::min(str1.length(), str2.length());
363 for (size_t index = 0; index < maxCommonPrefixLength; ++index) {
364 if (str1[index] != str2[index])
365 return index;
366 }
367
yosin_UTC9 2016/10/12 06:35:37 nit: Please remove an extra blank line.
yabinh 2016/10/12 07:32:32 Done.
368 return maxCommonPrefixLength;
369 }
370
371 static size_t computeCommonSuffixLength(const String& str1,
372 const String& str2) {
373 const size_t length1 = str1.length();
374 const size_t length2 = str2.length();
375 const size_t maxCommonSuffixLength = std::min(length1, length2);
376 for (size_t index = 0; index < maxCommonSuffixLength; ++index) {
377 if (str1[length1 - index - 1] != str2[length2 - index - 1])
378 return index;
379 }
380
yosin_UTC9 2016/10/12 06:35:38 nit: Please remove an extra blank line.
yabinh 2016/10/12 07:32:32 Done.
381 return maxCommonSuffixLength;
382 }
383
384 static size_t computeCommonGraphemeClusterPrefixLengthForSetComposition(
385 const String& oldText,
386 const String& newText,
387 const Element* rootEditableElement) {
388 const size_t commonPrefixLength = computeCommonPrefixLength(oldText, newText);
389
390 // For grapheme cluster, we should adjust it for grapheme boundary.
391 const EphemeralRange& range =
392 PlainTextRange(0, commonPrefixLength).createRange(*rootEditableElement);
393 if (range.isNull())
394 return 0;
395 const Position& position = range.endPosition();
396 const Position& adjustedPosition = previousPositionOf(
397 nextPositionOf(position, PositionMoveType::GraphemeCluster),
398 PositionMoveType::GraphemeCluster);
399 DCHECK_EQ(position.anchorNode(), adjustedPosition.anchorNode());
400 const int diff = position.computeOffsetInContainerNode() -
yosin_UTC9 2016/10/12 06:35:37 Let's do DCHECK_GE(position.computeOffsetInContain
yabinh 2016/10/12 07:32:32 Done.
401 adjustedPosition.computeOffsetInContainerNode();
402 DCHECK_GE(diff, 0);
403 return commonPrefixLength - static_cast<size_t>(diff);
404 }
405
406 static size_t computeCommonGraphemeClusterSuffixLengthForSetComposition(
407 const String& oldText,
408 const String& newText,
409 const Element* rootEditableElement) {
410 const size_t commonSuffixLength = computeCommonSuffixLength(oldText, newText);
411
412 // For grapheme cluster, we should adjust it for grapheme boundary.
413 const EphemeralRange& range =
414 PlainTextRange(0, oldText.length() - commonSuffixLength)
415 .createRange(*rootEditableElement);
416 if (range.isNull())
417 return 0;
418 const Position& position = range.endPosition();
419 const Position& adjustedPosition = nextPositionOf(
420 previousPositionOf(position, PositionMoveType::GraphemeCluster),
421 PositionMoveType::GraphemeCluster);
422 DCHECK_EQ(position.anchorNode(), adjustedPosition.anchorNode());
423 const int diff = adjustedPosition.computeOffsetInContainerNode() -
424 position.computeOffsetInContainerNode();
425 DCHECK_GE(diff, 0);
yosin_UTC9 2016/10/12 06:35:37 Ditto as computeCommonGraphemeClusterPrefixLengthF
yabinh 2016/10/12 07:32:33 Done.
426 return commonSuffixLength - static_cast<size_t>(diff);
427 }
428
429 void InputMethodController::setCompositionWithIncrementalText(
430 const String& text,
431 const Vector<CompositionUnderline>& underlines,
432 int selectionStart,
433 int selectionEnd) {
434 Element* editable = frame().selection().rootEditableElement();
435 if (!editable)
436 return;
437
438 String composing = composingText();
439 const size_t commonPrefixLength =
440 computeCommonGraphemeClusterPrefixLengthForSetComposition(composing, text,
441 editable);
442
443 // We should ignore common prefix when finding common suffix.
444 const size_t commonSuffixLength =
445 computeCommonGraphemeClusterSuffixLengthForSetComposition(
446 composing.right(composing.length() - commonPrefixLength),
447 text.right(text.length() - commonPrefixLength), editable);
448
449 const bool appending =
yosin_UTC9 2016/10/12 06:35:38 s/appending/inserting/
yabinh 2016/10/12 07:32:32 Done.
450 text.length() > commonPrefixLength + commonSuffixLength;
451 const bool subtracting =
yosin_UTC9 2016/10/12 06:35:37 s/subtracting/deleting/
yabinh 2016/10/12 07:32:32 Done.
452 composing.length() > commonPrefixLength + +commonSuffixLength;
yosin_UTC9 2016/10/12 06:35:38 nit: Remove |+| before |comonSuffixLength|.
yabinh 2016/10/12 07:32:32 Done.
453
454 if (appending || subtracting) {
455 // Select the text to be subtracted.
yosin_UTC9 2016/10/12 06:35:38 s/subtracted/deleted/
yabinh 2016/10/12 07:32:32 Done.
456 const size_t compositionStart =
457 PlainTextRange::create(*editable, compositionEphemeralRange()).start();
458 const size_t subtractionStart = compositionStart + commonPrefixLength;
459 const size_t subtractionEnd =
460 compositionStart + composing.length() - commonSuffixLength;
461 const EphemeralRange& subtractionRange =
462 PlainTextRange(subtractionStart, subtractionEnd).createRange(*editable);
463 VisibleSelection selection;
464 selection.setWithoutValidation(subtractionRange.startPosition(),
465 subtractionRange.endPosition());
466 const Document* currentDocument = frame().document();
yosin_UTC9 2016/10/12 06:35:37 s/const Document*/Document* const/
yabinh 2016/10/12 07:32:32 Done.
467 frame().selection().setSelection(selection, 0);
468 clear();
469
470 // FrameSeleciton::setSelection() can change document associate to |frame|.
471 if (*currentDocument != *frame().document())
yosin_UTC9 2016/10/12 06:35:38 Remove |*|, we don't need to de-reference for comp
yabinh 2016/10/12 07:32:32 Done.
472 return;
473 if (!currentDocument->focusedElement())
474 return;
475
476 // Append the incremental text.
477 const size_t appendingLength =
478 text.length() - commonPrefixLength - commonSuffixLength;
479 String appendingText = text.substring(commonPrefixLength, appendingLength);
yosin_UTC9 2016/10/12 06:35:37 s/String/const String&/
yabinh 2016/10/12 07:32:33 Done.
480 insertTextDuringCompositionWithEvents(frame(), appendingText,
481 TypingCommand::PreventSpellChecking,
482 TypingCommand::TextCompositionUpdate);
483
484 // Event handlers might destroy document.
485 if (!frame().document())
yosin_UTC9 2016/10/12 06:35:37 No need to checking null document. |currentDocumen
yabinh 2016/10/12 07:32:32 Done.
486 return;
487 if (*currentDocument != *frame().document())
488 return;
489
490 // TODO(yosin): The use of updateStyleAndLayoutIgnorePendingStylesheets
491 // needs to be audited. see http://crbug.com/590369 for more details.
492 frame().document()->updateStyleAndLayoutIgnorePendingStylesheets();
493
494 // Now recreate the composition starting at its original start, and
495 // apply the specified final selection offsets.
496 setCompositionFromExistingText(underlines, compositionStart,
497 compositionStart + text.length());
498 }
499
500 selectComposition();
501 const PlainTextRange& selectedRange = createSelectionRangeForSetComposition(
502 selectionStart, selectionEnd, text.length());
503 // We shouldn't close typing in the middle of setComposition.
504 setEditableSelectionOffsets(selectedRange, NotUserTriggered);
505 m_isDirty = true;
506 }
507
360 void InputMethodController::setComposition( 508 void InputMethodController::setComposition(
361 const String& text, 509 const String& text,
362 const Vector<CompositionUnderline>& underlines, 510 const Vector<CompositionUnderline>& underlines,
363 int selectionStart, 511 int selectionStart,
364 int selectionEnd) { 512 int selectionEnd) {
365 Editor::RevealSelectionScope revealSelectionScope(&editor()); 513 Editor::RevealSelectionScope revealSelectionScope(&editor());
366 514
367 // Updates styles before setting selection for composition to prevent 515 // Updates styles before setting selection for composition to prevent
368 // inserting the previous composition text into text nodes oddly. 516 // inserting the previous composition text into text nodes oddly.
369 // See https://bugs.webkit.org/show_bug.cgi?id=46868 517 // See https://bugs.webkit.org/show_bug.cgi?id=46868
370 frame().document()->updateStyleAndLayoutTree(); 518 frame().document()->updateStyleAndLayoutTree();
371 519
520 // When the IME only wants to change a few characters at the end of the
521 // composition, only touch those characters in order to preserve rich text
522 // substructure.
523 if (hasComposition() && text.length()) {
524 return setCompositionWithIncrementalText(text, underlines, selectionStart,
525 selectionEnd);
526 }
527
372 selectComposition(); 528 selectComposition();
373 529
374 if (frame().selection().isNone()) 530 if (frame().selection().isNone())
375 return; 531 return;
376 532
377 Element* target = frame().document()->focusedElement(); 533 Element* target = frame().document()->focusedElement();
378 if (!target) 534 if (!target)
379 return; 535 return;
380 536
381 // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets 537 PlainTextRange selectedRange = createSelectionRangeForSetComposition(
382 // needs to be audited. see http://crbug.com/590369 for more details. 538 selectionStart, selectionEnd, text.length());
383 frame().document()->updateStyleAndLayoutIgnorePendingStylesheets();
384
385 int selectionOffsetsStart = static_cast<int>(getSelectionOffsets().start());
386 int start = selectionOffsetsStart + selectionStart;
387 int end = selectionOffsetsStart + selectionEnd;
388 PlainTextRange selectedRange =
389 createRangeForSelection(start, end, text.length());
390 539
391 // Dispatch an appropriate composition event to the focused node. 540 // Dispatch an appropriate composition event to the focused node.
392 // We check the composition status and choose an appropriate composition event 541 // We check the composition status and choose an appropriate composition event
393 // since this function is used for three purposes: 542 // since this function is used for three purposes:
394 // 1. Starting a new composition. 543 // 1. Starting a new composition.
395 // Send a compositionstart and a compositionupdate event when this function 544 // Send a compositionstart and a compositionupdate event when this function
396 // creates a new composition node, i.e. !hasComposition() && 545 // creates a new composition node, i.e. !hasComposition() &&
397 // !text.isEmpty(). 546 // !text.isEmpty().
398 // Sending a compositionupdate event at this time ensures that at least one 547 // Sending a compositionupdate event at this time ensures that at least one
399 // compositionupdate event is dispatched. 548 // compositionupdate event is dispatched.
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after
496 EphemeralRange ephemeralLineRange = EphemeralRange( 645 EphemeralRange ephemeralLineRange = EphemeralRange(
497 Position(baseNode, underlineStart), Position(baseNode, underlineEnd)); 646 Position(baseNode, underlineStart), Position(baseNode, underlineEnd));
498 if (ephemeralLineRange.isNull()) 647 if (ephemeralLineRange.isNull())
499 continue; 648 continue;
500 frame().document()->markers().addCompositionMarker( 649 frame().document()->markers().addCompositionMarker(
501 ephemeralLineRange.startPosition(), ephemeralLineRange.endPosition(), 650 ephemeralLineRange.startPosition(), ephemeralLineRange.endPosition(),
502 underline.color(), underline.thick(), underline.backgroundColor()); 651 underline.color(), underline.thick(), underline.backgroundColor());
503 } 652 }
504 } 653 }
505 654
655 PlainTextRange InputMethodController::createSelectionRangeForSetComposition(
656 int selectionStart,
657 int selectionEnd,
658 size_t textLength) const {
659 // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets
660 // needs to be audited. see http://crbug.com/590369 for more details.
661 frame().document()->updateStyleAndLayoutIgnorePendingStylesheets();
yosin_UTC9 2016/10/12 06:35:37 This code fragment should be in callers rather tha
yabinh 2016/10/12 07:32:33 Done.
662
663 const int selectionOffsetsStart =
664 static_cast<int>(getSelectionOffsets().start());
665 int start = selectionOffsetsStart + selectionStart;
yosin_UTC9 2016/10/12 06:35:37 s/int/const int/
yabinh 2016/10/12 07:32:32 Done.
666 int end = selectionOffsetsStart + selectionEnd;
yosin_UTC9 2016/10/12 06:35:37 s/int/const int/
yabinh 2016/10/12 07:32:32 Done.
667 return createRangeForSelection(start, end, textLength);
668 }
669
506 void InputMethodController::setCompositionFromExistingText( 670 void InputMethodController::setCompositionFromExistingText(
507 const Vector<CompositionUnderline>& underlines, 671 const Vector<CompositionUnderline>& underlines,
508 unsigned compositionStart, 672 unsigned compositionStart,
509 unsigned compositionEnd) { 673 unsigned compositionEnd) {
510 Element* editable = frame().selection().rootEditableElement(); 674 Element* editable = frame().selection().rootEditableElement();
511 if (!editable) 675 if (!editable)
512 return; 676 return;
513 677
514 DCHECK(!editable->document().needsLayoutTreeUpdate()); 678 DCHECK(!editable->document().needsLayoutTreeUpdate());
515 679
(...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after
684 new RangeVector(1, m_frame->selection().firstRange())); 848 new RangeVector(1, m_frame->selection().firstRange()));
685 TypingCommand::deleteSelection(*frame().document()); 849 TypingCommand::deleteSelection(*frame().document());
686 } 850 }
687 851
688 DEFINE_TRACE(InputMethodController) { 852 DEFINE_TRACE(InputMethodController) {
689 visitor->trace(m_frame); 853 visitor->trace(m_frame);
690 visitor->trace(m_compositionRange); 854 visitor->trace(m_compositionRange);
691 } 855 }
692 856
693 } // namespace blink 857 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698