• 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/i18n/break_iterator.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16 #include "ui/gfx/break_list.h"
17 #include "ui/gfx/canvas.h"
18 #include "ui/gfx/render_text_harfbuzz.h"
19 
20 #if defined(OS_WIN)
21 #include "base/win/windows_version.h"
22 #include "ui/gfx/render_text_win.h"
23 #endif
24 
25 #if defined(OS_LINUX) && !defined(USE_OZONE)
26 #include "ui/gfx/render_text_pango.h"
27 #endif
28 
29 using base::ASCIIToUTF16;
30 using base::UTF8ToUTF16;
31 using base::WideToUTF16;
32 
33 namespace gfx {
34 
35 namespace {
36 
37 // Various weak, LTR, RTL, and Bidi string cases with three characters each.
38 const wchar_t kWeak[] =      L" . ";
39 const wchar_t kLtr[] =       L"abc";
40 const wchar_t kRtl[] =       L"\x5d0\x5d1\x5d2";
41 #if !defined(OS_MACOSX)
42 const wchar_t kLtrRtl[] =    L"a" L"\x5d0\x5d1";
43 const wchar_t kLtrRtlLtr[] = L"a" L"\x5d1" L"b";
44 const wchar_t kRtlLtr[] =    L"\x5d0\x5d1" L"a";
45 const wchar_t kRtlLtrRtl[] = L"\x5d0" L"a" L"\x5d1";
46 #endif
47 
48 // Checks whether |range| contains |index|. This is not the same as calling
49 // range.Contains(Range(index)), which returns true if |index| == |range.end()|.
IndexInRange(const Range & range,size_t index)50 bool IndexInRange(const Range& range, size_t index) {
51   return index >= range.start() && index < range.end();
52 }
53 
GetSelectedText(RenderText * render_text)54 base::string16 GetSelectedText(RenderText* render_text) {
55   return render_text->text().substr(render_text->selection().GetMin(),
56                                     render_text->selection().length());
57 }
58 
59 // A test utility function to set the application default text direction.
SetRTL(bool rtl)60 void SetRTL(bool rtl) {
61   // Override the current locale/direction.
62   base::i18n::SetICUDefaultLocale(rtl ? "he" : "en");
63   EXPECT_EQ(rtl, base::i18n::IsRTL());
64 }
65 
66 #if !defined(OS_MACOSX)
67 // Ensure cursor movement in the specified |direction| yields |expected| values.
RunMoveCursorLeftRightTest(RenderText * render_text,const std::vector<SelectionModel> & expected,VisualCursorDirection direction)68 void RunMoveCursorLeftRightTest(RenderText* render_text,
69                                 const std::vector<SelectionModel>& expected,
70                                 VisualCursorDirection direction) {
71   for (size_t i = 0; i < expected.size(); ++i) {
72     SCOPED_TRACE(base::StringPrintf("Going %s; expected value index %d.",
73         direction == CURSOR_LEFT ? "left" : "right", static_cast<int>(i)));
74     EXPECT_EQ(expected[i], render_text->selection_model());
75     render_text->MoveCursor(CHARACTER_BREAK, direction, false);
76   }
77   // Check that cursoring is clamped at the line edge.
78   EXPECT_EQ(expected.back(), render_text->selection_model());
79   // Check that it is the line edge.
80   render_text->MoveCursor(LINE_BREAK, direction, false);
81   EXPECT_EQ(expected.back(), render_text->selection_model());
82 }
83 #endif  // !defined(OS_MACOSX)
84 
85 }  // namespace
86 
87 class RenderTextTest : public testing::Test {
88 };
89 
TEST_F(RenderTextTest,DefaultStyle)90 TEST_F(RenderTextTest, DefaultStyle) {
91   // Check the default styles applied to new instances and adjusted text.
92   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
93   EXPECT_TRUE(render_text->text().empty());
94   const wchar_t* const cases[] = { kWeak, kLtr, L"Hello", kRtl, L"", L"" };
95   for (size_t i = 0; i < arraysize(cases); ++i) {
96     EXPECT_TRUE(render_text->colors().EqualsValueForTesting(SK_ColorBLACK));
97     for (size_t style = 0; style < NUM_TEXT_STYLES; ++style)
98       EXPECT_TRUE(render_text->styles()[style].EqualsValueForTesting(false));
99     render_text->SetText(WideToUTF16(cases[i]));
100   }
101 }
102 
TEST_F(RenderTextTest,SetColorAndStyle)103 TEST_F(RenderTextTest, SetColorAndStyle) {
104   // Ensure custom default styles persist across setting and clearing text.
105   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
106   const SkColor color = SK_ColorRED;
107   render_text->SetColor(color);
108   render_text->SetStyle(BOLD, true);
109   render_text->SetStyle(UNDERLINE, false);
110   const wchar_t* const cases[] = { kWeak, kLtr, L"Hello", kRtl, L"", L"" };
111   for (size_t i = 0; i < arraysize(cases); ++i) {
112     EXPECT_TRUE(render_text->colors().EqualsValueForTesting(color));
113     EXPECT_TRUE(render_text->styles()[BOLD].EqualsValueForTesting(true));
114     EXPECT_TRUE(render_text->styles()[UNDERLINE].EqualsValueForTesting(false));
115     render_text->SetText(WideToUTF16(cases[i]));
116 
117     // Ensure custom default styles can be applied after text has been set.
118     if (i == 1)
119       render_text->SetStyle(STRIKE, true);
120     if (i >= 1)
121       EXPECT_TRUE(render_text->styles()[STRIKE].EqualsValueForTesting(true));
122   }
123 }
124 
TEST_F(RenderTextTest,ApplyColorAndStyle)125 TEST_F(RenderTextTest, ApplyColorAndStyle) {
126   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
127   render_text->SetText(ASCIIToUTF16("012345678"));
128 
129   // Apply a ranged color and style and check the resulting breaks.
130   render_text->ApplyColor(SK_ColorRED, Range(1, 4));
131   render_text->ApplyStyle(BOLD, true, Range(2, 5));
132   std::vector<std::pair<size_t, SkColor> > expected_color;
133   expected_color.push_back(std::pair<size_t, SkColor>(0, SK_ColorBLACK));
134   expected_color.push_back(std::pair<size_t, SkColor>(1, SK_ColorRED));
135   expected_color.push_back(std::pair<size_t, SkColor>(4, SK_ColorBLACK));
136   EXPECT_TRUE(render_text->colors().EqualsForTesting(expected_color));
137   std::vector<std::pair<size_t, bool> > expected_style;
138   expected_style.push_back(std::pair<size_t, bool>(0, false));
139   expected_style.push_back(std::pair<size_t, bool>(2, true));
140   expected_style.push_back(std::pair<size_t, bool>(5, false));
141   EXPECT_TRUE(render_text->styles()[BOLD].EqualsForTesting(expected_style));
142 
143   // Ensure setting a color and style overrides the ranged colors and styles.
144   render_text->SetColor(SK_ColorBLUE);
145   EXPECT_TRUE(render_text->colors().EqualsValueForTesting(SK_ColorBLUE));
146   render_text->SetStyle(BOLD, false);
147   EXPECT_TRUE(render_text->styles()[BOLD].EqualsValueForTesting(false));
148 
149   // Apply a color and style over the text end and check the resulting breaks.
150   // (INT_MAX should be used instead of the text length for the range end)
151   const size_t text_length = render_text->text().length();
152   render_text->ApplyColor(SK_ColorRED, Range(0, text_length));
153   render_text->ApplyStyle(BOLD, true, Range(2, text_length));
154   std::vector<std::pair<size_t, SkColor> > expected_color_end;
155   expected_color_end.push_back(std::pair<size_t, SkColor>(0, SK_ColorRED));
156   EXPECT_TRUE(render_text->colors().EqualsForTesting(expected_color_end));
157   std::vector<std::pair<size_t, bool> > expected_style_end;
158   expected_style_end.push_back(std::pair<size_t, bool>(0, false));
159   expected_style_end.push_back(std::pair<size_t, bool>(2, true));
160   EXPECT_TRUE(render_text->styles()[BOLD].EqualsForTesting(expected_style_end));
161 
162   // Ensure ranged values adjust to accommodate text length changes.
163   render_text->ApplyStyle(ITALIC, true, Range(0, 2));
164   render_text->ApplyStyle(ITALIC, true, Range(3, 6));
165   render_text->ApplyStyle(ITALIC, true, Range(7, text_length));
166   std::vector<std::pair<size_t, bool> > expected_italic;
167   expected_italic.push_back(std::pair<size_t, bool>(0, true));
168   expected_italic.push_back(std::pair<size_t, bool>(2, false));
169   expected_italic.push_back(std::pair<size_t, bool>(3, true));
170   expected_italic.push_back(std::pair<size_t, bool>(6, false));
171   expected_italic.push_back(std::pair<size_t, bool>(7, true));
172   EXPECT_TRUE(render_text->styles()[ITALIC].EqualsForTesting(expected_italic));
173 
174   // Changing the text should clear any breaks except for the first one.
175   render_text->SetText(ASCIIToUTF16("0123456"));
176   expected_italic.resize(1);
177   EXPECT_TRUE(render_text->styles()[ITALIC].EqualsForTesting(expected_italic));
178   render_text->ApplyStyle(ITALIC, false, Range(2, 4));
179   render_text->SetText(ASCIIToUTF16("012345678"));
180   EXPECT_TRUE(render_text->styles()[ITALIC].EqualsForTesting(expected_italic));
181   render_text->ApplyStyle(ITALIC, false, Range(0, 1));
182   render_text->SetText(ASCIIToUTF16("0123456"));
183   expected_italic.begin()->second = false;
184   EXPECT_TRUE(render_text->styles()[ITALIC].EqualsForTesting(expected_italic));
185   render_text->ApplyStyle(ITALIC, true, Range(2, 4));
186   render_text->SetText(ASCIIToUTF16("012345678"));
187   EXPECT_TRUE(render_text->styles()[ITALIC].EqualsForTesting(expected_italic));
188 
189   // TODO(tmoniuszko): Enable when RenderTextMac::IsValidCursorIndex is
190   //                   implemented.
191 #if !defined(OS_MACOSX)
192   // Styles shouldn't be changed mid-grapheme.
193   render_text->SetText(WideToUTF16(
194       L"0" L"\x0915\x093f" L"1" L"\x0915\x093f" L"2"));
195   render_text->ApplyStyle(UNDERLINE, true, Range(2, 5));
196   std::vector<std::pair<size_t, bool> > expected_underline;
197   expected_underline.push_back(std::pair<size_t, bool>(0, false));
198   expected_underline.push_back(std::pair<size_t, bool>(1, true));
199   expected_underline.push_back(std::pair<size_t, bool>(6, false));
200   EXPECT_TRUE(render_text->styles()[UNDERLINE].EqualsForTesting(
201       expected_underline));
202 #endif  // OS_MACOSX
203 }
204 
205 #if defined(OS_LINUX) && !defined(USE_OZONE)
TEST_F(RenderTextTest,PangoAttributes)206 TEST_F(RenderTextTest, PangoAttributes) {
207   scoped_ptr<RenderText> render_text(RenderText::CreateNativeInstance());
208   render_text->SetText(ASCIIToUTF16("012345678"));
209 
210   // Apply ranged BOLD/ITALIC styles and check the resulting Pango attributes.
211   render_text->ApplyStyle(BOLD, true, Range(2, 4));
212   render_text->ApplyStyle(ITALIC, true, Range(1, 3));
213 
214   struct {
215     int start;
216     int end;
217     bool bold;
218     bool italic;
219   } cases[] = {
220     { 0, 1,       false, false },
221     { 1, 2,       false, true  },
222     { 2, 3,       true,  true  },
223     { 3, 4,       true,  false },
224     { 4, INT_MAX, false, false },
225   };
226 
227   int start = 0, end = 0;
228   RenderTextPango* rt_linux = static_cast<RenderTextPango*>(render_text.get());
229   rt_linux->EnsureLayout();
230   PangoAttrList* attributes = pango_layout_get_attributes(rt_linux->layout_);
231   PangoAttrIterator* iter = pango_attr_list_get_iterator(attributes);
232   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
233     pango_attr_iterator_range(iter, &start, &end);
234     EXPECT_EQ(cases[i].start, start);
235     EXPECT_EQ(cases[i].end, end);
236     PangoFontDescription* font = pango_font_description_new();
237     pango_attr_iterator_get_font(iter, font, NULL, NULL);
238     char* description_string = pango_font_description_to_string(font);
239     const base::string16 desc = ASCIIToUTF16(description_string);
240     const bool bold = desc.find(ASCIIToUTF16("Bold")) != std::string::npos;
241     EXPECT_EQ(cases[i].bold, bold);
242     const bool italic = desc.find(ASCIIToUTF16("Italic")) != std::string::npos;
243     EXPECT_EQ(cases[i].italic, italic);
244     pango_attr_iterator_next(iter);
245     pango_font_description_free(font);
246     g_free(description_string);
247   }
248   EXPECT_FALSE(pango_attr_iterator_next(iter));
249   pango_attr_iterator_destroy(iter);
250 }
251 #endif
252 
253 // TODO(asvitkine): Cursor movements tests disabled on Mac because RenderTextMac
254 //                  does not implement this yet. http://crbug.com/131618
255 #if !defined(OS_MACOSX)
TestVisualCursorMotionInObscuredField(RenderText * render_text,const base::string16 & text,bool select)256 void TestVisualCursorMotionInObscuredField(RenderText* render_text,
257                                            const base::string16& text,
258                                            bool select) {
259   ASSERT_TRUE(render_text->obscured());
260   render_text->SetText(text);
261   int len = text.length();
262   render_text->MoveCursor(LINE_BREAK, CURSOR_RIGHT, select);
263   EXPECT_EQ(SelectionModel(Range(select ? 0 : len, len), CURSOR_FORWARD),
264             render_text->selection_model());
265   render_text->MoveCursor(LINE_BREAK, CURSOR_LEFT, select);
266   EXPECT_EQ(SelectionModel(0, CURSOR_BACKWARD), render_text->selection_model());
267   for (int j = 1; j <= len; ++j) {
268     render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, select);
269     EXPECT_EQ(SelectionModel(Range(select ? 0 : j, j), CURSOR_BACKWARD),
270               render_text->selection_model());
271   }
272   for (int j = len - 1; j >= 0; --j) {
273     render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, select);
274     EXPECT_EQ(SelectionModel(Range(select ? 0 : j, j), CURSOR_FORWARD),
275               render_text->selection_model());
276   }
277   render_text->MoveCursor(WORD_BREAK, CURSOR_RIGHT, select);
278   EXPECT_EQ(SelectionModel(Range(select ? 0 : len, len), CURSOR_FORWARD),
279             render_text->selection_model());
280   render_text->MoveCursor(WORD_BREAK, CURSOR_LEFT, select);
281   EXPECT_EQ(SelectionModel(0, CURSOR_BACKWARD), render_text->selection_model());
282 }
283 
TEST_F(RenderTextTest,ObscuredText)284 TEST_F(RenderTextTest, ObscuredText) {
285   const base::string16 seuss = ASCIIToUTF16("hop on pop");
286   const base::string16 no_seuss = ASCIIToUTF16("**********");
287   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
288 
289   // GetLayoutText() returns asterisks when the obscured bit is set.
290   render_text->SetText(seuss);
291   render_text->SetObscured(true);
292   EXPECT_EQ(seuss, render_text->text());
293   EXPECT_EQ(no_seuss, render_text->GetLayoutText());
294   render_text->SetObscured(false);
295   EXPECT_EQ(seuss, render_text->text());
296   EXPECT_EQ(seuss, render_text->GetLayoutText());
297 
298   render_text->SetObscured(true);
299 
300   // Surrogate pairs are counted as one code point.
301   const base::char16 invalid_surrogates[] = {0xDC00, 0xD800, 0};
302   render_text->SetText(invalid_surrogates);
303   EXPECT_EQ(ASCIIToUTF16("**"), render_text->GetLayoutText());
304   const base::char16 valid_surrogates[] = {0xD800, 0xDC00, 0};
305   render_text->SetText(valid_surrogates);
306   EXPECT_EQ(ASCIIToUTF16("*"), render_text->GetLayoutText());
307   EXPECT_EQ(0U, render_text->cursor_position());
308   render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false);
309   EXPECT_EQ(2U, render_text->cursor_position());
310 
311   // Test index conversion and cursor validity with a valid surrogate pair.
312   EXPECT_EQ(0U, render_text->TextIndexToLayoutIndex(0U));
313   EXPECT_EQ(1U, render_text->TextIndexToLayoutIndex(1U));
314   EXPECT_EQ(1U, render_text->TextIndexToLayoutIndex(2U));
315   EXPECT_EQ(0U, render_text->LayoutIndexToTextIndex(0U));
316   EXPECT_EQ(2U, render_text->LayoutIndexToTextIndex(1U));
317   EXPECT_TRUE(render_text->IsValidCursorIndex(0U));
318   EXPECT_FALSE(render_text->IsValidCursorIndex(1U));
319   EXPECT_TRUE(render_text->IsValidCursorIndex(2U));
320 
321   // FindCursorPosition() should not return positions between a surrogate pair.
322   render_text->SetDisplayRect(Rect(0, 0, 20, 20));
323   EXPECT_EQ(render_text->FindCursorPosition(Point(0, 0)).caret_pos(), 0U);
324   EXPECT_EQ(render_text->FindCursorPosition(Point(20, 0)).caret_pos(), 2U);
325   for (int x = -1; x <= 20; ++x) {
326     SelectionModel selection = render_text->FindCursorPosition(Point(x, 0));
327     EXPECT_TRUE(selection.caret_pos() == 0U || selection.caret_pos() == 2U);
328   }
329 
330   // GetGlyphBounds() should yield the entire string bounds for text index 0.
331   EXPECT_EQ(render_text->GetStringSize().width(),
332             static_cast<int>(render_text->GetGlyphBounds(0U).length()));
333 
334   // Cursoring is independent of underlying characters when text is obscured.
335   const wchar_t* const texts[] = {
336     kWeak, kLtr, kLtrRtl, kLtrRtlLtr, kRtl, kRtlLtr, kRtlLtrRtl,
337     L"hop on pop",                              // Check LTR word boundaries.
338     L"\x05d0\x05d1 \x05d0\x05d2 \x05d1\x05d2",  // Check RTL word boundaries.
339   };
340   for (size_t i = 0; i < arraysize(texts); ++i) {
341     base::string16 text = WideToUTF16(texts[i]);
342     TestVisualCursorMotionInObscuredField(render_text.get(), text, false);
343     TestVisualCursorMotionInObscuredField(render_text.get(), text, true);
344   }
345 }
346 
TEST_F(RenderTextTest,RevealObscuredText)347 TEST_F(RenderTextTest, RevealObscuredText) {
348   const base::string16 seuss = ASCIIToUTF16("hop on pop");
349   const base::string16 no_seuss = ASCIIToUTF16("**********");
350   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
351 
352   render_text->SetText(seuss);
353   render_text->SetObscured(true);
354   EXPECT_EQ(seuss, render_text->text());
355   EXPECT_EQ(no_seuss, render_text->GetLayoutText());
356 
357   // Valid reveal index and new revealed index clears previous one.
358   render_text->RenderText::SetObscuredRevealIndex(0);
359   EXPECT_EQ(ASCIIToUTF16("h*********"), render_text->GetLayoutText());
360   render_text->RenderText::SetObscuredRevealIndex(1);
361   EXPECT_EQ(ASCIIToUTF16("*o********"), render_text->GetLayoutText());
362   render_text->RenderText::SetObscuredRevealIndex(2);
363   EXPECT_EQ(ASCIIToUTF16("**p*******"), render_text->GetLayoutText());
364 
365   // Invalid reveal index.
366   render_text->RenderText::SetObscuredRevealIndex(-1);
367   EXPECT_EQ(no_seuss, render_text->GetLayoutText());
368   render_text->RenderText::SetObscuredRevealIndex(seuss.length() + 1);
369   EXPECT_EQ(no_seuss, render_text->GetLayoutText());
370 
371   // SetObscured clears the revealed index.
372   render_text->RenderText::SetObscuredRevealIndex(0);
373   EXPECT_EQ(ASCIIToUTF16("h*********"), render_text->GetLayoutText());
374   render_text->SetObscured(false);
375   EXPECT_EQ(seuss, render_text->GetLayoutText());
376   render_text->SetObscured(true);
377   EXPECT_EQ(no_seuss, render_text->GetLayoutText());
378 
379   // SetText clears the revealed index.
380   render_text->SetText(ASCIIToUTF16("new"));
381   EXPECT_EQ(ASCIIToUTF16("***"), render_text->GetLayoutText());
382   render_text->RenderText::SetObscuredRevealIndex(2);
383   EXPECT_EQ(ASCIIToUTF16("**w"), render_text->GetLayoutText());
384   render_text->SetText(ASCIIToUTF16("new longer"));
385   EXPECT_EQ(ASCIIToUTF16("**********"), render_text->GetLayoutText());
386 
387   // Text with invalid surrogates.
388   const base::char16 invalid_surrogates[] = {0xDC00, 0xD800, 'h', 'o', 'p', 0};
389   render_text->SetText(invalid_surrogates);
390   EXPECT_EQ(ASCIIToUTF16("*****"), render_text->GetLayoutText());
391   render_text->RenderText::SetObscuredRevealIndex(0);
392   const base::char16 invalid_expect_0[] = {0xDC00, '*', '*', '*', '*', 0};
393   EXPECT_EQ(invalid_expect_0, render_text->GetLayoutText());
394   render_text->RenderText::SetObscuredRevealIndex(1);
395   const base::char16 invalid_expect_1[] = {'*', 0xD800, '*', '*', '*', 0};
396   EXPECT_EQ(invalid_expect_1, render_text->GetLayoutText());
397   render_text->RenderText::SetObscuredRevealIndex(2);
398   EXPECT_EQ(ASCIIToUTF16("**h**"), render_text->GetLayoutText());
399 
400   // Text with valid surrogates before and after the reveal index.
401   const base::char16 valid_surrogates[] =
402       {0xD800, 0xDC00, 'h', 'o', 'p', 0xD800, 0xDC00, 0};
403   render_text->SetText(valid_surrogates);
404   EXPECT_EQ(ASCIIToUTF16("*****"), render_text->GetLayoutText());
405   render_text->RenderText::SetObscuredRevealIndex(0);
406   const base::char16 valid_expect_0_and_1[] =
407       {0xD800, 0xDC00, '*', '*', '*', '*', 0};
408   EXPECT_EQ(valid_expect_0_and_1, render_text->GetLayoutText());
409   render_text->RenderText::SetObscuredRevealIndex(1);
410   EXPECT_EQ(valid_expect_0_and_1, render_text->GetLayoutText());
411   render_text->RenderText::SetObscuredRevealIndex(2);
412   EXPECT_EQ(ASCIIToUTF16("*h***"), render_text->GetLayoutText());
413   render_text->RenderText::SetObscuredRevealIndex(5);
414   const base::char16 valid_expect_5_and_6[] =
415       {'*', '*', '*', '*', 0xD800, 0xDC00, 0};
416   EXPECT_EQ(valid_expect_5_and_6, render_text->GetLayoutText());
417   render_text->RenderText::SetObscuredRevealIndex(6);
418   EXPECT_EQ(valid_expect_5_and_6, render_text->GetLayoutText());
419 }
420 
TEST_F(RenderTextTest,ElidedText)421 TEST_F(RenderTextTest, ElidedText) {
422   // TODO(skanuj) : Add more test cases for following
423   // - RenderText styles.
424   // - Cross interaction of truncate, elide and obscure.
425   // - ElideText tests from text_elider.cc.
426   struct {
427     const wchar_t* text;
428     const wchar_t* layout_text;
429     const bool elision_expected;
430   } cases[] = {
431     // Strings shorter than the elision width should be laid out in full.
432     { L"",        L""       , false },
433     { L"M",       L""       , false },
434     { L" . ",     L" . "    , false },
435     { kWeak,      kWeak     , false },
436     { kLtr,       kLtr      , false },
437     { kLtrRtl,    kLtrRtl   , false },
438     { kLtrRtlLtr, kLtrRtlLtr, false },
439     { kRtl,       kRtl      , false },
440     { kRtlLtr,    kRtlLtr   , false },
441     { kRtlLtrRtl, kRtlLtrRtl, false },
442     // Strings as long as the elision width should be laid out in full.
443     { L"012ab",   L"012ab"  , false },
444     // Long strings should be elided with an ellipsis appended at the end.
445     { L"012abc",              L"012a\x2026", true },
446     { L"012ab" L"\x5d0\x5d1", L"012a\x2026", true },
447     { L"012a" L"\x5d1" L"b",  L"012a\x2026", true },
448     // No RLM marker added as digits (012) have weak directionality.
449     { L"01" L"\x5d0\x5d1\x5d2", L"01\x5d0\x5d1\x2026", true },
450     // RLM marker added as "ab" have strong LTR directionality.
451     { L"ab" L"\x5d0\x5d1\x5d2", L"ab\x5d0\x5d1\x2026\x200f", true },
452     // Complex script is not handled. In this example, the "\x0915\x093f" is a
453     // compound glyph, but only half of it is elided.
454     { L"0123\x0915\x093f", L"0123\x0915\x2026", true },
455     // Surrogate pairs should be elided reasonably enough.
456     { L"0\x05e9\x05bc\x05c1\x05b8",   L"0\x05e9\x05bc\x05c1\x05b8", false },
457     { L"0\x05e9\x05bc\x05c1\x05b8",   L"0\x05e9\x05bc\x2026"      , true  },
458     { L"01\x05e9\x05bc\x05c1\x05b8",  L"01\x05e9\x2026"           , true  },
459     { L"012\x05e9\x05bc\x05c1\x05b8", L"012\x2026\x200E"          , true  },
460     { L"012\xF0\x9D\x84\x9E",         L"012\xF0\x2026"            , true  },
461   };
462 
463   scoped_ptr<RenderText> expected_render_text(RenderText::CreateInstance());
464   expected_render_text->SetFontList(FontList("serif, Sans serif, 12px"));
465   expected_render_text->SetDisplayRect(Rect(0, 0, 9999, 100));
466 
467   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
468   render_text->SetFontList(FontList("serif, Sans serif, 12px"));
469   render_text->SetElideBehavior(ELIDE_TAIL);
470 
471   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
472     // Compute expected width
473     expected_render_text->SetText(WideToUTF16(cases[i].layout_text));
474     int expected_width = expected_render_text->GetContentWidth();
475 
476     base::string16 input = WideToUTF16(cases[i].text);
477     // Extend the input text to ensure that it is wider than the layout_text,
478     // and so it will get elided.
479     if (cases[i].elision_expected)
480       input.append(WideToUTF16(L" MMMMMMMMMMM"));
481 
482     render_text->SetText(input);
483     render_text->SetDisplayRect(Rect(0, 0, expected_width, 100));
484     EXPECT_EQ(input, render_text->text())
485         << "->For case " << i << ": " << cases[i].text << "\n";
486     EXPECT_EQ(WideToUTF16(cases[i].layout_text), render_text->GetLayoutText())
487         << "->For case " << i << ": " << cases[i].text << "\n";
488     expected_render_text->SetText(base::string16());
489   }
490 }
491 
TEST_F(RenderTextTest,ElidedObscuredText)492 TEST_F(RenderTextTest, ElidedObscuredText) {
493   scoped_ptr<RenderText> expected_render_text(RenderText::CreateInstance());
494   expected_render_text->SetFontList(FontList("serif, Sans serif, 12px"));
495   expected_render_text->SetDisplayRect(Rect(0, 0, 9999, 100));
496   expected_render_text->SetText(WideToUTF16(L"**\x2026"));
497 
498   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
499   render_text->SetFontList(FontList("serif, Sans serif, 12px"));
500   render_text->SetElideBehavior(ELIDE_TAIL);
501   render_text->SetDisplayRect(
502       Rect(0, 0, expected_render_text->GetContentWidth(), 100));
503   render_text->SetObscured(true);
504   render_text->SetText(WideToUTF16(L"abcdef"));
505   EXPECT_EQ(WideToUTF16(L"abcdef"), render_text->text());
506   EXPECT_EQ(WideToUTF16(L"**\x2026"), render_text->GetLayoutText());
507 }
508 
TEST_F(RenderTextTest,TruncatedText)509 TEST_F(RenderTextTest, TruncatedText) {
510   struct {
511     const wchar_t* text;
512     const wchar_t* layout_text;
513   } cases[] = {
514     // Strings shorter than the truncation length should be laid out in full.
515     { L"",        L""        },
516     { kWeak,      kWeak      },
517     { kLtr,       kLtr       },
518     { kLtrRtl,    kLtrRtl    },
519     { kLtrRtlLtr, kLtrRtlLtr },
520     { kRtl,       kRtl       },
521     { kRtlLtr,    kRtlLtr    },
522     { kRtlLtrRtl, kRtlLtrRtl },
523     // Strings as long as the truncation length should be laid out in full.
524     { L"01234",   L"01234"   },
525     // Long strings should be truncated with an ellipsis appended at the end.
526     { L"012345",                  L"0123\x2026"     },
527     { L"012" L" . ",              L"012 \x2026"     },
528     { L"012" L"abc",              L"012a\x2026"     },
529     { L"012" L"a" L"\x5d0\x5d1",  L"012a\x2026"     },
530     { L"012" L"a" L"\x5d1" L"b",  L"012a\x2026"     },
531     { L"012" L"\x5d0\x5d1\x5d2",  L"012\x5d0\x2026" },
532     { L"012" L"\x5d0\x5d1" L"a",  L"012\x5d0\x2026" },
533     { L"012" L"\x5d0" L"a" L"\x5d1",    L"012\x5d0\x2026" },
534     // Surrogate pairs should be truncated reasonably enough.
535     { L"0123\x0915\x093f",              L"0123\x2026"                },
536     { L"0\x05e9\x05bc\x05c1\x05b8",     L"0\x05e9\x05bc\x05c1\x05b8" },
537     { L"01\x05e9\x05bc\x05c1\x05b8",    L"01\x05e9\x05bc\x2026"      },
538     { L"012\x05e9\x05bc\x05c1\x05b8",   L"012\x05e9\x2026"           },
539     { L"0123\x05e9\x05bc\x05c1\x05b8",  L"0123\x2026"                },
540     { L"01234\x05e9\x05bc\x05c1\x05b8", L"0123\x2026"                },
541     { L"012\xF0\x9D\x84\x9E",           L"012\xF0\x2026"             },
542   };
543 
544   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
545   render_text->set_truncate_length(5);
546   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
547     render_text->SetText(WideToUTF16(cases[i].text));
548     EXPECT_EQ(WideToUTF16(cases[i].text), render_text->text());
549     EXPECT_EQ(WideToUTF16(cases[i].layout_text), render_text->GetLayoutText())
550         << "For case " << i << ": " << cases[i].text;
551   }
552 }
553 
TEST_F(RenderTextTest,TruncatedObscuredText)554 TEST_F(RenderTextTest, TruncatedObscuredText) {
555   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
556   render_text->set_truncate_length(3);
557   render_text->SetObscured(true);
558   render_text->SetText(WideToUTF16(L"abcdef"));
559   EXPECT_EQ(WideToUTF16(L"abcdef"), render_text->text());
560   EXPECT_EQ(WideToUTF16(L"**\x2026"), render_text->GetLayoutText());
561 }
562 
TEST_F(RenderTextTest,TruncatedCursorMovementLTR)563 TEST_F(RenderTextTest, TruncatedCursorMovementLTR) {
564   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
565   render_text->set_truncate_length(2);
566   render_text->SetText(WideToUTF16(L"abcd"));
567 
568   EXPECT_EQ(SelectionModel(0, CURSOR_BACKWARD), render_text->selection_model());
569   render_text->MoveCursor(LINE_BREAK, CURSOR_RIGHT, false);
570   EXPECT_EQ(SelectionModel(4, CURSOR_FORWARD), render_text->selection_model());
571   render_text->MoveCursor(LINE_BREAK, CURSOR_LEFT, false);
572   EXPECT_EQ(SelectionModel(0, CURSOR_BACKWARD), render_text->selection_model());
573 
574   std::vector<SelectionModel> expected;
575   expected.push_back(SelectionModel(0, CURSOR_BACKWARD));
576   expected.push_back(SelectionModel(1, CURSOR_BACKWARD));
577   // The cursor hops over the ellipsis and elided text to the line end.
578   expected.push_back(SelectionModel(4, CURSOR_BACKWARD));
579   expected.push_back(SelectionModel(4, CURSOR_FORWARD));
580   RunMoveCursorLeftRightTest(render_text.get(), expected, CURSOR_RIGHT);
581 
582   expected.clear();
583   expected.push_back(SelectionModel(4, CURSOR_FORWARD));
584   // The cursor hops over the elided text to preceeding text.
585   expected.push_back(SelectionModel(1, CURSOR_FORWARD));
586   expected.push_back(SelectionModel(0, CURSOR_FORWARD));
587   expected.push_back(SelectionModel(0, CURSOR_BACKWARD));
588   RunMoveCursorLeftRightTest(render_text.get(), expected, CURSOR_LEFT);
589 }
590 
TEST_F(RenderTextTest,TruncatedCursorMovementRTL)591 TEST_F(RenderTextTest, TruncatedCursorMovementRTL) {
592   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
593   render_text->set_truncate_length(2);
594   render_text->SetText(WideToUTF16(L"\x5d0\x5d1\x5d2\x5d3"));
595 
596   EXPECT_EQ(SelectionModel(0, CURSOR_BACKWARD), render_text->selection_model());
597   render_text->MoveCursor(LINE_BREAK, CURSOR_LEFT, false);
598   EXPECT_EQ(SelectionModel(4, CURSOR_FORWARD), render_text->selection_model());
599   render_text->MoveCursor(LINE_BREAK, CURSOR_RIGHT, false);
600   EXPECT_EQ(SelectionModel(0, CURSOR_BACKWARD), render_text->selection_model());
601 
602   std::vector<SelectionModel> expected;
603   expected.push_back(SelectionModel(0, CURSOR_BACKWARD));
604   expected.push_back(SelectionModel(1, CURSOR_BACKWARD));
605   // The cursor hops over the ellipsis and elided text to the line end.
606   expected.push_back(SelectionModel(4, CURSOR_BACKWARD));
607   expected.push_back(SelectionModel(4, CURSOR_FORWARD));
608   RunMoveCursorLeftRightTest(render_text.get(), expected, CURSOR_LEFT);
609 
610   expected.clear();
611   expected.push_back(SelectionModel(4, CURSOR_FORWARD));
612   // The cursor hops over the elided text to preceeding text.
613   expected.push_back(SelectionModel(1, CURSOR_FORWARD));
614   expected.push_back(SelectionModel(0, CURSOR_FORWARD));
615   expected.push_back(SelectionModel(0, CURSOR_BACKWARD));
616   RunMoveCursorLeftRightTest(render_text.get(), expected, CURSOR_RIGHT);
617 }
618 
TEST_F(RenderTextTest,GetTextDirection)619 TEST_F(RenderTextTest, GetTextDirection) {
620   struct {
621     const wchar_t* text;
622     const base::i18n::TextDirection text_direction;
623   } cases[] = {
624     // Blank strings and those with no/weak directionality default to LTR.
625     { L"",        base::i18n::LEFT_TO_RIGHT },
626     { kWeak,      base::i18n::LEFT_TO_RIGHT },
627     // Strings that begin with strong LTR characters.
628     { kLtr,       base::i18n::LEFT_TO_RIGHT },
629     { kLtrRtl,    base::i18n::LEFT_TO_RIGHT },
630     { kLtrRtlLtr, base::i18n::LEFT_TO_RIGHT },
631     // Strings that begin with strong RTL characters.
632     { kRtl,       base::i18n::RIGHT_TO_LEFT },
633     { kRtlLtr,    base::i18n::RIGHT_TO_LEFT },
634     { kRtlLtrRtl, base::i18n::RIGHT_TO_LEFT },
635   };
636 
637   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
638   const bool was_rtl = base::i18n::IsRTL();
639 
640   for (size_t i = 0; i < 2; ++i) {
641     // Toggle the application default text direction (to try each direction).
642     SetRTL(!base::i18n::IsRTL());
643     const base::i18n::TextDirection ui_direction = base::i18n::IsRTL() ?
644         base::i18n::RIGHT_TO_LEFT : base::i18n::LEFT_TO_RIGHT;
645 
646     // Ensure that directionality modes yield the correct text directions.
647     for (size_t j = 0; j < ARRAYSIZE_UNSAFE(cases); j++) {
648       render_text->SetText(WideToUTF16(cases[j].text));
649       render_text->SetDirectionalityMode(DIRECTIONALITY_FROM_TEXT);
650       EXPECT_EQ(render_text->GetTextDirection(), cases[j].text_direction);
651       render_text->SetDirectionalityMode(DIRECTIONALITY_FROM_UI);
652       EXPECT_EQ(render_text->GetTextDirection(), ui_direction);
653       render_text->SetDirectionalityMode(DIRECTIONALITY_FORCE_LTR);
654       EXPECT_EQ(render_text->GetTextDirection(), base::i18n::LEFT_TO_RIGHT);
655       render_text->SetDirectionalityMode(DIRECTIONALITY_FORCE_RTL);
656       EXPECT_EQ(render_text->GetTextDirection(), base::i18n::RIGHT_TO_LEFT);
657     }
658   }
659 
660   EXPECT_EQ(was_rtl, base::i18n::IsRTL());
661 
662   // Ensure that text changes update the direction for DIRECTIONALITY_FROM_TEXT.
663   render_text->SetDirectionalityMode(DIRECTIONALITY_FROM_TEXT);
664   render_text->SetText(WideToUTF16(kLtr));
665   EXPECT_EQ(render_text->GetTextDirection(), base::i18n::LEFT_TO_RIGHT);
666   render_text->SetText(WideToUTF16(kRtl));
667   EXPECT_EQ(render_text->GetTextDirection(), base::i18n::RIGHT_TO_LEFT);
668 }
669 
TEST_F(RenderTextTest,MoveCursorLeftRightInLtr)670 TEST_F(RenderTextTest, MoveCursorLeftRightInLtr) {
671   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
672 
673   // Pure LTR.
674   render_text->SetText(ASCIIToUTF16("abc"));
675   // |expected| saves the expected SelectionModel when moving cursor from left
676   // to right.
677   std::vector<SelectionModel> expected;
678   expected.push_back(SelectionModel(0, CURSOR_BACKWARD));
679   expected.push_back(SelectionModel(1, CURSOR_BACKWARD));
680   expected.push_back(SelectionModel(2, CURSOR_BACKWARD));
681   expected.push_back(SelectionModel(3, CURSOR_BACKWARD));
682   expected.push_back(SelectionModel(3, CURSOR_FORWARD));
683   RunMoveCursorLeftRightTest(render_text.get(), expected, CURSOR_RIGHT);
684 
685   expected.clear();
686   expected.push_back(SelectionModel(3, CURSOR_FORWARD));
687   expected.push_back(SelectionModel(2, CURSOR_FORWARD));
688   expected.push_back(SelectionModel(1, CURSOR_FORWARD));
689   expected.push_back(SelectionModel(0, CURSOR_FORWARD));
690   expected.push_back(SelectionModel(0, CURSOR_BACKWARD));
691   RunMoveCursorLeftRightTest(render_text.get(), expected, CURSOR_LEFT);
692 }
693 
TEST_F(RenderTextTest,MoveCursorLeftRightInLtrRtl)694 TEST_F(RenderTextTest, MoveCursorLeftRightInLtrRtl) {
695   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
696   // LTR-RTL
697   render_text->SetText(WideToUTF16(L"abc\x05d0\x05d1\x05d2"));
698   // The last one is the expected END position.
699   std::vector<SelectionModel> expected;
700   expected.push_back(SelectionModel(0, CURSOR_BACKWARD));
701   expected.push_back(SelectionModel(1, CURSOR_BACKWARD));
702   expected.push_back(SelectionModel(2, CURSOR_BACKWARD));
703   expected.push_back(SelectionModel(3, CURSOR_BACKWARD));
704   expected.push_back(SelectionModel(5, CURSOR_FORWARD));
705   expected.push_back(SelectionModel(4, CURSOR_FORWARD));
706   expected.push_back(SelectionModel(3, CURSOR_FORWARD));
707   expected.push_back(SelectionModel(6, CURSOR_FORWARD));
708   RunMoveCursorLeftRightTest(render_text.get(), expected, CURSOR_RIGHT);
709 
710   expected.clear();
711   expected.push_back(SelectionModel(6, CURSOR_FORWARD));
712   expected.push_back(SelectionModel(4, CURSOR_BACKWARD));
713   expected.push_back(SelectionModel(5, CURSOR_BACKWARD));
714   expected.push_back(SelectionModel(6, CURSOR_BACKWARD));
715   expected.push_back(SelectionModel(2, CURSOR_FORWARD));
716   expected.push_back(SelectionModel(1, CURSOR_FORWARD));
717   expected.push_back(SelectionModel(0, CURSOR_FORWARD));
718   expected.push_back(SelectionModel(0, CURSOR_BACKWARD));
719   RunMoveCursorLeftRightTest(render_text.get(), expected, CURSOR_LEFT);
720 }
721 
TEST_F(RenderTextTest,MoveCursorLeftRightInLtrRtlLtr)722 TEST_F(RenderTextTest, MoveCursorLeftRightInLtrRtlLtr) {
723   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
724   // LTR-RTL-LTR.
725   render_text->SetText(WideToUTF16(L"a" L"\x05d1" L"b"));
726   std::vector<SelectionModel> expected;
727   expected.push_back(SelectionModel(0, CURSOR_BACKWARD));
728   expected.push_back(SelectionModel(1, CURSOR_BACKWARD));
729   expected.push_back(SelectionModel(1, CURSOR_FORWARD));
730   expected.push_back(SelectionModel(3, CURSOR_BACKWARD));
731   expected.push_back(SelectionModel(3, CURSOR_FORWARD));
732   RunMoveCursorLeftRightTest(render_text.get(), expected, CURSOR_RIGHT);
733 
734   expected.clear();
735   expected.push_back(SelectionModel(3, CURSOR_FORWARD));
736   expected.push_back(SelectionModel(2, CURSOR_FORWARD));
737   expected.push_back(SelectionModel(2, CURSOR_BACKWARD));
738   expected.push_back(SelectionModel(0, CURSOR_FORWARD));
739   expected.push_back(SelectionModel(0, CURSOR_BACKWARD));
740   RunMoveCursorLeftRightTest(render_text.get(), expected, CURSOR_LEFT);
741 }
742 
TEST_F(RenderTextTest,MoveCursorLeftRightInRtl)743 TEST_F(RenderTextTest, MoveCursorLeftRightInRtl) {
744   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
745   // Pure RTL.
746   render_text->SetText(WideToUTF16(L"\x05d0\x05d1\x05d2"));
747   render_text->MoveCursor(LINE_BREAK, CURSOR_RIGHT, false);
748   std::vector<SelectionModel> expected;
749 
750   expected.push_back(SelectionModel(0, CURSOR_BACKWARD));
751   expected.push_back(SelectionModel(1, CURSOR_BACKWARD));
752   expected.push_back(SelectionModel(2, CURSOR_BACKWARD));
753   expected.push_back(SelectionModel(3, CURSOR_BACKWARD));
754   expected.push_back(SelectionModel(3, CURSOR_FORWARD));
755   RunMoveCursorLeftRightTest(render_text.get(), expected, CURSOR_LEFT);
756 
757   expected.clear();
758 
759   expected.push_back(SelectionModel(3, CURSOR_FORWARD));
760   expected.push_back(SelectionModel(2, CURSOR_FORWARD));
761   expected.push_back(SelectionModel(1, CURSOR_FORWARD));
762   expected.push_back(SelectionModel(0, CURSOR_FORWARD));
763   expected.push_back(SelectionModel(0, CURSOR_BACKWARD));
764   RunMoveCursorLeftRightTest(render_text.get(), expected, CURSOR_RIGHT);
765 }
766 
TEST_F(RenderTextTest,MoveCursorLeftRightInRtlLtr)767 TEST_F(RenderTextTest, MoveCursorLeftRightInRtlLtr) {
768   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
769   // RTL-LTR
770   render_text->SetText(WideToUTF16(L"\x05d0\x05d1\x05d2" L"abc"));
771   render_text->MoveCursor(LINE_BREAK, CURSOR_RIGHT, false);
772   std::vector<SelectionModel> expected;
773   expected.push_back(SelectionModel(0, CURSOR_BACKWARD));
774   expected.push_back(SelectionModel(1, CURSOR_BACKWARD));
775   expected.push_back(SelectionModel(2, CURSOR_BACKWARD));
776   expected.push_back(SelectionModel(3, CURSOR_BACKWARD));
777   expected.push_back(SelectionModel(5, CURSOR_FORWARD));
778   expected.push_back(SelectionModel(4, CURSOR_FORWARD));
779   expected.push_back(SelectionModel(3, CURSOR_FORWARD));
780   expected.push_back(SelectionModel(6, CURSOR_FORWARD));
781   RunMoveCursorLeftRightTest(render_text.get(), expected, CURSOR_LEFT);
782 
783   expected.clear();
784   expected.push_back(SelectionModel(6, CURSOR_FORWARD));
785   expected.push_back(SelectionModel(4, CURSOR_BACKWARD));
786   expected.push_back(SelectionModel(5, CURSOR_BACKWARD));
787   expected.push_back(SelectionModel(6, CURSOR_BACKWARD));
788   expected.push_back(SelectionModel(2, CURSOR_FORWARD));
789   expected.push_back(SelectionModel(1, CURSOR_FORWARD));
790   expected.push_back(SelectionModel(0, CURSOR_FORWARD));
791   expected.push_back(SelectionModel(0, CURSOR_BACKWARD));
792   RunMoveCursorLeftRightTest(render_text.get(), expected, CURSOR_RIGHT);
793 }
794 
TEST_F(RenderTextTest,MoveCursorLeftRightInRtlLtrRtl)795 TEST_F(RenderTextTest, MoveCursorLeftRightInRtlLtrRtl) {
796   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
797   // RTL-LTR-RTL.
798   render_text->SetText(WideToUTF16(L"\x05d0" L"a" L"\x05d1"));
799   render_text->MoveCursor(LINE_BREAK, CURSOR_RIGHT, false);
800   std::vector<SelectionModel> expected;
801   expected.push_back(SelectionModel(0, CURSOR_BACKWARD));
802   expected.push_back(SelectionModel(1, CURSOR_BACKWARD));
803   expected.push_back(SelectionModel(1, CURSOR_FORWARD));
804   expected.push_back(SelectionModel(3, CURSOR_BACKWARD));
805   expected.push_back(SelectionModel(3, CURSOR_FORWARD));
806   RunMoveCursorLeftRightTest(render_text.get(), expected, CURSOR_LEFT);
807 
808   expected.clear();
809   expected.push_back(SelectionModel(3, CURSOR_FORWARD));
810   expected.push_back(SelectionModel(2, CURSOR_FORWARD));
811   expected.push_back(SelectionModel(2, CURSOR_BACKWARD));
812   expected.push_back(SelectionModel(0, CURSOR_FORWARD));
813   expected.push_back(SelectionModel(0, CURSOR_BACKWARD));
814   RunMoveCursorLeftRightTest(render_text.get(), expected, CURSOR_RIGHT);
815 }
816 
817 // TODO(xji): temporarily disable in platform Win since the complex script
818 // characters turned into empty square due to font regression. So, not able
819 // to test 2 characters belong to the same grapheme.
820 #if defined(OS_LINUX)
TEST_F(RenderTextTest,MoveCursorLeftRight_ComplexScript)821 TEST_F(RenderTextTest, MoveCursorLeftRight_ComplexScript) {
822   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
823 
824   render_text->SetText(WideToUTF16(L"\x0915\x093f\x0915\x094d\x0915"));
825   EXPECT_EQ(0U, render_text->cursor_position());
826   render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false);
827   EXPECT_EQ(2U, render_text->cursor_position());
828   render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false);
829   EXPECT_EQ(4U, render_text->cursor_position());
830   render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false);
831   EXPECT_EQ(5U, render_text->cursor_position());
832   render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false);
833   EXPECT_EQ(5U, render_text->cursor_position());
834 
835   render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, false);
836   EXPECT_EQ(4U, render_text->cursor_position());
837   render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, false);
838   EXPECT_EQ(2U, render_text->cursor_position());
839   render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, false);
840   EXPECT_EQ(0U, render_text->cursor_position());
841   render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, false);
842   EXPECT_EQ(0U, render_text->cursor_position());
843 }
844 #endif
845 
846 // TODO(ckocagil): Enable for RenderTextHarfBuzz. http://crbug.com/383265
TEST_F(RenderTextTest,MoveCursorLeftRight_MeiryoUILigatures)847 TEST_F(RenderTextTest, MoveCursorLeftRight_MeiryoUILigatures) {
848   scoped_ptr<RenderText> render_text(RenderText::CreateNativeInstance());
849   // Meiryo UI uses single-glyph ligatures for 'ff' and 'ffi', but each letter
850   // (code point) has unique bounds, so mid-glyph cursoring should be possible.
851   render_text->SetFontList(FontList("Meiryo UI, 12px"));
852   render_text->SetText(WideToUTF16(L"ff ffi"));
853   EXPECT_EQ(0U, render_text->cursor_position());
854   for (size_t i = 0; i < render_text->text().length(); ++i) {
855     render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false);
856     EXPECT_EQ(i + 1, render_text->cursor_position());
857   }
858   EXPECT_EQ(6U, render_text->cursor_position());
859 }
860 
TEST_F(RenderTextTest,GraphemePositions)861 TEST_F(RenderTextTest, GraphemePositions) {
862   // LTR 2-character grapheme, LTR abc, LTR 2-character grapheme.
863   const base::string16 kText1 =
864       WideToUTF16(L"\x0915\x093f" L"abc" L"\x0915\x093f");
865 
866   // LTR ab, LTR 2-character grapheme, LTR cd.
867   const base::string16 kText2 = WideToUTF16(L"ab" L"\x0915\x093f" L"cd");
868 
869   // The below is 'MUSICAL SYMBOL G CLEF', which is represented in UTF-16 as
870   // two characters forming the surrogate pair 0x0001D11E.
871   const std::string kSurrogate = "\xF0\x9D\x84\x9E";
872 
873   // LTR ab, UTF16 surrogate pair, LTR cd.
874   const base::string16 kText3 = UTF8ToUTF16("ab" + kSurrogate + "cd");
875 
876   struct {
877     base::string16 text;
878     size_t index;
879     size_t expected_previous;
880     size_t expected_next;
881   } cases[] = {
882     { base::string16(), 0, 0, 0 },
883     { base::string16(), 1, 0, 0 },
884     { base::string16(), 50, 0, 0 },
885     { kText1, 0, 0, 2 },
886     { kText1, 1, 0, 2 },
887     { kText1, 2, 0, 3 },
888     { kText1, 3, 2, 4 },
889     { kText1, 4, 3, 5 },
890     { kText1, 5, 4, 7 },
891     { kText1, 6, 5, 7 },
892     { kText1, 7, 5, 7 },
893     { kText1, 8, 7, 7 },
894     { kText1, 50, 7, 7 },
895     { kText2, 0, 0, 1 },
896     { kText2, 1, 0, 2 },
897     { kText2, 2, 1, 4 },
898     { kText2, 3, 2, 4 },
899     { kText2, 4, 2, 5 },
900     { kText2, 5, 4, 6 },
901     { kText2, 6, 5, 6 },
902     { kText2, 7, 6, 6 },
903     { kText2, 50, 6, 6 },
904     { kText3, 0, 0, 1 },
905     { kText3, 1, 0, 2 },
906     { kText3, 2, 1, 4 },
907     { kText3, 3, 2, 4 },
908     { kText3, 4, 2, 5 },
909     { kText3, 5, 4, 6 },
910     { kText3, 6, 5, 6 },
911     { kText3, 7, 6, 6 },
912     { kText3, 50, 6, 6 },
913   };
914 
915 #if defined(OS_WIN)
916   // TODO(msw): XP fails due to lack of font support: http://crbug.com/106450
917   if (base::win::GetVersion() < base::win::VERSION_VISTA)
918     return;
919 #endif
920 
921   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
922   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
923     SCOPED_TRACE(base::StringPrintf("Testing cases[%" PRIuS "]", i));
924     render_text->SetText(cases[i].text);
925 
926     size_t next = render_text->IndexOfAdjacentGrapheme(cases[i].index,
927                                                        CURSOR_FORWARD);
928     EXPECT_EQ(cases[i].expected_next, next);
929     EXPECT_TRUE(render_text->IsValidCursorIndex(next));
930 
931     size_t previous = render_text->IndexOfAdjacentGrapheme(cases[i].index,
932                                                            CURSOR_BACKWARD);
933     EXPECT_EQ(cases[i].expected_previous, previous);
934     EXPECT_TRUE(render_text->IsValidCursorIndex(previous));
935   }
936 }
937 
TEST_F(RenderTextTest,MidGraphemeSelectionBounds)938 TEST_F(RenderTextTest, MidGraphemeSelectionBounds) {
939 #if defined(OS_WIN)
940   // TODO(msw): XP fails due to lack of font support: http://crbug.com/106450
941   if (base::win::GetVersion() < base::win::VERSION_VISTA)
942     return;
943 #endif
944 
945   // Test that selection bounds may be set amid multi-character graphemes.
946   const base::string16 kHindi = WideToUTF16(L"\x0915\x093f");
947   const base::string16 kThai = WideToUTF16(L"\x0e08\x0e33");
948   const base::string16 cases[] = { kHindi, kThai };
949 
950   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
951   for (size_t i = 0; i < arraysize(cases); i++) {
952     SCOPED_TRACE(base::StringPrintf("Testing cases[%" PRIuS "]", i));
953     render_text->SetText(cases[i]);
954     EXPECT_TRUE(render_text->IsValidLogicalIndex(1));
955 #if !defined(OS_MACOSX)
956     EXPECT_FALSE(render_text->IsValidCursorIndex(1));
957 #endif
958     EXPECT_TRUE(render_text->SelectRange(Range(2, 1)));
959     EXPECT_EQ(Range(2, 1), render_text->selection());
960     EXPECT_EQ(1U, render_text->cursor_position());
961     // Although selection bounds may be set within a multi-character grapheme,
962     // cursor movement (e.g. via arrow key) should avoid those indices.
963     render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, false);
964     EXPECT_EQ(0U, render_text->cursor_position());
965     render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false);
966     EXPECT_EQ(2U, render_text->cursor_position());
967   }
968 }
969 
TEST_F(RenderTextTest,FindCursorPosition)970 TEST_F(RenderTextTest, FindCursorPosition) {
971   const wchar_t* kTestStrings[] = { kLtrRtl, kLtrRtlLtr, kRtlLtr, kRtlLtrRtl };
972   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
973   render_text->SetDisplayRect(Rect(0, 0, 100, 20));
974   for (size_t i = 0; i < arraysize(kTestStrings); ++i) {
975     SCOPED_TRACE(base::StringPrintf("Testing case[%" PRIuS "]", i));
976     render_text->SetText(WideToUTF16(kTestStrings[i]));
977     for(size_t j = 0; j < render_text->text().length(); ++j) {
978       const Range range(render_text->GetGlyphBounds(j));
979       // Test a point just inside the leading edge of the glyph bounds.
980       int x = range.is_reversed() ? range.GetMax() - 1 : range.GetMin() + 1;
981       EXPECT_EQ(j, render_text->FindCursorPosition(Point(x, 0)).caret_pos());
982     }
983   }
984 }
985 
TEST_F(RenderTextTest,EdgeSelectionModels)986 TEST_F(RenderTextTest, EdgeSelectionModels) {
987   // Simple Latin text.
988   const base::string16 kLatin = WideToUTF16(L"abc");
989   // LTR 2-character grapheme.
990   const base::string16 kLTRGrapheme = WideToUTF16(L"\x0915\x093f");
991   // LTR 2-character grapheme, LTR a, LTR 2-character grapheme.
992   const base::string16 kHindiLatin =
993       WideToUTF16(L"\x0915\x093f" L"a" L"\x0915\x093f");
994   // RTL 2-character grapheme.
995   const base::string16 kRTLGrapheme = WideToUTF16(L"\x05e0\x05b8");
996   // RTL 2-character grapheme, LTR a, RTL 2-character grapheme.
997   const base::string16 kHebrewLatin =
998       WideToUTF16(L"\x05e0\x05b8" L"a" L"\x05e0\x05b8");
999 
1000   struct {
1001     base::string16 text;
1002     base::i18n::TextDirection expected_text_direction;
1003   } cases[] = {
1004     { base::string16(), base::i18n::LEFT_TO_RIGHT },
1005     { kLatin,       base::i18n::LEFT_TO_RIGHT },
1006     { kLTRGrapheme, base::i18n::LEFT_TO_RIGHT },
1007     { kHindiLatin,  base::i18n::LEFT_TO_RIGHT },
1008     { kRTLGrapheme, base::i18n::RIGHT_TO_LEFT },
1009     { kHebrewLatin, base::i18n::RIGHT_TO_LEFT },
1010   };
1011 
1012 #if defined(OS_WIN)
1013   // TODO(msw): XP fails due to lack of font support: http://crbug.com/106450
1014   if (base::win::GetVersion() < base::win::VERSION_VISTA)
1015     return;
1016 #endif
1017 
1018   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1019   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
1020     render_text->SetText(cases[i].text);
1021     bool ltr = (cases[i].expected_text_direction == base::i18n::LEFT_TO_RIGHT);
1022 
1023     SelectionModel start_edge =
1024         render_text->EdgeSelectionModel(ltr ? CURSOR_LEFT : CURSOR_RIGHT);
1025     EXPECT_EQ(start_edge, SelectionModel(0, CURSOR_BACKWARD));
1026 
1027     SelectionModel end_edge =
1028         render_text->EdgeSelectionModel(ltr ? CURSOR_RIGHT : CURSOR_LEFT);
1029     EXPECT_EQ(end_edge, SelectionModel(cases[i].text.length(), CURSOR_FORWARD));
1030   }
1031 }
1032 
TEST_F(RenderTextTest,SelectAll)1033 TEST_F(RenderTextTest, SelectAll) {
1034   const wchar_t* const cases[] =
1035       { kWeak, kLtr, kLtrRtl, kLtrRtlLtr, kRtl, kRtlLtr, kRtlLtrRtl };
1036 
1037   // Ensure that SelectAll respects the |reversed| argument regardless of
1038   // application locale and text content directionality.
1039   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1040   const SelectionModel expected_reversed(Range(3, 0), CURSOR_FORWARD);
1041   const SelectionModel expected_forwards(Range(0, 3), CURSOR_BACKWARD);
1042   const bool was_rtl = base::i18n::IsRTL();
1043 
1044   for (size_t i = 0; i < 2; ++i) {
1045     SetRTL(!base::i18n::IsRTL());
1046     // Test that an empty string produces an empty selection model.
1047     render_text->SetText(base::string16());
1048     EXPECT_EQ(render_text->selection_model(), SelectionModel());
1049 
1050     // Test the weak, LTR, RTL, and Bidi string cases.
1051     for (size_t j = 0; j < ARRAYSIZE_UNSAFE(cases); j++) {
1052       render_text->SetText(WideToUTF16(cases[j]));
1053       render_text->SelectAll(false);
1054       EXPECT_EQ(render_text->selection_model(), expected_forwards);
1055       render_text->SelectAll(true);
1056       EXPECT_EQ(render_text->selection_model(), expected_reversed);
1057     }
1058   }
1059 
1060   EXPECT_EQ(was_rtl, base::i18n::IsRTL());
1061 }
1062 
TEST_F(RenderTextTest,MoveCursorLeftRightWithSelection)1063 TEST_F(RenderTextTest, MoveCursorLeftRightWithSelection) {
1064   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1065   render_text->SetText(WideToUTF16(L"abc\x05d0\x05d1\x05d2"));
1066   // Left arrow on select ranging (6, 4).
1067   render_text->MoveCursor(LINE_BREAK, CURSOR_RIGHT, false);
1068   EXPECT_EQ(Range(6), render_text->selection());
1069   render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, false);
1070   EXPECT_EQ(Range(4), render_text->selection());
1071   render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, false);
1072   EXPECT_EQ(Range(5), render_text->selection());
1073   render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, false);
1074   EXPECT_EQ(Range(6), render_text->selection());
1075   render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, true);
1076   EXPECT_EQ(Range(6, 5), render_text->selection());
1077   render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, true);
1078   EXPECT_EQ(Range(6, 4), render_text->selection());
1079   render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, false);
1080   EXPECT_EQ(Range(6), render_text->selection());
1081 
1082   // Right arrow on select ranging (4, 6).
1083   render_text->MoveCursor(LINE_BREAK, CURSOR_LEFT, false);
1084   EXPECT_EQ(Range(0), render_text->selection());
1085   render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false);
1086   EXPECT_EQ(Range(1), render_text->selection());
1087   render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false);
1088   EXPECT_EQ(Range(2), render_text->selection());
1089   render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false);
1090   EXPECT_EQ(Range(3), render_text->selection());
1091   render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false);
1092   EXPECT_EQ(Range(5), render_text->selection());
1093   render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false);
1094   EXPECT_EQ(Range(4), render_text->selection());
1095   render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, true);
1096   EXPECT_EQ(Range(4, 5), render_text->selection());
1097   render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, true);
1098   EXPECT_EQ(Range(4, 6), render_text->selection());
1099   render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false);
1100   EXPECT_EQ(Range(4), render_text->selection());
1101 }
1102 
TEST_F(RenderTextTest,CenteredDisplayOffset)1103 TEST_F(RenderTextTest, CenteredDisplayOffset) {
1104   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1105   render_text->SetText(ASCIIToUTF16("abcdefghij"));
1106   render_text->SetHorizontalAlignment(ALIGN_CENTER);
1107 
1108   const int kEnlargement = 10;
1109   const int content_width = render_text->GetContentWidth();
1110   Rect display_rect(0, 0, content_width / 2,
1111                     render_text->font_list().GetHeight());
1112   render_text->SetDisplayRect(display_rect);
1113 
1114   // Move the cursor to the beginning of the text and, by checking the cursor
1115   // bounds, make sure no empty space is to the left of the text.
1116   render_text->SetCursorPosition(0);
1117   EXPECT_EQ(display_rect.x(), render_text->GetUpdatedCursorBounds().x());
1118 
1119   // Widen the display rect and, by checking the cursor bounds, make sure no
1120   // empty space is introduced to the left of the text.
1121   display_rect.Inset(0, 0, -kEnlargement, 0);
1122   render_text->SetDisplayRect(display_rect);
1123   EXPECT_EQ(display_rect.x(), render_text->GetUpdatedCursorBounds().x());
1124 
1125   // Move the cursor to the end of the text and, by checking the cursor bounds,
1126   // make sure no empty space is to the right of the text.
1127   render_text->SetCursorPosition(render_text->text().length());
1128   EXPECT_EQ(display_rect.right(),
1129             render_text->GetUpdatedCursorBounds().right());
1130 
1131   // Widen the display rect and, by checking the cursor bounds, make sure no
1132   // empty space is introduced to the right of the text.
1133   display_rect.Inset(0, 0, -kEnlargement, 0);
1134   render_text->SetDisplayRect(display_rect);
1135   EXPECT_EQ(display_rect.right(),
1136             render_text->GetUpdatedCursorBounds().right());
1137 }
1138 #endif  // !defined(OS_MACOSX)
1139 
1140 // TODO(xji): Make these work on Windows.
1141 #if defined(OS_LINUX)
MoveLeftRightByWordVerifier(RenderText * render_text,const wchar_t * str)1142 void MoveLeftRightByWordVerifier(RenderText* render_text,
1143                                  const wchar_t* str) {
1144   render_text->SetText(WideToUTF16(str));
1145 
1146   // Test moving by word from left ro right.
1147   render_text->MoveCursor(LINE_BREAK, CURSOR_LEFT, false);
1148   bool first_word = true;
1149   while (true) {
1150     // First, test moving by word from a word break position, such as from
1151     // "|abc def" to "abc| def".
1152     SelectionModel start = render_text->selection_model();
1153     render_text->MoveCursor(WORD_BREAK, CURSOR_RIGHT, false);
1154     SelectionModel end = render_text->selection_model();
1155     if (end == start)  // reach the end.
1156       break;
1157 
1158     // For testing simplicity, each word is a 3-character word.
1159     int num_of_character_moves = first_word ? 3 : 4;
1160     first_word = false;
1161     render_text->MoveCursorTo(start);
1162     for (int j = 0; j < num_of_character_moves; ++j)
1163       render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false);
1164     EXPECT_EQ(end, render_text->selection_model());
1165 
1166     // Then, test moving by word from positions inside the word, such as from
1167     // "a|bc def" to "abc| def", and from "ab|c def" to "abc| def".
1168     for (int j = 1; j < num_of_character_moves; ++j) {
1169       render_text->MoveCursorTo(start);
1170       for (int k = 0; k < j; ++k)
1171         render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false);
1172       render_text->MoveCursor(WORD_BREAK, CURSOR_RIGHT, false);
1173       EXPECT_EQ(end, render_text->selection_model());
1174     }
1175   }
1176 
1177   // Test moving by word from right to left.
1178   render_text->MoveCursor(LINE_BREAK, CURSOR_RIGHT, false);
1179   first_word = true;
1180   while (true) {
1181     SelectionModel start = render_text->selection_model();
1182     render_text->MoveCursor(WORD_BREAK, CURSOR_LEFT, false);
1183     SelectionModel end = render_text->selection_model();
1184     if (end == start)  // reach the end.
1185       break;
1186 
1187     int num_of_character_moves = first_word ? 3 : 4;
1188     first_word = false;
1189     render_text->MoveCursorTo(start);
1190     for (int j = 0; j < num_of_character_moves; ++j)
1191       render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, false);
1192     EXPECT_EQ(end, render_text->selection_model());
1193 
1194     for (int j = 1; j < num_of_character_moves; ++j) {
1195       render_text->MoveCursorTo(start);
1196       for (int k = 0; k < j; ++k)
1197         render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, false);
1198       render_text->MoveCursor(WORD_BREAK, CURSOR_LEFT, false);
1199       EXPECT_EQ(end, render_text->selection_model());
1200     }
1201   }
1202 }
1203 
TEST_F(RenderTextTest,MoveLeftRightByWordInBidiText)1204 TEST_F(RenderTextTest, MoveLeftRightByWordInBidiText) {
1205   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1206 
1207   // For testing simplicity, each word is a 3-character word.
1208   std::vector<const wchar_t*> test;
1209   test.push_back(L"abc");
1210   test.push_back(L"abc def");
1211   test.push_back(L"\x05E1\x05E2\x05E3");
1212   test.push_back(L"\x05E1\x05E2\x05E3 \x05E4\x05E5\x05E6");
1213   test.push_back(L"abc \x05E1\x05E2\x05E3");
1214   test.push_back(L"abc def \x05E1\x05E2\x05E3 \x05E4\x05E5\x05E6");
1215   test.push_back(L"abc def hij \x05E1\x05E2\x05E3 \x05E4\x05E5\x05E6"
1216                  L" \x05E7\x05E8\x05E9");
1217 
1218   test.push_back(L"abc \x05E1\x05E2\x05E3 hij");
1219   test.push_back(L"abc def \x05E1\x05E2\x05E3 \x05E4\x05E5\x05E6 hij opq");
1220   test.push_back(L"abc def hij \x05E1\x05E2\x05E3 \x05E4\x05E5\x05E6"
1221                  L" \x05E7\x05E8\x05E9" L" opq rst uvw");
1222 
1223   test.push_back(L"\x05E1\x05E2\x05E3 abc");
1224   test.push_back(L"\x05E1\x05E2\x05E3 \x05E4\x05E5\x05E6 abc def");
1225   test.push_back(L"\x05E1\x05E2\x05E3 \x05E4\x05E5\x05E6 \x05E7\x05E8\x05E9"
1226                  L" abc def hij");
1227 
1228   test.push_back(L"\x05D1\x05D2\x05D3 abc \x05E1\x05E2\x05E3");
1229   test.push_back(L"\x05D1\x05D2\x05D3 \x05D4\x05D5\x05D6 abc def"
1230                  L" \x05E1\x05E2\x05E3 \x05E4\x05E5\x05E6");
1231   test.push_back(L"\x05D1\x05D2\x05D3 \x05D4\x05D5\x05D6 \x05D7\x05D8\x05D9"
1232                  L" abc def hij \x05E1\x05E2\x05E3 \x05E4\x05E5\x05E6"
1233                  L" \x05E7\x05E8\x05E9");
1234 
1235   for (size_t i = 0; i < test.size(); ++i)
1236     MoveLeftRightByWordVerifier(render_text.get(), test[i]);
1237 }
1238 
TEST_F(RenderTextTest,MoveLeftRightByWordInBidiText_TestEndOfText)1239 TEST_F(RenderTextTest, MoveLeftRightByWordInBidiText_TestEndOfText) {
1240   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1241 
1242   render_text->SetText(WideToUTF16(L"ab\x05E1"));
1243   // Moving the cursor by word from "abC|" to the left should return "|abC".
1244   // But since end of text is always treated as a word break, it returns
1245   // position "ab|C".
1246   // TODO(xji): Need to make it work as expected.
1247   render_text->MoveCursor(LINE_BREAK, CURSOR_RIGHT, false);
1248   render_text->MoveCursor(WORD_BREAK, CURSOR_LEFT, false);
1249   // EXPECT_EQ(SelectionModel(), render_text->selection_model());
1250 
1251   // Moving the cursor by word from "|abC" to the right returns "abC|".
1252   render_text->MoveCursor(LINE_BREAK, CURSOR_LEFT, false);
1253   render_text->MoveCursor(WORD_BREAK, CURSOR_RIGHT, false);
1254   EXPECT_EQ(SelectionModel(3, CURSOR_FORWARD), render_text->selection_model());
1255 
1256   render_text->SetText(WideToUTF16(L"\x05E1\x05E2" L"a"));
1257   // For logical text "BCa", moving the cursor by word from "aCB|" to the left
1258   // returns "|aCB".
1259   render_text->MoveCursor(LINE_BREAK, CURSOR_RIGHT, false);
1260   render_text->MoveCursor(WORD_BREAK, CURSOR_LEFT, false);
1261   EXPECT_EQ(SelectionModel(3, CURSOR_FORWARD), render_text->selection_model());
1262 
1263   // Moving the cursor by word from "|aCB" to the right should return "aCB|".
1264   // But since end of text is always treated as a word break, it returns
1265   // position "a|CB".
1266   // TODO(xji): Need to make it work as expected.
1267   render_text->MoveCursor(LINE_BREAK, CURSOR_LEFT, false);
1268   render_text->MoveCursor(WORD_BREAK, CURSOR_RIGHT, false);
1269   // EXPECT_EQ(SelectionModel(), render_text->selection_model());
1270 }
1271 
TEST_F(RenderTextTest,MoveLeftRightByWordInTextWithMultiSpaces)1272 TEST_F(RenderTextTest, MoveLeftRightByWordInTextWithMultiSpaces) {
1273   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1274   render_text->SetText(WideToUTF16(L"abc     def"));
1275   render_text->MoveCursorTo(SelectionModel(5, CURSOR_FORWARD));
1276   render_text->MoveCursor(WORD_BREAK, CURSOR_RIGHT, false);
1277   EXPECT_EQ(11U, render_text->cursor_position());
1278 
1279   render_text->MoveCursorTo(SelectionModel(5, CURSOR_FORWARD));
1280   render_text->MoveCursor(WORD_BREAK, CURSOR_LEFT, false);
1281   EXPECT_EQ(0U, render_text->cursor_position());
1282 }
1283 
TEST_F(RenderTextTest,MoveLeftRightByWordInChineseText)1284 TEST_F(RenderTextTest, MoveLeftRightByWordInChineseText) {
1285   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1286   render_text->SetText(WideToUTF16(L"\x6211\x4EEC\x53BB\x516C\x56ED\x73A9"));
1287   render_text->MoveCursor(LINE_BREAK, CURSOR_LEFT, false);
1288   EXPECT_EQ(0U, render_text->cursor_position());
1289   render_text->MoveCursor(WORD_BREAK, CURSOR_RIGHT, false);
1290   EXPECT_EQ(2U, render_text->cursor_position());
1291   render_text->MoveCursor(WORD_BREAK, CURSOR_RIGHT, false);
1292   EXPECT_EQ(3U, render_text->cursor_position());
1293   render_text->MoveCursor(WORD_BREAK, CURSOR_RIGHT, false);
1294   EXPECT_EQ(5U, render_text->cursor_position());
1295   render_text->MoveCursor(WORD_BREAK, CURSOR_RIGHT, false);
1296   EXPECT_EQ(6U, render_text->cursor_position());
1297   render_text->MoveCursor(WORD_BREAK, CURSOR_RIGHT, false);
1298   EXPECT_EQ(6U, render_text->cursor_position());
1299 }
1300 #endif
1301 
1302 // TODO(ckocagil): Remove when RenderTextWin goes away.
1303 #if defined(OS_WIN)
TEST_F(RenderTextTest,Win_LogicalClusters)1304 TEST_F(RenderTextTest, Win_LogicalClusters) {
1305   scoped_ptr<RenderTextWin> render_text(
1306       static_cast<RenderTextWin*>(RenderText::CreateNativeInstance()));
1307 
1308   const base::string16 test_string =
1309       WideToUTF16(L"\x0930\x0930\x0930\x0930\x0930");
1310   render_text->SetText(test_string);
1311   render_text->EnsureLayout();
1312   ASSERT_EQ(1U, render_text->runs_.size());
1313   WORD* logical_clusters = render_text->runs_[0]->logical_clusters.get();
1314   for (size_t i = 0; i < test_string.length(); ++i)
1315     EXPECT_EQ(i, logical_clusters[i]);
1316 }
1317 #endif  // defined(OS_WIN)
1318 
TEST_F(RenderTextTest,StringSizeSanity)1319 TEST_F(RenderTextTest, StringSizeSanity) {
1320   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1321   render_text->SetText(UTF8ToUTF16("Hello World"));
1322   const Size string_size = render_text->GetStringSize();
1323   EXPECT_GT(string_size.width(), 0);
1324   EXPECT_GT(string_size.height(), 0);
1325 }
1326 
TEST_F(RenderTextTest,StringSizeLongStrings)1327 TEST_F(RenderTextTest, StringSizeLongStrings) {
1328   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1329   Size previous_string_size;
1330   for (size_t length = 10; length < 1000000; length *= 10) {
1331     render_text->SetText(base::string16(length, 'a'));
1332     const Size string_size = render_text->GetStringSize();
1333     EXPECT_GT(string_size.width(), previous_string_size.width());
1334     EXPECT_GT(string_size.height(), 0);
1335     previous_string_size = string_size;
1336   }
1337 }
1338 
1339 // TODO(asvitkine): This test fails because PlatformFontMac uses point font
1340 //                  sizes instead of pixel sizes like other implementations.
1341 #if !defined(OS_MACOSX)
TEST_F(RenderTextTest,StringSizeEmptyString)1342 TEST_F(RenderTextTest, StringSizeEmptyString) {
1343   // Ascent and descent of Arial and Symbol are different on most platforms.
1344   const FontList font_list("Arial,Symbol, 16px");
1345   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1346   render_text->SetFontList(font_list);
1347   render_text->SetDisplayRect(Rect(0, 0, 0, font_list.GetHeight()));
1348 
1349   // The empty string respects FontList metrics for non-zero height
1350   // and baseline.
1351   render_text->SetText(base::string16());
1352   EXPECT_EQ(font_list.GetHeight(), render_text->GetStringSize().height());
1353   EXPECT_EQ(0, render_text->GetStringSize().width());
1354   EXPECT_EQ(font_list.GetBaseline(), render_text->GetBaseline());
1355 
1356   render_text->SetText(UTF8ToUTF16(" "));
1357   EXPECT_EQ(font_list.GetHeight(), render_text->GetStringSize().height());
1358   EXPECT_EQ(font_list.GetBaseline(), render_text->GetBaseline());
1359 }
1360 #endif  // !defined(OS_MACOSX)
1361 
TEST_F(RenderTextTest,StringSizeRespectsFontListMetrics)1362 TEST_F(RenderTextTest, StringSizeRespectsFontListMetrics) {
1363   // Check that Arial and Symbol have different font metrics.
1364   Font arial_font("Arial", 16);
1365   ASSERT_EQ("arial",
1366             base::StringToLowerASCII(arial_font.GetActualFontNameForTesting()));
1367   Font symbol_font("Symbol", 16);
1368   ASSERT_EQ("symbol",
1369             base::StringToLowerASCII(
1370                 symbol_font.GetActualFontNameForTesting()));
1371   EXPECT_NE(arial_font.GetHeight(), symbol_font.GetHeight());
1372   EXPECT_NE(arial_font.GetBaseline(), symbol_font.GetBaseline());
1373   // "a" should be rendered with Arial, not with Symbol.
1374   const char* arial_font_text = "a";
1375   // "®" (registered trademark symbol) should be rendered with Symbol,
1376   // not with Arial.
1377   const char* symbol_font_text = "\xC2\xAE";
1378 
1379   Font smaller_font = arial_font;
1380   Font larger_font = symbol_font;
1381   const char* smaller_font_text = arial_font_text;
1382   const char* larger_font_text = symbol_font_text;
1383   if (symbol_font.GetHeight() < arial_font.GetHeight() &&
1384       symbol_font.GetBaseline() < arial_font.GetBaseline()) {
1385     std::swap(smaller_font, larger_font);
1386     std::swap(smaller_font_text, larger_font_text);
1387   }
1388   ASSERT_LT(smaller_font.GetHeight(), larger_font.GetHeight());
1389   ASSERT_LT(smaller_font.GetBaseline(), larger_font.GetBaseline());
1390 
1391   // Check |smaller_font_text| is rendered with the smaller font.
1392   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1393   render_text->SetText(UTF8ToUTF16(smaller_font_text));
1394   render_text->SetFontList(FontList(smaller_font));
1395   render_text->SetDisplayRect(Rect(0, 0, 0,
1396                                    render_text->font_list().GetHeight()));
1397   EXPECT_EQ(smaller_font.GetHeight(), render_text->GetStringSize().height());
1398   EXPECT_EQ(smaller_font.GetBaseline(), render_text->GetBaseline());
1399 
1400   // Layout the same text with mixed fonts.  The text should be rendered with
1401   // the smaller font, but the height and baseline are determined with the
1402   // metrics of the font list, which is equal to the larger font.
1403   std::vector<Font> fonts;
1404   fonts.push_back(smaller_font);  // The primary font is the smaller font.
1405   fonts.push_back(larger_font);
1406   const FontList font_list(fonts);
1407   render_text->SetFontList(font_list);
1408   render_text->SetDisplayRect(Rect(0, 0, 0,
1409                                    render_text->font_list().GetHeight()));
1410   EXPECT_LT(smaller_font.GetHeight(), render_text->GetStringSize().height());
1411   EXPECT_LT(smaller_font.GetBaseline(), render_text->GetBaseline());
1412   EXPECT_EQ(font_list.GetHeight(), render_text->GetStringSize().height());
1413   EXPECT_EQ(font_list.GetBaseline(), render_text->GetBaseline());
1414 }
1415 
TEST_F(RenderTextTest,SetFontList)1416 TEST_F(RenderTextTest, SetFontList) {
1417   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1418   render_text->SetFontList(FontList("Arial,Symbol, 13px"));
1419   const std::vector<Font>& fonts = render_text->font_list().GetFonts();
1420   ASSERT_EQ(2U, fonts.size());
1421   EXPECT_EQ("Arial", fonts[0].GetFontName());
1422   EXPECT_EQ("Symbol", fonts[1].GetFontName());
1423   EXPECT_EQ(13, render_text->font_list().GetFontSize());
1424 }
1425 
TEST_F(RenderTextTest,StringSizeBoldWidth)1426 TEST_F(RenderTextTest, StringSizeBoldWidth) {
1427   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1428   render_text->SetText(UTF8ToUTF16("Hello World"));
1429 
1430   const int plain_width = render_text->GetStringSize().width();
1431   EXPECT_GT(plain_width, 0);
1432 
1433   // Apply a bold style and check that the new width is greater.
1434   render_text->SetStyle(BOLD, true);
1435   const int bold_width = render_text->GetStringSize().width();
1436   EXPECT_GT(bold_width, plain_width);
1437 
1438   // Now, apply a plain style over the first word only.
1439   render_text->ApplyStyle(BOLD, false, Range(0, 5));
1440   const int plain_bold_width = render_text->GetStringSize().width();
1441   EXPECT_GT(plain_bold_width, plain_width);
1442   EXPECT_LT(plain_bold_width, bold_width);
1443 }
1444 
TEST_F(RenderTextTest,StringSizeHeight)1445 TEST_F(RenderTextTest, StringSizeHeight) {
1446   base::string16 cases[] = {
1447     WideToUTF16(L"Hello World!"),  // English
1448     WideToUTF16(L"\x6328\x62f6"),  // Japanese
1449     WideToUTF16(L"\x0915\x093f"),  // Hindi
1450     WideToUTF16(L"\x05e0\x05b8"),  // Hebrew
1451   };
1452 
1453   const FontList default_font_list;
1454   const FontList& larger_font_list = default_font_list.DeriveWithSizeDelta(24);
1455   EXPECT_GT(larger_font_list.GetHeight(), default_font_list.GetHeight());
1456 
1457   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
1458     scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1459     render_text->SetFontList(default_font_list);
1460     render_text->SetText(cases[i]);
1461 
1462     const int height1 = render_text->GetStringSize().height();
1463     EXPECT_GT(height1, 0);
1464 
1465     // Check that setting the larger font increases the height.
1466     render_text->SetFontList(larger_font_list);
1467     const int height2 = render_text->GetStringSize().height();
1468     EXPECT_GT(height2, height1);
1469   }
1470 }
1471 
TEST_F(RenderTextTest,GetBaselineSanity)1472 TEST_F(RenderTextTest, GetBaselineSanity) {
1473   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1474   render_text->SetText(UTF8ToUTF16("Hello World"));
1475   const int baseline = render_text->GetBaseline();
1476   EXPECT_GT(baseline, 0);
1477 }
1478 
TEST_F(RenderTextTest,CursorBoundsInReplacementMode)1479 TEST_F(RenderTextTest, CursorBoundsInReplacementMode) {
1480   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1481   render_text->SetText(ASCIIToUTF16("abcdefg"));
1482   render_text->SetDisplayRect(Rect(100, 17));
1483   SelectionModel sel_b(1, CURSOR_FORWARD);
1484   SelectionModel sel_c(2, CURSOR_FORWARD);
1485   Rect cursor_around_b = render_text->GetCursorBounds(sel_b, false);
1486   Rect cursor_before_b = render_text->GetCursorBounds(sel_b, true);
1487   Rect cursor_before_c = render_text->GetCursorBounds(sel_c, true);
1488   EXPECT_EQ(cursor_around_b.x(), cursor_before_b.x());
1489   EXPECT_EQ(cursor_around_b.right(), cursor_before_c.x());
1490 }
1491 
TEST_F(RenderTextTest,GetTextOffset)1492 TEST_F(RenderTextTest, GetTextOffset) {
1493   // The default horizontal text offset differs for LTR and RTL, and is only set
1494   // when the RenderText object is created.  This test will check the default in
1495   // LTR mode, and the next test will check the RTL default.
1496   const bool was_rtl = base::i18n::IsRTL();
1497   SetRTL(false);
1498   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1499   render_text->SetText(ASCIIToUTF16("abcdefg"));
1500   render_text->SetFontList(FontList("Arial, 13px"));
1501 
1502   // Set display area's size equal to the font size.
1503   const Size font_size(render_text->GetContentWidth(),
1504                        render_text->font_list().GetHeight());
1505   Rect display_rect(font_size);
1506   render_text->SetDisplayRect(display_rect);
1507 
1508   Vector2d offset = render_text->GetLineOffset(0);
1509   EXPECT_TRUE(offset.IsZero());
1510 
1511   const int kEnlargementX = 2;
1512   display_rect.Inset(0, 0, -kEnlargementX, 0);
1513   render_text->SetDisplayRect(display_rect);
1514 
1515   // Check the default horizontal alignment.
1516   offset = render_text->GetLineOffset(0);
1517   EXPECT_EQ(0, offset.x());
1518 
1519   // Check explicitly setting the horizontal alignment.
1520   render_text->SetHorizontalAlignment(ALIGN_LEFT);
1521   offset = render_text->GetLineOffset(0);
1522   EXPECT_EQ(0, offset.x());
1523   render_text->SetHorizontalAlignment(ALIGN_CENTER);
1524   offset = render_text->GetLineOffset(0);
1525   EXPECT_EQ(kEnlargementX / 2, offset.x());
1526   render_text->SetHorizontalAlignment(ALIGN_RIGHT);
1527   offset = render_text->GetLineOffset(0);
1528   EXPECT_EQ(kEnlargementX, offset.x());
1529 
1530   // Check that text is vertically centered within taller display rects.
1531   const int kEnlargementY = display_rect.height();
1532   display_rect.Inset(0, 0, 0, -kEnlargementY);
1533   render_text->SetDisplayRect(display_rect);
1534   const Vector2d prev_offset = render_text->GetLineOffset(0);
1535   display_rect.Inset(0, 0, 0, -2 * kEnlargementY);
1536   render_text->SetDisplayRect(display_rect);
1537   offset = render_text->GetLineOffset(0);
1538   EXPECT_EQ(prev_offset.y() + kEnlargementY, offset.y());
1539 
1540   SetRTL(was_rtl);
1541 }
1542 
TEST_F(RenderTextTest,GetTextOffsetHorizontalDefaultInRTL)1543 TEST_F(RenderTextTest, GetTextOffsetHorizontalDefaultInRTL) {
1544   // This only checks the default horizontal alignment in RTL mode; all other
1545   // GetLineOffset(0) attributes are checked by the test above.
1546   const bool was_rtl = base::i18n::IsRTL();
1547   SetRTL(true);
1548   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1549   render_text->SetText(ASCIIToUTF16("abcdefg"));
1550   render_text->SetFontList(FontList("Arial, 13px"));
1551   const int kEnlargement = 2;
1552   const Size font_size(render_text->GetContentWidth() + kEnlargement,
1553                        render_text->GetStringSize().height());
1554   Rect display_rect(font_size);
1555   render_text->SetDisplayRect(display_rect);
1556   Vector2d offset = render_text->GetLineOffset(0);
1557   EXPECT_EQ(kEnlargement, offset.x());
1558   SetRTL(was_rtl);
1559 }
1560 
TEST_F(RenderTextTest,SetDisplayOffset)1561 TEST_F(RenderTextTest, SetDisplayOffset) {
1562   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1563   render_text->SetText(ASCIIToUTF16("abcdefg"));
1564   render_text->SetFontList(FontList("Arial, 13px"));
1565 
1566   const Size font_size(render_text->GetContentWidth(),
1567                        render_text->font_list().GetHeight());
1568   const int kEnlargement = 10;
1569 
1570   // Set display width |kEnlargement| pixels greater than content width and test
1571   // different possible situations. In this case the only possible display
1572   // offset is zero.
1573   Rect display_rect(font_size);
1574   display_rect.Inset(0, 0, -kEnlargement, 0);
1575   render_text->SetDisplayRect(display_rect);
1576 
1577   struct {
1578     HorizontalAlignment alignment;
1579     int offset;
1580   } small_content_cases[] = {
1581     { ALIGN_LEFT, -kEnlargement },
1582     { ALIGN_LEFT, 0 },
1583     { ALIGN_LEFT, kEnlargement },
1584     { ALIGN_RIGHT, -kEnlargement },
1585     { ALIGN_RIGHT, 0 },
1586     { ALIGN_RIGHT, kEnlargement },
1587     { ALIGN_CENTER, -kEnlargement },
1588     { ALIGN_CENTER, 0 },
1589     { ALIGN_CENTER, kEnlargement },
1590   };
1591 
1592   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(small_content_cases); i++) {
1593     render_text->SetHorizontalAlignment(small_content_cases[i].alignment);
1594     render_text->SetDisplayOffset(small_content_cases[i].offset);
1595     EXPECT_EQ(0, render_text->GetUpdatedDisplayOffset().x());
1596   }
1597 
1598   // Set display width |kEnlargement| pixels less than content width and test
1599   // different possible situations.
1600   display_rect = Rect(font_size);
1601   display_rect.Inset(0, 0, kEnlargement, 0);
1602   render_text->SetDisplayRect(display_rect);
1603 
1604   struct {
1605     HorizontalAlignment alignment;
1606     int offset;
1607     int expected_offset;
1608   } large_content_cases[] = {
1609     // When text is left-aligned, display offset can be in range
1610     // [-kEnlargement, 0].
1611     { ALIGN_LEFT, -2 * kEnlargement, -kEnlargement },
1612     { ALIGN_LEFT, -kEnlargement / 2, -kEnlargement / 2 },
1613     { ALIGN_LEFT, kEnlargement, 0 },
1614     // When text is right-aligned, display offset can be in range
1615     // [0, kEnlargement].
1616     { ALIGN_RIGHT, -kEnlargement, 0 },
1617     { ALIGN_RIGHT, kEnlargement / 2, kEnlargement / 2 },
1618     { ALIGN_RIGHT, 2 * kEnlargement, kEnlargement },
1619     // When text is center-aligned, display offset can be in range
1620     // [-kEnlargement / 2 - 1, (kEnlargement - 1) / 2].
1621     { ALIGN_CENTER, -kEnlargement, -kEnlargement / 2 - 1 },
1622     { ALIGN_CENTER, -kEnlargement / 4, -kEnlargement / 4 },
1623     { ALIGN_CENTER, kEnlargement / 4, kEnlargement / 4 },
1624     { ALIGN_CENTER, kEnlargement, (kEnlargement - 1) / 2 },
1625   };
1626 
1627   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(large_content_cases); i++) {
1628     render_text->SetHorizontalAlignment(large_content_cases[i].alignment);
1629     render_text->SetDisplayOffset(large_content_cases[i].offset);
1630     EXPECT_EQ(large_content_cases[i].expected_offset,
1631               render_text->GetUpdatedDisplayOffset().x());
1632   }
1633 }
1634 
TEST_F(RenderTextTest,SameFontForParentheses)1635 TEST_F(RenderTextTest, SameFontForParentheses) {
1636   struct {
1637     const base::char16 left_char;
1638     const base::char16 right_char;
1639   } punctuation_pairs[] = {
1640     { '(', ')' },
1641     { '{', '}' },
1642     { '<', '>' },
1643   };
1644   struct {
1645     base::string16 text;
1646   } cases[] = {
1647     // English(English)
1648     { WideToUTF16(L"Hello World(a)") },
1649     // English(English)English
1650     { WideToUTF16(L"Hello World(a)Hello World") },
1651 
1652     // Japanese(English)
1653     { WideToUTF16(L"\x6328\x62f6(a)") },
1654     // Japanese(English)Japanese
1655     { WideToUTF16(L"\x6328\x62f6(a)\x6328\x62f6") },
1656     // English(Japanese)English
1657     { WideToUTF16(L"Hello World(\x6328\x62f6)Hello World") },
1658 
1659     // Hindi(English)
1660     { WideToUTF16(L"\x0915\x093f(a)") },
1661     // Hindi(English)Hindi
1662     { WideToUTF16(L"\x0915\x093f(a)\x0915\x093f") },
1663     // English(Hindi)English
1664     { WideToUTF16(L"Hello World(\x0915\x093f)Hello World") },
1665 
1666     // Hebrew(English)
1667     { WideToUTF16(L"\x05e0\x05b8(a)") },
1668     // Hebrew(English)Hebrew
1669     { WideToUTF16(L"\x05e0\x05b8(a)\x05e0\x05b8") },
1670     // English(Hebrew)English
1671     { WideToUTF16(L"Hello World(\x05e0\x05b8)Hello World") },
1672   };
1673 
1674   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1675   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
1676     base::string16 text = cases[i].text;
1677     const size_t start_paren_char_index = text.find('(');
1678     ASSERT_NE(base::string16::npos, start_paren_char_index);
1679     const size_t end_paren_char_index = text.find(')');
1680     ASSERT_NE(base::string16::npos, end_paren_char_index);
1681 
1682     for (size_t j = 0; j < ARRAYSIZE_UNSAFE(punctuation_pairs); ++j) {
1683       text[start_paren_char_index] = punctuation_pairs[j].left_char;
1684       text[end_paren_char_index] = punctuation_pairs[j].right_char;
1685       render_text->SetText(text);
1686 
1687       const std::vector<RenderText::FontSpan> spans =
1688           render_text->GetFontSpansForTesting();
1689 
1690       int start_paren_span_index = -1;
1691       int end_paren_span_index = -1;
1692       for (size_t k = 0; k < spans.size(); ++k) {
1693         if (IndexInRange(spans[k].second, start_paren_char_index))
1694           start_paren_span_index = k;
1695         if (IndexInRange(spans[k].second, end_paren_char_index))
1696           end_paren_span_index = k;
1697       }
1698       ASSERT_NE(-1, start_paren_span_index);
1699       ASSERT_NE(-1, end_paren_span_index);
1700 
1701       const Font& start_font = spans[start_paren_span_index].first;
1702       const Font& end_font = spans[end_paren_span_index].first;
1703       EXPECT_EQ(start_font.GetFontName(), end_font.GetFontName());
1704       EXPECT_EQ(start_font.GetFontSize(), end_font.GetFontSize());
1705       EXPECT_EQ(start_font.GetStyle(), end_font.GetStyle());
1706     }
1707   }
1708 }
1709 
1710 // Make sure the caret width is always >=1 so that the correct
1711 // caret is drawn at high DPI. crbug.com/164100.
TEST_F(RenderTextTest,CaretWidth)1712 TEST_F(RenderTextTest, CaretWidth) {
1713   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1714   render_text->SetText(ASCIIToUTF16("abcdefg"));
1715   EXPECT_GE(render_text->GetUpdatedCursorBounds().width(), 1);
1716 }
1717 
TEST_F(RenderTextTest,SelectWord)1718 TEST_F(RenderTextTest, SelectWord) {
1719   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1720   render_text->SetText(ASCIIToUTF16(" foo  a.bc.d bar"));
1721 
1722   struct {
1723     size_t cursor;
1724     size_t selection_start;
1725     size_t selection_end;
1726   } cases[] = {
1727     { 0,   0,  1 },
1728     { 1,   1,  4 },
1729     { 2,   1,  4 },
1730     { 3,   1,  4 },
1731     { 4,   4,  6 },
1732     { 5,   4,  6 },
1733     { 6,   6,  7 },
1734     { 7,   7,  8 },
1735     { 8,   8, 10 },
1736     { 9,   8, 10 },
1737     { 10, 10, 11 },
1738     { 11, 11, 12 },
1739     { 12, 12, 13 },
1740     { 13, 13, 16 },
1741     { 14, 13, 16 },
1742     { 15, 13, 16 },
1743     { 16, 13, 16 },
1744   };
1745 
1746   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
1747     render_text->SetCursorPosition(cases[i].cursor);
1748     render_text->SelectWord();
1749     EXPECT_EQ(Range(cases[i].selection_start, cases[i].selection_end),
1750               render_text->selection());
1751   }
1752 }
1753 
1754 // Make sure the last word is selected when the cursor is at text.length().
TEST_F(RenderTextTest,LastWordSelected)1755 TEST_F(RenderTextTest, LastWordSelected) {
1756   const std::string kTestURL1 = "http://www.google.com";
1757   const std::string kTestURL2 = "http://www.google.com/something/";
1758 
1759   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1760 
1761   render_text->SetText(ASCIIToUTF16(kTestURL1));
1762   render_text->SetCursorPosition(kTestURL1.length());
1763   render_text->SelectWord();
1764   EXPECT_EQ(ASCIIToUTF16("com"), GetSelectedText(render_text.get()));
1765   EXPECT_FALSE(render_text->selection().is_reversed());
1766 
1767   render_text->SetText(ASCIIToUTF16(kTestURL2));
1768   render_text->SetCursorPosition(kTestURL2.length());
1769   render_text->SelectWord();
1770   EXPECT_EQ(ASCIIToUTF16("/"), GetSelectedText(render_text.get()));
1771   EXPECT_FALSE(render_text->selection().is_reversed());
1772 }
1773 
1774 // When given a non-empty selection, SelectWord should expand the selection to
1775 // nearest word boundaries.
TEST_F(RenderTextTest,SelectMultipleWords)1776 TEST_F(RenderTextTest, SelectMultipleWords) {
1777   const std::string kTestURL = "http://www.google.com";
1778 
1779   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1780 
1781   render_text->SetText(ASCIIToUTF16(kTestURL));
1782   render_text->SelectRange(Range(16, 20));
1783   render_text->SelectWord();
1784   EXPECT_EQ(ASCIIToUTF16("google.com"), GetSelectedText(render_text.get()));
1785   EXPECT_FALSE(render_text->selection().is_reversed());
1786 
1787   // SelectWord should preserve the selection direction.
1788   render_text->SelectRange(Range(20, 16));
1789   render_text->SelectWord();
1790   EXPECT_EQ(ASCIIToUTF16("google.com"), GetSelectedText(render_text.get()));
1791   EXPECT_TRUE(render_text->selection().is_reversed());
1792 }
1793 
1794 // TODO(asvitkine): Cursor movements tests disabled on Mac because RenderTextMac
1795 //                  does not implement this yet. http://crbug.com/131618
1796 #if !defined(OS_MACOSX)
TEST_F(RenderTextTest,DisplayRectShowsCursorLTR)1797 TEST_F(RenderTextTest, DisplayRectShowsCursorLTR) {
1798   ASSERT_FALSE(base::i18n::IsRTL());
1799   ASSERT_FALSE(base::i18n::ICUIsRTL());
1800 
1801   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1802   render_text->SetText(WideToUTF16(L"abcdefghijklmnopqrstuvwxzyabcdefg"));
1803   render_text->MoveCursorTo(SelectionModel(render_text->text().length(),
1804                                            CURSOR_FORWARD));
1805   int width = render_text->GetStringSize().width();
1806   ASSERT_GT(width, 10);
1807 
1808   // Ensure that the cursor is placed at the width of its preceding text.
1809   render_text->SetDisplayRect(Rect(width + 10, 1));
1810   EXPECT_EQ(width, render_text->GetUpdatedCursorBounds().x());
1811 
1812   // Ensure that shrinking the display rectangle keeps the cursor in view.
1813   render_text->SetDisplayRect(Rect(width - 10, 1));
1814   EXPECT_EQ(render_text->display_rect().width(),
1815             render_text->GetUpdatedCursorBounds().right());
1816 
1817   // Ensure that the text will pan to fill its expanding display rectangle.
1818   render_text->SetDisplayRect(Rect(width - 5, 1));
1819   EXPECT_EQ(render_text->display_rect().width(),
1820             render_text->GetUpdatedCursorBounds().right());
1821 
1822   // Ensure that a sufficiently large display rectangle shows all the text.
1823   render_text->SetDisplayRect(Rect(width + 10, 1));
1824   EXPECT_EQ(width, render_text->GetUpdatedCursorBounds().x());
1825 
1826   // Repeat the test with RTL text.
1827   render_text->SetText(WideToUTF16(L"\x5d0\x5d1\x5d2\x5d3\x5d4\x5d5\x5d6\x5d7"
1828       L"\x5d8\x5d9\x5da\x5db\x5dc\x5dd\x5de\x5df"));
1829   render_text->MoveCursorTo(SelectionModel(0, CURSOR_FORWARD));
1830   width = render_text->GetStringSize().width();
1831   ASSERT_GT(width, 10);
1832 
1833   // Ensure that the cursor is placed at the width of its preceding text.
1834   render_text->SetDisplayRect(Rect(width + 10, 1));
1835   EXPECT_EQ(width, render_text->GetUpdatedCursorBounds().x());
1836 
1837   // Ensure that shrinking the display rectangle keeps the cursor in view.
1838   render_text->SetDisplayRect(Rect(width - 10, 1));
1839   EXPECT_EQ(render_text->display_rect().width(),
1840             render_text->GetUpdatedCursorBounds().right());
1841 
1842   // Ensure that the text will pan to fill its expanding display rectangle.
1843   render_text->SetDisplayRect(Rect(width - 5, 1));
1844   EXPECT_EQ(render_text->display_rect().width(),
1845             render_text->GetUpdatedCursorBounds().right());
1846 
1847   // Ensure that a sufficiently large display rectangle shows all the text.
1848   render_text->SetDisplayRect(Rect(width + 10, 1));
1849   EXPECT_EQ(width, render_text->GetUpdatedCursorBounds().x());
1850 }
1851 
TEST_F(RenderTextTest,DisplayRectShowsCursorRTL)1852 TEST_F(RenderTextTest, DisplayRectShowsCursorRTL) {
1853   // Set the application default text direction to RTL.
1854   const bool was_rtl = base::i18n::IsRTL();
1855   SetRTL(true);
1856 
1857   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1858   render_text->SetText(WideToUTF16(L"abcdefghijklmnopqrstuvwxzyabcdefg"));
1859   render_text->MoveCursorTo(SelectionModel(0, CURSOR_FORWARD));
1860   int width = render_text->GetStringSize().width();
1861   ASSERT_GT(width, 10);
1862 
1863   // Ensure that the cursor is placed at the width of its preceding text.
1864   render_text->SetDisplayRect(Rect(width + 10, 1));
1865   EXPECT_EQ(render_text->display_rect().width() - width - 1,
1866             render_text->GetUpdatedCursorBounds().x());
1867 
1868   // Ensure that shrinking the display rectangle keeps the cursor in view.
1869   render_text->SetDisplayRect(Rect(width - 10, 1));
1870   EXPECT_EQ(0, render_text->GetUpdatedCursorBounds().x());
1871 
1872   // Ensure that the text will pan to fill its expanding display rectangle.
1873   render_text->SetDisplayRect(Rect(width - 5, 1));
1874   EXPECT_EQ(0, render_text->GetUpdatedCursorBounds().x());
1875 
1876   // Ensure that a sufficiently large display rectangle shows all the text.
1877   render_text->SetDisplayRect(Rect(width + 10, 1));
1878   EXPECT_EQ(render_text->display_rect().width() - width - 1,
1879             render_text->GetUpdatedCursorBounds().x());
1880 
1881   // Repeat the test with RTL text.
1882   render_text->SetText(WideToUTF16(L"\x5d0\x5d1\x5d2\x5d3\x5d4\x5d5\x5d6\x5d7"
1883       L"\x5d8\x5d9\x5da\x5db\x5dc\x5dd\x5de\x5df"));
1884   render_text->MoveCursorTo(SelectionModel(render_text->text().length(),
1885                                            CURSOR_FORWARD));
1886   width = render_text->GetStringSize().width();
1887   ASSERT_GT(width, 10);
1888 
1889   // Ensure that the cursor is placed at the width of its preceding text.
1890   render_text->SetDisplayRect(Rect(width + 10, 1));
1891   EXPECT_EQ(render_text->display_rect().width() - width - 1,
1892             render_text->GetUpdatedCursorBounds().x());
1893 
1894   // Ensure that shrinking the display rectangle keeps the cursor in view.
1895   render_text->SetDisplayRect(Rect(width - 10, 1));
1896   EXPECT_EQ(0, render_text->GetUpdatedCursorBounds().x());
1897 
1898   // Ensure that the text will pan to fill its expanding display rectangle.
1899   render_text->SetDisplayRect(Rect(width - 5, 1));
1900   EXPECT_EQ(0, render_text->GetUpdatedCursorBounds().x());
1901 
1902   // Ensure that a sufficiently large display rectangle shows all the text.
1903   render_text->SetDisplayRect(Rect(width + 10, 1));
1904   EXPECT_EQ(render_text->display_rect().width() - width - 1,
1905             render_text->GetUpdatedCursorBounds().x());
1906 
1907   // Reset the application default text direction to LTR.
1908   SetRTL(was_rtl);
1909   EXPECT_EQ(was_rtl, base::i18n::IsRTL());
1910 }
1911 #endif  // !defined(OS_MACOSX)
1912 
1913 // Changing colors between or inside ligated glyphs should not break shaping.
TEST_F(RenderTextTest,SelectionKeepsLigatures)1914 TEST_F(RenderTextTest, SelectionKeepsLigatures) {
1915   const wchar_t* kTestStrings[] = { L"\x644\x623", L"\x633\x627" };
1916   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
1917   render_text->set_selection_color(SK_ColorRED);
1918   Canvas canvas;
1919 
1920   for (size_t i = 0; i < arraysize(kTestStrings); ++i) {
1921     render_text->SetText(WideToUTF16(kTestStrings[i]));
1922     const int expected_width = render_text->GetStringSize().width();
1923     render_text->MoveCursorTo(SelectionModel(Range(0, 1), CURSOR_FORWARD));
1924     EXPECT_EQ(expected_width, render_text->GetStringSize().width());
1925     // Drawing the text should not DCHECK or crash; see http://crbug.com/262119
1926     render_text->Draw(&canvas);
1927     render_text->MoveCursorTo(SelectionModel(0, CURSOR_FORWARD));
1928   }
1929 }
1930 
1931 #if defined(OS_WIN)
1932 // TODO(ckocagil): Enable for RenderTextHarfBuzz after implementing multiline.
1933 // Ensure strings wrap onto multiple lines for a small available width.
TEST_F(RenderTextTest,Multiline_MinWidth)1934 TEST_F(RenderTextTest, Multiline_MinWidth) {
1935   const wchar_t* kTestStrings[] = { kWeak, kLtr, kLtrRtl, kLtrRtlLtr, kRtl,
1936                                     kRtlLtr, kRtlLtrRtl };
1937 
1938   scoped_ptr<RenderTextWin> render_text(
1939       static_cast<RenderTextWin*>(RenderText::CreateNativeInstance()));
1940   render_text->SetDisplayRect(Rect(1, 1000));
1941   render_text->SetMultiline(true);
1942   Canvas canvas;
1943 
1944   for (size_t i = 0; i < arraysize(kTestStrings); ++i) {
1945     SCOPED_TRACE(base::StringPrintf("kTestStrings[%" PRIuS "]", i));
1946     render_text->SetText(WideToUTF16(kTestStrings[i]));
1947     render_text->Draw(&canvas);
1948     EXPECT_GT(render_text->lines_.size(), 1U);
1949   }
1950 }
1951 
1952 // TODO(ckocagil): Enable for RenderTextHarfBuzz after implementing multiline.
1953 // Ensure strings wrap onto multiple lines for a normal available width.
TEST_F(RenderTextTest,Multiline_NormalWidth)1954 TEST_F(RenderTextTest, Multiline_NormalWidth) {
1955   const struct {
1956     const wchar_t* const text;
1957     const Range first_line_char_range;
1958     const Range second_line_char_range;
1959   } kTestStrings[] = {
1960     { L"abc defg hijkl", Range(0, 9), Range(9, 14) },
1961     { L"qwertyzxcvbn", Range(0, 8), Range(8, 12) },
1962     { L"\x062A\x0641\x0627\x062D\x05EA\x05E4\x05D5\x05D6\x05D9\x05DD",
1963           Range(4, 10), Range(0, 4) }
1964   };
1965 
1966   scoped_ptr<RenderTextWin> render_text(
1967       static_cast<RenderTextWin*>(RenderText::CreateNativeInstance()));
1968   render_text->SetDisplayRect(Rect(50, 1000));
1969   render_text->SetMultiline(true);
1970   Canvas canvas;
1971 
1972   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestStrings); ++i) {
1973     SCOPED_TRACE(base::StringPrintf("kTestStrings[%" PRIuS "]", i));
1974     render_text->SetText(WideToUTF16(kTestStrings[i].text));
1975     render_text->Draw(&canvas);
1976     ASSERT_EQ(2U, render_text->lines_.size());
1977     ASSERT_EQ(1U, render_text->lines_[0].segments.size());
1978     EXPECT_EQ(kTestStrings[i].first_line_char_range,
1979               render_text->lines_[0].segments[0].char_range);
1980     ASSERT_EQ(1U, render_text->lines_[1].segments.size());
1981     EXPECT_EQ(kTestStrings[i].second_line_char_range,
1982               render_text->lines_[1].segments[0].char_range);
1983   }
1984 }
1985 
1986 // TODO(ckocagil): Enable for RenderTextHarfBuzz after implementing multiline.
1987 // Ensure strings don't wrap onto multiple lines for a sufficient available
1988 // width.
TEST_F(RenderTextTest,Multiline_SufficientWidth)1989 TEST_F(RenderTextTest, Multiline_SufficientWidth) {
1990   const wchar_t* kTestStrings[] = { L"", L" ", L".", L" . ", L"abc", L"a b c",
1991                                     L"\x62E\x628\x632", L"\x62E \x628 \x632" };
1992 
1993   scoped_ptr<RenderTextWin> render_text(
1994       static_cast<RenderTextWin*>(RenderText::CreateNativeInstance()));
1995   render_text->SetDisplayRect(Rect(30, 1000));
1996   render_text->SetMultiline(true);
1997   Canvas canvas;
1998 
1999   for (size_t i = 0; i < arraysize(kTestStrings); ++i) {
2000     SCOPED_TRACE(base::StringPrintf("kTestStrings[%" PRIuS "]", i));
2001     render_text->SetText(WideToUTF16(kTestStrings[i]));
2002     render_text->Draw(&canvas);
2003     EXPECT_EQ(1U, render_text->lines_.size());
2004   }
2005 }
2006 
2007 // TODO(ckocagil): Enable for RenderTextHarfBuzz after implementing multiline.
TEST_F(RenderTextTest,Multiline_Newline)2008 TEST_F(RenderTextTest, Multiline_Newline) {
2009   const struct {
2010     const wchar_t* const text;
2011     // Ranges of the characters on each line preceding the newline.
2012     const Range first_line_char_range;
2013     const Range second_line_char_range;
2014   } kTestStrings[] = {
2015     { L"abc\ndef", Range(0, 3), Range(4, 7) },
2016     { L"a \n b ", Range(0, 2), Range(3, 6) },
2017     { L"\n" , Range::InvalidRange(), Range::InvalidRange() }
2018   };
2019 
2020   scoped_ptr<RenderTextWin> render_text(
2021       static_cast<RenderTextWin*>(RenderText::CreateNativeInstance()));
2022   render_text->SetDisplayRect(Rect(200, 1000));
2023   render_text->SetMultiline(true);
2024   Canvas canvas;
2025 
2026   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestStrings); ++i) {
2027     SCOPED_TRACE(base::StringPrintf("kTestStrings[%" PRIuS "]", i));
2028     render_text->SetText(WideToUTF16(kTestStrings[i].text));
2029     render_text->Draw(&canvas);
2030 
2031     ASSERT_EQ(2U, render_text->lines_.size());
2032 
2033     const Range first_expected_range = kTestStrings[i].first_line_char_range;
2034     ASSERT_EQ(first_expected_range.IsValid() ? 2U : 1U,
2035               render_text->lines_[0].segments.size());
2036     if (first_expected_range.IsValid())
2037       EXPECT_EQ(first_expected_range,
2038                 render_text->lines_[0].segments[0].char_range);
2039 
2040     const internal::LineSegment& newline_segment =
2041         render_text->lines_[0].segments[first_expected_range.IsValid() ? 1 : 0];
2042     ASSERT_EQ(1U, newline_segment.char_range.length());
2043     EXPECT_EQ(L'\n', kTestStrings[i].text[newline_segment.char_range.start()]);
2044 
2045     const Range second_expected_range = kTestStrings[i].second_line_char_range;
2046     ASSERT_EQ(second_expected_range.IsValid() ? 1U : 0U,
2047               render_text->lines_[1].segments.size());
2048     if (second_expected_range.IsValid())
2049       EXPECT_EQ(second_expected_range,
2050                 render_text->lines_[1].segments[0].char_range);
2051   }
2052 }
2053 
2054 // TODO(ckocagil): Remove when RenderTextWin goes away.
TEST_F(RenderTextTest,BreakRunsByUnicodeBlocks)2055 TEST_F(RenderTextTest, BreakRunsByUnicodeBlocks) {
2056   scoped_ptr<RenderTextWin> render_text(
2057       static_cast<RenderTextWin*>(RenderText::CreateNativeInstance()));
2058 
2059   // The '\x25B6' "play character" should break runs. http://crbug.com/278913
2060   render_text->SetText(WideToUTF16(L"x\x25B6y"));
2061   render_text->EnsureLayout();
2062   ASSERT_EQ(3U, render_text->runs_.size());
2063   EXPECT_EQ(Range(0, 1), render_text->runs_[0]->range);
2064   EXPECT_EQ(Range(1, 2), render_text->runs_[1]->range);
2065   EXPECT_EQ(Range(2, 3), render_text->runs_[2]->range);
2066 
2067   render_text->SetText(WideToUTF16(L"x \x25B6 y"));
2068   render_text->EnsureLayout();
2069   ASSERT_EQ(3U, render_text->runs_.size());
2070   EXPECT_EQ(Range(0, 2), render_text->runs_[0]->range);
2071   EXPECT_EQ(Range(2, 3), render_text->runs_[1]->range);
2072   EXPECT_EQ(Range(3, 5), render_text->runs_[2]->range);
2073 }
2074 #endif  // defined(OS_WIN)
2075 
2076 // Test TextRunHarfBuzz's cluster finding logic.
TEST_F(RenderTextTest,HarfBuzz_Clusters)2077 TEST_F(RenderTextTest, HarfBuzz_Clusters) {
2078   struct {
2079     uint32 glyph_to_char[4];
2080     Range chars[4];
2081     Range glyphs[4];
2082     bool is_rtl;
2083   } cases[] = {
2084     { // From string "A B C D" to glyphs "a b c d".
2085       { 0, 1, 2, 3 },
2086       { Range(0, 1), Range(1, 2), Range(2, 3), Range(3, 4) },
2087       { Range(0, 1), Range(1, 2), Range(2, 3), Range(3, 4) },
2088       false
2089     },
2090     { // From string "A B C D" to glyphs "d c b a".
2091       { 3, 2, 1, 0 },
2092       { Range(0, 1), Range(1, 2), Range(2, 3), Range(3, 4) },
2093       { Range(3, 4), Range(2, 3), Range(1, 2), Range(0, 1) },
2094       true
2095     },
2096     { // From string "A B C D" to glyphs "ab c c d".
2097       { 0, 2, 2, 3 },
2098       { Range(0, 2), Range(0, 2), Range(2, 3), Range(3, 4) },
2099       { Range(0, 1), Range(0, 1), Range(1, 3), Range(3, 4) },
2100       false
2101     },
2102     { // From string "A B C D" to glyphs "d c c ba".
2103       { 3, 2, 2, 0 },
2104       { Range(0, 2), Range(0, 2), Range(2, 3), Range(3, 4) },
2105       { Range(3, 4), Range(3, 4), Range(1, 3), Range(0, 1) },
2106       true
2107     },
2108   };
2109 
2110   internal::TextRunHarfBuzz run;
2111   run.range = Range(0, 4);
2112   run.glyph_count = 4;
2113   run.glyph_to_char.resize(4);
2114 
2115   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
2116     std::copy(cases[i].glyph_to_char, cases[i].glyph_to_char + 4,
2117               run.glyph_to_char.begin());
2118     run.is_rtl = cases[i].is_rtl;
2119 
2120     for (size_t j = 0; j < 4; ++j) {
2121       SCOPED_TRACE(base::StringPrintf("Case %" PRIuS ", char %" PRIuS, i, j));
2122       Range chars;
2123       Range glyphs;
2124       run.GetClusterAt(j, &chars, &glyphs);
2125       EXPECT_EQ(cases[i].chars[j], chars);
2126       EXPECT_EQ(cases[i].glyphs[j], glyphs);
2127       EXPECT_EQ(cases[i].glyphs[j], run.CharRangeToGlyphRange(chars));
2128     }
2129   }
2130 }
2131 
2132 // Ensure that graphemes with multiple code points do not get split.
TEST_F(RenderTextTest,HarfBuzz_SubglyphGraphemeCases)2133 TEST_F(RenderTextTest, HarfBuzz_SubglyphGraphemeCases) {
2134   const wchar_t* cases[] = {
2135     // "A" with a combining umlaut, followed by a "B".
2136     L"A\x0308" L"B",
2137     // Devanagari biconsonantal conjunct "ki", followed by an "a".
2138     L"\x0915\x093f\x0905",
2139     // Thai consonant and vowel pair "cho chan" + "sara am", followed by Thai
2140     // digit 0.
2141     L"\x0e08\x0e33\x0E50",
2142   };
2143 
2144   RenderTextHarfBuzz render_text;
2145 
2146   for (size_t i = 0; i < arraysize(cases); ++i) {
2147     SCOPED_TRACE(base::StringPrintf("Case %" PRIuS, i));
2148 
2149     base::string16 text = WideToUTF16(cases[i]);
2150     render_text.SetText(text);
2151     render_text.EnsureLayout();
2152     ASSERT_EQ(1U, render_text.runs_.size());
2153     internal::TextRunHarfBuzz* run = render_text.runs_[0];
2154 
2155     base::i18n::BreakIterator* iter = render_text.grapheme_iterator_.get();
2156     Range first_grapheme_bounds = run->GetGraphemeBounds(iter, 0);
2157     EXPECT_EQ(first_grapheme_bounds, run->GetGraphemeBounds(iter, 1));
2158     Range second_grapheme_bounds = run->GetGraphemeBounds(iter, 2);
2159     EXPECT_EQ(first_grapheme_bounds.end(), second_grapheme_bounds.start());
2160   }
2161 }
2162 
2163 // Test the partition of a multi-grapheme cluster into grapheme ranges.
TEST_F(RenderTextTest,HarfBuzz_SubglyphGraphemePartition)2164 TEST_F(RenderTextTest, HarfBuzz_SubglyphGraphemePartition) {
2165   struct {
2166     uint32 glyph_to_char[2];
2167     Range bounds[4];
2168     bool is_rtl;
2169   } cases[] = {
2170     { // From string "A B C D" to glyphs "a bcd".
2171       { 0, 1 },
2172       { Range(0, 10), Range(10, 13), Range(13, 17), Range(17, 20) },
2173       false
2174     },
2175     { // From string "A B C D" to glyphs "ab cd".
2176       { 0, 2 },
2177       { Range(0, 5), Range(5, 10), Range(10, 15), Range(15, 20) },
2178       false
2179     },
2180     { // From string "A B C D" to glyphs "dcb a".
2181       { 1, 0 },
2182       { Range(10, 20), Range(7, 10), Range(3, 7), Range(0, 3) },
2183       true
2184     },
2185     { // From string "A B C D" to glyphs "dc ba".
2186       { 2, 0 },
2187       { Range(15, 20), Range(10, 15), Range(5, 10), Range(0, 5) },
2188       true
2189     },
2190   };
2191 
2192   internal::TextRunHarfBuzz run;
2193   run.range = Range(0, 4);
2194   run.glyph_count = 2;
2195   run.glyph_to_char.resize(2);
2196   run.positions.reset(new SkPoint[4]);
2197   run.width = 20;
2198 
2199   const base::string16 kString = ASCIIToUTF16("abcd");
2200   scoped_ptr<base::i18n::BreakIterator> iter(new base::i18n::BreakIterator(
2201       kString, base::i18n::BreakIterator::BREAK_CHARACTER));
2202   ASSERT_TRUE(iter->Init());
2203 
2204   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
2205     std::copy(cases[i].glyph_to_char, cases[i].glyph_to_char + 2,
2206               run.glyph_to_char.begin());
2207     run.is_rtl = cases[i].is_rtl;
2208     for (int j = 0; j < 2; ++j)
2209       run.positions[j].set(j * 10, 0);
2210 
2211     for (size_t j = 0; j < 4; ++j) {
2212       SCOPED_TRACE(base::StringPrintf("Case %" PRIuS ", char %" PRIuS, i, j));
2213       EXPECT_EQ(cases[i].bounds[j], run.GetGraphemeBounds(iter.get(), j));
2214     }
2215   }
2216 }
2217 
TEST_F(RenderTextTest,HarfBuzz_RunDirection)2218 TEST_F(RenderTextTest, HarfBuzz_RunDirection) {
2219   RenderTextHarfBuzz render_text;
2220   const base::string16 mixed =
2221       WideToUTF16(L"\x05D0\x05D1" L"1234" L"\x05D2\x05D3");
2222   render_text.SetText(mixed);
2223   render_text.EnsureLayout();
2224   ASSERT_EQ(3U, render_text.runs_.size());
2225   EXPECT_TRUE(render_text.runs_[0]->is_rtl);
2226   EXPECT_FALSE(render_text.runs_[1]->is_rtl);
2227   EXPECT_TRUE(render_text.runs_[2]->is_rtl);
2228 }
2229 
TEST_F(RenderTextTest,HarfBuzz_BreakRunsByUnicodeBlocks)2230 TEST_F(RenderTextTest, HarfBuzz_BreakRunsByUnicodeBlocks) {
2231   RenderTextHarfBuzz render_text;
2232 
2233   // The '\x25B6' "play character" should break runs. http://crbug.com/278913
2234   render_text.SetText(WideToUTF16(L"x\x25B6y"));
2235   render_text.EnsureLayout();
2236   ASSERT_EQ(3U, render_text.runs_.size());
2237   EXPECT_EQ(Range(0, 1), render_text.runs_[0]->range);
2238   EXPECT_EQ(Range(1, 2), render_text.runs_[1]->range);
2239   EXPECT_EQ(Range(2, 3), render_text.runs_[2]->range);
2240 
2241   render_text.SetText(WideToUTF16(L"x \x25B6 y"));
2242   render_text.EnsureLayout();
2243   ASSERT_EQ(3U, render_text.runs_.size());
2244   EXPECT_EQ(Range(0, 2), render_text.runs_[0]->range);
2245   EXPECT_EQ(Range(2, 3), render_text.runs_[1]->range);
2246   EXPECT_EQ(Range(3, 5), render_text.runs_[2]->range);
2247 }
2248 
2249 // Disabled on Mac because RenderTextMac doesn't implement GetGlyphBounds.
2250 #if !defined(OS_MACOSX)
TEST_F(RenderTextTest,GlyphBounds)2251 TEST_F(RenderTextTest, GlyphBounds) {
2252   const wchar_t* kTestStrings[] = {
2253       L"asdf 1234 qwer", L"\x0647\x0654", L"\x0645\x0631\x062D\x0628\x0627"
2254   };
2255   scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
2256 
2257   for (size_t i = 0; i < arraysize(kTestStrings); ++i) {
2258     render_text->SetText(WideToUTF16(kTestStrings[i]));
2259     render_text->EnsureLayout();
2260 
2261     for (size_t j = 0; j < render_text->text().length(); ++j)
2262       EXPECT_FALSE(render_text->GetGlyphBounds(j).is_empty());
2263   }
2264 }
2265 #endif
2266 
2267 // Remove this after making RTHB default in favor of RenderTextTest.GlyphBounds.
TEST_F(RenderTextTest,HarfBuzz_GlyphBounds)2268 TEST_F(RenderTextTest, HarfBuzz_GlyphBounds) {
2269   const wchar_t* kTestStrings[] = {
2270       L"asdf 1234 qwer", L"\x0647\x0654", L"\x0645\x0631\x062D\x0628\x0627"
2271   };
2272   scoped_ptr<RenderText> render_text(new RenderTextHarfBuzz);
2273 
2274   for (size_t i = 0; i < arraysize(kTestStrings); ++i) {
2275     render_text->SetText(WideToUTF16(kTestStrings[i]));
2276     render_text->EnsureLayout();
2277 
2278     for (size_t j = 0; j < render_text->text().length(); ++j)
2279       EXPECT_FALSE(render_text->GetGlyphBounds(j).is_empty());
2280   }
2281 }
2282 
2283 // Ensure that shaping with a non-existent font does not cause a crash.
TEST_F(RenderTextTest,HarfBuzz_NonExistentFont)2284 TEST_F(RenderTextTest, HarfBuzz_NonExistentFont) {
2285   RenderTextHarfBuzz render_text;
2286   render_text.SetText(ASCIIToUTF16("test"));
2287   render_text.EnsureLayout();
2288   ASSERT_EQ(1U, render_text.runs_.size());
2289   internal::TextRunHarfBuzz* run = render_text.runs_[0];
2290   render_text.ShapeRunWithFont(run, "TheFontThatDoesntExist");
2291 }
2292 
2293 // Ensure an empty run returns sane values to queries.
TEST_F(RenderTextTest,HarfBuzz_EmptyRun)2294 TEST_F(RenderTextTest, HarfBuzz_EmptyRun) {
2295   internal::TextRunHarfBuzz run;
2296   const base::string16 kString = ASCIIToUTF16("abcdefgh");
2297   scoped_ptr<base::i18n::BreakIterator> iter(new base::i18n::BreakIterator(
2298       kString, base::i18n::BreakIterator::BREAK_CHARACTER));
2299   ASSERT_TRUE(iter->Init());
2300 
2301   run.range = Range(3, 8);
2302   run.glyph_count = 0;
2303   EXPECT_EQ(Range(0, 0), run.CharRangeToGlyphRange(Range(4, 5)));
2304   EXPECT_EQ(Range(0, 0), run.GetGraphemeBounds(iter.get(), 4));
2305   Range chars;
2306   Range glyphs;
2307   run.GetClusterAt(4, &chars, &glyphs);
2308   EXPECT_EQ(Range(3, 8), chars);
2309   EXPECT_EQ(Range(0, 0), glyphs);
2310 }
2311 
2312 }  // namespace gfx
2313