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.
4
5 #include "ui/gfx/render_text.h"
6
7 #include <algorithm>
8
9 #include "base/format_macros.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15 #include "ui/gfx/break_list.h"
16 #include "ui/gfx/canvas.h"
17
18 #if defined(OS_WIN)
19 #include "base/win/windows_version.h"
20 #include "ui/gfx/render_text_win.h"
21 #endif
22
23 #if defined(OS_LINUX) && !defined(USE_OZONE)
24 #include "ui/gfx/render_text_pango.h"
25 #endif
26
27 #if defined(TOOLKIT_GTK)
28 #include <gtk/gtk.h>
29 #endif
30
31 namespace gfx {
32
33 namespace {
34
35 // Various weak, LTR, RTL, and Bidi string cases with three characters each.
36 const wchar_t kWeak[] = L" . ";
37 const wchar_t kLtr[] = L"abc";
38 const wchar_t kRtl[] = L"\x5d0\x5d1\x5d2";
39 #if !defined(OS_MACOSX)
40 const wchar_t kLtrRtl[] = L"a" L"\x5d0\x5d1";
41 const wchar_t kLtrRtlLtr[] = L"a" L"\x5d1" L"b";
42 const wchar_t kRtlLtr[] = L"\x5d0\x5d1" L"a";
43 const wchar_t kRtlLtrRtl[] = L"\x5d0" L"a" L"\x5d1";
44 #endif
45
46 // Checks whether |range| contains |index|. This is not the same as calling
47 // |range.Contains(gfx::Range(index))| - as that would return true when
48 // |index| == |range.end()|.
IndexInRange(const Range & range,size_t index)49 bool IndexInRange(const Range& range, size_t index) {
50 return index >= range.start() && index < range.end();
51 }
52
GetSelectedText(RenderText * render_text)53 base::string16 GetSelectedText(RenderText* render_text) {
54 return render_text->text().substr(render_text->selection().GetMin(),
55 render_text->selection().length());
56 }
57
58 // A test utility function to set the application default text direction.
SetRTL(bool rtl)59 void SetRTL(bool rtl) {
60 // Override the current locale/direction.
61 base::i18n::SetICUDefaultLocale(rtl ? "he" : "en");
62 #if defined(TOOLKIT_GTK)
63 // Do the same for GTK, which does not rely on the ICU default locale.
64 gtk_widget_set_default_direction(rtl ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR);
65 #endif
66 EXPECT_EQ(rtl, base::i18n::IsRTL());
67 }
68
69 #if !defined(OS_MACOSX)
70 // Ensure cursor movement in the specified |direction| yields |expected| values.
RunMoveCursorLeftRightTest(RenderText * render_text,const std::vector<SelectionModel> & expected,VisualCursorDirection direction)71 void RunMoveCursorLeftRightTest(RenderText* render_text,
72 const std::vector<SelectionModel>& expected,
73 VisualCursorDirection direction) {
74 for (size_t i = 0; i < expected.size(); ++i) {
75 SCOPED_TRACE(base::StringPrintf("Going %s; expected value index %d.",
76 direction == CURSOR_LEFT ? "left" : "right", static_cast<int>(i)));
77 EXPECT_EQ(expected[i], render_text->selection_model());
78 render_text->MoveCursor(CHARACTER_BREAK, direction, false);
79 }
80 // Check that cursoring is clamped at the line edge.
81 EXPECT_EQ(expected.back(), render_text->selection_model());
82 // Check that it is the line edge.
83 render_text->MoveCursor(LINE_BREAK, direction, false);
84 EXPECT_EQ(expected.back(), render_text->selection_model());
85 }
86 #endif // !defined(OS_MACOSX)
87
88 } // namespace
89
90 class RenderTextTest : public testing::Test {
91 };
92
TEST_F(RenderTextTest,DefaultStyle)93 TEST_F(RenderTextTest, DefaultStyle) {
94 // Check the default styles applied to new instances and adjusted text.
95 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
96 EXPECT_TRUE(render_text->text().empty());
97 const wchar_t* const cases[] = { kWeak, kLtr, L"Hello", kRtl, L"", L"" };
98 for (size_t i = 0; i < arraysize(cases); ++i) {
99 EXPECT_TRUE(render_text->colors().EqualsValueForTesting(SK_ColorBLACK));
100 for (size_t style = 0; style < NUM_TEXT_STYLES; ++style)
101 EXPECT_TRUE(render_text->styles()[style].EqualsValueForTesting(false));
102 render_text->SetText(WideToUTF16(cases[i]));
103 }
104 }
105
TEST_F(RenderTextTest,SetColorAndStyle)106 TEST_F(RenderTextTest, SetColorAndStyle) {
107 // Ensure custom default styles persist across setting and clearing text.
108 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
109 const SkColor color = SK_ColorRED;
110 render_text->SetColor(color);
111 render_text->SetStyle(BOLD, true);
112 render_text->SetStyle(UNDERLINE, false);
113 const wchar_t* const cases[] = { kWeak, kLtr, L"Hello", kRtl, L"", L"" };
114 for (size_t i = 0; i < arraysize(cases); ++i) {
115 EXPECT_TRUE(render_text->colors().EqualsValueForTesting(color));
116 EXPECT_TRUE(render_text->styles()[BOLD].EqualsValueForTesting(true));
117 EXPECT_TRUE(render_text->styles()[UNDERLINE].EqualsValueForTesting(false));
118 render_text->SetText(WideToUTF16(cases[i]));
119
120 // Ensure custom default styles can be applied after text has been set.
121 if (i == 1)
122 render_text->SetStyle(STRIKE, true);
123 if (i >= 1)
124 EXPECT_TRUE(render_text->styles()[STRIKE].EqualsValueForTesting(true));
125 }
126 }
127
TEST_F(RenderTextTest,ApplyColorAndStyle)128 TEST_F(RenderTextTest, ApplyColorAndStyle) {
129 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
130 render_text->SetText(ASCIIToUTF16("012345678"));
131
132 // Apply a ranged color and style and check the resulting breaks.
133 render_text->ApplyColor(SK_ColorRED, Range(1, 4));
134 render_text->ApplyStyle(BOLD, true, Range(2, 5));
135 std::vector<std::pair<size_t, SkColor> > expected_color;
136 expected_color.push_back(std::pair<size_t, SkColor>(0, SK_ColorBLACK));
137 expected_color.push_back(std::pair<size_t, SkColor>(1, SK_ColorRED));
138 expected_color.push_back(std::pair<size_t, SkColor>(4, SK_ColorBLACK));
139 EXPECT_TRUE(render_text->colors().EqualsForTesting(expected_color));
140 std::vector<std::pair<size_t, bool> > expected_style;
141 expected_style.push_back(std::pair<size_t, bool>(0, false));
142 expected_style.push_back(std::pair<size_t, bool>(2, true));
143 expected_style.push_back(std::pair<size_t, bool>(5, false));
144 EXPECT_TRUE(render_text->styles()[BOLD].EqualsForTesting(expected_style));
145
146 // Ensure setting a color and style overrides the ranged colors and styles.
147 render_text->SetColor(SK_ColorBLUE);
148 EXPECT_TRUE(render_text->colors().EqualsValueForTesting(SK_ColorBLUE));
149 render_text->SetStyle(BOLD, false);
150 EXPECT_TRUE(render_text->styles()[BOLD].EqualsValueForTesting(false));
151
152 // Apply a color and style over the text end and check the resulting breaks.
153 // (INT_MAX should be used instead of the text length for the range end)
154 const size_t text_length = render_text->text().length();
155 render_text->ApplyColor(SK_ColorRED, Range(0, text_length));
156 render_text->ApplyStyle(BOLD, true, Range(2, text_length));
157 std::vector<std::pair<size_t, SkColor> > expected_color_end;
158 expected_color_end.push_back(std::pair<size_t, SkColor>(0, SK_ColorRED));
159 EXPECT_TRUE(render_text->colors().EqualsForTesting(expected_color_end));
160 std::vector<std::pair<size_t, bool> > expected_style_end;
161 expected_style_end.push_back(std::pair<size_t, bool>(0, false));
162 expected_style_end.push_back(std::pair<size_t, bool>(2, true));
163 EXPECT_TRUE(render_text->styles()[BOLD].EqualsForTesting(expected_style_end));
164
165 // Ensure ranged values adjust to accommodate text length changes.
166 render_text->ApplyStyle(ITALIC, true, Range(0, 2));
167 render_text->ApplyStyle(ITALIC, true, Range(3, 6));
168 render_text->ApplyStyle(ITALIC, true, Range(7, text_length));
169 std::vector<std::pair<size_t, bool> > expected_italic;
170 expected_italic.push_back(std::pair<size_t, bool>(0, true));
171 expected_italic.push_back(std::pair<size_t, bool>(2, false));
172 expected_italic.push_back(std::pair<size_t, bool>(3, true));
173 expected_italic.push_back(std::pair<size_t, bool>(6, false));
174 expected_italic.push_back(std::pair<size_t, bool>(7, true));
175 EXPECT_TRUE(render_text->styles()[ITALIC].EqualsForTesting(expected_italic));
176
177 // Truncating the text should trim any corresponding breaks.
178 render_text->SetText(ASCIIToUTF16("0123456"));
179 expected_italic.resize(4);
180 EXPECT_TRUE(render_text->styles()[ITALIC].EqualsForTesting(expected_italic));
181 render_text->SetText(ASCIIToUTF16("01234"));
182 expected_italic.resize(3);
183 EXPECT_TRUE(render_text->styles()[ITALIC].EqualsForTesting(expected_italic));
184
185 // Appending text should extend the terminal styles without changing breaks.
186 render_text->SetText(ASCIIToUTF16("012345678"));
187 EXPECT_TRUE(render_text->styles()[ITALIC].EqualsForTesting(expected_italic));
188 }
189
190 #if defined(OS_LINUX) && !defined(USE_OZONE)
TEST_F(RenderTextTest,PangoAttributes)191 TEST_F(RenderTextTest, PangoAttributes) {
192 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
193 render_text->SetText(ASCIIToUTF16("012345678"));
194
195 // Apply ranged BOLD/ITALIC styles and check the resulting Pango attributes.
196 render_text->ApplyStyle(BOLD, true, Range(2, 4));
197 render_text->ApplyStyle(ITALIC, true, Range(1, 3));
198
199 struct {
200 int start;
201 int end;
202 bool bold;
203 bool italic;
204 } cases[] = {
205 { 0, 1, false, false },
206 { 1, 2, false, true },
207 { 2, 3, true, true },
208 { 3, 4, true, false },
209 { 4, INT_MAX, false, false },
210 };
211
212 int start = 0, end = 0;
213 RenderTextPango* rt_linux = static_cast<RenderTextPango*>(render_text.get());
214 rt_linux->EnsureLayout();
215 PangoAttrList* attributes = pango_layout_get_attributes(rt_linux->layout_);
216 PangoAttrIterator* iter = pango_attr_list_get_iterator(attributes);
217 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
218 pango_attr_iterator_range(iter, &start, &end);
219 EXPECT_EQ(cases[i].start, start);
220 EXPECT_EQ(cases[i].end, end);
221 PangoFontDescription* font = pango_font_description_new();
222 pango_attr_iterator_get_font(iter, font, NULL, NULL);
223 char* description_string = pango_font_description_to_string(font);
224 const base::string16 desc = ASCIIToUTF16(description_string);
225 const bool bold = desc.find(ASCIIToUTF16("Bold")) != std::string::npos;
226 EXPECT_EQ(cases[i].bold, bold);
227 const bool italic = desc.find(ASCIIToUTF16("Italic")) != std::string::npos;
228 EXPECT_EQ(cases[i].italic, italic);
229 pango_attr_iterator_next(iter);
230 pango_font_description_free(font);
231 g_free(description_string);
232 }
233 EXPECT_FALSE(pango_attr_iterator_next(iter));
234 pango_attr_iterator_destroy(iter);
235 }
236 #endif
237
238 // TODO(asvitkine): Cursor movements tests disabled on Mac because RenderTextMac
239 // does not implement this yet. http://crbug.com/131618
240 #if !defined(OS_MACOSX)
TestVisualCursorMotionInObscuredField(RenderText * render_text,const base::string16 & text,bool select)241 void TestVisualCursorMotionInObscuredField(RenderText* render_text,
242 const base::string16& text,
243 bool select) {
244 ASSERT_TRUE(render_text->obscured());
245 render_text->SetText(text);
246 int len = text.length();
247 render_text->MoveCursor(LINE_BREAK, CURSOR_RIGHT, select);
248 EXPECT_EQ(SelectionModel(Range(select ? 0 : len, len), CURSOR_FORWARD),
249 render_text->selection_model());
250 render_text->MoveCursor(LINE_BREAK, CURSOR_LEFT, select);
251 EXPECT_EQ(SelectionModel(0, CURSOR_BACKWARD), render_text->selection_model());
252 for (int j = 1; j <= len; ++j) {
253 render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, select);
254 EXPECT_EQ(SelectionModel(Range(select ? 0 : j, j), CURSOR_BACKWARD),
255 render_text->selection_model());
256 }
257 for (int j = len - 1; j >= 0; --j) {
258 render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, select);
259 EXPECT_EQ(SelectionModel(Range(select ? 0 : j, j), CURSOR_FORWARD),
260 render_text->selection_model());
261 }
262 render_text->MoveCursor(WORD_BREAK, CURSOR_RIGHT, select);
263 EXPECT_EQ(SelectionModel(Range(select ? 0 : len, len), CURSOR_FORWARD),
264 render_text->selection_model());
265 render_text->MoveCursor(WORD_BREAK, CURSOR_LEFT, select);
266 EXPECT_EQ(SelectionModel(0, CURSOR_BACKWARD), render_text->selection_model());
267 }
268
TEST_F(RenderTextTest,ObscuredText)269 TEST_F(RenderTextTest, ObscuredText) {
270 const base::string16 seuss = ASCIIToUTF16("hop on pop");
271 const base::string16 no_seuss = ASCIIToUTF16("**********");
272 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
273
274 // GetLayoutText() returns asterisks when the obscured bit is set.
275 render_text->SetText(seuss);
276 render_text->SetObscured(true);
277 EXPECT_EQ(seuss, render_text->text());
278 EXPECT_EQ(no_seuss, render_text->GetLayoutText());
279 render_text->SetObscured(false);
280 EXPECT_EQ(seuss, render_text->text());
281 EXPECT_EQ(seuss, render_text->GetLayoutText());
282
283 render_text->SetObscured(true);
284
285 // Surrogate pairs are counted as one code point.
286 const char16 invalid_surrogates[] = {0xDC00, 0xD800, 0};
287 render_text->SetText(invalid_surrogates);
288 EXPECT_EQ(ASCIIToUTF16("**"), render_text->GetLayoutText());
289 const char16 valid_surrogates[] = {0xD800, 0xDC00, 0};
290 render_text->SetText(valid_surrogates);
291 EXPECT_EQ(ASCIIToUTF16("*"), render_text->GetLayoutText());
292 EXPECT_EQ(0U, render_text->cursor_position());
293 render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false);
294 EXPECT_EQ(2U, render_text->cursor_position());
295
296 // Test index conversion and cursor validity with a valid surrogate pair.
297 EXPECT_EQ(0U, render_text->TextIndexToLayoutIndex(0U));
298 EXPECT_EQ(1U, render_text->TextIndexToLayoutIndex(1U));
299 EXPECT_EQ(1U, render_text->TextIndexToLayoutIndex(2U));
300 EXPECT_EQ(0U, render_text->LayoutIndexToTextIndex(0U));
301 EXPECT_EQ(2U, render_text->LayoutIndexToTextIndex(1U));
302 EXPECT_TRUE(render_text->IsCursorablePosition(0U));
303 EXPECT_FALSE(render_text->IsCursorablePosition(1U));
304 EXPECT_TRUE(render_text->IsCursorablePosition(2U));
305
306 // FindCursorPosition() should not return positions between a surrogate pair.
307 render_text->SetDisplayRect(Rect(0, 0, 20, 20));
308 EXPECT_EQ(render_text->FindCursorPosition(Point(0, 0)).caret_pos(), 0U);
309 EXPECT_EQ(render_text->FindCursorPosition(Point(20, 0)).caret_pos(), 2U);
310 for (int x = -1; x <= 20; ++x) {
311 SelectionModel selection = render_text->FindCursorPosition(Point(x, 0));
312 EXPECT_TRUE(selection.caret_pos() == 0U || selection.caret_pos() == 2U);
313 }
314
315 // GetGlyphBounds() should yield the entire string bounds for text index 0.
316 EXPECT_EQ(render_text->GetStringSize().width(),
317 static_cast<int>(render_text->GetGlyphBounds(0U).length()));
318
319 // Cursoring is independent of underlying characters when text is obscured.
320 const wchar_t* const texts[] = {
321 kWeak, kLtr, kLtrRtl, kLtrRtlLtr, kRtl, kRtlLtr, kRtlLtrRtl,
322 L"hop on pop", // Check LTR word boundaries.
323 L"\x05d0\x05d1 \x05d0\x05d2 \x05d1\x05d2", // Check RTL word boundaries.
324 };
325 for (size_t i = 0; i < arraysize(texts); ++i) {
326 base::string16 text = WideToUTF16(texts[i]);
327 TestVisualCursorMotionInObscuredField(render_text.get(), text, false);
328 TestVisualCursorMotionInObscuredField(render_text.get(), text, true);
329 }
330 }
331
TEST_F(RenderTextTest,RevealObscuredText)332 TEST_F(RenderTextTest, RevealObscuredText) {
333 const base::string16 seuss = ASCIIToUTF16("hop on pop");
334 const base::string16 no_seuss = ASCIIToUTF16("**********");
335 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
336
337 render_text->SetText(seuss);
338 render_text->SetObscured(true);
339 EXPECT_EQ(seuss, render_text->text());
340 EXPECT_EQ(no_seuss, render_text->GetLayoutText());
341
342 // Valid reveal index and new revealed index clears previous one.
343 render_text->RenderText::SetObscuredRevealIndex(0);
344 EXPECT_EQ(ASCIIToUTF16("h*********"), render_text->GetLayoutText());
345 render_text->RenderText::SetObscuredRevealIndex(1);
346 EXPECT_EQ(ASCIIToUTF16("*o********"), render_text->GetLayoutText());
347 render_text->RenderText::SetObscuredRevealIndex(2);
348 EXPECT_EQ(ASCIIToUTF16("**p*******"), render_text->GetLayoutText());
349
350 // Invalid reveal index.
351 render_text->RenderText::SetObscuredRevealIndex(-1);
352 EXPECT_EQ(no_seuss, render_text->GetLayoutText());
353 render_text->RenderText::SetObscuredRevealIndex(seuss.length() + 1);
354 EXPECT_EQ(no_seuss, render_text->GetLayoutText());
355
356 // SetObscured clears the revealed index.
357 render_text->RenderText::SetObscuredRevealIndex(0);
358 EXPECT_EQ(ASCIIToUTF16("h*********"), render_text->GetLayoutText());
359 render_text->SetObscured(false);
360 EXPECT_EQ(seuss, render_text->GetLayoutText());
361 render_text->SetObscured(true);
362 EXPECT_EQ(no_seuss, render_text->GetLayoutText());
363
364 // SetText clears the revealed index.
365 render_text->SetText(ASCIIToUTF16("new"));
366 EXPECT_EQ(ASCIIToUTF16("***"), render_text->GetLayoutText());
367 render_text->RenderText::SetObscuredRevealIndex(2);
368 EXPECT_EQ(ASCIIToUTF16("**w"), render_text->GetLayoutText());
369 render_text->SetText(ASCIIToUTF16("new longer"));
370 EXPECT_EQ(ASCIIToUTF16("**********"), render_text->GetLayoutText());
371
372 // Text with invalid surrogates.
373 const char16 invalid_surrogates[] = {0xDC00, 0xD800, 'h', 'o', 'p', 0};
374 render_text->SetText(invalid_surrogates);
375 EXPECT_EQ(ASCIIToUTF16("*****"), render_text->GetLayoutText());
376 render_text->RenderText::SetObscuredRevealIndex(0);
377 const char16 invalid_expect_0[] = {0xDC00, '*', '*', '*', '*', 0};
378 EXPECT_EQ(invalid_expect_0, render_text->GetLayoutText());
379 render_text->RenderText::SetObscuredRevealIndex(1);
380 const char16 invalid_expect_1[] = {'*', 0xD800, '*', '*', '*', 0};
381 EXPECT_EQ(invalid_expect_1, render_text->GetLayoutText());
382 render_text->RenderText::SetObscuredRevealIndex(2);
383 EXPECT_EQ(ASCIIToUTF16("**h**"), render_text->GetLayoutText());
384
385 // Text with valid surrogates before and after the reveal index.
386 const char16 valid_surrogates[] =
387 {0xD800, 0xDC00, 'h', 'o', 'p', 0xD800, 0xDC00, 0};
388 render_text->SetText(valid_surrogates);
389 EXPECT_EQ(ASCIIToUTF16("*****"), render_text->GetLayoutText());
390 render_text->RenderText::SetObscuredRevealIndex(0);
391 const char16 valid_expect_0_and_1[] = {0xD800, 0xDC00, '*', '*', '*', '*', 0};
392 EXPECT_EQ(valid_expect_0_and_1, render_text->GetLayoutText());
393 render_text->RenderText::SetObscuredRevealIndex(1);
394 EXPECT_EQ(valid_expect_0_and_1, render_text->GetLayoutText());
395 render_text->RenderText::SetObscuredRevealIndex(2);
396 EXPECT_EQ(ASCIIToUTF16("*h***"), render_text->GetLayoutText());
397 render_text->RenderText::SetObscuredRevealIndex(5);
398 const char16 valid_expect_5_and_6[] = {'*', '*', '*', '*', 0xD800, 0xDC00, 0};
399 EXPECT_EQ(valid_expect_5_and_6, render_text->GetLayoutText());
400 render_text->RenderText::SetObscuredRevealIndex(6);
401 EXPECT_EQ(valid_expect_5_and_6, render_text->GetLayoutText());
402 }
403
TEST_F(RenderTextTest,TruncatedText)404 TEST_F(RenderTextTest, TruncatedText) {
405 struct {
406 const wchar_t* text;
407 const wchar_t* layout_text;
408 } cases[] = {
409 // Strings shorter than the truncation length should be laid out in full.
410 { L"", L"" },
411 { kWeak, kWeak },
412 { kLtr, kLtr },
413 { kLtrRtl, kLtrRtl },
414 { kLtrRtlLtr, kLtrRtlLtr },
415 { kRtl, kRtl },
416 { kRtlLtr, kRtlLtr },
417 { kRtlLtrRtl, kRtlLtrRtl },
418 // Strings as long as the truncation length should be laid out in full.
419 { L"01234", L"01234" },
420 // Long strings should be truncated with an ellipsis appended at the end.
421 { L"012345", L"0123\x2026" },
422 { L"012" L" . ", L"012 \x2026" },
423 { L"012" L"abc", L"012a\x2026" },
424 { L"012" L"a" L"\x5d0\x5d1", L"012a\x2026" },
425 { L"012" L"a" L"\x5d1" L"b", L"012a\x2026" },
426 { L"012" L"\x5d0\x5d1\x5d2", L"012\x5d0\x2026" },
427 { L"012" L"\x5d0\x5d1" L"a", L"012\x5d0\x2026" },
428 { L"012" L"\x5d0" L"a" L"\x5d1", L"012\x5d0\x2026" },
429 // Surrogate pairs should be truncated reasonably enough.
430 { L"0123\x0915\x093f", L"0123\x2026" },
431 { L"0\x05e9\x05bc\x05c1\x05b8", L"0\x05e9\x05bc\x05c1\x05b8" },
432 { L"01\x05e9\x05bc\x05c1\x05b8", L"01\x05e9\x05bc\x2026" },
433 { L"012\x05e9\x05bc\x05c1\x05b8", L"012\x05e9\x2026" },
434 { L"0123\x05e9\x05bc\x05c1\x05b8", L"0123\x2026" },
435 { L"01234\x05e9\x05bc\x05c1\x05b8", L"0123\x2026" },
436 { L"012\xF0\x9D\x84\x9E", L"012\xF0\x2026" },
437 };
438
439 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
440 render_text->set_truncate_length(5);
441 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
442 render_text->SetText(WideToUTF16(cases[i].text));
443 EXPECT_EQ(WideToUTF16(cases[i].text), render_text->text());
444 EXPECT_EQ(WideToUTF16(cases[i].layout_text), render_text->GetLayoutText())
445 << "For case " << i << ": " << cases[i].text;
446 }
447 }
448
TEST_F(RenderTextTest,TruncatedObscuredText)449 TEST_F(RenderTextTest, TruncatedObscuredText) {
450 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
451 render_text->set_truncate_length(3);
452 render_text->SetObscured(true);
453 render_text->SetText(WideToUTF16(L"abcdef"));
454 EXPECT_EQ(WideToUTF16(L"abcdef"), render_text->text());
455 EXPECT_EQ(WideToUTF16(L"**\x2026"), render_text->GetLayoutText());
456 }
457
TEST_F(RenderTextTest,TruncatedCursorMovementLTR)458 TEST_F(RenderTextTest, TruncatedCursorMovementLTR) {
459 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
460 render_text->set_truncate_length(2);
461 render_text->SetText(WideToUTF16(L"abcd"));
462
463 EXPECT_EQ(SelectionModel(0, CURSOR_BACKWARD), render_text->selection_model());
464 render_text->MoveCursor(LINE_BREAK, CURSOR_RIGHT, false);
465 EXPECT_EQ(SelectionModel(4, CURSOR_FORWARD), render_text->selection_model());
466 render_text->MoveCursor(LINE_BREAK, CURSOR_LEFT, false);
467 EXPECT_EQ(SelectionModel(0, CURSOR_BACKWARD), render_text->selection_model());
468
469 std::vector<SelectionModel> expected;
470 expected.push_back(SelectionModel(0, CURSOR_BACKWARD));
471 expected.push_back(SelectionModel(1, CURSOR_BACKWARD));
472 // The cursor hops over the ellipsis and elided text to the line end.
473 expected.push_back(SelectionModel(4, CURSOR_BACKWARD));
474 expected.push_back(SelectionModel(4, CURSOR_FORWARD));
475 RunMoveCursorLeftRightTest(render_text.get(), expected, CURSOR_RIGHT);
476
477 expected.clear();
478 expected.push_back(SelectionModel(4, CURSOR_FORWARD));
479 // The cursor hops over the elided text to preceeding text.
480 expected.push_back(SelectionModel(1, CURSOR_FORWARD));
481 expected.push_back(SelectionModel(0, CURSOR_FORWARD));
482 expected.push_back(SelectionModel(0, CURSOR_BACKWARD));
483 RunMoveCursorLeftRightTest(render_text.get(), expected, CURSOR_LEFT);
484 }
485
TEST_F(RenderTextTest,TruncatedCursorMovementRTL)486 TEST_F(RenderTextTest, TruncatedCursorMovementRTL) {
487 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
488 render_text->set_truncate_length(2);
489 render_text->SetText(WideToUTF16(L"\x5d0\x5d1\x5d2\x5d3"));
490
491 EXPECT_EQ(SelectionModel(0, CURSOR_BACKWARD), render_text->selection_model());
492 render_text->MoveCursor(LINE_BREAK, CURSOR_LEFT, false);
493 EXPECT_EQ(SelectionModel(4, CURSOR_FORWARD), render_text->selection_model());
494 render_text->MoveCursor(LINE_BREAK, CURSOR_RIGHT, false);
495 EXPECT_EQ(SelectionModel(0, CURSOR_BACKWARD), render_text->selection_model());
496
497 std::vector<SelectionModel> expected;
498 expected.push_back(SelectionModel(0, CURSOR_BACKWARD));
499 expected.push_back(SelectionModel(1, CURSOR_BACKWARD));
500 // The cursor hops over the ellipsis and elided text to the line end.
501 expected.push_back(SelectionModel(4, CURSOR_BACKWARD));
502 expected.push_back(SelectionModel(4, CURSOR_FORWARD));
503 RunMoveCursorLeftRightTest(render_text.get(), expected, CURSOR_LEFT);
504
505 expected.clear();
506 expected.push_back(SelectionModel(4, CURSOR_FORWARD));
507 // The cursor hops over the elided text to preceeding text.
508 expected.push_back(SelectionModel(1, CURSOR_FORWARD));
509 expected.push_back(SelectionModel(0, CURSOR_FORWARD));
510 expected.push_back(SelectionModel(0, CURSOR_BACKWARD));
511 RunMoveCursorLeftRightTest(render_text.get(), expected, CURSOR_RIGHT);
512 }
513
TEST_F(RenderTextTest,GetTextDirection)514 TEST_F(RenderTextTest, GetTextDirection) {
515 struct {
516 const wchar_t* text;
517 const base::i18n::TextDirection text_direction;
518 } cases[] = {
519 // Blank strings and those with no/weak directionality default to LTR.
520 { L"", base::i18n::LEFT_TO_RIGHT },
521 { kWeak, base::i18n::LEFT_TO_RIGHT },
522 // Strings that begin with strong LTR characters.
523 { kLtr, base::i18n::LEFT_TO_RIGHT },
524 { kLtrRtl, base::i18n::LEFT_TO_RIGHT },
525 { kLtrRtlLtr, base::i18n::LEFT_TO_RIGHT },
526 // Strings that begin with strong RTL characters.
527 { kRtl, base::i18n::RIGHT_TO_LEFT },
528 { kRtlLtr, base::i18n::RIGHT_TO_LEFT },
529 { kRtlLtrRtl, base::i18n::RIGHT_TO_LEFT },
530 };
531
532 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
533 const bool was_rtl = base::i18n::IsRTL();
534
535 for (size_t i = 0; i < 2; ++i) {
536 // Toggle the application default text direction (to try each direction).
537 SetRTL(!base::i18n::IsRTL());
538 const base::i18n::TextDirection ui_direction = base::i18n::IsRTL() ?
539 base::i18n::RIGHT_TO_LEFT : base::i18n::LEFT_TO_RIGHT;
540
541 // Ensure that directionality modes yield the correct text directions.
542 for (size_t j = 0; j < ARRAYSIZE_UNSAFE(cases); j++) {
543 render_text->SetText(WideToUTF16(cases[j].text));
544 render_text->SetDirectionalityMode(DIRECTIONALITY_FROM_TEXT);
545 EXPECT_EQ(render_text->GetTextDirection(), cases[j].text_direction);
546 render_text->SetDirectionalityMode(DIRECTIONALITY_FROM_UI);
547 EXPECT_EQ(render_text->GetTextDirection(), ui_direction);
548 render_text->SetDirectionalityMode(DIRECTIONALITY_FORCE_LTR);
549 EXPECT_EQ(render_text->GetTextDirection(), base::i18n::LEFT_TO_RIGHT);
550 render_text->SetDirectionalityMode(DIRECTIONALITY_FORCE_RTL);
551 EXPECT_EQ(render_text->GetTextDirection(), base::i18n::RIGHT_TO_LEFT);
552 }
553 }
554
555 EXPECT_EQ(was_rtl, base::i18n::IsRTL());
556
557 // Ensure that text changes update the direction for DIRECTIONALITY_FROM_TEXT.
558 render_text->SetDirectionalityMode(DIRECTIONALITY_FROM_TEXT);
559 render_text->SetText(WideToUTF16(kLtr));
560 EXPECT_EQ(render_text->GetTextDirection(), base::i18n::LEFT_TO_RIGHT);
561 render_text->SetText(WideToUTF16(kRtl));
562 EXPECT_EQ(render_text->GetTextDirection(), base::i18n::RIGHT_TO_LEFT);
563 }
564
TEST_F(RenderTextTest,MoveCursorLeftRightInLtr)565 TEST_F(RenderTextTest, MoveCursorLeftRightInLtr) {
566 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
567
568 // Pure LTR.
569 render_text->SetText(ASCIIToUTF16("abc"));
570 // |expected| saves the expected SelectionModel when moving cursor from left
571 // to right.
572 std::vector<SelectionModel> expected;
573 expected.push_back(SelectionModel(0, CURSOR_BACKWARD));
574 expected.push_back(SelectionModel(1, CURSOR_BACKWARD));
575 expected.push_back(SelectionModel(2, CURSOR_BACKWARD));
576 expected.push_back(SelectionModel(3, CURSOR_BACKWARD));
577 expected.push_back(SelectionModel(3, CURSOR_FORWARD));
578 RunMoveCursorLeftRightTest(render_text.get(), expected, CURSOR_RIGHT);
579
580 expected.clear();
581 expected.push_back(SelectionModel(3, CURSOR_FORWARD));
582 expected.push_back(SelectionModel(2, CURSOR_FORWARD));
583 expected.push_back(SelectionModel(1, CURSOR_FORWARD));
584 expected.push_back(SelectionModel(0, CURSOR_FORWARD));
585 expected.push_back(SelectionModel(0, CURSOR_BACKWARD));
586 RunMoveCursorLeftRightTest(render_text.get(), expected, CURSOR_LEFT);
587 }
588
TEST_F(RenderTextTest,MoveCursorLeftRightInLtrRtl)589 TEST_F(RenderTextTest, MoveCursorLeftRightInLtrRtl) {
590 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
591 // LTR-RTL
592 render_text->SetText(WideToUTF16(L"abc\x05d0\x05d1\x05d2"));
593 // The last one is the expected END position.
594 std::vector<SelectionModel> expected;
595 expected.push_back(SelectionModel(0, CURSOR_BACKWARD));
596 expected.push_back(SelectionModel(1, CURSOR_BACKWARD));
597 expected.push_back(SelectionModel(2, CURSOR_BACKWARD));
598 expected.push_back(SelectionModel(3, CURSOR_BACKWARD));
599 expected.push_back(SelectionModel(5, CURSOR_FORWARD));
600 expected.push_back(SelectionModel(4, CURSOR_FORWARD));
601 expected.push_back(SelectionModel(3, CURSOR_FORWARD));
602 expected.push_back(SelectionModel(6, CURSOR_FORWARD));
603 RunMoveCursorLeftRightTest(render_text.get(), expected, CURSOR_RIGHT);
604
605 expected.clear();
606 expected.push_back(SelectionModel(6, CURSOR_FORWARD));
607 expected.push_back(SelectionModel(4, CURSOR_BACKWARD));
608 expected.push_back(SelectionModel(5, CURSOR_BACKWARD));
609 expected.push_back(SelectionModel(6, CURSOR_BACKWARD));
610 expected.push_back(SelectionModel(2, CURSOR_FORWARD));
611 expected.push_back(SelectionModel(1, CURSOR_FORWARD));
612 expected.push_back(SelectionModel(0, CURSOR_FORWARD));
613 expected.push_back(SelectionModel(0, CURSOR_BACKWARD));
614 RunMoveCursorLeftRightTest(render_text.get(), expected, CURSOR_LEFT);
615 }
616
TEST_F(RenderTextTest,MoveCursorLeftRightInLtrRtlLtr)617 TEST_F(RenderTextTest, MoveCursorLeftRightInLtrRtlLtr) {
618 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
619 // LTR-RTL-LTR.
620 render_text->SetText(WideToUTF16(L"a" L"\x05d1" L"b"));
621 std::vector<SelectionModel> expected;
622 expected.push_back(SelectionModel(0, CURSOR_BACKWARD));
623 expected.push_back(SelectionModel(1, CURSOR_BACKWARD));
624 expected.push_back(SelectionModel(1, CURSOR_FORWARD));
625 expected.push_back(SelectionModel(3, CURSOR_BACKWARD));
626 expected.push_back(SelectionModel(3, CURSOR_FORWARD));
627 RunMoveCursorLeftRightTest(render_text.get(), expected, CURSOR_RIGHT);
628
629 expected.clear();
630 expected.push_back(SelectionModel(3, CURSOR_FORWARD));
631 expected.push_back(SelectionModel(2, CURSOR_FORWARD));
632 expected.push_back(SelectionModel(2, CURSOR_BACKWARD));
633 expected.push_back(SelectionModel(0, CURSOR_FORWARD));
634 expected.push_back(SelectionModel(0, CURSOR_BACKWARD));
635 RunMoveCursorLeftRightTest(render_text.get(), expected, CURSOR_LEFT);
636 }
637
TEST_F(RenderTextTest,MoveCursorLeftRightInRtl)638 TEST_F(RenderTextTest, MoveCursorLeftRightInRtl) {
639 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
640 // Pure RTL.
641 render_text->SetText(WideToUTF16(L"\x05d0\x05d1\x05d2"));
642 render_text->MoveCursor(LINE_BREAK, CURSOR_RIGHT, false);
643 std::vector<SelectionModel> expected;
644
645 expected.push_back(SelectionModel(0, CURSOR_BACKWARD));
646 expected.push_back(SelectionModel(1, CURSOR_BACKWARD));
647 expected.push_back(SelectionModel(2, CURSOR_BACKWARD));
648 expected.push_back(SelectionModel(3, CURSOR_BACKWARD));
649 expected.push_back(SelectionModel(3, CURSOR_FORWARD));
650 RunMoveCursorLeftRightTest(render_text.get(), expected, CURSOR_LEFT);
651
652 expected.clear();
653
654 expected.push_back(SelectionModel(3, CURSOR_FORWARD));
655 expected.push_back(SelectionModel(2, CURSOR_FORWARD));
656 expected.push_back(SelectionModel(1, CURSOR_FORWARD));
657 expected.push_back(SelectionModel(0, CURSOR_FORWARD));
658 expected.push_back(SelectionModel(0, CURSOR_BACKWARD));
659 RunMoveCursorLeftRightTest(render_text.get(), expected, CURSOR_RIGHT);
660 }
661
TEST_F(RenderTextTest,MoveCursorLeftRightInRtlLtr)662 TEST_F(RenderTextTest, MoveCursorLeftRightInRtlLtr) {
663 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
664 // RTL-LTR
665 render_text->SetText(WideToUTF16(L"\x05d0\x05d1\x05d2" L"abc"));
666 render_text->MoveCursor(LINE_BREAK, CURSOR_RIGHT, false);
667 std::vector<SelectionModel> expected;
668 expected.push_back(SelectionModel(0, CURSOR_BACKWARD));
669 expected.push_back(SelectionModel(1, CURSOR_BACKWARD));
670 expected.push_back(SelectionModel(2, CURSOR_BACKWARD));
671 expected.push_back(SelectionModel(3, CURSOR_BACKWARD));
672 expected.push_back(SelectionModel(5, CURSOR_FORWARD));
673 expected.push_back(SelectionModel(4, CURSOR_FORWARD));
674 expected.push_back(SelectionModel(3, CURSOR_FORWARD));
675 expected.push_back(SelectionModel(6, CURSOR_FORWARD));
676 RunMoveCursorLeftRightTest(render_text.get(), expected, CURSOR_LEFT);
677
678 expected.clear();
679 expected.push_back(SelectionModel(6, CURSOR_FORWARD));
680 expected.push_back(SelectionModel(4, CURSOR_BACKWARD));
681 expected.push_back(SelectionModel(5, CURSOR_BACKWARD));
682 expected.push_back(SelectionModel(6, CURSOR_BACKWARD));
683 expected.push_back(SelectionModel(2, CURSOR_FORWARD));
684 expected.push_back(SelectionModel(1, CURSOR_FORWARD));
685 expected.push_back(SelectionModel(0, CURSOR_FORWARD));
686 expected.push_back(SelectionModel(0, CURSOR_BACKWARD));
687 RunMoveCursorLeftRightTest(render_text.get(), expected, CURSOR_RIGHT);
688 }
689
TEST_F(RenderTextTest,MoveCursorLeftRightInRtlLtrRtl)690 TEST_F(RenderTextTest, MoveCursorLeftRightInRtlLtrRtl) {
691 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
692 // RTL-LTR-RTL.
693 render_text->SetText(WideToUTF16(L"\x05d0" L"a" L"\x05d1"));
694 render_text->MoveCursor(LINE_BREAK, CURSOR_RIGHT, false);
695 std::vector<SelectionModel> expected;
696 expected.push_back(SelectionModel(0, CURSOR_BACKWARD));
697 expected.push_back(SelectionModel(1, CURSOR_BACKWARD));
698 expected.push_back(SelectionModel(1, CURSOR_FORWARD));
699 expected.push_back(SelectionModel(3, CURSOR_BACKWARD));
700 expected.push_back(SelectionModel(3, CURSOR_FORWARD));
701 RunMoveCursorLeftRightTest(render_text.get(), expected, CURSOR_LEFT);
702
703 expected.clear();
704 expected.push_back(SelectionModel(3, CURSOR_FORWARD));
705 expected.push_back(SelectionModel(2, CURSOR_FORWARD));
706 expected.push_back(SelectionModel(2, CURSOR_BACKWARD));
707 expected.push_back(SelectionModel(0, CURSOR_FORWARD));
708 expected.push_back(SelectionModel(0, CURSOR_BACKWARD));
709 RunMoveCursorLeftRightTest(render_text.get(), expected, CURSOR_RIGHT);
710 }
711
712 // TODO(xji): temporarily disable in platform Win since the complex script
713 // characters turned into empty square due to font regression. So, not able
714 // to test 2 characters belong to the same grapheme.
715 #if defined(OS_LINUX)
TEST_F(RenderTextTest,MoveCursorLeftRight_ComplexScript)716 TEST_F(RenderTextTest, MoveCursorLeftRight_ComplexScript) {
717 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
718
719 render_text->SetText(WideToUTF16(L"\x0915\x093f\x0915\x094d\x0915"));
720 EXPECT_EQ(0U, render_text->cursor_position());
721 render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false);
722 EXPECT_EQ(2U, render_text->cursor_position());
723 render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false);
724 EXPECT_EQ(4U, render_text->cursor_position());
725 render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false);
726 EXPECT_EQ(5U, render_text->cursor_position());
727 render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false);
728 EXPECT_EQ(5U, render_text->cursor_position());
729
730 render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, false);
731 EXPECT_EQ(4U, render_text->cursor_position());
732 render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, false);
733 EXPECT_EQ(2U, render_text->cursor_position());
734 render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, false);
735 EXPECT_EQ(0U, render_text->cursor_position());
736 render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, false);
737 EXPECT_EQ(0U, render_text->cursor_position());
738 }
739 #endif
740
TEST_F(RenderTextTest,MoveCursorLeftRight_MeiryoUILigatures)741 TEST_F(RenderTextTest, MoveCursorLeftRight_MeiryoUILigatures) {
742 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
743 // Meiryo UI uses single-glyph ligatures for 'ff' and 'ffi', but each letter
744 // (code point) has unique bounds, so mid-glyph cursoring should be possible.
745 render_text->SetFont(Font("Meiryo UI", 12));
746 render_text->SetText(WideToUTF16(L"ff ffi"));
747 EXPECT_EQ(0U, render_text->cursor_position());
748 for (size_t i = 0; i < render_text->text().length(); ++i) {
749 render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false);
750 EXPECT_EQ(i + 1, render_text->cursor_position());
751 }
752 EXPECT_EQ(6U, render_text->cursor_position());
753 }
754
TEST_F(RenderTextTest,GraphemePositions)755 TEST_F(RenderTextTest, GraphemePositions) {
756 // LTR 2-character grapheme, LTR abc, LTR 2-character grapheme.
757 const base::string16 kText1 =
758 WideToUTF16(L"\x0915\x093f" L"abc" L"\x0915\x093f");
759
760 // LTR ab, LTR 2-character grapheme, LTR cd.
761 const base::string16 kText2 = WideToUTF16(L"ab" L"\x0915\x093f" L"cd");
762
763 // The below is 'MUSICAL SYMBOL G CLEF', which is represented in UTF-16 as
764 // two characters forming the surrogate pair 0x0001D11E.
765 const std::string kSurrogate = "\xF0\x9D\x84\x9E";
766
767 // LTR ab, UTF16 surrogate pair, LTR cd.
768 const base::string16 kText3 = UTF8ToUTF16("ab" + kSurrogate + "cd");
769
770 struct {
771 base::string16 text;
772 size_t index;
773 size_t expected_previous;
774 size_t expected_next;
775 } cases[] = {
776 { base::string16(), 0, 0, 0 },
777 { base::string16(), 1, 0, 0 },
778 { base::string16(), 50, 0, 0 },
779 { kText1, 0, 0, 2 },
780 { kText1, 1, 0, 2 },
781 { kText1, 2, 0, 3 },
782 { kText1, 3, 2, 4 },
783 { kText1, 4, 3, 5 },
784 { kText1, 5, 4, 7 },
785 { kText1, 6, 5, 7 },
786 { kText1, 7, 5, 7 },
787 { kText1, 8, 7, 7 },
788 { kText1, 50, 7, 7 },
789 { kText2, 0, 0, 1 },
790 { kText2, 1, 0, 2 },
791 { kText2, 2, 1, 4 },
792 { kText2, 3, 2, 4 },
793 { kText2, 4, 2, 5 },
794 { kText2, 5, 4, 6 },
795 { kText2, 6, 5, 6 },
796 { kText2, 7, 6, 6 },
797 { kText2, 50, 6, 6 },
798 { kText3, 0, 0, 1 },
799 { kText3, 1, 0, 2 },
800 { kText3, 2, 1, 4 },
801 { kText3, 3, 2, 4 },
802 { kText3, 4, 2, 5 },
803 { kText3, 5, 4, 6 },
804 { kText3, 6, 5, 6 },
805 { kText3, 7, 6, 6 },
806 { kText3, 50, 6, 6 },
807 };
808
809 // TODO(asvitkine): Disable tests that fail on XP bots due to lack of complete
810 // font support for some scripts - http://crbug.com/106450
811 #if defined(OS_WIN)
812 if (base::win::GetVersion() < base::win::VERSION_VISTA)
813 return;
814 #endif
815
816 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
817 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
818 render_text->SetText(cases[i].text);
819
820 size_t next = render_text->IndexOfAdjacentGrapheme(cases[i].index,
821 CURSOR_FORWARD);
822 EXPECT_EQ(cases[i].expected_next, next);
823 EXPECT_TRUE(render_text->IsCursorablePosition(next));
824
825 size_t previous = render_text->IndexOfAdjacentGrapheme(cases[i].index,
826 CURSOR_BACKWARD);
827 EXPECT_EQ(cases[i].expected_previous, previous);
828 EXPECT_TRUE(render_text->IsCursorablePosition(previous));
829 }
830 }
831
TEST_F(RenderTextTest,EdgeSelectionModels)832 TEST_F(RenderTextTest, EdgeSelectionModels) {
833 // Simple Latin text.
834 const base::string16 kLatin = WideToUTF16(L"abc");
835 // LTR 2-character grapheme.
836 const base::string16 kLTRGrapheme = WideToUTF16(L"\x0915\x093f");
837 // LTR 2-character grapheme, LTR a, LTR 2-character grapheme.
838 const base::string16 kHindiLatin =
839 WideToUTF16(L"\x0915\x093f" L"a" L"\x0915\x093f");
840 // RTL 2-character grapheme.
841 const base::string16 kRTLGrapheme = WideToUTF16(L"\x05e0\x05b8");
842 // RTL 2-character grapheme, LTR a, RTL 2-character grapheme.
843 const base::string16 kHebrewLatin =
844 WideToUTF16(L"\x05e0\x05b8" L"a" L"\x05e0\x05b8");
845
846 struct {
847 base::string16 text;
848 base::i18n::TextDirection expected_text_direction;
849 } cases[] = {
850 { base::string16(), base::i18n::LEFT_TO_RIGHT },
851 { kLatin, base::i18n::LEFT_TO_RIGHT },
852 { kLTRGrapheme, base::i18n::LEFT_TO_RIGHT },
853 { kHindiLatin, base::i18n::LEFT_TO_RIGHT },
854 { kRTLGrapheme, base::i18n::RIGHT_TO_LEFT },
855 { kHebrewLatin, base::i18n::RIGHT_TO_LEFT },
856 };
857
858 // TODO(asvitkine): Disable tests that fail on XP bots due to lack of complete
859 // font support for some scripts - http://crbug.com/106450
860 #if defined(OS_WIN)
861 if (base::win::GetVersion() < base::win::VERSION_VISTA)
862 return;
863 #endif
864
865 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
866 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
867 render_text->SetText(cases[i].text);
868 bool ltr = (cases[i].expected_text_direction == base::i18n::LEFT_TO_RIGHT);
869
870 SelectionModel start_edge =
871 render_text->EdgeSelectionModel(ltr ? CURSOR_LEFT : CURSOR_RIGHT);
872 EXPECT_EQ(start_edge, SelectionModel(0, CURSOR_BACKWARD));
873
874 SelectionModel end_edge =
875 render_text->EdgeSelectionModel(ltr ? CURSOR_RIGHT : CURSOR_LEFT);
876 EXPECT_EQ(end_edge, SelectionModel(cases[i].text.length(), CURSOR_FORWARD));
877 }
878 }
879
TEST_F(RenderTextTest,SelectAll)880 TEST_F(RenderTextTest, SelectAll) {
881 const wchar_t* const cases[] =
882 { kWeak, kLtr, kLtrRtl, kLtrRtlLtr, kRtl, kRtlLtr, kRtlLtrRtl };
883
884 // Ensure that SelectAll respects the |reversed| argument regardless of
885 // application locale and text content directionality.
886 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
887 const SelectionModel expected_reversed(Range(3, 0), CURSOR_FORWARD);
888 const SelectionModel expected_forwards(Range(0, 3), CURSOR_BACKWARD);
889 const bool was_rtl = base::i18n::IsRTL();
890
891 for (size_t i = 0; i < 2; ++i) {
892 SetRTL(!base::i18n::IsRTL());
893 // Test that an empty string produces an empty selection model.
894 render_text->SetText(base::string16());
895 EXPECT_EQ(render_text->selection_model(), SelectionModel());
896
897 // Test the weak, LTR, RTL, and Bidi string cases.
898 for (size_t j = 0; j < ARRAYSIZE_UNSAFE(cases); j++) {
899 render_text->SetText(WideToUTF16(cases[j]));
900 render_text->SelectAll(false);
901 EXPECT_EQ(render_text->selection_model(), expected_forwards);
902 render_text->SelectAll(true);
903 EXPECT_EQ(render_text->selection_model(), expected_reversed);
904 }
905 }
906
907 EXPECT_EQ(was_rtl, base::i18n::IsRTL());
908 }
909
TEST_F(RenderTextTest,MoveCursorLeftRightWithSelection)910 TEST_F(RenderTextTest, MoveCursorLeftRightWithSelection) {
911 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
912 render_text->SetText(WideToUTF16(L"abc\x05d0\x05d1\x05d2"));
913 // Left arrow on select ranging (6, 4).
914 render_text->MoveCursor(LINE_BREAK, CURSOR_RIGHT, false);
915 EXPECT_EQ(Range(6), render_text->selection());
916 render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, false);
917 EXPECT_EQ(Range(4), render_text->selection());
918 render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, false);
919 EXPECT_EQ(Range(5), render_text->selection());
920 render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, false);
921 EXPECT_EQ(Range(6), render_text->selection());
922 render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, true);
923 EXPECT_EQ(Range(6, 5), render_text->selection());
924 render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, true);
925 EXPECT_EQ(Range(6, 4), render_text->selection());
926 render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, false);
927 EXPECT_EQ(Range(6), render_text->selection());
928
929 // Right arrow on select ranging (4, 6).
930 render_text->MoveCursor(LINE_BREAK, CURSOR_LEFT, false);
931 EXPECT_EQ(Range(0), render_text->selection());
932 render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false);
933 EXPECT_EQ(Range(1), render_text->selection());
934 render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false);
935 EXPECT_EQ(Range(2), render_text->selection());
936 render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false);
937 EXPECT_EQ(Range(3), render_text->selection());
938 render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false);
939 EXPECT_EQ(Range(5), render_text->selection());
940 render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false);
941 EXPECT_EQ(Range(4), render_text->selection());
942 render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, true);
943 EXPECT_EQ(Range(4, 5), render_text->selection());
944 render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, true);
945 EXPECT_EQ(Range(4, 6), render_text->selection());
946 render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false);
947 EXPECT_EQ(Range(4), render_text->selection());
948 }
949 #endif // !defined(OS_MACOSX)
950
951 // TODO(xji): Make these work on Windows.
952 #if defined(OS_LINUX)
MoveLeftRightByWordVerifier(RenderText * render_text,const wchar_t * str)953 void MoveLeftRightByWordVerifier(RenderText* render_text,
954 const wchar_t* str) {
955 render_text->SetText(WideToUTF16(str));
956
957 // Test moving by word from left ro right.
958 render_text->MoveCursor(LINE_BREAK, CURSOR_LEFT, false);
959 bool first_word = true;
960 while (true) {
961 // First, test moving by word from a word break position, such as from
962 // "|abc def" to "abc| def".
963 SelectionModel start = render_text->selection_model();
964 render_text->MoveCursor(WORD_BREAK, CURSOR_RIGHT, false);
965 SelectionModel end = render_text->selection_model();
966 if (end == start) // reach the end.
967 break;
968
969 // For testing simplicity, each word is a 3-character word.
970 int num_of_character_moves = first_word ? 3 : 4;
971 first_word = false;
972 render_text->MoveCursorTo(start);
973 for (int j = 0; j < num_of_character_moves; ++j)
974 render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false);
975 EXPECT_EQ(end, render_text->selection_model());
976
977 // Then, test moving by word from positions inside the word, such as from
978 // "a|bc def" to "abc| def", and from "ab|c def" to "abc| def".
979 for (int j = 1; j < num_of_character_moves; ++j) {
980 render_text->MoveCursorTo(start);
981 for (int k = 0; k < j; ++k)
982 render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false);
983 render_text->MoveCursor(WORD_BREAK, CURSOR_RIGHT, false);
984 EXPECT_EQ(end, render_text->selection_model());
985 }
986 }
987
988 // Test moving by word from right to left.
989 render_text->MoveCursor(LINE_BREAK, CURSOR_RIGHT, false);
990 first_word = true;
991 while (true) {
992 SelectionModel start = render_text->selection_model();
993 render_text->MoveCursor(WORD_BREAK, CURSOR_LEFT, false);
994 SelectionModel end = render_text->selection_model();
995 if (end == start) // reach the end.
996 break;
997
998 int num_of_character_moves = first_word ? 3 : 4;
999 first_word = false;
1000 render_text->MoveCursorTo(start);
1001 for (int j = 0; j < num_of_character_moves; ++j)
1002 render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, false);
1003 EXPECT_EQ(end, render_text->selection_model());
1004
1005 for (int j = 1; j < num_of_character_moves; ++j) {
1006 render_text->MoveCursorTo(start);
1007 for (int k = 0; k < j; ++k)
1008 render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, false);
1009 render_text->MoveCursor(WORD_BREAK, CURSOR_LEFT, false);
1010 EXPECT_EQ(end, render_text->selection_model());
1011 }
1012 }
1013 }
1014
TEST_F(RenderTextTest,MoveLeftRightByWordInBidiText)1015 TEST_F(RenderTextTest, MoveLeftRightByWordInBidiText) {
1016 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1017
1018 // For testing simplicity, each word is a 3-character word.
1019 std::vector<const wchar_t*> test;
1020 test.push_back(L"abc");
1021 test.push_back(L"abc def");
1022 test.push_back(L"\x05E1\x05E2\x05E3");
1023 test.push_back(L"\x05E1\x05E2\x05E3 \x05E4\x05E5\x05E6");
1024 test.push_back(L"abc \x05E1\x05E2\x05E3");
1025 test.push_back(L"abc def \x05E1\x05E2\x05E3 \x05E4\x05E5\x05E6");
1026 test.push_back(L"abc def hij \x05E1\x05E2\x05E3 \x05E4\x05E5\x05E6"
1027 L" \x05E7\x05E8\x05E9");
1028
1029 test.push_back(L"abc \x05E1\x05E2\x05E3 hij");
1030 test.push_back(L"abc def \x05E1\x05E2\x05E3 \x05E4\x05E5\x05E6 hij opq");
1031 test.push_back(L"abc def hij \x05E1\x05E2\x05E3 \x05E4\x05E5\x05E6"
1032 L" \x05E7\x05E8\x05E9" L" opq rst uvw");
1033
1034 test.push_back(L"\x05E1\x05E2\x05E3 abc");
1035 test.push_back(L"\x05E1\x05E2\x05E3 \x05E4\x05E5\x05E6 abc def");
1036 test.push_back(L"\x05E1\x05E2\x05E3 \x05E4\x05E5\x05E6 \x05E7\x05E8\x05E9"
1037 L" abc def hij");
1038
1039 test.push_back(L"\x05D1\x05D2\x05D3 abc \x05E1\x05E2\x05E3");
1040 test.push_back(L"\x05D1\x05D2\x05D3 \x05D4\x05D5\x05D6 abc def"
1041 L" \x05E1\x05E2\x05E3 \x05E4\x05E5\x05E6");
1042 test.push_back(L"\x05D1\x05D2\x05D3 \x05D4\x05D5\x05D6 \x05D7\x05D8\x05D9"
1043 L" abc def hij \x05E1\x05E2\x05E3 \x05E4\x05E5\x05E6"
1044 L" \x05E7\x05E8\x05E9");
1045
1046 for (size_t i = 0; i < test.size(); ++i)
1047 MoveLeftRightByWordVerifier(render_text.get(), test[i]);
1048 }
1049
TEST_F(RenderTextTest,MoveLeftRightByWordInBidiText_TestEndOfText)1050 TEST_F(RenderTextTest, MoveLeftRightByWordInBidiText_TestEndOfText) {
1051 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1052
1053 render_text->SetText(WideToUTF16(L"ab\x05E1"));
1054 // Moving the cursor by word from "abC|" to the left should return "|abC".
1055 // But since end of text is always treated as a word break, it returns
1056 // position "ab|C".
1057 // TODO(xji): Need to make it work as expected.
1058 render_text->MoveCursor(LINE_BREAK, CURSOR_RIGHT, false);
1059 render_text->MoveCursor(WORD_BREAK, CURSOR_LEFT, false);
1060 // EXPECT_EQ(SelectionModel(), render_text->selection_model());
1061
1062 // Moving the cursor by word from "|abC" to the right returns "abC|".
1063 render_text->MoveCursor(LINE_BREAK, CURSOR_LEFT, false);
1064 render_text->MoveCursor(WORD_BREAK, CURSOR_RIGHT, false);
1065 EXPECT_EQ(SelectionModel(3, CURSOR_FORWARD), render_text->selection_model());
1066
1067 render_text->SetText(WideToUTF16(L"\x05E1\x05E2" L"a"));
1068 // For logical text "BCa", moving the cursor by word from "aCB|" to the left
1069 // returns "|aCB".
1070 render_text->MoveCursor(LINE_BREAK, CURSOR_RIGHT, false);
1071 render_text->MoveCursor(WORD_BREAK, CURSOR_LEFT, false);
1072 EXPECT_EQ(SelectionModel(3, CURSOR_FORWARD), render_text->selection_model());
1073
1074 // Moving the cursor by word from "|aCB" to the right should return "aCB|".
1075 // But since end of text is always treated as a word break, it returns
1076 // position "a|CB".
1077 // TODO(xji): Need to make it work as expected.
1078 render_text->MoveCursor(LINE_BREAK, CURSOR_LEFT, false);
1079 render_text->MoveCursor(WORD_BREAK, CURSOR_RIGHT, false);
1080 // EXPECT_EQ(SelectionModel(), render_text->selection_model());
1081 }
1082
TEST_F(RenderTextTest,MoveLeftRightByWordInTextWithMultiSpaces)1083 TEST_F(RenderTextTest, MoveLeftRightByWordInTextWithMultiSpaces) {
1084 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1085 render_text->SetText(WideToUTF16(L"abc def"));
1086 render_text->MoveCursorTo(SelectionModel(5, CURSOR_FORWARD));
1087 render_text->MoveCursor(WORD_BREAK, CURSOR_RIGHT, false);
1088 EXPECT_EQ(11U, render_text->cursor_position());
1089
1090 render_text->MoveCursorTo(SelectionModel(5, CURSOR_FORWARD));
1091 render_text->MoveCursor(WORD_BREAK, CURSOR_LEFT, false);
1092 EXPECT_EQ(0U, render_text->cursor_position());
1093 }
1094
TEST_F(RenderTextTest,MoveLeftRightByWordInChineseText)1095 TEST_F(RenderTextTest, MoveLeftRightByWordInChineseText) {
1096 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1097 render_text->SetText(WideToUTF16(L"\x6211\x4EEC\x53BB\x516C\x56ED\x73A9"));
1098 render_text->MoveCursor(LINE_BREAK, CURSOR_LEFT, false);
1099 EXPECT_EQ(0U, render_text->cursor_position());
1100 render_text->MoveCursor(WORD_BREAK, CURSOR_RIGHT, false);
1101 EXPECT_EQ(2U, render_text->cursor_position());
1102 render_text->MoveCursor(WORD_BREAK, CURSOR_RIGHT, false);
1103 EXPECT_EQ(3U, render_text->cursor_position());
1104 render_text->MoveCursor(WORD_BREAK, CURSOR_RIGHT, false);
1105 EXPECT_EQ(5U, render_text->cursor_position());
1106 render_text->MoveCursor(WORD_BREAK, CURSOR_RIGHT, false);
1107 EXPECT_EQ(6U, render_text->cursor_position());
1108 render_text->MoveCursor(WORD_BREAK, CURSOR_RIGHT, false);
1109 EXPECT_EQ(6U, render_text->cursor_position());
1110 }
1111 #endif
1112
1113 #if defined(OS_WIN)
TEST_F(RenderTextTest,Win_LogicalClusters)1114 TEST_F(RenderTextTest, Win_LogicalClusters) {
1115 scoped_ptr<RenderTextWin> render_text(
1116 static_cast<RenderTextWin*>(RenderText::CreateInstance()));
1117
1118 const base::string16 test_string =
1119 WideToUTF16(L"\x0930\x0930\x0930\x0930\x0930");
1120 render_text->SetText(test_string);
1121 render_text->EnsureLayout();
1122 ASSERT_EQ(1U, render_text->runs_.size());
1123 WORD* logical_clusters = render_text->runs_[0]->logical_clusters.get();
1124 for (size_t i = 0; i < test_string.length(); ++i)
1125 EXPECT_EQ(i, logical_clusters[i]);
1126 }
1127 #endif // defined(OS_WIN)
1128
TEST_F(RenderTextTest,StringSizeSanity)1129 TEST_F(RenderTextTest, StringSizeSanity) {
1130 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1131 render_text->SetText(UTF8ToUTF16("Hello World"));
1132 const Size string_size = render_text->GetStringSize();
1133 EXPECT_GT(string_size.width(), 0);
1134 EXPECT_GT(string_size.height(), 0);
1135 }
1136
1137 // TODO(asvitkine): This test fails because PlatformFontMac uses point font
1138 // sizes instead of pixel sizes like other implementations.
1139 #if !defined(OS_MACOSX)
TEST_F(RenderTextTest,StringSizeEmptyString)1140 TEST_F(RenderTextTest, StringSizeEmptyString) {
1141 // Ascent and descent of Arial and Symbol are different on most platforms.
1142 const FontList font_list("Arial,Symbol, 16px");
1143 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1144 render_text->SetFontList(font_list);
1145 render_text->SetDisplayRect(Rect(0, 0, 0, font_list.GetHeight()));
1146
1147 // The empty string respects FontList metrics for non-zero height
1148 // and baseline.
1149 render_text->SetText(base::string16());
1150 EXPECT_EQ(font_list.GetHeight(), render_text->GetStringSize().height());
1151 EXPECT_EQ(0, render_text->GetStringSize().width());
1152 EXPECT_EQ(font_list.GetBaseline(), render_text->GetBaseline());
1153
1154 render_text->SetText(UTF8ToUTF16(" "));
1155 EXPECT_EQ(font_list.GetHeight(), render_text->GetStringSize().height());
1156 EXPECT_EQ(font_list.GetBaseline(), render_text->GetBaseline());
1157 }
1158 #endif // !defined(OS_MACOSX)
1159
TEST_F(RenderTextTest,StringSizeRespectsFontListMetrics)1160 TEST_F(RenderTextTest, StringSizeRespectsFontListMetrics) {
1161 // Check that Arial and Symbol have different font metrics.
1162 Font arial_font("Arial", 16);
1163 ASSERT_EQ("arial",
1164 StringToLowerASCII(arial_font.GetActualFontNameForTesting()));
1165 Font symbol_font("Symbol", 16);
1166 ASSERT_EQ("symbol",
1167 StringToLowerASCII(symbol_font.GetActualFontNameForTesting()));
1168 EXPECT_NE(arial_font.GetHeight(), symbol_font.GetHeight());
1169 EXPECT_NE(arial_font.GetBaseline(), symbol_font.GetBaseline());
1170 // "a" should be rendered with Arial, not with Symbol.
1171 const char* arial_font_text = "a";
1172 // "®" (registered trademark symbol) should be rendered with Symbol,
1173 // not with Arial.
1174 const char* symbol_font_text = "\xC2\xAE";
1175
1176 Font smaller_font = arial_font;
1177 Font larger_font = symbol_font;
1178 const char* smaller_font_text = arial_font_text;
1179 const char* larger_font_text = symbol_font_text;
1180 if (symbol_font.GetHeight() < arial_font.GetHeight() &&
1181 symbol_font.GetBaseline() < arial_font.GetBaseline()) {
1182 std::swap(smaller_font, larger_font);
1183 std::swap(smaller_font_text, larger_font_text);
1184 }
1185 ASSERT_LT(smaller_font.GetHeight(), larger_font.GetHeight());
1186 ASSERT_LT(smaller_font.GetBaseline(), larger_font.GetBaseline());
1187
1188 // Check |smaller_font_text| is rendered with the smaller font.
1189 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1190 render_text->SetText(UTF8ToUTF16(smaller_font_text));
1191 render_text->SetFontList(FontList(smaller_font));
1192 render_text->SetDisplayRect(Rect(0, 0, 0,
1193 render_text->font_list().GetHeight()));
1194 EXPECT_EQ(smaller_font.GetHeight(), render_text->GetStringSize().height());
1195 EXPECT_EQ(smaller_font.GetBaseline(), render_text->GetBaseline());
1196
1197 // Layout the same text with mixed fonts. The text should be rendered with
1198 // the smaller font, but the height and baseline are determined with the
1199 // metrics of the font list, which is equal to the larger font.
1200 std::vector<Font> fonts;
1201 fonts.push_back(smaller_font); // The primary font is the smaller font.
1202 fonts.push_back(larger_font);
1203 const FontList font_list(fonts);
1204 render_text->SetFontList(font_list);
1205 render_text->SetDisplayRect(Rect(0, 0, 0,
1206 render_text->font_list().GetHeight()));
1207 EXPECT_LT(smaller_font.GetHeight(), render_text->GetStringSize().height());
1208 EXPECT_LT(smaller_font.GetBaseline(), render_text->GetBaseline());
1209 EXPECT_EQ(font_list.GetHeight(), render_text->GetStringSize().height());
1210 EXPECT_EQ(font_list.GetBaseline(), render_text->GetBaseline());
1211 }
1212
TEST_F(RenderTextTest,SetFont)1213 TEST_F(RenderTextTest, SetFont) {
1214 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1215 render_text->SetFont(Font("Arial", 12));
1216 EXPECT_EQ("Arial", render_text->GetPrimaryFont().GetFontName());
1217 EXPECT_EQ(12, render_text->GetPrimaryFont().GetFontSize());
1218 }
1219
TEST_F(RenderTextTest,SetFontList)1220 TEST_F(RenderTextTest, SetFontList) {
1221 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1222 render_text->SetFontList(FontList("Arial,Symbol, 13px"));
1223 const std::vector<Font>& fonts = render_text->font_list().GetFonts();
1224 ASSERT_EQ(2U, fonts.size());
1225 EXPECT_EQ("Arial", fonts[0].GetFontName());
1226 EXPECT_EQ("Symbol", fonts[1].GetFontName());
1227 EXPECT_EQ(13, render_text->GetPrimaryFont().GetFontSize());
1228 }
1229
TEST_F(RenderTextTest,StringSizeBoldWidth)1230 TEST_F(RenderTextTest, StringSizeBoldWidth) {
1231 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1232 render_text->SetText(UTF8ToUTF16("Hello World"));
1233
1234 const int plain_width = render_text->GetStringSize().width();
1235 EXPECT_GT(plain_width, 0);
1236
1237 // Apply a bold style and check that the new width is greater.
1238 render_text->SetStyle(BOLD, true);
1239 const int bold_width = render_text->GetStringSize().width();
1240 EXPECT_GT(bold_width, plain_width);
1241
1242 // Now, apply a plain style over the first word only.
1243 render_text->ApplyStyle(BOLD, false, Range(0, 5));
1244 const int plain_bold_width = render_text->GetStringSize().width();
1245 EXPECT_GT(plain_bold_width, plain_width);
1246 EXPECT_LT(plain_bold_width, bold_width);
1247 }
1248
TEST_F(RenderTextTest,StringSizeHeight)1249 TEST_F(RenderTextTest, StringSizeHeight) {
1250 base::string16 cases[] = {
1251 WideToUTF16(L"Hello World!"), // English
1252 WideToUTF16(L"\x6328\x62f6"), // Japanese
1253 WideToUTF16(L"\x0915\x093f"), // Hindi
1254 WideToUTF16(L"\x05e0\x05b8"), // Hebrew
1255 };
1256
1257 Font default_font;
1258 Font larger_font = default_font.DeriveFont(24, default_font.GetStyle());
1259 EXPECT_GT(larger_font.GetHeight(), default_font.GetHeight());
1260
1261 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
1262 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1263 render_text->SetFont(default_font);
1264 render_text->SetText(cases[i]);
1265
1266 const int height1 = render_text->GetStringSize().height();
1267 EXPECT_GT(height1, 0);
1268
1269 // Check that setting the larger font increases the height.
1270 render_text->SetFont(larger_font);
1271 const int height2 = render_text->GetStringSize().height();
1272 EXPECT_GT(height2, height1);
1273 }
1274 }
1275
TEST_F(RenderTextTest,GetBaselineSanity)1276 TEST_F(RenderTextTest, GetBaselineSanity) {
1277 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1278 render_text->SetText(UTF8ToUTF16("Hello World"));
1279 const int baseline = render_text->GetBaseline();
1280 EXPECT_GT(baseline, 0);
1281 }
1282
TEST_F(RenderTextTest,CursorBoundsInReplacementMode)1283 TEST_F(RenderTextTest, CursorBoundsInReplacementMode) {
1284 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1285 render_text->SetText(ASCIIToUTF16("abcdefg"));
1286 render_text->SetDisplayRect(Rect(100, 17));
1287 SelectionModel sel_b(1, CURSOR_FORWARD);
1288 SelectionModel sel_c(2, CURSOR_FORWARD);
1289 Rect cursor_around_b = render_text->GetCursorBounds(sel_b, false);
1290 Rect cursor_before_b = render_text->GetCursorBounds(sel_b, true);
1291 Rect cursor_before_c = render_text->GetCursorBounds(sel_c, true);
1292 EXPECT_EQ(cursor_around_b.x(), cursor_before_b.x());
1293 EXPECT_EQ(cursor_around_b.right(), cursor_before_c.x());
1294 }
1295
TEST_F(RenderTextTest,GetTextOffset)1296 TEST_F(RenderTextTest, GetTextOffset) {
1297 // The default horizontal text offset differs for LTR and RTL, and is only set
1298 // when the RenderText object is created. This test will check the default in
1299 // LTR mode, and the next test will check the RTL default.
1300 const bool was_rtl = base::i18n::IsRTL();
1301 SetRTL(false);
1302 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1303 render_text->SetText(ASCIIToUTF16("abcdefg"));
1304 render_text->SetFontList(FontList("Arial, 13px"));
1305
1306 // Set display area's size equal to the font size.
1307 const Size font_size(render_text->GetContentWidth(),
1308 render_text->font_list().GetHeight());
1309 Rect display_rect(font_size);
1310 render_text->SetDisplayRect(display_rect);
1311
1312 Vector2d offset = render_text->GetLineOffset(0);
1313 EXPECT_TRUE(offset.IsZero());
1314
1315 const int kEnlargementX = 2;
1316 display_rect.Inset(0, 0, -kEnlargementX, 0);
1317 render_text->SetDisplayRect(display_rect);
1318
1319 // Check the default horizontal alignment.
1320 offset = render_text->GetLineOffset(0);
1321 EXPECT_EQ(0, offset.x());
1322
1323 // Check explicitly setting the horizontal alignment.
1324 render_text->SetHorizontalAlignment(ALIGN_LEFT);
1325 offset = render_text->GetLineOffset(0);
1326 EXPECT_EQ(0, offset.x());
1327 render_text->SetHorizontalAlignment(ALIGN_CENTER);
1328 offset = render_text->GetLineOffset(0);
1329 EXPECT_EQ(kEnlargementX / 2, offset.x());
1330 render_text->SetHorizontalAlignment(ALIGN_RIGHT);
1331 offset = render_text->GetLineOffset(0);
1332 EXPECT_EQ(kEnlargementX, offset.x());
1333
1334 // Check that text is vertically centered within taller display rects.
1335 const int kEnlargementY = display_rect.height();
1336 display_rect.Inset(0, 0, 0, -kEnlargementY);
1337 render_text->SetDisplayRect(display_rect);
1338 const Vector2d prev_offset = render_text->GetLineOffset(0);
1339 display_rect.Inset(0, 0, 0, -2 * kEnlargementY);
1340 render_text->SetDisplayRect(display_rect);
1341 offset = render_text->GetLineOffset(0);
1342 EXPECT_EQ(prev_offset.y() + kEnlargementY, offset.y());
1343
1344 SetRTL(was_rtl);
1345 }
1346
TEST_F(RenderTextTest,GetTextOffsetHorizontalDefaultInRTL)1347 TEST_F(RenderTextTest, GetTextOffsetHorizontalDefaultInRTL) {
1348 // This only checks the default horizontal alignment in RTL mode; all other
1349 // GetLineOffset(0) attributes are checked by the test above.
1350 const bool was_rtl = base::i18n::IsRTL();
1351 SetRTL(true);
1352 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1353 render_text->SetText(ASCIIToUTF16("abcdefg"));
1354 render_text->SetFontList(FontList("Arial, 13px"));
1355 const int kEnlargement = 2;
1356 const Size font_size(render_text->GetContentWidth() + kEnlargement,
1357 render_text->GetStringSize().height());
1358 Rect display_rect(font_size);
1359 render_text->SetDisplayRect(display_rect);
1360 Vector2d offset = render_text->GetLineOffset(0);
1361 EXPECT_EQ(kEnlargement, offset.x());
1362 SetRTL(was_rtl);
1363 }
1364
TEST_F(RenderTextTest,SameFontForParentheses)1365 TEST_F(RenderTextTest, SameFontForParentheses) {
1366 struct {
1367 const char16 left_char;
1368 const char16 right_char;
1369 } punctuation_pairs[] = {
1370 { '(', ')' },
1371 { '{', '}' },
1372 { '<', '>' },
1373 };
1374 struct {
1375 base::string16 text;
1376 } cases[] = {
1377 // English(English)
1378 { WideToUTF16(L"Hello World(a)") },
1379 // English(English)English
1380 { WideToUTF16(L"Hello World(a)Hello World") },
1381
1382 // Japanese(English)
1383 { WideToUTF16(L"\x6328\x62f6(a)") },
1384 // Japanese(English)Japanese
1385 { WideToUTF16(L"\x6328\x62f6(a)\x6328\x62f6") },
1386 // English(Japanese)English
1387 { WideToUTF16(L"Hello World(\x6328\x62f6)Hello World") },
1388
1389 // Hindi(English)
1390 { WideToUTF16(L"\x0915\x093f(a)") },
1391 // Hindi(English)Hindi
1392 { WideToUTF16(L"\x0915\x093f(a)\x0915\x093f") },
1393 // English(Hindi)English
1394 { WideToUTF16(L"Hello World(\x0915\x093f)Hello World") },
1395
1396 // Hebrew(English)
1397 { WideToUTF16(L"\x05e0\x05b8(a)") },
1398 // Hebrew(English)Hebrew
1399 { WideToUTF16(L"\x05e0\x05b8(a)\x05e0\x05b8") },
1400 // English(Hebrew)English
1401 { WideToUTF16(L"Hello World(\x05e0\x05b8)Hello World") },
1402 };
1403
1404 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1405 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
1406 base::string16 text = cases[i].text;
1407 const size_t start_paren_char_index = text.find('(');
1408 ASSERT_NE(base::string16::npos, start_paren_char_index);
1409 const size_t end_paren_char_index = text.find(')');
1410 ASSERT_NE(base::string16::npos, end_paren_char_index);
1411
1412 for (size_t j = 0; j < ARRAYSIZE_UNSAFE(punctuation_pairs); ++j) {
1413 text[start_paren_char_index] = punctuation_pairs[j].left_char;
1414 text[end_paren_char_index] = punctuation_pairs[j].right_char;
1415 render_text->SetText(text);
1416
1417 const std::vector<RenderText::FontSpan> spans =
1418 render_text->GetFontSpansForTesting();
1419
1420 int start_paren_span_index = -1;
1421 int end_paren_span_index = -1;
1422 for (size_t k = 0; k < spans.size(); ++k) {
1423 if (IndexInRange(spans[k].second, start_paren_char_index))
1424 start_paren_span_index = k;
1425 if (IndexInRange(spans[k].second, end_paren_char_index))
1426 end_paren_span_index = k;
1427 }
1428 ASSERT_NE(-1, start_paren_span_index);
1429 ASSERT_NE(-1, end_paren_span_index);
1430
1431 const Font& start_font = spans[start_paren_span_index].first;
1432 const Font& end_font = spans[end_paren_span_index].first;
1433 EXPECT_EQ(start_font.GetFontName(), end_font.GetFontName());
1434 EXPECT_EQ(start_font.GetFontSize(), end_font.GetFontSize());
1435 EXPECT_EQ(start_font.GetStyle(), end_font.GetStyle());
1436 }
1437 }
1438 }
1439
1440 // Make sure the caret width is always >=1 so that the correct
1441 // caret is drawn at high DPI. crbug.com/164100.
TEST_F(RenderTextTest,CaretWidth)1442 TEST_F(RenderTextTest, CaretWidth) {
1443 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1444 render_text->SetText(ASCIIToUTF16("abcdefg"));
1445 EXPECT_GE(render_text->GetUpdatedCursorBounds().width(), 1);
1446 }
1447
TEST_F(RenderTextTest,SelectWord)1448 TEST_F(RenderTextTest, SelectWord) {
1449 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1450 render_text->SetText(ASCIIToUTF16(" foo a.bc.d bar"));
1451
1452 struct {
1453 size_t cursor;
1454 size_t selection_start;
1455 size_t selection_end;
1456 } cases[] = {
1457 { 0, 0, 1 },
1458 { 1, 1, 4 },
1459 { 2, 1, 4 },
1460 { 3, 1, 4 },
1461 { 4, 4, 6 },
1462 { 5, 4, 6 },
1463 { 6, 6, 7 },
1464 { 7, 7, 8 },
1465 { 8, 8, 10 },
1466 { 9, 8, 10 },
1467 { 10, 10, 11 },
1468 { 11, 11, 12 },
1469 { 12, 12, 13 },
1470 { 13, 13, 16 },
1471 { 14, 13, 16 },
1472 { 15, 13, 16 },
1473 { 16, 13, 16 },
1474 };
1475
1476 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
1477 render_text->SetCursorPosition(cases[i].cursor);
1478 render_text->SelectWord();
1479 EXPECT_EQ(Range(cases[i].selection_start, cases[i].selection_end),
1480 render_text->selection());
1481 }
1482 }
1483
1484 // Make sure the last word is selected when the cursor is at text.length().
TEST_F(RenderTextTest,LastWordSelected)1485 TEST_F(RenderTextTest, LastWordSelected) {
1486 const std::string kTestURL1 = "http://www.google.com";
1487 const std::string kTestURL2 = "http://www.google.com/something/";
1488
1489 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1490
1491 render_text->SetText(ASCIIToUTF16(kTestURL1));
1492 render_text->SetCursorPosition(kTestURL1.length());
1493 render_text->SelectWord();
1494 EXPECT_EQ(ASCIIToUTF16("com"), GetSelectedText(render_text.get()));
1495 EXPECT_FALSE(render_text->selection().is_reversed());
1496
1497 render_text->SetText(ASCIIToUTF16(kTestURL2));
1498 render_text->SetCursorPosition(kTestURL2.length());
1499 render_text->SelectWord();
1500 EXPECT_EQ(ASCIIToUTF16("/"), GetSelectedText(render_text.get()));
1501 EXPECT_FALSE(render_text->selection().is_reversed());
1502 }
1503
1504 // When given a non-empty selection, SelectWord should expand the selection to
1505 // nearest word boundaries.
TEST_F(RenderTextTest,SelectMultipleWords)1506 TEST_F(RenderTextTest, SelectMultipleWords) {
1507 const std::string kTestURL = "http://www.google.com";
1508
1509 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1510
1511 render_text->SetText(ASCIIToUTF16(kTestURL));
1512 render_text->SelectRange(Range(16, 20));
1513 render_text->SelectWord();
1514 EXPECT_EQ(ASCIIToUTF16("google.com"), GetSelectedText(render_text.get()));
1515 EXPECT_FALSE(render_text->selection().is_reversed());
1516
1517 // SelectWord should preserve the selection direction.
1518 render_text->SelectRange(Range(20, 16));
1519 render_text->SelectWord();
1520 EXPECT_EQ(ASCIIToUTF16("google.com"), GetSelectedText(render_text.get()));
1521 EXPECT_TRUE(render_text->selection().is_reversed());
1522 }
1523
1524 // TODO(asvitkine): Cursor movements tests disabled on Mac because RenderTextMac
1525 // does not implement this yet. http://crbug.com/131618
1526 #if !defined(OS_MACOSX)
TEST_F(RenderTextTest,DisplayRectShowsCursorLTR)1527 TEST_F(RenderTextTest, DisplayRectShowsCursorLTR) {
1528 ASSERT_FALSE(base::i18n::IsRTL());
1529 ASSERT_FALSE(base::i18n::ICUIsRTL());
1530
1531 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1532 render_text->SetText(WideToUTF16(L"abcdefghijklmnopqrstuvwxzyabcdefg"));
1533 render_text->MoveCursorTo(SelectionModel(render_text->text().length(),
1534 CURSOR_FORWARD));
1535 int width = render_text->GetStringSize().width();
1536 ASSERT_GT(width, 10);
1537
1538 // Ensure that the cursor is placed at the width of its preceding text.
1539 render_text->SetDisplayRect(Rect(width + 10, 1));
1540 EXPECT_EQ(width, render_text->GetUpdatedCursorBounds().x());
1541
1542 // Ensure that shrinking the display rectangle keeps the cursor in view.
1543 render_text->SetDisplayRect(Rect(width - 10, 1));
1544 EXPECT_EQ(render_text->display_rect().width(),
1545 render_text->GetUpdatedCursorBounds().right());
1546
1547 // Ensure that the text will pan to fill its expanding display rectangle.
1548 render_text->SetDisplayRect(Rect(width - 5, 1));
1549 EXPECT_EQ(render_text->display_rect().width(),
1550 render_text->GetUpdatedCursorBounds().right());
1551
1552 // Ensure that a sufficiently large display rectangle shows all the text.
1553 render_text->SetDisplayRect(Rect(width + 10, 1));
1554 EXPECT_EQ(width, render_text->GetUpdatedCursorBounds().x());
1555
1556 // Repeat the test with RTL text.
1557 render_text->SetText(WideToUTF16(L"\x5d0\x5d1\x5d2\x5d3\x5d4\x5d5\x5d6\x5d7"
1558 L"\x5d8\x5d9\x5da\x5db\x5dc\x5dd\x5de\x5df"));
1559 render_text->MoveCursorTo(SelectionModel(0, CURSOR_FORWARD));
1560 width = render_text->GetStringSize().width();
1561 ASSERT_GT(width, 10);
1562
1563 // Ensure that the cursor is placed at the width of its preceding text.
1564 render_text->SetDisplayRect(Rect(width + 10, 1));
1565 EXPECT_EQ(width, render_text->GetUpdatedCursorBounds().x());
1566
1567 // Ensure that shrinking the display rectangle keeps the cursor in view.
1568 render_text->SetDisplayRect(Rect(width - 10, 1));
1569 EXPECT_EQ(render_text->display_rect().width(),
1570 render_text->GetUpdatedCursorBounds().right());
1571
1572 // Ensure that the text will pan to fill its expanding display rectangle.
1573 render_text->SetDisplayRect(Rect(width - 5, 1));
1574 EXPECT_EQ(render_text->display_rect().width(),
1575 render_text->GetUpdatedCursorBounds().right());
1576
1577 // Ensure that a sufficiently large display rectangle shows all the text.
1578 render_text->SetDisplayRect(Rect(width + 10, 1));
1579 EXPECT_EQ(width, render_text->GetUpdatedCursorBounds().x());
1580 }
1581
TEST_F(RenderTextTest,DisplayRectShowsCursorRTL)1582 TEST_F(RenderTextTest, DisplayRectShowsCursorRTL) {
1583 // Set the application default text direction to RTL.
1584 const bool was_rtl = base::i18n::IsRTL();
1585 SetRTL(true);
1586
1587 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1588 render_text->SetText(WideToUTF16(L"abcdefghijklmnopqrstuvwxzyabcdefg"));
1589 render_text->MoveCursorTo(SelectionModel(0, CURSOR_FORWARD));
1590 int width = render_text->GetStringSize().width();
1591 ASSERT_GT(width, 10);
1592
1593 // Ensure that the cursor is placed at the width of its preceding text.
1594 render_text->SetDisplayRect(Rect(width + 10, 1));
1595 EXPECT_EQ(render_text->display_rect().width() - width - 1,
1596 render_text->GetUpdatedCursorBounds().x());
1597
1598 // Ensure that shrinking the display rectangle keeps the cursor in view.
1599 render_text->SetDisplayRect(Rect(width - 10, 1));
1600 EXPECT_EQ(0, render_text->GetUpdatedCursorBounds().x());
1601
1602 // Ensure that the text will pan to fill its expanding display rectangle.
1603 render_text->SetDisplayRect(Rect(width - 5, 1));
1604 EXPECT_EQ(0, render_text->GetUpdatedCursorBounds().x());
1605
1606 // Ensure that a sufficiently large display rectangle shows all the text.
1607 render_text->SetDisplayRect(Rect(width + 10, 1));
1608 EXPECT_EQ(render_text->display_rect().width() - width - 1,
1609 render_text->GetUpdatedCursorBounds().x());
1610
1611 // Repeat the test with RTL text.
1612 render_text->SetText(WideToUTF16(L"\x5d0\x5d1\x5d2\x5d3\x5d4\x5d5\x5d6\x5d7"
1613 L"\x5d8\x5d9\x5da\x5db\x5dc\x5dd\x5de\x5df"));
1614 render_text->MoveCursorTo(SelectionModel(render_text->text().length(),
1615 CURSOR_FORWARD));
1616 width = render_text->GetStringSize().width();
1617 ASSERT_GT(width, 10);
1618
1619 // Ensure that the cursor is placed at the width of its preceding text.
1620 render_text->SetDisplayRect(Rect(width + 10, 1));
1621 EXPECT_EQ(render_text->display_rect().width() - width - 1,
1622 render_text->GetUpdatedCursorBounds().x());
1623
1624 // Ensure that shrinking the display rectangle keeps the cursor in view.
1625 render_text->SetDisplayRect(Rect(width - 10, 1));
1626 EXPECT_EQ(0, render_text->GetUpdatedCursorBounds().x());
1627
1628 // Ensure that the text will pan to fill its expanding display rectangle.
1629 render_text->SetDisplayRect(Rect(width - 5, 1));
1630 EXPECT_EQ(0, render_text->GetUpdatedCursorBounds().x());
1631
1632 // Ensure that a sufficiently large display rectangle shows all the text.
1633 render_text->SetDisplayRect(Rect(width + 10, 1));
1634 EXPECT_EQ(render_text->display_rect().width() - width - 1,
1635 render_text->GetUpdatedCursorBounds().x());
1636
1637 // Reset the application default text direction to LTR.
1638 SetRTL(was_rtl);
1639 EXPECT_EQ(was_rtl, base::i18n::IsRTL());
1640 }
1641 #endif // !defined(OS_MACOSX)
1642
1643 // Changing colors between or inside ligated glyphs should not break shaping.
TEST_F(RenderTextTest,SelectionKeepsLigatures)1644 TEST_F(RenderTextTest, SelectionKeepsLigatures) {
1645 const wchar_t* kTestStrings[] = {
1646 L"\x644\x623",
1647 L"\x633\x627"
1648 };
1649
1650 scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1651 render_text->set_selection_color(SK_ColorRED);
1652 Canvas canvas;
1653
1654 for (size_t i = 0; i < arraysize(kTestStrings); ++i) {
1655 render_text->SetText(WideToUTF16(kTestStrings[i]));
1656 const int expected_width = render_text->GetStringSize().width();
1657 render_text->MoveCursorTo(SelectionModel(Range(0, 1), CURSOR_FORWARD));
1658 EXPECT_EQ(expected_width, render_text->GetStringSize().width());
1659 // Draw the text. It shouldn't hit any DCHECKs or crash.
1660 // See http://crbug.com/214150
1661 render_text->Draw(&canvas);
1662 render_text->MoveCursorTo(SelectionModel(0, CURSOR_FORWARD));
1663 }
1664 }
1665
1666 #if defined(OS_WIN)
1667 // Ensure strings wrap onto multiple lines for a small available width.
TEST_F(RenderTextTest,Multiline_MinWidth)1668 TEST_F(RenderTextTest, Multiline_MinWidth) {
1669 const wchar_t* kTestStrings[] = { kWeak, kLtr, kLtrRtl, kLtrRtlLtr, kRtl,
1670 kRtlLtr, kRtlLtrRtl };
1671
1672 scoped_ptr<RenderTextWin> render_text(
1673 static_cast<RenderTextWin*>(RenderText::CreateInstance()));
1674 render_text->SetDisplayRect(Rect(1, 1000));
1675 render_text->SetMultiline(true);
1676 Canvas canvas;
1677
1678 for (size_t i = 0; i < arraysize(kTestStrings); ++i) {
1679 SCOPED_TRACE(base::StringPrintf("kTestStrings[%" PRIuS "]", i));
1680 render_text->SetText(WideToUTF16(kTestStrings[i]));
1681 render_text->Draw(&canvas);
1682 EXPECT_GT(render_text->lines_.size(), 1U);
1683 }
1684 }
1685
1686 // Ensure strings wrap onto multiple lines for a normal available width.
TEST_F(RenderTextTest,Multiline_NormalWidth)1687 TEST_F(RenderTextTest, Multiline_NormalWidth) {
1688 const struct {
1689 const wchar_t* const text;
1690 const Range first_line_char_range;
1691 const Range second_line_char_range;
1692 } kTestStrings[] = {
1693 { L"abc defg hijkl", Range(0, 9), Range(9, 14) },
1694 { L"qwertyzxcvbn", Range(0, 8), Range(8, 12) },
1695 { L"\x062A\x0641\x0627\x062D\x05EA\x05E4\x05D5\x05D6\x05D9\x05DD",
1696 Range(4, 10), Range(0, 4) }
1697 };
1698
1699 scoped_ptr<RenderTextWin> render_text(
1700 static_cast<RenderTextWin*>(RenderText::CreateInstance()));
1701 render_text->SetDisplayRect(Rect(50, 1000));
1702 render_text->SetMultiline(true);
1703 Canvas canvas;
1704
1705 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestStrings); ++i) {
1706 SCOPED_TRACE(base::StringPrintf("kTestStrings[%" PRIuS "]", i));
1707 render_text->SetText(WideToUTF16(kTestStrings[i].text));
1708 render_text->Draw(&canvas);
1709 ASSERT_EQ(2U, render_text->lines_.size());
1710 ASSERT_EQ(1U, render_text->lines_[0].segments.size());
1711 EXPECT_EQ(kTestStrings[i].first_line_char_range,
1712 render_text->lines_[0].segments[0].char_range);
1713 ASSERT_EQ(1U, render_text->lines_[1].segments.size());
1714 EXPECT_EQ(kTestStrings[i].second_line_char_range,
1715 render_text->lines_[1].segments[0].char_range);
1716 }
1717 }
1718
1719 // Ensure strings don't wrap onto multiple lines for a sufficient available
1720 // width.
TEST_F(RenderTextTest,Multiline_SufficientWidth)1721 TEST_F(RenderTextTest, Multiline_SufficientWidth) {
1722 const wchar_t* kTestStrings[] = { L"", L" ", L".", L" . ", L"abc", L"a b c",
1723 L"\x62E\x628\x632", L"\x62E \x628 \x632" };
1724
1725 scoped_ptr<RenderTextWin> render_text(
1726 static_cast<RenderTextWin*>(RenderText::CreateInstance()));
1727 render_text->SetDisplayRect(Rect(30, 1000));
1728 render_text->SetMultiline(true);
1729 Canvas canvas;
1730
1731 for (size_t i = 0; i < arraysize(kTestStrings); ++i) {
1732 SCOPED_TRACE(base::StringPrintf("kTestStrings[%" PRIuS "]", i));
1733 render_text->SetText(WideToUTF16(kTestStrings[i]));
1734 render_text->Draw(&canvas);
1735 EXPECT_EQ(1U, render_text->lines_.size());
1736 }
1737 }
1738
TEST_F(RenderTextTest,Multiline_Newline)1739 TEST_F(RenderTextTest, Multiline_Newline) {
1740 const struct {
1741 const wchar_t* const text;
1742 // Ranges of the characters on each line preceding the newline.
1743 const Range first_line_char_range;
1744 const Range second_line_char_range;
1745 } kTestStrings[] = {
1746 { L"abc\ndef", Range(0, 3), Range(4, 7) },
1747 { L"a \n b ", Range(0, 2), Range(3, 6) },
1748 { L"\n" , Range::InvalidRange(), Range::InvalidRange() }
1749 };
1750
1751 scoped_ptr<RenderTextWin> render_text(
1752 static_cast<RenderTextWin*>(RenderText::CreateInstance()));
1753 render_text->SetDisplayRect(Rect(200, 1000));
1754 render_text->SetMultiline(true);
1755 Canvas canvas;
1756
1757 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestStrings); ++i) {
1758 SCOPED_TRACE(base::StringPrintf("kTestStrings[%" PRIuS "]", i));
1759 render_text->SetText(WideToUTF16(kTestStrings[i].text));
1760 render_text->Draw(&canvas);
1761
1762 ASSERT_EQ(2U, render_text->lines_.size());
1763
1764 const Range first_expected_range = kTestStrings[i].first_line_char_range;
1765 ASSERT_EQ(first_expected_range.IsValid() ? 2U : 1U,
1766 render_text->lines_[0].segments.size());
1767 if (first_expected_range.IsValid())
1768 EXPECT_EQ(first_expected_range,
1769 render_text->lines_[0].segments[0].char_range);
1770
1771 const internal::LineSegment& newline_segment =
1772 render_text->lines_[0].segments[first_expected_range.IsValid() ? 1 : 0];
1773 ASSERT_EQ(1U, newline_segment.char_range.length());
1774 EXPECT_EQ(L'\n', kTestStrings[i].text[newline_segment.char_range.start()]);
1775
1776 const Range second_expected_range = kTestStrings[i].second_line_char_range;
1777 ASSERT_EQ(second_expected_range.IsValid() ? 1U : 0U,
1778 render_text->lines_[1].segments.size());
1779 if (second_expected_range.IsValid())
1780 EXPECT_EQ(second_expected_range,
1781 render_text->lines_[1].segments[0].char_range);
1782 }
1783 }
1784
1785
TEST_F(RenderTextTest,Win_BreakRunsByUnicodeBlocks)1786 TEST_F(RenderTextTest, Win_BreakRunsByUnicodeBlocks) {
1787 scoped_ptr<RenderTextWin> render_text(
1788 static_cast<RenderTextWin*>(RenderText::CreateInstance()));
1789
1790 // The '\x25B6' "play character" should break runs. http://crbug.com/278913
1791 render_text->SetText(WideToUTF16(L"x\x25B6y"));
1792 render_text->EnsureLayout();
1793 ASSERT_EQ(3U, render_text->runs_.size());
1794 EXPECT_EQ(Range(0, 1), render_text->runs_[0]->range);
1795 EXPECT_EQ(Range(1, 2), render_text->runs_[1]->range);
1796 EXPECT_EQ(Range(2, 3), render_text->runs_[2]->range);
1797
1798 render_text->SetText(WideToUTF16(L"x \x25B6 y"));
1799 render_text->EnsureLayout();
1800 ASSERT_EQ(3U, render_text->runs_.size());
1801 EXPECT_EQ(Range(0, 2), render_text->runs_[0]->range);
1802 EXPECT_EQ(Range(2, 3), render_text->runs_[1]->range);
1803 EXPECT_EQ(Range(3, 5), render_text->runs_[2]->range);
1804 }
1805 #endif // defined(OS_WIN)
1806
1807 } // namespace gfx
1808