OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 2008 Nuanti Ltd. | |
3 * Copyright (C) 2009 Jan Alonzo | |
4 * Copyright (C) 2009, 2010, 2011, 2012 Igalia S.L. | |
5 * Copyright (C) 2013 Samsung Electronics | |
6 * | |
7 * Portions from Mozilla a11y, copyright as follows: | |
8 * | |
9 * The Original Code is mozilla.org code. | |
10 * | |
11 * The Initial Developer of the Original Code is | |
12 * Sun Microsystems, Inc. | |
13 * Portions created by the Initial Developer are Copyright (C) 2002 | |
14 * the Initial Developer. All Rights Reserved. | |
15 * | |
16 * This library is free software; you can redistribute it and/or | |
17 * modify it under the terms of the GNU Library General Public | |
18 * License as published by the Free Software Foundation; either | |
19 * version 2 of the License, or (at your option) any later version. | |
20 * | |
21 * This library is distributed in the hope that it will be useful, | |
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
24 * Library General Public License for more details. | |
25 * | |
26 * You should have received a copy of the GNU Library General Public License | |
27 * along with this library; see the file COPYING.LIB. If not, write to | |
28 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | |
29 * Boston, MA 02110-1301, USA. | |
30 */ | |
31 | |
32 #include "config.h" | |
33 #include "WebKitAccessibleWrapperAtk.h" | |
34 | |
35 #if HAVE(ACCESSIBILITY) | |
36 | |
37 #include "AXObjectCache.h" | |
38 #include "Document.h" | |
39 #include "Frame.h" | |
40 #include "FrameView.h" | |
41 #include "HTMLNames.h" | |
42 #include "HTMLTableElement.h" | |
43 #include "HostWindow.h" | |
44 #include "RenderObject.h" | |
45 #include "Settings.h" | |
46 #include "TextIterator.h" | |
47 #include "VisibleUnits.h" | |
48 #include "WebKitAccessibleHyperlink.h" | |
49 #include "WebKitAccessibleInterfaceAction.h" | |
50 #include "WebKitAccessibleInterfaceComponent.h" | |
51 #include "WebKitAccessibleInterfaceDocument.h" | |
52 #include "WebKitAccessibleInterfaceEditableText.h" | |
53 #include "WebKitAccessibleInterfaceHyperlinkImpl.h" | |
54 #include "WebKitAccessibleInterfaceHypertext.h" | |
55 #include "WebKitAccessibleInterfaceImage.h" | |
56 #include "WebKitAccessibleInterfaceSelection.h" | |
57 #include "WebKitAccessibleInterfaceTable.h" | |
58 #include "WebKitAccessibleInterfaceText.h" | |
59 #include "WebKitAccessibleInterfaceValue.h" | |
60 #include "WebKitAccessibleUtil.h" | |
61 #include "htmlediting.h" | |
62 #include <glib/gprintf.h> | |
63 | |
64 #if PLATFORM(GTK) | |
65 #include <gtk/gtk.h> | |
66 #endif | |
67 | |
68 using namespace WebCore; | |
69 | |
70 struct _WebKitAccessiblePrivate { | |
71 // Cached data for AtkObject. | |
72 CString accessibleName; | |
73 CString accessibleDescription; | |
74 | |
75 // Cached data for AtkAction. | |
76 CString actionName; | |
77 CString actionKeyBinding; | |
78 | |
79 // Cached data for AtkDocument. | |
80 CString documentLocale; | |
81 CString documentType; | |
82 CString documentEncoding; | |
83 CString documentURI; | |
84 | |
85 // Cached data for AtkImage. | |
86 CString imageDescription; | |
87 }; | |
88 | |
89 #define WEBKIT_ACCESSIBLE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), W
EBKIT_TYPE_ACCESSIBLE, WebKitAccessiblePrivate)) | |
90 | |
91 static AccessibilityObject* fallbackObject() | |
92 { | |
93 // FIXME: An AXObjectCache with a Document is meaningless. | |
94 static AXObjectCache* fallbackCache = new AXObjectCache(0); | |
95 static AccessibilityObject* object = 0; | |
96 if (!object) { | |
97 // FIXME: using fallbackCache->getOrCreate(ListBoxOptionRole) is a hack | |
98 object = fallbackCache->getOrCreate(ListBoxOptionRole); | |
99 object->ref(); | |
100 } | |
101 | |
102 return object; | |
103 } | |
104 | |
105 static AccessibilityObject* core(WebKitAccessible* accessible) | |
106 { | |
107 if (!accessible) | |
108 return 0; | |
109 | |
110 return accessible->m_object; | |
111 } | |
112 | |
113 static AccessibilityObject* core(AtkObject* object) | |
114 { | |
115 if (!WEBKIT_IS_ACCESSIBLE(object)) | |
116 return 0; | |
117 | |
118 return core(WEBKIT_ACCESSIBLE(object)); | |
119 } | |
120 | |
121 static const gchar* webkitAccessibleGetName(AtkObject* object) | |
122 { | |
123 AccessibilityObject* coreObject = core(object); | |
124 if (!coreObject->isAccessibilityRenderObject()) | |
125 return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName, coreOb
ject->stringValue()); | |
126 | |
127 if (coreObject->isFieldset()) { | |
128 AccessibilityObject* label = coreObject->titleUIElement(); | |
129 if (label) { | |
130 AtkObject* atkObject = label->wrapper(); | |
131 if (ATK_IS_TEXT(atkObject)) | |
132 return atk_text_get_text(ATK_TEXT(atkObject), 0, -1); | |
133 } | |
134 } | |
135 | |
136 if (coreObject->isControl()) { | |
137 AccessibilityObject* label = coreObject->correspondingLabelForControlEle
ment(); | |
138 if (label) { | |
139 AtkObject* atkObject = label->wrapper(); | |
140 if (ATK_IS_TEXT(atkObject)) | |
141 return atk_text_get_text(ATK_TEXT(atkObject), 0, -1); | |
142 } | |
143 | |
144 // Try text under the node. | |
145 String textUnder = coreObject->textUnderElement(); | |
146 if (textUnder.length()) | |
147 return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName, te
xtUnder); | |
148 } | |
149 | |
150 if (coreObject->isImage() || coreObject->isInputImage()) { | |
151 Node* node = coreObject->node(); | |
152 if (node && node->isHTMLElement()) { | |
153 // Get the attribute rather than altText String so as not to fall ba
ck on title. | |
154 String alt = toHTMLElement(node)->getAttribute(HTMLNames::altAttr); | |
155 if (!alt.isEmpty()) | |
156 return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName
, alt); | |
157 } | |
158 } | |
159 | |
160 // Fallback for the webArea object: just return the document's title. | |
161 if (coreObject->isWebArea()) { | |
162 Document* document = coreObject->document(); | |
163 if (document) | |
164 return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName, do
cument->title()); | |
165 } | |
166 | |
167 // Nothing worked so far, try with the AccessibilityObject's | |
168 // title() before going ahead with stringValue(). | |
169 String axTitle = accessibilityTitle(coreObject); | |
170 if (!axTitle.isEmpty()) | |
171 return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName, axTitl
e); | |
172 | |
173 return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName, coreObject
->stringValue()); | |
174 } | |
175 | |
176 static const gchar* webkitAccessibleGetDescription(AtkObject* object) | |
177 { | |
178 AccessibilityObject* coreObject = core(object); | |
179 Node* node = 0; | |
180 if (coreObject->isAccessibilityRenderObject()) | |
181 node = coreObject->node(); | |
182 if (!node || !node->isHTMLElement() || coreObject->ariaRoleAttribute() != Un
knownRole) | |
183 return cacheAndReturnAtkProperty(object, AtkCachedAccessibleDescription,
accessibilityDescription(coreObject)); | |
184 | |
185 // atk_table_get_summary returns an AtkObject. We have no summary object, so
expose summary here. | |
186 if (coreObject->roleValue() == TableRole) { | |
187 String summary = static_cast<HTMLTableElement*>(node)->summary(); | |
188 if (!summary.isEmpty()) | |
189 return cacheAndReturnAtkProperty(object, AtkCachedAccessibleDescript
ion, summary); | |
190 } | |
191 | |
192 // The title attribute should be reliably available as the object's descript
on. | |
193 // We do not want to fall back on other attributes in its absence. See bug 2
5524. | |
194 String title = toHTMLElement(node)->title(); | |
195 if (!title.isEmpty()) | |
196 return cacheAndReturnAtkProperty(object, AtkCachedAccessibleDescription,
title); | |
197 | |
198 return cacheAndReturnAtkProperty(object, AtkCachedAccessibleDescription, acc
essibilityDescription(coreObject)); | |
199 } | |
200 | |
201 static void setAtkRelationSetFromCoreObject(AccessibilityObject* coreObject, Atk
RelationSet* relationSet) | |
202 { | |
203 if (coreObject->isFieldset()) { | |
204 AccessibilityObject* label = coreObject->titleUIElement(); | |
205 if (label) | |
206 atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABE
LLED_BY, label->wrapper()); | |
207 return; | |
208 } | |
209 | |
210 if (coreObject->roleValue() == LegendRole) { | |
211 for (AccessibilityObject* parent = coreObject->parentObjectUnignored();
parent; parent = parent->parentObjectUnignored()) { | |
212 if (parent->isFieldset()) { | |
213 atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_
LABEL_FOR, parent->wrapper()); | |
214 break; | |
215 } | |
216 } | |
217 return; | |
218 } | |
219 | |
220 if (coreObject->isControl()) { | |
221 AccessibilityObject* label = coreObject->correspondingLabelForControlEle
ment(); | |
222 if (label) | |
223 atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABE
LLED_BY, label->wrapper()); | |
224 } else { | |
225 AccessibilityObject* control = coreObject->correspondingControlForLabelE
lement(); | |
226 if (control) | |
227 atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABE
L_FOR, control->wrapper()); | |
228 } | |
229 } | |
230 | |
231 static gpointer webkitAccessibleParentClass = 0; | |
232 | |
233 static bool isRootObject(AccessibilityObject* coreObject) | |
234 { | |
235 // The root accessible object in WebCore is always an object with | |
236 // the ScrolledArea role with one child with the WebArea role. | |
237 if (!coreObject || !coreObject->isScrollView()) | |
238 return false; | |
239 | |
240 AccessibilityObject* firstChild = coreObject->firstChild(); | |
241 if (!firstChild || !firstChild->isWebArea()) | |
242 return false; | |
243 | |
244 return true; | |
245 } | |
246 | |
247 static AtkObject* atkParentOfRootObject(AtkObject* object) | |
248 { | |
249 AccessibilityObject* coreObject = core(object); | |
250 AccessibilityObject* coreParent = coreObject->parentObjectUnignored(); | |
251 | |
252 // The top level object claims to not have a parent. This makes it | |
253 // impossible for assistive technologies to ascend the accessible | |
254 // hierarchy all the way to the application. (Bug 30489) | |
255 if (!coreParent && isRootObject(coreObject)) { | |
256 Document* document = coreObject->document(); | |
257 if (!document) | |
258 return 0; | |
259 | |
260 #if PLATFORM(GTK) | |
261 HostWindow* hostWindow = document->view()->hostWindow(); | |
262 if (hostWindow) { | |
263 PlatformPageClient scrollView = hostWindow->platformPageClient(); | |
264 if (scrollView) { | |
265 GtkWidget* scrollViewParent = gtk_widget_get_parent(scrollView); | |
266 if (scrollViewParent) | |
267 return gtk_widget_get_accessible(scrollViewParent); | |
268 } | |
269 } | |
270 #endif // PLATFORM(GTK) | |
271 } | |
272 | |
273 if (!coreParent) | |
274 return 0; | |
275 | |
276 return coreParent->wrapper(); | |
277 } | |
278 | |
279 static AtkObject* webkitAccessibleGetParent(AtkObject* object) | |
280 { | |
281 // Check first if the parent has been already set. | |
282 AtkObject* accessibleParent = ATK_OBJECT_CLASS(webkitAccessibleParentClass)-
>get_parent(object); | |
283 if (accessibleParent) | |
284 return accessibleParent; | |
285 | |
286 // Parent not set yet, so try to find it in the hierarchy. | |
287 AccessibilityObject* coreObject = core(object); | |
288 if (!coreObject) | |
289 return 0; | |
290 | |
291 AccessibilityObject* coreParent = coreObject->parentObjectUnignored(); | |
292 | |
293 if (!coreParent && isRootObject(coreObject)) | |
294 return atkParentOfRootObject(object); | |
295 | |
296 if (!coreParent) | |
297 return 0; | |
298 | |
299 // We don't expose table rows to Assistive technologies, but we | |
300 // need to have them anyway in the hierarchy from WebCore to | |
301 // properly perform coordinates calculations when requested. | |
302 if (coreParent->isTableRow() && coreObject->isTableCell()) | |
303 coreParent = coreParent->parentObjectUnignored(); | |
304 | |
305 return coreParent->wrapper(); | |
306 } | |
307 | |
308 static gint getNChildrenForTable(AccessibilityObject* coreObject) | |
309 { | |
310 AccessibilityObject::AccessibilityChildrenVector tableChildren = coreObject-
>children(); | |
311 size_t tableChildrenCount = tableChildren.size(); | |
312 size_t cellsCount = 0; | |
313 | |
314 // Look for the actual index of the cell inside the table. | |
315 for (unsigned i = 0; i < tableChildrenCount; ++i) { | |
316 if (tableChildren[i]->isTableRow()) { | |
317 AccessibilityObject::AccessibilityChildrenVector rowChildren = table
Children[i]->children(); | |
318 cellsCount += rowChildren.size(); | |
319 } else | |
320 cellsCount++; | |
321 } | |
322 | |
323 return cellsCount; | |
324 } | |
325 | |
326 static gint webkitAccessibleGetNChildren(AtkObject* object) | |
327 { | |
328 AccessibilityObject* coreObject = core(object); | |
329 | |
330 // Tables should be treated in a different way because rows should | |
331 // be bypassed when exposing the accessible hierarchy. | |
332 if (coreObject->isAccessibilityTable()) | |
333 return getNChildrenForTable(coreObject); | |
334 | |
335 return coreObject->children().size(); | |
336 } | |
337 | |
338 static AccessibilityObject* getChildForTable(AccessibilityObject* coreObject, gi
nt index) | |
339 { | |
340 AccessibilityObject::AccessibilityChildrenVector tableChildren = coreObject-
>children(); | |
341 size_t tableChildrenCount = tableChildren.size(); | |
342 size_t cellsCount = 0; | |
343 | |
344 // Look for the actual index of the cell inside the table. | |
345 size_t current = static_cast<size_t>(index); | |
346 for (unsigned i = 0; i < tableChildrenCount; ++i) { | |
347 if (tableChildren[i]->isTableRow()) { | |
348 AccessibilityObject::AccessibilityChildrenVector rowChildren = table
Children[i]->children(); | |
349 size_t rowChildrenCount = rowChildren.size(); | |
350 if (current < cellsCount + rowChildrenCount) | |
351 return rowChildren.at(current - cellsCount).get(); | |
352 cellsCount += rowChildrenCount; | |
353 } else if (cellsCount == current) | |
354 return tableChildren[i].get(); | |
355 else | |
356 cellsCount++; | |
357 } | |
358 | |
359 // Shouldn't reach if the child was found. | |
360 return 0; | |
361 } | |
362 | |
363 static AtkObject* webkitAccessibleRefChild(AtkObject* object, gint index) | |
364 { | |
365 if (index < 0) | |
366 return 0; | |
367 | |
368 AccessibilityObject* coreObject = core(object); | |
369 AccessibilityObject* coreChild = 0; | |
370 | |
371 // Tables are special cases because rows should be bypassed, but | |
372 // still taking their cells into account. | |
373 if (coreObject->isAccessibilityTable()) | |
374 coreChild = getChildForTable(coreObject, index); | |
375 else { | |
376 AccessibilityObject::AccessibilityChildrenVector children = coreObject->
children(); | |
377 if (static_cast<unsigned>(index) >= children.size()) | |
378 return 0; | |
379 coreChild = children.at(index).get(); | |
380 } | |
381 | |
382 if (!coreChild) | |
383 return 0; | |
384 | |
385 AtkObject* child = coreChild->wrapper(); | |
386 atk_object_set_parent(child, object); | |
387 g_object_ref(child); | |
388 | |
389 return child; | |
390 } | |
391 | |
392 static gint getIndexInParentForCellInRow(AccessibilityObject* coreObject) | |
393 { | |
394 AccessibilityObject* parent = coreObject->parentObjectUnignored(); | |
395 if (!parent) | |
396 return -1; | |
397 | |
398 AccessibilityObject* grandParent = parent->parentObjectUnignored(); | |
399 if (!grandParent) | |
400 return -1; | |
401 | |
402 AccessibilityObject::AccessibilityChildrenVector rows = grandParent->childre
n(); | |
403 size_t rowsCount = rows.size(); | |
404 size_t previousCellsCount = 0; | |
405 | |
406 // Look for the actual index of the cell inside the table. | |
407 for (unsigned i = 0; i < rowsCount; ++i) { | |
408 if (!rows[i]->isTableRow()) | |
409 continue; | |
410 | |
411 AccessibilityObject::AccessibilityChildrenVector cells = rows[i]->childr
en(); | |
412 size_t cellsCount = cells.size(); | |
413 | |
414 if (rows[i] == parent) { | |
415 for (unsigned j = 0; j < cellsCount; ++j) { | |
416 if (cells[j] == coreObject) | |
417 return previousCellsCount + j; | |
418 } | |
419 } | |
420 | |
421 previousCellsCount += cellsCount; | |
422 } | |
423 | |
424 return -1; | |
425 } | |
426 | |
427 static gint webkitAccessibleGetIndexInParent(AtkObject* object) | |
428 { | |
429 AccessibilityObject* coreObject = core(object); | |
430 AccessibilityObject* parent = coreObject->parentObjectUnignored(); | |
431 | |
432 if (!parent && isRootObject(coreObject)) { | |
433 AtkObject* atkParent = atkParentOfRootObject(object); | |
434 if (!atkParent) | |
435 return -1; | |
436 | |
437 unsigned count = atk_object_get_n_accessible_children(atkParent); | |
438 for (unsigned i = 0; i < count; ++i) { | |
439 AtkObject* child = atk_object_ref_accessible_child(atkParent, i); | |
440 bool childIsObject = child == object; | |
441 g_object_unref(child); | |
442 if (childIsObject) | |
443 return i; | |
444 } | |
445 } | |
446 | |
447 // Need to calculate the index of the cell in the table, as | |
448 // rows won't be exposed to assistive technologies. | |
449 if (parent && parent->isTableRow() && coreObject->isTableCell()) | |
450 return getIndexInParentForCellInRow(coreObject); | |
451 | |
452 if (!parent) | |
453 return -1; | |
454 | |
455 size_t index = parent->children().find(coreObject); | |
456 return (index == WTF::notFound) ? -1 : index; | |
457 } | |
458 | |
459 static AtkAttributeSet* webkitAccessibleGetAttributes(AtkObject* object) | |
460 { | |
461 AtkAttributeSet* attributeSet = 0; | |
462 #if PLATFORM(GTK) | |
463 attributeSet = addToAtkAttributeSet(attributeSet, "toolkit", "WebKitGtk"); | |
464 #endif | |
465 | |
466 AccessibilityObject* coreObject = core(object); | |
467 if (!coreObject) | |
468 return attributeSet; | |
469 | |
470 // Hack needed for WebKit2 tests because obtaining an element by its ID | |
471 // cannot be done from the UIProcess. Assistive technologies have no need | |
472 // for this information. | |
473 Node* node = coreObject->node(); | |
474 if (node && node->isElementNode()) { | |
475 String id = toElement(node)->getIdAttribute().string(); | |
476 if (!id.isEmpty()) | |
477 attributeSet = addToAtkAttributeSet(attributeSet, "html-id", id.utf8
().data()); | |
478 } | |
479 | |
480 int headingLevel = coreObject->headingLevel(); | |
481 if (headingLevel) { | |
482 String value = String::number(headingLevel); | |
483 attributeSet = addToAtkAttributeSet(attributeSet, "level", value.utf8().
data()); | |
484 } | |
485 | |
486 // Set the 'layout-guess' attribute to help Assistive | |
487 // Technologies know when an exposed table is not data table. | |
488 if (coreObject->isAccessibilityTable() && !coreObject->isDataTable()) | |
489 attributeSet = addToAtkAttributeSet(attributeSet, "layout-guess", "true"
); | |
490 | |
491 String placeholder = coreObject->placeholderValue(); | |
492 if (!placeholder.isEmpty()) | |
493 attributeSet = addToAtkAttributeSet(attributeSet, "placeholder-text", pl
aceholder.utf8().data()); | |
494 | |
495 return attributeSet; | |
496 } | |
497 | |
498 static AtkRole atkRole(AccessibilityRole role) | |
499 { | |
500 switch (role) { | |
501 case UnknownRole: | |
502 return ATK_ROLE_UNKNOWN; | |
503 case ButtonRole: | |
504 return ATK_ROLE_PUSH_BUTTON; | |
505 case ToggleButtonRole: | |
506 return ATK_ROLE_TOGGLE_BUTTON; | |
507 case RadioButtonRole: | |
508 return ATK_ROLE_RADIO_BUTTON; | |
509 case CheckBoxRole: | |
510 return ATK_ROLE_CHECK_BOX; | |
511 case SliderRole: | |
512 return ATK_ROLE_SLIDER; | |
513 case TabGroupRole: | |
514 case TabListRole: | |
515 return ATK_ROLE_PAGE_TAB_LIST; | |
516 case TextFieldRole: | |
517 case TextAreaRole: | |
518 return ATK_ROLE_ENTRY; | |
519 case StaticTextRole: | |
520 return ATK_ROLE_TEXT; | |
521 case OutlineRole: | |
522 return ATK_ROLE_TREE; | |
523 case MenuBarRole: | |
524 return ATK_ROLE_MENU_BAR; | |
525 case MenuListPopupRole: | |
526 case MenuRole: | |
527 return ATK_ROLE_MENU; | |
528 case MenuListOptionRole: | |
529 case MenuItemRole: | |
530 return ATK_ROLE_MENU_ITEM; | |
531 case ColumnRole: | |
532 // return ATK_ROLE_TABLE_COLUMN_HEADER; // Is this right? | |
533 return ATK_ROLE_UNKNOWN; // Matches Mozilla | |
534 case RowRole: | |
535 // return ATK_ROLE_TABLE_ROW_HEADER; // Is this right? | |
536 return ATK_ROLE_LIST_ITEM; // Matches Mozilla | |
537 case ToolbarRole: | |
538 return ATK_ROLE_TOOL_BAR; | |
539 case BusyIndicatorRole: | |
540 return ATK_ROLE_PROGRESS_BAR; // Is this right? | |
541 case ProgressIndicatorRole: | |
542 // return ATK_ROLE_SPIN_BUTTON; // Some confusion about this role in Acc
essibilityRenderObject.cpp | |
543 return ATK_ROLE_PROGRESS_BAR; | |
544 case WindowRole: | |
545 return ATK_ROLE_WINDOW; | |
546 case PopUpButtonRole: | |
547 case ComboBoxRole: | |
548 return ATK_ROLE_COMBO_BOX; | |
549 case SplitGroupRole: | |
550 return ATK_ROLE_SPLIT_PANE; | |
551 case SplitterRole: | |
552 return ATK_ROLE_UNKNOWN; | |
553 case ColorWellRole: | |
554 return ATK_ROLE_COLOR_CHOOSER; | |
555 case ListRole: | |
556 return ATK_ROLE_LIST; | |
557 case ScrollBarRole: | |
558 return ATK_ROLE_SCROLL_BAR; | |
559 case ScrollAreaRole: | |
560 return ATK_ROLE_SCROLL_PANE; | |
561 case GridRole: // Is this right? | |
562 case TableRole: | |
563 return ATK_ROLE_TABLE; | |
564 case ApplicationRole: | |
565 return ATK_ROLE_APPLICATION; | |
566 case GroupRole: | |
567 case RadioGroupRole: | |
568 case TabPanelRole: | |
569 return ATK_ROLE_PANEL; | |
570 case RowHeaderRole: // Row headers are cells after all. | |
571 case ColumnHeaderRole: // Column headers are cells after all. | |
572 case CellRole: | |
573 return ATK_ROLE_TABLE_CELL; | |
574 case LinkRole: | |
575 case WebCoreLinkRole: | |
576 case ImageMapLinkRole: | |
577 return ATK_ROLE_LINK; | |
578 case ImageMapRole: | |
579 case ImageRole: | |
580 return ATK_ROLE_IMAGE; | |
581 case ListMarkerRole: | |
582 return ATK_ROLE_TEXT; | |
583 case WebAreaRole: | |
584 // return ATK_ROLE_HTML_CONTAINER; // Is this right? | |
585 return ATK_ROLE_DOCUMENT_FRAME; | |
586 case HeadingRole: | |
587 return ATK_ROLE_HEADING; | |
588 case ListBoxRole: | |
589 return ATK_ROLE_LIST; | |
590 case ListItemRole: | |
591 case ListBoxOptionRole: | |
592 return ATK_ROLE_LIST_ITEM; | |
593 case ParagraphRole: | |
594 return ATK_ROLE_PARAGRAPH; | |
595 case LabelRole: | |
596 case LegendRole: | |
597 return ATK_ROLE_LABEL; | |
598 case DivRole: | |
599 return ATK_ROLE_SECTION; | |
600 case FormRole: | |
601 return ATK_ROLE_FORM; | |
602 case CanvasRole: | |
603 return ATK_ROLE_CANVAS; | |
604 case HorizontalRuleRole: | |
605 return ATK_ROLE_SEPARATOR; | |
606 case SpinButtonRole: | |
607 return ATK_ROLE_SPIN_BUTTON; | |
608 case TabRole: | |
609 return ATK_ROLE_PAGE_TAB; | |
610 default: | |
611 return ATK_ROLE_UNKNOWN; | |
612 } | |
613 } | |
614 | |
615 static AtkRole webkitAccessibleGetRole(AtkObject* object) | |
616 { | |
617 AccessibilityObject* coreObject = core(object); | |
618 | |
619 if (!coreObject) | |
620 return ATK_ROLE_UNKNOWN; | |
621 | |
622 // Note: Why doesn't WebCore have a password field for this | |
623 if (coreObject->isPasswordField()) | |
624 return ATK_ROLE_PASSWORD_TEXT; | |
625 | |
626 return atkRole(coreObject->roleValue()); | |
627 } | |
628 | |
629 static bool isTextWithCaret(AccessibilityObject* coreObject) | |
630 { | |
631 if (!coreObject || !coreObject->isAccessibilityRenderObject()) | |
632 return false; | |
633 | |
634 Document* document = coreObject->document(); | |
635 if (!document) | |
636 return false; | |
637 | |
638 Frame* frame = document->frame(); | |
639 if (!frame) | |
640 return false; | |
641 | |
642 Settings* settings = frame->settings(); | |
643 if (!settings || !settings->caretBrowsingEnabled()) | |
644 return false; | |
645 | |
646 // Check text objects and paragraphs only. | |
647 AtkObject* axObject = coreObject->wrapper(); | |
648 AtkRole role = axObject ? atk_object_get_role(axObject) : ATK_ROLE_INVALID; | |
649 if (role != ATK_ROLE_TEXT && role != ATK_ROLE_PARAGRAPH) | |
650 return false; | |
651 | |
652 // Finally, check whether the caret is set in the current object. | |
653 VisibleSelection selection = coreObject->selection(); | |
654 if (!selection.isCaret()) | |
655 return false; | |
656 | |
657 return selectionBelongsToObject(coreObject, selection); | |
658 } | |
659 | |
660 static void setAtkStateSetFromCoreObject(AccessibilityObject* coreObject, AtkSta
teSet* stateSet) | |
661 { | |
662 AccessibilityObject* parent = coreObject->parentObject(); | |
663 bool isListBoxOption = parent && parent->isListBox(); | |
664 | |
665 // Please keep the state list in alphabetical order | |
666 if (coreObject->isChecked()) | |
667 atk_state_set_add_state(stateSet, ATK_STATE_CHECKED); | |
668 | |
669 // FIXME: isReadOnly does not seem to do the right thing for | |
670 // controls, so check explicitly for them. In addition, because | |
671 // isReadOnly is false for listBoxOptions, we need to add one | |
672 // more check so that we do not present them as being "editable". | |
673 if ((!coreObject->isReadOnly() | |
674 || (coreObject->isControl() && coreObject->canSetValueAttribute())) | |
675 && !isListBoxOption) | |
676 atk_state_set_add_state(stateSet, ATK_STATE_EDITABLE); | |
677 | |
678 // FIXME: Put both ENABLED and SENSITIVE together here for now | |
679 if (coreObject->isEnabled()) { | |
680 atk_state_set_add_state(stateSet, ATK_STATE_ENABLED); | |
681 atk_state_set_add_state(stateSet, ATK_STATE_SENSITIVE); | |
682 } | |
683 | |
684 if (coreObject->canSetExpandedAttribute()) | |
685 atk_state_set_add_state(stateSet, ATK_STATE_EXPANDABLE); | |
686 | |
687 if (coreObject->isExpanded()) | |
688 atk_state_set_add_state(stateSet, ATK_STATE_EXPANDED); | |
689 | |
690 if (coreObject->canSetFocusAttribute()) | |
691 atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE); | |
692 | |
693 if (coreObject->isFocused() || isTextWithCaret(coreObject)) | |
694 atk_state_set_add_state(stateSet, ATK_STATE_FOCUSED); | |
695 | |
696 if (coreObject->orientation() == AccessibilityOrientationHorizontal) | |
697 atk_state_set_add_state(stateSet, ATK_STATE_HORIZONTAL); | |
698 else if (coreObject->orientation() == AccessibilityOrientationVertical) | |
699 atk_state_set_add_state(stateSet, ATK_STATE_VERTICAL); | |
700 | |
701 if (coreObject->isIndeterminate()) | |
702 atk_state_set_add_state(stateSet, ATK_STATE_INDETERMINATE); | |
703 | |
704 if (coreObject->isMultiSelectable()) | |
705 atk_state_set_add_state(stateSet, ATK_STATE_MULTISELECTABLE); | |
706 | |
707 // TODO: ATK_STATE_OPAQUE | |
708 | |
709 if (coreObject->isPressed()) | |
710 atk_state_set_add_state(stateSet, ATK_STATE_PRESSED); | |
711 | |
712 // TODO: ATK_STATE_SELECTABLE_TEXT | |
713 | |
714 if (coreObject->canSetSelectedAttribute()) { | |
715 atk_state_set_add_state(stateSet, ATK_STATE_SELECTABLE); | |
716 // Items in focusable lists have both STATE_SELECT{ABLE,ED} | |
717 // and STATE_FOCUS{ABLE,ED}. We'll fake the latter based on | |
718 // the former. | |
719 if (isListBoxOption) | |
720 atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE); | |
721 } | |
722 | |
723 if (coreObject->isSelected()) { | |
724 atk_state_set_add_state(stateSet, ATK_STATE_SELECTED); | |
725 // Items in focusable lists have both STATE_SELECT{ABLE,ED} | |
726 // and STATE_FOCUS{ABLE,ED}. We'll fake the latter based on the | |
727 // former. | |
728 if (isListBoxOption) | |
729 atk_state_set_add_state(stateSet, ATK_STATE_FOCUSED); | |
730 } | |
731 | |
732 // FIXME: Group both SHOWING and VISIBLE here for now | |
733 // Not sure how to handle this in WebKit, see bug | |
734 // http://bugzilla.gnome.org/show_bug.cgi?id=509650 for other | |
735 // issues with SHOWING vs VISIBLE. | |
736 if (!coreObject->isOffScreen()) { | |
737 atk_state_set_add_state(stateSet, ATK_STATE_SHOWING); | |
738 atk_state_set_add_state(stateSet, ATK_STATE_VISIBLE); | |
739 } | |
740 | |
741 // Mutually exclusive, so we group these two | |
742 if (coreObject->roleValue() == TextFieldRole) | |
743 atk_state_set_add_state(stateSet, ATK_STATE_SINGLE_LINE); | |
744 else if (coreObject->roleValue() == TextAreaRole) | |
745 atk_state_set_add_state(stateSet, ATK_STATE_MULTI_LINE); | |
746 | |
747 // TODO: ATK_STATE_SENSITIVE | |
748 | |
749 if (coreObject->isVisited()) | |
750 atk_state_set_add_state(stateSet, ATK_STATE_VISITED); | |
751 } | |
752 | |
753 static AtkStateSet* webkitAccessibleRefStateSet(AtkObject* object) | |
754 { | |
755 AtkStateSet* stateSet = ATK_OBJECT_CLASS(webkitAccessibleParentClass)->ref_s
tate_set(object); | |
756 AccessibilityObject* coreObject = core(object); | |
757 | |
758 if (coreObject == fallbackObject()) { | |
759 atk_state_set_add_state(stateSet, ATK_STATE_DEFUNCT); | |
760 return stateSet; | |
761 } | |
762 | |
763 // Text objects must be focusable. | |
764 AtkRole role = atk_object_get_role(object); | |
765 if (role == ATK_ROLE_TEXT || role == ATK_ROLE_PARAGRAPH) | |
766 atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE); | |
767 | |
768 setAtkStateSetFromCoreObject(coreObject, stateSet); | |
769 return stateSet; | |
770 } | |
771 | |
772 static AtkRelationSet* webkitAccessibleRefRelationSet(AtkObject* object) | |
773 { | |
774 AtkRelationSet* relationSet = ATK_OBJECT_CLASS(webkitAccessibleParentClass)-
>ref_relation_set(object); | |
775 AccessibilityObject* coreObject = core(object); | |
776 | |
777 setAtkRelationSetFromCoreObject(coreObject, relationSet); | |
778 | |
779 return relationSet; | |
780 } | |
781 | |
782 static void webkitAccessibleInit(AtkObject* object, gpointer data) | |
783 { | |
784 if (ATK_OBJECT_CLASS(webkitAccessibleParentClass)->initialize) | |
785 ATK_OBJECT_CLASS(webkitAccessibleParentClass)->initialize(object, data); | |
786 | |
787 WebKitAccessible* accessible = WEBKIT_ACCESSIBLE(object); | |
788 accessible->m_object = reinterpret_cast<AccessibilityObject*>(data); | |
789 accessible->priv = WEBKIT_ACCESSIBLE_GET_PRIVATE(accessible); | |
790 } | |
791 | |
792 static void webkitAccessibleFinalize(GObject* object) | |
793 { | |
794 G_OBJECT_CLASS(webkitAccessibleParentClass)->finalize(object); | |
795 } | |
796 | |
797 static void webkitAccessibleClassInit(AtkObjectClass* klass) | |
798 { | |
799 GObjectClass* gobjectClass = G_OBJECT_CLASS(klass); | |
800 | |
801 webkitAccessibleParentClass = g_type_class_peek_parent(klass); | |
802 | |
803 gobjectClass->finalize = webkitAccessibleFinalize; | |
804 | |
805 klass->initialize = webkitAccessibleInit; | |
806 klass->get_name = webkitAccessibleGetName; | |
807 klass->get_description = webkitAccessibleGetDescription; | |
808 klass->get_parent = webkitAccessibleGetParent; | |
809 klass->get_n_children = webkitAccessibleGetNChildren; | |
810 klass->ref_child = webkitAccessibleRefChild; | |
811 klass->get_role = webkitAccessibleGetRole; | |
812 klass->ref_state_set = webkitAccessibleRefStateSet; | |
813 klass->get_index_in_parent = webkitAccessibleGetIndexInParent; | |
814 klass->get_attributes = webkitAccessibleGetAttributes; | |
815 klass->ref_relation_set = webkitAccessibleRefRelationSet; | |
816 | |
817 g_type_class_add_private(klass, sizeof(WebKitAccessiblePrivate)); | |
818 } | |
819 | |
820 GType | |
821 webkitAccessibleGetType(void) | |
822 { | |
823 static volatile gsize typeVolatile = 0; | |
824 | |
825 if (g_once_init_enter(&typeVolatile)) { | |
826 static const GTypeInfo tinfo = { | |
827 sizeof(WebKitAccessibleClass), | |
828 (GBaseInitFunc) 0, | |
829 (GBaseFinalizeFunc) 0, | |
830 (GClassInitFunc) webkitAccessibleClassInit, | |
831 (GClassFinalizeFunc) 0, | |
832 0, /* class data */ | |
833 sizeof(WebKitAccessible), /* instance size */ | |
834 0, /* nb preallocs */ | |
835 (GInstanceInitFunc) 0, | |
836 0 /* value table */ | |
837 }; | |
838 | |
839 GType type = g_type_register_static(ATK_TYPE_OBJECT, "WebKitAccessible",
&tinfo, GTypeFlags(0)); | |
840 g_once_init_leave(&typeVolatile, type); | |
841 } | |
842 | |
843 return typeVolatile; | |
844 } | |
845 | |
846 static const GInterfaceInfo AtkInterfacesInitFunctions[] = { | |
847 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleActionInterfaceInit),
0, 0}, | |
848 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleSelectionInterfaceInit
), 0, 0}, | |
849 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleEditableTextInterfaceI
nit), 0, 0}, | |
850 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleTextInterfaceInit), 0,
0}, | |
851 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleComponentInterfaceInit
), 0, 0}, | |
852 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleImageInterfaceInit), 0
, 0}, | |
853 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleTableInterfaceInit), 0
, 0}, | |
854 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleHypertextInterfaceInit
), 0, 0}, | |
855 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleHyperlinkImplInterface
Init), 0, 0}, | |
856 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleDocumentInterfaceInit)
, 0, 0}, | |
857 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleValueInterfaceInit), 0
, 0} | |
858 }; | |
859 | |
860 enum WAIType { | |
861 WAI_ACTION, | |
862 WAI_SELECTION, | |
863 WAI_EDITABLE_TEXT, | |
864 WAI_TEXT, | |
865 WAI_COMPONENT, | |
866 WAI_IMAGE, | |
867 WAI_TABLE, | |
868 WAI_HYPERTEXT, | |
869 WAI_HYPERLINK, | |
870 WAI_DOCUMENT, | |
871 WAI_VALUE, | |
872 }; | |
873 | |
874 static GType GetAtkInterfaceTypeFromWAIType(WAIType type) | |
875 { | |
876 switch (type) { | |
877 case WAI_ACTION: | |
878 return ATK_TYPE_ACTION; | |
879 case WAI_SELECTION: | |
880 return ATK_TYPE_SELECTION; | |
881 case WAI_EDITABLE_TEXT: | |
882 return ATK_TYPE_EDITABLE_TEXT; | |
883 case WAI_TEXT: | |
884 return ATK_TYPE_TEXT; | |
885 case WAI_COMPONENT: | |
886 return ATK_TYPE_COMPONENT; | |
887 case WAI_IMAGE: | |
888 return ATK_TYPE_IMAGE; | |
889 case WAI_TABLE: | |
890 return ATK_TYPE_TABLE; | |
891 case WAI_HYPERTEXT: | |
892 return ATK_TYPE_HYPERTEXT; | |
893 case WAI_HYPERLINK: | |
894 return ATK_TYPE_HYPERLINK_IMPL; | |
895 case WAI_DOCUMENT: | |
896 return ATK_TYPE_DOCUMENT; | |
897 case WAI_VALUE: | |
898 return ATK_TYPE_VALUE; | |
899 } | |
900 | |
901 return G_TYPE_INVALID; | |
902 } | |
903 | |
904 static bool roleIsTextType(AccessibilityRole role) | |
905 { | |
906 return role == ParagraphRole || role == HeadingRole || role == DivRole || ro
le == CellRole || role == ListItemRole; | |
907 } | |
908 | |
909 static guint16 getInterfaceMaskFromObject(AccessibilityObject* coreObject) | |
910 { | |
911 guint16 interfaceMask = 0; | |
912 | |
913 // Component interface is always supported | |
914 interfaceMask |= 1 << WAI_COMPONENT; | |
915 | |
916 AccessibilityRole role = coreObject->roleValue(); | |
917 | |
918 // Action | |
919 // As the implementation of the AtkAction interface is a very | |
920 // basic one (just relays in executing the default action for each | |
921 // object, and only supports having one action per object), it is | |
922 // better just to implement this interface for every instance of | |
923 // the WebKitAccessible class and let WebCore decide what to do. | |
924 interfaceMask |= 1 << WAI_ACTION; | |
925 | |
926 // Selection | |
927 if (coreObject->isListBox() || coreObject->isMenuList()) | |
928 interfaceMask |= 1 << WAI_SELECTION; | |
929 | |
930 // Get renderer if available. | |
931 RenderObject* renderer = 0; | |
932 if (coreObject->isAccessibilityRenderObject()) | |
933 renderer = coreObject->renderer(); | |
934 | |
935 // Hyperlink (links and embedded objects). | |
936 if (coreObject->isLink() || (renderer && renderer->isReplaced())) | |
937 interfaceMask |= 1 << WAI_HYPERLINK; | |
938 | |
939 // Text & Editable Text | |
940 if (role == StaticTextRole || coreObject->isMenuListOption()) | |
941 interfaceMask |= 1 << WAI_TEXT; | |
942 else { | |
943 if (coreObject->isTextControl()) { | |
944 interfaceMask |= 1 << WAI_TEXT; | |
945 if (!coreObject->isReadOnly()) | |
946 interfaceMask |= 1 << WAI_EDITABLE_TEXT; | |
947 } else { | |
948 if (role != TableRole) { | |
949 interfaceMask |= 1 << WAI_HYPERTEXT; | |
950 if ((renderer && renderer->childrenInline()) || roleIsTextType(r
ole)) | |
951 interfaceMask |= 1 << WAI_TEXT; | |
952 } | |
953 | |
954 // Add the TEXT interface for list items whose | |
955 // first accessible child has a text renderer | |
956 if (role == ListItemRole) { | |
957 AccessibilityObject::AccessibilityChildrenVector children = core
Object->children(); | |
958 if (children.size()) { | |
959 AccessibilityObject* axRenderChild = children.at(0).get(); | |
960 interfaceMask |= getInterfaceMaskFromObject(axRenderChild); | |
961 } | |
962 } | |
963 } | |
964 } | |
965 | |
966 // Image | |
967 if (coreObject->isImage()) | |
968 interfaceMask |= 1 << WAI_IMAGE; | |
969 | |
970 // Table | |
971 if (role == TableRole) | |
972 interfaceMask |= 1 << WAI_TABLE; | |
973 | |
974 // Document | |
975 if (role == WebAreaRole) | |
976 interfaceMask |= 1 << WAI_DOCUMENT; | |
977 | |
978 // Value | |
979 if (role == SliderRole || role == SpinButtonRole || role == ScrollBarRole) | |
980 interfaceMask |= 1 << WAI_VALUE; | |
981 | |
982 return interfaceMask; | |
983 } | |
984 | |
985 static const char* getUniqueAccessibilityTypeName(guint16 interfaceMask) | |
986 { | |
987 #define WAI_TYPE_NAME_LEN (30) /* Enough for prefix + 5 hex characters (max) */ | |
988 static char name[WAI_TYPE_NAME_LEN + 1]; | |
989 | |
990 g_sprintf(name, "WAIType%x", interfaceMask); | |
991 name[WAI_TYPE_NAME_LEN] = '\0'; | |
992 | |
993 return name; | |
994 } | |
995 | |
996 static GType getAccessibilityTypeFromObject(AccessibilityObject* coreObject) | |
997 { | |
998 static const GTypeInfo typeInfo = { | |
999 sizeof(WebKitAccessibleClass), | |
1000 (GBaseInitFunc) 0, | |
1001 (GBaseFinalizeFunc) 0, | |
1002 (GClassInitFunc) 0, | |
1003 (GClassFinalizeFunc) 0, | |
1004 0, /* class data */ | |
1005 sizeof(WebKitAccessible), /* instance size */ | |
1006 0, /* nb preallocs */ | |
1007 (GInstanceInitFunc) 0, | |
1008 0 /* value table */ | |
1009 }; | |
1010 | |
1011 guint16 interfaceMask = getInterfaceMaskFromObject(coreObject); | |
1012 const char* atkTypeName = getUniqueAccessibilityTypeName(interfaceMask); | |
1013 GType type = g_type_from_name(atkTypeName); | |
1014 if (type) | |
1015 return type; | |
1016 | |
1017 type = g_type_register_static(WEBKIT_TYPE_ACCESSIBLE, atkTypeName, &typeInfo
, GTypeFlags(0)); | |
1018 for (guint i = 0; i < G_N_ELEMENTS(AtkInterfacesInitFunctions); i++) { | |
1019 if (interfaceMask & (1 << i)) | |
1020 g_type_add_interface_static(type, | |
1021 GetAtkInterfaceTypeFromWAIType(static_cast<WAIType>(i)), | |
1022 &AtkInterfacesInitFunctions[i]); | |
1023 } | |
1024 | |
1025 return type; | |
1026 } | |
1027 | |
1028 WebKitAccessible* webkitAccessibleNew(AccessibilityObject* coreObject) | |
1029 { | |
1030 GType type = getAccessibilityTypeFromObject(coreObject); | |
1031 AtkObject* object = static_cast<AtkObject*>(g_object_new(type, 0)); | |
1032 | |
1033 atk_object_initialize(object, coreObject); | |
1034 | |
1035 return WEBKIT_ACCESSIBLE(object); | |
1036 } | |
1037 | |
1038 AccessibilityObject* webkitAccessibleGetAccessibilityObject(WebKitAccessible* ac
cessible) | |
1039 { | |
1040 return accessible->m_object; | |
1041 } | |
1042 | |
1043 void webkitAccessibleDetach(WebKitAccessible* accessible) | |
1044 { | |
1045 ASSERT(accessible->m_object); | |
1046 | |
1047 if (core(accessible)->roleValue() == WebAreaRole) | |
1048 g_signal_emit_by_name(accessible, "state-change", "defunct", true); | |
1049 | |
1050 // We replace the WebCore AccessibilityObject with a fallback object that | |
1051 // provides default implementations to avoid repetitive null-checking after | |
1052 // detachment. | |
1053 accessible->m_object = fallbackObject(); | |
1054 } | |
1055 | |
1056 AtkObject* webkitAccessibleGetFocusedElement(WebKitAccessible* accessible) | |
1057 { | |
1058 if (!accessible->m_object) | |
1059 return 0; | |
1060 | |
1061 RefPtr<AccessibilityObject> focusedObj = accessible->m_object->focusedUIElem
ent(); | |
1062 if (!focusedObj) | |
1063 return 0; | |
1064 | |
1065 return focusedObj->wrapper(); | |
1066 } | |
1067 | |
1068 AccessibilityObject* objectFocusedAndCaretOffsetUnignored(AccessibilityObject* r
eferenceObject, int& offset) | |
1069 { | |
1070 // Indication that something bogus has transpired. | |
1071 offset = -1; | |
1072 | |
1073 Document* document = referenceObject->document(); | |
1074 if (!document) | |
1075 return 0; | |
1076 | |
1077 Node* focusedNode = referenceObject->selection().end().containerNode(); | |
1078 if (!focusedNode) | |
1079 return 0; | |
1080 | |
1081 RenderObject* focusedRenderer = focusedNode->renderer(); | |
1082 if (!focusedRenderer) | |
1083 return 0; | |
1084 | |
1085 AccessibilityObject* focusedObject = document->axObjectCache()->getOrCreate(
focusedRenderer); | |
1086 if (!focusedObject) | |
1087 return 0; | |
1088 | |
1089 // Look for the actual (not ignoring accessibility) selected object. | |
1090 AccessibilityObject* firstUnignoredParent = focusedObject; | |
1091 if (firstUnignoredParent->accessibilityIsIgnored()) | |
1092 firstUnignoredParent = firstUnignoredParent->parentObjectUnignored(); | |
1093 if (!firstUnignoredParent) | |
1094 return 0; | |
1095 | |
1096 // Don't ignore links if the offset is being requested for a link. | |
1097 if (!referenceObject->isLink() && firstUnignoredParent->isLink()) | |
1098 firstUnignoredParent = firstUnignoredParent->parentObjectUnignored(); | |
1099 if (!firstUnignoredParent) | |
1100 return 0; | |
1101 | |
1102 // The reference object must either coincide with the focused | |
1103 // object being considered, or be a descendant of it. | |
1104 if (referenceObject->isDescendantOfObject(firstUnignoredParent)) | |
1105 referenceObject = firstUnignoredParent; | |
1106 | |
1107 Node* startNode = 0; | |
1108 if (firstUnignoredParent != referenceObject || firstUnignoredParent->isTextC
ontrol()) { | |
1109 // We need to use the first child's node of the reference | |
1110 // object as the start point to calculate the caret offset | |
1111 // because we want it to be relative to the object of | |
1112 // reference, not just to the focused object (which could have | |
1113 // previous siblings which should be taken into account too). | |
1114 AccessibilityObject* axFirstChild = referenceObject->firstChild(); | |
1115 if (axFirstChild) | |
1116 startNode = axFirstChild->node(); | |
1117 } | |
1118 // Getting the Position of a PseudoElement now triggers an assertion. | |
1119 // This can occur when clicking on empty space in a render block. | |
1120 if (!startNode || startNode->isPseudoElement()) | |
1121 startNode = firstUnignoredParent->node(); | |
1122 | |
1123 // Check if the node for the first parent object not ignoring | |
1124 // accessibility is null again before using it. This might happen | |
1125 // with certain kind of accessibility objects, such as the root | |
1126 // one (the scroller containing the webArea object). | |
1127 if (!startNode) | |
1128 return 0; | |
1129 | |
1130 VisiblePosition startPosition = VisiblePosition(positionBeforeNode(startNode
), DOWNSTREAM); | |
1131 VisiblePosition endPosition = firstUnignoredParent->selection().visibleEnd()
; | |
1132 | |
1133 if (startPosition == endPosition) | |
1134 offset = 0; | |
1135 else if (!isStartOfLine(endPosition)) { | |
1136 RefPtr<Range> range = makeRange(startPosition, endPosition.previous()); | |
1137 offset = TextIterator::rangeLength(range.get(), true) + 1; | |
1138 } else { | |
1139 RefPtr<Range> range = makeRange(startPosition, endPosition); | |
1140 offset = TextIterator::rangeLength(range.get(), true); | |
1141 } | |
1142 | |
1143 return firstUnignoredParent; | |
1144 } | |
1145 | |
1146 const char* cacheAndReturnAtkProperty(AtkObject* object, AtkCachedProperty prope
rty, String value) | |
1147 { | |
1148 WebKitAccessiblePrivate* priv = WEBKIT_ACCESSIBLE(object)->priv; | |
1149 CString* propertyPtr = 0; | |
1150 | |
1151 switch (property) { | |
1152 case AtkCachedAccessibleName: | |
1153 propertyPtr = &priv->accessibleName; | |
1154 break; | |
1155 | |
1156 case AtkCachedAccessibleDescription: | |
1157 propertyPtr = &priv->accessibleDescription; | |
1158 break; | |
1159 | |
1160 case AtkCachedActionName: | |
1161 propertyPtr = &priv->actionName; | |
1162 break; | |
1163 | |
1164 case AtkCachedActionKeyBinding: | |
1165 propertyPtr = &priv->actionKeyBinding; | |
1166 break; | |
1167 | |
1168 case AtkCachedDocumentLocale: | |
1169 propertyPtr = &priv->documentLocale; | |
1170 break; | |
1171 | |
1172 case AtkCachedDocumentType: | |
1173 propertyPtr = &priv->documentType; | |
1174 break; | |
1175 | |
1176 case AtkCachedDocumentEncoding: | |
1177 propertyPtr = &priv->documentEncoding; | |
1178 break; | |
1179 | |
1180 case AtkCachedDocumentURI: | |
1181 propertyPtr = &priv->documentURI; | |
1182 break; | |
1183 | |
1184 case AtkCachedImageDescription: | |
1185 propertyPtr = &priv->imageDescription; | |
1186 break; | |
1187 | |
1188 default: | |
1189 ASSERT_NOT_REACHED(); | |
1190 } | |
1191 | |
1192 // Don't invalidate old memory if not stricly needed, since other | |
1193 // callers might be still holding on to it. | |
1194 if (*propertyPtr != value.utf8()) | |
1195 *propertyPtr = value.utf8(); | |
1196 | |
1197 return (*propertyPtr).data(); | |
1198 } | |
1199 | |
1200 #endif // HAVE(ACCESSIBILITY) | |
OLD | NEW |