blob: c747e6172df23ec8d67edd45fefac41777342b07 [file] [log] [blame]
// Copyright 2014 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 "core/editing/VisibleSelection.h"
#include "core/dom/Range.h"
#include "core/editing/EditingTestBase.h"
#define LOREM_IPSUM \
"Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor " \
"incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud " \
"exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure " \
"dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur." \
"Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt " \
"mollit anim id est laborum."
namespace blink {
class VisibleSelectionTest : public EditingTestBase {
protected:
// Helper function to set the VisibleSelection base/extent.
template <typename Strategy>
void setSelection(VisibleSelectionTemplate<Strategy>& selection, int base)
{
setSelection(selection, base, base);
}
// Helper function to set the VisibleSelection base/extent.
template <typename Strategy>
void setSelection(VisibleSelectionTemplate<Strategy>& selection, int base, int extend)
{
Node* node = document().body()->firstChild();
selection.setBase(PositionTemplate<Strategy>(node, base));
selection.setExtent(PositionTemplate<Strategy>(node, extend));
}
};
static void testComposedTreePositionsToEqualToDOMTreePositions(const VisibleSelection& selection, const VisibleSelectionInComposedTree& selectionInComposedTree)
{
// Since DOM tree positions can't be map to composed tree version, e.g.
// shadow root, not distributed node, we map a position in composed tree
// to DOM tree position.
EXPECT_EQ(selection.start(), toPositionInDOMTree(selectionInComposedTree.start()));
EXPECT_EQ(selection.end(), toPositionInDOMTree(selectionInComposedTree.end()));
EXPECT_EQ(selection.base(), toPositionInDOMTree(selectionInComposedTree.base()));
EXPECT_EQ(selection.extent(), toPositionInDOMTree(selectionInComposedTree.extent()));
}
TEST_F(VisibleSelectionTest, expandUsingGranularity)
{
const char* bodyContent = "<span id=host><a id=one>1</a><a id=two>22</a></span>";
const char* shadowContent = "<p><b id=three>333</b><content select=#two></content><b id=four>4444</b><span id=space> </span><content select=#one></content><b id=five>55555</b></p>";
setBodyContent(bodyContent);
RefPtrWillBeRawPtr<ShadowRoot> shadowRoot = setShadowContent(shadowContent, "host");
updateLayoutAndStyleForPainting();
Node* one = document().getElementById("one")->firstChild();
Node* two = document().getElementById("two")->firstChild();
Node* three = shadowRoot->getElementById("three")->firstChild();
Node* four = shadowRoot->getElementById("four")->firstChild();
Node* five = shadowRoot->getElementById("five")->firstChild();
VisibleSelection selection;
VisibleSelectionInComposedTree selectionInComposedTree;
// From a position at distributed node
selection = VisibleSelection(createVisiblePosition(Position(one, 1)));
selection.expandUsingGranularity(WordGranularity);
selectionInComposedTree = VisibleSelectionInComposedTree(createVisiblePosition(PositionInComposedTree(one, 1)));
selectionInComposedTree.expandUsingGranularity(WordGranularity);
EXPECT_EQ(Position(one, 1), selection.base());
EXPECT_EQ(Position(one, 1), selection.extent());
EXPECT_EQ(Position(one, 0), selection.start());
EXPECT_EQ(Position(two, 2), selection.end());
EXPECT_EQ(PositionInComposedTree(one, 1), selectionInComposedTree.base());
EXPECT_EQ(PositionInComposedTree(one, 1), selectionInComposedTree.extent());
EXPECT_EQ(PositionInComposedTree(one, 0), selectionInComposedTree.start());
EXPECT_EQ(PositionInComposedTree(five, 5), selectionInComposedTree.end());
// From a position at distributed node
selection = VisibleSelection(createVisiblePosition(Position(two, 1)));
selection.expandUsingGranularity(WordGranularity);
selectionInComposedTree = VisibleSelectionInComposedTree(createVisiblePosition(PositionInComposedTree(two, 1)));
selectionInComposedTree.expandUsingGranularity(WordGranularity);
EXPECT_EQ(Position(two, 1), selection.base());
EXPECT_EQ(Position(two, 1), selection.extent());
EXPECT_EQ(Position(one, 0), selection.start());
EXPECT_EQ(Position(two, 2), selection.end());
EXPECT_EQ(PositionInComposedTree(two, 1), selectionInComposedTree.base());
EXPECT_EQ(PositionInComposedTree(two, 1), selectionInComposedTree.extent());
EXPECT_EQ(PositionInComposedTree(three, 0), selectionInComposedTree.start());
EXPECT_EQ(PositionInComposedTree(four, 4), selectionInComposedTree.end());
// From a position at node in shadow tree
selection = VisibleSelection(createVisiblePosition(Position(three, 1)));
selection.expandUsingGranularity(WordGranularity);
selectionInComposedTree = VisibleSelectionInComposedTree(createVisiblePosition(PositionInComposedTree(three, 1)));
selectionInComposedTree.expandUsingGranularity(WordGranularity);
EXPECT_EQ(Position(three, 1), selection.base());
EXPECT_EQ(Position(three, 1), selection.extent());
EXPECT_EQ(Position(three, 0), selection.start());
EXPECT_EQ(Position(four, 4), selection.end());
EXPECT_EQ(PositionInComposedTree(three, 1), selectionInComposedTree.base());
EXPECT_EQ(PositionInComposedTree(three, 1), selectionInComposedTree.extent());
EXPECT_EQ(PositionInComposedTree(three, 0), selectionInComposedTree.start());
EXPECT_EQ(PositionInComposedTree(four, 4), selectionInComposedTree.end());
// From a position at node in shadow tree
selection = VisibleSelection(createVisiblePosition(Position(four, 1)));
selection.expandUsingGranularity(WordGranularity);
selectionInComposedTree = VisibleSelectionInComposedTree(createVisiblePosition(PositionInComposedTree(four, 1)));
selectionInComposedTree.expandUsingGranularity(WordGranularity);
EXPECT_EQ(Position(four, 1), selection.base());
EXPECT_EQ(Position(four, 1), selection.extent());
EXPECT_EQ(Position(three, 0), selection.start());
EXPECT_EQ(Position(four, 4), selection.end());
EXPECT_EQ(PositionInComposedTree(four, 1), selectionInComposedTree.base());
EXPECT_EQ(PositionInComposedTree(four, 1), selectionInComposedTree.extent());
EXPECT_EQ(PositionInComposedTree(three, 0), selectionInComposedTree.start());
EXPECT_EQ(PositionInComposedTree(four, 4), selectionInComposedTree.end());
// From a position at node in shadow tree
selection = VisibleSelection(createVisiblePosition(Position(five, 1)));
selection.expandUsingGranularity(WordGranularity);
selectionInComposedTree = VisibleSelectionInComposedTree(createVisiblePosition(PositionInComposedTree(five, 1)));
selectionInComposedTree.expandUsingGranularity(WordGranularity);
EXPECT_EQ(Position(five, 1), selection.base());
EXPECT_EQ(Position(five, 1), selection.extent());
EXPECT_EQ(Position(five, 0), selection.start());
EXPECT_EQ(Position(five, 5), selection.end());
EXPECT_EQ(PositionInComposedTree(five, 1), selectionInComposedTree.base());
EXPECT_EQ(PositionInComposedTree(five, 1), selectionInComposedTree.extent());
EXPECT_EQ(PositionInComposedTree(one, 0), selectionInComposedTree.start());
EXPECT_EQ(PositionInComposedTree(five, 5), selectionInComposedTree.end());
}
TEST_F(VisibleSelectionTest, Initialisation)
{
setBodyContent(LOREM_IPSUM);
VisibleSelection selection;
VisibleSelectionInComposedTree selectionInComposedTree;
setSelection(selection, 0);
setSelection(selectionInComposedTree, 0);
EXPECT_FALSE(selection.isNone());
EXPECT_FALSE(selectionInComposedTree.isNone());
EXPECT_TRUE(selection.isCaret());
EXPECT_TRUE(selectionInComposedTree.isCaret());
RefPtrWillBeRawPtr<Range> range = firstRangeOf(selection);
EXPECT_EQ(0, range->startOffset());
EXPECT_EQ(0, range->endOffset());
EXPECT_EQ("", range->text());
testComposedTreePositionsToEqualToDOMTreePositions(selection, selectionInComposedTree);
}
TEST_F(VisibleSelectionTest, ShadowCrossing)
{
const char* bodyContent = "<p id='host'>00<b id='one'>11</b><b id='two'>22</b>33</p>";
const char* shadowContent = "<a><span id='s4'>44</span><content select=#two></content><span id='s5'>55</span><content select=#one></content><span id='s6'>66</span></a>";
setBodyContent(bodyContent);
RefPtrWillBeRawPtr<ShadowRoot> shadowRoot = setShadowContent(shadowContent, "host");
RefPtrWillBeRawPtr<Element> body = document().body();
RefPtrWillBeRawPtr<Element> host = body->querySelector("#host", ASSERT_NO_EXCEPTION);
RefPtrWillBeRawPtr<Element> one = body->querySelector("#one", ASSERT_NO_EXCEPTION);
RefPtrWillBeRawPtr<Element> six = shadowRoot->querySelector("#s6", ASSERT_NO_EXCEPTION);
VisibleSelection selection(Position::firstPositionInNode(one.get()), Position::lastPositionInNode(shadowRoot.get()));
VisibleSelectionInComposedTree selectionInComposedTree(PositionInComposedTree::firstPositionInNode(one.get()), PositionInComposedTree::lastPositionInNode(host.get()));
EXPECT_EQ(Position(host.get(), PositionAnchorType::BeforeAnchor), selection.start());
EXPECT_EQ(Position(one->firstChild(), 0), selection.end());
EXPECT_EQ(PositionInComposedTree(one->firstChild(), 0), selectionInComposedTree.start());
EXPECT_EQ(PositionInComposedTree(six->firstChild(), 2), selectionInComposedTree.end());
}
TEST_F(VisibleSelectionTest, ShadowV0DistributedNodes)
{
const char* bodyContent = "<p id='host'>00<b id='one'>11</b><b id='two'>22</b>33</p>";
const char* shadowContent = "<a><span id='s4'>44</span><content select=#two></content><span id='s5'>55</span><content select=#one></content><span id='s6'>66</span></a>";
setBodyContent(bodyContent);
RefPtrWillBeRawPtr<ShadowRoot> shadowRoot = setShadowContent(shadowContent, "host");
RefPtrWillBeRawPtr<Element> body = document().body();
RefPtrWillBeRawPtr<Element> one = body->querySelector("#one", ASSERT_NO_EXCEPTION);
RefPtrWillBeRawPtr<Element> two = body->querySelector("#two", ASSERT_NO_EXCEPTION);
RefPtrWillBeRawPtr<Element> five = shadowRoot->querySelector("#s5", ASSERT_NO_EXCEPTION);
VisibleSelection selection(Position::firstPositionInNode(one.get()), Position::lastPositionInNode(two.get()));
VisibleSelectionInComposedTree selectionInComposedTree(PositionInComposedTree::firstPositionInNode(one.get()), PositionInComposedTree::lastPositionInNode(two.get()));
EXPECT_EQ(Position(one->firstChild(), 0), selection.start());
EXPECT_EQ(Position(two->firstChild(), 2), selection.end());
EXPECT_EQ(PositionInComposedTree(five->firstChild(), 0), selectionInComposedTree.start());
EXPECT_EQ(PositionInComposedTree(five->firstChild(), 2), selectionInComposedTree.end());
}
TEST_F(VisibleSelectionTest, ShadowNested)
{
const char* bodyContent = "<p id='host'>00<b id='one'>11</b><b id='two'>22</b>33</p>";
const char* shadowContent = "<a><span id='s4'>44</span><content select=#two></content><span id='s5'>55</span><content select=#one></content><span id='s6'>66</span></a>";
const char* shadowContent2 = "<span id='s7'>77</span><content></content><span id='s8'>88</span>";
setBodyContent(bodyContent);
RefPtrWillBeRawPtr<ShadowRoot> shadowRoot = setShadowContent(shadowContent, "host");
RefPtrWillBeRawPtr<ShadowRoot> shadowRoot2 = createShadowRootForElementWithIDAndSetInnerHTML(*shadowRoot, "s5", shadowContent2);
// Composed tree is something like below:
// <p id="host">
// <span id="s4">44</span>
// <b id="two">22</b>
// <span id="s5"><span id="s7">77>55</span id="s8">88</span>
// <b id="one">11</b>
// <span id="s6">66</span>
// </p>
RefPtrWillBeRawPtr<Element> body = document().body();
RefPtrWillBeRawPtr<Element> host = body->querySelector("#host", ASSERT_NO_EXCEPTION);
RefPtrWillBeRawPtr<Element> one = body->querySelector("#one", ASSERT_NO_EXCEPTION);
RefPtrWillBeRawPtr<Element> eight = shadowRoot2->querySelector("#s8", ASSERT_NO_EXCEPTION);
VisibleSelection selection(Position::firstPositionInNode(one.get()), Position::lastPositionInNode(shadowRoot2.get()));
VisibleSelectionInComposedTree selectionInComposedTree(PositionInComposedTree::firstPositionInNode(one.get()), PositionInComposedTree::afterNode(eight.get()));
EXPECT_EQ(Position(host.get(), PositionAnchorType::BeforeAnchor), selection.start());
EXPECT_EQ(Position(one->firstChild(), 0), selection.end());
EXPECT_EQ(PositionInComposedTree(eight->firstChild(), 2), selectionInComposedTree.start());
EXPECT_EQ(PositionInComposedTree(eight->firstChild(), 2), selectionInComposedTree.end());
}
TEST_F(VisibleSelectionTest, WordGranularity)
{
setBodyContent(LOREM_IPSUM);
VisibleSelection selection;
VisibleSelectionInComposedTree selectionInComposedTree;
// Beginning of a word.
{
setSelection(selection, 0);
setSelection(selectionInComposedTree, 0);
selection.expandUsingGranularity(WordGranularity);
selectionInComposedTree.expandUsingGranularity(WordGranularity);
RefPtrWillBeRawPtr<Range> range = firstRangeOf(selection);
EXPECT_EQ(0, range->startOffset());
EXPECT_EQ(5, range->endOffset());
EXPECT_EQ("Lorem", range->text());
testComposedTreePositionsToEqualToDOMTreePositions(selection, selectionInComposedTree);
}
// Middle of a word.
{
setSelection(selection, 8);
setSelection(selectionInComposedTree, 8);
selection.expandUsingGranularity(WordGranularity);
selectionInComposedTree.expandUsingGranularity(WordGranularity);
RefPtrWillBeRawPtr<Range> range = firstRangeOf(selection);
EXPECT_EQ(6, range->startOffset());
EXPECT_EQ(11, range->endOffset());
EXPECT_EQ("ipsum", range->text());
testComposedTreePositionsToEqualToDOMTreePositions(selection, selectionInComposedTree);
}
// End of a word.
// FIXME: that sounds buggy, we might want to select the word _before_ instead
// of the space...
{
setSelection(selection, 5);
setSelection(selectionInComposedTree, 5);
selection.expandUsingGranularity(WordGranularity);
selectionInComposedTree.expandUsingGranularity(WordGranularity);
RefPtrWillBeRawPtr<Range> range = firstRangeOf(selection);
EXPECT_EQ(5, range->startOffset());
EXPECT_EQ(6, range->endOffset());
EXPECT_EQ(" ", range->text());
testComposedTreePositionsToEqualToDOMTreePositions(selection, selectionInComposedTree);
}
// Before comma.
// FIXME: that sounds buggy, we might want to select the word _before_ instead
// of the comma.
{
setSelection(selection, 26);
setSelection(selectionInComposedTree, 26);
selection.expandUsingGranularity(WordGranularity);
selectionInComposedTree.expandUsingGranularity(WordGranularity);
RefPtrWillBeRawPtr<Range> range = firstRangeOf(selection);
EXPECT_EQ(26, range->startOffset());
EXPECT_EQ(27, range->endOffset());
EXPECT_EQ(",", range->text());
testComposedTreePositionsToEqualToDOMTreePositions(selection, selectionInComposedTree);
}
// After comma.
{
setSelection(selection, 27);
setSelection(selectionInComposedTree, 27);
selection.expandUsingGranularity(WordGranularity);
selectionInComposedTree.expandUsingGranularity(WordGranularity);
RefPtrWillBeRawPtr<Range> range = firstRangeOf(selection);
EXPECT_EQ(27, range->startOffset());
EXPECT_EQ(28, range->endOffset());
EXPECT_EQ(" ", range->text());
testComposedTreePositionsToEqualToDOMTreePositions(selection, selectionInComposedTree);
}
// When selecting part of a word.
{
setSelection(selection, 0, 1);
setSelection(selectionInComposedTree, 0, 1);
selection.expandUsingGranularity(WordGranularity);
selectionInComposedTree.expandUsingGranularity(WordGranularity);
RefPtrWillBeRawPtr<Range> range = firstRangeOf(selection);
EXPECT_EQ(0, range->startOffset());
EXPECT_EQ(5, range->endOffset());
EXPECT_EQ("Lorem", range->text());
testComposedTreePositionsToEqualToDOMTreePositions(selection, selectionInComposedTree);
}
// When selecting part of two words.
{
setSelection(selection, 2, 8);
setSelection(selectionInComposedTree, 2, 8);
selection.expandUsingGranularity(WordGranularity);
selectionInComposedTree.expandUsingGranularity(WordGranularity);
RefPtrWillBeRawPtr<Range> range = firstRangeOf(selection);
EXPECT_EQ(0, range->startOffset());
EXPECT_EQ(11, range->endOffset());
EXPECT_EQ("Lorem ipsum", range->text());
testComposedTreePositionsToEqualToDOMTreePositions(selection, selectionInComposedTree);
}
}
} // namespace blink