Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(42)

Side by Side Diff: Source/WebCore/accessibility/atk/WebKitAccessibleWrapperAtk.cpp

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

Powered by Google App Engine
This is Rietveld 408576698