Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(796)

Unified Diff: Source/core/editing/VisibleUnits.cpp

Issue 1299323006: [Editing][CodeHealth] Move Position::up/downstream implementation to VisibleUnit.cpp (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « Source/core/editing/VisibleUnits.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: Source/core/editing/VisibleUnits.cpp
diff --git a/Source/core/editing/VisibleUnits.cpp b/Source/core/editing/VisibleUnits.cpp
index fed0969f7d5879e02880fc2c814399da8de13ade..8226d7bdebceb890baf65f5a917c65f5eabcd0ce 100644
--- a/Source/core/editing/VisibleUnits.cpp
+++ b/Source/core/editing/VisibleUnits.cpp
@@ -36,6 +36,7 @@
#include "core/editing/EditingUtilities.h"
#include "core/editing/FrameSelection.h"
#include "core/editing/Position.h"
+#include "core/editing/PositionIterator.h"
#include "core/editing/RenderedPosition.h"
#include "core/editing/TextAffinity.h"
#include "core/editing/VisiblePosition.h"
@@ -1602,4 +1603,308 @@ bool rendersInDifferentPosition(const Position& position1, const Position& posit
return true;
}
+// 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();
+}
+
+template <typename Strategy>
+static PositionAlgorithm<Strategy> mostForwardCaretPosition(const PositionAlgorithm<Strategy>& position, EditingBoundaryCrossingRule rule)
+{
+ TRACE_EVENT0("blink", "Position::upstream");
+
+ Node* startNode = position.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(position.isAfterAnchor() ? PositionAlgorithm<Strategy>::editingPositionOf(position.anchorNode(), Strategy::caretMaxOffset(*position.anchorNode())) : position);
+ 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 PositionAlgorithm<Strategy>::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();
+}
+
+Position mostForwardCaretPosition(const Position& position, EditingBoundaryCrossingRule rule)
+{
+ return mostForwardCaretPosition<EditingStrategy>(position, rule);
+}
+
+PositionInComposedTree mostForwardCaretPosition(const PositionInComposedTree& position, EditingBoundaryCrossingRule rule)
+{
+ return mostForwardCaretPosition<EditingInComposedTreeStrategy>(position, rule);
+}
+
+template <typename Strategy>
+PositionAlgorithm<Strategy> mostBackwardCaretPosition(const PositionAlgorithm<Strategy>& position, EditingBoundaryCrossingRule rule)
+{
+ TRACE_EVENT0("blink", "Position::downstream");
+
+ Node* startNode = position.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(position.isAfterAnchor() ? PositionAlgorithm<Strategy>::editingPositionOf(position.anchorNode(), Strategy::caretMaxOffset(*position.anchorNode())) : position);
+ 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 PositionAlgorithm<Strategy>::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();
+}
+
+Position mostBackwardCaretPosition(const Position& position, EditingBoundaryCrossingRule rule)
+{
+ return mostBackwardCaretPosition<EditingStrategy>(position, rule);
+}
+
+PositionInComposedTree mostBackwardCaretPosition(const PositionInComposedTree& position, EditingBoundaryCrossingRule rule)
+{
+ return mostBackwardCaretPosition<EditingInComposedTreeStrategy>(position, rule);
+}
+
}
« no previous file with comments | « Source/core/editing/VisibleUnits.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698