OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ui/gfx/render_text.h" | 5 #include "ui/gfx/render_text.h" |
6 | 6 |
7 #include <limits.h> | 7 #include <limits.h> |
8 #include <stddef.h> | 8 #include <stddef.h> |
9 #include <stdint.h> | 9 #include <stdint.h> |
10 | 10 |
11 #include <algorithm> | 11 #include <algorithm> |
12 #include <memory> | 12 #include <memory> |
| 13 #include <numeric> |
13 | 14 |
14 #include "base/format_macros.h" | 15 #include "base/format_macros.h" |
15 #include "base/i18n/break_iterator.h" | 16 #include "base/i18n/break_iterator.h" |
16 #include "base/macros.h" | 17 #include "base/macros.h" |
17 #include "base/memory/ptr_util.h" | 18 #include "base/memory/ptr_util.h" |
18 #include "base/run_loop.h" | 19 #include "base/run_loop.h" |
19 #include "base/strings/string_split.h" | 20 #include "base/strings/string_split.h" |
20 #include "base/strings/string_util.h" | 21 #include "base/strings/string_util.h" |
21 #include "base/strings/stringprintf.h" | 22 #include "base/strings/stringprintf.h" |
22 #include "base/strings/utf_string_conversions.h" | 23 #include "base/strings/utf_string_conversions.h" |
(...skipping 442 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
465 return static_cast<RenderTextHarfBuzz*>(GetRenderText()); | 466 return static_cast<RenderTextHarfBuzz*>(GetRenderText()); |
466 } | 467 } |
467 | 468 |
468 #if defined(OS_MACOSX) | 469 #if defined(OS_MACOSX) |
469 RenderTextMac* GetRenderTextMac() { | 470 RenderTextMac* GetRenderTextMac() { |
470 DCHECK_EQ(RENDER_TEXT_MAC, GetParam()); | 471 DCHECK_EQ(RENDER_TEXT_MAC, GetParam()); |
471 return static_cast<RenderTextMac*>(GetRenderText()); | 472 return static_cast<RenderTextMac*>(GetRenderText()); |
472 } | 473 } |
473 #endif | 474 #endif |
474 | 475 |
| 476 Rect GetSelectionBoundsUnion() { |
| 477 const std::vector<Rect> bounds = |
| 478 render_text_->GetSubstringBoundsForTesting(render_text_->selection()); |
| 479 return std::accumulate(bounds.begin(), bounds.end(), Rect(), UnionRects); |
| 480 } |
| 481 |
475 Canvas* canvas() { return &canvas_; } | 482 Canvas* canvas() { return &canvas_; } |
476 TestSkiaTextRenderer* renderer() { return &renderer_; } | 483 TestSkiaTextRenderer* renderer() { return &renderer_; } |
477 test::RenderTextTestApi* test_api() { return test_api_.get(); }; | 484 test::RenderTextTestApi* test_api() { return test_api_.get(); }; |
478 | 485 |
479 private: | 486 private: |
480 std::unique_ptr<RenderText> render_text_; | 487 std::unique_ptr<RenderText> render_text_; |
481 std::unique_ptr<test::RenderTextTestApi> test_api_; | 488 std::unique_ptr<test::RenderTextTestApi> test_api_; |
482 Canvas canvas_; | 489 Canvas canvas_; |
483 TestSkiaTextRenderer renderer_; | 490 TestSkiaTextRenderer renderer_; |
484 | 491 |
(...skipping 22 matching lines...) Expand all Loading... |
507 GetRenderTextHarfBuzz()->set_glyph_width_for_test(test_width); | 514 GetRenderTextHarfBuzz()->set_glyph_width_for_test(test_width); |
508 } | 515 } |
509 | 516 |
510 bool ShapeRunWithFont(const base::string16& text, | 517 bool ShapeRunWithFont(const base::string16& text, |
511 const Font& font, | 518 const Font& font, |
512 const FontRenderParams& params, | 519 const FontRenderParams& params, |
513 internal::TextRunHarfBuzz* run) { | 520 internal::TextRunHarfBuzz* run) { |
514 return GetRenderTextHarfBuzz()->ShapeRunWithFont(text, font, params, run); | 521 return GetRenderTextHarfBuzz()->ShapeRunWithFont(text, font, params, run); |
515 } | 522 } |
516 | 523 |
| 524 int GetCursorYForTesting(int line_num = 0) { |
| 525 return GetRenderText()->GetLineOffset(line_num).y() + 1; |
| 526 } |
| 527 |
517 private: | 528 private: |
518 DISALLOW_COPY_AND_ASSIGN(RenderTextHarfBuzzTest); | 529 DISALLOW_COPY_AND_ASSIGN(RenderTextHarfBuzzTest); |
519 }; | 530 }; |
520 | 531 |
521 #if defined(OS_MACOSX) | 532 #if defined(OS_MACOSX) |
522 // Test fixture class. Use for tests which are only to be run for RenderTextMac. | 533 // Test fixture class. Use for tests which are only to be run for RenderTextMac. |
523 class RenderTextMacTest : public RenderTextTest { | 534 class RenderTextMacTest : public RenderTextTest { |
524 public: | 535 public: |
525 RenderTextMacTest() {} | 536 RenderTextMacTest() {} |
526 | 537 |
(...skipping 254 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
781 EXPECT_EQ(1U, test_api()->TextIndexToDisplayIndex(1U)); | 792 EXPECT_EQ(1U, test_api()->TextIndexToDisplayIndex(1U)); |
782 EXPECT_EQ(1U, test_api()->TextIndexToDisplayIndex(2U)); | 793 EXPECT_EQ(1U, test_api()->TextIndexToDisplayIndex(2U)); |
783 EXPECT_EQ(0U, test_api()->DisplayIndexToTextIndex(0U)); | 794 EXPECT_EQ(0U, test_api()->DisplayIndexToTextIndex(0U)); |
784 EXPECT_EQ(2U, test_api()->DisplayIndexToTextIndex(1U)); | 795 EXPECT_EQ(2U, test_api()->DisplayIndexToTextIndex(1U)); |
785 EXPECT_TRUE(render_text->IsValidCursorIndex(0U)); | 796 EXPECT_TRUE(render_text->IsValidCursorIndex(0U)); |
786 EXPECT_FALSE(render_text->IsValidCursorIndex(1U)); | 797 EXPECT_FALSE(render_text->IsValidCursorIndex(1U)); |
787 EXPECT_TRUE(render_text->IsValidCursorIndex(2U)); | 798 EXPECT_TRUE(render_text->IsValidCursorIndex(2U)); |
788 | 799 |
789 // FindCursorPosition() should not return positions between a surrogate pair. | 800 // FindCursorPosition() should not return positions between a surrogate pair. |
790 render_text->SetDisplayRect(Rect(0, 0, 20, 20)); | 801 render_text->SetDisplayRect(Rect(0, 0, 20, 20)); |
791 EXPECT_EQ(render_text->FindCursorPosition(Point(0, 0)).caret_pos(), 0U); | 802 const int cursor_y = GetCursorYForTesting(); |
792 EXPECT_EQ(render_text->FindCursorPosition(Point(20, 0)).caret_pos(), 2U); | 803 EXPECT_EQ(render_text->FindCursorPosition(Point(0, cursor_y)).caret_pos(), |
| 804 0U); |
| 805 EXPECT_EQ(render_text->FindCursorPosition(Point(20, cursor_y)).caret_pos(), |
| 806 2U); |
793 for (int x = -1; x <= 20; ++x) { | 807 for (int x = -1; x <= 20; ++x) { |
794 SelectionModel selection = render_text->FindCursorPosition(Point(x, 0)); | 808 SelectionModel selection = |
| 809 render_text->FindCursorPosition(Point(x, cursor_y)); |
795 EXPECT_TRUE(selection.caret_pos() == 0U || selection.caret_pos() == 2U); | 810 EXPECT_TRUE(selection.caret_pos() == 0U || selection.caret_pos() == 2U); |
796 } | 811 } |
797 | 812 |
798 // GetGlyphBounds() should yield the entire string bounds for text index 0. | 813 // GetGlyphBounds() should yield the entire string bounds for text index 0. |
799 EXPECT_EQ(render_text->GetStringSize().width(), | 814 EXPECT_EQ(render_text->GetStringSize().width(), |
800 static_cast<int>(render_text->GetGlyphBounds(0U).length())); | 815 static_cast<int>(render_text->GetGlyphBounds(0U).length())); |
801 | 816 |
802 // Cursoring is independent of underlying characters when text is obscured. | 817 // Cursoring is independent of underlying characters when text is obscured. |
803 const wchar_t* const texts[] = { | 818 const wchar_t* const texts[] = { |
804 kWeak, kLtr, kLtrRtl, kLtrRtlLtr, kRtl, kRtlLtr, kRtlLtrRtl, | 819 kWeak, kLtr, kLtrRtl, kLtrRtlLtr, kRtl, kRtlLtr, kRtlLtrRtl, |
(...skipping 841 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1646 | 1661 |
1647 // TODO(asvitkine): RenderTextMac cursor movements. http://crbug.com/131618 | 1662 // TODO(asvitkine): RenderTextMac cursor movements. http://crbug.com/131618 |
1648 // Crashes on Mac with RenderTextHarfBuzz. See http://crbug.com/640068. | 1663 // Crashes on Mac with RenderTextHarfBuzz. See http://crbug.com/640068. |
1649 #if !defined(OS_MACOSX) | 1664 #if !defined(OS_MACOSX) |
1650 TEST_P(RenderTextHarfBuzzTest, MoveCursorLeftRight_MeiryoUILigatures) { | 1665 TEST_P(RenderTextHarfBuzzTest, MoveCursorLeftRight_MeiryoUILigatures) { |
1651 RenderText* render_text = GetRenderText(); | 1666 RenderText* render_text = GetRenderText(); |
1652 // Meiryo UI uses single-glyph ligatures for 'ff' and 'ffi', but each letter | 1667 // Meiryo UI uses single-glyph ligatures for 'ff' and 'ffi', but each letter |
1653 // (code point) has unique bounds, so mid-glyph cursoring should be possible. | 1668 // (code point) has unique bounds, so mid-glyph cursoring should be possible. |
1654 render_text->SetFontList(FontList("Meiryo UI, 12px")); | 1669 render_text->SetFontList(FontList("Meiryo UI, 12px")); |
1655 render_text->SetText(WideToUTF16(L"ff ffi")); | 1670 render_text->SetText(WideToUTF16(L"ff ffi")); |
| 1671 render_text->SetDisplayRect(gfx::Rect(100, 100)); |
| 1672 test_api()->EnsureLayout(); |
1656 EXPECT_EQ(0U, render_text->cursor_position()); | 1673 EXPECT_EQ(0U, render_text->cursor_position()); |
| 1674 |
| 1675 gfx::Rect last_selection_bounds = GetSelectionBoundsUnion(); |
1657 for (size_t i = 0; i < render_text->text().length(); ++i) { | 1676 for (size_t i = 0; i < render_text->text().length(); ++i) { |
1658 render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, SELECTION_NONE); | 1677 render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, SELECTION_RETAIN); |
1659 EXPECT_EQ(i + 1, render_text->cursor_position()); | 1678 EXPECT_EQ(i + 1, render_text->cursor_position()); |
| 1679 |
| 1680 // Verify the selection bounds also increase and that the correct bounds are |
| 1681 // returned even when the grapheme boundary lies within a glyph. |
| 1682 const gfx::Rect selection_bounds = GetSelectionBoundsUnion(); |
| 1683 EXPECT_GT(selection_bounds.right(), last_selection_bounds.right()); |
| 1684 EXPECT_EQ(selection_bounds.x(), last_selection_bounds.x()); |
| 1685 last_selection_bounds = selection_bounds; |
1660 } | 1686 } |
1661 EXPECT_EQ(6U, render_text->cursor_position()); | 1687 EXPECT_EQ(6U, render_text->cursor_position()); |
1662 } | 1688 } |
1663 #endif // !defined(OS_MACOSX) | 1689 #endif // !defined(OS_MACOSX) |
1664 | 1690 |
1665 // TODO(asvitkine): RenderTextMac cursor movements. http://crbug.com/131618 | 1691 // TODO(asvitkine): RenderTextMac cursor movements. http://crbug.com/131618 |
1666 TEST_P(RenderTextHarfBuzzTest, GraphemePositions) { | 1692 TEST_P(RenderTextHarfBuzzTest, GraphemePositions) { |
1667 // LTR 2-character grapheme, LTR abc, LTR 2-character grapheme. | 1693 // LTR 2-character grapheme, LTR abc, LTR 2-character grapheme. |
1668 const base::string16 kText1 = | 1694 const base::string16 kText1 = |
1669 WideToUTF16(L"\x0915\x093f" L"abc" L"\x0915\x093f"); | 1695 WideToUTF16(L"\x0915\x093f" L"abc" L"\x0915\x093f"); |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1747 if (base::win::GetVersion() < base::win::VERSION_VISTA) | 1773 if (base::win::GetVersion() < base::win::VERSION_VISTA) |
1748 return; | 1774 return; |
1749 #endif | 1775 #endif |
1750 | 1776 |
1751 // Test that selection bounds may be set amid multi-character graphemes. | 1777 // Test that selection bounds may be set amid multi-character graphemes. |
1752 const base::string16 kHindi = WideToUTF16(L"\x0915\x093f"); | 1778 const base::string16 kHindi = WideToUTF16(L"\x0915\x093f"); |
1753 const base::string16 kThai = WideToUTF16(L"\x0e08\x0e33"); | 1779 const base::string16 kThai = WideToUTF16(L"\x0e08\x0e33"); |
1754 const base::string16 cases[] = { kHindi, kThai }; | 1780 const base::string16 cases[] = { kHindi, kThai }; |
1755 | 1781 |
1756 RenderText* render_text = GetRenderText(); | 1782 RenderText* render_text = GetRenderText(); |
| 1783 render_text->SetDisplayRect(Rect(100, 1000)); |
1757 for (size_t i = 0; i < arraysize(cases); i++) { | 1784 for (size_t i = 0; i < arraysize(cases); i++) { |
1758 SCOPED_TRACE(base::StringPrintf("Testing cases[%" PRIuS "]", i)); | 1785 SCOPED_TRACE(base::StringPrintf("Testing cases[%" PRIuS "]", i)); |
1759 render_text->SetText(cases[i]); | 1786 render_text->SetText(cases[i]); |
1760 EXPECT_TRUE(render_text->IsValidLogicalIndex(1)); | 1787 EXPECT_TRUE(render_text->IsValidLogicalIndex(1)); |
1761 EXPECT_FALSE(render_text->IsValidCursorIndex(1)); | 1788 EXPECT_FALSE(render_text->IsValidCursorIndex(1)); |
1762 EXPECT_TRUE(render_text->SelectRange(Range(2, 1))); | 1789 EXPECT_TRUE(render_text->SelectRange(Range(2, 1))); |
1763 EXPECT_EQ(Range(2, 1), render_text->selection()); | 1790 EXPECT_EQ(Range(2, 1), render_text->selection()); |
1764 EXPECT_EQ(1U, render_text->cursor_position()); | 1791 EXPECT_EQ(1U, render_text->cursor_position()); |
| 1792 |
| 1793 // Verify that the selection bounds extend over the entire grapheme, even if |
| 1794 // the selection is set amid the grapheme. |
| 1795 test_api()->EnsureLayout(); |
| 1796 const gfx::Rect mid_grapheme_bounds = GetSelectionBoundsUnion(); |
| 1797 render_text->SelectAll(false); |
| 1798 EXPECT_EQ(GetSelectionBoundsUnion(), mid_grapheme_bounds); |
| 1799 |
1765 // Although selection bounds may be set within a multi-character grapheme, | 1800 // Although selection bounds may be set within a multi-character grapheme, |
1766 // cursor movement (e.g. via arrow key) should avoid those indices. | 1801 // cursor movement (e.g. via arrow key) should avoid those indices. |
| 1802 EXPECT_TRUE(render_text->SelectRange(Range(2, 1))); |
1767 render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, SELECTION_NONE); | 1803 render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, SELECTION_NONE); |
1768 EXPECT_EQ(0U, render_text->cursor_position()); | 1804 EXPECT_EQ(0U, render_text->cursor_position()); |
1769 render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, SELECTION_NONE); | 1805 render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, SELECTION_NONE); |
1770 EXPECT_EQ(2U, render_text->cursor_position()); | 1806 EXPECT_EQ(2U, render_text->cursor_position()); |
1771 } | 1807 } |
1772 } | 1808 } |
1773 | 1809 |
1774 // TODO(asvitkine): RenderTextMac cursor movements. http://crbug.com/131618 | 1810 // TODO(asvitkine): RenderTextMac cursor movements. http://crbug.com/131618 |
1775 TEST_P(RenderTextHarfBuzzTest, FindCursorPosition) { | 1811 TEST_P(RenderTextHarfBuzzTest, FindCursorPosition) { |
1776 const wchar_t* kTestStrings[] = { kLtrRtl, kLtrRtlLtr, kRtlLtr, kRtlLtrRtl }; | 1812 const wchar_t* kTestStrings[] = { kLtrRtl, kLtrRtlLtr, kRtlLtr, kRtlLtrRtl }; |
1777 RenderText* render_text = GetRenderText(); | 1813 RenderText* render_text = GetRenderText(); |
1778 render_text->SetDisplayRect(Rect(0, 0, 100, 20)); | 1814 render_text->SetDisplayRect(Rect(0, 0, 100, 20)); |
1779 for (size_t i = 0; i < arraysize(kTestStrings); ++i) { | 1815 for (size_t i = 0; i < arraysize(kTestStrings); ++i) { |
1780 SCOPED_TRACE(base::StringPrintf("Testing case[%" PRIuS "]", i)); | 1816 SCOPED_TRACE(base::StringPrintf("Testing case[%" PRIuS "]", i)); |
1781 render_text->SetText(WideToUTF16(kTestStrings[i])); | 1817 render_text->SetText(WideToUTF16(kTestStrings[i])); |
1782 for (size_t j = 0; j < render_text->text().length(); ++j) { | 1818 for (size_t j = 0; j < render_text->text().length(); ++j) { |
1783 const Range range(render_text->GetGlyphBounds(j)); | 1819 const Range range(render_text->GetGlyphBounds(j)); |
1784 // Test a point just inside the leading edge of the glyph bounds. | 1820 // Test a point just inside the leading edge of the glyph bounds. |
1785 int x = range.is_reversed() ? range.GetMax() - 1 : range.GetMin() + 1; | 1821 int x = range.is_reversed() ? range.GetMax() - 1 : range.GetMin() + 1; |
1786 EXPECT_EQ(j, render_text->FindCursorPosition(Point(x, 0)).caret_pos()); | 1822 EXPECT_EQ( |
| 1823 j, render_text->FindCursorPosition(Point(x, GetCursorYForTesting())) |
| 1824 .caret_pos()); |
1787 } | 1825 } |
1788 } | 1826 } |
1789 } | 1827 } |
1790 | 1828 |
| 1829 // Tests that FindCursorPosition behaves correctly for multi-line text. |
| 1830 TEST_P(RenderTextHarfBuzzTest, FindCursorPositionMultiline) { |
| 1831 const wchar_t* kTestStrings[] = {L"abc def", |
| 1832 L"\x5d0\x5d1\x5d2 \x5d3\x5d4\x5d5" /*rtl*/}; |
| 1833 |
| 1834 SetGlyphWidth(5); |
| 1835 RenderText* render_text = GetRenderText(); |
| 1836 render_text->SetDisplayRect(Rect(25, 1000)); |
| 1837 render_text->SetMultiline(true); |
| 1838 |
| 1839 for (size_t i = 0; i < arraysize(kTestStrings); i++) { |
| 1840 render_text->SetText(WideToUTF16(kTestStrings[i])); |
| 1841 test_api()->EnsureLayout(); |
| 1842 EXPECT_EQ(2u, test_api()->lines().size()); |
| 1843 |
| 1844 const bool is_ltr = |
| 1845 render_text->GetDisplayTextDirection() == base::i18n::LEFT_TO_RIGHT; |
| 1846 for (size_t j = 0; j < render_text->text().length(); ++j) { |
| 1847 SCOPED_TRACE(base::StringPrintf( |
| 1848 "Testing index %" PRIuS " for case %" PRIuS "", j, i)); |
| 1849 render_text->SelectRange(Range(j, j + 1)); |
| 1850 |
| 1851 // Test a point inside the leading edge of the glyph bounds. |
| 1852 const Rect bounds = GetSelectionBoundsUnion(); |
| 1853 const Point cursor_position(is_ltr ? bounds.x() + 1 : bounds.right() - 1, |
| 1854 bounds.y() + 1); |
| 1855 |
| 1856 const SelectionModel model = |
| 1857 render_text->FindCursorPosition(cursor_position); |
| 1858 EXPECT_EQ(j, model.caret_pos()); |
| 1859 EXPECT_EQ(CURSOR_FORWARD, model.caret_affinity()); |
| 1860 } |
| 1861 } |
| 1862 } |
| 1863 |
| 1864 // Ensure FindCursorPosition returns positions only at valid grapheme |
| 1865 // boundaries. |
| 1866 TEST_P(RenderTextHarfBuzzTest, FindCursorPosition_GraphemeBoundaries) { |
| 1867 struct { |
| 1868 base::string16 text; |
| 1869 std::set<size_t> expected_cursor_positions; |
| 1870 } cases[] = { |
| 1871 // LTR 2-character grapheme, LTR abc, LTR 2-character grapheme. |
| 1872 {WideToUTF16(L"\x0915\x093f" |
| 1873 L"abc" |
| 1874 L"\x0915\x093f"), |
| 1875 {0, 2, 3, 4, 5, 7}}, |
| 1876 // LTR ab, LTR 2-character grapheme, LTR cd. |
| 1877 {WideToUTF16(L"ab" |
| 1878 L"\x0915\x093f" |
| 1879 L"cd"), |
| 1880 {0, 1, 2, 4, 5, 6}}, |
| 1881 // LTR ab, surrogate pair composed of two 16 bit characters, LTR cd. |
| 1882 {UTF8ToUTF16("ab" |
| 1883 "\xF0\x9D\x84\x9E" |
| 1884 "cd"), |
| 1885 {0, 1, 2, 4, 5, 6}}}; |
| 1886 |
| 1887 RenderText* render_text = GetRenderText(); |
| 1888 render_text->SetDisplayRect(gfx::Rect(100, 30)); |
| 1889 for (size_t i = 0; i < arraysize(cases); i++) { |
| 1890 SCOPED_TRACE(base::StringPrintf("Testing case %" PRIuS "", i)); |
| 1891 render_text->SetText(cases[i].text); |
| 1892 test_api()->EnsureLayout(); |
| 1893 std::set<size_t> obtained_cursor_positions; |
| 1894 size_t cursor_y = GetCursorYForTesting(); |
| 1895 for (int x = -5; x < 105; x++) |
| 1896 obtained_cursor_positions.insert( |
| 1897 render_text->FindCursorPosition(gfx::Point(x, cursor_y)).caret_pos()); |
| 1898 EXPECT_EQ(cases[i].expected_cursor_positions, obtained_cursor_positions); |
| 1899 } |
| 1900 } |
| 1901 |
1791 TEST_P(RenderTextTest, EdgeSelectionModels) { | 1902 TEST_P(RenderTextTest, EdgeSelectionModels) { |
1792 // Simple Latin text. | 1903 // Simple Latin text. |
1793 const base::string16 kLatin = WideToUTF16(L"abc"); | 1904 const base::string16 kLatin = WideToUTF16(L"abc"); |
1794 // LTR 2-character grapheme. | 1905 // LTR 2-character grapheme. |
1795 const base::string16 kLTRGrapheme = WideToUTF16(L"\x0915\x093f"); | 1906 const base::string16 kLTRGrapheme = WideToUTF16(L"\x0915\x093f"); |
1796 // LTR 2-character grapheme, LTR a, LTR 2-character grapheme. | 1907 // LTR 2-character grapheme, LTR a, LTR 2-character grapheme. |
1797 const base::string16 kHindiLatin = | 1908 const base::string16 kHindiLatin = |
1798 WideToUTF16(L"\x0915\x093f" L"a" L"\x0915\x093f"); | 1909 WideToUTF16(L"\x0915\x093f" L"a" L"\x0915\x093f"); |
1799 // RTL 2-character grapheme. | 1910 // RTL 2-character grapheme. |
1800 const base::string16 kRTLGrapheme = WideToUTF16(L"\x05e0\x05b8"); | 1911 const base::string16 kRTLGrapheme = WideToUTF16(L"\x05e0\x05b8"); |
(...skipping 1055 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2856 } | 2967 } |
2857 } | 2968 } |
2858 | 2969 |
2859 TEST_P(RenderTextHarfBuzzTest, Multiline_Newline) { | 2970 TEST_P(RenderTextHarfBuzzTest, Multiline_Newline) { |
2860 const struct { | 2971 const struct { |
2861 const wchar_t* const text; | 2972 const wchar_t* const text; |
2862 const size_t lines_count; | 2973 const size_t lines_count; |
2863 // Ranges of the characters on each line preceding the newline. | 2974 // Ranges of the characters on each line preceding the newline. |
2864 const Range line_char_ranges[3]; | 2975 const Range line_char_ranges[3]; |
2865 } kTestStrings[] = { | 2976 } kTestStrings[] = { |
2866 {L"abc\ndef", 2ul, { Range(0, 3), Range(4, 7), Range::InvalidRange() } }, | 2977 {L"abc\ndef", 2ul, {Range(0, 3), Range(4, 7), Range::InvalidRange()}}, |
2867 {L"a \n b ", 2ul, { Range(0, 2), Range(3, 6), Range::InvalidRange() } }, | 2978 {L"a \n b ", 2ul, {Range(0, 2), Range(3, 6), Range::InvalidRange()}}, |
2868 {L"ab\n", 2ul, { Range(0, 2), Range(), Range::InvalidRange() } }, | 2979 {L"ab\n", 2ul, {Range(0, 2), Range(), Range::InvalidRange()}}, |
2869 {L"a\n\nb", 3ul, { Range(0, 1), Range(), Range(3, 4) } }, | 2980 {L"a\n\nb", 3ul, {Range(0, 1), Range(2, 3), Range(3, 4)}}, |
2870 {L"\nab", 2ul, { Range(), Range(1, 3), Range::InvalidRange() } }, | 2981 {L"\nab", 2ul, {Range(0, 1), Range(1, 3), Range::InvalidRange()}}, |
2871 {L"\n", 2ul, { Range(), Range(), Range::InvalidRange() } }, | 2982 {L"\n", 2ul, {Range(0, 1), Range(), Range::InvalidRange()}}, |
2872 }; | 2983 }; |
2873 | 2984 |
2874 RenderText* render_text = GetRenderText(); | 2985 RenderText* render_text = GetRenderText(); |
2875 render_text->SetDisplayRect(Rect(200, 1000)); | 2986 render_text->SetDisplayRect(Rect(200, 1000)); |
2876 render_text->SetMultiline(true); | 2987 render_text->SetMultiline(true); |
2877 | 2988 |
2878 for (size_t i = 0; i < arraysize(kTestStrings); ++i) { | 2989 for (size_t i = 0; i < arraysize(kTestStrings); ++i) { |
2879 SCOPED_TRACE(base::StringPrintf("kTestStrings[%" PRIuS "]", i)); | 2990 SCOPED_TRACE(base::StringPrintf("kTestStrings[%" PRIuS "]", i)); |
2880 render_text->SetText(WideToUTF16(kTestStrings[i].text)); | 2991 render_text->SetText(WideToUTF16(kTestStrings[i].text)); |
2881 render_text->Draw(canvas()); | 2992 render_text->Draw(canvas()); |
(...skipping 1009 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3891 const int kWordTwoStartIndex = 6; | 4002 const int kWordTwoStartIndex = 6; |
3892 | 4003 |
3893 RenderText* render_text = GetRenderText(); | 4004 RenderText* render_text = GetRenderText(); |
3894 render_text->SetDisplayRect(Rect(100, 30)); | 4005 render_text->SetDisplayRect(Rect(100, 30)); |
3895 render_text->SetText(ltr); | 4006 render_text->SetText(ltr); |
3896 render_text->ApplyWeight(Font::Weight::SEMIBOLD, Range(0, 3)); | 4007 render_text->ApplyWeight(Font::Weight::SEMIBOLD, Range(0, 3)); |
3897 render_text->ApplyStyle(UNDERLINE, true, Range(1, 5)); | 4008 render_text->ApplyStyle(UNDERLINE, true, Range(1, 5)); |
3898 render_text->ApplyStyle(ITALIC, true, Range(3, 8)); | 4009 render_text->ApplyStyle(ITALIC, true, Range(3, 8)); |
3899 render_text->ApplyStyle(DIAGONAL_STRIKE, true, Range(5, 7)); | 4010 render_text->ApplyStyle(DIAGONAL_STRIKE, true, Range(5, 7)); |
3900 render_text->ApplyStyle(STRIKE, true, Range(1, 7)); | 4011 render_text->ApplyStyle(STRIKE, true, Range(1, 7)); |
| 4012 const int cursor_y = GetCursorYForTesting(); |
3901 | 4013 |
3902 const std::vector<RenderText::FontSpan> font_spans = | 4014 const std::vector<RenderText::FontSpan> font_spans = |
3903 render_text->GetFontSpansForTesting(); | 4015 render_text->GetFontSpansForTesting(); |
3904 | 4016 |
3905 // Create expected decorated text instances. | 4017 // Create expected decorated text instances. |
3906 DecoratedText expected_word_1; | 4018 DecoratedText expected_word_1; |
3907 expected_word_1.text = ASCIIToUTF16("ab"); | 4019 expected_word_1.text = ASCIIToUTF16("ab"); |
3908 // Attributes for the characters 'a' and 'b' at logical indices 2 and 3 | 4020 // Attributes for the characters 'a' and 'b' at logical indices 2 and 3 |
3909 // respectively. | 4021 // respectively. |
3910 expected_word_1.attributes.push_back(CreateRangedAttribute( | 4022 expected_word_1.attributes.push_back(CreateRangedAttribute( |
(...skipping 13 matching lines...) Expand all Loading... |
3924 ITALIC_MASK | DIAGONAL_STRIKE_MASK | STRIKE_MASK)); | 4036 ITALIC_MASK | DIAGONAL_STRIKE_MASK | STRIKE_MASK)); |
3925 const Rect left_glyph_word_2 = render_text->GetCursorBounds( | 4037 const Rect left_glyph_word_2 = render_text->GetCursorBounds( |
3926 SelectionModel(kWordTwoStartIndex, CURSOR_FORWARD), false); | 4038 SelectionModel(kWordTwoStartIndex, CURSOR_FORWARD), false); |
3927 | 4039 |
3928 DecoratedText decorated_word; | 4040 DecoratedText decorated_word; |
3929 Point baseline_point; | 4041 Point baseline_point; |
3930 | 4042 |
3931 { | 4043 { |
3932 SCOPED_TRACE(base::StringPrintf("Query to the left of text bounds")); | 4044 SCOPED_TRACE(base::StringPrintf("Query to the left of text bounds")); |
3933 EXPECT_TRUE(render_text->GetDecoratedWordAtPoint( | 4045 EXPECT_TRUE(render_text->GetDecoratedWordAtPoint( |
3934 Point(-5, 5), &decorated_word, &baseline_point)); | 4046 Point(-5, cursor_y), &decorated_word, &baseline_point)); |
3935 VerifyDecoratedWordsAreEqual(expected_word_1, decorated_word); | 4047 VerifyDecoratedWordsAreEqual(expected_word_1, decorated_word); |
3936 EXPECT_TRUE(left_glyph_word_1.Contains(baseline_point)); | 4048 EXPECT_TRUE(left_glyph_word_1.Contains(baseline_point)); |
3937 } | 4049 } |
3938 { | 4050 { |
3939 SCOPED_TRACE(base::StringPrintf("Query to the right of text bounds")); | 4051 SCOPED_TRACE(base::StringPrintf("Query to the right of text bounds")); |
3940 EXPECT_TRUE(render_text->GetDecoratedWordAtPoint( | 4052 EXPECT_TRUE(render_text->GetDecoratedWordAtPoint( |
3941 Point(105, 5), &decorated_word, &baseline_point)); | 4053 Point(105, cursor_y), &decorated_word, &baseline_point)); |
3942 VerifyDecoratedWordsAreEqual(expected_word_2, decorated_word); | 4054 VerifyDecoratedWordsAreEqual(expected_word_2, decorated_word); |
3943 EXPECT_TRUE(left_glyph_word_2.Contains(baseline_point)); | 4055 EXPECT_TRUE(left_glyph_word_2.Contains(baseline_point)); |
3944 } | 4056 } |
3945 | 4057 |
3946 for (size_t i = 0; i < render_text->text().length(); i++) { | 4058 for (size_t i = 0; i < render_text->text().length(); i++) { |
3947 SCOPED_TRACE(base::StringPrintf("Case[%" PRIuS "]", i)); | 4059 SCOPED_TRACE(base::StringPrintf("Case[%" PRIuS "]", i)); |
3948 // Query the decorated word using the origin of the i'th glyph's bounds. | 4060 // Query the decorated word using the origin of the i'th glyph's bounds. |
3949 const Point query = | 4061 const Point query = |
3950 render_text->GetCursorBounds(SelectionModel(i, CURSOR_FORWARD), false) | 4062 render_text->GetCursorBounds(SelectionModel(i, CURSOR_FORWARD), false) |
3951 .origin(); | 4063 .origin(); |
(...skipping 23 matching lines...) Expand all Loading... |
3975 const int kWordTwoStartIndex = 5; | 4087 const int kWordTwoStartIndex = 5; |
3976 | 4088 |
3977 RenderText* render_text = GetRenderText(); | 4089 RenderText* render_text = GetRenderText(); |
3978 render_text->SetDisplayRect(Rect(100, 30)); | 4090 render_text->SetDisplayRect(Rect(100, 30)); |
3979 render_text->SetText(rtl); | 4091 render_text->SetText(rtl); |
3980 render_text->ApplyWeight(Font::Weight::SEMIBOLD, Range(2, 3)); | 4092 render_text->ApplyWeight(Font::Weight::SEMIBOLD, Range(2, 3)); |
3981 render_text->ApplyStyle(UNDERLINE, true, Range(3, 6)); | 4093 render_text->ApplyStyle(UNDERLINE, true, Range(3, 6)); |
3982 render_text->ApplyStyle(ITALIC, true, Range(0, 3)); | 4094 render_text->ApplyStyle(ITALIC, true, Range(0, 3)); |
3983 render_text->ApplyStyle(DIAGONAL_STRIKE, true, Range(0, 2)); | 4095 render_text->ApplyStyle(DIAGONAL_STRIKE, true, Range(0, 2)); |
3984 render_text->ApplyStyle(STRIKE, true, Range(2, 5)); | 4096 render_text->ApplyStyle(STRIKE, true, Range(2, 5)); |
| 4097 const int cursor_y = GetCursorYForTesting(); |
3985 | 4098 |
3986 const std::vector<RenderText::FontSpan> font_spans = | 4099 const std::vector<RenderText::FontSpan> font_spans = |
3987 render_text->GetFontSpansForTesting(); | 4100 render_text->GetFontSpansForTesting(); |
3988 | 4101 |
3989 // Create expected decorated text instance. | 4102 // Create expected decorated text instance. |
3990 DecoratedText expected_word_1; | 4103 DecoratedText expected_word_1; |
3991 expected_word_1.text = WideToUTF16(L"\x0634\x0632"); | 4104 expected_word_1.text = WideToUTF16(L"\x0634\x0632"); |
3992 // Attributes for characters at logical indices 1 and 2. | 4105 // Attributes for characters at logical indices 1 and 2. |
3993 expected_word_1.attributes.push_back(CreateRangedAttribute( | 4106 expected_word_1.attributes.push_back(CreateRangedAttribute( |
3994 font_spans, 0, kWordOneStartIndex, Font::Weight::NORMAL, | 4107 font_spans, 0, kWordOneStartIndex, Font::Weight::NORMAL, |
(...skipping 12 matching lines...) Expand all Loading... |
4007 font_spans, 0, kWordTwoStartIndex, Font::Weight::NORMAL, UNDERLINE_MASK)); | 4120 font_spans, 0, kWordTwoStartIndex, Font::Weight::NORMAL, UNDERLINE_MASK)); |
4008 const Rect left_glyph_word_2 = render_text->GetCursorBounds( | 4121 const Rect left_glyph_word_2 = render_text->GetCursorBounds( |
4009 SelectionModel(kWordTwoStartIndex, CURSOR_FORWARD), false); | 4122 SelectionModel(kWordTwoStartIndex, CURSOR_FORWARD), false); |
4010 | 4123 |
4011 DecoratedText decorated_word; | 4124 DecoratedText decorated_word; |
4012 Point baseline_point; | 4125 Point baseline_point; |
4013 | 4126 |
4014 { | 4127 { |
4015 SCOPED_TRACE(base::StringPrintf("Query to the left of text bounds")); | 4128 SCOPED_TRACE(base::StringPrintf("Query to the left of text bounds")); |
4016 EXPECT_TRUE(render_text->GetDecoratedWordAtPoint( | 4129 EXPECT_TRUE(render_text->GetDecoratedWordAtPoint( |
4017 Point(-5, 5), &decorated_word, &baseline_point)); | 4130 Point(-5, cursor_y), &decorated_word, &baseline_point)); |
4018 VerifyDecoratedWordsAreEqual(expected_word_2, decorated_word); | 4131 VerifyDecoratedWordsAreEqual(expected_word_2, decorated_word); |
4019 EXPECT_TRUE(left_glyph_word_2.Contains(baseline_point)); | 4132 EXPECT_TRUE(left_glyph_word_2.Contains(baseline_point)); |
4020 } | 4133 } |
4021 { | 4134 { |
4022 SCOPED_TRACE(base::StringPrintf("Query to the right of text bounds")); | 4135 SCOPED_TRACE(base::StringPrintf("Query to the right of text bounds")); |
4023 EXPECT_TRUE(render_text->GetDecoratedWordAtPoint( | 4136 EXPECT_TRUE(render_text->GetDecoratedWordAtPoint( |
4024 Point(105, 5), &decorated_word, &baseline_point)); | 4137 Point(105, cursor_y), &decorated_word, &baseline_point)); |
4025 VerifyDecoratedWordsAreEqual(expected_word_1, decorated_word); | 4138 VerifyDecoratedWordsAreEqual(expected_word_1, decorated_word); |
4026 EXPECT_TRUE(left_glyph_word_1.Contains(baseline_point)); | 4139 EXPECT_TRUE(left_glyph_word_1.Contains(baseline_point)); |
4027 } | 4140 } |
4028 | 4141 |
4029 for (size_t i = 0; i < render_text->text().length(); i++) { | 4142 for (size_t i = 0; i < render_text->text().length(); i++) { |
4030 SCOPED_TRACE(base::StringPrintf("Case[%" PRIuS "]", i)); | 4143 SCOPED_TRACE(base::StringPrintf("Case[%" PRIuS "]", i)); |
4031 | 4144 |
4032 // Query the decorated word using the top right point of the i'th glyph's | 4145 // Query the decorated word using the top right point of the i'th glyph's |
4033 // bounds. | 4146 // bounds. |
4034 const Point query = | 4147 const Point query = |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4069 &baseline_point)); | 4182 &baseline_point)); |
4070 | 4183 |
4071 // False should be returned for obscured text. | 4184 // False should be returned for obscured text. |
4072 render_text->SetObscured(true); | 4185 render_text->SetObscured(true); |
4073 query = render_text->GetCursorBounds(SelectionModel(0, CURSOR_FORWARD), false) | 4186 query = render_text->GetCursorBounds(SelectionModel(0, CURSOR_FORWARD), false) |
4074 .origin(); | 4187 .origin(); |
4075 EXPECT_FALSE(render_text->GetDecoratedWordAtPoint(query, &decorated_word, | 4188 EXPECT_FALSE(render_text->GetDecoratedWordAtPoint(query, &decorated_word, |
4076 &baseline_point)); | 4189 &baseline_point)); |
4077 } | 4190 } |
4078 | 4191 |
| 4192 // Tests text selection made at end points of individual lines of multiline |
| 4193 // text. |
| 4194 TEST_P(RenderTextHarfBuzzTest, LineEndSelections) { |
| 4195 const wchar_t* const ltr = L"abc\n\ndef"; |
| 4196 const wchar_t* const rtl = L"\x5d0\x5d1\x5d2\n\n\x5d3\x5d4\x5d5"; |
| 4197 const int left_x = -100; |
| 4198 const int right_x = 200; |
| 4199 struct { |
| 4200 const wchar_t* const text; |
| 4201 const int line_num; |
| 4202 const int x; |
| 4203 const wchar_t* const selected_text; |
| 4204 } cases[] = { |
| 4205 {ltr, 1, left_x, L"abc\n"}, |
| 4206 {ltr, 1, right_x, L"abc\n\n"}, |
| 4207 {ltr, 2, left_x, L"abc\n\n"}, |
| 4208 {ltr, 2, right_x, ltr}, |
| 4209 |
| 4210 {rtl, 1, left_x, L"\x5d0\x5d1\x5d2\n\n"}, |
| 4211 {rtl, 1, right_x, L"\x5d0\x5d1\x5d2\n"}, |
| 4212 {rtl, 2, left_x, rtl}, |
| 4213 {rtl, 2, right_x, L"\x5d0\x5d1\x5d2\n\n"}, |
| 4214 }; |
| 4215 |
| 4216 RenderText* render_text = GetRenderText(); |
| 4217 render_text->SetMultiline(true); |
| 4218 render_text->SetDisplayRect(Rect(200, 1000)); |
| 4219 |
| 4220 for (size_t i = 0; i < arraysize(cases); i++) { |
| 4221 SCOPED_TRACE(base::StringPrintf("Testing case %" PRIuS "", i)); |
| 4222 render_text->SetText(WideToUTF16(cases[i].text)); |
| 4223 test_api()->EnsureLayout(); |
| 4224 |
| 4225 EXPECT_EQ(3u, test_api()->lines().size()); |
| 4226 // Position the cursor at the logical beginning of text. |
| 4227 render_text->SelectRange(Range(0)); |
| 4228 |
| 4229 render_text->MoveCursorTo( |
| 4230 Point(cases[i].x, GetCursorYForTesting(cases[i].line_num)), true); |
| 4231 EXPECT_EQ(WideToUTF16(cases[i].selected_text), |
| 4232 GetSelectedText(render_text)); |
| 4233 } |
| 4234 } |
| 4235 |
| 4236 // Tests that GetSubstringBounds returns the correct bounds for multiline text. |
| 4237 TEST_P(RenderTextHarfBuzzTest, GetSubstringBoundsMultiline) { |
| 4238 RenderText* render_text = GetRenderText(); |
| 4239 render_text->SetMultiline(true); |
| 4240 render_text->SetDisplayRect(Rect(200, 1000)); |
| 4241 render_text->SetText(WideToUTF16(L"abc\n\ndef")); |
| 4242 test_api()->EnsureLayout(); |
| 4243 |
| 4244 const std::vector<Range> line_char_range = {Range(0, 3), Range(4, 5), |
| 4245 Range(5, 8)}; |
| 4246 |
| 4247 // Test bounds for individual lines. |
| 4248 EXPECT_EQ(3u, test_api()->lines().size()); |
| 4249 Rect expected_total_bounds; |
| 4250 for (size_t i = 0; i < test_api()->lines().size(); i++) { |
| 4251 SCOPED_TRACE(base::StringPrintf("Testing bounds for line %" PRIuS "", i)); |
| 4252 const internal::Line& line = test_api()->lines()[i]; |
| 4253 const Size line_size(std::ceil(line.size.width()), |
| 4254 std::ceil(line.size.height())); |
| 4255 const Rect expected_line_bounds = |
| 4256 render_text->GetLineOffset(i) + Rect(line_size); |
| 4257 expected_total_bounds.Union(expected_line_bounds); |
| 4258 |
| 4259 render_text->SelectRange(line_char_range[i]); |
| 4260 EXPECT_EQ(expected_line_bounds, GetSelectionBoundsUnion()); |
| 4261 } |
| 4262 |
| 4263 // Test complete bounds. |
| 4264 render_text->SelectAll(false); |
| 4265 EXPECT_EQ(expected_total_bounds, GetSelectionBoundsUnion()); |
| 4266 } |
| 4267 |
4079 // Prefix for test instantiations intentionally left blank since each test | 4268 // Prefix for test instantiations intentionally left blank since each test |
4080 // fixture class has a single parameterization. | 4269 // fixture class has a single parameterization. |
4081 #if defined(OS_MACOSX) | 4270 #if defined(OS_MACOSX) |
4082 INSTANTIATE_TEST_CASE_P(, | 4271 INSTANTIATE_TEST_CASE_P(, |
4083 RenderTextTest, | 4272 RenderTextTest, |
4084 ::testing::Values(RENDER_TEXT_HARFBUZZ, | 4273 ::testing::Values(RENDER_TEXT_HARFBUZZ, |
4085 RENDER_TEXT_MAC), | 4274 RENDER_TEXT_MAC), |
4086 PrintRenderTextBackend()); | 4275 PrintRenderTextBackend()); |
4087 INSTANTIATE_TEST_CASE_P(, | 4276 INSTANTIATE_TEST_CASE_P(, |
4088 RenderTextMacTest, | 4277 RenderTextMacTest, |
4089 ::testing::Values(RENDER_TEXT_MAC), | 4278 ::testing::Values(RENDER_TEXT_MAC), |
4090 PrintRenderTextBackend()); | 4279 PrintRenderTextBackend()); |
4091 #else | 4280 #else |
4092 INSTANTIATE_TEST_CASE_P(, | 4281 INSTANTIATE_TEST_CASE_P(, |
4093 RenderTextTest, | 4282 RenderTextTest, |
4094 ::testing::Values(RENDER_TEXT_HARFBUZZ), | 4283 ::testing::Values(RENDER_TEXT_HARFBUZZ), |
4095 PrintRenderTextBackend()); | 4284 PrintRenderTextBackend()); |
4096 #endif | 4285 #endif |
4097 | 4286 |
4098 INSTANTIATE_TEST_CASE_P(, | 4287 INSTANTIATE_TEST_CASE_P(, |
4099 RenderTextHarfBuzzTest, | 4288 RenderTextHarfBuzzTest, |
4100 ::testing::Values(RENDER_TEXT_HARFBUZZ), | 4289 ::testing::Values(RENDER_TEXT_HARFBUZZ), |
4101 PrintRenderTextBackend()); | 4290 PrintRenderTextBackend()); |
4102 | 4291 |
4103 } // namespace gfx | 4292 } // namespace gfx |
OLD | NEW |