Index: third_party/WebKit/Source/core/editing/VisibleUnitsLine.cpp |
diff --git a/third_party/WebKit/Source/core/editing/VisibleUnitsLine.cpp b/third_party/WebKit/Source/core/editing/VisibleUnitsLine.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..624a66267d6c0b6620ffdfc98e00facc41445f91 |
--- /dev/null |
+++ b/third_party/WebKit/Source/core/editing/VisibleUnitsLine.cpp |
@@ -0,0 +1,689 @@ |
+/* |
+ * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights |
+ * reserved. |
+ * |
+ * Redistribution and use in source and binary forms, with or without |
+ * modification, are permitted provided that the following conditions |
+ * are met: |
+ * 1. Redistributions of source code must retain the above copyright |
+ * notice, this list of conditions and the following disclaimer. |
+ * 2. Redistributions in binary form must reproduce the above copyright |
+ * notice, this list of conditions and the following disclaimer in the |
+ * documentation and/or other materials provided with the distribution. |
+ * |
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY |
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR |
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+ */ |
+ |
+// Copyright 2017 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "core/editing/VisibleUnits.h" |
+ |
+#include "core/editing/EditingUtilities.h" |
+#include "core/editing/RenderedPosition.h" |
+#include "core/layout/api/LineLayoutBlockFlow.h" |
+#include "core/layout/line/InlineTextBox.h" |
+#include "core/layout/line/RootInlineBox.h" |
+ |
+namespace blink { |
+ |
+namespace { |
+ |
+bool HasEditableStyle(const Node& node, EditableType editable_type) { |
+ if (editable_type == kHasEditableAXRole) { |
+ if (AXObjectCache* cache = node.GetDocument().ExistingAXObjectCache()) { |
+ if (cache->RootAXEditableElement(&node)) |
+ return true; |
+ } |
+ } |
+ |
+ return HasEditableStyle(node); |
+} |
+ |
+Element* RootEditableElement(const Node& node, EditableType editable_type) { |
+ if (editable_type == kHasEditableAXRole) { |
+ if (AXObjectCache* cache = node.GetDocument().ExistingAXObjectCache()) |
+ return const_cast<Element*>(cache->RootAXEditableElement(&node)); |
+ } |
+ |
+ return RootEditableElement(node); |
+} |
+ |
+Element* RootAXEditableElementOf(const Position& position) { |
+ Node* node = position.ComputeContainerNode(); |
+ if (!node) |
+ return nullptr; |
+ |
+ if (IsDisplayInsideTable(node)) |
+ node = node->parentNode(); |
+ |
+ return RootEditableElement(*node, kHasEditableAXRole); |
+} |
+ |
+bool HasAXEditableStyle(const Node& node) { |
+ return HasEditableStyle(node, kHasEditableAXRole); |
+} |
+ |
+ContainerNode* HighestEditableRoot(const Position& position, |
+ EditableType editable_type) { |
+ if (editable_type == kHasEditableAXRole) { |
+ return HighestEditableRoot(position, RootAXEditableElementOf, |
+ HasAXEditableStyle); |
+ } |
+ |
+ return HighestEditableRoot(position); |
+} |
+ |
+Node* PreviousLeafWithSameEditability(Node* node, EditableType editable_type) { |
+ bool editable = HasEditableStyle(*node, editable_type); |
+ node = PreviousAtomicLeafNode(*node); |
+ while (node) { |
+ if (editable == HasEditableStyle(*node, editable_type)) |
+ return node; |
+ node = PreviousAtomicLeafNode(*node); |
+ } |
+ return nullptr; |
+} |
+ |
+Node* NextLeafWithSameEditability( |
+ Node* node, |
+ EditableType editable_type = kContentIsEditable) { |
+ if (!node) |
+ return nullptr; |
+ |
+ bool editable = HasEditableStyle(*node, editable_type); |
+ node = NextAtomicLeafNode(*node); |
+ while (node) { |
+ if (editable == HasEditableStyle(*node, editable_type)) |
+ return node; |
+ node = NextAtomicLeafNode(*node); |
+ } |
+ return nullptr; |
+} |
+ |
+enum LineEndpointComputationMode { kUseLogicalOrdering, kUseInlineBoxOrdering }; |
+template <typename Strategy> |
+PositionWithAffinityTemplate<Strategy> StartPositionForLine( |
+ const PositionWithAffinityTemplate<Strategy>& c, |
+ LineEndpointComputationMode mode) { |
+ if (c.IsNull()) |
+ return PositionWithAffinityTemplate<Strategy>(); |
+ |
+ RootInlineBox* root_box = |
+ RenderedPosition(c.GetPosition(), c.Affinity()).RootBox(); |
+ if (!root_box) { |
+ // There are VisiblePositions at offset 0 in blocks without |
+ // RootInlineBoxes, like empty editable blocks and bordered blocks. |
+ PositionTemplate<Strategy> p = c.GetPosition(); |
+ if (p.AnchorNode()->GetLayoutObject() && |
+ p.AnchorNode()->GetLayoutObject()->IsLayoutBlock() && |
+ !p.ComputeEditingOffset()) |
+ return c; |
+ |
+ return PositionWithAffinityTemplate<Strategy>(); |
+ } |
+ |
+ Node* start_node; |
+ InlineBox* start_box; |
+ if (mode == kUseLogicalOrdering) { |
+ start_node = root_box->GetLogicalStartBoxWithNode(start_box); |
+ if (!start_node) |
+ return PositionWithAffinityTemplate<Strategy>(); |
+ } else { |
+ // Generated content (e.g. list markers and CSS :before and :after |
+ // pseudoelements) have no corresponding DOM element, and so cannot be |
+ // represented by a VisiblePosition. Use whatever follows instead. |
+ start_box = root_box->FirstLeafChild(); |
+ while (true) { |
+ if (!start_box) |
+ return PositionWithAffinityTemplate<Strategy>(); |
+ |
+ start_node = start_box->GetLineLayoutItem().NonPseudoNode(); |
+ if (start_node) |
+ break; |
+ |
+ start_box = start_box->NextLeafChild(); |
+ } |
+ } |
+ |
+ return PositionWithAffinityTemplate<Strategy>( |
+ start_node->IsTextNode() |
+ ? PositionTemplate<Strategy>(ToText(start_node), |
+ ToInlineTextBox(start_box)->Start()) |
+ : PositionTemplate<Strategy>::BeforeNode(start_node)); |
+} |
+ |
+template <typename Strategy> |
+PositionWithAffinityTemplate<Strategy> StartOfLineAlgorithm( |
+ const PositionWithAffinityTemplate<Strategy>& c) { |
+ // TODO: this is the current behavior that might need to be fixed. |
+ // Please refer to https://bugs.webkit.org/show_bug.cgi?id=49107 for detail. |
+ PositionWithAffinityTemplate<Strategy> vis_pos = |
+ StartPositionForLine(c, kUseInlineBoxOrdering); |
+ return HonorEditingBoundaryAtOrBefore(vis_pos, c.GetPosition()); |
+} |
+ |
+PositionWithAffinity StartOfLine(const PositionWithAffinity& current_position) { |
+ return StartOfLineAlgorithm<EditingStrategy>(current_position); |
+} |
+ |
+PositionInFlatTreeWithAffinity StartOfLine( |
+ const PositionInFlatTreeWithAffinity& current_position) { |
+ return StartOfLineAlgorithm<EditingInFlatTreeStrategy>(current_position); |
+} |
+ |
+LayoutPoint AbsoluteLineDirectionPointToLocalPointInBlock( |
+ RootInlineBox* root, |
+ LayoutUnit line_direction_point) { |
+ DCHECK(root); |
+ LineLayoutBlockFlow containing_block = root->Block(); |
+ FloatPoint absolute_block_point = |
+ containing_block.LocalToAbsolute(FloatPoint()); |
+ if (containing_block.HasOverflowClip()) |
+ absolute_block_point -= FloatSize(containing_block.ScrolledContentOffset()); |
+ |
+ if (root->Block().IsHorizontalWritingMode()) { |
+ return LayoutPoint( |
+ LayoutUnit(line_direction_point - absolute_block_point.X()), |
+ root->BlockDirectionPointInLine()); |
+ } |
+ |
+ return LayoutPoint( |
+ root->BlockDirectionPointInLine(), |
+ LayoutUnit(line_direction_point - absolute_block_point.Y())); |
+} |
+ |
+} // namespace |
+ |
+// FIXME: consolidate with code in previousLinePosition. |
+Position PreviousRootInlineBoxCandidatePosition( |
+ Node* node, |
+ const VisiblePosition& visible_position, |
+ EditableType editable_type) { |
+ DCHECK(visible_position.IsValid()) << visible_position; |
+ ContainerNode* highest_root = |
+ HighestEditableRoot(visible_position.DeepEquivalent(), editable_type); |
+ Node* previous_node = PreviousLeafWithSameEditability(node, editable_type); |
+ |
+ while (previous_node && |
+ (!previous_node->GetLayoutObject() || |
+ InSameLine( |
+ CreateVisiblePosition(FirstPositionInOrBeforeNode(previous_node)), |
+ visible_position))) { |
+ previous_node = |
+ PreviousLeafWithSameEditability(previous_node, editable_type); |
+ } |
+ |
+ while (previous_node && !previous_node->IsShadowRoot()) { |
+ if (HighestEditableRoot(FirstPositionInOrBeforeNode(previous_node), |
+ editable_type) != highest_root) |
+ break; |
+ |
+ Position pos = isHTMLBRElement(*previous_node) |
+ ? Position::BeforeNode(previous_node) |
+ : Position::EditingPositionOf( |
+ previous_node, CaretMaxOffset(previous_node)); |
+ |
+ if (IsVisuallyEquivalentCandidate(pos)) |
+ return pos; |
+ |
+ previous_node = |
+ PreviousLeafWithSameEditability(previous_node, editable_type); |
+ } |
+ return Position(); |
+} |
+ |
+Position NextRootInlineBoxCandidatePosition( |
+ Node* node, |
+ const VisiblePosition& visible_position, |
+ EditableType editable_type) { |
+ DCHECK(visible_position.IsValid()) << visible_position; |
+ ContainerNode* highest_root = |
+ HighestEditableRoot(visible_position.DeepEquivalent(), editable_type); |
+ Node* next_node = NextLeafWithSameEditability(node, editable_type); |
+ while (next_node && (!next_node->GetLayoutObject() || |
+ InSameLine(CreateVisiblePosition( |
+ FirstPositionInOrBeforeNode(next_node)), |
+ visible_position))) |
+ next_node = NextLeafWithSameEditability(next_node, kContentIsEditable); |
+ |
+ while (next_node && !next_node->IsShadowRoot()) { |
+ if (HighestEditableRoot(FirstPositionInOrBeforeNode(next_node), |
+ editable_type) != highest_root) |
+ break; |
+ |
+ Position pos; |
+ pos = Position::EditingPositionOf(next_node, CaretMinOffset(next_node)); |
+ |
+ if (IsVisuallyEquivalentCandidate(pos)) |
+ return pos; |
+ |
+ next_node = NextLeafWithSameEditability(next_node, editable_type); |
+ } |
+ return Position(); |
+} |
+ |
+// FIXME: Rename this function to reflect the fact it ignores bidi levels. |
+VisiblePosition StartOfLine(const VisiblePosition& current_position) { |
+ DCHECK(current_position.IsValid()) << current_position; |
+ return CreateVisiblePosition( |
+ StartOfLine(current_position.ToPositionWithAffinity())); |
+} |
+ |
+VisiblePositionInFlatTree StartOfLine( |
+ const VisiblePositionInFlatTree& current_position) { |
+ DCHECK(current_position.IsValid()) << current_position; |
+ return CreateVisiblePosition( |
+ StartOfLine(current_position.ToPositionWithAffinity())); |
+} |
+ |
+template <typename Strategy> |
+static PositionWithAffinityTemplate<Strategy> LogicalStartOfLineAlgorithm( |
+ const PositionWithAffinityTemplate<Strategy>& c) { |
+ // TODO: this is the current behavior that might need to be fixed. |
+ // Please refer to https://bugs.webkit.org/show_bug.cgi?id=49107 for detail. |
+ PositionWithAffinityTemplate<Strategy> vis_pos = |
+ StartPositionForLine(c, kUseLogicalOrdering); |
+ |
+ if (ContainerNode* editable_root = HighestEditableRoot(c.GetPosition())) { |
+ if (!editable_root->contains( |
+ vis_pos.GetPosition().ComputeContainerNode())) { |
+ return PositionWithAffinityTemplate<Strategy>( |
+ PositionTemplate<Strategy>::FirstPositionInNode(editable_root)); |
+ } |
+ } |
+ |
+ return HonorEditingBoundaryAtOrBefore(vis_pos, c.GetPosition()); |
+} |
+ |
+VisiblePosition LogicalStartOfLine(const VisiblePosition& current_position) { |
+ DCHECK(current_position.IsValid()) << current_position; |
+ return CreateVisiblePosition(LogicalStartOfLineAlgorithm<EditingStrategy>( |
+ current_position.ToPositionWithAffinity())); |
+} |
+ |
+VisiblePositionInFlatTree LogicalStartOfLine( |
+ const VisiblePositionInFlatTree& current_position) { |
+ DCHECK(current_position.IsValid()) << current_position; |
+ return CreateVisiblePosition( |
+ LogicalStartOfLineAlgorithm<EditingInFlatTreeStrategy>( |
+ current_position.ToPositionWithAffinity())); |
+} |
+ |
+template <typename Strategy> |
+static VisiblePositionTemplate<Strategy> EndPositionForLine( |
+ const VisiblePositionTemplate<Strategy>& c, |
+ LineEndpointComputationMode mode) { |
+ DCHECK(c.IsValid()) << c; |
+ if (c.IsNull()) |
+ return VisiblePositionTemplate<Strategy>(); |
+ |
+ RootInlineBox* root_box = RenderedPosition(c).RootBox(); |
+ if (!root_box) { |
+ // There are VisiblePositions at offset 0 in blocks without |
+ // RootInlineBoxes, like empty editable blocks and bordered blocks. |
+ const PositionTemplate<Strategy> p = c.DeepEquivalent(); |
+ if (p.AnchorNode()->GetLayoutObject() && |
+ p.AnchorNode()->GetLayoutObject()->IsLayoutBlock() && |
+ !p.ComputeEditingOffset()) |
+ return c; |
+ return VisiblePositionTemplate<Strategy>(); |
+ } |
+ |
+ Node* end_node; |
+ InlineBox* end_box; |
+ if (mode == kUseLogicalOrdering) { |
+ end_node = root_box->GetLogicalEndBoxWithNode(end_box); |
+ if (!end_node) |
+ return VisiblePositionTemplate<Strategy>(); |
+ } else { |
+ // Generated content (e.g. list markers and CSS :before and :after |
+ // pseudo elements) have no corresponding DOM element, and so cannot be |
+ // represented by a VisiblePosition. Use whatever precedes instead. |
+ end_box = root_box->LastLeafChild(); |
+ while (true) { |
+ if (!end_box) |
+ return VisiblePositionTemplate<Strategy>(); |
+ |
+ end_node = end_box->GetLineLayoutItem().NonPseudoNode(); |
+ if (end_node) |
+ break; |
+ |
+ end_box = end_box->PrevLeafChild(); |
+ } |
+ } |
+ |
+ PositionTemplate<Strategy> pos; |
+ if (isHTMLBRElement(*end_node)) { |
+ pos = PositionTemplate<Strategy>::BeforeNode(end_node); |
+ } else if (end_box->IsInlineTextBox() && end_node->IsTextNode()) { |
+ InlineTextBox* end_text_box = ToInlineTextBox(end_box); |
+ int end_offset = end_text_box->Start(); |
+ if (!end_text_box->IsLineBreak()) |
+ end_offset += end_text_box->Len(); |
+ pos = PositionTemplate<Strategy>(ToText(end_node), end_offset); |
+ } else { |
+ pos = PositionTemplate<Strategy>::AfterNode(end_node); |
+ } |
+ |
+ return CreateVisiblePosition(pos, VP_UPSTREAM_IF_POSSIBLE); |
+} |
+ |
+// TODO(yosin) Rename this function to reflect the fact it ignores bidi levels. |
+template <typename Strategy> |
+static VisiblePositionTemplate<Strategy> EndOfLineAlgorithm( |
+ const VisiblePositionTemplate<Strategy>& current_position) { |
+ DCHECK(current_position.IsValid()) << current_position; |
+ // TODO(yosin) this is the current behavior that might need to be fixed. |
+ // Please refer to https://bugs.webkit.org/show_bug.cgi?id=49107 for detail. |
+ VisiblePositionTemplate<Strategy> vis_pos = |
+ EndPositionForLine(current_position, kUseInlineBoxOrdering); |
+ |
+ // Make sure the end of line is at the same line as the given input |
+ // position. Else use the previous position to obtain end of line. This |
+ // condition happens when the input position is before the space character |
+ // at the end of a soft-wrapped non-editable line. In this scenario, |
+ // |endPositionForLine()| would incorrectly hand back a position in the next |
+ // line instead. This fix is to account for the discrepancy between lines |
+ // with "webkit-line-break:after-white-space" style versus lines without |
+ // that style, which would break before a space by default. |
+ if (!InSameLine(current_position, vis_pos)) { |
+ vis_pos = PreviousPositionOf(current_position); |
+ if (vis_pos.IsNull()) |
+ return VisiblePositionTemplate<Strategy>(); |
+ vis_pos = EndPositionForLine(vis_pos, kUseInlineBoxOrdering); |
+ } |
+ |
+ return HonorEditingBoundaryAtOrAfter(vis_pos, |
+ current_position.DeepEquivalent()); |
+} |
+ |
+// TODO(yosin) Rename this function to reflect the fact it ignores bidi levels. |
+VisiblePosition EndOfLine(const VisiblePosition& current_position) { |
+ return EndOfLineAlgorithm<EditingStrategy>(current_position); |
+} |
+ |
+VisiblePositionInFlatTree EndOfLine( |
+ const VisiblePositionInFlatTree& current_position) { |
+ return EndOfLineAlgorithm<EditingInFlatTreeStrategy>(current_position); |
+} |
+ |
+template <typename Strategy> |
+static bool InSameLogicalLine(const VisiblePositionTemplate<Strategy>& a, |
+ const VisiblePositionTemplate<Strategy>& b) { |
+ DCHECK(a.IsValid()) << a; |
+ DCHECK(b.IsValid()) << b; |
+ return a.IsNotNull() && LogicalStartOfLine(a).DeepEquivalent() == |
+ LogicalStartOfLine(b).DeepEquivalent(); |
+} |
+ |
+template <typename Strategy> |
+static VisiblePositionTemplate<Strategy> LogicalEndOfLineAlgorithm( |
+ const VisiblePositionTemplate<Strategy>& current_position) { |
+ DCHECK(current_position.IsValid()) << current_position; |
+ // TODO(yosin) this is the current behavior that might need to be fixed. |
+ // Please refer to https://bugs.webkit.org/show_bug.cgi?id=49107 for detail. |
+ VisiblePositionTemplate<Strategy> vis_pos = |
+ EndPositionForLine(current_position, kUseLogicalOrdering); |
+ |
+ // Make sure the end of line is at the same line as the given input |
+ // position. For a wrapping line, the logical end position for the |
+ // not-last-2-lines might incorrectly hand back the logical beginning of the |
+ // next line. For example, |
+ // <div contenteditable dir="rtl" style="line-break:before-white-space">xyz |
+ // a xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz </div> |
+ // In this case, use the previous position of the computed logical end |
+ // position. |
+ if (!InSameLogicalLine(current_position, vis_pos)) |
+ vis_pos = PreviousPositionOf(vis_pos); |
+ |
+ if (ContainerNode* editable_root = |
+ HighestEditableRoot(current_position.DeepEquivalent())) { |
+ if (!editable_root->contains( |
+ vis_pos.DeepEquivalent().ComputeContainerNode())) { |
+ return CreateVisiblePosition( |
+ PositionTemplate<Strategy>::LastPositionInNode(editable_root)); |
+ } |
+ } |
+ |
+ return HonorEditingBoundaryAtOrAfter(vis_pos, |
+ current_position.DeepEquivalent()); |
+} |
+ |
+VisiblePosition LogicalEndOfLine(const VisiblePosition& current_position) { |
+ return LogicalEndOfLineAlgorithm<EditingStrategy>(current_position); |
+} |
+ |
+VisiblePositionInFlatTree LogicalEndOfLine( |
+ const VisiblePositionInFlatTree& current_position) { |
+ return LogicalEndOfLineAlgorithm<EditingInFlatTreeStrategy>(current_position); |
+} |
+ |
+template <typename Strategy> |
+static bool InSameLineAlgorithm( |
+ const PositionWithAffinityTemplate<Strategy>& position1, |
+ const PositionWithAffinityTemplate<Strategy>& position2) { |
+ if (position1.IsNull() || position2.IsNull()) |
+ return false; |
+ DCHECK_EQ(position1.GetDocument(), position2.GetDocument()); |
+ DCHECK(!position1.GetDocument()->NeedsLayoutTreeUpdate()); |
+ |
+ PositionWithAffinityTemplate<Strategy> start_of_line1 = |
+ StartOfLine(position1); |
+ PositionWithAffinityTemplate<Strategy> start_of_line2 = |
+ StartOfLine(position2); |
+ if (start_of_line1 == start_of_line2) |
+ return true; |
+ PositionTemplate<Strategy> canonicalized1 = |
+ CanonicalPositionOf(start_of_line1.GetPosition()); |
+ if (canonicalized1 == start_of_line2.GetPosition()) |
+ return true; |
+ return canonicalized1 == CanonicalPositionOf(start_of_line2.GetPosition()); |
+} |
+ |
+bool InSameLine(const PositionWithAffinity& a, const PositionWithAffinity& b) { |
+ return InSameLineAlgorithm<EditingStrategy>(a, b); |
+} |
+ |
+bool InSameLine(const PositionInFlatTreeWithAffinity& position1, |
+ const PositionInFlatTreeWithAffinity& position2) { |
+ return InSameLineAlgorithm<EditingInFlatTreeStrategy>(position1, position2); |
+} |
+ |
+bool InSameLine(const VisiblePosition& position1, |
+ const VisiblePosition& position2) { |
+ DCHECK(position1.IsValid()) << position1; |
+ DCHECK(position2.IsValid()) << position2; |
+ return InSameLine(position1.ToPositionWithAffinity(), |
+ position2.ToPositionWithAffinity()); |
+} |
+ |
+bool InSameLine(const VisiblePositionInFlatTree& position1, |
+ const VisiblePositionInFlatTree& position2) { |
+ DCHECK(position1.IsValid()) << position1; |
+ DCHECK(position2.IsValid()) << position2; |
+ return InSameLine(position1.ToPositionWithAffinity(), |
+ position2.ToPositionWithAffinity()); |
+} |
+ |
+template <typename Strategy> |
+static bool IsStartOfLineAlgorithm(const VisiblePositionTemplate<Strategy>& p) { |
+ DCHECK(p.IsValid()) << p; |
+ return p.IsNotNull() && p.DeepEquivalent() == StartOfLine(p).DeepEquivalent(); |
+} |
+ |
+bool IsStartOfLine(const VisiblePosition& p) { |
+ return IsStartOfLineAlgorithm<EditingStrategy>(p); |
+} |
+ |
+bool IsStartOfLine(const VisiblePositionInFlatTree& p) { |
+ return IsStartOfLineAlgorithm<EditingInFlatTreeStrategy>(p); |
+} |
+ |
+template <typename Strategy> |
+static bool IsEndOfLineAlgorithm(const VisiblePositionTemplate<Strategy>& p) { |
+ DCHECK(p.IsValid()) << p; |
+ return p.IsNotNull() && p.DeepEquivalent() == EndOfLine(p).DeepEquivalent(); |
+} |
+ |
+bool IsEndOfLine(const VisiblePosition& p) { |
+ return IsEndOfLineAlgorithm<EditingStrategy>(p); |
+} |
+ |
+bool IsEndOfLine(const VisiblePositionInFlatTree& p) { |
+ return IsEndOfLineAlgorithm<EditingInFlatTreeStrategy>(p); |
+} |
+ |
+template <typename Strategy> |
+static bool IsLogicalEndOfLineAlgorithm( |
+ const VisiblePositionTemplate<Strategy>& p) { |
+ DCHECK(p.IsValid()) << p; |
+ return p.IsNotNull() && |
+ p.DeepEquivalent() == LogicalEndOfLine(p).DeepEquivalent(); |
+} |
+ |
+bool IsLogicalEndOfLine(const VisiblePosition& p) { |
+ return IsLogicalEndOfLineAlgorithm<EditingStrategy>(p); |
+} |
+ |
+bool IsLogicalEndOfLine(const VisiblePositionInFlatTree& p) { |
+ return IsLogicalEndOfLineAlgorithm<EditingInFlatTreeStrategy>(p); |
+} |
+ |
+VisiblePosition PreviousLinePosition(const VisiblePosition& visible_position, |
+ LayoutUnit line_direction_point, |
+ EditableType editable_type) { |
+ DCHECK(visible_position.IsValid()) << visible_position; |
+ |
+ Position p = visible_position.DeepEquivalent(); |
+ Node* node = p.AnchorNode(); |
+ |
+ if (!node) |
+ return VisiblePosition(); |
+ |
+ LayoutObject* layout_object = node->GetLayoutObject(); |
+ if (!layout_object) |
+ return VisiblePosition(); |
+ |
+ RootInlineBox* root = nullptr; |
+ InlineBox* box = ComputeInlineBoxPosition(visible_position).inline_box; |
+ if (box) { |
+ root = box->Root().PrevRootBox(); |
+ // We want to skip zero height boxes. |
+ // This could happen in case it is a TrailingFloatsRootInlineBox. |
+ if (!root || !root->LogicalHeight() || !root->FirstLeafChild()) |
+ root = nullptr; |
+ } |
+ |
+ if (!root) { |
+ Position position = PreviousRootInlineBoxCandidatePosition( |
+ node, visible_position, editable_type); |
+ if (position.IsNotNull()) { |
+ RenderedPosition rendered_position((CreateVisiblePosition(position))); |
+ root = rendered_position.RootBox(); |
+ if (!root) |
+ return CreateVisiblePosition(position); |
+ } |
+ } |
+ |
+ if (root) { |
+ // FIXME: Can be wrong for multi-column layout and with transforms. |
+ LayoutPoint point_in_line = AbsoluteLineDirectionPointToLocalPointInBlock( |
+ root, line_direction_point); |
+ LineLayoutItem line_layout_item = |
+ root->ClosestLeafChildForPoint(point_in_line, IsEditablePosition(p)) |
+ ->GetLineLayoutItem(); |
+ Node* node = line_layout_item.GetNode(); |
+ if (node && EditingIgnoresContent(*node)) |
+ return VisiblePosition::InParentBeforeNode(*node); |
+ return CreateVisiblePosition( |
+ line_layout_item.PositionForPoint(point_in_line)); |
+ } |
+ |
+ // Could not find a previous line. This means we must already be on the first |
+ // line. Move to the start of the content in this block, which effectively |
+ // moves us to the start of the line we're on. |
+ Element* root_element = HasEditableStyle(*node, editable_type) |
+ ? RootEditableElement(*node, editable_type) |
+ : node->GetDocument().documentElement(); |
+ if (!root_element) |
+ return VisiblePosition(); |
+ return VisiblePosition::FirstPositionInNode(root_element); |
+} |
+ |
+VisiblePosition NextLinePosition(const VisiblePosition& visible_position, |
+ LayoutUnit line_direction_point, |
+ EditableType editable_type) { |
+ DCHECK(visible_position.IsValid()) << visible_position; |
+ |
+ Position p = visible_position.DeepEquivalent(); |
+ Node* node = p.AnchorNode(); |
+ |
+ if (!node) |
+ return VisiblePosition(); |
+ |
+ LayoutObject* layout_object = node->GetLayoutObject(); |
+ if (!layout_object) |
+ return VisiblePosition(); |
+ |
+ RootInlineBox* root = nullptr; |
+ InlineBox* box = ComputeInlineBoxPosition(visible_position).inline_box; |
+ if (box) { |
+ root = box->Root().NextRootBox(); |
+ // We want to skip zero height boxes. |
+ // This could happen in case it is a TrailingFloatsRootInlineBox. |
+ if (!root || !root->LogicalHeight() || !root->FirstLeafChild()) |
+ root = nullptr; |
+ } |
+ |
+ if (!root) { |
+ // FIXME: We need do the same in previousLinePosition. |
+ Node* child = NodeTraversal::ChildAt(*node, p.ComputeEditingOffset()); |
+ node = child ? child : &NodeTraversal::LastWithinOrSelf(*node); |
+ Position position = NextRootInlineBoxCandidatePosition( |
+ node, visible_position, editable_type); |
+ if (position.IsNotNull()) { |
+ RenderedPosition rendered_position((CreateVisiblePosition(position))); |
+ root = rendered_position.RootBox(); |
+ if (!root) |
+ return CreateVisiblePosition(position); |
+ } |
+ } |
+ |
+ if (root) { |
+ // FIXME: Can be wrong for multi-column layout and with transforms. |
+ LayoutPoint point_in_line = AbsoluteLineDirectionPointToLocalPointInBlock( |
+ root, line_direction_point); |
+ LineLayoutItem line_layout_item = |
+ root->ClosestLeafChildForPoint(point_in_line, IsEditablePosition(p)) |
+ ->GetLineLayoutItem(); |
+ Node* node = line_layout_item.GetNode(); |
+ if (node && EditingIgnoresContent(*node)) |
+ return VisiblePosition::InParentBeforeNode(*node); |
+ return CreateVisiblePosition( |
+ line_layout_item.PositionForPoint(point_in_line)); |
+ } |
+ |
+ // Could not find a next line. This means we must already be on the last line. |
+ // Move to the end of the content in this block, which effectively moves us |
+ // to the end of the line we're on. |
+ Element* root_element = HasEditableStyle(*node, editable_type) |
+ ? RootEditableElement(*node, editable_type) |
+ : node->GetDocument().documentElement(); |
+ if (!root_element) |
+ return VisiblePosition(); |
+ return VisiblePosition::LastPositionInNode(root_element); |
+} |
+ |
+} // namespace blink |