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