Changeset 153652 in webkit
- Timestamp:
- Aug 2, 2013 9:02:11 AM (11 years ago)
- Location:
- trunk/Source
- Files:
-
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebCore/ChangeLog
r153650 r153652 1 2013-08-02 Mario Sanchez Prada <mario.prada@samsung.com> 2 3 Implement atk_text_get_text_*_offset for WORD 4 https://bugs.webkit.org/show_bug.cgi?id=114871 5 6 Reviewed by Martin Robinson. 7 8 Re-implement this functions without using GailTextUtil nor Pango. 9 10 * accessibility/atk/WebKitAccessibleInterfaceText.cpp: 11 (textForObject): Made the parameter a const, to avoid warnings. 12 (getSelectionOffsetsForObject): Add special cases for END boundaries. 13 (emptyTextSelectionAtOffset): Convenience function to be used in 14 early returns from functions returning both text and offsets. 15 (webkitAccessibleTextGetChar): Use emptyTextSelectionAtOffset(), 16 and remove checks that are now done outside of this function, in 17 webkitAccessibleTextGetTextForOffset(). 18 (nextWordStartPosition): Helper function to reliably find the 19 start of the next word as and user would do it by navigating with 20 Ctrl and the arrows (considering spaces and punctuation). 21 (previousWordEndPosition): Similar to nextWordStartPosition, but 22 written to help find the end of the previous one. 23 (wordAtPositionForAtkBoundary): Helper function to find the word 24 at a given position considering values of AtkTextBoundary. 25 (numberOfReplacedElementsBeforeOffset): Helper function to help 26 figure out how many embedded objects we have exposed for an 27 AtkText object, used to adjust offsets coming from outside. 28 (webkitAccessibleTextGetWordForBoundary): New function, 29 implementing atk_text_get_text_*_offset for WORD. 30 (webkitAccessibleTextGetTextForOffset): Replace usage of Gail for 31 WORD boundaries with webkitAccessibleTextGetWordForBoundary(). 32 Also, moved the initialization of the start and end offsets to the 33 bottom, into the gail/pango section, since those values will be 34 from now on initialized in getSelectionOffsetsForObject(). 35 (webkitAccessibleTextGetSelection): Removed the initialization of 36 the start and end offsets, since those values will be from now on 37 initialized in getSelectionOffsetsForObject(). 38 1 39 2013-08-02 Zoltan Arvai <zarvai@inf.u-szeged.hu> 2 40 -
trunk/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceText.cpp
r153427 r153652 3 3 * Copyright (C) 2009 Jan Alonzo 4 4 * Copyright (C) 2009, 2010, 2011, 2012 Igalia S.L. 5 * Copyright (C) 2013 Samsung Electronics. All rights reserved. 5 6 * 6 7 * Portions from Mozilla a11y, copyright as follows: … … 50 51 #include "WebKitAccessibleWrapperAtk.h" 51 52 #include "htmlediting.h" 53 #include <wtf/NotFound.h> 52 54 #include <wtf/gobject/GOwnPtr.h> 53 55 #include <wtf/text/CString.h> … … 137 139 } 138 140 139 static gchar* textForObject( AccessibilityObject* coreObject)141 static gchar* textForObject(const AccessibilityObject* coreObject) 140 142 { 141 143 GString* str = g_string_new(0); … … 560 562 RefPtr<Range> nodeRange = Range::create(node->document(), nodeRangeStart, nodeRangeEnd); 561 563 int rangeLength = TextIterator::rangeLength(nodeRange.get(), true); 564 565 // Special cases that are only relevant when working with *_END boundaries. 566 if (selection.affinity() == UPSTREAM) { 567 VisiblePosition visibleStart(nodeRangeStart, UPSTREAM); 568 VisiblePosition visibleEnd(nodeRangeEnd, UPSTREAM); 569 570 // We need to adjust offsets when finding wrapped lines so the position at the end 571 // of the line is properly taking into account when calculating the offsets. 572 if (isEndOfLine(visibleStart) && !lineBreakExistsAtVisiblePosition(visibleStart)) { 573 if (isStartOfLine(visibleStart.next())) 574 rangeLength++; 575 576 if (!isEndOfBlock(visibleStart)) 577 startOffset = std::max(startOffset - 1, 0); 578 } 579 580 if (isEndOfLine(visibleEnd) && !lineBreakExistsAtVisiblePosition(visibleEnd) && !isEndOfBlock(visibleEnd)) 581 rangeLength--; 582 } 583 562 584 endOffset = std::min(startOffset + rangeLength, static_cast<int>(accessibilityObjectLength(coreObject))); 563 585 } … … 614 636 }; 615 637 616 static gchar* webkitAccessibleTextGetChar(AtkText* text, gint offset, GetTextRelativePosition textPosition, gint* startOffset, gint* endOffset) 617 { 618 AccessibilityObject* coreObject = core(text); 619 if (!coreObject || !coreObject->isAccessibilityRenderObject()) 620 return g_strdup(""); 621 638 // Convenience function to be used in early returns. 639 static char* emptyTextSelectionAtOffset(int offset, int* startOffset, int* endOffset) 640 { 641 *startOffset = offset; 642 *endOffset = offset; 643 return g_strdup(""); 644 } 645 646 static char* webkitAccessibleTextGetChar(AtkText* text, int offset, GetTextRelativePosition textPosition, int* startOffset, int* endOffset) 647 { 622 648 int actualOffset = offset; 623 649 if (textPosition == GetTextPositionBefore) … … 641 667 } 642 668 669 static VisiblePosition nextWordStartPosition(const VisiblePosition &position) 670 { 671 VisiblePosition positionAfterCurrentWord = nextWordPosition(position); 672 673 // In order to skip spaces when moving right, we advance one word further 674 // and then move one word back. This will put us at the beginning of the 675 // word following. 676 VisiblePosition positionAfterSpacingAndFollowingWord = nextWordPosition(positionAfterCurrentWord); 677 678 if (positionAfterSpacingAndFollowingWord != positionAfterCurrentWord) 679 positionAfterCurrentWord = previousWordPosition(positionAfterSpacingAndFollowingWord); 680 681 bool movingBackwardsMovedPositionToStartOfCurrentWord = positionAfterCurrentWord == previousWordPosition(nextWordPosition(position)); 682 if (movingBackwardsMovedPositionToStartOfCurrentWord) 683 positionAfterCurrentWord = positionAfterSpacingAndFollowingWord; 684 685 return positionAfterCurrentWord; 686 } 687 688 static VisiblePosition previousWordEndPosition(const VisiblePosition &position) 689 { 690 // We move forward and then backward to position ourselves at the beginning 691 // of the current word for this boundary, making the most of the semantics 692 // of previousWordPosition() and nextWordPosition(). 693 VisiblePosition positionAtStartOfCurrentWord = previousWordPosition(nextWordPosition(position)); 694 VisiblePosition positionAtPreviousWord = previousWordPosition(position); 695 696 // Need to consider special cases (punctuation) when we are in the last word of a sentence. 697 if (isStartOfWord(position) && positionAtPreviousWord != position && positionAtPreviousWord == positionAtStartOfCurrentWord) 698 return nextWordPosition(positionAtStartOfCurrentWord); 699 700 // In order to skip spaces when moving left, we advance one word backwards 701 // and then move one word forward. This will put us at the beginning of 702 // the word following. 703 VisiblePosition positionBeforeSpacingAndPreceedingWord = previousWordPosition(positionAtStartOfCurrentWord); 704 705 if (positionBeforeSpacingAndPreceedingWord != positionAtStartOfCurrentWord) 706 positionAtStartOfCurrentWord = nextWordPosition(positionBeforeSpacingAndPreceedingWord); 707 708 bool movingForwardMovedPositionToEndOfCurrentWord = nextWordPosition(positionAtStartOfCurrentWord) == previousWordPosition(nextWordPosition(position)); 709 if (movingForwardMovedPositionToEndOfCurrentWord) 710 positionAtStartOfCurrentWord = positionBeforeSpacingAndPreceedingWord; 711 712 return positionAtStartOfCurrentWord; 713 } 714 715 static VisibleSelection wordAtPositionForAtkBoundary(const AccessibilityObject* coreObject, const VisiblePosition& position, AtkTextBoundary boundaryType) 716 { 717 VisiblePosition startPosition; 718 VisiblePosition endPosition; 719 720 switch (boundaryType) { 721 case ATK_TEXT_BOUNDARY_WORD_START: 722 // isStartOfWord() returns true both when at the beginning of a "real" word 723 // as when at the beginning of a whitespace range between two "real" words, 724 // since that whitespace is considered a "word" as well. And in case we are 725 // already at the beginning of a "real" word we do not need to look backwards. 726 if (isStartOfWord(position) && isWhitespace(position.characterBefore())) 727 startPosition = position; 728 else 729 startPosition = previousWordPosition(position); 730 endPosition = nextWordStartPosition(startPosition); 731 732 // We need to make sure that we look for the word in the current line when 733 // we are at the beginning of a new line, and not look into the previous one 734 // at all, which might happen when lines belong to different nodes. 735 if (isStartOfLine(position) && isStartOfLine(endPosition)) { 736 startPosition = endPosition; 737 endPosition = nextWordStartPosition(startPosition); 738 } 739 break; 740 741 case ATK_TEXT_BOUNDARY_WORD_END: 742 startPosition = previousWordEndPosition(position); 743 endPosition = nextWordPosition(startPosition); 744 break; 745 746 default: 747 ASSERT_NOT_REACHED(); 748 } 749 750 VisibleSelection selectedWord(startPosition, endPosition); 751 752 // We mark the selection as 'upstream' so we can use that information later, 753 // when finding the actual offsets in getSelectionOffsetsForObject(). 754 if (boundaryType == ATK_TEXT_BOUNDARY_WORD_END) 755 selectedWord.setAffinity(UPSTREAM); 756 757 return selectedWord; 758 } 759 760 static int numberOfReplacedElementsBeforeOffset(AtkText* text, unsigned offset) 761 { 762 GOwnPtr<char> textForObject(webkitAccessibleTextGetText(text, 0, offset)); 763 String textBeforeOffset = String::fromUTF8(textForObject.get()); 764 765 int count = 0; 766 size_t index = textBeforeOffset.find(objectReplacementCharacter, 0); 767 while (index < offset && index != WTF::notFound) { 768 index = textBeforeOffset.find(objectReplacementCharacter, index + 1); 769 count++; 770 } 771 return count; 772 } 773 774 static char* webkitAccessibleTextGetWordForBoundary(AtkText* text, int offset, AtkTextBoundary boundaryType, GetTextRelativePosition textPosition, int* startOffset, int* endOffset) 775 { 776 AccessibilityObject* coreObject = core(text); 777 Document* document = coreObject->document(); 778 if (!document) 779 return emptyTextSelectionAtOffset(0, startOffset, endOffset); 780 781 Node* node = getNodeForAccessibilityObject(coreObject); 782 if (!node) 783 return emptyTextSelectionAtOffset(0, startOffset, endOffset); 784 785 int actualOffset = atkOffsetToWebCoreOffset(text, offset); 786 787 // Besides of the usual conversion from ATK offsets to WebCore offsets, 788 // we need to consider the potential embedded objects that might have been 789 // inserted in the text exposed through AtkText when calculating the offset. 790 actualOffset -= numberOfReplacedElementsBeforeOffset(text, actualOffset); 791 792 VisiblePosition caretPosition = coreObject->visiblePositionForIndex(actualOffset); 793 VisibleSelection currentWord = wordAtPositionForAtkBoundary(coreObject, caretPosition, boundaryType); 794 795 // Take into account other relative positions, if needed, by 796 // calculating the new position that we would need to consider. 797 VisiblePosition newPosition = caretPosition; 798 switch (textPosition) { 799 case GetTextPositionAt: 800 break; 801 802 case GetTextPositionBefore: 803 // Early return if asking for the previous word while already at the beginning. 804 if (isFirstVisiblePositionInNode(currentWord.visibleStart(), node)) 805 return emptyTextSelectionAtOffset(0, startOffset, endOffset); 806 807 if (isStartOfLine(currentWord.end())) 808 newPosition = currentWord.visibleStart().previous(); 809 else 810 newPosition = startOfWord(currentWord.start(), LeftWordIfOnBoundary); 811 break; 812 813 case GetTextPositionAfter: 814 // Early return if asking for the following word while already at the end. 815 if (isLastVisiblePositionInNode(currentWord.visibleEnd(), node)) 816 return emptyTextSelectionAtOffset(accessibilityObjectLength(coreObject), startOffset, endOffset); 817 818 if (isEndOfLine(currentWord.end())) 819 newPosition = currentWord.visibleEnd().next(); 820 else 821 newPosition = endOfWord(currentWord.end(), RightWordIfOnBoundary); 822 break; 823 824 default: 825 ASSERT_NOT_REACHED(); 826 } 827 828 // Determine the relevant word we are actually interested in 829 // and calculate the ATK offsets for it, then return everything. 830 VisibleSelection selectedWord = newPosition != caretPosition ? wordAtPositionForAtkBoundary(coreObject, newPosition, boundaryType) : currentWord; 831 getSelectionOffsetsForObject(coreObject, selectedWord, *startOffset, *endOffset); 832 return webkitAccessibleTextGetText(text, *startOffset, *endOffset); 833 } 834 643 835 static gchar* webkitAccessibleTextGetTextForOffset(AtkText* text, gint offset, AtkTextBoundary boundaryType, GetTextRelativePosition textPosition, gint* startOffset, gint* endOffset) 644 836 { 645 // Make sure we always return valid valid values for offsets.646 *startOffset = 0;647 *endOffset = 0;837 AccessibilityObject* coreObject = core(text); 838 if (!coreObject || !coreObject->isAccessibilityRenderObject()) 839 return emptyTextSelectionAtOffset(0, startOffset, endOffset); 648 840 649 841 if (boundaryType == ATK_TEXT_BOUNDARY_CHAR) 650 842 return webkitAccessibleTextGetChar(text, offset, textPosition, startOffset, endOffset); 651 843 844 if (boundaryType == ATK_TEXT_BOUNDARY_WORD_START || boundaryType == ATK_TEXT_BOUNDARY_WORD_END) 845 return webkitAccessibleTextGetWordForBoundary(text, offset, boundaryType, textPosition, startOffset, endOffset); 846 652 847 #if PLATFORM(GTK) 653 // FIXME: Get rid of the code below once every single get_text_*_offset654 // functionhas been properly implemented without using Pango/Cairo.848 // FIXME: Get rid of the code below once every single part above 849 // has been properly implemented without using Pango/Cairo. 655 850 GailOffsetType offsetType = GAIL_AT_OFFSET; 656 851 switch (textPosition) { … … 669 864 ASSERT_NOT_REACHED(); 670 865 } 866 867 // Make sure we always return valid valid values for offsets. 868 *startOffset = 0; 869 *endOffset = 0; 671 870 672 871 return gail_text_util_get_text(getGailTextUtilForAtk(text), getPangoLayoutForAtk(text), offsetType, boundaryType, offset, startOffset, endOffset); … … 804 1003 static gchar* webkitAccessibleTextGetSelection(AtkText* text, gint selectionNum, gint* startOffset, gint* endOffset) 805 1004 { 806 // Default values, unless the contrary is proved807 *startOffset = *endOffset = 0;808 809 1005 // WebCore does not support multiple selection, so anything but 0 does not make sense for now. 810 1006 if (selectionNum) -
trunk/Source/WebKit/gtk/ChangeLog
r153466 r153652 1 2013-08-02 Mario Sanchez Prada <mario.prada@samsung.com> 2 3 Implement atk_text_get_text_*_offset for WORD 4 https://bugs.webkit.org/show_bug.cgi?id=114871 5 6 Reviewed by Martin Robinson. 7 8 Updated current unit tests and add a new one specific for embedded 9 objects, to ensure we are covering even more cases than before. 10 11 * tests/testatk.c: 12 (testWebkitAtkGetTextAtOffsetWithEmbeddedObjects): New. 13 (main): Added new test to the test suite. 14 1 15 2013-07-30 Carlos Garcia Campos <cgarcia@igalia.com> 2 16 -
trunk/Source/WebKit/gtk/tests/testatk.c
r153427 r153652 1 1 /* 2 2 * Copyright (C) 2009 Igalia S.L. 3 * Copyright (C) 2013 Samsung Electronics. All rights reserved. 3 4 * 4 5 * This library is free software; you can redistribute it and/or … … 53 54 static const char* contentsWithWrappedLines = "<html><body><p style='max-width:150px;'>This is one line wrapped because of the maximum width of its container.</p><p>This is another line wrapped<br>because of one forced<br>line break in the middle.</body></html>"; 54 55 56 static const char* contentsWithEmbeddedObjects = "<html><body>This is one line containing two <img> embedded objects <img> in the middle.</body></html>"; 57 55 58 static const char* comboBoxSelector = "<html><body><select><option selected value='foo'>foo</option><option value='bar'>bar</option></select></body></html>"; 56 59 … … 1052 1055 g_object_unref(paragraph2); 1053 1056 1057 g_object_unref(webView); 1058 } 1059 1060 static void testWebkitAtkGetTextAtOffsetWithEmbeddedObjects() 1061 { 1062 WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new()); 1063 g_object_ref_sink(webView); 1064 GtkAllocation allocation = { 0, 0, 800, 600 }; 1065 gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation); 1066 webkit_web_view_load_string(webView, contentsWithEmbeddedObjects, 0, 0, 0); 1067 1068 /* Enable caret browsing. */ 1069 WebKitWebSettings* settings = webkit_web_view_get_settings(webView); 1070 g_object_set(settings, "enable-caret-browsing", TRUE, NULL); 1071 webkit_web_view_set_settings(webView, settings); 1072 1073 /* Get to the inner AtkText object. */ 1074 AtkObject* object = getWebAreaObject(webView); 1075 g_assert(object); 1076 1077 /* Check the paragraph with the text wrapped because of max-width. */ 1078 AtkText* paragraph = ATK_TEXT(atk_object_ref_accessible_child(object, 0)); 1079 g_assert(ATK_IS_TEXT(paragraph)); 1080 1081 gchar* text = atk_text_get_text(paragraph, 0, -1); 1082 g_assert_cmpstr(text, ==, "This is one line containing two \357\277\274 embedded objects \357\277\274 in the middle."); 1083 g_free(text); 1084 1085 /* Check right before the first embedded object */ 1086 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_CHAR, 32, "\357\277\274", 32, 33); 1087 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START, 32, "two \357\277\274 ", 28, 34); 1088 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END, 32, " \357\277\274 embedded", 31, 42); 1089 1090 /* Check right after the first embedded object (and before the first word after it) */ 1091 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_CHAR, 33, " ", 33, 34); 1092 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START, 33, "two \357\277\274 ", 28, 34); 1093 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END, 33, " \357\277\274 embedded", 31, 42); 1094 1095 /* Check at the beginning of the first word between the two embedded objects */ 1096 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_CHAR, 34, "e", 34, 35); 1097 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START, 34, "embedded ", 34, 43); 1098 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END, 34, " \357\277\274 embedded", 31, 42); 1099 1100 /* Check at the end of the first word between the two embedded objects */ 1101 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_CHAR, 42, " ", 42, 43); 1102 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START, 42, "embedded ", 34, 43); 1103 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END, 42, " objects", 42, 50); 1104 1105 /* Check right before the second embedded object */ 1106 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_CHAR, 51, "\357\277\274", 51, 52); 1107 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START, 51, "objects \357\277\274 ", 43, 53); 1108 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END, 51, " \357\277\274 in", 50, 55); 1109 1110 /* Check right after the second embedded object (and before the first word after it) */ 1111 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_CHAR, 52, " ", 52, 53); 1112 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START, 52, "objects \357\277\274 ", 43, 53); 1113 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END, 52, " \357\277\274 in", 50, 55); 1114 1115 /* Check at the beginning of the first word after the two embedded objects */ 1116 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_CHAR, 53, "i", 53, 54); 1117 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START, 53, "in ", 53, 56); 1118 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END, 53, " \357\277\274 in", 50, 55); 1119 1120 /* Check at the end of the first word after the two embedded objects */ 1121 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_CHAR, 55, " ", 55, 56); 1122 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START, 55, "in ", 53, 56); 1123 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END, 55, " the", 55, 59); 1124 1125 g_object_unref(paragraph); 1054 1126 g_object_unref(webView); 1055 1127 } … … 2126 2198 g_test_add_func("/webkit/atk/getTextAtOffsetWithSpecialCharacters", testWebkitAtkGetTextAtOffsetWithSpecialCharacters); 2127 2199 g_test_add_func("/webkit/atk/getTextAtOffsetWithWrappedLines", testWebkitAtkGetTextAtOffsetWithWrappedLines); 2200 g_test_add_func("/webkit/atk/getTextAtOffsetWithEmbeddedObjects", testWebkitAtkGetTextAtOffsetWithEmbeddedObjects); 2128 2201 g_test_add_func("/webkit/atk/getTextInParagraphAndBodySimple", testWebkitAtkGetTextInParagraphAndBodySimple); 2129 2202 g_test_add_func("/webkit/atk/getTextInParagraphAndBodyModerate", testWebkitAtkGetTextInParagraphAndBodyModerate);
Note: See TracChangeset
for help on using the changeset viewer.