Changeset 95964 in webkit


Ignore:
Timestamp:
Sep 26, 2011 11:30:29 AM (13 years ago)
Author:
rniwa@webkit.org
Message:

REGRESSION(r74971): Selection doesn't work correctly in BiDi Text
https://bugs.webkit.org/show_bug.cgi?id=57340

Reviewed by Eric Seidel.

Source/WebCore:

This patch adds the end point adjustment mechanism at bidi boundaries similar to the one NSTextView implements.

To understand the problem, suppose we have strong RTL letters "ABC" in a LTR block (visually laid out as CBA).

Per NSTextView convention, logical offsets between each letter is placed as (0)C(2)B(1)A(3). In other words,
placing the caret visually on the left of CBA yields the position inside the text node of "ABC" at offset 0.
Likewise, placing it between C and B yields ("ABC", 2), and placing it on the right of CBA yields ("ABC", 3).

Now suppose a user attempts to select the letter A by a mouse drag from the right of CBA to a point between
B and A. First, the initial mouse down places the selection's base at ("ABC", 3). Then as the mouse pointer
moves to a point on the left of A, the selection's extent is set at ("ABC", 1), selecting "BC".

To mitigate this issue, NSTextView adjusts selection base and extent under certain conditions. In the above
example, NSTextView detects user's intent and changes the selection's base to ("ABC", 0) temporarily.

This patch implements a similar trick on WebKit. We adjust the base or the extent when they're at the left
end or at the right end of a bidi run and the other end is inside of the run. In the above example, the
base position on the right of A is the right end of a bidi run and the extent position between B and A is
inside the same run (CBA), so we would adjust the base to be ("ABC", 0) as NSTextView does.

Take another example abcABC. Note offsets are assigned as (0)a(1)b(2)c(3)C(5)B(4)A(6) When the user starts
a mouse drag from the right of A to a point between B and A, we adjust the selection base to be ("abcABC", 3)
because the base is at the right end of a bidi run and the extent is in the same run. We keep the adjustment
when the mouse pointer moves to a point between C and B. However, when the mouser pointer reaches a point
between letters b and c, the selection extent is placed at ("abcABC", 2). Because the extent is outside of
the bidi run started from the selection base, we restore the original base at this point. Had we not done this,
we'll end up selecting just "c".

While this algorithm is implemented in FrameSelection::setNonDirectionalSelectionIfNeeded, this patch adds
various member functions to RenderedPosition to facilitate abstraction around inline boxes and bidi runs.

Test: editing/selection/select-bidi-run.html

  • editing/FrameSelection.cpp:

(WebCore::adjustEndpointsAtBidiBoundary): Added. Implements the endpoints adjustment algorithm.
(WebCore::FrameSelection::setNonDirectionalSelectionIfNeeded): Calls adjustEndpointsAtBidiBoundary, and
restores the original base as needed.

  • editing/FrameSelection.h:
  • editing/RenderedPosition.cpp:

(WebCore::RenderedPosition::RenderedPosition):
(WebCore::RenderedPosition::prevLeafChild): Added to cache prevLeafChild of the current inline box.
(WebCore::RenderedPosition::nextLeafChild): Ditto for nextLeafChild.
(WebCore::RenderedPosition::isEquivalent): Compares two RenderedPositions considering neighboring inline boxes
so that the rightmost position in a box and the leftmost position in the following box is considered equal.
(WebCore::RenderedPosition::bidiLevelOnLeft): Added. Returns the bidi level of the run on the left. We can't
add a generic bidiLevel to this class because it'll be ambiguous at bidi boundaries.
(WebCore::RenderedPosition::bidiLevelOnRight): Ditto for the run on the right.
(WebCore::RenderedPosition::leftBoundaryOfBidiRun): Added.
(WebCore::RenderedPosition::rightBoundaryOfBidiRun): Added.
(WebCore::RenderedPosition::atLeftBoundaryOfBidiRun): Added.
(WebCore::RenderedPosition::atRightBoundaryOfBidiRun): Added.
(WebCore::RenderedPosition::positionAtLeftBoundaryOfBiDiRun): Returns Position at the left edge of a bidi run
if RenderedPosition is at such a position. Asserts atLeftBoundaryOfBidiRun.
(WebCore::RenderedPosition::positionAtRightBoundaryOfBiDiRun): Ditto for the right edge.

  • editing/RenderedPosition.h:

(WebCore::RenderedPosition::atLeftBoundaryOfBidiRun): Added.
(WebCore::RenderedPosition::atRightBoundaryOfBidiRun): Added.
(WebCore::RenderedPosition::atLeftmostOffsetInBox): Added.
(WebCore::RenderedPosition::atRightmostOffsetInBox): Added.
(WebCore::RenderedPosition::uncachedInlineBox): Added. We can't use a static const variable because gcc thinks
reinterpret_cast<InlineBox*>(1) is not an integral value.
(WebCore::RenderedPosition::RenderedPosition):

  • editing/VisibleSelection.h:

(WebCore::VisibleSelection::visibleBase): Added.
(WebCore::VisibleSelection::visibleExtent): Added.

  • page/EventHandler.cpp:

(WebCore::EventHandler::updateSelectionForMouseDrag):

LayoutTests:

  • editing/selection/select-bidi-run-expected.txt: Added.
  • editing/selection/select-bidi-run.html: Added.
Location:
trunk
Files:
2 added
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r95963 r95964  
     12011-09-12  Ryosuke Niwa  <rniwa@webkit.org>
     2
     3        REGRESSION(r74971): Selection doesn't work correctly in BiDi Text
     4        https://bugs.webkit.org/show_bug.cgi?id=57340
     5
     6        Reviewed by Eric Seidel.
     7
     8        * editing/selection/select-bidi-run-expected.txt: Added.
     9        * editing/selection/select-bidi-run.html: Added.
     10
    1112011-09-26  Csaba Osztrogonác  <ossy@webkit.org>
    212
  • trunk/Source/WebCore/ChangeLog

    r95962 r95964  
     12011-09-12  Ryosuke Niwa  <rniwa@webkit.org>
     2
     3        REGRESSION(r74971): Selection doesn't work correctly in BiDi Text
     4        https://bugs.webkit.org/show_bug.cgi?id=57340
     5
     6        Reviewed by Eric Seidel.
     7
     8        This patch adds the end point adjustment mechanism at bidi boundaries similar to the one NSTextView implements.
     9
     10        To understand the problem, suppose we have strong RTL letters "ABC" in a LTR block (visually laid out as CBA).
     11
     12        Per NSTextView convention, logical offsets between each letter is placed as (0)C(2)B(1)A(3). In other words,
     13        placing the caret visually on the left of CBA yields the position inside the text node of "ABC" at offset 0.
     14        Likewise, placing it between C and B yields ("ABC", 2), and placing it on the right of CBA yields ("ABC", 3).
     15
     16        Now suppose a user attempts to select the letter A by a mouse drag from the right of CBA to a point between
     17        B and A. First, the initial mouse down places the selection's base at ("ABC", 3). Then as the mouse pointer
     18        moves to a point on the left of A, the selection's extent is set at ("ABC", 1), selecting "BC".
     19
     20        To mitigate this issue, NSTextView adjusts selection base and extent under certain conditions. In the above
     21        example, NSTextView detects user's intent and changes the selection's base to ("ABC", 0) temporarily.
     22
     23        This patch implements a similar trick on WebKit. We adjust the base or the extent when they're at the left
     24        end or at the right end of a bidi run and the other end is inside of the run. In the above example, the
     25        base position on the right of A is the right end of a bidi run and the extent position between B and A is
     26        inside the same run (CBA), so we would adjust the base to be ("ABC", 0) as NSTextView does.
     27
     28        Take another example abcABC. Note offsets are assigned as (0)a(1)b(2)c(3)C(5)B(4)A(6) When the user starts
     29        a mouse drag from the right of A to a point between B and A, we adjust the selection base to be ("abcABC", 3)
     30        because the base is at the right end of a bidi run and the extent is in the same run. We keep the adjustment
     31        when the mouse pointer moves to a point between C and B. However, when the mouser pointer reaches a point
     32        between letters b and c, the selection extent is placed at ("abcABC", 2). Because the extent is outside of
     33        the bidi run started from the selection base, we restore the original base at this point. Had we not done this,
     34        we'll end up selecting just "c".
     35
     36        While this algorithm is implemented in FrameSelection::setNonDirectionalSelectionIfNeeded, this patch adds
     37        various member functions to RenderedPosition to facilitate abstraction around inline boxes and bidi runs.
     38
     39        Test: editing/selection/select-bidi-run.html
     40
     41        * editing/FrameSelection.cpp:
     42        (WebCore::adjustEndpointsAtBidiBoundary): Added. Implements the endpoints adjustment algorithm.
     43        (WebCore::FrameSelection::setNonDirectionalSelectionIfNeeded): Calls adjustEndpointsAtBidiBoundary, and
     44        restores the original base as needed.
     45        * editing/FrameSelection.h:
     46        * editing/RenderedPosition.cpp:
     47        (WebCore::RenderedPosition::RenderedPosition):
     48        (WebCore::RenderedPosition::prevLeafChild): Added to cache prevLeafChild of the current inline box.
     49        (WebCore::RenderedPosition::nextLeafChild): Ditto for nextLeafChild.
     50        (WebCore::RenderedPosition::isEquivalent): Compares two RenderedPositions considering neighboring inline boxes
     51        so that the rightmost position in a box and the leftmost position in the following box is considered equal.
     52        (WebCore::RenderedPosition::bidiLevelOnLeft): Added. Returns the bidi level of the run on the left. We can't
     53        add a generic bidiLevel to this class because it'll be ambiguous at bidi boundaries.
     54        (WebCore::RenderedPosition::bidiLevelOnRight): Ditto for the run on the right.
     55        (WebCore::RenderedPosition::leftBoundaryOfBidiRun): Added.
     56        (WebCore::RenderedPosition::rightBoundaryOfBidiRun): Added.
     57        (WebCore::RenderedPosition::atLeftBoundaryOfBidiRun): Added.
     58        (WebCore::RenderedPosition::atRightBoundaryOfBidiRun): Added.
     59        (WebCore::RenderedPosition::positionAtLeftBoundaryOfBiDiRun): Returns Position at the left edge of a bidi run
     60        if RenderedPosition is at such a position. Asserts atLeftBoundaryOfBidiRun.
     61        (WebCore::RenderedPosition::positionAtRightBoundaryOfBiDiRun): Ditto for the right edge.
     62        * editing/RenderedPosition.h:
     63        (WebCore::RenderedPosition::atLeftBoundaryOfBidiRun): Added.
     64        (WebCore::RenderedPosition::atRightBoundaryOfBidiRun): Added.
     65        (WebCore::RenderedPosition::atLeftmostOffsetInBox): Added.
     66        (WebCore::RenderedPosition::atRightmostOffsetInBox): Added.
     67        (WebCore::RenderedPosition::uncachedInlineBox): Added. We can't use a static const variable because gcc thinks
     68        reinterpret_cast<InlineBox*>(1) is not an integral value.
     69        (WebCore::RenderedPosition::RenderedPosition):
     70        * editing/VisibleSelection.h:
     71        (WebCore::VisibleSelection::visibleBase): Added.
     72        (WebCore::VisibleSelection::visibleExtent): Added.
     73        * page/EventHandler.cpp:
     74        (WebCore::EventHandler::updateSelectionForMouseDrag):
     75
    1762011-09-26  Sheriff Bot  <webkit.review.bot@gmail.com>
    277
  • trunk/Source/WebCore/editing/FrameSelection.cpp

    r94966 r95964  
    5757#include "RenderView.h"
    5858#include "RenderWidget.h"
     59#include "RenderedPosition.h"
    5960#include "SecureTextInput.h"
    6061#include "Settings.h"
     
    164165}
    165166
    166 void FrameSelection::setNonDirectionalSelectionIfNeeded(const VisibleSelection& passedNewSelection, TextGranularity granularity)
     167static void adjustEndpointsAtBidiBoundary(VisiblePosition& visibleBase, VisiblePosition& visibleExtent)
     168{
     169    RenderedPosition base(visibleBase);
     170    RenderedPosition extent(visibleExtent);
     171
     172    if (base.isNull() || extent.isNull() || base.isEquivalent(extent))
     173        return;
     174
     175    if (base.atLeftBoundaryOfBidiRun()) {
     176        if (!extent.atRightBoundaryOfBidiRun(base.bidiLevelOnRight())
     177            && base.isEquivalent(extent.leftBoundaryOfBidiRun(base.bidiLevelOnRight()))) {
     178            visibleBase = base.positionAtLeftBoundaryOfBiDiRun();
     179            return;
     180        }
     181        return;
     182    }
     183
     184    if (base.atRightBoundaryOfBidiRun()) {
     185        if (!extent.atLeftBoundaryOfBidiRun(base.bidiLevelOnLeft())
     186            && base.isEquivalent(extent.rightBoundaryOfBidiRun(base.bidiLevelOnLeft()))) {
     187            visibleBase = base.positionAtRightBoundaryOfBiDiRun();
     188            return;
     189        }
     190        return;
     191    }
     192
     193    if (extent.atLeftBoundaryOfBidiRun() && extent.isEquivalent(base.leftBoundaryOfBidiRun(extent.bidiLevelOnRight()))) {
     194        visibleExtent = extent.positionAtLeftBoundaryOfBiDiRun();
     195        return;
     196    }
     197
     198    if (extent.atRightBoundaryOfBidiRun() && extent.isEquivalent(base.rightBoundaryOfBidiRun(extent.bidiLevelOnLeft()))) {
     199        visibleExtent = extent.positionAtRightBoundaryOfBiDiRun();
     200        return;
     201    }
     202}
     203
     204void FrameSelection::setNonDirectionalSelectionIfNeeded(const VisibleSelection& passedNewSelection, TextGranularity granularity,
     205    EndPointsAdjustmentMode endpointsAdjustmentMode)
    167206{
    168207    VisibleSelection newSelection = passedNewSelection;
    169 
    170     if (shouldAlwaysUseDirectionalSelection(m_frame))
    171         newSelection.setIsDirectional(true);
    172 
     208    bool isDirectional = shouldAlwaysUseDirectionalSelection(m_frame) || newSelection.isDirectional();
     209
     210    VisiblePosition base = m_originalBase.isNotNull() ? m_originalBase : newSelection.visibleBase();
     211    VisiblePosition newBase = base;
     212    VisiblePosition newExtent = newSelection.visibleExtent();
     213    if (endpointsAdjustmentMode == AdjustEndpointsAtBidiBoundary)
     214        adjustEndpointsAtBidiBoundary(newBase, newExtent);
     215
     216    if (newBase != base || newExtent != newSelection.visibleExtent()) {
     217        m_originalBase = base;
     218        newSelection.setBase(newBase);
     219        newSelection.setExtent(newExtent);
     220    } else if (m_originalBase.isNotNull()) {
     221        if (m_selection.base() == newSelection.base())
     222            newSelection.setBase(m_originalBase);
     223        m_originalBase.clear();
     224    }
     225
     226    newSelection.setIsDirectional(isDirectional); // Adjusting base and extent will make newSelection always directional
    173227    if (m_selection == newSelection || !shouldChangeSelection(newSelection))
    174228        return;
  • trunk/Source/WebCore/editing/FrameSelection.h

    r94966 r95964  
    224224    bool shouldChangeSelection(const VisibleSelection&) const;
    225225    bool shouldDeleteSelection(const VisibleSelection&) const;
    226     void setNonDirectionalSelectionIfNeeded(const VisibleSelection&, TextGranularity);
     226    enum EndPointsAdjustmentMode { AdjustEndpointsAtBidiBoundary, DoNotAdjsutEndpoints };
     227    void setNonDirectionalSelectionIfNeeded(const VisibleSelection&, TextGranularity, EndPointsAdjustmentMode = DoNotAdjsutEndpoints);
    227228    void setFocusedNodeIfNeeded();
    228229    void notifyRendererOfSelectionChange(EUserTriggered);
     
    283284
    284285    VisibleSelection m_selection;
     286    VisiblePosition m_originalBase; // Used to store base before the adjustment at bidi boundary
    285287    TextGranularity m_granularity;
    286288
  • trunk/Source/WebCore/editing/RenderedPosition.cpp

    r95901 r95964  
    3333
    3434#include "InlineBox.h"
     35#include "InlineTextBox.h"
    3536#include "Position.h"
    3637#include "VisiblePosition.h"
     
    6970    , m_inlineBox(0)
    7071    , m_offset(0)
     72    , m_prevLeafChild(uncachedInlineBox())
     73    , m_nextLeafChild(uncachedInlineBox())
    7174{
    7275    if (position.isNull())
     
    8386    , m_inlineBox(0)
    8487    , m_offset(0)
     88    , m_prevLeafChild(uncachedInlineBox())
     89    , m_nextLeafChild(uncachedInlineBox())
    8590{
    8691    if (position.isNull())
     
    9398}
    9499
     100InlineBox* RenderedPosition::prevLeafChild() const
     101{
     102    if (m_prevLeafChild == uncachedInlineBox())
     103        m_prevLeafChild = m_inlineBox->prevLeafChild();
     104    return m_prevLeafChild;
     105}
     106
     107InlineBox* RenderedPosition::nextLeafChild() const
     108{
     109    if (m_nextLeafChild == uncachedInlineBox())
     110        m_nextLeafChild = m_inlineBox->nextLeafChild();
     111    return m_nextLeafChild;
     112}
     113
     114bool RenderedPosition::isEquivalent(const RenderedPosition& other) const
     115{
     116    return (m_renderer == other.m_renderer && m_inlineBox == other.m_inlineBox && m_offset == other.m_offset)
     117        || (atLeftmostOffsetInBox() && other.atRightmostOffsetInBox() && prevLeafChild() == other.m_inlineBox)
     118        || (atRightmostOffsetInBox() && other.atLeftmostOffsetInBox() && nextLeafChild() == other.m_inlineBox);
     119}
     120
     121unsigned char RenderedPosition::bidiLevelOnLeft() const
     122{
     123    InlineBox* box = atLeftmostOffsetInBox() ? prevLeafChild() : m_inlineBox;
     124    return box ? box->bidiLevel() : 0;
     125}
     126
     127unsigned char RenderedPosition::bidiLevelOnRight() const
     128{
     129    InlineBox* box = atRightmostOffsetInBox() ? nextLeafChild() : m_inlineBox;
     130    return box ? box->bidiLevel() : 0;
     131}
     132
     133RenderedPosition RenderedPosition::leftBoundaryOfBidiRun(unsigned char bidiLevelOfRun)
     134{
     135    if (!m_inlineBox || bidiLevelOfRun > m_inlineBox->bidiLevel())
     136        return RenderedPosition();
     137
     138    InlineBox* box = m_inlineBox;
     139    do {
     140        InlineBox* prev = box->prevLeafChild();
     141        if (!prev || prev->bidiLevel() < bidiLevelOfRun)
     142            return RenderedPosition(box->renderer(), box, box->caretLeftmostOffset());
     143        box = prev;
     144    } while (box);
     145
     146    ASSERT_NOT_REACHED();
     147    return RenderedPosition();
     148}
     149
     150RenderedPosition RenderedPosition::rightBoundaryOfBidiRun(unsigned char bidiLevelOfRun)
     151{
     152    if (!m_inlineBox || bidiLevelOfRun > m_inlineBox->bidiLevel())
     153        return RenderedPosition();
     154
     155    InlineBox* box = m_inlineBox;
     156    do {
     157        InlineBox* next = box->nextLeafChild();
     158        if (!next || next->bidiLevel() < bidiLevelOfRun)
     159            return RenderedPosition(box->renderer(), box, box->caretRightmostOffset());
     160        box = next;
     161    } while (box);
     162
     163    ASSERT_NOT_REACHED();
     164    return RenderedPosition();
     165}
     166
     167bool RenderedPosition::atLeftBoundaryOfBidiRun(ShouldMatchBidiLevel shouldMatchBidiLevel, unsigned char bidiLevelOfRun) const
     168{
     169    if (!m_inlineBox)
     170        return false;
     171
     172    if (atLeftmostOffsetInBox()) {
     173        if (shouldMatchBidiLevel == IgnoreBidiLevel)
     174            return !prevLeafChild() || prevLeafChild()->bidiLevel() < m_inlineBox->bidiLevel();
     175        return m_inlineBox->bidiLevel() >= bidiLevelOfRun && (!prevLeafChild() || prevLeafChild()->bidiLevel() < bidiLevelOfRun);
     176    }
     177
     178    if (atRightmostOffsetInBox()) {
     179        if (shouldMatchBidiLevel == IgnoreBidiLevel)
     180            return nextLeafChild() && m_inlineBox->bidiLevel() < nextLeafChild()->bidiLevel();
     181        return nextLeafChild() && m_inlineBox->bidiLevel() < bidiLevelOfRun && nextLeafChild()->bidiLevel() >= bidiLevelOfRun;
     182    }
     183
     184    return false;
     185}
     186
     187bool RenderedPosition::atRightBoundaryOfBidiRun(ShouldMatchBidiLevel shouldMatchBidiLevel, unsigned char bidiLevelOfRun) const
     188{
     189    if (!m_inlineBox)
     190        return false;
     191
     192    if (atRightmostOffsetInBox()) {
     193        if (shouldMatchBidiLevel == IgnoreBidiLevel)
     194            return !nextLeafChild() || nextLeafChild()->bidiLevel() < m_inlineBox->bidiLevel();
     195        return m_inlineBox->bidiLevel() >= bidiLevelOfRun && (!nextLeafChild() || nextLeafChild()->bidiLevel() < bidiLevelOfRun);
     196    }
     197
     198    if (atLeftmostOffsetInBox()) {
     199        if (shouldMatchBidiLevel == IgnoreBidiLevel)
     200            return prevLeafChild() && m_inlineBox->bidiLevel() < prevLeafChild()->bidiLevel();
     201        return prevLeafChild() && m_inlineBox->bidiLevel() < bidiLevelOfRun && prevLeafChild()->bidiLevel() >= bidiLevelOfRun;
     202    }
     203
     204    return false;
     205}
     206
     207Position RenderedPosition::positionAtLeftBoundaryOfBiDiRun() const
     208{
     209    ASSERT(atLeftBoundaryOfBidiRun());
     210
     211    if (atLeftmostOffsetInBox())
     212        return createLegacyEditingPosition(m_renderer->node(), m_offset);
     213
     214    return createLegacyEditingPosition(nextLeafChild()->renderer()->node(), m_offset);
     215}
     216
     217Position RenderedPosition::positionAtRightBoundaryOfBiDiRun() const
     218{
     219    ASSERT(atRightBoundaryOfBidiRun());
     220
     221    if (atRightmostOffsetInBox())
     222        return createLegacyEditingPosition(m_renderer->node(), m_offset);
     223
     224    return createLegacyEditingPosition(prevLeafChild()->renderer()->node(), m_offset);
     225}
     226
    95227LayoutRect RenderedPosition::absoluteRect(int* extraWidthToEndOfLine) const
    96228{
  • trunk/Source/WebCore/editing/RenderedPosition.h

    r95901 r95964  
    4747    explicit RenderedPosition(const VisiblePosition&);
    4848    explicit RenderedPosition(const Position&, EAffinity);
     49    bool isEquivalent(const RenderedPosition&) const;
    4950
    5051    bool isNull() const { return !m_renderer; }
    5152    RootInlineBox* rootBox() { return m_inlineBox ? m_inlineBox->root() : 0; }
     53
     54    unsigned char bidiLevelOnLeft() const;
     55    unsigned char bidiLevelOnRight() const;
     56    RenderedPosition leftBoundaryOfBidiRun(unsigned char bidiLevelOfRun);
     57    RenderedPosition rightBoundaryOfBidiRun(unsigned char bidiLevelOfRun);
     58
     59    enum ShouldMatchBidiLevel { MatchBidiLevel, IgnoreBidiLevel };
     60    bool atLeftBoundaryOfBidiRun() const { return atLeftBoundaryOfBidiRun(IgnoreBidiLevel, 0); }
     61    bool atRightBoundaryOfBidiRun() const { return atRightBoundaryOfBidiRun(IgnoreBidiLevel, 0); }
     62    // The following two functions return true only if the current position is at the end of the bidi run
     63    // of the specified bidi embedding level.
     64    bool atLeftBoundaryOfBidiRun(unsigned char bidiLevelOfRun) const { return atLeftBoundaryOfBidiRun(MatchBidiLevel, bidiLevelOfRun); }
     65    bool atRightBoundaryOfBidiRun(unsigned char bidiLevelOfRun) const { return atRightBoundaryOfBidiRun(MatchBidiLevel, bidiLevelOfRun); }
     66
     67    Position positionAtLeftBoundaryOfBiDiRun() const;
     68    Position positionAtRightBoundaryOfBiDiRun() const;
    5269
    5370    LayoutRect absoluteRect() const { return absoluteRect(0); }
     
    5572
    5673private:
     74    bool operator==(const RenderedPosition&) const { return false; }
     75    explicit RenderedPosition(RenderObject*, InlineBox*, int offset);
     76
     77    InlineBox* prevLeafChild() const;
     78    InlineBox* nextLeafChild() const;
     79    bool atLeftmostOffsetInBox() const { return m_inlineBox && m_offset == m_inlineBox->caretLeftmostOffset(); }
     80    bool atRightmostOffsetInBox() const { return m_inlineBox && m_offset == m_inlineBox->caretRightmostOffset(); }
     81    bool atLeftBoundaryOfBidiRun(ShouldMatchBidiLevel, unsigned char bidiLevelOfRun) const;
     82    bool atRightBoundaryOfBidiRun(ShouldMatchBidiLevel, unsigned char bidiLevelOfRun) const;
     83
    5784    LayoutRect absoluteRect(int* extraWidthToEndOfLine) const;
    5885
     
    6087    InlineBox* m_inlineBox;
    6188    int m_offset;
     89
     90    static InlineBox* uncachedInlineBox() { return reinterpret_cast<InlineBox*>(1); }
     91    // Needs to be different form 0 so pick 1 because it's also on the null page.
     92
     93    mutable InlineBox* m_prevLeafChild;
     94    mutable InlineBox* m_nextLeafChild;
    6295};
    6396
     
    6699    , m_inlineBox(0)
    67100    , m_offset(0)
     101    , m_prevLeafChild(uncachedInlineBox())
     102    , m_nextLeafChild(uncachedInlineBox())
     103{
     104}
     105
     106inline RenderedPosition::RenderedPosition(RenderObject* renderer, InlineBox* box, int offset)
     107    : m_renderer(renderer)
     108    , m_inlineBox(box)
     109    , m_offset(offset)
     110    , m_prevLeafChild(uncachedInlineBox())
     111    , m_nextLeafChild(uncachedInlineBox())
    68112{
    69113}
  • trunk/Source/WebCore/editing/VisibleSelection.h

    r93134 r95964  
    7070    VisiblePosition visibleStart() const { return VisiblePosition(m_start, isRange() ? DOWNSTREAM : affinity()); }
    7171    VisiblePosition visibleEnd() const { return VisiblePosition(m_end, isRange() ? UPSTREAM : affinity()); }
     72    VisiblePosition visibleBase() const { return VisiblePosition(m_base, isRange() ? (isBaseFirst() ? UPSTREAM : DOWNSTREAM) : affinity()); }
     73    VisiblePosition visibleExtent() const { return VisiblePosition(m_extent, isRange() ? (isBaseFirst() ? DOWNSTREAM : UPSTREAM) : affinity()); }
    7274
    7375    bool isNone() const { return selectionType() == NoSelection; }
  • trunk/Source/WebCore/page/EventHandler.cpp

    r95922 r95964  
    691691        newSelection.expandUsingGranularity(m_frame->selection()->granularity());
    692692
    693     m_frame->selection()->setNonDirectionalSelectionIfNeeded(newSelection, m_frame->selection()->granularity());
     693    m_frame->selection()->setNonDirectionalSelectionIfNeeded(newSelection, m_frame->selection()->granularity(),
     694        FrameSelection::AdjustEndpointsAtBidiBoundary);
    694695}
    695696#endif // ENABLE(DRAG_SUPPORT)
Note: See TracChangeset for help on using the changeset viewer.