2011-03-30 Xiaomei Ji <xji@chromium.org>
authorxji@chromium.org <xji@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 31 Mar 2011 16:17:25 +0000 (16:17 +0000)
committerxji@chromium.org <xji@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 31 Mar 2011 16:17:25 +0000 (16:17 +0000)
        Reviewed by Ryosuke Niwa.

        Experiment with moving caret by word in visual order.
        https://bugs.webkit.org/show_bug.cgi?id=57336

        * editing/selection/move-by-word-visually-expected.txt: Added.
        * editing/selection/move-by-word-visually.html: Added.
2011-03-30  Xiaomei Ji  <xji@chromium.org>

        Reviewed by Ryosuke Niwa.

        Experiment with moving caret by word in visual order.
        https://bugs.webkit.org/show_bug.cgi?id=57336

        Follow Firefox's convention in Windows,
        In LTR block, word break visually moves cursor to the left boundary of words,
        In RTL block, word break visually moves cursor to the right boundary of words.

        This is the 1st version of implementing "move caret by word in visual order".
        It only works in the following situation:
        1. For a LTR box in a LTR block or a RTL box in RTL block,
        when caret is at the left boundary of the box and we are looking for
        the word boundary in right.
        2. For a LTR or RTL box in a LTR block, when caret is at the left boundary
        of the box and we are looking for the word boundary in left and
        previous box is a LTR box.
        3. For a LTR or RTL box in a RTL block, when the caret is at the right
        boundary of the box and we are looking for the word boundary in right and next box is RTL box.

        An experimental granularity is introduced, as a side effect, functions having switch statements
        to handle those granularities have to add more one case to handle this new granularity.
        The experimental granularity is exposed though JS by '-webkit-visual-word".

        The overall algorithm is looping through inline boxes visually and looking
        for the visually nearest word break position.

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

        * editing/SelectionController.cpp:
        (WebCore::SelectionController::modifyExtendingRight):
        (WebCore::SelectionController::modifyExtendingForward):
        (WebCore::SelectionController::modifyMovingRight):
        (WebCore::SelectionController::modifyMovingForward):
        (WebCore::SelectionController::modifyExtendingLeft):
        (WebCore::SelectionController::modifyExtendingBackward):
        (WebCore::SelectionController::modifyMovingLeft):
        (WebCore::SelectionController::modifyMovingBackward):
        * editing/TextGranularity.h:
        * editing/VisibleSelection.cpp:
        (WebCore::VisibleSelection::setStartAndEndFromBaseAndExtentRespectingGranularity):
        * editing/visible_units.cpp:
        (WebCore::previousWordBreakInBoxInsideBlockWithSameDirectionality):
        (WebCore::wordBoundaryInBox):
        (WebCore::wordBoundaryInAdjacentBoxes):
        (WebCore::leftWordBoundary):
        (WebCore::rightWordBoundary):
        (WebCore::leftWordPosition):
        (WebCore::rightWordPosition):
        * editing/visible_units.h:
        * page/DOMSelection.cpp:
        (WebCore::DOMSelection::modify):

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

LayoutTests/ChangeLog
LayoutTests/editing/selection/move-by-word-visually-expected.txt [new file with mode: 0644]
LayoutTests/editing/selection/move-by-word-visually.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/editing/SelectionController.cpp
Source/WebCore/editing/TextGranularity.h
Source/WebCore/editing/VisibleSelection.cpp
Source/WebCore/editing/visible_units.cpp
Source/WebCore/editing/visible_units.h
Source/WebCore/page/DOMSelection.cpp

index 8afe31fd4f94208c04738a196754004fd2ca3ba1..4ce0d0b3dee3e33fa733f9074d8887fb13f30229 100644 (file)
@@ -1,3 +1,13 @@
+2011-03-30  Xiaomei Ji  <xji@chromium.org>
+
+        Reviewed by Ryosuke Niwa.
+
+        Experiment with moving caret by word in visual order.
+        https://bugs.webkit.org/show_bug.cgi?id=57336
+
+        * editing/selection/move-by-word-visually-expected.txt: Added.
+        * editing/selection/move-by-word-visually.html: Added.
+
 2011-03-31  Pavel Podivilov  <podivilov@chromium.org>
 
         Unreviewed, update chromium test expectations.
diff --git a/LayoutTests/editing/selection/move-by-word-visually-expected.txt b/LayoutTests/editing/selection/move-by-word-visually-expected.txt
new file mode 100644 (file)
index 0000000..b36ac28
--- /dev/null
@@ -0,0 +1,43 @@
+
+======== Move By Word ====
+Test 1, LTR:
+Move right by one word
+"abc def hij opq rst"[0, 4, 8, 12, 16, 19]    FAIL expected: [4, 8, 12, 16, 19, 19]
+Move left by one word
+"abc def hij opq rst"[16, 16, 12, 8, 4, 0]    FAIL expected: [16, 12, 8, 4, 0, 0]
+Test 2, RTL:
+Move left by one word
+"abc def hij opq rst"[0, 15, 11, 7, 3, 19]    FAIL expected: [15, 11, 7, 3, 19, 19]
+Move right by one word
+"abc def hij opq rst"[19, 3, 7, 11, 15, 0]    FAIL expected: [3, 7, 11, 15, 0, 0]
+Test 3, LTR:
+Move right by one word
+"ZZZ QQQ BBB CCC XXX"[0, 15, 11, 7, 3, 19]    FAIL expected: [15, 11, 7, 3, 19, 19]
+Move left by one word
+"ZZZ QQQ BBB CCC XXX"[19, 3, 7, 11, 15, 0]    FAIL expected: [3, 7, 11, 15, 0, 0]
+Test 4, RTL:
+Move left by one word
+"ZZZ QQQ BBB CCC XXX"[0, 4, 8, 12, 16, 19]    FAIL expected: [4, 8, 12, 16, 19, 19]
+Move right by one word
+"ZZZ QQQ BBB CCC XXX"[16, 16, 12, 8, 4, 0]    FAIL expected: [16, 12, 8, 4, 0, 0]
+Test 5, LTR:
+Move right by one word
+"abc def ZQB RIG uvw xyz"[0, 4, 8, 11, 16, 20, 23]    FAIL expected: [4, 8, 11, 16, 20, 23, 23]
+Move left by one word
+"abc def ZQB RIG uvw xyz"[20, 20, 16, 11, 4, 4, 0]    FAIL expected: [20, 16, 11, 8, 4, 0, 0]
+Test 6, RTL:
+Move left by one word
+"abc def ZQB RIG uvw xyz"[0, 3, 8, 12, 16, 19, 23]    FAIL expected: [3, 8, 12, 16, 19, 23, 23]
+Move right by one word
+"abc def ZQB RIG uvw xyz"[12, 19, 12, 12, 8, 3, 0]    FAIL expected: [19, 16, 12, 8, 3, 0, 0]
+Test 7, LTR:
+Move right by one word
+"ZQB abc RIG"[0, 4, 8, 11]    FAIL expected: [4, 8, 11, 11]
+Move left by one word
+"ZQB abc RIG"[4, 4, 4, 0]    FAIL expected: [8, 4, 0, 0]
+Test 8, RTL:
+Move left by one word
+"ZQB abc RIG"[0, 4, 8, 11]    FAIL expected: [4, 8, 11, 11]
+Move right by one word
+"ZQB abc RIG"[8, 8, 0, 0]    FAIL expected: [8, 4, 0, 0]
+
diff --git a/LayoutTests/editing/selection/move-by-word-visually.html b/LayoutTests/editing/selection/move-by-word-visually.html
new file mode 100644 (file)
index 0000000..1f64a16
--- /dev/null
@@ -0,0 +1,198 @@
+<!DOCTYPE HTML>
+<html>
+<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; }
+test_move_by_word {display: none;}
+</style>
+<script>
+var messages = [];
+
+function log(message)
+{
+    messages.push(message);
+}
+
+function flushLog()
+{
+    document.getElementById("console").appendChild(document.createTextNode(messages.join("")));
+}
+
+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 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("]");
+}
+
+var wordBreaks;
+
+function validateData(positions)
+{
+    for (var i = 0; i < wordBreaks.length - 1; ++i) {
+        if (positions[i].offset != wordBreaks[i + 1]) {
+            break;
+        }
+    }
+    if (i != wordBreaks.length - 1 && positions[i] != wordBreaks[i]) {
+        log("    FAIL expected: [");
+        for (var i = 1; i < wordBreaks.length; ++i) {
+            log(wordBreaks[i] + ", ");
+        }
+        log(wordBreaks[wordBreaks.length - 1] + "]");
+    }
+}
+
+function collectWordBreaks(test, searchDirection)
+{
+    if (searchDirection == "right") {
+        if (test.getAttribute("dir") == 'ltr') 
+            wordBreaks = test.title.split("|")[0].split(" ");
+        else 
+            wordBreaks = test.title.split("|")[1].split(" ");
+    } else {
+        if (test.getAttribute("dir") == 'ltr') 
+            wordBreaks = test.title.split("|")[1].split(" ");
+        else 
+            wordBreaks = test.title.split("|")[0].split(" ");
+    }
+}
+
+function moveByWord(sel, test, searchDirection, dir)
+{
+    log("Move " + searchDirection + " by one word\n");
+    var prevOffset = sel.anchorOffset;
+    var node = sel.anchorNode;
+    collectWordBreaks(test, searchDirection);
+    sel.setPosition(node, wordBreaks[0]);
+    var positions = [];
+    for (var index = 1; index < wordBreaks.length; ++index) {
+        sel.modify("move", searchDirection, "-webkit-visual-word");
+        positions.push({ node: sel.anchorNode, offset: sel.anchorOffset, point: caretCoordinates() });
+        sel.setPosition(node, wordBreaks[index]);
+    }
+    sel.modify("move", searchDirection, "-webkit-visual-word");
+    positions.push({ node: sel.anchorNode, offset: sel.anchorOffset, point: caretCoordinates() });
+    logPositions(positions);
+    validateData(positions);
+    log("\n");
+}
+
+function moveByWordForEveryPosition(sel, test, dir)
+{
+    // Check ctrl-right-arrow works for every position.
+    sel.setPosition(test, 0);
+    var direction = "right";
+    if (dir == "rtl")
+        direction = "left";    
+    moveByWord(sel, test, direction, dir);    
+    // Check ctrl-left-arrow works for every position.
+    if (dir == "ltr")
+        direction = "left";
+    else
+        direction = "right";    
+    moveByWord(sel, test, direction, dir);    
+}
+
+function runMoveLeftRight(tests, unit)
+{
+    var sel = getSelection();
+    for (var i = 0; i < tests.length; ++i) {
+        var positionsMovingRight;
+        var positionsMovingLeft;
+
+        if (tests[i].getAttribute("dir") == 'ltr')
+        {
+            log("Test " + (i + 1) + ", LTR:\n");
+            moveByWordForEveryPosition(sel, tests[i], "ltr");
+        } else {
+            log("Test " + (i + 1) + ", RTL:\n");
+            moveByWordForEveryPosition(sel, tests[i], "rtl");
+        }
+
+    }
+    document.getElementById("testMoveByWord").style.display = "none";
+}
+
+function runTest() {
+    log("\n======== Move By Word ====\n");
+    var tests = document.getElementsByClassName("test_move_by_word");
+    runMoveLeftRight(tests, "word");
+}
+
+onload = function() {
+    try {
+        runTest();
+    } finally {
+        flushLog();
+    }
+};
+
+if (window.layoutTestController)
+    layoutTestController.dumpAsText();
+</script>
+</head>
+<body>
+<div id="testMoveByWord">
+<!-- The numbers put in title are starting word boundaries.
+The numbers printed out in the output are ending word boundaries. -->
+<div dir=ltr class="test_move_by_word" title="0 4 8 12 16 19|19 16 12 8 4 0" contenteditable>abc def hij opq rst</div>
+<div dir=rtl class="test_move_by_word" title="0 15 11 7 3 19|19 3 7 11 15 0" contenteditable>abc def hij opq rst</div>
+<div dir=ltr class="test_move_by_word" title="0 15 11 7 3 19|19 3 7 11 15 0" contenteditable>ששש נננ בבב גגג קקק</div>
+<div dir=rtl class="test_move_by_word" title="0 4 8 12 16 19|19 16 12 8 4 0" contenteditable>ששש נננ בבב גגג קקק</div>
+<div dir=ltr class="test_move_by_word" title="0 4 8 11 16 20 23|23 20 16 11 8 4 0" contenteditable>abc def שנב סטז uvw xyz</div>
+<div dir=rtl class="test_move_by_word" title="0 3 8 12 16 19 23|23 19 16 12 8 3 0" contenteditable>abc def שנב סטז uvw xyz</div>
+<div dir=ltr class="test_move_by_word" title="0 4 8 11|11 8 4 0" contenteditable>שנב abc סטז</div>
+<div dir=rtl class="test_move_by_word" title="0 4 8 11|11 8 4 0" contenteditable>שנב abc סטז</div>
+</div>
+
+<pre id="console"></pre>
+</body>
+</html>
index 21c0e72719e04e69691dda4b67b38158b6691cc7..2161adf7d30115c5124709a4eed58125fdd463e6 100644 (file)
@@ -1,3 +1,58 @@
+2011-03-30  Xiaomei Ji  <xji@chromium.org>
+
+        Reviewed by Ryosuke Niwa.
+
+        Experiment with moving caret by word in visual order.
+        https://bugs.webkit.org/show_bug.cgi?id=57336
+
+        Follow Firefox's convention in Windows, 
+        In LTR block, word break visually moves cursor to the left boundary of words,
+        In RTL block, word break visually moves cursor to the right boundary of words.
+
+        This is the 1st version of implementing "move caret by word in visual order".
+        It only works in the following situation:
+        1. For a LTR box in a LTR block or a RTL box in RTL block, 
+        when caret is at the left boundary of the box and we are looking for 
+        the word boundary in right.
+        2. For a LTR or RTL box in a LTR block, when caret is at the left boundary
+        of the box and we are looking for the word boundary in left and 
+        previous box is a LTR box.
+        3. For a LTR or RTL box in a RTL block, when the caret is at the right 
+        boundary of the box and we are looking for the word boundary in right and next box is RTL box.
+
+        An experimental granularity is introduced, as a side effect, functions having switch statements
+        to handle those granularities have to add more one case to handle this new granularity.
+        The experimental granularity is exposed though JS by '-webkit-visual-word".
+
+        The overall algorithm is looping through inline boxes visually and looking
+        for the visually nearest word break position. 
+
+        Test: editing/selection/move-by-word-visually.html
+
+        * editing/SelectionController.cpp:
+        (WebCore::SelectionController::modifyExtendingRight):
+        (WebCore::SelectionController::modifyExtendingForward):
+        (WebCore::SelectionController::modifyMovingRight):
+        (WebCore::SelectionController::modifyMovingForward):
+        (WebCore::SelectionController::modifyExtendingLeft):
+        (WebCore::SelectionController::modifyExtendingBackward):
+        (WebCore::SelectionController::modifyMovingLeft):
+        (WebCore::SelectionController::modifyMovingBackward):
+        * editing/TextGranularity.h:
+        * editing/VisibleSelection.cpp:
+        (WebCore::VisibleSelection::setStartAndEndFromBaseAndExtentRespectingGranularity):
+        * editing/visible_units.cpp:
+        (WebCore::previousWordBreakInBoxInsideBlockWithSameDirectionality):
+        (WebCore::wordBoundaryInBox):
+        (WebCore::wordBoundaryInAdjacentBoxes):
+        (WebCore::leftWordBoundary):
+        (WebCore::rightWordBoundary):
+        (WebCore::leftWordPosition):
+        (WebCore::rightWordPosition):
+        * editing/visible_units.h:
+        * page/DOMSelection.cpp:
+        (WebCore::DOMSelection::modify):
+
 2011-03-31  Dimitri Glazkov  <dglazkov@chromium.org>
 
         Reviewed by Darin Adler.
index 4ff490bca3ae0f363cdb1e5a2cc36adbbccd55c6..ae980e0af3e21e0f798c9183644ca070475ff555 100644 (file)
@@ -447,6 +447,9 @@ VisiblePosition SelectionController::modifyExtendingRight(TextGranularity granul
     case DocumentBoundary:
         // FIXME: implement all of the above?
         pos = modifyExtendingForward(granularity);
+        break;
+    case WebKitVisualWordGranularity:
+        break;
     }
     return pos;
 }
@@ -486,6 +489,8 @@ VisiblePosition SelectionController::modifyExtendingForward(TextGranularity gran
         else
             pos = endOfDocument(pos);
         break;
+    case WebKitVisualWordGranularity:
+        break;
     }
     
     return pos;
@@ -517,6 +522,9 @@ VisiblePosition SelectionController::modifyMovingRight(TextGranularity granulari
     case LineBoundary:
         pos = rightBoundaryOfLine(startForPlatform(), directionOfEnclosingBlock());
         break;
+    case WebKitVisualWordGranularity:
+        pos = rightWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
+        break;
     }
     return pos;
 }
@@ -565,6 +573,8 @@ VisiblePosition SelectionController::modifyMovingForward(TextGranularity granula
         else
             pos = endOfDocument(pos);
         break;
+    case WebKitVisualWordGranularity:
+        break;
     }
     return pos;
 }
@@ -604,6 +614,9 @@ VisiblePosition SelectionController::modifyExtendingLeft(TextGranularity granula
     case ParagraphBoundary:
     case DocumentBoundary:
         pos = modifyExtendingBackward(granularity);
+        break;
+    case WebKitVisualWordGranularity:
+        break;
     }
     return pos;
 }
@@ -648,6 +661,8 @@ VisiblePosition SelectionController::modifyExtendingBackward(TextGranularity gra
         else
             pos = startOfDocument(pos);
         break;
+    case WebKitVisualWordGranularity:
+        break;
     }
     return pos;
 }
@@ -666,6 +681,8 @@ VisiblePosition SelectionController::modifyMovingLeft(TextGranularity granularit
             pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).left(true);
         break;
     case WordGranularity:
+        pos = leftWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
+        break;
     case SentenceGranularity:
     case LineGranularity:
     case ParagraphGranularity:
@@ -678,6 +695,9 @@ VisiblePosition SelectionController::modifyMovingLeft(TextGranularity granularit
     case LineBoundary:
         pos = leftBoundaryOfLine(startForPlatform(), directionOfEnclosingBlock());
         break;
+    case WebKitVisualWordGranularity:
+        pos = leftWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
+        break;
     }
     return pos;
 }
@@ -720,6 +740,8 @@ VisiblePosition SelectionController::modifyMovingBackward(TextGranularity granul
         else
             pos = startOfDocument(pos);
         break;
+    case WebKitVisualWordGranularity:
+        break;
     }
     return pos;
 }
index 09cc4edd756eb5446a40717124e0e0837bb76360..965d7a0d175f2680ba1c8ec52a9fc29bcd18b01f 100644 (file)
@@ -39,7 +39,10 @@ enum TextGranularity {
     SentenceBoundary,
     LineBoundary,
     ParagraphBoundary,
-    DocumentBoundary
+    DocumentBoundary,
+    // FIXME: this is added temporarily for experiment with visually move
+    // caret by wordGranularity. Once all patches are landed, it should be removed.
+    WebKitVisualWordGranularity
 };
 
 }
index 5633c900ccfb2f1f09a895b231d43e4df078e02b..9e9692e3f292fe16af1e999cd85b3ffa2543ef8c 100644 (file)
@@ -380,6 +380,8 @@ void VisibleSelection::setStartAndEndFromBaseAndExtentRespectingGranularity(Text
             m_start = startOfSentence(VisiblePosition(m_start, m_affinity)).deepEquivalent();
             m_end = endOfSentence(VisiblePosition(m_end, m_affinity)).deepEquivalent();
             break;
+        case WebKitVisualWordGranularity:
+            break;
     }
     
     // Make sure we do not have a dangling start or end.
index c9ca8c0b3792347def3f68005452bdc4759b58e4..fafcdcfb63fa144deb02f7423d2b400d32a9e142 100644 (file)
@@ -37,6 +37,7 @@
 #include "TextBreakIterator.h"
 #include "TextIterator.h"
 #include "VisiblePosition.h"
+#include "VisibleSelection.h"
 #include "htmlediting.h"
 #include <wtf/unicode/Unicode.h>
 
@@ -1146,4 +1147,108 @@ VisiblePosition rightBoundaryOfLine(const VisiblePosition& c, TextDirection dire
     return direction == LTR ? logicalEndOfLine(c) : logicalStartOfLine(c);
 }
 
+static const int invalidOffset = -1;
+    
+static VisiblePosition previousWordBreakInBoxInsideBlockWithSameDirectionality(const InlineBox* box, const VisiblePosition& previousWordBreak, int& offsetOfWordBreak)
+{
+    bool hasSeenWordBreakInThisBox = previousWordBreak.isNotNull();
+    // In a LTR block, the word break should be on the left boundary of a word.
+    // In a RTL block, the word break should be on the right boundary of a word.
+    // Because nextWordPosition() returns the word break on the right boundary of the word for LTR text,
+    // we need to use previousWordPosition() to traverse words within the inline boxes from right to left
+    // to find the previous word break (i.e. the first word break on the left). The same applies to RTL text.
+    
+    VisiblePosition wordBreak = hasSeenWordBreakInThisBox ? previousWordBreak : Position(box->renderer()->node(), box->caretMaxOffset(), Position::PositionIsOffsetInAnchor);
+
+    // FIXME: handle multi-spaces (http://webkit.org/b/57543).
+    
+    wordBreak = previousWordPosition(wordBreak);
+    if (previousWordBreak == wordBreak)
+        return VisiblePosition();
+
+    InlineBox* boxContainingPreviousWordBreak;
+    wordBreak.getInlineBoxAndOffset(boxContainingPreviousWordBreak, offsetOfWordBreak);
+    if (boxContainingPreviousWordBreak != box)
+        return VisiblePosition();
+    return wordBreak;
+}
+
+static VisiblePosition previousWordBreakInBox(const InlineBox* box, int offset, TextDirection blockDirection)
+{
+    int offsetOfWordBreak = 0;
+    VisiblePosition wordBreak;
+    while (true) {
+        if (box->direction() == blockDirection)
+            wordBreak = previousWordBreakInBoxInsideBlockWithSameDirectionality(box, wordBreak, offsetOfWordBreak);
+        // FIXME: Implement the 'else' case when the box direction is not equal to the block direction.
+        if (wordBreak.isNull())
+            break;
+        if (offset == invalidOffset || offsetOfWordBreak != offset)
+            return wordBreak;
+    }        
+    return VisiblePosition();
+}
+
+static VisiblePosition leftWordBoundary(const InlineBox* box, int offset, TextDirection blockDirection)
+{
+    VisiblePosition wordBreak;
+    for  (const InlineBox* adjacentBox = box; adjacentBox; adjacentBox = adjacentBox->prevLeafChild()) {
+        if (blockDirection == LTR) 
+            wordBreak = previousWordBreakInBox(adjacentBox, adjacentBox == box ? offset : invalidOffset, blockDirection);
+        // FIXME: Implement the "else" case.
+        if (wordBreak.isNotNull())
+            return wordBreak;
+    }
+    return VisiblePosition();
+}
+static VisiblePosition rightWordBoundary(const InlineBox* box, int offset, TextDirection blockDirection)
+{
+    
+    VisiblePosition wordBreak;
+    for (const InlineBox* adjacentBox = box; adjacentBox; adjacentBox = adjacentBox->nextLeafChild()) {
+        if (blockDirection == RTL)
+            wordBreak = previousWordBreakInBox(adjacentBox, adjacentBox == box ? offset : invalidOffset, blockDirection);
+        // FIXME: Implement the "else" case.
+        if (!wordBreak.isNull())
+            return wordBreak;
+    }
+    return VisiblePosition();
+}
+
+VisiblePosition leftWordPosition(const VisiblePosition& visiblePosition)
+{
+    InlineBox* box;
+    int offset;
+    visiblePosition.getInlineBoxAndOffset(box, offset);
+    TextDirection blockDirection = directionOfEnclosingBlock(visiblePosition.deepEquivalent());
+    
+    // FIXME: If the box's directionality is the same as that of the enclosing block, when the offset is at the box boundary
+    // 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,
+    // when offset is at box's caretMinOffset and the direction is DirectionRight, should it be taken care as a general case?
+    if (offset == box->caretLeftmostOffset())
+        return leftWordBoundary(box->prevLeafChild(), invalidOffset, blockDirection);
+    if (offset == box->caretRightmostOffset())
+        return leftWordBoundary(box, offset, blockDirection);
+    
+    // FIXME: Not implemented.
+    return VisiblePosition();
+}
+
+VisiblePosition rightWordPosition(const VisiblePosition& visiblePosition)
+{
+    InlineBox* box;
+    int offset;
+    visiblePosition.getInlineBoxAndOffset(box, offset);
+    TextDirection blockDirection = directionOfEnclosingBlock(visiblePosition.deepEquivalent());
+    
+    if (offset == box->caretLeftmostOffset())
+        return rightWordBoundary(box, offset, blockDirection);
+    if (offset == box->caretRightmostOffset())
+        return rightWordBoundary(box->nextLeafChild(), -1, blockDirection);
+    
+    // FIXME: Not implemented.
+    return VisiblePosition();
+}
+
 }
index 18b9665ec66205742b5c69de52797d4acb0b2120..d734f78033e3723e71aa4ecf27fc737f16b1299d 100644 (file)
@@ -42,6 +42,8 @@ VisiblePosition startOfWord(const VisiblePosition &, EWordSide = RightWordIfOnBo
 VisiblePosition endOfWord(const VisiblePosition &, EWordSide = RightWordIfOnBoundary);
 VisiblePosition previousWordPosition(const VisiblePosition &);
 VisiblePosition nextWordPosition(const VisiblePosition &);
+VisiblePosition rightWordPosition(const VisiblePosition&);
+VisiblePosition leftWordPosition(const VisiblePosition&);
 
 // sentences
 VisiblePosition startOfSentence(const VisiblePosition &);
index 31ab956efd29cf6408b3fa49a120b0fcf82cf82b..da3b65429896e905c7badb639f8dea397d73b895 100644 (file)
@@ -332,6 +332,8 @@ void DOMSelection::modify(const String& alterString, const String& directionStri
         granularity = ParagraphBoundary;
     else if (equalIgnoringCase(granularityString, "documentboundary"))
         granularity = DocumentBoundary;
+    else if (equalIgnoringCase(granularityString, "-webkit-visual-word"))
+        granularity = WebKitVisualWordGranularity;
     else
         return;