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