diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/accessibility/atk/WebKitAccessibleWrapperAtk.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebCore/accessibility/atk/WebKitAccessibleWrapperAtk.cpp')
-rw-r--r-- | Source/WebCore/accessibility/atk/WebKitAccessibleWrapperAtk.cpp | 659 |
1 files changed, 306 insertions, 353 deletions
diff --git a/Source/WebCore/accessibility/atk/WebKitAccessibleWrapperAtk.cpp b/Source/WebCore/accessibility/atk/WebKitAccessibleWrapperAtk.cpp index 79eb46d35..86cee4e25 100644 --- a/Source/WebCore/accessibility/atk/WebKitAccessibleWrapperAtk.cpp +++ b/Source/WebCore/accessibility/atk/WebKitAccessibleWrapperAtk.cpp @@ -37,13 +37,17 @@ #include "AXObjectCache.h" #include "AccessibilityList.h" #include "AccessibilityListBoxOption.h" +#include "AccessibilityTable.h" #include "Document.h" #include "Frame.h" #include "FrameView.h" #include "HTMLNames.h" #include "HTMLTableElement.h" #include "HostWindow.h" +#include "RenderAncestorIterator.h" +#include "RenderFieldset.h" #include "RenderObject.h" +#include "SVGElement.h" #include "Settings.h" #include "TextIterator.h" #include "VisibleUnits.h" @@ -57,6 +61,7 @@ #include "WebKitAccessibleInterfaceImage.h" #include "WebKitAccessibleInterfaceSelection.h" #include "WebKitAccessibleInterfaceTable.h" +#include "WebKitAccessibleInterfaceTableCell.h" #include "WebKitAccessibleInterfaceText.h" #include "WebKitAccessibleInterfaceValue.h" #include "WebKitAccessibleUtil.h" @@ -64,10 +69,6 @@ #include <glib/gprintf.h> #include <wtf/text/CString.h> -#if PLATFORM(GTK) -#include <gtk/gtk.h> -#endif - using namespace WebCore; struct _WebKitAccessiblePrivate { @@ -93,7 +94,7 @@ struct _WebKitAccessiblePrivate { static AccessibilityObject* fallbackObject() { - static AccessibilityObject* object = AccessibilityListBoxOption::create().leakRef(); + static AccessibilityObject* object = &AccessibilityListBoxOption::create().leakRef(); return object; } @@ -110,58 +111,24 @@ static const gchar* webkitAccessibleGetName(AtkObject* object) g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0); returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), 0); - AccessibilityObject* coreObject = core(object); - - if (!coreObject->isAccessibilityRenderObject()) - return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName, coreObject->stringValue()); + Vector<AccessibilityText> textOrder; + core(object)->accessibilityText(textOrder); - if (coreObject->isFieldset()) { - AccessibilityObject* label = coreObject->titleUIElement(); - if (label) { - AtkObject* atkObject = label->wrapper(); - if (ATK_IS_TEXT(atkObject)) - return atk_text_get_text(ATK_TEXT(atkObject), 0, -1); - } - } - - if (coreObject->isControl()) { - AccessibilityObject* label = coreObject->correspondingLabelForControlElement(); - if (label) { - AtkObject* atkObject = label->wrapper(); - if (ATK_IS_TEXT(atkObject)) - return atk_text_get_text(ATK_TEXT(atkObject), 0, -1); - } - - // Try text under the node. - String textUnder = coreObject->textUnderElement(); - if (textUnder.length()) - return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName, textUnder); - } - - if (coreObject->isImage() || coreObject->isInputImage()) { - Node* node = coreObject->node(); - if (node && node->isHTMLElement()) { - // Get the attribute rather than altText String so as not to fall back on title. - String alt = toHTMLElement(node)->getAttribute(HTMLNames::altAttr); - if (!alt.isEmpty()) - return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName, alt); - } - } + for (const auto& text : textOrder) { + // FIXME: This check is here because AccessibilityNodeObject::titleElementText() + // appends an empty String for the LabelByElementText source when there is a + // titleUIElement(). Removing this check makes some fieldsets lose their name. + if (text.text.isEmpty()) + continue; - // Fallback for the webArea object: just return the document's title. - if (coreObject->isWebArea()) { - Document* document = coreObject->document(); - if (document) - return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName, document->title()); + // WebCore Accessibility should provide us with the text alternative computation + // in the order defined by that spec. So take the first thing that our platform + // does not expose via the AtkObject description. + if (text.textSource != HelpText && text.textSource != SummaryText) + return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName, text.text); } - // Nothing worked so far, try with the AccessibilityObject's - // title() before going ahead with stringValue(). - String axTitle = accessibilityTitle(coreObject); - if (!axTitle.isEmpty()) - return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName, axTitle); - - return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName, coreObject->stringValue()); + return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName, ""); } static const gchar* webkitAccessibleGetDescription(AtkObject* object) @@ -169,27 +136,26 @@ static const gchar* webkitAccessibleGetDescription(AtkObject* object) g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0); returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), 0); - AccessibilityObject* coreObject = core(object); - Node* node = 0; - if (coreObject->isAccessibilityRenderObject()) - node = coreObject->node(); - if (!node || !node->isHTMLElement() || coreObject->ariaRoleAttribute() != UnknownRole || coreObject->isImage()) - return cacheAndReturnAtkProperty(object, AtkCachedAccessibleDescription, accessibilityDescription(coreObject)); - - // atk_table_get_summary returns an AtkObject. We have no summary object, so expose summary here. - if (coreObject->roleValue() == TableRole) { - String summary = toHTMLTableElement(node)->summary(); - if (!summary.isEmpty()) - return cacheAndReturnAtkProperty(object, AtkCachedAccessibleDescription, summary); - } + Vector<AccessibilityText> textOrder; + core(object)->accessibilityText(textOrder); + + bool nameTextAvailable = false; + for (const auto& text : textOrder) { + // WebCore Accessibility should provide us with the text alternative computation + // in the order defined by that spec. So take the first thing that our platform + // does not expose via the AtkObject name. + if (text.textSource == HelpText || text.textSource == SummaryText) + return cacheAndReturnAtkProperty(object, AtkCachedAccessibleDescription, text.text); - // The title attribute should be reliably available as the object's descripton. - // We do not want to fall back on other attributes in its absence. See bug 25524. - String title = toHTMLElement(node)->title(); - if (!title.isEmpty()) - return cacheAndReturnAtkProperty(object, AtkCachedAccessibleDescription, title); + // If there is no other text alternative, the title tag contents will have been + // used for the AtkObject name. We don't want to duplicate it here. + if (text.textSource == TitleTagText && nameTextAvailable) + return cacheAndReturnAtkProperty(object, AtkCachedAccessibleDescription, text.text); - return cacheAndReturnAtkProperty(object, AtkCachedAccessibleDescription, accessibilityDescription(coreObject)); + nameTextAvailable = true; + } + + return cacheAndReturnAtkProperty(object, AtkCachedAccessibleDescription, ""); } static void removeAtkRelationByType(AtkRelationSet* relationSet, AtkRelationType relationType) @@ -206,57 +172,56 @@ static void removeAtkRelationByType(AtkRelationSet* relationSet, AtkRelationType static void setAtkRelationSetFromCoreObject(AccessibilityObject* coreObject, AtkRelationSet* relationSet) { - if (coreObject->isFieldset()) { - AccessibilityObject* label = coreObject->titleUIElement(); - if (label) { - removeAtkRelationByType(relationSet, ATK_RELATION_LABELLED_BY); - atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABELLED_BY, label->wrapper()); - } - return; - } - - if (coreObject->roleValue() == LegendRole) { - for (AccessibilityObject* parent = coreObject->parentObjectUnignored(); parent; parent = parent->parentObjectUnignored()) { - if (parent->isFieldset()) { - atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABEL_FOR, parent->wrapper()); - break; - } - } - return; - } + // FIXME: We're not implementing all the relation types, most notably the inverse/reciprocal + // types. Filed as bug 155494. + // Elements with aria-labelledby should have the labelled-by relation as per the ARIA AAM spec. + // Controls with a label element and fieldsets with a legend element should also use this relation + // as per the HTML AAM spec. The reciprocal label-for relation should also be used. + removeAtkRelationByType(relationSet, ATK_RELATION_LABELLED_BY); if (coreObject->isControl()) { - AccessibilityObject* label = coreObject->correspondingLabelForControlElement(); - if (label) { - removeAtkRelationByType(relationSet, ATK_RELATION_LABELLED_BY); + if (AccessibilityObject* label = coreObject->correspondingLabelForControlElement()) atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABELLED_BY, label->wrapper()); + } else if (coreObject->isFieldset()) { + if (AccessibilityObject* label = coreObject->titleUIElement()) + atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABELLED_BY, label->wrapper()); + } else if (coreObject->roleValue() == LegendRole) { + if (RenderFieldset* renderFieldset = ancestorsOfType<RenderFieldset>(*coreObject->renderer()).first()) { + AccessibilityObject* fieldset = coreObject->axObjectCache()->getOrCreate(renderFieldset); + atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABEL_FOR, fieldset->wrapper()); } + } else if (AccessibilityObject* control = coreObject->correspondingControlForLabelElement()) { + atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABEL_FOR, control->wrapper()); } else { - AccessibilityObject* control = coreObject->correspondingControlForLabelElement(); - if (control) - atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABEL_FOR, control->wrapper()); + AccessibilityObject::AccessibilityChildrenVector ariaLabelledByElements; + coreObject->ariaLabelledByElements(ariaLabelledByElements); + for (const auto& accessibilityObject : ariaLabelledByElements) + atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABELLED_BY, accessibilityObject->wrapper()); } - // Check whether object supports aria-flowto - if (coreObject->supportsARIAFlowTo()) { - removeAtkRelationByType(relationSet, ATK_RELATION_FLOWS_TO); - AccessibilityObject::AccessibilityChildrenVector ariaFlowToElements; - coreObject->ariaFlowToElements(ariaFlowToElements); - for (const auto& accessibilityObject : ariaFlowToElements) - atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_FLOWS_TO, accessibilityObject->wrapper()); - } - - // Check whether object supports aria-describedby. It provides an additional information for the user. - if (coreObject->supportsARIADescribedBy()) { - removeAtkRelationByType(relationSet, ATK_RELATION_DESCRIBED_BY); - AccessibilityObject::AccessibilityChildrenVector ariaDescribedByElements; - coreObject->ariaDescribedByElements(ariaDescribedByElements); - for (const auto& accessibilityObject : ariaDescribedByElements) - atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_DESCRIBED_BY, accessibilityObject->wrapper()); - } + // Elements with aria-flowto should have the flows-to relation as per the ARIA AAM spec. + removeAtkRelationByType(relationSet, ATK_RELATION_FLOWS_TO); + AccessibilityObject::AccessibilityChildrenVector ariaFlowToElements; + coreObject->ariaFlowToElements(ariaFlowToElements); + for (const auto& accessibilityObject : ariaFlowToElements) + atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_FLOWS_TO, accessibilityObject->wrapper()); + + // Elements with aria-describedby should have the described-by relation as per the ARIA AAM spec. + removeAtkRelationByType(relationSet, ATK_RELATION_DESCRIBED_BY); + AccessibilityObject::AccessibilityChildrenVector ariaDescribedByElements; + coreObject->ariaDescribedByElements(ariaDescribedByElements); + for (const auto& accessibilityObject : ariaDescribedByElements) + atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_DESCRIBED_BY, accessibilityObject->wrapper()); + + // Elements with aria-controls should have the controller-for relation as per the ARIA AAM spec. + removeAtkRelationByType(relationSet, ATK_RELATION_CONTROLLER_FOR); + AccessibilityObject::AccessibilityChildrenVector ariaControls; + coreObject->ariaControlsElements(ariaControls); + for (const auto& accessibilityObject : ariaControls) + atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_CONTROLLER_FOR, accessibilityObject->wrapper()); } -static gpointer webkitAccessibleParentClass = 0; +static gpointer webkitAccessibleParentClass = nullptr; static bool isRootObject(AccessibilityObject* coreObject) { @@ -284,18 +249,6 @@ static AtkObject* atkParentOfRootObject(AtkObject* object) Document* document = coreObject->document(); if (!document) return 0; - -#if PLATFORM(GTK) - HostWindow* hostWindow = document->view()->hostWindow(); - if (hostWindow) { - PlatformPageClient scrollView = hostWindow->platformPageClient(); - if (scrollView) { - GtkWidget* scrollViewParent = gtk_widget_get_parent(scrollView); - if (scrollViewParent) - return gtk_widget_get_accessible(scrollViewParent); - } - } -#endif // PLATFORM(GTK) } if (!coreParent) @@ -327,31 +280,9 @@ static AtkObject* webkitAccessibleGetParent(AtkObject* object) if (!coreParent) return 0; - // We don't expose table rows to Assistive technologies, but we - // need to have them anyway in the hierarchy from WebCore to - // properly perform coordinates calculations when requested. - if (coreParent->isTableRow() && coreObject->isTableCell()) - coreParent = coreParent->parentObjectUnignored(); - return coreParent->wrapper(); } -static gint getNChildrenForTable(AccessibilityObject* coreObject) -{ - const AccessibilityObject::AccessibilityChildrenVector& tableChildren = coreObject->children(); - size_t cellsCount = 0; - - // Look for the actual index of the cell inside the table. - for (const auto& tableChild : tableChildren) { - if (tableChild->isTableRow()) - cellsCount += tableChild->children().size(); - else - cellsCount++; - } - - return cellsCount; -} - static gint webkitAccessibleGetNChildren(AtkObject* object) { g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0); @@ -359,38 +290,9 @@ static gint webkitAccessibleGetNChildren(AtkObject* object) AccessibilityObject* coreObject = core(object); - // Tables should be treated in a different way because rows should - // be bypassed when exposing the accessible hierarchy. - if (coreObject->isAccessibilityTable()) - return getNChildrenForTable(coreObject); - return coreObject->children().size(); } -static AccessibilityObject* getChildForTable(AccessibilityObject* coreObject, gint index) -{ - const AccessibilityObject::AccessibilityChildrenVector& tableChildren = coreObject->children(); - size_t cellsCount = 0; - - // Look for the actual index of the cell inside the table. - size_t current = static_cast<size_t>(index); - for (const auto& tableChild : tableChildren) { - if (tableChild->isTableRow()) { - const AccessibilityObject::AccessibilityChildrenVector& rowChildren = tableChild->children(); - size_t rowChildrenCount = rowChildren.size(); - if (current < cellsCount + rowChildrenCount) - return rowChildren.at(current - cellsCount).get(); - cellsCount += rowChildrenCount; - } else if (cellsCount == current) - return tableChild.get(); - else - cellsCount++; - } - - // Shouldn't reach if the child was found. - return 0; -} - static AtkObject* webkitAccessibleRefChild(AtkObject* object, gint index) { g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0); @@ -400,18 +302,12 @@ static AtkObject* webkitAccessibleRefChild(AtkObject* object, gint index) return 0; AccessibilityObject* coreObject = core(object); - AccessibilityObject* coreChild = 0; - - // Tables are special cases because rows should be bypassed, but - // still taking their cells into account. - if (coreObject->isAccessibilityTable()) - coreChild = getChildForTable(coreObject, index); - else { - const AccessibilityObject::AccessibilityChildrenVector& children = coreObject->children(); - if (static_cast<unsigned>(index) >= children.size()) - return 0; - coreChild = children.at(index).get(); - } + AccessibilityObject* coreChild = nullptr; + + const AccessibilityObject::AccessibilityChildrenVector& children = coreObject->children(); + if (static_cast<size_t>(index) >= children.size()) + return 0; + coreChild = children.at(index).get(); if (!coreChild) return 0; @@ -423,40 +319,6 @@ static AtkObject* webkitAccessibleRefChild(AtkObject* object, gint index) return child; } -static gint getIndexInParentForCellInRow(AccessibilityObject* coreObject) -{ - AccessibilityObject* parent = coreObject->parentObjectUnignored(); - if (!parent) - return -1; - - AccessibilityObject* grandParent = parent->parentObjectUnignored(); - if (!grandParent) - return -1; - - const AccessibilityObject::AccessibilityChildrenVector& rows = grandParent->children(); - size_t previousCellsCount = 0; - - // Look for the actual index of the cell inside the table. - for (const auto& row : rows) { - if (!row->isTableRow()) - continue; - - const AccessibilityObject::AccessibilityChildrenVector& cells = row->children(); - size_t cellsCount = cells.size(); - - if (row == parent) { - for (unsigned j = 0; j < cellsCount; ++j) { - if (cells[j] == coreObject) - return previousCellsCount + j; - } - } - - previousCellsCount += cellsCount; - } - - return -1; -} - static gint webkitAccessibleGetIndexInParent(AtkObject* object) { g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), -1); @@ -480,11 +342,6 @@ static gint webkitAccessibleGetIndexInParent(AtkObject* object) } } - // Need to calculate the index of the cell in the table, as - // rows won't be exposed to assistive technologies. - if (parent && parent->isTableRow() && coreObject->isTableCell()) - return getIndexInParentForCellInRow(coreObject); - if (!parent) return -1; @@ -497,11 +354,9 @@ static AtkAttributeSet* webkitAccessibleGetAttributes(AtkObject* object) g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0); returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), 0); - AtkAttributeSet* attributeSet = 0; + AtkAttributeSet* attributeSet = nullptr; #if PLATFORM(GTK) attributeSet = addToAtkAttributeSet(attributeSet, "toolkit", "WebKitGtk"); -#elif PLATFORM(EFL) - attributeSet = addToAtkAttributeSet(attributeSet, "toolkit", "WebKitEfl"); #endif AccessibilityObject* coreObject = core(object); @@ -511,9 +366,12 @@ static AtkAttributeSet* webkitAccessibleGetAttributes(AtkObject* object) // Hack needed for WebKit2 tests because obtaining an element by its ID // cannot be done from the UIProcess. Assistive technologies have no need // for this information. - Node* node = coreObject->node(); - if (node && node->isElementNode()) { - String id = toElement(node)->getIdAttribute().string(); + Element* element = coreObject->element() ? coreObject->element() : coreObject->actionElement(); + if (element) { + String tagName = element->tagName(); + if (!tagName.isEmpty()) + attributeSet = addToAtkAttributeSet(attributeSet, "tag", tagName.convertToASCIILowercase().utf8().data()); + String id = element->getIdAttribute().string(); if (!id.isEmpty()) attributeSet = addToAtkAttributeSet(attributeSet, "html-id", id.utf8().data()); } @@ -524,9 +382,16 @@ static AtkAttributeSet* webkitAccessibleGetAttributes(AtkObject* object) attributeSet = addToAtkAttributeSet(attributeSet, "level", value.utf8().data()); } + if (coreObject->roleValue() == MathElementRole) { + if (coreObject->isMathMultiscriptObject(PreSuperscript) || coreObject->isMathMultiscriptObject(PreSubscript)) + attributeSet = addToAtkAttributeSet(attributeSet, "multiscript-type", "pre"); + else if (coreObject->isMathMultiscriptObject(PostSuperscript) || coreObject->isMathMultiscriptObject(PostSubscript)) + attributeSet = addToAtkAttributeSet(attributeSet, "multiscript-type", "post"); + } + // Set the 'layout-guess' attribute to help Assistive // Technologies know when an exposed table is not data table. - if (coreObject->isAccessibilityTable() && !coreObject->isDataTable()) + if (is<AccessibilityTable>(*coreObject) && downcast<AccessibilityTable>(*coreObject).isExposableThroughAccessibility() && !coreObject->isDataTable()) attributeSet = addToAtkAttributeSet(attributeSet, "layout-guess", "true"); String placeholder = coreObject->placeholderValue(); @@ -543,32 +408,38 @@ static AtkAttributeSet* webkitAccessibleGetAttributes(AtkObject* object) attributeSet = addToAtkAttributeSet(attributeSet, "sort", sortAttribute.string().utf8().data()); } - // Landmarks will be exposed with xml-roles object attributes, with the exception - // of LandmarkApplicationRole, which will be exposed with ATK_ROLE_EMBEDDED. - AccessibilityRole role = coreObject->roleValue(); - switch (role) { - case LandmarkBannerRole: - attributeSet = addToAtkAttributeSet(attributeSet, "xml-roles", "banner"); - break; - case LandmarkComplementaryRole: - attributeSet = addToAtkAttributeSet(attributeSet, "xml-roles", "complementary"); - break; - case LandmarkContentInfoRole: - attributeSet = addToAtkAttributeSet(attributeSet, "xml-roles", "contentinfo"); - break; - case LandmarkMainRole: - attributeSet = addToAtkAttributeSet(attributeSet, "xml-roles", "main"); - break; - case LandmarkNavigationRole: - attributeSet = addToAtkAttributeSet(attributeSet, "xml-roles", "navigation"); - break; - case LandmarkSearchRole: - attributeSet = addToAtkAttributeSet(attributeSet, "xml-roles", "search"); - break; - default: - break; + if (coreObject->supportsARIAPosInSet()) + attributeSet = addToAtkAttributeSet(attributeSet, "posinset", String::number(coreObject->ariaPosInSet()).utf8().data()); + + if (coreObject->supportsARIASetSize()) + attributeSet = addToAtkAttributeSet(attributeSet, "setsize", String::number(coreObject->ariaSetSize()).utf8().data()); + + String isReadOnly = coreObject->ariaReadOnlyValue(); + if (!isReadOnly.isEmpty()) + attributeSet = addToAtkAttributeSet(attributeSet, "readonly", isReadOnly.utf8().data()); + + String valueDescription = coreObject->valueDescription(); + if (!valueDescription.isEmpty()) + attributeSet = addToAtkAttributeSet(attributeSet, "valuetext", valueDescription.utf8().data()); + + // According to the W3C Core Accessibility API Mappings 1.1, section 5.4.1 General Rules: + // "User agents must expose the WAI-ARIA role string if the API supports a mechanism to do so." + // In the case of ATK, the mechanism to do so is an object attribute pair (xml-roles:"string"). + // The computedRoleString is primarily for testing, and not limited to elements with ARIA roles. + // Because the computedRoleString currently contains the ARIA role string, we'll use it for + // both purposes, as the "computed-role" object attribute for all elements which have a value + // and also via the "xml-roles" attribute for elements with ARIA, as well as for landmarks. + String roleString = coreObject->computedRoleString(); + if (!roleString.isEmpty()) { + if (coreObject->ariaRoleAttribute() != UnknownRole || coreObject->isLandmark()) + attributeSet = addToAtkAttributeSet(attributeSet, "xml-roles", roleString.utf8().data()); + attributeSet = addToAtkAttributeSet(attributeSet, "computed-role", roleString.utf8().data()); } + String roleDescription = coreObject->roleDescription(); + if (!roleDescription.isEmpty()) + attributeSet = addToAtkAttributeSet(attributeSet, "roledescription", roleDescription.utf8().data()); + return attributeSet; } @@ -586,10 +457,17 @@ static AtkRole atkRole(AccessibilityObject* coreObject) case UnknownRole: return ATK_ROLE_UNKNOWN; case AudioRole: +#if ATK_CHECK_VERSION(2, 11, 3) + return ATK_ROLE_AUDIO; +#endif case VideoRole: +#if ATK_CHECK_VERSION(2, 11, 3) + return ATK_ROLE_VIDEO; +#endif return ATK_ROLE_EMBEDDED; case ButtonRole: return ATK_ROLE_PUSH_BUTTON; + case SwitchRole: case ToggleButtonRole: return ATK_ROLE_TOGGLE_BUTTON; case RadioButtonRole: @@ -603,9 +481,14 @@ static AtkRole atkRole(AccessibilityObject* coreObject) return ATK_ROLE_PAGE_TAB_LIST; case TextFieldRole: case TextAreaRole: + case SearchFieldRole: return ATK_ROLE_ENTRY; case StaticTextRole: +#if ATK_CHECK_VERSION(2, 15, 2) + return ATK_ROLE_STATIC; +#else return ATK_ROLE_TEXT; +#endif case OutlineRole: case TreeRole: return ATK_ROLE_TREE; @@ -627,15 +510,13 @@ static AtkRole atkRole(AccessibilityObject* coreObject) // return ATK_ROLE_TABLE_COLUMN_HEADER; // Is this right? return ATK_ROLE_UNKNOWN; // Matches Mozilla case RowRole: - // return ATK_ROLE_TABLE_ROW_HEADER; // Is this right? - return ATK_ROLE_LIST_ITEM; // Matches Mozilla + return ATK_ROLE_TABLE_ROW; case ToolbarRole: return ATK_ROLE_TOOL_BAR; case BusyIndicatorRole: return ATK_ROLE_PROGRESS_BAR; // Is this right? case ProgressIndicatorRole: - // return ATK_ROLE_SPIN_BUTTON; // Some confusion about this role in AccessibilityRenderObject.cpp - return ATK_ROLE_PROGRESS_BAR; + return coreObject->isMeter() ? ATK_ROLE_LEVEL_BAR : ATK_ROLE_PROGRESS_BAR; case WindowRole: return ATK_ROLE_WINDOW; case PopUpButtonRole: @@ -646,31 +527,42 @@ static AtkRole atkRole(AccessibilityObject* coreObject) case SplitterRole: return ATK_ROLE_SEPARATOR; case ColorWellRole: - return ATK_ROLE_COLOR_CHOOSER; +#if PLATFORM(GTK) + // ATK_ROLE_COLOR_CHOOSER is defined as a dialog (i.e. it's what appears when you push the button). + return ATK_ROLE_PUSH_BUTTON; +#endif case ListRole: return ATK_ROLE_LIST; case ScrollBarRole: return ATK_ROLE_SCROLL_BAR; case ScrollAreaRole: return ATK_ROLE_SCROLL_PANE; - case GridRole: // Is this right? + case GridRole: case TableRole: return ATK_ROLE_TABLE; case ApplicationRole: return ATK_ROLE_APPLICATION; - case GroupRole: case RadioGroupRole: + case SVGRootRole: case TabPanelRole: return ATK_ROLE_PANEL; - case RowHeaderRole: // Row headers are cells after all. - case ColumnHeaderRole: // Column headers are cells after all. + case GroupRole: + return coreObject->isStyleFormatGroup() ? ATK_ROLE_SECTION : ATK_ROLE_PANEL; + case RowHeaderRole: + return ATK_ROLE_ROW_HEADER; + case ColumnHeaderRole: + return ATK_ROLE_COLUMN_HEADER; + case CaptionRole: + return ATK_ROLE_CAPTION; case CellRole: - return ATK_ROLE_TABLE_CELL; + case GridCellRole: + return coreObject->inheritsPresentationalRole() ? ATK_ROLE_SECTION : ATK_ROLE_TABLE_CELL; case LinkRole: case WebCoreLinkRole: case ImageMapLinkRole: return ATK_ROLE_LINK; case ImageMapRole: + return ATK_ROLE_IMAGE_MAP; case ImageRole: return ATK_ROLE_IMAGE; case ListMarkerRole: @@ -686,17 +578,27 @@ static AtkRole atkRole(AccessibilityObject* coreObject) case HeadingRole: return ATK_ROLE_HEADING; case ListBoxRole: - return ATK_ROLE_LIST; + // https://rawgit.com/w3c/aria/master/core-aam/core-aam.html#role-map-listbox + return coreObject->isDescendantOfRole(ComboBoxRole) ? ATK_ROLE_MENU : ATK_ROLE_LIST_BOX; case ListItemRole: + return coreObject->inheritsPresentationalRole() ? ATK_ROLE_SECTION : ATK_ROLE_LIST_ITEM; case ListBoxOptionRole: - return ATK_ROLE_LIST_ITEM; + return coreObject->isDescendantOfRole(ComboBoxRole) ? ATK_ROLE_MENU_ITEM : ATK_ROLE_LIST_ITEM; case ParagraphRole: return ATK_ROLE_PARAGRAPH; case LabelRole: case LegendRole: return ATK_ROLE_LABEL; + case BlockquoteRole: +#if ATK_CHECK_VERSION(2, 11, 3) + return ATK_ROLE_BLOCK_QUOTE; +#endif case DivRole: + case PreRole: + case SVGTextRole: return ATK_ROLE_SECTION; + case FooterRole: + return ATK_ROLE_FOOTER; case FormRole: return ATK_ROLE_FORM; case CanvasRole: @@ -711,7 +613,7 @@ static AtkRole atkRole(AccessibilityObject* coreObject) return ATK_ROLE_TOOL_TIP; case WebAreaRole: return ATK_ROLE_DOCUMENT_WEB; - case LandmarkApplicationRole: + case WebApplicationRole: return ATK_ROLE_EMBEDDED; #if ATK_CHECK_VERSION(2, 11, 3) case ApplicationLogRole: @@ -724,11 +626,40 @@ static AtkRole atkRole(AccessibilityObject* coreObject) return ATK_ROLE_DEFINITION; case DocumentMathRole: return ATK_ROLE_MATH; + case MathElementRole: + if (coreObject->isMathRow()) + return ATK_ROLE_PANEL; + if (coreObject->isMathTable()) + return ATK_ROLE_TABLE; + if (coreObject->isMathTableRow()) + return ATK_ROLE_TABLE_ROW; + if (coreObject->isMathTableCell()) + return ATK_ROLE_TABLE_CELL; + if (coreObject->isMathSubscriptSuperscript() || coreObject->isMathMultiscript()) + return ATK_ROLE_SECTION; +#if ATK_CHECK_VERSION(2, 15, 4) + if (coreObject->isMathFraction()) + return ATK_ROLE_MATH_FRACTION; + if (coreObject->isMathSquareRoot() || coreObject->isMathRoot()) + return ATK_ROLE_MATH_ROOT; + if (coreObject->isMathScriptObject(Subscript) + || coreObject->isMathMultiscriptObject(PreSubscript) || coreObject->isMathMultiscriptObject(PostSubscript)) + return ATK_ROLE_SUBSCRIPT; + if (coreObject->isMathScriptObject(Superscript) + || coreObject->isMathMultiscriptObject(PreSuperscript) || coreObject->isMathMultiscriptObject(PostSuperscript)) + return ATK_ROLE_SUPERSCRIPT; +#endif +#if ATK_CHECK_VERSION(2, 15, 2) + if (coreObject->isMathToken()) + return ATK_ROLE_STATIC; +#endif + return ATK_ROLE_UNKNOWN; case LandmarkBannerRole: case LandmarkComplementaryRole: case LandmarkContentInfoRole: case LandmarkMainRole: case LandmarkNavigationRole: + case LandmarkRegionRole: case LandmarkSearchRole: return ATK_ROLE_LANDMARK; #endif @@ -740,6 +671,19 @@ static AtkRole atkRole(AccessibilityObject* coreObject) case DescriptionListDetailRole: return ATK_ROLE_DESCRIPTION_VALUE; #endif + case InlineRole: +#if ATK_CHECK_VERSION(2, 15, 4) + if (coreObject->isSubscriptStyleGroup()) + return ATK_ROLE_SUBSCRIPT; + if (coreObject->isSuperscriptStyleGroup()) + return ATK_ROLE_SUPERSCRIPT; +#endif +#if ATK_CHECK_VERSION(2, 15, 2) + return ATK_ROLE_STATIC; + case SVGTextPathRole: + case SVGTSpanRole: + return ATK_ROLE_STATIC; +#endif default: return ATK_ROLE_UNKNOWN; } @@ -803,16 +747,18 @@ static void setAtkStateSetFromCoreObject(AccessibilityObject* coreObject, AtkSta if (isListBoxOption && coreObject->isSelectedOptionActive()) atk_state_set_add_state(stateSet, ATK_STATE_ACTIVE); + if (coreObject->isBusy()) + atk_state_set_add_state(stateSet, ATK_STATE_BUSY); + +#if ATK_CHECK_VERSION(2,11,2) + if (coreObject->supportsChecked() && coreObject->canSetValueAttribute()) + atk_state_set_add_state(stateSet, ATK_STATE_CHECKABLE); +#endif + if (coreObject->isChecked()) atk_state_set_add_state(stateSet, ATK_STATE_CHECKED); - // FIXME: isReadOnly does not seem to do the right thing for - // controls, so check explicitly for them. In addition, because - // isReadOnly is false for listBoxOptions, we need to add one - // more check so that we do not present them as being "editable". - if ((!coreObject->isReadOnly() - || (coreObject->isControl() && coreObject->canSetValueAttribute())) - && !isListBoxOption) + if ((coreObject->isTextControl() || coreObject->isNonNativeTextControl()) && coreObject->canSetValueAttribute()) atk_state_set_add_state(stateSet, ATK_STATE_EDITABLE); // FIXME: Put both ENABLED and SENSITIVE together here for now @@ -857,6 +803,11 @@ static void setAtkStateSetFromCoreObject(AccessibilityObject* coreObject, AtkSta if (coreObject->isPressed()) atk_state_set_add_state(stateSet, ATK_STATE_PRESSED); +#if ATK_CHECK_VERSION(2,15,3) + if (!coreObject->canSetValueAttribute() && (coreObject->supportsARIAReadOnly())) + atk_state_set_add_state(stateSet, ATK_STATE_READ_ONLY); +#endif + if (coreObject->isRequired()) atk_state_set_add_state(stateSet, ATK_STATE_REQUIRED); @@ -890,10 +841,10 @@ static void setAtkStateSetFromCoreObject(AccessibilityObject* coreObject, AtkSta } // Mutually exclusive, so we group these two - if (coreObject->roleValue() == TextFieldRole) - atk_state_set_add_state(stateSet, ATK_STATE_SINGLE_LINE); - else if (coreObject->roleValue() == TextAreaRole) + if (coreObject->roleValue() == TextAreaRole || coreObject->ariaIsMultiline()) atk_state_set_add_state(stateSet, ATK_STATE_MULTI_LINE); + else if (coreObject->roleValue() == TextFieldRole || coreObject->roleValue() == SearchFieldRole) + atk_state_set_add_state(stateSet, ATK_STATE_SINGLE_LINE); // TODO: ATK_STATE_SENSITIVE @@ -965,7 +916,7 @@ static const gchar* webkitAccessibleGetObjectLocale(AtkObject* object) return cacheAndReturnAtkProperty(object, AtkCachedDocumentLocale, language); } else if (ATK_IS_TEXT(object)) { - const gchar* locale = 0; + const gchar* locale = nullptr; AtkAttributeSet* textAttributes = atk_text_get_default_attributes(ATK_TEXT(object)); for (AtkAttributeSet* attributes = textAttributes; attributes; attributes = attributes->next) { @@ -1046,6 +997,9 @@ static const GInterfaceInfo AtkInterfacesInitFunctions[] = { {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleComponentInterfaceInit), 0, 0}, {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleImageInterfaceInit), 0, 0}, {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleTableInterfaceInit), 0, 0}, +#if ATK_CHECK_VERSION(2,11,90) + {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleTableCellInterfaceInit), 0, 0}, +#endif {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleHypertextInterfaceInit), 0, 0}, {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleHyperlinkImplInterfaceInit), 0, 0}, {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleDocumentInterfaceInit), 0, 0}, @@ -1053,43 +1007,50 @@ static const GInterfaceInfo AtkInterfacesInitFunctions[] = { }; enum WAIType { - WAI_ACTION, - WAI_SELECTION, - WAI_EDITABLE_TEXT, - WAI_TEXT, - WAI_COMPONENT, - WAI_IMAGE, - WAI_TABLE, - WAI_HYPERTEXT, - WAI_HYPERLINK, - WAI_DOCUMENT, - WAI_VALUE, + WAIAction, + WAISelection, + WAIEditableText, + WAIText, + WAIComponent, + WAIImage, + WAITable, +#if ATK_CHECK_VERSION(2,11,90) + WAITableCell, +#endif + WAIHypertext, + WAIHyperlink, + WAIDocument, + WAIValue, }; static GType GetAtkInterfaceTypeFromWAIType(WAIType type) { switch (type) { - case WAI_ACTION: + case WAIAction: return ATK_TYPE_ACTION; - case WAI_SELECTION: + case WAISelection: return ATK_TYPE_SELECTION; - case WAI_EDITABLE_TEXT: + case WAIEditableText: return ATK_TYPE_EDITABLE_TEXT; - case WAI_TEXT: + case WAIText: return ATK_TYPE_TEXT; - case WAI_COMPONENT: + case WAIComponent: return ATK_TYPE_COMPONENT; - case WAI_IMAGE: + case WAIImage: return ATK_TYPE_IMAGE; - case WAI_TABLE: + case WAITable: return ATK_TYPE_TABLE; - case WAI_HYPERTEXT: +#if ATK_CHECK_VERSION(2,11,90) + case WAITableCell: + return ATK_TYPE_TABLE_CELL; +#endif + case WAIHypertext: return ATK_TYPE_HYPERTEXT; - case WAI_HYPERLINK: + case WAIHyperlink: return ATK_TYPE_HYPERLINK_IMPL; - case WAI_DOCUMENT: + case WAIDocument: return ATK_TYPE_DOCUMENT; - case WAI_VALUE: + case WAIValue: return ATK_TYPE_VALUE; } @@ -1099,7 +1060,8 @@ static GType GetAtkInterfaceTypeFromWAIType(WAIType type) static bool roleIsTextType(AccessibilityRole role) { return role == ParagraphRole || role == HeadingRole || role == DivRole || role == CellRole - || role == LinkRole || role == WebCoreLinkRole || role == ListItemRole; + || role == LinkRole || role == WebCoreLinkRole || role == ListItemRole || role == PreRole + || role == GridCellRole; } static guint16 getInterfaceMaskFromObject(AccessibilityObject* coreObject) @@ -1107,7 +1069,7 @@ static guint16 getInterfaceMaskFromObject(AccessibilityObject* coreObject) guint16 interfaceMask = 0; // Component interface is always supported - interfaceMask |= 1 << WAI_COMPONENT; + interfaceMask |= 1 << WAIComponent; AccessibilityRole role = coreObject->roleValue(); @@ -1117,68 +1079,71 @@ static guint16 getInterfaceMaskFromObject(AccessibilityObject* coreObject) // object, and only supports having one action per object), it is // better just to implement this interface for every instance of // the WebKitAccessible class and let WebCore decide what to do. - interfaceMask |= 1 << WAI_ACTION; + interfaceMask |= 1 << WAIAction; // Selection - if (coreObject->isListBox() || coreObject->isMenuList()) - interfaceMask |= 1 << WAI_SELECTION; + if (coreObject->canHaveSelectedChildren() || coreObject->isMenuList()) + interfaceMask |= 1 << WAISelection; // Get renderer if available. - RenderObject* renderer = 0; + RenderObject* renderer = nullptr; if (coreObject->isAccessibilityRenderObject()) renderer = coreObject->renderer(); // Hyperlink (links and embedded objects). if (coreObject->isLink() || (renderer && renderer->isReplaced())) - interfaceMask |= 1 << WAI_HYPERLINK; + interfaceMask |= 1 << WAIHyperlink; - // Text & Editable Text + // Text, Editable Text & Hypertext if (role == StaticTextRole || coreObject->isMenuListOption()) - interfaceMask |= 1 << WAI_TEXT; - else { - if (coreObject->isTextControl()) { - interfaceMask |= 1 << WAI_TEXT; - if (!coreObject->isReadOnly()) - interfaceMask |= 1 << WAI_EDITABLE_TEXT; - } else { - if (role != TableRole) { - interfaceMask |= 1 << WAI_HYPERTEXT; - if ((renderer && renderer->childrenInline()) || roleIsTextType(role)) - interfaceMask |= 1 << WAI_TEXT; - } + interfaceMask |= 1 << WAIText; + else if (coreObject->isTextControl() || coreObject->isNonNativeTextControl()) { + interfaceMask |= 1 << WAIText; + if (coreObject->canSetValueAttribute()) + interfaceMask |= 1 << WAIEditableText; + } else if (!coreObject->isWebArea()) { + if (role != TableRole) { + interfaceMask |= 1 << WAIHypertext; + if ((renderer && renderer->childrenInline()) || roleIsTextType(role) || coreObject->isMathToken()) + interfaceMask |= 1 << WAIText; + } - // Add the TEXT interface for list items whose - // first accessible child has a text renderer - if (role == ListItemRole) { - const AccessibilityObject::AccessibilityChildrenVector& children = coreObject->children(); - if (children.size()) { - AccessibilityObject* axRenderChild = children.at(0).get(); - interfaceMask |= getInterfaceMaskFromObject(axRenderChild); - } + // Add the TEXT interface for list items whose + // first accessible child has a text renderer + if (role == ListItemRole) { + const AccessibilityObject::AccessibilityChildrenVector& children = coreObject->children(); + if (children.size()) { + AccessibilityObject* axRenderChild = children.at(0).get(); + interfaceMask |= getInterfaceMaskFromObject(axRenderChild); } } } // Image if (coreObject->isImage()) - interfaceMask |= 1 << WAI_IMAGE; + interfaceMask |= 1 << WAIImage; // Table - if (role == TableRole) - interfaceMask |= 1 << WAI_TABLE; + if (role == TableRole || role == GridRole) + interfaceMask |= 1 << WAITable; + +#if ATK_CHECK_VERSION(2,11,90) + if (role == CellRole || role == ColumnHeaderRole || role == RowHeaderRole) + interfaceMask |= 1 << WAITableCell; +#endif // Document if (role == WebAreaRole) - interfaceMask |= 1 << WAI_DOCUMENT; + interfaceMask |= 1 << WAIDocument; // Value if (role == SliderRole || role == SpinButtonRole || role == ScrollBarRole || role == ProgressIndicatorRole) - interfaceMask |= 1 << WAI_VALUE; + interfaceMask |= 1 << WAIValue; #if ENABLE(INPUT_TYPE_COLOR) // Color type. if (role == ColorWellRole) - interfaceMask |= 1 << WAI_TEXT; + interfaceMask |= 1 << WAIText; #endif return interfaceMask; @@ -1261,18 +1226,6 @@ bool webkitAccessibleIsDetached(WebKitAccessible* accessible) return accessible->m_object == fallbackObject(); } -AtkObject* webkitAccessibleGetFocusedElement(WebKitAccessible* accessible) -{ - if (!accessible->m_object) - return 0; - - RefPtr<AccessibilityObject> focusedObj = accessible->m_object->focusedUIElement(); - if (!focusedObj) - return 0; - - return focusedObj->wrapper(); -} - AccessibilityObject* objectFocusedAndCaretOffsetUnignored(AccessibilityObject* referenceObject, int& offset) { // Indication that something bogus has transpired. @@ -1314,7 +1267,7 @@ AccessibilityObject* objectFocusedAndCaretOffsetUnignored(AccessibilityObject* r if (referenceObject->isDescendantOfObject(firstUnignoredParent)) referenceObject = firstUnignoredParent; - Node* startNode = 0; + Node* startNode = nullptr; if (firstUnignoredParent != referenceObject || firstUnignoredParent->isTextControl()) { // We need to use the first child's node of the reference // object as the start point to calculate the caret offset @@ -1356,7 +1309,7 @@ AccessibilityObject* objectFocusedAndCaretOffsetUnignored(AccessibilityObject* r const char* cacheAndReturnAtkProperty(AtkObject* object, AtkCachedProperty property, String value) { WebKitAccessiblePrivate* priv = WEBKIT_ACCESSIBLE(object)->priv; - CString* propertyPtr = 0; + CString* propertyPtr = nullptr; switch (property) { case AtkCachedAccessibleName: |