diff options
Diffstat (limited to 'chromium/third_party/blink/renderer/core/editing/selection_template.cc')
-rw-r--r-- | chromium/third_party/blink/renderer/core/editing/selection_template.cc | 438 |
1 files changed, 438 insertions, 0 deletions
diff --git a/chromium/third_party/blink/renderer/core/editing/selection_template.cc b/chromium/third_party/blink/renderer/core/editing/selection_template.cc new file mode 100644 index 00000000000..b0194fef85b --- /dev/null +++ b/chromium/third_party/blink/renderer/core/editing/selection_template.cc @@ -0,0 +1,438 @@ +// Copyright 2016 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 "third_party/blink/renderer/core/editing/selection_template.h" + +#include <ostream> // NOLINT +#include "third_party/blink/renderer/core/editing/ephemeral_range.h" +#include "third_party/blink/renderer/core/editing/position_with_affinity.h" +#include "third_party/blink/renderer/platform/wtf/assertions.h" + +namespace blink { + +template <typename Strategy> +SelectionTemplate<Strategy>::SelectionTemplate(const SelectionTemplate& other) + : base_(other.base_), + extent_(other.extent_), + affinity_(other.affinity_), + direction_(other.direction_) +#if DCHECK_IS_ON() + , + dom_tree_version_(other.dom_tree_version_) +#endif +{ + DCHECK(other.AssertValid()); +} + +template <typename Strategy> +SelectionTemplate<Strategy>::SelectionTemplate() = default; + +template <typename Strategy> +bool SelectionTemplate<Strategy>::operator==( + const SelectionTemplate& other) const { + DCHECK(AssertValid()); + DCHECK(other.AssertValid()); + if (IsNone()) + return other.IsNone(); + if (other.IsNone()) + return false; + DCHECK_EQ(base_.GetDocument(), other.GetDocument()) << *this << ' ' << other; + return base_ == other.base_ && extent_ == other.extent_ && + affinity_ == other.affinity_; +} + +template <typename Strategy> +bool SelectionTemplate<Strategy>::operator!=( + const SelectionTemplate& other) const { + return !operator==(other); +} + +template <typename Strategy> +void SelectionTemplate<Strategy>::Trace(blink::Visitor* visitor) { + visitor->Trace(base_); + visitor->Trace(extent_); +} + +template <typename Strategy> +PositionTemplate<Strategy> SelectionTemplate<Strategy>::Base() const { + DCHECK(AssertValid()); + DCHECK(!base_.IsOrphan()) << base_; + return base_; +} + +template <typename Strategy> +Document* SelectionTemplate<Strategy>::GetDocument() const { + DCHECK(AssertValid()); + return base_.GetDocument(); +} + +template <typename Strategy> +PositionTemplate<Strategy> SelectionTemplate<Strategy>::Extent() const { + DCHECK(AssertValid()); + DCHECK(!extent_.IsOrphan()) << extent_; + return extent_; +} + +template <typename Strategy> +bool SelectionTemplate<Strategy>::IsCaret() const { + return base_.IsNotNull() && base_ == extent_; +} + +template <typename Strategy> +bool SelectionTemplate<Strategy>::IsRange() const { + return base_ != extent_; +} + +template <typename Strategy> +bool SelectionTemplate<Strategy>::IsValidFor(const Document& document) const { + if (IsNone()) + return true; + return base_.IsValidFor(document) && extent_.IsValidFor(document); +} + +template <typename Strategy> +bool SelectionTemplate<Strategy>::AssertValidFor( + const Document& document) const { + if (!AssertValid()) + return false; + if (base_.IsNull()) + return true; + DCHECK_EQ(base_.GetDocument(), document) << *this; + return true; +} + +#if DCHECK_IS_ON() +template <typename Strategy> +bool SelectionTemplate<Strategy>::AssertValid() const { + if (base_.IsNull()) + return true; + DCHECK_EQ(base_.GetDocument()->DomTreeVersion(), dom_tree_version_) << *this; + DCHECK(!base_.IsOrphan()) << *this; + DCHECK(!extent_.IsOrphan()) << *this; + DCHECK_EQ(base_.GetDocument(), extent_.GetDocument()); + return true; +} +#else +template <typename Strategy> +bool SelectionTemplate<Strategy>::AssertValid() const { + return true; +} +#endif + +#ifndef NDEBUG +template <typename Strategy> +void SelectionTemplate<Strategy>::ShowTreeForThis() const { + if (base_.IsNull()) { + LOG(INFO) << "\nbase is null"; + return; + } + + LOG(INFO) << "\n" + << base_.AnchorNode() + ->ToMarkedTreeString(base_.AnchorNode(), "B", + extent_.AnchorNode(), "E") + .Utf8() + .data() + << "base: " << base_.ToAnchorTypeAndOffsetString().Utf8().data() + << "\n" + << "extent: " + << extent_.ToAnchorTypeAndOffsetString().Utf8().data(); +} +#endif + +template <typename Strategy> +PositionTemplate<Strategy> SelectionTemplate<Strategy>::ComputeEndPosition() + const { + return IsBaseFirst() ? extent_ : base_; +} + +template <typename Strategy> +PositionTemplate<Strategy> SelectionTemplate<Strategy>::ComputeStartPosition() + const { + return IsBaseFirst() ? base_ : extent_; +} + +template <typename Strategy> +EphemeralRangeTemplate<Strategy> SelectionTemplate<Strategy>::ComputeRange() + const { + return EphemeralRangeTemplate<Strategy>(ComputeStartPosition(), + ComputeEndPosition()); +} + +template <typename Strategy> +bool SelectionTemplate<Strategy>::IsBaseFirst() const { + DCHECK(AssertValid()); + if (base_ == extent_) { + DCHECK_EQ(direction_, Direction::kForward); + return true; + } + if (direction_ == Direction::kForward) { + DCHECK_LE(base_, extent_); + return true; + } + if (direction_ == Direction::kBackward) { + DCHECK_GT(base_, extent_); + return false; + } + // Note: Since same position can be represented in different anchor type, + // e.g. Position(div, 0) and BeforeNode(first-child), we use |<=| to check + // forward selection. + DCHECK_EQ(direction_, Direction::kNotComputed); + direction_ = base_ <= extent_ ? Direction::kForward : Direction::kBackward; + return direction_ == Direction::kForward; +} + +template <typename Strategy> +void SelectionTemplate<Strategy>::ResetDirectionCache() const { + direction_ = base_ == extent_ ? Direction::kForward : Direction::kNotComputed; +} + +template <typename Strategy> +SelectionType SelectionTemplate<Strategy>::Type() const { + if (base_.IsNull()) + return kNoSelection; + if (base_ == extent_) + return kCaretSelection; + return kRangeSelection; +} + +template <typename Strategy> +void SelectionTemplate<Strategy>::PrintTo(std::ostream* ostream, + const char* type) const { + if (IsNone()) { + *ostream << "()"; + return; + } + *ostream << type << '('; +#if DCHECK_IS_ON() + if (dom_tree_version_ != base_.GetDocument()->DomTreeVersion()) { + *ostream << "Dirty: " << dom_tree_version_; + *ostream << " != " << base_.GetDocument()->DomTreeVersion() << ' '; + } +#endif + *ostream << "base: " << base_ << ", extent: " << extent_ << ')'; +} + +std::ostream& operator<<(std::ostream& ostream, + const SelectionInDOMTree& selection) { + selection.PrintTo(&ostream, "Selection"); + return ostream; +} + +std::ostream& operator<<(std::ostream& ostream, + const SelectionInFlatTree& selection) { + selection.PrintTo(&ostream, "SelectionInFlatTree"); + return ostream; +} + +// -- + +template <typename Strategy> +SelectionTemplate<Strategy>::Builder::Builder( + const SelectionTemplate<Strategy>& selection) + : selection_(selection) {} + +template <typename Strategy> +SelectionTemplate<Strategy>::Builder::Builder() = default; + +template <typename Strategy> +SelectionTemplate<Strategy> SelectionTemplate<Strategy>::Builder::Build() + const { + DCHECK(selection_.AssertValid()); + if (selection_.direction_ == Direction::kBackward) { + DCHECK_LE(selection_.extent_, selection_.base_); + return selection_; + } + if (selection_.direction_ == Direction::kForward) { + if (selection_.IsNone()) + return selection_; + DCHECK_LE(selection_.base_, selection_.extent_); + return selection_; + } + DCHECK_EQ(selection_.direction_, Direction::kNotComputed); + selection_.ResetDirectionCache(); + return selection_; +} + +template <typename Strategy> +typename SelectionTemplate<Strategy>::Builder& +SelectionTemplate<Strategy>::Builder::Collapse( + const PositionTemplate<Strategy>& position) { + DCHECK(position.IsConnected()) << position; + selection_.base_ = position; + selection_.extent_ = position; +#if DCHECK_IS_ON() + selection_.dom_tree_version_ = position.GetDocument()->DomTreeVersion(); +#endif + return *this; +} + +template <typename Strategy> +typename SelectionTemplate<Strategy>::Builder& +SelectionTemplate<Strategy>::Builder::Collapse( + const PositionWithAffinityTemplate<Strategy>& position_with_affinity) { + Collapse(position_with_affinity.GetPosition()); + SetAffinity(position_with_affinity.Affinity()); + return *this; +} + +template <typename Strategy> +typename SelectionTemplate<Strategy>::Builder& +SelectionTemplate<Strategy>::Builder::Extend( + const PositionTemplate<Strategy>& position) { + DCHECK(position.IsConnected()) << position; + DCHECK_EQ(selection_.GetDocument(), position.GetDocument()); + DCHECK(selection_.Base().IsConnected()) << selection_.Base(); + DCHECK(selection_.AssertValid()); + selection_.extent_ = position; + selection_.direction_ = Direction::kNotComputed; + return *this; +} + +template <typename Strategy> +typename SelectionTemplate<Strategy>::Builder& +SelectionTemplate<Strategy>::Builder::SelectAllChildren(const Node& node) { + DCHECK(node.CanContainRangeEndPoint()) << node; + return SetBaseAndExtent( + EphemeralRangeTemplate<Strategy>::RangeOfContents(node)); +} + +template <typename Strategy> +typename SelectionTemplate<Strategy>::Builder& +SelectionTemplate<Strategy>::Builder::SetAffinity(TextAffinity affinity) { + selection_.affinity_ = affinity; + return *this; +} + +template <typename Strategy> +typename SelectionTemplate<Strategy>::Builder& +SelectionTemplate<Strategy>::Builder::SetAsBackwardSelection( + const EphemeralRangeTemplate<Strategy>& range) { + DCHECK(range.IsNotNull()); + DCHECK(!range.IsCollapsed()); + DCHECK(selection_.IsNone()) << selection_; + selection_.base_ = range.EndPosition(); + selection_.extent_ = range.StartPosition(); + selection_.direction_ = Direction::kBackward; + DCHECK_GT(selection_.base_, selection_.extent_); +#if DCHECK_IS_ON() + selection_.dom_tree_version_ = range.GetDocument().DomTreeVersion(); +#endif + return *this; +} + +template <typename Strategy> +typename SelectionTemplate<Strategy>::Builder& +SelectionTemplate<Strategy>::Builder::SetAsForwardSelection( + const EphemeralRangeTemplate<Strategy>& range) { + DCHECK(range.IsNotNull()); + DCHECK(selection_.IsNone()) << selection_; + selection_.base_ = range.StartPosition(); + selection_.extent_ = range.EndPosition(); + selection_.direction_ = Direction::kForward; + DCHECK_LE(selection_.base_, selection_.extent_); +#if DCHECK_IS_ON() + selection_.dom_tree_version_ = range.GetDocument().DomTreeVersion(); +#endif + return *this; +} + +template <typename Strategy> +typename SelectionTemplate<Strategy>::Builder& +SelectionTemplate<Strategy>::Builder::SetBaseAndExtent( + const EphemeralRangeTemplate<Strategy>& range) { + if (range.IsNull()) { + selection_.base_ = PositionTemplate<Strategy>(); + selection_.extent_ = PositionTemplate<Strategy>(); +#if DCHECK_IS_ON() + selection_.dom_tree_version_ = 0; +#endif + return *this; + } + return SetAsForwardSelection(range); +} + +template <typename Strategy> +typename SelectionTemplate<Strategy>::Builder& +SelectionTemplate<Strategy>::Builder::SetBaseAndExtent( + const PositionTemplate<Strategy>& base, + const PositionTemplate<Strategy>& extent) { + if (base.IsNull()) { + DCHECK(extent.IsNull()) << extent; + return SetBaseAndExtent(EphemeralRangeTemplate<Strategy>()); + } + DCHECK(extent.IsNotNull()); + return Collapse(base).Extend(extent); +} + +template <typename Strategy> +typename SelectionTemplate<Strategy>::Builder& +SelectionTemplate<Strategy>::Builder::SetBaseAndExtentDeprecated( + const PositionTemplate<Strategy>& base, + const PositionTemplate<Strategy>& extent) { + if (base.IsNotNull() && extent.IsNotNull()) { + return SetBaseAndExtent(base, extent); + } + if (base.IsNotNull()) + return Collapse(base); + if (extent.IsNotNull()) + return Collapse(extent); + return SetBaseAndExtent(EphemeralRangeTemplate<Strategy>()); +} + +// --- + +template <typename Strategy> +SelectionTemplate<Strategy>::InvalidSelectionResetter::InvalidSelectionResetter( + const SelectionTemplate<Strategy>& selection) + : document_(selection.GetDocument()), + selection_(const_cast<SelectionTemplate&>(selection)) { + DCHECK(selection_.AssertValid()); +} + +template <typename Strategy> +SelectionTemplate< + Strategy>::InvalidSelectionResetter::~InvalidSelectionResetter() { + if (selection_.IsNone()) + return; + DCHECK(document_); + if (!selection_.IsValidFor(*document_)) { + selection_ = SelectionTemplate<Strategy>(); + return; + } +#if DCHECK_IS_ON() + selection_.dom_tree_version_ = document_->DomTreeVersion(); +#endif + selection_.ResetDirectionCache(); +} + +SelectionInDOMTree ConvertToSelectionInDOMTree( + const SelectionInFlatTree& selection_in_flat_tree) { + return SelectionInDOMTree::Builder() + .SetAffinity(selection_in_flat_tree.Affinity()) + .SetBaseAndExtent(ToPositionInDOMTree(selection_in_flat_tree.Base()), + ToPositionInDOMTree(selection_in_flat_tree.Extent())) + .Build(); +} + +SelectionInFlatTree ConvertToSelectionInFlatTree( + const SelectionInDOMTree& selection) { + return SelectionInFlatTree::Builder() + .SetAffinity(selection.Affinity()) + .SetBaseAndExtent(ToPositionInFlatTree(selection.Base()), + ToPositionInFlatTree(selection.Extent())) + .Build(); +} + +template <typename Strategy> +void SelectionTemplate<Strategy>::InvalidSelectionResetter::Trace( + blink::Visitor* visitor) { + visitor->Trace(document_); +} + +template class CORE_TEMPLATE_EXPORT SelectionTemplate<EditingStrategy>; +template class CORE_TEMPLATE_EXPORT + SelectionTemplate<EditingInFlatTreeStrategy>; + +} // namespace blink |