blob: dca55e7ebee013fa5bd0150359a0b96f2cded4b1 [file] [log] [blame]
// Copyright 2015 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/ephemeral_range.h"
#include <ostream> // NOLINT
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/element.h"
#include "third_party/blink/renderer/core/dom/range.h"
#include "third_party/blink/renderer/core/dom/text.h"
namespace blink {
namespace {
template <typename Strategy>
Node* CommonAncestorContainerNode(const Node* container_a,
const Node* container_b) {
if (!container_a || !container_b)
return nullptr;
return Strategy::CommonAncestor(*container_a, *container_b);
}
}
template <typename Strategy>
EphemeralRangeTemplate<Strategy>::EphemeralRangeTemplate(
const PositionTemplate<Strategy>& start,
const PositionTemplate<Strategy>& end)
: start_position_(start),
end_position_(end)
#if DCHECK_IS_ON()
,
dom_tree_version_(start.IsNull() ? 0
: start.GetDocument()->DomTreeVersion())
#endif
{
if (start_position_.IsNull()) {
DCHECK(end_position_.IsNull());
return;
}
DCHECK(end_position_.IsNotNull());
DCHECK(start_position_.IsValidFor(*start_position_.GetDocument()));
DCHECK(end_position_.IsValidFor(*end_position_.GetDocument()));
DCHECK_EQ(start_position_.GetDocument(), end_position_.GetDocument());
DCHECK_LE(start_position_, end_position_);
}
template <typename Strategy>
EphemeralRangeTemplate<Strategy>::EphemeralRangeTemplate(
const EphemeralRangeTemplate<Strategy>& other)
: EphemeralRangeTemplate(other.start_position_, other.end_position_) {
DCHECK(other.IsValid());
}
template <typename Strategy>
EphemeralRangeTemplate<Strategy>::EphemeralRangeTemplate(
const PositionTemplate<Strategy>& position)
: EphemeralRangeTemplate(position, position) {}
template <typename Strategy>
EphemeralRangeTemplate<Strategy>::EphemeralRangeTemplate(const Range* range) {
if (!range)
return;
DCHECK(range->IsConnected());
start_position_ = FromPositionInDOMTree<Strategy>(range->StartPosition());
end_position_ = FromPositionInDOMTree<Strategy>(range->EndPosition());
#if DCHECK_IS_ON()
dom_tree_version_ = range->OwnerDocument().DomTreeVersion();
#endif
}
template <typename Strategy>
EphemeralRangeTemplate<Strategy>::EphemeralRangeTemplate() = default;
template <typename Strategy>
EphemeralRangeTemplate<Strategy>::~EphemeralRangeTemplate() = default;
template <typename Strategy>
EphemeralRangeTemplate<Strategy>& EphemeralRangeTemplate<Strategy>::operator=(
const EphemeralRangeTemplate<Strategy>& other) {
DCHECK(other.IsValid());
start_position_ = other.start_position_;
end_position_ = other.end_position_;
#if DCHECK_IS_ON()
dom_tree_version_ = other.dom_tree_version_;
#endif
return *this;
}
template <typename Strategy>
bool EphemeralRangeTemplate<Strategy>::operator==(
const EphemeralRangeTemplate<Strategy>& other) const {
return StartPosition() == other.StartPosition() &&
EndPosition() == other.EndPosition();
}
template <typename Strategy>
bool EphemeralRangeTemplate<Strategy>::operator!=(
const EphemeralRangeTemplate<Strategy>& other) const {
return !operator==(other);
}
template <typename Strategy>
Document& EphemeralRangeTemplate<Strategy>::GetDocument() const {
DCHECK(IsNotNull());
return *start_position_.GetDocument();
}
template <typename Strategy>
PositionTemplate<Strategy> EphemeralRangeTemplate<Strategy>::StartPosition()
const {
DCHECK(IsValid());
return start_position_;
}
template <typename Strategy>
PositionTemplate<Strategy> EphemeralRangeTemplate<Strategy>::EndPosition()
const {
DCHECK(IsValid());
return end_position_;
}
template <typename Strategy>
Node* EphemeralRangeTemplate<Strategy>::CommonAncestorContainer() const {
return CommonAncestorContainerNode<Strategy>(
start_position_.ComputeContainerNode(),
end_position_.ComputeContainerNode());
}
template <typename Strategy>
bool EphemeralRangeTemplate<Strategy>::IsCollapsed() const {
DCHECK(IsValid());
return start_position_ == end_position_;
}
template <typename Strategy>
typename EphemeralRangeTemplate<Strategy>::RangeTraversal
EphemeralRangeTemplate<Strategy>::Nodes() const {
return RangeTraversal(start_position_.NodeAsRangeFirstNode(),
end_position_.NodeAsRangePastLastNode());
}
template <typename Strategy>
EphemeralRangeTemplate<Strategy>
EphemeralRangeTemplate<Strategy>::RangeOfContents(const Node& node) {
return EphemeralRangeTemplate<Strategy>(
PositionTemplate<Strategy>::FirstPositionInNode(node),
PositionTemplate<Strategy>::LastPositionInNode(node));
}
#if DCHECK_IS_ON()
template <typename Strategy>
bool EphemeralRangeTemplate<Strategy>::IsValid() const {
return start_position_.IsNull() ||
dom_tree_version_ == start_position_.GetDocument()->DomTreeVersion();
}
#else
template <typename Strategy>
bool EphemeralRangeTemplate<Strategy>::IsValid() const {
return true;
}
#endif
#ifndef NDEBUG
template <typename Strategy>
void EphemeralRangeTemplate<Strategy>::ShowTreeForThis() const {
if (IsNull()) {
LOG(INFO) << "<null range>" << std::endl;
return;
}
LOG(INFO) << std::endl
<< StartPosition()
.AnchorNode()
->ToMarkedTreeString(StartPosition().AnchorNode(), "S",
EndPosition().AnchorNode(), "E")
.Utf8()
.data()
<< "start: "
<< StartPosition().ToAnchorTypeAndOffsetString().Utf8().data()
<< std::endl
<< "end: "
<< EndPosition().ToAnchorTypeAndOffsetString().Utf8().data();
}
#endif
Range* CreateRange(const EphemeralRange& range) {
if (range.IsNull())
return nullptr;
return Range::Create(range.GetDocument(), range.StartPosition(),
range.EndPosition());
}
template <typename Strategy>
static std::ostream& PrintEphemeralRange(
std::ostream& ostream,
const EphemeralRangeTemplate<Strategy> range) {
if (range.IsNull())
return ostream << "null";
if (range.IsCollapsed())
return ostream << range.StartPosition();
return ostream << '[' << range.StartPosition() << ", " << range.EndPosition()
<< ']';
}
std::ostream& operator<<(std::ostream& ostream, const EphemeralRange& range) {
return PrintEphemeralRange(ostream, range);
}
std::ostream& operator<<(std::ostream& ostream,
const EphemeralRangeInFlatTree& range) {
return PrintEphemeralRange(ostream, range);
}
template class CORE_TEMPLATE_EXPORT EphemeralRangeTemplate<EditingStrategy>;
template class CORE_TEMPLATE_EXPORT
EphemeralRangeTemplate<EditingInFlatTreeStrategy>;
} // namespace blink