blob: 9026294bea5b6ca5f35244171bc1827c3ca1368e [file] [log] [blame]
/*
* Copyright (C) 2008 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 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 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.
*/
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_DOM_RANGE_BOUNDARY_POINT_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_DOM_RANGE_BOUNDARY_POINT_H_
#include "third_party/blink/renderer/core/dom/character_data.h"
#include "third_party/blink/renderer/core/dom/node.h"
#include "third_party/blink/renderer/core/dom/node_traversal.h"
#include "third_party/blink/renderer/core/editing/position.h"
namespace blink {
class RangeBoundaryPoint {
DISALLOW_NEW();
public:
explicit RangeBoundaryPoint(Node& container);
explicit RangeBoundaryPoint(const RangeBoundaryPoint&);
bool IsConnected() const;
const Position ToPosition() const;
Node& Container() const;
unsigned Offset() const;
Node* ChildBefore() const;
void Set(Node& container, unsigned offset, Node* child_before);
void SetOffset(unsigned);
void SetToBeforeChild(Node&);
void SetToStartOfNode(Node&);
void SetToEndOfNode(Node&);
void ChildBeforeWillBeRemoved();
void InvalidateOffset();
void MarkValid() const;
void Trace(Visitor* visitor) {
visitor->Trace(container_node_);
visitor->Trace(child_before_boundary_);
}
private:
uint64_t DomTreeVersion() const;
void EnsureOffsetIsValid() const;
bool IsOffsetValid() const;
static const unsigned kInvalidOffset = static_cast<unsigned>(-1);
Member<Node> container_node_;
Member<Node> child_before_boundary_;
mutable uint64_t dom_tree_version_;
mutable unsigned offset_in_container_;
};
inline RangeBoundaryPoint::RangeBoundaryPoint(Node& container)
: container_node_(container),
child_before_boundary_(nullptr),
dom_tree_version_(DomTreeVersion()),
offset_in_container_(0) {}
inline RangeBoundaryPoint::RangeBoundaryPoint(const RangeBoundaryPoint& other)
: container_node_(other.Container()),
child_before_boundary_(other.ChildBefore()),
dom_tree_version_(other.dom_tree_version_),
offset_in_container_(other.Offset()) {}
inline Node& RangeBoundaryPoint::Container() const {
return *container_node_;
}
inline Node* RangeBoundaryPoint::ChildBefore() const {
return child_before_boundary_.Get();
}
inline uint64_t RangeBoundaryPoint::DomTreeVersion() const {
return container_node_->GetDocument().DomTreeVersion();
}
inline void RangeBoundaryPoint::EnsureOffsetIsValid() const {
if (IsOffsetValid())
return;
DCHECK(!container_node_->IsCharacterDataNode());
MarkValid();
if (!child_before_boundary_) {
offset_in_container_ = 0;
return;
}
offset_in_container_ = child_before_boundary_->NodeIndex() + 1;
}
inline bool RangeBoundaryPoint::IsConnected() const {
return container_node_ && container_node_->isConnected();
}
inline bool RangeBoundaryPoint::IsOffsetValid() const {
if (offset_in_container_ == kInvalidOffset) {
DCHECK(!container_node_->IsTextNode());
return false;
}
return DomTreeVersion() == dom_tree_version_ ||
container_node_->IsCharacterDataNode();
}
inline const Position RangeBoundaryPoint::ToPosition() const {
EnsureOffsetIsValid();
// TODO(yosin): We should return |Position::BeforeAnchor| when
// |container_node_| isn't a |Text| node.
return Position(container_node_.Get(), offset_in_container_);
}
inline unsigned RangeBoundaryPoint::Offset() const {
EnsureOffsetIsValid();
return offset_in_container_;
}
inline void RangeBoundaryPoint::Set(Node& container,
unsigned offset,
Node* child_before) {
DCHECK_GE(offset, 0u);
DCHECK_EQ(child_before,
offset ? NodeTraversal::ChildAt(container, offset - 1) : nullptr);
container_node_ = container;
offset_in_container_ = offset;
child_before_boundary_ = child_before;
MarkValid();
}
inline void RangeBoundaryPoint::SetOffset(unsigned offset) {
DCHECK(container_node_);
DCHECK(container_node_->IsCharacterDataNode());
DCHECK_GE(offset_in_container_, 0u);
DCHECK(!child_before_boundary_);
offset_in_container_ = offset;
MarkValid();
}
inline void RangeBoundaryPoint::SetToBeforeChild(Node& child) {
DCHECK(child.parentNode());
child_before_boundary_ = child.previousSibling();
container_node_ = child.parentNode();
offset_in_container_ = child_before_boundary_ ? kInvalidOffset : 0;
MarkValid();
}
inline void RangeBoundaryPoint::SetToStartOfNode(Node& container) {
container_node_ = &container;
offset_in_container_ = 0;
child_before_boundary_ = nullptr;
MarkValid();
}
inline void RangeBoundaryPoint::SetToEndOfNode(Node& container) {
container_node_ = &container;
if (auto* character_data = DynamicTo<CharacterData>(container_node_.Get())) {
offset_in_container_ = character_data->length();
child_before_boundary_ = nullptr;
} else {
child_before_boundary_ = container_node_->lastChild();
offset_in_container_ = child_before_boundary_ ? kInvalidOffset : 0;
}
MarkValid();
}
inline void RangeBoundaryPoint::ChildBeforeWillBeRemoved() {
child_before_boundary_ = child_before_boundary_->previousSibling();
if (!IsOffsetValid())
return;
DCHECK_GT(offset_in_container_, 0u);
if (!child_before_boundary_)
offset_in_container_ = 0;
else if (offset_in_container_ > 0)
--offset_in_container_;
MarkValid();
}
inline void RangeBoundaryPoint::InvalidateOffset() {
offset_in_container_ = kInvalidOffset;
}
inline void RangeBoundaryPoint::MarkValid() const {
dom_tree_version_ = DomTreeVersion();
}
inline bool operator==(const RangeBoundaryPoint& a,
const RangeBoundaryPoint& b) {
if (a.Container() != b.Container())
return false;
if (a.ChildBefore() || b.ChildBefore()) {
if (a.ChildBefore() != b.ChildBefore())
return false;
} else {
if (a.Offset() != b.Offset())
return false;
}
return true;
}
} // namespace blink
#endif