AX: [ATK] Convert the get_text atktest.c unit tests to layout tests
[WebKit-https.git] / Source / WebKit / gtk / tests / testatk.c
1 /*
2  * Copyright (C) 2009 Igalia S.L.
3  * Copyright (C) 2013 Samsung Electronics. All rights reserved.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public License
16  * along with this library; see the file COPYING.LIB.  If not, write to
17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 #include "autotoolsconfig.h"
22 #include <errno.h>
23 #include <glib.h>
24 #include <glib/gstdio.h>
25 #include <gtk/gtk.h>
26 #include <locale.h>
27 #include <unistd.h>
28 #include <webkit/webkit.h>
29
30 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>";
31
32 static const char* contents = "<html><body><p>This is a test. This is the second sentence. And this the third.</p></body></html>";
33
34 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>";
35
36 static const char* contentsWithExtraneousWhiteSpaces = "<html><head><body><p>This\n                          paragraph\n                                                      is\n                                                                                                                                                                                                                                                                                                                                                                            borked!</p></body></html>";
37
38 static const char* comboBoxSelector = "<html><body><select><option selected value='foo'>foo</option><option value='bar'>bar</option></select></body></html>";
39
40 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>";
41
42 static const char* formWithTextInputs = "<html><body><form><input type='text' name='entry' /><input type='password' name='passwordEntry' /></form></body></html>";
43
44 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>";
45
46 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>";
47
48 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>";
49
50 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>";
51
52 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><li><span style='display:block;'>Block span in a list item</span><span>Inline span in a list item</span></li><li><a href='foo'><span style='display:block;'>Block span in a link in a list item</span><span>Inline span in a link in a list item</span></a></li></ol><select><option selected value='foo'>An option in a combo box</option></select><input type='text' name='foo' value='foo bar baz' /><table><tr><td>a table cell</td><td></td><td><a href='foo'><span style='display:block;'>Block span in a link in a table cell</span><span>Inline span in a link in a table cell</span></a></td><td><span style='display:block;'>Block span in a table cell</span><span>Inline span in a table cell</span></td></tr></table><h4><a href='foo'><span style='display:block;'>Block span in a link in a heading</span><span>Inline span in a link in a heading</span></h4><h4><span style='display:block;'>Block span in a heading</span><span>Inline span in a heading</span></h4></body></html>";
53
54 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>";
55
56 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>";
57
58 static AtkObject* getWebAreaObject(WebKitWebView* webView)
59 {
60     /* Manually spin the main context to make sure the accessible
61        objects are properly created before continuing. */
62     while (g_main_context_pending(0))
63         g_main_context_iteration(0, TRUE);
64
65     AtkObject* rootObject = gtk_widget_get_accessible(GTK_WIDGET(webView));
66     if (!rootObject)
67         return NULL;
68
69     AtkObject* webAreaObject = atk_object_ref_accessible_child(rootObject, 0);
70     if (!webAreaObject)
71         return NULL;
72
73     /* We don't need the extra ref here. */
74     g_object_unref(webAreaObject);
75
76     return webAreaObject;
77 }
78
79 static gchar* textCaretMovedResult = 0;
80
81 static void textCaretMovedCallback(AtkText* text, gint pos, gpointer data)
82 {
83     g_assert(ATK_IS_TEXT(text));
84
85     g_free(textCaretMovedResult);
86     AtkRole role = atk_object_get_role(ATK_OBJECT(text));
87     textCaretMovedResult = g_strdup_printf("|%s|%d|", atk_role_get_name(role), pos);
88 }
89
90 static void testWebkitAtkCaretOffsets()
91 {
92     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
93     g_object_ref_sink(webView);
94     GtkAllocation allocation = { 0, 0, 800, 600 };
95     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
96     webkit_web_view_load_string(webView, textForCaretBrowsing, 0, 0, 0);
97
98     AtkObject* object = getWebAreaObject(webView);
99     g_assert(object);
100
101     AtkObject* header = atk_object_ref_accessible_child(object, 0);
102     g_assert(ATK_IS_TEXT(header));
103     g_signal_connect(header, "text-caret-moved", G_CALLBACK(textCaretMovedCallback), 0);
104
105     /* It should be possible to place the caret inside a header. */
106     gboolean result = atk_text_set_caret_offset(ATK_TEXT(header), 5);
107     g_assert_cmpint(result, ==, TRUE);
108     gint offset = atk_text_get_caret_offset(ATK_TEXT(header));
109     g_assert_cmpint(offset, ==, 5);
110     g_assert_cmpstr(textCaretMovedResult, ==, "|heading|5|");
111
112     AtkObject* paragraph = atk_object_ref_accessible_child(object, 1);
113     g_assert(ATK_IS_TEXT(paragraph));
114     g_signal_connect(paragraph, "text-caret-moved", G_CALLBACK(textCaretMovedCallback), 0);
115
116     /* It should be possible to place the caret inside a paragraph and a link. */
117     result = atk_text_set_caret_offset(ATK_TEXT(paragraph), 5);
118     g_assert_cmpint(result, ==, TRUE);
119     offset = atk_text_get_caret_offset(ATK_TEXT(paragraph));
120     g_assert_cmpint(offset, ==, 5);
121     g_assert_cmpstr(textCaretMovedResult, ==, "|paragraph|5|");
122
123     result = atk_text_set_caret_offset(ATK_TEXT(paragraph), 20);
124     g_assert_cmpint(result, ==, TRUE);
125     offset = atk_text_get_caret_offset(ATK_TEXT(paragraph));
126     g_assert_cmpint(offset, ==, 20);
127     g_assert_cmpstr(textCaretMovedResult, ==, "|paragraph|20|");
128
129     result = atk_text_set_caret_offset(ATK_TEXT(paragraph), 30);
130     g_assert_cmpint(result, ==, TRUE);
131     offset = atk_text_get_caret_offset(ATK_TEXT(paragraph));
132     g_assert_cmpint(offset, ==, 30);
133     g_assert_cmpstr(textCaretMovedResult, ==, "|paragraph|30|");
134
135     AtkObject* link = atk_object_ref_accessible_child(paragraph, 0);
136     g_assert(ATK_IS_TEXT(link));
137
138     result = atk_text_set_caret_offset(ATK_TEXT(link), 5);
139     g_assert_cmpint(result, ==, TRUE);
140     offset = atk_text_get_caret_offset(ATK_TEXT(link));
141     g_assert_cmpint(offset, ==, 5);
142     /* Positions inside links are reported relative to the paragraph. */
143     g_assert_cmpstr(textCaretMovedResult, ==, "|paragraph|17|");
144
145     AtkObject* list = atk_object_ref_accessible_child(object, 2);
146     g_assert(ATK_OBJECT(list));
147     g_assert(atk_object_get_role(list) == ATK_ROLE_LIST);
148     g_assert_cmpint(atk_object_get_n_accessible_children(list), ==, 3);
149
150     AtkObject* listItem = atk_object_ref_accessible_child(list, 0);
151     listItem = atk_object_ref_accessible_child(list, 2);
152     g_assert(ATK_IS_TEXT(listItem));
153
154     /* It's not possible to place the caret inside an item's marker. */
155     result = atk_text_set_caret_offset(ATK_TEXT(listItem), 1);
156     g_assert_cmpint(result, ==, FALSE);
157
158     /* It should be possible to place the caret inside an item's text. */
159     result = atk_text_set_caret_offset(ATK_TEXT(listItem), 5);
160     g_assert_cmpint(result, ==, TRUE);
161     offset = atk_text_get_caret_offset(ATK_TEXT(listItem));
162     g_assert_cmpint(offset, ==, 5);
163
164     AtkObject* panel = atk_object_ref_accessible_child(object, 3);
165     g_assert(ATK_IS_OBJECT(panel));
166     g_assert(atk_object_get_role(panel) == ATK_ROLE_PANEL);
167
168     AtkObject* comboBox = atk_object_ref_accessible_child(panel, 0);
169     g_assert(ATK_IS_OBJECT(comboBox));
170     g_assert(atk_object_get_role(comboBox) == ATK_ROLE_COMBO_BOX);
171
172     AtkObject* menuPopup = atk_object_ref_accessible_child(comboBox, 0);
173     g_assert(ATK_IS_OBJECT(menuPopup));
174     g_assert(atk_object_get_role(menuPopup) == ATK_ROLE_MENU);
175
176     AtkObject* comboBoxOption = atk_object_ref_accessible_child(menuPopup, 0);
177     g_assert(ATK_IS_OBJECT(comboBoxOption));
178     g_assert(atk_object_get_role(comboBoxOption) == ATK_ROLE_MENU_ITEM);
179
180     /* It's not possible to place the caret inside an option for a combobox. */
181     result = atk_text_set_caret_offset(ATK_TEXT(comboBoxOption), 1);
182     g_assert_cmpint(result, ==, FALSE);
183
184     AtkObject* textEntry = atk_object_ref_accessible_child(panel, 1);
185     g_assert(ATK_IS_OBJECT(textEntry));
186     g_assert(atk_object_get_role(textEntry) == ATK_ROLE_ENTRY);
187
188     result = atk_text_set_caret_offset(ATK_TEXT(textEntry), 5);
189     g_assert_cmpint(result, ==, TRUE);
190     offset = atk_text_get_caret_offset(ATK_TEXT(textEntry));
191     g_assert_cmpint(offset, ==, 5);
192
193     AtkObject* table = atk_object_ref_accessible_child(object, 4);
194     g_assert(ATK_IS_OBJECT(table));
195     g_assert(atk_object_get_role(table) == ATK_ROLE_TABLE);
196     g_assert_cmpint(atk_object_get_n_accessible_children(table), ==, 4);
197
198     AtkObject* tableCell = atk_object_ref_accessible_child(table, 0);
199     g_assert(atk_object_get_role(tableCell) == ATK_ROLE_TABLE_CELL);
200     result = atk_text_set_caret_offset(ATK_TEXT(tableCell), 2);
201     g_assert_cmpint(result, ==, TRUE);
202     offset = atk_text_get_caret_offset(ATK_TEXT(tableCell));
203     g_assert_cmpint(offset, ==, 2);
204
205     g_free(textCaretMovedResult);
206
207     g_object_unref(paragraph);
208     g_object_unref(link);
209     g_object_unref(list);
210     g_object_unref(listItem);
211     g_object_unref(panel);
212     g_object_unref(comboBox);
213     g_object_unref(menuPopup);
214     g_object_unref(comboBoxOption);
215     g_object_unref(textEntry);
216     g_object_unref(table);
217     g_object_unref(tableCell);
218     g_object_unref(webView);
219 }
220
221 static void testWebkitAtkCaretOffsetsAndExtranousWhiteSpaces()
222 {
223     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
224     g_object_ref_sink(webView);
225     GtkAllocation allocation = { 0, 0, 800, 600 };
226     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
227     webkit_web_view_load_string(webView, contentsWithExtraneousWhiteSpaces, 0, 0, 0);
228
229     /* Enable caret browsing. */
230     WebKitWebSettings* settings = webkit_web_view_get_settings(webView);
231     g_object_set(G_OBJECT(settings), "enable-caret-browsing", TRUE, NULL);
232
233     /* Get to the inner AtkText object. */
234     AtkObject* object = getWebAreaObject(webView);
235     g_assert(object);
236     object = atk_object_ref_accessible_child(object, 0);
237     g_assert(object);
238
239     AtkText* textObject = ATK_TEXT(object);
240     g_assert(ATK_IS_TEXT(textObject));
241
242     gint characterCount = atk_text_get_character_count(textObject);
243     g_assert_cmpint(characterCount, ==, 25);
244
245     gboolean result = atk_text_set_caret_offset(textObject, characterCount - 1);
246     g_assert_cmpint(result, ==, TRUE);
247
248     gint caretOffset = atk_text_get_caret_offset(textObject);
249     g_assert_cmpint(caretOffset, ==, characterCount - 1);
250
251     result = atk_text_set_caret_offset(textObject, characterCount);
252     g_assert_cmpint(result, ==, TRUE);
253
254     caretOffset = atk_text_get_caret_offset(textObject);
255     g_assert_cmpint(caretOffset, ==, characterCount);
256
257     g_object_unref(object);
258     g_object_unref(webView);
259 }
260
261 static void testWebkitAtkComboBox()
262 {
263     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
264     g_object_ref_sink(webView);
265     GtkAllocation allocation = { 0, 0, 800, 600 };
266     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
267     webkit_web_view_load_string(webView, comboBoxSelector, 0, 0, 0);
268
269     AtkObject* object = getWebAreaObject(webView);
270     g_assert(object);
271
272     AtkObject* formObject = atk_object_ref_accessible_child(object, 0);
273     g_assert(formObject);
274
275     AtkObject* comboBox = atk_object_ref_accessible_child(formObject, 0);
276     g_assert(ATK_IS_OBJECT(comboBox));
277
278     AtkObject* menuPopup = atk_object_ref_accessible_child(comboBox, 0);
279     g_assert(ATK_IS_OBJECT(menuPopup));
280
281     AtkObject* item1 = atk_object_ref_accessible_child(menuPopup, 0);
282     g_assert(ATK_IS_OBJECT(item1));
283
284     AtkObject* item2 = atk_object_ref_accessible_child(menuPopup, 1);
285     g_assert(ATK_IS_OBJECT(item2));
286
287     /* Check roles. */
288     g_assert(atk_object_get_role(comboBox) == ATK_ROLE_COMBO_BOX);
289     g_assert(atk_object_get_role(menuPopup) == ATK_ROLE_MENU);
290     g_assert(atk_object_get_role(item1) == ATK_ROLE_MENU_ITEM);
291     g_assert(atk_object_get_role(item2) == ATK_ROLE_MENU_ITEM);
292
293     /* Check the implementation of the AtkSelection interface. */
294     g_assert(ATK_IS_SELECTION(comboBox));
295     AtkSelection* atkSelection = ATK_SELECTION(comboBox);
296     g_assert_cmpint(atk_selection_get_selection_count(atkSelection), ==, 1);
297     g_assert(atk_selection_is_child_selected(atkSelection, 0));
298     g_assert(!atk_selection_is_child_selected(atkSelection, 1));
299     AtkObject* selectedItem = atk_selection_ref_selection(atkSelection, 0);
300     g_assert(selectedItem == item1);
301     g_object_unref(selectedItem);
302
303     /* Check that the menu popup has 0 links and doesn't crash from checking. */
304     gint nLinks = atk_hypertext_get_n_links(ATK_HYPERTEXT(menuPopup));
305     g_assert_cmpint(nLinks, ==, 0);
306
307     /* Check the implementations of the AtkAction interface. */
308     g_assert(ATK_IS_ACTION(comboBox));
309     AtkAction* atkAction = ATK_ACTION(comboBox);
310     g_assert_cmpint(atk_action_get_n_actions(atkAction), ==, 1);
311     g_assert(atk_action_do_action(atkAction, 0));
312
313     g_assert(ATK_IS_ACTION(menuPopup));
314     atkAction = ATK_ACTION(menuPopup);
315     g_assert_cmpint(atk_action_get_n_actions(atkAction), ==, 1);
316     g_assert(atk_action_do_action(atkAction, 0));
317
318     g_assert(ATK_IS_ACTION(item1));
319     atkAction = ATK_ACTION(item1);
320     g_assert_cmpint(atk_action_get_n_actions(atkAction), ==, 1);
321     g_assert(atk_action_do_action(atkAction, 0));
322
323     g_assert(ATK_IS_ACTION(item2));
324     atkAction = ATK_ACTION(item2);
325     g_assert_cmpint(atk_action_get_n_actions(atkAction), ==, 1);
326     g_assert(atk_action_do_action(atkAction, 0));
327
328     /* After selecting the second item, selection should have changed. */
329     g_assert_cmpint(atk_selection_get_selection_count(atkSelection), ==, 1);
330     g_assert(!atk_selection_is_child_selected(atkSelection, 0));
331     g_assert(atk_selection_is_child_selected(atkSelection, 1));
332     selectedItem = atk_selection_ref_selection(atkSelection, 0);
333     g_assert(selectedItem == item2);
334     g_object_unref(selectedItem);
335
336     g_object_unref(formObject);
337     g_object_unref(comboBox);
338     g_object_unref(menuPopup);
339     g_object_unref(item1);
340     g_object_unref(item2);
341     g_object_unref(webView);
342 }
343
344 static gchar* loadingEventsResult = 0;
345
346 static void updateLoadingEventsResult(const gchar* signalName)
347 {
348     g_assert(signalName);
349
350     gchar* previousResult = loadingEventsResult;
351     loadingEventsResult = g_strdup_printf("%s|%s", previousResult, signalName);
352     g_free(previousResult);
353 }
354
355 static gboolean documentLoadingEventCallback(GSignalInvocationHint *signalHint, guint numParamValues, const GValue *paramValues, gpointer data)
356 {
357     // At least we should receive the instance emitting the signal.
358     if (numParamValues < 1)
359         return TRUE;
360
361     GSignalQuery signal_query;
362     g_signal_query(signalHint->signal_id, &signal_query);
363
364     updateLoadingEventsResult(signal_query.signal_name);
365     return TRUE;
366 }
367
368 static void testWebkitAtkDocumentLoadingEvents()
369 {
370     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
371     g_object_ref_sink(webView);
372     GtkAllocation allocation = { 0, 0, 800, 600 };
373     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
374
375     /* Connect globally to see those events during a future load. */
376     guint loadCompleteListenerId = atk_add_global_event_listener(documentLoadingEventCallback, "ATK:AtkDocument:load-complete");
377
378     /* Do the load, so we can see those events happening. */
379     loadingEventsResult = g_strdup("");
380     webkit_web_view_load_string(webView, contents, 0, 0, 0);
381
382     /* Trigger the creation of the full accessibility hierarchy by
383        asking for the webArea object, so we can listen to events. */
384     getWebAreaObject(webView);
385
386     atk_remove_global_event_listener(loadCompleteListenerId);
387
388     g_assert_cmpstr(loadingEventsResult, ==, "|load-complete");
389
390     g_free(loadingEventsResult);
391     g_object_unref(webView);
392 }
393
394 static void testWebkitAtkEmbeddedObjects()
395 {
396     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
397     g_object_ref_sink(webView);
398     GtkAllocation allocation = { 0, 0, 800, 600 };
399     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
400     webkit_web_view_load_string(webView, embeddedObjects, 0, 0, 0);
401
402     AtkObject* object = getWebAreaObject(webView);
403     g_assert(object);
404
405     AtkText* paragraph1 = ATK_TEXT(atk_object_ref_accessible_child(object, 0));
406     g_assert(ATK_IS_TEXT(paragraph1));
407     g_assert(ATK_IS_HYPERTEXT(paragraph1));
408
409     gint nLinks = atk_hypertext_get_n_links(ATK_HYPERTEXT(paragraph1));
410     g_assert_cmpint(nLinks, ==, 2);
411
412     AtkHyperlink* hLink = atk_hypertext_get_link(ATK_HYPERTEXT(paragraph1), 0);
413     g_assert(ATK_HYPERLINK(hLink));
414     AtkObject* hLinkObject = atk_hyperlink_get_object(hLink, 0);
415     g_assert(ATK_OBJECT(hLinkObject));
416     g_assert(atk_object_get_role(hLinkObject) == ATK_ROLE_CHECK_BOX);
417     g_assert_cmpint(atk_hyperlink_get_start_index(hLink), ==, 8);
418     g_assert_cmpint(atk_hyperlink_get_end_index(hLink), ==, 9);
419     g_assert_cmpint(atk_hyperlink_get_n_anchors(hLink), ==, 1);
420     g_assert_cmpstr(atk_hyperlink_get_uri(hLink, 0), ==, 0);
421
422     AtkText* paragraph2 = ATK_TEXT(atk_object_ref_accessible_child(object, 1));
423     g_assert(ATK_IS_HYPERTEXT(paragraph2));
424
425     nLinks = atk_hypertext_get_n_links(ATK_HYPERTEXT(paragraph2));
426     g_assert_cmpint(nLinks, ==, 1);
427
428     hLink = atk_hypertext_get_link(ATK_HYPERTEXT(paragraph2), 0);
429     g_assert(ATK_HYPERLINK(hLink));
430     hLinkObject = atk_hyperlink_get_object(hLink, 0);
431     g_assert(ATK_OBJECT(hLinkObject));
432     g_assert(atk_object_get_role(hLinkObject) == ATK_ROLE_COMBO_BOX);
433     g_assert_cmpint(atk_hyperlink_get_start_index(hLink), ==, 8);
434     g_assert_cmpint(atk_hyperlink_get_end_index(hLink), ==, 9);
435     g_assert_cmpint(atk_hyperlink_get_n_anchors(hLink), ==, 1);
436     g_assert_cmpstr(atk_hyperlink_get_uri(hLink, 0), ==, 0);
437
438     AtkText* paragraph3 = ATK_TEXT(atk_object_ref_accessible_child(object, 2));
439     g_assert(ATK_IS_HYPERTEXT(paragraph3));
440
441     nLinks = atk_hypertext_get_n_links(ATK_HYPERTEXT(paragraph3));
442     g_assert_cmpint(nLinks, ==, 1);
443
444     hLink = atk_hypertext_get_link(ATK_HYPERTEXT(paragraph3), 0);
445     g_assert(ATK_HYPERLINK(hLink));
446     hLinkObject = atk_hyperlink_get_object(hLink, 0);
447     g_assert(ATK_OBJECT(hLinkObject));
448     g_assert(atk_object_get_role(hLinkObject) == ATK_ROLE_PUSH_BUTTON);
449     g_assert_cmpint(atk_hyperlink_get_start_index(hLink), ==, 0);
450     g_assert_cmpint(atk_hyperlink_get_end_index(hLink), ==, 1);
451     g_assert_cmpint(atk_hyperlink_get_n_anchors(hLink), ==, 1);
452     g_assert_cmpstr(atk_hyperlink_get_uri(hLink, 0), ==, 0);
453
454     g_object_unref(paragraph1);
455     g_object_unref(paragraph2);
456     g_object_unref(paragraph3);
457     g_object_unref(webView);
458 }
459
460 static void testWebkitAtkGetHeadersInTable()
461 {
462     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
463     g_object_ref_sink(webView);
464     GtkAllocation allocation = { 0, 0, 800, 600 };
465     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
466     webkit_web_view_load_string(webView, contentsInTableWithHeaders, 0, 0, 0);
467
468     AtkObject* axWebView = getWebAreaObject(webView);
469     g_assert(axWebView);
470
471     /* Check table with both column and row headers. */
472     AtkObject* table = atk_object_ref_accessible_child(axWebView, 0);
473     g_assert(table);
474     g_assert(atk_object_get_role(table) == ATK_ROLE_TABLE);
475
476     AtkObject* colHeader = atk_table_get_column_header(ATK_TABLE(table), 0);
477     g_assert(colHeader);
478     g_assert(atk_object_get_role(colHeader) == ATK_ROLE_TABLE_CELL);
479     g_assert(atk_object_get_index_in_parent(colHeader) == 0);
480
481     colHeader = atk_table_get_column_header(ATK_TABLE(table), 1);
482     g_assert(colHeader);
483     g_assert(atk_object_get_role(colHeader) == ATK_ROLE_TABLE_CELL);
484     g_assert(atk_object_get_index_in_parent(colHeader) == 1);
485
486     colHeader = atk_table_get_column_header(ATK_TABLE(table), 2);
487     g_assert(colHeader);
488     g_assert(atk_object_get_role(colHeader) == ATK_ROLE_TABLE_CELL);
489     g_assert(atk_object_get_index_in_parent(colHeader) == 2);
490
491     colHeader = atk_table_get_column_header(ATK_TABLE(table), 3);
492     g_assert(colHeader);
493     g_assert(atk_object_get_role(colHeader) == ATK_ROLE_TABLE_CELL);
494     g_assert(atk_object_get_index_in_parent(colHeader) == 2);
495
496     AtkObject* rowHeader = atk_table_get_row_header(ATK_TABLE(table), 0);
497     g_assert(rowHeader);
498     g_assert(atk_object_get_role(rowHeader) == ATK_ROLE_TABLE_CELL);
499     g_assert(atk_object_get_index_in_parent(rowHeader) == 0);
500
501     rowHeader = atk_table_get_row_header(ATK_TABLE(table), 1);
502     g_assert(rowHeader);
503     g_assert(atk_object_get_role(rowHeader) == ATK_ROLE_TABLE_CELL);
504     g_assert(atk_object_get_index_in_parent(rowHeader) == 3);
505
506     rowHeader = atk_table_get_row_header(ATK_TABLE(table), 2);
507     g_assert(rowHeader);
508     g_assert(atk_object_get_role(rowHeader) == ATK_ROLE_TABLE_CELL);
509     g_assert(atk_object_get_index_in_parent(rowHeader) == 7);
510
511     rowHeader = atk_table_get_row_header(ATK_TABLE(table), 3);
512     g_assert(rowHeader);
513     g_assert(atk_object_get_role(rowHeader) == ATK_ROLE_TABLE_CELL);
514     g_assert(atk_object_get_index_in_parent(rowHeader) == 7);
515
516     g_object_unref(table);
517
518     /* Check table with no headers at all. */
519     table = atk_object_ref_accessible_child(axWebView, 1);
520     g_assert(table);
521     g_assert(atk_object_get_role(table) == ATK_ROLE_TABLE);
522
523     colHeader = atk_table_get_column_header(ATK_TABLE(table), 0);
524     g_assert(colHeader == 0);
525
526     colHeader = atk_table_get_column_header(ATK_TABLE(table), 1);
527     g_assert(colHeader == 0);
528
529     rowHeader = atk_table_get_row_header(ATK_TABLE(table), 0);
530     g_assert(rowHeader == 0);
531
532     rowHeader = atk_table_get_row_header(ATK_TABLE(table), 1);
533     g_assert(rowHeader == 0);
534
535     g_object_unref(table);
536     g_object_unref(webView);
537 }
538
539 static gint compAtkAttribute(AtkAttribute* a1, AtkAttribute* a2)
540 {
541     gint strcmpVal = g_strcmp0(a1->name, a2->name);
542     if (strcmpVal)
543         return strcmpVal;
544     return g_strcmp0(a1->value, a2->value);
545 }
546
547 static gint compAtkAttributeName(AtkAttribute* a1, AtkAttribute* a2)
548 {
549     return g_strcmp0(a1->name, a2->name);
550 }
551
552 static gboolean atkAttributeSetAttributeNameHasValue(AtkAttributeSet* set, const gchar* attributeName, const gchar* value)
553 {
554     AtkAttribute at;
555     at.name = (gchar*)attributeName;
556     GSList* element = g_slist_find_custom(set, &at, (GCompareFunc)compAtkAttributeName);
557     return element && !g_strcmp0(((AtkAttribute*)(element->data))->value, value);
558 }
559
560 static gboolean atkAttributeSetContainsAttributeName(AtkAttributeSet* set, const gchar* attributeName)
561 {
562     AtkAttribute at;
563     at.name = (gchar*)attributeName;
564     return g_slist_find_custom(set, &at, (GCompareFunc)compAtkAttributeName) ? true : false;
565 }
566
567 static gboolean atkAttributeSetAttributeHasValue(AtkAttributeSet* set, AtkTextAttribute attribute, const gchar* value)
568 {
569     return atkAttributeSetAttributeNameHasValue(set, atk_text_attribute_get_name(attribute), value);
570 }
571
572 static gboolean atkAttributeSetAreEqual(AtkAttributeSet* set1, AtkAttributeSet* set2)
573 {
574     if (!set1)
575         return !set2;
576
577     set1 = g_slist_sort(set1, (GCompareFunc)compAtkAttribute);
578     set2 = g_slist_sort(set2, (GCompareFunc)compAtkAttribute);
579
580     while (set1) {
581         if (!set2 || compAtkAttribute(set1->data, set2->data))
582             return FALSE;
583
584         set1 = set1->next;
585         set2 = set2->next;
586     }
587
588     return (!set2);
589 }
590
591 static void testWebkitAtkTextAttributes()
592 {
593     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
594     g_object_ref_sink(webView);
595     GtkAllocation allocation = { 0, 0, 800, 600 };
596     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
597     webkit_web_view_load_string(webView, textWithAttributes, 0, 0, 0);
598
599     AtkObject* object = getWebAreaObject(webView);
600     g_assert(object);
601
602     AtkObject* child = atk_object_ref_accessible_child(object, 0);
603     g_assert(child && ATK_IS_TEXT(child));
604     AtkText* childText = ATK_TEXT(child);
605
606     gint startOffset;
607     gint endOffset;
608     AtkAttributeSet* set1 = atk_text_get_run_attributes(childText, 0, &startOffset, &endOffset);
609     g_assert_cmpint(startOffset, ==, 0);
610     g_assert_cmpint(endOffset, ==, 12);
611     g_assert(atkAttributeSetAreEqual(set1, 0));
612
613     AtkAttributeSet* set2 = atk_text_get_run_attributes(childText, 15, &startOffset, &endOffset);
614     g_assert_cmpint(startOffset, ==, 12);
615     g_assert_cmpint(endOffset, ==, 17);
616     g_assert(atkAttributeSetAttributeHasValue(set2, ATK_TEXT_ATTR_STYLE, "italic"));
617
618     AtkAttributeSet* set3 = atk_text_get_run_attributes(childText, 17, &startOffset, &endOffset);
619     g_assert_cmpint(startOffset, ==, 17);
620     g_assert_cmpint(endOffset, ==, 40);
621     g_assert(atkAttributeSetAttributeHasValue(set3, ATK_TEXT_ATTR_WEIGHT, "700"));
622
623     AtkAttributeSet* set4 = atk_text_get_default_attributes(childText);
624     g_assert(atkAttributeSetAttributeHasValue(set4, ATK_TEXT_ATTR_STYLE, "normal"));
625     g_assert(atkAttributeSetAttributeHasValue(set4, ATK_TEXT_ATTR_JUSTIFICATION, "right"));
626     g_assert(atkAttributeSetAttributeHasValue(set4, ATK_TEXT_ATTR_SIZE, "14"));
627     atk_attribute_set_free(set1);
628     atk_attribute_set_free(set2);
629     atk_attribute_set_free(set3);
630     atk_attribute_set_free(set4);
631
632     g_object_unref(child);
633     child = atk_object_ref_accessible_child(object, 1);
634     g_assert(child && ATK_IS_TEXT(child));
635     childText = ATK_TEXT(child);
636
637     set1 = atk_text_get_default_attributes(childText);
638     g_assert(atkAttributeSetAttributeHasValue(set1, ATK_TEXT_ATTR_FAMILY_NAME, "monospace"));
639     g_assert(atkAttributeSetAttributeHasValue(set1, ATK_TEXT_ATTR_STYLE, "normal"));
640     g_assert(atkAttributeSetAttributeHasValue(set1, ATK_TEXT_ATTR_STRIKETHROUGH, "false"));
641     g_assert(atkAttributeSetAttributeHasValue(set1, ATK_TEXT_ATTR_WEIGHT, "400"));
642     g_assert(atkAttributeSetAttributeHasValue(set1, ATK_TEXT_ATTR_FG_COLOR, "120,121,122"));
643
644     set2 = atk_text_get_run_attributes(childText, 43, &startOffset, &endOffset);
645     g_assert_cmpint(startOffset, ==, 43);
646     g_assert_cmpint(endOffset, ==, 80);
647     /* Checks that default attributes of text are not returned when called to atk_text_get_run_attributes. */
648     g_assert(!atkAttributeSetAttributeHasValue(set2, ATK_TEXT_ATTR_FG_COLOR, "120,121,122"));
649     g_assert(atkAttributeSetAttributeHasValue(set2, ATK_TEXT_ATTR_UNDERLINE, "single"));
650     g_assert(atkAttributeSetAttributeHasValue(set2, ATK_TEXT_ATTR_BG_COLOR, "80,81,82"));
651     atk_attribute_set_free(set1);
652     atk_attribute_set_free(set2);
653     g_object_unref(child);
654
655     child = atk_object_ref_accessible_child(object, 2);
656     g_assert(child && ATK_IS_TEXT(child));
657     childText = ATK_TEXT(child);
658
659     set1 = atk_text_get_run_attributes(childText, 0, &startOffset, &endOffset);
660     set2 = atk_text_get_run_attributes(childText, 3, &startOffset, &endOffset);
661     g_assert(atkAttributeSetAreEqual(set1, set2));
662     atk_attribute_set_free(set2);
663
664     set2 = atk_text_get_run_attributes(childText, 1, &startOffset, &endOffset);
665     set3 = atk_text_get_run_attributes(childText, 5, &startOffset, &endOffset);
666     g_assert(atkAttributeSetAreEqual(set2, set3));
667     g_assert(!atkAttributeSetAreEqual(set1, set2));
668     atk_attribute_set_free(set3);
669
670     set3 = atk_text_get_run_attributes(childText, 2, &startOffset, &endOffset);
671     set4 = atk_text_get_run_attributes(childText, 6, &startOffset, &endOffset);
672     g_assert(atkAttributeSetAreEqual(set3, set4));
673     g_assert(!atkAttributeSetAreEqual(set1, set3));
674     g_assert(!atkAttributeSetAreEqual(set2, set3));
675     atk_attribute_set_free(set1);
676     atk_attribute_set_free(set2);
677     atk_attribute_set_free(set3);
678     atk_attribute_set_free(set4);
679     g_object_unref(child);
680
681     child = atk_object_ref_accessible_child(object, 3);
682     g_assert(child && ATK_IS_TEXT(child));
683     childText = ATK_TEXT(child);
684     set1 = atk_text_get_run_attributes(childText, 24, &startOffset, &endOffset);
685     g_assert_cmpint(startOffset, ==, 21);
686     g_assert_cmpint(endOffset, ==, 25);
687     g_assert(atkAttributeSetAttributeHasValue(set1, ATK_TEXT_ATTR_STRIKETHROUGH, "true"));
688
689     set2 = atk_text_get_run_attributes(childText, 25, &startOffset, &endOffset);
690     g_assert_cmpint(startOffset, ==, 25);
691     g_assert_cmpint(endOffset, ==, 30);
692     g_assert(atkAttributeSetAreEqual(set2, 0));
693
694     set3 = atk_text_get_default_attributes(childText);
695     g_assert(atkAttributeSetAttributeHasValue(set3, ATK_TEXT_ATTR_JUSTIFICATION, "center"));
696     atk_attribute_set_free(set1);
697     atk_attribute_set_free(set2);
698     atk_attribute_set_free(set3);
699
700     g_object_unref(child);
701 }
702
703 static gchar* textSelectionChangedResult = 0;
704
705 static void textSelectionChangedCallback(AtkText* text, gpointer data)
706 {
707     g_assert(ATK_IS_TEXT(text));
708
709     g_free(textSelectionChangedResult);
710     AtkRole role = atk_object_get_role(ATK_OBJECT(text));
711     int startOffset = 0;
712     int endOffset = 0;
713     atk_text_get_selection(ATK_TEXT(text), 0, &startOffset, &endOffset);
714     textSelectionChangedResult = g_strdup_printf("|%s|%d|%d|", atk_role_get_name(role), startOffset, endOffset);
715 }
716
717 static void testWebkitAtkTextSelections()
718 {
719     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
720     g_object_ref_sink(webView);
721     GtkAllocation allocation = { 0, 0, 800, 600 };
722     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
723     webkit_web_view_load_string(webView, textForSelections, 0, 0, 0);
724
725     AtkObject* object = getWebAreaObject(webView);
726     g_assert(object);
727
728     AtkText* paragraph1 = ATK_TEXT(atk_object_ref_accessible_child(object, 0));
729     g_assert(ATK_IS_TEXT(paragraph1));
730     g_signal_connect(paragraph1, "text-selection-changed", G_CALLBACK(textSelectionChangedCallback), 0);
731
732     AtkText* paragraph2 = ATK_TEXT(atk_object_ref_accessible_child(object, 1));
733     g_assert(ATK_IS_TEXT(paragraph2));
734     g_signal_connect(paragraph2, "text-selection-changed", G_CALLBACK(textSelectionChangedCallback), 0);
735
736     AtkText* link = ATK_TEXT(atk_object_ref_accessible_child(ATK_OBJECT(paragraph2), 0));
737     g_assert(ATK_IS_TEXT(link));
738
739     AtkObject* list = atk_object_ref_accessible_child(object, 2);
740     g_assert(ATK_OBJECT(list));
741
742     AtkText* listItem = ATK_TEXT(atk_object_ref_accessible_child(list, 0));
743     g_assert(ATK_IS_TEXT(listItem));
744
745     /* First paragraph (simple text). */
746
747     /* Basic initial checks. */
748     g_assert_cmpint(atk_text_get_n_selections(paragraph1), ==, 0);
749
750     gint startOffset;
751     gint endOffset;
752     gchar* selectedText = atk_text_get_selection(paragraph1, 0, &startOffset, &endOffset);
753     g_assert_cmpint(startOffset, ==, 0);
754     g_assert_cmpint(endOffset, ==, 0);
755     g_assert_cmpstr(selectedText, ==, 0);
756     g_free (selectedText);
757
758     /* Try removing a non existing (yet) selection. */
759     gboolean result = atk_text_remove_selection(paragraph1, 0);
760     g_assert(!result);
761
762     /* Try setting a 0-char selection. */
763     result = atk_text_set_selection(paragraph1, 0, 5, 5);
764     g_assert(result);
765
766     /* Make a selection and test it. */
767     result = atk_text_set_selection(paragraph1, 0, 5, 25);
768     g_assert(result);
769     g_assert_cmpint(atk_text_get_n_selections(paragraph1), ==, 1);
770     g_assert_cmpstr(textSelectionChangedResult, ==, "|paragraph|5|25|");
771     selectedText = atk_text_get_selection(paragraph1, 0, &startOffset, &endOffset);
772     g_assert_cmpint(startOffset, ==, 5);
773     g_assert_cmpint(endOffset, ==, 25);
774     g_assert_cmpstr(selectedText, ==, "agraph with plain te");
775     g_free (selectedText);
776
777     /* Try removing the selection from other AtkText object (should fail). */
778     result = atk_text_remove_selection(paragraph2, 0);
779     g_assert(!result);
780
781     /* Remove the selection and test everything again. */
782     result = atk_text_remove_selection(paragraph1, 0);
783     g_assert(result);
784     g_assert_cmpint(atk_text_get_n_selections(paragraph1), ==, 0);
785     selectedText = atk_text_get_selection(paragraph1, 0, &startOffset, &endOffset);
786     /* Now offsets should be the same, set to the last position of the caret. */
787     g_assert_cmpint(startOffset, ==, endOffset);
788     g_assert_cmpint(startOffset, ==, 25);
789     g_assert_cmpint(endOffset, ==, 25);
790     g_assert_cmpstr(selectedText, ==, 0);
791     g_free (selectedText);
792
793     /* Second paragraph (text + link + text). */
794
795     /* Set a selection partially covering the link and test it. */
796     result = atk_text_set_selection(paragraph2, 0, 7, 21);
797     g_assert(result);
798
799     /* Test the paragraph first. */
800     g_assert_cmpint(atk_text_get_n_selections(paragraph2), ==, 1);
801     selectedText = atk_text_get_selection(paragraph2, 0, &startOffset, &endOffset);
802     g_assert_cmpint(startOffset, ==, 7);
803     g_assert_cmpint(endOffset, ==, 21);
804     g_assert_cmpstr(selectedText, ==, "raph with a li");
805     g_free (selectedText);
806
807     /* Now test just the link. */
808     g_assert_cmpint(atk_text_get_n_selections(link), ==, 1);
809     selectedText = atk_text_get_selection(link, 0, &startOffset, &endOffset);
810     g_assert_cmpint(startOffset, ==, 0);
811     g_assert_cmpint(endOffset, ==, 4);
812     g_assert_cmpstr(selectedText, ==, "a li");
813     g_free (selectedText);
814
815     /* Make a selection after the link and check selection for the whole paragraph. */
816     result = atk_text_set_selection(paragraph2, 0, 27, 37);
817     g_assert(result);
818     g_assert_cmpint(atk_text_get_n_selections(paragraph2), ==, 1);
819     g_assert_cmpstr(textSelectionChangedResult, ==, "|paragraph|27|37|");
820     selectedText = atk_text_get_selection(paragraph2, 0, &startOffset, &endOffset);
821     g_assert_cmpint(startOffset, ==, 27);
822     g_assert_cmpint(endOffset, ==, 37);
823     g_assert_cmpstr(selectedText, ==, "the middle");
824     g_free (selectedText);
825
826     /* Remove selections and text everything again. */
827     result = atk_text_remove_selection(paragraph2, 0);
828     g_assert(result);
829     g_assert_cmpint(atk_text_get_n_selections(paragraph2), ==, 0);
830     selectedText = atk_text_get_selection(paragraph2, 0, &startOffset, &endOffset);
831     /* Now offsets should be the same (no selection). */
832     g_assert_cmpint(startOffset, ==, endOffset);
833     g_assert_cmpstr(selectedText, ==, 0);
834     g_free (selectedText);
835
836     g_assert_cmpint(atk_text_get_n_selections(link), ==, 0);
837     selectedText = atk_text_get_selection(link, 0, &startOffset, &endOffset);
838     /* Now offsets should be the same (no selection). */
839     g_assert_cmpint(startOffset, ==, endOffset);
840     g_assert_cmpstr(selectedText, ==, 0);
841     g_free (selectedText);
842
843     /* List item */
844
845     g_assert(atk_object_get_role(list) == ATK_ROLE_LIST);
846     g_assert_cmpint(atk_object_get_n_accessible_children(list), ==, 1);
847
848     /* It's not possible to select text inside an item's marker. */
849     result = atk_text_set_selection (listItem, 0, 0, 9);
850     g_assert(!result);
851     result = atk_text_set_selection (listItem, 0, 9, 1);
852     g_assert(!result);
853
854     /* It should be possible to select text inside an item's text. */
855     result = atk_text_set_selection (listItem, 0, 3, 9);
856     g_assert(result);
857
858     g_assert_cmpint(atk_text_get_n_selections(listItem), ==, 1);
859     selectedText = atk_text_get_selection(listItem, 0, &startOffset, &endOffset);
860     g_assert_cmpint(startOffset, ==, 3);
861     g_assert_cmpint(endOffset, ==, 9);
862     g_assert_cmpstr(selectedText, ==, "A list");
863     g_free (selectedText);
864
865     g_free(textSelectionChangedResult);
866
867     g_object_unref(paragraph1);
868     g_object_unref(paragraph2);
869     g_object_unref(link);
870     g_object_unref(list);
871     g_object_unref(listItem);
872     g_object_unref(webView);
873 }
874
875 static void testWebkitAtkGetExtents()
876 {
877     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
878     g_object_ref_sink(webView);
879     GtkAllocation allocation = { 0, 0, 800, 600 };
880     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
881     webkit_web_view_load_string(webView, centeredContents, 0, 0, 0);
882
883     AtkObject* object = getWebAreaObject(webView);
884     g_assert(object);
885
886     AtkText* shortText1 = ATK_TEXT(atk_object_ref_accessible_child(object, 0));
887     g_assert(ATK_IS_TEXT(shortText1));
888     AtkText* longText = ATK_TEXT(atk_object_ref_accessible_child(object, 1));
889     g_assert(ATK_IS_TEXT(longText));
890     AtkText* shortText2 = ATK_TEXT(atk_object_ref_accessible_child(object, 2));
891     g_assert(ATK_IS_TEXT(shortText2));
892     AtkText* multilineText = ATK_TEXT(atk_object_ref_accessible_child(object, 3));
893     g_assert(ATK_IS_TEXT(multilineText));
894
895     /* Start with window extents. */
896     AtkTextRectangle sline_window1, sline_window2, lline_window, mline_window;
897     atk_text_get_range_extents(shortText1, 0, 10, ATK_XY_WINDOW, &sline_window1);
898     atk_text_get_range_extents(longText, 0, 44, ATK_XY_WINDOW, &lline_window);
899     atk_text_get_range_extents(shortText2, 0, 10, ATK_XY_WINDOW, &sline_window2);
900     atk_text_get_range_extents(multilineText, 0, 60, ATK_XY_WINDOW, &mline_window);
901
902     /* Check vertical line position. */
903     g_assert_cmpint(sline_window1.y + sline_window1.height, <=, lline_window.y);
904     g_assert_cmpint(lline_window.y + lline_window.height + sline_window2.height, <=, mline_window.y);
905
906     /* Paragraphs 1 and 3 have identical text and alignment. */
907     g_assert_cmpint(sline_window1.x, ==, sline_window2.x);
908     g_assert_cmpint(sline_window1.width, ==, sline_window2.width);
909     g_assert_cmpint(sline_window1.height, ==, sline_window2.height);
910
911     /* All lines should be the same height; line 2 is the widest line. */
912     g_assert_cmpint(sline_window1.height, ==, lline_window.height);
913     g_assert_cmpint(sline_window1.width, <, lline_window.width);
914
915     /* Make sure the character extents jive with the range extents. */
916     gint x;
917     gint y;
918     gint width;
919     gint height;
920
921     /* First paragraph (short text). */
922     atk_text_get_character_extents(shortText1, 0, &x, &y, &width, &height, ATK_XY_WINDOW);
923     g_assert_cmpint(x, ==, sline_window1.x);
924     g_assert_cmpint(y, ==, sline_window1.y);
925     g_assert_cmpint(height, ==, sline_window1.height);
926
927     atk_text_get_character_extents(shortText1, 9, &x, &y, &width, &height, ATK_XY_WINDOW);
928     g_assert_cmpint(x, ==, sline_window1.x + sline_window1.width - width);
929     g_assert_cmpint(y, ==, sline_window1.y);
930     g_assert_cmpint(height, ==, sline_window1.height);
931
932     /* Second paragraph (long text). */
933     atk_text_get_character_extents(longText, 0, &x, &y, &width, &height, ATK_XY_WINDOW);
934     g_assert_cmpint(x, ==, lline_window.x);
935     g_assert_cmpint(y, ==, lline_window.y);
936     g_assert_cmpint(height, ==, lline_window.height);
937
938     atk_text_get_character_extents(longText, 43, &x, &y, &width, &height, ATK_XY_WINDOW);
939     g_assert_cmpint(x, ==, lline_window.x + lline_window.width - width);
940     g_assert_cmpint(y, ==, lline_window.y);
941     g_assert_cmpint(height, ==, lline_window.height);
942
943     /* Third paragraph (short text). */
944     atk_text_get_character_extents(shortText2, 0, &x, &y, &width, &height, ATK_XY_WINDOW);
945     g_assert_cmpint(x, ==, sline_window2.x);
946     g_assert_cmpint(y, ==, sline_window2.y);
947     g_assert_cmpint(height, ==, sline_window2.height);
948
949     atk_text_get_character_extents(shortText2, 9, &x, &y, &width, &height, ATK_XY_WINDOW);
950     g_assert_cmpint(x, ==, sline_window2.x + sline_window2.width - width);
951     g_assert_cmpint(y, ==, sline_window2.y);
952     g_assert_cmpint(height, ==, sline_window2.height);
953
954     /* Four paragraph (3 lines multi-line text). */
955     atk_text_get_character_extents(multilineText, 0, &x, &y, &width, &height, ATK_XY_WINDOW);
956     g_assert_cmpint(x, ==, mline_window.x);
957     g_assert_cmpint(y, ==, mline_window.y);
958     g_assert_cmpint(3 * height, ==, mline_window.height);
959
960     atk_text_get_character_extents(multilineText, 59, &x, &y, &width, &height, ATK_XY_WINDOW);
961     /* Last line won't fill the whole width of the rectangle. */
962     g_assert_cmpint(x, <=, mline_window.x + mline_window.width - width);
963     g_assert_cmpint(y, ==, mline_window.y + mline_window.height - height);
964     g_assert_cmpint(height, <=, mline_window.height);
965
966     /* Check that extent for a full line are the same height than for
967        a partial section of the same line */
968     gint startOffset;
969     gint endOffset;
970
971 #if !ATK_CHECK_VERSION(2, 10, 0)
972     gchar* text = atk_text_get_text_at_offset(multilineText, 0, ATK_TEXT_BOUNDARY_LINE_START, &startOffset, &endOffset);
973     g_free(text);
974 #else
975     gchar* text = atk_text_get_string_at_offset(multilineText, 0, ATK_TEXT_GRANULARITY_LINE, &startOffset, &endOffset);
976     g_free(text);
977 #endif
978
979     AtkTextRectangle fline_window;
980     AtkTextRectangle afline_window;
981     atk_text_get_range_extents(multilineText, startOffset, endOffset, ATK_XY_WINDOW, &fline_window);
982     atk_text_get_range_extents(multilineText, startOffset, endOffset - 1, ATK_XY_WINDOW, &afline_window);
983     g_assert_cmpint(fline_window.x, ==, afline_window.x);
984     g_assert_cmpint(fline_window.y, ==, afline_window.y);
985     g_assert_cmpint(fline_window.height, ==, afline_window.height);
986
987     g_object_unref(shortText1);
988     g_object_unref(shortText2);
989     g_object_unref(longText);
990     g_object_unref(multilineText);
991     g_object_unref(webView);
992 }
993
994 static void testWebkitAtkLayoutAndDataTables()
995 {
996     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
997     g_object_ref_sink(webView);
998     GtkAllocation allocation = { 0, 0, 800, 600 };
999     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
1000     webkit_web_view_load_string(webView, layoutAndDataTables, 0, 0, 0);
1001
1002     AtkObject* object = getWebAreaObject(webView);
1003     g_assert(object);
1004
1005     /* Check the non-layout table (data table). */
1006
1007     AtkObject* table1 = atk_object_ref_accessible_child(object, 0);
1008     g_assert(ATK_IS_TABLE(table1));
1009     AtkAttributeSet* set1 = atk_object_get_attributes(table1);
1010     g_assert(set1);
1011     g_assert(!atkAttributeSetContainsAttributeName(set1, "layout-guess"));
1012     atk_attribute_set_free(set1);
1013
1014     /* Check the layout table. */
1015
1016     AtkObject* table2 = atk_object_ref_accessible_child(object, 1);
1017     g_assert(ATK_IS_TABLE(table2));
1018     AtkAttributeSet* set2 = atk_object_get_attributes(table2);
1019     g_assert(set2);
1020     g_assert(atkAttributeSetContainsAttributeName(set2, "layout-guess"));
1021     g_assert(atkAttributeSetAttributeNameHasValue(set2, "layout-guess", "true"));
1022     atk_attribute_set_free(set2);
1023
1024     g_object_unref(table1);
1025     g_object_unref(table2);
1026     g_object_unref(webView);
1027 }
1028
1029 static void testWebkitAtkLinksWithInlineImages()
1030 {
1031     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
1032     g_object_ref_sink(webView);
1033     GtkAllocation allocation = { 0, 0, 800, 600 };
1034     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
1035     webkit_web_view_load_string(webView, linksWithInlineImages, 0, 0, 0);
1036
1037     AtkObject* object = getWebAreaObject(webView);
1038     g_assert(object);
1039
1040     /* First paragraph (link at the beginning). */
1041     AtkObject* paragraph = atk_object_ref_accessible_child(object, 0);
1042     g_assert(ATK_IS_TEXT(paragraph));
1043     gint startOffset;
1044     gint endOffset;
1045
1046 #if !ATK_CHECK_VERSION(2, 10, 0)
1047     gchar* text = atk_text_get_text_at_offset(ATK_TEXT(paragraph), 0, ATK_TEXT_BOUNDARY_LINE_START, &startOffset, &endOffset);
1048     g_assert(text);
1049     g_assert_cmpstr(text, ==, "foo bar baz");
1050     g_assert_cmpint(startOffset, ==, 0);
1051     g_assert_cmpint(endOffset, ==, 11);
1052     g_free(text);
1053 #else
1054     gchar* text = atk_text_get_string_at_offset(ATK_TEXT(paragraph), 0, ATK_TEXT_GRANULARITY_LINE, &startOffset, &endOffset);
1055     g_assert(text);
1056     g_assert_cmpstr(text, ==, "foo bar baz");
1057     g_assert_cmpint(startOffset, ==, 0);
1058     g_assert_cmpint(endOffset, ==, 11);
1059     g_free(text);
1060 #endif
1061
1062     g_object_unref(paragraph);
1063
1064     /* Second paragraph (link in the middle). */
1065     paragraph = atk_object_ref_accessible_child(object, 1);
1066     g_assert(ATK_IS_TEXT(paragraph));
1067
1068 #if !ATK_CHECK_VERSION(2, 10, 0)
1069     text = atk_text_get_text_at_offset(ATK_TEXT(paragraph), 0, ATK_TEXT_BOUNDARY_LINE_START, &startOffset, &endOffset);
1070     g_assert(text);
1071     g_assert_cmpstr(text, ==, "foo bar baz");
1072     g_assert_cmpint(startOffset, ==, 0);
1073     g_assert_cmpint(endOffset, ==, 11);
1074     g_free(text);
1075 #else
1076     text = atk_text_get_string_at_offset(ATK_TEXT(paragraph), 0, ATK_TEXT_GRANULARITY_LINE, &startOffset, &endOffset);
1077     g_assert(text);
1078     g_assert_cmpstr(text, ==, "foo bar baz");
1079     g_assert_cmpint(startOffset, ==, 0);
1080     g_assert_cmpint(endOffset, ==, 11);
1081     g_free(text);
1082 #endif
1083
1084     g_object_unref(paragraph);
1085
1086     /* Third paragraph (link at the end). */
1087     paragraph = atk_object_ref_accessible_child(object, 2);
1088     g_assert(ATK_IS_TEXT(paragraph));
1089
1090 #if !ATK_CHECK_VERSION(2, 10, 0)
1091     text = atk_text_get_text_at_offset(ATK_TEXT(paragraph), 0, ATK_TEXT_BOUNDARY_LINE_START, &startOffset, &endOffset);
1092     g_assert(text);
1093     g_assert_cmpstr(text, ==, "foo bar baz");
1094     g_assert_cmpint(startOffset, ==, 0);
1095     g_assert_cmpint(endOffset, ==, 11);
1096     g_free(text);
1097 #else
1098     text = atk_text_get_string_at_offset(ATK_TEXT(paragraph), 0, ATK_TEXT_GRANULARITY_LINE, &startOffset, &endOffset);
1099     g_assert(text);
1100     g_assert_cmpstr(text, ==, "foo bar baz");
1101     g_assert_cmpint(startOffset, ==, 0);
1102     g_assert_cmpint(endOffset, ==, 11);
1103     g_free(text);
1104 #endif
1105
1106     g_object_unref(paragraph);
1107     g_object_unref(webView);
1108 }
1109
1110 static void testWebkitAtkHypertextAndHyperlinks()
1111 {
1112     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
1113     g_object_ref_sink(webView);
1114     GtkAllocation allocation = { 0, 0, 800, 600 };
1115     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
1116     webkit_web_view_load_string(webView, hypertextAndHyperlinks, 0, 0, 0);
1117
1118     AtkObject* object = getWebAreaObject(webView);
1119     g_assert(object);
1120
1121     AtkObject* paragraph1 = atk_object_ref_accessible_child(object, 0);
1122     g_assert(ATK_OBJECT(paragraph1));
1123     g_assert(atk_object_get_role(paragraph1) == ATK_ROLE_PARAGRAPH);
1124     g_assert(ATK_IS_HYPERTEXT(paragraph1));
1125
1126     /* No links in the first paragraph. */
1127     gint nLinks = atk_hypertext_get_n_links(ATK_HYPERTEXT(paragraph1));
1128     g_assert_cmpint(nLinks, ==, 0);
1129
1130     AtkObject* paragraph2 = atk_object_ref_accessible_child(object, 1);
1131     g_assert(ATK_OBJECT(paragraph2));
1132     g_assert(atk_object_get_role(paragraph2) == ATK_ROLE_PARAGRAPH);
1133     g_assert(ATK_IS_HYPERTEXT(paragraph2));
1134
1135     /* Check links in the second paragraph.
1136        nLinks = atk_hypertext_get_n_links(ATK_HYPERTEXT(paragraph2));
1137        g_assert_cmpint(nLinks, ==, 3); */
1138
1139     AtkHyperlink* hLink1 = atk_hypertext_get_link(ATK_HYPERTEXT(paragraph2), 0);
1140     g_assert(ATK_HYPERLINK(hLink1));
1141     AtkObject* hLinkObject1 = atk_hyperlink_get_object(hLink1, 0);
1142     g_assert(ATK_OBJECT(hLinkObject1));
1143     g_assert(atk_object_get_role(hLinkObject1) == ATK_ROLE_LINK);
1144     g_assert_cmpint(atk_hyperlink_get_start_index(hLink1), ==, 0);
1145     g_assert_cmpint(atk_hyperlink_get_end_index(hLink1), ==, 6);
1146     g_assert_cmpint(atk_hyperlink_get_n_anchors(hLink1), ==, 1);
1147     g_assert_cmpstr(atk_hyperlink_get_uri(hLink1, 0), ==, "http://foo.bar.baz/");
1148
1149     AtkHyperlink* hLink2 = atk_hypertext_get_link(ATK_HYPERTEXT(paragraph2), 1);
1150     g_assert(ATK_HYPERLINK(hLink2));
1151     AtkObject* hLinkObject2 = atk_hyperlink_get_object(hLink2, 0);
1152     g_assert(ATK_OBJECT(hLinkObject2));
1153     g_assert(atk_object_get_role(hLinkObject2) == ATK_ROLE_LINK);
1154     g_assert_cmpint(atk_hyperlink_get_start_index(hLink2), ==, 12);
1155     g_assert_cmpint(atk_hyperlink_get_end_index(hLink2), ==, 32);
1156     g_assert_cmpint(atk_hyperlink_get_n_anchors(hLink2), ==, 1);
1157     g_assert_cmpstr(atk_hyperlink_get_uri(hLink2, 0), ==, "http://bar.baz.foo/");
1158
1159     AtkHyperlink* hLink3 = atk_hypertext_get_link(ATK_HYPERTEXT(paragraph2), 2);
1160     g_assert(ATK_HYPERLINK(hLink3));
1161     AtkObject* hLinkObject3 = atk_hyperlink_get_object(hLink3, 0);
1162     g_assert(ATK_OBJECT(hLinkObject3));
1163     g_assert(atk_object_get_role(hLinkObject3) == ATK_ROLE_LINK);
1164     g_assert_cmpint(atk_hyperlink_get_start_index(hLink3), ==, 65);
1165     g_assert_cmpint(atk_hyperlink_get_end_index(hLink3), ==, 75);
1166     g_assert_cmpint(atk_hyperlink_get_n_anchors(hLink3), ==, 1);
1167     g_assert_cmpstr(atk_hyperlink_get_uri(hLink3, 0), ==, "http://baz.foo.bar/");
1168
1169     AtkObject* list = atk_object_ref_accessible_child(object, 2);
1170     g_assert(ATK_OBJECT(list));
1171     g_assert(atk_object_get_role(list) == ATK_ROLE_LIST);
1172     g_assert_cmpint(atk_object_get_n_accessible_children(list), ==, 1);
1173
1174     AtkObject* listItem = atk_object_ref_accessible_child(list, 0);
1175     g_assert(ATK_IS_TEXT(listItem));
1176     g_assert(ATK_IS_HYPERTEXT(listItem));
1177
1178     AtkHyperlink* hLinkInListItem = atk_hypertext_get_link(ATK_HYPERTEXT(listItem), 0);
1179     g_assert(ATK_HYPERLINK(hLinkInListItem));
1180     AtkObject* hLinkObject = atk_hyperlink_get_object(hLinkInListItem, 0);
1181     g_assert(ATK_OBJECT(hLinkObject));
1182     g_assert(atk_object_get_role(hLinkObject) == ATK_ROLE_LINK);
1183     g_assert_cmpint(atk_hyperlink_get_start_index(hLinkInListItem), ==, 20);
1184     g_assert_cmpint(atk_hyperlink_get_end_index(hLinkInListItem), ==, 43);
1185     g_assert_cmpint(atk_hyperlink_get_n_anchors(hLinkInListItem), ==, 1);
1186     g_assert_cmpstr(atk_hyperlink_get_uri(hLinkInListItem, 0), ==, "http://foo.bar.baz/");
1187
1188     /* Finally check the AtkAction interface for a given AtkHyperlink. */
1189     g_assert(ATK_IS_ACTION(hLink1));
1190     g_assert_cmpint(atk_action_get_n_actions(ATK_ACTION(hLink1)), ==, 1);
1191     g_assert_cmpstr(atk_action_get_keybinding(ATK_ACTION(hLink1), 0), ==, "");
1192     g_assert_cmpstr(atk_action_get_name(ATK_ACTION(hLink1), 0), ==, "jump");
1193     g_assert(atk_action_do_action(ATK_ACTION(hLink1), 0));
1194
1195     g_object_unref(paragraph1);
1196     g_object_unref(paragraph2);
1197     g_object_unref(list);
1198     g_object_unref(listItem);
1199     g_object_unref(webView);
1200 }
1201
1202 static void testWebkitAtkListsOfItems()
1203 {
1204     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
1205     g_object_ref_sink(webView);
1206     GtkAllocation allocation = { 0, 0, 800, 600 };
1207     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
1208     webkit_web_view_load_string(webView, listsOfItems, 0, 0, 0);
1209
1210     AtkObject* object = getWebAreaObject(webView);
1211     g_assert(object);
1212
1213     /* Unordered list. */
1214
1215     AtkObject* uList = atk_object_ref_accessible_child(object, 0);
1216     g_assert(ATK_OBJECT(uList));
1217     g_assert(atk_object_get_role(uList) == ATK_ROLE_LIST);
1218     g_assert_cmpint(atk_object_get_n_accessible_children(uList), ==, 3);
1219
1220     AtkObject* item1 = atk_object_ref_accessible_child(uList, 0);
1221     AtkObject* item2 = atk_object_ref_accessible_child(uList, 1);
1222     AtkObject* item3 = atk_object_ref_accessible_child(uList, 2);
1223
1224     g_assert_cmpint(atk_object_get_n_accessible_children(item1), ==, 0);
1225     g_assert_cmpint(atk_object_get_n_accessible_children(item2), ==, 1);
1226     g_assert_cmpint(atk_object_get_n_accessible_children(item3), ==, 1);
1227
1228     g_object_unref(item1);
1229     g_object_unref(item2);
1230     g_object_unref(item3);
1231
1232     /* Ordered list. */
1233
1234     AtkObject* oList = atk_object_ref_accessible_child(object, 1);
1235     g_assert(ATK_OBJECT(oList));
1236     g_assert(atk_object_get_role(oList) == ATK_ROLE_LIST);
1237     g_assert_cmpint(atk_object_get_n_accessible_children(oList), ==, 3);
1238
1239     item1 = atk_object_ref_accessible_child(oList, 0);
1240     item2 = atk_object_ref_accessible_child(oList, 1);
1241     item3 = atk_object_ref_accessible_child(oList, 2);
1242
1243     g_assert_cmpint(atk_object_get_n_accessible_children(item1), ==, 0);
1244     g_assert_cmpint(atk_object_get_n_accessible_children(item2), ==, 1);
1245     g_assert_cmpint(atk_object_get_n_accessible_children(item3), ==, 1);
1246
1247     g_object_unref(item1);
1248     g_object_unref(item2);
1249     g_object_unref(item3);
1250
1251     g_object_unref(uList);
1252     g_object_unref(oList);
1253     g_object_unref(webView);
1254 }
1255
1256 typedef enum {
1257   TEXT_CHANGE_INSERT = 1,
1258   TEXT_CHANGE_REMOVE = 2
1259 } TextChangeType;
1260
1261 static gchar* textChangedResult = 0;
1262
1263 static void textChangedCb(AtkText* text, gint pos, gint len, gchar* modifiedText, gpointer data)
1264 {
1265     g_assert(text && ATK_IS_OBJECT(text));
1266
1267     TextChangeType type = GPOINTER_TO_INT(data);
1268     g_free(textChangedResult);
1269     textChangedResult = g_strdup_printf("|%d|%d|%d|'%s'|", type, pos, len, modifiedText);
1270 }
1271
1272 static void testWebkitAtkTextChangedNotifications()
1273 {
1274     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
1275     g_object_ref_sink(webView);
1276     GtkAllocation allocation = { 0, 0, 800, 600 };
1277     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
1278     webkit_web_view_load_string(webView, formWithTextInputs, 0, 0, 0);
1279
1280     AtkObject* object = getWebAreaObject(webView);
1281     g_assert(object);
1282
1283     AtkObject* form = atk_object_ref_accessible_child(object, 0);
1284     g_assert(ATK_IS_OBJECT(form));
1285
1286     /* First check normal text entries. */
1287     AtkObject* textEntry = atk_object_ref_accessible_child(form, 0);
1288     g_assert(ATK_IS_EDITABLE_TEXT(textEntry));
1289     g_assert(atk_object_get_role(ATK_OBJECT(textEntry)) == ATK_ROLE_ENTRY);
1290
1291     g_signal_connect(textEntry, "text-insert",
1292                      G_CALLBACK(textChangedCb),
1293                      GINT_TO_POINTER(TEXT_CHANGE_INSERT));
1294     g_signal_connect(textEntry, "text-remove",
1295                      G_CALLBACK(textChangedCb),
1296                      GINT_TO_POINTER(TEXT_CHANGE_REMOVE));
1297
1298     gint pos = 0;
1299     atk_editable_text_insert_text(ATK_EDITABLE_TEXT(textEntry), "foo bar baz", 11, &pos);
1300     char* text = atk_text_get_text(ATK_TEXT(textEntry), 0, -1);
1301     g_assert_cmpstr(text, ==, "foo bar baz");
1302     g_assert_cmpstr(textChangedResult, ==, "|1|0|11|'foo bar baz'|");
1303     g_free(text);
1304
1305     atk_editable_text_delete_text(ATK_EDITABLE_TEXT(textEntry), 4, 7);
1306     text = atk_text_get_text(ATK_TEXT(textEntry), 0, -1);
1307     g_assert_cmpstr(text, ==, "foo  baz");
1308     g_assert_cmpstr(textChangedResult, ==, "|2|4|3|'bar'|");
1309     g_free(text);
1310
1311     pos = 4;
1312     atk_editable_text_insert_text(ATK_EDITABLE_TEXT(textEntry), "qux quux tobeignored", 8, &pos);
1313     text = atk_text_get_text(ATK_TEXT(textEntry), 0, -1);
1314     g_assert_cmpstr(text, ==, "foo qux quux baz");
1315     g_assert_cmpstr(textChangedResult, ==, "|1|4|8|'qux quux'|");
1316     g_free(text);
1317
1318     /* Now check for password entries. */
1319     AtkObject* passwordEntry = atk_object_ref_accessible_child(form, 1);
1320     g_assert(ATK_IS_EDITABLE_TEXT(passwordEntry));
1321     g_assert(atk_object_get_role(ATK_OBJECT(passwordEntry)) == ATK_ROLE_PASSWORD_TEXT);
1322
1323     g_signal_connect(passwordEntry, "text-insert",
1324                      G_CALLBACK(textChangedCb),
1325                      GINT_TO_POINTER(TEXT_CHANGE_INSERT));
1326     g_signal_connect(passwordEntry, "text-remove",
1327                      G_CALLBACK(textChangedCb),
1328                      GINT_TO_POINTER(TEXT_CHANGE_REMOVE));
1329
1330     pos = 0;
1331     /* A single bullet character is '\342\200\242' */
1332     atk_editable_text_insert_text(ATK_EDITABLE_TEXT(passwordEntry), "foobar", 6, &pos);
1333     g_assert_cmpstr(textChangedResult, ==, "|1|0|6|'\342\200\242\342\200\242\342\200\242\342\200\242\342\200\242\342\200\242'|");
1334     text = atk_text_get_text(ATK_TEXT(passwordEntry), 0, -1);
1335     g_assert_cmpstr(text, ==, "\342\200\242\342\200\242\342\200\242\342\200\242\342\200\242\342\200\242");
1336     g_free(text);
1337
1338     atk_editable_text_delete_text(ATK_EDITABLE_TEXT(passwordEntry), 2, 4);
1339     g_assert_cmpstr(textChangedResult, ==, "|2|2|2|'\342\200\242\342\200\242'|");
1340
1341     text = atk_text_get_text(ATK_TEXT(passwordEntry), 0, -1);
1342     g_assert_cmpstr(text, ==, "\342\200\242\342\200\242\342\200\242\342\200\242");
1343     g_free(text);
1344
1345     pos = 3;
1346     atk_editable_text_insert_text(ATK_EDITABLE_TEXT(passwordEntry), "qux tobeignored", 3, &pos);
1347     g_assert_cmpstr(textChangedResult, ==, "|1|3|3|'\342\200\242\342\200\242\342\200\242'|");
1348
1349     text = atk_text_get_text(ATK_TEXT(passwordEntry), 0, -1);
1350     g_assert_cmpstr(text, ==, "\342\200\242\342\200\242\342\200\242\342\200\242\342\200\242\342\200\242\342\200\242");
1351     g_free(text);
1352
1353     g_free(textChangedResult);
1354
1355     g_object_unref(form);
1356     g_object_unref(textEntry);
1357     g_object_unref(passwordEntry);
1358     g_object_unref(webView);
1359 }
1360
1361 static void testWebkitAtkParentForRootObject()
1362 {
1363     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
1364     GtkAllocation allocation = { 0, 0, 800, 600 };
1365     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
1366     webkit_web_view_load_string(webView, contents, 0, 0, 0);
1367
1368     /* We need a parent container widget for the webview. */
1369     GtkWidget* parentContainer = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1370     g_object_ref_sink(parentContainer);
1371     gtk_container_add(GTK_CONTAINER(parentContainer), GTK_WIDGET(webView));
1372
1373     AtkObject* axParent = gtk_widget_get_accessible(parentContainer);
1374     g_assert(ATK_IS_OBJECT(axParent));
1375
1376     AtkObject* axRoot = gtk_widget_get_accessible(GTK_WIDGET(webView));
1377     g_assert(ATK_IS_OBJECT(axRoot));
1378
1379     /* The child for the parent container's accessibility object
1380        should be the accessibility object for the WebView's root. */
1381     AtkObject* axParentChild = atk_object_ref_accessible_child(axParent, 0);
1382     g_assert(axParentChild == axRoot);
1383
1384     /* Bottom-up navigation should match top-down one. */
1385     g_assert(atk_object_get_parent(axParentChild) == axParent);
1386
1387     g_object_unref(axParentChild);
1388     g_object_unref(parentContainer);
1389 }
1390
1391 static void testWebkitAtkSetParentForObject()
1392 {
1393     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
1394     GtkAllocation allocation = { 0, 0, 800, 600 };
1395     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
1396     webkit_web_view_load_string(webView, contents, 0, 0, 0);
1397
1398     /* Put the webview in a parent container widget to check that the
1399        normal behaviour keeps working as expected by default. */
1400     GtkWidget* parentContainer = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1401     g_object_ref_sink(parentContainer);
1402     gtk_container_add(GTK_CONTAINER(parentContainer), GTK_WIDGET(webView));
1403
1404     AtkObject* axRoot = gtk_widget_get_accessible(GTK_WIDGET(webView));
1405     g_assert(ATK_IS_OBJECT(axRoot));
1406
1407     AtkObject* axParent = gtk_widget_get_accessible(parentContainer);
1408     g_assert(ATK_IS_OBJECT(axParent));
1409
1410     /* The parent of the root object is the parent container's a11y object. */
1411     g_assert(atk_object_get_parent(axRoot) == axParent);
1412
1413     /* We now need to use something as a an alternative parent for
1414        the a11y object associated with the root of the DOM tree. */
1415     GtkWidget* alternativeParent = gtk_button_new();
1416     g_object_ref_sink(alternativeParent);
1417
1418     AtkObject* axAlternativeParent = gtk_widget_get_accessible(alternativeParent);
1419     g_assert(ATK_IS_OBJECT(axAlternativeParent));
1420
1421     /* Manually set the alternative parent's accessibility object as
1422        the parent for the WebKit accessibility root object and check. */
1423     atk_object_set_parent(axRoot, axAlternativeParent);
1424     g_assert(atk_object_get_parent(axRoot) == axAlternativeParent);
1425
1426     g_object_unref(alternativeParent);
1427     g_object_unref(parentContainer);
1428 }
1429
1430 #ifdef GTK_API_VERSION_2
1431 static void initializeTestingFramework(int argc, char** argv)
1432 {
1433     /* Ensure GAIL is the only module loaded. */
1434     g_setenv("GTK_MODULES", "gail", TRUE);
1435
1436     /* Following lines were taken from gtk_test_init(). */
1437     g_test_init(&argc, &argv, 0);
1438     gtk_disable_setlocale();
1439     setlocale(LC_ALL, "C");
1440
1441     gtk_init(&argc, &argv);
1442 }
1443 #endif
1444
1445 int main(int argc, char** argv)
1446 {
1447 #ifdef GTK_API_VERSION_2
1448     /* We can't just call to gtk_test_init() in this case because its
1449        implementation makes sure that no GTK+ module will be loaded, and
1450        we will need to load GAIL for tests that need to use AtkObjects
1451        from non-WebKit GtkWidgets (e.g parentForRootObject).*/
1452     initializeTestingFramework(argc, argv);
1453 #else
1454     gtk_test_init(&argc, &argv, NULL);
1455 #endif
1456
1457     g_test_bug_base("https://bugs.webkit.org/");
1458     g_test_add_func("/webkit/atk/caretOffsets", testWebkitAtkCaretOffsets);
1459     g_test_add_func("/webkit/atk/caretOffsetsAndExtranousWhiteSpaces", testWebkitAtkCaretOffsetsAndExtranousWhiteSpaces);
1460     g_test_add_func("/webkit/atk/comboBox", testWebkitAtkComboBox);
1461     g_test_add_func("/webkit/atk/documentLoadingEvents", testWebkitAtkDocumentLoadingEvents);
1462     g_test_add_func("/webkit/atk/embeddedObjects", testWebkitAtkEmbeddedObjects);
1463     g_test_add_func("/webkit/atk/getHeadersInTable", testWebkitAtkGetHeadersInTable);
1464     g_test_add_func("/webkit/atk/textAttributes", testWebkitAtkTextAttributes);
1465     g_test_add_func("/webkit/atk/textSelections", testWebkitAtkTextSelections);
1466     g_test_add_func("/webkit/atk/getExtents", testWebkitAtkGetExtents);
1467     g_test_add_func("/webkit/atk/hypertextAndHyperlinks", testWebkitAtkHypertextAndHyperlinks);
1468     g_test_add_func("/webkit/atk/layoutAndDataTables", testWebkitAtkLayoutAndDataTables);
1469     g_test_add_func("/webkit/atk/linksWithInlineImages", testWebkitAtkLinksWithInlineImages);
1470     g_test_add_func("/webkit/atk/listsOfItems", testWebkitAtkListsOfItems);
1471     g_test_add_func("/webkit/atk/textChangedNotifications", testWebkitAtkTextChangedNotifications);
1472     g_test_add_func("/webkit/atk/parentForRootObject", testWebkitAtkParentForRootObject);
1473     g_test_add_func("/webkit/atk/setParentForObject", testWebkitAtkSetParentForObject);
1474     return g_test_run ();
1475 }
1476