2 * Copyright (C) 2009 Igalia S.L.
3 * Copyright (C) 2013 Samsung Electronics. All rights reserved.
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public License
16 * along with this library; see the file COPYING.LIB. If not, write to
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
21 #include "autotoolsconfig.h"
24 #include <glib/gstdio.h>
28 #include <webkit/webkit.h>
30 static const char* centeredContents = "<html><body><p style='text-align: center;'>Short line</p><p style='text-align: center;'>Long-size line with some foo bar baz content</p><p style='text-align: center;'>Short line</p><p style='text-align: center;'>This is a multi-line paragraph<br />where the first line<br />is the biggest one</p></body></html>";
32 static const char* contents = "<html><body><p>This is a test. This is the second sentence. And this the third.</p></body></html>";
34 static const char* contentsWithNewlines = "<html><body><p>This is a test. \n\nThis\n is the second sentence. And this the third.</p></body></html>";
36 static const char* contentsWithPreformattedText = "<html><body><pre>\n\t\n\tfirst line\n\tsecond line\n</pre></body></html>";
38 static const char* contentsWithSpecialChars = "<html><body><p>« This is a paragraph with “special” characters inside. »</p><ul><li style='max-width:100px;'>List item with some text that wraps across different lines.</li><li style='max-width:100px;'><p>List item with some text that wraps across different lines.</p></li></ul></body></html>";
40 static const char* contentsInTextarea = "<html><body><textarea cols='80'>This is a test. This is the second sentence. And this the third.</textarea></body></html>";
42 static const char* contentsInTextInput = "<html><body><input type='text' size='80' value='This is a test. This is the second sentence. And this the third.'/></body></html>";
44 static const char* contentsInParagraphAndBodySimple = "<html><body><p>This is a test.</p>Hello world.</body></html>";
46 static const char* contentsInParagraphAndBodyModerate = "<html><body><p>This is a test.</p>Hello world.<br /><font color='#00cc00'>This sentence is green.</font><br />This one is not.</body></html>";
48 static const char* contentsInTable = "<html><body><table><tr><td>foo</td><td>bar</td></tr></table></body></html>";
50 static const char* contentsInTableWithHeaders = "<html><body><table><tr><th>foo</th><th>bar</th><th colspan='2'>baz</th></tr><tr><th>qux</th><td>1</td><td>2</td><td>3</td></tr><tr><th rowspan='2'>quux</th><td>4</td><td>5</td><td>6</td></tr><tr><td>6</td><td>7</td><td>8</td></tr><tr><th>corge</th><td>9</td><td>10</td><td>11</td></tr></table><table><tr><td>1</td><td>2</td></tr><tr><td>3</td><td>4</td></tr></table></body></html>";
52 static const char* contentsWithExtraneousWhiteSpaces = "<html><head><body><p>This\n paragraph\n is\n borked!</p></body></html>";
54 static const char* contentsWithWrappedLines = "<html><body><p style='max-width:150px;'>This is one line wrapped because of the maximum width of its container.</p><p>This is another line wrapped<br>because of one forced<br>line break in the middle.</body></html>";
56 static const char* contentsWithEmbeddedObjects = "<html><body>This is one line containing two <img> embedded objects <img> in the middle.</body></html>";
58 static const char* comboBoxSelector = "<html><body><select><option selected value='foo'>foo</option><option value='bar'>bar</option></select></body></html>";
60 static const char* embeddedObjects = "<html><body><p>Choose: <input value='foo' type='checkbox'/>foo <input value='bar' type='checkbox'/>bar (pick one)</p><p>Choose: <select name='foo'><option>bar</option><option>baz</option></select> (pick one)</p><p><input name='foobarbutton' value='foobar' type='button'/></p></body></html>";
62 static const char* formWithTextInputs = "<html><body><form><input type='text' name='entry' /><input type='password' name='passwordEntry' /></form></body></html>";
64 static const char* hypertextAndHyperlinks = "<html><body><p>A paragraph with no links at all</p><p><a href='http://foo.bar.baz/'>A line</a> with <a href='http://bar.baz.foo/'>a link in the middle</a> as well as at the beginning and <a href='http://baz.foo.bar/'>at the end</a></p><ol><li>List item with a <span><a href='http://foo.bar.baz/'>link inside a span node</a></span></li></ol></body></html>";
66 static const char* layoutAndDataTables = "<html><body><table><tr><th>Odd</th><th>Even</th></tr><tr><td>1</td><td>2</td></tr></table><table><tr><td>foo</td><td>bar</td></tr></table></body></html>";
68 static const char* linksWithInlineImages = "<html><head><style>a.http:before {content: url(no-image.png);}</style><body><p><a class='http' href='foo'>foo</a> bar baz</p><p>foo <a class='http' href='bar'>bar</a> baz</p><p>foo bar <a class='http' href='baz'>baz</a></p></body></html>";
70 static const char* listsOfItems = "<html><body><ul><li>text only</li><li><a href='foo'>link only</a></li><li>text and a <a href='bar'>link</a></li></ul><ol><li>text only</li><li><a href='foo'>link only</a></li><li>text and a <a href='bar'>link</a></li></ol></body></html>";
72 static const char* textForCaretBrowsing = "<html><body><h1>A text header</h1><p>A paragraph <a href='http://foo.bar.baz/'>with a link</a> in the middle</p><ol><li>A list item</li><li><span style='display:block;'>Block span in a list item</span><span>Inline span in a list item</span></li><li><a href='foo'><span style='display:block;'>Block span in a link in a list item</span><span>Inline span in a link in a list item</span></a></li></ol><select><option selected value='foo'>An option in a combo box</option></select><input type='text' name='foo' value='foo bar baz' /><table><tr><td>a table cell</td><td></td><td><a href='foo'><span style='display:block;'>Block span in a link in a table cell</span><span>Inline span in a link in a table cell</span></a></td><td><span style='display:block;'>Block span in a table cell</span><span>Inline span in a table cell</span></td></tr></table><h4><a href='foo'><span style='display:block;'>Block span in a link in a heading</span><span>Inline span in a link in a heading</span></h4><h4><span style='display:block;'>Block span in a heading</span><span>Inline span in a heading</span></h4></body></html>";
74 static const char* textForSelections = "<html><body><p>A paragraph with plain text</p><p>A paragraph with <a href='http://webkit.org'>a link</a> in the middle</p><ol><li>A list item</li></ol><select></body></html>";
76 static const char* textWithAttributes = "<html><head><style>.st1 {font-family: monospace; color:rgb(120,121,122);} .st2 {text-decoration:underline; background-color:rgb(80,81,82);}</style></head><body><p style=\"font-size:14; text-align:right;\">This is the <i>first</i><b> sentence of this text.</b></p><p class=\"st1\">This sentence should have an style applied <span class=\"st2\">and this part should have another one</span>.</p><p>x<sub>1</sub><sup>2</sup>=x<sub>2</sub><sup>3</sup></p><p style=\"text-align:center;\">This sentence is the <strike>last</strike> one.</p></body></html>";
78 static AtkObject* getWebAreaObject(WebKitWebView* webView)
80 /* Manually spin the main context to make sure the accessible
81 objects are properly created before continuing. */
82 while (g_main_context_pending(0))
83 g_main_context_iteration(0, TRUE);
85 AtkObject* rootObject = gtk_widget_get_accessible(GTK_WIDGET(webView));
89 AtkObject* webAreaObject = atk_object_ref_accessible_child(rootObject, 0);
93 /* We don't need the extra ref here. */
94 g_object_unref(webAreaObject);
99 typedef gchar* (*AtkGetTextFunction) (AtkText*, gint, AtkTextBoundary, gint*, gint*);
101 static void testGetTextFunction(AtkText* textObject, AtkGetTextFunction fn, AtkTextBoundary boundary, gint offset, const char* textResult, gint startOffsetResult, gint endOffsetResult)
105 char* text = fn(textObject, offset, boundary, &startOffset, &endOffset);
106 g_assert_cmpstr(text, ==, textResult);
107 g_assert_cmpint(startOffset, ==, startOffsetResult);
108 g_assert_cmpint(endOffset, ==, endOffsetResult);
112 static void runGetTextTests(AtkText* textObject)
114 char* text = atk_text_get_text(textObject, 0, -1);
115 g_assert_cmpstr(text, ==, "This is a test. This is the second sentence. And this the third.");
118 /* ATK_TEXT_BOUNDARY_CHAR */
119 testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_CHAR,
122 testGetTextFunction(textObject, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_CHAR,
125 testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_CHAR,
128 testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_CHAR,
131 /* ATK_TEXT_BOUNDARY_WORD_START */
132 testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START,
135 testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START,
138 testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START,
139 10, "test. ", 10, 16);
141 testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START,
142 58, "third.", 58, 64);
144 testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START,
145 64, "third.", 58, 64);
147 testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_WORD_START,
150 testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_WORD_START,
153 testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_WORD_START,
156 testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_WORD_START,
159 testGetTextFunction(textObject, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_WORD_START,
162 testGetTextFunction(textObject, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_WORD_START,
165 testGetTextFunction(textObject, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_WORD_START,
168 testGetTextFunction(textObject, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_WORD_START,
171 /* ATK_TEXT_BOUNDARY_WORD_END */
172 testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END,
175 testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END,
178 testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END,
181 testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END,
184 testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END,
185 58, " third", 57, 63);
187 testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END,
190 testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_WORD_END,
193 testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_WORD_END,
196 testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_WORD_END,
199 testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_WORD_END,
202 testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_WORD_END,
203 64, " third", 57, 63);
205 testGetTextFunction(textObject, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_WORD_END,
208 testGetTextFunction(textObject, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_WORD_END,
211 testGetTextFunction(textObject, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_WORD_END,
214 testGetTextFunction(textObject, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_WORD_END,
217 /* ATK_TEXT_BOUNDARY_SENTENCE_START */
218 testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_SENTENCE_START,
219 0, "This is a test. ", 0, 16);
221 testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_SENTENCE_START,
222 15, "This is a test. ", 0, 16);
224 testGetTextFunction(textObject, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_SENTENCE_START,
225 0, "This is the second sentence. ", 16, 45);
227 testGetTextFunction(textObject, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_SENTENCE_START,
228 15, "This is the second sentence. ", 16, 45);
230 testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_SENTENCE_START,
231 16, "This is a test. ", 0, 16);
233 testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_SENTENCE_START,
234 44, "This is a test. ", 0, 16);
236 testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_SENTENCE_START,
239 /* ATK_TEXT_BOUNDARY_SENTENCE_END */
240 testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_SENTENCE_END,
241 0, "This is a test.", 0, 15);
243 testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_SENTENCE_END,
244 15, " This is the second sentence.", 15, 44);
246 testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_SENTENCE_END,
247 16, " This is the second sentence.", 15, 44);
249 testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_SENTENCE_END,
250 17, " This is the second sentence.", 15, 44);
252 testGetTextFunction(textObject, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_SENTENCE_END,
253 0, " This is the second sentence.", 15, 44);
255 testGetTextFunction(textObject, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_SENTENCE_END,
256 15, " And this the third.", 44, 64);
258 testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_SENTENCE_END,
259 16, "This is a test.", 0, 15);
261 testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_SENTENCE_END,
262 15, "This is a test.", 0, 15);
264 testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_SENTENCE_END,
267 testGetTextFunction(textObject, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_SENTENCE_END,
268 44, " This is the second sentence.", 15, 44);
270 /* It's tricky to test these properly right now, since our a11y
271 implementation splits different lines in different a11y items. */
272 /* ATK_TEXT_BOUNDARY_LINE_START */
273 testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_START,
274 0, "This is a test. This is the second sentence. And this the third.", 0, 64);
276 /* ATK_TEXT_BOUNDARY_LINE_END */
277 testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_END,
278 0, "This is a test. This is the second sentence. And this the third.", 0, 64);
280 /* For objects implementing AtkEditableText, try to change the
281 exposed text and retrieve it again as a full line.
282 (see https://bugs.webkit.org/show_bug.cgi?id=72830) */
283 if (ATK_IS_EDITABLE_TEXT(textObject)) {
284 atk_editable_text_set_text_contents(ATK_EDITABLE_TEXT(textObject), "foo bar baz");
285 testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_START, 0, "foo bar baz", 0, 11);
286 testGetTextFunction(textObject, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_END, 0, "foo bar baz", 0, 11);
290 static gchar* textCaretMovedResult = 0;
292 static void textCaretMovedCallback(AtkText* text, gint pos, gpointer data)
294 g_assert(ATK_IS_TEXT(text));
296 g_free(textCaretMovedResult);
297 AtkRole role = atk_object_get_role(ATK_OBJECT(text));
298 textCaretMovedResult = g_strdup_printf("|%s|%d|", atk_role_get_name(role), pos);
301 static void testWebkitAtkCaretOffsets()
303 WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
304 g_object_ref_sink(webView);
305 GtkAllocation allocation = { 0, 0, 800, 600 };
306 gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
307 webkit_web_view_load_string(webView, textForCaretBrowsing, 0, 0, 0);
309 AtkObject* object = getWebAreaObject(webView);
312 AtkObject* header = atk_object_ref_accessible_child(object, 0);
313 g_assert(ATK_IS_TEXT(header));
314 g_signal_connect(header, "text-caret-moved", G_CALLBACK(textCaretMovedCallback), 0);
316 gchar* text = atk_text_get_text(ATK_TEXT(header), 0, -1);
317 g_assert_cmpstr(text, ==, "A text header");
320 /* It should be possible to place the caret inside a header. */
321 gboolean result = atk_text_set_caret_offset(ATK_TEXT(header), 5);
322 g_assert_cmpint(result, ==, TRUE);
323 gint offset = atk_text_get_caret_offset(ATK_TEXT(header));
324 g_assert_cmpint(offset, ==, 5);
325 g_assert_cmpstr(textCaretMovedResult, ==, "|heading|5|");
327 AtkObject* paragraph = atk_object_ref_accessible_child(object, 1);
328 g_assert(ATK_IS_TEXT(paragraph));
329 g_signal_connect(paragraph, "text-caret-moved", G_CALLBACK(textCaretMovedCallback), 0);
331 text = atk_text_get_text(ATK_TEXT(paragraph), 0, -1);
332 g_assert_cmpstr(text, ==, "A paragraph with a link in the middle");
335 /* It should be possible to place the caret inside a paragraph and a link. */
336 result = atk_text_set_caret_offset(ATK_TEXT(paragraph), 5);
337 g_assert_cmpint(result, ==, TRUE);
338 offset = atk_text_get_caret_offset(ATK_TEXT(paragraph));
339 g_assert_cmpint(offset, ==, 5);
340 g_assert_cmpstr(textCaretMovedResult, ==, "|paragraph|5|");
342 result = atk_text_set_caret_offset(ATK_TEXT(paragraph), 20);
343 g_assert_cmpint(result, ==, TRUE);
344 offset = atk_text_get_caret_offset(ATK_TEXT(paragraph));
345 g_assert_cmpint(offset, ==, 20);
346 g_assert_cmpstr(textCaretMovedResult, ==, "|paragraph|20|");
348 result = atk_text_set_caret_offset(ATK_TEXT(paragraph), 30);
349 g_assert_cmpint(result, ==, TRUE);
350 offset = atk_text_get_caret_offset(ATK_TEXT(paragraph));
351 g_assert_cmpint(offset, ==, 30);
352 g_assert_cmpstr(textCaretMovedResult, ==, "|paragraph|30|");
354 AtkObject* link = atk_object_ref_accessible_child(paragraph, 0);
355 g_assert(ATK_IS_TEXT(link));
356 text = atk_text_get_text(ATK_TEXT(link), 0, -1);
357 g_assert_cmpstr(text, ==, "with a link");
360 result = atk_text_set_caret_offset(ATK_TEXT(link), 5);
361 g_assert_cmpint(result, ==, TRUE);
362 offset = atk_text_get_caret_offset(ATK_TEXT(link));
363 g_assert_cmpint(offset, ==, 5);
364 /* Positions inside links are reported relative to the paragraph. */
365 g_assert_cmpstr(textCaretMovedResult, ==, "|paragraph|17|");
367 AtkObject* list = atk_object_ref_accessible_child(object, 2);
368 g_assert(ATK_OBJECT(list));
369 g_assert(atk_object_get_role(list) == ATK_ROLE_LIST);
370 g_assert_cmpint(atk_object_get_n_accessible_children(list), ==, 3);
372 AtkObject* listItem = atk_object_ref_accessible_child(list, 0);
373 g_assert(ATK_IS_TEXT(listItem));
374 text = atk_text_get_text(ATK_TEXT(listItem), 0, -1);
375 g_assert_cmpstr(text, ==, "1. A list item");
377 g_object_unref(listItem);
379 listItem = atk_object_ref_accessible_child(list, 1);
380 g_assert(ATK_IS_TEXT(listItem));
381 text = atk_text_get_text(ATK_TEXT(listItem), 0, -1);
382 g_assert_cmpstr(text, ==, "2. Block span in a list item\nInline span in a list item");
384 g_object_unref(listItem);
386 listItem = atk_object_ref_accessible_child(list, 2);
387 g_assert(ATK_IS_TEXT(listItem));
388 text = atk_text_get_text(ATK_TEXT(listItem), 0, -1);
389 g_assert_cmpstr(text, ==, "3. Block span in a link in a list item\nInline span in a link in a list item");
392 /* It's not possible to place the caret inside an item's marker. */
393 result = atk_text_set_caret_offset(ATK_TEXT(listItem), 1);
394 g_assert_cmpint(result, ==, FALSE);
396 /* It should be possible to place the caret inside an item's text. */
397 result = atk_text_set_caret_offset(ATK_TEXT(listItem), 5);
398 g_assert_cmpint(result, ==, TRUE);
399 offset = atk_text_get_caret_offset(ATK_TEXT(listItem));
400 g_assert_cmpint(offset, ==, 5);
402 AtkObject* panel = atk_object_ref_accessible_child(object, 3);
403 g_assert(ATK_IS_OBJECT(panel));
404 g_assert(atk_object_get_role(panel) == ATK_ROLE_PANEL);
406 AtkObject* comboBox = atk_object_ref_accessible_child(panel, 0);
407 g_assert(ATK_IS_OBJECT(comboBox));
408 g_assert(!ATK_IS_TEXT(comboBox));
409 g_assert(atk_object_get_role(comboBox) == ATK_ROLE_COMBO_BOX);
411 AtkObject* menuPopup = atk_object_ref_accessible_child(comboBox, 0);
412 g_assert(ATK_IS_OBJECT(menuPopup));
413 g_assert(!ATK_IS_TEXT(menuPopup));
414 g_assert(atk_object_get_role(menuPopup) == ATK_ROLE_MENU);
416 AtkObject* comboBoxOption = atk_object_ref_accessible_child(menuPopup, 0);
417 g_assert(ATK_IS_OBJECT(comboBoxOption));
418 g_assert(atk_object_get_role(comboBoxOption) == ATK_ROLE_MENU_ITEM);
419 g_assert(ATK_IS_TEXT(comboBoxOption));
420 text = atk_text_get_text(ATK_TEXT(comboBoxOption), 0, -1);
421 g_assert_cmpstr(text, ==, "An option in a combo box");
424 /* It's not possible to place the caret inside an option for a combobox. */
425 result = atk_text_set_caret_offset(ATK_TEXT(comboBoxOption), 1);
426 g_assert_cmpint(result, ==, FALSE);
428 AtkObject* textEntry = atk_object_ref_accessible_child(panel, 1);
429 g_assert(ATK_IS_OBJECT(textEntry));
430 g_assert(atk_object_get_role(textEntry) == ATK_ROLE_ENTRY);
431 g_assert(ATK_IS_TEXT(textEntry));
432 text = atk_text_get_text(ATK_TEXT(textEntry), 0, -1);
433 g_assert_cmpstr(text, ==, "foo bar baz");
436 result = atk_text_set_caret_offset(ATK_TEXT(textEntry), 5);
437 g_assert_cmpint(result, ==, TRUE);
438 offset = atk_text_get_caret_offset(ATK_TEXT(textEntry));
439 g_assert_cmpint(offset, ==, 5);
441 AtkObject* table = atk_object_ref_accessible_child(object, 4);
442 g_assert(ATK_IS_OBJECT(table));
443 g_assert(!ATK_IS_TEXT(table));
444 g_assert(atk_object_get_role(table) == ATK_ROLE_TABLE);
445 g_assert_cmpint(atk_object_get_n_accessible_children(table), ==, 4);
447 AtkObject* tableCell = atk_object_ref_accessible_child(table, 0);
448 g_assert(ATK_IS_TEXT(tableCell));
449 g_assert(atk_object_get_role(tableCell) == ATK_ROLE_TABLE_CELL);
450 text = atk_text_get_text(ATK_TEXT(tableCell), 0, -1);
451 g_assert_cmpstr(text, ==, "a table cell");
454 result = atk_text_set_caret_offset(ATK_TEXT(tableCell), 2);
455 g_assert_cmpint(result, ==, TRUE);
456 offset = atk_text_get_caret_offset(ATK_TEXT(tableCell));
457 g_assert_cmpint(offset, ==, 2);
458 g_object_unref(tableCell);
460 /* Even empty table cells should implement AtkText, but report an empty string */
461 tableCell = atk_object_ref_accessible_child(table, 1);
462 g_assert(ATK_IS_TEXT(tableCell));
463 g_assert(atk_object_get_role(tableCell) == ATK_ROLE_TABLE_CELL);
464 text = atk_text_get_text(ATK_TEXT(tableCell), 0, -1);
465 g_assert_cmpstr(text, ==, "");
467 g_object_unref(tableCell);
469 tableCell = atk_object_ref_accessible_child(table, 2);
470 g_assert(ATK_IS_TEXT(tableCell));
471 g_assert(atk_object_get_role(tableCell) == ATK_ROLE_TABLE_CELL);
472 text = atk_text_get_text(ATK_TEXT(tableCell), 0, -1);
473 g_assert_cmpstr(text, ==, "Block span in a link in a table cell\nInline span in a link in a table cell");
475 g_object_unref(tableCell);
477 tableCell = atk_object_ref_accessible_child(table, 3);
478 g_assert(ATK_IS_TEXT(tableCell));
479 g_assert(atk_object_get_role(tableCell) == ATK_ROLE_TABLE_CELL);
480 text = atk_text_get_text(ATK_TEXT(tableCell), 0, -1);
481 g_assert_cmpstr(text, ==, "Block span in a table cell\nInline span in a table cell");
484 header = atk_object_ref_accessible_child(object, 5);
485 g_assert(ATK_IS_TEXT(header));
486 g_assert(atk_object_get_role(header) == ATK_ROLE_HEADING);
487 text = atk_text_get_text(ATK_TEXT(header), 0, -1);
488 g_assert_cmpstr(text, ==, "Block span in a link in a heading\nInline span in a link in a heading");
490 g_object_unref(header);
492 header = atk_object_ref_accessible_child(object, 6);
493 g_assert(ATK_IS_TEXT(header));
494 g_assert(atk_object_get_role(header) == ATK_ROLE_HEADING);
495 text = atk_text_get_text(ATK_TEXT(header), 0, -1);
496 g_assert_cmpstr(text, ==, "Block span in a heading\nInline span in a heading");
499 g_free(textCaretMovedResult);
501 g_object_unref(header);
502 g_object_unref(paragraph);
503 g_object_unref(link);
504 g_object_unref(list);
505 g_object_unref(listItem);
506 g_object_unref(panel);
507 g_object_unref(comboBox);
508 g_object_unref(menuPopup);
509 g_object_unref(comboBoxOption);
510 g_object_unref(textEntry);
511 g_object_unref(table);
512 g_object_unref(tableCell);
513 g_object_unref(webView);
516 static void testWebkitAtkCaretOffsetsAndExtranousWhiteSpaces()
518 WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
519 g_object_ref_sink(webView);
520 GtkAllocation allocation = { 0, 0, 800, 600 };
521 gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
522 webkit_web_view_load_string(webView, contentsWithExtraneousWhiteSpaces, 0, 0, 0);
524 /* Enable caret browsing. */
525 WebKitWebSettings* settings = webkit_web_view_get_settings(webView);
526 g_object_set(G_OBJECT(settings), "enable-caret-browsing", TRUE, NULL);
527 webkit_web_view_set_settings(webView, settings);
529 /* Get to the inner AtkText object. */
530 AtkObject* object = getWebAreaObject(webView);
532 object = atk_object_ref_accessible_child(object, 0);
535 AtkText* textObject = ATK_TEXT(object);
536 g_assert(ATK_IS_TEXT(textObject));
538 gchar* text = atk_text_get_text(textObject, 0, -1);
539 g_assert_cmpstr(text, ==, "This paragraph is borked!");
542 gint characterCount = atk_text_get_character_count(textObject);
543 g_assert_cmpint(characterCount, ==, 25);
545 gboolean result = atk_text_set_caret_offset(textObject, characterCount - 1);
546 g_assert_cmpint(result, ==, TRUE);
548 gint caretOffset = atk_text_get_caret_offset(textObject);
549 g_assert_cmpint(caretOffset, ==, characterCount - 1);
551 result = atk_text_set_caret_offset(textObject, characterCount);
552 g_assert_cmpint(result, ==, TRUE);
554 caretOffset = atk_text_get_caret_offset(textObject);
555 g_assert_cmpint(caretOffset, ==, characterCount);
557 g_object_unref(object);
558 g_object_unref(webView);
561 static void testWebkitAtkComboBox()
563 WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
564 g_object_ref_sink(webView);
565 GtkAllocation allocation = { 0, 0, 800, 600 };
566 gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
567 webkit_web_view_load_string(webView, comboBoxSelector, 0, 0, 0);
569 AtkObject* object = getWebAreaObject(webView);
572 AtkObject* formObject = atk_object_ref_accessible_child(object, 0);
573 g_assert(formObject);
575 AtkObject* comboBox = atk_object_ref_accessible_child(formObject, 0);
576 g_assert(ATK_IS_OBJECT(comboBox));
578 AtkObject* menuPopup = atk_object_ref_accessible_child(comboBox, 0);
579 g_assert(ATK_IS_OBJECT(menuPopup));
581 AtkObject* item1 = atk_object_ref_accessible_child(menuPopup, 0);
582 g_assert(ATK_IS_OBJECT(item1));
584 AtkObject* item2 = atk_object_ref_accessible_child(menuPopup, 1);
585 g_assert(ATK_IS_OBJECT(item2));
588 g_assert(atk_object_get_role(comboBox) == ATK_ROLE_COMBO_BOX);
589 g_assert(atk_object_get_role(menuPopup) == ATK_ROLE_MENU);
590 g_assert(atk_object_get_role(item1) == ATK_ROLE_MENU_ITEM);
591 g_assert(atk_object_get_role(item2) == ATK_ROLE_MENU_ITEM);
593 /* Check the implementation of the AtkSelection interface. */
594 g_assert(ATK_IS_SELECTION(comboBox));
595 AtkSelection* atkSelection = ATK_SELECTION(comboBox);
596 g_assert_cmpint(atk_selection_get_selection_count(atkSelection), ==, 1);
597 g_assert(atk_selection_is_child_selected(atkSelection, 0));
598 g_assert(!atk_selection_is_child_selected(atkSelection, 1));
599 AtkObject* selectedItem = atk_selection_ref_selection(atkSelection, 0);
600 g_assert(selectedItem == item1);
601 g_object_unref(selectedItem);
603 /* Check that the menu popup has 0 links and doesn't crash from checking. */
604 gint nLinks = atk_hypertext_get_n_links(ATK_HYPERTEXT(menuPopup));
605 g_assert_cmpint(nLinks, ==, 0);
607 /* Check the implementations of the AtkAction interface. */
608 g_assert(ATK_IS_ACTION(comboBox));
609 AtkAction* atkAction = ATK_ACTION(comboBox);
610 g_assert_cmpint(atk_action_get_n_actions(atkAction), ==, 1);
611 g_assert(atk_action_do_action(atkAction, 0));
613 g_assert(ATK_IS_ACTION(menuPopup));
614 atkAction = ATK_ACTION(menuPopup);
615 g_assert_cmpint(atk_action_get_n_actions(atkAction), ==, 1);
616 g_assert(atk_action_do_action(atkAction, 0));
618 g_assert(ATK_IS_ACTION(item1));
619 atkAction = ATK_ACTION(item1);
620 g_assert_cmpint(atk_action_get_n_actions(atkAction), ==, 1);
621 g_assert(atk_action_do_action(atkAction, 0));
623 g_assert(ATK_IS_ACTION(item2));
624 atkAction = ATK_ACTION(item2);
625 g_assert_cmpint(atk_action_get_n_actions(atkAction), ==, 1);
626 g_assert(atk_action_do_action(atkAction, 0));
628 /* After selecting the second item, selection should have changed. */
629 g_assert_cmpint(atk_selection_get_selection_count(atkSelection), ==, 1);
630 g_assert(!atk_selection_is_child_selected(atkSelection, 0));
631 g_assert(atk_selection_is_child_selected(atkSelection, 1));
632 selectedItem = atk_selection_ref_selection(atkSelection, 0);
633 g_assert(selectedItem == item2);
634 g_object_unref(selectedItem);
636 /* Check the implementation of the AtkText interface. */
637 g_assert(ATK_IS_TEXT(item1));
638 AtkText* atkText = ATK_TEXT(item1);
639 char *text = atk_text_get_text(atkText, 0, -1);
640 g_assert_cmpstr(text, ==, "foo");
642 text = atk_text_get_text(atkText, 0, 2);
643 g_assert_cmpstr(text, ==, "fo");
646 g_assert(ATK_IS_TEXT(item2));
647 atkText = ATK_TEXT(item2);
648 text = atk_text_get_text(atkText, 0, -1);
649 g_assert_cmpstr(text, ==, "bar");
651 text = atk_text_get_text(atkText, 1, 3);
652 g_assert_cmpstr(text, ==, "ar");
655 g_object_unref(formObject);
656 g_object_unref(comboBox);
657 g_object_unref(menuPopup);
658 g_object_unref(item1);
659 g_object_unref(item2);
660 g_object_unref(webView);
663 static gchar* loadingEventsResult = 0;
665 static void updateLoadingEventsResult(const gchar* signalName)
667 g_assert(signalName);
669 gchar* previousResult = loadingEventsResult;
670 loadingEventsResult = g_strdup_printf("%s|%s", previousResult, signalName);
671 g_free(previousResult);
674 static gboolean documentLoadingEventCallback(GSignalInvocationHint *signalHint, guint numParamValues, const GValue *paramValues, gpointer data)
676 // At least we should receive the instance emitting the signal.
677 if (numParamValues < 1)
680 GSignalQuery signal_query;
681 g_signal_query(signalHint->signal_id, &signal_query);
683 updateLoadingEventsResult(signal_query.signal_name);
687 static void testWebkitAtkDocumentLoadingEvents()
689 WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
690 g_object_ref_sink(webView);
691 GtkAllocation allocation = { 0, 0, 800, 600 };
692 gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
694 /* Connect globally to see those events during a future load. */
695 guint loadCompleteListenerId = atk_add_global_event_listener(documentLoadingEventCallback, "ATK:AtkDocument:load-complete");
697 /* Do the load, so we can see those events happening. */
698 loadingEventsResult = g_strdup("");
699 webkit_web_view_load_string(webView, contents, 0, 0, 0);
701 /* Trigger the creation of the full accessibility hierarchy by
702 asking for the webArea object, so we can listen to events. */
703 getWebAreaObject(webView);
705 atk_remove_global_event_listener(loadCompleteListenerId);
707 g_assert_cmpstr(loadingEventsResult, ==, "|load-complete");
709 g_free(loadingEventsResult);
710 g_object_unref(webView);
713 static void testWebkitAtkEmbeddedObjects()
715 WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
716 g_object_ref_sink(webView);
717 GtkAllocation allocation = { 0, 0, 800, 600 };
718 gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
719 webkit_web_view_load_string(webView, embeddedObjects, 0, 0, 0);
721 AtkObject* object = getWebAreaObject(webView);
724 AtkText* paragraph1 = ATK_TEXT(atk_object_ref_accessible_child(object, 0));
725 g_assert(ATK_IS_TEXT(paragraph1));
726 g_assert(ATK_IS_HYPERTEXT(paragraph1));
728 const gchar* expectedText = "Choose: \357\277\274foo \357\277\274bar (pick one)";
729 char* text = atk_text_get_text(paragraph1, 0, -1);
730 g_assert_cmpstr(text, ==, expectedText);
733 gint nLinks = atk_hypertext_get_n_links(ATK_HYPERTEXT(paragraph1));
734 g_assert_cmpint(nLinks, ==, 2);
736 AtkHyperlink* hLink = atk_hypertext_get_link(ATK_HYPERTEXT(paragraph1), 0);
737 g_assert(ATK_HYPERLINK(hLink));
738 AtkObject* hLinkObject = atk_hyperlink_get_object(hLink, 0);
739 g_assert(ATK_OBJECT(hLinkObject));
740 g_assert(atk_object_get_role(hLinkObject) == ATK_ROLE_CHECK_BOX);
741 g_assert_cmpint(atk_hyperlink_get_start_index(hLink), ==, 8);
742 g_assert_cmpint(atk_hyperlink_get_end_index(hLink), ==, 9);
743 g_assert_cmpint(atk_hyperlink_get_n_anchors(hLink), ==, 1);
744 g_assert_cmpstr(atk_hyperlink_get_uri(hLink, 0), ==, 0);
746 AtkText* paragraph2 = ATK_TEXT(atk_object_ref_accessible_child(object, 1));
747 g_assert(ATK_IS_TEXT(paragraph2));
748 g_assert(ATK_IS_HYPERTEXT(paragraph2));
750 expectedText = "Choose: \357\277\274 (pick one)";
751 text = atk_text_get_text(paragraph2, 0, -1);
752 g_assert_cmpstr(text, ==, expectedText);
755 nLinks = atk_hypertext_get_n_links(ATK_HYPERTEXT(paragraph2));
756 g_assert_cmpint(nLinks, ==, 1);
758 hLink = atk_hypertext_get_link(ATK_HYPERTEXT(paragraph2), 0);
759 g_assert(ATK_HYPERLINK(hLink));
760 hLinkObject = atk_hyperlink_get_object(hLink, 0);
761 g_assert(ATK_OBJECT(hLinkObject));
762 g_assert(atk_object_get_role(hLinkObject) == ATK_ROLE_COMBO_BOX);
763 g_assert_cmpint(atk_hyperlink_get_start_index(hLink), ==, 8);
764 g_assert_cmpint(atk_hyperlink_get_end_index(hLink), ==, 9);
765 g_assert_cmpint(atk_hyperlink_get_n_anchors(hLink), ==, 1);
766 g_assert_cmpstr(atk_hyperlink_get_uri(hLink, 0), ==, 0);
768 AtkText* paragraph3 = ATK_TEXT(atk_object_ref_accessible_child(object, 2));
769 g_assert(ATK_IS_TEXT(paragraph3));
770 g_assert(ATK_IS_HYPERTEXT(paragraph3));
772 expectedText = "\357\277\274";
773 text = atk_text_get_text(paragraph3, 0, -1);
774 g_assert_cmpstr(text, ==, expectedText);
777 nLinks = atk_hypertext_get_n_links(ATK_HYPERTEXT(paragraph3));
778 g_assert_cmpint(nLinks, ==, 1);
780 hLink = atk_hypertext_get_link(ATK_HYPERTEXT(paragraph3), 0);
781 g_assert(ATK_HYPERLINK(hLink));
782 hLinkObject = atk_hyperlink_get_object(hLink, 0);
783 g_assert(ATK_OBJECT(hLinkObject));
784 g_assert(atk_object_get_role(hLinkObject) == ATK_ROLE_PUSH_BUTTON);
785 g_assert_cmpint(atk_hyperlink_get_start_index(hLink), ==, 0);
786 g_assert_cmpint(atk_hyperlink_get_end_index(hLink), ==, 1);
787 g_assert_cmpint(atk_hyperlink_get_n_anchors(hLink), ==, 1);
788 g_assert_cmpstr(atk_hyperlink_get_uri(hLink, 0), ==, 0);
790 g_object_unref(paragraph1);
791 g_object_unref(paragraph2);
792 g_object_unref(paragraph3);
793 g_object_unref(webView);
796 static void testWebkitAtkGetTextAtOffset()
798 WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
799 g_object_ref_sink(webView);
800 GtkAllocation allocation = { 0, 0, 800, 600 };
801 gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
802 webkit_web_view_load_string(webView, contents, 0, 0, 0);
804 /* Get to the inner AtkText object. */
805 AtkObject* object = getWebAreaObject(webView);
807 object = atk_object_ref_accessible_child(object, 0);
810 AtkText* textObject = ATK_TEXT(object);
811 g_assert(ATK_IS_TEXT(textObject));
813 runGetTextTests(textObject);
815 g_object_unref(object);
816 g_object_unref(webView);
819 static void testWebkitAtkGetTextAtOffsetNewlines()
821 WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
822 g_object_ref_sink(webView);
823 GtkAllocation allocation = { 0, 0, 800, 600 };
824 gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
825 webkit_web_view_load_string(webView, contentsWithNewlines, 0, 0, 0);
827 /* Get to the inner AtkText object. */
828 AtkObject* object = getWebAreaObject(webView);
830 object = atk_object_ref_accessible_child(object, 0);
833 AtkText* textObject = ATK_TEXT(object);
834 g_assert(ATK_IS_TEXT(textObject));
836 runGetTextTests(textObject);
838 g_object_unref(object);
839 g_object_unref(webView);
842 static void testWebkitAtkGetTextAtOffsetTextarea()
844 WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
845 g_object_ref_sink(webView);
846 GtkAllocation allocation = { 0, 0, 800, 600 };
847 gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
848 webkit_web_view_load_string(webView, contentsInTextarea, 0, 0, 0);
850 /* Get to the inner AtkText object. */
851 AtkObject* object = getWebAreaObject(webView);
853 AtkObject* child = atk_object_ref_accessible_child(object, 0);
855 AtkObject* grandchild = atk_object_ref_accessible_child(child, 0);
856 g_assert(grandchild);
858 AtkText* textObject = ATK_TEXT(grandchild);
859 g_assert(ATK_IS_TEXT(textObject));
861 runGetTextTests(textObject);
863 g_object_unref(child);
864 g_object_unref(grandchild);
865 g_object_unref(webView);
868 static void testWebkitAtkGetTextAtOffsetTextInput()
870 WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
871 g_object_ref_sink(webView);
872 GtkAllocation allocation = { 0, 0, 800, 600 };
873 gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
874 webkit_web_view_load_string(webView, contentsInTextInput, 0, 0, 0);
876 /* Get to the inner AtkText object. */
877 AtkObject* object = getWebAreaObject(webView);
879 AtkObject* child = atk_object_ref_accessible_child(object, 0);
881 AtkObject* grandchild = atk_object_ref_accessible_child(child, 0);
882 g_assert(grandchild);
884 AtkText* textObject = ATK_TEXT(grandchild);
885 g_assert(ATK_IS_TEXT(textObject));
887 runGetTextTests(textObject);
889 g_object_unref(child);
890 g_object_unref(grandchild);
891 g_object_unref(webView);
894 static void testWebkitAtkGetTextAtOffsetWithPreformattedText()
896 WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
897 g_object_ref_sink(webView);
898 GtkAllocation allocation = { 0, 0, 800, 600 };
899 gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
900 webkit_web_view_load_string(webView, contentsWithPreformattedText, 0, 0, 0);
902 AtkObject* object = getWebAreaObject(webView);
905 AtkObject* preformattedText = atk_object_ref_accessible_child(object, 0);
906 g_assert(ATK_IS_OBJECT(preformattedText));
907 g_assert(atk_object_get_role(preformattedText) == ATK_ROLE_PANEL);
908 g_assert(ATK_IS_TEXT(preformattedText));
909 char* text = atk_text_get_text(ATK_TEXT(preformattedText), 0, -1);
910 g_assert_cmpstr(text, ==, "\t\n\tfirst line\n\tsecond line\n");
913 /* Try retrieving all the lines indicating the position of the offsets at the beginning of each of them. */
914 testGetTextFunction(ATK_TEXT(preformattedText), atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_START, 0, "\t\n", 0, 2);
915 testGetTextFunction(ATK_TEXT(preformattedText), atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_START, 2, "\tfirst line\n", 2, 14);
916 testGetTextFunction(ATK_TEXT(preformattedText), atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_START, 14, "\tsecond line\n", 14, 27);
918 g_object_unref(preformattedText);
919 g_object_unref(webView);
922 static void testWebkitAtkGetTextAtOffsetWithSpecialCharacters()
924 WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
925 g_object_ref_sink(webView);
926 GtkAllocation allocation = { 0, 0, 800, 600 };
927 gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
928 webkit_web_view_load_string(webView, contentsWithSpecialChars, 0, 0, 0);
930 /* Get to the inner AtkText object. */
931 AtkObject* object = getWebAreaObject(webView);
934 AtkObject* paragraph = atk_object_ref_accessible_child(object, 0);
935 g_assert(ATK_IS_TEXT(paragraph));
937 gchar* expectedText = g_strdup("\302\253\302\240This is a paragraph with \342\200\234special\342\200\235 characters inside.\302\240\302\273");
938 char* text = atk_text_get_text(ATK_TEXT(paragraph), 0, -1);
939 g_assert_cmpstr(text, ==, expectedText);
942 /* Check that getting the text with ATK_TEXT_BOUNDARY_LINE_START
943 and ATK_TEXT_BOUNDARY_LINE_END does not crash because of not
944 properly handling characters inside the UTF-8 string. */
945 testGetTextFunction(ATK_TEXT(paragraph), atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_START, 0, expectedText, 0, 57);
946 testGetTextFunction(ATK_TEXT(paragraph), atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_END, 0, expectedText, 0, 57);
947 g_free(expectedText);
949 AtkObject* list = atk_object_ref_accessible_child(object, 1);
950 g_assert(ATK_OBJECT(list));
952 AtkText* listItem = ATK_TEXT(atk_object_ref_accessible_child(list, 0));
953 g_assert(ATK_IS_TEXT(listItem));
955 text = atk_text_get_text(ATK_TEXT(listItem), 0, -1);
956 g_assert_cmpstr(text, ==, "\342\200\242 List item with some text that wraps across different lines.");
959 /* Check that getting the text with ATK_TEXT_BOUNDARY_LINE_START
960 and ATK_TEXT_BOUNDARY_LINE_END for line items with bullets
961 (special character) and wrapped text always return the right
962 piece of text for each line. */
963 testGetTextFunction(ATK_TEXT(listItem), atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_START, 3, "\342\200\242 List item ", 0, 12);
964 testGetTextFunction(ATK_TEXT(listItem), atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_START, 13, "with some ", 12, 22);
965 testGetTextFunction(ATK_TEXT(listItem), atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_END, 0, "\342\200\242 List item", 0, 11);
966 testGetTextFunction(ATK_TEXT(listItem), atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_END, 12, " with some", 11, 21);
968 g_object_unref(listItem);
970 listItem = ATK_TEXT(atk_object_ref_accessible_child(list, 1));
971 g_assert(ATK_IS_TEXT(listItem));
973 /* Check that placing the same text in a paragraph doesn't break things. */
974 text = atk_text_get_text(ATK_TEXT(listItem), 0, -1);
975 g_assert_cmpstr(text, ==, "\342\200\242 List item with some text that wraps across different lines.");
978 testGetTextFunction(ATK_TEXT(listItem), atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_START, 3, "\342\200\242 List item ", 0, 12);
979 testGetTextFunction(ATK_TEXT(listItem), atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_START, 13, "with some ", 12, 22);
980 testGetTextFunction(ATK_TEXT(listItem), atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_END, 0, "\342\200\242 List item", 0, 11);
981 testGetTextFunction(ATK_TEXT(listItem), atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_END, 12, " with some", 11, 21);
983 g_object_unref(list);
984 g_object_unref(listItem);
985 g_object_unref(paragraph);
986 g_object_unref(webView);
989 static void testWebkitAtkGetTextAtOffsetWithWrappedLines()
991 WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
992 g_object_ref_sink(webView);
993 GtkAllocation allocation = { 0, 0, 800, 600 };
994 gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
995 webkit_web_view_load_string(webView, contentsWithWrappedLines, 0, 0, 0);
997 /* Enable caret browsing. */
998 WebKitWebSettings* settings = webkit_web_view_get_settings(webView);
999 g_object_set(settings, "enable-caret-browsing", TRUE, NULL);
1000 webkit_web_view_set_settings(webView, settings);
1002 /* Get to the inner AtkText object. */
1003 AtkObject* object = getWebAreaObject(webView);
1006 /* Check the paragraph with the text wrapped because of max-width. */
1007 AtkText* paragraph1 = ATK_TEXT(atk_object_ref_accessible_child(object, 0));
1008 g_assert(ATK_IS_TEXT(paragraph1));
1010 gchar* text = atk_text_get_text(paragraph1, 0, -1);
1011 g_assert_cmpstr(text, ==, "This is one line wrapped because of the maximum width of its container.");
1014 testGetTextFunction(paragraph1, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_CHAR, 16, "e", 15, 16);
1015 testGetTextFunction(paragraph1, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_CHAR, 16, " ", 16, 17);
1016 testGetTextFunction(paragraph1, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_CHAR, 16, "w", 17, 18);
1018 testGetTextFunction(paragraph1, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_WORD_START, 16, "one ", 8, 12);
1019 testGetTextFunction(paragraph1, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START, 16, "line ", 12, 17);
1020 testGetTextFunction(paragraph1, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_WORD_START, 16, "wrapped ", 17, 25);
1022 testGetTextFunction(paragraph1, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_WORD_END, 16, " line", 11, 16);
1023 testGetTextFunction(paragraph1, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END, 16, " wrapped", 16, 24);
1024 testGetTextFunction(paragraph1, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_WORD_END, 16, " because", 24, 32);
1026 testGetTextFunction(paragraph1, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_LINE_START, 17, "This is one line ", 0, 17);
1027 testGetTextFunction(paragraph1, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_START, 17, "wrapped because ", 17, 33);
1028 testGetTextFunction(paragraph1, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_LINE_START, 17, "of the maximum ", 33, 48);
1030 /* The following line won't work at the moment because of a bug in GailTextUtil.
1031 see https://bugzilla.gnome.org/show_bug.cgi?id=703554
1032 testGetTextFunction(paragraph1, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_LINE_END, 17, "This is one line", 0, 16); */
1033 testGetTextFunction(paragraph1, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_END, 17, " wrapped because", 16, 32);
1034 testGetTextFunction(paragraph1, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_LINE_END, 17, " of the maximum", 32, 47);
1036 g_object_unref(paragraph1);
1038 /* Check the paragraph with the text wrapped because of <br> elements. */
1039 AtkText* paragraph2 = ATK_TEXT(atk_object_ref_accessible_child(object, 1));
1040 g_assert(ATK_IS_TEXT(paragraph2));
1042 text = atk_text_get_text(paragraph2, 0, -1);
1043 g_assert_cmpstr(text, ==, "This is another line wrapped\nbecause of one forced\nline break in the middle.");
1046 testGetTextFunction(paragraph2, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_CHAR, 28, "d", 27, 28);
1047 testGetTextFunction(paragraph2, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_CHAR, 28, "\n", 28, 29);
1048 testGetTextFunction(paragraph2, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_CHAR, 28, "b", 29, 30);
1050 testGetTextFunction(paragraph2, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_WORD_START, 28, "line ", 16, 21);
1051 testGetTextFunction(paragraph2, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START, 28, "wrapped\n", 21, 29);
1052 testGetTextFunction(paragraph2, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_WORD_START, 28, "because ", 29, 37);
1054 testGetTextFunction(paragraph2, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_WORD_END, 28, " wrapped", 20, 28);
1055 testGetTextFunction(paragraph2, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END, 28, "\nbecause", 28, 36);
1056 testGetTextFunction(paragraph2, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_WORD_END, 28, " of", 36, 39);
1058 testGetTextFunction(paragraph2, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_LINE_START, 30, "This is another line wrapped\n", 0, 29);
1059 testGetTextFunction(paragraph2, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_START, 30, "because of one forced\n", 29, 51);
1060 testGetTextFunction(paragraph2, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_LINE_START, 30, "line break in the middle.", 51, 76);
1062 /* The following line won't work at the moment because of a bug in GailTextUtil.
1063 see https://bugzilla.gnome.org/show_bug.cgi?id=703554
1064 testGetTextFunction(paragraph2, atk_text_get_text_before_offset, ATK_TEXT_BOUNDARY_LINE_END, 30, "This is another line wrapped", 0, 28); */
1065 testGetTextFunction(paragraph2, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_LINE_END, 30, "\nbecause of one forced", 28, 50);
1066 testGetTextFunction(paragraph2, atk_text_get_text_after_offset, ATK_TEXT_BOUNDARY_LINE_END, 30, "\nline break in the middle.", 50, 76);
1068 g_object_unref(paragraph2);
1070 g_object_unref(webView);
1073 static void testWebkitAtkGetTextAtOffsetWithEmbeddedObjects()
1075 WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
1076 g_object_ref_sink(webView);
1077 GtkAllocation allocation = { 0, 0, 800, 600 };
1078 gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
1079 webkit_web_view_load_string(webView, contentsWithEmbeddedObjects, 0, 0, 0);
1081 /* Enable caret browsing. */
1082 WebKitWebSettings* settings = webkit_web_view_get_settings(webView);
1083 g_object_set(settings, "enable-caret-browsing", TRUE, NULL);
1084 webkit_web_view_set_settings(webView, settings);
1086 /* Get to the inner AtkText object. */
1087 AtkObject* object = getWebAreaObject(webView);
1090 /* Check the paragraph with the text wrapped because of max-width. */
1091 AtkText* paragraph = ATK_TEXT(atk_object_ref_accessible_child(object, 0));
1092 g_assert(ATK_IS_TEXT(paragraph));
1094 gchar* text = atk_text_get_text(paragraph, 0, -1);
1095 g_assert_cmpstr(text, ==, "This is one line containing two \357\277\274 embedded objects \357\277\274 in the middle.");
1098 /* Check right before the first embedded object */
1099 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_CHAR, 32, "\357\277\274", 32, 33);
1100 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START, 32, "two \357\277\274 ", 28, 34);
1101 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END, 32, " \357\277\274 embedded", 31, 42);
1103 /* Check right after the first embedded object (and before the first word after it) */
1104 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_CHAR, 33, " ", 33, 34);
1105 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START, 33, "two \357\277\274 ", 28, 34);
1106 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END, 33, " \357\277\274 embedded", 31, 42);
1108 /* Check at the beginning of the first word between the two embedded objects */
1109 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_CHAR, 34, "e", 34, 35);
1110 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START, 34, "embedded ", 34, 43);
1111 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END, 34, " \357\277\274 embedded", 31, 42);
1113 /* Check at the end of the first word between the two embedded objects */
1114 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_CHAR, 42, " ", 42, 43);
1115 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START, 42, "embedded ", 34, 43);
1116 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END, 42, " objects", 42, 50);
1118 /* Check right before the second embedded object */
1119 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_CHAR, 51, "\357\277\274", 51, 52);
1120 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START, 51, "objects \357\277\274 ", 43, 53);
1121 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END, 51, " \357\277\274 in", 50, 55);
1123 /* Check right after the second embedded object (and before the first word after it) */
1124 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_CHAR, 52, " ", 52, 53);
1125 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START, 52, "objects \357\277\274 ", 43, 53);
1126 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END, 52, " \357\277\274 in", 50, 55);
1128 /* Check at the beginning of the first word after the two embedded objects */
1129 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_CHAR, 53, "i", 53, 54);
1130 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START, 53, "in ", 53, 56);
1131 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END, 53, " \357\277\274 in", 50, 55);
1133 /* Check at the end of the first word after the two embedded objects */
1134 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_CHAR, 55, " ", 55, 56);
1135 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_START, 55, "in ", 53, 56);
1136 testGetTextFunction(paragraph, atk_text_get_text_at_offset, ATK_TEXT_BOUNDARY_WORD_END, 55, " the", 55, 59);
1138 g_object_unref(paragraph);
1139 g_object_unref(webView);
1142 static void testWebkitAtkGetTextInParagraphAndBodySimple()
1144 WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
1145 g_object_ref_sink(webView);
1146 GtkAllocation allocation = { 0, 0, 800, 600 };
1147 gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
1148 webkit_web_view_load_string(webView, contentsInParagraphAndBodySimple, 0, 0, 0);
1150 /* Get to the inner AtkText object. */
1151 AtkObject* object = getWebAreaObject(webView);
1153 AtkObject* object1 = atk_object_ref_accessible_child(object, 0);
1155 AtkObject* object2 = atk_object_ref_accessible_child(object, 1);
1158 AtkText* textObject1 = ATK_TEXT(object1);
1159 g_assert(ATK_IS_TEXT(textObject1));
1160 AtkText* textObject2 = ATK_TEXT(object2);
1161 g_assert(ATK_IS_TEXT(textObject2));
1163 char *text = atk_text_get_text(textObject1, 0, -1);
1164 g_assert_cmpstr(text, ==, "This is a test.");
1167 text = atk_text_get_text(textObject2, 0, 12);
1168 g_assert_cmpstr(text, ==, "Hello world.");
1171 g_object_unref(object1);
1172 g_object_unref(object2);
1173 g_object_unref(webView);
1176 static void testWebkitAtkGetTextInParagraphAndBodyModerate()
1178 WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
1179 g_object_ref_sink(webView);
1180 GtkAllocation allocation = { 0, 0, 800, 600 };
1181 gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
1182 webkit_web_view_load_string(webView, contentsInParagraphAndBodyModerate, 0, 0, 0);
1184 /* Get to the inner AtkText object. */
1185 AtkObject* object = getWebAreaObject(webView);
1187 AtkObject* object1 = atk_object_ref_accessible_child(object, 0);
1189 AtkObject* object2 = atk_object_ref_accessible_child(object, 1);
1192 AtkText* textObject1 = ATK_TEXT(object1);
1193 g_assert(ATK_IS_TEXT(textObject1));
1194 AtkText* textObject2 = ATK_TEXT(object2);
1195 g_assert(ATK_IS_TEXT(textObject2));
1197 char *text = atk_text_get_text(textObject1, 0, -1);
1198 g_assert_cmpstr(text, ==, "This is a test.");
1201 text = atk_text_get_text(textObject2, 0, 53);
1202 g_assert_cmpstr(text, ==, "Hello world.\nThis sentence is green.\nThis one is not.");
1205 g_object_unref(object1);
1206 g_object_unref(object2);
1207 g_object_unref(webView);
1210 static void testWebkitAtkGetTextInTable()
1212 WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
1213 g_object_ref_sink(webView);
1214 GtkAllocation allocation = { 0, 0, 800, 600 };
1215 gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
1216 webkit_web_view_load_string(webView, contentsInTable, 0, 0, 0);
1218 AtkObject* object = getWebAreaObject(webView);
1220 object = atk_object_ref_accessible_child(object, 0);
1223 /* Tables should not implement AtkText. */
1224 g_assert(!G_TYPE_INSTANCE_GET_INTERFACE(object, ATK_TYPE_TEXT, AtkTextIface));
1226 g_object_unref(object);
1227 g_object_unref(webView);
1230 static void testWebkitAtkGetHeadersInTable()
1232 WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
1233 g_object_ref_sink(webView);
1234 GtkAllocation allocation = { 0, 0, 800, 600 };
1235 gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
1236 webkit_web_view_load_string(webView, contentsInTableWithHeaders, 0, 0, 0);
1238 AtkObject* axWebView = getWebAreaObject(webView);
1239 g_assert(axWebView);
1241 /* Check table with both column and row headers. */
1242 AtkObject* table = atk_object_ref_accessible_child(axWebView, 0);
1244 g_assert(atk_object_get_role(table) == ATK_ROLE_TABLE);
1246 AtkObject* colHeader = atk_table_get_column_header(ATK_TABLE(table), 0);
1247 g_assert(colHeader);
1248 g_assert(atk_object_get_role(colHeader) == ATK_ROLE_TABLE_CELL);
1249 g_assert(atk_object_get_index_in_parent(colHeader) == 0);
1251 colHeader = atk_table_get_column_header(ATK_TABLE(table), 1);
1252 g_assert(colHeader);
1253 g_assert(atk_object_get_role(colHeader) == ATK_ROLE_TABLE_CELL);
1254 g_assert(atk_object_get_index_in_parent(colHeader) == 1);
1256 colHeader = atk_table_get_column_header(ATK_TABLE(table), 2);
1257 g_assert(colHeader);
1258 g_assert(atk_object_get_role(colHeader) == ATK_ROLE_TABLE_CELL);
1259 g_assert(atk_object_get_index_in_parent(colHeader) == 2);
1261 colHeader = atk_table_get_column_header(ATK_TABLE(table), 3);
1262 g_assert(colHeader);
1263 g_assert(atk_object_get_role(colHeader) == ATK_ROLE_TABLE_CELL);
1264 g_assert(atk_object_get_index_in_parent(colHeader) == 2);
1266 AtkObject* rowHeader = atk_table_get_row_header(ATK_TABLE(table), 0);
1267 g_assert(rowHeader);
1268 g_assert(atk_object_get_role(rowHeader) == ATK_ROLE_TABLE_CELL);
1269 g_assert(atk_object_get_index_in_parent(rowHeader) == 0);
1271 rowHeader = atk_table_get_row_header(ATK_TABLE(table), 1);
1272 g_assert(rowHeader);
1273 g_assert(atk_object_get_role(rowHeader) == ATK_ROLE_TABLE_CELL);
1274 g_assert(atk_object_get_index_in_parent(rowHeader) == 3);
1276 rowHeader = atk_table_get_row_header(ATK_TABLE(table), 2);
1277 g_assert(rowHeader);
1278 g_assert(atk_object_get_role(rowHeader) == ATK_ROLE_TABLE_CELL);
1279 g_assert(atk_object_get_index_in_parent(rowHeader) == 7);
1281 rowHeader = atk_table_get_row_header(ATK_TABLE(table), 3);
1282 g_assert(rowHeader);
1283 g_assert(atk_object_get_role(rowHeader) == ATK_ROLE_TABLE_CELL);
1284 g_assert(atk_object_get_index_in_parent(rowHeader) == 7);
1286 g_object_unref(table);
1288 /* Check table with no headers at all. */
1289 table = atk_object_ref_accessible_child(axWebView, 1);
1291 g_assert(atk_object_get_role(table) == ATK_ROLE_TABLE);
1293 colHeader = atk_table_get_column_header(ATK_TABLE(table), 0);
1294 g_assert(colHeader == 0);
1296 colHeader = atk_table_get_column_header(ATK_TABLE(table), 1);
1297 g_assert(colHeader == 0);
1299 rowHeader = atk_table_get_row_header(ATK_TABLE(table), 0);
1300 g_assert(rowHeader == 0);
1302 rowHeader = atk_table_get_row_header(ATK_TABLE(table), 1);
1303 g_assert(rowHeader == 0);
1305 g_object_unref(table);
1306 g_object_unref(webView);
1309 static gint compAtkAttribute(AtkAttribute* a1, AtkAttribute* a2)
1311 gint strcmpVal = g_strcmp0(a1->name, a2->name);
1314 return g_strcmp0(a1->value, a2->value);
1317 static gint compAtkAttributeName(AtkAttribute* a1, AtkAttribute* a2)
1319 return g_strcmp0(a1->name, a2->name);
1322 static gboolean atkAttributeSetAttributeNameHasValue(AtkAttributeSet* set, const gchar* attributeName, const gchar* value)
1325 at.name = (gchar*)attributeName;
1326 GSList* element = g_slist_find_custom(set, &at, (GCompareFunc)compAtkAttributeName);
1327 return element && !g_strcmp0(((AtkAttribute*)(element->data))->value, value);
1330 static gboolean atkAttributeSetContainsAttributeName(AtkAttributeSet* set, const gchar* attributeName)
1333 at.name = (gchar*)attributeName;
1334 return g_slist_find_custom(set, &at, (GCompareFunc)compAtkAttributeName) ? true : false;
1337 static gboolean atkAttributeSetAttributeHasValue(AtkAttributeSet* set, AtkTextAttribute attribute, const gchar* value)
1339 return atkAttributeSetAttributeNameHasValue(set, atk_text_attribute_get_name(attribute), value);
1342 static gboolean atkAttributeSetAreEqual(AtkAttributeSet* set1, AtkAttributeSet* set2)
1347 set1 = g_slist_sort(set1, (GCompareFunc)compAtkAttribute);
1348 set2 = g_slist_sort(set2, (GCompareFunc)compAtkAttribute);
1351 if (!set2 || compAtkAttribute(set1->data, set2->data))
1361 static void testWebkitAtkTextAttributes()
1363 WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
1364 g_object_ref_sink(webView);
1365 GtkAllocation allocation = { 0, 0, 800, 600 };
1366 gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
1367 webkit_web_view_load_string(webView, textWithAttributes, 0, 0, 0);
1369 AtkObject* object = getWebAreaObject(webView);
1372 AtkObject* child = atk_object_ref_accessible_child(object, 0);
1373 g_assert(child && ATK_IS_TEXT(child));
1374 AtkText* childText = ATK_TEXT(child);
1378 AtkAttributeSet* set1 = atk_text_get_run_attributes(childText, 0, &startOffset, &endOffset);
1379 g_assert_cmpint(startOffset, ==, 0);
1380 g_assert_cmpint(endOffset, ==, 12);
1381 g_assert(atkAttributeSetAreEqual(set1, 0));
1383 AtkAttributeSet* set2 = atk_text_get_run_attributes(childText, 15, &startOffset, &endOffset);
1384 g_assert_cmpint(startOffset, ==, 12);
1385 g_assert_cmpint(endOffset, ==, 17);
1386 g_assert(atkAttributeSetAttributeHasValue(set2, ATK_TEXT_ATTR_STYLE, "italic"));
1388 AtkAttributeSet* set3 = atk_text_get_run_attributes(childText, 17, &startOffset, &endOffset);
1389 g_assert_cmpint(startOffset, ==, 17);
1390 g_assert_cmpint(endOffset, ==, 40);
1391 g_assert(atkAttributeSetAttributeHasValue(set3, ATK_TEXT_ATTR_WEIGHT, "700"));
1393 AtkAttributeSet* set4 = atk_text_get_default_attributes(childText);
1394 g_assert(atkAttributeSetAttributeHasValue(set4, ATK_TEXT_ATTR_STYLE, "normal"));
1395 g_assert(atkAttributeSetAttributeHasValue(set4, ATK_TEXT_ATTR_JUSTIFICATION, "right"));
1396 g_assert(atkAttributeSetAttributeHasValue(set4, ATK_TEXT_ATTR_SIZE, "14"));
1397 atk_attribute_set_free(set1);
1398 atk_attribute_set_free(set2);
1399 atk_attribute_set_free(set3);
1400 atk_attribute_set_free(set4);
1402 g_object_unref(child);
1403 child = atk_object_ref_accessible_child(object, 1);
1404 g_assert(child && ATK_IS_TEXT(child));
1405 childText = ATK_TEXT(child);
1407 set1 = atk_text_get_default_attributes(childText);
1408 g_assert(atkAttributeSetAttributeHasValue(set1, ATK_TEXT_ATTR_FAMILY_NAME, "monospace"));
1409 g_assert(atkAttributeSetAttributeHasValue(set1, ATK_TEXT_ATTR_STYLE, "normal"));
1410 g_assert(atkAttributeSetAttributeHasValue(set1, ATK_TEXT_ATTR_STRIKETHROUGH, "false"));
1411 g_assert(atkAttributeSetAttributeHasValue(set1, ATK_TEXT_ATTR_WEIGHT, "400"));
1412 g_assert(atkAttributeSetAttributeHasValue(set1, ATK_TEXT_ATTR_FG_COLOR, "120,121,122"));
1414 set2 = atk_text_get_run_attributes(childText, 43, &startOffset, &endOffset);
1415 g_assert_cmpint(startOffset, ==, 43);
1416 g_assert_cmpint(endOffset, ==, 80);
1417 /* Checks that default attributes of text are not returned when called to atk_text_get_run_attributes. */
1418 g_assert(!atkAttributeSetAttributeHasValue(set2, ATK_TEXT_ATTR_FG_COLOR, "120,121,122"));
1419 g_assert(atkAttributeSetAttributeHasValue(set2, ATK_TEXT_ATTR_UNDERLINE, "single"));
1420 g_assert(atkAttributeSetAttributeHasValue(set2, ATK_TEXT_ATTR_BG_COLOR, "80,81,82"));
1421 atk_attribute_set_free(set1);
1422 atk_attribute_set_free(set2);
1423 g_object_unref(child);
1425 child = atk_object_ref_accessible_child(object, 2);
1426 g_assert(child && ATK_IS_TEXT(child));
1427 childText = ATK_TEXT(child);
1429 set1 = atk_text_get_run_attributes(childText, 0, &startOffset, &endOffset);
1430 set2 = atk_text_get_run_attributes(childText, 3, &startOffset, &endOffset);
1431 g_assert(atkAttributeSetAreEqual(set1, set2));
1432 atk_attribute_set_free(set2);
1434 set2 = atk_text_get_run_attributes(childText, 1, &startOffset, &endOffset);
1435 set3 = atk_text_get_run_attributes(childText, 5, &startOffset, &endOffset);
1436 g_assert(atkAttributeSetAreEqual(set2, set3));
1437 g_assert(!atkAttributeSetAreEqual(set1, set2));
1438 atk_attribute_set_free(set3);
1440 set3 = atk_text_get_run_attributes(childText, 2, &startOffset, &endOffset);
1441 set4 = atk_text_get_run_attributes(childText, 6, &startOffset, &endOffset);
1442 g_assert(atkAttributeSetAreEqual(set3, set4));
1443 g_assert(!atkAttributeSetAreEqual(set1, set3));
1444 g_assert(!atkAttributeSetAreEqual(set2, set3));
1445 atk_attribute_set_free(set1);
1446 atk_attribute_set_free(set2);
1447 atk_attribute_set_free(set3);
1448 atk_attribute_set_free(set4);
1449 g_object_unref(child);
1451 child = atk_object_ref_accessible_child(object, 3);
1452 g_assert(child && ATK_IS_TEXT(child));
1453 childText = ATK_TEXT(child);
1454 set1 = atk_text_get_run_attributes(childText, 24, &startOffset, &endOffset);
1455 g_assert_cmpint(startOffset, ==, 21);
1456 g_assert_cmpint(endOffset, ==, 25);
1457 g_assert(atkAttributeSetAttributeHasValue(set1, ATK_TEXT_ATTR_STRIKETHROUGH, "true"));
1459 set2 = atk_text_get_run_attributes(childText, 25, &startOffset, &endOffset);
1460 g_assert_cmpint(startOffset, ==, 25);
1461 g_assert_cmpint(endOffset, ==, 30);
1462 g_assert(atkAttributeSetAreEqual(set2, 0));
1464 set3 = atk_text_get_default_attributes(childText);
1465 g_assert(atkAttributeSetAttributeHasValue(set3, ATK_TEXT_ATTR_JUSTIFICATION, "center"));
1466 atk_attribute_set_free(set1);
1467 atk_attribute_set_free(set2);
1468 atk_attribute_set_free(set3);
1470 g_object_unref(child);
1473 static gchar* textSelectionChangedResult = 0;
1475 static void textSelectionChangedCallback(AtkText* text, gpointer data)
1477 g_assert(ATK_IS_TEXT(text));
1479 g_free(textSelectionChangedResult);
1480 AtkRole role = atk_object_get_role(ATK_OBJECT(text));
1481 int startOffset = 0;
1483 atk_text_get_selection(ATK_TEXT(text), 0, &startOffset, &endOffset);
1484 textSelectionChangedResult = g_strdup_printf("|%s|%d|%d|", atk_role_get_name(role), startOffset, endOffset);
1487 static void testWebkitAtkTextSelections()
1489 WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
1490 g_object_ref_sink(webView);
1491 GtkAllocation allocation = { 0, 0, 800, 600 };
1492 gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
1493 webkit_web_view_load_string(webView, textForSelections, 0, 0, 0);
1495 AtkObject* object = getWebAreaObject(webView);
1498 AtkText* paragraph1 = ATK_TEXT(atk_object_ref_accessible_child(object, 0));
1499 g_assert(ATK_IS_TEXT(paragraph1));
1500 g_signal_connect(paragraph1, "text-selection-changed", G_CALLBACK(textSelectionChangedCallback), 0);
1502 AtkText* paragraph2 = ATK_TEXT(atk_object_ref_accessible_child(object, 1));
1503 g_assert(ATK_IS_TEXT(paragraph2));
1504 g_signal_connect(paragraph2, "text-selection-changed", G_CALLBACK(textSelectionChangedCallback), 0);
1506 AtkText* link = ATK_TEXT(atk_object_ref_accessible_child(ATK_OBJECT(paragraph2), 0));
1507 g_assert(ATK_IS_TEXT(link));
1509 AtkObject* list = atk_object_ref_accessible_child(object, 2);
1510 g_assert(ATK_OBJECT(list));
1512 AtkText* listItem = ATK_TEXT(atk_object_ref_accessible_child(list, 0));
1513 g_assert(ATK_IS_TEXT(listItem));
1515 /* First paragraph (simple text). */
1517 /* Basic initial checks. */
1518 g_assert_cmpint(atk_text_get_n_selections(paragraph1), ==, 0);
1522 gchar* selectedText = atk_text_get_selection(paragraph1, 0, &startOffset, &endOffset);
1523 g_assert_cmpint(startOffset, ==, 0);
1524 g_assert_cmpint(endOffset, ==, 0);
1525 g_assert_cmpstr(selectedText, ==, 0);
1526 g_free (selectedText);
1528 /* Try removing a non existing (yet) selection. */
1529 gboolean result = atk_text_remove_selection(paragraph1, 0);
1532 /* Try setting a 0-char selection. */
1533 result = atk_text_set_selection(paragraph1, 0, 5, 5);
1536 /* Make a selection and test it. */
1537 result = atk_text_set_selection(paragraph1, 0, 5, 25);
1539 g_assert_cmpint(atk_text_get_n_selections(paragraph1), ==, 1);
1540 g_assert_cmpstr(textSelectionChangedResult, ==, "|paragraph|5|25|");
1541 selectedText = atk_text_get_selection(paragraph1, 0, &startOffset, &endOffset);
1542 g_assert_cmpint(startOffset, ==, 5);
1543 g_assert_cmpint(endOffset, ==, 25);
1544 g_assert_cmpstr(selectedText, ==, "agraph with plain te");
1545 g_free (selectedText);
1547 /* Try removing the selection from other AtkText object (should fail). */
1548 result = atk_text_remove_selection(paragraph2, 0);
1551 /* Remove the selection and test everything again. */
1552 result = atk_text_remove_selection(paragraph1, 0);
1554 g_assert_cmpint(atk_text_get_n_selections(paragraph1), ==, 0);
1555 selectedText = atk_text_get_selection(paragraph1, 0, &startOffset, &endOffset);
1556 /* Now offsets should be the same, set to the last position of the caret. */
1557 g_assert_cmpint(startOffset, ==, endOffset);
1558 g_assert_cmpint(startOffset, ==, 25);
1559 g_assert_cmpint(endOffset, ==, 25);
1560 g_assert_cmpstr(selectedText, ==, 0);
1561 g_free (selectedText);
1563 /* Second paragraph (text + link + text). */
1565 /* Set a selection partially covering the link and test it. */
1566 result = atk_text_set_selection(paragraph2, 0, 7, 21);
1569 /* Test the paragraph first. */
1570 g_assert_cmpint(atk_text_get_n_selections(paragraph2), ==, 1);
1571 selectedText = atk_text_get_selection(paragraph2, 0, &startOffset, &endOffset);
1572 g_assert_cmpint(startOffset, ==, 7);
1573 g_assert_cmpint(endOffset, ==, 21);
1574 g_assert_cmpstr(selectedText, ==, "raph with a li");
1575 g_free (selectedText);
1577 /* Now test just the link. */
1578 g_assert_cmpint(atk_text_get_n_selections(link), ==, 1);
1579 selectedText = atk_text_get_selection(link, 0, &startOffset, &endOffset);
1580 g_assert_cmpint(startOffset, ==, 0);
1581 g_assert_cmpint(endOffset, ==, 4);
1582 g_assert_cmpstr(selectedText, ==, "a li");
1583 g_free (selectedText);
1585 /* Make a selection after the link and check selection for the whole paragraph. */
1586 result = atk_text_set_selection(paragraph2, 0, 27, 37);
1588 g_assert_cmpint(atk_text_get_n_selections(paragraph2), ==, 1);
1589 g_assert_cmpstr(textSelectionChangedResult, ==, "|paragraph|27|37|");
1590 selectedText = atk_text_get_selection(paragraph2, 0, &startOffset, &endOffset);
1591 g_assert_cmpint(startOffset, ==, 27);
1592 g_assert_cmpint(endOffset, ==, 37);
1593 g_assert_cmpstr(selectedText, ==, "the middle");
1594 g_free (selectedText);
1596 /* Remove selections and text everything again. */
1597 result = atk_text_remove_selection(paragraph2, 0);
1599 g_assert_cmpint(atk_text_get_n_selections(paragraph2), ==, 0);
1600 selectedText = atk_text_get_selection(paragraph2, 0, &startOffset, &endOffset);
1601 /* Now offsets should be the same (no selection). */
1602 g_assert_cmpint(startOffset, ==, endOffset);
1603 g_assert_cmpstr(selectedText, ==, 0);
1604 g_free (selectedText);
1606 g_assert_cmpint(atk_text_get_n_selections(link), ==, 0);
1607 selectedText = atk_text_get_selection(link, 0, &startOffset, &endOffset);
1608 /* Now offsets should be the same (no selection). */
1609 g_assert_cmpint(startOffset, ==, endOffset);
1610 g_assert_cmpstr(selectedText, ==, 0);
1611 g_free (selectedText);
1615 g_assert(atk_object_get_role(list) == ATK_ROLE_LIST);
1616 g_assert_cmpint(atk_object_get_n_accessible_children(list), ==, 1);
1618 gchar* text = atk_text_get_text(listItem, 0, -1);
1619 g_assert_cmpstr(text, ==, "1. A list item");
1622 /* It's not possible to select text inside an item's marker. */
1623 result = atk_text_set_selection (listItem, 0, 0, 9);
1625 result = atk_text_set_selection (listItem, 0, 9, 1);
1628 /* It should be possible to select text inside an item's text. */
1629 result = atk_text_set_selection (listItem, 0, 3, 9);
1632 g_assert_cmpint(atk_text_get_n_selections(listItem), ==, 1);
1633 selectedText = atk_text_get_selection(listItem, 0, &startOffset, &endOffset);
1634 g_assert_cmpint(startOffset, ==, 3);
1635 g_assert_cmpint(endOffset, ==, 9);
1636 g_assert_cmpstr(selectedText, ==, "A list");
1637 g_free (selectedText);
1639 g_free(textSelectionChangedResult);
1641 g_object_unref(paragraph1);
1642 g_object_unref(paragraph2);
1643 g_object_unref(link);
1644 g_object_unref(list);
1645 g_object_unref(listItem);
1646 g_object_unref(webView);
1649 static void testWebkitAtkGetExtents()
1651 WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
1652 g_object_ref_sink(webView);
1653 GtkAllocation allocation = { 0, 0, 800, 600 };
1654 gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
1655 webkit_web_view_load_string(webView, centeredContents, 0, 0, 0);
1657 AtkObject* object = getWebAreaObject(webView);
1660 AtkText* shortText1 = ATK_TEXT(atk_object_ref_accessible_child(object, 0));
1661 g_assert(ATK_IS_TEXT(shortText1));
1662 AtkText* longText = ATK_TEXT(atk_object_ref_accessible_child(object, 1));
1663 g_assert(ATK_IS_TEXT(longText));
1664 AtkText* shortText2 = ATK_TEXT(atk_object_ref_accessible_child(object, 2));
1665 g_assert(ATK_IS_TEXT(shortText2));
1666 AtkText* multilineText = ATK_TEXT(atk_object_ref_accessible_child(object, 3));
1667 g_assert(ATK_IS_TEXT(multilineText));
1669 /* Start with window extents. */
1670 AtkTextRectangle sline_window1, sline_window2, lline_window, mline_window;
1671 atk_text_get_range_extents(shortText1, 0, 10, ATK_XY_WINDOW, &sline_window1);
1672 atk_text_get_range_extents(longText, 0, 44, ATK_XY_WINDOW, &lline_window);
1673 atk_text_get_range_extents(shortText2, 0, 10, ATK_XY_WINDOW, &sline_window2);
1674 atk_text_get_range_extents(multilineText, 0, 60, ATK_XY_WINDOW, &mline_window);
1676 /* Check vertical line position. */
1677 g_assert_cmpint(sline_window1.y + sline_window1.height, <=, lline_window.y);
1678 g_assert_cmpint(lline_window.y + lline_window.height + sline_window2.height, <=, mline_window.y);
1680 /* Paragraphs 1 and 3 have identical text and alignment. */
1681 g_assert_cmpint(sline_window1.x, ==, sline_window2.x);
1682 g_assert_cmpint(sline_window1.width, ==, sline_window2.width);
1683 g_assert_cmpint(sline_window1.height, ==, sline_window2.height);
1685 /* All lines should be the same height; line 2 is the widest line. */
1686 g_assert_cmpint(sline_window1.height, ==, lline_window.height);
1687 g_assert_cmpint(sline_window1.width, <, lline_window.width);
1689 /* Make sure the character extents jive with the range extents. */
1695 /* First paragraph (short text). */
1696 atk_text_get_character_extents(shortText1, 0, &x, &y, &width, &height, ATK_XY_WINDOW);
1697 g_assert_cmpint(x, ==, sline_window1.x);
1698 g_assert_cmpint(y, ==, sline_window1.y);
1699 g_assert_cmpint(height, ==, sline_window1.height);
1701 atk_text_get_character_extents(shortText1, 9, &x, &y, &width, &height, ATK_XY_WINDOW);
1702 g_assert_cmpint(x, ==, sline_window1.x + sline_window1.width - width);
1703 g_assert_cmpint(y, ==, sline_window1.y);
1704 g_assert_cmpint(height, ==, sline_window1.height);
1706 /* Second paragraph (long text). */
1707 atk_text_get_character_extents(longText, 0, &x, &y, &width, &height, ATK_XY_WINDOW);
1708 g_assert_cmpint(x, ==, lline_window.x);
1709 g_assert_cmpint(y, ==, lline_window.y);
1710 g_assert_cmpint(height, ==, lline_window.height);
1712 atk_text_get_character_extents(longText, 43, &x, &y, &width, &height, ATK_XY_WINDOW);
1713 g_assert_cmpint(x, ==, lline_window.x + lline_window.width - width);
1714 g_assert_cmpint(y, ==, lline_window.y);
1715 g_assert_cmpint(height, ==, lline_window.height);
1717 /* Third paragraph (short text). */
1718 atk_text_get_character_extents(shortText2, 0, &x, &y, &width, &height, ATK_XY_WINDOW);
1719 g_assert_cmpint(x, ==, sline_window2.x);
1720 g_assert_cmpint(y, ==, sline_window2.y);
1721 g_assert_cmpint(height, ==, sline_window2.height);
1723 atk_text_get_character_extents(shortText2, 9, &x, &y, &width, &height, ATK_XY_WINDOW);
1724 g_assert_cmpint(x, ==, sline_window2.x + sline_window2.width - width);
1725 g_assert_cmpint(y, ==, sline_window2.y);
1726 g_assert_cmpint(height, ==, sline_window2.height);
1728 /* Four paragraph (3 lines multi-line text). */
1729 atk_text_get_character_extents(multilineText, 0, &x, &y, &width, &height, ATK_XY_WINDOW);
1730 g_assert_cmpint(x, ==, mline_window.x);
1731 g_assert_cmpint(y, ==, mline_window.y);
1732 g_assert_cmpint(3 * height, ==, mline_window.height);
1734 atk_text_get_character_extents(multilineText, 59, &x, &y, &width, &height, ATK_XY_WINDOW);
1735 /* Last line won't fill the whole width of the rectangle. */
1736 g_assert_cmpint(x, <=, mline_window.x + mline_window.width - width);
1737 g_assert_cmpint(y, ==, mline_window.y + mline_window.height - height);
1738 g_assert_cmpint(height, <=, mline_window.height);
1740 /* Check that extent for a full line are the same height than for
1741 a partial section of the same line */
1744 gchar* text = atk_text_get_text_at_offset(multilineText, 0, ATK_TEXT_BOUNDARY_LINE_START, &startOffset, &endOffset);
1747 AtkTextRectangle fline_window;
1748 AtkTextRectangle afline_window;
1749 atk_text_get_range_extents(multilineText, startOffset, endOffset, ATK_XY_WINDOW, &fline_window);
1750 atk_text_get_range_extents(multilineText, startOffset, endOffset - 1, ATK_XY_WINDOW, &afline_window);
1751 g_assert_cmpint(fline_window.x, ==, afline_window.x);
1752 g_assert_cmpint(fline_window.y, ==, afline_window.y);
1753 g_assert_cmpint(fline_window.height, ==, afline_window.height);
1755 g_object_unref(shortText1);
1756 g_object_unref(shortText2);
1757 g_object_unref(longText);
1758 g_object_unref(multilineText);
1759 g_object_unref(webView);
1762 static void testWebkitAtkLayoutAndDataTables()
1764 WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
1765 g_object_ref_sink(webView);
1766 GtkAllocation allocation = { 0, 0, 800, 600 };
1767 gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
1768 webkit_web_view_load_string(webView, layoutAndDataTables, 0, 0, 0);
1770 AtkObject* object = getWebAreaObject(webView);
1773 /* Check the non-layout table (data table). */
1775 AtkObject* table1 = atk_object_ref_accessible_child(object, 0);
1776 g_assert(ATK_IS_TABLE(table1));
1777 AtkAttributeSet* set1 = atk_object_get_attributes(table1);
1779 g_assert(!atkAttributeSetContainsAttributeName(set1, "layout-guess"));
1780 atk_attribute_set_free(set1);
1782 /* Check the layout table. */
1784 AtkObject* table2 = atk_object_ref_accessible_child(object, 1);
1785 g_assert(ATK_IS_TABLE(table2));
1786 AtkAttributeSet* set2 = atk_object_get_attributes(table2);
1788 g_assert(atkAttributeSetContainsAttributeName(set2, "layout-guess"));
1789 g_assert(atkAttributeSetAttributeNameHasValue(set2, "layout-guess", "true"));
1790 atk_attribute_set_free(set2);
1792 g_object_unref(table1);
1793 g_object_unref(table2);
1794 g_object_unref(webView);
1797 static void testWebkitAtkLinksWithInlineImages()
1799 WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
1800 g_object_ref_sink(webView);
1801 GtkAllocation allocation = { 0, 0, 800, 600 };
1802 gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
1803 webkit_web_view_load_string(webView, linksWithInlineImages, 0, 0, 0);
1805 AtkObject* object = getWebAreaObject(webView);
1808 /* First paragraph (link at the beginning). */
1809 AtkObject* paragraph = atk_object_ref_accessible_child(object, 0);
1810 g_assert(ATK_IS_TEXT(paragraph));
1813 gchar* text = atk_text_get_text_at_offset(ATK_TEXT(paragraph), 0, ATK_TEXT_BOUNDARY_LINE_START, &startOffset, &endOffset);
1815 g_assert_cmpstr(text, ==, "foo bar baz");
1816 g_assert_cmpint(startOffset, ==, 0);
1817 g_assert_cmpint(endOffset, ==, 11);
1819 g_object_unref(paragraph);
1821 /* Second paragraph (link in the middle). */
1822 paragraph = atk_object_ref_accessible_child(object, 1);
1823 g_assert(ATK_IS_TEXT(paragraph));
1824 text = atk_text_get_text_at_offset(ATK_TEXT(paragraph), 0, ATK_TEXT_BOUNDARY_LINE_START, &startOffset, &endOffset);
1826 g_assert_cmpstr(text, ==, "foo bar baz");
1827 g_assert_cmpint(startOffset, ==, 0);
1828 g_assert_cmpint(endOffset, ==, 11);
1830 g_object_unref(paragraph);
1832 /* Third paragraph (link at the end). */
1833 paragraph = atk_object_ref_accessible_child(object, 2);
1834 g_assert(ATK_IS_TEXT(paragraph));
1835 text = atk_text_get_text_at_offset(ATK_TEXT(paragraph), 0, ATK_TEXT_BOUNDARY_LINE_START, &startOffset, &endOffset);
1837 g_assert_cmpstr(text, ==, "foo bar baz");
1838 g_assert_cmpint(startOffset, ==, 0);
1839 g_assert_cmpint(endOffset, ==, 11);
1841 g_object_unref(paragraph);
1843 g_object_unref(webView);
1846 static void testWebkitAtkHypertextAndHyperlinks()
1848 WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
1849 g_object_ref_sink(webView);
1850 GtkAllocation allocation = { 0, 0, 800, 600 };
1851 gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
1852 webkit_web_view_load_string(webView, hypertextAndHyperlinks, 0, 0, 0);
1854 AtkObject* object = getWebAreaObject(webView);
1857 AtkObject* paragraph1 = atk_object_ref_accessible_child(object, 0);
1858 g_assert(ATK_OBJECT(paragraph1));
1859 g_assert(atk_object_get_role(paragraph1) == ATK_ROLE_PARAGRAPH);
1860 g_assert(ATK_IS_HYPERTEXT(paragraph1));
1862 /* No links in the first paragraph. */
1863 gint nLinks = atk_hypertext_get_n_links(ATK_HYPERTEXT(paragraph1));
1864 g_assert_cmpint(nLinks, ==, 0);
1866 AtkObject* paragraph2 = atk_object_ref_accessible_child(object, 1);
1867 g_assert(ATK_OBJECT(paragraph2));
1868 g_assert(atk_object_get_role(paragraph2) == ATK_ROLE_PARAGRAPH);
1869 g_assert(ATK_IS_HYPERTEXT(paragraph2));
1871 /* Check links in the second paragraph.
1872 nLinks = atk_hypertext_get_n_links(ATK_HYPERTEXT(paragraph2));
1873 g_assert_cmpint(nLinks, ==, 3); */
1875 AtkHyperlink* hLink1 = atk_hypertext_get_link(ATK_HYPERTEXT(paragraph2), 0);
1876 g_assert(ATK_HYPERLINK(hLink1));
1877 AtkObject* hLinkObject1 = atk_hyperlink_get_object(hLink1, 0);
1878 g_assert(ATK_OBJECT(hLinkObject1));
1879 g_assert(atk_object_get_role(hLinkObject1) == ATK_ROLE_LINK);
1880 g_assert_cmpint(atk_hyperlink_get_start_index(hLink1), ==, 0);
1881 g_assert_cmpint(atk_hyperlink_get_end_index(hLink1), ==, 6);
1882 g_assert_cmpint(atk_hyperlink_get_n_anchors(hLink1), ==, 1);
1883 g_assert_cmpstr(atk_hyperlink_get_uri(hLink1, 0), ==, "http://foo.bar.baz/");
1885 AtkHyperlink* hLink2 = atk_hypertext_get_link(ATK_HYPERTEXT(paragraph2), 1);
1886 g_assert(ATK_HYPERLINK(hLink2));
1887 AtkObject* hLinkObject2 = atk_hyperlink_get_object(hLink2, 0);
1888 g_assert(ATK_OBJECT(hLinkObject2));
1889 g_assert(atk_object_get_role(hLinkObject2) == ATK_ROLE_LINK);
1890 g_assert_cmpint(atk_hyperlink_get_start_index(hLink2), ==, 12);
1891 g_assert_cmpint(atk_hyperlink_get_end_index(hLink2), ==, 32);
1892 g_assert_cmpint(atk_hyperlink_get_n_anchors(hLink2), ==, 1);
1893 g_assert_cmpstr(atk_hyperlink_get_uri(hLink2, 0), ==, "http://bar.baz.foo/");
1895 AtkHyperlink* hLink3 = atk_hypertext_get_link(ATK_HYPERTEXT(paragraph2), 2);
1896 g_assert(ATK_HYPERLINK(hLink3));
1897 AtkObject* hLinkObject3 = atk_hyperlink_get_object(hLink3, 0);
1898 g_assert(ATK_OBJECT(hLinkObject3));
1899 g_assert(atk_object_get_role(hLinkObject3) == ATK_ROLE_LINK);
1900 g_assert_cmpint(atk_hyperlink_get_start_index(hLink3), ==, 65);
1901 g_assert_cmpint(atk_hyperlink_get_end_index(hLink3), ==, 75);
1902 g_assert_cmpint(atk_hyperlink_get_n_anchors(hLink3), ==, 1);
1903 g_assert_cmpstr(atk_hyperlink_get_uri(hLink3, 0), ==, "http://baz.foo.bar/");
1905 AtkObject* list = atk_object_ref_accessible_child(object, 2);
1906 g_assert(ATK_OBJECT(list));
1907 g_assert(atk_object_get_role(list) == ATK_ROLE_LIST);
1908 g_assert_cmpint(atk_object_get_n_accessible_children(list), ==, 1);
1910 AtkObject* listItem = atk_object_ref_accessible_child(list, 0);
1911 g_assert(ATK_IS_TEXT(listItem));
1912 g_assert(ATK_IS_HYPERTEXT(listItem));
1914 AtkHyperlink* hLinkInListItem = atk_hypertext_get_link(ATK_HYPERTEXT(listItem), 0);
1915 g_assert(ATK_HYPERLINK(hLinkInListItem));
1916 AtkObject* hLinkObject = atk_hyperlink_get_object(hLinkInListItem, 0);
1917 g_assert(ATK_OBJECT(hLinkObject));
1918 g_assert(atk_object_get_role(hLinkObject) == ATK_ROLE_LINK);
1919 g_assert_cmpint(atk_hyperlink_get_start_index(hLinkInListItem), ==, 20);
1920 g_assert_cmpint(atk_hyperlink_get_end_index(hLinkInListItem), ==, 43);
1921 g_assert_cmpint(atk_hyperlink_get_n_anchors(hLinkInListItem), ==, 1);
1922 g_assert_cmpstr(atk_hyperlink_get_uri(hLinkInListItem, 0), ==, "http://foo.bar.baz/");
1924 /* Finally check the AtkAction interface for a given AtkHyperlink. */
1925 g_assert(ATK_IS_ACTION(hLink1));
1926 g_assert_cmpint(atk_action_get_n_actions(ATK_ACTION(hLink1)), ==, 1);
1927 g_assert_cmpstr(atk_action_get_keybinding(ATK_ACTION(hLink1), 0), ==, "");
1928 g_assert_cmpstr(atk_action_get_name(ATK_ACTION(hLink1), 0), ==, "jump");
1929 g_assert(atk_action_do_action(ATK_ACTION(hLink1), 0));
1931 g_object_unref(paragraph1);
1932 g_object_unref(paragraph2);
1933 g_object_unref(list);
1934 g_object_unref(listItem);
1935 g_object_unref(webView);
1938 static void testWebkitAtkListsOfItems()
1940 WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
1941 g_object_ref_sink(webView);
1942 GtkAllocation allocation = { 0, 0, 800, 600 };
1943 gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
1944 webkit_web_view_load_string(webView, listsOfItems, 0, 0, 0);
1946 AtkObject* object = getWebAreaObject(webView);
1949 /* Unordered list. */
1951 AtkObject* uList = atk_object_ref_accessible_child(object, 0);
1952 g_assert(ATK_OBJECT(uList));
1953 g_assert(atk_object_get_role(uList) == ATK_ROLE_LIST);
1954 g_assert_cmpint(atk_object_get_n_accessible_children(uList), ==, 3);
1956 AtkObject* item1 = atk_object_ref_accessible_child(uList, 0);
1957 g_assert(ATK_IS_TEXT(item1));
1958 AtkObject* item2 = atk_object_ref_accessible_child(uList, 1);
1959 g_assert(ATK_IS_TEXT(item2));
1960 AtkObject* item3 = atk_object_ref_accessible_child(uList, 2);
1961 g_assert(ATK_IS_TEXT(item3));
1963 g_assert_cmpint(atk_object_get_n_accessible_children(item1), ==, 0);
1964 g_assert_cmpint(atk_object_get_n_accessible_children(item2), ==, 1);
1965 g_assert_cmpint(atk_object_get_n_accessible_children(item3), ==, 1);
1967 char* text = atk_text_get_text(ATK_TEXT(item1), 0, -1);
1968 g_assert_cmpstr(text, ==, "\342\200\242 text only");
1970 text = atk_text_get_text(ATK_TEXT(item2), 0, -1);
1971 g_assert_cmpstr(text, ==, "\342\200\242 link only");
1973 text = atk_text_get_text(ATK_TEXT(item3), 0, -1);
1974 g_assert_cmpstr(text, ==, "\342\200\242 text and a link");
1977 g_object_unref(item1);
1978 g_object_unref(item2);
1979 g_object_unref(item3);
1983 AtkObject* oList = atk_object_ref_accessible_child(object, 1);
1984 g_assert(ATK_OBJECT(oList));
1985 g_assert(atk_object_get_role(oList) == ATK_ROLE_LIST);
1986 g_assert_cmpint(atk_object_get_n_accessible_children(oList), ==, 3);
1988 item1 = atk_object_ref_accessible_child(oList, 0);
1989 g_assert(ATK_IS_TEXT(item1));
1990 item2 = atk_object_ref_accessible_child(oList, 1);
1991 g_assert(ATK_IS_TEXT(item2));
1992 item3 = atk_object_ref_accessible_child(oList, 2);
1993 g_assert(ATK_IS_TEXT(item3));
1995 text = atk_text_get_text(ATK_TEXT(item1), 0, -1);
1996 g_assert_cmpstr(text, ==, "1. text only");
1998 text = atk_text_get_text(ATK_TEXT(item2), 0, -1);
1999 g_assert_cmpstr(text, ==, "2. link only");
2001 text = atk_text_get_text(ATK_TEXT(item3), 0, -1);
2002 g_assert_cmpstr(text, ==, "3. text and a link");
2005 g_assert_cmpint(atk_object_get_n_accessible_children(item1), ==, 0);
2006 g_assert_cmpint(atk_object_get_n_accessible_children(item2), ==, 1);
2007 g_assert_cmpint(atk_object_get_n_accessible_children(item3), ==, 1);
2009 g_object_unref(item1);
2010 g_object_unref(item2);
2011 g_object_unref(item3);
2013 g_object_unref(uList);
2014 g_object_unref(oList);
2015 g_object_unref(webView);
2019 TEXT_CHANGE_INSERT = 1,
2020 TEXT_CHANGE_REMOVE = 2
2023 static gchar* textChangedResult = 0;
2025 static void textChangedCb(AtkText* text, gint pos, gint len, gchar* modifiedText, gpointer data)
2027 g_assert(text && ATK_IS_OBJECT(text));
2029 TextChangeType type = GPOINTER_TO_INT(data);
2030 g_free(textChangedResult);
2031 textChangedResult = g_strdup_printf("|%d|%d|%d|'%s'|", type, pos, len, modifiedText);
2034 static void testWebkitAtkTextChangedNotifications()
2036 WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
2037 g_object_ref_sink(webView);
2038 GtkAllocation allocation = { 0, 0, 800, 600 };
2039 gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
2040 webkit_web_view_load_string(webView, formWithTextInputs, 0, 0, 0);
2042 AtkObject* object = getWebAreaObject(webView);
2045 AtkObject* form = atk_object_ref_accessible_child(object, 0);
2046 g_assert(ATK_IS_OBJECT(form));
2048 /* First check normal text entries. */
2049 AtkObject* textEntry = atk_object_ref_accessible_child(form, 0);
2050 g_assert(ATK_IS_EDITABLE_TEXT(textEntry));
2051 g_assert(atk_object_get_role(ATK_OBJECT(textEntry)) == ATK_ROLE_ENTRY);
2053 g_signal_connect(textEntry, "text-insert",
2054 G_CALLBACK(textChangedCb),
2055 GINT_TO_POINTER(TEXT_CHANGE_INSERT));
2056 g_signal_connect(textEntry, "text-remove",
2057 G_CALLBACK(textChangedCb),
2058 GINT_TO_POINTER(TEXT_CHANGE_REMOVE));
2061 atk_editable_text_insert_text(ATK_EDITABLE_TEXT(textEntry), "foo bar baz", 11, &pos);
2062 char* text = atk_text_get_text(ATK_TEXT(textEntry), 0, -1);
2063 g_assert_cmpstr(text, ==, "foo bar baz");
2064 g_assert_cmpstr(textChangedResult, ==, "|1|0|11|'foo bar baz'|");
2067 atk_editable_text_delete_text(ATK_EDITABLE_TEXT(textEntry), 4, 7);
2068 text = atk_text_get_text(ATK_TEXT(textEntry), 0, -1);
2069 g_assert_cmpstr(text, ==, "foo baz");
2070 g_assert_cmpstr(textChangedResult, ==, "|2|4|3|'bar'|");
2074 atk_editable_text_insert_text(ATK_EDITABLE_TEXT(textEntry), "qux quux tobeignored", 8, &pos);
2075 text = atk_text_get_text(ATK_TEXT(textEntry), 0, -1);
2076 g_assert_cmpstr(text, ==, "foo qux quux baz");
2077 g_assert_cmpstr(textChangedResult, ==, "|1|4|8|'qux quux'|");
2080 /* Now check for password entries. */
2081 AtkObject* passwordEntry = atk_object_ref_accessible_child(form, 1);
2082 g_assert(ATK_IS_EDITABLE_TEXT(passwordEntry));
2083 g_assert(atk_object_get_role(ATK_OBJECT(passwordEntry)) == ATK_ROLE_PASSWORD_TEXT);
2085 g_signal_connect(passwordEntry, "text-insert",
2086 G_CALLBACK(textChangedCb),
2087 GINT_TO_POINTER(TEXT_CHANGE_INSERT));
2088 g_signal_connect(passwordEntry, "text-remove",
2089 G_CALLBACK(textChangedCb),
2090 GINT_TO_POINTER(TEXT_CHANGE_REMOVE));
2093 /* A single bullet character is '\342\200\242' */
2094 atk_editable_text_insert_text(ATK_EDITABLE_TEXT(passwordEntry), "foobar", 6, &pos);
2095 g_assert_cmpstr(textChangedResult, ==, "|1|0|6|'\342\200\242\342\200\242\342\200\242\342\200\242\342\200\242\342\200\242'|");
2096 text = atk_text_get_text(ATK_TEXT(passwordEntry), 0, -1);
2097 g_assert_cmpstr(text, ==, "\342\200\242\342\200\242\342\200\242\342\200\242\342\200\242\342\200\242");
2100 atk_editable_text_delete_text(ATK_EDITABLE_TEXT(passwordEntry), 2, 4);
2101 g_assert_cmpstr(textChangedResult, ==, "|2|2|2|'\342\200\242\342\200\242'|");
2103 text = atk_text_get_text(ATK_TEXT(passwordEntry), 0, -1);
2104 g_assert_cmpstr(text, ==, "\342\200\242\342\200\242\342\200\242\342\200\242");
2108 atk_editable_text_insert_text(ATK_EDITABLE_TEXT(passwordEntry), "qux tobeignored", 3, &pos);
2109 g_assert_cmpstr(textChangedResult, ==, "|1|3|3|'\342\200\242\342\200\242\342\200\242'|");
2111 text = atk_text_get_text(ATK_TEXT(passwordEntry), 0, -1);
2112 g_assert_cmpstr(text, ==, "\342\200\242\342\200\242\342\200\242\342\200\242\342\200\242\342\200\242\342\200\242");
2115 g_free(textChangedResult);
2117 g_object_unref(form);
2118 g_object_unref(textEntry);
2119 g_object_unref(passwordEntry);
2120 g_object_unref(webView);
2123 static void testWebkitAtkParentForRootObject()
2125 WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
2126 GtkAllocation allocation = { 0, 0, 800, 600 };
2127 gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
2128 webkit_web_view_load_string(webView, contents, 0, 0, 0);
2130 /* We need a parent container widget for the webview. */
2131 GtkWidget* parentContainer = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2132 g_object_ref_sink(parentContainer);
2133 gtk_container_add(GTK_CONTAINER(parentContainer), GTK_WIDGET(webView));
2135 AtkObject* axParent = gtk_widget_get_accessible(parentContainer);
2136 g_assert(ATK_IS_OBJECT(axParent));
2138 AtkObject* axRoot = gtk_widget_get_accessible(GTK_WIDGET(webView));
2139 g_assert(ATK_IS_OBJECT(axRoot));
2141 /* The child for the parent container's accessibility object
2142 should be the accessibility object for the WebView's root. */
2143 AtkObject* axParentChild = atk_object_ref_accessible_child(axParent, 0);
2144 g_assert(axParentChild == axRoot);
2146 /* Bottom-up navigation should match top-down one. */
2147 g_assert(atk_object_get_parent(axParentChild) == axParent);
2149 g_object_unref(axParentChild);
2150 g_object_unref(parentContainer);
2153 static void testWebkitAtkSetParentForObject()
2155 WebKitWebView* webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
2156 GtkAllocation allocation = { 0, 0, 800, 600 };
2157 gtk_widget_size_allocate(GTK_WIDGET(webView), &allocation);
2158 webkit_web_view_load_string(webView, contents, 0, 0, 0);
2160 /* Put the webview in a parent container widget to check that the
2161 normal behaviour keeps working as expected by default. */
2162 GtkWidget* parentContainer = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2163 g_object_ref_sink(parentContainer);
2164 gtk_container_add(GTK_CONTAINER(parentContainer), GTK_WIDGET(webView));
2166 AtkObject* axRoot = gtk_widget_get_accessible(GTK_WIDGET(webView));
2167 g_assert(ATK_IS_OBJECT(axRoot));
2169 AtkObject* axParent = gtk_widget_get_accessible(parentContainer);
2170 g_assert(ATK_IS_OBJECT(axParent));
2172 /* The parent of the root object is the parent container's a11y object. */
2173 g_assert(atk_object_get_parent(axRoot) == axParent);
2175 /* We now need to use something as a an alternative parent for
2176 the a11y object associated with the root of the DOM tree. */
2177 GtkWidget* alternativeParent = gtk_button_new();
2178 g_object_ref_sink(alternativeParent);
2180 AtkObject* axAlternativeParent = gtk_widget_get_accessible(alternativeParent);
2181 g_assert(ATK_IS_OBJECT(axAlternativeParent));
2183 /* Manually set the alternative parent's accessibility object as
2184 the parent for the WebKit accessibility root object and check. */
2185 atk_object_set_parent(axRoot, axAlternativeParent);
2186 g_assert(atk_object_get_parent(axRoot) == axAlternativeParent);
2188 g_object_unref(alternativeParent);
2189 g_object_unref(parentContainer);
2192 #ifdef GTK_API_VERSION_2
2193 static void initializeTestingFramework(int argc, char** argv)
2195 /* Ensure GAIL is the only module loaded. */
2196 g_setenv("GTK_MODULES", "gail", TRUE);
2198 /* Following lines were taken from gtk_test_init(). */
2199 g_test_init(&argc, &argv, 0);
2200 gtk_disable_setlocale();
2201 setlocale(LC_ALL, "C");
2203 gtk_init(&argc, &argv);
2207 int main(int argc, char** argv)
2209 #ifdef GTK_API_VERSION_2
2210 /* We can't just call to gtk_test_init() in this case because its
2211 implementation makes sure that no GTK+ module will be loaded, and
2212 we will need to load GAIL for tests that need to use AtkObjects
2213 from non-WebKit GtkWidgets (e.g parentForRootObject).*/
2214 initializeTestingFramework(argc, argv);
2216 gtk_test_init(&argc, &argv, NULL);
2219 g_test_bug_base("https://bugs.webkit.org/");
2220 g_test_add_func("/webkit/atk/caretOffsets", testWebkitAtkCaretOffsets);
2221 g_test_add_func("/webkit/atk/caretOffsetsAndExtranousWhiteSpaces", testWebkitAtkCaretOffsetsAndExtranousWhiteSpaces);
2222 g_test_add_func("/webkit/atk/comboBox", testWebkitAtkComboBox);
2223 g_test_add_func("/webkit/atk/documentLoadingEvents", testWebkitAtkDocumentLoadingEvents);
2224 g_test_add_func("/webkit/atk/embeddedObjects", testWebkitAtkEmbeddedObjects);
2225 g_test_add_func("/webkit/atk/getTextAtOffset", testWebkitAtkGetTextAtOffset);
2226 g_test_add_func("/webkit/atk/getTextAtOffsetNewlines", testWebkitAtkGetTextAtOffsetNewlines);
2227 g_test_add_func("/webkit/atk/getTextAtOffsetTextarea", testWebkitAtkGetTextAtOffsetTextarea);
2228 g_test_add_func("/webkit/atk/getTextAtOffsetTextInput", testWebkitAtkGetTextAtOffsetTextInput);
2229 g_test_add_func("/webkit/atk/getTextAtOffsetWithPreformattedText", testWebkitAtkGetTextAtOffsetWithPreformattedText);
2230 g_test_add_func("/webkit/atk/getTextAtOffsetWithSpecialCharacters", testWebkitAtkGetTextAtOffsetWithSpecialCharacters);
2231 g_test_add_func("/webkit/atk/getTextAtOffsetWithWrappedLines", testWebkitAtkGetTextAtOffsetWithWrappedLines);
2232 g_test_add_func("/webkit/atk/getTextAtOffsetWithEmbeddedObjects", testWebkitAtkGetTextAtOffsetWithEmbeddedObjects);
2233 g_test_add_func("/webkit/atk/getTextInParagraphAndBodySimple", testWebkitAtkGetTextInParagraphAndBodySimple);
2234 g_test_add_func("/webkit/atk/getTextInParagraphAndBodyModerate", testWebkitAtkGetTextInParagraphAndBodyModerate);
2235 g_test_add_func("/webkit/atk/getTextInTable", testWebkitAtkGetTextInTable);
2236 g_test_add_func("/webkit/atk/getHeadersInTable", testWebkitAtkGetHeadersInTable);
2237 g_test_add_func("/webkit/atk/textAttributes", testWebkitAtkTextAttributes);
2238 g_test_add_func("/webkit/atk/textSelections", testWebkitAtkTextSelections);
2239 g_test_add_func("/webkit/atk/getExtents", testWebkitAtkGetExtents);
2240 g_test_add_func("/webkit/atk/hypertextAndHyperlinks", testWebkitAtkHypertextAndHyperlinks);
2241 g_test_add_func("/webkit/atk/layoutAndDataTables", testWebkitAtkLayoutAndDataTables);
2242 g_test_add_func("/webkit/atk/linksWithInlineImages", testWebkitAtkLinksWithInlineImages);
2243 g_test_add_func("/webkit/atk/listsOfItems", testWebkitAtkListsOfItems);
2244 g_test_add_func("/webkit/atk/textChangedNotifications", testWebkitAtkTextChangedNotifications);
2245 g_test_add_func("/webkit/atk/parentForRootObject", testWebkitAtkParentForRootObject);
2246 g_test_add_func("/webkit/atk/setParentForObject", testWebkitAtkSetParentForObject);
2247 return g_test_run ();