Title: [153652] trunk/Source
Revision
153652
Author
ma...@webkit.org
Date
2013-08-02 09:02:11 -0700 (Fri, 02 Aug 2013)

Log Message

Implement atk_text_get_text_*_offset for WORD
https://bugs.webkit.org/show_bug.cgi?id=114871

Reviewed by Martin Robinson.

Source/WebCore:

Re-implement this functions without using GailTextUtil nor Pango.

* accessibility/atk/WebKitAccessibleInterfaceText.cpp:
(textForObject): Made the parameter a const, to avoid warnings.
(getSelectionOffsetsForObject): Add special cases for END boundaries.
(emptyTextSelectionAtOffset): Convenience function to be used in
early returns from functions returning both text and offsets.
(webkitAccessibleTextGetChar): Use emptyTextSelectionAtOffset(),
and remove checks that are now done outside of this function, in
webkitAccessibleTextGetTextForOffset().
(nextWordStartPosition): Helper function to reliably find the
start of the next word as and user would do it by navigating with
Ctrl and the arrows (considering spaces and punctuation).
(previousWordEndPosition): Similar to nextWordStartPosition, but
written to help find the end of the previous one.
(wordAtPositionForAtkBoundary): Helper function to find the word
at a given position considering values of AtkTextBoundary.
(numberOfReplacedElementsBeforeOffset): Helper function to help
figure out how many embedded objects we have exposed for an
AtkText object, used to adjust offsets coming from outside.
(webkitAccessibleTextGetWordForBoundary): New function,
implementing atk_text_get_text_*_offset for WORD.
(webkitAccessibleTextGetTextForOffset): Replace usage of Gail for
WORD boundaries with webkitAccessibleTextGetWordForBoundary().
Also, moved the initialization of the start and end offsets to the
bottom, into the gail/pango section, since those values will be
from now on initialized in getSelectionOffsetsForObject().
(webkitAccessibleTextGetSelection): Removed the initialization of
the start and end offsets, since those values will be from now on
initialized in getSelectionOffsetsForObject().

Source/WebKit/gtk:

Updated current unit tests and add a new one specific for embedded
objects, to ensure we are covering even more cases than before.

* tests/testatk.c:
(testWebkitAtkGetTextAtOffsetWithEmbeddedObjects): New.
(main): Added new test to the test suite.

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (153651 => 153652)


--- trunk/Source/WebCore/ChangeLog	2013-08-02 15:52:39 UTC (rev 153651)
+++ trunk/Source/WebCore/ChangeLog	2013-08-02 16:02:11 UTC (rev 153652)
@@ -1,3 +1,41 @@
+2013-08-02  Mario Sanchez Prada  <mario.pr...@samsung.com>
+
+        Implement atk_text_get_text_*_offset for WORD
+        https://bugs.webkit.org/show_bug.cgi?id=114871
+
+        Reviewed by Martin Robinson.
+
+        Re-implement this functions without using GailTextUtil nor Pango.
+
+        * accessibility/atk/WebKitAccessibleInterfaceText.cpp:
+        (textForObject): Made the parameter a const, to avoid warnings.
+        (getSelectionOffsetsForObject): Add special cases for END boundaries.
+        (emptyTextSelectionAtOffset): Convenience function to be used in
+        early returns from functions returning both text and offsets.
+        (webkitAccessibleTextGetChar): Use emptyTextSelectionAtOffset(),
+        and remove checks that are now done outside of this function, in
+        webkitAccessibleTextGetTextForOffset().
+        (nextWordStartPosition): Helper function to reliably find the
+        start of the next word as and user would do it by navigating with
+        Ctrl and the arrows (considering spaces and punctuation).
+        (previousWordEndPosition): Similar to nextWordStartPosition, but
+        written to help find the end of the previous one.
+        (wordAtPositionForAtkBoundary): Helper function to find the word
+        at a given position considering values of AtkTextBoundary.
+        (numberOfReplacedElementsBeforeOffset): Helper function to help
+        figure out how many embedded objects we have exposed for an
+        AtkText object, used to adjust offsets coming from outside.
+        (webkitAccessibleTextGetWordForBoundary): New function,
+        implementing atk_text_get_text_*_offset for WORD.
+        (webkitAccessibleTextGetTextForOffset): Replace usage of Gail for
+        WORD boundaries with webkitAccessibleTextGetWordForBoundary().
+        Also, moved the initialization of the start and end offsets to the
+        bottom, into the gail/pango section, since those values will be
+        from now on initialized in getSelectionOffsetsForObject().
+        (webkitAccessibleTextGetSelection): Removed the initialization of
+        the start and end offsets, since those values will be from now on
+        initialized in getSelectionOffsetsForObject().
+
 2013-08-02  Zoltan Arvai  <zar...@inf.u-szeged.hu>
 
         Buildfix for !ENABLE(SVG) platforms after r153581.

Modified: trunk/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceText.cpp (153651 => 153652)


--- trunk/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceText.cpp	2013-08-02 15:52:39 UTC (rev 153651)
+++ trunk/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceText.cpp	2013-08-02 16:02:11 UTC (rev 153652)
@@ -2,6 +2,7 @@
  * Copyright (C) 2008 Nuanti Ltd.
  * Copyright (C) 2009 Jan Alonzo
  * Copyright (C) 2009, 2010, 2011, 2012 Igalia S.L.
+ * Copyright (C) 2013 Samsung Electronics. All rights reserved.
  *
  * Portions from Mozilla a11y, copyright as follows:
  *
@@ -49,6 +50,7 @@
 #include "WebKitAccessibleUtil.h"
 #include "WebKitAccessibleWrapperAtk.h"
 #include "htmlediting.h"
+#include <wtf/NotFound.h>
 #include <wtf/gobject/GOwnPtr.h>
 #include <wtf/text/CString.h>
 
@@ -136,7 +138,7 @@
     return g_string_free(resultText, FALSE);
 }
 
-static gchar* textForObject(AccessibilityObject* coreObject)
+static gchar* textForObject(const AccessibilityObject* coreObject)
 {
     GString* str = g_string_new(0);
 
@@ -559,6 +561,26 @@
     startOffset = webCoreOffsetToAtkOffset(coreObject, TextIterator::rangeLength(rangeInParent.get(), true));
     RefPtr<Range> nodeRange = Range::create(node->document(), nodeRangeStart, nodeRangeEnd);
     int rangeLength = TextIterator::rangeLength(nodeRange.get(), true);
+
+    // Special cases that are only relevant when working with *_END boundaries.
+    if (selection.affinity() == UPSTREAM) {
+        VisiblePosition visibleStart(nodeRangeStart, UPSTREAM);
+        VisiblePosition visibleEnd(nodeRangeEnd, UPSTREAM);
+
+        // We need to adjust offsets when finding wrapped lines so the position at the end
+        // of the line is properly taking into account when calculating the offsets.
+        if (isEndOfLine(visibleStart) && !lineBreakExistsAtVisiblePosition(visibleStart)) {
+            if (isStartOfLine(visibleStart.next()))
+                rangeLength++;
+
+            if (!isEndOfBlock(visibleStart))
+                startOffset = std::max(startOffset - 1, 0);
+        }
+
+        if (isEndOfLine(visibleEnd) && !lineBreakExistsAtVisiblePosition(visibleEnd) && !isEndOfBlock(visibleEnd))
+            rangeLength--;
+    }
+
     endOffset = std::min(startOffset + rangeLength, static_cast<int>(accessibilityObjectLength(coreObject)));
 }
 
@@ -613,12 +635,16 @@
     GetTextPositionAfter
 };
 
-static gchar* webkitAccessibleTextGetChar(AtkText* text, gint offset, GetTextRelativePosition textPosition, gint* startOffset, gint* endOffset)
+// Convenience function to be used in early returns.
+static char* emptyTextSelectionAtOffset(int offset, int* startOffset, int* endOffset)
 {
-    AccessibilityObject* coreObject = core(text);
-    if (!coreObject || !coreObject->isAccessibilityRenderObject())
-        return g_strdup("");
+    *startOffset = offset;
+    *endOffset = offset;
+    return g_strdup("");
+}
 
+static char* webkitAccessibleTextGetChar(AtkText* text, int offset, GetTextRelativePosition textPosition, int* startOffset, int* endOffset)
+{
     int actualOffset = offset;
     if (textPosition == GetTextPositionBefore)
         actualOffset--;
@@ -640,18 +666,187 @@
     return g_utf8_substring(textData.get(), *startOffset, *endOffset);
 }
 
+static VisiblePosition nextWordStartPosition(const VisiblePosition &position)
+{
+    VisiblePosition positionAfterCurrentWord = nextWordPosition(position);
+
+    // In order to skip spaces when moving right, we advance one word further
+    // and then move one word back. This will put us at the beginning of the
+    // word following.
+    VisiblePosition positionAfterSpacingAndFollowingWord = nextWordPosition(positionAfterCurrentWord);
+
+    if (positionAfterSpacingAndFollowingWord != positionAfterCurrentWord)
+        positionAfterCurrentWord = previousWordPosition(positionAfterSpacingAndFollowingWord);
+
+    bool movingBackwardsMovedPositionToStartOfCurrentWord = positionAfterCurrentWord == previousWordPosition(nextWordPosition(position));
+    if (movingBackwardsMovedPositionToStartOfCurrentWord)
+        positionAfterCurrentWord = positionAfterSpacingAndFollowingWord;
+
+    return positionAfterCurrentWord;
+}
+
+static VisiblePosition previousWordEndPosition(const VisiblePosition &position)
+{
+    // We move forward and then backward to position ourselves at the beginning
+    // of the current word for this boundary, making the most of the semantics
+    // of previousWordPosition() and nextWordPosition().
+    VisiblePosition positionAtStartOfCurrentWord = previousWordPosition(nextWordPosition(position));
+    VisiblePosition positionAtPreviousWord = previousWordPosition(position);
+
+    // Need to consider special cases (punctuation) when we are in the last word of a sentence.
+    if (isStartOfWord(position) && positionAtPreviousWord != position && positionAtPreviousWord == positionAtStartOfCurrentWord)
+        return nextWordPosition(positionAtStartOfCurrentWord);
+
+    // In order to skip spaces when moving left, we advance one word backwards
+    // and then move one word forward. This will put us at the beginning of
+    // the word following.
+    VisiblePosition positionBeforeSpacingAndPreceedingWord = previousWordPosition(positionAtStartOfCurrentWord);
+
+    if (positionBeforeSpacingAndPreceedingWord != positionAtStartOfCurrentWord)
+        positionAtStartOfCurrentWord = nextWordPosition(positionBeforeSpacingAndPreceedingWord);
+
+    bool movingForwardMovedPositionToEndOfCurrentWord = nextWordPosition(positionAtStartOfCurrentWord) == previousWordPosition(nextWordPosition(position));
+    if (movingForwardMovedPositionToEndOfCurrentWord)
+        positionAtStartOfCurrentWord = positionBeforeSpacingAndPreceedingWord;
+
+    return positionAtStartOfCurrentWord;
+}
+
+static VisibleSelection wordAtPositionForAtkBoundary(const AccessibilityObject* coreObject, const VisiblePosition& position, AtkTextBoundary boundaryType)
+{
+    VisiblePosition startPosition;
+    VisiblePosition endPosition;
+
+    switch (boundaryType) {
+    case ATK_TEXT_BOUNDARY_WORD_START:
+        // isStartOfWord() returns true both when at the beginning of a "real" word
+        // as when at the beginning of a whitespace range between two "real" words,
+        // since that whitespace is considered a "word" as well. And in case we are
+        // already at the beginning of a "real" word we do not need to look backwards.
+        if (isStartOfWord(position) && isWhitespace(position.characterBefore()))
+            startPosition = position;
+        else
+            startPosition = previousWordPosition(position);
+        endPosition = nextWordStartPosition(startPosition);
+
+        // We need to make sure that we look for the word in the current line when
+        // we are at the beginning of a new line, and not look into the previous one
+        // at all, which might happen when lines belong to different nodes.
+        if (isStartOfLine(position) && isStartOfLine(endPosition)) {
+            startPosition = endPosition;
+            endPosition = nextWordStartPosition(startPosition);
+        }
+        break;
+
+    case ATK_TEXT_BOUNDARY_WORD_END:
+        startPosition = previousWordEndPosition(position);
+        endPosition = nextWordPosition(startPosition);
+        break;
+
+    default:
+        ASSERT_NOT_REACHED();
+    }
+
+    VisibleSelection selectedWord(startPosition, endPosition);
+
+    // We mark the selection as 'upstream' so we can use that information later,
+    // when finding the actual offsets in getSelectionOffsetsForObject().
+    if (boundaryType == ATK_TEXT_BOUNDARY_WORD_END)
+        selectedWord.setAffinity(UPSTREAM);
+
+    return selectedWord;
+}
+
+static int numberOfReplacedElementsBeforeOffset(AtkText* text, unsigned offset)
+{
+    GOwnPtr<char> textForObject(webkitAccessibleTextGetText(text, 0, offset));
+    String textBeforeOffset = String::fromUTF8(textForObject.get());
+
+    int count = 0;
+    size_t index = textBeforeOffset.find(objectReplacementCharacter, 0);
+    while (index < offset && index != WTF::notFound) {
+        index = textBeforeOffset.find(objectReplacementCharacter, index + 1);
+        count++;
+    }
+    return count;
+}
+
+static char* webkitAccessibleTextGetWordForBoundary(AtkText* text, int offset, AtkTextBoundary boundaryType, GetTextRelativePosition textPosition, int* startOffset, int* endOffset)
+{
+    AccessibilityObject* coreObject = core(text);
+    Document* document = coreObject->document();
+    if (!document)
+        return emptyTextSelectionAtOffset(0, startOffset, endOffset);
+
+    Node* node = getNodeForAccessibilityObject(coreObject);
+    if (!node)
+        return emptyTextSelectionAtOffset(0, startOffset, endOffset);
+
+    int actualOffset = atkOffsetToWebCoreOffset(text, offset);
+
+    // Besides of the usual conversion from ATK offsets to WebCore offsets,
+    // we need to consider the potential embedded objects that might have been
+    // inserted in the text exposed through AtkText when calculating the offset.
+    actualOffset -= numberOfReplacedElementsBeforeOffset(text, actualOffset);
+
+    VisiblePosition caretPosition = coreObject->visiblePositionForIndex(actualOffset);
+    VisibleSelection currentWord = wordAtPositionForAtkBoundary(coreObject, caretPosition, boundaryType);
+
+    // Take into account other relative positions, if needed, by
+    // calculating the new position that we would need to consider.
+    VisiblePosition newPosition = caretPosition;
+    switch (textPosition) {
+    case GetTextPositionAt:
+        break;
+
+    case GetTextPositionBefore:
+        // Early return if asking for the previous word while already at the beginning.
+        if (isFirstVisiblePositionInNode(currentWord.visibleStart(), node))
+            return emptyTextSelectionAtOffset(0, startOffset, endOffset);
+
+        if (isStartOfLine(currentWord.end()))
+            newPosition = currentWord.visibleStart().previous();
+        else
+            newPosition = startOfWord(currentWord.start(), LeftWordIfOnBoundary);
+        break;
+
+    case GetTextPositionAfter:
+        // Early return if asking for the following word while already at the end.
+        if (isLastVisiblePositionInNode(currentWord.visibleEnd(), node))
+            return emptyTextSelectionAtOffset(accessibilityObjectLength(coreObject), startOffset, endOffset);
+
+        if (isEndOfLine(currentWord.end()))
+            newPosition = currentWord.visibleEnd().next();
+        else
+            newPosition = endOfWord(currentWord.end(), RightWordIfOnBoundary);
+        break;
+
+    default:
+        ASSERT_NOT_REACHED();
+    }
+
+    // Determine the relevant word we are actually interested in
+    // and calculate the ATK offsets for it, then return everything.
+    VisibleSelection selectedWord = newPosition != caretPosition ? wordAtPositionForAtkBoundary(coreObject, newPosition, boundaryType) : currentWord;
+    getSelectionOffsetsForObject(coreObject, selectedWord, *startOffset, *endOffset);
+    return webkitAccessibleTextGetText(text, *startOffset, *endOffset);
+}
+
 static gchar* webkitAccessibleTextGetTextForOffset(AtkText* text, gint offset, AtkTextBoundary boundaryType, GetTextRelativePosition textPosition, gint* startOffset, gint* endOffset)
 {
-    // Make sure we always return valid valid values for offsets.
-    *startOffset = 0;
-    *endOffset = 0;
+    AccessibilityObject* coreObject = core(text);
+    if (!coreObject || !coreObject->isAccessibilityRenderObject())
+        return emptyTextSelectionAtOffset(0, startOffset, endOffset);
 
     if (boundaryType == ATK_TEXT_BOUNDARY_CHAR)
         return webkitAccessibleTextGetChar(text, offset, textPosition, startOffset, endOffset);
 
+    if (boundaryType == ATK_TEXT_BOUNDARY_WORD_START || boundaryType == ATK_TEXT_BOUNDARY_WORD_END)
+        return webkitAccessibleTextGetWordForBoundary(text, offset, boundaryType, textPosition, startOffset, endOffset);
+
 #if PLATFORM(GTK)
-    // FIXME: Get rid of the code below once every single get_text_*_offset
-    // function has been properly implemented without using Pango/Cairo.
+    // FIXME: Get rid of the code below once every single part above
+    // has been properly implemented without using Pango/Cairo.
     GailOffsetType offsetType = GAIL_AT_OFFSET;
     switch (textPosition) {
     case GetTextPositionBefore:
@@ -669,6 +864,10 @@
         ASSERT_NOT_REACHED();
     }
 
+    // Make sure we always return valid valid values for offsets.
+    *startOffset = 0;
+    *endOffset = 0;
+
     return gail_text_util_get_text(getGailTextUtilForAtk(text), getPangoLayoutForAtk(text), offsetType, boundaryType, offset, startOffset, endOffset);
 #endif
 
@@ -803,9 +1002,6 @@
 
 static gchar* webkitAccessibleTextGetSelection(AtkText* text, gint selectionNum, gint* startOffset, gint* endOffset)
 {
-    // Default values, unless the contrary is proved
-    *startOffset = *endOffset = 0;
-
     // WebCore does not support multiple selection, so anything but 0 does not make sense for now.
     if (selectionNum)
         return 0;

Modified: trunk/Source/WebKit/gtk/ChangeLog (153651 => 153652)


--- trunk/Source/WebKit/gtk/ChangeLog	2013-08-02 15:52:39 UTC (rev 153651)
+++ trunk/Source/WebKit/gtk/ChangeLog	2013-08-02 16:02:11 UTC (rev 153652)
@@ -1,3 +1,17 @@
+2013-08-02  Mario Sanchez Prada  <mario.pr...@samsung.com>
+
+        Implement atk_text_get_text_*_offset for WORD
+        https://bugs.webkit.org/show_bug.cgi?id=114871
+
+        Reviewed by Martin Robinson.
+
+        Updated current unit tests and add a new one specific for embedded
+        objects, to ensure we are covering even more cases than before.
+
+        * tests/testatk.c:
+        (testWebkitAtkGetTextAtOffsetWithEmbeddedObjects): New.
+        (main): Added new test to the test suite.
+
 2013-07-30  Carlos Garcia Campos  <cgar...@igalia.com>
 
         Unreviewed. Update NEWS and Versions.m4 for 2.1.4 release.

Modified: trunk/Source/WebKit/gtk/tests/testatk.c (153651 => 153652)


--- trunk/Source/WebKit/gtk/tests/testatk.c	2013-08-02 15:52:39 UTC (rev 153651)
+++ trunk/Source/WebKit/gtk/tests/testatk.c	2013-08-02 16:02:11 UTC (rev 153652)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2009 Igalia S.L.
+ * Copyright (C) 2013 Samsung Electronics. All rights reserved.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -52,6 +53,8 @@
 
 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>";
 
+static const char* contentsWithEmbeddedObjects = "<html><body>This is one line containing two <img> embedded objects <img> in the middle.</body></html>";
+
 static const char* comboBoxSelector = "<html><body><select><option selected value='foo'>foo</option><option value='bar'>bar</option></select></body></html>";
 
 static const char* embeddedObjects = "<html><body><p>Choose: <input value='foo' type='checkbox'/>foo <input value='bar' type='checkbox'/>bar (pick one)</p><p>Choose: <select name='foo'><option>bar</option><option>baz</option></select> (pick one)</p><p><input name='foobarbutton' value='foobar' type='button'/></p></body></html>";
@@ -1054,6 +1057,75 @@
     g_object_unref(webView);
 }
 
+static void testWebkitAtkGetTextAtOffsetWithEmbeddedObjects()
+{
+    WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
+    g_object_ref_sink(webView);
+    GtkAllocation allocation = { 0, 0, 800, 600 };
+    gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
+    webkit_web_view_load_string(webView, contentsWithEmbeddedObjects, 0, 0, 0);
+
+    /* Enable caret browsing. */
+    WebKitWebSettings* settings = webkit_web_view_get_settings(webView);
+    g_object_set(settings, "enable-caret-browsing", TRUE, NULL);
+    webkit_web_view_set_settings(webView, settings);
+
+    /* Get to the inner AtkText object. */
+    AtkObject* object = getWebAreaObject(webView);
+    g_assert(object);
+
+    /* Check the paragraph with the text wrapped because of max-width. */
+    AtkText* paragraph = ATK_TEXT(atk_object_ref_accessible_child(object, 0));
+    g_assert(ATK_IS_TEXT(paragraph));
+
+    gchar* text = atk_text_get_text(paragraph, 0, -1);
+    g_assert_cmpstr(text, ==, "This is one line containing two \357\277\274 embedded objects \357\277\274 in the middle.");
+    g_free(text);
+
+    /* Check right before the first embedded object */
+    testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_CHAR, 32, "\357\277\274", 32, 33);
+    testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START, 32, "two \357\277\274 ", 28, 34);
+    testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END, 32, " \357\277\274 embedded", 31, 42);
+
+    /* Check right after the first embedded object (and before the first word after it) */
+    testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_CHAR, 33, " ", 33, 34);
+    testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START, 33, "two \357\277\274 ", 28, 34);
+    testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END, 33, " \357\277\274 embedded", 31, 42);
+
+    /* Check at the beginning of the first word between the two embedded objects */
+    testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_CHAR, 34, "e", 34, 35);
+    testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START, 34, "embedded ", 34, 43);
+    testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END, 34, " \357\277\274 embedded", 31, 42);
+
+    /* Check at the end of the first word between the two embedded objects */
+    testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_CHAR, 42, " ", 42, 43);
+    testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START, 42, "embedded ", 34, 43);
+    testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END, 42, " objects", 42, 50);
+
+    /* Check right before the second embedded object */
+    testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_CHAR, 51, "\357\277\274", 51, 52);
+    testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START, 51, "objects \357\277\274 ", 43, 53);
+    testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END, 51, " \357\277\274 in", 50, 55);
+
+    /* Check right after the second embedded object (and before the first word after it) */
+    testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_CHAR, 52, " ", 52, 53);
+    testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START, 52, "objects \357\277\274 ", 43, 53);
+    testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END, 52, " \357\277\274 in", 50, 55);
+
+    /* Check at the beginning of the first word after the two embedded objects */
+    testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_CHAR, 53, "i", 53, 54);
+    testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START, 53, "in ", 53, 56);
+    testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END, 53, " \357\277\274 in", 50, 55);
+
+    /* Check at the end of the first word after the two embedded objects */
+    testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_CHAR, 55, " ", 55, 56);
+    testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START, 55, "in ", 53, 56);
+    testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END, 55, " the", 55, 59);
+
+    g_object_unref(paragraph);
+    g_object_unref(webView);
+}
+
 static void testWebkitAtkGetTextInParagraphAndBodySimple()
 {
     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
@@ -2125,6 +2197,7 @@
     g_test_add_func("/webkit/atk/getTextAtOffsetWithPreformattedText", testWebkitAtkGetTextAtOffsetWithPreformattedText);
     g_test_add_func("/webkit/atk/getTextAtOffsetWithSpecialCharacters", testWebkitAtkGetTextAtOffsetWithSpecialCharacters);
     g_test_add_func("/webkit/atk/getTextAtOffsetWithWrappedLines", testWebkitAtkGetTextAtOffsetWithWrappedLines);
+    g_test_add_func("/webkit/atk/getTextAtOffsetWithEmbeddedObjects", testWebkitAtkGetTextAtOffsetWithEmbeddedObjects);
     g_test_add_func("/webkit/atk/getTextInParagraphAndBodySimple", testWebkitAtkGetTextInParagraphAndBodySimple);
     g_test_add_func("/webkit/atk/getTextInParagraphAndBodyModerate", testWebkitAtkGetTextInParagraphAndBodyModerate);
     g_test_add_func("/webkit/atk/getTextInTable", testWebkitAtkGetTextInTable);
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to