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

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

Issue 2372493002: Workaround for setComposition styling clobber (Closed)
Patch Set: Remove a DCHECK. 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 return maxCommonPrefixLength;
368 }
369
370 static size_t computeCommonSuffixLength(const String& str1,
371 const String& str2) {
372 const size_t length1 = str1.length();
373 const size_t length2 = str2.length();
374 const size_t maxCommonSuffixLength = std::min(length1, length2);
375 for (size_t index = 0; index < maxCommonSuffixLength; ++index) {
376 if (str1[length1 - index - 1] != str2[length2 - index - 1])
377 return index;
378 }
379 return maxCommonSuffixLength;
380 }
381
yabinh 2016/10/12 09:14:08 Refactor.
382 // If current position is at grapheme boundary, return 0; otherwise, return the
383 // distance to its nearest left grapheme boundary.
384 static size_t computeDistanceToLeftGraphemeBoundary(const Position& position) {
385 const Position& adjustedPosition = previousPositionOf(
386 nextPositionOf(position, PositionMoveType::GraphemeCluster),
387 PositionMoveType::GraphemeCluster);
388 DCHECK_EQ(position.anchorNode(), adjustedPosition.anchorNode());
389 DCHECK_GE(position.computeOffsetInContainerNode(),
390 adjustedPosition.computeOffsetInContainerNode());
391 return static_cast<size_t>(position.computeOffsetInContainerNode() -
392 adjustedPosition.computeOffsetInContainerNode());
393 }
394
395 static size_t computeCommonGraphemeClusterPrefixLengthForSetComposition(
396 const String& oldText,
397 const String& newText,
398 const Element* rootEditableElement) {
399 const size_t commonPrefixLength = computeCommonPrefixLength(oldText, newText);
400
401 // For grapheme cluster, we should adjust it for grapheme boundary.
402 const EphemeralRange& range =
403 PlainTextRange(0, commonPrefixLength).createRange(*rootEditableElement);
404 if (range.isNull())
405 return 0;
406 const Position& position = range.endPosition();
407 const size_t diff = computeDistanceToLeftGraphemeBoundary(position);
408
yosin_UTC9 2016/10/12 09:49:32 nit: Remove an extra blank line.
yabinh 2016/10/12 09:56:43 Done.
409 return commonPrefixLength - diff;
yosin_UTC9 2016/10/12 09:49:32 DCHECK_GE(commonPrefixLength, diff);
yabinh 2016/10/12 09:56:43 Done.
410 }
411
yabinh 2016/10/12 09:14:08 Refactor.
412 // If current position is at grapheme boundary, return 0; otherwise, return the
413 // distance to its nearest right grapheme boundary.
414 static size_t computeDistanceToRightGraphemeBoundary(const Position& position) {
415 const Position& adjustedPosition = nextPositionOf(
416 previousPositionOf(position, PositionMoveType::GraphemeCluster),
417 PositionMoveType::GraphemeCluster);
418 DCHECK_EQ(position.anchorNode(), adjustedPosition.anchorNode());
419 DCHECK_GE(adjustedPosition.computeOffsetInContainerNode(),
420 position.computeOffsetInContainerNode());
421 return static_cast<size_t>(adjustedPosition.computeOffsetInContainerNode() -
422 position.computeOffsetInContainerNode());
423 }
424
425 static size_t computeCommonGraphemeClusterSuffixLengthForSetComposition(
426 const String& oldText,
427 const String& newText,
428 const Element* rootEditableElement) {
429 const size_t commonSuffixLength = computeCommonSuffixLength(oldText, newText);
430
431 // For grapheme cluster, we should adjust it for grapheme boundary.
432 const EphemeralRange& range =
433 PlainTextRange(0, oldText.length() - commonSuffixLength)
434 .createRange(*rootEditableElement);
435 if (range.isNull())
436 return 0;
437 const Position& position = range.endPosition();
438 const size_t diff = computeDistanceToRightGraphemeBoundary(position);
439 return commonSuffixLength - diff;
yosin_UTC9 2016/10/12 09:49:32 DCHECK_GE(commonSuffixLength, diff); Tips: For su
yabinh 2016/10/12 09:56:43 Done.
440 }
441
442 void InputMethodController::setCompositionWithIncrementalText(
443 const String& text,
444 const Vector<CompositionUnderline>& underlines,
445 int selectionStart,
446 int selectionEnd) {
447 Element* editable = frame().selection().rootEditableElement();
448 if (!editable)
449 return;
450
451 DCHECK_LE(selectionStart, selectionEnd);
452 String composing = composingText();
453 const size_t commonPrefixLength =
454 computeCommonGraphemeClusterPrefixLengthForSetComposition(composing, text,
455 editable);
456
457 // We should ignore common prefix when finding common suffix.
458 const size_t commonSuffixLength =
459 computeCommonGraphemeClusterSuffixLengthForSetComposition(
460 composing.right(composing.length() - commonPrefixLength),
461 text.right(text.length() - commonPrefixLength), editable);
462
463 const bool inserting =
464 text.length() > commonPrefixLength + commonSuffixLength;
465 const bool deleting =
466 composing.length() > commonPrefixLength + commonSuffixLength;
467
468 if (inserting || deleting) {
469 // Select the text to be deleted.
470 const size_t compositionStart =
471 PlainTextRange::create(*editable, compositionEphemeralRange()).start();
472 const size_t deletionStart = compositionStart + commonPrefixLength;
473 const size_t deletionEnd =
474 compositionStart + composing.length() - commonSuffixLength;
475 const EphemeralRange& deletionRange =
476 PlainTextRange(deletionStart, deletionEnd).createRange(*editable);
477 VisibleSelection selection;
478 selection.setWithoutValidation(deletionRange.startPosition(),
479 deletionRange.endPosition());
480 Document* const currentDocument = frame().document();
481 frame().selection().setSelection(selection, 0);
482 clear();
483
484 // FrameSeleciton::setSelection() can change document associate to |frame|.
485 if (currentDocument != frame().document())
486 return;
487 if (!currentDocument->focusedElement())
488 return;
489
490 // Insert the incremental text.
491 const size_t insertionLength =
492 text.length() - commonPrefixLength - commonSuffixLength;
493 const String& insertingText =
494 text.substring(commonPrefixLength, insertionLength);
495 insertTextDuringCompositionWithEvents(frame(), insertingText,
496 TypingCommand::PreventSpellChecking,
497 TypingCommand::TextCompositionUpdate);
498
499 // Event handlers might destroy document.
500 if (currentDocument != frame().document())
501 return;
502
503 // TODO(yosin): The use of updateStyleAndLayoutIgnorePendingStylesheets
504 // needs to be audited. see http://crbug.com/590369 for more details.
505 frame().document()->updateStyleAndLayoutIgnorePendingStylesheets();
506
507 // Now recreate the composition starting at its original start, and
508 // apply the specified final selection offsets.
509 setCompositionFromExistingText(underlines, compositionStart,
510 compositionStart + text.length());
511 }
512
513 selectComposition();
514
515 // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets
516 // needs to be audited. see http://crbug.com/590369 for more details.
517 frame().document()->updateStyleAndLayoutIgnorePendingStylesheets();
518
519 const PlainTextRange& selectedRange = createSelectionRangeForSetComposition(
520 selectionStart, selectionEnd, text.length());
521 // We shouldn't close typing in the middle of setComposition.
522 setEditableSelectionOffsets(selectedRange, NotUserTriggered);
523 m_isDirty = true;
524 }
525
360 void InputMethodController::setComposition( 526 void InputMethodController::setComposition(
361 const String& text, 527 const String& text,
362 const Vector<CompositionUnderline>& underlines, 528 const Vector<CompositionUnderline>& underlines,
363 int selectionStart, 529 int selectionStart,
364 int selectionEnd) { 530 int selectionEnd) {
365 Editor::RevealSelectionScope revealSelectionScope(&editor()); 531 Editor::RevealSelectionScope revealSelectionScope(&editor());
366 532
367 // Updates styles before setting selection for composition to prevent 533 // Updates styles before setting selection for composition to prevent
368 // inserting the previous composition text into text nodes oddly. 534 // inserting the previous composition text into text nodes oddly.
369 // See https://bugs.webkit.org/show_bug.cgi?id=46868 535 // See https://bugs.webkit.org/show_bug.cgi?id=46868
370 frame().document()->updateStyleAndLayoutTree(); 536 frame().document()->updateStyleAndLayoutTree();
371 537
538 // When the IME only wants to change a few characters at the end of the
539 // composition, only touch those characters in order to preserve rich text
540 // substructure.
541 if (hasComposition() && text.length()) {
542 return setCompositionWithIncrementalText(text, underlines, selectionStart,
543 selectionEnd);
544 }
545
372 selectComposition(); 546 selectComposition();
373 547
374 if (frame().selection().isNone()) 548 if (frame().selection().isNone())
375 return; 549 return;
376 550
377 Element* target = frame().document()->focusedElement(); 551 Element* target = frame().document()->focusedElement();
378 if (!target) 552 if (!target)
379 return; 553 return;
380 554
381 // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets 555 // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets
382 // needs to be audited. see http://crbug.com/590369 for more details. 556 // needs to be audited. see http://crbug.com/590369 for more details.
383 frame().document()->updateStyleAndLayoutIgnorePendingStylesheets(); 557 frame().document()->updateStyleAndLayoutIgnorePendingStylesheets();
384 558
385 int selectionOffsetsStart = static_cast<int>(getSelectionOffsets().start()); 559 PlainTextRange selectedRange = createSelectionRangeForSetComposition(
386 int start = selectionOffsetsStart + selectionStart; 560 selectionStart, selectionEnd, text.length());
387 int end = selectionOffsetsStart + selectionEnd;
388 PlainTextRange selectedRange =
389 createRangeForSelection(start, end, text.length());
390 561
391 // Dispatch an appropriate composition event to the focused node. 562 // Dispatch an appropriate composition event to the focused node.
392 // We check the composition status and choose an appropriate composition event 563 // We check the composition status and choose an appropriate composition event
393 // since this function is used for three purposes: 564 // since this function is used for three purposes:
394 // 1. Starting a new composition. 565 // 1. Starting a new composition.
395 // Send a compositionstart and a compositionupdate event when this function 566 // Send a compositionstart and a compositionupdate event when this function
396 // creates a new composition node, i.e. !hasComposition() && 567 // creates a new composition node, i.e. !hasComposition() &&
397 // !text.isEmpty(). 568 // !text.isEmpty().
398 // Sending a compositionupdate event at this time ensures that at least one 569 // Sending a compositionupdate event at this time ensures that at least one
399 // compositionupdate event is dispatched. 570 // compositionupdate event is dispatched.
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after
496 EphemeralRange ephemeralLineRange = EphemeralRange( 667 EphemeralRange ephemeralLineRange = EphemeralRange(
497 Position(baseNode, underlineStart), Position(baseNode, underlineEnd)); 668 Position(baseNode, underlineStart), Position(baseNode, underlineEnd));
498 if (ephemeralLineRange.isNull()) 669 if (ephemeralLineRange.isNull())
499 continue; 670 continue;
500 frame().document()->markers().addCompositionMarker( 671 frame().document()->markers().addCompositionMarker(
501 ephemeralLineRange.startPosition(), ephemeralLineRange.endPosition(), 672 ephemeralLineRange.startPosition(), ephemeralLineRange.endPosition(),
502 underline.color(), underline.thick(), underline.backgroundColor()); 673 underline.color(), underline.thick(), underline.backgroundColor());
503 } 674 }
504 } 675 }
505 676
677 PlainTextRange InputMethodController::createSelectionRangeForSetComposition(
678 int selectionStart,
679 int selectionEnd,
680 size_t textLength) const {
681 const int selectionOffsetsStart =
682 static_cast<int>(getSelectionOffsets().start());
683 const int start = selectionOffsetsStart + selectionStart;
684 const int end = selectionOffsetsStart + selectionEnd;
685 return createRangeForSelection(start, end, textLength);
686 }
687
506 void InputMethodController::setCompositionFromExistingText( 688 void InputMethodController::setCompositionFromExistingText(
507 const Vector<CompositionUnderline>& underlines, 689 const Vector<CompositionUnderline>& underlines,
508 unsigned compositionStart, 690 unsigned compositionStart,
509 unsigned compositionEnd) { 691 unsigned compositionEnd) {
510 Element* editable = frame().selection().rootEditableElement(); 692 Element* editable = frame().selection().rootEditableElement();
511 if (!editable) 693 if (!editable)
512 return; 694 return;
513 695
514 DCHECK(!editable->document().needsLayoutTreeUpdate()); 696 DCHECK(!editable->document().needsLayoutTreeUpdate());
515 697
(...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after
684 new RangeVector(1, m_frame->selection().firstRange())); 866 new RangeVector(1, m_frame->selection().firstRange()));
685 TypingCommand::deleteSelection(*frame().document()); 867 TypingCommand::deleteSelection(*frame().document());
686 } 868 }
687 869
688 DEFINE_TRACE(InputMethodController) { 870 DEFINE_TRACE(InputMethodController) {
689 visitor->trace(m_frame); 871 visitor->trace(m_frame);
690 visitor->trace(m_compositionRange); 872 visitor->trace(m_compositionRange);
691 } 873 }
692 874
693 } // namespace blink 875 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698