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