1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ui/gfx/render_text.h"
7 #include "base/memory/scoped_ptr.h"
8 #include "base/utf_string_conversions.h"
9 #include "testing/gtest/include/gtest/gtest.h"
10 #include "ui/gfx/break_list.h"
13 #include "base/win/windows_version.h"
17 #include "ui/gfx/render_text_linux.h"
20 #if defined(TOOLKIT_GTK)
28 // Various weak, LTR, RTL, and Bidi string cases with three characters each.
29 const wchar_t kWeak
[] = L
" . ";
30 const wchar_t kLtr
[] = L
"abc";
31 const wchar_t kLtrRtl
[] = L
"a"L
"\x5d0\x5d1";
32 const wchar_t kLtrRtlLtr
[] = L
"a"L
"\x5d1"L
"b";
33 const wchar_t kRtl
[] = L
"\x5d0\x5d1\x5d2";
34 const wchar_t kRtlLtr
[] = L
"\x5d0\x5d1"L
"a";
35 const wchar_t kRtlLtrRtl
[] = L
"\x5d0"L
"a"L
"\x5d1";
37 // Checks whether |range| contains |index|. This is not the same as calling
38 // |range.Contains(ui::Range(index))| - as that would return true when
39 // |index| == |range.end()|.
40 bool IndexInRange(const ui::Range
& range
, size_t index
) {
41 return index
>= range
.start() && index
< range
.end();
44 #if !defined(OS_MACOSX)
45 // A test utility function to set the application default text direction.
46 void SetRTL(bool rtl
) {
47 // Override the current locale/direction.
48 base::i18n::SetICUDefaultLocale(rtl
? "he" : "en");
49 #if defined(TOOLKIT_GTK)
50 // Do the same for GTK, which does not rely on the ICU default locale.
51 gtk_widget_set_default_direction(rtl
? GTK_TEXT_DIR_RTL
: GTK_TEXT_DIR_LTR
);
53 EXPECT_EQ(rtl
, base::i18n::IsRTL());
59 class RenderTextTest
: public testing::Test
{
62 TEST_F(RenderTextTest
, DefaultStyle
) {
63 // Check the default styles applied to new instances and adjusted text.
64 scoped_ptr
<RenderText
> render_text(RenderText::CreateInstance());
65 EXPECT_TRUE(render_text
->text().empty());
66 const wchar_t* const cases
[] = { kWeak
, kLtr
, L
"Hello", kRtl
, L
"", L
"" };
67 for (size_t i
= 0; i
< arraysize(cases
); ++i
) {
68 EXPECT_TRUE(render_text
->colors().EqualsValueForTesting(SK_ColorBLACK
));
69 for (size_t style
= 0; style
< NUM_TEXT_STYLES
; ++style
)
70 EXPECT_TRUE(render_text
->styles()[style
].EqualsValueForTesting(false));
71 render_text
->SetText(WideToUTF16(cases
[i
]));
75 TEST_F(RenderTextTest
, SetColorAndStyle
) {
76 // Ensure custom default styles persist across setting and clearing text.
77 scoped_ptr
<RenderText
> render_text(RenderText::CreateInstance());
78 const SkColor color
= SK_ColorRED
;
79 render_text
->SetColor(color
);
80 render_text
->SetStyle(BOLD
, true);
81 render_text
->SetStyle(UNDERLINE
, false);
82 const wchar_t* const cases
[] = { kWeak
, kLtr
, L
"Hello", kRtl
, L
"", L
"" };
83 for (size_t i
= 0; i
< arraysize(cases
); ++i
) {
84 EXPECT_TRUE(render_text
->colors().EqualsValueForTesting(color
));
85 EXPECT_TRUE(render_text
->styles()[BOLD
].EqualsValueForTesting(true));
86 EXPECT_TRUE(render_text
->styles()[UNDERLINE
].EqualsValueForTesting(false));
87 render_text
->SetText(WideToUTF16(cases
[i
]));
89 // Ensure custom default styles can be applied after text has been set.
91 render_text
->SetStyle(STRIKE
, true);
93 EXPECT_TRUE(render_text
->styles()[STRIKE
].EqualsValueForTesting(true));
97 TEST_F(RenderTextTest
, ApplyColorAndStyle
) {
98 scoped_ptr
<RenderText
> render_text(RenderText::CreateInstance());
99 render_text
->SetText(ASCIIToUTF16("012345678"));
101 // Apply a ranged color and style and check the resulting breaks.
102 render_text
->ApplyColor(SK_ColorRED
, ui::Range(1, 4));
103 render_text
->ApplyStyle(BOLD
, true, ui::Range(2, 5));
104 std::vector
<std::pair
<size_t, SkColor
> > expected_color
;
105 expected_color
.push_back(std::pair
<size_t, SkColor
>(0, SK_ColorBLACK
));
106 expected_color
.push_back(std::pair
<size_t, SkColor
>(1, SK_ColorRED
));
107 expected_color
.push_back(std::pair
<size_t, SkColor
>(4, SK_ColorBLACK
));
108 EXPECT_TRUE(render_text
->colors().EqualsForTesting(expected_color
));
109 std::vector
<std::pair
<size_t, bool> > expected_style
;
110 expected_style
.push_back(std::pair
<size_t, bool>(0, false));
111 expected_style
.push_back(std::pair
<size_t, bool>(2, true));
112 expected_style
.push_back(std::pair
<size_t, bool>(5, false));
113 EXPECT_TRUE(render_text
->styles()[BOLD
].EqualsForTesting(expected_style
));
115 // Ensure setting a color and style overrides the ranged colors and styles.
116 render_text
->SetColor(SK_ColorBLUE
);
117 EXPECT_TRUE(render_text
->colors().EqualsValueForTesting(SK_ColorBLUE
));
118 render_text
->SetStyle(BOLD
, false);
119 EXPECT_TRUE(render_text
->styles()[BOLD
].EqualsValueForTesting(false));
121 // Apply a color and style over the text end and check the resulting breaks.
122 // (INT_MAX should be used instead of the text length for the range end)
123 const size_t text_length
= render_text
->text().length();
124 render_text
->ApplyColor(SK_ColorRED
, ui::Range(0, text_length
));
125 render_text
->ApplyStyle(BOLD
, true, ui::Range(2, text_length
));
126 std::vector
<std::pair
<size_t, SkColor
> > expected_color_end
;
127 expected_color_end
.push_back(std::pair
<size_t, SkColor
>(0, SK_ColorRED
));
128 EXPECT_TRUE(render_text
->colors().EqualsForTesting(expected_color_end
));
129 std::vector
<std::pair
<size_t, bool> > expected_style_end
;
130 expected_style_end
.push_back(std::pair
<size_t, bool>(0, false));
131 expected_style_end
.push_back(std::pair
<size_t, bool>(2, true));
132 EXPECT_TRUE(render_text
->styles()[BOLD
].EqualsForTesting(expected_style_end
));
134 // Ensure ranged values adjust to accommodate text length changes.
135 render_text
->ApplyStyle(ITALIC
, true, ui::Range(0, 2));
136 render_text
->ApplyStyle(ITALIC
, true, ui::Range(3, 6));
137 render_text
->ApplyStyle(ITALIC
, true, ui::Range(7, text_length
));
138 std::vector
<std::pair
<size_t, bool> > expected_italic
;
139 expected_italic
.push_back(std::pair
<size_t, bool>(0, true));
140 expected_italic
.push_back(std::pair
<size_t, bool>(2, false));
141 expected_italic
.push_back(std::pair
<size_t, bool>(3, true));
142 expected_italic
.push_back(std::pair
<size_t, bool>(6, false));
143 expected_italic
.push_back(std::pair
<size_t, bool>(7, true));
144 EXPECT_TRUE(render_text
->styles()[ITALIC
].EqualsForTesting(expected_italic
));
146 // Truncating the text should trim any corresponding breaks.
147 render_text
->SetText(ASCIIToUTF16("0123456"));
148 expected_italic
.resize(4);
149 EXPECT_TRUE(render_text
->styles()[ITALIC
].EqualsForTesting(expected_italic
));
150 render_text
->SetText(ASCIIToUTF16("01234"));
151 expected_italic
.resize(3);
152 EXPECT_TRUE(render_text
->styles()[ITALIC
].EqualsForTesting(expected_italic
));
154 // Appending text should extend the terminal styles without changing breaks.
155 render_text
->SetText(ASCIIToUTF16("012345678"));
156 EXPECT_TRUE(render_text
->styles()[ITALIC
].EqualsForTesting(expected_italic
));
159 #if defined(OS_LINUX)
160 TEST_F(RenderTextTest
, PangoAttributes
) {
161 scoped_ptr
<RenderText
> render_text(RenderText::CreateInstance());
162 render_text
->SetText(ASCIIToUTF16("012345678"));
164 // Apply ranged BOLD/ITALIC styles and check the resulting Pango attributes.
165 render_text
->ApplyStyle(BOLD
, true, ui::Range(2, 4));
166 render_text
->ApplyStyle(ITALIC
, true, ui::Range(1, 3));
174 { 0, 1, false, false },
175 { 1, 2, false, true },
176 { 2, 3, true, true },
177 { 3, 4, true, false },
178 { 4, INT_MAX
, false, false },
181 int start
= 0, end
= 0;
182 RenderTextLinux
* rt_linux
= static_cast<RenderTextLinux
*>(render_text
.get());
183 rt_linux
->EnsureLayout();
184 PangoAttrList
* attributes
= pango_layout_get_attributes(rt_linux
->layout_
);
185 PangoAttrIterator
* iter
= pango_attr_list_get_iterator(attributes
);
186 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(cases
); ++i
) {
187 pango_attr_iterator_range(iter
, &start
, &end
);
188 EXPECT_EQ(cases
[i
].start
, start
);
189 EXPECT_EQ(cases
[i
].end
, end
);
190 PangoFontDescription
* font
= pango_font_description_new();
191 pango_attr_iterator_get_font(iter
, font
, NULL
, NULL
);
192 char* description_string
= pango_font_description_to_string(font
);
193 const string16 desc
= ASCIIToUTF16(description_string
);
194 const bool bold
= desc
.find(ASCIIToUTF16("Bold")) != std::string::npos
;
195 EXPECT_EQ(cases
[i
].bold
, bold
);
196 const bool italic
= desc
.find(ASCIIToUTF16("Italic")) != std::string::npos
;
197 EXPECT_EQ(cases
[i
].italic
, italic
);
198 pango_attr_iterator_next(iter
);
199 pango_font_description_free(font
);
200 g_free(description_string
);
202 EXPECT_FALSE(pango_attr_iterator_next(iter
));
203 pango_attr_iterator_destroy(iter
);
207 // TODO(asvitkine): Cursor movements tests disabled on Mac because RenderTextMac
208 // does not implement this yet. http://crbug.com/131618
209 #if !defined(OS_MACOSX)
210 void TestVisualCursorMotionInObscuredField(RenderText
* render_text
,
211 const string16
& text
,
213 ASSERT_TRUE(render_text
->obscured());
214 render_text
->SetText(text
);
215 int len
= text
.length();
216 render_text
->MoveCursor(LINE_BREAK
, CURSOR_RIGHT
, select
);
217 EXPECT_EQ(SelectionModel(ui::Range(select
? 0 : len
, len
), CURSOR_FORWARD
),
218 render_text
->selection_model());
219 render_text
->MoveCursor(LINE_BREAK
, CURSOR_LEFT
, select
);
220 EXPECT_EQ(SelectionModel(0, CURSOR_BACKWARD
), render_text
->selection_model());
221 for (int j
= 1; j
<= len
; ++j
) {
222 render_text
->MoveCursor(CHARACTER_BREAK
, CURSOR_RIGHT
, select
);
223 EXPECT_EQ(SelectionModel(ui::Range(select
? 0 : j
, j
), CURSOR_BACKWARD
),
224 render_text
->selection_model());
226 for (int j
= len
- 1; j
>= 0; --j
) {
227 render_text
->MoveCursor(CHARACTER_BREAK
, CURSOR_LEFT
, select
);
228 EXPECT_EQ(SelectionModel(ui::Range(select
? 0 : j
, j
), CURSOR_FORWARD
),
229 render_text
->selection_model());
231 render_text
->MoveCursor(WORD_BREAK
, CURSOR_RIGHT
, select
);
232 EXPECT_EQ(SelectionModel(ui::Range(select
? 0 : len
, len
), CURSOR_FORWARD
),
233 render_text
->selection_model());
234 render_text
->MoveCursor(WORD_BREAK
, CURSOR_LEFT
, select
);
235 EXPECT_EQ(SelectionModel(0, CURSOR_BACKWARD
), render_text
->selection_model());
238 TEST_F(RenderTextTest
, ObscuredText
) {
239 const string16 seuss
= ASCIIToUTF16("hop on pop");
240 const string16 no_seuss
= ASCIIToUTF16("**********");
241 scoped_ptr
<RenderText
> render_text(RenderText::CreateInstance());
243 // GetLayoutText() returns asterisks when the obscured bit is set.
244 render_text
->SetText(seuss
);
245 render_text
->SetObscured(true);
246 EXPECT_EQ(seuss
, render_text
->text());
247 EXPECT_EQ(no_seuss
, render_text
->GetLayoutText());
248 render_text
->SetObscured(false);
249 EXPECT_EQ(seuss
, render_text
->text());
250 EXPECT_EQ(seuss
, render_text
->GetLayoutText());
252 render_text
->SetObscured(true);
254 // Surrogate pairs are counted as one code point.
255 const char16 invalid_surrogates
[] = {0xDC00, 0xD800, 0};
256 render_text
->SetText(invalid_surrogates
);
257 EXPECT_EQ(ASCIIToUTF16("**"), render_text
->GetLayoutText());
258 const char16 valid_surrogates
[] = {0xD800, 0xDC00, 0};
259 render_text
->SetText(valid_surrogates
);
260 EXPECT_EQ(ASCIIToUTF16("*"), render_text
->GetLayoutText());
261 EXPECT_EQ(0U, render_text
->cursor_position());
262 render_text
->MoveCursor(CHARACTER_BREAK
, CURSOR_RIGHT
, false);
263 EXPECT_EQ(2U, render_text
->cursor_position());
265 // Test index conversion and cursor validity with a valid surrogate pair.
266 EXPECT_EQ(0U, render_text
->TextIndexToLayoutIndex(0U));
267 EXPECT_EQ(1U, render_text
->TextIndexToLayoutIndex(1U));
268 EXPECT_EQ(1U, render_text
->TextIndexToLayoutIndex(2U));
269 EXPECT_EQ(0U, render_text
->LayoutIndexToTextIndex(0U));
270 EXPECT_EQ(2U, render_text
->LayoutIndexToTextIndex(1U));
271 EXPECT_TRUE(render_text
->IsCursorablePosition(0U));
272 EXPECT_FALSE(render_text
->IsCursorablePosition(1U));
273 EXPECT_TRUE(render_text
->IsCursorablePosition(2U));
275 // FindCursorPosition() should not return positions between a surrogate pair.
276 render_text
->SetDisplayRect(Rect(0, 0, 20, 20));
277 EXPECT_EQ(render_text
->FindCursorPosition(Point(0, 0)).caret_pos(), 0U);
278 EXPECT_EQ(render_text
->FindCursorPosition(Point(20, 0)).caret_pos(), 2U);
279 for (int x
= -1; x
<= 20; ++x
) {
280 SelectionModel selection
= render_text
->FindCursorPosition(Point(x
, 0));
281 EXPECT_TRUE(selection
.caret_pos() == 0U || selection
.caret_pos() == 2U);
284 // GetGlyphBounds() should yield the entire string bounds for text index 0.
287 render_text
->GetGlyphBounds(0U, &bounds
, &height
);
288 EXPECT_EQ(render_text
->GetStringSize().width(),
289 static_cast<int>(bounds
.length()));
291 // Cursoring is independent of underlying characters when text is obscured.
292 const wchar_t* const texts
[] = {
293 kWeak
, kLtr
, kLtrRtl
, kLtrRtlLtr
, kRtl
, kRtlLtr
, kRtlLtrRtl
,
294 L
"hop on pop", // Check LTR word boundaries.
295 L
"\x05d0\x05d1 \x05d0\x05d2 \x05d1\x05d2", // Check RTL word boundaries.
297 for (size_t i
= 0; i
< arraysize(texts
); ++i
) {
298 string16 text
= WideToUTF16(texts
[i
]);
299 TestVisualCursorMotionInObscuredField(render_text
.get(), text
, false);
300 TestVisualCursorMotionInObscuredField(render_text
.get(), text
, true);
304 TEST_F(RenderTextTest
, GetTextDirection
) {
307 const base::i18n::TextDirection text_direction
;
309 // Blank strings and those with no/weak directionality default to LTR.
310 { L
"", base::i18n::LEFT_TO_RIGHT
},
311 { kWeak
, base::i18n::LEFT_TO_RIGHT
},
312 // Strings that begin with strong LTR characters.
313 { kLtr
, base::i18n::LEFT_TO_RIGHT
},
314 { kLtrRtl
, base::i18n::LEFT_TO_RIGHT
},
315 { kLtrRtlLtr
, base::i18n::LEFT_TO_RIGHT
},
316 // Strings that begin with strong RTL characters.
317 { kRtl
, base::i18n::RIGHT_TO_LEFT
},
318 { kRtlLtr
, base::i18n::RIGHT_TO_LEFT
},
319 { kRtlLtrRtl
, base::i18n::RIGHT_TO_LEFT
},
322 scoped_ptr
<RenderText
> render_text(RenderText::CreateInstance());
323 const bool was_rtl
= base::i18n::IsRTL();
325 for (size_t i
= 0; i
< 2; ++i
) {
326 // Toggle the application default text direction (to try each direction).
327 SetRTL(!base::i18n::IsRTL());
328 const base::i18n::TextDirection ui_direction
= base::i18n::IsRTL() ?
329 base::i18n::RIGHT_TO_LEFT
: base::i18n::LEFT_TO_RIGHT
;
331 // Ensure that directionality modes yield the correct text directions.
332 for (size_t j
= 0; j
< ARRAYSIZE_UNSAFE(cases
); j
++) {
333 render_text
->SetText(WideToUTF16(cases
[j
].text
));
334 render_text
->SetDirectionalityMode(DIRECTIONALITY_FROM_TEXT
);
335 EXPECT_EQ(render_text
->GetTextDirection(), cases
[j
].text_direction
);
336 render_text
->SetDirectionalityMode(DIRECTIONALITY_FROM_UI
);
337 EXPECT_EQ(render_text
->GetTextDirection(), ui_direction
);
338 render_text
->SetDirectionalityMode(DIRECTIONALITY_FORCE_LTR
);
339 EXPECT_EQ(render_text
->GetTextDirection(), base::i18n::LEFT_TO_RIGHT
);
340 render_text
->SetDirectionalityMode(DIRECTIONALITY_FORCE_RTL
);
341 EXPECT_EQ(render_text
->GetTextDirection(), base::i18n::RIGHT_TO_LEFT
);
345 EXPECT_EQ(was_rtl
, base::i18n::IsRTL());
347 // Ensure that text changes update the direction for DIRECTIONALITY_FROM_TEXT.
348 render_text
->SetDirectionalityMode(DIRECTIONALITY_FROM_TEXT
);
349 render_text
->SetText(WideToUTF16(kLtr
));
350 EXPECT_EQ(render_text
->GetTextDirection(), base::i18n::LEFT_TO_RIGHT
);
351 render_text
->SetText(WideToUTF16(kRtl
));
352 EXPECT_EQ(render_text
->GetTextDirection(), base::i18n::RIGHT_TO_LEFT
);
355 void RunMoveCursorLeftRightTest(RenderText
* render_text
,
356 const std::vector
<SelectionModel
>& expected
,
357 VisualCursorDirection direction
) {
358 for (size_t i
= 0; i
< expected
.size(); ++i
) {
359 EXPECT_EQ(expected
[i
], render_text
->selection_model());
360 render_text
->MoveCursor(CHARACTER_BREAK
, direction
, false);
362 // Check that cursoring is clamped at the line edge.
363 EXPECT_EQ(expected
.back(), render_text
->selection_model());
364 // Check that it is the line edge.
365 render_text
->MoveCursor(LINE_BREAK
, direction
, false);
366 EXPECT_EQ(expected
.back(), render_text
->selection_model());
369 TEST_F(RenderTextTest
, MoveCursorLeftRightInLtr
) {
370 scoped_ptr
<RenderText
> render_text(RenderText::CreateInstance());
373 render_text
->SetText(ASCIIToUTF16("abc"));
374 // |expected| saves the expected SelectionModel when moving cursor from left
376 std::vector
<SelectionModel
> expected
;
377 expected
.push_back(SelectionModel(0, CURSOR_BACKWARD
));
378 expected
.push_back(SelectionModel(1, CURSOR_BACKWARD
));
379 expected
.push_back(SelectionModel(2, CURSOR_BACKWARD
));
380 expected
.push_back(SelectionModel(3, CURSOR_BACKWARD
));
381 expected
.push_back(SelectionModel(3, CURSOR_FORWARD
));
382 RunMoveCursorLeftRightTest(render_text
.get(), expected
, CURSOR_RIGHT
);
385 expected
.push_back(SelectionModel(3, CURSOR_FORWARD
));
386 expected
.push_back(SelectionModel(2, CURSOR_FORWARD
));
387 expected
.push_back(SelectionModel(1, CURSOR_FORWARD
));
388 expected
.push_back(SelectionModel(0, CURSOR_FORWARD
));
389 expected
.push_back(SelectionModel(0, CURSOR_BACKWARD
));
390 RunMoveCursorLeftRightTest(render_text
.get(), expected
, CURSOR_LEFT
);
393 TEST_F(RenderTextTest
, MoveCursorLeftRightInLtrRtl
) {
394 scoped_ptr
<RenderText
> render_text(RenderText::CreateInstance());
396 render_text
->SetText(WideToUTF16(L
"abc\x05d0\x05d1\x05d2"));
397 // The last one is the expected END position.
398 std::vector
<SelectionModel
> expected
;
399 expected
.push_back(SelectionModel(0, CURSOR_BACKWARD
));
400 expected
.push_back(SelectionModel(1, CURSOR_BACKWARD
));
401 expected
.push_back(SelectionModel(2, CURSOR_BACKWARD
));
402 expected
.push_back(SelectionModel(3, CURSOR_BACKWARD
));
403 expected
.push_back(SelectionModel(5, CURSOR_FORWARD
));
404 expected
.push_back(SelectionModel(4, CURSOR_FORWARD
));
405 expected
.push_back(SelectionModel(3, CURSOR_FORWARD
));
406 expected
.push_back(SelectionModel(6, CURSOR_FORWARD
));
407 RunMoveCursorLeftRightTest(render_text
.get(), expected
, CURSOR_RIGHT
);
410 expected
.push_back(SelectionModel(6, CURSOR_FORWARD
));
411 expected
.push_back(SelectionModel(4, CURSOR_BACKWARD
));
412 expected
.push_back(SelectionModel(5, CURSOR_BACKWARD
));
413 expected
.push_back(SelectionModel(6, CURSOR_BACKWARD
));
414 expected
.push_back(SelectionModel(2, CURSOR_FORWARD
));
415 expected
.push_back(SelectionModel(1, CURSOR_FORWARD
));
416 expected
.push_back(SelectionModel(0, CURSOR_FORWARD
));
417 expected
.push_back(SelectionModel(0, CURSOR_BACKWARD
));
418 RunMoveCursorLeftRightTest(render_text
.get(), expected
, CURSOR_LEFT
);
421 TEST_F(RenderTextTest
, MoveCursorLeftRightInLtrRtlLtr
) {
422 scoped_ptr
<RenderText
> render_text(RenderText::CreateInstance());
424 render_text
->SetText(WideToUTF16(L
"a"L
"\x05d1"L
"b"));
425 std::vector
<SelectionModel
> expected
;
426 expected
.push_back(SelectionModel(0, CURSOR_BACKWARD
));
427 expected
.push_back(SelectionModel(1, CURSOR_BACKWARD
));
428 expected
.push_back(SelectionModel(1, CURSOR_FORWARD
));
429 expected
.push_back(SelectionModel(3, CURSOR_BACKWARD
));
430 expected
.push_back(SelectionModel(3, CURSOR_FORWARD
));
431 RunMoveCursorLeftRightTest(render_text
.get(), expected
, CURSOR_RIGHT
);
434 expected
.push_back(SelectionModel(3, CURSOR_FORWARD
));
435 expected
.push_back(SelectionModel(2, CURSOR_FORWARD
));
436 expected
.push_back(SelectionModel(2, CURSOR_BACKWARD
));
437 expected
.push_back(SelectionModel(0, CURSOR_FORWARD
));
438 expected
.push_back(SelectionModel(0, CURSOR_BACKWARD
));
439 RunMoveCursorLeftRightTest(render_text
.get(), expected
, CURSOR_LEFT
);
442 TEST_F(RenderTextTest
, MoveCursorLeftRightInRtl
) {
443 scoped_ptr
<RenderText
> render_text(RenderText::CreateInstance());
445 render_text
->SetText(WideToUTF16(L
"\x05d0\x05d1\x05d2"));
446 render_text
->MoveCursor(LINE_BREAK
, CURSOR_RIGHT
, false);
447 std::vector
<SelectionModel
> expected
;
449 expected
.push_back(SelectionModel(0, CURSOR_BACKWARD
));
450 expected
.push_back(SelectionModel(1, CURSOR_BACKWARD
));
451 expected
.push_back(SelectionModel(2, CURSOR_BACKWARD
));
452 expected
.push_back(SelectionModel(3, CURSOR_BACKWARD
));
453 expected
.push_back(SelectionModel(3, CURSOR_FORWARD
));
454 RunMoveCursorLeftRightTest(render_text
.get(), expected
, CURSOR_LEFT
);
458 expected
.push_back(SelectionModel(3, CURSOR_FORWARD
));
459 expected
.push_back(SelectionModel(2, CURSOR_FORWARD
));
460 expected
.push_back(SelectionModel(1, CURSOR_FORWARD
));
461 expected
.push_back(SelectionModel(0, CURSOR_FORWARD
));
462 expected
.push_back(SelectionModel(0, CURSOR_BACKWARD
));
463 RunMoveCursorLeftRightTest(render_text
.get(), expected
, CURSOR_RIGHT
);
466 TEST_F(RenderTextTest
, MoveCursorLeftRightInRtlLtr
) {
467 scoped_ptr
<RenderText
> render_text(RenderText::CreateInstance());
469 render_text
->SetText(WideToUTF16(L
"\x05d0\x05d1\x05d2"L
"abc"));
470 render_text
->MoveCursor(LINE_BREAK
, CURSOR_RIGHT
, false);
471 std::vector
<SelectionModel
> expected
;
472 expected
.push_back(SelectionModel(0, CURSOR_BACKWARD
));
473 expected
.push_back(SelectionModel(1, CURSOR_BACKWARD
));
474 expected
.push_back(SelectionModel(2, CURSOR_BACKWARD
));
475 expected
.push_back(SelectionModel(3, CURSOR_BACKWARD
));
476 expected
.push_back(SelectionModel(5, CURSOR_FORWARD
));
477 expected
.push_back(SelectionModel(4, CURSOR_FORWARD
));
478 expected
.push_back(SelectionModel(3, CURSOR_FORWARD
));
479 expected
.push_back(SelectionModel(6, CURSOR_FORWARD
));
480 RunMoveCursorLeftRightTest(render_text
.get(), expected
, CURSOR_LEFT
);
483 expected
.push_back(SelectionModel(6, CURSOR_FORWARD
));
484 expected
.push_back(SelectionModel(4, CURSOR_BACKWARD
));
485 expected
.push_back(SelectionModel(5, CURSOR_BACKWARD
));
486 expected
.push_back(SelectionModel(6, CURSOR_BACKWARD
));
487 expected
.push_back(SelectionModel(2, CURSOR_FORWARD
));
488 expected
.push_back(SelectionModel(1, CURSOR_FORWARD
));
489 expected
.push_back(SelectionModel(0, CURSOR_FORWARD
));
490 expected
.push_back(SelectionModel(0, CURSOR_BACKWARD
));
491 RunMoveCursorLeftRightTest(render_text
.get(), expected
, CURSOR_RIGHT
);
494 TEST_F(RenderTextTest
, MoveCursorLeftRightInRtlLtrRtl
) {
495 scoped_ptr
<RenderText
> render_text(RenderText::CreateInstance());
497 render_text
->SetText(WideToUTF16(L
"\x05d0"L
"a"L
"\x05d1"));
498 render_text
->MoveCursor(LINE_BREAK
, CURSOR_RIGHT
, false);
499 std::vector
<SelectionModel
> expected
;
500 expected
.push_back(SelectionModel(0, CURSOR_BACKWARD
));
501 expected
.push_back(SelectionModel(1, CURSOR_BACKWARD
));
502 expected
.push_back(SelectionModel(1, CURSOR_FORWARD
));
503 expected
.push_back(SelectionModel(3, CURSOR_BACKWARD
));
504 expected
.push_back(SelectionModel(3, CURSOR_FORWARD
));
505 RunMoveCursorLeftRightTest(render_text
.get(), expected
, CURSOR_LEFT
);
508 expected
.push_back(SelectionModel(3, CURSOR_FORWARD
));
509 expected
.push_back(SelectionModel(2, CURSOR_FORWARD
));
510 expected
.push_back(SelectionModel(2, CURSOR_BACKWARD
));
511 expected
.push_back(SelectionModel(0, CURSOR_FORWARD
));
512 expected
.push_back(SelectionModel(0, CURSOR_BACKWARD
));
513 RunMoveCursorLeftRightTest(render_text
.get(), expected
, CURSOR_RIGHT
);
516 // TODO(xji): temporarily disable in platform Win since the complex script
517 // characters turned into empty square due to font regression. So, not able
518 // to test 2 characters belong to the same grapheme.
519 #if defined(OS_LINUX)
520 TEST_F(RenderTextTest
, MoveCursorLeftRight_ComplexScript
) {
521 scoped_ptr
<RenderText
> render_text(RenderText::CreateInstance());
523 render_text
->SetText(WideToUTF16(L
"\x0915\x093f\x0915\x094d\x0915"));
524 EXPECT_EQ(0U, render_text
->cursor_position());
525 render_text
->MoveCursor(CHARACTER_BREAK
, CURSOR_RIGHT
, false);
526 EXPECT_EQ(2U, render_text
->cursor_position());
527 render_text
->MoveCursor(CHARACTER_BREAK
, CURSOR_RIGHT
, false);
528 EXPECT_EQ(4U, render_text
->cursor_position());
529 render_text
->MoveCursor(CHARACTER_BREAK
, CURSOR_RIGHT
, false);
530 EXPECT_EQ(5U, render_text
->cursor_position());
531 render_text
->MoveCursor(CHARACTER_BREAK
, CURSOR_RIGHT
, false);
532 EXPECT_EQ(5U, render_text
->cursor_position());
534 render_text
->MoveCursor(CHARACTER_BREAK
, CURSOR_LEFT
, false);
535 EXPECT_EQ(4U, render_text
->cursor_position());
536 render_text
->MoveCursor(CHARACTER_BREAK
, CURSOR_LEFT
, false);
537 EXPECT_EQ(2U, render_text
->cursor_position());
538 render_text
->MoveCursor(CHARACTER_BREAK
, CURSOR_LEFT
, false);
539 EXPECT_EQ(0U, render_text
->cursor_position());
540 render_text
->MoveCursor(CHARACTER_BREAK
, CURSOR_LEFT
, false);
541 EXPECT_EQ(0U, render_text
->cursor_position());
545 TEST_F(RenderTextTest
, GraphemePositions
) {
546 // LTR 2-character grapheme, LTR abc, LTR 2-character grapheme.
547 const string16 kText1
= WideToUTF16(L
"\x0915\x093f"L
"abc"L
"\x0915\x093f");
549 // LTR ab, LTR 2-character grapheme, LTR cd.
550 const string16 kText2
= WideToUTF16(L
"ab"L
"\x0915\x093f"L
"cd");
552 // The below is 'MUSICAL SYMBOL G CLEF', which is represented in UTF-16 as
553 // two characters forming the surrogate pair 0x0001D11E.
554 const std::string kSurrogate
= "\xF0\x9D\x84\x9E";
556 // LTR ab, UTF16 surrogate pair, LTR cd.
557 const string16 kText3
= UTF8ToUTF16("ab" + kSurrogate
+ "cd");
562 size_t expected_previous
;
563 size_t expected_next
;
565 { string16(), 0, 0, 0 },
566 { string16(), 1, 0, 0 },
567 { string16(), 50, 0, 0 },
577 { kText1
, 50, 7, 7 },
586 { kText2
, 50, 6, 6 },
595 { kText3
, 50, 6, 6 },
598 // TODO(asvitkine): Disable tests that fail on XP bots due to lack of complete
599 // font support for some scripts - http://crbug.com/106450
601 if (base::win::GetVersion() < base::win::VERSION_VISTA
)
605 scoped_ptr
<RenderText
> render_text(RenderText::CreateInstance());
606 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(cases
); i
++) {
607 render_text
->SetText(cases
[i
].text
);
609 size_t next
= render_text
->IndexOfAdjacentGrapheme(cases
[i
].index
,
611 EXPECT_EQ(cases
[i
].expected_next
, next
);
612 EXPECT_TRUE(render_text
->IsCursorablePosition(next
));
614 size_t previous
= render_text
->IndexOfAdjacentGrapheme(cases
[i
].index
,
616 EXPECT_EQ(cases
[i
].expected_previous
, previous
);
617 EXPECT_TRUE(render_text
->IsCursorablePosition(previous
));
621 TEST_F(RenderTextTest
, EdgeSelectionModels
) {
622 // Simple Latin text.
623 const string16 kLatin
= WideToUTF16(L
"abc");
624 // LTR 2-character grapheme.
625 const string16 kLTRGrapheme
= WideToUTF16(L
"\x0915\x093f");
626 // LTR 2-character grapheme, LTR a, LTR 2-character grapheme.
627 const string16 kHindiLatin
= WideToUTF16(L
"\x0915\x093f"L
"a"L
"\x0915\x093f");
628 // RTL 2-character grapheme.
629 const string16 kRTLGrapheme
= WideToUTF16(L
"\x05e0\x05b8");
630 // RTL 2-character grapheme, LTR a, RTL 2-character grapheme.
631 const string16 kHebrewLatin
= WideToUTF16(L
"\x05e0\x05b8"L
"a"L
"\x05e0\x05b8");
635 base::i18n::TextDirection expected_text_direction
;
637 { string16(), base::i18n::LEFT_TO_RIGHT
},
638 { kLatin
, base::i18n::LEFT_TO_RIGHT
},
639 { kLTRGrapheme
, base::i18n::LEFT_TO_RIGHT
},
640 { kHindiLatin
, base::i18n::LEFT_TO_RIGHT
},
641 { kRTLGrapheme
, base::i18n::RIGHT_TO_LEFT
},
642 { kHebrewLatin
, base::i18n::RIGHT_TO_LEFT
},
645 // TODO(asvitkine): Disable tests that fail on XP bots due to lack of complete
646 // font support for some scripts - http://crbug.com/106450
648 if (base::win::GetVersion() < base::win::VERSION_VISTA
)
652 scoped_ptr
<RenderText
> render_text(RenderText::CreateInstance());
653 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(cases
); i
++) {
654 render_text
->SetText(cases
[i
].text
);
655 bool ltr
= (cases
[i
].expected_text_direction
== base::i18n::LEFT_TO_RIGHT
);
657 SelectionModel start_edge
=
658 render_text
->EdgeSelectionModel(ltr
? CURSOR_LEFT
: CURSOR_RIGHT
);
659 EXPECT_EQ(start_edge
, SelectionModel(0, CURSOR_BACKWARD
));
661 SelectionModel end_edge
=
662 render_text
->EdgeSelectionModel(ltr
? CURSOR_RIGHT
: CURSOR_LEFT
);
663 EXPECT_EQ(end_edge
, SelectionModel(cases
[i
].text
.length(), CURSOR_FORWARD
));
667 TEST_F(RenderTextTest
, SelectAll
) {
668 const wchar_t* const cases
[] =
669 { kWeak
, kLtr
, kLtrRtl
, kLtrRtlLtr
, kRtl
, kRtlLtr
, kRtlLtrRtl
};
671 // Ensure that SelectAll respects the |reversed| argument regardless of
672 // application locale and text content directionality.
673 scoped_ptr
<RenderText
> render_text(RenderText::CreateInstance());
674 const SelectionModel
expected_reversed(ui::Range(3, 0), CURSOR_FORWARD
);
675 const SelectionModel
expected_forwards(ui::Range(0, 3), CURSOR_BACKWARD
);
676 const bool was_rtl
= base::i18n::IsRTL();
678 for (size_t i
= 0; i
< 2; ++i
) {
679 SetRTL(!base::i18n::IsRTL());
680 // Test that an empty string produces an empty selection model.
681 render_text
->SetText(string16());
682 EXPECT_EQ(render_text
->selection_model(), SelectionModel());
684 // Test the weak, LTR, RTL, and Bidi string cases.
685 for (size_t j
= 0; j
< ARRAYSIZE_UNSAFE(cases
); j
++) {
686 render_text
->SetText(WideToUTF16(cases
[j
]));
687 render_text
->SelectAll(false);
688 EXPECT_EQ(render_text
->selection_model(), expected_forwards
);
689 render_text
->SelectAll(true);
690 EXPECT_EQ(render_text
->selection_model(), expected_reversed
);
694 EXPECT_EQ(was_rtl
, base::i18n::IsRTL());
697 TEST_F(RenderTextTest
, MoveCursorLeftRightWithSelection
) {
698 scoped_ptr
<RenderText
> render_text(RenderText::CreateInstance());
699 render_text
->SetText(WideToUTF16(L
"abc\x05d0\x05d1\x05d2"));
700 // Left arrow on select ranging (6, 4).
701 render_text
->MoveCursor(LINE_BREAK
, CURSOR_RIGHT
, false);
702 EXPECT_EQ(ui::Range(6), render_text
->selection());
703 render_text
->MoveCursor(CHARACTER_BREAK
, CURSOR_LEFT
, false);
704 EXPECT_EQ(ui::Range(4), render_text
->selection());
705 render_text
->MoveCursor(CHARACTER_BREAK
, CURSOR_LEFT
, false);
706 EXPECT_EQ(ui::Range(5), render_text
->selection());
707 render_text
->MoveCursor(CHARACTER_BREAK
, CURSOR_LEFT
, false);
708 EXPECT_EQ(ui::Range(6), render_text
->selection());
709 render_text
->MoveCursor(CHARACTER_BREAK
, CURSOR_RIGHT
, true);
710 EXPECT_EQ(ui::Range(6, 5), render_text
->selection());
711 render_text
->MoveCursor(CHARACTER_BREAK
, CURSOR_RIGHT
, true);
712 EXPECT_EQ(ui::Range(6, 4), render_text
->selection());
713 render_text
->MoveCursor(CHARACTER_BREAK
, CURSOR_LEFT
, false);
714 EXPECT_EQ(ui::Range(6), render_text
->selection());
716 // Right arrow on select ranging (4, 6).
717 render_text
->MoveCursor(LINE_BREAK
, CURSOR_LEFT
, false);
718 EXPECT_EQ(ui::Range(0), render_text
->selection());
719 render_text
->MoveCursor(CHARACTER_BREAK
, CURSOR_RIGHT
, false);
720 EXPECT_EQ(ui::Range(1), render_text
->selection());
721 render_text
->MoveCursor(CHARACTER_BREAK
, CURSOR_RIGHT
, false);
722 EXPECT_EQ(ui::Range(2), render_text
->selection());
723 render_text
->MoveCursor(CHARACTER_BREAK
, CURSOR_RIGHT
, false);
724 EXPECT_EQ(ui::Range(3), render_text
->selection());
725 render_text
->MoveCursor(CHARACTER_BREAK
, CURSOR_RIGHT
, false);
726 EXPECT_EQ(ui::Range(5), render_text
->selection());
727 render_text
->MoveCursor(CHARACTER_BREAK
, CURSOR_RIGHT
, false);
728 EXPECT_EQ(ui::Range(4), render_text
->selection());
729 render_text
->MoveCursor(CHARACTER_BREAK
, CURSOR_LEFT
, true);
730 EXPECT_EQ(ui::Range(4, 5), render_text
->selection());
731 render_text
->MoveCursor(CHARACTER_BREAK
, CURSOR_LEFT
, true);
732 EXPECT_EQ(ui::Range(4, 6), render_text
->selection());
733 render_text
->MoveCursor(CHARACTER_BREAK
, CURSOR_RIGHT
, false);
734 EXPECT_EQ(ui::Range(4), render_text
->selection());
736 #endif // !defined(OS_MACOSX)
738 // TODO(xji): Make these work on Windows.
739 #if defined(OS_LINUX)
740 void MoveLeftRightByWordVerifier(RenderText
* render_text
,
741 const wchar_t* str
) {
742 render_text
->SetText(WideToUTF16(str
));
744 // Test moving by word from left ro right.
745 render_text
->MoveCursor(LINE_BREAK
, CURSOR_LEFT
, false);
746 bool first_word
= true;
748 // First, test moving by word from a word break position, such as from
749 // "|abc def" to "abc| def".
750 SelectionModel start
= render_text
->selection_model();
751 render_text
->MoveCursor(WORD_BREAK
, CURSOR_RIGHT
, false);
752 SelectionModel end
= render_text
->selection_model();
753 if (end
== start
) // reach the end.
756 // For testing simplicity, each word is a 3-character word.
757 int num_of_character_moves
= first_word
? 3 : 4;
759 render_text
->MoveCursorTo(start
);
760 for (int j
= 0; j
< num_of_character_moves
; ++j
)
761 render_text
->MoveCursor(CHARACTER_BREAK
, CURSOR_RIGHT
, false);
762 EXPECT_EQ(end
, render_text
->selection_model());
764 // Then, test moving by word from positions inside the word, such as from
765 // "a|bc def" to "abc| def", and from "ab|c def" to "abc| def".
766 for (int j
= 1; j
< num_of_character_moves
; ++j
) {
767 render_text
->MoveCursorTo(start
);
768 for (int k
= 0; k
< j
; ++k
)
769 render_text
->MoveCursor(CHARACTER_BREAK
, CURSOR_RIGHT
, false);
770 render_text
->MoveCursor(WORD_BREAK
, CURSOR_RIGHT
, false);
771 EXPECT_EQ(end
, render_text
->selection_model());
775 // Test moving by word from right to left.
776 render_text
->MoveCursor(LINE_BREAK
, CURSOR_RIGHT
, false);
779 SelectionModel start
= render_text
->selection_model();
780 render_text
->MoveCursor(WORD_BREAK
, CURSOR_LEFT
, false);
781 SelectionModel end
= render_text
->selection_model();
782 if (end
== start
) // reach the end.
785 int num_of_character_moves
= first_word
? 3 : 4;
787 render_text
->MoveCursorTo(start
);
788 for (int j
= 0; j
< num_of_character_moves
; ++j
)
789 render_text
->MoveCursor(CHARACTER_BREAK
, CURSOR_LEFT
, false);
790 EXPECT_EQ(end
, render_text
->selection_model());
792 for (int j
= 1; j
< num_of_character_moves
; ++j
) {
793 render_text
->MoveCursorTo(start
);
794 for (int k
= 0; k
< j
; ++k
)
795 render_text
->MoveCursor(CHARACTER_BREAK
, CURSOR_LEFT
, false);
796 render_text
->MoveCursor(WORD_BREAK
, CURSOR_LEFT
, false);
797 EXPECT_EQ(end
, render_text
->selection_model());
802 TEST_F(RenderTextTest
, MoveLeftRightByWordInBidiText
) {
803 scoped_ptr
<RenderText
> render_text(RenderText::CreateInstance());
805 // For testing simplicity, each word is a 3-character word.
806 std::vector
<const wchar_t*> test
;
807 test
.push_back(L
"abc");
808 test
.push_back(L
"abc def");
809 test
.push_back(L
"\x05E1\x05E2\x05E3");
810 test
.push_back(L
"\x05E1\x05E2\x05E3 \x05E4\x05E5\x05E6");
811 test
.push_back(L
"abc \x05E1\x05E2\x05E3");
812 test
.push_back(L
"abc def \x05E1\x05E2\x05E3 \x05E4\x05E5\x05E6");
813 test
.push_back(L
"abc def hij \x05E1\x05E2\x05E3 \x05E4\x05E5\x05E6"
814 L
" \x05E7\x05E8\x05E9");
816 test
.push_back(L
"abc \x05E1\x05E2\x05E3 hij");
817 test
.push_back(L
"abc def \x05E1\x05E2\x05E3 \x05E4\x05E5\x05E6 hij opq");
818 test
.push_back(L
"abc def hij \x05E1\x05E2\x05E3 \x05E4\x05E5\x05E6"
819 L
" \x05E7\x05E8\x05E9"L
" opq rst uvw");
821 test
.push_back(L
"\x05E1\x05E2\x05E3 abc");
822 test
.push_back(L
"\x05E1\x05E2\x05E3 \x05E4\x05E5\x05E6 abc def");
823 test
.push_back(L
"\x05E1\x05E2\x05E3 \x05E4\x05E5\x05E6 \x05E7\x05E8\x05E9"
826 test
.push_back(L
"\x05D1\x05D2\x05D3 abc \x05E1\x05E2\x05E3");
827 test
.push_back(L
"\x05D1\x05D2\x05D3 \x05D4\x05D5\x05D6 abc def"
828 L
" \x05E1\x05E2\x05E3 \x05E4\x05E5\x05E6");
829 test
.push_back(L
"\x05D1\x05D2\x05D3 \x05D4\x05D5\x05D6 \x05D7\x05D8\x05D9"
830 L
" abc def hij \x05E1\x05E2\x05E3 \x05E4\x05E5\x05E6"
831 L
" \x05E7\x05E8\x05E9");
833 for (size_t i
= 0; i
< test
.size(); ++i
)
834 MoveLeftRightByWordVerifier(render_text
.get(), test
[i
]);
837 TEST_F(RenderTextTest
, MoveLeftRightByWordInBidiText_TestEndOfText
) {
838 scoped_ptr
<RenderText
> render_text(RenderText::CreateInstance());
840 render_text
->SetText(WideToUTF16(L
"ab\x05E1"));
841 // Moving the cursor by word from "abC|" to the left should return "|abC".
842 // But since end of text is always treated as a word break, it returns
844 // TODO(xji): Need to make it work as expected.
845 render_text
->MoveCursor(LINE_BREAK
, CURSOR_RIGHT
, false);
846 render_text
->MoveCursor(WORD_BREAK
, CURSOR_LEFT
, false);
847 // EXPECT_EQ(SelectionModel(), render_text->selection_model());
849 // Moving the cursor by word from "|abC" to the right returns "abC|".
850 render_text
->MoveCursor(LINE_BREAK
, CURSOR_LEFT
, false);
851 render_text
->MoveCursor(WORD_BREAK
, CURSOR_RIGHT
, false);
852 EXPECT_EQ(SelectionModel(3, CURSOR_FORWARD
), render_text
->selection_model());
854 render_text
->SetText(WideToUTF16(L
"\x05E1\x05E2"L
"a"));
855 // For logical text "BCa", moving the cursor by word from "aCB|" to the left
857 render_text
->MoveCursor(LINE_BREAK
, CURSOR_RIGHT
, false);
858 render_text
->MoveCursor(WORD_BREAK
, CURSOR_LEFT
, false);
859 EXPECT_EQ(SelectionModel(3, CURSOR_FORWARD
), render_text
->selection_model());
861 // Moving the cursor by word from "|aCB" to the right should return "aCB|".
862 // But since end of text is always treated as a word break, it returns
864 // TODO(xji): Need to make it work as expected.
865 render_text
->MoveCursor(LINE_BREAK
, CURSOR_LEFT
, false);
866 render_text
->MoveCursor(WORD_BREAK
, CURSOR_RIGHT
, false);
867 // EXPECT_EQ(SelectionModel(), render_text->selection_model());
870 TEST_F(RenderTextTest
, MoveLeftRightByWordInTextWithMultiSpaces
) {
871 scoped_ptr
<RenderText
> render_text(RenderText::CreateInstance());
872 render_text
->SetText(WideToUTF16(L
"abc def"));
873 render_text
->MoveCursorTo(SelectionModel(5, CURSOR_FORWARD
));
874 render_text
->MoveCursor(WORD_BREAK
, CURSOR_RIGHT
, false);
875 EXPECT_EQ(11U, render_text
->cursor_position());
877 render_text
->MoveCursorTo(SelectionModel(5, CURSOR_FORWARD
));
878 render_text
->MoveCursor(WORD_BREAK
, CURSOR_LEFT
, false);
879 EXPECT_EQ(0U, render_text
->cursor_position());
882 TEST_F(RenderTextTest
, MoveLeftRightByWordInChineseText
) {
883 scoped_ptr
<RenderText
> render_text(RenderText::CreateInstance());
884 render_text
->SetText(WideToUTF16(L
"\x6211\x4EEC\x53BB\x516C\x56ED\x73A9"));
885 render_text
->MoveCursor(LINE_BREAK
, CURSOR_LEFT
, false);
886 EXPECT_EQ(0U, render_text
->cursor_position());
887 render_text
->MoveCursor(WORD_BREAK
, CURSOR_RIGHT
, false);
888 EXPECT_EQ(2U, render_text
->cursor_position());
889 render_text
->MoveCursor(WORD_BREAK
, CURSOR_RIGHT
, false);
890 EXPECT_EQ(3U, render_text
->cursor_position());
891 render_text
->MoveCursor(WORD_BREAK
, CURSOR_RIGHT
, false);
892 EXPECT_EQ(5U, render_text
->cursor_position());
893 render_text
->MoveCursor(WORD_BREAK
, CURSOR_RIGHT
, false);
894 EXPECT_EQ(6U, render_text
->cursor_position());
895 render_text
->MoveCursor(WORD_BREAK
, CURSOR_RIGHT
, false);
896 EXPECT_EQ(6U, render_text
->cursor_position());
900 TEST_F(RenderTextTest
, StringSizeSanity
) {
901 scoped_ptr
<RenderText
> render_text(RenderText::CreateInstance());
902 render_text
->SetText(UTF8ToUTF16("Hello World"));
903 const Size string_size
= render_text
->GetStringSize();
904 EXPECT_GT(string_size
.width(), 0);
905 EXPECT_GT(string_size
.height(), 0);
908 // TODO(asvitkine): This test fails because PlatformFontMac uses point font
909 // sizes instead of pixel sizes like other implementations.
910 #if !defined(OS_MACOSX)
911 TEST_F(RenderTextTest
, StringSizeEmptyString
) {
913 scoped_ptr
<RenderText
> render_text(RenderText::CreateInstance());
914 render_text
->SetFont(font
);
916 render_text
->SetText(string16());
917 EXPECT_EQ(font
.GetHeight(), render_text
->GetStringSize().height());
918 EXPECT_EQ(0, render_text
->GetStringSize().width());
920 render_text
->SetText(UTF8ToUTF16(" "));
921 EXPECT_EQ(font
.GetHeight(), render_text
->GetStringSize().height());
923 #endif // !defined(OS_MACOSX)
925 TEST_F(RenderTextTest
, SetFont
) {
926 scoped_ptr
<RenderText
> render_text(RenderText::CreateInstance());
927 render_text
->SetFont(Font("Arial", 12));
928 EXPECT_EQ("Arial", render_text
->GetFont().GetFontName());
929 EXPECT_EQ(12, render_text
->GetFont().GetFontSize());
932 TEST_F(RenderTextTest
, StringSizeBoldWidth
) {
933 scoped_ptr
<RenderText
> render_text(RenderText::CreateInstance());
934 render_text
->SetText(UTF8ToUTF16("Hello World"));
936 const int plain_width
= render_text
->GetStringSize().width();
937 EXPECT_GT(plain_width
, 0);
939 // Apply a bold style and check that the new width is greater.
940 render_text
->SetStyle(gfx::BOLD
, true);
941 const int bold_width
= render_text
->GetStringSize().width();
942 EXPECT_GT(bold_width
, plain_width
);
944 // Now, apply a plain style over the first word only.
945 render_text
->ApplyStyle(gfx::BOLD
, false, ui::Range(0, 5));
946 const int plain_bold_width
= render_text
->GetStringSize().width();
947 EXPECT_GT(plain_bold_width
, plain_width
);
948 EXPECT_LT(plain_bold_width
, bold_width
);
951 TEST_F(RenderTextTest
, StringSizeHeight
) {
953 WideToUTF16(L
"Hello World!"), // English
954 WideToUTF16(L
"\x6328\x62f6"), // Japanese
955 WideToUTF16(L
"\x0915\x093f"), // Hindi
956 WideToUTF16(L
"\x05e0\x05b8"), // Hebrew
960 Font larger_font
= default_font
.DeriveFont(24, default_font
.GetStyle());
961 EXPECT_GT(larger_font
.GetHeight(), default_font
.GetHeight());
963 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(cases
); i
++) {
964 scoped_ptr
<RenderText
> render_text(RenderText::CreateInstance());
965 render_text
->SetFont(default_font
);
966 render_text
->SetText(cases
[i
]);
968 const int height1
= render_text
->GetStringSize().height();
969 EXPECT_GT(height1
, 0);
971 // Check that setting the larger font increases the height.
972 render_text
->SetFont(larger_font
);
973 const int height2
= render_text
->GetStringSize().height();
974 EXPECT_GT(height2
, height1
);
978 TEST_F(RenderTextTest
, GetBaselineSanity
) {
979 scoped_ptr
<RenderText
> render_text(RenderText::CreateInstance());
980 render_text
->SetText(UTF8ToUTF16("Hello World"));
981 const int baseline
= render_text
->GetBaseline();
982 EXPECT_GT(baseline
, 0);
985 TEST_F(RenderTextTest
, CursorBoundsInReplacementMode
) {
986 scoped_ptr
<RenderText
> render_text(RenderText::CreateInstance());
987 render_text
->SetText(ASCIIToUTF16("abcdefg"));
988 render_text
->SetDisplayRect(Rect(100, 17));
989 SelectionModel
sel_b(1, CURSOR_FORWARD
);
990 SelectionModel
sel_c(2, CURSOR_FORWARD
);
991 Rect cursor_around_b
= render_text
->GetCursorBounds(sel_b
, false);
992 Rect cursor_before_b
= render_text
->GetCursorBounds(sel_b
, true);
993 Rect cursor_before_c
= render_text
->GetCursorBounds(sel_c
, true);
994 EXPECT_EQ(cursor_around_b
.x(), cursor_before_b
.x());
995 EXPECT_EQ(cursor_around_b
.right(), cursor_before_c
.x());
998 // http://crbug.com/161902
999 #if defined(OS_LINUX)
1000 #define MAYBE_OriginForDrawing DISABLED_OriginForDrawing
1002 #define MAYBE_OriginForDrawing OriginForDrawing
1004 TEST_F(RenderTextTest
, MAYBE_OriginForDrawing
) {
1005 scoped_ptr
<RenderText
> render_text(RenderText::CreateInstance());
1006 render_text
->SetText(ASCIIToUTF16("abcdefg"));
1007 render_text
->SetFontList(FontList("Arial, 13px"));
1009 // Set display area's height equals to font height.
1010 const int font_height
= render_text
->GetStringSize().height();
1011 Rect
display_rect(0, 0, 100, font_height
);
1012 render_text
->SetDisplayRect(display_rect
);
1014 Vector2d offset
= render_text
->GetOffsetForDrawing();
1015 EXPECT_TRUE(offset
.IsZero());
1017 // Set display area's height greater than font height.
1018 const int kEnlargement
= 2;
1019 display_rect
= Rect(0, 0, 100, font_height
+ kEnlargement
);
1020 render_text
->SetDisplayRect(display_rect
);
1022 // Text should be vertically centered.
1023 offset
= render_text
->GetOffsetForDrawing();
1024 EXPECT_EQ(offset
.x(), 0);
1025 EXPECT_EQ(offset
.y(), kEnlargement
/ 2);
1028 TEST_F(RenderTextTest
, SameFontForParentheses
) {
1030 const char16 left_char
;
1031 const char16 right_char
;
1032 } punctuation_pairs
[] = {
1041 { WideToUTF16(L
"Hello World(a)") },
1042 // English(English)English
1043 { WideToUTF16(L
"Hello World(a)Hello World") },
1045 // Japanese(English)
1046 { WideToUTF16(L
"\x6328\x62f6(a)") },
1047 // Japanese(English)Japanese
1048 { WideToUTF16(L
"\x6328\x62f6(a)\x6328\x62f6") },
1049 // English(Japanese)English
1050 { WideToUTF16(L
"Hello World(\x6328\x62f6)Hello World") },
1053 { WideToUTF16(L
"\x0915\x093f(a)") },
1054 // Hindi(English)Hindi
1055 { WideToUTF16(L
"\x0915\x093f(a)\x0915\x093f") },
1056 // English(Hindi)English
1057 { WideToUTF16(L
"Hello World(\x0915\x093f)Hello World") },
1060 { WideToUTF16(L
"\x05e0\x05b8(a)") },
1061 // Hebrew(English)Hebrew
1062 { WideToUTF16(L
"\x05e0\x05b8(a)\x05e0\x05b8") },
1063 // English(Hebrew)English
1064 { WideToUTF16(L
"Hello World(\x05e0\x05b8)Hello World") },
1067 scoped_ptr
<RenderText
> render_text(RenderText::CreateInstance());
1068 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(cases
); ++i
) {
1069 string16 text
= cases
[i
].text
;
1070 const size_t start_paren_char_index
= text
.find('(');
1071 ASSERT_NE(string16::npos
, start_paren_char_index
);
1072 const size_t end_paren_char_index
= text
.find(')');
1073 ASSERT_NE(string16::npos
, end_paren_char_index
);
1075 for (size_t j
= 0; j
< ARRAYSIZE_UNSAFE(punctuation_pairs
); ++j
) {
1076 text
[start_paren_char_index
] = punctuation_pairs
[j
].left_char
;
1077 text
[end_paren_char_index
] = punctuation_pairs
[j
].right_char
;
1078 render_text
->SetText(text
);
1080 const std::vector
<RenderText::FontSpan
> spans
=
1081 render_text
->GetFontSpansForTesting();
1083 int start_paren_span_index
= -1;
1084 int end_paren_span_index
= -1;
1085 for (size_t k
= 0; k
< spans
.size(); ++k
) {
1086 if (IndexInRange(spans
[k
].second
, start_paren_char_index
))
1087 start_paren_span_index
= k
;
1088 if (IndexInRange(spans
[k
].second
, end_paren_char_index
))
1089 end_paren_span_index
= k
;
1091 ASSERT_NE(-1, start_paren_span_index
);
1092 ASSERT_NE(-1, end_paren_span_index
);
1094 const Font
& start_font
= spans
[start_paren_span_index
].first
;
1095 const Font
& end_font
= spans
[end_paren_span_index
].first
;
1096 EXPECT_EQ(start_font
.GetFontName(), end_font
.GetFontName());
1097 EXPECT_EQ(start_font
.GetFontSize(), end_font
.GetFontSize());
1098 EXPECT_EQ(start_font
.GetStyle(), end_font
.GetStyle());
1103 // Make sure the caret width is always >=1 so that the correct
1104 // caret is drawn at high DPI. crbug.com/164100.
1105 TEST_F(RenderTextTest
, CaretWidth
) {
1106 scoped_ptr
<RenderText
> render_text(RenderText::CreateInstance());
1107 render_text
->SetText(ASCIIToUTF16("abcdefg"));
1108 EXPECT_GE(render_text
->GetUpdatedCursorBounds().width(), 1);
1111 // TODO(asvitkine): Cursor movements tests disabled on Mac because RenderTextMac
1112 // does not implement this yet. http://crbug.com/131618
1113 #if !defined(OS_MACOSX)
1114 TEST_F(RenderTextTest
, DisplayRectShowsCursorLTR
) {
1115 ASSERT_FALSE(base::i18n::IsRTL());
1116 ASSERT_FALSE(base::i18n::ICUIsRTL());
1118 scoped_ptr
<RenderText
> render_text(RenderText::CreateInstance());
1119 render_text
->SetText(WideToUTF16(L
"abcdefghijklmnopqrstuvwxzyabcdefg"));
1120 render_text
->MoveCursorTo(SelectionModel(render_text
->text().length(),
1122 int width
= render_text
->GetStringSize().width();
1123 ASSERT_GT(width
, 10);
1125 // Ensure that the cursor is placed at the width of its preceding text.
1126 render_text
->SetDisplayRect(Rect(width
+ 10, 1));
1127 EXPECT_EQ(width
, render_text
->GetUpdatedCursorBounds().x());
1129 // Ensure that shrinking the display rectangle keeps the cursor in view.
1130 render_text
->SetDisplayRect(Rect(width
- 10, 1));
1131 EXPECT_EQ(render_text
->display_rect().width() - 1,
1132 render_text
->GetUpdatedCursorBounds().right());
1134 // TODO(msw): Investigate why this test passes with
1135 // |GetUpdateCursorBounds().x()|, while the above have to use
1137 // Ensure that the text will pan to fill its expanding display rectangle.
1138 render_text
->SetDisplayRect(Rect(width
- 5, 1));
1139 EXPECT_EQ(render_text
->display_rect().width() - 1,
1140 render_text
->GetUpdatedCursorBounds().x());
1142 // Ensure that a sufficiently large display rectangle shows all the text.
1143 render_text
->SetDisplayRect(Rect(width
+ 10, 1));
1144 EXPECT_EQ(width
, render_text
->GetUpdatedCursorBounds().x());
1146 // Repeat the test with RTL text.
1147 render_text
->SetText(WideToUTF16(L
"\x5d0\x5d1\x5d2\x5d3\x5d4\x5d5\x5d6\x5d7"
1148 L
"\x5d8\x5d9\x5da\x5db\x5dc\x5dd\x5de\x5df"));
1149 render_text
->MoveCursorTo(SelectionModel(0, CURSOR_FORWARD
));
1150 width
= render_text
->GetStringSize().width();
1151 ASSERT_GT(width
, 10);
1153 // Ensure that the cursor is placed at the width of its preceding text.
1154 render_text
->SetDisplayRect(Rect(width
+ 10, 1));
1155 EXPECT_EQ(width
, render_text
->GetUpdatedCursorBounds().x());
1157 // Ensure that shrinking the display rectangle keeps the cursor in view.
1158 render_text
->SetDisplayRect(Rect(width
- 10, 1));
1159 EXPECT_EQ(render_text
->display_rect().width() - 1,
1160 render_text
->GetUpdatedCursorBounds().right());
1162 // Ensure that the text will pan to fill its expanding display rectangle.
1163 render_text
->SetDisplayRect(Rect(width
- 5, 1));
1164 EXPECT_EQ(render_text
->display_rect().width() - 1,
1165 render_text
->GetUpdatedCursorBounds().x());
1167 // Ensure that a sufficiently large display rectangle shows all the text.
1168 render_text
->SetDisplayRect(Rect(width
+ 10, 1));
1169 EXPECT_EQ(width
, render_text
->GetUpdatedCursorBounds().x());
1172 TEST_F(RenderTextTest
, DisplayRectShowsCursorRTL
) {
1173 // Set the application default text direction to RTL.
1174 const bool was_rtl
= base::i18n::IsRTL();
1177 scoped_ptr
<RenderText
> render_text(RenderText::CreateInstance());
1178 render_text
->SetText(WideToUTF16(L
"abcdefghijklmnopqrstuvwxzyabcdefg"));
1179 render_text
->MoveCursorTo(SelectionModel(0, CURSOR_FORWARD
));
1180 int width
= render_text
->GetStringSize().width();
1181 ASSERT_GT(width
, 10);
1183 // Ensure that the cursor is placed at the width of its preceding text.
1184 render_text
->SetDisplayRect(Rect(width
+ 10, 1));
1185 EXPECT_EQ(render_text
->display_rect().width() - width
- 1,
1186 render_text
->GetUpdatedCursorBounds().x());
1188 // Ensure that shrinking the display rectangle keeps the cursor in view.
1189 render_text
->SetDisplayRect(Rect(width
- 10, 1));
1190 EXPECT_EQ(0, render_text
->GetUpdatedCursorBounds().x());
1192 // Ensure that the text will pan to fill its expanding display rectangle.
1193 render_text
->SetDisplayRect(Rect(width
- 5, 1));
1194 EXPECT_EQ(0, render_text
->GetUpdatedCursorBounds().x());
1196 // Ensure that a sufficiently large display rectangle shows all the text.
1197 render_text
->SetDisplayRect(Rect(width
+ 10, 1));
1198 EXPECT_EQ(render_text
->display_rect().width() - width
- 1,
1199 render_text
->GetUpdatedCursorBounds().x());
1201 // Repeat the test with RTL text.
1202 render_text
->SetText(WideToUTF16(L
"\x5d0\x5d1\x5d2\x5d3\x5d4\x5d5\x5d6\x5d7"
1203 L
"\x5d8\x5d9\x5da\x5db\x5dc\x5dd\x5de\x5df"));
1204 render_text
->MoveCursorTo(SelectionModel(render_text
->text().length(),
1206 width
= render_text
->GetStringSize().width();
1207 ASSERT_GT(width
, 10);
1209 // Ensure that the cursor is placed at the width of its preceding text.
1210 render_text
->SetDisplayRect(Rect(width
+ 10, 1));
1211 EXPECT_EQ(render_text
->display_rect().width() - width
- 1,
1212 render_text
->GetUpdatedCursorBounds().x());
1214 // Ensure that shrinking the display rectangle keeps the cursor in view.
1215 render_text
->SetDisplayRect(Rect(width
- 10, 1));
1216 EXPECT_EQ(0, render_text
->GetUpdatedCursorBounds().x());
1218 // Ensure that the text will pan to fill its expanding display rectangle.
1219 render_text
->SetDisplayRect(Rect(width
- 5, 1));
1220 EXPECT_EQ(0, render_text
->GetUpdatedCursorBounds().x());
1222 // Ensure that a sufficiently large display rectangle shows all the text.
1223 render_text
->SetDisplayRect(Rect(width
+ 10, 1));
1224 EXPECT_EQ(render_text
->display_rect().width() - width
- 1,
1225 render_text
->GetUpdatedCursorBounds().x());
1227 // Reset the application default text direction to LTR.
1229 EXPECT_EQ(was_rtl
, base::i18n::IsRTL());
1231 #endif // !defined(OS_MACOSX)