Home | History | Annotate | Download | only in tests
      1 /*
      2  * Copyright (C) 2009 Igalia S.L.
      3  *
      4  * This library is free software; you can redistribute it and/or
      5  * modify it under the terms of the GNU Library General Public
      6  * License as published by the Free Software Foundation; either
      7  * version 2 of the License, or (at your option) any later version.
      8  *
      9  * This library is distributed in the hope that it will be useful,
     10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12  * Library General Public License for more details.
     13  *
     14  * You should have received a copy of the GNU Library General Public License
     15  * along with this library; see the file COPYING.LIB.  If not, write to
     16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     17  * Boston, MA 02110-1301, USA.
     18  */
     19 
     20 #include <errno.h>
     21 #include <glib.h>
     22 #include <glib/gstdio.h>
     23 #include <gtk/gtk.h>
     24 #include <unistd.h>
     25 #include <webkit/webkit.h>
     26 
     27 #if GTK_CHECK_VERSION(2, 14, 0)
     28 
     29 static const char* centeredContents = "<html><body><p style='text-align: center;'>Short line</p><p style='text-align: center;'>Long-size line with some foo bar baz content</p><p style='text-align: center;'>Short line</p><p style='text-align: center;'>This is a multi-line paragraph<br />where the first line<br />is the biggest one</p></body></html>";
     30 
     31 static const char* contents = "<html><body><p>This is a test. This is the second sentence. And this the third.</p></body></html>";
     32 
     33 static const char* contentsWithNewlines = "<html><body><p>This is a test. \n\nThis\n is the second sentence. And this the third.</p></body></html>";
     34 
     35 static const char* contentsWithSpecialChars = "<html><body><p>&laquo;&nbsp;This is a paragraph with &ldquo;special&rdquo; characters inside.&nbsp;&raquo;</p></body></html>";
     36 
     37 static const char* contentsInTextarea = "<html><body><textarea cols='80'>This is a test. This is the second sentence. And this the third.</textarea></body></html>";
     38 
     39 static const char* contentsInTextInput = "<html><body><input type='text' size='80' value='This is a test. This is the second sentence. And this the third.'/></body></html>";
     40 
     41 static const char* contentsInParagraphAndBodySimple = "<html><body><p>This is a test.</p>Hello world.</body></html>";
     42 
     43 static const char* contentsInParagraphAndBodyModerate = "<html><body><p>This is a test.</p>Hello world.<br /><font color='#00cc00'>This sentence is green.</font><br />This one is not.</body></html>";
     44 
     45 static const char* contentsInTable = "<html><body><table><tr><td>foo</td><td>bar</td></tr></table></body></html>";
     46 
     47 static const char* contentsInTableWithHeaders = "<html><body><table><tr><th>foo</th><th>bar</th><th colspan='2'>baz</th></tr><tr><th>qux</th><td>1</td><td>2</td><td>3</td></tr><tr><th rowspan='2'>quux</th><td>4</td><td>5</td><td>6</td></tr><tr><td>6</td><td>7</td><td>8</td></tr><tr><th>corge</th><td>9</td><td>10</td><td>11</td></tr></table><table><tr><td>1</td><td>2</td></tr><tr><td>3</td><td>4</td></tr></table></body></html>";
     48 
     49 static const char* contentsWithExtraneousWhiteSpaces = "<html><head><body><p>This\n                          paragraph\n                                                      is\n                                                                                                                                                                                                                                                                                                                                                                            borked!</p></body></html>";
     50 
     51 static const char* comboBoxSelector = "<html><body><select><option selected value='foo'>foo</option><option value='bar'>bar</option></select></body></html>";
     52 
     53 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>";
     54 
     55 static const char* formWithTextInputs = "<html><body><form><input type='text' name='entry' /></form></body></html>";
     56 
     57 static const char* hypertextAndHyperlinks = "<html><body><p>A paragraph with no links at all</p><p><a href='http://foo.bar.baz/'>A line</a> with <a href='http://bar.baz.foo/'>a link in the middle</a> as well as at the beginning and <a href='http://baz.foo.bar/'>at the end</a></p><ol><li>List item with a <span><a href='http://foo.bar.baz/'>link inside a span node</a></span></li></ol></body></html>";
     58 
     59 static const char* layoutAndDataTables = "<html><body><table><tr><th>Odd</th><th>Even</th></tr><tr><td>1</td><td>2</td></tr></table><table><tr><td>foo</td><td>bar</td></tr></table></body></html>";
     60 
     61 static const char* linksWithInlineImages = "<html><head><style>a.http:before {content: url(no-image.png);}</style><body><p><a class='http' href='foo'>foo</a> bar baz</p><p>foo <a class='http' href='bar'>bar</a> baz</p><p>foo bar <a class='http' href='baz'>baz</a></p></body></html>";
     62 
     63 static const char* listsOfItems = "<html><body><ul><li>text only</li><li><a href='foo'>link only</a></li><li>text and a <a href='bar'>link</a></li></ul><ol><li>text only</li><li><a href='foo'>link only</a></li><li>text and a <a href='bar'>link</a></li></ol></body></html>";
     64 
     65 static const char* textForCaretBrowsing = "<html><body><h1>A text header</h1><p>A paragraph <a href='http://foo.bar.baz/'>with a link</a> in the middle</p><ol><li>A list item</li></ol><select><option selected value='foo'>An option in a combo box</option></select></body></html>";
     66 
     67 static const char* textForSelections = "<html><body><p>A paragraph with plain text</p><p>A paragraph with <a href='http://webkit.org'>a link</a> in the middle</p><ol><li>A list item</li></ol><select></body></html>";
     68 
     69 static const char* textWithAttributes = "<html><head><style>.st1 {font-family: monospace; color:rgb(120,121,122);} .st2 {text-decoration:underline; background-color:rgb(80,81,82);}</style></head><body><p style=\"font-size:14; text-align:right;\">This is the <i>first</i><b> sentence of this text.</b></p><p class=\"st1\">This sentence should have an style applied <span class=\"st2\">and this part should have another one</span>.</p><p>x<sub>1</sub><sup>2</sup>=x<sub>2</sub><sup>3</sup></p><p style=\"text-align:center;\">This sentence is the <strike>last</strike> one.</p></body></html>";
     70 
     71 static void waitForAccessibleObjects()
     72 {
     73     /* Manually spin the main context to make sure the accessible
     74        objects are properly created before continuing. */
     75     while (g_main_context_pending(0))
     76         g_main_context_iteration(0, TRUE);
     77 }
     78 
     79 typedef gchar* (*AtkGetTextFunction) (AtkText*, gint, AtkTextBoundary, gint*, gint*);
     80 
     81 static void testGetTextFunction(AtkText* textObject, AtkGetTextFunction fn, AtkTextBoundary boundary, gint offset, const char* textResult, gint startOffsetResult, gint endOffsetResult)
     82 {
     83     gint startOffset;
     84     gint endOffset;
     85     char* text = fn(textObject, offset, boundary, &startOffset, &endOffset);
     86     g_assert_cmpstr(text, ==, textResult);
     87     g_assert_cmpint(startOffset, ==, startOffsetResult);
     88     g_assert_cmpint(endOffset, ==, endOffsetResult);
     89     g_free(text);
     90 }
     91 
     92 static void runGetTextTests(AtkText* textObject)
     93 {
     94     char* text = atk_text_get_text(textObject, 0, -1);
     95     g_assert_cmpstr(text, ==, "This is a test. This is the second sentence. And this the third.");
     96     g_free(text);
     97 
     98     /* ATK_TEXT_BOUNDARY_CHAR */
     99     testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_CHAR,
    100                         0, "T", 0, 1);
    101 
    102     testGetTextFunction(textObject, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_CHAR,
    103                         0, "h", 1, 2);
    104 
    105     testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_CHAR,
    106                         0, "", 0, 0);
    107 
    108     testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_CHAR,
    109                         1, "T", 0, 1);
    110 
    111     /* ATK_TEXT_BOUNDARY_WORD_START */
    112     testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START,
    113                         0, "This ", 0, 5);
    114 
    115     testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START,
    116                         4, "This ", 0, 5);
    117 
    118     testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START,
    119                         10, "test. ", 10, 16);
    120 
    121     testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START,
    122                         58, "third.", 58, 64);
    123 
    124     testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_WORD_START,
    125                         5, "This ", 0, 5);
    126 
    127     testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_WORD_START,
    128                         7, "This ", 0, 5);
    129 
    130     testGetTextFunction(textObject, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_WORD_START,
    131                         0, "is ", 5, 8);
    132 
    133     testGetTextFunction(textObject, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_WORD_START,
    134                         4, "is ", 5, 8);
    135 
    136     testGetTextFunction(textObject, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_WORD_START,
    137                         3, "is ", 5, 8);
    138 
    139     /* ATK_TEXT_BOUNDARY_WORD_END */
    140     testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END,
    141                         0, "This", 0, 4);
    142 
    143     testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END,
    144                         4, " is", 4, 7);
    145 
    146     testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END,
    147                         5, " is", 4, 7);
    148 
    149     testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END,
    150                         9, " test", 9, 14);
    151 
    152     testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_WORD_END,
    153                         5, "This", 0, 4);
    154 
    155     testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_WORD_END,
    156                         4, "This", 0, 4);
    157 
    158     testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_WORD_END,
    159                         7, " is", 4, 7);
    160 
    161     testGetTextFunction(textObject, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_WORD_END,
    162                         5, " a", 7, 9);
    163 
    164     testGetTextFunction(textObject, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_WORD_END,
    165                         4, " a", 7, 9);
    166 
    167     testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END,
    168                         58, " third", 57, 63);
    169 
    170     /* ATK_TEXT_BOUNDARY_SENTENCE_START */
    171     testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_SENTENCE_START,
    172                         0, "This is a test. ", 0, 16);
    173 
    174     testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_SENTENCE_START,
    175                         15, "This is a test. ", 0, 16);
    176 
    177     testGetTextFunction(textObject, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_SENTENCE_START,
    178                         0, "This is the second sentence. ", 16, 45);
    179 
    180     testGetTextFunction(textObject, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_SENTENCE_START,
    181                         15, "This is the second sentence. ", 16, 45);
    182 
    183     testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_SENTENCE_START,
    184                         16, "This is a test. ", 0, 16);
    185 
    186     testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_SENTENCE_START,
    187                         44, "This is a test. ", 0, 16);
    188 
    189     testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_SENTENCE_START,
    190                         15, "", 0, 0);
    191 
    192     /* ATK_TEXT_BOUNDARY_SENTENCE_END */
    193     testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_SENTENCE_END,
    194                         0, "This is a test.", 0, 15);
    195 
    196     testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_SENTENCE_END,
    197                         15, " This is the second sentence.", 15, 44);
    198 
    199     testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_SENTENCE_END,
    200                         16, " This is the second sentence.", 15, 44);
    201 
    202     testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_SENTENCE_END,
    203                         17, " This is the second sentence.", 15, 44);
    204 
    205     testGetTextFunction(textObject, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_SENTENCE_END,
    206                         0, " This is the second sentence.", 15, 44);
    207 
    208     testGetTextFunction(textObject, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_SENTENCE_END,
    209                         15, " And this the third.", 44, 64);
    210 
    211     testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_SENTENCE_END,
    212                         16, "This is a test.", 0, 15);
    213 
    214     testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_SENTENCE_END,
    215                         15, "This is a test.", 0, 15);
    216 
    217     testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_SENTENCE_END,
    218                         14, "", 0, 0);
    219 
    220     testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_SENTENCE_END,
    221                         44, " This is the second sentence.", 15, 44);
    222 
    223     /* It's trick to test these properly right now, since our a11y
    224        implementation splits different lines in different a11y items. */
    225     /* ATK_TEXT_BOUNDARY_LINE_START */
    226     testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_START,
    227                         0, "This is a test. This is the second sentence. And this the third.", 0, 64);
    228 
    229     /* ATK_TEXT_BOUNDARY_LINE_END */
    230     testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_END,
    231                         0, "This is a test. This is the second sentence. And this the third.", 0, 64);
    232 }
    233 
    234 static void testWebkitAtkCaretOffsets()
    235 {
    236     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
    237     g_object_ref_sink(webView);
    238     GtkAllocation allocation = { 0, 0, 800, 600 };
    239     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
    240     webkit_web_view_load_string(webView, textForCaretBrowsing, 0, 0, 0);
    241 
    242     /* Wait for the accessible objects to be created. */
    243     waitForAccessibleObjects();
    244 
    245     AtkObject* object = gtk_widget_get_accessible(GTK_WIDGET(webView));
    246     g_assert(object);
    247 
    248     AtkObject* header = atk_object_ref_accessible_child(object, 0);
    249     g_assert(ATK_IS_TEXT(header));
    250     gchar* text = atk_text_get_text(ATK_TEXT(header), 0, -1);
    251     g_assert_cmpstr(text, ==, "A text header");
    252     g_free (text);
    253 
    254     /* It should be possible to place the caret inside a header. */
    255     gboolean result = atk_text_set_caret_offset(ATK_TEXT(header), 5);
    256     g_assert_cmpint(result, ==, TRUE);
    257     gint offset = atk_text_get_caret_offset(ATK_TEXT(header));
    258     g_assert_cmpint(offset, ==, 5);
    259 
    260     AtkObject* paragraph = atk_object_ref_accessible_child(object, 1);
    261     g_assert(ATK_IS_TEXT(paragraph));
    262     text = atk_text_get_text(ATK_TEXT(paragraph), 0, -1);
    263     g_assert_cmpstr(text, ==, "A paragraph with a link in the middle");
    264     g_free (text);
    265 
    266     /* It should be possible to place the caret inside a paragraph and a link. */
    267     result = atk_text_set_caret_offset(ATK_TEXT(paragraph), 5);
    268     g_assert_cmpint(result, ==, TRUE);
    269     offset = atk_text_get_caret_offset(ATK_TEXT(paragraph));
    270     g_assert_cmpint(offset, ==, 5);
    271 
    272     result = atk_text_set_caret_offset(ATK_TEXT(paragraph), 20);
    273     g_assert_cmpint(result, ==, TRUE);
    274     offset = atk_text_get_caret_offset(ATK_TEXT(paragraph));
    275     g_assert_cmpint(offset, ==, 20);
    276 
    277     result = atk_text_set_caret_offset(ATK_TEXT(paragraph), 30);
    278     g_assert_cmpint(result, ==, TRUE);
    279     offset = atk_text_get_caret_offset(ATK_TEXT(paragraph));
    280     g_assert_cmpint(offset, ==, 30);
    281 
    282     AtkObject* list = atk_object_ref_accessible_child(object, 2);
    283     g_assert(ATK_OBJECT(list));
    284     g_assert(atk_object_get_role(list) == ATK_ROLE_LIST);
    285     g_assert_cmpint(atk_object_get_n_accessible_children(list), ==, 1);
    286 
    287     AtkObject* listItem = atk_object_ref_accessible_child(list, 0);
    288     g_assert(ATK_IS_TEXT(listItem));
    289     text = atk_text_get_text(ATK_TEXT(listItem), 0, -1);
    290     g_assert_cmpstr(text, ==, "1. A list item");
    291     g_free (text);
    292 
    293     /* It's not possible to place the caret inside an item's marker. */
    294     result = atk_text_set_caret_offset(ATK_TEXT(listItem), 1);
    295     g_assert_cmpint(result, ==, FALSE);
    296 
    297     /* It should be possible to place the caret inside an item's text. */
    298     result = atk_text_set_caret_offset(ATK_TEXT(listItem), 5);
    299     g_assert_cmpint(result, ==, TRUE);
    300     offset = atk_text_get_caret_offset(ATK_TEXT(listItem));
    301     g_assert_cmpint(offset, ==, 5);
    302 
    303     AtkObject* panel = atk_object_ref_accessible_child(object, 3);
    304     g_assert(ATK_IS_OBJECT(panel));
    305     g_assert(atk_object_get_role(panel) == ATK_ROLE_PANEL);
    306 
    307     AtkObject* comboBox = atk_object_ref_accessible_child(panel, 0);
    308     g_assert(ATK_IS_OBJECT(comboBox));
    309     g_assert(atk_object_get_role(comboBox) == ATK_ROLE_COMBO_BOX);
    310 
    311     AtkObject* menuPopup = atk_object_ref_accessible_child(comboBox, 0);
    312     g_assert(ATK_IS_OBJECT(menuPopup));
    313     g_assert(atk_object_get_role(menuPopup) == ATK_ROLE_MENU);
    314 
    315     AtkObject* comboBoxOption = atk_object_ref_accessible_child(menuPopup, 0);
    316     g_assert(ATK_IS_OBJECT(comboBoxOption));
    317     g_assert(atk_object_get_role(comboBoxOption) == ATK_ROLE_MENU_ITEM);
    318     g_assert(ATK_IS_TEXT(comboBoxOption));
    319     text = atk_text_get_text(ATK_TEXT(comboBoxOption), 0, -1);
    320     g_assert_cmpstr(text, ==, "An option in a combo box");
    321 
    322     /* It's not possible to place the caret inside an option for a combobox. */
    323     result = atk_text_set_caret_offset(ATK_TEXT(comboBoxOption), 1);
    324     g_assert_cmpint(result, ==, FALSE);
    325 
    326     g_object_unref(header);
    327     g_object_unref(paragraph);
    328     g_object_unref(list);
    329     g_object_unref(listItem);
    330     g_object_unref(panel);
    331     g_object_unref(comboBox);
    332     g_object_unref(menuPopup);
    333     g_object_unref(comboBoxOption);
    334     g_object_unref(webView);
    335 }
    336 
    337 static void testWebkitAtkCaretOffsetsAndExtranousWhiteSpaces()
    338 {
    339     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
    340     g_object_ref_sink(webView);
    341     GtkAllocation allocation = { 0, 0, 800, 600 };
    342     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
    343     webkit_web_view_load_string(webView, contentsWithExtraneousWhiteSpaces, 0, 0, 0);
    344 
    345     /* Wait for the accessible objects to be created. */
    346     waitForAccessibleObjects();
    347 
    348     /* Enable caret browsing. */
    349     WebKitWebSettings* settings = webkit_web_view_get_settings(webView);
    350     g_object_set(G_OBJECT(settings), "enable-caret-browsing", TRUE, NULL);
    351     webkit_web_view_set_settings(webView, settings);
    352 
    353     /* Get to the inner AtkText object. */
    354     AtkObject* object = gtk_widget_get_accessible(GTK_WIDGET(webView));
    355     g_assert(object);
    356     object = atk_object_ref_accessible_child(object, 0);
    357     g_assert(object);
    358 
    359     AtkText* textObject = ATK_TEXT(object);
    360     g_assert(ATK_IS_TEXT(textObject));
    361 
    362     gchar* text = atk_text_get_text(textObject, 0, -1);
    363     g_assert_cmpstr(text, ==, "This paragraph is borked!");
    364     g_free(text);
    365 
    366     gint characterCount = atk_text_get_character_count(textObject);
    367     g_assert_cmpint(characterCount, ==, 25);
    368 
    369     gboolean result = atk_text_set_caret_offset(textObject, characterCount - 1);
    370     g_assert_cmpint(result, ==, TRUE);
    371 
    372     gint caretOffset = atk_text_get_caret_offset(textObject);
    373     g_assert_cmpint(caretOffset, ==, characterCount - 1);
    374 
    375     result = atk_text_set_caret_offset(textObject, characterCount);
    376     g_assert_cmpint(result, ==, TRUE);
    377 
    378     caretOffset = atk_text_get_caret_offset(textObject);
    379     g_assert_cmpint(caretOffset, ==, characterCount);
    380 
    381     g_object_unref(webView);
    382 }
    383 
    384 static void testWebkitAtkComboBox()
    385 {
    386     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
    387     g_object_ref_sink(webView);
    388     GtkAllocation allocation = { 0, 0, 800, 600 };
    389     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
    390     webkit_web_view_load_string(webView, comboBoxSelector, 0, 0, 0);
    391 
    392     /* Wait for the accessible objects to be created. */
    393     waitForAccessibleObjects();
    394 
    395     AtkObject* object = gtk_widget_get_accessible(GTK_WIDGET(webView));
    396     g_assert(object);
    397 
    398     AtkObject* formObject = atk_object_ref_accessible_child(object, 0);
    399     g_assert(formObject);
    400 
    401     AtkObject* comboBox = atk_object_ref_accessible_child(formObject, 0);
    402     g_assert(ATK_IS_OBJECT(comboBox));
    403 
    404     AtkObject* menuPopup = atk_object_ref_accessible_child(comboBox, 0);
    405     g_assert(ATK_IS_OBJECT(menuPopup));
    406 
    407     AtkObject* item1 = atk_object_ref_accessible_child(menuPopup, 0);
    408     g_assert(ATK_IS_OBJECT(item1));
    409 
    410     AtkObject* item2 = atk_object_ref_accessible_child(menuPopup, 1);
    411     g_assert(ATK_IS_OBJECT(item2));
    412 
    413     /* Check roles. */
    414     g_assert(atk_object_get_role(comboBox) == ATK_ROLE_COMBO_BOX);
    415     g_assert(atk_object_get_role(menuPopup) == ATK_ROLE_MENU);
    416     g_assert(atk_object_get_role(item1) == ATK_ROLE_MENU_ITEM);
    417     g_assert(atk_object_get_role(item2) == ATK_ROLE_MENU_ITEM);
    418 
    419     /* Check the implementation of the AtkSelection interface. */
    420     g_assert(ATK_IS_SELECTION(comboBox));
    421     AtkSelection* atkSelection = ATK_SELECTION(comboBox);
    422     g_assert_cmpint(atk_selection_get_selection_count(atkSelection), ==, 1);
    423     g_assert(atk_selection_is_child_selected(atkSelection, 0));
    424     g_assert(!atk_selection_is_child_selected(atkSelection, 1));
    425     AtkObject* selectedItem = atk_selection_ref_selection(atkSelection, 0);
    426     g_assert(selectedItem == item1);
    427     g_object_unref(selectedItem);
    428 
    429     /* Check the implementations of the AtkAction interface. */
    430     g_assert(ATK_IS_ACTION(comboBox));
    431     AtkAction* atkAction = ATK_ACTION(comboBox);
    432     g_assert_cmpint(atk_action_get_n_actions(atkAction), ==, 1);
    433     g_assert(atk_action_do_action(atkAction, 0));
    434 
    435     g_assert(ATK_IS_ACTION(menuPopup));
    436     atkAction = ATK_ACTION(menuPopup);
    437     g_assert_cmpint(atk_action_get_n_actions(atkAction), ==, 1);
    438     g_assert(atk_action_do_action(atkAction, 0));
    439 
    440     g_assert(ATK_IS_ACTION(item1));
    441     atkAction = ATK_ACTION(item1);
    442     g_assert_cmpint(atk_action_get_n_actions(atkAction), ==, 1);
    443     g_assert(atk_action_do_action(atkAction, 0));
    444 
    445     g_assert(ATK_IS_ACTION(item2));
    446     atkAction = ATK_ACTION(item2);
    447     g_assert_cmpint(atk_action_get_n_actions(atkAction), ==, 1);
    448     g_assert(atk_action_do_action(atkAction, 0));
    449 
    450     /* After selecting the second item, selection should have changed. */
    451     g_assert_cmpint(atk_selection_get_selection_count(atkSelection), ==, 1);
    452     g_assert(!atk_selection_is_child_selected(atkSelection, 0));
    453     g_assert(atk_selection_is_child_selected(atkSelection, 1));
    454     selectedItem = atk_selection_ref_selection(atkSelection, 0);
    455     g_assert(selectedItem == item2);
    456     g_object_unref(selectedItem);
    457 
    458     /* Check the implementation of the AtkText interface. */
    459     g_assert(ATK_IS_TEXT(item1));
    460     AtkText* atkText = ATK_TEXT(item1);
    461     char *text = atk_text_get_text(atkText, 0, -1);
    462     g_assert_cmpstr(text, ==, "foo");
    463     g_free(text);
    464     text = atk_text_get_text(atkText, 0, 2);
    465     g_assert_cmpstr(text, ==, "fo");
    466     g_free(text);
    467 
    468     g_assert(ATK_IS_TEXT(item2));
    469     atkText = ATK_TEXT(item2);
    470     text = atk_text_get_text(atkText, 0, -1);
    471     g_assert_cmpstr(text, ==, "bar");
    472     g_free(text);
    473     text = atk_text_get_text(atkText, 1, 3);
    474     g_assert_cmpstr(text, ==, "ar");
    475     g_free(text);
    476 
    477     g_object_unref(formObject);
    478     g_object_unref(comboBox);
    479     g_object_unref(menuPopup);
    480     g_object_unref(item1);
    481     g_object_unref(item2);
    482     g_object_unref(webView);
    483 }
    484 
    485 static void testWebkitAtkEmbeddedObjects()
    486 {
    487     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
    488     g_object_ref_sink(webView);
    489     GtkAllocation allocation = { 0, 0, 800, 600 };
    490     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
    491     webkit_web_view_load_string(webView, embeddedObjects, 0, 0, 0);
    492 
    493     /* Wait for the accessible objects to be created. */
    494     waitForAccessibleObjects();
    495 
    496     AtkObject* object = gtk_widget_get_accessible(GTK_WIDGET(webView));
    497     g_assert(object);
    498 
    499     AtkText* paragraph1 = ATK_TEXT(atk_object_ref_accessible_child(object, 0));
    500     g_assert(ATK_IS_TEXT(paragraph1));
    501     g_assert(ATK_IS_HYPERTEXT(paragraph1));
    502 
    503     const gchar* expectedText = "Choose: \357\277\274foo \357\277\274bar (pick one)";
    504     char* text = atk_text_get_text(paragraph1, 0, -1);
    505     g_assert_cmpstr(text, ==, expectedText);
    506     g_free(text);
    507 
    508     gint nLinks = atk_hypertext_get_n_links(ATK_HYPERTEXT(paragraph1));
    509     g_assert_cmpint(nLinks, ==, 2);
    510 
    511     AtkHyperlink* hLink = atk_hypertext_get_link(ATK_HYPERTEXT(paragraph1), 0);
    512     g_assert(ATK_HYPERLINK(hLink));
    513     AtkObject* hLinkObject = atk_hyperlink_get_object(hLink, 0);
    514     g_assert(ATK_OBJECT(hLinkObject));
    515     g_assert(atk_object_get_role(hLinkObject) == ATK_ROLE_CHECK_BOX);
    516     g_assert_cmpint(atk_hyperlink_get_start_index(hLink), ==, 8);
    517     g_assert_cmpint(atk_hyperlink_get_end_index(hLink), ==, 9);
    518     g_assert_cmpint(atk_hyperlink_get_n_anchors(hLink), ==, 1);
    519     g_assert_cmpstr(atk_hyperlink_get_uri(hLink, 0), ==, 0);
    520 
    521     AtkText* paragraph2 = ATK_TEXT(atk_object_ref_accessible_child(object, 1));
    522     g_assert(ATK_IS_TEXT(paragraph2));
    523     g_assert(ATK_IS_HYPERTEXT(paragraph2));
    524 
    525     expectedText = "Choose: \357\277\274 (pick one)";
    526     text = atk_text_get_text(paragraph2, 0, -1);
    527     g_assert_cmpstr(text, ==, expectedText);
    528     g_free(text);
    529 
    530     nLinks = atk_hypertext_get_n_links(ATK_HYPERTEXT(paragraph2));
    531     g_assert_cmpint(nLinks, ==, 1);
    532 
    533     hLink = atk_hypertext_get_link(ATK_HYPERTEXT(paragraph2), 0);
    534     g_assert(ATK_HYPERLINK(hLink));
    535     hLinkObject = atk_hyperlink_get_object(hLink, 0);
    536     g_assert(ATK_OBJECT(hLinkObject));
    537     g_assert(atk_object_get_role(hLinkObject) == ATK_ROLE_COMBO_BOX);
    538     g_assert_cmpint(atk_hyperlink_get_start_index(hLink), ==, 8);
    539     g_assert_cmpint(atk_hyperlink_get_end_index(hLink), ==, 9);
    540     g_assert_cmpint(atk_hyperlink_get_n_anchors(hLink), ==, 1);
    541     g_assert_cmpstr(atk_hyperlink_get_uri(hLink, 0), ==, 0);
    542 
    543     AtkText* paragraph3 = ATK_TEXT(atk_object_ref_accessible_child(object, 2));
    544     g_assert(ATK_IS_TEXT(paragraph3));
    545     g_assert(ATK_IS_HYPERTEXT(paragraph3));
    546 
    547     expectedText = "\357\277\274";
    548     text = atk_text_get_text(paragraph3, 0, -1);
    549     g_assert_cmpstr(text, ==, expectedText);
    550     g_free(text);
    551 
    552     nLinks = atk_hypertext_get_n_links(ATK_HYPERTEXT(paragraph3));
    553     g_assert_cmpint(nLinks, ==, 1);
    554 
    555     hLink = atk_hypertext_get_link(ATK_HYPERTEXT(paragraph3), 0);
    556     g_assert(ATK_HYPERLINK(hLink));
    557     hLinkObject = atk_hyperlink_get_object(hLink, 0);
    558     g_assert(ATK_OBJECT(hLinkObject));
    559     g_assert(atk_object_get_role(hLinkObject) == ATK_ROLE_PUSH_BUTTON);
    560     g_assert_cmpint(atk_hyperlink_get_start_index(hLink), ==, 0);
    561     g_assert_cmpint(atk_hyperlink_get_end_index(hLink), ==, 1);
    562     g_assert_cmpint(atk_hyperlink_get_n_anchors(hLink), ==, 1);
    563     g_assert_cmpstr(atk_hyperlink_get_uri(hLink, 0), ==, 0);
    564 
    565     g_object_unref(paragraph1);
    566     g_object_unref(paragraph2);
    567     g_object_unref(paragraph3);
    568     g_object_unref(webView);
    569 }
    570 
    571 static void testWebkitAtkGetTextAtOffsetForms()
    572 {
    573     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
    574     g_object_ref_sink(webView);
    575     GtkAllocation allocation = { 0, 0, 800, 600 };
    576     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
    577     webkit_web_view_load_string(webView, contents, 0, 0, 0);
    578 
    579     /* Wait for the accessible objects to be created. */
    580     waitForAccessibleObjects();
    581 
    582     /* Get to the inner AtkText object. */
    583     AtkObject* object = gtk_widget_get_accessible(GTK_WIDGET(webView));
    584     g_assert(object);
    585     object = atk_object_ref_accessible_child(object, 0);
    586     g_assert(object);
    587 
    588     AtkText* textObject = ATK_TEXT(object);
    589     g_assert(ATK_IS_TEXT(textObject));
    590 
    591     runGetTextTests(textObject);
    592 
    593     g_object_unref(webView);
    594 }
    595 
    596 static void testWebkitAtkGetTextAtOffset()
    597 {
    598     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
    599     g_object_ref_sink(webView);
    600     GtkAllocation allocation = { 0, 0, 800, 600 };
    601     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
    602     webkit_web_view_load_string(webView, contents, 0, 0, 0);
    603 
    604     /* Wait for the accessible objects to be created. */
    605     waitForAccessibleObjects();
    606 
    607     /* Get to the inner AtkText object. */
    608     AtkObject* object = gtk_widget_get_accessible(GTK_WIDGET(webView));
    609     g_assert(object);
    610     object = atk_object_ref_accessible_child(object, 0);
    611     g_assert(object);
    612 
    613     AtkText* textObject = ATK_TEXT(object);
    614     g_assert(ATK_IS_TEXT(textObject));
    615 
    616     runGetTextTests(textObject);
    617 
    618     g_object_unref(webView);
    619 }
    620 
    621 static void testWebkitAtkGetTextAtOffsetNewlines()
    622 {
    623     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
    624     g_object_ref_sink(webView);
    625     GtkAllocation allocation = { 0, 0, 800, 600 };
    626     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
    627     webkit_web_view_load_string(webView, contentsWithNewlines, 0, 0, 0);
    628 
    629     /* Wait for the accessible objects to be created. */
    630     waitForAccessibleObjects();
    631 
    632     /* Get to the inner AtkText object. */
    633     AtkObject* object = gtk_widget_get_accessible(GTK_WIDGET(webView));
    634     g_assert(object);
    635     object = atk_object_ref_accessible_child(object, 0);
    636     g_assert(object);
    637 
    638     AtkText* textObject = ATK_TEXT(object);
    639     g_assert(ATK_IS_TEXT(textObject));
    640 
    641     runGetTextTests(textObject);
    642 
    643     g_object_unref(webView);
    644 }
    645 
    646 static void testWebkitAtkGetTextAtOffsetTextarea()
    647 {
    648     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
    649     g_object_ref_sink(webView);
    650     GtkAllocation allocation = { 0, 0, 800, 600 };
    651     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
    652     webkit_web_view_load_string(webView, contentsInTextarea, 0, 0, 0);
    653 
    654     /* Wait for the accessible objects to be created. */
    655     waitForAccessibleObjects();
    656 
    657     /* Get to the inner AtkText object. */
    658     AtkObject* object = gtk_widget_get_accessible(GTK_WIDGET(webView));
    659     g_assert(object);
    660     object = atk_object_ref_accessible_child(object, 0);
    661     g_assert(object);
    662     object = atk_object_ref_accessible_child(object, 0);
    663     g_assert(object);
    664 
    665     AtkText* textObject = ATK_TEXT(object);
    666     g_assert(ATK_IS_TEXT(textObject));
    667 
    668     runGetTextTests(textObject);
    669 
    670     g_object_unref(webView);
    671 }
    672 
    673 static void testWebkitAtkGetTextAtOffsetTextInput()
    674 {
    675     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
    676     g_object_ref_sink(webView);
    677     GtkAllocation allocation = { 0, 0, 800, 600 };
    678     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
    679     webkit_web_view_load_string(webView, contentsInTextInput, 0, 0, 0);
    680 
    681     /* Wait for the accessible objects to be created. */
    682     waitForAccessibleObjects();
    683 
    684     /* Get to the inner AtkText object. */
    685     AtkObject* object = gtk_widget_get_accessible(GTK_WIDGET(webView));
    686     g_assert(object);
    687     object = atk_object_ref_accessible_child(object, 0);
    688     g_assert(object);
    689     object = atk_object_ref_accessible_child(object, 0);
    690     g_assert(object);
    691 
    692     AtkText* textObject = ATK_TEXT(object);
    693     g_assert(ATK_IS_TEXT(textObject));
    694 
    695     runGetTextTests(textObject);
    696 
    697     g_object_unref(webView);
    698 }
    699 
    700 static void testWebkitAtkGetTextAtOffsetWithSpecialCharacters()
    701 {
    702     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
    703     g_object_ref_sink(webView);
    704     GtkAllocation allocation = { 0, 0, 800, 600 };
    705     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
    706     webkit_web_view_load_string(webView, contentsWithSpecialChars, 0, 0, 0);
    707 
    708     /* Wait for the accessible objects to be created. */
    709     waitForAccessibleObjects();
    710 
    711     /* Get to the inner AtkText object. */
    712     AtkObject* object = gtk_widget_get_accessible(GTK_WIDGET(webView));
    713     g_assert(object);
    714     object = atk_object_ref_accessible_child(object, 0);
    715     g_assert(object);
    716 
    717     AtkText* textObject = ATK_TEXT(object);
    718     g_assert(ATK_IS_TEXT(textObject));
    719 
    720     const gchar* expectedText = "\302\253\302\240This is a paragraph with \342\200\234special\342\200\235 characters inside.\302\240\302\273";
    721     char* text = atk_text_get_text(textObject, 0, -1);
    722     g_assert_cmpstr(text, ==, expectedText);
    723     g_free(text);
    724 
    725     /* Check that getting the text with ATK_TEXT_BOUNDARY_LINE_START
    726        and ATK_TEXT_BOUNDARY_LINE_END does not crash because of not
    727        properly handling characters inside the UTF-8 string. */
    728     testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_START, 0, expectedText, 0, 57);
    729     testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_END, 0, expectedText, 0, 57);
    730 
    731     g_object_unref(webView);
    732 }
    733 
    734 static void testWebkitAtkGetTextInParagraphAndBodySimple()
    735 {
    736     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
    737     g_object_ref_sink(webView);
    738     GtkAllocation allocation = { 0, 0, 800, 600 };
    739     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
    740     webkit_web_view_load_string(webView, contentsInParagraphAndBodySimple, 0, 0, 0);
    741 
    742     /* Wait for the accessible objects to be created. */
    743     waitForAccessibleObjects();
    744 
    745     /* Get to the inner AtkText object. */
    746     AtkObject* object = gtk_widget_get_accessible(GTK_WIDGET(webView));
    747     g_assert(object);
    748     AtkObject* object1 = atk_object_ref_accessible_child(object, 0);
    749     g_assert(object1);
    750     AtkObject* object2 = atk_object_ref_accessible_child(object, 1);
    751     g_assert(object2);
    752 
    753     AtkText* textObject1 = ATK_TEXT(object1);
    754     g_assert(ATK_IS_TEXT(textObject1));
    755     AtkText* textObject2 = ATK_TEXT(object2);
    756     g_assert(ATK_IS_TEXT(textObject2));
    757 
    758     char *text = atk_text_get_text(textObject1, 0, -1);
    759     g_assert_cmpstr(text, ==, "This is a test.");
    760 
    761     text = atk_text_get_text(textObject2, 0, 12);
    762     g_assert_cmpstr(text, ==, "Hello world.");
    763 
    764     g_object_unref(object1);
    765     g_object_unref(object2);
    766     g_object_unref(webView);
    767 }
    768 
    769 static void testWebkitAtkGetTextInParagraphAndBodyModerate()
    770 {
    771     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
    772     g_object_ref_sink(webView);
    773     GtkAllocation allocation = { 0, 0, 800, 600 };
    774     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
    775     webkit_web_view_load_string(webView, contentsInParagraphAndBodyModerate, 0, 0, 0);
    776 
    777     /* Wait for the accessible objects to be created. */
    778     waitForAccessibleObjects();
    779 
    780     /* Get to the inner AtkText object. */
    781     AtkObject* object = gtk_widget_get_accessible(GTK_WIDGET(webView));
    782     g_assert(object);
    783     AtkObject* object1 = atk_object_ref_accessible_child(object, 0);
    784     g_assert(object1);
    785     AtkObject* object2 = atk_object_ref_accessible_child(object, 1);
    786     g_assert(object2);
    787 
    788     AtkText* textObject1 = ATK_TEXT(object1);
    789     g_assert(ATK_IS_TEXT(textObject1));
    790     AtkText* textObject2 = ATK_TEXT(object2);
    791     g_assert(ATK_IS_TEXT(textObject2));
    792 
    793     char *text = atk_text_get_text(textObject1, 0, -1);
    794     g_assert_cmpstr(text, ==, "This is a test.");
    795 
    796     text = atk_text_get_text(textObject2, 0, 53);
    797     g_assert_cmpstr(text, ==, "Hello world.\nThis sentence is green.\nThis one is not.");
    798 
    799     g_object_unref(object1);
    800     g_object_unref(object2);
    801     g_object_unref(webView);
    802 }
    803 
    804 static void testWebkitAtkGetTextInTable()
    805 {
    806     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
    807     g_object_ref_sink(webView);
    808     GtkAllocation allocation = { 0, 0, 800, 600 };
    809     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
    810     webkit_web_view_load_string(webView, contentsInTable, 0, 0, 0);
    811 
    812     /* Wait for the accessible objects to be created. */
    813     waitForAccessibleObjects();
    814 
    815     AtkObject* object = gtk_widget_get_accessible(GTK_WIDGET(webView));
    816     g_assert(object);
    817     object = atk_object_ref_accessible_child(object, 0);
    818     g_assert(object);
    819 
    820     /* Tables should not implement AtkText. */
    821     g_assert(!G_TYPE_INSTANCE_GET_INTERFACE(object, ATK_TYPE_TEXT, AtkTextIface));
    822 
    823     g_object_unref(object);
    824     g_object_unref(webView);
    825 }
    826 
    827 static void testWebkitAtkGetHeadersInTable()
    828 {
    829     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
    830     g_object_ref_sink(webView);
    831     GtkAllocation allocation = { 0, 0, 800, 600 };
    832     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
    833     webkit_web_view_load_string(webView, contentsInTableWithHeaders, 0, 0, 0);
    834 
    835     /* Wait for the accessible objects to be created. */
    836     waitForAccessibleObjects();
    837 
    838     AtkObject* axWebView = gtk_widget_get_accessible(GTK_WIDGET(webView));
    839     g_assert(axWebView);
    840 
    841     /* Check table with both column and row headers. */
    842     AtkObject* table = atk_object_ref_accessible_child(axWebView, 0);
    843     g_assert(table);
    844     g_assert(atk_object_get_role(table) == ATK_ROLE_TABLE);
    845 
    846     AtkObject* colHeader = atk_table_get_column_header(ATK_TABLE(table), 0);
    847     g_assert(colHeader);
    848     g_assert(atk_object_get_role(colHeader) == ATK_ROLE_TABLE_CELL);
    849     g_assert(atk_object_get_index_in_parent(colHeader) == 0);
    850 
    851     colHeader = atk_table_get_column_header(ATK_TABLE(table), 1);
    852     g_assert(colHeader);
    853     g_assert(atk_object_get_role(colHeader) == ATK_ROLE_TABLE_CELL);
    854     g_assert(atk_object_get_index_in_parent(colHeader) == 1);
    855 
    856     colHeader = atk_table_get_column_header(ATK_TABLE(table), 2);
    857     g_assert(colHeader);
    858     g_assert(atk_object_get_role(colHeader) == ATK_ROLE_TABLE_CELL);
    859     g_assert(atk_object_get_index_in_parent(colHeader) == 2);
    860 
    861     colHeader = atk_table_get_column_header(ATK_TABLE(table), 3);
    862     g_assert(colHeader);
    863     g_assert(atk_object_get_role(colHeader) == ATK_ROLE_TABLE_CELL);
    864     g_assert(atk_object_get_index_in_parent(colHeader) == 2);
    865 
    866     AtkObject* rowHeader = atk_table_get_row_header(ATK_TABLE(table), 0);
    867     g_assert(rowHeader);
    868     g_assert(atk_object_get_role(rowHeader) == ATK_ROLE_TABLE_CELL);
    869     g_assert(atk_object_get_index_in_parent(rowHeader) == 0);
    870 
    871     rowHeader = atk_table_get_row_header(ATK_TABLE(table), 1);
    872     g_assert(rowHeader);
    873     g_assert(atk_object_get_role(rowHeader) == ATK_ROLE_TABLE_CELL);
    874     g_assert(atk_object_get_index_in_parent(rowHeader) == 3);
    875 
    876     rowHeader = atk_table_get_row_header(ATK_TABLE(table), 2);
    877     g_assert(rowHeader);
    878     g_assert(atk_object_get_role(rowHeader) == ATK_ROLE_TABLE_CELL);
    879     g_assert(atk_object_get_index_in_parent(rowHeader) == 7);
    880 
    881     rowHeader = atk_table_get_row_header(ATK_TABLE(table), 3);
    882     g_assert(rowHeader);
    883     g_assert(atk_object_get_role(rowHeader) == ATK_ROLE_TABLE_CELL);
    884     g_assert(atk_object_get_index_in_parent(rowHeader) == 7);
    885 
    886     g_object_unref(table);
    887 
    888     /* Check table with no headers at all. */
    889     table = atk_object_ref_accessible_child(axWebView, 1);
    890     g_assert(table);
    891     g_assert(atk_object_get_role(table) == ATK_ROLE_TABLE);
    892 
    893     colHeader = atk_table_get_column_header(ATK_TABLE(table), 0);
    894     g_assert(colHeader == 0);
    895 
    896     colHeader = atk_table_get_column_header(ATK_TABLE(table), 1);
    897     g_assert(colHeader == 0);
    898 
    899     rowHeader = atk_table_get_row_header(ATK_TABLE(table), 0);
    900     g_assert(rowHeader == 0);
    901 
    902     rowHeader = atk_table_get_row_header(ATK_TABLE(table), 1);
    903     g_assert(rowHeader == 0);
    904 
    905     g_object_unref(table);
    906     g_object_unref(webView);
    907 }
    908 
    909 static gint compAtkAttribute(AtkAttribute* a1, AtkAttribute* a2)
    910 {
    911     gint strcmpVal = g_strcmp0(a1->name, a2->name);
    912     if (strcmpVal)
    913         return strcmpVal;
    914     return g_strcmp0(a1->value, a2->value);
    915 }
    916 
    917 static gint compAtkAttributeName(AtkAttribute* a1, AtkAttribute* a2)
    918 {
    919     return g_strcmp0(a1->name, a2->name);
    920 }
    921 
    922 static gboolean atkAttributeSetAttributeNameHasValue(AtkAttributeSet* set, const gchar* attributeName, const gchar* value)
    923 {
    924     AtkAttribute at;
    925     at.name = (gchar*)attributeName;
    926     GSList* element = g_slist_find_custom(set, &at, (GCompareFunc)compAtkAttributeName);
    927     return element && !g_strcmp0(((AtkAttribute*)(element->data))->value, value);
    928 }
    929 
    930 static gboolean atkAttributeSetContainsAttributeName(AtkAttributeSet* set, const gchar* attributeName)
    931 {
    932     AtkAttribute at;
    933     at.name = (gchar*)attributeName;
    934     return g_slist_find_custom(set, &at, (GCompareFunc)compAtkAttributeName) ? true : false;
    935 }
    936 
    937 static gboolean atkAttributeSetAttributeHasValue(AtkAttributeSet* set, AtkTextAttribute attribute, const gchar* value)
    938 {
    939     return atkAttributeSetAttributeNameHasValue(set, atk_text_attribute_get_name(attribute), value);
    940 }
    941 
    942 static gboolean atkAttributeSetAreEqual(AtkAttributeSet* set1, AtkAttributeSet* set2)
    943 {
    944     if (!set1)
    945         return !set2;
    946 
    947     set1 = g_slist_sort(set1, (GCompareFunc)compAtkAttribute);
    948     set2 = g_slist_sort(set2, (GCompareFunc)compAtkAttribute);
    949 
    950     while (set1) {
    951         if (!set2 || compAtkAttribute(set1->data, set2->data))
    952             return FALSE;
    953 
    954         set1 = set1->next;
    955         set2 = set2->next;
    956     }
    957 
    958     return (!set2);
    959 }
    960 
    961 static void testWebkitAtkTextAttributes()
    962 {
    963     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
    964     g_object_ref_sink(webView);
    965     GtkAllocation allocation = { 0, 0, 800, 600 };
    966     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
    967     webkit_web_view_load_string(webView, textWithAttributes, 0, 0, 0);
    968 
    969     /* Wait for the accessible objects to be created. */
    970     waitForAccessibleObjects();
    971 
    972     AtkObject* object = gtk_widget_get_accessible(GTK_WIDGET(webView));
    973     g_assert(object);
    974 
    975     AtkObject* child = atk_object_ref_accessible_child(object, 0);
    976     g_assert(child && ATK_IS_TEXT(child));
    977     AtkText* childText = ATK_TEXT(child);
    978 
    979     gint startOffset;
    980     gint endOffset;
    981     AtkAttributeSet* set1 = atk_text_get_run_attributes(childText, 0, &startOffset, &endOffset);
    982     g_assert_cmpint(startOffset, ==, 0);
    983     g_assert_cmpint(endOffset, ==, 12);
    984     g_assert(atkAttributeSetAreEqual(set1, 0));
    985 
    986     AtkAttributeSet* set2 = atk_text_get_run_attributes(childText, 15, &startOffset, &endOffset);
    987     g_assert_cmpint(startOffset, ==, 12);
    988     g_assert_cmpint(endOffset, ==, 17);
    989     g_assert(atkAttributeSetAttributeHasValue(set2, ATK_TEXT_ATTR_STYLE, "italic"));
    990 
    991     AtkAttributeSet* set3 = atk_text_get_run_attributes(childText, 17, &startOffset, &endOffset);
    992     g_assert_cmpint(startOffset, ==, 17);
    993     g_assert_cmpint(endOffset, ==, 40);
    994     g_assert(atkAttributeSetAttributeHasValue(set3, ATK_TEXT_ATTR_WEIGHT, "700"));
    995 
    996     AtkAttributeSet* set4 = atk_text_get_default_attributes(childText);
    997     g_assert(atkAttributeSetAttributeHasValue(set4, ATK_TEXT_ATTR_STYLE, "normal"));
    998     g_assert(atkAttributeSetAttributeHasValue(set4, ATK_TEXT_ATTR_JUSTIFICATION, "right"));
    999     g_assert(atkAttributeSetAttributeHasValue(set4, ATK_TEXT_ATTR_SIZE, "14"));
   1000     atk_attribute_set_free(set1);
   1001     atk_attribute_set_free(set2);
   1002     atk_attribute_set_free(set3);
   1003     atk_attribute_set_free(set4);
   1004 
   1005     child = atk_object_ref_accessible_child(object, 1);
   1006     g_assert(child && ATK_IS_TEXT(child));
   1007     childText = ATK_TEXT(child);
   1008 
   1009     set1 = atk_text_get_default_attributes(childText);
   1010     g_assert(atkAttributeSetAttributeHasValue(set1, ATK_TEXT_ATTR_FAMILY_NAME, "monospace"));
   1011     g_assert(atkAttributeSetAttributeHasValue(set1, ATK_TEXT_ATTR_STYLE, "normal"));
   1012     g_assert(atkAttributeSetAttributeHasValue(set1, ATK_TEXT_ATTR_STRIKETHROUGH, "false"));
   1013     g_assert(atkAttributeSetAttributeHasValue(set1, ATK_TEXT_ATTR_WEIGHT, "400"));
   1014     g_assert(atkAttributeSetAttributeHasValue(set1, ATK_TEXT_ATTR_FG_COLOR, "120,121,122"));
   1015 
   1016     set2 = atk_text_get_run_attributes(childText, 43, &startOffset, &endOffset);
   1017     g_assert_cmpint(startOffset, ==, 43);
   1018     g_assert_cmpint(endOffset, ==, 80);
   1019     /* Checks that default attributes of text are not returned when called to atk_text_get_run_attributes. */
   1020     g_assert(!atkAttributeSetAttributeHasValue(set2, ATK_TEXT_ATTR_FG_COLOR, "120,121,122"));
   1021     g_assert(atkAttributeSetAttributeHasValue(set2, ATK_TEXT_ATTR_UNDERLINE, "single"));
   1022     g_assert(atkAttributeSetAttributeHasValue(set2, ATK_TEXT_ATTR_BG_COLOR, "80,81,82"));
   1023     atk_attribute_set_free(set1);
   1024     atk_attribute_set_free(set2);
   1025 
   1026     child = atk_object_ref_accessible_child(object, 2);
   1027     g_assert(child && ATK_IS_TEXT(child));
   1028     childText = ATK_TEXT(child);
   1029 
   1030     set1 = atk_text_get_run_attributes(childText, 0, &startOffset, &endOffset);
   1031     set2 = atk_text_get_run_attributes(childText, 3, &startOffset, &endOffset);
   1032     g_assert(atkAttributeSetAreEqual(set1, set2));
   1033     atk_attribute_set_free(set2);
   1034 
   1035     set2 = atk_text_get_run_attributes(childText, 1, &startOffset, &endOffset);
   1036     set3 = atk_text_get_run_attributes(childText, 5, &startOffset, &endOffset);
   1037     g_assert(atkAttributeSetAreEqual(set2, set3));
   1038     g_assert(!atkAttributeSetAreEqual(set1, set2));
   1039     atk_attribute_set_free(set3);
   1040 
   1041     set3 = atk_text_get_run_attributes(childText, 2, &startOffset, &endOffset);
   1042     set4 = atk_text_get_run_attributes(childText, 6, &startOffset, &endOffset);
   1043     g_assert(atkAttributeSetAreEqual(set3, set4));
   1044     g_assert(!atkAttributeSetAreEqual(set1, set3));
   1045     g_assert(!atkAttributeSetAreEqual(set2, set3));
   1046     atk_attribute_set_free(set1);
   1047     atk_attribute_set_free(set2);
   1048     atk_attribute_set_free(set3);
   1049     atk_attribute_set_free(set4);
   1050 
   1051     child = atk_object_ref_accessible_child(object, 3);
   1052     g_assert(child && ATK_IS_TEXT(child));
   1053     childText = ATK_TEXT(child);
   1054     set1 = atk_text_get_run_attributes(childText, 24, &startOffset, &endOffset);
   1055     g_assert_cmpint(startOffset, ==, 21);
   1056     g_assert_cmpint(endOffset, ==, 25);
   1057     g_assert(atkAttributeSetAttributeHasValue(set1, ATK_TEXT_ATTR_STRIKETHROUGH, "true"));
   1058 
   1059     set2 = atk_text_get_run_attributes(childText, 25, &startOffset, &endOffset);
   1060     g_assert_cmpint(startOffset, ==, 25);
   1061     g_assert_cmpint(endOffset, ==, 30);
   1062     g_assert(atkAttributeSetAreEqual(set2, 0));
   1063 
   1064     set3 = atk_text_get_default_attributes(childText);
   1065     g_assert(atkAttributeSetAttributeHasValue(set3, ATK_TEXT_ATTR_JUSTIFICATION, "center"));
   1066     atk_attribute_set_free(set1);
   1067     atk_attribute_set_free(set2);
   1068     atk_attribute_set_free(set3);
   1069 }
   1070 
   1071 static void testWebkitAtkTextSelections()
   1072 {
   1073     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
   1074     g_object_ref_sink(webView);
   1075     GtkAllocation allocation = { 0, 0, 800, 600 };
   1076     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
   1077     webkit_web_view_load_string(webView, textForSelections, 0, 0, 0);
   1078 
   1079     /* Wait for the accessible objects to be created. */
   1080     waitForAccessibleObjects();
   1081 
   1082     AtkObject* object = gtk_widget_get_accessible(GTK_WIDGET(webView));
   1083     g_assert(object);
   1084 
   1085     AtkText* paragraph1 = ATK_TEXT(atk_object_ref_accessible_child(object, 0));
   1086     g_assert(ATK_IS_TEXT(paragraph1));
   1087 
   1088     AtkText* paragraph2 = ATK_TEXT(atk_object_ref_accessible_child(object, 1));
   1089     g_assert(ATK_IS_TEXT(paragraph2));
   1090 
   1091     AtkText* link = ATK_TEXT(atk_object_ref_accessible_child(ATK_OBJECT(paragraph2), 0));
   1092     g_assert(ATK_IS_TEXT(link));
   1093 
   1094     AtkObject* list = atk_object_ref_accessible_child(object, 2);
   1095     g_assert(ATK_OBJECT(list));
   1096 
   1097     AtkText* listItem = ATK_TEXT(atk_object_ref_accessible_child(list, 0));
   1098     g_assert(ATK_IS_TEXT(listItem));
   1099 
   1100     /* First paragraph (simple text). */
   1101 
   1102     /* Basic initial checks. */
   1103     g_assert_cmpint(atk_text_get_n_selections(paragraph1), ==, 0);
   1104 
   1105     gint startOffset;
   1106     gint endOffset;
   1107     gchar* selectedText = atk_text_get_selection(paragraph1, 0, &startOffset, &endOffset);
   1108     g_assert_cmpint(startOffset, ==, 0);
   1109     g_assert_cmpint(endOffset, ==, 0);
   1110     g_assert_cmpstr(selectedText, ==, 0);
   1111     g_free (selectedText);
   1112 
   1113     /* Try removing a non existing (yet) selection. */
   1114     gboolean result = atk_text_remove_selection(paragraph1, 0);
   1115     g_assert(!result);
   1116 
   1117     /* Try setting a 0-char selection. */
   1118     result = atk_text_set_selection(paragraph1, 0, 5, 5);
   1119     g_assert(result);
   1120 
   1121     /* Make a selection and test it. */
   1122     result = atk_text_set_selection(paragraph1, 0, 5, 25);
   1123     g_assert(result);
   1124     g_assert_cmpint(atk_text_get_n_selections(paragraph1), ==, 1);
   1125     selectedText = atk_text_get_selection(paragraph1, 0, &startOffset, &endOffset);
   1126     g_assert_cmpint(startOffset, ==, 5);
   1127     g_assert_cmpint(endOffset, ==, 25);
   1128     g_assert_cmpstr(selectedText, ==, "agraph with plain te");
   1129     g_free (selectedText);
   1130     /* Try removing the selection from other AtkText object (should fail). */
   1131     result = atk_text_remove_selection(paragraph2, 0);
   1132     g_assert(!result);
   1133 
   1134     /* Remove the selection and test everything again. */
   1135     result = atk_text_remove_selection(paragraph1, 0);
   1136     g_assert(result);
   1137     g_assert_cmpint(atk_text_get_n_selections(paragraph1), ==, 0);
   1138     selectedText = atk_text_get_selection(paragraph1, 0, &startOffset, &endOffset);
   1139     /* Now offsets should be the same, set to the last position of the caret. */
   1140     g_assert_cmpint(startOffset, ==, endOffset);
   1141     g_assert_cmpint(startOffset, ==, 25);
   1142     g_assert_cmpint(endOffset, ==, 25);
   1143     g_assert_cmpstr(selectedText, ==, 0);
   1144     g_free (selectedText);
   1145 
   1146     /* Second paragraph (text + link + text). */
   1147 
   1148     /* Set a selection partially covering the link and test it. */
   1149     result = atk_text_set_selection(paragraph2, 0, 7, 21);
   1150     g_assert(result);
   1151 
   1152     /* Test the paragraph first. */
   1153     g_assert_cmpint(atk_text_get_n_selections(paragraph2), ==, 1);
   1154     selectedText = atk_text_get_selection(paragraph2, 0, &startOffset, &endOffset);
   1155     g_assert_cmpint(startOffset, ==, 7);
   1156     g_assert_cmpint(endOffset, ==, 21);
   1157     g_assert_cmpstr(selectedText, ==, "raph with a li");
   1158     g_free (selectedText);
   1159 
   1160     /* Now test just the link. */
   1161     g_assert_cmpint(atk_text_get_n_selections(link), ==, 1);
   1162     selectedText = atk_text_get_selection(link, 0, &startOffset, &endOffset);
   1163     g_assert_cmpint(startOffset, ==, 0);
   1164     g_assert_cmpint(endOffset, ==, 4);
   1165     g_assert_cmpstr(selectedText, ==, "a li");
   1166     g_free (selectedText);
   1167 
   1168     /* Make a selection after the link and check selection for the whole paragraph. */
   1169     result = atk_text_set_selection(paragraph2, 0, 27, 37);
   1170     g_assert(result);
   1171     g_assert_cmpint(atk_text_get_n_selections(paragraph2), ==, 1);
   1172     selectedText = atk_text_get_selection(paragraph2, 0, &startOffset, &endOffset);
   1173     g_assert_cmpint(startOffset, ==, 27);
   1174     g_assert_cmpint(endOffset, ==, 37);
   1175     g_assert_cmpstr(selectedText, ==, "the middle");
   1176     g_free (selectedText);
   1177 
   1178     /* Remove selections and text everything again. */
   1179     result = atk_text_remove_selection(paragraph2, 0);
   1180     g_assert(result);
   1181     g_assert_cmpint(atk_text_get_n_selections(paragraph2), ==, 0);
   1182     selectedText = atk_text_get_selection(paragraph2, 0, &startOffset, &endOffset);
   1183     /* Now offsets should be the same (no selection). */
   1184     g_assert_cmpint(startOffset, ==, endOffset);
   1185     g_assert_cmpstr(selectedText, ==, 0);
   1186     g_free (selectedText);
   1187 
   1188     g_assert_cmpint(atk_text_get_n_selections(link), ==, 0);
   1189     selectedText = atk_text_get_selection(link, 0, &startOffset, &endOffset);
   1190     /* Now offsets should be the same (no selection). */
   1191     g_assert_cmpint(startOffset, ==, endOffset);
   1192     g_assert_cmpstr(selectedText, ==, 0);
   1193     g_free (selectedText);
   1194 
   1195     /* List item */
   1196 
   1197     g_assert(atk_object_get_role(list) == ATK_ROLE_LIST);
   1198     g_assert_cmpint(atk_object_get_n_accessible_children(list), ==, 1);
   1199 
   1200     gchar* text = atk_text_get_text(listItem, 0, -1);
   1201     g_assert_cmpstr(text, ==, "1. A list item");
   1202     g_free (text);
   1203 
   1204     /* It's not possible to select text inside an item's marker. */
   1205     result = atk_text_set_selection (listItem, 0, 0, 9);
   1206     g_assert(!result);
   1207     result = atk_text_set_selection (listItem, 0, 9, 1);
   1208     g_assert(!result);
   1209 
   1210     /* It should be possible to select text inside an item's text. */
   1211     result = atk_text_set_selection (listItem, 0, 3, 9);
   1212     g_assert(result);
   1213 
   1214     g_assert_cmpint(atk_text_get_n_selections(listItem), ==, 1);
   1215     selectedText = atk_text_get_selection(listItem, 0, &startOffset, &endOffset);
   1216     g_assert_cmpint(startOffset, ==, 3);
   1217     g_assert_cmpint(endOffset, ==, 9);
   1218     g_assert_cmpstr(selectedText, ==, "A list");
   1219     g_free (selectedText);
   1220 
   1221     g_object_unref(paragraph1);
   1222     g_object_unref(paragraph2);
   1223     g_object_unref(list);
   1224     g_object_unref(listItem);
   1225     g_object_unref(webView);
   1226 }
   1227 
   1228 static void testWebkitAtkGetExtents()
   1229 {
   1230     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
   1231     g_object_ref_sink(webView);
   1232     GtkAllocation allocation = { 0, 0, 800, 600 };
   1233     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
   1234     webkit_web_view_load_string(webView, centeredContents, 0, 0, 0);
   1235 
   1236     /* Wait for the accessible objects to be created. */
   1237     waitForAccessibleObjects();
   1238 
   1239     AtkObject* object = gtk_widget_get_accessible(GTK_WIDGET(webView));
   1240     g_assert(object);
   1241 
   1242     AtkText* shortText1 = ATK_TEXT(atk_object_ref_accessible_child(object, 0));
   1243     g_assert(ATK_IS_TEXT(shortText1));
   1244     AtkText* longText = ATK_TEXT(atk_object_ref_accessible_child(object, 1));
   1245     g_assert(ATK_IS_TEXT(longText));
   1246     AtkText* shortText2 = ATK_TEXT(atk_object_ref_accessible_child(object, 2));
   1247     g_assert(ATK_IS_TEXT(shortText2));
   1248     AtkText* multilineText = ATK_TEXT(atk_object_ref_accessible_child(object, 3));
   1249     g_assert(ATK_IS_TEXT(multilineText));
   1250 
   1251     /* Start with window extents. */
   1252     AtkTextRectangle sline_window1, sline_window2, lline_window, mline_window;
   1253     atk_text_get_range_extents(shortText1, 0, 10, ATK_XY_WINDOW, &sline_window1);
   1254     atk_text_get_range_extents(longText, 0, 44, ATK_XY_WINDOW, &lline_window);
   1255     atk_text_get_range_extents(shortText2, 0, 10, ATK_XY_WINDOW, &sline_window2);
   1256     atk_text_get_range_extents(multilineText, 0, 60, ATK_XY_WINDOW, &mline_window);
   1257 
   1258     /* Check vertical line position. */
   1259     g_assert_cmpint(sline_window1.y + sline_window1.height, <=, lline_window.y);
   1260     g_assert_cmpint(lline_window.y + lline_window.height + sline_window2.height, <=, mline_window.y);
   1261 
   1262     /* Paragraphs 1 and 3 have identical text and alignment. */
   1263     g_assert_cmpint(sline_window1.x, ==, sline_window2.x);
   1264     g_assert_cmpint(sline_window1.width, ==, sline_window2.width);
   1265     g_assert_cmpint(sline_window1.height, ==, sline_window2.height);
   1266 
   1267     /* All lines should be the same height; line 2 is the widest line. */
   1268     g_assert_cmpint(sline_window1.height, ==, lline_window.height);
   1269     g_assert_cmpint(sline_window1.width, <, lline_window.width);
   1270 
   1271     /* Make sure the character extents jive with the range extents. */
   1272     gint x;
   1273     gint y;
   1274     gint width;
   1275     gint height;
   1276 
   1277     /* First paragraph (short text). */
   1278     atk_text_get_character_extents(shortText1, 0, &x, &y, &width, &height, ATK_XY_WINDOW);
   1279     g_assert_cmpint(x, ==, sline_window1.x);
   1280     g_assert_cmpint(y, ==, sline_window1.y);
   1281     g_assert_cmpint(height, ==, sline_window1.height);
   1282 
   1283     atk_text_get_character_extents(shortText1, 9, &x, &y, &width, &height, ATK_XY_WINDOW);
   1284     g_assert_cmpint(x, ==, sline_window1.x + sline_window1.width - width);
   1285     g_assert_cmpint(y, ==, sline_window1.y);
   1286     g_assert_cmpint(height, ==, sline_window1.height);
   1287 
   1288     /* Second paragraph (long text). */
   1289     atk_text_get_character_extents(longText, 0, &x, &y, &width, &height, ATK_XY_WINDOW);
   1290     g_assert_cmpint(x, ==, lline_window.x);
   1291     g_assert_cmpint(y, ==, lline_window.y);
   1292     g_assert_cmpint(height, ==, lline_window.height);
   1293 
   1294     atk_text_get_character_extents(longText, 43, &x, &y, &width, &height, ATK_XY_WINDOW);
   1295     g_assert_cmpint(x, ==, lline_window.x + lline_window.width - width);
   1296     g_assert_cmpint(y, ==, lline_window.y);
   1297     g_assert_cmpint(height, ==, lline_window.height);
   1298 
   1299     /* Third paragraph (short text). */
   1300     atk_text_get_character_extents(shortText2, 0, &x, &y, &width, &height, ATK_XY_WINDOW);
   1301     g_assert_cmpint(x, ==, sline_window2.x);
   1302     g_assert_cmpint(y, ==, sline_window2.y);
   1303     g_assert_cmpint(height, ==, sline_window2.height);
   1304 
   1305     atk_text_get_character_extents(shortText2, 9, &x, &y, &width, &height, ATK_XY_WINDOW);
   1306     g_assert_cmpint(x, ==, sline_window2.x + sline_window2.width - width);
   1307     g_assert_cmpint(y, ==, sline_window2.y);
   1308     g_assert_cmpint(height, ==, sline_window2.height);
   1309 
   1310     /* Four paragraph (3 lines multi-line text). */
   1311     atk_text_get_character_extents(multilineText, 0, &x, &y, &width, &height, ATK_XY_WINDOW);
   1312     g_assert_cmpint(x, ==, mline_window.x);
   1313     g_assert_cmpint(y, ==, mline_window.y);
   1314     g_assert_cmpint(3 * height, ==, mline_window.height);
   1315 
   1316     atk_text_get_character_extents(multilineText, 59, &x, &y, &width, &height, ATK_XY_WINDOW);
   1317     /* Last line won't fill the whole width of the rectangle. */
   1318     g_assert_cmpint(x, <=, mline_window.x + mline_window.width - width);
   1319     g_assert_cmpint(y, ==, mline_window.y + mline_window.height - height);
   1320     g_assert_cmpint(height, <=, mline_window.height);
   1321 
   1322     /* Check that extent for a full line are the same height than for
   1323        a partial section of the same line */
   1324     gint startOffset;
   1325     gint endOffset;
   1326     gchar* text = atk_text_get_text_at_offset(multilineText, 0, ATK_TEXT_BOUNDARY_LINE_START, &startOffset, &endOffset);
   1327     g_free(text);
   1328 
   1329     AtkTextRectangle fline_window;
   1330     AtkTextRectangle afline_window;
   1331     atk_text_get_range_extents(multilineText, startOffset, endOffset, ATK_XY_WINDOW, &fline_window);
   1332     atk_text_get_range_extents(multilineText, startOffset, endOffset - 1, ATK_XY_WINDOW, &afline_window);
   1333     g_assert_cmpint(fline_window.x, ==, afline_window.x);
   1334     g_assert_cmpint(fline_window.y, ==, afline_window.y);
   1335     g_assert_cmpint(fline_window.height, ==, afline_window.height);
   1336 
   1337     g_object_unref(shortText1);
   1338     g_object_unref(shortText2);
   1339     g_object_unref(longText);
   1340     g_object_unref(multilineText);
   1341     g_object_unref(webView);
   1342 }
   1343 
   1344 static void testWebkitAtkLayoutAndDataTables()
   1345 {
   1346     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
   1347     g_object_ref_sink(webView);
   1348     GtkAllocation allocation = { 0, 0, 800, 600 };
   1349     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
   1350     webkit_web_view_load_string(webView, layoutAndDataTables, 0, 0, 0);
   1351 
   1352     /* Wait for the accessible objects to be created. */
   1353     waitForAccessibleObjects();
   1354 
   1355     AtkObject* object = gtk_widget_get_accessible(GTK_WIDGET(webView));
   1356     g_assert(object);
   1357 
   1358     /* Check the non-layout table (data table). */
   1359 
   1360     AtkObject* table1 = atk_object_ref_accessible_child(object, 0);
   1361     g_assert(ATK_IS_TABLE(table1));
   1362     AtkAttributeSet* set1 = atk_object_get_attributes(table1);
   1363     g_assert(set1);
   1364     g_assert(!atkAttributeSetContainsAttributeName(set1, "layout-guess"));
   1365     atk_attribute_set_free(set1);
   1366 
   1367     /* Check the layout table. */
   1368 
   1369     AtkObject* table2 = atk_object_ref_accessible_child(object, 1);
   1370     g_assert(ATK_IS_TABLE(table2));
   1371     AtkAttributeSet* set2 = atk_object_get_attributes(table2);
   1372     g_assert(set2);
   1373     g_assert(atkAttributeSetContainsAttributeName(set2, "layout-guess"));
   1374     g_assert(atkAttributeSetAttributeNameHasValue(set2, "layout-guess", "true"));
   1375     atk_attribute_set_free(set2);
   1376 
   1377     g_object_unref(table1);
   1378     g_object_unref(table2);
   1379     g_object_unref(webView);
   1380 }
   1381 
   1382 static void testWebkitAtkLinksWithInlineImages()
   1383 {
   1384     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
   1385     g_object_ref_sink(webView);
   1386     GtkAllocation allocation = { 0, 0, 800, 600 };
   1387     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
   1388     webkit_web_view_load_string(webView, linksWithInlineImages, 0, 0, 0);
   1389 
   1390     /* Wait for the accessible objects to be created. */
   1391     waitForAccessibleObjects();
   1392 
   1393     AtkObject* object = gtk_widget_get_accessible(GTK_WIDGET(webView));
   1394     g_assert(object);
   1395 
   1396     /* First paragraph (link at the beginning). */
   1397     AtkObject* paragraph = atk_object_ref_accessible_child(object, 0);
   1398     g_assert(ATK_IS_TEXT(paragraph));
   1399     gint startOffset;
   1400     gint endOffset;
   1401     gchar* text = atk_text_get_text_at_offset(ATK_TEXT(paragraph), 0, ATK_TEXT_BOUNDARY_LINE_START, &startOffset, &endOffset);
   1402     g_assert(text);
   1403     g_assert_cmpstr(text, ==, "foo bar baz");
   1404     g_assert_cmpint(startOffset, ==, 0);
   1405     g_assert_cmpint(endOffset, ==, 11);
   1406     g_free(text);
   1407     g_object_unref(paragraph);
   1408 
   1409     /* Second paragraph (link in the middle). */
   1410     paragraph = atk_object_ref_accessible_child(object, 1);
   1411     g_assert(ATK_IS_TEXT(paragraph));
   1412     text = atk_text_get_text_at_offset(ATK_TEXT(paragraph), 0, ATK_TEXT_BOUNDARY_LINE_START, &startOffset, &endOffset);
   1413     g_assert(text);
   1414     g_assert_cmpstr(text, ==, "foo bar baz");
   1415     g_assert_cmpint(startOffset, ==, 0);
   1416     g_assert_cmpint(endOffset, ==, 11);
   1417     g_free(text);
   1418     g_object_unref(paragraph);
   1419 
   1420     /* Third paragraph (link at the end). */
   1421     paragraph = atk_object_ref_accessible_child(object, 2);
   1422     g_assert(ATK_IS_TEXT(paragraph));
   1423     text = atk_text_get_text_at_offset(ATK_TEXT(paragraph), 0, ATK_TEXT_BOUNDARY_LINE_START, &startOffset, &endOffset);
   1424     g_assert(text);
   1425     g_assert_cmpstr(text, ==, "foo bar baz");
   1426     g_assert_cmpint(startOffset, ==, 0);
   1427     g_assert_cmpint(endOffset, ==, 11);
   1428     g_free(text);
   1429     g_object_unref(paragraph);
   1430 
   1431     g_object_unref(webView);
   1432 }
   1433 
   1434 static void testWebkitAtkHypertextAndHyperlinks()
   1435 {
   1436     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
   1437     g_object_ref_sink(webView);
   1438     GtkAllocation allocation = { 0, 0, 800, 600 };
   1439     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
   1440     webkit_web_view_load_string(webView, hypertextAndHyperlinks, 0, 0, 0);
   1441 
   1442     /* Wait for the accessible objects to be created. */
   1443     waitForAccessibleObjects();
   1444 
   1445     AtkObject* object = gtk_widget_get_accessible(GTK_WIDGET(webView));
   1446     g_assert(object);
   1447 
   1448     AtkObject* paragraph1 = atk_object_ref_accessible_child(object, 0);
   1449     g_assert(ATK_OBJECT(paragraph1));
   1450     g_assert(atk_object_get_role(paragraph1) == ATK_ROLE_PARAGRAPH);
   1451     g_assert(ATK_IS_HYPERTEXT(paragraph1));
   1452 
   1453     /* No links in the first paragraph. */
   1454     gint nLinks = atk_hypertext_get_n_links(ATK_HYPERTEXT(paragraph1));
   1455     g_assert_cmpint(nLinks, ==, 0);
   1456 
   1457     AtkObject* paragraph2 = atk_object_ref_accessible_child(object, 1);
   1458     g_assert(ATK_OBJECT(paragraph2));
   1459     g_assert(atk_object_get_role(paragraph2) == ATK_ROLE_PARAGRAPH);
   1460     g_assert(ATK_IS_HYPERTEXT(paragraph2));
   1461 
   1462     /* Check links in the second paragraph.
   1463        nLinks = atk_hypertext_get_n_links(ATK_HYPERTEXT(paragraph2));
   1464        g_assert_cmpint(nLinks, ==, 3); */
   1465 
   1466     AtkHyperlink* hLink1 = atk_hypertext_get_link(ATK_HYPERTEXT(paragraph2), 0);
   1467     g_assert(ATK_HYPERLINK(hLink1));
   1468     AtkObject* hLinkObject1 = atk_hyperlink_get_object(hLink1, 0);
   1469     g_assert(ATK_OBJECT(hLinkObject1));
   1470     g_assert(atk_object_get_role(hLinkObject1) == ATK_ROLE_LINK);
   1471     g_assert_cmpint(atk_hyperlink_get_start_index(hLink1), ==, 0);
   1472     g_assert_cmpint(atk_hyperlink_get_end_index(hLink1), ==, 6);
   1473     g_assert_cmpint(atk_hyperlink_get_n_anchors(hLink1), ==, 1);
   1474     g_assert_cmpstr(atk_hyperlink_get_uri(hLink1, 0), ==, "http://foo.bar.baz/");
   1475 
   1476     AtkHyperlink* hLink2 = atk_hypertext_get_link(ATK_HYPERTEXT(paragraph2), 1);
   1477     g_assert(ATK_HYPERLINK(hLink2));
   1478     AtkObject* hLinkObject2 = atk_hyperlink_get_object(hLink2, 0);
   1479     g_assert(ATK_OBJECT(hLinkObject2));
   1480     g_assert(atk_object_get_role(hLinkObject2) == ATK_ROLE_LINK);
   1481     g_assert_cmpint(atk_hyperlink_get_start_index(hLink2), ==, 12);
   1482     g_assert_cmpint(atk_hyperlink_get_end_index(hLink2), ==, 32);
   1483     g_assert_cmpint(atk_hyperlink_get_n_anchors(hLink2), ==, 1);
   1484     g_assert_cmpstr(atk_hyperlink_get_uri(hLink2, 0), ==, "http://bar.baz.foo/");
   1485 
   1486     AtkHyperlink* hLink3 = atk_hypertext_get_link(ATK_HYPERTEXT(paragraph2), 2);
   1487     g_assert(ATK_HYPERLINK(hLink3));
   1488     AtkObject* hLinkObject3 = atk_hyperlink_get_object(hLink3, 0);
   1489     g_assert(ATK_OBJECT(hLinkObject3));
   1490     g_assert(atk_object_get_role(hLinkObject3) == ATK_ROLE_LINK);
   1491     g_assert_cmpint(atk_hyperlink_get_start_index(hLink3), ==, 65);
   1492     g_assert_cmpint(atk_hyperlink_get_end_index(hLink3), ==, 75);
   1493     g_assert_cmpint(atk_hyperlink_get_n_anchors(hLink3), ==, 1);
   1494     g_assert_cmpstr(atk_hyperlink_get_uri(hLink3, 0), ==, "http://baz.foo.bar/");
   1495 
   1496     AtkObject* list = atk_object_ref_accessible_child(object, 2);
   1497     g_assert(ATK_OBJECT(list));
   1498     g_assert(atk_object_get_role(list) == ATK_ROLE_LIST);
   1499     g_assert_cmpint(atk_object_get_n_accessible_children(list), ==, 1);
   1500 
   1501     AtkObject* listItem = atk_object_ref_accessible_child(list, 0);
   1502     g_assert(ATK_IS_TEXT(listItem));
   1503     g_assert(ATK_IS_HYPERTEXT(listItem));
   1504 
   1505     AtkHyperlink* hLinkInListItem = atk_hypertext_get_link(ATK_HYPERTEXT(listItem), 0);
   1506     g_assert(ATK_HYPERLINK(hLinkInListItem));
   1507     AtkObject* hLinkObject = atk_hyperlink_get_object(hLinkInListItem, 0);
   1508     g_assert(ATK_OBJECT(hLinkObject));
   1509     g_assert(atk_object_get_role(hLinkObject) == ATK_ROLE_LINK);
   1510     g_assert_cmpint(atk_hyperlink_get_start_index(hLinkInListItem), ==, 20);
   1511     g_assert_cmpint(atk_hyperlink_get_end_index(hLinkInListItem), ==, 43);
   1512     g_assert_cmpint(atk_hyperlink_get_n_anchors(hLinkInListItem), ==, 1);
   1513     g_assert_cmpstr(atk_hyperlink_get_uri(hLinkInListItem, 0), ==, "http://foo.bar.baz/");
   1514 
   1515     /* Finally check the AtkAction interface for a given AtkHyperlink. */
   1516     g_assert(ATK_IS_ACTION(hLink1));
   1517     g_assert_cmpint(atk_action_get_n_actions(ATK_ACTION(hLink1)), ==, 1);
   1518     g_assert_cmpstr(atk_action_get_keybinding(ATK_ACTION(hLink1), 0), ==, "");
   1519     g_assert_cmpstr(atk_action_get_name(ATK_ACTION(hLink1), 0), ==, "jump");
   1520     g_assert(atk_action_do_action(ATK_ACTION(hLink1), 0));
   1521 
   1522     g_object_unref(paragraph1);
   1523     g_object_unref(paragraph2);
   1524     g_object_unref(list);
   1525     g_object_unref(listItem);
   1526     g_object_unref(webView);
   1527 }
   1528 
   1529 static void testWebkitAtkListsOfItems()
   1530 {
   1531     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
   1532     g_object_ref_sink(webView);
   1533     GtkAllocation allocation = { 0, 0, 800, 600 };
   1534     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
   1535     webkit_web_view_load_string(webView, listsOfItems, 0, 0, 0);
   1536 
   1537     /* Wait for the accessible objects to be created. */
   1538     waitForAccessibleObjects();
   1539 
   1540     AtkObject* object = gtk_widget_get_accessible(GTK_WIDGET(webView));
   1541     g_assert(object);
   1542 
   1543     /* Unordered list. */
   1544 
   1545     AtkObject* uList = atk_object_ref_accessible_child(object, 0);
   1546     g_assert(ATK_OBJECT(uList));
   1547     g_assert(atk_object_get_role(uList) == ATK_ROLE_LIST);
   1548     g_assert_cmpint(atk_object_get_n_accessible_children(uList), ==, 3);
   1549 
   1550     AtkObject* item1 = atk_object_ref_accessible_child(uList, 0);
   1551     g_assert(ATK_IS_TEXT(item1));
   1552     AtkObject* item2 = atk_object_ref_accessible_child(uList, 1);
   1553     g_assert(ATK_IS_TEXT(item2));
   1554     AtkObject* item3 = atk_object_ref_accessible_child(uList, 2);
   1555     g_assert(ATK_IS_TEXT(item3));
   1556 
   1557     g_assert_cmpint(atk_object_get_n_accessible_children(item1), ==, 0);
   1558     g_assert_cmpint(atk_object_get_n_accessible_children(item2), ==, 1);
   1559     g_assert_cmpint(atk_object_get_n_accessible_children(item3), ==, 1);
   1560 
   1561     g_assert_cmpstr(atk_text_get_text(ATK_TEXT(item1), 0, -1), ==, "\342\200\242 text only");
   1562     g_assert_cmpstr(atk_text_get_text(ATK_TEXT(item2), 0, -1), ==, "\342\200\242 link only");
   1563     g_assert_cmpstr(atk_text_get_text(ATK_TEXT(item3), 0, -1), ==, "\342\200\242 text and a link");
   1564 
   1565     g_object_unref(item1);
   1566     g_object_unref(item2);
   1567     g_object_unref(item3);
   1568 
   1569     /* Ordered list. */
   1570 
   1571     AtkObject* oList = atk_object_ref_accessible_child(object, 1);
   1572     g_assert(ATK_OBJECT(oList));
   1573     g_assert(atk_object_get_role(oList) == ATK_ROLE_LIST);
   1574     g_assert_cmpint(atk_object_get_n_accessible_children(oList), ==, 3);
   1575 
   1576     item1 = atk_object_ref_accessible_child(oList, 0);
   1577     g_assert(ATK_IS_TEXT(item1));
   1578     item2 = atk_object_ref_accessible_child(oList, 1);
   1579     g_assert(ATK_IS_TEXT(item2));
   1580     item3 = atk_object_ref_accessible_child(oList, 2);
   1581     g_assert(ATK_IS_TEXT(item3));
   1582 
   1583     g_assert_cmpstr(atk_text_get_text(ATK_TEXT(item1), 0, -1), ==, "1. text only");
   1584     g_assert_cmpstr(atk_text_get_text(ATK_TEXT(item2), 0, -1), ==, "2. link only");
   1585     g_assert_cmpstr(atk_text_get_text(ATK_TEXT(item3), 0, -1), ==, "3. text and a link");
   1586 
   1587     g_assert_cmpint(atk_object_get_n_accessible_children(item1), ==, 0);
   1588     g_assert_cmpint(atk_object_get_n_accessible_children(item2), ==, 1);
   1589     g_assert_cmpint(atk_object_get_n_accessible_children(item3), ==, 1);
   1590 
   1591     g_object_unref(item1);
   1592     g_object_unref(item2);
   1593     g_object_unref(item3);
   1594 
   1595     g_object_unref(uList);
   1596     g_object_unref(oList);
   1597     g_object_unref(webView);
   1598 }
   1599 
   1600 static gboolean textInserted = FALSE;
   1601 static gboolean textDeleted = FALSE;
   1602 
   1603 static void textChangedCb(AtkText* text, gint pos, gint len, const gchar* detail)
   1604 {
   1605     g_assert(text && ATK_IS_OBJECT(text));
   1606 
   1607     if (!g_strcmp0(detail, "insert"))
   1608         textInserted = TRUE;
   1609     else if (!g_strcmp0(detail, "delete"))
   1610         textDeleted = TRUE;
   1611 }
   1612 
   1613 static gboolean checkTextChanges(gpointer unused)
   1614 {
   1615     g_assert_cmpint(textInserted, ==, TRUE);
   1616     g_assert_cmpint(textDeleted, ==, TRUE);
   1617     return FALSE;
   1618 }
   1619 
   1620 static void testWebkitAtkTextChangedNotifications()
   1621 {
   1622     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
   1623     g_object_ref_sink(webView);
   1624     GtkAllocation allocation = { 0, 0, 800, 600 };
   1625     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
   1626     webkit_web_view_load_string(webView, formWithTextInputs, 0, 0, 0);
   1627 
   1628     /* Wait for the accessible objects to be created. */
   1629     waitForAccessibleObjects();
   1630 
   1631     AtkObject* object = gtk_widget_get_accessible(GTK_WIDGET(webView));
   1632     g_assert(object);
   1633 
   1634     AtkObject* form = atk_object_ref_accessible_child(object, 0);
   1635     g_assert(ATK_IS_OBJECT(form));
   1636 
   1637     AtkObject* textEntry = atk_object_ref_accessible_child(form, 0);
   1638     g_assert(ATK_IS_EDITABLE_TEXT(textEntry));
   1639     g_assert(atk_object_get_role(ATK_OBJECT(textEntry)) == ATK_ROLE_ENTRY);
   1640 
   1641     g_signal_connect(textEntry, "text-changed::insert",
   1642                      G_CALLBACK(textChangedCb),
   1643                      (gpointer)"insert");
   1644     g_signal_connect(textEntry, "text-changed::delete",
   1645                      G_CALLBACK(textChangedCb),
   1646                      (gpointer)"delete");
   1647 
   1648     gint pos = 0;
   1649     atk_editable_text_insert_text(ATK_EDITABLE_TEXT(textEntry), "foo bar baz", 11, &pos);
   1650     atk_editable_text_delete_text(ATK_EDITABLE_TEXT(textEntry), 4, 7);
   1651     textInserted = FALSE;
   1652     textDeleted = FALSE;
   1653 
   1654     g_idle_add((GSourceFunc)checkTextChanges, 0);
   1655 
   1656     g_object_unref(form);
   1657     g_object_unref(textEntry);
   1658     g_object_unref(webView);
   1659 }
   1660 
   1661 int main(int argc, char** argv)
   1662 {
   1663     g_thread_init(0);
   1664     gtk_test_init(&argc, &argv, 0);
   1665 
   1666     g_test_bug_base("https://bugs.webkit.org/");
   1667     g_test_add_func("/webkit/atk/caretOffsets", testWebkitAtkCaretOffsets);
   1668     g_test_add_func("/webkit/atk/caretOffsetsAndExtranousWhiteSpaces", testWebkitAtkCaretOffsetsAndExtranousWhiteSpaces);
   1669     g_test_add_func("/webkit/atk/comboBox", testWebkitAtkComboBox);
   1670     g_test_add_func("/webkit/atk/embeddedObjects", testWebkitAtkEmbeddedObjects);
   1671     g_test_add_func("/webkit/atk/getTextAtOffset", testWebkitAtkGetTextAtOffset);
   1672     g_test_add_func("/webkit/atk/getTextAtOffsetForms", testWebkitAtkGetTextAtOffsetForms);
   1673     g_test_add_func("/webkit/atk/getTextAtOffsetNewlines", testWebkitAtkGetTextAtOffsetNewlines);
   1674     g_test_add_func("/webkit/atk/getTextAtOffsetTextarea", testWebkitAtkGetTextAtOffsetTextarea);
   1675     g_test_add_func("/webkit/atk/getTextAtOffsetTextInput", testWebkitAtkGetTextAtOffsetTextInput);
   1676     g_test_add_func("/webkit/atk/getTextAtOffsetWithSpecialCharacters", testWebkitAtkGetTextAtOffsetWithSpecialCharacters);
   1677     g_test_add_func("/webkit/atk/getTextInParagraphAndBodySimple", testWebkitAtkGetTextInParagraphAndBodySimple);
   1678     g_test_add_func("/webkit/atk/getTextInParagraphAndBodyModerate", testWebkitAtkGetTextInParagraphAndBodyModerate);
   1679     g_test_add_func("/webkit/atk/getTextInTable", testWebkitAtkGetTextInTable);
   1680     g_test_add_func("/webkit/atk/getHeadersInTable", testWebkitAtkGetHeadersInTable);
   1681     g_test_add_func("/webkit/atk/textAttributes", testWebkitAtkTextAttributes);
   1682     g_test_add_func("/webkit/atk/textSelections", testWebkitAtkTextSelections);
   1683     g_test_add_func("/webkit/atk/getExtents", testWebkitAtkGetExtents);
   1684     g_test_add_func("/webkit/atk/hypertextAndHyperlinks", testWebkitAtkHypertextAndHyperlinks);
   1685     g_test_add_func("/webkit/atk/layoutAndDataTables", testWebkitAtkLayoutAndDataTables);
   1686     g_test_add_func("/webkit/atk/linksWithInlineImages", testWebkitAtkLinksWithInlineImages);
   1687     g_test_add_func("/webkit/atk/listsOfItems", testWebkitAtkListsOfItems);
   1688     g_test_add_func("/webkit/atk/textChangedNotifications", testWebkitAtkTextChangedNotifications);
   1689     return g_test_run ();
   1690 }
   1691 
   1692 #else
   1693 int main(int argc, char** argv)
   1694 {
   1695     g_critical("You will need gtk-2.14.0 to run the unit tests. Doing nothing now.");
   1696     return 0;
   1697 }
   1698 
   1699 #endif
   1700