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>« This is a paragraph with “special” characters inside. »</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