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