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