diff options
Diffstat (limited to 'WebCore/accessibility')
9 files changed, 165 insertions, 51 deletions
diff --git a/WebCore/accessibility/AccessibilityImageMapLink.cpp b/WebCore/accessibility/AccessibilityImageMapLink.cpp index 9b77400..870efe3 100644 --- a/WebCore/accessibility/AccessibilityImageMapLink.cpp +++ b/WebCore/accessibility/AccessibilityImageMapLink.cpp @@ -59,7 +59,7 @@ AccessibilityObject* AccessibilityImageMapLink::parentObject() const if (m_parent) return m_parent; - if (!m_mapElement || !m_mapElement->renderer()) + if (!m_mapElement.get() || !m_mapElement->renderer()) return 0; return m_mapElement->document()->axObjectCache()->getOrCreate(m_mapElement->renderer()); @@ -84,12 +84,12 @@ Element* AccessibilityImageMapLink::actionElement() const Element* AccessibilityImageMapLink::anchorElement() const { - return m_areaElement; + return m_areaElement.get(); } KURL AccessibilityImageMapLink::url() const { - if (!m_areaElement) + if (!m_areaElement.get()) return KURL(); return m_areaElement->href(); @@ -121,7 +121,7 @@ String AccessibilityImageMapLink::title() const IntRect AccessibilityImageMapLink::elementRect() const { - if (!m_mapElement || !m_areaElement) + if (!m_mapElement.get() || !m_areaElement.get()) return IntRect(); RenderObject* renderer; diff --git a/WebCore/accessibility/AccessibilityImageMapLink.h b/WebCore/accessibility/AccessibilityImageMapLink.h index e2e1544..011d5de 100644 --- a/WebCore/accessibility/AccessibilityImageMapLink.h +++ b/WebCore/accessibility/AccessibilityImageMapLink.h @@ -44,12 +44,12 @@ public: virtual ~AccessibilityImageMapLink(); void setHTMLAreaElement(HTMLAreaElement* element) { m_areaElement = element; } - HTMLAreaElement* areaElement() const { return m_areaElement; } + HTMLAreaElement* areaElement() const { return m_areaElement.get(); } void setHTMLMapElement(HTMLMapElement* element) { m_mapElement = element; } - HTMLMapElement* mapElement() const { return m_mapElement; } + HTMLMapElement* mapElement() const { return m_mapElement.get(); } - virtual Node* node() const { return m_areaElement; } + virtual Node* node() const { return m_areaElement.get(); } void setParent(AccessibilityObject* parent) { m_parent = parent; } virtual AccessibilityRole roleValue() const; @@ -72,8 +72,8 @@ public: virtual IntRect elementRect() const; private: - HTMLAreaElement* m_areaElement; - HTMLMapElement* m_mapElement; + RefPtr<HTMLAreaElement> m_areaElement; + RefPtr<HTMLMapElement> m_mapElement; AccessibilityObject* m_parent; virtual bool isImageMapLink() const { return true; } diff --git a/WebCore/accessibility/AccessibilityMenuListOption.cpp b/WebCore/accessibility/AccessibilityMenuListOption.cpp index d7473de..5bca580 100644 --- a/WebCore/accessibility/AccessibilityMenuListOption.cpp +++ b/WebCore/accessibility/AccessibilityMenuListOption.cpp @@ -91,7 +91,7 @@ void AccessibilityMenuListOption::setSelected(bool b) String AccessibilityMenuListOption::nameForMSAA() const { - return static_cast<HTMLOptionElement*>(m_element.get())->text(); + return stringValue(); } bool AccessibilityMenuListOption::canSetSelectedAttribute() const @@ -110,4 +110,9 @@ IntRect AccessibilityMenuListOption::elementRect() const return grandparent->elementRect(); } +String AccessibilityMenuListOption::stringValue() const +{ + return static_cast<HTMLOptionElement*>(m_element.get())->text(); +} + } // namespace WebCore diff --git a/WebCore/accessibility/AccessibilityMenuListOption.h b/WebCore/accessibility/AccessibilityMenuListOption.h index 7e27888..9393d56 100644 --- a/WebCore/accessibility/AccessibilityMenuListOption.h +++ b/WebCore/accessibility/AccessibilityMenuListOption.h @@ -59,6 +59,7 @@ private: virtual void setSelected(bool); virtual bool canSetSelectedAttribute() const; virtual IntRect elementRect() const; + virtual String stringValue() const; RefPtr<HTMLElement> m_element; AccessibilityMenuListPopup* m_popup; diff --git a/WebCore/accessibility/AccessibilityObject.cpp b/WebCore/accessibility/AccessibilityObject.cpp index caf12dc..327a736 100644 --- a/WebCore/accessibility/AccessibilityObject.cpp +++ b/WebCore/accessibility/AccessibilityObject.cpp @@ -937,9 +937,8 @@ static ARIARoleMap* createARIARoleMap() { "treeitem", TreeItemRole } }; ARIARoleMap* roleMap = new ARIARoleMap; - - const unsigned numRoles = sizeof(roles) / sizeof(roles[0]); - for (unsigned i = 0; i < numRoles; ++i) + + for (size_t i = 0; i < WTF_ARRAY_LENGTH(roles); ++i) roleMap->set(roles[i].ariaRole, roles[i].webcoreRole); return roleMap; } diff --git a/WebCore/accessibility/AccessibilityRenderObject.cpp b/WebCore/accessibility/AccessibilityRenderObject.cpp index 141d725..3c6079b 100644 --- a/WebCore/accessibility/AccessibilityRenderObject.cpp +++ b/WebCore/accessibility/AccessibilityRenderObject.cpp @@ -1325,17 +1325,30 @@ String AccessibilityRenderObject::ariaDescribedByAttribute() const return accessibilityDescriptionForElements(elements); } + +String AccessibilityRenderObject::ariaAccessibilityDescription() const +{ + const AtomicString& ariaLabel = getAttribute(aria_labelAttr); + if (!ariaLabel.isEmpty()) + return ariaLabel; + + String ariaDescription = ariaDescribedByAttribute(); + if (!ariaDescription.isEmpty()) + return ariaDescription; + + return String(); +} String AccessibilityRenderObject::accessibilityDescription() const { if (!m_renderer) return String(); - const AtomicString& ariaLabel = getAttribute(aria_labelAttr); - if (!ariaLabel.isEmpty()) - return ariaLabel; + // Static text should not have a description, it should only have a stringValue. + if (roleValue() == StaticTextRole) + return String(); - String ariaDescription = ariaDescribedByAttribute(); + String ariaDescription = ariaAccessibilityDescription(); if (!ariaDescription.isEmpty()) return ariaDescription; @@ -1895,7 +1908,7 @@ String AccessibilityRenderObject::text() const { // If this is a user defined static text, use the accessible name computation. if (ariaRoleAttribute() == StaticTextRole) - return accessibilityDescription(); + return ariaAccessibilityDescription(); if (!isTextControl() || isPasswordField()) return String(); diff --git a/WebCore/accessibility/AccessibilityRenderObject.h b/WebCore/accessibility/AccessibilityRenderObject.h index 970ef9f..a01008a 100644 --- a/WebCore/accessibility/AccessibilityRenderObject.h +++ b/WebCore/accessibility/AccessibilityRenderObject.h @@ -300,6 +300,7 @@ private: String accessibilityDescriptionForElements(Vector<Element*> &elements) const; void elementsFromAttribute(Vector<Element*>& elements, const QualifiedName& name) const; + String ariaAccessibilityDescription() const; virtual ESpeak speakProperty() const; diff --git a/WebCore/accessibility/gtk/AXObjectCacheAtk.cpp b/WebCore/accessibility/gtk/AXObjectCacheAtk.cpp index 4216be4..c341a2d 100644 --- a/WebCore/accessibility/gtk/AXObjectCacheAtk.cpp +++ b/WebCore/accessibility/gtk/AXObjectCacheAtk.cpp @@ -95,10 +95,19 @@ static void notifyChildrenSelectionChange(AccessibilityObject* object) void AXObjectCache::postPlatformNotification(AccessibilityObject* coreObject, AXNotification notification) { + AtkObject* axObject = coreObject->wrapper(); + if (!axObject) + return; + if (notification == AXCheckedStateChanged) { if (!coreObject->isCheckboxOrRadio()) return; - g_signal_emit_by_name(coreObject->wrapper(), "state-change", "checked", coreObject->isChecked()); + g_signal_emit_by_name(axObject, "state-change", "checked", coreObject->isChecked()); + } else if (notification == AXMenuListValueChanged) { + if (!coreObject->isMenuList()) + return; + g_signal_emit_by_name(axObject, "focus-event", true); + g_signal_emit_by_name(axObject, "state-change", "focused", true); } else if (notification == AXSelectedChildrenChanged) notifyChildrenSelectionChange(coreObject); } diff --git a/WebCore/accessibility/gtk/AccessibilityObjectWrapperAtk.cpp b/WebCore/accessibility/gtk/AccessibilityObjectWrapperAtk.cpp index 726cdb1..ca0d80b 100644 --- a/WebCore/accessibility/gtk/AccessibilityObjectWrapperAtk.cpp +++ b/WebCore/accessibility/gtk/AccessibilityObjectWrapperAtk.cpp @@ -58,6 +58,7 @@ #include "RenderListItem.h" #include "RenderListMarker.h" #include "RenderText.h" +#include "SelectElement.h" #include "TextEncoding.h" #include "TextIterator.h" #include "WebKitAccessibleHyperlink.h" @@ -500,6 +501,12 @@ static void setAtkStateSetFromCoreObject(AccessibilityObject* coreObject, AtkSta atk_state_set_add_state(stateSet, ATK_STATE_SENSITIVE); } + if (coreObject->canSetExpandedAttribute()) + atk_state_set_add_state(stateSet, ATK_STATE_EXPANDABLE); + + if (coreObject->isExpanded()) + atk_state_set_add_state(stateSet, ATK_STATE_EXPANDED); + if (coreObject->canSetFocusAttribute()) atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE); @@ -694,13 +701,44 @@ static void atk_action_interface_init(AtkActionIface* iface) // Selection (for controls) +static AccessibilityObject* listObjectForSelection(AtkSelection* selection) +{ + AccessibilityObject* coreSelection = core(selection); + + // Only list boxes and menu lists supported so far. + if (!coreSelection->isListBox() && !coreSelection->isMenuList()) + return 0; + + // For list boxes the list object is just itself. + if (coreSelection->isListBox()) + return coreSelection; + + // For menu lists we need to return the first accessible child, + // with role MenuListPopupRole, since that's the one holding the list + // of items with role MenuListOptionRole. + AccessibilityObject::AccessibilityChildrenVector children = coreSelection->children(); + if (!children.size()) + return 0; + + AccessibilityObject* listObject = children.at(0).get(); + if (!listObject->isMenuListPopup()) + return 0; + + return listObject; +} + static AccessibilityObject* optionFromList(AtkSelection* selection, gint i) { AccessibilityObject* coreSelection = core(selection); if (!coreSelection || i < 0) return 0; - AccessibilityRenderObject::AccessibilityChildrenVector options = core(selection)->children(); + // Need to select the proper list object depending on the type. + AccessibilityObject* listObject = listObjectForSelection(selection); + if (!listObject) + return 0; + + AccessibilityRenderObject::AccessibilityChildrenVector options = listObject->children(); if (i < static_cast<gint>(options.size())) return options.at(i).get(); @@ -712,14 +750,26 @@ static AccessibilityObject* optionFromSelection(AtkSelection* selection, gint i) // i is the ith selection as opposed to the ith child. AccessibilityObject* coreSelection = core(selection); - if (!coreSelection || i < 0) + if (!coreSelection || !coreSelection->isAccessibilityRenderObject() || i < 0) return 0; AccessibilityRenderObject::AccessibilityChildrenVector selectedItems; if (coreSelection->isListBox()) - static_cast<AccessibilityListBox*>(coreSelection)->selectedChildren(selectedItems); + coreSelection->selectedChildren(selectedItems); + else if (coreSelection->isMenuList()) { + RenderObject* renderer = toAccessibilityRenderObject(coreSelection)->renderer(); + if (!renderer) + return 0; + + SelectElement* selectNode = toSelectElement(static_cast<Element*>(renderer->node())); + int selectedIndex = selectNode->selectedIndex(); + const Vector<Element*> listItems = selectNode->listItems(); - // TODO: Combo boxes + if (selectedIndex < 0 || selectedIndex >= static_cast<int>(listItems.size())) + return 0; + + return optionFromList(selection, selectedIndex); + } if (i < static_cast<gint>(selectedItems.size())) return selectedItems.at(i).get(); @@ -729,11 +779,14 @@ static AccessibilityObject* optionFromSelection(AtkSelection* selection, gint i) static gboolean webkit_accessible_selection_add_selection(AtkSelection* selection, gint i) { + AccessibilityObject* coreSelection = core(selection); + if (!coreSelection) + return false; + AccessibilityObject* option = optionFromList(selection, i); - if (option && core(selection)->isListBox()) { - AccessibilityListBoxOption* listBoxOption = static_cast<AccessibilityListBoxOption*>(option); - listBoxOption->setSelected(true); - return listBoxOption->isSelected(); + if (option && (coreSelection->isListBox() || coreSelection->isMenuList())) { + option->setSelected(true); + return option->isSelected(); } return false; @@ -746,7 +799,7 @@ static gboolean webkit_accessible_selection_clear_selection(AtkSelection* select return false; AccessibilityRenderObject::AccessibilityChildrenVector selectedItems; - if (coreSelection->isListBox()) { + if (coreSelection->isListBox() || coreSelection->isMenuList()) { // Set the list of selected items to an empty list; then verify that it worked. AccessibilityListBox* listBox = static_cast<AccessibilityListBox*>(coreSelection); listBox->setSelectedChildren(selectedItems); @@ -771,32 +824,54 @@ static AtkObject* webkit_accessible_selection_ref_selection(AtkSelection* select static gint webkit_accessible_selection_get_selection_count(AtkSelection* selection) { AccessibilityObject* coreSelection = core(selection); - if (coreSelection && coreSelection->isListBox()) { + if (!coreSelection || !coreSelection->isAccessibilityRenderObject()) + return 0; + + if (coreSelection->isListBox()) { AccessibilityRenderObject::AccessibilityChildrenVector selectedItems; - static_cast<AccessibilityListBox*>(coreSelection)->selectedChildren(selectedItems); + coreSelection->selectedChildren(selectedItems); return static_cast<gint>(selectedItems.size()); } + if (coreSelection->isMenuList()) { + RenderObject* renderer = toAccessibilityRenderObject(coreSelection)->renderer(); + if (!renderer) + return 0; + + SelectElement* selectNode = toSelectElement(static_cast<Element*>(renderer->node())); + int selectedIndex = selectNode->selectedIndex(); + const Vector<Element*> listItems = selectNode->listItems(); + + return selectedIndex >= 0 && selectedIndex < static_cast<int>(listItems.size()); + } + return 0; } static gboolean webkit_accessible_selection_is_child_selected(AtkSelection* selection, gint i) { + AccessibilityObject* coreSelection = core(selection); + if (!coreSelection) + return 0; + AccessibilityObject* option = optionFromList(selection, i); - if (option && core(selection)->isListBox()) - return static_cast<AccessibilityListBoxOption*>(option)->isSelected(); + if (option && (coreSelection->isListBox() || coreSelection->isMenuList())) + return option->isSelected(); return false; } static gboolean webkit_accessible_selection_remove_selection(AtkSelection* selection, gint i) { + AccessibilityObject* coreSelection = core(selection); + if (!coreSelection) + return 0; + // TODO: This is only getting called if i == 0. What is preventing the rest? AccessibilityObject* option = optionFromSelection(selection, i); - if (option && core(selection)->isListBox()) { - AccessibilityListBoxOption* listBoxOption = static_cast<AccessibilityListBoxOption*>(option); - listBoxOption->setSelected(false); - return !listBoxOption->isSelected(); + if (option && (coreSelection->isListBox() || coreSelection->isMenuList())) { + option->setSelected(false); + return !option->isSelected(); } return false; @@ -969,8 +1044,11 @@ static gchar* webkit_accessible_text_get_text(AtkText* text, gint startOffset, g if (coreObject->isTextControl()) ret = coreObject->doAXStringForRange(PlainTextRange(start, length)); - else - ret = coreObject->textUnderElement().substring(start, length); + else { + ret = coreObject->stringValue().substring(start, length); + if (!ret) + ret = coreObject->textUnderElement().substring(start, length); + } if (!ret.length()) { // This can happen at least with anonymous RenderBlocks (e.g. body text amongst paragraphs) @@ -1433,13 +1511,13 @@ static void getSelectionOffsetsForObject(AccessibilityObject* coreObject, Visibl if (!coreObject->isAccessibilityRenderObject()) return; - // Early return if the selection doesn't affect the selected node + // Early return if the selection doesn't affect the selected node. if (!selectionBelongsToObject(coreObject, selection)) return; // We need to find the exact start and end positions in the // selected node that intersects the selection, to later on get - // the right values for the effective start and end offsets + // the right values for the effective start and end offsets. ExceptionCode ec = 0; Position nodeRangeStart; Position nodeRangeEnd; @@ -1449,7 +1527,7 @@ static void getSelectionOffsetsForObject(AccessibilityObject* coreObject, Visibl // If the selection affects the selected node and its first // possible position is also in the selection, we must set // nodeRangeStart to that position, otherwise to the selection's - // start position (it would belong to the node anyway) + // start position (it would belong to the node anyway). Node* firstLeafNode = node->firstDescendant(); if (selRange->isPointInRange(firstLeafNode, 0, ec)) nodeRangeStart = firstPositionInNode(firstLeafNode); @@ -1459,16 +1537,20 @@ static void getSelectionOffsetsForObject(AccessibilityObject* coreObject, Visibl // If the selection affects the selected node and its last // possible position is also in the selection, we must set // nodeRangeEnd to that position, otherwise to the selection's - // end position (it would belong to the node anyway) + // end position (it would belong to the node anyway). Node* lastLeafNode = node->lastDescendant(); if (selRange->isPointInRange(lastLeafNode, lastOffsetInNode(lastLeafNode), ec)) nodeRangeEnd = lastPositionInNode(lastLeafNode); else nodeRangeEnd = selRange->endPosition(); - // Set values for start and end offsets + // Calculate position of the selected range inside the object. + Position parentFirstPosition = firstPositionInNode(node); + RefPtr<Range> rangeInParent = Range::create(node->document(), parentFirstPosition, nodeRangeStart); + + // Set values for start and end offsets. + startOffset = TextIterator::rangeLength(rangeInParent.get()); RefPtr<Range> nodeRange = Range::create(node->document(), nodeRangeStart, nodeRangeEnd); - startOffset = nodeRangeStart.offsetInContainerNode(); endOffset = startOffset + TextIterator::rangeLength(nodeRange.get()); } @@ -2173,19 +2255,23 @@ static guint16 getInterfaceMaskFromObject(AccessibilityObject* coreObject) AccessibilityRole role = coreObject->roleValue(); // Action - if (!coreObject->actionVerb().isEmpty()) { - interfaceMask |= 1 << WAI_ACTION; + // As the implementation of the AtkAction interface is a very + // basic one (just relays in executing the default action for each + // 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; - if (!coreObject->accessibilityIsIgnored() && coreObject->isLink()) - interfaceMask |= 1 << WAI_HYPERLINK; - } + // Hyperlink + if (coreObject->isLink()) + interfaceMask |= 1 << WAI_HYPERLINK; // Selection - if (coreObject->isListBox()) + if (coreObject->isListBox() || coreObject->isMenuList()) interfaceMask |= 1 << WAI_SELECTION; // Text & Editable Text - if (role == StaticTextRole) + if (role == StaticTextRole || coreObject->isMenuListOption()) interfaceMask |= 1 << WAI_TEXT; else if (coreObject->isAccessibilityRenderObject()) { if (coreObject->isTextControl()) { |