• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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