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