| Index: Source/core/editing/VisibleUnits.cpp
|
| diff --git a/Source/core/editing/VisibleUnits.cpp b/Source/core/editing/VisibleUnits.cpp
|
| index 13d9ae5c497294c5cbd774f84df8bf78f73c9519..b3cab4afb4220392778ecdaa274ac5b0ed691fae 100644
|
| --- a/Source/core/editing/VisibleUnits.cpp
|
| +++ b/Source/core/editing/VisibleUnits.cpp
|
| @@ -1456,6 +1456,261 @@ VisiblePosition rightBoundaryOfLine(const VisiblePosition& c, TextDirection dire
|
| return direction == LTR ? logicalEndOfLine(c) : logicalStartOfLine(c);
|
| }
|
|
|
| +static bool isNonTextLeafChild(LayoutObject* object)
|
| +{
|
| + if (object->slowFirstChild())
|
| + return false;
|
| + if (object->isText())
|
| + return false;
|
| + return true;
|
| +}
|
| +
|
| +static InlineTextBox* searchAheadForBetterMatch(LayoutObject* layoutObject)
|
| +{
|
| + LayoutBlock* container = layoutObject->containingBlock();
|
| + for (LayoutObject* next = layoutObject->nextInPreOrder(container); next; next = next->nextInPreOrder(container)) {
|
| + if (next->isLayoutBlock())
|
| + return 0;
|
| + if (next->isBR())
|
| + return 0;
|
| + if (isNonTextLeafChild(next))
|
| + return 0;
|
| + if (next->isText()) {
|
| + InlineTextBox* match = 0;
|
| + int minOffset = INT_MAX;
|
| + for (InlineTextBox* box = toLayoutText(next)->firstTextBox(); box; box = box->nextTextBox()) {
|
| + int caretMinOffset = box->caretMinOffset();
|
| + if (caretMinOffset < minOffset) {
|
| + match = box;
|
| + minOffset = caretMinOffset;
|
| + }
|
| + }
|
| + if (match)
|
| + return match;
|
| + }
|
| + }
|
| + return 0;
|
| +}
|
| +
|
| +template <typename Strategy>
|
| +PositionAlgorithm<Strategy> downstreamIgnoringEditingBoundaries(PositionAlgorithm<Strategy> position)
|
| +{
|
| + PositionAlgorithm<Strategy> lastPosition;
|
| + while (position != lastPosition) {
|
| + lastPosition = position;
|
| + position = position.downstream(CanCrossEditingBoundary);
|
| + }
|
| + return position;
|
| +}
|
| +
|
| +template <typename Strategy>
|
| +PositionAlgorithm<Strategy> upstreamIgnoringEditingBoundaries(PositionAlgorithm<Strategy> position)
|
| +{
|
| + PositionAlgorithm<Strategy> lastPosition;
|
| + while (position != lastPosition) {
|
| + lastPosition = position;
|
| + position = position.upstream(CanCrossEditingBoundary);
|
| + }
|
| + return position;
|
| +}
|
| +
|
| +template <typename Strategy>
|
| +static InlineBoxPosition computeInlineBoxPositionAlgorithm(const PositionAlgorithm<Strategy>& position, TextAffinity affinity, TextDirection primaryDirection)
|
| +{
|
| + InlineBox* inlineBox = nullptr;
|
| + int caretOffset = position.computeEditingOffset();
|
| + Node* const anchorNode = position.anchorNode();
|
| + LayoutObject* layoutObject = anchorNode->isShadowRoot() ? toShadowRoot(anchorNode)->host()->layoutObject() : anchorNode->layoutObject();
|
| +
|
| + if (!layoutObject->isText()) {
|
| + inlineBox = 0;
|
| + if (canHaveChildrenForEditing(anchorNode) && layoutObject->isLayoutBlockFlow() && hasRenderedNonAnonymousDescendantsWithHeight(layoutObject)) {
|
| + // Try a visually equivalent position with possibly opposite
|
| + // editability. This helps in case |this| is in an editable block
|
| + // but surrounded by non-editable positions. It acts to negate the
|
| + // logic at the beginning of LayoutObject::createVisiblePosition().
|
| + PositionAlgorithm<Strategy> equivalent = downstreamIgnoringEditingBoundaries(position);
|
| + if (equivalent == position) {
|
| + equivalent = upstreamIgnoringEditingBoundaries(position);
|
| + if (equivalent == position || downstreamIgnoringEditingBoundaries(equivalent) == position)
|
| + return InlineBoxPosition(inlineBox, caretOffset);
|
| + }
|
| +
|
| + return computeInlineBoxPosition(equivalent, TextAffinity::Upstream, primaryDirection);
|
| + }
|
| + if (layoutObject->isBox()) {
|
| + inlineBox = toLayoutBox(layoutObject)->inlineBoxWrapper();
|
| + if (!inlineBox || (caretOffset > inlineBox->caretMinOffset() && caretOffset < inlineBox->caretMaxOffset()))
|
| + return InlineBoxPosition(inlineBox, caretOffset);
|
| + }
|
| + } else {
|
| + LayoutText* textLayoutObject = toLayoutText(layoutObject);
|
| +
|
| + InlineTextBox* box;
|
| + InlineTextBox* candidate = 0;
|
| +
|
| + for (box = textLayoutObject->firstTextBox(); box; box = box->nextTextBox()) {
|
| + int caretMinOffset = box->caretMinOffset();
|
| + int caretMaxOffset = box->caretMaxOffset();
|
| +
|
| + if (caretOffset < caretMinOffset || caretOffset > caretMaxOffset || (caretOffset == caretMaxOffset && box->isLineBreak()))
|
| + continue;
|
| +
|
| + if (caretOffset > caretMinOffset && caretOffset < caretMaxOffset)
|
| + return InlineBoxPosition(box, caretOffset);
|
| +
|
| + if (((caretOffset == caretMaxOffset) ^ (affinity == TextAffinity::Downstream))
|
| + || ((caretOffset == caretMinOffset) ^ (affinity == TextAffinity::Upstream))
|
| + || (caretOffset == caretMaxOffset && box->nextLeafChild() && box->nextLeafChild()->isLineBreak()))
|
| + break;
|
| +
|
| + candidate = box;
|
| + }
|
| + if (candidate && candidate == textLayoutObject->lastTextBox() && affinity == TextAffinity::Downstream) {
|
| + box = searchAheadForBetterMatch(textLayoutObject);
|
| + if (box)
|
| + caretOffset = box->caretMinOffset();
|
| + }
|
| + inlineBox = box ? box : candidate;
|
| + }
|
| +
|
| + if (!inlineBox)
|
| + return InlineBoxPosition(inlineBox, caretOffset);
|
| +
|
| + unsigned char level = inlineBox->bidiLevel();
|
| +
|
| + if (inlineBox->direction() == primaryDirection) {
|
| + if (caretOffset == inlineBox->caretRightmostOffset()) {
|
| + InlineBox* nextBox = inlineBox->nextLeafChild();
|
| + if (!nextBox || nextBox->bidiLevel() >= level)
|
| + return InlineBoxPosition(inlineBox, caretOffset);
|
| +
|
| + level = nextBox->bidiLevel();
|
| + InlineBox* prevBox = inlineBox;
|
| + do {
|
| + prevBox = prevBox->prevLeafChild();
|
| + } while (prevBox && prevBox->bidiLevel() > level);
|
| +
|
| + // For example, abc FED 123 ^ CBA
|
| + if (prevBox && prevBox->bidiLevel() == level)
|
| + return InlineBoxPosition(inlineBox, caretOffset);
|
| +
|
| + // For example, abc 123 ^ CBA
|
| + while (InlineBox* nextBox = inlineBox->nextLeafChild()) {
|
| + if (nextBox->bidiLevel() < level)
|
| + break;
|
| + inlineBox = nextBox;
|
| + }
|
| + return InlineBoxPosition(inlineBox, inlineBox->caretRightmostOffset());
|
| + }
|
| +
|
| + InlineBox* prevBox = inlineBox->prevLeafChild();
|
| + if (!prevBox || prevBox->bidiLevel() >= level)
|
| + return InlineBoxPosition(inlineBox, caretOffset);
|
| +
|
| + level = prevBox->bidiLevel();
|
| + InlineBox* nextBox = inlineBox;
|
| + do {
|
| + nextBox = nextBox->nextLeafChild();
|
| + } while (nextBox && nextBox->bidiLevel() > level);
|
| +
|
| + if (nextBox && nextBox->bidiLevel() == level)
|
| + return InlineBoxPosition(inlineBox, caretOffset);
|
| +
|
| + while (InlineBox* prevBox = inlineBox->prevLeafChild()) {
|
| + if (prevBox->bidiLevel() < level)
|
| + break;
|
| + inlineBox = prevBox;
|
| + }
|
| + return InlineBoxPosition(inlineBox, inlineBox->caretLeftmostOffset());
|
| + }
|
| +
|
| + if (caretOffset == inlineBox->caretLeftmostOffset()) {
|
| + InlineBox* prevBox = inlineBox->prevLeafChildIgnoringLineBreak();
|
| + if (!prevBox || prevBox->bidiLevel() < level) {
|
| + // Left edge of a secondary run. Set to the right edge of the entire
|
| + // run.
|
| + while (InlineBox* nextBox = inlineBox->nextLeafChildIgnoringLineBreak()) {
|
| + if (nextBox->bidiLevel() < level)
|
| + break;
|
| + inlineBox = nextBox;
|
| + }
|
| + return InlineBoxPosition(inlineBox, inlineBox->caretRightmostOffset());
|
| + }
|
| +
|
| + if (prevBox->bidiLevel() > level) {
|
| + // Right edge of a "tertiary" run. Set to the left edge of that run.
|
| + while (InlineBox* tertiaryBox = inlineBox->prevLeafChildIgnoringLineBreak()) {
|
| + if (tertiaryBox->bidiLevel() <= level)
|
| + break;
|
| + inlineBox = tertiaryBox;
|
| + }
|
| + return InlineBoxPosition(inlineBox, inlineBox->caretLeftmostOffset());
|
| + }
|
| + return InlineBoxPosition(inlineBox, caretOffset);
|
| + }
|
| +
|
| + if (layoutObject && layoutObject->style()->unicodeBidi() == Plaintext) {
|
| + if (inlineBox->bidiLevel() < level)
|
| + return InlineBoxPosition(inlineBox, inlineBox->caretLeftmostOffset());
|
| + return InlineBoxPosition(inlineBox, inlineBox->caretRightmostOffset());
|
| + }
|
| +
|
| + InlineBox* nextBox = inlineBox->nextLeafChildIgnoringLineBreak();
|
| + if (!nextBox || nextBox->bidiLevel() < level) {
|
| + // Right edge of a secondary run. Set to the left edge of the entire
|
| + // run.
|
| + while (InlineBox* prevBox = inlineBox->prevLeafChildIgnoringLineBreak()) {
|
| + if (prevBox->bidiLevel() < level)
|
| + break;
|
| + inlineBox = prevBox;
|
| + }
|
| + return InlineBoxPosition(inlineBox, inlineBox->caretLeftmostOffset());
|
| + }
|
| +
|
| + if (nextBox->bidiLevel() <= level)
|
| + return InlineBoxPosition(inlineBox, caretOffset);
|
| +
|
| + // Left edge of a "tertiary" run. Set to the right edge of that run.
|
| + while (InlineBox* tertiaryBox = inlineBox->nextLeafChildIgnoringLineBreak()) {
|
| + if (tertiaryBox->bidiLevel() <= level)
|
| + break;
|
| + inlineBox = tertiaryBox;
|
| + }
|
| + return InlineBoxPosition(inlineBox, inlineBox->caretRightmostOffset());
|
| +}
|
| +
|
| +template <typename Strategy>
|
| +static InlineBoxPosition computeInlineBoxPositionAlgorithm(const PositionAlgorithm<Strategy>& position, TextAffinity affinity)
|
| +{
|
| + return computeInlineBoxPositionAlgorithm<Strategy>(position, affinity, primaryDirectionOf(*position.anchorNode()));
|
| +}
|
| +
|
| +InlineBoxPosition computeInlineBoxPosition(const Position& position, TextAffinity affinity)
|
| +{
|
| + return computeInlineBoxPositionAlgorithm<EditingStrategy>(position, affinity);
|
| +}
|
| +
|
| +InlineBoxPosition computeInlineBoxPosition(const PositionInComposedTree& position, TextAffinity affinity)
|
| +{
|
| + return computeInlineBoxPositionAlgorithm<EditingInComposedTreeStrategy>(position, affinity);
|
| +}
|
| +
|
| +InlineBoxPosition computeInlineBoxPosition(const VisiblePosition& position)
|
| +{
|
| + return computeInlineBoxPosition(position.deepEquivalent(), position.affinity());
|
| +}
|
| +
|
| +InlineBoxPosition computeInlineBoxPosition(const Position& position, TextAffinity affinity, TextDirection primaryDirection)
|
| +{
|
| + return computeInlineBoxPositionAlgorithm<EditingStrategy>(position, affinity, primaryDirection);
|
| +}
|
| +
|
| +InlineBoxPosition computeInlineBoxPosition(const PositionInComposedTree& position, TextAffinity affinity, TextDirection primaryDirection)
|
| +{
|
| + return computeInlineBoxPositionAlgorithm<EditingInComposedTreeStrategy>(position, affinity, primaryDirection);
|
| +}
|
| +
|
| LayoutRect localCaretRectOfPosition(const PositionWithAffinity& position, LayoutObject*& layoutObject)
|
| {
|
| if (position.position().isNull()) {
|
|
|