blob: 930aa4ec7237f63db597f08209dff503210d2736 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/editing/ephemeral_range.h"
#include <sstream>
#include "third_party/blink/renderer/core/dom/range.h"
#include "third_party/blink/renderer/core/editing/testing/editing_test_base.h"
namespace blink {
class EphemeralRangeTest : public EditingTestBase {
protected:
template <typename Traversal = NodeTraversal>
std::string TraverseRange(Range*) const;
template <typename Strategy>
std::string TraverseRange(const EphemeralRangeTemplate<Strategy>&) const;
Range* GetBodyRange() const;
};
template <typename Traversal>
std::string EphemeralRangeTest::TraverseRange(Range* range) const {
std::stringstream nodes_content;
for (Node* node = range->FirstNode(); node != range->PastLastNode();
node = Traversal::Next(*node)) {
nodes_content << "[" << *node << "]";
}
return nodes_content.str();
}
template <typename Strategy>
std::string EphemeralRangeTest::TraverseRange(
const EphemeralRangeTemplate<Strategy>& range) const {
std::stringstream nodes_content;
for (const Node& node : range.Nodes())
nodes_content << "[" << node << "]";
return nodes_content.str();
}
Range* EphemeralRangeTest::GetBodyRange() const {
Range* range = Range::Create(GetDocument());
range->selectNode(GetDocument().body());
return range;
}
// Tests that |EphemeralRange::nodes()| will traverse the whole range exactly as
// |for (Node* n = firstNode(); n != pastLastNode(); n = Traversal::next(*n))|
// does.
TEST_F(EphemeralRangeTest, rangeTraversalDOM) {
const char* body_content =
"<p id='host'>"
"<b id='zero'>0</b>"
"<b id='one'>1</b>"
"<b id='two'>22</b>"
"<span id='three'>333</span>"
"</p>";
SetBodyContent(body_content);
const std::string expected_nodes(
"[BODY][P id=\"host\"][B id=\"zero\"][#text \"0\"][B id=\"one\"][#text "
"\"1\"][B id=\"two\"][#text \"22\"][SPAN id=\"three\"][#text \"333\"]");
// Check two ways to traverse.
EXPECT_EQ(expected_nodes, TraverseRange<>(GetBodyRange()));
EXPECT_EQ(TraverseRange<>(GetBodyRange()),
TraverseRange(EphemeralRange(GetBodyRange())));
EXPECT_EQ(expected_nodes, TraverseRange<FlatTreeTraversal>(GetBodyRange()));
EXPECT_EQ(TraverseRange<FlatTreeTraversal>(GetBodyRange()),
TraverseRange(EphemeralRangeInFlatTree(GetBodyRange())));
}
// Tests that |inRange| helper will traverse the whole range with shadow DOM.
TEST_F(EphemeralRangeTest, rangeShadowTraversal) {
const char* body_content =
"<b id='zero'>0</b>"
"<p id='host'>"
"<b id='one'>1</b>"
"<b id='two'>22</b>"
"<b id='three'>333</b>"
"</p>"
"<b id='four'>4444</b>";
const char* shadow_content =
"<p id='five'>55555</p>"
"<content select=#two></content>"
"<content select=#one></content>"
"<span id='six'>666666</span>"
"<p id='seven'>7777777</p>";
SetBodyContent(body_content);
SetShadowContent(shadow_content, "host");
const std::string expected_nodes(
"[BODY][B id=\"zero\"][#text \"0\"][P id=\"host\"][P id=\"five\"][#text "
"\"55555\"][B id=\"two\"][#text \"22\"][B id=\"one\"][#text \"1\"][SPAN "
"id=\"six\"][#text \"666666\"][P id=\"seven\"][#text \"7777777\"][B "
"id=\"four\"][#text \"4444\"]");
EXPECT_EQ(expected_nodes, TraverseRange<FlatTreeTraversal>(GetBodyRange()));
EXPECT_EQ(TraverseRange<FlatTreeTraversal>(GetBodyRange()),
TraverseRange(EphemeralRangeInFlatTree(GetBodyRange())));
// Node 'three' should not appear in FlatTreeTraversal.
EXPECT_EQ(expected_nodes.find("three") == std::string::npos, true);
}
// Limit a range and check that it will be traversed correctly.
TEST_F(EphemeralRangeTest, rangeTraversalLimitedDOM) {
const char* body_content =
"<p id='host'>"
"<b id='zero'>0</b>"
"<b id='one'>1</b>"
"<b id='two'>22</b>"
"<span id='three'>333</span>"
"</p>";
SetBodyContent(body_content);
Range* until_b = GetBodyRange();
until_b->setEnd(GetDocument().getElementById("one"), 0,
IGNORE_EXCEPTION_FOR_TESTING);
EXPECT_EQ("[BODY][P id=\"host\"][B id=\"zero\"][#text \"0\"][B id=\"one\"]",
TraverseRange<>(until_b));
EXPECT_EQ(TraverseRange<>(until_b), TraverseRange(EphemeralRange(until_b)));
Range* from_b_to_span = GetBodyRange();
from_b_to_span->setStart(GetDocument().getElementById("one"), 0,
IGNORE_EXCEPTION_FOR_TESTING);
from_b_to_span->setEnd(GetDocument().getElementById("three"), 0,
IGNORE_EXCEPTION_FOR_TESTING);
EXPECT_EQ("[#text \"1\"][B id=\"two\"][#text \"22\"][SPAN id=\"three\"]",
TraverseRange<>(from_b_to_span));
EXPECT_EQ(TraverseRange<>(from_b_to_span),
TraverseRange(EphemeralRange(from_b_to_span)));
}
TEST_F(EphemeralRangeTest, rangeTraversalLimitedFlatTree) {
const char* body_content =
"<b id='zero'>0</b>"
"<p id='host'>"
"<b id='one'>1</b>"
"<b id='two'>22</b>"
"</p>"
"<b id='three'>333</b>";
const char* shadow_content =
"<p id='four'>4444</p>"
"<content select=#two></content>"
"<content select=#one></content>"
"<span id='five'>55555</span>"
"<p id='six'>666666</p>";
SetBodyContent(body_content);
ShadowRoot* shadow_root = SetShadowContent(shadow_content, "host");
const PositionInFlatTree start_position(GetDocument().getElementById("one"),
0);
const PositionInFlatTree limit_position(shadow_root->getElementById("five"),
0);
const PositionInFlatTree end_position(shadow_root->getElementById("six"), 0);
const EphemeralRangeInFlatTree from_b_to_span(start_position, limit_position);
EXPECT_EQ("[#text \"1\"][SPAN id=\"five\"]", TraverseRange(from_b_to_span));
const EphemeralRangeInFlatTree from_span_to_end(limit_position, end_position);
EXPECT_EQ("[#text \"55555\"][P id=\"six\"]", TraverseRange(from_span_to_end));
}
TEST_F(EphemeralRangeTest, traversalEmptyRanges) {
const char* body_content =
"<p id='host'>"
"<b id='one'>1</b>"
"</p>";
SetBodyContent(body_content);
// Expect no iterations in loop for an empty EphemeralRange.
EXPECT_EQ(std::string(), TraverseRange(EphemeralRange()));
auto iterable = EphemeralRange().Nodes();
// Tree iterators have only |operator !=| ATM.
EXPECT_FALSE(iterable.begin() != iterable.end());
const EphemeralRange single_position_range(GetBodyRange()->StartPosition());
EXPECT_FALSE(single_position_range.IsNull());
EXPECT_EQ(std::string(), TraverseRange(single_position_range));
EXPECT_EQ(single_position_range.StartPosition().NodeAsRangeFirstNode(),
single_position_range.EndPosition().NodeAsRangePastLastNode());
}
TEST_F(EphemeralRangeTest, commonAncesstorDOM) {
const char* body_content =
"<p id='host'>00"
"<b id='one'>11</b>"
"<b id='two'>22</b>"
"<b id='three'>33</b>"
"</p>";
SetBodyContent(body_content);
const Position start_position(GetDocument().getElementById("one"), 0);
const Position end_position(GetDocument().getElementById("two"), 0);
const EphemeralRange range(start_position, end_position);
EXPECT_EQ(GetDocument().getElementById("host"),
range.CommonAncestorContainer());
}
TEST_F(EphemeralRangeTest, commonAncesstorFlatTree) {
const char* body_content =
"<b id='zero'>0</b>"
"<p id='host'>"
"<b id='one'>1</b>"
"<b id='two'>22</b>"
"</p>"
"<b id='three'>333</b>";
const char* shadow_content =
"<p id='four'>4444</p>"
"<content select=#two></content>"
"<content select=#one></content>"
"<p id='five'>55555</p>";
SetBodyContent(body_content);
ShadowRoot* shadow_root = SetShadowContent(shadow_content, "host");
const PositionInFlatTree start_position(GetDocument().getElementById("one"),
0);
const PositionInFlatTree end_position(shadow_root->getElementById("five"), 0);
const EphemeralRangeInFlatTree range(start_position, end_position);
EXPECT_EQ(GetDocument().getElementById("host"),
range.CommonAncestorContainer());
}
} // namespace blink