Changeset 110965 in webkit
- Timestamp:
- Mar 16, 2012 1:27:28 AM (12 years ago)
- Location:
- trunk
- Files:
-
- 2 added
- 17 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r110961 r110965 1 2012-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 1 34 2012-03-16 Mike Reed <reed@google.com> 2 35 -
trunk/LayoutTests/editing/selection/move-by-word-visually-inline-block-positioned-element-expected.txt
r101430 r110965 3 3 Test 1, LTR: 4 4 Move 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] 6 6 Move 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] 8 8 Test 2, LTR: 9 9 Move 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] 11 11 Move 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] 13 13 Test 3, LTR: 14 14 Move 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] 16 16 Move 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] 18 18 Test 4, LTR: 19 19 Move 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] 21 21 Move 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] 23 23 Test 5, LTR: 24 24 Move right by one word 25 25 "this is fixed"[0, 5, 8], "this is relative"[0, 5, 8], "this is absolute"[0, 5, 8, 16] 26 26 Move 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] 28 28 Test 6, LTR: 29 29 Move right by one word 30 30 "this is relative"[0, 5, 8], "this is absolute"[0, 5, 8, 16] 31 31 Move 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] 33 33 Test 7, LTR: 34 34 Move right by one word 35 35 "this is absolute"[0, 5, 8, 16] 36 36 Move 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] 38 38 -
trunk/LayoutTests/editing/selection/move-by-word-visually-inline-block-positioned-element.html
r102252 r110965 18 18 <div id="testMoveByWord" contenteditable style="width:2000px; height:2000px"> 19 19 <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]"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, 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]" 22 22 class="test_move_by_word">begin start</div> 23 23 24 24 <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]"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, 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]" 27 27 class="test_move_by_word" style="display:inline-block">abc def</div> 28 28 29 29 <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]"30 title="[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]" 32 32 class="test_move_by_word">end ing</div> 33 33 34 34 <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]"35 title="[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]" 37 37 class="test_move_by_word" style="float:left">this is float</div> 38 38 39 39 <div id="d_5" dir=ltr 40 40 title="[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]" 42 42 class="test_move_by_word" style="position:fixed; top:30px; right:5px">this is fixed</div> 43 43 44 44 <div id="d_6" dir=ltr 45 45 title="[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]" 47 47 class="test_move_by_word" style="position:relative; left:20px">this is relative</div> 48 48 49 49 <div id="d_7" dir=ltr 50 50 title="[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]" 52 52 class="test_move_by_word" style="position:absolute; left:100px; top:150px">this is absolute</div> 53 53 -
trunk/LayoutTests/editing/selection/move-by-word-visually-multi-line-expected.txt
r101430 r110965 38 38 Test 8, RTL: 39 39 Move 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] 43 41 Move right by one word 44 "opq rst uvw xyz"[15, 11, 8, 3, 0], "abc def ghi jkl mn "[1 8, 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] 45 43 Test 9, RTL: 46 44 Move left by one word … … 60 58 Test 12, RTL: 61 59 Move 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] 66 61 Move 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 "[3 6, 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] 68 63 Test 13, LTR: 69 64 Move 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] 66 Move left by one word 67 "opq rst uvw xyz"[15, 12, 8, 4, 0], "abc def ghi jkl mn "[16, 12, 8, 4, 0] 68 Test 14, LTR: 69 Move right by one word 70 "abc def ghi jkl mn "[0, 4, 8, 12, 16, 18] 71 Move left by one word 72 "abc def ghi jkl mn "[18, 16, 12, 8, 4, 0] 73 Test 15, LTR: 74 Move right by one word 75 "abc def ghi jkl mn "[0, 4, 8, 12, 16], "opq rst uvw xyz"[0, 4, 8, 12, 15] 76 Move left by one word 77 "opq rst uvw xyz"[15, 12, 8, 4, 0], "abc def ghi jkl mn "[16, 12, 8, 4, 0] 78 Test 16, LTR: 79 Move right by one word 80 "abc def "[0, 4, 8] 71 81 Move left by one word 72 82 " hij opq"[8, 5, 1] 73 Test 1 4, LTR:83 Test 17, LTR: 74 84 Move right by one word 75 85 <DIV>[0] 76 86 Move left by one word 77 87 <DIV>[0] 78 Test 1 5, LTR:88 Test 18, LTR: 79 89 Move right by one word 80 90 "\n00"[3] -
trunk/LayoutTests/editing/selection/move-by-word-visually-multi-line.html
r102252 r110965 55 55 <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> 56 56 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, 1 8][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> 58 58 59 59 <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> … … 66 66 67 67 <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, 3 6][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] 69 69 "> abc def אאא אאא hij אאא אאא uvw xyz <div><br/></div><div><br/></div><div><br/></div>אאא kj אאא mn opq אאא אאא</div> 70 70 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 71 77 <!-- 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> 73 79 74 80 <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 18 18 Test 4, RTL: 19 19 Move 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] 25 21 Move 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] 31 23 Test 5, RTL: 32 24 Move 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] 38 26 Move 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] 44 28 Test 6, LTR: 45 29 Move right by one word … … 89 73 Test 15, RTL: 90 74 Move 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] 95 76 Move 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] 98 78 Test 16, LTR: 99 79 Move 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] 112 81 Move 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] 117 83 Test 17, RTL: 118 84 Move left by one word … … 132 98 Test 20, RTL: 133 99 Move 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] 142 101 Move right by one word 143 102 "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 83 83 Test 17, LTR: 84 84 Move 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 91 Move left by one word 92 "abc BAD def xyz"[17, 14, 8, 9, 0] 93 Test 18, LTR: 94 Move right by one word 95 "abc def hij "[0, 4, 8], " opq rst "[1, 5, 8] 96 Move left by one word 97 " opq rst "[8, 5, 1], "abc def hij "[8, 4, 0] 98 Test 19, LTR: 99 Move right by one word 85 100 <DIV>[0] 86 101 Move left by one word -
trunk/LayoutTests/editing/selection/move-by-word-visually-single-space-one-element.html
r102252 r110965 66 66 <div dir=rtl class="test_move_by_word" title="11 8 4 0|0 4 8 11" contenteditable>שנב abc סטז</div> 67 67 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 ‫באד def‬ 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 68 74 <!-- empty div --> 69 75 <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 175 175 var prevOffset = sel.anchorOffset; 176 176 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; 177 181 178 182 while (1) { … … 206 210 207 211 position = { node: sel.anchorNode, offset: sel.anchorOffset }; 208 if ( wordBreakIndex < wordBreaks.length212 if ((wordBreakIndex < wordBreaks.length 209 213 && positionEqualToWordBreak(position, wordBreaks[wordBreakIndex])) 214 || (test == document.getElementById("notReachablePosition") 215 && sel.anchorOffset > wordBreaks[wordBreakIndex] 216 && advance 217 && searchDirection == "right")) { 210 218 ++wordBreakIndex; 219 advance = false; 220 } 211 221 212 222 prevNode = sel.anchorNode; -
trunk/Source/WebCore/ChangeLog
r110964 r110965 1 2012-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 1 50 2012-03-16 Dana Jansens <danakj@chromium.org> 2 51 -
trunk/Source/WebCore/editing/FrameSelection.cpp
r107700 r110965 643 643 break; 644 644 case WordGranularity: 645 #if !OS(WINCE) 646 // Visual word movement relies on isWordTextBreak which is not implemented in WinCE. 645 647 if (visualWordMovementEnabled() 646 648 || (m_frame && m_frame->editor()->behavior().shouldMoveLeftRightByWordInVisualOrder())) { … … 648 650 break; 649 651 } 652 #endif 650 653 case SentenceGranularity: 651 654 case LineGranularity: … … 810 813 break; 811 814 case WordGranularity: 815 #if !OS(WINCE) 812 816 if (visualWordMovementEnabled() 813 817 || (m_frame && m_frame->editor()->behavior().shouldMoveLeftRightByWordInVisualOrder())) { … … 815 819 break; 816 820 } 821 #endif 817 822 case SentenceGranularity: 818 823 case LineGranularity: -
trunk/Source/WebCore/editing/visible_units.cpp
r108382 r110965 50 50 using namespace WTF::Unicode; 51 51 52 static 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 64 static 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 73 static 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 87 static 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. 103 static 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 132 static 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 160 static 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 169 static 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 190 static 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 221 static 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 243 static 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 274 static 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 295 static 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 314 static 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 325 static bool islogicalEndOfWord(TextBreakIterator* iter, int position, bool hardLineBreak) 326 { 327 bool boundary = isTextBreak(iter, position); 328 return (hardLineBreak || boundary) && isWordTextBreak(iter); 329 } 330 331 enum CursorMovementDirection { MoveLeft, MoveRight }; 332 333 static 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 394 VisiblePosition 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 407 VisiblePosition 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 52 421 enum BoundarySearchContextAvailability { DontHaveMoreContext, MayHaveMoreContext }; 53 422 … … 516 885 { 517 886 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;540 887 } 541 888 … … 626 973 } 627 974 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 }656 975 657 976 VisiblePosition nextLinePosition(const VisiblePosition &visiblePosition, int lineDirectionPoint, EditableType editableType) … … 1100 1419 } 1101 1420 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 57 57 int textBreakFollowing(TextBreakIterator*, int); 58 58 bool isTextBreak(TextBreakIterator*, int); 59 bool isWordTextBreak(TextBreakIterator*); 59 60 60 61 const int TextBreakDone = -1; -
trunk/Source/WebCore/platform/text/TextBreakIteratorICU.cpp
r109144 r110965 138 138 { 139 139 return ubrk_isBoundary(reinterpret_cast<UBreakIterator*>(iterator), position); 140 } 141 142 bool isWordTextBreak(TextBreakIterator* iterator) 143 { 144 int ruleStatus = ubrk_getRuleStatus(reinterpret_cast<UBreakIterator*>(iterator)); 145 return ruleStatus != UBRK_WORD_NONE; 140 146 } 141 147 -
trunk/Source/WebCore/platform/text/gtk/TextBreakIteratorGtk.cpp
r95901 r110965 386 386 } 387 387 388 } 388 bool isWordTextBreak(TextBreakIterator*) 389 { 390 return true; 391 } 392 393 } -
trunk/Source/WebCore/platform/text/qt/TextBreakIteratorQt.cpp
r94527 r110965 151 151 } 152 152 153 bool isWordTextBreak(TextBreakIterator*) 154 { 155 return true; 156 } 157 153 158 } -
trunk/Source/WebCore/platform/text/wince/TextBreakIteratorWinCE.cpp
r89592 r110965 318 318 } 319 319 320 bool isWordTextBreak(TextBreakIterator*) 321 { 322 return true; 323 } 324 320 325 TextBreakIterator* cursorMovementIterator(const UChar* string, int length) 321 326 {
Note: See TracChangeset
for help on using the changeset viewer.