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