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