Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(9)

Side by Side Diff: ui/gfx/render_text_unittest.cc

Issue 2541313002: RenderTextHarfBuzz: Add support for multi line text selection. (Closed)
Patch Set: FindCursorPosition now returns valid grapheme boundaries. crbug.com/673986 Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
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 whole grapheme is selected even if the selection is set
1794 // 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 2 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
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
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
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
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
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
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 = 100;
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698