// Copyright 2015 The Chromium Authors
// 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/visible_units.h"

#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
#include "third_party/blink/renderer/core/dom/text.h"
#include "third_party/blink/renderer/core/editing/ephemeral_range.h"
#include "third_party/blink/renderer/core/editing/position_units.h"
#include "third_party/blink/renderer/core/editing/position_with_affinity.h"
#include "third_party/blink/renderer/core/editing/selection_template.h"
#include "third_party/blink/renderer/core/editing/testing/editing_test_base.h"
#include "third_party/blink/renderer/core/editing/visible_position.h"
#include "third_party/blink/renderer/core/html/forms/text_control_element.h"
#include "third_party/blink/renderer/core/layout/layout_text_fragment.h"

namespace blink {
namespace visible_units_test {

PositionWithAffinity PositionWithAffinityInDomTree(
    Node& anchor,
    int offset,
    TextAffinity affinity = TextAffinity::kDownstream) {
  return PositionWithAffinity(CanonicalPositionOf(Position(&anchor, offset)),
                              affinity);
}

VisiblePosition CreateVisiblePositionInDomTree(
    Node& anchor,
    int offset,
    TextAffinity affinity = TextAffinity::kDownstream) {
  return CreateVisiblePosition(Position(&anchor, offset), affinity);
}

PositionInFlatTreeWithAffinity PositionWithAffinityInFlatTree(
    Node& anchor,
    int offset,
    TextAffinity affinity = TextAffinity::kDownstream) {
  return PositionInFlatTreeWithAffinity(
      CanonicalPositionOf(PositionInFlatTree(&anchor, offset)), affinity);
}

VisiblePositionInFlatTree CreateVisiblePositionInFlatTree(
    Node& anchor,
    int offset,
    TextAffinity affinity = TextAffinity::kDownstream) {
  return CreateVisiblePosition(PositionInFlatTree(&anchor, offset), affinity);
}

class VisibleUnitsTest : public EditingTestBase {
 protected:
  std::string TestSnapBackward(
      const std::string& selection_text,
      EditingBoundaryCrossingRule rule = kCannotCrossEditingBoundary) {
    const Position position = SetCaretTextToBody(selection_text);
    return GetCaretTextFromBody(MostBackwardCaretPosition(position, rule));
  }

  std::string TestSnapForward(
      const std::string& selection_text,
      EditingBoundaryCrossingRule rule = kCannotCrossEditingBoundary) {
    const Position position = SetCaretTextToBody(selection_text);
    return GetCaretTextFromBody(MostForwardCaretPosition(position, rule));
  }
};

TEST_F(VisibleUnitsTest, caretMinOffset) {
  const char* body_content = "<p id=one>one</p>";
  SetBodyContent(body_content);

  Element* one = GetDocument().getElementById(AtomicString("one"));

  EXPECT_EQ(0, CaretMinOffset(one->firstChild()));
}

TEST_F(VisibleUnitsTest, caretMinOffsetWithFirstLetter) {
  const char* body_content =
      "<style>#one:first-letter { font-size: 200%; }</style><p id=one>one</p>";
  SetBodyContent(body_content);

  Element* one = GetDocument().getElementById(AtomicString("one"));

  EXPECT_EQ(0, CaretMinOffset(one->firstChild()));
}

TEST_F(VisibleUnitsTest, characterAfter) {
  const char* body_content =
      "<p id='host'><b slot='#one' id='one'>1</b><b slot='#two' "
      "id='two'>22</b></p><b "
      "id='three'>333</b>";
  const char* shadow_content =
      "<b id='four'>4444</b><slot name='#two'></slot><slot name=#one></slot><b "
      "id='five'>5555</b>";
  SetBodyContent(body_content);
  SetShadowContent(shadow_content, "host");

  Element* one = GetDocument().getElementById(AtomicString("one"));
  Element* two = GetDocument().getElementById(AtomicString("two"));

  EXPECT_EQ(
      0, CharacterAfter(CreateVisiblePositionInDomTree(*one->firstChild(), 1)));
  EXPECT_EQ('5', CharacterAfter(
                     CreateVisiblePositionInFlatTree(*one->firstChild(), 1)));

  EXPECT_EQ('1', CharacterAfter(
                     CreateVisiblePositionInDomTree(*two->firstChild(), 2)));
  EXPECT_EQ('1', CharacterAfter(
                     CreateVisiblePositionInFlatTree(*two->firstChild(), 2)));
}

// http://crbug.com/1176202
TEST_F(VisibleUnitsTest, CanonicalPositionOfWithBefore) {
  LoadAhem();
  InsertStyleElement(
      "body { font: 10px/15px Ahem; }"
      "b::before { content: '\\u200B'");
  // |LayoutInline::PhysicalLinesBoundingBox()| for <span></span> returns
  //    LayoutNG: (0,0)+(0x10)
  //    Legacy:   (0,0)+(0x0)
  //  because we don't cull empty <span> in LayoutNG.
  SetBodyContent("<div contenteditable id=target><span></span><b></b></div>");
  Element& target = *GetElementById("target");

  EXPECT_EQ(Position(target, 0), CanonicalPositionOf(Position(target, 0)));
  EXPECT_EQ(Position(target, 0), CanonicalPositionOf(Position(target, 1)));
  EXPECT_EQ(Position(target, 0), CanonicalPositionOf(Position(target, 2)));
}

TEST_F(VisibleUnitsTest, canonicalPositionOfWithHTMLHtmlElement) {
  const char* body_content =
      "<html><div id=one contenteditable>1</div><span id=two "
      "contenteditable=false>22</span><span id=three "
      "contenteditable=false>333</span><span id=four "
      "contenteditable=false>333</span></html>";
  SetBodyContent(body_content);

  Node* one = QuerySelector("#one");
  Node* two = QuerySelector("#two");
  Node* three = QuerySelector("#three");
  Node* four = QuerySelector("#four");
  Element* html = GetDocument().CreateRawElement(html_names::kHTMLTag);
  // Move two, three and four into second html element.
  html->AppendChild(two);
  html->AppendChild(three);
  html->AppendChild(four);
  one->appendChild(html);
  UpdateAllLifecyclePhasesForTest();

  EXPECT_EQ(Position(),
            CanonicalPositionOf(Position(GetDocument().documentElement(), 0)));

  EXPECT_EQ(Position(one->firstChild(), 0),
            CanonicalPositionOf(Position(one, 0)));
  EXPECT_EQ(Position(one->firstChild(), 1),
            CanonicalPositionOf(Position(one, 1)));

  EXPECT_EQ(Position(one->firstChild(), 0),
            CanonicalPositionOf(Position(one->firstChild(), 0)));
  EXPECT_EQ(Position(one->firstChild(), 1),
            CanonicalPositionOf(Position(one->firstChild(), 1)));

  EXPECT_EQ(Position(html, 0), CanonicalPositionOf(Position(html, 0)));
  EXPECT_EQ(Position(html, 1), CanonicalPositionOf(Position(html, 1)));
  EXPECT_EQ(Position(html, 2), CanonicalPositionOf(Position(html, 2)));

  EXPECT_EQ(Position(two->firstChild(), 0),
            CanonicalPositionOf(Position(two, 0)));
  EXPECT_EQ(Position(two->firstChild(), 2),
            CanonicalPositionOf(Position(two, 1)));
}

// For http://crbug.com/695317
TEST_F(VisibleUnitsTest, canonicalPositionOfWithInputElement) {
  SetBodyContent("<input>123");
  Element* const input = QuerySelector("input");

  EXPECT_EQ(Position::BeforeNode(*input),
            CanonicalPositionOf(Position::FirstPositionInNode(
                *GetDocument().documentElement())));

  EXPECT_EQ(PositionInFlatTree::BeforeNode(*input),
            CanonicalPositionOf(PositionInFlatTree::FirstPositionInNode(
                *GetDocument().documentElement())));
}

// http://crbug.com/1116214
TEST_F(VisibleUnitsTest, canonicalPositionOfWithCrossBlockFlowlement) {
  const char* body_content =
      "<div id=one>line1<span>X</span><div>line2</div></div>"
      "<div id=two>line3"
      "<span style='user-select: none'>X</span><div>line4</div></div>"
      "<div id=three>line5"
      "<span style='user-select: none'>X</span>333<div>line6</div></div>";
  SetBodyContent(body_content);

  UpdateAllLifecyclePhasesForTest();

  Element* const one = QuerySelector("#one");
  Element* const two = QuerySelector("#two");
  Element* const three = QuerySelector("#three");
  Element* const one_span = one->QuerySelector(AtomicString("span"));
  Element* const two_span = two->QuerySelector(AtomicString("span"));
  Element* const three_span = three->QuerySelector(AtomicString("span"));
  Position one_text_pos(one_span->firstChild(), 1);
  Position two_text_pos(two_span->firstChild(), 1);
  Position three_text_pos(three_span->firstChild(), 1);

  EXPECT_EQ(one_text_pos, CanonicalPositionOf(one_text_pos));

  EXPECT_EQ(Position::LastPositionInNode(*two->firstChild()),
            CanonicalPositionOf(two_text_pos));

  EXPECT_EQ(Position(*three->lastChild()->previousSibling(), 0),
            CanonicalPositionOf(three_text_pos));
}

TEST_F(VisibleUnitsTest, characterBefore) {
  const char* body_content =
      "<p id=host><b slot='#one' id=one>1</b><b slot='#two' "
      "id=two>22</b></p><b id=three>333</b>";
  const char* shadow_content =
      "<b id=four>4444</b><slot name='#two'></slot><slot name=#one></slot><b "
      "id=five>5555</b>";
  SetBodyContent(body_content);
  ShadowRoot* shadow_root = SetShadowContent(shadow_content, "host");

  Node* one = GetDocument().getElementById(AtomicString("one"))->firstChild();
  Node* two = GetDocument().getElementById(AtomicString("two"))->firstChild();
  Node* five = shadow_root->getElementById(AtomicString("five"))->firstChild();

  EXPECT_EQ('2', CharacterBefore(CreateVisiblePositionInDomTree(*one, 0)));
  EXPECT_EQ('2', CharacterBefore(CreateVisiblePositionInFlatTree(*one, 0)));

  EXPECT_EQ('1', CharacterBefore(CreateVisiblePositionInDomTree(*one, 1)));
  EXPECT_EQ('1', CharacterBefore(CreateVisiblePositionInFlatTree(*one, 1)));

  EXPECT_EQ(0, CharacterBefore(CreateVisiblePositionInDomTree(*two, 0)));
  EXPECT_EQ('4', CharacterBefore(CreateVisiblePositionInFlatTree(*two, 0)));

  EXPECT_EQ(0, CharacterBefore(CreateVisiblePositionInDomTree(*five, 0)));
  EXPECT_EQ('1', CharacterBefore(CreateVisiblePositionInFlatTree(*five, 0)));
}

TEST_F(VisibleUnitsTest, endOfDocument) {
  const char* body_content =
      "<span id=host><b slot='#one' id=one>1</b><b slot='#two' "
      "id=two>22</b></span>";
  const char* shadow_content =
      "<p><slot name='#two'></slot></p><p><slot name=#one></slot></p>";
  SetBodyContent(body_content);
  SetShadowContent(shadow_content, "host");

  Element* one = GetDocument().getElementById(AtomicString("one"));
  Element* two = GetDocument().getElementById(AtomicString("two"));

  EXPECT_EQ(Position(two->firstChild(), 2),
            EndOfDocument(CreateVisiblePositionInDomTree(*one->firstChild(), 0))
                .DeepEquivalent());
  EXPECT_EQ(
      PositionInFlatTree(one->firstChild(), 1),
      EndOfDocument(CreateVisiblePositionInFlatTree(*one->firstChild(), 0))
          .DeepEquivalent());

  EXPECT_EQ(Position(two->firstChild(), 2),
            EndOfDocument(CreateVisiblePositionInDomTree(*two->firstChild(), 1))
                .DeepEquivalent());
  EXPECT_EQ(
      PositionInFlatTree(one->firstChild(), 1),
      EndOfDocument(CreateVisiblePositionInFlatTree(*two->firstChild(), 1))
          .DeepEquivalent());
}

TEST_F(VisibleUnitsTest,
       AdjustForwardPositionToAvoidCrossingEditingBoundariesNestedEditable) {
  const SelectionInDomTree& selection = SetSelectionTextToBody(
      "<div contenteditable>"
      "abc"
      "<span contenteditable=\"false\">A^BC</span>"
      "d|ef"
      "</div>");
  const PositionWithAffinity& result =
      AdjustForwardPositionToAvoidCrossingEditingBoundaries(
          PositionWithAffinity(selection.Focus()), selection.Anchor());
  ASSERT_TRUE(result.IsNotNull());
  EXPECT_EQ(
      "<div contenteditable>"
      "abc"
      "<span contenteditable=\"false\">ABC|</span>"
      "def"
      "</div>",
      GetCaretTextFromBody(result.GetPosition()));
  EXPECT_EQ(TextAffinity::kDownstream, result.Affinity());
}

TEST_F(VisibleUnitsTest, isEndOfEditableOrNonEditableContent) {
  const char* body_content =
      "<span id=host><b slot='#one' id=one contenteditable>1</b><b slot='#two' "
      "id=two>22</b></span>";
  const char* shadow_content =
      "<slot name='#two'></slot></p><p><slot name='#one'></slot>";
  SetBodyContent(body_content);
  SetShadowContent(shadow_content, "host");

  Element* one = GetDocument().getElementById(AtomicString("one"));
  Element* two = GetDocument().getElementById(AtomicString("two"));

  EXPECT_FALSE(IsEndOfEditableOrNonEditableContent(
      CreateVisiblePositionInDomTree(*one->firstChild(), 1)));
  EXPECT_TRUE(IsEndOfEditableOrNonEditableContent(
      CreateVisiblePositionInFlatTree(*one->firstChild(), 1)));

  EXPECT_TRUE(IsEndOfEditableOrNonEditableContent(
      CreateVisiblePositionInDomTree(*two->firstChild(), 2)));
  EXPECT_FALSE(IsEndOfEditableOrNonEditableContent(
      CreateVisiblePositionInFlatTree(*two->firstChild(), 2)));
}

TEST_F(VisibleUnitsTest, isEndOfEditableOrNonEditableContentWithInput) {
  const char* body_content = "<input id=sample value=ab>cde";
  SetBodyContent(body_content);

  Node* text =
      ToTextControl(GetDocument().getElementById(AtomicString("sample")))
          ->InnerEditorElement()
          ->firstChild();

  EXPECT_FALSE(IsEndOfEditableOrNonEditableContent(
      CreateVisiblePositionInDomTree(*text, 0)));
  EXPECT_FALSE(IsEndOfEditableOrNonEditableContent(
      CreateVisiblePositionInFlatTree(*text, 0)));

  EXPECT_FALSE(IsEndOfEditableOrNonEditableContent(
      CreateVisiblePositionInDomTree(*text, 1)));
  EXPECT_FALSE(IsEndOfEditableOrNonEditableContent(
      CreateVisiblePositionInFlatTree(*text, 1)));

  EXPECT_TRUE(IsEndOfEditableOrNonEditableContent(
      CreateVisiblePositionInDomTree(*text, 2)));
  EXPECT_TRUE(IsEndOfEditableOrNonEditableContent(
      CreateVisiblePositionInFlatTree(*text, 2)));
}

TEST_F(VisibleUnitsTest, IsVisuallyEquivalentCandidateWithHTMLHtmlElement) {
  const char* body_content =
      "<html><div id=one contenteditable>1</div><span id=two "
      "contenteditable=false>22</span><span id=three "
      "contenteditable=false>333</span><span id=four "
      "contenteditable=false>333</span></html>";
  SetBodyContent(body_content);

  Node* one = QuerySelector("#one");
  Node* two = QuerySelector("#two");
  Node* three = QuerySelector("#three");
  Node* four = QuerySelector("#four");
  Element* html = GetDocument().CreateRawElement(html_names::kHTMLTag);
  // Move two, three and four into second html element.
  html->AppendChild(two);
  html->AppendChild(three);
  html->AppendChild(four);
  one->appendChild(html);
  UpdateAllLifecyclePhasesForTest();

  EXPECT_FALSE(IsVisuallyEquivalentCandidate(
      Position(GetDocument().documentElement(), 0)));

  EXPECT_FALSE(IsVisuallyEquivalentCandidate(Position(one, 0)));
  EXPECT_FALSE(IsVisuallyEquivalentCandidate(Position(one, 1)));

  EXPECT_TRUE(IsVisuallyEquivalentCandidate(Position(one->firstChild(), 0)));
  EXPECT_TRUE(IsVisuallyEquivalentCandidate(Position(one->firstChild(), 1)));

  EXPECT_TRUE(IsVisuallyEquivalentCandidate(Position(html, 0)));
  EXPECT_TRUE(IsVisuallyEquivalentCandidate(Position(html, 1)));
  EXPECT_TRUE(IsVisuallyEquivalentCandidate(Position(html, 2)));

  EXPECT_FALSE(IsVisuallyEquivalentCandidate(Position(two, 0)));
  EXPECT_FALSE(IsVisuallyEquivalentCandidate(Position(two, 1)));
}

TEST_F(VisibleUnitsTest, isVisuallyEquivalentCandidateWithHTMLBodyElement) {
  const char* body_content =
      "<div id=one contenteditable>1</div><span id=two "
      "contenteditable=false>22</span><span id=three "
      "contenteditable=false>333</span><span id=four "
      "contenteditable=false>333</span>";
  SetBodyContent(body_content);

  Node* one = QuerySelector("#one");
  Node* two = QuerySelector("#two");
  Node* three = QuerySelector("#three");
  Node* four = QuerySelector("#four");
  Element* body = GetDocument().CreateRawElement(html_names::kBodyTag);
  Element* empty_body = GetDocument().CreateRawElement(html_names::kBodyTag);
  Element* div = GetDocument().CreateRawElement(html_names::kDivTag);
  Element* br = GetDocument().CreateRawElement(html_names::kBrTag);
  empty_body->appendChild(div);
  empty_body->appendChild(br);
  one->appendChild(empty_body);
  // Move two, three and four into second body element.
  body->appendChild(two);
  body->AppendChild(three);
  body->AppendChild(four);
  one->appendChild(body);
  GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest);

  EXPECT_FALSE(IsVisuallyEquivalentCandidate(
      Position(GetDocument().documentElement(), 0)));

  EXPECT_FALSE(IsVisuallyEquivalentCandidate(Position(one, 0)));
  EXPECT_FALSE(IsVisuallyEquivalentCandidate(Position(one, 1)));

  EXPECT_TRUE(IsVisuallyEquivalentCandidate(Position(one->firstChild(), 0)));
  EXPECT_TRUE(IsVisuallyEquivalentCandidate(Position(one->firstChild(), 1)));

  EXPECT_TRUE(IsVisuallyEquivalentCandidate(Position(body, 0)));
  EXPECT_TRUE(IsVisuallyEquivalentCandidate(Position(body, 1)));
  EXPECT_TRUE(IsVisuallyEquivalentCandidate(Position(body, 2)));

  EXPECT_FALSE(IsVisuallyEquivalentCandidate(Position(two, 0)));
  EXPECT_FALSE(IsVisuallyEquivalentCandidate(Position(two, 1)));

  EXPECT_FALSE(IsVisuallyEquivalentCandidate(Position(empty_body, 0)));
  EXPECT_FALSE(IsVisuallyEquivalentCandidate(Position(empty_body, 1)));
}

TEST_F(VisibleUnitsTest, isVisuallyEquivalentCandidateWithDocument) {
  UpdateAllLifecyclePhasesForTest();

  EXPECT_FALSE(IsVisuallyEquivalentCandidate(Position(&GetDocument(), 0)));
}

TEST_F(VisibleUnitsTest, mostBackwardCaretPositionAfterAnchor) {
  const char* body_content =
      "<p id='host'><b id='one'>1</b></p><b id='two'>22</b>";
  const char* shadow_content =
      "<b id='two'>22</b><slot name='#one'></slot><b id='three'>333</b>";
  SetBodyContent(body_content);
  SetShadowContent(shadow_content, "host");

  Element* host = GetDocument().getElementById(AtomicString("host"));

  EXPECT_EQ(Position::LastPositionInNode(*host),
            MostForwardCaretPosition(Position::AfterNode(*host)));
  EXPECT_EQ(PositionInFlatTree::LastPositionInNode(*host),
            MostForwardCaretPosition(PositionInFlatTree::AfterNode(*host)));
}

TEST_F(VisibleUnitsTest, mostBackwardCaretPositionFirstLetter) {
  // Note: first-letter pseudo-element contains letter and punctuations.
  const char* body_content =
      "<style>p:first-letter {color:red;}</style><p id=sample> (2)45 </p>";
  SetBodyContent(body_content);

  Node* sample =
      GetDocument().getElementById(AtomicString("sample"))->firstChild();

  EXPECT_EQ(Position(sample->parentNode(), 0),
            MostBackwardCaretPosition(Position(sample, 0)));
  EXPECT_EQ(Position(sample->parentNode(), 0),
            MostBackwardCaretPosition(Position(sample, 1)));
  EXPECT_EQ(Position(sample, 2),
            MostBackwardCaretPosition(Position(sample, 2)));
  EXPECT_EQ(Position(sample, 3),
            MostBackwardCaretPosition(Position(sample, 3)));
  EXPECT_EQ(Position(sample, 4),
            MostBackwardCaretPosition(Position(sample, 4)));
  EXPECT_EQ(Position(sample, 5),
            MostBackwardCaretPosition(Position(sample, 5)));
  EXPECT_EQ(Position(sample, 6),
            MostBackwardCaretPosition(Position(sample, 6)));
  EXPECT_EQ(Position(sample, 6),
            MostBackwardCaretPosition(Position(sample, 7)));
  EXPECT_EQ(Position(sample, 6),
            MostBackwardCaretPosition(
                Position::LastPositionInNode(*sample->parentNode())));
  EXPECT_EQ(
      Position(sample, 6),
      MostBackwardCaretPosition(Position::AfterNode(*sample->parentNode())));
  EXPECT_EQ(Position::LastPositionInNode(*GetDocument().body()),
            MostBackwardCaretPosition(
                Position::LastPositionInNode(*GetDocument().body())));
}

TEST_F(VisibleUnitsTest, mostBackwardCaretPositionFirstLetterSplit) {
  V8TestingScope scope;

  const char* body_content =
      "<style>p:first-letter {color:red;}</style><p id=sample>abc</p>";
  SetBodyContent(body_content);

  Node* sample = GetDocument().getElementById(AtomicString("sample"));
  Node* first_letter = sample->firstChild();
  // Split "abc" into "a" "bc"
  auto* remaining = To<Text>(first_letter)->splitText(1, ASSERT_NO_EXCEPTION);
  UpdateAllLifecyclePhasesForTest();

  EXPECT_EQ(Position(sample, 0),
            MostBackwardCaretPosition(Position(first_letter, 0)));
  EXPECT_EQ(Position(first_letter, 1),
            MostBackwardCaretPosition(Position(first_letter, 1)));
  EXPECT_EQ(Position(first_letter, 1),
            MostBackwardCaretPosition(Position(remaining, 0)));
  EXPECT_EQ(Position(remaining, 1),
            MostBackwardCaretPosition(Position(remaining, 1)));
  EXPECT_EQ(Position(remaining, 2),
            MostBackwardCaretPosition(Position(remaining, 2)));
  EXPECT_EQ(Position(remaining, 2),
            MostBackwardCaretPosition(Position::LastPositionInNode(*sample)));
  EXPECT_EQ(Position(remaining, 2),
            MostBackwardCaretPosition(Position::AfterNode(*sample)));
}

TEST_F(VisibleUnitsTest, mostForwardCaretPositionAfterAnchor) {
  const char* body_content = "<p id='host'><b id='one'>1</b></p>";
  const char* shadow_content =
      "<b id='two'>22</b><slot name='#one'></slot><b id='three'>333</b>";
  SetBodyContent(body_content);
  ShadowRoot* shadow_root = SetShadowContent(shadow_content, "host");
  UpdateAllLifecyclePhasesForTest();

  Element* host = GetDocument().getElementById(AtomicString("host"));
  Element* three = shadow_root->getElementById(AtomicString("three"));

  EXPECT_EQ(Position::AfterNode(*host),
            MostBackwardCaretPosition(Position::AfterNode(*host)));
  EXPECT_EQ(PositionInFlatTree(three->firstChild(), 3),
            MostBackwardCaretPosition(PositionInFlatTree::AfterNode(*host)));
}

// http://crbug.com/1348816
TEST_F(VisibleUnitsTest, MostBackwardCaretPositionBeforeSvg) {
  EXPECT_EQ(
      "<div>A<svg><foreignObject height=\"10\" width=\"20\">| "
      "Z</foreignObject></svg></div>",
      TestSnapBackward("<div>A<svg><foreignObject height=10 width=20> "
                       "|Z</foreignObject></svg></div>"));
}

// http://crbug.com/1348816
TEST_F(VisibleUnitsTest, MostForwardCaretPositionBeforeSvg) {
  EXPECT_EQ(
      "<div>A|<svg><foreignObject height=\"10\" width=\"20\"> "
      "Z</foreignObject></svg></div>",
      TestSnapForward("<div>A|<svg><foreignObject height=10 width=20> "
                      "Z</foreignObject></svg></div>"));

  EXPECT_EQ(
      "<div>A<svg><foreignObject height=\"10\" width=\"20\"> "
      "|Z</foreignObject></svg></div>",
      TestSnapForward("<div>A<svg><foreignObject height=10 width=20>| "
                      "Z</foreignObject></svg></div>"));
}

TEST_F(VisibleUnitsTest, mostForwardCaretPositionFirstLetter) {
  // Note: first-letter pseudo-element contains letter and punctuations.
  const char* body_content =
      "<style>p:first-letter {color:red;}</style><p id=sample> (2)45 </p>";
  SetBodyContent(body_content);

  Node* sample =
      GetDocument().getElementById(AtomicString("sample"))->firstChild();

  EXPECT_EQ(Position(GetDocument().body(), 0),
            MostForwardCaretPosition(
                Position::FirstPositionInNode(*GetDocument().body())));
  EXPECT_EQ(
      Position(sample, 1),
      MostForwardCaretPosition(Position::BeforeNode(*sample->parentNode())));
  EXPECT_EQ(Position(sample, 1),
            MostForwardCaretPosition(
                Position::FirstPositionInNode(*sample->parentNode())));
  EXPECT_EQ(Position(sample, 1), MostForwardCaretPosition(Position(sample, 0)));
  EXPECT_EQ(Position(sample, 1), MostForwardCaretPosition(Position(sample, 1)));
  EXPECT_EQ(Position(sample, 2), MostForwardCaretPosition(Position(sample, 2)));
  EXPECT_EQ(Position(sample, 3), MostForwardCaretPosition(Position(sample, 3)));
  EXPECT_EQ(Position(sample, 4), MostForwardCaretPosition(Position(sample, 4)));
  EXPECT_EQ(Position(sample, 5), MostForwardCaretPosition(Position(sample, 5)));
  EXPECT_EQ(Position(sample, 7), MostForwardCaretPosition(Position(sample, 6)));
  EXPECT_EQ(Position(sample, 7), MostForwardCaretPosition(Position(sample, 7)));
}

TEST_F(VisibleUnitsTest, nextPositionOf) {
  const char* body_content =
      "<b id=zero>0</b><p id=host><b slot='#one' id=one>1</b><b slot='#two' "
      "id=two>22</b></p><b "
      "id=three>333</b>";
  const char* shadow_content =
      "<b id=four>4444</b><slot name='#two'></slot><slot name=#one></slot><b "
      "id=five>55555</b>";
  SetBodyContent(body_content);
  ShadowRoot* shadow_root = SetShadowContent(shadow_content, "host");

  Element* zero = GetDocument().getElementById(AtomicString("zero"));
  Element* one = GetDocument().getElementById(AtomicString("one"));
  Element* two = GetDocument().getElementById(AtomicString("two"));
  Element* three = GetDocument().getElementById(AtomicString("three"));
  Element* four = shadow_root->getElementById(AtomicString("four"));
  Element* five = shadow_root->getElementById(AtomicString("five"));

  EXPECT_EQ(Position(two->firstChild(), 2),
            NextPositionOf(CreateVisiblePosition(Position(zero, 1)))
                .DeepEquivalent());
  EXPECT_EQ(PositionInFlatTree(four->firstChild(), 0),
            NextPositionOf(CreateVisiblePosition(PositionInFlatTree(zero, 1)))
                .DeepEquivalent());

  EXPECT_EQ(Position(three->firstChild(), 0),
            NextPositionOf(CreateVisiblePosition(Position(one, 0),
                                                 TextAffinity::kUpstream))
                .DeepEquivalent());
  EXPECT_EQ(PositionInFlatTree(one->firstChild(), 1),
            NextPositionOf(CreateVisiblePosition(PositionInFlatTree(one, 0)))
                .DeepEquivalent());

  EXPECT_EQ(Position(two->firstChild(), 0),
            NextPositionOf(CreateVisiblePosition(Position(one, 1),
                                                 TextAffinity::kUpstream))
                .DeepEquivalent());
  EXPECT_EQ(PositionInFlatTree(five->firstChild(), 1),
            NextPositionOf(CreateVisiblePosition(PositionInFlatTree(one, 1)))
                .DeepEquivalent());

  EXPECT_EQ(
      Position(three->firstChild(), 0),
      NextPositionOf(CreateVisiblePosition(Position(two, 1))).DeepEquivalent());
  EXPECT_EQ(PositionInFlatTree(one->firstChild(), 1),
            NextPositionOf(CreateVisiblePosition(PositionInFlatTree(two, 1)))
                .DeepEquivalent());
}

TEST_F(VisibleUnitsTest, nextPositionOfTable) {
  SetBodyContent("<table id='table'></table>");
  Element* table = GetDocument().getElementById(AtomicString("table"));
  // Couldn't include the <br> in the HTML above since the parser would have
  // messed up the structure in the DOM.
  table->SetInnerHTMLWithoutTrustedTypes("<br>");
  UpdateAllLifecyclePhasesForTest();

  Position position(table, 0);
  Position next =
      NextPositionOf(CreateVisiblePosition(position)).DeepEquivalent();
  EXPECT_NE(position, next);
  EXPECT_NE(MostBackwardCaretPosition(position),
            MostBackwardCaretPosition(next));
  EXPECT_NE(MostForwardCaretPosition(position), MostForwardCaretPosition(next));
}

TEST_F(VisibleUnitsTest, previousPositionOf) {
  const char* body_content =
      "<b id=zero>0</b><p id=host><b slot='#one' id=one>1</b><b slot='#two' "
      "id=two>22</b></p><b id=three>333</b>";
  const char* shadow_content =
      "<b id=four>4444</b><slot name='#two'></slot><slot name=#one></slot><b "
      "id=five>55555</b>";
  SetBodyContent(body_content);
  ShadowRoot* shadow_root = SetShadowContent(shadow_content, "host");

  Node* zero = GetDocument().getElementById(AtomicString("zero"))->firstChild();
  Node* one = GetDocument().getElementById(AtomicString("one"))->firstChild();
  Node* two = GetDocument().getElementById(AtomicString("two"))->firstChild();
  Node* three =
      GetDocument().getElementById(AtomicString("three"))->firstChild();
  Node* four = shadow_root->getElementById(AtomicString("four"))->firstChild();
  Node* five = shadow_root->getElementById(AtomicString("five"))->firstChild();

  EXPECT_EQ(Position(zero, 0),
            PreviousPositionOf(CreateVisiblePosition(Position(zero, 1)))
                .DeepEquivalent());
  EXPECT_EQ(
      PositionInFlatTree(zero, 0),
      PreviousPositionOf(CreateVisiblePosition(PositionInFlatTree(zero, 1)))
          .DeepEquivalent());

  EXPECT_EQ(Position(two, 1),
            PreviousPositionOf(CreateVisiblePosition(Position(one, 0)))
                .DeepEquivalent());
  EXPECT_EQ(
      PositionInFlatTree(two, 1),
      PreviousPositionOf(CreateVisiblePosition(PositionInFlatTree(one, 0)))
          .DeepEquivalent());

  EXPECT_EQ(Position(two, 2),
            PreviousPositionOf(CreateVisiblePosition(Position(one, 1)))
                .DeepEquivalent());
  EXPECT_EQ(
      PositionInFlatTree(two, 2),
      PreviousPositionOf(CreateVisiblePosition(PositionInFlatTree(one, 1)))
          .DeepEquivalent());

  EXPECT_EQ(Position(one, 1),
            PreviousPositionOf(CreateVisiblePosition(Position(two, 0)))
                .DeepEquivalent());
  EXPECT_EQ(
      PositionInFlatTree(four, 3),
      PreviousPositionOf(CreateVisiblePosition(PositionInFlatTree(two, 0)))
          .DeepEquivalent());

  // DOM tree to shadow tree
  EXPECT_EQ(Position(two, 2),
            PreviousPositionOf(CreateVisiblePosition(Position(three, 0)))
                .DeepEquivalent());
  EXPECT_EQ(
      PositionInFlatTree(five, 5),
      PreviousPositionOf(CreateVisiblePosition(PositionInFlatTree(three, 0)))
          .DeepEquivalent());

  // Shadow tree to DOM tree
  EXPECT_EQ(Position(),
            PreviousPositionOf(CreateVisiblePosition(Position(four, 0)))
                .DeepEquivalent());
  EXPECT_EQ(
      PositionInFlatTree(zero, 1),
      PreviousPositionOf(CreateVisiblePosition(PositionInFlatTree(four, 0)))
          .DeepEquivalent());

  // Note: Canonicalization maps (five, 0) to (five, 0) in DOM tree and
  // (one, 1) in flat tree.
  EXPECT_EQ(Position(five, 0),
            PreviousPositionOf(CreateVisiblePosition(Position(five, 1)))
                .DeepEquivalent());
  EXPECT_EQ(
      PositionInFlatTree(one, 1),
      PreviousPositionOf(CreateVisiblePosition(PositionInFlatTree(five, 1)))
          .DeepEquivalent());
}

TEST_F(VisibleUnitsTest, previousPositionOfOneCharPerLine) {
  const char* body_content =
      "<div id=sample style='font-size: 500px'>A&#x714a;&#xfa67;</div>";
  SetBodyContent(body_content);

  Node* sample =
      GetDocument().getElementById(AtomicString("sample"))->firstChild();

  // In case of each line has one character, VisiblePosition are:
  // [C,Dn]   [C,Up]  [B, Dn]   [B, Up]
  //  A        A       A         A|
  //  B        B|     |B         B
  // |C        C       C         C
  EXPECT_EQ(PositionWithAffinity(Position(sample, 1)),
            PreviousPositionOf(CreateVisiblePosition(Position(sample, 2)))
                .ToPositionWithAffinity());
  EXPECT_EQ(PositionWithAffinity(Position(sample, 1)),
            PreviousPositionOf(CreateVisiblePosition(Position(sample, 2),
                                                     TextAffinity::kUpstream))
                .ToPositionWithAffinity());
}

TEST_F(VisibleUnitsTest, previousPositionOfNoPreviousPosition) {
  SetBodyContent(
      "<span contenteditable='true'>"
      "<span> </span>"
      " "  // This whitespace causes no previous position.
      "<div id='anchor'> bar</div>"
      "</span>");
  const Position position(
      GetDocument().getElementById(AtomicString("anchor"))->firstChild(), 1);
  EXPECT_EQ(
      Position(),
      PreviousPositionOf(CreateVisiblePosition(position)).DeepEquivalent());
}

TEST_F(VisibleUnitsTest, rendersInDifferentPositionAfterAnchor) {
  const char* body_content = "<p id='sample'>00</p>";
  SetBodyContent(body_content);
  Element* sample = GetDocument().getElementById(AtomicString("sample"));

  EXPECT_FALSE(RendersInDifferentPosition(Position(), Position()));
  EXPECT_FALSE(
      RendersInDifferentPosition(Position(), Position::AfterNode(*sample)))
      << "if one of position is null, the reuslt is false.";
  EXPECT_FALSE(RendersInDifferentPosition(Position::AfterNode(*sample),
                                          Position(sample, 1)));
  EXPECT_FALSE(RendersInDifferentPosition(Position::LastPositionInNode(*sample),
                                          Position(sample, 1)));
}

TEST_F(VisibleUnitsTest, rendersInDifferentPositionAfterAnchorWithHidden) {
  const char* body_content =
      "<p><span id=one>11</span><span id=two style='display:none'>  "
      "</span></p>";
  SetBodyContent(body_content);
  Element* one = GetDocument().getElementById(AtomicString("one"));
  Element* two = GetDocument().getElementById(AtomicString("two"));

  EXPECT_TRUE(RendersInDifferentPosition(Position::LastPositionInNode(*one),
                                         Position(two, 0)))
      << "two doesn't have layout object";
}

TEST_F(VisibleUnitsTest,
       rendersInDifferentPositionAfterAnchorWithDifferentLayoutObjects) {
  const char* body_content =
      "<p><span id=one>11</span><span id=two>  </span></p>";
  SetBodyContent(body_content);
  Element* one = GetDocument().getElementById(AtomicString("one"));
  Element* two = GetDocument().getElementById(AtomicString("two"));

  EXPECT_FALSE(RendersInDifferentPosition(Position::LastPositionInNode(*one),
                                          Position(two, 0)));
  EXPECT_FALSE(RendersInDifferentPosition(Position::LastPositionInNode(*one),
                                          Position(two, 1)))
      << "width of two is zero since contents is collapsed whitespaces";
}

TEST_F(VisibleUnitsTest, renderedOffset) {
  const char* body_content =
      "<div contenteditable><span id='sample1'>1</span><span "
      "id='sample2'>22</span></div>";
  SetBodyContent(body_content);
  Element* sample1 = GetDocument().getElementById(AtomicString("sample1"));
  Element* sample2 = GetDocument().getElementById(AtomicString("sample2"));

  EXPECT_FALSE(
      RendersInDifferentPosition(Position::AfterNode(*sample1->firstChild()),
                                 Position(sample2->firstChild(), 0)));
  EXPECT_FALSE(RendersInDifferentPosition(
      Position::LastPositionInNode(*sample1->firstChild()),
      Position(sample2->firstChild(), 0)));
}

TEST_F(VisibleUnitsTest, startOfDocument) {
  const char* body_content =
      "<span id=host><b slot='#one' id=one>1</b><b slot='#two' "
      "id=two>22</b></span>";
  const char* shadow_content =
      "<p><slot name='#two'></slot></p><p><slot name=#one></slot></p>";
  SetBodyContent(body_content);
  SetShadowContent(shadow_content, "host");

  Node* one = GetDocument().getElementById(AtomicString("one"))->firstChild();
  Node* two = GetDocument().getElementById(AtomicString("two"))->firstChild();

  EXPECT_EQ(Position(one, 0),
            CreateVisiblePosition(StartOfDocument(Position(*one, 0)))
                .DeepEquivalent());
  EXPECT_EQ(PositionInFlatTree(two, 0),
            CreateVisiblePosition(StartOfDocument(PositionInFlatTree(*one, 0)))
                .DeepEquivalent());

  EXPECT_EQ(Position(one, 0),
            CreateVisiblePosition(StartOfDocument(Position(*two, 1)))
                .DeepEquivalent());
  EXPECT_EQ(PositionInFlatTree(two, 0),
            CreateVisiblePosition(StartOfDocument(PositionInFlatTree(*two, 1)))
                .DeepEquivalent());
}

TEST_F(VisibleUnitsTest,
       endsOfNodeAreVisuallyDistinctPositionsWithInvisibleChild) {
  // Repro case of crbug.com/582247
  const char* body_content =
      "<button> </button><script>document.designMode = 'on'</script>";
  SetBodyContent(body_content);

  Node* button = QuerySelector("button");
  EXPECT_TRUE(EndsOfNodeAreVisuallyDistinctPositions(button));
}

TEST_F(VisibleUnitsTest,
       endsOfNodeAreVisuallyDistinctPositionsWithEmptyLayoutChild) {
  // Repro case of crbug.com/584030
  const char* body_content =
      "<button><rt><script>document.designMode = 'on'</script></rt></button>";
  SetBodyContent(body_content);

  Node* button = QuerySelector("button");
  EXPECT_TRUE(EndsOfNodeAreVisuallyDistinctPositions(button));
}

// Regression test for crbug.com/675429
TEST_F(VisibleUnitsTest,
       canonicalizationWithCollapsedSpaceAndIsolatedCombiningCharacter) {
  SetBodyContent("<p>  &#x20E3;</p>");  // Leading space is necessary

  Node* paragraph = QuerySelector("p");
  Node* text = paragraph->firstChild();
  Position start = CanonicalPositionOf(Position::BeforeNode(*paragraph));
  EXPECT_EQ(Position(text, 2), start);
}

TEST_F(VisibleUnitsTest, MostForwardCaretPositionWithInvisibleFirstLetter) {
  InsertStyleElement("div::first-letter{visibility:hidden}");
  // Use special syntax to set input position DIV@0
  const Position position = SetCaretTextToBody("<div><!--|-->foo</div>");
  const Node* foo = QuerySelector("div")->firstChild();
  EXPECT_EQ(Position(foo, 1), MostForwardCaretPosition(position));
}

// Regression test for crbug.com/1172091
TEST_F(VisibleUnitsTest, MostBackwardOrForwardCaretPositionWithBrInOptgroup) {
  SetBodyContent("<optgroup><br></optgroup>");
  Node* br = QuerySelector("br");
  const Position& before = Position::BeforeNode(*br);
  EXPECT_EQ(before, MostBackwardCaretPosition(before));
  EXPECT_EQ(before, MostForwardCaretPosition(before));
  const Position& after = Position::AfterNode(*br);
  EXPECT_EQ(after, MostBackwardCaretPosition(after));
  EXPECT_EQ(after, MostForwardCaretPosition(after));
}

// http://crbug.com/1134470
TEST_F(VisibleUnitsTest, SnapBackwardWithZeroWidthSpace) {
  // Note: We should skip <wbr> otherwise caret stops before/after <wbr>.

  EXPECT_EQ("<p>ab|<wbr></p>", TestSnapBackward("<p>ab<wbr>|</p>"));
  EXPECT_EQ("<p>ab\u200B|</p>", TestSnapBackward("<p>ab\u200B|</p>"));
  EXPECT_EQ("<p>ab<!-- -->\u200B|</p>",
            TestSnapBackward("<p>ab<!-- -->\u200B|</p>"));

  EXPECT_EQ("<p>ab|<wbr><wbr></p>", TestSnapBackward("<p>ab<wbr><wbr>|</p>"));
  EXPECT_EQ("<p>ab\u200B\u200B|</p>",
            TestSnapBackward("<p>ab\u200B\u200B|</p>"));

  EXPECT_EQ("<p>ab|<wbr>cd</p>", TestSnapBackward("<p>ab<wbr>|cd</p>"));
  EXPECT_EQ("<p>ab\u200B|cd</p>", TestSnapBackward("<p>ab\u200B|cd</p>"));

  EXPECT_EQ("<p>ab|<wbr><wbr>cd</p>",
            TestSnapBackward("<p>ab<wbr><wbr>|cd</p>"));
  EXPECT_EQ("<p>ab\u200B\u200B|cd</p>",
            TestSnapBackward("<p>ab\u200B\u200B|cd</p>"));
}
TEST_F(VisibleUnitsTest, SnapForwardWithImg) {
  SetBodyContent("<img>");
  const auto& body = *GetDocument().body();
  const auto& img = *QuerySelector("img");

  EXPECT_EQ(Position::BeforeNode(img),
            MostForwardCaretPosition(Position::FirstPositionInNode(body)));
  EXPECT_EQ(Position::BeforeNode(img),
            MostForwardCaretPosition(Position(body, 0)));
  EXPECT_EQ(Position::BeforeNode(img),
            MostForwardCaretPosition(Position::BeforeNode(img)));
  EXPECT_EQ(Position::BeforeNode(img),
            MostForwardCaretPosition(Position(img, 0)));
  EXPECT_EQ(Position::AfterNode(img),
            MostForwardCaretPosition(Position::LastPositionInNode(img)));
  EXPECT_EQ(Position::AfterNode(img),
            MostForwardCaretPosition(Position::AfterNode(img)));
}

TEST_F(VisibleUnitsTest, SnapForwardWithInput) {
  SetBodyContent("<input>");
  const auto& body = *GetDocument().body();
  const auto& input = *QuerySelector("input");

  EXPECT_EQ(Position::BeforeNode(input),
            MostForwardCaretPosition(Position::FirstPositionInNode(body)));
  EXPECT_EQ(Position::BeforeNode(input),
            MostForwardCaretPosition(Position(body, 0)));
  EXPECT_EQ(Position::BeforeNode(input),
            MostForwardCaretPosition(Position::BeforeNode(input)));
  EXPECT_EQ(Position::BeforeNode(input),
            MostForwardCaretPosition(Position::FirstPositionInNode(input)));
  EXPECT_EQ(Position::BeforeNode(input),
            MostForwardCaretPosition(Position(input, 0)));
  EXPECT_EQ(Position::AfterNode(input),
            MostForwardCaretPosition(Position::LastPositionInNode(input)));
  EXPECT_EQ(Position::AfterNode(input),
            MostForwardCaretPosition(Position::AfterNode(input)));
}

TEST_F(VisibleUnitsTest, SnapForwardWithSelect) {
  SetBodyContent(
      "<select><option>1</option><option>2</option><option>3</option></"
      "select>");
  const auto& body = *GetDocument().body();
  const auto& select = *QuerySelector("select");

  EXPECT_EQ(Position::BeforeNode(select),
            MostForwardCaretPosition(Position(body, 0)));
  EXPECT_EQ(Position::BeforeNode(select),
            MostForwardCaretPosition(Position::FirstPositionInNode(body)));
  EXPECT_EQ(Position::BeforeNode(select),
            MostForwardCaretPosition(Position::BeforeNode(select)));
  EXPECT_EQ(Position::BeforeNode(select),
            MostForwardCaretPosition(Position::FirstPositionInNode(select)));
  EXPECT_EQ(Position::BeforeNode(select),
            MostForwardCaretPosition(Position(select, 0)));

  // The internal version of `MostForwardCaretPosition()` is called with
  // `PositionInFlatTree(slot, 1)` and it scans at end of `<select>` then
  // returns `PositionInFlatTree(slot, 1)` and converts to
  // `Position(select, 1)`.
  EXPECT_EQ(Position(select, 1), MostForwardCaretPosition(Position(select, 1)));
  EXPECT_EQ(Position(select, 2), MostForwardCaretPosition(Position(select, 2)));
  EXPECT_EQ(Position::AfterNode(select),
            MostForwardCaretPosition(Position(select, 3)));
  EXPECT_EQ(Position::AfterNode(select),
            MostForwardCaretPosition(Position::LastPositionInNode(select)));
  EXPECT_EQ(Position::AfterNode(select),
            MostForwardCaretPosition(Position::AfterNode(select)));

  // Flat tree is
  //  <select>
  //    <div>""</div>
  //    <slot><option>1</option><option>2</option></slot>
  //  </select>
  EXPECT_EQ(PositionInFlatTree::BeforeNode(select),
            MostForwardCaretPosition(PositionInFlatTree(body, 0)));
  EXPECT_EQ(
      PositionInFlatTree::BeforeNode(select),
      MostForwardCaretPosition(PositionInFlatTree::FirstPositionInNode(body)));
  EXPECT_EQ(PositionInFlatTree::BeforeNode(select),
            MostForwardCaretPosition(PositionInFlatTree::BeforeNode(select)));

  // Note: `PositionIterator::DeprecatedComputePosition()` returns
  // `BeforeNode(<select>)` for <select>@n where n is 0 to 3, because
  // `EditingIgnoresContent(<select>)` is true.
  EXPECT_EQ(PositionInFlatTree::BeforeNode(select),
            MostForwardCaretPosition(
                PositionInFlatTree::FirstPositionInNode(select)));
  EXPECT_EQ(PositionInFlatTree::BeforeNode(select),
            MostForwardCaretPosition(PositionInFlatTree(select, 0)));
  EXPECT_EQ(PositionInFlatTree::BeforeNode(select),
            MostForwardCaretPosition(PositionInFlatTree(select, 1)));
  EXPECT_EQ(PositionInFlatTree::BeforeNode(select),
            MostForwardCaretPosition(PositionInFlatTree(select, 2)));
  EXPECT_EQ(PositionInFlatTree::BeforeNode(select),
            MostForwardCaretPosition(PositionInFlatTree(select, 3)));
  EXPECT_EQ(PositionInFlatTree::AfterNode(select),
            MostForwardCaretPosition(PositionInFlatTree(select, 4)));

  EXPECT_EQ(
      PositionInFlatTree::AfterNode(select),
      MostForwardCaretPosition(PositionInFlatTree::LastPositionInNode(select)));
  EXPECT_EQ(PositionInFlatTree::AfterNode(select),
            MostForwardCaretPosition(PositionInFlatTree::AfterNode(select)));
}

// From ReplaceSelectionCommandTest.TableAndImages)
TEST_F(VisibleUnitsTest, SnapForwardWithTableAndImages) {
  SetBodyContent("<table> <tbody></tbody> </table>");
  const auto& table = *QuerySelector("table");
  const auto& body = *GetDocument().body();
  auto& tbody = *QuerySelector("tbody");
  auto& img1 = *GetDocument().CreateRawElement(html_names::kImgTag);
  tbody.AppendChild(&img1);
  auto& img2 = *GetDocument().CreateRawElement(html_names::kImgTag);
  tbody.AppendChild(&img2);
  UpdateAllLifecyclePhasesForTest();

  EXPECT_EQ(Position(body, 0), MostForwardCaretPosition(Position(body, 0)));
  EXPECT_EQ(Position(body, 0),
            MostForwardCaretPosition(Position::FirstPositionInNode(body)));
  EXPECT_EQ(Position(table, 0),
            MostForwardCaretPosition(Position::BeforeNode(table)));
  EXPECT_EQ(Position(table, 0),
            MostForwardCaretPosition(Position::FirstPositionInNode(table)));
  EXPECT_EQ(Position(table, 0), MostForwardCaretPosition(Position(table, 0)));
  EXPECT_EQ(Position(table, 1), MostForwardCaretPosition(Position(table, 1)));
  EXPECT_EQ(Position::BeforeNode(img1),
            MostForwardCaretPosition(Position::BeforeNode(tbody)));
  EXPECT_EQ(Position::BeforeNode(img1),
            MostForwardCaretPosition(Position(tbody, 0)));
  EXPECT_EQ(Position::BeforeNode(img1),
            MostForwardCaretPosition(Position::FirstPositionInNode(tbody)));
  EXPECT_EQ(Position::BeforeNode(img2),
            MostForwardCaretPosition(Position(tbody, 1)));
  EXPECT_EQ(Position::LastPositionInNode(tbody),
            MostForwardCaretPosition(Position(tbody, 2)));
  EXPECT_EQ(Position::LastPositionInNode(tbody),
            MostForwardCaretPosition(Position::LastPositionInNode(tbody)));
  EXPECT_EQ(Position::LastPositionInNode(tbody),
            MostForwardCaretPosition(Position::AfterNode(tbody)));
  EXPECT_EQ(Position(table, 2), MostForwardCaretPosition(Position(table, 2)));
  EXPECT_EQ(Position::LastPositionInNode(table),
            MostForwardCaretPosition(Position(table, 3)));
  EXPECT_EQ(Position::LastPositionInNode(table),
            MostForwardCaretPosition(Position::LastPositionInNode(table)));
  EXPECT_EQ(Position::LastPositionInNode(table),
            MostForwardCaretPosition(Position::AfterNode(table)));
}

// http://crbug.com/1134470
TEST_F(VisibleUnitsTest, SnapForwardWithZeroWidthSpace) {
  // Note: We should skip <wbr> otherwise caret stops before/after <wbr>.

  EXPECT_EQ("<p>ab<wbr></p>", TestSnapForward("<p>ab|<wbr></p>"))
      << "We get <wbr>@0";
  EXPECT_EQ("<p>ab|\u200B</p>", TestSnapForward("<p>ab|\u200B</p>"));
  EXPECT_EQ("<p>ab<!-- -->|\u200B</p>",
            TestSnapForward("<p>ab<!-- -->|\u200B</p>"));

  EXPECT_EQ("<p>ab<wbr><wbr></p>", TestSnapForward("<p>ab|<wbr><wbr></p>"))
      << "We get <wbr>@0";
  EXPECT_EQ("<p>ab|\u200B\u200B</p>",
            TestSnapForward("<p>ab|\u200B\u200B</p>"));

  EXPECT_EQ("<p>ab<wbr>|cd</p>", TestSnapForward("<p>ab|<wbr>cd</p>"));
  EXPECT_EQ("<p>ab|\u200Bcd</p>", TestSnapForward("<p>ab|\u200Bcd</p>"));

  EXPECT_EQ("<p>ab<wbr><wbr>|cd</p>",
            TestSnapForward("<p>ab|<wbr><wbr>cd</p>"));
  EXPECT_EQ("<p>ab|\u200B\u200Bcd</p>",
            TestSnapForward("<p>ab|\u200B\u200Bcd</p>"));
}

TEST_F(VisibleUnitsTest, FirstRectForRangeHorizontal) {
  LoadAhem();
  InsertStyleElement("div { font:20px/20px Ahem;}");
  const SelectionInDomTree selection =
      SetSelectionTextToBody("<div>^abcdef|</div>");
  const gfx::Rect rect = FirstRectForRange(selection.ComputeRange());
  EXPECT_EQ(gfx::Rect(8, 8, 120, 20), rect);
}

TEST_F(VisibleUnitsTest, FirstRectForRangeHorizontalWrap) {
  LoadAhem();
  InsertStyleElement("div { font:20px/20px Ahem; inline-size:60px;}");
  const SelectionInDomTree selection =
      SetSelectionTextToBody("<div>^abc def|</div>");
  const gfx::Rect rect = FirstRectForRange(selection.ComputeRange());
  EXPECT_EQ(gfx::Rect(8, 8, 59, 20), rect);
}

TEST_F(VisibleUnitsTest, FirstRectForRangeVertical) {
  LoadAhem();
  InsertStyleElement("div { writing-mode:vertical-rl; font:20px/20px Ahem;}");
  const SelectionInDomTree selection =
      SetSelectionTextToBody("<div>^abcdef|</div>");
  const gfx::Rect rect = FirstRectForRange(selection.ComputeRange());
  EXPECT_EQ(gfx::Rect(8, 8, 20, 119), rect);
}

TEST_F(VisibleUnitsTest, FirstRectForRangeVerticalWrap) {
  LoadAhem();
  InsertStyleElement(
      "div { writing-mode:vertical-rl; font:20px/20px Ahem; "
      "inline-size:60px;}");
  const SelectionInDomTree selection =
      SetSelectionTextToBody("<div>^abc def|</div>");
  const gfx::Rect rect = FirstRectForRange(selection.ComputeRange());
  EXPECT_EQ(gfx::Rect(28, 8, 20, 59), rect);
}

// crbug.com/402791086
TEST_F(VisibleUnitsTest, ComputeTextRect) {
  LoadAhem();
  InsertStyleElement("div { font:10px Ahem; white-space:pre}");
  const gfx::Rect rect = ComputeTextRect(
      SetSelectionTextToBody("<div>^start<br>end|</div>").ComputeRange());
  const gfx::Rect reference = ComputeTextRect(
      SetSelectionTextToBody("<div>^start\nend|</div>").ComputeRange());
  EXPECT_EQ(reference, rect);
}

}  // namespace visible_units_test
}  // namespace blink
