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