blob: 17bff03d1fb3d22f97b604f1c93c7e073c482f9a [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 <memory>
#include "base/memory/scoped_refptr.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/element.h"
#include "third_party/blink/renderer/core/dom/text.h"
#include "third_party/blink/renderer/core/editing/frame_selection.h"
#include "third_party/blink/renderer/core/editing/local_caret_rect.h"
#include "third_party/blink/renderer/core/editing/selection_template.h"
#include "third_party/blink/renderer/core/editing/visible_position.h"
#include "third_party/blink/renderer/core/editing/visible_selection.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/frame/settings.h"
#include "third_party/blink/renderer/core/html/html_body_element.h"
#include "third_party/blink/renderer/core/html/html_span_element.h"
#include "third_party/blink/renderer/core/testing/page_test_base.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/heap/handle.h"
#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
namespace blink {
#define EXPECT_EQ_SELECTED_TEXT(text) \
EXPECT_EQ(text, WebString(Selection().SelectedText()).Utf8())
IntPoint VisiblePositionToContentsPoint(const VisiblePosition& pos) {
IntPoint result = AbsoluteSelectionBoundsOf(pos).MinXMaxYCorner();
// Need to move the point at least by 1 - caret's minXMaxYCorner is not
// evaluated to the same line as the text by hit testing.
result.Move(0, -1);
return result;
}
using TextNodeVector = HeapVector<Member<Text>>;
class GranularityStrategyTest : public PageTestBase {
protected:
void SetUp() override;
Text* AppendTextNode(const String& data);
void SetInnerHTML(const char*);
// Parses the text node, appending the info to letter_pos_ and word_middles_.
void ParseText(Text*);
void ParseText(const TextNodeVector&);
Text* SetupTranslateZ(String);
Text* SetupTransform(String);
Text* SetupRotate(String);
void SetupTextSpan(String str1,
String str2,
String str3,
wtf_size_t sel_begin,
wtf_size_t sel_end);
void SetupVerticalAlign(String str1,
String str2,
String str3,
wtf_size_t sel_begin,
wtf_size_t sel_end);
void SetupFontSize(String str1,
String str2,
String str3,
wtf_size_t sel_begin,
wtf_size_t sel_end);
void TestDirectionExpand();
void TestDirectionShrink();
void TestDirectionSwitchSide();
// Pixel coordinates of the positions for each letter within the text being
// tested.
Vector<IntPoint> letter_pos_;
// Pixel coordinates of the middles of the words in the text being tested.
// (y coordinate is based on y coordinates of letter_pos_)
Vector<IntPoint> word_middles_;
};
void GranularityStrategyTest::SetUp() {
PageTestBase::SetUp();
GetFrame().GetSettings()->SetDefaultFontSize(12);
GetFrame().GetSettings()->SetSelectionStrategy(SelectionStrategy::kDirection);
}
Text* GranularityStrategyTest::AppendTextNode(const String& data) {
Text* text = GetDocument().createTextNode(data);
GetDocument().body()->AppendChild(text);
return text;
}
void GranularityStrategyTest::SetInnerHTML(const char* html_content) {
GetDocument().documentElement()->SetInnerHTMLFromString(
String::FromUTF8(html_content));
UpdateAllLifecyclePhasesForTest();
}
void GranularityStrategyTest::ParseText(Text* text) {
TextNodeVector text_nodes;
text_nodes.push_back(text);
ParseText(text_nodes);
}
void GranularityStrategyTest::ParseText(const TextNodeVector& text_nodes) {
bool word_started = false;
int word_start_index = 0;
for (auto& text : text_nodes) {
wtf_size_t word_start_index_offset = letter_pos_.size();
String str = text->wholeText();
for (wtf_size_t i = 0; i < str.length(); i++) {
letter_pos_.push_back(VisiblePositionToContentsPoint(
CreateVisiblePosition(Position(text, i))));
char c = str[i];
if (IsASCIIAlphanumeric(c) && !word_started) {
word_start_index = i + word_start_index_offset;
word_started = true;
} else if (!IsASCIIAlphanumeric(c) && word_started) {
IntPoint word_middle((letter_pos_[word_start_index].X() +
letter_pos_[i + word_start_index_offset].X()) /
2,
letter_pos_[word_start_index].Y());
word_middles_.push_back(word_middle);
word_started = false;
}
}
}
if (word_started) {
const auto& last_node = text_nodes.back();
int x_end = VisiblePositionToContentsPoint(
CreateVisiblePosition(
Position(last_node, last_node->wholeText().length())))
.X();
IntPoint word_middle((letter_pos_[word_start_index].X() + x_end) / 2,
letter_pos_[word_start_index].Y());
word_middles_.push_back(word_middle);
}
}
Text* GranularityStrategyTest::SetupTranslateZ(String str) {
SetInnerHTML(
"<html>"
"<head>"
"<style>"
"div {"
"transform: translateZ(0);"
"}"
"</style>"
"</head>"
"<body>"
"<div id='mytext'></div>"
"</body>"
"</html>");
Text* text = GetDocument().createTextNode(str);
Element* div = GetDocument().getElementById("mytext");
div->AppendChild(text);
UpdateAllLifecyclePhasesForTest();
ParseText(text);
return text;
}
Text* GranularityStrategyTest::SetupTransform(String str) {
SetInnerHTML(
"<html>"
"<head>"
"<style>"
"div {"
"transform: scale(1,-1) translate(0,-100px);"
"}"
"</style>"
"</head>"
"<body>"
"<div id='mytext'></div>"
"</body>"
"</html>");
Text* text = GetDocument().createTextNode(str);
Element* div = GetDocument().getElementById("mytext");
div->AppendChild(text);
UpdateAllLifecyclePhasesForTest();
ParseText(text);
return text;
}
Text* GranularityStrategyTest::SetupRotate(String str) {
SetInnerHTML(
"<html>"
"<head>"
"<style>"
"div {"
"transform: translate(0px,600px) rotate(90deg);"
"}"
"</style>"
"</head>"
"<body>"
"<div id='mytext'></div>"
"</body>"
"</html>");
Text* text = GetDocument().createTextNode(str);
Element* div = GetDocument().getElementById("mytext");
div->AppendChild(text);
UpdateAllLifecyclePhasesForTest();
ParseText(text);
return text;
}
void GranularityStrategyTest::SetupTextSpan(String str1,
String str2,
String str3,
wtf_size_t sel_begin,
wtf_size_t sel_end) {
Text* text1 = GetDocument().createTextNode(str1);
Text* text2 = GetDocument().createTextNode(str2);
Text* text3 = GetDocument().createTextNode(str3);
auto* span = MakeGarbageCollected<HTMLSpanElement>(GetDocument());
Element* div = GetDocument().getElementById("mytext");
div->AppendChild(text1);
div->AppendChild(span);
span->AppendChild(text2);
div->AppendChild(text3);
UpdateAllLifecyclePhasesForTest();
Vector<IntPoint> letter_pos;
Vector<IntPoint> word_middle_pos;
TextNodeVector text_nodes;
text_nodes.push_back(text1);
text_nodes.push_back(text2);
text_nodes.push_back(text3);
ParseText(text_nodes);
Position p1;
Position p2;
if (sel_begin < str1.length())
p1 = Position(text1, sel_begin);
else if (sel_begin < str1.length() + str2.length())
p1 = Position(text2, sel_begin - str1.length());
else
p1 = Position(text3, sel_begin - str1.length() - str2.length());
if (sel_end < str1.length())
p2 = Position(text1, sel_end);
else if (sel_end < str1.length() + str2.length())
p2 = Position(text2, sel_end - str1.length());
else
p2 = Position(text3, sel_end - str1.length() - str2.length());
Selection().SetSelection(
SelectionInDOMTree::Builder().SetBaseAndExtent(p1, p2).Build(),
SetSelectionOptions());
}
void GranularityStrategyTest::SetupVerticalAlign(String str1,
String str2,
String str3,
wtf_size_t sel_begin,
wtf_size_t sel_end) {
SetInnerHTML(
"<html>"
"<head>"
"<style>"
"span {"
"vertical-align:20px;"
"}"
"</style>"
"</head>"
"<body>"
"<div id='mytext'></div>"
"</body>"
"</html>");
SetupTextSpan(str1, str2, str3, sel_begin, sel_end);
}
void GranularityStrategyTest::SetupFontSize(String str1,
String str2,
String str3,
wtf_size_t sel_begin,
wtf_size_t sel_end) {
SetInnerHTML(
"<html>"
"<head>"
"<style>"
"span {"
"font-size: 200%;"
"}"
"</style>"
"</head>"
"<body>"
"<div id='mytext'></div>"
"</body>"
"</html>");
SetupTextSpan(str1, str2, str3, sel_begin, sel_end);
}
// Tests expanding selection on text "abcdef ghij kl mno^p|>qr stuvwi inm mnii,"
// (^ means base, | means extent, < means start, and > means end). Text needs to
// be laid out on a single line with no rotation.
void GranularityStrategyTest::TestDirectionExpand() {
// Expand selection using character granularity until the end of the word
// is reached.
// "abcdef ghij kl mno^pq|>r stuvwi inm mnii,"
Selection().MoveRangeSelectionExtent(letter_pos_[20]);
EXPECT_EQ_SELECTED_TEXT("pq");
// Move to the same postion shouldn't change anything.
Selection().MoveRangeSelectionExtent(letter_pos_[20]);
EXPECT_EQ_SELECTED_TEXT("pq");
// "abcdef ghij kl mno^pqr|> stuvwi inm mnii,"
Selection().MoveRangeSelectionExtent(letter_pos_[21]);
EXPECT_EQ_SELECTED_TEXT("pqr");
// Selection should stay the same until the middle of the word is passed.
// "abcdef ghij kl mno^pqr |>stuvwi inm mnii," -
Selection().MoveRangeSelectionExtent(letter_pos_[22]);
EXPECT_EQ_SELECTED_TEXT("pqr ");
// "abcdef ghij kl mno^pqr >st|uvwi inm mnii,"
Selection().MoveRangeSelectionExtent(letter_pos_[24]);
EXPECT_EQ_SELECTED_TEXT("pqr ");
IntPoint p = word_middles_[4];
p.Move(-1, 0);
Selection().MoveRangeSelectionExtent(p);
EXPECT_EQ_SELECTED_TEXT("pqr ");
p.Move(1, 0);
Selection().MoveRangeSelectionExtent(p);
EXPECT_EQ_SELECTED_TEXT("pqr stuvwi");
// Selection should stay the same until the end of the word is reached.
// "abcdef ghij kl mno^pqr stuvw|i> inm mnii,"
Selection().MoveRangeSelectionExtent(letter_pos_[27]);
EXPECT_EQ_SELECTED_TEXT("pqr stuvwi");
// "abcdef ghij kl mno^pqr stuvwi|> inm mnii,"
Selection().MoveRangeSelectionExtent(letter_pos_[28]);
EXPECT_EQ_SELECTED_TEXT("pqr stuvwi");
// "abcdef ghij kl mno^pqr stuvwi |>inm mnii,"
Selection().MoveRangeSelectionExtent(letter_pos_[29]);
EXPECT_EQ_SELECTED_TEXT("pqr stuvwi ");
// Now expand slowly to the middle of word #5.
int y = letter_pos_[29].Y();
for (int x = letter_pos_[29].X() + 1; x < word_middles_[5].X(); x++) {
Selection().MoveRangeSelectionExtent(IntPoint(x, y));
Selection().MoveRangeSelectionExtent(IntPoint(x, y));
EXPECT_EQ_SELECTED_TEXT("pqr stuvwi ");
}
Selection().MoveRangeSelectionExtent(word_middles_[5]);
EXPECT_EQ_SELECTED_TEXT("pqr stuvwi inm");
// Jump over quickly to just before the middle of the word #6 and then
// move over it.
p = word_middles_[6];
p.Move(-1, 0);
Selection().MoveRangeSelectionExtent(p);
EXPECT_EQ_SELECTED_TEXT("pqr stuvwi inm ");
p.Move(1, 0);
Selection().MoveRangeSelectionExtent(p);
EXPECT_EQ_SELECTED_TEXT("pqr stuvwi inm mnii");
}
// Tests shrinking selection on text "abcdef ghij kl mno^pqr|> iiinmni, abc"
// (^ means base, | means extent, < means start, and > means end).
// Text needs to be laid out on a single line with no rotation.
void GranularityStrategyTest::TestDirectionShrink() {
// Move to the middle of word #4 to it and then move back, confirming
// that the selection end is moving with the extent. The offset between the
// extent and the selection end will be equal to half the width of "iiinmni".
Selection().MoveRangeSelectionExtent(word_middles_[4]);
EXPECT_EQ_SELECTED_TEXT("pqr iiinmni");
IntPoint p = word_middles_[4];
p.Move(letter_pos_[28].X() - letter_pos_[29].X(), 0);
Selection().MoveRangeSelectionExtent(p);
EXPECT_EQ_SELECTED_TEXT("pqr iiinmn");
p.Move(letter_pos_[27].X() - letter_pos_[28].X(), 0);
Selection().MoveRangeSelectionExtent(p);
EXPECT_EQ_SELECTED_TEXT("pqr iiinm");
p.Move(letter_pos_[26].X() - letter_pos_[27].X(), 0);
Selection().MoveRangeSelectionExtent(p);
EXPECT_EQ_SELECTED_TEXT("pqr iiin");
// Move right by the width of char 30 ('m'). Selection shouldn't change,
// but offset should be reduced.
p.Move(letter_pos_[27].X() - letter_pos_[26].X(), 0);
Selection().MoveRangeSelectionExtent(p);
EXPECT_EQ_SELECTED_TEXT("pqr iiin");
// Move back a couple of character widths and confirm the selection still
// updates accordingly.
p.Move(letter_pos_[25].X() - letter_pos_[26].X(), 0);
Selection().MoveRangeSelectionExtent(p);
EXPECT_EQ_SELECTED_TEXT("pqr iii");
p.Move(letter_pos_[24].X() - letter_pos_[25].X(), 0);
Selection().MoveRangeSelectionExtent(p);
EXPECT_EQ_SELECTED_TEXT("pqr ii");
// "Catch up" with the handle - move the extent to where the handle is.
// "abcdef ghij kl mno^pqr ii|>inmni, abc"
Selection().MoveRangeSelectionExtent(letter_pos_[24]);
EXPECT_EQ_SELECTED_TEXT("pqr ii");
// Move ahead and confirm the selection expands accordingly
// "abcdef ghij kl mno^pqr iii|>nmni, abc"
Selection().MoveRangeSelectionExtent(letter_pos_[25]);
EXPECT_EQ_SELECTED_TEXT("pqr iii");
// Confirm we stay in character granularity if the user moves within a word.
// "abcdef ghij kl mno^pqr |>iiinmni, abc"
Selection().MoveRangeSelectionExtent(letter_pos_[22]);
EXPECT_EQ_SELECTED_TEXT("pqr ");
// It's possible to get a move when position doesn't change.
// It shouldn't affect anything.
p = letter_pos_[22];
p.Move(1, 0);
Selection().MoveRangeSelectionExtent(p);
EXPECT_EQ_SELECTED_TEXT("pqr ");
// "abcdef ghij kl mno^pqr i|>iinmni, abc"
Selection().MoveRangeSelectionExtent(letter_pos_[23]);
EXPECT_EQ_SELECTED_TEXT("pqr i");
}
// Tests moving selection extent over to the other side of the base
// on text "abcd efgh ijkl mno^pqr|> iiinmni, abc"
// (^ means base, | means extent, < means start, and > means end).
// Text needs to be laid out on a single line with no rotation.
void GranularityStrategyTest::TestDirectionSwitchSide() {
// Move to the middle of word #4, selecting it - this will set the offset to
// be half the width of "iiinmni.
Selection().MoveRangeSelectionExtent(word_middles_[4]);
EXPECT_EQ_SELECTED_TEXT("pqr iiinmni");
// Move back leaving only one letter selected.
IntPoint p = word_middles_[4];
p.Move(letter_pos_[19].X() - letter_pos_[29].X(), 0);
Selection().MoveRangeSelectionExtent(p);
EXPECT_EQ_SELECTED_TEXT("p");
// Confirm selection doesn't change if extent is positioned at base.
p.Move(letter_pos_[18].X() - letter_pos_[19].X(), 0);
Selection().MoveRangeSelectionExtent(p);
EXPECT_EQ_SELECTED_TEXT("p");
// Move over to the other side of the base. Confirm the offset is preserved.
// (i.e. the selection start stays on the right of the extent)
// Confirm we stay in character granularity until the beginning of the word
// is passed.
p.Move(letter_pos_[17].X() - letter_pos_[18].X(), 0);
Selection().MoveRangeSelectionExtent(p);
EXPECT_EQ_SELECTED_TEXT("o");
p.Move(letter_pos_[16].X() - letter_pos_[17].X(), 0);
Selection().MoveRangeSelectionExtent(p);
EXPECT_EQ_SELECTED_TEXT("no");
p.Move(letter_pos_[14].X() - letter_pos_[16].X(), 0);
Selection().MoveRangeSelectionExtent(p);
EXPECT_EQ_SELECTED_TEXT(" mno");
// Move to just one pixel on the right before the middle of the word #2.
// We should switch to word granularity, so the selection shouldn't change.
p.Move(word_middles_[2].X() - letter_pos_[14].X() + 1, 0);
Selection().MoveRangeSelectionExtent(p);
EXPECT_EQ_SELECTED_TEXT(" mno");
// Move over the middle of the word. The word should get selected.
// This should reduce the offset, but it should still stay greated than 0,
// since the width of "iiinmni" is greater than the width of "ijkl".
p.Move(-2, 0);
Selection().MoveRangeSelectionExtent(p);
EXPECT_EQ_SELECTED_TEXT("ijkl mno");
// Move to just one pixel on the right of the middle of word #1.
// The selection should now include the space between the words.
p.Move(word_middles_[1].X() - letter_pos_[10].X() + 1, 0);
Selection().MoveRangeSelectionExtent(p);
EXPECT_EQ_SELECTED_TEXT(" ijkl mno");
// Move over the middle of the word. The word should get selected.
p.Move(-2, 0);
Selection().MoveRangeSelectionExtent(p);
EXPECT_EQ_SELECTED_TEXT("efgh ijkl mno");
}
// Test for the default CharacterGranularityStrategy
TEST_F(GranularityStrategyTest, Character) {
GetDummyPageHolder().GetFrame().GetSettings()->SetSelectionStrategy(
SelectionStrategy::kCharacter);
GetDummyPageHolder().GetFrame().GetSettings()->SetDefaultFontSize(12);
// "Foo Bar Baz,"
Text* text = AppendTextNode("Foo Bar Baz,");
GetDocument().UpdateStyleAndLayout();
// "Foo B^a|>r Baz," (^ means base, | means extent, , < means start, and >
// means end).
Selection().SetSelection(
SelectionInDOMTree::Builder()
.SetBaseAndExtent(Position(text, 5), Position(text, 6))
.Build(),
SetSelectionOptions());
EXPECT_EQ_SELECTED_TEXT("a");
// "Foo B^ar B|>az,"
Selection().MoveRangeSelectionExtent(
VisiblePositionToContentsPoint(CreateVisiblePosition(Position(text, 9))));
EXPECT_EQ_SELECTED_TEXT("ar B");
// "F<|oo B^ar Baz,"
Selection().MoveRangeSelectionExtent(
VisiblePositionToContentsPoint(CreateVisiblePosition(Position(text, 1))));
EXPECT_EQ_SELECTED_TEXT("oo B");
}
// DirectionGranularityStrategy strategy on rotated text should revert to the
// same behavior as CharacterGranularityStrategy
TEST_F(GranularityStrategyTest, DirectionRotate) {
Text* text = SetupRotate("Foo Bar Baz,");
// "Foo B^a|>r Baz," (^ means base, | means extent, , < means start, and >
// means end).
Selection().SetSelection(
SelectionInDOMTree::Builder()
.SetBaseAndExtent(Position(text, 5), Position(text, 6))
.Build(),
SetSelectionOptions());
EXPECT_EQ_SELECTED_TEXT("a");
IntPoint p = letter_pos_[9];
// Need to move by one pixel, otherwise this point is not evaluated
// to the same line as the text by hit testing.
p.Move(1, 0);
// "Foo B^ar B|>az,"
Selection().MoveRangeSelectionExtent(p);
EXPECT_EQ_SELECTED_TEXT("ar B");
p = letter_pos_[1];
p.Move(1, 0);
// "F<|oo B^ar Baz,"
Selection().MoveRangeSelectionExtent(p);
EXPECT_EQ_SELECTED_TEXT("oo B");
}
TEST_F(GranularityStrategyTest, DirectionExpandTranslateZ) {
Text* text = SetupTranslateZ("abcdef ghij kl mnopqr stuvwi inm mnii,");
// "abcdef ghij kl mno^p|>qr stuvwi inm mnii," (^ means base, | means extent,
// < means start, and > means end).
Selection().SetSelection(
SelectionInDOMTree::Builder()
.SetBaseAndExtent(Position(text, 18), Position(text, 19))
.Build(),
SetSelectionOptions());
EXPECT_EQ_SELECTED_TEXT("p");
TestDirectionExpand();
}
TEST_F(GranularityStrategyTest, DirectionExpandTransform) {
Text* text = SetupTransform("abcdef ghij kl mnopqr stuvwi inm mnii,");
// "abcdef ghij kl mno^p|>qr stuvwi inm mnii," (^ means base, | means extent,
// < means start, and > means end).
Selection().SetSelection(
SelectionInDOMTree::Builder()
.SetBaseAndExtent(Position(text, 18), Position(text, 19))
.Build(),
SetSelectionOptions());
EXPECT_EQ_SELECTED_TEXT("p");
TestDirectionExpand();
}
TEST_F(GranularityStrategyTest, DirectionExpandVerticalAlign) {
// "abcdef ghij kl mno^p|>qr stuvwi inm mnii," (^ means base, | means extent,
// < means start, and > means end).
SetupVerticalAlign("abcdef ghij kl m", "nopq", "r stuvwi inm mnii,", 18, 19);
EXPECT_EQ_SELECTED_TEXT("p");
TestDirectionExpand();
}
TEST_F(GranularityStrategyTest, DirectionExpandFontSizes) {
SetupFontSize("abcdef ghij kl mnopqr st", "uv", "wi inm mnii,", 18, 19);
EXPECT_EQ_SELECTED_TEXT("p");
TestDirectionExpand();
}
TEST_F(GranularityStrategyTest, DirectionShrinkTranslateZ) {
Text* text = SetupTranslateZ("abcdef ghij kl mnopqr iiinmni, abc");
Selection().SetSelection(
SelectionInDOMTree::Builder()
.SetBaseAndExtent(Position(text, 18), Position(text, 21))
.Build(),
SetSelectionOptions());
EXPECT_EQ_SELECTED_TEXT("pqr");
TestDirectionShrink();
}
TEST_F(GranularityStrategyTest, DirectionShrinkTransform) {
Text* text = SetupTransform("abcdef ghij kl mnopqr iiinmni, abc");
Selection().SetSelection(
SelectionInDOMTree::Builder()
.SetBaseAndExtent(Position(text, 18), Position(text, 21))
.Build(),
SetSelectionOptions());
EXPECT_EQ_SELECTED_TEXT("pqr");
TestDirectionShrink();
}
TEST_F(GranularityStrategyTest, DirectionShrinkVerticalAlign) {
SetupVerticalAlign("abcdef ghij kl mnopqr ii", "inm", "ni, abc", 18, 21);
EXPECT_EQ_SELECTED_TEXT("pqr");
TestDirectionShrink();
}
TEST_F(GranularityStrategyTest, DirectionShrinkFontSizes) {
SetupFontSize("abcdef ghij kl mnopqr ii", "inm", "ni, abc", 18, 21);
EXPECT_EQ_SELECTED_TEXT("pqr");
TestDirectionShrink();
}
TEST_F(GranularityStrategyTest, DirectionSwitchSideTranslateZ) {
Text* text = SetupTranslateZ("abcd efgh ijkl mnopqr iiinmni, abc");
Selection().SetSelection(
SelectionInDOMTree::Builder()
.SetBaseAndExtent(Position(text, 18), Position(text, 21))
.Build(),
SetSelectionOptions());
EXPECT_EQ_SELECTED_TEXT("pqr");
TestDirectionSwitchSide();
}
TEST_F(GranularityStrategyTest, DirectionSwitchSideTransform) {
Text* text = SetupTransform("abcd efgh ijkl mnopqr iiinmni, abc");
Selection().SetSelection(
SelectionInDOMTree::Builder()
.SetBaseAndExtent(Position(text, 18), Position(text, 21))
.Build(),
SetSelectionOptions());
EXPECT_EQ_SELECTED_TEXT("pqr");
TestDirectionSwitchSide();
}
TEST_F(GranularityStrategyTest, DirectionSwitchSideVerticalAlign) {
SetupVerticalAlign("abcd efgh ijkl", " mnopqr", " iiinmni, abc", 18, 21);
EXPECT_EQ_SELECTED_TEXT("pqr");
TestDirectionSwitchSide();
}
TEST_F(GranularityStrategyTest, DirectionSwitchSideFontSizes) {
SetupFontSize("abcd efgh i", "jk", "l mnopqr iiinmni, abc", 18, 21);
EXPECT_EQ_SELECTED_TEXT("pqr");
TestDirectionSwitchSide();
}
// Tests moving extent over to the other side of the vase and immediately
// passing the word boundary and going into word granularity.
TEST_F(GranularityStrategyTest, DirectionSwitchSideWordGranularityThenShrink) {
GetDummyPageHolder().GetFrame().GetSettings()->SetDefaultFontSize(12);
String str = "ab cd efghijkl mnopqr iiin, abc";
Text* text = GetDocument().createTextNode(str);
GetDocument().body()->AppendChild(text);
GetDocument().UpdateStyleAndLayout();
GetDummyPageHolder().GetFrame().GetSettings()->SetSelectionStrategy(
SelectionStrategy::kDirection);
ParseText(text);
// "abcd efgh ijkl mno^pqr|> iiin, abc" (^ means base, | means extent, < means
// start, and > means end).
Selection().SetSelection(
SelectionInDOMTree::Builder()
.SetBaseAndExtent(Position(text, 18), Position(text, 21))
.Build(),
SetSelectionOptions());
EXPECT_EQ_SELECTED_TEXT("pqr");
// Move to the middle of word #4 selecting it - this will set the offset to
// be half the width of "iiin".
Selection().MoveRangeSelectionExtent(word_middles_[4]);
EXPECT_EQ_SELECTED_TEXT("pqr iiin");
// Move to the middle of word #2 - extent will switch over to the other
// side of the base, and we should enter word granularity since we pass
// the word boundary. The offset should become negative since the width
// of "efghjkkl" is greater than that of "iiin".
int offset = letter_pos_[26].X() - word_middles_[4].X();
IntPoint p =
IntPoint(word_middles_[2].X() - offset - 1, word_middles_[2].Y());
Selection().MoveRangeSelectionExtent(p);
EXPECT_EQ_SELECTED_TEXT("efghijkl mno");
p.Move(letter_pos_[7].X() - letter_pos_[6].X(), 0);
Selection().MoveRangeSelectionExtent(p);
EXPECT_EQ_SELECTED_TEXT("fghijkl mno");
}
// Make sure we switch to word granularity right away when starting on a
// word boundary and extending.
TEST_F(GranularityStrategyTest, DirectionSwitchStartOnBoundary) {
GetDummyPageHolder().GetFrame().GetSettings()->SetDefaultFontSize(12);
String str = "ab cd efghijkl mnopqr iiin, abc";
Text* text = GetDocument().createTextNode(str);
GetDocument().body()->AppendChild(text);
GetDocument().UpdateStyleAndLayout();
GetDummyPageHolder().GetFrame().GetSettings()->SetSelectionStrategy(
SelectionStrategy::kDirection);
ParseText(text);
// "ab cd efghijkl ^mnopqr |>stuvwi inm," (^ means base and | means extent,
// > means end).
Selection().SetSelection(
SelectionInDOMTree::Builder()
.SetBaseAndExtent(Position(text, 15), Position(text, 22))
.Build(),
SetSelectionOptions());
EXPECT_EQ_SELECTED_TEXT("mnopqr ");
Selection().MoveRangeSelectionExtent(word_middles_[4]);
EXPECT_EQ_SELECTED_TEXT("mnopqr iiin");
}
// For http://crbug.com/704529
TEST_F(GranularityStrategyTest, UpdateExtentWithNullPositionForCharacter) {
GetDummyPageHolder().GetFrame().GetSettings()->SetSelectionStrategy(
SelectionStrategy::kCharacter);
GetDocument().body()->SetInnerHTMLFromString(
"<div id=host></div><div id=sample>ab</div>");
// Simulate VIDEO element which has a RANGE as slider of video time.
Element* const host = GetDocument().getElementById("host");
ShadowRoot& shadow_root =
host->AttachShadowRootInternal(ShadowRootType::kOpen);
shadow_root.SetInnerHTMLFromString("<input type=range>");
Element* const sample = GetDocument().getElementById("sample");
GetDocument().UpdateStyleAndLayout();
const SelectionInDOMTree& selection_in_dom_tree =
SelectionInDOMTree::Builder()
.Collapse(Position(sample->firstChild(), 2))
.Build();
Selection().SetSelection(selection_in_dom_tree,
SetSelectionOptions::Builder()
.SetShouldCloseTyping(true)
.SetShouldClearTypingStyle(true)
.SetShouldShowHandle(true)
.SetIsDirectional(true)
.Build());
// Since, it is not obvious that
// |PositionForContentsPointRespectingEditingBoundary()| returns null
// position, we verify here.
ASSERT_EQ(Position(), CreateVisiblePosition(
PositionForContentsPointRespectingEditingBoundary(
IntPoint(0, 0), &GetFrame()))
.DeepEquivalent())
<< "This test requires null position.";
// Point to RANGE inside shadow root to get null position from
// |visiblePositionForContentsPoint()|.
Selection().MoveRangeSelectionExtent(IntPoint(0, 0));
EXPECT_EQ(selection_in_dom_tree, Selection().GetSelectionInDOMTree());
}
// For http://crbug.com/704529
TEST_F(GranularityStrategyTest, UpdateExtentWithNullPositionForDirectional) {
GetDocument().body()->SetInnerHTMLFromString(
"<div id=host></div><div id=sample>ab</div>");
// Simulate VIDEO element which has a RANGE as slider of video time.
Element* const host = GetDocument().getElementById("host");
ShadowRoot& shadow_root =
host->AttachShadowRootInternal(ShadowRootType::kOpen);
shadow_root.SetInnerHTMLFromString("<input type=range>");
Element* const sample = GetDocument().getElementById("sample");
GetDocument().UpdateStyleAndLayout();
const SelectionInDOMTree& selection_in_dom_tree =
SelectionInDOMTree::Builder()
.Collapse(Position(sample->firstChild(), 2))
.Build();
Selection().SetSelection(selection_in_dom_tree,
SetSelectionOptions::Builder()
.SetShouldCloseTyping(true)
.SetShouldClearTypingStyle(true)
.SetShouldShowHandle(true)
.SetIsDirectional(true)
.Build());
// Since, it is not obvious that
// |PositionForContentsPointRespectingEditingBoundary()| returns null
// position, we verify here.
ASSERT_EQ(Position(), CreateVisiblePosition(
PositionForContentsPointRespectingEditingBoundary(
IntPoint(0, 0), &GetFrame()))
.DeepEquivalent())
<< "This test requires null position.";
// Point to RANGE inside shadow root to get null position from
// |visiblePositionForContentsPoint()|.
Selection().MoveRangeSelectionExtent(IntPoint(0, 0));
EXPECT_EQ(selection_in_dom_tree, Selection().GetSelectionInDOMTree());
}
} // namespace blink