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