Index: Source/core/editing/Position.cpp |
diff --git a/Source/core/editing/Position.cpp b/Source/core/editing/Position.cpp |
index 6716f960a70ab8033c29ccc9d94401ea67a8e9c0..9733c7a295ee7e49e5f5b2481d0f5c5d8ac8abaf 100644 |
--- a/Source/core/editing/Position.cpp |
+++ b/Source/core/editing/Position.cpp |
@@ -515,301 +515,16 @@ bool PositionAlgorithm<Strategy>::atEndOfTree() const |
return !Strategy::parent(*anchorNode()) && m_offset >= EditingStrategy::lastOffsetForEditing(anchorNode()); |
} |
-// Whether or not [node, 0] and [node, lastOffsetForEditing(node)] are their own VisiblePositions. |
-// If true, adjacent candidates are visually distinct. |
-// FIXME: Disregard nodes with layoutObjects that have no height, as we do in isCandidate. |
-// FIXME: Share code with isCandidate, if possible. |
-static bool endsOfNodeAreVisuallyDistinctPositions(Node* node) |
-{ |
- if (!node || !node->layoutObject()) |
- return false; |
- |
- if (!node->layoutObject()->isInline()) |
- return true; |
- |
- // Don't include inline tables. |
- if (isHTMLTableElement(*node)) |
- return false; |
- |
- // A Marquee elements are moving so we should assume their ends are always |
- // visibily distinct. |
- if (isHTMLMarqueeElement(*node)) |
- return true; |
- |
- // There is a VisiblePosition inside an empty inline-block container. |
- return node->layoutObject()->isReplaced() && canHaveChildrenForEditing(node) && toLayoutBox(node->layoutObject())->size().height() != 0 && !node->hasChildren(); |
-} |
- |
-template <typename Strategy> |
-static Node* enclosingVisualBoundary(Node* node) |
-{ |
- while (node && !endsOfNodeAreVisuallyDistinctPositions(node)) |
- node = Strategy::parent(*node); |
- |
- return node; |
-} |
- |
-// upstream() and downstream() want to return positions that are either in a |
-// text node or at just before a non-text node. This method checks for that. |
-template <typename Strategy> |
-static bool isStreamer(const PositionIteratorAlgorithm<Strategy>& pos) |
-{ |
- if (!pos.node()) |
- return true; |
- |
- if (isAtomicNode(pos.node())) |
- return true; |
- |
- return pos.atStartOfNode(); |
-} |
- |
-// This function and downstream() are used for moving back and forth between visually equivalent candidates. |
-// For example, for the text node "foo bar" where whitespace is collapsible, there are two candidates |
-// that map to the VisiblePosition between 'b' and the space. This function will return the left candidate |
-// and downstream() will return the right one. |
-// Also, upstream() will return [boundary, 0] for any of the positions from [boundary, 0] to the first candidate |
-// in boundary, where endsOfNodeAreVisuallyDistinctPositions(boundary) is true. |
template <typename Strategy> |
PositionAlgorithm<Strategy> PositionAlgorithm<Strategy>::upstream(EditingBoundaryCrossingRule rule) const |
{ |
- TRACE_EVENT0("blink", "Position::upstream"); |
- |
- Node* startNode = anchorNode(); |
- if (!startNode) |
- return PositionAlgorithm<Strategy>(); |
- |
- // iterate backward from there, looking for a qualified position |
- Node* boundary = enclosingVisualBoundary<Strategy>(startNode); |
- // FIXME: PositionIterator should respect Before and After positions. |
- PositionIteratorAlgorithm<Strategy> lastVisible(isAfterAnchor() ? editingPositionOf(m_anchorNode.get(), Strategy::caretMaxOffset(*m_anchorNode)) : PositionAlgorithm<Strategy>(*this)); |
- PositionIteratorAlgorithm<Strategy> currentPos = lastVisible; |
- bool startEditable = startNode->hasEditableStyle(); |
- Node* lastNode = startNode; |
- bool boundaryCrossed = false; |
- for (; !currentPos.atStart(); currentPos.decrement()) { |
- Node* currentNode = currentPos.node(); |
- // Don't check for an editability change if we haven't moved to a different node, |
- // to avoid the expense of computing hasEditableStyle(). |
- if (currentNode != lastNode) { |
- // Don't change editability. |
- bool currentEditable = currentNode->hasEditableStyle(); |
- if (startEditable != currentEditable) { |
- if (rule == CannotCrossEditingBoundary) |
- break; |
- boundaryCrossed = true; |
- } |
- lastNode = currentNode; |
- } |
- |
- // If we've moved to a position that is visually distinct, return the last saved position. There |
- // is code below that terminates early if we're *about* to move to a visually distinct position. |
- if (endsOfNodeAreVisuallyDistinctPositions(currentNode) && currentNode != boundary) |
- return lastVisible.deprecatedComputePosition(); |
- |
- // skip position in non-laid out or invisible node |
- LayoutObject* layoutObject = currentNode->layoutObject(); |
- if (!layoutObject || layoutObject->style()->visibility() != VISIBLE) |
- continue; |
- |
- if (rule == CanCrossEditingBoundary && boundaryCrossed) { |
- lastVisible = currentPos; |
- break; |
- } |
- |
- // track last visible streamer position |
- if (isStreamer<Strategy>(currentPos)) |
- lastVisible = currentPos; |
- |
- // Don't move past a position that is visually distinct. We could rely on code above to terminate and |
- // return lastVisible on the next iteration, but we terminate early to avoid doing a nodeIndex() call. |
- if (endsOfNodeAreVisuallyDistinctPositions(currentNode) && currentPos.atStartOfNode()) |
- return lastVisible.deprecatedComputePosition(); |
- |
- // Return position after tables and nodes which have content that can be ignored. |
- if (Strategy::editingIgnoresContent(currentNode) || isRenderedHTMLTableElement(currentNode)) { |
- if (currentPos.atEndOfNode()) |
- return afterNode(currentNode); |
- continue; |
- } |
- |
- // return current position if it is in laid out text |
- if (layoutObject->isText() && toLayoutText(layoutObject)->firstTextBox()) { |
- if (currentNode != startNode) { |
- // This assertion fires in layout tests in the case-transform.html test because |
- // of a mix-up between offsets in the text in the DOM tree with text in the |
- // layout tree which can have a different length due to case transformation. |
- // Until we resolve that, disable this so we can run the layout tests! |
- // ASSERT(currentOffset >= layoutObject->caretMaxOffset()); |
- return PositionAlgorithm<Strategy>(currentNode, layoutObject->caretMaxOffset()); |
- } |
- |
- unsigned textOffset = currentPos.offsetInLeafNode(); |
- LayoutText* textLayoutObject = toLayoutText(layoutObject); |
- InlineTextBox* lastTextBox = textLayoutObject->lastTextBox(); |
- for (InlineTextBox* box = textLayoutObject->firstTextBox(); box; box = box->nextTextBox()) { |
- if (textOffset <= box->start() + box->len()) { |
- if (textOffset > box->start()) |
- return currentPos.computePosition(); |
- continue; |
- } |
- |
- if (box == lastTextBox || textOffset != box->start() + box->len() + 1) |
- continue; |
- |
- // The text continues on the next line only if the last text box is not on this line and |
- // none of the boxes on this line have a larger start offset. |
- |
- bool continuesOnNextLine = true; |
- InlineBox* otherBox = box; |
- while (continuesOnNextLine) { |
- otherBox = otherBox->nextLeafChild(); |
- if (!otherBox) |
- break; |
- if (otherBox == lastTextBox || (otherBox->layoutObject() == textLayoutObject && toInlineTextBox(otherBox)->start() > textOffset)) |
- continuesOnNextLine = false; |
- } |
- |
- otherBox = box; |
- while (continuesOnNextLine) { |
- otherBox = otherBox->prevLeafChild(); |
- if (!otherBox) |
- break; |
- if (otherBox == lastTextBox || (otherBox->layoutObject() == textLayoutObject && toInlineTextBox(otherBox)->start() > textOffset)) |
- continuesOnNextLine = false; |
- } |
- |
- if (continuesOnNextLine) |
- return currentPos.computePosition(); |
- } |
- } |
- } |
- return lastVisible.deprecatedComputePosition(); |
+ return mostForwardCaretPosition(*this, rule); |
} |
-// This function and upstream() are used for moving back and forth between visually equivalent candidates. |
-// For example, for the text node "foo bar" where whitespace is collapsible, there are two candidates |
-// that map to the VisiblePosition between 'b' and the space. This function will return the right candidate |
-// and upstream() will return the left one. |
-// Also, downstream() will return the last position in the last atomic node in boundary for all of the positions |
-// in boundary after the last candidate, where endsOfNodeAreVisuallyDistinctPositions(boundary). |
-// FIXME: This function should never be called when the line box tree is dirty. See https://bugs.webkit.org/show_bug.cgi?id=97264 |
template <typename Strategy> |
PositionAlgorithm<Strategy> PositionAlgorithm<Strategy>::downstream(EditingBoundaryCrossingRule rule) const |
{ |
- TRACE_EVENT0("blink", "Position::downstream"); |
- |
- Node* startNode = anchorNode(); |
- if (!startNode) |
- return PositionAlgorithm<Strategy>(); |
- |
- // iterate forward from there, looking for a qualified position |
- Node* boundary = enclosingVisualBoundary<Strategy>(startNode); |
- // FIXME: PositionIterator should respect Before and After positions. |
- PositionIteratorAlgorithm<Strategy> lastVisible(isAfterAnchor() ? editingPositionOf(m_anchorNode.get(), Strategy::caretMaxOffset(*m_anchorNode)) : PositionAlgorithm<Strategy>(*this)); |
- PositionIteratorAlgorithm<Strategy> currentPos = lastVisible; |
- bool startEditable = startNode->hasEditableStyle(); |
- Node* lastNode = startNode; |
- bool boundaryCrossed = false; |
- for (; !currentPos.atEnd(); currentPos.increment()) { |
- Node* currentNode = currentPos.node(); |
- // Don't check for an editability change if we haven't moved to a different node, |
- // to avoid the expense of computing hasEditableStyle(). |
- if (currentNode != lastNode) { |
- // Don't change editability. |
- bool currentEditable = currentNode->hasEditableStyle(); |
- if (startEditable != currentEditable) { |
- if (rule == CannotCrossEditingBoundary) |
- break; |
- boundaryCrossed = true; |
- } |
- |
- lastNode = currentNode; |
- } |
- |
- // stop before going above the body, up into the head |
- // return the last visible streamer position |
- if (isHTMLBodyElement(*currentNode) && currentPos.atEndOfNode()) |
- break; |
- |
- // Do not move to a visually distinct position. |
- if (endsOfNodeAreVisuallyDistinctPositions(currentNode) && currentNode != boundary) |
- return lastVisible.deprecatedComputePosition(); |
- // Do not move past a visually disinct position. |
- // Note: The first position after the last in a node whose ends are visually distinct |
- // positions will be [boundary->parentNode(), originalBlock->nodeIndex() + 1]. |
- if (boundary && Strategy::parent(*boundary) == currentNode) |
- return lastVisible.deprecatedComputePosition(); |
- |
- // skip position in non-laid out or invisible node |
- LayoutObject* layoutObject = currentNode->layoutObject(); |
- if (!layoutObject || layoutObject->style()->visibility() != VISIBLE) |
- continue; |
- |
- if (rule == CanCrossEditingBoundary && boundaryCrossed) { |
- lastVisible = currentPos; |
- break; |
- } |
- |
- // track last visible streamer position |
- if (isStreamer<Strategy>(currentPos)) |
- lastVisible = currentPos; |
- |
- // Return position before tables and nodes which have content that can be ignored. |
- if (Strategy::editingIgnoresContent(currentNode) || isRenderedHTMLTableElement(currentNode)) { |
- if (currentPos.offsetInLeafNode() <= layoutObject->caretMinOffset()) |
- return editingPositionOf(currentNode, layoutObject->caretMinOffset()); |
- continue; |
- } |
- |
- // return current position if it is in laid out text |
- if (layoutObject->isText() && toLayoutText(layoutObject)->firstTextBox()) { |
- if (currentNode != startNode) { |
- ASSERT(currentPos.atStartOfNode()); |
- return PositionAlgorithm<Strategy>(currentNode, layoutObject->caretMinOffset()); |
- } |
- |
- unsigned textOffset = currentPos.offsetInLeafNode(); |
- LayoutText* textLayoutObject = toLayoutText(layoutObject); |
- InlineTextBox* lastTextBox = textLayoutObject->lastTextBox(); |
- for (InlineTextBox* box = textLayoutObject->firstTextBox(); box; box = box->nextTextBox()) { |
- if (textOffset <= box->end()) { |
- if (textOffset >= box->start()) |
- return currentPos.computePosition(); |
- continue; |
- } |
- |
- if (box == lastTextBox || textOffset != box->start() + box->len()) |
- continue; |
- |
- // The text continues on the next line only if the last text box is not on this line and |
- // none of the boxes on this line have a larger start offset. |
- |
- bool continuesOnNextLine = true; |
- InlineBox* otherBox = box; |
- while (continuesOnNextLine) { |
- otherBox = otherBox->nextLeafChild(); |
- if (!otherBox) |
- break; |
- if (otherBox == lastTextBox || (otherBox->layoutObject() == textLayoutObject && toInlineTextBox(otherBox)->start() >= textOffset)) |
- continuesOnNextLine = false; |
- } |
- |
- otherBox = box; |
- while (continuesOnNextLine) { |
- otherBox = otherBox->prevLeafChild(); |
- if (!otherBox) |
- break; |
- if (otherBox == lastTextBox || (otherBox->layoutObject() == textLayoutObject && toInlineTextBox(otherBox)->start() >= textOffset)) |
- continuesOnNextLine = false; |
- } |
- |
- if (continuesOnNextLine) |
- return currentPos.computePosition(); |
- } |
- } |
- } |
- |
- return lastVisible.deprecatedComputePosition(); |
+ return mostBackwardCaretPosition(*this, rule); |
} |
static int boundingBoxLogicalHeight(LayoutObject *o, const IntRect &rect) |