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