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

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

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

Powered by Google App Engine
This is Rietveld 408576698