/*
 * Copyright (C) 2004, 2005, 2006, 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.
 */

#include "third_party/blink/renderer/core/editing/position.h"

#include <stdio.h>

#include <ostream>

#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/editing/editing_utilities.h"
#include "third_party/blink/renderer/core/editing/text_affinity.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"

namespace blink {

#if DCHECK_IS_ON()
template <typename Strategy>
static bool CanBeAnchorNode(Node*);

template <>
bool CanBeAnchorNode<EditingStrategy>(Node* node) {
  return !node || !node->IsPseudoElement();
}

template <>
bool CanBeAnchorNode<EditingInFlatTreeStrategy>(Node* node) {
  return CanBeAnchorNode<EditingStrategy>(node) &&
         (!node || !node->IsShadowRoot());
}
#endif

template <typename Strategy>
void PositionTemplate<Strategy>::Trace(Visitor* visitor) const {
  visitor->Trace(anchor_node_);
}

template <typename Strategy>
const TreeScope* PositionTemplate<Strategy>::CommonAncestorTreeScope(
    const PositionTemplate<Strategy>& a,
    const PositionTemplate<Strategy>& b) {
  if (!a.ComputeContainerNode() || !b.ComputeContainerNode())
    return nullptr;
  return a.ComputeContainerNode()->GetTreeScope().CommonAncestorTreeScope(
      b.ComputeContainerNode()->GetTreeScope());
}

template <typename Strategy>
PositionTemplate<Strategy> PositionTemplate<Strategy>::EditingPositionOf(
    const Node* anchor_node,
    wtf_size_t offset) {
  if (!anchor_node || anchor_node->IsTextNode())
    return PositionTemplate<Strategy>(anchor_node, offset);

  if (!EditingIgnoresContent(*anchor_node)) {
    return PositionTemplate<Strategy>::CreateWithoutValidationDeprecated(
        *anchor_node, offset);
  }

  if (offset == 0)
    return PositionTemplate<Strategy>(anchor_node,
                                      PositionAnchorType::kBeforeAnchor);

  // Note: |offset| can be >= 1, if |anchorNode| have child nodes, e.g.
  // using Node.appendChild() to add a child node TEXTAREA.
  DCHECK_GE(offset, 1u);
  return PositionTemplate<Strategy>(anchor_node,
                                    PositionAnchorType::kAfterAnchor);
}

// TODO(editing-dev): Once we change type of |anchor_node_| to
// |Member<const Node>|, we should get rid of |const_cast<Node*>()|.
// See http://crbug.com/735327
template <typename Strategy>
PositionTemplate<Strategy>::PositionTemplate(const Node* anchor_node,
                                             PositionAnchorType anchor_type)
    : anchor_node_(const_cast<Node*>(anchor_node)), anchor_type_(anchor_type) {
#if DCHECK_IS_ON()
  DCHECK(anchor_node_);
  DCHECK_NE(anchor_type_, PositionAnchorType::kOffsetInAnchor);
  DCHECK(CanBeAnchorNode<Strategy>(anchor_node_.Get())) << anchor_node_;
  if (anchor_node_->IsTextNode()) {
    DCHECK(anchor_type_ == PositionAnchorType::kBeforeAnchor ||
           anchor_type_ == PositionAnchorType::kAfterAnchor)
        << *this;
    return;
  }
  if (!Strategy::Parent(*anchor_node_)) {
    // Before/After |anchor_node_| should have a parent node for converting
    // to offset in anchor position.
    DCHECK(IsBeforeChildren() || IsAfterChildren()) << *this;
    return;
  }
#endif
}

// TODO(editing-dev): Once we change type of |anchor_node_| to
// |Member<const Node>|, we should get rid of |const_cast<Node*>()|.
// See http://crbug.com/735327
template <typename Strategy>
PositionTemplate<Strategy>::PositionTemplate(const Node* anchor_node,
                                             wtf_size_t offset)
    : anchor_node_(const_cast<Node*>(anchor_node)), offset_(offset) {
#if DCHECK_IS_ON()
  DCHECK(CanBeAnchorNode<Strategy>(anchor_node_.Get())) << anchor_node_;
  if (!anchor_node_) {
    DCHECK_EQ(offset, 0u);
    return;
  }
  if (auto* data = DynamicTo<CharacterData>(anchor_node_.Get())) {
    DCHECK_LE(offset, data->length()) << anchor_node_;
    return;
  }
  DCHECK_LE(offset, Strategy::CountChildren(*anchor_node)) << anchor_node_;
#endif
}

template <typename Strategy>
PositionTemplate<Strategy>::PositionTemplate(const Node& anchor_node,
                                             wtf_size_t offset)
    : PositionTemplate(&anchor_node, offset) {}

template <typename Strategy>
PositionTemplate<Strategy>::PositionTemplate() = default;

template <typename Strategy>
PositionTemplate<Strategy>::PositionTemplate(const PositionTemplate&) = default;

template <typename Strategy>
PositionTemplate<Strategy>& PositionTemplate<Strategy>::operator=(
    const PositionTemplate&) = default;

// static
template <typename Strategy>
PositionTemplate<Strategy> PositionTemplate<Strategy>::CreateWithoutValidation(
    const Node& container,
    wtf_size_t offset) {
  PositionTemplate<Strategy> result(container, 0);
  result.offset_ = offset;
  return result;
}

// static
template <typename Strategy>
PositionTemplate<Strategy>
PositionTemplate<Strategy>::CreateWithoutValidationDeprecated(
    const Node& container,
    wtf_size_t offset) {
  return CreateWithoutValidation(container, offset);
}

// --

template <typename Strategy>
Node* PositionTemplate<Strategy>::ComputeContainerNode() const {
  if (!anchor_node_)
    return nullptr;

  switch (AnchorType()) {
    case PositionAnchorType::kAfterChildren:
    case PositionAnchorType::kOffsetInAnchor:
      return anchor_node_.Get();
    case PositionAnchorType::kBeforeAnchor:
    case PositionAnchorType::kAfterAnchor: {
      Node* const parent = Strategy::Parent(*anchor_node_);
      // TODO(https://crbug.com/889737), Once we fix the issue, we should have
      // |DCHECK(parent)|.
      return parent;
    }
  }
  NOTREACHED();
}

template <typename Strategy>
static wtf_size_t MinOffsetForNode(Node* anchor_node, wtf_size_t offset) {
  if (auto* data = DynamicTo<CharacterData>(anchor_node))
    return std::min(offset, data->length());

  wtf_size_t new_offset = 0;
  for (Node* node = Strategy::FirstChild(*anchor_node);
       node && new_offset < offset; node = Strategy::NextSibling(*node))
    ++new_offset;

  return new_offset;
}

template <typename Strategy>
wtf_size_t PositionTemplate<Strategy>::ComputeOffsetInContainerNode() const {
  if (!anchor_node_)
    return 0;

  switch (AnchorType()) {
    case PositionAnchorType::kAfterChildren:
      return LastOffsetInNode(*anchor_node_);
    case PositionAnchorType::kOffsetInAnchor:
      return MinOffsetForNode<Strategy>(anchor_node_.Get(), offset_);
    case PositionAnchorType::kBeforeAnchor:
      return Strategy::Index(*anchor_node_);
    case PositionAnchorType::kAfterAnchor:
      return Strategy::Index(*anchor_node_) + 1;
  }
  NOTREACHED();
}

// Neighbor-anchored positions are invalid DOM positions, so they need to be
// fixed up before handing them off to the Range object.
template <typename Strategy>
PositionTemplate<Strategy>
PositionTemplate<Strategy>::ParentAnchoredEquivalent() const {
  if (!anchor_node_)
    return PositionTemplate<Strategy>();

  // FIXME: This should only be necessary for legacy positions, but is also
  // needed for positions before and after Tables
  if (offset_ == 0 && !IsAfterAnchorOrAfterChildren()) {
    if (Strategy::Parent(*anchor_node_) &&
        (EditingIgnoresContent(*anchor_node_) ||
         IsDisplayInsideTable(anchor_node_.Get())))
      return InParentBeforeNode(*anchor_node_);
    return PositionTemplate<Strategy>(anchor_node_.Get(), 0);
  }
  if (!anchor_node_->IsCharacterDataNode() &&
      (IsAfterAnchorOrAfterChildren() ||
       offset_ == anchor_node_->CountChildren()) &&
      (EditingIgnoresContent(*anchor_node_) ||
       IsDisplayInsideTable(anchor_node_.Get())) &&
      ComputeContainerNode()) {
    return InParentAfterNode(*anchor_node_);
  }

  return PositionTemplate<Strategy>(ComputeContainerNode(),
                                    ComputeOffsetInContainerNode());
}

template <typename Strategy>
PositionTemplate<Strategy> PositionTemplate<Strategy>::ToOffsetInAnchor()
    const {
  if (IsNull())
    return PositionTemplate<Strategy>();

  return PositionTemplate<Strategy>(ComputeContainerNode(),
                                    ComputeOffsetInContainerNode());
}

template <typename Strategy>
wtf_size_t PositionTemplate<Strategy>::ComputeEditingOffset() const {
  if (IsAfterAnchorOrAfterChildren())
    return Strategy::LastOffsetForEditing(anchor_node_.Get());
  return offset_;
}

template <typename Strategy>
Node* PositionTemplate<Strategy>::ComputeNodeBeforePosition() const {
  if (!anchor_node_)
    return nullptr;
  switch (AnchorType()) {
    case PositionAnchorType::kAfterChildren:
      return Strategy::LastChild(*anchor_node_);
    case PositionAnchorType::kOffsetInAnchor:
      return offset_ ? Strategy::ChildAt(*anchor_node_, offset_ - 1) : nullptr;
    case PositionAnchorType::kBeforeAnchor:
      return Strategy::PreviousSibling(*anchor_node_);
    case PositionAnchorType::kAfterAnchor:
      return anchor_node_.Get();
  }
  NOTREACHED();
}

template <typename Strategy>
Node* PositionTemplate<Strategy>::ComputeNodeAfterPosition() const {
  if (!anchor_node_)
    return nullptr;

  switch (AnchorType()) {
    case PositionAnchorType::kAfterChildren:
      return nullptr;
    case PositionAnchorType::kOffsetInAnchor:
      return Strategy::ChildAt(*anchor_node_, offset_);
    case PositionAnchorType::kBeforeAnchor:
      return anchor_node_.Get();
    case PositionAnchorType::kAfterAnchor:
      return Strategy::NextSibling(*anchor_node_);
  }
  NOTREACHED();
}

// An implementation of |Range::firstNode()|.
template <typename Strategy>
Node* PositionTemplate<Strategy>::NodeAsRangeFirstNode() const {
  if (!anchor_node_)
    return nullptr;
  if (!IsOffsetInAnchor())
    return ToOffsetInAnchor().NodeAsRangeFirstNode();
  if (anchor_node_->IsCharacterDataNode())
    return anchor_node_.Get();
  if (Node* child = Strategy::ChildAt(*anchor_node_, offset_))
    return child;
  if (!offset_)
    return anchor_node_.Get();
  return Strategy::NextSkippingChildren(*anchor_node_);
}

template <typename Strategy>
Node* PositionTemplate<Strategy>::NodeAsRangeLastNode() const {
  if (IsNull())
    return nullptr;
  if (Node* past_last_node = NodeAsRangePastLastNode())
    return Strategy::Previous(*past_last_node);
  return &Strategy::LastWithinOrSelf(*ComputeContainerNode());
}

// An implementation of |Range::pastLastNode()|.
template <typename Strategy>
Node* PositionTemplate<Strategy>::NodeAsRangePastLastNode() const {
  if (!anchor_node_)
    return nullptr;
  if (!IsOffsetInAnchor())
    return ToOffsetInAnchor().NodeAsRangePastLastNode();
  if (anchor_node_->IsCharacterDataNode())
    return Strategy::NextSkippingChildren(*anchor_node_);
  if (Node* child = Strategy::ChildAt(*anchor_node_, offset_))
    return child;
  return Strategy::NextSkippingChildren(*anchor_node_);
}

template <typename Strategy>
Node* PositionTemplate<Strategy>::CommonAncestorContainer(
    const PositionTemplate<Strategy>& other) const {
  return Strategy::CommonAncestor(*ComputeContainerNode(),
                                  *other.ComputeContainerNode());
}

static bool IsPositionConnected(const Position& position) {
  return position.AnchorNode() && position.AnchorNode()->isConnected();
}

static bool IsPositionConnected(const PositionInFlatTree& position) {
  if (position.IsNull())
    return false;
  return FlatTreeTraversal::Contains(*position.GetDocument(),
                                     *position.AnchorNode());
}

template <typename Strategy>
bool PositionTemplate<Strategy>::IsBeforeChildren() const {
  if (IsBeforeAnchor())
    return !Strategy::PreviousSibling(*anchor_node_);
  return IsOffsetInAnchor() && !offset_;
}

template <typename Strategy>
bool PositionTemplate<Strategy>::IsConnected() const {
  return IsPositionConnected(*this);
}

template <typename Strategy>
bool PositionTemplate<Strategy>::IsValidFor(const Document& document) const {
  if (IsNull())
    return true;
  if (GetDocument() != document)
    return false;
  if (!IsConnected())
    return false;
  return !IsOffsetInAnchor() ||
         OffsetInContainerNode() <= LastOffsetInNode(*AnchorNode());
}

template <typename Strategy>
int16_t PositionTemplate<Strategy>::CompareTo(
    const PositionTemplate<Strategy>& other) const {
  return ComparePositions(*this, other);
}

template <typename Strategy>
bool PositionTemplate<Strategy>::operator<(
    const PositionTemplate<Strategy>& other) const {
  return ComparePositions(*this, other) < 0;
}

template <typename Strategy>
bool PositionTemplate<Strategy>::operator<=(
    const PositionTemplate<Strategy>& other) const {
  return ComparePositions(*this, other) <= 0;
}

template <typename Strategy>
bool PositionTemplate<Strategy>::operator>(
    const PositionTemplate<Strategy>& other) const {
  return ComparePositions(*this, other) > 0;
}

template <typename Strategy>
bool PositionTemplate<Strategy>::operator>=(
    const PositionTemplate<Strategy>& other) const {
  return ComparePositions(*this, other) >= 0;
}

template <typename Strategy>
bool PositionTemplate<Strategy>::IsEquivalent(
    const PositionTemplate<Strategy>& other) const {
  if (IsNull())
    return other.IsNull();
  if (anchor_type_ == other.anchor_type_)
    return *this == other;
  return ToOffsetInAnchor() == other.ToOffsetInAnchor();
}

template <typename Strategy>
bool PositionTemplate<Strategy>::AtFirstEditingPositionForNode() const {
  if (IsNull())
    return true;
  // FIXME: Position before anchor shouldn't be considered as at the first
  // editing position for node since that position resides outside of the node.
  switch (anchor_type_) {
    case PositionAnchorType::kOffsetInAnchor:
      return offset_ == 0;
    case PositionAnchorType::kBeforeAnchor:
      return true;
    case PositionAnchorType::kAfterChildren:
    case PositionAnchorType::kAfterAnchor:
      // TODO(yosin) We should use |Strategy::lastOffsetForEditing()| instead
      // of DOM tree version.
      return !EditingStrategy::LastOffsetForEditing(AnchorNode());
  }
  NOTREACHED();
}

template <typename Strategy>
bool PositionTemplate<Strategy>::AtLastEditingPositionForNode() const {
  if (IsNull())
    return true;
  // TODO(yosin): Position after anchor shouldn't be considered as at the
  // first editing position for node since that position resides outside of
  // the node.
  // TODO(yosin) We should use |Strategy::lastOffsetForEditing()| instead of
  // DOM tree version.
  return IsAfterAnchorOrAfterChildren() ||
         offset_ >= EditingStrategy::LastOffsetForEditing(AnchorNode());
}

template <typename Strategy>
bool PositionTemplate<Strategy>::AtStartOfTree() const {
  if (IsNull())
    return true;
  return !Strategy::Parent(*AnchorNode()) && !ComputeNodeBeforePosition();
}

// static
template <typename Strategy>
PositionTemplate<Strategy> PositionTemplate<Strategy>::InParentBeforeNode(
    const Node& node) {
  // FIXME: This should DCHECK(node.parentNode()). At least one caller currently
  // hits this DCHECK though, which indicates that the caller is trying to make
  // a position relative to a disconnected node (which is likely an error)
  // Specifically, editing/deleting/delete-ligature-001.html crashes with
  // DCHECK(node->parentNode())
  return PositionTemplate<Strategy>(Strategy::Parent(node),
                                    Strategy::Index(node));
}

// static
template <typename Strategy>
PositionTemplate<Strategy> PositionTemplate<Strategy>::InParentAfterNode(
    const Node& node) {
  DCHECK(node.parentNode()) << node;
  return PositionTemplate<Strategy>(Strategy::Parent(node),
                                    Strategy::Index(node) + 1);
}

// static
template <typename Strategy>
PositionTemplate<Strategy> PositionTemplate<Strategy>::BeforeNode(
    const Node& anchor_node) {
  return PositionTemplate<Strategy>(&anchor_node,
                                    PositionAnchorType::kBeforeAnchor);
}

// static
template <typename Strategy>
PositionTemplate<Strategy> PositionTemplate<Strategy>::AfterNode(
    const Node& anchor_node) {
  return PositionTemplate<Strategy>(&anchor_node,
                                    PositionAnchorType::kAfterAnchor);
}

// static
template <typename Strategy>
wtf_size_t PositionTemplate<Strategy>::LastOffsetInNode(const Node& node) {
  if (auto* data = DynamicTo<CharacterData>(node))
    return data->length();

  return Strategy::CountChildren(node);
}

// static
template <typename Strategy>
PositionTemplate<Strategy> PositionTemplate<Strategy>::FirstPositionInNode(
    const Node& anchor_node) {
  return PositionTemplate<Strategy>(anchor_node, 0);
}

// static
template <typename Strategy>
PositionTemplate<Strategy> PositionTemplate<Strategy>::LastPositionInNode(
    const Node& anchor_node) {
  if (anchor_node.IsTextNode()) {
    return PositionTemplate<Strategy>(anchor_node,
                                      LastOffsetInNode(anchor_node));
  }
  return PositionTemplate<Strategy>(&anchor_node,
                                    PositionAnchorType::kAfterChildren);
}

// static
template <typename Strategy>
PositionTemplate<Strategy>
PositionTemplate<Strategy>::FirstPositionInOrBeforeNode(const Node& node) {
  return EditingIgnoresContent(node) ? BeforeNode(node)
                                     : FirstPositionInNode(node);
}

// static
template <typename Strategy>
PositionTemplate<Strategy>
PositionTemplate<Strategy>::LastPositionInOrAfterNode(const Node& node) {
  return EditingIgnoresContent(node) && Strategy::Parent(node)
             ? AfterNode(node)
             : LastPositionInNode(node);
}

PositionInFlatTree ToPositionInFlatTree(const Position& pos) {
  if (pos.IsNull())
    return PositionInFlatTree();

  Node* const anchor = pos.AnchorNode();
  if (pos.IsOffsetInAnchor()) {
    if (anchor->IsCharacterDataNode())
      return PositionInFlatTree(anchor, pos.ComputeOffsetInContainerNode());
    int offset = pos.ComputeOffsetInContainerNode();
    if (!offset) {
      Node* node = anchor->IsShadowRoot() ? anchor->OwnerShadowHost() : anchor;
      return PositionInFlatTree::FirstPositionInNode(*node);
    }
    Node* child = NodeTraversal::ChildAt(*anchor, offset);
    if (!child) {
      Node* node = anchor->IsShadowRoot() ? anchor->OwnerShadowHost() : anchor;
      return PositionInFlatTree::LastPositionInNode(*node);
    }
    if (child->IsShadowRoot()) {
      if (anchor->IsShadowRoot())
        return PositionInFlatTree(anchor->OwnerShadowHost(), offset);
      return PositionInFlatTree(anchor, offset);
    }
    if (Node* parent = FlatTreeTraversal::Parent(*child))
      return PositionInFlatTree(parent, FlatTreeTraversal::Index(*child));
    // When |pos| isn't appeared in flat tree, we map |pos| to after
    // children of shadow host.
    // e.g. "foo",0 in <progress>foo</progress>
    if (anchor->IsShadowRoot()) {
      return PositionInFlatTree::LastPositionInNode(*anchor->OwnerShadowHost());
    }
    return PositionInFlatTree::LastPositionInNode(*anchor);
  }

  if (anchor->IsShadowRoot())
    return PositionInFlatTree(anchor->OwnerShadowHost(), pos.AnchorType());
  if (pos.IsBeforeAnchor() || pos.IsAfterAnchor()) {
    if (!FlatTreeTraversal::Parent(*anchor)) {
      // For Before/AfterAnchor, if |anchor| doesn't have parent in the flat
      // tree, there is no valid corresponding PositionInFlatTree.
      // Since this function is a primitive function, we do not adjust |pos|
      // to somewhere else in flat tree.
      // Reached by unit test
      // FrameSelectionTest.SelectInvalidPositionInFlatTreeDoesntCrash.
      return PositionInFlatTree();
    }
  }
  // TODO(yosin): Once we have a test case for SLOT or active insertion point,
  // this function should handle it.
  return PositionInFlatTree(anchor, pos.AnchorType());
}

PositionInFlatTree ToPositionInFlatTree(const PositionInFlatTree& position) {
  return position;
}

Position ToPositionInDomTree(const Position& position) {
  return position;
}

Position ToPositionInDomTree(const PositionInFlatTree& position) {
  if (position.IsNull())
    return Position();

  Node* anchor_node = position.AnchorNode();

  switch (position.AnchorType()) {
    case PositionAnchorType::kAfterChildren:
      // FIXME: When anchorNode is <img>, assertion fails in the constructor.
      return Position::LastPositionInNode(*anchor_node);
    case PositionAnchorType::kAfterAnchor:
      return Position::AfterNode(*anchor_node);
    case PositionAnchorType::kBeforeAnchor:
      return Position::BeforeNode(*anchor_node);
    case PositionAnchorType::kOffsetInAnchor: {
      int offset = position.OffsetInContainerNode();
      if (anchor_node->IsCharacterDataNode())
        return Position(anchor_node, offset);
      if (!offset)
        return Position::FirstPositionInNode(*anchor_node);
      Node* child = FlatTreeTraversal::ChildAt(*anchor_node, offset);
      if (child)
        return Position(child->parentNode(), child->NodeIndex());

      // |child| is null when the position is at the end of the children.
      // <div>foo|</div>
      return Position::LastPositionInNode(*anchor_node);
    }
    default:
      NOTREACHED();
  }
}

template <typename Strategy>
String PositionTemplate<Strategy>::ToAnchorTypeAndOffsetString() const {
  switch (AnchorType()) {
    case PositionAnchorType::kOffsetInAnchor: {
      StringBuilder builder;
      builder.Append("offsetInAnchor[");
      builder.AppendNumber(offset_);
      builder.Append("]");
      return builder.ToString();
    }
    case PositionAnchorType::kAfterChildren:
      return "afterChildren";
    case PositionAnchorType::kBeforeAnchor:
      return "beforeAnchor";
    case PositionAnchorType::kAfterAnchor:
      return "afterAnchor";
  }
  NOTREACHED();
}

#if DCHECK_IS_ON()

template <typename Strategy>
void PositionTemplate<Strategy>::ShowTreeForThis() const {
  if (!AnchorNode()) {
    LOG(INFO) << "\nposition is null";
    return;
  }
  LOG(INFO) << "\n"
            << AnchorNode()->ToTreeStringForThis().Utf8()
            << ToAnchorTypeAndOffsetString().Utf8();
}

template <typename Strategy>
void PositionTemplate<Strategy>::ShowTreeForThisInFlatTree() const {
  if (!AnchorNode()) {
    LOG(INFO) << "\nposition is null";
    return;
  }
  LOG(INFO) << "\n"
            << AnchorNode()->ToFlatTreeStringForThis().Utf8()
            << ToAnchorTypeAndOffsetString().Utf8();
}

#endif  // DCHECK_IS_ON()

template <typename PositionType>
static std::ostream& PrintPosition(std::ostream& ostream,
                                   const PositionType& position) {
  if (position.IsNull())
    return ostream << "null";
  return ostream << position.AnchorNode() << "@"
                 << position.ToAnchorTypeAndOffsetString().Utf8();
}

std::ostream& operator<<(std::ostream& ostream,
                         PositionAnchorType anchor_type) {
  switch (anchor_type) {
    case PositionAnchorType::kAfterAnchor:
      return ostream << "afterAnchor";
    case PositionAnchorType::kAfterChildren:
      return ostream << "afterChildren";
    case PositionAnchorType::kBeforeAnchor:
      return ostream << "beforeAnchor";
    case PositionAnchorType::kOffsetInAnchor:
      return ostream << "offsetInAnchor";
  }
  NOTREACHED();
}

std::ostream& operator<<(std::ostream& ostream, const Position& position) {
  return PrintPosition(ostream, position);
}

std::ostream& operator<<(std::ostream& ostream,
                         const PositionInFlatTree& position) {
  return PrintPosition(ostream, position);
}

template class CORE_TEMPLATE_EXPORT PositionTemplate<EditingStrategy>;
template class CORE_TEMPLATE_EXPORT PositionTemplate<EditingInFlatTreeStrategy>;

}  // namespace blink

#if DCHECK_IS_ON()

void ShowTree(const blink::Position& pos) {
  pos.ShowTreeForThis();
}

void ShowTree(const blink::Position* pos) {
  if (pos)
    pos->ShowTreeForThis();
  else
    LOG(INFO) << "Cannot showTree for <null>";
}

#endif
