blob: 33277ece0fa59f7520fd02d1bbc29e199b8d9bbb [file] [log] [blame]
// Copyright 2014 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/dom/range.h"
#include "base/memory/scoped_refptr.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_font_face_descriptors.h"
#include "third_party/blink/renderer/core/css/font_face_set_document.h"
#include "third_party/blink/renderer/core/dom/element.h"
#include "third_party/blink/renderer/core/dom/node_list.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/frame_selection.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_units.h"
#include "third_party/blink/renderer/core/frame/settings.h"
#include "third_party/blink/renderer/core/html/forms/html_text_area_element.h"
#include "third_party/blink/renderer/core/html/html_body_element.h"
#include "third_party/blink/renderer/core/html/html_div_element.h"
#include "third_party/blink/renderer/core/html/html_document.h"
#include "third_party/blink/renderer/core/html/html_element.h"
#include "third_party/blink/renderer/core/html/html_html_element.h"
#include "third_party/blink/renderer/core/testing/null_execution_context.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
#include "ui/gfx/geometry/quad_f.h"
#include "ui/gfx/geometry/rect_conversions.h"
namespace blink {
using ::testing::ElementsAre;
class RangeTest : public EditingTestBase {};
TEST_F(RangeTest, extractContentsWithDOMMutationEvent) {
if (!RuntimeEnabledFeatures::MutationEventsEnabledByRuntimeFlag()) {
// TODO(crbug.com/1446498) Remove this test when MutationEvents are disabled
// for good. This is just a test of `DOMSubtreeModified` and ranges.
return;
}
GetDocument().body()->setInnerHTML("<span><b>abc</b>def</span>");
GetDocument().GetSettings()->SetScriptEnabled(true);
Element* const script_element =
GetDocument().CreateRawElement(html_names::kScriptTag);
script_element->setTextContent(
"let count = 0;"
"const span = document.querySelector('span');"
"span.addEventListener('DOMSubtreeModified', () => {"
" if (++count > 1) return;"
" span.firstChild.textContent = 'ABC';"
" span.lastChild.textContent = 'DEF';"
"});");
GetDocument().body()->AppendChild(script_element);
Element* const span_element =
GetDocument().QuerySelector(AtomicString("span"));
auto* const range = MakeGarbageCollected<Range>(GetDocument(), span_element,
0, span_element, 1);
Element* const result = GetDocument().CreateRawElement(html_names::kDivTag);
result->AppendChild(range->extractContents(ASSERT_NO_EXCEPTION));
EXPECT_EQ("<b>abc</b>", result->innerHTML())
<< "DOM mutation event handler should not affect result.";
EXPECT_EQ("<span>DEF</span>", span_element->outerHTML())
<< "DOM mutation event handler should be executed.";
}
// http://crbug.com/822510
TEST_F(RangeTest, IntersectsNode) {
SetBodyContent(
"<div>"
"<span id='s0'>s0</span>"
"<span id='s1'>s1</span>"
"<span id='s2'>s2</span>"
"</div>");
Element* const div = GetDocument().QuerySelector(AtomicString("div"));
Element* const s0 = GetDocument().getElementById(AtomicString("s0"));
Element* const s1 = GetDocument().getElementById(AtomicString("s1"));
Element* const s2 = GetDocument().getElementById(AtomicString("s2"));
Range& range = *Range::Create(GetDocument());
// Range encloses s0
range.setStart(div, 0);
range.setEnd(div, 1);
EXPECT_TRUE(range.intersectsNode(s0, ASSERT_NO_EXCEPTION));
EXPECT_FALSE(range.intersectsNode(s1, ASSERT_NO_EXCEPTION));
EXPECT_FALSE(range.intersectsNode(s2, ASSERT_NO_EXCEPTION));
// Range encloses s1
range.setStart(div, 1);
range.setEnd(div, 2);
EXPECT_FALSE(range.intersectsNode(s0, ASSERT_NO_EXCEPTION));
EXPECT_TRUE(range.intersectsNode(s1, ASSERT_NO_EXCEPTION));
EXPECT_FALSE(range.intersectsNode(s2, ASSERT_NO_EXCEPTION));
// Range encloses s2
range.setStart(div, 2);
range.setEnd(div, 3);
EXPECT_FALSE(range.intersectsNode(s0, ASSERT_NO_EXCEPTION));
EXPECT_FALSE(range.intersectsNode(s1, ASSERT_NO_EXCEPTION));
EXPECT_TRUE(range.intersectsNode(s2, ASSERT_NO_EXCEPTION));
}
TEST_F(RangeTest, SplitTextNodeRangeWithinText) {
V8TestingScope scope;
GetDocument().body()->setInnerHTML("1234");
auto* old_text = To<Text>(GetDocument().body()->firstChild());
auto* range04 =
MakeGarbageCollected<Range>(GetDocument(), old_text, 0, old_text, 4);
auto* range02 =
MakeGarbageCollected<Range>(GetDocument(), old_text, 0, old_text, 2);
auto* range22 =
MakeGarbageCollected<Range>(GetDocument(), old_text, 2, old_text, 2);
auto* range24 =
MakeGarbageCollected<Range>(GetDocument(), old_text, 2, old_text, 4);
old_text->splitText(2, ASSERT_NO_EXCEPTION);
auto* new_text = To<Text>(old_text->nextSibling());
EXPECT_TRUE(range04->BoundaryPointsValid());
EXPECT_EQ(old_text, range04->startContainer());
EXPECT_EQ(0u, range04->startOffset());
EXPECT_EQ(new_text, range04->endContainer());
EXPECT_EQ(2u, range04->endOffset());
EXPECT_TRUE(range02->BoundaryPointsValid());
EXPECT_EQ(old_text, range02->startContainer());
EXPECT_EQ(0u, range02->startOffset());
EXPECT_EQ(old_text, range02->endContainer());
EXPECT_EQ(2u, range02->endOffset());
// Our implementation always moves the boundary point at the separation point
// to the end of the original text node.
EXPECT_TRUE(range22->BoundaryPointsValid());
EXPECT_EQ(old_text, range22->startContainer());
EXPECT_EQ(2u, range22->startOffset());
EXPECT_EQ(old_text, range22->endContainer());
EXPECT_EQ(2u, range22->endOffset());
EXPECT_TRUE(range24->BoundaryPointsValid());
EXPECT_EQ(old_text, range24->startContainer());
EXPECT_EQ(2u, range24->startOffset());
EXPECT_EQ(new_text, range24->endContainer());
EXPECT_EQ(2u, range24->endOffset());
}
TEST_F(RangeTest, SplitTextNodeRangeOutsideText) {
V8TestingScope scope;
GetDocument().body()->setInnerHTML(
"<span id=\"outer\">0<span id=\"inner-left\">1</span>SPLITME<span "
"id=\"inner-right\">2</span>3</span>");
Element* outer =
GetDocument().getElementById(AtomicString::FromUTF8("outer"));
Element* inner_left =
GetDocument().getElementById(AtomicString::FromUTF8("inner-left"));
Element* inner_right =
GetDocument().getElementById(AtomicString::FromUTF8("inner-right"));
auto* old_text = To<Text>(outer->childNodes()->item(2));
auto* range_outer_outside =
MakeGarbageCollected<Range>(GetDocument(), outer, 0, outer, 5);
auto* range_outer_inside =
MakeGarbageCollected<Range>(GetDocument(), outer, 1, outer, 4);
auto* range_outer_surrounding_text =
MakeGarbageCollected<Range>(GetDocument(), outer, 2, outer, 3);
auto* range_inner_left =
MakeGarbageCollected<Range>(GetDocument(), inner_left, 0, inner_left, 1);
auto* range_inner_right = MakeGarbageCollected<Range>(
GetDocument(), inner_right, 0, inner_right, 1);
auto* range_from_text_to_middle_of_element =
MakeGarbageCollected<Range>(GetDocument(), old_text, 6, outer, 3);
old_text->splitText(3, ASSERT_NO_EXCEPTION);
auto* new_text = To<Text>(old_text->nextSibling());
EXPECT_TRUE(range_outer_outside->BoundaryPointsValid());
EXPECT_EQ(outer, range_outer_outside->startContainer());
EXPECT_EQ(0u, range_outer_outside->startOffset());
EXPECT_EQ(outer, range_outer_outside->endContainer());
EXPECT_EQ(6u,
range_outer_outside
->endOffset()); // Increased by 1 since a new node is inserted.
EXPECT_TRUE(range_outer_inside->BoundaryPointsValid());
EXPECT_EQ(outer, range_outer_inside->startContainer());
EXPECT_EQ(1u, range_outer_inside->startOffset());
EXPECT_EQ(outer, range_outer_inside->endContainer());
EXPECT_EQ(5u, range_outer_inside->endOffset());
EXPECT_TRUE(range_outer_surrounding_text->BoundaryPointsValid());
EXPECT_EQ(outer, range_outer_surrounding_text->startContainer());
EXPECT_EQ(2u, range_outer_surrounding_text->startOffset());
EXPECT_EQ(outer, range_outer_surrounding_text->endContainer());
EXPECT_EQ(4u, range_outer_surrounding_text->endOffset());
EXPECT_TRUE(range_inner_left->BoundaryPointsValid());
EXPECT_EQ(inner_left, range_inner_left->startContainer());
EXPECT_EQ(0u, range_inner_left->startOffset());
EXPECT_EQ(inner_left, range_inner_left->endContainer());
EXPECT_EQ(1u, range_inner_left->endOffset());
EXPECT_TRUE(range_inner_right->BoundaryPointsValid());
EXPECT_EQ(inner_right, range_inner_right->startContainer());
EXPECT_EQ(0u, range_inner_right->startOffset());
EXPECT_EQ(inner_right, range_inner_right->endContainer());
EXPECT_EQ(1u, range_inner_right->endOffset());
EXPECT_TRUE(range_from_text_to_middle_of_element->BoundaryPointsValid());
EXPECT_EQ(new_text, range_from_text_to_middle_of_element->startContainer());
EXPECT_EQ(3u, range_from_text_to_middle_of_element->startOffset());
EXPECT_EQ(outer, range_from_text_to_middle_of_element->endContainer());
EXPECT_EQ(4u, range_from_text_to_middle_of_element->endOffset());
}
TEST_F(RangeTest, updateOwnerDocumentIfNeeded) {
Element* foo = GetDocument().CreateElementForBinding(AtomicString("foo"));
Element* bar = GetDocument().CreateElementForBinding(AtomicString("bar"));
foo->AppendChild(bar);
auto* range = MakeGarbageCollected<Range>(GetDocument(), Position(bar, 0),
Position(foo, 1));
ScopedNullExecutionContext execution_context;
auto* another_document =
Document::CreateForTest(execution_context.GetExecutionContext());
another_document->AppendChild(foo);
EXPECT_EQ(bar, range->startContainer());
EXPECT_EQ(0u, range->startOffset());
EXPECT_EQ(foo, range->endContainer());
EXPECT_EQ(1u, range->endOffset());
}
// Regression test for crbug.com/639184
TEST_F(RangeTest, NotMarkedValidByIrrelevantTextInsert) {
GetDocument().body()->setInnerHTML(
"<div><span id=span1>foo</span>bar<span id=span2>baz</span></div>");
Element* div = GetDocument().QuerySelector(AtomicString("div"));
Element* span1 = GetDocument().getElementById(AtomicString("span1"));
Element* span2 = GetDocument().getElementById(AtomicString("span2"));
auto* text = To<Text>(div->childNodes()->item(1));
auto* range = MakeGarbageCollected<Range>(GetDocument(), span2, 0, div, 3);
div->RemoveChild(span1);
text->insertData(0, "bar", ASSERT_NO_EXCEPTION);
EXPECT_TRUE(range->BoundaryPointsValid());
EXPECT_EQ(span2, range->startContainer());
EXPECT_EQ(0u, range->startOffset());
EXPECT_EQ(div, range->endContainer());
EXPECT_EQ(2u, range->endOffset());
}
// Regression test for crbug.com/639184
TEST_F(RangeTest, NotMarkedValidByIrrelevantTextRemove) {
GetDocument().body()->setInnerHTML(
"<div><span id=span1>foofoo</span>bar<span id=span2>baz</span></div>");
Element* div = GetDocument().QuerySelector(AtomicString("div"));
Element* span1 = GetDocument().getElementById(AtomicString("span1"));
Element* span2 = GetDocument().getElementById(AtomicString("span2"));
auto* text = To<Text>(div->childNodes()->item(1));
auto* range = MakeGarbageCollected<Range>(GetDocument(), span2, 0, div, 3);
div->RemoveChild(span1);
text->deleteData(0, 3, ASSERT_NO_EXCEPTION);
EXPECT_TRUE(range->BoundaryPointsValid());
EXPECT_EQ(span2, range->startContainer());
EXPECT_EQ(0u, range->startOffset());
EXPECT_EQ(div, range->endContainer());
EXPECT_EQ(2u, range->endOffset());
}
// Regression test for crbug.com/698123
TEST_F(RangeTest, ExpandNotCrash) {
Range* range = Range::Create(GetDocument());
auto* div = MakeGarbageCollected<HTMLDivElement>(GetDocument());
range->setStart(div, 0, ASSERT_NO_EXCEPTION);
range->expand("", ASSERT_NO_EXCEPTION);
}
TEST_F(RangeTest, ToPosition) {
auto& textarea = *MakeGarbageCollected<HTMLTextAreaElement>(GetDocument());
Range& range = *Range::Create(GetDocument());
const Position position = Position(&textarea, 0);
range.setStart(position, ASSERT_NO_EXCEPTION);
EXPECT_EQ(position, range.StartPosition());
EXPECT_EQ(position, range.EndPosition());
}
TEST_F(RangeTest, BoundingRectMustIndependentFromSelection) {
LoadAhem();
GetDocument().body()->setInnerHTML(
"<div style='font: Ahem; width: 2em;letter-spacing: 5px;'>xx xx </div>");
Node* const div = GetDocument().QuerySelector(AtomicString("div"));
// "x^x
// x|x "
auto* const range = MakeGarbageCollected<Range>(
GetDocument(), div->firstChild(), 1, div->firstChild(), 4);
const gfx::RectF rect_before = range->BoundingRect();
EXPECT_GT(rect_before.width(), 0);
EXPECT_GT(rect_before.height(), 0);
Selection().SetSelection(SelectionInDOMTree::Builder()
.SetBaseAndExtent(EphemeralRange(range))
.Build(),
SetSelectionOptions());
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ(Selection().SelectedText(), "x x");
const gfx::RectF rect_after = range->BoundingRect();
EXPECT_EQ(rect_before, rect_after);
}
// Regression test for crbug.com/681536
TEST_F(RangeTest, BorderAndTextQuadsWithInputInBetween) {
GetDocument().body()->setInnerHTML("<div>foo <u><input> bar</u></div>");
GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest);
Node* foo = GetDocument().QuerySelector(AtomicString("div"))->firstChild();
Node* bar = GetDocument().QuerySelector(AtomicString("u"))->lastChild();
auto* range = MakeGarbageCollected<Range>(GetDocument(), foo, 2, bar, 2);
Vector<gfx::QuadF> quads;
range->GetBorderAndTextQuads(quads);
// Should get one quad for "o ", <input> and " b", respectively.
ASSERT_EQ(3u, quads.size());
}
static Vector<gfx::QuadF> GetBorderAndTextQuads(const Position& start,
const Position& end) {
DCHECK_LE(start, end);
auto* const range =
MakeGarbageCollected<Range>(*start.GetDocument(), start, end);
Vector<gfx::QuadF> quads;
range->GetBorderAndTextQuads(quads);
return quads;
}
static Vector<gfx::Size> ComputeSizesOfQuads(const Vector<gfx::QuadF>& quads) {
Vector<gfx::Size> sizes;
for (const auto& quad : quads)
sizes.push_back(gfx::ToEnclosingRect(quad.BoundingBox()).size());
return sizes;
}
// http://crbug.com/1240510
TEST_F(RangeTest, GetBorderAndTextQuadsWithCombinedText) {
LoadAhem();
InsertStyleElement(
"body { font: 20px/25px Ahem; margin: 0px; }"
"#sample { writing-mode: vertical-rl; }"
"c { text-combine-upright: all; }");
SetBodyInnerHTML(
"<div id=sample>"
"<c id=c1>M</c><c id=c2>MM</c><c id=c3>MMM</c><c id=c4>MMMM</c>"
"</div>");
const Text& text1 = *To<Text>(GetElementById("c1")->firstChild());
const Text& text2 = *To<Text>(GetElementById("c2")->firstChild());
const Text& text3 = *To<Text>(GetElementById("c3")->firstChild());
const Text& text4 = *To<Text>(GetElementById("c4")->firstChild());
EXPECT_THAT(GetBorderAndTextQuads(Position(text1, 0), Position(text1, 1)),
ElementsAre(gfx::QuadF(gfx::RectF(3, 0, 20, 20))));
EXPECT_THAT(GetBorderAndTextQuads(Position(text2, 0), Position(text2, 2)),
ElementsAre(gfx::QuadF(gfx::RectF(2, 20, 22, 20))));
EXPECT_THAT(GetBorderAndTextQuads(Position(text3, 0), Position(text3, 3)),
ElementsAre(gfx::QuadF(gfx::RectF(2, 40, 22, 20))));
EXPECT_THAT(GetBorderAndTextQuads(Position(text4, 0), Position(text4, 4)),
ElementsAre(gfx::QuadF(gfx::RectF(2, 60, 22, 20))));
}
TEST_F(RangeTest, GetBorderAndTextQuadsWithFirstLetterOne) {
GetDocument().body()->setInnerHTML(R"HTML(
<style>
body { font-size: 20px; }
#sample::first-letter { font-size: 500%; }
</style>
<p id=sample>abc</p>
<p id=expected><span style='font-size: 500%'>a</span>bc</p>
)HTML");
GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest);
Element* const expected =
GetDocument().getElementById(AtomicString("expected"));
Element* const sample = GetDocument().getElementById(AtomicString("sample"));
const Vector<gfx::QuadF> expected_quads =
GetBorderAndTextQuads(Position(expected, 0), Position(expected, 2));
const Vector<gfx::QuadF> sample_quads =
GetBorderAndTextQuads(Position(sample, 0), Position(sample, 1));
ASSERT_EQ(2u, sample_quads.size());
ASSERT_EQ(3u, expected_quads.size())
<< "expected_quads has SPAN, SPAN.firstChild and P.lastChild";
EXPECT_EQ(gfx::ToEnclosingRect(expected_quads[0].BoundingBox()).size(),
gfx::ToEnclosingRect(sample_quads[0].BoundingBox()).size())
<< "Check size of first-letter part";
EXPECT_EQ(gfx::ToEnclosingRect(expected_quads[2].BoundingBox()).size(),
gfx::ToEnclosingRect(sample_quads[1].BoundingBox()).size())
<< "Check size of first-letter part";
EXPECT_EQ(ComputeSizesOfQuads(
GetBorderAndTextQuads(Position(expected->firstChild(), 0),
Position(expected->firstChild(), 1))),
ComputeSizesOfQuads(
GetBorderAndTextQuads(Position(sample->firstChild(), 0),
Position(sample->firstChild(), 1))))
<< "All first-letter part";
EXPECT_EQ(ComputeSizesOfQuads(
GetBorderAndTextQuads(Position(expected->lastChild(), 0),
Position(expected->lastChild(), 2))),
ComputeSizesOfQuads(
GetBorderAndTextQuads(Position(sample->firstChild(), 1),
Position(sample->firstChild(), 3))))
<< "All remaining part";
}
TEST_F(RangeTest, GetBorderAndTextQuadsWithFirstLetterThree) {
GetDocument().body()->setInnerHTML(R"HTML(
<style>
body { font-size: 20px; }
#sample::first-letter { font-size: 500%; }
</style>
<p id=sample>(a)bc</p>
<p id=expected><span style='font-size: 500%'>(a)</span>bc</p>
)HTML");
GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest);
Element* const expected =
GetDocument().getElementById(AtomicString("expected"));
Element* const sample = GetDocument().getElementById(AtomicString("sample"));
const Vector<gfx::QuadF> expected_quads =
GetBorderAndTextQuads(Position(expected, 0), Position(expected, 2));
const Vector<gfx::QuadF> sample_quads =
GetBorderAndTextQuads(Position(sample, 0), Position(sample, 1));
ASSERT_EQ(2u, sample_quads.size());
ASSERT_EQ(3u, expected_quads.size())
<< "expected_quads has SPAN, SPAN.firstChild and P.lastChild";
EXPECT_EQ(gfx::ToEnclosingRect(expected_quads[0].BoundingBox()).size(),
gfx::ToEnclosingRect(sample_quads[0].BoundingBox()).size())
<< "Check size of first-letter part";
EXPECT_EQ(gfx::ToEnclosingRect(expected_quads[2].BoundingBox()).size(),
gfx::ToEnclosingRect(sample_quads[1].BoundingBox()).size())
<< "Check size of first-letter part";
EXPECT_EQ(ComputeSizesOfQuads(
GetBorderAndTextQuads(Position(expected->firstChild(), 0),
Position(expected->firstChild(), 1))),
ComputeSizesOfQuads(
GetBorderAndTextQuads(Position(sample->firstChild(), 0),
Position(sample->firstChild(), 3))))
<< "All first-letter part";
EXPECT_EQ(ComputeSizesOfQuads(
GetBorderAndTextQuads(Position(expected->lastChild(), 0),
Position(expected->lastChild(), 2))),
ComputeSizesOfQuads(
GetBorderAndTextQuads(Position(sample->firstChild(), 3),
Position(sample->firstChild(), 5))))
<< "All remaining part";
EXPECT_EQ(ComputeSizesOfQuads(GetBorderAndTextQuads(
Position(expected->firstChild()->firstChild(), 1),
Position(expected->firstChild()->firstChild(), 2))),
ComputeSizesOfQuads(
GetBorderAndTextQuads(Position(sample->firstChild(), 1),
Position(sample->firstChild(), 2))))
<< "Partial first-letter part";
EXPECT_EQ(ComputeSizesOfQuads(GetBorderAndTextQuads(
Position(expected->firstChild()->firstChild(), 1),
Position(expected->lastChild(), 1))),
ComputeSizesOfQuads(
GetBorderAndTextQuads(Position(sample->firstChild(), 1),
Position(sample->firstChild(), 4))))
<< "Partial first-letter part and remaining part";
}
TEST_F(RangeTest, CollapsedRangeGetBorderAndTextQuadsWithFirstLetter) {
GetDocument().body()->setInnerHTML(R"HTML(
<style>
body { font-size: 20px; }
#sample::first-letter { font-size: 500%; }
</style>
<p id=sample>abc</p>
<p id=expected><span style='font-size: 500%'>a</span>bc</p>
)HTML");
GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest);
Element* const expected =
GetDocument().getElementById(AtomicString("expected"));
Element* const sample = GetDocument().getElementById(AtomicString("sample"));
const Vector<gfx::QuadF> expected_quads =
GetBorderAndTextQuads(Position(expected, 0), Position(expected, 2));
const Vector<gfx::QuadF> sample_quads =
GetBorderAndTextQuads(Position(sample, 0), Position(sample, 1));
ASSERT_EQ(2u, sample_quads.size());
ASSERT_EQ(3u, expected_quads.size())
<< "expected_quads has SPAN, SPAN.firstChild and P.lastChild";
EXPECT_EQ(gfx::ToEnclosingRect(expected_quads[0].BoundingBox()).size(),
gfx::ToEnclosingRect(sample_quads[0].BoundingBox()).size())
<< "Check size of first-letter part";
EXPECT_EQ(gfx::ToEnclosingRect(expected_quads[2].BoundingBox()).size(),
gfx::ToEnclosingRect(sample_quads[1].BoundingBox()).size())
<< "Check size of first-letter part";
EXPECT_EQ(ComputeSizesOfQuads(GetBorderAndTextQuads(
Position(expected->firstChild()->firstChild(), 0),
Position(expected->firstChild()->firstChild(), 0))),
ComputeSizesOfQuads(
GetBorderAndTextQuads(Position(sample->firstChild(), 0),
Position(sample->firstChild(), 0))))
<< "Collapsed range before first-letter part";
EXPECT_EQ(ComputeSizesOfQuads(GetBorderAndTextQuads(
Position(expected->firstChild()->firstChild(), 1),
Position(expected->firstChild()->firstChild(), 1))),
ComputeSizesOfQuads(
GetBorderAndTextQuads(Position(sample->firstChild(), 1),
Position(sample->firstChild(), 1))))
<< "Collapsed range after first-letter part";
EXPECT_EQ(ComputeSizesOfQuads(GetBorderAndTextQuads(
Position(expected->firstChild()->nextSibling(), 1),
Position(expected->firstChild()->nextSibling(), 1))),
ComputeSizesOfQuads(
GetBorderAndTextQuads(Position(sample->firstChild(), 2),
Position(sample->firstChild(), 2))))
<< "Collapsed range in remaining text part";
}
TEST_F(RangeTest, ContainerNodeRemoval) {
GetDocument().body()->setInnerHTML("<p>aaaa</p><p>bbbbbb</p>");
auto* node_a = GetDocument().body()->firstChild();
auto* node_b = node_a->nextSibling();
auto* text_a = To<Text>(node_a->firstChild());
auto* text_b = To<Text>(node_b->firstChild());
auto* rangea0a2 =
MakeGarbageCollected<Range>(GetDocument(), text_a, 0, text_a, 2);
auto* rangea2a4 =
MakeGarbageCollected<Range>(GetDocument(), text_a, 2, text_a, 4);
auto* rangea2b2 =
MakeGarbageCollected<Range>(GetDocument(), text_a, 0, text_b, 2);
auto* rangeb2b6 =
MakeGarbageCollected<Range>(GetDocument(), text_b, 2, text_b, 6);
// remove children in node_a
node_a->setTextContent("");
EXPECT_TRUE(rangea0a2->BoundaryPointsValid());
EXPECT_EQ(node_a, rangea0a2->startContainer());
EXPECT_EQ(0u, rangea0a2->startOffset());
EXPECT_EQ(node_a, rangea0a2->endContainer());
EXPECT_EQ(0u, rangea0a2->endOffset());
EXPECT_TRUE(rangea2a4->BoundaryPointsValid());
EXPECT_EQ(node_a, rangea2a4->startContainer());
EXPECT_EQ(0u, rangea2a4->startOffset());
EXPECT_EQ(node_a, rangea2a4->endContainer());
EXPECT_EQ(0u, rangea2a4->endOffset());
EXPECT_TRUE(rangea2b2->BoundaryPointsValid());
EXPECT_EQ(node_a, rangea2b2->startContainer());
EXPECT_EQ(0u, rangea2b2->startOffset());
EXPECT_EQ(text_b, rangea2b2->endContainer());
EXPECT_EQ(2u, rangea2b2->endOffset());
EXPECT_TRUE(rangeb2b6->BoundaryPointsValid());
EXPECT_EQ(text_b, rangeb2b6->startContainer());
EXPECT_EQ(2u, rangeb2b6->startOffset());
EXPECT_EQ(text_b, rangeb2b6->endContainer());
EXPECT_EQ(6u, rangeb2b6->endOffset());
// remove children in body.
GetDocument().body()->setTextContent("");
EXPECT_TRUE(rangea0a2->BoundaryPointsValid());
EXPECT_EQ(GetDocument().body(), rangea0a2->startContainer());
EXPECT_EQ(0u, rangea0a2->startOffset());
EXPECT_EQ(GetDocument().body(), rangea0a2->endContainer());
EXPECT_EQ(0u, rangea0a2->endOffset());
EXPECT_TRUE(rangea2a4->BoundaryPointsValid());
EXPECT_EQ(GetDocument().body(), rangea2a4->startContainer());
EXPECT_EQ(0u, rangea2a4->startOffset());
EXPECT_EQ(GetDocument().body(), rangea2a4->endContainer());
EXPECT_EQ(0u, rangea2a4->endOffset());
EXPECT_TRUE(rangea2b2->BoundaryPointsValid());
EXPECT_EQ(GetDocument().body(), rangea2b2->startContainer());
EXPECT_EQ(0u, rangea2b2->startOffset());
EXPECT_EQ(GetDocument().body(), rangea2b2->endContainer());
EXPECT_EQ(0u, rangea2b2->endOffset());
EXPECT_TRUE(rangeb2b6->BoundaryPointsValid());
EXPECT_EQ(GetDocument().body(), rangeb2b6->startContainer());
EXPECT_EQ(0u, rangeb2b6->startOffset());
EXPECT_EQ(GetDocument().body(), rangeb2b6->endContainer());
EXPECT_EQ(0u, rangeb2b6->endOffset());
}
} // namespace blink