• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 Nuanti Ltd.
3  * Copyright (C) 2009 Igalia S.L.
4  * Copyright (C) 2009 Jan Alonzo
5  *
6  * Portions from Mozilla a11y, copyright as follows:
7  *
8  * The Original Code is mozilla.org code.
9  *
10  * The Initial Developer of the Original Code is
11  * Sun Microsystems, Inc.
12  * Portions created by the Initial Developer are Copyright (C) 2002
13  * the Initial Developer. All Rights Reserved.
14  *
15  * This library is free software; you can redistribute it and/or
16  * modify it under the terms of the GNU Library General Public
17  * License as published by the Free Software Foundation; either
18  * version 2 of the License, or (at your option) any later version.
19  *
20  * This library is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23  * Library General Public License for more details.
24  *
25  * You should have received a copy of the GNU Library General Public License
26  * along with this library; see the file COPYING.LIB.  If not, write to
27  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
28  * Boston, MA 02110-1301, USA.
29  */
30 
31 #include "config.h"
32 #include "AccessibilityObjectWrapperAtk.h"
33 
34 #if HAVE(ACCESSIBILITY)
35 
36 #include "AXObjectCache.h"
37 #include "AccessibilityList.h"
38 #include "AccessibilityListBox.h"
39 #include "AccessibilityListBoxOption.h"
40 #include "AccessibilityTable.h"
41 #include "AccessibilityTableCell.h"
42 #include "AccessibilityTableColumn.h"
43 #include "AccessibilityTableRow.h"
44 #include "CharacterNames.h"
45 #include "Document.h"
46 #include "DocumentType.h"
47 #include "Editor.h"
48 #include "Frame.h"
49 #include "FrameView.h"
50 #include "GOwnPtr.h"
51 #include "HostWindow.h"
52 #include "HTMLNames.h"
53 #include "HTMLTableCaptionElement.h"
54 #include "HTMLTableElement.h"
55 #include "InlineTextBox.h"
56 #include "IntRect.h"
57 #include "NotImplemented.h"
58 #include "RenderListItem.h"
59 #include "RenderListMarker.h"
60 #include "RenderText.h"
61 #include "SelectElement.h"
62 #include "Settings.h"
63 #include "TextEncoding.h"
64 #include "TextIterator.h"
65 #include "WebKitAccessibleHyperlink.h"
66 #include "htmlediting.h"
67 #include "visible_units.h"
68 
69 #include <atk/atk.h>
70 #include <glib.h>
71 #include <glib/gprintf.h>
72 #include <libgail-util/gail-util.h>
73 #include <pango/pango.h>
74 #include <wtf/text/AtomicString.h>
75 #include <wtf/text/CString.h>
76 
77 using namespace WebCore;
78 
fallbackObject()79 static AccessibilityObject* fallbackObject()
80 {
81     // FIXME: An AXObjectCache with a Document is meaningless.
82     static AXObjectCache* fallbackCache = new AXObjectCache(0);
83     static AccessibilityObject* object = 0;
84     if (!object) {
85         // FIXME: using fallbackCache->getOrCreate(ListBoxOptionRole) is a hack
86         object = fallbackCache->getOrCreate(ListBoxOptionRole);
87         object->ref();
88     }
89 
90     return object;
91 }
92 
93 // Used to provide const char* returns.
returnString(const String & str)94 static const char* returnString(const String& str)
95 {
96     static CString returnedString;
97     returnedString = str.utf8();
98     return returnedString.data();
99 }
100 
core(WebKitAccessible * accessible)101 static AccessibilityObject* core(WebKitAccessible* accessible)
102 {
103     if (!accessible)
104         return 0;
105 
106     return accessible->m_object;
107 }
108 
core(AtkObject * object)109 static AccessibilityObject* core(AtkObject* object)
110 {
111     if (!WEBKIT_IS_ACCESSIBLE(object))
112         return 0;
113 
114     return core(WEBKIT_ACCESSIBLE(object));
115 }
116 
core(AtkAction * action)117 static AccessibilityObject* core(AtkAction* action)
118 {
119     return core(ATK_OBJECT(action));
120 }
121 
core(AtkSelection * selection)122 static AccessibilityObject* core(AtkSelection* selection)
123 {
124     return core(ATK_OBJECT(selection));
125 }
126 
core(AtkText * text)127 static AccessibilityObject* core(AtkText* text)
128 {
129     return core(ATK_OBJECT(text));
130 }
131 
core(AtkEditableText * text)132 static AccessibilityObject* core(AtkEditableText* text)
133 {
134     return core(ATK_OBJECT(text));
135 }
136 
core(AtkComponent * component)137 static AccessibilityObject* core(AtkComponent* component)
138 {
139     return core(ATK_OBJECT(component));
140 }
141 
core(AtkImage * image)142 static AccessibilityObject* core(AtkImage* image)
143 {
144     return core(ATK_OBJECT(image));
145 }
146 
core(AtkTable * table)147 static AccessibilityObject* core(AtkTable* table)
148 {
149     return core(ATK_OBJECT(table));
150 }
151 
core(AtkHypertext * hypertext)152 static AccessibilityObject* core(AtkHypertext* hypertext)
153 {
154     return core(ATK_OBJECT(hypertext));
155 }
156 
core(AtkDocument * document)157 static AccessibilityObject* core(AtkDocument* document)
158 {
159     return core(ATK_OBJECT(document));
160 }
161 
core(AtkValue * value)162 static AccessibilityObject* core(AtkValue* value)
163 {
164     return core(ATK_OBJECT(value));
165 }
166 
167 static gchar* webkit_accessible_text_get_text(AtkText* text, gint startOffset, gint endOffset);
168 
webkit_accessible_get_name(AtkObject * object)169 static const gchar* webkit_accessible_get_name(AtkObject* object)
170 {
171     AccessibilityObject* coreObject = core(object);
172     if (!coreObject->isAccessibilityRenderObject())
173         return returnString(coreObject->stringValue());
174 
175     if (coreObject->isControl()) {
176         AccessibilityObject* label = coreObject->correspondingLabelForControlElement();
177         if (label) {
178             AtkObject* atkObject = label->wrapper();
179             if (ATK_IS_TEXT(atkObject))
180                 return webkit_accessible_text_get_text(ATK_TEXT(atkObject), 0, -1);
181         }
182 
183         // Try text under the node.
184         String textUnder = coreObject->textUnderElement();
185         if (textUnder.length())
186             return returnString(textUnder);
187     }
188 
189     if (coreObject->isImage() || coreObject->isInputImage()) {
190         Node* node = coreObject->node();
191         if (node && node->isHTMLElement()) {
192             // Get the attribute rather than altText String so as not to fall back on title.
193             String alt = toHTMLElement(node)->getAttribute(HTMLNames::altAttr);
194             if (!alt.isEmpty())
195                 return returnString(alt);
196         }
197     }
198 
199     // Fallback for the webArea object: just return the document's title.
200     if (coreObject->isWebArea()) {
201         Document* document = coreObject->document();
202         if (document)
203             return returnString(document->title());
204     }
205 
206     return returnString(coreObject->stringValue());
207 }
208 
webkit_accessible_get_description(AtkObject * object)209 static const gchar* webkit_accessible_get_description(AtkObject* object)
210 {
211     AccessibilityObject* coreObject = core(object);
212     Node* node = 0;
213     if (coreObject->isAccessibilityRenderObject())
214         node = coreObject->node();
215     if (!node || !node->isHTMLElement() || coreObject->ariaRoleAttribute() != UnknownRole)
216         return returnString(coreObject->accessibilityDescription());
217 
218     // atk_table_get_summary returns an AtkObject. We have no summary object, so expose summary here.
219     if (coreObject->roleValue() == TableRole) {
220         String summary = static_cast<HTMLTableElement*>(node)->summary();
221         if (!summary.isEmpty())
222             return returnString(summary);
223     }
224 
225     // The title attribute should be reliably available as the object's descripton.
226     // We do not want to fall back on other attributes in its absence. See bug 25524.
227     String title = toHTMLElement(node)->title();
228     if (!title.isEmpty())
229         return returnString(title);
230 
231     return returnString(coreObject->accessibilityDescription());
232 }
233 
setAtkRelationSetFromCoreObject(AccessibilityObject * coreObject,AtkRelationSet * relationSet)234 static void setAtkRelationSetFromCoreObject(AccessibilityObject* coreObject, AtkRelationSet* relationSet)
235 {
236     if (coreObject->isControl()) {
237         AccessibilityObject* label = coreObject->correspondingLabelForControlElement();
238         if (label)
239             atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABELLED_BY, label->wrapper());
240     } else {
241         AccessibilityObject* control = coreObject->correspondingControlForLabelElement();
242         if (control)
243             atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABEL_FOR, control->wrapper());
244     }
245 }
246 
247 static gpointer webkit_accessible_parent_class = 0;
248 
isRootObject(AccessibilityObject * coreObject)249 static bool isRootObject(AccessibilityObject* coreObject)
250 {
251     // The root accessible object in WebCore is always an object with
252     // the ScrolledArea role with one child with the WebArea role.
253     if (!coreObject || !coreObject->isScrollView())
254         return false;
255 
256     AccessibilityObject* firstChild = coreObject->firstChild();
257     if (!firstChild || !firstChild->isWebArea())
258         return false;
259 
260     return true;
261 }
262 
atkParentOfRootObject(AtkObject * object)263 static AtkObject* atkParentOfRootObject(AtkObject* object)
264 {
265     AccessibilityObject* coreObject = core(object);
266     AccessibilityObject* coreParent = coreObject->parentObjectUnignored();
267 
268     // The top level object claims to not have a parent. This makes it
269     // impossible for assistive technologies to ascend the accessible
270     // hierarchy all the way to the application. (Bug 30489)
271     if (!coreParent && isRootObject(coreObject)) {
272         Document* document = coreObject->document();
273         if (!document)
274             return 0;
275 
276         HostWindow* hostWindow = document->view()->hostWindow();
277         if (hostWindow) {
278             PlatformPageClient scrollView = hostWindow->platformPageClient();
279             if (scrollView) {
280                 GtkWidget* scrollViewParent = gtk_widget_get_parent(scrollView);
281                 if (scrollViewParent)
282                     return gtk_widget_get_accessible(scrollViewParent);
283             }
284         }
285     }
286 
287     if (!coreParent)
288         return 0;
289 
290     return coreParent->wrapper();
291 }
292 
webkit_accessible_get_parent(AtkObject * object)293 static AtkObject* webkit_accessible_get_parent(AtkObject* object)
294 {
295     AccessibilityObject* coreObject = core(object);
296     AccessibilityObject* coreParent = coreObject->parentObjectUnignored();
297     if (!coreParent && isRootObject(coreObject))
298         return atkParentOfRootObject(object);
299 
300     if (!coreParent)
301         return 0;
302 
303     return coreParent->wrapper();
304 }
305 
webkit_accessible_get_n_children(AtkObject * object)306 static gint webkit_accessible_get_n_children(AtkObject* object)
307 {
308     return core(object)->children().size();
309 }
310 
webkit_accessible_ref_child(AtkObject * object,gint index)311 static AtkObject* webkit_accessible_ref_child(AtkObject* object, gint index)
312 {
313     AccessibilityObject* coreObject = core(object);
314     AccessibilityObject::AccessibilityChildrenVector children = coreObject->children();
315     if (index < 0 || static_cast<unsigned>(index) >= children.size())
316         return 0;
317 
318     AccessibilityObject* coreChild = children.at(index).get();
319 
320     if (!coreChild)
321         return 0;
322 
323     AtkObject* child = coreChild->wrapper();
324     atk_object_set_parent(child, object);
325     g_object_ref(child);
326 
327     return child;
328 }
329 
webkit_accessible_get_index_in_parent(AtkObject * object)330 static gint webkit_accessible_get_index_in_parent(AtkObject* object)
331 {
332     AccessibilityObject* coreObject = core(object);
333     AccessibilityObject* parent = coreObject->parentObjectUnignored();
334 
335     if (!parent && isRootObject(coreObject)) {
336         AtkObject* atkParent = atkParentOfRootObject(object);
337         if (!atkParent)
338             return -1;
339 
340         unsigned count = atk_object_get_n_accessible_children(atkParent);
341         for (unsigned i = 0; i < count; ++i) {
342             AtkObject* child = atk_object_ref_accessible_child(atkParent, i);
343             bool childIsObject = child == object;
344             g_object_unref(child);
345             if (childIsObject)
346                 return i;
347         }
348     }
349 
350     AccessibilityObject::AccessibilityChildrenVector children = parent->children();
351     unsigned count = children.size();
352     for (unsigned i = 0; i < count; ++i) {
353         if (children[i] == coreObject)
354             return i;
355     }
356 
357     return -1;
358 }
359 
addAttributeToSet(AtkAttributeSet * attributeSet,const char * name,const char * value)360 static AtkAttributeSet* addAttributeToSet(AtkAttributeSet* attributeSet, const char* name, const char* value)
361 {
362     AtkAttribute* attribute = static_cast<AtkAttribute*>(g_malloc(sizeof(AtkAttribute)));
363     attribute->name = g_strdup(name);
364     attribute->value = g_strdup(value);
365     attributeSet = g_slist_prepend(attributeSet, attribute);
366 
367     return attributeSet;
368 }
369 
webkit_accessible_get_attributes(AtkObject * object)370 static AtkAttributeSet* webkit_accessible_get_attributes(AtkObject* object)
371 {
372     AtkAttributeSet* attributeSet = 0;
373     attributeSet = addAttributeToSet(attributeSet, "toolkit", "WebKitGtk");
374 
375     AccessibilityObject* coreObject = core(object);
376     if (!coreObject)
377         return attributeSet;
378 
379     int headingLevel = coreObject->headingLevel();
380     if (headingLevel) {
381         String value = String::number(headingLevel);
382         attributeSet = addAttributeToSet(attributeSet, "level", value.utf8().data());
383     }
384 
385     // Set the 'layout-guess' attribute to help Assistive
386     // Technologies know when an exposed table is not data table.
387     if (coreObject->isAccessibilityTable() && !coreObject->isDataTable())
388         attributeSet = addAttributeToSet(attributeSet, "layout-guess", "true");
389 
390     return attributeSet;
391 }
392 
atkRole(AccessibilityRole role)393 static AtkRole atkRole(AccessibilityRole role)
394 {
395     switch (role) {
396     case UnknownRole:
397         return ATK_ROLE_UNKNOWN;
398     case ButtonRole:
399         return ATK_ROLE_PUSH_BUTTON;
400     case RadioButtonRole:
401         return ATK_ROLE_RADIO_BUTTON;
402     case CheckBoxRole:
403         return ATK_ROLE_CHECK_BOX;
404     case SliderRole:
405         return ATK_ROLE_SLIDER;
406     case TabGroupRole:
407         return ATK_ROLE_PAGE_TAB_LIST;
408     case TextFieldRole:
409     case TextAreaRole:
410         return ATK_ROLE_ENTRY;
411     case StaticTextRole:
412         return ATK_ROLE_TEXT;
413     case OutlineRole:
414         return ATK_ROLE_TREE;
415     case MenuBarRole:
416         return ATK_ROLE_MENU_BAR;
417     case MenuListPopupRole:
418     case MenuRole:
419         return ATK_ROLE_MENU;
420     case MenuListOptionRole:
421     case MenuItemRole:
422         return ATK_ROLE_MENU_ITEM;
423     case ColumnRole:
424         //return ATK_ROLE_TABLE_COLUMN_HEADER; // Is this right?
425         return ATK_ROLE_UNKNOWN; // Matches Mozilla
426     case RowRole:
427         //return ATK_ROLE_TABLE_ROW_HEADER; // Is this right?
428         return ATK_ROLE_LIST_ITEM; // Matches Mozilla
429     case ToolbarRole:
430         return ATK_ROLE_TOOL_BAR;
431     case BusyIndicatorRole:
432         return ATK_ROLE_PROGRESS_BAR; // Is this right?
433     case ProgressIndicatorRole:
434         //return ATK_ROLE_SPIN_BUTTON; // Some confusion about this role in AccessibilityRenderObject.cpp
435         return ATK_ROLE_PROGRESS_BAR;
436     case WindowRole:
437         return ATK_ROLE_WINDOW;
438     case PopUpButtonRole:
439     case ComboBoxRole:
440         return ATK_ROLE_COMBO_BOX;
441     case SplitGroupRole:
442         return ATK_ROLE_SPLIT_PANE;
443     case SplitterRole:
444         return ATK_ROLE_SEPARATOR;
445     case ColorWellRole:
446         return ATK_ROLE_COLOR_CHOOSER;
447     case ListRole:
448         return ATK_ROLE_LIST;
449     case ScrollBarRole:
450         return ATK_ROLE_SCROLL_BAR;
451     case ScrollAreaRole:
452         return ATK_ROLE_SCROLL_PANE;
453     case GridRole: // Is this right?
454     case TableRole:
455         return ATK_ROLE_TABLE;
456     case ApplicationRole:
457         return ATK_ROLE_APPLICATION;
458     case GroupRole:
459     case RadioGroupRole:
460         return ATK_ROLE_PANEL;
461     case RowHeaderRole: // Row headers are cells after all.
462     case ColumnHeaderRole: // Column headers are cells after all.
463     case CellRole:
464         return ATK_ROLE_TABLE_CELL;
465     case LinkRole:
466     case WebCoreLinkRole:
467     case ImageMapLinkRole:
468         return ATK_ROLE_LINK;
469     case ImageMapRole:
470     case ImageRole:
471         return ATK_ROLE_IMAGE;
472     case ListMarkerRole:
473         return ATK_ROLE_TEXT;
474     case WebAreaRole:
475         //return ATK_ROLE_HTML_CONTAINER; // Is this right?
476         return ATK_ROLE_DOCUMENT_FRAME;
477     case HeadingRole:
478         return ATK_ROLE_HEADING;
479     case ListBoxRole:
480         return ATK_ROLE_LIST;
481     case ListItemRole:
482     case ListBoxOptionRole:
483         return ATK_ROLE_LIST_ITEM;
484     case ParagraphRole:
485         return ATK_ROLE_PARAGRAPH;
486     case LabelRole:
487         return ATK_ROLE_LABEL;
488     case DivRole:
489         return ATK_ROLE_SECTION;
490     case FormRole:
491         return ATK_ROLE_FORM;
492     default:
493         return ATK_ROLE_UNKNOWN;
494     }
495 }
496 
webkit_accessible_get_role(AtkObject * object)497 static AtkRole webkit_accessible_get_role(AtkObject* object)
498 {
499     AccessibilityObject* coreObject = core(object);
500 
501     if (!coreObject)
502         return ATK_ROLE_UNKNOWN;
503 
504     // Note: Why doesn't WebCore have a password field for this
505     if (coreObject->isPasswordField())
506         return ATK_ROLE_PASSWORD_TEXT;
507 
508     return atkRole(coreObject->roleValue());
509 }
510 
selectionBelongsToObject(AccessibilityObject * coreObject,VisibleSelection & selection)511 static bool selectionBelongsToObject(AccessibilityObject* coreObject, VisibleSelection& selection)
512 {
513     if (!coreObject || !coreObject->isAccessibilityRenderObject())
514         return false;
515 
516     if (selection.isNone())
517         return false;
518 
519     RefPtr<Range> range = selection.toNormalizedRange();
520     if (!range)
521         return false;
522 
523     // We want to check that both the selection intersects the node
524     // AND that the selection is not just "touching" one of the
525     // boundaries for the selected node. We want to check whether the
526     // node is actually inside the region, at least partially.
527     Node* node = coreObject->node();
528     Node* lastDescendant = node->lastDescendant();
529     ExceptionCode ec = 0;
530     return (range->intersectsNode(node, ec)
531             && (range->endContainer() != node || range->endOffset())
532             && (range->startContainer() != lastDescendant || range->startOffset() != lastOffsetInNode(lastDescendant)));
533 }
534 
isTextWithCaret(AccessibilityObject * coreObject)535 static bool isTextWithCaret(AccessibilityObject* coreObject)
536 {
537     if (!coreObject || !coreObject->isAccessibilityRenderObject())
538         return false;
539 
540     Document* document = coreObject->document();
541     if (!document)
542         return false;
543 
544     Frame* frame = document->frame();
545     if (!frame)
546         return false;
547 
548     Settings* settings = frame->settings();
549     if (!settings || !settings->caretBrowsingEnabled())
550         return false;
551 
552     // Check text objects and paragraphs only.
553     AtkObject* axObject = coreObject->wrapper();
554     AtkRole role = axObject ? atk_object_get_role(axObject) : ATK_ROLE_INVALID;
555     if (role != ATK_ROLE_TEXT && role != ATK_ROLE_PARAGRAPH)
556         return false;
557 
558     // Finally, check whether the caret is set in the current object.
559     VisibleSelection selection = coreObject->selection();
560     if (!selection.isCaret())
561         return false;
562 
563     return selectionBelongsToObject(coreObject, selection);
564 }
565 
setAtkStateSetFromCoreObject(AccessibilityObject * coreObject,AtkStateSet * stateSet)566 static void setAtkStateSetFromCoreObject(AccessibilityObject* coreObject, AtkStateSet* stateSet)
567 {
568     AccessibilityObject* parent = coreObject->parentObject();
569     bool isListBoxOption = parent && parent->isListBox();
570 
571     // Please keep the state list in alphabetical order
572     if (coreObject->isChecked())
573         atk_state_set_add_state(stateSet, ATK_STATE_CHECKED);
574 
575     // FIXME: isReadOnly does not seem to do the right thing for
576     // controls, so check explicitly for them. In addition, because
577     // isReadOnly is false for listBoxOptions, we need to add one
578     // more check so that we do not present them as being "editable".
579     if ((!coreObject->isReadOnly() ||
580         (coreObject->isControl() && coreObject->canSetValueAttribute())) &&
581         !isListBoxOption)
582         atk_state_set_add_state(stateSet, ATK_STATE_EDITABLE);
583 
584     // FIXME: Put both ENABLED and SENSITIVE together here for now
585     if (coreObject->isEnabled()) {
586         atk_state_set_add_state(stateSet, ATK_STATE_ENABLED);
587         atk_state_set_add_state(stateSet, ATK_STATE_SENSITIVE);
588     }
589 
590     if (coreObject->canSetExpandedAttribute())
591         atk_state_set_add_state(stateSet, ATK_STATE_EXPANDABLE);
592 
593     if (coreObject->isExpanded())
594         atk_state_set_add_state(stateSet, ATK_STATE_EXPANDED);
595 
596     if (coreObject->canSetFocusAttribute())
597         atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE);
598 
599     if (coreObject->isFocused() || isTextWithCaret(coreObject))
600         atk_state_set_add_state(stateSet, ATK_STATE_FOCUSED);
601 
602     // TODO: ATK_STATE_HORIZONTAL
603 
604     if (coreObject->isIndeterminate())
605         atk_state_set_add_state(stateSet, ATK_STATE_INDETERMINATE);
606 
607     if (coreObject->isMultiSelectable())
608         atk_state_set_add_state(stateSet, ATK_STATE_MULTISELECTABLE);
609 
610     // TODO: ATK_STATE_OPAQUE
611 
612     if (coreObject->isPressed())
613         atk_state_set_add_state(stateSet, ATK_STATE_PRESSED);
614 
615     // TODO: ATK_STATE_SELECTABLE_TEXT
616 
617     if (coreObject->canSetSelectedAttribute()) {
618         atk_state_set_add_state(stateSet, ATK_STATE_SELECTABLE);
619         // Items in focusable lists in Gtk have both STATE_SELECT{ABLE,ED}
620         // and STATE_FOCUS{ABLE,ED}. We'll fake the latter based on the
621         // former.
622         if (isListBoxOption)
623             atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE);
624     }
625 
626     if (coreObject->isSelected()) {
627         atk_state_set_add_state(stateSet, ATK_STATE_SELECTED);
628         // Items in focusable lists in Gtk have both STATE_SELECT{ABLE,ED}
629         // and STATE_FOCUS{ABLE,ED}. We'll fake the latter based on the
630         // former.
631         if (isListBoxOption)
632             atk_state_set_add_state(stateSet, ATK_STATE_FOCUSED);
633     }
634 
635     // FIXME: Group both SHOWING and VISIBLE here for now
636     // Not sure how to handle this in WebKit, see bug
637     // http://bugzilla.gnome.org/show_bug.cgi?id=509650 for other
638     // issues with SHOWING vs VISIBLE within GTK+
639     if (!coreObject->isOffScreen()) {
640         atk_state_set_add_state(stateSet, ATK_STATE_SHOWING);
641         atk_state_set_add_state(stateSet, ATK_STATE_VISIBLE);
642     }
643 
644     // Mutually exclusive, so we group these two
645     if (coreObject->roleValue() == TextFieldRole)
646         atk_state_set_add_state(stateSet, ATK_STATE_SINGLE_LINE);
647     else if (coreObject->roleValue() == TextAreaRole)
648         atk_state_set_add_state(stateSet, ATK_STATE_MULTI_LINE);
649 
650     // TODO: ATK_STATE_SENSITIVE
651 
652     // TODO: ATK_STATE_VERTICAL
653 
654     if (coreObject->isVisited())
655         atk_state_set_add_state(stateSet, ATK_STATE_VISITED);
656 }
657 
webkit_accessible_ref_state_set(AtkObject * object)658 static AtkStateSet* webkit_accessible_ref_state_set(AtkObject* object)
659 {
660     AtkStateSet* stateSet = ATK_OBJECT_CLASS(webkit_accessible_parent_class)->ref_state_set(object);
661     AccessibilityObject* coreObject = core(object);
662 
663     if (coreObject == fallbackObject()) {
664         atk_state_set_add_state(stateSet, ATK_STATE_DEFUNCT);
665         return stateSet;
666     }
667 
668     // Text objects must be focusable.
669     AtkRole role = atk_object_get_role(object);
670     if (role == ATK_ROLE_TEXT || role == ATK_ROLE_PARAGRAPH)
671         atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE);
672 
673     setAtkStateSetFromCoreObject(coreObject, stateSet);
674     return stateSet;
675 }
676 
webkit_accessible_ref_relation_set(AtkObject * object)677 static AtkRelationSet* webkit_accessible_ref_relation_set(AtkObject* object)
678 {
679     AtkRelationSet* relationSet = ATK_OBJECT_CLASS(webkit_accessible_parent_class)->ref_relation_set(object);
680     AccessibilityObject* coreObject = core(object);
681 
682     setAtkRelationSetFromCoreObject(coreObject, relationSet);
683 
684     return relationSet;
685 }
686 
webkit_accessible_init(AtkObject * object,gpointer data)687 static void webkit_accessible_init(AtkObject* object, gpointer data)
688 {
689     if (ATK_OBJECT_CLASS(webkit_accessible_parent_class)->initialize)
690         ATK_OBJECT_CLASS(webkit_accessible_parent_class)->initialize(object, data);
691 
692     WEBKIT_ACCESSIBLE(object)->m_object = reinterpret_cast<AccessibilityObject*>(data);
693 }
694 
webkit_accessible_finalize(GObject * object)695 static void webkit_accessible_finalize(GObject* object)
696 {
697     // This is a good time to clear the return buffer.
698     returnString(String());
699 
700     G_OBJECT_CLASS(webkit_accessible_parent_class)->finalize(object);
701 }
702 
webkit_accessible_class_init(AtkObjectClass * klass)703 static void webkit_accessible_class_init(AtkObjectClass* klass)
704 {
705     GObjectClass* gobjectClass = G_OBJECT_CLASS(klass);
706 
707     webkit_accessible_parent_class = g_type_class_peek_parent(klass);
708 
709     gobjectClass->finalize = webkit_accessible_finalize;
710 
711     klass->initialize = webkit_accessible_init;
712     klass->get_name = webkit_accessible_get_name;
713     klass->get_description = webkit_accessible_get_description;
714     klass->get_parent = webkit_accessible_get_parent;
715     klass->get_n_children = webkit_accessible_get_n_children;
716     klass->ref_child = webkit_accessible_ref_child;
717     klass->get_role = webkit_accessible_get_role;
718     klass->ref_state_set = webkit_accessible_ref_state_set;
719     klass->get_index_in_parent = webkit_accessible_get_index_in_parent;
720     klass->get_attributes = webkit_accessible_get_attributes;
721     klass->ref_relation_set = webkit_accessible_ref_relation_set;
722 }
723 
724 GType
webkit_accessible_get_type(void)725 webkit_accessible_get_type(void)
726 {
727     static volatile gsize type_volatile = 0;
728 
729     if (g_once_init_enter(&type_volatile)) {
730         static const GTypeInfo tinfo = {
731             sizeof(WebKitAccessibleClass),
732             (GBaseInitFunc) 0,
733             (GBaseFinalizeFunc) 0,
734             (GClassInitFunc) webkit_accessible_class_init,
735             (GClassFinalizeFunc) 0,
736             0, /* class data */
737             sizeof(WebKitAccessible), /* instance size */
738             0, /* nb preallocs */
739             (GInstanceInitFunc) 0,
740             0 /* value table */
741         };
742 
743         GType type = g_type_register_static(ATK_TYPE_OBJECT,
744                                             "WebKitAccessible", &tinfo, GTypeFlags(0));
745         g_once_init_leave(&type_volatile, type);
746     }
747 
748     return type_volatile;
749 }
750 
webkit_accessible_action_do_action(AtkAction * action,gint i)751 static gboolean webkit_accessible_action_do_action(AtkAction* action, gint i)
752 {
753     g_return_val_if_fail(i == 0, FALSE);
754     return core(action)->performDefaultAction();
755 }
756 
webkit_accessible_action_get_n_actions(AtkAction * action)757 static gint webkit_accessible_action_get_n_actions(AtkAction* action)
758 {
759     return 1;
760 }
761 
webkit_accessible_action_get_description(AtkAction * action,gint i)762 static const gchar* webkit_accessible_action_get_description(AtkAction* action, gint i)
763 {
764     g_return_val_if_fail(i == 0, 0);
765     // TODO: Need a way to provide/localize action descriptions.
766     notImplemented();
767     return "";
768 }
769 
webkit_accessible_action_get_keybinding(AtkAction * action,gint i)770 static const gchar* webkit_accessible_action_get_keybinding(AtkAction* action, gint i)
771 {
772     g_return_val_if_fail(i == 0, 0);
773     // FIXME: Construct a proper keybinding string.
774     return returnString(core(action)->accessKey().string());
775 }
776 
webkit_accessible_action_get_name(AtkAction * action,gint i)777 static const gchar* webkit_accessible_action_get_name(AtkAction* action, gint i)
778 {
779     g_return_val_if_fail(i == 0, 0);
780     return returnString(core(action)->actionVerb());
781 }
782 
atk_action_interface_init(AtkActionIface * iface)783 static void atk_action_interface_init(AtkActionIface* iface)
784 {
785     iface->do_action = webkit_accessible_action_do_action;
786     iface->get_n_actions = webkit_accessible_action_get_n_actions;
787     iface->get_description = webkit_accessible_action_get_description;
788     iface->get_keybinding = webkit_accessible_action_get_keybinding;
789     iface->get_name = webkit_accessible_action_get_name;
790 }
791 
792 // Selection (for controls)
793 
listObjectForSelection(AtkSelection * selection)794 static AccessibilityObject* listObjectForSelection(AtkSelection* selection)
795 {
796     AccessibilityObject* coreSelection = core(selection);
797 
798     // Only list boxes and menu lists supported so far.
799     if (!coreSelection->isListBox() && !coreSelection->isMenuList())
800         return 0;
801 
802     // For list boxes the list object is just itself.
803     if (coreSelection->isListBox())
804         return coreSelection;
805 
806     // For menu lists we need to return the first accessible child,
807     // with role MenuListPopupRole, since that's the one holding the list
808     // of items with role MenuListOptionRole.
809     AccessibilityObject::AccessibilityChildrenVector children = coreSelection->children();
810     if (!children.size())
811         return 0;
812 
813     AccessibilityObject* listObject = children.at(0).get();
814     if (!listObject->isMenuListPopup())
815         return 0;
816 
817     return listObject;
818 }
819 
optionFromList(AtkSelection * selection,gint i)820 static AccessibilityObject* optionFromList(AtkSelection* selection, gint i)
821 {
822     AccessibilityObject* coreSelection = core(selection);
823     if (!coreSelection || i < 0)
824         return 0;
825 
826     // Need to select the proper list object depending on the type.
827     AccessibilityObject* listObject = listObjectForSelection(selection);
828     if (!listObject)
829         return 0;
830 
831     AccessibilityObject::AccessibilityChildrenVector options = listObject->children();
832     if (i < static_cast<gint>(options.size()))
833         return options.at(i).get();
834 
835     return 0;
836 }
837 
optionFromSelection(AtkSelection * selection,gint i)838 static AccessibilityObject* optionFromSelection(AtkSelection* selection, gint i)
839 {
840     // i is the ith selection as opposed to the ith child.
841 
842     AccessibilityObject* coreSelection = core(selection);
843     if (!coreSelection || !coreSelection->isAccessibilityRenderObject() || i < 0)
844         return 0;
845 
846     AccessibilityObject::AccessibilityChildrenVector selectedItems;
847     if (coreSelection->isListBox())
848         coreSelection->selectedChildren(selectedItems);
849     else if (coreSelection->isMenuList()) {
850         RenderObject* renderer = coreSelection->renderer();
851         if (!renderer)
852             return 0;
853 
854         SelectElement* selectNode = toSelectElement(static_cast<Element*>(renderer->node()));
855         int selectedIndex = selectNode->selectedIndex();
856         const Vector<Element*> listItems = selectNode->listItems();
857 
858         if (selectedIndex < 0 || selectedIndex >= static_cast<int>(listItems.size()))
859             return 0;
860 
861         return optionFromList(selection, selectedIndex);
862     }
863 
864     if (i < static_cast<gint>(selectedItems.size()))
865         return selectedItems.at(i).get();
866 
867     return 0;
868 }
869 
webkit_accessible_selection_add_selection(AtkSelection * selection,gint i)870 static gboolean webkit_accessible_selection_add_selection(AtkSelection* selection, gint i)
871 {
872     AccessibilityObject* coreSelection = core(selection);
873     if (!coreSelection)
874         return false;
875 
876     AccessibilityObject* option = optionFromList(selection, i);
877     if (option && (coreSelection->isListBox() || coreSelection->isMenuList())) {
878         option->setSelected(true);
879         return option->isSelected();
880     }
881 
882     return false;
883 }
884 
webkit_accessible_selection_clear_selection(AtkSelection * selection)885 static gboolean webkit_accessible_selection_clear_selection(AtkSelection* selection)
886 {
887     AccessibilityObject* coreSelection = core(selection);
888     if (!coreSelection)
889         return false;
890 
891     AccessibilityObject::AccessibilityChildrenVector selectedItems;
892     if (coreSelection->isListBox() || coreSelection->isMenuList()) {
893         // Set the list of selected items to an empty list; then verify that it worked.
894         AccessibilityListBox* listBox = static_cast<AccessibilityListBox*>(coreSelection);
895         listBox->setSelectedChildren(selectedItems);
896         listBox->selectedChildren(selectedItems);
897         return selectedItems.size() == 0;
898     }
899     return false;
900 }
901 
webkit_accessible_selection_ref_selection(AtkSelection * selection,gint i)902 static AtkObject* webkit_accessible_selection_ref_selection(AtkSelection* selection, gint i)
903 {
904     AccessibilityObject* option = optionFromSelection(selection, i);
905     if (option) {
906         AtkObject* child = option->wrapper();
907         g_object_ref(child);
908         return child;
909     }
910 
911     return 0;
912 }
913 
webkit_accessible_selection_get_selection_count(AtkSelection * selection)914 static gint webkit_accessible_selection_get_selection_count(AtkSelection* selection)
915 {
916     AccessibilityObject* coreSelection = core(selection);
917     if (!coreSelection || !coreSelection->isAccessibilityRenderObject())
918         return 0;
919 
920     if (coreSelection->isListBox()) {
921         AccessibilityObject::AccessibilityChildrenVector selectedItems;
922         coreSelection->selectedChildren(selectedItems);
923         return static_cast<gint>(selectedItems.size());
924     }
925 
926     if (coreSelection->isMenuList()) {
927         RenderObject* renderer = coreSelection->renderer();
928         if (!renderer)
929             return 0;
930 
931         SelectElement* selectNode = toSelectElement(static_cast<Element*>(renderer->node()));
932         int selectedIndex = selectNode->selectedIndex();
933         const Vector<Element*> listItems = selectNode->listItems();
934 
935         return selectedIndex >= 0 && selectedIndex < static_cast<int>(listItems.size());
936     }
937 
938     return 0;
939 }
940 
webkit_accessible_selection_is_child_selected(AtkSelection * selection,gint i)941 static gboolean webkit_accessible_selection_is_child_selected(AtkSelection* selection, gint i)
942 {
943     AccessibilityObject* coreSelection = core(selection);
944     if (!coreSelection)
945         return 0;
946 
947     AccessibilityObject* option = optionFromList(selection, i);
948     if (option && (coreSelection->isListBox() || coreSelection->isMenuList()))
949         return option->isSelected();
950 
951     return false;
952 }
953 
webkit_accessible_selection_remove_selection(AtkSelection * selection,gint i)954 static gboolean webkit_accessible_selection_remove_selection(AtkSelection* selection, gint i)
955 {
956     AccessibilityObject* coreSelection = core(selection);
957     if (!coreSelection)
958         return 0;
959 
960     // TODO: This is only getting called if i == 0. What is preventing the rest?
961     AccessibilityObject* option = optionFromSelection(selection, i);
962     if (option && (coreSelection->isListBox() || coreSelection->isMenuList())) {
963         option->setSelected(false);
964         return !option->isSelected();
965     }
966 
967     return false;
968 }
969 
webkit_accessible_selection_select_all_selection(AtkSelection * selection)970 static gboolean webkit_accessible_selection_select_all_selection(AtkSelection* selection)
971 {
972     AccessibilityObject* coreSelection = core(selection);
973     if (!coreSelection || !coreSelection->isMultiSelectable())
974         return false;
975 
976     AccessibilityObject::AccessibilityChildrenVector children = coreSelection->children();
977     if (coreSelection->isListBox()) {
978         AccessibilityListBox* listBox = static_cast<AccessibilityListBox*>(coreSelection);
979         listBox->setSelectedChildren(children);
980         AccessibilityObject::AccessibilityChildrenVector selectedItems;
981         listBox->selectedChildren(selectedItems);
982         return selectedItems.size() == children.size();
983     }
984 
985     return false;
986 }
987 
atk_selection_interface_init(AtkSelectionIface * iface)988 static void atk_selection_interface_init(AtkSelectionIface* iface)
989 {
990     iface->add_selection = webkit_accessible_selection_add_selection;
991     iface->clear_selection = webkit_accessible_selection_clear_selection;
992     iface->ref_selection = webkit_accessible_selection_ref_selection;
993     iface->get_selection_count = webkit_accessible_selection_get_selection_count;
994     iface->is_child_selected = webkit_accessible_selection_is_child_selected;
995     iface->remove_selection = webkit_accessible_selection_remove_selection;
996     iface->select_all_selection = webkit_accessible_selection_select_all_selection;
997 }
998 
999 // Text
1000 
utf8Substr(const gchar * string,gint start,gint end)1001 static gchar* utf8Substr(const gchar* string, gint start, gint end)
1002 {
1003     ASSERT(string);
1004     glong strLen = g_utf8_strlen(string, -1);
1005     if (start > strLen || end > strLen)
1006         return 0;
1007     gchar* startPtr = g_utf8_offset_to_pointer(string, start);
1008     gsize lenInBytes = g_utf8_offset_to_pointer(string, end + 1) -  startPtr;
1009     gchar* output = static_cast<gchar*>(g_malloc0(lenInBytes + 1));
1010     return g_utf8_strncpy(output, startPtr, end - start + 1);
1011 }
1012 
1013 // This function is not completely general, is it's tied to the
1014 // internals of WebCore's text presentation.
convertUniCharToUTF8(const UChar * characters,gint length,int from,int to)1015 static gchar* convertUniCharToUTF8(const UChar* characters, gint length, int from, int to)
1016 {
1017     CString stringUTF8 = UTF8Encoding().encode(characters, length, QuestionMarksForUnencodables);
1018     gchar* utf8String = utf8Substr(stringUTF8.data(), from, to);
1019     if (!g_utf8_validate(utf8String, -1, 0)) {
1020         g_free(utf8String);
1021         return 0;
1022     }
1023     gsize len = strlen(utf8String);
1024     GString* ret = g_string_new_len(0, len);
1025     gchar* ptr = utf8String;
1026 
1027     // WebCore introduces line breaks in the text that do not reflect
1028     // the layout you see on the screen, replace them with spaces
1029     while (len > 0) {
1030         gint index, start;
1031         pango_find_paragraph_boundary(ptr, len, &index, &start);
1032         g_string_append_len(ret, ptr, index);
1033         if (index == start)
1034             break;
1035         g_string_append_c(ret, ' ');
1036         ptr += start;
1037         len -= start;
1038     }
1039 
1040     g_free(utf8String);
1041     return g_string_free(ret, FALSE);
1042 }
1043 
textForRenderer(RenderObject * renderer)1044 gchar* textForRenderer(RenderObject* renderer)
1045 {
1046     GString* resultText = g_string_new(0);
1047 
1048     if (!renderer)
1049         return g_string_free(resultText, FALSE);
1050 
1051     // For RenderBlocks, piece together the text from the RenderText objects they contain.
1052     for (RenderObject* object = renderer->firstChild(); object; object = object->nextSibling()) {
1053         if (object->isBR()) {
1054             g_string_append(resultText, "\n");
1055             continue;
1056         }
1057 
1058         RenderText* renderText;
1059         if (object->isText())
1060             renderText = toRenderText(object);
1061         else {
1062             if (object->isReplaced())
1063                 g_string_append_unichar(resultText, objectReplacementCharacter);
1064 
1065             // We need to check children, if any, to consider when
1066             // current object is not a text object but some of its
1067             // children are, in order not to miss those portions of
1068             // text by not properly handling those situations
1069             if (object->firstChild())
1070                 g_string_append(resultText, textForRenderer(object));
1071 
1072             continue;
1073         }
1074 
1075         InlineTextBox* box = renderText ? renderText->firstTextBox() : 0;
1076         while (box) {
1077             gchar* text = convertUniCharToUTF8(renderText->characters(), renderText->textLength(), box->start(), box->end());
1078             g_string_append(resultText, text);
1079             // Newline chars in the source result in separate text boxes, so check
1080             // before adding a newline in the layout. See bug 25415 comment #78.
1081             // If the next sibling is a BR, we'll add the newline when we examine that child.
1082             if (!box->nextOnLineExists() && (!object->nextSibling() || !object->nextSibling()->isBR()))
1083                 g_string_append(resultText, "\n");
1084             box = box->nextTextBox();
1085         }
1086     }
1087 
1088     // Insert the text of the marker for list item in the right place, if present
1089     if (renderer->isListItem()) {
1090         String markerText = toRenderListItem(renderer)->markerTextWithSuffix();
1091         if (renderer->style()->direction() == LTR)
1092             g_string_prepend(resultText, markerText.utf8().data());
1093         else
1094             g_string_append(resultText, markerText.utf8().data());
1095     }
1096 
1097     return g_string_free(resultText, FALSE);
1098 }
1099 
textForObject(AccessibilityObject * coreObject)1100 gchar* textForObject(AccessibilityObject* coreObject)
1101 {
1102     GString* str = g_string_new(0);
1103 
1104     // For text controls, we can get the text line by line.
1105     if (coreObject->isTextControl()) {
1106         unsigned textLength = coreObject->textLength();
1107         int lineNumber = 0;
1108         PlainTextRange range = coreObject->doAXRangeForLine(lineNumber);
1109         while (range.length) {
1110             // When a line of text wraps in a text area, the final space is removed.
1111             if (range.start + range.length < textLength)
1112                 range.length -= 1;
1113             String lineText = coreObject->doAXStringForRange(range);
1114             g_string_append(str, lineText.utf8().data());
1115             g_string_append(str, "\n");
1116             range = coreObject->doAXRangeForLine(++lineNumber);
1117         }
1118     } else if (coreObject->isAccessibilityRenderObject()) {
1119         GOwnPtr<gchar> rendererText(textForRenderer(coreObject->renderer()));
1120         g_string_append(str, rendererText.get());
1121     }
1122 
1123     return g_string_free(str, FALSE);
1124 }
1125 
webkit_accessible_text_get_text(AtkText * text,gint startOffset,gint endOffset)1126 static gchar* webkit_accessible_text_get_text(AtkText* text, gint startOffset, gint endOffset)
1127 {
1128     AccessibilityObject* coreObject = core(text);
1129 
1130     int end = endOffset;
1131     if (endOffset == -1) {
1132         end = coreObject->stringValue().length();
1133         if (!end)
1134             end = coreObject->textUnderElement().length();
1135     }
1136 
1137     String ret;
1138     if (coreObject->isTextControl())
1139         ret = coreObject->doAXStringForRange(PlainTextRange(0, endOffset));
1140     else {
1141         ret = coreObject->stringValue();
1142         if (!ret)
1143             ret = coreObject->textUnderElement();
1144     }
1145 
1146     if (!ret.length()) {
1147         // This can happen at least with anonymous RenderBlocks (e.g. body text amongst paragraphs)
1148         ret = String(textForObject(coreObject));
1149         if (!end)
1150             end = ret.length();
1151     }
1152 
1153     // Prefix a item number/bullet if needed
1154     if (coreObject->roleValue() == ListItemRole) {
1155         RenderObject* objRenderer = coreObject->renderer();
1156         if (objRenderer && objRenderer->isListItem()) {
1157             String markerText = toRenderListItem(objRenderer)->markerTextWithSuffix();
1158             ret = objRenderer->style()->direction() == LTR ? markerText + ret : ret + markerText;
1159             if (endOffset == -1)
1160                 end += markerText.length();
1161         }
1162     }
1163 
1164     ret = ret.substring(startOffset, end - startOffset);
1165     return g_strdup(ret.utf8().data());
1166 }
1167 
getGailTextUtilForAtk(AtkText * textObject)1168 static GailTextUtil* getGailTextUtilForAtk(AtkText* textObject)
1169 {
1170     gpointer data = g_object_get_data(G_OBJECT(textObject), "webkit-accessible-gail-text-util");
1171     if (data)
1172         return static_cast<GailTextUtil*>(data);
1173 
1174     GailTextUtil* gailTextUtil = gail_text_util_new();
1175     gail_text_util_text_setup(gailTextUtil, webkit_accessible_text_get_text(textObject, 0, -1));
1176     g_object_set_data_full(G_OBJECT(textObject), "webkit-accessible-gail-text-util", gailTextUtil, g_object_unref);
1177     return gailTextUtil;
1178 }
1179 
getPangoLayoutForAtk(AtkText * textObject)1180 static PangoLayout* getPangoLayoutForAtk(AtkText* textObject)
1181 {
1182     AccessibilityObject* coreObject = core(textObject);
1183 
1184     Document* document = coreObject->document();
1185     if (!document)
1186         return 0;
1187 
1188     HostWindow* hostWindow = document->view()->hostWindow();
1189     if (!hostWindow)
1190         return 0;
1191     PlatformPageClient webView = hostWindow->platformPageClient();
1192     if (!webView)
1193         return 0;
1194 
1195     // Create a string with the layout as it appears on the screen
1196     PangoLayout* layout = gtk_widget_create_pango_layout(static_cast<GtkWidget*>(webView), textForObject(coreObject));
1197     g_object_set_data_full(G_OBJECT(textObject), "webkit-accessible-pango-layout", layout, g_object_unref);
1198     return layout;
1199 }
1200 
webkit_accessible_text_get_text_after_offset(AtkText * text,gint offset,AtkTextBoundary boundaryType,gint * startOffset,gint * endOffset)1201 static gchar* webkit_accessible_text_get_text_after_offset(AtkText* text, gint offset, AtkTextBoundary boundaryType, gint* startOffset, gint* endOffset)
1202 {
1203     return gail_text_util_get_text(getGailTextUtilForAtk(text), getPangoLayoutForAtk(text), GAIL_AFTER_OFFSET, boundaryType, offset, startOffset, endOffset);
1204 }
1205 
webkit_accessible_text_get_text_at_offset(AtkText * text,gint offset,AtkTextBoundary boundaryType,gint * startOffset,gint * endOffset)1206 static gchar* webkit_accessible_text_get_text_at_offset(AtkText* text, gint offset, AtkTextBoundary boundaryType, gint* startOffset, gint* endOffset)
1207 {
1208     return gail_text_util_get_text(getGailTextUtilForAtk(text), getPangoLayoutForAtk(text), GAIL_AT_OFFSET, boundaryType, offset, startOffset, endOffset);
1209 }
1210 
webkit_accessible_text_get_text_before_offset(AtkText * text,gint offset,AtkTextBoundary boundaryType,gint * startOffset,gint * endOffset)1211 static gchar* webkit_accessible_text_get_text_before_offset(AtkText* text, gint offset, AtkTextBoundary boundaryType, gint* startOffset, gint* endOffset)
1212 {
1213     return gail_text_util_get_text(getGailTextUtilForAtk(text), getPangoLayoutForAtk(text), GAIL_BEFORE_OFFSET, boundaryType, offset, startOffset, endOffset);
1214 }
1215 
webkit_accessible_text_get_character_at_offset(AtkText * text,gint offset)1216 static gunichar webkit_accessible_text_get_character_at_offset(AtkText* text, gint offset)
1217 {
1218     notImplemented();
1219     return 0;
1220 }
1221 
webkit_accessible_text_get_caret_offset(AtkText * text)1222 static gint webkit_accessible_text_get_caret_offset(AtkText* text)
1223 {
1224     // coreObject is the unignored object whose offset the caller is requesting.
1225     // focusedObject is the object with the caret. It is likely ignored -- unless it's a link.
1226     AccessibilityObject* coreObject = core(text);
1227     if (!coreObject->isAccessibilityRenderObject())
1228         return 0;
1229 
1230     Document* document = coreObject->document();
1231     if (!document)
1232         return 0;
1233 
1234     Node* focusedNode = coreObject->selection().end().deprecatedNode();
1235     if (!focusedNode)
1236         return 0;
1237 
1238     RenderObject* focusedRenderer = focusedNode->renderer();
1239     AccessibilityObject* focusedObject = document->axObjectCache()->getOrCreate(focusedRenderer);
1240 
1241     int offset;
1242     // Don't ignore links if the offset is being requested for a link.
1243     if (!objectAndOffsetUnignored(focusedObject, offset, !coreObject->isLink()))
1244         return 0;
1245 
1246     RenderObject* renderer = coreObject->renderer();
1247     if (renderer && renderer->isListItem()) {
1248         String markerText = toRenderListItem(renderer)->markerTextWithSuffix();
1249 
1250         // We need to adjust the offset for the list item marker.
1251         offset += markerText.length();
1252     }
1253 
1254     // TODO: Verify this for RTL text.
1255     return offset;
1256 }
1257 
baselinePositionForRenderObject(RenderObject * renderObject)1258 static int baselinePositionForRenderObject(RenderObject* renderObject)
1259 {
1260     // FIXME: This implementation of baselinePosition originates from RenderObject.cpp and was
1261     // removed in r70072. The implementation looks incorrect though, because this is not the
1262     // baseline of the underlying RenderObject, but of the AccessibilityRenderObject.
1263     const FontMetrics& fontMetrics = renderObject->firstLineStyle()->fontMetrics();
1264     return fontMetrics.ascent() + (renderObject->firstLineStyle()->computedLineHeight() - fontMetrics.height()) / 2;
1265 }
1266 
getAttributeSetForAccessibilityObject(const AccessibilityObject * object)1267 static AtkAttributeSet* getAttributeSetForAccessibilityObject(const AccessibilityObject* object)
1268 {
1269     if (!object->isAccessibilityRenderObject())
1270         return 0;
1271 
1272     RenderObject* renderer = object->renderer();
1273     RenderStyle* style = renderer->style();
1274 
1275     AtkAttributeSet* result = 0;
1276     GOwnPtr<gchar> buffer(g_strdup_printf("%i", style->fontSize()));
1277     result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_SIZE), buffer.get());
1278 
1279     Color bgColor = style->visitedDependentColor(CSSPropertyBackgroundColor);
1280     if (bgColor.isValid()) {
1281         buffer.set(g_strdup_printf("%i,%i,%i",
1282                                    bgColor.red(), bgColor.green(), bgColor.blue()));
1283         result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_BG_COLOR), buffer.get());
1284     }
1285 
1286     Color fgColor = style->visitedDependentColor(CSSPropertyColor);
1287     if (fgColor.isValid()) {
1288         buffer.set(g_strdup_printf("%i,%i,%i",
1289                                    fgColor.red(), fgColor.green(), fgColor.blue()));
1290         result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_FG_COLOR), buffer.get());
1291     }
1292 
1293     int baselinePosition;
1294     bool includeRise = true;
1295     switch (style->verticalAlign()) {
1296     case SUB:
1297         baselinePosition = -1 * baselinePositionForRenderObject(renderer);
1298         break;
1299     case SUPER:
1300         baselinePosition = baselinePositionForRenderObject(renderer);
1301         break;
1302     case BASELINE:
1303         baselinePosition = 0;
1304         break;
1305     default:
1306         includeRise = false;
1307         break;
1308     }
1309 
1310     if (includeRise) {
1311         buffer.set(g_strdup_printf("%i", baselinePosition));
1312         result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_RISE), buffer.get());
1313     }
1314 
1315     int indentation = style->textIndent().calcValue(object->size().width());
1316     if (indentation != undefinedLength) {
1317         buffer.set(g_strdup_printf("%i", indentation));
1318         result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_INDENT), buffer.get());
1319     }
1320 
1321     String fontFamilyName = style->font().family().family().string();
1322     if (fontFamilyName.left(8) == "-webkit-")
1323         fontFamilyName = fontFamilyName.substring(8);
1324 
1325     result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_FAMILY_NAME), fontFamilyName.utf8().data());
1326 
1327     int fontWeight = -1;
1328     switch (style->font().weight()) {
1329     case FontWeight100:
1330         fontWeight = 100;
1331         break;
1332     case FontWeight200:
1333         fontWeight = 200;
1334         break;
1335     case FontWeight300:
1336         fontWeight = 300;
1337         break;
1338     case FontWeight400:
1339         fontWeight = 400;
1340         break;
1341     case FontWeight500:
1342         fontWeight = 500;
1343         break;
1344     case FontWeight600:
1345         fontWeight = 600;
1346         break;
1347     case FontWeight700:
1348         fontWeight = 700;
1349         break;
1350     case FontWeight800:
1351         fontWeight = 800;
1352         break;
1353     case FontWeight900:
1354         fontWeight = 900;
1355     }
1356     if (fontWeight > 0) {
1357         buffer.set(g_strdup_printf("%i", fontWeight));
1358         result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_WEIGHT), buffer.get());
1359     }
1360 
1361     switch (style->textAlign()) {
1362     case TAAUTO:
1363     case TASTART:
1364     case TAEND:
1365         break;
1366     case LEFT:
1367     case WEBKIT_LEFT:
1368         result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_JUSTIFICATION), "left");
1369         break;
1370     case RIGHT:
1371     case WEBKIT_RIGHT:
1372         result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_JUSTIFICATION), "right");
1373         break;
1374     case CENTER:
1375     case WEBKIT_CENTER:
1376         result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_JUSTIFICATION), "center");
1377         break;
1378     case JUSTIFY:
1379         result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_JUSTIFICATION), "fill");
1380     }
1381 
1382     result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_UNDERLINE), (style->textDecoration() & UNDERLINE) ? "single" : "none");
1383 
1384     result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_STYLE), style->font().italic() ? "italic" : "normal");
1385 
1386     result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_STRIKETHROUGH), (style->textDecoration() & LINE_THROUGH) ? "true" : "false");
1387 
1388     result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_INVISIBLE), (style->visibility() == HIDDEN) ? "true" : "false");
1389 
1390     result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_EDITABLE), object->isReadOnly() ? "false" : "true");
1391 
1392     return result;
1393 }
1394 
compareAttribute(const AtkAttribute * a,const AtkAttribute * b)1395 static gint compareAttribute(const AtkAttribute* a, const AtkAttribute* b)
1396 {
1397     return g_strcmp0(a->name, b->name) || g_strcmp0(a->value, b->value);
1398 }
1399 
1400 // Returns an AtkAttributeSet with the elements of a1 which are either
1401 // not present or different in a2.  Neither a1 nor a2 should be used
1402 // after calling this function.
attributeSetDifference(AtkAttributeSet * a1,AtkAttributeSet * a2)1403 static AtkAttributeSet* attributeSetDifference(AtkAttributeSet* a1, AtkAttributeSet* a2)
1404 {
1405     if (!a2)
1406         return a1;
1407 
1408     AtkAttributeSet* i = a1;
1409     AtkAttributeSet* found;
1410     AtkAttributeSet* toDelete = 0;
1411 
1412     while (i) {
1413         found = g_slist_find_custom(a2, i->data, (GCompareFunc)compareAttribute);
1414         if (found) {
1415             AtkAttributeSet* t = i->next;
1416             toDelete = g_slist_prepend(toDelete, i->data);
1417             a1 = g_slist_delete_link(a1, i);
1418             i = t;
1419         } else
1420             i = i->next;
1421     }
1422 
1423     atk_attribute_set_free(a2);
1424     atk_attribute_set_free(toDelete);
1425     return a1;
1426 }
1427 
accessibilityObjectLength(const AccessibilityObject * object)1428 static guint accessibilityObjectLength(const AccessibilityObject* object)
1429 {
1430     // Non render objects are not taken into account
1431     if (!object->isAccessibilityRenderObject())
1432         return 0;
1433 
1434     // For those objects implementing the AtkText interface we use the
1435     // well known API to always get the text in a consistent way
1436     AtkObject* atkObj = ATK_OBJECT(object->wrapper());
1437     if (ATK_IS_TEXT(atkObj)) {
1438         GOwnPtr<gchar> text(webkit_accessible_text_get_text(ATK_TEXT(atkObj), 0, -1));
1439         return g_utf8_strlen(text.get(), -1);
1440     }
1441 
1442     // Even if we don't expose list markers to Assistive
1443     // Technologies, we need to have a way to measure their length
1444     // for those cases when it's needed to take it into account
1445     // separately (as in getAccessibilityObjectForOffset)
1446     RenderObject* renderer = object->renderer();
1447     if (renderer && renderer->isListMarker()) {
1448         RenderListMarker* marker = toRenderListMarker(renderer);
1449         return marker->text().length() + marker->suffix().length();
1450     }
1451 
1452     return 0;
1453 }
1454 
getAccessibilityObjectForOffset(const AccessibilityObject * object,guint offset,gint * startOffset,gint * endOffset)1455 static const AccessibilityObject* getAccessibilityObjectForOffset(const AccessibilityObject* object, guint offset, gint* startOffset, gint* endOffset)
1456 {
1457     const AccessibilityObject* result;
1458     guint length = accessibilityObjectLength(object);
1459     if (length > offset) {
1460         *startOffset = 0;
1461         *endOffset = length;
1462         result = object;
1463     } else {
1464         *startOffset = -1;
1465         *endOffset = -1;
1466         result = 0;
1467     }
1468 
1469     if (!object->firstChild())
1470         return result;
1471 
1472     AccessibilityObject* child = object->firstChild();
1473     guint currentOffset = 0;
1474     guint childPosition = 0;
1475     while (child && currentOffset <= offset) {
1476         guint childLength = accessibilityObjectLength(child);
1477         currentOffset = childLength + childPosition;
1478         if (currentOffset > offset) {
1479             gint childStartOffset;
1480             gint childEndOffset;
1481             const AccessibilityObject* grandChild = getAccessibilityObjectForOffset(child, offset-childPosition,  &childStartOffset, &childEndOffset);
1482             if (childStartOffset >= 0) {
1483                 *startOffset = childStartOffset + childPosition;
1484                 *endOffset = childEndOffset + childPosition;
1485                 result = grandChild;
1486             }
1487         } else {
1488             childPosition += childLength;
1489             child = child->nextSibling();
1490         }
1491     }
1492     return result;
1493 }
1494 
getRunAttributesFromAccesibilityObject(const AccessibilityObject * element,gint offset,gint * startOffset,gint * endOffset)1495 static AtkAttributeSet* getRunAttributesFromAccesibilityObject(const AccessibilityObject* element, gint offset, gint* startOffset, gint* endOffset)
1496 {
1497     const AccessibilityObject *child = getAccessibilityObjectForOffset(element, offset, startOffset, endOffset);
1498     if (!child) {
1499         *startOffset = -1;
1500         *endOffset = -1;
1501         return 0;
1502     }
1503 
1504     AtkAttributeSet* defaultAttributes = getAttributeSetForAccessibilityObject(element);
1505     AtkAttributeSet* childAttributes = getAttributeSetForAccessibilityObject(child);
1506 
1507     return attributeSetDifference(childAttributes, defaultAttributes);
1508 }
1509 
webkit_accessible_text_get_run_attributes(AtkText * text,gint offset,gint * startOffset,gint * endOffset)1510 static AtkAttributeSet* webkit_accessible_text_get_run_attributes(AtkText* text, gint offset, gint* startOffset, gint* endOffset)
1511 {
1512     AccessibilityObject* coreObject = core(text);
1513     AtkAttributeSet* result;
1514 
1515     if (!coreObject) {
1516         *startOffset = 0;
1517         *endOffset = atk_text_get_character_count(text);
1518         return 0;
1519     }
1520 
1521     if (offset == -1)
1522         offset = atk_text_get_caret_offset(text);
1523 
1524     result = getRunAttributesFromAccesibilityObject(coreObject, offset, startOffset, endOffset);
1525 
1526     if (*startOffset < 0) {
1527         *startOffset = offset;
1528         *endOffset = offset;
1529     }
1530 
1531     return result;
1532 }
1533 
webkit_accessible_text_get_default_attributes(AtkText * text)1534 static AtkAttributeSet* webkit_accessible_text_get_default_attributes(AtkText* text)
1535 {
1536     AccessibilityObject* coreObject = core(text);
1537     if (!coreObject || !coreObject->isAccessibilityRenderObject())
1538         return 0;
1539 
1540     return getAttributeSetForAccessibilityObject(coreObject);
1541 }
1542 
textExtents(AtkText * text,gint startOffset,gint length,AtkCoordType coords)1543 static IntRect textExtents(AtkText* text, gint startOffset, gint length, AtkCoordType coords)
1544 {
1545     gchar* textContent = webkit_accessible_text_get_text(text, startOffset, -1);
1546     gint textLength = g_utf8_strlen(textContent, -1);
1547 
1548     // The first case (endOffset of -1) should work, but seems broken for all Gtk+ apps.
1549     gint rangeLength = length;
1550     if (rangeLength < 0 || rangeLength > textLength)
1551         rangeLength = textLength;
1552     AccessibilityObject* coreObject = core(text);
1553 
1554     IntRect extents = coreObject->doAXBoundsForRange(PlainTextRange(startOffset, rangeLength));
1555     switch(coords) {
1556     case ATK_XY_SCREEN:
1557         if (Document* document = coreObject->document())
1558             extents = document->view()->contentsToScreen(extents);
1559         break;
1560     case ATK_XY_WINDOW:
1561         // No-op
1562         break;
1563     }
1564 
1565     return extents;
1566 }
1567 
webkit_accessible_text_get_character_extents(AtkText * text,gint offset,gint * x,gint * y,gint * width,gint * height,AtkCoordType coords)1568 static void webkit_accessible_text_get_character_extents(AtkText* text, gint offset, gint* x, gint* y, gint* width, gint* height, AtkCoordType coords)
1569 {
1570     IntRect extents = textExtents(text, offset, 1, coords);
1571     *x = extents.x();
1572     *y = extents.y();
1573     *width = extents.width();
1574     *height = extents.height();
1575 }
1576 
webkit_accessible_text_get_range_extents(AtkText * text,gint startOffset,gint endOffset,AtkCoordType coords,AtkTextRectangle * rect)1577 static void webkit_accessible_text_get_range_extents(AtkText* text, gint startOffset, gint endOffset, AtkCoordType coords, AtkTextRectangle* rect)
1578 {
1579     IntRect extents = textExtents(text, startOffset, endOffset - startOffset, coords);
1580     rect->x = extents.x();
1581     rect->y = extents.y();
1582     rect->width = extents.width();
1583     rect->height = extents.height();
1584 }
1585 
webkit_accessible_text_get_character_count(AtkText * text)1586 static gint webkit_accessible_text_get_character_count(AtkText* text)
1587 {
1588     return accessibilityObjectLength(core(text));
1589 }
1590 
webkit_accessible_text_get_offset_at_point(AtkText * text,gint x,gint y,AtkCoordType coords)1591 static gint webkit_accessible_text_get_offset_at_point(AtkText* text, gint x, gint y, AtkCoordType coords)
1592 {
1593     // FIXME: Use the AtkCoordType
1594     // TODO: Is it correct to ignore range.length?
1595     IntPoint pos(x, y);
1596     PlainTextRange range = core(text)->doAXRangeForPosition(pos);
1597     return range.start;
1598 }
1599 
getSelectionOffsetsForObject(AccessibilityObject * coreObject,VisibleSelection & selection,gint & startOffset,gint & endOffset)1600 static void getSelectionOffsetsForObject(AccessibilityObject* coreObject, VisibleSelection& selection, gint& startOffset, gint& endOffset)
1601 {
1602     if (!coreObject->isAccessibilityRenderObject())
1603         return;
1604 
1605     // Early return if the selection doesn't affect the selected node.
1606     if (!selectionBelongsToObject(coreObject, selection))
1607         return;
1608 
1609     // We need to find the exact start and end positions in the
1610     // selected node that intersects the selection, to later on get
1611     // the right values for the effective start and end offsets.
1612     ExceptionCode ec = 0;
1613     Position nodeRangeStart;
1614     Position nodeRangeEnd;
1615     Node* node = coreObject->node();
1616     RefPtr<Range> selRange = selection.toNormalizedRange();
1617 
1618     // If the selection affects the selected node and its first
1619     // possible position is also in the selection, we must set
1620     // nodeRangeStart to that position, otherwise to the selection's
1621     // start position (it would belong to the node anyway).
1622     Node* firstLeafNode = node->firstDescendant();
1623     if (selRange->isPointInRange(firstLeafNode, 0, ec))
1624         nodeRangeStart = firstPositionInOrBeforeNode(firstLeafNode);
1625     else
1626         nodeRangeStart = selRange->startPosition();
1627 
1628     // If the selection affects the selected node and its last
1629     // possible position is also in the selection, we must set
1630     // nodeRangeEnd to that position, otherwise to the selection's
1631     // end position (it would belong to the node anyway).
1632     Node* lastLeafNode = node->lastDescendant();
1633     if (selRange->isPointInRange(lastLeafNode, lastOffsetInNode(lastLeafNode), ec))
1634         nodeRangeEnd = lastPositionInOrAfterNode(lastLeafNode);
1635     else
1636         nodeRangeEnd = selRange->endPosition();
1637 
1638     // Calculate position of the selected range inside the object.
1639     Position parentFirstPosition = firstPositionInOrBeforeNode(node);
1640     RefPtr<Range> rangeInParent = Range::create(node->document(), parentFirstPosition, nodeRangeStart);
1641 
1642     // Set values for start and end offsets.
1643     startOffset = TextIterator::rangeLength(rangeInParent.get(), true);
1644 
1645     // We need to adjust the offsets for the list item marker.
1646     RenderObject* renderer = coreObject->renderer();
1647     if (renderer && renderer->isListItem()) {
1648         String markerText = toRenderListItem(renderer)->markerTextWithSuffix();
1649         startOffset += markerText.length();
1650     }
1651 
1652     RefPtr<Range> nodeRange = Range::create(node->document(), nodeRangeStart, nodeRangeEnd);
1653     endOffset = startOffset + TextIterator::rangeLength(nodeRange.get(), true);
1654 }
1655 
webkit_accessible_text_get_n_selections(AtkText * text)1656 static gint webkit_accessible_text_get_n_selections(AtkText* text)
1657 {
1658     AccessibilityObject* coreObject = core(text);
1659     VisibleSelection selection = coreObject->selection();
1660 
1661     // Only range selections are needed for the purpose of this method
1662     if (!selection.isRange())
1663         return 0;
1664 
1665     // We don't support multiple selections for now, so there's only
1666     // two possibilities
1667     // Also, we don't want to do anything if the selection does not
1668     // belong to the currently selected object. We have to check since
1669     // there's no way to get the selection for a given object, only
1670     // the global one (the API is a bit confusing)
1671     return selectionBelongsToObject(coreObject, selection) ? 1 : 0;
1672 }
1673 
webkit_accessible_text_get_selection(AtkText * text,gint selectionNum,gint * startOffset,gint * endOffset)1674 static gchar* webkit_accessible_text_get_selection(AtkText* text, gint selectionNum, gint* startOffset, gint* endOffset)
1675 {
1676     // Default values, unless the contrary is proved
1677     *startOffset = *endOffset = 0;
1678 
1679     // WebCore does not support multiple selection, so anything but 0 does not make sense for now.
1680     if (selectionNum)
1681         return 0;
1682 
1683     // Get the offsets of the selection for the selected object
1684     AccessibilityObject* coreObject = core(text);
1685     VisibleSelection selection = coreObject->selection();
1686     getSelectionOffsetsForObject(coreObject, selection, *startOffset, *endOffset);
1687 
1688     // Return 0 instead of "", as that's the expected result for
1689     // this AtkText method when there's no selection
1690     if (*startOffset == *endOffset)
1691         return 0;
1692 
1693     return webkit_accessible_text_get_text(text, *startOffset, *endOffset);
1694 }
1695 
webkit_accessible_text_add_selection(AtkText * text,gint start_offset,gint end_offset)1696 static gboolean webkit_accessible_text_add_selection(AtkText* text, gint start_offset, gint end_offset)
1697 {
1698     notImplemented();
1699     return FALSE;
1700 }
1701 
webkit_accessible_text_set_selection(AtkText * text,gint selectionNum,gint startOffset,gint endOffset)1702 static gboolean webkit_accessible_text_set_selection(AtkText* text, gint selectionNum, gint startOffset, gint endOffset)
1703 {
1704     // WebCore does not support multiple selection, so anything but 0 does not make sense for now.
1705     if (selectionNum)
1706         return FALSE;
1707 
1708     AccessibilityObject* coreObject = core(text);
1709     if (!coreObject->isAccessibilityRenderObject())
1710         return FALSE;
1711 
1712     // Consider -1 and out-of-bound values and correct them to length
1713     gint textCount = webkit_accessible_text_get_character_count(text);
1714     if (startOffset < 0 || startOffset > textCount)
1715         startOffset = textCount;
1716     if (endOffset < 0 || endOffset > textCount)
1717         endOffset = textCount;
1718 
1719     // We need to adjust the offsets for the list item marker.
1720     RenderObject* renderer = coreObject->renderer();
1721     if (renderer && renderer->isListItem()) {
1722         String markerText = toRenderListItem(renderer)->markerTextWithSuffix();
1723         int markerLength = markerText.length();
1724         if (startOffset < markerLength || endOffset < markerLength)
1725             return FALSE;
1726 
1727         startOffset -= markerLength;
1728         endOffset -= markerLength;
1729     }
1730 
1731     PlainTextRange textRange(startOffset, endOffset - startOffset);
1732     VisiblePositionRange range = coreObject->visiblePositionRangeForRange(textRange);
1733     if (range.isNull())
1734         return FALSE;
1735 
1736     coreObject->setSelectedVisiblePositionRange(range);
1737     return TRUE;
1738 }
1739 
webkit_accessible_text_remove_selection(AtkText * text,gint selectionNum)1740 static gboolean webkit_accessible_text_remove_selection(AtkText* text, gint selectionNum)
1741 {
1742     // WebCore does not support multiple selection, so anything but 0 does not make sense for now.
1743     if (selectionNum)
1744         return FALSE;
1745 
1746     // Do nothing if current selection doesn't belong to the object
1747     if (!webkit_accessible_text_get_n_selections(text))
1748         return FALSE;
1749 
1750     // Set a new 0-sized selection to the caret position, in order
1751     // to simulate selection removal (GAIL style)
1752     gint caretOffset = webkit_accessible_text_get_caret_offset(text);
1753     return webkit_accessible_text_set_selection(text, selectionNum, caretOffset, caretOffset);
1754 }
1755 
webkit_accessible_text_set_caret_offset(AtkText * text,gint offset)1756 static gboolean webkit_accessible_text_set_caret_offset(AtkText* text, gint offset)
1757 {
1758     AccessibilityObject* coreObject = core(text);
1759 
1760     if (!coreObject->isAccessibilityRenderObject())
1761         return FALSE;
1762 
1763     RenderObject* renderer = coreObject->renderer();
1764     if (renderer && renderer->isListItem()) {
1765         String markerText = toRenderListItem(renderer)->markerTextWithSuffix();
1766         int markerLength = markerText.length();
1767         if (offset < markerLength)
1768             return FALSE;
1769 
1770         // We need to adjust the offset for list items.
1771         offset -= markerLength;
1772     }
1773 
1774     PlainTextRange textRange(offset, 0);
1775     VisiblePositionRange range = coreObject->visiblePositionRangeForRange(textRange);
1776     if (range.isNull())
1777         return FALSE;
1778 
1779     coreObject->setSelectedVisiblePositionRange(range);
1780     return TRUE;
1781 }
1782 
atk_text_interface_init(AtkTextIface * iface)1783 static void atk_text_interface_init(AtkTextIface* iface)
1784 {
1785     iface->get_text = webkit_accessible_text_get_text;
1786     iface->get_text_after_offset = webkit_accessible_text_get_text_after_offset;
1787     iface->get_text_at_offset = webkit_accessible_text_get_text_at_offset;
1788     iface->get_character_at_offset = webkit_accessible_text_get_character_at_offset;
1789     iface->get_text_before_offset = webkit_accessible_text_get_text_before_offset;
1790     iface->get_caret_offset = webkit_accessible_text_get_caret_offset;
1791     iface->get_run_attributes = webkit_accessible_text_get_run_attributes;
1792     iface->get_default_attributes = webkit_accessible_text_get_default_attributes;
1793     iface->get_character_extents = webkit_accessible_text_get_character_extents;
1794     iface->get_range_extents = webkit_accessible_text_get_range_extents;
1795     iface->get_character_count = webkit_accessible_text_get_character_count;
1796     iface->get_offset_at_point = webkit_accessible_text_get_offset_at_point;
1797     iface->get_n_selections = webkit_accessible_text_get_n_selections;
1798     iface->get_selection = webkit_accessible_text_get_selection;
1799 
1800     // set methods
1801     iface->add_selection = webkit_accessible_text_add_selection;
1802     iface->remove_selection = webkit_accessible_text_remove_selection;
1803     iface->set_selection = webkit_accessible_text_set_selection;
1804     iface->set_caret_offset = webkit_accessible_text_set_caret_offset;
1805 }
1806 
1807 // EditableText
1808 
webkit_accessible_editable_text_set_run_attributes(AtkEditableText * text,AtkAttributeSet * attrib_set,gint start_offset,gint end_offset)1809 static gboolean webkit_accessible_editable_text_set_run_attributes(AtkEditableText* text, AtkAttributeSet* attrib_set, gint start_offset, gint end_offset)
1810 {
1811     notImplemented();
1812     return FALSE;
1813 }
1814 
webkit_accessible_editable_text_set_text_contents(AtkEditableText * text,const gchar * string)1815 static void webkit_accessible_editable_text_set_text_contents(AtkEditableText* text, const gchar* string)
1816 {
1817     // FIXME: string nullcheck?
1818     core(text)->setValue(String::fromUTF8(string));
1819 }
1820 
webkit_accessible_editable_text_insert_text(AtkEditableText * text,const gchar * string,gint length,gint * position)1821 static void webkit_accessible_editable_text_insert_text(AtkEditableText* text, const gchar* string, gint length, gint* position)
1822 {
1823     // FIXME: string nullcheck?
1824 
1825     AccessibilityObject* coreObject = core(text);
1826     // FIXME: Not implemented in WebCore
1827     //coreObject->setSelectedTextRange(PlainTextRange(*position, 0));
1828     //coreObject->setSelectedText(String::fromUTF8(string));
1829 
1830     Document* document = coreObject->document();
1831     if (!document || !document->frame())
1832         return;
1833 
1834     coreObject->setSelectedVisiblePositionRange(coreObject->visiblePositionRangeForRange(PlainTextRange(*position, 0)));
1835     coreObject->setFocused(true);
1836     // FIXME: We should set position to the actual inserted text length, which may be less than that requested.
1837     if (document->frame()->editor()->insertTextWithoutSendingTextEvent(String::fromUTF8(string), false, 0))
1838         *position += length;
1839 }
1840 
webkit_accessible_editable_text_copy_text(AtkEditableText * text,gint start_pos,gint end_pos)1841 static void webkit_accessible_editable_text_copy_text(AtkEditableText* text, gint start_pos, gint end_pos)
1842 {
1843     notImplemented();
1844 }
1845 
webkit_accessible_editable_text_cut_text(AtkEditableText * text,gint start_pos,gint end_pos)1846 static void webkit_accessible_editable_text_cut_text(AtkEditableText* text, gint start_pos, gint end_pos)
1847 {
1848     notImplemented();
1849 }
1850 
webkit_accessible_editable_text_delete_text(AtkEditableText * text,gint start_pos,gint end_pos)1851 static void webkit_accessible_editable_text_delete_text(AtkEditableText* text, gint start_pos, gint end_pos)
1852 {
1853     AccessibilityObject* coreObject = core(text);
1854     // FIXME: Not implemented in WebCore
1855     //coreObject->setSelectedTextRange(PlainTextRange(start_pos, end_pos - start_pos));
1856     //coreObject->setSelectedText(String());
1857 
1858     Document* document = coreObject->document();
1859     if (!document || !document->frame())
1860         return;
1861 
1862     coreObject->setSelectedVisiblePositionRange(coreObject->visiblePositionRangeForRange(PlainTextRange(start_pos, end_pos - start_pos)));
1863     coreObject->setFocused(true);
1864     document->frame()->editor()->performDelete();
1865 }
1866 
webkit_accessible_editable_text_paste_text(AtkEditableText * text,gint position)1867 static void webkit_accessible_editable_text_paste_text(AtkEditableText* text, gint position)
1868 {
1869     notImplemented();
1870 }
1871 
atk_editable_text_interface_init(AtkEditableTextIface * iface)1872 static void atk_editable_text_interface_init(AtkEditableTextIface* iface)
1873 {
1874     iface->set_run_attributes = webkit_accessible_editable_text_set_run_attributes;
1875     iface->set_text_contents = webkit_accessible_editable_text_set_text_contents;
1876     iface->insert_text = webkit_accessible_editable_text_insert_text;
1877     iface->copy_text = webkit_accessible_editable_text_copy_text;
1878     iface->cut_text = webkit_accessible_editable_text_cut_text;
1879     iface->delete_text = webkit_accessible_editable_text_delete_text;
1880     iface->paste_text = webkit_accessible_editable_text_paste_text;
1881 }
1882 
contentsToAtk(AccessibilityObject * coreObject,AtkCoordType coordType,IntRect rect,gint * x,gint * y,gint * width=0,gint * height=0)1883 static void contentsToAtk(AccessibilityObject* coreObject, AtkCoordType coordType, IntRect rect, gint* x, gint* y, gint* width = 0, gint* height = 0)
1884 {
1885     FrameView* frameView = coreObject->documentFrameView();
1886 
1887     if (frameView) {
1888         switch (coordType) {
1889         case ATK_XY_WINDOW:
1890             rect = frameView->contentsToWindow(rect);
1891             break;
1892         case ATK_XY_SCREEN:
1893             rect = frameView->contentsToScreen(rect);
1894             break;
1895         }
1896     }
1897 
1898     if (x)
1899         *x = rect.x();
1900     if (y)
1901         *y = rect.y();
1902     if (width)
1903         *width = rect.width();
1904     if (height)
1905         *height = rect.height();
1906 }
1907 
atkToContents(AccessibilityObject * coreObject,AtkCoordType coordType,gint x,gint y)1908 static IntPoint atkToContents(AccessibilityObject* coreObject, AtkCoordType coordType, gint x, gint y)
1909 {
1910     IntPoint pos(x, y);
1911 
1912     FrameView* frameView = coreObject->documentFrameView();
1913     if (frameView) {
1914         switch (coordType) {
1915         case ATK_XY_SCREEN:
1916             return frameView->screenToContents(pos);
1917         case ATK_XY_WINDOW:
1918             return frameView->windowToContents(pos);
1919         }
1920     }
1921 
1922     return pos;
1923 }
1924 
webkit_accessible_component_ref_accessible_at_point(AtkComponent * component,gint x,gint y,AtkCoordType coordType)1925 static AtkObject* webkit_accessible_component_ref_accessible_at_point(AtkComponent* component, gint x, gint y, AtkCoordType coordType)
1926 {
1927     IntPoint pos = atkToContents(core(component), coordType, x, y);
1928 
1929     AccessibilityObject* target = core(component)->accessibilityHitTest(pos);
1930     if (!target)
1931         return 0;
1932     g_object_ref(target->wrapper());
1933     return target->wrapper();
1934 }
1935 
webkit_accessible_component_get_extents(AtkComponent * component,gint * x,gint * y,gint * width,gint * height,AtkCoordType coordType)1936 static void webkit_accessible_component_get_extents(AtkComponent* component, gint* x, gint* y, gint* width, gint* height, AtkCoordType coordType)
1937 {
1938     IntRect rect = core(component)->elementRect();
1939     contentsToAtk(core(component), coordType, rect, x, y, width, height);
1940 }
1941 
webkit_accessible_component_grab_focus(AtkComponent * component)1942 static gboolean webkit_accessible_component_grab_focus(AtkComponent* component)
1943 {
1944     core(component)->setFocused(true);
1945     return core(component)->isFocused();
1946 }
1947 
atk_component_interface_init(AtkComponentIface * iface)1948 static void atk_component_interface_init(AtkComponentIface* iface)
1949 {
1950     iface->ref_accessible_at_point = webkit_accessible_component_ref_accessible_at_point;
1951     iface->get_extents = webkit_accessible_component_get_extents;
1952     iface->grab_focus = webkit_accessible_component_grab_focus;
1953 }
1954 
1955 // Image
1956 
webkit_accessible_image_get_image_position(AtkImage * image,gint * x,gint * y,AtkCoordType coordType)1957 static void webkit_accessible_image_get_image_position(AtkImage* image, gint* x, gint* y, AtkCoordType coordType)
1958 {
1959     IntRect rect = core(image)->elementRect();
1960     contentsToAtk(core(image), coordType, rect, x, y);
1961 }
1962 
webkit_accessible_image_get_image_description(AtkImage * image)1963 static const gchar* webkit_accessible_image_get_image_description(AtkImage* image)
1964 {
1965     return returnString(core(image)->accessibilityDescription());
1966 }
1967 
webkit_accessible_image_get_image_size(AtkImage * image,gint * width,gint * height)1968 static void webkit_accessible_image_get_image_size(AtkImage* image, gint* width, gint* height)
1969 {
1970     IntSize size = core(image)->size();
1971 
1972     if (width)
1973         *width = size.width();
1974     if (height)
1975         *height = size.height();
1976 }
1977 
atk_image_interface_init(AtkImageIface * iface)1978 static void atk_image_interface_init(AtkImageIface* iface)
1979 {
1980     iface->get_image_position = webkit_accessible_image_get_image_position;
1981     iface->get_image_description = webkit_accessible_image_get_image_description;
1982     iface->get_image_size = webkit_accessible_image_get_image_size;
1983 }
1984 
1985 // Table
1986 
cell(AtkTable * table,guint row,guint column)1987 static AccessibilityTableCell* cell(AtkTable* table, guint row, guint column)
1988 {
1989     AccessibilityObject* accTable = core(table);
1990     if (accTable->isAccessibilityRenderObject())
1991         return static_cast<AccessibilityTable*>(accTable)->cellForColumnAndRow(column, row);
1992     return 0;
1993 }
1994 
cellIndex(AccessibilityTableCell * axCell,AccessibilityTable * axTable)1995 static gint cellIndex(AccessibilityTableCell* axCell, AccessibilityTable* axTable)
1996 {
1997     // Calculate the cell's index as if we had a traditional Gtk+ table in
1998     // which cells are all direct children of the table, arranged row-first.
1999     AccessibilityObject::AccessibilityChildrenVector allCells;
2000     axTable->cells(allCells);
2001     AccessibilityObject::AccessibilityChildrenVector::iterator position;
2002     position = std::find(allCells.begin(), allCells.end(), axCell);
2003     if (position == allCells.end())
2004         return -1;
2005     return position - allCells.begin();
2006 }
2007 
cellAtIndex(AtkTable * table,gint index)2008 static AccessibilityTableCell* cellAtIndex(AtkTable* table, gint index)
2009 {
2010     AccessibilityObject* accTable = core(table);
2011     if (accTable->isAccessibilityRenderObject()) {
2012         AccessibilityObject::AccessibilityChildrenVector allCells;
2013         static_cast<AccessibilityTable*>(accTable)->cells(allCells);
2014         if (0 <= index && static_cast<unsigned>(index) < allCells.size()) {
2015             AccessibilityObject* accCell = allCells.at(index).get();
2016             return static_cast<AccessibilityTableCell*>(accCell);
2017         }
2018     }
2019     return 0;
2020 }
2021 
webkit_accessible_table_ref_at(AtkTable * table,gint row,gint column)2022 static AtkObject* webkit_accessible_table_ref_at(AtkTable* table, gint row, gint column)
2023 {
2024     AccessibilityTableCell* axCell = cell(table, row, column);
2025     if (!axCell)
2026         return 0;
2027     return axCell->wrapper();
2028 }
2029 
webkit_accessible_table_get_index_at(AtkTable * table,gint row,gint column)2030 static gint webkit_accessible_table_get_index_at(AtkTable* table, gint row, gint column)
2031 {
2032     AccessibilityTableCell* axCell = cell(table, row, column);
2033     AccessibilityTable* axTable = static_cast<AccessibilityTable*>(core(table));
2034     return cellIndex(axCell, axTable);
2035 }
2036 
webkit_accessible_table_get_column_at_index(AtkTable * table,gint index)2037 static gint webkit_accessible_table_get_column_at_index(AtkTable* table, gint index)
2038 {
2039     AccessibilityTableCell* axCell = cellAtIndex(table, index);
2040     if (axCell){
2041         pair<int, int> columnRange;
2042         axCell->columnIndexRange(columnRange);
2043         return columnRange.first;
2044     }
2045     return -1;
2046 }
2047 
webkit_accessible_table_get_row_at_index(AtkTable * table,gint index)2048 static gint webkit_accessible_table_get_row_at_index(AtkTable* table, gint index)
2049 {
2050     AccessibilityTableCell* axCell = cellAtIndex(table, index);
2051     if (axCell){
2052         pair<int, int> rowRange;
2053         axCell->rowIndexRange(rowRange);
2054         return rowRange.first;
2055     }
2056     return -1;
2057 }
2058 
webkit_accessible_table_get_n_columns(AtkTable * table)2059 static gint webkit_accessible_table_get_n_columns(AtkTable* table)
2060 {
2061     AccessibilityObject* accTable = core(table);
2062     if (accTable->isAccessibilityRenderObject())
2063         return static_cast<AccessibilityTable*>(accTable)->columnCount();
2064     return 0;
2065 }
2066 
webkit_accessible_table_get_n_rows(AtkTable * table)2067 static gint webkit_accessible_table_get_n_rows(AtkTable* table)
2068 {
2069     AccessibilityObject* accTable = core(table);
2070     if (accTable->isAccessibilityRenderObject())
2071         return static_cast<AccessibilityTable*>(accTable)->rowCount();
2072     return 0;
2073 }
2074 
webkit_accessible_table_get_column_extent_at(AtkTable * table,gint row,gint column)2075 static gint webkit_accessible_table_get_column_extent_at(AtkTable* table, gint row, gint column)
2076 {
2077     AccessibilityTableCell* axCell = cell(table, row, column);
2078     if (axCell) {
2079         pair<int, int> columnRange;
2080         axCell->columnIndexRange(columnRange);
2081         return columnRange.second;
2082     }
2083     return 0;
2084 }
2085 
webkit_accessible_table_get_row_extent_at(AtkTable * table,gint row,gint column)2086 static gint webkit_accessible_table_get_row_extent_at(AtkTable* table, gint row, gint column)
2087 {
2088     AccessibilityTableCell* axCell = cell(table, row, column);
2089     if (axCell) {
2090         pair<int, int> rowRange;
2091         axCell->rowIndexRange(rowRange);
2092         return rowRange.second;
2093     }
2094     return 0;
2095 }
2096 
webkit_accessible_table_get_column_header(AtkTable * table,gint column)2097 static AtkObject* webkit_accessible_table_get_column_header(AtkTable* table, gint column)
2098 {
2099     AccessibilityObject* accTable = core(table);
2100     if (accTable->isAccessibilityRenderObject()) {
2101         AccessibilityObject::AccessibilityChildrenVector allColumnHeaders;
2102         static_cast<AccessibilityTable*>(accTable)->columnHeaders(allColumnHeaders);
2103         unsigned columnCount = allColumnHeaders.size();
2104         for (unsigned k = 0; k < columnCount; ++k) {
2105             pair<int, int> columnRange;
2106             AccessibilityTableCell* cell = static_cast<AccessibilityTableCell*>(allColumnHeaders.at(k).get());
2107             cell->columnIndexRange(columnRange);
2108             if (columnRange.first <= column && column < columnRange.first + columnRange.second)
2109                 return allColumnHeaders[k]->wrapper();
2110         }
2111     }
2112     return 0;
2113 }
2114 
webkit_accessible_table_get_row_header(AtkTable * table,gint row)2115 static AtkObject* webkit_accessible_table_get_row_header(AtkTable* table, gint row)
2116 {
2117     AccessibilityObject* accTable = core(table);
2118     if (accTable->isAccessibilityRenderObject()) {
2119         AccessibilityObject::AccessibilityChildrenVector allRowHeaders;
2120         static_cast<AccessibilityTable*>(accTable)->rowHeaders(allRowHeaders);
2121         unsigned rowCount = allRowHeaders.size();
2122         for (unsigned k = 0; k < rowCount; ++k) {
2123             pair<int, int> rowRange;
2124             AccessibilityTableCell* cell = static_cast<AccessibilityTableCell*>(allRowHeaders.at(k).get());
2125             cell->rowIndexRange(rowRange);
2126             if (rowRange.first <= row && row < rowRange.first + rowRange.second)
2127                 return allRowHeaders[k]->wrapper();
2128         }
2129     }
2130     return 0;
2131 }
2132 
webkit_accessible_table_get_caption(AtkTable * table)2133 static AtkObject* webkit_accessible_table_get_caption(AtkTable* table)
2134 {
2135     AccessibilityObject* accTable = core(table);
2136     if (accTable->isAccessibilityRenderObject()) {
2137         Node* node = accTable->node();
2138         if (node && node->hasTagName(HTMLNames::tableTag)) {
2139             HTMLTableCaptionElement* caption = static_cast<HTMLTableElement*>(node)->caption();
2140             if (caption)
2141                 return AccessibilityObject::firstAccessibleObjectFromNode(caption->renderer()->node())->wrapper();
2142         }
2143     }
2144     return 0;
2145 }
2146 
webkit_accessible_table_get_column_description(AtkTable * table,gint column)2147 static const gchar* webkit_accessible_table_get_column_description(AtkTable* table, gint column)
2148 {
2149     AtkObject* columnHeader = atk_table_get_column_header(table, column);
2150     if (columnHeader && ATK_IS_TEXT(columnHeader))
2151         return webkit_accessible_text_get_text(ATK_TEXT(columnHeader), 0, -1);
2152 
2153     return 0;
2154 }
2155 
webkit_accessible_table_get_row_description(AtkTable * table,gint row)2156 static const gchar* webkit_accessible_table_get_row_description(AtkTable* table, gint row)
2157 {
2158     AtkObject* rowHeader = atk_table_get_row_header(table, row);
2159     if (rowHeader && ATK_IS_TEXT(rowHeader))
2160         return webkit_accessible_text_get_text(ATK_TEXT(rowHeader), 0, -1);
2161 
2162     return 0;
2163 }
2164 
atk_table_interface_init(AtkTableIface * iface)2165 static void atk_table_interface_init(AtkTableIface* iface)
2166 {
2167     iface->ref_at = webkit_accessible_table_ref_at;
2168     iface->get_index_at = webkit_accessible_table_get_index_at;
2169     iface->get_column_at_index = webkit_accessible_table_get_column_at_index;
2170     iface->get_row_at_index = webkit_accessible_table_get_row_at_index;
2171     iface->get_n_columns = webkit_accessible_table_get_n_columns;
2172     iface->get_n_rows = webkit_accessible_table_get_n_rows;
2173     iface->get_column_extent_at = webkit_accessible_table_get_column_extent_at;
2174     iface->get_row_extent_at = webkit_accessible_table_get_row_extent_at;
2175     iface->get_column_header = webkit_accessible_table_get_column_header;
2176     iface->get_row_header = webkit_accessible_table_get_row_header;
2177     iface->get_caption = webkit_accessible_table_get_caption;
2178     iface->get_column_description = webkit_accessible_table_get_column_description;
2179     iface->get_row_description = webkit_accessible_table_get_row_description;
2180 }
2181 
webkitAccessibleHypertextGetLink(AtkHypertext * hypertext,gint index)2182 static AtkHyperlink* webkitAccessibleHypertextGetLink(AtkHypertext* hypertext, gint index)
2183 {
2184     AccessibilityObject::AccessibilityChildrenVector children = core(hypertext)->children();
2185     if (index < 0 || static_cast<unsigned>(index) >= children.size())
2186         return 0;
2187 
2188     gint currentLink = -1;
2189     for (unsigned i = 0; i < children.size(); i++) {
2190         AccessibilityObject* coreChild = children.at(i).get();
2191         if (!coreChild->accessibilityIsIgnored()) {
2192             AtkObject* axObject = coreChild->wrapper();
2193             if (!axObject || !ATK_IS_HYPERLINK_IMPL(axObject))
2194                 continue;
2195 
2196             currentLink++;
2197             if (index != currentLink)
2198                 continue;
2199 
2200             return atk_hyperlink_impl_get_hyperlink(ATK_HYPERLINK_IMPL(axObject));
2201         }
2202     }
2203 
2204     return 0;
2205 }
2206 
webkitAccessibleHypertextGetNLinks(AtkHypertext * hypertext)2207 static gint webkitAccessibleHypertextGetNLinks(AtkHypertext* hypertext)
2208 {
2209     AccessibilityObject::AccessibilityChildrenVector children = core(hypertext)->children();
2210     if (!children.size())
2211         return 0;
2212 
2213     gint linksFound = 0;
2214     for (size_t i = 0; i < children.size(); i++) {
2215         AccessibilityObject* coreChild = children.at(i).get();
2216         if (!coreChild->accessibilityIsIgnored()) {
2217             AtkObject* axObject = coreChild->wrapper();
2218             if (axObject && ATK_IS_HYPERLINK_IMPL(axObject))
2219                 linksFound++;
2220         }
2221     }
2222 
2223     return linksFound;
2224 }
2225 
webkitAccessibleHypertextGetLinkIndex(AtkHypertext * hypertext,gint charIndex)2226 static gint webkitAccessibleHypertextGetLinkIndex(AtkHypertext* hypertext, gint charIndex)
2227 {
2228     size_t linksCount = webkitAccessibleHypertextGetNLinks(hypertext);
2229     if (!linksCount)
2230         return -1;
2231 
2232     for (size_t i = 0; i < linksCount; i++) {
2233         AtkHyperlink* hyperlink = ATK_HYPERLINK(webkitAccessibleHypertextGetLink(hypertext, i));
2234         gint startIndex = atk_hyperlink_get_start_index(hyperlink);
2235         gint endIndex = atk_hyperlink_get_end_index(hyperlink);
2236 
2237         // Check if the char index in the link's offset range
2238         if (startIndex <= charIndex && charIndex < endIndex)
2239             return i;
2240     }
2241 
2242     // Not found if reached
2243     return -1;
2244 }
2245 
atkHypertextInterfaceInit(AtkHypertextIface * iface)2246 static void atkHypertextInterfaceInit(AtkHypertextIface* iface)
2247 {
2248     iface->get_link = webkitAccessibleHypertextGetLink;
2249     iface->get_n_links = webkitAccessibleHypertextGetNLinks;
2250     iface->get_link_index = webkitAccessibleHypertextGetLinkIndex;
2251 }
2252 
webkitAccessibleHyperlinkImplGetHyperlink(AtkHyperlinkImpl * hyperlink)2253 static AtkHyperlink* webkitAccessibleHyperlinkImplGetHyperlink(AtkHyperlinkImpl* hyperlink)
2254 {
2255     AtkHyperlink* hyperlinkObject = ATK_HYPERLINK(g_object_get_data(G_OBJECT(hyperlink), "hyperlink-object"));
2256     if (!hyperlinkObject) {
2257         hyperlinkObject = ATK_HYPERLINK(webkitAccessibleHyperlinkNew(hyperlink));
2258         g_object_set_data(G_OBJECT(hyperlink), "hyperlink-object", hyperlinkObject);
2259     }
2260     return hyperlinkObject;
2261 }
2262 
atkHyperlinkImplInterfaceInit(AtkHyperlinkImplIface * iface)2263 static void atkHyperlinkImplInterfaceInit(AtkHyperlinkImplIface* iface)
2264 {
2265     iface->get_hyperlink = webkitAccessibleHyperlinkImplGetHyperlink;
2266 }
2267 
documentAttributeValue(AtkDocument * document,const gchar * attribute)2268 static const gchar* documentAttributeValue(AtkDocument* document, const gchar* attribute)
2269 {
2270     Document* coreDocument = core(document)->document();
2271     if (!coreDocument)
2272         return 0;
2273 
2274     String value = String();
2275     if (!g_ascii_strcasecmp(attribute, "DocType") && coreDocument->doctype())
2276         value = coreDocument->doctype()->name();
2277     else if (!g_ascii_strcasecmp(attribute, "Encoding"))
2278         value = coreDocument->charset();
2279     else if (!g_ascii_strcasecmp(attribute, "URI"))
2280         value = coreDocument->documentURI();
2281     if (!value.isEmpty())
2282         return returnString(value);
2283 
2284     return 0;
2285 }
2286 
webkit_accessible_document_get_attribute_value(AtkDocument * document,const gchar * attribute)2287 static const gchar* webkit_accessible_document_get_attribute_value(AtkDocument* document, const gchar* attribute)
2288 {
2289     return documentAttributeValue(document, attribute);
2290 }
2291 
webkit_accessible_document_get_attributes(AtkDocument * document)2292 static AtkAttributeSet* webkit_accessible_document_get_attributes(AtkDocument* document)
2293 {
2294     AtkAttributeSet* attributeSet = 0;
2295     const gchar* attributes [] = {"DocType", "Encoding", "URI"};
2296 
2297     for (unsigned i = 0; i < G_N_ELEMENTS(attributes); i++) {
2298         const gchar* value = documentAttributeValue(document, attributes[i]);
2299         if (value)
2300             attributeSet = addAttributeToSet(attributeSet, attributes[i], value);
2301     }
2302 
2303     return attributeSet;
2304 }
2305 
webkit_accessible_document_get_locale(AtkDocument * document)2306 static const gchar* webkit_accessible_document_get_locale(AtkDocument* document)
2307 {
2308 
2309     // TODO: Should we fall back on lang xml:lang when the following comes up empty?
2310     String language = core(document)->language();
2311     if (!language.isEmpty())
2312         return returnString(language);
2313 
2314     return 0;
2315 }
2316 
atk_document_interface_init(AtkDocumentIface * iface)2317 static void atk_document_interface_init(AtkDocumentIface* iface)
2318 {
2319     iface->get_document_attribute_value = webkit_accessible_document_get_attribute_value;
2320     iface->get_document_attributes = webkit_accessible_document_get_attributes;
2321     iface->get_document_locale = webkit_accessible_document_get_locale;
2322 }
2323 
2324 
webkitAccessibleValueGetCurrentValue(AtkValue * value,GValue * gValue)2325 static void webkitAccessibleValueGetCurrentValue(AtkValue* value, GValue* gValue)
2326 {
2327     memset(gValue,  0, sizeof(GValue));
2328     g_value_init(gValue, G_TYPE_DOUBLE);
2329     g_value_set_double(gValue, core(value)->valueForRange());
2330 }
2331 
webkitAccessibleValueGetMaximumValue(AtkValue * value,GValue * gValue)2332 static void webkitAccessibleValueGetMaximumValue(AtkValue* value, GValue* gValue)
2333 {
2334     memset(gValue,  0, sizeof(GValue));
2335     g_value_init(gValue, G_TYPE_DOUBLE);
2336     g_value_set_double(gValue, core(value)->maxValueForRange());
2337 }
2338 
webkitAccessibleValueGetMinimumValue(AtkValue * value,GValue * gValue)2339 static void webkitAccessibleValueGetMinimumValue(AtkValue* value, GValue* gValue)
2340 {
2341     memset(gValue,  0, sizeof(GValue));
2342     g_value_init(gValue, G_TYPE_DOUBLE);
2343     g_value_set_double(gValue, core(value)->minValueForRange());
2344 }
2345 
webkitAccessibleValueSetCurrentValue(AtkValue * value,const GValue * gValue)2346 static gboolean webkitAccessibleValueSetCurrentValue(AtkValue* value, const GValue* gValue)
2347 {
2348     if (!G_VALUE_HOLDS_DOUBLE(gValue) && !G_VALUE_HOLDS_INT(gValue))
2349         return FALSE;
2350 
2351     AccessibilityObject* coreObject = core(value);
2352     if (!coreObject->canSetValueAttribute())
2353         return FALSE;
2354 
2355     if (G_VALUE_HOLDS_DOUBLE(gValue))
2356         coreObject->setValue(String::number(g_value_get_double(gValue)));
2357     else
2358         coreObject->setValue(String::number(g_value_get_int(gValue)));
2359 
2360     return TRUE;
2361 }
2362 
webkitAccessibleValueGetMinimumIncrement(AtkValue * value,GValue * gValue)2363 static void webkitAccessibleValueGetMinimumIncrement(AtkValue* value, GValue* gValue)
2364 {
2365     memset(gValue,  0, sizeof(GValue));
2366     g_value_init(gValue, G_TYPE_DOUBLE);
2367 
2368     // There's not such a thing in the WAI-ARIA specification, thus return zero.
2369     g_value_set_double(gValue, 0.0);
2370 }
2371 
atkValueInterfaceInit(AtkValueIface * iface)2372 static void atkValueInterfaceInit(AtkValueIface* iface)
2373 {
2374     iface->get_current_value = webkitAccessibleValueGetCurrentValue;
2375     iface->get_maximum_value = webkitAccessibleValueGetMaximumValue;
2376     iface->get_minimum_value = webkitAccessibleValueGetMinimumValue;
2377     iface->set_current_value = webkitAccessibleValueSetCurrentValue;
2378     iface->get_minimum_increment = webkitAccessibleValueGetMinimumIncrement;
2379 }
2380 
2381 static const GInterfaceInfo AtkInterfacesInitFunctions[] = {
2382     {(GInterfaceInitFunc)atk_action_interface_init,
2383      (GInterfaceFinalizeFunc) 0, 0},
2384     {(GInterfaceInitFunc)atk_selection_interface_init,
2385      (GInterfaceFinalizeFunc) 0, 0},
2386     {(GInterfaceInitFunc)atk_editable_text_interface_init,
2387      (GInterfaceFinalizeFunc) 0, 0},
2388     {(GInterfaceInitFunc)atk_text_interface_init,
2389      (GInterfaceFinalizeFunc) 0, 0},
2390     {(GInterfaceInitFunc)atk_component_interface_init,
2391      (GInterfaceFinalizeFunc) 0, 0},
2392     {(GInterfaceInitFunc)atk_image_interface_init,
2393      (GInterfaceFinalizeFunc) 0, 0},
2394     {(GInterfaceInitFunc)atk_table_interface_init,
2395      (GInterfaceFinalizeFunc) 0, 0},
2396     {(GInterfaceInitFunc)atkHypertextInterfaceInit,
2397      (GInterfaceFinalizeFunc) 0, 0},
2398     {(GInterfaceInitFunc)atkHyperlinkImplInterfaceInit,
2399      (GInterfaceFinalizeFunc) 0, 0},
2400     {(GInterfaceInitFunc)atk_document_interface_init,
2401      (GInterfaceFinalizeFunc) 0, 0},
2402     {(GInterfaceInitFunc)atkValueInterfaceInit,
2403      (GInterfaceFinalizeFunc) 0, 0}
2404 };
2405 
2406 enum WAIType {
2407     WAI_ACTION,
2408     WAI_SELECTION,
2409     WAI_EDITABLE_TEXT,
2410     WAI_TEXT,
2411     WAI_COMPONENT,
2412     WAI_IMAGE,
2413     WAI_TABLE,
2414     WAI_HYPERTEXT,
2415     WAI_HYPERLINK,
2416     WAI_DOCUMENT,
2417     WAI_VALUE,
2418 };
2419 
GetAtkInterfaceTypeFromWAIType(WAIType type)2420 static GType GetAtkInterfaceTypeFromWAIType(WAIType type)
2421 {
2422     switch (type) {
2423     case WAI_ACTION:
2424         return ATK_TYPE_ACTION;
2425     case WAI_SELECTION:
2426         return ATK_TYPE_SELECTION;
2427     case WAI_EDITABLE_TEXT:
2428         return ATK_TYPE_EDITABLE_TEXT;
2429     case WAI_TEXT:
2430         return ATK_TYPE_TEXT;
2431     case WAI_COMPONENT:
2432         return ATK_TYPE_COMPONENT;
2433     case WAI_IMAGE:
2434         return ATK_TYPE_IMAGE;
2435     case WAI_TABLE:
2436         return ATK_TYPE_TABLE;
2437     case WAI_HYPERTEXT:
2438         return ATK_TYPE_HYPERTEXT;
2439     case WAI_HYPERLINK:
2440         return ATK_TYPE_HYPERLINK_IMPL;
2441     case WAI_DOCUMENT:
2442         return ATK_TYPE_DOCUMENT;
2443     case WAI_VALUE:
2444         return ATK_TYPE_VALUE;
2445     }
2446 
2447     return G_TYPE_INVALID;
2448 }
2449 
getInterfaceMaskFromObject(AccessibilityObject * coreObject)2450 static guint16 getInterfaceMaskFromObject(AccessibilityObject* coreObject)
2451 {
2452     guint16 interfaceMask = 0;
2453 
2454     // Component interface is always supported
2455     interfaceMask |= 1 << WAI_COMPONENT;
2456 
2457     AccessibilityRole role = coreObject->roleValue();
2458 
2459     // Action
2460     // As the implementation of the AtkAction interface is a very
2461     // basic one (just relays in executing the default action for each
2462     // object, and only supports having one action per object), it is
2463     // better just to implement this interface for every instance of
2464     // the WebKitAccessible class and let WebCore decide what to do.
2465     interfaceMask |= 1 << WAI_ACTION;
2466 
2467     // Selection
2468     if (coreObject->isListBox() || coreObject->isMenuList())
2469         interfaceMask |= 1 << WAI_SELECTION;
2470 
2471     // Get renderer if available.
2472     RenderObject* renderer = 0;
2473     if (coreObject->isAccessibilityRenderObject())
2474         renderer = coreObject->renderer();
2475 
2476     // Hyperlink (links and embedded objects).
2477     if (coreObject->isLink() || (renderer && renderer->isReplaced()))
2478         interfaceMask |= 1 << WAI_HYPERLINK;
2479 
2480     // Text & Editable Text
2481     if (role == StaticTextRole || coreObject->isMenuListOption())
2482         interfaceMask |= 1 << WAI_TEXT;
2483     else {
2484         if (coreObject->isTextControl()) {
2485             interfaceMask |= 1 << WAI_TEXT;
2486             if (!coreObject->isReadOnly())
2487                 interfaceMask |= 1 << WAI_EDITABLE_TEXT;
2488         } else {
2489             if (role != TableRole) {
2490                 interfaceMask |= 1 << WAI_HYPERTEXT;
2491                 if (renderer && renderer->childrenInline())
2492                     interfaceMask |= 1 << WAI_TEXT;
2493             }
2494 
2495             // Add the TEXT interface for list items whose
2496             // first accessible child has a text renderer
2497             if (role == ListItemRole) {
2498                 AccessibilityObject::AccessibilityChildrenVector children = coreObject->children();
2499                 if (children.size()) {
2500                     AccessibilityObject* axRenderChild = children.at(0).get();
2501                     interfaceMask |= getInterfaceMaskFromObject(axRenderChild);
2502                 }
2503             }
2504         }
2505     }
2506 
2507     // Image
2508     if (coreObject->isImage())
2509         interfaceMask |= 1 << WAI_IMAGE;
2510 
2511     // Table
2512     if (role == TableRole)
2513         interfaceMask |= 1 << WAI_TABLE;
2514 
2515     // Document
2516     if (role == WebAreaRole)
2517         interfaceMask |= 1 << WAI_DOCUMENT;
2518 
2519     // Value
2520     if (role == SliderRole)
2521         interfaceMask |= 1 << WAI_VALUE;
2522 
2523     return interfaceMask;
2524 }
2525 
getUniqueAccessibilityTypeName(guint16 interfaceMask)2526 static const char* getUniqueAccessibilityTypeName(guint16 interfaceMask)
2527 {
2528 #define WAI_TYPE_NAME_LEN (30) /* Enough for prefix + 5 hex characters (max) */
2529     static char name[WAI_TYPE_NAME_LEN + 1];
2530 
2531     g_sprintf(name, "WAIType%x", interfaceMask);
2532     name[WAI_TYPE_NAME_LEN] = '\0';
2533 
2534     return name;
2535 }
2536 
getAccessibilityTypeFromObject(AccessibilityObject * coreObject)2537 static GType getAccessibilityTypeFromObject(AccessibilityObject* coreObject)
2538 {
2539     static const GTypeInfo typeInfo = {
2540         sizeof(WebKitAccessibleClass),
2541         (GBaseInitFunc) 0,
2542         (GBaseFinalizeFunc) 0,
2543         (GClassInitFunc) 0,
2544         (GClassFinalizeFunc) 0,
2545         0, /* class data */
2546         sizeof(WebKitAccessible), /* instance size */
2547         0, /* nb preallocs */
2548         (GInstanceInitFunc) 0,
2549         0 /* value table */
2550     };
2551 
2552     guint16 interfaceMask = getInterfaceMaskFromObject(coreObject);
2553     const char* atkTypeName = getUniqueAccessibilityTypeName(interfaceMask);
2554     GType type = g_type_from_name(atkTypeName);
2555     if (type)
2556         return type;
2557 
2558     type = g_type_register_static(WEBKIT_TYPE_ACCESSIBLE,
2559                                   atkTypeName,
2560                                   &typeInfo, GTypeFlags(0));
2561     for (guint i = 0; i < G_N_ELEMENTS(AtkInterfacesInitFunctions); i++) {
2562         if (interfaceMask & (1 << i))
2563             g_type_add_interface_static(type,
2564                                         GetAtkInterfaceTypeFromWAIType(static_cast<WAIType>(i)),
2565                                         &AtkInterfacesInitFunctions[i]);
2566     }
2567 
2568     return type;
2569 }
2570 
webkit_accessible_new(AccessibilityObject * coreObject)2571 WebKitAccessible* webkit_accessible_new(AccessibilityObject* coreObject)
2572 {
2573     GType type = getAccessibilityTypeFromObject(coreObject);
2574     AtkObject* object = static_cast<AtkObject*>(g_object_new(type, 0));
2575 
2576     atk_object_initialize(object, coreObject);
2577 
2578     return WEBKIT_ACCESSIBLE(object);
2579 }
2580 
webkit_accessible_get_accessibility_object(WebKitAccessible * accessible)2581 AccessibilityObject* webkit_accessible_get_accessibility_object(WebKitAccessible* accessible)
2582 {
2583     return accessible->m_object;
2584 }
2585 
webkit_accessible_detach(WebKitAccessible * accessible)2586 void webkit_accessible_detach(WebKitAccessible* accessible)
2587 {
2588     ASSERT(accessible->m_object);
2589 
2590     if (core(accessible)->roleValue() == WebAreaRole)
2591         g_signal_emit_by_name(accessible, "state-change", "defunct", true);
2592 
2593     // We replace the WebCore AccessibilityObject with a fallback object that
2594     // provides default implementations to avoid repetitive null-checking after
2595     // detachment.
2596     accessible->m_object = fallbackObject();
2597 }
2598 
webkit_accessible_get_focused_element(WebKitAccessible * accessible)2599 AtkObject* webkit_accessible_get_focused_element(WebKitAccessible* accessible)
2600 {
2601     if (!accessible->m_object)
2602         return 0;
2603 
2604     RefPtr<AccessibilityObject> focusedObj = accessible->m_object->focusedUIElement();
2605     if (!focusedObj)
2606         return 0;
2607 
2608     return focusedObj->wrapper();
2609 }
2610 
objectAndOffsetUnignored(AccessibilityObject * coreObject,int & offset,bool ignoreLinks)2611 AccessibilityObject* objectAndOffsetUnignored(AccessibilityObject* coreObject, int& offset, bool ignoreLinks)
2612 {
2613     // Indication that something bogus has transpired.
2614     offset = -1;
2615 
2616     AccessibilityObject* realObject = coreObject;
2617     if (realObject->accessibilityIsIgnored())
2618         realObject = realObject->parentObjectUnignored();
2619     if (!realObject)
2620         return 0;
2621 
2622     if (ignoreLinks && realObject->isLink())
2623         realObject = realObject->parentObjectUnignored();
2624     if (!realObject)
2625         return 0;
2626 
2627     Node* node = realObject->node();
2628     if (node) {
2629         VisiblePosition startPosition = VisiblePosition(positionBeforeNode(node), DOWNSTREAM);
2630         VisiblePosition endPosition = realObject->selection().visibleEnd();
2631 
2632         if (startPosition == endPosition)
2633             offset = 0;
2634         else if (!isStartOfLine(endPosition)) {
2635             RefPtr<Range> range = makeRange(startPosition, endPosition.previous());
2636             offset = TextIterator::rangeLength(range.get(), true) + 1;
2637         } else {
2638             RefPtr<Range> range = makeRange(startPosition, endPosition);
2639             offset = TextIterator::rangeLength(range.get(), true);
2640         }
2641 
2642     }
2643 
2644     return realObject;
2645 }
2646 
2647 #endif // HAVE(ACCESSIBILITY)
2648