Changeset 110965 in webkit


Ignore:
Timestamp:
Mar 16, 2012 1:27:28 AM (12 years ago)
Author:
xji@chromium.org
Message:

Using ICU break iterator to simplify visual word movement implementation.
https://bugs.webkit.org/show_bug.cgi?id=78856

Reviewed by Ryosuke Niwa.

Source/WebCore:

This patch relies on ICU word break iterator and cursor visual movement by character to get the word break
position in visual order. It reduces the complexity of old implementation.

Test: editing/selection/move-by-word-visually-wrong-left-right.html

  • editing/FrameSelection.cpp: Exclude WinCE from visual word movement since isWordTextBreak is not implemented.

(WebCore::FrameSelection::modifyMovingRight):
(WebCore::FrameSelection::modifyMovingLeft):

  • editing/visible_units.cpp:

(WebCore):
(WebCore::previousLeafWithSameEditability): Just moving to the top without functionality change.
(WebCore::enclosingNodeWithNonInlineRenderer): ditto.
(WebCore::nextLeafWithSameEditability): ditto.
(WebCore::previousRootInlineBox): return previous RootInlineBox which is in different renderer.
(WebCore::nextRootInlineBox): return next RootInlineBox which is in different renderer.
(WebCore::boxIndexInVector):
(WebCore::previousBoxInLine): returns logically previous box in one line.
(WebCore::logicallyPreviousBox): returns logically previous box.
(WebCore::nextBoxInLine): returns logically next box in one line.
(WebCore::logicallyNextBox): returns logically next box.
(WebCore::wordBreakIteratorForMinOffsetBoundary): create word break iterator for position that is a box's min offset.
(WebCore::wordBreakIteratorForMaxOffsetBoundary): create word break iterator for position that is a box's max offset.
(WebCore::isLogicalStartOfWord): return whether a position is logically start of word.
(WebCore::islogicalEndOfWord): return whether a position is logically end of word.
(WebCore::visualWordPosition): returns the visual left or right word position.
(WebCore::leftWordPosition):
(WebCore::rightWordPosition):

  • platform/text/TextBreakIterator.h: Add isWordTextBreak().

(WebCore):

  • platform/text/TextBreakIteratorICU.cpp:

(WebCore::isWordTextBreak):
(WebCore):

  • platform/text/gtk/TextBreakIteratorGtk.cpp:

(WebCore::isWordTextBreak):
(WebCore):

  • platform/text/qt/TextBreakIteratorQt.cpp:

(WebCore::isWordTextBreak):
(WebCore):

  • platform/text/wince/TextBreakIteratorWinCE.cpp:

(WebCore::isWordTextBreak):
(WebCore):

LayoutTests:

  • editing/selection/move-by-word-visually-inline-block-positioned-element-expected.txt:
  • editing/selection/move-by-word-visually-inline-block-positioned-element.html: The word break stops at the beginning of fixed positioned element, which is correct and consistent behavior.
  • editing/selection/move-by-word-visually-multi-line-expected.txt:
  • editing/selection/move-by-word-visually-multi-line.html: The word break does not stop at the end of a text if there is next line of text available under the same editable root, which is a consistent behavior cross the board. Add more test cases, including one with non InlineTextBox.
  • editing/selection/move-by-word-visually-single-space-inline-element-expected.txt: Fixing of previously missing or extra word break positions.
  • editing/selection/move-by-word-visually-single-space-one-element-expected.txt:
  • editing/selection/move-by-word-visually-single-space-one-element.html: Add a test case including bidi control character which renders wrong result since right-arrow could not reach a position which is the word break position. Add another test case containing non InlineTextBox.
  • editing/selection/move-by-word-visually-wrong-left-right-expected.txt: Added.
  • editing/selection/move-by-word-visually-wrong-left-right.html: Added. Add a test case which renders wrong result due to left/right-arrow returns wrong result.
  • editing/selection/resources/move-by-word-visually.js:

(moveByWordOnEveryChar): Handle a special case when left/right arrow missing certain position.

Location:
trunk
Files:
2 added
17 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r110961 r110965  
     12012-03-16  Xiaomei Ji  <xji@chromium.org>
     2
     3        Using ICU break iterator to simplify visual word movement implementation.
     4        https://bugs.webkit.org/show_bug.cgi?id=78856
     5
     6        Reviewed by Ryosuke Niwa.
     7
     8        * editing/selection/move-by-word-visually-inline-block-positioned-element-expected.txt:
     9        * editing/selection/move-by-word-visually-inline-block-positioned-element.html:
     10          The word break stops at the beginning of fixed positioned element, which is correct and consistent behavior.
     11
     12        * editing/selection/move-by-word-visually-multi-line-expected.txt:
     13        * editing/selection/move-by-word-visually-multi-line.html:
     14          The word break does not stop at the end of a text if there is next line of text available under the same
     15          editable root, which is a consistent behavior cross the board.
     16          Add more test cases, including one with non InlineTextBox.
     17
     18        * editing/selection/move-by-word-visually-single-space-inline-element-expected.txt:
     19          Fixing of previously missing or extra word break positions.
     20
     21        * editing/selection/move-by-word-visually-single-space-one-element-expected.txt:
     22        * editing/selection/move-by-word-visually-single-space-one-element.html:
     23          Add a test case including bidi control character which renders wrong result since right-arrow could not reach
     24          a position which is the word break position.
     25          Add another test case containing non InlineTextBox.
     26
     27        * editing/selection/move-by-word-visually-wrong-left-right-expected.txt: Added.
     28        * editing/selection/move-by-word-visually-wrong-left-right.html: Added.
     29          Add a test case which renders wrong result due to left/right-arrow returns wrong result.
     30
     31        * editing/selection/resources/move-by-word-visually.js:
     32        (moveByWordOnEveryChar): Handle a special case when left/right arrow missing certain position.
     33
    1342012-03-16  Mike Reed  <reed@google.com>
    235
  • trunk/LayoutTests/editing/selection/move-by-word-visually-inline-block-positioned-element-expected.txt

    r101430 r110965  
    33Test 1, LTR:
    44Move right by one word
    5 "begin start"[0, 6], "abc def"[0, 4], "end ing"[0, 4], "this is float"[0, 5, 8], "this is fixed"[5, 8], "this is relative"[0, 5, 8], "this is absolute"[0, 5, 8, 16]
     5"begin start"[0, 6], "abc def"[0, 4], "end ing"[0, 4], "this is float"[0, 5, 8], "this is fixed"[0, 5, 8], "this is relative"[0, 5, 8], "this is absolute"[0, 5, 8, 16]
    66Move left by one word
    7 "this is absolute"[16, 8, 5, 0], "this is relative"[8, 5, 0], "this is fixed"[8, 5], "this is float"[8, 5, 0], "end ing"[4, 0], "abc def"[4, 0], "begin start"[6, 0]
     7"this is absolute"[16, 8, 5, 0], "this is relative"[8, 5, 0], "this is fixed"[8, 5, 0], "this is float"[8, 5, 0], "end ing"[4, 0], "abc def"[4, 0], "begin start"[6, 0]
    88Test 2, LTR:
    99Move right by one word
    10 "abc def"[0, 4], "end ing"[0, 4], "this is float"[0, 5, 8], "this is fixed"[5, 8], "this is relative"[0, 5, 8], "this is absolute"[0, 5, 8, 16]
     10"abc def"[0, 4], "end ing"[0, 4], "this is float"[0, 5, 8], "this is fixed"[0, 5, 8], "this is relative"[0, 5, 8], "this is absolute"[0, 5, 8, 16]
    1111Move left by one word
    12 "this is absolute"[16, 8, 5, 0], "this is relative"[8, 5, 0], "this is fixed"[8, 5], "this is float"[8, 5, 0], "end ing"[4, 0], "abc def"[4, 0], "begin start"[6, 0]
     12"this is absolute"[16, 8, 5, 0], "this is relative"[8, 5, 0], "this is fixed"[8, 5, 0], "this is float"[8, 5, 0], "end ing"[4, 0], "abc def"[4, 0], "begin start"[6, 0]
    1313Test 3, LTR:
    1414Move right by one word
    15 "end ing"[0, 4], "this is float"[0, 5, 8], "this is fixed"[5, 8], "this is relative"[0, 5, 8], "this is absolute"[0, 5, 8, 16]
     15"end ing"[0, 4], "this is float"[0, 5, 8], "this is fixed"[0, 5, 8], "this is relative"[0, 5, 8], "this is absolute"[0, 5, 8, 16]
    1616Move left by one word
    17 "this is absolute"[16, 8, 5, 0], "this is relative"[8, 5, 0], "this is fixed"[8, 5], "this is float"[8, 5, 0], "end ing"[4, 0], "abc def"[4, 0], "begin start"[6, 0]
     17"this is absolute"[16, 8, 5, 0], "this is relative"[8, 5, 0], "this is fixed"[8, 5, 0], "this is float"[8, 5, 0], "end ing"[4, 0], "abc def"[4, 0], "begin start"[6, 0]
    1818Test 4, LTR:
    1919Move right by one word
    20 "this is float"[0, 5, 8], "this is fixed"[5, 8], "this is relative"[0, 5, 8], "this is absolute"[0, 5, 8, 16]
     20"this is float"[0, 5, 8], "this is fixed"[0, 5, 8], "this is relative"[0, 5, 8], "this is absolute"[0, 5, 8, 16]
    2121Move left by one word
    22 "this is absolute"[16, 8, 5, 0], "this is relative"[8, 5, 0], "this is fixed"[8, 5], "this is float"[8, 5, 0], "end ing"[4, 0], "abc def"[4, 0], "begin start"[6, 0]
     22"this is absolute"[16, 8, 5, 0], "this is relative"[8, 5, 0], "this is fixed"[8, 5, 0], "this is float"[8, 5, 0], "end ing"[4, 0], "abc def"[4, 0], "begin start"[6, 0]
    2323Test 5, LTR:
    2424Move right by one word
    2525"this is fixed"[0, 5, 8], "this is relative"[0, 5, 8], "this is absolute"[0, 5, 8, 16]
    2626Move left by one word
    27 "this is absolute"[16, 8, 5, 0], "this is relative"[8, 5, 0], "this is fixed"[8, 5], "this is float"[8, 5, 0], "end ing"[4, 0], "abc def"[4, 0], "begin start"[6, 0]
     27"this is absolute"[16, 8, 5, 0], "this is relative"[8, 5, 0], "this is fixed"[8, 5, 0], "this is float"[8, 5, 0], "end ing"[4, 0], "abc def"[4, 0], "begin start"[6, 0]
    2828Test 6, LTR:
    2929Move right by one word
    3030"this is relative"[0, 5, 8], "this is absolute"[0, 5, 8, 16]
    3131Move left by one word
    32 "this is absolute"[16, 8, 5, 0], "this is relative"[8, 5, 0], "this is fixed"[8, 5], "this is float"[8, 5, 0], "end ing"[4, 0], "abc def"[4, 0], "begin start"[6, 0]
     32"this is absolute"[16, 8, 5, 0], "this is relative"[8, 5, 0], "this is fixed"[8, 5, 0], "this is float"[8, 5, 0], "end ing"[4, 0], "abc def"[4, 0], "begin start"[6, 0]
    3333Test 7, LTR:
    3434Move right by one word
    3535"this is absolute"[0, 5, 8, 16]
    3636Move left by one word
    37 "this is absolute"[16, 8, 5, 0], "this is relative"[8, 5, 0], "this is fixed"[8, 5], "this is float"[8, 5, 0], "end ing"[4, 0], "abc def"[4, 0], "begin start"[6, 0]
     37"this is absolute"[16, 8, 5, 0], "this is relative"[8, 5, 0], "this is fixed"[8, 5, 0], "this is float"[8, 5, 0], "end ing"[4, 0], "abc def"[4, 0], "begin start"[6, 0]
    3838
  • trunk/LayoutTests/editing/selection/move-by-word-visually-inline-block-positioned-element.html

    r102252 r110965  
    1818<div id="testMoveByWord" contenteditable style="width:2000px; height:2000px">
    1919<div id="d_1" dir=ltr
    20 title="[d_1, 0][d_1, 6][d_2, 0][d_2, 4][d_3, 0][d_3, 4][d_4, 0][d_4, 5][d_4, 8][d_5, 5][d_5, 8][d_6, 0][d_6, 5][d_6, 8][d_7, 0][d_7, 5][d_7, 8][d_7, 16]|
    21 [d_7, 16][d_7, 8][d_7, 5][d_7, 0][d_6, 8][d_6, 5][d_6, 0][d_5, 8][d_5, 5][d_4, 8][d_4, 5][d_4, 0][d_3, 4][d_3, 0][d_2, 4][d_2, 0][d_1, 6][d_1, 0]"
     20title="[d_1, 0][d_1, 6][d_2, 0][d_2, 4][d_3, 0][d_3, 4][d_4, 0][d_4, 5][d_4, 8][d_5, 0][d_5, 5][d_5, 8][d_6, 0][d_6, 5][d_6, 8][d_7, 0][d_7, 5][d_7, 8][d_7, 16]|
     21[d_7, 16][d_7, 8][d_7, 5][d_7, 0][d_6, 8][d_6, 5][d_6, 0][d_5, 8][d_5, 5][d_5, 0][d_4, 8][d_4, 5][d_4, 0][d_3, 4][d_3, 0][d_2, 4][d_2, 0][d_1, 6][d_1, 0]"
    2222class="test_move_by_word">begin start</div>
    2323
    2424<div id="d_2" dir=ltr
    25 title="[d_2, 0][d_2, 4][d_3, 0][d_3, 4][d_4, 0][d_4, 5][d_4, 8][d_5, 5][d_5, 8][d_6, 0][d_6, 5][d_6, 8][d_7, 0][d_7, 5][d_7, 8][d_7, 16]|
    26 [d_7, 16][d_7, 8][d_7, 5][d_7, 0][d_6, 8][d_6, 5][d_6, 0][d_5, 8][d_5, 5][d_4, 8][d_4, 5][d_4, 0][d_3, 4][d_3, 0][d_2, 4][d_2, 0][d_1, 6][d_1, 0]"
     25title="[d_2, 0][d_2, 4][d_3, 0][d_3, 4][d_4, 0][d_4, 5][d_4, 8][d_5, 0][d_5, 5][d_5, 8][d_6, 0][d_6, 5][d_6, 8][d_7, 0][d_7, 5][d_7, 8][d_7, 16]|
     26[d_7, 16][d_7, 8][d_7, 5][d_7, 0][d_6, 8][d_6, 5][d_6, 0][d_5, 8][d_5, 5][d_5, 0][d_4, 8][d_4, 5][d_4, 0][d_3, 4][d_3, 0][d_2, 4][d_2, 0][d_1, 6][d_1, 0]"
    2727class="test_move_by_word" style="display:inline-block">abc def</div>
    2828
    2929<div id="d_3" dir=ltr
    30 title="[d_3, 0][d_3, 4][d_4, 0][d_4, 5][d_4, 8][d_5, 5][d_5, 8][d_6, 0][d_6, 5][d_6, 8][d_7, 0][d_7, 5][d_7, 8][d_7, 16]|
    31 [d_7, 16][d_7, 8][d_7, 5][d_7, 0][d_6, 8][d_6, 5][d_6, 0][d_5, 8][d_5, 5][d_4, 8][d_4, 5][d_4, 0][d_3, 4][d_3, 0][d_2, 4][d_2, 0][d_1, 6][d_1, 0]"
     30title="[d_3, 0][d_3, 4][d_4, 0][d_4, 5][d_4, 8][d_5, 0][d_5, 5][d_5, 8][d_6, 0][d_6, 5][d_6, 8][d_7, 0][d_7, 5][d_7, 8][d_7, 16]|
     31[d_7, 16][d_7, 8][d_7, 5][d_7, 0][d_6, 8][d_6, 5][d_6, 0][d_5, 8][d_5, 5][d_5, 0][d_4, 8][d_4, 5][d_4, 0][d_3, 4][d_3, 0][d_2, 4][d_2, 0][d_1, 6][d_1, 0]"
    3232class="test_move_by_word">end ing</div>
    3333
    3434<div id="d_4" dir=ltr
    35 title="[d_4, 0][d_4, 5][d_4, 8][d_5, 5][d_5, 8][d_6, 0][d_6, 5][d_6, 8][d_7, 0][d_7, 5][d_7, 8][d_7, 16]|
    36 [d_7, 16][d_7, 8][d_7, 5][d_7, 0][d_6, 8][d_6, 5][d_6, 0][d_5, 8][d_5, 5][d_4, 8][d_4, 5][d_4, 0][d_3, 4][d_3, 0][d_2, 4][d_2, 0][d_1, 6][d_1, 0]"
     35title="[d_4, 0][d_4, 5][d_4, 8][d_5, 0][d_5, 5][d_5, 8][d_6, 0][d_6, 5][d_6, 8][d_7, 0][d_7, 5][d_7, 8][d_7, 16]|
     36[d_7, 16][d_7, 8][d_7, 5][d_7, 0][d_6, 8][d_6, 5][d_6, 0][d_5, 8][d_5, 5][d_5, 0][d_4, 8][d_4, 5][d_4, 0][d_3, 4][d_3, 0][d_2, 4][d_2, 0][d_1, 6][d_1, 0]"
    3737class="test_move_by_word" style="float:left">this is float</div>
    3838
    3939<div id="d_5" dir=ltr
    4040title="[d_5, 0][d_5, 5][d_5, 8][d_6, 0][d_6, 5][d_6, 8][d_7, 0][d_7, 5][d_7, 8][d_7, 16]|
    41 [d_7, 16][d_7, 8][d_7, 5][d_7, 0][d_6, 8][d_6, 5][d_6, 0][d_5, 8][d_5, 5][d_4, 8][d_4, 5][d_4, 0][d_3, 4][d_3, 0][d_2, 4][d_2, 0][d_1, 6][d_1, 0]"
     41[d_7, 16][d_7, 8][d_7, 5][d_7, 0][d_6, 8][d_6, 5][d_6, 0][d_5, 8][d_5, 5][d_5, 0][d_4, 8][d_4, 5][d_4, 0][d_3, 4][d_3, 0][d_2, 4][d_2, 0][d_1, 6][d_1, 0]"
    4242class="test_move_by_word" style="position:fixed; top:30px; right:5px">this is fixed</div>
    4343
    4444<div id="d_6" dir=ltr
    4545title="[d_6, 0][d_6, 5][d_6, 8][d_7, 0][d_7, 5][d_7, 8][d_7, 16]|
    46 [d_7, 16][d_7, 8][d_7, 5][d_7, 0][d_6, 8][d_6, 5][d_6, 0][d_5, 8][d_5, 5][d_4, 8][d_4, 5][d_4, 0][d_3, 4][d_3, 0][d_2, 4][d_2, 0][d_1, 6][d_1, 0]"
     46[d_7, 16][d_7, 8][d_7, 5][d_7, 0][d_6, 8][d_6, 5][d_6, 0][d_5, 8][d_5, 5][d_5, 0][d_4, 8][d_4, 5][d_4, 0][d_3, 4][d_3, 0][d_2, 4][d_2, 0][d_1, 6][d_1, 0]"
    4747class="test_move_by_word" style="position:relative; left:20px">this is relative</div>
    4848
    4949<div id="d_7" dir=ltr
    5050title="[d_7, 0][d_7, 5][d_7, 8][d_7, 16]|
    51 [d_7, 16][d_7, 8][d_7, 5][d_7, 0][d_6, 8][d_6, 5][d_6, 0][d_5, 8][d_5, 5][d_4, 8][d_4, 5][d_4, 0][d_3, 4][d_3, 0][d_2, 4][d_2, 0][d_1, 6][d_1, 0]"
     51[d_7, 16][d_7, 8][d_7, 5][d_7, 0][d_6, 8][d_6, 5][d_6, 0][d_5, 8][d_5, 5][d_5, 0][d_4, 8][d_4, 5][d_4, 0][d_3, 4][d_3, 0][d_2, 4][d_2, 0][d_1, 6][d_1, 0]"
    5252class="test_move_by_word" style="position:absolute; left:100px; top:150px">this is absolute</div>
    5353
  • trunk/LayoutTests/editing/selection/move-by-word-visually-multi-line-expected.txt

    r101430 r110965  
    3838Test 8, RTL:
    3939Move left by one word
    40 "abc def ghi jkl mn "[0, 3, 8, 11, 16, 18], "opq rst uvw xyz"[0, 3, 8, 11, 15]    FAIL expected: ["abc def ghi jkl mn "[ 0,  3,  8,  11,  16, ]"opq rst uvw xyz"[ 0,  3,  8,  11,  15]
    41 "abc def ghi jkl mn "[16, 18]   FAIL expected "opq rst uvw xyz"[ 0]
    42 "abc def ghi jkl mn "[17, 18]   FAIL expected "opq rst uvw xyz"[ 0]
     40"abc def ghi jkl mn "[0, 3, 8, 11, 16], "opq rst uvw xyz"[0, 3, 8, 11, 15]
    4341Move right by one word
    44 "opq rst uvw xyz"[15, 11, 8, 3, 0], "abc def ghi jkl mn "[18, 16, 11, 8, 3, 0]
     42"opq rst uvw xyz"[15, 11, 8, 3, 0], "abc def ghi jkl mn "[16, 11, 8, 3, 0]
    4543Test 9, RTL:
    4644Move left by one word
     
    6058Test 12, RTL:
    6159Move left by one word
    62 " abc def AAA AAA hij AAA AAA uvw xyz "[1, 4, 9, 13, 17, 21, 25, 29, 33, 36], "AAA kj AAA mn opq AAA AAA"[0, 4, 7, 11, 14, 18, 22, 25]    FAIL expected: [" abc def AAA AAA hij AAA AAA uvw xyz "[ 1,  4,  9,  13,  17,  21,  25,  29,  33, ]"AAA kj AAA mn opq AAA AAA"[ 0,  4,  7,  11,  14,  18,  22,  25]
    63 " abc def AAA AAA hij AAA AAA uvw xyz "[33, 36]   FAIL expected "AAA kj AAA mn opq AAA AAA"[ 0]
    64 " abc def AAA AAA hij AAA AAA uvw xyz "[35, 36]   FAIL expected "AAA kj AAA mn opq AAA AAA"[ 0]
    65 " abc def AAA AAA hij AAA AAA uvw xyz "[34, 36]   FAIL expected "AAA kj AAA mn opq AAA AAA"[ 0]
     60" abc def AAA AAA hij AAA AAA uvw xyz "[1, 4, 9, 13, 17, 21, 25, 29, 33], "AAA kj AAA mn opq AAA AAA"[0, 4, 7, 11, 14, 18, 22, 25]
    6661Move right by one word
    67 "AAA kj AAA mn opq AAA AAA"[25, 22, 18, 14, 11, 7, 4, 0], " abc def AAA AAA hij AAA AAA uvw xyz "[36, 33, 29, 25, 21, 17, 13, 9, 4, 1]
     62"AAA kj AAA mn opq AAA AAA"[25, 22, 18, 14, 11, 7, 4, 0], " abc def AAA AAA hij AAA AAA uvw xyz "[33, 29, 25, 21, 17, 13, 9, 4, 1]
    6863Test 13, LTR:
    6964Move right by one word
    70 "abc def "[0, 4]
     65"abc def ghi jkl mn "[0, 4, 8, 12, 16], "opq rst uvw xyz"[0, 4, 8, 12, 15]
     66Move left by one word
     67"opq rst uvw xyz"[15, 12, 8, 4, 0], "abc def ghi jkl mn "[16, 12, 8, 4, 0]
     68Test 14, LTR:
     69Move right by one word
     70"abc def ghi jkl mn "[0, 4, 8, 12, 16, 18]
     71Move left by one word
     72"abc def ghi jkl mn "[18, 16, 12, 8, 4, 0]
     73Test 15, LTR:
     74Move right by one word
     75"abc def ghi jkl mn "[0, 4, 8, 12, 16], "opq rst uvw xyz"[0, 4, 8, 12, 15]
     76Move left by one word
     77"opq rst uvw xyz"[15, 12, 8, 4, 0], "abc def ghi jkl mn "[16, 12, 8, 4, 0]
     78Test 16, LTR:
     79Move right by one word
     80"abc def "[0, 4, 8]
    7181Move left by one word
    7282" hij opq"[8, 5, 1]
    73 Test 14, LTR:
     83Test 17, LTR:
    7484Move right by one word
    7585<DIV>[0]
    7686Move left by one word
    7787<DIV>[0]
    78 Test 15, LTR:
     88Test 18, LTR:
    7989Move right by one word
    8090"\n00"[3]
  • trunk/LayoutTests/editing/selection/move-by-word-visually-multi-line.html

    r102252 r110965  
    5555<div contenteditable dir=rtl id="ml_7" class="test_move_by_word fix_width" title="[ml_7, 15, 5][ml_7, 11, 5][ml_7, 8, 5][ml_7, 3, 5][ml_7, 0, 5][ml_7, 16][ml_7, 11][ml_7, 8][ml_7, 3][ml_7, 0]|[ml_7, 0][ml_7, 3][ml_7, 8][ml_7, 11][ml_7, 16][ml_7, 0, 5][ml_7, 3, 5][ml_7, 8, 5][ml_7, 11, 5][ml_7, 15, 5]">abc def ghi jkl mn <br/><br/><br/>opq rst uvw xyz</div>
    5656
    57 <div contenteditable dir=rtl id="ml_8" class="test_move_by_word fix_width" title="[ml_8, 15, 5][ml_8, 11, 5][ml_8, 8, 5][ml_8, 3, 5][ml_8, 0, 5][ml_8, 18][ml_8, 16][ml_8, 11][ml_8, 8][ml_8, 3][ml_8, 0]|[ml_8, 0][ml_8, 3][ml_8, 8][ml_8, 11][ml_8, 16][ml_8, 0, 5][ml_8, 3, 5][ml_8, 8, 5][ml_8, 11, 5][ml_8, 15, 5]">abc def ghi jkl mn <div><br/></div><div><br/></div><div><br/></div>opq rst uvw xyz</div>
     57<div contenteditable dir=rtl id="ml_8" class="test_move_by_word fix_width" title="[ml_8, 15, 5][ml_8, 11, 5][ml_8, 8, 5][ml_8, 3, 5][ml_8, 0, 5][ml_8, 16][ml_8, 11][ml_8, 8][ml_8, 3][ml_8, 0]|[ml_8, 0][ml_8, 3][ml_8, 8][ml_8, 11][ml_8, 16][ml_8, 0, 5][ml_8, 3, 5][ml_8, 8, 5][ml_8, 11, 5][ml_8, 15, 5]">abc def ghi jkl mn <div><br/></div><div><br/></div><div><br/></div>opq rst uvw xyz</div>
    5858
    5959<div contenteditable dir=rtl id="ml_9" class="test_move_by_word fix_width" title="[ml_9, 15, 5][ml_9, 12, 5][ml_9, 8, 5][ml_9, 4, 5][ml_9, 0, 5][ml_9, 12][ml_9, 8][ml_9, 4][ml_9, 0]|[ml_9, 0][ml_9, 4][ml_9, 8][ml_9, 12][ml_9, 0, 5][ml_9, 4, 5][ml_9, 8, 5][ml_9, 12, 5][ml_9, 15, 5]">אאא אאא אאא אאא <br/><br/><br/>אאא אאא אאא אאא</div>
     
    6666
    6767<div contenteditable dir=rtl id="ml_12" class="test_move_by_word fix_width" title="
    68 [ml_12, 25, 5][ml_12, 22, 5][ml_12, 18, 5][ml_12, 14, 5][ml_12, 11, 5][ml_12, 7, 5][ml_12, 4, 5][ml_12, 0, 5][ml_12, 36][ml_12, 33][ml_12, 29][ml_12, 25][ml_12, 21][ml_12, 17][ml_12, 13][ml_12, 9][ml_12, 4][ml_12, 1]|[ml_12, 1][ml_12, 4][ml_12, 9][ml_12, 13][ml_12, 17][ml_12, 21][ml_12, 25][ml_12, 29][ml_12, 33][ml_12, 0, 5][ml_12, 4, 5][ml_12, 7, 5][ml_12, 11, 5][ml_12, 14, 5][ml_12, 18, 5][ml_12, 22, 5][ml_12, 25, 5]
     68[ml_12, 25, 5][ml_12, 22, 5][ml_12, 18, 5][ml_12, 14, 5][ml_12, 11, 5][ml_12, 7, 5][ml_12, 4, 5][ml_12, 0, 5][ml_12, 33][ml_12, 29][ml_12, 25][ml_12, 21][ml_12, 17][ml_12, 13][ml_12, 9][ml_12, 4][ml_12, 1]|[ml_12, 1][ml_12, 4][ml_12, 9][ml_12, 13][ml_12, 17][ml_12, 21][ml_12, 25][ml_12, 29][ml_12, 33][ml_12, 0, 5][ml_12, 4, 5][ml_12, 7, 5][ml_12, 11, 5][ml_12, 14, 5][ml_12, 18, 5][ml_12, 22, 5][ml_12, 25, 5]
    6969"> abc def אאא אאא hij אאא אאא uvw xyz <div><br/></div><div><br/></div><div><br/></div>אאא kj אאא mn opq אאא אאא</div>
    7070
     71<div contenteditable dir=ltr id="ml_13" class="test_move_by_word fix_width" title="[ml_13, 0][ml_13, 4][ml_13, 8][ml_13, 12][ml_13, 16][ml_13, 0, 5][ml_13, 4, 5][ml_13, 8, 5][ml_13, 12, 5][ml_13, 15, 5]|[ml_13, 15, 5][ml_13, 12, 5][ml_13, 8, 5][ml_13, 4, 5][ml_13, 0, 5][ml_13, 16][ml_13, 12][ml_13, 8][ml_13, 4][ml_13, 0]">abc def ghi jkl mn <div></div><div></div><div></div>opq rst uvw xyz</div>
     72
     73<div contenteditable dir=ltr id="ml_14" class="test_move_by_word fix_width" title="[ml_14, 0][ml_14, 4][ml_14, 8][ml_14, 12][ml_14, 16][ml_14, 18]|[ml_14, 18][ml_14, 16][ml_14, 12][ml_14, 8][ml_14, 4][ml_14, 0]">abc def ghi jkl mn <div></div><div></div><div></div></div>
     74
     75<div contenteditable dir=ltr id="ml_15" class="test_move_by_word fix_width" title="[ml_15, 0][ml_15, 4][ml_15, 8][ml_15, 12][ml_15, 16][ml_15, 0, 5][ml_15, 4, 5][ml_15, 8, 5][ml_15, 12, 5][ml_15, 15, 5]|[ml_15, 15, 5][ml_15, 12, 5][ml_15, 8, 5][ml_15, 4, 5][ml_15, 0, 5][ml_15, 16][ml_15, 12][ml_15, 8][ml_15, 4][ml_15, 0]">abc def ghi jkl mn <div><img src=../../accessibility/resources/cake.png></div><div></div><div></div>opq rst uvw xyz</div>
     76
    7177<!-- mixed editability -->
    72 <div dir=ltr class="test_move_by_word" title="0 4|8 5 1">abc def <span contenteditable> inside span </span> hij opq</div>
     78<div dir=ltr class="test_move_by_word" title="0 4 8|8 5 1">abc def <span contenteditable> inside span </span> hij opq</div>
    7379
    7480<div class="test_move_by_word" contenteditable dir=ltr title="0|0"></div>
  • trunk/LayoutTests/editing/selection/move-by-word-visually-single-space-inline-element-expected.txt

    r101430 r110965  
    1818Test 4, RTL:
    1919Move left by one word
    20 "abc def "[0], " rst uvw"[4], "hij opq"[3], "abc def "[7, 3], " rst uvw"[8]    FAIL expected: ["abc def "[ 0, ]" rst uvw"[ 4, ]"hij opq"[ 7,  3, ]"abc def "[ 7,  3, ]" rst uvw"[ 8]
    21 " rst uvw"[4], "hij opq"[3]   FAIL expected "hij opq"[ 7]
    22 " rst uvw"[3], "hij opq"[3]   FAIL expected "hij opq"[ 7]
    23 " rst uvw"[2], "hij opq"[3]   FAIL expected "hij opq"[ 7]
    24 " rst uvw"[1], "hij opq"[3]   FAIL expected "hij opq"[ 7]
     20"abc def "[0], " rst uvw"[4], "hij opq"[7, 3], "abc def "[7, 3], " rst uvw"[8]
    2521Move right by one word
    26 " rst uvw"[8], "abc def "[3, 7], "hij opq"[3], " rst uvw"[4], "abc def "[0]    FAIL expected: [" rst uvw"[ 8, ]"abc def "[ 3,  7, ]"hij opq"[ 3,  7, ]" rst uvw"[ 4, ]"abc def "[ 0]
    27 "hij opq"[3], " rst uvw"[4]   FAIL expected "hij opq"[ 7]
    28 "hij opq"[4], " rst uvw"[4]   FAIL expected "hij opq"[ 7]
    29 "hij opq"[5], " rst uvw"[4]   FAIL expected "hij opq"[ 7]
    30 "hij opq"[6], " rst uvw"[4]   FAIL expected "hij opq"[ 7]
     22" rst uvw"[8], "abc def "[3, 7], "hij opq"[3, 7], " rst uvw"[4], "abc def "[0]
    3123Test 5, RTL:
    3224Move left by one word
    33 "abc def "[0], " rst uvw"[4], "hij opq"[3], "abc def "[7, 3], " rst uvw"[8]    FAIL expected: ["abc def "[ 0, ]" rst uvw"[ 4, ]"hij opq"[ 7,  3, ]"abc def "[ 7,  3, ]" rst uvw"[ 8]
    34 " rst uvw"[4], "hij opq"[3]   FAIL expected "hij opq"[ 7]
    35 " rst uvw"[3], "hij opq"[3]   FAIL expected "hij opq"[ 7]
    36 " rst uvw"[2], "hij opq"[3]   FAIL expected "hij opq"[ 7]
    37 " rst uvw"[1], "hij opq"[3]   FAIL expected "hij opq"[ 7]
     25"abc def "[0], " rst uvw"[4], "hij opq"[7, 3], "abc def "[7, 3], " rst uvw"[8]
    3826Move right by one word
    39 " rst uvw"[8], "abc def "[3, 7], "hij opq"[3], " rst uvw"[4], "abc def "[0]    FAIL expected: [" rst uvw"[ 8, ]"abc def "[ 3,  7, ]"hij opq"[ 3,  7, ]" rst uvw"[ 4, ]"abc def "[ 0]
    40 "hij opq"[3], " rst uvw"[4]   FAIL expected "hij opq"[ 7]
    41 "hij opq"[4], " rst uvw"[4]   FAIL expected "hij opq"[ 7]
    42 "hij opq"[5], " rst uvw"[4]   FAIL expected "hij opq"[ 7]
    43 "hij opq"[6], " rst uvw"[4]   FAIL expected "hij opq"[ 7]
     27" rst uvw"[8], "abc def "[3, 7], "hij opq"[3, 7], " rst uvw"[4], "abc def "[0]
    4428Test 6, LTR:
    4529Move right by one word
     
    8973Test 15, RTL:
    9074Move left by one word
    91 "ABD opq DSU "[0, 4, 8, 12], "abc AAA def"[8, 4, 3], "FFZ rst LIG"[4, 8, 11]    FAIL expected: ["ABD opq DSU "[ 0,  4,  8,  12, ]"abc AAA def"[ 4,  3, ]"FFZ rst LIG"[ 4,  8,  11]
    92 "ABD opq DSU "[12], "abc AAA def"[8]   FAIL expected "abc AAA def"[ 4]
    93 "abc AAA def"[10, 8]   FAIL expected "abc AAA def"[ 4]
    94 "abc AAA def"[9, 8]   FAIL expected "abc AAA def"[ 4]
     75"ABD opq DSU "[0, 4, 8, 12], "abc AAA def"[4, 3], "FFZ rst LIG"[4, 8, 11]
    9576Move right by one word
    96 "FFZ rst LIG"[11, 8, 4], "abc AAA def"[3, 4, 8], "ABD opq DSU "[12, 8, 4, 0]    FAIL expected: ["FFZ rst LIG"[ 11,  8,  4, ]"abc AAA def"[ 3,  4, ]"ABD opq DSU "[ 12,  8,  4,  0]
    97 "abc AAA def"[4, 8]   FAIL expected "ABD opq DSU "[ 12]
     77"FFZ rst LIG"[11, 8, 4], "abc AAA def"[3, 4], "ABD opq DSU "[12, 8, 4, 0]
    9878Test 16, LTR:
    9979Move right by one word
    100 "ABD opq DSU "[0, 4], "abc AAA def"[8, 4], "ABD opq DSU "[12, 11], "FFZ rst LIG"[4, 8, 11]    FAIL expected: ["ABD opq DSU "[ 0,  4,  8, ]"abc AAA def"[ 8,  7, ]"ABD opq DSU "[ 12,  11, ]"FFZ rst LIG"[ 4,  8,  11]
    101 "ABD opq DSU "[4], "abc AAA def"[8]   FAIL expected "ABD opq DSU "[ 8]
    102 "ABD opq DSU "[5], "abc AAA def"[8]   FAIL expected "ABD opq DSU "[ 8]
    103 "ABD opq DSU "[6], "abc AAA def"[8]   FAIL expected "ABD opq DSU "[ 8]
    104 "ABD opq DSU "[7], "abc AAA def"[8]   FAIL expected "ABD opq DSU "[ 8]
    105 "abc AAA def"[8, 4]   FAIL expected "abc AAA def"[ 7]
    106 "abc AAA def"[9, 4]   FAIL expected "abc AAA def"[ 7]
    107 "abc AAA def"[10, 4]   FAIL expected "abc AAA def"[ 7]
    108 "abc AAA def"[11, 4]   FAIL expected "abc AAA def"[ 7]
    109 "abc AAA def"[7, 4]   FAIL expected "ABD opq DSU "[ 12]
    110 "abc AAA def"[6, 4]   FAIL expected "ABD opq DSU "[ 12]
    111 "abc AAA def"[5, 4]   FAIL expected "ABD opq DSU "[ 12]
     80"ABD opq DSU "[0, 4, 8], "abc AAA def"[8, 7], "ABD opq DSU "[12, 11], "FFZ rst LIG"[4, 8, 11]
    11281Move left by one word
    113 "FFZ rst LIG"[11, 8, 4], "ABD opq DSU "[11, 12], "abc AAA def"[7, 8], "ABD opq DSU "[4, 0]    FAIL expected: ["FFZ rst LIG"[ 11,  8,  4, ]"ABD opq DSU "[ 11,  12, ]"abc AAA def"[ 7,  8, ]"ABD opq DSU "[ 8,  4,  0]
    114 "abc AAA def"[8], "ABD opq DSU "[4]   FAIL expected "ABD opq DSU "[ 8]
    115 "FFZ rst LIG"[1], "ABD opq DSU "[4]   FAIL expected "ABD opq DSU "[ 8]
    116 "FFZ rst LIG"[2], "ABD opq DSU "[4]   FAIL expected "ABD opq DSU "[ 8]
     82"FFZ rst LIG"[11, 8, 4], "ABD opq DSU "[11, 12], "abc AAA def"[7, 8], "ABD opq DSU "[8, 4, 0]
    11783Test 17, RTL:
    11884Move left by one word
     
    13298Test 20, RTL:
    13399Move left by one word
    134 "ABD opq rst DSU "[0, 4, 7, 12, 16], "abc uvw AAA def lmn"[12, 8, 7, 3], "ABW hij xyz FXX"[4, 7, 12, 15]    FAIL expected: ["ABD opq rst DSU "[ 0,  4,  7,  12,  16, ]"abc uvw AAA def lmn"[ 15,  8,  7,  3, ]"ABW hij xyz FXX"[ 4,  7,  12,  15]
    135 "ABD opq rst DSU "[16], "abc uvw AAA def lmn"[12]   FAIL expected "abc uvw AAA def lmn"[ 15]
    136 "abc uvw AAA def lmn"[18, 12]   FAIL expected "abc uvw AAA def lmn"[ 15]
    137 "abc uvw AAA def lmn"[17, 12]   FAIL expected "abc uvw AAA def lmn"[ 15]
    138 "abc uvw AAA def lmn"[16, 12]   FAIL expected "abc uvw AAA def lmn"[ 15]
    139 "abc uvw AAA def lmn"[15, 12]   FAIL expected "abc uvw AAA def lmn"[ 8]
    140 "abc uvw AAA def lmn"[14, 12]   FAIL expected "abc uvw AAA def lmn"[ 8]
    141 "abc uvw AAA def lmn"[13, 12]   FAIL expected "abc uvw AAA def lmn"[ 8]
     100"ABD opq rst DSU "[0, 4, 7, 12, 16], "abc uvw AAA def lmn"[15, 8, 7, 3], "ABW hij xyz FXX"[4, 7, 12, 15]
    142101Move right by one word
    143102"ABW hij xyz FXX"[15, 12, 7, 4], "abc uvw AAA def lmn"[3, 7, 8, 15], "ABD opq rst DSU "[16, 12, 7, 4, 0]
  • trunk/LayoutTests/editing/selection/move-by-word-visually-single-space-one-element-expected.txt

    r98428 r110965  
    8383Test 17, LTR:
    8484Move right by one word
     85"abc ᪜BAD def᪝ xyz"[0, 8, 14, 17]    FAIL expected: [0, 9, 8, 14, 17]
     86"abc ᪜BAD def᪝ xyz"[0, 8]   FAIL expected 9
     87"abc ᪜BAD def᪝ xyz"[1, 8]   FAIL expected 9
     88"abc ᪜BAD def᪝ xyz"[2, 8]   FAIL expected 9
     89"abc ᪜BAD def᪝ xyz"[3, 8]   FAIL expected 9
     90"abc ᪜BAD def᪝ xyz"[4, 8]   FAIL expected 9
     91Move left by one word
     92"abc ᪜BAD def᪝ xyz"[17, 14, 8, 9, 0]
     93Test 18, LTR:
     94Move right by one word
     95"abc def hij "[0, 4, 8], " opq rst "[1, 5, 8]
     96Move left by one word
     97" opq rst "[8, 5, 1], "abc def hij "[8, 4, 0]
     98Test 19, LTR:
     99Move right by one word
    85100<DIV>[0]
    86101Move left by one word
  • trunk/LayoutTests/editing/selection/move-by-word-visually-single-space-one-element.html

    r102252 r110965  
    6666<div dir=rtl class="test_move_by_word" title="11 8 4 0|0 4 8 11" contenteditable>שנב abc סטז</div>
    6767
     68<!-- Test with Bidi control characters -->
     69<div dir=ltr class="test_move_by_word" id="notReachablePosition" title="0 9 8 14 17|17 14 8 9 0" contenteditable>abc &#x202b;באד def&#x202c; xyz</div>
     70
     71<!-- Test with image -- non-inline-text-box -->
     72<div id="d_1" dir=ltr class="test_move_by_word" contenteditable title="[d_1, 0, 1][d_1, 4, 1][d_1, 8, 1][d_1, 1, 3][d_1, 5, 3][d_1, 8, 3]|[d_1, 8, 3][d_1, 5, 3][d_1, 1, 3][d_1, 8, 1][d_1, 4, 1][d_1, 0, 1]">abc def hij <img src=../../accessibility/resources/cake.png> opq rst </div>
     73
    6874<!-- empty div -->
    6975<div dir=ltr class="test_move_by_word" title="0|0" contenteditable></div>
  • trunk/LayoutTests/editing/selection/resources/move-by-word-visually.js

    r102252 r110965  
    175175    var prevOffset = sel.anchorOffset;
    176176    var prevNode = sel.anchorNode;
     177
     178    // advance is used to special handling the case that arrow key is not able to reach certain position.
     179    // In which case, we will need to manually skip this word break position in order to report correct log.
     180    var advance = true;
    177181
    178182    while (1) {
     
    206210
    207211        position = { node: sel.anchorNode, offset: sel.anchorOffset };
    208         if (wordBreakIndex < wordBreaks.length
     212        if ((wordBreakIndex < wordBreaks.length
    209213            && positionEqualToWordBreak(position, wordBreaks[wordBreakIndex]))
     214            || (test == document.getElementById("notReachablePosition")
     215            && sel.anchorOffset > wordBreaks[wordBreakIndex]
     216            && advance
     217            && searchDirection == "right")) {
    210218            ++wordBreakIndex;
     219            advance = false;
     220        }
    211221
    212222        prevNode = sel.anchorNode;
  • trunk/Source/WebCore/ChangeLog

    r110964 r110965  
     12012-03-16  Xiaomei Ji  <xji@chromium.org>
     2
     3        Using ICU break iterator to simplify visual word movement implementation.
     4        https://bugs.webkit.org/show_bug.cgi?id=78856
     5
     6        Reviewed by Ryosuke Niwa.
     7
     8        This patch relies on ICU word break iterator and cursor visual movement by character to get the word break
     9        position in visual order. It reduces the complexity of old implementation.
     10
     11        Test: editing/selection/move-by-word-visually-wrong-left-right.html
     12
     13        * editing/FrameSelection.cpp: Exclude WinCE from visual word movement since isWordTextBreak is not implemented.
     14        (WebCore::FrameSelection::modifyMovingRight):
     15        (WebCore::FrameSelection::modifyMovingLeft):
     16        * editing/visible_units.cpp:
     17        (WebCore):
     18        (WebCore::previousLeafWithSameEditability): Just moving to the top without functionality change.
     19        (WebCore::enclosingNodeWithNonInlineRenderer): ditto.
     20        (WebCore::nextLeafWithSameEditability): ditto.
     21        (WebCore::previousRootInlineBox): return previous RootInlineBox which is in different renderer.
     22        (WebCore::nextRootInlineBox): return next RootInlineBox which is in different renderer.
     23        (WebCore::boxIndexInVector):
     24        (WebCore::previousBoxInLine): returns logically previous box in one line.
     25        (WebCore::logicallyPreviousBox): returns logically previous box.
     26        (WebCore::nextBoxInLine): returns logically next box in one line.
     27        (WebCore::logicallyNextBox): returns logically next box.
     28        (WebCore::wordBreakIteratorForMinOffsetBoundary): create word break iterator for position that is a box's min offset.
     29        (WebCore::wordBreakIteratorForMaxOffsetBoundary): create word break iterator for position that is a box's max offset.
     30        (WebCore::isLogicalStartOfWord): return whether a position is logically start of word.
     31        (WebCore::islogicalEndOfWord): return whether a position is logically end of word.
     32        (WebCore::visualWordPosition): returns the visual left or right word position.
     33        (WebCore::leftWordPosition):
     34        (WebCore::rightWordPosition):
     35        * platform/text/TextBreakIterator.h: Add isWordTextBreak().
     36        (WebCore):
     37        * platform/text/TextBreakIteratorICU.cpp:
     38        (WebCore::isWordTextBreak):
     39        (WebCore):
     40        * platform/text/gtk/TextBreakIteratorGtk.cpp:
     41        (WebCore::isWordTextBreak):
     42        (WebCore):
     43        * platform/text/qt/TextBreakIteratorQt.cpp:
     44        (WebCore::isWordTextBreak):
     45        (WebCore):
     46        * platform/text/wince/TextBreakIteratorWinCE.cpp:
     47        (WebCore::isWordTextBreak):
     48        (WebCore):
     49
    1502012-03-16  Dana Jansens  <danakj@chromium.org>
    251
  • trunk/Source/WebCore/editing/FrameSelection.cpp

    r107700 r110965  
    643643        break;
    644644    case WordGranularity:
     645#if !OS(WINCE)
     646        // Visual word movement relies on isWordTextBreak which is not implemented in WinCE.
    645647        if (visualWordMovementEnabled()
    646648            || (m_frame && m_frame->editor()->behavior().shouldMoveLeftRightByWordInVisualOrder())) {
     
    648650            break;
    649651        }
     652#endif
    650653    case SentenceGranularity:
    651654    case LineGranularity:
     
    810813        break;
    811814    case WordGranularity:
     815#if !OS(WINCE)
    812816        if (visualWordMovementEnabled()
    813817            || (m_frame && m_frame->editor()->behavior().shouldMoveLeftRightByWordInVisualOrder())) {
     
    815819            break;
    816820        }
     821#endif
    817822    case SentenceGranularity:
    818823    case LineGranularity:
  • trunk/Source/WebCore/editing/visible_units.cpp

    r108382 r110965  
    5050using namespace WTF::Unicode;
    5151
     52static Node* previousLeafWithSameEditability(Node* node, EditableType editableType)
     53{
     54    bool editable = node->rendererIsEditable(editableType);
     55    node = node->previousLeafNode();
     56    while (node) {
     57        if (editable == node->rendererIsEditable(editableType))
     58            return node;
     59        node = node->previousLeafNode();
     60    }
     61    return 0;
     62}
     63
     64static Node* enclosingNodeWithNonInlineRenderer(Node* node)
     65{
     66    for (; node; node = node->parentNode()) {
     67        if (node->renderer() && !node->renderer()->isInline())
     68            return node;
     69    }
     70    return 0;
     71}
     72
     73static Node* nextLeafWithSameEditability(Node* node, int offset)
     74{
     75    bool editable = node->rendererIsEditable();
     76    ASSERT(offset >= 0);
     77    Node* child = node->childNode(offset);
     78    node = child ? child->nextLeafNode() : node->lastDescendant()->nextLeafNode();
     79    while (node) {
     80        if (editable == node->rendererIsEditable())
     81            return node;
     82        node = node->nextLeafNode();
     83    }
     84    return 0;
     85}
     86
     87static Node* nextLeafWithSameEditability(Node* node, EditableType editableType = ContentIsEditable)
     88{
     89    if (!node)
     90        return 0;
     91   
     92    bool editable = node->rendererIsEditable(editableType);
     93    node = node->nextLeafNode();
     94    while (node) {
     95        if (editable == node->rendererIsEditable(editableType))
     96            return node;
     97        node = node->nextLeafNode();
     98    }
     99    return 0;
     100}
     101
     102// FIXME: consolidate with code in previousLinePosition.
     103static const RootInlineBox* previousRootInlineBox(const InlineBox* box, const VisiblePosition& visiblePosition)
     104{
     105    Node* highestRoot = highestEditableRoot(visiblePosition.deepEquivalent(), ContentIsEditable);
     106    Node* node = box->renderer()->node();
     107    Node* enclosingBlockNode = enclosingNodeWithNonInlineRenderer(node);
     108    Node* previousNode = previousLeafWithSameEditability(node, ContentIsEditable);
     109
     110    while (previousNode && enclosingBlockNode == enclosingNodeWithNonInlineRenderer(previousNode))
     111        previousNode = previousLeafWithSameEditability(previousNode, ContentIsEditable);
     112 
     113    while (previousNode && !previousNode->isShadowRoot()) {
     114        if (highestEditableRoot(firstPositionInOrBeforeNode(previousNode), ContentIsEditable) != highestRoot)
     115            break;
     116
     117        Position pos = previousNode->hasTagName(brTag) ? positionBeforeNode(previousNode) :
     118            createLegacyEditingPosition(previousNode, caretMaxOffset(previousNode));
     119       
     120        if (pos.isCandidate()) {
     121            RenderedPosition renderedPos(pos, DOWNSTREAM);
     122            RootInlineBox* root = renderedPos.rootBox();
     123            if (root)
     124                return root;
     125        }
     126
     127        previousNode = previousLeafWithSameEditability(previousNode, ContentIsEditable);
     128    }
     129    return 0;
     130}
     131
     132static const RootInlineBox* nextRootInlineBox(const InlineBox* box, const VisiblePosition& visiblePosition)
     133{
     134    Node* highestRoot = highestEditableRoot(visiblePosition.deepEquivalent(), ContentIsEditable);
     135    Node* node = box->renderer()->node();
     136    Node* enclosingBlockNode = enclosingNodeWithNonInlineRenderer(node);
     137    Node* nextNode = nextLeafWithSameEditability(node, ContentIsEditable);
     138    while (nextNode && enclosingBlockNode == enclosingNodeWithNonInlineRenderer(nextNode))
     139        nextNode = nextLeafWithSameEditability(nextNode, ContentIsEditable);
     140 
     141    while (nextNode && !nextNode->isShadowRoot()) {
     142        if (highestEditableRoot(firstPositionInOrBeforeNode(nextNode), ContentIsEditable) != highestRoot)
     143            break;
     144
     145        Position pos;
     146        pos = createLegacyEditingPosition(nextNode, caretMinOffset(nextNode));
     147       
     148        if (pos.isCandidate()) {
     149            RenderedPosition renderedPos(pos, DOWNSTREAM);
     150            RootInlineBox* root = renderedPos.rootBox();
     151            if (root)
     152                return root;
     153        }
     154
     155        nextNode = nextLeafWithSameEditability(nextNode, ContentIsEditable);
     156    }
     157    return 0;
     158}
     159
     160static int boxIndexInVector(const InlineTextBox* box, const Vector<InlineBox*>& leafBoxesInLogicalOrder)
     161{
     162    for (size_t i = 0; i < leafBoxesInLogicalOrder.size(); ++i) {
     163        if (box == leafBoxesInLogicalOrder[i])
     164            return i;
     165    }
     166    return 0;
     167}
     168
     169static const InlineTextBox* previousBoxInLine(const RootInlineBox* root, const InlineTextBox* box, Vector<InlineBox*>& leafBoxesInLogicalOrder)
     170{
     171    if (!root)
     172        return 0;
     173
     174    leafBoxesInLogicalOrder.clear();
     175    root->collectLeafBoxesInLogicalOrder(leafBoxesInLogicalOrder);
     176
     177    // If box is null, root is box's previous RootInlineBox, and previousBox is the last logical box in root.
     178    int boxIndex = leafBoxesInLogicalOrder.size() - 1;
     179    if (box)
     180        boxIndex = boxIndexInVector(box, leafBoxesInLogicalOrder) - 1;
     181
     182    for (int i = boxIndex; i >= 0; --i) {
     183        if (leafBoxesInLogicalOrder[i]->isInlineTextBox())
     184            return toInlineTextBox(leafBoxesInLogicalOrder[i]);
     185    }
     186
     187    return 0;
     188}
     189
     190static const InlineTextBox* logicallyPreviousBox(const VisiblePosition& visiblePosition, const InlineTextBox* textBox, bool& previousBoxInDifferentBlock)
     191{
     192    const InlineBox* startBox = textBox;
     193    Vector<InlineBox*> leafBoxesInLogicalOrder;
     194
     195    const InlineTextBox* previousBox = previousBoxInLine(startBox->root(), textBox, leafBoxesInLogicalOrder);
     196    if (previousBox)
     197        return previousBox;
     198
     199    previousBox = previousBoxInLine(startBox->root()->prevRootBox(), 0, leafBoxesInLogicalOrder);
     200    if (previousBox)
     201        return previousBox;
     202
     203    while (1) {
     204        const RootInlineBox* previousRoot = previousRootInlineBox(startBox, visiblePosition);
     205        if (!previousRoot)
     206            break;
     207
     208        previousBox = previousBoxInLine(previousRoot, 0, leafBoxesInLogicalOrder);
     209        if (previousBox) {
     210            previousBoxInDifferentBlock = true;
     211            return previousBox;
     212        }
     213
     214        if (!leafBoxesInLogicalOrder.size())
     215            break;
     216        startBox = leafBoxesInLogicalOrder[0];
     217    }
     218    return 0;
     219}
     220
     221static const InlineTextBox* nextBoxInLine(const RootInlineBox* root, const InlineTextBox* box, Vector<InlineBox*>& leafBoxesInLogicalOrder)
     222{
     223    if (!root)
     224        return 0;
     225
     226    leafBoxesInLogicalOrder.clear();
     227    root->collectLeafBoxesInLogicalOrder(leafBoxesInLogicalOrder);
     228
     229    // If box is null, root is box's next RootInlineBox, and nextBox is the first logical box in root.
     230    // Otherwise, root is box's RootInlineBox, and nextBox is the next logical box in the same line.
     231    size_t nextBoxIndex = 0;
     232    if (box)
     233        nextBoxIndex = boxIndexInVector(box, leafBoxesInLogicalOrder) + 1;
     234
     235    for (size_t i = nextBoxIndex; i < leafBoxesInLogicalOrder.size(); ++i) {
     236        if (leafBoxesInLogicalOrder[i]->isInlineTextBox())
     237            return toInlineTextBox(leafBoxesInLogicalOrder[i]);
     238    }
     239
     240    return 0;
     241}
     242
     243static const InlineTextBox* logicallyNextBox(const VisiblePosition& visiblePosition, const InlineTextBox* textBox, bool& nextBoxInDifferentBlock)
     244{
     245    const InlineBox* startBox = textBox;
     246    Vector<InlineBox*> leafBoxesInLogicalOrder;
     247
     248    const InlineTextBox* nextBox = nextBoxInLine(startBox->root(), textBox, leafBoxesInLogicalOrder);
     249    if (nextBox)
     250        return nextBox;
     251
     252    nextBox = nextBoxInLine(startBox->root()->nextRootBox(), 0, leafBoxesInLogicalOrder);
     253    if (nextBox)
     254        return nextBox;
     255
     256    while (1) {
     257        const RootInlineBox* nextRoot = nextRootInlineBox(startBox, visiblePosition);
     258        if (!nextRoot)
     259            break;
     260
     261        nextBox = nextBoxInLine(nextRoot, 0, leafBoxesInLogicalOrder);
     262        if (nextBox) {
     263            nextBoxInDifferentBlock = true;
     264            return nextBox;
     265        }
     266
     267        if (!leafBoxesInLogicalOrder.size())
     268            break;
     269        startBox = leafBoxesInLogicalOrder[0];
     270    }
     271    return 0;
     272}
     273
     274static TextBreakIterator* wordBreakIteratorForMinOffsetBoundary(const VisiblePosition& visiblePosition, const InlineTextBox* textBox,
     275     int& previousBoxLength, bool& previousBoxInDifferentBlock)
     276{
     277    previousBoxInDifferentBlock = false;
     278
     279    // FIXME: Handle the case when we don't have an inline text box.
     280    const InlineTextBox* previousBox = logicallyPreviousBox(visiblePosition, textBox, previousBoxInDifferentBlock);
     281
     282    int len = 0;
     283    Vector<UChar, 1024> string;
     284    if (previousBox) {
     285        previousBoxLength = previousBox->len();
     286        string.append(previousBox->textRenderer()->text()->characters() + previousBox->start(), previousBoxLength);
     287        len += previousBoxLength;
     288    }
     289    string.append(textBox->textRenderer()->text()->characters() + textBox->start(), textBox->len());
     290    len += textBox->len();
     291
     292    return wordBreakIterator(string.data(), len);
     293}
     294
     295static TextBreakIterator* wordBreakIteratorForMaxOffsetBoundary(const VisiblePosition& visiblePosition, const InlineTextBox* textBox, bool& nextBoxInDifferentBlock)
     296{
     297    nextBoxInDifferentBlock = false;
     298
     299    // FIXME: Handle the case when we don't have an inline text box.
     300    const InlineTextBox* nextBox = logicallyNextBox(visiblePosition, textBox, nextBoxInDifferentBlock);
     301
     302    int len = 0;
     303    Vector<UChar, 1024> string;
     304    string.append(textBox->textRenderer()->text()->characters() + textBox->start(), textBox->len());
     305    len += textBox->len();
     306    if (nextBox) {
     307        string.append(nextBox->textRenderer()->text()->characters() + nextBox->start(), nextBox->len());
     308        len += nextBox->len();
     309    }
     310
     311    return wordBreakIterator(string.data(), len);
     312}
     313
     314static bool isLogicalStartOfWord(TextBreakIterator* iter, int position, bool hardLineBreak)
     315{
     316    bool boundary = hardLineBreak ? true : isTextBreak(iter, position);
     317    if (!boundary)
     318        return false;
     319
     320    textBreakFollowing(iter, position);
     321    // isWordTextBreak returns true after moving across a word and false after moving across a punctuation/space.
     322    return isWordTextBreak(iter);
     323}
     324
     325static bool islogicalEndOfWord(TextBreakIterator* iter, int position, bool hardLineBreak)
     326{
     327    bool boundary = isTextBreak(iter, position);
     328    return (hardLineBreak || boundary) && isWordTextBreak(iter);
     329}
     330
     331enum CursorMovementDirection { MoveLeft, MoveRight };
     332
     333static VisiblePosition visualWordPosition(const VisiblePosition& visiblePosition, CursorMovementDirection direction)
     334{
     335    if (visiblePosition.isNull())
     336        return VisiblePosition();
     337
     338    TextDirection blockDirection = directionOfEnclosingBlock(visiblePosition.deepEquivalent());
     339    InlineBox* previouslyVisitedBox = 0;
     340    VisiblePosition current = visiblePosition;
     341    TextBreakIterator* iter = 0;
     342
     343    while (1) {
     344        VisiblePosition adjacentCharacterPosition = direction == MoveRight ? current.right(true) : current.left(true);
     345        if (adjacentCharacterPosition == current || adjacentCharacterPosition.isNull())
     346            return VisiblePosition();
     347   
     348        InlineBox* box;
     349        int offsetInBox;
     350        adjacentCharacterPosition.deepEquivalent().getInlineBoxAndOffset(UPSTREAM, box, offsetInBox);
     351   
     352        if (!box)
     353            break;
     354        if (!box->isInlineTextBox()) {
     355            current = adjacentCharacterPosition;
     356            continue;
     357        }
     358
     359        InlineTextBox* textBox = toInlineTextBox(box);
     360        int previousBoxLength = 0;
     361        bool previousBoxInDifferentBlock = false;
     362        bool nextBoxInDifferentBlock = false;
     363        bool movingIntoNewBox = previouslyVisitedBox != box;
     364
     365        if (offsetInBox == box->caretMinOffset())
     366            iter = wordBreakIteratorForMinOffsetBoundary(visiblePosition, textBox, previousBoxLength, previousBoxInDifferentBlock);
     367        else if (offsetInBox == box->caretMaxOffset())
     368            iter = wordBreakIteratorForMaxOffsetBoundary(visiblePosition, textBox, nextBoxInDifferentBlock);
     369        else if (movingIntoNewBox) {
     370            iter = wordBreakIterator(textBox->textRenderer()->text()->characters() + textBox->start(), textBox->len());
     371            previouslyVisitedBox = box;
     372        }
     373
     374        textBreakFirst(iter);
     375        int offsetInIterator = offsetInBox - textBox->start() + previousBoxLength;
     376
     377        bool isWordBreak;
     378        if (box->direction() == blockDirection) {
     379            bool logicalStartInRenderer = offsetInBox == static_cast<int>(textBox->start()) && previousBoxInDifferentBlock;
     380            isWordBreak = isLogicalStartOfWord(iter, offsetInIterator, logicalStartInRenderer);
     381        } else {
     382            bool logicalEndInRenderer = offsetInBox == static_cast<int>(textBox->start() + textBox->len()) && nextBoxInDifferentBlock;
     383            isWordBreak = islogicalEndOfWord(iter, offsetInIterator, logicalEndInRenderer);
     384        }     
     385
     386        if (isWordBreak)
     387            return adjacentCharacterPosition;
     388   
     389        current = adjacentCharacterPosition;
     390    }
     391    return VisiblePosition();
     392}
     393
     394VisiblePosition leftWordPosition(const VisiblePosition& visiblePosition)
     395{
     396    VisiblePosition leftWordBreak = visualWordPosition(visiblePosition, MoveLeft);
     397    leftWordBreak = visiblePosition.honorEditingBoundaryAtOrBefore(leftWordBreak);
     398   
     399    // FIXME: How should we handle a non-editable position?
     400    if (leftWordBreak.isNull() && isEditablePosition(visiblePosition.deepEquivalent())) {
     401        TextDirection blockDirection = directionOfEnclosingBlock(visiblePosition.deepEquivalent());
     402        leftWordBreak = blockDirection == LTR ? startOfEditableContent(visiblePosition) : endOfEditableContent(visiblePosition);
     403    }
     404    return leftWordBreak;
     405}
     406
     407VisiblePosition rightWordPosition(const VisiblePosition& visiblePosition)
     408{
     409    VisiblePosition rightWordBreak = visualWordPosition(visiblePosition, MoveRight);
     410    rightWordBreak = visiblePosition.honorEditingBoundaryAtOrBefore(rightWordBreak);
     411
     412    // FIXME: How should we handle a non-editable position?
     413    if (rightWordBreak.isNull() && isEditablePosition(visiblePosition.deepEquivalent())) {
     414        TextDirection blockDirection = directionOfEnclosingBlock(visiblePosition.deepEquivalent());
     415        rightWordBreak = blockDirection == LTR ? endOfEditableContent(visiblePosition) : startOfEditableContent(visiblePosition);
     416    }
     417    return rightWordBreak;
     418}
     419
     420
    52421enum BoundarySearchContextAvailability { DontHaveMoreContext, MayHaveMoreContext };
    53422
     
    516885{
    517886    return p.isNotNull() && p == endOfLine(p);
    518 }
    519 
    520 // The first leaf before node that has the same editability as node.
    521 static Node* previousLeafWithSameEditability(Node* node, EditableType editableType)
    522 {
    523     bool editable = node->rendererIsEditable(editableType);
    524     Node* n = node->previousLeafNode();
    525     while (n) {
    526         if (editable == n->rendererIsEditable(editableType))
    527             return n;
    528         n = n->previousLeafNode();
    529     }
    530     return 0;
    531 }
    532 
    533 static Node* enclosingNodeWithNonInlineRenderer(Node* n)
    534 {
    535     for (Node* p = n; p; p = p->parentNode()) {
    536         if (p->renderer() && !p->renderer()->isInline())
    537             return p;
    538     }
    539     return 0;
    540887}
    541888
     
    626973}
    627974
    628 static Node* nextLeafWithSameEditability(Node* node, int offset)
    629 {
    630     bool editable = node->rendererIsEditable();
    631     ASSERT(offset >= 0);
    632     Node* child = node->childNode(offset);
    633     Node* n = child ? child->nextLeafNode() : node->lastDescendant()->nextLeafNode();
    634     while (n) {
    635         if (editable == n->rendererIsEditable())
    636             return n;
    637         n = n->nextLeafNode();
    638     }
    639     return 0;
    640 }
    641 
    642 static Node* nextLeafWithSameEditability(Node* node, EditableType editableType = ContentIsEditable)
    643 {
    644     if (!node)
    645         return 0;
    646    
    647     bool editable = node->rendererIsEditable(editableType);
    648     Node* n = node->nextLeafNode();
    649     while (n) {
    650         if (editable == n->rendererIsEditable(editableType))
    651             return n;
    652         n = n->nextLeafNode();
    653     }
    654     return 0;
    655 }
    656975
    657976VisiblePosition nextLinePosition(const VisiblePosition &visiblePosition, int lineDirectionPoint, EditableType editableType)
     
    11001419}
    11011420
    1102 static const int invalidOffset = -1;
    1103 static const int offsetNotFound = -1;
    1104 
    1105 static bool positionIsInBox(const VisiblePosition& wordBreak, const InlineBox* box, int& offsetOfWordBreak)
    1106 {
    1107     if (wordBreak.isNull())
    1108         return false;
    1109 
    1110     InlineBox* boxOfWordBreak;
    1111     wordBreak.getInlineBoxAndOffset(boxOfWordBreak, offsetOfWordBreak);
    1112     return box == boxOfWordBreak;
    1113 }
    1114 
    1115 static VisiblePosition previousWordBreakInBoxInsideBlockWithSameDirectionality(const InlineBox* box, const VisiblePosition& previousWordBreak, int& offsetOfWordBreak)
    1116 {
    1117     // In a LTR block, the word break should be on the left boundary of a word.
    1118     // In a RTL block, the word break should be on the right boundary of a word.
    1119     // Because nextWordPosition() returns the word break on the right boundary of the word for LTR text,
    1120     // we need to use previousWordPosition() to traverse words within the inline boxes from right to left
    1121     // to find the previous word break (i.e. the first word break on the left). The same applies to RTL text.
    1122    
    1123     bool hasSeenWordBreakInThisBox = previousWordBreak.isNotNull();
    1124 
    1125     VisiblePosition wordBreak;
    1126 
    1127     if (hasSeenWordBreakInThisBox)
    1128         wordBreak = previousWordBreak;
    1129     else {
    1130         wordBreak = createLegacyEditingPosition(box->renderer()->node(), box->caretMaxOffset());
    1131 
    1132         // Return the rightmost word boundary of LTR box or leftmost word boundary of RTL box if
    1133         // it is not in the previously visited boxes. For example, given a logical text
    1134         // "abc def     hij opq", there are 2 boxes: the "abc def " (starts at 0 and length is 8)
    1135         // and the "hij opq" (starts at 12 and length is 7). The word breaks are
    1136         // "abc |def |    hij |opq". We normally catch the word break between "def" and "hij" when
    1137         // we visit the box that contains "hij opq", but this word break doesn't exist in the box
    1138         // that contains "hij opq" when there are multiple spaces. So we detect it when we're
    1139         // traversing the box that contains "abc def " instead.
    1140 
    1141         if ((box->isLeftToRightDirection() && box->nextLeafChild())
    1142             || (!box->isLeftToRightDirection() && box->prevLeafChild())) {
    1143 
    1144             VisiblePosition positionAfterWord = nextBoundary(wordBreak, nextWordPositionBoundary);
    1145             if (positionAfterWord.isNotNull()) {
    1146                 VisiblePosition positionBeforeWord = previousBoundary(positionAfterWord, previousWordPositionBoundary);
    1147            
    1148                 if (positionIsInBox(positionBeforeWord, box, offsetOfWordBreak))
    1149                     return positionBeforeWord;
    1150             }
    1151         }
    1152     }
    1153  
    1154     wordBreak = previousBoundary(wordBreak, previousWordPositionBoundary);
    1155     if (previousWordBreak == wordBreak)
    1156         return VisiblePosition();
    1157 
    1158     return positionIsInBox(wordBreak, box, offsetOfWordBreak) ? wordBreak : VisiblePosition();
    1159 }
    1160 
    1161 static VisiblePosition leftmostPositionInRTLBoxInLTRBlock(const InlineBox* box)
    1162 {
    1163     // FIXME: Probably need to take care of bidi level too.
    1164     Node* node = box->renderer()->node();
    1165     InlineBox* previousLeaf = box->prevLeafChild();
    1166     InlineBox* nextLeaf = box->nextLeafChild();   
    1167    
    1168     if (previousLeaf && !previousLeaf->isLeftToRightDirection())
    1169         return createLegacyEditingPosition(node, box->caretMaxOffset());
    1170 
    1171     if (nextLeaf && !nextLeaf->isLeftToRightDirection()) {
    1172         if (previousLeaf)
    1173             return createLegacyEditingPosition(previousLeaf->renderer()->node(), previousLeaf->caretMaxOffset());
    1174 
    1175         InlineBox* lastRTLLeaf;
    1176         do {
    1177             lastRTLLeaf = nextLeaf;
    1178             nextLeaf = nextLeaf->nextLeafChild();
    1179         } while (nextLeaf && !nextLeaf->isLeftToRightDirection());
    1180         return createLegacyEditingPosition(lastRTLLeaf->renderer()->node(), lastRTLLeaf->caretMinOffset());
    1181     }
    1182 
    1183     return createLegacyEditingPosition(node, box->caretMinOffset());
    1184 }
    1185 
    1186 static VisiblePosition rightmostPositionInLTRBoxInRTLBlock(const InlineBox* box)
    1187 {
    1188     // FIXME: Probably need to take care of bidi level too.
    1189     Node* node = box->renderer()->node();
    1190     InlineBox* previousLeaf = box->prevLeafChild();
    1191     InlineBox* nextLeaf = box->nextLeafChild();   
    1192    
    1193     if (nextLeaf && nextLeaf->isLeftToRightDirection())   
    1194         return createLegacyEditingPosition(node, box->caretMaxOffset());
    1195 
    1196     if (previousLeaf && previousLeaf->isLeftToRightDirection()) {
    1197         if (nextLeaf)
    1198             return createLegacyEditingPosition(nextLeaf->renderer()->node(), nextLeaf->caretMaxOffset());
    1199 
    1200         InlineBox* firstLTRLeaf;
    1201         do {
    1202             firstLTRLeaf = previousLeaf;
    1203             previousLeaf = previousLeaf->prevLeafChild();
    1204         } while (previousLeaf && previousLeaf->isLeftToRightDirection());
    1205         return createLegacyEditingPosition(firstLTRLeaf->renderer()->node(), firstLTRLeaf->caretMinOffset());
    1206     }
    1207 
    1208     return createLegacyEditingPosition(node, box->caretMinOffset());
    1209 }
    1210    
    1211 static VisiblePosition lastWordBreakInBox(const InlineBox* box, int& offsetOfWordBreak)
    1212 {
    1213     // Add the leftmost word break for RTL box or rightmost word break for LTR box.
    1214     InlineBox* previousLeaf = box->prevLeafChild();
    1215     InlineBox* nextLeaf = box->nextLeafChild();
    1216     VisiblePosition boundaryPosition;
    1217     if (box->direction() == RTL && (!previousLeaf || previousLeaf->isLeftToRightDirection()))
    1218         boundaryPosition = leftmostPositionInRTLBoxInLTRBlock(box);
    1219     else if (box->direction() == LTR && (!nextLeaf || !nextLeaf->isLeftToRightDirection()))
    1220         boundaryPosition = rightmostPositionInLTRBoxInRTLBlock(box);
    1221 
    1222     if (boundaryPosition.isNull())
    1223         return VisiblePosition();           
    1224 
    1225     VisiblePosition wordBreak = nextBoundary(boundaryPosition, nextWordPositionBoundary);
    1226     if (wordBreak.isNull())
    1227         wordBreak = boundaryPosition;
    1228     else if (wordBreak != boundaryPosition)
    1229         wordBreak = previousBoundary(wordBreak, previousWordPositionBoundary);
    1230 
    1231     return positionIsInBox(wordBreak, box, offsetOfWordBreak) ? wordBreak : VisiblePosition();   
    1232 }
    1233 
    1234 static bool positionIsVisuallyOrderedInBoxInBlockWithDifferentDirectionality(const VisiblePosition& wordBreak, const InlineBox* box, int& offsetOfWordBreak)
    1235 {
    1236     int previousOffset = offsetOfWordBreak;
    1237     return positionIsInBox(wordBreak, box, offsetOfWordBreak)
    1238         && (previousOffset == invalidOffset || previousOffset < offsetOfWordBreak);
    1239 }
    1240    
    1241 static VisiblePosition nextWordBreakInBoxInsideBlockWithDifferentDirectionality(
    1242     const InlineBox* box, const VisiblePosition& previousWordBreak, int& offsetOfWordBreak, bool& isLastWordBreakInBox)
    1243 {
    1244     // FIXME: Probably need to take care of bidi level too.
    1245    
    1246     // In a LTR block, the word break should be on the left boundary of a word.
    1247     // In a RTL block, the word break should be on the right boundary of a word.
    1248     // Because previousWordPosition() returns the word break on the right boundary of the word for RTL text,
    1249     // we need to use nextWordPosition() to traverse words within the inline boxes from right to left to find the next word break.
    1250     // The same applies to LTR text, in which words are traversed within the inline boxes from left to right.
    1251    
    1252     bool hasSeenWordBreakInThisBox = previousWordBreak.isNotNull();
    1253     VisiblePosition wordBreak = hasSeenWordBreakInThisBox ? previousWordBreak :
    1254         createLegacyEditingPosition(box->renderer()->node(), box->caretMinOffset());
    1255 
    1256     wordBreak = nextBoundary(wordBreak, nextWordPositionBoundary);
    1257  
    1258     // Given RTL box "ABC DEF" either follows a LTR box or is the first visual box in an LTR block as an example,
    1259     // the visual display of the RTL box is: "(0)J(10)I(9)H(8) (7)F(6)E(5)D(4) (3)C(2)B(1)A(11)",
    1260     // where the number in parenthesis represents offset in visiblePosition.
    1261     // Start at offset 0, the first word break is at offset 3, the 2nd word break is at offset 7, and the 3rd word break should be at offset 0.
    1262     // But nextWordPosition() of offset 7 is offset 11, which should be ignored,
    1263     // and the position at offset 0 should be manually added as the last word break within the box.
    1264     if (wordBreak != previousWordBreak && positionIsVisuallyOrderedInBoxInBlockWithDifferentDirectionality(wordBreak, box, offsetOfWordBreak)) {
    1265         isLastWordBreakInBox = false;
    1266         return wordBreak;
    1267     }
    1268    
    1269     isLastWordBreakInBox = true;
    1270     return lastWordBreakInBox(box, offsetOfWordBreak);
    1271 }
    1272 
    1273 struct WordBoundaryEntry {
    1274     WordBoundaryEntry()
    1275         : offsetInInlineBox(invalidOffset)
    1276     {
    1277     }
    1278 
    1279     WordBoundaryEntry(const VisiblePosition& position, int offset)
    1280         : visiblePosition(position)
    1281         , offsetInInlineBox(offset)
    1282     {
    1283     }
    1284 
    1285     VisiblePosition visiblePosition;
    1286     int offsetInInlineBox;
    1287 };
    1288    
    1289 typedef Vector<WordBoundaryEntry, 50> WordBoundaryVector;
    1290    
    1291 static void collectWordBreaksInBoxInsideBlockWithSameDirectionality(const InlineBox* box, WordBoundaryVector& orderedWordBoundaries)
    1292 {
    1293     orderedWordBoundaries.clear();
    1294 
    1295     VisiblePosition wordBreak;
    1296     int offsetOfWordBreak = invalidOffset;
    1297     while (1) {
    1298         wordBreak = previousWordBreakInBoxInsideBlockWithSameDirectionality(box, wordBreak, offsetOfWordBreak);
    1299         if (wordBreak.isNull())
    1300             break;
    1301         WordBoundaryEntry wordBoundaryEntry(wordBreak, offsetOfWordBreak);
    1302         orderedWordBoundaries.append(wordBoundaryEntry);
    1303     }
    1304 }
    1305 
    1306 static void collectWordBreaksInBoxInsideBlockWithDifferntDirectionality(const InlineBox* box, WordBoundaryVector& orderedWordBoundaries)
    1307 {
    1308     orderedWordBoundaries.clear();
    1309    
    1310     VisiblePosition wordBreak;
    1311     int offsetOfWordBreak = invalidOffset;
    1312     bool isLastWordBreakInBox = false;
    1313     while (1) {
    1314         wordBreak = nextWordBreakInBoxInsideBlockWithDifferentDirectionality(box, wordBreak, offsetOfWordBreak, isLastWordBreakInBox);
    1315         if (wordBreak.isNotNull()) {
    1316             WordBoundaryEntry wordBoundaryEntry(wordBreak, offsetOfWordBreak);
    1317             orderedWordBoundaries.append(wordBoundaryEntry);
    1318         }
    1319         if (isLastWordBreakInBox)
    1320             break;
    1321     }
    1322 }
    1323 
    1324 static void collectWordBreaksInBox(const InlineBox* box, WordBoundaryVector& orderedWordBoundaries, TextDirection blockDirection)
    1325 {
    1326     if (box->direction() == blockDirection)
    1327         collectWordBreaksInBoxInsideBlockWithSameDirectionality(box, orderedWordBoundaries);
    1328     else
    1329         collectWordBreaksInBoxInsideBlockWithDifferntDirectionality(box, orderedWordBoundaries);       
    1330 }
    1331    
    1332 static VisiblePosition previousWordBoundaryInBox(const InlineBox* box, int offset)
    1333 {
    1334     int offsetOfWordBreak = 0;
    1335     VisiblePosition wordBreak;
    1336     while (true) {
    1337         wordBreak = previousWordBreakInBoxInsideBlockWithSameDirectionality(box, wordBreak, offsetOfWordBreak);
    1338         if (wordBreak.isNull())
    1339             break;
    1340         if (offset == invalidOffset || offsetOfWordBreak != offset)
    1341             return wordBreak;
    1342     }       
    1343     return VisiblePosition();
    1344 }
    1345 
    1346 static VisiblePosition nextWordBoundaryInBox(const InlineBox* box, int offset)
    1347 {
    1348     int offsetOfWordBreak = 0;
    1349     VisiblePosition wordBreak;
    1350     bool isLastWordBreakInBox = false;
    1351     do {
    1352         wordBreak = nextWordBreakInBoxInsideBlockWithDifferentDirectionality(box, wordBreak, offsetOfWordBreak, isLastWordBreakInBox);
    1353         if (wordBreak.isNotNull() && (offset == invalidOffset || offsetOfWordBreak != offset))
    1354             return wordBreak;
    1355     } while (!isLastWordBreakInBox);       
    1356     return VisiblePosition();
    1357 }
    1358    
    1359 static VisiblePosition visuallyLastWordBoundaryInBox(const InlineBox* box, int offset, TextDirection blockDirection)
    1360 {
    1361     WordBoundaryVector orderedWordBoundaries;
    1362     collectWordBreaksInBox(box, orderedWordBoundaries, blockDirection);
    1363     if (!orderedWordBoundaries.size())
    1364         return VisiblePosition();
    1365     if (offset == invalidOffset || orderedWordBoundaries[orderedWordBoundaries.size() - 1].offsetInInlineBox != offset)
    1366         return orderedWordBoundaries[orderedWordBoundaries.size() - 1].visiblePosition;
    1367     if (orderedWordBoundaries.size() > 1)
    1368         return orderedWordBoundaries[orderedWordBoundaries.size() - 2].visiblePosition;
    1369     return VisiblePosition();
    1370 }
    1371        
    1372 static int greatestOffsetUnder(int offset, bool boxAndBlockAreInSameDirection, const WordBoundaryVector& orderedWordBoundaries)
    1373 {
    1374     if (!orderedWordBoundaries.size())
    1375         return offsetNotFound;
    1376     // FIXME: binary search.
    1377     if (boxAndBlockAreInSameDirection) {
    1378         for (unsigned i = 0; i < orderedWordBoundaries.size(); ++i) {
    1379             if (orderedWordBoundaries[i].offsetInInlineBox < offset)
    1380                 return i;
    1381         }
    1382         return offsetNotFound;
    1383     }
    1384     for (int i = orderedWordBoundaries.size() - 1; i >= 0; --i) {
    1385         if (orderedWordBoundaries[i].offsetInInlineBox < offset)
    1386             return i;
    1387     }
    1388     return offsetNotFound;
    1389 }
    1390 
    1391 static int smallestOffsetAbove(int offset, bool boxAndBlockAreInSameDirection, const WordBoundaryVector& orderedWordBoundaries)
    1392 {
    1393     if (!orderedWordBoundaries.size())
    1394         return offsetNotFound;
    1395     // FIXME: binary search.
    1396     if (boxAndBlockAreInSameDirection) {
    1397         for (int i = orderedWordBoundaries.size() - 1; i >= 0; --i) {
    1398             if (orderedWordBoundaries[i].offsetInInlineBox > offset)
    1399                 return i;
    1400         }
    1401         return offsetNotFound;
    1402     }
    1403     for (unsigned i = 0; i < orderedWordBoundaries.size(); ++i) {
    1404         if (orderedWordBoundaries[i].offsetInInlineBox > offset)
    1405             return i;
    1406     }
    1407     return offsetNotFound;
    1408 }
    1409 
    1410 static const RootInlineBox* previousRootInlineBox(const InlineBox* box)
    1411 {
    1412     Node* node = box->renderer()->node();
    1413     Node* enclosingBlockNode = enclosingNodeWithNonInlineRenderer(node);
    1414     Node* previousNode = node->previousLeafNode();
    1415     while (previousNode && enclosingBlockNode == enclosingNodeWithNonInlineRenderer(previousNode))
    1416         previousNode = previousNode->previousLeafNode();
    1417  
    1418     while (previousNode && !previousNode->isShadowRoot()) {
    1419         Position pos = createLegacyEditingPosition(previousNode, caretMaxOffset(previousNode));
    1420        
    1421         if (pos.isCandidate()) {
    1422             RenderedPosition renderedPos(pos, DOWNSTREAM);
    1423             RootInlineBox* root = renderedPos.rootBox();
    1424             if (root)
    1425                 return root;
    1426         }
    1427 
    1428         previousNode = previousNode->previousLeafNode();
    1429     }
    1430     return 0;
    1431 }
    1432 
    1433 static const RootInlineBox* nextRootInlineBox(const InlineBox* box)
    1434 {
    1435     Node* node = box->renderer()->node();
    1436     Node* enclosingBlockNode = enclosingNodeWithNonInlineRenderer(node);
    1437     Node* nextNode = node->nextLeafNode();
    1438     while (nextNode && enclosingBlockNode == enclosingNodeWithNonInlineRenderer(nextNode))
    1439         nextNode = nextNode->nextLeafNode();
    1440  
    1441     while (nextNode && !nextNode->isShadowRoot()) {
    1442         Position pos;
    1443         pos = createLegacyEditingPosition(nextNode, caretMinOffset(nextNode));
    1444        
    1445         if (pos.isCandidate()) {
    1446             RenderedPosition renderedPos(pos, DOWNSTREAM);
    1447             RootInlineBox* root = renderedPos.rootBox();
    1448             if (root)
    1449                 return root;
    1450         }
    1451 
    1452         nextNode = nextNode->nextLeafNode();
    1453     }
    1454     return 0;
    1455 }
    1456 
    1457 static const InlineBox* leftInlineBox(const InlineBox* box, TextDirection blockDirection)
    1458 {
    1459     if (box->prevLeafChild())
    1460         return box->prevLeafChild();
    1461    
    1462     const RootInlineBox* rootBox = box->root();
    1463     const bool isBlockLTR = blockDirection == LTR;
    1464     const InlineFlowBox* leftLineBox = isBlockLTR ? rootBox->prevLineBox() : rootBox->nextLineBox();
    1465     if (leftLineBox)
    1466         return leftLineBox->lastLeafChild();
    1467 
    1468     const RootInlineBox* leftRootInlineBox = isBlockLTR ? previousRootInlineBox(box) :
    1469         nextRootInlineBox(box);
    1470     return leftRootInlineBox ? leftRootInlineBox->lastLeafChild() : 0;
    1471 }
    1472 
    1473 static const InlineBox* rightInlineBox(const InlineBox* box, TextDirection blockDirection)
    1474 {
    1475     if (box->nextLeafChild())
    1476         return box->nextLeafChild();
    1477    
    1478     const RootInlineBox* rootBox = box->root();
    1479     const bool isBlockLTR = blockDirection == LTR;
    1480     const InlineFlowBox* rightLineBox = isBlockLTR ? rootBox->nextLineBox() : rootBox->prevLineBox();
    1481     if (rightLineBox)
    1482         return rightLineBox->firstLeafChild();
    1483 
    1484     const RootInlineBox* rightRootInlineBox = isBlockLTR ? nextRootInlineBox(box) :
    1485         previousRootInlineBox(box);
    1486     return rightRootInlineBox ? rightRootInlineBox->firstLeafChild() : 0;
    1487 }
    1488 
    1489 static VisiblePosition leftWordBoundary(const InlineBox* box, int offset, TextDirection blockDirection)
    1490 {
    1491     VisiblePosition wordBreak;
    1492     for  (const InlineBox* adjacentBox = box; adjacentBox; adjacentBox = leftInlineBox(adjacentBox, blockDirection)) {
    1493         if (blockDirection == LTR) {
    1494             if (adjacentBox->isLeftToRightDirection())
    1495                 wordBreak = previousWordBoundaryInBox(adjacentBox, adjacentBox == box ? offset : invalidOffset);
    1496             else
    1497                 wordBreak = nextWordBoundaryInBox(adjacentBox, adjacentBox == box ? offset : invalidOffset);
    1498         } else
    1499             wordBreak = visuallyLastWordBoundaryInBox(adjacentBox, adjacentBox == box ? offset : invalidOffset, blockDirection);           
    1500         if (wordBreak.isNotNull())
    1501             return wordBreak;
    1502     }
    1503     return VisiblePosition();
    1504 }
    1505  
    1506 static VisiblePosition rightWordBoundary(const InlineBox* box, int offset, TextDirection blockDirection)
    1507 {
    1508    
    1509     VisiblePosition wordBreak;
    1510     for (const InlineBox* adjacentBox = box; adjacentBox; adjacentBox = rightInlineBox(adjacentBox, blockDirection)) {
    1511         if (blockDirection == RTL) {
    1512             if (adjacentBox->isLeftToRightDirection())
    1513                 wordBreak = nextWordBoundaryInBox(adjacentBox, adjacentBox == box ? offset : invalidOffset);
    1514             else
    1515                 wordBreak = previousWordBoundaryInBox(adjacentBox, adjacentBox == box ? offset : invalidOffset);
    1516         } else
    1517             wordBreak = visuallyLastWordBoundaryInBox(adjacentBox, adjacentBox == box ? offset : invalidOffset, blockDirection);           
    1518         if (!wordBreak.isNull())
    1519             return wordBreak;
    1520     }
    1521     return VisiblePosition();
    1522 }
    1523    
    1524 static bool positionIsInBoxButNotOnBoundary(const VisiblePosition& wordBreak, const InlineBox* box)
    1525 {
    1526     int offsetOfWordBreak;
    1527     return positionIsInBox(wordBreak, box, offsetOfWordBreak)
    1528         && offsetOfWordBreak != box->caretMaxOffset() && offsetOfWordBreak != box->caretMinOffset();
    1529 }
    1530 
    1531 static VisiblePosition leftWordPositionIgnoringEditingBoundary(const VisiblePosition& visiblePosition)
    1532 {
    1533     InlineBox* box;
    1534     int offset;
    1535     visiblePosition.getInlineBoxAndOffset(box, offset);
    1536 
    1537     if (!box)
    1538         return VisiblePosition();
    1539 
    1540     TextDirection blockDirection = directionOfEnclosingBlock(visiblePosition.deepEquivalent());
    1541 
    1542     // FIXME: If the box's directionality is the same as that of the enclosing block, when the offset is at the box boundary
    1543     // and the direction is towards inside the box, do I still need to make it a special case? For example, a LTR box inside a LTR block,
    1544     // when offset is at box's caretMinOffset and the direction is DirectionRight, should it be taken care as a general case?
    1545     if (offset == box->caretLeftmostOffset())
    1546         return leftWordBoundary(leftInlineBox(box, blockDirection), invalidOffset, blockDirection);
    1547     if (offset == box->caretRightmostOffset())
    1548         return leftWordBoundary(box, offset, blockDirection);
    1549    
    1550    
    1551     VisiblePosition wordBreak;
    1552     if (blockDirection == LTR) {
    1553         if (box->direction() == blockDirection)
    1554             wordBreak = previousBoundary(visiblePosition, previousWordPositionBoundary);
    1555         else
    1556             wordBreak = nextBoundary(visiblePosition, nextWordPositionBoundary);
    1557     }
    1558     if (wordBreak.isNotNull() && positionIsInBoxButNotOnBoundary(wordBreak, box))
    1559         return wordBreak;
    1560    
    1561     WordBoundaryVector orderedWordBoundaries;
    1562     collectWordBreaksInBox(box, orderedWordBoundaries, blockDirection);
    1563 
    1564     int index = box->isLeftToRightDirection() ? greatestOffsetUnder(offset, blockDirection == LTR, orderedWordBoundaries)
    1565         : smallestOffsetAbove(offset, blockDirection == RTL, orderedWordBoundaries);
    1566     if (index >= 0)
    1567         return orderedWordBoundaries[index].visiblePosition;
    1568    
    1569     return leftWordBoundary(leftInlineBox(box, blockDirection), invalidOffset, blockDirection);
    1570 }
    1571 
    1572 static VisiblePosition rightWordPositionIgnoringEditingBoundary(const VisiblePosition& visiblePosition)
    1573 {
    1574     InlineBox* box;
    1575     int offset;
    1576     visiblePosition.getInlineBoxAndOffset(box, offset);
    1577 
    1578     if (!box)
    1579         return VisiblePosition();
    1580 
    1581     TextDirection blockDirection = directionOfEnclosingBlock(visiblePosition.deepEquivalent());
    1582    
    1583     if (offset == box->caretLeftmostOffset())
    1584         return rightWordBoundary(box, offset, blockDirection);
    1585     if (offset == box->caretRightmostOffset())
    1586         return rightWordBoundary(rightInlineBox(box, blockDirection), invalidOffset, blockDirection);
    1587  
    1588     VisiblePosition wordBreak;
    1589     if (blockDirection == RTL) {
    1590         if (box->direction() == blockDirection)
    1591             wordBreak = previousBoundary(visiblePosition, previousWordPositionBoundary);
    1592         else
    1593             wordBreak = nextBoundary(visiblePosition, nextWordPositionBoundary);
    1594     }
    1595     if (wordBreak.isNotNull() && positionIsInBoxButNotOnBoundary(wordBreak, box))
    1596         return wordBreak;
    1597    
    1598     WordBoundaryVector orderedWordBoundaries;
    1599     collectWordBreaksInBox(box, orderedWordBoundaries, blockDirection);
    1600    
    1601     int index = box->isLeftToRightDirection() ? smallestOffsetAbove(offset, blockDirection == LTR, orderedWordBoundaries)
    1602         : greatestOffsetUnder(offset, blockDirection == RTL, orderedWordBoundaries);
    1603     if (index >= 0)
    1604         return orderedWordBoundaries[index].visiblePosition;
    1605    
    1606     return rightWordBoundary(rightInlineBox(box, blockDirection), invalidOffset, blockDirection);
    1607 }
    1608 
    1609 VisiblePosition leftWordPosition(const VisiblePosition& visiblePosition)
    1610 {
    1611     if (visiblePosition.isNull())
    1612         return VisiblePosition();
    1613 
    1614     VisiblePosition leftWordBreak = leftWordPositionIgnoringEditingBoundary(visiblePosition);
    1615     leftWordBreak = visiblePosition.honorEditingBoundaryAtOrBefore(leftWordBreak);
    1616    
    1617     // FIXME: How should we handle a non-editable position?
    1618     if (leftWordBreak.isNull() && isEditablePosition(visiblePosition.deepEquivalent())) {
    1619         TextDirection blockDirection = directionOfEnclosingBlock(visiblePosition.deepEquivalent());
    1620         leftWordBreak = blockDirection == LTR ? startOfEditableContent(visiblePosition) : endOfEditableContent(visiblePosition);
    1621     }
    1622     return leftWordBreak;
    1623 }
    1624 
    1625 VisiblePosition rightWordPosition(const VisiblePosition& visiblePosition)
    1626 {
    1627     if (visiblePosition.isNull())
    1628         return VisiblePosition();
    1629 
    1630     VisiblePosition rightWordBreak = rightWordPositionIgnoringEditingBoundary(visiblePosition);
    1631     rightWordBreak = visiblePosition.honorEditingBoundaryAtOrBefore(rightWordBreak);
    1632 
    1633     // FIXME: How should we handle a non-editable position?
    1634     if (rightWordBreak.isNull() && isEditablePosition(visiblePosition.deepEquivalent())) {
    1635         TextDirection blockDirection = directionOfEnclosingBlock(visiblePosition.deepEquivalent());
    1636         rightWordBreak = blockDirection == LTR ? endOfEditableContent(visiblePosition) : startOfEditableContent(visiblePosition);
    1637     }
    1638     return rightWordBreak;
    1639 }
    1640 
    1641 }
     1421}
  • trunk/Source/WebCore/platform/text/TextBreakIterator.h

    r89592 r110965  
    5757    int textBreakFollowing(TextBreakIterator*, int);
    5858    bool isTextBreak(TextBreakIterator*, int);
     59    bool isWordTextBreak(TextBreakIterator*);
    5960
    6061    const int TextBreakDone = -1;
  • trunk/Source/WebCore/platform/text/TextBreakIteratorICU.cpp

    r109144 r110965  
    138138{
    139139    return ubrk_isBoundary(reinterpret_cast<UBreakIterator*>(iterator), position);
     140}
     141
     142bool isWordTextBreak(TextBreakIterator* iterator)
     143{
     144    int ruleStatus = ubrk_getRuleStatus(reinterpret_cast<UBreakIterator*>(iterator));
     145    return ruleStatus != UBRK_WORD_NONE;
    140146}
    141147
  • trunk/Source/WebCore/platform/text/gtk/TextBreakIteratorGtk.cpp

    r95901 r110965  
    386386}
    387387
    388 }
     388bool isWordTextBreak(TextBreakIterator*)
     389{
     390    return true;
     391}
     392
     393}
  • trunk/Source/WebCore/platform/text/qt/TextBreakIteratorQt.cpp

    r94527 r110965  
    151151    }
    152152
     153    bool isWordTextBreak(TextBreakIterator*)
     154    {
     155        return true;
     156    }
     157
    153158}
  • trunk/Source/WebCore/platform/text/wince/TextBreakIteratorWinCE.cpp

    r89592 r110965  
    318318}
    319319
     320bool isWordTextBreak(TextBreakIterator*)
     321{
     322    return true;
     323}
     324
    320325TextBreakIterator* cursorMovementIterator(const UChar* string, int length)
    321326{
Note: See TracChangeset for help on using the changeset viewer.