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