Move positionForPoint to RenderTextLineBoxes
authorantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 12 Oct 2013 22:33:53 +0000 (22:33 +0000)
committerantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 12 Oct 2013 22:33:53 +0000 (22:33 +0000)
https://bugs.webkit.org/show_bug.cgi?id=122703

Reviewed by Andreas Kling.

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

Source/WebCore/ChangeLog
Source/WebCore/rendering/RenderObject.cpp
Source/WebCore/rendering/RenderObject.h
Source/WebCore/rendering/RenderText.cpp
Source/WebCore/rendering/RenderTextLineBoxes.cpp
Source/WebCore/rendering/RenderTextLineBoxes.h

index 908e7b8..55d01cb 100644 (file)
@@ -1,3 +1,10 @@
+2013-10-12  Antti Koivisto  <antti@apple.com>
+
+        Move positionForPoint to RenderTextLineBoxes
+        https://bugs.webkit.org/show_bug.cgi?id=122703
+
+        Reviewed by Andreas Kling.
+
 2013-10-12  Andreas Kling  <akling@apple.com>
 
         Remove unused ScrollingCoordinator hooks.
index 453e053..34893c7 100644 (file)
@@ -2497,7 +2497,7 @@ RenderBoxModelObject* RenderObject::offsetParent() const
     return curr && curr->isBoxModelObject() ? toRenderBoxModelObject(curr) : 0;
 }
 
-VisiblePosition RenderObject::createVisiblePosition(int offset, EAffinity affinity)
+VisiblePosition RenderObject::createVisiblePosition(int offset, EAffinity affinity) const
 {
     // If this is a non-anonymous renderer in an editable area, then it's simple.
     if (Node* node = nonPseudoNode()) {
@@ -2521,10 +2521,10 @@ VisiblePosition RenderObject::createVisiblePosition(int offset, EAffinity affini
     // find a single non-anonymous renderer.
 
     // Find a nearby non-anonymous renderer.
-    RenderObject* child = this;
-    while (RenderObject* parent = child->parent()) {
+    const RenderObject* child = this;
+    while (const RenderElement* parent = child->parent()) {
         // Find non-anonymous content after.
-        RenderObject* renderer = child;
+        const RenderObject* renderer = child;
         while ((renderer = renderer->nextInPreOrder(parent))) {
             if (Node* node = renderer->nonPseudoNode())
                 return VisiblePosition(firstPositionInOrBeforeNode(node), DOWNSTREAM);
@@ -2540,8 +2540,8 @@ VisiblePosition RenderObject::createVisiblePosition(int offset, EAffinity affini
         }
 
         // Use the parent itself unless it too is anonymous.
-        if (Node* node = parent->nonPseudoNode())
-            return VisiblePosition(firstPositionInOrBeforeNode(node), DOWNSTREAM);
+        if (Element* element = parent->nonPseudoElement())
+            return VisiblePosition(firstPositionInOrBeforeNode(element), DOWNSTREAM);
 
         // Repeat at the next level up.
         child = parent;
@@ -2551,7 +2551,7 @@ VisiblePosition RenderObject::createVisiblePosition(int offset, EAffinity affini
     return VisiblePosition();
 }
 
-VisiblePosition RenderObject::createVisiblePosition(const Position& position)
+VisiblePosition RenderObject::createVisiblePosition(const Position& position) const
 {
     if (position.isNotNull())
         return VisiblePosition(position);
index 640dba7..c3ac716 100644 (file)
@@ -669,8 +669,8 @@ public:
     virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction);
 
     virtual VisiblePosition positionForPoint(const LayoutPoint&);
-    VisiblePosition createVisiblePosition(int offset, EAffinity);
-    VisiblePosition createVisiblePosition(const Position&);
+    VisiblePosition createVisiblePosition(int offset, EAffinity) const;
+    VisiblePosition createVisiblePosition(const Position&) const;
 
     // returns the containing block level element for this element.
     RenderBlock* containingBlock() const;
index 541c11f..10365c8 100644 (file)
@@ -403,169 +403,9 @@ void RenderText::absoluteQuadsForRange(Vector<FloatQuad>& quads, unsigned start,
     }
 }
 
-enum ShouldAffinityBeDownstream { AlwaysDownstream, AlwaysUpstream, UpstreamIfPositionIsNotAtStart };
-
-static bool lineDirectionPointFitsInBox(int pointLineDirection, InlineTextBox* box, ShouldAffinityBeDownstream& shouldAffinityBeDownstream)
-{
-    shouldAffinityBeDownstream = AlwaysDownstream;
-
-    // the x coordinate is equal to the left edge of this box
-    // the affinity must be downstream so the position doesn't jump back to the previous line
-    // except when box is the first box in the line
-    if (pointLineDirection <= box->logicalLeft()) {
-        shouldAffinityBeDownstream = !box->prevLeafChild() ? UpstreamIfPositionIsNotAtStart : AlwaysDownstream;
-        return true;
-    }
-
-    // and the x coordinate is to the left of the right edge of this box
-    // check to see if position goes in this box
-    if (pointLineDirection < box->logicalRight()) {
-        shouldAffinityBeDownstream = UpstreamIfPositionIsNotAtStart;
-        return true;
-    }
-
-    // box is first on line
-    // and the x coordinate is to the left of the first text box left edge
-    if (!box->prevLeafChildIgnoringLineBreak() && pointLineDirection < box->logicalLeft())
-        return true;
-
-    if (!box->nextLeafChildIgnoringLineBreak()) {
-        // box is last on line
-        // and the x coordinate is to the right of the last text box right edge
-        // generate VisiblePosition, use UPSTREAM affinity if possible
-        shouldAffinityBeDownstream = UpstreamIfPositionIsNotAtStart;
-        return true;
-    }
-
-    return false;
-}
-
-static VisiblePosition createVisiblePositionForBox(const InlineBox* box, int offset, ShouldAffinityBeDownstream shouldAffinityBeDownstream)
-{
-    EAffinity affinity = VP_DEFAULT_AFFINITY;
-    switch (shouldAffinityBeDownstream) {
-    case AlwaysDownstream:
-        affinity = DOWNSTREAM;
-        break;
-    case AlwaysUpstream:
-        affinity = VP_UPSTREAM_IF_POSSIBLE;
-        break;
-    case UpstreamIfPositionIsNotAtStart:
-        affinity = offset > box->caretMinOffset() ? VP_UPSTREAM_IF_POSSIBLE : DOWNSTREAM;
-        break;
-    }
-    return box->renderer().createVisiblePosition(offset, affinity);
-}
-
-static VisiblePosition createVisiblePositionAfterAdjustingOffsetForBiDi(const InlineTextBox* box, int offset, ShouldAffinityBeDownstream shouldAffinityBeDownstream)
-{
-    ASSERT(box);
-    ASSERT(offset >= 0);
-
-    if (offset && static_cast<unsigned>(offset) < box->len())
-        return createVisiblePositionForBox(box, box->start() + offset, shouldAffinityBeDownstream);
-
-    bool positionIsAtStartOfBox = !offset;
-    if (positionIsAtStartOfBox == box->isLeftToRightDirection()) {
-        // offset is on the left edge
-
-        const InlineBox* prevBox = box->prevLeafChildIgnoringLineBreak();
-        if ((prevBox && prevBox->bidiLevel() == box->bidiLevel())
-            || box->renderer().containingBlock()->style()->direction() == box->direction()) // FIXME: left on 12CBA
-            return createVisiblePositionForBox(box, box->caretLeftmostOffset(), shouldAffinityBeDownstream);
-
-        if (prevBox && prevBox->bidiLevel() > box->bidiLevel()) {
-            // e.g. left of B in aDC12BAb
-            const InlineBox* leftmostBox;
-            do {
-                leftmostBox = prevBox;
-                prevBox = leftmostBox->prevLeafChildIgnoringLineBreak();
-            } while (prevBox && prevBox->bidiLevel() > box->bidiLevel());
-            return createVisiblePositionForBox(leftmostBox, leftmostBox->caretRightmostOffset(), shouldAffinityBeDownstream);
-        }
-
-        if (!prevBox || prevBox->bidiLevel() < box->bidiLevel()) {
-            // e.g. left of D in aDC12BAb
-            const InlineBox* rightmostBox;
-            const InlineBox* nextBox = box;
-            do {
-                rightmostBox = nextBox;
-                nextBox = rightmostBox->nextLeafChildIgnoringLineBreak();
-            } while (nextBox && nextBox->bidiLevel() >= box->bidiLevel());
-            return createVisiblePositionForBox(rightmostBox,
-                box->isLeftToRightDirection() ? rightmostBox->caretMaxOffset() : rightmostBox->caretMinOffset(), shouldAffinityBeDownstream);
-        }
-
-        return createVisiblePositionForBox(box, box->caretRightmostOffset(), shouldAffinityBeDownstream);
-    }
-
-    const InlineBox* nextBox = box->nextLeafChildIgnoringLineBreak();
-    if ((nextBox && nextBox->bidiLevel() == box->bidiLevel())
-        || box->renderer().containingBlock()->style()->direction() == box->direction())
-        return createVisiblePositionForBox(box, box->caretRightmostOffset(), shouldAffinityBeDownstream);
-
-    // offset is on the right edge
-    if (nextBox && nextBox->bidiLevel() > box->bidiLevel()) {
-        // e.g. right of C in aDC12BAb
-        const InlineBox* rightmostBox;
-        do {
-            rightmostBox = nextBox;
-            nextBox = rightmostBox->nextLeafChildIgnoringLineBreak();
-        } while (nextBox && nextBox->bidiLevel() > box->bidiLevel());
-        return createVisiblePositionForBox(rightmostBox, rightmostBox->caretLeftmostOffset(), shouldAffinityBeDownstream);
-    }
-
-    if (!nextBox || nextBox->bidiLevel() < box->bidiLevel()) {
-        // e.g. right of A in aDC12BAb
-        const InlineBox* leftmostBox;
-        const InlineBox* prevBox = box;
-        do {
-            leftmostBox = prevBox;
-            prevBox = leftmostBox->prevLeafChildIgnoringLineBreak();
-        } while (prevBox && prevBox->bidiLevel() >= box->bidiLevel());
-        return createVisiblePositionForBox(leftmostBox,
-            box->isLeftToRightDirection() ? leftmostBox->caretMinOffset() : leftmostBox->caretMaxOffset(), shouldAffinityBeDownstream);
-    }
-
-    return createVisiblePositionForBox(box, box->caretLeftmostOffset(), shouldAffinityBeDownstream);
-}
-
 VisiblePosition RenderText::positionForPoint(const LayoutPoint& point)
 {
-    if (!firstTextBox() || textLength() == 0)
-        return createVisiblePosition(0, DOWNSTREAM);
-
-    LayoutUnit pointLineDirection = firstTextBox()->isHorizontal() ? point.x() : point.y();
-    LayoutUnit pointBlockDirection = firstTextBox()->isHorizontal() ? point.y() : point.x();
-    bool blocksAreFlipped = style()->isFlippedBlocksWritingMode();
-
-    InlineTextBox* lastBox = 0;
-    for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) {
-        if (box->isLineBreak() && !box->prevLeafChild() && box->nextLeafChild() && !box->nextLeafChild()->isLineBreak())
-            box = box->nextTextBox();
-
-        const RootInlineBox& rootBox = box->root();
-        LayoutUnit top = min(rootBox.selectionTop(), rootBox.lineTop());
-        if (pointBlockDirection > top || (!blocksAreFlipped && pointBlockDirection == top)) {
-            LayoutUnit bottom = rootBox.selectionBottom();
-            if (rootBox.nextRootBox())
-                bottom = min(bottom, rootBox.nextRootBox()->lineTop());
-
-            if (pointBlockDirection < bottom || (blocksAreFlipped && pointBlockDirection == bottom)) {
-                ShouldAffinityBeDownstream shouldAffinityBeDownstream;
-                if (lineDirectionPointFitsInBox(pointLineDirection, box, shouldAffinityBeDownstream))
-                    return createVisiblePositionAfterAdjustingOffsetForBiDi(box, box->offsetForPosition(pointLineDirection), shouldAffinityBeDownstream);
-            }
-        }
-        lastBox = box;
-    }
-
-    if (lastBox) {
-        ShouldAffinityBeDownstream shouldAffinityBeDownstream;
-        lineDirectionPointFitsInBox(pointLineDirection, lastBox, shouldAffinityBeDownstream);
-        return createVisiblePositionAfterAdjustingOffsetForBiDi(lastBox, lastBox->offsetForPosition(pointLineDirection) + lastBox->start(), shouldAffinityBeDownstream);
-    }
-    return createVisiblePosition(0, DOWNSTREAM);
+    return m_lineBoxes.positionForPoint(*this, point);
 }
 
 LayoutRect RenderText::localCaretRect(InlineBox* inlineBox, int caretOffset, LayoutUnit* extraWidthToEndOfLine)
index d7aab7a..301fc2f 100644 (file)
@@ -27,6 +27,7 @@
 #include "RenderTextLineBoxes.h"
 
 #include "InlineTextBox.h"
+#include "RenderBlock.h"
 #include "RenderStyle.h"
 #include "RootInlineBox.h"
 
@@ -224,6 +225,170 @@ int RenderTextLineBoxes::caretMaxOffset(const RenderText& renderer) const
     return maxOffset;
 }
 
+enum ShouldAffinityBeDownstream { AlwaysDownstream, AlwaysUpstream, UpstreamIfPositionIsNotAtStart };
+
+static bool lineDirectionPointFitsInBox(int pointLineDirection, const InlineTextBox& box, ShouldAffinityBeDownstream& shouldAffinityBeDownstream)
+{
+    shouldAffinityBeDownstream = AlwaysDownstream;
+
+    // the x coordinate is equal to the left edge of this box
+    // the affinity must be downstream so the position doesn't jump back to the previous line
+    // except when box is the first box in the line
+    if (pointLineDirection <= box.logicalLeft()) {
+        shouldAffinityBeDownstream = !box.prevLeafChild() ? UpstreamIfPositionIsNotAtStart : AlwaysDownstream;
+        return true;
+    }
+
+    // and the x coordinate is to the left of the right edge of this box
+    // check to see if position goes in this box
+    if (pointLineDirection < box.logicalRight()) {
+        shouldAffinityBeDownstream = UpstreamIfPositionIsNotAtStart;
+        return true;
+    }
+
+    // box is first on line
+    // and the x coordinate is to the left of the first text box left edge
+    if (!box.prevLeafChildIgnoringLineBreak() && pointLineDirection < box.logicalLeft())
+        return true;
+
+    if (!box.nextLeafChildIgnoringLineBreak()) {
+        // box is last on line
+        // and the x coordinate is to the right of the last text box right edge
+        // generate VisiblePosition, use UPSTREAM affinity if possible
+        shouldAffinityBeDownstream = UpstreamIfPositionIsNotAtStart;
+        return true;
+    }
+
+    return false;
+}
+
+static VisiblePosition createVisiblePositionForBox(const InlineBox& box, int offset, ShouldAffinityBeDownstream shouldAffinityBeDownstream)
+{
+    EAffinity affinity = VP_DEFAULT_AFFINITY;
+    switch (shouldAffinityBeDownstream) {
+    case AlwaysDownstream:
+        affinity = DOWNSTREAM;
+        break;
+    case AlwaysUpstream:
+        affinity = VP_UPSTREAM_IF_POSSIBLE;
+        break;
+    case UpstreamIfPositionIsNotAtStart:
+        affinity = offset > box.caretMinOffset() ? VP_UPSTREAM_IF_POSSIBLE : DOWNSTREAM;
+        break;
+    }
+    return box.renderer().createVisiblePosition(offset, affinity);
+}
+
+static VisiblePosition createVisiblePositionAfterAdjustingOffsetForBiDi(const InlineTextBox& box, int offset, ShouldAffinityBeDownstream shouldAffinityBeDownstream)
+{
+    ASSERT(offset >= 0);
+
+    if (offset && static_cast<unsigned>(offset) < box.len())
+        return createVisiblePositionForBox(box, box.start() + offset, shouldAffinityBeDownstream);
+
+    bool positionIsAtStartOfBox = !offset;
+    if (positionIsAtStartOfBox == box.isLeftToRightDirection()) {
+        // offset is on the left edge
+
+        const InlineBox* prevBox = box.prevLeafChildIgnoringLineBreak();
+        if ((prevBox && prevBox->bidiLevel() == box.bidiLevel())
+            || box.renderer().containingBlock()->style()->direction() == box.direction()) // FIXME: left on 12CBA
+            return createVisiblePositionForBox(box, box.caretLeftmostOffset(), shouldAffinityBeDownstream);
+
+        if (prevBox && prevBox->bidiLevel() > box.bidiLevel()) {
+            // e.g. left of B in aDC12BAb
+            const InlineBox* leftmostBox;
+            do {
+                leftmostBox = prevBox;
+                prevBox = leftmostBox->prevLeafChildIgnoringLineBreak();
+            } while (prevBox && prevBox->bidiLevel() > box.bidiLevel());
+            return createVisiblePositionForBox(*leftmostBox, leftmostBox->caretRightmostOffset(), shouldAffinityBeDownstream);
+        }
+
+        if (!prevBox || prevBox->bidiLevel() < box.bidiLevel()) {
+            // e.g. left of D in aDC12BAb
+            const InlineBox* rightmostBox;
+            const InlineBox* nextBox = &box;
+            do {
+                rightmostBox = nextBox;
+                nextBox = rightmostBox->nextLeafChildIgnoringLineBreak();
+            } while (nextBox && nextBox->bidiLevel() >= box.bidiLevel());
+            return createVisiblePositionForBox(*rightmostBox,
+                box.isLeftToRightDirection() ? rightmostBox->caretMaxOffset() : rightmostBox->caretMinOffset(), shouldAffinityBeDownstream);
+        }
+
+        return createVisiblePositionForBox(box, box.caretRightmostOffset(), shouldAffinityBeDownstream);
+    }
+
+    const InlineBox* nextBox = box.nextLeafChildIgnoringLineBreak();
+    if ((nextBox && nextBox->bidiLevel() == box.bidiLevel())
+        || box.renderer().containingBlock()->style()->direction() == box.direction())
+        return createVisiblePositionForBox(box, box.caretRightmostOffset(), shouldAffinityBeDownstream);
+
+    // offset is on the right edge
+    if (nextBox && nextBox->bidiLevel() > box.bidiLevel()) {
+        // e.g. right of C in aDC12BAb
+        const InlineBox* rightmostBox;
+        do {
+            rightmostBox = nextBox;
+            nextBox = rightmostBox->nextLeafChildIgnoringLineBreak();
+        } while (nextBox && nextBox->bidiLevel() > box.bidiLevel());
+        return createVisiblePositionForBox(*rightmostBox, rightmostBox->caretLeftmostOffset(), shouldAffinityBeDownstream);
+    }
+
+    if (!nextBox || nextBox->bidiLevel() < box.bidiLevel()) {
+        // e.g. right of A in aDC12BAb
+        const InlineBox* leftmostBox;
+        const InlineBox* prevBox = &box;
+        do {
+            leftmostBox = prevBox;
+            prevBox = leftmostBox->prevLeafChildIgnoringLineBreak();
+        } while (prevBox && prevBox->bidiLevel() >= box.bidiLevel());
+        return createVisiblePositionForBox(*leftmostBox,
+            box.isLeftToRightDirection() ? leftmostBox->caretMinOffset() : leftmostBox->caretMaxOffset(), shouldAffinityBeDownstream);
+    }
+
+    return createVisiblePositionForBox(box, box.caretLeftmostOffset(), shouldAffinityBeDownstream);
+}
+
+VisiblePosition RenderTextLineBoxes::positionForPoint(const RenderText& renderer, const LayoutPoint& point) const
+{
+    if (!m_first || !renderer.textLength())
+        return renderer.createVisiblePosition(0, DOWNSTREAM);
+
+    LayoutUnit pointLineDirection = m_first->isHorizontal() ? point.x() : point.y();
+    LayoutUnit pointBlockDirection = m_first->isHorizontal() ? point.y() : point.x();
+    bool blocksAreFlipped = renderer.style()->isFlippedBlocksWritingMode();
+
+    InlineTextBox* lastBox = nullptr;
+    for (auto box = m_first; box; box = box->nextTextBox()) {
+        if (box->isLineBreak() && !box->prevLeafChild() && box->nextLeafChild() && !box->nextLeafChild()->isLineBreak())
+            box = box->nextTextBox();
+
+        auto& rootBox = box->root();
+        LayoutUnit top = std::min(rootBox.selectionTop(), rootBox.lineTop());
+        if (pointBlockDirection > top || (!blocksAreFlipped && pointBlockDirection == top)) {
+            LayoutUnit bottom = rootBox.selectionBottom();
+            if (rootBox.nextRootBox())
+                bottom = std::min(bottom, rootBox.nextRootBox()->lineTop());
+
+            if (pointBlockDirection < bottom || (blocksAreFlipped && pointBlockDirection == bottom)) {
+                ShouldAffinityBeDownstream shouldAffinityBeDownstream;
+                if (lineDirectionPointFitsInBox(pointLineDirection, *box, shouldAffinityBeDownstream))
+                    return createVisiblePositionAfterAdjustingOffsetForBiDi(*box, box->offsetForPosition(pointLineDirection), shouldAffinityBeDownstream);
+            }
+        }
+        lastBox = box;
+    }
+
+    if (lastBox) {
+        ShouldAffinityBeDownstream shouldAffinityBeDownstream;
+        lineDirectionPointFitsInBox(pointLineDirection, *lastBox, shouldAffinityBeDownstream);
+        return createVisiblePositionAfterAdjustingOffsetForBiDi(*lastBox, lastBox->offsetForPosition(pointLineDirection) + lastBox->start(), shouldAffinityBeDownstream);
+    }
+    return renderer.createVisiblePosition(0, DOWNSTREAM);
+}
+
 void RenderTextLineBoxes::dirtyAll()
 {
     for (auto box = m_first; box; box = box->nextTextBox())
@@ -305,15 +470,9 @@ bool RenderTextLineBoxes::dirtyRange(RenderText& renderer, unsigned start, unsig
     return dirtiedLines;
 }
 
-#if !ASSERT_DISABLED
-RenderTextLineBoxes::~RenderTextLineBoxes()
-{
-    ASSERT(!m_first);
-    ASSERT(!m_last);
-}
-
-void RenderTextLineBoxes::checkConsistency() const
+inline void RenderTextLineBoxes::checkConsistency() const
 {
+#if !ASSERT_DISABLED
 #ifdef CHECK_CONSISTENCY
     const InlineTextBox* prev = nullptr;
     for (auto child = m_first; child; child = child->nextTextBox()) {
@@ -323,6 +482,14 @@ void RenderTextLineBoxes::checkConsistency() const
     }
     ASSERT(prev == m_last);
 #endif
+#endif
+}
+
+#if !ASSERT_DISABLED
+RenderTextLineBoxes::~RenderTextLineBoxes()
+{
+    ASSERT(!m_first);
+    ASSERT(!m_last);
 }
 #endif
 
index 93aca13..e95629e 100644 (file)
@@ -27,6 +27,7 @@
 #define RenderTextLineBoxes_h
 
 #include "LayoutRect.h"
+#include "VisiblePosition.h"
 
 namespace WebCore {
 
@@ -56,6 +57,8 @@ public:
     int caretMinOffset() const;
     int caretMaxOffset(const RenderText&) const;
 
+    VisiblePosition positionForPoint(const RenderText&, const LayoutPoint&) const;
+
     IntRect boundingBox(const RenderText&) const;
     LayoutRect visualOverflowBoundingBox(const RenderText&) const;
 
@@ -67,11 +70,7 @@ public:
 #endif
 
 private:
-#if !ASSERT_DISABLED
     void checkConsistency() const;
-#else
-    void checkConsistency() const { }
-#endif
 
     InlineTextBox* m_first;
     InlineTextBox* m_last;
@@ -79,5 +78,4 @@ private:
 
 }
 
-
 #endif