2009-05-12 Xan Lopez <xlopez@igalia.com>
authorxan@webkit.org <xan@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 28 May 2009 18:20:01 +0000 (18:20 +0000)
committerxan@webkit.org <xan@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 28 May 2009 18:20:01 +0000 (18:20 +0000)
            Reviewed by Jan Alonzo and Gustavo Noronha.

            https://bugs.webkit.org/show_bug.cgi?id=25415
            [GTK][ATK] Please implement support for get_text_at_offset

            Add new test file for ATK.

            * GNUmakefile.am:

    WebCore:

    2009-05-12  Xan Lopez  <xlopez@igalia.com>

            Reviewed by Jan Alonzo and Gustavo Noronha.

            https://bugs.webkit.org/show_bug.cgi?id=25415
            [GTK][ATK] Please implement support for get_text_at_offset

            Implement atk_text_get_text_{at,after,before}_offset.

            * accessibility/gtk/AccessibilityObjectWrapperAtk.cpp:

    WebKit/gtk:

    2009-05-12  Xan Lopez  <xlopez@igalia.com>

            Reviewed by Jan Alonzo and Gustavo Noronha.

            https://bugs.webkit.org/show_bug.cgi?id=25415
            [GTK][ATK] Please implement support for get_text_at_offset

            New test file for ATK functionality.

            * tests/testatk.c: Added.
            (bail_out):
            (test_get_text_function):
            (test_webkit_atk_get_text_at_offset):
            (main):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@44246 268f45cc-cd09-0410-ab3c-d52691b4dbfc

ChangeLog
GNUmakefile.am
WebCore/ChangeLog
WebCore/accessibility/gtk/AccessibilityObjectWrapperAtk.cpp
WebKit/gtk/ChangeLog
WebKit/gtk/tests/testatk.c [new file with mode: 0644]

index 67a4a99e081b3e7901fe0794fa849610345e82c1..f5a1324138d58c2d1e504e360b33aafe420b56e6 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2009-05-19  Xan Lopez  <xlopez@igalia.com>
+
+        Reviewed by Jan Alonzo and Gustavo Noronha.
+
+        https://bugs.webkit.org/show_bug.cgi?id=25415
+        [GTK][ATK] Please implement support for get_text_at_offset
+
+        Add new test file for ATK.
+
+        * GNUmakefile.am:
+
 2009-05-28  Gustavo Noronha Silva  <gustavo.noronha@collabora.co.uk>
 
         Rubber-stamped by Xan Lopez.
index d766a9e9cd47305488aa65a45a2d156b62299313..781b37f0c25516c05aabb548b11483ccecf7c50f 100644 (file)
@@ -526,7 +526,8 @@ webkit_tests_ldflags = \
 TEST_PROGS += Programs/unittests/testwebframe \
        Programs/unittests/testwebbackforwardlist \
        Programs/unittests/testwebhistoryitem \
-       Programs/unittests/testdownload
+       Programs/unittests/testdownload \
+       Programs/unittests/testatk
 
 # Add additional tests here
 Programs_unittests_testwebframe_SOURCES = WebKit/gtk/tests/testwebframe.c
@@ -549,6 +550,11 @@ Programs_unittests_testdownload_CFLAGS = $(webkit_tests_cflags)
 Programs_unittests_testdownload_LDADD = $(webkit_tests_ldadd)
 Programs_unittests_testdownload_LDFLAGS = $(webkit_tests_ldflags)
 
+Programs_unittests_testatk_SOURCES = WebKit/gtk/tests/testatk.c
+Programs_unittests_testatk_CFLAGS = $(webkit_tests_cflags)
+Programs_unittests_testatk_LDADD = $(webkit_tests_ldadd)
+Programs_unittests_testatk_LDFLAGS = $(webkit_tests_ldflags)
+
 # Autogenerated sources
 BUILT_SOURCES := \
        $(javascriptcore_built_sources) \
index 5b02db61637ff837506ab6a1dffc8f0cabb0d5b3..65ad3c9cac2f46a25e54ebd5dfb03a18a036f94c 100644 (file)
@@ -1,3 +1,14 @@
+2009-05-19  Xan Lopez  <xlopez@igalia.com>
+
+        Reviewed by Jan Alonzo and Gustavo Noronha.
+
+        https://bugs.webkit.org/show_bug.cgi?id=25415
+        [GTK][ATK] Please implement support for get_text_at_offset
+
+        Implement atk_text_get_text_{at,after,before}_offset.
+
+        * accessibility/gtk/AccessibilityObjectWrapperAtk.cpp:
+
 2009-05-28  Nikolas Zimmermann  <nikolas.zimmermann@torchmobile.com>
 
         Rubber-stamped by Darin Adler.
index eae21180d2051c1d81f272e65b47b0e0ecfce466..141d561347511c3b30d718217a9e1d6cc8721dc9 100644 (file)
@@ -48,6 +48,7 @@
 #include <atk/atk.h>
 #include <glib.h>
 #include <glib/gprintf.h>
+#include <pango/pango.h>
 
 using namespace WebCore;
 
@@ -481,25 +482,228 @@ static gchar* webkit_accessible_text_get_text(AtkText* text, gint startOffset, g
     return g_strdup(ret.utf8().data());
 }
 
-static gchar* webkit_accessible_text_get_text_after_offset(AtkText* text, gint offset, AtkTextBoundary boundary_type, gint* start_offset, gint* end_offset)
+enum GetTextFunctionType {
+    AfterOffset,
+    AtOffset,
+    BeforeOffset
+};
+
+typedef bool (*isCharacterAttribute) (PangoLogAttr* attr);
+
+static inline bool isWordStart(PangoLogAttr* attr)
 {
-    notImplemented();
-    return NULL;
+    return attr->is_word_start;
 }
 
-static gchar* webkit_accessible_text_get_text_at_offset(AtkText* text, gint offset, AtkTextBoundary boundary_type, gint* start_offset, gint* end_offset)
+static inline bool isWordEnd(PangoLogAttr* attr)
 {
-    notImplemented();
-    return NULL;
+    return attr->is_word_end;
 }
 
-static gunichar webkit_accessible_text_get_character_at_offset(AtkText* text, gint offset)
+static inline bool isSentenceStart(PangoLogAttr* attr)
 {
-    notImplemented();
-    return 0;
+    return attr->is_sentence_start;
+}
+
+static inline bool isSentenceEnd(PangoLogAttr* attr)
+{
+    return attr->is_sentence_end;
+}
+
+enum Direction {
+    DirectionForward,
+    DirectionBackwards
+};
+
+static bool findCharacterAttribute(isCharacterAttribute predicateFunction, PangoLogAttr* attributes, Direction direction, int startOffset, int attrsLength, int* resultOffset)
+{
+    int advanceBy = direction == DirectionForward ? 1 : -1;
+
+    *resultOffset = -1;
+
+    for (int i = startOffset; i >= 0 && i < attrsLength; i += advanceBy) {
+        if (predicateFunction(attributes + i)) {
+            *resultOffset = i;
+            return true;
+        }
+    }
+
+    return false;
+}
+
+static bool findCharacterAttributeSkip(isCharacterAttribute predicateFunction, unsigned skip, PangoLogAttr* attributes, Direction direction, int startOffset, int attrsLength, int* resultOffset)
+{
+    int tmpOffset;
+
+    bool retValue = findCharacterAttribute(predicateFunction, attributes, direction, startOffset, attrsLength, &tmpOffset);
+    if (skip == 0) {
+        *resultOffset = tmpOffset;
+        return retValue;
+    }
+
+    if (direction == DirectionForward)
+        tmpOffset++;
+    else
+        tmpOffset--;
+
+    return findCharacterAttributeSkip(predicateFunction, skip - 1, attributes, direction, tmpOffset, attrsLength, resultOffset);
+}
+
+static isCharacterAttribute oppositePredicate(isCharacterAttribute predicate)
+{
+    if (predicate == isWordStart)
+        return isWordEnd;
+    if (predicate == isWordEnd)
+        return isWordStart;
+    if (predicate == isSentenceStart)
+        return isSentenceEnd;
+    if (predicate == isSentenceEnd)
+        return isSentenceStart;
+
+    g_assert_not_reached();
 }
 
-static gchar* webkit_accessible_text_get_text_before_offset(AtkText* text, gint offset, AtkTextBoundary boundary_type, gint* start_offset, gint* end_offset)
+static gchar* getTextHelper(GetTextFunctionType getTextFunctionType, AtkText* textObject, gint offset, AtkTextBoundary boundaryType, gint* startOffset, gint* endOffset)
+{
+    AccessibilityObject* coreObject = core(textObject);
+    String text;
+
+    *startOffset = *endOffset = -1;
+
+    if (coreObject->isTextControl())
+        text = coreObject->text();
+    else
+        text = coreObject->textUnderElement();
+
+    char* cText = g_strdup(text.utf8().data());
+    glong textLength = g_utf8_strlen(cText, -1);
+
+    if (boundaryType == ATK_TEXT_BOUNDARY_CHAR) {
+        int effectiveOffset;
+
+        switch (getTextFunctionType) {
+        case AfterOffset:
+            effectiveOffset = offset + 1;
+            break;
+        case BeforeOffset:
+            effectiveOffset = offset - 1;
+            break;
+        case AtOffset:
+            effectiveOffset = offset;
+            break;
+        default:
+            g_assert_not_reached();
+        }
+
+        *startOffset = effectiveOffset;
+        *endOffset = effectiveOffset + 1;
+    } else {
+        PangoLogAttr* attrs = g_new(PangoLogAttr, textLength + 1);
+        PangoLanguage* language = pango_language_get_default();
+        pango_get_log_attrs(cText, -1, -1, language, attrs, textLength + 1);
+      
+        isCharacterAttribute predicate;
+
+        if (boundaryType == ATK_TEXT_BOUNDARY_WORD_START)
+            predicate = isWordStart;
+        else if (boundaryType == ATK_TEXT_BOUNDARY_WORD_END)
+            predicate = isWordEnd;
+        else if (boundaryType == ATK_TEXT_BOUNDARY_SENTENCE_START)
+            predicate = isSentenceStart;
+        else if (boundaryType == ATK_TEXT_BOUNDARY_SENTENCE_END)
+            predicate = isSentenceEnd;
+        else
+            // FIXME: bail out for now, since we are missing the LINE
+            // boundary implementations
+            goto out;
+
+        switch (boundaryType) {
+        case ATK_TEXT_BOUNDARY_WORD_START:
+        case ATK_TEXT_BOUNDARY_SENTENCE_START:
+            if (getTextFunctionType == AfterOffset) {
+                // Take the item after the current one in any case
+                findCharacterAttribute(predicate, attrs, DirectionForward, offset + 1, textLength + 1, startOffset);
+                findCharacterAttributeSkip(predicate, 1, attrs, DirectionForward, offset + 1, textLength + 1, endOffset);
+            } else if (getTextFunctionType == AtOffset) {
+                // Take the item at point if the offset is in an item or
+                // the item before otherwise
+                findCharacterAttribute(predicate, attrs, DirectionBackwards, offset, textLength + 1, startOffset);
+                if (!findCharacterAttribute(predicate, attrs, DirectionForward, offset + 1, textLength + 1, endOffset)) {
+                    findCharacterAttribute(oppositePredicate(predicate), attrs, DirectionForward, offset + 1, textLength + 1, endOffset);
+                    // We want to include the actual end boundary
+                    // here, since *_START would have done so. Advance
+                    // until the end of the string if possible
+                    if (*endOffset != -1 && *endOffset < textLength)
+                        *endOffset = textLength;
+                }
+            } else {
+                // Take the item before the point if the offset is in an
+                // item, or the the item before that one otherwise
+                findCharacterAttributeSkip(predicate, 1, attrs, DirectionBackwards, offset, textLength + 1, startOffset);
+                findCharacterAttribute(predicate, attrs, DirectionBackwards, offset, textLength + 1, endOffset);
+            }
+            break;
+        case ATK_TEXT_BOUNDARY_WORD_END:
+        case ATK_TEXT_BOUNDARY_SENTENCE_END:
+            if (getTextFunctionType == AfterOffset) {
+                // Take the item after the current item if the offset is
+                // in a item, or the item after that otherwise
+                findCharacterAttribute(predicate, attrs, DirectionForward, offset, textLength + 1, startOffset);
+                findCharacterAttributeSkip(predicate, 1, attrs, DirectionForward, offset, textLength + 1, endOffset);
+            } else if (getTextFunctionType == AtOffset) {
+                // Take the item at point if the offset is in a item or
+                // the item after otherwise
+                if (!findCharacterAttribute(predicate, attrs, DirectionBackwards, offset, textLength + 1, startOffset))
+                    // No match before offset, take the first opposite match at or before the offset
+                    findCharacterAttribute(oppositePredicate(predicate), attrs, DirectionBackwards, offset, textLength + 1, startOffset);
+                findCharacterAttribute(predicate, attrs, DirectionForward, offset + 1, textLength + 1, endOffset);
+            } else {
+                // Take the item before the point in any case
+                if (!findCharacterAttributeSkip(predicate, 1, attrs, DirectionBackwards, offset, textLength + 1, startOffset)) {
+                    int tmpOffset;
+                    // No match before offset, take the first opposite match at or before the offset
+                    findCharacterAttribute(predicate, attrs, DirectionBackwards, offset, textLength + 1, &tmpOffset);
+                    findCharacterAttribute(oppositePredicate(predicate), attrs, DirectionBackwards, tmpOffset - 1, textLength + 1, startOffset);
+                }
+                findCharacterAttribute(predicate, attrs, DirectionBackwards, offset, textLength + 1, endOffset);
+            }
+            break;
+        default:
+            g_assert_not_reached();
+        }
+
+        g_free(attrs);
+    }
+
+ out:
+    if (*startOffset < 0 || *endOffset < 0) {
+        *startOffset = *endOffset = 0;
+        return g_strdup("");
+    }
+
+    char* start = g_utf8_offset_to_pointer(cText, (glong)*startOffset);
+    char* end = g_utf8_offset_to_pointer(cText, (glong)*endOffset);
+    char* resultText = g_strndup(start, end - start);
+    g_free(cText);
+    return resultText;
+}
+
+static gchar* webkit_accessible_text_get_text_after_offset(AtkText* text, gint offset, AtkTextBoundary boundaryType, gint* startOffset, gint* endOffset)
+{
+    return getTextHelper(AfterOffset, text, offset, boundaryType, startOffset, endOffset);
+}
+
+static gchar* webkit_accessible_text_get_text_at_offset(AtkText* text, gint offset, AtkTextBoundary boundaryType, gint* startOffset, gint* endOffset)
+{
+    return getTextHelper(AtOffset, text, offset, boundaryType, startOffset, endOffset);
+}
+
+static gchar* webkit_accessible_text_get_text_before_offset(AtkText* text, gint offset, AtkTextBoundary boundaryType, gint* startOffset, gint* endOffset)
+{
+    return getTextHelper(BeforeOffset, text, offset, boundaryType, startOffset, endOffset);
+}
+
+static gunichar webkit_accessible_text_get_character_at_offset(AtkText* text, gint offset)
 {
     notImplemented();
     return NULL;
index d5769aca8366170528b1d9475b5ad69d91f3c39a..0ecca477305ec56d235d3f1ed520557e2b251f40 100644 (file)
@@ -1,3 +1,18 @@
+2009-05-19  Xan Lopez  <xlopez@igalia.com>
+
+        Reviewed by Jan Alonzo and Gustavo Noronha.
+
+        https://bugs.webkit.org/show_bug.cgi?id=25415
+        [GTK][ATK] Please implement support for get_text_at_offset
+
+        New test file for ATK functionality.
+
+        * tests/testatk.c: Added.
+        (bail_out):
+        (test_get_text_function):
+        (test_webkit_atk_get_text_at_offset):
+        (main):
+
 2009-05-28  Gustavo Noronha Silva  <gustavo.noronha@collabora.co.uk>
 
         Reviewed by Xan Lopez.
diff --git a/WebKit/gtk/tests/testatk.c b/WebKit/gtk/tests/testatk.c
new file mode 100644 (file)
index 0000000..166a8e2
--- /dev/null
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2009 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <errno.h>
+#include <unistd.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <gtk/gtk.h>
+#include <webkit/webkit.h>
+
+#if GLIB_CHECK_VERSION(2, 16, 0) && GTK_CHECK_VERSION(2, 14, 0)
+
+static const char* contents = "<html><body><p>This is a test. This is the second sentence. And this the third.</p></body></html>";
+
+static gboolean bail_out(GMainLoop* loop)
+{
+    if (g_main_loop_is_running(loop))
+        g_main_loop_quit(loop);
+
+    return FALSE;
+}
+
+typedef gchar* (*AtkGetTextFunction) (AtkText*, gint, AtkTextBoundary, gint*, gint*);
+
+static void test_get_text_function(AtkText* text_obj, AtkGetTextFunction fn, AtkTextBoundary boundary, gint offset, const char* text_result, gint start_offset_result, gint end_offset_result)
+{
+    gint start_offset, end_offset;
+    char* text;
+
+    text = fn(text_obj, offset, boundary, &start_offset, &end_offset);
+    g_assert_cmpstr(text, ==, text_result);
+    g_assert_cmpint(start_offset, ==, start_offset_result);
+    g_assert_cmpint(end_offset, ==, end_offset_result);
+    g_free(text);
+}
+
+static void test_webkit_atk_get_text_at_offset(void)
+{
+    WebKitWebView* webView;
+    AtkObject *obj;
+    GMainLoop* loop;
+    AtkText* text_obj;
+    char* text;
+
+    webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
+    g_object_ref_sink(webView);
+    webkit_web_view_load_string(webView, contents, NULL, NULL, NULL);
+    loop = g_main_loop_new(NULL, TRUE);
+
+    g_timeout_add(100, (GSourceFunc)bail_out, loop);
+    g_main_loop_run(loop);
+
+    /* Get to the inner AtkText object */
+    obj = gtk_widget_get_accessible(GTK_WIDGET(webView));
+    g_assert(obj);
+    obj = atk_object_ref_accessible_child(obj, 0);
+    g_assert(obj);
+    obj = atk_object_ref_accessible_child(obj, 0);
+    g_assert(obj);
+
+    text_obj = ATK_TEXT(obj);
+    g_assert(ATK_IS_TEXT(text_obj));
+
+    text = atk_text_get_text(text_obj, 0, -1);
+    g_assert_cmpstr(text, ==, "This is a test. This is the second sentence. And this the third.");
+    g_free(text);
+
+    /* ATK_TEXT_BOUNDARY_CHAR */
+    test_get_text_function(text_obj, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_CHAR,
+                           0, "T", 0, 1);
+
+    test_get_text_function(text_obj, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_CHAR,
+                           0, "h", 1, 2);
+
+    test_get_text_function(text_obj, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_CHAR,
+                           0, "", 0, 0);
+
+    test_get_text_function(text_obj, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_CHAR,
+                           1, "T", 0, 1);
+    
+    /* ATK_TEXT_BOUNDARY_WORD_START */
+    test_get_text_function(text_obj, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START,
+                           0, "This ", 0, 5);
+
+    test_get_text_function(text_obj, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START,
+                           4, "This ", 0, 5);
+
+    test_get_text_function(text_obj, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START,
+                           10, "test. ", 10, 16);
+
+    test_get_text_function(text_obj, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START,
+                           58, "third.", 58, 64);
+
+    test_get_text_function(text_obj, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_WORD_START,
+                           5, "This ", 0, 5);
+
+    test_get_text_function(text_obj, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_WORD_START,
+                           7, "This ", 0, 5);
+
+    test_get_text_function(text_obj, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_WORD_START,
+                           0, "is ", 5, 8);
+
+    test_get_text_function(text_obj, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_WORD_START,
+                           4, "is ", 5, 8);
+
+    test_get_text_function(text_obj, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_WORD_START,
+                           3, "is ", 5, 8);
+
+    /* ATK_TEXT_BOUNDARY_WORD_END */
+    test_get_text_function(text_obj, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END,
+                           0, "This", 0, 4);
+
+    test_get_text_function(text_obj, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END,
+                           4, " is", 4, 7);
+
+    test_get_text_function(text_obj, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END,
+                           5, " is", 4, 7);
+
+    test_get_text_function(text_obj, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END,
+                           9, " test", 9, 14);
+
+    test_get_text_function(text_obj, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_WORD_END,
+                           5, "This", 0, 4);
+
+    test_get_text_function(text_obj, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_WORD_END,
+                           4, "This", 0, 4);
+
+    test_get_text_function(text_obj, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_WORD_END,
+                           7, " is", 4, 7);
+
+    test_get_text_function(text_obj, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_WORD_END,
+                           5, " a", 7, 9);
+
+    test_get_text_function(text_obj, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_WORD_END,
+                           4, " is", 4, 7);
+
+    test_get_text_function(text_obj, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END,
+                           58, " third", 57, 63);
+
+    /* ATK_TEXT_BOUNDARY_SENTENCE_START */
+    test_get_text_function(text_obj, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_SENTENCE_START,
+                           0, "This is a test. ", 0, 16);
+
+    test_get_text_function(text_obj, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_SENTENCE_START,
+                           15, "This is a test. ", 0, 16);
+
+    test_get_text_function(text_obj, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_SENTENCE_START,
+                           0, "This is the second sentence. ", 16, 45);
+
+    test_get_text_function(text_obj, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_SENTENCE_START,
+                           15, "This is the second sentence. ", 16, 45);
+
+    test_get_text_function(text_obj, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_SENTENCE_START,
+                           16, "This is a test. ", 0, 16);
+
+    test_get_text_function(text_obj, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_SENTENCE_START,
+                           44, "This is a test. ", 0, 16);
+
+    test_get_text_function(text_obj, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_SENTENCE_START,
+                           15, "", 0, 0);
+
+    /* ATK_TEXT_BOUNDARY_SENTENCE_END */
+    test_get_text_function(text_obj, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_SENTENCE_END,
+                           0, "This is a test.", 0, 15);
+
+    test_get_text_function(text_obj, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_SENTENCE_END,
+                           15, " This is the second sentence.", 15, 44);
+
+    test_get_text_function(text_obj, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_SENTENCE_END,
+                           16, " This is the second sentence.", 15, 44);
+
+    test_get_text_function(text_obj, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_SENTENCE_END,
+                           17, " This is the second sentence.", 15, 44);
+
+    test_get_text_function(text_obj, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_SENTENCE_END,
+                           0, " This is the second sentence.", 15, 44);
+
+    test_get_text_function(text_obj, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_SENTENCE_END,
+                           15, " This is the second sentence.", 15, 44);
+
+    test_get_text_function(text_obj, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_SENTENCE_END,
+                           16, "This is a test.", 0, 15);
+
+    test_get_text_function(text_obj, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_SENTENCE_END,
+                           15, "This is a test.", 0, 15);
+
+    test_get_text_function(text_obj, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_SENTENCE_END,
+                           14, "", 0, 0);
+
+    test_get_text_function(text_obj, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_SENTENCE_END,
+                           44, " This is the second sentence.", 15, 44);
+
+    /* ATK_TEXT_BOUNDARY_LINE_START */
+    /* TODO */
+    test_get_text_function(text_obj, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_START,
+                           0, "", 0, 0);
+
+    /* ATK_TEXT_BOUNDARY_LINE_END */
+    /* TODO */
+    test_get_text_function(text_obj, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_END,
+                           0, "", 0, 0);
+
+    g_object_unref(webView);
+}
+
+int main(int argc, char** argv)
+{
+    g_thread_init(NULL);
+    gtk_test_init(&argc, &argv, NULL);
+
+    g_test_bug_base("https://bugs.webkit.org/");
+    g_test_add_func("/webkit/atk/get_text_at_offset", test_webkit_atk_get_text_at_offset);
+    return g_test_run ();
+}
+
+#else
+int main(int argc, char** argv)
+{
+    g_critical("You will need at least glib-2.16.0 and gtk-2.14.0 to run the unit tests. Doing nothing now.");
+    return 0;
+}
+
+#endif