Changeset 232635 in webkit


Ignore:
Timestamp:
Jun 8, 2018 1:36:09 PM (6 years ago)
Author:
rniwa@webkit.org
Message:

REGRESSION(macOS Mojave): move-by-word-visually-inline-block-positioned-element.html fails
https://bugs.webkit.org/show_bug.cgi?id=186424

Reviewed by Wenson Hsieh.

The test failure is ultimately caused by the change in ICU's behavior. With the CPU in the latest macOS Mojave,
ubrk_getRuleStatus returns 200 / UBRK_WORD_LETTER at the end of a buffer given to UBreakIterator. This caused
isWordTextBreak to return true instead of false in isLogicalStartOfWord at the end of the buffer.

This ICU behavior shouldn't have caused a problem in theory. However, WebKit had a bug in visualWordPosition which
caused UBreakIterator to not include the succeeding word when traversing words to the left (backwards in LTR text)
at the beginning of the last block element with exactly one line box after an non-statically positioned element.

In this case, visualWordPosition invokes wordBreakIteratorForMaxOffsetBoundary (because adjacentCharacterPosition
is now at the end of the last word in the non-statically positioned element) to setup UBreakIterator. Because
there are no line boxes left in the current line (in the last block element with exactly one line box),
logicallyNextBox enters the while loop and invoke nextRootInlineBoxCandidatePosition to find the next root line box.
However, the visible position given to this function is at the beginning of the first word in the block element.
As a result, nextRootInlineBoxCandidatePosition skips over this entire line and finds no line box after the one
we had in the non-statically positioned element.

Let us consider the following concrete example in which a position: static div is followed by another div, and each
div contains text nodes "hello" and "world" respectively:

  • div position: static (1)
    • "hello"
  • div (2)
    • "world"

Suppose we're at the offset 0 of "world", and trying to move to the left. In this case, adjacentCharacterPosition is
at offset 5 of "world". The next line box should be that of "world". However, because we invoke logicallyNextBox via
wordBreakIteratorForMaxOffsetBoundary with the visible position at offset 0 of "world", it skips this line and return
nullptr.

This patch addresses this test failure by fixing visualWordPosition by passing adjacentCharacterPosition (at offset 5
of "hello") as the visible position to find the next text box so that nextRootInlineBoxCandidatePosition invoked in
logicallyNextBox would not skip the line ("world") from which we started the traversal to find the next line box.

Tests: editing/selection/move-by-word-visually-inline-block-positioned-element.html

  • editing/VisibleUnits.cpp:

(WebCore::visualWordPosition):

Location:
trunk/Source/WebCore
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r232628 r232635  
     12018-06-07  Ryosuke Niwa  <rniwa@webkit.org>
     2
     3        REGRESSION(macOS Mojave): move-by-word-visually-inline-block-positioned-element.html fails
     4        https://bugs.webkit.org/show_bug.cgi?id=186424
     5
     6        Reviewed by Wenson Hsieh.
     7
     8        The test failure is ultimately caused by the change in ICU's behavior. With the CPU in the latest macOS Mojave,
     9        ubrk_getRuleStatus returns 200 / UBRK_WORD_LETTER at the end of a buffer given to UBreakIterator. This caused
     10        isWordTextBreak to return true instead of false in isLogicalStartOfWord at the end of the buffer.
     11
     12        This ICU behavior shouldn't have caused a problem in theory. However, WebKit had a bug in visualWordPosition which
     13        caused UBreakIterator to not include the succeeding word when traversing words to the left (backwards in LTR text)
     14        at the beginning of the last block element with exactly one line box after an non-statically positioned element.
     15
     16        In this case, visualWordPosition invokes wordBreakIteratorForMaxOffsetBoundary (because adjacentCharacterPosition
     17        is now at the end of the last word in the non-statically positioned element) to setup UBreakIterator. Because
     18        there are no line boxes left in the current line (in the last block element with exactly one line box),
     19        logicallyNextBox enters the while loop and invoke nextRootInlineBoxCandidatePosition to find the next root line box.
     20        However, the visible position given to this function is at the beginning of the first word in the block element.
     21        As a result, nextRootInlineBoxCandidatePosition skips over this entire line and finds no line box after the one
     22        we had in the non-statically positioned element.
     23
     24        Let us consider the following concrete example in which a position: static div is followed by another div, and each
     25        div contains text nodes "hello" and "world" respectively:
     26        - div position: static (1)
     27            - "hello"
     28        - div (2)
     29            - "world"
     30        Suppose we're at the offset 0 of "world", and trying to move to the left. In this case, adjacentCharacterPosition is
     31        at offset 5 of "world". The next line box should be that of "world". However, because we invoke logicallyNextBox via
     32        wordBreakIteratorForMaxOffsetBoundary with the visible position at offset 0 of "world", it skips this line and return
     33        nullptr.
     34
     35        This patch addresses this test failure by fixing visualWordPosition by passing adjacentCharacterPosition (at offset 5
     36        of "hello") as the visible position to find the next text box so that nextRootInlineBoxCandidatePosition invoked in
     37        logicallyNextBox would not skip the line ("world") from which we started the traversal to find the next line box.
     38
     39        Tests: editing/selection/move-by-word-visually-inline-block-positioned-element.html
     40
     41        * editing/VisibleUnits.cpp:
     42        (WebCore::visualWordPosition):
     43
    1442018-06-08  Brent Fulgham  <bfulgham@apple.com>
    245
  • trunk/Source/WebCore/editing/VisibleUnits.cpp

    r232178 r232635  
    385385
    386386        if (offsetInBox == box->caretMinOffset())
    387             iter = wordBreakIteratorForMinOffsetBoundary(visiblePosition, &textBox, previousBoxLength, previousBoxInDifferentBlock, string, leafBoxes);
     387            iter = wordBreakIteratorForMinOffsetBoundary(adjacentCharacterPosition, &textBox, previousBoxLength, previousBoxInDifferentBlock, string, leafBoxes);
    388388        else if (offsetInBox == box->caretMaxOffset())
    389             iter = wordBreakIteratorForMaxOffsetBoundary(visiblePosition, &textBox, nextBoxInDifferentBlock, string, leafBoxes);
     389            iter = wordBreakIteratorForMaxOffsetBoundary(adjacentCharacterPosition, &textBox, nextBoxInDifferentBlock, string, leafBoxes);
    390390        else if (movingIntoNewBox) {
    391391            iter = wordBreakIterator(StringView(textBox.renderer().text()).substring(textBox.start(), textBox.len()));
Note: See TracChangeset for help on using the changeset viewer.