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