[ATK] Leak: Leaks in testatk.c
[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* contentsWithNewlines = "<html><body><p>This is a test. \n\nThis\n is the second sentence. And this the third.</p></body></html>";
35
36 static const char* contentsWithPreformattedText = "<html><body><pre>\n\t\n\tfirst line\n\tsecond line\n</pre></body></html>";
37
38 static const char* contentsWithSpecialChars = "<html><body><p>&laquo;&nbsp;This is a paragraph with &ldquo;special&rdquo; characters inside.&nbsp;&raquo;</p><ul><li style='max-width:100px;'>List item with some text that wraps across different lines.</li><li style='max-width:100px;'><p>List item with some text that wraps across different lines.</p></li></ul></body></html>";
39
40 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>";
41
42 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>";
43
44 static const char* contentsInParagraphAndBodySimple = "<html><body><p>This is a test.</p>Hello world.</body></html>";
45
46 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>";
47
48 static const char* contentsInTable = "<html><body><table><tr><td>foo</td><td>bar</td></tr></table></body></html>";
49
50 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>";
51
52 static const char* contentsWithExtraneousWhiteSpaces = "<html><head><body><p>This\n                          paragraph\n                                                      is\n                                                                                                                                                                                                                                                                                                                                                                            borked!</p></body></html>";
53
54 static const char* contentsWithWrappedLines = "<html><body><p style='max-width:150px;'>This is one line wrapped because of the maximum width of its container.</p><p>This is another line wrapped<br>because of one forced<br>line break in the middle.</body></html>";
55
56 static const char* contentsWithEmbeddedObjects = "<html><body>This is one line containing two <img> embedded objects <img> in the middle.</body></html>";
57
58 static const char* comboBoxSelector = "<html><body><select><option selected value='foo'>foo</option><option value='bar'>bar</option></select></body></html>";
59
60 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>";
61
62 static const char* formWithTextInputs = "<html><body><form><input type='text' name='entry' /><input type='password' name='passwordEntry' /></form></body></html>";
63
64 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>";
65
66 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>";
67
68 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>";
69
70 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>";
71
72 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>";
73
74 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>";
75
76 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>";
77
78 static AtkObject* getWebAreaObject(WebKitWebView* webView)
79 {
80     /* Manually spin the main context to make sure the accessible
81        objects are properly created before continuing. */
82     while (g_main_context_pending(0))
83         g_main_context_iteration(0, TRUE);
84
85     AtkObject* rootObject = gtk_widget_get_accessible(GTK_WIDGET(webView));
86     if (!rootObject)
87         return NULL;
88
89     AtkObject* webAreaObject = atk_object_ref_accessible_child(rootObject, 0);
90     if (!webAreaObject)
91         return NULL;
92
93     /* We don't need the extra ref here. */
94     g_object_unref(webAreaObject);
95
96     return webAreaObject;
97 }
98
99 typedef gchar* (*AtkGetTextFunction) (AtkText*, gint, AtkTextBoundary, gint*, gint*);
100
101 static void testGetTextFunction(AtkText* textObject, AtkGetTextFunction fn, AtkTextBoundary boundary, gint offset, const char* textResult, gint startOffsetResult, gint endOffsetResult)
102 {
103     gint startOffset;
104     gint endOffset;
105     char* text = fn(textObject, offset, boundary, &startOffset, &endOffset);
106     g_assert_cmpstr(text, ==, textResult);
107     g_assert_cmpint(startOffset, ==, startOffsetResult);
108     g_assert_cmpint(endOffset, ==, endOffsetResult);
109     g_free(text);
110 }
111
112 static void runGetTextTests(AtkText* textObject)
113 {
114     char* text = atk_text_get_text(textObject, 0, -1);
115     g_assert_cmpstr(text, ==, "This is a test. This is the second sentence. And this the third.");
116     g_free(text);
117
118     /* ATK_TEXT_BOUNDARY_CHAR */
119     testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_CHAR,
120                         0, "T", 0, 1);
121
122     testGetTextFunction(textObject, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_CHAR,
123                         0, "h", 1, 2);
124
125     testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_CHAR,
126                         0, "", 0, 0);
127
128     testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_CHAR,
129                         1, "T", 0, 1);
130
131     /* ATK_TEXT_BOUNDARY_WORD_START */
132     testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START,
133                         0, "This ", 0, 5);
134
135     testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START,
136                         4, "This ", 0, 5);
137
138     testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START,
139                         10, "test. ", 10, 16);
140
141     testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START,
142                         58, "third.", 58, 64);
143
144     testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START,
145                         64, "third.", 58, 64);
146
147     testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_WORD_START,
148                         0, "", 0, 0);
149
150     testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_WORD_START,
151                         5, "This ", 0, 5);
152
153     testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_WORD_START,
154                         7, "This ", 0, 5);
155
156     testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_WORD_START,
157                         64, "the ", 54, 58);
158
159     testGetTextFunction(textObject, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_WORD_START,
160                         0, "is ", 5, 8);
161
162     testGetTextFunction(textObject, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_WORD_START,
163                         4, "is ", 5, 8);
164
165     testGetTextFunction(textObject, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_WORD_START,
166                         3, "is ", 5, 8);
167
168     testGetTextFunction(textObject, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_WORD_START,
169                         64, "", 64, 64);
170
171     /* ATK_TEXT_BOUNDARY_WORD_END */
172     testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END,
173                         0, "This", 0, 4);
174
175     testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END,
176                         4, " is", 4, 7);
177
178     testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END,
179                         5, " is", 4, 7);
180
181     testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END,
182                         9, " test", 9, 14);
183
184     testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END,
185                         58, " third", 57, 63);
186
187     testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END,
188                         64, ".", 63, 64);
189
190     testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_WORD_END,
191                         0, "", 0, 0);
192
193     testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_WORD_END,
194                         5, "This", 0, 4);
195
196     testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_WORD_END,
197                         4, "This", 0, 4);
198
199     testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_WORD_END,
200                         7, " is", 4, 7);
201
202     testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_WORD_END,
203                         64, " third", 57, 63);
204
205     testGetTextFunction(textObject, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_WORD_END,
206                         0, " is", 4, 7);
207
208     testGetTextFunction(textObject, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_WORD_END,
209                         5, " a", 7, 9);
210
211     testGetTextFunction(textObject, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_WORD_END,
212                         4, " a", 7, 9);
213
214     testGetTextFunction(textObject, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_WORD_END,
215                         64, "", 64, 64);
216
217     /* ATK_TEXT_BOUNDARY_SENTENCE_START */
218     testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_SENTENCE_START,
219                         0, "This is a test. ", 0, 16);
220
221     testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_SENTENCE_START,
222                         15, "This is a test. ", 0, 16);
223
224     testGetTextFunction(textObject, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_SENTENCE_START,
225                         0, "This is the second sentence. ", 16, 45);
226
227     testGetTextFunction(textObject, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_SENTENCE_START,
228                         15, "This is the second sentence. ", 16, 45);
229
230     testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_SENTENCE_START,
231                         16, "This is a test. ", 0, 16);
232
233     testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_SENTENCE_START,
234                         44, "This is a test. ", 0, 16);
235
236     testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_SENTENCE_START,
237                         15, "", 0, 0);
238
239     /* ATK_TEXT_BOUNDARY_SENTENCE_END */
240     testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_SENTENCE_END,
241                         0, "This is a test.", 0, 15);
242
243     testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_SENTENCE_END,
244                         15, " This is the second sentence.", 15, 44);
245
246     testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_SENTENCE_END,
247                         16, " This is the second sentence.", 15, 44);
248
249     testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_SENTENCE_END,
250                         17, " This is the second sentence.", 15, 44);
251
252     testGetTextFunction(textObject, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_SENTENCE_END,
253                         0, " This is the second sentence.", 15, 44);
254
255     testGetTextFunction(textObject, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_SENTENCE_END,
256                         15, " And this the third.", 44, 64);
257
258     testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_SENTENCE_END,
259                         16, "This is a test.", 0, 15);
260
261     testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_SENTENCE_END,
262                         15, "This is a test.", 0, 15);
263
264     testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_SENTENCE_END,
265                         14, "", 0, 0);
266
267     testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_SENTENCE_END,
268                         44, " This is the second sentence.", 15, 44);
269
270     /* It's tricky to test these properly right now, since our a11y
271        implementation splits different lines in different a11y items. */
272     /* ATK_TEXT_BOUNDARY_LINE_START */
273     testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_START,
274                         0, "This is a test. This is the second sentence. And this the third.", 0, 64);
275
276     /* ATK_TEXT_BOUNDARY_LINE_END */
277     testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_END,
278                         0, "This is a test. This is the second sentence. And this the third.", 0, 64);
279
280     /* For objects implementing AtkEditableText, try to change the
281        exposed text and retrieve it again as a full line.
282        (see https://bugs.webkit.org/show_bug.cgi?id=72830) */
283     if (ATK_IS_EDITABLE_TEXT(textObject)) {
284         atk_editable_text_set_text_contents(ATK_EDITABLE_TEXT(textObject), "foo bar baz");
285         testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_START, 0, "foo bar baz", 0, 11);
286         testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_END, 0, "foo bar baz", 0, 11);
287     }
288 }
289
290 static gchar* textCaretMovedResult = 0;
291
292 static void textCaretMovedCallback(AtkText* text, gint pos, gpointer data)
293 {
294     g_assert(ATK_IS_TEXT(text));
295
296     g_free(textCaretMovedResult);
297     AtkRole role = atk_object_get_role(ATK_OBJECT(text));
298     textCaretMovedResult = g_strdup_printf("|%s|%d|", atk_role_get_name(role), pos);
299 }
300
301 static void testWebkitAtkCaretOffsets()
302 {
303     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
304     g_object_ref_sink(webView);
305     GtkAllocation allocation = { 0, 0, 800, 600 };
306     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
307     webkit_web_view_load_string(webView, textForCaretBrowsing, 0, 0, 0);
308
309     AtkObject* object = getWebAreaObject(webView);
310     g_assert(object);
311
312     AtkObject* header = atk_object_ref_accessible_child(object, 0);
313     g_assert(ATK_IS_TEXT(header));
314     g_signal_connect(header, "text-caret-moved", G_CALLBACK(textCaretMovedCallback), 0);
315
316     gchar* text = atk_text_get_text(ATK_TEXT(header), 0, -1);
317     g_assert_cmpstr(text, ==, "A text header");
318     g_free (text);
319
320     /* It should be possible to place the caret inside a header. */
321     gboolean result = atk_text_set_caret_offset(ATK_TEXT(header), 5);
322     g_assert_cmpint(result, ==, TRUE);
323     gint offset = atk_text_get_caret_offset(ATK_TEXT(header));
324     g_assert_cmpint(offset, ==, 5);
325     g_assert_cmpstr(textCaretMovedResult, ==, "|heading|5|");
326
327     AtkObject* paragraph = atk_object_ref_accessible_child(object, 1);
328     g_assert(ATK_IS_TEXT(paragraph));
329     g_signal_connect(paragraph, "text-caret-moved", G_CALLBACK(textCaretMovedCallback), 0);
330
331     text = atk_text_get_text(ATK_TEXT(paragraph), 0, -1);
332     g_assert_cmpstr(text, ==, "A paragraph with a link in the middle");
333     g_free (text);
334
335     /* It should be possible to place the caret inside a paragraph and a link. */
336     result = atk_text_set_caret_offset(ATK_TEXT(paragraph), 5);
337     g_assert_cmpint(result, ==, TRUE);
338     offset = atk_text_get_caret_offset(ATK_TEXT(paragraph));
339     g_assert_cmpint(offset, ==, 5);
340     g_assert_cmpstr(textCaretMovedResult, ==, "|paragraph|5|");
341
342     result = atk_text_set_caret_offset(ATK_TEXT(paragraph), 20);
343     g_assert_cmpint(result, ==, TRUE);
344     offset = atk_text_get_caret_offset(ATK_TEXT(paragraph));
345     g_assert_cmpint(offset, ==, 20);
346     g_assert_cmpstr(textCaretMovedResult, ==, "|paragraph|20|");
347
348     result = atk_text_set_caret_offset(ATK_TEXT(paragraph), 30);
349     g_assert_cmpint(result, ==, TRUE);
350     offset = atk_text_get_caret_offset(ATK_TEXT(paragraph));
351     g_assert_cmpint(offset, ==, 30);
352     g_assert_cmpstr(textCaretMovedResult, ==, "|paragraph|30|");
353
354     AtkObject* link = atk_object_ref_accessible_child(paragraph, 0);
355     g_assert(ATK_IS_TEXT(link));
356     text = atk_text_get_text(ATK_TEXT(link), 0, -1);
357     g_assert_cmpstr(text, ==, "with a link");
358     g_free (text);
359
360     result = atk_text_set_caret_offset(ATK_TEXT(link), 5);
361     g_assert_cmpint(result, ==, TRUE);
362     offset = atk_text_get_caret_offset(ATK_TEXT(link));
363     g_assert_cmpint(offset, ==, 5);
364     /* Positions inside links are reported relative to the paragraph. */
365     g_assert_cmpstr(textCaretMovedResult, ==, "|paragraph|17|");
366
367     AtkObject* list = atk_object_ref_accessible_child(object, 2);
368     g_assert(ATK_OBJECT(list));
369     g_assert(atk_object_get_role(list) == ATK_ROLE_LIST);
370     g_assert_cmpint(atk_object_get_n_accessible_children(list), ==, 3);
371
372     AtkObject* listItem = atk_object_ref_accessible_child(list, 0);
373     g_assert(ATK_IS_TEXT(listItem));
374     text = atk_text_get_text(ATK_TEXT(listItem), 0, -1);
375     g_assert_cmpstr(text, ==, "1. A list item");
376     g_free (text);
377     g_object_unref(listItem);
378
379     listItem = atk_object_ref_accessible_child(list, 1);
380     g_assert(ATK_IS_TEXT(listItem));
381     text = atk_text_get_text(ATK_TEXT(listItem), 0, -1);
382     g_assert_cmpstr(text, ==, "2. Block span in a list item\nInline span in a list item");
383     g_free (text);
384     g_object_unref(listItem);
385
386     listItem = atk_object_ref_accessible_child(list, 2);
387     g_assert(ATK_IS_TEXT(listItem));
388     text = atk_text_get_text(ATK_TEXT(listItem), 0, -1);
389     g_assert_cmpstr(text, ==, "3. Block span in a link in a list item\nInline span in a link in a list item");
390     g_free (text);
391
392     /* It's not possible to place the caret inside an item's marker. */
393     result = atk_text_set_caret_offset(ATK_TEXT(listItem), 1);
394     g_assert_cmpint(result, ==, FALSE);
395
396     /* It should be possible to place the caret inside an item's text. */
397     result = atk_text_set_caret_offset(ATK_TEXT(listItem), 5);
398     g_assert_cmpint(result, ==, TRUE);
399     offset = atk_text_get_caret_offset(ATK_TEXT(listItem));
400     g_assert_cmpint(offset, ==, 5);
401
402     AtkObject* panel = atk_object_ref_accessible_child(object, 3);
403     g_assert(ATK_IS_OBJECT(panel));
404     g_assert(atk_object_get_role(panel) == ATK_ROLE_PANEL);
405
406     AtkObject* comboBox = atk_object_ref_accessible_child(panel, 0);
407     g_assert(ATK_IS_OBJECT(comboBox));
408     g_assert(!ATK_IS_TEXT(comboBox));
409     g_assert(atk_object_get_role(comboBox) == ATK_ROLE_COMBO_BOX);
410
411     AtkObject* menuPopup = atk_object_ref_accessible_child(comboBox, 0);
412     g_assert(ATK_IS_OBJECT(menuPopup));
413     g_assert(!ATK_IS_TEXT(menuPopup));
414     g_assert(atk_object_get_role(menuPopup) == ATK_ROLE_MENU);
415
416     AtkObject* comboBoxOption = atk_object_ref_accessible_child(menuPopup, 0);
417     g_assert(ATK_IS_OBJECT(comboBoxOption));
418     g_assert(atk_object_get_role(comboBoxOption) == ATK_ROLE_MENU_ITEM);
419     g_assert(ATK_IS_TEXT(comboBoxOption));
420     text = atk_text_get_text(ATK_TEXT(comboBoxOption), 0, -1);
421     g_assert_cmpstr(text, ==, "An option in a combo box");
422     g_free(text);
423
424     /* It's not possible to place the caret inside an option for a combobox. */
425     result = atk_text_set_caret_offset(ATK_TEXT(comboBoxOption), 1);
426     g_assert_cmpint(result, ==, FALSE);
427
428     AtkObject* textEntry = atk_object_ref_accessible_child(panel, 1);
429     g_assert(ATK_IS_OBJECT(textEntry));
430     g_assert(atk_object_get_role(textEntry) == ATK_ROLE_ENTRY);
431     g_assert(ATK_IS_TEXT(textEntry));
432     text = atk_text_get_text(ATK_TEXT(textEntry), 0, -1);
433     g_assert_cmpstr(text, ==, "foo bar baz");
434     g_free(text);
435
436     result = atk_text_set_caret_offset(ATK_TEXT(textEntry), 5);
437     g_assert_cmpint(result, ==, TRUE);
438     offset = atk_text_get_caret_offset(ATK_TEXT(textEntry));
439     g_assert_cmpint(offset, ==, 5);
440
441     AtkObject* table = atk_object_ref_accessible_child(object, 4);
442     g_assert(ATK_IS_OBJECT(table));
443     g_assert(!ATK_IS_TEXT(table));
444     g_assert(atk_object_get_role(table) == ATK_ROLE_TABLE);
445     g_assert_cmpint(atk_object_get_n_accessible_children(table), ==, 4);
446
447     AtkObject* tableCell = atk_object_ref_accessible_child(table, 0);
448     g_assert(ATK_IS_TEXT(tableCell));
449     g_assert(atk_object_get_role(tableCell) == ATK_ROLE_TABLE_CELL);
450     text = atk_text_get_text(ATK_TEXT(tableCell), 0, -1);
451     g_assert_cmpstr(text, ==, "a table cell");
452     g_free(text);
453
454     result = atk_text_set_caret_offset(ATK_TEXT(tableCell), 2);
455     g_assert_cmpint(result, ==, TRUE);
456     offset = atk_text_get_caret_offset(ATK_TEXT(tableCell));
457     g_assert_cmpint(offset, ==, 2);
458     g_object_unref(tableCell);
459
460     /* Even empty table cells should implement AtkText, but report an empty string */
461     tableCell = atk_object_ref_accessible_child(table, 1);
462     g_assert(ATK_IS_TEXT(tableCell));
463     g_assert(atk_object_get_role(tableCell) == ATK_ROLE_TABLE_CELL);
464     text = atk_text_get_text(ATK_TEXT(tableCell), 0, -1);
465     g_assert_cmpstr(text, ==, "");
466     g_free(text);
467     g_object_unref(tableCell);
468
469     tableCell = atk_object_ref_accessible_child(table, 2);
470     g_assert(ATK_IS_TEXT(tableCell));
471     g_assert(atk_object_get_role(tableCell) == ATK_ROLE_TABLE_CELL);
472     text = atk_text_get_text(ATK_TEXT(tableCell), 0, -1);
473     g_assert_cmpstr(text, ==, "Block span in a link in a table cell\nInline span in a link in a table cell");
474     g_free(text);
475     g_object_unref(tableCell);
476
477     tableCell = atk_object_ref_accessible_child(table, 3);
478     g_assert(ATK_IS_TEXT(tableCell));
479     g_assert(atk_object_get_role(tableCell) == ATK_ROLE_TABLE_CELL);
480     text = atk_text_get_text(ATK_TEXT(tableCell), 0, -1);
481     g_assert_cmpstr(text, ==, "Block span in a table cell\nInline span in a table cell");
482     g_free(text);
483
484     header = atk_object_ref_accessible_child(object, 5);
485     g_assert(ATK_IS_TEXT(header));
486     g_assert(atk_object_get_role(header) == ATK_ROLE_HEADING);
487     text = atk_text_get_text(ATK_TEXT(header), 0, -1);
488     g_assert_cmpstr(text, ==, "Block span in a link in a heading\nInline span in a link in a heading");
489     g_free(text);
490     g_object_unref(header);
491
492     header = atk_object_ref_accessible_child(object, 6);
493     g_assert(ATK_IS_TEXT(header));
494     g_assert(atk_object_get_role(header) == ATK_ROLE_HEADING);
495     text = atk_text_get_text(ATK_TEXT(header), 0, -1);
496     g_assert_cmpstr(text, ==, "Block span in a heading\nInline span in a heading");
497     g_free(text);
498
499     g_free(textCaretMovedResult);
500
501     g_object_unref(header);
502     g_object_unref(paragraph);
503     g_object_unref(link);
504     g_object_unref(list);
505     g_object_unref(listItem);
506     g_object_unref(panel);
507     g_object_unref(comboBox);
508     g_object_unref(menuPopup);
509     g_object_unref(comboBoxOption);
510     g_object_unref(textEntry);
511     g_object_unref(table);
512     g_object_unref(tableCell);
513     g_object_unref(webView);
514 }
515
516 static void testWebkitAtkCaretOffsetsAndExtranousWhiteSpaces()
517 {
518     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
519     g_object_ref_sink(webView);
520     GtkAllocation allocation = { 0, 0, 800, 600 };
521     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
522     webkit_web_view_load_string(webView, contentsWithExtraneousWhiteSpaces, 0, 0, 0);
523
524     /* Enable caret browsing. */
525     WebKitWebSettings* settings = webkit_web_view_get_settings(webView);
526     g_object_set(G_OBJECT(settings), "enable-caret-browsing", TRUE, NULL);
527     webkit_web_view_set_settings(webView, settings);
528
529     /* Get to the inner AtkText object. */
530     AtkObject* object = getWebAreaObject(webView);
531     g_assert(object);
532     object = atk_object_ref_accessible_child(object, 0);
533     g_assert(object);
534
535     AtkText* textObject = ATK_TEXT(object);
536     g_assert(ATK_IS_TEXT(textObject));
537
538     gchar* text = atk_text_get_text(textObject, 0, -1);
539     g_assert_cmpstr(text, ==, "This paragraph is borked!");
540     g_free(text);
541
542     gint characterCount = atk_text_get_character_count(textObject);
543     g_assert_cmpint(characterCount, ==, 25);
544
545     gboolean result = atk_text_set_caret_offset(textObject, characterCount - 1);
546     g_assert_cmpint(result, ==, TRUE);
547
548     gint caretOffset = atk_text_get_caret_offset(textObject);
549     g_assert_cmpint(caretOffset, ==, characterCount - 1);
550
551     result = atk_text_set_caret_offset(textObject, characterCount);
552     g_assert_cmpint(result, ==, TRUE);
553
554     caretOffset = atk_text_get_caret_offset(textObject);
555     g_assert_cmpint(caretOffset, ==, characterCount);
556
557     g_object_unref(object);
558     g_object_unref(webView);
559 }
560
561 static void testWebkitAtkComboBox()
562 {
563     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
564     g_object_ref_sink(webView);
565     GtkAllocation allocation = { 0, 0, 800, 600 };
566     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
567     webkit_web_view_load_string(webView, comboBoxSelector, 0, 0, 0);
568
569     AtkObject* object = getWebAreaObject(webView);
570     g_assert(object);
571
572     AtkObject* formObject = atk_object_ref_accessible_child(object, 0);
573     g_assert(formObject);
574
575     AtkObject* comboBox = atk_object_ref_accessible_child(formObject, 0);
576     g_assert(ATK_IS_OBJECT(comboBox));
577
578     AtkObject* menuPopup = atk_object_ref_accessible_child(comboBox, 0);
579     g_assert(ATK_IS_OBJECT(menuPopup));
580
581     AtkObject* item1 = atk_object_ref_accessible_child(menuPopup, 0);
582     g_assert(ATK_IS_OBJECT(item1));
583
584     AtkObject* item2 = atk_object_ref_accessible_child(menuPopup, 1);
585     g_assert(ATK_IS_OBJECT(item2));
586
587     /* Check roles. */
588     g_assert(atk_object_get_role(comboBox) == ATK_ROLE_COMBO_BOX);
589     g_assert(atk_object_get_role(menuPopup) == ATK_ROLE_MENU);
590     g_assert(atk_object_get_role(item1) == ATK_ROLE_MENU_ITEM);
591     g_assert(atk_object_get_role(item2) == ATK_ROLE_MENU_ITEM);
592
593     /* Check the implementation of the AtkSelection interface. */
594     g_assert(ATK_IS_SELECTION(comboBox));
595     AtkSelection* atkSelection = ATK_SELECTION(comboBox);
596     g_assert_cmpint(atk_selection_get_selection_count(atkSelection), ==, 1);
597     g_assert(atk_selection_is_child_selected(atkSelection, 0));
598     g_assert(!atk_selection_is_child_selected(atkSelection, 1));
599     AtkObject* selectedItem = atk_selection_ref_selection(atkSelection, 0);
600     g_assert(selectedItem == item1);
601     g_object_unref(selectedItem);
602
603     /* Check that the menu popup has 0 links and doesn't crash from checking. */
604     gint nLinks = atk_hypertext_get_n_links(ATK_HYPERTEXT(menuPopup));
605     g_assert_cmpint(nLinks, ==, 0);
606
607     /* Check the implementations of the AtkAction interface. */
608     g_assert(ATK_IS_ACTION(comboBox));
609     AtkAction* atkAction = ATK_ACTION(comboBox);
610     g_assert_cmpint(atk_action_get_n_actions(atkAction), ==, 1);
611     g_assert(atk_action_do_action(atkAction, 0));
612
613     g_assert(ATK_IS_ACTION(menuPopup));
614     atkAction = ATK_ACTION(menuPopup);
615     g_assert_cmpint(atk_action_get_n_actions(atkAction), ==, 1);
616     g_assert(atk_action_do_action(atkAction, 0));
617
618     g_assert(ATK_IS_ACTION(item1));
619     atkAction = ATK_ACTION(item1);
620     g_assert_cmpint(atk_action_get_n_actions(atkAction), ==, 1);
621     g_assert(atk_action_do_action(atkAction, 0));
622
623     g_assert(ATK_IS_ACTION(item2));
624     atkAction = ATK_ACTION(item2);
625     g_assert_cmpint(atk_action_get_n_actions(atkAction), ==, 1);
626     g_assert(atk_action_do_action(atkAction, 0));
627
628     /* After selecting the second item, selection should have changed. */
629     g_assert_cmpint(atk_selection_get_selection_count(atkSelection), ==, 1);
630     g_assert(!atk_selection_is_child_selected(atkSelection, 0));
631     g_assert(atk_selection_is_child_selected(atkSelection, 1));
632     selectedItem = atk_selection_ref_selection(atkSelection, 0);
633     g_assert(selectedItem == item2);
634     g_object_unref(selectedItem);
635
636     /* Check the implementation of the AtkText interface. */
637     g_assert(ATK_IS_TEXT(item1));
638     AtkText* atkText = ATK_TEXT(item1);
639     char *text = atk_text_get_text(atkText, 0, -1);
640     g_assert_cmpstr(text, ==, "foo");
641     g_free(text);
642     text = atk_text_get_text(atkText, 0, 2);
643     g_assert_cmpstr(text, ==, "fo");
644     g_free(text);
645
646     g_assert(ATK_IS_TEXT(item2));
647     atkText = ATK_TEXT(item2);
648     text = atk_text_get_text(atkText, 0, -1);
649     g_assert_cmpstr(text, ==, "bar");
650     g_free(text);
651     text = atk_text_get_text(atkText, 1, 3);
652     g_assert_cmpstr(text, ==, "ar");
653     g_free(text);
654
655     g_object_unref(formObject);
656     g_object_unref(comboBox);
657     g_object_unref(menuPopup);
658     g_object_unref(item1);
659     g_object_unref(item2);
660     g_object_unref(webView);
661 }
662
663 static gchar* loadingEventsResult = 0;
664
665 static void updateLoadingEventsResult(const gchar* signalName)
666 {
667     g_assert(signalName);
668
669     gchar* previousResult = loadingEventsResult;
670     loadingEventsResult = g_strdup_printf("%s|%s", previousResult, signalName);
671     g_free(previousResult);
672 }
673
674 static gboolean documentLoadingEventCallback(GSignalInvocationHint *signalHint, guint numParamValues, const GValue *paramValues, gpointer data)
675 {
676     // At least we should receive the instance emitting the signal.
677     if (numParamValues < 1)
678         return TRUE;
679
680     GSignalQuery signal_query;
681     g_signal_query(signalHint->signal_id, &signal_query);
682
683     updateLoadingEventsResult(signal_query.signal_name);
684     return TRUE;
685 }
686
687 static void testWebkitAtkDocumentLoadingEvents()
688 {
689     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
690     g_object_ref_sink(webView);
691     GtkAllocation allocation = { 0, 0, 800, 600 };
692     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
693
694     /* Connect globally to see those events during a future load. */
695     guint loadCompleteListenerId = atk_add_global_event_listener(documentLoadingEventCallback, "ATK:AtkDocument:load-complete");
696
697     /* Do the load, so we can see those events happening. */
698     loadingEventsResult = g_strdup("");
699     webkit_web_view_load_string(webView, contents, 0, 0, 0);
700
701     /* Trigger the creation of the full accessibility hierarchy by
702        asking for the webArea object, so we can listen to events. */
703     getWebAreaObject(webView);
704
705     atk_remove_global_event_listener(loadCompleteListenerId);
706
707     g_assert_cmpstr(loadingEventsResult, ==, "|load-complete");
708
709     g_free(loadingEventsResult);
710     g_object_unref(webView);
711 }
712
713 static void testWebkitAtkEmbeddedObjects()
714 {
715     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
716     g_object_ref_sink(webView);
717     GtkAllocation allocation = { 0, 0, 800, 600 };
718     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
719     webkit_web_view_load_string(webView, embeddedObjects, 0, 0, 0);
720
721     AtkObject* object = getWebAreaObject(webView);
722     g_assert(object);
723
724     AtkText* paragraph1 = ATK_TEXT(atk_object_ref_accessible_child(object, 0));
725     g_assert(ATK_IS_TEXT(paragraph1));
726     g_assert(ATK_IS_HYPERTEXT(paragraph1));
727
728     const gchar* expectedText = "Choose: \357\277\274foo \357\277\274bar (pick one)";
729     char* text = atk_text_get_text(paragraph1, 0, -1);
730     g_assert_cmpstr(text, ==, expectedText);
731     g_free(text);
732
733     gint nLinks = atk_hypertext_get_n_links(ATK_HYPERTEXT(paragraph1));
734     g_assert_cmpint(nLinks, ==, 2);
735
736     AtkHyperlink* hLink = atk_hypertext_get_link(ATK_HYPERTEXT(paragraph1), 0);
737     g_assert(ATK_HYPERLINK(hLink));
738     AtkObject* hLinkObject = atk_hyperlink_get_object(hLink, 0);
739     g_assert(ATK_OBJECT(hLinkObject));
740     g_assert(atk_object_get_role(hLinkObject) == ATK_ROLE_CHECK_BOX);
741     g_assert_cmpint(atk_hyperlink_get_start_index(hLink), ==, 8);
742     g_assert_cmpint(atk_hyperlink_get_end_index(hLink), ==, 9);
743     g_assert_cmpint(atk_hyperlink_get_n_anchors(hLink), ==, 1);
744     g_assert_cmpstr(atk_hyperlink_get_uri(hLink, 0), ==, 0);
745
746     AtkText* paragraph2 = ATK_TEXT(atk_object_ref_accessible_child(object, 1));
747     g_assert(ATK_IS_TEXT(paragraph2));
748     g_assert(ATK_IS_HYPERTEXT(paragraph2));
749
750     expectedText = "Choose: \357\277\274 (pick one)";
751     text = atk_text_get_text(paragraph2, 0, -1);
752     g_assert_cmpstr(text, ==, expectedText);
753     g_free(text);
754
755     nLinks = atk_hypertext_get_n_links(ATK_HYPERTEXT(paragraph2));
756     g_assert_cmpint(nLinks, ==, 1);
757
758     hLink = atk_hypertext_get_link(ATK_HYPERTEXT(paragraph2), 0);
759     g_assert(ATK_HYPERLINK(hLink));
760     hLinkObject = atk_hyperlink_get_object(hLink, 0);
761     g_assert(ATK_OBJECT(hLinkObject));
762     g_assert(atk_object_get_role(hLinkObject) == ATK_ROLE_COMBO_BOX);
763     g_assert_cmpint(atk_hyperlink_get_start_index(hLink), ==, 8);
764     g_assert_cmpint(atk_hyperlink_get_end_index(hLink), ==, 9);
765     g_assert_cmpint(atk_hyperlink_get_n_anchors(hLink), ==, 1);
766     g_assert_cmpstr(atk_hyperlink_get_uri(hLink, 0), ==, 0);
767
768     AtkText* paragraph3 = ATK_TEXT(atk_object_ref_accessible_child(object, 2));
769     g_assert(ATK_IS_TEXT(paragraph3));
770     g_assert(ATK_IS_HYPERTEXT(paragraph3));
771
772     expectedText = "\357\277\274";
773     text = atk_text_get_text(paragraph3, 0, -1);
774     g_assert_cmpstr(text, ==, expectedText);
775     g_free(text);
776
777     nLinks = atk_hypertext_get_n_links(ATK_HYPERTEXT(paragraph3));
778     g_assert_cmpint(nLinks, ==, 1);
779
780     hLink = atk_hypertext_get_link(ATK_HYPERTEXT(paragraph3), 0);
781     g_assert(ATK_HYPERLINK(hLink));
782     hLinkObject = atk_hyperlink_get_object(hLink, 0);
783     g_assert(ATK_OBJECT(hLinkObject));
784     g_assert(atk_object_get_role(hLinkObject) == ATK_ROLE_PUSH_BUTTON);
785     g_assert_cmpint(atk_hyperlink_get_start_index(hLink), ==, 0);
786     g_assert_cmpint(atk_hyperlink_get_end_index(hLink), ==, 1);
787     g_assert_cmpint(atk_hyperlink_get_n_anchors(hLink), ==, 1);
788     g_assert_cmpstr(atk_hyperlink_get_uri(hLink, 0), ==, 0);
789
790     g_object_unref(paragraph1);
791     g_object_unref(paragraph2);
792     g_object_unref(paragraph3);
793     g_object_unref(webView);
794 }
795
796 static void testWebkitAtkGetTextAtOffset()
797 {
798     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
799     g_object_ref_sink(webView);
800     GtkAllocation allocation = { 0, 0, 800, 600 };
801     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
802     webkit_web_view_load_string(webView, contents, 0, 0, 0);
803
804     /* Get to the inner AtkText object. */
805     AtkObject* object = getWebAreaObject(webView);
806     g_assert(object);
807     object = atk_object_ref_accessible_child(object, 0);
808     g_assert(object);
809
810     AtkText* textObject = ATK_TEXT(object);
811     g_assert(ATK_IS_TEXT(textObject));
812
813     runGetTextTests(textObject);
814
815     g_object_unref(object);
816     g_object_unref(webView);
817 }
818
819 static void testWebkitAtkGetTextAtOffsetNewlines()
820 {
821     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
822     g_object_ref_sink(webView);
823     GtkAllocation allocation = { 0, 0, 800, 600 };
824     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
825     webkit_web_view_load_string(webView, contentsWithNewlines, 0, 0, 0);
826
827     /* Get to the inner AtkText object. */
828     AtkObject* object = getWebAreaObject(webView);
829     g_assert(object);
830     object = atk_object_ref_accessible_child(object, 0);
831     g_assert(object);
832
833     AtkText* textObject = ATK_TEXT(object);
834     g_assert(ATK_IS_TEXT(textObject));
835
836     runGetTextTests(textObject);
837
838     g_object_unref(object);
839     g_object_unref(webView);
840 }
841
842 static void testWebkitAtkGetTextAtOffsetTextarea()
843 {
844     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
845     g_object_ref_sink(webView);
846     GtkAllocation allocation = { 0, 0, 800, 600 };
847     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
848     webkit_web_view_load_string(webView, contentsInTextarea, 0, 0, 0);
849
850     /* Get to the inner AtkText object. */
851     AtkObject* object = getWebAreaObject(webView);
852     g_assert(object);
853     AtkObject* child = atk_object_ref_accessible_child(object, 0);
854     g_assert(child);
855     AtkObject* grandchild = atk_object_ref_accessible_child(child, 0);
856     g_assert(grandchild);
857
858     AtkText* textObject = ATK_TEXT(grandchild);
859     g_assert(ATK_IS_TEXT(textObject));
860
861     runGetTextTests(textObject);
862
863     g_object_unref(child);
864     g_object_unref(grandchild);
865     g_object_unref(webView);
866 }
867
868 static void testWebkitAtkGetTextAtOffsetTextInput()
869 {
870     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
871     g_object_ref_sink(webView);
872     GtkAllocation allocation = { 0, 0, 800, 600 };
873     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
874     webkit_web_view_load_string(webView, contentsInTextInput, 0, 0, 0);
875
876     /* Get to the inner AtkText object. */
877     AtkObject* object = getWebAreaObject(webView);
878     g_assert(object);
879     AtkObject* child = atk_object_ref_accessible_child(object, 0);
880     g_assert(child);
881     AtkObject* grandchild = atk_object_ref_accessible_child(child, 0);
882     g_assert(grandchild);
883
884     AtkText* textObject = ATK_TEXT(grandchild);
885     g_assert(ATK_IS_TEXT(textObject));
886
887     runGetTextTests(textObject);
888
889     g_object_unref(child);
890     g_object_unref(grandchild);
891     g_object_unref(webView);
892 }
893
894 static void testWebkitAtkGetTextAtOffsetWithPreformattedText()
895 {
896     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
897     g_object_ref_sink(webView);
898     GtkAllocation allocation = { 0, 0, 800, 600 };
899     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
900     webkit_web_view_load_string(webView, contentsWithPreformattedText, 0, 0, 0);
901
902     AtkObject* object = getWebAreaObject(webView);
903     g_assert(object);
904
905     AtkObject* preformattedText = atk_object_ref_accessible_child(object, 0);
906     g_assert(ATK_IS_OBJECT(preformattedText));
907     g_assert(atk_object_get_role(preformattedText) == ATK_ROLE_PANEL);
908     g_assert(ATK_IS_TEXT(preformattedText));
909     char* text = atk_text_get_text(ATK_TEXT(preformattedText), 0, -1);
910     g_assert_cmpstr(text, ==, "\t\n\tfirst line\n\tsecond line\n");
911     g_free(text);
912
913     /* Try retrieving all the lines indicating the position of the offsets at the beginning of each of them. */
914     testGetTextFunction(ATK_TEXT(preformattedText), atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_START, 0, "\t\n", 0, 2);
915     testGetTextFunction(ATK_TEXT(preformattedText), atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_START, 2, "\tfirst line\n", 2, 14);
916     testGetTextFunction(ATK_TEXT(preformattedText), atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_START, 14, "\tsecond line\n", 14, 27);
917
918     g_object_unref(preformattedText);
919     g_object_unref(webView);
920 }
921
922 static void testWebkitAtkGetTextAtOffsetWithSpecialCharacters()
923 {
924     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
925     g_object_ref_sink(webView);
926     GtkAllocation allocation = { 0, 0, 800, 600 };
927     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
928     webkit_web_view_load_string(webView, contentsWithSpecialChars, 0, 0, 0);
929
930     /* Get to the inner AtkText object. */
931     AtkObject* object = getWebAreaObject(webView);
932     g_assert(object);
933
934     AtkObject* paragraph = atk_object_ref_accessible_child(object, 0);
935     g_assert(ATK_IS_TEXT(paragraph));
936
937     gchar* expectedText = g_strdup("\302\253\302\240This is a paragraph with \342\200\234special\342\200\235 characters inside.\302\240\302\273");
938     char* text = atk_text_get_text(ATK_TEXT(paragraph), 0, -1);
939     g_assert_cmpstr(text, ==, expectedText);
940     g_free(text);
941
942     /* Check that getting the text with ATK_TEXT_BOUNDARY_LINE_START
943        and ATK_TEXT_BOUNDARY_LINE_END does not crash because of not
944        properly handling characters inside the UTF-8 string. */
945     testGetTextFunction(ATK_TEXT(paragraph), atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_START, 0, expectedText, 0, 57);
946     testGetTextFunction(ATK_TEXT(paragraph), atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_END, 0, expectedText, 0, 57);
947     g_free(expectedText);
948
949     AtkObject* list = atk_object_ref_accessible_child(object, 1);
950     g_assert(ATK_OBJECT(list));
951
952     AtkText* listItem = ATK_TEXT(atk_object_ref_accessible_child(list, 0));
953     g_assert(ATK_IS_TEXT(listItem));
954
955     text = atk_text_get_text(ATK_TEXT(listItem), 0, -1);
956     g_assert_cmpstr(text, ==, "\342\200\242 List item with some text that wraps across different lines.");
957     g_free(text);
958
959     /* Check that getting the text with ATK_TEXT_BOUNDARY_LINE_START
960        and ATK_TEXT_BOUNDARY_LINE_END for line items with bullets
961        (special character) and wrapped text always return the right
962        piece of text for each line. */
963     testGetTextFunction(ATK_TEXT(listItem), atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_START, 3, "\342\200\242 List item ", 0, 12);
964     testGetTextFunction(ATK_TEXT(listItem), atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_START, 13, "with some ", 12, 22);
965     testGetTextFunction(ATK_TEXT(listItem), atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_END, 0, "\342\200\242 List item", 0, 11);
966     testGetTextFunction(ATK_TEXT(listItem), atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_END, 12, " with some", 11, 21);
967
968     g_object_unref(listItem);
969
970     listItem = ATK_TEXT(atk_object_ref_accessible_child(list, 1));
971     g_assert(ATK_IS_TEXT(listItem));
972
973     /* Check that placing the same text in a paragraph doesn't break things. */
974     text = atk_text_get_text(ATK_TEXT(listItem), 0, -1);
975     g_assert_cmpstr(text, ==, "\342\200\242 List item with some text that wraps across different lines.");
976     g_free(text);
977
978     testGetTextFunction(ATK_TEXT(listItem), atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_START, 3, "\342\200\242 List item ", 0, 12);
979     testGetTextFunction(ATK_TEXT(listItem), atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_START, 13, "with some ", 12, 22);
980     testGetTextFunction(ATK_TEXT(listItem), atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_END, 0, "\342\200\242 List item", 0, 11);
981     testGetTextFunction(ATK_TEXT(listItem), atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_END, 12, " with some", 11, 21);
982
983     g_object_unref(list);
984     g_object_unref(listItem);
985     g_object_unref(paragraph);
986     g_object_unref(webView);
987 }
988
989 static void testWebkitAtkGetTextAtOffsetWithWrappedLines()
990 {
991     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
992     g_object_ref_sink(webView);
993     GtkAllocation allocation = { 0, 0, 800, 600 };
994     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
995     webkit_web_view_load_string(webView, contentsWithWrappedLines, 0, 0, 0);
996
997     /* Enable caret browsing. */
998     WebKitWebSettings* settings = webkit_web_view_get_settings(webView);
999     g_object_set(settings, "enable-caret-browsing", TRUE, NULL);
1000     webkit_web_view_set_settings(webView, settings);
1001
1002     /* Get to the inner AtkText object. */
1003     AtkObject* object = getWebAreaObject(webView);
1004     g_assert(object);
1005
1006     /* Check the paragraph with the text wrapped because of max-width. */
1007     AtkText* paragraph1 = ATK_TEXT(atk_object_ref_accessible_child(object, 0));
1008     g_assert(ATK_IS_TEXT(paragraph1));
1009
1010     gchar* text = atk_text_get_text(paragraph1, 0, -1);
1011     g_assert_cmpstr(text, ==, "This is one line wrapped because of the maximum width of its container.");
1012     g_free(text);
1013
1014     testGetTextFunction(paragraph1, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_CHAR, 16, "e", 15, 16);
1015     testGetTextFunction(paragraph1, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_CHAR, 16, " ", 16, 17);
1016     testGetTextFunction(paragraph1, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_CHAR, 16, "w", 17, 18);
1017
1018     testGetTextFunction(paragraph1, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_WORD_START, 16, "one ", 8, 12);
1019     testGetTextFunction(paragraph1, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START, 16, "line ", 12, 17);
1020     testGetTextFunction(paragraph1, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_WORD_START, 16, "wrapped ", 17, 25);
1021
1022     testGetTextFunction(paragraph1, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_WORD_END, 16, " line", 11, 16);
1023     testGetTextFunction(paragraph1, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END, 16, " wrapped", 16, 24);
1024     testGetTextFunction(paragraph1, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_WORD_END, 16, " because", 24, 32);
1025
1026     testGetTextFunction(paragraph1, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_LINE_START, 17, "This is one line ", 0, 17);
1027     testGetTextFunction(paragraph1, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_START, 17, "wrapped because ", 17, 33);
1028     testGetTextFunction(paragraph1, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_LINE_START, 17, "of the maximum ", 33, 48);
1029
1030     /* The following line won't work at the moment because of a bug in GailTextUtil.
1031        see https://bugzilla.gnome.org/show_bug.cgi?id=703554
1032        testGetTextFunction(paragraph1, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_LINE_END, 17, "This is one line", 0, 16); */
1033     testGetTextFunction(paragraph1, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_END, 17, " wrapped because", 16, 32);
1034     testGetTextFunction(paragraph1, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_LINE_END, 17, " of the maximum", 32, 47);
1035
1036     g_object_unref(paragraph1);
1037
1038     /* Check the paragraph with the text wrapped because of <br> elements. */
1039     AtkText* paragraph2 = ATK_TEXT(atk_object_ref_accessible_child(object, 1));
1040     g_assert(ATK_IS_TEXT(paragraph2));
1041
1042     text = atk_text_get_text(paragraph2, 0, -1);
1043     g_assert_cmpstr(text, ==, "This is another line wrapped\nbecause of one forced\nline break in the middle.");
1044     g_free(text);
1045
1046     testGetTextFunction(paragraph2, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_CHAR, 28, "d", 27, 28);
1047     testGetTextFunction(paragraph2, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_CHAR, 28, "\n", 28, 29);
1048     testGetTextFunction(paragraph2, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_CHAR, 28, "b", 29, 30);
1049
1050     testGetTextFunction(paragraph2, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_WORD_START, 28, "line ", 16, 21);
1051     testGetTextFunction(paragraph2, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START, 28, "wrapped\n", 21, 29);
1052     testGetTextFunction(paragraph2, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_WORD_START, 28, "because ", 29, 37);
1053
1054     testGetTextFunction(paragraph2, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_WORD_END, 28, " wrapped", 20, 28);
1055     testGetTextFunction(paragraph2, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END, 28, "\nbecause", 28, 36);
1056     testGetTextFunction(paragraph2, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_WORD_END, 28, " of", 36, 39);
1057
1058     testGetTextFunction(paragraph2, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_LINE_START, 30, "This is another line wrapped\n", 0, 29);
1059     testGetTextFunction(paragraph2, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_START, 30, "because of one forced\n", 29, 51);
1060     testGetTextFunction(paragraph2, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_LINE_START, 30, "line break in the middle.", 51, 76);
1061
1062     /* The following line won't work at the moment because of a bug in GailTextUtil.
1063        see https://bugzilla.gnome.org/show_bug.cgi?id=703554
1064        testGetTextFunction(paragraph2, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_LINE_END, 30, "This is another line wrapped", 0, 28); */
1065     testGetTextFunction(paragraph2, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_END, 30, "\nbecause of one forced", 28, 50);
1066     testGetTextFunction(paragraph2, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_LINE_END, 30, "\nline break in the middle.", 50, 76);
1067
1068     g_object_unref(paragraph2);
1069
1070     g_object_unref(webView);
1071 }
1072
1073 static void testWebkitAtkGetTextAtOffsetWithEmbeddedObjects()
1074 {
1075     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
1076     g_object_ref_sink(webView);
1077     GtkAllocation allocation = { 0, 0, 800, 600 };
1078     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
1079     webkit_web_view_load_string(webView, contentsWithEmbeddedObjects, 0, 0, 0);
1080
1081     /* Enable caret browsing. */
1082     WebKitWebSettings* settings = webkit_web_view_get_settings(webView);
1083     g_object_set(settings, "enable-caret-browsing", TRUE, NULL);
1084     webkit_web_view_set_settings(webView, settings);
1085
1086     /* Get to the inner AtkText object. */
1087     AtkObject* object = getWebAreaObject(webView);
1088     g_assert(object);
1089
1090     /* Check the paragraph with the text wrapped because of max-width. */
1091     AtkText* paragraph = ATK_TEXT(atk_object_ref_accessible_child(object, 0));
1092     g_assert(ATK_IS_TEXT(paragraph));
1093
1094     gchar* text = atk_text_get_text(paragraph, 0, -1);
1095     g_assert_cmpstr(text, ==, "This is one line containing two \357\277\274 embedded objects \357\277\274 in the middle.");
1096     g_free(text);
1097
1098     /* Check right before the first embedded object */
1099     testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_CHAR, 32, "\357\277\274", 32, 33);
1100     testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START, 32, "two \357\277\274 ", 28, 34);
1101     testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END, 32, " \357\277\274 embedded", 31, 42);
1102
1103     /* Check right after the first embedded object (and before the first word after it) */
1104     testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_CHAR, 33, " ", 33, 34);
1105     testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START, 33, "two \357\277\274 ", 28, 34);
1106     testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END, 33, " \357\277\274 embedded", 31, 42);
1107
1108     /* Check at the beginning of the first word between the two embedded objects */
1109     testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_CHAR, 34, "e", 34, 35);
1110     testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START, 34, "embedded ", 34, 43);
1111     testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END, 34, " \357\277\274 embedded", 31, 42);
1112
1113     /* Check at the end of the first word between the two embedded objects */
1114     testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_CHAR, 42, " ", 42, 43);
1115     testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START, 42, "embedded ", 34, 43);
1116     testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END, 42, " objects", 42, 50);
1117
1118     /* Check right before the second embedded object */
1119     testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_CHAR, 51, "\357\277\274", 51, 52);
1120     testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START, 51, "objects \357\277\274 ", 43, 53);
1121     testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END, 51, " \357\277\274 in", 50, 55);
1122
1123     /* Check right after the second embedded object (and before the first word after it) */
1124     testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_CHAR, 52, " ", 52, 53);
1125     testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START, 52, "objects \357\277\274 ", 43, 53);
1126     testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END, 52, " \357\277\274 in", 50, 55);
1127
1128     /* Check at the beginning of the first word after the two embedded objects */
1129     testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_CHAR, 53, "i", 53, 54);
1130     testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START, 53, "in ", 53, 56);
1131     testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END, 53, " \357\277\274 in", 50, 55);
1132
1133     /* Check at the end of the first word after the two embedded objects */
1134     testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_CHAR, 55, " ", 55, 56);
1135     testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START, 55, "in ", 53, 56);
1136     testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END, 55, " the", 55, 59);
1137
1138     g_object_unref(paragraph);
1139     g_object_unref(webView);
1140 }
1141
1142 static void testWebkitAtkGetTextInParagraphAndBodySimple()
1143 {
1144     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
1145     g_object_ref_sink(webView);
1146     GtkAllocation allocation = { 0, 0, 800, 600 };
1147     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
1148     webkit_web_view_load_string(webView, contentsInParagraphAndBodySimple, 0, 0, 0);
1149
1150     /* Get to the inner AtkText object. */
1151     AtkObject* object = getWebAreaObject(webView);
1152     g_assert(object);
1153     AtkObject* object1 = atk_object_ref_accessible_child(object, 0);
1154     g_assert(object1);
1155     AtkObject* object2 = atk_object_ref_accessible_child(object, 1);
1156     g_assert(object2);
1157
1158     AtkText* textObject1 = ATK_TEXT(object1);
1159     g_assert(ATK_IS_TEXT(textObject1));
1160     AtkText* textObject2 = ATK_TEXT(object2);
1161     g_assert(ATK_IS_TEXT(textObject2));
1162
1163     char *text = atk_text_get_text(textObject1, 0, -1);
1164     g_assert_cmpstr(text, ==, "This is a test.");
1165     g_free(text);
1166
1167     text = atk_text_get_text(textObject2, 0, 12);
1168     g_assert_cmpstr(text, ==, "Hello world.");
1169     g_free(text);
1170
1171     g_object_unref(object1);
1172     g_object_unref(object2);
1173     g_object_unref(webView);
1174 }
1175
1176 static void testWebkitAtkGetTextInParagraphAndBodyModerate()
1177 {
1178     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
1179     g_object_ref_sink(webView);
1180     GtkAllocation allocation = { 0, 0, 800, 600 };
1181     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
1182     webkit_web_view_load_string(webView, contentsInParagraphAndBodyModerate, 0, 0, 0);
1183
1184     /* Get to the inner AtkText object. */
1185     AtkObject* object = getWebAreaObject(webView);
1186     g_assert(object);
1187     AtkObject* object1 = atk_object_ref_accessible_child(object, 0);
1188     g_assert(object1);
1189     AtkObject* object2 = atk_object_ref_accessible_child(object, 1);
1190     g_assert(object2);
1191
1192     AtkText* textObject1 = ATK_TEXT(object1);
1193     g_assert(ATK_IS_TEXT(textObject1));
1194     AtkText* textObject2 = ATK_TEXT(object2);
1195     g_assert(ATK_IS_TEXT(textObject2));
1196
1197     char *text = atk_text_get_text(textObject1, 0, -1);
1198     g_assert_cmpstr(text, ==, "This is a test.");
1199     g_free(text);
1200
1201     text = atk_text_get_text(textObject2, 0, 53);
1202     g_assert_cmpstr(text, ==, "Hello world.\nThis sentence is green.\nThis one is not.");
1203     g_free(text);
1204
1205     g_object_unref(object1);
1206     g_object_unref(object2);
1207     g_object_unref(webView);
1208 }
1209
1210 static void testWebkitAtkGetTextInTable()
1211 {
1212     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
1213     g_object_ref_sink(webView);
1214     GtkAllocation allocation = { 0, 0, 800, 600 };
1215     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
1216     webkit_web_view_load_string(webView, contentsInTable, 0, 0, 0);
1217
1218     AtkObject* object = getWebAreaObject(webView);
1219     g_assert(object);
1220     object = atk_object_ref_accessible_child(object, 0);
1221     g_assert(object);
1222
1223     /* Tables should not implement AtkText. */
1224     g_assert(!G_TYPE_INSTANCE_GET_INTERFACE(object, ATK_TYPE_TEXT, AtkTextIface));
1225
1226     g_object_unref(object);
1227     g_object_unref(webView);
1228 }
1229
1230 static void testWebkitAtkGetHeadersInTable()
1231 {
1232     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
1233     g_object_ref_sink(webView);
1234     GtkAllocation allocation = { 0, 0, 800, 600 };
1235     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
1236     webkit_web_view_load_string(webView, contentsInTableWithHeaders, 0, 0, 0);
1237
1238     AtkObject* axWebView = getWebAreaObject(webView);
1239     g_assert(axWebView);
1240
1241     /* Check table with both column and row headers. */
1242     AtkObject* table = atk_object_ref_accessible_child(axWebView, 0);
1243     g_assert(table);
1244     g_assert(atk_object_get_role(table) == ATK_ROLE_TABLE);
1245
1246     AtkObject* colHeader = atk_table_get_column_header(ATK_TABLE(table), 0);
1247     g_assert(colHeader);
1248     g_assert(atk_object_get_role(colHeader) == ATK_ROLE_TABLE_CELL);
1249     g_assert(atk_object_get_index_in_parent(colHeader) == 0);
1250
1251     colHeader = atk_table_get_column_header(ATK_TABLE(table), 1);
1252     g_assert(colHeader);
1253     g_assert(atk_object_get_role(colHeader) == ATK_ROLE_TABLE_CELL);
1254     g_assert(atk_object_get_index_in_parent(colHeader) == 1);
1255
1256     colHeader = atk_table_get_column_header(ATK_TABLE(table), 2);
1257     g_assert(colHeader);
1258     g_assert(atk_object_get_role(colHeader) == ATK_ROLE_TABLE_CELL);
1259     g_assert(atk_object_get_index_in_parent(colHeader) == 2);
1260
1261     colHeader = atk_table_get_column_header(ATK_TABLE(table), 3);
1262     g_assert(colHeader);
1263     g_assert(atk_object_get_role(colHeader) == ATK_ROLE_TABLE_CELL);
1264     g_assert(atk_object_get_index_in_parent(colHeader) == 2);
1265
1266     AtkObject* rowHeader = atk_table_get_row_header(ATK_TABLE(table), 0);
1267     g_assert(rowHeader);
1268     g_assert(atk_object_get_role(rowHeader) == ATK_ROLE_TABLE_CELL);
1269     g_assert(atk_object_get_index_in_parent(rowHeader) == 0);
1270
1271     rowHeader = atk_table_get_row_header(ATK_TABLE(table), 1);
1272     g_assert(rowHeader);
1273     g_assert(atk_object_get_role(rowHeader) == ATK_ROLE_TABLE_CELL);
1274     g_assert(atk_object_get_index_in_parent(rowHeader) == 3);
1275
1276     rowHeader = atk_table_get_row_header(ATK_TABLE(table), 2);
1277     g_assert(rowHeader);
1278     g_assert(atk_object_get_role(rowHeader) == ATK_ROLE_TABLE_CELL);
1279     g_assert(atk_object_get_index_in_parent(rowHeader) == 7);
1280
1281     rowHeader = atk_table_get_row_header(ATK_TABLE(table), 3);
1282     g_assert(rowHeader);
1283     g_assert(atk_object_get_role(rowHeader) == ATK_ROLE_TABLE_CELL);
1284     g_assert(atk_object_get_index_in_parent(rowHeader) == 7);
1285
1286     g_object_unref(table);
1287
1288     /* Check table with no headers at all. */
1289     table = atk_object_ref_accessible_child(axWebView, 1);
1290     g_assert(table);
1291     g_assert(atk_object_get_role(table) == ATK_ROLE_TABLE);
1292
1293     colHeader = atk_table_get_column_header(ATK_TABLE(table), 0);
1294     g_assert(colHeader == 0);
1295
1296     colHeader = atk_table_get_column_header(ATK_TABLE(table), 1);
1297     g_assert(colHeader == 0);
1298
1299     rowHeader = atk_table_get_row_header(ATK_TABLE(table), 0);
1300     g_assert(rowHeader == 0);
1301
1302     rowHeader = atk_table_get_row_header(ATK_TABLE(table), 1);
1303     g_assert(rowHeader == 0);
1304
1305     g_object_unref(table);
1306     g_object_unref(webView);
1307 }
1308
1309 static gint compAtkAttribute(AtkAttribute* a1, AtkAttribute* a2)
1310 {
1311     gint strcmpVal = g_strcmp0(a1->name, a2->name);
1312     if (strcmpVal)
1313         return strcmpVal;
1314     return g_strcmp0(a1->value, a2->value);
1315 }
1316
1317 static gint compAtkAttributeName(AtkAttribute* a1, AtkAttribute* a2)
1318 {
1319     return g_strcmp0(a1->name, a2->name);
1320 }
1321
1322 static gboolean atkAttributeSetAttributeNameHasValue(AtkAttributeSet* set, const gchar* attributeName, const gchar* value)
1323 {
1324     AtkAttribute at;
1325     at.name = (gchar*)attributeName;
1326     GSList* element = g_slist_find_custom(set, &at, (GCompareFunc)compAtkAttributeName);
1327     return element && !g_strcmp0(((AtkAttribute*)(element->data))->value, value);
1328 }
1329
1330 static gboolean atkAttributeSetContainsAttributeName(AtkAttributeSet* set, const gchar* attributeName)
1331 {
1332     AtkAttribute at;
1333     at.name = (gchar*)attributeName;
1334     return g_slist_find_custom(set, &at, (GCompareFunc)compAtkAttributeName) ? true : false;
1335 }
1336
1337 static gboolean atkAttributeSetAttributeHasValue(AtkAttributeSet* set, AtkTextAttribute attribute, const gchar* value)
1338 {
1339     return atkAttributeSetAttributeNameHasValue(set, atk_text_attribute_get_name(attribute), value);
1340 }
1341
1342 static gboolean atkAttributeSetAreEqual(AtkAttributeSet* set1, AtkAttributeSet* set2)
1343 {
1344     if (!set1)
1345         return !set2;
1346
1347     set1 = g_slist_sort(set1, (GCompareFunc)compAtkAttribute);
1348     set2 = g_slist_sort(set2, (GCompareFunc)compAtkAttribute);
1349
1350     while (set1) {
1351         if (!set2 || compAtkAttribute(set1->data, set2->data))
1352             return FALSE;
1353
1354         set1 = set1->next;
1355         set2 = set2->next;
1356     }
1357
1358     return (!set2);
1359 }
1360
1361 static void testWebkitAtkTextAttributes()
1362 {
1363     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
1364     g_object_ref_sink(webView);
1365     GtkAllocation allocation = { 0, 0, 800, 600 };
1366     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
1367     webkit_web_view_load_string(webView, textWithAttributes, 0, 0, 0);
1368
1369     AtkObject* object = getWebAreaObject(webView);
1370     g_assert(object);
1371
1372     AtkObject* child = atk_object_ref_accessible_child(object, 0);
1373     g_assert(child && ATK_IS_TEXT(child));
1374     AtkText* childText = ATK_TEXT(child);
1375
1376     gint startOffset;
1377     gint endOffset;
1378     AtkAttributeSet* set1 = atk_text_get_run_attributes(childText, 0, &startOffset, &endOffset);
1379     g_assert_cmpint(startOffset, ==, 0);
1380     g_assert_cmpint(endOffset, ==, 12);
1381     g_assert(atkAttributeSetAreEqual(set1, 0));
1382
1383     AtkAttributeSet* set2 = atk_text_get_run_attributes(childText, 15, &startOffset, &endOffset);
1384     g_assert_cmpint(startOffset, ==, 12);
1385     g_assert_cmpint(endOffset, ==, 17);
1386     g_assert(atkAttributeSetAttributeHasValue(set2, ATK_TEXT_ATTR_STYLE, "italic"));
1387
1388     AtkAttributeSet* set3 = atk_text_get_run_attributes(childText, 17, &startOffset, &endOffset);
1389     g_assert_cmpint(startOffset, ==, 17);
1390     g_assert_cmpint(endOffset, ==, 40);
1391     g_assert(atkAttributeSetAttributeHasValue(set3, ATK_TEXT_ATTR_WEIGHT, "700"));
1392
1393     AtkAttributeSet* set4 = atk_text_get_default_attributes(childText);
1394     g_assert(atkAttributeSetAttributeHasValue(set4, ATK_TEXT_ATTR_STYLE, "normal"));
1395     g_assert(atkAttributeSetAttributeHasValue(set4, ATK_TEXT_ATTR_JUSTIFICATION, "right"));
1396     g_assert(atkAttributeSetAttributeHasValue(set4, ATK_TEXT_ATTR_SIZE, "14"));
1397     atk_attribute_set_free(set1);
1398     atk_attribute_set_free(set2);
1399     atk_attribute_set_free(set3);
1400     atk_attribute_set_free(set4);
1401
1402     g_object_unref(child);
1403     child = atk_object_ref_accessible_child(object, 1);
1404     g_assert(child && ATK_IS_TEXT(child));
1405     childText = ATK_TEXT(child);
1406
1407     set1 = atk_text_get_default_attributes(childText);
1408     g_assert(atkAttributeSetAttributeHasValue(set1, ATK_TEXT_ATTR_FAMILY_NAME, "monospace"));
1409     g_assert(atkAttributeSetAttributeHasValue(set1, ATK_TEXT_ATTR_STYLE, "normal"));
1410     g_assert(atkAttributeSetAttributeHasValue(set1, ATK_TEXT_ATTR_STRIKETHROUGH, "false"));
1411     g_assert(atkAttributeSetAttributeHasValue(set1, ATK_TEXT_ATTR_WEIGHT, "400"));
1412     g_assert(atkAttributeSetAttributeHasValue(set1, ATK_TEXT_ATTR_FG_COLOR, "120,121,122"));
1413
1414     set2 = atk_text_get_run_attributes(childText, 43, &startOffset, &endOffset);
1415     g_assert_cmpint(startOffset, ==, 43);
1416     g_assert_cmpint(endOffset, ==, 80);
1417     /* Checks that default attributes of text are not returned when called to atk_text_get_run_attributes. */
1418     g_assert(!atkAttributeSetAttributeHasValue(set2, ATK_TEXT_ATTR_FG_COLOR, "120,121,122"));
1419     g_assert(atkAttributeSetAttributeHasValue(set2, ATK_TEXT_ATTR_UNDERLINE, "single"));
1420     g_assert(atkAttributeSetAttributeHasValue(set2, ATK_TEXT_ATTR_BG_COLOR, "80,81,82"));
1421     atk_attribute_set_free(set1);
1422     atk_attribute_set_free(set2);
1423     g_object_unref(child);
1424
1425     child = atk_object_ref_accessible_child(object, 2);
1426     g_assert(child && ATK_IS_TEXT(child));
1427     childText = ATK_TEXT(child);
1428
1429     set1 = atk_text_get_run_attributes(childText, 0, &startOffset, &endOffset);
1430     set2 = atk_text_get_run_attributes(childText, 3, &startOffset, &endOffset);
1431     g_assert(atkAttributeSetAreEqual(set1, set2));
1432     atk_attribute_set_free(set2);
1433
1434     set2 = atk_text_get_run_attributes(childText, 1, &startOffset, &endOffset);
1435     set3 = atk_text_get_run_attributes(childText, 5, &startOffset, &endOffset);
1436     g_assert(atkAttributeSetAreEqual(set2, set3));
1437     g_assert(!atkAttributeSetAreEqual(set1, set2));
1438     atk_attribute_set_free(set3);
1439
1440     set3 = atk_text_get_run_attributes(childText, 2, &startOffset, &endOffset);
1441     set4 = atk_text_get_run_attributes(childText, 6, &startOffset, &endOffset);
1442     g_assert(atkAttributeSetAreEqual(set3, set4));
1443     g_assert(!atkAttributeSetAreEqual(set1, set3));
1444     g_assert(!atkAttributeSetAreEqual(set2, set3));
1445     atk_attribute_set_free(set1);
1446     atk_attribute_set_free(set2);
1447     atk_attribute_set_free(set3);
1448     atk_attribute_set_free(set4);
1449     g_object_unref(child);
1450
1451     child = atk_object_ref_accessible_child(object, 3);
1452     g_assert(child && ATK_IS_TEXT(child));
1453     childText = ATK_TEXT(child);
1454     set1 = atk_text_get_run_attributes(childText, 24, &startOffset, &endOffset);
1455     g_assert_cmpint(startOffset, ==, 21);
1456     g_assert_cmpint(endOffset, ==, 25);
1457     g_assert(atkAttributeSetAttributeHasValue(set1, ATK_TEXT_ATTR_STRIKETHROUGH, "true"));
1458
1459     set2 = atk_text_get_run_attributes(childText, 25, &startOffset, &endOffset);
1460     g_assert_cmpint(startOffset, ==, 25);
1461     g_assert_cmpint(endOffset, ==, 30);
1462     g_assert(atkAttributeSetAreEqual(set2, 0));
1463
1464     set3 = atk_text_get_default_attributes(childText);
1465     g_assert(atkAttributeSetAttributeHasValue(set3, ATK_TEXT_ATTR_JUSTIFICATION, "center"));
1466     atk_attribute_set_free(set1);
1467     atk_attribute_set_free(set2);
1468     atk_attribute_set_free(set3);
1469
1470     g_object_unref(child);
1471 }
1472
1473 static gchar* textSelectionChangedResult = 0;
1474
1475 static void textSelectionChangedCallback(AtkText* text, gpointer data)
1476 {
1477     g_assert(ATK_IS_TEXT(text));
1478
1479     g_free(textSelectionChangedResult);
1480     AtkRole role = atk_object_get_role(ATK_OBJECT(text));
1481     int startOffset = 0;
1482     int endOffset = 0;
1483     atk_text_get_selection(ATK_TEXT(text), 0, &startOffset, &endOffset);
1484     textSelectionChangedResult = g_strdup_printf("|%s|%d|%d|", atk_role_get_name(role), startOffset, endOffset);
1485 }
1486
1487 static void testWebkitAtkTextSelections()
1488 {
1489     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
1490     g_object_ref_sink(webView);
1491     GtkAllocation allocation = { 0, 0, 800, 600 };
1492     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
1493     webkit_web_view_load_string(webView, textForSelections, 0, 0, 0);
1494
1495     AtkObject* object = getWebAreaObject(webView);
1496     g_assert(object);
1497
1498     AtkText* paragraph1 = ATK_TEXT(atk_object_ref_accessible_child(object, 0));
1499     g_assert(ATK_IS_TEXT(paragraph1));
1500     g_signal_connect(paragraph1, "text-selection-changed", G_CALLBACK(textSelectionChangedCallback), 0);
1501
1502     AtkText* paragraph2 = ATK_TEXT(atk_object_ref_accessible_child(object, 1));
1503     g_assert(ATK_IS_TEXT(paragraph2));
1504     g_signal_connect(paragraph2, "text-selection-changed", G_CALLBACK(textSelectionChangedCallback), 0);
1505
1506     AtkText* link = ATK_TEXT(atk_object_ref_accessible_child(ATK_OBJECT(paragraph2), 0));
1507     g_assert(ATK_IS_TEXT(link));
1508
1509     AtkObject* list = atk_object_ref_accessible_child(object, 2);
1510     g_assert(ATK_OBJECT(list));
1511
1512     AtkText* listItem = ATK_TEXT(atk_object_ref_accessible_child(list, 0));
1513     g_assert(ATK_IS_TEXT(listItem));
1514
1515     /* First paragraph (simple text). */
1516
1517     /* Basic initial checks. */
1518     g_assert_cmpint(atk_text_get_n_selections(paragraph1), ==, 0);
1519
1520     gint startOffset;
1521     gint endOffset;
1522     gchar* selectedText = atk_text_get_selection(paragraph1, 0, &startOffset, &endOffset);
1523     g_assert_cmpint(startOffset, ==, 0);
1524     g_assert_cmpint(endOffset, ==, 0);
1525     g_assert_cmpstr(selectedText, ==, 0);
1526     g_free (selectedText);
1527
1528     /* Try removing a non existing (yet) selection. */
1529     gboolean result = atk_text_remove_selection(paragraph1, 0);
1530     g_assert(!result);
1531
1532     /* Try setting a 0-char selection. */
1533     result = atk_text_set_selection(paragraph1, 0, 5, 5);
1534     g_assert(result);
1535
1536     /* Make a selection and test it. */
1537     result = atk_text_set_selection(paragraph1, 0, 5, 25);
1538     g_assert(result);
1539     g_assert_cmpint(atk_text_get_n_selections(paragraph1), ==, 1);
1540     g_assert_cmpstr(textSelectionChangedResult, ==, "|paragraph|5|25|");
1541     selectedText = atk_text_get_selection(paragraph1, 0, &startOffset, &endOffset);
1542     g_assert_cmpint(startOffset, ==, 5);
1543     g_assert_cmpint(endOffset, ==, 25);
1544     g_assert_cmpstr(selectedText, ==, "agraph with plain te");
1545     g_free (selectedText);
1546
1547     /* Try removing the selection from other AtkText object (should fail). */
1548     result = atk_text_remove_selection(paragraph2, 0);
1549     g_assert(!result);
1550
1551     /* Remove the selection and test everything again. */
1552     result = atk_text_remove_selection(paragraph1, 0);
1553     g_assert(result);
1554     g_assert_cmpint(atk_text_get_n_selections(paragraph1), ==, 0);
1555     selectedText = atk_text_get_selection(paragraph1, 0, &startOffset, &endOffset);
1556     /* Now offsets should be the same, set to the last position of the caret. */
1557     g_assert_cmpint(startOffset, ==, endOffset);
1558     g_assert_cmpint(startOffset, ==, 25);
1559     g_assert_cmpint(endOffset, ==, 25);
1560     g_assert_cmpstr(selectedText, ==, 0);
1561     g_free (selectedText);
1562
1563     /* Second paragraph (text + link + text). */
1564
1565     /* Set a selection partially covering the link and test it. */
1566     result = atk_text_set_selection(paragraph2, 0, 7, 21);
1567     g_assert(result);
1568
1569     /* Test the paragraph first. */
1570     g_assert_cmpint(atk_text_get_n_selections(paragraph2), ==, 1);
1571     selectedText = atk_text_get_selection(paragraph2, 0, &startOffset, &endOffset);
1572     g_assert_cmpint(startOffset, ==, 7);
1573     g_assert_cmpint(endOffset, ==, 21);
1574     g_assert_cmpstr(selectedText, ==, "raph with a li");
1575     g_free (selectedText);
1576
1577     /* Now test just the link. */
1578     g_assert_cmpint(atk_text_get_n_selections(link), ==, 1);
1579     selectedText = atk_text_get_selection(link, 0, &startOffset, &endOffset);
1580     g_assert_cmpint(startOffset, ==, 0);
1581     g_assert_cmpint(endOffset, ==, 4);
1582     g_assert_cmpstr(selectedText, ==, "a li");
1583     g_free (selectedText);
1584
1585     /* Make a selection after the link and check selection for the whole paragraph. */
1586     result = atk_text_set_selection(paragraph2, 0, 27, 37);
1587     g_assert(result);
1588     g_assert_cmpint(atk_text_get_n_selections(paragraph2), ==, 1);
1589     g_assert_cmpstr(textSelectionChangedResult, ==, "|paragraph|27|37|");
1590     selectedText = atk_text_get_selection(paragraph2, 0, &startOffset, &endOffset);
1591     g_assert_cmpint(startOffset, ==, 27);
1592     g_assert_cmpint(endOffset, ==, 37);
1593     g_assert_cmpstr(selectedText, ==, "the middle");
1594     g_free (selectedText);
1595
1596     /* Remove selections and text everything again. */
1597     result = atk_text_remove_selection(paragraph2, 0);
1598     g_assert(result);
1599     g_assert_cmpint(atk_text_get_n_selections(paragraph2), ==, 0);
1600     selectedText = atk_text_get_selection(paragraph2, 0, &startOffset, &endOffset);
1601     /* Now offsets should be the same (no selection). */
1602     g_assert_cmpint(startOffset, ==, endOffset);
1603     g_assert_cmpstr(selectedText, ==, 0);
1604     g_free (selectedText);
1605
1606     g_assert_cmpint(atk_text_get_n_selections(link), ==, 0);
1607     selectedText = atk_text_get_selection(link, 0, &startOffset, &endOffset);
1608     /* Now offsets should be the same (no selection). */
1609     g_assert_cmpint(startOffset, ==, endOffset);
1610     g_assert_cmpstr(selectedText, ==, 0);
1611     g_free (selectedText);
1612
1613     /* List item */
1614
1615     g_assert(atk_object_get_role(list) == ATK_ROLE_LIST);
1616     g_assert_cmpint(atk_object_get_n_accessible_children(list), ==, 1);
1617
1618     gchar* text = atk_text_get_text(listItem, 0, -1);
1619     g_assert_cmpstr(text, ==, "1. A list item");
1620     g_free (text);
1621
1622     /* It's not possible to select text inside an item's marker. */
1623     result = atk_text_set_selection (listItem, 0, 0, 9);
1624     g_assert(!result);
1625     result = atk_text_set_selection (listItem, 0, 9, 1);
1626     g_assert(!result);
1627
1628     /* It should be possible to select text inside an item's text. */
1629     result = atk_text_set_selection (listItem, 0, 3, 9);
1630     g_assert(result);
1631
1632     g_assert_cmpint(atk_text_get_n_selections(listItem), ==, 1);
1633     selectedText = atk_text_get_selection(listItem, 0, &startOffset, &endOffset);
1634     g_assert_cmpint(startOffset, ==, 3);
1635     g_assert_cmpint(endOffset, ==, 9);
1636     g_assert_cmpstr(selectedText, ==, "A list");
1637     g_free (selectedText);
1638
1639     g_free(textSelectionChangedResult);
1640
1641     g_object_unref(paragraph1);
1642     g_object_unref(paragraph2);
1643     g_object_unref(link);
1644     g_object_unref(list);
1645     g_object_unref(listItem);
1646     g_object_unref(webView);
1647 }
1648
1649 static void testWebkitAtkGetExtents()
1650 {
1651     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
1652     g_object_ref_sink(webView);
1653     GtkAllocation allocation = { 0, 0, 800, 600 };
1654     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
1655     webkit_web_view_load_string(webView, centeredContents, 0, 0, 0);
1656
1657     AtkObject* object = getWebAreaObject(webView);
1658     g_assert(object);
1659
1660     AtkText* shortText1 = ATK_TEXT(atk_object_ref_accessible_child(object, 0));
1661     g_assert(ATK_IS_TEXT(shortText1));
1662     AtkText* longText = ATK_TEXT(atk_object_ref_accessible_child(object, 1));
1663     g_assert(ATK_IS_TEXT(longText));
1664     AtkText* shortText2 = ATK_TEXT(atk_object_ref_accessible_child(object, 2));
1665     g_assert(ATK_IS_TEXT(shortText2));
1666     AtkText* multilineText = ATK_TEXT(atk_object_ref_accessible_child(object, 3));
1667     g_assert(ATK_IS_TEXT(multilineText));
1668
1669     /* Start with window extents. */
1670     AtkTextRectangle sline_window1, sline_window2, lline_window, mline_window;
1671     atk_text_get_range_extents(shortText1, 0, 10, ATK_XY_WINDOW, &sline_window1);
1672     atk_text_get_range_extents(longText, 0, 44, ATK_XY_WINDOW, &lline_window);
1673     atk_text_get_range_extents(shortText2, 0, 10, ATK_XY_WINDOW, &sline_window2);
1674     atk_text_get_range_extents(multilineText, 0, 60, ATK_XY_WINDOW, &mline_window);
1675
1676     /* Check vertical line position. */
1677     g_assert_cmpint(sline_window1.y + sline_window1.height, <=, lline_window.y);
1678     g_assert_cmpint(lline_window.y + lline_window.height + sline_window2.height, <=, mline_window.y);
1679
1680     /* Paragraphs 1 and 3 have identical text and alignment. */
1681     g_assert_cmpint(sline_window1.x, ==, sline_window2.x);
1682     g_assert_cmpint(sline_window1.width, ==, sline_window2.width);
1683     g_assert_cmpint(sline_window1.height, ==, sline_window2.height);
1684
1685     /* All lines should be the same height; line 2 is the widest line. */
1686     g_assert_cmpint(sline_window1.height, ==, lline_window.height);
1687     g_assert_cmpint(sline_window1.width, <, lline_window.width);
1688
1689     /* Make sure the character extents jive with the range extents. */
1690     gint x;
1691     gint y;
1692     gint width;
1693     gint height;
1694
1695     /* First paragraph (short text). */
1696     atk_text_get_character_extents(shortText1, 0, &x, &y, &width, &height, ATK_XY_WINDOW);
1697     g_assert_cmpint(x, ==, sline_window1.x);
1698     g_assert_cmpint(y, ==, sline_window1.y);
1699     g_assert_cmpint(height, ==, sline_window1.height);
1700
1701     atk_text_get_character_extents(shortText1, 9, &x, &y, &width, &height, ATK_XY_WINDOW);
1702     g_assert_cmpint(x, ==, sline_window1.x + sline_window1.width - width);
1703     g_assert_cmpint(y, ==, sline_window1.y);
1704     g_assert_cmpint(height, ==, sline_window1.height);
1705
1706     /* Second paragraph (long text). */
1707     atk_text_get_character_extents(longText, 0, &x, &y, &width, &height, ATK_XY_WINDOW);
1708     g_assert_cmpint(x, ==, lline_window.x);
1709     g_assert_cmpint(y, ==, lline_window.y);
1710     g_assert_cmpint(height, ==, lline_window.height);
1711
1712     atk_text_get_character_extents(longText, 43, &x, &y, &width, &height, ATK_XY_WINDOW);
1713     g_assert_cmpint(x, ==, lline_window.x + lline_window.width - width);
1714     g_assert_cmpint(y, ==, lline_window.y);
1715     g_assert_cmpint(height, ==, lline_window.height);
1716
1717     /* Third paragraph (short text). */
1718     atk_text_get_character_extents(shortText2, 0, &x, &y, &width, &height, ATK_XY_WINDOW);
1719     g_assert_cmpint(x, ==, sline_window2.x);
1720     g_assert_cmpint(y, ==, sline_window2.y);
1721     g_assert_cmpint(height, ==, sline_window2.height);
1722
1723     atk_text_get_character_extents(shortText2, 9, &x, &y, &width, &height, ATK_XY_WINDOW);
1724     g_assert_cmpint(x, ==, sline_window2.x + sline_window2.width - width);
1725     g_assert_cmpint(y, ==, sline_window2.y);
1726     g_assert_cmpint(height, ==, sline_window2.height);
1727
1728     /* Four paragraph (3 lines multi-line text). */
1729     atk_text_get_character_extents(multilineText, 0, &x, &y, &width, &height, ATK_XY_WINDOW);
1730     g_assert_cmpint(x, ==, mline_window.x);
1731     g_assert_cmpint(y, ==, mline_window.y);
1732     g_assert_cmpint(3 * height, ==, mline_window.height);
1733
1734     atk_text_get_character_extents(multilineText, 59, &x, &y, &width, &height, ATK_XY_WINDOW);
1735     /* Last line won't fill the whole width of the rectangle. */
1736     g_assert_cmpint(x, <=, mline_window.x + mline_window.width - width);
1737     g_assert_cmpint(y, ==, mline_window.y + mline_window.height - height);
1738     g_assert_cmpint(height, <=, mline_window.height);
1739
1740     /* Check that extent for a full line are the same height than for
1741        a partial section of the same line */
1742     gint startOffset;
1743     gint endOffset;
1744     gchar* text = atk_text_get_text_at_offset(multilineText, 0, ATK_TEXT_BOUNDARY_LINE_START, &startOffset, &endOffset);
1745     g_free(text);
1746
1747     AtkTextRectangle fline_window;
1748     AtkTextRectangle afline_window;
1749     atk_text_get_range_extents(multilineText, startOffset, endOffset, ATK_XY_WINDOW, &fline_window);
1750     atk_text_get_range_extents(multilineText, startOffset, endOffset - 1, ATK_XY_WINDOW, &afline_window);
1751     g_assert_cmpint(fline_window.x, ==, afline_window.x);
1752     g_assert_cmpint(fline_window.y, ==, afline_window.y);
1753     g_assert_cmpint(fline_window.height, ==, afline_window.height);
1754
1755     g_object_unref(shortText1);
1756     g_object_unref(shortText2);
1757     g_object_unref(longText);
1758     g_object_unref(multilineText);
1759     g_object_unref(webView);
1760 }
1761
1762 static void testWebkitAtkLayoutAndDataTables()
1763 {
1764     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
1765     g_object_ref_sink(webView);
1766     GtkAllocation allocation = { 0, 0, 800, 600 };
1767     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
1768     webkit_web_view_load_string(webView, layoutAndDataTables, 0, 0, 0);
1769
1770     AtkObject* object = getWebAreaObject(webView);
1771     g_assert(object);
1772
1773     /* Check the non-layout table (data table). */
1774
1775     AtkObject* table1 = atk_object_ref_accessible_child(object, 0);
1776     g_assert(ATK_IS_TABLE(table1));
1777     AtkAttributeSet* set1 = atk_object_get_attributes(table1);
1778     g_assert(set1);
1779     g_assert(!atkAttributeSetContainsAttributeName(set1, "layout-guess"));
1780     atk_attribute_set_free(set1);
1781
1782     /* Check the layout table. */
1783
1784     AtkObject* table2 = atk_object_ref_accessible_child(object, 1);
1785     g_assert(ATK_IS_TABLE(table2));
1786     AtkAttributeSet* set2 = atk_object_get_attributes(table2);
1787     g_assert(set2);
1788     g_assert(atkAttributeSetContainsAttributeName(set2, "layout-guess"));
1789     g_assert(atkAttributeSetAttributeNameHasValue(set2, "layout-guess", "true"));
1790     atk_attribute_set_free(set2);
1791
1792     g_object_unref(table1);
1793     g_object_unref(table2);
1794     g_object_unref(webView);
1795 }
1796
1797 static void testWebkitAtkLinksWithInlineImages()
1798 {
1799     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
1800     g_object_ref_sink(webView);
1801     GtkAllocation allocation = { 0, 0, 800, 600 };
1802     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
1803     webkit_web_view_load_string(webView, linksWithInlineImages, 0, 0, 0);
1804
1805     AtkObject* object = getWebAreaObject(webView);
1806     g_assert(object);
1807
1808     /* First paragraph (link at the beginning). */
1809     AtkObject* paragraph = atk_object_ref_accessible_child(object, 0);
1810     g_assert(ATK_IS_TEXT(paragraph));
1811     gint startOffset;
1812     gint endOffset;
1813     gchar* text = atk_text_get_text_at_offset(ATK_TEXT(paragraph), 0, ATK_TEXT_BOUNDARY_LINE_START, &startOffset, &endOffset);
1814     g_assert(text);
1815     g_assert_cmpstr(text, ==, "foo bar baz");
1816     g_assert_cmpint(startOffset, ==, 0);
1817     g_assert_cmpint(endOffset, ==, 11);
1818     g_free(text);
1819     g_object_unref(paragraph);
1820
1821     /* Second paragraph (link in the middle). */
1822     paragraph = atk_object_ref_accessible_child(object, 1);
1823     g_assert(ATK_IS_TEXT(paragraph));
1824     text = atk_text_get_text_at_offset(ATK_TEXT(paragraph), 0, ATK_TEXT_BOUNDARY_LINE_START, &startOffset, &endOffset);
1825     g_assert(text);
1826     g_assert_cmpstr(text, ==, "foo bar baz");
1827     g_assert_cmpint(startOffset, ==, 0);
1828     g_assert_cmpint(endOffset, ==, 11);
1829     g_free(text);
1830     g_object_unref(paragraph);
1831
1832     /* Third paragraph (link at the end). */
1833     paragraph = atk_object_ref_accessible_child(object, 2);
1834     g_assert(ATK_IS_TEXT(paragraph));
1835     text = atk_text_get_text_at_offset(ATK_TEXT(paragraph), 0, ATK_TEXT_BOUNDARY_LINE_START, &startOffset, &endOffset);
1836     g_assert(text);
1837     g_assert_cmpstr(text, ==, "foo bar baz");
1838     g_assert_cmpint(startOffset, ==, 0);
1839     g_assert_cmpint(endOffset, ==, 11);
1840     g_free(text);
1841     g_object_unref(paragraph);
1842
1843     g_object_unref(webView);
1844 }
1845
1846 static void testWebkitAtkHypertextAndHyperlinks()
1847 {
1848     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
1849     g_object_ref_sink(webView);
1850     GtkAllocation allocation = { 0, 0, 800, 600 };
1851     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
1852     webkit_web_view_load_string(webView, hypertextAndHyperlinks, 0, 0, 0);
1853
1854     AtkObject* object = getWebAreaObject(webView);
1855     g_assert(object);
1856
1857     AtkObject* paragraph1 = atk_object_ref_accessible_child(object, 0);
1858     g_assert(ATK_OBJECT(paragraph1));
1859     g_assert(atk_object_get_role(paragraph1) == ATK_ROLE_PARAGRAPH);
1860     g_assert(ATK_IS_HYPERTEXT(paragraph1));
1861
1862     /* No links in the first paragraph. */
1863     gint nLinks = atk_hypertext_get_n_links(ATK_HYPERTEXT(paragraph1));
1864     g_assert_cmpint(nLinks, ==, 0);
1865
1866     AtkObject* paragraph2 = atk_object_ref_accessible_child(object, 1);
1867     g_assert(ATK_OBJECT(paragraph2));
1868     g_assert(atk_object_get_role(paragraph2) == ATK_ROLE_PARAGRAPH);
1869     g_assert(ATK_IS_HYPERTEXT(paragraph2));
1870
1871     /* Check links in the second paragraph.
1872        nLinks = atk_hypertext_get_n_links(ATK_HYPERTEXT(paragraph2));
1873        g_assert_cmpint(nLinks, ==, 3); */
1874
1875     AtkHyperlink* hLink1 = atk_hypertext_get_link(ATK_HYPERTEXT(paragraph2), 0);
1876     g_assert(ATK_HYPERLINK(hLink1));
1877     AtkObject* hLinkObject1 = atk_hyperlink_get_object(hLink1, 0);
1878     g_assert(ATK_OBJECT(hLinkObject1));
1879     g_assert(atk_object_get_role(hLinkObject1) == ATK_ROLE_LINK);
1880     g_assert_cmpint(atk_hyperlink_get_start_index(hLink1), ==, 0);
1881     g_assert_cmpint(atk_hyperlink_get_end_index(hLink1), ==, 6);
1882     g_assert_cmpint(atk_hyperlink_get_n_anchors(hLink1), ==, 1);
1883     g_assert_cmpstr(atk_hyperlink_get_uri(hLink1, 0), ==, "http://foo.bar.baz/");
1884
1885     AtkHyperlink* hLink2 = atk_hypertext_get_link(ATK_HYPERTEXT(paragraph2), 1);
1886     g_assert(ATK_HYPERLINK(hLink2));
1887     AtkObject* hLinkObject2 = atk_hyperlink_get_object(hLink2, 0);
1888     g_assert(ATK_OBJECT(hLinkObject2));
1889     g_assert(atk_object_get_role(hLinkObject2) == ATK_ROLE_LINK);
1890     g_assert_cmpint(atk_hyperlink_get_start_index(hLink2), ==, 12);
1891     g_assert_cmpint(atk_hyperlink_get_end_index(hLink2), ==, 32);
1892     g_assert_cmpint(atk_hyperlink_get_n_anchors(hLink2), ==, 1);
1893     g_assert_cmpstr(atk_hyperlink_get_uri(hLink2, 0), ==, "http://bar.baz.foo/");
1894
1895     AtkHyperlink* hLink3 = atk_hypertext_get_link(ATK_HYPERTEXT(paragraph2), 2);
1896     g_assert(ATK_HYPERLINK(hLink3));
1897     AtkObject* hLinkObject3 = atk_hyperlink_get_object(hLink3, 0);
1898     g_assert(ATK_OBJECT(hLinkObject3));
1899     g_assert(atk_object_get_role(hLinkObject3) == ATK_ROLE_LINK);
1900     g_assert_cmpint(atk_hyperlink_get_start_index(hLink3), ==, 65);
1901     g_assert_cmpint(atk_hyperlink_get_end_index(hLink3), ==, 75);
1902     g_assert_cmpint(atk_hyperlink_get_n_anchors(hLink3), ==, 1);
1903     g_assert_cmpstr(atk_hyperlink_get_uri(hLink3, 0), ==, "http://baz.foo.bar/");
1904
1905     AtkObject* list = atk_object_ref_accessible_child(object, 2);
1906     g_assert(ATK_OBJECT(list));
1907     g_assert(atk_object_get_role(list) == ATK_ROLE_LIST);
1908     g_assert_cmpint(atk_object_get_n_accessible_children(list), ==, 1);
1909
1910     AtkObject* listItem = atk_object_ref_accessible_child(list, 0);
1911     g_assert(ATK_IS_TEXT(listItem));
1912     g_assert(ATK_IS_HYPERTEXT(listItem));
1913
1914     AtkHyperlink* hLinkInListItem = atk_hypertext_get_link(ATK_HYPERTEXT(listItem), 0);
1915     g_assert(ATK_HYPERLINK(hLinkInListItem));
1916     AtkObject* hLinkObject = atk_hyperlink_get_object(hLinkInListItem, 0);
1917     g_assert(ATK_OBJECT(hLinkObject));
1918     g_assert(atk_object_get_role(hLinkObject) == ATK_ROLE_LINK);
1919     g_assert_cmpint(atk_hyperlink_get_start_index(hLinkInListItem), ==, 20);
1920     g_assert_cmpint(atk_hyperlink_get_end_index(hLinkInListItem), ==, 43);
1921     g_assert_cmpint(atk_hyperlink_get_n_anchors(hLinkInListItem), ==, 1);
1922     g_assert_cmpstr(atk_hyperlink_get_uri(hLinkInListItem, 0), ==, "http://foo.bar.baz/");
1923
1924     /* Finally check the AtkAction interface for a given AtkHyperlink. */
1925     g_assert(ATK_IS_ACTION(hLink1));
1926     g_assert_cmpint(atk_action_get_n_actions(ATK_ACTION(hLink1)), ==, 1);
1927     g_assert_cmpstr(atk_action_get_keybinding(ATK_ACTION(hLink1), 0), ==, "");
1928     g_assert_cmpstr(atk_action_get_name(ATK_ACTION(hLink1), 0), ==, "jump");
1929     g_assert(atk_action_do_action(ATK_ACTION(hLink1), 0));
1930
1931     g_object_unref(paragraph1);
1932     g_object_unref(paragraph2);
1933     g_object_unref(list);
1934     g_object_unref(listItem);
1935     g_object_unref(webView);
1936 }
1937
1938 static void testWebkitAtkListsOfItems()
1939 {
1940     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
1941     g_object_ref_sink(webView);
1942     GtkAllocation allocation = { 0, 0, 800, 600 };
1943     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
1944     webkit_web_view_load_string(webView, listsOfItems, 0, 0, 0);
1945
1946     AtkObject* object = getWebAreaObject(webView);
1947     g_assert(object);
1948
1949     /* Unordered list. */
1950
1951     AtkObject* uList = atk_object_ref_accessible_child(object, 0);
1952     g_assert(ATK_OBJECT(uList));
1953     g_assert(atk_object_get_role(uList) == ATK_ROLE_LIST);
1954     g_assert_cmpint(atk_object_get_n_accessible_children(uList), ==, 3);
1955
1956     AtkObject* item1 = atk_object_ref_accessible_child(uList, 0);
1957     g_assert(ATK_IS_TEXT(item1));
1958     AtkObject* item2 = atk_object_ref_accessible_child(uList, 1);
1959     g_assert(ATK_IS_TEXT(item2));
1960     AtkObject* item3 = atk_object_ref_accessible_child(uList, 2);
1961     g_assert(ATK_IS_TEXT(item3));
1962
1963     g_assert_cmpint(atk_object_get_n_accessible_children(item1), ==, 0);
1964     g_assert_cmpint(atk_object_get_n_accessible_children(item2), ==, 1);
1965     g_assert_cmpint(atk_object_get_n_accessible_children(item3), ==, 1);
1966
1967     char* text = atk_text_get_text(ATK_TEXT(item1), 0, -1);
1968     g_assert_cmpstr(text, ==, "\342\200\242 text only");
1969     g_free(text);
1970     text = atk_text_get_text(ATK_TEXT(item2), 0, -1);
1971     g_assert_cmpstr(text, ==, "\342\200\242 link only");
1972     g_free(text);
1973     text = atk_text_get_text(ATK_TEXT(item3), 0, -1);
1974     g_assert_cmpstr(text, ==, "\342\200\242 text and a link");
1975     g_free(text);
1976
1977     g_object_unref(item1);
1978     g_object_unref(item2);
1979     g_object_unref(item3);
1980
1981     /* Ordered list. */
1982
1983     AtkObject* oList = atk_object_ref_accessible_child(object, 1);
1984     g_assert(ATK_OBJECT(oList));
1985     g_assert(atk_object_get_role(oList) == ATK_ROLE_LIST);
1986     g_assert_cmpint(atk_object_get_n_accessible_children(oList), ==, 3);
1987
1988     item1 = atk_object_ref_accessible_child(oList, 0);
1989     g_assert(ATK_IS_TEXT(item1));
1990     item2 = atk_object_ref_accessible_child(oList, 1);
1991     g_assert(ATK_IS_TEXT(item2));
1992     item3 = atk_object_ref_accessible_child(oList, 2);
1993     g_assert(ATK_IS_TEXT(item3));
1994
1995     text = atk_text_get_text(ATK_TEXT(item1), 0, -1);
1996     g_assert_cmpstr(text, ==, "1. text only");
1997     g_free(text);
1998     text = atk_text_get_text(ATK_TEXT(item2), 0, -1);
1999     g_assert_cmpstr(text, ==, "2. link only");
2000     g_free(text);
2001     text = atk_text_get_text(ATK_TEXT(item3), 0, -1);
2002     g_assert_cmpstr(text, ==, "3. text and a link");
2003     g_free(text);
2004
2005     g_assert_cmpint(atk_object_get_n_accessible_children(item1), ==, 0);
2006     g_assert_cmpint(atk_object_get_n_accessible_children(item2), ==, 1);
2007     g_assert_cmpint(atk_object_get_n_accessible_children(item3), ==, 1);
2008
2009     g_object_unref(item1);
2010     g_object_unref(item2);
2011     g_object_unref(item3);
2012
2013     g_object_unref(uList);
2014     g_object_unref(oList);
2015     g_object_unref(webView);
2016 }
2017
2018 typedef enum {
2019   TEXT_CHANGE_INSERT = 1,
2020   TEXT_CHANGE_REMOVE = 2
2021 } TextChangeType;
2022
2023 static gchar* textChangedResult = 0;
2024
2025 static void textChangedCb(AtkText* text, gint pos, gint len, gchar* modifiedText, gpointer data)
2026 {
2027     g_assert(text && ATK_IS_OBJECT(text));
2028
2029     TextChangeType type = GPOINTER_TO_INT(data);
2030     g_free(textChangedResult);
2031     textChangedResult = g_strdup_printf("|%d|%d|%d|'%s'|", type, pos, len, modifiedText);
2032 }
2033
2034 static void testWebkitAtkTextChangedNotifications()
2035 {
2036     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
2037     g_object_ref_sink(webView);
2038     GtkAllocation allocation = { 0, 0, 800, 600 };
2039     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
2040     webkit_web_view_load_string(webView, formWithTextInputs, 0, 0, 0);
2041
2042     AtkObject* object = getWebAreaObject(webView);
2043     g_assert(object);
2044
2045     AtkObject* form = atk_object_ref_accessible_child(object, 0);
2046     g_assert(ATK_IS_OBJECT(form));
2047
2048     /* First check normal text entries. */
2049     AtkObject* textEntry = atk_object_ref_accessible_child(form, 0);
2050     g_assert(ATK_IS_EDITABLE_TEXT(textEntry));
2051     g_assert(atk_object_get_role(ATK_OBJECT(textEntry)) == ATK_ROLE_ENTRY);
2052
2053     g_signal_connect(textEntry, "text-insert",
2054                      G_CALLBACK(textChangedCb),
2055                      GINT_TO_POINTER(TEXT_CHANGE_INSERT));
2056     g_signal_connect(textEntry, "text-remove",
2057                      G_CALLBACK(textChangedCb),
2058                      GINT_TO_POINTER(TEXT_CHANGE_REMOVE));
2059
2060     gint pos = 0;
2061     atk_editable_text_insert_text(ATK_EDITABLE_TEXT(textEntry), "foo bar baz", 11, &pos);
2062     char* text = atk_text_get_text(ATK_TEXT(textEntry), 0, -1);
2063     g_assert_cmpstr(text, ==, "foo bar baz");
2064     g_assert_cmpstr(textChangedResult, ==, "|1|0|11|'foo bar baz'|");
2065     g_free(text);
2066
2067     atk_editable_text_delete_text(ATK_EDITABLE_TEXT(textEntry), 4, 7);
2068     text = atk_text_get_text(ATK_TEXT(textEntry), 0, -1);
2069     g_assert_cmpstr(text, ==, "foo  baz");
2070     g_assert_cmpstr(textChangedResult, ==, "|2|4|3|'bar'|");
2071     g_free(text);
2072
2073     pos = 4;
2074     atk_editable_text_insert_text(ATK_EDITABLE_TEXT(textEntry), "qux quux tobeignored", 8, &pos);
2075     text = atk_text_get_text(ATK_TEXT(textEntry), 0, -1);
2076     g_assert_cmpstr(text, ==, "foo qux quux baz");
2077     g_assert_cmpstr(textChangedResult, ==, "|1|4|8|'qux quux'|");
2078     g_free(text);
2079
2080     /* Now check for password entries. */
2081     AtkObject* passwordEntry = atk_object_ref_accessible_child(form, 1);
2082     g_assert(ATK_IS_EDITABLE_TEXT(passwordEntry));
2083     g_assert(atk_object_get_role(ATK_OBJECT(passwordEntry)) == ATK_ROLE_PASSWORD_TEXT);
2084
2085     g_signal_connect(passwordEntry, "text-insert",
2086                      G_CALLBACK(textChangedCb),
2087                      GINT_TO_POINTER(TEXT_CHANGE_INSERT));
2088     g_signal_connect(passwordEntry, "text-remove",
2089                      G_CALLBACK(textChangedCb),
2090                      GINT_TO_POINTER(TEXT_CHANGE_REMOVE));
2091
2092     pos = 0;
2093     /* A single bullet character is '\342\200\242' */
2094     atk_editable_text_insert_text(ATK_EDITABLE_TEXT(passwordEntry), "foobar", 6, &pos);
2095     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'|");
2096     text = atk_text_get_text(ATK_TEXT(passwordEntry), 0, -1);
2097     g_assert_cmpstr(text, ==, "\342\200\242\342\200\242\342\200\242\342\200\242\342\200\242\342\200\242");
2098     g_free(text);
2099
2100     atk_editable_text_delete_text(ATK_EDITABLE_TEXT(passwordEntry), 2, 4);
2101     g_assert_cmpstr(textChangedResult, ==, "|2|2|2|'\342\200\242\342\200\242'|");
2102
2103     text = atk_text_get_text(ATK_TEXT(passwordEntry), 0, -1);
2104     g_assert_cmpstr(text, ==, "\342\200\242\342\200\242\342\200\242\342\200\242");
2105     g_free(text);
2106
2107     pos = 3;
2108     atk_editable_text_insert_text(ATK_EDITABLE_TEXT(passwordEntry), "qux tobeignored", 3, &pos);
2109     g_assert_cmpstr(textChangedResult, ==, "|1|3|3|'\342\200\242\342\200\242\342\200\242'|");
2110
2111     text = atk_text_get_text(ATK_TEXT(passwordEntry), 0, -1);
2112     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");
2113     g_free(text);
2114
2115     g_free(textChangedResult);
2116
2117     g_object_unref(form);
2118     g_object_unref(textEntry);
2119     g_object_unref(passwordEntry);
2120     g_object_unref(webView);
2121 }
2122
2123 static void testWebkitAtkParentForRootObject()
2124 {
2125     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
2126     GtkAllocation allocation = { 0, 0, 800, 600 };
2127     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
2128     webkit_web_view_load_string(webView, contents, 0, 0, 0);
2129
2130     /* We need a parent container widget for the webview. */
2131     GtkWidget* parentContainer = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2132     g_object_ref_sink(parentContainer);
2133     gtk_container_add(GTK_CONTAINER(parentContainer), GTK_WIDGET(webView));
2134
2135     AtkObject* axParent = gtk_widget_get_accessible(parentContainer);
2136     g_assert(ATK_IS_OBJECT(axParent));
2137
2138     AtkObject* axRoot = gtk_widget_get_accessible(GTK_WIDGET(webView));
2139     g_assert(ATK_IS_OBJECT(axRoot));
2140
2141     /* The child for the parent container's accessibility object
2142        should be the accessibility object for the WebView's root. */
2143     AtkObject* axParentChild = atk_object_ref_accessible_child(axParent, 0);
2144     g_assert(axParentChild == axRoot);
2145
2146     /* Bottom-up navigation should match top-down one. */
2147     g_assert(atk_object_get_parent(axParentChild) == axParent);
2148
2149     g_object_unref(axParentChild);
2150     g_object_unref(parentContainer);
2151 }
2152
2153 static void testWebkitAtkSetParentForObject()
2154 {
2155     WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
2156     GtkAllocation allocation = { 0, 0, 800, 600 };
2157     gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
2158     webkit_web_view_load_string(webView, contents, 0, 0, 0);
2159
2160     /* Put the webview in a parent container widget to check that the
2161        normal behaviour keeps working as expected by default. */
2162     GtkWidget* parentContainer = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2163     g_object_ref_sink(parentContainer);
2164     gtk_container_add(GTK_CONTAINER(parentContainer), GTK_WIDGET(webView));
2165
2166     AtkObject* axRoot = gtk_widget_get_accessible(GTK_WIDGET(webView));
2167     g_assert(ATK_IS_OBJECT(axRoot));
2168
2169     AtkObject* axParent = gtk_widget_get_accessible(parentContainer);
2170     g_assert(ATK_IS_OBJECT(axParent));
2171
2172     /* The parent of the root object is the parent container's a11y object. */
2173     g_assert(atk_object_get_parent(axRoot) == axParent);
2174
2175     /* We now need to use something as a an alternative parent for
2176        the a11y object associated with the root of the DOM tree. */
2177     GtkWidget* alternativeParent = gtk_button_new();
2178     g_object_ref_sink(alternativeParent);
2179
2180     AtkObject* axAlternativeParent = gtk_widget_get_accessible(alternativeParent);
2181     g_assert(ATK_IS_OBJECT(axAlternativeParent));
2182
2183     /* Manually set the alternative parent's accessibility object as
2184        the parent for the WebKit accessibility root object and check. */
2185     atk_object_set_parent(axRoot, axAlternativeParent);
2186     g_assert(atk_object_get_parent(axRoot) == axAlternativeParent);
2187
2188     g_object_unref(alternativeParent);
2189     g_object_unref(parentContainer);
2190 }
2191
2192 #ifdef GTK_API_VERSION_2
2193 static void initializeTestingFramework(int argc, char** argv)
2194 {
2195     /* Ensure GAIL is the only module loaded. */
2196     g_setenv("GTK_MODULES", "gail", TRUE);
2197
2198     /* Following lines were taken from gtk_test_init(). */
2199     g_test_init(&argc, &argv, 0);
2200     gtk_disable_setlocale();
2201     setlocale(LC_ALL, "C");
2202
2203     gtk_init(&argc, &argv);
2204 }
2205 #endif
2206
2207 int main(int argc, char** argv)
2208 {
2209 #ifdef GTK_API_VERSION_2
2210     /* We can't just call to gtk_test_init() in this case because its
2211        implementation makes sure that no GTK+ module will be loaded, and
2212        we will need to load GAIL for tests that need to use AtkObjects
2213        from non-WebKit GtkWidgets (e.g parentForRootObject).*/
2214     initializeTestingFramework(argc, argv);
2215 #else
2216     gtk_test_init(&argc, &argv, NULL);
2217 #endif
2218
2219     g_test_bug_base("https://bugs.webkit.org/");
2220     g_test_add_func("/webkit/atk/caretOffsets", testWebkitAtkCaretOffsets);
2221     g_test_add_func("/webkit/atk/caretOffsetsAndExtranousWhiteSpaces", testWebkitAtkCaretOffsetsAndExtranousWhiteSpaces);
2222     g_test_add_func("/webkit/atk/comboBox", testWebkitAtkComboBox);
2223     g_test_add_func("/webkit/atk/documentLoadingEvents", testWebkitAtkDocumentLoadingEvents);
2224     g_test_add_func("/webkit/atk/embeddedObjects", testWebkitAtkEmbeddedObjects);
2225     g_test_add_func("/webkit/atk/getTextAtOffset", testWebkitAtkGetTextAtOffset);
2226     g_test_add_func("/webkit/atk/getTextAtOffsetNewlines", testWebkitAtkGetTextAtOffsetNewlines);
2227     g_test_add_func("/webkit/atk/getTextAtOffsetTextarea", testWebkitAtkGetTextAtOffsetTextarea);
2228     g_test_add_func("/webkit/atk/getTextAtOffsetTextInput", testWebkitAtkGetTextAtOffsetTextInput);
2229     g_test_add_func("/webkit/atk/getTextAtOffsetWithPreformattedText", testWebkitAtkGetTextAtOffsetWithPreformattedText);
2230     g_test_add_func("/webkit/atk/getTextAtOffsetWithSpecialCharacters", testWebkitAtkGetTextAtOffsetWithSpecialCharacters);
2231     g_test_add_func("/webkit/atk/getTextAtOffsetWithWrappedLines", testWebkitAtkGetTextAtOffsetWithWrappedLines);
2232     g_test_add_func("/webkit/atk/getTextAtOffsetWithEmbeddedObjects", testWebkitAtkGetTextAtOffsetWithEmbeddedObjects);
2233     g_test_add_func("/webkit/atk/getTextInParagraphAndBodySimple", testWebkitAtkGetTextInParagraphAndBodySimple);
2234     g_test_add_func("/webkit/atk/getTextInParagraphAndBodyModerate", testWebkitAtkGetTextInParagraphAndBodyModerate);
2235     g_test_add_func("/webkit/atk/getTextInTable", testWebkitAtkGetTextInTable);
2236     g_test_add_func("/webkit/atk/getHeadersInTable", testWebkitAtkGetHeadersInTable);
2237     g_test_add_func("/webkit/atk/textAttributes", testWebkitAtkTextAttributes);
2238     g_test_add_func("/webkit/atk/textSelections", testWebkitAtkTextSelections);
2239     g_test_add_func("/webkit/atk/getExtents", testWebkitAtkGetExtents);
2240     g_test_add_func("/webkit/atk/hypertextAndHyperlinks", testWebkitAtkHypertextAndHyperlinks);
2241     g_test_add_func("/webkit/atk/layoutAndDataTables", testWebkitAtkLayoutAndDataTables);
2242     g_test_add_func("/webkit/atk/linksWithInlineImages", testWebkitAtkLinksWithInlineImages);
2243     g_test_add_func("/webkit/atk/listsOfItems", testWebkitAtkListsOfItems);
2244     g_test_add_func("/webkit/atk/textChangedNotifications", testWebkitAtkTextChangedNotifications);
2245     g_test_add_func("/webkit/atk/parentForRootObject", testWebkitAtkParentForRootObject);
2246     g_test_add_func("/webkit/atk/setParentForObject", testWebkitAtkSetParentForObject);
2247     return g_test_run ();
2248 }
2249