WebCore:
authormitz@apple.com <mitz@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 28 Apr 2008 04:46:17 +0000 (04:46 +0000)
committermitz@apple.com <mitz@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 28 Apr 2008 04:46:17 +0000 (04:46 +0000)
        Reviewed by Darin Adler.

        - fix https://bugs.webkit.org/show_bug.cgi?id=3729
          <rdar://problem/4036353> REGRESSION: arrow keys move insertion bar backwards in RTL text

        Test: editing/selection/move-left-right.html

        * editing/SelectionController.cpp:
        (WebCore::SelectionController::modifyMovingRight): Added. Currently
        implemented for character granularity, all other being treated as
        "forward".
        (WebCore::SelectionController::modifyMovingForward): Renamed
        modifyMovingRightForward() to this.
        (WebCore::SelectionController::modifyMovingLeft): Added. Currently
        implemented for character granularity, all other being treated as
        "backward".
        (WebCore::SelectionController::modifyMovingBackward): Renamed
        modifyMovingLeftBackward() to this.
        (WebCore::SelectionController::modify): Changed to call either the
        visual (left/right) or logical (backward/forward) methods depending on
        the 'dir' argument for moves.
        * editing/SelectionController.h:
        * editing/VisiblePosition.cpp:
        (WebCore::VisiblePosition::leftVisuallyDistinctCandidate): Added.
        (WebCore::VisiblePosition::left): Added.
        (WebCore::VisiblePosition::rightVisuallyDistinctCandidate): Added.
        (WebCore::VisiblePosition::right): Added.
        * editing/VisiblePosition.h:

LayoutTests:

        Reviewed by Darin Adler.

        - test for https://bugs.webkit.org/show_bug.cgi?id=3729
          <rdar://problem/4036353> REGRESSION: arrow keys move insertion bar backwards in RTL text

        * editing/selection/move-left-right-expected.txt: Added.
        * editing/selection/move-left-right.html: Added.
        * platform/mac/editing/selection/move-left-right-expected.txt: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@32605 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/editing/selection/move-left-right-expected.txt [new file with mode: 0644]
LayoutTests/editing/selection/move-left-right.html [new file with mode: 0644]
LayoutTests/platform/mac/editing/selection/move-left-right-expected.txt [new file with mode: 0644]
WebCore/ChangeLog
WebCore/editing/SelectionController.cpp
WebCore/editing/SelectionController.h
WebCore/editing/VisiblePosition.cpp
WebCore/editing/VisiblePosition.h

index a44d723..29fdcb2 100644 (file)
@@ -1,3 +1,14 @@
+2008-04-27  Dan Bernstein  <mitz@apple.com>
+
+        Reviewed by Darin Adler.
+
+        - test for https://bugs.webkit.org/show_bug.cgi?id=3729
+          <rdar://problem/4036353> REGRESSION: arrow keys move insertion bar backwards in RTL text
+
+        * editing/selection/move-left-right-expected.txt: Added.
+        * editing/selection/move-left-right.html: Added.
+        * platform/mac/editing/selection/move-left-right-expected.txt: Added.
+
 2008-04-27  Sam Weinig  <sam@webkit.org>
 
         Reviewed by Maciej Stachowiak.
diff --git a/LayoutTests/editing/selection/move-left-right-expected.txt b/LayoutTests/editing/selection/move-left-right-expected.txt
new file mode 100644 (file)
index 0000000..44d8043
--- /dev/null
@@ -0,0 +1,230 @@
+Test 1, LTR:
+  Moving right: "\n        abc\n    "[9, 10, 11, 12]
+  Moving left:  "\n        abc\n    "[12, 11, 10, 9]
+Test 1, RTL:
+  Moving left: "\n        abc\n    "[9, 11, 10, 12]
+  Moving right:  "\n        abc\n    "[12, 10, 11, 9]
+Test 2, LTR:
+  Moving right: "\n        ABC\n    "[9, 11, 10, 12]
+  Moving left:  "\n        ABC\n    "[12, 10, 11, 9]
+Test 2, RTL:
+  Moving left: "\n        ABC\n    "[9, 10, 11, 12]
+  Moving right:  "\n        ABC\n    "[12, 11, 10, 9]
+Test 3, LTR:
+  Moving right: <DIV>[0], "abc\n    "[0, 1, 2, 3]
+  Moving left:  "abc\n    "[3, 2, 1, 0], <DIV>[0]
+Test 3, RTL:
+  Moving left: <DIV>[0], "abc\n    "[0, 2, 1, 3]
+  Moving right:  "abc\n    "[3, 1, 2, 0], <DIV>[0]
+Test 4, LTR:
+  Moving right: <DIV>[0], "ABC\n    "[0, 2, 1, 3]
+  Moving left:  "ABC\n    "[3, 1, 2, 0], <DIV>[0]
+Test 4, RTL:
+  Moving left: <DIV>[0], "ABC\n    "[0, 1, 2, 3]
+  Moving right:  "ABC\n    "[3, 2, 1, 0], <DIV>[0]
+Test 5, LTR:
+  Moving right: "\n        abcABCdef\n    "[9, 10, 11, 12, 14, 13, 15, 16, 17, 18]
+  Moving left:  "\n        abcABCdef\n    "[18, 17, 16, 15, 13, 14, 12, 11, 10, 9]
+Test 5, RTL:
+  Moving left: "\n        abcABCdef\n    "[9, 11, 10, 12, 13, 14, 15, 17, 16, 18]
+  Moving right:  "\n        abcABCdef\n    "[18, 16, 17, 15, 14, 13, 12, 10, 11, 9]
+Test 6, LTR:
+  Moving right: "\n        ABCabcDEF\n    "[9, 11, 10, 12, 13, 14, 15, 17, 16, 18]
+  Moving left:  "\n        ABCabcDEF\n    "[18, 16, 17, 15, 14, 13, 12, 10, 11, 9]
+Test 6, RTL:
+  Moving left: "\n        ABCabcDEF\n    "[9, 10, 11, 12, 14, 13, 15, 16, 17, 18]
+  Moving right:  "\n        ABCabcDEF\n    "[18, 17, 16, 15, 13, 14, 12, 11, 10, 9]
+Test 7, LTR:
+  Moving right: "\n        abcABCDEF\n    "[9, 10, 11, 12, 17, 16, 15, 14, 13, 18]
+  Moving left:  "\n        abcABCDEF\n    "[18, 13, 14, 15, 16, 17, 12, 11, 10, 9]
+Test 7, RTL:
+  Moving left: "\n        abcABCDEF\n    "[9, 11, 10, 12, 13, 14, 15, 16, 17, 18]
+  Moving right:  "\n        abcABCDEF\n    "[18, 17, 16, 15, 14, 13, 12, 10, 11, 9]
+Test 8, LTR:
+  Moving right: "\n        ABCabcdef\n    "[9, 11, 10, 12, 13, 14, 15, 16, 17, 18]
+  Moving left:  "\n        ABCabcdef\n    "[18, 17, 16, 15, 14, 13, 12, 10, 11, 9]
+Test 8, RTL:
+  Moving left: "\n        ABCabcdef\n    "[9, 10, 11, 12, 17, 16, 15, 14, 13, 18]
+  Moving right:  "\n        ABCabcdef\n    "[18, 13, 14, 15, 16, 17, 12, 11, 10, 9]
+Test 9, LTR:
+  Moving right: "abc"[0, 1, 2, 3], "ABCdef\n    "[2, 1, 3, 4, 5, 6]
+  Moving left:  "ABCdef\n    "[6, 5, 4, 3, 1, 2], "abc"[3, 2, 1, 0]
+Test 9, RTL:
+  Moving left: "abc"[0, 2, 1, 3], "ABCdef\n    "[1, 2, 3, 5, 4, 6]
+  Moving right:  "ABCdef\n    "[6, 4, 5, 3, 2, 1], "abc"[3, 1, 2, 0]
+Test 10, LTR:
+  Moving right: "ABC"[0, 2, 1, 3], "abcDEF\n    "[1, 2, 3, 5, 4, 6]
+  Moving left:  "abcDEF\n    "[6, 4, 5, 3, 2, 1], "ABC"[3, 1, 2, 0]
+Test 10, RTL:
+  Moving left: "ABC"[0, 1, 2, 3], "abcDEF\n    "[2, 1, 3, 4, 5, 6]
+  Moving right:  "abcDEF\n    "[6, 5, 4, 3, 1, 2], "ABC"[3, 2, 1, 0]
+Test 11, LTR:
+  Moving right: "abcABC123DEFdef\n    "[0, 1, 2, 3, 11, 10, 6, 7, 8, 9, 5, 4, 12, 13, 14, 15]
+  Moving left:  "abcABC123DEFdef\n    "[15, 14, 13, 12, 4, 5, 9, 8, 7, 6, 10, 11, 3, 2, 1, 0]
+Test 11, RTL:
+  Moving left: "abcABC123DEFdef\n    "[0, 2, 1, 3, 4, 5, 6, 8, 7, 9, 10, 11, 12, 14, 13, 15]
+  Moving right:  "abcABC123DEFdef\n    "[15, 13, 14, 12, 11, 10, 9, 7, 8, 6, 5, 4, 3, 1, 2, 0]
+Test 12, LTR:
+  Moving right: "abcABC123\n    "[0, 1, 2, 3, 7, 8, 5, 4, 9]
+  Moving left:  "abcABC123\n    "[9, 4, 5, 8, 7, 3, 2, 1, 0]
+Test 12, RTL:
+  Moving left: "abcABC123\n    "[0, 2, 1, 3, 4, 5, 6, 8, 7, 9]
+  Moving right:  "abcABC123\n    "[9, 7, 8, 6, 5, 4, 3, 1, 2, 0]
+Test 13, LTR:
+  Moving right: "abcABC123def\n    "[0, 1, 2, 3, 7, 8, 5, 4, 9, 10, 11, 12]
+  Moving left:  "abcABC123def\n    "[12, 11, 10, 9, 4, 5, 8, 7, 6, 2, 1, 0]
+WARNING: Moving to the left did not visit the same positions in reverse order as moving to the right.
+Test 13, RTL:
+  Moving left: "abcABC123def\n    "[0, 2, 1, 3, 4, 5, 6, 11, 10, 9, 8, 7, 12]
+  Moving right:  "abcABC123def\n    "[12, 7, 8, 9, 10, 11, 6, 5, 4, 3, 1, 2, 0]
+Test 14, LTR:
+  Moving right: "ABC123DEFabcGHI456JLM\n    "[0, 8, 7, 3, 4, 5, 6, 2, 1, 9, 10, 11, 12, 20, 19, 15, 16, 17, 18, 14, 13, 21]
+  Moving left:  "ABC123DEFabcGHI456JLM\n    "[21, 13, 14, 18, 17, 16, 15, 19, 20, 12, 11, 10, 9, 1, 2, 6, 5, 4, 3, 7, 8, 0]
+Test 14, RTL:
+  Moving left: "ABC123DEFabcGHI456JLM\n    "[0, 1, 2, 3, 5, 4, 6, 7, 8, 9, 11, 10, 12, 13, 14, 15, 17, 16, 18, 19, 20, 21]
+  Moving right:  "ABC123DEFabcGHI456JLM\n    "[21, 20, 19, 18, 16, 17, 15, 14, 13, 12, 10, 11, 9, 8, 7, 6, 4, 5, 3, 2, 1, 0]
+Test 15, LTR:
+  Moving right: "\n        before    AHYJ AQWJXMFUDJE\n    "[9, 10, 11, 12, 13, 14, 15, 16, 22, 21, 20, 23, 24, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 35]
+  Moving left:  "\n        before    AHYJ AQWJXMFUDJE\n    "[35, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 24, 23, 20, 21, 22, 16, 15, 14, 13, 12, 11, 10, 9]
+Test 15, RTL:
+  Moving left: "\n        before    AHYJ AQWJXMFUDJE\n    "[9, 14, 13, 12, 11, 10, 15, 16, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35]
+  Moving right:  "\n        before    AHYJ AQWJXMFUDJE\n    "[35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 16, 15, 10, 11, 12, 13, 14, 9]
+Test 16, LTR:
+  Moving right: "\n        MUQJ    after encyclopedia\n    "[9, 12, 11, 10, 13, 14, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35]
+  Moving left:  "\n        MUQJ    after encyclopedia\n    "[35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 14, 13, 10, 11, 12, 9]
+Test 16, RTL:
+  Moving left: "\n        MUQJ    after encyclopedia\n    "[9, 10, 11, 12, 13, 14, 21, 20, 19, 18, 22, 23, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 35]
+  Moving right:  "\n        MUQJ    after encyclopedia\n    "[35, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 23, 22, 18, 19, 20, 21, 14, 13, 12, 11, 10, 9]
+Test 17, LTR:
+  Moving right: "\n        before    AHYJ AQWJXMFUDJE\n    "[9, 10, 11, 12, 13, 14, 15, 16, 22, 21, 20, 23, 24, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 35]
+  Moving left:  "\n        before    AHYJ AQWJXMFUDJE\n    "[35, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 24, 23, 20, 21, 22, 16, 15, 14, 13, 12, 11, 10, 9]
+Test 17, RTL:
+  Moving left: "\n        before    AHYJ AQWJXMFUDJE\n    "[9, 14, 13, 12, 11, 10, 15, 16, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35]
+  Moving right:  "\n        before    AHYJ AQWJXMFUDJE\n    "[35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 16, 15, 10, 11, 12, 13, 14, 9]
+Test 18, LTR:
+  Moving right: "\n        MUQJ    after encyclopedia\n    "[9, 12, 11, 10, 13, 14, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35]
+  Moving left:  "\n        MUQJ    after encyclopedia\n    "[35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 14, 13, 10, 11, 12, 9]
+Test 18, RTL:
+  Moving left: "\n        MUQJ    after encyclopedia\n    "[9, 10, 11, 12, 13, 14, 21, 20, 19, 18, 22, 23, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 35]
+  Moving right:  "\n        MUQJ    after encyclopedia\n    "[35, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 23, 22, 18, 19, 20, 21, 14, 13, 12, 11, 10, 9]
+Test 19, LTR:
+  Moving right: "\n        This is JF[Y WY OJ[Y the boxes. \n    "[9, 10, 11, 12, 13, 14, 15, 16, 17, 20, 19, 18, 21, 22, 28, 27, 26, 25, 24, 23, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40]
+  Moving left:  "\n        This is JF[Y WY OJ[Y the boxes. \n    "[40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 23, 24, 25, 26, 27, 28, 22, 21, 18, 19, 20, 17, 16, 15, 14, 13, 12, 11, 10, 9]
+Test 19, RTL:
+  Moving left: "\n        This is JF[Y WY OJ[Y the boxes. \n    "[9, 15, 14, 13, 12, 11, 10, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 32, 31, 33, 34, 38, 37, 36, 35, 39, 40]
+  Moving right:  "\n        This is JF[Y WY OJ[Y the boxes. \n    "[40, 39, 35, 36, 37, 38, 34, 33, 31, 32, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 10, 11, 12, 13, 14, 15, 9]
+Test 20, LTR:
+  Moving right: "\n        This is JF[Y WY OJ[Y the boxes. \n    "[9, 10, 11, 12, 13, 14, 15, 16, 17, 20, 19, 18, 21, 22, 28, 27, 26, 25, 24, 23, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40]
+  Moving left:  "\n        This is JF[Y WY OJ[Y the boxes. \n    "[40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 23, 24, 25, 26, 27, 28, 22, 21, 18, 19, 20, 17, 16, 15, 14, 13, 12, 11, 10, 9]
+Test 20, RTL:
+  Moving left: "\n        This is JF[Y WY OJ[Y the boxes. \n    "[9, 15, 14, 13, 12, 11, 10, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 32, 31, 33, 34, 38, 37, 36, 35, 39, 40]
+  Moving right:  "\n        This is JF[Y WY OJ[Y the boxes. \n    "[40, 39, 35, 36, 37, 38, 34, 33, 31, 32, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 10, 11, 12, 13, 14, 15, 9]
+Test 21, LTR:
+  Moving right: "\n        Lorem\n        "[9, 10, 11, 12, 13, 14], <DIV>[0], "\n        ipsum\n    "[9, 10, 11, 12, 13, 14]
+  Moving left:  "\n        ipsum\n    "[14, 13, 12, 11, 10, 9], <DIV>[0], "\n        Lorem\n        "[14, 13, 12, 11, 10, 9]
+Test 21, RTL:
+  Moving left: "\n        Lorem\n        "[9, 13, 12, 11, 10, 14], <DIV>[0], "\n        ipsum\n    "[9, 13, 12, 11, 10, 14]
+  Moving right:  "\n        ipsum\n    "[14, 10, 11, 12, 13, 9], <DIV>[0], "\n        Lorem\n        "[14, 10, 11, 12, 13, 9]
+Test 22, LTR:
+  Moving right: "\n        WMH[\n        "[9, 12, 11, 10, 13], <DIV>[0], "\n        OWQU[\n    "[9, 13, 12, 11, 10, 14]
+  Moving left:  "\n        OWQU[\n    "[14, 10, 11, 12, 13, 9], <DIV>[0], "\n        WMH[\n        "[13, 10, 11, 12, 9]
+Test 22, RTL:
+  Moving left: "\n        WMH[\n        "[9, 10, 11, 12, 13], <DIV>[0], "\n        OWQU[\n    "[9, 10, 11, 12, 13, 14]
+  Moving right:  "\n        OWQU[\n    "[14, 13, 12, 11, 10, 9], <DIV>[0], "\n        WMH[\n        "[13, 12, 11, 10, 9]
+Test 23, LTR:
+  Moving right: "\n        abcdefABC"[9, 10, 11, 12, 13, 14, 15], "DEF\n    "[2, 1], <DIV>[2], "\n        abcdefABC"[18, 17, 16], "DEF\n    "[3]
+  Moving left:  "DEF\n    "[3], "\n        abcdefABC"[16, 17, 18], <DIV>[2], "DEF\n    "[1, 2], "\n        abcdefABC"[15, 14, 13, 12, 11, 10, 9]
+Test 23, RTL:
+  Moving left: "\n        abcdefABC"[9, 14, 13, 12, 11, 10, 15, 16, 17, 18], <DIV>[2], "DEF\n    "[1, 2, 3]
+  Moving right:  "DEF\n    "[3, 2, 1], <DIV>[2], "\n        abcdefABC"[18, 17, 16, 15, 10, 11, 12, 13, 14, 9]
+Test 24, LTR:
+  Moving right: "\n        ABCDEFabc"[9, 14, 13, 12, 11, 10, 15, 16, 17, 18], <DIV>[2], "def\n    "[1, 2, 3]
+  Moving left:  "def\n    "[3, 2, 1], <DIV>[2], "\n        ABCDEFabc"[18, 17, 16, 15, 10, 11, 12, 13, 14, 9]
+Test 24, RTL:
+  Moving left: "\n        ABCDEFabc"[9, 10, 11, 12, 13, 14, 15], "def\n    "[2, 1], <DIV>[2], "\n        ABCDEFabc"[18, 17, 16], "def\n    "[3]
+  Moving right:  "def\n    "[3], "\n        ABCDEFabc"[16, 17, 18], <DIV>[2], "def\n    "[1, 2], "\n        ABCDEFabc"[15, 14, 13, 12, 11, 10, 9]
+Test 25, LTR:
+  Moving right: "\n        abc"[9, 10, 11, 12], <DIV>[2], "DEFghi\n    "[2, 1], <DIV>[5, 4], "ABC"[3, 2, 1], "DEFghi\n    "[3, 4, 5, 6]
+  Moving left:  "DEFghi\n    "[6, 5, 4, 3], "ABC"[1, 2, 3], <DIV>[4, 5], "DEFghi\n    "[1, 2], <DIV>[2], "\n        abc"[12, 11, 10, 9]
+Test 25, RTL:
+  Moving left: "\n        abc"[9, 11, 10, 12], <DIV>[2], "ABC"[1, 2, 3], <DIV>[4, 5], "DEFghi\n    "[1, 2, 3, 5, 4, 6]
+  Moving right:  "DEFghi\n    "[6, 4, 5, 3, 2, 1], <DIV>[5, 4], "ABC"[3, 2, 1], <DIV>[2], "\n        abc"[12, 10, 11, 9]
+Test 26, LTR:
+  Moving right: "\n        ABC"[9, 11, 10, 12], <DIV>[2], "abc"[1, 2, 3], <DIV>[4, 5], "defDEF\n    "[1, 2, 3, 5, 4, 6]
+  Moving left:  "defDEF\n    "[6, 4, 5, 3, 2, 1], <DIV>[5, 4], "abc"[3, 2, 1], <DIV>[2], "\n        ABC"[12, 10, 11, 9]
+Test 26, RTL:
+  Moving left: "\n        ABC"[9, 10, 11, 12], <DIV>[2], "defDEF\n    "[2, 1], <DIV>[5, 4], "abc"[3, 2, 1], "defDEF\n    "[3, 4, 5, 6]
+  Moving right:  "defDEF\n    "[6, 5, 4, 3], "abc"[1, 2, 3], <DIV>[4, 5], "defDEF\n    "[1, 2], <DIV>[2], "\n        ABC"[12, 11, 10, 9]
+Test 27, LTR:
+  Moving right: "\n        abcABC"[9, 10, 11, 12], "DEF"[2, 1], "\n        abcABC"[15, 14, 13], "DEF"[3]
+  Moving left:  "DEF"[3], "\n        abcABC"[13, 14, 15], "DEF"[1, 2], "\n        abcABC"[12, 11, 10, 9]
+Test 27, RTL:
+  Moving left: "\n        abcABC"[9, 11, 10, 12, 13, 14, 15], "DEF"[1, 2, 3]
+  Moving right:  "DEF"[3, 2, 1], "\n        abcABC"[15, 14, 13, 12, 10, 11, 9]
+Test 28, LTR:
+  Moving right: "\n        ABCabc"[9, 11, 10, 12, 13, 14, 15], "def"[1, 2, 3]
+  Moving left:  "def"[3, 2, 1], "\n        ABCabc"[15, 14, 13, 12, 10, 11, 9]
+Test 28, RTL:
+  Moving left: "\n        ABCabc"[9, 10, 11, 12], "def"[2, 1], "\n        ABCabc"[15, 14, 13], "def"[3]
+  Moving right:  "def"[3], "\n        ABCabc"[13, 14, 15], "def"[1, 2], "\n        ABCabc"[12, 11, 10, 9]
+Test 29, LTR:
+  Moving right: "\n        ab"[9, 10, 11], "cABCdef"[1, 3, 2, 4, 5, 6, 7]
+  Moving left:  "cABCdef"[7, 6, 5, 4, 2, 3, 1], "\n        ab"[11, 10, 9]
+Test 29, RTL:
+  Moving left: "\n        ab"[9, 11, 10], "cABCdef"[1, 2, 3, 4, 6, 5, 7]
+  Moving right:  "cABCdef"[7, 5, 6, 4, 3, 2, 1], "\n        ab"[10, 11, 9]
+Test 30, LTR:
+  Moving right: "\n        AB"[9, 11, 10], "CabcDEF"[1, 2, 3, 4, 6, 5, 7]
+  Moving left:  "CabcDEF"[7, 5, 6, 4, 3, 2, 1], "\n        AB"[10, 11, 9]
+Test 30, RTL:
+  Moving left: "\n        AB"[9, 10, 11], "CabcDEF"[1, 3, 2, 4, 5, 6, 7]
+  Moving right:  "CabcDEF"[7, 6, 5, 4, 2, 3, 1], "\n        AB"[11, 10, 9]
+Test 31, LTR:
+  Moving right: "\n        abc"[9, 10, 11, 12], "ABCdef"[2, 1, 3, 4, 5, 6]
+  Moving left:  "ABCdef"[6, 5, 4, 3, 1, 2], "\n        abc"[12, 11, 10, 9]
+Test 31, RTL:
+  Moving left: "\n        abc"[9, 11, 10, 12], "ABCdef"[1, 2, 3, 5, 4, 6]
+  Moving right:  "ABCdef"[6, 4, 5, 3, 2, 1], "\n        abc"[12, 10, 11, 9]
+Test 32, LTR:
+  Moving right: "\n        ABC"[9, 11, 10, 12], "abcDEF"[1, 2, 3, 5, 4, 6]
+  Moving left:  "abcDEF"[6, 4, 5, 3, 2, 1], "\n        ABC"[12, 10, 11, 9]
+Test 32, RTL:
+  Moving left: "\n        ABC"[9, 10, 11, 12], "abcDEF"[2, 1, 3, 4, 5, 6]
+  Moving right:  "abcDEF"[6, 5, 4, 3, 1, 2], "\n        ABC"[12, 11, 10, 9]
+Test 33, LTR:
+  Moving right: "\n        abcAdef\n    "[9, 10, 11, 12, 13, 14, 15, 16]
+  Moving left:  "\n        abcAdef\n    "[16, 15, 14, 13, 12, 11, 10, 9]
+Test 33, RTL:
+  Moving left: "\n        abcAdef\n    "[9, 11, 10, 12, 13, 15, 14, 16]
+  Moving right:  "\n        abcAdef\n    "[16, 14, 15, 13, 12, 10, 11, 9]
+Test 34, LTR:
+  Moving right: "\n        ABCaDEF\n    "[9, 11, 10, 12, 13, 15, 14, 16]
+  Moving left:  "\n        ABCaDEF\n    "[16, 14, 15, 13, 12, 10, 11, 9]
+Test 34, RTL:
+  Moving left: "\n        ABCaDEF\n    "[9, 10, 11, 12, 13, 14, 15, 16]
+  Moving right:  "\n        ABCaDEF\n    "[16, 15, 14, 13, 12, 11, 10, 9]
+Test 35, LTR:
+  Moving right: "\n        abcABC"[9, 10, 11, 12, 14, 13, 15], "def"[1, 2, 3]
+  Moving left:  "def"[3, 2, 1], "\n        abcABC"[15, 13, 14, 12, 11, 10, 9]
+Test 35, RTL:
+  Moving left: "\n        abcABC"[9, 11, 10, 12, 13, 14, 15], "def"[2, 1, 3]
+  Moving right:  "def"[3, 1, 2], "\n        abcABC"[15, 14, 13, 12, 10, 11, 9]
+Test 36, LTR:
+  Moving right: "\n        ABCabc"[9, 11, 10, 12, 13, 14, 15], "DEF"[2, 1, 3]
+  Moving left:  "DEF"[3, 1, 2], "\n        ABCabc"[15, 14, 13, 12, 10, 11, 9]
+Test 36, RTL:
+  Moving left: "\n        ABCabc"[9, 10, 11, 12, 14, 13, 15], "DEF"[1, 2, 3]
+  Moving right:  "DEF"[3, 2, 1], "\n        ABCabc"[15, 13, 14, 12, 11, 10, 9]
+Test 37, LTR:
+  Moving right: "\n        abcA"[9, 10, 11, 12], "BCdef"[1], "\n        abcA"[13], "BCdef"[2, 3, 4, 5]
+  Moving left:  "BCdef"[5, 4, 3, 2], "\n        abcA"[13], "BCdef"[1], "\n        abcA"[12, 11, 10, 9]
+Test 37, RTL:
+  Moving left: "\n        abcA"[9, 11, 10, 12, 13], "BCdef"[1, 2, 4, 3, 5]
+  Moving right:  "BCdef"[5, 3, 4, 2, 1], "\n        abcA"[13, 12, 10, 11, 9]
+Test 38, LTR:
+  Moving right: "\n        ABCa"[9, 11, 10, 12, 13], "bcDEF"[1, 2, 4, 3, 5]
+  Moving left:  "bcDEF"[5, 3, 4, 2, 1], "\n        ABCa"[13, 12, 10, 11, 9]
+Test 38, RTL:
+  Moving left: "\n        ABCa"[9, 10, 11, 12], "bcDEF"[1], "\n        ABCa"[13], "bcDEF"[2, 3, 4, 5]
+  Moving right:  "bcDEF"[5, 4, 3, 2], "\n        ABCa"[13], "bcDEF"[1], "\n        ABCa"[12, 11, 10, 9]
+
diff --git a/LayoutTests/editing/selection/move-left-right.html b/LayoutTests/editing/selection/move-left-right.html
new file mode 100644 (file)
index 0000000..e03e01f
--- /dev/null
@@ -0,0 +1,327 @@
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+    <style>
+        div.test {
+            -webkit-user-modify: read-write;
+            padding: 4px;
+            border: 1px dashed lightblue;
+            margin: 4px 4px 4px 24px;
+            outline: none;
+            font-family: Lucida Grande;
+            counter-increment: test-number;
+        }
+        div.test:before { content: counter(test-number); position: absolute; left: 8px; font-size: x-small; text-align: right; width: 20px; }
+        div.test span { background-color: #def; }
+        div.test img { width: 1em; height: 1em; background-color: lightgreen; }
+        div.test img + img { background-color: lightblue; }
+        div.test div { border: 1px dashed pink; padding: 3px; height: 2em; }
+    </style>
+    <script>
+        function log(message)
+        {
+            document.getElementById("console").appendChild(document.createTextNode(message));
+        }
+
+        function caretCoordinates()
+        {
+            if (!window.textInputController)
+                return { x: 0, y :0 };
+            var caretRect = textInputController.firstRectForCharacterRange(textInputController.selectedRange()[0], 0);
+            return { x: caretRect[0], y: caretRect[1] };
+        }
+
+        function positionsMovingInDirection(sel, direction)
+        {
+            var positions = [];
+            while (true) {
+                positions.push({ node: sel.anchorNode, offset: sel.anchorOffset, point: caretCoordinates() });
+                sel.modify("move", direction, "character");
+                if (positions[positions.length - 1].node == sel.anchorNode && positions[positions.length - 1].offset == sel.anchorOffset)
+                    break;
+            };
+            return positions;
+        }
+
+        function fold(string)
+        {
+            var result = "";
+            for (var i = 0; i < string.length; ++i) {
+                var char = string.charCodeAt(i);
+                if (char >= 0x05d0)
+                    char -= 0x058f;
+                else if (char == 10) {
+                    result += "\\n";
+                    continue;
+                }
+                result += String.fromCharCode(char);
+            }
+            return result;
+        }
+
+        function logPositions(positions)
+        {
+            for (var i = 0; i < positions.length; ++i) {
+                if (i) {
+                    if (positions[i].node != positions[i - 1].node)
+                        log("]");
+                    log(", ");
+                }
+                if (!i || positions[i].node != positions[i - 1].node)
+                    log((positions[i].node instanceof Text ? '"' + fold(positions[i].node.data) + '"' : "<" + positions[i].node.tagName + ">") + "[");
+                log(positions[i].offset);
+            }
+            log("]");
+        }
+
+        function checkReverseOrder(positions, reversePositions)
+        {
+            var mismatch = positions.length != reversePositions.length;
+            while (!mismatch) {
+                var pos = positions.pop();
+                if (!pos)
+                    break;
+                var reversePos = reversePositions.shift();
+                if (pos.node != reversePos.node || pos.offset != reversePos.offset)
+                    mismatch = true;
+            }
+
+            if (mismatch)
+                log("WARNING: Moving to the left did not visit the same positions in reverse order as moving to the right.\n");
+        }
+
+        function checkCoordinatesMovingRightDown(positions)
+        {
+            for (var i = 1; i < positions.length; ++i) {
+                if (positions[i].point.y > positions[i - 1].point.y || positions[i].point.x < positions[i - 1].point.x && positions[i].point.y >= positions[i - 1].point.y)
+                    log("WARNING: Moved in the wrong direction in step " + i + ": from (" + positions[i - 1].point.x + ", " + positions[i - 1].point.y + ") to (" + positions[i].point.x + ", " + positions[i].point.y + ").\n");
+            }
+        }
+
+        function checkCoordinatesMovingLeftDown(positions)
+        {
+            for (var i = 1; i < positions.length; ++i) {
+                if (positions[i].point.y > positions[i - 1].point.y || positions[i].point.x > positions[i - 1].point.x && positions[i].point.y >= positions[i - 1].point.y)
+                    log("WARNING: Moved in the wrong direction in step " + i + ": from (" + positions[i - 1].point.x + ", " + positions[i - 1].point.y + ") to (" + positions[i].point.x + ", " + positions[i].point.y + ").\n");
+            }
+        }
+
+        function checkCoordinatesMovingRightUp(positions)
+        {
+            for (var i = 1; i < positions.length; ++i) {
+                if (positions[i].point.y < positions[i - 1].point.y || positions[i].point.x < positions[i - 1].point.x && positions[i].point.y <= positions[i - 1].point.y)
+                    log("WARNING: Moved in the wrong direction in step " + i + ": from (" + positions[i - 1].point.x + ", " + positions[i - 1].point.y + ") to (" + positions[i].point.x + ", " + positions[i].point.y + ").\n");
+            }
+        }
+
+        function checkCoordinatesMovingLeftUp(positions)
+        {
+            for (var i = 1; i < positions.length; ++i) {
+                if (positions[i].point.y < positions[i - 1].point.y || positions[i].point.x > positions[i - 1].point.x && positions[i].point.y <= positions[i - 1].point.y)
+                    log("WARNING: Moved in the wrong direction in step " + i + ": from (" + positions[i - 1].point.x + ", " + positions[i - 1].point.y + ") to (" + positions[i].point.x + ", " + positions[i].point.y + ").\n");
+            }
+        }
+
+        onload = function()
+        {
+            if (!window.layoutTestController)
+                return;
+
+            layoutTestController.dumpAsText();
+
+            var tests = document.getElementsByClassName("test");
+            var sel = getSelection();
+            for (var i = 0; i < tests.length; ++i) {
+                var positionsMovingRight;
+                var positionsMovingLeft;
+    
+                log("Test " + (i + 1) + ", LTR:\n  Moving right: ");
+                sel.setPosition(tests[i], 0);
+                positionsMovingRight = positionsMovingInDirection(sel, "right");
+                logPositions(positionsMovingRight);
+                log("\n");
+                checkCoordinatesMovingRightDown(positionsMovingRight);
+
+                log("  Moving left:  ");
+                positionsMovingLeft = positionsMovingInDirection(sel, "left");
+                logPositions(positionsMovingLeft);
+                log("\n");
+                checkCoordinatesMovingLeftUp(positionsMovingLeft);
+
+                checkReverseOrder(positionsMovingLeft, positionsMovingRight);
+
+                tests[i].style.direction = "rtl";
+
+                log("Test " + (i + 1) + ", RTL:\n  Moving left: ");
+                sel.setPosition(tests[i], 0);
+                positionsMovingLeft = positionsMovingInDirection(sel, "left");
+                logPositions(positionsMovingLeft);
+                log("\n");
+                checkCoordinatesMovingLeftDown(positionsMovingLeft);
+
+                log("  Moving right:  ");
+                positionsMovingRight = positionsMovingInDirection(sel, "right");
+                logPositions(positionsMovingRight);
+                log("\n");
+                checkCoordinatesMovingRightUp(positionsMovingRight);
+
+                checkReverseOrder(positionsMovingLeft, positionsMovingRight);
+
+                tests[i].style.display = "none";
+            }
+        }
+
+    </script>
+</head>
+<body>
+    <div class="test">
+        abc
+    </div>
+
+    <div class="test">
+        אבג
+    </div>
+
+    <div class="test"><br>abc
+    </div>
+
+    <div class="test"><br>אבג
+    </div>
+
+    <div class="test">
+        abcאבגdef
+    </div>
+
+    <div class="test">
+        אבגabcדהו
+    </div>
+
+    <div class="test">
+        abcאבגדהו
+    </div>
+
+    <div class="test">
+        אבגabcdef
+    </div>
+
+    <div class="test">
+        <span>abc</span>אבגdef
+    </div>
+
+    <div class="test">
+        <span>אבג</span>abcדהו
+    </div>
+
+    <div class="test">abcאבג123דהוdef
+    </div>
+
+    <div class="test">abcאבג123
+    </div>
+
+    <div class="test">abcאבג123def
+    </div>
+
+    <div class="test">אבג123דהוabcזחט456יכל
+    </div>
+
+    <div class="test" style="width: 120px;">
+        before    אחרי אנציקלופדיה
+    </div>
+
+    <div class="test" style="width: 120px;">
+        לפני    after encyclopedia
+    </div>
+
+    <div class="test" contenteditable style="width: 120px;">
+        before    אחרי אנציקלופדיה
+    </div>
+
+    <div class="test" contenteditable style="width: 120px;">
+        לפני    after encyclopedia
+    </div>
+
+    <div class="test" style="width: 100px;">
+        This is יותר צר מיתר the boxes. 
+    </div>
+
+    <div contenteditable class="test" style="width: 100px;">
+        This is יותר צר מיתר the boxes. 
+    </div>
+
+    <div class="test">
+        Lorem
+        <div></div>
+        ipsum
+    </div>
+
+    <div class="test">
+        צלחת
+        <div></div>
+        מצנפת
+    </div>
+
+    <div class="test">
+        abcdefאבג<img>דהו
+    </div>
+
+    <div class="test">
+        אבגדהוabc<img>def
+    </div>
+
+    <div class="test">
+        abc<input>אבג<img><img>דהוghi
+    </div>
+
+    <div class="test">
+        אבג<input>abc<img><img>defדהו
+    </div>
+
+    <div class="test">
+        abcאבג<span>דהו</span>
+    </div>
+
+    <div class="test">
+        אבגabc<span>def</span>
+    </div>
+
+    <div class="test">
+        ab<span>cאבגdef</span>
+    </div>
+
+    <div class="test">
+        אב<span>גabcדהו</span>
+    </div>
+
+    <div class="test">
+        abc<span>אבגdef</span>
+    </div>
+
+    <div class="test">
+        אבג<span>abcדהו</span>
+    </div>
+
+    <div class="test">
+        abcאdef
+    </div>
+
+    <div class="test">
+        אבגaדהו
+    </div>
+
+    <div class="test">
+        abcאבג<span>def</span>
+    </div>
+
+    <div class="test">
+        אבגabc<span>דהו</span>
+    </div>
+
+    <div class="test">
+        abcא<span>בגdef</span>
+    </div>
+
+    <div class="test">
+        אבגa<span>bcדהו</span>
+    </div>
+
+    <pre id="console"></pre>
+</body>
diff --git a/LayoutTests/platform/mac/editing/selection/move-left-right-expected.txt b/LayoutTests/platform/mac/editing/selection/move-left-right-expected.txt
new file mode 100644 (file)
index 0000000..e3df31e
--- /dev/null
@@ -0,0 +1,238 @@
+Test 1, LTR:
+  Moving right: "\n        abc\n    "[9, 10, 11, 12]
+  Moving left:  "\n        abc\n    "[12, 11, 10, 9]
+Test 1, RTL:
+  Moving left: "\n        abc\n    "[9, 11, 10, 12]
+  Moving right:  "\n        abc\n    "[12, 10, 11, 9]
+Test 2, LTR:
+  Moving right: "\n        ABC\n    "[9, 11, 10, 12]
+  Moving left:  "\n        ABC\n    "[12, 10, 11, 9]
+Test 2, RTL:
+  Moving left: "\n        ABC\n    "[9, 10, 11, 12]
+  Moving right:  "\n        ABC\n    "[12, 11, 10, 9]
+Test 3, LTR:
+  Moving right: <DIV>[0], "abc\n    "[0, 1, 2, 3]
+  Moving left:  "abc\n    "[3, 2, 1, 0], <DIV>[0]
+Test 3, RTL:
+  Moving left: <DIV>[0], "abc\n    "[0, 2, 1, 3]
+  Moving right:  "abc\n    "[3, 1, 2, 0], <DIV>[0]
+Test 4, LTR:
+  Moving right: <DIV>[0], "ABC\n    "[0, 2, 1, 3]
+  Moving left:  "ABC\n    "[3, 1, 2, 0], <DIV>[0]
+Test 4, RTL:
+  Moving left: <DIV>[0], "ABC\n    "[0, 1, 2, 3]
+  Moving right:  "ABC\n    "[3, 2, 1, 0], <DIV>[0]
+Test 5, LTR:
+  Moving right: "\n        abcABCdef\n    "[9, 10, 11, 12, 14, 13, 15, 16, 17, 18]
+  Moving left:  "\n        abcABCdef\n    "[18, 17, 16, 15, 13, 14, 12, 11, 10, 9]
+Test 5, RTL:
+  Moving left: "\n        abcABCdef\n    "[9, 11, 10, 12, 13, 14, 15, 17, 16, 18]
+  Moving right:  "\n        abcABCdef\n    "[18, 16, 17, 15, 14, 13, 12, 10, 11, 9]
+Test 6, LTR:
+  Moving right: "\n        ABCabcDEF\n    "[9, 11, 10, 12, 13, 14, 15, 17, 16, 18]
+  Moving left:  "\n        ABCabcDEF\n    "[18, 16, 17, 15, 14, 13, 12, 10, 11, 9]
+Test 6, RTL:
+  Moving left: "\n        ABCabcDEF\n    "[9, 10, 11, 12, 14, 13, 15, 16, 17, 18]
+  Moving right:  "\n        ABCabcDEF\n    "[18, 17, 16, 15, 13, 14, 12, 11, 10, 9]
+Test 7, LTR:
+  Moving right: "\n        abcABCDEF\n    "[9, 10, 11, 12, 17, 16, 15, 14, 13, 18]
+  Moving left:  "\n        abcABCDEF\n    "[18, 13, 14, 15, 16, 17, 12, 11, 10, 9]
+Test 7, RTL:
+  Moving left: "\n        abcABCDEF\n    "[9, 11, 10, 12, 13, 14, 15, 16, 17, 18]
+  Moving right:  "\n        abcABCDEF\n    "[18, 17, 16, 15, 14, 13, 12, 10, 11, 9]
+Test 8, LTR:
+  Moving right: "\n        ABCabcdef\n    "[9, 11, 10, 12, 13, 14, 15, 16, 17, 18]
+  Moving left:  "\n        ABCabcdef\n    "[18, 17, 16, 15, 14, 13, 12, 10, 11, 9]
+Test 8, RTL:
+  Moving left: "\n        ABCabcdef\n    "[9, 10, 11, 12, 17, 16, 15, 14, 13, 18]
+  Moving right:  "\n        ABCabcdef\n    "[18, 13, 14, 15, 16, 17, 12, 11, 10, 9]
+Test 9, LTR:
+  Moving right: "abc"[0, 1, 2, 3], "ABCdef\n    "[2, 1, 3, 4, 5, 6]
+  Moving left:  "ABCdef\n    "[6, 5, 4, 3, 1, 2], "abc"[3, 2, 1, 0]
+Test 9, RTL:
+  Moving left: "abc"[0, 2, 1, 3], "ABCdef\n    "[1, 2, 3, 5, 4, 6]
+  Moving right:  "ABCdef\n    "[6, 4, 5, 3, 2, 1], "abc"[3, 1, 2, 0]
+Test 10, LTR:
+  Moving right: "ABC"[0, 2, 1, 3], "abcDEF\n    "[1, 2, 3, 5, 4, 6]
+  Moving left:  "abcDEF\n    "[6, 4, 5, 3, 2, 1], "ABC"[3, 1, 2, 0]
+Test 10, RTL:
+  Moving left: "ABC"[0, 1, 2, 3], "abcDEF\n    "[2, 1, 3, 4, 5, 6]
+  Moving right:  "abcDEF\n    "[6, 5, 4, 3, 1, 2], "ABC"[3, 2, 1, 0]
+Test 11, LTR:
+  Moving right: "abcABC123DEFdef\n    "[0, 1, 2, 3, 11, 10, 6, 7, 8, 9, 5, 4, 12, 13, 14, 15]
+  Moving left:  "abcABC123DEFdef\n    "[15, 14, 13, 12, 4, 5, 9, 8, 7, 6, 10, 11, 3, 2, 1, 0]
+Test 11, RTL:
+  Moving left: "abcABC123DEFdef\n    "[0, 2, 1, 3, 4, 5, 6, 8, 7, 9, 10, 11, 12, 14, 13, 15]
+  Moving right:  "abcABC123DEFdef\n    "[15, 13, 14, 12, 11, 10, 9, 7, 8, 6, 5, 4, 3, 1, 2, 0]
+Test 12, LTR:
+  Moving right: "abcABC123\n    "[0, 1, 2, 3, 7, 8, 5, 4, 9]
+  Moving left:  "abcABC123\n    "[9, 4, 5, 8, 7, 3, 2, 1, 0]
+Test 12, RTL:
+  Moving left: "abcABC123\n    "[0, 2, 1, 3, 4, 5, 6, 8, 7, 9]
+  Moving right:  "abcABC123\n    "[9, 7, 8, 6, 5, 4, 3, 1, 2, 0]
+Test 13, LTR:
+  Moving right: "abcABC123def\n    "[0, 1, 2, 3, 7, 8, 5, 4, 9, 10, 11, 12]
+  Moving left:  "abcABC123def\n    "[12, 11, 10, 9, 4, 5, 8, 7, 6, 2, 1, 0]
+WARNING: Moving to the left did not visit the same positions in reverse order as moving to the right.
+Test 13, RTL:
+  Moving left: "abcABC123def\n    "[0, 2, 1, 3, 4, 5, 6, 11, 10, 9, 8, 7, 12]
+  Moving right:  "abcABC123def\n    "[12, 7, 8, 9, 10, 11, 6, 5, 4, 3, 1, 2, 0]
+Test 14, LTR:
+  Moving right: "ABC123DEFabcGHI456JLM\n    "[0, 8, 7, 3, 4, 5, 6, 2, 1, 9, 10, 11, 12, 20, 19, 15, 16, 17, 18, 14, 13, 21]
+  Moving left:  "ABC123DEFabcGHI456JLM\n    "[21, 13, 14, 18, 17, 16, 15, 19, 20, 12, 11, 10, 9, 1, 2, 6, 5, 4, 3, 7, 8, 0]
+Test 14, RTL:
+  Moving left: "ABC123DEFabcGHI456JLM\n    "[0, 1, 2, 3, 5, 4, 6, 7, 8, 9, 11, 10, 12, 13, 14, 15, 17, 16, 18, 19, 20, 21]
+  Moving right:  "ABC123DEFabcGHI456JLM\n    "[21, 20, 19, 18, 16, 17, 15, 14, 13, 12, 10, 11, 9, 8, 7, 6, 4, 5, 3, 2, 1, 0]
+Test 15, LTR:
+  Moving right: "\n        before    AHYJ AQWJXMFUDJE\n    "[9, 10, 11, 12, 13, 14, 15, 16, 22, 21, 20, 23, 24, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 35]
+WARNING: Moved in the wrong direction in step 13: from (117, 551) to (49, 551).
+  Moving left:  "\n        before    AHYJ AQWJXMFUDJE\n    "[35, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 24, 23, 20, 21, 22, 16, 15, 14, 13, 12, 11, 10, 9]
+WARNING: Moved in the wrong direction in step 11: from (49, 551) to (117, 551).
+Test 15, RTL:
+  Moving left: "\n        before    AHYJ AQWJXMFUDJE\n    "[9, 14, 13, 12, 11, 10, 15, 16, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35]
+  Moving right:  "\n        before    AHYJ AQWJXMFUDJE\n    "[35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 16, 15, 10, 11, 12, 13, 14, 9]
+Test 16, LTR:
+  Moving right: "\n        MUQJ    after encyclopedia\n    "[9, 12, 11, 10, 13, 14, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35]
+  Moving left:  "\n        MUQJ    after encyclopedia\n    "[35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 14, 13, 10, 11, 12, 9]
+Test 16, RTL:
+  Moving left: "\n        MUQJ    after encyclopedia\n    "[9, 10, 11, 12, 13, 14, 21, 20, 19, 18, 22, 23, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 35]
+WARNING: Moved in the wrong direction in step 12: from (65, 551) to (148, 551).
+  Moving right:  "\n        MUQJ    after encyclopedia\n    "[35, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 23, 22, 18, 19, 20, 21, 14, 13, 12, 11, 10, 9]
+WARNING: Moved in the wrong direction in step 12: from (148, 551) to (65, 551).
+Test 17, LTR:
+  Moving right: "\n        before    AHYJ AQWJXMFUDJE\n    "[9, 10, 11, 12, 13, 14, 15, 16, 22, 21, 20, 23, 24, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 35]
+  Moving left:  "\n        before    AHYJ AQWJXMFUDJE\n    "[35, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 24, 23, 20, 21, 22, 16, 15, 14, 13, 12, 11, 10, 9]
+Test 17, RTL:
+  Moving left: "\n        before    AHYJ AQWJXMFUDJE\n    "[9, 14, 13, 12, 11, 10, 15, 16, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35]
+  Moving right:  "\n        before    AHYJ AQWJXMFUDJE\n    "[35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 16, 15, 10, 11, 12, 13, 14, 9]
+Test 18, LTR:
+  Moving right: "\n        MUQJ    after encyclopedia\n    "[9, 12, 11, 10, 13, 14, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35]
+  Moving left:  "\n        MUQJ    after encyclopedia\n    "[35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 14, 13, 10, 11, 12, 9]
+Test 18, RTL:
+  Moving left: "\n        MUQJ    after encyclopedia\n    "[9, 10, 11, 12, 13, 14, 21, 20, 19, 18, 22, 23, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 35]
+  Moving right:  "\n        MUQJ    after encyclopedia\n    "[35, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 23, 22, 18, 19, 20, 21, 14, 13, 12, 11, 10, 9]
+Test 19, LTR:
+  Moving right: "\n        This is JF[Y WY OJ[Y the boxes. \n    "[9, 10, 11, 12, 13, 14, 15, 16, 17, 20, 19, 18, 21, 22, 28, 27, 26, 25, 24, 23, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40]
+WARNING: Moved in the wrong direction in step 14: from (87, 551) to (46, 551).
+  Moving left:  "\n        This is JF[Y WY OJ[Y the boxes. \n    "[40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 23, 24, 25, 26, 27, 28, 22, 21, 18, 19, 20, 17, 16, 15, 14, 13, 12, 11, 10, 9]
+WARNING: Moved in the wrong direction in step 18: from (46, 551) to (87, 551).
+Test 19, RTL:
+  Moving left: "\n        This is JF[Y WY OJ[Y the boxes. \n    "[9, 15, 14, 13, 12, 11, 10, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 32, 31, 33, 34, 38, 37, 36, 35, 39, 40]
+WARNING: Moved in the wrong direction in step 26: from (100, 533) to (129, 533).
+  Moving right:  "\n        This is JF[Y WY OJ[Y the boxes. \n    "[40, 39, 35, 36, 37, 38, 34, 33, 31, 32, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 10, 11, 12, 13, 14, 15, 9]
+WARNING: Moved in the wrong direction in step 6: from (129, 533) to (100, 533).
+Test 20, LTR:
+  Moving right: "\n        This is JF[Y WY OJ[Y the boxes. \n    "[9, 10, 11, 12, 13, 14, 15, 16, 17, 20, 19, 18, 21, 22, 28, 27, 26, 25, 24, 23, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40]
+  Moving left:  "\n        This is JF[Y WY OJ[Y the boxes. \n    "[40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 23, 24, 25, 26, 27, 28, 22, 21, 18, 19, 20, 17, 16, 15, 14, 13, 12, 11, 10, 9]
+Test 20, RTL:
+  Moving left: "\n        This is JF[Y WY OJ[Y the boxes. \n    "[9, 15, 14, 13, 12, 11, 10, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 32, 31, 33, 34, 38, 37, 36, 35, 39, 40]
+  Moving right:  "\n        This is JF[Y WY OJ[Y the boxes. \n    "[40, 39, 35, 36, 37, 38, 34, 33, 31, 32, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 10, 11, 12, 13, 14, 15, 9]
+Test 21, LTR:
+  Moving right: "\n        Lorem\n        "[9, 10, 11, 12, 13, 14], <DIV>[0], "\n        ipsum\n    "[9, 10, 11, 12, 13, 14]
+  Moving left:  "\n        ipsum\n    "[14, 13, 12, 11, 10, 9], <DIV>[0], "\n        Lorem\n        "[14, 13, 12, 11, 10, 9]
+Test 21, RTL:
+  Moving left: "\n        Lorem\n        "[9, 13, 12, 11, 10, 14], <DIV>[0], "\n        ipsum\n    "[9, 13, 12, 11, 10, 14]
+  Moving right:  "\n        ipsum\n    "[14, 10, 11, 12, 13, 9], <DIV>[0], "\n        Lorem\n        "[14, 10, 11, 12, 13, 9]
+Test 22, LTR:
+  Moving right: "\n        WMH[\n        "[9, 12, 11, 10, 13], <DIV>[0], "\n        OWQU[\n    "[9, 13, 12, 11, 10, 14]
+  Moving left:  "\n        OWQU[\n    "[14, 10, 11, 12, 13, 9], <DIV>[0], "\n        WMH[\n        "[13, 10, 11, 12, 9]
+Test 22, RTL:
+  Moving left: "\n        WMH[\n        "[9, 10, 11, 12, 13], <DIV>[0], "\n        OWQU[\n    "[9, 10, 11, 12, 13, 14]
+  Moving right:  "\n        OWQU[\n    "[14, 13, 12, 11, 10, 9], <DIV>[0], "\n        WMH[\n        "[13, 12, 11, 10, 9]
+Test 23, LTR:
+  Moving right: "\n        abcdefABC"[9, 10, 11, 12, 13, 14, 15], "DEF\n    "[2, 1], <DIV>[2], "\n        abcdefABC"[18, 17, 16], "DEF\n    "[3]
+  Moving left:  "DEF\n    "[3], "\n        abcdefABC"[16, 17, 18], <DIV>[2], "DEF\n    "[1, 2], "\n        abcdefABC"[15, 14, 13, 12, 11, 10, 9]
+Test 23, RTL:
+  Moving left: "\n        abcdefABC"[9, 14, 13, 12, 11, 10, 15, 16, 17, 18], <DIV>[2], "DEF\n    "[1, 2, 3]
+  Moving right:  "DEF\n    "[3, 2, 1], <DIV>[2], "\n        abcdefABC"[18, 17, 16, 15, 10, 11, 12, 13, 14, 9]
+Test 24, LTR:
+  Moving right: "\n        ABCDEFabc"[9, 14, 13, 12, 11, 10, 15, 16, 17, 18], <DIV>[2], "def\n    "[1, 2, 3]
+  Moving left:  "def\n    "[3, 2, 1], <DIV>[2], "\n        ABCDEFabc"[18, 17, 16, 15, 10, 11, 12, 13, 14, 9]
+Test 24, RTL:
+  Moving left: "\n        ABCDEFabc"[9, 10, 11, 12, 13, 14, 15], "def\n    "[2, 1], <DIV>[2], "\n        ABCDEFabc"[18, 17, 16], "def\n    "[3]
+  Moving right:  "def\n    "[3], "\n        ABCDEFabc"[16, 17, 18], <DIV>[2], "def\n    "[1, 2], "\n        ABCDEFabc"[15, 14, 13, 12, 11, 10, 9]
+Test 25, LTR:
+  Moving right: "\n        abc"[9, 10, 11, 12], <DIV>[2], "DEFghi\n    "[2, 1], <DIV>[5, 4], "ABC"[3, 2, 1], "DEFghi\n    "[3, 4, 5, 6]
+  Moving left:  "DEFghi\n    "[6, 5, 4, 3], "ABC"[1, 2, 3], <DIV>[4, 5], "DEFghi\n    "[1, 2], <DIV>[2], "\n        abc"[12, 11, 10, 9]
+Test 25, RTL:
+  Moving left: "\n        abc"[9, 11, 10, 12], <DIV>[2], "ABC"[1, 2, 3], <DIV>[4, 5], "DEFghi\n    "[1, 2, 3, 5, 4, 6]
+  Moving right:  "DEFghi\n    "[6, 4, 5, 3, 2, 1], <DIV>[5, 4], "ABC"[3, 2, 1], <DIV>[2], "\n        abc"[12, 10, 11, 9]
+Test 26, LTR:
+  Moving right: "\n        ABC"[9, 11, 10, 12], <DIV>[2], "abc"[1, 2, 3], <DIV>[4, 5], "defDEF\n    "[1, 2, 3, 5, 4, 6]
+  Moving left:  "defDEF\n    "[6, 4, 5, 3, 2, 1], <DIV>[5, 4], "abc"[3, 2, 1], <DIV>[2], "\n        ABC"[12, 10, 11, 9]
+Test 26, RTL:
+  Moving left: "\n        ABC"[9, 10, 11, 12], <DIV>[2], "defDEF\n    "[2, 1], <DIV>[5, 4], "abc"[3, 2, 1], "defDEF\n    "[3, 4, 5, 6]
+  Moving right:  "defDEF\n    "[6, 5, 4, 3], "abc"[1, 2, 3], <DIV>[4, 5], "defDEF\n    "[1, 2], <DIV>[2], "\n        ABC"[12, 11, 10, 9]
+Test 27, LTR:
+  Moving right: "\n        abcABC"[9, 10, 11, 12], "DEF"[2, 1], "\n        abcABC"[15, 14, 13], "DEF"[3]
+  Moving left:  "DEF"[3], "\n        abcABC"[13, 14, 15], "DEF"[1, 2], "\n        abcABC"[12, 11, 10, 9]
+Test 27, RTL:
+  Moving left: "\n        abcABC"[9, 11, 10, 12, 13, 14, 15], "DEF"[1, 2, 3]
+  Moving right:  "DEF"[3, 2, 1], "\n        abcABC"[15, 14, 13, 12, 10, 11, 9]
+Test 28, LTR:
+  Moving right: "\n        ABCabc"[9, 11, 10, 12, 13, 14, 15], "def"[1, 2, 3]
+  Moving left:  "def"[3, 2, 1], "\n        ABCabc"[15, 14, 13, 12, 10, 11, 9]
+Test 28, RTL:
+  Moving left: "\n        ABCabc"[9, 10, 11, 12], "def"[2, 1], "\n        ABCabc"[15, 14, 13], "def"[3]
+  Moving right:  "def"[3], "\n        ABCabc"[13, 14, 15], "def"[1, 2], "\n        ABCabc"[12, 11, 10, 9]
+Test 29, LTR:
+  Moving right: "\n        ab"[9, 10, 11], "cABCdef"[1, 3, 2, 4, 5, 6, 7]
+  Moving left:  "cABCdef"[7, 6, 5, 4, 2, 3, 1], "\n        ab"[11, 10, 9]
+Test 29, RTL:
+  Moving left: "\n        ab"[9, 11, 10], "cABCdef"[1, 2, 3, 4, 6, 5, 7]
+  Moving right:  "cABCdef"[7, 5, 6, 4, 3, 2, 1], "\n        ab"[10, 11, 9]
+Test 30, LTR:
+  Moving right: "\n        AB"[9, 11, 10], "CabcDEF"[1, 2, 3, 4, 6, 5, 7]
+  Moving left:  "CabcDEF"[7, 5, 6, 4, 3, 2, 1], "\n        AB"[10, 11, 9]
+Test 30, RTL:
+  Moving left: "\n        AB"[9, 10, 11], "CabcDEF"[1, 3, 2, 4, 5, 6, 7]
+  Moving right:  "CabcDEF"[7, 6, 5, 4, 2, 3, 1], "\n        AB"[11, 10, 9]
+Test 31, LTR:
+  Moving right: "\n        abc"[9, 10, 11, 12], "ABCdef"[2, 1, 3, 4, 5, 6]
+  Moving left:  "ABCdef"[6, 5, 4, 3, 1, 2], "\n        abc"[12, 11, 10, 9]
+Test 31, RTL:
+  Moving left: "\n        abc"[9, 11, 10, 12], "ABCdef"[1, 2, 3, 5, 4, 6]
+  Moving right:  "ABCdef"[6, 4, 5, 3, 2, 1], "\n        abc"[12, 10, 11, 9]
+Test 32, LTR:
+  Moving right: "\n        ABC"[9, 11, 10, 12], "abcDEF"[1, 2, 3, 5, 4, 6]
+  Moving left:  "abcDEF"[6, 4, 5, 3, 2, 1], "\n        ABC"[12, 10, 11, 9]
+Test 32, RTL:
+  Moving left: "\n        ABC"[9, 10, 11, 12], "abcDEF"[2, 1, 3, 4, 5, 6]
+  Moving right:  "abcDEF"[6, 5, 4, 3, 1, 2], "\n        ABC"[12, 11, 10, 9]
+Test 33, LTR:
+  Moving right: "\n        abcAdef\n    "[9, 10, 11, 12, 13, 14, 15, 16]
+  Moving left:  "\n        abcAdef\n    "[16, 15, 14, 13, 12, 11, 10, 9]
+Test 33, RTL:
+  Moving left: "\n        abcAdef\n    "[9, 11, 10, 12, 13, 15, 14, 16]
+  Moving right:  "\n        abcAdef\n    "[16, 14, 15, 13, 12, 10, 11, 9]
+Test 34, LTR:
+  Moving right: "\n        ABCaDEF\n    "[9, 11, 10, 12, 13, 15, 14, 16]
+  Moving left:  "\n        ABCaDEF\n    "[16, 14, 15, 13, 12, 10, 11, 9]
+Test 34, RTL:
+  Moving left: "\n        ABCaDEF\n    "[9, 10, 11, 12, 13, 14, 15, 16]
+  Moving right:  "\n        ABCaDEF\n    "[16, 15, 14, 13, 12, 11, 10, 9]
+Test 35, LTR:
+  Moving right: "\n        abcABC"[9, 10, 11, 12, 14, 13, 15], "def"[1, 2, 3]
+  Moving left:  "def"[3, 2, 1], "\n        abcABC"[15, 13, 14, 12, 11, 10, 9]
+Test 35, RTL:
+  Moving left: "\n        abcABC"[9, 11, 10, 12, 13, 14, 15], "def"[2, 1, 3]
+  Moving right:  "def"[3, 1, 2], "\n        abcABC"[15, 14, 13, 12, 10, 11, 9]
+Test 36, LTR:
+  Moving right: "\n        ABCabc"[9, 11, 10, 12, 13, 14, 15], "DEF"[2, 1, 3]
+  Moving left:  "DEF"[3, 1, 2], "\n        ABCabc"[15, 14, 13, 12, 10, 11, 9]
+Test 36, RTL:
+  Moving left: "\n        ABCabc"[9, 10, 11, 12, 14, 13, 15], "DEF"[1, 2, 3]
+  Moving right:  "DEF"[3, 2, 1], "\n        ABCabc"[15, 13, 14, 12, 11, 10, 9]
+Test 37, LTR:
+  Moving right: "\n        abcA"[9, 10, 11, 12], "BCdef"[1], "\n        abcA"[13], "BCdef"[2, 3, 4, 5]
+  Moving left:  "BCdef"[5, 4, 3, 2], "\n        abcA"[13], "BCdef"[1], "\n        abcA"[12, 11, 10, 9]
+Test 37, RTL:
+  Moving left: "\n        abcA"[9, 11, 10, 12, 13], "BCdef"[1, 2, 4, 3, 5]
+  Moving right:  "BCdef"[5, 3, 4, 2, 1], "\n        abcA"[13, 12, 10, 11, 9]
+Test 38, LTR:
+  Moving right: "\n        ABCa"[9, 11, 10, 12, 13], "bcDEF"[1, 2, 4, 3, 5]
+  Moving left:  "bcDEF"[5, 3, 4, 2, 1], "\n        ABCa"[13, 12, 10, 11, 9]
+Test 38, RTL:
+  Moving left: "\n        ABCa"[9, 10, 11, 12], "bcDEF"[1], "\n        ABCa"[13], "bcDEF"[2, 3, 4, 5]
+  Moving right:  "bcDEF"[5, 4, 3, 2], "\n        ABCa"[13], "bcDEF"[1], "\n        ABCa"[12, 11, 10, 9]
+
index 35c43ae..c3bcee8 100644 (file)
@@ -1,3 +1,34 @@
+2008-04-27  Dan Bernstein  <mitz@apple.com>
+
+        Reviewed by Darin Adler.
+
+        - fix https://bugs.webkit.org/show_bug.cgi?id=3729
+          <rdar://problem/4036353> REGRESSION: arrow keys move insertion bar backwards in RTL text
+
+        Test: editing/selection/move-left-right.html
+
+        * editing/SelectionController.cpp:
+        (WebCore::SelectionController::modifyMovingRight): Added. Currently
+        implemented for character granularity, all other being treated as
+        "forward".
+        (WebCore::SelectionController::modifyMovingForward): Renamed
+        modifyMovingRightForward() to this.
+        (WebCore::SelectionController::modifyMovingLeft): Added. Currently
+        implemented for character granularity, all other being treated as
+        "backward".
+        (WebCore::SelectionController::modifyMovingBackward): Renamed
+        modifyMovingLeftBackward() to this.
+        (WebCore::SelectionController::modify): Changed to call either the
+        visual (left/right) or logical (backward/forward) methods depending on
+        the 'dir' argument for moves.
+        * editing/SelectionController.h:
+        * editing/VisiblePosition.cpp:
+        (WebCore::VisiblePosition::leftVisuallyDistinctCandidate): Added.
+        (WebCore::VisiblePosition::left): Added.
+        (WebCore::VisiblePosition::rightVisuallyDistinctCandidate): Added.
+        (WebCore::VisiblePosition::right): Added.
+        * editing/VisiblePosition.h:
+
 2008-04-27  Sam Weinig  <sam@webkit.org>
 
         Reviewed by Maciej Stachowiak.
index 6f46571..1b73411 100644 (file)
@@ -285,7 +285,32 @@ VisiblePosition SelectionController::modifyExtendingRightForward(TextGranularity
     return pos;
 }
 
-VisiblePosition SelectionController::modifyMovingRightForward(TextGranularity granularity)
+VisiblePosition SelectionController::modifyMovingRight(TextGranularity granularity)
+{
+    VisiblePosition pos;
+    switch (granularity) {
+        case CharacterGranularity:
+            if (isRange()) 
+                pos = VisiblePosition(m_sel.end(), m_sel.affinity());
+            else
+                pos = VisiblePosition(m_sel.extent(), m_sel.affinity()).right(true);
+            break;
+        case WordGranularity:
+        case SentenceGranularity:
+        case LineGranularity:
+        case ParagraphGranularity:
+        case SentenceBoundary:
+        case LineBoundary:
+        case ParagraphBoundary:
+        case DocumentBoundary:
+            // FIXME: Implement all of the above.
+            pos = modifyMovingForward(granularity);
+            break;
+    }
+    return pos;
+}
+
+VisiblePosition SelectionController::modifyMovingForward(TextGranularity granularity)
 {
     VisiblePosition pos;
     // FIXME: Stay in editable content for the less common granularities.
@@ -378,7 +403,32 @@ VisiblePosition SelectionController::modifyExtendingLeftBackward(TextGranularity
     return pos;
 }
 
-VisiblePosition SelectionController::modifyMovingLeftBackward(TextGranularity granularity)
+VisiblePosition SelectionController::modifyMovingLeft(TextGranularity granularity)
+{
+    VisiblePosition pos;
+    switch (granularity) {
+        case CharacterGranularity:
+            if (isRange()) 
+                pos = VisiblePosition(m_sel.start(), m_sel.affinity());
+            else
+                pos = VisiblePosition(m_sel.extent(), m_sel.affinity()).left(true);
+            break;
+        case WordGranularity:
+        case SentenceGranularity:
+        case LineGranularity:
+        case ParagraphGranularity:
+        case SentenceBoundary:
+        case LineBoundary:
+        case ParagraphBoundary:
+        case DocumentBoundary:
+            // FIXME: Implement all of the above.
+            pos = modifyMovingBackward(granularity);
+            break;
+    }
+    return pos;
+}
+
+VisiblePosition SelectionController::modifyMovingBackward(TextGranularity granularity)
 {
     VisiblePosition pos;
     switch (granularity) {
@@ -440,20 +490,29 @@ bool SelectionController::modify(EAlteration alter, EDirection dir, TextGranular
 
     VisiblePosition pos;
     switch (dir) {
-        // EDIT FIXME: These need to handle bidi
         case RIGHT:
+            if (alter == MOVE)
+                pos = modifyMovingRight(granularity);
+            else
+                pos = modifyExtendingRightForward(granularity);
+            break;
         case FORWARD:
             if (alter == EXTEND)
                 pos = modifyExtendingRightForward(granularity);
             else
-                pos = modifyMovingRightForward(granularity);
+                pos = modifyMovingForward(granularity);
             break;
         case LEFT:
+            if (alter == MOVE)
+                pos = modifyMovingLeft(granularity);
+            else
+                pos = modifyExtendingLeftBackward(granularity);
+            break;
         case BACKWARD:
             if (alter == EXTEND)
                 pos = modifyExtendingLeftBackward(granularity);
             else
-                pos = modifyMovingLeftBackward(granularity);
+                pos = modifyMovingBackward(granularity);
             break;
     }
 
index 5e2f5ed..8683edc 100644 (file)
@@ -125,9 +125,11 @@ private:
     enum EPositionType { START, END, BASE, EXTENT };
 
     VisiblePosition modifyExtendingRightForward(TextGranularity);
-    VisiblePosition modifyMovingRightForward(TextGranularity);
+    VisiblePosition modifyMovingRight(TextGranularity);
+    VisiblePosition modifyMovingForward(TextGranularity);
     VisiblePosition modifyExtendingLeftBackward(TextGranularity);
-    VisiblePosition modifyMovingLeftBackward(TextGranularity);
+    VisiblePosition modifyMovingLeft(TextGranularity);
+    VisiblePosition modifyMovingBackward(TextGranularity);
 
     void layout();
     IntRect caretRepaintRect() const;
index b2481c9..cd7c8eb 100644 (file)
@@ -102,6 +102,285 @@ VisiblePosition VisiblePosition::previous(bool stayInEditableContent) const
     return honorEditableBoundaryAtOrBefore(prev);
 }
 
+Position VisiblePosition::leftVisuallyDistinctCandidate() const
+{
+    Position p = m_deepPosition;
+    Position downstreamStart = p.downstream();
+
+    TextDirection primaryDirection = LTR;
+    for (RenderObject* r = p.node()->renderer(); r; r = r->parent()) {
+        if (r->isBlockFlow()) {
+            primaryDirection = r->style()->direction();
+            break;
+        }
+    }
+
+    while (true) {
+        InlineBox* box;
+        int offset;
+        p.getInlineBoxAndOffset(m_affinity, primaryDirection, box, offset);
+        if (!box)
+            return primaryDirection == LTR ? previousVisuallyDistinctCandidate(p) : nextVisuallyDistinctCandidate(p);
+
+        RenderObject* renderer = box->object();
+
+        while (true) {
+            if ((renderer->isReplaced() || renderer->isBR()) && offset == box->caretRightmostOffset())
+                return box->direction() == LTR ? previousVisuallyDistinctCandidate(p) : nextVisuallyDistinctCandidate(p);
+
+            offset = box->direction() == LTR ? renderer->previousOffset(offset) : renderer->nextOffset(offset);
+
+            int caretMinOffset = box->caretMinOffset();
+            int caretMaxOffset = box->caretMaxOffset();
+
+            if (offset > caretMinOffset && offset < caretMaxOffset)
+                break;
+
+            if (box->direction() == LTR ? offset < caretMinOffset : offset > caretMaxOffset) {
+                // Overshot to the left.
+                InlineBox* prevBox = box->prevLeafChild();
+                if (!prevBox)
+                    return primaryDirection == LTR ? previousVisuallyDistinctCandidate(p) : nextVisuallyDistinctCandidate(p);;
+
+                // Reposition at the other logical position corresponding to our edge's visual position and go for another round.
+                box = prevBox;
+                renderer = box->object();
+                offset = prevBox->caretRightmostOffset();
+                continue;
+            }
+
+            ASSERT(offset == box->caretLeftmostOffset());
+
+            unsigned char level = box->bidiLevel();
+            InlineBox* prevBox = box->prevLeafChild();
+
+            if (box->direction() == primaryDirection) {
+                if (!prevBox || prevBox->bidiLevel() >= level)
+                    break;
+
+                level = prevBox->bidiLevel();
+
+                InlineBox* nextBox = box;
+                do {
+                    nextBox = nextBox->nextLeafChild();
+                } while (nextBox && nextBox->bidiLevel() > level);
+
+                if (nextBox && nextBox->bidiLevel() == level)
+                    break;
+
+                while (InlineBox* prevBox = box->prevLeafChild()) {
+                    if (prevBox->bidiLevel() < level)
+                        break;
+                    box = prevBox;
+                }
+                renderer = box->object();
+                offset = box->caretRightmostOffset();
+                if (box->direction() == primaryDirection)
+                    break;
+                continue;
+            }
+
+            if (prevBox) {
+                box = prevBox;
+                renderer = box->object();
+                offset = box->caretRightmostOffset();
+                if (box->bidiLevel() > level) {
+                    do {
+                        prevBox = box->prevLeafChild();
+                    } while (prevBox && prevBox->bidiLevel() > level);
+
+                    if (!prevBox || prevBox->bidiLevel() < level)
+                        continue;
+                }
+            } else {
+                // Trailing edge of a secondary run. Set to the leading edge of the entire run.
+                while (true) {
+                    while (InlineBox* nextBox = box->nextLeafChild()) {
+                        if (nextBox->bidiLevel() < level)
+                            break;
+                        box = nextBox;
+                    }
+                    if (box->bidiLevel() == level)
+                        break;
+                    level = box->bidiLevel();
+                    while (InlineBox* prevBox = box->prevLeafChild()) {
+                        if (prevBox->bidiLevel() < level)
+                            break;
+                        box = prevBox;
+                    }
+                    if (box->bidiLevel() == level)
+                        break;
+                    level = box->bidiLevel();
+                }
+                renderer = box->object();
+                offset = primaryDirection == LTR ? box->caretMinOffset() : box->caretMaxOffset();
+            }
+            break;
+        }
+
+        p = Position(renderer->element(), offset);
+
+        if (p.isCandidate() && p.downstream() != downstreamStart || p.atStart() || p.atEnd())
+            return p;
+    }
+}
+
+VisiblePosition VisiblePosition::left(bool stayInEditableContent) const
+{
+    Position pos = leftVisuallyDistinctCandidate();
+    if (pos.atStart() || pos.atEnd())
+        return VisiblePosition();
+
+    VisiblePosition left = VisiblePosition(pos, DOWNSTREAM);
+    ASSERT(left != *this);
+
+    if (!stayInEditableContent)
+        return left;
+
+    // FIXME: This may need to do something different from "before".
+    return honorEditableBoundaryAtOrBefore(left);
+}
+
+Position VisiblePosition::rightVisuallyDistinctCandidate() const
+{
+    Position p = m_deepPosition;
+    Position downstreamStart = p.downstream();
+
+    TextDirection primaryDirection = LTR;
+    for (RenderObject* r = p.node()->renderer(); r; r = r->parent()) {
+        if (r->isBlockFlow()) {
+            primaryDirection = r->style()->direction();
+            break;
+        }
+    }
+
+    while (true) {
+        InlineBox* box;
+        int offset;
+        p.getInlineBoxAndOffset(m_affinity, primaryDirection, box, offset);
+        if (!box)
+            return primaryDirection == LTR ? nextVisuallyDistinctCandidate(p) : previousVisuallyDistinctCandidate(p);
+
+        RenderObject* renderer = box->object();
+
+        while (true) {
+            if ((renderer->isReplaced() || renderer->isBR()) && offset == box->caretLeftmostOffset())
+                return box->direction() == LTR ? nextVisuallyDistinctCandidate(p) : previousVisuallyDistinctCandidate(p);
+
+            offset = box->direction() == LTR ? renderer->nextOffset(offset) : renderer->previousOffset(offset);
+
+            int caretMinOffset = box->caretMinOffset();
+            int caretMaxOffset = box->caretMaxOffset();
+
+            if (offset > caretMinOffset && offset < caretMaxOffset)
+                break;
+
+            if (box->direction() == LTR ? offset > caretMaxOffset : offset < caretMinOffset) {
+                // Overshot to the right.
+                InlineBox* nextBox = box->nextLeafChild();
+                if (!nextBox)
+                    return primaryDirection == LTR ? nextVisuallyDistinctCandidate(p) : previousVisuallyDistinctCandidate(p);;
+
+                // Reposition at the other logical position corresponding to our edge's visual position and go for another round.
+                box = nextBox;
+                renderer = box->object();
+                offset = nextBox->caretLeftmostOffset();
+                continue;
+            }
+
+            ASSERT(offset == box->caretRightmostOffset());
+
+            unsigned char level = box->bidiLevel();
+            InlineBox* nextBox = box->nextLeafChild();
+
+            if (box->direction() == primaryDirection) {
+                if (!nextBox || nextBox->bidiLevel() >= level)
+                    break;
+
+                level = nextBox->bidiLevel();
+
+                InlineBox* prevBox = box;
+                do {
+                    prevBox = prevBox->prevLeafChild();
+                } while (prevBox && prevBox->bidiLevel() > level);
+
+                if (prevBox && prevBox->bidiLevel() == level)   // For example, abc FED 123 ^ CBA
+                    break;
+
+                // For example, abc 123 ^ CBA
+                while (InlineBox* nextBox = box->nextLeafChild()) {
+                    if (nextBox->bidiLevel() < level)
+                        break;
+                    box = nextBox;
+                }
+                renderer = box->object();
+                offset = box->caretLeftmostOffset();
+                if (box->direction() == primaryDirection)
+                    break;
+                continue;
+            }
+
+            if (nextBox) {
+                box = nextBox;
+                renderer = box->object();
+                offset = box->caretLeftmostOffset();
+                if (box->bidiLevel() > level) {
+                    do {
+                        nextBox = box->nextLeafChild();
+                    } while (nextBox && nextBox->bidiLevel() > level);
+
+                    if (!nextBox || nextBox->bidiLevel() < level)
+                        continue;
+                }
+            } else {
+                // Trailing edge of a secondary run. Set to the leading edge of the entire run.
+                while (true) {
+                    while (InlineBox* prevBox = box->prevLeafChild()) {
+                        if (prevBox->bidiLevel() < level)
+                            break;
+                        box = prevBox;
+                    }
+                    if (box->bidiLevel() == level)
+                        break;
+                    level = box->bidiLevel();
+                    while (InlineBox* nextBox = box->nextLeafChild()) {
+                        if (nextBox->bidiLevel() < level)
+                            break;
+                        box = nextBox;
+                    }
+                    if (box->bidiLevel() == level)
+                        break;
+                    level = box->bidiLevel();
+                }
+                renderer = box->object();
+                offset = primaryDirection == LTR ? box->caretMaxOffset() : box->caretMinOffset();
+            }
+            break;
+        }
+
+        p = Position(renderer->element(), offset);
+
+        if (p.isCandidate() && p.downstream() != downstreamStart || p.atStart() || p.atEnd())
+            return p;
+    }
+}
+
+VisiblePosition VisiblePosition::right(bool stayInEditableContent) const
+{
+    Position pos = rightVisuallyDistinctCandidate();
+    if (pos.atStart() || pos.atEnd())
+        return VisiblePosition();
+
+    VisiblePosition right = VisiblePosition(pos, DOWNSTREAM);
+    ASSERT(right != *this);
+
+    if (!stayInEditableContent)
+        return right;
+
+    // FIXME: This may need to do something different from "after".
+    return honorEditableBoundaryAtOrAfter(right);
+}
+
 VisiblePosition VisiblePosition::honorEditableBoundaryAtOrBefore(const VisiblePosition &pos) const
 {
     if (pos.isNull())
index 64b9d44..79f3a57 100644 (file)
@@ -70,6 +70,9 @@ public:
     VisiblePosition honorEditableBoundaryAtOrBefore(const VisiblePosition&) const;
     VisiblePosition honorEditableBoundaryAtOrAfter(const VisiblePosition&) const;
 
+    VisiblePosition left(bool stayInEditableContent = false) const;
+    VisiblePosition right(bool stayInEditableContent = false) const;
+
     UChar characterAfter() const;
     UChar characterBefore() const { return previous().characterAfter(); }
     
@@ -98,6 +101,9 @@ private:
     void init(const Position&, EAffinity);
     Position canonicalPosition(const Position&);
 
+    Position leftVisuallyDistinctCandidate() const;
+    Position rightVisuallyDistinctCandidate() const;
+
     Position m_deepPosition;
     EAffinity m_affinity;
 };