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