<https://webkit.org/b/98350> [GTK] accessibility/aria-invalid.html times out
[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     String invalidStatus = coreObject->invalidStatus().string();
502     if (!invalidStatus.isEmpty() && invalidStatus != "false")
503         attributeSet = addToAtkAttributeSet(attributeSet, "aria-invalid", coreObject->invalidStatus().string().utf8().data());
504
505     return attributeSet;
506 }
507
508 static AtkRole atkRole(AccessibilityRole role)
509 {
510     switch (role) {
511     case UnknownRole:
512         return ATK_ROLE_UNKNOWN;
513     case ButtonRole:
514         return ATK_ROLE_PUSH_BUTTON;
515     case ToggleButtonRole:
516         return ATK_ROLE_TOGGLE_BUTTON;
517     case RadioButtonRole:
518         return ATK_ROLE_RADIO_BUTTON;
519     case CheckBoxRole:
520         return ATK_ROLE_CHECK_BOX;
521     case SliderRole:
522         return ATK_ROLE_SLIDER;
523     case TabGroupRole:
524     case TabListRole:
525         return ATK_ROLE_PAGE_TAB_LIST;
526     case TextFieldRole:
527     case TextAreaRole:
528         return ATK_ROLE_ENTRY;
529     case StaticTextRole:
530         return ATK_ROLE_TEXT;
531     case OutlineRole:
532         return ATK_ROLE_TREE;
533     case MenuBarRole:
534         return ATK_ROLE_MENU_BAR;
535     case MenuListPopupRole:
536     case MenuRole:
537         return ATK_ROLE_MENU;
538     case MenuListOptionRole:
539     case MenuItemRole:
540         return ATK_ROLE_MENU_ITEM;
541     case ColumnRole:
542         // return ATK_ROLE_TABLE_COLUMN_HEADER; // Is this right?
543         return ATK_ROLE_UNKNOWN; // Matches Mozilla
544     case RowRole:
545         // return ATK_ROLE_TABLE_ROW_HEADER; // Is this right?
546         return ATK_ROLE_LIST_ITEM; // Matches Mozilla
547     case ToolbarRole:
548         return ATK_ROLE_TOOL_BAR;
549     case BusyIndicatorRole:
550         return ATK_ROLE_PROGRESS_BAR; // Is this right?
551     case ProgressIndicatorRole:
552         // return ATK_ROLE_SPIN_BUTTON; // Some confusion about this role in AccessibilityRenderObject.cpp
553         return ATK_ROLE_PROGRESS_BAR;
554     case WindowRole:
555         return ATK_ROLE_WINDOW;
556     case PopUpButtonRole:
557     case ComboBoxRole:
558         return ATK_ROLE_COMBO_BOX;
559     case SplitGroupRole:
560         return ATK_ROLE_SPLIT_PANE;
561     case SplitterRole:
562         return ATK_ROLE_UNKNOWN;
563     case ColorWellRole:
564         return ATK_ROLE_COLOR_CHOOSER;
565     case ListRole:
566         return ATK_ROLE_LIST;
567     case ScrollBarRole:
568         return ATK_ROLE_SCROLL_BAR;
569     case ScrollAreaRole:
570         return ATK_ROLE_SCROLL_PANE;
571     case GridRole: // Is this right?
572     case TableRole:
573         return ATK_ROLE_TABLE;
574     case ApplicationRole:
575         return ATK_ROLE_APPLICATION;
576     case GroupRole:
577     case RadioGroupRole:
578     case TabPanelRole:
579         return ATK_ROLE_PANEL;
580     case RowHeaderRole: // Row headers are cells after all.
581     case ColumnHeaderRole: // Column headers are cells after all.
582     case CellRole:
583         return ATK_ROLE_TABLE_CELL;
584     case LinkRole:
585     case WebCoreLinkRole:
586     case ImageMapLinkRole:
587         return ATK_ROLE_LINK;
588     case ImageMapRole:
589     case ImageRole:
590         return ATK_ROLE_IMAGE;
591     case ListMarkerRole:
592         return ATK_ROLE_TEXT;
593     case WebAreaRole:
594         // return ATK_ROLE_HTML_CONTAINER; // Is this right?
595         return ATK_ROLE_DOCUMENT_FRAME;
596     case HeadingRole:
597         return ATK_ROLE_HEADING;
598     case ListBoxRole:
599         return ATK_ROLE_LIST;
600     case ListItemRole:
601     case ListBoxOptionRole:
602         return ATK_ROLE_LIST_ITEM;
603     case ParagraphRole:
604         return ATK_ROLE_PARAGRAPH;
605     case LabelRole:
606     case LegendRole:
607         return ATK_ROLE_LABEL;
608     case DivRole:
609         return ATK_ROLE_SECTION;
610     case FormRole:
611         return ATK_ROLE_FORM;
612     case CanvasRole:
613         return ATK_ROLE_CANVAS;
614     case HorizontalRuleRole:
615         return ATK_ROLE_SEPARATOR;
616     case SpinButtonRole:
617         return ATK_ROLE_SPIN_BUTTON;
618     case TabRole:
619         return ATK_ROLE_PAGE_TAB;
620     default:
621         return ATK_ROLE_UNKNOWN;
622     }
623 }
624
625 static AtkRole webkitAccessibleGetRole(AtkObject* object)
626 {
627     AccessibilityObject* coreObject = core(object);
628
629     if (!coreObject)
630         return ATK_ROLE_UNKNOWN;
631
632     // Note: Why doesn't WebCore have a password field for this
633     if (coreObject->isPasswordField())
634         return ATK_ROLE_PASSWORD_TEXT;
635
636     return atkRole(coreObject->roleValue());
637 }
638
639 static bool isTextWithCaret(AccessibilityObject* coreObject)
640 {
641     if (!coreObject || !coreObject->isAccessibilityRenderObject())
642         return false;
643
644     Document* document = coreObject->document();
645     if (!document)
646         return false;
647
648     Frame* frame = document->frame();
649     if (!frame)
650         return false;
651
652     if (!frame->settings().caretBrowsingEnabled())
653         return false;
654
655     // Check text objects and paragraphs only.
656     AtkObject* axObject = coreObject->wrapper();
657     AtkRole role = axObject ? atk_object_get_role(axObject) : ATK_ROLE_INVALID;
658     if (role != ATK_ROLE_TEXT && role != ATK_ROLE_PARAGRAPH)
659         return false;
660
661     // Finally, check whether the caret is set in the current object.
662     VisibleSelection selection = coreObject->selection();
663     if (!selection.isCaret())
664         return false;
665
666     return selectionBelongsToObject(coreObject, selection);
667 }
668
669 static void setAtkStateSetFromCoreObject(AccessibilityObject* coreObject, AtkStateSet* stateSet)
670 {
671     AccessibilityObject* parent = coreObject->parentObject();
672     bool isListBoxOption = parent && parent->isListBox();
673
674     // Please keep the state list in alphabetical order
675     if (coreObject->isChecked())
676         atk_state_set_add_state(stateSet, ATK_STATE_CHECKED);
677
678     // FIXME: isReadOnly does not seem to do the right thing for
679     // controls, so check explicitly for them. In addition, because
680     // isReadOnly is false for listBoxOptions, we need to add one
681     // more check so that we do not present them as being "editable".
682     if ((!coreObject->isReadOnly()
683         || (coreObject->isControl() && coreObject->canSetValueAttribute()))
684         && !isListBoxOption)
685         atk_state_set_add_state(stateSet, ATK_STATE_EDITABLE);
686
687     // FIXME: Put both ENABLED and SENSITIVE together here for now
688     if (coreObject->isEnabled()) {
689         atk_state_set_add_state(stateSet, ATK_STATE_ENABLED);
690         atk_state_set_add_state(stateSet, ATK_STATE_SENSITIVE);
691     }
692
693     if (coreObject->canSetExpandedAttribute())
694         atk_state_set_add_state(stateSet, ATK_STATE_EXPANDABLE);
695
696     if (coreObject->isExpanded())
697         atk_state_set_add_state(stateSet, ATK_STATE_EXPANDED);
698
699     if (coreObject->canSetFocusAttribute())
700         atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE);
701
702     if (coreObject->isFocused() || isTextWithCaret(coreObject))
703         atk_state_set_add_state(stateSet, ATK_STATE_FOCUSED);
704
705     if (coreObject->orientation() == AccessibilityOrientationHorizontal)
706         atk_state_set_add_state(stateSet, ATK_STATE_HORIZONTAL);
707     else if (coreObject->orientation() == AccessibilityOrientationVertical)
708         atk_state_set_add_state(stateSet, ATK_STATE_VERTICAL);
709
710     if (coreObject->isIndeterminate())
711         atk_state_set_add_state(stateSet, ATK_STATE_INDETERMINATE);
712
713     if (coreObject->isMultiSelectable())
714         atk_state_set_add_state(stateSet, ATK_STATE_MULTISELECTABLE);
715
716     // TODO: ATK_STATE_OPAQUE
717
718     if (coreObject->isPressed())
719         atk_state_set_add_state(stateSet, ATK_STATE_PRESSED);
720
721     if (coreObject->isRequired())
722         atk_state_set_add_state(stateSet, ATK_STATE_REQUIRED);
723
724     // TODO: ATK_STATE_SELECTABLE_TEXT
725
726     if (coreObject->canSetSelectedAttribute()) {
727         atk_state_set_add_state(stateSet, ATK_STATE_SELECTABLE);
728         // Items in focusable lists have both STATE_SELECT{ABLE,ED}
729         // and STATE_FOCUS{ABLE,ED}. We'll fake the latter based on
730         // the former.
731         if (isListBoxOption)
732             atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE);
733     }
734
735     if (coreObject->isSelected()) {
736         atk_state_set_add_state(stateSet, ATK_STATE_SELECTED);
737         // Items in focusable lists have both STATE_SELECT{ABLE,ED}
738         // and STATE_FOCUS{ABLE,ED}. We'll fake the latter based on the
739         // former.
740         if (isListBoxOption)
741             atk_state_set_add_state(stateSet, ATK_STATE_FOCUSED);
742     }
743
744     // FIXME: Group both SHOWING and VISIBLE here for now
745     // Not sure how to handle this in WebKit, see bug
746     // http://bugzilla.gnome.org/show_bug.cgi?id=509650 for other
747     // issues with SHOWING vs VISIBLE.
748     if (!coreObject->isOffScreen()) {
749         atk_state_set_add_state(stateSet, ATK_STATE_SHOWING);
750         atk_state_set_add_state(stateSet, ATK_STATE_VISIBLE);
751     }
752
753     // Mutually exclusive, so we group these two
754     if (coreObject->roleValue() == TextFieldRole)
755         atk_state_set_add_state(stateSet, ATK_STATE_SINGLE_LINE);
756     else if (coreObject->roleValue() == TextAreaRole)
757         atk_state_set_add_state(stateSet, ATK_STATE_MULTI_LINE);
758
759     // TODO: ATK_STATE_SENSITIVE
760
761     if (coreObject->isVisited())
762         atk_state_set_add_state(stateSet, ATK_STATE_VISITED);
763 }
764
765 static AtkStateSet* webkitAccessibleRefStateSet(AtkObject* object)
766 {
767     AtkStateSet* stateSet = ATK_OBJECT_CLASS(webkitAccessibleParentClass)->ref_state_set(object);
768     AccessibilityObject* coreObject = core(object);
769
770     if (coreObject == fallbackObject()) {
771         atk_state_set_add_state(stateSet, ATK_STATE_DEFUNCT);
772         return stateSet;
773     }
774
775     // Text objects must be focusable.
776     AtkRole role = atk_object_get_role(object);
777     if (role == ATK_ROLE_TEXT || role == ATK_ROLE_PARAGRAPH)
778         atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE);
779
780     setAtkStateSetFromCoreObject(coreObject, stateSet);
781     return stateSet;
782 }
783
784 static AtkRelationSet* webkitAccessibleRefRelationSet(AtkObject* object)
785 {
786     AtkRelationSet* relationSet = ATK_OBJECT_CLASS(webkitAccessibleParentClass)->ref_relation_set(object);
787     AccessibilityObject* coreObject = core(object);
788
789     setAtkRelationSetFromCoreObject(coreObject, relationSet);
790
791     return relationSet;
792 }
793
794 static void webkitAccessibleInit(AtkObject* object, gpointer data)
795 {
796     if (ATK_OBJECT_CLASS(webkitAccessibleParentClass)->initialize)
797         ATK_OBJECT_CLASS(webkitAccessibleParentClass)->initialize(object, data);
798
799     WebKitAccessible* accessible = WEBKIT_ACCESSIBLE(object);
800     accessible->m_object = reinterpret_cast<AccessibilityObject*>(data);
801     accessible->priv = WEBKIT_ACCESSIBLE_GET_PRIVATE(accessible);
802 }
803
804 static const gchar* webkitAccessibleGetObjectLocale(AtkObject* object)
805 {
806     if (ATK_IS_DOCUMENT(object)) {
807         AccessibilityObject* coreObject = core(object);
808         if (!coreObject)
809             return 0;
810
811         // TODO: Should we fall back on lang xml:lang when the following comes up empty?
812         String language = coreObject->language();
813         if (!language.isEmpty())
814             return cacheAndReturnAtkProperty(object, AtkCachedDocumentLocale, language);
815
816     } else if (ATK_IS_TEXT(object)) {
817         const gchar* locale = 0;
818
819         AtkAttributeSet* textAttributes = atk_text_get_default_attributes(ATK_TEXT(object));
820         for (AtkAttributeSet* attributes = textAttributes; attributes; attributes = attributes->next) {
821             AtkAttribute* atkAttribute = static_cast<AtkAttribute*>(attributes->data);
822             if (!strcmp(atkAttribute->name, atk_text_attribute_get_name(ATK_TEXT_ATTR_LANGUAGE))) {
823                 locale = cacheAndReturnAtkProperty(object, AtkCachedDocumentLocale, String::fromUTF8(atkAttribute->value));
824                 break;
825             }
826         }
827         atk_attribute_set_free(textAttributes);
828
829         return locale;
830     }
831
832     return 0;
833 }
834
835 static void webkitAccessibleFinalize(GObject* object)
836 {
837     G_OBJECT_CLASS(webkitAccessibleParentClass)->finalize(object);
838 }
839
840 static void webkitAccessibleClassInit(AtkObjectClass* klass)
841 {
842     GObjectClass* gobjectClass = G_OBJECT_CLASS(klass);
843
844     webkitAccessibleParentClass = g_type_class_peek_parent(klass);
845
846     gobjectClass->finalize = webkitAccessibleFinalize;
847
848     klass->initialize = webkitAccessibleInit;
849     klass->get_name = webkitAccessibleGetName;
850     klass->get_description = webkitAccessibleGetDescription;
851     klass->get_parent = webkitAccessibleGetParent;
852     klass->get_n_children = webkitAccessibleGetNChildren;
853     klass->ref_child = webkitAccessibleRefChild;
854     klass->get_role = webkitAccessibleGetRole;
855     klass->ref_state_set = webkitAccessibleRefStateSet;
856     klass->get_index_in_parent = webkitAccessibleGetIndexInParent;
857     klass->get_attributes = webkitAccessibleGetAttributes;
858     klass->ref_relation_set = webkitAccessibleRefRelationSet;
859     klass->get_object_locale = webkitAccessibleGetObjectLocale;
860
861     g_type_class_add_private(klass, sizeof(WebKitAccessiblePrivate));
862 }
863
864 GType
865 webkitAccessibleGetType(void)
866 {
867     static volatile gsize typeVolatile = 0;
868
869     if (g_once_init_enter(&typeVolatile)) {
870         static const GTypeInfo tinfo = {
871             sizeof(WebKitAccessibleClass),
872             (GBaseInitFunc) 0,
873             (GBaseFinalizeFunc) 0,
874             (GClassInitFunc) webkitAccessibleClassInit,
875             (GClassFinalizeFunc) 0,
876             0, /* class data */
877             sizeof(WebKitAccessible), /* instance size */
878             0, /* nb preallocs */
879             (GInstanceInitFunc) 0,
880             0 /* value table */
881         };
882
883         GType type = g_type_register_static(ATK_TYPE_OBJECT, "WebKitAccessible", &tinfo, GTypeFlags(0));
884         g_once_init_leave(&typeVolatile, type);
885     }
886
887     return typeVolatile;
888 }
889
890 static const GInterfaceInfo AtkInterfacesInitFunctions[] = {
891     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleActionInterfaceInit), 0, 0},
892     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleSelectionInterfaceInit), 0, 0},
893     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleEditableTextInterfaceInit), 0, 0},
894     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleTextInterfaceInit), 0, 0},
895     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleComponentInterfaceInit), 0, 0},
896     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleImageInterfaceInit), 0, 0},
897     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleTableInterfaceInit), 0, 0},
898     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleHypertextInterfaceInit), 0, 0},
899     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleHyperlinkImplInterfaceInit), 0, 0},
900     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleDocumentInterfaceInit), 0, 0},
901     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleValueInterfaceInit), 0, 0}
902 };
903
904 enum WAIType {
905     WAI_ACTION,
906     WAI_SELECTION,
907     WAI_EDITABLE_TEXT,
908     WAI_TEXT,
909     WAI_COMPONENT,
910     WAI_IMAGE,
911     WAI_TABLE,
912     WAI_HYPERTEXT,
913     WAI_HYPERLINK,
914     WAI_DOCUMENT,
915     WAI_VALUE,
916 };
917
918 static GType GetAtkInterfaceTypeFromWAIType(WAIType type)
919 {
920     switch (type) {
921     case WAI_ACTION:
922         return ATK_TYPE_ACTION;
923     case WAI_SELECTION:
924         return ATK_TYPE_SELECTION;
925     case WAI_EDITABLE_TEXT:
926         return ATK_TYPE_EDITABLE_TEXT;
927     case WAI_TEXT:
928         return ATK_TYPE_TEXT;
929     case WAI_COMPONENT:
930         return ATK_TYPE_COMPONENT;
931     case WAI_IMAGE:
932         return ATK_TYPE_IMAGE;
933     case WAI_TABLE:
934         return ATK_TYPE_TABLE;
935     case WAI_HYPERTEXT:
936         return ATK_TYPE_HYPERTEXT;
937     case WAI_HYPERLINK:
938         return ATK_TYPE_HYPERLINK_IMPL;
939     case WAI_DOCUMENT:
940         return ATK_TYPE_DOCUMENT;
941     case WAI_VALUE:
942         return ATK_TYPE_VALUE;
943     }
944
945     return G_TYPE_INVALID;
946 }
947
948 static bool roleIsTextType(AccessibilityRole role)
949 {
950     return role == ParagraphRole || role == HeadingRole || role == DivRole || role == CellRole || role == ListItemRole;
951 }
952
953 static guint16 getInterfaceMaskFromObject(AccessibilityObject* coreObject)
954 {
955     guint16 interfaceMask = 0;
956
957     // Component interface is always supported
958     interfaceMask |= 1 << WAI_COMPONENT;
959
960     AccessibilityRole role = coreObject->roleValue();
961
962     // Action
963     // As the implementation of the AtkAction interface is a very
964     // basic one (just relays in executing the default action for each
965     // object, and only supports having one action per object), it is
966     // better just to implement this interface for every instance of
967     // the WebKitAccessible class and let WebCore decide what to do.
968     interfaceMask |= 1 << WAI_ACTION;
969
970     // Selection
971     if (coreObject->isListBox() || coreObject->isMenuList())
972         interfaceMask |= 1 << WAI_SELECTION;
973
974     // Get renderer if available.
975     RenderObject* renderer = 0;
976     if (coreObject->isAccessibilityRenderObject())
977         renderer = coreObject->renderer();
978
979     // Hyperlink (links and embedded objects).
980     if (coreObject->isLink() || (renderer && renderer->isReplaced()))
981         interfaceMask |= 1 << WAI_HYPERLINK;
982
983     // Text & Editable Text
984     if (role == StaticTextRole || coreObject->isMenuListOption())
985         interfaceMask |= 1 << WAI_TEXT;
986     else {
987         if (coreObject->isTextControl()) {
988             interfaceMask |= 1 << WAI_TEXT;
989             if (!coreObject->isReadOnly())
990                 interfaceMask |= 1 << WAI_EDITABLE_TEXT;
991         } else {
992             if (role != TableRole) {
993                 interfaceMask |= 1 << WAI_HYPERTEXT;
994                 if ((renderer && renderer->childrenInline()) || roleIsTextType(role))
995                     interfaceMask |= 1 << WAI_TEXT;
996             }
997
998             // Add the TEXT interface for list items whose
999             // first accessible child has a text renderer
1000             if (role == ListItemRole) {
1001                 AccessibilityObject::AccessibilityChildrenVector children = coreObject->children();
1002                 if (children.size()) {
1003                     AccessibilityObject* axRenderChild = children.at(0).get();
1004                     interfaceMask |= getInterfaceMaskFromObject(axRenderChild);
1005                 }
1006             }
1007         }
1008     }
1009
1010     // Image
1011     if (coreObject->isImage())
1012         interfaceMask |= 1 << WAI_IMAGE;
1013
1014     // Table
1015     if (role == TableRole)
1016         interfaceMask |= 1 << WAI_TABLE;
1017
1018     // Document
1019     if (role == WebAreaRole)
1020         interfaceMask |= 1 << WAI_DOCUMENT;
1021
1022     // Value
1023     if (role == SliderRole || role == SpinButtonRole || role == ScrollBarRole)
1024         interfaceMask |= 1 << WAI_VALUE;
1025
1026     return interfaceMask;
1027 }
1028
1029 static const char* getUniqueAccessibilityTypeName(guint16 interfaceMask)
1030 {
1031 #define WAI_TYPE_NAME_LEN (30) /* Enough for prefix + 5 hex characters (max) */
1032     static char name[WAI_TYPE_NAME_LEN + 1];
1033
1034     g_sprintf(name, "WAIType%x", interfaceMask);
1035     name[WAI_TYPE_NAME_LEN] = '\0';
1036
1037     return name;
1038 }
1039
1040 static GType getAccessibilityTypeFromObject(AccessibilityObject* coreObject)
1041 {
1042     static const GTypeInfo typeInfo = {
1043         sizeof(WebKitAccessibleClass),
1044         (GBaseInitFunc) 0,
1045         (GBaseFinalizeFunc) 0,
1046         (GClassInitFunc) 0,
1047         (GClassFinalizeFunc) 0,
1048         0, /* class data */
1049         sizeof(WebKitAccessible), /* instance size */
1050         0, /* nb preallocs */
1051         (GInstanceInitFunc) 0,
1052         0 /* value table */
1053     };
1054
1055     guint16 interfaceMask = getInterfaceMaskFromObject(coreObject);
1056     const char* atkTypeName = getUniqueAccessibilityTypeName(interfaceMask);
1057     GType type = g_type_from_name(atkTypeName);
1058     if (type)
1059         return type;
1060
1061     type = g_type_register_static(WEBKIT_TYPE_ACCESSIBLE, atkTypeName, &typeInfo, GTypeFlags(0));
1062     for (guint i = 0; i < G_N_ELEMENTS(AtkInterfacesInitFunctions); i++) {
1063         if (interfaceMask & (1 << i))
1064             g_type_add_interface_static(type,
1065                 GetAtkInterfaceTypeFromWAIType(static_cast<WAIType>(i)),
1066                 &AtkInterfacesInitFunctions[i]);
1067     }
1068
1069     return type;
1070 }
1071
1072 WebKitAccessible* webkitAccessibleNew(AccessibilityObject* coreObject)
1073 {
1074     GType type = getAccessibilityTypeFromObject(coreObject);
1075     AtkObject* object = static_cast<AtkObject*>(g_object_new(type, 0));
1076
1077     atk_object_initialize(object, coreObject);
1078
1079     return WEBKIT_ACCESSIBLE(object);
1080 }
1081
1082 AccessibilityObject* webkitAccessibleGetAccessibilityObject(WebKitAccessible* accessible)
1083 {
1084     return accessible->m_object;
1085 }
1086
1087 void webkitAccessibleDetach(WebKitAccessible* accessible)
1088 {
1089     ASSERT(accessible->m_object);
1090
1091     if (core(accessible)->roleValue() == WebAreaRole)
1092         g_signal_emit_by_name(accessible, "state-change", "defunct", true);
1093
1094     // We replace the WebCore AccessibilityObject with a fallback object that
1095     // provides default implementations to avoid repetitive null-checking after
1096     // detachment.
1097     accessible->m_object = fallbackObject();
1098 }
1099
1100 AtkObject* webkitAccessibleGetFocusedElement(WebKitAccessible* accessible)
1101 {
1102     if (!accessible->m_object)
1103         return 0;
1104
1105     RefPtr<AccessibilityObject> focusedObj = accessible->m_object->focusedUIElement();
1106     if (!focusedObj)
1107         return 0;
1108
1109     return focusedObj->wrapper();
1110 }
1111
1112 AccessibilityObject* objectFocusedAndCaretOffsetUnignored(AccessibilityObject* referenceObject, int& offset)
1113 {
1114     // Indication that something bogus has transpired.
1115     offset = -1;
1116
1117     Document* document = referenceObject->document();
1118     if (!document)
1119         return 0;
1120
1121     Node* focusedNode = referenceObject->selection().end().containerNode();
1122     if (!focusedNode)
1123         return 0;
1124
1125     RenderObject* focusedRenderer = focusedNode->renderer();
1126     if (!focusedRenderer)
1127         return 0;
1128
1129     AccessibilityObject* focusedObject = document->axObjectCache()->getOrCreate(focusedRenderer);
1130     if (!focusedObject)
1131         return 0;
1132
1133     // Look for the actual (not ignoring accessibility) selected object.
1134     AccessibilityObject* firstUnignoredParent = focusedObject;
1135     if (firstUnignoredParent->accessibilityIsIgnored())
1136         firstUnignoredParent = firstUnignoredParent->parentObjectUnignored();
1137     if (!firstUnignoredParent)
1138         return 0;
1139
1140     // Don't ignore links if the offset is being requested for a link.
1141     if (!referenceObject->isLink() && firstUnignoredParent->isLink())
1142         firstUnignoredParent = firstUnignoredParent->parentObjectUnignored();
1143     if (!firstUnignoredParent)
1144         return 0;
1145
1146     // The reference object must either coincide with the focused
1147     // object being considered, or be a descendant of it.
1148     if (referenceObject->isDescendantOfObject(firstUnignoredParent))
1149         referenceObject = firstUnignoredParent;
1150
1151     Node* startNode = 0;
1152     if (firstUnignoredParent != referenceObject || firstUnignoredParent->isTextControl()) {
1153         // We need to use the first child's node of the reference
1154         // object as the start point to calculate the caret offset
1155         // because we want it to be relative to the object of
1156         // reference, not just to the focused object (which could have
1157         // previous siblings which should be taken into account too).
1158         AccessibilityObject* axFirstChild = referenceObject->firstChild();
1159         if (axFirstChild)
1160             startNode = axFirstChild->node();
1161     }
1162     // Getting the Position of a PseudoElement now triggers an assertion.
1163     // This can occur when clicking on empty space in a render block.
1164     if (!startNode || startNode->isPseudoElement())
1165         startNode = firstUnignoredParent->node();
1166
1167     // Check if the node for the first parent object not ignoring
1168     // accessibility is null again before using it. This might happen
1169     // with certain kind of accessibility objects, such as the root
1170     // one (the scroller containing the webArea object).
1171     if (!startNode)
1172         return 0;
1173
1174     VisiblePosition startPosition = VisiblePosition(positionBeforeNode(startNode), DOWNSTREAM);
1175     VisiblePosition endPosition = firstUnignoredParent->selection().visibleEnd();
1176
1177     if (startPosition == endPosition)
1178         offset = 0;
1179     else if (!isStartOfLine(endPosition)) {
1180         RefPtr<Range> range = makeRange(startPosition, endPosition.previous());
1181         offset = TextIterator::rangeLength(range.get(), true) + 1;
1182     } else {
1183         RefPtr<Range> range = makeRange(startPosition, endPosition);
1184         offset = TextIterator::rangeLength(range.get(), true);
1185     }
1186
1187     return firstUnignoredParent;
1188 }
1189
1190 const char* cacheAndReturnAtkProperty(AtkObject* object, AtkCachedProperty property, String value)
1191 {
1192     WebKitAccessiblePrivate* priv = WEBKIT_ACCESSIBLE(object)->priv;
1193     CString* propertyPtr = 0;
1194
1195     switch (property) {
1196     case AtkCachedAccessibleName:
1197         propertyPtr = &priv->accessibleName;
1198         break;
1199
1200     case AtkCachedAccessibleDescription:
1201         propertyPtr = &priv->accessibleDescription;
1202         break;
1203
1204     case AtkCachedActionName:
1205         propertyPtr = &priv->actionName;
1206         break;
1207
1208     case AtkCachedActionKeyBinding:
1209         propertyPtr = &priv->actionKeyBinding;
1210         break;
1211
1212     case AtkCachedDocumentLocale:
1213         propertyPtr = &priv->documentLocale;
1214         break;
1215
1216     case AtkCachedDocumentType:
1217         propertyPtr = &priv->documentType;
1218         break;
1219
1220     case AtkCachedDocumentEncoding:
1221         propertyPtr = &priv->documentEncoding;
1222         break;
1223
1224     case AtkCachedDocumentURI:
1225         propertyPtr = &priv->documentURI;
1226         break;
1227
1228     case AtkCachedImageDescription:
1229         propertyPtr = &priv->imageDescription;
1230         break;
1231
1232     default:
1233         ASSERT_NOT_REACHED();
1234     }
1235
1236     // Don't invalidate old memory if not stricly needed, since other
1237     // callers might be still holding on to it.
1238     if (*propertyPtr != value.utf8())
1239         *propertyPtr = value.utf8();
1240
1241     return (*propertyPtr).data();
1242 }
1243
1244 #endif // HAVE(ACCESSIBILITY)