blob: d56b7aefc78bf1a16b4a16a792fff00372b5e5db [file] [log] [blame]
/*
* 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/SelectionModifier.h"
#include "core/editing/EditingUtilities.h"
#include "core/editing/InlineBoxPosition.h"
#include "core/editing/InlineBoxTraversal.h"
#include "core/editing/VisiblePosition.h"
#include "core/editing/VisibleUnits.h"
#include "core/layout/api/LineLayoutAPIShim.h"
#include "core/layout/api/LineLayoutItem.h"
#include "core/layout/line/InlineTextBox.h"
#include "core/layout/line/RootInlineBox.h"
namespace blink {
namespace {
// The traversal strategy for |LeftPositionOf()|.
template <typename Strategy>
struct TraversalLeft {
STATIC_ONLY(TraversalLeft);
static InlineBox* BackwardLeafChildOf(const InlineBox& box) {
return box.NextLeafChild();
}
static int CaretEndOffsetOf(const InlineBox& box) {
return box.CaretRightmostOffset();
}
static int CaretMinOffsetOf(TextDirection direction, const InlineBox& box) {
if (direction == TextDirection::kLtr)
return box.CaretMinOffset();
return box.CaretMaxOffset();
}
static int CaretStartOffsetOf(const InlineBox& box) {
return box.CaretLeftmostOffset();
}
static InlineBox* FindBackwardBidiRun(const InlineBox& box,
unsigned bidi_level) {
return InlineBoxTraversal::FindRightBidiRun(box, bidi_level);
}
static InlineBox* FindBackwardBoundaryOfEntireBidiRun(const InlineBox& box,
unsigned bidi_level) {
return InlineBoxTraversal::FindRightBoundaryOfEntireBidiRun(box,
bidi_level);
}
static InlineBox* FindForwardBidiRun(const InlineBox& box,
unsigned bidi_level) {
return InlineBoxTraversal::FindLeftBidiRun(box, bidi_level);
}
static InlineBox* FindForwardBoundaryOfEntireBidiRun(const InlineBox& box,
unsigned bidi_level) {
return InlineBoxTraversal::FindLeftBoundaryOfEntireBidiRun(box, bidi_level);
}
static int ForwardGraphemeBoundaryOf(TextDirection direction,
const Node& node,
int offset) {
if (direction == TextDirection::kLtr)
return PreviousGraphemeBoundaryOf(node, offset);
return NextGraphemeBoundaryOf(node, offset);
}
static InlineBox* ForwardLeafChildOf(const InlineBox& box) {
return box.PrevLeafChild();
}
static InlineBox* ForwardLeafChildIgnoringLineBreakOf(const InlineBox& box) {
return box.PrevLeafChildIgnoringLineBreak();
}
static PositionTemplate<Strategy> ForwardVisuallyDistinctCandidateOf(
TextDirection direction,
const PositionTemplate<Strategy>& position) {
if (direction == TextDirection::kLtr)
return PreviousVisuallyDistinctCandidate(position);
return NextVisuallyDistinctCandidate(position);
}
static VisiblePositionTemplate<Strategy> HonorEditingBoundary(
TextDirection direction,
const VisiblePositionTemplate<Strategy>& visible_position,
const PositionTemplate<Strategy>& anchor) {
if (direction == TextDirection::kLtr)
return HonorEditingBoundaryAtOrBefore(visible_position, anchor);
return HonorEditingBoundaryAtOrAfter(visible_position, anchor);
}
static Node* LogicalStartBoxOf(TextDirection direction,
const InlineBox& box,
InlineBox*& result_box) {
if (direction == TextDirection::kLtr)
return box.Root().GetLogicalStartBoxWithNode(result_box);
return box.Root().GetLogicalEndBoxWithNode(result_box);
}
static bool IsOvershot(int offset, const InlineBox& box) {
if (box.IsLeftToRightDirection())
return offset < box.CaretMinOffset();
return offset > box.CaretMaxOffset();
}
};
// The traversal strategy for |RightPositionOf()|.
template <typename Strategy>
struct TraversalRight {
STATIC_ONLY(TraversalRight);
static InlineBox* BackwardLeafChildOf(const InlineBox& box) {
return box.PrevLeafChild();
}
static int CaretEndOffsetOf(const InlineBox& box) {
return box.CaretLeftmostOffset();
}
static int CaretMinOffsetOf(TextDirection direction, const InlineBox& box) {
if (direction == TextDirection::kLtr)
return box.CaretMaxOffset();
return box.CaretMinOffset();
}
static int CaretStartOffsetOf(const InlineBox& box) {
return box.CaretRightmostOffset();
}
static InlineBox* FindBackwardBidiRun(const InlineBox& box,
unsigned bidi_level) {
return InlineBoxTraversal::FindLeftBidiRun(box, bidi_level);
}
static InlineBox* FindBackwardBoundaryOfEntireBidiRun(const InlineBox& box,
unsigned bidi_level) {
return InlineBoxTraversal::FindLeftBoundaryOfEntireBidiRun(box, bidi_level);
}
static InlineBox* FindForwardBidiRun(const InlineBox& box,
unsigned bidi_level) {
return InlineBoxTraversal::FindRightBidiRun(box, bidi_level);
}
static InlineBox* FindForwardBoundaryOfEntireBidiRun(const InlineBox& box,
unsigned bidi_level) {
return InlineBoxTraversal::FindRightBoundaryOfEntireBidiRun(box,
bidi_level);
}
static int ForwardGraphemeBoundaryOf(TextDirection direction,
const Node& node,
int offset) {
if (direction == TextDirection::kLtr)
return NextGraphemeBoundaryOf(node, offset);
return PreviousGraphemeBoundaryOf(node, offset);
}
static InlineBox* ForwardLeafChildOf(const InlineBox& box) {
return box.NextLeafChild();
}
static InlineBox* ForwardLeafChildIgnoringLineBreakOf(const InlineBox& box) {
return box.NextLeafChildIgnoringLineBreak();
}
static PositionTemplate<Strategy> ForwardVisuallyDistinctCandidateOf(
TextDirection direction,
const PositionTemplate<Strategy>& position) {
if (direction == TextDirection::kLtr)
return NextVisuallyDistinctCandidate(position);
return PreviousVisuallyDistinctCandidate(position);
}
static VisiblePositionTemplate<Strategy> HonorEditingBoundary(
TextDirection direction,
const VisiblePositionTemplate<Strategy>& visible_position,
const PositionTemplate<Strategy>& anchor) {
if (direction == TextDirection::kLtr)
return HonorEditingBoundaryAtOrAfter(visible_position, anchor);
return HonorEditingBoundaryAtOrBefore(visible_position, anchor);
}
static Node* LogicalStartBoxOf(TextDirection direction,
const InlineBox& box,
InlineBox*& result_box) {
if (direction == TextDirection::kLtr)
return box.Root().GetLogicalEndBoxWithNode(result_box);
return box.Root().GetLogicalStartBoxWithNode(result_box);
}
static bool IsOvershot(int offset, const InlineBox& box) {
if (box.IsLeftToRightDirection())
return offset > box.CaretMaxOffset();
return offset < box.CaretMinOffset();
}
};
// TODO(yosin): We should rename local variables and comments in
// |TraverseInternalAlgorithm()| to generic name based on |Traversal| instead of
// assuming right-to-left traversal.
template <typename Strategy, typename Traversal>
static PositionTemplate<Strategy> TraverseInternalAlgorithm(
const VisiblePositionTemplate<Strategy>& visible_position) {
DCHECK(visible_position.IsValid()) << visible_position;
const PositionTemplate<Strategy> deep_position =
visible_position.DeepEquivalent();
PositionTemplate<Strategy> p = deep_position;
if (p.IsNull())
return PositionTemplate<Strategy>();
const PositionTemplate<Strategy> downstream_start =
MostForwardCaretPosition(p);
const TextDirection primary_direction = PrimaryDirectionOf(*p.AnchorNode());
const TextAffinity affinity = visible_position.Affinity();
while (true) {
InlineBoxPosition box_position = ComputeInlineBoxPosition(
PositionWithAffinityTemplate<Strategy>(p, affinity), primary_direction);
const InlineBox* box = box_position.inline_box;
int offset = box_position.offset_in_box;
if (!box) {
return Traversal::ForwardVisuallyDistinctCandidateOf(primary_direction,
deep_position);
}
LineLayoutItem line_layout_item = box->GetLineLayoutItem();
while (true) {
if ((line_layout_item.IsAtomicInlineLevel() || line_layout_item.IsBR()) &&
offset == Traversal::CaretEndOffsetOf(*box)) {
return Traversal::ForwardVisuallyDistinctCandidateOf(box->Direction(),
deep_position);
}
if (!line_layout_item.GetNode()) {
box = Traversal::ForwardLeafChildOf(*box);
if (!box) {
return Traversal::ForwardVisuallyDistinctCandidateOf(
primary_direction, deep_position);
}
line_layout_item = box->GetLineLayoutItem();
offset = Traversal::CaretEndOffsetOf(*box);
continue;
}
offset = Traversal::ForwardGraphemeBoundaryOf(
box->Direction(), *line_layout_item.GetNode(), offset);
const int caret_min_offset = box->CaretMinOffset();
const int caret_max_offset = box->CaretMaxOffset();
if (offset > caret_min_offset && offset < caret_max_offset)
break;
if (Traversal::IsOvershot(offset, *box)) {
// Overshot to the left.
InlineBox* const prev_box =
Traversal::ForwardLeafChildIgnoringLineBreakOf(*box);
if (!prev_box) {
const PositionTemplate<Strategy>& position_on_left =
Traversal::ForwardVisuallyDistinctCandidateOf(
primary_direction, visible_position.DeepEquivalent());
if (position_on_left.IsNull())
return PositionTemplate<Strategy>();
const InlineBox* box_on_left =
ComputeInlineBoxPosition(PositionWithAffinityTemplate<Strategy>(
position_on_left, affinity),
primary_direction)
.inline_box;
if (box_on_left && box_on_left->Root() == box->Root())
return PositionTemplate<Strategy>();
return position_on_left;
}
// Reposition at the other logical position corresponding to our
// edge's visual position and go for another round.
box = prev_box;
line_layout_item = box->GetLineLayoutItem();
offset = Traversal::CaretEndOffsetOf(*prev_box);
continue;
}
DCHECK_EQ(offset, Traversal::CaretStartOffsetOf(*box));
unsigned char level = box->BidiLevel();
InlineBox* prev_box = Traversal::ForwardLeafChildOf(*box);
if (box->Direction() == primary_direction) {
if (!prev_box) {
InlineBox* logical_start = nullptr;
if (Traversal::LogicalStartBoxOf(primary_direction, *box,
logical_start)) {
box = logical_start;
line_layout_item = box->GetLineLayoutItem();
offset = Traversal::CaretMinOffsetOf(primary_direction, *box);
}
break;
}
if (prev_box->BidiLevel() >= level)
break;
level = prev_box->BidiLevel();
InlineBox* const next_box = Traversal::FindBackwardBidiRun(*box, level);
if (next_box && next_box->BidiLevel() == level)
break;
box = prev_box;
line_layout_item = box->GetLineLayoutItem();
offset = Traversal::CaretEndOffsetOf(*box);
if (box->Direction() == primary_direction)
break;
continue;
}
while (prev_box && !prev_box->GetLineLayoutItem().GetNode())
prev_box = Traversal::ForwardLeafChildOf(*prev_box);
if (prev_box) {
box = prev_box;
line_layout_item = box->GetLineLayoutItem();
offset = Traversal::CaretEndOffsetOf(*box);
if (box->BidiLevel() > level) {
prev_box = Traversal::FindForwardBidiRun(*prev_box, level);
if (!prev_box || prev_box->BidiLevel() < level)
continue;
}
break;
}
// Trailing edge of a secondary run. Set to the leading edge of
// the entire run.
while (true) {
box = Traversal::FindBackwardBoundaryOfEntireBidiRun(*box, level);
if (box->BidiLevel() == level)
break;
level = box->BidiLevel();
box = Traversal::FindForwardBoundaryOfEntireBidiRun(*box, level);
if (box->BidiLevel() == level)
break;
level = box->BidiLevel();
}
line_layout_item = box->GetLineLayoutItem();
offset = Traversal::CaretMinOffsetOf(primary_direction, *box);
break;
}
p = PositionTemplate<Strategy>::EditingPositionOf(
line_layout_item.GetNode(), offset);
if ((IsVisuallyEquivalentCandidate(p) &&
MostForwardCaretPosition(p) != downstream_start) ||
p.AtStartOfTree() || p.AtEndOfTree())
return p;
DCHECK_NE(p, deep_position);
}
}
template <typename Strategy, typename Traversal>
VisiblePositionTemplate<Strategy> TraverseAlgorithm(
const VisiblePositionTemplate<Strategy>& visible_position) {
DCHECK(visible_position.IsValid()) << visible_position;
const PositionTemplate<Strategy> pos =
TraverseInternalAlgorithm<Strategy, Traversal>(visible_position);
// TODO(yosin) Why can't we move left from the last position in a tree?
if (pos.AtStartOfTree() || pos.AtEndOfTree())
return VisiblePositionTemplate<Strategy>();
const VisiblePositionTemplate<Strategy> result = CreateVisiblePosition(pos);
DCHECK_NE(result.DeepEquivalent(), visible_position.DeepEquivalent());
return Traversal::HonorEditingBoundary(
DirectionOfEnclosingBlockOf(result.DeepEquivalent()), result,
visible_position.DeepEquivalent());
}
} // namespace
VisiblePosition LeftPositionOf(const VisiblePosition& visible_position) {
return TraverseAlgorithm<EditingStrategy, TraversalLeft<EditingStrategy>>(
visible_position);
}
VisiblePositionInFlatTree LeftPositionOf(
const VisiblePositionInFlatTree& visible_position) {
return TraverseAlgorithm<EditingInFlatTreeStrategy,
TraversalLeft<EditingInFlatTreeStrategy>>(
visible_position);
}
VisiblePosition RightPositionOf(const VisiblePosition& visible_position) {
return TraverseAlgorithm<EditingStrategy, TraversalRight<EditingStrategy>>(
visible_position);
}
VisiblePositionInFlatTree RightPositionOf(
const VisiblePositionInFlatTree& visible_position) {
return TraverseAlgorithm<EditingInFlatTreeStrategy,
TraversalRight<EditingInFlatTreeStrategy>>(
visible_position);
}
} // namespace blink