summaryrefslogtreecommitdiff
path: root/Source/WebCore/accessibility
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/accessibility')
-rw-r--r--Source/WebCore/accessibility/AXObjectCache.cpp1698
-rw-r--r--Source/WebCore/accessibility/AXObjectCache.h295
-rw-r--r--Source/WebCore/accessibility/AXTextStateChangeIntent.h97
-rw-r--r--Source/WebCore/accessibility/AccessibilityARIAGrid.cpp78
-rw-r--r--Source/WebCore/accessibility/AccessibilityARIAGrid.h22
-rw-r--r--Source/WebCore/accessibility/AccessibilityARIAGridCell.cpp109
-rw-r--r--Source/WebCore/accessibility/AccessibilityARIAGridCell.h21
-rw-r--r--Source/WebCore/accessibility/AccessibilityARIAGridRow.cpp51
-rw-r--r--Source/WebCore/accessibility/AccessibilityARIAGridRow.h24
-rw-r--r--Source/WebCore/accessibility/AccessibilityAllInOne.cpp8
-rw-r--r--Source/WebCore/accessibility/AccessibilityImageMapLink.cpp43
-rw-r--r--Source/WebCore/accessibility/AccessibilityImageMapLink.h56
-rw-r--r--Source/WebCore/accessibility/AccessibilityList.cpp114
-rw-r--r--Source/WebCore/accessibility/AccessibilityList.h29
-rw-r--r--Source/WebCore/accessibility/AccessibilityListBox.cpp62
-rw-r--r--Source/WebCore/accessibility/AccessibilityListBox.h32
-rw-r--r--Source/WebCore/accessibility/AccessibilityListBoxOption.cpp63
-rw-r--r--Source/WebCore/accessibility/AccessibilityListBoxOption.h47
-rw-r--r--Source/WebCore/accessibility/AccessibilityMediaControls.cpp85
-rw-r--r--Source/WebCore/accessibility/AccessibilityMediaControls.h60
-rw-r--r--Source/WebCore/accessibility/AccessibilityMenuList.cpp54
-rw-r--r--Source/WebCore/accessibility/AccessibilityMenuList.h26
-rw-r--r--Source/WebCore/accessibility/AccessibilityMenuListOption.cpp16
-rw-r--r--Source/WebCore/accessibility/AccessibilityMenuListOption.h34
-rw-r--r--Source/WebCore/accessibility/AccessibilityMenuListPopup.cpp27
-rw-r--r--Source/WebCore/accessibility/AccessibilityMenuListPopup.h27
-rw-r--r--Source/WebCore/accessibility/AccessibilityMockObject.cpp2
-rw-r--r--Source/WebCore/accessibility/AccessibilityMockObject.h30
-rw-r--r--Source/WebCore/accessibility/AccessibilityNodeObject.cpp1065
-rw-r--r--Source/WebCore/accessibility/AccessibilityNodeObject.h224
-rw-r--r--Source/WebCore/accessibility/AccessibilityObject.cpp1568
-rw-r--r--Source/WebCore/accessibility/AccessibilityObject.h350
-rw-r--r--Source/WebCore/accessibility/AccessibilityProgressIndicator.cpp77
-rw-r--r--Source/WebCore/accessibility/AccessibilityProgressIndicator.h30
-rw-r--r--Source/WebCore/accessibility/AccessibilityRenderObject.cpp2227
-rw-r--r--Source/WebCore/accessibility/AccessibilityRenderObject.h361
-rw-r--r--Source/WebCore/accessibility/AccessibilitySVGRoot.cpp8
-rw-r--r--Source/WebCore/accessibility/AccessibilitySVGRoot.h27
-rw-r--r--Source/WebCore/accessibility/AccessibilityScrollView.cpp132
-rw-r--r--Source/WebCore/accessibility/AccessibilityScrollView.h63
-rw-r--r--Source/WebCore/accessibility/AccessibilityScrollbar.cpp20
-rw-r--r--Source/WebCore/accessibility/AccessibilityScrollbar.h28
-rw-r--r--Source/WebCore/accessibility/AccessibilitySlider.cpp53
-rw-r--r--Source/WebCore/accessibility/AccessibilitySlider.h47
-rw-r--r--Source/WebCore/accessibility/AccessibilitySpinButton.cpp38
-rw-r--r--Source/WebCore/accessibility/AccessibilitySpinButton.h46
-rw-r--r--Source/WebCore/accessibility/AccessibilityTable.cpp434
-rw-r--r--Source/WebCore/accessibility/AccessibilityTable.h62
-rw-r--r--Source/WebCore/accessibility/AccessibilityTableCell.cpp358
-rw-r--r--Source/WebCore/accessibility/AccessibilityTableCell.h54
-rw-r--r--Source/WebCore/accessibility/AccessibilityTableColumn.cpp131
-rw-r--r--Source/WebCore/accessibility/AccessibilityTableColumn.h32
-rw-r--r--Source/WebCore/accessibility/AccessibilityTableHeaderContainer.cpp23
-rw-r--r--Source/WebCore/accessibility/AccessibilityTableHeaderContainer.h21
-rw-r--r--Source/WebCore/accessibility/AccessibilityTableRow.cpp90
-rw-r--r--Source/WebCore/accessibility/AccessibilityTableRow.h37
-rw-r--r--Source/WebCore/accessibility/AccessibilityTree.cpp99
-rw-r--r--Source/WebCore/accessibility/AccessibilityTree.h (renamed from Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.h)31
-rw-r--r--Source/WebCore/accessibility/AccessibilityTreeItem.cpp (renamed from Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperBase.h)69
-rw-r--r--Source/WebCore/accessibility/AccessibilityTreeItem.h53
-rw-r--r--Source/WebCore/accessibility/atk/AXObjectCacheAtk.cpp178
-rw-r--r--Source/WebCore/accessibility/atk/AccessibilityObjectAtk.cpp115
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleHyperlink.cpp38
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceAction.cpp20
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceComponent.cpp11
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceDocument.cpp11
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceEditableText.cpp30
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceHypertext.cpp23
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceImage.cpp13
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceSelection.cpp98
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceTable.cpp126
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceTableCell.cpp160
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceTableCell.h13
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceText.cpp900
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceValue.cpp121
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleUtil.cpp27
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleUtil.h28
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleWrapperAtk.cpp736
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleWrapperAtk.h2
-rw-r--r--Source/WebCore/accessibility/ios/AXObjectCacheIOS.mm112
-rw-r--r--Source/WebCore/accessibility/ios/AccessibilityObjectIOS.mm89
-rw-r--r--Source/WebCore/accessibility/ios/WebAccessibilityObjectWrapperIOS.h68
-rw-r--r--Source/WebCore/accessibility/ios/WebAccessibilityObjectWrapperIOS.mm2242
-rw-r--r--Source/WebCore/accessibility/mac/AXObjectCacheMac.mm153
-rw-r--r--Source/WebCore/accessibility/mac/AccessibilityObjectMac.mm87
-rw-r--r--Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperBase.mm404
-rw-r--r--Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.mm3697
-rw-r--r--Source/WebCore/accessibility/win/AXObjectCacheWin.cpp187
-rw-r--r--Source/WebCore/accessibility/win/AccessibilityObjectWin.cpp48
-rw-r--r--Source/WebCore/accessibility/win/AccessibilityObjectWrapperWin.cpp73
-rw-r--r--Source/WebCore/accessibility/win/AccessibilityObjectWrapperWin.h58
91 files changed, 9390 insertions, 11546 deletions
diff --git a/Source/WebCore/accessibility/AXObjectCache.cpp b/Source/WebCore/accessibility/AXObjectCache.cpp
index cd16a9d4b..8eeb73226 100644
--- a/Source/WebCore/accessibility/AXObjectCache.cpp
+++ b/Source/WebCore/accessibility/AXObjectCache.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, 2009, 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2009, 2010, 2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -55,11 +55,15 @@
#include "AccessibilityTableColumn.h"
#include "AccessibilityTableHeaderContainer.h"
#include "AccessibilityTableRow.h"
+#include "AccessibilityTree.h"
+#include "AccessibilityTreeItem.h"
#include "Document.h"
#include "Editor.h"
+#include "ElementIterator.h"
#include "FocusController.h"
#include "Frame.h"
#include "HTMLAreaElement.h"
+#include "HTMLCanvasElement.h"
#include "HTMLImageElement.h"
#include "HTMLInputElement.h"
#include "HTMLLabelElement.h"
@@ -70,13 +74,17 @@
#include "RenderMenuList.h"
#include "RenderMeter.h"
#include "RenderProgress.h"
+#include "RenderSVGRoot.h"
#include "RenderSlider.h"
#include "RenderTable.h"
#include "RenderTableCell.h"
#include "RenderTableRow.h"
#include "RenderView.h"
#include "ScrollView.h"
-#include <wtf/PassRefPtr.h>
+#include "TextBoundaries.h"
+#include "TextIterator.h"
+#include "htmlediting.h"
+#include <wtf/DataLog.h>
#if ENABLE(VIDEO)
#include "MediaControlElements.h"
@@ -86,6 +94,10 @@ namespace WebCore {
using namespace HTMLNames;
+// Post value change notifications for password fields or elements contained in password fields at a 40hz interval to thwart analysis of typing cadence
+static double AccessibilityPasswordValueChangeNotificationInterval = 0.025;
+static double AccessibilityLiveRegionChangedNotificationInterval = 0.020;
+
AccessibilityObjectInclusion AXComputedObjectAttributeCache::getIgnored(AXID id) const
{
HashMap<AXID, CachedAXObjectAttributes>::const_iterator it = m_idMapping.find(id);
@@ -107,23 +119,132 @@ void AXComputedObjectAttributeCache::setIgnored(AXID id, AccessibilityObjectIncl
bool AXObjectCache::gAccessibilityEnabled = false;
bool AXObjectCache::gAccessibilityEnhancedUserInterfaceEnabled = false;
-AXObjectCache::AXObjectCache(const Document* doc)
- : m_notificationPostTimer(this, &AXObjectCache::notificationPostTimerFired)
+void AXObjectCache::enableAccessibility()
+{
+ gAccessibilityEnabled = true;
+}
+
+void AXObjectCache::disableAccessibility()
+{
+ gAccessibilityEnabled = false;
+}
+
+void AXObjectCache::setEnhancedUserInterfaceAccessibility(bool flag)
+{
+ gAccessibilityEnhancedUserInterfaceEnabled = flag;
+#if PLATFORM(MAC)
+#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100
+ if (flag)
+ enableAccessibility();
+#endif
+#endif
+}
+
+AXObjectCache::AXObjectCache(Document& document)
+ : m_document(document)
+ , m_notificationPostTimer(*this, &AXObjectCache::notificationPostTimerFired)
+ , m_passwordNotificationPostTimer(*this, &AXObjectCache::passwordNotificationPostTimerFired)
+ , m_liveRegionChangedPostTimer(*this, &AXObjectCache::liveRegionChangedNotificationPostTimerFired)
+ , m_currentAriaModalNode(nullptr)
{
- m_document = const_cast<Document*>(doc);
+ findAriaModalNodes();
}
AXObjectCache::~AXObjectCache()
{
m_notificationPostTimer.stop();
+ m_liveRegionChangedPostTimer.stop();
+
+ for (const auto& object : m_objects.values()) {
+ detachWrapper(object.get(), CacheDestroyed);
+ object->detach(CacheDestroyed);
+ removeAXID(object.get());
+ }
+}
+
+void AXObjectCache::findAriaModalNodes()
+{
+ // Traverse the DOM tree to look for the aria-modal=true nodes.
+ for (Element* element = ElementTraversal::firstWithin(document().rootNode()); element; element = ElementTraversal::nextIncludingPseudo(*element)) {
+
+ // Must have dialog or alertdialog role
+ if (!nodeHasRole(element, "dialog") && !nodeHasRole(element, "alertdialog"))
+ continue;
+ if (!equalLettersIgnoringASCIICase(element->fastGetAttribute(aria_modalAttr), "true"))
+ continue;
+
+ m_ariaModalNodesSet.add(element);
+ }
+
+ // Set the current valid aria-modal node if possible.
+ updateCurrentAriaModalNode();
+}
- HashMap<AXID, RefPtr<AccessibilityObject> >::iterator end = m_objects.end();
- for (HashMap<AXID, RefPtr<AccessibilityObject> >::iterator it = m_objects.begin(); it != end; ++it) {
- AccessibilityObject* obj = (*it).value.get();
- detachWrapper(obj);
- obj->detach();
- removeAXID(obj);
+void AXObjectCache::updateCurrentAriaModalNode()
+{
+ // There might be multiple nodes with aria-modal=true set.
+ // We use this function to pick the one we want.
+ m_currentAriaModalNode = nullptr;
+ if (m_ariaModalNodesSet.isEmpty())
+ return;
+
+ // We only care about the nodes which are visible.
+ ListHashSet<RefPtr<Node>> visibleNodes;
+ for (auto& object : m_ariaModalNodesSet) {
+ if (isNodeVisible(object))
+ visibleNodes.add(object);
}
+
+ if (visibleNodes.isEmpty())
+ return;
+
+ // If any of the node are keyboard focused, we want to pick that.
+ Node* focusedNode = document().focusedElement();
+ for (auto& object : visibleNodes) {
+ if (focusedNode != nullptr && focusedNode->isDescendantOf(object.get())) {
+ m_currentAriaModalNode = object.get();
+ break;
+ }
+ }
+
+ // If none of the nodes are focused, we want to pick the last dialog in the DOM.
+ if (!m_currentAriaModalNode)
+ m_currentAriaModalNode = visibleNodes.last().get();
+}
+
+bool AXObjectCache::isNodeVisible(Node* node) const
+{
+ if (!is<Element>(node))
+ return false;
+
+ RenderObject* renderer = node->renderer();
+ if (!renderer)
+ return false;
+ const RenderStyle& style = renderer->style();
+ if (style.display() == NONE || style.visibility() != VISIBLE)
+ return false;
+
+ // We also need to consider aria hidden status.
+ if (!isNodeAriaVisible(node))
+ return false;
+
+ return true;
+}
+
+Node* AXObjectCache::ariaModalNode()
+{
+ // This function returns the valid aria modal node.
+ if (m_ariaModalNodesSet.isEmpty())
+ return nullptr;
+
+ // Check the current valid aria modal node first.
+ // Usually when one dialog sets aria-modal=true, that dialog is the one we want.
+ if (isNodeVisible(m_currentAriaModalNode))
+ return m_currentAriaModalNode;
+
+ // Recompute the valid aria modal node when m_currentAriaModalNode is null or hidden.
+ updateCurrentAriaModalNode();
+ return isNodeVisible(m_currentAriaModalNode) ? m_currentAriaModalNode : nullptr;
}
AccessibilityObject* AXObjectCache::focusedImageMapUIElement(HTMLAreaElement* areaElement)
@@ -131,44 +252,41 @@ AccessibilityObject* AXObjectCache::focusedImageMapUIElement(HTMLAreaElement* ar
// Find the corresponding accessibility object for the HTMLAreaElement. This should be
// in the list of children for its corresponding image.
if (!areaElement)
- return 0;
+ return nullptr;
HTMLImageElement* imageElement = areaElement->imageElement();
if (!imageElement)
- return 0;
+ return nullptr;
- AccessibilityObject* axRenderImage = areaElement->document()->axObjectCache()->getOrCreate(imageElement);
+ AccessibilityObject* axRenderImage = areaElement->document().axObjectCache()->getOrCreate(imageElement);
if (!axRenderImage)
- return 0;
+ return nullptr;
- AccessibilityObject::AccessibilityChildrenVector imageChildren = axRenderImage->children();
- unsigned count = imageChildren.size();
- for (unsigned k = 0; k < count; ++k) {
- AccessibilityObject* child = imageChildren[k].get();
- if (!child->isImageMapLink())
+ for (const auto& child : axRenderImage->children()) {
+ if (!is<AccessibilityImageMapLink>(*child))
continue;
- if (static_cast<AccessibilityImageMapLink*>(child)->areaElement() == areaElement)
- return child;
+ if (downcast<AccessibilityImageMapLink>(*child).areaElement() == areaElement)
+ return child.get();
}
- return 0;
+ return nullptr;
}
AccessibilityObject* AXObjectCache::focusedUIElementForPage(const Page* page)
{
if (!gAccessibilityEnabled)
- return 0;
+ return nullptr;
// get the focused node in the page
- Document* focusedDocument = page->focusController()->focusedOrMainFrame()->document();
+ Document* focusedDocument = page->focusController().focusedOrMainFrame().document();
Element* focusedElement = focusedDocument->focusedElement();
- if (focusedElement && isHTMLAreaElement(focusedElement))
- return focusedImageMapUIElement(toHTMLAreaElement(focusedElement));
+ if (is<HTMLAreaElement>(focusedElement))
+ return focusedImageMapUIElement(downcast<HTMLAreaElement>(focusedElement));
AccessibilityObject* obj = focusedDocument->axObjectCache()->getOrCreate(focusedElement ? static_cast<Node*>(focusedElement) : focusedDocument);
if (!obj)
- return 0;
+ return nullptr;
if (obj->shouldFocusActiveDescendant()) {
if (AccessibilityObject* descendant = obj->activeDescendant())
@@ -185,12 +303,12 @@ AccessibilityObject* AXObjectCache::focusedUIElementForPage(const Page* page)
AccessibilityObject* AXObjectCache::get(Widget* widget)
{
if (!widget)
- return 0;
+ return nullptr;
AXID axID = m_widgetObjectMapping.get(widget);
ASSERT(!HashTraits<AXID>::isDeletedValue(axID));
if (!axID)
- return 0;
+ return nullptr;
return m_objects.get(axID);
}
@@ -198,12 +316,12 @@ AccessibilityObject* AXObjectCache::get(Widget* widget)
AccessibilityObject* AXObjectCache::get(RenderObject* renderer)
{
if (!renderer)
- return 0;
+ return nullptr;
AXID axID = m_renderObjectMapping.get(renderer);
ASSERT(!HashTraits<AXID>::isDeletedValue(axID));
if (!axID)
- return 0;
+ return nullptr;
return m_objects.get(axID);
}
@@ -211,7 +329,7 @@ AccessibilityObject* AXObjectCache::get(RenderObject* renderer)
AccessibilityObject* AXObjectCache::get(Node* node)
{
if (!node)
- return 0;
+ return nullptr;
AXID renderID = node->renderer() ? m_renderObjectMapping.get(node->renderer()) : 0;
ASSERT(!HashTraits<AXID>::isDeletedValue(renderID));
@@ -224,14 +342,14 @@ AccessibilityObject* AXObjectCache::get(Node* node)
// rendered, but later something changes and it gets a renderer (like if it's
// reparented).
remove(nodeID);
- return 0;
+ return nullptr;
}
if (renderID)
return m_objects.get(renderID);
if (!nodeID)
- return 0;
+ return nullptr;
return m_objects.get(nodeID);
}
@@ -240,13 +358,19 @@ AccessibilityObject* AXObjectCache::get(Node* node)
// FIXME: This should take a const char*, but one caller passes nullAtom.
bool nodeHasRole(Node* node, const String& role)
{
- if (!node || !node->isElementNode())
+ if (!node || !is<Element>(node))
+ return false;
+
+ auto& roleValue = downcast<Element>(*node).fastGetAttribute(roleAttr);
+ if (role.isNull())
+ return roleValue.isEmpty();
+ if (roleValue.isEmpty())
return false;
- return equalIgnoringCase(toElement(node)->getAttribute(roleAttr), role);
+ return SpaceSplitString(roleValue, true).contains(role);
}
-static PassRefPtr<AccessibilityObject> createFromRenderer(RenderObject* renderer)
+static Ref<AccessibilityObject> createFromRenderer(RenderObject* renderer)
{
// FIXME: How could renderer->node() ever not be an Element?
Node* node = renderer->node();
@@ -258,58 +382,61 @@ static PassRefPtr<AccessibilityObject> createFromRenderer(RenderObject* renderer
return AccessibilityList::create(renderer);
// aria tables
- if (nodeHasRole(node, "grid") || nodeHasRole(node, "treegrid"))
+ if (nodeHasRole(node, "grid") || nodeHasRole(node, "treegrid") || nodeHasRole(node, "table"))
return AccessibilityARIAGrid::create(renderer);
if (nodeHasRole(node, "row"))
return AccessibilityARIAGridRow::create(renderer);
- if (nodeHasRole(node, "gridcell") || nodeHasRole(node, "columnheader") || nodeHasRole(node, "rowheader"))
+ if (nodeHasRole(node, "gridcell") || nodeHasRole(node, "cell") || nodeHasRole(node, "columnheader") || nodeHasRole(node, "rowheader"))
return AccessibilityARIAGridCell::create(renderer);
+ // aria tree
+ if (nodeHasRole(node, "tree"))
+ return AccessibilityTree::create(renderer);
+ if (nodeHasRole(node, "treeitem"))
+ return AccessibilityTreeItem::create(renderer);
+
#if ENABLE(VIDEO)
// media controls
if (node && node->isMediaControlElement())
return AccessibilityMediaControl::create(renderer);
#endif
-#if ENABLE(SVG)
- if (renderer->isSVGRoot())
+ if (is<RenderSVGRoot>(*renderer))
return AccessibilitySVGRoot::create(renderer);
-#endif
- if (renderer->isBoxModelObject()) {
- RenderBoxModelObject* cssBox = toRenderBoxModelObject(renderer);
- if (cssBox->isListBox())
- return AccessibilityListBox::create(toRenderListBox(cssBox));
- if (cssBox->isMenuList())
- return AccessibilityMenuList::create(toRenderMenuList(cssBox));
+ if (is<RenderBoxModelObject>(*renderer)) {
+ RenderBoxModelObject& cssBox = downcast<RenderBoxModelObject>(*renderer);
+ if (is<RenderListBox>(cssBox))
+ return AccessibilityListBox::create(&downcast<RenderListBox>(cssBox));
+ if (is<RenderMenuList>(cssBox))
+ return AccessibilityMenuList::create(&downcast<RenderMenuList>(cssBox));
// standard tables
- if (cssBox->isTable())
- return AccessibilityTable::create(toRenderTable(cssBox));
- if (cssBox->isTableRow())
- return AccessibilityTableRow::create(toRenderTableRow(cssBox));
- if (cssBox->isTableCell())
- return AccessibilityTableCell::create(toRenderTableCell(cssBox));
-
-#if ENABLE(PROGRESS_ELEMENT)
+ if (is<RenderTable>(cssBox))
+ return AccessibilityTable::create(&downcast<RenderTable>(cssBox));
+ if (is<RenderTableRow>(cssBox))
+ return AccessibilityTableRow::create(&downcast<RenderTableRow>(cssBox));
+ if (is<RenderTableCell>(cssBox))
+ return AccessibilityTableCell::create(&downcast<RenderTableCell>(cssBox));
+
// progress bar
- if (cssBox->isProgress())
- return AccessibilityProgressIndicator::create(toRenderProgress(cssBox));
-#endif
+ if (is<RenderProgress>(cssBox))
+ return AccessibilityProgressIndicator::create(&downcast<RenderProgress>(cssBox));
+
#if ENABLE(METER_ELEMENT)
- if (cssBox->isMeter())
- return AccessibilityProgressIndicator::create(toRenderMeter(cssBox));
+ if (is<RenderMeter>(cssBox))
+ return AccessibilityProgressIndicator::create(&downcast<RenderMeter>(cssBox));
#endif
// input type=range
- if (cssBox->isSlider())
- return AccessibilitySlider::create(toRenderSlider(cssBox));
+ if (is<RenderSlider>(cssBox))
+ return AccessibilitySlider::create(&downcast<RenderSlider>(cssBox));
}
return AccessibilityRenderObject::create(renderer);
}
-static PassRefPtr<AccessibilityObject> createFromNode(Node* node)
+static Ref<AccessibilityObject> createFromNode(Node* node)
{
return AccessibilityNodeObject::create(node);
}
@@ -317,20 +444,25 @@ static PassRefPtr<AccessibilityObject> createFromNode(Node* node)
AccessibilityObject* AXObjectCache::getOrCreate(Widget* widget)
{
if (!widget)
- return 0;
+ return nullptr;
if (AccessibilityObject* obj = get(widget))
return obj;
- RefPtr<AccessibilityObject> newObj = 0;
- if (widget->isFrameView())
- newObj = AccessibilityScrollView::create(static_cast<ScrollView*>(widget));
- else if (widget->isScrollbar())
- newObj = AccessibilityScrollbar::create(static_cast<Scrollbar*>(widget));
+ RefPtr<AccessibilityObject> newObj;
+ if (is<ScrollView>(*widget))
+ newObj = AccessibilityScrollView::create(downcast<ScrollView>(widget));
+ else if (is<Scrollbar>(*widget))
+ newObj = AccessibilityScrollbar::create(downcast<Scrollbar>(widget));
// Will crash later if we have two objects for the same widget.
ASSERT(!get(widget));
-
+
+ // Catch the case if an (unsupported) widget type is used. Only FrameView and ScrollBar are supported now.
+ ASSERT(newObj);
+ if (!newObj)
+ return nullptr;
+
getAXID(newObj.get());
m_widgetObjectMapping.set(widget, newObj->axObjectID());
@@ -343,7 +475,7 @@ AccessibilityObject* AXObjectCache::getOrCreate(Widget* widget)
AccessibilityObject* AXObjectCache::getOrCreate(Node* node)
{
if (!node)
- return 0;
+ return nullptr;
if (AccessibilityObject* obj = get(node))
return obj;
@@ -352,20 +484,25 @@ AccessibilityObject* AXObjectCache::getOrCreate(Node* node)
return getOrCreate(node->renderer());
if (!node->parentElement())
- return 0;
+ return nullptr;
// It's only allowed to create an AccessibilityObject from a Node if it's in a canvas subtree.
// Or if it's a hidden element, but we still want to expose it because of other ARIA attributes.
- bool inCanvasSubtree = node->parentElement()->isInCanvasSubtree();
- bool isHidden = !node->renderer() && isNodeAriaVisible(node);
+ bool inCanvasSubtree = lineageOfType<HTMLCanvasElement>(*node->parentElement()).first();
+ bool isHidden = isNodeAriaVisible(node);
bool insideMeterElement = false;
#if ENABLE(METER_ELEMENT)
- insideMeterElement = isHTMLMeterElement(node->parentElement());
+ insideMeterElement = is<HTMLMeterElement>(*node->parentElement());
#endif
if (!inCanvasSubtree && !isHidden && !insideMeterElement)
- return 0;
+ return nullptr;
+
+ // Fallback content is only focusable as long as the canvas is displayed and visible.
+ // Update the style before Element::isFocusable() gets called.
+ if (inCanvasSubtree)
+ node->document().updateStyleIfNeeded();
RefPtr<AccessibilityObject> newObj = createFromNode(node);
@@ -379,14 +516,18 @@ AccessibilityObject* AXObjectCache::getOrCreate(Node* node)
newObj->init();
attachWrapper(newObj.get());
newObj->setLastKnownIsIgnoredValue(newObj->accessibilityIsIgnored());
-
+ // Sometimes asking accessibilityIsIgnored() will cause the newObject to be deallocated, and then
+ // it will disappear when this function is finished, leading to a use-after-free.
+ if (newObj->isDetached())
+ return nullptr;
+
return newObj.get();
}
AccessibilityObject* AXObjectCache::getOrCreate(RenderObject* renderer)
{
if (!renderer)
- return 0;
+ return nullptr;
if (AccessibilityObject* obj = get(renderer))
return obj;
@@ -403,31 +544,35 @@ AccessibilityObject* AXObjectCache::getOrCreate(RenderObject* renderer)
newObj->init();
attachWrapper(newObj.get());
newObj->setLastKnownIsIgnoredValue(newObj->accessibilityIsIgnored());
-
+ // Sometimes asking accessibilityIsIgnored() will cause the newObject to be deallocated, and then
+ // it will disappear when this function is finished, leading to a use-after-free.
+ if (newObj->isDetached())
+ return nullptr;
+
return newObj.get();
}
AccessibilityObject* AXObjectCache::rootObject()
{
if (!gAccessibilityEnabled)
- return 0;
-
- return getOrCreate(m_document->view());
+ return nullptr;
+
+ return getOrCreate(m_document.view());
}
AccessibilityObject* AXObjectCache::rootObjectForFrame(Frame* frame)
{
if (!gAccessibilityEnabled)
- return 0;
+ return nullptr;
if (!frame)
- return 0;
+ return nullptr;
return getOrCreate(frame->view());
}
AccessibilityObject* AXObjectCache::getOrCreate(AccessibilityRole role)
{
- RefPtr<AccessibilityObject> obj = 0;
+ RefPtr<AccessibilityObject> obj = nullptr;
// will be filled in...
switch (role) {
@@ -459,13 +604,13 @@ AccessibilityObject* AXObjectCache::getOrCreate(AccessibilityRole role)
obj = AccessibilitySpinButtonPart::create();
break;
default:
- obj = 0;
+ obj = nullptr;
}
if (obj)
getAXID(obj.get());
else
- return 0;
+ return nullptr;
m_objects.set(obj->axObjectID(), obj);
obj->init();
@@ -483,8 +628,8 @@ void AXObjectCache::remove(AXID axID)
if (!obj)
return;
- detachWrapper(obj);
- obj->detach();
+ detachWrapper(obj, ElementDestroyed);
+ obj->detach(ElementDestroyed, this);
removeAXID(obj);
// finally remove the object
@@ -516,6 +661,12 @@ void AXObjectCache::remove(Node* node)
remove(axID);
m_nodeObjectMapping.remove(node);
+ // Cleanup for aria modal nodes.
+ if (m_currentAriaModalNode == node)
+ m_currentAriaModalNode = nullptr;
+ if (m_ariaModalNodesSet.contains(node))
+ m_ariaModalNodesSet.remove(node);
+
if (node->renderer()) {
remove(node->renderer());
return;
@@ -533,7 +684,7 @@ void AXObjectCache::remove(Widget* view)
}
-#if !PLATFORM(WIN) || OS(WINCE)
+#if !PLATFORM(WIN)
AXID AXObjectCache::platformGenerateAXID() const
{
static AXID lastUsedID = 0;
@@ -598,7 +749,7 @@ void AXObjectCache::textChanged(AccessibilityObject* obj)
bool parentAlreadyExists = obj->parentObjectIfExists();
obj->textChanged();
- postNotification(obj, obj->document(), AXObjectCache::AXTextChanged, true);
+ postNotification(obj, obj->document(), AXObjectCache::AXTextChanged);
if (parentAlreadyExists)
obj->notifyIfIgnoredValueChanged();
}
@@ -610,13 +761,51 @@ void AXObjectCache::updateCacheAfterNodeIsAttached(Node* node)
get(node);
}
-void AXObjectCache::childrenChanged(Node* node)
+void AXObjectCache::handleMenuOpened(Node* node)
+{
+ if (!node || !node->renderer() || !nodeHasRole(node, "menu"))
+ return;
+
+ postNotification(getOrCreate(node), &document(), AXMenuOpened);
+}
+
+void AXObjectCache::handleLiveRegionCreated(Node* node)
+{
+ if (!is<Element>(node) || !node->renderer())
+ return;
+
+ Element* element = downcast<Element>(node);
+ String liveRegionStatus = element->fastGetAttribute(aria_liveAttr);
+ if (liveRegionStatus.isEmpty()) {
+ const AtomicString& ariaRole = element->fastGetAttribute(roleAttr);
+ if (!ariaRole.isEmpty())
+ liveRegionStatus = AccessibilityObject::defaultLiveRegionStatusForRole(AccessibilityObject::ariaRoleToWebCoreRole(ariaRole));
+ }
+
+ if (AccessibilityObject::liveRegionStatusIsEnabled(liveRegionStatus))
+ postNotification(getOrCreate(node), &document(), AXLiveRegionCreated);
+}
+
+void AXObjectCache::childrenChanged(Node* node, Node* newChild)
{
+ if (newChild) {
+ handleMenuOpened(newChild);
+ handleLiveRegionCreated(newChild);
+ }
+
childrenChanged(get(node));
}
-void AXObjectCache::childrenChanged(RenderObject* renderer)
+void AXObjectCache::childrenChanged(RenderObject* renderer, RenderObject* newChild)
{
+ if (!renderer)
+ return;
+
+ if (newChild) {
+ handleMenuOpened(newChild->node());
+ handleLiveRegionCreated(newChild->node());
+ }
+
childrenChanged(get(renderer));
}
@@ -628,40 +817,64 @@ void AXObjectCache::childrenChanged(AccessibilityObject* obj)
obj->childrenChanged();
}
-void AXObjectCache::notificationPostTimerFired(Timer<AXObjectCache>*)
+void AXObjectCache::notificationPostTimerFired()
{
- RefPtr<Document> protectorForCacheOwner(m_document);
-
+ Ref<Document> protectorForCacheOwner(m_document);
m_notificationPostTimer.stop();
-
- unsigned i = 0, count = m_notificationsToPost.size();
- for (i = 0; i < count; ++i) {
- AccessibilityObject* obj = m_notificationsToPost[i].first.get();
+
+ // In tests, posting notifications has a tendency to immediately queue up other notifications, which can lead to unexpected behavior
+ // when the notification list is cleared at the end. Instead copy this list at the start.
+ auto notifications = WTFMove(m_notificationsToPost);
+
+ for (const auto& note : notifications) {
+ AccessibilityObject* obj = note.first.get();
if (!obj->axObjectID())
continue;
+ if (!obj->axObjectCache())
+ continue;
+
#ifndef NDEBUG
// Make sure none of the render views are in the process of being layed out.
// Notifications should only be sent after the renderer has finished
- if (obj->isAccessibilityRenderObject()) {
- AccessibilityRenderObject* renderObj = static_cast<AccessibilityRenderObject*>(obj);
- RenderObject* renderer = renderObj->renderer();
- if (renderer && renderer->view())
- ASSERT(!renderer->view()->layoutState());
+ if (is<AccessibilityRenderObject>(*obj)) {
+ if (auto* renderer = downcast<AccessibilityRenderObject>(*obj).renderer())
+ ASSERT(!renderer->view().layoutState());
}
#endif
+
+ AXNotification notification = note.second;
+
+ // Ensure that this menu really is a menu. We do this check here so that we don't have to create
+ // the axChildren when the menu is marked as opening.
+ if (notification == AXMenuOpened) {
+ obj->updateChildrenIfNecessary();
+ if (obj->roleValue() != MenuRole)
+ continue;
+ }
- AXNotification notification = m_notificationsToPost[i].second;
postPlatformNotification(obj, notification);
if (notification == AXChildrenChanged && obj->parentObjectIfExists() && obj->lastKnownIsIgnoredValue() != obj->accessibilityIsIgnored())
childrenChanged(obj->parentObject());
}
-
- m_notificationsToPost.clear();
+}
+
+void AXObjectCache::passwordNotificationPostTimerFired()
+{
+#if PLATFORM(COCOA)
+ m_passwordNotificationPostTimer.stop();
+
+ // In tests, posting notifications has a tendency to immediately queue up other notifications, which can lead to unexpected behavior
+ // when the notification list is cleared at the end. Instead copy this list at the start.
+ auto notifications = WTFMove(m_passwordNotificationsToPost);
+
+ for (auto& notification : notifications)
+ postTextStateChangePlatformNotification(notification.get(), AXTextEditTypeInsert, " ", VisiblePosition());
+#endif
}
-void AXObjectCache::postNotification(RenderObject* renderer, AXNotification notification, bool postToElement, PostType postType)
+void AXObjectCache::postNotification(RenderObject* renderer, AXNotification notification, PostTarget postTarget, PostType postType)
{
if (!renderer)
return;
@@ -679,10 +892,10 @@ void AXObjectCache::postNotification(RenderObject* renderer, AXNotification noti
if (!renderer)
return;
- postNotification(object.get(), renderer->document(), notification, postToElement, postType);
+ postNotification(object.get(), &renderer->document(), notification, postTarget, postType);
}
-void AXObjectCache::postNotification(Node* node, AXNotification notification, bool postToElement, PostType postType)
+void AXObjectCache::postNotification(Node* node, AXNotification notification, PostTarget postTarget, PostType postType)
{
if (!node)
return;
@@ -700,18 +913,18 @@ void AXObjectCache::postNotification(Node* node, AXNotification notification, bo
if (!node)
return;
- postNotification(object.get(), node->document(), notification, postToElement, postType);
+ postNotification(object.get(), &node->document(), notification, postTarget, postType);
}
-void AXObjectCache::postNotification(AccessibilityObject* object, Document* document, AXNotification notification, bool postToElement, PostType postType)
+void AXObjectCache::postNotification(AccessibilityObject* object, Document* document, AXNotification notification, PostTarget postTarget, PostType postType)
{
stopCachingComputedObjectAttributes();
- if (object && !postToElement)
+ if (object && postTarget == TargetObservableParent)
object = object->observableObject();
if (!object && document)
- object = get(document->renderer());
+ object = get(document->renderView());
if (!object)
return;
@@ -726,33 +939,312 @@ void AXObjectCache::postNotification(AccessibilityObject* object, Document* docu
void AXObjectCache::checkedStateChanged(Node* node)
{
- postNotification(node, AXObjectCache::AXCheckedStateChanged, true);
+ postNotification(node, AXObjectCache::AXCheckedStateChanged);
}
+void AXObjectCache::handleMenuItemSelected(Node* node)
+{
+ if (!node)
+ return;
+
+ if (!nodeHasRole(node, "menuitem") && !nodeHasRole(node, "menuitemradio") && !nodeHasRole(node, "menuitemcheckbox"))
+ return;
+
+ if (!downcast<Element>(*node).focused() && !equalLettersIgnoringASCIICase(downcast<Element>(*node).fastGetAttribute(aria_selectedAttr), "true"))
+ return;
+
+ postNotification(getOrCreate(node), &document(), AXMenuListItemSelected);
+}
+
+void AXObjectCache::handleFocusedUIElementChanged(Node* oldNode, Node* newNode)
+{
+ handleMenuItemSelected(newNode);
+ platformHandleFocusedUIElementChanged(oldNode, newNode);
+}
+
void AXObjectCache::selectedChildrenChanged(Node* node)
{
- // postToElement is false so that you can pass in any child of an element and it will go up the parent tree
+ handleMenuItemSelected(node);
+
+ // postTarget is TargetObservableParent so that you can pass in any child of an element and it will go up the parent tree
// to find the container which should send out the notification.
- postNotification(node, AXSelectedChildrenChanged, false);
+ postNotification(node, AXSelectedChildrenChanged, TargetObservableParent);
}
void AXObjectCache::selectedChildrenChanged(RenderObject* renderer)
{
- // postToElement is false so that you can pass in any child of an element and it will go up the parent tree
+ if (renderer)
+ handleMenuItemSelected(renderer->node());
+
+ // postTarget is TargetObservableParent so that you can pass in any child of an element and it will go up the parent tree
// to find the container which should send out the notification.
- postNotification(renderer, AXSelectedChildrenChanged, false);
+ postNotification(renderer, AXSelectedChildrenChanged, TargetObservableParent);
}
-void AXObjectCache::nodeTextChangeNotification(Node* node, AXTextChange textChange, unsigned offset, const String& text)
+#ifndef NDEBUG
+void AXObjectCache::showIntent(const AXTextStateChangeIntent &intent)
+{
+ switch (intent.type) {
+ case AXTextStateChangeTypeUnknown:
+ dataLog("Unknown");
+ break;
+ case AXTextStateChangeTypeEdit:
+ dataLog("Edit::");
+ break;
+ case AXTextStateChangeTypeSelectionMove:
+ dataLog("Move::");
+ break;
+ case AXTextStateChangeTypeSelectionExtend:
+ dataLog("Extend::");
+ break;
+ case AXTextStateChangeTypeSelectionBoundary:
+ dataLog("Boundary::");
+ break;
+ }
+ switch (intent.type) {
+ case AXTextStateChangeTypeUnknown:
+ break;
+ case AXTextStateChangeTypeEdit:
+ switch (intent.change) {
+ case AXTextEditTypeUnknown:
+ dataLog("Unknown");
+ break;
+ case AXTextEditTypeDelete:
+ dataLog("Delete");
+ break;
+ case AXTextEditTypeInsert:
+ dataLog("Insert");
+ break;
+ case AXTextEditTypeDictation:
+ dataLog("DictationInsert");
+ break;
+ case AXTextEditTypeTyping:
+ dataLog("TypingInsert");
+ break;
+ case AXTextEditTypeCut:
+ dataLog("Cut");
+ break;
+ case AXTextEditTypePaste:
+ dataLog("Paste");
+ break;
+ case AXTextEditTypeAttributesChange:
+ dataLog("AttributesChange");
+ break;
+ }
+ break;
+ case AXTextStateChangeTypeSelectionMove:
+ case AXTextStateChangeTypeSelectionExtend:
+ case AXTextStateChangeTypeSelectionBoundary:
+ switch (intent.selection.direction) {
+ case AXTextSelectionDirectionUnknown:
+ dataLog("Unknown::");
+ break;
+ case AXTextSelectionDirectionBeginning:
+ dataLog("Beginning::");
+ break;
+ case AXTextSelectionDirectionEnd:
+ dataLog("End::");
+ break;
+ case AXTextSelectionDirectionPrevious:
+ dataLog("Previous::");
+ break;
+ case AXTextSelectionDirectionNext:
+ dataLog("Next::");
+ break;
+ case AXTextSelectionDirectionDiscontiguous:
+ dataLog("Discontiguous::");
+ break;
+ }
+ switch (intent.selection.direction) {
+ case AXTextSelectionDirectionUnknown:
+ case AXTextSelectionDirectionBeginning:
+ case AXTextSelectionDirectionEnd:
+ case AXTextSelectionDirectionPrevious:
+ case AXTextSelectionDirectionNext:
+ switch (intent.selection.granularity) {
+ case AXTextSelectionGranularityUnknown:
+ dataLog("Unknown");
+ break;
+ case AXTextSelectionGranularityCharacter:
+ dataLog("Character");
+ break;
+ case AXTextSelectionGranularityWord:
+ dataLog("Word");
+ break;
+ case AXTextSelectionGranularityLine:
+ dataLog("Line");
+ break;
+ case AXTextSelectionGranularitySentence:
+ dataLog("Sentence");
+ break;
+ case AXTextSelectionGranularityParagraph:
+ dataLog("Paragraph");
+ break;
+ case AXTextSelectionGranularityPage:
+ dataLog("Page");
+ break;
+ case AXTextSelectionGranularityDocument:
+ dataLog("Document");
+ break;
+ case AXTextSelectionGranularityAll:
+ dataLog("All");
+ break;
+ }
+ break;
+ case AXTextSelectionDirectionDiscontiguous:
+ break;
+ }
+ break;
+ }
+ dataLog("\n");
+}
+#endif
+
+void AXObjectCache::setTextSelectionIntent(const AXTextStateChangeIntent& intent)
+{
+ m_textSelectionIntent = intent;
+}
+
+void AXObjectCache::setIsSynchronizingSelection(bool isSynchronizing)
+{
+ m_isSynchronizingSelection = isSynchronizing;
+}
+
+static bool isPasswordFieldOrContainedByPasswordField(AccessibilityObject* object)
+{
+ return object && (object->isPasswordField() || object->isContainedByPasswordField());
+}
+
+void AXObjectCache::postTextStateChangeNotification(Node* node, const AXTextStateChangeIntent& intent, const VisibleSelection& selection)
{
if (!node)
return;
+#if PLATFORM(COCOA)
stopCachingComputedObjectAttributes();
- // Delegate on the right platform
- AccessibilityObject* obj = getOrCreate(node);
- nodeTextChangePlatformNotification(obj, textChange, offset, text);
+ postTextStateChangeNotification(getOrCreate(node), intent, selection);
+#else
+ postNotification(node->renderer(), AXObjectCache::AXSelectedTextChanged, TargetObservableParent);
+ UNUSED_PARAM(intent);
+ UNUSED_PARAM(selection);
+#endif
+}
+
+void AXObjectCache::postTextStateChangeNotification(const Position& position, const AXTextStateChangeIntent& intent, const VisibleSelection& selection)
+{
+ Node* node = position.deprecatedNode();
+ if (!node)
+ return;
+
+ stopCachingComputedObjectAttributes();
+
+#if PLATFORM(COCOA)
+ AccessibilityObject* object = getOrCreate(node);
+ if (object && object->accessibilityIsIgnored()) {
+ if (position.atLastEditingPositionForNode()) {
+ if (AccessibilityObject* nextSibling = object->nextSiblingUnignored(1))
+ object = nextSibling;
+ } else if (position.atFirstEditingPositionForNode()) {
+ if (AccessibilityObject* previousSibling = object->previousSiblingUnignored(1))
+ object = previousSibling;
+ }
+ }
+
+ postTextStateChangeNotification(object, intent, selection);
+#else
+ postTextStateChangeNotification(node, intent, selection);
+#endif
+}
+
+void AXObjectCache::postTextStateChangeNotification(AccessibilityObject* object, const AXTextStateChangeIntent& intent, const VisibleSelection& selection)
+{
+ stopCachingComputedObjectAttributes();
+
+#if PLATFORM(COCOA)
+ if (object) {
+ if (isPasswordFieldOrContainedByPasswordField(object))
+ return;
+
+ if (auto observableObject = object->observableObject())
+ object = observableObject;
+ }
+
+ const AXTextStateChangeIntent& newIntent = (intent.type == AXTextStateChangeTypeUnknown || (m_isSynchronizingSelection && m_textSelectionIntent.type != AXTextStateChangeTypeUnknown)) ? m_textSelectionIntent : intent;
+ postTextStateChangePlatformNotification(object, newIntent, selection);
+#else
+ UNUSED_PARAM(object);
+ UNUSED_PARAM(intent);
+ UNUSED_PARAM(selection);
+#endif
+
+ setTextSelectionIntent(AXTextStateChangeIntent());
+ setIsSynchronizingSelection(false);
+}
+
+void AXObjectCache::postTextStateChangeNotification(Node* node, AXTextEditType type, const String& text, const VisiblePosition& position)
+{
+ if (!node)
+ return;
+ ASSERT(type != AXTextEditTypeUnknown);
+
+ stopCachingComputedObjectAttributes();
+
+ AccessibilityObject* object = getOrCreate(node);
+#if PLATFORM(COCOA)
+ if (object) {
+ if (enqueuePasswordValueChangeNotification(object))
+ return;
+ object = object->observableObject();
+ }
+
+ postTextStateChangePlatformNotification(object, type, text, position);
+#else
+ nodeTextChangePlatformNotification(object, textChangeForEditType(type), position.deepEquivalent().deprecatedEditingOffset(), text);
+#endif
+}
+
+void AXObjectCache::postTextReplacementNotification(Node* node, AXTextEditType deletionType, const String& deletedText, AXTextEditType insertionType, const String& insertedText, const VisiblePosition& position)
+{
+ if (!node)
+ return;
+ ASSERT(deletionType == AXTextEditTypeDelete);
+ ASSERT(insertionType == AXTextEditTypeInsert || insertionType == AXTextEditTypeTyping || insertionType == AXTextEditTypeDictation || insertionType == AXTextEditTypePaste);
+
+ stopCachingComputedObjectAttributes();
+
+ AccessibilityObject* object = getOrCreate(node);
+#if PLATFORM(COCOA)
+ if (object) {
+ if (enqueuePasswordValueChangeNotification(object))
+ return;
+ object = object->observableObject();
+ }
+
+ postTextReplacementPlatformNotification(object, deletionType, deletedText, insertionType, insertedText, position);
+#else
+ nodeTextChangePlatformNotification(object, textChangeForEditType(deletionType), position.deepEquivalent().deprecatedEditingOffset(), deletedText);
+ nodeTextChangePlatformNotification(object, textChangeForEditType(insertionType), position.deepEquivalent().deprecatedEditingOffset(), insertedText);
+#endif
+}
+
+bool AXObjectCache::enqueuePasswordValueChangeNotification(AccessibilityObject* object)
+{
+ if (!isPasswordFieldOrContainedByPasswordField(object))
+ return false;
+
+ AccessibilityObject* observableObject = object->observableObject();
+ if (!observableObject) {
+ ASSERT_NOT_REACHED();
+ // return true even though the enqueue didn't happen because this is a password field and caller shouldn't post a notification
+ return true;
+ }
+
+ m_passwordNotificationsToPost.add(observableObject);
+ if (!m_passwordNotificationPostTimer.isActive())
+ m_passwordNotificationPostTimer.startOneShot(AccessibilityPasswordValueChangeNotificationInterval);
+
+ return true;
}
void AXObjectCache::frameLoadingEventNotification(Frame* frame, AXLoadingEvent loadingEvent)
@@ -769,6 +1261,29 @@ void AXObjectCache::frameLoadingEventNotification(Frame* frame, AXLoadingEvent l
frameLoadingEventPlatformNotification(obj, loadingEvent);
}
+void AXObjectCache::postLiveRegionChangeNotification(AccessibilityObject* object)
+{
+ if (m_liveRegionChangedPostTimer.isActive())
+ m_liveRegionChangedPostTimer.stop();
+
+ if (!m_liveRegionObjectsSet.contains(object))
+ m_liveRegionObjectsSet.add(object);
+
+ m_liveRegionChangedPostTimer.startOneShot(AccessibilityLiveRegionChangedNotificationInterval);
+}
+
+void AXObjectCache::liveRegionChangedNotificationPostTimerFired()
+{
+ m_liveRegionChangedPostTimer.stop();
+
+ if (m_liveRegionObjectsSet.isEmpty())
+ return;
+
+ for (auto& object : m_liveRegionObjectsSet)
+ postNotification(object.get(), object->document(), AXObjectCache::AXLiveRegionChanged);
+ m_liveRegionObjectsSet.clear();
+}
+
void AXObjectCache::handleScrollbarUpdate(ScrollView* view)
{
if (!view)
@@ -809,7 +1324,7 @@ void AXObjectCache::handleAttributeChanged(const QualifiedName& attrName, Elemen
handleAriaRoleChanged(element);
else if (attrName == altAttr || attrName == titleAttr)
textChanged(element);
- else if (attrName == forAttr && isHTMLLabelElement(element))
+ else if (attrName == forAttr && is<HTMLLabelElement>(*element))
labelChanged(element);
if (!attrName.localName().string().startsWith("aria-"))
@@ -817,8 +1332,10 @@ void AXObjectCache::handleAttributeChanged(const QualifiedName& attrName, Elemen
if (attrName == aria_activedescendantAttr)
handleActiveDescendantChanged(element);
+ else if (attrName == aria_busyAttr)
+ postNotification(element, AXObjectCache::AXElementBusyChanged);
else if (attrName == aria_valuenowAttr || attrName == aria_valuetextAttr)
- postNotification(element, AXObjectCache::AXValueChanged, true);
+ postNotification(element, AXObjectCache::AXValueChanged);
else if (attrName == aria_labelAttr || attrName == aria_labeledbyAttr || attrName == aria_labelledbyAttr)
textChanged(element);
else if (attrName == aria_checkedAttr)
@@ -828,17 +1345,41 @@ void AXObjectCache::handleAttributeChanged(const QualifiedName& attrName, Elemen
else if (attrName == aria_expandedAttr)
handleAriaExpandedChange(element);
else if (attrName == aria_hiddenAttr)
- childrenChanged(element->parentNode());
+ childrenChanged(element->parentNode(), element);
else if (attrName == aria_invalidAttr)
- postNotification(element, AXObjectCache::AXInvalidStatusChanged, true);
+ postNotification(element, AXObjectCache::AXInvalidStatusChanged);
+ else if (attrName == aria_modalAttr)
+ handleAriaModalChange(element);
else
- postNotification(element, AXObjectCache::AXAriaAttributeChanged, true);
+ postNotification(element, AXObjectCache::AXAriaAttributeChanged);
+}
+
+void AXObjectCache::handleAriaModalChange(Node* node)
+{
+ if (!is<Element>(node))
+ return;
+
+ if (!nodeHasRole(node, "dialog") && !nodeHasRole(node, "alertdialog"))
+ return;
+
+ stopCachingComputedObjectAttributes();
+ if (equalLettersIgnoringASCIICase(downcast<Element>(*node).fastGetAttribute(aria_modalAttr), "true")) {
+ // Add the newly modified node to the modal nodes set, and set it to be the current valid aria modal node.
+ // We will recompute the current valid aria modal node in ariaModalNode() when this node is not visible.
+ m_ariaModalNodesSet.add(node);
+ m_currentAriaModalNode = node;
+ } else {
+ // Remove the node from the modal nodes set. There might be other visible modal nodes, so we recompute here.
+ m_ariaModalNodesSet.remove(node);
+ updateCurrentAriaModalNode();
+ }
+ startCachingComputedObjectAttributesUntilTreeMutates();
}
void AXObjectCache::labelChanged(Element* element)
{
- ASSERT(isHTMLLabelElement(element));
- HTMLElement* correspondingControl = toHTMLLabelElement(element)->control();
+ ASSERT(is<HTMLLabelElement>(*element));
+ HTMLElement* correspondingControl = downcast<HTMLLabelElement>(*element).control();
textChanged(correspondingControl);
}
@@ -851,13 +1392,12 @@ void AXObjectCache::recomputeIsIgnored(RenderObject* renderer)
void AXObjectCache::startCachingComputedObjectAttributesUntilTreeMutates()
{
if (!m_computedObjectAttributeCache)
- m_computedObjectAttributeCache = AXComputedObjectAttributeCache::create();
+ m_computedObjectAttributeCache = std::make_unique<AXComputedObjectAttributeCache>();
}
void AXObjectCache::stopCachingComputedObjectAttributes()
{
- if (m_computedObjectAttributeCache)
- m_computedObjectAttributeCache.clear();
+ m_computedObjectAttributeCache = nullptr;
}
VisiblePosition AXObjectCache::visiblePositionForTextMarkerData(TextMarkerData& textMarkerData)
@@ -875,7 +1415,7 @@ VisiblePosition AXObjectCache::visiblePositionForTextMarkerData(TextMarkerData&
if (!renderer)
return VisiblePosition();
- AXObjectCache* cache = renderer->document()->axObjectCache();
+ AXObjectCache* cache = renderer->document().axObjectCache();
if (!cache->isIDinUse(textMarkerData.axID))
return VisiblePosition();
@@ -885,6 +1425,449 @@ VisiblePosition AXObjectCache::visiblePositionForTextMarkerData(TextMarkerData&
return visiblePos;
}
+CharacterOffset AXObjectCache::characterOffsetForTextMarkerData(TextMarkerData& textMarkerData)
+{
+ if (!isNodeInUse(textMarkerData.node))
+ return CharacterOffset();
+
+ if (textMarkerData.ignored)
+ return CharacterOffset();
+
+ return CharacterOffset(textMarkerData.node, textMarkerData.characterStartIndex, textMarkerData.characterOffset);
+}
+
+CharacterOffset AXObjectCache::traverseToOffsetInRange(RefPtr<Range>range, int offset, bool toNodeEnd, bool stayWithinRange)
+{
+ if (!range)
+ return CharacterOffset();
+
+ int offsetInCharacter = 0;
+ int offsetSoFar = 0;
+ int remaining = 0;
+ int lastLength = 0;
+ Node* currentNode = nullptr;
+ bool finished = false;
+ int lastStartOffset = 0;
+
+ TextIterator iterator(range.get());
+
+ // When the range has zero length, there might be replaced node or brTag that we need to increment the characterOffset.
+ if (iterator.atEnd()) {
+ currentNode = &range->startContainer();
+ lastStartOffset = range->startOffset();
+ if (offset > 0 || toNodeEnd) {
+ if (AccessibilityObject::replacedNodeNeedsCharacter(currentNode) || (currentNode->renderer() && currentNode->renderer()->isBR()))
+ offsetSoFar++;
+ lastLength = offsetSoFar;
+
+ // When going backwards, stayWithinRange is false.
+ // Here when we don't have any character to move and we are going backwards, we traverse to the previous node.
+ if (!lastLength && toNodeEnd && !stayWithinRange) {
+ if (Node* preNode = previousNode(currentNode))
+ return traverseToOffsetInRange(rangeForNodeContents(preNode), offset, toNodeEnd);
+ return CharacterOffset();
+ }
+ }
+ }
+
+ // Sometimes text contents in a node are splitted into several iterations, so that iterator.range()->startOffset()
+ // might not be the correct character count. Here we use a previousNode object to keep track of that.
+ Node* previousNode = nullptr;
+ for (; !iterator.atEnd(); iterator.advance()) {
+ int currentLength = iterator.text().length();
+ bool hasReplacedNodeOrBR = false;
+
+ Node& node = iterator.range()->startContainer();
+ currentNode = &node;
+ // When currentLength == 0, we check if there's any replaced node.
+ // If not, we skip the node with no length.
+ if (!currentLength) {
+ int subOffset = iterator.range()->startOffset();
+ Node* childNode = node.traverseToChildAt(subOffset);
+ if (AccessibilityObject::replacedNodeNeedsCharacter(childNode)) {
+ offsetSoFar++;
+ currentLength++;
+ currentNode = childNode;
+ hasReplacedNodeOrBR = true;
+ } else
+ continue;
+ } else {
+ // Ignore space, new line, tag node.
+ if (currentLength == 1) {
+ if (isSpaceOrNewline(iterator.text()[0])) {
+ // If the node has BR tag, we want to set the currentNode to it.
+ int subOffset = iterator.range()->startOffset();
+ Node* childNode = node.traverseToChildAt(subOffset);
+ if (childNode && childNode->renderer() && childNode->renderer()->isBR()) {
+ currentNode = childNode;
+ hasReplacedNodeOrBR = true;
+ } else if (currentNode != previousNode)
+ continue;
+ }
+ }
+ offsetSoFar += currentLength;
+ }
+
+ if (currentNode == previousNode)
+ lastLength += currentLength;
+ else {
+ lastLength = currentLength;
+ lastStartOffset = hasReplacedNodeOrBR ? 0 : iterator.range()->startOffset();
+ }
+
+ // Break early if we have advanced enough characters.
+ if (!toNodeEnd && offsetSoFar >= offset) {
+ offsetInCharacter = offset - (offsetSoFar - lastLength);
+ finished = true;
+ break;
+ }
+ previousNode = currentNode;
+ }
+
+ if (!finished) {
+ offsetInCharacter = lastLength;
+ if (!toNodeEnd)
+ remaining = offset - offsetSoFar;
+ }
+
+ return CharacterOffset(currentNode, lastStartOffset, offsetInCharacter, remaining);
+}
+
+int AXObjectCache::lengthForRange(Range* range)
+{
+ if (!range)
+ return -1;
+
+ int length = 0;
+ for (TextIterator it(range); !it.atEnd(); it.advance()) {
+ // non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX)
+ if (it.text().length())
+ length += it.text().length();
+ else {
+ // locate the node and starting offset for this replaced range
+ Node& node = it.range()->startContainer();
+ int offset = it.range()->startOffset();
+ if (AccessibilityObject::replacedNodeNeedsCharacter(node.traverseToChildAt(offset)))
+ ++length;
+ }
+ }
+
+ return length;
+}
+
+RefPtr<Range> AXObjectCache::rangeForNodeContents(Node* node)
+{
+ if (!node)
+ return nullptr;
+
+ Document* document = &node->document();
+ if (!document)
+ return nullptr;
+ RefPtr<Range> range = Range::create(*document);
+ ExceptionCode ec = 0;
+ range->selectNodeContents(node, ec);
+ return ec ? nullptr : range;
+}
+
+static bool isReplacedNodeOrBR(Node* node)
+{
+ return node && (AccessibilityObject::replacedNodeNeedsCharacter(node) || node->hasTagName(brTag));
+}
+
+static bool characterOffsetsInOrder(const CharacterOffset& characterOffset1, const CharacterOffset& characterOffset2)
+{
+ if (characterOffset1.isNull() || characterOffset2.isNull())
+ return false;
+
+ if (characterOffset1.node == characterOffset2.node)
+ return characterOffset1.offset <= characterOffset2.offset;
+
+ Node* node1 = characterOffset1.node;
+ Node* node2 = characterOffset2.node;
+ if (!node1->offsetInCharacters() && !isReplacedNodeOrBR(node1))
+ node1 = node1->traverseToChildAt(characterOffset1.offset);
+ if (!node2->offsetInCharacters() && !isReplacedNodeOrBR(node2))
+ node2 = node2->traverseToChildAt(characterOffset2.offset);
+
+ if (!node1 || !node2)
+ return false;
+
+ RefPtr<Range> range1 = AXObjectCache::rangeForNodeContents(node1);
+ RefPtr<Range> range2 = AXObjectCache::rangeForNodeContents(node2);
+ return range1->compareBoundaryPoints(Range::START_TO_START, range2.get(), IGNORE_EXCEPTION) <= 0;
+}
+
+static Node* resetNodeAndOffsetForReplacedNode(Node* replacedNode, int& offset, int characterCount)
+{
+ // Use this function to include the replaced node itself in the range we are creating.
+ if (!replacedNode)
+ return nullptr;
+
+ RefPtr<Range> nodeRange = AXObjectCache::rangeForNodeContents(replacedNode);
+ int nodeLength = TextIterator::rangeLength(nodeRange.get());
+ offset = characterCount <= nodeLength ? replacedNode->computeNodeIndex() : replacedNode->computeNodeIndex() + 1;
+ return replacedNode->parentNode();
+}
+
+static void setRangeStartOrEndWithCharacterOffset(RefPtr<Range> range, const CharacterOffset& characterOffset, bool isStart, ExceptionCode& ec)
+{
+ if (!range) {
+ ec = RangeError;
+ return;
+ }
+ if (characterOffset.isNull()) {
+ ec = TypeError;
+ return;
+ }
+
+ int offset = characterOffset.startIndex + characterOffset.offset;
+ Node* node = characterOffset.node;
+ if (isReplacedNodeOrBR(node))
+ node = resetNodeAndOffsetForReplacedNode(node, offset, characterOffset.offset);
+
+ if (isStart)
+ range->setStart(node, offset, ec);
+ else
+ range->setEnd(node, offset, ec);
+}
+
+RefPtr<Range> AXObjectCache::rangeForUnorderedCharacterOffsets(const CharacterOffset& characterOffset1, const CharacterOffset& characterOffset2)
+{
+ if (characterOffset1.isNull() || characterOffset2.isNull())
+ return nullptr;
+
+ bool alreadyInOrder = characterOffsetsInOrder(characterOffset1, characterOffset2);
+ CharacterOffset startCharacterOffset = alreadyInOrder ? characterOffset1 : characterOffset2;
+ CharacterOffset endCharacterOffset = alreadyInOrder ? characterOffset2 : characterOffset1;
+
+ RefPtr<Range> result = Range::create(m_document);
+ ExceptionCode ec = 0;
+ setRangeStartOrEndWithCharacterOffset(result, startCharacterOffset, true, ec);
+ setRangeStartOrEndWithCharacterOffset(result, endCharacterOffset, false, ec);
+ if (ec)
+ return nullptr;
+
+ return result;
+}
+
+void AXObjectCache::setTextMarkerDataWithCharacterOffset(TextMarkerData& textMarkerData, const CharacterOffset& characterOffset)
+{
+ if (characterOffset.isNull())
+ return;
+
+ Node* domNode = characterOffset.node;
+ if (is<HTMLInputElement>(*domNode) && downcast<HTMLInputElement>(*domNode).isPasswordField()) {
+ textMarkerData.ignored = true;
+ return;
+ }
+
+ RefPtr<AccessibilityObject> obj = this->getOrCreate(domNode);
+ if (!obj)
+ return;
+
+ // Convert to visible position.
+ VisiblePosition visiblePosition = visiblePositionFromCharacterOffset(characterOffset);
+ int vpOffset = 0;
+ if (!visiblePosition.isNull()) {
+ Position deepPos = visiblePosition.deepEquivalent();
+ vpOffset = deepPos.deprecatedEditingOffset();
+ }
+
+ textMarkerData.axID = obj.get()->axObjectID();
+ textMarkerData.node = domNode;
+ textMarkerData.characterOffset = characterOffset.offset;
+ textMarkerData.characterStartIndex = characterOffset.startIndex;
+ textMarkerData.offset = vpOffset;
+ textMarkerData.affinity = visiblePosition.affinity();
+
+ this->setNodeInUse(domNode);
+}
+
+CharacterOffset AXObjectCache::startOrEndCharacterOffsetForRange(RefPtr<Range> range, bool isStart)
+{
+ if (!range)
+ return CharacterOffset();
+
+ // If it's end text marker, we want to go to the end of the range, and stay within the range.
+ bool stayWithinRange = !isStart;
+
+ RefPtr<Range> copyRange = range;
+ // Change the start of the range, so the character offset starts from node beginning.
+ int offset = 0;
+ Node* node = &copyRange->startContainer();
+ if (node->offsetInCharacters()) {
+ copyRange = Range::create(range->ownerDocument(), &range->startContainer(), range->startOffset(), &range->endContainer(), range->endOffset());
+ CharacterOffset nodeStartOffset = traverseToOffsetInRange(rangeForNodeContents(node), 0, false);
+ offset = std::max(copyRange->startOffset() - nodeStartOffset.startIndex, 0);
+ copyRange->setStart(node, nodeStartOffset.startIndex);
+ }
+
+ return traverseToOffsetInRange(copyRange, offset, !isStart, stayWithinRange);
+}
+
+void AXObjectCache::startOrEndTextMarkerDataForRange(TextMarkerData& textMarkerData, RefPtr<Range> range, bool isStart)
+{
+ memset(&textMarkerData, 0, sizeof(TextMarkerData));
+
+ CharacterOffset characterOffset = startOrEndCharacterOffsetForRange(range, isStart);
+ if (characterOffset.isNull())
+ return;
+
+ setTextMarkerDataWithCharacterOffset(textMarkerData, characterOffset);
+}
+
+CharacterOffset AXObjectCache::characterOffsetForNodeAndOffset(Node& node, int offset, TraverseOption option)
+{
+ Node* domNode = &node;
+ if (!domNode)
+ return CharacterOffset();
+
+ bool toNodeEnd = option & TraverseOptionToNodeEnd;
+ bool includeStart = option & TraverseOptionIncludeStart;
+
+ // ignoreStart is used to determine if we should go to previous node or
+ // stay in current node when offset is 0.
+ if (!toNodeEnd && (offset < 0 || (!offset && !includeStart))) {
+ // Set the offset to the amount of characters we need to go backwards.
+ offset = - offset;
+ CharacterOffset charOffset = CharacterOffset();
+ while (offset >= 0 && charOffset.offset <= offset) {
+ offset -= charOffset.offset;
+ domNode = previousNode(domNode);
+ if (domNode) {
+ charOffset = characterOffsetForNodeAndOffset(*domNode, 0, TraverseOptionToNodeEnd);
+ } else
+ return CharacterOffset();
+ if (charOffset.offset == offset)
+ break;
+ }
+ if (offset > 0)
+ charOffset = characterOffsetForNodeAndOffset(*charOffset.node, charOffset.offset - offset, TraverseOptionIncludeStart);
+ return charOffset;
+ }
+
+ RefPtr<Range> range = rangeForNodeContents(domNode);
+
+ // Traverse the offset amount of characters forward and see if there's remaining offsets.
+ // Keep traversing to the next node when there's remaining offsets.
+ CharacterOffset characterOffset = traverseToOffsetInRange(range, offset, toNodeEnd);
+ while (!characterOffset.isNull() && characterOffset.remaining() && !toNodeEnd) {
+ domNode = nextNode(domNode);
+ if (!domNode)
+ return CharacterOffset();
+ range = rangeForNodeContents(domNode);
+ characterOffset = traverseToOffsetInRange(range, characterOffset.remaining(), toNodeEnd);
+ }
+
+ return characterOffset;
+}
+
+void AXObjectCache::textMarkerDataForCharacterOffset(TextMarkerData& textMarkerData, const CharacterOffset& characterOffset)
+{
+ memset(&textMarkerData, 0, sizeof(TextMarkerData));
+ setTextMarkerDataWithCharacterOffset(textMarkerData, characterOffset);
+}
+
+void AXObjectCache::textMarkerDataForNextCharacterOffset(TextMarkerData& textMarkerData, const CharacterOffset& characterOffset)
+{
+ CharacterOffset next = characterOffset;
+ do {
+ next = nextCharacterOffset(next);
+ textMarkerDataForCharacterOffset(textMarkerData, next);
+ } while (textMarkerData.ignored);
+}
+
+void AXObjectCache::textMarkerDataForPreviousCharacterOffset(TextMarkerData& textMarkerData, const CharacterOffset& characterOffset)
+{
+ CharacterOffset previous = characterOffset;
+ do {
+ previous = previousCharacterOffset(previous);
+ textMarkerDataForCharacterOffset(textMarkerData, previous);
+ } while (textMarkerData.ignored);
+}
+
+Node* AXObjectCache::nextNode(Node* node) const
+{
+ if (!node)
+ return nullptr;
+
+ return NodeTraversal::nextSkippingChildren(*node);
+}
+
+Node* AXObjectCache::previousNode(Node* node) const
+{
+ if (!node)
+ return nullptr;
+
+ // First child of body shouldn't have previous node.
+ if (node->parentNode() && node->parentNode()->renderer() && node->parentNode()->renderer()->isBody() && !node->previousSibling())
+ return nullptr;
+
+ return NodeTraversal::previousSkippingChildren(*node);
+}
+
+VisiblePosition AXObjectCache::visiblePositionFromCharacterOffset(const CharacterOffset& characterOffset)
+{
+ if (characterOffset.isNull())
+ return VisiblePosition();
+
+ RefPtr<AccessibilityObject> obj = this->getOrCreate(characterOffset.node);
+ if (!obj)
+ return VisiblePosition();
+
+ // nextVisiblePosition means advancing one character. Use this to calculate the character offset.
+ VisiblePositionRange vpRange = obj->visiblePositionRange();
+ VisiblePosition start = vpRange.start;
+ VisiblePosition result = start;
+ for (int i = 0; i < characterOffset.offset; i++)
+ result = obj->nextVisiblePosition(result);
+
+ return result;
+}
+
+CharacterOffset AXObjectCache::characterOffsetFromVisiblePosition(const VisiblePosition& visiblePos)
+{
+ if (visiblePos.isNull())
+ return CharacterOffset();
+
+ Position deepPos = visiblePos.deepEquivalent();
+ Node* domNode = deepPos.deprecatedNode();
+ ASSERT(domNode);
+
+ RefPtr<AccessibilityObject> obj = this->getOrCreate(domNode);
+ if (!obj)
+ return CharacterOffset();
+
+ // Use nextVisiblePosition to calculate how many characters we need to traverse to the current position.
+ VisiblePositionRange vpRange = obj->visiblePositionRange();
+ VisiblePosition vp = vpRange.start;
+ int characterOffset = 0;
+ Position vpDeepPos = vp.deepEquivalent();
+
+ VisiblePosition previousVisiblePos;
+ while (!vpDeepPos.isNull() && !deepPos.equals(vpDeepPos)) {
+ previousVisiblePos = vp;
+ vp = obj->nextVisiblePosition(vp);
+ vpDeepPos = vp.deepEquivalent();
+ // Sometimes nextVisiblePosition will give the same VisiblePostion,
+ // we break here to avoid infinite loop.
+ if (vpDeepPos.equals(previousVisiblePos.deepEquivalent()))
+ break;
+ characterOffset++;
+ }
+
+ return traverseToOffsetInRange(rangeForNodeContents(obj->node()), characterOffset, false);
+}
+
+AccessibilityObject* AXObjectCache::accessibilityObjectForTextMarkerData(TextMarkerData& textMarkerData)
+{
+ if (!isNodeInUse(textMarkerData.node))
+ return nullptr;
+
+ Node* domNode = textMarkerData.node;
+ return this->getOrCreate(domNode);
+}
+
void AXObjectCache::textMarkerDataForVisiblePosition(TextMarkerData& textMarkerData, const VisiblePosition& visiblePos)
{
// This memory must be bzero'd so instances of TextMarkerData can be tested for byte-equivalence.
@@ -900,28 +1883,378 @@ void AXObjectCache::textMarkerDataForVisiblePosition(TextMarkerData& textMarkerD
if (!domNode)
return;
- if (domNode->isHTMLElement()) {
- HTMLInputElement* inputElement = domNode->toInputElement();
- if (inputElement && inputElement->isPasswordField())
- return;
- }
+ if (is<HTMLInputElement>(*domNode) && downcast<HTMLInputElement>(*domNode).isPasswordField())
+ return;
// find or create an accessibility object for this node
- AXObjectCache* cache = domNode->document()->axObjectCache();
+ AXObjectCache* cache = domNode->document().axObjectCache();
RefPtr<AccessibilityObject> obj = cache->getOrCreate(domNode);
textMarkerData.axID = obj.get()->axObjectID();
textMarkerData.node = domNode;
textMarkerData.offset = deepPos.deprecatedEditingOffset();
- textMarkerData.affinity = visiblePos.affinity();
+ textMarkerData.affinity = visiblePos.affinity();
+
+ // convert to character offset
+ CharacterOffset characterOffset = characterOffsetFromVisiblePosition(visiblePos);
+ textMarkerData.characterOffset = characterOffset.offset;
+ textMarkerData.characterStartIndex = characterOffset.startIndex;
cache->setNodeInUse(domNode);
}
+CharacterOffset AXObjectCache::nextCharacterOffset(const CharacterOffset& characterOffset, bool ignoreStart)
+{
+ if (characterOffset.isNull())
+ return CharacterOffset();
+
+ return characterOffsetForNodeAndOffset(*characterOffset.node, characterOffset.offset + 1, ignoreStart ? TraverseOptionDefault : TraverseOptionIncludeStart);
+}
+
+CharacterOffset AXObjectCache::previousCharacterOffset(const CharacterOffset& characterOffset, bool ignoreStart)
+{
+ if (characterOffset.isNull())
+ return CharacterOffset();
+
+ return characterOffsetForNodeAndOffset(*characterOffset.node, characterOffset.offset - 1, ignoreStart ? TraverseOptionDefault : TraverseOptionIncludeStart);
+}
+
+CharacterOffset AXObjectCache::startCharacterOffsetOfWord(const CharacterOffset& characterOffset, EWordSide side)
+{
+ if (characterOffset.isNull())
+ return CharacterOffset();
+
+ CharacterOffset c = characterOffset;
+ if (side == RightWordIfOnBoundary) {
+ CharacterOffset endOfParagraph = endCharacterOffsetOfParagraph(c);
+ if (c.isEqual(endOfParagraph))
+ return c;
+
+ c = nextCharacterOffset(characterOffset);
+ if (c.isNull())
+ return characterOffset;
+ }
+
+ return previousBoundary(c, startWordBoundary);
+}
+
+CharacterOffset AXObjectCache::endCharacterOffsetOfWord(const CharacterOffset& characterOffset, EWordSide side)
+{
+ if (characterOffset.isNull())
+ return CharacterOffset();
+
+ CharacterOffset c = characterOffset;
+ if (side == LeftWordIfOnBoundary) {
+ CharacterOffset startOfParagraph = startCharacterOffsetOfParagraph(c);
+ if (c.isEqual(startOfParagraph))
+ return c;
+
+ c = previousCharacterOffset(characterOffset, false);
+ if (c.isNull())
+ return characterOffset;
+ }
+
+ return nextBoundary(c, endWordBoundary);
+}
+
+CharacterOffset AXObjectCache::previousWordStartCharacterOffset(const CharacterOffset& characterOffset)
+{
+ if (characterOffset.isNull())
+ return CharacterOffset();
+
+ CharacterOffset previousOffset = previousCharacterOffset(characterOffset, false);
+ if (previousOffset.isNull())
+ return CharacterOffset();
+
+ return startCharacterOffsetOfWord(previousOffset, RightWordIfOnBoundary);
+}
+
+CharacterOffset AXObjectCache::nextWordEndCharacterOffset(const CharacterOffset& characterOffset)
+{
+ if (characterOffset.isNull())
+ return CharacterOffset();
+
+ CharacterOffset nextOffset = nextCharacterOffset(characterOffset);
+ if (nextOffset.isNull())
+ return CharacterOffset();
+
+ return endCharacterOffsetOfWord(nextOffset, LeftWordIfOnBoundary);
+}
+
+RefPtr<Range> AXObjectCache::leftWordRange(const CharacterOffset& characterOffset)
+{
+ CharacterOffset start = startCharacterOffsetOfWord(characterOffset, LeftWordIfOnBoundary);
+ CharacterOffset end = endCharacterOffsetOfWord(start);
+ return rangeForUnorderedCharacterOffsets(start, end);
+}
+
+RefPtr<Range> AXObjectCache::rightWordRange(const CharacterOffset& characterOffset)
+{
+ CharacterOffset start = startCharacterOffsetOfWord(characterOffset);
+ CharacterOffset end = endCharacterOffsetOfWord(start);
+ return rangeForUnorderedCharacterOffsets(start, end);
+}
+
+static UChar32 characterForCharacterOffset(const CharacterOffset& characterOffset)
+{
+ if (characterOffset.isNull() || !characterOffset.node->isTextNode())
+ return 0;
+
+ UChar32 ch = 0;
+ unsigned offset = characterOffset.startIndex + characterOffset.offset;
+ if (offset < characterOffset.node->textContent().length())
+ U16_NEXT(characterOffset.node->textContent(), offset, characterOffset.node->textContent().length(), ch);
+ return ch;
+}
+
+UChar32 AXObjectCache::characterAfter(const CharacterOffset& characterOffset)
+{
+ return characterForCharacterOffset(nextCharacterOffset(characterOffset));
+}
+
+UChar32 AXObjectCache::characterBefore(const CharacterOffset& characterOffset)
+{
+ return characterForCharacterOffset(characterOffset);
+}
+
+static bool characterOffsetNodeIsBR(const CharacterOffset& characterOffset)
+{
+ if (characterOffset.isNull())
+ return false;
+
+ return characterOffset.node->hasTagName(brTag);
+}
+
+static Node* parentEditingBoundary(Node* node)
+{
+ if (!node)
+ return nullptr;
+
+ Node* documentElement = node->document().documentElement();
+ if (!documentElement)
+ return nullptr;
+
+ Node* boundary = node;
+ while (boundary != documentElement && boundary->nonShadowBoundaryParentNode() && node->hasEditableStyle() == boundary->parentNode()->hasEditableStyle())
+ boundary = boundary->nonShadowBoundaryParentNode();
+
+ return boundary;
+}
+
+CharacterOffset AXObjectCache::nextBoundary(const CharacterOffset& characterOffset, BoundarySearchFunction searchFunction)
+{
+ if (characterOffset.isNull())
+ return CharacterOffset();
+
+ Node* boundary = parentEditingBoundary(characterOffset.node);
+ if (!boundary)
+ return CharacterOffset();
+
+ RefPtr<Range> searchRange = rangeForNodeContents(boundary);
+ Vector<UChar, 1024> string;
+ unsigned prefixLength = 0;
+
+ ExceptionCode ec = 0;
+ if (requiresContextForWordBoundary(characterAfter(characterOffset))) {
+ RefPtr<Range> backwardsScanRange(boundary->document().createRange());
+ setRangeStartOrEndWithCharacterOffset(backwardsScanRange, characterOffset, false, ec);
+ prefixLength = prefixLengthForRange(backwardsScanRange, string);
+ }
+
+ setRangeStartOrEndWithCharacterOffset(searchRange, characterOffset, true, ec);
+ CharacterOffset end = startOrEndCharacterOffsetForRange(searchRange, false);
+
+ ASSERT(!ec);
+ if (ec)
+ return CharacterOffset();
+
+ TextIterator it(searchRange.get(), TextIteratorEmitsObjectReplacementCharacters);
+ unsigned next = forwardSearchForBoundaryWithTextIterator(it, string, prefixLength, searchFunction);
+
+ if (it.atEnd() && next == string.size())
+ return end;
+
+ // The endSentenceBoundary function will include a line break at the end of the sentence.
+ if (searchFunction == endSentenceBoundary && string[next - 1] == '\n')
+ next--;
+
+ if (next > prefixLength)
+ return characterOffsetForNodeAndOffset(*characterOffset.node, characterOffset.offset + next - prefixLength);
+
+ return characterOffset;
+}
+
+CharacterOffset AXObjectCache::previousBoundary(const CharacterOffset& characterOffset, BoundarySearchFunction searchFunction)
+{
+ if (characterOffset.isNull())
+ return CharacterOffset();
+
+ Node* boundary = parentEditingBoundary(characterOffset.node);
+ if (!boundary)
+ return CharacterOffset();
+
+ RefPtr<Range> searchRange = rangeForNodeContents(boundary);
+ Vector<UChar, 1024> string;
+ unsigned suffixLength = 0;
+
+ ExceptionCode ec = 0;
+ if (requiresContextForWordBoundary(characterBefore(characterOffset))) {
+ RefPtr<Range> forwardsScanRange(boundary->document().createRange());
+ forwardsScanRange->setEndAfter(boundary, ec);
+ setRangeStartOrEndWithCharacterOffset(forwardsScanRange, characterOffset, true, ec);
+ suffixLength = suffixLengthForRange(forwardsScanRange, string);
+ }
+
+ setRangeStartOrEndWithCharacterOffset(searchRange, characterOffset, false, ec);
+ CharacterOffset start = startOrEndCharacterOffsetForRange(searchRange, true);
+
+ ASSERT(!ec);
+ if (ec)
+ return CharacterOffset();
+
+ SimplifiedBackwardsTextIterator it(*searchRange);
+ unsigned next = backwardSearchForBoundaryWithTextIterator(it, string, suffixLength, searchFunction);
+
+ if (!next)
+ return it.atEnd() ? start : characterOffset;
+
+ Node& node = it.atEnd() ? searchRange->startContainer() : it.range()->startContainer();
+ if ((node.isTextNode() && static_cast<int>(next) <= node.maxCharacterOffset()) || (node.renderer() && node.renderer()->isBR() && !next)) {
+ // The next variable contains a usable index into a text node
+ if (&node == characterOffset.node)
+ next -= characterOffset.startIndex;
+ return characterOffsetForNodeAndOffset(node, next, TraverseOptionIncludeStart);
+ }
+
+ int characterCount = characterOffset.offset - (string.size() - suffixLength - next);
+ // We don't want to go to the previous node if the node is at the start of a new line.
+ if (characterCount < 0 && (characterOffsetNodeIsBR(characterOffset) || string[string.size() - 1] == '\n'))
+ characterCount = 0;
+ return characterOffsetForNodeAndOffset(*characterOffset.node, characterCount, TraverseOptionIncludeStart);
+}
+
+CharacterOffset AXObjectCache::startCharacterOffsetOfParagraph(const CharacterOffset& characterOffset, EditingBoundaryCrossingRule boundaryCrossingRule)
+{
+ if (characterOffset.isNull())
+ return CharacterOffset();
+
+ Node* startNode = characterOffset.node;
+
+ if (isRenderedAsNonInlineTableImageOrHR(startNode))
+ return startOrEndCharacterOffsetForRange(rangeForNodeContents(startNode), true);
+
+ Node* startBlock = enclosingBlock(startNode);
+ int offset = characterOffset.startIndex + characterOffset.offset;
+ Position p(startNode, offset, Position::PositionIsOffsetInAnchor);
+ Node* highestRoot = highestEditableRoot(p);
+ Position::AnchorType type = Position::PositionIsOffsetInAnchor;
+
+ Node* node = findStartOfParagraph(startNode, highestRoot, startBlock, offset, type, boundaryCrossingRule);
+
+ if (type == Position::PositionIsOffsetInAnchor)
+ return characterOffsetForNodeAndOffset(*node, offset, TraverseOptionIncludeStart);
+
+ return startOrEndCharacterOffsetForRange(rangeForNodeContents(node), true);
+}
+
+CharacterOffset AXObjectCache::endCharacterOffsetOfParagraph(const CharacterOffset& characterOffset, EditingBoundaryCrossingRule boundaryCrossingRule)
+{
+ if (characterOffset.isNull())
+ return CharacterOffset();
+
+ Node* startNode = characterOffset.node;
+ if (isRenderedAsNonInlineTableImageOrHR(startNode))
+ return startOrEndCharacterOffsetForRange(rangeForNodeContents(startNode), false);
+
+ Node* stayInsideBlock = enclosingBlock(startNode);
+ int offset = characterOffset.startIndex + characterOffset.offset;
+ Position p(startNode, offset, Position::PositionIsOffsetInAnchor);
+ Node* highestRoot = highestEditableRoot(p);
+ Position::AnchorType type = Position::PositionIsOffsetInAnchor;
+
+ Node* node = findEndOfParagraph(startNode, highestRoot, stayInsideBlock, offset, type, boundaryCrossingRule);
+ if (type == Position::PositionIsOffsetInAnchor) {
+ if (node->isTextNode()) {
+ CharacterOffset startOffset = startOrEndCharacterOffsetForRange(rangeForNodeContents(node), true);
+ offset -= startOffset.startIndex;
+ }
+ return characterOffsetForNodeAndOffset(*node, offset, TraverseOptionIncludeStart);
+ }
+
+ return startOrEndCharacterOffsetForRange(rangeForNodeContents(node), false);
+}
+
+RefPtr<Range> AXObjectCache::paragraphForCharacterOffset(const CharacterOffset& characterOffset)
+{
+ CharacterOffset start = startCharacterOffsetOfParagraph(characterOffset);
+ CharacterOffset end = endCharacterOffsetOfParagraph(start);
+
+ return rangeForUnorderedCharacterOffsets(start, end);
+}
+
+CharacterOffset AXObjectCache::nextParagraphEndCharacterOffset(const CharacterOffset& characterOffset)
+{
+ // make sure we move off of a paragraph end
+ CharacterOffset next = nextCharacterOffset(characterOffset);
+
+ // We should skip the following BR node.
+ if (characterOffsetNodeIsBR(next) && !characterOffsetNodeIsBR(characterOffset))
+ next = nextCharacterOffset(next);
+
+ return endCharacterOffsetOfParagraph(next);
+}
+
+CharacterOffset AXObjectCache::previousParagraphStartCharacterOffset(const CharacterOffset& characterOffset)
+{
+ // make sure we move off of a paragraph start
+ CharacterOffset previous = previousCharacterOffset(characterOffset, false);
+
+ // We should skip the preceding BR node.
+ if (characterOffsetNodeIsBR(previous) && !characterOffsetNodeIsBR(characterOffset))
+ previous = previousCharacterOffset(previous, false);
+
+ return startCharacterOffsetOfParagraph(previous);
+}
+
+CharacterOffset AXObjectCache::startCharacterOffsetOfSentence(const CharacterOffset& characterOffset)
+{
+ return previousBoundary(characterOffset, startSentenceBoundary);
+}
+
+CharacterOffset AXObjectCache::endCharacterOffsetOfSentence(const CharacterOffset& characterOffset)
+{
+ return nextBoundary(characterOffset, endSentenceBoundary);
+}
+
+RefPtr<Range> AXObjectCache::sentenceForCharacterOffset(const CharacterOffset& characterOffset)
+{
+ CharacterOffset start = startCharacterOffsetOfSentence(characterOffset);
+ CharacterOffset end = endCharacterOffsetOfSentence(start);
+ return rangeForUnorderedCharacterOffsets(start, end);
+}
+
+CharacterOffset AXObjectCache::nextSentenceEndCharacterOffset(const CharacterOffset& characterOffset)
+{
+ // Make sure we move off of a sentence end.
+ return endCharacterOffsetOfSentence(nextCharacterOffset(characterOffset));
+}
+
+CharacterOffset AXObjectCache::previousSentenceStartCharacterOffset(const CharacterOffset& characterOffset)
+{
+ // Make sure we move off of a sentence start.
+ CharacterOffset previous = previousCharacterOffset(characterOffset, false);
+
+ // We should skip the preceding BR node.
+ if (!previous.isNull() && previous.node->hasTagName(brTag) && !characterOffset.node->hasTagName(brTag))
+ previous = previousCharacterOffset(previous, false);
+
+ return startCharacterOffsetOfSentence(previous);
+}
+
const Element* AXObjectCache::rootAXEditableElement(const Node* node)
{
const Element* result = node->rootEditableElement();
- const Element* element = node->isElementNode() ? toElement(node) : node->parentElement();
+ const Element* element = is<Element>(*node) ? downcast<Element>(node) : node->parentElement();
for (; element; element = element->parentElement()) {
if (nodeIsTextControl(element))
@@ -931,6 +2264,22 @@ const Element* AXObjectCache::rootAXEditableElement(const Node* node)
return result;
}
+void AXObjectCache::clearTextMarkerNodesInUse(Document* document)
+{
+ if (!document)
+ return;
+
+ // Check each node to see if it's inside the document being deleted, of if it no longer belongs to a document.
+ HashSet<Node*> nodesToDelete;
+ for (const auto& node : m_textMarkerNodes) {
+ if (!node->inDocument() || &(node)->document() == document)
+ nodesToDelete.add(node);
+ }
+
+ for (const auto& node : nodesToDelete)
+ m_textMarkerNodes.remove(node);
+}
+
bool AXObjectCache::nodeIsTextControl(const Node* node)
{
if (!node)
@@ -944,11 +2293,38 @@ bool isNodeAriaVisible(Node* node)
{
if (!node)
return false;
+
+ // ARIA Node visibility is controlled by aria-hidden
+ // 1) if aria-hidden=true, the whole subtree is hidden
+ // 2) if aria-hidden=false, and the object is rendered, there's no effect
+ // 3) if aria-hidden=false, and the object is NOT rendered, then it must have
+ // aria-hidden=false on each parent until it gets to a rendered object
+ // 3b) a text node inherits a parents aria-hidden value
+ bool requiresAriaHiddenFalse = !node->renderer();
+ bool ariaHiddenFalsePresent = false;
+ for (Node* testNode = node; testNode; testNode = testNode->parentNode()) {
+ if (is<Element>(*testNode)) {
+ const AtomicString& ariaHiddenValue = downcast<Element>(*testNode).fastGetAttribute(aria_hiddenAttr);
+ if (equalLettersIgnoringASCIICase(ariaHiddenValue, "true"))
+ return false;
+
+ bool ariaHiddenFalse = equalLettersIgnoringASCIICase(ariaHiddenValue, "false");
+ if (!testNode->renderer() && !ariaHiddenFalse)
+ return false;
+ if (!ariaHiddenFalsePresent && ariaHiddenFalse)
+ ariaHiddenFalsePresent = true;
+ }
+ }
- if (!node->isElementNode())
- return false;
-
- return equalIgnoringCase(toElement(node)->getAttribute(aria_hiddenAttr), "false");
+ return !requiresAriaHiddenFalse || ariaHiddenFalsePresent;
+}
+
+AccessibilityObject* AXObjectCache::rootWebArea()
+{
+ AccessibilityObject* rootObject = this->rootObject();
+ if (!rootObject || !rootObject->isAccessibilityScrollView())
+ return nullptr;
+ return downcast<AccessibilityScrollView>(*rootObject).webAreaObject();
}
AXAttributeCacheEnabler::AXAttributeCacheEnabler(AXObjectCache* cache)
@@ -963,6 +2339,28 @@ AXAttributeCacheEnabler::~AXAttributeCacheEnabler()
if (m_cache)
m_cache->stopCachingComputedObjectAttributes();
}
+
+#if !PLATFORM(COCOA)
+AXTextChange AXObjectCache::textChangeForEditType(AXTextEditType type)
+{
+ switch (type) {
+ case AXTextEditTypeCut:
+ case AXTextEditTypeDelete:
+ return AXTextDeleted;
+ case AXTextEditTypeInsert:
+ case AXTextEditTypeDictation:
+ case AXTextEditTypeTyping:
+ case AXTextEditTypePaste:
+ return AXTextInserted;
+ case AXTextEditTypeAttributesChange:
+ return AXTextAttributesChanged;
+ case AXTextEditTypeUnknown:
+ break;
+ }
+ ASSERT_NOT_REACHED();
+ return AXTextInserted;
+}
+#endif
} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AXObjectCache.h b/Source/WebCore/accessibility/AXObjectCache.h
index 52b4e2fb9..ab11f32ec 100644
--- a/Source/WebCore/accessibility/AXObjectCache.h
+++ b/Source/WebCore/accessibility/AXObjectCache.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2003, 2006, 2007, 2008, 2009, 2010, 2011, 2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -26,12 +26,16 @@
#ifndef AXObjectCache_h
#define AXObjectCache_h
+#include "AXTextStateChangeIntent.h"
#include "AccessibilityObject.h"
+#include "Range.h"
#include "Timer.h"
+#include "VisibleUnits.h"
#include <limits.h>
#include <wtf/Forward.h>
#include <wtf/HashMap.h>
#include <wtf/HashSet.h>
+#include <wtf/ListHashSet.h>
#include <wtf/RefPtr.h>
namespace WebCore {
@@ -49,19 +53,41 @@ struct TextMarkerData {
AXID axID;
Node* node;
int offset;
+ int characterStartIndex;
+ int characterOffset;
+ bool ignored;
EAffinity affinity;
};
+struct CharacterOffset {
+ Node* node;
+ int startIndex;
+ int offset;
+ int remainingOffset;
+
+ CharacterOffset(Node* n = nullptr, int startIndex = 0, int offset = 0, int remaining = 0)
+ : node(n)
+ , startIndex(startIndex)
+ , offset(offset)
+ , remainingOffset(remaining)
+ { }
+
+ int remaining() const { return remainingOffset; }
+ bool isNull() const { return !node; }
+ bool isEqual(CharacterOffset& other) const
+ {
+ if (isNull() || other.isNull())
+ return false;
+ return node == other.node && startIndex == other.startIndex && offset == other.offset;
+ }
+};
+
class AXComputedObjectAttributeCache {
public:
- static PassOwnPtr<AXComputedObjectAttributeCache> create() { return adoptPtr(new AXComputedObjectAttributeCache()); }
-
AccessibilityObjectInclusion getIgnored(AXID) const;
void setIgnored(AXID, AccessibilityObjectInclusion);
private:
- AXComputedObjectAttributeCache() { }
-
struct CachedAXObjectAttributes {
CachedAXObjectAttributes() : ignored(DefaultBehavior) { }
@@ -71,20 +97,26 @@ private:
HashMap<AXID, CachedAXObjectAttributes> m_idMapping;
};
+#if !PLATFORM(COCOA)
+enum AXTextChange { AXTextInserted, AXTextDeleted, AXTextAttributesChanged };
+#endif
+
+enum PostTarget { TargetElement, TargetObservableParent };
+
enum PostType { PostSynchronously, PostAsynchronously };
class AXObjectCache {
WTF_MAKE_NONCOPYABLE(AXObjectCache); WTF_MAKE_FAST_ALLOCATED;
public:
- explicit AXObjectCache(const Document*);
+ explicit AXObjectCache(Document&);
~AXObjectCache();
- static AccessibilityObject* focusedUIElementForPage(const Page*);
+ WEBCORE_EXPORT static AccessibilityObject* focusedUIElementForPage(const Page*);
// Returns the root object for the entire document.
- AccessibilityObject* rootObject();
+ WEBCORE_EXPORT AccessibilityObject* rootObject();
// Returns the root object for a specific frame.
- AccessibilityObject* rootObjectForFrame(Frame*);
+ WEBCORE_EXPORT AccessibilityObject* rootObjectForFrame(Frame*);
// For AX objects with elements that back them.
AccessibilityObject* getOrCreate(RenderObject*);
@@ -104,10 +136,10 @@ public:
void remove(Widget*);
void remove(AXID);
- void detachWrapper(AccessibilityObject*);
+ void detachWrapper(AccessibilityObject*, AccessibilityDetachmentType);
void attachWrapper(AccessibilityObject*);
- void childrenChanged(Node*);
- void childrenChanged(RenderObject*);
+ void childrenChanged(Node*, Node* newChild = nullptr);
+ void childrenChanged(RenderObject*, RenderObject* newChild = nullptr);
void childrenChanged(AccessibilityObject*);
void checkedStateChanged(Node*);
void selectedChildrenChanged(Node*);
@@ -124,19 +156,25 @@ public:
void handleScrolledToAnchor(const Node* anchorNode);
void handleAriaExpandedChange(Node*);
void handleScrollbarUpdate(ScrollView*);
+
+ void handleAriaModalChange(Node*);
+ Node* ariaModalNode();
void handleAttributeChanged(const QualifiedName& attrName, Element*);
void recomputeIsIgnored(RenderObject* renderer);
#if HAVE(ACCESSIBILITY)
- static void enableAccessibility() { gAccessibilityEnabled = true; }
+ WEBCORE_EXPORT static void enableAccessibility();
+ WEBCORE_EXPORT static void disableAccessibility();
+
// Enhanced user interface accessibility can be toggled by the assistive technology.
- static void setEnhancedUserInterfaceAccessibility(bool flag) { gAccessibilityEnhancedUserInterfaceEnabled = flag; }
+ WEBCORE_EXPORT static void setEnhancedUserInterfaceAccessibility(bool flag);
static bool accessibilityEnabled() { return gAccessibilityEnabled; }
static bool accessibilityEnhancedUserInterfaceEnabled() { return gAccessibilityEnhancedUserInterfaceEnabled; }
#else
static void enableAccessibility() { }
+ static void disableAccessibility() { }
static void setEnhancedUserInterfaceAccessibility(bool) { }
static bool accessibilityEnabled() { return false; }
static bool accessibilityEnhancedUserInterfaceEnabled() { return false; }
@@ -145,7 +183,6 @@ public:
void removeAXID(AccessibilityObject*);
bool isIDinUse(AXID id) const { return m_idsInUse.contains(id); }
- Element* rootAXEditableElement(Node*);
const Element* rootAXEditableElement(const Node*);
bool nodeIsTextControl(const Node*);
@@ -154,7 +191,34 @@ public:
// Text marker utilities.
void textMarkerDataForVisiblePosition(TextMarkerData&, const VisiblePosition&);
+ void textMarkerDataForCharacterOffset(TextMarkerData&, const CharacterOffset&);
+ void textMarkerDataForNextCharacterOffset(TextMarkerData&, const CharacterOffset&);
+ void textMarkerDataForPreviousCharacterOffset(TextMarkerData&, const CharacterOffset&);
VisiblePosition visiblePositionForTextMarkerData(TextMarkerData&);
+ CharacterOffset characterOffsetForTextMarkerData(TextMarkerData&);
+ CharacterOffset nextCharacterOffset(const CharacterOffset&, bool ignoreStart = true);
+ CharacterOffset previousCharacterOffset(const CharacterOffset&, bool ignoreStart = true);
+ void startOrEndTextMarkerDataForRange(TextMarkerData&, RefPtr<Range>, bool);
+ AccessibilityObject* accessibilityObjectForTextMarkerData(TextMarkerData&);
+ RefPtr<Range> rangeForUnorderedCharacterOffsets(const CharacterOffset&, const CharacterOffset&);
+ static RefPtr<Range> rangeForNodeContents(Node*);
+ static int lengthForRange(Range*);
+
+ // Word boundary
+ CharacterOffset nextWordEndCharacterOffset(const CharacterOffset&);
+ CharacterOffset previousWordStartCharacterOffset(const CharacterOffset&);
+ RefPtr<Range> leftWordRange(const CharacterOffset&);
+ RefPtr<Range> rightWordRange(const CharacterOffset&);
+
+ // Paragraph
+ RefPtr<Range> paragraphForCharacterOffset(const CharacterOffset&);
+ CharacterOffset nextParagraphEndCharacterOffset(const CharacterOffset&);
+ CharacterOffset previousParagraphStartCharacterOffset(const CharacterOffset&);
+
+ // Sentence
+ RefPtr<Range> sentenceForCharacterOffset(const CharacterOffset&);
+ CharacterOffset nextSentenceEndCharacterOffset(const CharacterOffset&);
+ CharacterOffset previousSentenceStartCharacterOffset(const CharacterOffset&);
enum AXNotification {
AXActiveDescendantChanged,
@@ -164,31 +228,43 @@ public:
AXFocusedUIElementChanged,
AXLayoutComplete,
AXLoadComplete,
+ AXNewDocumentLoadComplete,
AXSelectedChildrenChanged,
AXSelectedTextChanged,
AXValueChanged,
AXScrolledToAnchor,
+ AXLiveRegionCreated,
AXLiveRegionChanged,
AXMenuListItemSelected,
AXMenuListValueChanged,
+ AXMenuClosed,
+ AXMenuOpened,
AXRowCountChanged,
AXRowCollapsed,
AXRowExpanded,
+ AXExpandedChanged,
AXInvalidStatusChanged,
AXTextChanged,
- AXAriaAttributeChanged
+ AXAriaAttributeChanged,
+ AXElementBusyChanged
};
- void postNotification(RenderObject*, AXNotification, bool postToElement, PostType = PostAsynchronously);
- void postNotification(Node*, AXNotification, bool postToElement, PostType = PostAsynchronously);
- void postNotification(AccessibilityObject*, Document*, AXNotification, bool postToElement, PostType = PostAsynchronously);
+ void postNotification(RenderObject*, AXNotification, PostTarget = TargetElement, PostType = PostAsynchronously);
+ void postNotification(Node*, AXNotification, PostTarget = TargetElement, PostType = PostAsynchronously);
+ void postNotification(AccessibilityObject*, Document*, AXNotification, PostTarget = TargetElement, PostType = PostAsynchronously);
- enum AXTextChange {
- AXTextInserted,
- AXTextDeleted,
- };
+#ifndef NDEBUG
+ void showIntent(const AXTextStateChangeIntent&);
+#endif
+
+ void setTextSelectionIntent(const AXTextStateChangeIntent&);
+ void setIsSynchronizingSelection(bool);
- void nodeTextChangeNotification(Node*, AXTextChange, unsigned offset, const String&);
+ void postTextStateChangeNotification(Node*, AXTextEditType, const String&, const VisiblePosition&);
+ void postTextReplacementNotification(Node*, AXTextEditType deletionType, const String& deletedText, AXTextEditType insertionType, const String& insertedText, const VisiblePosition&);
+ void postTextStateChangeNotification(Node*, const AXTextStateChangeIntent&, const VisibleSelection&);
+ void postTextStateChangeNotification(const Position&, const AXTextStateChangeIntent&, const VisibleSelection&);
+ void postLiveRegionChangeNotification(AccessibilityObject*);
enum AXLoadingEvent {
AXLoadingStarted,
@@ -199,16 +275,32 @@ public:
void frameLoadingEventNotification(Frame*, AXLoadingEvent);
- bool nodeHasRole(Node*, const AtomicString& role);
+ void clearTextMarkerNodesInUse(Document*);
void startCachingComputedObjectAttributesUntilTreeMutates();
void stopCachingComputedObjectAttributes();
AXComputedObjectAttributeCache* computedObjectAttributeCache() { return m_computedObjectAttributeCache.get(); }
+ Document& document() const { return m_document; }
+
+#if PLATFORM(MAC)
+ static void setShouldRepostNotificationsForTests(bool value);
+#endif
+
protected:
void postPlatformNotification(AccessibilityObject*, AXNotification);
- void nodeTextChangePlatformNotification(AccessibilityObject*, AXTextChange, unsigned offset, const String&);
+ void platformHandleFocusedUIElementChanged(Node* oldFocusedNode, Node* newFocusedNode);
+
+#if PLATFORM(COCOA)
+ void postTextStateChangePlatformNotification(AccessibilityObject*, const AXTextStateChangeIntent&, const VisibleSelection&);
+ void postTextStateChangePlatformNotification(AccessibilityObject*, AXTextEditType, const String&, const VisiblePosition&);
+ void postTextReplacementPlatformNotification(AccessibilityObject*, AXTextEditType, const String&, AXTextEditType, const String&, const VisiblePosition&);
+#else
+ static AXTextChange textChangeForEditType(AXTextEditType);
+ void nodeTextChangePlatformNotification(AccessibilityObject*, AXTextChange, unsigned, const String&);
+#endif
+
void frameLoadingEventPlatformNotification(AccessibilityObject*, AXLoadingEvent);
void textChanged(AccessibilityObject*);
void labelChanged(Element*);
@@ -217,27 +309,80 @@ protected:
void setNodeInUse(Node* n) { m_textMarkerNodes.add(n); }
void removeNodeForUse(Node* n) { m_textMarkerNodes.remove(n); }
bool isNodeInUse(Node* n) { return m_textMarkerNodes.contains(n); }
+
+ // CharacterOffset functions.
+ enum TraverseOption { TraverseOptionDefault = 1 << 0, TraverseOptionToNodeEnd = 1 << 1, TraverseOptionIncludeStart = 1 << 2 };
+ Node* nextNode(Node*) const;
+ Node* previousNode(Node*) const;
+ CharacterOffset traverseToOffsetInRange(RefPtr<Range>, int, bool, bool stayWithinRange = false);
+ VisiblePosition visiblePositionFromCharacterOffset(const CharacterOffset&);
+ CharacterOffset characterOffsetFromVisiblePosition(const VisiblePosition&);
+ void setTextMarkerDataWithCharacterOffset(TextMarkerData&, const CharacterOffset&);
+ UChar32 characterAfter(const CharacterOffset&);
+ UChar32 characterBefore(const CharacterOffset&);
+ CharacterOffset startOrEndCharacterOffsetForRange(RefPtr<Range>, bool);
+ CharacterOffset characterOffsetForNodeAndOffset(Node&, int, TraverseOption = TraverseOptionDefault);
+ CharacterOffset previousBoundary(const CharacterOffset&, BoundarySearchFunction);
+ CharacterOffset nextBoundary(const CharacterOffset&, BoundarySearchFunction);
+ CharacterOffset startCharacterOffsetOfWord(const CharacterOffset&, EWordSide = RightWordIfOnBoundary);
+ CharacterOffset endCharacterOffsetOfWord(const CharacterOffset&, EWordSide = RightWordIfOnBoundary);
+ CharacterOffset startCharacterOffsetOfParagraph(const CharacterOffset&, EditingBoundaryCrossingRule = CannotCrossEditingBoundary);
+ CharacterOffset endCharacterOffsetOfParagraph(const CharacterOffset&, EditingBoundaryCrossingRule = CannotCrossEditingBoundary);
+ CharacterOffset startCharacterOffsetOfSentence(const CharacterOffset&);
+ CharacterOffset endCharacterOffsetOfSentence(const CharacterOffset&);
private:
- Document* m_document;
- HashMap<AXID, RefPtr<AccessibilityObject> > m_objects;
+ AccessibilityObject* rootWebArea();
+
+ static AccessibilityObject* focusedImageMapUIElement(HTMLAreaElement*);
+
+ AXID getAXID(AccessibilityObject*);
+
+ void notificationPostTimerFired();
+
+ void liveRegionChangedNotificationPostTimerFired();
+
+ void postTextStateChangeNotification(AccessibilityObject*, const AXTextStateChangeIntent&, const VisibleSelection&);
+
+ bool enqueuePasswordValueChangeNotification(AccessibilityObject*);
+ void passwordNotificationPostTimerFired();
+
+ void handleMenuOpened(Node*);
+ void handleLiveRegionCreated(Node*);
+ void handleMenuItemSelected(Node*);
+
+ // aria-modal related
+ void findAriaModalNodes();
+ void updateCurrentAriaModalNode();
+ bool isNodeVisible(Node*) const;
+
+ Document& m_document;
+ HashMap<AXID, RefPtr<AccessibilityObject>> m_objects;
HashMap<RenderObject*, AXID> m_renderObjectMapping;
HashMap<Widget*, AXID> m_widgetObjectMapping;
HashMap<Node*, AXID> m_nodeObjectMapping;
HashSet<Node*> m_textMarkerNodes;
- OwnPtr<AXComputedObjectAttributeCache> m_computedObjectAttributeCache;
- static bool gAccessibilityEnabled;
- static bool gAccessibilityEnhancedUserInterfaceEnabled;
-
+ std::unique_ptr<AXComputedObjectAttributeCache> m_computedObjectAttributeCache;
+ WEBCORE_EXPORT static bool gAccessibilityEnabled;
+ WEBCORE_EXPORT static bool gAccessibilityEnhancedUserInterfaceEnabled;
+
HashSet<AXID> m_idsInUse;
+
+ Timer m_notificationPostTimer;
+ Vector<std::pair<RefPtr<AccessibilityObject>, AXNotification>> m_notificationsToPost;
+
+ Timer m_passwordNotificationPostTimer;
+
+ ListHashSet<RefPtr<AccessibilityObject>> m_passwordNotificationsToPost;
- Timer<AXObjectCache> m_notificationPostTimer;
- Vector<pair<RefPtr<AccessibilityObject>, AXNotification> > m_notificationsToPost;
- void notificationPostTimerFired(Timer<AXObjectCache>*);
-
- static AccessibilityObject* focusedImageMapUIElement(HTMLAreaElement*);
+ Timer m_liveRegionChangedPostTimer;
+ ListHashSet<RefPtr<AccessibilityObject>> m_liveRegionObjectsSet;
- AXID getAXID(AccessibilityObject*);
+ Node* m_currentAriaModalNode;
+ ListHashSet<Node*> m_ariaModalNodesSet;
+
+ AXTextStateChangeIntent m_textSelectionIntent;
+ bool m_isSynchronizingSelection { false };
};
class AXAttributeCacheEnabler
@@ -245,11 +390,13 @@ class AXAttributeCacheEnabler
public:
explicit AXAttributeCacheEnabler(AXObjectCache *cache);
~AXAttributeCacheEnabler();
-
+
+#if HAVE(ACCESSIBILITY)
private:
AXObjectCache* m_cache;
+#endif
};
-
+
bool nodeHasRole(Node*, const String& role);
// This will let you know if aria-hidden was explicitly set to false.
bool isNodeAriaVisible(Node*);
@@ -257,56 +404,78 @@ bool isNodeAriaVisible(Node*);
#if !HAVE(ACCESSIBILITY)
inline AccessibilityObjectInclusion AXComputedObjectAttributeCache::getIgnored(AXID) const { return DefaultBehavior; }
inline void AXComputedObjectAttributeCache::setIgnored(AXID, AccessibilityObjectInclusion) { }
-inline AXObjectCache::AXObjectCache(const Document* doc) : m_document(const_cast<Document*>(doc)), m_notificationPostTimer(this, 0) { }
+inline AXObjectCache::AXObjectCache(Document& document) : m_document(document), m_notificationPostTimer(nullptr), m_passwordNotificationPostTimer(nullptr), m_liveRegionChangedPostTimer(nullptr) { }
inline AXObjectCache::~AXObjectCache() { }
-inline AccessibilityObject* AXObjectCache::focusedUIElementForPage(const Page*) { return 0; }
-inline AccessibilityObject* AXObjectCache::get(RenderObject*) { return 0; }
-inline AccessibilityObject* AXObjectCache::get(Node*) { return 0; }
-inline AccessibilityObject* AXObjectCache::get(Widget*) { return 0; }
-inline AccessibilityObject* AXObjectCache::getOrCreate(AccessibilityRole) { return 0; }
-inline AccessibilityObject* AXObjectCache::getOrCreate(RenderObject*) { return 0; }
-inline AccessibilityObject* AXObjectCache::getOrCreate(Node*) { return 0; }
-inline AccessibilityObject* AXObjectCache::getOrCreate(Widget*) { return 0; }
-inline AccessibilityObject* AXObjectCache::rootObject() { return 0; }
-inline AccessibilityObject* AXObjectCache::rootObjectForFrame(Frame*) { return 0; }
-inline Element* AXObjectCache::rootAXEditableElement(Node*) { return 0; }
+inline AccessibilityObject* AXObjectCache::focusedUIElementForPage(const Page*) { return nullptr; }
+inline AccessibilityObject* AXObjectCache::get(RenderObject*) { return nullptr; }
+inline AccessibilityObject* AXObjectCache::get(Node*) { return nullptr; }
+inline AccessibilityObject* AXObjectCache::get(Widget*) { return nullptr; }
+inline AccessibilityObject* AXObjectCache::getOrCreate(AccessibilityRole) { return nullptr; }
+inline AccessibilityObject* AXObjectCache::getOrCreate(RenderObject*) { return nullptr; }
+inline AccessibilityObject* AXObjectCache::getOrCreate(Node*) { return nullptr; }
+inline AccessibilityObject* AXObjectCache::getOrCreate(Widget*) { return nullptr; }
+inline AccessibilityObject* AXObjectCache::rootObject() { return nullptr; }
+inline AccessibilityObject* AXObjectCache::rootObjectForFrame(Frame*) { return nullptr; }
inline bool nodeHasRole(Node*, const String&) { return false; }
inline void AXObjectCache::startCachingComputedObjectAttributesUntilTreeMutates() { }
inline void AXObjectCache::stopCachingComputedObjectAttributes() { }
inline bool isNodeAriaVisible(Node*) { return true; }
-inline const Element* AXObjectCache::rootAXEditableElement(const Node*) { return 0; }
+inline const Element* AXObjectCache::rootAXEditableElement(const Node*) { return nullptr; }
+inline Node* AXObjectCache::ariaModalNode() { return nullptr; }
inline void AXObjectCache::attachWrapper(AccessibilityObject*) { }
inline void AXObjectCache::checkedStateChanged(Node*) { }
-inline void AXObjectCache::childrenChanged(RenderObject*) { }
-inline void AXObjectCache::childrenChanged(Node*) { }
+inline void AXObjectCache::childrenChanged(RenderObject*, RenderObject*) { }
+inline void AXObjectCache::childrenChanged(Node*, Node*) { }
inline void AXObjectCache::childrenChanged(AccessibilityObject*) { }
inline void AXObjectCache::textChanged(RenderObject*) { }
inline void AXObjectCache::textChanged(Node*) { }
inline void AXObjectCache::textChanged(AccessibilityObject*) { }
inline void AXObjectCache::updateCacheAfterNodeIsAttached(Node*) { }
-inline void AXObjectCache::detachWrapper(AccessibilityObject*) { }
+inline void AXObjectCache::detachWrapper(AccessibilityObject*, AccessibilityDetachmentType) { }
inline void AXObjectCache::frameLoadingEventNotification(Frame*, AXLoadingEvent) { }
inline void AXObjectCache::frameLoadingEventPlatformNotification(AccessibilityObject*, AXLoadingEvent) { }
inline void AXObjectCache::handleActiveDescendantChanged(Node*) { }
inline void AXObjectCache::handleAriaExpandedChange(Node*) { }
inline void AXObjectCache::handleAriaRoleChanged(Node*) { }
+inline void AXObjectCache::handleAriaModalChange(Node*) { }
inline void AXObjectCache::handleFocusedUIElementChanged(Node*, Node*) { }
inline void AXObjectCache::handleScrollbarUpdate(ScrollView*) { }
inline void AXObjectCache::handleAttributeChanged(const QualifiedName&, Element*) { }
inline void AXObjectCache::recomputeIsIgnored(RenderObject*) { }
inline void AXObjectCache::handleScrolledToAnchor(const Node*) { }
-inline void AXObjectCache::nodeTextChangeNotification(Node*, AXTextChange, unsigned, const String&) { }
-inline void AXObjectCache::nodeTextChangePlatformNotification(AccessibilityObject*, AXTextChange, unsigned, const String&) { }
-inline void AXObjectCache::postNotification(AccessibilityObject*, Document*, AXNotification, bool, PostType) { }
-inline void AXObjectCache::postNotification(RenderObject*, AXNotification, bool, PostType) { }
-inline void AXObjectCache::postNotification(Node*, AXNotification, bool, PostType) { }
+inline void AXObjectCache::postTextStateChangeNotification(Node*, const AXTextStateChangeIntent&, const VisibleSelection&) { }
+inline void AXObjectCache::postTextStateChangeNotification(Node*, AXTextEditType, const String&, const VisiblePosition&) { }
+inline void AXObjectCache::postTextReplacementNotification(Node*, AXTextEditType, const String&, AXTextEditType, const String&, const VisiblePosition&) { }
+inline void AXObjectCache::postNotification(AccessibilityObject*, Document*, AXNotification, PostTarget, PostType) { }
+inline void AXObjectCache::postNotification(RenderObject*, AXNotification, PostTarget, PostType) { }
+inline void AXObjectCache::postNotification(Node*, AXNotification, PostTarget, PostType) { }
inline void AXObjectCache::postPlatformNotification(AccessibilityObject*, AXNotification) { }
+inline void AXObjectCache::postLiveRegionChangeNotification(AccessibilityObject*) { }
+inline RefPtr<Range> AXObjectCache::rangeForNodeContents(Node*) { return nullptr; }
inline void AXObjectCache::remove(AXID) { }
inline void AXObjectCache::remove(RenderObject*) { }
inline void AXObjectCache::remove(Node*) { }
inline void AXObjectCache::remove(Widget*) { }
inline void AXObjectCache::selectedChildrenChanged(RenderObject*) { }
inline void AXObjectCache::selectedChildrenChanged(Node*) { }
+inline void AXObjectCache::setIsSynchronizingSelection(bool) { }
+inline void AXObjectCache::setTextSelectionIntent(const AXTextStateChangeIntent&) { }
+inline RefPtr<Range> AXObjectCache::rangeForUnorderedCharacterOffsets(const CharacterOffset&, const CharacterOffset&) { return nullptr; }
+inline CharacterOffset AXObjectCache::startOrEndCharacterOffsetForRange(RefPtr<Range>, bool) { return CharacterOffset(); }
+inline CharacterOffset AXObjectCache::nextCharacterOffset(const CharacterOffset&, bool) { return CharacterOffset(); }
+inline CharacterOffset AXObjectCache::previousCharacterOffset(const CharacterOffset&, bool) { return CharacterOffset(); }
+#if PLATFORM(COCOA)
+inline void AXObjectCache::postTextStateChangePlatformNotification(AccessibilityObject*, const AXTextStateChangeIntent&, const VisibleSelection&) { }
+inline void AXObjectCache::postTextStateChangePlatformNotification(AccessibilityObject*, AXTextEditType, const String&, const VisiblePosition&) { }
+inline void AXObjectCache::postTextReplacementPlatformNotification(AccessibilityObject*, AXTextEditType, const String&, AXTextEditType, const String&, const VisiblePosition&) { }
+#else
+inline AXTextChange AXObjectCache::textChangeForEditType(AXTextEditType) { return AXTextInserted; }
+inline void AXObjectCache::nodeTextChangePlatformNotification(AccessibilityObject*, AXTextChange, unsigned, const String&) { }
+#endif
+
+inline AXAttributeCacheEnabler::AXAttributeCacheEnabler(AXObjectCache*) { }
+inline AXAttributeCacheEnabler::~AXAttributeCacheEnabler() { }
+
#endif
}
diff --git a/Source/WebCore/accessibility/AXTextStateChangeIntent.h b/Source/WebCore/accessibility/AXTextStateChangeIntent.h
new file mode 100644
index 000000000..389dd9aa7
--- /dev/null
+++ b/Source/WebCore/accessibility/AXTextStateChangeIntent.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef AXTextStateChangeIntent_h
+#define AXTextStateChangeIntent_h
+
+namespace WebCore {
+
+enum AXTextStateChangeType {
+ AXTextStateChangeTypeUnknown,
+ AXTextStateChangeTypeEdit,
+ AXTextStateChangeTypeSelectionMove,
+ AXTextStateChangeTypeSelectionExtend,
+ AXTextStateChangeTypeSelectionBoundary
+};
+
+enum AXTextEditType {
+ AXTextEditTypeUnknown,
+ AXTextEditTypeDelete, // Generic text delete
+ AXTextEditTypeInsert, // Generic text insert
+ AXTextEditTypeTyping, // Insert via typing
+ AXTextEditTypeDictation, // Insert via dictation
+ AXTextEditTypeCut, // Delete via Cut
+ AXTextEditTypePaste, // Insert via Paste
+ AXTextEditTypeAttributesChange // Change font, style, alignment, color, etc.
+};
+
+enum AXTextSelectionDirection {
+ AXTextSelectionDirectionUnknown,
+ AXTextSelectionDirectionBeginning,
+ AXTextSelectionDirectionEnd,
+ AXTextSelectionDirectionPrevious,
+ AXTextSelectionDirectionNext,
+ AXTextSelectionDirectionDiscontiguous
+};
+
+enum AXTextSelectionGranularity {
+ AXTextSelectionGranularityUnknown,
+ AXTextSelectionGranularityCharacter,
+ AXTextSelectionGranularityWord,
+ AXTextSelectionGranularityLine,
+ AXTextSelectionGranularitySentence,
+ AXTextSelectionGranularityParagraph,
+ AXTextSelectionGranularityPage,
+ AXTextSelectionGranularityDocument,
+ AXTextSelectionGranularityAll // All granularity represents the action of selecting the whole document as a single action. Extending selection by some other granularity until it encompasses the whole document will not result in a all granularity notification.
+};
+
+struct AXTextSelection {
+ AXTextSelectionDirection direction;
+ AXTextSelectionGranularity granularity;
+ bool focusChange;
+};
+
+struct AXTextStateChangeIntent {
+ AXTextStateChangeType type;
+ union {
+ AXTextSelection selection;
+ AXTextEditType change;
+ };
+
+ AXTextStateChangeIntent(AXTextStateChangeType type = AXTextStateChangeTypeUnknown, AXTextSelection selection = AXTextSelection())
+ : type(type)
+ , selection(selection)
+ { }
+
+ AXTextStateChangeIntent(AXTextEditType change)
+ : type(AXTextStateChangeTypeEdit)
+ , change(change)
+ { }
+};
+
+}
+
+#endif // AXTextStateChangeIntent_h
diff --git a/Source/WebCore/accessibility/AccessibilityARIAGrid.cpp b/Source/WebCore/accessibility/AccessibilityARIAGrid.cpp
index 5632f5aaa..a0b95760c 100644
--- a/Source/WebCore/accessibility/AccessibilityARIAGrid.cpp
+++ b/Source/WebCore/accessibility/AccessibilityARIAGrid.cpp
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -30,13 +30,13 @@
#include "AccessibilityARIAGrid.h"
#include "AXObjectCache.h"
+#include "AccessibilityARIAGridRow.h"
#include "AccessibilityTableCell.h"
#include "AccessibilityTableColumn.h"
#include "AccessibilityTableHeaderContainer.h"
#include "AccessibilityTableRow.h"
#include "RenderObject.h"
-
-using namespace std;
+#include "RenderTableSection.h"
namespace WebCore {
@@ -49,36 +49,36 @@ AccessibilityARIAGrid::~AccessibilityARIAGrid()
{
}
-PassRefPtr<AccessibilityARIAGrid> AccessibilityARIAGrid::create(RenderObject* renderer)
+Ref<AccessibilityARIAGrid> AccessibilityARIAGrid::create(RenderObject* renderer)
{
- return adoptRef(new AccessibilityARIAGrid(renderer));
+ return adoptRef(*new AccessibilityARIAGrid(renderer));
}
bool AccessibilityARIAGrid::addTableCellChild(AccessibilityObject* child, HashSet<AccessibilityObject*>& appendedRows, unsigned& columnCount)
{
- if (!child || !child->isTableRow() || child->ariaRoleAttribute() != RowRole)
+ if (!child || (!is<AccessibilityTableRow>(*child) && !is<AccessibilityARIAGridRow>(*child)))
return false;
- AccessibilityTableRow* row = static_cast<AccessibilityTableRow*>(child);
- if (appendedRows.contains(row))
+ auto& row = downcast<AccessibilityTableRow>(*child);
+ if (appendedRows.contains(&row))
return false;
// store the maximum number of columns
- unsigned rowCellCount = row->children().size();
+ unsigned rowCellCount = row.children().size();
if (rowCellCount > columnCount)
columnCount = rowCellCount;
- row->setRowIndex((int)m_rows.size());
- m_rows.append(row);
+ row.setRowIndex((int)m_rows.size());
+ m_rows.append(&row);
// Try adding the row if it's not ignoring accessibility,
// otherwise add its children (the cells) as the grid's children.
- if (!row->accessibilityIsIgnored())
- m_children.append(row);
+ if (!row.accessibilityIsIgnored())
+ m_children.append(&row);
else
- m_children.appendVector(row->children());
+ m_children.appendVector(row.children());
- appendedRows.add(row);
+ appendedRows.add(&row);
return true;
}
@@ -87,13 +87,11 @@ void AccessibilityARIAGrid::addRowDescendant(AccessibilityObject* rowChild, Hash
if (!rowChild)
return;
- if (!rowChild->isTableRow()) {
+ if (!rowChild->isTableRow() || !rowChild->node()) {
// Although a "grid" should have rows as its direct descendants, if this is not a table row,
- // dive deeper into the descendants to try to find a valid row.
- AccessibilityChildrenVector children = rowChild->children();
- size_t length = children.size();
- for (size_t i = 0; i < length; ++i)
- addRowDescendant(children[i].get(), appendedRows, columnCount);
+ // or this row is anonymous, dive deeper into the descendants to try to find a valid row.
+ for (const auto& child : rowChild->children())
+ addRowDescendant(child.get(), appendedRows, columnCount);
} else
addTableCellChild(rowChild, appendedRows, columnCount);
}
@@ -102,7 +100,7 @@ void AccessibilityARIAGrid::addChildren()
{
ASSERT(!m_haveChildren);
- if (!isAccessibilityTable()) {
+ if (!isExposableThroughAccessibility()) {
AccessibilityRenderObject::addChildren();
return;
}
@@ -111,22 +109,38 @@ void AccessibilityARIAGrid::addChildren()
if (!m_renderer)
return;
- AXObjectCache* axCache = m_renderer->document()->axObjectCache();
+ AXObjectCache* axCache = m_renderer->document().axObjectCache();
- // add only rows that are labeled as aria rows
+ // Add the children rows but be mindful in case there are footer sections in this table.
HashSet<AccessibilityObject*> appendedRows;
unsigned columnCount = 0;
- for (RefPtr<AccessibilityObject> child = firstChild(); child; child = child->nextSibling())
- addRowDescendant(child.get(), appendedRows, columnCount);
+ AccessibilityChildrenVector footerSections;
+ for (RefPtr<AccessibilityObject> child = firstChild(); child; child = child->nextSibling()) {
+ bool footerSection = false;
+ if (RenderObject* childRenderer = child->renderer()) {
+ if (is<RenderTableSection>(*childRenderer)) {
+ RenderTableSection& childSection = downcast<RenderTableSection>(*childRenderer);
+ if (&childSection == childSection.table()->footer()) {
+ footerSections.append(child);
+ footerSection = true;
+ }
+ }
+ }
+ if (!footerSection)
+ addRowDescendant(child.get(), appendedRows, columnCount);
+ }
+
+ for (const auto& footerSection : footerSections)
+ addRowDescendant(footerSection.get(), appendedRows, columnCount);
// make the columns based on the number of columns in the first body
for (unsigned i = 0; i < columnCount; ++i) {
- AccessibilityTableColumn* column = static_cast<AccessibilityTableColumn*>(axCache->getOrCreate(ColumnRole));
- column->setColumnIndex((int)i);
- column->setParent(this);
- m_columns.append(column);
- if (!column->accessibilityIsIgnored())
- m_children.append(column);
+ auto& column = downcast<AccessibilityTableColumn>(*axCache->getOrCreate(ColumnRole));
+ column.setColumnIndex(static_cast<int>(i));
+ column.setParent(this);
+ m_columns.append(&column);
+ if (!column.accessibilityIsIgnored())
+ m_children.append(&column);
}
AccessibilityObject* headerContainerObject = headerContainer();
diff --git a/Source/WebCore/accessibility/AccessibilityARIAGrid.h b/Source/WebCore/accessibility/AccessibilityARIAGrid.h
index 22910e0a3..f16cb916a 100644
--- a/Source/WebCore/accessibility/AccessibilityARIAGrid.h
+++ b/Source/WebCore/accessibility/AccessibilityARIAGrid.h
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -37,23 +37,21 @@ namespace WebCore {
class AccessibilityTableCell;
class AccessibilityTableHeaderContainer;
-class AccessibilityARIAGrid : public AccessibilityTable {
-
-private:
- explicit AccessibilityARIAGrid(RenderObject*);
+class AccessibilityARIAGrid final : public AccessibilityTable {
public:
- static PassRefPtr<AccessibilityARIAGrid> create(RenderObject*);
+ static Ref<AccessibilityARIAGrid> create(RenderObject*);
virtual ~AccessibilityARIAGrid();
-
- virtual bool isAriaTable() const { return true; }
- virtual void addChildren();
+ virtual void addChildren() override;
private:
+ explicit AccessibilityARIAGrid(RenderObject*);
+
// ARIA treegrids and grids support selected rows.
- virtual bool supportsSelectedRows() { return true; }
- virtual bool isMultiSelectable() const { return true; }
- virtual bool isTableExposableThroughAccessibility() const { return true; }
+ virtual bool supportsSelectedRows() override { return true; }
+ virtual bool isMultiSelectable() const override { return true; }
+ virtual bool computeIsTableExposableThroughAccessibility() const override { return true; }
+ virtual bool isAriaTable() const override { return true; }
void addRowDescendant(AccessibilityObject*, HashSet<AccessibilityObject*>& appendedRows, unsigned& columnCount);
bool addTableCellChild(AccessibilityObject*, HashSet<AccessibilityObject*>& appendedRows, unsigned& columnCount);
diff --git a/Source/WebCore/accessibility/AccessibilityARIAGridCell.cpp b/Source/WebCore/accessibility/AccessibilityARIAGridCell.cpp
index 9c3b1679d..36aac823d 100644
--- a/Source/WebCore/accessibility/AccessibilityARIAGridCell.cpp
+++ b/Source/WebCore/accessibility/AccessibilityARIAGridCell.cpp
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -32,11 +32,12 @@
#include "AccessibilityObject.h"
#include "AccessibilityTable.h"
#include "AccessibilityTableRow.h"
-
-using namespace std;
+#include "HTMLNames.h"
namespace WebCore {
+using namespace HTMLNames;
+
AccessibilityARIAGridCell::AccessibilityARIAGridCell(RenderObject* renderer)
: AccessibilityTableCell(renderer)
{
@@ -46,47 +47,41 @@ AccessibilityARIAGridCell::~AccessibilityARIAGridCell()
{
}
-PassRefPtr<AccessibilityARIAGridCell> AccessibilityARIAGridCell::create(RenderObject* renderer)
+Ref<AccessibilityARIAGridCell> AccessibilityARIAGridCell::create(RenderObject* renderer)
{
- return adoptRef(new AccessibilityARIAGridCell(renderer));
+ return adoptRef(*new AccessibilityARIAGridCell(renderer));
}
-AccessibilityObject* AccessibilityARIAGridCell::parentTable() const
+AccessibilityTable* AccessibilityARIAGridCell::parentTable() const
{
- AccessibilityObject* parent = parentObjectUnignored();
- if (!parent)
- return 0;
-
- if (parent->isAccessibilityTable())
- return parent;
-
- // It could happen that we hadn't reached the parent table yet (in
- // case objects for rows were not ignoring accessibility) so for
- // that reason we need to run parentObjectUnignored once again.
- parent = parent->parentObjectUnignored();
- if (!parent || !parent->isAccessibilityTable())
- return 0;
-
- return parent;
+ // ARIA gridcells may have multiple levels of unignored ancestors that are not the parent table,
+ // including rows and interactive rowgroups. In addition, poorly-formed grids may contain elements
+ // which pass the tests for inclusion.
+ for (AccessibilityObject* parent = parentObjectUnignored(); parent; parent = parent->parentObjectUnignored()) {
+ if (is<AccessibilityTable>(*parent) && downcast<AccessibilityTable>(*parent).isExposableThroughAccessibility())
+ return downcast<AccessibilityTable>(parent);
+ }
+
+ return nullptr;
}
-void AccessibilityARIAGridCell::rowIndexRange(pair<unsigned, unsigned>& rowRange)
+void AccessibilityARIAGridCell::rowIndexRange(std::pair<unsigned, unsigned>& rowRange) const
{
AccessibilityObject* parent = parentObjectUnignored();
if (!parent)
return;
- if (parent->isTableRow()) {
+ if (is<AccessibilityTableRow>(*parent)) {
// We already got a table row, use its API.
- rowRange.first = static_cast<AccessibilityTableRow*>(parent)->rowIndex();
- } else if (parent->isAccessibilityTable()) {
+ rowRange.first = downcast<AccessibilityTableRow>(*parent).rowIndex();
+ } else if (is<AccessibilityTable>(*parent) && downcast<AccessibilityTable>(*parent).isExposableThroughAccessibility()) {
// We reached the parent table, so we need to inspect its
// children to determine the row index for the cell in it.
- unsigned columnCount = static_cast<AccessibilityTable*>(parent)->columnCount();
+ unsigned columnCount = downcast<AccessibilityTable>(*parent).columnCount();
if (!columnCount)
return;
- AccessibilityChildrenVector siblings = parent->children();
+ const auto& siblings = parent->children();
unsigned childrenSize = siblings.size();
for (unsigned k = 0; k < childrenSize; ++k) {
if (siblings[k].get() == this) {
@@ -96,20 +91,54 @@ void AccessibilityARIAGridCell::rowIndexRange(pair<unsigned, unsigned>& rowRange
}
}
- // as far as I can tell, grid cells cannot span rows
- rowRange.second = 1;
+ // ARIA 1.1, aria-rowspan attribute is intended for cells and gridcells which are not contained in a native table.
+ // So we should check for that attribute here.
+ rowRange.second = ariaRowSpanWithRowIndex(rowRange.first);
}
-void AccessibilityARIAGridCell::columnIndexRange(pair<unsigned, unsigned>& columnRange)
+unsigned AccessibilityARIAGridCell::ariaRowSpanWithRowIndex(unsigned rowIndex) const
+{
+ unsigned rowSpan = AccessibilityTableCell::ariaRowSpan();
+ AccessibilityObject* parent = parentObjectUnignored();
+ if (!parent)
+ return 1;
+
+ // Setting the value to 0 indicates that the cell or gridcell is to span all the remaining rows in the row group.
+ if (!rowSpan) {
+ // rowSpan defaults to 1.
+ rowSpan = 1;
+ if (AccessibilityObject* parentRowGroup = this->parentRowGroup()) {
+ // If the row group is the parent table, we use total row count to calculate the span.
+ if (is<AccessibilityTable>(*parentRowGroup))
+ rowSpan = downcast<AccessibilityTable>(*parentRowGroup).rowCount() - rowIndex;
+ // Otherwise, we have to get the index for the current row within the parent row group.
+ else if (is<AccessibilityTableRow>(*parent)) {
+ const auto& siblings = parentRowGroup->children();
+ unsigned rowCount = siblings.size();
+ for (unsigned k = 0; k < rowCount; ++k) {
+ if (siblings[k].get() == parent) {
+ rowSpan = rowCount - k;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return rowSpan;
+}
+
+void AccessibilityARIAGridCell::columnIndexRange(std::pair<unsigned, unsigned>& columnRange) const
{
AccessibilityObject* parent = parentObjectUnignored();
if (!parent)
return;
- if (!parent->isTableRow() && !parent->isAccessibilityTable())
+ if (!is<AccessibilityTableRow>(*parent)
+ && !(is<AccessibilityTable>(*parent) && downcast<AccessibilityTable>(*parent).isExposableThroughAccessibility()))
return;
- AccessibilityChildrenVector siblings = parent->children();
+ const AccessibilityChildrenVector& siblings = parent->children();
unsigned childrenSize = siblings.size();
for (unsigned k = 0; k < childrenSize; ++k) {
if (siblings[k].get() == this) {
@@ -118,8 +147,20 @@ void AccessibilityARIAGridCell::columnIndexRange(pair<unsigned, unsigned>& colum
}
}
- // as far as I can tell, grid cells cannot span columns
- columnRange.second = 1;
+ // ARIA 1.1, aria-colspan attribute is intended for cells and gridcells which are not contained in a native table.
+ // So we should check for that attribute here.
+ columnRange.second = ariaColumnSpan();
+}
+
+AccessibilityObject* AccessibilityARIAGridCell::parentRowGroup() const
+{
+ for (AccessibilityObject* parent = parentObject(); parent; parent = parent->parentObject()) {
+ if (parent->hasTagName(theadTag) || parent->hasTagName(tbodyTag) || parent->hasTagName(tfootTag) || parent->roleValue() == RowGroupRole)
+ return parent;
+ }
+
+ // If there's no row group found, we use the parent table as the row group.
+ return parentTable();
}
} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AccessibilityARIAGridCell.h b/Source/WebCore/accessibility/AccessibilityARIAGridCell.h
index d5ae4ac59..aa77136be 100644
--- a/Source/WebCore/accessibility/AccessibilityARIAGridCell.h
+++ b/Source/WebCore/accessibility/AccessibilityARIAGridCell.h
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -33,21 +33,22 @@
namespace WebCore {
-class AccessibilityARIAGridCell : public AccessibilityTableCell {
-
-private:
- explicit AccessibilityARIAGridCell(RenderObject*);
+class AccessibilityARIAGridCell final : public AccessibilityTableCell {
public:
- static PassRefPtr<AccessibilityARIAGridCell> create(RenderObject*);
+ static Ref<AccessibilityARIAGridCell> create(RenderObject*);
virtual ~AccessibilityARIAGridCell();
// fills in the start location and row span of cell
- virtual void rowIndexRange(pair<unsigned, unsigned>& rowRange);
+ virtual void rowIndexRange(std::pair<unsigned, unsigned>& rowRange) const override;
// fills in the start location and column span of cell
- virtual void columnIndexRange(pair<unsigned, unsigned>& columnRange);
+ virtual void columnIndexRange(std::pair<unsigned, unsigned>& columnRange) const override;
-protected:
- virtual AccessibilityObject* parentTable() const;
+private:
+ explicit AccessibilityARIAGridCell(RenderObject*);
+
+ virtual AccessibilityTable* parentTable() const override;
+ AccessibilityObject* parentRowGroup() const;
+ unsigned ariaRowSpanWithRowIndex(unsigned index) const;
};
} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AccessibilityARIAGridRow.cpp b/Source/WebCore/accessibility/AccessibilityARIAGridRow.cpp
index 39edf702f..dd4fda567 100644
--- a/Source/WebCore/accessibility/AccessibilityARIAGridRow.cpp
+++ b/Source/WebCore/accessibility/AccessibilityARIAGridRow.cpp
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -33,8 +33,6 @@
#include "AccessibilityTable.h"
#include "RenderObject.h"
-using namespace std;
-
namespace WebCore {
AccessibilityARIAGridRow::AccessibilityARIAGridRow(RenderObject* renderer)
@@ -46,9 +44,9 @@ AccessibilityARIAGridRow::~AccessibilityARIAGridRow()
{
}
-PassRefPtr<AccessibilityARIAGridRow> AccessibilityARIAGridRow::create(RenderObject* renderer)
+Ref<AccessibilityARIAGridRow> AccessibilityARIAGridRow::create(RenderObject* renderer)
{
- return adoptRef(new AccessibilityARIAGridRow(renderer));
+ return adoptRef(*new AccessibilityARIAGridRow(renderer));
}
bool AccessibilityARIAGridRow::isARIATreeGridRow() const
@@ -65,7 +63,7 @@ void AccessibilityARIAGridRow::disclosedRows(AccessibilityChildrenVector& disclo
// The contiguous disclosed rows will be the rows in the table that
// have an aria-level of plus 1 from this row.
AccessibilityObject* parent = parentObjectUnignored();
- if (!parent || !parent->isAccessibilityTable())
+ if (!is<AccessibilityTable>(*parent) || !downcast<AccessibilityTable>(*parent).isExposableThroughAccessibility())
return;
// Search for rows that match the correct level.
@@ -75,7 +73,7 @@ void AccessibilityARIAGridRow::disclosedRows(AccessibilityChildrenVector& disclo
return;
unsigned level = hierarchicalLevel();
- AccessibilityChildrenVector& allRows = static_cast<AccessibilityTable*>(parent)->rows();
+ auto& allRows = downcast<AccessibilityTable>(*parent).rows();
int rowCount = allRows.size();
for (int k = index + 1; k < rowCount; ++k) {
AccessibilityObject* row = allRows[k].get();
@@ -92,20 +90,20 @@ AccessibilityObject* AccessibilityARIAGridRow::disclosedByRow() const
// The row that discloses this one is the row in the table
// that is aria-level subtract 1 from this row.
AccessibilityObject* parent = parentObjectUnignored();
- if (!parent || !parent->isAccessibilityTable())
- return 0;
+ if (!is<AccessibilityTable>(*parent) || !downcast<AccessibilityTable>(*parent).isExposableThroughAccessibility())
+ return nullptr;
// If the level is 1 or less, than nothing discloses this row.
unsigned level = hierarchicalLevel();
if (level <= 1)
- return 0;
+ return nullptr;
// Search for the previous row that matches the correct level.
int index = rowIndex();
- AccessibilityChildrenVector& allRows = static_cast<AccessibilityTable*>(parent)->rows();
+ auto& allRows = downcast<AccessibilityTable>(parent)->rows();
int rowCount = allRows.size();
if (index >= rowCount)
- return 0;
+ return nullptr;
for (int k = index - 1; k >= 0; --k) {
AccessibilityObject* row = allRows[k].get();
@@ -113,33 +111,38 @@ AccessibilityObject* AccessibilityARIAGridRow::disclosedByRow() const
return row;
}
- return 0;
+ return nullptr;
+}
+
+AccessibilityObject* AccessibilityARIAGridRow::parentObjectUnignored() const
+{
+ return parentTable();
}
-AccessibilityObject* AccessibilityARIAGridRow::parentTable() const
+AccessibilityTable* AccessibilityARIAGridRow::parentTable() const
{
// The parent table might not be the direct ancestor of the row unfortunately. ARIA states that role="grid" should
// only have "row" elements, but if not, we still should handle it gracefully by finding the right table.
for (AccessibilityObject* parent = parentObject(); parent; parent = parent->parentObject()) {
// The parent table for an ARIA grid row should be an ARIA table.
- if (parent->isTable() && parent->isAccessibilityTable() && toAccessibilityTable(parent)->isAriaTable())
- return parent;
+ if (is<AccessibilityTable>(*parent)) {
+ AccessibilityTable& tableParent = downcast<AccessibilityTable>(*parent);
+ if (tableParent.isExposableThroughAccessibility() && tableParent.isAriaTable())
+ return &tableParent;
+ }
}
- return 0;
+ return nullptr;
}
AccessibilityObject* AccessibilityARIAGridRow::headerObject()
{
- AccessibilityChildrenVector rowChildren = children();
- unsigned childrenCount = rowChildren.size();
- for (unsigned i = 0; i < childrenCount; ++i) {
- AccessibilityObject* cell = rowChildren[i].get();
- if (cell->ariaRoleAttribute() == RowHeaderRole)
- return cell;
+ for (const auto& child : children()) {
+ if (child->ariaRoleAttribute() == RowHeaderRole)
+ return child.get();
}
- return 0;
+ return nullptr;
}
} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AccessibilityARIAGridRow.h b/Source/WebCore/accessibility/AccessibilityARIAGridRow.h
index 054fcf6ff..2d9ac292f 100644
--- a/Source/WebCore/accessibility/AccessibilityARIAGridRow.h
+++ b/Source/WebCore/accessibility/AccessibilityARIAGridRow.h
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -33,24 +33,28 @@
namespace WebCore {
-class AccessibilityARIAGridRow : public AccessibilityTableRow {
+class AccessibilityTable;
-private:
- explicit AccessibilityARIAGridRow(RenderObject*);
+class AccessibilityARIAGridRow final : public AccessibilityTableRow {
public:
- static PassRefPtr<AccessibilityARIAGridRow> create(RenderObject*);
+ static Ref<AccessibilityARIAGridRow> create(RenderObject*);
virtual ~AccessibilityARIAGridRow();
void disclosedRows(AccessibilityChildrenVector&);
AccessibilityObject* disclosedByRow() const;
- virtual AccessibilityObject* headerObject();
+ virtual AccessibilityObject* headerObject() override;
private:
- virtual bool isARIATreeGridRow() const;
- virtual AccessibilityObject* parentTable() const;
-};
-
+ explicit AccessibilityARIAGridRow(RenderObject*);
+
+ virtual bool isARIATreeGridRow() const override;
+ virtual AccessibilityTable* parentTable() const override;
+ virtual AccessibilityObject* parentObjectUnignored() const override;
+};
+
} // namespace WebCore
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilityARIAGridRow, isARIATreeGridRow())
+
#endif // AccessibilityARIAGridRow_h
diff --git a/Source/WebCore/accessibility/AccessibilityAllInOne.cpp b/Source/WebCore/accessibility/AccessibilityAllInOne.cpp
index 00740fa43..06afd8b22 100644
--- a/Source/WebCore/accessibility/AccessibilityAllInOne.cpp
+++ b/Source/WebCore/accessibility/AccessibilityAllInOne.cpp
@@ -34,6 +34,10 @@
#include "AccessibilityListBox.cpp"
#include "AccessibilityListBoxOption.cpp"
#include "AccessibilityMediaControls.cpp"
+#include "AccessibilityMenuList.cpp"
+#include "AccessibilityMenuListOption.cpp"
+#include "AccessibilityMenuListPopup.cpp"
+#include "AccessibilityMockObject.cpp"
#include "AccessibilityNodeObject.cpp"
#include "AccessibilityObject.cpp"
#include "AccessibilityProgressIndicator.cpp"
@@ -42,9 +46,11 @@
#include "AccessibilityScrollView.cpp"
#include "AccessibilityScrollbar.cpp"
#include "AccessibilitySlider.cpp"
+#include "AccessibilitySpinButton.cpp"
#include "AccessibilityTable.cpp"
#include "AccessibilityTableCell.cpp"
#include "AccessibilityTableColumn.cpp"
#include "AccessibilityTableHeaderContainer.cpp"
#include "AccessibilityTableRow.cpp"
-
+#include "AccessibilityTree.cpp"
+#include "AccessibilityTreeItem.cpp"
diff --git a/Source/WebCore/accessibility/AccessibilityImageMapLink.cpp b/Source/WebCore/accessibility/AccessibilityImageMapLink.cpp
index 132f16b0b..96bc1392b 100644
--- a/Source/WebCore/accessibility/AccessibilityImageMapLink.cpp
+++ b/Source/WebCore/accessibility/AccessibilityImageMapLink.cpp
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -40,8 +40,8 @@ namespace WebCore {
using namespace HTMLNames;
AccessibilityImageMapLink::AccessibilityImageMapLink()
- : m_areaElement(0)
- , m_mapElement(0)
+ : m_areaElement(nullptr)
+ , m_mapElement(nullptr)
{
}
@@ -49,9 +49,9 @@ AccessibilityImageMapLink::~AccessibilityImageMapLink()
{
}
-PassRefPtr<AccessibilityImageMapLink> AccessibilityImageMapLink::create()
+Ref<AccessibilityImageMapLink> AccessibilityImageMapLink::create()
{
- return adoptRef(new AccessibilityImageMapLink());
+ return adoptRef(*new AccessibilityImageMapLink());
}
AccessibilityObject* AccessibilityImageMapLink::parentObject() const
@@ -60,9 +60,9 @@ AccessibilityObject* AccessibilityImageMapLink::parentObject() const
return m_parent;
if (!m_mapElement.get() || !m_mapElement->renderer())
- return 0;
+ return nullptr;
- return m_mapElement->document()->axObjectCache()->getOrCreate(m_mapElement->renderer());
+ return m_mapElement->document().axObjectCache()->getOrCreate(m_mapElement->renderer());
}
AccessibilityRole AccessibilityImageMapLink::roleValue() const
@@ -87,10 +87,10 @@ Element* AccessibilityImageMapLink::anchorElement() const
return m_areaElement.get();
}
-KURL AccessibilityImageMapLink::url() const
+URL AccessibilityImageMapLink::url() const
{
if (!m_areaElement.get())
- return KURL();
+ return URL();
return m_areaElement->href();
}
@@ -134,23 +134,30 @@ String AccessibilityImageMapLink::title() const
return String();
}
-RenderObject* AccessibilityImageMapLink::imageMapLinkRenderer() const
+RenderElement* AccessibilityImageMapLink::imageMapLinkRenderer() const
{
- if (!m_mapElement.get() || !m_areaElement.get())
- return 0;
+ if (!m_mapElement || !m_areaElement)
+ return nullptr;
- RenderObject* renderer = 0;
- if (m_parent && m_parent->isAccessibilityRenderObject())
- renderer = static_cast<AccessibilityRenderObject*>(m_parent)->renderer();
+ RenderElement* renderer = nullptr;
+ if (is<AccessibilityRenderObject>(m_parent))
+ renderer = downcast<RenderElement>(downcast<AccessibilityRenderObject>(*m_parent).renderer());
else
renderer = m_mapElement->renderer();
return renderer;
}
-
+
+void AccessibilityImageMapLink::detachFromParent()
+{
+ AccessibilityMockObject::detachFromParent();
+ m_areaElement = nullptr;
+ m_mapElement = nullptr;
+}
+
Path AccessibilityImageMapLink::elementPath() const
{
- RenderObject* renderer = imageMapLinkRenderer();
+ auto renderer = imageMapLinkRenderer();
if (!renderer)
return Path();
@@ -159,7 +166,7 @@ Path AccessibilityImageMapLink::elementPath() const
LayoutRect AccessibilityImageMapLink::elementRect() const
{
- RenderObject* renderer = imageMapLinkRenderer();
+ auto renderer = imageMapLinkRenderer();
if (!renderer)
return LayoutRect();
diff --git a/Source/WebCore/accessibility/AccessibilityImageMapLink.h b/Source/WebCore/accessibility/AccessibilityImageMapLink.h
index 40fb92678..69a7e20a5 100644
--- a/Source/WebCore/accessibility/AccessibilityImageMapLink.h
+++ b/Source/WebCore/accessibility/AccessibilityImageMapLink.h
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -35,12 +35,9 @@
namespace WebCore {
-class AccessibilityImageMapLink : public AccessibilityMockObject {
-
-private:
- AccessibilityImageMapLink();
+class AccessibilityImageMapLink final : public AccessibilityMockObject {
public:
- static PassRefPtr<AccessibilityImageMapLink> create();
+ static Ref<AccessibilityImageMapLink> create();
virtual ~AccessibilityImageMapLink();
void setHTMLAreaElement(HTMLAreaElement* element) { m_areaElement = element; }
@@ -49,36 +46,41 @@ public:
void setHTMLMapElement(HTMLMapElement* element) { m_mapElement = element; }
HTMLMapElement* mapElement() const { return m_mapElement.get(); }
- virtual Node* node() const { return m_areaElement.get(); }
+ virtual Node* node() const override { return m_areaElement.get(); }
- virtual AccessibilityRole roleValue() const;
- virtual bool isEnabled() const { return true; }
+ virtual AccessibilityRole roleValue() const override;
+ virtual bool isEnabled() const override { return true; }
- virtual Element* anchorElement() const;
- virtual Element* actionElement() const;
- virtual KURL url() const;
- virtual bool isLink() const { return true; }
- virtual bool isLinked() const { return true; }
- virtual String title() const;
- virtual String accessibilityDescription() const;
- virtual AccessibilityObject* parentObject() const;
+ virtual Element* anchorElement() const override;
+ virtual Element* actionElement() const override;
+ virtual URL url() const override;
+ virtual bool isLink() const override { return true; }
+ virtual bool isLinked() const override { return true; }
+ virtual String title() const override;
+ virtual String accessibilityDescription() const override;
+ virtual AccessibilityObject* parentObject() const override;
- virtual String stringValueForMSAA() const;
- virtual String nameForMSAA() const;
+ virtual String stringValueForMSAA() const override;
+ virtual String nameForMSAA() const override;
- virtual LayoutRect elementRect() const;
+ virtual LayoutRect elementRect() const override;
+
+private:
+ AccessibilityImageMapLink();
+
+ virtual void detachFromParent() override;
+ virtual Path elementPath() const override;
+ RenderElement* imageMapLinkRenderer() const;
+ virtual void accessibilityText(Vector<AccessibilityText>&) override;
+ virtual bool isImageMapLink() const override { return true; }
+ virtual bool supportsPath() const override { return true; }
-private:
RefPtr<HTMLAreaElement> m_areaElement;
RefPtr<HTMLMapElement> m_mapElement;
-
- virtual Path elementPath() const;
- RenderObject* imageMapLinkRenderer() const;
- virtual void accessibilityText(Vector<AccessibilityText>&);
- virtual bool isImageMapLink() const { return true; }
- virtual bool supportsPath() const { return true; }
};
} // namespace WebCore
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilityImageMapLink, isImageMapLink())
+
#endif // AccessibilityImageMapLink_h
diff --git a/Source/WebCore/accessibility/AccessibilityList.cpp b/Source/WebCore/accessibility/AccessibilityList.cpp
index 8039cf034..c78d36486 100644
--- a/Source/WebCore/accessibility/AccessibilityList.cpp
+++ b/Source/WebCore/accessibility/AccessibilityList.cpp
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -30,10 +30,12 @@
#include "AccessibilityList.h"
#include "AXObjectCache.h"
+#include "HTMLElement.h"
#include "HTMLNames.h"
+#include "PseudoElement.h"
+#include "RenderListItem.h"
#include "RenderObject.h"
-
-using namespace std;
+#include "RenderStyle.h"
namespace WebCore {
@@ -48,9 +50,9 @@ AccessibilityList::~AccessibilityList()
{
}
-PassRefPtr<AccessibilityList> AccessibilityList::create(RenderObject* renderer)
+Ref<AccessibilityList> AccessibilityList::create(RenderObject* renderer)
{
- return adoptRef(new AccessibilityList(renderer));
+ return adoptRef(*new AccessibilityList(renderer));
}
bool AccessibilityList::computeAccessibilityIsIgnored() const
@@ -93,8 +95,108 @@ bool AccessibilityList::isDescriptionList() const
return false;
Node* node = m_renderer->node();
- return node && node->hasTagName(dlTag);
+ return node && node->hasTagName(dlTag);
+}
+
+bool AccessibilityList::childHasPseudoVisibleListItemMarkers(RenderObject* listItem)
+{
+ // Check if the list item has a pseudo-element that should be accessible (e.g. an image or text)
+ Element* listItemElement = downcast<Element>(listItem->node());
+ if (!listItemElement || !listItemElement->beforePseudoElement())
+ return false;
+
+ AccessibilityObject* axObj = axObjectCache()->getOrCreate(listItemElement->beforePseudoElement()->renderer());
+ if (!axObj)
+ return false;
+
+ if (!axObj->accessibilityIsIgnored())
+ return true;
+
+ for (const auto& child : axObj->children()) {
+ if (!child->accessibilityIsIgnored())
+ return true;
+ }
+
+ // Platforms which expose rendered text content through the parent element will treat
+ // those renderers as "ignored" objects.
+#if PLATFORM(GTK) || PLATFORM(EFL)
+ String text = axObj->textUnderElement();
+ return !text.isEmpty() && !text.containsOnlyWhitespace();
+#else
+ return false;
+#endif
}
+AccessibilityRole AccessibilityList::determineAccessibilityRole()
+{
+ m_ariaRole = determineAriaRoleAttribute();
+
+ // Directory is mapped to list for now, but does not adhere to the same heuristics.
+ if (ariaRoleAttribute() == DirectoryRole)
+ return ListRole;
+
+ // Heuristic to determine if this list is being used for layout or for content.
+ // 1. If it's a named list, like ol or aria=list, then it's a list.
+ // 1a. Unless the list has no children, then it's not a list.
+ // 2. If it displays visible list markers, it's a list.
+ // 3. If it does not display list markers and has only one child, it's not a list.
+ // 4. If it does not have any listitem children, it's not a list.
+ // 5. Otherwise it's a list (for now).
+
+ AccessibilityRole role = ListRole;
+
+ // Temporarily set role so that we can query children (otherwise canHaveChildren returns false).
+ m_role = role;
+
+ unsigned listItemCount = 0;
+ bool hasVisibleMarkers = false;
+
+ const auto& children = this->children();
+ // DescriptionLists are always semantically a description list, so do not apply heuristics.
+ if (isDescriptionList() && children.size())
+ return DescriptionListRole;
+
+ for (const auto& child : children) {
+ if (child->ariaRoleAttribute() == ListItemRole)
+ listItemCount++;
+ else if (child->roleValue() == ListItemRole) {
+ RenderObject* listItem = child->renderer();
+ if (!listItem)
+ continue;
+
+ // Rendered list items always count.
+ if (listItem->isListItem()) {
+ if (!hasVisibleMarkers && (listItem->style().listStyleType() != NoneListStyle || listItem->style().listStyleImage() || childHasPseudoVisibleListItemMarkers(listItem)))
+ hasVisibleMarkers = true;
+ listItemCount++;
+ } else if (listItem->node() && listItem->node()->hasTagName(liTag)) {
+ // Inline elements that are in a list with an explicit role should also count.
+ if (m_ariaRole == ListRole)
+ listItemCount++;
+
+ if (childHasPseudoVisibleListItemMarkers(listItem)) {
+ hasVisibleMarkers = true;
+ listItemCount++;
+ }
+ }
+ }
+ }
+
+ // Non <ul> lists and ARIA lists only need to have one child.
+ // <ul>, <ol> lists need to have visible markers.
+ if (ariaRoleAttribute() != UnknownRole) {
+ if (!listItemCount)
+ role = GroupRole;
+ } else if (!hasVisibleMarkers)
+ role = GroupRole;
+
+ return role;
+}
+
+AccessibilityRole AccessibilityList::roleValue() const
+{
+ ASSERT(m_role != UnknownRole);
+ return m_role;
+}
} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AccessibilityList.h b/Source/WebCore/accessibility/AccessibilityList.h
index c5916ae6e..10d4276f7 100644
--- a/Source/WebCore/accessibility/AccessibilityList.h
+++ b/Source/WebCore/accessibility/AccessibilityList.h
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -33,30 +33,27 @@
namespace WebCore {
-class AccessibilityList : public AccessibilityRenderObject {
-
-private:
- explicit AccessibilityList(RenderObject*);
+class AccessibilityList final : public AccessibilityRenderObject {
public:
- static PassRefPtr<AccessibilityList> create(RenderObject*);
+ static Ref<AccessibilityList> create(RenderObject*);
virtual ~AccessibilityList();
- virtual bool isList() const { return true; }
bool isUnorderedList() const;
bool isOrderedList() const;
bool isDescriptionList() const;
- virtual AccessibilityRole roleValue() const { return ListRole; }
+ virtual AccessibilityRole roleValue() const override;
+
private:
- virtual bool computeAccessibilityIsIgnored() const;
+ explicit AccessibilityList(RenderObject*);
+ virtual bool isList() const override { return true; }
+ virtual bool computeAccessibilityIsIgnored() const override;
+ virtual AccessibilityRole determineAccessibilityRole() override;
+ bool childHasPseudoVisibleListItemMarkers(RenderObject*);
};
-
-inline AccessibilityList* toAccessibilityList(AccessibilityObject* object)
-{
- ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isList());
- return static_cast<AccessibilityList*>(object);
-}
-
+
} // namespace WebCore
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilityList, isList())
+
#endif // AccessibilityList_h
diff --git a/Source/WebCore/accessibility/AccessibilityListBox.cpp b/Source/WebCore/accessibility/AccessibilityListBox.cpp
index 9c4970edb..7fc68a9b9 100644
--- a/Source/WebCore/accessibility/AccessibilityListBox.cpp
+++ b/Source/WebCore/accessibility/AccessibilityListBox.cpp
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -37,8 +37,6 @@
#include "RenderListBox.h"
#include "RenderObject.h"
-using namespace std;
-
namespace WebCore {
using namespace HTMLNames;
@@ -51,10 +49,10 @@ AccessibilityListBox::AccessibilityListBox(RenderObject* renderer)
AccessibilityListBox::~AccessibilityListBox()
{
}
-
-PassRefPtr<AccessibilityListBox> AccessibilityListBox::create(RenderObject* renderer)
+
+Ref<AccessibilityListBox> AccessibilityListBox::create(RenderObject* renderer)
{
- return adoptRef(new AccessibilityListBox(renderer));
+ return adoptRef(*new AccessibilityListBox(renderer));
}
bool AccessibilityListBox::canSetSelectedChildrenAttribute() const
@@ -63,7 +61,7 @@ bool AccessibilityListBox::canSetSelectedChildrenAttribute() const
if (!selectNode)
return false;
- return !toHTMLSelectElement(selectNode)->isDisabledFormControl();
+ return !downcast<HTMLSelectElement>(*selectNode).isDisabledFormControl();
}
void AccessibilityListBox::addChildren()
@@ -74,18 +72,16 @@ void AccessibilityListBox::addChildren()
m_haveChildren = true;
- const Vector<HTMLElement*>& listItems = toHTMLSelectElement(selectNode)->listItems();
- unsigned length = listItems.size();
- for (unsigned i = 0; i < length; i++) {
+ for (const auto& listItem : downcast<HTMLSelectElement>(*selectNode).listItems()) {
// The cast to HTMLElement below is safe because the only other possible listItem type
// would be a WMLElement, but WML builds don't use accessibility features at all.
- AccessibilityObject* listOption = listBoxOptionAccessibilityObject(listItems[i]);
+ AccessibilityObject* listOption = listBoxOptionAccessibilityObject(listItem);
if (listOption && !listOption->accessibilityIsIgnored())
m_children.append(listOption);
}
}
-void AccessibilityListBox::setSelectedChildren(AccessibilityChildrenVector& children)
+void AccessibilityListBox::setSelectedChildren(const AccessibilityChildrenVector& children)
{
if (!canSetSelectedChildrenAttribute())
return;
@@ -95,20 +91,17 @@ void AccessibilityListBox::setSelectedChildren(AccessibilityChildrenVector& chil
return;
// disable any selected options
- unsigned length = m_children.size();
- for (unsigned i = 0; i < length; i++) {
- AccessibilityListBoxOption* listBoxOption = static_cast<AccessibilityListBoxOption*>(m_children[i].get());
- if (listBoxOption->isSelected())
- listBoxOption->setSelected(false);
+ for (const auto& child : m_children) {
+ auto& listBoxOption = downcast<AccessibilityListBoxOption>(*child);
+ if (listBoxOption.isSelected())
+ listBoxOption.setSelected(false);
}
- length = children.size();
- for (unsigned i = 0; i < length; i++) {
- AccessibilityObject* obj = children[i].get();
+ for (const auto& obj : children) {
if (obj->roleValue() != ListBoxOptionRole)
continue;
- static_cast<AccessibilityListBoxOption*>(obj)->setSelected(true);
+ downcast<AccessibilityListBoxOption>(*obj).setSelected(true);
}
}
@@ -119,10 +112,9 @@ void AccessibilityListBox::selectedChildren(AccessibilityChildrenVector& result)
if (!hasChildren())
addChildren();
- unsigned length = m_children.size();
- for (unsigned i = 0; i < length; i++) {
- if (static_cast<AccessibilityListBoxOption*>(m_children[i].get())->isSelected())
- result.append(m_children[i]);
+ for (const auto& child : m_children) {
+ if (downcast<AccessibilityListBoxOption>(*child).isSelected())
+ result.append(child.get());
}
}
@@ -135,7 +127,7 @@ void AccessibilityListBox::visibleChildren(AccessibilityChildrenVector& result)
unsigned length = m_children.size();
for (unsigned i = 0; i < length; i++) {
- if (toRenderListBox(m_renderer)->listIndexIsVisible(i))
+ if (downcast<RenderListBox>(*m_renderer).listIndexIsVisible(i))
result.append(m_children[i]);
}
}
@@ -144,12 +136,12 @@ AccessibilityObject* AccessibilityListBox::listBoxOptionAccessibilityObject(HTML
{
// skip hr elements
if (!element || element->hasTagName(hrTag))
- return 0;
+ return nullptr;
- AccessibilityObject* listBoxObject = m_renderer->document()->axObjectCache()->getOrCreate(ListBoxOptionRole);
- static_cast<AccessibilityListBoxOption*>(listBoxObject)->setHTMLElement(element);
+ AccessibilityObject& listBoxObject = *m_renderer->document().axObjectCache()->getOrCreate(ListBoxOptionRole);
+ downcast<AccessibilityListBoxOption>(listBoxObject).setHTMLElement(element);
- return listBoxObject;
+ return &listBoxObject;
}
AccessibilityObject* AccessibilityListBox::elementAccessibilityHitTest(const IntPoint& point) const
@@ -157,18 +149,18 @@ AccessibilityObject* AccessibilityListBox::elementAccessibilityHitTest(const Int
// the internal HTMLSelectElement methods for returning a listbox option at a point
// ignore optgroup elements.
if (!m_renderer)
- return 0;
+ return nullptr;
Node* node = m_renderer->node();
if (!node)
- return 0;
+ return nullptr;
LayoutRect parentRect = boundingBoxRect();
- AccessibilityObject* listBoxOption = 0;
+ AccessibilityObject* listBoxOption = nullptr;
unsigned length = m_children.size();
- for (unsigned i = 0; i < length; i++) {
- LayoutRect rect = toRenderListBox(m_renderer)->itemBoundingBoxRect(parentRect.location(), i);
+ for (unsigned i = 0; i < length; ++i) {
+ LayoutRect rect = downcast<RenderListBox>(*m_renderer).itemBoundingBoxRect(parentRect.location(), i);
// The cast to HTMLElement below is safe because the only other possible listItem type
// would be a WMLElement, but WML builds don't use accessibility features at all.
if (rect.contains(point)) {
diff --git a/Source/WebCore/accessibility/AccessibilityListBox.h b/Source/WebCore/accessibility/AccessibilityListBox.h
index dda37e8d5..ad2df1da4 100644
--- a/Source/WebCore/accessibility/AccessibilityListBox.h
+++ b/Source/WebCore/accessibility/AccessibilityListBox.h
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -33,30 +33,30 @@
namespace WebCore {
-class AccessibilityListBox : public AccessibilityRenderObject {
-
-private:
- explicit AccessibilityListBox(RenderObject*);
+class AccessibilityListBox final : public AccessibilityRenderObject {
public:
- static PassRefPtr<AccessibilityListBox> create(RenderObject*);
+ static Ref<AccessibilityListBox> create(RenderObject*);
virtual ~AccessibilityListBox();
- virtual bool isListBox() const { return true; }
-
- virtual bool canSetSelectedChildrenAttribute() const;
- void setSelectedChildren(AccessibilityChildrenVector&);
- virtual AccessibilityRole roleValue() const { return ListBoxRole; }
+ virtual bool canSetSelectedChildrenAttribute() const override;
+ void setSelectedChildren(const AccessibilityChildrenVector&);
+ virtual AccessibilityRole roleValue() const override { return ListBoxRole; }
- virtual void selectedChildren(AccessibilityChildrenVector&);
- virtual void visibleChildren(AccessibilityChildrenVector&);
+ virtual void selectedChildren(AccessibilityChildrenVector&) override;
+ virtual void visibleChildren(AccessibilityChildrenVector&) override;
- virtual void addChildren();
+ virtual void addChildren() override;
-private:
+private:
+ explicit AccessibilityListBox(RenderObject*);
+
+ virtual bool isListBox() const override { return true; }
AccessibilityObject* listBoxOptionAccessibilityObject(HTMLElement*) const;
- virtual AccessibilityObject* elementAccessibilityHitTest(const IntPoint&) const;
+ virtual AccessibilityObject* elementAccessibilityHitTest(const IntPoint&) const override;
};
} // namespace WebCore
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilityListBox, isListBox())
+
#endif // AccessibilityListBox_h
diff --git a/Source/WebCore/accessibility/AccessibilityListBoxOption.cpp b/Source/WebCore/accessibility/AccessibilityListBoxOption.cpp
index 8895f1015..9cb5a74ab 100644
--- a/Source/WebCore/accessibility/AccessibilityListBoxOption.cpp
+++ b/Source/WebCore/accessibility/AccessibilityListBoxOption.cpp
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -41,14 +41,12 @@
#include "RenderListBox.h"
#include "RenderObject.h"
-using namespace std;
-
namespace WebCore {
using namespace HTMLNames;
AccessibilityListBoxOption::AccessibilityListBoxOption()
- : m_optionElement(0)
+ : m_optionElement(nullptr)
{
}
@@ -56,23 +54,20 @@ AccessibilityListBoxOption::~AccessibilityListBoxOption()
{
}
-PassRefPtr<AccessibilityListBoxOption> AccessibilityListBoxOption::create()
+Ref<AccessibilityListBoxOption> AccessibilityListBoxOption::create()
{
- return adoptRef(new AccessibilityListBoxOption());
+ return adoptRef(*new AccessibilityListBoxOption());
}
bool AccessibilityListBoxOption::isEnabled() const
{
- if (!m_optionElement)
- return false;
-
- if (isHTMLOptGroupElement(m_optionElement))
+ if (is<HTMLOptGroupElement>(m_optionElement))
return false;
- if (equalIgnoringCase(getAttribute(aria_disabledAttr), "true"))
+ if (equalLettersIgnoringASCIICase(getAttribute(aria_disabledAttr), "true"))
return false;
- if (m_optionElement->hasAttribute(disabledAttr))
+ if (m_optionElement->fastHasAttribute(disabledAttr))
return false;
return true;
@@ -80,13 +75,10 @@ bool AccessibilityListBoxOption::isEnabled() const
bool AccessibilityListBoxOption::isSelected() const
{
- if (!m_optionElement)
+ if (!is<HTMLOptionElement>(m_optionElement))
return false;
- if (!isHTMLOptionElement(m_optionElement))
- return false;
-
- return toHTMLOptionElement(m_optionElement)->selected();
+ return downcast<HTMLOptionElement>(*m_optionElement).selected();
}
bool AccessibilityListBoxOption::isSelectedOptionActive() const
@@ -108,14 +100,14 @@ LayoutRect AccessibilityListBoxOption::elementRect() const
if (!listBoxParentNode)
return rect;
- RenderObject* listBoxRenderer = listBoxParentNode->renderer();
+ RenderElement* listBoxRenderer = listBoxParentNode->renderer();
if (!listBoxRenderer)
return rect;
- LayoutRect parentRect = listBoxRenderer->document()->axObjectCache()->getOrCreate(listBoxRenderer)->boundingBoxRect();
+ LayoutRect parentRect = listBoxRenderer->document().axObjectCache()->getOrCreate(listBoxRenderer)->boundingBoxRect();
int index = listBoxOptionIndex();
if (index != -1)
- rect = toRenderListBox(listBoxRenderer)->itemBoundingBoxRect(parentRect.location(), index);
+ rect = downcast<RenderListBox>(*listBoxRenderer).itemBoundingBoxRect(parentRect.location(), index);
return rect;
}
@@ -133,10 +125,7 @@ bool AccessibilityListBoxOption::computeAccessibilityIsIgnored() const
bool AccessibilityListBoxOption::canSetSelectedAttribute() const
{
- if (!m_optionElement)
- return false;
-
- if (!isHTMLOptionElement(m_optionElement))
+ if (!is<HTMLOptionElement>(m_optionElement))
return false;
if (m_optionElement->isDisabledFormControl())
@@ -158,11 +147,11 @@ String AccessibilityListBoxOption::stringValue() const
if (!ariaLabel.isNull())
return ariaLabel;
- if (isHTMLOptionElement(m_optionElement))
- return toHTMLOptionElement(m_optionElement)->text();
+ if (is<HTMLOptionElement>(*m_optionElement))
+ return downcast<HTMLOptionElement>(*m_optionElement).label();
- if (isHTMLOptGroupElement(m_optionElement))
- return toHTMLOptGroupElement(m_optionElement)->groupLabelText();
+ if (is<HTMLOptGroupElement>(*m_optionElement))
+ return downcast<HTMLOptGroupElement>(*m_optionElement).groupLabelText();
return String();
}
@@ -176,9 +165,9 @@ AccessibilityObject* AccessibilityListBoxOption::parentObject() const
{
HTMLSelectElement* parentNode = listBoxOptionParentNode();
if (!parentNode)
- return 0;
+ return nullptr;
- return m_optionElement->document()->axObjectCache()->getOrCreate(parentNode);
+ return m_optionElement->document().axObjectCache()->getOrCreate(parentNode);
}
void AccessibilityListBoxOption::setSelected(bool selected)
@@ -202,15 +191,15 @@ void AccessibilityListBoxOption::setSelected(bool selected)
HTMLSelectElement* AccessibilityListBoxOption::listBoxOptionParentNode() const
{
if (!m_optionElement)
- return 0;
+ return nullptr;
- if (isHTMLOptionElement(m_optionElement))
- return toHTMLOptionElement(m_optionElement)->ownerSelectElement();
+ if (is<HTMLOptionElement>(*m_optionElement))
+ return downcast<HTMLOptionElement>(*m_optionElement).ownerSelectElement();
- if (isHTMLOptGroupElement(m_optionElement))
- return toHTMLOptGroupElement(m_optionElement)->ownerSelectElement();
+ if (is<HTMLOptGroupElement>(*m_optionElement))
+ return downcast<HTMLOptGroupElement>(*m_optionElement).ownerSelectElement();
- return 0;
+ return nullptr;
}
int AccessibilityListBoxOption::listBoxOptionIndex() const
@@ -222,7 +211,7 @@ int AccessibilityListBoxOption::listBoxOptionIndex() const
if (!selectElement)
return -1;
- const Vector<HTMLElement*>& listItems = selectElement->listItems();
+ const auto& listItems = selectElement->listItems();
unsigned length = listItems.size();
for (unsigned i = 0; i < length; i++)
if (listItems[i] == m_optionElement)
diff --git a/Source/WebCore/accessibility/AccessibilityListBoxOption.h b/Source/WebCore/accessibility/AccessibilityListBoxOption.h
index 114445c31..31c433670 100644
--- a/Source/WebCore/accessibility/AccessibilityListBoxOption.h
+++ b/Source/WebCore/accessibility/AccessibilityListBoxOption.h
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -40,41 +40,42 @@ class Element;
class HTMLElement;
class HTMLSelectElement;
-class AccessibilityListBoxOption : public AccessibilityObject {
-
-private:
- AccessibilityListBoxOption();
+class AccessibilityListBoxOption final : public AccessibilityObject {
public:
- static PassRefPtr<AccessibilityListBoxOption> create();
+ static Ref<AccessibilityListBoxOption> create();
virtual ~AccessibilityListBoxOption();
void setHTMLElement(HTMLElement* element) { m_optionElement = element; }
- virtual AccessibilityRole roleValue() const { return ListBoxOptionRole; }
- virtual bool isSelected() const;
- virtual bool isEnabled() const;
- virtual bool isSelectedOptionActive() const;
- virtual String stringValue() const;
- virtual Element* actionElement() const;
- virtual Node* node() const { return m_optionElement; }
- virtual void setSelected(bool);
- virtual bool canSetSelectedAttribute() const;
+ virtual AccessibilityRole roleValue() const override { return ListBoxOptionRole; }
+ virtual bool isSelected() const override;
+ virtual bool isEnabled() const override;
+ virtual bool isSelectedOptionActive() const override;
+ virtual String stringValue() const override;
+ virtual Element* actionElement() const override;
+ virtual Node* node() const override { return m_optionElement; }
+ virtual void setSelected(bool) override;
+ virtual bool canSetSelectedAttribute() const override;
+
+ virtual LayoutRect elementRect() const override;
+ virtual AccessibilityObject* parentObject() const override;
- virtual LayoutRect elementRect() const;
- virtual AccessibilityObject* parentObject() const;
- bool isListBoxOption() const { return true; }
-
private:
- HTMLElement* m_optionElement;
-
- virtual bool canHaveChildren() const { return false; }
+ AccessibilityListBoxOption();
+
+ virtual bool isListBoxOption() const override { return true; }
+ virtual bool canHaveChildren() const override { return false; }
HTMLSelectElement* listBoxOptionParentNode() const;
int listBoxOptionIndex() const;
IntRect listBoxOptionRect() const;
AccessibilityObject* listBoxOptionAccessibilityObject(HTMLElement*) const;
- virtual bool computeAccessibilityIsIgnored() const;
+ virtual bool computeAccessibilityIsIgnored() const override;
+
+ HTMLElement* m_optionElement;
};
} // namespace WebCore
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilityListBoxOption, isListBoxOption())
+
#endif // AccessibilityListBoxOption_h
diff --git a/Source/WebCore/accessibility/AccessibilityMediaControls.cpp b/Source/WebCore/accessibility/AccessibilityMediaControls.cpp
index 3a464ae0a..a59d1682b 100644
--- a/Source/WebCore/accessibility/AccessibilityMediaControls.cpp
+++ b/Source/WebCore/accessibility/AccessibilityMediaControls.cpp
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -41,6 +41,7 @@
#include "MediaControlElements.h"
#include "RenderObject.h"
#include "RenderSlider.h"
+#include <wtf/NeverDestroyed.h>
namespace WebCore {
@@ -52,7 +53,7 @@ AccessibilityMediaControl::AccessibilityMediaControl(RenderObject* renderer)
{
}
-PassRefPtr<AccessibilityObject> AccessibilityMediaControl::create(RenderObject* renderer)
+Ref<AccessibilityObject> AccessibilityMediaControl::create(RenderObject* renderer)
{
ASSERT(renderer->node());
@@ -68,7 +69,7 @@ PassRefPtr<AccessibilityObject> AccessibilityMediaControl::create(RenderObject*
return AccessibilityMediaControlsContainer::create(renderer);
default:
- return adoptRef(new AccessibilityMediaControl(renderer));
+ return adoptRef(*new AccessibilityMediaControl(renderer));
}
}
@@ -80,23 +81,23 @@ MediaControlElementType AccessibilityMediaControl::controlType() const
return mediaControlElementType(renderer()->node());
}
-String AccessibilityMediaControl::controlTypeName() const
+const String& AccessibilityMediaControl::controlTypeName() const
{
- DEFINE_STATIC_LOCAL(const String, mediaEnterFullscreenButtonName, (ASCIILiteral("EnterFullscreenButton")));
- DEFINE_STATIC_LOCAL(const String, mediaExitFullscreenButtonName, (ASCIILiteral("ExitFullscreenButton")));
- DEFINE_STATIC_LOCAL(const String, mediaMuteButtonName, (ASCIILiteral("MuteButton")));
- DEFINE_STATIC_LOCAL(const String, mediaPlayButtonName, (ASCIILiteral("PlayButton")));
- DEFINE_STATIC_LOCAL(const String, mediaSeekBackButtonName, (ASCIILiteral("SeekBackButton")));
- DEFINE_STATIC_LOCAL(const String, mediaSeekForwardButtonName, (ASCIILiteral("SeekForwardButton")));
- DEFINE_STATIC_LOCAL(const String, mediaRewindButtonName, (ASCIILiteral("RewindButton")));
- DEFINE_STATIC_LOCAL(const String, mediaReturnToRealtimeButtonName, (ASCIILiteral("ReturnToRealtimeButton")));
- DEFINE_STATIC_LOCAL(const String, mediaUnMuteButtonName, (ASCIILiteral("UnMuteButton")));
- DEFINE_STATIC_LOCAL(const String, mediaPauseButtonName, (ASCIILiteral("PauseButton")));
- DEFINE_STATIC_LOCAL(const String, mediaStatusDisplayName, (ASCIILiteral("StatusDisplay")));
- DEFINE_STATIC_LOCAL(const String, mediaCurrentTimeDisplay, (ASCIILiteral("CurrentTimeDisplay")));
- DEFINE_STATIC_LOCAL(const String, mediaTimeRemainingDisplay, (ASCIILiteral("TimeRemainingDisplay")));
- DEFINE_STATIC_LOCAL(const String, mediaShowClosedCaptionsButtonName, (ASCIILiteral("ShowClosedCaptionsButton")));
- DEFINE_STATIC_LOCAL(const String, mediaHideClosedCaptionsButtonName, (ASCIILiteral("HideClosedCaptionsButton")));
+ static NeverDestroyed<const String> mediaEnterFullscreenButtonName(ASCIILiteral("EnterFullscreenButton"));
+ static NeverDestroyed<const String> mediaExitFullscreenButtonName(ASCIILiteral("ExitFullscreenButton"));
+ static NeverDestroyed<const String> mediaMuteButtonName(ASCIILiteral("MuteButton"));
+ static NeverDestroyed<const String> mediaPlayButtonName(ASCIILiteral("PlayButton"));
+ static NeverDestroyed<const String> mediaSeekBackButtonName(ASCIILiteral("SeekBackButton"));
+ static NeverDestroyed<const String> mediaSeekForwardButtonName(ASCIILiteral("SeekForwardButton"));
+ static NeverDestroyed<const String> mediaRewindButtonName(ASCIILiteral("RewindButton"));
+ static NeverDestroyed<const String> mediaReturnToRealtimeButtonName(ASCIILiteral("ReturnToRealtimeButton"));
+ static NeverDestroyed<const String> mediaUnMuteButtonName(ASCIILiteral("UnMuteButton"));
+ static NeverDestroyed<const String> mediaPauseButtonName(ASCIILiteral("PauseButton"));
+ static NeverDestroyed<const String> mediaStatusDisplayName(ASCIILiteral("StatusDisplay"));
+ static NeverDestroyed<const String> mediaCurrentTimeDisplay(ASCIILiteral("CurrentTimeDisplay"));
+ static NeverDestroyed<const String> mediaTimeRemainingDisplay(ASCIILiteral("TimeRemainingDisplay"));
+ static NeverDestroyed<const String> mediaShowClosedCaptionsButtonName(ASCIILiteral("ShowClosedCaptionsButton"));
+ static NeverDestroyed<const String> mediaHideClosedCaptionsButtonName(ASCIILiteral("HideClosedCaptionsButton"));
switch (controlType()) {
case MediaEnterFullscreenButton:
@@ -134,7 +135,7 @@ String AccessibilityMediaControl::controlTypeName() const
break;
}
- return String();
+ return nullAtom;
}
void AccessibilityMediaControl::accessibilityText(Vector<AccessibilityText>& textOrder)
@@ -155,7 +156,7 @@ void AccessibilityMediaControl::accessibilityText(Vector<AccessibilityText>& tex
String AccessibilityMediaControl::title() const
{
- DEFINE_STATIC_LOCAL(const String, controlsPanel, (ASCIILiteral("ControlsPanel")));
+ static NeverDestroyed<const String> controlsPanel(ASCIILiteral("ControlsPanel"));
if (controlType() == MediaControlsPanel)
return localizedMediaControlElementString(controlsPanel);
@@ -175,7 +176,7 @@ String AccessibilityMediaControl::helpText() const
bool AccessibilityMediaControl::computeAccessibilityIsIgnored() const
{
- if (!m_renderer || !m_renderer->style() || m_renderer->style()->visibility() != VISIBLE || controlType() == MediaTimelineContainer)
+ if (!m_renderer || m_renderer->style().visibility() != VISIBLE || controlType() == MediaTimelineContainer)
return true;
return accessibilityIsIgnoredByDefault();
@@ -221,9 +222,9 @@ AccessibilityMediaControlsContainer::AccessibilityMediaControlsContainer(RenderO
{
}
-PassRefPtr<AccessibilityObject> AccessibilityMediaControlsContainer::create(RenderObject* renderer)
+Ref<AccessibilityObject> AccessibilityMediaControlsContainer::create(RenderObject* renderer)
{
- return adoptRef(new AccessibilityMediaControlsContainer(renderer));
+ return adoptRef(*new AccessibilityMediaControlsContainer(renderer));
}
String AccessibilityMediaControlsContainer::accessibilityDescription() const
@@ -238,18 +239,14 @@ String AccessibilityMediaControlsContainer::helpText() const
bool AccessibilityMediaControlsContainer::controllingVideoElement() const
{
- if (!m_renderer->node())
- return true;
-
- MediaControlTimeDisplayElement* element = static_cast<MediaControlTimeDisplayElement*>(m_renderer->node());
-
- return toParentMediaElement(element)->isVideo();
+ auto element = parentMediaElement(*m_renderer);
+ return !element || element->isVideo();
}
-const String AccessibilityMediaControlsContainer::elementTypeName() const
+const String& AccessibilityMediaControlsContainer::elementTypeName() const
{
- DEFINE_STATIC_LOCAL(const String, videoElement, (ASCIILiteral("VideoElement")));
- DEFINE_STATIC_LOCAL(const String, audioElement, (ASCIILiteral("AudioElement")));
+ static NeverDestroyed<const String> videoElement(ASCIILiteral("VideoElement"));
+ static NeverDestroyed<const String> audioElement(ASCIILiteral("AudioElement"));
if (controllingVideoElement())
return videoElement;
@@ -269,24 +266,24 @@ AccessibilityMediaTimeline::AccessibilityMediaTimeline(RenderObject* renderer)
{
}
-PassRefPtr<AccessibilityObject> AccessibilityMediaTimeline::create(RenderObject* renderer)
+Ref<AccessibilityObject> AccessibilityMediaTimeline::create(RenderObject* renderer)
{
- return adoptRef(new AccessibilityMediaTimeline(renderer));
+ return adoptRef(*new AccessibilityMediaTimeline(renderer));
}
String AccessibilityMediaTimeline::valueDescription() const
{
Node* node = m_renderer->node();
- if (!isHTMLInputElement(node))
+ if (!is<HTMLInputElement>(*node))
return String();
- float time = toHTMLInputElement(node)->value().toFloat();
+ float time = downcast<HTMLInputElement>(*node).value().toFloat();
return localizedMediaTimeDescription(time);
}
String AccessibilityMediaTimeline::helpText() const
{
- DEFINE_STATIC_LOCAL(const String, slider, (ASCIILiteral("Slider")));
+ static NeverDestroyed<const String> slider(ASCIILiteral("Slider"));
return localizedMediaControlElementHelpText(slider);
}
@@ -299,17 +296,17 @@ AccessibilityMediaTimeDisplay::AccessibilityMediaTimeDisplay(RenderObject* rende
{
}
-PassRefPtr<AccessibilityObject> AccessibilityMediaTimeDisplay::create(RenderObject* renderer)
+Ref<AccessibilityObject> AccessibilityMediaTimeDisplay::create(RenderObject* renderer)
{
- return adoptRef(new AccessibilityMediaTimeDisplay(renderer));
+ return adoptRef(*new AccessibilityMediaTimeDisplay(renderer));
}
bool AccessibilityMediaTimeDisplay::computeAccessibilityIsIgnored() const
{
- if (!m_renderer || !m_renderer->style() || m_renderer->style()->visibility() != VISIBLE)
+ if (!m_renderer || m_renderer->style().visibility() != VISIBLE)
return true;
- if (!m_renderer->style()->width().value())
+ if (!m_renderer->style().width().value())
return true;
return accessibilityIsIgnoredByDefault();
@@ -317,8 +314,8 @@ bool AccessibilityMediaTimeDisplay::computeAccessibilityIsIgnored() const
String AccessibilityMediaTimeDisplay::accessibilityDescription() const
{
- DEFINE_STATIC_LOCAL(const String, currentTimeDisplay, (ASCIILiteral("CurrentTimeDisplay")));
- DEFINE_STATIC_LOCAL(const String, timeRemainingDisplay, (ASCIILiteral("TimeRemainingDisplay")));
+ static NeverDestroyed<const String> currentTimeDisplay(ASCIILiteral("CurrentTimeDisplay"));
+ static NeverDestroyed<const String> timeRemainingDisplay(ASCIILiteral("TimeRemainingDisplay"));
if (controlType() == MediaCurrentTimeDisplay)
return localizedMediaControlElementString(currentTimeDisplay);
diff --git a/Source/WebCore/accessibility/AccessibilityMediaControls.h b/Source/WebCore/accessibility/AccessibilityMediaControls.h
index 337ec5c72..a0ce71792 100644
--- a/Source/WebCore/accessibility/AccessibilityMediaControls.h
+++ b/Source/WebCore/accessibility/AccessibilityMediaControls.h
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -40,75 +40,77 @@ namespace WebCore {
class AccessibilityMediaControl : public AccessibilityRenderObject {
public:
- static PassRefPtr<AccessibilityObject> create(RenderObject*);
+ static Ref<AccessibilityObject> create(RenderObject*);
virtual ~AccessibilityMediaControl() { }
- virtual AccessibilityRole roleValue() const;
+ virtual AccessibilityRole roleValue() const override;
- virtual String title() const;
- virtual String accessibilityDescription() const;
- virtual String helpText() const;
+ virtual String title() const override;
+ virtual String accessibilityDescription() const override;
+ virtual String helpText() const override;
protected:
explicit AccessibilityMediaControl(RenderObject*);
MediaControlElementType controlType() const;
- String controlTypeName() const;
- virtual void accessibilityText(Vector<AccessibilityText>&);
- virtual bool computeAccessibilityIsIgnored() const;
+ const String& controlTypeName() const;
+ virtual bool computeAccessibilityIsIgnored() const override;
+
+private:
+ virtual void accessibilityText(Vector<AccessibilityText>&) override;
};
-class AccessibilityMediaTimeline : public AccessibilitySlider {
+class AccessibilityMediaTimeline final : public AccessibilitySlider {
public:
- static PassRefPtr<AccessibilityObject> create(RenderObject*);
+ static Ref<AccessibilityObject> create(RenderObject*);
virtual ~AccessibilityMediaTimeline() { }
- virtual bool isMediaTimeline() const { return true; }
-
- virtual String helpText() const;
- virtual String valueDescription() const;
+ virtual String helpText() const override;
+ virtual String valueDescription() const override;
const AtomicString& getAttribute(const QualifiedName& attribute) const;
private:
explicit AccessibilityMediaTimeline(RenderObject*);
+
+ virtual bool isMediaTimeline() const override { return true; }
};
-class AccessibilityMediaControlsContainer : public AccessibilityMediaControl {
+class AccessibilityMediaControlsContainer final : public AccessibilityMediaControl {
public:
- static PassRefPtr<AccessibilityObject> create(RenderObject*);
+ static Ref<AccessibilityObject> create(RenderObject*);
virtual ~AccessibilityMediaControlsContainer() { }
- virtual AccessibilityRole roleValue() const { return ToolbarRole; }
+ virtual AccessibilityRole roleValue() const override { return ToolbarRole; }
- virtual String helpText() const;
- virtual String accessibilityDescription() const;
+ virtual String helpText() const override;
+ virtual String accessibilityDescription() const override;
private:
explicit AccessibilityMediaControlsContainer(RenderObject*);
bool controllingVideoElement() const;
- const String elementTypeName() const;
- virtual bool computeAccessibilityIsIgnored() const;
+ const String& elementTypeName() const;
+ virtual bool computeAccessibilityIsIgnored() const override;
};
-class AccessibilityMediaTimeDisplay : public AccessibilityMediaControl {
+class AccessibilityMediaTimeDisplay final : public AccessibilityMediaControl {
public:
- static PassRefPtr<AccessibilityObject> create(RenderObject*);
+ static Ref<AccessibilityObject> create(RenderObject*);
virtual ~AccessibilityMediaTimeDisplay() { }
- virtual AccessibilityRole roleValue() const { return ApplicationTimerRole; }
+ virtual AccessibilityRole roleValue() const override { return ApplicationTimerRole; }
- virtual String stringValue() const;
- virtual String accessibilityDescription() const;
+ virtual String stringValue() const override;
+ virtual String accessibilityDescription() const override;
private:
explicit AccessibilityMediaTimeDisplay(RenderObject*);
- virtual bool isMediaControlLabel() const { return true; }
- virtual bool computeAccessibilityIsIgnored() const;
+ virtual bool isMediaControlLabel() const override { return true; }
+ virtual bool computeAccessibilityIsIgnored() const override;
};
diff --git a/Source/WebCore/accessibility/AccessibilityMenuList.cpp b/Source/WebCore/accessibility/AccessibilityMenuList.cpp
index 994a33468..c0e5cf00e 100644
--- a/Source/WebCore/accessibility/AccessibilityMenuList.cpp
+++ b/Source/WebCore/accessibility/AccessibilityMenuList.cpp
@@ -37,37 +37,45 @@ AccessibilityMenuList::AccessibilityMenuList(RenderMenuList* renderer)
{
}
-PassRefPtr<AccessibilityMenuList> AccessibilityMenuList::create(RenderMenuList* renderer)
+Ref<AccessibilityMenuList> AccessibilityMenuList::create(RenderMenuList* renderer)
{
- return adoptRef(new AccessibilityMenuList(renderer));
+ return adoptRef(*new AccessibilityMenuList(renderer));
}
-bool AccessibilityMenuList::press() const
+bool AccessibilityMenuList::press()
{
+#if !PLATFORM(IOS)
RenderMenuList* menuList = static_cast<RenderMenuList*>(m_renderer);
if (menuList->popupIsVisible())
menuList->hidePopup();
else
menuList->showPopup();
return true;
+#else
+ return false;
+#endif
}
void AccessibilityMenuList::addChildren()
{
- m_haveChildren = true;
-
- AXObjectCache* cache = m_renderer->document()->axObjectCache();
-
+ if (!m_renderer)
+ return;
+
+ AXObjectCache* cache = axObjectCache();
+ if (!cache)
+ return;
+
AccessibilityObject* list = cache->getOrCreate(MenuListPopupRole);
if (!list)
return;
- static_cast<AccessibilityMockObject*>(list)->setParent(this);
+ downcast<AccessibilityMockObject>(*list).setParent(this);
if (list->accessibilityIsIgnored()) {
cache->remove(list->axObjectID());
return;
}
+ m_haveChildren = true;
m_children.append(list);
list->addChildren();
@@ -84,7 +92,11 @@ void AccessibilityMenuList::childrenChanged()
bool AccessibilityMenuList::isCollapsed() const
{
+#if !PLATFORM(IOS)
return !static_cast<RenderMenuList*>(m_renderer)->popupIsVisible();
+#else
+ return true;
+#endif
}
bool AccessibilityMenuList::canSetFocusAttribute() const
@@ -92,26 +104,32 @@ bool AccessibilityMenuList::canSetFocusAttribute() const
if (!node())
return false;
- return !toElement(node())->isDisabledFormControl();
+ return !downcast<Element>(*node()).isDisabledFormControl();
}
void AccessibilityMenuList::didUpdateActiveOption(int optionIndex)
{
- RefPtr<Document> document = m_renderer->document();
+ Ref<Document> document(m_renderer->document());
AXObjectCache* cache = document->axObjectCache();
- const AccessibilityChildrenVector& childObjects = children();
+ const auto& childObjects = children();
if (!childObjects.isEmpty()) {
ASSERT(childObjects.size() == 1);
- ASSERT(childObjects[0]->isMenuListPopup());
-
- if (childObjects[0]->isMenuListPopup()) {
- if (AccessibilityMenuListPopup* popup = static_cast<AccessibilityMenuListPopup*>(childObjects[0].get()))
- popup->didUpdateActiveOption(optionIndex);
- }
+ ASSERT(is<AccessibilityMenuListPopup>(*childObjects[0]));
+
+ // We might be calling this method in situations where the renderers for list items
+ // associated to the menu list have not been created (e.g. they might be rendered
+ // in the UI process, as it's the case in the GTK+ port, which uses GtkMenuItem).
+ // So, we need to make sure that the accessibility popup object has some children
+ // before asking it to update its active option, or it will read invalid memory.
+ // You can reproduce the issue in the GTK+ port by removing this check and running
+ // accessibility/insert-selected-option-into-select-causes-crash.html (will crash).
+ int popupChildrenSize = static_cast<int>(childObjects[0]->children().size());
+ if (is<AccessibilityMenuListPopup>(*childObjects[0]) && optionIndex >= 0 && optionIndex < popupChildrenSize)
+ downcast<AccessibilityMenuListPopup>(*childObjects[0]).didUpdateActiveOption(optionIndex);
}
- cache->postNotification(this, document.get(), AXObjectCache::AXMenuListValueChanged, true, PostSynchronously);
+ cache->postNotification(this, document.ptr(), AXObjectCache::AXMenuListValueChanged, TargetElement, PostSynchronously);
}
} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AccessibilityMenuList.h b/Source/WebCore/accessibility/AccessibilityMenuList.h
index 95f7972b3..c081a72f7 100644
--- a/Source/WebCore/accessibility/AccessibilityMenuList.h
+++ b/Source/WebCore/accessibility/AccessibilityMenuList.h
@@ -35,32 +35,28 @@ class AccessibilityMenuListPopup;
class HTMLOptionElement;
class RenderMenuList;
-class AccessibilityMenuList : public AccessibilityRenderObject {
+class AccessibilityMenuList final : public AccessibilityRenderObject {
public:
- static PassRefPtr<AccessibilityMenuList> create(RenderMenuList* renderer);
+ static Ref<AccessibilityMenuList> create(RenderMenuList* renderer);
- virtual bool isCollapsed() const;
- virtual bool press() const;
+ virtual bool isCollapsed() const override;
+ virtual bool press() override;
void didUpdateActiveOption(int optionIndex);
private:
explicit AccessibilityMenuList(RenderMenuList*);
- virtual bool isMenuList() const { return true; }
- virtual AccessibilityRole roleValue() const { return PopUpButtonRole; }
- virtual bool canSetFocusAttribute() const;
+ virtual bool isMenuList() const override { return true; }
+ virtual AccessibilityRole roleValue() const override { return PopUpButtonRole; }
+ virtual bool canSetFocusAttribute() const override;
- virtual void addChildren();
- virtual void childrenChanged();
+ virtual void addChildren() override;
+ virtual void childrenChanged() override;
};
-inline AccessibilityMenuList* toAccessibilityMenuList(AccessibilityObject* object)
-{
- ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isMenuList());
- return static_cast<AccessibilityMenuList*>(object);
-}
-
} // namespace WebCore
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilityMenuList, isMenuList())
+
#endif // AccessibilityMenuList_h
diff --git a/Source/WebCore/accessibility/AccessibilityMenuListOption.cpp b/Source/WebCore/accessibility/AccessibilityMenuListOption.cpp
index 88e2e6ee1..ddb26464a 100644
--- a/Source/WebCore/accessibility/AccessibilityMenuListOption.cpp
+++ b/Source/WebCore/accessibility/AccessibilityMenuListOption.cpp
@@ -41,7 +41,7 @@ AccessibilityMenuListOption::AccessibilityMenuListOption()
void AccessibilityMenuListOption::setElement(HTMLElement* element)
{
- ASSERT_ARG(element, isHTMLOptionElement(element));
+ ASSERT_ARG(element, is<HTMLOptionElement>(element));
m_element = element;
}
@@ -54,7 +54,7 @@ bool AccessibilityMenuListOption::isEnabled() const
{
// isDisabledFormControl() returns true if the parent <select> element is disabled,
// which we don't want.
- return !toHTMLOptionElement(m_element.get())->ownElementDisabled();
+ return !downcast<HTMLOptionElement>(*m_element).ownElementDisabled();
}
bool AccessibilityMenuListOption::isVisible() const
@@ -75,15 +75,15 @@ bool AccessibilityMenuListOption::isOffScreen() const
bool AccessibilityMenuListOption::isSelected() const
{
- return toHTMLOptionElement(m_element.get())->selected();
+ return downcast<HTMLOptionElement>(*m_element).selected();
}
-void AccessibilityMenuListOption::setSelected(bool b)
+void AccessibilityMenuListOption::setSelected(bool selected)
{
if (!canSetSelectedAttribute())
return;
- toHTMLOptionElement(m_element.get())->setSelected(b);
+ downcast<HTMLOptionElement>(*m_element).setSelected(selected);
}
String AccessibilityMenuListOption::nameForMSAA() const
@@ -104,9 +104,13 @@ bool AccessibilityMenuListOption::computeAccessibilityIsIgnored() const
LayoutRect AccessibilityMenuListOption::elementRect() const
{
AccessibilityObject* parent = parentObject();
+ if (!parent)
+ return boundingBoxRect();
ASSERT(parent->isMenuListPopup());
AccessibilityObject* grandparent = parent->parentObject();
+ if (!grandparent)
+ return boundingBoxRect();
ASSERT(grandparent->isMenuList());
return grandparent->elementRect();
@@ -114,7 +118,7 @@ LayoutRect AccessibilityMenuListOption::elementRect() const
String AccessibilityMenuListOption::stringValue() const
{
- return toHTMLOptionElement(m_element.get())->text();
+ return downcast<HTMLOptionElement>(*m_element).label();
}
} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AccessibilityMenuListOption.h b/Source/WebCore/accessibility/AccessibilityMenuListOption.h
index c340b7e4f..b68150355 100644
--- a/Source/WebCore/accessibility/AccessibilityMenuListOption.h
+++ b/Source/WebCore/accessibility/AccessibilityMenuListOption.h
@@ -33,35 +33,37 @@ namespace WebCore {
class AccessibilityMenuListPopup;
class HTMLElement;
-class AccessibilityMenuListOption : public AccessibilityMockObject {
+class AccessibilityMenuListOption final : public AccessibilityMockObject {
public:
- static PassRefPtr<AccessibilityMenuListOption> create() { return adoptRef(new AccessibilityMenuListOption); }
+ static Ref<AccessibilityMenuListOption> create() { return adoptRef(*new AccessibilityMenuListOption); }
void setElement(HTMLElement*);
private:
AccessibilityMenuListOption();
- virtual bool isMenuListOption() const { return true; }
+ virtual bool isMenuListOption() const override { return true; }
- virtual AccessibilityRole roleValue() const { return MenuListOptionRole; }
- virtual bool canHaveChildren() const { return false; }
+ virtual AccessibilityRole roleValue() const override { return MenuListOptionRole; }
+ virtual bool canHaveChildren() const override { return false; }
- virtual Element* actionElement() const;
- virtual bool isEnabled() const;
- virtual bool isVisible() const;
- virtual bool isOffScreen() const;
- virtual bool isSelected() const;
- virtual String nameForMSAA() const;
- virtual void setSelected(bool);
- virtual bool canSetSelectedAttribute() const;
- virtual LayoutRect elementRect() const;
- virtual String stringValue() const;
- virtual bool computeAccessibilityIsIgnored() const;
+ virtual Element* actionElement() const override;
+ virtual bool isEnabled() const override;
+ virtual bool isVisible() const override;
+ virtual bool isOffScreen() const override;
+ virtual bool isSelected() const override;
+ virtual String nameForMSAA() const override;
+ virtual void setSelected(bool) override;
+ virtual bool canSetSelectedAttribute() const override;
+ virtual LayoutRect elementRect() const override;
+ virtual String stringValue() const override;
+ virtual bool computeAccessibilityIsIgnored() const override;
RefPtr<HTMLElement> m_element;
};
} // namespace WebCore
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilityMenuListOption, isMenuListOption())
+
#endif // AccessibilityMenuListOption_h
diff --git a/Source/WebCore/accessibility/AccessibilityMenuListPopup.cpp b/Source/WebCore/accessibility/AccessibilityMenuListPopup.cpp
index 6e67f2cf5..fe1bd34b8 100644
--- a/Source/WebCore/accessibility/AccessibilityMenuListPopup.cpp
+++ b/Source/WebCore/accessibility/AccessibilityMenuListPopup.cpp
@@ -70,19 +70,16 @@ bool AccessibilityMenuListPopup::computeAccessibilityIsIgnored() const
AccessibilityMenuListOption* AccessibilityMenuListPopup::menuListOptionAccessibilityObject(HTMLElement* element) const
{
- if (!element || !isHTMLOptionElement(element))
- return 0;
+ if (!is<HTMLOptionElement>(element) || !element->inRenderedDocument())
+ return nullptr;
- AccessibilityObject* object = document()->axObjectCache()->getOrCreate(MenuListOptionRole);
- ASSERT_WITH_SECURITY_IMPLICATION(object->isMenuListOption());
+ auto& option = downcast<AccessibilityMenuListOption>(*document()->axObjectCache()->getOrCreate(MenuListOptionRole));
+ option.setElement(element);
- AccessibilityMenuListOption* option = static_cast<AccessibilityMenuListOption*>(object);
- option->setElement(element);
-
- return option;
+ return &option;
}
-bool AccessibilityMenuListPopup::press() const
+bool AccessibilityMenuListPopup::press()
{
if (!m_parent)
return false;
@@ -102,10 +99,8 @@ void AccessibilityMenuListPopup::addChildren()
m_haveChildren = true;
- const Vector<HTMLElement*>& listItems = toHTMLSelectElement(selectNode)->listItems();
- unsigned length = listItems.size();
- for (unsigned i = 0; i < length; i++) {
- AccessibilityMenuListOption* option = menuListOptionAccessibilityObject(listItems[i]);
+ for (const auto& listItem : downcast<HTMLSelectElement>(*selectNode).listItems()) {
+ AccessibilityMenuListOption* option = menuListOptionAccessibilityObject(listItem);
if (option) {
option->setParent(this);
m_children.append(option);
@@ -118,7 +113,7 @@ void AccessibilityMenuListPopup::childrenChanged()
AXObjectCache* cache = axObjectCache();
for (size_t i = m_children.size(); i > 0 ; --i) {
AccessibilityObject* child = m_children[i - 1].get();
- if (child->actionElement() && !child->actionElement()->attached()) {
+ if (child->actionElement() && !child->actionElement()->inRenderedDocument()) {
child->detachFromParent();
cache->remove(child->axObjectID());
}
@@ -137,8 +132,8 @@ void AccessibilityMenuListPopup::didUpdateActiveOption(int optionIndex)
AXObjectCache* cache = axObjectCache();
RefPtr<AccessibilityObject> child = m_children[optionIndex].get();
- cache->postNotification(child.get(), document(), AXObjectCache::AXFocusedUIElementChanged, true, PostSynchronously);
- cache->postNotification(child.get(), document(), AXObjectCache::AXMenuListItemSelected, true, PostSynchronously);
+ cache->postNotification(child.get(), document(), AXObjectCache::AXFocusedUIElementChanged, TargetElement, PostSynchronously);
+ cache->postNotification(child.get(), document(), AXObjectCache::AXMenuListItemSelected, TargetElement, PostSynchronously);
}
} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AccessibilityMenuListPopup.h b/Source/WebCore/accessibility/AccessibilityMenuListPopup.h
index 52b30dcef..6b4d223b3 100644
--- a/Source/WebCore/accessibility/AccessibilityMenuListPopup.h
+++ b/Source/WebCore/accessibility/AccessibilityMenuListPopup.h
@@ -35,33 +35,34 @@ class AccessibilityMenuListOption;
class HTMLElement;
class HTMLSelectElement;
-class AccessibilityMenuListPopup : public AccessibilityMockObject {
+class AccessibilityMenuListPopup final : public AccessibilityMockObject {
public:
- static PassRefPtr<AccessibilityMenuListPopup> create() { return adoptRef(new AccessibilityMenuListPopup); }
+ static Ref<AccessibilityMenuListPopup> create() { return adoptRef(*new AccessibilityMenuListPopup); }
- virtual bool isEnabled() const;
- virtual bool isOffScreen() const;
+ virtual bool isEnabled() const override;
+ virtual bool isOffScreen() const override;
void didUpdateActiveOption(int optionIndex);
-
private:
AccessibilityMenuListPopup();
- virtual bool isMenuListPopup() const { return true; }
+ virtual bool isMenuListPopup() const override { return true; }
- virtual LayoutRect elementRect() const { return LayoutRect(); }
- virtual AccessibilityRole roleValue() const { return MenuListPopupRole; }
+ virtual LayoutRect elementRect() const override { return LayoutRect(); }
+ virtual AccessibilityRole roleValue() const override { return MenuListPopupRole; }
- virtual bool isVisible() const;
- virtual bool press() const;
- virtual void addChildren();
- virtual void childrenChanged();
- virtual bool computeAccessibilityIsIgnored() const;
+ virtual bool isVisible() const override;
+ virtual bool press() override;
+ virtual void addChildren() override;
+ virtual void childrenChanged() override;
+ virtual bool computeAccessibilityIsIgnored() const override;
AccessibilityMenuListOption* menuListOptionAccessibilityObject(HTMLElement*) const;
};
} // namespace WebCore
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilityMenuListPopup, isMenuListPopup())
+
#endif // AccessibilityMenuListPopup_h
diff --git a/Source/WebCore/accessibility/AccessibilityMockObject.cpp b/Source/WebCore/accessibility/AccessibilityMockObject.cpp
index f48156125..0d804cecb 100644
--- a/Source/WebCore/accessibility/AccessibilityMockObject.cpp
+++ b/Source/WebCore/accessibility/AccessibilityMockObject.cpp
@@ -29,7 +29,7 @@
namespace WebCore {
AccessibilityMockObject::AccessibilityMockObject()
- : m_parent(0)
+ : m_parent(nullptr)
{
}
diff --git a/Source/WebCore/accessibility/AccessibilityMockObject.h b/Source/WebCore/accessibility/AccessibilityMockObject.h
index 8a9a36e89..5059350b8 100644
--- a/Source/WebCore/accessibility/AccessibilityMockObject.h
+++ b/Source/WebCore/accessibility/AccessibilityMockObject.h
@@ -37,27 +37,25 @@ protected:
public:
virtual ~AccessibilityMockObject();
- virtual AccessibilityObject* parentObject() const { return m_parent; }
- virtual void setParent(AccessibilityObject* parent) { m_parent = parent; };
- virtual bool isEnabled() const { return true; }
-
+ virtual AccessibilityObject* parentObject() const override { return m_parent; }
+ virtual void setParent(AccessibilityObject* parent) { m_parent = parent; }
+ virtual bool isEnabled() const override { return true; }
+
protected:
AccessibilityObject* m_parent;
+ // Must be called when the parent object clears its children.
+ virtual void detachFromParent() override { m_parent = nullptr; }
+
private:
- virtual bool isMockObject() const { return true; }
+ virtual bool isMockObject() const override final { return true; }
+ virtual bool isDetachedFromParent() override { return !m_parent; }
+
+ virtual bool computeAccessibilityIsIgnored() const override;
+};
- virtual bool computeAccessibilityIsIgnored() const;
- // Must be called when the parent object clears its children.
- virtual void detachFromParent() { m_parent = 0; }
-};
-
-inline AccessibilityMockObject* toAccessibilityMockObject(AccessibilityObject* object)
-{
- ASSERT(!object || object->isMockObject());
- return static_cast<AccessibilityMockObject*>(object);
-}
-
} // namespace WebCore
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilityMockObject, isMockObject())
+
#endif // AccessibilityMockObject_h
diff --git a/Source/WebCore/accessibility/AccessibilityNodeObject.cpp b/Source/WebCore/accessibility/AccessibilityNodeObject.cpp
index 6aedd68be..5be294ff0 100644
--- a/Source/WebCore/accessibility/AccessibilityNodeObject.cpp
+++ b/Source/WebCore/accessibility/AccessibilityNodeObject.cpp
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
-* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+* 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -31,9 +31,11 @@
#include "AXObjectCache.h"
#include "AccessibilityImageMapLink.h"
+#include "AccessibilityList.h"
#include "AccessibilityListBox.h"
#include "AccessibilitySpinButton.h"
#include "AccessibilityTable.h"
+#include "ElementIterator.h"
#include "EventNames.h"
#include "FloatRect.h"
#include "Frame.h"
@@ -41,6 +43,8 @@
#include "FrameSelection.h"
#include "FrameView.h"
#include "HTMLAreaElement.h"
+#include "HTMLCanvasElement.h"
+#include "HTMLDetailsElement.h"
#include "HTMLFieldSetElement.h"
#include "HTMLFormElement.h"
#include "HTMLFrameElementBase.h"
@@ -53,6 +57,7 @@
#include "HTMLOptGroupElement.h"
#include "HTMLOptionElement.h"
#include "HTMLOptionsCollection.h"
+#include "HTMLParserIdioms.h"
#include "HTMLPlugInImageElement.h"
#include "HTMLSelectElement.h"
#include "HTMLTextAreaElement.h"
@@ -61,17 +66,18 @@
#include "HitTestResult.h"
#include "LabelableElement.h"
#include "LocalizedStrings.h"
+#include "MathMLElement.h"
#include "MathMLNames.h"
#include "NodeList.h"
#include "NodeTraversal.h"
#include "Page.h"
#include "ProgressTracker.h"
+#include "RenderImage.h"
+#include "RenderView.h"
#include "SVGElement.h"
#include "SVGNames.h"
-#include "SVGStyledElement.h"
#include "Text.h"
#include "TextControlInnerElements.h"
-#include "TextIterator.h"
#include "UserGestureIndicator.h"
#include "VisibleUnits.h"
#include "Widget.h"
@@ -80,12 +86,12 @@
#include <wtf/text/StringBuilder.h>
#include <wtf/unicode/CharacterNames.h>
-using namespace std;
-
namespace WebCore {
using namespace HTMLNames;
+static String accessibleNameForNode(Node* node, Node* labelledbyNode = nullptr);
+
AccessibilityNodeObject::AccessibilityNodeObject(Node* node)
: AccessibilityObject()
, m_ariaRole(UnknownRole)
@@ -112,16 +118,16 @@ void AccessibilityNodeObject::init()
m_role = determineAccessibilityRole();
}
-PassRefPtr<AccessibilityNodeObject> AccessibilityNodeObject::create(Node* node)
+Ref<AccessibilityNodeObject> AccessibilityNodeObject::create(Node* node)
{
- return adoptRef(new AccessibilityNodeObject(node));
+ return adoptRef(*new AccessibilityNodeObject(node));
}
-void AccessibilityNodeObject::detach()
+void AccessibilityNodeObject::detach(AccessibilityDetachmentType detachmentType, AXObjectCache* cache)
{
- clearChildren();
- AccessibilityObject::detach();
- m_node = 0;
+ // AccessibilityObject calls clearChildren.
+ AccessibilityObject::detach(detachmentType, cache);
+ m_node = nullptr;
}
void AccessibilityNodeObject::childrenChanged()
@@ -130,7 +136,10 @@ void AccessibilityNodeObject::childrenChanged()
if (!node() && !renderer())
return;
- axObjectCache()->postNotification(this, document(), AXObjectCache::AXChildrenChanged, true);
+ AXObjectCache* cache = axObjectCache();
+ if (!cache)
+ return;
+ cache->postNotification(this, document(), AXObjectCache::AXChildrenChanged);
// Go up the accessibility parent chain, but only if the element already exists. This method is
// called during render layouts, minimal work should be done.
@@ -143,12 +152,15 @@ void AccessibilityNodeObject::childrenChanged()
// In other words, they need to be sent even when the screen reader has not accessed this live region since the last update.
// If this element supports ARIA live regions, then notify the AT of changes.
+ // Sometimes this function can be called many times within a short period of time, leading to posting too many AXLiveRegionChanged
+ // notifications. To fix this, we used a timer to make sure we only post one notification for the children changes within a pre-defined
+ // time interval.
if (parent->supportsARIALiveRegion())
- axObjectCache()->postNotification(parent, parent->document(), AXObjectCache::AXLiveRegionChanged, true);
+ cache->postLiveRegionChangeNotification(parent);
// If this element is an ARIA text control, notify the AT of changes.
- if (parent->isARIATextControl() && !parent->isNativeTextControl() && !parent->node()->rendererIsEditable())
- axObjectCache()->postNotification(parent, parent->document(), AXObjectCache::AXValueChanged, true);
+ if (parent->isNonNativeTextControl())
+ cache->postNotification(parent, parent->document(), AXObjectCache::AXValueChanged);
}
}
@@ -165,12 +177,12 @@ void AccessibilityNodeObject::updateAccessibilityRole()
AccessibilityObject* AccessibilityNodeObject::firstChild() const
{
if (!node())
- return 0;
+ return nullptr;
Node* firstChild = node()->firstChild();
if (!firstChild)
- return 0;
+ return nullptr;
return axObjectCache()->getOrCreate(firstChild);
}
@@ -178,11 +190,11 @@ AccessibilityObject* AccessibilityNodeObject::firstChild() const
AccessibilityObject* AccessibilityNodeObject::lastChild() const
{
if (!node())
- return 0;
+ return nullptr;
Node* lastChild = node()->lastChild();
if (!lastChild)
- return 0;
+ return nullptr;
return axObjectCache()->getOrCreate(lastChild);
}
@@ -190,11 +202,11 @@ AccessibilityObject* AccessibilityNodeObject::lastChild() const
AccessibilityObject* AccessibilityNodeObject::previousSibling() const
{
if (!node())
- return 0;
+ return nullptr;
Node* previousSibling = node()->previousSibling();
if (!previousSibling)
- return 0;
+ return nullptr;
return axObjectCache()->getOrCreate(previousSibling);
}
@@ -202,11 +214,11 @@ AccessibilityObject* AccessibilityNodeObject::previousSibling() const
AccessibilityObject* AccessibilityNodeObject::nextSibling() const
{
if (!node())
- return 0;
+ return nullptr;
Node* nextSibling = node()->nextSibling();
if (!nextSibling)
- return 0;
+ return nullptr;
return axObjectCache()->getOrCreate(nextSibling);
}
@@ -219,13 +231,16 @@ AccessibilityObject* AccessibilityNodeObject::parentObjectIfExists() const
AccessibilityObject* AccessibilityNodeObject::parentObject() const
{
if (!node())
- return 0;
+ return nullptr;
Node* parentObj = node()->parentNode();
- if (parentObj)
- return axObjectCache()->getOrCreate(parentObj);
+ if (!parentObj)
+ return nullptr;
- return 0;
+ if (AXObjectCache* cache = axObjectCache())
+ return cache->getOrCreate(parentObj);
+
+ return nullptr;
}
LayoutRect AccessibilityNodeObject::elementRect() const
@@ -261,8 +276,8 @@ void AccessibilityNodeObject::setNode(Node* node)
Document* AccessibilityNodeObject::document() const
{
if (!node())
- return 0;
- return node()->document();
+ return nullptr;
+ return &node()->document();
}
AccessibilityRole AccessibilityNodeObject::determineAccessibilityRole()
@@ -270,52 +285,52 @@ AccessibilityRole AccessibilityNodeObject::determineAccessibilityRole()
if (!node())
return UnknownRole;
- m_ariaRole = determineAriaRoleAttribute();
+ if ((m_ariaRole = determineAriaRoleAttribute()) != UnknownRole)
+ return m_ariaRole;
- AccessibilityRole ariaRole = ariaRoleAttribute();
- if (ariaRole != UnknownRole)
- return ariaRole;
-
if (node()->isLink())
return WebCoreLinkRole;
if (node()->isTextNode())
return StaticTextRole;
if (node()->hasTagName(buttonTag))
return buttonRoleType();
- if (isHTMLInputElement(node())) {
- HTMLInputElement* input = toHTMLInputElement(node());
- if (input->isCheckbox())
+ if (is<HTMLInputElement>(*node())) {
+ HTMLInputElement& input = downcast<HTMLInputElement>(*node());
+ if (input.isCheckbox())
return CheckBoxRole;
- if (input->isRadioButton())
+ if (input.isRadioButton())
return RadioButtonRole;
- if (input->isTextButton())
+ if (input.isTextButton())
return buttonRoleType();
- if (input->isRangeControl())
+ if (input.isRangeControl())
return SliderRole;
-
+ if (input.isInputTypeHidden())
+ return IgnoredRole;
+ if (input.isSearchField())
+ return SearchFieldRole;
#if ENABLE(INPUT_TYPE_COLOR)
- const AtomicString& type = input->getAttribute(typeAttr);
- if (equalIgnoringCase(type, "color"))
+ if (input.isColorControl())
return ColorWellRole;
#endif
-
return TextFieldRole;
}
if (node()->hasTagName(selectTag)) {
- HTMLSelectElement* selectElement = toHTMLSelectElement(node());
- return selectElement->multiple() ? ListBoxRole : PopUpButtonRole;
+ HTMLSelectElement& selectElement = downcast<HTMLSelectElement>(*node());
+ return selectElement.multiple() ? ListBoxRole : PopUpButtonRole;
}
- if (isHTMLTextAreaElement(node()))
+ if (is<HTMLTextAreaElement>(*node()))
return TextAreaRole;
if (headingLevel())
return HeadingRole;
+ if (node()->hasTagName(blockquoteTag))
+ return BlockquoteRole;
if (node()->hasTagName(divTag))
return DivRole;
if (node()->hasTagName(pTag))
return ParagraphRole;
- if (isHTMLLabelElement(node()))
+ if (is<HTMLLabelElement>(*node()))
return LabelRole;
- if (node()->isElementNode() && toElement(node())->isFocusable())
+ if (is<Element>(*node()) && downcast<Element>(*node()).isFocusable())
return GroupRole;
return UnknownRole;
@@ -332,7 +347,7 @@ void AccessibilityNodeObject::insertChild(AccessibilityObject* child, unsigned i
child->clearChildren();
if (child->accessibilityIsIgnored()) {
- AccessibilityChildrenVector children = child->children();
+ const auto& children = child->children();
size_t length = children.size();
for (size_t i = 0; i < length; ++i)
m_children.insert(index + i, children[i]);
@@ -374,6 +389,10 @@ bool AccessibilityNodeObject::canHaveChildren() const
if (!node() && !isAccessibilityRenderObject())
return false;
+ // When <noscript> is not being used (its renderer() == 0), ignore its children.
+ if (node() && !renderer() && node()->hasTagName(noscriptTag))
+ return false;
+
// Elements that should not have children
switch (roleValue()) {
case ImageRole:
@@ -387,6 +406,7 @@ bool AccessibilityNodeObject::canHaveChildren() const
case ListBoxOptionRole:
case ScrollBarRole:
case ProgressIndicatorRole:
+ case SwitchRole:
return false;
default:
return true;
@@ -401,28 +421,43 @@ bool AccessibilityNodeObject::computeAccessibilityIsIgnored() const
ASSERT(m_initialized);
#endif
+ // Handle non-rendered text that is exposed through aria-hidden=false.
+ if (m_node && m_node->isTextNode() && !renderer()) {
+ // Fallback content in iframe nodes should be ignored.
+ if (m_node->parentNode() && m_node->parentNode()->hasTagName(iframeTag) && m_node->parentNode()->renderer())
+ return true;
+
+ // Whitespace only text elements should be ignored when they have no renderer.
+ String string = stringValue().stripWhiteSpace().simplifyWhiteSpace();
+ if (!string.length())
+ return true;
+ }
+
+ AccessibilityObjectInclusion decision = defaultObjectInclusion();
+ if (decision == IncludeObject)
+ return false;
+ if (decision == IgnoreObject)
+ return true;
// If this element is within a parent that cannot have children, it should not be exposed.
if (isDescendantOfBarrenParent())
return true;
+ if (roleValue() == IgnoredRole)
+ return true;
+
return m_role == UnknownRole;
}
bool AccessibilityNodeObject::canvasHasFallbackContent() const
{
Node* node = this->node();
- if (!node || !node->hasTagName(canvasTag))
+ if (!is<HTMLCanvasElement>(node))
return false;
-
+ HTMLCanvasElement& canvasElement = downcast<HTMLCanvasElement>(*node);
// If it has any children that are elements, we'll assume it might be fallback
// content. If it has no children or its only children are not elements
// (e.g. just text nodes), it doesn't have fallback content.
- for (Node* child = node->firstChild(); child; child = child->nextSibling()) {
- if (child->isElementNode())
- return true;
- }
-
- return false;
+ return childrenOfType<Element>(canvasElement).first();
}
bool AccessibilityNodeObject::isImageButton() const
@@ -430,23 +465,18 @@ bool AccessibilityNodeObject::isImageButton() const
return isNativeImage() && isButton();
}
-bool AccessibilityNodeObject::isAnchor() const
-{
- return !isNativeImage() && isLink();
-}
-
bool AccessibilityNodeObject::isNativeTextControl() const
{
Node* node = this->node();
if (!node)
return false;
- if (isHTMLTextAreaElement(node))
+ if (is<HTMLTextAreaElement>(*node))
return true;
- if (isHTMLInputElement(node)) {
- HTMLInputElement* input = toHTMLInputElement(node);
- return input->isText() || input->isNumberField();
+ if (is<HTMLInputElement>(*node)) {
+ HTMLInputElement& input = downcast<HTMLInputElement>(*node);
+ return input.isText() || input.isNumberField();
}
return false;
@@ -458,12 +488,13 @@ bool AccessibilityNodeObject::isSearchField() const
if (!node)
return false;
- HTMLInputElement* inputElement = node->toInputElement();
- if (!inputElement)
+ if (roleValue() == SearchFieldRole)
+ return true;
+
+ if (!is<HTMLInputElement>(*node))
return false;
- if (inputElement->isSearchField())
- return true;
+ auto& inputElement = downcast<HTMLInputElement>(*node);
// Some websites don't label their search fields as such. However, they will
// use the word "search" in either the form or input type. This won't catch every case,
@@ -475,7 +506,7 @@ bool AccessibilityNodeObject::isSearchField() const
return true;
// Check the form action and the name, which will sometimes be "search".
- HTMLFormElement* form = inputElement->form();
+ auto* form = inputElement.form();
if (form && (form->name().contains("search", false) || form->action().contains("search", false)))
return true;
@@ -488,15 +519,15 @@ bool AccessibilityNodeObject::isNativeImage() const
if (!node)
return false;
- if (isHTMLImageElement(node))
+ if (is<HTMLImageElement>(*node))
return true;
if (node->hasTagName(appletTag) || node->hasTagName(embedTag) || node->hasTagName(objectTag))
return true;
- if (isHTMLInputElement(node)) {
- HTMLInputElement* input = toHTMLInputElement(node);
- return input->isImageButton();
+ if (is<HTMLInputElement>(*node)) {
+ HTMLInputElement& input = downcast<HTMLInputElement>(*node);
+ return input.isImageButton();
}
return false;
@@ -509,29 +540,41 @@ bool AccessibilityNodeObject::isImage() const
bool AccessibilityNodeObject::isPasswordField() const
{
- Node* node = this->node();
- if (!node || !node->isHTMLElement())
+ auto* node = this->node();
+ if (!is<HTMLInputElement>(node))
return false;
if (ariaRoleAttribute() != UnknownRole)
return false;
- HTMLInputElement* inputElement = node->toInputElement();
- if (!inputElement)
- return false;
+ return downcast<HTMLInputElement>(*node).isPasswordField();
+}
+
+AccessibilityObject* AccessibilityNodeObject::passwordFieldOrContainingPasswordField()
+{
+ Node* node = this->node();
+ if (!node)
+ return nullptr;
- return inputElement->isPasswordField();
+ if (is<HTMLInputElement>(*node) && downcast<HTMLInputElement>(*node).isPasswordField())
+ return this;
+
+ auto* element = node->shadowHost();
+ if (!is<HTMLInputElement>(element))
+ return nullptr;
+
+ if (auto* cache = axObjectCache())
+ return cache->getOrCreate(element);
+
+ return nullptr;
}
bool AccessibilityNodeObject::isInputImage() const
{
Node* node = this->node();
- if (!node)
- return false;
-
- if (roleValue() == ButtonRole && isHTMLInputElement(node)) {
- HTMLInputElement* input = toHTMLInputElement(node);
- return input->isImageButton();
+ if (is<HTMLInputElement>(node) && roleValue() == ButtonRole) {
+ HTMLInputElement& input = downcast<HTMLInputElement>(*node);
+ return input.isImageButton();
}
return false;
@@ -554,6 +597,8 @@ bool AccessibilityNodeObject::isMenuRelated() const
case MenuBarRole:
case MenuButtonRole:
case MenuItemRole:
+ case MenuItemCheckboxRole:
+ case MenuItemRadioRole:
return true;
default:
return false;
@@ -577,45 +622,51 @@ bool AccessibilityNodeObject::isMenuButton() const
bool AccessibilityNodeObject::isMenuItem() const
{
- return roleValue() == MenuItemRole;
+ switch (roleValue()) {
+ case MenuItemRole:
+ case MenuItemRadioRole:
+ case MenuItemCheckboxRole:
+ return true;
+ default:
+ return false;
+ }
}
bool AccessibilityNodeObject::isNativeCheckboxOrRadio() const
{
Node* node = this->node();
- if (!node)
+ if (!is<HTMLInputElement>(node))
return false;
- HTMLInputElement* input = node->toInputElement();
- if (input)
- return input->isCheckbox() || input->isRadioButton();
-
- return false;
+ auto& input = downcast<HTMLInputElement>(*node);
+ return input.isCheckbox() || input.isRadioButton();
}
bool AccessibilityNodeObject::isEnabled() const
{
- if (equalIgnoringCase(getAttribute(aria_disabledAttr), "true"))
+ // ARIA says that the disabled status applies to the current element and all descendant elements.
+ for (AccessibilityObject* object = const_cast<AccessibilityNodeObject*>(this); object; object = object->parentObject()) {
+ const AtomicString& disabledStatus = object->getAttribute(aria_disabledAttr);
+ if (equalLettersIgnoringASCIICase(disabledStatus, "true"))
+ return false;
+ if (equalLettersIgnoringASCIICase(disabledStatus, "false"))
+ break;
+ }
+
+ if (roleValue() == HorizontalRuleRole)
return false;
-
+
Node* node = this->node();
- if (!node || !node->isElementNode())
+ if (!is<Element>(node))
return true;
- return !toElement(node)->isDisabledFormControl();
+ return !downcast<Element>(*node).isDisabledFormControl();
}
bool AccessibilityNodeObject::isIndeterminate() const
{
- Node* node = this->node();
- if (!node)
- return false;
-
- HTMLInputElement* inputElement = node->toInputElement();
- if (!inputElement)
- return false;
-
- return inputElement->shouldAppearIndeterminate();
+ auto* node = this->node();
+ return is<HTMLInputElement>(node) && downcast<HTMLInputElement>(*node).shouldAppearIndeterminate();
}
bool AccessibilityNodeObject::isPressed() const
@@ -628,15 +679,16 @@ bool AccessibilityNodeObject::isPressed() const
return false;
// If this is an ARIA button, check the aria-pressed attribute rather than node()->active()
- if (ariaRoleAttribute() == ButtonRole) {
- if (equalIgnoringCase(getAttribute(aria_pressedAttr), "true"))
+ AccessibilityRole ariaRole = ariaRoleAttribute();
+ if (ariaRole == ButtonRole || ariaRole == ToggleButtonRole) {
+ if (equalLettersIgnoringASCIICase(getAttribute(aria_pressedAttr), "true"))
return true;
return false;
}
- if (!node->isElementNode())
+ if (!is<Element>(*node))
return false;
- return toElement(node)->active();
+ return downcast<Element>(*node).active();
}
bool AccessibilityNodeObject::isChecked() const
@@ -646,9 +698,8 @@ bool AccessibilityNodeObject::isChecked() const
return false;
// First test for native checkedness semantics
- HTMLInputElement* inputElement = node->toInputElement();
- if (inputElement)
- return inputElement->shouldAppearChecked();
+ if (is<HTMLInputElement>(*node))
+ return downcast<HTMLInputElement>(*node).shouldAppearChecked();
// Else, if this is an ARIA checkbox or radio, respect the aria-checked attribute
bool validRole = false;
@@ -656,13 +707,16 @@ bool AccessibilityNodeObject::isChecked() const
case RadioButtonRole:
case CheckBoxRole:
case MenuItemRole:
+ case MenuItemCheckboxRole:
+ case MenuItemRadioRole:
+ case SwitchRole:
validRole = true;
break;
default:
break;
}
- if (validRole && equalIgnoringCase(getAttribute(aria_checkedAttr), "true"))
+ if (validRole && equalLettersIgnoringASCIICase(getAttribute(aria_checkedAttr), "true"))
return true;
return false;
@@ -671,49 +725,32 @@ bool AccessibilityNodeObject::isChecked() const
bool AccessibilityNodeObject::isHovered() const
{
Node* node = this->node();
- if (!node)
- return false;
-
- return node->isElementNode() && toElement(node)->hovered();
+ return is<Element>(node) && downcast<Element>(*node).hovered();
}
bool AccessibilityNodeObject::isMultiSelectable() const
{
const AtomicString& ariaMultiSelectable = getAttribute(aria_multiselectableAttr);
- if (equalIgnoringCase(ariaMultiSelectable, "true"))
+ if (equalLettersIgnoringASCIICase(ariaMultiSelectable, "true"))
return true;
- if (equalIgnoringCase(ariaMultiSelectable, "false"))
+ if (equalLettersIgnoringASCIICase(ariaMultiSelectable, "false"))
return false;
- return node() && node()->hasTagName(selectTag) && toHTMLSelectElement(node())->multiple();
-}
-
-bool AccessibilityNodeObject::isReadOnly() const
-{
- Node* node = this->node();
- if (!node)
- return true;
-
- if (isHTMLTextAreaElement(node))
- return toHTMLFormControlElement(node)->isReadOnly();
-
- if (isHTMLInputElement(node)) {
- HTMLInputElement* input = toHTMLInputElement(node);
- if (input->isTextField())
- return input->isReadOnly();
- }
-
- return !node->rendererIsEditable();
+ return node() && node()->hasTagName(selectTag) && downcast<HTMLSelectElement>(*node()).multiple();
}
bool AccessibilityNodeObject::isRequired() const
{
- if (equalIgnoringCase(getAttribute(aria_requiredAttr), "true"))
+ // Explicit aria-required values should trump native required attributes.
+ const AtomicString& requiredValue = getAttribute(aria_requiredAttr);
+ if (equalLettersIgnoringASCIICase(requiredValue, "true"))
return true;
+ if (equalLettersIgnoringASCIICase(requiredValue, "false"))
+ return false;
Node* n = this->node();
- if (n && (n->isElementNode() && toElement(n)->isFormControlElement()))
- return static_cast<HTMLFormControlElement*>(n)->isRequired();
+ if (is<HTMLFormControlElement>(n))
+ return downcast<HTMLFormControlElement>(*n).isRequired();
return false;
}
@@ -721,10 +758,14 @@ bool AccessibilityNodeObject::isRequired() const
bool AccessibilityNodeObject::supportsRequiredAttribute() const
{
switch (roleValue()) {
+ case ButtonRole:
+ return isFileUploadButton();
case CellRole:
+ case ColumnHeaderRole:
case CheckBoxRole:
case ComboBoxRole:
case GridRole:
+ case GridCellRole:
case IncrementorRole:
case ListBoxRole:
case PopUpButtonRole:
@@ -787,10 +828,10 @@ String AccessibilityNodeObject::valueDescription() const
float AccessibilityNodeObject::valueForRange() const
{
- if (node() && isHTMLInputElement(node())) {
- HTMLInputElement* input = toHTMLInputElement(node());
- if (input->isRangeControl())
- return input->valueAsNumber();
+ if (is<HTMLInputElement>(node())) {
+ HTMLInputElement& input = downcast<HTMLInputElement>(*node());
+ if (input.isRangeControl())
+ return input.valueAsNumber();
}
if (!isRangeControl())
@@ -801,10 +842,10 @@ float AccessibilityNodeObject::valueForRange() const
float AccessibilityNodeObject::maxValueForRange() const
{
- if (node() && isHTMLInputElement(node())) {
- HTMLInputElement* input = toHTMLInputElement(node());
- if (input->isRangeControl())
- return input->maximum();
+ if (is<HTMLInputElement>(node())) {
+ HTMLInputElement& input = downcast<HTMLInputElement>(*node());
+ if (input.isRangeControl())
+ return input.maximum();
}
if (!isRangeControl())
@@ -815,10 +856,10 @@ float AccessibilityNodeObject::maxValueForRange() const
float AccessibilityNodeObject::minValueForRange() const
{
- if (node() && isHTMLInputElement(node())) {
- HTMLInputElement* input = toHTMLInputElement(node());
- if (input->isRangeControl())
- return input->minimum();
+ if (is<HTMLInputElement>(node())) {
+ HTMLInputElement& input = downcast<HTMLInputElement>(*node());
+ if (input.isRangeControl())
+ return input.minimum();
}
if (!isRangeControl())
@@ -848,8 +889,7 @@ bool AccessibilityNodeObject::isControl() const
if (!node)
return false;
- return ((node->isElementNode() && toElement(node)->isFormControlElement())
- || AccessibilityObject::isARIAControl(ariaRoleAttribute()));
+ return is<HTMLFormControlElement>(*node) || AccessibilityObject::isARIAControl(ariaRoleAttribute());
}
bool AccessibilityNodeObject::isFieldset() const
@@ -869,37 +909,30 @@ bool AccessibilityNodeObject::isGroup() const
AccessibilityObject* AccessibilityNodeObject::selectedRadioButton()
{
if (!isRadioGroup())
- return 0;
-
- AccessibilityObject::AccessibilityChildrenVector children = this->children();
+ return nullptr;
// Find the child radio button that is selected (ie. the intValue == 1).
- size_t size = children.size();
- for (size_t i = 0; i < size; ++i) {
- AccessibilityObject* object = children[i].get();
- if (object->roleValue() == RadioButtonRole && object->checkboxOrRadioValue() == ButtonStateOn)
- return object;
+ for (const auto& child : children()) {
+ if (child->roleValue() == RadioButtonRole && child->checkboxOrRadioValue() == ButtonStateOn)
+ return child.get();
}
- return 0;
+ return nullptr;
}
AccessibilityObject* AccessibilityNodeObject::selectedTabItem()
{
if (!isTabList())
- return 0;
+ return nullptr;
// Find the child tab item that is selected (ie. the intValue == 1).
AccessibilityObject::AccessibilityChildrenVector tabs;
tabChildren(tabs);
- AccessibilityObject::AccessibilityChildrenVector children = this->children();
- size_t size = tabs.size();
- for (size_t i = 0; i < size; ++i) {
- AccessibilityObject* object = children[i].get();
- if (object->isTabItem() && object->isChecked())
- return object;
+ for (const auto& child : children()) {
+ if (child->isTabItem() && child->isChecked())
+ return child.get();
}
- return 0;
+ return nullptr;
}
AccessibilityButtonState AccessibilityNodeObject::checkboxOrRadioValue() const
@@ -914,44 +947,62 @@ Element* AccessibilityNodeObject::anchorElement() const
{
Node* node = this->node();
if (!node)
- return 0;
+ return nullptr;
AXObjectCache* cache = axObjectCache();
// search up the DOM tree for an anchor element
// NOTE: this assumes that any non-image with an anchor is an HTMLAnchorElement
for ( ; node; node = node->parentNode()) {
- if (isHTMLAnchorElement(node) || (node->renderer() && cache->getOrCreate(node->renderer())->isAnchor()))
- return toElement(node);
+ if (is<HTMLAnchorElement>(*node) || (node->renderer() && cache->getOrCreate(node->renderer())->isLink()))
+ return downcast<Element>(node);
}
- return 0;
+ return nullptr;
}
+static bool isNodeActionElement(Node* node)
+{
+ if (is<HTMLInputElement>(*node)) {
+ HTMLInputElement& input = downcast<HTMLInputElement>(*node);
+ if (!input.isDisabledFormControl() && (input.isRadioButton() || input.isCheckbox() || input.isTextButton() || input.isFileUpload() || input.isImageButton()))
+ return true;
+ } else if (node->hasTagName(buttonTag) || node->hasTagName(selectTag))
+ return true;
+
+ return false;
+}
+
+static Element* nativeActionElement(Node* start)
+{
+ if (!start)
+ return nullptr;
+
+ // Do a deep-dive to see if any nodes should be used as the action element.
+ // We have to look at Nodes, since this method should only be called on objects that do not have children (like buttons).
+ // It solves the problem when authors put role="button" on a group and leave the actual button inside the group.
+
+ for (Node* child = start->firstChild(); child; child = child->nextSibling()) {
+ if (isNodeActionElement(child))
+ return downcast<Element>(child);
+
+ if (Element* subChild = nativeActionElement(child))
+ return subChild;
+ }
+ return nullptr;
+}
+
Element* AccessibilityNodeObject::actionElement() const
{
Node* node = this->node();
if (!node)
- return 0;
-
- if (isHTMLInputElement(node)) {
- HTMLInputElement* input = toHTMLInputElement(node);
- if (!input->isDisabledFormControl() && (isCheckboxOrRadio() || input->isTextButton()))
- return input;
- } else if (node->hasTagName(buttonTag))
- return toElement(node);
-
- if (isFileUploadButton())
- return toElement(node);
-
- if (AccessibilityObject::isARIAInput(ariaRoleAttribute()))
- return toElement(node);
+ return nullptr;
- if (isImageButton())
- return toElement(node);
+ if (isNodeActionElement(node))
+ return downcast<Element>(node);
- if (node->hasTagName(selectTag))
- return toElement(node);
+ if (AccessibilityObject::isARIAInput(ariaRoleAttribute()))
+ return downcast<Element>(node);
switch (roleValue()) {
case ButtonRole:
@@ -959,8 +1010,13 @@ Element* AccessibilityNodeObject::actionElement() const
case ToggleButtonRole:
case TabRole:
case MenuItemRole:
+ case MenuItemCheckboxRole:
+ case MenuItemRadioRole:
case ListItemRole:
- return toElement(node);
+ // Check if the author is hiding the real control element inside the ARIA element.
+ if (Element* nativeElement = nativeActionElement(node))
+ return nativeElement;
+ return downcast<Element>(node);
default:
break;
}
@@ -971,31 +1027,25 @@ Element* AccessibilityNodeObject::actionElement() const
return elt;
}
-Element* AccessibilityNodeObject::mouseButtonListener() const
+Element* AccessibilityNodeObject::mouseButtonListener(MouseButtonListenerResultFilter filter) const
{
Node* node = this->node();
if (!node)
- return 0;
+ return nullptr;
// check if our parent is a mouse button listener
- while (node && !node->isElementNode())
- node = node->parentNode();
-
- if (!node)
- return 0;
-
// FIXME: Do the continuation search like anchorElement does
- for (Element* element = toElement(node); element; element = element->parentElement()) {
- // If we've reached the body and this is not a control element, do not expose press action for this element.
- // It can cause false positives, where every piece of text is labeled as accepting press actions.
- if (element->hasTagName(bodyTag) && isStaticText())
+ for (auto& element : elementLineage(is<Element>(*node) ? downcast<Element>(node) : node->parentElement())) {
+ // If we've reached the body and this is not a control element, do not expose press action for this element unless filter is IncludeBodyElement.
+ // It can cause false positives, where every piece of text is labeled as accepting press actions.
+ if (element.hasTagName(bodyTag) && isStaticText() && filter == ExcludeBodyElement)
break;
- if (element->hasEventListeners(eventNames().clickEvent) || element->hasEventListeners(eventNames().mousedownEvent) || element->hasEventListeners(eventNames().mouseupEvent))
- return element;
+ if (element.hasEventListeners(eventNames().clickEvent) || element.hasEventListeners(eventNames().mousedownEvent) || element.hasEventListeners(eventNames().mouseupEvent))
+ return &element;
}
- return 0;
+ return nullptr;
}
bool AccessibilityNodeObject::isDescendantOfBarrenParent() const
@@ -1021,13 +1071,13 @@ void AccessibilityNodeObject::alterSliderValue(bool increase)
void AccessibilityNodeObject::increment()
{
- UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture);
+ UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture, document());
alterSliderValue(true);
}
void AccessibilityNodeObject::decrement()
{
- UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture);
+ UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture, document());
alterSliderValue(false);
}
@@ -1040,18 +1090,23 @@ void AccessibilityNodeObject::changeValueByStep(bool increase)
setValue(String::number(value));
- axObjectCache()->postNotification(node(), AXObjectCache::AXValueChanged, true);
+ axObjectCache()->postNotification(node(), AXObjectCache::AXValueChanged);
}
void AccessibilityNodeObject::changeValueByPercent(float percentChange)
{
float range = maxValueForRange() - minValueForRange();
+ float step = range * (percentChange / 100);
float value = valueForRange();
- value += range * (percentChange / 100);
+ // Make sure the specified percent will cause a change of one integer step or larger.
+ if (fabs(step) < 1)
+ step = fabs(percentChange) * (1 / percentChange);
+
+ value += step;
setValue(String::number(value));
- axObjectCache()->postNotification(node(), AXObjectCache::AXValueChanged, true);
+ axObjectCache()->postNotification(node(), AXObjectCache::AXValueChanged);
}
bool AccessibilityNodeObject::isGenericFocusableElement() const
@@ -1059,8 +1114,12 @@ bool AccessibilityNodeObject::isGenericFocusableElement() const
if (!canSetFocusAttribute())
return false;
- // If it's a control, it's not generic.
- if (isControl())
+ // If it's a control, it's not generic.
+ if (isControl())
+ return false;
+
+ AccessibilityRole role = roleValue();
+ if (role == VideoRole || role == AudioRole)
return false;
// If it has an aria role, it's not generic.
@@ -1076,14 +1135,14 @@ bool AccessibilityNodeObject::isGenericFocusableElement() const
// The web area and body element are both focusable, but existing logic handles these
// cases already, so we don't need to include them here.
- if (roleValue() == WebAreaRole)
+ if (role == WebAreaRole)
return false;
if (node() && node()->hasTagName(bodyTag))
return false;
// An SVG root is focusable by default, but it's probably not interactive, so don't
// include it. It can still be made accessible by giving it an ARIA role.
- if (roleValue() == SVGRootRole)
+ if (role == SVGRootRole)
return false;
return true;
@@ -1091,21 +1150,16 @@ bool AccessibilityNodeObject::isGenericFocusableElement() const
HTMLLabelElement* AccessibilityNodeObject::labelForElement(Element* element) const
{
- if (!element->isHTMLElement() || !toHTMLElement(element)->isLabelable())
- return 0;
+ if (!is<HTMLElement>(*element) || !downcast<HTMLElement>(*element).isLabelable())
+ return nullptr;
const AtomicString& id = element->getIdAttribute();
if (!id.isEmpty()) {
- if (HTMLLabelElement* label = element->treeScope()->labelElementForId(id))
+ if (HTMLLabelElement* label = element->treeScope().labelElementForId(id))
return label;
}
- for (Element* parent = element->parentElement(); parent; parent = parent->parentElement()) {
- if (isHTMLLabelElement(parent))
- return toHTMLLabelElement(parent);
- }
-
- return 0;
+ return ancestorsOfType<HTMLLabelElement>(*element).first();
}
String AccessibilityNodeObject::ariaAccessibilityDescription() const
@@ -1121,69 +1175,81 @@ String AccessibilityNodeObject::ariaAccessibilityDescription() const
return String();
}
-static Element* siblingWithAriaRole(String role, Node* node)
+static Element* siblingWithAriaRole(Node* node, const char* role)
{
- Node* parent = node->parentNode();
+ // FIXME: Either we should add a null check here or change the function to take a reference instead of a pointer.
+ ContainerNode* parent = node->parentNode();
if (!parent)
- return 0;
-
- for (Node* sibling = parent->firstChild(); sibling; sibling = sibling->nextSibling()) {
- if (sibling->isElementNode()) {
- const AtomicString& siblingAriaRole = toElement(sibling)->getAttribute(roleAttr);
- if (equalIgnoringCase(siblingAriaRole, role))
- return toElement(sibling);
- }
+ return nullptr;
+
+ for (auto& sibling : childrenOfType<Element>(*parent)) {
+ // FIXME: Should skip sibling that is the same as the node.
+ if (equalIgnoringASCIICase(sibling.fastGetAttribute(roleAttr), role))
+ return &sibling;
}
-
- return 0;
+
+ return nullptr;
}
Element* AccessibilityNodeObject::menuElementForMenuButton() const
{
if (ariaRoleAttribute() != MenuButtonRole)
- return 0;
+ return nullptr;
- return siblingWithAriaRole("menu", node());
+ return siblingWithAriaRole(node(), "menu");
}
AccessibilityObject* AccessibilityNodeObject::menuForMenuButton() const
{
- return axObjectCache()->getOrCreate(menuElementForMenuButton());
+ if (AXObjectCache* cache = axObjectCache())
+ return cache->getOrCreate(menuElementForMenuButton());
+ return nullptr;
}
Element* AccessibilityNodeObject::menuItemElementForMenu() const
{
if (ariaRoleAttribute() != MenuRole)
- return 0;
+ return nullptr;
- return siblingWithAriaRole("menuitem", node());
+ return siblingWithAriaRole(node(), "menuitem");
}
AccessibilityObject* AccessibilityNodeObject::menuButtonForMenu() const
{
+ AXObjectCache* cache = axObjectCache();
+ if (!cache)
+ return nullptr;
+
Element* menuItem = menuItemElementForMenu();
if (menuItem) {
// ARIA just has generic menu items. AppKit needs to know if this is a top level items like MenuBarButton or MenuBarItem
- AccessibilityObject* menuItemAX = axObjectCache()->getOrCreate(menuItem);
+ AccessibilityObject* menuItemAX = cache->getOrCreate(menuItem);
if (menuItemAX && menuItemAX->isMenuButton())
return menuItemAX;
}
- return 0;
+ return nullptr;
}
-void AccessibilityNodeObject::titleElementText(Vector<AccessibilityText>& textOrder)
+bool AccessibilityNodeObject::usesAltTagForTextComputation() const
+{
+ return isImage() || isInputImage() || isNativeImage() || isCanvas() || (node() && node()->hasTagName(imgTag));
+}
+
+void AccessibilityNodeObject::titleElementText(Vector<AccessibilityText>& textOrder) const
{
Node* node = this->node();
if (!node)
return;
- bool isInputTag = isHTMLInputElement(node);
+ bool isInputTag = is<HTMLInputElement>(*node);
if (isInputTag || AccessibilityObject::isARIAInput(ariaRoleAttribute()) || isControl()) {
- HTMLLabelElement* label = labelForElement(toElement(node));
- if (label) {
+ if (HTMLLabelElement* label = labelForElement(downcast<Element>(node))) {
AccessibilityObject* labelObject = axObjectCache()->getOrCreate(label);
- textOrder.append(AccessibilityText(label->innerText(), LabelByElementText, labelObject));
+ String innerText = label->innerText();
+ // Only use the <label> text if there's no ARIA override.
+ if (!innerText.isEmpty() && !ariaAccessibilityDescription())
+ textOrder.append(AccessibilityText(innerText, LabelByElementText, labelObject));
return;
}
}
@@ -1208,11 +1274,20 @@ void AccessibilityNodeObject::alternativeText(Vector<AccessibilityText>& textOrd
if (!ariaLabel.isEmpty())
textOrder.append(AccessibilityText(ariaLabel, AlternativeText));
- if (isImage() || isInputImage() || isNativeImage() || isCanvas()) {
+ if (usesAltTagForTextComputation()) {
+ if (is<RenderImage>(renderer())) {
+ String renderAltText = downcast<RenderImage>(*renderer()).altText();
+
+ // RenderImage will return title as a fallback from altText, but we don't want title here because we consider that in helpText.
+ if (!renderAltText.isEmpty() && renderAltText != getAttribute(titleAttr)) {
+ textOrder.append(AccessibilityText(renderAltText, AlternativeText));
+ return;
+ }
+ }
// Images should use alt as long as the attribute is present, even if empty.
// Otherwise, it should fallback to other methods, like the title attribute.
const AtomicString& alt = getAttribute(altAttr);
- if (!alt.isNull())
+ if (!alt.isEmpty())
textOrder.append(AccessibilityText(alt, AlternativeText));
}
@@ -1220,14 +1295,19 @@ void AccessibilityNodeObject::alternativeText(Vector<AccessibilityText>& textOrd
if (!node)
return;
-#if ENABLE(SVG)
+ // The fieldset element derives its alternative text from the first associated legend element if one is available.
+ if (is<HTMLFieldSetElement>(*node)) {
+ AccessibilityObject* object = axObjectCache()->getOrCreate(downcast<HTMLFieldSetElement>(*node).legend());
+ if (object && !object->isHidden())
+ textOrder.append(AccessibilityText(accessibleNameForNode(object->node()), AlternativeText));
+ }
+
// SVG elements all can have a <svg:title> element inside which should act as the descriptive text.
- if (node->isSVGElement() && toSVGElement(node)->isSVGStyledElement())
- textOrder.append(AccessibilityText(toSVGStyledElement(node)->title(), AlternativeText));
-#endif
+ if (node->isSVGElement())
+ textOrder.append(AccessibilityText(downcast<SVGElement>(*node).title(), AlternativeText));
#if ENABLE(MATHML)
- if (node->isElementNode() && toElement(node)->isMathMLElement())
+ if (node->isMathMLElement())
textOrder.append(AccessibilityText(getAttribute(MathMLNames::alttextAttr), AlternativeText));
#endif
}
@@ -1238,11 +1318,11 @@ void AccessibilityNodeObject::visibleText(Vector<AccessibilityText>& textOrder)
if (!node)
return;
- bool isInputTag = isHTMLInputElement(node);
+ bool isInputTag = is<HTMLInputElement>(*node);
if (isInputTag) {
- HTMLInputElement* input = toHTMLInputElement(node);
- if (input->isTextButton()) {
- textOrder.append(AccessibilityText(input->valueWithDefault(), VisibleText));
+ HTMLInputElement& input = downcast<HTMLInputElement>(*node);
+ if (input.isTextButton()) {
+ textOrder.append(AccessibilityText(input.valueWithDefault(), VisibleText));
return;
}
}
@@ -1258,16 +1338,22 @@ void AccessibilityNodeObject::visibleText(Vector<AccessibilityText>& textOrder)
// Native popup buttons should not use their button children's text as a title. That value is retrieved through stringValue().
if (node->hasTagName(selectTag))
break;
+ FALLTHROUGH;
case ButtonRole:
case ToggleButtonRole:
case CheckBoxRole:
case ListBoxOptionRole:
+ // MacOS does not expect native <li> elements to expose label information, it only expects leaf node elements to do that.
+#if !PLATFORM(COCOA)
case ListItemRole:
+#endif
case MenuButtonRole:
case MenuItemRole:
+ case MenuItemCheckboxRole:
+ case MenuItemRadioRole:
case RadioButtonRole:
+ case SwitchRole:
case TabRole:
- case ProgressIndicatorRole:
useTextUnderElement = true;
break;
default:
@@ -1276,11 +1362,17 @@ void AccessibilityNodeObject::visibleText(Vector<AccessibilityText>& textOrder)
// If it's focusable but it's not content editable or a known control type, then it will appear to
// the user as a single atomic object, so we should use its text as the default title.
- if (isHeading() || isLink() || isGenericFocusableElement())
+ if (isHeading() || isLink())
useTextUnderElement = true;
if (useTextUnderElement) {
- String text = textUnderElement();
+ AccessibilityTextUnderElementMode mode;
+
+ // Headings often include links as direct children. Those links need to be included in text under element.
+ if (isHeading())
+ mode.includeFocusableContent = true;
+
+ String text = textUnderElement(mode);
if (!text.isEmpty())
textOrder.append(AccessibilityText(text, ChildrenText));
}
@@ -1295,27 +1387,23 @@ void AccessibilityNodeObject::helpText(Vector<AccessibilityText>& textOrder) con
String describedBy = ariaDescribedByAttribute();
if (!describedBy.isEmpty())
textOrder.append(AccessibilityText(describedBy, SummaryText));
-
- // Add help type text that is derived from ancestors.
- for (Node* curr = node(); curr; curr = curr->parentNode()) {
- const AtomicString& summary = getAttribute(summaryAttr);
- if (!summary.isEmpty())
- textOrder.append(AccessibilityText(summary, SummaryText));
-
- // The title attribute should be used as help text unless it is already being used as descriptive text.
- const AtomicString& title = getAttribute(titleAttr);
- if (!title.isEmpty())
+
+ // Summary attribute used as help text on tables.
+ const AtomicString& summary = getAttribute(summaryAttr);
+ if (!summary.isEmpty())
+ textOrder.append(AccessibilityText(summary, SummaryText));
+
+ // The title attribute should be used as help text unless it is already being used as descriptive text.
+ // However, when the title attribute is the only text alternative provided, it may be exposed as the
+ // descriptive text. This is problematic in the case of meters because the HTML spec suggests authors
+ // can expose units through this attribute. Therefore, if the element is a meter, change its source
+ // type to HelpText.
+ const AtomicString& title = getAttribute(titleAttr);
+ if (!title.isEmpty()) {
+ if (!isMeter())
textOrder.append(AccessibilityText(title, TitleTagText));
-
- // Only take help text from an ancestor element if its a group or an unknown role. If help was
- // added to those kinds of elements, it is likely it was meant for a child element.
- AccessibilityObject* axObj = axObjectCache()->getOrCreate(curr);
- if (!axObj)
- return;
-
- AccessibilityRole role = axObj->roleValue();
- if (role != GroupRole && role != UnknownRole)
- break;
+ else
+ textOrder.append(AccessibilityText(title, HelpText));
}
}
@@ -1338,14 +1426,13 @@ void AccessibilityNodeObject::ariaLabeledByText(Vector<AccessibilityText>& textO
Vector<Element*> elements;
ariaLabeledByElements(elements);
- Vector<RefPtr<AccessibilityObject> > axElements;
- unsigned length = elements.size();
- for (unsigned k = 0; k < length; k++) {
- RefPtr<AccessibilityObject> axElement = axObjectCache()->getOrCreate(elements[k]);
+ Vector<RefPtr<AccessibilityObject>> axElements;
+ for (const auto& element : elements) {
+ RefPtr<AccessibilityObject> axElement = axObjectCache()->getOrCreate(element);
axElements.append(axElement);
}
- textOrder.append(AccessibilityText(ariaLabeledBy, AlternativeText, axElements));
+ textOrder.append(AccessibilityText(ariaLabeledBy, AlternativeText, WTFMove(axElements)));
}
}
@@ -1367,30 +1454,26 @@ String AccessibilityNodeObject::alternativeTextForWebArea() const
// Check if the HTML element has an aria-label for the webpage.
if (Element* documentElement = document->documentElement()) {
- const AtomicString& ariaLabel = documentElement->getAttribute(aria_labelAttr);
+ const AtomicString& ariaLabel = documentElement->fastGetAttribute(aria_labelAttr);
if (!ariaLabel.isEmpty())
return ariaLabel;
}
- Node* owner = document->ownerElement();
- if (owner) {
+ if (auto* owner = document->ownerElement()) {
if (owner->hasTagName(frameTag) || owner->hasTagName(iframeTag)) {
- const AtomicString& title = toElement(owner)->getAttribute(titleAttr);
+ const AtomicString& title = owner->fastGetAttribute(titleAttr);
if (!title.isEmpty())
return title;
- return toElement(owner)->getNameAttribute();
}
- if (owner->isHTMLElement())
- return toHTMLElement(owner)->getNameAttribute();
+ return owner->getNameAttribute();
}
String documentTitle = document->title();
if (!documentTitle.isEmpty())
return documentTitle;
- owner = document->body();
- if (owner && owner->isHTMLElement())
- return toHTMLElement(owner)->getNameAttribute();
+ if (auto* body = document->bodyOrFrameset())
+ return body->getNameAttribute();
return String();
}
@@ -1405,7 +1488,7 @@ String AccessibilityNodeObject::accessibilityDescription() const
if (!ariaDescription.isEmpty())
return ariaDescription;
- if (isImage() || isInputImage() || isNativeImage() || isCanvas()) {
+ if (usesAltTagForTextComputation()) {
// Images should use alt as long as the attribute is present, even if empty.
// Otherwise, it should fallback to other methods, like the title attribute.
const AtomicString& alt = getAttribute(altAttr);
@@ -1413,14 +1496,12 @@ String AccessibilityNodeObject::accessibilityDescription() const
return alt;
}
-#if ENABLE(SVG)
// SVG elements all can have a <svg:title> element inside which should act as the descriptive text.
- if (m_node && m_node->isSVGElement() && toSVGElement(m_node)->isSVGStyledElement())
- return toSVGStyledElement(m_node)->title();
-#endif
+ if (m_node && m_node->isSVGElement())
+ return downcast<SVGElement>(*m_node).title();
#if ENABLE(MATHML)
- if (m_node && m_node->isElementNode() && toElement(m_node)->isMathMLElement())
+ if (is<MathMLElement>(m_node))
return getAttribute(MathMLNames::alttextAttr);
#endif
@@ -1449,21 +1530,22 @@ String AccessibilityNodeObject::helpText() const
return describedBy;
String description = accessibilityDescription();
- for (Node* curr = node; curr; curr = curr->parentNode()) {
- if (curr->isHTMLElement()) {
- const AtomicString& summary = toElement(curr)->getAttribute(summaryAttr);
+ for (Node* ancestor = node; ancestor; ancestor = ancestor->parentNode()) {
+ if (is<HTMLElement>(*ancestor)) {
+ HTMLElement& element = downcast<HTMLElement>(*ancestor);
+ const AtomicString& summary = element.getAttribute(summaryAttr);
if (!summary.isEmpty())
return summary;
// The title attribute should be used as help text unless it is already being used as descriptive text.
- const AtomicString& title = toElement(curr)->getAttribute(titleAttr);
+ const AtomicString& title = element.getAttribute(titleAttr);
if (!title.isEmpty() && description != title)
return title;
}
// Only take help text from an ancestor element if its a group or an unknown role. If help was
// added to those kinds of elements, it is likely it was meant for a child element.
- AccessibilityObject* axObj = axObjectCache()->getOrCreate(curr);
+ AccessibilityObject* axObj = axObjectCache()->getOrCreate(ancestor);
if (axObj) {
AccessibilityRole role = axObj->roleValue();
if (role != GroupRole && role != UnknownRole)
@@ -1477,10 +1559,10 @@ String AccessibilityNodeObject::helpText() const
unsigned AccessibilityNodeObject::hierarchicalLevel() const
{
Node* node = this->node();
- if (!node || !node->isElementNode())
+ if (!is<Element>(node))
return 0;
- Element* element = toElement(node);
- String ariaLevel = element->getAttribute(aria_levelAttr);
+ Element& element = downcast<Element>(*node);
+ const AtomicString& ariaLevel = element.fastGetAttribute(aria_levelAttr);
if (!ariaLevel.isEmpty())
return ariaLevel.toInt();
@@ -1502,12 +1584,25 @@ unsigned AccessibilityNodeObject::hierarchicalLevel() const
return level;
}
+void AccessibilityNodeObject::setIsExpanded(bool expand)
+{
+#if ENABLE(DETAILS_ELEMENT)
+ if (is<HTMLDetailsElement>(node())) {
+ auto& details = downcast<HTMLDetailsElement>(*node());
+ if (expand != details.isOpen())
+ details.toggleOpen();
+ }
+#else
+ UNUSED_PARAM(expand);
+#endif
+}
+
// When building the textUnderElement for an object, determine whether or not
// we should include the inner text of this given descendant object or skip it.
-static bool shouldUseAccessiblityObjectInnerText(AccessibilityObject* obj, AccessibilityTextUnderElementMode mode)
+static bool shouldUseAccessibilityObjectInnerText(AccessibilityObject* obj, AccessibilityTextUnderElementMode mode)
{
// Do not use any heuristic if we are explicitly asking to include all the children.
- if (mode == TextUnderElementModeIncludeAllChildren)
+ if (mode.childrenInclusion == AccessibilityTextUnderElementMode::TextUnderElementModeIncludeAllChildren)
return true;
// Consider this hypothetical example:
@@ -1527,7 +1622,12 @@ static bool shouldUseAccessiblityObjectInnerText(AccessibilityObject* obj, Acces
// quite long. As a heuristic, skip links, controls, and elements that are usually
// containers with lots of children.
- if (equalIgnoringCase(obj->getAttribute(aria_hiddenAttr), "true"))
+ // ARIA states that certain elements are not allowed to expose their children content for name calculation.
+ if (mode.childrenInclusion == AccessibilityTextUnderElementMode::TextUnderElementModeIncludeNameFromContentsChildren
+ && !obj->accessibleNameDerivesFromContent())
+ return false;
+
+ if (equalLettersIgnoringASCIICase(obj->getAttribute(aria_hiddenAttr), "true"))
return false;
// If something doesn't expose any children, then we can always take the inner text content.
@@ -1536,47 +1636,73 @@ static bool shouldUseAccessiblityObjectInnerText(AccessibilityObject* obj, Acces
return true;
// Skip focusable children, so we don't include the text of links and controls.
- if (obj->canSetFocusAttribute())
+ if (obj->canSetFocusAttribute() && !mode.includeFocusableContent)
return false;
// Skip big container elements like lists, tables, etc.
- if (obj->isList() || obj->isAccessibilityTable() || obj->isTree() || obj->isCanvas())
+ if (is<AccessibilityList>(*obj))
+ return false;
+
+ if (is<AccessibilityTable>(*obj) && downcast<AccessibilityTable>(*obj).isExposableThroughAccessibility())
+ return false;
+
+ if (obj->isTree() || obj->isCanvas())
return false;
return true;
}
+static bool shouldAddSpaceBeforeAppendingNextElement(StringBuilder& builder, const String& childText)
+{
+ if (!builder.length() || !childText.length())
+ return false;
+
+ // We don't need to add an additional space before or after a line break.
+ return !(isHTMLLineBreak(childText[0]) || isHTMLLineBreak(builder[builder.length() - 1]));
+}
+
+static void appendNameToStringBuilder(StringBuilder& builder, const String& text)
+{
+ if (shouldAddSpaceBeforeAppendingNextElement(builder, text))
+ builder.append(' ');
+ builder.append(text);
+}
+
String AccessibilityNodeObject::textUnderElement(AccessibilityTextUnderElementMode mode) const
{
Node* node = this->node();
- if (node && node->isTextNode())
- return toText(node)->wholeText();
+ if (is<Text>(node))
+ return downcast<Text>(*node).wholeText();
StringBuilder builder;
for (AccessibilityObject* child = firstChild(); child; child = child->nextSibling()) {
- if (!shouldUseAccessiblityObjectInnerText(child, mode))
+ if (mode.ignoredChildNode && child->node() == mode.ignoredChildNode)
+ continue;
+
+ bool shouldDeriveNameFromAuthor = (mode.childrenInclusion == AccessibilityTextUnderElementMode::TextUnderElementModeIncludeNameFromContentsChildren && !child->accessibleNameDerivesFromContent());
+ if (shouldDeriveNameFromAuthor) {
+ appendNameToStringBuilder(builder, accessibleNameForNode(child->node()));
+ continue;
+ }
+
+ if (!shouldUseAccessibilityObjectInnerText(child, mode))
continue;
- if (child->isAccessibilityNodeObject()) {
+ if (is<AccessibilityNodeObject>(*child)) {
Vector<AccessibilityText> textOrder;
- toAccessibilityNodeObject(child)->alternativeText(textOrder);
+ downcast<AccessibilityNodeObject>(*child).alternativeText(textOrder);
if (textOrder.size() > 0 && textOrder[0].text.length()) {
- if (builder.length())
- builder.append(' ');
- builder.append(textOrder[0].text);
+ appendNameToStringBuilder(builder, textOrder[0].text);
continue;
}
}
-
+
String childText = child->textUnderElement(mode);
- if (childText.length()) {
- if (builder.length())
- builder.append(' ');
- builder.append(childText);
- }
+ if (childText.length())
+ appendNameToStringBuilder(builder, childText);
}
- return builder.toString().stripWhiteSpace().simplifyWhiteSpace();
+ return builder.toString().stripWhiteSpace().simplifyWhiteSpace(isHTMLSpaceButNotLineBreak);
}
String AccessibilityNodeObject::title() const
@@ -1585,16 +1711,17 @@ String AccessibilityNodeObject::title() const
if (!node)
return String();
- bool isInputTag = isHTMLInputElement(node);
+ bool isInputTag = is<HTMLInputElement>(*node);
if (isInputTag) {
- HTMLInputElement* input = toHTMLInputElement(node);
- if (input->isTextButton())
- return input->valueWithDefault();
+ HTMLInputElement& input = downcast<HTMLInputElement>(*node);
+ if (input.isTextButton())
+ return input.valueWithDefault();
}
if (isInputTag || AccessibilityObject::isARIAInput(ariaRoleAttribute()) || isControl()) {
- HTMLLabelElement* label = labelForElement(toElement(node));
- if (label && !exposesTitleUIElement())
+ HTMLLabelElement* label = labelForElement(downcast<Element>(node));
+ // Use the label text as the title if 1) the title element is NOT an exposed element and 2) there's no ARIA override.
+ if (label && !exposesTitleUIElement() && !ariaAccessibilityDescription().length())
return label->innerText();
}
@@ -1607,6 +1734,7 @@ String AccessibilityNodeObject::title() const
// Native popup buttons should not use their button children's text as a title. That value is retrieved through stringValue().
if (node->hasTagName(selectTag))
return String();
+ FALLTHROUGH;
case ButtonRole:
case ToggleButtonRole:
case CheckBoxRole:
@@ -1614,7 +1742,10 @@ String AccessibilityNodeObject::title() const
case ListItemRole:
case MenuButtonRole:
case MenuItemRole:
+ case MenuItemCheckboxRole:
+ case MenuItemRadioRole:
case RadioButtonRole:
+ case SwitchRole:
case TabRole:
return textUnderElement();
// SVGRoots should not use the text under itself as a title. That could include the text of objects like <text>.
@@ -1624,13 +1755,10 @@ String AccessibilityNodeObject::title() const
break;
}
- if (isHeading() || isLink())
- return textUnderElement();
-
- // If it's focusable but it's not content editable or a known control type, then it will appear to
- // the user as a single atomic object, so we should use its text as the default title.
- if (isGenericFocusableElement())
+ if (isLink())
return textUnderElement();
+ if (isHeading())
+ return textUnderElement(AccessibilityTextUnderElementMode(AccessibilityTextUnderElementMode::TextUnderElementModeSkipIgnoredChildren, true));
return String();
}
@@ -1638,8 +1766,12 @@ String AccessibilityNodeObject::title() const
String AccessibilityNodeObject::text() const
{
// If this is a user defined static text, use the accessible name computation.
- if (ariaRoleAttribute() == StaticTextRole)
- return ariaAccessibilityDescription();
+ if (ariaRoleAttribute() == StaticTextRole) {
+ Vector<AccessibilityText> textOrder;
+ alternativeText(textOrder);
+ if (textOrder.size() > 0 && textOrder[0].text.length())
+ return textOrder[0].text;
+ }
if (!isTextControl())
return String();
@@ -1648,13 +1780,13 @@ String AccessibilityNodeObject::text() const
if (!node)
return String();
- if (isNativeTextControl() && (isHTMLTextAreaElement(node) || isHTMLInputElement(node)))
- return toHTMLTextFormControlElement(node)->value();
+ if (isNativeTextControl() && is<HTMLTextFormControlElement>(*node))
+ return downcast<HTMLTextFormControlElement>(*node).value();
if (!node->isElementNode())
return String();
- return toElement(node)->innerText();
+ return downcast<Element>(node)->innerText();
}
String AccessibilityNodeObject::stringValue() const
@@ -1674,16 +1806,16 @@ String AccessibilityNodeObject::stringValue() const
return textUnderElement();
if (node->hasTagName(selectTag)) {
- HTMLSelectElement* selectElement = toHTMLSelectElement(node);
- int selectedIndex = selectElement->selectedIndex();
- const Vector<HTMLElement*> listItems = selectElement->listItems();
+ HTMLSelectElement& selectElement = downcast<HTMLSelectElement>(*node);
+ int selectedIndex = selectElement.selectedIndex();
+ const Vector<HTMLElement*>& listItems = selectElement.listItems();
if (selectedIndex >= 0 && static_cast<size_t>(selectedIndex) < listItems.size()) {
const AtomicString& overriddenDescription = listItems[selectedIndex]->fastGetAttribute(aria_labelAttr);
if (!overriddenDescription.isNull())
return overriddenDescription;
}
- if (!selectElement->multiple())
- return selectElement->value();
+ if (!selectElement.multiple())
+ return selectElement.value();
return String();
}
@@ -1703,57 +1835,103 @@ void AccessibilityNodeObject::colorValue(int& r, int& g, int& b) const
g = 0;
b = 0;
+#if ENABLE(INPUT_TYPE_COLOR)
if (!isColorWell())
return;
- if (!node() || !isHTMLInputElement(node()))
+ if (!is<HTMLInputElement>(node()))
return;
- HTMLInputElement* input = toHTMLInputElement(node());
- const AtomicString& type = input->getAttribute(typeAttr);
- if (!equalIgnoringCase(type, "color"))
+ auto& input = downcast<HTMLInputElement>(*node());
+ if (!input.isColorControl())
return;
// HTMLInputElement::value always returns a string parseable by Color().
- Color color(input->value());
+ Color color(input.value());
r = color.red();
g = color.green();
b = color.blue();
+#endif
}
// This function implements the ARIA accessible name as described by the Mozilla
// ARIA Implementer's Guide.
-static String accessibleNameForNode(Node* node)
+static String accessibleNameForNode(Node* node, Node* labelledbyNode)
{
- if (node->isTextNode())
- return toText(node)->data();
+ ASSERT(node);
+ if (!is<Element>(node))
+ return String();
+
+ Element& element = downcast<Element>(*node);
+ const AtomicString& ariaLabel = element.fastGetAttribute(aria_labelAttr);
+ if (!ariaLabel.isEmpty())
+ return ariaLabel;
+
+ const AtomicString& alt = element.fastGetAttribute(altAttr);
+ if (!alt.isEmpty())
+ return alt;
+
+ // If the node can be turned into an AX object, we can use standard name computation rules.
+ // If however, the node cannot (because there's no renderer e.g.) fallback to using the basic text underneath.
+ AccessibilityObject* axObject = node->document().axObjectCache()->getOrCreate(node);
+ if (axObject) {
+ String valueDescription = axObject->valueDescription();
+ if (!valueDescription.isEmpty())
+ return valueDescription;
+ }
+
+ if (is<HTMLInputElement>(*node))
+ return downcast<HTMLInputElement>(*node).value();
+
+ String text;
+ if (axObject) {
+ if (axObject->accessibleNameDerivesFromContent())
+ text = axObject->textUnderElement(AccessibilityTextUnderElementMode(AccessibilityTextUnderElementMode::TextUnderElementModeIncludeNameFromContentsChildren, true, labelledbyNode));
+ } else
+ text = element.innerText();
- if (isHTMLInputElement(node))
- return toHTMLInputElement(node)->value();
+ if (!text.isEmpty())
+ return text;
+
+ const AtomicString& title = element.fastGetAttribute(titleAttr);
+ if (!title.isEmpty())
+ return title;
+
+ return String();
+}
- if (node->isHTMLElement()) {
- const AtomicString& alt = toHTMLElement(node)->getAttribute(altAttr);
- if (!alt.isEmpty())
- return alt;
+String AccessibilityNodeObject::accessibilityDescriptionForChildren() const
+{
+ Node* node = this->node();
+ if (!node)
+ return String();
+
+ AXObjectCache* cache = axObjectCache();
+ if (!cache)
+ return String();
+
+ StringBuilder builder;
+ for (Node* child = node->firstChild(); child; child = child->nextSibling()) {
+ if (!is<Element>(child))
+ continue;
+
+ if (AccessibilityObject* axObject = cache->getOrCreate(child)) {
+ String description = axObject->ariaLabeledByAttribute();
+ if (description.isEmpty())
+ description = accessibleNameForNode(child);
+ appendNameToStringBuilder(builder, description);
+ }
}
- return String();
+ return builder.toString();
}
String AccessibilityNodeObject::accessibilityDescriptionForElements(Vector<Element*> &elements) const
{
StringBuilder builder;
unsigned size = elements.size();
- for (unsigned i = 0; i < size; ++i) {
- Element* idElement = elements[i];
-
- builder.append(accessibleNameForNode(idElement));
- for (Node* n = idElement->firstChild(); n; n = NodeTraversal::next(n, idElement))
- builder.append(accessibleNameForNode(n));
-
- if (i != size - 1)
- builder.append(' ');
- }
+ for (unsigned i = 0; i < size; ++i)
+ appendNameToStringBuilder(builder, accessibleNameForNode(elements[i], node()));
return builder.toString();
}
@@ -1765,39 +1943,11 @@ String AccessibilityNodeObject::ariaDescribedByAttribute() const
return accessibilityDescriptionForElements(elements);
}
-void AccessibilityNodeObject::elementsFromAttribute(Vector<Element*>& elements, const QualifiedName& attribute) const
-{
- Node* node = this->node();
- if (!node || !node->isElementNode())
- return;
-
- TreeScope* scope = node->treeScope();
- if (!scope)
- return;
-
- String idList = getAttribute(attribute).string();
- if (idList.isEmpty())
- return;
-
- idList.replace('\n', ' ');
- Vector<String> idVector;
- idList.split(' ', idVector);
-
- unsigned size = idVector.size();
- for (unsigned i = 0; i < size; ++i) {
- AtomicString idName(idVector[i]);
- Element* idElement = scope->getElementById(idName);
- if (idElement)
- elements.append(idElement);
- }
-}
-
-
void AccessibilityNodeObject::ariaLabeledByElements(Vector<Element*>& elements) const
{
- elementsFromAttribute(elements, aria_labeledbyAttr);
+ elementsFromAttribute(elements, aria_labelledbyAttr);
if (!elements.size())
- elementsFromAttribute(elements, aria_labelledbyAttr);
+ elementsFromAttribute(elements, aria_labeledbyAttr);
}
@@ -1809,6 +1959,17 @@ String AccessibilityNodeObject::ariaLabeledByAttribute() const
return accessibilityDescriptionForElements(elements);
}
+bool AccessibilityNodeObject::hasAttributesRequiredForInclusion() const
+{
+ if (AccessibilityObject::hasAttributesRequiredForInclusion())
+ return true;
+
+ if (!ariaAccessibilityDescription().isEmpty())
+ return true;
+
+ return false;
+}
+
bool AccessibilityNodeObject::canSetFocusAttribute() const
{
Node* node = this->node();
@@ -1821,18 +1982,66 @@ bool AccessibilityNodeObject::canSetFocusAttribute() const
// NOTE: It would be more accurate to ask the document whether setFocusedElement() would
// do anything. For example, setFocusedElement() will do nothing if the current focused
// node will not relinquish the focus.
- if (!node)
+ if (!is<Element>(node))
return false;
- if (!node->isElementNode())
+ Element& element = downcast<Element>(*node);
+
+ if (element.isDisabledFormControl())
return false;
- Element* element = toElement(node);
+ return element.supportsFocus();
+}
- if (element->isDisabledFormControl())
+bool AccessibilityNodeObject::canSetValueAttribute() const
+{
+ Node* node = this->node();
+ if (!node)
return false;
- return element->supportsFocus();
+ // The host-language readonly attribute trumps aria-readonly.
+ if (is<HTMLTextAreaElement>(*node))
+ return !downcast<HTMLTextAreaElement>(*node).isReadOnly();
+ if (is<HTMLInputElement>(*node)) {
+ HTMLInputElement& input = downcast<HTMLInputElement>(*node);
+ if (input.isTextField())
+ return !input.isReadOnly();
+ }
+
+ String readOnly = ariaReadOnlyValue();
+ if (!readOnly.isEmpty())
+ return readOnly == "true" ? false : true;
+
+ if (isNonNativeTextControl())
+ return true;
+
+ if (isMeter())
+ return false;
+
+ if (isProgressIndicator() || isSlider())
+ return true;
+
+#if PLATFORM(GTK) || PLATFORM(EFL)
+ // In ATK, input types which support aria-readonly are treated as having a
+ // settable value if the user can modify the widget's value or its state.
+ if (supportsARIAReadOnly() || isRadioButton())
+ return true;
+#endif
+
+ if (isWebArea()) {
+ Document* document = this->document();
+ if (!document)
+ return false;
+
+ if (HTMLElement* body = document->bodyOrFrameset()) {
+ if (body->hasEditableStyle())
+ return true;
+ }
+
+ return document->hasEditableStyle();
+ }
+
+ return node->hasEditableStyle();
}
AccessibilityRole AccessibilityNodeObject::determineAriaRoleAttribute() const
@@ -1855,6 +2064,10 @@ AccessibilityRole AccessibilityNodeObject::determineAriaRoleAttribute() const
role = remapAriaRoleDueToParent(role);
+ // Presentational roles are invalidated by the presence of ARIA attributes.
+ if (role == PresentationalRole && supportsARIAAttributes())
+ role = UnknownRole;
+
if (role)
return role;
@@ -1894,22 +2107,12 @@ AccessibilityRole AccessibilityNodeObject::remapAriaRoleDueToParent(Accessibilit
return role;
}
-// If you call node->rendererIsEditable() since that will return true if an ancestor is editable.
-// This only returns true if this is the element that actually has the contentEditable attribute set.
-bool AccessibilityNodeObject::hasContentEditableAttributeSet() const
-{
- if (!hasAttribute(contenteditableAttr))
- return false;
- const AtomicString& contentEditableValue = getAttribute(contenteditableAttr);
- // Both "true" (case-insensitive) and the empty string count as true.
- return contentEditableValue.isEmpty() || equalIgnoringCase(contentEditableValue, "true");
-}
-
bool AccessibilityNodeObject::canSetSelectedAttribute() const
{
// Elements that can be selected
switch (roleValue()) {
case CellRole:
+ case GridCellRole:
case RadioButtonRole:
case RowHeaderRole:
case RowRole:
diff --git a/Source/WebCore/accessibility/AccessibilityNodeObject.h b/Source/WebCore/accessibility/AccessibilityNodeObject.h
index bc7088919..7b63eaa67 100644
--- a/Source/WebCore/accessibility/AccessibilityNodeObject.h
+++ b/Source/WebCore/accessibility/AccessibilityNodeObject.h
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -54,105 +54,112 @@ class RenderTextControl;
class RenderView;
class VisibleSelection;
class Widget;
-
+
+enum MouseButtonListenerResultFilter {
+ ExcludeBodyElement = 1,
+ IncludeBodyElement,
+};
+
class AccessibilityNodeObject : public AccessibilityObject {
-protected:
- explicit AccessibilityNodeObject(Node*);
public:
- static PassRefPtr<AccessibilityNodeObject> create(Node*);
+ static Ref<AccessibilityNodeObject> create(Node*);
virtual ~AccessibilityNodeObject();
- virtual void init();
-
- virtual bool isAccessibilityNodeObject() const { return true; }
-
- virtual bool canvasHasFallbackContent() const;
-
- virtual bool isAnchor() const;
- virtual bool isControl() const;
- virtual bool isFieldset() const;
- virtual bool isGroup() const;
- virtual bool isHeading() const;
- virtual bool isHovered() const;
- virtual bool isImage() const;
- virtual bool isImageButton() const;
- virtual bool isInputImage() const;
- virtual bool isLink() const;
- virtual bool isMenu() const;
- virtual bool isMenuBar() const;
- virtual bool isMenuButton() const;
- virtual bool isMenuItem() const;
- virtual bool isMenuRelated() const;
- virtual bool isMultiSelectable() const;
+ virtual void init() override;
+
+ virtual bool canvasHasFallbackContent() const override;
+
+ virtual bool isControl() const override;
+ virtual bool isFieldset() const override;
+ virtual bool isGroup() const override;
+ virtual bool isHeading() const override;
+ virtual bool isHovered() const override;
+ virtual bool isImage() const override;
+ virtual bool isImageButton() const override;
+ virtual bool isInputImage() const override;
+ virtual bool isLink() const override;
+ virtual bool isMenu() const override;
+ virtual bool isMenuBar() const override;
+ virtual bool isMenuButton() const override;
+ virtual bool isMenuItem() const override;
+ virtual bool isMenuRelated() const override;
+ virtual bool isMultiSelectable() const override;
virtual bool isNativeCheckboxOrRadio() const;
- virtual bool isNativeImage() const;
- virtual bool isNativeTextControl() const;
- virtual bool isPasswordField() const;
- virtual bool isProgressIndicator() const;
- virtual bool isSearchField() const;
- virtual bool isSlider() const;
-
- virtual bool isChecked() const;
- virtual bool isEnabled() const;
- virtual bool isIndeterminate() const;
- virtual bool isPressed() const;
- virtual bool isReadOnly() const;
- virtual bool isRequired() const;
- virtual bool supportsRequiredAttribute() const;
-
- virtual bool canSetSelectedAttribute() const OVERRIDE;
+ virtual bool isNativeImage() const override;
+ virtual bool isNativeTextControl() const override;
+ virtual bool isPasswordField() const override;
+ virtual AccessibilityObject* passwordFieldOrContainingPasswordField() override;
+ virtual bool isProgressIndicator() const override;
+ virtual bool isSearchField() const override;
+ virtual bool isSlider() const override;
+
+ virtual bool isChecked() const override;
+ virtual bool isEnabled() const override;
+ virtual bool isIndeterminate() const override;
+ virtual bool isPressed() const override;
+ virtual bool isRequired() const override;
+ virtual bool supportsRequiredAttribute() const override;
+
+ virtual bool canSetSelectedAttribute() const override;
void setNode(Node*);
- virtual Node* node() const { return m_node; }
- virtual Document* document() const;
-
- virtual bool canSetFocusAttribute() const;
- virtual int headingLevel() const;
-
- virtual String valueDescription() const;
- virtual float valueForRange() const;
- virtual float maxValueForRange() const;
- virtual float minValueForRange() const;
- virtual float stepValueForRange() const;
-
- virtual AccessibilityObject* selectedRadioButton();
- virtual AccessibilityObject* selectedTabItem();
- virtual AccessibilityButtonState checkboxOrRadioValue() const;
-
- virtual unsigned hierarchicalLevel() const;
- virtual String textUnderElement(AccessibilityTextUnderElementMode = TextUnderElementModeSkipIgnoredChildren) const;
- virtual String accessibilityDescription() const;
- virtual String helpText() const;
- virtual String title() const;
- virtual String text() const;
- virtual String stringValue() const;
- virtual void colorValue(int& r, int& g, int& b) const;
- virtual String ariaLabeledByAttribute() const;
-
- virtual Element* actionElement() const;
- Element* mouseButtonListener() const;
- virtual Element* anchorElement() const;
+ virtual Node* node() const override { return m_node; }
+ virtual Document* document() const override;
+
+ virtual bool canSetFocusAttribute() const override;
+ virtual int headingLevel() const override;
+
+ bool canSetValueAttribute() const override;
+
+ virtual String valueDescription() const override;
+ virtual float valueForRange() const override;
+ virtual float maxValueForRange() const override;
+ virtual float minValueForRange() const override;
+ virtual float stepValueForRange() const override;
+
+ virtual AccessibilityObject* selectedRadioButton() override;
+ virtual AccessibilityObject* selectedTabItem() override;
+ virtual AccessibilityButtonState checkboxOrRadioValue() const override;
+
+ virtual unsigned hierarchicalLevel() const override;
+ virtual String textUnderElement(AccessibilityTextUnderElementMode = AccessibilityTextUnderElementMode()) const override;
+ virtual String accessibilityDescriptionForChildren() const;
+ virtual String accessibilityDescription() const override;
+ virtual String helpText() const override;
+ virtual String title() const override;
+ virtual String text() const override;
+ virtual String stringValue() const override;
+ virtual void colorValue(int& r, int& g, int& b) const override;
+ virtual String ariaLabeledByAttribute() const override;
+ virtual bool hasAttributesRequiredForInclusion() const override final;
+ virtual void setIsExpanded(bool) override;
+
+ virtual Element* actionElement() const override;
+ Element* mouseButtonListener(MouseButtonListenerResultFilter = ExcludeBodyElement) const;
+ virtual Element* anchorElement() const override;
AccessibilityObject* menuForMenuButton() const;
virtual void changeValueByPercent(float percentChange);
- virtual AccessibilityObject* firstChild() const;
- virtual AccessibilityObject* lastChild() const;
- virtual AccessibilityObject* previousSibling() const;
- virtual AccessibilityObject* nextSibling() const;
- virtual AccessibilityObject* parentObject() const;
- virtual AccessibilityObject* parentObjectIfExists() const;
+ virtual AccessibilityObject* firstChild() const override;
+ virtual AccessibilityObject* lastChild() const override;
+ virtual AccessibilityObject* previousSibling() const override;
+ virtual AccessibilityObject* nextSibling() const override;
+ virtual AccessibilityObject* parentObject() const override;
+ virtual AccessibilityObject* parentObjectIfExists() const override;
- virtual void detach();
- virtual void childrenChanged();
- virtual void updateAccessibilityRole();
+ virtual void detach(AccessibilityDetachmentType, AXObjectCache*) override;
+ virtual void childrenChanged() override;
+ virtual void updateAccessibilityRole() override;
- virtual void increment();
- virtual void decrement();
+ virtual void increment() override;
+ virtual void decrement() override;
- virtual LayoutRect elementRect() const;
+ virtual LayoutRect elementRect() const override;
protected:
+ explicit AccessibilityNodeObject(Node*);
+
AccessibilityRole m_ariaRole;
bool m_childrenDirty;
mutable AccessibilityRole m_roleForMSAA;
@@ -160,19 +167,18 @@ protected:
bool m_initialized;
#endif
- virtual bool isDetached() const { return !m_node; }
+ virtual bool isDetached() const override { return !m_node; }
virtual AccessibilityRole determineAccessibilityRole();
- virtual void addChildren();
- virtual void addChild(AccessibilityObject*);
- virtual void insertChild(AccessibilityObject*, unsigned index);
+ virtual void addChildren() override;
+ virtual void addChild(AccessibilityObject*) override;
+ virtual void insertChild(AccessibilityObject*, unsigned index) override;
- virtual bool canHaveChildren() const;
- AccessibilityRole ariaRoleAttribute() const;
+ virtual bool canHaveChildren() const override;
+ virtual AccessibilityRole ariaRoleAttribute() const override;
AccessibilityRole determineAriaRoleAttribute() const;
AccessibilityRole remapAriaRoleDueToParent(AccessibilityRole) const;
- bool hasContentEditableAttributeSet() const;
- virtual bool isDescendantOfBarrenParent() const;
+ virtual bool isDescendantOfBarrenParent() const override;
void alterSliderValue(bool increase);
void changeValueByStep(bool increase);
// This returns true if it's focusable but it's not content editable and it's not a control or ARIA control.
@@ -181,42 +187,30 @@ protected:
String ariaAccessibilityDescription() const;
void ariaLabeledByElements(Vector<Element*>& elements) const;
String accessibilityDescriptionForElements(Vector<Element*> &elements) const;
- void elementsFromAttribute(Vector<Element*>& elements, const QualifiedName&) const;
- virtual LayoutRect boundingBoxRect() const;
- String ariaDescribedByAttribute() const;
+ virtual LayoutRect boundingBoxRect() const override;
+ virtual String ariaDescribedByAttribute() const override;
Element* menuElementForMenuButton() const;
Element* menuItemElementForMenu() const;
AccessibilityObject* menuButtonForMenu() const;
private:
- Node* m_node;
-
- virtual void accessibilityText(Vector<AccessibilityText>&);
- void titleElementText(Vector<AccessibilityText>&);
+ virtual bool isAccessibilityNodeObject() const override final { return true; }
+ virtual void accessibilityText(Vector<AccessibilityText>&) override;
+ virtual void titleElementText(Vector<AccessibilityText>&) const;
void alternativeText(Vector<AccessibilityText>&) const;
void visibleText(Vector<AccessibilityText>&) const;
void helpText(Vector<AccessibilityText>&) const;
String alternativeTextForWebArea() const;
void ariaLabeledByText(Vector<AccessibilityText>&) const;
- virtual bool computeAccessibilityIsIgnored() const;
-};
-
-inline AccessibilityNodeObject* toAccessibilityNodeObject(AccessibilityObject* object)
-{
- ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isAccessibilityNodeObject());
- return static_cast<AccessibilityNodeObject*>(object);
-}
+ virtual bool computeAccessibilityIsIgnored() const override;
+ bool usesAltTagForTextComputation() const;
-inline const AccessibilityNodeObject* toAccessibilityNodeObject(const AccessibilityObject* object)
-{
- ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isAccessibilityNodeObject());
- return static_cast<const AccessibilityNodeObject*>(object);
-}
-
-// This will catch anyone doing an unnecessary cast.
-void toAccessibilityNodeObject(const AccessibilityNodeObject*);
+ Node* m_node;
+};
} // namespace WebCore
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilityNodeObject, isAccessibilityNodeObject())
+
#endif // AccessibilityNodeObject_h
diff --git a/Source/WebCore/accessibility/AccessibilityObject.cpp b/Source/WebCore/accessibility/AccessibilityObject.cpp
index ff473e649..a5203012a 100644
--- a/Source/WebCore/accessibility/AccessibilityObject.cpp
+++ b/Source/WebCore/accessibility/AccessibilityObject.cpp
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -31,23 +31,34 @@
#include "AXObjectCache.h"
#include "AccessibilityRenderObject.h"
+#include "AccessibilityScrollView.h"
#include "AccessibilityTable.h"
+#include "DOMTokenList.h"
#include "Editor.h"
+#include "ElementIterator.h"
+#include "EventHandler.h"
#include "FloatRect.h"
#include "FocusController.h"
#include "Frame.h"
#include "FrameLoader.h"
#include "FrameSelection.h"
+#include "HTMLDetailsElement.h"
+#include "HTMLInputElement.h"
#include "HTMLNames.h"
+#include "HTMLParserIdioms.h"
+#include "HitTestResult.h"
#include "LocalizedStrings.h"
+#include "MainFrame.h"
+#include "MathMLNames.h"
#include "NodeList.h"
#include "NodeTraversal.h"
-#include "NotImplemented.h"
#include "Page.h"
#include "RenderImage.h"
+#include "RenderLayer.h"
#include "RenderListItem.h"
#include "RenderListMarker.h"
#include "RenderMenuList.h"
+#include "RenderText.h"
#include "RenderTextControl.h"
#include "RenderTheme.h"
#include "RenderView.h"
@@ -60,13 +71,12 @@
#include "UserGestureIndicator.h"
#include "VisibleUnits.h"
#include "htmlediting.h"
+#include <wtf/NeverDestroyed.h>
#include <wtf/StdLibExtras.h>
#include <wtf/text/StringBuilder.h>
#include <wtf/text/WTFString.h>
#include <wtf/unicode/CharacterNames.h>
-using namespace std;
-
namespace WebCore {
using namespace HTMLNames;
@@ -77,7 +87,7 @@ AccessibilityObject::AccessibilityObject()
, m_role(UnknownRole)
, m_lastKnownIsIgnoredValue(DefaultBehavior)
#if PLATFORM(GTK) || (PLATFORM(EFL) && HAVE(ACCESSIBILITY))
- , m_wrapper(0)
+ , m_wrapper(nullptr)
#endif
{
}
@@ -87,14 +97,18 @@ AccessibilityObject::~AccessibilityObject()
ASSERT(isDetached());
}
-void AccessibilityObject::detach()
+void AccessibilityObject::detach(AccessibilityDetachmentType detachmentType, AXObjectCache* cache)
{
+ // Menu close events need to notify the platform. No element is used in the notification because it's a destruction event.
+ if (detachmentType == ElementDestroyed && roleValue() == MenuRole && cache)
+ cache->postNotification(nullptr, &cache->document(), AXObjectCache::AXMenuClosed);
+
// Clear any children and call detachFromParent on them so that
// no children are left with dangling pointers to their parent.
clearChildren();
#if HAVE(ACCESSIBILITY)
- setWrapper(0);
+ setWrapper(nullptr);
#endif
}
@@ -199,6 +213,9 @@ bool AccessibilityObject::isAccessibilityObjectSearchMatchAtIndex(AccessibilityO
case MisspelledWordSearchKey:
return axObject->hasMisspelling();
+ case OutlineSearchKey:
+ return axObject->isTree();
+
case PlainTextSearchKey:
return axObject->hasPlainText();
@@ -218,11 +235,11 @@ bool AccessibilityObject::isAccessibilityObjectSearchMatchAtIndex(AccessibilityO
case TableSameLevelSearchKey:
return criteria->startObject
- && axObject->isAccessibilityTable()
- && axObject->tableLevel() == criteria->startObject->tableLevel();
+ && is<AccessibilityTable>(*axObject) && downcast<AccessibilityTable>(*axObject).isExposableThroughAccessibility()
+ && downcast<AccessibilityTable>(*axObject).tableLevel() == criteria->startObject->tableLevel();
case TableSearchKey:
- return axObject->isAccessibilityTable();
+ return is<AccessibilityTable>(*axObject) && downcast<AccessibilityTable>(*axObject).isExposableThroughAccessibility();
case TextFieldSearchKey:
return axObject->isTextControl();
@@ -262,7 +279,7 @@ bool AccessibilityObject::isAccessibilityTextSearchMatch(AccessibilityObject* ax
if (!axObject || !criteria)
return false;
- return axObject->accessibilityObjectContainsText(criteria->searchText);
+ return axObject->accessibilityObjectContainsText(&criteria->searchText);
}
bool AccessibilityObject::accessibilityObjectContainsText(String* text) const
@@ -275,17 +292,95 @@ bool AccessibilityObject::accessibilityObjectContainsText(String* text) const
|| stringValue().contains(*text, false);
}
+// ARIA marks elements as having their accessible name derive from either their contents, or their author provide name.
+bool AccessibilityObject::accessibleNameDerivesFromContent() const
+{
+ // First check for objects specifically identified by ARIA.
+ switch (ariaRoleAttribute()) {
+ case ApplicationAlertRole:
+ case ApplicationAlertDialogRole:
+ case ApplicationDialogRole:
+ case ApplicationLogRole:
+ case ApplicationMarqueeRole:
+ case ApplicationStatusRole:
+ case ApplicationTimerRole:
+ case ComboBoxRole:
+ case DefinitionRole:
+ case DocumentRole:
+ case DocumentArticleRole:
+ case DocumentMathRole:
+ case DocumentNoteRole:
+ case DocumentRegionRole:
+ case FormRole:
+ case GridRole:
+ case GroupRole:
+ case ImageRole:
+ case ListRole:
+ case ListBoxRole:
+ case LandmarkApplicationRole:
+ case LandmarkBannerRole:
+ case LandmarkComplementaryRole:
+ case LandmarkContentInfoRole:
+ case LandmarkNavigationRole:
+ case LandmarkMainRole:
+ case LandmarkSearchRole:
+ case MenuRole:
+ case MenuBarRole:
+ case ProgressIndicatorRole:
+ case RadioGroupRole:
+ case ScrollBarRole:
+ case SliderRole:
+ case SpinButtonRole:
+ case SplitterRole:
+ case TableRole:
+ case TabListRole:
+ case TabPanelRole:
+ case TextAreaRole:
+ case TextFieldRole:
+ case ToolbarRole:
+ case TreeGridRole:
+ case TreeRole:
+ return false;
+ default:
+ break;
+ }
+
+ // Now check for generically derived elements now that we know the element does not match a specific ARIA role.
+ switch (roleValue()) {
+ case SliderRole:
+ return false;
+ default:
+ break;
+ }
+
+ return true;
+}
+
+String AccessibilityObject::computedLabel()
+{
+ // This method is being called by WebKit inspector, which may happen at any time, so we need to update our backing store now.
+ // Also hold onto this object in case updateBackingStore deletes this node.
+ RefPtr<AccessibilityObject> protector(this);
+ updateBackingStore();
+ Vector<AccessibilityText> text;
+ accessibilityText(text);
+ if (text.size())
+ return text[0].text;
+ return String();
+}
+
bool AccessibilityObject::isBlockquote() const
{
- return node() && node()->hasTagName(blockquoteTag);
+ return roleValue() == BlockquoteRole;
}
bool AccessibilityObject::isTextControl() const
{
switch (roleValue()) {
+ case ComboBoxRole:
+ case SearchFieldRole:
case TextAreaRole:
case TextFieldRole:
- case ComboBoxRole:
return true;
default:
return false;
@@ -294,7 +389,12 @@ bool AccessibilityObject::isTextControl() const
bool AccessibilityObject::isARIATextControl() const
{
- return ariaRoleAttribute() == TextAreaRole || ariaRoleAttribute() == TextFieldRole;
+ return ariaRoleAttribute() == TextAreaRole || ariaRoleAttribute() == TextFieldRole || ariaRoleAttribute() == SearchFieldRole;
+}
+
+bool AccessibilityObject::isNonNativeTextControl() const
+{
+ return (isARIATextControl() || hasContentEditableAttributeSet()) && !isNativeTextControl();
}
bool AccessibilityObject::isLandmark() const
@@ -315,11 +415,7 @@ bool AccessibilityObject::hasMisspelling() const
if (!node())
return false;
- Document* document = node()->document();
- if (!document)
- return false;
-
- Frame* frame = document->frame();
+ Frame* frame = node()->document().frame();
if (!frame)
return false;
@@ -329,13 +425,11 @@ bool AccessibilityObject::hasMisspelling() const
if (!textChecker)
return false;
- const UChar* chars = stringValue().characters();
- int charsLength = stringValue().length();
bool isMisspelled = false;
if (unifiedTextCheckerEnabled(frame)) {
Vector<TextCheckingResult> results;
- checkTextOfParagraph(textChecker, chars, charsLength, TextCheckingTypeSpelling, results);
+ checkTextOfParagraph(*textChecker, stringValue(), TextCheckingTypeSpelling, results);
if (!results.isEmpty())
isMisspelled = true;
return isMisspelled;
@@ -343,7 +437,7 @@ bool AccessibilityObject::hasMisspelling() const
int misspellingLength = 0;
int misspellingLocation = -1;
- textChecker->checkSpellingOfString(chars, charsLength, &misspellingLocation, &misspellingLength);
+ textChecker->checkSpellingOfString(stringValue(), &misspellingLocation, &misspellingLength);
if (misspellingLength || misspellingLocation != -1)
isMisspelled = true;
@@ -370,26 +464,48 @@ AccessibilityObject* AccessibilityObject::parentObjectUnignored() const
return parent;
}
-AccessibilityObject* AccessibilityObject::firstAccessibleObjectFromNode(const Node* node)
+AccessibilityObject* AccessibilityObject::previousSiblingUnignored(int limit) const
{
- if (!node)
- return 0;
+ AccessibilityObject* previous;
+ ASSERT(limit >= 0);
+ for (previous = previousSibling(); previous && previous->accessibilityIsIgnored(); previous = previous->previousSibling()) {
+ limit--;
+ if (limit <= 0)
+ break;
+ }
+ return previous;
+}
- Document* document = node->document();
- if (!document)
- return 0;
+AccessibilityObject* AccessibilityObject::nextSiblingUnignored(int limit) const
+{
+ AccessibilityObject* next;
+ ASSERT(limit >= 0);
+ for (next = nextSibling(); next && next->accessibilityIsIgnored(); next = next->nextSibling()) {
+ limit--;
+ if (limit <= 0)
+ break;
+ }
+ return next;
+}
- AXObjectCache* cache = document->axObjectCache();
+AccessibilityObject* AccessibilityObject::firstAccessibleObjectFromNode(const Node* node)
+{
+ if (!node)
+ return nullptr;
+ AXObjectCache* cache = node->document().axObjectCache();
+ if (!cache)
+ return nullptr;
+
AccessibilityObject* accessibleObject = cache->getOrCreate(node->renderer());
while (accessibleObject && accessibleObject->accessibilityIsIgnored()) {
- node = NodeTraversal::next(node);
+ node = NodeTraversal::next(*node);
while (node && !node->renderer())
- node = NodeTraversal::nextSkippingChildren(node);
+ node = NodeTraversal::nextSkippingChildren(*node);
if (!node)
- return 0;
+ return nullptr;
accessibleObject = cache->getOrCreate(node->renderer());
}
@@ -402,14 +518,14 @@ static void appendAccessibilityObject(AccessibilityObject* object, Accessibility
// Find the next descendant of this attachment object so search can continue through frames.
if (object->isAttachment()) {
Widget* widget = object->widgetForAttachmentView();
- if (!widget || !widget->isFrameView())
+ if (!is<FrameView>(widget))
return;
- Document* doc = toFrameView(widget)->frame()->document();
- if (!doc || !doc->renderer())
+ Document* document = downcast<FrameView>(*widget).frame().document();
+ if (!document || !document->hasLivingRenderTree())
return;
- object = object->axObjectCache()->getOrCreate(doc);
+ object = object->axObjectCache()->getOrCreate(document);
}
if (object)
@@ -418,13 +534,9 @@ static void appendAccessibilityObject(AccessibilityObject* object, Accessibility
static void appendChildrenToArray(AccessibilityObject* object, bool isForward, AccessibilityObject* startObject, AccessibilityObject::AccessibilityChildrenVector& results)
{
- AccessibilityObject::AccessibilityChildrenVector searchChildren;
// A table's children includes elements whose own children are also the table's children (due to the way the Mac exposes tables).
// The rows from the table should be queried, since those are direct descendants of the table, and they contain content.
- if (object->isAccessibilityTable())
- searchChildren = toAccessibilityTable(object)->rows();
- else
- searchChildren = object->children();
+ const auto& searchChildren = is<AccessibilityTable>(*object) && downcast<AccessibilityTable>(*object).isExposableThroughAccessibility() ? downcast<AccessibilityTable>(*object).rows() : object->children();
size_t childrenSize = searchChildren.size();
@@ -470,7 +582,8 @@ void AccessibilityObject::findMatchingObjects(AccessibilitySearchCriteria* crite
if (!criteria)
return;
- axObjectCache()->startCachingComputedObjectAttributesUntilTreeMutates();
+ if (AXObjectCache* cache = axObjectCache())
+ cache->startCachingComputedObjectAttributesUntilTreeMutates();
// This search mechanism only searches the elements before/after the starting object.
// It does this by stepping up the parent chain and at each level doing a DFS.
@@ -482,10 +595,11 @@ void AccessibilityObject::findMatchingObjects(AccessibilitySearchCriteria* crite
bool isForward = criteria->searchDirection == SearchDirectionNext;
- // In the first iteration of the loop, it will examine the children of the start object for matches.
- // However, when going backwards, those children should not be considered, so the loop is skipped ahead.
- AccessibilityObject* previousObject = 0;
- if (!isForward) {
+ // The first iteration of the outer loop will examine the children of the start object for matches. However, when
+ // iterating backwards, the start object children should not be considered, so the loop is skipped ahead. We make an
+ // exception when no start object was specified because we want to search everything regardless of search direction.
+ AccessibilityObject* previousObject = nullptr;
+ if (!isForward && startObject != this) {
previousObject = startObject;
startObject = startObject->parentObjectUnignored();
}
@@ -496,7 +610,8 @@ void AccessibilityObject::findMatchingObjects(AccessibilitySearchCriteria* crite
// Only append the children after/before the previous element, so that the search does not check elements that are
// already behind/ahead of start element.
AccessibilityChildrenVector searchStack;
- appendChildrenToArray(startObject, isForward, previousObject, searchStack);
+ if (!criteria->immediateDescendantsOnly || startObject == this)
+ appendChildrenToArray(startObject, isForward, previousObject, searchStack);
// This now does a DFS at the current level of the parent.
while (!searchStack.isEmpty()) {
@@ -506,23 +621,214 @@ void AccessibilityObject::findMatchingObjects(AccessibilitySearchCriteria* crite
if (objectMatchesSearchCriteriaWithResultLimit(searchObject, criteria, results))
break;
- appendChildrenToArray(searchObject, isForward, 0, searchStack);
+ if (!criteria->immediateDescendantsOnly)
+ appendChildrenToArray(searchObject, isForward, 0, searchStack);
}
if (results.size() >= criteria->resultsLimit)
break;
// When moving backwards, the parent object needs to be checked, because technically it's "before" the starting element.
- if (!isForward && objectMatchesSearchCriteriaWithResultLimit(startObject, criteria, results))
+ if (!isForward && startObject != this && objectMatchesSearchCriteriaWithResultLimit(startObject, criteria, results))
break;
previousObject = startObject;
}
}
+// Returns the range that is fewer positions away from the reference range.
+// NOTE: The after range is expected to ACTUALLY be after the reference range and the before
+// range is expected to ACTUALLY be before. These are not checked for performance reasons.
+static RefPtr<Range> rangeClosestToRange(Range* referenceRange, RefPtr<Range>&& afterRange, RefPtr<Range>&& beforeRange)
+{
+ if (!referenceRange)
+ return nullptr;
+
+ // The treeScope for shadow nodes may not be the same scope as another element in a document.
+ // Comparisons may fail in that case, which are expected behavior and should not assert.
+ if (afterRange && ((afterRange->startPosition().anchorNode()->compareDocumentPosition(referenceRange->endPosition().anchorNode()) & Node::DOCUMENT_POSITION_DISCONNECTED) == Node::DOCUMENT_POSITION_DISCONNECTED))
+ return nullptr;
+ ASSERT(!afterRange || afterRange->startPosition() >= referenceRange->endPosition());
+
+ if (beforeRange && ((beforeRange->endPosition().anchorNode()->compareDocumentPosition(referenceRange->startPosition().anchorNode()) & Node::DOCUMENT_POSITION_DISCONNECTED) == Node::DOCUMENT_POSITION_DISCONNECTED))
+ return nullptr;
+ ASSERT(!beforeRange || beforeRange->endPosition() <= referenceRange->startPosition());
+
+ if (!afterRange && !beforeRange)
+ return nullptr;
+ if (afterRange && !beforeRange)
+ return afterRange;
+ if (!afterRange && beforeRange)
+ return beforeRange;
+
+ unsigned positionsToAfterRange = Position::positionCountBetweenPositions(afterRange->startPosition(), referenceRange->endPosition());
+ unsigned positionsToBeforeRange = Position::positionCountBetweenPositions(beforeRange->endPosition(), referenceRange->startPosition());
+
+ return positionsToAfterRange < positionsToBeforeRange ? afterRange : beforeRange;
+}
+
+RefPtr<Range> AccessibilityObject::rangeOfStringClosestToRangeInDirection(Range* referenceRange, AccessibilitySearchDirection searchDirection, Vector<String>& searchStrings) const
+{
+ Frame* frame = this->frame();
+ if (!frame)
+ return nullptr;
+
+ if (!referenceRange)
+ return nullptr;
+
+ bool isBackwardSearch = searchDirection == SearchDirectionPrevious;
+ FindOptions findOptions = AtWordStarts | AtWordEnds | CaseInsensitive | StartInSelection;
+ if (isBackwardSearch)
+ findOptions |= Backwards;
+
+ RefPtr<Range> closestStringRange = nullptr;
+ for (const auto& searchString : searchStrings) {
+ if (RefPtr<Range> searchStringRange = frame->editor().rangeOfString(searchString, referenceRange, findOptions)) {
+ if (!closestStringRange)
+ closestStringRange = searchStringRange;
+ else {
+ // If searching backward, use the trailing range edges to correctly determine which
+ // range is closest. Similarly, if searching forward, use the leading range edges.
+ Position closestStringPosition = isBackwardSearch ? closestStringRange->endPosition() : closestStringRange->startPosition();
+ Position searchStringPosition = isBackwardSearch ? searchStringRange->endPosition() : searchStringRange->startPosition();
+
+ int closestPositionOffset = closestStringPosition.computeOffsetInContainerNode();
+ int searchPositionOffset = searchStringPosition.computeOffsetInContainerNode();
+ Node* closestContainerNode = closestStringPosition.containerNode();
+ Node* searchContainerNode = searchStringPosition.containerNode();
+
+ short result = Range::compareBoundaryPoints(closestContainerNode, closestPositionOffset, searchContainerNode, searchPositionOffset, ASSERT_NO_EXCEPTION);
+ if ((!isBackwardSearch && result > 0) || (isBackwardSearch && result < 0))
+ closestStringRange = searchStringRange;
+ }
+ }
+ }
+ return closestStringRange;
+}
+
+// Returns the range of the entire document if there is no selection.
+RefPtr<Range> AccessibilityObject::selectionRange() const
+{
+ Frame* frame = this->frame();
+ if (!frame)
+ return nullptr;
+
+ const VisibleSelection& selection = frame->selection().selection();
+ if (!selection.isNone())
+ return selection.firstRange();
+
+ return Range::create(*frame->document());
+}
+
+RefPtr<Range> AccessibilityObject::elementRange() const
+{
+ return AXObjectCache::rangeForNodeContents(node());
+}
+
+String AccessibilityObject::selectText(AccessibilitySelectTextCriteria* criteria)
+{
+ ASSERT(criteria);
+
+ if (!criteria)
+ return String();
+
+ Frame* frame = this->frame();
+ if (!frame)
+ return String();
+
+ AccessibilitySelectTextActivity& activity = criteria->activity;
+ AccessibilitySelectTextAmbiguityResolution& ambiguityResolution = criteria->ambiguityResolution;
+ String& replacementString = criteria->replacementString;
+ Vector<String>& searchStrings = criteria->searchStrings;
+
+ RefPtr<Range> selectedStringRange = selectionRange();
+ // When starting our search again, make this a zero length range so that search forwards will find this selected range if its appropriate.
+ selectedStringRange->setEnd(&selectedStringRange->startContainer(), selectedStringRange->startOffset());
+
+ RefPtr<Range> closestAfterStringRange = nullptr;
+ RefPtr<Range> closestBeforeStringRange = nullptr;
+ // Search forward if necessary.
+ if (ambiguityResolution == ClosestAfterSelectionAmbiguityResolution || ambiguityResolution == ClosestToSelectionAmbiguityResolution)
+ closestAfterStringRange = rangeOfStringClosestToRangeInDirection(selectedStringRange.get(), SearchDirectionNext, searchStrings);
+ // Search backward if necessary.
+ if (ambiguityResolution == ClosestBeforeSelectionAmbiguityResolution || ambiguityResolution == ClosestToSelectionAmbiguityResolution)
+ closestBeforeStringRange = rangeOfStringClosestToRangeInDirection(selectedStringRange.get(), SearchDirectionPrevious, searchStrings);
+
+ // Determine which candidate is closest to the selection and perform the activity.
+ if (RefPtr<Range> closestStringRange = rangeClosestToRange(selectedStringRange.get(), WTFMove(closestAfterStringRange), WTFMove(closestBeforeStringRange))) {
+ // If the search started within a text control, ensure that the result is inside that element.
+ if (element() && element()->isTextFormControl()) {
+ if (!closestStringRange->startContainer().isDescendantOrShadowDescendantOf(element()) || !closestStringRange->endContainer().isDescendantOrShadowDescendantOf(element()))
+ return String();
+ }
+
+ String closestString = closestStringRange->text();
+ bool replaceSelection = false;
+ if (frame->selection().setSelectedRange(closestStringRange.get(), DOWNSTREAM, true)) {
+ switch (activity) {
+ case FindAndCapitalize:
+ replacementString = closestString;
+ makeCapitalized(&replacementString, 0);
+ replaceSelection = true;
+ break;
+ case FindAndUppercase:
+ replacementString = closestString.convertToUppercaseWithoutLocale(); // FIXME: Needs locale to work correctly.
+ replaceSelection = true;
+ break;
+ case FindAndLowercase:
+ replacementString = closestString.convertToLowercaseWithoutLocale(); // FIXME: Needs locale to work correctly.
+ replaceSelection = true;
+ break;
+ case FindAndReplaceActivity: {
+ replaceSelection = true;
+ // When applying find and replace activities, we want to match the capitalization of the replaced text,
+ // (unless we're replacing with an abbreviation.)
+ if (closestString.length() > 0 && replacementString.length() > 2 && replacementString != replacementString.convertToUppercaseWithoutLocale()) {
+ if (closestString[0] == u_toupper(closestString[0]))
+ makeCapitalized(&replacementString, 0);
+ else
+ replacementString = replacementString.convertToLowercaseWithoutLocale(); // FIXME: Needs locale to work correctly.
+ }
+ break;
+ }
+ case FindAndSelectActivity:
+ break;
+ }
+
+ // A bit obvious, but worth noting the API contract for this method is that we should
+ // return the replacement string when replacing, but the selected string if not.
+ if (replaceSelection) {
+ frame->editor().replaceSelectionWithText(replacementString, true, true);
+ return replacementString;
+ }
+
+ return closestString;
+ }
+ }
+
+ return String();
+}
+
+bool AccessibilityObject::hasAttributesRequiredForInclusion() const
+{
+ // These checks are simplified in the interest of execution speed.
+ if (!getAttribute(aria_helpAttr).isEmpty()
+ || !getAttribute(aria_describedbyAttr).isEmpty()
+ || !getAttribute(altAttr).isEmpty()
+ || !getAttribute(titleAttr).isEmpty())
+ return true;
+
+#if ENABLE(MATHML)
+ if (!getAttribute(MathMLNames::alttextAttr).isEmpty())
+ return true;
+#endif
+
+ return false;
+}
+
bool AccessibilityObject::isARIAInput(AccessibilityRole ariaRole)
{
- return ariaRole == RadioButtonRole || ariaRole == CheckBoxRole || ariaRole == TextFieldRole;
+ return ariaRole == RadioButtonRole || ariaRole == CheckBoxRole || ariaRole == TextFieldRole || ariaRole == SwitchRole || ariaRole == SearchFieldRole;
}
bool AccessibilityObject::isARIAControl(AccessibilityRole ariaRole)
@@ -544,6 +850,16 @@ bool AccessibilityObject::isRangeControl() const
}
}
+bool AccessibilityObject::isMeter() const
+{
+#if ENABLE(METER_ELEMENT)
+ RenderObject* renderer = this->renderer();
+ return renderer && renderer->isMeter();
+#else
+ return false;
+#endif
+}
+
IntPoint AccessibilityObject::clickPoint()
{
LayoutRect rect = elementRect();
@@ -556,35 +872,105 @@ IntRect AccessibilityObject::boundingBoxForQuads(RenderObject* obj, const Vector
if (!obj)
return IntRect();
- size_t count = quads.size();
- if (!count)
- return IntRect();
-
- IntRect result;
- for (size_t i = 0; i < count; ++i) {
- IntRect r = quads[i].enclosingBoundingBox();
+ FloatRect result;
+ for (const auto& quad : quads) {
+ FloatRect r = quad.enclosingBoundingBox();
if (!r.isEmpty()) {
- if (obj->style()->hasAppearance())
- obj->theme()->adjustRepaintRect(obj, r);
+ if (obj->style().hasAppearance())
+ obj->theme().adjustRepaintRect(*obj, r);
result.unite(r);
}
}
- return result;
+ return snappedIntRect(LayoutRect(result));
}
-bool AccessibilityObject::press() const
+bool AccessibilityObject::press()
{
+ // The presence of the actionElement will confirm whether we should even attempt a press.
Element* actionElem = actionElement();
if (!actionElem)
return false;
- if (Frame* f = actionElem->document()->frame())
- f->loader()->resetMultipleFormSubmissionProtection();
+ if (Frame* f = actionElem->document().frame())
+ f->loader().resetMultipleFormSubmissionProtection();
+
+ // Hit test at this location to determine if there is a sub-node element that should act
+ // as the target of the action.
+ Element* hitTestElement = nullptr;
+ Document* document = this->document();
+ if (document) {
+ HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::AccessibilityHitTest);
+ HitTestResult hitTestResult(clickPoint());
+ document->renderView()->hitTest(request, hitTestResult);
+ if (hitTestResult.innerNode()) {
+ Node* innerNode = hitTestResult.innerNode()->deprecatedShadowAncestorNode();
+ if (is<Element>(*innerNode))
+ hitTestElement = downcast<Element>(innerNode);
+ else if (innerNode)
+ hitTestElement = innerNode->parentElement();
+ }
+ }
+
+
+ // Prefer the actionElement instead of this node, if the actionElement is inside this node.
+ Element* pressElement = this->element();
+ if (!pressElement || actionElem->isDescendantOf(pressElement))
+ pressElement = actionElem;
+
+ // Prefer the hit test element, if it is inside the target element.
+ if (hitTestElement && hitTestElement->isDescendantOf(pressElement))
+ pressElement = hitTestElement;
+
+ UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture, document);
+
+ bool dispatchedTouchEvent = dispatchTouchEvent();
+ if (!dispatchedTouchEvent)
+ pressElement->accessKeyAction(true);
- UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture);
- actionElem->accessKeyAction(true);
return true;
}
+bool AccessibilityObject::dispatchTouchEvent()
+{
+ bool handled = false;
+#if ENABLE(IOS_TOUCH_EVENTS)
+ MainFrame* frame = mainFrame();
+ if (!frame)
+ return false;
+
+ frame->eventHandler().dispatchSimulatedTouchEvent(clickPoint());
+#endif
+ return handled;
+}
+
+Frame* AccessibilityObject::frame() const
+{
+ Node* node = this->node();
+ if (!node)
+ return nullptr;
+
+ return node->document().frame();
+}
+
+MainFrame* AccessibilityObject::mainFrame() const
+{
+ Document* document = topDocument();
+ if (!document)
+ return nullptr;
+
+ Frame* frame = document->frame();
+ if (!frame)
+ return nullptr;
+
+ return &frame->mainFrame();
+}
+
+Document* AccessibilityObject::topDocument() const
+{
+ if (!document())
+ return nullptr;
+ return &document()->topDocument();
+}
+
String AccessibilityObject::language() const
{
const AtomicString& lang = getAttribute(langAttr);
@@ -609,6 +995,10 @@ VisiblePositionRange AccessibilityObject::visiblePositionRangeForUnorderedPositi
if (visiblePos1.isNull() || visiblePos2.isNull())
return VisiblePositionRange();
+ // If there's no common tree scope between positions, return early.
+ if (!commonTreeScope(visiblePos1.deepEquivalent().deprecatedNode(), visiblePos2.deepEquivalent().deprecatedNode()))
+ return VisiblePositionRange();
+
VisiblePosition startPos;
VisiblePosition endPos;
bool alreadyInOrder;
@@ -750,16 +1140,16 @@ static VisiblePosition startOfStyleRange(const VisiblePosition& visiblePos)
{
RenderObject* renderer = visiblePos.deepEquivalent().deprecatedNode()->renderer();
RenderObject* startRenderer = renderer;
- RenderStyle* style = renderer->style();
+ RenderStyle* style = &renderer->style();
// traverse backward by renderer to look for style change
for (RenderObject* r = renderer->previousInPreOrder(); r; r = r->previousInPreOrder()) {
// skip non-leaf nodes
- if (r->firstChild())
+ if (r->firstChildSlow())
continue;
// stop at style change
- if (r->style() != style)
+ if (&r->style() != style)
break;
// remember match
@@ -773,16 +1163,16 @@ static VisiblePosition endOfStyleRange(const VisiblePosition& visiblePos)
{
RenderObject* renderer = visiblePos.deepEquivalent().deprecatedNode()->renderer();
RenderObject* endRenderer = renderer;
- RenderStyle* style = renderer->style();
+ const RenderStyle& style = renderer->style();
// traverse forward by renderer to look for style change
for (RenderObject* r = renderer->nextInPreOrder(); r; r = r->nextInPreOrder()) {
// skip non-leaf nodes
- if (r->firstChild())
+ if (r->firstChildSlow())
continue;
// stop at style change
- if (r->style() != style)
+ if (&r->style() != &style)
break;
// remember match
@@ -813,15 +1203,22 @@ VisiblePositionRange AccessibilityObject::visiblePositionRangeForRange(const Pla
return VisiblePositionRange(startPosition, endPosition);
}
-static bool replacedNodeNeedsCharacter(Node* replacedNode)
+VisiblePositionRange AccessibilityObject::lineRangeForPosition(const VisiblePosition& visiblePosition) const
+{
+ VisiblePosition startPosition = startOfLine(visiblePosition);
+ VisiblePosition endPosition = endOfLine(visiblePosition);
+ return VisiblePositionRange(startPosition, endPosition);
+}
+
+bool AccessibilityObject::replacedNodeNeedsCharacter(Node* replacedNode)
{
// we should always be given a rendered node and a replaced node, but be safe
// replaced nodes are either attachments (widgets) or images
- if (!replacedNode || !replacedNode->renderer() || !replacedNode->renderer()->isReplaced() || replacedNode->isTextNode())
+ if (!replacedNode || !isRendererReplacedElement(replacedNode->renderer()) || replacedNode->isTextNode())
return false;
// create an AX object, but skip it if it is not supposed to be seen
- AccessibilityObject* object = replacedNode->renderer()->document()->axObjectCache()->getOrCreate(replacedNode);
+ AccessibilityObject* object = replacedNode->renderer()->document().axObjectCache()->getOrCreate(replacedNode);
if (object->accessibilityIsIgnored())
return false;
@@ -833,12 +1230,24 @@ static RenderListItem* renderListItemContainerForNode(Node* node)
{
for (; node; node = node->parentNode()) {
RenderBoxModelObject* renderer = node->renderBoxModelObject();
- if (renderer && renderer->isListItem())
- return toRenderListItem(renderer);
+ if (is<RenderListItem>(renderer))
+ return downcast<RenderListItem>(renderer);
}
- return 0;
+ return nullptr;
}
+
+static String listMarkerTextForNode(Node* node)
+{
+ RenderListItem* listItem = renderListItemContainerForNode(node);
+ if (!listItem)
+ return String();
+ // If this is in a list item, we need to manually add the text for the list marker
+ // because a RenderListMarker does not have a Node equivalent and thus does not appear
+ // when iterating text.
+ return listItem->markerTextWithSuffix();
+}
+
// Returns the text associated with a list marker if this node is contained within a list item.
String AccessibilityObject::listMarkerTextForNodeAndPosition(Node* node, const VisiblePosition& visiblePositionStart) const
{
@@ -846,16 +1255,38 @@ String AccessibilityObject::listMarkerTextForNodeAndPosition(Node* node, const V
if (!isStartOfLine(visiblePositionStart))
return String();
- RenderListItem* listItem = renderListItemContainerForNode(node);
- if (!listItem)
- return String();
-
- // If this is in a list item, we need to manually add the text for the list marker
- // because a RenderListMarker does not have a Node equivalent and thus does not appear
- // when iterating text.
- return listItem->markerTextWithSuffix();
+ return listMarkerTextForNode(node);
}
+
+String AccessibilityObject::stringForRange(RefPtr<Range> range) const
+{
+ if (!range)
+ return String();
+
+ TextIterator it(range.get());
+ if (it.atEnd())
+ return String();
+
+ StringBuilder builder;
+ for (; !it.atEnd(); it.advance()) {
+ // non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX)
+ if (it.text().length()) {
+ // Add a textual representation for list marker text.
+ builder.append(listMarkerTextForNode(it.node()));
+ it.appendTextToStringBuilder(builder);
+ } else {
+ // locate the node and starting offset for this replaced range
+ Node& node = it.range()->startContainer();
+ ASSERT(&node == &it.range()->endContainer());
+ int offset = it.range()->startOffset();
+ if (replacedNodeNeedsCharacter(node.traverseToChildAt(offset)))
+ builder.append(objectReplacementCharacter);
+ }
+ }
+ return builder.toString();
+}
+
String AccessibilityObject::stringForVisiblePositionRange(const VisiblePositionRange& visiblePositionRange) const
{
if (visiblePositionRange.isNull())
@@ -865,21 +1296,16 @@ String AccessibilityObject::stringForVisiblePositionRange(const VisiblePositionR
RefPtr<Range> range = makeRange(visiblePositionRange.start, visiblePositionRange.end);
for (TextIterator it(range.get()); !it.atEnd(); it.advance()) {
// non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX)
- if (it.length()) {
- // Add a textual representation for list marker text
- String listMarkerText = listMarkerTextForNodeAndPosition(it.node(), visiblePositionRange.start);
- if (!listMarkerText.isEmpty())
- builder.append(listMarkerText);
-
+ if (it.text().length()) {
+ // Add a textual representation for list marker text.
+ builder.append(listMarkerTextForNodeAndPosition(it.node(), visiblePositionRange.start));
it.appendTextToStringBuilder(builder);
} else {
// locate the node and starting offset for this replaced range
- int exception = 0;
- Node* node = it.range()->startContainer(exception);
- ASSERT(node == it.range()->endContainer(exception));
- int offset = it.range()->startOffset(exception);
-
- if (replacedNodeNeedsCharacter(node->childNode(offset)))
+ Node& node = it.range()->startContainer();
+ ASSERT(&node == &it.range()->endContainer());
+ int offset = it.range()->startOffset();
+ if (replacedNodeNeedsCharacter(node.traverseToChildAt(offset)))
builder.append(objectReplacementCharacter);
}
}
@@ -897,23 +1323,54 @@ int AccessibilityObject::lengthForVisiblePositionRange(const VisiblePositionRang
RefPtr<Range> range = makeRange(visiblePositionRange.start, visiblePositionRange.end);
for (TextIterator it(range.get()); !it.atEnd(); it.advance()) {
// non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX)
- if (it.length())
- length += it.length();
+ if (it.text().length())
+ length += it.text().length();
else {
// locate the node and starting offset for this replaced range
- int exception = 0;
- Node* node = it.range()->startContainer(exception);
- ASSERT(node == it.range()->endContainer(exception));
- int offset = it.range()->startOffset(exception);
+ Node& node = it.range()->startContainer();
+ ASSERT(&node == &it.range()->endContainer());
+ int offset = it.range()->startOffset();
- if (replacedNodeNeedsCharacter(node->childNode(offset)))
- length++;
+ if (replacedNodeNeedsCharacter(node.traverseToChildAt(offset)))
+ ++length;
}
}
return length;
}
+VisiblePosition AccessibilityObject::visiblePositionForBounds(const IntRect& rect, AccessibilityVisiblePositionForBounds visiblePositionForBounds) const
+{
+ if (rect.isEmpty())
+ return VisiblePosition();
+
+ MainFrame* mainFrame = this->mainFrame();
+ if (!mainFrame)
+ return VisiblePosition();
+
+ // FIXME: Add support for right-to-left languages.
+ IntPoint corner = (visiblePositionForBounds == FirstVisiblePositionForBounds) ? rect.minXMinYCorner() : rect.maxXMaxYCorner();
+ VisiblePosition position = mainFrame->visiblePositionForPoint(corner);
+
+ if (rect.contains(position.absoluteCaretBounds().center()))
+ return position;
+
+ // If the initial position is located outside the bounds adjust it incrementally as needed.
+ VisiblePosition nextPosition = position.next();
+ VisiblePosition previousPosition = position.previous();
+ while (nextPosition.isNotNull() || previousPosition.isNotNull()) {
+ if (rect.contains(nextPosition.absoluteCaretBounds().center()))
+ return nextPosition;
+ if (rect.contains(previousPosition.absoluteCaretBounds().center()))
+ return previousPosition;
+
+ nextPosition = nextPosition.next();
+ previousPosition = previousPosition.previous();
+ }
+
+ return VisiblePosition();
+}
+
VisiblePosition AccessibilityObject::nextWordEnd(const VisiblePosition& visiblePos) const
{
if (visiblePos.isNull())
@@ -1065,15 +1522,66 @@ VisiblePosition AccessibilityObject::previousParagraphStartPosition(const Visibl
AccessibilityObject* AccessibilityObject::accessibilityObjectForPosition(const VisiblePosition& visiblePos) const
{
if (visiblePos.isNull())
- return 0;
+ return nullptr;
RenderObject* obj = visiblePos.deepEquivalent().deprecatedNode()->renderer();
if (!obj)
- return 0;
+ return nullptr;
- return obj->document()->axObjectCache()->getOrCreate(obj);
+ return obj->document().axObjectCache()->getOrCreate(obj);
+}
+
+// If you call node->hasEditableStyle() since that will return true if an ancestor is editable.
+// This only returns true if this is the element that actually has the contentEditable attribute set.
+bool AccessibilityObject::hasContentEditableAttributeSet() const
+{
+ return contentEditableAttributeIsEnabled(element());
+}
+
+bool AccessibilityObject::supportsARIAReadOnly() const
+{
+ AccessibilityRole role = roleValue();
+
+ return role == CheckBoxRole
+ || role == ColumnHeaderRole
+ || role == ComboBoxRole
+ || role == GridRole
+ || role == GridCellRole
+ || role == ListBoxRole
+ || role == MenuItemCheckboxRole
+ || role == MenuItemRadioRole
+ || role == RadioGroupRole
+ || role == RowHeaderRole
+ || role == SearchFieldRole
+ || role == SliderRole
+ || role == SpinButtonRole
+ || role == SwitchRole
+ || role == TextFieldRole
+ || role == TreeGridRole
+ || isPasswordField();
}
+String AccessibilityObject::ariaReadOnlyValue() const
+{
+ if (!hasAttribute(aria_readonlyAttr))
+ return ariaRoleAttribute() != UnknownRole && supportsARIAReadOnly() ? "false" : String();
+
+ return getAttribute(aria_readonlyAttr).string().convertToASCIILowercase();
+}
+
+bool AccessibilityObject::contentEditableAttributeIsEnabled(Element* element)
+{
+ if (!element)
+ return false;
+
+ const AtomicString& contentEditableValue = element->fastGetAttribute(contenteditableAttr);
+ if (contentEditableValue.isNull())
+ return false;
+
+ // Both "true" (case-insensitive) and the empty string count as true.
+ return contentEditableValue.isEmpty() || equalLettersIgnoringASCIICase(contentEditableValue, "true");
+}
+
#if HAVE(ACCESSIBILITY)
int AccessibilityObject::lineForPosition(const VisiblePosition& visiblePos) const
{
@@ -1148,25 +1656,41 @@ unsigned AccessibilityObject::doAXLineForIndex(unsigned index)
void AccessibilityObject::updateBackingStore()
{
// Updating the layout may delete this object.
- if (Document* document = this->document())
- document->updateLayoutIgnorePendingStylesheets();
+ RefPtr<AccessibilityObject> protector(this);
+
+ if (Document* document = this->document()) {
+ if (!document->view()->isInRenderTreeLayout())
+ document->updateLayoutIgnorePendingStylesheets();
+ }
+
+ updateChildrenIfNecessary();
}
#endif
-
+
+ScrollView* AccessibilityObject::scrollViewAncestor() const
+{
+ for (const AccessibilityObject* scrollParent = this; scrollParent; scrollParent = scrollParent->parentObject()) {
+ if (is<AccessibilityScrollView>(*scrollParent))
+ return downcast<AccessibilityScrollView>(*scrollParent).scrollView();
+ }
+
+ return nullptr;
+}
+
Document* AccessibilityObject::document() const
{
FrameView* frameView = documentFrameView();
if (!frameView)
- return 0;
+ return nullptr;
- return frameView->frame()->document();
+ return frameView->frame().document();
}
Page* AccessibilityObject::page() const
{
Document* document = this->document();
if (!document)
- return 0;
+ return nullptr;
return document->page();
}
@@ -1177,15 +1701,16 @@ FrameView* AccessibilityObject::documentFrameView() const
object = object->parentObject();
if (!object)
- return 0;
+ return nullptr;
return object->documentFrameView();
}
#if HAVE(ACCESSIBILITY)
-const AccessibilityObject::AccessibilityChildrenVector& AccessibilityObject::children()
+const AccessibilityObject::AccessibilityChildrenVector& AccessibilityObject::children(bool updateChildrenIfNeeded)
{
- updateChildrenIfNecessary();
+ if (updateChildrenIfNeeded)
+ updateChildrenIfNecessary();
return m_children;
}
@@ -1193,16 +1718,18 @@ const AccessibilityObject::AccessibilityChildrenVector& AccessibilityObject::chi
void AccessibilityObject::updateChildrenIfNecessary()
{
- if (!hasChildren())
- addChildren();
+ if (!hasChildren()) {
+ // Enable the cache in case we end up adding a lot of children, we don't want to recompute axIsIgnored each time.
+ AXAttributeCacheEnabler enableCache(axObjectCache());
+ addChildren();
+ }
}
void AccessibilityObject::clearChildren()
{
// Some objects have weak pointers to their parents and those associations need to be detached.
- size_t length = m_children.size();
- for (size_t i = 0; i < length; i++)
- m_children[i]->detachFromParent();
+ for (const auto& child : m_children)
+ child->detachFromParent();
m_children.clear();
m_haveChildren = false;
@@ -1212,58 +1739,62 @@ AccessibilityObject* AccessibilityObject::anchorElementForNode(Node* node)
{
RenderObject* obj = node->renderer();
if (!obj)
- return 0;
+ return nullptr;
- RefPtr<AccessibilityObject> axObj = obj->document()->axObjectCache()->getOrCreate(obj);
+ RefPtr<AccessibilityObject> axObj = obj->document().axObjectCache()->getOrCreate(obj);
Element* anchor = axObj->anchorElement();
if (!anchor)
- return 0;
+ return nullptr;
RenderObject* anchorRenderer = anchor->renderer();
if (!anchorRenderer)
- return 0;
+ return nullptr;
- return anchorRenderer->document()->axObjectCache()->getOrCreate(anchorRenderer);
+ return anchorRenderer->document().axObjectCache()->getOrCreate(anchorRenderer);
}
+
+AccessibilityObject* AccessibilityObject::headingElementForNode(Node* node)
+{
+ if (!node)
+ return nullptr;
+
+ RenderObject* renderObject = node->renderer();
+ if (!renderObject)
+ return nullptr;
+ AccessibilityObject* axObject = renderObject->document().axObjectCache()->getOrCreate(renderObject);
+ for (; axObject && axObject->roleValue() != HeadingRole; axObject = axObject->parentObject()) { }
+
+ return axObject;
+}
+
void AccessibilityObject::ariaTreeRows(AccessibilityChildrenVector& result)
{
- AccessibilityChildrenVector axChildren = children();
- unsigned count = axChildren.size();
- for (unsigned k = 0; k < count; ++k) {
- AccessibilityObject* obj = axChildren[k].get();
-
+ for (const auto& child : children()) {
// Add tree items as the rows.
- if (obj->roleValue() == TreeItemRole)
- result.append(obj);
+ if (child->roleValue() == TreeItemRole)
+ result.append(child);
// Now see if this item also has rows hiding inside of it.
- obj->ariaTreeRows(result);
+ child->ariaTreeRows(result);
}
}
void AccessibilityObject::ariaTreeItemContent(AccessibilityChildrenVector& result)
{
// The ARIA tree item content are the item that are not other tree items or their containing groups.
- AccessibilityChildrenVector axChildren = children();
- unsigned count = axChildren.size();
- for (unsigned k = 0; k < count; ++k) {
- AccessibilityObject* obj = axChildren[k].get();
- AccessibilityRole role = obj->roleValue();
+ for (const auto& child : children()) {
+ AccessibilityRole role = child->roleValue();
if (role == TreeItemRole || role == GroupRole)
continue;
- result.append(obj);
+ result.append(child);
}
}
void AccessibilityObject::ariaTreeItemDisclosedRows(AccessibilityChildrenVector& result)
{
- AccessibilityChildrenVector axChildren = children();
- unsigned count = axChildren.size();
- for (unsigned k = 0; k < count; ++k) {
- AccessibilityObject* obj = axChildren[k].get();
-
+ for (const auto& obj : children()) {
// Add tree items as the rows.
if (obj->roleValue() == TreeItemRole)
result.append(obj);
@@ -1272,21 +1803,38 @@ void AccessibilityObject::ariaTreeItemDisclosedRows(AccessibilityChildrenVector&
obj->ariaTreeRows(result);
}
}
-
+
+const String AccessibilityObject::defaultLiveRegionStatusForRole(AccessibilityRole role)
+{
+ switch (role) {
+ case ApplicationAlertDialogRole:
+ case ApplicationAlertRole:
+ return ASCIILiteral("assertive");
+ case ApplicationLogRole:
+ case ApplicationStatusRole:
+ return ASCIILiteral("polite");
+ case ApplicationTimerRole:
+ case ApplicationMarqueeRole:
+ return ASCIILiteral("off");
+ default:
+ return nullAtom;
+ }
+}
+
#if HAVE(ACCESSIBILITY)
const String& AccessibilityObject::actionVerb() const
{
+#if !PLATFORM(IOS)
// FIXME: Need to add verbs for select elements.
- DEFINE_STATIC_LOCAL(const String, buttonAction, (AXButtonActionVerb()));
- DEFINE_STATIC_LOCAL(const String, textFieldAction, (AXTextFieldActionVerb()));
- DEFINE_STATIC_LOCAL(const String, radioButtonAction, (AXRadioButtonActionVerb()));
- DEFINE_STATIC_LOCAL(const String, checkedCheckBoxAction, (AXCheckedCheckBoxActionVerb()));
- DEFINE_STATIC_LOCAL(const String, uncheckedCheckBoxAction, (AXUncheckedCheckBoxActionVerb()));
- DEFINE_STATIC_LOCAL(const String, linkAction, (AXLinkActionVerb()));
- DEFINE_STATIC_LOCAL(const String, menuListAction, (AXMenuListActionVerb()));
- DEFINE_STATIC_LOCAL(const String, menuListPopupAction, (AXMenuListPopupActionVerb()));
- DEFINE_STATIC_LOCAL(const String, listItemAction, (AXListItemActionVerb()));
- DEFINE_STATIC_LOCAL(const String, noAction, ());
+ static NeverDestroyed<const String> buttonAction(AXButtonActionVerb());
+ static NeverDestroyed<const String> textFieldAction(AXTextFieldActionVerb());
+ static NeverDestroyed<const String> radioButtonAction(AXRadioButtonActionVerb());
+ static NeverDestroyed<const String> checkedCheckBoxAction(AXCheckedCheckBoxActionVerb());
+ static NeverDestroyed<const String> uncheckedCheckBoxAction(AXUncheckedCheckBoxActionVerb());
+ static NeverDestroyed<const String> linkAction(AXLinkActionVerb());
+ static NeverDestroyed<const String> menuListAction(AXMenuListActionVerb());
+ static NeverDestroyed<const String> menuListPopupAction(AXMenuListPopupActionVerb());
+ static NeverDestroyed<const String> listItemAction(AXListItemActionVerb());
switch (roleValue()) {
case ButtonRole:
@@ -1298,6 +1846,7 @@ const String& AccessibilityObject::actionVerb() const
case RadioButtonRole:
return radioButtonAction;
case CheckBoxRole:
+ case SwitchRole:
return isChecked() ? checkedCheckBoxAction : uncheckedCheckBoxAction;
case LinkRole:
case WebCoreLinkRole:
@@ -1309,54 +1858,126 @@ const String& AccessibilityObject::actionVerb() const
case ListItemRole:
return listItemAction;
default:
- return noAction;
+ return nullAtom;
}
+#else
+ return nullAtom;
+#endif
}
#endif
bool AccessibilityObject::ariaIsMultiline() const
{
- return equalIgnoringCase(getAttribute(aria_multilineAttr), "true");
+ return equalLettersIgnoringASCIICase(getAttribute(aria_multilineAttr), "true");
}
-const AtomicString& AccessibilityObject::invalidStatus() const
+String AccessibilityObject::invalidStatus() const
+{
+ String grammarValue = ASCIILiteral("grammar");
+ String falseValue = ASCIILiteral("false");
+ String spellingValue = ASCIILiteral("spelling");
+ String trueValue = ASCIILiteral("true");
+ String undefinedValue = ASCIILiteral("undefined");
+
+ // aria-invalid can return false (default), grammar, spelling, or true.
+ String ariaInvalid = stripLeadingAndTrailingHTMLSpaces(getAttribute(aria_invalidAttr));
+
+ // If "false", "undefined" [sic, string value], empty, or missing, return "false".
+ if (ariaInvalid.isEmpty() || ariaInvalid == falseValue || ariaInvalid == undefinedValue)
+ return falseValue;
+ // Besides true/false/undefined, the only tokens defined by WAI-ARIA 1.0...
+ // ...for @aria-invalid are "grammar" and "spelling".
+ if (ariaInvalid == grammarValue)
+ return grammarValue;
+ if (ariaInvalid == spellingValue)
+ return spellingValue;
+ // Any other non empty string should be treated as "true".
+ return trueValue;
+}
+
+AccessibilityARIACurrentState AccessibilityObject::ariaCurrentState() const
{
- DEFINE_STATIC_LOCAL(const AtomicString, invalidStatusFalse, ("false", AtomicString::ConstructFromLiteral));
+ // aria-current can return false (default), true, page, step, location, date or time.
+ String currentStateValue = stripLeadingAndTrailingHTMLSpaces(getAttribute(aria_currentAttr));
- // aria-invalid can return false (default), grammer, spelling, or true.
- const AtomicString& ariaInvalid = getAttribute(aria_invalidAttr);
+ // If "false", empty, or missing, return false state.
+ if (currentStateValue.isEmpty() || currentStateValue == "false")
+ return ARIACurrentFalse;
- // If empty or not present, it should return false.
- if (ariaInvalid.isEmpty())
- return invalidStatusFalse;
+ if (currentStateValue == "page")
+ return ARIACurrentPage;
+ if (currentStateValue == "step")
+ return ARIACurrentStep;
+ if (currentStateValue == "location")
+ return ARIACurrentLocation;
+ if (currentStateValue == "date")
+ return ARIACurrentDate;
+ if (currentStateValue == "time")
+ return ARIACurrentTime;
- return ariaInvalid;
+ // Any value not included in the list of allowed values should be treated as "true".
+ return ARIACurrentTrue;
}
-
-bool AccessibilityObject::hasAttribute(const QualifiedName& attribute) const
+
+bool AccessibilityObject::isAriaModalDescendant(Node* ariaModalNode) const
{
- Node* elementNode = node();
- if (!elementNode)
+ if (!ariaModalNode || !this->element())
+ return false;
+
+ if (this->element() == ariaModalNode)
+ return true;
+
+ // ARIA 1.1 aria-modal, indicates whether an element is modal when displayed.
+ // For the decendants of the modal object, they should also be considered as aria-modal=true.
+ for (auto& ancestor : elementAncestors(this->element())) {
+ if (&ancestor == ariaModalNode)
+ return true;
+ }
+ return false;
+}
+
+bool AccessibilityObject::ignoredFromARIAModalPresence() const
+{
+ // We shouldn't ignore the top node.
+ if (!node() || !node()->parentNode())
+ return false;
+
+ AXObjectCache* cache = axObjectCache();
+ if (!cache)
+ return false;
+
+ // ariaModalNode is the current displayed modal dialog.
+ Node* ariaModalNode = cache->ariaModalNode();
+ if (!ariaModalNode)
return false;
- if (!elementNode->isElementNode())
+ // We only want to ignore the objects within the same frame as the modal dialog.
+ if (ariaModalNode->document().frame() != this->frame())
return false;
- Element* element = toElement(elementNode);
- return element->fastHasAttribute(attribute);
+ return !isAriaModalDescendant(ariaModalNode);
+}
+
+bool AccessibilityObject::hasTagName(const QualifiedName& tagName) const
+{
+ Node* node = this->node();
+ return is<Element>(node) && downcast<Element>(*node).hasTagName(tagName);
}
-const AtomicString& AccessibilityObject::getAttribute(const QualifiedName& attribute) const
+bool AccessibilityObject::hasAttribute(const QualifiedName& attribute) const
{
- Node* elementNode = node();
- if (!elementNode)
- return nullAtom;
+ Node* node = this->node();
+ if (!is<Element>(node))
+ return false;
- if (!elementNode->isElementNode())
- return nullAtom;
+ return downcast<Element>(*node).fastHasAttribute(attribute);
+}
- Element* element = toElement(elementNode);
- return element->fastGetAttribute(attribute);
+const AtomicString& AccessibilityObject::getAttribute(const QualifiedName& attribute) const
+{
+ if (Element* element = this->element())
+ return element->fastGetAttribute(attribute);
+ return nullAtom;
}
// Lacking concrete evidence of orientation, horizontal means width > height. vertical is height > width;
@@ -1368,8 +1989,7 @@ AccessibilityOrientation AccessibilityObject::orientation() const
if (bounds.size().height() > bounds.size().width())
return AccessibilityOrientationVertical;
- // A tie goes to horizontal.
- return AccessibilityOrientationHorizontal;
+ return AccessibilityOrientationUndefined;
}
bool AccessibilityObject::isDescendantOfObject(const AccessibilityObject* axObject) const
@@ -1398,18 +2018,26 @@ AccessibilityObject* AccessibilityObject::firstAnonymousBlockChild() const
if (child->renderer() && child->renderer()->isAnonymousBlock())
return child;
}
- return 0;
+ return nullptr;
}
-typedef HashMap<String, AccessibilityRole, CaseFoldingHash> ARIARoleMap;
+typedef HashMap<String, AccessibilityRole, ASCIICaseInsensitiveHash> ARIARoleMap;
+typedef HashMap<AccessibilityRole, String, DefaultHash<int>::Hash, WTF::UnsignedWithZeroKeyHashTraits<int>> ARIAReverseRoleMap;
+
+static ARIARoleMap* gAriaRoleMap = nullptr;
+static ARIAReverseRoleMap* gAriaReverseRoleMap = nullptr;
struct RoleEntry {
String ariaRole;
AccessibilityRole webcoreRole;
};
-static ARIARoleMap* createARIARoleMap()
+static void initializeRoleMap()
{
+ if (gAriaRoleMap)
+ return;
+ ASSERT(!gAriaReverseRoleMap);
+
const RoleEntry roles[] = {
{ "alert", ApplicationAlertRole },
{ "alertdialog", ApplicationAlertDialogRole },
@@ -1422,12 +2050,15 @@ static ARIARoleMap* createARIARoleMap()
{ "contentinfo", LandmarkContentInfoRole },
{ "dialog", ApplicationDialogRole },
{ "directory", DirectoryRole },
- { "grid", TableRole },
- { "gridcell", CellRole },
+ { "grid", GridRole },
+ { "gridcell", GridCellRole },
+ { "table", TableRole },
+ { "cell", CellRole },
{ "columnheader", ColumnHeaderRole },
{ "combobox", ComboBoxRole },
{ "definition", DefinitionRole },
{ "document", DocumentRole },
+ { "form", FormRole },
{ "rowheader", RowHeaderRole },
{ "group", GroupRole },
{ "heading", HeadingRole },
@@ -1444,8 +2075,9 @@ static ARIARoleMap* createARIARoleMap()
{ "menu", MenuRole },
{ "menubar", MenuBarRole },
{ "menuitem", MenuItemRole },
- { "menuitemcheckbox", MenuItemRole },
- { "menuitemradio", MenuItemRole },
+ { "menuitemcheckbox", MenuItemCheckboxRole },
+ { "menuitemradio", MenuItemRadioRole },
+ { "none", PresentationalRole },
{ "note", DocumentNoteRole },
{ "navigation", LandmarkNavigationRole },
{ "option", ListBoxOptionRole },
@@ -1455,12 +2087,15 @@ static ARIARoleMap* createARIARoleMap()
{ "radiogroup", RadioGroupRole },
{ "region", DocumentRegionRole },
{ "row", RowRole },
+ { "rowgroup", RowGroupRole },
{ "scrollbar", ScrollBarRole },
{ "search", LandmarkSearchRole },
+ { "searchbox", SearchFieldRole },
{ "separator", SplitterRole },
{ "slider", SliderRole },
{ "spinbutton", SpinButtonRole },
{ "status", ApplicationStatusRole },
+ { "switch", SwitchRole },
{ "tab", TabRole },
{ "tablist", TabListRole },
{ "tabpanel", TabPanelRole },
@@ -1473,26 +2108,37 @@ static ARIARoleMap* createARIARoleMap()
{ "treegrid", TreeGridRole },
{ "treeitem", TreeItemRole }
};
- ARIARoleMap* roleMap = new ARIARoleMap;
- for (size_t i = 0; i < WTF_ARRAY_LENGTH(roles); ++i)
- roleMap->set(roles[i].ariaRole, roles[i].webcoreRole);
- return roleMap;
+ gAriaRoleMap = new ARIARoleMap;
+ gAriaReverseRoleMap = new ARIAReverseRoleMap;
+ size_t roleLength = WTF_ARRAY_LENGTH(roles);
+ for (size_t i = 0; i < roleLength; ++i) {
+ gAriaRoleMap->set(roles[i].ariaRole, roles[i].webcoreRole);
+ gAriaReverseRoleMap->set(roles[i].webcoreRole, roles[i].ariaRole);
+ }
+}
+
+static ARIARoleMap& ariaRoleMap()
+{
+ initializeRoleMap();
+ return *gAriaRoleMap;
+}
+
+static ARIAReverseRoleMap& reverseAriaRoleMap()
+{
+ initializeRoleMap();
+ return *gAriaReverseRoleMap;
}
AccessibilityRole AccessibilityObject::ariaRoleToWebCoreRole(const String& value)
{
ASSERT(!value.isEmpty());
- static const ARIARoleMap* roleMap = createARIARoleMap();
-
Vector<String> roleVector;
value.split(' ', roleVector);
AccessibilityRole role = UnknownRole;
- unsigned size = roleVector.size();
- for (unsigned i = 0; i < size; ++i) {
- String roleName = roleVector[i];
- role = roleMap->get(roleName);
+ for (const auto& roleName : roleVector) {
+ role = ariaRoleMap().get(roleName);
if (role)
return role;
}
@@ -1500,6 +2146,16 @@ AccessibilityRole AccessibilityObject::ariaRoleToWebCoreRole(const String& value
return role;
}
+String AccessibilityObject::computedRoleString() const
+{
+ // FIXME: Need a few special cases that aren't in the RoleMap: option, etc. http://webkit.org/b/128296
+ AccessibilityRole role = roleValue();
+ if (role == HorizontalRuleRole)
+ role = SplitterRole;
+
+ return reverseAriaRoleMap().get(role);
+}
+
bool AccessibilityObject::hasHighlighting() const
{
for (Node* node = this->node(); node; node = node->parentNode()) {
@@ -1510,8 +2166,77 @@ bool AccessibilityObject::hasHighlighting() const
return false;
}
+const AtomicString& AccessibilityObject::roleDescription() const
+{
+ return getAttribute(aria_roledescriptionAttr);
+}
+
+static bool nodeHasPresentationRole(Node* node)
+{
+ return nodeHasRole(node, "presentation") || nodeHasRole(node, "none");
+}
+
+bool AccessibilityObject::supportsPressAction() const
+{
+ if (isButton())
+ return true;
+ if (roleValue() == DetailsRole)
+ return true;
+
+ Element* actionElement = this->actionElement();
+ if (!actionElement)
+ return false;
+
+ // [Bug: 136247] Heuristic: element handlers that have more than one accessible descendant should not be exposed as supporting press.
+ if (actionElement != element()) {
+ if (AccessibilityObject* axObj = axObjectCache()->getOrCreate(actionElement)) {
+ AccessibilityChildrenVector results;
+ // Search within for immediate descendants that are static text. If we find more than one
+ // then this is an event delegator actionElement and we should expose the press action.
+ Vector<AccessibilitySearchKey> keys({ StaticTextSearchKey, ControlSearchKey, GraphicSearchKey, HeadingSearchKey, LinkSearchKey });
+ AccessibilitySearchCriteria criteria(axObj, SearchDirectionNext, "", 2, false, false);
+ criteria.searchKeys = keys;
+ axObj->findMatchingObjects(&criteria, results);
+ if (results.size() > 1)
+ return false;
+ }
+ }
+
+ // [Bug: 133613] Heuristic: If the action element is presentational, we shouldn't expose press as a supported action.
+ return !nodeHasPresentationRole(actionElement);
+}
+
+bool AccessibilityObject::supportsDatetimeAttribute() const
+{
+ return hasTagName(insTag) || hasTagName(delTag) || hasTagName(timeTag);
+}
+
+Element* AccessibilityObject::element() const
+{
+ Node* node = this->node();
+ if (is<Element>(node))
+ return downcast<Element>(node);
+ return nullptr;
+}
+
+bool AccessibilityObject::isValueAutofilled() const
+{
+ if (!isNativeTextControl())
+ return false;
+
+ Node* node = this->node();
+ if (!node || !is<HTMLInputElement>(*node))
+ return false;
+
+ return downcast<HTMLInputElement>(*node).isAutoFilled();
+}
+
const AtomicString& AccessibilityObject::placeholderValue() const
{
+ const AtomicString& ariaPlaceholder = getAttribute(aria_placeholderAttr);
+ if (!ariaPlaceholder.isEmpty())
+ return ariaPlaceholder;
+
const AtomicString& placeholder = getAttribute(placeholderAttr);
if (!placeholder.isEmpty())
return placeholder;
@@ -1534,18 +2259,32 @@ bool AccessibilityObject::isInsideARIALiveRegion() const
bool AccessibilityObject::supportsARIAAttributes() const
{
+ // This returns whether the element supports any global ARIA attributes.
return supportsARIALiveRegion()
|| supportsARIADragging()
|| supportsARIADropping()
|| supportsARIAFlowTo()
|| supportsARIAOwns()
- || hasAttribute(aria_labelAttr);
+ || hasAttribute(aria_atomicAttr)
+ || hasAttribute(aria_busyAttr)
+ || hasAttribute(aria_controlsAttr)
+ || hasAttribute(aria_describedbyAttr)
+ || hasAttribute(aria_disabledAttr)
+ || hasAttribute(aria_haspopupAttr)
+ || hasAttribute(aria_invalidAttr)
+ || hasAttribute(aria_labelAttr)
+ || hasAttribute(aria_labelledbyAttr)
+ || hasAttribute(aria_relevantAttr);
+}
+
+bool AccessibilityObject::liveRegionStatusIsEnabled(const AtomicString& liveRegionStatus)
+{
+ return equalLettersIgnoringASCIICase(liveRegionStatus, "polite") || equalLettersIgnoringASCIICase(liveRegionStatus, "assertive");
}
bool AccessibilityObject::supportsARIALiveRegion() const
{
- const AtomicString& liveRegion = ariaLiveRegionStatus();
- return equalIgnoringCase(liveRegion, "polite") || equalIgnoringCase(liveRegion, "assertive");
+ return liveRegionStatusIsEnabled(ariaLiveRegionStatus());
}
AccessibilityObject* AccessibilityObject::elementAccessibilityHitTest(const IntPoint& point) const
@@ -1554,15 +2293,16 @@ AccessibilityObject* AccessibilityObject::elementAccessibilityHitTest(const IntP
if (isAttachment()) {
Widget* widget = widgetForAttachmentView();
// Normalize the point for the widget's bounds.
- if (widget && widget->isFrameView())
- return axObjectCache()->getOrCreate(widget)->accessibilityHitTest(IntPoint(point - widget->frameRect().location()));
+ if (widget && widget->isFrameView()) {
+ if (AXObjectCache* cache = axObjectCache())
+ return cache->getOrCreate(widget)->accessibilityHitTest(IntPoint(point - widget->frameRect().location()));
+ }
}
// Check if there are any mock elements that need to be handled.
- size_t count = m_children.size();
- for (size_t k = 0; k < count; k++) {
- if (m_children[k]->isMockObject() && m_children[k]->elementRect().contains(point))
- return m_children[k]->elementAccessibilityHitTest(point);
+ for (const auto& child : m_children) {
+ if (child->isMockObject() && child->elementRect().contains(point))
+ return child->elementAccessibilityHitTest(point);
}
return const_cast<AccessibilityObject*>(this);
@@ -1573,18 +2313,18 @@ AXObjectCache* AccessibilityObject::axObjectCache() const
Document* doc = document();
if (doc)
return doc->axObjectCache();
- return 0;
+ return nullptr;
}
AccessibilityObject* AccessibilityObject::focusedUIElement() const
{
Document* doc = document();
if (!doc)
- return 0;
+ return nullptr;
Page* page = doc->page();
if (!page)
- return 0;
+ return nullptr;
return AXObjectCache::focusedUIElementForPage(page);
}
@@ -1592,10 +2332,12 @@ AccessibilityObject* AccessibilityObject::focusedUIElement() const
AccessibilitySortDirection AccessibilityObject::sortDirection() const
{
const AtomicString& sortAttribute = getAttribute(aria_sortAttr);
- if (equalIgnoringCase(sortAttribute, "ascending"))
+ if (equalLettersIgnoringASCIICase(sortAttribute, "ascending"))
return SortDirectionAscending;
- if (equalIgnoringCase(sortAttribute, "descending"))
+ if (equalLettersIgnoringASCIICase(sortAttribute, "descending"))
return SortDirectionDescending;
+ if (equalLettersIgnoringASCIICase(sortAttribute, "other"))
+ return SortDirectionOther;
return SortDirectionNone;
}
@@ -1628,28 +2370,98 @@ int AccessibilityObject::ariaPosInSet() const
return getAttribute(aria_posinsetAttr).toInt();
}
-bool AccessibilityObject::supportsARIAExpanded() const
+String AccessibilityObject::identifierAttribute() const
+{
+ return getAttribute(idAttr);
+}
+
+void AccessibilityObject::classList(Vector<String>& classList) const
{
- return !getAttribute(aria_expandedAttr).isEmpty();
+ Node* node = this->node();
+ if (!is<Element>(node))
+ return;
+
+ Element* element = downcast<Element>(node);
+ DOMTokenList& list = element->classList();
+ unsigned length = list.length();
+ for (unsigned k = 0; k < length; k++)
+ classList.append(list.item(k).string());
+}
+
+bool AccessibilityObject::supportsARIAPressed() const
+{
+ const AtomicString& expanded = getAttribute(aria_pressedAttr);
+ return equalLettersIgnoringASCIICase(expanded, "true") || equalLettersIgnoringASCIICase(expanded, "false");
+}
+
+bool AccessibilityObject::supportsExpanded() const
+{
+ // Undefined values should not result in this attribute being exposed to ATs according to ARIA.
+ const AtomicString& expanded = getAttribute(aria_expandedAttr);
+ if (equalLettersIgnoringASCIICase(expanded, "true") || equalLettersIgnoringASCIICase(expanded, "false"))
+ return true;
+ switch (roleValue()) {
+ case ComboBoxRole:
+ case DisclosureTriangleRole:
+ case DetailsRole:
+ return true;
+ default:
+ return false;
+ }
}
bool AccessibilityObject::isExpanded() const
{
- if (equalIgnoringCase(getAttribute(aria_expandedAttr), "true"))
+ if (equalLettersIgnoringASCIICase(getAttribute(aria_expandedAttr), "true"))
return true;
+ if (is<HTMLDetailsElement>(node()))
+ return downcast<HTMLDetailsElement>(node())->isOpen();
+
return false;
}
-
+
+bool AccessibilityObject::supportsChecked() const
+{
+ switch (roleValue()) {
+ case CheckBoxRole:
+ case MenuItemCheckboxRole:
+ case MenuItemRadioRole:
+ case RadioButtonRole:
+ case SwitchRole:
+ return true;
+ default:
+ return false;
+ }
+}
+
AccessibilityButtonState AccessibilityObject::checkboxOrRadioValue() const
{
// If this is a real checkbox or radio button, AccessibilityRenderObject will handle.
- // If it's an ARIA checkbox or radio, the aria-checked attribute should be used.
-
+ // If it's an ARIA checkbox, radio, or switch the aria-checked attribute should be used.
+ // If it's a toggle button, the aria-pressed attribute is consulted.
+
+ if (isToggleButton()) {
+ const AtomicString& ariaPressed = getAttribute(aria_pressedAttr);
+ if (equalLettersIgnoringASCIICase(ariaPressed, "true"))
+ return ButtonStateOn;
+ if (equalLettersIgnoringASCIICase(ariaPressed, "mixed"))
+ return ButtonStateMixed;
+ return ButtonStateOff;
+ }
+
const AtomicString& result = getAttribute(aria_checkedAttr);
- if (equalIgnoringCase(result, "true"))
+ if (equalLettersIgnoringASCIICase(result, "true"))
return ButtonStateOn;
- if (equalIgnoringCase(result, "mixed"))
+ if (equalLettersIgnoringASCIICase(result, "mixed")) {
+ // ARIA says that radio, menuitemradio, and switch elements must NOT expose button state mixed.
+ AccessibilityRole ariaRole = ariaRoleAttribute();
+ if (ariaRole == RadioButtonRole || ariaRole == MenuItemRadioRole || ariaRole == SwitchRole)
+ return ButtonStateOff;
+ return ButtonStateMixed;
+ }
+
+ if (equalLettersIgnoringASCIICase(getAttribute(indeterminateAttr), "true"))
return ButtonStateMixed;
return ButtonStateOff;
@@ -1660,71 +2472,89 @@ AccessibilityButtonState AccessibilityObject::checkboxOrRadioValue() const
// logic is the same. The goal is to compute the best scroll offset
// in order to make an object visible within a viewport.
//
+// If the object is already fully visible, returns the same scroll
+// offset.
+//
// In case the whole object cannot fit, you can specify a
// subfocus - a smaller region within the object that should
// be prioritized. If the whole object can fit, the subfocus is
// ignored.
//
-// Example: the viewport is scrolled to the right just enough
-// that the object is in view.
+// If possible, the object and subfocus are centered within the
+// viewport.
+//
+// Example 1: the object is already visible, so nothing happens.
+// +----------Viewport---------+
+// +---Object---+
+// +--SubFocus--+
+//
+// Example 2: the object is not fully visible, so it's centered
+// within the viewport.
// Before:
// +----------Viewport---------+
// +---Object---+
// +--SubFocus--+
//
// After:
-// +----------Viewport---------+
+// +----------Viewport---------+
// +---Object---+
// +--SubFocus--+
//
+// Example 3: the object is larger than the viewport, so the
+// viewport moves to show as much of the object as possible,
+// while also trying to center the subfocus.
+// Before:
+// +----------Viewport---------+
+// +---------------Object--------------+
+// +-SubFocus-+
+//
+// After:
+// +----------Viewport---------+
+// +---------------Object--------------+
+// +-SubFocus-+
+//
// When constraints cannot be fully satisfied, the min
// (left/top) position takes precedence over the max (right/bottom).
//
// Note that the return value represents the ideal new scroll offset.
// This may be out of range - the calling function should clip this
// to the available range.
-static int computeBestScrollOffset(int currentScrollOffset,
- int subfocusMin, int subfocusMax,
- int objectMin, int objectMax,
- int viewportMin, int viewportMax) {
+static int computeBestScrollOffset(int currentScrollOffset, int subfocusMin, int subfocusMax, int objectMin, int objectMax, int viewportMin, int viewportMax)
+{
int viewportSize = viewportMax - viewportMin;
-
- // If the focus size is larger than the viewport size, shrink it in the
- // direction of subfocus.
+
+ // If the object size is larger than the viewport size, consider
+ // only a portion that's as large as the viewport, centering on
+ // the subfocus as much as possible.
if (objectMax - objectMin > viewportSize) {
- // Subfocus must be within focus:
+ // Since it's impossible to fit the whole object in the
+ // viewport, exit now if the subfocus is already within the viewport.
+ if (subfocusMin - currentScrollOffset >= viewportMin && subfocusMax - currentScrollOffset <= viewportMax)
+ return currentScrollOffset;
+
+ // Subfocus must be within focus.
subfocusMin = std::max(subfocusMin, objectMin);
subfocusMax = std::min(subfocusMax, objectMax);
-
+
// Subfocus must be no larger than the viewport size; favor top/left.
if (subfocusMax - subfocusMin > viewportSize)
subfocusMax = subfocusMin + viewportSize;
+
+ // Compute the size of an object centered on the subfocus, the size of the viewport.
+ int centeredObjectMin = (subfocusMin + subfocusMax - viewportSize) / 2;
+ int centeredObjectMax = centeredObjectMin + viewportSize;
- if (subfocusMin + viewportSize > objectMax)
- objectMin = objectMax - viewportSize;
- else {
- objectMin = subfocusMin;
- objectMax = subfocusMin + viewportSize;
- }
+ objectMin = std::max(objectMin, centeredObjectMin);
+ objectMax = std::min(objectMax, centeredObjectMax);
}
// Exit now if the focus is already within the viewport.
if (objectMin - currentScrollOffset >= viewportMin
&& objectMax - currentScrollOffset <= viewportMax)
return currentScrollOffset;
-
- // Scroll left if we're too far to the right.
- if (objectMax - currentScrollOffset > viewportMax)
- return objectMax - viewportMax;
-
- // Scroll right if we're too far to the left.
- if (objectMin - currentScrollOffset < viewportMin)
- return objectMin - viewportMin;
-
- ASSERT_NOT_REACHED();
-
- // This shouldn't happen.
- return currentScrollOffset;
+
+ // Center the object in the viewport.
+ return (objectMin + objectMax - viewportMin - viewportMax) / 2;
}
bool AccessibilityObject::isOnscreen() const
@@ -1748,8 +2578,9 @@ bool AccessibilityObject::isOnscreen() const
for (size_t i = levels; i >= 1; i--) {
const AccessibilityObject* outer = objects[i];
const AccessibilityObject* inner = objects[i - 1];
- const IntRect outerRect = i < levels ? pixelSnappedIntRect(outer->boundingBoxRect()) : outer->getScrollableAreaIfScrollable()->visibleContentRect();
- const IntRect innerRect = pixelSnappedIntRect(inner->isAccessibilityScrollView() ? inner->parentObject()->boundingBoxRect() : inner->boundingBoxRect());
+ // FIXME: unclear if we need LegacyIOSDocumentVisibleRect.
+ const IntRect outerRect = i < levels ? snappedIntRect(outer->boundingBoxRect()) : outer->getScrollableAreaIfScrollable()->visibleContentRect(ScrollableArea::LegacyIOSDocumentVisibleRect);
+ const IntRect innerRect = snappedIntRect(inner->isAccessibilityScrollView() ? inner->parentObject()->boundingBoxRect() : inner->boundingBoxRect());
if (!outerRect.intersects(innerRect)) {
isOnscreen = false;
@@ -1762,7 +2593,7 @@ bool AccessibilityObject::isOnscreen() const
void AccessibilityObject::scrollToMakeVisible() const
{
- IntRect objectRect = pixelSnappedIntRect(boundingBoxRect());
+ IntRect objectRect = snappedIntRect(boundingBoxRect());
objectRect.setLocation(IntPoint());
scrollToMakeVisibleWithSubFocus(objectRect);
}
@@ -1772,7 +2603,7 @@ void AccessibilityObject::scrollToMakeVisibleWithSubFocus(const IntRect& subfocu
// Search up the parent chain until we find the first one that's scrollable.
AccessibilityObject* scrollParent = parentObject();
ScrollableArea* scrollableArea;
- for (scrollableArea = 0;
+ for (scrollableArea = nullptr;
scrollParent && !(scrollableArea = scrollParent->getScrollableAreaIfScrollable());
scrollParent = scrollParent->parentObject()) { }
if (!scrollableArea)
@@ -1780,8 +2611,14 @@ void AccessibilityObject::scrollToMakeVisibleWithSubFocus(const IntRect& subfocu
LayoutRect objectRect = boundingBoxRect();
IntPoint scrollPosition = scrollableArea->scrollPosition();
- IntRect scrollVisibleRect = scrollableArea->visibleContentRect();
+ // FIXME: unclear if we need LegacyIOSDocumentVisibleRect.
+ IntRect scrollVisibleRect = scrollableArea->visibleContentRect(ScrollableArea::LegacyIOSDocumentVisibleRect);
+ if (!scrollParent->isScrollView()) {
+ objectRect.moveBy(scrollPosition);
+ objectRect.moveBy(-snappedIntRect(scrollParent->elementRect()).location());
+ }
+
int desiredX = computeBestScrollOffset(
scrollPosition.x(),
objectRect.x() + subfocus.x(), objectRect.x() + subfocus.maxX(),
@@ -1795,9 +2632,16 @@ void AccessibilityObject::scrollToMakeVisibleWithSubFocus(const IntRect& subfocu
scrollParent->scrollTo(IntPoint(desiredX, desiredY));
+ // Convert the subfocus into the coordinates of the scroll parent.
+ IntRect newSubfocus = subfocus;
+ IntRect newElementRect = snappedIntRect(elementRect());
+ IntRect scrollParentRect = snappedIntRect(scrollParent->elementRect());
+ newSubfocus.move(newElementRect.x(), newElementRect.y());
+ newSubfocus.move(-scrollParentRect.x(), -scrollParentRect.y());
+
// Recursively make sure the scroll parent itself is visible.
if (scrollParent->parentObject())
- scrollParent->scrollToMakeVisible();
+ scrollParent->scrollToMakeVisibleWithSubFocus(newSubfocus);
}
void AccessibilityObject::scrollToGlobalPoint(const IntPoint& globalPoint) const
@@ -1862,6 +2706,102 @@ void AccessibilityObject::scrollToGlobalPoint(const IntPoint& globalPoint) const
}
}
}
+
+void AccessibilityObject::scrollAreaAndAncestor(std::pair<ScrollableArea*, AccessibilityObject*>& scrollers) const
+{
+ // Search up the parent chain until we find the first one that's scrollable.
+ scrollers.first = nullptr;
+ for (scrollers.second = parentObject(); scrollers.second; scrollers.second = scrollers.second->parentObject()) {
+ if ((scrollers.first = scrollers.second->getScrollableAreaIfScrollable()))
+ break;
+ }
+}
+
+ScrollableArea* AccessibilityObject::scrollableAreaAncestor() const
+{
+ std::pair<ScrollableArea*, AccessibilityObject*> scrollers;
+ scrollAreaAndAncestor(scrollers);
+ return scrollers.first;
+}
+
+IntPoint AccessibilityObject::scrollPosition() const
+{
+ if (auto scroller = scrollableAreaAncestor())
+ return scroller->scrollPosition();
+
+ return IntPoint();
+}
+
+IntRect AccessibilityObject::scrollVisibleContentRect() const
+{
+ if (auto scroller = scrollableAreaAncestor())
+ return scroller->visibleContentRect(ScrollableArea::LegacyIOSDocumentVisibleRect);
+
+ return IntRect();
+}
+
+IntSize AccessibilityObject::scrollContentsSize() const
+{
+ if (auto scroller = scrollableAreaAncestor())
+ return scroller->contentsSize();
+
+ return IntSize();
+}
+
+bool AccessibilityObject::scrollByPage(ScrollByPageDirection direction) const
+{
+ std::pair<ScrollableArea*, AccessibilityObject*> scrollers;
+ scrollAreaAndAncestor(scrollers);
+ ScrollableArea* scrollableArea = scrollers.first;
+ AccessibilityObject* scrollParent = scrollers.second;
+
+ if (!scrollableArea)
+ return false;
+
+ IntPoint scrollPosition = scrollableArea->scrollPosition();
+ IntPoint newScrollPosition = scrollPosition;
+ IntSize scrollSize = scrollableArea->contentsSize();
+ IntRect scrollVisibleRect = scrollableArea->visibleContentRect(ScrollableArea::LegacyIOSDocumentVisibleRect);
+ switch (direction) {
+ case Right: {
+ int scrollAmount = scrollVisibleRect.size().width();
+ int newX = scrollPosition.x() - scrollAmount;
+ newScrollPosition.setX(std::max(newX, 0));
+ break;
+ }
+ case Left: {
+ int scrollAmount = scrollVisibleRect.size().width();
+ int newX = scrollAmount + scrollPosition.x();
+ int maxX = scrollSize.width() - scrollAmount;
+ newScrollPosition.setX(std::min(newX, maxX));
+ break;
+ }
+ case Up: {
+ int scrollAmount = scrollVisibleRect.size().height();
+ int newY = scrollPosition.y() - scrollAmount;
+ newScrollPosition.setY(std::max(newY, 0));
+ break;
+ }
+ case Down: {
+ int scrollAmount = scrollVisibleRect.size().height();
+ int newY = scrollAmount + scrollPosition.y();
+ int maxY = scrollSize.height() - scrollAmount;
+ newScrollPosition.setY(std::min(newY, maxY));
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (newScrollPosition != scrollPosition) {
+ scrollParent->scrollTo(newScrollPosition);
+ document()->updateLayoutIgnorePendingStylesheets();
+ return true;
+ }
+
+ return false;
+}
+
bool AccessibilityObject::lastKnownIsIgnoredValue()
{
@@ -1880,7 +2820,8 @@ void AccessibilityObject::notifyIfIgnoredValueChanged()
{
bool isIgnored = accessibilityIsIgnored();
if (lastKnownIsIgnoredValue() != isIgnored) {
- axObjectCache()->childrenChanged(parentObject());
+ if (AXObjectCache* cache = axObjectCache())
+ cache->childrenChanged(parentObject());
setLastKnownIsIgnoredValue(isIgnored);
}
}
@@ -1894,7 +2835,7 @@ TextIteratorBehavior AccessibilityObject::textIteratorBehaviorForTextRange() con
{
TextIteratorBehavior behavior = TextIteratorIgnoresStyleVisibility;
-#if PLATFORM(GTK)
+#if PLATFORM(GTK) || PLATFORM(EFL)
// We need to emit replaced elements for GTK, and present
// them with the 'object replacement character' (0xFFFC).
behavior = static_cast<TextIteratorBehavior>(behavior | TextIteratorEmitsObjectReplacementCharacters);
@@ -1929,22 +2870,35 @@ bool AccessibilityObject::accessibilityIsIgnoredByDefault() const
return defaultObjectInclusion() == IgnoreObject;
}
-bool AccessibilityObject::ariaIsHidden() const
+// ARIA component of hidden definition.
+// http://www.w3.org/TR/wai-aria/terms#def_hidden
+bool AccessibilityObject::isARIAHidden() const
{
- if (equalIgnoringCase(getAttribute(aria_hiddenAttr), "true"))
- return true;
-
- for (AccessibilityObject* object = parentObject(); object; object = object->parentObject()) {
- if (equalIgnoringCase(object->getAttribute(aria_hiddenAttr), "true"))
+ for (const AccessibilityObject* object = this; object; object = object->parentObject()) {
+ if (equalLettersIgnoringASCIICase(object->getAttribute(aria_hiddenAttr), "true"))
return true;
}
-
return false;
}
+// DOM component of hidden definition.
+// http://www.w3.org/TR/wai-aria/terms#def_hidden
+bool AccessibilityObject::isDOMHidden() const
+{
+ RenderObject* renderer = this->renderer();
+ if (!renderer)
+ return true;
+
+ const RenderStyle& style = renderer->style();
+ return style.display() == NONE || style.visibility() != VISIBLE;
+}
+
AccessibilityObjectInclusion AccessibilityObject::defaultObjectInclusion() const
{
- if (ariaIsHidden())
+ if (isARIAHidden())
+ return IgnoreObject;
+
+ if (ignoredFromARIAModalPresence())
return IgnoreObject;
if (isPresentationalChildOfAriaRole())
@@ -1955,7 +2909,11 @@ AccessibilityObjectInclusion AccessibilityObject::defaultObjectInclusion() const
bool AccessibilityObject::accessibilityIsIgnored() const
{
- AXComputedObjectAttributeCache* attributeCache = axObjectCache()->computedObjectAttributeCache();
+ AXComputedObjectAttributeCache* attributeCache = nullptr;
+ AXObjectCache* cache = axObjectCache();
+ if (cache)
+ attributeCache = cache->computedObjectAttributeCache();
+
if (attributeCache) {
AccessibilityObjectInclusion ignored = attributeCache->getIgnored(axObjectID());
switch (ignored) {
@@ -1970,10 +2928,74 @@ bool AccessibilityObject::accessibilityIsIgnored() const
bool result = computeAccessibilityIsIgnored();
- if (attributeCache)
+ // In case computing axIsIgnored disables attribute caching, we should refetch the object to see if it exists.
+ if (cache && (attributeCache = cache->computedObjectAttributeCache()))
attributeCache->setIgnored(axObjectID(), result ? IgnoreObject : IncludeObject);
return result;
}
+void AccessibilityObject::elementsFromAttribute(Vector<Element*>& elements, const QualifiedName& attribute) const
+{
+ Node* node = this->node();
+ if (!node || !node->isElementNode())
+ return;
+
+ TreeScope& treeScope = node->treeScope();
+
+ String idList = getAttribute(attribute).string();
+ if (idList.isEmpty())
+ return;
+
+ idList.replace('\n', ' ');
+ Vector<String> idVector;
+ idList.split(' ', idVector);
+
+ for (const auto& idName : idVector) {
+ if (Element* idElement = treeScope.getElementById(idName))
+ elements.append(idElement);
+ }
+}
+
+#if PLATFORM(COCOA)
+bool AccessibilityObject::preventKeyboardDOMEventDispatch() const
+{
+ Frame* frame = this->frame();
+ return frame && frame->settings().preventKeyboardDOMEventDispatch();
+}
+
+void AccessibilityObject::setPreventKeyboardDOMEventDispatch(bool on)
+{
+ Frame* frame = this->frame();
+ if (!frame)
+ return;
+ frame->settings().setPreventKeyboardDOMEventDispatch(on);
+}
+#endif
+
+bool AccessibilityObject::isStyleFormatGroup() const
+{
+ Node* node = this->node();
+ if (!node)
+ return false;
+
+ return node->hasTagName(kbdTag) || node->hasTagName(codeTag)
+ || node->hasTagName(preTag) || node->hasTagName(sampTag)
+ || node->hasTagName(varTag) || node->hasTagName(citeTag)
+ || node->hasTagName(insTag) || node->hasTagName(delTag);
+}
+
+bool AccessibilityObject::isContainedByPasswordField() const
+{
+ Node* node = this->node();
+ if (!node)
+ return false;
+
+ if (ariaRoleAttribute() != UnknownRole)
+ return false;
+
+ Element* element = node->shadowHost();
+ return is<HTMLInputElement>(element) && downcast<HTMLInputElement>(*element).isPasswordField();
+}
+
} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AccessibilityObject.h b/Source/WebCore/accessibility/AccessibilityObject.h
index 680e367d1..d629778ae 100644
--- a/Source/WebCore/accessibility/AccessibilityObject.h
+++ b/Source/WebCore/accessibility/AccessibilityObject.h
@@ -11,7 +11,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -33,21 +33,21 @@
#include "FloatQuad.h"
#include "LayoutRect.h"
#include "Path.h"
-#include "TextIterator.h"
+#include "TextIteratorBehavior.h"
#include "VisiblePosition.h"
#include "VisibleSelection.h"
#include <wtf/Forward.h>
#include <wtf/RefPtr.h>
#include <wtf/Vector.h>
-#if PLATFORM(MAC)
+#if PLATFORM(COCOA)
#include <wtf/RetainPtr.h>
#elif PLATFORM(WIN)
#include "AccessibilityObjectWrapperWin.h"
#include "COMPtr.h"
#endif
-#if PLATFORM(MAC)
+#if PLATFORM(COCOA)
typedef struct _NSRange NSRange;
@@ -80,12 +80,13 @@ class HTMLAnchorElement;
class HTMLAreaElement;
class IntPoint;
class IntSize;
+class MainFrame;
class Node;
class Page;
class RenderObject;
class RenderListItem;
class ScrollableArea;
-class VisibleSelection;
+class ScrollView;
class Widget;
typedef unsigned AXID;
@@ -99,11 +100,14 @@ enum AccessibilityRole {
ApplicationLogRole,
ApplicationMarqueeRole,
ApplicationStatusRole,
- ApplicationTimerRole,
+ ApplicationTimerRole,
+ AudioRole,
+ BlockquoteRole,
BrowserRole,
BusyIndicatorRole,
ButtonRole,
CanvasRole,
+ CaptionRole,
CellRole,
CheckBoxRole,
ColorWellRole,
@@ -114,6 +118,7 @@ enum AccessibilityRole {
DescriptionListRole,
DescriptionListTermRole,
DescriptionListDetailRole,
+ DetailsRole,
DirectoryRole,
DisclosureTriangleRole,
DivRole,
@@ -127,12 +132,14 @@ enum AccessibilityRole {
FooterRole,
FormRole,
GridRole,
+ GridCellRole,
GroupRole,
GrowAreaRole,
HeadingRole,
HelpTagRole,
HorizontalRuleRole,
IgnoredRole,
+ InlineRole,
ImageRole,
ImageMapRole,
ImageMapLinkRole,
@@ -158,22 +165,31 @@ enum AccessibilityRole {
MenuBarRole,
MenuButtonRole,
MenuItemRole,
+ MenuItemCheckboxRole,
+ MenuItemRadioRole,
MenuListPopupRole,
MenuListOptionRole,
OutlineRole,
ParagraphRole,
PopUpButtonRole,
+ PreRole,
PresentationalRole,
ProgressIndicatorRole,
RadioButtonRole,
RadioGroupRole,
RowHeaderRole,
RowRole,
+ RowGroupRole,
+ RubyBaseRole,
+ RubyBlockRole,
+ RubyInlineRole,
+ RubyRunRole,
+ RubyTextRole,
RulerRole,
RulerMarkerRole,
ScrollAreaRole,
ScrollBarRole,
- SeamlessWebAreaRole,
+ SearchFieldRole,
SheetRole,
SliderRole,
SliderThumbRole,
@@ -182,6 +198,8 @@ enum AccessibilityRole {
SplitGroupRole,
SplitterRole,
StaticTextRole,
+ SummaryRole,
+ SwitchRole,
SystemWideRole,
SVGRootRole,
TabGroupRole,
@@ -199,7 +217,8 @@ enum AccessibilityRole {
ToolbarRole,
UnknownRole,
UserInterfaceTooltipRole,
- ValueIndicatorRole,
+ ValueIndicatorRole,
+ VideoRole,
WebAreaRole,
WebCoreLinkRole,
WindowRole,
@@ -219,17 +238,17 @@ enum AccessibilityTextSource {
struct AccessibilityText {
String text;
AccessibilityTextSource textSource;
- Vector<RefPtr<AccessibilityObject> > textElements;
+ Vector<RefPtr<AccessibilityObject>> textElements;
AccessibilityText(const String& t, const AccessibilityTextSource& s)
: text(t)
, textSource(s)
{ }
- AccessibilityText(const String& t, const AccessibilityTextSource& s, const Vector<RefPtr<AccessibilityObject> > elements)
+ AccessibilityText(const String& t, const AccessibilityTextSource& s, Vector<RefPtr<AccessibilityObject>> elements)
: text(t)
, textSource(s)
- , textElements(elements)
+ , textElements(WTFMove(elements))
{ }
AccessibilityText(const String& t, const AccessibilityTextSource& s, const RefPtr<AccessibilityObject> element)
@@ -240,14 +259,28 @@ struct AccessibilityText {
}
};
-enum AccessibilityTextUnderElementMode {
- TextUnderElementModeSkipIgnoredChildren,
- TextUnderElementModeIncludeAllChildren
+struct AccessibilityTextUnderElementMode {
+ enum ChildrenInclusion {
+ TextUnderElementModeSkipIgnoredChildren,
+ TextUnderElementModeIncludeAllChildren,
+ TextUnderElementModeIncludeNameFromContentsChildren, // This corresponds to ARIA concept: nameFrom
+ };
+
+ ChildrenInclusion childrenInclusion;
+ bool includeFocusableContent;
+ Node* ignoredChildNode;
+
+ AccessibilityTextUnderElementMode(ChildrenInclusion c = TextUnderElementModeSkipIgnoredChildren, bool i = false, Node* ignored = nullptr)
+ : childrenInclusion(c)
+ , includeFocusableContent(i)
+ , ignoredChildNode(ignored)
+ { }
};
enum AccessibilityOrientation {
AccessibilityOrientationVertical,
AccessibilityOrientationHorizontal,
+ AccessibilityOrientationUndefined,
};
enum AccessibilityObjectInclusion {
@@ -266,6 +299,7 @@ enum AccessibilitySortDirection {
SortDirectionNone,
SortDirectionAscending,
SortDirectionDescending,
+ SortDirectionOther
};
enum AccessibilitySearchDirection {
@@ -301,6 +335,7 @@ enum AccessibilitySearchKey {
ListSearchKey,
LiveRegionSearchKey,
MisspelledWordSearchKey,
+ OutlineSearchKey,
PlainTextSearchKey,
RadioGroupSearchKey,
SameTypeSearchKey,
@@ -314,22 +349,31 @@ enum AccessibilitySearchKey {
VisitedLinkSearchKey
};
+enum AccessibilityVisiblePositionForBounds {
+ FirstVisiblePositionForBounds,
+ LastVisiblePositionForBounds
+};
+
struct AccessibilitySearchCriteria {
AccessibilityObject* startObject;
AccessibilitySearchDirection searchDirection;
Vector<AccessibilitySearchKey> searchKeys;
- String* searchText;
+ String searchText;
unsigned resultsLimit;
bool visibleOnly;
+ bool immediateDescendantsOnly;
- AccessibilitySearchCriteria(AccessibilityObject* o, AccessibilitySearchDirection d, String* t, unsigned l, bool v)
- : startObject(o)
- , searchDirection(d)
- , searchText(t)
- , resultsLimit(l)
- , visibleOnly(v)
+ AccessibilitySearchCriteria(AccessibilityObject* startObject, AccessibilitySearchDirection searchDirection, String searchText, unsigned resultsLimit, bool visibleOnly, bool immediateDescendantsOnly)
+ : startObject(startObject)
+ , searchDirection(searchDirection)
+ , searchText(searchText)
+ , resultsLimit(resultsLimit)
+ , visibleOnly(visibleOnly)
+ , immediateDescendantsOnly(immediateDescendantsOnly)
{ }
};
+
+enum AccessibilityDetachmentType { CacheDestroyed, ElementDestroyed };
struct VisiblePositionRange {
@@ -364,6 +408,38 @@ struct PlainTextRange {
bool isNull() const { return !start && !length; }
};
+enum AccessibilitySelectTextActivity {
+ FindAndReplaceActivity,
+ FindAndSelectActivity,
+ FindAndCapitalize,
+ FindAndLowercase,
+ FindAndUppercase
+};
+
+enum AccessibilitySelectTextAmbiguityResolution {
+ ClosestAfterSelectionAmbiguityResolution,
+ ClosestBeforeSelectionAmbiguityResolution,
+ ClosestToSelectionAmbiguityResolution
+};
+
+struct AccessibilitySelectTextCriteria {
+ AccessibilitySelectTextActivity activity;
+ AccessibilitySelectTextAmbiguityResolution ambiguityResolution;
+ String replacementString;
+ Vector<String> searchStrings;
+
+ AccessibilitySelectTextCriteria(AccessibilitySelectTextActivity activity, AccessibilitySelectTextAmbiguityResolution ambiguityResolution, const String& replacementString)
+ : activity(activity)
+ , ambiguityResolution(ambiguityResolution)
+ , replacementString(replacementString)
+ { }
+};
+
+enum AccessibilityMathScriptObjectType { Subscript, Superscript };
+enum AccessibilityMathMultiscriptObjectType { PreSubscript, PreSuperscript, PostSubscript, PostSuperscript };
+
+enum AccessibilityARIACurrentState { ARIACurrentFalse, ARIACurrentTrue, ARIACurrentPage, ARIACurrentStep, ARIACurrentLocation, ARIACurrentDate, ARIACurrentTime };
+
class AccessibilityObject : public RefCounted<AccessibilityObject> {
protected:
AccessibilityObject();
@@ -379,10 +455,10 @@ public:
// When the corresponding WebCore object that this AccessibilityObject
// wraps is deleted, it must be detached.
- virtual void detach();
+ virtual void detach(AccessibilityDetachmentType, AXObjectCache* cache = nullptr);
virtual bool isDetached() const;
- typedef Vector<RefPtr<AccessibilityObject> > AccessibilityChildrenVector;
+ typedef Vector<RefPtr<AccessibilityObject>> AccessibilityChildrenVector;
virtual bool isAccessibilityNodeObject() const { return false; }
virtual bool isAccessibilityRenderObject() const { return false; }
@@ -391,22 +467,24 @@ public:
virtual bool isAccessibilitySVGRoot() const { return false; }
bool accessibilityObjectContainsText(String *) const;
-
- virtual bool isAnchor() const { return false; }
+
virtual bool isAttachment() const { return false; }
virtual bool isHeading() const { return false; }
virtual bool isLink() const { return false; }
virtual bool isImage() const { return false; }
+ virtual bool isImageMap() const { return roleValue() == ImageMapRole; }
virtual bool isNativeImage() const { return false; }
virtual bool isImageButton() const { return false; }
virtual bool isPasswordField() const { return false; }
+ bool isContainedByPasswordField() const;
+ virtual AccessibilityObject* passwordFieldOrContainingPasswordField() { return nullptr; }
virtual bool isNativeTextControl() const { return false; }
virtual bool isSearchField() const { return false; }
bool isWebArea() const { return roleValue() == WebAreaRole; }
- bool isSeamlessWebArea() const { return roleValue() == SeamlessWebAreaRole; }
virtual bool isCheckbox() const { return roleValue() == CheckBoxRole; }
virtual bool isRadioButton() const { return roleValue() == RadioButtonRole; }
virtual bool isListBox() const { return roleValue() == ListBoxRole; }
+ virtual bool isListBoxOption() const { return false; }
virtual bool isMediaTimeline() const { return false; }
virtual bool isMenuRelated() const { return false; }
virtual bool isMenu() const { return false; }
@@ -417,11 +495,11 @@ public:
virtual bool isInputImage() const { return false; }
virtual bool isProgressIndicator() const { return false; }
virtual bool isSlider() const { return false; }
+ virtual bool isSliderThumb() const { return false; }
virtual bool isInputSlider() const { return false; }
virtual bool isControl() const { return false; }
virtual bool isList() const { return false; }
virtual bool isTable() const { return false; }
- virtual bool isAccessibilityTable() const { return false; }
virtual bool isDataTable() const { return false; }
virtual bool isTableRow() const { return false; }
virtual bool isTableColumn() const { return false; }
@@ -438,8 +516,11 @@ public:
virtual bool isSpinButtonPart() const { return false; }
virtual bool isMockObject() const { return false; }
virtual bool isMediaControlLabel() const { return false; }
+ bool isSwitch() const { return roleValue() == SwitchRole; }
+ bool isToggleButton() const { return roleValue() == ToggleButtonRole; }
bool isTextControl() const;
bool isARIATextControl() const;
+ bool isNonNativeTextControl() const;
bool isTabList() const { return roleValue() == TabListRole; }
bool isTabItem() const { return roleValue() == TabRole; }
bool isRadioGroup() const { return roleValue() == RadioGroupRole; }
@@ -457,7 +538,11 @@ public:
bool isLandmark() const;
bool isColorWell() const { return roleValue() == ColorWellRole; }
bool isRangeControl() const;
-
+ bool isMeter() const;
+ bool isSplitter() const { return roleValue() == SplitterRole; }
+ bool isToolbar() const { return roleValue() == ToolbarRole; }
+ bool isStyleFormatGroup() const;
+
virtual bool isChecked() const { return false; }
virtual bool isEnabled() const { return false; }
virtual bool isSelected() const { return false; }
@@ -468,7 +553,6 @@ public:
virtual bool isMultiSelectable() const { return false; }
virtual bool isOffScreen() const { return false; }
virtual bool isPressed() const { return false; }
- virtual bool isReadOnly() const { return false; }
virtual bool isUnvisited() const { return false; }
virtual bool isVisited() const { return false; }
virtual bool isRequired() const { return false; }
@@ -493,6 +577,8 @@ public:
virtual bool hasUnderline() const { return false; }
bool hasHighlighting() const;
+ bool supportsDatetimeAttribute() const;
+
virtual bool canSetFocusAttribute() const { return false; }
virtual bool canSetTextRangeAttributes() const { return false; }
virtual bool canSetValueAttribute() const { return false; }
@@ -501,8 +587,9 @@ public:
virtual bool canSetSelectedChildrenAttribute() const { return false; }
virtual bool canSetExpandedAttribute() const { return false; }
- virtual Node* node() const { return 0; }
- virtual RenderObject* renderer() const { return 0; }
+ virtual Element* element() const;
+ virtual Node* node() const { return nullptr; }
+ virtual RenderObject* renderer() const { return nullptr; }
virtual bool accessibilityIsIgnored() const;
virtual AccessibilityObjectInclusion defaultObjectInclusion() const;
bool accessibilityIsIgnoredByDefault() const;
@@ -516,8 +603,8 @@ public:
virtual float maxValueForRange() const { return 0.0f; }
virtual float minValueForRange() const { return 0.0f; }
virtual float stepValueForRange() const { return 0.0f; }
- virtual AccessibilityObject* selectedRadioButton() { return 0; }
- virtual AccessibilityObject* selectedTabItem() { return 0; }
+ virtual AccessibilityObject* selectedRadioButton() { return nullptr; }
+ virtual AccessibilityObject* selectedTabItem() { return nullptr; }
virtual int layoutCount() const { return 0; }
virtual double estimatedLoadingProgress() const { return 0; }
static bool isARIAControl(AccessibilityRole);
@@ -526,15 +613,29 @@ public:
virtual void ariaOwnsElements(AccessibilityChildrenVector&) const { }
virtual bool supportsARIAFlowTo() const { return false; }
virtual void ariaFlowToElements(AccessibilityChildrenVector&) const { }
+ virtual bool supportsARIADescribedBy() const { return false; }
+ virtual void ariaDescribedByElements(AccessibilityChildrenVector&) const { }
+ virtual bool supportsARIAControls() const { return false; }
+ virtual void ariaControlsElements(AccessibilityChildrenVector&) const { }
virtual bool ariaHasPopup() const { return false; }
- virtual bool ariaPressedIsPresent() const;
+ bool ariaPressedIsPresent() const;
bool ariaIsMultiline() const;
- virtual const AtomicString& invalidStatus() const;
- bool supportsARIAExpanded() const;
+ String invalidStatus() const;
+ bool supportsARIAPressed() const;
+ bool supportsExpanded() const;
+ bool supportsChecked() const;
AccessibilitySortDirection sortDirection() const;
virtual bool canvasHasFallbackContent() const { return false; }
bool supportsRangeValue() const;
-
+ String identifierAttribute() const;
+ void classList(Vector<String>&) const;
+ const AtomicString& roleDescription() const;
+ AccessibilityARIACurrentState ariaCurrentState() const;
+
+ // This function checks if the object should be ignored when there's a modal dialog displayed.
+ bool ignoredFromARIAModalPresence() const;
+ bool isAriaModalDescendant(Node*) const;
+
bool supportsARIASetSize() const;
bool supportsARIAPosInSet() const;
int ariaSetSize() const;
@@ -548,40 +649,51 @@ public:
virtual void determineARIADropEffects(Vector<String>&) { }
// Called on the root AX object to return the deepest available element.
- virtual AccessibilityObject* accessibilityHitTest(const IntPoint&) const { return 0; }
+ virtual AccessibilityObject* accessibilityHitTest(const IntPoint&) const { return nullptr; }
// Called on the AX object after the render tree determines which is the right AccessibilityRenderObject.
virtual AccessibilityObject* elementAccessibilityHitTest(const IntPoint&) const;
virtual AccessibilityObject* focusedUIElement() const;
- virtual AccessibilityObject* firstChild() const { return 0; }
- virtual AccessibilityObject* lastChild() const { return 0; }
- virtual AccessibilityObject* previousSibling() const { return 0; }
- virtual AccessibilityObject* nextSibling() const { return 0; }
+ virtual AccessibilityObject* firstChild() const { return nullptr; }
+ virtual AccessibilityObject* lastChild() const { return nullptr; }
+ virtual AccessibilityObject* previousSibling() const { return nullptr; }
+ virtual AccessibilityObject* nextSibling() const { return nullptr; }
+ virtual AccessibilityObject* nextSiblingUnignored(int limit) const;
+ virtual AccessibilityObject* previousSiblingUnignored(int limit) const;
virtual AccessibilityObject* parentObject() const = 0;
virtual AccessibilityObject* parentObjectUnignored() const;
- virtual AccessibilityObject* parentObjectIfExists() const { return 0; }
+ virtual AccessibilityObject* parentObjectIfExists() const { return nullptr; }
static AccessibilityObject* firstAccessibleObjectFromNode(const Node*);
void findMatchingObjects(AccessibilitySearchCriteria*, AccessibilityChildrenVector&);
virtual bool isDescendantOfBarrenParent() const { return false; }
- virtual AccessibilityObject* observableObject() const { return 0; }
+ // Text selection
+ RefPtr<Range> rangeOfStringClosestToRangeInDirection(Range*, AccessibilitySearchDirection, Vector<String>&) const;
+ RefPtr<Range> selectionRange() const;
+ String selectText(AccessibilitySelectTextCriteria*);
+
+ virtual AccessibilityObject* observableObject() const { return nullptr; }
virtual void linkedUIElements(AccessibilityChildrenVector&) const { }
- virtual AccessibilityObject* titleUIElement() const { return 0; }
+ virtual AccessibilityObject* titleUIElement() const { return nullptr; }
virtual bool exposesTitleUIElement() const { return true; }
- virtual AccessibilityObject* correspondingLabelForControlElement() const { return 0; }
- virtual AccessibilityObject* correspondingControlForLabelElement() const { return 0; }
- virtual AccessibilityObject* scrollBar(AccessibilityOrientation) { return 0; }
+ virtual AccessibilityObject* correspondingLabelForControlElement() const { return nullptr; }
+ virtual AccessibilityObject* correspondingControlForLabelElement() const { return nullptr; }
+ virtual AccessibilityObject* scrollBar(AccessibilityOrientation) { return nullptr; }
virtual AccessibilityRole ariaRoleAttribute() const { return UnknownRole; }
virtual bool isPresentationalChildOfAriaRole() const { return false; }
virtual bool ariaRoleHasPresentationalChildren() const { return false; }
+ virtual bool inheritsPresentationalRole() const { return false; }
// Accessibility Text
virtual void accessibilityText(Vector<AccessibilityText>&) { };
-
+ // A single method for getting a computed label for an AXObject. It condenses the nuances of accessibilityText. Used by Inspector.
+ String computedLabel();
+
// A programmatic way to set a name on an AccessibleObject.
virtual void setAccessibleName(const AtomicString&) { }
+ virtual bool hasAttributesRequiredForInclusion() const;
// Accessibility Text - (To be deprecated).
virtual String accessibilityDescription() const { return String(); }
@@ -590,31 +702,37 @@ public:
// Methods for determining accessibility text.
virtual String stringValue() const { return String(); }
- virtual String textUnderElement(AccessibilityTextUnderElementMode = TextUnderElementModeSkipIgnoredChildren) const { return String(); }
+ virtual String textUnderElement(AccessibilityTextUnderElementMode = AccessibilityTextUnderElementMode()) const { return String(); }
virtual String text() const { return String(); }
virtual int textLength() const { return 0; }
virtual String ariaLabeledByAttribute() const { return String(); }
virtual String ariaDescribedByAttribute() const { return String(); }
const AtomicString& placeholderValue() const;
+ bool accessibleNameDerivesFromContent() const;
+
+ // Abbreviations
+ virtual String expandedTextValue() const { return String(); }
+ virtual bool supportsExpandedTextValue() const { return false; }
+
+ void elementsFromAttribute(Vector<Element*>&, const QualifiedName&) const;
// Only if isColorWell()
virtual void colorValue(int& r, int& g, int& b) const { r = 0; g = 0; b = 0; }
- void setRoleValue(AccessibilityRole role) { m_role = role; }
virtual AccessibilityRole roleValue() const { return m_role; }
virtual AXObjectCache* axObjectCache() const;
AXID axObjectID() const { return m_id; }
static AccessibilityObject* anchorElementForNode(Node*);
- virtual Element* anchorElement() const { return 0; }
- virtual Element* actionElement() const { return 0; }
+ static AccessibilityObject* headingElementForNode(Node*);
+ virtual Element* anchorElement() const { return nullptr; }
+ bool supportsPressAction() const;
+ virtual Element* actionElement() const { return nullptr; }
virtual LayoutRect boundingBoxRect() const { return LayoutRect(); }
- IntRect pixelSnappedBoundingBoxRect() const { return pixelSnappedIntRect(boundingBoxRect()); }
+ IntRect pixelSnappedBoundingBoxRect() const { return snappedIntRect(boundingBoxRect()); }
virtual LayoutRect elementRect() const = 0;
- IntRect pixelSnappedElementRect() const { return pixelSnappedIntRect(elementRect()); }
LayoutSize size() const { return elementRect().size(); }
- IntSize pixelSnappedSize() const { return elementRect().pixelSnappedSize(); }
virtual IntPoint clickPoint();
static IntRect boundingBoxForQuads(RenderObject*, const Vector<FloatQuad>&);
virtual Path elementPath() const { return Path(); }
@@ -625,17 +743,20 @@ public:
unsigned selectionStart() const { return selectedTextRange().start; }
unsigned selectionEnd() const { return selectedTextRange().length; }
- virtual KURL url() const { return KURL(); }
+ virtual URL url() const { return URL(); }
virtual VisibleSelection selection() const { return VisibleSelection(); }
virtual String selectedText() const { return String(); }
virtual const AtomicString& accessKey() const { return nullAtom; }
const String& actionVerb() const;
- virtual Widget* widget() const { return 0; }
- virtual Widget* widgetForAttachmentView() const { return 0; }
+ virtual Widget* widget() const { return nullptr; }
+ virtual Widget* widgetForAttachmentView() const { return nullptr; }
Page* page() const;
virtual Document* document() const;
- virtual FrameView* topDocumentFrameView() const { return 0; }
virtual FrameView* documentFrameView() const;
+ Frame* frame() const;
+ MainFrame* mainFrame() const;
+ Document* topDocument() const;
+ ScrollView* scrollViewAncestor() const;
String language() const;
// 1-based, to match the aria-level spec.
virtual unsigned hierarchicalLevel() const { return 0; }
@@ -649,8 +770,8 @@ public:
virtual void setSelectedRows(AccessibilityChildrenVector&) { }
virtual void makeRangeVisible(const PlainTextRange&) { }
- virtual bool press() const;
- bool performDefaultAction() const { return press(); }
+ virtual bool press();
+ bool performDefaultAction() { return press(); }
virtual AccessibilityOrientation orientation() const;
virtual void increment() { }
@@ -659,27 +780,30 @@ public:
virtual void childrenChanged() { }
virtual void textChanged() { }
virtual void updateAccessibilityRole() { }
- const AccessibilityChildrenVector& children();
+ const AccessibilityChildrenVector& children(bool updateChildrenIfNeeded = true);
virtual void addChildren() { }
virtual void addChild(AccessibilityObject*) { }
virtual void insertChild(AccessibilityObject*, unsigned) { }
+
+ virtual bool shouldIgnoreAttributeRole() const { return false; }
virtual bool canHaveChildren() const { return true; }
virtual bool hasChildren() const { return m_haveChildren; }
virtual void updateChildrenIfNecessary();
virtual void setNeedsToUpdateChildren() { }
virtual void clearChildren();
-#if PLATFORM(MAC)
+#if PLATFORM(COCOA)
virtual void detachFromParent();
#else
virtual void detachFromParent() { }
#endif
+ virtual bool isDetachedFromParent() { return false; }
virtual void selectedChildren(AccessibilityChildrenVector&) { }
virtual void visibleChildren(AccessibilityChildrenVector&) { }
virtual void tabChildren(AccessibilityChildrenVector&) { }
virtual bool shouldFocusActiveDescendant() const { return false; }
- virtual AccessibilityObject* activeDescendant() const { return 0; }
+ virtual AccessibilityObject* activeDescendant() const { return nullptr; }
virtual void handleActiveDescendantChanged() { }
virtual void handleAriaExpandedChanged() { }
bool isDescendantOfObject(const AccessibilityObject*) const;
@@ -689,10 +813,14 @@ public:
static AccessibilityRole ariaRoleToWebCoreRole(const String&);
bool hasAttribute(const QualifiedName&) const;
const AtomicString& getAttribute(const QualifiedName&) const;
+ bool hasTagName(const QualifiedName&) const;
virtual VisiblePositionRange visiblePositionRange() const { return VisiblePositionRange(); }
virtual VisiblePositionRange visiblePositionRangeForLine(unsigned) const { return VisiblePositionRange(); }
+ RefPtr<Range> elementRange() const;
+ static bool replacedNodeNeedsCharacter(Node* replacedNode);
+
VisiblePositionRange visiblePositionRangeForUnorderedPositions(const VisiblePosition&, const VisiblePosition&) const;
VisiblePositionRange positionOfLeftWord(const VisiblePosition&) const;
VisiblePositionRange positionOfRightWord(const VisiblePosition&) const;
@@ -702,12 +830,15 @@ public:
VisiblePositionRange paragraphForPosition(const VisiblePosition&) const;
VisiblePositionRange styleRangeForPosition(const VisiblePosition&) const;
VisiblePositionRange visiblePositionRangeForRange(const PlainTextRange&) const;
+ VisiblePositionRange lineRangeForPosition(const VisiblePosition&) const;
String stringForVisiblePositionRange(const VisiblePositionRange&) const;
+ String stringForRange(RefPtr<Range>) const;
virtual IntRect boundsForVisiblePositionRange(const VisiblePositionRange&) const { return IntRect(); }
int lengthForVisiblePositionRange(const VisiblePositionRange&) const;
virtual void setSelectedVisiblePositionRange(const VisiblePositionRange&) const { }
+ VisiblePosition visiblePositionForBounds(const IntRect&, AccessibilityVisiblePositionForBounds) const;
virtual VisiblePosition visiblePositionForPoint(const IntPoint&) const { return VisiblePosition(); }
VisiblePosition nextVisiblePosition(const VisiblePosition& visiblePos) const { return visiblePos.next(); }
VisiblePosition previousVisiblePosition(const VisiblePosition& visiblePos) const { return visiblePos.previous(); }
@@ -741,6 +872,8 @@ public:
unsigned doAXLineForIndex(unsigned);
+ String computedRoleString() const;
+
virtual String stringValueForMSAA() const { return String(); }
virtual String stringRoleForMSAA() const { return String(); }
virtual String nameForMSAA() const { return String(); }
@@ -748,7 +881,8 @@ public:
virtual AccessibilityRole roleValueForMSAA() const { return roleValue(); }
virtual String passwordFieldValue() const { return String(); }
-
+ bool isValueAutofilled() const;
+
// Used by an ARIA tree to get all its rows.
void ariaTreeRows(AccessibilityChildrenVector&);
// Used by an ARIA tree item to get all of its direct rows that it can disclose.
@@ -759,10 +893,17 @@ public:
// ARIA live-region features.
bool supportsARIALiveRegion() const;
bool isInsideARIALiveRegion() const;
- virtual const AtomicString& ariaLiveRegionStatus() const { return nullAtom; }
+ virtual const String ariaLiveRegionStatus() const { return String(); }
virtual const AtomicString& ariaLiveRegionRelevant() const { return nullAtom; }
virtual bool ariaLiveRegionAtomic() const { return false; }
virtual bool ariaLiveRegionBusy() const { return false; }
+ static const String defaultLiveRegionStatusForRole(AccessibilityRole);
+ static bool liveRegionStatusIsEnabled(const AtomicString&);
+ static bool contentEditableAttributeIsEnabled(Element*);
+ bool hasContentEditableAttributeSet() const;
+
+ bool supportsARIAReadOnly() const;
+ String ariaReadOnlyValue() const;
bool supportsARIAAttributes() const;
@@ -775,7 +916,13 @@ public:
virtual void scrollToMakeVisibleWithSubFocus(const IntRect&) const;
// Scroll this object to a given point in global coordinates of the top-level window.
virtual void scrollToGlobalPoint(const IntPoint&) const;
-
+
+ enum ScrollByPageDirection { Up, Down, Left, Right };
+ bool scrollByPage(ScrollByPageDirection) const;
+ IntPoint scrollPosition() const;
+ IntSize scrollContentsSize() const;
+ IntRect scrollVisibleContentRect() const;
+
bool lastKnownIsIgnoredValue();
void setLastKnownIsIgnoredValue(bool);
@@ -801,23 +948,26 @@ public:
virtual bool isMathTableRow() const { return false; }
virtual bool isMathTableCell() const { return false; }
virtual bool isMathMultiscript() const { return false; }
+ virtual bool isMathToken() const { return false; }
+ virtual bool isMathScriptObject(AccessibilityMathScriptObjectType) const { return false; }
+ virtual bool isMathMultiscriptObject(AccessibilityMathMultiscriptObjectType) const { return false; }
// Root components.
- virtual AccessibilityObject* mathRadicandObject() { return 0; }
- virtual AccessibilityObject* mathRootIndexObject() { return 0; }
+ virtual AccessibilityObject* mathRadicandObject() { return nullptr; }
+ virtual AccessibilityObject* mathRootIndexObject() { return nullptr; }
// Under over components.
- virtual AccessibilityObject* mathUnderObject() { return 0; }
- virtual AccessibilityObject* mathOverObject() { return 0; }
+ virtual AccessibilityObject* mathUnderObject() { return nullptr; }
+ virtual AccessibilityObject* mathOverObject() { return nullptr; }
// Fraction components.
- virtual AccessibilityObject* mathNumeratorObject() { return 0; }
- virtual AccessibilityObject* mathDenominatorObject() { return 0; }
+ virtual AccessibilityObject* mathNumeratorObject() { return nullptr; }
+ virtual AccessibilityObject* mathDenominatorObject() { return nullptr; }
// Subscript/superscript components.
- virtual AccessibilityObject* mathBaseObject() { return 0; }
- virtual AccessibilityObject* mathSubscriptObject() { return 0; }
- virtual AccessibilityObject* mathSuperscriptObject() { return 0; }
+ virtual AccessibilityObject* mathBaseObject() { return nullptr; }
+ virtual AccessibilityObject* mathSubscriptObject() { return nullptr; }
+ virtual AccessibilityObject* mathSuperscriptObject() { return nullptr; }
// Fenced components.
virtual String mathFencedOpenString() const { return String(); }
@@ -825,10 +975,15 @@ public:
virtual int mathLineThickness() const { return 0; }
// Multiscripts components.
- typedef Vector<pair<AccessibilityObject*, AccessibilityObject*> > AccessibilityMathMultiscriptPairs;
+ typedef Vector<std::pair<AccessibilityObject*, AccessibilityObject*>> AccessibilityMathMultiscriptPairs;
virtual void mathPrescripts(AccessibilityMathMultiscriptPairs&) { }
virtual void mathPostscripts(AccessibilityMathMultiscriptPairs&) { }
-
+
+ // Visibility.
+ bool isARIAHidden() const;
+ bool isDOMHidden() const;
+ bool isHidden() const { return isARIAHidden() || isDOMHidden(); }
+
#if HAVE(ACCESSIBILITY)
#if PLATFORM(GTK) || PLATFORM(EFL)
AccessibilityObjectWrapper* wrapper() const;
@@ -842,7 +997,7 @@ public:
#endif
#endif
-#if PLATFORM(MAC)
+#if PLATFORM(COCOA)
void overrideAttachmentParent(AccessibilityObject* parent);
#else
void overrideAttachmentParent(AccessibilityObject*) { }
@@ -858,10 +1013,24 @@ public:
AccessibilityObjectInclusion accessibilityPlatformIncludesObject() const { return DefaultBehavior; }
#endif
+#if PLATFORM(IOS)
+ int accessibilityPasswordFieldLength();
+#endif
+
// allows for an AccessibilityObject to update its render tree or perform
// other operations update type operations
void updateBackingStore();
+#if PLATFORM(COCOA)
+ bool preventKeyboardDOMEventDispatch() const;
+ void setPreventKeyboardDOMEventDispatch(bool);
+#endif
+
+#if PLATFORM(COCOA) && !PLATFORM(IOS)
+ bool caretBrowsingEnabled() const;
+ void setCaretBrowsingEnabled(bool);
+#endif
+
protected:
AXID m_id;
AccessibilityChildrenVector m_children;
@@ -872,18 +1041,20 @@ protected:
virtual bool computeAccessibilityIsIgnored() const { return true; }
// If this object itself scrolls, return its ScrollableArea.
- virtual ScrollableArea* getScrollableAreaIfScrollable() const { return 0; }
+ virtual ScrollableArea* getScrollableAreaIfScrollable() const { return nullptr; }
virtual void scrollTo(const IntPoint&) const { }
-
+ ScrollableArea* scrollableAreaAncestor() const;
+ void scrollAreaAndAncestor(std::pair<ScrollableArea*, AccessibilityObject*>&) const;
+
static bool isAccessibilityObjectSearchMatchAtIndex(AccessibilityObject*, AccessibilitySearchCriteria*, size_t);
static bool isAccessibilityObjectSearchMatch(AccessibilityObject*, AccessibilitySearchCriteria*);
static bool isAccessibilityTextSearchMatch(AccessibilityObject*, AccessibilitySearchCriteria*);
static bool objectMatchesSearchCriteriaWithResultLimit(AccessibilityObject*, AccessibilitySearchCriteria*, AccessibilityChildrenVector&);
virtual AccessibilityRole buttonRoleType() const;
- bool ariaIsHidden() const;
bool isOnscreen() const;
-
-#if PLATFORM(GTK) || (PLATFORM(EFL) && HAVE(ACCESSIBILITY))
+ bool dispatchTouchEvent();
+
+#if (PLATFORM(GTK) || PLATFORM(EFL)) && HAVE(ACCESSIBILITY)
bool allowsTextRanges() const;
unsigned getLengthForTextRange() const;
#else
@@ -891,7 +1062,7 @@ protected:
unsigned getLengthForTextRange() const { return text().length(); }
#endif
-#if PLATFORM(MAC)
+#if PLATFORM(COCOA)
RetainPtr<WebAccessibilityObjectWrapper> m_wrapper;
#elif PLATFORM(WIN)
COMPtr<AccessibilityObjectWrapper> m_wrapper;
@@ -901,7 +1072,7 @@ protected:
};
#if !HAVE(ACCESSIBILITY)
-inline const AccessibilityObject::AccessibilityChildrenVector& AccessibilityObject::children() { return m_children; }
+inline const AccessibilityObject::AccessibilityChildrenVector& AccessibilityObject::children(bool) { return m_children; }
inline const String& AccessibilityObject::actionVerb() const { return emptyString(); }
inline int AccessibilityObject::lineForPosition(const VisiblePosition&) const { return -1; }
inline void AccessibilityObject::updateBackingStore() { }
@@ -909,4 +1080,9 @@ inline void AccessibilityObject::updateBackingStore() { }
} // namespace WebCore
+#define SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(ToValueTypeName, predicate) \
+SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::ToValueTypeName) \
+ static bool isType(const WebCore::AccessibilityObject& object) { return object.predicate; } \
+SPECIALIZE_TYPE_TRAITS_END()
+
#endif // AccessibilityObject_h
diff --git a/Source/WebCore/accessibility/AccessibilityProgressIndicator.cpp b/Source/WebCore/accessibility/AccessibilityProgressIndicator.cpp
index 1dac10988..19485396f 100644
--- a/Source/WebCore/accessibility/AccessibilityProgressIndicator.cpp
+++ b/Source/WebCore/accessibility/AccessibilityProgressIndicator.cpp
@@ -21,7 +21,7 @@
#include "config.h"
#include "AccessibilityProgressIndicator.h"
-#if ENABLE(PROGRESS_ELEMENT) || ENABLE(METER_ELEMENT)
+#include "AXObjectCache.h"
#include "FloatConversion.h"
#include "HTMLMeterElement.h"
#include "HTMLNames.h"
@@ -34,17 +34,15 @@ namespace WebCore {
using namespace HTMLNames;
-#if ENABLE(PROGRESS_ELEMENT)
AccessibilityProgressIndicator::AccessibilityProgressIndicator(RenderProgress* renderer)
: AccessibilityRenderObject(renderer)
{
}
-PassRefPtr<AccessibilityProgressIndicator> AccessibilityProgressIndicator::create(RenderProgress* renderer)
+Ref<AccessibilityProgressIndicator> AccessibilityProgressIndicator::create(RenderProgress* renderer)
{
- return adoptRef(new AccessibilityProgressIndicator(renderer));
+ return adoptRef(*new AccessibilityProgressIndicator(renderer));
}
-#endif
#if ENABLE(METER_ELEMENT)
AccessibilityProgressIndicator::AccessibilityProgressIndicator(RenderMeter* renderer)
@@ -52,9 +50,9 @@ AccessibilityProgressIndicator::AccessibilityProgressIndicator(RenderMeter* rend
{
}
-PassRefPtr<AccessibilityProgressIndicator> AccessibilityProgressIndicator::create(RenderMeter* renderer)
+Ref<AccessibilityProgressIndicator> AccessibilityProgressIndicator::create(RenderMeter* renderer)
{
- return adoptRef(new AccessibilityProgressIndicator(renderer));
+ return adoptRef(*new AccessibilityProgressIndicator(renderer));
}
#endif
@@ -63,18 +61,46 @@ bool AccessibilityProgressIndicator::computeAccessibilityIsIgnored() const
return accessibilityIsIgnoredByDefault();
}
+String AccessibilityProgressIndicator::valueDescription() const
+{
+ // If the author has explicitly provided a value through aria-valuetext, use it.
+ String description = AccessibilityRenderObject::valueDescription();
+ if (!description.isEmpty())
+ return description;
+
+#if ENABLE(METER_ELEMENT)
+ if (!m_renderer->isMeter())
+ return String();
+
+ HTMLMeterElement* meter = meterElement();
+ if (!meter)
+ return String();
+
+ // The HTML spec encourages authors to include a textual representation of the meter's state in
+ // the element's contents. We'll fall back on that if there is not a more accessible alternative.
+ AccessibilityObject* axMeter = axObjectCache()->getOrCreate(meter);
+ if (is<AccessibilityNodeObject>(axMeter)) {
+ description = downcast<AccessibilityNodeObject>(axMeter)->accessibilityDescriptionForChildren();
+ if (!description.isEmpty())
+ return description;
+ }
+
+ return meter->textContent();
+#endif
+
+ return String();
+}
+
float AccessibilityProgressIndicator::valueForRange() const
{
if (!m_renderer)
return 0.0;
-#if ENABLE(PROGRESS_ELEMENT)
if (m_renderer->isProgress()) {
HTMLProgressElement* progress = progressElement();
if (progress && progress->position() >= 0)
return narrowPrecisionToFloat(progress->value());
}
-#endif
#if ENABLE(METER_ELEMENT)
if (m_renderer->isMeter()) {
@@ -92,12 +118,10 @@ float AccessibilityProgressIndicator::maxValueForRange() const
if (!m_renderer)
return 0.0;
-#if ENABLE(PROGRESS_ELEMENT)
if (m_renderer->isProgress()) {
if (HTMLProgressElement* progress = progressElement())
return narrowPrecisionToFloat(progress->max());
}
-#endif
#if ENABLE(METER_ELEMENT)
if (m_renderer->isMeter()) {
@@ -114,10 +138,8 @@ float AccessibilityProgressIndicator::minValueForRange() const
if (!m_renderer)
return 0.0;
-#if ENABLE(PROGRESS_ELEMENT)
if (m_renderer->isProgress())
return 0.0;
-#endif
#if ENABLE(METER_ELEMENT)
if (m_renderer->isMeter()) {
@@ -129,27 +151,36 @@ float AccessibilityProgressIndicator::minValueForRange() const
return 0.0;
}
-#if ENABLE(PROGRESS_ELEMENT)
HTMLProgressElement* AccessibilityProgressIndicator::progressElement() const
{
- if (!m_renderer->isProgress())
- return 0;
+ if (!is<RenderProgress>(*m_renderer))
+ return nullptr;
- return toRenderProgress(m_renderer)->progressElement();
+ return downcast<RenderProgress>(*m_renderer).progressElement();
}
-#endif
#if ENABLE(METER_ELEMENT)
HTMLMeterElement* AccessibilityProgressIndicator::meterElement() const
{
- if (!m_renderer->isMeter())
- return 0;
+ if (!is<RenderMeter>(*m_renderer))
+ return nullptr;
- return toRenderMeter(m_renderer)->meterElement();
+ return downcast<RenderMeter>(*m_renderer).meterElement();
}
#endif
-} // namespace WebCore
+Element* AccessibilityProgressIndicator::element() const
+{
+ if (m_renderer->isProgress())
+ return progressElement();
-#endif // ENABLE(PROGRESS_ELEMENT) || ENABLE(METER_ELEMENT)
+#if ENABLE(METER_ELEMENT)
+ if (m_renderer->isMeter())
+ return meterElement();
+#endif
+
+ return AccessibilityObject::element();
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AccessibilityProgressIndicator.h b/Source/WebCore/accessibility/AccessibilityProgressIndicator.h
index 3144c8b0d..3833b2c2c 100644
--- a/Source/WebCore/accessibility/AccessibilityProgressIndicator.h
+++ b/Source/WebCore/accessibility/AccessibilityProgressIndicator.h
@@ -21,7 +21,6 @@
#ifndef AccessibilityProgressIndicator_h
#define AccessibilityProgressIndicator_h
-#if ENABLE(PROGRESS_ELEMENT) || ENABLE(METER_ELEMENT)
#include "AccessibilityRenderObject.h"
namespace WebCore {
@@ -31,44 +30,39 @@ class HTMLMeterElement;
class RenderMeter;
#endif
-#if ENABLE(PROGRESS_ELEMENT)
class HTMLProgressElement;
class RenderProgress;
-#endif
-class AccessibilityProgressIndicator : public AccessibilityRenderObject {
+class AccessibilityProgressIndicator final : public AccessibilityRenderObject {
public:
-#if ENABLE(PROGRESS_ELEMENT)
- static PassRefPtr<AccessibilityProgressIndicator> create(RenderProgress*);
-#endif
+ static Ref<AccessibilityProgressIndicator> create(RenderProgress*);
#if ENABLE(METER_ELEMENT)
- static PassRefPtr<AccessibilityProgressIndicator> create(RenderMeter*);
+ static Ref<AccessibilityProgressIndicator> create(RenderMeter*);
#endif
+ virtual Element* element() const override;
private:
- virtual AccessibilityRole roleValue() const { return ProgressIndicatorRole; }
+ virtual AccessibilityRole roleValue() const override { return ProgressIndicatorRole; }
- virtual bool isProgressIndicator() const { return true; }
+ virtual bool isProgressIndicator() const override { return true; }
- virtual float valueForRange() const;
- virtual float maxValueForRange() const;
- virtual float minValueForRange() const;
+ virtual String valueDescription() const override;
+ virtual float valueForRange() const override;
+ virtual float maxValueForRange() const override;
+ virtual float minValueForRange() const override;
-#if ENABLE(PROGRESS_ELEMENT)
explicit AccessibilityProgressIndicator(RenderProgress*);
HTMLProgressElement* progressElement() const;
-#endif
+
#if ENABLE(METER_ELEMENT)
explicit AccessibilityProgressIndicator(RenderMeter*);
HTMLMeterElement* meterElement() const;
#endif
- virtual bool computeAccessibilityIsIgnored() const;
+ virtual bool computeAccessibilityIsIgnored() const override;
};
} // namespace WebCore
-#endif // ENABLE(PROGRESS_ELEMENT) || ENABLE(METER_ELEMENT)
-
#endif // AccessibilityProgressIndicator_h
diff --git a/Source/WebCore/accessibility/AccessibilityRenderObject.cpp b/Source/WebCore/accessibility/AccessibilityRenderObject.cpp
index ebbc190e4..dff12e007 100644
--- a/Source/WebCore/accessibility/AccessibilityRenderObject.cpp
+++ b/Source/WebCore/accessibility/AccessibilityRenderObject.cpp
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
-* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+* 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -37,12 +37,14 @@
#include "AccessibilityTable.h"
#include "CachedImage.h"
#include "Chrome.h"
+#include "ElementIterator.h"
#include "EventNames.h"
#include "FloatRect.h"
#include "Frame.h"
#include "FrameLoader.h"
#include "FrameSelection.h"
#include "HTMLAreaElement.h"
+#include "HTMLAudioElement.h"
#include "HTMLFormElement.h"
#include "HTMLFrameElementBase.h"
#include "HTMLImageElement.h"
@@ -56,13 +58,13 @@
#include "HTMLSelectElement.h"
#include "HTMLTableElement.h"
#include "HTMLTextAreaElement.h"
+#include "HTMLVideoElement.h"
#include "HitTestRequest.h"
#include "HitTestResult.h"
#include "Image.h"
#include "LocalizedStrings.h"
#include "MathMLNames.h"
#include "NodeList.h"
-#include "NodeTraversal.h"
#include "Page.h"
#include "ProgressTracker.h"
#include "RenderButton.h"
@@ -71,14 +73,20 @@
#include "RenderHTMLCanvas.h"
#include "RenderImage.h"
#include "RenderInline.h"
+#include "RenderIterator.h"
#include "RenderLayer.h"
+#include "RenderLineBreak.h"
#include "RenderListBox.h"
+#include "RenderListItem.h"
#include "RenderListMarker.h"
#include "RenderMathMLBlock.h"
#include "RenderMathMLFraction.h"
#include "RenderMathMLOperator.h"
+#include "RenderMathMLRoot.h"
#include "RenderMenuList.h"
+#include "RenderSVGRoot.h"
#include "RenderSVGShape.h"
+#include "RenderTableCell.h"
#include "RenderText.h"
#include "RenderTextControl.h"
#include "RenderTextControlSingleLine.h"
@@ -89,19 +97,18 @@
#include "RenderedPosition.h"
#include "SVGDocument.h"
#include "SVGImage.h"
-#include "SVGImageChromeClient.h"
#include "SVGNames.h"
#include "SVGSVGElement.h"
#include "Text.h"
#include "TextControlInnerElements.h"
+#include "TextIterator.h"
#include "VisibleUnits.h"
#include "htmlediting.h"
+#include <wtf/NeverDestroyed.h>
#include <wtf/StdLibExtras.h>
#include <wtf/text/StringBuilder.h>
#include <wtf/unicode/CharacterNames.h>
-using namespace std;
-
namespace WebCore {
using namespace HTMLNames;
@@ -125,14 +132,14 @@ void AccessibilityRenderObject::init()
AccessibilityNodeObject::init();
}
-PassRefPtr<AccessibilityRenderObject> AccessibilityRenderObject::create(RenderObject* renderer)
+Ref<AccessibilityRenderObject> AccessibilityRenderObject::create(RenderObject* renderer)
{
- return adoptRef(new AccessibilityRenderObject(renderer));
+ return adoptRef(*new AccessibilityRenderObject(renderer));
}
-void AccessibilityRenderObject::detach()
+void AccessibilityRenderObject::detach(AccessibilityDetachmentType detachmentType, AXObjectCache* cache)
{
- AccessibilityNodeObject::detach();
+ AccessibilityNodeObject::detach(detachmentType, cache);
detachRemoteSVGRoot();
@@ -140,14 +147,14 @@ void AccessibilityRenderObject::detach()
if (m_renderer)
m_renderer->setHasAXObject(false);
#endif
- m_renderer = 0;
+ m_renderer = nullptr;
}
RenderBoxModelObject* AccessibilityRenderObject::renderBoxModelObject() const
{
- if (!m_renderer || !m_renderer->isBoxModelObject())
- return 0;
- return toRenderBoxModelObject(m_renderer);
+ if (!is<RenderBoxModelObject>(m_renderer))
+ return nullptr;
+ return downcast<RenderBoxModelObject>(m_renderer);
}
void AccessibilityRenderObject::setRenderer(RenderObject* renderer)
@@ -156,64 +163,55 @@ void AccessibilityRenderObject::setRenderer(RenderObject* renderer)
setNode(renderer->node());
}
-static inline bool isInlineWithContinuation(RenderObject* object)
+static inline bool isInlineWithContinuation(RenderObject& object)
{
- if (!object->isBoxModelObject())
- return false;
-
- RenderBoxModelObject* renderer = toRenderBoxModelObject(object);
- if (!renderer->isRenderInline())
- return false;
-
- return toRenderInline(renderer)->continuation();
+ return is<RenderInline>(object) && downcast<RenderInline>(object).continuation();
}
-static inline RenderObject* firstChildInContinuation(RenderObject* renderer)
+static inline RenderObject* firstChildInContinuation(RenderInline& renderer)
{
- RenderObject* r = toRenderInline(renderer)->continuation();
+ auto continuation = renderer.continuation();
- while (r) {
- if (r->isRenderBlock())
- return r;
- if (RenderObject* child = r->firstChild())
+ while (continuation) {
+ if (is<RenderBlock>(*continuation))
+ return continuation;
+ if (RenderObject* child = continuation->firstChild())
return child;
- r = toRenderInline(r)->continuation();
+ continuation = downcast<RenderInline>(*continuation).continuation();
}
- return 0;
+ return nullptr;
}
-static inline RenderObject* firstChildConsideringContinuation(RenderObject* renderer)
+static inline RenderObject* firstChildConsideringContinuation(RenderObject& renderer)
{
- RenderObject* firstChild = renderer->firstChild();
+ RenderObject* firstChild = renderer.firstChildSlow();
if (!firstChild && isInlineWithContinuation(renderer))
- firstChild = firstChildInContinuation(renderer);
+ firstChild = firstChildInContinuation(downcast<RenderInline>(renderer));
return firstChild;
}
-static inline RenderObject* lastChildConsideringContinuation(RenderObject* renderer)
+static inline RenderObject* lastChildConsideringContinuation(RenderObject& renderer)
{
- RenderObject* lastChild = renderer->lastChild();
- RenderObject* prev;
- RenderObject* cur = renderer;
-
- if (!cur->isRenderInline() && !cur->isRenderBlock())
- return renderer;
+ if (!is<RenderInline>(renderer) && !is<RenderBlock>(renderer))
+ return &renderer;
- while (cur) {
- prev = cur;
+ RenderObject* lastChild = downcast<RenderBoxModelObject>(renderer).lastChild();
+ RenderBoxModelObject* previous;
+ for (auto* current = &downcast<RenderBoxModelObject>(renderer); current; ) {
+ previous = current;
- if (RenderObject* lc = cur->lastChild())
- lastChild = lc;
+ if (RenderObject* newLastChild = current->lastChild())
+ lastChild = newLastChild;
- if (cur->isRenderInline()) {
- cur = toRenderInline(cur)->inlineElementContinuation();
- ASSERT_UNUSED(prev, cur || !toRenderInline(prev)->continuation());
+ if (is<RenderInline>(*current)) {
+ current = downcast<RenderInline>(*current).inlineElementContinuation();
+ ASSERT_UNUSED(previous, current || !downcast<RenderInline>(*previous).continuation());
} else
- cur = toRenderBlock(cur)->inlineElementContinuation();
+ current = downcast<RenderBlock>(*current).inlineElementContinuation();
}
return lastChild;
@@ -222,9 +220,9 @@ static inline RenderObject* lastChildConsideringContinuation(RenderObject* rende
AccessibilityObject* AccessibilityRenderObject::firstChild() const
{
if (!m_renderer)
- return 0;
+ return nullptr;
- RenderObject* firstChild = firstChildConsideringContinuation(m_renderer);
+ RenderObject* firstChild = firstChildConsideringContinuation(*m_renderer);
// If an object can't have children, then it is using this method to help
// calculate some internal property (like its description).
@@ -239,9 +237,9 @@ AccessibilityObject* AccessibilityRenderObject::firstChild() const
AccessibilityObject* AccessibilityRenderObject::lastChild() const
{
if (!m_renderer)
- return 0;
+ return nullptr;
- RenderObject* lastChild = lastChildConsideringContinuation(m_renderer);
+ RenderObject* lastChild = lastChildConsideringContinuation(*m_renderer);
if (!lastChild && !canHaveChildren())
return AccessibilityNodeObject::lastChild();
@@ -249,103 +247,91 @@ AccessibilityObject* AccessibilityRenderObject::lastChild() const
return axObjectCache()->getOrCreate(lastChild);
}
-static inline RenderInline* startOfContinuations(RenderObject* r)
+static inline RenderInline* startOfContinuations(RenderObject& renderer)
{
- if (r->isInlineElementContinuation()) {
-#if ENABLE(MATHML)
- // MathML elements make anonymous RenderObjects, then set their node to the parent's node.
- // This makes it so that the renderer() != renderer()->node()->renderer()
- // (which is what isInlineElementContinuation() uses as a determinant).
- if (r->node()->isElementNode() && toElement(r->node())->isMathMLElement())
- return 0;
-#endif
-
- return toRenderInline(r->node()->renderer());
- }
+ if (renderer.isInlineElementContinuation() && is<RenderInline>(renderer.node()->renderer()))
+ return downcast<RenderInline>(renderer.node()->renderer());
// Blocks with a previous continuation always have a next continuation
- if (r->isRenderBlock() && toRenderBlock(r)->inlineElementContinuation())
- return toRenderInline(toRenderBlock(r)->inlineElementContinuation()->node()->renderer());
+ if (is<RenderBlock>(renderer) && downcast<RenderBlock>(renderer).inlineElementContinuation())
+ return downcast<RenderInline>(downcast<RenderBlock>(renderer).inlineElementContinuation()->element()->renderer());
- return 0;
+ return nullptr;
}
-static inline RenderObject* endOfContinuations(RenderObject* renderer)
+static inline RenderObject* endOfContinuations(RenderObject& renderer)
{
- RenderObject* prev = renderer;
- RenderObject* cur = renderer;
+ if (!is<RenderInline>(renderer) && !is<RenderBlock>(renderer))
+ return &renderer;
- if (!cur->isRenderInline() && !cur->isRenderBlock())
- return renderer;
-
- while (cur) {
- prev = cur;
- if (cur->isRenderInline()) {
- cur = toRenderInline(cur)->inlineElementContinuation();
- ASSERT(cur || !toRenderInline(prev)->continuation());
+ auto* previous = &downcast<RenderBoxModelObject>(renderer);
+ for (auto* current = previous; current; ) {
+ previous = current;
+ if (is<RenderInline>(*current)) {
+ current = downcast<RenderInline>(*current).inlineElementContinuation();
+ ASSERT(current || !downcast<RenderInline>(*previous).continuation());
} else
- cur = toRenderBlock(cur)->inlineElementContinuation();
+ current = downcast<RenderBlock>(*current).inlineElementContinuation();
}
- return prev;
+ return previous;
}
-static inline RenderObject* childBeforeConsideringContinuations(RenderInline* r, RenderObject* child)
+static inline RenderObject* childBeforeConsideringContinuations(RenderInline* renderer, RenderObject* child)
{
- RenderBoxModelObject* curContainer = r;
- RenderObject* cur = 0;
- RenderObject* prev = 0;
-
- while (curContainer) {
- if (curContainer->isRenderInline()) {
- cur = curContainer->firstChild();
- while (cur) {
- if (cur == child)
- return prev;
- prev = cur;
- cur = cur->nextSibling();
+ RenderObject* previous = nullptr;
+ for (RenderBoxModelObject* currentContainer = renderer; currentContainer; ) {
+ if (is<RenderInline>(*currentContainer)) {
+ auto* current = currentContainer->firstChild();
+ while (current) {
+ if (current == child)
+ return previous;
+ previous = current;
+ current = current->nextSibling();
}
- curContainer = toRenderInline(curContainer)->continuation();
- } else if (curContainer->isRenderBlock()) {
- if (curContainer == child)
- return prev;
+ currentContainer = downcast<RenderInline>(*currentContainer).continuation();
+ } else if (is<RenderBlock>(*currentContainer)) {
+ if (currentContainer == child)
+ return previous;
- prev = curContainer;
- curContainer = toRenderBlock(curContainer)->inlineElementContinuation();
+ previous = currentContainer;
+ currentContainer = downcast<RenderBlock>(*currentContainer).inlineElementContinuation();
}
}
ASSERT_NOT_REACHED();
-
- return 0;
+ return nullptr;
}
-static inline bool firstChildIsInlineContinuation(RenderObject* renderer)
+static inline bool firstChildIsInlineContinuation(RenderElement& renderer)
{
- return renderer->firstChild() && renderer->firstChild()->isInlineElementContinuation();
+ RenderObject* child = renderer.firstChild();
+ return child && child->isInlineElementContinuation();
}
AccessibilityObject* AccessibilityRenderObject::previousSibling() const
{
if (!m_renderer)
- return 0;
+ return nullptr;
- RenderObject* previousSibling = 0;
+ RenderObject* previousSibling = nullptr;
// Case 1: The node is a block and is an inline's continuation. In that case, the inline's
// last child is our previous sibling (or further back in the continuation chain)
RenderInline* startOfConts;
- if (m_renderer->isRenderBlock() && (startOfConts = startOfContinuations(m_renderer)))
+ if (is<RenderBox>(*m_renderer) && (startOfConts = startOfContinuations(*m_renderer)))
previousSibling = childBeforeConsideringContinuations(startOfConts, m_renderer);
// Case 2: Anonymous block parent of the end of a continuation - skip all the way to before
// the parent of the start, since everything in between will be linked up via the continuation.
- else if (m_renderer->isAnonymousBlock() && firstChildIsInlineContinuation(m_renderer)) {
- RenderObject* firstParent = startOfContinuations(m_renderer->firstChild())->parent();
- while (firstChildIsInlineContinuation(firstParent))
- firstParent = startOfContinuations(firstParent->firstChild())->parent();
+ else if (m_renderer->isAnonymousBlock() && firstChildIsInlineContinuation(downcast<RenderBlock>(*m_renderer))) {
+ RenderBlock& renderBlock = downcast<RenderBlock>(*m_renderer);
+ auto* firstParent = startOfContinuations(*renderBlock.firstChild())->parent();
+ ASSERT(firstParent);
+ while (firstChildIsInlineContinuation(*firstParent))
+ firstParent = startOfContinuations(*firstParent->firstChild())->parent();
previousSibling = firstParent->previousSibling();
}
@@ -355,39 +341,41 @@ AccessibilityObject* AccessibilityRenderObject::previousSibling() const
// Case 4: This node has no previous siblings, but its parent is an inline,
// and is another node's inline continutation. Follow the continuation chain.
- else if (m_renderer->parent()->isRenderInline() && (startOfConts = startOfContinuations(m_renderer->parent())))
+ else if (is<RenderInline>(*m_renderer->parent()) && (startOfConts = startOfContinuations(*m_renderer->parent())))
previousSibling = childBeforeConsideringContinuations(startOfConts, m_renderer->parent()->firstChild());
if (!previousSibling)
- return 0;
+ return nullptr;
return axObjectCache()->getOrCreate(previousSibling);
}
-static inline bool lastChildHasContinuation(RenderObject* renderer)
+static inline bool lastChildHasContinuation(RenderElement& renderer)
{
- return renderer->lastChild() && isInlineWithContinuation(renderer->lastChild());
+ RenderObject* child = renderer.lastChild();
+ return child && isInlineWithContinuation(*child);
}
AccessibilityObject* AccessibilityRenderObject::nextSibling() const
{
if (!m_renderer)
- return 0;
+ return nullptr;
- RenderObject* nextSibling = 0;
+ RenderObject* nextSibling = nullptr;
// Case 1: node is a block and has an inline continuation. Next sibling is the inline continuation's
// first child.
RenderInline* inlineContinuation;
- if (m_renderer->isRenderBlock() && (inlineContinuation = toRenderBlock(m_renderer)->inlineElementContinuation()))
- nextSibling = firstChildConsideringContinuation(inlineContinuation);
+ if (is<RenderBlock>(*m_renderer) && (inlineContinuation = downcast<RenderBlock>(*m_renderer).inlineElementContinuation()))
+ nextSibling = firstChildConsideringContinuation(*inlineContinuation);
// Case 2: Anonymous block parent of the start of a continuation - skip all the way to
// after the parent of the end, since everything in between will be linked up via the continuation.
- else if (m_renderer->isAnonymousBlock() && lastChildHasContinuation(m_renderer)) {
- RenderObject* lastParent = endOfContinuations(m_renderer->lastChild())->parent();
- while (lastChildHasContinuation(lastParent))
- lastParent = endOfContinuations(lastParent->lastChild())->parent();
+ else if (m_renderer->isAnonymousBlock() && lastChildHasContinuation(downcast<RenderBlock>(*m_renderer))) {
+ RenderElement* lastParent = endOfContinuations(*downcast<RenderBlock>(*m_renderer).lastChild())->parent();
+ ASSERT(lastParent);
+ while (lastChildHasContinuation(*lastParent))
+ lastParent = endOfContinuations(*lastParent->lastChild())->parent();
nextSibling = lastParent->nextSibling();
}
@@ -397,54 +385,53 @@ AccessibilityObject* AccessibilityRenderObject::nextSibling() const
// Case 4: node is an inline with a continuation. Next sibling is the next sibling of the end
// of the continuation chain.
- else if (isInlineWithContinuation(m_renderer))
- nextSibling = endOfContinuations(m_renderer)->nextSibling();
+ else if (isInlineWithContinuation(*m_renderer))
+ nextSibling = endOfContinuations(*m_renderer)->nextSibling();
// Case 5: node has no next sibling, and its parent is an inline with a continuation.
- else if (isInlineWithContinuation(m_renderer->parent())) {
- RenderObject* continuation = toRenderInline(m_renderer->parent())->continuation();
+ else if (isInlineWithContinuation(*m_renderer->parent())) {
+ auto& continuation = *downcast<RenderInline>(*m_renderer->parent()).continuation();
// Case 5a: continuation is a block - in this case the block itself is the next sibling.
- if (continuation->isRenderBlock())
- nextSibling = continuation;
+ if (is<RenderBlock>(continuation))
+ nextSibling = &continuation;
// Case 5b: continuation is an inline - in this case the inline's first child is the next sibling
else
nextSibling = firstChildConsideringContinuation(continuation);
}
if (!nextSibling)
- return 0;
+ return nullptr;
return axObjectCache()->getOrCreate(nextSibling);
}
-static RenderBoxModelObject* nextContinuation(RenderObject* renderer)
+static RenderBoxModelObject* nextContinuation(RenderObject& renderer)
{
- ASSERT(renderer);
- if (renderer->isRenderInline() && !renderer->isReplaced())
- return toRenderInline(renderer)->continuation();
- if (renderer->isRenderBlock())
- return toRenderBlock(renderer)->inlineElementContinuation();
- return 0;
+ if (is<RenderInline>(renderer) && !renderer.isReplaced())
+ return downcast<RenderInline>(renderer).continuation();
+ if (is<RenderBlock>(renderer))
+ return downcast<RenderBlock>(renderer).inlineElementContinuation();
+ return nullptr;
}
RenderObject* AccessibilityRenderObject::renderParentObject() const
{
if (!m_renderer)
- return 0;
+ return nullptr;
- RenderObject* parent = m_renderer->parent();
+ RenderElement* parent = m_renderer->parent();
// Case 1: node is a block and is an inline's continuation. Parent
// is the start of the continuation chain.
- RenderObject* startOfConts = 0;
- RenderObject* firstChild = 0;
- if (m_renderer->isRenderBlock() && (startOfConts = startOfContinuations(m_renderer)))
+ RenderInline* startOfConts = nullptr;
+ RenderObject* firstChild = nullptr;
+ if (is<RenderBlock>(*m_renderer) && (startOfConts = startOfContinuations(*m_renderer)))
parent = startOfConts;
// Case 2: node's parent is an inline which is some node's continuation; parent is
// the earliest node in the continuation chain.
- else if (parent && parent->isRenderInline() && (startOfConts = startOfContinuations(parent)))
+ else if (is<RenderInline>(parent) && (startOfConts = startOfContinuations(*parent)))
parent = startOfConts;
// Case 3: The first sibling is the beginning of a continuation chain. Find the origin of that continuation.
@@ -452,15 +439,16 @@ RenderObject* AccessibilityRenderObject::renderParentObject() const
// Get the node's renderer and follow that continuation chain until the first child is found
RenderObject* nodeRenderFirstChild = firstChild->node()->renderer();
while (nodeRenderFirstChild != firstChild) {
- for (RenderObject* contsTest = nodeRenderFirstChild; contsTest; contsTest = nextContinuation(contsTest)) {
+ for (RenderObject* contsTest = nodeRenderFirstChild; contsTest; contsTest = nextContinuation(*contsTest)) {
if (contsTest == firstChild) {
parent = nodeRenderFirstChild->parent();
break;
}
}
- if (firstChild == parent->firstChild())
+ RenderObject* parentFirstChild = parent->firstChild();
+ if (firstChild == parentFirstChild)
break;
- firstChild = parent->firstChild();
+ firstChild = parentFirstChild;
if (!firstChild->node())
break;
nodeRenderFirstChild = firstChild->node()->renderer();
@@ -472,17 +460,21 @@ RenderObject* AccessibilityRenderObject::renderParentObject() const
AccessibilityObject* AccessibilityRenderObject::parentObjectIfExists() const
{
+ AXObjectCache* cache = axObjectCache();
+ if (!cache)
+ return nullptr;
+
// WebArea's parent should be the scroll view containing it.
- if (isWebArea() || isSeamlessWebArea())
- return axObjectCache()->get(m_renderer->frame()->view());
+ if (isWebArea())
+ return cache->get(&m_renderer->view().frameView());
- return axObjectCache()->get(renderParentObject());
+ return cache->get(renderParentObject());
}
AccessibilityObject* AccessibilityRenderObject::parentObject() const
{
if (!m_renderer)
- return 0;
+ return nullptr;
if (ariaRoleAttribute() == MenuBarRole)
return axObjectCache()->getOrCreate(m_renderer->parent());
@@ -494,15 +486,19 @@ AccessibilityObject* AccessibilityRenderObject::parentObject() const
return parent;
}
+ AXObjectCache* cache = axObjectCache();
+ if (!cache)
+ return nullptr;
+
RenderObject* parentObj = renderParentObject();
if (parentObj)
- return axObjectCache()->getOrCreate(parentObj);
+ return cache->getOrCreate(parentObj);
// WebArea's parent should be the scroll view containing it.
- if (isWebArea() || isSeamlessWebArea())
- return axObjectCache()->getOrCreate(m_renderer->frame()->view());
+ if (isWebArea())
+ return cache->getOrCreate(&m_renderer->view().frameView());
- return 0;
+ return nullptr;
}
bool AccessibilityRenderObject::isAttachment() const
@@ -518,39 +514,20 @@ bool AccessibilityRenderObject::isAttachment() const
bool AccessibilityRenderObject::isFileUploadButton() const
{
- if (m_renderer && m_renderer->node() && isHTMLInputElement(m_renderer->node())) {
- HTMLInputElement* input = toHTMLInputElement(m_renderer->node());
- return input->isFileUpload();
+ if (m_renderer && is<HTMLInputElement>(m_renderer->node())) {
+ HTMLInputElement& input = downcast<HTMLInputElement>(*m_renderer->node());
+ return input.isFileUpload();
}
return false;
}
-
-bool AccessibilityRenderObject::isReadOnly() const
-{
- ASSERT(m_renderer);
-
- if (isWebArea()) {
- Document* document = m_renderer->document();
- if (!document)
- return true;
-
- HTMLElement* body = document->body();
- if (body && body->rendererIsEditable())
- return false;
-
- return !document->rendererIsEditable();
- }
-
- return AccessibilityNodeObject::isReadOnly();
-}
bool AccessibilityRenderObject::isOffScreen() const
{
ASSERT(m_renderer);
- IntRect contentRect = pixelSnappedIntRect(m_renderer->absoluteClippedOverflowRect());
- FrameView* view = m_renderer->frame()->view();
- IntRect viewRect = view->visibleContentRect();
+ IntRect contentRect = snappedIntRect(m_renderer->absoluteClippedOverflowRect());
+ // FIXME: unclear if we need LegacyIOSDocumentVisibleRect.
+ IntRect viewRect = m_renderer->view().frameView().visibleContentRect(ScrollableArea::LegacyIOSDocumentVisibleRect);
viewRect.intersect(contentRect);
return viewRect.isEmpty();
}
@@ -558,33 +535,34 @@ bool AccessibilityRenderObject::isOffScreen() const
Element* AccessibilityRenderObject::anchorElement() const
{
if (!m_renderer)
- return 0;
+ return nullptr;
AXObjectCache* cache = axObjectCache();
- RenderObject* currRenderer;
+ if (!cache)
+ return nullptr;
+
+ RenderObject* currentRenderer;
// Search up the render tree for a RenderObject with a DOM node. Defer to an earlier continuation, though.
- for (currRenderer = m_renderer; currRenderer && !currRenderer->node(); currRenderer = currRenderer->parent()) {
- if (currRenderer->isAnonymousBlock()) {
- RenderObject* continuation = toRenderBlock(currRenderer)->continuation();
- if (continuation)
+ for (currentRenderer = m_renderer; currentRenderer && !currentRenderer->node(); currentRenderer = currentRenderer->parent()) {
+ if (currentRenderer->isAnonymousBlock()) {
+ if (RenderObject* continuation = downcast<RenderBlock>(*currentRenderer).continuation())
return cache->getOrCreate(continuation)->anchorElement();
}
}
// bail if none found
- if (!currRenderer)
- return 0;
+ if (!currentRenderer)
+ return nullptr;
// search up the DOM tree for an anchor element
// NOTE: this assumes that any non-image with an anchor is an HTMLAnchorElement
- Node* node = currRenderer->node();
- for ( ; node; node = node->parentNode()) {
- if (isHTMLAnchorElement(node) || (node->renderer() && cache->getOrCreate(node->renderer())->isAnchor()))
- return toElement(node);
+ for (Node* node = currentRenderer->node(); node; node = node->parentNode()) {
+ if (is<HTMLAnchorElement>(*node) || (node->renderer() && cache->getOrCreate(node->renderer())->isLink()))
+ return downcast<Element>(node);
}
- return 0;
+ return nullptr;
}
String AccessibilityRenderObject::helpText() const
@@ -601,21 +579,22 @@ String AccessibilityRenderObject::helpText() const
return describedBy;
String description = accessibilityDescription();
- for (RenderObject* curr = m_renderer; curr; curr = curr->parent()) {
- if (curr->node() && curr->node()->isHTMLElement()) {
- const AtomicString& summary = toElement(curr->node())->getAttribute(summaryAttr);
+ for (RenderObject* ancestor = m_renderer; ancestor; ancestor = ancestor->parent()) {
+ if (is<HTMLElement>(ancestor->node())) {
+ HTMLElement& element = downcast<HTMLElement>(*ancestor->node());
+ const AtomicString& summary = element.getAttribute(summaryAttr);
if (!summary.isEmpty())
return summary;
// The title attribute should be used as help text unless it is already being used as descriptive text.
- const AtomicString& title = toElement(curr->node())->getAttribute(titleAttr);
+ const AtomicString& title = element.getAttribute(titleAttr);
if (!title.isEmpty() && description != title)
return title;
}
// Only take help text from an ancestor element if its a group or an unknown role. If help was
// added to those kinds of elements, it is likely it was meant for a child element.
- AccessibilityObject* axObj = axObjectCache()->getOrCreate(curr);
+ AccessibilityObject* axObj = axObjectCache()->getOrCreate(ancestor);
if (axObj) {
AccessibilityRole role = axObj->roleValue();
if (role != GroupRole && role != UnknownRole)
@@ -631,54 +610,127 @@ String AccessibilityRenderObject::textUnderElement(AccessibilityTextUnderElement
if (!m_renderer)
return String();
- if (m_renderer->isFileUploadControl())
- return toRenderFileUploadControl(m_renderer)->buttonValue();
+ if (is<RenderFileUploadControl>(*m_renderer))
+ return downcast<RenderFileUploadControl>(*m_renderer).buttonValue();
+ // Reflect when a content author has explicitly marked a line break.
+ if (m_renderer->isBR())
+ return ASCIILiteral("\n");
+
+ bool isRenderText = is<RenderText>(*m_renderer);
+
#if ENABLE(MATHML)
// Math operators create RenderText nodes on the fly that are not tied into the DOM in a reasonable way,
// so rangeOfContents does not work for them (nor does regular text selection).
- if (m_renderer->isText() && isMathElement()) {
- for (RenderObject* parent = m_renderer->parent(); parent; parent = parent->parent()) {
- if (parent->isRenderMathMLBlock() && toRenderMathMLBlock(parent)->isRenderMathMLOperator())
- return toRenderText(m_renderer)->text();
- }
- }
+ if (isRenderText && m_renderer->isAnonymous() && ancestorsOfType<RenderMathMLOperator>(*m_renderer).first())
+ return downcast<RenderText>(*m_renderer).text();
+ if (is<RenderMathMLOperator>(*m_renderer) && !m_renderer->isAnonymous())
+ return downcast<RenderMathMLOperator>(*m_renderer).element().textContent();
#endif
+ if (shouldGetTextFromNode(mode))
+ return AccessibilityNodeObject::textUnderElement(mode);
+
// We use a text iterator for text objects AND for those cases where we are
// explicitly asking for the full text under a given element.
- if (m_renderer->isText() || mode == TextUnderElementModeIncludeAllChildren) {
+ bool shouldIncludeAllChildren = mode.childrenInclusion == AccessibilityTextUnderElementMode::TextUnderElementModeIncludeAllChildren;
+ if (isRenderText || shouldIncludeAllChildren) {
// If possible, use a text iterator to get the text, so that whitespace
// is handled consistently.
- if (Node* node = this->node()) {
- if (Frame* frame = node->document()->frame()) {
+ Document* nodeDocument = nullptr;
+ RefPtr<Range> textRange;
+ if (Node* node = m_renderer->node()) {
+ nodeDocument = &node->document();
+ textRange = rangeOfContents(*node);
+ } else {
+ // For anonymous blocks, we work around not having a direct node to create a range from
+ // defining one based in the two external positions defining the boundaries of the subtree.
+ RenderObject* firstChildRenderer = m_renderer->firstChildSlow();
+ RenderObject* lastChildRenderer = m_renderer->lastChildSlow();
+ if (firstChildRenderer && lastChildRenderer) {
+ ASSERT(firstChildRenderer->node());
+ ASSERT(lastChildRenderer->node());
+
+ // We define the start and end positions for the range as the ones right before and after
+ // the first and the last nodes in the DOM tree that is wrapped inside the anonymous block.
+ Node* firstNodeInBlock = firstChildRenderer->node();
+ Position startPosition = positionInParentBeforeNode(firstNodeInBlock);
+ Position endPosition = positionInParentAfterNode(lastChildRenderer->node());
+
+ nodeDocument = &firstNodeInBlock->document();
+ textRange = Range::create(*nodeDocument, startPosition, endPosition);
+ }
+ }
+
+ if (nodeDocument && textRange) {
+ if (Frame* frame = nodeDocument->frame()) {
// catch stale WebCoreAXObject (see <rdar://problem/3960196>)
- if (frame->document() != node->document())
+ if (frame->document() != nodeDocument)
return String();
- return plainText(rangeOfContents(node).get(), textIteratorBehaviorForTextRange());
+ // The tree should be stable before looking through the children of a non-Render Text object.
+ // Otherwise, further uses of TextIterator will force a layout update, potentially altering
+ // the accessibility tree and causing crashes in the loop that computes the result text.
+ ASSERT((isRenderText || !shouldIncludeAllChildren) || (!nodeDocument->renderView()->layoutState() && !nodeDocument->childNeedsStyleRecalc()));
+
+ return plainText(textRange.get(), textIteratorBehaviorForTextRange());
}
}
// Sometimes text fragments don't have Nodes associated with them (like when
// CSS content is used to insert text or when a RenderCounter is used.)
- if (m_renderer->isText()) {
- RenderText* renderTextObject = toRenderText(m_renderer);
- if (renderTextObject->isTextFragment())
- return String(static_cast<RenderTextFragment*>(m_renderer)->contentString());
+ if (is<RenderText>(*m_renderer)) {
+ RenderText& renderTextObject = downcast<RenderText>(*m_renderer);
+ if (is<RenderTextFragment>(renderTextObject)) {
+ RenderTextFragment& renderTextFragment = downcast<RenderTextFragment>(renderTextObject);
+ // The alt attribute may be set on a text fragment through CSS, which should be honored.
+ const String& altText = renderTextFragment.altText();
+ if (!altText.isEmpty())
+ return altText;
+ return renderTextFragment.contentString();
+ }
- return String(renderTextObject->text());
+ return renderTextObject.text();
}
}
return AccessibilityNodeObject::textUnderElement(mode);
}
+bool AccessibilityRenderObject::shouldGetTextFromNode(AccessibilityTextUnderElementMode mode) const
+{
+ if (!m_renderer)
+ return false;
+
+ // AccessibilityRenderObject::textUnderElement() gets the text of anonymous blocks by using
+ // the child nodes to define positions. CSS tables and their anonymous descendants lack
+ // children with nodes.
+ if (m_renderer->isAnonymous() && m_renderer->isTablePart())
+ return mode.childrenInclusion == AccessibilityTextUnderElementMode::TextUnderElementModeIncludeAllChildren;
+
+ // AccessibilityRenderObject::textUnderElement() calls rangeOfContents() to create the text
+ // range. rangeOfContents() does not include CSS-generated content.
+ if (m_renderer->isBeforeOrAfterContent())
+ return true;
+ if (Node* node = m_renderer->node()) {
+ Node* firstChild = node->pseudoAwareFirstChild();
+ Node* lastChild = node->pseudoAwareLastChild();
+ if ((firstChild && firstChild->isPseudoElement()) || (lastChild && lastChild->isPseudoElement()))
+ return true;
+ }
+
+ return false;
+}
+
Node* AccessibilityRenderObject::node() const
-{
- return m_renderer ? m_renderer->node() : 0;
+{
+ if (!m_renderer)
+ return nullptr;
+ if (m_renderer->isRenderView())
+ return &m_renderer->document();
+ return m_renderer->node();
}
-
+
String AccessibilityRenderObject::stringValue() const
{
if (!m_renderer)
@@ -696,39 +748,34 @@ String AccessibilityRenderObject::stringValue() const
return staticText;
}
- if (m_renderer->isText())
+ if (is<RenderText>(*m_renderer))
return textUnderElement();
- if (cssBox && cssBox->isMenuList()) {
+ if (is<RenderMenuList>(cssBox)) {
// RenderMenuList will go straight to the text() of its selected item.
// This has to be overridden in the case where the selected item has an ARIA label.
- HTMLSelectElement* selectElement = toHTMLSelectElement(m_renderer->node());
- int selectedIndex = selectElement->selectedIndex();
- const Vector<HTMLElement*> listItems = selectElement->listItems();
+ HTMLSelectElement& selectElement = downcast<HTMLSelectElement>(*m_renderer->node());
+ int selectedIndex = selectElement.selectedIndex();
+ const Vector<HTMLElement*>& listItems = selectElement.listItems();
if (selectedIndex >= 0 && static_cast<size_t>(selectedIndex) < listItems.size()) {
const AtomicString& overriddenDescription = listItems[selectedIndex]->fastGetAttribute(aria_labelAttr);
if (!overriddenDescription.isNull())
return overriddenDescription;
}
- return toRenderMenuList(m_renderer)->text();
+ return downcast<RenderMenuList>(*m_renderer).text();
}
- if (m_renderer->isListMarker())
- return toRenderListMarker(m_renderer)->text();
+ if (is<RenderListMarker>(*m_renderer))
+ return downcast<RenderListMarker>(*m_renderer).text();
- if (isWebArea()) {
- // FIXME: Why would a renderer exist when the Document isn't attached to a frame?
- if (m_renderer->frame())
- return String();
-
- ASSERT_NOT_REACHED();
- }
+ if (isWebArea())
+ return String();
if (isTextControl())
return text();
- if (m_renderer->isFileUploadControl())
- return toRenderFileUploadControl(m_renderer)->fileTextValue();
+ if (is<RenderFileUploadControl>(*m_renderer))
+ return downcast<RenderFileUploadControl>(*m_renderer).fileTextValue();
// FIXME: We might need to implement a value here for more types
// FIXME: It would be better not to advertise a value at all for the types for which we don't implement one;
@@ -740,19 +787,19 @@ String AccessibilityRenderObject::stringValue() const
HTMLLabelElement* AccessibilityRenderObject::labelElementContainer() const
{
if (!m_renderer)
- return 0;
+ return nullptr;
// the control element should not be considered part of the label
if (isControl())
- return 0;
+ return nullptr;
// find if this has a parent that is a label
for (Node* parentNode = m_renderer->node(); parentNode; parentNode = parentNode->parentNode()) {
- if (isHTMLLabelElement(parentNode))
- return toHTMLLabelElement(parentNode);
+ if (is<HTMLLabelElement>(*parentNode))
+ return downcast<HTMLLabelElement>(parentNode);
}
- return 0;
+ return nullptr;
}
// The boundingBox for elements within the remote SVG element needs to be offset by its position
@@ -782,28 +829,26 @@ LayoutRect AccessibilityRenderObject::boundingBoxRect() const
// We should also use absoluteQuads for SVG elements, otherwise transforms won't be applied.
Vector<FloatQuad> quads;
bool isSVGRoot = false;
-#if ENABLE(SVG)
+
if (obj->isSVGRoot())
isSVGRoot = true;
-#endif
- if (obj->isText())
- toRenderText(obj)->absoluteQuads(quads, 0, RenderText::ClipToEllipsis);
- else if (isWebArea() || isSeamlessWebArea() || isSVGRoot)
+
+ if (is<RenderText>(*obj))
+ quads = downcast<RenderText>(*obj).absoluteQuadsClippedToEllipsis();
+ else if (isWebArea() || isSVGRoot)
obj->absoluteQuads(quads);
else
obj->absoluteFocusRingQuads(quads);
LayoutRect result = boundingBoxForQuads(obj, quads);
-#if ENABLE(SVG)
Document* document = this->document();
if (document && document->isSVGDocument())
offsetBoundingBoxForRemoteSVGElement(result);
-#endif
// The size of the web area should be the content size, not the clipped size.
- if ((isWebArea() || isSeamlessWebArea()) && obj->frame()->view())
- result.setSize(obj->frame()->view()->contentsSize());
+ if (isWebArea())
+ result.setSize(obj->view().frameView().contentsSize());
return result;
}
@@ -813,7 +858,7 @@ LayoutRect AccessibilityRenderObject::checkboxOrRadioRect() const
if (!m_renderer)
return LayoutRect();
- HTMLLabelElement* label = labelForElement(toElement(m_renderer->node()));
+ HTMLLabelElement* label = labelForElement(downcast<Element>(m_renderer->node()));
if (!label || !label->renderer())
return boundingBoxRect();
@@ -833,32 +878,22 @@ LayoutRect AccessibilityRenderObject::elementRect() const
bool AccessibilityRenderObject::supportsPath() const
{
-#if ENABLE(SVG)
- if (m_renderer && m_renderer->isSVGShape())
- return true;
-#endif
-
- return false;
+ return is<RenderSVGShape>(m_renderer);
}
Path AccessibilityRenderObject::elementPath() const
{
-#if ENABLE(SVG)
- if (m_renderer && m_renderer->isSVGShape() && toRenderSVGShape(m_renderer)->hasPath()) {
- Path path = toRenderSVGShape(m_renderer)->path();
+ if (is<RenderSVGShape>(m_renderer) && downcast<RenderSVGShape>(*m_renderer).hasPath()) {
+ Path path = downcast<RenderSVGShape>(*m_renderer).path();
// The SVG path is in terms of the parent's bounding box. The path needs to be offset to frame coordinates.
- for (RenderObject* parent = m_renderer->parent(); parent; parent = parent->parent()) {
- if (parent->isSVGRoot()) {
- LayoutPoint parentOffset = axObjectCache()->getOrCreate(parent)->elementRect().location();
- path.transform(AffineTransform().translate(parentOffset.x(), parentOffset.y()));
- break;
- }
+ if (auto svgRoot = ancestorsOfType<RenderSVGRoot>(*m_renderer).first()) {
+ LayoutPoint parentOffset = axObjectCache()->getOrCreate(&*svgRoot)->elementRect().location();
+ path.transform(AffineTransform().translate(parentOffset.x(), parentOffset.y()));
}
return path;
}
-#endif
return Path();
}
@@ -870,14 +905,14 @@ IntPoint AccessibilityRenderObject::clickPoint()
return children()[0]->clickPoint();
// use the default position unless this is an editable web area, in which case we use the selection bounds.
- if (!isWebArea() || isReadOnly())
+ if (!isWebArea() || !canSetValueAttribute())
return AccessibilityObject::clickPoint();
VisibleSelection visSelection = selection();
VisiblePositionRange range = VisiblePositionRange(visSelection.visibleStart(), visSelection.visibleEnd());
IntRect bounds = boundsForVisiblePositionRange(range);
-#if PLATFORM(MAC)
- bounds.setLocation(m_renderer->document()->view()->screenToContents(bounds.location()));
+#if PLATFORM(COCOA)
+ bounds.setLocation(m_renderer->view().frameView().screenToContents(bounds.location()));
#endif
return IntPoint(bounds.x() + (bounds.width() / 2), bounds.y() - (bounds.height() / 2));
}
@@ -885,27 +920,24 @@ IntPoint AccessibilityRenderObject::clickPoint()
AccessibilityObject* AccessibilityRenderObject::internalLinkElement() const
{
Element* element = anchorElement();
- if (!element)
- return 0;
-
// Right now, we do not support ARIA links as internal link elements
- if (!isHTMLAnchorElement(element))
- return 0;
- HTMLAnchorElement* anchor = toHTMLAnchorElement(element);
+ if (!is<HTMLAnchorElement>(element))
+ return nullptr;
+ HTMLAnchorElement& anchor = downcast<HTMLAnchorElement>(*element);
- KURL linkURL = anchor->href();
+ URL linkURL = anchor.href();
String fragmentIdentifier = linkURL.fragmentIdentifier();
if (fragmentIdentifier.isEmpty())
- return 0;
+ return nullptr;
// check if URL is the same as current URL
- KURL documentURL = m_renderer->document()->url();
+ URL documentURL = m_renderer->document().url();
if (!equalIgnoringFragmentIdentifier(documentURL, linkURL))
- return 0;
+ return nullptr;
- Node* linkedNode = m_renderer->document()->findAnchor(fragmentIdentifier);
+ Node* linkedNode = m_renderer->document().findAnchor(fragmentIdentifier);
if (!linkedNode)
- return 0;
+ return nullptr;
// The element we find may not be accessible, so find the first accessible object.
return firstAccessibleObjectFromNode(linkedNode);
@@ -916,7 +948,7 @@ ESpeak AccessibilityRenderObject::speakProperty() const
if (!m_renderer)
return AccessibilityObject::speakProperty();
- return m_renderer->style()->speak();
+ return m_renderer->style().speak();
}
void AccessibilityRenderObject::addRadioButtonGroupMembers(AccessibilityChildrenVector& linkedUIElements) const
@@ -925,31 +957,21 @@ void AccessibilityRenderObject::addRadioButtonGroupMembers(AccessibilityChildren
return;
Node* node = m_renderer->node();
- if (!node || !isHTMLInputElement(node))
+ if (!is<HTMLInputElement>(node))
return;
- HTMLInputElement* input = toHTMLInputElement(node);
+ HTMLInputElement& input = downcast<HTMLInputElement>(*node);
// if there's a form, then this is easy
- if (input->form()) {
- Vector<RefPtr<Node> > formElements;
- input->form()->getNamedElements(input->name(), formElements);
-
- unsigned len = formElements.size();
- for (unsigned i = 0; i < len; ++i) {
- Node* associateElement = formElements[i].get();
- if (AccessibilityObject* object = axObjectCache()->getOrCreate(associateElement))
+ if (input.form()) {
+ for (auto& associateElement : input.form()->namedElements(input.name())) {
+ if (AccessibilityObject* object = axObjectCache()->getOrCreate(&associateElement.get()))
linkedUIElements.append(object);
}
} else {
- RefPtr<NodeList> list = node->document()->getElementsByTagName("input");
- unsigned len = list->length();
- for (unsigned i = 0; i < len; ++i) {
- if (isHTMLInputElement(list->item(i))) {
- HTMLInputElement* associateElement = toHTMLInputElement(list->item(i));
- if (associateElement->isRadioButton() && associateElement->name() == input->name()) {
- if (AccessibilityObject* object = axObjectCache()->getOrCreate(associateElement))
- linkedUIElements.append(object);
- }
+ for (auto& associateElement : descendantsOfType<HTMLInputElement>(node->document())) {
+ if (associateElement.isRadioButton() && associateElement.name() == input.name()) {
+ if (AccessibilityObject* object = axObjectCache()->getOrCreate(&associateElement))
+ linkedUIElements.append(object);
}
}
}
@@ -961,7 +983,7 @@ void AccessibilityRenderObject::linkedUIElements(AccessibilityChildrenVector& li
{
ariaFlowToElements(linkedUIElements);
- if (isAnchor()) {
+ if (isLink()) {
AccessibilityObject* linkedAXElement = internalLinkElement();
if (linkedAXElement)
linkedUIElements.append(linkedAXElement);
@@ -975,10 +997,7 @@ bool AccessibilityRenderObject::hasTextAlternative() const
{
// ARIA: section 2A, bullet #3 says if aria-labeledby or aria-label appears, it should
// override the "label" element association.
- if (!ariaLabeledByAttribute().isEmpty() || !getAttribute(aria_labelAttr).isEmpty())
- return true;
-
- return false;
+ return ariaAccessibilityDescription().length();
}
bool AccessibilityRenderObject::ariaHasPopup() const
@@ -986,6 +1005,17 @@ bool AccessibilityRenderObject::ariaHasPopup() const
return elementAttributeValue(aria_haspopupAttr);
}
+void AccessibilityRenderObject::ariaElementsFromAttribute(AccessibilityChildrenVector& children, const QualifiedName& attributeName) const
+{
+ Vector<Element*> elements;
+ elementsFromAttribute(elements, attributeName);
+ AXObjectCache* cache = axObjectCache();
+ for (const auto& element : elements) {
+ if (AccessibilityObject* axObject = cache->getOrCreate(element))
+ children.append(axObject);
+ }
+}
+
bool AccessibilityRenderObject::supportsARIAFlowTo() const
{
return !getAttribute(aria_flowtoAttr).isEmpty();
@@ -993,20 +1023,29 @@ bool AccessibilityRenderObject::supportsARIAFlowTo() const
void AccessibilityRenderObject::ariaFlowToElements(AccessibilityChildrenVector& flowTo) const
{
- Vector<Element*> elements;
- elementsFromAttribute(elements, aria_flowtoAttr);
-
- AXObjectCache* cache = axObjectCache();
- unsigned count = elements.size();
- for (unsigned k = 0; k < count; ++k) {
- Element* element = elements[k];
- AccessibilityObject* flowToElement = cache->getOrCreate(element);
- if (flowToElement)
- flowTo.append(flowToElement);
- }
-
+ ariaElementsFromAttribute(flowTo, aria_flowtoAttr);
}
-
+
+bool AccessibilityRenderObject::supportsARIADescribedBy() const
+{
+ return !getAttribute(aria_describedbyAttr).isEmpty();
+}
+
+void AccessibilityRenderObject::ariaDescribedByElements(AccessibilityChildrenVector& ariaDescribedBy) const
+{
+ ariaElementsFromAttribute(ariaDescribedBy, aria_describedbyAttr);
+}
+
+bool AccessibilityRenderObject::supportsARIAControls() const
+{
+ return !getAttribute(aria_controlsAttr).isEmpty();
+}
+
+void AccessibilityRenderObject::ariaControlsElements(AccessibilityChildrenVector& ariaControls) const
+{
+ ariaElementsFromAttribute(ariaControls, aria_controlsAttr);
+}
+
bool AccessibilityRenderObject::supportsARIADropping() const
{
const AtomicString& dropEffect = getAttribute(aria_dropeffectAttr);
@@ -1016,7 +1055,7 @@ bool AccessibilityRenderObject::supportsARIADropping() const
bool AccessibilityRenderObject::supportsARIADragging() const
{
const AtomicString& grabbed = getAttribute(aria_grabbedAttr);
- return equalIgnoringCase(grabbed, "true") || equalIgnoringCase(grabbed, "false");
+ return equalLettersIgnoringASCIICase(grabbed, "true") || equalLettersIgnoringASCIICase(grabbed, "false");
}
bool AccessibilityRenderObject::isARIAGrabbed()
@@ -1047,12 +1086,6 @@ bool AccessibilityRenderObject::exposesTitleUIElement() const
if (accessibilityIsIgnored())
return true;
- // Checkboxes and radio buttons use the text of their title ui element as their own AXTitle.
- // This code controls whether the title ui element should appear in the AX tree (usually, no).
- // It should appear if the control already has a label (which will be used as the AXTitle instead).
- if (isCheckboxOrRadio())
- return hasTextAlternative();
-
// When controls have their own descriptions, the title element should be ignored.
if (hasTextAlternative())
return false;
@@ -1063,20 +1096,20 @@ bool AccessibilityRenderObject::exposesTitleUIElement() const
AccessibilityObject* AccessibilityRenderObject::titleUIElement() const
{
if (!m_renderer)
- return 0;
+ return nullptr;
// if isFieldset is true, the renderer is guaranteed to be a RenderFieldset
if (isFieldset())
- return axObjectCache()->getOrCreate(toRenderFieldset(m_renderer)->findLegend(RenderFieldset::IncludeFloatingOrOutOfFlow));
+ return axObjectCache()->getOrCreate(downcast<RenderFieldset>(*m_renderer).findLegend(RenderFieldset::IncludeFloatingOrOutOfFlow));
Node* node = m_renderer->node();
- if (!node || !node->isElementNode())
- return 0;
- HTMLLabelElement* label = labelForElement(toElement(node));
+ if (!is<Element>(node))
+ return nullptr;
+ HTMLLabelElement* label = labelForElement(downcast<Element>(node));
if (label && label->renderer())
return axObjectCache()->getOrCreate(label);
- return 0;
+ return nullptr;
}
bool AccessibilityRenderObject::isAllowedChildOfTree() const
@@ -1084,7 +1117,10 @@ bool AccessibilityRenderObject::isAllowedChildOfTree() const
// Determine if this is in a tree. If so, we apply special behavior to make it work like an AXOutline.
AccessibilityObject* axObj = parentObject();
bool isInTree = false;
+ bool isTreeItemDescendant = false;
while (axObj) {
+ if (axObj->roleValue() == TreeItemRole)
+ isTreeItemDescendant = true;
if (axObj->isTree()) {
isInTree = true;
break;
@@ -1095,12 +1131,25 @@ bool AccessibilityRenderObject::isAllowedChildOfTree() const
// If the object is in a tree, only tree items should be exposed (and the children of tree items).
if (isInTree) {
AccessibilityRole role = roleValue();
- if (role != TreeItemRole && role != StaticTextRole)
+ if (role != TreeItemRole && role != StaticTextRole && !isTreeItemDescendant)
return false;
}
return true;
}
+static AccessibilityObjectInclusion objectInclusionFromAltText(const String& altText)
+{
+ // Don't ignore an image that has an alt tag.
+ if (!altText.containsOnlyWhitespace())
+ return IncludeObject;
+
+ // The informal standard is to ignore images with zero-length alt strings.
+ if (!altText.isNull())
+ return IgnoreObject;
+
+ return DefaultBehavior;
+}
+
AccessibilityObjectInclusion AccessibilityRenderObject::defaultObjectInclusion() const
{
// The following cases can apply to any element that's a subclass of AccessibilityRenderObject.
@@ -1108,9 +1157,9 @@ AccessibilityObjectInclusion AccessibilityRenderObject::defaultObjectInclusion()
if (!m_renderer)
return IgnoreObject;
- if (m_renderer->style()->visibility() != VISIBLE) {
+ if (m_renderer->style().visibility() != VISIBLE) {
// aria-hidden is meant to override visibility as the determinant in AX hierarchy inclusion.
- if (equalIgnoringCase(getAttribute(aria_hiddenAttr), "false"))
+ if (equalLettersIgnoringASCIICase(getAttribute(aria_hiddenAttr), "false"))
return DefaultBehavior;
return IgnoreObject;
@@ -1125,6 +1174,9 @@ bool AccessibilityRenderObject::computeAccessibilityIsIgnored() const
ASSERT(m_initialized);
#endif
+ if (!m_renderer)
+ return true;
+
// Check first if any of the common reasons cause this element to be ignored.
// Then process other use cases that need to be applied to all the various roles
// that AccessibilityRenderObjects take on.
@@ -1153,25 +1205,25 @@ bool AccessibilityRenderObject::computeAccessibilityIsIgnored() const
return accessibilityIgnoreAttachment();
// ignore popup menu items because AppKit does
- for (RenderObject* parent = m_renderer->parent(); parent; parent = parent->parent()) {
- if (parent->isBoxModelObject() && toRenderBoxModelObject(parent)->isMenuList())
- return true;
- }
+ if (m_renderer && ancestorsOfType<RenderMenuList>(*m_renderer).first())
+ return true;
// find out if this element is inside of a label element.
// if so, it may be ignored because it's the label for a checkbox or radio button
AccessibilityObject* controlObject = correspondingControlForLabelElement();
if (controlObject && !controlObject->exposesTitleUIElement() && controlObject->isCheckboxOrRadio())
return true;
-
- // NOTE: BRs always have text boxes now, so the text box check here can be removed
- if (m_renderer->isText()) {
+
+ if (m_renderer->isBR())
+ return true;
+
+ if (is<RenderText>(*m_renderer)) {
// static text beneath MenuItems and MenuButtons are just reported along with the menu item, so it's ignored on an individual level
AccessibilityObject* parent = parentObjectUnignored();
- if (parent && (parent->ariaRoleAttribute() == MenuItemRole || parent->ariaRoleAttribute() == MenuButtonRole))
+ if (parent && (parent->isMenuItem() || parent->ariaRoleAttribute() == MenuButtonRole))
return true;
- RenderText* renderText = toRenderText(m_renderer);
- if (m_renderer->isBR() || !renderText->firstTextBox())
+ auto& renderText = downcast<RenderText>(*m_renderer);
+ if (!renderText.hasRenderedText())
return true;
// static text beneath TextControls is reported along with the text control text so it's ignored.
@@ -1179,9 +1231,18 @@ bool AccessibilityRenderObject::computeAccessibilityIsIgnored() const
if (parent->roleValue() == TextFieldRole)
return true;
}
+
+ // The alt attribute may be set on a text fragment through CSS, which should be honored.
+ if (is<RenderTextFragment>(renderText)) {
+ AccessibilityObjectInclusion altTextInclusion = objectInclusionFromAltText(downcast<RenderTextFragment>(renderText).altText());
+ if (altTextInclusion == IgnoreObject)
+ return true;
+ if (altTextInclusion == IncludeObject)
+ return false;
+ }
// text elements that are just empty whitespace should not be returned
- return renderText->text()->containsOnlyWhitespace();
+ return renderText.text()->containsOnlyWhitespace();
}
if (isHeading())
@@ -1190,27 +1251,44 @@ bool AccessibilityRenderObject::computeAccessibilityIsIgnored() const
if (isLink())
return false;
+ if (isLandmark())
+ return false;
+
// all controls are accessible
if (isControl())
return false;
+
+ switch (roleValue()) {
+ case AudioRole:
+ case DescriptionListTermRole:
+ case DescriptionListDetailRole:
+ case DetailsRole:
+ case DocumentArticleRole:
+ case DocumentRegionRole:
+ case ListItemRole:
+ case VideoRole:
+ return false;
+ default:
+ break;
+ }
if (ariaRoleAttribute() != UnknownRole)
return false;
-
+
+ if (roleValue() == HorizontalRuleRole)
+ return false;
+
// don't ignore labels, because they serve as TitleUIElements
Node* node = m_renderer->node();
- if (node && isHTMLLabelElement(node))
+ if (is<HTMLLabelElement>(node))
return false;
// Anything that is content editable should not be ignored.
- // However, one cannot just call node->rendererIsEditable() since that will ask if its parents
+ // However, one cannot just call node->hasEditableStyle() since that will ask if its parents
// are also editable. Only the top level content editable region should be exposed.
if (hasContentEditableAttributeSet())
return false;
- // List items play an important role in defining the structure of lists. They should not be ignored.
- if (roleValue() == ListItemRole)
- return false;
// if this element has aria attributes on it, it should not be ignored.
if (supportsARIAAttributes())
@@ -1225,17 +1303,8 @@ bool AccessibilityRenderObject::computeAccessibilityIsIgnored() const
return false;
#endif
- // <span> tags are inline tags and not meant to convey information if they have no other aria
- // information on them. If we don't ignore them, they may emit signals expected to come from
- // their parent. In addition, because included spans are GroupRole objects, and GroupRole
- // objects are often containers with meaningful information, the inclusion of a span can have
- // the side effect of causing the immediate parent accessible to be ignored. This is especially
- // problematic for platforms which have distinct roles for textual block elements.
- if (node && node->hasTagName(spanTag))
- return true;
-
- if (m_renderer->isBlockFlow() && m_renderer->childrenInline() && !canSetFocusAttribute())
- return !toRenderBlock(m_renderer)->firstLineBox() && !mouseButtonListener();
+ if (is<RenderBlockFlow>(*m_renderer) && m_renderer->childrenInline() && !canSetFocusAttribute())
+ return !downcast<RenderBlockFlow>(*m_renderer).hasLines() && !mouseButtonListener();
// ignore images seemingly used as spacers
if (isImage()) {
@@ -1244,29 +1313,33 @@ bool AccessibilityRenderObject::computeAccessibilityIsIgnored() const
if (canSetFocusAttribute())
return false;
- if (node && node->isElementNode()) {
- Element* elt = toElement(node);
- const AtomicString& alt = elt->getAttribute(altAttr);
- // don't ignore an image that has an alt tag
- if (!alt.string().containsOnlyWhitespace())
- return false;
- // informal standard is to ignore images with zero-length alt strings
- if (!alt.isNull())
- return true;
- // If an image has a title attribute on it, accessibility should be lenient and allow it to appear in the hierarchy (according to WAI-ARIA).
- if (!getAttribute(titleAttr).isEmpty())
- return false;
- }
+ // First check the RenderImage's altText (which can be set through a style sheet, or come from the Element).
+ // However, if this is not a native image, fallback to the attribute on the Element.
+ AccessibilityObjectInclusion altTextInclusion = DefaultBehavior;
+ bool isRenderImage = is<RenderImage>(m_renderer);
+ if (isRenderImage)
+ altTextInclusion = objectInclusionFromAltText(downcast<RenderImage>(*m_renderer).altText());
+ else
+ altTextInclusion = objectInclusionFromAltText(getAttribute(altAttr).string());
+
+ if (altTextInclusion == IgnoreObject)
+ return true;
+ if (altTextInclusion == IncludeObject)
+ return false;
- if (isNativeImage()) {
+ // If an image has a title attribute on it, accessibility should be lenient and allow it to appear in the hierarchy (according to WAI-ARIA).
+ if (!getAttribute(titleAttr).isEmpty())
+ return false;
+
+ if (isRenderImage) {
// check for one-dimensional image
- RenderImage* image = toRenderImage(m_renderer);
- if (image->height() <= 1 || image->width() <= 1)
+ RenderImage& image = downcast<RenderImage>(*m_renderer);
+ if (image.height() <= 1 || image.width() <= 1)
return true;
// check whether rendered image was stretched from one-dimensional file image
- if (image->cachedImage()) {
- LayoutSize imageSize = image->cachedImage()->imageSizeForRenderer(m_renderer, image->view()->zoomFactor());
+ if (image.cachedImage()) {
+ LayoutSize imageSize = image.cachedImage()->imageSizeForRenderer(&image, image.view().zoomFactor());
return imageSize.height() <= 1 || imageSize.width() <= 1;
}
}
@@ -1277,26 +1350,34 @@ bool AccessibilityRenderObject::computeAccessibilityIsIgnored() const
if (canvasHasFallbackContent())
return false;
- if (m_renderer->isBox()) {
- RenderBox* canvasBox = toRenderBox(m_renderer);
- if (canvasBox->height() <= 1 || canvasBox->width() <= 1)
+ if (is<RenderBox>(*m_renderer)) {
+ auto& canvasBox = downcast<RenderBox>(*m_renderer);
+ if (canvasBox.height() <= 1 || canvasBox.width() <= 1)
return true;
}
// Otherwise fall through; use presence of help text, title, or description to decide.
}
- if (isWebArea() || isSeamlessWebArea() || m_renderer->isListMarker())
+ if (m_renderer->isListMarker()) {
+ AccessibilityObject* parent = parentObjectUnignored();
+ return parent && !parent->isListItem();
+ }
+
+ if (isWebArea())
return false;
- // Using the help text, title or accessibility description (so we
- // check if there's some kind of accessible name for the element)
- // to decide an element's visibility is not as definitive as
- // previous checks, so this should remain as one of the last.
- //
- // These checks are simplified in the interest of execution speed;
- // for example, any element having an alt attribute will make it
- // not ignored, rather than just images.
- if (!getAttribute(aria_helpAttr).isEmpty() || !getAttribute(aria_describedbyAttr).isEmpty() || !getAttribute(altAttr).isEmpty() || !getAttribute(titleAttr).isEmpty())
+#if ENABLE(METER_ELEMENT)
+ // The render tree of meter includes a RenderBlock (meter) and a RenderMeter (div).
+ // We expose the latter and thus should ignore the former. However, if the author
+ // includes a title attribute on the element, hasAttributesRequiredForInclusion()
+ // will return true, potentially resulting in a redundant accessible object.
+ if (is<HTMLMeterElement>(node))
+ return true;
+#endif
+
+ // Using the presence of an accessible name to decide an element's visibility is not
+ // as definitive as previous checks, so this should remain as one of the last.
+ if (hasAttributesRequiredForInclusion())
return false;
// Don't ignore generic focusable elements like <div tabindex=0>
@@ -1304,26 +1385,34 @@ bool AccessibilityRenderObject::computeAccessibilityIsIgnored() const
if (isGenericFocusableElement() && node->firstChild())
return false;
- if (!ariaAccessibilityDescription().isEmpty())
- return false;
-
-#if ENABLE(MATHML)
- if (!getAttribute(MathMLNames::alttextAttr).isEmpty())
- return false;
-#endif
+ // <span> tags are inline tags and not meant to convey information if they have no other aria
+ // information on them. If we don't ignore them, they may emit signals expected to come from
+ // their parent. In addition, because included spans are GroupRole objects, and GroupRole
+ // objects are often containers with meaningful information, the inclusion of a span can have
+ // the side effect of causing the immediate parent accessible to be ignored. This is especially
+ // problematic for platforms which have distinct roles for textual block elements.
+ if (node && node->hasTagName(spanTag))
+ return true;
// Other non-ignored host language elements
if (node && node->hasTagName(dfnTag))
return false;
- // By default, objects should be ignored so that the AX hierarchy is not
+ if (isStyleFormatGroup())
+ return false;
+
+ // Make sure that ruby containers are not ignored.
+ if (m_renderer->isRubyRun() || m_renderer->isRubyBlock() || m_renderer->isRubyInline())
+ return false;
+
+ // By default, objects should be ignored so that the AX hierarchy is not
// filled with unnecessary items.
return true;
}
bool AccessibilityRenderObject::isLoaded() const
{
- return !m_renderer->document()->parser();
+ return !m_renderer->document().parser();
}
double AccessibilityRenderObject::estimatedLoadingProgress() const
@@ -1334,18 +1423,18 @@ double AccessibilityRenderObject::estimatedLoadingProgress() const
if (isLoaded())
return 1.0;
- Page* page = m_renderer->document()->page();
+ Page* page = m_renderer->document().page();
if (!page)
return 0;
- return page->progress()->estimatedProgress();
+ return page->progress().estimatedProgress();
}
int AccessibilityRenderObject::layoutCount() const
{
- if (!m_renderer->isRenderView())
+ if (!is<RenderView>(*m_renderer))
return 0;
- return toRenderView(m_renderer)->frameView()->layoutCount();
+ return downcast<RenderView>(*m_renderer).frameView().layoutCount();
}
String AccessibilityRenderObject::text() const
@@ -1361,16 +1450,12 @@ int AccessibilityRenderObject::textLength() const
ASSERT(isTextControl());
if (isPasswordField())
-#if PLATFORM(GTK)
return passwordFieldValue().length();
-#else
- return -1; // need to return something distinct from 0
-#endif
return text().length();
}
-PlainTextRange AccessibilityRenderObject::ariaSelectedTextRange() const
+PlainTextRange AccessibilityRenderObject::documentBasedSelectedTextRange() const
{
Node* node = m_renderer->node();
if (!node)
@@ -1395,29 +1480,24 @@ String AccessibilityRenderObject::selectedText() const
return String(); // need to return something distinct from empty string
if (isNativeTextControl()) {
- HTMLTextFormControlElement* textControl = toRenderTextControl(m_renderer)->textFormControlElement();
- return textControl->selectedText();
+ HTMLTextFormControlElement& textControl = downcast<RenderTextControl>(*m_renderer).textFormControlElement();
+ return textControl.selectedText();
}
- if (ariaRoleAttribute() == UnknownRole)
- return String();
-
- return doAXStringForRange(ariaSelectedTextRange());
+ return doAXStringForRange(documentBasedSelectedTextRange());
}
const AtomicString& AccessibilityRenderObject::accessKey() const
{
Node* node = m_renderer->node();
- if (!node)
- return nullAtom;
- if (!node->isElementNode())
+ if (!is<Element>(node))
return nullAtom;
- return toElement(node)->getAttribute(accesskeyAttr);
+ return downcast<Element>(*node).getAttribute(accesskeyAttr);
}
VisibleSelection AccessibilityRenderObject::selection() const
{
- return m_renderer->frame()->selection()->selection();
+ return m_renderer->frame().selection().selection();
}
PlainTextRange AccessibilityRenderObject::selectedTextRange() const
@@ -1428,65 +1508,78 @@ PlainTextRange AccessibilityRenderObject::selectedTextRange() const
return PlainTextRange();
AccessibilityRole ariaRole = ariaRoleAttribute();
- if (isNativeTextControl() && ariaRole == UnknownRole) {
- HTMLTextFormControlElement* textControl = toRenderTextControl(m_renderer)->textFormControlElement();
- return PlainTextRange(textControl->selectionStart(), textControl->selectionEnd() - textControl->selectionStart());
+ // Use the text control native range if it's a native object and it has no ARIA role (or has a text based ARIA role).
+ if (isNativeTextControl() && (ariaRole == UnknownRole || isARIATextControl())) {
+ HTMLTextFormControlElement& textControl = downcast<RenderTextControl>(*m_renderer).textFormControlElement();
+ return PlainTextRange(textControl.selectionStart(), textControl.selectionEnd() - textControl.selectionStart());
}
- if (ariaRole == UnknownRole)
- return PlainTextRange();
-
- return ariaSelectedTextRange();
+ return documentBasedSelectedTextRange();
+}
+
+static void setTextSelectionIntent(AXObjectCache* cache, AXTextStateChangeType type)
+{
+ if (!cache)
+ return;
+ AXTextStateChangeIntent intent(type, AXTextSelection { AXTextSelectionDirectionDiscontiguous, AXTextSelectionGranularityUnknown, false });
+ cache->setTextSelectionIntent(intent);
+ cache->setIsSynchronizingSelection(true);
+}
+
+static void clearTextSelectionIntent(AXObjectCache* cache)
+{
+ if (!cache)
+ return;
+ cache->setTextSelectionIntent(AXTextStateChangeIntent());
+ cache->setIsSynchronizingSelection(false);
}
void AccessibilityRenderObject::setSelectedTextRange(const PlainTextRange& range)
{
if (isNativeTextControl()) {
- HTMLTextFormControlElement* textControl = toRenderTextControl(m_renderer)->textFormControlElement();
- textControl->setSelectionRange(range.start, range.start + range.length);
+ setTextSelectionIntent(axObjectCache(), range.length ? AXTextStateChangeTypeSelectionExtend : AXTextStateChangeTypeSelectionMove);
+ HTMLTextFormControlElement& textControl = downcast<RenderTextControl>(*m_renderer).textFormControlElement();
+ textControl.setSelectionRange(range.start, range.start + range.length);
+ clearTextSelectionIntent(axObjectCache());
return;
}
- Document* document = m_renderer->document();
- if (!document)
- return;
- Frame* frame = document->frame();
- if (!frame)
- return;
Node* node = m_renderer->node();
- frame->selection()->setSelection(VisibleSelection(Position(node, range.start, Position::PositionIsOffsetInAnchor),
- Position(node, range.start + range.length, Position::PositionIsOffsetInAnchor), DOWNSTREAM));
+ VisibleSelection newSelection(Position(node, range.start, Position::PositionIsOffsetInAnchor), Position(node, range.start + range.length, Position::PositionIsOffsetInAnchor), DOWNSTREAM);
+ setTextSelectionIntent(axObjectCache(), range.length ? AXTextStateChangeTypeSelectionExtend : AXTextStateChangeTypeSelectionMove);
+ m_renderer->frame().selection().setSelection(newSelection, FrameSelection::defaultSetSelectionOptions());
+ clearTextSelectionIntent(axObjectCache());
}
-KURL AccessibilityRenderObject::url() const
+URL AccessibilityRenderObject::url() const
{
- if (isAnchor() && isHTMLAnchorElement(m_renderer->node())) {
- if (HTMLAnchorElement* anchor = toHTMLAnchorElement(anchorElement()))
+ if (isLink() && is<HTMLAnchorElement>(*m_renderer->node())) {
+ if (HTMLAnchorElement* anchor = downcast<HTMLAnchorElement>(anchorElement()))
return anchor->href();
}
if (isWebArea())
- return m_renderer->document()->url();
+ return m_renderer->document().url();
- if (isImage() && m_renderer->node() && isHTMLImageElement(m_renderer->node()))
- return toHTMLImageElement(m_renderer->node())->src();
+ if (isImage() && is<HTMLImageElement>(m_renderer->node()))
+ return downcast<HTMLImageElement>(*m_renderer->node()).src();
if (isInputImage())
- return toHTMLInputElement(m_renderer->node())->src();
+ return downcast<HTMLInputElement>(*m_renderer->node()).src();
- return KURL();
+ return URL();
}
bool AccessibilityRenderObject::isUnvisited() const
{
// FIXME: Is it a privacy violation to expose unvisited information to accessibility APIs?
- return m_renderer->style()->isLink() && m_renderer->style()->insideLink() == InsideUnvisitedLink;
+ return m_renderer->style().isLink() && m_renderer->style().insideLink() == InsideUnvisitedLink;
}
bool AccessibilityRenderObject::isVisited() const
{
// FIXME: Is it a privacy violation to expose visited information to accessibility APIs?
- return m_renderer->style()->isLink() && m_renderer->style()->insideLink() == InsideVisitedLink;
+ return m_renderer->style().isLink() && m_renderer->style().insideLink() == InsideVisitedLink;
}
void AccessibilityRenderObject::setElementAttributeValue(const QualifiedName& attributeName, bool value)
@@ -1495,11 +1588,10 @@ void AccessibilityRenderObject::setElementAttributeValue(const QualifiedName& at
return;
Node* node = m_renderer->node();
- if (!node || !node->isElementNode())
+ if (!is<Element>(node))
return;
- Element* element = toElement(node);
- element->setAttribute(attributeName, (value) ? "true" : "false");
+ downcast<Element>(*node).setAttribute(attributeName, (value) ? "true" : "false");
}
bool AccessibilityRenderObject::elementAttributeValue(const QualifiedName& attributeName) const
@@ -1507,7 +1599,7 @@ bool AccessibilityRenderObject::elementAttributeValue(const QualifiedName& attri
if (!m_renderer)
return false;
- return equalIgnoringCase(getAttribute(attributeName), "true");
+ return equalLettersIgnoringASCIICase(getAttribute(attributeName), "true");
}
bool AccessibilityRenderObject::isSelected() const
@@ -1515,12 +1607,10 @@ bool AccessibilityRenderObject::isSelected() const
if (!m_renderer)
return false;
- Node* node = m_renderer->node();
- if (!node)
+ if (!m_renderer->node())
return false;
- const AtomicString& ariaSelected = getAttribute(aria_selectedAttr);
- if (equalIgnoringCase(ariaSelected, "true"))
+ if (equalLettersIgnoringASCIICase(getAttribute(aria_selectedAttr), "true"))
return true;
if (isTabItem() && isTabItemSelected())
@@ -1548,10 +1638,12 @@ bool AccessibilityRenderObject::isTabItemSelected() const
Vector<Element*> elements;
elementsFromAttribute(elements, aria_controlsAttr);
- unsigned count = elements.size();
- for (unsigned k = 0; k < count; ++k) {
- Element* element = elements[k];
- AccessibilityObject* tabPanel = axObjectCache()->getOrCreate(element);
+ AXObjectCache* cache = axObjectCache();
+ if (!cache)
+ return false;
+
+ for (const auto& element : elements) {
+ AccessibilityObject* tabPanel = cache->getOrCreate(element);
// A tab item should only control tab panels.
if (!tabPanel || tabPanel->roleValue() != TabPanelRole)
@@ -1574,18 +1666,16 @@ bool AccessibilityRenderObject::isFocused() const
if (!m_renderer)
return false;
- Document* document = m_renderer->document();
- if (!document)
- return false;
-
- Element* focusedElement = document->focusedElement();
+ Document& document = m_renderer->document();
+
+ Element* focusedElement = document.focusedElement();
if (!focusedElement)
return false;
// A web area is represented by the Document node in the DOM tree, which isn't focusable.
// Check instead if the frame's selection controller is focused
if (focusedElement == m_renderer->node()
- || (roleValue() == WebAreaRole && document->frame()->selection()->isFocusedAndActive()))
+ || (roleValue() == WebAreaRole && document.frame()->selection().isFocusedAndActive()))
return true;
return false;
@@ -1599,25 +1689,35 @@ void AccessibilityRenderObject::setFocused(bool on)
Document* document = this->document();
Node* node = this->node();
- if (!on || !node || !node->isElementNode()) {
- document->setFocusedElement(0);
+ if (!on || !is<Element>(node)) {
+ document->setFocusedElement(nullptr);
return;
}
+ // When a node is told to set focus, that can cause it to be deallocated, which means that doing
+ // anything else inside this object will crash. To fix this, we added a RefPtr to protect this object
+ // long enough for duration.
+ RefPtr<AccessibilityObject> protect(this);
+
// If this node is already the currently focused node, then calling focus() won't do anything.
// That is a problem when focus is removed from the webpage to chrome, and then returns.
// In these cases, we need to do what keyboard and mouse focus do, which is reset focus first.
if (document->focusedElement() == node)
- document->setFocusedElement(0);
+ document->setFocusedElement(nullptr);
- toElement(node)->focus();
+ // If we return from setFocusedElement and our element has been removed from a tree, axObjectCache() may be null.
+ if (AXObjectCache* cache = axObjectCache()) {
+ cache->setIsSynchronizingSelection(true);
+ downcast<Element>(*node).focus();
+ cache->setIsSynchronizingSelection(false);
+ }
}
void AccessibilityRenderObject::setSelectedRows(AccessibilityChildrenVector& selectedRows)
{
// Setting selected only makes sense in trees and tables (and tree-tables).
AccessibilityRole role = roleValue();
- if (role != TreeRole && role != TreeGridRole && role != TableRole)
+ if (role != TreeRole && role != TreeGridRole && role != TableRole && role != GridRole)
return;
bool isMulti = isMultiSelectable();
@@ -1625,42 +1725,30 @@ void AccessibilityRenderObject::setSelectedRows(AccessibilityChildrenVector& sel
if (count > 1 && !isMulti)
count = 1;
- for (unsigned k = 0; k < count; ++k)
- selectedRows[k]->setSelected(true);
+ for (const auto& selectedRow : selectedRows)
+ selectedRow->setSelected(true);
}
void AccessibilityRenderObject::setValue(const String& string)
{
- if (!m_renderer || !m_renderer->node() || !m_renderer->node()->isElementNode())
+ if (!m_renderer || !is<Element>(m_renderer->node()))
return;
- Element* element = toElement(m_renderer->node());
+ Element& element = downcast<Element>(*m_renderer->node());
- if (!m_renderer->isBoxModelObject())
+ if (!is<RenderBoxModelObject>(*m_renderer))
return;
- RenderBoxModelObject* renderer = toRenderBoxModelObject(m_renderer);
+ RenderBoxModelObject& renderer = downcast<RenderBoxModelObject>(*m_renderer);
// FIXME: Do we want to do anything here for ARIA textboxes?
- if (renderer->isTextField()) {
- // FIXME: This is not safe! Other elements could have a TextField renderer.
- toHTMLInputElement(element)->setValue(string);
- } else if (renderer->isTextArea()) {
- // FIXME: This is not safe! Other elements could have a TextArea renderer.
- toHTMLTextAreaElement(element)->setValue(string);
- }
+ if (renderer.isTextField() && is<HTMLInputElement>(element))
+ downcast<HTMLInputElement>(element).setValue(string);
+ else if (renderer.isTextArea() && is<HTMLTextAreaElement>(element))
+ downcast<HTMLTextAreaElement>(element).setValue(string);
}
void AccessibilityRenderObject::ariaOwnsElements(AccessibilityChildrenVector& axObjects) const
{
- Vector<Element*> elements;
- elementsFromAttribute(elements, aria_ownsAttr);
-
- unsigned count = elements.size();
- for (unsigned k = 0; k < count; ++k) {
- RenderObject* render = elements[k]->renderer();
- AccessibilityObject* obj = axObjectCache()->getOrCreate(render);
- if (obj)
- axObjects.append(obj);
- }
+ ariaElementsFromAttribute(axObjects, aria_ownsAttr);
}
bool AccessibilityRenderObject::supportsARIAOwns() const
@@ -1676,7 +1764,7 @@ RenderView* AccessibilityRenderObject::topRenderer() const
{
Document* topDoc = topDocument();
if (!topDoc)
- return 0;
+ return nullptr;
return topDoc->renderView();
}
@@ -1684,66 +1772,53 @@ RenderView* AccessibilityRenderObject::topRenderer() const
Document* AccessibilityRenderObject::document() const
{
if (!m_renderer)
- return 0;
- return m_renderer->document();
-}
-
-Document* AccessibilityRenderObject::topDocument() const
-{
- if (!document())
- return 0;
- return document()->topDocument();
-}
-
-FrameView* AccessibilityRenderObject::topDocumentFrameView() const
-{
- RenderView* renderView = topRenderer();
- if (!renderView || !renderView->view())
- return 0;
- return renderView->view()->frameView();
+ return nullptr;
+ return &m_renderer->document();
}
Widget* AccessibilityRenderObject::widget() const
{
- if (!m_renderer->isBoxModelObject() || !toRenderBoxModelObject(m_renderer)->isWidget())
- return 0;
- return toRenderWidget(m_renderer)->widget();
+ if (!is<RenderWidget>(*m_renderer))
+ return nullptr;
+ return downcast<RenderWidget>(*m_renderer).widget();
}
AccessibilityObject* AccessibilityRenderObject::accessibilityParentForImageMap(HTMLMapElement* map) const
{
// find an image that is using this map
if (!map)
- return 0;
+ return nullptr;
HTMLImageElement* imageElement = map->imageElement();
if (!imageElement)
- return 0;
+ return nullptr;
+
+ if (AXObjectCache* cache = axObjectCache())
+ return cache->getOrCreate(imageElement);
- return axObjectCache()->getOrCreate(imageElement);
+ return nullptr;
}
void AccessibilityRenderObject::getDocumentLinks(AccessibilityChildrenVector& result)
{
- Document* document = m_renderer->document();
- RefPtr<HTMLCollection> links = document->links();
- for (unsigned i = 0; Node* curr = links->item(i); i++) {
- RenderObject* obj = curr->renderer();
- if (obj) {
- RefPtr<AccessibilityObject> axobj = document->axObjectCache()->getOrCreate(obj);
- ASSERT(axobj);
- if (!axobj->accessibilityIsIgnored() && axobj->isLink())
- result.append(axobj);
+ Document& document = m_renderer->document();
+ Ref<HTMLCollection> links = document.links();
+ for (unsigned i = 0; auto* current = links->item(i); ++i) {
+ if (auto* renderer = current->renderer()) {
+ RefPtr<AccessibilityObject> axObject = document.axObjectCache()->getOrCreate(renderer);
+ ASSERT(axObject);
+ if (!axObject->accessibilityIsIgnored() && axObject->isLink())
+ result.append(axObject);
} else {
- Node* parent = curr->parentNode();
- if (parent && isHTMLAreaElement(curr) && isHTMLMapElement(parent)) {
- AccessibilityImageMapLink* areaObject = static_cast<AccessibilityImageMapLink*>(axObjectCache()->getOrCreate(ImageMapLinkRole));
- HTMLMapElement* map = toHTMLMapElement(parent);
- areaObject->setHTMLAreaElement(toHTMLAreaElement(curr));
- areaObject->setHTMLMapElement(map);
- areaObject->setParent(accessibilityParentForImageMap(map));
-
- result.append(areaObject);
+ auto* parent = current->parentNode();
+ if (is<HTMLAreaElement>(*current) && is<HTMLMapElement>(parent)) {
+ auto& areaObject = downcast<AccessibilityImageMapLink>(*axObjectCache()->getOrCreate(ImageMapLinkRole));
+ HTMLMapElement& map = downcast<HTMLMapElement>(*parent);
+ areaObject.setHTMLAreaElement(downcast<HTMLAreaElement>(current));
+ areaObject.setHTMLMapElement(&map);
+ areaObject.setParent(accessibilityParentForImageMap(&map));
+
+ result.append(&areaObject);
}
}
}
@@ -1751,26 +1826,18 @@ void AccessibilityRenderObject::getDocumentLinks(AccessibilityChildrenVector& re
FrameView* AccessibilityRenderObject::documentFrameView() const
{
- if (!m_renderer || !m_renderer->document())
- return 0;
+ if (!m_renderer)
+ return nullptr;
// this is the RenderObject's Document's Frame's FrameView
- return m_renderer->document()->view();
+ return &m_renderer->view().frameView();
}
Widget* AccessibilityRenderObject::widgetForAttachmentView() const
{
if (!isAttachment())
- return 0;
- return toRenderWidget(m_renderer)->widget();
-}
-
-FrameView* AccessibilityRenderObject::frameViewIfRenderView() const
-{
- if (!m_renderer->isRenderView())
- return 0;
- // this is the RenderObject's Document's renderer's FrameView
- return m_renderer->view()->frameView();
+ return nullptr;
+ return downcast<RenderWidget>(*m_renderer).widget();
}
// This function is like a cross-platform version of - (WebCoreTextMarkerRange*)textMarkerRange. It returns
@@ -1809,7 +1876,7 @@ VisiblePositionRange AccessibilityRenderObject::visiblePositionRangeForLine(unsi
// iterate over the lines
// FIXME: this is wrong when lineNumber is lineCount+1, because nextLinePosition takes you to the
// last offset of the last line
- VisiblePosition visiblePos = m_renderer->document()->renderer()->positionForPoint(IntPoint());
+ VisiblePosition visiblePos = m_renderer->view().positionForPoint(IntPoint(), nullptr);
VisiblePosition savedVisiblePos;
while (--lineCount) {
savedVisiblePos = visiblePos;
@@ -1833,33 +1900,24 @@ VisiblePosition AccessibilityRenderObject::visiblePositionForIndex(int index) co
{
if (!m_renderer)
return VisiblePosition();
-
+
if (isNativeTextControl())
- return toRenderTextControl(m_renderer)->visiblePositionForIndex(index);
+ return downcast<RenderTextControl>(*m_renderer).textFormControlElement().visiblePositionForIndex(index);
- if (!allowsTextRanges() && !m_renderer->isText())
+ if (!allowsTextRanges() && !is<RenderText>(*m_renderer))
return VisiblePosition();
Node* node = m_renderer->node();
if (!node)
return VisiblePosition();
-
- if (index <= 0)
- return VisiblePosition(firstPositionInOrBeforeNode(node), DOWNSTREAM);
-
- RefPtr<Range> range = Range::create(m_renderer->document());
- range->selectNodeContents(node, IGNORE_EXCEPTION);
- CharacterIterator it(range.get());
- it.advance(index - 1);
- return VisiblePosition(Position(it.range()->endContainer(), it.range()->endOffset(), Position::PositionIsOffsetInAnchor), UPSTREAM);
+
+ return visiblePositionForIndexUsingCharacterIterator(node, index);
}
int AccessibilityRenderObject::indexForVisiblePosition(const VisiblePosition& pos) const
{
- if (isNativeTextControl()) {
- HTMLTextFormControlElement* textControl = toRenderTextControl(m_renderer)->textFormControlElement();
- return textControl->indexForVisiblePosition(pos);
- }
+ if (isNativeTextControl())
+ return downcast<RenderTextControl>(*m_renderer).textFormControlElement().indexForVisiblePosition(pos);
if (!isTextControl())
return 0;
@@ -1867,28 +1925,26 @@ int AccessibilityRenderObject::indexForVisiblePosition(const VisiblePosition& po
Node* node = m_renderer->node();
if (!node)
return 0;
-
+
Position indexPosition = pos.deepEquivalent();
if (indexPosition.isNull() || highestEditableRoot(indexPosition, HasEditableAXRole) != node)
return 0;
-
- RefPtr<Range> range = Range::create(m_renderer->document());
- range->setStart(node, 0, IGNORE_EXCEPTION);
- range->setEnd(indexPosition, IGNORE_EXCEPTION);
#if PLATFORM(GTK)
// We need to consider replaced elements for GTK, as they will be
// presented with the 'object replacement character' (0xFFFC).
- return TextIterator::rangeLength(range.get(), true);
+ bool forSelectionPreservation = true;
#else
- return TextIterator::rangeLength(range.get());
+ bool forSelectionPreservation = false;
#endif
+
+ return WebCore::indexForVisiblePosition(node, pos, forSelectionPreservation);
}
Element* AccessibilityRenderObject::rootEditableElementForPosition(const Position& position) const
{
// Find the root editable or pseudo-editable (i.e. having an editable ARIA role) element.
- Element* result = 0;
+ Element* result = nullptr;
Element* rootEditableElement = position.rootEditableElement();
@@ -1910,11 +1966,12 @@ bool AccessibilityRenderObject::nodeIsTextControl(const Node* node) const
if (!node)
return false;
- const AccessibilityObject* axObjectForNode = axObjectCache()->getOrCreate(const_cast<Node*>(node));
- if (!axObjectForNode)
- return false;
+ if (AXObjectCache* cache = axObjectCache()) {
+ if (AccessibilityObject* axObjectForNode = cache->getOrCreate(const_cast<Node*>(node)))
+ return axObjectForNode->isTextControl();
+ }
- return axObjectForNode->isTextControl();
+ return false;
}
IntRect AccessibilityRenderObject::boundsForVisiblePositionRange(const VisiblePositionRange& visiblePositionRange) const
@@ -1946,16 +2003,16 @@ IntRect AccessibilityRenderObject::boundsForVisiblePositionRange(const VisiblePo
// if the rectangle spans lines and contains multiple text chars, use the range's bounding box intead
if (rect1.maxY() != rect2.maxY()) {
RefPtr<Range> dataRange = makeRange(range.start, range.end);
- LayoutRect boundingBox = dataRange->boundingBox();
+ LayoutRect boundingBox = dataRange->absoluteBoundingBox();
String rangeString = plainText(dataRange.get());
if (rangeString.length() > 1 && !boundingBox.isEmpty())
ourrect = boundingBox;
}
#if PLATFORM(MAC)
- return m_renderer->document()->view()->contentsToScreen(pixelSnappedIntRect(ourrect));
+ return m_renderer->view().frameView().contentsToScreen(snappedIntRect(ourrect));
#else
- return pixelSnappedIntRect(ourrect);
+ return snappedIntRect(ourrect);
#endif
}
@@ -1963,14 +2020,19 @@ void AccessibilityRenderObject::setSelectedVisiblePositionRange(const VisiblePos
{
if (range.start.isNull() || range.end.isNull())
return;
-
+
// make selection and tell the document to use it. if it's zero length, then move to that position
- if (range.start == range.end)
- m_renderer->frame()->selection()->moveTo(range.start, UserTriggered);
+ if (range.start == range.end) {
+ setTextSelectionIntent(axObjectCache(), AXTextStateChangeTypeSelectionMove);
+ m_renderer->frame().selection().moveTo(range.start, UserTriggered);
+ clearTextSelectionIntent(axObjectCache());
+ }
else {
+ setTextSelectionIntent(axObjectCache(), AXTextStateChangeTypeSelectionExtend);
VisibleSelection newSelection = VisibleSelection(range.start, range.end);
- m_renderer->frame()->selection()->setSelection(newSelection);
- }
+ m_renderer->frame().selection().setSelection(newSelection, FrameSelection::defaultSetSelectionOptions());
+ clearTextSelectionIntent(axObjectCache());
+ }
}
VisiblePosition AccessibilityRenderObject::visiblePositionForPoint(const IntPoint& point) const
@@ -1983,11 +2045,11 @@ VisiblePosition AccessibilityRenderObject::visiblePositionForPoint(const IntPoin
if (!renderView)
return VisiblePosition();
- FrameView* frameView = renderView->frameView();
- if (!frameView)
- return VisiblePosition();
+#if PLATFORM(COCOA)
+ FrameView* frameView = &renderView->frameView();
+#endif
- Node* innerNode = 0;
+ Node* innerNode = nullptr;
// locate the node containing the point
LayoutPoint pointResult;
@@ -2013,21 +2075,21 @@ VisiblePosition AccessibilityRenderObject::visiblePositionForPoint(const IntPoin
pointResult = result.localPoint();
// done if hit something other than a widget
- if (!renderer->isWidget())
+ if (!is<RenderWidget>(*renderer))
break;
// descend into widget (FRAME, IFRAME, OBJECT...)
- Widget* widget = toRenderWidget(renderer)->widget();
- if (!widget || !widget->isFrameView())
- break;
- Frame* frame = toFrameView(widget)->frame();
- if (!frame)
+ Widget* widget = downcast<RenderWidget>(*renderer).widget();
+ if (!is<FrameView>(widget))
break;
- renderView = frame->document()->renderView();
- frameView = toFrameView(widget);
+ Frame& frame = downcast<FrameView>(*widget).frame();
+ renderView = frame.document()->renderView();
+#if PLATFORM(COCOA)
+ frameView = downcast<FrameView>(widget);
+#endif
}
- return innerNode->renderer()->positionForPoint(pointResult);
+ return innerNode->renderer()->positionForPoint(pointResult, nullptr);
}
// NOTE: Consider providing this utility method as AX API
@@ -2133,9 +2195,6 @@ String AccessibilityRenderObject::doAXStringForRange(const PlainTextRange& range
return String();
String elementText = isPasswordField() ? passwordFieldValue() : text();
- if (range.start + range.length > elementText.length())
- return String();
-
return elementText.substring(range.start, range.length);
}
@@ -2152,34 +2211,31 @@ IntRect AccessibilityRenderObject::doAXBoundsForRange(const PlainTextRange& rang
AccessibilityObject* AccessibilityRenderObject::accessibilityImageMapHitTest(HTMLAreaElement* area, const IntPoint& point) const
{
if (!area)
- return 0;
+ return nullptr;
- AccessibilityObject* parent = 0;
+ AccessibilityObject* parent = nullptr;
for (Element* mapParent = area->parentElement(); mapParent; mapParent = mapParent->parentElement()) {
- if (isHTMLMapElement(mapParent)) {
- parent = accessibilityParentForImageMap(toHTMLMapElement(mapParent));
+ if (is<HTMLMapElement>(*mapParent)) {
+ parent = accessibilityParentForImageMap(downcast<HTMLMapElement>(mapParent));
break;
}
}
if (!parent)
- return 0;
+ return nullptr;
- AccessibilityObject::AccessibilityChildrenVector children = parent->children();
-
- unsigned count = children.size();
- for (unsigned k = 0; k < count; ++k) {
- if (children[k]->elementRect().contains(point))
- return children[k].get();
+ for (const auto& child : parent->children()) {
+ if (child->elementRect().contains(point))
+ return child.get();
}
- return 0;
+ return nullptr;
}
AccessibilityObject* AccessibilityRenderObject::remoteSVGElementHitTest(const IntPoint& point) const
{
AccessibilityObject* remote = remoteSVGRootElement();
if (!remote)
- return 0;
+ return nullptr;
IntSize offset = point - roundedIntPoint(boundingBoxRect().location());
return remote->accessibilityHitTest(IntPoint(offset));
@@ -2196,28 +2252,31 @@ AccessibilityObject* AccessibilityRenderObject::elementAccessibilityHitTest(cons
AccessibilityObject* AccessibilityRenderObject::accessibilityHitTest(const IntPoint& point) const
{
if (!m_renderer || !m_renderer->hasLayer())
- return 0;
+ return nullptr;
- RenderLayer* layer = toRenderBox(m_renderer)->layer();
+ m_renderer->document().updateLayout();
+
+ RenderLayer* layer = downcast<RenderBox>(*m_renderer).layer();
HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::AccessibilityHitTest);
HitTestResult hitTestResult = HitTestResult(point);
layer->hitTest(request, hitTestResult);
if (!hitTestResult.innerNode())
- return 0;
+ return nullptr;
Node* node = hitTestResult.innerNode()->deprecatedShadowAncestorNode();
+ ASSERT(node);
- if (isHTMLAreaElement(node))
- return accessibilityImageMapHitTest(toHTMLAreaElement(node), point);
+ if (is<HTMLAreaElement>(*node))
+ return accessibilityImageMapHitTest(downcast<HTMLAreaElement>(node), point);
- if (isHTMLOptionElement(node))
- node = toHTMLOptionElement(node)->ownerSelectElement();
+ if (is<HTMLOptionElement>(*node))
+ node = downcast<HTMLOptionElement>(*node).ownerSelectElement();
RenderObject* obj = node->renderer();
if (!obj)
- return 0;
+ return nullptr;
- AccessibilityObject* result = obj->document()->axObjectCache()->getOrCreate(obj);
+ AccessibilityObject* result = obj->document().axObjectCache()->getOrCreate(obj);
result->updateChildrenIfNecessary();
// Allow the element to perform any hit-testing it might need to do to reach non-render children.
@@ -2276,25 +2335,28 @@ bool AccessibilityRenderObject::shouldFocusActiveDescendant() const
AccessibilityObject* AccessibilityRenderObject::activeDescendant() const
{
if (!m_renderer)
- return 0;
+ return nullptr;
- if (m_renderer->node() && !m_renderer->node()->isElementNode())
- return 0;
- Element* element = toElement(m_renderer->node());
-
- const AtomicString& activeDescendantAttrStr = element->getAttribute(aria_activedescendantAttr);
+ const AtomicString& activeDescendantAttrStr = getAttribute(aria_activedescendantAttr);
if (activeDescendantAttrStr.isNull() || activeDescendantAttrStr.isEmpty())
- return 0;
+ return nullptr;
+
+ Element* element = this->element();
+ if (!element)
+ return nullptr;
- Element* target = element->treeScope()->getElementById(activeDescendantAttrStr);
+ Element* target = element->treeScope().getElementById(activeDescendantAttrStr);
if (!target)
- return 0;
+ return nullptr;
- AccessibilityObject* obj = axObjectCache()->getOrCreate(target);
- if (obj && obj->isAccessibilityRenderObject())
- // an activedescendant is only useful if it has a renderer, because that's what's needed to post the notification
- return obj;
- return 0;
+ if (AXObjectCache* cache = axObjectCache()) {
+ AccessibilityObject* obj = cache->getOrCreate(target);
+ if (obj && obj->isAccessibilityRenderObject())
+ // an activedescendant is only useful if it has a renderer, because that's what's needed to post the notification
+ return obj;
+ }
+
+ return nullptr;
}
void AccessibilityRenderObject::handleAriaExpandedChanged()
@@ -2323,41 +2385,45 @@ void AccessibilityRenderObject::handleAriaExpandedChanged()
}
// Post that the row count changed.
+ AXObjectCache* cache = axObjectCache();
+ if (!cache)
+ return;
+
if (containerParent)
- axObjectCache()->postNotification(containerParent, document(), AXObjectCache::AXRowCountChanged, true);
+ cache->postNotification(containerParent, document(), AXObjectCache::AXRowCountChanged);
// Post that the specific row either collapsed or expanded.
if (roleValue() == RowRole || roleValue() == TreeItemRole)
- axObjectCache()->postNotification(this, document(), isExpanded() ? AXObjectCache::AXRowExpanded : AXObjectCache::AXRowCollapsed, true);
+ cache->postNotification(this, document(), isExpanded() ? AXObjectCache::AXRowExpanded : AXObjectCache::AXRowCollapsed);
+ else
+ cache->postNotification(this, document(), AXObjectCache::AXExpandedChanged);
}
void AccessibilityRenderObject::handleActiveDescendantChanged()
{
- Element* element = toElement(renderer()->node());
+ Element* element = downcast<Element>(renderer()->node());
if (!element)
return;
- Document* doc = renderer()->document();
- if (!doc->frame()->selection()->isFocusedAndActive() || doc->focusedElement() != element)
- return;
- AccessibilityRenderObject* activedescendant = static_cast<AccessibilityRenderObject*>(activeDescendant());
-
- if (activedescendant && shouldNotifyActiveDescendant())
- doc->axObjectCache()->postNotification(m_renderer, AXObjectCache::AXActiveDescendantChanged, true);
+ if (!renderer()->frame().selection().isFocusedAndActive() || renderer()->document().focusedElement() != element)
+ return;
+
+ if (activeDescendant() && shouldNotifyActiveDescendant())
+ renderer()->document().axObjectCache()->postNotification(m_renderer, AXObjectCache::AXActiveDescendantChanged);
}
AccessibilityObject* AccessibilityRenderObject::correspondingControlForLabelElement() const
{
HTMLLabelElement* labelElement = labelElementContainer();
if (!labelElement)
- return 0;
+ return nullptr;
HTMLElement* correspondingControl = labelElement->control();
if (!correspondingControl)
- return 0;
+ return nullptr;
// Make sure the corresponding control isn't a descendant of this label that's in the middle of being destroyed.
if (correspondingControl->renderer() && !correspondingControl->renderer()->parent())
- return 0;
+ return nullptr;
return axObjectCache()->getOrCreate(correspondingControl);
}
@@ -2365,36 +2431,38 @@ AccessibilityObject* AccessibilityRenderObject::correspondingControlForLabelElem
AccessibilityObject* AccessibilityRenderObject::correspondingLabelForControlElement() const
{
if (!m_renderer)
- return 0;
+ return nullptr;
// ARIA: section 2A, bullet #3 says if aria-labeledby or aria-label appears, it should
// override the "label" element association.
if (hasTextAlternative())
- return 0;
+ return nullptr;
Node* node = m_renderer->node();
- if (node && node->isHTMLElement()) {
- HTMLLabelElement* label = labelForElement(toElement(node));
- if (label)
+ if (is<HTMLElement>(node)) {
+ if (HTMLLabelElement* label = labelForElement(downcast<HTMLElement>(node)))
return axObjectCache()->getOrCreate(label);
}
- return 0;
+ return nullptr;
}
-bool AccessibilityRenderObject::renderObjectIsObservable(RenderObject* renderer) const
+bool AccessibilityRenderObject::renderObjectIsObservable(RenderObject& renderer) const
{
// AX clients will listen for AXValueChange on a text control.
- if (renderer->isTextControl())
+ if (is<RenderTextControl>(renderer))
return true;
// AX clients will listen for AXSelectedChildrenChanged on listboxes.
- Node* node = renderer->node();
- if (nodeHasRole(node, "listbox") || (renderer->isBoxModelObject() && toRenderBoxModelObject(renderer)->isListBox()))
+ Node* node = renderer.node();
+ if (!node)
+ return false;
+
+ if (nodeHasRole(node, "listbox") || (is<RenderBoxModelObject>(renderer) && downcast<RenderBoxModelObject>(renderer).isListBox()))
return true;
// Textboxes should send out notifications.
- if (nodeHasRole(node, "textbox"))
+ if (nodeHasRole(node, "textbox") || (is<Element>(*node) && contentEditableAttributeIsEnabled(downcast<Element>(node))))
return true;
return false;
@@ -2404,41 +2472,61 @@ AccessibilityObject* AccessibilityRenderObject::observableObject() const
{
// Find the object going up the parent chain that is used in accessibility to monitor certain notifications.
for (RenderObject* renderer = m_renderer; renderer && renderer->node(); renderer = renderer->parent()) {
- if (renderObjectIsObservable(renderer))
- return axObjectCache()->getOrCreate(renderer);
+ if (renderObjectIsObservable(*renderer)) {
+ if (AXObjectCache* cache = axObjectCache())
+ return cache->getOrCreate(renderer);
+ }
}
- return 0;
+ return nullptr;
}
bool AccessibilityRenderObject::isDescendantOfElementType(const QualifiedName& tagName) const
{
- for (RenderObject* parent = m_renderer->parent(); parent; parent = parent->parent()) {
- if (parent->node() && parent->node()->hasTagName(tagName))
+ for (auto& ancestor : ancestorsOfType<RenderElement>(*m_renderer)) {
+ if (ancestor.element() && ancestor.element()->hasTagName(tagName))
return true;
}
return false;
}
+
+String AccessibilityRenderObject::expandedTextValue() const
+{
+ if (AccessibilityObject* parent = parentObject()) {
+ if (parent->hasTagName(abbrTag) || parent->hasTagName(acronymTag))
+ return parent->getAttribute(titleAttr);
+ }
+
+ return String();
+}
+
+bool AccessibilityRenderObject::supportsExpandedTextValue() const
+{
+ if (roleValue() == StaticTextRole) {
+ if (AccessibilityObject* parent = parentObject())
+ return parent->hasTagName(abbrTag) || parent->hasTagName(acronymTag);
+ }
+
+ return false;
+}
AccessibilityRole AccessibilityRenderObject::determineAccessibilityRole()
{
if (!m_renderer)
return UnknownRole;
- m_ariaRole = determineAriaRoleAttribute();
+ // Sometimes we need to ignore the attribute role. Like if a tree is malformed,
+ // we want to ignore the treeitem's attribute role.
+ if ((m_ariaRole = determineAriaRoleAttribute()) != UnknownRole && !shouldIgnoreAttributeRole())
+ return m_ariaRole;
Node* node = m_renderer->node();
- AccessibilityRole ariaRole = ariaRoleAttribute();
- if (ariaRole != UnknownRole)
- return ariaRole;
-
RenderBoxModelObject* cssBox = renderBoxModelObject();
- if (node && node->isLink()) {
- if (cssBox && cssBox->isImage())
- return ImageMapRole;
+ if (node && node->isLink())
return WebCoreLinkRole;
- }
+ if (node && is<HTMLImageElement>(*node) && downcast<HTMLImageElement>(*node).fastHasAttribute(usemapAttr))
+ return ImageMapRole;
if ((cssBox && cssBox->isListItem()) || (node && node->hasTagName(liTag)))
return ListItemRole;
if (m_renderer->isListMarker())
@@ -2450,7 +2538,7 @@ AccessibilityRole AccessibilityRenderObject::determineAccessibilityRole()
if (m_renderer->isText())
return StaticTextRole;
if (cssBox && cssBox->isImage()) {
- if (node && isHTMLInputElement(node))
+ if (is<HTMLInputElement>(node))
return ariaHasPopup() ? PopUpButtonRole : ButtonRole;
if (isSVGImage())
return SVGRootRole;
@@ -2460,35 +2548,39 @@ AccessibilityRole AccessibilityRenderObject::determineAccessibilityRole()
if (node && node->hasTagName(canvasTag))
return CanvasRole;
- if (cssBox && cssBox->isRenderView()) {
- // If the iframe is seamless, it should not be announced as a web area to AT clients.
- if (document() && document()->shouldDisplaySeamlesslyWithParent())
- return SeamlessWebAreaRole;
+ if (cssBox && cssBox->isRenderView())
return WebAreaRole;
- }
- if (cssBox && cssBox->isTextField())
- return TextFieldRole;
+ if (cssBox && cssBox->isTextField()) {
+ if (is<HTMLInputElement>(node))
+ return downcast<HTMLInputElement>(*node).isSearchField() ? SearchFieldRole : TextFieldRole;
+ }
if (cssBox && cssBox->isTextArea())
return TextAreaRole;
- if (node && isHTMLInputElement(node)) {
- HTMLInputElement* input = toHTMLInputElement(node);
- if (input->isCheckbox())
+ if (is<HTMLInputElement>(node)) {
+ HTMLInputElement& input = downcast<HTMLInputElement>(*node);
+ if (input.isCheckbox())
return CheckBoxRole;
- if (input->isRadioButton())
+ if (input.isRadioButton())
return RadioButtonRole;
- if (input->isTextButton())
+ if (input.isTextButton())
return buttonRoleType();
-
+ // On iOS, the date field and time field are popup buttons. On other platforms they are text fields.
+#if PLATFORM(IOS)
+ if (input.isDateField() || input.isTimeField())
+ return PopUpButtonRole;
+#endif
#if ENABLE(INPUT_TYPE_COLOR)
- const AtomicString& type = input->getAttribute(typeAttr);
- if (equalIgnoringCase(type, "color"))
+ if (input.isColorControl())
return ColorWellRole;
#endif
}
-
+
+ if (hasContentEditableAttributeSet())
+ return TextAreaRole;
+
if (isFileUploadButton())
return ButtonRole;
@@ -2498,15 +2590,16 @@ AccessibilityRole AccessibilityRenderObject::determineAccessibilityRole()
if (headingLevel())
return HeadingRole;
-#if ENABLE(SVG)
if (m_renderer->isSVGImage())
return ImageRole;
if (m_renderer->isSVGRoot())
return SVGRootRole;
if (node && node->hasTagName(SVGNames::gTag))
return GroupRole;
-#endif
-
+
+ if (isStyleFormatGroup())
+ return is<RenderInline>(*m_renderer) ? InlineRole : GroupRole;
+
#if ENABLE(MATHML)
if (node && node->hasTagName(MathMLNames::mathTag))
return DocumentMathRole;
@@ -2525,19 +2618,27 @@ AccessibilityRole AccessibilityRenderObject::determineAccessibilityRole()
if (node && node->hasTagName(dlTag))
return DescriptionListRole;
- if (node && (node->hasTagName(rpTag) || node->hasTagName(rtTag)))
- return AnnotationRole;
-
-#if PLATFORM(GTK)
- // Gtk ATs expect all tables, data and layout, to be exposed as tables.
- if (node && (node->hasTagName(tdTag) || node->hasTagName(thTag)))
- return CellRole;
-
- if (node && node->hasTagName(trTag))
- return RowRole;
-
- if (node && isHTMLTableElement(node))
- return TableRole;
+ // Check for Ruby elements
+ if (m_renderer->isRubyText())
+ return RubyTextRole;
+ if (m_renderer->isRubyBase())
+ return RubyBaseRole;
+ if (m_renderer->isRubyRun())
+ return RubyRunRole;
+ if (m_renderer->isRubyBlock())
+ return RubyBlockRole;
+ if (m_renderer->isRubyInline())
+ return RubyInlineRole;
+
+ // This return value is what will be used if AccessibilityTableCell determines
+ // the cell should not be treated as a cell (e.g. because it is a layout table.
+ // In ATK, there is a distinction between generic text block elements and other
+ // generic containers; AX API does not make this distinction.
+ if (is<RenderTableCell>(m_renderer))
+#if PLATFORM(GTK) || PLATFORM(EFL)
+ return DivRole;
+#else
+ return GroupRole;
#endif
// Table sections should be ignored.
@@ -2550,7 +2651,7 @@ AccessibilityRole AccessibilityRenderObject::determineAccessibilityRole()
if (node && node->hasTagName(pTag))
return ParagraphRole;
- if (node && isHTMLLabelElement(node))
+ if (is<HTMLLabelElement>(node))
return LabelRole;
if (node && node->hasTagName(dfnTag))
@@ -2559,7 +2660,7 @@ AccessibilityRole AccessibilityRenderObject::determineAccessibilityRole()
if (node && node->hasTagName(divTag))
return DivRole;
- if (node && isHTMLFormElement(node))
+ if (is<HTMLFormElement>(node))
return FormRole;
if (node && node->hasTagName(articleTag))
@@ -2580,6 +2681,27 @@ AccessibilityRole AccessibilityRenderObject::determineAccessibilityRole()
if (node && node->hasTagName(addressTag))
return LandmarkContentInfoRole;
+ if (node && node->hasTagName(blockquoteTag))
+ return BlockquoteRole;
+
+ if (node && node->hasTagName(captionTag))
+ return CaptionRole;
+
+ if (node && node->hasTagName(preTag))
+ return PreRole;
+
+ if (is<HTMLDetailsElement>(node))
+ return DetailsRole;
+ if (is<HTMLSummaryElement>(node))
+ return SummaryRole;
+
+#if ENABLE(VIDEO)
+ if (is<HTMLVideoElement>(node))
+ return VideoRole;
+ if (is<HTMLAudioElement>(node))
+ return AudioRole;
+#endif
+
// The HTML element should not be exposed as an element. That's what the RenderView element does.
if (node && node->hasTagName(htmlTag))
return IgnoredRole;
@@ -2590,25 +2712,51 @@ AccessibilityRole AccessibilityRenderObject::determineAccessibilityRole()
return LandmarkBannerRole;
if (node && node->hasTagName(footerTag) && !isDescendantOfElementType(articleTag) && !isDescendantOfElementType(sectionTag))
return FooterRole;
-
- if (m_renderer->isBlockFlow())
- return GroupRole;
- // If the element does not have role, but it has ARIA attributes, accessibility should fallback to exposing it as a group.
- if (supportsARIAAttributes())
+ // If the element does not have role, but it has ARIA attributes, or accepts tab focus, accessibility should fallback to exposing it as a group.
+ if (supportsARIAAttributes() || canSetFocusAttribute())
return GroupRole;
+
+ if (m_renderer->isRenderBlockFlow()) {
+#if PLATFORM(GTK) || PLATFORM(EFL)
+ // For ATK, GroupRole maps to ATK_ROLE_PANEL. Panels are most commonly found (and hence
+ // expected) in UI elements; not text blocks.
+ return m_renderer->isAnonymousBlock() ? DivRole : GroupRole;
+#else
+ return GroupRole;
+#endif
+ }
+ // InlineRole is the final fallback before assigning UnknownRole to an object. It makes it
+ // possible to distinguish truly unknown objects from non-focusable inline text elements
+ // which have an event handler or attribute suggesting possible inclusion by the platform.
+ if (is<RenderInline>(*m_renderer)
+ && (hasAttributesRequiredForInclusion()
+ || (node && node->hasEventListeners())
+ || (supportsDatetimeAttribute() && !getAttribute(datetimeAttr).isEmpty())))
+ return InlineRole;
+
return UnknownRole;
}
AccessibilityOrientation AccessibilityRenderObject::orientation() const
{
const AtomicString& ariaOrientation = getAttribute(aria_orientationAttr);
- if (equalIgnoringCase(ariaOrientation, "horizontal"))
+ if (equalLettersIgnoringASCIICase(ariaOrientation, "horizontal"))
return AccessibilityOrientationHorizontal;
- if (equalIgnoringCase(ariaOrientation, "vertical"))
+ if (equalLettersIgnoringASCIICase(ariaOrientation, "vertical"))
+ return AccessibilityOrientationVertical;
+ if (equalLettersIgnoringASCIICase(ariaOrientation, "undefined"))
+ return AccessibilityOrientationUndefined;
+
+ // ARIA 1.1 Implicit defaults are defined on some roles.
+ // http://www.w3.org/TR/wai-aria-1.1/#aria-orientation
+ if (isScrollbar() || isComboBox() || isListBox() || isMenu() || isTree())
return AccessibilityOrientationVertical;
+ if (isMenuBar() || isSplitter() || isTabList() || isToolbar())
+ return AccessibilityOrientationHorizontal;
+
return AccessibilityObject::orientation();
}
@@ -2621,18 +2769,25 @@ bool AccessibilityRenderObject::inheritsPresentationalRole() const
// ARIA spec says that when a parent object is presentational, and it has required child elements,
// those child elements are also presentational. For example, <li> becomes presentational from <ul>.
// http://www.w3.org/WAI/PF/aria/complete#presentation
- DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, listItemParents, ());
+ static NeverDestroyed<HashSet<QualifiedName>> listItemParents;
+ static NeverDestroyed<HashSet<QualifiedName>> tableCellParents;
- HashSet<QualifiedName>* possibleParentTagNames = 0;
+ HashSet<QualifiedName>* possibleParentTagNames = nullptr;
switch (roleValue()) {
case ListItemRole:
case ListMarkerRole:
- if (listItemParents.isEmpty()) {
- listItemParents.add(ulTag);
- listItemParents.add(olTag);
- listItemParents.add(dlTag);
+ if (listItemParents.get().isEmpty()) {
+ listItemParents.get().add(ulTag);
+ listItemParents.get().add(olTag);
+ listItemParents.get().add(dlTag);
}
- possibleParentTagNames = &listItemParents;
+ possibleParentTagNames = &listItemParents.get();
+ break;
+ case GridCellRole:
+ case CellRole:
+ if (tableCellParents.get().isEmpty())
+ tableCellParents.get().add(tableTag);
+ possibleParentTagNames = &tableCellParents.get();
break;
default:
break;
@@ -2643,16 +2798,16 @@ bool AccessibilityRenderObject::inheritsPresentationalRole() const
return false;
for (AccessibilityObject* parent = parentObject(); parent; parent = parent->parentObject()) {
- if (!parent->isAccessibilityRenderObject())
+ if (!is<AccessibilityRenderObject>(*parent))
continue;
- Node* elementNode = static_cast<AccessibilityRenderObject*>(parent)->node();
- if (!elementNode || !elementNode->isElementNode())
+ Node* node = downcast<AccessibilityRenderObject>(*parent).node();
+ if (!is<Element>(node))
continue;
// If native tag of the parent element matches an acceptable name, then return
// based on its presentational status.
- if (possibleParentTagNames->contains(toElement(elementNode)->tagQName()))
+ if (possibleParentTagNames->contains(downcast<Element>(node)->tagQName()))
return parent->roleValue() == PresentationalRole;
}
@@ -2686,34 +2841,12 @@ bool AccessibilityRenderObject::ariaRoleHasPresentationalChildren() const
bool AccessibilityRenderObject::canSetExpandedAttribute() const
{
+ if (roleValue() == DetailsRole)
+ return true;
+
// An object can be expanded if it aria-expanded is true or false.
const AtomicString& ariaExpanded = getAttribute(aria_expandedAttr);
- return equalIgnoringCase(ariaExpanded, "true") || equalIgnoringCase(ariaExpanded, "false");
-}
-
-bool AccessibilityRenderObject::canSetValueAttribute() const
-{
-
- // In the event of a (Boolean)@readonly and (True/False/Undefined)@aria-readonly
- // value mismatch, the host language native attribute value wins.
- if (isNativeTextControl())
- return !isReadOnly();
-
- if (equalIgnoringCase(getAttribute(aria_readonlyAttr), "true"))
- return false;
-
- if (equalIgnoringCase(getAttribute(aria_readonlyAttr), "false"))
- return true;
-
- if (isProgressIndicator() || isSlider())
- return true;
-
- if (isTextControl() && !isNativeTextControl())
- return true;
-
- // Any node could be contenteditable, so isReadOnly should be relied upon
- // for this information for all elements.
- return !isReadOnly();
+ return equalLettersIgnoringASCIICase(ariaExpanded, "true") || equalLettersIgnoringASCIICase(ariaExpanded, "false");
}
bool AccessibilityRenderObject::canSetTextRangeAttributes() const
@@ -2726,16 +2859,19 @@ void AccessibilityRenderObject::textChanged()
// If this element supports ARIA live regions, or is part of a region with an ARIA editable role,
// then notify the AT of changes.
AXObjectCache* cache = axObjectCache();
+ if (!cache)
+ return;
+
for (RenderObject* renderParent = m_renderer; renderParent; renderParent = renderParent->parent()) {
AccessibilityObject* parent = cache->get(renderParent);
if (!parent)
continue;
if (parent->supportsARIALiveRegion())
- cache->postNotification(renderParent, AXObjectCache::AXLiveRegionChanged, true);
+ cache->postNotification(renderParent, AXObjectCache::AXLiveRegionChanged);
- if (parent->isARIATextControl() && !parent->isNativeTextControl() && !parent->node()->rendererIsEditable())
- cache->postNotification(renderParent, AXObjectCache::AXValueChanged, true);
+ if (parent->isNonNativeTextControl())
+ cache->postNotification(renderParent, AXObjectCache::AXValueChanged);
}
}
@@ -2748,25 +2884,25 @@ void AccessibilityRenderObject::clearChildren()
void AccessibilityRenderObject::addImageMapChildren()
{
RenderBoxModelObject* cssBox = renderBoxModelObject();
- if (!cssBox || !cssBox->isRenderImage())
+ if (!is<RenderImage>(cssBox))
return;
- HTMLMapElement* map = toRenderImage(cssBox)->imageMap();
+ HTMLMapElement* map = downcast<RenderImage>(*cssBox).imageMap();
if (!map)
return;
- for (Element* current = ElementTraversal::firstWithin(map); current; current = ElementTraversal::next(current, map)) {
+ for (auto& area : descendantsOfType<HTMLAreaElement>(*map)) {
// add an <area> element for this child if it has a link
- if (isHTMLAreaElement(current) && current->isLink()) {
- AccessibilityImageMapLink* areaObject = static_cast<AccessibilityImageMapLink*>(axObjectCache()->getOrCreate(ImageMapLinkRole));
- areaObject->setHTMLAreaElement(toHTMLAreaElement(current));
- areaObject->setHTMLMapElement(map);
- areaObject->setParent(this);
- if (!areaObject->accessibilityIsIgnored())
- m_children.append(areaObject);
- else
- axObjectCache()->remove(areaObject->axObjectID());
- }
+ if (!area.isLink())
+ continue;
+ auto& areaObject = downcast<AccessibilityImageMapLink>(*axObjectCache()->getOrCreate(ImageMapLinkRole));
+ areaObject.setHTMLAreaElement(&area);
+ areaObject.setHTMLMapElement(map);
+ areaObject.setParent(this);
+ if (!areaObject.accessibilityIsIgnored())
+ m_children.append(&areaObject);
+ else
+ axObjectCache()->remove(areaObject.axObjectID());
}
}
@@ -2781,18 +2917,18 @@ void AccessibilityRenderObject::updateChildrenIfNecessary()
void AccessibilityRenderObject::addTextFieldChildren()
{
Node* node = this->node();
- if (!node || !isHTMLInputElement(node))
+ if (!is<HTMLInputElement>(node))
return;
- HTMLInputElement* input = toHTMLInputElement(node);
- HTMLElement* spinButtonElement = input->innerSpinButtonElement();
- if (!spinButtonElement || !spinButtonElement->isSpinButtonElement())
+ HTMLInputElement& input = downcast<HTMLInputElement>(*node);
+ HTMLElement* spinButtonElement = input.innerSpinButtonElement();
+ if (!is<SpinButtonElement>(spinButtonElement))
return;
- AccessibilitySpinButton* axSpinButton = static_cast<AccessibilitySpinButton*>(axObjectCache()->getOrCreate(SpinButtonRole));
- axSpinButton->setSpinButtonElement(static_cast<SpinButtonElement*>(spinButtonElement));
- axSpinButton->setParent(this);
- m_children.append(axSpinButton);
+ auto& axSpinButton = downcast<AccessibilitySpinButton>(*axObjectCache()->getOrCreate(SpinButtonRole));
+ axSpinButton.setSpinButtonElement(downcast<SpinButtonElement>(spinButtonElement));
+ axSpinButton.setParent(this);
+ m_children.append(&axSpinButton);
}
bool AccessibilityRenderObject::isSVGImage() const
@@ -2803,54 +2939,50 @@ bool AccessibilityRenderObject::isSVGImage() const
void AccessibilityRenderObject::detachRemoteSVGRoot()
{
if (AccessibilitySVGRoot* root = remoteSVGRootElement())
- root->setParent(0);
+ root->setParent(nullptr);
}
AccessibilitySVGRoot* AccessibilityRenderObject::remoteSVGRootElement() const
{
-#if ENABLE(SVG)
- if (!m_renderer || !m_renderer->isRenderImage())
- return 0;
+ if (!is<RenderImage>(m_renderer))
+ return nullptr;
- CachedImage* cachedImage = toRenderImage(m_renderer)->cachedImage();
+ CachedImage* cachedImage = downcast<RenderImage>(*m_renderer).cachedImage();
if (!cachedImage)
- return 0;
+ return nullptr;
Image* image = cachedImage->image();
- if (!image || !image->isSVGImage())
- return 0;
+ if (!is<SVGImage>(image))
+ return nullptr;
- SVGImage* svgImage = static_cast<SVGImage*>(image);
- FrameView* frameView = svgImage->frameView();
+ FrameView* frameView = downcast<SVGImage>(*image).frameView();
if (!frameView)
- return 0;
- Frame* frame = frameView->frame();
- if (!frame)
- return 0;
+ return nullptr;
+ Frame& frame = frameView->frame();
- Document* doc = frame->document();
- if (!doc || !doc->isSVGDocument())
- return 0;
+ Document* document = frame.document();
+ if (!is<SVGDocument>(document))
+ return nullptr;
- SVGSVGElement* rootElement = toSVGDocument(doc)->rootElement();
+ SVGSVGElement* rootElement = downcast<SVGDocument>(*document).rootElement();
if (!rootElement)
- return 0;
+ return nullptr;
RenderObject* rendererRoot = rootElement->renderer();
if (!rendererRoot)
- return 0;
+ return nullptr;
- AccessibilityObject* rootSVGObject = frame->document()->axObjectCache()->getOrCreate(rendererRoot);
+ AXObjectCache* cache = frame.document()->axObjectCache();
+ if (!cache)
+ return nullptr;
+ AccessibilityObject* rootSVGObject = cache->getOrCreate(rendererRoot);
// In order to connect the AX hierarchy from the SVG root element from the loaded resource
// the parent must be set, because there's no other way to get back to who created the image.
- ASSERT(rootSVGObject && rootSVGObject->isAccessibilitySVGRoot());
- if (!rootSVGObject->isAccessibilitySVGRoot())
- return 0;
+ ASSERT(rootSVGObject);
+ if (!is<AccessibilitySVGRoot>(*rootSVGObject))
+ return nullptr;
- return toAccessibilitySVGRoot(rootSVGObject);
-#else
- return 0;
-#endif
+ return downcast<AccessibilitySVGRoot>(rootSVGObject);
}
void AccessibilityRenderObject::addRemoteSVGChildren()
@@ -2862,17 +2994,17 @@ void AccessibilityRenderObject::addRemoteSVGChildren()
root->setParent(this);
if (root->accessibilityIsIgnored()) {
- AccessibilityChildrenVector children = root->children();
- unsigned length = children.size();
- for (unsigned i = 0; i < length; ++i)
- m_children.append(children[i]);
+ for (const auto& child : root->children())
+ m_children.append(child);
} else
m_children.append(root);
}
void AccessibilityRenderObject::addCanvasChildren()
{
- if (!node() || !node()->hasTagName(canvasTag))
+ // Add the unrendered canvas children as AX nodes, unless we're not using a canvas renderer
+ // because JS is disabled for example.
+ if (!node() || !node()->hasTagName(canvasTag) || (renderer() && !renderer()->isCanvas()))
return;
// If it's a canvas, it won't have rendered children, but it might have accessible fallback content.
@@ -2897,7 +3029,7 @@ void AccessibilityRenderObject::addAttachmentChildren()
m_children.append(axWidget);
}
-#if PLATFORM(MAC)
+#if PLATFORM(COCOA)
void AccessibilityRenderObject::updateAttachmentViewParents()
{
// Only the unignored parent should set the attachment parent, because that's what is reflected in the AX
@@ -2905,10 +3037,9 @@ void AccessibilityRenderObject::updateAttachmentViewParents()
if (accessibilityIsIgnored())
return;
- size_t length = m_children.size();
- for (size_t k = 0; k < length; k++) {
- if (m_children[k]->isAttachment())
- m_children[k]->overrideAttachmentParent(this);
+ for (const auto& child : m_children) {
+ if (child->isAttachment())
+ child->overrideAttachmentParent(this);
}
}
#endif
@@ -2942,11 +3073,11 @@ void AccessibilityRenderObject::addHiddenChildren()
// Find out where the last render sibling is located within m_children.
AccessibilityObject* childObject = axObjectCache()->get(child->renderer());
if (childObject && childObject->accessibilityIsIgnored()) {
- AccessibilityChildrenVector children = childObject->children();
+ auto& children = childObject->children();
if (children.size())
childObject = children.last().get();
else
- childObject = 0;
+ childObject = nullptr;
}
if (childObject)
@@ -2966,6 +3097,24 @@ void AccessibilityRenderObject::addHiddenChildren()
}
}
+void AccessibilityRenderObject::updateRoleAfterChildrenCreation()
+{
+ // If a menu does not have valid menuitem children, it should not be exposed as a menu.
+ if (roleValue() == MenuRole) {
+ // Elements marked as menus must have at least one menu item child.
+ size_t menuItemCount = 0;
+ for (const auto& child : children()) {
+ if (child->isMenuItem()) {
+ menuItemCount++;
+ break;
+ }
+ }
+
+ if (!menuItemCount)
+ m_role = GroupRole;
+ }
+}
+
void AccessibilityRenderObject::addChildren()
{
// If the need to add more children in addition to existing children arises,
@@ -2987,9 +3136,11 @@ void AccessibilityRenderObject::addChildren()
addCanvasChildren();
addRemoteSVGChildren();
-#if PLATFORM(MAC)
+#if PLATFORM(COCOA)
updateAttachmentViewParents();
#endif
+
+ updateRoleAfterChildrenCreation();
}
bool AccessibilityRenderObject::canHaveChildren() const
@@ -3000,36 +3151,19 @@ bool AccessibilityRenderObject::canHaveChildren() const
return AccessibilityNodeObject::canHaveChildren();
}
-const AtomicString& AccessibilityRenderObject::ariaLiveRegionStatus() const
+const String AccessibilityRenderObject::ariaLiveRegionStatus() const
{
- DEFINE_STATIC_LOCAL(const AtomicString, liveRegionStatusAssertive, ("assertive", AtomicString::ConstructFromLiteral));
- DEFINE_STATIC_LOCAL(const AtomicString, liveRegionStatusPolite, ("polite", AtomicString::ConstructFromLiteral));
- DEFINE_STATIC_LOCAL(const AtomicString, liveRegionStatusOff, ("off", AtomicString::ConstructFromLiteral));
-
const AtomicString& liveRegionStatus = getAttribute(aria_liveAttr);
// These roles have implicit live region status.
- if (liveRegionStatus.isEmpty()) {
- switch (roleValue()) {
- case ApplicationAlertDialogRole:
- case ApplicationAlertRole:
- return liveRegionStatusAssertive;
- case ApplicationLogRole:
- case ApplicationStatusRole:
- return liveRegionStatusPolite;
- case ApplicationTimerRole:
- case ApplicationMarqueeRole:
- return liveRegionStatusOff;
- default:
- break;
- }
- }
+ if (liveRegionStatus.isEmpty())
+ return defaultLiveRegionStatusForRole(roleValue());
return liveRegionStatus;
}
const AtomicString& AccessibilityRenderObject::ariaLiveRegionRelevant() const
{
- DEFINE_STATIC_LOCAL(const AtomicString, defaultLiveRegionRelevant, ("additions text", AtomicString::ConstructFromLiteral));
+ static NeverDestroyed<const AtomicString> defaultLiveRegionRelevant("additions text", AtomicString::ConstructFromLiteral);
const AtomicString& relevant = getAttribute(aria_relevantAttr);
// Default aria-relevant = "additions text".
@@ -3041,7 +3175,20 @@ const AtomicString& AccessibilityRenderObject::ariaLiveRegionRelevant() const
bool AccessibilityRenderObject::ariaLiveRegionAtomic() const
{
- return elementAttributeValue(aria_atomicAttr);
+ const AtomicString& atomic = getAttribute(aria_atomicAttr);
+ if (equalLettersIgnoringASCIICase(atomic, "true"))
+ return true;
+ if (equalLettersIgnoringASCIICase(atomic, "false"))
+ return false;
+
+ // WAI-ARIA "alert" and "status" roles have an implicit aria-atomic value of true.
+ switch (roleValue()) {
+ case ApplicationAlertRole:
+ case ApplicationStatusRole:
+ return true;
+ default:
+ return false;
+ }
}
bool AccessibilityRenderObject::ariaLiveRegionBusy() const
@@ -3051,13 +3198,6 @@ bool AccessibilityRenderObject::ariaLiveRegionBusy() const
void AccessibilityRenderObject::ariaSelectedRows(AccessibilityChildrenVector& result)
{
- // Get all the rows.
- AccessibilityChildrenVector allRows;
- if (isTree())
- ariaTreeRows(allRows);
- else if (isAccessibilityTable() && toAccessibilityTable(this)->supportsSelectedRows())
- allRows = toAccessibilityTable(this)->rows();
-
// Determine which rows are selected.
bool isMulti = isMultiSelectable();
@@ -3069,13 +3209,24 @@ void AccessibilityRenderObject::ariaSelectedRows(AccessibilityChildrenVector& re
return;
}
- unsigned count = allRows.size();
- for (unsigned k = 0; k < count; ++k) {
- if (allRows[k]->isSelected()) {
- result.append(allRows[k]);
- if (!isMulti)
- break;
+ // Get all the rows.
+ auto rowsIteration = [&](const AccessibilityChildrenVector& rows) {
+ for (auto& row : rows) {
+ if (row->isSelected()) {
+ result.append(row);
+ if (!isMulti)
+ break;
+ }
}
+ };
+ if (isTree()) {
+ AccessibilityChildrenVector allRows;
+ ariaTreeRows(allRows);
+ rowsIteration(allRows);
+ } else if (is<AccessibilityTable>(*this)) {
+ auto& thisTable = downcast<AccessibilityTable>(*this);
+ if (thisTable.isExposableThroughAccessibility() && thisTable.supportsSelectedRows())
+ rowsIteration(thisTable.rows());
}
}
@@ -3083,11 +3234,8 @@ void AccessibilityRenderObject::ariaListboxSelectedChildren(AccessibilityChildre
{
bool isMulti = isMultiSelectable();
- AccessibilityChildrenVector childObjects = children();
- unsigned childrenSize = childObjects.size();
- for (unsigned k = 0; k < childrenSize; ++k) {
+ for (const auto& child : children()) {
// Every child should have aria-role option, and if so, check for selected attribute/state.
- AccessibilityObject* child = childObjects[k].get();
if (child->isSelected() && child->ariaRoleAttribute() == ListBoxOptionRole) {
result.append(child);
if (!isMulti)
@@ -3104,7 +3252,7 @@ void AccessibilityRenderObject::selectedChildren(AccessibilityChildrenVector& re
AccessibilityRole role = roleValue();
if (role == ListBoxRole) // native list boxes would be AccessibilityListBoxes, so only check for aria list boxes
ariaListboxSelectedChildren(result);
- else if (role == TreeRole || role == TreeGridRole || role == TableRole)
+ else if (role == TreeRole || role == TreeGridRole || role == TableRole || role == GridRole)
ariaSelectedRows(result);
}
@@ -3113,11 +3261,9 @@ void AccessibilityRenderObject::ariaListboxVisibleChildren(AccessibilityChildren
if (!hasChildren())
addChildren();
- AccessibilityObject::AccessibilityChildrenVector children = this->children();
- size_t size = children.size();
- for (size_t i = 0; i < size; i++) {
- if (!children[i]->isOffScreen())
- result.append(children[i]);
+ for (const auto& child : children()) {
+ if (child->isOffScreen())
+ result.append(child);
}
}
@@ -3137,25 +3283,23 @@ void AccessibilityRenderObject::tabChildren(AccessibilityChildrenVector& result)
{
ASSERT(roleValue() == TabListRole);
- AccessibilityObject::AccessibilityChildrenVector children = this->children();
- size_t size = children.size();
- for (size_t i = 0; i < size; ++i) {
- if (children[i]->isTabItem())
- result.append(children[i]);
+ for (const auto& child : children()) {
+ if (child->isTabItem())
+ result.append(child);
}
}
const String& AccessibilityRenderObject::actionVerb() const
{
+#if !PLATFORM(IOS)
// FIXME: Need to add verbs for select elements.
- DEFINE_STATIC_LOCAL(const String, buttonAction, (AXButtonActionVerb()));
- DEFINE_STATIC_LOCAL(const String, textFieldAction, (AXTextFieldActionVerb()));
- DEFINE_STATIC_LOCAL(const String, radioButtonAction, (AXRadioButtonActionVerb()));
- DEFINE_STATIC_LOCAL(const String, checkedCheckBoxAction, (AXCheckedCheckBoxActionVerb()));
- DEFINE_STATIC_LOCAL(const String, uncheckedCheckBoxAction, (AXUncheckedCheckBoxActionVerb()));
- DEFINE_STATIC_LOCAL(const String, linkAction, (AXLinkActionVerb()));
- DEFINE_STATIC_LOCAL(const String, noAction, ());
-
+ static NeverDestroyed<const String> buttonAction(AXButtonActionVerb());
+ static NeverDestroyed<const String> textFieldAction(AXTextFieldActionVerb());
+ static NeverDestroyed<const String> radioButtonAction(AXRadioButtonActionVerb());
+ static NeverDestroyed<const String> checkedCheckBoxAction(AXUncheckedCheckBoxActionVerb());
+ static NeverDestroyed<const String> uncheckedCheckBoxAction(AXUncheckedCheckBoxActionVerb());
+ static NeverDestroyed<const String> linkAction(AXLinkActionVerb());
+
switch (roleValue()) {
case ButtonRole:
case ToggleButtonRole:
@@ -3171,8 +3315,11 @@ const String& AccessibilityRenderObject::actionVerb() const
case WebCoreLinkRole:
return linkAction;
default:
- return noAction;
+ return nullAtom;
}
+#else
+ return nullAtom;
+#endif
}
void AccessibilityRenderObject::setAccessibleName(const AtomicString& name)
@@ -3181,15 +3328,15 @@ void AccessibilityRenderObject::setAccessibleName(const AtomicString& name)
if (!m_renderer)
return;
- Node* domNode = 0;
+ Node* node = nullptr;
// For web areas, set the aria-label on the HTML element.
if (isWebArea())
- domNode = m_renderer->document()->documentElement();
+ node = m_renderer->document().documentElement();
else
- domNode = m_renderer->node();
+ node = m_renderer->node();
- if (domNode && domNode->isElementNode())
- toElement(domNode)->setAttribute(aria_labelAttr, name);
+ if (is<Element>(node))
+ downcast<Element>(*node).setAttribute(aria_labelAttr, name);
}
static bool isLinkable(const AccessibilityRenderObject& object)
@@ -3206,8 +3353,8 @@ String AccessibilityRenderObject::stringValueForMSAA() const
{
if (isLinkable(*this)) {
Element* anchor = anchorElement();
- if (anchor && isHTMLAnchorElement(anchor))
- return toHTMLAnchorElement(anchor)->href();
+ if (is<HTMLAnchorElement>(anchor))
+ return downcast<HTMLAnchorElement>(*anchor).href();
}
return stringValue();
@@ -3219,10 +3366,10 @@ bool AccessibilityRenderObject::isLinked() const
return false;
Element* anchor = anchorElement();
- if (!anchor || !isHTMLAnchorElement(anchor))
+ if (!is<HTMLAnchorElement>(anchor))
return false;
- return !toHTMLAnchorElement(anchor)->href().isEmpty();
+ return !downcast<HTMLAnchorElement>(*anchor).href().isEmpty();
}
bool AccessibilityRenderObject::hasBoldFont() const
@@ -3230,7 +3377,7 @@ bool AccessibilityRenderObject::hasBoldFont() const
if (!m_renderer)
return false;
- return m_renderer->style()->fontDescription().weight() >= FontWeightBold;
+ return m_renderer->style().fontDescription().weight() >= FontWeightBold;
}
bool AccessibilityRenderObject::hasItalicFont() const
@@ -3238,7 +3385,7 @@ bool AccessibilityRenderObject::hasItalicFont() const
if (!m_renderer)
return false;
- return m_renderer->style()->fontDescription().italic() == FontItalicOn;
+ return m_renderer->style().fontDescription().italic() == FontItalicOn;
}
bool AccessibilityRenderObject::hasPlainText() const
@@ -3246,11 +3393,11 @@ bool AccessibilityRenderObject::hasPlainText() const
if (!m_renderer)
return false;
- RenderStyle* style = m_renderer->style();
+ const RenderStyle& style = m_renderer->style();
- return style->fontDescription().weight() == FontWeightNormal
- && style->fontDescription().italic() == FontItalicOff
- && style->textDecorationsInEffect() == TextDecorationNone;
+ return style.fontDescription().weight() == FontWeightNormal
+ && style.fontDescription().italic() == FontItalicOff
+ && style.textDecorationsInEffect() == TextDecorationNone;
}
bool AccessibilityRenderObject::hasSameFont(RenderObject* renderer) const
@@ -3258,7 +3405,7 @@ bool AccessibilityRenderObject::hasSameFont(RenderObject* renderer) const
if (!m_renderer || !renderer)
return false;
- return m_renderer->style()->fontDescription().families() == renderer->style()->fontDescription().families();
+ return m_renderer->style().fontDescription().families() == renderer->style().fontDescription().families();
}
bool AccessibilityRenderObject::hasSameFontColor(RenderObject* renderer) const
@@ -3266,7 +3413,7 @@ bool AccessibilityRenderObject::hasSameFontColor(RenderObject* renderer) const
if (!m_renderer || !renderer)
return false;
- return m_renderer->style()->visitedDependentColor(CSSPropertyColor) == renderer->style()->visitedDependentColor(CSSPropertyColor);
+ return m_renderer->style().visitedDependentColor(CSSPropertyColor) == renderer->style().visitedDependentColor(CSSPropertyColor);
}
bool AccessibilityRenderObject::hasSameStyle(RenderObject* renderer) const
@@ -3282,7 +3429,7 @@ bool AccessibilityRenderObject::hasUnderline() const
if (!m_renderer)
return false;
- return m_renderer->style()->textDecorationsInEffect() & TextDecorationUnderline;
+ return m_renderer->style().textDecorationsInEffect() & TextDecorationUnderline;
}
String AccessibilityRenderObject::nameForMSAA() const
@@ -3309,14 +3456,14 @@ String AccessibilityRenderObject::stringRoleForMSAA() const
return String();
Node* node = m_renderer->node();
- if (!node || !node->isElementNode())
+ if (!is<Element>(node))
return String();
- Element* element = toElement(node);
- if (!shouldReturnTagNameAsRoleForMSAA(*element))
+ Element& element = downcast<Element>(*node);
+ if (!shouldReturnTagNameAsRoleForMSAA(element))
return String();
- return element->tagName();
+ return element.tagName();
}
String AccessibilityRenderObject::positionalDescriptionForMSAA() const
@@ -3354,10 +3501,10 @@ static AccessibilityRole msaaRoleForRenderer(const RenderObject* renderer)
if (!renderer)
return UnknownRole;
- if (renderer->isText())
+ if (is<RenderText>(*renderer))
return EditableTextRole;
- if (renderer->isBoxModelObject() && toRenderBoxModelObject(renderer)->isListItem())
+ if (is<RenderListItem>(*renderer))
return ListItemRole;
return UnknownRole;
@@ -3378,161 +3525,125 @@ AccessibilityRole AccessibilityRenderObject::roleValueForMSAA() const
String AccessibilityRenderObject::passwordFieldValue() const
{
-#if PLATFORM(GTK)
ASSERT(isPasswordField());
// Look for the RenderText object in the RenderObject tree for this input field.
RenderObject* renderer = node()->renderer();
- while (renderer && !renderer->isText())
- renderer = renderer->firstChild();
+ while (renderer && !is<RenderText>(renderer))
+ renderer = downcast<RenderElement>(*renderer).firstChild();
- if (!renderer || !renderer->isText())
+ if (!is<RenderText>(renderer))
return String();
// Return the text that is actually being rendered in the input field.
- return static_cast<RenderText*>(renderer)->textWithoutTranscoding();
-#else
- // It seems only GTK is interested in this at the moment.
- return String();
-#endif
+ return downcast<RenderText>(*renderer).textWithoutConvertingBackslashToYenSymbol();
}
ScrollableArea* AccessibilityRenderObject::getScrollableAreaIfScrollable() const
{
// If the parent is a scroll view, then this object isn't really scrollable, the parent ScrollView should handle the scrolling.
if (parentObject() && parentObject()->isAccessibilityScrollView())
- return 0;
+ return nullptr;
- if (!m_renderer || !m_renderer->isBox())
- return 0;
+ if (!is<RenderBox>(m_renderer))
+ return nullptr;
- RenderBox* box = toRenderBox(m_renderer);
- if (!box->canBeScrolledAndHasScrollableArea())
- return 0;
+ auto& box = downcast<RenderBox>(*m_renderer);
+ if (!box.canBeScrolledAndHasScrollableArea())
+ return nullptr;
- return box->layer();
+ return box.layer();
}
void AccessibilityRenderObject::scrollTo(const IntPoint& point) const
{
- if (!m_renderer || !m_renderer->isBox())
+ if (!is<RenderBox>(m_renderer))
return;
- RenderBox* box = toRenderBox(m_renderer);
- if (!box->canBeScrolledAndHasScrollableArea())
+ auto& box = downcast<RenderBox>(*m_renderer);
+ if (!box.canBeScrolledAndHasScrollableArea())
return;
- RenderLayer* layer = box->layer();
- layer->scrollToOffset(toIntSize(point), RenderLayer::ScrollOffsetClamped);
+ // FIXME: is point a ScrollOffset or ScrollPosition? Test in RTL overflow.
+ box.layer()->scrollToOffset(point, RenderLayer::ScrollOffsetClamped);
}
#if ENABLE(MATHML)
bool AccessibilityRenderObject::isMathElement() const
{
- Node* node = this->node();
- if (!m_renderer || !node)
+ if (!m_renderer)
return false;
- return node->isElementNode() && toElement(node)->isMathMLElement();
+ return is<MathMLElement>(node());
}
bool AccessibilityRenderObject::isMathFraction() const
{
- if (!m_renderer || !m_renderer->isRenderMathMLBlock())
- return false;
-
- return toRenderMathMLBlock(m_renderer)->isRenderMathMLFraction();
+ return m_renderer && m_renderer->isRenderMathMLFraction();
}
bool AccessibilityRenderObject::isMathFenced() const
{
- if (!m_renderer || !m_renderer->isRenderMathMLBlock())
- return false;
-
- return toRenderMathMLBlock(m_renderer)->isRenderMathMLFenced();
+ return m_renderer && m_renderer->isRenderMathMLFenced();
}
bool AccessibilityRenderObject::isMathSubscriptSuperscript() const
{
- if (!m_renderer || !m_renderer->isRenderMathMLBlock())
- return false;
-
- return toRenderMathMLBlock(m_renderer)->isRenderMathMLSubSup();
+ return m_renderer && m_renderer->isRenderMathMLScripts() && !isMathMultiscript();
}
bool AccessibilityRenderObject::isMathRow() const
{
- if (!m_renderer || !m_renderer->isRenderMathMLBlock())
- return false;
-
- return toRenderMathMLBlock(m_renderer)->isRenderMathMLRow();
+ return m_renderer && m_renderer->isRenderMathMLRow();
}
bool AccessibilityRenderObject::isMathUnderOver() const
{
- if (!m_renderer || !m_renderer->isRenderMathMLBlock())
- return false;
-
- return toRenderMathMLBlock(m_renderer)->isRenderMathMLUnderOver();
+ return m_renderer && m_renderer->isRenderMathMLUnderOver();
}
bool AccessibilityRenderObject::isMathSquareRoot() const
{
- if (!m_renderer || !m_renderer->isRenderMathMLBlock())
- return false;
-
- return toRenderMathMLBlock(m_renderer)->isRenderMathMLSquareRoot();
+ return m_renderer && m_renderer->isRenderMathMLSquareRoot();
}
+bool AccessibilityRenderObject::isMathToken() const
+{
+ return m_renderer && m_renderer->isRenderMathMLToken();
+}
+
bool AccessibilityRenderObject::isMathRoot() const
{
- if (!m_renderer || !m_renderer->isRenderMathMLBlock())
- return false;
-
- return toRenderMathMLBlock(m_renderer)->isRenderMathMLRoot();
+ return m_renderer && m_renderer->isRenderMathMLRoot();
}
bool AccessibilityRenderObject::isMathOperator() const
{
- if (!m_renderer || !m_renderer->isRenderMathMLBlock())
- return false;
-
- // Ensure that this is actually a render MathML operator because
- // MathML will create MathMLBlocks and use the original node as the node
- // of this new block that is not tied to the DOM.
- if (!toRenderMathMLBlock(m_renderer)->isRenderMathMLOperator())
+ if (!m_renderer || !m_renderer->isRenderMathMLOperator())
return false;
-
- return isMathElement() && node()->hasTagName(MathMLNames::moTag);
+
+ return true;
}
bool AccessibilityRenderObject::isMathFenceOperator() const
{
- if (!m_renderer || !m_renderer->isRenderMathMLBlock())
+ if (!is<RenderMathMLOperator>(m_renderer))
return false;
-
- if (!toRenderMathMLBlock(m_renderer)->isRenderMathMLOperator())
- return false;
-
- RenderMathMLOperator* mathOperator = toRenderMathMLOperator(toRenderMathMLBlock(m_renderer));
- return mathOperator->operatorType() == RenderMathMLOperator::Fence;
+
+ return downcast<RenderMathMLOperator>(*m_renderer).hasOperatorFlag(MathMLOperatorDictionary::Fence);
}
bool AccessibilityRenderObject::isMathSeparatorOperator() const
{
- if (!m_renderer || !m_renderer->isRenderMathMLBlock())
- return false;
-
- if (!toRenderMathMLBlock(m_renderer)->isRenderMathMLOperator())
+ if (!is<RenderMathMLOperator>(m_renderer))
return false;
-
- RenderMathMLOperator* mathOperator = toRenderMathMLOperator(toRenderMathMLBlock(m_renderer));
- return mathOperator->operatorType() == RenderMathMLOperator::Separator;
+
+ return downcast<RenderMathMLOperator>(*m_renderer).hasOperatorFlag(MathMLOperatorDictionary::Separator);
}
bool AccessibilityRenderObject::isMathText() const
{
- return node() && node()->hasTagName(MathMLNames::mtextTag);
+ return node() && (node()->hasTagName(MathMLNames::mtextTag) || hasTagName(MathMLNames::msTag));
}
bool AccessibilityRenderObject::isMathNumber() const
@@ -3557,28 +3668,63 @@ bool AccessibilityRenderObject::isMathTable() const
bool AccessibilityRenderObject::isMathTableRow() const
{
- return node() && node()->hasTagName(MathMLNames::mtrTag);
+ return node() && (node()->hasTagName(MathMLNames::mtrTag) || hasTagName(MathMLNames::mlabeledtrTag));
}
bool AccessibilityRenderObject::isMathTableCell() const
{
return node() && node()->hasTagName(MathMLNames::mtdTag);
}
+
+bool AccessibilityRenderObject::isMathScriptObject(AccessibilityMathScriptObjectType type) const
+{
+ AccessibilityObject* parent = parentObjectUnignored();
+ if (!parent)
+ return false;
+
+ return type == Subscript ? this == parent->mathSubscriptObject() : this == parent->mathSuperscriptObject();
+}
+
+bool AccessibilityRenderObject::isMathMultiscriptObject(AccessibilityMathMultiscriptObjectType type) const
+{
+ AccessibilityObject* parent = parentObjectUnignored();
+ if (!parent || !parent->isMathMultiscript())
+ return false;
+
+ // The scripts in a MathML <mmultiscripts> element consist of one or more
+ // subscript, superscript pairs. In order to determine if this object is
+ // a scripted token, we need to examine each set of pairs to see if the
+ // this token is present and in the position corresponding with the type.
+
+ AccessibilityMathMultiscriptPairs pairs;
+ if (type == PreSubscript || type == PreSuperscript)
+ parent->mathPrescripts(pairs);
+ else
+ parent->mathPostscripts(pairs);
+
+ for (const auto& pair : pairs) {
+ if (this == pair.first)
+ return (type == PreSubscript || type == PostSubscript);
+ if (this == pair.second)
+ return (type == PreSuperscript || type == PostSuperscript);
+ }
+
+ return false;
+}
bool AccessibilityRenderObject::isIgnoredElementWithinMathTree() const
{
if (!m_renderer)
return true;
- // Ignore items that were created for layout purposes only.
- if (m_renderer->isRenderMathMLBlock() && toRenderMathMLBlock(m_renderer)->ignoreInAccessibilityTree())
- return true;
-
- // Ignore anonymous renderers inside math blocks.
+ // We ignore anonymous renderers inside math blocks.
+ // However, we do not exclude anonymous RenderMathMLOperator nodes created by the mfenced element nor RenderText nodes created by math operators so that the text can be exposed by AccessibilityRenderObject::textUnderElement.
if (m_renderer->isAnonymous()) {
+ if (m_renderer->isRenderMathMLOperator())
+ return false;
for (AccessibilityObject* parent = parentObject(); parent; parent = parent->parentObject()) {
if (parent->isMathElement())
- return true;
+ return !(m_renderer->isText() && ancestorsOfType<RenderMathMLOperator>(*m_renderer).first());
}
}
@@ -3599,38 +3745,37 @@ bool AccessibilityRenderObject::isIgnoredElementWithinMathTree() const
AccessibilityObject* AccessibilityRenderObject::mathRadicandObject()
{
if (!isMathRoot())
- return 0;
-
- AccessibilityObject::AccessibilityChildrenVector children = this->children();
- if (children.size() < 1)
- return 0;
-
- // The radicand is the value being rooted and must be listed first.
- return children[0].get();
+ return nullptr;
+ RenderMathMLRoot* root = &downcast<RenderMathMLRoot>(*m_renderer);
+ AccessibilityObject* rootRadicandWrapper = axObjectCache()->getOrCreate(root->baseWrapper());
+ if (!rootRadicandWrapper)
+ return nullptr;
+ AccessibilityObject* rootRadicand = rootRadicandWrapper->children().first().get();
+ ASSERT(rootRadicand && children().contains(rootRadicand));
+ return rootRadicand;
}
AccessibilityObject* AccessibilityRenderObject::mathRootIndexObject()
{
if (!isMathRoot())
- return 0;
-
- AccessibilityObject::AccessibilityChildrenVector children = this->children();
- if (children.size() != 2)
- return 0;
-
- // The index in a root is the value which determines if it's a square, cube, etc, root
- // and must be listed second.
- return children[1].get();
+ return nullptr;
+ RenderMathMLRoot* root = &downcast<RenderMathMLRoot>(*m_renderer);
+ AccessibilityObject* rootIndexWrapper = axObjectCache()->getOrCreate(root->indexWrapper());
+ if (!rootIndexWrapper)
+ return nullptr;
+ AccessibilityObject* rootIndex = rootIndexWrapper->children().first().get();
+ ASSERT(rootIndex && children().contains(rootIndex));
+ return rootIndex;
}
AccessibilityObject* AccessibilityRenderObject::mathNumeratorObject()
{
if (!isMathFraction())
- return 0;
+ return nullptr;
- AccessibilityObject::AccessibilityChildrenVector children = this->children();
+ const auto& children = this->children();
if (children.size() != 2)
- return 0;
+ return nullptr;
return children[0].get();
}
@@ -3638,11 +3783,11 @@ AccessibilityObject* AccessibilityRenderObject::mathNumeratorObject()
AccessibilityObject* AccessibilityRenderObject::mathDenominatorObject()
{
if (!isMathFraction())
- return 0;
+ return nullptr;
- AccessibilityObject::AccessibilityChildrenVector children = this->children();
+ const auto& children = this->children();
if (children.size() != 2)
- return 0;
+ return nullptr;
return children[1].get();
}
@@ -3650,78 +3795,78 @@ AccessibilityObject* AccessibilityRenderObject::mathDenominatorObject()
AccessibilityObject* AccessibilityRenderObject::mathUnderObject()
{
if (!isMathUnderOver() || !node())
- return 0;
+ return nullptr;
- AccessibilityChildrenVector children = this->children();
+ const auto& children = this->children();
if (children.size() < 2)
- return 0;
+ return nullptr;
if (node()->hasTagName(MathMLNames::munderTag) || node()->hasTagName(MathMLNames::munderoverTag))
return children[1].get();
- return 0;
+ return nullptr;
}
AccessibilityObject* AccessibilityRenderObject::mathOverObject()
{
if (!isMathUnderOver() || !node())
- return 0;
+ return nullptr;
- AccessibilityChildrenVector children = this->children();
+ const auto& children = this->children();
if (children.size() < 2)
- return 0;
+ return nullptr;
if (node()->hasTagName(MathMLNames::moverTag))
return children[1].get();
if (node()->hasTagName(MathMLNames::munderoverTag))
return children[2].get();
- return 0;
+ return nullptr;
}
AccessibilityObject* AccessibilityRenderObject::mathBaseObject()
{
if (!isMathSubscriptSuperscript() && !isMathUnderOver() && !isMathMultiscript())
- return 0;
+ return nullptr;
- AccessibilityChildrenVector children = this->children();
+ const auto& children = this->children();
// The base object in question is always the first child.
if (children.size() > 0)
return children[0].get();
- return 0;
+ return nullptr;
}
AccessibilityObject* AccessibilityRenderObject::mathSubscriptObject()
{
if (!isMathSubscriptSuperscript() || !node())
- return 0;
+ return nullptr;
- AccessibilityChildrenVector children = this->children();
+ const auto& children = this->children();
if (children.size() < 2)
- return 0;
+ return nullptr;
if (node()->hasTagName(MathMLNames::msubTag) || node()->hasTagName(MathMLNames::msubsupTag))
return children[1].get();
- return 0;
+ return nullptr;
}
AccessibilityObject* AccessibilityRenderObject::mathSuperscriptObject()
{
if (!isMathSubscriptSuperscript() || !node())
- return 0;
-
- AccessibilityChildrenVector children = this->children();
- if (children.size() < 2)
- return 0;
+ return nullptr;
- if (node()->hasTagName(MathMLNames::msupTag))
+ const auto& children = this->children();
+ unsigned count = children.size();
+
+ if (count >= 2 && node()->hasTagName(MathMLNames::msupTag))
return children[1].get();
- if (node()->hasTagName(MathMLNames::msubsupTag))
+
+ if (count >= 3 && node()->hasTagName(MathMLNames::msubsupTag))
return children[2].get();
- return 0;
+ return nullptr;
}
String AccessibilityRenderObject::mathFencedOpenString() const
@@ -3746,7 +3891,7 @@ void AccessibilityRenderObject::mathPrescripts(AccessibilityMathMultiscriptPairs
return;
bool foundPrescript = false;
- pair<AccessibilityObject*, AccessibilityObject*> prescriptPair;
+ std::pair<AccessibilityObject*, AccessibilityObject*> prescriptPair;
for (Node* child = node()->firstChild(); child; child = child->nextSibling()) {
if (foundPrescript) {
AccessibilityObject* axChild = axObjectCache()->getOrCreate(child);
@@ -3756,8 +3901,8 @@ void AccessibilityRenderObject::mathPrescripts(AccessibilityMathMultiscriptPairs
else {
prescriptPair.second = axChild;
prescripts.append(prescriptPair);
- prescriptPair.first = 0;
- prescriptPair.second = 0;
+ prescriptPair.first = nullptr;
+ prescriptPair.second = nullptr;
}
}
} else if (child->hasTagName(MathMLNames::mprescriptsTag))
@@ -3776,7 +3921,7 @@ void AccessibilityRenderObject::mathPostscripts(AccessibilityMathMultiscriptPair
// In Multiscripts, the post-script elements start after the first element (which is the base)
// and continue until a <mprescripts> tag is found
- pair<AccessibilityObject*, AccessibilityObject*> postscriptPair;
+ std::pair<AccessibilityObject*, AccessibilityObject*> postscriptPair;
bool foundBaseElement = false;
for (Node* child = node()->firstChild(); child; child = child->nextSibling()) {
if (child->hasTagName(MathMLNames::mprescriptsTag))
@@ -3791,8 +3936,8 @@ void AccessibilityRenderObject::mathPostscripts(AccessibilityMathMultiscriptPair
else {
postscriptPair.second = axChild;
postscripts.append(postscriptPair);
- postscriptPair.first = 0;
- postscriptPair.second = 0;
+ postscriptPair.first = nullptr;
+ postscriptPair.second = nullptr;
}
}
}
@@ -3804,10 +3949,10 @@ void AccessibilityRenderObject::mathPostscripts(AccessibilityMathMultiscriptPair
int AccessibilityRenderObject::mathLineThickness() const
{
- if (!isMathFraction())
+ if (!is<RenderMathMLFraction>(m_renderer))
return -1;
- return toRenderMathMLFraction(m_renderer)->lineThickness();
+ return downcast<RenderMathMLFraction>(*m_renderer).lineThickness();
}
#endif
diff --git a/Source/WebCore/accessibility/AccessibilityRenderObject.h b/Source/WebCore/accessibility/AccessibilityRenderObject.h
index c4512f492..709245d48 100644
--- a/Source/WebCore/accessibility/AccessibilityRenderObject.h
+++ b/Source/WebCore/accessibility/AccessibilityRenderObject.h
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -57,185 +57,183 @@ class VisibleSelection;
class Widget;
class AccessibilityRenderObject : public AccessibilityNodeObject {
-protected:
- explicit AccessibilityRenderObject(RenderObject*);
public:
- static PassRefPtr<AccessibilityRenderObject> create(RenderObject*);
+ static Ref<AccessibilityRenderObject> create(RenderObject*);
virtual ~AccessibilityRenderObject();
-
- virtual bool isAccessibilityRenderObject() const { return true; }
- virtual void init();
+ virtual void init() override;
- virtual bool isAttachment() const;
- virtual bool isFileUploadButton() const;
+ virtual bool isAttachment() const override;
+ virtual bool isFileUploadButton() const override;
- virtual bool isSelected() const;
- virtual bool isFocused() const;
- virtual bool isLoaded() const;
- virtual bool isOffScreen() const;
- virtual bool isReadOnly() const;
- virtual bool isUnvisited() const;
- virtual bool isVisited() const;
- virtual bool isLinked() const;
- virtual bool hasBoldFont() const;
- virtual bool hasItalicFont() const;
- virtual bool hasPlainText() const;
- virtual bool hasSameFont(RenderObject*) const;
- virtual bool hasSameFontColor(RenderObject*) const;
- virtual bool hasSameStyle(RenderObject*) const;
- virtual bool hasUnderline() const;
+ virtual bool isSelected() const override;
+ virtual bool isFocused() const override;
+ virtual bool isLoaded() const override;
+ virtual bool isOffScreen() const override;
+ virtual bool isUnvisited() const override;
+ virtual bool isVisited() const override;
+ virtual bool isLinked() const override;
+ virtual bool hasBoldFont() const override;
+ virtual bool hasItalicFont() const override;
+ virtual bool hasPlainText() const override;
+ virtual bool hasSameFont(RenderObject*) const override;
+ virtual bool hasSameFontColor(RenderObject*) const override;
+ virtual bool hasSameStyle(RenderObject*) const override;
+ virtual bool hasUnderline() const override;
- virtual bool canSetTextRangeAttributes() const;
- virtual bool canSetValueAttribute() const;
- virtual bool canSetExpandedAttribute() const;
+ virtual bool canSetTextRangeAttributes() const override;
+ virtual bool canSetExpandedAttribute() const override;
- virtual void setAccessibleName(const AtomicString&);
+ virtual void setAccessibleName(const AtomicString&) override;
// Provides common logic used by all elements when determining isIgnored.
- virtual AccessibilityObjectInclusion defaultObjectInclusion() const;
+ virtual AccessibilityObjectInclusion defaultObjectInclusion() const override;
- virtual int layoutCount() const;
- virtual double estimatedLoadingProgress() const;
+ virtual int layoutCount() const override;
+ virtual double estimatedLoadingProgress() const override;
- virtual AccessibilityObject* firstChild() const;
- virtual AccessibilityObject* lastChild() const;
- virtual AccessibilityObject* previousSibling() const;
- virtual AccessibilityObject* nextSibling() const;
- virtual AccessibilityObject* parentObject() const;
- virtual AccessibilityObject* parentObjectIfExists() const;
- virtual AccessibilityObject* observableObject() const;
- virtual void linkedUIElements(AccessibilityChildrenVector&) const;
- virtual bool exposesTitleUIElement() const;
- virtual AccessibilityObject* titleUIElement() const;
- virtual AccessibilityObject* correspondingControlForLabelElement() const;
- virtual AccessibilityObject* correspondingLabelForControlElement() const;
+ virtual AccessibilityObject* firstChild() const override;
+ virtual AccessibilityObject* lastChild() const override;
+ virtual AccessibilityObject* previousSibling() const override;
+ virtual AccessibilityObject* nextSibling() const override;
+ virtual AccessibilityObject* parentObject() const override;
+ virtual AccessibilityObject* parentObjectIfExists() const override;
+ virtual AccessibilityObject* observableObject() const override;
+ virtual void linkedUIElements(AccessibilityChildrenVector&) const override;
+ virtual bool exposesTitleUIElement() const override;
+ virtual AccessibilityObject* titleUIElement() const override;
+ virtual AccessibilityObject* correspondingControlForLabelElement() const override;
+ virtual AccessibilityObject* correspondingLabelForControlElement() const override;
- virtual void ariaOwnsElements(AccessibilityChildrenVector&) const;
- virtual bool supportsARIAOwns() const;
- virtual bool isPresentationalChildOfAriaRole() const;
- virtual bool ariaRoleHasPresentationalChildren() const;
+ virtual void ariaOwnsElements(AccessibilityChildrenVector&) const override;
+ virtual bool supportsARIAOwns() const override;
+ virtual bool isPresentationalChildOfAriaRole() const override;
+ virtual bool ariaRoleHasPresentationalChildren() const override;
// Should be called on the root accessibility object to kick off a hit test.
- virtual AccessibilityObject* accessibilityHitTest(const IntPoint&) const;
+ virtual AccessibilityObject* accessibilityHitTest(const IntPoint&) const override;
- FrameView* frameViewIfRenderView() const;
- virtual Element* anchorElement() const;
+ virtual Element* anchorElement() const override;
- virtual LayoutRect boundingBoxRect() const;
- virtual LayoutRect elementRect() const;
- virtual IntPoint clickPoint();
+ virtual LayoutRect boundingBoxRect() const override;
+ virtual LayoutRect elementRect() const override;
+ virtual IntPoint clickPoint() override;
void setRenderer(RenderObject*);
- virtual RenderObject* renderer() const { return m_renderer; }
+ virtual RenderObject* renderer() const override { return m_renderer; }
RenderBoxModelObject* renderBoxModelObject() const;
- virtual Node* node() const;
+ virtual Node* node() const override;
- virtual Document* document() const;
+ virtual Document* document() const override;
RenderView* topRenderer() const;
RenderTextControl* textControl() const;
- FrameView* topDocumentFrameView() const;
- Document* topDocument() const;
HTMLLabelElement* labelElementContainer() const;
- virtual KURL url() const;
- virtual PlainTextRange selectedTextRange() const;
- virtual VisibleSelection selection() const;
- virtual String stringValue() const;
- virtual String helpText() const;
- virtual String textUnderElement(AccessibilityTextUnderElementMode = TextUnderElementModeSkipIgnoredChildren) const;
- virtual String text() const;
- virtual int textLength() const;
- virtual String selectedText() const;
- virtual const AtomicString& accessKey() const;
+ virtual URL url() const override;
+ virtual PlainTextRange selectedTextRange() const override;
+ virtual VisibleSelection selection() const override;
+ virtual String stringValue() const override;
+ virtual String helpText() const override;
+ virtual String textUnderElement(AccessibilityTextUnderElementMode = AccessibilityTextUnderElementMode()) const override;
+ virtual String text() const override;
+ virtual int textLength() const override;
+ virtual String selectedText() const override;
+ virtual const AtomicString& accessKey() const override;
virtual const String& actionVerb() const;
- virtual Widget* widget() const;
- virtual Widget* widgetForAttachmentView() const;
+ virtual Widget* widget() const override;
+ virtual Widget* widgetForAttachmentView() const override;
virtual void getDocumentLinks(AccessibilityChildrenVector&);
- virtual FrameView* documentFrameView() const;
+ virtual FrameView* documentFrameView() const override;
- virtual void clearChildren();
- virtual void updateChildrenIfNecessary();
+ virtual void clearChildren() override;
+ virtual void updateChildrenIfNecessary() override;
- virtual void setFocused(bool);
- virtual void setSelectedTextRange(const PlainTextRange&);
- virtual void setValue(const String&);
- virtual void setSelectedRows(AccessibilityChildrenVector&);
- virtual AccessibilityOrientation orientation() const;
+ virtual void setFocused(bool) override;
+ virtual void setSelectedTextRange(const PlainTextRange&) override;
+ virtual void setValue(const String&) override;
+ virtual void setSelectedRows(AccessibilityChildrenVector&) override;
+ virtual AccessibilityOrientation orientation() const override;
- virtual void detach();
- virtual void textChanged();
- virtual void addChildren();
- virtual bool canHaveChildren() const;
- virtual void selectedChildren(AccessibilityChildrenVector&);
- virtual void visibleChildren(AccessibilityChildrenVector&);
- virtual void tabChildren(AccessibilityChildrenVector&);
- virtual bool shouldFocusActiveDescendant() const;
+ virtual void detach(AccessibilityDetachmentType, AXObjectCache*) override;
+ virtual void textChanged() override;
+ virtual void addChildren() override;
+ virtual bool canHaveChildren() const override;
+ virtual void selectedChildren(AccessibilityChildrenVector&) override;
+ virtual void visibleChildren(AccessibilityChildrenVector&) override;
+ virtual void tabChildren(AccessibilityChildrenVector&) override;
+ virtual bool shouldFocusActiveDescendant() const override;
bool shouldNotifyActiveDescendant() const;
- virtual AccessibilityObject* activeDescendant() const;
- virtual void handleActiveDescendantChanged();
- virtual void handleAriaExpandedChanged();
+ virtual AccessibilityObject* activeDescendant() const override;
+ virtual void handleActiveDescendantChanged() override;
+ virtual void handleAriaExpandedChanged() override;
- virtual VisiblePositionRange visiblePositionRange() const;
- virtual VisiblePositionRange visiblePositionRangeForLine(unsigned) const;
- virtual IntRect boundsForVisiblePositionRange(const VisiblePositionRange&) const;
- virtual void setSelectedVisiblePositionRange(const VisiblePositionRange&) const;
- virtual bool supportsARIAFlowTo() const;
- virtual void ariaFlowToElements(AccessibilityChildrenVector&) const;
- virtual bool ariaHasPopup() const;
+ virtual VisiblePositionRange visiblePositionRange() const override;
+ virtual VisiblePositionRange visiblePositionRangeForLine(unsigned) const override;
+ virtual IntRect boundsForVisiblePositionRange(const VisiblePositionRange&) const override;
+ virtual void setSelectedVisiblePositionRange(const VisiblePositionRange&) const override;
+ virtual bool supportsARIAFlowTo() const override;
+ virtual void ariaFlowToElements(AccessibilityChildrenVector&) const override;
+ virtual bool supportsARIADescribedBy() const override;
+ virtual void ariaDescribedByElements(AccessibilityChildrenVector&) const override;
+ virtual bool supportsARIAControls() const override;
+ virtual void ariaControlsElements(AccessibilityChildrenVector&) const override;
+ virtual bool ariaHasPopup() const override;
- virtual bool supportsARIADropping() const;
- virtual bool supportsARIADragging() const;
- virtual bool isARIAGrabbed();
- virtual void determineARIADropEffects(Vector<String>&);
+ virtual bool supportsARIADropping() const override;
+ virtual bool supportsARIADragging() const override;
+ virtual bool isARIAGrabbed() override;
+ virtual void determineARIADropEffects(Vector<String>&) override;
- virtual VisiblePosition visiblePositionForPoint(const IntPoint&) const;
- virtual VisiblePosition visiblePositionForIndex(unsigned indexValue, bool lastIndexOK) const;
- virtual int index(const VisiblePosition&) const;
+ virtual VisiblePosition visiblePositionForPoint(const IntPoint&) const override;
+ virtual VisiblePosition visiblePositionForIndex(unsigned indexValue, bool lastIndexOK) const override;
+ virtual int index(const VisiblePosition&) const override;
- virtual VisiblePosition visiblePositionForIndex(int) const;
- virtual int indexForVisiblePosition(const VisiblePosition&) const;
+ virtual VisiblePosition visiblePositionForIndex(int) const override;
+ virtual int indexForVisiblePosition(const VisiblePosition&) const override;
- virtual void lineBreaks(Vector<int>&) const;
- virtual PlainTextRange doAXRangeForLine(unsigned) const;
- virtual PlainTextRange doAXRangeForIndex(unsigned) const;
+ virtual void lineBreaks(Vector<int>&) const override;
+ virtual PlainTextRange doAXRangeForLine(unsigned) const override;
+ virtual PlainTextRange doAXRangeForIndex(unsigned) const override;
- virtual String doAXStringForRange(const PlainTextRange&) const;
- virtual IntRect doAXBoundsForRange(const PlainTextRange&) const;
+ virtual String doAXStringForRange(const PlainTextRange&) const override;
+ virtual IntRect doAXBoundsForRange(const PlainTextRange&) const override;
- virtual String stringValueForMSAA() const;
- virtual String stringRoleForMSAA() const;
- virtual String nameForMSAA() const;
- virtual String descriptionForMSAA() const;
- virtual AccessibilityRole roleValueForMSAA() const;
+ virtual String stringValueForMSAA() const override;
+ virtual String stringRoleForMSAA() const override;
+ virtual String nameForMSAA() const override;
+ virtual String descriptionForMSAA() const override;
+ virtual AccessibilityRole roleValueForMSAA() const override;
- virtual String passwordFieldValue() const;
+ virtual String passwordFieldValue() const override;
protected:
- RenderObject* m_renderer;
-
+ explicit AccessibilityRenderObject(RenderObject*);
void setRenderObject(RenderObject* renderer) { m_renderer = renderer; }
+ void ariaElementsFromAttribute(AccessibilityChildrenVector&, const QualifiedName&) const;
bool needsToUpdateChildren() const { return m_childrenDirty; }
- ScrollableArea* getScrollableAreaIfScrollable() const;
- void scrollTo(const IntPoint&) const;
+ virtual ScrollableArea* getScrollableAreaIfScrollable() const override;
+ virtual void scrollTo(const IntPoint&) const override;
- virtual bool isDetached() const { return !m_renderer; }
+ virtual bool isDetached() const override { return !m_renderer; }
- virtual AccessibilityRole determineAccessibilityRole();
- virtual bool computeAccessibilityIsIgnored() const;
+ virtual AccessibilityRole determineAccessibilityRole() override;
+ virtual bool computeAccessibilityIsIgnored() const override;
+
+ RenderObject* m_renderer;
private:
+ virtual bool isAccessibilityRenderObject() const override final { return true; }
void ariaListboxSelectedChildren(AccessibilityChildrenVector&);
void ariaListboxVisibleChildren(AccessibilityChildrenVector&);
bool isAllowedChildOfTree() const;
bool hasTextAlternative() const;
String positionalDescriptionForMSAA() const;
- PlainTextRange ariaSelectedTextRange() const;
+ PlainTextRange documentBasedSelectedTextRange() const;
Element* rootEditableElementForPosition(const Position&) const;
bool nodeIsTextControl(const Node*) const;
- virtual void setNeedsToUpdateChildren() { m_childrenDirty = true; }
- virtual Path elementPath() const;
+ virtual void setNeedsToUpdateChildren() override { m_childrenDirty = true; }
+ virtual Path elementPath() const override;
bool isTabItemSelected() const;
LayoutRect checkboxOrRadioRect() const;
@@ -243,9 +241,9 @@ private:
AccessibilityObject* internalLinkElement() const;
AccessibilityObject* accessibilityImageMapHitTest(HTMLAreaElement*, const IntPoint&) const;
AccessibilityObject* accessibilityParentForImageMap(HTMLMapElement*) const;
- virtual AccessibilityObject* elementAccessibilityHitTest(const IntPoint&) const;
+ virtual AccessibilityObject* elementAccessibilityHitTest(const IntPoint&) const override;
- bool renderObjectIsObservable(RenderObject*) const;
+ bool renderObjectIsObservable(RenderObject&) const;
RenderObject* renderParentObject() const;
bool isDescendantOfElementType(const QualifiedName& tagName) const;
@@ -254,7 +252,7 @@ private:
AccessibilitySVGRoot* remoteSVGRootElement() const;
AccessibilityObject* remoteSVGElementHitTest(const IntPoint&) const;
void offsetBoundingBoxForRemoteSVGElement(LayoutRect&) const;
- virtual bool supportsPath() const;
+ virtual bool supportsPath() const override;
void addHiddenChildren();
void addTextFieldChildren();
@@ -262,92 +260,87 @@ private:
void addCanvasChildren();
void addAttachmentChildren();
void addRemoteSVGChildren();
-#if PLATFORM(MAC)
+#if PLATFORM(COCOA)
void updateAttachmentViewParents();
#endif
-
+ virtual String expandedTextValue() const override;
+ virtual bool supportsExpandedTextValue() const override;
+ void updateRoleAfterChildrenCreation();
+
void ariaSelectedRows(AccessibilityChildrenVector&);
bool elementAttributeValue(const QualifiedName&) const;
void setElementAttributeValue(const QualifiedName&, bool);
- virtual ESpeak speakProperty() const;
+ virtual ESpeak speakProperty() const override;
- virtual const AtomicString& ariaLiveRegionStatus() const;
- virtual const AtomicString& ariaLiveRegionRelevant() const;
- virtual bool ariaLiveRegionAtomic() const;
- virtual bool ariaLiveRegionBusy() const;
+ virtual const String ariaLiveRegionStatus() const override;
+ virtual const AtomicString& ariaLiveRegionRelevant() const override;
+ virtual bool ariaLiveRegionAtomic() const override;
+ virtual bool ariaLiveRegionBusy() const override;
- bool inheritsPresentationalRole() const;
+ virtual bool inheritsPresentationalRole() const override;
+
+ bool shouldGetTextFromNode(AccessibilityTextUnderElementMode) const;
#if ENABLE(MATHML)
// All math elements return true for isMathElement().
- virtual bool isMathElement() const;
- virtual bool isMathFraction() const;
- virtual bool isMathFenced() const;
- virtual bool isMathSubscriptSuperscript() const;
- virtual bool isMathRow() const;
- virtual bool isMathUnderOver() const;
- virtual bool isMathRoot() const;
- virtual bool isMathSquareRoot() const;
- virtual bool isMathText() const;
- virtual bool isMathNumber() const;
- virtual bool isMathOperator() const;
- virtual bool isMathFenceOperator() const;
- virtual bool isMathSeparatorOperator() const;
- virtual bool isMathIdentifier() const;
- virtual bool isMathTable() const;
- virtual bool isMathTableRow() const;
- virtual bool isMathTableCell() const;
- virtual bool isMathMultiscript() const;
+ virtual bool isMathElement() const override;
+ virtual bool isMathFraction() const override;
+ virtual bool isMathFenced() const override;
+ virtual bool isMathSubscriptSuperscript() const override;
+ virtual bool isMathRow() const override;
+ virtual bool isMathUnderOver() const override;
+ virtual bool isMathRoot() const override;
+ virtual bool isMathSquareRoot() const override;
+ virtual bool isMathText() const override;
+ virtual bool isMathNumber() const override;
+ virtual bool isMathOperator() const override;
+ virtual bool isMathFenceOperator() const override;
+ virtual bool isMathSeparatorOperator() const override;
+ virtual bool isMathIdentifier() const override;
+ virtual bool isMathTable() const override;
+ virtual bool isMathTableRow() const override;
+ virtual bool isMathTableCell() const override;
+ virtual bool isMathMultiscript() const override;
+ virtual bool isMathToken() const override;
+ virtual bool isMathScriptObject(AccessibilityMathScriptObjectType) const override;
+ virtual bool isMathMultiscriptObject(AccessibilityMathMultiscriptObjectType) const override;
// Generic components.
- virtual AccessibilityObject* mathBaseObject();
+ virtual AccessibilityObject* mathBaseObject() override;
// Root components.
- virtual AccessibilityObject* mathRadicandObject();
- virtual AccessibilityObject* mathRootIndexObject();
+ virtual AccessibilityObject* mathRadicandObject() override;
+ virtual AccessibilityObject* mathRootIndexObject() override;
// Fraction components.
- virtual AccessibilityObject* mathNumeratorObject();
- virtual AccessibilityObject* mathDenominatorObject();
+ virtual AccessibilityObject* mathNumeratorObject() override;
+ virtual AccessibilityObject* mathDenominatorObject() override;
// Under over components.
- virtual AccessibilityObject* mathUnderObject();
- virtual AccessibilityObject* mathOverObject();
+ virtual AccessibilityObject* mathUnderObject() override;
+ virtual AccessibilityObject* mathOverObject() override;
// Subscript/superscript components.
- virtual AccessibilityObject* mathSubscriptObject();
- virtual AccessibilityObject* mathSuperscriptObject();
+ virtual AccessibilityObject* mathSubscriptObject() override;
+ virtual AccessibilityObject* mathSuperscriptObject() override;
// Fenced components.
- virtual String mathFencedOpenString() const;
- virtual String mathFencedCloseString() const;
- virtual int mathLineThickness() const;
+ virtual String mathFencedOpenString() const override;
+ virtual String mathFencedCloseString() const override;
+ virtual int mathLineThickness() const override;
// Multiscripts components.
- virtual void mathPrescripts(AccessibilityMathMultiscriptPairs&);
- virtual void mathPostscripts(AccessibilityMathMultiscriptPairs&);
+ virtual void mathPrescripts(AccessibilityMathMultiscriptPairs&) override;
+ virtual void mathPostscripts(AccessibilityMathMultiscriptPairs&) override;
bool isIgnoredElementWithinMathTree() const;
#endif
};
-inline AccessibilityRenderObject* toAccessibilityRenderObject(AccessibilityObject* object)
-{
- ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isAccessibilityRenderObject());
- return static_cast<AccessibilityRenderObject*>(object);
-}
-
-inline const AccessibilityRenderObject* toAccessibilityRenderObject(const AccessibilityObject* object)
-{
- ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isAccessibilityRenderObject());
- return static_cast<const AccessibilityRenderObject*>(object);
-}
-
-// This will catch anyone doing an unnecessary cast.
-void toAccessibilityRenderObject(const AccessibilityRenderObject*);
-
} // namespace WebCore
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilityRenderObject, isAccessibilityRenderObject())
+
#endif // AccessibilityRenderObject_h
diff --git a/Source/WebCore/accessibility/AccessibilitySVGRoot.cpp b/Source/WebCore/accessibility/AccessibilitySVGRoot.cpp
index 08a2d589d..2b62d92f2 100644
--- a/Source/WebCore/accessibility/AccessibilitySVGRoot.cpp
+++ b/Source/WebCore/accessibility/AccessibilitySVGRoot.cpp
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -35,7 +35,7 @@ namespace WebCore {
AccessibilitySVGRoot::AccessibilitySVGRoot(RenderObject* renderer)
: AccessibilityRenderObject(renderer)
- , m_parent(0)
+ , m_parent(nullptr)
{
}
@@ -43,9 +43,9 @@ AccessibilitySVGRoot::~AccessibilitySVGRoot()
{
}
-PassRefPtr<AccessibilitySVGRoot> AccessibilitySVGRoot::create(RenderObject* renderer)
+Ref<AccessibilitySVGRoot> AccessibilitySVGRoot::create(RenderObject* renderer)
{
- return adoptRef(new AccessibilitySVGRoot(renderer));
+ return adoptRef(*new AccessibilitySVGRoot(renderer));
}
AccessibilityObject* AccessibilitySVGRoot::parentObject() const
diff --git a/Source/WebCore/accessibility/AccessibilitySVGRoot.h b/Source/WebCore/accessibility/AccessibilitySVGRoot.h
index 519229db3..44eedc591 100644
--- a/Source/WebCore/accessibility/AccessibilitySVGRoot.h
+++ b/Source/WebCore/accessibility/AccessibilitySVGRoot.h
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -33,29 +33,24 @@
namespace WebCore {
-class AccessibilitySVGRoot : public AccessibilityRenderObject {
-
-protected:
- explicit AccessibilitySVGRoot(RenderObject*);
+class AccessibilitySVGRoot final : public AccessibilityRenderObject {
public:
- static PassRefPtr<AccessibilitySVGRoot> create(RenderObject*);
+ static Ref<AccessibilitySVGRoot> create(RenderObject*);
virtual ~AccessibilitySVGRoot();
void setParent(AccessibilityObject* parent) { m_parent = parent; }
-
+
private:
- AccessibilityObject* m_parent;
+ explicit AccessibilitySVGRoot(RenderObject*);
- virtual AccessibilityObject* parentObject() const;
- virtual bool isAccessibilitySVGRoot() const { return true; }
-};
+ virtual AccessibilityObject* parentObject() const override;
+ virtual bool isAccessibilitySVGRoot() const override { return true; }
-inline AccessibilitySVGRoot* toAccessibilitySVGRoot(AccessibilityObject* object)
-{
- ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isAccessibilitySVGRoot());
- return static_cast<AccessibilitySVGRoot*>(object);
-}
+ AccessibilityObject* m_parent;
+};
} // namespace WebCore
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilitySVGRoot, isAccessibilitySVGRoot())
+
#endif // AccessibilitySVGRoot_h
diff --git a/Source/WebCore/accessibility/AccessibilityScrollView.cpp b/Source/WebCore/accessibility/AccessibilityScrollView.cpp
index 26ff62fc0..ed5aa5844 100644
--- a/Source/WebCore/accessibility/AccessibilityScrollView.cpp
+++ b/Source/WebCore/accessibility/AccessibilityScrollView.cpp
@@ -31,7 +31,7 @@
#include "Frame.h"
#include "FrameView.h"
#include "HTMLFrameOwnerElement.h"
-#include "RenderPart.h"
+#include "RenderElement.h"
#include "ScrollView.h"
#include "Widget.h"
@@ -48,15 +48,15 @@ AccessibilityScrollView::~AccessibilityScrollView()
ASSERT(isDetached());
}
-void AccessibilityScrollView::detach()
+void AccessibilityScrollView::detach(AccessibilityDetachmentType detachmentType, AXObjectCache* cache)
{
- AccessibilityObject::detach();
- m_scrollView = 0;
+ AccessibilityObject::detach(detachmentType, cache);
+ m_scrollView = nullptr;
}
-PassRefPtr<AccessibilityScrollView> AccessibilityScrollView::create(ScrollView* view)
+Ref<AccessibilityScrollView> AccessibilityScrollView::create(ScrollView* view)
{
- return adoptRef(new AccessibilityScrollView(view));
+ return adoptRef(*new AccessibilityScrollView(view));
}
AccessibilityObject* AccessibilityScrollView::scrollBar(AccessibilityOrientation orientation)
@@ -64,13 +64,15 @@ AccessibilityObject* AccessibilityScrollView::scrollBar(AccessibilityOrientation
updateScrollbars();
switch (orientation) {
+ // ARIA 1.1 Elements with the role scrollbar have an implicit aria-orientation value of vertical.
+ case AccessibilityOrientationUndefined:
case AccessibilityOrientationVertical:
- return m_verticalScrollbar ? m_verticalScrollbar.get() : 0;
+ return m_verticalScrollbar ? m_verticalScrollbar.get() : nullptr;
case AccessibilityOrientationHorizontal:
- return m_horizontalScrollbar ? m_horizontalScrollbar.get() : 0;
+ return m_horizontalScrollbar ? m_horizontalScrollbar.get() : nullptr;
}
- return 0;
+ return nullptr;
}
// If this is WebKit1 then the native scroll view needs to return the
@@ -86,15 +88,31 @@ Widget* AccessibilityScrollView::widgetForAttachmentView() const
return m_scrollView;
}
-void AccessibilityScrollView::updateChildrenIfNecessary()
+bool AccessibilityScrollView::canSetFocusAttribute() const
{
- if (m_childrenDirty)
- clearChildren();
-
- if (!m_haveChildren)
- addChildren();
+ AccessibilityObject* webArea = webAreaObject();
+ return webArea && webArea->canSetFocusAttribute();
+}
- updateScrollbars();
+bool AccessibilityScrollView::isFocused() const
+{
+ AccessibilityObject* webArea = webAreaObject();
+ return webArea && webArea->isFocused();
+}
+
+void AccessibilityScrollView::setFocused(bool focused)
+{
+ if (AccessibilityObject* webArea = webAreaObject())
+ webArea->setFocused(focused);
+}
+
+void AccessibilityScrollView::updateChildrenIfNecessary()
+{
+ // Always update our children when asked for them so that we don't inadvertently cache them after
+ // a new web area has been created for this scroll view (like when moving back and forth through history).
+ // Since a ScrollViews children will always be relatively small and limited this should not be a performance problem.
+ clearChildren();
+ addChildren();
}
void AccessibilityScrollView::updateScrollbars()
@@ -106,14 +124,14 @@ void AccessibilityScrollView::updateScrollbars()
m_horizontalScrollbar = addChildScrollbar(m_scrollView->horizontalScrollbar());
else if (!m_scrollView->horizontalScrollbar() && m_horizontalScrollbar) {
removeChildScrollbar(m_horizontalScrollbar.get());
- m_horizontalScrollbar = 0;
+ m_horizontalScrollbar = nullptr;
}
if (m_scrollView->verticalScrollbar() && !m_verticalScrollbar)
m_verticalScrollbar = addChildScrollbar(m_scrollView->verticalScrollbar());
else if (!m_scrollView->verticalScrollbar() && m_verticalScrollbar) {
removeChildScrollbar(m_verticalScrollbar.get());
- m_verticalScrollbar = 0;
+ m_verticalScrollbar = nullptr;
}
}
@@ -129,19 +147,23 @@ void AccessibilityScrollView::removeChildScrollbar(AccessibilityObject* scrollba
AccessibilityScrollbar* AccessibilityScrollView::addChildScrollbar(Scrollbar* scrollbar)
{
if (!scrollbar)
- return 0;
+ return nullptr;
- AccessibilityScrollbar* scrollBarObject = static_cast<AccessibilityScrollbar*>(axObjectCache()->getOrCreate(scrollbar));
- scrollBarObject->setParent(this);
- m_children.append(scrollBarObject);
- return scrollBarObject;
+ AXObjectCache* cache = axObjectCache();
+ if (!cache)
+ return nullptr;
+
+ auto& scrollBarObject = downcast<AccessibilityScrollbar>(*cache->getOrCreate(scrollbar));
+ scrollBarObject.setParent(this);
+ m_children.append(&scrollBarObject);
+ return &scrollBarObject;
}
void AccessibilityScrollView::clearChildren()
{
AccessibilityObject::clearChildren();
- m_verticalScrollbar = 0;
- m_horizontalScrollbar = 0;
+ m_verticalScrollbar = nullptr;
+ m_horizontalScrollbar = nullptr;
}
bool AccessibilityScrollView::computeAccessibilityIsIgnored() const
@@ -167,21 +189,24 @@ void AccessibilityScrollView::addChildren()
AccessibilityObject* AccessibilityScrollView::webAreaObject() const
{
- if (!m_scrollView || !m_scrollView->isFrameView())
- return 0;
+ if (!is<FrameView>(m_scrollView))
+ return nullptr;
- Document* doc = toFrameView(m_scrollView)->frame()->document();
- if (!doc || !doc->renderer())
- return 0;
+ Document* document = downcast<FrameView>(*m_scrollView).frame().document();
+ if (!document || !document->hasLivingRenderTree())
+ return nullptr;
- return axObjectCache()->getOrCreate(doc);
+ if (AXObjectCache* cache = axObjectCache())
+ return cache->getOrCreate(document);
+
+ return nullptr;
}
AccessibilityObject* AccessibilityScrollView::accessibilityHitTest(const IntPoint& point) const
{
AccessibilityObject* webArea = webAreaObject();
if (!webArea)
- return 0;
+ return nullptr;
if (m_horizontalScrollbar && m_horizontalScrollbar->elementRect().contains(point))
return m_horizontalScrollbar.get();
@@ -196,39 +221,52 @@ LayoutRect AccessibilityScrollView::elementRect() const
if (!m_scrollView)
return LayoutRect();
- return m_scrollView->frameRect();
+ LayoutRect rect = m_scrollView->frameRect();
+ float topContentInset = m_scrollView->topContentInset();
+ // Top content inset pushes the frame down and shrinks it.
+ rect.move(0, topContentInset);
+ rect.contract(0, topContentInset);
+ return rect;
}
FrameView* AccessibilityScrollView::documentFrameView() const
{
- if (!m_scrollView || !m_scrollView->isFrameView())
- return 0;
+ if (!is<FrameView>(m_scrollView))
+ return nullptr;
- return toFrameView(m_scrollView);
+ return downcast<FrameView>(m_scrollView);
}
AccessibilityObject* AccessibilityScrollView::parentObject() const
{
- if (!m_scrollView || !m_scrollView->isFrameView())
- return 0;
-
- HTMLFrameOwnerElement* owner = toFrameView(m_scrollView)->frame()->ownerElement();
+ if (!is<FrameView>(m_scrollView))
+ return nullptr;
+
+ AXObjectCache* cache = axObjectCache();
+ if (!cache)
+ return nullptr;
+
+ HTMLFrameOwnerElement* owner = downcast<FrameView>(*m_scrollView).frame().ownerElement();
if (owner && owner->renderer())
- return axObjectCache()->getOrCreate(owner);
+ return cache->getOrCreate(owner);
- return 0;
+ return nullptr;
}
AccessibilityObject* AccessibilityScrollView::parentObjectIfExists() const
{
- if (!m_scrollView || !m_scrollView->isFrameView())
- return 0;
+ if (!is<FrameView>(m_scrollView))
+ return nullptr;
- HTMLFrameOwnerElement* owner = toFrameView(m_scrollView)->frame()->ownerElement();
+ AXObjectCache* cache = axObjectCache();
+ if (!cache)
+ return nullptr;
+
+ HTMLFrameOwnerElement* owner = downcast<FrameView>(m_scrollView)->frame().ownerElement();
if (owner && owner->renderer())
- return axObjectCache()->get(owner);
+ return cache->get(owner);
- return 0;
+ return nullptr;
}
ScrollableArea* AccessibilityScrollView::getScrollableAreaIfScrollable() const
diff --git a/Source/WebCore/accessibility/AccessibilityScrollView.h b/Source/WebCore/accessibility/AccessibilityScrollView.h
index 20ab64ea6..0c464bb09 100644
--- a/Source/WebCore/accessibility/AccessibilityScrollView.h
+++ b/Source/WebCore/accessibility/AccessibilityScrollView.h
@@ -34,44 +34,46 @@ class AccessibilityScrollbar;
class Scrollbar;
class ScrollView;
-class AccessibilityScrollView : public AccessibilityObject {
+class AccessibilityScrollView final : public AccessibilityObject {
public:
- static PassRefPtr<AccessibilityScrollView> create(ScrollView*);
- virtual AccessibilityRole roleValue() const { return ScrollAreaRole; }
+ static Ref<AccessibilityScrollView> create(ScrollView*);
+ virtual AccessibilityRole roleValue() const override { return ScrollAreaRole; }
ScrollView* scrollView() const { return m_scrollView; }
virtual ~AccessibilityScrollView();
- virtual void detach();
+ virtual void detach(AccessibilityDetachmentType, AXObjectCache*) override;
+
+ AccessibilityObject* webAreaObject() const;
-protected:
- virtual ScrollableArea* getScrollableAreaIfScrollable() const;
- virtual void scrollTo(const IntPoint&) const;
-
private:
explicit AccessibilityScrollView(ScrollView*);
- virtual bool computeAccessibilityIsIgnored() const;
- virtual bool isAccessibilityScrollView() const { return true; }
- virtual bool isEnabled() const { return true; }
+ virtual ScrollableArea* getScrollableAreaIfScrollable() const override;
+ virtual void scrollTo(const IntPoint&) const override;
+ virtual bool computeAccessibilityIsIgnored() const override;
+ virtual bool isAccessibilityScrollView() const override { return true; }
+ virtual bool isEnabled() const override { return true; }
- virtual bool isAttachment() const;
- virtual Widget* widgetForAttachmentView() const;
+ virtual bool isAttachment() const override;
+ virtual Widget* widgetForAttachmentView() const override;
- virtual AccessibilityObject* scrollBar(AccessibilityOrientation);
- virtual void addChildren();
- virtual void clearChildren();
- virtual AccessibilityObject* accessibilityHitTest(const IntPoint&) const;
- virtual void updateChildrenIfNecessary();
- virtual void setNeedsToUpdateChildren() { m_childrenDirty = true; }
+ virtual AccessibilityObject* scrollBar(AccessibilityOrientation) override;
+ virtual void addChildren() override;
+ virtual void clearChildren() override;
+ virtual AccessibilityObject* accessibilityHitTest(const IntPoint&) const override;
+ virtual void updateChildrenIfNecessary() override;
+ virtual void setNeedsToUpdateChildren() override { m_childrenDirty = true; }
void updateScrollbars();
+ virtual void setFocused(bool) override;
+ virtual bool canSetFocusAttribute() const override;
+ virtual bool isFocused() const override;
- virtual FrameView* documentFrameView() const;
- virtual LayoutRect elementRect() const;
- virtual AccessibilityObject* parentObject() const;
- virtual AccessibilityObject* parentObjectIfExists() const;
+ virtual FrameView* documentFrameView() const override;
+ virtual LayoutRect elementRect() const override;
+ virtual AccessibilityObject* parentObject() const override;
+ virtual AccessibilityObject* parentObjectIfExists() const override;
- AccessibilityObject* webAreaObject() const;
- virtual AccessibilityObject* firstChild() const { return webAreaObject(); }
+ virtual AccessibilityObject* firstChild() const override { return webAreaObject(); }
AccessibilityScrollbar* addChildScrollbar(Scrollbar*);
void removeChildScrollbar(AccessibilityObject*);
@@ -80,16 +82,9 @@ private:
RefPtr<AccessibilityObject> m_verticalScrollbar;
bool m_childrenDirty;
};
-
-inline AccessibilityScrollView* toAccessibilityScrollView(AccessibilityObject* object)
-{
- ASSERT(!object || object->isAccessibilityScrollView());
- if (!object->isAccessibilityScrollView())
- return 0;
-
- return static_cast<AccessibilityScrollView*>(object);
-}
} // namespace WebCore
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilityScrollView, isAccessibilityScrollView())
+
#endif // AccessibilityScrollView_h
diff --git a/Source/WebCore/accessibility/AccessibilityScrollbar.cpp b/Source/WebCore/accessibility/AccessibilityScrollbar.cpp
index bf6d042d7..a46d1b21f 100644
--- a/Source/WebCore/accessibility/AccessibilityScrollbar.cpp
+++ b/Source/WebCore/accessibility/AccessibilityScrollbar.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2010, 2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -42,9 +42,9 @@ AccessibilityScrollbar::AccessibilityScrollbar(Scrollbar* scrollbar)
ASSERT(scrollbar);
}
-PassRefPtr<AccessibilityScrollbar> AccessibilityScrollbar::create(Scrollbar* scrollbar)
+Ref<AccessibilityScrollbar> AccessibilityScrollbar::create(Scrollbar* scrollbar)
{
- return adoptRef(new AccessibilityScrollbar(scrollbar));
+ return adoptRef(*new AccessibilityScrollbar(scrollbar));
}
LayoutRect AccessibilityScrollbar::elementRect() const
@@ -59,21 +59,22 @@ Document* AccessibilityScrollbar::document() const
{
AccessibilityObject* parent = parentObject();
if (!parent)
- return 0;
+ return nullptr;
return parent->document();
}
AccessibilityOrientation AccessibilityScrollbar::orientation() const
{
+ // ARIA 1.1 Elements with the role scrollbar have an implicit aria-orientation value of vertical.
if (!m_scrollbar)
- return AccessibilityOrientationHorizontal;
+ return AccessibilityOrientationVertical;
if (m_scrollbar->orientation() == HorizontalScrollbar)
return AccessibilityOrientationHorizontal;
if (m_scrollbar->orientation() == VerticalScrollbar)
return AccessibilityOrientationVertical;
- return AccessibilityOrientationHorizontal;
+ return AccessibilityOrientationVertical;
}
bool AccessibilityScrollbar::isEnabled() const
@@ -96,11 +97,8 @@ void AccessibilityScrollbar::setValue(float value)
if (!m_scrollbar)
return;
- if (!m_scrollbar->scrollableArea())
- return;
-
float newValue = value * m_scrollbar->maximum();
- m_scrollbar->scrollableArea()->scrollToOffsetWithoutAnimation(m_scrollbar->orientation(), newValue);
+ m_scrollbar->scrollableArea().scrollToOffsetWithoutAnimation(m_scrollbar->orientation(), newValue);
}
} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AccessibilityScrollbar.h b/Source/WebCore/accessibility/AccessibilityScrollbar.h
index 6caeaea92..2af3c9537 100644
--- a/Source/WebCore/accessibility/AccessibilityScrollbar.h
+++ b/Source/WebCore/accessibility/AccessibilityScrollbar.h
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -35,33 +35,35 @@ namespace WebCore {
class Scrollbar;
-class AccessibilityScrollbar : public AccessibilityMockObject {
+class AccessibilityScrollbar final : public AccessibilityMockObject {
public:
- static PassRefPtr<AccessibilityScrollbar> create(Scrollbar*);
+ static Ref<AccessibilityScrollbar> create(Scrollbar*);
Scrollbar* scrollbar() const { return m_scrollbar.get(); }
private:
explicit AccessibilityScrollbar(Scrollbar*);
- virtual bool canSetValueAttribute() const { return true; }
- virtual bool canSetNumericValue() const { return true; }
+ virtual bool canSetValueAttribute() const override { return true; }
+ virtual bool canSetNumericValue() const override { return true; }
- virtual bool isAccessibilityScrollbar() const { return true; }
- virtual LayoutRect elementRect() const;
+ virtual bool isAccessibilityScrollbar() const override { return true; }
+ virtual LayoutRect elementRect() const override;
- virtual AccessibilityRole roleValue() const { return ScrollBarRole; }
- virtual AccessibilityOrientation orientation() const;
- virtual Document* document() const;
- virtual bool isEnabled() const;
+ virtual AccessibilityRole roleValue() const override { return ScrollBarRole; }
+ virtual AccessibilityOrientation orientation() const override;
+ virtual Document* document() const override;
+ virtual bool isEnabled() const override;
// Assumes float [0..1]
- virtual void setValue(float);
- virtual float valueForRange() const;
+ virtual void setValue(float) override;
+ virtual float valueForRange() const override;
RefPtr<Scrollbar> m_scrollbar;
};
} // namespace WebCore
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilityScrollbar, isAccessibilityScrollbar())
+
#endif // AccessibilityScrollbar_h
diff --git a/Source/WebCore/accessibility/AccessibilitySlider.cpp b/Source/WebCore/accessibility/AccessibilitySlider.cpp
index 51cc898ef..d7ba9e43d 100644
--- a/Source/WebCore/accessibility/AccessibilitySlider.cpp
+++ b/Source/WebCore/accessibility/AccessibilitySlider.cpp
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -45,9 +45,9 @@ AccessibilitySlider::AccessibilitySlider(RenderObject* renderer)
{
}
-PassRefPtr<AccessibilitySlider> AccessibilitySlider::create(RenderObject* renderer)
+Ref<AccessibilitySlider> AccessibilitySlider::create(RenderObject* renderer)
{
- return adoptRef(new AccessibilitySlider(renderer));
+ return adoptRef(*new AccessibilitySlider(renderer));
}
AccessibilityOrientation AccessibilitySlider::orientation() const
@@ -56,11 +56,9 @@ AccessibilityOrientation AccessibilitySlider::orientation() const
if (!m_renderer)
return AccessibilityOrientationHorizontal;
- RenderStyle* style = m_renderer->style();
- if (!style)
- return AccessibilityOrientationHorizontal;
-
- ControlPart styleAppearance = style->appearance();
+ const RenderStyle& style = m_renderer->style();
+
+ ControlPart styleAppearance = style.appearance();
switch (styleAppearance) {
case SliderThumbHorizontalPart:
case SliderHorizontalPart:
@@ -84,22 +82,22 @@ void AccessibilitySlider::addChildren()
m_haveChildren = true;
- AXObjectCache* cache = m_renderer->document()->axObjectCache();
+ AXObjectCache* cache = m_renderer->document().axObjectCache();
- AccessibilitySliderThumb* thumb = static_cast<AccessibilitySliderThumb*>(cache->getOrCreate(SliderThumbRole));
- thumb->setParent(this);
+ auto& thumb = downcast<AccessibilitySliderThumb>(*cache->getOrCreate(SliderThumbRole));
+ thumb.setParent(this);
// Before actually adding the value indicator to the hierarchy,
// allow the platform to make a final decision about it.
- if (thumb->accessibilityIsIgnored())
- cache->remove(thumb->axObjectID());
+ if (thumb.accessibilityIsIgnored())
+ cache->remove(thumb.axObjectID());
else
- m_children.append(thumb);
+ m_children.append(&thumb);
}
const AtomicString& AccessibilitySlider::getAttribute(const QualifiedName& attribute) const
{
- return element()->getAttribute(attribute);
+ return inputElement()->getAttribute(attribute);
}
AccessibilityObject* AccessibilitySlider::elementAccessibilityHitTest(const IntPoint& point) const
@@ -115,35 +113,32 @@ AccessibilityObject* AccessibilitySlider::elementAccessibilityHitTest(const IntP
float AccessibilitySlider::valueForRange() const
{
- return element()->value().toFloat();
+ return inputElement()->value().toFloat();
}
float AccessibilitySlider::maxValueForRange() const
{
- return static_cast<float>(element()->maximum());
+ return static_cast<float>(inputElement()->maximum());
}
float AccessibilitySlider::minValueForRange() const
{
- return static_cast<float>(element()->minimum());
+ return static_cast<float>(inputElement()->minimum());
}
void AccessibilitySlider::setValue(const String& value)
{
- HTMLInputElement* input = element();
+ HTMLInputElement* input = inputElement();
if (input->value() == value)
return;
- input->setValue(value);
-
- // Fire change event manually, as RenderSlider::setValueForPosition does.
- input->dispatchFormControlChangeEvent();
+ input->setValue(value, DispatchChangeEvent);
}
-HTMLInputElement* AccessibilitySlider::element() const
+HTMLInputElement* AccessibilitySlider::inputElement() const
{
- return toHTMLInputElement(m_renderer->node());
+ return downcast<HTMLInputElement>(m_renderer->node());
}
@@ -151,9 +146,9 @@ AccessibilitySliderThumb::AccessibilitySliderThumb()
{
}
-PassRefPtr<AccessibilitySliderThumb> AccessibilitySliderThumb::create()
+Ref<AccessibilitySliderThumb> AccessibilitySliderThumb::create()
{
- return adoptRef(new AccessibilitySliderThumb());
+ return adoptRef(*new AccessibilitySliderThumb());
}
LayoutRect AccessibilitySliderThumb::elementRect() const
@@ -164,7 +159,9 @@ LayoutRect AccessibilitySliderThumb::elementRect() const
RenderObject* sliderRenderer = m_parent->renderer();
if (!sliderRenderer || !sliderRenderer->isSlider())
return LayoutRect();
- return sliderThumbElementOf(sliderRenderer->node())->boundingBox();
+ if (auto* thumbRenderer = downcast<RenderSlider>(*sliderRenderer).element().sliderThumbElement()->renderer())
+ return thumbRenderer->absoluteBoundingBoxRect();
+ return LayoutRect();
}
bool AccessibilitySliderThumb::computeAccessibilityIsIgnored() const
diff --git a/Source/WebCore/accessibility/AccessibilitySlider.h b/Source/WebCore/accessibility/AccessibilitySlider.h
index 2c4dbbf2d..1f2e573c9 100644
--- a/Source/WebCore/accessibility/AccessibilitySlider.h
+++ b/Source/WebCore/accessibility/AccessibilitySlider.h
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -37,52 +37,51 @@ namespace WebCore {
class HTMLInputElement;
class AccessibilitySlider : public AccessibilityRenderObject {
-
public:
- static PassRefPtr<AccessibilitySlider> create(RenderObject*);
+ static Ref<AccessibilitySlider> create(RenderObject*);
virtual ~AccessibilitySlider() { }
protected:
explicit AccessibilitySlider(RenderObject*);
private:
- HTMLInputElement* element() const;
- virtual AccessibilityObject* elementAccessibilityHitTest(const IntPoint&) const;
+ HTMLInputElement* inputElement() const;
+ virtual AccessibilityObject* elementAccessibilityHitTest(const IntPoint&) const override;
- virtual AccessibilityRole roleValue() const { return SliderRole; }
- virtual bool isSlider() const { return true; }
- virtual bool isInputSlider() const { return true; }
- virtual bool isControl() const { return true; }
+ virtual AccessibilityRole roleValue() const override { return SliderRole; }
+ virtual bool isSlider() const override final { return true; }
+ virtual bool isInputSlider() const override { return true; }
+ virtual bool isControl() const override { return true; }
- virtual void addChildren();
+ virtual void addChildren() override;
- virtual bool canSetValueAttribute() const { return true; }
+ virtual bool canSetValueAttribute() const override { return true; }
const AtomicString& getAttribute(const QualifiedName& attribute) const;
- virtual void setValue(const String&);
- virtual float valueForRange() const;
- virtual float maxValueForRange() const;
- virtual float minValueForRange() const;
- virtual AccessibilityOrientation orientation() const;
+ virtual void setValue(const String&) override;
+ virtual float valueForRange() const override;
+ virtual float maxValueForRange() const override;
+ virtual float minValueForRange() const override;
+ virtual AccessibilityOrientation orientation() const override;
};
-class AccessibilitySliderThumb : public AccessibilityMockObject {
-
+class AccessibilitySliderThumb final : public AccessibilityMockObject {
public:
- static PassRefPtr<AccessibilitySliderThumb> create();
+ static Ref<AccessibilitySliderThumb> create();
virtual ~AccessibilitySliderThumb() { }
- virtual AccessibilityRole roleValue() const { return SliderThumbRole; }
-
- virtual LayoutRect elementRect() const;
+ virtual AccessibilityRole roleValue() const override { return SliderThumbRole; }
+ virtual LayoutRect elementRect() const override;
private:
AccessibilitySliderThumb();
- virtual bool computeAccessibilityIsIgnored() const;
+ virtual bool isSliderThumb() const override { return true; }
+ virtual bool computeAccessibilityIsIgnored() const override;
};
-
} // namespace WebCore
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilitySliderThumb, isSliderThumb())
+
#endif // AccessibilitySlider_h
diff --git a/Source/WebCore/accessibility/AccessibilitySpinButton.cpp b/Source/WebCore/accessibility/AccessibilitySpinButton.cpp
index 46a182805..0c53370c0 100644
--- a/Source/WebCore/accessibility/AccessibilitySpinButton.cpp
+++ b/Source/WebCore/accessibility/AccessibilitySpinButton.cpp
@@ -27,17 +27,17 @@
#include "AccessibilitySpinButton.h"
#include "AXObjectCache.h"
-#include "RenderObject.h"
+#include "RenderElement.h"
namespace WebCore {
-PassRefPtr<AccessibilitySpinButton> AccessibilitySpinButton::create()
+Ref<AccessibilitySpinButton> AccessibilitySpinButton::create()
{
- return adoptRef(new AccessibilitySpinButton);
+ return adoptRef(*new AccessibilitySpinButton);
}
AccessibilitySpinButton::AccessibilitySpinButton()
- : m_spinButtonElement(0)
+ : m_spinButtonElement(nullptr)
{
}
@@ -82,15 +82,15 @@ void AccessibilitySpinButton::addChildren()
{
m_haveChildren = true;
- AccessibilitySpinButtonPart* incrementor = static_cast<AccessibilitySpinButtonPart*>(axObjectCache()->getOrCreate(SpinButtonPartRole));
- incrementor->setIsIncrementor(true);
- incrementor->setParent(this);
- m_children.append(incrementor);
+ auto& incrementor = downcast<AccessibilitySpinButtonPart>(*axObjectCache()->getOrCreate(SpinButtonPartRole));
+ incrementor.setIsIncrementor(true);
+ incrementor.setParent(this);
+ m_children.append(&incrementor);
- AccessibilitySpinButtonPart* decrementor = static_cast<AccessibilitySpinButtonPart*>(axObjectCache()->getOrCreate(SpinButtonPartRole));
- decrementor->setIsIncrementor(false);
- decrementor->setParent(this);
- m_children.append(decrementor);
+ auto& decrementor = downcast<AccessibilitySpinButtonPart>(*axObjectCache()->getOrCreate(SpinButtonPartRole));
+ decrementor.setIsIncrementor(false);
+ decrementor.setParent(this);
+ m_children.append(&decrementor);
}
void AccessibilitySpinButton::step(int amount)
@@ -109,9 +109,9 @@ AccessibilitySpinButtonPart::AccessibilitySpinButtonPart()
{
}
-PassRefPtr<AccessibilitySpinButtonPart> AccessibilitySpinButtonPart::create()
+Ref<AccessibilitySpinButtonPart> AccessibilitySpinButtonPart::create()
{
- return adoptRef(new AccessibilitySpinButtonPart);
+ return adoptRef(*new AccessibilitySpinButtonPart);
}
LayoutRect AccessibilitySpinButtonPart::elementRect() const
@@ -130,16 +130,16 @@ LayoutRect AccessibilitySpinButtonPart::elementRect() const
return parentRect;
}
-bool AccessibilitySpinButtonPart::press() const
+bool AccessibilitySpinButtonPart::press()
{
- if (!m_parent || !m_parent->isSpinButton())
+ if (!is<AccessibilitySpinButton>(m_parent))
return false;
- AccessibilitySpinButton* spinButton = toAccessibilitySpinButton(parentObject());
+ auto& spinButton = downcast<AccessibilitySpinButton>(*m_parent);
if (m_isIncrementor)
- spinButton->step(1);
+ spinButton.step(1);
else
- spinButton->step(-1);
+ spinButton.step(-1);
return true;
}
diff --git a/Source/WebCore/accessibility/AccessibilitySpinButton.h b/Source/WebCore/accessibility/AccessibilitySpinButton.h
index d3d6e6ad7..e0cb6c39a 100644
--- a/Source/WebCore/accessibility/AccessibilitySpinButton.h
+++ b/Source/WebCore/accessibility/AccessibilitySpinButton.h
@@ -32,9 +32,9 @@
namespace WebCore {
-class AccessibilitySpinButton : public AccessibilityMockObject {
+class AccessibilitySpinButton final : public AccessibilityMockObject {
public:
- static PassRefPtr<AccessibilitySpinButton> create();
+ static Ref<AccessibilitySpinButton> create();
virtual ~AccessibilitySpinButton();
void setSpinButtonElement(SpinButtonElement* spinButton) { m_spinButtonElement = spinButton; }
@@ -47,18 +47,18 @@ public:
private:
AccessibilitySpinButton();
- virtual AccessibilityRole roleValue() const { return SpinButtonRole; }
- virtual bool isSpinButton() const { return true; }
- virtual bool isNativeSpinButton() const { return true; }
- virtual void addChildren();
- virtual LayoutRect elementRect() const;
+ virtual AccessibilityRole roleValue() const override { return SpinButtonRole; }
+ virtual bool isSpinButton() const override { return true; }
+ virtual bool isNativeSpinButton() const override { return true; }
+ virtual void addChildren() override;
+ virtual LayoutRect elementRect() const override;
SpinButtonElement* m_spinButtonElement;
};
-class AccessibilitySpinButtonPart : public AccessibilityMockObject {
+class AccessibilitySpinButtonPart final : public AccessibilityMockObject {
public:
- static PassRefPtr<AccessibilitySpinButtonPart> create();
+ static Ref<AccessibilitySpinButtonPart> create();
virtual ~AccessibilitySpinButtonPart() { }
bool isIncrementor() const { return m_isIncrementor; }
@@ -66,26 +66,18 @@ public:
private:
AccessibilitySpinButtonPart();
- bool m_isIncrementor : 1;
- virtual bool press() const;
- virtual AccessibilityRole roleValue() const { return ButtonRole; }
- virtual bool isSpinButtonPart() const { return true; }
- virtual LayoutRect elementRect() const;
+ virtual bool press() override;
+ virtual AccessibilityRole roleValue() const override { return ButtonRole; }
+ virtual bool isSpinButtonPart() const override { return true; }
+ virtual LayoutRect elementRect() const override;
+
+ unsigned m_isIncrementor : 1;
};
-inline AccessibilitySpinButton* toAccessibilitySpinButton(AccessibilityObject* object)
-{
- ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isNativeSpinButton());
- return static_cast<AccessibilitySpinButton*>(object);
-}
-
-inline AccessibilitySpinButtonPart* toAccessibilitySpinButtonPart(AccessibilityObject* object)
-{
- ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isSpinButtonPart());
- return static_cast<AccessibilitySpinButtonPart*>(object);
-}
-
-} // namespace WebCore
+} // namespace WebCore
+
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilitySpinButton, isNativeSpinButton())
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilitySpinButtonPart, isSpinButtonPart())
#endif // AccessibilitySpinButton_h
diff --git a/Source/WebCore/accessibility/AccessibilityTable.cpp b/Source/WebCore/accessibility/AccessibilityTable.cpp
index 9ac955d06..e5bb16978 100644
--- a/Source/WebCore/accessibility/AccessibilityTable.cpp
+++ b/Source/WebCore/accessibility/AccessibilityTable.cpp
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -34,6 +34,7 @@
#include "AccessibilityTableColumn.h"
#include "AccessibilityTableHeaderContainer.h"
#include "AccessibilityTableRow.h"
+#include "ElementIterator.h"
#include "HTMLNames.h"
#include "HTMLTableCaptionElement.h"
#include "HTMLTableCellElement.h"
@@ -43,14 +44,16 @@
#include "RenderTableCell.h"
#include "RenderTableSection.h"
+#include <wtf/Deque.h>
+
namespace WebCore {
using namespace HTMLNames;
AccessibilityTable::AccessibilityTable(RenderObject* renderer)
: AccessibilityRenderObject(renderer)
- , m_headerContainer(0)
- , m_isAccessibilityTable(true)
+ , m_headerContainer(nullptr)
+ , m_isExposableThroughAccessibility(true)
{
}
@@ -61,12 +64,12 @@ AccessibilityTable::~AccessibilityTable()
void AccessibilityTable::init()
{
AccessibilityRenderObject::init();
- m_isAccessibilityTable = isTableExposableThroughAccessibility();
+ m_isExposableThroughAccessibility = computeIsTableExposableThroughAccessibility();
}
-PassRefPtr<AccessibilityTable> AccessibilityTable::create(RenderObject* renderer)
+Ref<AccessibilityTable> AccessibilityTable::create(RenderObject* renderer)
{
- return adoptRef(new AccessibilityTable(renderer));
+ return adoptRef(*new AccessibilityTable(renderer));
}
bool AccessibilityTable::hasARIARole() const
@@ -81,14 +84,34 @@ bool AccessibilityTable::hasARIARole() const
return false;
}
-bool AccessibilityTable::isAccessibilityTable() const
+bool AccessibilityTable::isExposableThroughAccessibility() const
{
if (!m_renderer)
return false;
- return m_isAccessibilityTable;
+ return m_isExposableThroughAccessibility;
}
+HTMLTableElement* AccessibilityTable::tableElement() const
+{
+ if (!is<RenderTable>(*m_renderer))
+ return nullptr;
+
+ RenderTable& table = downcast<RenderTable>(*m_renderer);
+ if (is<HTMLTableElement>(table.element()))
+ return downcast<HTMLTableElement>(table.element());
+
+ table.forceSectionsRecalc();
+
+ // If the table has a display:table-row-group, then the RenderTable does not have a pointer to it's HTMLTableElement.
+ // We can instead find it by asking the firstSection for its parent.
+ RenderTableSection* firstBody = table.firstBody();
+ if (!firstBody || !firstBody->element())
+ return nullptr;
+
+ return ancestorsOfType<HTMLTableElement>(*(firstBody->element())).first();
+}
+
bool AccessibilityTable::isDataTable() const
{
if (!m_renderer)
@@ -101,38 +124,41 @@ bool AccessibilityTable::isDataTable() const
// When a section of the document is contentEditable, all tables should be
// treated as data tables, otherwise users may not be able to work with rich
// text editors that allow creating and editing tables.
- if (node() && node()->rendererIsEditable())
+ if (node() && node()->hasEditableStyle())
return true;
+ if (!is<RenderTable>(*m_renderer))
+ return false;
+
// This employs a heuristic to determine if this table should appear.
// Only "data" tables should be exposed as tables.
// Unfortunately, there is no good way to determine the difference
// between a "layout" table and a "data" table.
-
- RenderTable* table = toRenderTable(m_renderer);
- Node* tableNode = table->node();
- if (!tableNode || !isHTMLTableElement(tableNode))
- return false;
-
- // if there is a caption element, summary, THEAD, or TFOOT section, it's most certainly a data table
- HTMLTableElement* tableElement = toHTMLTableElement(tableNode);
- if (!tableElement->summary().isEmpty() || tableElement->tHead() || tableElement->tFoot() || tableElement->caption())
- return true;
-
- // if someone used "rules" attribute than the table should appear
- if (!tableElement->rules().isEmpty())
- return true;
-
- // if there's a colgroup or col element, it's probably a data table.
- for (Node* child = tableElement->firstChild(); child; child = child->nextSibling()) {
- if (child->hasTagName(colTag) || child->hasTagName(colgroupTag))
+ if (HTMLTableElement* tableElement = this->tableElement()) {
+ // If there is a caption element, summary, THEAD, or TFOOT section, it's most certainly a data table.
+ if (!tableElement->summary().isEmpty() || tableElement->tHead() || tableElement->tFoot() || tableElement->caption())
+ return true;
+
+ // If someone used "rules" attribute than the table should appear.
+ if (!tableElement->rules().isEmpty())
return true;
+
+ // If there's a colgroup or col element, it's probably a data table.
+ for (const auto& child : childrenOfType<HTMLElement>(*tableElement)) {
+ if (child.hasTagName(colTag) || child.hasTagName(colgroupTag))
+ return true;
+ }
}
+ // The following checks should only apply if this is a real <table> element.
+ if (!hasTagName(tableTag))
+ return false;
+
+ RenderTable& table = downcast<RenderTable>(*m_renderer);
// go through the cell's and check for tell-tale signs of "data" table status
// cells have borders, or use attributes like headers, abbr, scope or axis
- table->recalcSectionsIfNeeded();
- RenderTableSection* firstBody = table->firstBody();
+ table.recalcSectionsIfNeeded();
+ RenderTableSection* firstBody = table.firstBody();
if (!firstBody)
return false;
@@ -148,10 +174,8 @@ bool AccessibilityTable::isDataTable() const
return true;
// Store the background color of the table to check against cell's background colors.
- RenderStyle* tableStyle = table->style();
- if (!tableStyle)
- return false;
- Color tableBGColor = tableStyle->visitedDependentColor(CSSPropertyBackgroundColor);
+ const RenderStyle& tableStyle = table.style();
+ Color tableBGColor = tableStyle.visitedDependentColor(CSSPropertyBackgroundColor);
// check enough of the cells to find if the table matches our criteria
// Criteria:
@@ -177,61 +201,60 @@ bool AccessibilityTable::isDataTable() const
RenderTableCell* cell = firstBody->primaryCellAt(row, col);
if (!cell)
continue;
- Node* cellNode = cell->node();
- if (!cellNode)
+
+ Element* cellElement = cell->element();
+ if (!cellElement)
continue;
if (cell->width() < 1 || cell->height() < 1)
continue;
- validCellCount++;
-
- HTMLTableCellElement* cellElement = static_cast<HTMLTableCellElement*>(cellNode);
+ ++validCellCount;
bool isTHCell = cellElement->hasTagName(thTag);
// If the first row is comprised of all <th> tags, assume it is a data table.
if (!row && isTHCell)
- headersInFirstRowCount++;
+ ++headersInFirstRowCount;
// If the first column is comprised of all <th> tags, assume it is a data table.
if (!col && isTHCell)
- headersInFirstColumnCount++;
-
- // in this case, the developer explicitly assigned a "data" table attribute
- if (!cellElement->headers().isEmpty() || !cellElement->abbr().isEmpty()
- || !cellElement->axis().isEmpty() || !cellElement->scope().isEmpty())
- return true;
+ ++headersInFirstColumnCount;
- RenderStyle* renderStyle = cell->style();
- if (!renderStyle)
- continue;
+ // In this case, the developer explicitly assigned a "data" table attribute.
+ if (is<HTMLTableCellElement>(*cellElement)) {
+ HTMLTableCellElement& tableCellElement = downcast<HTMLTableCellElement>(*cellElement);
+ if (!tableCellElement.headers().isEmpty() || !tableCellElement.abbr().isEmpty()
+ || !tableCellElement.axis().isEmpty() || !tableCellElement.scope().isEmpty())
+ return true;
+ }
+ const RenderStyle& renderStyle = cell->style();
// If the empty-cells style is set, we'll call it a data table.
- if (renderStyle->emptyCells() == HIDE)
+ if (renderStyle.emptyCells() == HIDE)
return true;
// If a cell has matching bordered sides, call it a (fully) bordered cell.
if ((cell->borderTop() > 0 && cell->borderBottom() > 0)
|| (cell->borderLeft() > 0 && cell->borderRight() > 0))
- borderedCellCount++;
+ ++borderedCellCount;
// Also keep track of each individual border, so we can catch tables where most
// cells have a bottom border, for example.
if (cell->borderTop() > 0)
- cellsWithTopBorder++;
+ ++cellsWithTopBorder;
if (cell->borderBottom() > 0)
- cellsWithBottomBorder++;
+ ++cellsWithBottomBorder;
if (cell->borderLeft() > 0)
- cellsWithLeftBorder++;
+ ++cellsWithLeftBorder;
if (cell->borderRight() > 0)
- cellsWithRightBorder++;
+ ++cellsWithRightBorder;
// If the cell has a different color from the table and there is cell spacing,
// then it is probably a data table cell (spacing and colors take the place of borders).
- Color cellColor = renderStyle->visitedDependentColor(CSSPropertyBackgroundColor);
- if (table->hBorderSpacing() > 0 && table->vBorderSpacing() > 0
+ Color cellColor = renderStyle.visitedDependentColor(CSSPropertyBackgroundColor);
+ if (table.hBorderSpacing() > 0 && table.vBorderSpacing() > 0
&& tableBGColor != cellColor && cellColor.alpha() != 1)
- backgroundDifferenceCellCount++;
+ ++backgroundDifferenceCellCount;
// If we've found 10 "good" cells, we don't need to keep searching.
if (borderedCellCount >= 10 || backgroundDifferenceCellCount >= 10)
@@ -239,15 +262,13 @@ bool AccessibilityTable::isDataTable() const
// For the first 5 rows, cache the background color so we can check if this table has zebra-striped rows.
if (row < 5 && row == alternatingRowColorCount) {
- RenderObject* renderRow = cell->parent();
- if (!renderRow || !renderRow->isBoxModelObject() || !toRenderBoxModelObject(renderRow)->isTableRow())
+ RenderElement* renderRow = cell->parent();
+ if (!is<RenderTableRow>(renderRow))
continue;
- RenderStyle* rowRenderStyle = renderRow->style();
- if (!rowRenderStyle)
- continue;
- Color rowColor = rowRenderStyle->visitedDependentColor(CSSPropertyBackgroundColor);
+ const RenderStyle& rowRenderStyle = renderRow->style();
+ Color rowColor = rowRenderStyle.visitedDependentColor(CSSPropertyBackgroundColor);
alternatingRowColors[alternatingRowColorCount] = rowColor;
- alternatingRowColorCount++;
+ ++alternatingRowColorCount;
}
}
@@ -292,7 +313,7 @@ bool AccessibilityTable::isDataTable() const
return false;
}
-bool AccessibilityTable::isTableExposableThroughAccessibility() const
+bool AccessibilityTable::computeIsTableExposableThroughAccessibility() const
{
// The following is a heuristic used to determine if a
// <table> should be exposed as an AXTable. The goal
@@ -307,12 +328,6 @@ bool AccessibilityTable::isTableExposableThroughAccessibility() const
if (hasARIARole())
return false;
- // Gtk+ ATs expect all tables to be exposed as tables.
-#if PLATFORM(GTK)
- Node* tableNode = toRenderTable(m_renderer)->node();
- return tableNode && isHTMLTableElement(tableNode);
-#endif
-
return isDataTable();
}
@@ -324,13 +339,13 @@ void AccessibilityTable::clearChildren()
if (m_headerContainer) {
m_headerContainer->detachFromParent();
- m_headerContainer = 0;
+ m_headerContainer = nullptr;
}
}
void AccessibilityTable::addChildren()
{
- if (!isAccessibilityTable()) {
+ if (!isExposableThroughAccessibility()) {
AccessibilityRenderObject::addChildren();
return;
}
@@ -338,68 +353,120 @@ void AccessibilityTable::addChildren()
ASSERT(!m_haveChildren);
m_haveChildren = true;
- if (!m_renderer || !m_renderer->isTable())
+ if (!is<RenderTable>(m_renderer))
return;
- RenderTable* table = toRenderTable(m_renderer);
- AXObjectCache* axCache = m_renderer->document()->axObjectCache();
-
+ RenderTable& table = downcast<RenderTable>(*m_renderer);
// Go through all the available sections to pull out the rows and add them as children.
- table->recalcSectionsIfNeeded();
- RenderTableSection* tableSection = table->topSection();
- if (!tableSection)
- return;
+ table.recalcSectionsIfNeeded();
- unsigned maxColumnCount = 0;
- while (tableSection) {
-
- HashSet<AccessibilityObject*> appendedRows;
- unsigned numRows = tableSection->numRows();
- for (unsigned rowIndex = 0; rowIndex < numRows; ++rowIndex) {
-
- RenderTableRow* renderRow = tableSection->rowRendererAt(rowIndex);
- if (!renderRow)
- continue;
-
- AccessibilityObject* rowObject = axCache->getOrCreate(renderRow);
- if (!rowObject->isTableRow())
- continue;
-
- AccessibilityTableRow* row = static_cast<AccessibilityTableRow*>(rowObject);
- // We need to check every cell for a new row, because cell spans
- // can cause us to miss rows if we just check the first column.
- if (appendedRows.contains(row))
- continue;
-
- row->setRowIndex(static_cast<int>(m_rows.size()));
- m_rows.append(row);
- if (!row->accessibilityIsIgnored())
- m_children.append(row);
-#if PLATFORM(GTK)
- else
- m_children.appendVector(row->children());
-#endif
- appendedRows.add(row);
+ if (HTMLTableElement* tableElement = this->tableElement()) {
+ if (HTMLTableCaptionElement* caption = tableElement->caption()) {
+ AccessibilityObject* axCaption = axObjectCache()->getOrCreate(caption);
+ if (axCaption && !axCaption->accessibilityIsIgnored())
+ m_children.append(axCaption);
}
+ }
+
+ unsigned maxColumnCount = 0;
+ RenderTableSection* footer = table.footer();
- maxColumnCount = max(tableSection->numColumns(), maxColumnCount);
- tableSection = table->sectionBelow(tableSection, SkipEmptySections);
+ for (RenderTableSection* tableSection = table.topSection(); tableSection; tableSection = table.sectionBelow(tableSection, SkipEmptySections)) {
+ if (tableSection == footer)
+ continue;
+ addChildrenFromSection(tableSection, maxColumnCount);
}
+ // Process the footer last, in case it was ordered earlier in the DOM.
+ if (footer)
+ addChildrenFromSection(footer, maxColumnCount);
+
+ AXObjectCache* axCache = m_renderer->document().axObjectCache();
// make the columns based on the number of columns in the first body
unsigned length = maxColumnCount;
for (unsigned i = 0; i < length; ++i) {
- AccessibilityTableColumn* column = static_cast<AccessibilityTableColumn*>(axCache->getOrCreate(ColumnRole));
- column->setColumnIndex((int)i);
- column->setParent(this);
- m_columns.append(column);
- if (!column->accessibilityIsIgnored())
- m_children.append(column);
+ auto& column = downcast<AccessibilityTableColumn>(*axCache->getOrCreate(ColumnRole));
+ column.setColumnIndex((int)i);
+ column.setParent(this);
+ m_columns.append(&column);
+ if (!column.accessibilityIsIgnored())
+ m_children.append(&column);
}
AccessibilityObject* headerContainerObject = headerContainer();
if (headerContainerObject && !headerContainerObject->accessibilityIsIgnored())
m_children.append(headerContainerObject);
+
+ // Sometimes the cell gets the wrong role initially because it is created before the parent
+ // determines whether it is an accessibility table. Iterate all the cells and allow them to
+ // update their roles now that the table knows its status.
+ // see bug: https://bugs.webkit.org/show_bug.cgi?id=147001
+ for (const auto& row : m_rows) {
+ for (const auto& cell : row->children())
+ cell->updateAccessibilityRole();
+ }
+
+}
+
+void AccessibilityTable::addTableCellChild(AccessibilityObject* rowObject, HashSet<AccessibilityObject*>& appendedRows, unsigned& columnCount)
+{
+ if (!rowObject || !is<AccessibilityTableRow>(*rowObject))
+ return;
+
+ auto& row = downcast<AccessibilityTableRow>(*rowObject);
+ // We need to check every cell for a new row, because cell spans
+ // can cause us to miss rows if we just check the first column.
+ if (appendedRows.contains(&row))
+ return;
+
+ row.setRowIndex(static_cast<int>(m_rows.size()));
+ m_rows.append(&row);
+ if (!row.accessibilityIsIgnored())
+ m_children.append(&row);
+ appendedRows.add(&row);
+
+ // store the maximum number of columns
+ unsigned rowCellCount = row.children().size();
+ if (rowCellCount > columnCount)
+ columnCount = rowCellCount;
+}
+
+void AccessibilityTable::addChildrenFromSection(RenderTableSection* tableSection, unsigned& maxColumnCount)
+{
+ ASSERT(tableSection);
+ if (!tableSection)
+ return;
+
+ AXObjectCache* axCache = m_renderer->document().axObjectCache();
+ HashSet<AccessibilityObject*> appendedRows;
+ unsigned numRows = tableSection->numRows();
+ for (unsigned rowIndex = 0; rowIndex < numRows; ++rowIndex) {
+
+ RenderTableRow* renderRow = tableSection->rowRendererAt(rowIndex);
+ if (!renderRow)
+ continue;
+
+ AccessibilityObject& rowObject = *axCache->getOrCreate(renderRow);
+
+ // If the row is anonymous, we should dive deeper into the descendants to try to find a valid row.
+ if (renderRow->isAnonymous()) {
+ Deque<AccessibilityObject*> queue;
+ queue.append(&rowObject);
+
+ while (!queue.isEmpty()) {
+ AccessibilityObject* obj = queue.takeFirst();
+ if (obj->node() && is<AccessibilityTableRow>(*obj)) {
+ addTableCellChild(obj, appendedRows, maxColumnCount);
+ continue;
+ }
+ for (auto* child = obj->firstChild(); child; child = child->nextSibling())
+ queue.append(child);
+ }
+ } else
+ addTableCellChild(&rowObject, appendedRows, maxColumnCount);
+ }
+
+ maxColumnCount = std::max(tableSection->numColumns(), maxColumnCount);
}
AccessibilityObject* AccessibilityTable::headerContainer()
@@ -407,27 +474,43 @@ AccessibilityObject* AccessibilityTable::headerContainer()
if (m_headerContainer)
return m_headerContainer.get();
- AccessibilityMockObject* tableHeader = toAccessibilityMockObject(axObjectCache()->getOrCreate(TableHeaderContainerRole));
- tableHeader->setParent(this);
+ auto& tableHeader = downcast<AccessibilityMockObject>(*axObjectCache()->getOrCreate(TableHeaderContainerRole));
+ tableHeader.setParent(this);
- m_headerContainer = tableHeader;
+ m_headerContainer = &tableHeader;
return m_headerContainer.get();
}
-AccessibilityObject::AccessibilityChildrenVector& AccessibilityTable::columns()
+const AccessibilityObject::AccessibilityChildrenVector& AccessibilityTable::columns()
{
updateChildrenIfNecessary();
return m_columns;
}
-AccessibilityObject::AccessibilityChildrenVector& AccessibilityTable::rows()
+const AccessibilityObject::AccessibilityChildrenVector& AccessibilityTable::rows()
{
updateChildrenIfNecessary();
return m_rows;
}
+
+void AccessibilityTable::columnHeaders(AccessibilityChildrenVector& headers)
+{
+ if (!m_renderer)
+ return;
+
+ updateChildrenIfNecessary();
+ // Sometimes m_columns can be reset during the iteration, we cache it here to be safe.
+ AccessibilityChildrenVector columnsCopy = m_columns;
+
+ for (const auto& column : columnsCopy) {
+ if (AccessibilityObject* header = downcast<AccessibilityTableColumn>(*column).headerObject())
+ headers.append(header);
+ }
+}
+
void AccessibilityTable::rowHeaders(AccessibilityChildrenVector& headers)
{
if (!m_renderer)
@@ -435,31 +518,28 @@ void AccessibilityTable::rowHeaders(AccessibilityChildrenVector& headers)
updateChildrenIfNecessary();
- unsigned rowCount = m_rows.size();
- for (unsigned k = 0; k < rowCount; ++k) {
- AccessibilityObject* header = static_cast<AccessibilityTableRow*>(m_rows[k].get())->headerObject();
- if (!header)
- continue;
- headers.append(header);
+ // Sometimes m_rows can be reset during the iteration, we cache it here to be safe.
+ AccessibilityChildrenVector rowsCopy = m_rows;
+
+ for (const auto& row : rowsCopy) {
+ if (AccessibilityObject* header = downcast<AccessibilityTableRow>(*row).headerObject())
+ headers.append(header);
}
}
-void AccessibilityTable::columnHeaders(AccessibilityChildrenVector& headers)
+void AccessibilityTable::visibleRows(AccessibilityChildrenVector& rows)
{
if (!m_renderer)
return;
updateChildrenIfNecessary();
- unsigned colCount = m_columns.size();
- for (unsigned k = 0; k < colCount; ++k) {
- AccessibilityObject* header = static_cast<AccessibilityTableColumn*>(m_columns[k].get())->headerObject();
- if (!header)
- continue;
- headers.append(header);
+ for (const auto& row : m_rows) {
+ if (row && !row->isOffScreen())
+ rows.append(row);
}
}
-
+
void AccessibilityTable::cells(AccessibilityObject::AccessibilityChildrenVector& cells)
{
if (!m_renderer)
@@ -467,8 +547,8 @@ void AccessibilityTable::cells(AccessibilityObject::AccessibilityChildrenVector&
updateChildrenIfNecessary();
- for (size_t row = 0; row < m_rows.size(); ++row)
- cells.appendVector(m_rows[row]->children());
+ for (const auto& row : m_rows)
+ cells.appendVector(row->children());
}
unsigned AccessibilityTable::columnCount()
@@ -489,7 +569,7 @@ int AccessibilityTable::tableLevel() const
{
int level = 0;
for (AccessibilityObject* obj = static_cast<AccessibilityObject*>(const_cast<AccessibilityTable*>(this)); obj; obj = obj->parentObject()) {
- if (obj->isAccessibilityTable())
+ if (is<AccessibilityTable>(*obj) && downcast<AccessibilityTable>(*obj).isExposableThroughAccessibility())
++level;
}
@@ -500,40 +580,44 @@ AccessibilityTableCell* AccessibilityTable::cellForColumnAndRow(unsigned column,
{
updateChildrenIfNecessary();
if (column >= columnCount() || row >= rowCount())
- return 0;
+ return nullptr;
// Iterate backwards through the rows in case the desired cell has a rowspan and exists in a previous row.
for (unsigned rowIndexCounter = row + 1; rowIndexCounter > 0; --rowIndexCounter) {
unsigned rowIndex = rowIndexCounter - 1;
- AccessibilityChildrenVector children = m_rows[rowIndex]->children();
+ const auto& children = m_rows[rowIndex]->children();
// Since some cells may have colspans, we have to check the actual range of each
// cell to determine which is the right one.
for (unsigned colIndexCounter = std::min(static_cast<unsigned>(children.size()), column + 1); colIndexCounter > 0; --colIndexCounter) {
unsigned colIndex = colIndexCounter - 1;
AccessibilityObject* child = children[colIndex].get();
- ASSERT(child->isTableCell());
- if (!child->isTableCell())
+ ASSERT(is<AccessibilityTableCell>(*child));
+ if (!is<AccessibilityTableCell>(*child))
continue;
- pair<unsigned, unsigned> columnRange;
- pair<unsigned, unsigned> rowRange;
- AccessibilityTableCell* tableCellChild = static_cast<AccessibilityTableCell*>(child);
- tableCellChild->columnIndexRange(columnRange);
- tableCellChild->rowIndexRange(rowRange);
+ std::pair<unsigned, unsigned> columnRange;
+ std::pair<unsigned, unsigned> rowRange;
+ auto& tableCellChild = downcast<AccessibilityTableCell>(*child);
+ tableCellChild.columnIndexRange(columnRange);
+ tableCellChild.rowIndexRange(rowRange);
if ((column >= columnRange.first && column < (columnRange.first + columnRange.second))
&& (row >= rowRange.first && row < (rowRange.first + rowRange.second)))
- return tableCellChild;
+ return &tableCellChild;
}
}
- return 0;
+ return nullptr;
}
AccessibilityRole AccessibilityTable::roleValue() const
{
- if (!isAccessibilityTable())
+ if (!isExposableThroughAccessibility())
return AccessibilityRenderObject::roleValue();
+
+ AccessibilityRole ariaRole = ariaRoleAttribute();
+ if (ariaRole == GridRole || ariaRole == TreeGridRole)
+ return GridRole;
return TableRole;
}
@@ -546,15 +630,22 @@ bool AccessibilityTable::computeAccessibilityIsIgnored() const
if (decision == IgnoreObject)
return true;
- if (!isAccessibilityTable())
+ if (!isExposableThroughAccessibility())
return AccessibilityRenderObject::computeAccessibilityIsIgnored();
return false;
}
-
+
+void AccessibilityTable::titleElementText(Vector<AccessibilityText>& textOrder) const
+{
+ String title = this->title();
+ if (!title.isEmpty())
+ textOrder.append(AccessibilityText(title, LabelByElementText));
+}
+
String AccessibilityTable::title() const
{
- if (!isAccessibilityTable())
+ if (!isExposableThroughAccessibility())
return AccessibilityRenderObject::title();
String title;
@@ -563,9 +654,8 @@ String AccessibilityTable::title() const
// see if there is a caption
Node* tableElement = m_renderer->node();
- if (tableElement && isHTMLTableElement(tableElement)) {
- HTMLTableCaptionElement* caption = toHTMLTableElement(tableElement)->caption();
- if (caption)
+ if (is<HTMLTableElement>(tableElement)) {
+ if (HTMLTableCaptionElement* caption = downcast<HTMLTableElement>(*tableElement).caption())
title = caption->innerText();
}
@@ -576,4 +666,30 @@ String AccessibilityTable::title() const
return title;
}
+int AccessibilityTable::ariaColumnCount() const
+{
+ const AtomicString& colCountValue = getAttribute(aria_colcountAttr);
+
+ int colCountInt = colCountValue.toInt();
+ // If only a portion of the columns is present in the DOM at a given moment, this attribute is needed to
+ // provide an explicit indication of the number of columns in the full table.
+ if (colCountInt > (int)m_columns.size())
+ return colCountInt;
+
+ return -1;
+}
+
+int AccessibilityTable::ariaRowCount() const
+{
+ const AtomicString& rowCountValue = getAttribute(aria_rowcountAttr);
+
+ int rowCountInt = rowCountValue.toInt();
+ // If only a portion of the rows is present in the DOM at a given moment, this attribute is needed to
+ // provide an explicit indication of the number of rows in the full table.
+ if (rowCountInt > (int)m_rows.size())
+ return rowCountInt;
+
+ return -1;
+}
+
} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AccessibilityTable.h b/Source/WebCore/accessibility/AccessibilityTable.h
index f1945e8ee..255729dc5 100644
--- a/Source/WebCore/accessibility/AccessibilityTable.h
+++ b/Source/WebCore/accessibility/AccessibilityTable.h
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -35,33 +35,31 @@
namespace WebCore {
class AccessibilityTableCell;
+class HTMLTableElement;
class RenderTableSection;
class AccessibilityTable : public AccessibilityRenderObject {
-
-protected:
- explicit AccessibilityTable(RenderObject*);
public:
- static PassRefPtr<AccessibilityTable> create(RenderObject*);
+ static Ref<AccessibilityTable> create(RenderObject*);
virtual ~AccessibilityTable();
- virtual void init();
+ virtual void init() override final;
- virtual AccessibilityRole roleValue() const;
+ virtual AccessibilityRole roleValue() const override final;
virtual bool isAriaTable() const { return false; }
- virtual void addChildren();
- virtual void clearChildren();
+ virtual void addChildren() override;
+ virtual void clearChildren() override final;
- AccessibilityChildrenVector& columns();
- AccessibilityChildrenVector& rows();
+ const AccessibilityChildrenVector& columns();
+ const AccessibilityChildrenVector& rows();
virtual bool supportsSelectedRows() { return false; }
unsigned columnCount();
unsigned rowCount();
- virtual int tableLevel() const;
+ virtual int tableLevel() const override final;
- virtual String title() const;
+ virtual String title() const override final;
// all the cells in the table
void cells(AccessibilityChildrenVector&);
@@ -69,37 +67,45 @@ public:
void columnHeaders(AccessibilityChildrenVector&);
void rowHeaders(AccessibilityChildrenVector&);
-
+ void visibleRows(AccessibilityChildrenVector&);
+
// an object that contains, as children, all the objects that act as headers
AccessibilityObject* headerContainer();
+ // isExposableThroughAccessibility() is whether it is exposed as an AccessibilityTable to the platform.
+ bool isExposableThroughAccessibility() const;
+
+ int ariaColumnCount() const;
+ int ariaRowCount() const;
+
protected:
+ explicit AccessibilityTable(RenderObject*);
+
AccessibilityChildrenVector m_rows;
AccessibilityChildrenVector m_columns;
RefPtr<AccessibilityObject> m_headerContainer;
- bool m_isAccessibilityTable;
+ bool m_isExposableThroughAccessibility;
bool hasARIARole() const;
// isTable is whether it's an AccessibilityTable object.
- virtual bool isTable() const OVERRIDE { return true; }
- // isAccessibilityTable is whether it is exposed as an AccessibilityTable to the platform.
- virtual bool isAccessibilityTable() const OVERRIDE;
+ virtual bool isTable() const override final { return true; }
// isDataTable is whether it is exposed as an AccessibilityTable because the heuristic
// think this "looks" like a data-based table (instead of a table used for layout).
- virtual bool isDataTable() const OVERRIDE;
+ virtual bool isDataTable() const override final;
+ virtual bool computeAccessibilityIsIgnored() const override final;
- virtual bool isTableExposableThroughAccessibility() const;
- virtual bool computeAccessibilityIsIgnored() const OVERRIDE;
+private:
+ virtual bool computeIsTableExposableThroughAccessibility() const;
+ virtual void titleElementText(Vector<AccessibilityText>&) const override final;
+ HTMLTableElement* tableElement() const;
+ void addChildrenFromSection(RenderTableSection*, unsigned& maxColumnCount);
+ void addTableCellChild(AccessibilityObject*, HashSet<AccessibilityObject*>& appendedRows, unsigned& columnCount);
};
-
-inline AccessibilityTable* toAccessibilityTable(AccessibilityObject* object)
-{
- ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isTable());
- return static_cast<AccessibilityTable*>(object);
-}
-
+
} // namespace WebCore
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilityTable, isTable())
+
#endif // AccessibilityTable_h
diff --git a/Source/WebCore/accessibility/AccessibilityTableCell.cpp b/Source/WebCore/accessibility/AccessibilityTableCell.cpp
index 32227d21f..287997b08 100644
--- a/Source/WebCore/accessibility/AccessibilityTableCell.cpp
+++ b/Source/WebCore/accessibility/AccessibilityTableCell.cpp
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -30,18 +30,21 @@
#include "AccessibilityTableCell.h"
#include "AXObjectCache.h"
+#include "AccessibilityTable.h"
+#include "AccessibilityTableRow.h"
+#include "ElementIterator.h"
+#include "HTMLElement.h"
#include "HTMLNames.h"
#include "RenderObject.h"
#include "RenderTableCell.h"
-using namespace std;
-
namespace WebCore {
using namespace HTMLNames;
AccessibilityTableCell::AccessibilityTableCell(RenderObject* renderer)
: AccessibilityRenderObject(renderer)
+ , m_ariaColIndexFromRow(-1)
{
}
@@ -49,9 +52,9 @@ AccessibilityTableCell::~AccessibilityTableCell()
{
}
-PassRefPtr<AccessibilityTableCell> AccessibilityTableCell::create(RenderObject* renderer)
+Ref<AccessibilityTableCell> AccessibilityTableCell::create(RenderObject* renderer)
{
- return adoptRef(new AccessibilityTableCell(renderer));
+ return adoptRef(*new AccessibilityTableCell(renderer));
}
bool AccessibilityTableCell::computeAccessibilityIsIgnored() const
@@ -62,85 +65,273 @@ bool AccessibilityTableCell::computeAccessibilityIsIgnored() const
if (decision == IgnoreObject)
return true;
+ // Ignore anonymous table cells as long as they're not in a table (ie. when display:table is used).
+ RenderObject* renderTable = is<RenderTableCell>(m_renderer) ? downcast<RenderTableCell>(*m_renderer).table() : nullptr;
+ bool inTable = renderTable && renderTable->node() && (renderTable->node()->hasTagName(tableTag) || nodeHasRole(renderTable->node(), "grid"));
+ if (!node() && !inTable)
+ return true;
+
if (!isTableCell())
return AccessibilityRenderObject::computeAccessibilityIsIgnored();
return false;
}
-
-AccessibilityObject* AccessibilityTableCell::parentTable() const
+
+AccessibilityTable* AccessibilityTableCell::parentTable() const
{
- if (!m_renderer || !m_renderer->isTableCell())
- return 0;
+ if (!is<RenderTableCell>(m_renderer))
+ return nullptr;
// If the document no longer exists, we might not have an axObjectCache.
if (!axObjectCache())
- return 0;
+ return nullptr;
// Do not use getOrCreate. parentTable() can be called while the render tree is being modified
// by javascript, and creating a table element may try to access the render tree while in a bad state.
// By using only get() implies that the AXTable must be created before AXTableCells. This should
// always be the case when AT clients access a table.
- // https://bugs.webkit.org/show_bug.cgi?id=42652
- return axObjectCache()->get(toRenderTableCell(m_renderer)->table());
+ // https://bugs.webkit.org/show_bug.cgi?id=42652
+ AccessibilityObject* parentTable = axObjectCache()->get(downcast<RenderTableCell>(*m_renderer).table());
+ if (!is<AccessibilityTable>(parentTable))
+ return nullptr;
+
+ // The RenderTableCell's table() object might be anonymous sometimes. We should handle it gracefully
+ // by finding the right table.
+ if (!parentTable->node()) {
+ for (AccessibilityObject* parent = parentObject(); parent; parent = parent->parentObject()) {
+ // If this is a non-anonymous table object, but not an accessibility table, we should stop because
+ // we don't want to choose another ancestor table as this cell's table.
+ if (is<AccessibilityTable>(*parent)) {
+ auto& parentTable = downcast<AccessibilityTable>(*parent);
+ if (parentTable.isExposableThroughAccessibility())
+ return &parentTable;
+ if (parentTable.node())
+ break;
+ }
+ }
+ return nullptr;
+ }
+
+ return downcast<AccessibilityTable>(parentTable);
}
bool AccessibilityTableCell::isTableCell() const
{
- AccessibilityObject* parent = parentObjectUnignored();
- if (!parent || !parent->isTableRow())
- return false;
-
- return true;
+ // If the parent table is an accessibility table, then we are a table cell.
+ // This used to check if the unignoredParent was a row, but that exploded performance if
+ // this was in nested tables. This check should be just as good.
+ AccessibilityObject* parentTable = this->parentTable();
+ return is<AccessibilityTable>(parentTable) && downcast<AccessibilityTable>(*parentTable).isExposableThroughAccessibility();
}
AccessibilityRole AccessibilityTableCell::determineAccessibilityRole()
{
- // Always call determineAccessibleRole so that the ARIA role is set.
- // Even though this object reports a Cell role, the ARIA role will be used
- // to determine if it's a column header.
+ // AccessibilityRenderObject::determineAccessibleRole provides any ARIA-supplied
+ // role, falling back on the role to be used if we determine here that the element
+ // should not be exposed as a cell. Thus if we already know it's a cell, return that.
AccessibilityRole defaultRole = AccessibilityRenderObject::determineAccessibilityRole();
+ if (defaultRole == ColumnHeaderRole || defaultRole == RowHeaderRole || defaultRole == CellRole || defaultRole == GridCellRole)
+ return defaultRole;
+
if (!isTableCell())
return defaultRole;
-
+ if (isColumnHeaderCell())
+ return ColumnHeaderRole;
+ if (isRowHeaderCell())
+ return RowHeaderRole;
+
return CellRole;
}
-void AccessibilityTableCell::rowIndexRange(pair<unsigned, unsigned>& rowRange)
+bool AccessibilityTableCell::isTableHeaderCell() const
+{
+ return node() && node()->hasTagName(thTag);
+}
+
+bool AccessibilityTableCell::isColumnHeaderCell() const
+{
+ const AtomicString& scope = getAttribute(scopeAttr);
+ if (scope == "col" || scope == "colgroup")
+ return true;
+ if (scope == "row" || scope == "rowgroup")
+ return false;
+ if (!isTableHeaderCell())
+ return false;
+
+ // We are in a situation after checking the scope attribute.
+ // It is an attempt to resolve the type of th element without support in the specification.
+ // Checking tableTag and tbodyTag allows to check the case of direct row placement in the table and lets stop the loop at the table level.
+ for (Node* parentNode = node(); parentNode; parentNode = parentNode->parentNode()) {
+ if (parentNode->hasTagName(theadTag))
+ return true;
+ if (parentNode->hasTagName(tfootTag))
+ return false;
+ if (parentNode->hasTagName(tableTag) || parentNode->hasTagName(tbodyTag)) {
+ std::pair<unsigned, unsigned> rowRange;
+ rowIndexRange(rowRange);
+ if (!rowRange.first)
+ return true;
+ return false;
+ }
+ }
+ return false;
+}
+
+bool AccessibilityTableCell::isRowHeaderCell() const
+{
+ const AtomicString& scope = getAttribute(scopeAttr);
+ if (scope == "row" || scope == "rowgroup")
+ return true;
+ if (scope == "col" || scope == "colgroup")
+ return false;
+ if (!isTableHeaderCell())
+ return false;
+
+ // We are in a situation after checking the scope attribute.
+ // It is an attempt to resolve the type of th element without support in the specification.
+ // Checking tableTag allows to check the case of direct row placement in the table and lets stop the loop at the table level.
+ for (Node* parentNode = node(); parentNode; parentNode = parentNode->parentNode()) {
+ if (parentNode->hasTagName(tfootTag) || parentNode->hasTagName(tbodyTag) || parentNode->hasTagName(tableTag)) {
+ std::pair<unsigned, unsigned> colRange;
+ columnIndexRange(colRange);
+ if (!colRange.first)
+ return true;
+ return false;
+ }
+ if (parentNode->hasTagName(theadTag))
+ return false;
+ }
+ return false;
+}
+
+bool AccessibilityTableCell::isTableCellInSameRowGroup(AccessibilityTableCell* otherTableCell)
+{
+ Node* parentNode = node();
+ for ( ; parentNode; parentNode = parentNode->parentNode()) {
+ if (parentNode->hasTagName(theadTag) || parentNode->hasTagName(tbodyTag) || parentNode->hasTagName(tfootTag))
+ break;
+ }
+
+ Node* otherParentNode = otherTableCell->node();
+ for ( ; otherParentNode; otherParentNode = otherParentNode->parentNode()) {
+ if (otherParentNode->hasTagName(theadTag) || otherParentNode->hasTagName(tbodyTag) || otherParentNode->hasTagName(tfootTag))
+ break;
+ }
+
+ return otherParentNode == parentNode;
+}
+
+
+bool AccessibilityTableCell::isTableCellInSameColGroup(AccessibilityTableCell* tableCell)
+{
+ std::pair<unsigned, unsigned> colRange;
+ columnIndexRange(colRange);
+
+ std::pair<unsigned, unsigned> otherColRange;
+ tableCell->columnIndexRange(otherColRange);
+
+ if (colRange.first <= (otherColRange.first + otherColRange.second))
+ return true;
+ return false;
+}
+
+String AccessibilityTableCell::expandedTextValue() const
{
- if (!m_renderer || !m_renderer->isTableCell())
+ return getAttribute(abbrAttr);
+}
+
+bool AccessibilityTableCell::supportsExpandedTextValue() const
+{
+ return isTableHeaderCell() && hasAttribute(abbrAttr);
+}
+
+void AccessibilityTableCell::columnHeaders(AccessibilityChildrenVector& headers)
+{
+ AccessibilityTable* parent = parentTable();
+ if (!parent)
return;
+
+ // Choose columnHeaders as the place where the "headers" attribute is reported.
+ ariaElementsFromAttribute(headers, headersAttr);
+ // If the headers attribute returned valid values, then do not further search for column headers.
+ if (!headers.isEmpty())
+ return;
+
+ std::pair<unsigned, unsigned> rowRange;
+ rowIndexRange(rowRange);
+
+ std::pair<unsigned, unsigned> colRange;
+ columnIndexRange(colRange);
- RenderTableCell* renderCell = toRenderTableCell(m_renderer);
- rowRange.first = renderCell->rowIndex();
- rowRange.second = renderCell->rowSpan();
+ for (unsigned row = 0; row < rowRange.first; row++) {
+ AccessibilityTableCell* tableCell = parent->cellForColumnAndRow(colRange.first, row);
+ if (!tableCell || tableCell == this || headers.contains(tableCell))
+ continue;
+
+ std::pair<unsigned, unsigned> childRowRange;
+ tableCell->rowIndexRange(childRowRange);
+
+ const AtomicString& scope = tableCell->getAttribute(scopeAttr);
+ if (scope == "colgroup" && isTableCellInSameColGroup(tableCell))
+ headers.append(tableCell);
+ else if (tableCell->isColumnHeaderCell())
+ headers.append(tableCell);
+ }
+}
- // since our table might have multiple sections, we have to offset our row appropriately
- RenderTableSection* section = renderCell->section();
- RenderTable* table = renderCell->table();
- if (!table || !section)
+void AccessibilityTableCell::rowHeaders(AccessibilityChildrenVector& headers)
+{
+ AccessibilityTable* parent = parentTable();
+ if (!parent)
return;
- RenderTableSection* tableSection = table->topSection();
- unsigned rowOffset = 0;
- while (tableSection) {
- if (tableSection == section)
- break;
- rowOffset += tableSection->numRows();
- tableSection = table->sectionBelow(tableSection, SkipEmptySections);
+ std::pair<unsigned, unsigned> rowRange;
+ rowIndexRange(rowRange);
+
+ std::pair<unsigned, unsigned> colRange;
+ columnIndexRange(colRange);
+
+ for (unsigned column = 0; column < colRange.first; column++) {
+ AccessibilityTableCell* tableCell = parent->cellForColumnAndRow(column, rowRange.first);
+ if (!tableCell || tableCell == this || headers.contains(tableCell))
+ continue;
+
+ const AtomicString& scope = tableCell->getAttribute(scopeAttr);
+ if (scope == "rowgroup" && isTableCellInSameRowGroup(tableCell))
+ headers.append(tableCell);
+ else if (tableCell->isRowHeaderCell())
+ headers.append(tableCell);
}
+}
- rowRange.first += rowOffset;
+AccessibilityTableRow* AccessibilityTableCell::parentRow() const
+{
+ AccessibilityObject* parent = parentObjectUnignored();
+ if (!is<AccessibilityTableRow>(*parent))
+ return nullptr;
+ return downcast<AccessibilityTableRow>(parent);
+}
+
+void AccessibilityTableCell::rowIndexRange(std::pair<unsigned, unsigned>& rowRange) const
+{
+ if (!is<RenderTableCell>(m_renderer))
+ return;
+
+ RenderTableCell& renderCell = downcast<RenderTableCell>(*m_renderer);
+ rowRange.second = renderCell.rowSpan();
+
+ if (AccessibilityTableRow* parentRow = this->parentRow())
+ rowRange.first = parentRow->rowIndex();
}
-void AccessibilityTableCell::columnIndexRange(pair<unsigned, unsigned>& columnRange)
+void AccessibilityTableCell::columnIndexRange(std::pair<unsigned, unsigned>& columnRange) const
{
- if (!m_renderer || !m_renderer->isTableCell())
+ if (!is<RenderTableCell>(m_renderer))
return;
- RenderTableCell* renderCell = toRenderTableCell(m_renderer);
- columnRange.first = renderCell->col();
- columnRange.second = renderCell->colSpan();
+ const RenderTableCell& cell = downcast<RenderTableCell>(*m_renderer);
+ columnRange.first = cell.table()->colToEffCol(cell.col());
+ columnRange.second = cell.table()->colToEffCol(cell.col() + cell.colSpan()) - columnRange.first;
}
AccessibilityObject* AccessibilityTableCell::titleUIElement() const
@@ -148,37 +339,90 @@ AccessibilityObject* AccessibilityTableCell::titleUIElement() const
// Try to find if the first cell in this row is a <th>. If it is,
// then it can act as the title ui element. (This is only in the
// case when the table is not appearing as an AXTable.)
- if (isTableCell() || !m_renderer || !m_renderer->isTableCell())
- return 0;
+ if (isTableCell() || !is<RenderTableCell>(m_renderer))
+ return nullptr;
// Table cells that are th cannot have title ui elements, since by definition
// they are title ui elements
Node* node = m_renderer->node();
if (node && node->hasTagName(thTag))
- return 0;
+ return nullptr;
- RenderTableCell* renderCell = toRenderTableCell(m_renderer);
+ RenderTableCell& renderCell = downcast<RenderTableCell>(*m_renderer);
// If this cell is in the first column, there is no need to continue.
- int col = renderCell->col();
+ int col = renderCell.col();
if (!col)
- return 0;
+ return nullptr;
- int row = renderCell->rowIndex();
+ int row = renderCell.rowIndex();
- RenderTableSection* section = renderCell->section();
+ RenderTableSection* section = renderCell.section();
if (!section)
- return 0;
+ return nullptr;
RenderTableCell* headerCell = section->primaryCellAt(row, 0);
- if (!headerCell || headerCell == renderCell)
- return 0;
+ if (!headerCell || headerCell == &renderCell)
+ return nullptr;
- Node* cellElement = headerCell->node();
- if (!cellElement || !cellElement->hasTagName(thTag))
- return 0;
+ if (!headerCell->element() || !headerCell->element()->hasTagName(thTag))
+ return nullptr;
return axObjectCache()->getOrCreate(headerCell);
}
+int AccessibilityTableCell::ariaColumnIndex() const
+{
+ const AtomicString& colIndexValue = getAttribute(aria_colindexAttr);
+ if (colIndexValue.toInt() >= 1)
+ return colIndexValue.toInt();
+
+ // "ARIA 1.1: If the set of columns which is present in the DOM is contiguous, and if there are no cells which span more than one row
+ // or column in that set, then authors may place aria-colindex on each row, setting the value to the index of the first column of the set."
+ // Here, we let its parent row to set its index beforehand, so we don't have to go through the siblings to calculate the index.
+ AccessibilityTableRow* parentRow = this->parentRow();
+ if (parentRow && m_ariaColIndexFromRow != -1)
+ return m_ariaColIndexFromRow;
+
+ return -1;
+}
+
+int AccessibilityTableCell::ariaRowIndex() const
+{
+ // ARIA 1.1: Authors should place aria-rowindex on each row. Authors may also place
+ // aria-rowindex on all of the children or owned elements of each row.
+ const AtomicString& rowIndexValue = getAttribute(aria_rowindexAttr);
+ if (rowIndexValue.toInt() >= 1)
+ return rowIndexValue.toInt();
+
+ if (AccessibilityTableRow* parentRow = this->parentRow())
+ return parentRow->ariaRowIndex();
+
+ return -1;
+}
+
+unsigned AccessibilityTableCell::ariaColumnSpan() const
+{
+ const AtomicString& colSpanValue = getAttribute(aria_colspanAttr);
+ // ARIA 1.1: Authors must set the value of aria-colspan to an integer greater than or equal to 1.
+ if (colSpanValue.toInt() >= 1)
+ return colSpanValue.toInt();
+
+ return 1;
+}
+
+unsigned AccessibilityTableCell::ariaRowSpan() const
+{
+ const AtomicString& rowSpanValue = getAttribute(aria_rowspanAttr);
+
+ // ARIA 1.1: Authors must set the value of aria-rowspan to an integer greater than or equal to 0.
+ // Setting the value to 0 indicates that the cell or gridcell is to span all the remaining rows in the row group.
+ if (rowSpanValue == "0")
+ return 0;
+ if (rowSpanValue.toInt() >= 1)
+ return rowSpanValue.toInt();
+
+ return 1;
+}
+
} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AccessibilityTableCell.h b/Source/WebCore/accessibility/AccessibilityTableCell.h
index 78f0b906b..85c418252 100644
--- a/Source/WebCore/accessibility/AccessibilityTableCell.h
+++ b/Source/WebCore/accessibility/AccessibilityTableCell.h
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -33,33 +33,57 @@
namespace WebCore {
+class AccessibilityTable;
+class AccessibilityTableRow;
+
class AccessibilityTableCell : public AccessibilityRenderObject {
-
-protected:
- explicit AccessibilityTableCell(RenderObject*);
public:
- static PassRefPtr<AccessibilityTableCell> create(RenderObject*);
+ static Ref<AccessibilityTableCell> create(RenderObject*);
virtual ~AccessibilityTableCell();
- virtual bool isTableCell() const;
+ virtual bool isTableCell() const override final;
+ bool isTableHeaderCell() const;
+ bool isColumnHeaderCell() const;
+ bool isRowHeaderCell() const;
// fills in the start location and row span of cell
- virtual void rowIndexRange(pair<unsigned, unsigned>& rowRange);
+ virtual void rowIndexRange(std::pair<unsigned, unsigned>& rowRange) const;
// fills in the start location and column span of cell
- virtual void columnIndexRange(pair<unsigned, unsigned>& columnRange);
+ virtual void columnIndexRange(std::pair<unsigned, unsigned>& columnRange) const;
+ void columnHeaders(AccessibilityChildrenVector&);
+ void rowHeaders(AccessibilityChildrenVector&);
+
+ int ariaColumnIndex() const;
+ int ariaRowIndex() const;
+ unsigned ariaColumnSpan() const;
+ unsigned ariaRowSpan() const;
+ void setARIAColIndexFromRow(int index) { m_ariaColIndexFromRow = index; }
+
protected:
- virtual AccessibilityObject* parentTable() const;
+ explicit AccessibilityTableCell(RenderObject*);
+
+ AccessibilityTableRow* parentRow() const;
+ virtual AccessibilityTable* parentTable() const;
+ virtual AccessibilityRole determineAccessibilityRole() override final;
+
int m_rowIndex;
- virtual AccessibilityRole determineAccessibilityRole();
+ int m_ariaColIndexFromRow;
private:
// If a table cell is not exposed as a table cell, a TH element can serve as its title UI element.
- virtual AccessibilityObject* titleUIElement() const;
- virtual bool exposesTitleUIElement() const { return true; }
- virtual bool computeAccessibilityIsIgnored() const;
-};
-
+ virtual AccessibilityObject* titleUIElement() const override final;
+ virtual bool exposesTitleUIElement() const override final { return true; }
+ virtual bool computeAccessibilityIsIgnored() const override final;
+ virtual String expandedTextValue() const override final;
+ virtual bool supportsExpandedTextValue() const override final;
+
+ bool isTableCellInSameRowGroup(AccessibilityTableCell*);
+ bool isTableCellInSameColGroup(AccessibilityTableCell*);
+};
+
} // namespace WebCore
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilityTableCell, isTableCell())
+
#endif // AccessibilityTableCell_h
diff --git a/Source/WebCore/accessibility/AccessibilityTableColumn.cpp b/Source/WebCore/accessibility/AccessibilityTableColumn.cpp
index 4d494822d..3fa78b558 100644
--- a/Source/WebCore/accessibility/AccessibilityTableColumn.cpp
+++ b/Source/WebCore/accessibility/AccessibilityTableColumn.cpp
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -31,13 +31,12 @@
#include "AXObjectCache.h"
#include "AccessibilityTableCell.h"
+#include "HTMLElement.h"
#include "HTMLNames.h"
#include "RenderTable.h"
#include "RenderTableCell.h"
#include "RenderTableSection.h"
-using namespace std;
-
namespace WebCore {
using namespace HTMLNames;
@@ -50,9 +49,9 @@ AccessibilityTableColumn::~AccessibilityTableColumn()
{
}
-PassRefPtr<AccessibilityTableColumn> AccessibilityTableColumn::create()
+Ref<AccessibilityTableColumn> AccessibilityTableColumn::create()
{
- return adoptRef(new AccessibilityTableColumn());
+ return adoptRef(*new AccessibilityTableColumn());
}
void AccessibilityTableColumn::setParent(AccessibilityObject* parent)
@@ -64,90 +63,100 @@ void AccessibilityTableColumn::setParent(AccessibilityObject* parent)
LayoutRect AccessibilityTableColumn::elementRect() const
{
- // this will be filled in when addChildren is called
- return m_columnRect;
+ // This used to be cached during the call to addChildren(), but calling elementRect()
+ // can invalidate elements, so its better to ask for this on demand.
+ LayoutRect columnRect;
+ AccessibilityChildrenVector childrenCopy = m_children;
+ for (const auto& cell : childrenCopy)
+ columnRect.unite(cell->elementRect());
+
+ return columnRect;
}
AccessibilityObject* AccessibilityTableColumn::headerObject()
{
if (!m_parent)
- return 0;
+ return nullptr;
RenderObject* renderer = m_parent->renderer();
if (!renderer)
- return 0;
-
- if (!m_parent->isAccessibilityTable())
- return 0;
+ return nullptr;
+ if (!is<AccessibilityTable>(*m_parent))
+ return nullptr;
+
+ auto& parentTable = downcast<AccessibilityTable>(*m_parent);
+ if (!parentTable.isExposableThroughAccessibility())
+ return nullptr;
- AccessibilityTable* parentTable = toAccessibilityTable(m_parent);
- if (parentTable->isAriaTable()) {
- AccessibilityChildrenVector rowChildren = children();
- unsigned childrenCount = rowChildren.size();
- for (unsigned i = 0; i < childrenCount; ++i) {
- AccessibilityObject* cell = rowChildren[i].get();
+ if (parentTable.isAriaTable()) {
+ for (const auto& cell : children()) {
if (cell->ariaRoleAttribute() == ColumnHeaderRole)
- return cell;
+ return cell.get();
}
- return 0;
+ return nullptr;
}
- if (!renderer->isTable())
- return 0;
-
- RenderTable* table = toRenderTable(renderer);
-
- AccessibilityObject* headerObject = 0;
+ if (!is<RenderTable>(*renderer))
+ return nullptr;
- // try the <thead> section first. this doesn't require th tags
- headerObject = headerObjectForSection(table->header(), false);
+ RenderTable& table = downcast<RenderTable>(*renderer);
- if (headerObject)
+ // try the <thead> section first. this doesn't require th tags
+ if (auto* headerObject = headerObjectForSection(table.header(), false))
return headerObject;
- // now try for <th> tags in the first body
- headerObject = headerObjectForSection(table->firstBody(), true);
-
- return headerObject;
+ RenderTableSection* bodySection = table.firstBody();
+ while (bodySection && bodySection->isAnonymous())
+ bodySection = table.sectionBelow(bodySection, SkipEmptySections);
+
+ // now try for <th> tags in the first body. If the first body is
+ return headerObjectForSection(bodySection, true);
}
AccessibilityObject* AccessibilityTableColumn::headerObjectForSection(RenderTableSection* section, bool thTagRequired)
{
if (!section)
- return 0;
+ return nullptr;
unsigned numCols = section->numColumns();
if (m_columnIndex >= numCols)
- return 0;
+ return nullptr;
if (!section->numRows())
- return 0;
+ return nullptr;
- RenderTableCell* cell = 0;
+ RenderTableCell* cell = nullptr;
// also account for cells that have a span
for (int testCol = m_columnIndex; testCol >= 0; --testCol) {
- RenderTableCell* testCell = section->primaryCellAt(0, testCol);
- if (!testCell)
- continue;
- // we've reached a cell that doesn't even overlap our column
- // it can't be our header
- if ((testCell->col() + (testCell->colSpan()-1)) < m_columnIndex)
+ // Run down the rows in case initial rows are invalid (like when a <caption> is used).
+ unsigned rowCount = section->numRows();
+ for (unsigned testRow = 0; testRow < rowCount; testRow++) {
+ RenderTableCell* testCell = section->primaryCellAt(testRow, testCol);
+ // No cell at this index, keep checking more rows and columns.
+ if (!testCell)
+ continue;
+
+ // If we've reached a cell that doesn't even overlap our column it can't be the header.
+ if ((testCell->col() + (testCell->colSpan()-1)) < m_columnIndex)
+ break;
+
+ // If this does not have an element (like a <caption>) then check the next row
+ if (!testCell->element())
+ continue;
+
+ // If th is required, but we found an element that doesn't have a th tag, we can stop looking.
+ if (thTagRequired && !testCell->element()->hasTagName(thTag))
+ break;
+
+ cell = testCell;
break;
-
- Node* node = testCell->node();
- if (!node)
- continue;
-
- if (thTagRequired && !node->hasTagName(thTag))
- continue;
-
- cell = testCell;
+ }
}
if (!cell)
- return 0;
+ return nullptr;
return axObjectCache()->getOrCreate(cell);
}
@@ -157,7 +166,7 @@ bool AccessibilityTableColumn::computeAccessibilityIsIgnored() const
if (!m_parent)
return true;
-#if PLATFORM(GTK)
+#if PLATFORM(IOS) || PLATFORM(GTK) || PLATFORM(EFL)
return true;
#endif
@@ -169,14 +178,17 @@ void AccessibilityTableColumn::addChildren()
ASSERT(!m_haveChildren);
m_haveChildren = true;
- if (!m_parent || !m_parent->isAccessibilityTable())
+ if (!is<AccessibilityTable>(m_parent))
+ return;
+
+ auto& parentTable = downcast<AccessibilityTable>(*m_parent);
+ if (!parentTable.isExposableThroughAccessibility())
return;
- AccessibilityTable* parentTable = toAccessibilityTable(m_parent);
- int numRows = parentTable->rowCount();
+ int numRows = parentTable.rowCount();
- for (int i = 0; i < numRows; i++) {
- AccessibilityTableCell* cell = parentTable->cellForColumnAndRow(m_columnIndex, i);
+ for (int i = 0; i < numRows; ++i) {
+ AccessibilityTableCell* cell = parentTable.cellForColumnAndRow(m_columnIndex, i);
if (!cell)
continue;
@@ -185,7 +197,6 @@ void AccessibilityTableColumn::addChildren()
continue;
m_children.append(cell);
- m_columnRect.unite(cell->elementRect());
}
}
diff --git a/Source/WebCore/accessibility/AccessibilityTableColumn.h b/Source/WebCore/accessibility/AccessibilityTableColumn.h
index 6e737c121..5b6eb7b1c 100644
--- a/Source/WebCore/accessibility/AccessibilityTableColumn.h
+++ b/Source/WebCore/accessibility/AccessibilityTableColumn.h
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -37,35 +37,35 @@ namespace WebCore {
class RenderTableSection;
-class AccessibilityTableColumn : public AccessibilityMockObject {
-
-private:
- AccessibilityTableColumn();
+class AccessibilityTableColumn final : public AccessibilityMockObject {
public:
- static PassRefPtr<AccessibilityTableColumn> create();
+ static Ref<AccessibilityTableColumn> create();
virtual ~AccessibilityTableColumn();
AccessibilityObject* headerObject();
- virtual AccessibilityRole roleValue() const { return ColumnRole; }
- virtual bool isTableColumn() const { return true; }
+ virtual AccessibilityRole roleValue() const override { return ColumnRole; }
void setColumnIndex(int columnIndex) { m_columnIndex = columnIndex; }
int columnIndex() const { return m_columnIndex; }
- virtual void addChildren();
- virtual void setParent(AccessibilityObject*);
+ virtual void addChildren() override;
+ virtual void setParent(AccessibilityObject*) override;
- virtual LayoutRect elementRect() const;
+ virtual LayoutRect elementRect() const override;
-private:
- unsigned m_columnIndex;
- LayoutRect m_columnRect;
+private:
+ AccessibilityTableColumn();
AccessibilityObject* headerObjectForSection(RenderTableSection*, bool thTagRequired);
- virtual bool computeAccessibilityIsIgnored() const;
+ virtual bool computeAccessibilityIsIgnored() const override;
+ virtual bool isTableColumn() const override { return true; }
+
+ unsigned m_columnIndex;
};
-
+
} // namespace WebCore
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilityTableColumn, isTableColumn())
+
#endif // AccessibilityTableColumn_h
diff --git a/Source/WebCore/accessibility/AccessibilityTableHeaderContainer.cpp b/Source/WebCore/accessibility/AccessibilityTableHeaderContainer.cpp
index 3e789def2..e30114829 100644
--- a/Source/WebCore/accessibility/AccessibilityTableHeaderContainer.cpp
+++ b/Source/WebCore/accessibility/AccessibilityTableHeaderContainer.cpp
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -32,8 +32,6 @@
#include "AXObjectCache.h"
#include "AccessibilityTable.h"
-using namespace std;
-
namespace WebCore {
AccessibilityTableHeaderContainer::AccessibilityTableHeaderContainer()
@@ -44,9 +42,9 @@ AccessibilityTableHeaderContainer::~AccessibilityTableHeaderContainer()
{
}
-PassRefPtr<AccessibilityTableHeaderContainer> AccessibilityTableHeaderContainer::create()
+Ref<AccessibilityTableHeaderContainer> AccessibilityTableHeaderContainer::create()
{
- return adoptRef(new AccessibilityTableHeaderContainer());
+ return adoptRef(*new AccessibilityTableHeaderContainer());
}
LayoutRect AccessibilityTableHeaderContainer::elementRect() const
@@ -60,7 +58,7 @@ bool AccessibilityTableHeaderContainer::computeAccessibilityIsIgnored() const
if (!m_parent)
return true;
-#if PLATFORM(GTK)
+#if PLATFORM(IOS) || PLATFORM(GTK) || PLATFORM(EFL)
return true;
#endif
@@ -72,14 +70,17 @@ void AccessibilityTableHeaderContainer::addChildren()
ASSERT(!m_haveChildren);
m_haveChildren = true;
- if (!m_parent || !m_parent->isAccessibilityTable())
+ if (!is<AccessibilityTable>(m_parent))
+ return;
+
+ auto& parentTable = downcast<AccessibilityTable>(*m_parent);
+ if (!parentTable.isExposableThroughAccessibility())
return;
- static_cast<AccessibilityTable*>(m_parent)->columnHeaders(m_children);
+ parentTable.columnHeaders(m_children);
- unsigned length = m_children.size();
- for (unsigned k = 0; k < length; ++k)
- m_headerRect.unite(m_children[k]->elementRect());
+ for (const auto& child : m_children)
+ m_headerRect.unite(child->elementRect());
}
} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AccessibilityTableHeaderContainer.h b/Source/WebCore/accessibility/AccessibilityTableHeaderContainer.h
index 6a9b2c95b..e4ce74068 100644
--- a/Source/WebCore/accessibility/AccessibilityTableHeaderContainer.h
+++ b/Source/WebCore/accessibility/AccessibilityTableHeaderContainer.h
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -35,24 +35,23 @@
namespace WebCore {
-class AccessibilityTableHeaderContainer : public AccessibilityMockObject {
-
-private:
- AccessibilityTableHeaderContainer();
+class AccessibilityTableHeaderContainer final : public AccessibilityMockObject {
public:
- static PassRefPtr<AccessibilityTableHeaderContainer> create();
+ static Ref<AccessibilityTableHeaderContainer> create();
virtual ~AccessibilityTableHeaderContainer();
- virtual AccessibilityRole roleValue() const { return TableHeaderContainerRole; }
+ virtual AccessibilityRole roleValue() const override { return TableHeaderContainerRole; }
- virtual void addChildren();
+ virtual void addChildren() override;
- virtual LayoutRect elementRect() const;
+ virtual LayoutRect elementRect() const override;
private:
- LayoutRect m_headerRect;
+ AccessibilityTableHeaderContainer();
- virtual bool computeAccessibilityIsIgnored() const;
+ virtual bool computeAccessibilityIsIgnored() const override;
+
+ LayoutRect m_headerRect;
};
} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AccessibilityTableRow.cpp b/Source/WebCore/accessibility/AccessibilityTableRow.cpp
index 44edada28..b51b18c41 100644
--- a/Source/WebCore/accessibility/AccessibilityTableRow.cpp
+++ b/Source/WebCore/accessibility/AccessibilityTableRow.cpp
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -30,6 +30,7 @@
#include "AccessibilityTableRow.h"
#include "AXObjectCache.h"
+#include "AccessibilityTable.h"
#include "AccessibilityTableCell.h"
#include "HTMLNames.h"
#include "HTMLTableRowElement.h"
@@ -37,8 +38,6 @@
#include "RenderTableCell.h"
#include "RenderTableRow.h"
-using namespace std;
-
namespace WebCore {
using namespace HTMLNames;
@@ -52,9 +51,9 @@ AccessibilityTableRow::~AccessibilityTableRow()
{
}
-PassRefPtr<AccessibilityTableRow> AccessibilityTableRow::create(RenderObject* renderer)
+Ref<AccessibilityTableRow> AccessibilityTableRow::create(RenderObject* renderer)
{
- return adoptRef(new AccessibilityTableRow(renderer));
+ return adoptRef(*new AccessibilityTableRow(renderer));
}
AccessibilityRole AccessibilityTableRow::determineAccessibilityRole()
@@ -62,11 +61,8 @@ AccessibilityRole AccessibilityTableRow::determineAccessibilityRole()
if (!isTableRow())
return AccessibilityRenderObject::determineAccessibilityRole();
- m_ariaRole = determineAriaRoleAttribute();
-
- AccessibilityRole ariaRole = ariaRoleAttribute();
- if (ariaRole != UnknownRole)
- return ariaRole;
+ if ((m_ariaRole = determineAriaRoleAttribute()) != UnknownRole)
+ return m_ariaRole;
return RowRole;
}
@@ -74,10 +70,7 @@ AccessibilityRole AccessibilityTableRow::determineAccessibilityRole()
bool AccessibilityTableRow::isTableRow() const
{
AccessibilityObject* table = parentTable();
- if (!table || !table->isAccessibilityTable())
- return false;
-
- return true;
+ return is<AccessibilityTable>(table) && downcast<AccessibilityTable>(*table).isExposableThroughAccessibility();
}
AccessibilityObject* AccessibilityTableRow::observableObject() const
@@ -100,43 +93,86 @@ bool AccessibilityTableRow::computeAccessibilityIsIgnored() const
return false;
}
-AccessibilityObject* AccessibilityTableRow::parentTable() const
+AccessibilityTable* AccessibilityTableRow::parentTable() const
{
// The parent table might not be the direct ancestor of the row unfortunately. ARIA states that role="grid" should
// only have "row" elements, but if not, we still should handle it gracefully by finding the right table.
for (AccessibilityObject* parent = parentObject(); parent; parent = parent->parentObject()) {
- // If this is a table object, but not an accessibility table, we should stop because we don't want to
+ // If this is a non-anonymous table object, but not an accessibility table, we should stop because we don't want to
// choose another ancestor table as this row's table.
- if (parent->isTable())
- return parent->isAccessibilityTable() ? parent : 0;
+ if (is<AccessibilityTable>(*parent)) {
+ auto& parentTable = downcast<AccessibilityTable>(*parent);
+ if (parentTable.isExposableThroughAccessibility())
+ return &parentTable;
+ if (parentTable.node())
+ break;
+ }
}
- return 0;
+ return nullptr;
}
AccessibilityObject* AccessibilityTableRow::headerObject()
{
if (!m_renderer || !m_renderer->isTableRow())
- return 0;
+ return nullptr;
- AccessibilityChildrenVector rowChildren = children();
+ const auto& rowChildren = children();
if (!rowChildren.size())
- return 0;
+ return nullptr;
// check the first element in the row to see if it is a TH element
AccessibilityObject* cell = rowChildren[0].get();
- if (!cell->isTableCell())
- return 0;
+ if (!is<AccessibilityTableCell>(*cell))
+ return nullptr;
- RenderObject* cellRenderer = static_cast<AccessibilityTableCell*>(cell)->renderer();
+ RenderObject* cellRenderer = downcast<AccessibilityTableCell>(*cell).renderer();
if (!cellRenderer)
- return 0;
+ return nullptr;
Node* cellNode = cellRenderer->node();
if (!cellNode || !cellNode->hasTagName(thTag))
- return 0;
+ return nullptr;
return cell;
}
+void AccessibilityTableRow::addChildren()
+{
+ AccessibilityRenderObject::addChildren();
+
+ // "ARIA 1.1, If the set of columns which is present in the DOM is contiguous, and if there are no cells which span more than one row or
+ // column in that set, then authors may place aria-colindex on each row, setting the value to the index of the first column of the set."
+ // Update child cells' ariaColIndex if there's an aria-colindex value set for the row. So the cell doesn't have to go through the siblings
+ // to calculate the index.
+ int colIndex = ariaColumnIndex();
+ if (colIndex == -1)
+ return;
+
+ unsigned index = 0;
+ for (const auto& cell : children()) {
+ if (is<AccessibilityTableCell>(*cell))
+ downcast<AccessibilityTableCell>(*cell).setARIAColIndexFromRow(colIndex + index);
+ index++;
+ }
+}
+
+int AccessibilityTableRow::ariaColumnIndex() const
+{
+ const AtomicString& colIndexValue = getAttribute(aria_colindexAttr);
+ if (colIndexValue.toInt() >= 1)
+ return colIndexValue.toInt();
+
+ return -1;
+}
+
+int AccessibilityTableRow::ariaRowIndex() const
+{
+ const AtomicString& rowIndexValue = getAttribute(aria_rowindexAttr);
+ if (rowIndexValue.toInt() >= 1)
+ return rowIndexValue.toInt();
+
+ return -1;
+}
+
} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AccessibilityTableRow.h b/Source/WebCore/accessibility/AccessibilityTableRow.h
index d242ee449..887c213c7 100644
--- a/Source/WebCore/accessibility/AccessibilityTableRow.h
+++ b/Source/WebCore/accessibility/AccessibilityTableRow.h
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -33,19 +33,16 @@
namespace WebCore {
+class AccessibilityTable;
+
class AccessibilityTableRow : public AccessibilityRenderObject {
-
-protected:
- explicit AccessibilityTableRow(RenderObject*);
public:
- static PassRefPtr<AccessibilityTableRow> create(RenderObject*);
+ static Ref<AccessibilityTableRow> create(RenderObject*);
virtual ~AccessibilityTableRow();
-
- virtual bool isTableRow() const;
// retrieves the "row" header (a th tag in the rightmost column)
virtual AccessibilityObject* headerObject();
- virtual AccessibilityObject* parentTable() const;
+ virtual AccessibilityTable* parentTable() const;
void setRowIndex(int rowIndex) { m_rowIndex = rowIndex; }
int rowIndex() const { return m_rowIndex; }
@@ -54,16 +51,26 @@ public:
// in the row, but their col/row spans overlap into it
void appendChild(AccessibilityObject*);
+ virtual void addChildren() override;
+
+ int ariaColumnIndex() const;
+ int ariaRowIndex() const;
+
protected:
- virtual AccessibilityRole determineAccessibilityRole();
+ explicit AccessibilityTableRow(RenderObject*);
+
+ virtual AccessibilityRole determineAccessibilityRole() override final;
private:
+ virtual bool isTableRow() const override final;
+ virtual AccessibilityObject* observableObject() const override final;
+ virtual bool computeAccessibilityIsIgnored() const override final;
+
int m_rowIndex;
-
- virtual AccessibilityObject* observableObject() const;
- virtual bool computeAccessibilityIsIgnored() const;
-};
-
-} // namespace WebCore
+};
+
+} // namespace WebCore
+
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilityTableRow, isTableRow())
#endif // AccessibilityTableRow_h
diff --git a/Source/WebCore/accessibility/AccessibilityTree.cpp b/Source/WebCore/accessibility/AccessibilityTree.cpp
new file mode 100644
index 000000000..f4613cdce
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityTree.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "AccessibilityTree.h"
+
+#include "AXObjectCache.h"
+#include "AccessibilityTreeItem.h"
+#include "Element.h"
+#include "HTMLNames.h"
+
+#include <wtf/Deque.h>
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+AccessibilityTree::AccessibilityTree(RenderObject* renderer)
+ : AccessibilityRenderObject(renderer)
+{
+}
+
+AccessibilityTree::~AccessibilityTree()
+{
+}
+
+Ref<AccessibilityTree> AccessibilityTree::create(RenderObject* renderer)
+{
+ return adoptRef(*new AccessibilityTree(renderer));
+}
+
+bool AccessibilityTree::computeAccessibilityIsIgnored() const
+{
+ return accessibilityIsIgnoredByDefault();
+}
+
+AccessibilityRole AccessibilityTree::determineAccessibilityRole()
+{
+ if ((m_ariaRole = determineAriaRoleAttribute()) != TreeRole)
+ return AccessibilityRenderObject::determineAccessibilityRole();
+
+ return isTreeValid() ? TreeRole : GroupRole;
+}
+
+bool AccessibilityTree::isTreeValid() const
+{
+ // A valid tree can only have treeitem or group of treeitems as a child
+ // http://www.w3.org/TR/wai-aria/roles#tree
+
+ Node* node = this->node();
+ if (!node)
+ return false;
+
+ Deque<Node*> queue;
+ for (auto* child = node->firstChild(); child; child = child->nextSibling())
+ queue.append(child);
+
+ while (!queue.isEmpty()) {
+ auto child = queue.takeFirst();
+
+ if (!is<Element>(*child))
+ continue;
+ if (nodeHasRole(child, "treeitem"))
+ continue;
+ if (!nodeHasRole(child, "group"))
+ return false;
+
+ for (auto* groupChild = child->firstChild(); groupChild; groupChild = groupChild->nextSibling())
+ queue.append(groupChild);
+ }
+ return true;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.h b/Source/WebCore/accessibility/AccessibilityTree.h
index 86fdc361a..2e69fe9cd 100644
--- a/Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.h
+++ b/Source/WebCore/accessibility/AccessibilityTree.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -26,13 +26,28 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef WebAccessibilityObjectWrapper_h
-#define WebAccessibilityObjectWrapper_h
-#import "WebAccessibilityObjectWrapperBase.h"
+#ifndef AccessibilityTree_h
+#define AccessibilityTree_h
-@interface WebAccessibilityObjectWrapper : WebAccessibilityObjectWrapperBase
+#include "AccessibilityRenderObject.h"
-@end
+namespace WebCore {
+
+class AccessibilityTree final : public AccessibilityRenderObject {
+public:
+ static Ref<AccessibilityTree> create(RenderObject*);
+ virtual ~AccessibilityTree();
-#endif // WebAccessibilityObjectWrapper_h
+private:
+ explicit AccessibilityTree(RenderObject*);
+ virtual bool computeAccessibilityIsIgnored() const override;
+ virtual AccessibilityRole determineAccessibilityRole() override;
+ bool isTreeValid() const;
+};
+
+} // namespace WebCore
+
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilityTree, isTree())
+
+#endif // AccessibilityTree_h
diff --git a/Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperBase.h b/Source/WebCore/accessibility/AccessibilityTreeItem.cpp
index 873dff7ac..cd36c38bd 100644
--- a/Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperBase.h
+++ b/Source/WebCore/accessibility/AccessibilityTreeItem.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -26,45 +26,40 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef WebAccessibilityObjectWrapperBase_h
-#define WebAccessibilityObjectWrapperBase_h
+#include "config.h"
+#include "AccessibilityTreeItem.h"
-#include <CoreGraphics/CoreGraphics.h>
+#include "AXObjectCache.h"
+#include "HTMLNames.h"
namespace WebCore {
-class AccessibilityObject;
-class IntRect;
-class FloatPoint;
-class Path;
-class VisiblePosition;
+
+using namespace HTMLNames;
+
+AccessibilityTreeItem::AccessibilityTreeItem(RenderObject* renderer)
+ : AccessibilityRenderObject(renderer)
+{
}
-
-@interface WebAccessibilityObjectWrapperBase : NSObject {
- WebCore::AccessibilityObject* m_object;
+
+AccessibilityTreeItem::~AccessibilityTreeItem()
+{
+}
+
+Ref<AccessibilityTreeItem> AccessibilityTreeItem::create(RenderObject* renderer)
+{
+ return adoptRef(*new AccessibilityTreeItem(renderer));
}
-
-- (id)initWithAccessibilityObject:(WebCore::AccessibilityObject*)axObject;
-- (void)detach;
-- (WebCore::AccessibilityObject*)accessibilityObject;
-- (BOOL)updateObjectBackingStore;
-
-- (NSString *)accessibilityTitle;
-- (NSString *)accessibilityDescription;
-- (NSString *)accessibilityHelpText;
-
-- (NSString *)ariaLandmarkRoleDescription;
-
-- (id)attachmentView;
-// Used to inform an element when a notification is posted for it. Used by DRT.
-- (void)accessibilityPostedNotification:(NSString *)notificationName;
-
-- (CGPathRef)convertPathToScreenSpace:(WebCore::Path &)path;
-- (CGPoint)convertPointToScreenSpace:(WebCore::FloatPoint &)point;
-
-// Math related functions
-- (NSArray *)accessibilityMathPostscriptPairs;
-- (NSArray *)accessibilityMathPrescriptPairs;
-@end
+AccessibilityRole AccessibilityTreeItem::determineAccessibilityRole()
+{
+
+ // Walk the parent chain looking for a parent that is a tree. A treeitem is
+ // only considered valid if it is in a tree.
+ AccessibilityObject* parent = nullptr;
+ for (parent = parentObject(); parent && !parent->isTree(); parent = parent->parentObject()) { }
+ m_isTreeItemValid = parent;
-#endif // WebAccessibilityObjectWrapperBase_h
+ return AccessibilityRenderObject::determineAccessibilityRole();
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AccessibilityTreeItem.h b/Source/WebCore/accessibility/AccessibilityTreeItem.h
new file mode 100644
index 000000000..f6e332ad8
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityTreeItem.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef AccessibilityTreeItem_h
+#define AccessibilityTreeItem_h
+
+#include "AccessibilityRenderObject.h"
+
+namespace WebCore {
+
+class AccessibilityTreeItem final : public AccessibilityRenderObject {
+public:
+ static Ref<AccessibilityTreeItem> create(RenderObject*);
+ virtual ~AccessibilityTreeItem();
+
+ virtual bool shouldIgnoreAttributeRole() const override { return !m_isTreeItemValid; }
+
+private:
+ explicit AccessibilityTreeItem(RenderObject*);
+ virtual AccessibilityRole determineAccessibilityRole() override;
+ bool m_isTreeItemValid;
+};
+
+} // namespace WebCore
+
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilityTreeItem, isTreeItem())
+
+#endif // AccessibilityTreeItem_h
diff --git a/Source/WebCore/accessibility/atk/AXObjectCacheAtk.cpp b/Source/WebCore/accessibility/atk/AXObjectCacheAtk.cpp
index 826d6c20e..85e0c64d5 100644
--- a/Source/WebCore/accessibility/atk/AXObjectCacheAtk.cpp
+++ b/Source/WebCore/accessibility/atk/AXObjectCacheAtk.cpp
@@ -30,14 +30,38 @@
#include "Range.h"
#include "TextIterator.h"
#include "WebKitAccessibleWrapperAtk.h"
-#include <wtf/gobject/GOwnPtr.h>
+#include <wtf/NeverDestroyed.h>
+#include <wtf/glib/GRefPtr.h>
#include <wtf/text/CString.h>
namespace WebCore {
-void AXObjectCache::detachWrapper(AccessibilityObject* obj)
+void AXObjectCache::detachWrapper(AccessibilityObject* obj, AccessibilityDetachmentType detachmentType)
{
- webkitAccessibleDetach(WEBKIT_ACCESSIBLE(obj->wrapper()));
+ AtkObject* wrapper = obj->wrapper();
+ ASSERT(wrapper);
+
+ // If an object is being detached NOT because of the AXObjectCache being destroyed,
+ // then it's being removed from the accessibility tree and we should emit a signal.
+ if (detachmentType != CacheDestroyed) {
+ if (obj->document()) {
+ // Look for the right object to emit the signal from, but using the implementation
+ // of atk_object_get_parent from AtkObject class (which uses a cached pointer if set)
+ // since the accessibility hierarchy in WebCore will no longer be navigable.
+ gpointer webkitAccessibleClass = g_type_class_peek_parent(WEBKIT_ACCESSIBLE_GET_CLASS(wrapper));
+ gpointer atkObjectClass = g_type_class_peek_parent(webkitAccessibleClass);
+ AtkObject* atkParent = ATK_OBJECT_CLASS(atkObjectClass)->get_parent(ATK_OBJECT(wrapper));
+
+ // We don't want to emit any signal from an object outside WebKit's world.
+ if (WEBKIT_IS_ACCESSIBLE(atkParent)) {
+ // The accessibility hierarchy is already invalid, so the parent-children relationships
+ // in the AccessibilityObject tree are not there anymore, so we can't know the offset.
+ g_signal_emit_by_name(atkParent, "children-changed::remove", -1, wrapper);
+ }
+ }
+ }
+
+ webkitAccessibleDetach(WEBKIT_ACCESSIBLE(wrapper));
}
void AXObjectCache::attachWrapper(AccessibilityObject* obj)
@@ -45,6 +69,37 @@ void AXObjectCache::attachWrapper(AccessibilityObject* obj)
AtkObject* atkObj = ATK_OBJECT(webkitAccessibleNew(obj));
obj->setWrapper(atkObj);
g_object_unref(atkObj);
+
+ // If an object is being attached and we are not in the middle of a layout update, then
+ // we should report ATs by emitting the children-changed::add signal from the parent.
+ Document* document = obj->document();
+ if (!document || document->childNeedsStyleRecalc())
+ return;
+
+ // Don't emit the signal when the actual object being added is not going to be exposed.
+ if (obj->accessibilityIsIgnoredByDefault())
+ return;
+
+ // Don't emit the signal if the object being added is not -- or not yet -- rendered,
+ // which can occur in nested iframes. In these instances we don't want to ignore the
+ // child. But if an assistive technology is listening, AT-SPI2 will attempt to create
+ // and cache the state set for the child upon emission of the signal. If the object
+ // has not yet been rendered, this will result in a crash.
+ if (!obj->renderer())
+ return;
+
+ // Don't emit the signal for objects whose parents won't be exposed directly.
+ AccessibilityObject* coreParent = obj->parentObjectUnignored();
+ if (!coreParent || coreParent->accessibilityIsIgnoredByDefault())
+ return;
+
+ // Look for the right object to emit the signal from.
+ AtkObject* atkParent = coreParent->wrapper();
+ if (!atkParent)
+ return;
+
+ size_t index = coreParent->children(false).find(obj);
+ g_signal_emit_by_name(atkParent, "children-changed::add", index, atkObj);
}
static AccessibilityObject* getListObject(AccessibilityObject* object)
@@ -60,7 +115,7 @@ static AccessibilityObject* getListObject(AccessibilityObject* object)
// 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 = object->children();
+ const AccessibilityObject::AccessibilityChildrenVector& children = object->children();
if (!children.size())
return 0;
@@ -77,8 +132,8 @@ static void notifyChildrenSelectionChange(AccessibilityObject* object)
// focused object and its associated list object, as per previous
// calls to this function, in order to properly decide whether to
// emit some signals or not.
- DEFINE_STATIC_LOCAL(RefPtr<AccessibilityObject>, oldListObject, ());
- DEFINE_STATIC_LOCAL(RefPtr<AccessibilityObject>, oldFocusedObject, ());
+ static NeverDestroyed<RefPtr<AccessibilityObject>> oldListObject;
+ static NeverDestroyed<RefPtr<AccessibilityObject>> oldFocusedObject;
// Only list boxes and menu lists supported so far.
if (!object || !(object->isListBox() || object->isMenuList()))
@@ -86,25 +141,23 @@ static void notifyChildrenSelectionChange(AccessibilityObject* object)
// Only support HTML select elements so far (ARIA selectors not supported).
Node* node = object->node();
- if (!node || !node->hasTagName(HTMLNames::selectTag))
+ if (!is<HTMLSelectElement>(node))
return;
// Emit signal from the listbox's point of view first.
g_signal_emit_by_name(object->wrapper(), "selection-changed");
// Find the item where the selection change was triggered from.
- HTMLSelectElement* select = toHTMLSelectElement(node);
- if (!select)
- return;
- int changedItemIndex = select->activeSelectionStartListIndex();
+ HTMLSelectElement& select = downcast<HTMLSelectElement>(*node);
+ int changedItemIndex = select.activeSelectionStartListIndex();
AccessibilityObject* listObject = getListObject(object);
if (!listObject) {
- oldListObject = 0;
+ oldListObject.get() = nullptr;
return;
}
- AccessibilityObject::AccessibilityChildrenVector items = listObject->children();
+ const AccessibilityObject::AccessibilityChildrenVector& items = listObject->children();
if (changedItemIndex < 0 || changedItemIndex >= static_cast<int>(items.size()))
return;
AccessibilityObject* item = items.at(changedItemIndex).get();
@@ -112,29 +165,33 @@ static void notifyChildrenSelectionChange(AccessibilityObject* object)
// Ensure the current list object is the same than the old one so
// further comparisons make sense. Otherwise, just reset
// oldFocusedObject so it won't be taken into account.
- if (oldListObject != listObject)
- oldFocusedObject = 0;
+ if (oldListObject.get() != listObject)
+ oldFocusedObject.get() = nullptr;
- AtkObject* axItem = item ? item->wrapper() : 0;
- AtkObject* axOldFocusedObject = oldFocusedObject ? oldFocusedObject->wrapper() : 0;
+ AtkObject* axItem = item ? item->wrapper() : nullptr;
+ AtkObject* axOldFocusedObject = oldFocusedObject.get() ? oldFocusedObject.get()->wrapper() : nullptr;
// Old focused object just lost focus, so emit the events.
if (axOldFocusedObject && axItem != axOldFocusedObject) {
g_signal_emit_by_name(axOldFocusedObject, "focus-event", false);
- g_signal_emit_by_name(axOldFocusedObject, "state-change", "focused", false);
+ atk_object_notify_state_change(axOldFocusedObject, ATK_STATE_FOCUSED, false);
}
// Emit needed events for the currently (un)selected item.
if (axItem) {
bool isSelected = item->isSelected();
- g_signal_emit_by_name(axItem, "state-change", "selected", isSelected);
- g_signal_emit_by_name(axItem, "focus-event", isSelected);
- g_signal_emit_by_name(axItem, "state-change", "focused", isSelected);
+ atk_object_notify_state_change(axItem, ATK_STATE_SELECTED, isSelected);
+ // When the selection changes in a collapsed widget such as a combo box
+ // whose child menu is not showing, that collapsed widget retains focus.
+ if (!object->isCollapsed()) {
+ g_signal_emit_by_name(axItem, "focus-event", isSelected);
+ atk_object_notify_state_change(axItem, ATK_STATE_FOCUSED, isSelected);
+ }
}
// Update pointers to the previously involved objects.
- oldListObject = listObject;
- oldFocusedObject = item;
+ oldListObject.get() = listObject;
+ oldFocusedObject.get() = item;
}
void AXObjectCache::postPlatformNotification(AccessibilityObject* coreObject, AXNotification notification)
@@ -143,27 +200,47 @@ void AXObjectCache::postPlatformNotification(AccessibilityObject* coreObject, AX
if (!axObject)
return;
- if (notification == AXCheckedStateChanged) {
- if (!coreObject->isCheckboxOrRadio())
+ switch (notification) {
+ case AXCheckedStateChanged:
+ if (!coreObject->isCheckboxOrRadio() && !coreObject->isSwitch())
return;
- g_signal_emit_by_name(axObject, "state-change", "checked", coreObject->isChecked());
- } else if (notification == AXSelectedChildrenChanged || notification == AXMenuListValueChanged) {
- if (notification == AXMenuListValueChanged && coreObject->isMenuList()) {
+ atk_object_notify_state_change(axObject, ATK_STATE_CHECKED, coreObject->isChecked());
+ break;
+
+ case AXSelectedChildrenChanged:
+ case AXMenuListValueChanged:
+ // Accessible focus claims should not be made if the associated widget is not focused.
+ if (notification == AXMenuListValueChanged && coreObject->isMenuList() && coreObject->isFocused()) {
g_signal_emit_by_name(axObject, "focus-event", true);
- g_signal_emit_by_name(axObject, "state-change", "focused", true);
+ atk_object_notify_state_change(axObject, ATK_STATE_FOCUSED, true);
}
notifyChildrenSelectionChange(coreObject);
- } else if (notification == AXValueChanged) {
- if (!ATK_IS_VALUE(axObject))
- return;
+ break;
- AtkPropertyValues propertyValues;
- propertyValues.property_name = "accessible-value";
+ case AXValueChanged:
+ if (ATK_IS_VALUE(axObject)) {
+ AtkPropertyValues propertyValues;
+ propertyValues.property_name = "accessible-value";
+
+ memset(&propertyValues.new_value, 0, sizeof(GValue));
+#if ATK_CHECK_VERSION(2,11,92)
+ double value;
+ atk_value_get_value_and_text(ATK_VALUE(axObject), &value, nullptr);
+ g_value_set_double(g_value_init(&propertyValues.new_value, G_TYPE_DOUBLE), value);
+#else
+ atk_value_get_current_value(ATK_VALUE(axObject), &propertyValues.new_value);
+#endif
- memset(&propertyValues.new_value, 0, sizeof(GValue));
- atk_value_get_current_value(ATK_VALUE(axObject), &propertyValues.new_value);
+ g_signal_emit_by_name(ATK_OBJECT(axObject), "property-change::accessible-value", &propertyValues, NULL);
+ }
+ break;
+
+ case AXInvalidStatusChanged:
+ atk_object_notify_state_change(axObject, ATK_STATE_INVALID_ENTRY, coreObject->invalidStatus() != "false");
+ break;
- g_signal_emit_by_name(ATK_OBJECT(axObject), "property-change::accessible-value", &propertyValues, NULL);
+ default:
+ break;
}
}
@@ -172,7 +249,7 @@ void AXObjectCache::nodeTextChangePlatformNotification(AccessibilityObject* obje
if (!object || text.isEmpty())
return;
- AccessibilityObject* parentObject = object->parentObjectUnignored();
+ AccessibilityObject* parentObject = object->isNonNativeTextControl() ? object : object->parentObjectUnignored();
if (!parentObject)
return;
@@ -185,18 +262,21 @@ void AXObjectCache::nodeTextChangePlatformNotification(AccessibilityObject* obje
return;
// Ensure document's layout is up-to-date before using TextIterator.
- Document* document = node->document();
- document->updateLayout();
+ Document& document = node->document();
+ document.updateLayout();
// Select the right signal to be emitted
CString detail;
switch (textChange) {
- case AXObjectCache::AXTextInserted:
+ case AXTextInserted:
detail = "text-insert";
break;
- case AXObjectCache::AXTextDeleted:
+ case AXTextDeleted:
detail = "text-remove";
break;
+ case AXTextAttributesChanged:
+ detail = "text-attributes-changed";
+ break;
}
String textToEmit = text;
@@ -229,34 +309,34 @@ void AXObjectCache::frameLoadingEventPlatformNotification(AccessibilityObject* o
switch (loadingEvent) {
case AXObjectCache::AXLoadingStarted:
- g_signal_emit_by_name(axObject, "state-change", "busy", true);
+ atk_object_notify_state_change(axObject, ATK_STATE_BUSY, true);
break;
case AXObjectCache::AXLoadingReloaded:
- g_signal_emit_by_name(axObject, "state-change", "busy", true);
+ atk_object_notify_state_change(axObject, ATK_STATE_BUSY, true);
g_signal_emit_by_name(axObject, "reload");
break;
case AXObjectCache::AXLoadingFailed:
g_signal_emit_by_name(axObject, "load-stopped");
- g_signal_emit_by_name(axObject, "state-change", "busy", false);
+ atk_object_notify_state_change(axObject, ATK_STATE_BUSY, false);
break;
case AXObjectCache::AXLoadingFinished:
g_signal_emit_by_name(axObject, "load-complete");
- g_signal_emit_by_name(axObject, "state-change", "busy", false);
+ atk_object_notify_state_change(axObject, ATK_STATE_BUSY, false);
break;
}
}
-void AXObjectCache::handleFocusedUIElementChanged(Node* oldFocusedNode, Node* newFocusedNode)
+void AXObjectCache::platformHandleFocusedUIElementChanged(Node* oldFocusedNode, Node* newFocusedNode)
{
RefPtr<AccessibilityObject> oldObject = getOrCreate(oldFocusedNode);
if (oldObject) {
g_signal_emit_by_name(oldObject->wrapper(), "focus-event", false);
- g_signal_emit_by_name(oldObject->wrapper(), "state-change", "focused", false);
+ atk_object_notify_state_change(oldObject->wrapper(), ATK_STATE_FOCUSED, false);
}
RefPtr<AccessibilityObject> newObject = getOrCreate(newFocusedNode);
if (newObject) {
g_signal_emit_by_name(newObject->wrapper(), "focus-event", true);
- g_signal_emit_by_name(newObject->wrapper(), "state-change", "focused", true);
+ atk_object_notify_state_change(newObject->wrapper(), ATK_STATE_FOCUSED, true);
}
}
diff --git a/Source/WebCore/accessibility/atk/AccessibilityObjectAtk.cpp b/Source/WebCore/accessibility/atk/AccessibilityObjectAtk.cpp
index 8bac09c11..84668e2e2 100644
--- a/Source/WebCore/accessibility/atk/AccessibilityObjectAtk.cpp
+++ b/Source/WebCore/accessibility/atk/AccessibilityObjectAtk.cpp
@@ -21,8 +21,13 @@
#include "config.h"
#include "AccessibilityObject.h"
-#include "RenderObject.h"
+#include "HTMLSpanElement.h"
+#include "RenderBlock.h"
+#include "RenderInline.h"
+#include "RenderIterator.h"
+#include "RenderTableCell.h"
#include "RenderText.h"
+#include "TextControlInnerElements.h"
#include <glib-object.h>
#if HAVE(ACCESSIBILITY)
@@ -41,9 +46,6 @@ AccessibilityObjectInclusion AccessibilityObject::accessibilityPlatformIncludesO
return DefaultBehavior;
AccessibilityRole role = roleValue();
- if (role == HorizontalRuleRole)
- return IncludeObject;
-
// We expose the slider as a whole but not its value indicator.
if (role == SliderThumbRole)
return IgnoreObject;
@@ -58,7 +60,7 @@ AccessibilityObjectInclusion AccessibilityObject::accessibilityPlatformIncludesO
return IgnoreObject;
// Include all tables, even layout tables. The AT can decide what to do with each.
- if (role == CellRole || role == TableRole)
+ if (role == CellRole || role == TableRole || role == ColumnHeaderRole || role == RowHeaderRole)
return IncludeObject;
// The object containing the text should implement AtkText itself.
@@ -78,39 +80,72 @@ AccessibilityObjectInclusion AccessibilityObject::accessibilityPlatformIncludesO
if (role == UnknownRole)
return IgnoreObject;
- // Given a paragraph or div containing a non-nested anonymous block, WebCore
- // ignores the paragraph or div and includes the block. We want the opposite:
- // ATs are expecting accessible objects associated with textual elements. They
- // usually have no need for the anonymous block. And when the wrong objects
- // get included or ignored, needed accessibility signals do not get emitted.
- if (role == ParagraphRole || role == DivRole) {
- // Don't call textUnderElement() here, because it's slow and it can
- // crash when called while we're in the middle of a subtree being deleted.
- if (!renderer()->firstChild())
- return DefaultBehavior;
-
- if (!parent->renderer() || parent->renderer()->isAnonymousBlock())
- return DefaultBehavior;
-
- for (RenderObject* r = renderer()->firstChild(); r; r = r->nextSibling()) {
- if (r->isAnonymousBlock())
+ if (role == InlineRole)
+ return IncludeObject;
+
+ // Lines past this point only make sense for AccessibilityRenderObjects.
+ RenderObject* renderObject = renderer();
+ if (!renderObject)
+ return DefaultBehavior;
+
+ // We always want to include paragraphs that have rendered content.
+ // WebCore Accessibility does so unless there is a RenderBlock child.
+ if (role == ParagraphRole) {
+ auto child = childrenOfType<RenderBlock>(downcast<RenderElement>(*renderObject)).first();
+ return child ? IncludeObject : DefaultBehavior;
+ }
+
+ // We always want to include table cells (layout and CSS) that have rendered text content.
+ if (is<RenderTableCell>(renderObject)) {
+ for (const auto& child : childrenOfType<RenderObject>(downcast<RenderElement>(*renderObject))) {
+ if (is<RenderInline>(child) || is<RenderText>(child) || is<HTMLSpanElement>(child.node()))
return IncludeObject;
}
+ return DefaultBehavior;
+ }
+
+ if (renderObject->isAnonymousBlock()) {
+ // The text displayed by an ARIA menu item is exposed through the accessible name.
+ if (parent->isMenuItem())
+ return IgnoreObject;
+
+ // The text displayed in headings is typically exposed in the heading itself.
+ if (parent->isHeading())
+ return IgnoreObject;
+
+ // The text displayed in list items is typically exposed in the list item itself.
+ if (parent->isListItem())
+ return IgnoreObject;
+
+ // The text displayed in links is typically exposed in the link itself.
+ if (parent->isLink())
+ return IgnoreObject;
+
+ // FIXME: This next one needs some further consideration. But paragraphs are not
+ // typically huge (like divs). And ignoring anonymous block children of paragraphs
+ // will preserve existing behavior.
+ if (parent->roleValue() == ParagraphRole)
+ return IgnoreObject;
+
+ return DefaultBehavior;
}
- // Block spans result in objects of ATK_ROLE_PANEL which are almost always unwanted.
- // However, if we ignore block spans whose parent is the body, the child controls
- // will become immediate children of the ATK_ROLE_DOCUMENT_FRAME and any text will
- // become text within the document frame itself. This ultimately may be what we want
- // and would largely be consistent with what we see from Gecko. However, ignoring
- // spans whose parent is the body changes the current behavior we see from WebCore.
- // Until we have sufficient time to properly analyze these cases, we will defer to
- // WebCore. We only check that the parent is not aria because we do not expect
- // anonymous blocks which are aria-related to themselves have an aria role, nor
- // have we encountered instances where the parent of an anonymous block also lacked
- // an aria role but the grandparent had one.
- if (renderer() && renderer()->isAnonymousBlock() && !parent->renderer()->isBody()
- && parent->ariaRoleAttribute() == UnknownRole)
+ Node* node = renderObject->node();
+ if (!node)
+ return DefaultBehavior;
+
+ // We don't want <span> elements to show up in the accessibility hierarchy unless
+ // we have good reasons for that (e.g. focusable or visible because of containing
+ // a meaningful accessible name, maybe set through ARIA), so we can use
+ // atk_component_grab_focus() to set the focus to it.
+ if (is<HTMLSpanElement>(node) && !canSetFocusAttribute() && !hasAttributesRequiredForInclusion())
+ return IgnoreObject;
+
+ // If we include TextControlInnerTextElement children, changes to those children
+ // will result in focus and text notifications that suggest the user is no longer
+ // in the control. This can be especially problematic for screen reader users with
+ // key echo enabled when typing in a password input.
+ if (is<TextControlInnerTextElement>(node))
return IgnoreObject;
return DefaultBehavior;
@@ -143,7 +178,7 @@ bool AccessibilityObject::allowsTextRanges() const
// Check roles as the last fallback mechanism.
AccessibilityRole role = roleValue();
- return role == ParagraphRole || role == LabelRole || role == DivRole || role == FormRole;
+ return role == ParagraphRole || role == LabelRole || role == DivRole || role == FormRole || role == PreRole;
}
unsigned AccessibilityObject::getLengthForTextRange() const
@@ -155,16 +190,14 @@ unsigned AccessibilityObject::getLengthForTextRange() const
// Gtk ATs need this for all text objects; not just text controls.
Node* node = this->node();
- RenderObject* renderer = node ? node->renderer() : 0;
- if (renderer && renderer->isText()) {
- RenderText* renderText = toRenderText(renderer);
- textLength = renderText ? renderText->textLength() : 0;
- }
+ RenderObject* renderer = node ? node->renderer() : nullptr;
+ if (is<RenderText>(renderer))
+ textLength = downcast<RenderText>(*renderer).textLength();
// Get the text length from the elements under the
// accessibility object if the value is still zero.
if (!textLength && allowsTextRanges())
- textLength = textUnderElement(TextUnderElementModeIncludeAllChildren).length();
+ textLength = textUnderElement(AccessibilityTextUnderElementMode(AccessibilityTextUnderElementMode::TextUnderElementModeIncludeAllChildren)).length();
return textLength;
}
diff --git a/Source/WebCore/accessibility/atk/WebKitAccessibleHyperlink.cpp b/Source/WebCore/accessibility/atk/WebKitAccessibleHyperlink.cpp
index 8a4ff4227..9df03efa9 100644
--- a/Source/WebCore/accessibility/atk/WebKitAccessibleHyperlink.cpp
+++ b/Source/WebCore/accessibility/atk/WebKitAccessibleHyperlink.cpp
@@ -94,6 +94,7 @@ static gboolean webkitAccessibleHyperlinkActionDoAction(AtkAction* action, gint
g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE_HYPERLINK(action), FALSE);
g_return_val_if_fail(WEBKIT_ACCESSIBLE_HYPERLINK(action)->priv->hyperlinkImpl, FALSE);
g_return_val_if_fail(!index, FALSE);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE_HYPERLINK(action)->priv->hyperlinkImpl, FALSE);
if (!ATK_IS_ACTION(WEBKIT_ACCESSIBLE_HYPERLINK(action)->priv->hyperlinkImpl))
return FALSE;
@@ -109,6 +110,7 @@ static gint webkitAccessibleHyperlinkActionGetNActions(AtkAction* action)
{
g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE_HYPERLINK(action), 0);
g_return_val_if_fail(WEBKIT_ACCESSIBLE_HYPERLINK(action)->priv->hyperlinkImpl, 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE_HYPERLINK(action)->priv->hyperlinkImpl, 0);
if (!ATK_IS_ACTION(WEBKIT_ACCESSIBLE_HYPERLINK(action)->priv->hyperlinkImpl))
return 0;
@@ -121,6 +123,7 @@ static const gchar* webkitAccessibleHyperlinkActionGetDescription(AtkAction* act
g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE_HYPERLINK(action), 0);
g_return_val_if_fail(WEBKIT_ACCESSIBLE_HYPERLINK(action)->priv->hyperlinkImpl, 0);
g_return_val_if_fail(!index, 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE_HYPERLINK(action)->priv->hyperlinkImpl, 0);
// TODO: Need a way to provide/localize action descriptions.
notImplemented();
@@ -130,10 +133,11 @@ static const gchar* webkitAccessibleHyperlinkActionGetDescription(AtkAction* act
static const gchar* webkitAccessibleHyperlinkActionGetKeybinding(AtkAction* action, gint index)
{
g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE_HYPERLINK(action), 0);
+ g_return_val_if_fail(WEBKIT_ACCESSIBLE_HYPERLINK(action)->priv->hyperlinkImpl, 0);
g_return_val_if_fail(!index, 0);
WebKitAccessibleHyperlinkPrivate* priv = WEBKIT_ACCESSIBLE_HYPERLINK(action)->priv;
- g_return_val_if_fail(priv->hyperlinkImpl, 0);
+ returnValIfWebKitAccessibleIsInvalid(priv->hyperlinkImpl, 0);
if (!ATK_IS_ACTION(priv->hyperlinkImpl))
return 0;
@@ -149,10 +153,11 @@ static const gchar* webkitAccessibleHyperlinkActionGetKeybinding(AtkAction* acti
static const gchar* webkitAccessibleHyperlinkActionGetName(AtkAction* action, gint index)
{
g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE_HYPERLINK(action), 0);
+ g_return_val_if_fail(WEBKIT_ACCESSIBLE_HYPERLINK(action)->priv->hyperlinkImpl, 0);
g_return_val_if_fail(!index, 0);
WebKitAccessibleHyperlinkPrivate* priv = WEBKIT_ACCESSIBLE_HYPERLINK(action)->priv;
- g_return_val_if_fail(priv->hyperlinkImpl, 0);
+ returnValIfWebKitAccessibleIsInvalid(priv->hyperlinkImpl, 0);
if (!ATK_IS_ACTION(priv->hyperlinkImpl))
return 0;
@@ -177,10 +182,14 @@ static void atkActionInterfaceInit(AtkActionIface* iface)
static gchar* webkitAccessibleHyperlinkGetURI(AtkHyperlink* link, gint index)
{
g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE_HYPERLINK(link), 0);
+ g_return_val_if_fail(WEBKIT_ACCESSIBLE_HYPERLINK(link)->priv->hyperlinkImpl, 0);
+
// FIXME: Do NOT support more than one instance of an AtkObject
// implementing AtkHyperlinkImpl in every instance of AtkHyperLink
g_return_val_if_fail(!index, 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE_HYPERLINK(link)->priv->hyperlinkImpl, 0);
+
AccessibilityObject* coreObject = core(link);
if (!coreObject || coreObject->url().isNull())
return 0;
@@ -197,6 +206,8 @@ static AtkObject* webkitAccessibleHyperlinkGetObject(AtkHyperlink* link, gint in
// implementing AtkHyperlinkImpl in every instance of AtkHyperLink
g_return_val_if_fail(!index, 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE_HYPERLINK(link)->priv->hyperlinkImpl, 0);
+
return ATK_OBJECT(WEBKIT_ACCESSIBLE_HYPERLINK(link)->priv->hyperlinkImpl);
}
@@ -220,16 +231,18 @@ static gint getRangeLengthForObject(AccessibilityObject* obj, Range* range)
return baseLength;
RenderObject* renderer = markerObj->renderer();
- if (!renderer || !renderer->isListMarker())
+ if (!is<RenderListMarker>(renderer))
return baseLength;
- RenderListMarker* marker = toRenderListMarker(renderer);
- return baseLength + marker->text().length() + marker->suffix().length();
+ auto& marker = downcast<RenderListMarker>(*renderer);
+ return baseLength + marker.text().length() + marker.suffix().length();
}
static gint webkitAccessibleHyperlinkGetStartIndex(AtkHyperlink* link)
{
g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE_HYPERLINK(link), 0);
+ g_return_val_if_fail(WEBKIT_ACCESSIBLE_HYPERLINK(link)->priv->hyperlinkImpl, 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE_HYPERLINK(link)->priv->hyperlinkImpl, 0);
AccessibilityObject* coreObject = core(link);
if (!coreObject)
@@ -254,6 +267,8 @@ static gint webkitAccessibleHyperlinkGetStartIndex(AtkHyperlink* link)
static gint webkitAccessibleHyperlinkGetEndIndex(AtkHyperlink* link)
{
g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE_HYPERLINK(link), 0);
+ g_return_val_if_fail(WEBKIT_ACCESSIBLE_HYPERLINK(link)->priv->hyperlinkImpl, 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE_HYPERLINK(link)->priv->hyperlinkImpl, 0);
AccessibilityObject* coreObject = core(link);
if (!coreObject)
@@ -277,8 +292,9 @@ static gint webkitAccessibleHyperlinkGetEndIndex(AtkHyperlink* link)
static gboolean webkitAccessibleHyperlinkIsValid(AtkHyperlink* link)
{
- g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE_HYPERLINK(link), 0);
+ g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE_HYPERLINK(link), FALSE);
g_return_val_if_fail(WEBKIT_ACCESSIBLE_HYPERLINK(link)->priv->hyperlinkImpl, FALSE);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE_HYPERLINK(link)->priv->hyperlinkImpl, FALSE);
// Link is valid for the whole object's lifetime
return TRUE;
@@ -290,11 +306,17 @@ static gint webkitAccessibleHyperlinkGetNAnchors(AtkHyperlink* link)
// implementing AtkHyperlinkImpl in every instance of AtkHyperLink
g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE_HYPERLINK(link), 0);
g_return_val_if_fail(WEBKIT_ACCESSIBLE_HYPERLINK(link)->priv->hyperlinkImpl, 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE_HYPERLINK(link)->priv->hyperlinkImpl, 0);
+
return 1;
}
-static gboolean webkitAccessibleHyperlinkIsSelectedLink(AtkHyperlink*)
+static gboolean webkitAccessibleHyperlinkIsSelectedLink(AtkHyperlink* link)
{
+ g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE_HYPERLINK(link), FALSE);
+ g_return_val_if_fail(WEBKIT_ACCESSIBLE_HYPERLINK(link)->priv->hyperlinkImpl, FALSE);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE_HYPERLINK(link)->priv->hyperlinkImpl, FALSE);
+
// Not implemented: this function is deprecated in ATK now
notImplemented();
return FALSE;
@@ -363,7 +385,7 @@ static void webkitAccessibleHyperlinkClassInit(AtkHyperlinkClass* klass)
static void webkitAccessibleHyperlinkInit(AtkHyperlink* link)
{
WEBKIT_ACCESSIBLE_HYPERLINK(link)->priv = WEBKIT_ACCESSIBLE_HYPERLINK_GET_PRIVATE(link);
- WEBKIT_ACCESSIBLE_HYPERLINK(link)->priv->hyperlinkImpl = 0;
+ WEBKIT_ACCESSIBLE_HYPERLINK(link)->priv->hyperlinkImpl = nullptr;
}
GType webkitAccessibleHyperlinkGetType()
diff --git a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceAction.cpp b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceAction.cpp
index eb18beb6d..c79bdc4d6 100644
--- a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceAction.cpp
+++ b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceAction.cpp
@@ -51,17 +51,27 @@ static AccessibilityObject* core(AtkAction* action)
static gboolean webkitAccessibleActionDoAction(AtkAction* action, gint index)
{
+ g_return_val_if_fail(ATK_IS_ACTION(action), FALSE);
g_return_val_if_fail(!index, FALSE);
+
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(action), FALSE);
+
return core(action)->performDefaultAction();
}
-static gint webkitAccessibleActionGetNActions(AtkAction*)
+static gint webkitAccessibleActionGetNActions(AtkAction* action)
{
+ g_return_val_if_fail(ATK_IS_ACTION(action), 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(action), 0);
+
return 1;
}
-static const gchar* webkitAccessibleActionGetDescription(AtkAction*, gint)
+static const gchar* webkitAccessibleActionGetDescription(AtkAction* action, gint)
{
+ g_return_val_if_fail(ATK_IS_ACTION(action), 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(action), 0);
+
// TODO: Need a way to provide/localize action descriptions.
notImplemented();
return "";
@@ -69,14 +79,20 @@ static const gchar* webkitAccessibleActionGetDescription(AtkAction*, gint)
static const gchar* webkitAccessibleActionGetKeybinding(AtkAction* action, gint index)
{
+ g_return_val_if_fail(ATK_IS_ACTION(action), 0);
g_return_val_if_fail(!index, 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(action), 0);
+
// FIXME: Construct a proper keybinding string.
return cacheAndReturnAtkProperty(ATK_OBJECT(action), AtkCachedActionKeyBinding, core(action)->accessKey().string());
}
static const gchar* webkitAccessibleActionGetName(AtkAction* action, gint index)
{
+ g_return_val_if_fail(ATK_IS_ACTION(action), 0);
g_return_val_if_fail(!index, 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(action), 0);
+
return cacheAndReturnAtkProperty(ATK_OBJECT(action), AtkCachedActionName, core(action)->actionVerb());
}
diff --git a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceComponent.cpp b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceComponent.cpp
index 7103ea686..e86f23098 100644
--- a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceComponent.cpp
+++ b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceComponent.cpp
@@ -68,6 +68,9 @@ static IntPoint atkToContents(AccessibilityObject* coreObject, AtkCoordType coor
static AtkObject* webkitAccessibleComponentRefAccessibleAtPoint(AtkComponent* component, gint x, gint y, AtkCoordType coordType)
{
+ g_return_val_if_fail(ATK_IS_COMPONENT(component), 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(component), 0);
+
IntPoint pos = atkToContents(core(component), coordType, x, y);
AccessibilityObject* target = core(component)->accessibilityHitTest(pos);
@@ -79,12 +82,18 @@ static AtkObject* webkitAccessibleComponentRefAccessibleAtPoint(AtkComponent* co
static void webkitAccessibleComponentGetExtents(AtkComponent* component, gint* x, gint* y, gint* width, gint* height, AtkCoordType coordType)
{
- IntRect rect = pixelSnappedIntRect(core(component)->elementRect());
+ g_return_if_fail(ATK_IS_COMPONENT(component));
+ returnIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(component));
+
+ IntRect rect = snappedIntRect(core(component)->elementRect());
contentsRelativeToAtkCoordinateType(core(component), coordType, rect, x, y, width, height);
}
static gboolean webkitAccessibleComponentGrabFocus(AtkComponent* component)
{
+ g_return_val_if_fail(ATK_IS_COMPONENT(component), FALSE);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(component), FALSE);
+
core(component)->setFocused(true);
return core(component)->isFocused();
}
diff --git a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceDocument.cpp b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceDocument.cpp
index f07484318..6752d12d3 100644
--- a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceDocument.cpp
+++ b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceDocument.cpp
@@ -78,12 +78,18 @@ static const gchar* documentAttributeValue(AtkDocument* document, const gchar* a
static const gchar* webkitAccessibleDocumentGetAttributeValue(AtkDocument* document, const gchar* attribute)
{
+ g_return_val_if_fail(ATK_IS_DOCUMENT(document), 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(document), 0);
+
return documentAttributeValue(document, attribute);
}
static AtkAttributeSet* webkitAccessibleDocumentGetAttributes(AtkDocument* document)
{
- AtkAttributeSet* attributeSet = 0;
+ g_return_val_if_fail(ATK_IS_DOCUMENT(document), 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(document), 0);
+
+ AtkAttributeSet* attributeSet = nullptr;
const gchar* attributes[] = { "DocType", "Encoding", "URI" };
for (unsigned i = 0; i < G_N_ELEMENTS(attributes); i++) {
@@ -97,6 +103,9 @@ static AtkAttributeSet* webkitAccessibleDocumentGetAttributes(AtkDocument* docum
static const gchar* webkitAccessibleDocumentGetLocale(AtkDocument* document)
{
+ g_return_val_if_fail(ATK_IS_DOCUMENT(document), 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(document), 0);
+
// The logic to resolve locale has been moved to
// AtkObject::get_object_locale() virtual method. However, to avoid breaking
// clients expecting the deprecated AtkDocumentIface::get_document_locale()
diff --git a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceEditableText.cpp b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceEditableText.cpp
index a33c59360..a12e96a8f 100644
--- a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceEditableText.cpp
+++ b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceEditableText.cpp
@@ -38,6 +38,7 @@
#include "Editor.h"
#include "Frame.h"
#include "NotImplemented.h"
+#include "WebKitAccessibleUtil.h"
#include "WebKitAccessibleWrapperAtk.h"
using namespace WebCore;
@@ -50,20 +51,29 @@ static AccessibilityObject* core(AtkEditableText* text)
return webkitAccessibleGetAccessibilityObject(WEBKIT_ACCESSIBLE(text));
}
-static gboolean webkitAccessibleEditableTextSetRunAttributes(AtkEditableText*, AtkAttributeSet*, gint, gint)
+static gboolean webkitAccessibleEditableTextSetRunAttributes(AtkEditableText* text, AtkAttributeSet*, gint, gint)
{
+ g_return_val_if_fail(ATK_IS_EDITABLE_TEXT(text), FALSE);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), FALSE);
+
notImplemented();
return FALSE;
}
static void webkitAccessibleEditableTextSetTextContents(AtkEditableText* text, const gchar* string)
{
+ g_return_if_fail(ATK_IS_EDITABLE_TEXT(text));
+ returnIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text));
+
// FIXME: string nullcheck?
core(text)->setValue(String::fromUTF8(string));
}
static void webkitAccessibleEditableTextInsertText(AtkEditableText* text, const gchar* string, gint length, gint* position)
{
+ g_return_if_fail(ATK_IS_EDITABLE_TEXT(text));
+ returnIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text));
+
if (!string)
return;
@@ -83,18 +93,27 @@ static void webkitAccessibleEditableTextInsertText(AtkEditableText* text, const
*position += length;
}
-static void webkitAccessibleEditableTextCopyText(AtkEditableText*, gint, gint)
+static void webkitAccessibleEditableTextCopyText(AtkEditableText* text, gint, gint)
{
+ g_return_if_fail(ATK_IS_EDITABLE_TEXT(text));
+ returnIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text));
+
notImplemented();
}
-static void webkitAccessibleEditableTextCutText(AtkEditableText*, gint, gint)
+static void webkitAccessibleEditableTextCutText(AtkEditableText* text, gint, gint)
{
+ g_return_if_fail(ATK_IS_EDITABLE_TEXT(text));
+ returnIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text));
+
notImplemented();
}
static void webkitAccessibleEditableTextDeleteText(AtkEditableText* text, gint startPos, gint endPos)
{
+ g_return_if_fail(ATK_IS_EDITABLE_TEXT(text));
+ returnIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text));
+
AccessibilityObject* coreObject = core(text);
// FIXME: Not implemented in WebCore
// coreObject->setSelectedTextRange(PlainTextRange(startPos, endPos - startPos));
@@ -109,8 +128,11 @@ static void webkitAccessibleEditableTextDeleteText(AtkEditableText* text, gint s
document->frame()->editor().performDelete();
}
-static void webkitAccessibleEditableTextPasteText(AtkEditableText*, gint)
+static void webkitAccessibleEditableTextPasteText(AtkEditableText* text, gint)
{
+ g_return_if_fail(ATK_IS_EDITABLE_TEXT(text));
+ returnIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text));
+
notImplemented();
}
diff --git a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceHypertext.cpp b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceHypertext.cpp
index 0e338d8c8..db60218df 100644
--- a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceHypertext.cpp
+++ b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceHypertext.cpp
@@ -23,6 +23,7 @@
#if HAVE(ACCESSIBILITY)
#include "AccessibilityObject.h"
+#include "WebKitAccessibleUtil.h"
#include "WebKitAccessibleWrapperAtk.h"
using namespace WebCore;
@@ -37,13 +38,16 @@ static AccessibilityObject* core(AtkHypertext* hypertext)
static AtkHyperlink* webkitAccessibleHypertextGetLink(AtkHypertext* hypertext, gint index)
{
- AccessibilityObject::AccessibilityChildrenVector children = core(hypertext)->children();
+ g_return_val_if_fail(ATK_HYPERTEXT(hypertext), 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(hypertext), 0);
+
+ const AccessibilityObject::AccessibilityChildrenVector& children = core(hypertext)->children();
if (index < 0 || static_cast<unsigned>(index) >= children.size())
return 0;
gint currentLink = -1;
- for (unsigned i = 0; i < children.size(); i++) {
- AccessibilityObject* coreChild = children.at(i).get();
+ for (const auto& child : children) {
+ AccessibilityObject* coreChild = child.get();
if (!coreChild->accessibilityIsIgnored()) {
AtkObject* axObject = coreChild->wrapper();
if (!axObject || !ATK_IS_HYPERLINK_IMPL(axObject))
@@ -62,13 +66,13 @@ static AtkHyperlink* webkitAccessibleHypertextGetLink(AtkHypertext* hypertext, g
static gint webkitAccessibleHypertextGetNLinks(AtkHypertext* hypertext)
{
- AccessibilityObject::AccessibilityChildrenVector children = core(hypertext)->children();
- if (!children.size())
- return 0;
+ g_return_val_if_fail(ATK_HYPERTEXT(hypertext), 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(hypertext), 0);
+ const AccessibilityObject::AccessibilityChildrenVector& children = core(hypertext)->children();
gint linksFound = 0;
- for (size_t i = 0; i < children.size(); i++) {
- AccessibilityObject* coreChild = children.at(i).get();
+ for (const auto& child : children) {
+ AccessibilityObject* coreChild = child.get();
if (!coreChild->accessibilityIsIgnored()) {
AtkObject* axObject = coreChild->wrapper();
if (axObject && ATK_IS_HYPERLINK_IMPL(axObject))
@@ -81,6 +85,9 @@ static gint webkitAccessibleHypertextGetNLinks(AtkHypertext* hypertext)
static gint webkitAccessibleHypertextGetLinkIndex(AtkHypertext* hypertext, gint charIndex)
{
+ g_return_val_if_fail(ATK_HYPERTEXT(hypertext), -1);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(hypertext), -1);
+
size_t linksCount = webkitAccessibleHypertextGetNLinks(hypertext);
if (!linksCount)
return -1;
diff --git a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceImage.cpp b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceImage.cpp
index 6d7df1279..843969b42 100644
--- a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceImage.cpp
+++ b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceImage.cpp
@@ -51,18 +51,27 @@ static AccessibilityObject* core(AtkImage* image)
static void webkitAccessibleImageGetImagePosition(AtkImage* image, gint* x, gint* y, AtkCoordType coordType)
{
- IntRect rect = pixelSnappedIntRect(core(image)->elementRect());
+ g_return_if_fail(ATK_IMAGE(image));
+ returnIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(image));
+
+ IntRect rect = snappedIntRect(core(image)->elementRect());
contentsRelativeToAtkCoordinateType(core(image), coordType, rect, x, y);
}
static const gchar* webkitAccessibleImageGetImageDescription(AtkImage* image)
{
+ g_return_val_if_fail(ATK_IMAGE(image), 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(image), 0);
+
return cacheAndReturnAtkProperty(ATK_OBJECT(image), AtkCachedImageDescription, accessibilityDescription(core(image)));
}
static void webkitAccessibleImageGetImageSize(AtkImage* image, gint* width, gint* height)
{
- IntSize size = core(image)->pixelSnappedSize();
+ g_return_if_fail(ATK_IMAGE(image));
+ returnIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(image));
+
+ IntSize size = snappedIntRect(core(image)->elementRect()).size();
if (width)
*width = size.width();
diff --git a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceSelection.cpp b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceSelection.cpp
index 7e024b02c..0f79b0085 100644
--- a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceSelection.cpp
+++ b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceSelection.cpp
@@ -37,6 +37,7 @@
#include "AccessibilityObject.h"
#include "HTMLSelectElement.h"
#include "RenderObject.h"
+#include "WebKitAccessibleUtil.h"
#include "WebKitAccessibleWrapperAtk.h"
using namespace WebCore;
@@ -44,7 +45,7 @@ using namespace WebCore;
static AccessibilityObject* core(AtkSelection* selection)
{
if (!WEBKIT_IS_ACCESSIBLE(selection))
- return 0;
+ return nullptr;
return webkitAccessibleGetAccessibilityObject(WEBKIT_ACCESSIBLE(selection));
}
@@ -55,7 +56,7 @@ static AccessibilityObject* listObjectForSelection(AtkSelection* selection)
// Only list boxes and menu lists supported so far.
if (!coreSelection->isListBox() && !coreSelection->isMenuList())
- return 0;
+ return nullptr;
// For list boxes the list object is just itself.
if (coreSelection->isListBox())
@@ -64,13 +65,13 @@ static AccessibilityObject* listObjectForSelection(AtkSelection* selection)
// 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();
+ const AccessibilityObject::AccessibilityChildrenVector& children = coreSelection->children();
if (!children.size())
- return 0;
+ return nullptr;
AccessibilityObject* listObject = children.at(0).get();
if (!listObject->isMenuListPopup())
- return 0;
+ return nullptr;
return listObject;
}
@@ -79,18 +80,18 @@ static AccessibilityObject* optionFromList(AtkSelection* selection, gint index)
{
AccessibilityObject* coreSelection = core(selection);
if (!coreSelection || index < 0)
- return 0;
+ return nullptr;
// Need to select the proper list object depending on the type.
AccessibilityObject* listObject = listObjectForSelection(selection);
if (!listObject)
- return 0;
+ return nullptr;
- AccessibilityObject::AccessibilityChildrenVector options = listObject->children();
+ const AccessibilityObject::AccessibilityChildrenVector& options = listObject->children();
if (index < static_cast<gint>(options.size()))
return options.at(index).get();
- return 0;
+ return nullptr;
}
static AccessibilityObject* optionFromSelection(AtkSelection* selection, gint index)
@@ -99,34 +100,33 @@ static AccessibilityObject* optionFromSelection(AtkSelection* selection, gint in
AccessibilityObject* coreSelection = core(selection);
if (!coreSelection || !coreSelection->isAccessibilityRenderObject() || index < 0)
- return 0;
+ return nullptr;
- AccessibilityObject::AccessibilityChildrenVector selectedItems;
- if (coreSelection->isListBox())
- coreSelection->selectedChildren(selectedItems);
- else if (coreSelection->isMenuList()) {
+ int selectedIndex = index;
+ if (coreSelection->isMenuList()) {
RenderObject* renderer = coreSelection->renderer();
if (!renderer)
- return 0;
+ return nullptr;
- HTMLSelectElement* selectNode = toHTMLSelectElement(renderer->node());
- int selectedIndex = selectNode->selectedIndex();
- const Vector<HTMLElement*> listItems = selectNode->listItems();
+ HTMLSelectElement* selectNode = downcast<HTMLSelectElement>(renderer->node());
+ if (!selectNode)
+ return nullptr;
- if (selectedIndex < 0 || selectedIndex >= static_cast<int>(listItems.size()))
- return 0;
+ selectedIndex = selectNode->selectedIndex();
+ const auto& listItems = selectNode->listItems();
- return optionFromList(selection, selectedIndex);
+ if (selectedIndex < 0 || selectedIndex >= static_cast<int>(listItems.size()))
+ return nullptr;
}
- if (index < static_cast<gint>(selectedItems.size()))
- return selectedItems.at(index).get();
-
- return 0;
+ return optionFromList(selection, selectedIndex);
}
static gboolean webkitAccessibleSelectionAddSelection(AtkSelection* selection, gint index)
{
+ g_return_val_if_fail(ATK_SELECTION(selection), FALSE);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(selection), FALSE);
+
AccessibilityObject* coreSelection = core(selection);
if (!coreSelection)
return FALSE;
@@ -142,23 +142,29 @@ static gboolean webkitAccessibleSelectionAddSelection(AtkSelection* selection, g
static gboolean webkitAccessibleSelectionClearSelection(AtkSelection* selection)
{
+ g_return_val_if_fail(ATK_SELECTION(selection), FALSE);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(selection), FALSE);
+
AccessibilityObject* coreSelection = core(selection);
if (!coreSelection)
return FALSE;
AccessibilityObject::AccessibilityChildrenVector selectedItems;
- if (coreSelection->isListBox() || coreSelection->isMenuList()) {
+ if (is<AccessibilityListBox>(*coreSelection)) {
// Set the list of selected items to an empty list; then verify that it worked.
- AccessibilityListBox* listBox = static_cast<AccessibilityListBox*>(coreSelection);
- listBox->setSelectedChildren(selectedItems);
- listBox->selectedChildren(selectedItems);
- return !selectedItems.size();
+ auto& listBox = downcast<AccessibilityListBox>(*coreSelection);
+ listBox.setSelectedChildren(selectedItems);
+ listBox.selectedChildren(selectedItems);
+ return selectedItems.isEmpty();
}
return FALSE;
}
static AtkObject* webkitAccessibleSelectionRefSelection(AtkSelection* selection, gint index)
{
+ g_return_val_if_fail(ATK_SELECTION(selection), nullptr);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(selection), nullptr);
+
AccessibilityObject* option = optionFromSelection(selection, index);
if (option) {
AtkObject* child = option->wrapper();
@@ -166,11 +172,14 @@ static AtkObject* webkitAccessibleSelectionRefSelection(AtkSelection* selection,
return child;
}
- return 0;
+ return nullptr;
}
static gint webkitAccessibleSelectionGetSelectionCount(AtkSelection* selection)
{
+ g_return_val_if_fail(ATK_SELECTION(selection), 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(selection), 0);
+
AccessibilityObject* coreSelection = core(selection);
if (!coreSelection || !coreSelection->isAccessibilityRenderObject())
return 0;
@@ -186,8 +195,8 @@ static gint webkitAccessibleSelectionGetSelectionCount(AtkSelection* selection)
if (!renderer)
return 0;
- int selectedIndex = toHTMLSelectElement(renderer->node())->selectedIndex();
- return selectedIndex >= 0 && selectedIndex < static_cast<int>(toHTMLSelectElement(renderer->node())->listItems().size());
+ int selectedIndex = downcast<HTMLSelectElement>(renderer->node())->selectedIndex();
+ return selectedIndex >= 0 && selectedIndex < static_cast<int>(downcast<HTMLSelectElement>(renderer->node())->listItems().size());
}
return 0;
@@ -195,9 +204,12 @@ static gint webkitAccessibleSelectionGetSelectionCount(AtkSelection* selection)
static gboolean webkitAccessibleSelectionIsChildSelected(AtkSelection* selection, gint index)
{
+ g_return_val_if_fail(ATK_SELECTION(selection), FALSE);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(selection), FALSE);
+
AccessibilityObject* coreSelection = core(selection);
if (!coreSelection)
- return 0;
+ return FALSE;
AccessibilityObject* option = optionFromList(selection, index);
if (option && (coreSelection->isListBox() || coreSelection->isMenuList()))
@@ -208,9 +220,12 @@ static gboolean webkitAccessibleSelectionIsChildSelected(AtkSelection* selection
static gboolean webkitAccessibleSelectionRemoveSelection(AtkSelection* selection, gint index)
{
+ g_return_val_if_fail(ATK_SELECTION(selection), FALSE);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(selection), FALSE);
+
AccessibilityObject* coreSelection = core(selection);
if (!coreSelection)
- return 0;
+ return FALSE;
// TODO: This is only getting called if i == 0. What is preventing the rest?
AccessibilityObject* option = optionFromSelection(selection, index);
@@ -224,16 +239,19 @@ static gboolean webkitAccessibleSelectionRemoveSelection(AtkSelection* selection
static gboolean webkitAccessibleSelectionSelectAllSelection(AtkSelection* selection)
{
+ g_return_val_if_fail(ATK_SELECTION(selection), FALSE);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(selection), FALSE);
+
AccessibilityObject* coreSelection = core(selection);
if (!coreSelection || !coreSelection->isMultiSelectable())
return FALSE;
- AccessibilityObject::AccessibilityChildrenVector children = coreSelection->children();
- if (coreSelection->isListBox()) {
- AccessibilityListBox* listBox = static_cast<AccessibilityListBox*>(coreSelection);
- listBox->setSelectedChildren(children);
+ if (is<AccessibilityListBox>(*coreSelection)) {
+ const AccessibilityObject::AccessibilityChildrenVector& children = coreSelection->children();
+ AccessibilityListBox& listBox = downcast<AccessibilityListBox>(*coreSelection);
+ listBox.setSelectedChildren(children);
AccessibilityObject::AccessibilityChildrenVector selectedItems;
- listBox->selectedChildren(selectedItems);
+ listBox.selectedChildren(selectedItems);
return selectedItems.size() == children.size();
}
diff --git a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceTable.cpp b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceTable.cpp
index 668474575..d089a54bb 100644
--- a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceTable.cpp
+++ b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceTable.cpp
@@ -40,8 +40,9 @@
#include "HTMLSelectElement.h"
#include "HTMLTableCaptionElement.h"
#include "HTMLTableElement.h"
-#include "RenderObject.h"
+#include "RenderElement.h"
#include "WebKitAccessibleInterfaceText.h"
+#include "WebKitAccessibleUtil.h"
#include "WebKitAccessibleWrapperAtk.h"
using namespace WebCore;
@@ -49,7 +50,7 @@ using namespace WebCore;
static AccessibilityObject* core(AtkTable* table)
{
if (!WEBKIT_IS_ACCESSIBLE(table))
- return 0;
+ return nullptr;
return webkitAccessibleGetAccessibilityObject(WEBKIT_ACCESSIBLE(table));
}
@@ -57,9 +58,9 @@ static AccessibilityObject* core(AtkTable* table)
static AccessibilityTableCell* cell(AtkTable* table, guint row, guint column)
{
AccessibilityObject* accTable = core(table);
- if (accTable->isAccessibilityRenderObject())
- return static_cast<AccessibilityTable*>(accTable)->cellForColumnAndRow(column, row);
- return 0;
+ if (is<AccessibilityTable>(*accTable))
+ return downcast<AccessibilityTable>(*accTable).cellForColumnAndRow(column, row);
+ return nullptr;
}
static gint cellIndex(AccessibilityTableCell* axCell, AccessibilityTable* axTable)
@@ -78,19 +79,20 @@ static gint cellIndex(AccessibilityTableCell* axCell, AccessibilityTable* axTabl
static AccessibilityTableCell* cellAtIndex(AtkTable* table, gint index)
{
AccessibilityObject* accTable = core(table);
- if (accTable->isAccessibilityRenderObject()) {
+ if (is<AccessibilityTable>(*accTable)) {
AccessibilityObject::AccessibilityChildrenVector allCells;
- static_cast<AccessibilityTable*>(accTable)->cells(allCells);
- if (0 <= index && static_cast<unsigned>(index) < allCells.size()) {
- AccessibilityObject* accCell = allCells.at(index).get();
- return static_cast<AccessibilityTableCell*>(accCell);
- }
+ downcast<AccessibilityTable>(*accTable).cells(allCells);
+ if (0 <= index && static_cast<unsigned>(index) < allCells.size())
+ return downcast<AccessibilityTableCell>(allCells[index].get());
}
- return 0;
+ return nullptr;
}
static AtkObject* webkitAccessibleTableRefAt(AtkTable* table, gint row, gint column)
{
+ g_return_val_if_fail(ATK_TABLE(table), 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), 0);
+
AccessibilityTableCell* axCell = cell(table, row, column);
if (!axCell)
return 0;
@@ -106,16 +108,22 @@ static AtkObject* webkitAccessibleTableRefAt(AtkTable* table, gint row, gint col
static gint webkitAccessibleTableGetIndexAt(AtkTable* table, gint row, gint column)
{
+ g_return_val_if_fail(ATK_TABLE(table), -1);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), -1);
+
AccessibilityTableCell* axCell = cell(table, row, column);
- AccessibilityTable* axTable = static_cast<AccessibilityTable*>(core(table));
+ AccessibilityTable* axTable = downcast<AccessibilityTable>(core(table));
return cellIndex(axCell, axTable);
}
static gint webkitAccessibleTableGetColumnAtIndex(AtkTable* table, gint index)
{
+ g_return_val_if_fail(ATK_TABLE(table), -1);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), -1);
+
AccessibilityTableCell* axCell = cellAtIndex(table, index);
if (axCell) {
- pair<unsigned, unsigned> columnRange;
+ std::pair<unsigned, unsigned> columnRange;
axCell->columnIndexRange(columnRange);
return columnRange.first;
}
@@ -124,9 +132,12 @@ static gint webkitAccessibleTableGetColumnAtIndex(AtkTable* table, gint index)
static gint webkitAccessibleTableGetRowAtIndex(AtkTable* table, gint index)
{
+ g_return_val_if_fail(ATK_TABLE(table), -1);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), -1);
+
AccessibilityTableCell* axCell = cellAtIndex(table, index);
if (axCell) {
- pair<unsigned, unsigned> rowRange;
+ std::pair<unsigned, unsigned> rowRange;
axCell->rowIndexRange(rowRange);
return rowRange.first;
}
@@ -135,25 +146,34 @@ static gint webkitAccessibleTableGetRowAtIndex(AtkTable* table, gint index)
static gint webkitAccessibleTableGetNColumns(AtkTable* table)
{
+ g_return_val_if_fail(ATK_TABLE(table), 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), 0);
+
AccessibilityObject* accTable = core(table);
- if (accTable->isAccessibilityRenderObject())
- return static_cast<AccessibilityTable*>(accTable)->columnCount();
+ if (is<AccessibilityTable>(*accTable))
+ return downcast<AccessibilityTable>(*accTable).columnCount();
return 0;
}
static gint webkitAccessibleTableGetNRows(AtkTable* table)
{
+ g_return_val_if_fail(ATK_TABLE(table), 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), 0);
+
AccessibilityObject* accTable = core(table);
- if (accTable->isAccessibilityRenderObject())
- return static_cast<AccessibilityTable*>(accTable)->rowCount();
+ if (is<AccessibilityTable>(*accTable))
+ return downcast<AccessibilityTable>(*accTable).rowCount();
return 0;
}
static gint webkitAccessibleTableGetColumnExtentAt(AtkTable* table, gint row, gint column)
{
+ g_return_val_if_fail(ATK_TABLE(table), 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), 0);
+
AccessibilityTableCell* axCell = cell(table, row, column);
if (axCell) {
- pair<unsigned, unsigned> columnRange;
+ std::pair<unsigned, unsigned> columnRange;
axCell->columnIndexRange(columnRange);
return columnRange.second;
}
@@ -162,9 +182,12 @@ static gint webkitAccessibleTableGetColumnExtentAt(AtkTable* table, gint row, gi
static gint webkitAccessibleTableGetRowExtentAt(AtkTable* table, gint row, gint column)
{
+ g_return_val_if_fail(ATK_TABLE(table), 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), 0);
+
AccessibilityTableCell* axCell = cell(table, row, column);
if (axCell) {
- pair<unsigned, unsigned> rowRange;
+ std::pair<unsigned, unsigned> rowRange;
axCell->rowIndexRange(rowRange);
return rowRange.second;
}
@@ -173,56 +196,66 @@ static gint webkitAccessibleTableGetRowExtentAt(AtkTable* table, gint row, gint
static AtkObject* webkitAccessibleTableGetColumnHeader(AtkTable* table, gint column)
{
+ g_return_val_if_fail(ATK_TABLE(table), 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), 0);
+
AccessibilityObject* accTable = core(table);
- if (accTable->isAccessibilityRenderObject()) {
- AccessibilityObject::AccessibilityChildrenVector allColumnHeaders;
- static_cast<AccessibilityTable*>(accTable)->columnHeaders(allColumnHeaders);
- unsigned columnCount = allColumnHeaders.size();
- for (unsigned k = 0; k < columnCount; ++k) {
- pair<unsigned, unsigned> columnRange;
- AccessibilityTableCell* cell = static_cast<AccessibilityTableCell*>(allColumnHeaders.at(k).get());
- cell->columnIndexRange(columnRange);
+ if (is<AccessibilityTable>(*accTable)) {
+ AccessibilityObject::AccessibilityChildrenVector columnHeaders;
+ downcast<AccessibilityTable>(*accTable).columnHeaders(columnHeaders);
+
+ for (const auto& columnHeader : columnHeaders) {
+ std::pair<unsigned, unsigned> columnRange;
+ downcast<AccessibilityTableCell>(*columnHeader).columnIndexRange(columnRange);
if (columnRange.first <= static_cast<unsigned>(column) && static_cast<unsigned>(column) < columnRange.first + columnRange.second)
- return allColumnHeaders[k]->wrapper();
+ return columnHeader->wrapper();
}
}
- return 0;
+ return nullptr;
}
static AtkObject* webkitAccessibleTableGetRowHeader(AtkTable* table, gint row)
{
+ g_return_val_if_fail(ATK_TABLE(table), 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), 0);
+
AccessibilityObject* accTable = core(table);
- if (accTable->isAccessibilityRenderObject()) {
- AccessibilityObject::AccessibilityChildrenVector allRowHeaders;
- static_cast<AccessibilityTable*>(accTable)->rowHeaders(allRowHeaders);
- unsigned rowCount = allRowHeaders.size();
- for (unsigned k = 0; k < rowCount; ++k) {
- pair<unsigned, unsigned> rowRange;
- AccessibilityTableCell* cell = static_cast<AccessibilityTableCell*>(allRowHeaders.at(k).get());
- cell->rowIndexRange(rowRange);
+ if (is<AccessibilityTable>(*accTable)) {
+ AccessibilityObject::AccessibilityChildrenVector rowHeaders;
+ downcast<AccessibilityTable>(*accTable).rowHeaders(rowHeaders);
+
+ for (const auto& rowHeader : rowHeaders) {
+ std::pair<unsigned, unsigned> rowRange;
+ downcast<AccessibilityTableCell>(*rowHeader).rowIndexRange(rowRange);
if (rowRange.first <= static_cast<unsigned>(row) && static_cast<unsigned>(row) < rowRange.first + rowRange.second)
- return allRowHeaders[k]->wrapper();
+ return rowHeader->wrapper();
}
}
- return 0;
+ return nullptr;
}
static AtkObject* webkitAccessibleTableGetCaption(AtkTable* table)
{
+ g_return_val_if_fail(ATK_TABLE(table), nullptr);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), nullptr);
+
AccessibilityObject* accTable = core(table);
if (accTable->isAccessibilityRenderObject()) {
Node* node = accTable->node();
- if (node && isHTMLTableElement(node)) {
- HTMLTableCaptionElement* caption = toHTMLTableElement(node)->caption();
+ if (is<HTMLTableElement>(node)) {
+ HTMLTableCaptionElement* caption = downcast<HTMLTableElement>(*node).caption();
if (caption)
- return AccessibilityObject::firstAccessibleObjectFromNode(caption->renderer()->node())->wrapper();
+ return AccessibilityObject::firstAccessibleObjectFromNode(caption->renderer()->element())->wrapper();
}
}
- return 0;
+ return nullptr;
}
static const gchar* webkitAccessibleTableGetColumnDescription(AtkTable* table, gint column)
{
+ g_return_val_if_fail(ATK_TABLE(table), 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), 0);
+
AtkObject* columnHeader = atk_table_get_column_header(table, column);
if (columnHeader && ATK_IS_TEXT(columnHeader))
return atk_text_get_text(ATK_TEXT(columnHeader), 0, -1);
@@ -232,6 +265,9 @@ static const gchar* webkitAccessibleTableGetColumnDescription(AtkTable* table, g
static const gchar* webkitAccessibleTableGetRowDescription(AtkTable* table, gint row)
{
+ g_return_val_if_fail(ATK_TABLE(table), 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), 0);
+
AtkObject* rowHeader = atk_table_get_row_header(table, row);
if (rowHeader && ATK_IS_TEXT(rowHeader))
return atk_text_get_text(ATK_TEXT(rowHeader), 0, -1);
diff --git a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceTableCell.cpp b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceTableCell.cpp
new file mode 100644
index 000000000..3d1e96211
--- /dev/null
+++ b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceTableCell.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2014 Samsung Electronics. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "WebKitAccessibleInterfaceTableCell.h"
+
+#if HAVE(ACCESSIBILITY)
+#if ATK_CHECK_VERSION(2,11,90)
+#include "AccessibilityObject.h"
+#include "AccessibilityTable.h"
+#include "AccessibilityTableCell.h"
+#include "WebKitAccessibleUtil.h"
+#include "WebKitAccessibleWrapperAtk.h"
+
+using namespace WebCore;
+
+static GPtrArray* convertToGPtrArray(const AccessibilityObject::AccessibilityChildrenVector& children)
+{
+ GPtrArray* array = g_ptr_array_new();
+ for (const auto& child : children) {
+ if (AtkObject* atkObject = child->wrapper())
+ g_ptr_array_add(array, atkObject);
+ }
+ return array;
+}
+
+static AccessibilityObject* core(AtkTableCell* cell)
+{
+ if (!WEBKIT_IS_ACCESSIBLE(cell))
+ return nullptr;
+
+ return webkitAccessibleGetAccessibilityObject(WEBKIT_ACCESSIBLE(cell));
+}
+
+GPtrArray* webkitAccessibleTableCellGetColumnHeaderCells(AtkTableCell* cell)
+{
+ g_return_val_if_fail(ATK_TABLE_CELL(cell), nullptr);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(cell), nullptr);
+
+ AccessibilityObject* axObject = core(cell);
+ if (!is<AccessibilityTableCell>(axObject))
+ return nullptr;
+
+ AccessibilityObject::AccessibilityChildrenVector columnHeaders;
+ downcast<AccessibilityTableCell>(*axObject).columnHeaders(columnHeaders);
+
+ return convertToGPtrArray(columnHeaders);
+}
+
+GPtrArray* webkitAccessibleTableCellGetRowHeaderCells(AtkTableCell* cell)
+{
+ g_return_val_if_fail(ATK_TABLE_CELL(cell), nullptr);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(cell), nullptr);
+
+ AccessibilityObject* axObject = core(cell);
+ if (!is<AccessibilityTableCell>(axObject))
+ return nullptr;
+
+ AccessibilityObject::AccessibilityChildrenVector rowHeaders;
+ downcast<AccessibilityTableCell>(*axObject).rowHeaders(rowHeaders);
+
+ return convertToGPtrArray(rowHeaders);
+}
+
+gint webkitAccessibleTableCellGetColumnSpan(AtkTableCell* cell)
+{
+ g_return_val_if_fail(ATK_TABLE_CELL(cell), 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(cell), 0);
+
+ AccessibilityObject* axObject = core(cell);
+ if (!is<AccessibilityTableCell>(axObject))
+ return 0;
+
+ std::pair<unsigned, unsigned> columnRange;
+ downcast<AccessibilityTableCell>(*axObject).columnIndexRange(columnRange);
+
+ return columnRange.second;
+}
+
+gint webkitAccessibleTableCellGetRowSpan(AtkTableCell* cell)
+{
+ g_return_val_if_fail(ATK_TABLE_CELL(cell), 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(cell), 0);
+
+ AccessibilityObject* axObject = core(cell);
+ if (!is<AccessibilityTableCell>(axObject))
+ return 0;
+
+ std::pair<unsigned, unsigned> rowRange;
+ downcast<AccessibilityTableCell>(*axObject).rowIndexRange(rowRange);
+
+ return rowRange.second;
+}
+
+gboolean webkitAccessibleTableCellGetPosition(AtkTableCell* cell, gint* row, gint* column)
+{
+ g_return_val_if_fail(ATK_TABLE_CELL(cell), false);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(cell), false);
+
+ AccessibilityObject* axObject = core(cell);
+ if (!is<AccessibilityTableCell>(axObject))
+ return false;
+
+ std::pair<unsigned, unsigned> columnRowRange;
+ if (row) {
+ downcast<AccessibilityTableCell>(*axObject).rowIndexRange(columnRowRange);
+ *row = columnRowRange.first;
+ }
+ if (column) {
+ downcast<AccessibilityTableCell>(*axObject).columnIndexRange(columnRowRange);
+ *column = columnRowRange.first;
+ }
+
+ return true;
+}
+
+AtkObject* webkitAccessibleTableCellGetTable(AtkTableCell* cell)
+{
+ g_return_val_if_fail(ATK_TABLE_CELL(cell), nullptr);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(cell), nullptr);
+
+ AccessibilityObject* axObject = core(cell);
+ if (!axObject || !axObject->isTableCell())
+ return nullptr;
+
+ AtkObject* table = atk_object_get_parent(axObject->wrapper());
+ if (!table || !ATK_IS_TABLE(table))
+ return nullptr;
+
+ return ATK_OBJECT(g_object_ref(table));
+}
+
+void webkitAccessibleTableCellInterfaceInit(AtkTableCellIface* iface)
+{
+ iface->get_column_header_cells = webkitAccessibleTableCellGetColumnHeaderCells;
+ iface->get_row_header_cells = webkitAccessibleTableCellGetRowHeaderCells;
+ iface->get_column_span = webkitAccessibleTableCellGetColumnSpan;
+ iface->get_row_span = webkitAccessibleTableCellGetRowSpan;
+ iface->get_position = webkitAccessibleTableCellGetPosition;
+ iface->get_table = webkitAccessibleTableCellGetTable;
+}
+
+#endif // ATK_CHECK_VERSION(2,11,90)
+#endif // HAVE(ACCESSIBILITY)
diff --git a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceTableCell.h b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceTableCell.h
new file mode 100644
index 000000000..8fa4d7a8f
--- /dev/null
+++ b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceTableCell.h
@@ -0,0 +1,13 @@
+#ifndef WebKitAccessibleInterfaceTableCell_h
+#define WebKitAccessibleInterfaceTableCell_h
+
+#if HAVE(ACCESSIBILITY)
+
+#include <atk/atk.h>
+
+#if ATK_CHECK_VERSION(2,11,90)
+void webkitAccessibleTableCellInterfaceInit(AtkTableCellIface*);
+#endif
+
+#endif // HAVE(ACCESSIBILITY)
+#endif // WebKitAccessibleInterfaceTableCell_h
diff --git a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceText.cpp b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceText.cpp
index 55cc1f2c4..043a69451 100644
--- a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceText.cpp
+++ b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceText.cpp
@@ -2,6 +2,7 @@
* Copyright (C) 2008 Nuanti Ltd.
* Copyright (C) 2009 Jan Alonzo
* Copyright (C) 2009, 2010, 2011, 2012 Igalia S.L.
+ * Copyright (C) 2013 Samsung Electronics. All rights reserved.
*
* Portions from Mozilla a11y, copyright as follows:
*
@@ -35,8 +36,9 @@
#include "AccessibilityObject.h"
#include "Document.h"
-#include "Font.h"
+#include "FontCascade.h"
#include "FrameView.h"
+#include "HTMLParserIdioms.h"
#include "HostWindow.h"
#include "InlineTextBox.h"
#include "NotImplemented.h"
@@ -49,16 +51,16 @@
#include "WebKitAccessibleUtil.h"
#include "WebKitAccessibleWrapperAtk.h"
#include "htmlediting.h"
-#include <wtf/gobject/GOwnPtr.h>
+#include <wtf/glib/GUniquePtr.h>
#include <wtf/text/CString.h>
-#if PLATFORM(GTK)
-#include <libgail-util/gail-util.h>
-#include <pango/pango.h>
-#endif
-
using namespace WebCore;
+// Text attribute to expose the ARIA 'aria-invalid' attribute. Initially initialized
+// to ATK_TEXT_ATTR_INVALID (which means 'invalid' text attribute'), will later on
+// hold a reference to the custom registered AtkTextAttribute that we will use.
+static AtkTextAttribute atkTextAttributeInvalid = ATK_TEXT_ATTR_INVALID;
+
static AccessibilityObject* core(AtkText* text)
{
if (!WEBKIT_IS_ACCESSIBLE(text))
@@ -67,141 +69,13 @@ static AccessibilityObject* core(AtkText* text)
return webkitAccessibleGetAccessibilityObject(WEBKIT_ACCESSIBLE(text));
}
-static gchar* textForRenderer(RenderObject* renderer)
-{
- GString* resultText = g_string_new(0);
-
- if (!renderer)
- return g_string_free(resultText, FALSE);
-
- // For RenderBlocks, piece together the text from the RenderText objects they contain.
- for (RenderObject* object = renderer->firstChild(); object; object = object->nextSibling()) {
- if (object->isBR()) {
- g_string_append(resultText, "\n");
- continue;
- }
-
- RenderText* renderText;
- if (object->isText())
- renderText = toRenderText(object);
- else {
- // List item's markers will be treated in an special way
- // later on this function, so ignore them here.
- if (object->isReplaced() && !object->isListMarker())
- g_string_append_unichar(resultText, objectReplacementCharacter);
-
- // We need to check children, if any, to consider when
- // current object is not a text object but some of its
- // children are, in order not to miss those portions of
- // text by not properly handling those situations
- if (object->firstChild()) {
- GOwnPtr<char> objectText(textForRenderer(object));
- g_string_append(resultText, objectText.get());
- }
- continue;
- }
-
- InlineTextBox* box = renderText ? renderText->firstTextBox() : 0;
- while (box) {
- // WebCore introduces line breaks in the text that do not reflect
- // the layout you see on the screen, replace them with spaces.
- String text = String(renderText->characters(), renderText->textLength()).replace("\n", " ");
- g_string_append(resultText, text.substring(box->start(), box->end() - box->start() + 1).utf8().data());
-
- // Newline chars in the source result in separate text boxes, so check
- // before adding a newline in the layout. See bug 25415 comment #78.
- // If the next sibling is a BR, we'll add the newline when we examine that child.
- if (!box->nextOnLineExists() && !(object->nextSibling() && object->nextSibling()->isBR())) {
- // If there was a '\n' in the last position of the
- // current text box, it would have been converted to a
- // space in String::replace(), so remove it first.
- if (renderText->characters()[box->end()] == '\n')
- g_string_erase(resultText, resultText->len - 1, -1);
-
- g_string_append(resultText, "\n");
- }
- box = box->nextTextBox();
- }
- }
-
- // Insert the text of the marker for list item in the right place, if present
- if (renderer->isListItem()) {
- String markerText = toRenderListItem(renderer)->markerTextWithSuffix();
- if (renderer->style()->direction() == LTR)
- g_string_prepend(resultText, markerText.utf8().data());
- else
- g_string_append(resultText, markerText.utf8().data());
- }
-
- return g_string_free(resultText, FALSE);
-}
-
-static gchar* textForObject(AccessibilityObject* coreObject)
-{
- GString* str = g_string_new(0);
-
- // For text controls, we can get the text line by line.
- if (coreObject->isTextControl()) {
- unsigned textLength = coreObject->textLength();
- int lineNumber = 0;
- PlainTextRange range = coreObject->doAXRangeForLine(lineNumber);
- while (range.length) {
- // When a line of text wraps in a text area, the final space is removed.
- if (range.start + range.length < textLength)
- range.length -= 1;
- String lineText = coreObject->doAXStringForRange(range);
- g_string_append(str, lineText.utf8().data());
- g_string_append(str, "\n");
- range = coreObject->doAXRangeForLine(++lineNumber);
- }
- } else if (coreObject->isAccessibilityRenderObject()) {
- GOwnPtr<gchar> rendererText(textForRenderer(coreObject->renderer()));
- g_string_append(str, rendererText.get());
- }
-
- return g_string_free(str, FALSE);
-}
-
-static gchar* webkitAccessibleTextGetText(AtkText*, gint startOffset, gint endOffset);
-
-#if PLATFORM(GTK)
-static GailTextUtil* getGailTextUtilForAtk(AtkText* textObject)
-{
- GailTextUtil* gailTextUtil = gail_text_util_new();
- GOwnPtr<char> text(webkitAccessibleTextGetText(textObject, 0, -1));
- gail_text_util_text_setup(gailTextUtil, text.get());
- return gailTextUtil;
-}
-
-static PangoLayout* getPangoLayoutForAtk(AtkText* textObject)
-{
- AccessibilityObject* coreObject = core(textObject);
-
- Document* document = coreObject->document();
- if (!document)
- return 0;
-
- HostWindow* hostWindow = document->view()->hostWindow();
- if (!hostWindow)
- return 0;
- PlatformPageClient webView = hostWindow->platformPageClient();
- if (!webView)
- return 0;
-
- // Create a string with the layout as it appears on the screen
- GOwnPtr<char> objectText(textForObject(coreObject));
- PangoLayout* layout = gtk_widget_create_pango_layout(static_cast<GtkWidget*>(webView), objectText.get());
- return layout;
-}
-#endif
-
static int baselinePositionForRenderObject(RenderObject* renderObject)
{
// FIXME: This implementation of baselinePosition originates from RenderObject.cpp and was
// removed in r70072. The implementation looks incorrect though, because this is not the
// baseline of the underlying RenderObject, but of the AccessibilityRenderObject.
- const FontMetrics& fontMetrics = renderObject->firstLineStyle()->fontMetrics();
- return fontMetrics.ascent() + (renderObject->firstLineStyle()->computedLineHeight() - fontMetrics.height()) / 2;
+ const FontMetrics& fontMetrics = renderObject->firstLineStyle().fontMetrics();
+ return fontMetrics.ascent() + (renderObject->firstLineStyle().computedLineHeight() - fontMetrics.height()) / 2;
}
static AtkAttributeSet* getAttributeSetForAccessibilityObject(const AccessibilityObject* object)
@@ -210,21 +84,21 @@ static AtkAttributeSet* getAttributeSetForAccessibilityObject(const Accessibilit
return 0;
RenderObject* renderer = object->renderer();
- RenderStyle* style = renderer->style();
+ RenderStyle* style = &renderer->style();
- AtkAttributeSet* result = 0;
- GOwnPtr<gchar> buffer(g_strdup_printf("%i", style->fontSize()));
+ AtkAttributeSet* result = nullptr;
+ GUniquePtr<gchar> buffer(g_strdup_printf("%i", style->fontSize()));
result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_SIZE), buffer.get());
Color bgColor = style->visitedDependentColor(CSSPropertyBackgroundColor);
if (bgColor.isValid()) {
- buffer.set(g_strdup_printf("%i,%i,%i", bgColor.red(), bgColor.green(), bgColor.blue()));
+ buffer.reset(g_strdup_printf("%i,%i,%i", bgColor.red(), bgColor.green(), bgColor.blue()));
result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_BG_COLOR), buffer.get());
}
Color fgColor = style->visitedDependentColor(CSSPropertyColor);
if (fgColor.isValid()) {
- buffer.set(g_strdup_printf("%i,%i,%i", fgColor.red(), fgColor.green(), fgColor.blue()));
+ buffer.reset(g_strdup_printf("%i,%i,%i", fgColor.red(), fgColor.green(), fgColor.blue()));
result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_FG_COLOR), buffer.get());
}
@@ -246,24 +120,24 @@ static AtkAttributeSet* getAttributeSetForAccessibilityObject(const Accessibilit
}
if (includeRise) {
- buffer.set(g_strdup_printf("%i", baselinePosition));
+ buffer.reset(g_strdup_printf("%i", baselinePosition));
result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_RISE), buffer.get());
}
if (!style->textIndent().isUndefined()) {
- int indentation = valueForLength(style->textIndent(), object->size().width(), renderer->view());
- buffer.set(g_strdup_printf("%i", indentation));
+ int indentation = valueForLength(style->textIndent(), object->size().width());
+ buffer.reset(g_strdup_printf("%i", indentation));
result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_INDENT), buffer.get());
}
- String fontFamilyName = style->font().firstFamily();
+ String fontFamilyName = style->fontCascade().firstFamily();
if (fontFamilyName.left(8) == "-webkit-")
fontFamilyName = fontFamilyName.substring(8);
result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_FAMILY_NAME), fontFamilyName.utf8().data());
int fontWeight = -1;
- switch (style->font().weight()) {
+ switch (style->fontCascade().weight()) {
case FontWeight100:
fontWeight = 100;
break;
@@ -292,7 +166,7 @@ static AtkAttributeSet* getAttributeSetForAccessibilityObject(const Accessibilit
fontWeight = 900;
}
if (fontWeight > 0) {
- buffer.set(g_strdup_printf("%i", fontWeight));
+ buffer.reset(g_strdup_printf("%i", fontWeight));
result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_WEIGHT), buffer.get());
}
@@ -318,18 +192,27 @@ static AtkAttributeSet* getAttributeSetForAccessibilityObject(const Accessibilit
result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_UNDERLINE), (style->textDecoration() & TextDecorationUnderline) ? "single" : "none");
- result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_STYLE), style->font().italic() ? "italic" : "normal");
+ result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_STYLE), style->fontCascade().italic() ? "italic" : "normal");
result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_STRIKETHROUGH), (style->textDecoration() & TextDecorationLineThrough) ? "true" : "false");
result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_INVISIBLE), (style->visibility() == HIDDEN) ? "true" : "false");
- result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_EDITABLE), object->isReadOnly() ? "false" : "true");
+ result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_EDITABLE), object->canSetValueAttribute() ? "true" : "false");
String language = object->language();
if (!language.isEmpty())
result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_LANGUAGE), language.utf8().data());
+ String invalidStatus = object->invalidStatus();
+ if (invalidStatus != "false") {
+ // Register the custom attribute for 'aria-invalid' if not done yet.
+ if (atkTextAttributeInvalid == ATK_TEXT_ATTR_INVALID)
+ atkTextAttributeInvalid = atk_text_attribute_register("invalid");
+
+ result = addToAtkAttributeSet(result, atk_text_attribute_get_name(atkTextAttributeInvalid), invalidStatus.utf8().data());
+ }
+
return result;
}
@@ -348,7 +231,7 @@ static AtkAttributeSet* attributeSetDifference(AtkAttributeSet* attributeSet1, A
AtkAttributeSet* currentSet = attributeSet1;
AtkAttributeSet* found;
- AtkAttributeSet* toDelete = 0;
+ AtkAttributeSet* toDelete = nullptr;
while (currentSet) {
found = g_slist_find_custom(attributeSet2, currentSet->data, (GCompareFunc)compareAttribute);
@@ -366,6 +249,8 @@ static AtkAttributeSet* attributeSetDifference(AtkAttributeSet* attributeSet1, A
return attributeSet1;
}
+static gchar* webkitAccessibleTextGetText(AtkText*, gint startOffset, gint endOffset);
+
static guint accessibilityObjectLength(const AccessibilityObject* object)
{
// Non render objects are not taken into account
@@ -376,7 +261,7 @@ static guint accessibilityObjectLength(const AccessibilityObject* object)
// well known API to always get the text in a consistent way
AtkObject* atkObj = ATK_OBJECT(object->wrapper());
if (ATK_IS_TEXT(atkObj)) {
- GOwnPtr<gchar> text(webkitAccessibleTextGetText(ATK_TEXT(atkObj), 0, -1));
+ GUniquePtr<gchar> text(webkitAccessibleTextGetText(ATK_TEXT(atkObj), 0, -1));
return g_utf8_strlen(text.get(), -1);
}
@@ -385,9 +270,9 @@ static guint accessibilityObjectLength(const AccessibilityObject* object)
// for those cases when it's needed to take it into account
// separately (as in getAccessibilityObjectForOffset)
RenderObject* renderer = object->renderer();
- if (renderer && renderer->isListMarker()) {
- RenderListMarker* marker = toRenderListMarker(renderer);
- return marker->text().length() + marker->suffix().length();
+ if (is<RenderListMarker>(renderer)) {
+ auto& marker = downcast<RenderListMarker>(*renderer);
+ return marker.text().length() + marker.suffix().length();
}
return 0;
@@ -433,7 +318,7 @@ static const AccessibilityObject* getAccessibilityObjectForOffset(const Accessib
return result;
}
-static AtkAttributeSet* getRunAttributesFromAccesibilityObject(const AccessibilityObject* element, gint offset, gint* startOffset, gint* endOffset)
+static AtkAttributeSet* getRunAttributesFromAccessibilityObject(const AccessibilityObject* element, gint offset, gint* startOffset, gint* endOffset)
{
const AccessibilityObject* child = getAccessibilityObjectForOffset(element, offset, startOffset, endOffset);
if (!child) {
@@ -450,7 +335,7 @@ static AtkAttributeSet* getRunAttributesFromAccesibilityObject(const Accessibili
static IntRect textExtents(AtkText* text, gint startOffset, gint length, AtkCoordType coords)
{
- GOwnPtr<char> textContent(webkitAccessibleTextGetText(text, startOffset, -1));
+ GUniquePtr<char> textContent(webkitAccessibleTextGetText(text, startOffset, -1));
gint textLength = g_utf8_strlen(textContent.get(), -1);
// The first case (endOffset of -1) should work, but seems broken for all Gtk+ apps.
@@ -478,8 +363,8 @@ static int offsetAdjustmentForListItem(const AccessibilityObject* object)
// We need to adjust the offsets for the list item marker in
// Left-To-Right text, since we expose it together with the text.
RenderObject* renderer = object->renderer();
- if (renderer && renderer->isListItem() && renderer->style()->direction() == LTR)
- return toRenderListItem(renderer)->markerTextWithSuffix().length();
+ if (is<RenderListItem>(renderer) && renderer->style().direction() == LTR)
+ return downcast<RenderListItem>(*renderer).markerTextWithSuffix().length();
return 0;
}
@@ -501,65 +386,101 @@ static int atkOffsetToWebCoreOffset(AtkText* text, int offset)
return offset - offsetAdjustmentForListItem(coreObject);
}
+static Node* getNodeForAccessibilityObject(AccessibilityObject* coreObject)
+{
+ if (!coreObject->isNativeTextControl())
+ return coreObject->node();
+
+ // For text controls, we get the first visible position on it (which will
+ // belong to its inner element, unreachable from the DOM) and return its
+ // parent node, so we have a "bounding node" for the accessibility object.
+ VisiblePosition positionInTextControlInnerElement = coreObject->visiblePositionForIndex(0, true);
+ Node* innerMostNode = positionInTextControlInnerElement.deepEquivalent().anchorNode();
+ if (!innerMostNode)
+ return 0;
+
+ return innerMostNode->parentNode();
+}
+
static void getSelectionOffsetsForObject(AccessibilityObject* coreObject, VisibleSelection& selection, gint& startOffset, gint& endOffset)
{
- if (!coreObject->isAccessibilityRenderObject())
+ // Default values, unless the contrary is proved.
+ startOffset = 0;
+ endOffset = 0;
+
+ Node* node = getNodeForAccessibilityObject(coreObject);
+ if (!node)
return;
- // Early return if the selection doesn't affect the selected node.
- if (!selectionBelongsToObject(coreObject, selection))
+ if (selection.isNone())
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.
- Position nodeRangeStart;
- Position nodeRangeEnd;
- Node* node = coreObject->node();
- RefPtr<Range> selRange = selection.toNormalizedRange();
-
- // 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).
- Node* firstLeafNode = node->firstDescendant();
- if (selRange->isPointInRange(firstLeafNode, 0, IGNORE_EXCEPTION))
- nodeRangeStart = firstPositionInOrBeforeNode(firstLeafNode);
- else
- nodeRangeStart = selRange->startPosition();
-
- // 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).
- Node* lastLeafNode = node->lastDescendant();
- if (selRange->isPointInRange(lastLeafNode, lastOffsetInNode(lastLeafNode), IGNORE_EXCEPTION))
- nodeRangeEnd = lastPositionInOrAfterNode(lastLeafNode);
- else
- nodeRangeEnd = selRange->endPosition();
+ // We need to limit our search to positions that fall inside the domain of the current object.
+ Position firstValidPosition = firstPositionInOrBeforeNode(node->firstDescendant());
+ Position lastValidPosition = lastPositionInOrAfterNode(node->lastDescendant());
+
+ // Early return with proper values if the selection falls entirely out of the object.
+ if (!selectionBelongsToObject(coreObject, selection)) {
+ startOffset = comparePositions(selection.start(), firstValidPosition) <= 0 ? 0 : accessibilityObjectLength(coreObject);
+ endOffset = startOffset;
+ return;
+ }
+
+ // Find the proper range for the selection that falls inside the object.
+ Position nodeRangeStart = selection.start();
+ if (comparePositions(nodeRangeStart, firstValidPosition) < 0)
+ nodeRangeStart = firstValidPosition;
+
+ Position nodeRangeEnd = selection.end();
+ if (comparePositions(nodeRangeEnd, lastValidPosition) > 0)
+ nodeRangeEnd = lastValidPosition;
// Calculate position of the selected range inside the object.
Position parentFirstPosition = firstPositionInOrBeforeNode(node);
RefPtr<Range> rangeInParent = Range::create(node->document(), parentFirstPosition, nodeRangeStart);
-
- // Set values for start and end offsets.
+ // Set values for start offsets and calculate initial range length.
+ // These values might be adjusted later to cover special cases.
startOffset = webCoreOffsetToAtkOffset(coreObject, TextIterator::rangeLength(rangeInParent.get(), true));
-
RefPtr<Range> nodeRange = Range::create(node->document(), nodeRangeStart, nodeRangeEnd);
- endOffset = startOffset + TextIterator::rangeLength(nodeRange.get(), true);
+ int rangeLength = TextIterator::rangeLength(nodeRange.get(), true);
+
+ // Special cases that are only relevant when working with *_END boundaries.
+ if (selection.affinity() == UPSTREAM) {
+ VisiblePosition visibleStart(nodeRangeStart, UPSTREAM);
+ VisiblePosition visibleEnd(nodeRangeEnd, UPSTREAM);
+
+ // We need to adjust offsets when finding wrapped lines so the position at the end
+ // of the line is properly taking into account when calculating the offsets.
+ if (isEndOfLine(visibleStart) && !lineBreakExistsAtVisiblePosition(visibleStart)) {
+ if (isStartOfLine(visibleStart.next()))
+ rangeLength++;
+
+ if (!isEndOfBlock(visibleStart))
+ startOffset = std::max(startOffset - 1, 0);
+ }
+
+ if (isEndOfLine(visibleEnd) && !lineBreakExistsAtVisiblePosition(visibleEnd) && !isEndOfBlock(visibleEnd))
+ rangeLength--;
+ }
+
+ endOffset = std::min(startOffset + rangeLength, static_cast<int>(accessibilityObjectLength(coreObject)));
}
static gchar* webkitAccessibleTextGetText(AtkText* text, gint startOffset, gint endOffset)
{
+ g_return_val_if_fail(ATK_TEXT(text), 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), 0);
+
AccessibilityObject* coreObject = core(text);
- int end = endOffset;
- if (endOffset == -1) {
- end = coreObject->stringValue().length();
- if (!end)
- end = coreObject->textUnderElement(TextUnderElementModeIncludeAllChildren).length();
+#if ENABLE(INPUT_TYPE_COLOR)
+ if (coreObject->roleValue() == ColorWellRole) {
+ int r, g, b;
+ coreObject->colorValue(r, g, b);
+ return g_strdup_printf("rgb %7.5f %7.5f %7.5f 1", r / 255., g / 255., b / 255.);
}
+#endif
String ret;
if (coreObject->isTextControl())
@@ -567,31 +488,22 @@ static gchar* webkitAccessibleTextGetText(AtkText* text, gint startOffset, gint
else {
ret = coreObject->stringValue();
if (!ret)
- ret = coreObject->textUnderElement(TextUnderElementModeIncludeAllChildren);
- }
-
- if (!ret.length()) {
- // This can happen at least with anonymous RenderBlocks (e.g. body text amongst paragraphs)
- // In such instances, there may also be embedded objects. The object replacement character
- // is something ATs want included and we have to account for the fact that it is multibyte.
- GOwnPtr<char> objectText(textForObject(coreObject));
- ret = String::fromUTF8(objectText.get());
- if (!end)
- end = ret.length();
+ ret = coreObject->textUnderElement(AccessibilityTextUnderElementMode(AccessibilityTextUnderElementMode::TextUnderElementModeIncludeAllChildren));
}
// Prefix a item number/bullet if needed
+ int actualEndOffset = endOffset == -1 ? ret.length() : endOffset;
if (coreObject->roleValue() == ListItemRole) {
RenderObject* objRenderer = coreObject->renderer();
- if (objRenderer && objRenderer->isListItem()) {
- String markerText = toRenderListItem(objRenderer)->markerTextWithSuffix();
- ret = objRenderer->style()->direction() == LTR ? markerText + ret : ret + markerText;
+ if (is<RenderListItem>(objRenderer)) {
+ String markerText = downcast<RenderListItem>(*objRenderer).markerTextWithSuffix();
+ ret = objRenderer->style().direction() == LTR ? markerText + ret : ret + markerText;
if (endOffset == -1)
- end += markerText.length();
+ actualEndOffset = ret.length() + markerText.length();
}
}
- ret = ret.substring(startOffset, end - startOffset);
+ ret = ret.substring(startOffset, actualEndOffset - startOffset);
return g_strdup(ret.utf8().data());
}
@@ -601,19 +513,23 @@ enum GetTextRelativePosition {
GetTextPositionAfter
};
-static gchar* webkitAccessibleTextGetChar(AtkText* text, gint offset, GetTextRelativePosition textPosition, gint* startOffset, gint* endOffset)
+// Convenience function to be used in early returns.
+static char* emptyTextSelectionAtOffset(int offset, int* startOffset, int* endOffset)
{
- AccessibilityObject* coreObject = core(text);
- if (!coreObject || !coreObject->isAccessibilityRenderObject())
- return g_strdup("");
+ *startOffset = offset;
+ *endOffset = offset;
+ return g_strdup("");
+}
+static char* webkitAccessibleTextGetChar(AtkText* text, int offset, GetTextRelativePosition textPosition, int* startOffset, int* endOffset)
+{
int actualOffset = offset;
if (textPosition == GetTextPositionBefore)
actualOffset--;
else if (textPosition == GetTextPositionAfter)
actualOffset++;
- GOwnPtr<char> textData(webkitAccessibleTextGetText(text, 0, -1));
+ GUniquePtr<char> textData(webkitAccessibleTextGetText(text, 0, -1));
int textLength = g_utf8_strlen(textData.get(), -1);
*startOffset = std::max(0, actualOffset);
@@ -628,65 +544,481 @@ static gchar* webkitAccessibleTextGetChar(AtkText* text, gint offset, GetTextRel
return g_utf8_substring(textData.get(), *startOffset, *endOffset);
}
-static gchar* webkitAccessibleTextGetTextForOffset(AtkText* text, gint offset, AtkTextBoundary boundaryType, GetTextRelativePosition textPosition, gint* startOffset, gint* endOffset)
+static VisiblePosition nextWordStartPosition(const VisiblePosition &position)
{
- // Make sure we always return valid valid values for offsets.
- *startOffset = 0;
- *endOffset = 0;
+ VisiblePosition positionAfterCurrentWord = nextWordPosition(position);
- if (boundaryType == ATK_TEXT_BOUNDARY_CHAR)
- return webkitAccessibleTextGetChar(text, offset, textPosition, startOffset, endOffset);
+ // In order to skip spaces when moving right, we advance one word further
+ // and then move one word back. This will put us at the beginning of the
+ // word following.
+ VisiblePosition positionAfterSpacingAndFollowingWord = nextWordPosition(positionAfterCurrentWord);
+
+ if (positionAfterSpacingAndFollowingWord != positionAfterCurrentWord)
+ positionAfterCurrentWord = previousWordPosition(positionAfterSpacingAndFollowingWord);
+
+ bool movingBackwardsMovedPositionToStartOfCurrentWord = positionAfterCurrentWord == previousWordPosition(nextWordPosition(position));
+ if (movingBackwardsMovedPositionToStartOfCurrentWord)
+ positionAfterCurrentWord = positionAfterSpacingAndFollowingWord;
+
+ return positionAfterCurrentWord;
+}
+
+static VisiblePosition previousWordEndPosition(const VisiblePosition &position)
+{
+ // We move forward and then backward to position ourselves at the beginning
+ // of the current word for this boundary, making the most of the semantics
+ // of previousWordPosition() and nextWordPosition().
+ VisiblePosition positionAtStartOfCurrentWord = previousWordPosition(nextWordPosition(position));
+ VisiblePosition positionAtPreviousWord = previousWordPosition(position);
+
+ // Need to consider special cases (punctuation) when we are in the last word of a sentence.
+ if (isStartOfWord(position) && positionAtPreviousWord != position && positionAtPreviousWord == positionAtStartOfCurrentWord)
+ return nextWordPosition(positionAtStartOfCurrentWord);
+
+ // In order to skip spaces when moving left, we advance one word backwards
+ // and then move one word forward. This will put us at the beginning of
+ // the word following.
+ VisiblePosition positionBeforeSpacingAndPreceedingWord = previousWordPosition(positionAtStartOfCurrentWord);
+
+ if (positionBeforeSpacingAndPreceedingWord != positionAtStartOfCurrentWord)
+ positionAtStartOfCurrentWord = nextWordPosition(positionBeforeSpacingAndPreceedingWord);
+
+ bool movingForwardMovedPositionToEndOfCurrentWord = nextWordPosition(positionAtStartOfCurrentWord) == previousWordPosition(nextWordPosition(position));
+ if (movingForwardMovedPositionToEndOfCurrentWord)
+ positionAtStartOfCurrentWord = positionBeforeSpacingAndPreceedingWord;
+
+ return positionAtStartOfCurrentWord;
+}
+
+static VisibleSelection wordAtPositionForAtkBoundary(const AccessibilityObject* /*coreObject*/, const VisiblePosition& position, AtkTextBoundary boundaryType)
+{
+ VisiblePosition startPosition;
+ VisiblePosition endPosition;
+
+ switch (boundaryType) {
+ case ATK_TEXT_BOUNDARY_WORD_START:
+ // isStartOfWord() returns true both when at the beginning of a "real" word
+ // as when at the beginning of a whitespace range between two "real" words,
+ // since that whitespace is considered a "word" as well. And in case we are
+ // already at the beginning of a "real" word we do not need to look backwards.
+ if (isStartOfWord(position) && isWhitespace(position.characterBefore()))
+ startPosition = position;
+ else
+ startPosition = previousWordPosition(position);
+ endPosition = nextWordStartPosition(startPosition);
+
+ // We need to make sure that we look for the word in the current line when
+ // we are at the beginning of a new line, and not look into the previous one
+ // at all, which might happen when lines belong to different nodes.
+ if (isStartOfLine(position) && isStartOfLine(endPosition)) {
+ startPosition = endPosition;
+ endPosition = nextWordStartPosition(startPosition);
+ }
+ break;
+
+ case ATK_TEXT_BOUNDARY_WORD_END:
+ startPosition = previousWordEndPosition(position);
+ endPosition = nextWordPosition(startPosition);
+ break;
+
+ default:
+ ASSERT_NOT_REACHED();
+ }
+
+ VisibleSelection selectedWord(startPosition, endPosition);
+
+ // We mark the selection as 'upstream' so we can use that information later,
+ // when finding the actual offsets in getSelectionOffsetsForObject().
+ if (boundaryType == ATK_TEXT_BOUNDARY_WORD_END)
+ selectedWord.setAffinity(UPSTREAM);
+
+ return selectedWord;
+}
+
+static int numberOfReplacedElementsBeforeOffset(AtkText* text, unsigned offset)
+{
+ GUniquePtr<char> textForObject(webkitAccessibleTextGetText(text, 0, offset));
+ String textBeforeOffset = String::fromUTF8(textForObject.get());
+
+ int count = 0;
+ size_t index = textBeforeOffset.find(objectReplacementCharacter, 0);
+ while (index < offset && index != WTF::notFound) {
+ index = textBeforeOffset.find(objectReplacementCharacter, index + 1);
+ count++;
+ }
+ return count;
+}
+
+static char* webkitAccessibleTextWordForBoundary(AtkText* text, int offset, AtkTextBoundary boundaryType, GetTextRelativePosition textPosition, int* startOffset, int* endOffset)
+{
+ AccessibilityObject* coreObject = core(text);
+ Document* document = coreObject->document();
+ if (!document)
+ return emptyTextSelectionAtOffset(0, startOffset, endOffset);
+
+ Node* node = getNodeForAccessibilityObject(coreObject);
+ if (!node)
+ return emptyTextSelectionAtOffset(0, startOffset, endOffset);
+
+ int actualOffset = atkOffsetToWebCoreOffset(text, offset);
+
+ // Besides of the usual conversion from ATK offsets to WebCore offsets,
+ // we need to consider the potential embedded objects that might have been
+ // inserted in the text exposed through AtkText when calculating the offset.
+ actualOffset -= numberOfReplacedElementsBeforeOffset(text, actualOffset);
+
+ VisiblePosition caretPosition = coreObject->visiblePositionForIndex(actualOffset);
+ VisibleSelection currentWord = wordAtPositionForAtkBoundary(coreObject, caretPosition, boundaryType);
+
+ // Take into account other relative positions, if needed, by
+ // calculating the new position that we would need to consider.
+ VisiblePosition newPosition = caretPosition;
+ switch (textPosition) {
+ case GetTextPositionAt:
+ break;
+
+ case GetTextPositionBefore:
+ // Early return if asking for the previous word while already at the beginning.
+ if (isFirstVisiblePositionInNode(currentWord.visibleStart(), node))
+ return emptyTextSelectionAtOffset(0, startOffset, endOffset);
+
+ if (isStartOfLine(currentWord.end()))
+ newPosition = currentWord.visibleStart().previous();
+ else
+ newPosition = startOfWord(currentWord.start(), LeftWordIfOnBoundary);
+ break;
+
+ case GetTextPositionAfter:
+ // Early return if asking for the following word while already at the end.
+ if (isLastVisiblePositionInNode(currentWord.visibleEnd(), node))
+ return emptyTextSelectionAtOffset(accessibilityObjectLength(coreObject), startOffset, endOffset);
+
+ if (isEndOfLine(currentWord.end()))
+ newPosition = currentWord.visibleEnd().next();
+ else
+ newPosition = endOfWord(currentWord.end(), RightWordIfOnBoundary);
+ break;
+
+ default:
+ ASSERT_NOT_REACHED();
+ }
-#if PLATFORM(GTK)
- // FIXME: Get rid of the code below once every single get_text_*_offset
- // function has been properly implemented without using Pango/Cairo.
- GailOffsetType offsetType = GAIL_AT_OFFSET;
+ // Determine the relevant word we are actually interested in
+ // and calculate the ATK offsets for it, then return everything.
+ VisibleSelection selectedWord = newPosition != caretPosition ? wordAtPositionForAtkBoundary(coreObject, newPosition, boundaryType) : currentWord;
+ getSelectionOffsetsForObject(coreObject, selectedWord, *startOffset, *endOffset);
+ return webkitAccessibleTextGetText(text, *startOffset, *endOffset);
+}
+
+static bool isSentenceBoundary(const VisiblePosition &pos)
+{
+ if (pos.isNull())
+ return false;
+
+ // It's definitely a sentence boundary if there's nothing before.
+ if (pos.previous().isNull())
+ return true;
+
+ // We go backwards and forward to make sure about this.
+ VisiblePosition startOfPreviousSentence = startOfSentence(pos);
+ return startOfPreviousSentence.isNotNull() && pos == endOfSentence(startOfPreviousSentence);
+}
+
+static bool isWhiteSpaceBetweenSentences(const VisiblePosition& position)
+{
+ if (position.isNull())
+ return false;
+
+ if (!isWhitespace(position.characterAfter()))
+ return false;
+
+ VisiblePosition startOfWhiteSpace = startOfWord(position, RightWordIfOnBoundary);
+ VisiblePosition endOfWhiteSpace = endOfWord(startOfWhiteSpace, RightWordIfOnBoundary);
+ if (!isSentenceBoundary(startOfWhiteSpace) && !isSentenceBoundary(endOfWhiteSpace))
+ return false;
+
+ return comparePositions(startOfWhiteSpace, position) <= 0 && comparePositions(endOfWhiteSpace, position) >= 0;
+}
+
+static VisibleSelection sentenceAtPositionForAtkBoundary(const AccessibilityObject*, const VisiblePosition& position, AtkTextBoundary boundaryType)
+{
+ VisiblePosition startPosition;
+ VisiblePosition endPosition;
+
+ bool isAtStartOfSentenceForEndBoundary = isWhiteSpaceBetweenSentences(position) || isSentenceBoundary(position);
+ if (boundaryType == ATK_TEXT_BOUNDARY_SENTENCE_START || !isAtStartOfSentenceForEndBoundary) {
+ startPosition = isSentenceBoundary(position) ? position : startOfSentence(position);
+ // startOfSentence might stop at a linebreak in the HTML source code,
+ // but we don't want to stop there yet, so keep going.
+ while (!isSentenceBoundary(startPosition) && isHTMLLineBreak(startPosition.characterBefore()))
+ startPosition = startOfSentence(startPosition);
+
+ endPosition = endOfSentence(startPosition);
+ }
+
+ if (boundaryType == ATK_TEXT_BOUNDARY_SENTENCE_END) {
+ if (isAtStartOfSentenceForEndBoundary) {
+ startPosition = position;
+ endPosition = endOfSentence(endOfWord(position, RightWordIfOnBoundary));
+ }
+
+ // startOfSentence returns a position after any white space previous to
+ // the sentence, so we might need to adjust that offset for this boundary.
+ if (isWhitespace(startPosition.characterBefore()))
+ startPosition = startOfWord(startPosition, LeftWordIfOnBoundary);
+
+ // endOfSentence returns a position after any white space after the
+ // sentence, so we might need to adjust that offset for this boundary.
+ if (isWhitespace(endPosition.characterBefore()))
+ endPosition = startOfWord(endPosition, LeftWordIfOnBoundary);
+
+ // Finally, do some additional adjustments that might be needed if
+ // positions are at the start or the end of a line.
+ if (isStartOfLine(startPosition) && !isStartOfBlock(startPosition))
+ startPosition = startPosition.previous();
+ if (isStartOfLine(endPosition) && !isStartOfBlock(endPosition))
+ endPosition = endPosition.previous();
+ }
+
+ VisibleSelection selectedSentence(startPosition, endPosition);
+
+ // We mark the selection as 'upstream' so we can use that information later,
+ // when finding the actual offsets in getSelectionOffsetsForObject().
+ if (boundaryType == ATK_TEXT_BOUNDARY_SENTENCE_END)
+ selectedSentence.setAffinity(UPSTREAM);
+
+ return selectedSentence;
+}
+
+static char* webkitAccessibleTextSentenceForBoundary(AtkText* text, int offset, AtkTextBoundary boundaryType, GetTextRelativePosition textPosition, int* startOffset, int* endOffset)
+{
+ AccessibilityObject* coreObject = core(text);
+ Document* document = coreObject->document();
+ if (!document)
+ return emptyTextSelectionAtOffset(0, startOffset, endOffset);
+
+ Node* node = getNodeForAccessibilityObject(coreObject);
+ if (!node)
+ return emptyTextSelectionAtOffset(0, startOffset, endOffset);
+
+ int actualOffset = atkOffsetToWebCoreOffset(text, offset);
+
+ // Besides of the usual conversion from ATK offsets to WebCore offsets,
+ // we need to consider the potential embedded objects that might have been
+ // inserted in the text exposed through AtkText when calculating the offset.
+ actualOffset -= numberOfReplacedElementsBeforeOffset(text, actualOffset);
+
+ VisiblePosition caretPosition = coreObject->visiblePositionForIndex(actualOffset);
+ VisibleSelection currentSentence = sentenceAtPositionForAtkBoundary(coreObject, caretPosition, boundaryType);
+
+ // Take into account other relative positions, if needed, by
+ // calculating the new position that we would need to consider.
+ VisiblePosition newPosition = caretPosition;
switch (textPosition) {
+ case GetTextPositionAt:
+ break;
+
case GetTextPositionBefore:
- offsetType = GAIL_BEFORE_OFFSET;
+ // Early return if asking for the previous sentence while already at the beginning.
+ if (isFirstVisiblePositionInNode(currentSentence.visibleStart(), node))
+ return emptyTextSelectionAtOffset(0, startOffset, endOffset);
+ newPosition = currentSentence.visibleStart().previous();
+ break;
+
+ case GetTextPositionAfter:
+ // Early return if asking for the following word while already at the end.
+ if (isLastVisiblePositionInNode(currentSentence.visibleEnd(), node))
+ return emptyTextSelectionAtOffset(accessibilityObjectLength(coreObject), startOffset, endOffset);
+ newPosition = currentSentence.visibleEnd().next();
+ break;
+
+ default:
+ ASSERT_NOT_REACHED();
+ }
+
+ // Determine the relevant sentence we are actually interested in
+ // and calculate the ATK offsets for it, then return everything.
+ VisibleSelection selectedSentence = newPosition != caretPosition ? sentenceAtPositionForAtkBoundary(coreObject, newPosition, boundaryType) : currentSentence;
+ getSelectionOffsetsForObject(coreObject, selectedSentence, *startOffset, *endOffset);
+ return webkitAccessibleTextGetText(text, *startOffset, *endOffset);
+}
+
+static VisibleSelection lineAtPositionForAtkBoundary(const AccessibilityObject* coreObject, const VisiblePosition& position, AtkTextBoundary boundaryType)
+{
+ UNUSED_PARAM(coreObject);
+ VisiblePosition startPosition;
+ VisiblePosition endPosition;
+
+ switch (boundaryType) {
+ case ATK_TEXT_BOUNDARY_LINE_START:
+ startPosition = isStartOfLine(position) ? position : logicalStartOfLine(position);
+ endPosition = logicalEndOfLine(position);
+
+ // In addition to checking that we are not at the end of a block, we need
+ // to check that endPosition has not UPSTREAM affinity, since that would
+ // cause trouble inside of text controls (we would be advancing too much).
+ if (!isEndOfBlock(endPosition) && endPosition.affinity() != UPSTREAM)
+ endPosition = endPosition.next();
+ break;
+
+ case ATK_TEXT_BOUNDARY_LINE_END:
+ startPosition = isEndOfLine(position) ? position : logicalStartOfLine(position);
+ if (!isStartOfBlock(startPosition))
+ startPosition = startPosition.previous();
+ endPosition = logicalEndOfLine(position);
break;
+ default:
+ ASSERT_NOT_REACHED();
+ }
+
+ VisibleSelection selectedLine(startPosition, endPosition);
+
+ // We mark the selection as 'upstream' so we can use that information later,
+ // when finding the actual offsets in getSelectionOffsetsForObject().
+ if (boundaryType == ATK_TEXT_BOUNDARY_LINE_END)
+ selectedLine.setAffinity(UPSTREAM);
+
+ return selectedLine;
+}
+
+static char* webkitAccessibleTextLineForBoundary(AtkText* text, int offset, AtkTextBoundary boundaryType, GetTextRelativePosition textPosition, int* startOffset, int* endOffset)
+{
+ AccessibilityObject* coreObject = core(text);
+ Document* document = coreObject->document();
+ if (!document)
+ return emptyTextSelectionAtOffset(0, startOffset, endOffset);
+
+ Node* node = getNodeForAccessibilityObject(coreObject);
+ if (!node)
+ return emptyTextSelectionAtOffset(0, startOffset, endOffset);
+
+ int actualOffset = atkOffsetToWebCoreOffset(text, offset);
+
+ // Besides the usual conversion from ATK offsets to WebCore offsets,
+ // we need to consider the potential embedded objects that might have been
+ // inserted in the text exposed through AtkText when calculating the offset.
+ actualOffset -= numberOfReplacedElementsBeforeOffset(text, actualOffset);
+
+ VisiblePosition caretPosition = coreObject->visiblePositionForIndex(actualOffset);
+ VisibleSelection currentLine = lineAtPositionForAtkBoundary(coreObject, caretPosition, boundaryType);
+
+ // Take into account other relative positions, if needed, by
+ // calculating the new position that we would need to consider.
+ VisiblePosition newPosition = caretPosition;
+ switch (textPosition) {
case GetTextPositionAt:
+ // No need to do additional work if we are using the "at" position, we just
+ // explicitly list this case option to catch invalid values in the default case.
+ break;
+
+ case GetTextPositionBefore:
+ // Early return if asking for the previous line while already at the beginning.
+ if (isFirstVisiblePositionInNode(currentLine.visibleStart(), node))
+ return emptyTextSelectionAtOffset(0, startOffset, endOffset);
+ newPosition = currentLine.visibleStart().previous();
break;
case GetTextPositionAfter:
- offsetType = GAIL_AFTER_OFFSET;
+ // Early return if asking for the following word while already at the end.
+ if (isLastVisiblePositionInNode(currentLine.visibleEnd(), node))
+ return emptyTextSelectionAtOffset(accessibilityObjectLength(coreObject), startOffset, endOffset);
+ newPosition = currentLine.visibleEnd().next();
break;
default:
ASSERT_NOT_REACHED();
}
- return gail_text_util_get_text(getGailTextUtilForAtk(text), getPangoLayoutForAtk(text), offsetType, boundaryType, offset, startOffset, endOffset);
-#endif
+ // Determine the relevant line we are actually interested in
+ // and calculate the ATK offsets for it, then return everything.
+ VisibleSelection selectedLine = newPosition != caretPosition ? lineAtPositionForAtkBoundary(coreObject, newPosition, boundaryType) : currentLine;
+ getSelectionOffsetsForObject(coreObject, selectedLine, *startOffset, *endOffset);
- notImplemented();
+ // We might need to adjust the start or end offset to include the list item marker,
+ // if present, when printing the first or the last full line for a list item.
+ RenderObject* renderer = coreObject->renderer();
+ if (renderer->isListItem()) {
+ // For Left-to-Right, the list item marker is at the beginning of the exposed text.
+ if (renderer->style().direction() == LTR && isFirstVisiblePositionInNode(selectedLine.visibleStart(), node))
+ *startOffset = 0;
+
+ // For Right-to-Left, the list item marker is at the end of the exposed text.
+ if (renderer->style().direction() == RTL && isLastVisiblePositionInNode(selectedLine.visibleEnd(), node))
+ *endOffset = accessibilityObjectLength(coreObject);
+ }
+
+ return webkitAccessibleTextGetText(text, *startOffset, *endOffset);
+}
+
+static gchar* webkitAccessibleTextGetTextForOffset(AtkText* text, gint offset, AtkTextBoundary boundaryType, GetTextRelativePosition textPosition, gint* startOffset, gint* endOffset)
+{
+ AccessibilityObject* coreObject = core(text);
+ if (!coreObject || !coreObject->isAccessibilityRenderObject())
+ return emptyTextSelectionAtOffset(0, startOffset, endOffset);
+
+ switch (boundaryType) {
+ case ATK_TEXT_BOUNDARY_CHAR:
+ return webkitAccessibleTextGetChar(text, offset, textPosition, startOffset, endOffset);
+
+ case ATK_TEXT_BOUNDARY_WORD_START:
+ case ATK_TEXT_BOUNDARY_WORD_END:
+ return webkitAccessibleTextWordForBoundary(text, offset, boundaryType, textPosition, startOffset, endOffset);
+
+ case ATK_TEXT_BOUNDARY_LINE_START:
+ case ATK_TEXT_BOUNDARY_LINE_END:
+ return webkitAccessibleTextLineForBoundary(text, offset, boundaryType, textPosition, startOffset, endOffset);
+
+ case ATK_TEXT_BOUNDARY_SENTENCE_START:
+ case ATK_TEXT_BOUNDARY_SENTENCE_END:
+ return webkitAccessibleTextSentenceForBoundary(text, offset, boundaryType, textPosition, startOffset, endOffset);
+
+ default:
+ ASSERT_NOT_REACHED();
+ }
+
+ // This should never be reached.
return 0;
}
static gchar* webkitAccessibleTextGetTextAfterOffset(AtkText* text, gint offset, AtkTextBoundary boundaryType, gint* startOffset, gint* endOffset)
{
+ g_return_val_if_fail(ATK_TEXT(text), 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), 0);
+
return webkitAccessibleTextGetTextForOffset(text, offset, boundaryType, GetTextPositionAfter, startOffset, endOffset);
}
static gchar* webkitAccessibleTextGetTextAtOffset(AtkText* text, gint offset, AtkTextBoundary boundaryType, gint* startOffset, gint* endOffset)
{
+ g_return_val_if_fail(ATK_TEXT(text), 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), 0);
+
return webkitAccessibleTextGetTextForOffset(text, offset, boundaryType, GetTextPositionAt, startOffset, endOffset);
}
static gchar* webkitAccessibleTextGetTextBeforeOffset(AtkText* text, gint offset, AtkTextBoundary boundaryType, gint* startOffset, gint* endOffset)
{
+ g_return_val_if_fail(ATK_TEXT(text), 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), 0);
+
return webkitAccessibleTextGetTextForOffset(text, offset, boundaryType, GetTextPositionBefore, startOffset, endOffset);
}
-static gunichar webkitAccessibleTextGetCharacterAtOffset(AtkText*, gint)
+static gunichar webkitAccessibleTextGetCharacterAtOffset(AtkText* text, gint)
{
+ g_return_val_if_fail(ATK_TEXT(text), 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), 0);
+
notImplemented();
return 0;
}
static gint webkitAccessibleTextGetCaretOffset(AtkText* text)
{
+ g_return_val_if_fail(ATK_TEXT(text), 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), 0);
+
// coreObject is the unignored object whose offset the caller is requesting.
// focusedObject is the object with the caret. It is likely ignored -- unless it's a link.
AccessibilityObject* coreObject = core(text);
@@ -708,6 +1040,9 @@ static gint webkitAccessibleTextGetCaretOffset(AtkText* text)
static AtkAttributeSet* webkitAccessibleTextGetRunAttributes(AtkText* text, gint offset, gint* startOffset, gint* endOffset)
{
+ g_return_val_if_fail(ATK_TEXT(text), 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), 0);
+
AccessibilityObject* coreObject = core(text);
AtkAttributeSet* result;
@@ -720,7 +1055,7 @@ static AtkAttributeSet* webkitAccessibleTextGetRunAttributes(AtkText* text, gint
if (offset == -1)
offset = atk_text_get_caret_offset(text);
- result = getRunAttributesFromAccesibilityObject(coreObject, offset, startOffset, endOffset);
+ result = getRunAttributesFromAccessibilityObject(coreObject, offset, startOffset, endOffset);
if (*startOffset < 0) {
*startOffset = offset;
@@ -732,6 +1067,9 @@ static AtkAttributeSet* webkitAccessibleTextGetRunAttributes(AtkText* text, gint
static AtkAttributeSet* webkitAccessibleTextGetDefaultAttributes(AtkText* text)
{
+ g_return_val_if_fail(ATK_TEXT(text), 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), 0);
+
AccessibilityObject* coreObject = core(text);
if (!coreObject || !coreObject->isAccessibilityRenderObject())
return 0;
@@ -741,6 +1079,9 @@ static AtkAttributeSet* webkitAccessibleTextGetDefaultAttributes(AtkText* text)
static void webkitAccessibleTextGetCharacterExtents(AtkText* text, gint offset, gint* x, gint* y, gint* width, gint* height, AtkCoordType coords)
{
+ g_return_if_fail(ATK_TEXT(text));
+ returnIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text));
+
IntRect extents = textExtents(text, offset, 1, coords);
*x = extents.x();
*y = extents.y();
@@ -750,6 +1091,9 @@ static void webkitAccessibleTextGetCharacterExtents(AtkText* text, gint offset,
static void webkitAccessibleTextGetRangeExtents(AtkText* text, gint startOffset, gint endOffset, AtkCoordType coords, AtkTextRectangle* rect)
{
+ g_return_if_fail(ATK_TEXT(text));
+ returnIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text));
+
IntRect extents = textExtents(text, startOffset, endOffset - startOffset, coords);
rect->x = extents.x();
rect->y = extents.y();
@@ -759,11 +1103,17 @@ static void webkitAccessibleTextGetRangeExtents(AtkText* text, gint startOffset,
static gint webkitAccessibleTextGetCharacterCount(AtkText* text)
{
+ g_return_val_if_fail(ATK_TEXT(text), 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), 0);
+
return accessibilityObjectLength(core(text));
}
static gint webkitAccessibleTextGetOffsetAtPoint(AtkText* text, gint x, gint y, AtkCoordType)
{
+ g_return_val_if_fail(ATK_TEXT(text), 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), 0);
+
// FIXME: Use the AtkCoordType
// TODO: Is it correct to ignore range.length?
IntPoint pos(x, y);
@@ -773,6 +1123,9 @@ static gint webkitAccessibleTextGetOffsetAtPoint(AtkText* text, gint x, gint y,
static gint webkitAccessibleTextGetNSelections(AtkText* text)
{
+ g_return_val_if_fail(ATK_TEXT(text), 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), 0);
+
AccessibilityObject* coreObject = core(text);
VisibleSelection selection = coreObject->selection();
@@ -791,8 +1144,8 @@ static gint webkitAccessibleTextGetNSelections(AtkText* text)
static gchar* webkitAccessibleTextGetSelection(AtkText* text, gint selectionNum, gint* startOffset, gint* endOffset)
{
- // Default values, unless the contrary is proved
- *startOffset = *endOffset = 0;
+ g_return_val_if_fail(ATK_TEXT(text), 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), 0);
// WebCore does not support multiple selection, so anything but 0 does not make sense for now.
if (selectionNum)
@@ -811,14 +1164,20 @@ static gchar* webkitAccessibleTextGetSelection(AtkText* text, gint selectionNum,
return webkitAccessibleTextGetText(text, *startOffset, *endOffset);
}
-static gboolean webkitAccessibleTextAddSelection(AtkText*, gint, gint)
+static gboolean webkitAccessibleTextAddSelection(AtkText* text, gint, gint)
{
+ g_return_val_if_fail(ATK_TEXT(text), FALSE);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), FALSE);
+
notImplemented();
return FALSE;
}
static gboolean webkitAccessibleTextSetSelection(AtkText* text, gint selectionNum, gint startOffset, gint endOffset)
{
+ g_return_val_if_fail(ATK_TEXT(text), FALSE);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), FALSE);
+
// WebCore does not support multiple selection, so anything but 0 does not make sense for now.
if (selectionNum)
return FALSE;
@@ -855,6 +1214,9 @@ static gboolean webkitAccessibleTextSetSelection(AtkText* text, gint selectionNu
static gboolean webkitAccessibleTextRemoveSelection(AtkText* text, gint selectionNum)
{
+ g_return_val_if_fail(ATK_TEXT(text), FALSE);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), FALSE);
+
// WebCore does not support multiple selection, so anything but 0 does not make sense for now.
if (selectionNum)
return FALSE;
@@ -871,28 +1233,52 @@ static gboolean webkitAccessibleTextRemoveSelection(AtkText* text, gint selectio
static gboolean webkitAccessibleTextSetCaretOffset(AtkText* text, gint offset)
{
- AccessibilityObject* coreObject = core(text);
+ // Internally, setting the caret offset is equivalent to set a zero-length
+ // selection, so delegate in that implementation and void duplicated code.
+ return webkitAccessibleTextSetSelection(text, 0, offset, offset);
+}
- if (!coreObject->isAccessibilityRenderObject())
- return FALSE;
+#if ATK_CHECK_VERSION(2, 10, 0)
+static gchar* webkitAccessibleTextGetStringAtOffset(AtkText* text, gint offset, AtkTextGranularity granularity, gint* startOffset, gint* endOffset)
+{
+ // This new API has been designed to simplify the AtkText interface and it has been
+ // designed to keep exactly the same behaviour the atk_text_get_text_at_text() for
+ // ATK_TEXT_BOUNDARY_*_START boundaries, so for now we just need to translate the
+ // granularity to the right old boundary and reuse the code for the old API.
+ // However, this should be simplified later on (and a lot of code removed) once
+ // WebKitGTK+ depends on ATK >= 2.9.4 *and* can safely assume that a version of
+ // AT-SPI2 new enough not to include the old APIs is being used. But until then,
+ // we will have to live with both the old and new APIs implemented here.
+ AtkTextBoundary boundaryType = ATK_TEXT_BOUNDARY_CHAR;
+ switch (granularity) {
+ case ATK_TEXT_GRANULARITY_CHAR:
+ break;
- // We need to adjust the offsets for the list item marker.
- int offsetAdjustment = offsetAdjustmentForListItem(coreObject);
- if (offsetAdjustment) {
- if (offset < offsetAdjustment)
- return FALSE;
+ case ATK_TEXT_GRANULARITY_WORD:
+ boundaryType = ATK_TEXT_BOUNDARY_WORD_START;
+ break;
- offset = atkOffsetToWebCoreOffset(text, offset);
- }
+ case ATK_TEXT_GRANULARITY_SENTENCE:
+ boundaryType = ATK_TEXT_BOUNDARY_SENTENCE_START;
+ break;
- PlainTextRange textRange(offset, 0);
- VisiblePositionRange range = coreObject->visiblePositionRangeForRange(textRange);
- if (range.isNull())
- return FALSE;
+ case ATK_TEXT_GRANULARITY_LINE:
+ boundaryType = ATK_TEXT_BOUNDARY_LINE_START;
+ break;
- coreObject->setSelectedVisiblePositionRange(range);
- return TRUE;
+ case ATK_TEXT_GRANULARITY_PARAGRAPH:
+ // FIXME: This has not been a need with the old AtkText API, which means ATs won't
+ // need it yet for some time, so we can skip it for now.
+ notImplemented();
+ return g_strdup("");
+
+ default:
+ ASSERT_NOT_REACHED();
+ }
+
+ return webkitAccessibleTextGetTextForOffset(text, offset, boundaryType, GetTextPositionAt, startOffset, endOffset);
}
+#endif
void webkitAccessibleTextInterfaceInit(AtkTextIface* iface)
{
@@ -914,6 +1300,10 @@ void webkitAccessibleTextInterfaceInit(AtkTextIface* iface)
iface->remove_selection = webkitAccessibleTextRemoveSelection;
iface->set_selection = webkitAccessibleTextSetSelection;
iface->set_caret_offset = webkitAccessibleTextSetCaretOffset;
+
+#if ATK_CHECK_VERSION(2, 10, 0)
+ iface->get_string_at_offset = webkitAccessibleTextGetStringAtOffset;
+#endif
}
#endif
diff --git a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceValue.cpp b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceValue.cpp
index 6ba7424aa..ac582fa66 100644
--- a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceValue.cpp
+++ b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceValue.cpp
@@ -24,7 +24,9 @@
#include "AccessibilityObject.h"
#include "HTMLNames.h"
+#include "WebKitAccessibleUtil.h"
#include "WebKitAccessibleWrapperAtk.h"
+#include <wtf/text/CString.h>
using namespace WebCore;
@@ -36,8 +38,88 @@ static AccessibilityObject* core(AtkValue* value)
return webkitAccessibleGetAccessibilityObject(WEBKIT_ACCESSIBLE(value));
}
+static bool webkitAccessibleSetNewValue(AtkValue* coreValue, const gdouble newValue)
+{
+ AccessibilityObject* coreObject = core(coreValue);
+ if (!coreObject->canSetValueAttribute())
+ return FALSE;
+
+ // Check value against range limits
+ double value;
+ value = std::max(static_cast<double>(coreObject->minValueForRange()), newValue);
+ value = std::min(static_cast<double>(coreObject->maxValueForRange()), newValue);
+
+ coreObject->setValue(String::number(value));
+ return TRUE;
+}
+
+static float webkitAccessibleGetIncrementValue(AccessibilityObject* coreObject)
+{
+ if (!coreObject->getAttribute(HTMLNames::stepAttr).isEmpty())
+ return coreObject->stepValueForRange();
+
+ // If 'step' attribute is not defined, WebCore assumes a 5% of the
+ // range between minimum and maximum values. Implicit value of step should be one or larger.
+ float step = (coreObject->maxValueForRange() - coreObject->minValueForRange()) * 0.05;
+ return step < 1 ? 1 : step;
+}
+
+#if ATK_CHECK_VERSION(2,11,92)
+static void webkitAccessibleGetValueAndText(AtkValue* value, gdouble* currentValue, gchar** alternativeText)
+{
+ g_return_if_fail(ATK_VALUE(value));
+ returnIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(value));
+
+ AccessibilityObject* coreObject = core(value);
+ if (!coreObject)
+ return;
+
+ if (currentValue)
+ *currentValue = coreObject->valueForRange();
+ if (alternativeText)
+ *alternativeText = g_strdup_printf("%s", coreObject->valueDescription().utf8().data());
+}
+
+static double webkitAccessibleGetIncrement(AtkValue* value)
+{
+ g_return_val_if_fail(ATK_VALUE(value), 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(value), 0);
+
+ AccessibilityObject* coreObject = core(value);
+ if (!coreObject)
+ return 0;
+
+ return webkitAccessibleGetIncrementValue(coreObject);
+}
+
+static void webkitAccessibleSetValue(AtkValue* value, const gdouble newValue)
+{
+ g_return_if_fail(ATK_VALUE(value));
+ returnIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(value));
+
+ webkitAccessibleSetNewValue(value, newValue);
+}
+
+static AtkRange* webkitAccessibleGetRange(AtkValue* value)
+{
+ g_return_val_if_fail(ATK_VALUE(value), nullptr);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(value), nullptr);
+
+ AccessibilityObject* coreObject = core(value);
+ if (!coreObject)
+ return nullptr;
+
+ gdouble minValue = coreObject->minValueForRange();
+ gdouble maxValue = coreObject->maxValueForRange();
+ gchar* valueDescription = g_strdup_printf("%s", coreObject->valueDescription().utf8().data());
+ return atk_range_new(minValue, maxValue, valueDescription);
+}
+#endif
static void webkitAccessibleValueGetCurrentValue(AtkValue* value, GValue* gValue)
{
+ g_return_if_fail(ATK_VALUE(value));
+ returnIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(value));
+
memset(gValue, 0, sizeof(GValue));
g_value_init(gValue, G_TYPE_FLOAT);
g_value_set_float(gValue, core(value)->valueForRange());
@@ -45,6 +127,9 @@ static void webkitAccessibleValueGetCurrentValue(AtkValue* value, GValue* gValue
static void webkitAccessibleValueGetMaximumValue(AtkValue* value, GValue* gValue)
{
+ g_return_if_fail(ATK_VALUE(value));
+ returnIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(value));
+
memset(gValue, 0, sizeof(GValue));
g_value_init(gValue, G_TYPE_FLOAT);
g_value_set_float(gValue, core(value)->maxValueForRange());
@@ -52,6 +137,9 @@ static void webkitAccessibleValueGetMaximumValue(AtkValue* value, GValue* gValue
static void webkitAccessibleValueGetMinimumValue(AtkValue* value, GValue* gValue)
{
+ g_return_if_fail(ATK_VALUE(value));
+ returnIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(value));
+
memset(gValue, 0, sizeof(GValue));
g_value_init(gValue, G_TYPE_FLOAT);
g_value_set_float(gValue, core(value)->minValueForRange());
@@ -59,6 +147,9 @@ static void webkitAccessibleValueGetMinimumValue(AtkValue* value, GValue* gValue
static gboolean webkitAccessibleValueSetCurrentValue(AtkValue* value, const GValue* gValue)
{
+ g_return_val_if_fail(ATK_VALUE(value), FALSE);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(value), FALSE);
+
double newValue;
if (G_VALUE_HOLDS_DOUBLE(gValue))
newValue = g_value_get_double(gValue);
@@ -77,39 +168,31 @@ static gboolean webkitAccessibleValueSetCurrentValue(AtkValue* value, const GVal
else if (G_VALUE_HOLDS_UINT(gValue))
newValue = g_value_get_uint(gValue);
else
- return false;
-
- AccessibilityObject* coreObject = core(value);
- if (!coreObject->canSetValueAttribute())
return FALSE;
- // Check value against range limits
- newValue = std::max(static_cast<double>(coreObject->minValueForRange()), newValue);
- newValue = std::min(static_cast<double>(coreObject->maxValueForRange()), newValue);
-
- coreObject->setValue(String::number(newValue));
- return TRUE;
+ return webkitAccessibleSetNewValue(value, newValue);
}
static void webkitAccessibleValueGetMinimumIncrement(AtkValue* value, GValue* gValue)
{
+ g_return_if_fail(ATK_VALUE(value));
+ returnIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(value));
+
memset(gValue, 0, sizeof(GValue));
g_value_init(gValue, G_TYPE_FLOAT);
AccessibilityObject* coreObject = core(value);
- if (!coreObject->getAttribute(HTMLNames::stepAttr).isEmpty()) {
- g_value_set_float(gValue, coreObject->stepValueForRange());
- return;
- }
-
- // If 'step' attribute is not defined, WebCore assumes a 5% of the
- // range between minimum and maximum values, so return that.
- float range = coreObject->maxValueForRange() - coreObject->minValueForRange();
- g_value_set_float(gValue, range * 0.05);
+ g_value_set_float(gValue, webkitAccessibleGetIncrementValue(coreObject));
}
void webkitAccessibleValueInterfaceInit(AtkValueIface* iface)
{
+#if ATK_CHECK_VERSION(2,11,92)
+ iface->get_value_and_text = webkitAccessibleGetValueAndText;
+ iface->get_increment = webkitAccessibleGetIncrement;
+ iface->set_value = webkitAccessibleSetValue;
+ iface->get_range = webkitAccessibleGetRange;
+#endif
iface->get_current_value = webkitAccessibleValueGetCurrentValue;
iface->get_maximum_value = webkitAccessibleValueGetMaximumValue;
iface->get_minimum_value = webkitAccessibleValueGetMinimumValue;
diff --git a/Source/WebCore/accessibility/atk/WebKitAccessibleUtil.cpp b/Source/WebCore/accessibility/atk/WebKitAccessibleUtil.cpp
index 34a71f178..94a7a2378 100644
--- a/Source/WebCore/accessibility/atk/WebKitAccessibleUtil.cpp
+++ b/Source/WebCore/accessibility/atk/WebKitAccessibleUtil.cpp
@@ -94,10 +94,7 @@ String accessibilityTitle(AccessibilityObject* coreObject)
Vector<AccessibilityText> textOrder;
coreObject->accessibilityText(textOrder);
- unsigned length = textOrder.size();
- for (unsigned k = 0; k < length; k++) {
- const AccessibilityText& text = textOrder[k];
-
+ for (const AccessibilityText& text : textOrder) {
// Once we encounter visible text, or the text from our children that should be used foremost.
if (text.textSource == VisibleText || text.textSource == ChildrenText)
return text.text;
@@ -116,6 +113,7 @@ String accessibilityTitle(AccessibilityObject* coreObject)
if (text.textSource == TitleTagText && !titleTagShouldBeUsedInDescriptionField(coreObject))
return text.text;
}
+
return String();
}
@@ -124,14 +122,21 @@ String accessibilityDescription(AccessibilityObject* coreObject)
Vector<AccessibilityText> textOrder;
coreObject->accessibilityText(textOrder);
- unsigned length = textOrder.size();
- for (unsigned k = 0; k < length; k++) {
- const AccessibilityText& text = textOrder[k];
-
+ bool visibleTextAvailable = false;
+ for (const AccessibilityText& text : textOrder) {
if (text.textSource == AlternativeText)
return text.text;
- if (text.textSource == TitleTagText && titleTagShouldBeUsedInDescriptionField(coreObject))
+ switch (text.textSource) {
+ case VisibleText:
+ case ChildrenText:
+ case LabelByElementText:
+ visibleTextAvailable = true;
+ default:
+ break;
+ }
+
+ if (text.textSource == TitleTagText && !visibleTextAvailable)
return text.text;
}
@@ -157,8 +162,8 @@ bool selectionBelongsToObject(AccessibilityObject* coreObject, VisibleSelection&
Node* node = coreObject->node();
Node* lastDescendant = node->lastDescendant();
return (range->intersectsNode(node, IGNORE_EXCEPTION)
- && (range->endContainer() != node || range->endOffset())
- && (range->startContainer() != lastDescendant || range->startOffset() != lastOffsetInNode(lastDescendant)));
+ && (&range->endContainer() != node || range->endOffset())
+ && (&range->startContainer() != lastDescendant || range->startOffset() != lastOffsetInNode(lastDescendant)));
}
#endif
diff --git a/Source/WebCore/accessibility/atk/WebKitAccessibleUtil.h b/Source/WebCore/accessibility/atk/WebKitAccessibleUtil.h
index 35c78f379..e2938a597 100644
--- a/Source/WebCore/accessibility/atk/WebKitAccessibleUtil.h
+++ b/Source/WebCore/accessibility/atk/WebKitAccessibleUtil.h
@@ -33,9 +33,35 @@ class IntRect;
class VisibleSelection;
}
+// An existing accessibility object is considered to be invalid whether it's already
+// detached or if it's not but just updating the layout will detach it anyway.
+#define returnIfWebKitAccessibleIsInvalid(webkitAccessible) G_STMT_START { \
+ if (!webkitAccessible || webkitAccessibleIsDetached(webkitAccessible)) { \
+ return; \
+ } else { \
+ AccessibilityObject* coreObject = webkitAccessibleGetAccessibilityObject(webkitAccessible); \
+ if (!coreObject || !coreObject->document()) \
+ return; \
+ coreObject->updateBackingStore(); \
+ if (webkitAccessibleIsDetached(webkitAccessible)) \
+ return; \
+ }; } G_STMT_END
+
+#define returnValIfWebKitAccessibleIsInvalid(webkitAccessible, val) G_STMT_START { \
+ if (!webkitAccessible || webkitAccessibleIsDetached(webkitAccessible)) { \
+ return (val); \
+ } else { \
+ AccessibilityObject* coreObject = webkitAccessibleGetAccessibilityObject(webkitAccessible); \
+ if (!coreObject || !coreObject->document()) \
+ return (val); \
+ coreObject->updateBackingStore(); \
+ if (webkitAccessibleIsDetached(webkitAccessible)) \
+ return (val); \
+ }; } G_STMT_END
+
AtkAttributeSet* addToAtkAttributeSet(AtkAttributeSet*, const char* name, const char* value);
-void contentsRelativeToAtkCoordinateType(WebCore::AccessibilityObject*, AtkCoordType, WebCore::IntRect, gint* x, gint* y, gint* width = 0, gint* height = 0);
+void contentsRelativeToAtkCoordinateType(WebCore::AccessibilityObject*, AtkCoordType, WebCore::IntRect, gint* x, gint* y, gint* width = nullptr, gint* height = nullptr);
String accessibilityTitle(WebCore::AccessibilityObject*);
diff --git a/Source/WebCore/accessibility/atk/WebKitAccessibleWrapperAtk.cpp b/Source/WebCore/accessibility/atk/WebKitAccessibleWrapperAtk.cpp
index 679a73915..353b0b1b8 100644
--- a/Source/WebCore/accessibility/atk/WebKitAccessibleWrapperAtk.cpp
+++ b/Source/WebCore/accessibility/atk/WebKitAccessibleWrapperAtk.cpp
@@ -35,6 +35,9 @@
#if HAVE(ACCESSIBILITY)
#include "AXObjectCache.h"
+#include "AccessibilityList.h"
+#include "AccessibilityListBoxOption.h"
+#include "AccessibilityTable.h"
#include "Document.h"
#include "Frame.h"
#include "FrameView.h"
@@ -55,6 +58,7 @@
#include "WebKitAccessibleInterfaceImage.h"
#include "WebKitAccessibleInterfaceSelection.h"
#include "WebKitAccessibleInterfaceTable.h"
+#include "WebKitAccessibleInterfaceTableCell.h"
#include "WebKitAccessibleInterfaceText.h"
#include "WebKitAccessibleInterfaceValue.h"
#include "WebKitAccessibleUtil.h"
@@ -62,10 +66,6 @@
#include <glib/gprintf.h>
#include <wtf/text/CString.h>
-#if PLATFORM(GTK)
-#include <gtk/gtk.h>
-#endif
-
using namespace WebCore;
struct _WebKitAccessiblePrivate {
@@ -91,120 +91,90 @@ struct _WebKitAccessiblePrivate {
static AccessibilityObject* fallbackObject()
{
- // FIXME: An AXObjectCache with a Document is meaningless.
- static AXObjectCache* fallbackCache = new AXObjectCache(0);
- static AccessibilityObject* object = 0;
- if (!object) {
- // FIXME: using fallbackCache->getOrCreate(ListBoxOptionRole) is a hack
- object = fallbackCache->getOrCreate(ListBoxOptionRole);
- object->ref();
- }
-
+ static AccessibilityObject* object = &AccessibilityListBoxOption::create().leakRef();
return object;
}
-static AccessibilityObject* core(WebKitAccessible* accessible)
-{
- if (!accessible)
- return 0;
-
- return accessible->m_object;
-}
-
static AccessibilityObject* core(AtkObject* object)
{
if (!WEBKIT_IS_ACCESSIBLE(object))
return 0;
- return core(WEBKIT_ACCESSIBLE(object));
+ return webkitAccessibleGetAccessibilityObject(WEBKIT_ACCESSIBLE(object));
}
static const gchar* webkitAccessibleGetName(AtkObject* object)
{
- AccessibilityObject* coreObject = core(object);
- if (!coreObject->isAccessibilityRenderObject())
- return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName, coreObject->stringValue());
-
- 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);
- }
+ g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), 0);
- // Try text under the node.
- String textUnder = coreObject->textUnderElement();
- if (textUnder.length())
- return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName, textUnder);
- }
+ Vector<AccessibilityText> textOrder;
+ core(object)->accessibilityText(textOrder);
- 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)
{
- AccessibilityObject* coreObject = core(object);
- Node* node = 0;
- if (coreObject->isAccessibilityRenderObject())
- node = coreObject->node();
- if (!node || !node->isHTMLElement() || coreObject->ariaRoleAttribute() != UnknownRole)
- 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);
+ g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), 0);
+
+ 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);
+
+ // 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);
+
+ nameTextAvailable = true;
}
- // 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);
+ return cacheAndReturnAtkProperty(object, AtkCachedAccessibleDescription, "");
+}
- return cacheAndReturnAtkProperty(object, AtkCachedAccessibleDescription, accessibilityDescription(coreObject));
+static void removeAtkRelationByType(AtkRelationSet* relationSet, AtkRelationType relationType)
+{
+ int count = atk_relation_set_get_n_relations(relationSet);
+ for (int i = 0; i < count; i++) {
+ AtkRelation* relation = atk_relation_set_get_relation(relationSet, i);
+ if (atk_relation_get_relation_type(relation) == relationType) {
+ atk_relation_set_remove(relationSet, relation);
+ break;
+ }
+ }
}
static void setAtkRelationSetFromCoreObject(AccessibilityObject* coreObject, AtkRelationSet* relationSet)
{
if (coreObject->isFieldset()) {
AccessibilityObject* label = coreObject->titleUIElement();
- if (label)
+ if (label) {
+ removeAtkRelationByType(relationSet, ATK_RELATION_LABELLED_BY);
atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABELLED_BY, label->wrapper());
+ }
return;
}
@@ -220,16 +190,45 @@ static void setAtkRelationSetFromCoreObject(AccessibilityObject* coreObject, Atk
if (coreObject->isControl()) {
AccessibilityObject* label = coreObject->correspondingLabelForControlElement();
- if (label)
+ if (label) {
+ removeAtkRelationByType(relationSet, ATK_RELATION_LABELLED_BY);
atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABELLED_BY, label->wrapper());
+ }
} else {
AccessibilityObject* control = coreObject->correspondingControlForLabelElement();
if (control)
atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABEL_FOR, control->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());
+ }
+
+ // Check whether object supports aria-controls. It provides information about elements that are controlled by the current object.
+ if (coreObject->supportsARIAControls()) {
+ 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)
{
@@ -257,18 +256,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)
@@ -279,6 +266,9 @@ static AtkObject* atkParentOfRootObject(AtkObject* object)
static AtkObject* webkitAccessibleGetParent(AtkObject* object)
{
+ g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), 0);
+
// Check first if the parent has been already set.
AtkObject* accessibleParent = ATK_OBJECT_CLASS(webkitAccessibleParentClass)->get_parent(object);
if (accessibleParent)
@@ -297,88 +287,34 @@ 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)
-{
- AccessibilityObject::AccessibilityChildrenVector tableChildren = coreObject->children();
- size_t tableChildrenCount = tableChildren.size();
- size_t cellsCount = 0;
-
- // Look for the actual index of the cell inside the table.
- for (unsigned i = 0; i < tableChildrenCount; ++i) {
- if (tableChildren[i]->isTableRow()) {
- AccessibilityObject::AccessibilityChildrenVector rowChildren = tableChildren[i]->children();
- cellsCount += rowChildren.size();
- } else
- cellsCount++;
- }
-
- return cellsCount;
-}
-
static gint webkitAccessibleGetNChildren(AtkObject* object)
{
- AccessibilityObject* coreObject = core(object);
+ g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), 0);
- // Tables should be treated in a different way because rows should
- // be bypassed when exposing the accessible hierarchy.
- if (coreObject->isAccessibilityTable())
- return getNChildrenForTable(coreObject);
+ AccessibilityObject* coreObject = core(object);
return coreObject->children().size();
}
-static AccessibilityObject* getChildForTable(AccessibilityObject* coreObject, gint index)
-{
- AccessibilityObject::AccessibilityChildrenVector tableChildren = coreObject->children();
- size_t tableChildrenCount = tableChildren.size();
- size_t cellsCount = 0;
-
- // Look for the actual index of the cell inside the table.
- size_t current = static_cast<size_t>(index);
- for (unsigned i = 0; i < tableChildrenCount; ++i) {
- if (tableChildren[i]->isTableRow()) {
- AccessibilityObject::AccessibilityChildrenVector rowChildren = tableChildren[i]->children();
- size_t rowChildrenCount = rowChildren.size();
- if (current < cellsCount + rowChildrenCount)
- return rowChildren.at(current - cellsCount).get();
- cellsCount += rowChildrenCount;
- } else if (cellsCount == current)
- return tableChildren[i].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);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), 0);
+
if (index < 0)
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 {
- 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;
@@ -390,43 +326,11 @@ 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;
-
- AccessibilityObject::AccessibilityChildrenVector rows = grandParent->children();
- size_t rowsCount = rows.size();
- size_t previousCellsCount = 0;
-
- // Look for the actual index of the cell inside the table.
- for (unsigned i = 0; i < rowsCount; ++i) {
- if (!rows[i]->isTableRow())
- continue;
-
- AccessibilityObject::AccessibilityChildrenVector cells = rows[i]->children();
- size_t cellsCount = cells.size();
-
- if (rows[i] == 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);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), -1);
+
AccessibilityObject* coreObject = core(object);
AccessibilityObject* parent = coreObject->parentObjectUnignored();
@@ -445,11 +349,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;
@@ -459,7 +358,10 @@ static gint webkitAccessibleGetIndexInParent(AtkObject* object)
static AtkAttributeSet* webkitAccessibleGetAttributes(AtkObject* object)
{
- AtkAttributeSet* attributeSet = 0;
+ g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), 0);
+
+ AtkAttributeSet* attributeSet = nullptr;
#if PLATFORM(GTK)
attributeSet = addToAtkAttributeSet(attributeSet, "toolkit", "WebKitGtk");
#elif PLATFORM(EFL)
@@ -473,9 +375,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());
}
@@ -486,9 +391,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();
@@ -496,18 +408,71 @@ static AtkAttributeSet* webkitAccessibleGetAttributes(AtkObject* object)
attributeSet = addToAtkAttributeSet(attributeSet, "placeholder-text", placeholder.utf8().data());
if (coreObject->ariaHasPopup())
- attributeSet = addToAtkAttributeSet(attributeSet, "aria-haspopup", "true");
+ attributeSet = addToAtkAttributeSet(attributeSet, "haspopup", "true");
+
+ AccessibilitySortDirection sortDirection = coreObject->sortDirection();
+ if (sortDirection != SortDirectionNone) {
+ // WAI-ARIA spec says to translate the value as is from the attribute.
+ const AtomicString& sortAttribute = coreObject->getAttribute(HTMLNames::aria_sortAttr);
+ attributeSet = addToAtkAttributeSet(attributeSet, "sort", sortAttribute.string().utf8().data());
+ }
+
+ 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());
+ }
return attributeSet;
}
-static AtkRole atkRole(AccessibilityRole role)
+static AtkRole atkRole(AccessibilityObject* coreObject)
{
+ AccessibilityRole role = coreObject->roleValue();
switch (role) {
+ case ApplicationAlertDialogRole:
+ case ApplicationAlertRole:
+ return ATK_ROLE_ALERT;
+ case ApplicationDialogRole:
+ return ATK_ROLE_DIALOG;
+ case ApplicationStatusRole:
+ return ATK_ROLE_STATUSBAR;
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:
@@ -521,11 +486,15 @@ static AtkRole atkRole(AccessibilityRole role)
return ATK_ROLE_PAGE_TAB_LIST;
case TextFieldRole:
case TextAreaRole:
+ case SearchFieldRole:
return ATK_ROLE_ENTRY;
case StaticTextRole:
return ATK_ROLE_TEXT;
case OutlineRole:
+ case TreeRole:
return ATK_ROLE_TREE;
+ case TreeItemRole:
+ return ATK_ROLE_TREE_ITEM;
case MenuBarRole:
return ATK_ROLE_MENU_BAR;
case MenuListPopupRole:
@@ -534,19 +503,21 @@ static AtkRole atkRole(AccessibilityRole role)
case MenuListOptionRole:
case MenuItemRole:
return ATK_ROLE_MENU_ITEM;
+ case MenuItemCheckboxRole:
+ return ATK_ROLE_CHECK_MENU_ITEM;
+ case MenuItemRadioRole:
+ return ATK_ROLE_RADIO_MENU_ITEM;
case ColumnRole:
// 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:
@@ -555,45 +526,64 @@ static AtkRole atkRole(AccessibilityRole role)
case SplitGroupRole:
return ATK_ROLE_SPLIT_PANE;
case SplitterRole:
- return ATK_ROLE_UNKNOWN;
+ return ATK_ROLE_SEPARATOR;
case ColorWellRole:
+#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;
+#elif PLATFORM(EFL)
return ATK_ROLE_COLOR_CHOOSER;
+#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 DocumentRegionRole:
case RadioGroupRole:
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:
return ATK_ROLE_TEXT;
- case WebAreaRole:
- // return ATK_ROLE_HTML_CONTAINER; // Is this right?
+ case DocumentArticleRole:
+#if ATK_CHECK_VERSION(2, 11, 3)
+ return ATK_ROLE_ARTICLE;
+#endif
+ case DocumentRole:
return ATK_ROLE_DOCUMENT_FRAME;
+ case DocumentNoteRole:
+ return ATK_ROLE_COMMENT;
case HeadingRole:
return ATK_ROLE_HEADING;
case ListBoxRole:
- return ATK_ROLE_LIST;
+ return ATK_ROLE_LIST_BOX;
case ListItemRole:
+ return coreObject->inheritsPresentationalRole() ? ATK_ROLE_SECTION : ATK_ROLE_LIST_ITEM;
case ListBoxOptionRole:
return ATK_ROLE_LIST_ITEM;
case ParagraphRole:
@@ -601,8 +591,15 @@ static AtkRole atkRole(AccessibilityRole role)
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:
return ATK_ROLE_SECTION;
+ case FooterRole:
+ return ATK_ROLE_FOOTER;
case FormRole:
return ATK_ROLE_FORM;
case CanvasRole:
@@ -613,6 +610,71 @@ static AtkRole atkRole(AccessibilityRole role)
return ATK_ROLE_SPIN_BUTTON;
case TabRole:
return ATK_ROLE_PAGE_TAB;
+ case UserInterfaceTooltipRole:
+ return ATK_ROLE_TOOL_TIP;
+ case WebAreaRole:
+ return ATK_ROLE_DOCUMENT_WEB;
+ case LandmarkApplicationRole:
+ return ATK_ROLE_EMBEDDED;
+#if ATK_CHECK_VERSION(2, 11, 3)
+ case ApplicationLogRole:
+ return ATK_ROLE_LOG;
+ case ApplicationMarqueeRole:
+ return ATK_ROLE_MARQUEE;
+ case ApplicationTimerRole:
+ return ATK_ROLE_TIMER;
+ case DefinitionRole:
+ 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 LandmarkSearchRole:
+ return ATK_ROLE_LANDMARK;
+#endif
+#if ATK_CHECK_VERSION(2, 11, 4)
+ case DescriptionListRole:
+ return ATK_ROLE_DESCRIPTION_LIST;
+ case DescriptionListTermRole:
+ return ATK_ROLE_DESCRIPTION_TERM;
+ case DescriptionListDetailRole:
+ return ATK_ROLE_DESCRIPTION_VALUE;
+#endif
+#if ATK_CHECK_VERSION(2, 15, 2)
+ case InlineRole:
+ return ATK_ROLE_STATIC;
+#endif
default:
return ATK_ROLE_UNKNOWN;
}
@@ -620,16 +682,21 @@ static AtkRole atkRole(AccessibilityRole role)
static AtkRole webkitAccessibleGetRole(AtkObject* object)
{
+ // ATK_ROLE_UNKNOWN should only be applied in cases where there is a valid
+ // WebCore accessible object for which the platform role mapping is unknown.
+ g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), ATK_ROLE_INVALID);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), ATK_ROLE_INVALID);
+
AccessibilityObject* coreObject = core(object);
if (!coreObject)
- return ATK_ROLE_UNKNOWN;
+ return ATK_ROLE_INVALID;
// Note: Why doesn't WebCore have a password field for this
if (coreObject->isPasswordField())
return ATK_ROLE_PASSWORD_TEXT;
- return atkRole(coreObject->roleValue());
+ return atkRole(coreObject);
}
static bool isTextWithCaret(AccessibilityObject* coreObject)
@@ -645,8 +712,7 @@ static bool isTextWithCaret(AccessibilityObject* coreObject)
if (!frame)
return false;
- Settings* settings = frame->settings();
- if (!settings || !settings->caretBrowsingEnabled())
+ if (!frame->settings().caretBrowsingEnabled())
return false;
// Check text objects and paragraphs only.
@@ -669,16 +735,18 @@ static void setAtkStateSetFromCoreObject(AccessibilityObject* coreObject, AtkSta
bool isListBoxOption = parent && parent->isListBox();
// Please keep the state list in alphabetical order
+ if (isListBoxOption && coreObject->isSelectedOptionActive())
+ atk_state_set_add_state(stateSet, ATK_STATE_ACTIVE);
+
+#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
@@ -707,6 +775,14 @@ static void setAtkStateSetFromCoreObject(AccessibilityObject* coreObject, AtkSta
if (coreObject->isIndeterminate())
atk_state_set_add_state(stateSet, ATK_STATE_INDETERMINATE);
+ if (coreObject->isCheckboxOrRadio() || coreObject->isMenuItem()) {
+ if (coreObject->checkboxOrRadioValue() == ButtonStateMixed)
+ atk_state_set_add_state(stateSet, ATK_STATE_INDETERMINATE);
+ }
+
+ if (coreObject->invalidStatus() != "false")
+ atk_state_set_add_state(stateSet, ATK_STATE_INVALID_ENTRY);
+
if (coreObject->isMultiSelectable())
atk_state_set_add_state(stateSet, ATK_STATE_MULTISELECTABLE);
@@ -715,6 +791,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);
@@ -761,9 +842,15 @@ static void setAtkStateSetFromCoreObject(AccessibilityObject* coreObject, AtkSta
static AtkStateSet* webkitAccessibleRefStateSet(AtkObject* object)
{
+ g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0);
+
AtkStateSet* stateSet = ATK_OBJECT_CLASS(webkitAccessibleParentClass)->ref_state_set(object);
AccessibilityObject* coreObject = core(object);
+ // Make sure the layout is updated to really know whether the object
+ // is defunct or not, so we can return the proper state.
+ coreObject->updateBackingStore();
+
if (coreObject == fallbackObject()) {
atk_state_set_add_state(stateSet, ATK_STATE_DEFUNCT);
return stateSet;
@@ -780,6 +867,9 @@ static AtkStateSet* webkitAccessibleRefStateSet(AtkObject* object)
static AtkRelationSet* webkitAccessibleRefRelationSet(AtkObject* object)
{
+ g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), 0);
+
AtkRelationSet* relationSet = ATK_OBJECT_CLASS(webkitAccessibleParentClass)->ref_relation_set(object);
AccessibilityObject* coreObject = core(object);
@@ -800,18 +890,21 @@ static void webkitAccessibleInit(AtkObject* object, gpointer data)
static const gchar* webkitAccessibleGetObjectLocale(AtkObject* object)
{
- if (ATK_IS_DOCUMENT(object)) {
- AccessibilityObject* coreObject = core(object);
- if (!coreObject)
- return 0;
+ g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), 0);
+ AccessibilityObject* coreObject = core(object);
+ if (!coreObject)
+ return 0;
+
+ if (ATK_IS_DOCUMENT(object)) {
// TODO: Should we fall back on lang xml:lang when the following comes up empty?
String language = coreObject->language();
if (!language.isEmpty())
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) {
@@ -892,6 +985,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},
@@ -899,43 +995,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;
}
@@ -944,7 +1047,9 @@ static GType GetAtkInterfaceTypeFromWAIType(WAIType type)
static bool roleIsTextType(AccessibilityRole role)
{
- return role == ParagraphRole || role == HeadingRole || role == DivRole || role == CellRole || role == ListItemRole;
+ return role == ParagraphRole || role == HeadingRole || role == DivRole || role == CellRole
+ || role == LinkRole || role == WebCoreLinkRole || role == ListItemRole || role == PreRole
+ || role == GridCellRole;
}
static guint16 getInterfaceMaskFromObject(AccessibilityObject* coreObject)
@@ -952,7 +1057,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();
@@ -962,63 +1067,72 @@ 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;
+ 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) {
- 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)
- interfaceMask |= 1 << WAI_VALUE;
+ if (role == SliderRole || role == SpinButtonRole || role == ScrollBarRole || role == ProgressIndicatorRole)
+ interfaceMask |= 1 << WAIValue;
+
+#if ENABLE(INPUT_TYPE_COLOR)
+ // Color type.
+ if (role == ColorWellRole)
+ interfaceMask |= 1 << WAIText;
+#endif
return interfaceMask;
}
@@ -1085,8 +1199,8 @@ void webkitAccessibleDetach(WebKitAccessible* accessible)
{
ASSERT(accessible->m_object);
- if (core(accessible)->roleValue() == WebAreaRole)
- g_signal_emit_by_name(accessible, "state-change", "defunct", true);
+ if (accessible->m_object->roleValue() == WebAreaRole)
+ atk_object_notify_state_change(ATK_OBJECT(accessible), ATK_STATE_DEFUNCT, true);
// We replace the WebCore AccessibilityObject with a fallback object that
// provides default implementations to avoid repetitive null-checking after
@@ -1094,16 +1208,10 @@ void webkitAccessibleDetach(WebKitAccessible* accessible)
accessible->m_object = fallbackObject();
}
-AtkObject* webkitAccessibleGetFocusedElement(WebKitAccessible* accessible)
+bool webkitAccessibleIsDetached(WebKitAccessible* accessible)
{
- if (!accessible->m_object)
- return 0;
-
- RefPtr<AccessibilityObject> focusedObj = accessible->m_object->focusedUIElement();
- if (!focusedObj)
- return 0;
-
- return focusedObj->wrapper();
+ ASSERT(accessible->m_object);
+ return accessible->m_object == fallbackObject();
}
AccessibilityObject* objectFocusedAndCaretOffsetUnignored(AccessibilityObject* referenceObject, int& offset)
@@ -1134,8 +1242,10 @@ AccessibilityObject* objectFocusedAndCaretOffsetUnignored(AccessibilityObject* r
if (!firstUnignoredParent)
return 0;
- // Don't ignore links if the offset is being requested for a link.
- if (!referenceObject->isLink() && firstUnignoredParent->isLink())
+ // Don't ignore links if the offset is being requested for a link
+ // or if the link is a block.
+ if (!referenceObject->isLink() && firstUnignoredParent->isLink()
+ && !(firstUnignoredParent->renderer() && !firstUnignoredParent->renderer()->isInline()))
firstUnignoredParent = firstUnignoredParent->parentObjectUnignored();
if (!firstUnignoredParent)
return 0;
@@ -1145,7 +1255,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
@@ -1187,7 +1297,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:
diff --git a/Source/WebCore/accessibility/atk/WebKitAccessibleWrapperAtk.h b/Source/WebCore/accessibility/atk/WebKitAccessibleWrapperAtk.h
index 3080aeb93..ae3a0bea0 100644
--- a/Source/WebCore/accessibility/atk/WebKitAccessibleWrapperAtk.h
+++ b/Source/WebCore/accessibility/atk/WebKitAccessibleWrapperAtk.h
@@ -77,7 +77,7 @@ WebCore::AccessibilityObject* webkitAccessibleGetAccessibilityObject(WebKitAcces
void webkitAccessibleDetach(WebKitAccessible*);
-AtkObject* webkitAccessibleGetFocusedElement(WebKitAccessible*);
+bool webkitAccessibleIsDetached(WebKitAccessible*);
WebCore::AccessibilityObject* objectFocusedAndCaretOffsetUnignored(WebCore::AccessibilityObject*, int& offset);
diff --git a/Source/WebCore/accessibility/ios/AXObjectCacheIOS.mm b/Source/WebCore/accessibility/ios/AXObjectCacheIOS.mm
deleted file mode 100644
index 479a2ca5b..000000000
--- a/Source/WebCore/accessibility/ios/AXObjectCacheIOS.mm
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2010 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#import "config.h"
-#import "AXObjectCache.h"
-
-#if HAVE(ACCESSIBILITY) && PLATFORM(IOS)
-
-#import "AccessibilityObject.h"
-#import "WebAccessibilityObjectWrapperIOS.h"
-#import "RenderObject.h"
-
-#import <wtf/PassRefPtr.h>
-#import <wtf/RetainPtr.h>
-
-namespace WebCore {
-
-void AXObjectCache::detachWrapper(AccessibilityObject* obj)
-{
- [obj->wrapper() detach];
- obj->setWrapper(0);
-}
-
-void AXObjectCache::attachWrapper(AccessibilityObject* obj)
-{
- RetainPtr<AccessibilityObjectWrapper> wrapper = adoptNS([[WebAccessibilityObjectWrapper alloc] initWithAccessibilityObject:obj]);
- obj->setWrapper(wrapper.get());
-}
-
-void AXObjectCache::postPlatformNotification(AccessibilityObject* obj, AXNotification notification)
-{
- if (!obj)
- return;
-
- NSString *notificationString = nil;
- switch (notification) {
- case AXActiveDescendantChanged:
- case AXFocusedUIElementChanged:
- [obj->wrapper() postFocusChangeNotification];
- notificationString = @"AXFocusChanged";
- break;
- case AXSelectedTextChanged:
- [obj->wrapper() postSelectedTextChangeNotification];
- break;
- case AXLayoutComplete:
- [obj->wrapper() postLayoutChangeNotification];
- break;
- case AXLiveRegionChanged:
- [obj->wrapper() postLiveRegionChangeNotification];
- break;
- case AXChildrenChanged:
- [obj->wrapper() postChildrenChangedNotification];
- break;
- case AXLoadComplete:
- [obj->wrapper() postLoadCompleteNotification];
- break;
- case AXInvalidStatusChanged:
- [obj->wrapper() postInvalidStatusChangedNotification];
- break;
- case AXSelectedChildrenChanged:
- case AXValueChanged:
- case AXCheckedStateChanged:
- default:
- break;
- }
-
- // Used by DRT to know when notifications are posted.
- [obj->wrapper() accessibilityPostedNotification:notificationString];
-}
-
-void AXObjectCache::nodeTextChangePlatformNotification(AccessibilityObject*, AXTextChange, unsigned, const String&)
-{
-}
-
-void AXObjectCache::frameLoadingEventPlatformNotification(AccessibilityObject*, AXLoadingEvent)
-{
-}
-
-void AXObjectCache::handleFocusedUIElementChanged(Node*, Node* newNode)
-{
- postNotification(newNode, AXFocusedUIElementChanged, true, PostAsynchronously);
-}
-
-void AXObjectCache::handleScrolledToAnchor(const Node*)
-{
-}
-
-}
-
-#endif // HAVE(ACCESSIBILITY) && PLATFORM(IOS)
diff --git a/Source/WebCore/accessibility/ios/AccessibilityObjectIOS.mm b/Source/WebCore/accessibility/ios/AccessibilityObjectIOS.mm
deleted file mode 100644
index dcde88ace..000000000
--- a/Source/WebCore/accessibility/ios/AccessibilityObjectIOS.mm
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2009 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#import "config.h"
-#import "AccessibilityObject.h"
-#import "AccessibilityRenderObject.h"
-#import "HTMLInputElement.h"
-#import "RenderObject.h"
-#import "WAKView.h"
-
-#if HAVE(ACCESSIBILITY) && PLATFORM(IOS)
-
-#import "WebAccessibilityObjectWrapperIOS.h"
-
-@interface WAKView (iOSAccessibility)
-- (BOOL)accessibilityIsIgnored;
-@end
-
-@implementation WAKView (iOSAccessibility)
-
-- (BOOL)accessibilityIsIgnored
-{
- return YES;
-}
-
-@end
-
-namespace WebCore {
-
-void AccessibilityObject::detachFromParent()
-{
-}
-
-void AccessibilityObject::overrideAttachmentParent(AccessibilityObject*)
-{
-}
-
-// In iPhone only code for now. It's debateable whether this is desired on all platforms.
-int AccessibilityObject::accessibilityPasswordFieldLength()
-{
- if (!isPasswordField())
- return 0;
- RenderObject* renderObject = static_cast<AccessibilityRenderObject*>(this)->renderer();
-
- if (!renderObject || !renderObject->node() || !renderObject->node()->isHTMLElement())
- return false;
-
- HTMLInputElement* inputElement = toHTMLInputElement(renderObject->node());
- if (!inputElement)
- return false;
-
- return inputElement->value().length();
-}
-
-bool AccessibilityObject::accessibilityIgnoreAttachment() const
-{
- return [[wrapper() attachmentView] accessibilityIsIgnored];
-}
-
-AccessibilityObjectInclusion AccessibilityObject::accessibilityPlatformIncludesObject() const
-{
- return DefaultBehavior;
-}
-
-} // WebCore
-
-#endif // HAVE(ACCESSIBILITY) && PLATFORM(IOS)
diff --git a/Source/WebCore/accessibility/ios/WebAccessibilityObjectWrapperIOS.h b/Source/WebCore/accessibility/ios/WebAccessibilityObjectWrapperIOS.h
deleted file mode 100644
index b750201eb..000000000
--- a/Source/WebCore/accessibility/ios/WebAccessibilityObjectWrapperIOS.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2008, Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef AccessibilityObjectWrapperIOS_h
-#define AccessibilityObjectWrapperIOS_h
-
-#if HAVE(ACCESSIBILITY) && PLATFORM(IOS)
-
-#include "AXObjectCache.h"
-#include "AccessibilityObject.h"
-#include "WebAccessibilityObjectWrapperBase.h"
-
-@interface WebAccessibilityObjectWrapper : WebAccessibilityObjectWrapperBase {
- // Cached data to avoid frequent re-computation.
- int m_isAccessibilityElement;
- uint64_t m_accessibilityTraitsFromAncestor;
-}
-
-- (id)accessibilityHitTest:(CGPoint)point;
-- (AccessibilityObjectWrapper *)accessibilityPostProcessHitTest:(CGPoint)point;
-- (BOOL)accessibilityCanFuzzyHitTest;
-
-- (BOOL)isAccessibilityElement;
-- (NSString *)accessibilityLabel;
-- (CGRect)accessibilityFrame;
-- (NSString *)accessibilityValue;
-
-- (NSInteger)accessibilityElementCount;
-- (id)accessibilityElementAtIndex:(NSInteger)index;
-- (NSInteger)indexOfAccessibilityElement:(id)element;
-
-- (BOOL)isAttachment;
-
-- (void)postFocusChangeNotification;
-- (void)postSelectedTextChangeNotification;
-- (void)postLayoutChangeNotification;
-- (void)postLiveRegionChangeNotification;
-- (void)postLoadCompleteNotification;
-- (void)postChildrenChangedNotification;
-- (void)postInvalidStatusChangedNotification;
-
-@end
-
-#endif // HAVE(ACCESSIBILITY) && PLATFORM(IOS)
-
-#endif // AccessibilityObjectWrapperIOS_h
diff --git a/Source/WebCore/accessibility/ios/WebAccessibilityObjectWrapperIOS.mm b/Source/WebCore/accessibility/ios/WebAccessibilityObjectWrapperIOS.mm
deleted file mode 100644
index bc35baa20..000000000
--- a/Source/WebCore/accessibility/ios/WebAccessibilityObjectWrapperIOS.mm
+++ /dev/null
@@ -1,2242 +0,0 @@
-/*
- * Copyright (C) 2008, Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#import "config.h"
-#import "WebAccessibilityObjectWrapperIOS.h"
-
-#if HAVE(ACCESSIBILITY) && PLATFORM(IOS)
-
-#import "AccessibilityRenderObject.h"
-#import "AccessibilityTable.h"
-#import "AccessibilityTableCell.h"
-#import "Font.h"
-#import "Frame.h"
-#import "FrameSelection.h"
-#import "FrameView.h"
-#import "HitTestResult.h"
-#import "HTMLFrameOwnerElement.h"
-#import "HTMLInputElement.h"
-#import "HTMLNames.h"
-#import "IntRect.h"
-#import "IntSize.h"
-#import "Range.h"
-#import "RenderView.h"
-#import "RuntimeApplicationChecksIOS.h"
-#import "SVGNames.h"
-#import "TextIterator.h"
-#import "WAKScrollView.h"
-#import "WAKView.h"
-#import "WAKWindow.h"
-#import "WebCoreThread.h"
-#import "visible_units.h"
-
-#import <GraphicsServices/GraphicsServices.h>
-
-@interface NSObject (AccessibilityPrivate)
-- (void)_accessibilityUnregister;
-- (NSString *)accessibilityLabel;
-- (NSString *)accessibilityValue;
-- (BOOL)isAccessibilityElement;
-- (NSInteger)accessibilityElementCount;
-- (id)accessibilityElementAtIndex:(NSInteger)index;
-- (NSInteger)indexOfAccessibilityElement:(id)element;
-@end
-
-@interface WebAccessibilityObjectWrapper (AccessibilityPrivate)
-- (id)_accessibilityWebDocumentView;
-- (id)accessibilityContainer;
-- (void)setAccessibilityLabel:(NSString *)label;
-- (void)setAccessibilityValue:(NSString *)value;
-- (BOOL)containsUnnaturallySegmentedChildren;
-- (NSInteger)positionForTextMarker:(id)marker;
-@end
-
-@interface WAKView (iOSAccessibility)
-- (BOOL)accessibilityIsIgnored;
-@end
-
-using namespace WebCore;
-using namespace HTMLNames;
-
-// These are tokens accessibility uses to denote attributes.
-static NSString * const UIAccessibilityTokenBlockquoteLevel = @"UIAccessibilityTokenBlockquoteLevel";
-static NSString * const UIAccessibilityTokenHeadingLevel = @"UIAccessibilityTokenHeadingLevel";
-static NSString * const UIAccessibilityTokenFontName = @"UIAccessibilityTokenFontName";
-static NSString * const UIAccessibilityTokenFontFamily = @"UIAccessibilityTokenFontFamily";
-static NSString * const UIAccessibilityTokenFontSize = @"UIAccessibilityTokenFontSize";
-static NSString * const UIAccessibilityTokenBold = @"UIAccessibilityTokenBold";
-static NSString * const UIAccessibilityTokenItalic = @"UIAccessibilityTokenItalic";
-static NSString * const UIAccessibilityTokenUnderline = @"UIAccessibilityTokenUnderline";
-
-static AccessibilityObjectWrapper* AccessibilityUnignoredAncestor(AccessibilityObjectWrapper *wrapper)
-{
- while (wrapper && ![wrapper isAccessibilityElement]) {
- AccessibilityObject* object = [wrapper accessibilityObject];
- if (!object)
- break;
-
- if ([wrapper isAttachment] && ![[wrapper attachmentView] accessibilityIsIgnored])
- break;
-
- AccessibilityObject* parentObject = object->parentObjectUnignored();
- if (!parentObject)
- break;
-
- wrapper = parentObject->wrapper();
- }
- return wrapper;
-}
-
-#pragma mark Accessibility Text Marker
-
-@interface WebAccessibilityTextMarker : NSObject
-{
- AXObjectCache* _cache;
- TextMarkerData _textMarkerData;
-}
-
-+ (WebAccessibilityTextMarker *)textMarkerWithVisiblePosition:(VisiblePosition&)visiblePos cache:(AXObjectCache*)cache;
-
-@end
-
-@implementation WebAccessibilityTextMarker
-
-- (id)initWithTextMarker:(TextMarkerData *)data cache:(AXObjectCache*)cache
-{
- if (!(self = [super init]))
- return nil;
-
- _cache = cache;
- memcpy(&_textMarkerData, data, sizeof(TextMarkerData));
- return self;
-}
-
-- (id)initWithData:(NSData *)data cache:(AXObjectCache*)cache
-{
- if (!(self = [super init]))
- return nil;
-
- _cache = cache;
- [data getBytes:&_textMarkerData length:sizeof(TextMarkerData)];
-
- return self;
-}
-
-// This is needed for external clients to be able to create a text marker without having a pointer to the cache.
-- (id)initWithData:(NSData *)data accessibilityObject:(AccessibilityObjectWrapper *)wrapper
-{
- WebCore::AccessibilityObject* axObject = [wrapper accessibilityObject];
- if (!axObject)
- return nil;
-
- return [self initWithData:data cache:axObject->axObjectCache()];
-}
-
-+ (WebAccessibilityTextMarker *)textMarkerWithVisiblePosition:(VisiblePosition&)visiblePos cache:(AXObjectCache*)cache
-{
- TextMarkerData textMarkerData;
- cache->textMarkerDataForVisiblePosition(textMarkerData, visiblePos);
-
- return [[[WebAccessibilityTextMarker alloc] initWithTextMarker:&textMarkerData cache:cache] autorelease];
-}
-
-- (NSData *)dataRepresentation
-{
- return [NSData dataWithBytes:&_textMarkerData length:sizeof(TextMarkerData)];
-}
-
-- (VisiblePosition)visiblePosition
-{
- return _cache->visiblePositionForTextMarkerData(_textMarkerData);
-}
-
-- (NSString *)description
-{
- return [NSString stringWithFormat:@"[AXTextMarker %p] = node: %p offset: %d", self, _textMarkerData.node, _textMarkerData.offset];
-}
-
-@end
-
-@implementation WebAccessibilityObjectWrapper
-
-- (id)initWithAccessibilityObject:(AccessibilityObject*)axObject
-{
- self = [super initWithAccessibilityObject:axObject];
- if (!self)
- return nil;
-
- // Initialize to a sentinel value.
- m_accessibilityTraitsFromAncestor = ULLONG_MAX;
- m_isAccessibilityElement = -1;
-
- return self;
-}
-
-- (void)detach
-{
- // rdar://8798960 Make sure the object is gone early, so that anything _accessibilityUnregister
- // does can't call back into the render tree.
- m_object = 0;
-
- if ([self respondsToSelector:@selector(_accessibilityUnregister)])
- [self _accessibilityUnregister];
-}
-
-- (void)dealloc
-{
- // We should have been detached before deallocated.
- ASSERT(!m_object);
- [super dealloc];
-}
-
-- (BOOL)_prepareAccessibilityCall
-{
- // rdar://7980318 if we start a call, then block in WebThreadLock(), then we're dealloced on another, thread, we could
- // crash, so we should retain ourself for the duration of usage here.
- [[self retain] autorelease];
-
- WebThreadLock();
-
- // If we came back from our thread lock and we were detached, we will no longer have an m_object.
- if (!m_object)
- return NO;
-
- m_object->updateBackingStore();
- if (!m_object)
- return NO;
-
- return YES;
-}
-
-// These are here so that we don't have to import AXRuntime.
-// The methods will be swizzled when the accessibility bundle is loaded.
-
-- (uint64_t)_axLinkTrait { return (1 << 0); }
-- (uint64_t)_axVisitedTrait { return (1 << 1); }
-- (uint64_t)_axHeaderTrait { return (1 << 2); }
-- (uint64_t)_axContainedByListTrait { return (1 << 3); }
-- (uint64_t)_axContainedByTableTrait { return (1 << 4); }
-- (uint64_t)_axContainedByLandmarkTrait { return (1 << 5); }
-- (uint64_t)_axWebContentTrait { return (1 << 6); }
-- (uint64_t)_axSecureTextFieldTrait { return (1 << 7); }
-- (uint64_t)_axTextEntryTrait { return (1 << 8); }
-- (uint64_t)_axHasTextCursorTrait { return (1 << 9); }
-- (uint64_t)_axTextOperationsAvailableTrait { return (1 << 10); }
-- (uint64_t)_axImageTrait { return (1 << 11); }
-- (uint64_t)_axTabButtonTrait { return (1 << 12); }
-- (uint64_t)_axButtonTrait { return (1 << 13); }
-- (uint64_t)_axToggleTrait { return (1 << 14); }
-- (uint64_t)_axPopupButtonTrait { return (1 << 15); }
-- (uint64_t)_axStaticTextTrait { return (1 << 16); }
-- (uint64_t)_axAdjustableTrait { return (1 << 17); }
-- (uint64_t)_axMenuItemTrait { return (1 << 18); }
-- (uint64_t)_axSelectedTrait { return (1 << 19); }
-- (uint64_t)_axNotEnabledTrait { return (1 << 20); }
-- (uint64_t)_axRadioButtonTrait { return (1 << 21); }
-
-- (BOOL)accessibilityCanFuzzyHitTest
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- AccessibilityRole role = m_object->roleValue();
- // Elements that can be returned when performing fuzzy hit testing.
- switch (role) {
- case ButtonRole:
- case CheckBoxRole:
- case ComboBoxRole:
- case DisclosureTriangleRole:
- case HeadingRole:
- case ImageMapLinkRole:
- case ImageRole:
- case LinkRole:
- case ListBoxRole:
- case ListBoxOptionRole:
- case MenuButtonRole:
- case MenuItemRole:
- case PopUpButtonRole:
- case RadioButtonRole:
- case ScrollBarRole:
- case SliderRole:
- case StaticTextRole:
- case TabRole:
- case TextFieldRole:
- return !m_object->accessibilityIsIgnored();
- default:
- return false;
- }
-}
-
-- (AccessibilityObjectWrapper *)accessibilityPostProcessHitTest:(CGPoint)point
-{
- UNUSED_PARAM(point);
- // The UIKit accessibility wrapper will override this and perform the post process hit test.
- return nil;
-}
-
-- (id)accessibilityHitTest:(CGPoint)point
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- // Try a fuzzy hit test first to find an accessible element.
- RefPtr<AccessibilityObject> axObject;
- {
- AXAttributeCacheEnabler enableCache(m_object->axObjectCache());
- axObject = m_object->accessibilityHitTest(IntPoint(point));
- }
-
- if (!axObject)
- return nil;
-
- // If this is a good accessible object to return, no extra work is required.
- if ([axObject->wrapper() accessibilityCanFuzzyHitTest])
- return AccessibilityUnignoredAncestor(axObject->wrapper());
-
- // Check to see if we can post-process this hit test to find a better candidate.
- AccessibilityObjectWrapper* wrapper = [axObject->wrapper() accessibilityPostProcessHitTest:point];
- if (wrapper)
- return AccessibilityUnignoredAncestor(wrapper);
-
- // Fall back to default behavior.
- return AccessibilityUnignoredAncestor(axObject->wrapper());
-}
-
-- (NSInteger)accessibilityElementCount
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- AXAttributeCacheEnabler enableCache(m_object->axObjectCache());
- if ([self isAttachment])
- return [[self attachmentView] accessibilityElementCount];
-
- return m_object->children().size();
-}
-
-- (id)accessibilityElementAtIndex:(NSInteger)index
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- AXAttributeCacheEnabler enableCache(m_object->axObjectCache());
- if ([self isAttachment])
- return [[self attachmentView] accessibilityElementAtIndex:index];
-
- AccessibilityObject::AccessibilityChildrenVector children = m_object->children();
- if (static_cast<unsigned>(index) >= children.size())
- return nil;
-
- AccessibilityObjectWrapper* wrapper = children[index]->wrapper();
- if (children[index]->isAttachment())
- return [wrapper attachmentView];
-
- return wrapper;
-}
-
-- (NSInteger)indexOfAccessibilityElement:(id)element
-{
- if (![self _prepareAccessibilityCall])
- return NSNotFound;
-
- AXAttributeCacheEnabler enableCache(m_object->axObjectCache());
- if ([self isAttachment])
- return [[self attachmentView] indexOfAccessibilityElement:element];
-
- AccessibilityObject::AccessibilityChildrenVector children = m_object->children();
- unsigned count = children.size();
- for (unsigned k = 0; k < count; ++k) {
- AccessibilityObjectWrapper* wrapper = children[k]->wrapper();
- if (wrapper == element || (children[k]->isAttachment() && [wrapper attachmentView] == element))
- return k;
- }
-
- return NSNotFound;
-}
-
-- (CGPathRef)_accessibilityPath
-{
- if (![self _prepareAccessibilityCall])
- return NULL;
-
- if (!m_object->supportsPath())
- return NULL;
-
- Path path = m_object->elementPath();
- if (path.isEmpty())
- return NULL;
-
- return [self convertPathToScreenSpace:path];
-}
-
-- (NSString *)accessibilityLanguage
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- return m_object->language();
-}
-
-- (BOOL)_accessibilityIsLandmarkRole:(AccessibilityRole)role
-{
- switch (role) {
- case LandmarkApplicationRole:
- case LandmarkBannerRole:
- case LandmarkComplementaryRole:
- case LandmarkContentInfoRole:
- case LandmarkMainRole:
- case LandmarkNavigationRole:
- case LandmarkSearchRole:
- return YES;
- default:
- return NO;
- }
-}
-
-- (AccessibilityObjectWrapper*)_accessibilityListAncestor
-{
- for (AccessibilityObject* parent = m_object->parentObject(); parent != nil; parent = parent->parentObject()) {
- AccessibilityRole role = parent->roleValue();
- if (role == ListRole || role == ListBoxRole)
- return parent->wrapper();
- }
-
- return nil;
-}
-
-- (AccessibilityObjectWrapper*)_accessibilityLandmarkAncestor
-{
- for (AccessibilityObject* parent = m_object->parentObject(); parent != nil; parent = parent->parentObject()) {
- if ([self _accessibilityIsLandmarkRole:parent->roleValue()])
- return parent->wrapper();
- }
-
- return nil;
-}
-
-- (AccessibilityObjectWrapper*)_accessibilityTableAncestor
-{
- for (AccessibilityObject* parent = m_object->parentObject(); parent != nil; parent = parent->parentObject()) {
- if (parent->roleValue() == TableRole)
- return parent->wrapper();
- }
-
- return nil;
-}
-
-- (uint64_t)_accessibilityTraitsFromAncestors
-{
- uint64_t traits = 0;
- AccessibilityRole role = m_object->roleValue();
-
- // Trait information also needs to be gathered from the parents above the object.
- // The parentObject is needed instead of the unignoredParentObject, because a table might be ignored, but information still needs to be gathered from it.
- for (AccessibilityObject* parent = m_object->parentObject(); parent != nil; parent = parent->parentObject()) {
- AccessibilityRole parentRole = parent->roleValue();
- if (parentRole == WebAreaRole)
- break;
-
- switch (parentRole) {
- case LinkRole:
- case WebCoreLinkRole:
- traits |= [self _axLinkTrait];
- if (parent->isVisited())
- traits |= [self _axVisitedTrait];
- break;
- case HeadingRole:
- {
- traits |= [self _axHeaderTrait];
- // If this object has the header trait, we should set the value
- // to the heading level. If it was a static text element, we need to store
- // the value as the label, because the heading level needs to the value.
- AccessibilityObjectWrapper* wrapper = parent->wrapper();
- if (role == StaticTextRole)
- [self setAccessibilityLabel:m_object->stringValue()];
- [self setAccessibilityValue:[wrapper accessibilityValue]];
- break;
- }
- case ListBoxRole:
- case ListRole:
- traits |= [self _axContainedByListTrait];
- break;
- case TableRole:
- traits |= [self _axContainedByTableTrait];
- break;
- default:
- if ([self _accessibilityIsLandmarkRole:parentRole])
- traits |= [self _axContainedByLandmarkTrait];
- break;
- }
- }
-
- return traits;
-}
-
-- (uint64_t)accessibilityTraits
-{
- if (![self _prepareAccessibilityCall])
- return 0;
-
- AccessibilityRole role = m_object->roleValue();
- uint64_t traits = [self _axWebContentTrait];
- switch (role) {
- case LinkRole:
- case WebCoreLinkRole:
- traits |= [self _axLinkTrait];
- if (m_object->isVisited())
- traits |= [self _axVisitedTrait];
- break;
- // TextFieldRole is intended to fall through to TextAreaRole, in order to pick up the text entry and text cursor traits.
- case TextFieldRole:
- if (m_object->isPasswordField())
- traits |= [self _axSecureTextFieldTrait];
- case TextAreaRole:
- traits |= [self _axTextEntryTrait];
- if (m_object->isFocused())
- traits |= ([self _axHasTextCursorTrait] | [self _axTextOperationsAvailableTrait]);
- break;
- case ImageRole:
- traits |= [self _axImageTrait];
- break;
- case TabRole:
- traits |= [self _axTabButtonTrait];
- break;
- case ButtonRole:
- traits |= [self _axButtonTrait];
- if (m_object->isPressed())
- traits |= [self _axToggleTrait];
- break;
- case PopUpButtonRole:
- traits |= [self _axPopupButtonTrait];
- break;
- case RadioButtonRole:
- traits |= [self _axRadioButtonTrait] | [self _axToggleTrait];
- break;
- case CheckBoxRole:
- traits |= ([self _axButtonTrait] | [self _axToggleTrait]);
- break;
- case HeadingRole:
- traits |= [self _axHeaderTrait];
- break;
- case StaticTextRole:
- traits |= [self _axStaticTextTrait];
- break;
- case SliderRole:
- traits |= [self _axAdjustableTrait];
- break;
- case MenuButtonRole:
- case MenuItemRole:
- traits |= [self _axMenuItemTrait];
- break;
- default:
- break;
- }
-
- if (m_object->isSelected())
- traits |= [self _axSelectedTrait];
-
- if (!m_object->isEnabled())
- traits |= [self _axNotEnabledTrait];
-
- if (m_accessibilityTraitsFromAncestor == ULLONG_MAX)
- m_accessibilityTraitsFromAncestor = [self _accessibilityTraitsFromAncestors];
-
- traits |= m_accessibilityTraitsFromAncestor;
-
- return traits;
-}
-
-- (BOOL)isSVGGroupElement
-{
- // If an SVG group element has a title, it should be an accessible element on iOS.
-#if ENABLE(SVG)
- Node* node = m_object->node();
- if (node && node->hasTagName(SVGNames::gTag) && [[self accessibilityLabel] length] > 0)
- return YES;
-#endif
-
- return NO;
-}
-
-- (BOOL)determineIsAccessibilityElement
-{
- if (!m_object)
- return false;
-
- // Honor when something explicitly makes this an element (super will contain that logic)
- if ([super isAccessibilityElement])
- return YES;
-
- m_object->updateBackingStore();
-
- switch (m_object->roleValue()) {
- case TextFieldRole:
- case TextAreaRole:
- case ButtonRole:
- case PopUpButtonRole:
- case CheckBoxRole:
- case RadioButtonRole:
- case SliderRole:
- case MenuButtonRole:
- case ValueIndicatorRole:
- case ImageRole:
- case ProgressIndicatorRole:
- case MenuItemRole:
- case IncrementorRole:
- case ComboBoxRole:
- case DisclosureTriangleRole:
- case ImageMapRole:
- case ListMarkerRole:
- case ListBoxOptionRole:
- case TabRole:
- case DocumentMathRole:
- return true;
- case StaticTextRole:
- {
- // Many text elements only contain a space.
- if (![[[self accessibilityLabel] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] length])
- return false;
-
- // Text elements that are just pieces of links or headers should not be exposed.
- if ([AccessibilityUnignoredAncestor([self accessibilityContainer]) containsUnnaturallySegmentedChildren])
- return false;
- return true;
- }
-
- // Don't expose headers as elements; instead expose their children as elements, with the header trait (unless they have no children)
- case HeadingRole:
- if (![self accessibilityElementCount])
- return true;
- return false;
-
- // Links can sometimes be elements (when they only contain static text or don't contain anything).
- // They should not be elements when containing text and other types.
- case WebCoreLinkRole:
- case LinkRole:
- if ([self containsUnnaturallySegmentedChildren] || ![self accessibilityElementCount])
- return true;
- return false;
- case GroupRole:
- if ([self isSVGGroupElement])
- return true;
- // All other elements are ignored on the iphone.
- default:
- case UnknownRole:
- case TabGroupRole:
- case ScrollAreaRole:
- case TableRole:
- case ApplicationRole:
- case RadioGroupRole:
- case ListRole:
- case ListBoxRole:
- case ScrollBarRole:
- case MenuBarRole:
- case MenuRole:
- case ColumnRole:
- case RowRole:
- case ToolbarRole:
- case BusyIndicatorRole:
- case WindowRole:
- case DrawerRole:
- case SystemWideRole:
- case OutlineRole:
- case BrowserRole:
- case SplitGroupRole:
- case SplitterRole:
- case ColorWellRole:
- case GrowAreaRole:
- case SheetRole:
- case HelpTagRole:
- case MatteRole:
- case RulerRole:
- case RulerMarkerRole:
- case GridRole:
- case WebAreaRole:
- return false;
- }
-}
-
-- (BOOL)isAccessibilityElement
-{
- if (![self _prepareAccessibilityCall])
- return NO;
-
- if (m_isAccessibilityElement == -1)
- m_isAccessibilityElement = [self determineIsAccessibilityElement];
-
- return m_isAccessibilityElement;
-}
-
-- (BOOL)stringValueShouldBeUsedInLabel
-{
- if (m_object->isTextControl())
- return NO;
- if (m_object->roleValue() == PopUpButtonRole)
- return NO;
- if (m_object->isFileUploadButton())
- return NO;
-
- return YES;
-}
-
-- (BOOL)fileUploadButtonReturnsValueInTitle
-{
- return NO;
-}
-
-static void appendStringToResult(NSMutableString *result, NSString *string)
-{
- ASSERT(result);
- if (![string length])
- return;
- if ([result length])
- [result appendString:@", "];
- [result appendString:string];
-}
-
-- (CGFloat)_accessibilityMinValue
-{
- return m_object->minValueForRange();
-}
-
-- (CGFloat)_accessibilityMaxValue
-{
- return m_object->maxValueForRange();
-}
-
-- (NSString *)accessibilityLabel
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- // check if the label was overriden
- NSString *label = [super accessibilityLabel];
- if (label)
- return label;
-
- // iOS doesn't distinguish between a title and description field,
- // so concatentation will yield the best result.
- NSString *axTitle = [self accessibilityTitle];
- NSString *axDescription = [self accessibilityDescription];
- NSString *landmarkDescription = [self ariaLandmarkRoleDescription];
-
- NSMutableString *result = [NSMutableString string];
-
- appendStringToResult(result, axTitle);
- appendStringToResult(result, axDescription);
- if ([self stringValueShouldBeUsedInLabel]) {
- NSString *valueLabel = m_object->stringValue();
- valueLabel = [valueLabel stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
- appendStringToResult(result, valueLabel);
- }
- appendStringToResult(result, landmarkDescription);
-
- return [result length] ? result : nil;
-}
-
-- (AccessibilityTableCell*)tableCellParent
-{
- // Find if this element is in a table cell.
- AccessibilityObject* cell = 0;
- for (cell = m_object; cell && !cell->isTableCell(); cell = cell->parentObject())
- { }
-
- if (!cell)
- return 0;
-
- return static_cast<AccessibilityTableCell*>(cell);
-}
-
-- (AccessibilityTable*)tableParent
-{
- // Find if the parent table for the table cell.
- AccessibilityObject* parentTable = 0;
- for (parentTable = m_object; parentTable && !parentTable->isDataTable(); parentTable = parentTable->parentObject())
- { }
-
- if (!parentTable)
- return 0;
-
- return static_cast<AccessibilityTable*>(parentTable);
-}
-
-- (id)accessibilityTitleElement
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- AccessibilityObject* titleElement = m_object->titleUIElement();
- if (titleElement)
- return titleElement->wrapper();
-
- return nil;
-}
-
-// Meant to return row or column headers (or other things as the future permits).
-- (NSArray *)accessibilityHeaderElements
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- AccessibilityTableCell* tableCell = [self tableCellParent];
- if (!tableCell)
- return nil;
-
- AccessibilityTable* table = [self tableParent];
- if (!table)
- return nil;
-
- // Get the row and column range, so we can use them to find the headers.
- pair<unsigned, unsigned> rowRange;
- pair<unsigned, unsigned> columnRange;
- tableCell->rowIndexRange(rowRange);
- tableCell->columnIndexRange(columnRange);
-
- AccessibilityObject::AccessibilityChildrenVector rowHeaders;
- AccessibilityObject::AccessibilityChildrenVector columnHeaders;
- table->rowHeaders(rowHeaders);
- table->columnHeaders(columnHeaders);
-
- NSMutableArray *headers = [NSMutableArray array];
-
- unsigned columnRangeIndex = static_cast<unsigned>(columnRange.first);
- if (columnRangeIndex < columnHeaders.size()) {
- RefPtr<AccessibilityObject> columnHeader = columnHeaders[columnRange.first];
- AccessibilityObjectWrapper* wrapper = columnHeader->wrapper();
- if (wrapper)
- [headers addObject:wrapper];
- }
-
- unsigned rowRangeIndex = static_cast<unsigned>(rowRange.first);
- if (rowRangeIndex < rowHeaders.size()) {
- RefPtr<AccessibilityObject> rowHeader = rowHeaders[rowRange.first];
- AccessibilityObjectWrapper* wrapper = rowHeader->wrapper();
- if (wrapper)
- [headers addObject:wrapper];
- }
-
- return headers;
-}
-
-- (id)accessibilityElementForRow:(NSInteger)row andColumn:(NSInteger)column
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- AccessibilityTable* table = [self tableParent];
- if (!table)
- return nil;
-
- AccessibilityTableCell* cell = table->cellForColumnAndRow(column, row);
- if (!cell)
- return nil;
- return cell->wrapper();
-}
-
-- (NSRange)accessibilityRowRange
-{
- if (![self _prepareAccessibilityCall])
- return NSMakeRange(NSNotFound, 0);
-
- if (m_object->isRadioButton()) {
- AccessibilityObject::AccessibilityChildrenVector radioButtonSiblings;
- m_object->linkedUIElements(radioButtonSiblings);
- if (radioButtonSiblings.size() <= 1)
- return NSMakeRange(NSNotFound, 0);
-
- return NSMakeRange(radioButtonSiblings.find(m_object), radioButtonSiblings.size());
- }
-
- AccessibilityTableCell* tableCell = [self tableCellParent];
- if (!tableCell)
- return NSMakeRange(NSNotFound, 0);
-
- pair<unsigned, unsigned> rowRange;
- tableCell->rowIndexRange(rowRange);
- return NSMakeRange(rowRange.first, rowRange.second);
-}
-
-- (NSRange)accessibilityColumnRange
-{
- if (![self _prepareAccessibilityCall])
- return NSMakeRange(NSNotFound, 0);
-
- AccessibilityTableCell* tableCell = [self tableCellParent];
- if (!tableCell)
- return NSMakeRange(NSNotFound, 0);
-
- pair<unsigned, unsigned> columnRange;
- tableCell->columnIndexRange(columnRange);
- return NSMakeRange(columnRange.first, columnRange.second);
-}
-
-- (NSString *)accessibilityPlaceholderValue
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- return m_object->placeholderValue();
-}
-
-- (NSString *)accessibilityValue
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- // check if the value was overriden
- NSString *value = [super accessibilityValue];
- if (value)
- return value;
-
- if (m_object->isCheckboxOrRadio()) {
- switch (m_object->checkboxOrRadioValue()) {
- case ButtonStateOff:
- return [NSString stringWithFormat:@"%d", 0];
- case ButtonStateOn:
- return [NSString stringWithFormat:@"%d", 1];
- case ButtonStateMixed:
- return [NSString stringWithFormat:@"%d", 2];
- }
- ASSERT_NOT_REACHED();
- return [NSString stringWithFormat:@"%d", 0];
- }
-
- if (m_object->isButton() && m_object->isPressed())
- return [NSString stringWithFormat:@"%d", 1];
-
- // rdar://8131388 WebKit should expose the same info as UIKit for its password fields.
- if (m_object->isPasswordField()) {
- int passwordLength = m_object->accessibilityPasswordFieldLength();
- NSMutableString* string = [NSMutableString string];
- for (int k = 0; k < passwordLength; ++k)
- [string appendString:@"•"];
- return string;
- }
-
- // A text control should return its text data as the axValue (per iPhone AX API).
- if (![self stringValueShouldBeUsedInLabel])
- return m_object->stringValue();
-
- if (m_object->isProgressIndicator() || m_object->isSlider()) {
- // Prefer a valueDescription if provided by the author (through aria-valuetext).
- String valueDescription = m_object->valueDescription();
- if (!valueDescription.isEmpty())
- return valueDescription;
-
- return [NSString stringWithFormat:@"%.2f", m_object->valueForRange()];
- }
-
- if (m_object->isHeading())
- return [NSString stringWithFormat:@"%d", m_object->headingLevel()];
-
- return nil;
-}
-
-- (BOOL)accessibilityIsComboBox
-{
- if (![self _prepareAccessibilityCall])
- return NO;
-
- return m_object->roleValue() == ComboBoxRole;
-}
-
-- (NSString *)accessibilityHint
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- return [self accessibilityHelpText];
-}
-
-- (NSURL *)accessibilityURL
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- KURL url = m_object->url();
- if (url.isNull())
- return nil;
- return (NSURL*)url;
-}
-
-- (CGPoint)convertPointToScreenSpace:(FloatPoint &)point
-{
- if (!m_object)
- return CGPointZero;
-
- CGPoint cgPoint = CGPointMake(point.x(), point.y());
-
- FrameView* frameView = m_object->documentFrameView();
- if (frameView) {
- WAKView* view = frameView->documentView();
- cgPoint = [view convertPoint:cgPoint toView:nil];
- }
-
- // we need the web document view to give us our final screen coordinates
- // because that can take account of the scroller
- id webDocument = [self _accessibilityWebDocumentView];
- if (webDocument)
- cgPoint = [webDocument convertPoint:cgPoint toView:nil];
-
- return cgPoint;
-}
-
-- (CGRect)convertRectToScreenSpace:(IntRect &)rect
-{
- if (!m_object)
- return CGRectZero;
-
- CGSize size = CGSizeMake(rect.size().width(), rect.size().height());
- CGPoint point = CGPointMake(rect.x(), rect.y());
-
- CGRect frame = CGRectMake(point.x, point.y, size.width, size.height);
-
- FrameView* frameView = m_object->documentFrameView();
- if (frameView) {
- WAKView* view = frameView->documentView();
- frame = [view convertRect:frame toView:nil];
- }
-
- // we need the web document view to give us our final screen coordinates
- // because that can take account of the scroller
- id webDocument = [self _accessibilityWebDocumentView];
- if (webDocument)
- frame = [webDocument convertRect:frame toView:nil];
-
- return frame;
-}
-
-// Used by UIKit accessibility bundle to help determine distance during a hit-test.
-- (CGRect)accessibilityElementRect
-{
- if (![self _prepareAccessibilityCall])
- return CGRectZero;
-
- LayoutRect rect = m_object->elementRect();
- return CGRectMake(rect.x(), rect.y(), rect.width(), rect.height());
-}
-
-// The "center point" is where VoiceOver will "press" an object. This may not be the actual
-// center of the accessibilityFrame
-- (CGPoint)accessibilityActivationPoint
-{
- if (![self _prepareAccessibilityCall])
- return CGPointZero;
-
- IntRect rect = pixelSnappedIntRect(m_object->boundingBoxRect());
- CGRect cgRect = [self convertRectToScreenSpace:rect];
- return CGPointMake(CGRectGetMidX(cgRect), CGRectGetMidY(cgRect));
-}
-
-- (CGRect)accessibilityFrame
-{
- if (![self _prepareAccessibilityCall])
- return CGRectZero;
-
- IntRect rect = pixelSnappedIntRect(m_object->elementRect());
- return [self convertRectToScreenSpace:rect];
-}
-
-// Checks whether a link contains only static text and images (and has been divided unnaturally by <spans> and other nefarious mechanisms).
-- (BOOL)containsUnnaturallySegmentedChildren
-{
- if (!m_object)
- return NO;
-
- AccessibilityRole role = m_object->roleValue();
- if (role != LinkRole && role != WebCoreLinkRole)
- return NO;
-
- AccessibilityObject::AccessibilityChildrenVector children = m_object->children();
- unsigned childrenSize = children.size();
-
- // If there's only one child, then it doesn't have segmented children.
- if (childrenSize == 1)
- return NO;
-
- for (unsigned i = 0; i < childrenSize; ++i) {
- AccessibilityRole role = children[i]->roleValue();
- if (role != StaticTextRole && role != ImageRole && role != GroupRole)
- return NO;
- }
-
- return YES;
-}
-
-- (id)accessibilityContainer
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- AXAttributeCacheEnabler enableCache(m_object->axObjectCache());
-
- // As long as there's a parent wrapper, that's the correct chain to climb.
- AccessibilityObject* parent = m_object->parentObjectUnignored();
- if (parent)
- return parent->wrapper();
-
- // The only object without a parent wrapper should be a scroll view.
- ASSERT(m_object->isAccessibilityScrollView());
-
- // Verify this is the top document. If not, we might need to go through the platform widget.
- FrameView* frameView = m_object->documentFrameView();
- Document* document = m_object->document();
- if (document && frameView && document != document->topDocument())
- return frameView->platformWidget();
-
- // The top scroll view's parent is the web document view.
- return [self _accessibilityWebDocumentView];
-}
-
-- (id)accessibilityFocusedUIElement
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- AccessibilityObject* focusedObj = m_object->focusedUIElement();
-
- if (!focusedObj)
- return nil;
-
- return focusedObj->wrapper();
-}
-
-- (id)_accessibilityWebDocumentView
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- // This method performs the crucial task of connecting to the UIWebDocumentView.
- // This is needed to correctly calculate the screen position of the AX object.
- static Class webViewClass = nil;
- if (!webViewClass)
- webViewClass = NSClassFromString(@"WebView");
-
- if (!webViewClass)
- return nil;
-
- FrameView* frameView = m_object->documentFrameView();
-
- if (!frameView)
- return nil;
-
- // If this is the top level frame, the UIWebDocumentView should be returned.
- id parentView = frameView->documentView();
- while (parentView && ![parentView isKindOfClass:webViewClass])
- parentView = [parentView superview];
-
- // The parentView should have an accessibilityContainer, if the UIKit accessibility bundle was loaded.
- // The exception is DRT, which tests accessibility without the entire system turning accessibility on. Hence,
- // this check should be valid for everything except DRT.
- ASSERT([parentView accessibilityContainer] || applicationIsDumpRenderTree());
-
- return [parentView accessibilityContainer];
-}
-
-- (NSArray *)_accessibilityNextElementsWithCount:(UInt32)count
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- return [[self _accessibilityWebDocumentView] _accessibilityNextElementsWithCount:count];
-}
-
-- (NSArray *)_accessibilityPreviousElementsWithCount:(UInt32)count
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- return [[self _accessibilityWebDocumentView] _accessibilityPreviousElementsWithCount:count];
-}
-
-- (BOOL)accessibilityRequired
-{
- if (![self _prepareAccessibilityCall])
- return NO;
-
- return m_object->isRequired();
-}
-
-- (NSArray *)accessibilityFlowToElements
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- AccessibilityObject::AccessibilityChildrenVector children;
- m_object->ariaFlowToElements(children);
-
- unsigned length = children.size();
- NSMutableArray* array = [NSMutableArray arrayWithCapacity:length];
- for (unsigned i = 0; i < length; ++i) {
- AccessibilityObjectWrapper* wrapper = children[i]->wrapper();
- ASSERT(wrapper);
- if (!wrapper)
- continue;
-
- if (children[i]->isAttachment() && [wrapper attachmentView])
- [array addObject:[wrapper attachmentView]];
- else
- [array addObject:wrapper];
- }
- return array;
-}
-
-- (id)accessibilityLinkedElement
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- // If this static text inside of a link, it should use its parent's linked element.
- AccessibilityObject* element = m_object;
- if (m_object->roleValue() == StaticTextRole && m_object->parentObjectUnignored()->isLink())
- element = m_object->parentObjectUnignored();
-
- AccessibilityObject::AccessibilityChildrenVector children;
- element->linkedUIElements(children);
- if (children.size() == 0)
- return nil;
-
- return children[0]->wrapper();
-}
-
-
-- (BOOL)isAttachment
-{
- if (!m_object)
- return NO;
-
- return m_object->isAttachment();
-}
-
-- (void)_accessibilityActivate
-{
- if (![self _prepareAccessibilityCall])
- return;
-
- m_object->press();
-}
-
-- (id)attachmentView
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- ASSERT([self isAttachment]);
- Widget* widget = m_object->widgetForAttachmentView();
- if (!widget)
- return nil;
- return widget->platformWidget();
-}
-
-static RenderObject* rendererForView(WAKView* view)
-{
- if (![view conformsToProtocol:@protocol(WebCoreFrameView)])
- return 0;
-
- WAKView<WebCoreFrameView>* frameView = (WAKView<WebCoreFrameView>*)view;
- Frame* frame = [frameView _web_frame];
- if (!frame)
- return 0;
-
- Node* node = frame->document()->ownerElement();
- if (!node)
- return 0;
-
- return node->renderer();
-}
-
-- (id)_accessibilityParentForSubview:(id)subview
-{
- RenderObject* renderer = rendererForView(subview);
- if (!renderer)
- return nil;
-
- AccessibilityObject* obj = renderer->document()->axObjectCache()->getOrCreate(renderer);
- if (obj)
- return obj->parentObjectUnignored()->wrapper();
- return nil;
-}
-
-- (void)postFocusChangeNotification
-{
- // The UIKit accessibility wrapper will override and post appropriate notification.
-}
-
-- (void)postSelectedTextChangeNotification
-{
- // The UIKit accessibility wrapper will override and post appropriate notification.
-}
-
-- (void)postLayoutChangeNotification
-{
- // The UIKit accessibility wrapper will override and post appropriate notification.
-}
-
-- (void)postLiveRegionChangeNotification
-{
- // The UIKit accessibility wrapper will override and post appropriate notification.
-}
-
-- (void)postLoadCompleteNotification
-{
- // The UIKit accessibility wrapper will override and post appropriate notification.
-}
-
-- (void)postChildrenChangedNotification
-{
- // The UIKit accessibility wrapper will override and post appropriate notification.
-}
-
-- (void)postInvalidStatusChangedNotification
-{
- // The UIKit accessibility wrapper will override and post appropriate notification.
-}
-
-- (void)accessibilityElementDidBecomeFocused
-{
- if (![self _prepareAccessibilityCall])
- return;
-
- // The focused VoiceOver element might be the text inside a link.
- // In those cases we should focus on the link itself.
- for (AccessibilityObject* object = m_object; object != nil; object = object->parentObject()) {
- if (object->roleValue() == WebAreaRole)
- break;
-
- if (object->canSetFocusAttribute()) {
- object->setFocused(true);
- break;
- }
- }
-}
-
-- (void)accessibilityModifySelection:(TextGranularity)granularity increase:(BOOL)increase
-{
- if (![self _prepareAccessibilityCall])
- return;
-
- FrameSelection* frameSelection = m_object->document()->frame()->selection();
- VisibleSelection selection = m_object->selection();
- VisiblePositionRange range = m_object->visiblePositionRange();
-
- // Before a selection with length exists, the cursor position needs to move to the right starting place.
- // That should be the beginning of this element (range.start). However, if the cursor is already within the
- // range of this element (the cursor is represented by selection), then the cursor does not need to move.
- if (frameSelection->isNone() && (selection.visibleStart() < range.start || selection.visibleEnd() > range.end))
- frameSelection->moveTo(range.start, UserTriggered);
-
- frameSelection->modify(FrameSelection::AlterationExtend, (increase) ? DirectionRight : DirectionLeft, granularity, UserTriggered);
-}
-
-- (void)accessibilityIncreaseSelection:(TextGranularity)granularity
-{
- [self accessibilityModifySelection:granularity increase:YES];
-}
-
-- (void)accessibilityDecreaseSelection:(TextGranularity)granularity
-{
- [self accessibilityModifySelection:granularity increase:NO];
-}
-
-- (void)accessibilityMoveSelectionToMarker:(WebAccessibilityTextMarker *)marker
-{
- if (![self _prepareAccessibilityCall])
- return;
-
- VisiblePosition visiblePosition = [marker visiblePosition];
- if (visiblePosition.isNull())
- return;
-
- FrameSelection* frameSelection = m_object->document()->frame()->selection();
- frameSelection->moveTo(visiblePosition, UserTriggered);
-}
-
-- (void)accessibilityIncrement
-{
- if (![self _prepareAccessibilityCall])
- return;
-
- m_object->increment();
-}
-
-- (void)accessibilityDecrement
-{
- if (![self _prepareAccessibilityCall])
- return;
-
- m_object->decrement();
-}
-
-#pragma mark Accessibility Text Marker Handlers
-
-- (BOOL)_addAccessibilityObject:(AccessibilityObject*)axObject toTextMarkerArray:(NSMutableArray *)array
-{
- if (!axObject)
- return NO;
-
- AccessibilityObjectWrapper* wrapper = axObject->wrapper();
- if (!wrapper)
- return NO;
-
- // Don't add the same object twice, but since this has already been added, we should return
- // YES because we want to inform that it's in the array
- if ([array containsObject:wrapper])
- return YES;
-
- // Explicity set that this is now an element (in case other logic tries to override).
- [wrapper setValue:[NSNumber numberWithBool:YES] forKey:@"isAccessibilityElement"];
- [array addObject:wrapper];
- return YES;
-}
-
-- (NSString *)stringForTextMarkers:(NSArray *)markers
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- if ([markers count] != 2)
- return nil;
-
- WebAccessibilityTextMarker* startMarker = [markers objectAtIndex:0];
- WebAccessibilityTextMarker* endMarker = [markers objectAtIndex:1];
- if (![startMarker isKindOfClass:[WebAccessibilityTextMarker class]] || ![endMarker isKindOfClass:[WebAccessibilityTextMarker class]])
- return nil;
-
- // extract the start and end VisiblePosition
- VisiblePosition startVisiblePosition = [startMarker visiblePosition];
- if (startVisiblePosition.isNull())
- return nil;
-
- VisiblePosition endVisiblePosition = [endMarker visiblePosition];
- if (endVisiblePosition.isNull())
- return nil;
-
- VisiblePositionRange visiblePosRange = VisiblePositionRange(startVisiblePosition, endVisiblePosition);
- return m_object->stringForVisiblePositionRange(visiblePosRange);
-}
-
-static int blockquoteLevel(RenderObject* renderer)
-{
- if (!renderer)
- return 0;
-
- int result = 0;
- for (Node* node = renderer->node(); node; node = node->parentNode()) {
- if (node->hasTagName(blockquoteTag))
- result += 1;
- }
-
- return result;
-}
-
-static void AXAttributeStringSetBlockquoteLevel(NSMutableAttributedString* attrString, RenderObject* renderer, NSRange range)
-{
- int quoteLevel = blockquoteLevel(renderer);
-
- if (quoteLevel)
- [attrString addAttribute:UIAccessibilityTokenBlockquoteLevel value:[NSNumber numberWithInt:quoteLevel] range:range];
- else
- [attrString removeAttribute:UIAccessibilityTokenBlockquoteLevel range:range];
-}
-
-static void AXAttributeStringSetHeadingLevel(NSMutableAttributedString* attrString, RenderObject* renderer, NSRange range)
-{
- if (!renderer)
- return;
-
- AccessibilityObject* parentObject = renderer->document()->axObjectCache()->getOrCreate(renderer->parent());
- int parentHeadingLevel = parentObject->headingLevel();
-
- if (parentHeadingLevel)
- [attrString addAttribute:UIAccessibilityTokenHeadingLevel value:[NSNumber numberWithInt:parentHeadingLevel] range:range];
- else
- [attrString removeAttribute:UIAccessibilityTokenHeadingLevel range:range];
-}
-
-static void AXAttributeStringSetFont(NSMutableAttributedString* attrString, GSFontRef font, NSRange range)
-{
- if (!font)
- return;
-
- const char* nameCStr = GSFontGetFullName(font);
- const char* familyCStr = GSFontGetFamilyName(font);
- NSNumber* size = [NSNumber numberWithFloat:GSFontGetSize(font)];
- NSNumber* bold = [NSNumber numberWithBool:GSFontIsBold(font)];
- GSFontTraitMask traits = GSFontGetTraits(font);
- if (nameCStr)
- [attrString addAttribute:UIAccessibilityTokenFontName value:[NSString stringWithUTF8String:nameCStr] range:range];
- if (familyCStr)
- [attrString addAttribute:UIAccessibilityTokenFontFamily value:[NSString stringWithUTF8String:familyCStr] range:range];
- if ([size boolValue])
- [attrString addAttribute:UIAccessibilityTokenFontSize value:size range:range];
- if ([bold boolValue] || (traits & GSBoldFontMask))
- [attrString addAttribute:UIAccessibilityTokenBold value:[NSNumber numberWithBool:YES] range:range];
- if (traits & GSItalicFontMask)
- [attrString addAttribute:UIAccessibilityTokenItalic value:[NSNumber numberWithBool:YES] range:range];
-
-}
-
-static void AXAttributeStringSetNumber(NSMutableAttributedString* attrString, NSString* attribute, NSNumber* number, NSRange range)
-{
- if (number)
- [attrString addAttribute:attribute value:number range:range];
- else
- [attrString removeAttribute:attribute range:range];
-}
-
-static void AXAttributeStringSetStyle(NSMutableAttributedString* attrString, RenderObject* renderer, NSRange range)
-{
- RenderStyle* style = renderer->style();
-
- // set basic font info
- AXAttributeStringSetFont(attrString, style->font().primaryFont()->getGSFont(), range);
-
- int decor = style->textDecorationsInEffect();
- if ((decor & (TextDecorationUnderline | TextDecorationLineThrough)) != 0) {
- // find colors using quirk mode approach (strict mode would use current
- // color for all but the root line box, which would use getTextDecorationColors)
- Color underline, overline, linethrough;
- renderer->getTextDecorationColors(decor, underline, overline, linethrough);
-
- if (decor & TextDecorationUnderline)
- AXAttributeStringSetNumber(attrString, UIAccessibilityTokenUnderline, [NSNumber numberWithBool:YES], range);
- }
-}
-
-static void AXAttributedStringAppendText(NSMutableAttributedString* attrString, Node* node, const UChar* chars, int length)
-{
- // skip invisible text
- if (!node->renderer())
- return;
-
- // easier to calculate the range before appending the string
- NSRange attrStringRange = NSMakeRange([attrString length], length);
-
- // append the string from this node
- [[attrString mutableString] appendString:[NSString stringWithCharacters:chars length:length]];
-
- // set new attributes
- AXAttributeStringSetStyle(attrString, node->renderer(), attrStringRange);
- AXAttributeStringSetHeadingLevel(attrString, node->renderer(), attrStringRange);
- AXAttributeStringSetBlockquoteLevel(attrString, node->renderer(), attrStringRange);
-}
-
-
-// This method is intended to return an array of strings and accessibility elements that
-// represent the objects on one line of rendered web content. The array of markers sent
-// in should be ordered and contain only a start and end marker.
-- (NSArray *)arrayOfTextForTextMarkers:(NSArray *)markers attributed:(BOOL)attributed
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- if ([markers count] != 2)
- return nil;
-
- WebAccessibilityTextMarker* startMarker = [markers objectAtIndex:0];
- WebAccessibilityTextMarker* endMarker = [markers objectAtIndex:1];
- if (![startMarker isKindOfClass:[WebAccessibilityTextMarker class]] || ![endMarker isKindOfClass:[WebAccessibilityTextMarker class]])
- return nil;
-
- // extract the start and end VisiblePosition
- VisiblePosition startVisiblePosition = [startMarker visiblePosition];
- if (startVisiblePosition.isNull())
- return nil;
-
- VisiblePosition endVisiblePosition = [endMarker visiblePosition];
- if (endVisiblePosition.isNull())
- return nil;
-
- // iterate over the range to build the AX attributed string
- NSMutableArray* array = [[NSMutableArray alloc] init];
- TextIterator it(makeRange(startVisiblePosition, endVisiblePosition).get());
- for (; !it.atEnd(); it.advance()) {
- // locate the node and starting offset for this range
- int exception = 0;
- Node* node = it.range()->startContainer(exception);
- ASSERT(node == it.range()->endContainer(exception));
- int offset = it.range()->startOffset(exception);
-
- // non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX)
- if (it.length() != 0) {
- if (!attributed) {
- // First check if this is represented by a link.
- AccessibilityObject* linkObject = AccessibilityObject::anchorElementForNode(node);
- if ([self _addAccessibilityObject:linkObject toTextMarkerArray:array])
- continue;
-
- // Next check if this region is represented by a heading.
- AccessibilityObject* headingObject = AccessibilityObject::headingElementForNode(node);
- if ([self _addAccessibilityObject:headingObject toTextMarkerArray:array])
- continue;
-
- String listMarkerText = m_object->listMarkerTextForNodeAndPosition(node, VisiblePosition(it.range()->startPosition()));
-
- if (!listMarkerText.isEmpty())
- [array addObject:[NSString stringWithCharacters:listMarkerText.characters() length:listMarkerText.length()]];
- // There was not an element representation, so just return the text.
- [array addObject:[NSString stringWithCharacters:it.characters() length:it.length()]];
- }
- else
- {
- String listMarkerText = m_object->listMarkerTextForNodeAndPosition(node, VisiblePosition(it.range()->startPosition()));
-
- if (!listMarkerText.isEmpty()) {
- NSMutableAttributedString* attrString = [[NSMutableAttributedString alloc] init];
- AXAttributedStringAppendText(attrString, node, listMarkerText.characters(), listMarkerText.length());
- [array addObject:attrString];
- [attrString release];
- }
-
- NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] init];
- AXAttributedStringAppendText(attrString, node, it.characters(), it.length());
- [array addObject:attrString];
- [attrString release];
- }
- } else {
- Node* replacedNode = node->childNode(offset);
- if (replacedNode) {
- AccessibilityObject* obj = m_object->axObjectCache()->getOrCreate(replacedNode->renderer());
- if (obj && !obj->accessibilityIsIgnored())
- [self _addAccessibilityObject:obj toTextMarkerArray:array];
- }
- }
- }
-
- return [array autorelease];
-}
-
-- (NSRange)_convertToNSRange:(Range *)range
-{
- if (!range || !range->startContainer())
- return NSMakeRange(NSNotFound, 0);
-
- Document* document = m_object->document();
- FrameSelection* frameSelection = document->frame()->selection();
-
- Element* selectionRoot = frameSelection->rootEditableElement();
- Element* scope = selectionRoot ? selectionRoot : document->documentElement();
-
- // Mouse events may cause TSM to attempt to create an NSRange for a portion of the view
- // that is not inside the current editable region. These checks ensure we don't produce
- // potentially invalid data when responding to such requests.
- if (range->startContainer() != scope && !range->startContainer()->isDescendantOf(scope))
- return NSMakeRange(NSNotFound, 0);
- if (range->endContainer() != scope && !range->endContainer()->isDescendantOf(scope))
- return NSMakeRange(NSNotFound, 0);
-
- RefPtr<Range> testRange = Range::create(scope->document(), scope, 0, range->startContainer(), range->startOffset());
- ASSERT(testRange->startContainer() == scope);
- int startPosition = TextIterator::rangeLength(testRange.get());
-
- ExceptionCode ec;
- testRange->setEnd(range->endContainer(), range->endOffset(), ec);
- ASSERT(testRange->startContainer() == scope);
- int endPosition = TextIterator::rangeLength(testRange.get());
- return NSMakeRange(startPosition, endPosition - startPosition);
-}
-
-- (PassRefPtr<Range>)_convertToDOMRange:(NSRange)nsrange
-{
- if (nsrange.location > INT_MAX)
- return 0;
- if (nsrange.length > INT_MAX || nsrange.location + nsrange.length > INT_MAX)
- nsrange.length = INT_MAX - nsrange.location;
-
- // our critical assumption is that we are only called by input methods that
- // concentrate on a given area containing the selection
- // We have to do this because of text fields and textareas. The DOM for those is not
- // directly in the document DOM, so serialization is problematic. Our solution is
- // to use the root editable element of the selection start as the positional base.
- // That fits with AppKit's idea of an input context.
- Document* document = m_object->document();
- FrameSelection* frameSelection = document->frame()->selection();
- Element* selectionRoot = frameSelection->rootEditableElement();
- Element* scope = selectionRoot ? selectionRoot : document->documentElement();
- return TextIterator::rangeFromLocationAndLength(scope, nsrange.location, nsrange.length);
-}
-
-// This method is intended to take a text marker representing a VisiblePosition and convert it
-// into a normalized location within the document.
-- (NSInteger)positionForTextMarker:(WebAccessibilityTextMarker *)marker
-{
- if (![self _prepareAccessibilityCall])
- return NSNotFound;
-
- if (!marker)
- return NSNotFound;
-
- VisibleSelection selection([marker visiblePosition]);
- RefPtr<Range> range = selection.toNormalizedRange();
- NSRange nsRange = [self _convertToNSRange:range.get()];
- return nsRange.location;
-}
-
-- (NSArray *)textMarkerRange
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- VisiblePositionRange range = m_object->visiblePositionRange();
- VisiblePosition startPosition = range.start;
- VisiblePosition endPosition = range.end;
- WebAccessibilityTextMarker* start = [WebAccessibilityTextMarker textMarkerWithVisiblePosition:startPosition cache:m_object->axObjectCache()];
- WebAccessibilityTextMarker* end = [WebAccessibilityTextMarker textMarkerWithVisiblePosition:endPosition cache:m_object->axObjectCache()];
-
- return [NSArray arrayWithObjects:start, end, nil];
-}
-
-// A method to get the normalized text cursor range of an element. Used in DumpRenderTree.
-- (NSRange)elementTextRange
-{
- if (![self _prepareAccessibilityCall])
- return NSMakeRange(NSNotFound, 0);
-
- NSArray *markers = [self textMarkerRange];
- if ([markers count] != 2)
- return NSMakeRange(NSNotFound, 0);
-
- WebAccessibilityTextMarker *startMarker = [markers objectAtIndex:0];
- WebAccessibilityTextMarker *endMarker = [markers objectAtIndex:1];
-
- NSInteger startPosition = [self positionForTextMarker:startMarker];
- NSInteger endPosition = [self positionForTextMarker:endMarker];
-
- return NSMakeRange(startPosition, endPosition - startPosition);
-}
-
-- (AccessibilityObjectWrapper *)accessibilityObjectForTextMarker:(WebAccessibilityTextMarker *)marker
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- if (!marker)
- return nil;
-
- VisiblePosition visiblePosition = [marker visiblePosition];
- AccessibilityObject* obj = m_object->accessibilityObjectForPosition(visiblePosition);
- if (!obj)
- return nil;
-
- return AccessibilityUnignoredAncestor(obj->wrapper());
-}
-
-- (NSArray *)textMarkerRangeForSelection
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- VisibleSelection selection = m_object->selection();
- if (selection.isNone())
- return nil;
- VisiblePosition startPosition = selection.visibleStart();
- VisiblePosition endPosition = selection.visibleEnd();
-
- WebAccessibilityTextMarker* startMarker = [WebAccessibilityTextMarker textMarkerWithVisiblePosition:startPosition cache:m_object->axObjectCache()];
- WebAccessibilityTextMarker* endMarker = [WebAccessibilityTextMarker textMarkerWithVisiblePosition:endPosition cache:m_object->axObjectCache()];
- if (!startMarker || !endMarker)
- return nil;
-
- return [NSArray arrayWithObjects:startMarker, endMarker, nil];
-}
-
-- (WebAccessibilityTextMarker *)textMarkerForPosition:(NSInteger)position
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- PassRefPtr<Range> range = [self _convertToDOMRange:NSMakeRange(position, 0)];
- if (!range)
- return nil;
-
- VisibleSelection selection = VisibleSelection(range.get(), DOWNSTREAM);
-
- VisiblePosition visiblePosition = selection.visibleStart();
- return [WebAccessibilityTextMarker textMarkerWithVisiblePosition:visiblePosition cache:m_object->axObjectCache()];
-}
-
-- (id)_stringForRange:(NSRange)range attributed:(BOOL)attributed
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- WebAccessibilityTextMarker* startMarker = [self textMarkerForPosition:range.location];
- WebAccessibilityTextMarker* endMarker = [self textMarkerForPosition:NSMaxRange(range)];
-
- // Clients don't always know the exact range, rather than force them to compute it,
- // allow clients to overshoot and use the max text marker range.
- if (!startMarker || !endMarker) {
- NSArray *markers = [self textMarkerRange];
- if ([markers count] != 2)
- return nil;
- if (!startMarker)
- startMarker = [markers objectAtIndex:0];
- if (!endMarker)
- endMarker = [markers objectAtIndex:1];
- }
-
- NSArray* array = [self arrayOfTextForTextMarkers:[NSArray arrayWithObjects:startMarker, endMarker, nil] attributed:attributed];
- Class returnClass = attributed ? [NSMutableAttributedString class] : [NSMutableString class];
- id returnValue = [[[returnClass alloc] init] autorelease];
-
- NSInteger count = [array count];
- for (NSInteger k = 0; k < count; ++k) {
- id object = [array objectAtIndex:k];
-
- if (![object isKindOfClass:returnClass])
- continue;
-
- if (attributed)
- [(NSMutableAttributedString *)returnValue appendAttributedString:object];
- else
- [(NSMutableString *)returnValue appendString:object];
- }
- return returnValue;
-}
-
-
-// A convenience method for getting the text of a NSRange. Currently used only by DRT.
-- (NSString *)stringForRange:(NSRange)range
-{
- return [self _stringForRange:range attributed:NO];
-}
-
-- (NSAttributedString *)attributedStringForRange:(NSRange)range
-{
- return [self _stringForRange:range attributed:YES];
-}
-
-// A convenience method for getting the accessibility objects of a NSRange. Currently used only by DRT.
-- (NSArray *)elementsForRange:(NSRange)range
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- WebAccessibilityTextMarker* startMarker = [self textMarkerForPosition:range.location];
- WebAccessibilityTextMarker* endMarker = [self textMarkerForPosition:NSMaxRange(range)];
- if (!startMarker || !endMarker)
- return nil;
-
- NSArray* array = [self arrayOfTextForTextMarkers:[NSArray arrayWithObjects:startMarker, endMarker, nil] attributed:NO];
- NSMutableArray* elements = [NSMutableArray array];
- for (id element in array) {
- if (![element isKindOfClass:[AccessibilityObjectWrapper class]])
- continue;
- [elements addObject:element];
- }
- return elements;
-}
-
-- (NSString *)selectionRangeString
-{
- NSArray *markers = [self textMarkerRangeForSelection];
- return [self stringForTextMarkers:markers];
-}
-
-- (WebAccessibilityTextMarker *)selectedTextMarker
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- VisibleSelection selection = m_object->selection();
- VisiblePosition position = selection.visibleStart();
-
- // if there's no selection, start at the top of the document
- if (position.isNull())
- position = startOfDocument(m_object->document());
-
- return [WebAccessibilityTextMarker textMarkerWithVisiblePosition:position cache:m_object->axObjectCache()];
-}
-
-// This method is intended to return the marker at the end of the line starting at
-// the marker that is passed into the method.
-- (WebAccessibilityTextMarker *)lineEndMarkerForMarker:(WebAccessibilityTextMarker *)marker
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- if (!marker)
- return nil;
-
- VisiblePosition start = [marker visiblePosition];
- VisiblePosition lineEnd = m_object->nextLineEndPosition(start);
-
- return [WebAccessibilityTextMarker textMarkerWithVisiblePosition:lineEnd cache:m_object->axObjectCache()];
-}
-
-// This method is intended to return the marker at the start of the line starting at
-// the marker that is passed into the method.
-- (WebAccessibilityTextMarker *)lineStartMarkerForMarker:(WebAccessibilityTextMarker *)marker
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- if (!marker)
- return nil;
-
- VisiblePosition start = [marker visiblePosition];
- VisiblePosition lineStart = m_object->previousLineStartPosition(start);
-
- return [WebAccessibilityTextMarker textMarkerWithVisiblePosition:lineStart cache:m_object->axObjectCache()];
-}
-
-- (WebAccessibilityTextMarker *)nextMarkerForMarker:(WebAccessibilityTextMarker *)marker
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- if (!marker)
- return nil;
-
- VisiblePosition start = [marker visiblePosition];
- VisiblePosition nextMarker = m_object->nextVisiblePosition(start);
-
- return [WebAccessibilityTextMarker textMarkerWithVisiblePosition:nextMarker cache:m_object->axObjectCache()];
-}
-
-// This method is intended to return the marker at the start of the line starting at
-// the marker that is passed into the method.
-- (WebAccessibilityTextMarker *)previousMarkerForMarker:(WebAccessibilityTextMarker *)marker
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- if (!marker)
- return nil;
-
- VisiblePosition start = [marker visiblePosition];
- VisiblePosition previousMarker = m_object->previousVisiblePosition(start);
-
- return [WebAccessibilityTextMarker textMarkerWithVisiblePosition:previousMarker cache:m_object->axObjectCache()];
-}
-
-// This method is intended to return the bounds of a text marker range in screen coordinates.
-- (CGRect)frameForTextMarkers:(NSArray *)array
-{
- if (![self _prepareAccessibilityCall])
- return CGRectZero;
-
- if ([array count] != 2)
- return CGRectZero;
-
- WebAccessibilityTextMarker* startMarker = [array objectAtIndex:0];
- WebAccessibilityTextMarker* endMarker = [array objectAtIndex:1];
- if (![startMarker isKindOfClass:[WebAccessibilityTextMarker class]] || ![endMarker isKindOfClass:[WebAccessibilityTextMarker class]])
- return CGRectZero;
-
- IntRect rect = m_object->boundsForVisiblePositionRange(VisiblePositionRange([startMarker visiblePosition], [endMarker visiblePosition]));
- return [self convertRectToScreenSpace:rect];
-}
-
-- (WebAccessibilityTextMarker *)textMarkerForPoint:(CGPoint)point
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- VisiblePosition pos = m_object->visiblePositionForPoint(IntPoint(point));
- return [WebAccessibilityTextMarker textMarkerWithVisiblePosition:pos cache:m_object->axObjectCache()];
-}
-
-- (NSString *)accessibilityIdentifier
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- return m_object->getAttribute(HTMLNames::idAttr);
-}
-
-- (NSString *)accessibilitySpeechHint
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- switch (m_object->speakProperty()) {
- default:
- case SpeakNormal:
- return @"normal";
- case SpeakNone:
- return @"none";
- case SpeakSpellOut:
- return @"spell-out";
- case SpeakDigits:
- return @"digits";
- case SpeakLiteralPunctuation:
- return @"literal-punctuation";
- case SpeakNoPunctuation:
- return @"no-punctuation";
- }
-
- return nil;
-}
-
-- (BOOL)accessibilityARIAIsBusy
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- return m_object->ariaLiveRegionBusy();
-}
-
-- (NSString *)accessibilityARIALiveRegionStatus
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- return m_object->ariaLiveRegionStatus();
-}
-
-- (NSString *)accessibilityARIARelevantStatus
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- return m_object->ariaLiveRegionRelevant();
-}
-
-- (BOOL)accessibilityARIALiveRegionIsAtomic
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- return m_object->ariaLiveRegionAtomic();
-}
-
-- (NSString *)accessibilityInvalidStatus
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- return m_object->invalidStatus();
-}
-
-- (WebAccessibilityObjectWrapper *)accessibilityMathRootIndexObject
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- return m_object->mathRootIndexObject() ? m_object->mathRootIndexObject()->wrapper() : 0;
-}
-
-- (WebAccessibilityObjectWrapper *)accessibilityMathRadicandObject
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- return m_object->mathRadicandObject() ? m_object->mathRadicandObject()->wrapper() : 0;
-}
-
-- (WebAccessibilityObjectWrapper *)accessibilityMathNumeratorObject
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- return m_object->mathNumeratorObject() ? m_object->mathNumeratorObject()->wrapper() : 0;
-}
-
-- (WebAccessibilityObjectWrapper *)accessibilityMathDenominatorObject
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- return m_object->mathDenominatorObject() ? m_object->mathDenominatorObject()->wrapper() : 0;
-}
-
-- (WebAccessibilityObjectWrapper *)accessibilityMathBaseObject
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- return m_object->mathBaseObject() ? m_object->mathBaseObject()->wrapper() : 0;
-}
-
-- (WebAccessibilityObjectWrapper *)accessibilityMathSubscriptObject
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- return m_object->mathSubscriptObject() ? m_object->mathSubscriptObject()->wrapper() : 0;
-}
-
-- (WebAccessibilityObjectWrapper *)accessibilityMathSuperscriptObject
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- return m_object->mathSuperscriptObject() ? m_object->mathSuperscriptObject()->wrapper() : 0;
-}
-
-- (WebAccessibilityObjectWrapper *)accessibilityMathUnderObject
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- return m_object->mathUnderObject() ? m_object->mathUnderObject()->wrapper() : 0;
-}
-
-- (WebAccessibilityObjectWrapper *)accessibilityMathOverObject
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- return m_object->mathOverObject() ? m_object->mathOverObject()->wrapper() : 0;
-}
-
-- (NSString *)accessibilityPlatformMathSubscriptKey
-{
- return @"AXMSubscriptObject";
-}
-
-- (NSString *)accessibilityPlatformMathSuperscriptKey
-{
- return @"AXMSuperscriptObject";
-}
-
-- (NSArray *)accessibilityMathPostscripts
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- return [self accessibilityMathPostscriptPairs];
-}
-
-- (NSArray *)accessibilityMathPrescripts
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- return [self accessibilityMathPrescriptPairs];
-}
-
-- (NSString *)accessibilityMathFencedOpenString
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- return m_object->mathFencedOpenString();
-}
-
-- (NSString *)accessibilityMathFencedCloseString
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- return m_object->mathFencedCloseString();
-}
-
-- (BOOL)accessibilityIsMathTopObject
-{
- if (![self _prepareAccessibilityCall])
- return NO;
-
- return m_object->roleValue() == DocumentMathRole;
-}
-
-- (NSInteger)accessibilityMathLineThickness
-{
- if (![self _prepareAccessibilityCall])
- return 0;
-
- return m_object->mathLineThickness();
-}
-
-- (NSString *)accessibilityMathType
-{
- if (![self _prepareAccessibilityCall])
- return nil;
-
- if (m_object->roleValue() == MathElementRole) {
- if (m_object->isMathFraction())
- return @"AXMathFraction";
- if (m_object->isMathFenced())
- return @"AXMathFenced";
- if (m_object->isMathSubscriptSuperscript())
- return @"AXMathSubscriptSuperscript";
- if (m_object->isMathRow())
- return @"AXMathRow";
- if (m_object->isMathUnderOver())
- return @"AXMathUnderOver";
- if (m_object->isMathSquareRoot())
- return @"AXMathSquareRoot";
- if (m_object->isMathRoot())
- return @"AXMathRoot";
- if (m_object->isMathText())
- return @"AXMathText";
- if (m_object->isMathNumber())
- return @"AXMathNumber";
- if (m_object->isMathIdentifier())
- return @"AXMathIdentifier";
- if (m_object->isMathTable())
- return @"AXMathTable";
- if (m_object->isMathTableRow())
- return @"AXMathTableRow";
- if (m_object->isMathTableCell())
- return @"AXMathTableCell";
- if (m_object->isMathFenceOperator())
- return @"AXMathFenceOperator";
- if (m_object->isMathSeparatorOperator())
- return @"AXMathSeparatorOperator";
- if (m_object->isMathOperator())
- return @"AXMathOperator";
- if (m_object->isMathMultiscript())
- return @"AXMathMultiscript";
- }
-
- return nil;
-}
-
-- (CGPoint)accessibilityClickPoint
-{
- return m_object->clickPoint();
-}
-
-// These are used by DRT so that it can know when notifications are sent.
-// Since they are static, only one callback can be installed at a time (that's all DRT should need).
-typedef void (*AXPostedNotificationCallback)(id element, NSString* notification, void* context);
-static AXPostedNotificationCallback AXNotificationCallback = 0;
-static void* AXPostedNotificationContext = 0;
-
-- (void)accessibilitySetPostedNotificationCallback:(AXPostedNotificationCallback)function withContext:(void*)context
-{
- AXNotificationCallback = function;
- AXPostedNotificationContext = context;
-}
-
-- (void)accessibilityPostedNotification:(NSString *)notificationName
-{
- if (AXNotificationCallback && notificationName)
- AXNotificationCallback(self, notificationName, AXPostedNotificationContext);
-}
-
-#ifndef NDEBUG
-- (NSString *)description
-{
- CGRect frame = [self accessibilityFrame];
- return [NSString stringWithFormat:@"Role: (%d) - Text: %@: Value: %@ -- Frame: %f %f %f %f", m_object ? m_object->roleValue() : 0, [self accessibilityLabel], [self accessibilityValue], frame.origin.x, frame.origin.y, frame.size.width, frame.size.height];
-}
-#endif
-
-@end
-
-#endif // HAVE(ACCESSIBILITY) && PLATFORM(IOS)
diff --git a/Source/WebCore/accessibility/mac/AXObjectCacheMac.mm b/Source/WebCore/accessibility/mac/AXObjectCacheMac.mm
deleted file mode 100644
index 58870836c..000000000
--- a/Source/WebCore/accessibility/mac/AXObjectCacheMac.mm
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2012 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#import "config.h"
-#import "AXObjectCache.h"
-
-#if HAVE(ACCESSIBILITY)
-
-#import "AccessibilityObject.h"
-#import "RenderObject.h"
-#import "WebAccessibilityObjectWrapperMac.h"
-#import "WebCoreSystemInterface.h"
-
-#import <wtf/PassRefPtr.h>
-
-#ifndef NSAccessibilityLiveRegionChangedNotification
-#define NSAccessibilityLiveRegionChangedNotification @"AXLiveRegionChanged"
-#endif
-
-// The simple Cocoa calls in this file don't throw exceptions.
-
-namespace WebCore {
-
-void AXObjectCache::detachWrapper(AccessibilityObject* obj)
-{
- [obj->wrapper() detach];
- obj->setWrapper(0);
-}
-
-void AXObjectCache::attachWrapper(AccessibilityObject* obj)
-{
- RetainPtr<WebAccessibilityObjectWrapper> wrapper = adoptNS([[WebAccessibilityObjectWrapper alloc] initWithAccessibilityObject:obj]);
- obj->setWrapper(wrapper.get());
-}
-
-void AXObjectCache::postPlatformNotification(AccessibilityObject* obj, AXNotification notification)
-{
- if (!obj)
- return;
-
- // Some notifications are unique to Safari and do not have NSAccessibility equivalents.
- NSString *macNotification;
- switch (notification) {
- case AXActiveDescendantChanged:
- // An active descendant change for trees means a selected rows change.
- if (obj->isTree())
- macNotification = NSAccessibilitySelectedRowsChangedNotification;
-
- // When a combobox uses active descendant, it means the selected item in its associated
- // list has changed. In these cases we should use selected children changed, because
- // we don't want the focus to change away from the combobox where the user is typing.
- else if (obj->isComboBox())
- macNotification = NSAccessibilitySelectedChildrenChangedNotification;
- else
- macNotification = NSAccessibilityFocusedUIElementChangedNotification;
- break;
- case AXAutocorrectionOccured:
- macNotification = @"AXAutocorrectionOccurred";
- break;
- case AXFocusedUIElementChanged:
- macNotification = NSAccessibilityFocusedUIElementChangedNotification;
- break;
- case AXLayoutComplete:
- macNotification = @"AXLayoutComplete";
- break;
- case AXLoadComplete:
- macNotification = @"AXLoadComplete";
- break;
- case AXInvalidStatusChanged:
- macNotification = @"AXInvalidStatusChanged";
- break;
- case AXSelectedChildrenChanged:
- if (obj->isAccessibilityTable())
- macNotification = NSAccessibilitySelectedRowsChangedNotification;
- else
- macNotification = NSAccessibilitySelectedChildrenChangedNotification;
- break;
- case AXSelectedTextChanged:
- macNotification = NSAccessibilitySelectedTextChangedNotification;
- break;
- case AXValueChanged:
- macNotification = NSAccessibilityValueChangedNotification;
- break;
- case AXLiveRegionChanged:
- macNotification = NSAccessibilityLiveRegionChangedNotification;
- break;
- case AXRowCountChanged:
- macNotification = NSAccessibilityRowCountChangedNotification;
- break;
- case AXRowExpanded:
- macNotification = NSAccessibilityRowExpandedNotification;
- break;
- case AXRowCollapsed:
- macNotification = NSAccessibilityRowCollapsedNotification;
- break;
- // Does not exist on Mac.
- case AXCheckedStateChanged:
- default:
- return;
- }
-
- // NSAccessibilityPostNotification will call this method, (but not when running DRT), so ASSERT here to make sure it does not crash.
- // https://bugs.webkit.org/show_bug.cgi?id=46662
- ASSERT([obj->wrapper() accessibilityIsIgnored] || true);
-
- NSAccessibilityPostNotification(obj->wrapper(), macNotification);
-
- // Used by DRT to know when notifications are posted.
- [obj->wrapper() accessibilityPostedNotification:macNotification];
-}
-
-void AXObjectCache::nodeTextChangePlatformNotification(AccessibilityObject*, AXTextChange, unsigned, const String&)
-{
-}
-
-void AXObjectCache::frameLoadingEventPlatformNotification(AccessibilityObject*, AXLoadingEvent)
-{
-}
-
-void AXObjectCache::handleFocusedUIElementChanged(Node*, Node*)
-{
- wkAccessibilityHandleFocusChanged();
-}
-
-void AXObjectCache::handleScrolledToAnchor(const Node*)
-{
-}
-
-}
-
-#endif // HAVE(ACCESSIBILITY)
diff --git a/Source/WebCore/accessibility/mac/AccessibilityObjectMac.mm b/Source/WebCore/accessibility/mac/AccessibilityObjectMac.mm
deleted file mode 100644
index 300ef18bd..000000000
--- a/Source/WebCore/accessibility/mac/AccessibilityObjectMac.mm
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2008 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#import "config.h"
-#import "AccessibilityObject.h"
-
-#if HAVE(ACCESSIBILITY)
-
-#import "WebAccessibilityObjectWrapperMac.h"
-#import "Widget.h"
-
-namespace WebCore {
-
-void AccessibilityObject::detachFromParent()
-{
- if (isAttachment())
- overrideAttachmentParent(0);
-}
-
-void AccessibilityObject::overrideAttachmentParent(AccessibilityObject* parent)
-{
- if (!isAttachment())
- return;
-
- id parentWrapper = nil;
- if (parent) {
- if (parent->accessibilityIsIgnored())
- parent = parent->parentObjectUnignored();
- parentWrapper = parent->wrapper();
- }
-
- [[wrapper() attachmentView] accessibilitySetOverrideValue:parentWrapper forAttribute:NSAccessibilityParentAttribute];
-}
-
-bool AccessibilityObject::accessibilityIgnoreAttachment() const
-{
- // FrameView attachments are now handled by AccessibilityScrollView,
- // so if this is the attachment, it should be ignored.
- Widget* widget = 0;
- if (isAttachment() && (widget = widgetForAttachmentView()) && widget->isFrameView())
- return true;
-
- if ([wrapper() attachmentView])
- return [[wrapper() attachmentView] accessibilityIsIgnored];
-
- // Attachments are ignored by default (unless we determine that we should expose them).
- return true;
-}
-
-AccessibilityObjectInclusion AccessibilityObject::accessibilityPlatformIncludesObject() const
-{
- if (isMenuListPopup() || isMenuListOption())
- return IgnoreObject;
-
- // Never expose an unknown object on the Mac. Clients of the AX API will not know what to do with it.
- // Special case is when the unknown object is actually an attachment.
- if (roleValue() == UnknownRole && !isAttachment())
- return IgnoreObject;
-
- return DefaultBehavior;
-}
-
-} // WebCore
-
-#endif // HAVE(ACCESSIBILITY)
diff --git a/Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperBase.mm b/Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperBase.mm
deleted file mode 100644
index c5cd4056e..000000000
--- a/Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperBase.mm
+++ /dev/null
@@ -1,404 +0,0 @@
-/*
- * Copyright (C) 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
- * its contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#import "config.h"
-#import "WebAccessibilityObjectWrapperBase.h"
-
-#if HAVE(ACCESSIBILITY)
-
-#import "AXObjectCache.h"
-#import "AccessibilityARIAGridRow.h"
-#import "AccessibilityList.h"
-#import "AccessibilityListBox.h"
-#import "AccessibilityRenderObject.h"
-#import "AccessibilityScrollView.h"
-#import "AccessibilitySpinButton.h"
-#import "AccessibilityTable.h"
-#import "AccessibilityTableCell.h"
-#import "AccessibilityTableColumn.h"
-#import "AccessibilityTableRow.h"
-#import "Chrome.h"
-#import "ChromeClient.h"
-#import "ColorMac.h"
-#import "ContextMenuController.h"
-#import "Font.h"
-#import "Frame.h"
-#import "FrameLoaderClient.h"
-#import "FrameSelection.h"
-#import "HTMLAnchorElement.h"
-#import "HTMLAreaElement.h"
-#import "HTMLFrameOwnerElement.h"
-#import "HTMLImageElement.h"
-#import "HTMLInputElement.h"
-#import "HTMLNames.h"
-#import "HTMLTextAreaElement.h"
-#import "LocalizedStrings.h"
-#import "Page.h"
-#import "RenderTextControl.h"
-#import "RenderView.h"
-#import "RenderWidget.h"
-#import "ScrollView.h"
-#import "SimpleFontData.h"
-#import "TextCheckerClient.h"
-#import "TextCheckingHelper.h"
-#import "TextIterator.h"
-#import "VisibleUnits.h"
-#import "WebCoreFrameView.h"
-#import "WebCoreObjCExtras.h"
-#import "WebCoreSystemInterface.h"
-#import "htmlediting.h"
-
-using namespace WebCore;
-using namespace HTMLNames;
-using namespace std;
-
-static NSArray *convertMathPairsToNSArray(const AccessibilityObject::AccessibilityMathMultiscriptPairs& pairs, NSString *subscriptKey, NSString *superscriptKey)
-{
- unsigned length = pairs.size();
- NSMutableArray *array = [NSMutableArray arrayWithCapacity:length];
- for (unsigned i = 0; i < length; ++i) {
- NSMutableDictionary *pairDictionary = [NSMutableDictionary dictionary];
- pair<AccessibilityObject*, AccessibilityObject*> pair = pairs[i];
- if (pair.first && pair.first->wrapper() && !pair.first->accessibilityIsIgnored())
- [pairDictionary setObject:pair.first->wrapper() forKey:subscriptKey];
- if (pair.second && pair.second->wrapper() && !pair.second->accessibilityIsIgnored())
- [pairDictionary setObject:pair.second->wrapper() forKey:superscriptKey];
- [array addObject:pairDictionary];
- }
- return array;
-}
-
-@implementation WebAccessibilityObjectWrapperBase
-
-- (id)initWithAccessibilityObject:(AccessibilityObject*)axObject
-{
- if (!(self = [super init]))
- return nil;
-
- m_object = axObject;
- return self;
-}
-
-- (void)detach
-{
- m_object = 0;
-}
-
-- (BOOL)updateObjectBackingStore
-{
- // Calling updateBackingStore() can invalidate this element so self must be retained.
- // If it does become invalidated, m_object will be nil.
- [[self retain] autorelease];
-
- if (!m_object)
- return NO;
-
- m_object->updateBackingStore();
- if (!m_object)
- return NO;
-
- return YES;
-}
-
-- (id)attachmentView
-{
- return nil;
-}
-
-- (AccessibilityObject*)accessibilityObject
-{
- return m_object;
-}
-
-// FIXME: Different kinds of elements are putting the title tag to use in different
-// AX fields. This should be rectified, but in the initial patch I want to achieve
-// parity with existing behavior.
-- (BOOL)titleTagShouldBeUsedInDescriptionField
-{
- return (m_object->isLink() && !m_object->isImageMapLink()) || m_object->isImage();
-}
-
-// On iOS, we don't have to return the value in the title. We can return the actual title, given the API.
-- (BOOL)fileUploadButtonReturnsValueInTitle
-{
- return YES;
-}
-
-// This should be the "visible" text that's actually on the screen if possible.
-// If there's alternative text, that can override the title.
-- (NSString *)accessibilityTitle
-{
- // Static text objects should not have a title. Its content is communicated in its AXValue.
- if (m_object->roleValue() == StaticTextRole)
- return [NSString string];
-
- // A file upload button presents a challenge because it has button text and a value, but the
- // API doesn't support this paradigm.
- // The compromise is to return the button type in the role description and the value of the file path in the title
- if (m_object->isFileUploadButton() && [self fileUploadButtonReturnsValueInTitle])
- return m_object->stringValue();
-
- Vector<AccessibilityText> textOrder;
- m_object->accessibilityText(textOrder);
-
- unsigned length = textOrder.size();
- for (unsigned k = 0; k < length; k++) {
- const AccessibilityText& text = textOrder[k];
-
- // If we have alternative text, then we should not expose a title.
- if (text.textSource == AlternativeText)
- break;
-
- // Once we encounter visible text, or the text from our children that should be used foremost.
- if (text.textSource == VisibleText || text.textSource == ChildrenText)
- return text.text;
-
- // If there's an element that labels this object and it's not exposed, then we should use
- // that text as our title.
- if (text.textSource == LabelByElementText && !m_object->exposesTitleUIElement())
- return text.text;
-
- // FIXME: The title tag is used in certain cases for the title. This usage should
- // probably be in the description field since it's not "visible".
- if (text.textSource == TitleTagText && ![self titleTagShouldBeUsedInDescriptionField])
- return text.text;
- }
-
- return [NSString string];
-}
-
-- (NSString *)accessibilityDescription
-{
- // Static text objects should not have a description. Its content is communicated in its AXValue.
- // One exception is the media control labels that have a value and a description. Those are set programatically.
- if (m_object->roleValue() == StaticTextRole && !m_object->isMediaControlLabel())
- return [NSString string];
-
- Vector<AccessibilityText> textOrder;
- m_object->accessibilityText(textOrder);
-
- unsigned length = textOrder.size();
- for (unsigned k = 0; k < length; k++) {
- const AccessibilityText& text = textOrder[k];
-
- if (text.textSource == AlternativeText)
- return text.text;
-
- if (text.textSource == TitleTagText && [self titleTagShouldBeUsedInDescriptionField])
- return text.text;
- }
-
- return [NSString string];
-}
-
-- (NSString *)accessibilityHelpText
-{
- Vector<AccessibilityText> textOrder;
- m_object->accessibilityText(textOrder);
-
- unsigned length = textOrder.size();
- bool descriptiveTextAvailable = false;
- for (unsigned k = 0; k < length; k++) {
- const AccessibilityText& text = textOrder[k];
-
- if (text.textSource == HelpText || text.textSource == SummaryText)
- return text.text;
-
- // If an element does NOT have other descriptive text the title tag should be used as its descriptive text.
- // But, if those ARE available, then the title tag should be used for help text instead.
- switch (text.textSource) {
- case AlternativeText:
- case VisibleText:
- case ChildrenText:
- case LabelByElementText:
- descriptiveTextAvailable = true;
- default:
- break;
- }
-
- if (text.textSource == TitleTagText && descriptiveTextAvailable)
- return text.text;
- }
-
- return [NSString string];
-}
-
-struct PathConversionInfo {
- WebAccessibilityObjectWrapperBase *wrapper;
- CGMutablePathRef path;
-};
-
-static void ConvertPathToScreenSpaceFunction(void* info, const PathElement* element)
-{
- PathConversionInfo* conversion = (PathConversionInfo*)info;
- WebAccessibilityObjectWrapperBase *wrapper = conversion->wrapper;
- CGMutablePathRef newPath = conversion->path;
- switch (element->type) {
- case PathElementMoveToPoint:
- {
- CGPoint newPoint = [wrapper convertPointToScreenSpace:element->points[0]];
- CGPathMoveToPoint(newPath, nil, newPoint.x, newPoint.y);
- break;
- }
- case PathElementAddLineToPoint:
- {
- CGPoint newPoint = [wrapper convertPointToScreenSpace:element->points[0]];
- CGPathAddLineToPoint(newPath, nil, newPoint.x, newPoint.y);
- break;
- }
- case PathElementAddQuadCurveToPoint:
- {
- CGPoint newPoint1 = [wrapper convertPointToScreenSpace:element->points[0]];
- CGPoint newPoint2 = [wrapper convertPointToScreenSpace:element->points[1]];
- CGPathAddQuadCurveToPoint(newPath, nil, newPoint1.x, newPoint1.y, newPoint2.x, newPoint2.y);
- break;
- }
- case PathElementAddCurveToPoint:
- {
- CGPoint newPoint1 = [wrapper convertPointToScreenSpace:element->points[0]];
- CGPoint newPoint2 = [wrapper convertPointToScreenSpace:element->points[1]];
- CGPoint newPoint3 = [wrapper convertPointToScreenSpace:element->points[2]];
- CGPathAddCurveToPoint(newPath, nil, newPoint1.x, newPoint1.y, newPoint2.x, newPoint2.y, newPoint3.x, newPoint3.y);
- break;
- }
- case PathElementCloseSubpath:
- {
- CGPathCloseSubpath(newPath);
- break;
- }
- }
-}
-
-- (CGPathRef)convertPathToScreenSpace:(Path &)path
-{
- PathConversionInfo conversion = { self, CGPathCreateMutable() };
- path.apply(&conversion, ConvertPathToScreenSpaceFunction);
- return (CGPathRef)[(id)conversion.path autorelease];
-}
-
-- (CGPoint)convertPointToScreenSpace:(FloatPoint &)point
-{
- UNUSED_PARAM(point);
- ASSERT_NOT_REACHED();
- return CGPointZero;
-}
-
-- (NSString *)ariaLandmarkRoleDescription
-{
- switch (m_object->roleValue()) {
- case LandmarkApplicationRole:
- return AXARIAContentGroupText(@"ARIALandmarkApplication");
- case LandmarkBannerRole:
- return AXARIAContentGroupText(@"ARIALandmarkBanner");
- case LandmarkComplementaryRole:
- return AXARIAContentGroupText(@"ARIALandmarkComplementary");
- case LandmarkContentInfoRole:
- return AXARIAContentGroupText(@"ARIALandmarkContentInfo");
- case LandmarkMainRole:
- return AXARIAContentGroupText(@"ARIALandmarkMain");
- case LandmarkNavigationRole:
- return AXARIAContentGroupText(@"ARIALandmarkNavigation");
- case LandmarkSearchRole:
- return AXARIAContentGroupText(@"ARIALandmarkSearch");
- case ApplicationAlertRole:
- return AXARIAContentGroupText(@"ARIAApplicationAlert");
- case ApplicationAlertDialogRole:
- return AXARIAContentGroupText(@"ARIAApplicationAlertDialog");
- case ApplicationDialogRole:
- return AXARIAContentGroupText(@"ARIAApplicationDialog");
- case ApplicationLogRole:
- return AXARIAContentGroupText(@"ARIAApplicationLog");
- case ApplicationMarqueeRole:
- return AXARIAContentGroupText(@"ARIAApplicationMarquee");
- case ApplicationStatusRole:
- return AXARIAContentGroupText(@"ARIAApplicationStatus");
- case ApplicationTimerRole:
- return AXARIAContentGroupText(@"ARIAApplicationTimer");
- case DocumentRole:
- return AXARIAContentGroupText(@"ARIADocument");
- case DocumentArticleRole:
- return AXARIAContentGroupText(@"ARIADocumentArticle");
- case DocumentMathRole:
- return AXARIAContentGroupText(@"ARIADocumentMath");
- case DocumentNoteRole:
- return AXARIAContentGroupText(@"ARIADocumentNote");
- case DocumentRegionRole:
- return AXARIAContentGroupText(@"ARIADocumentRegion");
- case UserInterfaceTooltipRole:
- return AXARIAContentGroupText(@"ARIAUserInterfaceTooltip");
- case TabPanelRole:
- return AXARIAContentGroupText(@"ARIATabPanel");
- default:
- return nil;
- }
-}
-
-- (NSString *)accessibilityPlatformMathSubscriptKey
-{
- ASSERT_NOT_REACHED();
- return nil;
-}
-
-- (NSString *)accessibilityPlatformMathSuperscriptKey
-{
- ASSERT_NOT_REACHED();
- return nil;
-}
-
-- (NSArray *)accessibilityMathPostscriptPairs
-{
- AccessibilityObject::AccessibilityMathMultiscriptPairs pairs;
- m_object->mathPostscripts(pairs);
- return convertMathPairsToNSArray(pairs, [self accessibilityPlatformMathSubscriptKey], [self accessibilityPlatformMathSuperscriptKey]);
-}
-
-- (NSArray *)accessibilityMathPrescriptPairs
-{
- AccessibilityObject::AccessibilityMathMultiscriptPairs pairs;
- m_object->mathPrescripts(pairs);
- return convertMathPairsToNSArray(pairs, [self accessibilityPlatformMathSubscriptKey], [self accessibilityPlatformMathSuperscriptKey]);
-}
-
-// This is set by DRT when it wants to listen for notifications.
-static BOOL accessibilityShouldRepostNotifications;
-+ (void)accessibilitySetShouldRepostNotifications:(BOOL)repost
-{
- accessibilityShouldRepostNotifications = repost;
-}
-
-- (void)accessibilityPostedNotification:(NSString *)notificationName
-{
- if (accessibilityShouldRepostNotifications) {
- NSDictionary* userInfo = [NSDictionary dictionaryWithObjectsAndKeys:notificationName, @"notificationName", nil];
- [[NSNotificationCenter defaultCenter] postNotificationName:@"AXDRTNotification" object:self userInfo:userInfo];
- }
-}
-
-@end
-
-#endif // HAVE(ACCESSIBILITY)
diff --git a/Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.mm b/Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.mm
deleted file mode 100644
index 6b98c3197..000000000
--- a/Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.mm
+++ /dev/null
@@ -1,3697 +0,0 @@
-/*
- * Copyright (C) 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
- * its contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#import "config.h"
-#import "WebAccessibilityObjectWrapperMac.h"
-
-#if HAVE(ACCESSIBILITY)
-
-#import "AXObjectCache.h"
-#import "AccessibilityARIAGridRow.h"
-#import "AccessibilityList.h"
-#import "AccessibilityListBox.h"
-#import "AccessibilityRenderObject.h"
-#import "AccessibilityScrollView.h"
-#import "AccessibilitySpinButton.h"
-#import "AccessibilityTable.h"
-#import "AccessibilityTableCell.h"
-#import "AccessibilityTableColumn.h"
-#import "AccessibilityTableRow.h"
-#import "Chrome.h"
-#import "ChromeClient.h"
-#import "ColorMac.h"
-#import "ContextMenuController.h"
-#import "Editor.h"
-#import "Font.h"
-#import "Frame.h"
-#import "FrameLoaderClient.h"
-#import "FrameSelection.h"
-#import "HTMLAnchorElement.h"
-#import "HTMLAreaElement.h"
-#import "HTMLFrameOwnerElement.h"
-#import "HTMLImageElement.h"
-#import "HTMLInputElement.h"
-#import "HTMLNames.h"
-#import "HTMLTextAreaElement.h"
-#import "LocalizedStrings.h"
-#import "Page.h"
-#import "RenderTextControl.h"
-#import "RenderView.h"
-#import "RenderWidget.h"
-#import "ScrollView.h"
-#import "SimpleFontData.h"
-#import "TextCheckerClient.h"
-#import "TextCheckingHelper.h"
-#import "TextIterator.h"
-#import "VisibleUnits.h"
-#import "WebCoreFrameView.h"
-#import "WebCoreObjCExtras.h"
-#import "WebCoreSystemInterface.h"
-#import "htmlediting.h"
-
-using namespace WebCore;
-using namespace HTMLNames;
-using namespace std;
-
-// Cell Tables
-#ifndef NSAccessibilitySelectedCellsAttribute
-#define NSAccessibilitySelectedCellsAttribute @"AXSelectedCells"
-#endif
-
-#ifndef NSAccessibilityVisibleCellsAttribute
-#define NSAccessibilityVisibleCellsAttribute @"AXVisibleCells"
-#endif
-
-#ifndef NSAccessibilityRowHeaderUIElementsAttribute
-#define NSAccessibilityRowHeaderUIElementsAttribute @"AXRowHeaderUIElements"
-#endif
-
-#ifndef NSAccessibilityRowIndexRangeAttribute
-#define NSAccessibilityRowIndexRangeAttribute @"AXRowIndexRange"
-#endif
-
-#ifndef NSAccessibilityColumnIndexRangeAttribute
-#define NSAccessibilityColumnIndexRangeAttribute @"AXColumnIndexRange"
-#endif
-
-#ifndef NSAccessibilityCellForColumnAndRowParameterizedAttribute
-#define NSAccessibilityCellForColumnAndRowParameterizedAttribute @"AXCellForColumnAndRow"
-#endif
-
-#ifndef NSAccessibilityCellRole
-#define NSAccessibilityCellRole @"AXCell"
-#endif
-
-// Lists
-#ifndef NSAccessibilityContentListSubrole
-#define NSAccessibilityContentListSubrole @"AXContentList"
-#endif
-
-#ifndef NSAccessibilityDefinitionListSubrole
-#define NSAccessibilityDefinitionListSubrole @"AXDefinitionList"
-#endif
-
-#ifndef NSAccessibilityDescriptionListSubrole
-#define NSAccessibilityDescriptionListSubrole @"AXDescriptionList"
-#endif
-
-// Miscellaneous
-#ifndef NSAccessibilityBlockQuoteLevelAttribute
-#define NSAccessibilityBlockQuoteLevelAttribute @"AXBlockQuoteLevel"
-#endif
-
-#ifndef NSAccessibilityAccessKeyAttribute
-#define NSAccessibilityAccessKeyAttribute @"AXAccessKey"
-#endif
-
-#ifndef NSAccessibilityLanguageAttribute
-#define NSAccessibilityLanguageAttribute @"AXLanguage"
-#endif
-
-#ifndef NSAccessibilityRequiredAttribute
-#define NSAccessibilityRequiredAttribute @"AXRequired"
-#endif
-
-#ifndef NSAccessibilityInvalidAttribute
-#define NSAccessibilityInvalidAttribute @"AXInvalid"
-#endif
-
-#ifndef NSAccessibilityOwnsAttribute
-#define NSAccessibilityOwnsAttribute @"AXOwns"
-#endif
-
-#ifndef NSAccessibilityGrabbedAttribute
-#define NSAccessibilityGrabbedAttribute @"AXGrabbed"
-#endif
-
-#ifndef NSAccessibilityDropEffectsAttribute
-#define NSAccessibilityDropEffectsAttribute @"AXDropEffects"
-#endif
-
-#ifndef NSAccessibilityARIALiveAttribute
-#define NSAccessibilityARIALiveAttribute @"AXARIALive"
-#endif
-
-#ifndef NSAccessibilityARIAAtomicAttribute
-#define NSAccessibilityARIAAtomicAttribute @"AXARIAAtomic"
-#endif
-
-#ifndef NSAccessibilityARIARelevantAttribute
-#define NSAccessibilityARIARelevantAttribute @"AXARIARelevant"
-#endif
-
-#ifndef NSAccessibilityARIABusyAttribute
-#define NSAccessibilityARIABusyAttribute @"AXARIABusy"
-#endif
-
-#ifndef NSAccessibilityARIAPosInSetAttribute
-#define NSAccessibilityARIAPosInSetAttribute @"AXARIAPosInSet"
-#endif
-
-#ifndef NSAccessibilityARIASetSizeAttribute
-#define NSAccessibilityARIASetSizeAttribute @"AXARIASetSize"
-#endif
-
-#ifndef NSAccessibilityLoadingProgressAttribute
-#define NSAccessibilityLoadingProgressAttribute @"AXLoadingProgress"
-#endif
-
-#ifndef NSAccessibilityHasPopupAttribute
-#define NSAccessibilityHasPopupAttribute @"AXHasPopup"
-#endif
-
-#ifndef NSAccessibilityPlaceholderValueAttribute
-#define NSAccessibilityPlaceholderValueAttribute @"AXPlaceholderValue"
-#endif
-
-// Search
-#ifndef NSAccessibilityUIElementsForSearchPredicateParameterizedAttribute
-#define NSAccessibilityUIElementsForSearchPredicateParameterizedAttribute @"AXUIElementsForSearchPredicate"
-#endif
-
-// Search Keys
-#ifndef NSAccessibilityAnyTypeSearchKey
-#define NSAccessibilityAnyTypeSearchKey @"AXAnyTypeSearchKey"
-#endif
-
-#ifndef NSAccessibilityBlockquoteSameLevelSearchKey
-#define NSAccessibilityBlockquoteSameLevelSearchKey @"AXBlockquoteSameLevelSearchKey"
-#endif
-
-#ifndef NSAccessibilityBlockquoteSearchKey
-#define NSAccessibilityBlockquoteSearchKey @"AXBlockquoteSearchKey"
-#endif
-
-#ifndef NSAccessibilityBoldFontSearchKey
-#define NSAccessibilityBoldFontSearchKey @"AXBoldFontSearchKey"
-#endif
-
-#ifndef NSAccessibilityButtonSearchKey
-#define NSAccessibilityButtonSearchKey @"AXButtonSearchKey"
-#endif
-
-#ifndef NSAccessibilityCheckBoxSearchKey
-#define NSAccessibilityCheckBoxSearchKey @"AXCheckBoxSearchKey"
-#endif
-
-#ifndef NSAccessibilityControlSearchKey
-#define NSAccessibilityControlSearchKey @"AXControlSearchKey"
-#endif
-
-#ifndef NSAccessibilityDifferentTypeSearchKey
-#define NSAccessibilityDifferentTypeSearchKey @"AXDifferentTypeSearchKey"
-#endif
-
-#ifndef NSAccessibilityFontChangeSearchKey
-#define NSAccessibilityFontChangeSearchKey @"AXFontChangeSearchKey"
-#endif
-
-#ifndef NSAccessibilityFontColorChangeSearchKey
-#define NSAccessibilityFontColorChangeSearchKey @"AXFontColorChangeSearchKey"
-#endif
-
-#ifndef NSAccessibilityFrameSearchKey
-#define NSAccessibilityFrameSearchKey @"AXFrameSearchKey"
-#endif
-
-#ifndef NSAccessibilityGraphicSearchKey
-#define NSAccessibilityGraphicSearchKey @"AXGraphicSearchKey"
-#endif
-
-#ifndef NSAccessibilityHeadingLevel1SearchKey
-#define NSAccessibilityHeadingLevel1SearchKey @"AXHeadingLevel1SearchKey"
-#endif
-
-#ifndef NSAccessibilityHeadingLevel2SearchKey
-#define NSAccessibilityHeadingLevel2SearchKey @"AXHeadingLevel2SearchKey"
-#endif
-
-#ifndef NSAccessibilityHeadingLevel3SearchKey
-#define NSAccessibilityHeadingLevel3SearchKey @"AXHeadingLevel3SearchKey"
-#endif
-
-#ifndef NSAccessibilityHeadingLevel4SearchKey
-#define NSAccessibilityHeadingLevel4SearchKey @"AXHeadingLevel4SearchKey"
-#endif
-
-#ifndef NSAccessibilityHeadingLevel5SearchKey
-#define NSAccessibilityHeadingLevel5SearchKey @"AXHeadingLevel5SearchKey"
-#endif
-
-#ifndef NSAccessibilityHeadingLevel6SearchKey
-#define NSAccessibilityHeadingLevel6SearchKey @"AXHeadingLevel6SearchKey"
-#endif
-
-#ifndef NSAccessibilityHeadingSameLevelSearchKey
-#define NSAccessibilityHeadingSameLevelSearchKey @"AXHeadingSameLevelSearchKey"
-#endif
-
-#ifndef NSAccessibilityHeadingSearchKey
-#define NSAccessibilityHeadingSearchKey @"AXHeadingSearchKey"
-#endif
-
-#ifndef NSAccessibilityHighlightedSearchKey
-#define NSAccessibilityHighlightedSearchKey @"AXHighlightedSearchKey"
-#endif
-
-#ifndef NSAccessibilityItalicFontSearchKey
-#define NSAccessibilityItalicFontSearchKey @"AXItalicFontSearchKey"
-#endif
-
-#ifndef NSAccessibilityLandmarkSearchKey
-#define NSAccessibilityLandmarkSearchKey @"AXLandmarkSearchKey"
-#endif
-
-#ifndef NSAccessibilityLinkSearchKey
-#define NSAccessibilityLinkSearchKey @"AXLinkSearchKey"
-#endif
-
-#ifndef NSAccessibilityListSearchKey
-#define NSAccessibilityListSearchKey @"AXListSearchKey"
-#endif
-
-#ifndef NSAccessibilityLiveRegionSearchKey
-#define NSAccessibilityLiveRegionSearchKey @"AXLiveRegionSearchKey"
-#endif
-
-#ifndef NSAccessibilityMisspelledWordSearchKey
-#define NSAccessibilityMisspelledWordSearchKey @"AXMisspelledWordSearchKey"
-#endif
-
-#ifndef NSAccessibilityPlainTextSearchKey
-#define NSAccessibilityPlainTextSearchKey @"AXPlainTextSearchKey"
-#endif
-
-#ifndef NSAccessibilityRadioGroupSearchKey
-#define NSAccessibilityRadioGroupSearchKey @"AXRadioGroupSearchKey"
-#endif
-
-#ifndef NSAccessibilitySameTypeSearchKey
-#define NSAccessibilitySameTypeSearchKey @"AXSameTypeSearchKey"
-#endif
-
-#ifndef NSAccessibilityStaticTextSearchKey
-#define NSAccessibilityStaticTextSearchKey @"AXStaticTextSearchKey"
-#endif
-
-#ifndef NSAccessibilityStyleChangeSearchKey
-#define NSAccessibilityStyleChangeSearchKey @"AXStyleChangeSearchKey"
-#endif
-
-#ifndef NSAccessibilityTableSameLevelSearchKey
-#define NSAccessibilityTableSameLevelSearchKey @"AXTableSameLevelSearchKey"
-#endif
-
-#ifndef NSAccessibilityTableSearchKey
-#define NSAccessibilityTableSearchKey @"AXTableSearchKey"
-#endif
-
-#ifndef NSAccessibilityTextFieldSearchKey
-#define NSAccessibilityTextFieldSearchKey @"AXTextFieldSearchKey"
-#endif
-
-#ifndef NSAccessibilityUnderlineSearchKey
-#define NSAccessibilityUnderlineSearchKey @"AXUnderlineSearchKey"
-#endif
-
-#ifndef NSAccessibilityUnvisitedLinkSearchKey
-#define NSAccessibilityUnvisitedLinkSearchKey @"AXUnvisitedLinkSearchKey"
-#endif
-
-#ifndef NSAccessibilityVisitedLinkSearchKey
-#define NSAccessibilityVisitedLinkSearchKey @"AXVisitedLinkSearchKey"
-#endif
-
-#define NSAccessibilityTextMarkerIsValidParameterizedAttribute @"AXTextMarkerIsValid"
-#define NSAccessibilityIndexForTextMarkerParameterizedAttribute @"AXIndexForTextMarker"
-#define NSAccessibilityTextMarkerForIndexParameterizedAttribute @"AXTextMarkerForIndex"
-
-#ifndef NSAccessibilityScrollToVisibleAction
-#define NSAccessibilityScrollToVisibleAction @"AXScrollToVisible"
-#endif
-
-#ifndef NSAccessibilityPathAttribute
-#define NSAccessibilityPathAttribute @"AXPath"
-#endif
-
-// Math attributes
-#define NSAccessibilityMathRootRadicandAttribute @"AXMathRootRadicand"
-#define NSAccessibilityMathRootIndexAttribute @"AXMathRootIndex"
-#define NSAccessibilityMathFractionDenominatorAttribute @"AXMathFractionDenominator"
-#define NSAccessibilityMathFractionNumeratorAttribute @"AXMathFractionNumerator"
-#define NSAccessibilityMathBaseAttribute @"AXMathBase"
-#define NSAccessibilityMathSubscriptAttribute @"AXMathSubscript"
-#define NSAccessibilityMathSuperscriptAttribute @"AXMathSuperscript"
-#define NSAccessibilityMathUnderAttribute @"AXMathUnder"
-#define NSAccessibilityMathOverAttribute @"AXMathOver"
-#define NSAccessibilityMathFencedOpenAttribute @"AXMathFencedOpen"
-#define NSAccessibilityMathFencedCloseAttribute @"AXMathFencedClose"
-#define NSAccessibilityMathLineThicknessAttribute @"AXMathLineThickness"
-#define NSAccessibilityMathPrescriptsAttribute @"AXMathPrescripts"
-#define NSAccessibilityMathPostscriptsAttribute @"AXMathPostscripts"
-
-@implementation WebAccessibilityObjectWrapper
-
-- (void)unregisterUniqueIdForUIElement
-{
- wkUnregisterUniqueIdForElement(self);
-}
-
-- (void)detach
-{
- // Send unregisterUniqueIdForUIElement unconditionally because if it is
- // ever accidentally not done (via other bugs in our AX implementation) you
- // end up with a crash like <rdar://problem/4273149>. It is safe and not
- // expensive to send even if the object is not registered.
- [self unregisterUniqueIdForUIElement];
- [super detach];
-}
-
-- (id)attachmentView
-{
- ASSERT(m_object->isAttachment());
- Widget* widget = m_object->widgetForAttachmentView();
- if (!widget)
- return nil;
- return NSAccessibilityUnignoredDescendant(widget->platformWidget());
-}
-
-#pragma mark SystemInterface wrappers
-
-static inline id CFAutoreleaseHelper(CFTypeRef obj)
-{
- if (obj)
- CFMakeCollectable(obj);
- [(id)obj autorelease];
- return (id)obj;
-}
-
-static inline BOOL AXObjectIsTextMarker(id obj)
-{
- return obj != nil && CFGetTypeID(obj) == wkGetAXTextMarkerTypeID();
-}
-
-static inline BOOL AXObjectIsTextMarkerRange(id obj)
-{
- return obj != nil && CFGetTypeID(obj) == wkGetAXTextMarkerRangeTypeID();
-}
-
-static id AXTextMarkerRange(id startMarker, id endMarker)
-{
- ASSERT(startMarker != nil);
- ASSERT(endMarker != nil);
- ASSERT(CFGetTypeID(startMarker) == wkGetAXTextMarkerTypeID());
- ASSERT(CFGetTypeID(endMarker) == wkGetAXTextMarkerTypeID());
- return CFAutoreleaseHelper(wkCreateAXTextMarkerRange((CFTypeRef)startMarker, (CFTypeRef)endMarker));
-}
-
-static id AXTextMarkerRangeStart(id range)
-{
- ASSERT(range != nil);
- ASSERT(CFGetTypeID(range) == wkGetAXTextMarkerRangeTypeID());
- return CFAutoreleaseHelper(wkCopyAXTextMarkerRangeStart(range));
-}
-
-static id AXTextMarkerRangeEnd(id range)
-{
- ASSERT(range != nil);
- ASSERT(CFGetTypeID(range) == wkGetAXTextMarkerRangeTypeID());
- return CFAutoreleaseHelper(wkCopyAXTextMarkerRangeEnd(range));
-}
-
-#pragma mark Search helpers
-
-typedef HashMap<String, AccessibilitySearchKey> AccessibilitySearchKeyMap;
-
-struct SearchKeyEntry {
- String key;
- AccessibilitySearchKey value;
-};
-
-static AccessibilitySearchKeyMap* createAccessibilitySearchKeyMap()
-{
- const SearchKeyEntry searchKeys[] = {
- { NSAccessibilityAnyTypeSearchKey, AnyTypeSearchKey },
- { NSAccessibilityBlockquoteSameLevelSearchKey, BlockquoteSameLevelSearchKey },
- { NSAccessibilityBlockquoteSearchKey, BlockquoteSearchKey },
- { NSAccessibilityBoldFontSearchKey, BoldFontSearchKey },
- { NSAccessibilityButtonSearchKey, ButtonSearchKey },
- { NSAccessibilityCheckBoxSearchKey, CheckBoxSearchKey },
- { NSAccessibilityControlSearchKey, ControlSearchKey },
- { NSAccessibilityDifferentTypeSearchKey, DifferentTypeSearchKey },
- { NSAccessibilityFontChangeSearchKey, FontChangeSearchKey },
- { NSAccessibilityFontColorChangeSearchKey, FontColorChangeSearchKey },
- { NSAccessibilityFrameSearchKey, FrameSearchKey },
- { NSAccessibilityGraphicSearchKey, GraphicSearchKey },
- { NSAccessibilityHeadingLevel1SearchKey, HeadingLevel1SearchKey },
- { NSAccessibilityHeadingLevel2SearchKey, HeadingLevel2SearchKey },
- { NSAccessibilityHeadingLevel3SearchKey, HeadingLevel3SearchKey },
- { NSAccessibilityHeadingLevel4SearchKey, HeadingLevel4SearchKey },
- { NSAccessibilityHeadingLevel5SearchKey, HeadingLevel5SearchKey },
- { NSAccessibilityHeadingLevel6SearchKey, HeadingLevel6SearchKey },
- { NSAccessibilityHeadingSameLevelSearchKey, HeadingSameLevelSearchKey },
- { NSAccessibilityHeadingSearchKey, HeadingSearchKey },
- { NSAccessibilityHighlightedSearchKey, HighlightedSearchKey },
- { NSAccessibilityItalicFontSearchKey, ItalicFontSearchKey },
- { NSAccessibilityLandmarkSearchKey, LandmarkSearchKey },
- { NSAccessibilityLinkSearchKey, LinkSearchKey },
- { NSAccessibilityListSearchKey, ListSearchKey },
- { NSAccessibilityLiveRegionSearchKey, LiveRegionSearchKey },
- { NSAccessibilityMisspelledWordSearchKey, MisspelledWordSearchKey },
- { NSAccessibilityPlainTextSearchKey, PlainTextSearchKey },
- { NSAccessibilityRadioGroupSearchKey, RadioGroupSearchKey },
- { NSAccessibilitySameTypeSearchKey, SameTypeSearchKey },
- { NSAccessibilityStaticTextSearchKey, StaticTextSearchKey },
- { NSAccessibilityStyleChangeSearchKey, StyleChangeSearchKey },
- { NSAccessibilityTableSameLevelSearchKey, TableSameLevelSearchKey },
- { NSAccessibilityTableSearchKey, TableSearchKey },
- { NSAccessibilityTextFieldSearchKey, TextFieldSearchKey },
- { NSAccessibilityUnderlineSearchKey, UnderlineSearchKey },
- { NSAccessibilityUnvisitedLinkSearchKey, UnvisitedLinkSearchKey },
- { NSAccessibilityVisitedLinkSearchKey, VisitedLinkSearchKey }
- };
-
- AccessibilitySearchKeyMap* searchKeyMap = new AccessibilitySearchKeyMap;
- for (size_t i = 0; i < WTF_ARRAY_LENGTH(searchKeys); i++)
- searchKeyMap->set(searchKeys[i].key, searchKeys[i].value);
-
- return searchKeyMap;
-}
-
-static AccessibilitySearchKey accessibilitySearchKeyForString(const String& value)
-{
- if (value.isEmpty())
- return AnyTypeSearchKey;
-
- static const AccessibilitySearchKeyMap* searchKeyMap = createAccessibilitySearchKeyMap();
-
- AccessibilitySearchKey searchKey = searchKeyMap->get(value);
-
- return searchKey ? searchKey : AnyTypeSearchKey;
-}
-
-#pragma mark Text Marker helpers
-
-static id textMarkerForVisiblePosition(AXObjectCache* cache, const VisiblePosition& visiblePos)
-{
- ASSERT(cache);
-
- TextMarkerData textMarkerData;
- cache->textMarkerDataForVisiblePosition(textMarkerData, visiblePos);
- if (!textMarkerData.axID)
- return nil;
-
- return CFAutoreleaseHelper(wkCreateAXTextMarker(&textMarkerData, sizeof(textMarkerData)));
-}
-
-- (id)textMarkerForVisiblePosition:(const VisiblePosition &)visiblePos
-{
- return textMarkerForVisiblePosition(m_object->axObjectCache(), visiblePos);
-}
-
-static VisiblePosition visiblePositionForTextMarker(AXObjectCache* cache, CFTypeRef textMarker)
-{
- ASSERT(cache);
-
- if (!textMarker)
- return VisiblePosition();
- TextMarkerData textMarkerData;
- if (!wkGetBytesFromAXTextMarker(textMarker, &textMarkerData, sizeof(textMarkerData)))
- return VisiblePosition();
-
- return cache->visiblePositionForTextMarkerData(textMarkerData);
-}
-
-- (VisiblePosition)visiblePositionForTextMarker:(id)textMarker
-{
- return visiblePositionForTextMarker(m_object->axObjectCache(), textMarker);
-}
-
-static VisiblePosition visiblePositionForStartOfTextMarkerRange(AXObjectCache *cache, id textMarkerRange)
-{
- return visiblePositionForTextMarker(cache, AXTextMarkerRangeStart(textMarkerRange));
-}
-
-static VisiblePosition visiblePositionForEndOfTextMarkerRange(AXObjectCache *cache, id textMarkerRange)
-{
- return visiblePositionForTextMarker(cache, AXTextMarkerRangeEnd(textMarkerRange));
-}
-
-static id textMarkerRangeFromMarkers(id textMarker1, id textMarker2)
-{
- if (!textMarker1 || !textMarker2)
- return nil;
-
- return AXTextMarkerRange(textMarker1, textMarker2);
-}
-
-// When modifying attributed strings, the range can come from a source which may provide faulty information (e.g. the spell checker).
-// To protect against such cases the range should be validated before adding or removing attributes.
-static BOOL AXAttributedStringRangeIsValid(NSAttributedString* attrString, NSRange range)
-{
- return (range.location < [attrString length] && NSMaxRange(range) <= [attrString length]);
-}
-
-static void AXAttributeStringSetFont(NSMutableAttributedString* attrString, NSString* attribute, NSFont* font, NSRange range)
-{
- if (!AXAttributedStringRangeIsValid(attrString, range))
- return;
-
- if (font) {
- NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:
- [font fontName] , NSAccessibilityFontNameKey,
- [font familyName] , NSAccessibilityFontFamilyKey,
- [font displayName] , NSAccessibilityVisibleNameKey,
- [NSNumber numberWithFloat:[font pointSize]] , NSAccessibilityFontSizeKey,
- nil];
-
- [attrString addAttribute:attribute value:dict range:range];
- } else
- [attrString removeAttribute:attribute range:range];
-
-}
-
-static CGColorRef CreateCGColorIfDifferent(NSColor* nsColor, CGColorRef existingColor)
-{
- // get color information assuming NSDeviceRGBColorSpace
- NSColor* rgbColor = [nsColor colorUsingColorSpaceName:NSDeviceRGBColorSpace];
- if (rgbColor == nil)
- rgbColor = [NSColor blackColor];
- CGFloat components[4];
- [rgbColor getRed:&components[0] green:&components[1] blue:&components[2] alpha:&components[3]];
-
- // create a new CGColorRef to return
- CGColorSpaceRef cgColorSpace = CGColorSpaceCreateDeviceRGB();
- CGColorRef cgColor = CGColorCreate(cgColorSpace, components);
- CGColorSpaceRelease(cgColorSpace);
-
- // check for match with existing color
- if (existingColor && CGColorEqualToColor(cgColor, existingColor)) {
- CGColorRelease(cgColor);
- cgColor = 0;
- }
-
- return cgColor;
-}
-
-static void AXAttributeStringSetColor(NSMutableAttributedString* attrString, NSString* attribute, NSColor* color, NSRange range)
-{
- if (!AXAttributedStringRangeIsValid(attrString, range))
- return;
-
- if (color) {
- CGColorRef existingColor = (CGColorRef) [attrString attribute:attribute atIndex:range.location effectiveRange:nil];
- CGColorRef cgColor = CreateCGColorIfDifferent(color, existingColor);
- if (cgColor) {
- [attrString addAttribute:attribute value:(id)cgColor range:range];
- CGColorRelease(cgColor);
- }
- } else
- [attrString removeAttribute:attribute range:range];
-}
-
-static void AXAttributeStringSetNumber(NSMutableAttributedString* attrString, NSString* attribute, NSNumber* number, NSRange range)
-{
- if (!AXAttributedStringRangeIsValid(attrString, range))
- return;
-
- if (number)
- [attrString addAttribute:attribute value:number range:range];
- else
- [attrString removeAttribute:attribute range:range];
-}
-
-static void AXAttributeStringSetStyle(NSMutableAttributedString* attrString, RenderObject* renderer, NSRange range)
-{
- RenderStyle* style = renderer->style();
-
- // set basic font info
- AXAttributeStringSetFont(attrString, NSAccessibilityFontTextAttribute, style->font().primaryFont()->getNSFont(), range);
-
- // set basic colors
- AXAttributeStringSetColor(attrString, NSAccessibilityForegroundColorTextAttribute, nsColor(style->visitedDependentColor(CSSPropertyColor)), range);
- AXAttributeStringSetColor(attrString, NSAccessibilityBackgroundColorTextAttribute, nsColor(style->visitedDependentColor(CSSPropertyBackgroundColor)), range);
-
- // set super/sub scripting
- EVerticalAlign alignment = style->verticalAlign();
- if (alignment == SUB)
- AXAttributeStringSetNumber(attrString, NSAccessibilitySuperscriptTextAttribute, [NSNumber numberWithInt:(-1)], range);
- else if (alignment == SUPER)
- AXAttributeStringSetNumber(attrString, NSAccessibilitySuperscriptTextAttribute, [NSNumber numberWithInt:1], range);
- else
- [attrString removeAttribute:NSAccessibilitySuperscriptTextAttribute range:range];
-
- // set shadow
- if (style->textShadow())
- AXAttributeStringSetNumber(attrString, NSAccessibilityShadowTextAttribute, [NSNumber numberWithBool:YES], range);
- else
- [attrString removeAttribute:NSAccessibilityShadowTextAttribute range:range];
-
- // set underline and strikethrough
- int decor = style->textDecorationsInEffect();
- if ((decor & TextDecorationUnderline) == 0) {
- [attrString removeAttribute:NSAccessibilityUnderlineTextAttribute range:range];
- [attrString removeAttribute:NSAccessibilityUnderlineColorTextAttribute range:range];
- }
-
- if ((decor & TextDecorationLineThrough) == 0) {
- [attrString removeAttribute:NSAccessibilityStrikethroughTextAttribute range:range];
- [attrString removeAttribute:NSAccessibilityStrikethroughColorTextAttribute range:range];
- }
-
- if ((decor & (TextDecorationUnderline | TextDecorationLineThrough)) != 0) {
- // find colors using quirk mode approach (strict mode would use current
- // color for all but the root line box, which would use getTextDecorationColors)
- Color underline, overline, linethrough;
- renderer->getTextDecorationColors(decor, underline, overline, linethrough);
-
- if ((decor & TextDecorationUnderline) != 0) {
- AXAttributeStringSetNumber(attrString, NSAccessibilityUnderlineTextAttribute, [NSNumber numberWithBool:YES], range);
- AXAttributeStringSetColor(attrString, NSAccessibilityUnderlineColorTextAttribute, nsColor(underline), range);
- }
-
- if ((decor & TextDecorationLineThrough) != 0) {
- AXAttributeStringSetNumber(attrString, NSAccessibilityStrikethroughTextAttribute, [NSNumber numberWithBool:YES], range);
- AXAttributeStringSetColor(attrString, NSAccessibilityStrikethroughColorTextAttribute, nsColor(linethrough), range);
- }
- }
-
- // Indicate background highlighting.
- for (Node* node = renderer->node(); node; node = node->parentNode()) {
- if (node->hasTagName(markTag))
- AXAttributeStringSetNumber(attrString, @"AXHighlight", [NSNumber numberWithBool:YES], range);
- }
-}
-
-static void AXAttributeStringSetBlockquoteLevel(NSMutableAttributedString* attrString, RenderObject* renderer, NSRange range)
-{
- if (!AXAttributedStringRangeIsValid(attrString, range))
- return;
-
- AccessibilityObject* obj = renderer->document()->axObjectCache()->getOrCreate(renderer);
- int quoteLevel = obj->blockquoteLevel();
-
- if (quoteLevel)
- [attrString addAttribute:NSAccessibilityBlockQuoteLevelAttribute value:[NSNumber numberWithInt:quoteLevel] range:range];
- else
- [attrString removeAttribute:NSAccessibilityBlockQuoteLevelAttribute range:range];
-}
-
-static void AXAttributeStringSetSpelling(NSMutableAttributedString* attrString, Node* node, const UChar* chars, int charLength, NSRange range)
-{
- if (unifiedTextCheckerEnabled(node->document()->frame())) {
- // Check the spelling directly since document->markersForNode() does not store the misspelled marking when the cursor is in a word.
- TextCheckerClient* checker = node->document()->frame()->editor().textChecker();
-
- // checkTextOfParagraph is the only spelling/grammar checker implemented in WK1 and WK2
- Vector<TextCheckingResult> results;
- checkTextOfParagraph(checker, chars, charLength, TextCheckingTypeSpelling, results);
-
- size_t size = results.size();
- NSNumber* trueValue = [NSNumber numberWithBool:YES];
- for (unsigned i = 0; i < size; i++) {
- const TextCheckingResult& result = results[i];
- AXAttributeStringSetNumber(attrString, NSAccessibilityMisspelledTextAttribute, trueValue, NSMakeRange(result.location + range.location, result.length));
-#if PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090
- AXAttributeStringSetNumber(attrString, NSAccessibilityMarkedMisspelledTextAttribute, trueValue, NSMakeRange(result.location + range.location, result.length));
-#endif
- }
- return;
- }
-
- int currentPosition = 0;
- while (charLength > 0) {
- const UChar* charData = chars + currentPosition;
- TextCheckerClient* checker = node->document()->frame()->editor().textChecker();
-
- int misspellingLocation = -1;
- int misspellingLength = 0;
- checker->checkSpellingOfString(charData, charLength, &misspellingLocation, &misspellingLength);
- if (misspellingLocation == -1 || !misspellingLength)
- break;
-
- NSRange spellRange = NSMakeRange(range.location + currentPosition + misspellingLocation, misspellingLength);
- AXAttributeStringSetNumber(attrString, NSAccessibilityMisspelledTextAttribute, [NSNumber numberWithBool:YES], spellRange);
-#if PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090
- AXAttributeStringSetNumber(attrString, NSAccessibilityMarkedMisspelledTextAttribute, [NSNumber numberWithBool:YES], spellRange);
-#endif
- charLength -= (misspellingLocation + misspellingLength);
- currentPosition += (misspellingLocation + misspellingLength);
- }
-}
-
-static void AXAttributeStringSetHeadingLevel(NSMutableAttributedString* attrString, RenderObject* renderer, NSRange range)
-{
- if (!renderer)
- return;
-
- if (!AXAttributedStringRangeIsValid(attrString, range))
- return;
-
- // Sometimes there are objects between the text and the heading.
- // In those cases the parent hierarchy should be queried to see if there is a heading level.
- int parentHeadingLevel = 0;
- AccessibilityObject* parentObject = renderer->document()->axObjectCache()->getOrCreate(renderer->parent());
- for (; parentObject; parentObject = parentObject->parentObject()) {
- parentHeadingLevel = parentObject->headingLevel();
- if (parentHeadingLevel)
- break;
- }
-
- if (parentHeadingLevel)
- [attrString addAttribute:@"AXHeadingLevel" value:[NSNumber numberWithInt:parentHeadingLevel] range:range];
- else
- [attrString removeAttribute:@"AXHeadingLevel" range:range];
-}
-
-static void AXAttributeStringSetElement(NSMutableAttributedString* attrString, NSString* attribute, AccessibilityObject* object, NSRange range)
-{
- if (!AXAttributedStringRangeIsValid(attrString, range))
- return;
-
- if (object && object->isAccessibilityRenderObject()) {
- // make a serializable AX object
-
- RenderObject* renderer = static_cast<AccessibilityRenderObject*>(object)->renderer();
- if (!renderer)
- return;
-
- Document* doc = renderer->document();
- if (!doc)
- return;
-
- AXObjectCache* cache = doc->axObjectCache();
- if (!cache)
- return;
-
- AXUIElementRef axElement = wkCreateAXUIElementRef(object->wrapper());
- if (axElement) {
- [attrString addAttribute:attribute value:(id)axElement range:range];
- CFRelease(axElement);
- }
- } else
- [attrString removeAttribute:attribute range:range];
-}
-
-static void AXAttributedStringAppendText(NSMutableAttributedString* attrString, Node* node, const UChar* chars, int length)
-{
- // skip invisible text
- if (!node->renderer())
- return;
-
- // easier to calculate the range before appending the string
- NSRange attrStringRange = NSMakeRange([attrString length], length);
-
- // append the string from this node
- [[attrString mutableString] appendString:[NSString stringWithCharacters:chars length:length]];
-
- // add new attributes and remove irrelevant inherited ones
- // NOTE: color attributes are handled specially because -[NSMutableAttributedString addAttribute: value: range:] does not merge
- // identical colors. Workaround is to not replace an existing color attribute if it matches what we are adding. This also means
- // we cannot just pre-remove all inherited attributes on the appended string, so we have to remove the irrelevant ones individually.
-
- // remove inherited attachment from prior AXAttributedStringAppendReplaced
- [attrString removeAttribute:NSAccessibilityAttachmentTextAttribute range:attrStringRange];
-#if PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090
- [attrString removeAttribute:NSAccessibilityMarkedMisspelledTextAttribute range:attrStringRange];
-#endif
- [attrString removeAttribute:NSAccessibilityMisspelledTextAttribute range:attrStringRange];
-
- // set new attributes
- AXAttributeStringSetStyle(attrString, node->renderer(), attrStringRange);
- AXAttributeStringSetHeadingLevel(attrString, node->renderer(), attrStringRange);
- AXAttributeStringSetBlockquoteLevel(attrString, node->renderer(), attrStringRange);
- AXAttributeStringSetElement(attrString, NSAccessibilityLinkTextAttribute, AccessibilityObject::anchorElementForNode(node), attrStringRange);
-
- // do spelling last because it tends to break up the range
- AXAttributeStringSetSpelling(attrString, node, chars, length, attrStringRange);
-}
-
-static NSString* nsStringForReplacedNode(Node* replacedNode)
-{
- // we should always be given a rendered node and a replaced node, but be safe
- // replaced nodes are either attachments (widgets) or images
- if (!replacedNode || !replacedNode->renderer() || !replacedNode->renderer()->isReplaced() || replacedNode->isTextNode()) {
- ASSERT_NOT_REACHED();
- return nil;
- }
-
- // create an AX object, but skip it if it is not supposed to be seen
- RefPtr<AccessibilityObject> obj = replacedNode->renderer()->document()->axObjectCache()->getOrCreate(replacedNode->renderer());
- if (obj->accessibilityIsIgnored())
- return nil;
-
- // use the attachmentCharacter to represent the replaced node
- const UniChar attachmentChar = NSAttachmentCharacter;
- return [NSString stringWithCharacters:&attachmentChar length:1];
-}
-
-- (NSAttributedString*)doAXAttributedStringForTextMarkerRange:(id)textMarkerRange
-{
- if (!m_object)
- return nil;
-
- // extract the start and end VisiblePosition
- VisiblePosition startVisiblePosition = visiblePositionForStartOfTextMarkerRange(m_object->axObjectCache(), textMarkerRange);
- if (startVisiblePosition.isNull())
- return nil;
-
- VisiblePosition endVisiblePosition = visiblePositionForEndOfTextMarkerRange(m_object->axObjectCache(), textMarkerRange);
- if (endVisiblePosition.isNull())
- return nil;
-
- VisiblePositionRange visiblePositionRange(startVisiblePosition, endVisiblePosition);
- // iterate over the range to build the AX attributed string
- NSMutableAttributedString* attrString = [[NSMutableAttributedString alloc] init];
- TextIterator it(makeRange(startVisiblePosition, endVisiblePosition).get());
- while (!it.atEnd()) {
- // locate the node and starting offset for this range
- int exception = 0;
- Node* node = it.range()->startContainer(exception);
- ASSERT(node == it.range()->endContainer(exception));
- int offset = it.range()->startOffset(exception);
-
- // non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX)
- if (it.length() != 0) {
- // Add the text of the list marker item if necessary.
- String listMarkerText = m_object->listMarkerTextForNodeAndPosition(node, VisiblePosition(it.range()->startPosition()));
- if (!listMarkerText.isEmpty())
- AXAttributedStringAppendText(attrString, node, listMarkerText.characters(), listMarkerText.length());
-
- AXAttributedStringAppendText(attrString, node, it.characters(), it.length());
- } else {
- Node* replacedNode = node->childNode(offset);
- NSString *attachmentString = nsStringForReplacedNode(replacedNode);
- if (attachmentString) {
- NSRange attrStringRange = NSMakeRange([attrString length], [attachmentString length]);
-
- // append the placeholder string
- [[attrString mutableString] appendString:attachmentString];
-
- // remove all inherited attributes
- [attrString setAttributes:nil range:attrStringRange];
-
- // add the attachment attribute
- AccessibilityObject* obj = replacedNode->renderer()->document()->axObjectCache()->getOrCreate(replacedNode->renderer());
- AXAttributeStringSetElement(attrString, NSAccessibilityAttachmentTextAttribute, obj, attrStringRange);
- }
- }
- it.advance();
- }
-
- return [attrString autorelease];
-}
-
-static id textMarkerRangeFromVisiblePositions(AXObjectCache *cache, VisiblePosition startPosition, VisiblePosition endPosition)
-{
- id startTextMarker = textMarkerForVisiblePosition(cache, startPosition);
- id endTextMarker = textMarkerForVisiblePosition(cache, endPosition);
- return textMarkerRangeFromMarkers(startTextMarker, endTextMarker);
-}
-
-- (id)textMarkerRangeFromVisiblePositions:(VisiblePosition)startPosition endPosition:(VisiblePosition)endPosition
-{
- return textMarkerRangeFromVisiblePositions(m_object->axObjectCache(), startPosition, endPosition);
-}
-
-- (NSArray*)accessibilityActionNames
-{
- if (![self updateObjectBackingStore])
- return nil;
-
- // All elements should get ShowMenu and ScrollToVisible.
- // But certain earlier VoiceOver versions do not support scroll to visible, and it confuses them to see it in the list.
-#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1090
- static NSArray *defaultElementActions = [[NSArray alloc] initWithObjects:NSAccessibilityShowMenuAction, nil];
-#else
- static NSArray *defaultElementActions = [[NSArray alloc] initWithObjects:NSAccessibilityShowMenuAction, NSAccessibilityScrollToVisibleAction, nil];
-#endif
-
- // Action elements allow Press.
- // The order is important to VoiceOver, which expects the 'default' action to be the first action. In this case the default action should be press.
-#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1090
- static NSArray *actionElementActions = [[NSArray alloc] initWithObjects:NSAccessibilityPressAction, NSAccessibilityShowMenuAction, nil];
-#else
- static NSArray *actionElementActions = [[NSArray alloc] initWithObjects:NSAccessibilityPressAction, NSAccessibilityShowMenuAction, NSAccessibilityScrollToVisibleAction, nil];
-#endif
-
- // Menu elements allow Press and Cancel.
- static NSArray *menuElementActions = [[actionElementActions arrayByAddingObject:NSAccessibilityCancelAction] retain];
-
- // Slider elements allow Increment/Decrement.
- static NSArray *sliderActions = [[defaultElementActions arrayByAddingObjectsFromArray:[NSArray arrayWithObjects:NSAccessibilityIncrementAction, NSAccessibilityDecrementAction, nil]] retain];
-
- NSArray *actions;
- if (m_object->actionElement() || m_object->isButton())
- actions = actionElementActions;
- else if (m_object->isMenuRelated())
- actions = menuElementActions;
- else if (m_object->isSlider())
- actions = sliderActions;
- else if (m_object->isAttachment())
- actions = [[self attachmentView] accessibilityActionNames];
- else
- actions = defaultElementActions;
-
- return actions;
-}
-
-- (NSArray*)additionalAccessibilityAttributeNames
-{
- if (!m_object)
- return nil;
-
- NSMutableArray *additional = [NSMutableArray array];
- if (m_object->supportsARIAOwns())
- [additional addObject:NSAccessibilityOwnsAttribute];
-
- if (m_object->supportsARIAExpanded())
- [additional addObject:NSAccessibilityExpandedAttribute];
-
- if (m_object->isScrollbar())
- [additional addObject:NSAccessibilityOrientationAttribute];
-
- if (m_object->supportsARIADragging())
- [additional addObject:NSAccessibilityGrabbedAttribute];
-
- if (m_object->supportsARIADropping())
- [additional addObject:NSAccessibilityDropEffectsAttribute];
-
- if (m_object->isAccessibilityTable() && static_cast<AccessibilityTable*>(m_object)->supportsSelectedRows())
- [additional addObject:NSAccessibilitySelectedRowsAttribute];
-
- if (m_object->supportsARIALiveRegion()) {
- [additional addObject:NSAccessibilityARIALiveAttribute];
- [additional addObject:NSAccessibilityARIARelevantAttribute];
- }
-
- if (m_object->supportsARIASetSize())
- [additional addObject:NSAccessibilityARIASetSizeAttribute];
- if (m_object->supportsARIAPosInSet())
- [additional addObject:NSAccessibilityARIAPosInSetAttribute];
-
- if (m_object->sortDirection() != SortDirectionNone)
- [additional addObject:NSAccessibilitySortDirectionAttribute];
-
- // If an object is a child of a live region, then add these
- if (m_object->isInsideARIALiveRegion())
- [additional addObject:NSAccessibilityARIAAtomicAttribute];
- // All objects should expose the ARIA busy attribute (ARIA 1.1 with ISSUE-538).
- [additional addObject:NSAccessibilityARIABusyAttribute];
-
- // Popup buttons on the Mac expose the value attribute.
- if (m_object->isPopUpButton()) {
- [additional addObject:NSAccessibilityValueAttribute];
- }
-
- if (m_object->supportsRequiredAttribute()) {
- [additional addObject:NSAccessibilityRequiredAttribute];
- }
-
- if (m_object->ariaHasPopup())
- [additional addObject:NSAccessibilityHasPopupAttribute];
-
- if (m_object->isMathRoot()) {
- // The index of a square root is always known, so there's no object associated with it.
- if (!m_object->isMathSquareRoot())
- [additional addObject:NSAccessibilityMathRootIndexAttribute];
- [additional addObject:NSAccessibilityMathRootRadicandAttribute];
- } else if (m_object->isMathFraction()) {
- [additional addObject:NSAccessibilityMathFractionNumeratorAttribute];
- [additional addObject:NSAccessibilityMathFractionDenominatorAttribute];
- [additional addObject:NSAccessibilityMathLineThicknessAttribute];
- } else if (m_object->isMathSubscriptSuperscript()) {
- [additional addObject:NSAccessibilityMathBaseAttribute];
- [additional addObject:NSAccessibilityMathSubscriptAttribute];
- [additional addObject:NSAccessibilityMathSuperscriptAttribute];
- } else if (m_object->isMathUnderOver()) {
- [additional addObject:NSAccessibilityMathBaseAttribute];
- [additional addObject:NSAccessibilityMathUnderAttribute];
- [additional addObject:NSAccessibilityMathOverAttribute];
- } else if (m_object->isMathFenced()) {
- [additional addObject:NSAccessibilityMathFencedOpenAttribute];
- [additional addObject:NSAccessibilityMathFencedCloseAttribute];
- } else if (m_object->isMathMultiscript()) {
- [additional addObject:NSAccessibilityMathBaseAttribute];
- [additional addObject:NSAccessibilityMathPrescriptsAttribute];
- [additional addObject:NSAccessibilityMathPostscriptsAttribute];
- }
-
- if (m_object->supportsPath())
- [additional addObject:NSAccessibilityPathAttribute];
-
- return additional;
-}
-
-- (NSArray*)accessibilityAttributeNames
-{
- if (![self updateObjectBackingStore])
- return nil;
-
- if (m_object->isAttachment())
- return [[self attachmentView] accessibilityAttributeNames];
-
- static NSArray* attributes = nil;
- static NSArray* anchorAttrs = nil;
- static NSArray* webAreaAttrs = nil;
- static NSArray* textAttrs = nil;
- static NSArray* listAttrs = nil;
- static NSArray* listBoxAttrs = nil;
- static NSArray* rangeAttrs = nil;
- static NSArray* commonMenuAttrs = nil;
- static NSArray* menuAttrs = nil;
- static NSArray* menuBarAttrs = nil;
- static NSArray* menuItemAttrs = nil;
- static NSArray* menuButtonAttrs = nil;
- static NSArray* controlAttrs = nil;
- static NSArray* tableAttrs = nil;
- static NSArray* tableRowAttrs = nil;
- static NSArray* tableColAttrs = nil;
- static NSArray* tableCellAttrs = nil;
- static NSArray* groupAttrs = nil;
- static NSArray* inputImageAttrs = nil;
- static NSArray* passwordFieldAttrs = nil;
- static NSArray* tabListAttrs = nil;
- static NSArray* comboBoxAttrs = nil;
- static NSArray* outlineAttrs = nil;
- static NSArray* outlineRowAttrs = nil;
- static NSArray* buttonAttrs = nil;
- static NSArray* scrollViewAttrs = nil;
- static NSArray* incrementorAttrs = nil;
- NSMutableArray* tempArray;
- if (attributes == nil) {
- attributes = [[NSArray alloc] initWithObjects: NSAccessibilityRoleAttribute,
- NSAccessibilitySubroleAttribute,
- NSAccessibilityRoleDescriptionAttribute,
- NSAccessibilityChildrenAttribute,
- NSAccessibilityHelpAttribute,
- NSAccessibilityParentAttribute,
- NSAccessibilityPositionAttribute,
- NSAccessibilitySizeAttribute,
- NSAccessibilityTitleAttribute,
- NSAccessibilityDescriptionAttribute,
- NSAccessibilityValueAttribute,
- NSAccessibilityFocusedAttribute,
- NSAccessibilityEnabledAttribute,
- NSAccessibilityWindowAttribute,
- @"AXSelectedTextMarkerRange",
- @"AXStartTextMarker",
- @"AXEndTextMarker",
- @"AXVisited",
- NSAccessibilityLinkedUIElementsAttribute,
- NSAccessibilitySelectedAttribute,
- NSAccessibilityBlockQuoteLevelAttribute,
- NSAccessibilityTopLevelUIElementAttribute,
- nil];
- }
- if (commonMenuAttrs == nil) {
- commonMenuAttrs = [[NSArray alloc] initWithObjects: NSAccessibilityRoleAttribute,
- NSAccessibilityRoleDescriptionAttribute,
- NSAccessibilityChildrenAttribute,
- NSAccessibilityParentAttribute,
- NSAccessibilityEnabledAttribute,
- NSAccessibilityPositionAttribute,
- NSAccessibilitySizeAttribute,
- nil];
- }
- if (anchorAttrs == nil) {
- tempArray = [[NSMutableArray alloc] initWithArray:attributes];
- [tempArray addObject:NSAccessibilityURLAttribute];
- [tempArray addObject:NSAccessibilityAccessKeyAttribute];
- anchorAttrs = [[NSArray alloc] initWithArray:tempArray];
- [tempArray release];
- }
- if (webAreaAttrs == nil) {
- tempArray = [[NSMutableArray alloc] initWithArray:attributes];
- [tempArray addObject:@"AXLinkUIElements"];
- [tempArray addObject:@"AXLoaded"];
- [tempArray addObject:@"AXLayoutCount"];
- [tempArray addObject:NSAccessibilityLoadingProgressAttribute];
- [tempArray addObject:NSAccessibilityURLAttribute];
- webAreaAttrs = [[NSArray alloc] initWithArray:tempArray];
- [tempArray release];
- }
- if (textAttrs == nil) {
- tempArray = [[NSMutableArray alloc] initWithArray:attributes];
- [tempArray addObject:NSAccessibilityNumberOfCharactersAttribute];
- [tempArray addObject:NSAccessibilitySelectedTextAttribute];
- [tempArray addObject:NSAccessibilitySelectedTextRangeAttribute];
- [tempArray addObject:NSAccessibilityVisibleCharacterRangeAttribute];
- [tempArray addObject:NSAccessibilityInsertionPointLineNumberAttribute];
- [tempArray addObject:NSAccessibilityTitleUIElementAttribute];
- [tempArray addObject:NSAccessibilityAccessKeyAttribute];
- [tempArray addObject:NSAccessibilityRequiredAttribute];
- [tempArray addObject:NSAccessibilityInvalidAttribute];
- [tempArray addObject:NSAccessibilityPlaceholderValueAttribute];
- textAttrs = [[NSArray alloc] initWithArray:tempArray];
- [tempArray release];
- }
- if (listAttrs == nil) {
- tempArray = [[NSMutableArray alloc] initWithArray:attributes];
- [tempArray addObject:NSAccessibilitySelectedChildrenAttribute];
- [tempArray addObject:NSAccessibilityVisibleChildrenAttribute];
- [tempArray addObject:NSAccessibilityOrientationAttribute];
- [tempArray addObject:NSAccessibilityTitleUIElementAttribute];
- listAttrs = [[NSArray alloc] initWithArray:tempArray];
- [tempArray release];
- }
- if (listBoxAttrs == nil) {
- tempArray = [[NSMutableArray alloc] initWithArray:listAttrs];
- [tempArray addObject:NSAccessibilityAccessKeyAttribute];
- [tempArray addObject:NSAccessibilityRequiredAttribute];
- [tempArray addObject:NSAccessibilityInvalidAttribute];
- listBoxAttrs = [[NSArray alloc] initWithArray:tempArray];
- [tempArray release];
- }
- if (rangeAttrs == nil) {
- tempArray = [[NSMutableArray alloc] initWithArray:attributes];
- [tempArray addObject:NSAccessibilityMinValueAttribute];
- [tempArray addObject:NSAccessibilityMaxValueAttribute];
- [tempArray addObject:NSAccessibilityOrientationAttribute];
- [tempArray addObject:NSAccessibilityValueDescriptionAttribute];
- [tempArray addObject:NSAccessibilityTitleUIElementAttribute];
- rangeAttrs = [[NSArray alloc] initWithArray:tempArray];
- [tempArray release];
- }
- if (menuBarAttrs == nil) {
- tempArray = [[NSMutableArray alloc] initWithArray:commonMenuAttrs];
- [tempArray addObject:NSAccessibilitySelectedChildrenAttribute];
- [tempArray addObject:NSAccessibilityVisibleChildrenAttribute];
- [tempArray addObject:NSAccessibilityTitleUIElementAttribute];
- menuBarAttrs = [[NSArray alloc] initWithArray:tempArray];
- [tempArray release];
- }
- if (menuAttrs == nil) {
- tempArray = [[NSMutableArray alloc] initWithArray:commonMenuAttrs];
- [tempArray addObject:NSAccessibilitySelectedChildrenAttribute];
- [tempArray addObject:NSAccessibilityVisibleChildrenAttribute];
- [tempArray addObject:NSAccessibilityTitleUIElementAttribute];
- menuAttrs = [[NSArray alloc] initWithArray:tempArray];
- [tempArray release];
- }
- if (menuItemAttrs == nil) {
- tempArray = [[NSMutableArray alloc] initWithArray:commonMenuAttrs];
- [tempArray addObject:NSAccessibilityTitleAttribute];
- [tempArray addObject:NSAccessibilityHelpAttribute];
- [tempArray addObject:NSAccessibilitySelectedAttribute];
- [tempArray addObject:(NSString*)kAXMenuItemCmdCharAttribute];
- [tempArray addObject:(NSString*)kAXMenuItemCmdVirtualKeyAttribute];
- [tempArray addObject:(NSString*)kAXMenuItemCmdGlyphAttribute];
- [tempArray addObject:(NSString*)kAXMenuItemCmdModifiersAttribute];
- [tempArray addObject:(NSString*)kAXMenuItemMarkCharAttribute];
- [tempArray addObject:(NSString*)kAXMenuItemPrimaryUIElementAttribute];
- [tempArray addObject:NSAccessibilityServesAsTitleForUIElementsAttribute];
- menuItemAttrs = [[NSArray alloc] initWithArray:tempArray];
- [tempArray release];
- }
- if (menuButtonAttrs == nil) {
- menuButtonAttrs = [[NSArray alloc] initWithObjects:NSAccessibilityRoleAttribute,
- NSAccessibilityRoleDescriptionAttribute,
- NSAccessibilityParentAttribute,
- NSAccessibilityPositionAttribute,
- NSAccessibilitySizeAttribute,
- NSAccessibilityWindowAttribute,
- NSAccessibilityEnabledAttribute,
- NSAccessibilityFocusedAttribute,
- NSAccessibilityTitleAttribute,
- NSAccessibilityChildrenAttribute, nil];
- }
- if (controlAttrs == nil) {
- tempArray = [[NSMutableArray alloc] initWithArray:attributes];
- [tempArray addObject:NSAccessibilityTitleUIElementAttribute];
- [tempArray addObject:NSAccessibilityAccessKeyAttribute];
- [tempArray addObject:NSAccessibilityRequiredAttribute];
- [tempArray addObject:NSAccessibilityInvalidAttribute];
- controlAttrs = [[NSArray alloc] initWithArray:tempArray];
- [tempArray release];
- }
- if (incrementorAttrs == nil) {
- tempArray = [[NSMutableArray alloc] initWithArray:attributes];
- [tempArray addObject:NSAccessibilityIncrementButtonAttribute];
- [tempArray addObject:NSAccessibilityDecrementButtonAttribute];
- incrementorAttrs = [[NSArray alloc] initWithArray:tempArray];
- [tempArray release];
- }
- if (buttonAttrs == nil) {
- tempArray = [[NSMutableArray alloc] initWithArray:attributes];
- // Buttons should not expose AXValue.
- [tempArray removeObject:NSAccessibilityValueAttribute];
- [tempArray addObject:NSAccessibilityTitleUIElementAttribute];
- [tempArray addObject:NSAccessibilityAccessKeyAttribute];
- buttonAttrs = [[NSArray alloc] initWithArray:tempArray];
- [tempArray release];
- }
- if (comboBoxAttrs == nil) {
- tempArray = [[NSMutableArray alloc] initWithArray:controlAttrs];
- [tempArray addObject:NSAccessibilityExpandedAttribute];
- comboBoxAttrs = [[NSArray alloc] initWithArray:tempArray];
- [tempArray release];
- }
- if (tableAttrs == nil) {
- tempArray = [[NSMutableArray alloc] initWithArray:attributes];
- [tempArray addObject:NSAccessibilityRowsAttribute];
- [tempArray addObject:NSAccessibilityVisibleRowsAttribute];
- [tempArray addObject:NSAccessibilityColumnsAttribute];
- [tempArray addObject:NSAccessibilityVisibleColumnsAttribute];
- [tempArray addObject:NSAccessibilityVisibleCellsAttribute];
- [tempArray addObject:(NSString *)kAXColumnHeaderUIElementsAttribute];
- [tempArray addObject:NSAccessibilityRowHeaderUIElementsAttribute];
- [tempArray addObject:NSAccessibilityHeaderAttribute];
- tableAttrs = [[NSArray alloc] initWithArray:tempArray];
- [tempArray release];
- }
- if (tableRowAttrs == nil) {
- tempArray = [[NSMutableArray alloc] initWithArray:attributes];
- [tempArray addObject:NSAccessibilityIndexAttribute];
- tableRowAttrs = [[NSArray alloc] initWithArray:tempArray];
- [tempArray release];
- }
- if (tableColAttrs == nil) {
- tempArray = [[NSMutableArray alloc] initWithArray:attributes];
- [tempArray addObject:NSAccessibilityIndexAttribute];
- [tempArray addObject:NSAccessibilityHeaderAttribute];
- [tempArray addObject:NSAccessibilityRowsAttribute];
- [tempArray addObject:NSAccessibilityVisibleRowsAttribute];
- tableColAttrs = [[NSArray alloc] initWithArray:tempArray];
- [tempArray release];
- }
- if (tableCellAttrs == nil) {
- tempArray = [[NSMutableArray alloc] initWithArray:attributes];
- [tempArray addObject:NSAccessibilityRowIndexRangeAttribute];
- [tempArray addObject:NSAccessibilityColumnIndexRangeAttribute];
- tableCellAttrs = [[NSArray alloc] initWithArray:tempArray];
- [tempArray release];
- }
- if (groupAttrs == nil) {
- tempArray = [[NSMutableArray alloc] initWithArray:attributes];
- [tempArray addObject:NSAccessibilityTitleUIElementAttribute];
- groupAttrs = [[NSArray alloc] initWithArray:tempArray];
- [tempArray release];
- }
- if (inputImageAttrs == nil) {
- tempArray = [[NSMutableArray alloc] initWithArray:buttonAttrs];
- [tempArray addObject:NSAccessibilityURLAttribute];
- inputImageAttrs = [[NSArray alloc] initWithArray:tempArray];
- [tempArray release];
- }
- if (passwordFieldAttrs == nil) {
- tempArray = [[NSMutableArray alloc] initWithArray:attributes];
- [tempArray addObject:NSAccessibilityTitleUIElementAttribute];
- [tempArray addObject:NSAccessibilityRequiredAttribute];
- [tempArray addObject:NSAccessibilityInvalidAttribute];
- [tempArray addObject:NSAccessibilityPlaceholderValueAttribute];
- passwordFieldAttrs = [[NSArray alloc] initWithArray:tempArray];
- [tempArray release];
- }
- if (tabListAttrs == nil) {
- tempArray = [[NSMutableArray alloc] initWithArray:attributes];
- [tempArray addObject:NSAccessibilityTabsAttribute];
- [tempArray addObject:NSAccessibilityContentsAttribute];
- tabListAttrs = [[NSArray alloc] initWithArray:tempArray];
- [tempArray release];
- }
- if (outlineAttrs == nil) {
- tempArray = [[NSMutableArray alloc] initWithArray:attributes];
- [tempArray addObject:NSAccessibilitySelectedRowsAttribute];
- [tempArray addObject:NSAccessibilityRowsAttribute];
- [tempArray addObject:NSAccessibilityColumnsAttribute];
- outlineAttrs = [[NSArray alloc] initWithArray:tempArray];
- [tempArray release];
- }
- if (outlineRowAttrs == nil) {
- tempArray = [[NSMutableArray alloc] initWithArray:tableRowAttrs];
- [tempArray addObject:NSAccessibilityDisclosingAttribute];
- [tempArray addObject:NSAccessibilityDisclosedByRowAttribute];
- [tempArray addObject:NSAccessibilityDisclosureLevelAttribute];
- [tempArray addObject:NSAccessibilityDisclosedRowsAttribute];
- outlineRowAttrs = [[NSArray alloc] initWithArray:tempArray];
- [tempArray release];
- }
- if (scrollViewAttrs == nil) {
- tempArray = [[NSMutableArray alloc] initWithArray:attributes];
- [tempArray addObject:NSAccessibilityContentsAttribute];
- [tempArray addObject:NSAccessibilityHorizontalScrollBarAttribute];
- [tempArray addObject:NSAccessibilityVerticalScrollBarAttribute];
- scrollViewAttrs = [[NSArray alloc] initWithArray:tempArray];
- [tempArray release];
- }
-
- NSArray *objectAttributes = attributes;
-
- if (m_object->isPasswordField())
- objectAttributes = passwordFieldAttrs;
-
- else if (m_object->isWebArea())
- objectAttributes = webAreaAttrs;
-
- else if (m_object->isTextControl())
- objectAttributes = textAttrs;
-
- else if (m_object->isAnchor() || m_object->isImage() || m_object->isLink())
- objectAttributes = anchorAttrs;
-
- else if (m_object->isAccessibilityTable())
- objectAttributes = tableAttrs;
- else if (m_object->isTableColumn())
- objectAttributes = tableColAttrs;
- else if (m_object->isTableCell())
- objectAttributes = tableCellAttrs;
- else if (m_object->isTableRow()) {
- // An ARIA table row can be collapsed and expanded, so it needs the extra attributes.
- if (m_object->isARIATreeGridRow())
- objectAttributes = outlineRowAttrs;
- else
- objectAttributes = tableRowAttrs;
- }
-
- else if (m_object->isTree())
- objectAttributes = outlineAttrs;
- else if (m_object->isTreeItem())
- objectAttributes = outlineRowAttrs;
-
- else if (m_object->isListBox())
- objectAttributes = listBoxAttrs;
- else if (m_object->isList())
- objectAttributes = listAttrs;
-
- else if (m_object->isComboBox())
- objectAttributes = comboBoxAttrs;
-
- else if (m_object->isProgressIndicator() || m_object->isSlider())
- objectAttributes = rangeAttrs;
-
- // These are processed in order because an input image is a button, and a button is a control.
- else if (m_object->isInputImage())
- objectAttributes = inputImageAttrs;
- else if (m_object->isButton())
- objectAttributes = buttonAttrs;
- else if (m_object->isControl())
- objectAttributes = controlAttrs;
-
- else if (m_object->isGroup() || m_object->isListItem())
- objectAttributes = groupAttrs;
- else if (m_object->isTabList())
- objectAttributes = tabListAttrs;
- else if (m_object->isScrollView())
- objectAttributes = scrollViewAttrs;
- else if (m_object->isSpinButton())
- objectAttributes = incrementorAttrs;
-
- else if (m_object->isMenu())
- objectAttributes = menuAttrs;
- else if (m_object->isMenuBar())
- objectAttributes = menuBarAttrs;
- else if (m_object->isMenuButton())
- objectAttributes = menuButtonAttrs;
- else if (m_object->isMenuItem())
- objectAttributes = menuItemAttrs;
-
- NSArray *additionalAttributes = [self additionalAccessibilityAttributeNames];
- if ([additionalAttributes count])
- objectAttributes = [objectAttributes arrayByAddingObjectsFromArray:additionalAttributes];
-
- return objectAttributes;
-}
-
-- (VisiblePositionRange)visiblePositionRangeForTextMarkerRange:(id)textMarkerRange
-{
- if (!textMarkerRange)
- return VisiblePositionRange();
- AXObjectCache* cache = m_object->axObjectCache();
- return VisiblePositionRange(visiblePositionForStartOfTextMarkerRange(cache, textMarkerRange), visiblePositionForEndOfTextMarkerRange(cache, textMarkerRange));
-}
-
-- (NSArray*)renderWidgetChildren
-{
- Widget* widget = m_object->widget();
- if (!widget)
- return nil;
- return [(widget->platformWidget()) accessibilityAttributeValue: NSAccessibilityChildrenAttribute];
-}
-
-- (id)remoteAccessibilityParentObject
-{
- if (!m_object)
- return nil;
-
- Document* document = m_object->document();
- if (!document)
- return nil;
-
- Frame* frame = document->frame();
- if (!frame)
- return nil;
-
- return frame->loader()->client()->accessibilityRemoteObject();
-}
-
-static void convertToVector(NSArray* array, AccessibilityObject::AccessibilityChildrenVector& vector)
-{
- unsigned length = [array count];
- vector.reserveInitialCapacity(length);
- for (unsigned i = 0; i < length; ++i) {
- AccessibilityObject* obj = [[array objectAtIndex:i] accessibilityObject];
- if (obj)
- vector.append(obj);
- }
-}
-
-static NSMutableArray* convertToNSArray(const AccessibilityObject::AccessibilityChildrenVector& vector)
-{
- unsigned length = vector.size();
- NSMutableArray* array = [NSMutableArray arrayWithCapacity: length];
- for (unsigned i = 0; i < length; ++i) {
- WebAccessibilityObjectWrapper* wrapper = vector[i]->wrapper();
- ASSERT(wrapper);
- if (wrapper) {
- // we want to return the attachment view instead of the object representing the attachment.
- // otherwise, we get palindrome errors in the AX hierarchy
- if (vector[i]->isAttachment() && [wrapper attachmentView])
- [array addObject:[wrapper attachmentView]];
- else
- [array addObject:wrapper];
- }
- }
- return array;
-}
-
-- (id)textMarkerRangeForSelection
-{
- VisibleSelection selection = m_object->selection();
- if (selection.isNone())
- return nil;
- return [self textMarkerRangeFromVisiblePositions:selection.visibleStart() endPosition:selection.visibleEnd()];
-}
-
-- (CGPoint)convertPointToScreenSpace:(FloatPoint &)point
-{
- FrameView* frameView = m_object->documentFrameView();
-
- // WebKit1 code path... platformWidget() exists.
- if (frameView && frameView->platformWidget()) {
-
- NSPoint nsPoint = (NSPoint)point;
- NSView* view = frameView->documentView();
- nsPoint = [[view window] convertBaseToScreen:[view convertPoint:nsPoint toView:nil]];
- return CGPointMake(nsPoint.x, nsPoint.y);
-
- } else {
-
- // Find the appropriate scroll view to use to convert the contents to the window.
- ScrollView* scrollView = 0;
- AccessibilityObject* parent = 0;
- for (parent = m_object->parentObject(); parent; parent = parent->parentObject()) {
- if (parent->isAccessibilityScrollView()) {
- scrollView = toAccessibilityScrollView(parent)->scrollView();
- break;
- }
- }
-
- IntPoint intPoint = flooredIntPoint(point);
- if (scrollView)
- intPoint = scrollView->contentsToRootView(intPoint);
-
- Page* page = m_object->page();
-
- // If we have an empty chrome client (like SVG) then we should use the page
- // of the scroll view parent to help us get to the screen rect.
- if (parent && page && page->chrome().client()->isEmptyChromeClient())
- page = parent->page();
-
- if (page) {
- IntRect rect = IntRect(intPoint, IntSize(0, 0));
- intPoint = page->chrome().rootViewToScreen(rect).location();
- }
-
- return intPoint;
- }
-}
-
-static void WebTransformCGPathToNSBezierPath(void *info, const CGPathElement *element)
-{
- NSBezierPath *bezierPath = (NSBezierPath *)info;
- switch (element->type) {
- case kCGPathElementMoveToPoint:
- [bezierPath moveToPoint:NSPointFromCGPoint(element->points[0])];
- break;
- case kCGPathElementAddLineToPoint:
- [bezierPath lineToPoint:NSPointFromCGPoint(element->points[0])];
- break;
- case kCGPathElementAddCurveToPoint:
- [bezierPath curveToPoint:NSPointFromCGPoint(element->points[0]) controlPoint1:NSPointFromCGPoint(element->points[1]) controlPoint2:NSPointFromCGPoint(element->points[2])];
- break;
- case kCGPathElementCloseSubpath:
- [bezierPath closePath];
- break;
- default:
- break;
- }
-}
-
-- (NSBezierPath *)bezierPathFromPath:(CGPathRef)path
-{
- NSBezierPath *bezierPath = [NSBezierPath bezierPath];
- CGPathApply(path, bezierPath, WebTransformCGPathToNSBezierPath);
- return bezierPath;
-}
-
-- (NSBezierPath *)path
-{
- Path path = m_object->elementPath();
- if (path.isEmpty())
- return NULL;
-
- CGPathRef transformedPath = [self convertPathToScreenSpace:path];
- return [self bezierPathFromPath:transformedPath];
-}
-
-- (NSValue *)position
-{
- IntRect rect = pixelSnappedIntRect(m_object->elementRect());
-
- // The Cocoa accessibility API wants the lower-left corner.
- FloatPoint floatPoint = FloatPoint(rect.x(), rect.maxY());
-
- CGPoint cgPoint = [self convertPointToScreenSpace:floatPoint];
-
- return [NSValue valueWithPoint:NSMakePoint(cgPoint.x, cgPoint.y)];
-}
-
-typedef HashMap<int, NSString*> AccessibilityRoleMap;
-
-static const AccessibilityRoleMap& createAccessibilityRoleMap()
-{
- struct RoleEntry {
- AccessibilityRole value;
- NSString* string;
- };
-
- static const RoleEntry roles[] = {
- { UnknownRole, NSAccessibilityUnknownRole },
- { ButtonRole, NSAccessibilityButtonRole },
- { RadioButtonRole, NSAccessibilityRadioButtonRole },
- { CheckBoxRole, NSAccessibilityCheckBoxRole },
- { SliderRole, NSAccessibilitySliderRole },
- { TabGroupRole, NSAccessibilityTabGroupRole },
- { TextFieldRole, NSAccessibilityTextFieldRole },
- { StaticTextRole, NSAccessibilityStaticTextRole },
- { TextAreaRole, NSAccessibilityTextAreaRole },
- { ScrollAreaRole, NSAccessibilityScrollAreaRole },
- { PopUpButtonRole, NSAccessibilityPopUpButtonRole },
- { MenuButtonRole, NSAccessibilityMenuButtonRole },
- { TableRole, NSAccessibilityTableRole },
- { ApplicationRole, NSAccessibilityApplicationRole },
- { GroupRole, NSAccessibilityGroupRole },
- { RadioGroupRole, NSAccessibilityRadioGroupRole },
- { ListRole, NSAccessibilityListRole },
- { DirectoryRole, NSAccessibilityListRole },
- { ScrollBarRole, NSAccessibilityScrollBarRole },
- { ValueIndicatorRole, NSAccessibilityValueIndicatorRole },
- { ImageRole, NSAccessibilityImageRole },
- { MenuBarRole, NSAccessibilityMenuBarRole },
- { MenuRole, NSAccessibilityMenuRole },
- { MenuItemRole, NSAccessibilityMenuItemRole },
- { ColumnRole, NSAccessibilityColumnRole },
- { RowRole, NSAccessibilityRowRole },
- { ToolbarRole, NSAccessibilityToolbarRole },
- { BusyIndicatorRole, NSAccessibilityBusyIndicatorRole },
- { ProgressIndicatorRole, NSAccessibilityProgressIndicatorRole },
- { WindowRole, NSAccessibilityWindowRole },
- { DrawerRole, NSAccessibilityDrawerRole },
- { SystemWideRole, NSAccessibilitySystemWideRole },
- { OutlineRole, NSAccessibilityOutlineRole },
- { IncrementorRole, NSAccessibilityIncrementorRole },
- { BrowserRole, NSAccessibilityBrowserRole },
- { ComboBoxRole, NSAccessibilityComboBoxRole },
- { SplitGroupRole, NSAccessibilitySplitGroupRole },
- { SplitterRole, NSAccessibilitySplitterRole },
- { ColorWellRole, NSAccessibilityColorWellRole },
- { GrowAreaRole, NSAccessibilityGrowAreaRole },
- { SheetRole, NSAccessibilitySheetRole },
- { HelpTagRole, NSAccessibilityHelpTagRole },
- { MatteRole, NSAccessibilityMatteRole },
- { RulerRole, NSAccessibilityRulerRole },
- { RulerMarkerRole, NSAccessibilityRulerMarkerRole },
- { LinkRole, NSAccessibilityLinkRole },
- { DisclosureTriangleRole, NSAccessibilityDisclosureTriangleRole },
- { GridRole, NSAccessibilityGridRole },
- { WebCoreLinkRole, NSAccessibilityLinkRole },
- { ImageMapLinkRole, NSAccessibilityLinkRole },
- { ImageMapRole, @"AXImageMap" },
- { ListMarkerRole, @"AXListMarker" },
- { WebAreaRole, @"AXWebArea" },
- { SeamlessWebAreaRole, NSAccessibilityGroupRole },
- { HeadingRole, @"AXHeading" },
- { ListBoxRole, NSAccessibilityListRole },
- { ListBoxOptionRole, NSAccessibilityStaticTextRole },
- { CellRole, NSAccessibilityCellRole },
- { TableHeaderContainerRole, NSAccessibilityGroupRole },
- { RowHeaderRole, NSAccessibilityGroupRole },
- { DefinitionRole, NSAccessibilityGroupRole },
- { DescriptionListDetailRole, NSAccessibilityGroupRole },
- { DescriptionListTermRole, NSAccessibilityGroupRole },
- { DescriptionListRole, NSAccessibilityListRole },
- { SliderThumbRole, NSAccessibilityValueIndicatorRole },
- { LandmarkApplicationRole, NSAccessibilityGroupRole },
- { LandmarkBannerRole, NSAccessibilityGroupRole },
- { LandmarkComplementaryRole, NSAccessibilityGroupRole },
- { LandmarkContentInfoRole, NSAccessibilityGroupRole },
- { LandmarkMainRole, NSAccessibilityGroupRole },
- { LandmarkNavigationRole, NSAccessibilityGroupRole },
- { LandmarkSearchRole, NSAccessibilityGroupRole },
- { ApplicationAlertRole, NSAccessibilityGroupRole },
- { ApplicationAlertDialogRole, NSAccessibilityGroupRole },
- { ApplicationDialogRole, NSAccessibilityGroupRole },
- { ApplicationLogRole, NSAccessibilityGroupRole },
- { ApplicationMarqueeRole, NSAccessibilityGroupRole },
- { ApplicationStatusRole, NSAccessibilityGroupRole },
- { ApplicationTimerRole, NSAccessibilityGroupRole },
- { DocumentRole, NSAccessibilityGroupRole },
- { DocumentArticleRole, NSAccessibilityGroupRole },
- { DocumentMathRole, NSAccessibilityGroupRole },
- { DocumentNoteRole, NSAccessibilityGroupRole },
- { DocumentRegionRole, NSAccessibilityGroupRole },
- { UserInterfaceTooltipRole, NSAccessibilityGroupRole },
- { TabRole, NSAccessibilityRadioButtonRole },
- { TabListRole, NSAccessibilityTabGroupRole },
- { TabPanelRole, NSAccessibilityGroupRole },
- { TreeRole, NSAccessibilityOutlineRole },
- { TreeItemRole, NSAccessibilityRowRole },
- { ListItemRole, NSAccessibilityGroupRole },
- { ParagraphRole, NSAccessibilityGroupRole },
- { LabelRole, NSAccessibilityGroupRole },
- { DivRole, NSAccessibilityGroupRole },
- { FormRole, NSAccessibilityGroupRole },
- { SpinButtonRole, NSAccessibilityIncrementorRole },
- { FooterRole, NSAccessibilityGroupRole },
- { ToggleButtonRole, NSAccessibilityButtonRole },
- { CanvasRole, NSAccessibilityImageRole },
- { SVGRootRole, NSAccessibilityGroupRole },
- { LegendRole, NSAccessibilityGroupRole },
- { MathElementRole, NSAccessibilityGroupRole }
- };
- AccessibilityRoleMap& roleMap = *new AccessibilityRoleMap;
-
- const unsigned numRoles = sizeof(roles) / sizeof(roles[0]);
- for (unsigned i = 0; i < numRoles; ++i)
- roleMap.set(roles[i].value, roles[i].string);
- return roleMap;
-}
-
-static NSString* roleValueToNSString(AccessibilityRole value)
-{
- ASSERT(value);
- static const AccessibilityRoleMap& roleMap = createAccessibilityRoleMap();
- return roleMap.get(value);
-}
-
-- (NSString*)role
-{
- if (m_object->isAttachment())
- return [[self attachmentView] accessibilityAttributeValue:NSAccessibilityRoleAttribute];
- AccessibilityRole role = m_object->roleValue();
- if (role == CanvasRole && m_object->canvasHasFallbackContent())
- role = GroupRole;
- NSString* string = roleValueToNSString(role);
- if (string != nil)
- return string;
- return NSAccessibilityUnknownRole;
-}
-
-- (NSString*)subrole
-{
- if (m_object->isPasswordField())
- return NSAccessibilitySecureTextFieldSubrole;
- if (m_object->isSearchField())
- return NSAccessibilitySearchFieldSubrole;
-
- if (m_object->isAttachment()) {
- NSView* attachView = [self attachmentView];
- if ([[attachView accessibilityAttributeNames] containsObject:NSAccessibilitySubroleAttribute]) {
- return [attachView accessibilityAttributeValue:NSAccessibilitySubroleAttribute];
- }
- }
-
- if (m_object->isSpinButtonPart()) {
- if (toAccessibilitySpinButtonPart(m_object)->isIncrementor())
- return NSAccessibilityIncrementArrowSubrole;
-
- return NSAccessibilityDecrementArrowSubrole;
- }
-
- if (m_object->isFileUploadButton())
- return @"AXFileUploadButton";
-
- if (m_object->isTreeItem())
- return NSAccessibilityOutlineRowSubrole;
-
- if (m_object->isList()) {
- AccessibilityList* listObject = toAccessibilityList(m_object);
- if (listObject->isUnorderedList() || listObject->isOrderedList())
- return NSAccessibilityContentListSubrole;
- if (listObject->isDescriptionList()) {
-#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1090
- return NSAccessibilityDefinitionListSubrole;
-#else
- return NSAccessibilityDescriptionListSubrole;
-#endif
- }
- }
-
- // ARIA content subroles.
- switch (m_object->roleValue()) {
- case LandmarkApplicationRole:
- return @"AXLandmarkApplication";
- case LandmarkBannerRole:
- return @"AXLandmarkBanner";
- case LandmarkComplementaryRole:
- return @"AXLandmarkComplementary";
- // Footer roles should appear as content info types.
- case FooterRole:
- case LandmarkContentInfoRole:
- return @"AXLandmarkContentInfo";
- case LandmarkMainRole:
- return @"AXLandmarkMain";
- case LandmarkNavigationRole:
- return @"AXLandmarkNavigation";
- case LandmarkSearchRole:
- return @"AXLandmarkSearch";
- case ApplicationAlertRole:
- return @"AXApplicationAlert";
- case ApplicationAlertDialogRole:
- return @"AXApplicationAlertDialog";
- case ApplicationDialogRole:
- return @"AXApplicationDialog";
- case ApplicationLogRole:
- return @"AXApplicationLog";
- case ApplicationMarqueeRole:
- return @"AXApplicationMarquee";
- case ApplicationStatusRole:
- return @"AXApplicationStatus";
- case ApplicationTimerRole:
- return @"AXApplicationTimer";
- case DocumentRole:
- return @"AXDocument";
- case DocumentArticleRole:
- return @"AXDocumentArticle";
- case DocumentMathRole:
- return @"AXDocumentMath";
- case DocumentNoteRole:
- return @"AXDocumentNote";
- case DocumentRegionRole:
- return @"AXDocumentRegion";
- case UserInterfaceTooltipRole:
- return @"AXUserInterfaceTooltip";
- case TabPanelRole:
- return @"AXTabPanel";
- case DefinitionRole:
- return @"AXDefinition";
- case DescriptionListTermRole:
- return @"AXTerm";
- case DescriptionListDetailRole:
- return @"AXDescription";
- // Default doesn't return anything, so roles defined below can be chosen.
- default:
- break;
- }
-
- if (m_object->roleValue() == MathElementRole) {
- if (m_object->isMathFraction())
- return @"AXMathFraction";
- if (m_object->isMathFenced())
- return @"AXMathFenced";
- if (m_object->isMathSubscriptSuperscript())
- return @"AXMathSubscriptSuperscript";
- if (m_object->isMathRow())
- return @"AXMathRow";
- if (m_object->isMathUnderOver())
- return @"AXMathUnderOver";
- if (m_object->isMathSquareRoot())
- return @"AXMathSquareRoot";
- if (m_object->isMathRoot())
- return @"AXMathRoot";
- if (m_object->isMathText())
- return @"AXMathText";
- if (m_object->isMathNumber())
- return @"AXMathNumber";
- if (m_object->isMathIdentifier())
- return @"AXMathIdentifier";
- if (m_object->isMathTable())
- return @"AXMathTable";
- if (m_object->isMathTableRow())
- return @"AXMathTableRow";
- if (m_object->isMathTableCell())
- return @"AXMathTableCell";
- if (m_object->isMathFenceOperator())
- return @"AXMathFenceOperator";
- if (m_object->isMathSeparatorOperator())
- return @"AXMathSeparatorOperator";
- if (m_object->isMathOperator())
- return @"AXMathOperator";
- if (m_object->isMathMultiscript())
- return @"AXMathMultiscript";
- }
-
- if (m_object->isMediaTimeline())
- return NSAccessibilityTimelineSubrole;
-
- return nil;
-}
-
-- (NSString*)roleDescription
-{
- if (!m_object)
- return nil;
-
- // attachments have the AXImage role, but a different subrole
- if (m_object->isAttachment())
- return [[self attachmentView] accessibilityAttributeValue:NSAccessibilityRoleDescriptionAttribute];
-
- NSString* axRole = [self role];
-
- if ([axRole isEqualToString:NSAccessibilityGroupRole]) {
-
- NSString *ariaLandmarkRoleDescription = [self ariaLandmarkRoleDescription];
- if (ariaLandmarkRoleDescription)
- return ariaLandmarkRoleDescription;
-
- switch (m_object->roleValue()) {
- case DefinitionRole:
- return AXDefinitionText();
- case DescriptionListTermRole:
- return AXDescriptionListTermText();
- case DescriptionListDetailRole:
- return AXDescriptionListDetailText();
- case FooterRole:
- return AXFooterRoleDescriptionText();
- default:
- return NSAccessibilityRoleDescription(NSAccessibilityGroupRole, [self subrole]);
- }
- }
-
- if ([axRole isEqualToString:@"AXWebArea"])
- return AXWebAreaText();
-
- if ([axRole isEqualToString:@"AXLink"])
- return AXLinkText();
-
- if ([axRole isEqualToString:@"AXListMarker"])
- return AXListMarkerText();
-
- if ([axRole isEqualToString:@"AXImageMap"])
- return AXImageMapText();
-
- if ([axRole isEqualToString:@"AXHeading"])
- return AXHeadingText();
-
- if (m_object->isFileUploadButton())
- return AXFileUploadButtonText();
-
- // Only returning for DL (not UL or OL) because description changed with HTML5 from 'definition list' to
- // superset 'description list' and does not return the same values in AX API on some OS versions.
- if (m_object->isList()) {
- AccessibilityList* listObject = toAccessibilityList(m_object);
- if (listObject->isDescriptionList())
- return AXDescriptionListText();
- }
-
- // AppKit also returns AXTab for the role description for a tab item.
- if (m_object->isTabItem())
- return NSAccessibilityRoleDescription(@"AXTab", nil);
-
- // We should try the system default role description for all other roles.
- // If we get the same string back, then as a last resort, return unknown.
- NSString* defaultRoleDescription = NSAccessibilityRoleDescription(axRole, [self subrole]);
-
- // On earlier Mac versions (Lion), using a non-standard subrole would result in a role description
- // being returned that looked like AXRole:AXSubrole. To make all platforms have the same role descriptions
- // we should fallback on a role description ignoring the subrole in these cases.
- if ([defaultRoleDescription isEqualToString:[NSString stringWithFormat:@"%@:%@", axRole, [self subrole]]])
- defaultRoleDescription = NSAccessibilityRoleDescription(axRole, nil);
-
- if (![defaultRoleDescription isEqualToString:axRole])
- return defaultRoleDescription;
-
- return NSAccessibilityRoleDescription(NSAccessibilityUnknownRole, nil);
-}
-
-- (id)scrollViewParent
-{
- if (!m_object || !m_object->isAccessibilityScrollView())
- return nil;
-
- // If this scroll view provides it's parent object (because it's a sub-frame), then
- // we should not find the remoteAccessibilityParent.
- if (m_object->parentObject())
- return nil;
-
- AccessibilityScrollView* scrollView = toAccessibilityScrollView(m_object);
- ScrollView* scroll = scrollView->scrollView();
- if (!scroll)
- return nil;
-
- if (scroll->platformWidget())
- return NSAccessibilityUnignoredAncestor(scroll->platformWidget());
-
- return [self remoteAccessibilityParentObject];
-}
-
-// FIXME: Different kinds of elements are putting the title tag to use in different
-// AX fields. This should be rectified, but in the initial patch I want to achieve
-// parity with existing behavior.
-- (BOOL)titleTagShouldBeUsedInDescriptionField
-{
- return (m_object->isLink() && !m_object->isImageMapLink()) || m_object->isImage();
-}
-
-// This should be the "visible" text that's actually on the screen if possible.
-// If there's alternative text, that can override the title.
-- (NSString *)accessibilityTitle
-{
- // Static text objects should not have a title. Its content is communicated in its AXValue.
- if (m_object->roleValue() == StaticTextRole)
- return [NSString string];
-
- // A file upload button presents a challenge because it has button text and a value, but the
- // API doesn't support this paradigm.
- // The compromise is to return the button type in the role description and the value of the file path in the title
- if (m_object->isFileUploadButton())
- return m_object->stringValue();
-
- Vector<AccessibilityText> textOrder;
- m_object->accessibilityText(textOrder);
-
- unsigned length = textOrder.size();
- for (unsigned k = 0; k < length; k++) {
- const AccessibilityText& text = textOrder[k];
-
- // If we have alternative text, then we should not expose a title.
- if (text.textSource == AlternativeText)
- break;
-
- // Once we encounter visible text, or the text from our children that should be used foremost.
- if (text.textSource == VisibleText || text.textSource == ChildrenText)
- return text.text;
-
- // If there's an element that labels this object and it's not exposed, then we should use
- // that text as our title.
- if (text.textSource == LabelByElementText && !m_object->exposesTitleUIElement())
- return text.text;
-
- // FIXME: The title tag is used in certain cases for the title. This usage should
- // probably be in the description field since it's not "visible".
- if (text.textSource == TitleTagText && ![self titleTagShouldBeUsedInDescriptionField])
- return text.text;
- }
-
- return [NSString string];
-}
-
-- (NSString *)accessibilityDescription
-{
- // Static text objects should not have a description. Its content is communicated in its AXValue.
- // One exception is the media control labels that have a value and a description. Those are set programatically.
- if (m_object->roleValue() == StaticTextRole && !m_object->isMediaControlLabel())
- return [NSString string];
-
- Vector<AccessibilityText> textOrder;
- m_object->accessibilityText(textOrder);
-
- unsigned length = textOrder.size();
- for (unsigned k = 0; k < length; k++) {
- const AccessibilityText& text = textOrder[k];
-
- if (text.textSource == AlternativeText)
- return text.text;
-
- if (text.textSource == TitleTagText && [self titleTagShouldBeUsedInDescriptionField])
- return text.text;
- }
-
- return [NSString string];
-}
-
-- (NSString *)accessibilityHelpText
-{
- Vector<AccessibilityText> textOrder;
- m_object->accessibilityText(textOrder);
-
- unsigned length = textOrder.size();
- bool descriptiveTextAvailable = false;
- for (unsigned k = 0; k < length; k++) {
- const AccessibilityText& text = textOrder[k];
-
- if (text.textSource == HelpText || text.textSource == SummaryText)
- return text.text;
-
- // If an element does NOT have other descriptive text the title tag should be used as its descriptive text.
- // But, if those ARE available, then the title tag should be used for help text instead.
- switch (text.textSource) {
- case AlternativeText:
- case VisibleText:
- case ChildrenText:
- case LabelByElementText:
- descriptiveTextAvailable = true;
- default:
- break;
- }
-
- if (text.textSource == TitleTagText && descriptiveTextAvailable)
- return text.text;
- }
-
- return [NSString string];
-}
-
-// FIXME: split up this function in a better way.
-// suggestions: Use a hash table that maps attribute names to function calls,
-// or maybe pointers to member functions
-- (id)accessibilityAttributeValue:(NSString*)attributeName
-{
- if (![self updateObjectBackingStore])
- return nil;
-
- if ([attributeName isEqualToString: NSAccessibilityRoleAttribute])
- return [self role];
-
- if ([attributeName isEqualToString: NSAccessibilitySubroleAttribute])
- return [self subrole];
-
- if ([attributeName isEqualToString: NSAccessibilityRoleDescriptionAttribute])
- return [self roleDescription];
-
- if ([attributeName isEqualToString: NSAccessibilityParentAttribute]) {
-
- // This will return the parent of the AXWebArea, if this is a web area.
- id scrollViewParent = [self scrollViewParent];
- if (scrollViewParent)
- return scrollViewParent;
-
- // Tree item (changed to AXRows) can only report the tree (AXOutline) as its parent.
- if (m_object->isTreeItem()) {
- AccessibilityObject* parent = m_object->parentObjectUnignored();
- while (parent) {
- if (parent->isTree())
- return parent->wrapper();
- parent = parent->parentObjectUnignored();
- }
- }
-
- AccessibilityObject* parent = m_object->parentObjectUnignored();
- if (!parent)
- return nil;
-
- // In WebKit1, the scroll view is provided by the system (the attachment view), so the parent
- // should be reported directly as such.
- if (m_object->isWebArea() && parent->isAttachment())
- return [parent->wrapper() attachmentView];
-
- return parent->wrapper();
- }
-
- if ([attributeName isEqualToString: NSAccessibilityChildrenAttribute]) {
- if (m_object->children().isEmpty()) {
- NSArray* children = [self renderWidgetChildren];
- if (children != nil)
- return children;
- }
-
- // The tree's (AXOutline) children are supposed to be its rows and columns.
- // The ARIA spec doesn't have columns, so we just need rows.
- if (m_object->isTree())
- return [self accessibilityAttributeValue:NSAccessibilityRowsAttribute];
-
- // A tree item should only expose its content as its children (not its rows)
- if (m_object->isTreeItem()) {
- AccessibilityObject::AccessibilityChildrenVector contentCopy;
- m_object->ariaTreeItemContent(contentCopy);
- return convertToNSArray(contentCopy);
- }
-
- return convertToNSArray(m_object->children());
- }
-
- if ([attributeName isEqualToString: NSAccessibilitySelectedChildrenAttribute]) {
- if (m_object->isListBox()) {
- AccessibilityObject::AccessibilityChildrenVector selectedChildrenCopy;
- m_object->selectedChildren(selectedChildrenCopy);
- return convertToNSArray(selectedChildrenCopy);
- }
- return nil;
- }
-
- if ([attributeName isEqualToString: NSAccessibilityVisibleChildrenAttribute]) {
- if (m_object->isListBox()) {
- AccessibilityObject::AccessibilityChildrenVector visibleChildrenCopy;
- m_object->visibleChildren(visibleChildrenCopy);
- return convertToNSArray(visibleChildrenCopy);
- }
- else if (m_object->isList())
- return [self accessibilityAttributeValue:NSAccessibilityChildrenAttribute];
-
- return nil;
- }
-
-
- if (m_object->isWebArea()) {
- if ([attributeName isEqualToString:@"AXLinkUIElements"]) {
- AccessibilityObject::AccessibilityChildrenVector links;
- static_cast<AccessibilityRenderObject*>(m_object)->getDocumentLinks(links);
- return convertToNSArray(links);
- }
- if ([attributeName isEqualToString:@"AXLoaded"])
- return [NSNumber numberWithBool:m_object->isLoaded()];
- if ([attributeName isEqualToString:@"AXLayoutCount"])
- return [NSNumber numberWithInt:m_object->layoutCount()];
- if ([attributeName isEqualToString:NSAccessibilityLoadingProgressAttribute])
- return [NSNumber numberWithDouble:m_object->estimatedLoadingProgress()];
- }
-
- if (m_object->isTextControl()) {
- if ([attributeName isEqualToString: NSAccessibilityNumberOfCharactersAttribute]) {
- int length = m_object->textLength();
- if (length < 0)
- return nil;
- return [NSNumber numberWithUnsignedInt:length];
- }
- if ([attributeName isEqualToString: NSAccessibilitySelectedTextAttribute]) {
- String selectedText = m_object->selectedText();
- if (selectedText.isNull())
- return nil;
- return (NSString*)selectedText;
- }
- if ([attributeName isEqualToString: NSAccessibilitySelectedTextRangeAttribute]) {
- PlainTextRange textRange = m_object->selectedTextRange();
- if (textRange.isNull())
- return [NSValue valueWithRange:NSMakeRange(0, 0)];
- return [NSValue valueWithRange:NSMakeRange(textRange.start, textRange.length)];
- }
- // TODO: Get actual visible range. <rdar://problem/4712101>
- if ([attributeName isEqualToString: NSAccessibilityVisibleCharacterRangeAttribute])
- return m_object->isPasswordField() ? nil : [NSValue valueWithRange: NSMakeRange(0, m_object->textLength())];
- if ([attributeName isEqualToString: NSAccessibilityInsertionPointLineNumberAttribute]) {
- // if selectionEnd > 0, then there is selected text and this question should not be answered
- if (m_object->isPasswordField() || m_object->selectionEnd() > 0)
- return nil;
-
- AccessibilityObject* focusedObject = m_object->focusedUIElement();
- if (focusedObject != m_object)
- return nil;
-
- VisiblePosition focusedPosition = focusedObject->visiblePositionForIndex(focusedObject->selectionStart(), true);
- int lineNumber = m_object->lineForPosition(focusedPosition);
- if (lineNumber < 0)
- return nil;
-
- return [NSNumber numberWithInt:lineNumber];
- }
- }
-
- if ([attributeName isEqualToString: NSAccessibilityURLAttribute]) {
- KURL url = m_object->url();
- if (url.isNull())
- return nil;
- return (NSURL*)url;
- }
-
- // Only native spin buttons have increment and decrement buttons.
- if (m_object->isNativeSpinButton()) {
- if ([attributeName isEqualToString:NSAccessibilityIncrementButtonAttribute])
- return toAccessibilitySpinButton(m_object)->incrementButton()->wrapper();
- if ([attributeName isEqualToString:NSAccessibilityDecrementButtonAttribute])
- return toAccessibilitySpinButton(m_object)->decrementButton()->wrapper();
- }
-
- if ([attributeName isEqualToString: @"AXVisited"])
- return [NSNumber numberWithBool: m_object->isVisited()];
-
- if ([attributeName isEqualToString: NSAccessibilityTitleAttribute]) {
- if (m_object->isAttachment()) {
- if ([[[self attachmentView] accessibilityAttributeNames] containsObject:NSAccessibilityTitleAttribute])
- return [[self attachmentView] accessibilityAttributeValue:NSAccessibilityTitleAttribute];
- }
-
- return [self accessibilityTitle];
- }
-
- if ([attributeName isEqualToString: NSAccessibilityDescriptionAttribute]) {
- if (m_object->isAttachment()) {
- if ([[[self attachmentView] accessibilityAttributeNames] containsObject:NSAccessibilityDescriptionAttribute])
- return [[self attachmentView] accessibilityAttributeValue:NSAccessibilityDescriptionAttribute];
- }
- return [self accessibilityDescription];
- }
-
- if ([attributeName isEqualToString: NSAccessibilityValueAttribute]) {
- if (m_object->isAttachment()) {
- if ([[[self attachmentView] accessibilityAttributeNames] containsObject:NSAccessibilityValueAttribute])
- return [[self attachmentView] accessibilityAttributeValue:NSAccessibilityValueAttribute];
- }
- if (m_object->supportsRangeValue())
- return [NSNumber numberWithFloat:m_object->valueForRange()];
- if (m_object->roleValue() == SliderThumbRole)
- return [NSNumber numberWithFloat:m_object->parentObject()->valueForRange()];
- if (m_object->isHeading())
- return [NSNumber numberWithInt:m_object->headingLevel()];
-
- if (m_object->isCheckboxOrRadio()) {
- switch (m_object->checkboxOrRadioValue()) {
- case ButtonStateOff:
- return [NSNumber numberWithInt:0];
- case ButtonStateOn:
- return [NSNumber numberWithInt:1];
- case ButtonStateMixed:
- return [NSNumber numberWithInt:2];
- }
- }
-
- // radio groups return the selected radio button as the AXValue
- if (m_object->isRadioGroup()) {
- AccessibilityObject* radioButton = m_object->selectedRadioButton();
- if (!radioButton)
- return nil;
- return radioButton->wrapper();
- }
-
- if (m_object->isTabList()) {
- AccessibilityObject* tabItem = m_object->selectedTabItem();
- if (!tabItem)
- return nil;
- return tabItem->wrapper();
- }
-
- if (m_object->isTabItem())
- return [NSNumber numberWithInt:m_object->isSelected()];
-
- if (m_object->isColorWell()) {
- int r, g, b;
- m_object->colorValue(r, g, b);
- return [NSString stringWithFormat:@"rgb %7.5f %7.5f %7.5f 1", r / 255., g / 255., b / 255.];
- }
-
- return m_object->stringValue();
- }
-
- if ([attributeName isEqualToString:(NSString *)kAXMenuItemMarkCharAttribute]) {
- const unichar ch = 0x2713; // ✓ used on Mac for selected menu items.
- return (m_object->isChecked()) ? [NSString stringWithCharacters:&ch length:1] : nil;
- }
-
- if ([attributeName isEqualToString: NSAccessibilityMinValueAttribute])
- return [NSNumber numberWithFloat:m_object->minValueForRange()];
-
- if ([attributeName isEqualToString: NSAccessibilityMaxValueAttribute])
- return [NSNumber numberWithFloat:m_object->maxValueForRange()];
-
- if ([attributeName isEqualToString: NSAccessibilityHelpAttribute])
- return [self accessibilityHelpText];
-
- if ([attributeName isEqualToString: NSAccessibilityFocusedAttribute])
- return [NSNumber numberWithBool: m_object->isFocused()];
-
- if ([attributeName isEqualToString: NSAccessibilityEnabledAttribute])
- return [NSNumber numberWithBool: m_object->isEnabled()];
-
- if ([attributeName isEqualToString: NSAccessibilitySizeAttribute]) {
- IntSize s = m_object->pixelSnappedSize();
- return [NSValue valueWithSize: NSMakeSize(s.width(), s.height())];
- }
-
- if ([attributeName isEqualToString: NSAccessibilityPositionAttribute])
- return [self position];
- if ([attributeName isEqualToString:NSAccessibilityPathAttribute])
- return [self path];
-
- if ([attributeName isEqualToString: NSAccessibilityWindowAttribute] ||
- [attributeName isEqualToString: NSAccessibilityTopLevelUIElementAttribute]) {
-
- id remoteParent = [self remoteAccessibilityParentObject];
- if (remoteParent)
- return [remoteParent accessibilityAttributeValue:attributeName];
-
- FrameView* fv = m_object->documentFrameView();
- if (fv)
- return [fv->platformWidget() window];
- return nil;
- }
-
- if ([attributeName isEqualToString:NSAccessibilityAccessKeyAttribute]) {
- AtomicString accessKey = m_object->accessKey();
- if (accessKey.isNull())
- return nil;
- return accessKey;
- }
-
- if ([attributeName isEqualToString:NSAccessibilityTabsAttribute]) {
- if (m_object->isTabList()) {
- AccessibilityObject::AccessibilityChildrenVector tabsChildren;
- m_object->tabChildren(tabsChildren);
- return convertToNSArray(tabsChildren);
- }
- }
-
- if ([attributeName isEqualToString:NSAccessibilityContentsAttribute]) {
- // The contents of a tab list are all the children except the tabs.
- if (m_object->isTabList()) {
- AccessibilityObject::AccessibilityChildrenVector children = m_object->children();
- AccessibilityObject::AccessibilityChildrenVector tabsChildren;
- m_object->tabChildren(tabsChildren);
-
- AccessibilityObject::AccessibilityChildrenVector contents;
- unsigned childrenSize = children.size();
- for (unsigned k = 0; k < childrenSize; ++k) {
- if (tabsChildren.find(children[k]) == WTF::notFound)
- contents.append(children[k]);
- }
- return convertToNSArray(contents);
- } else if (m_object->isScrollView()) {
- AccessibilityObject::AccessibilityChildrenVector children = m_object->children();
-
- // A scrollView's contents are everything except the scroll bars.
- AccessibilityObject::AccessibilityChildrenVector contents;
- unsigned childrenSize = children.size();
- for (unsigned k = 0; k < childrenSize; ++k) {
- if (!children[k]->isScrollbar())
- contents.append(children[k]);
- }
- return convertToNSArray(contents);
- }
- }
-
- if (m_object->isAccessibilityTable()) {
- // TODO: distinguish between visible and non-visible rows
- if ([attributeName isEqualToString:NSAccessibilityRowsAttribute] ||
- [attributeName isEqualToString:NSAccessibilityVisibleRowsAttribute]) {
- return convertToNSArray(static_cast<AccessibilityTable*>(m_object)->rows());
- }
- // TODO: distinguish between visible and non-visible columns
- if ([attributeName isEqualToString:NSAccessibilityColumnsAttribute] ||
- [attributeName isEqualToString:NSAccessibilityVisibleColumnsAttribute]) {
- return convertToNSArray(static_cast<AccessibilityTable*>(m_object)->columns());
- }
-
- if ([attributeName isEqualToString:NSAccessibilitySelectedRowsAttribute]) {
- AccessibilityObject::AccessibilityChildrenVector selectedChildrenCopy;
- m_object->selectedChildren(selectedChildrenCopy);
- return convertToNSArray(selectedChildrenCopy);
- }
-
- // HTML tables don't support these
- if ([attributeName isEqualToString:NSAccessibilitySelectedColumnsAttribute] ||
- [attributeName isEqualToString:NSAccessibilitySelectedCellsAttribute])
- return nil;
-
- if ([attributeName isEqualToString:(NSString *)kAXColumnHeaderUIElementsAttribute]) {
- AccessibilityObject::AccessibilityChildrenVector columnHeaders;
- static_cast<AccessibilityTable*>(m_object)->columnHeaders(columnHeaders);
- return convertToNSArray(columnHeaders);
- }
-
- if ([attributeName isEqualToString:NSAccessibilityHeaderAttribute]) {
- AccessibilityObject* headerContainer = static_cast<AccessibilityTable*>(m_object)->headerContainer();
- if (headerContainer)
- return headerContainer->wrapper();
- return nil;
- }
-
- if ([attributeName isEqualToString:NSAccessibilityRowHeaderUIElementsAttribute]) {
- AccessibilityObject::AccessibilityChildrenVector rowHeaders;
- static_cast<AccessibilityTable*>(m_object)->rowHeaders(rowHeaders);
- return convertToNSArray(rowHeaders);
- }
-
- if ([attributeName isEqualToString:NSAccessibilityVisibleCellsAttribute]) {
- AccessibilityObject::AccessibilityChildrenVector cells;
- static_cast<AccessibilityTable*>(m_object)->cells(cells);
- return convertToNSArray(cells);
- }
- }
-
- if (m_object->isTableColumn()) {
- if ([attributeName isEqualToString:NSAccessibilityIndexAttribute])
- return [NSNumber numberWithInt:static_cast<AccessibilityTableColumn*>(m_object)->columnIndex()];
-
- // rows attribute for a column is the list of all the elements in that column at each row
- if ([attributeName isEqualToString:NSAccessibilityRowsAttribute] ||
- [attributeName isEqualToString:NSAccessibilityVisibleRowsAttribute]) {
- return convertToNSArray(static_cast<AccessibilityTableColumn*>(m_object)->children());
- }
- if ([attributeName isEqualToString:NSAccessibilityHeaderAttribute]) {
- AccessibilityObject* header = static_cast<AccessibilityTableColumn*>(m_object)->headerObject();
- if (!header)
- return nil;
- return header->wrapper();
- }
- }
-
- if (m_object->isTableCell()) {
- if ([attributeName isEqualToString:NSAccessibilityRowIndexRangeAttribute]) {
- pair<unsigned, unsigned> rowRange;
- static_cast<AccessibilityTableCell*>(m_object)->rowIndexRange(rowRange);
- return [NSValue valueWithRange:NSMakeRange(rowRange.first, rowRange.second)];
- }
- if ([attributeName isEqualToString:NSAccessibilityColumnIndexRangeAttribute]) {
- pair<unsigned, unsigned> columnRange;
- static_cast<AccessibilityTableCell*>(m_object)->columnIndexRange(columnRange);
- return [NSValue valueWithRange:NSMakeRange(columnRange.first, columnRange.second)];
- }
- }
-
- if (m_object->isTree()) {
- if ([attributeName isEqualToString:NSAccessibilitySelectedRowsAttribute]) {
- AccessibilityObject::AccessibilityChildrenVector selectedChildrenCopy;
- m_object->selectedChildren(selectedChildrenCopy);
- return convertToNSArray(selectedChildrenCopy);
- }
- if ([attributeName isEqualToString:NSAccessibilityRowsAttribute]) {
- AccessibilityObject::AccessibilityChildrenVector rowsCopy;
- m_object->ariaTreeRows(rowsCopy);
- return convertToNSArray(rowsCopy);
- }
-
- // TreeRoles do not support columns, but Mac AX expects to be able to ask about columns at the least.
- if ([attributeName isEqualToString:NSAccessibilityColumnsAttribute])
- return [NSArray array];
- }
-
- if ([attributeName isEqualToString:NSAccessibilityIndexAttribute]) {
- if (m_object->isTreeItem()) {
- AccessibilityObject* parent = m_object->parentObject();
- for (; parent && !parent->isTree(); parent = parent->parentObject())
- { }
-
- if (!parent)
- return nil;
-
- // Find the index of this item by iterating the parents.
- AccessibilityObject::AccessibilityChildrenVector rowsCopy;
- parent->ariaTreeRows(rowsCopy);
- size_t count = rowsCopy.size();
- for (size_t k = 0; k < count; ++k)
- if (rowsCopy[k]->wrapper() == self)
- return [NSNumber numberWithUnsignedInt:k];
-
- return nil;
- }
- if (m_object->isTableRow()) {
- if ([attributeName isEqualToString:NSAccessibilityIndexAttribute])
- return [NSNumber numberWithInt:static_cast<AccessibilityTableRow*>(m_object)->rowIndex()];
- }
- }
-
- // The rows that are considered inside this row.
- if ([attributeName isEqualToString:NSAccessibilityDisclosedRowsAttribute]) {
- if (m_object->isTreeItem()) {
- AccessibilityObject::AccessibilityChildrenVector rowsCopy;
- m_object->ariaTreeItemDisclosedRows(rowsCopy);
- return convertToNSArray(rowsCopy);
- } else if (m_object->isARIATreeGridRow()) {
- AccessibilityObject::AccessibilityChildrenVector rowsCopy;
- static_cast<AccessibilityARIAGridRow*>(m_object)->disclosedRows(rowsCopy);
- return convertToNSArray(rowsCopy);
- }
- }
-
- // The row that contains this row. It should be the same as the first parent that is a treeitem.
- if ([attributeName isEqualToString:NSAccessibilityDisclosedByRowAttribute]) {
- if (m_object->isTreeItem()) {
- AccessibilityObject* parent = m_object->parentObject();
- while (parent) {
- if (parent->isTreeItem())
- return parent->wrapper();
- // If the parent is the tree itself, then this value == nil.
- if (parent->isTree())
- return nil;
- parent = parent->parentObject();
- }
- return nil;
- } else if (m_object->isARIATreeGridRow()) {
- AccessibilityObject* row = static_cast<AccessibilityARIAGridRow*>(m_object)->disclosedByRow();
- if (!row)
- return nil;
- return row->wrapper();
- }
- }
-
- if ([attributeName isEqualToString:NSAccessibilityDisclosureLevelAttribute]) {
- // Convert from 1-based level (from aria-level spec) to 0-based level (Mac)
- int level = m_object->hierarchicalLevel();
- if (level > 0)
- level -= 1;
- return [NSNumber numberWithInt:level];
- }
- if ([attributeName isEqualToString:NSAccessibilityDisclosingAttribute])
- return [NSNumber numberWithBool:m_object->isExpanded()];
-
- if ((m_object->isListBox() || m_object->isList()) && [attributeName isEqualToString:NSAccessibilityOrientationAttribute])
- return NSAccessibilityVerticalOrientationValue;
-
- if ([attributeName isEqualToString: @"AXSelectedTextMarkerRange"])
- return [self textMarkerRangeForSelection];
-
- if (m_object->renderer()) {
- if ([attributeName isEqualToString: @"AXStartTextMarker"])
- return [self textMarkerForVisiblePosition:startOfDocument(m_object->renderer()->document())];
- if ([attributeName isEqualToString: @"AXEndTextMarker"])
- return [self textMarkerForVisiblePosition:endOfDocument(m_object->renderer()->document())];
- }
-
- if ([attributeName isEqualToString:NSAccessibilityBlockQuoteLevelAttribute])
- return [NSNumber numberWithInt:m_object->blockquoteLevel()];
- if ([attributeName isEqualToString:@"AXTableLevel"])
- return [NSNumber numberWithInt:m_object->tableLevel()];
-
- if ([attributeName isEqualToString: NSAccessibilityLinkedUIElementsAttribute]) {
- AccessibilityObject::AccessibilityChildrenVector linkedUIElements;
- m_object->linkedUIElements(linkedUIElements);
- if (linkedUIElements.size() == 0)
- return nil;
- return convertToNSArray(linkedUIElements);
- }
-
- if ([attributeName isEqualToString: NSAccessibilitySelectedAttribute])
- return [NSNumber numberWithBool:m_object->isSelected()];
-
- if ([attributeName isEqualToString: NSAccessibilityServesAsTitleForUIElementsAttribute] && m_object->isMenuButton()) {
- AccessibilityObject* uiElement = static_cast<AccessibilityRenderObject*>(m_object)->menuForMenuButton();
- if (uiElement)
- return [NSArray arrayWithObject:uiElement->wrapper()];
- }
-
- if ([attributeName isEqualToString:NSAccessibilityTitleUIElementAttribute]) {
- if (!m_object->exposesTitleUIElement())
- return nil;
-
- AccessibilityObject* obj = m_object->titleUIElement();
- if (obj)
- return obj->wrapper();
- return nil;
- }
-
- if ([attributeName isEqualToString:NSAccessibilityValueDescriptionAttribute])
- return m_object->valueDescription();
-
- if ([attributeName isEqualToString:NSAccessibilityOrientationAttribute]) {
- AccessibilityOrientation elementOrientation = m_object->orientation();
- if (elementOrientation == AccessibilityOrientationVertical)
- return NSAccessibilityVerticalOrientationValue;
- if (elementOrientation == AccessibilityOrientationHorizontal)
- return NSAccessibilityHorizontalOrientationValue;
- return nil;
- }
-
- if ([attributeName isEqualToString:NSAccessibilityHorizontalScrollBarAttribute]) {
- AccessibilityObject* scrollBar = m_object->scrollBar(AccessibilityOrientationHorizontal);
- if (scrollBar)
- return scrollBar->wrapper();
- return nil;
- }
- if ([attributeName isEqualToString:NSAccessibilityVerticalScrollBarAttribute]) {
- AccessibilityObject* scrollBar = m_object->scrollBar(AccessibilityOrientationVertical);
- if (scrollBar)
- return scrollBar->wrapper();
- return nil;
- }
-
- if ([attributeName isEqualToString:NSAccessibilitySortDirectionAttribute]) {
- switch (m_object->sortDirection()) {
- case SortDirectionAscending:
- return NSAccessibilityAscendingSortDirectionValue;
- case SortDirectionDescending:
- return NSAccessibilityDescendingSortDirectionValue;
- default:
- return NSAccessibilityUnknownSortDirectionValue;
- }
- }
-
- if ([attributeName isEqualToString:NSAccessibilityLanguageAttribute])
- return m_object->language();
-
- if ([attributeName isEqualToString:NSAccessibilityExpandedAttribute])
- return [NSNumber numberWithBool:m_object->isExpanded()];
-
- if ([attributeName isEqualToString:NSAccessibilityRequiredAttribute])
- return [NSNumber numberWithBool:m_object->isRequired()];
-
- if ([attributeName isEqualToString:NSAccessibilityInvalidAttribute])
- return m_object->invalidStatus();
-
- if ([attributeName isEqualToString:NSAccessibilityOwnsAttribute]) {
- AccessibilityObject::AccessibilityChildrenVector ariaOwns;
- m_object->ariaOwnsElements(ariaOwns);
- return convertToNSArray(ariaOwns);
- }
-
- if ([attributeName isEqualToString:NSAccessibilityARIAPosInSetAttribute])
- return [NSNumber numberWithInt:m_object->ariaPosInSet()];
- if ([attributeName isEqualToString:NSAccessibilityARIASetSizeAttribute])
- return [NSNumber numberWithInt:m_object->ariaSetSize()];
-
- if ([attributeName isEqualToString:NSAccessibilityGrabbedAttribute])
- return [NSNumber numberWithBool:m_object->isARIAGrabbed()];
-
- if ([attributeName isEqualToString:NSAccessibilityDropEffectsAttribute]) {
- Vector<String> dropEffects;
- m_object->determineARIADropEffects(dropEffects);
- size_t length = dropEffects.size();
-
- NSMutableArray* dropEffectsArray = [NSMutableArray arrayWithCapacity:length];
- for (size_t i = 0; i < length; ++i)
- [dropEffectsArray addObject:dropEffects[i]];
- return dropEffectsArray;
- }
-
- if ([attributeName isEqualToString:NSAccessibilityPlaceholderValueAttribute])
- return m_object->placeholderValue();
-
- if ([attributeName isEqualToString:NSAccessibilityHasPopupAttribute])
- return [NSNumber numberWithBool:m_object->ariaHasPopup()];
-
- // ARIA Live region attributes.
- if ([attributeName isEqualToString:NSAccessibilityARIALiveAttribute])
- return m_object->ariaLiveRegionStatus();
- if ([attributeName isEqualToString:NSAccessibilityARIARelevantAttribute])
- return m_object->ariaLiveRegionRelevant();
- if ([attributeName isEqualToString:NSAccessibilityARIAAtomicAttribute])
- return [NSNumber numberWithBool:m_object->ariaLiveRegionAtomic()];
- if ([attributeName isEqualToString:NSAccessibilityARIABusyAttribute])
- return [NSNumber numberWithBool:m_object->ariaLiveRegionBusy()];
-
- // MathML Attributes.
- if (m_object->isMathElement()) {
- if ([attributeName isEqualToString:NSAccessibilityMathRootIndexAttribute])
- return (m_object->mathRootIndexObject()) ? m_object->mathRootIndexObject()->wrapper() : 0;
- if ([attributeName isEqualToString:NSAccessibilityMathRootRadicandAttribute])
- return (m_object->mathRadicandObject()) ? m_object->mathRadicandObject()->wrapper() : 0;
- if ([attributeName isEqualToString:NSAccessibilityMathFractionNumeratorAttribute])
- return (m_object->mathNumeratorObject()) ? m_object->mathNumeratorObject()->wrapper() : 0;
- if ([attributeName isEqualToString:NSAccessibilityMathFractionDenominatorAttribute])
- return (m_object->mathDenominatorObject()) ? m_object->mathDenominatorObject()->wrapper() : 0;
- if ([attributeName isEqualToString:NSAccessibilityMathBaseAttribute])
- return (m_object->mathBaseObject()) ? m_object->mathBaseObject()->wrapper() : 0;
- if ([attributeName isEqualToString:NSAccessibilityMathSubscriptAttribute])
- return (m_object->mathSubscriptObject()) ? m_object->mathSubscriptObject()->wrapper() : 0;
- if ([attributeName isEqualToString:NSAccessibilityMathSuperscriptAttribute])
- return (m_object->mathSuperscriptObject()) ? m_object->mathSuperscriptObject()->wrapper() : 0;
- if ([attributeName isEqualToString:NSAccessibilityMathUnderAttribute])
- return (m_object->mathUnderObject()) ? m_object->mathUnderObject()->wrapper() : 0;
- if ([attributeName isEqualToString:NSAccessibilityMathOverAttribute])
- return (m_object->mathOverObject()) ? m_object->mathOverObject()->wrapper() : 0;
- if ([attributeName isEqualToString:NSAccessibilityMathFencedOpenAttribute])
- return m_object->mathFencedOpenString();
- if ([attributeName isEqualToString:NSAccessibilityMathFencedCloseAttribute])
- return m_object->mathFencedCloseString();
- if ([attributeName isEqualToString:NSAccessibilityMathLineThicknessAttribute])
- return [NSNumber numberWithInteger:m_object->mathLineThickness()];
- if ([attributeName isEqualToString:NSAccessibilityMathPostscriptsAttribute])
- return [self accessibilityMathPostscriptPairs];
- if ([attributeName isEqualToString:NSAccessibilityMathPrescriptsAttribute])
- return [self accessibilityMathPrescriptPairs];
- }
-
- // this is used only by DumpRenderTree for testing
- if ([attributeName isEqualToString:@"AXClickPoint"])
- return [NSValue valueWithPoint:m_object->clickPoint()];
-
- // This is used by DRT to verify CSS3 speech works.
- if ([attributeName isEqualToString:@"AXDRTSpeechAttribute"]) {
- ESpeak speakProperty = m_object->speakProperty();
- switch (speakProperty) {
- case SpeakNone:
- return @"none";
- case SpeakSpellOut:
- return @"spell-out";
- case SpeakDigits:
- return @"digits";
- case SpeakLiteralPunctuation:
- return @"literal-punctuation";
- case SpeakNoPunctuation:
- return @"no-punctuation";
- default:
- case SpeakNormal:
- return @"normal";
- }
- }
-
- // Used by DRT to find an accessible node by its element id.
- if ([attributeName isEqualToString:@"AXDRTElementIdAttribute"])
- return m_object->getAttribute(idAttr);
-
- return nil;
-}
-
-- (NSString *)accessibilityPlatformMathSubscriptKey
-{
- return NSAccessibilityMathSubscriptAttribute;
-}
-
-- (NSString *)accessibilityPlatformMathSuperscriptKey
-{
- return NSAccessibilityMathSuperscriptAttribute;
-}
-
-- (id)accessibilityFocusedUIElement
-{
- if (![self updateObjectBackingStore])
- return nil;
-
- RefPtr<AccessibilityObject> focusedObj = m_object->focusedUIElement();
-
- if (!focusedObj)
- return nil;
-
- return focusedObj->wrapper();
-}
-
-- (id)accessibilityHitTest:(NSPoint)point
-{
- if (![self updateObjectBackingStore])
- return nil;
-
- m_object->updateChildrenIfNecessary();
- RefPtr<AccessibilityObject> axObject = m_object->accessibilityHitTest(IntPoint(point));
- if (axObject)
- return NSAccessibilityUnignoredAncestor(axObject->wrapper());
- return NSAccessibilityUnignoredAncestor(self);
-}
-
-- (BOOL)accessibilityIsAttributeSettable:(NSString*)attributeName
-{
- if (![self updateObjectBackingStore])
- return nil;
-
- if ([attributeName isEqualToString: @"AXSelectedTextMarkerRange"])
- return YES;
-
- if ([attributeName isEqualToString: NSAccessibilityFocusedAttribute])
- return m_object->canSetFocusAttribute();
-
- if ([attributeName isEqualToString: NSAccessibilityValueAttribute])
- return m_object->canSetValueAttribute();
-
- if ([attributeName isEqualToString: NSAccessibilitySelectedAttribute])
- return m_object->canSetSelectedAttribute();
-
- if ([attributeName isEqualToString: NSAccessibilitySelectedChildrenAttribute])
- return m_object->canSetSelectedChildrenAttribute();
-
- if ([attributeName isEqualToString:NSAccessibilityDisclosingAttribute])
- return m_object->canSetExpandedAttribute();
-
- if ([attributeName isEqualToString:NSAccessibilitySelectedRowsAttribute])
- return YES;
-
- if ([attributeName isEqualToString: NSAccessibilitySelectedTextAttribute] ||
- [attributeName isEqualToString: NSAccessibilitySelectedTextRangeAttribute] ||
- [attributeName isEqualToString: NSAccessibilityVisibleCharacterRangeAttribute])
- return m_object->canSetTextRangeAttributes();
-
- if ([attributeName isEqualToString:NSAccessibilityGrabbedAttribute])
- return YES;
-
- return NO;
-}
-
-// accessibilityShouldUseUniqueId is an AppKit method we override so that
-// objects will be given a unique ID, and therefore allow AppKit to know when they
-// become obsolete (e.g. when the user navigates to a new web page, making this one
-// unrendered but not deallocated because it is in the back/forward cache).
-// It is important to call NSAccessibilityUnregisterUniqueIdForUIElement in the
-// appropriate place (e.g. dealloc) to remove these non-retained references from
-// AppKit's id mapping tables. We do this in detach by calling unregisterUniqueIdForUIElement.
-//
-// Registering an object is also required for observing notifications. Only registered objects can be observed.
-- (BOOL)accessibilityIsIgnored
-{
- if (![self updateObjectBackingStore])
- return YES;
-
- if (m_object->isAttachment())
- return [[self attachmentView] accessibilityIsIgnored];
- return m_object->accessibilityIsIgnored();
-}
-
-- (NSArray* )accessibilityParameterizedAttributeNames
-{
- if (![self updateObjectBackingStore])
- return nil;
-
- if (m_object->isAttachment())
- return nil;
-
- static NSArray* paramAttrs = nil;
- static NSArray* textParamAttrs = nil;
- static NSArray* tableParamAttrs = nil;
- static NSArray* webAreaParamAttrs = nil;
- if (paramAttrs == nil) {
- paramAttrs = [[NSArray alloc] initWithObjects:
- @"AXUIElementForTextMarker",
- @"AXTextMarkerRangeForUIElement",
- @"AXLineForTextMarker",
- @"AXTextMarkerRangeForLine",
- @"AXStringForTextMarkerRange",
- @"AXTextMarkerForPosition",
- @"AXBoundsForTextMarkerRange",
- @"AXAttributedStringForTextMarkerRange",
- @"AXTextMarkerRangeForUnorderedTextMarkers",
- @"AXNextTextMarkerForTextMarker",
- @"AXPreviousTextMarkerForTextMarker",
- @"AXLeftWordTextMarkerRangeForTextMarker",
- @"AXRightWordTextMarkerRangeForTextMarker",
- @"AXLeftLineTextMarkerRangeForTextMarker",
- @"AXRightLineTextMarkerRangeForTextMarker",
- @"AXSentenceTextMarkerRangeForTextMarker",
- @"AXParagraphTextMarkerRangeForTextMarker",
- @"AXNextWordEndTextMarkerForTextMarker",
- @"AXPreviousWordStartTextMarkerForTextMarker",
- @"AXNextLineEndTextMarkerForTextMarker",
- @"AXPreviousLineStartTextMarkerForTextMarker",
- @"AXNextSentenceEndTextMarkerForTextMarker",
- @"AXPreviousSentenceStartTextMarkerForTextMarker",
- @"AXNextParagraphEndTextMarkerForTextMarker",
- @"AXPreviousParagraphStartTextMarkerForTextMarker",
- @"AXStyleTextMarkerRangeForTextMarker",
- @"AXLengthForTextMarkerRange",
- NSAccessibilityBoundsForRangeParameterizedAttribute,
- NSAccessibilityStringForRangeParameterizedAttribute,
- NSAccessibilityUIElementsForSearchPredicateParameterizedAttribute,
- nil];
- }
-
- if (textParamAttrs == nil) {
- NSMutableArray* tempArray = [[NSMutableArray alloc] initWithArray:paramAttrs];
- [tempArray addObject:(NSString*)kAXLineForIndexParameterizedAttribute];
- [tempArray addObject:(NSString*)kAXRangeForLineParameterizedAttribute];
- [tempArray addObject:(NSString*)kAXStringForRangeParameterizedAttribute];
- [tempArray addObject:(NSString*)kAXRangeForPositionParameterizedAttribute];
- [tempArray addObject:(NSString*)kAXRangeForIndexParameterizedAttribute];
- [tempArray addObject:(NSString*)kAXBoundsForRangeParameterizedAttribute];
- [tempArray addObject:(NSString*)kAXRTFForRangeParameterizedAttribute];
- [tempArray addObject:(NSString*)kAXAttributedStringForRangeParameterizedAttribute];
- [tempArray addObject:(NSString*)kAXStyleRangeForIndexParameterizedAttribute];
- textParamAttrs = [[NSArray alloc] initWithArray:tempArray];
- [tempArray release];
- }
- if (tableParamAttrs == nil) {
- NSMutableArray* tempArray = [[NSMutableArray alloc] initWithArray:paramAttrs];
- [tempArray addObject:NSAccessibilityCellForColumnAndRowParameterizedAttribute];
- tableParamAttrs = [[NSArray alloc] initWithArray:tempArray];
- [tempArray release];
- }
- if (!webAreaParamAttrs) {
- NSMutableArray* tempArray = [[NSMutableArray alloc] initWithArray:paramAttrs];
- [tempArray addObject:NSAccessibilityTextMarkerForIndexParameterizedAttribute];
- [tempArray addObject:NSAccessibilityTextMarkerIsValidParameterizedAttribute];
- [tempArray addObject:NSAccessibilityIndexForTextMarkerParameterizedAttribute];
- webAreaParamAttrs = [[NSArray alloc] initWithArray:tempArray];
- [tempArray release];
- }
-
- if (m_object->isPasswordField())
- return [NSArray array];
-
- if (!m_object->isAccessibilityRenderObject())
- return paramAttrs;
-
- if (m_object->isTextControl())
- return textParamAttrs;
-
- if (m_object->isAccessibilityTable())
- return tableParamAttrs;
-
- if (m_object->isMenuRelated())
- return nil;
-
- if (m_object->isWebArea())
- return webAreaParamAttrs;
-
- return paramAttrs;
-}
-
-- (void)accessibilityPerformPressAction
-{
- if (![self updateObjectBackingStore])
- return;
-
- if (m_object->isAttachment())
- [[self attachmentView] accessibilityPerformAction:NSAccessibilityPressAction];
- else
- m_object->press();
-}
-
-- (void)accessibilityPerformIncrementAction
-{
- if (![self updateObjectBackingStore])
- return;
-
- if (m_object->isAttachment())
- [[self attachmentView] accessibilityPerformAction:NSAccessibilityIncrementAction];
- else
- m_object->increment();
-}
-
-- (void)accessibilityPerformDecrementAction
-{
- if (![self updateObjectBackingStore])
- return;
-
- if (m_object->isAttachment())
- [[self attachmentView] accessibilityPerformAction:NSAccessibilityDecrementAction];
- else
- m_object->decrement();
-}
-
-- (void)accessibilityPerformShowMenuAction
-{
- if (m_object->roleValue() == ComboBoxRole)
- m_object->setIsExpanded(true);
- else {
- // This needs to be performed in an iteration of the run loop that did not start from an AX call.
- // If it's the same run loop iteration, the menu open notification won't be sent
- [self performSelector:@selector(accessibilityShowContextMenu) withObject:nil afterDelay:0.0];
- }
-}
-
-- (void)accessibilityShowContextMenu
-{
- Page* page = m_object->page();
- if (!page)
- return;
-
- IntRect rect = pixelSnappedIntRect(m_object->elementRect());
- FrameView* frameView = m_object->documentFrameView();
-
- // On WK2, we need to account for the scroll position.
- // On WK1, this isn't necessary, it's taken care of by the attachment views.
- if (frameView && !frameView->platformWidget()) {
- // Find the appropriate scroll view to use to convert the contents to the window.
- for (AccessibilityObject* parent = m_object->parentObject(); parent; parent = parent->parentObject()) {
- if (parent->isAccessibilityScrollView()) {
- ScrollView* scrollView = toAccessibilityScrollView(parent)->scrollView();
- rect = scrollView->contentsToRootView(rect);
- break;
- }
- }
- }
-
- page->contextMenuController()->showContextMenuAt(page->mainFrame(), rect.center());
-}
-
-- (void)accessibilityScrollToVisible
-{
- m_object->scrollToMakeVisible();
-}
-
-- (void)accessibilityPerformAction:(NSString*)action
-{
- if (![self updateObjectBackingStore])
- return;
-
- if ([action isEqualToString:NSAccessibilityPressAction])
- [self accessibilityPerformPressAction];
-
- else if ([action isEqualToString:NSAccessibilityShowMenuAction])
- [self accessibilityPerformShowMenuAction];
-
- else if ([action isEqualToString:NSAccessibilityIncrementAction])
- [self accessibilityPerformIncrementAction];
-
- else if ([action isEqualToString:NSAccessibilityDecrementAction])
- [self accessibilityPerformDecrementAction];
-
- else if ([action isEqualToString:NSAccessibilityScrollToVisibleAction])
- [self accessibilityScrollToVisible];
-}
-
-- (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attributeName
-{
- if (![self updateObjectBackingStore])
- return;
-
- id textMarkerRange = nil;
- NSNumber* number = nil;
- NSString* string = nil;
- NSRange range = {0, 0};
- NSArray* array = nil;
-
- // decode the parameter
- if (AXObjectIsTextMarkerRange(value))
- textMarkerRange = value;
-
- else if ([value isKindOfClass:[NSNumber self]])
- number = value;
-
- else if ([value isKindOfClass:[NSString self]])
- string = value;
-
- else if ([value isKindOfClass:[NSValue self]])
- range = [value rangeValue];
-
- else if ([value isKindOfClass:[NSArray self]])
- array = value;
-
- // handle the command
- if ([attributeName isEqualToString: @"AXSelectedTextMarkerRange"]) {
- ASSERT(textMarkerRange);
- m_object->setSelectedVisiblePositionRange([self visiblePositionRangeForTextMarkerRange:textMarkerRange]);
- } else if ([attributeName isEqualToString: NSAccessibilityFocusedAttribute]) {
- ASSERT(number);
-
- bool focus = [number boolValue];
-
- // If focus is just set without making the view the first responder, then keyboard focus won't move to the right place.
- if (focus && m_object->isWebArea() && !m_object->document()->frame()->selection()->isFocusedAndActive()) {
- FrameView* frameView = m_object->documentFrameView();
- Page* page = m_object->page();
- if (page && frameView) {
- ChromeClient* client = page->chrome().client();
- client->focus();
- if (frameView->platformWidget())
- client->makeFirstResponder(frameView->platformWidget());
- else
- client->makeFirstResponder();
- }
- }
-
- m_object->setFocused(focus);
- } else if ([attributeName isEqualToString: NSAccessibilityValueAttribute]) {
- if (number && m_object->canSetNumericValue())
- m_object->setValue([number floatValue]);
- else if (string)
- m_object->setValue(string);
- } else if ([attributeName isEqualToString: NSAccessibilitySelectedAttribute]) {
- if (!number)
- return;
- m_object->setSelected([number boolValue]);
- } else if ([attributeName isEqualToString: NSAccessibilitySelectedChildrenAttribute]) {
- if (!array || m_object->roleValue() != ListBoxRole)
- return;
- AccessibilityObject::AccessibilityChildrenVector selectedChildren;
- convertToVector(array, selectedChildren);
- static_cast<AccessibilityListBox*>(m_object)->setSelectedChildren(selectedChildren);
- } else if (m_object->isTextControl()) {
- if ([attributeName isEqualToString: NSAccessibilitySelectedTextAttribute]) {
- m_object->setSelectedText(string);
- } else if ([attributeName isEqualToString: NSAccessibilitySelectedTextRangeAttribute]) {
- m_object->setSelectedTextRange(PlainTextRange(range.location, range.length));
- } else if ([attributeName isEqualToString: NSAccessibilityVisibleCharacterRangeAttribute]) {
- m_object->makeRangeVisible(PlainTextRange(range.location, range.length));
- }
- } else if ([attributeName isEqualToString:NSAccessibilityDisclosingAttribute])
- m_object->setIsExpanded([number boolValue]);
- else if ([attributeName isEqualToString:NSAccessibilitySelectedRowsAttribute]) {
- AccessibilityObject::AccessibilityChildrenVector selectedRows;
- convertToVector(array, selectedRows);
- if (m_object->isTree() || m_object->isAccessibilityTable())
- m_object->setSelectedRows(selectedRows);
- } else if ([attributeName isEqualToString:NSAccessibilityGrabbedAttribute])
- m_object->setARIAGrabbed([number boolValue]);
-}
-
-static RenderObject* rendererForView(NSView* view)
-{
- if (![view conformsToProtocol:@protocol(WebCoreFrameView)])
- return 0;
-
- NSView<WebCoreFrameView>* frameView = (NSView<WebCoreFrameView>*)view;
- Frame* frame = [frameView _web_frame];
- if (!frame)
- return 0;
-
- Node* node = frame->document()->ownerElement();
- if (!node)
- return 0;
-
- return node->renderer();
-}
-
-- (id)_accessibilityParentForSubview:(NSView*)subview
-{
- RenderObject* renderer = rendererForView(subview);
- if (!renderer)
- return nil;
-
- AccessibilityObject* obj = renderer->document()->axObjectCache()->getOrCreate(renderer);
- if (obj)
- return obj->parentObjectUnignored()->wrapper();
- return nil;
-}
-
-- (NSString*)accessibilityActionDescription:(NSString*)action
-{
- // we have no custom actions
- return NSAccessibilityActionDescription(action);
-}
-
-// The CFAttributedStringType representation of the text associated with this accessibility
-// object that is specified by the given range.
-- (NSAttributedString*)doAXAttributedStringForRange:(NSRange)range
-{
- PlainTextRange textRange = PlainTextRange(range.location, range.length);
- VisiblePositionRange visiblePosRange = m_object->visiblePositionRangeForRange(textRange);
- return [self doAXAttributedStringForTextMarkerRange:[self textMarkerRangeFromVisiblePositions:visiblePosRange.start endPosition:visiblePosRange.end]];
-}
-
-- (NSRange)_convertToNSRange:(Range*)range
-{
- NSRange result = NSMakeRange(NSNotFound, 0);
- if (!range || !range->startContainer())
- return result;
-
- Document* document = m_object->document();
- if (!document)
- return result;
-
- size_t location;
- size_t length;
- TextIterator::getLocationAndLengthFromRange(document->documentElement(), range, location, length);
- result.location = location;
- result.length = length;
-
- return result;
-}
-
-- (NSInteger)_indexForTextMarker:(id)marker
-{
- if (!marker)
- return NSNotFound;
-
- VisibleSelection selection([self visiblePositionForTextMarker:marker]);
- return [self _convertToNSRange:selection.toNormalizedRange().get()].location;
-}
-
-- (id)_textMarkerForIndex:(NSInteger)textIndex
-{
- Document* document = m_object->document();
- if (!document)
- return nil;
-
- PassRefPtr<Range> textRange = TextIterator::rangeFromLocationAndLength(document->documentElement(), textIndex, 0);
- if (!textRange || !textRange->boundaryPointsValid())
- return nil;
-
- VisiblePosition position(textRange->startPosition());
- return [self textMarkerForVisiblePosition:position];
-}
-
-// The RTF representation of the text associated with this accessibility object that is
-// specified by the given range.
-- (NSData*)doAXRTFForRange:(NSRange)range
-{
- NSAttributedString* attrString = [self doAXAttributedStringForRange:range];
- return [attrString RTFFromRange: NSMakeRange(0, [attrString length]) documentAttributes: nil];
-}
-
-- (id)accessibilityAttributeValue:(NSString*)attribute forParameter:(id)parameter
-{
- id textMarker = nil;
- id textMarkerRange = nil;
- NSNumber* number = nil;
- NSArray* array = nil;
- NSDictionary* dictionary = nil;
- RefPtr<AccessibilityObject> uiElement = 0;
- NSPoint point = NSZeroPoint;
- bool pointSet = false;
- NSRange range = {0, 0};
- bool rangeSet = false;
-
- // basic parameter validation
- if (!m_object || !attribute || !parameter)
- return nil;
-
- if (![self updateObjectBackingStore])
- return nil;
-
- // common parameter type check/casting. Nil checks in handlers catch wrong type case.
- // NOTE: This assumes nil is not a valid parameter, because it is indistinguishable from
- // a parameter of the wrong type.
- if (AXObjectIsTextMarker(parameter))
- textMarker = parameter;
-
- else if (AXObjectIsTextMarkerRange(parameter))
- textMarkerRange = parameter;
-
- else if ([parameter isKindOfClass:[WebAccessibilityObjectWrapper self]])
- uiElement = [(WebAccessibilityObjectWrapper*)parameter accessibilityObject];
-
- else if ([parameter isKindOfClass:[NSNumber self]])
- number = parameter;
-
- else if ([parameter isKindOfClass:[NSArray self]])
- array = parameter;
-
- else if ([parameter isKindOfClass:[NSDictionary self]])
- dictionary = parameter;
-
- else if ([parameter isKindOfClass:[NSValue self]] && strcmp([(NSValue*)parameter objCType], @encode(NSPoint)) == 0) {
- pointSet = true;
- point = [(NSValue*)parameter pointValue];
-
- } else if ([parameter isKindOfClass:[NSValue self]] && strcmp([(NSValue*)parameter objCType], @encode(NSRange)) == 0) {
- rangeSet = true;
- range = [(NSValue*)parameter rangeValue];
- } else {
- // Attribute type is not supported. Allow super to handle.
- return [super accessibilityAttributeValue:attribute forParameter:parameter];
- }
-
- // dispatch
- if ([attribute isEqualToString:NSAccessibilityUIElementsForSearchPredicateParameterizedAttribute]) {
- AccessibilityObject* startObject = 0;
- if ([[dictionary objectForKey:@"AXStartElement"] isKindOfClass:[WebAccessibilityObjectWrapper self]])
- startObject = [(WebAccessibilityObjectWrapper*)[dictionary objectForKey:@"AXStartElement"] accessibilityObject];
-
- AccessibilitySearchDirection searchDirection = SearchDirectionNext;
- if ([[dictionary objectForKey:@"AXDirection"] isKindOfClass:[NSString self]])
- searchDirection = ([(NSString*)[dictionary objectForKey:@"AXDirection"] isEqualToString:@"AXDirectionNext"]) ? SearchDirectionNext : SearchDirectionPrevious;
-
- String searchText;
- if ([[dictionary objectForKey:@"AXSearchText"] isKindOfClass:[NSString self]])
- searchText = (CFStringRef)[dictionary objectForKey:@"AXSearchText"];
-
- unsigned resultsLimit = 0;
- if ([[dictionary objectForKey:@"AXResultsLimit"] isKindOfClass:[NSNumber self]])
- resultsLimit = [(NSNumber*)[dictionary objectForKey:@"AXResultsLimit"] unsignedIntValue];
-
- BOOL visibleOnly = NO;
- if ([[dictionary objectForKey:@"AXVisibleOnly"] isKindOfClass:[NSNumber self]])
- visibleOnly = [(NSNumber*)[dictionary objectForKey:@"AXVisibleOnly"] boolValue];
-
- AccessibilitySearchCriteria criteria = AccessibilitySearchCriteria(startObject, searchDirection, &searchText, resultsLimit, visibleOnly);
-
- id searchKeyEntry = [dictionary objectForKey:@"AXSearchKey"];
- if ([searchKeyEntry isKindOfClass:[NSString class]])
- criteria.searchKeys.append(accessibilitySearchKeyForString((CFStringRef)searchKeyEntry));
- else if ([searchKeyEntry isKindOfClass:[NSArray class]]) {
- size_t length = static_cast<size_t>([(NSArray *)searchKeyEntry count]);
- criteria.searchKeys.reserveInitialCapacity(length);
- for (size_t i = 0; i < length; ++i) {
- id searchKey = [(NSArray *)searchKeyEntry objectAtIndex:i];
- if ([searchKey isKindOfClass:[NSString class]])
- criteria.searchKeys.append(accessibilitySearchKeyForString((CFStringRef)searchKey));
- }
- }
-
- AccessibilityObject::AccessibilityChildrenVector results;
- m_object->findMatchingObjects(&criteria, results);
-
- return convertToNSArray(results);
- }
-
- if ([attribute isEqualToString:NSAccessibilityTextMarkerIsValidParameterizedAttribute]) {
- VisiblePosition pos = [self visiblePositionForTextMarker:textMarker];
- return [NSNumber numberWithBool:!pos.isNull()];
- }
- if ([attribute isEqualToString:NSAccessibilityIndexForTextMarkerParameterizedAttribute]) {
- return [NSNumber numberWithInteger:[self _indexForTextMarker:textMarker]];
- }
- if ([attribute isEqualToString:NSAccessibilityTextMarkerForIndexParameterizedAttribute]) {
- return [self _textMarkerForIndex:[number integerValue]];
- }
-
- if ([attribute isEqualToString:@"AXUIElementForTextMarker"]) {
- VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];
- AccessibilityObject* axObject = m_object->accessibilityObjectForPosition(visiblePos);
- if (!axObject)
- return nil;
- return axObject->wrapper();
- }
-
- if ([attribute isEqualToString:@"AXTextMarkerRangeForUIElement"]) {
- VisiblePositionRange vpRange = uiElement.get()->visiblePositionRange();
- return [self textMarkerRangeFromVisiblePositions:vpRange.start endPosition:vpRange.end];
- }
-
- if ([attribute isEqualToString:@"AXLineForTextMarker"]) {
- VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];
- return [NSNumber numberWithUnsignedInt:m_object->lineForPosition(visiblePos)];
- }
-
- if ([attribute isEqualToString:@"AXTextMarkerRangeForLine"]) {
- VisiblePositionRange vpRange = m_object->visiblePositionRangeForLine([number intValue]);
- return [self textMarkerRangeFromVisiblePositions:vpRange.start endPosition:vpRange.end];
- }
-
- if ([attribute isEqualToString:@"AXStringForTextMarkerRange"]) {
- VisiblePositionRange visiblePosRange = [self visiblePositionRangeForTextMarkerRange:textMarkerRange];
- return m_object->stringForVisiblePositionRange(visiblePosRange);
- }
-
- if ([attribute isEqualToString:@"AXTextMarkerForPosition"]) {
- IntPoint webCorePoint = IntPoint(point);
- return pointSet ? [self textMarkerForVisiblePosition:m_object->visiblePositionForPoint(webCorePoint)] : nil;
- }
-
- if ([attribute isEqualToString:@"AXBoundsForTextMarkerRange"]) {
- VisiblePositionRange visiblePosRange = [self visiblePositionRangeForTextMarkerRange:textMarkerRange];
- NSRect rect = m_object->boundsForVisiblePositionRange(visiblePosRange);
- return [NSValue valueWithRect:rect];
- }
-
- if ([attribute isEqualToString:NSAccessibilityBoundsForRangeParameterizedAttribute]) {
- VisiblePosition start = m_object->visiblePositionForIndex(range.location);
- VisiblePosition end = m_object->visiblePositionForIndex(range.location+range.length);
- if (start.isNull() || end.isNull())
- return nil;
- NSRect rect = m_object->boundsForVisiblePositionRange(VisiblePositionRange(start, end));
- return [NSValue valueWithRect:rect];
- }
-
- if ([attribute isEqualToString:NSAccessibilityStringForRangeParameterizedAttribute]) {
- VisiblePosition start = m_object->visiblePositionForIndex(range.location);
- VisiblePosition end = m_object->visiblePositionForIndex(range.location+range.length);
- if (start.isNull() || end.isNull())
- return nil;
- return m_object->stringForVisiblePositionRange(VisiblePositionRange(start, end));
- }
-
- if ([attribute isEqualToString:@"AXAttributedStringForTextMarkerRange"])
- return [self doAXAttributedStringForTextMarkerRange:textMarkerRange];
-
- if ([attribute isEqualToString:@"AXTextMarkerRangeForUnorderedTextMarkers"]) {
- if ([array count] < 2)
- return nil;
-
- id textMarker1 = [array objectAtIndex:0];
- id textMarker2 = [array objectAtIndex:1];
- if (!AXObjectIsTextMarker(textMarker1) || !AXObjectIsTextMarker(textMarker2))
- return nil;
-
- VisiblePosition visiblePos1 = [self visiblePositionForTextMarker:(textMarker1)];
- VisiblePosition visiblePos2 = [self visiblePositionForTextMarker:(textMarker2)];
- VisiblePositionRange vpRange = m_object->visiblePositionRangeForUnorderedPositions(visiblePos1, visiblePos2);
- return [self textMarkerRangeFromVisiblePositions:vpRange.start endPosition:vpRange.end];
- }
-
- if ([attribute isEqualToString:@"AXNextTextMarkerForTextMarker"]) {
- VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];
- return [self textMarkerForVisiblePosition:m_object->nextVisiblePosition(visiblePos)];
- }
-
- if ([attribute isEqualToString:@"AXPreviousTextMarkerForTextMarker"]) {
- VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];
- return [self textMarkerForVisiblePosition:m_object->previousVisiblePosition(visiblePos)];
- }
-
- if ([attribute isEqualToString:@"AXLeftWordTextMarkerRangeForTextMarker"]) {
- VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];
- VisiblePositionRange vpRange = m_object->positionOfLeftWord(visiblePos);
- return [self textMarkerRangeFromVisiblePositions:vpRange.start endPosition:vpRange.end];
- }
-
- if ([attribute isEqualToString:@"AXRightWordTextMarkerRangeForTextMarker"]) {
- VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];
- VisiblePositionRange vpRange = m_object->positionOfRightWord(visiblePos);
- return [self textMarkerRangeFromVisiblePositions:vpRange.start endPosition:vpRange.end];
- }
-
- if ([attribute isEqualToString:@"AXLeftLineTextMarkerRangeForTextMarker"]) {
- VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];
- VisiblePositionRange vpRange = m_object->leftLineVisiblePositionRange(visiblePos);
- return [self textMarkerRangeFromVisiblePositions:vpRange.start endPosition:vpRange.end];
- }
-
- if ([attribute isEqualToString:@"AXRightLineTextMarkerRangeForTextMarker"]) {
- VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];
- VisiblePositionRange vpRange = m_object->rightLineVisiblePositionRange(visiblePos);
- return [self textMarkerRangeFromVisiblePositions:vpRange.start endPosition:vpRange.end];
- }
-
- if ([attribute isEqualToString:@"AXSentenceTextMarkerRangeForTextMarker"]) {
- VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];
- VisiblePositionRange vpRange = m_object->sentenceForPosition(visiblePos);
- return [self textMarkerRangeFromVisiblePositions:vpRange.start endPosition:vpRange.end];
- }
-
- if ([attribute isEqualToString:@"AXParagraphTextMarkerRangeForTextMarker"]) {
- VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];
- VisiblePositionRange vpRange = m_object->paragraphForPosition(visiblePos);
- return [self textMarkerRangeFromVisiblePositions:vpRange.start endPosition:vpRange.end];
- }
-
- if ([attribute isEqualToString:@"AXNextWordEndTextMarkerForTextMarker"]) {
- VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];
- return [self textMarkerForVisiblePosition:m_object->nextWordEnd(visiblePos)];
- }
-
- if ([attribute isEqualToString:@"AXPreviousWordStartTextMarkerForTextMarker"]) {
- VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];
- return [self textMarkerForVisiblePosition:m_object->previousWordStart(visiblePos)];
- }
-
- if ([attribute isEqualToString:@"AXNextLineEndTextMarkerForTextMarker"]) {
- VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];
- return [self textMarkerForVisiblePosition:m_object->nextLineEndPosition(visiblePos)];
- }
-
- if ([attribute isEqualToString:@"AXPreviousLineStartTextMarkerForTextMarker"]) {
- VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];
- return [self textMarkerForVisiblePosition:m_object->previousLineStartPosition(visiblePos)];
- }
-
- if ([attribute isEqualToString:@"AXNextSentenceEndTextMarkerForTextMarker"]) {
- VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];
- return [self textMarkerForVisiblePosition:m_object->nextSentenceEndPosition(visiblePos)];
- }
-
- if ([attribute isEqualToString:@"AXPreviousSentenceStartTextMarkerForTextMarker"]) {
- VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];
- return [self textMarkerForVisiblePosition:m_object->previousSentenceStartPosition(visiblePos)];
- }
-
- if ([attribute isEqualToString:@"AXNextParagraphEndTextMarkerForTextMarker"]) {
- VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];
- return [self textMarkerForVisiblePosition:m_object->nextParagraphEndPosition(visiblePos)];
- }
-
- if ([attribute isEqualToString:@"AXPreviousParagraphStartTextMarkerForTextMarker"]) {
- VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];
- return [self textMarkerForVisiblePosition:m_object->previousParagraphStartPosition(visiblePos)];
- }
-
- if ([attribute isEqualToString:@"AXStyleTextMarkerRangeForTextMarker"]) {
- VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];
- VisiblePositionRange vpRange = m_object->styleRangeForPosition(visiblePos);
- return [self textMarkerRangeFromVisiblePositions:vpRange.start endPosition:vpRange.end];
- }
-
- if ([attribute isEqualToString:@"AXLengthForTextMarkerRange"]) {
- VisiblePositionRange visiblePosRange = [self visiblePositionRangeForTextMarkerRange:textMarkerRange];
- int length = m_object->lengthForVisiblePositionRange(visiblePosRange);
- if (length < 0)
- return nil;
- return [NSNumber numberWithInt:length];
- }
-
- // Used only by DumpRenderTree (so far).
- if ([attribute isEqualToString:@"AXStartTextMarkerForTextMarkerRange"]) {
- VisiblePositionRange visiblePosRange = [self visiblePositionRangeForTextMarkerRange:textMarkerRange];
- return [self textMarkerForVisiblePosition:visiblePosRange.start];
- }
-
- if ([attribute isEqualToString:@"AXEndTextMarkerForTextMarkerRange"]) {
- VisiblePositionRange visiblePosRange = [self visiblePositionRangeForTextMarkerRange:textMarkerRange];
- return [self textMarkerForVisiblePosition:visiblePosRange.end];
- }
-
- if (m_object->isAccessibilityTable()) {
- if ([attribute isEqualToString:NSAccessibilityCellForColumnAndRowParameterizedAttribute]) {
- if (array == nil || [array count] != 2)
- return nil;
- AccessibilityTableCell* cell = static_cast<AccessibilityTable*>(m_object)->cellForColumnAndRow([[array objectAtIndex:0] unsignedIntValue], [[array objectAtIndex:1] unsignedIntValue]);
- if (!cell)
- return nil;
-
- return cell->wrapper();
- }
- }
-
- if (m_object->isTextControl()) {
- if ([attribute isEqualToString: (NSString *)kAXLineForIndexParameterizedAttribute]) {
- int lineNumber = m_object->doAXLineForIndex([number intValue]);
- if (lineNumber < 0)
- return nil;
- return [NSNumber numberWithUnsignedInt:lineNumber];
- }
-
- if ([attribute isEqualToString: (NSString *)kAXRangeForLineParameterizedAttribute]) {
- PlainTextRange textRange = m_object->doAXRangeForLine([number intValue]);
- return [NSValue valueWithRange: NSMakeRange(textRange.start, textRange.length)];
- }
-
- if ([attribute isEqualToString: (NSString*)kAXStringForRangeParameterizedAttribute]) {
- PlainTextRange plainTextRange = PlainTextRange(range.location, range.length);
- return rangeSet ? (id)(m_object->doAXStringForRange(plainTextRange)) : nil;
- }
-
- if ([attribute isEqualToString: (NSString*)kAXRangeForPositionParameterizedAttribute]) {
- if (!pointSet)
- return nil;
- IntPoint webCorePoint = IntPoint(point);
- PlainTextRange textRange = m_object->doAXRangeForPosition(webCorePoint);
- return [NSValue valueWithRange: NSMakeRange(textRange.start, textRange.length)];
- }
-
- if ([attribute isEqualToString: (NSString*)kAXRangeForIndexParameterizedAttribute]) {
- PlainTextRange textRange = m_object->doAXRangeForIndex([number intValue]);
- return [NSValue valueWithRange: NSMakeRange(textRange.start, textRange.length)];
- }
-
- if ([attribute isEqualToString: (NSString*)kAXBoundsForRangeParameterizedAttribute]) {
- if (!rangeSet)
- return nil;
- PlainTextRange plainTextRange = PlainTextRange(range.location, range.length);
- NSRect rect = m_object->doAXBoundsForRange(plainTextRange);
- return [NSValue valueWithRect:rect];
- }
-
- if ([attribute isEqualToString: (NSString*)kAXRTFForRangeParameterizedAttribute])
- return rangeSet ? [self doAXRTFForRange:range] : nil;
-
- if ([attribute isEqualToString: (NSString*)kAXAttributedStringForRangeParameterizedAttribute])
- return rangeSet ? [self doAXAttributedStringForRange:range] : nil;
-
- if ([attribute isEqualToString: (NSString*)kAXStyleRangeForIndexParameterizedAttribute]) {
- PlainTextRange textRange = m_object->doAXStyleRangeForIndex([number intValue]);
- return [NSValue valueWithRange: NSMakeRange(textRange.start, textRange.length)];
- }
- }
-
- // There are some parameters that super handles that are not explicitly returned by the list of the element's attributes.
- // In that case it must be passed to super.
- return [super accessibilityAttributeValue:attribute forParameter:parameter];
-}
-
-- (BOOL)accessibilitySupportsOverriddenAttributes
-{
- return YES;
-}
-
-- (BOOL)accessibilityShouldUseUniqueId
-{
- // All AX object wrappers should use unique ID's because it's faster within AppKit to look them up.
- return YES;
-}
-
-// API that AppKit uses for faster access
-- (NSUInteger)accessibilityIndexOfChild:(id)child
-{
- if (![self updateObjectBackingStore])
- return NSNotFound;
-
- // Tree objects return their rows as their children. We can use the original method
- // here, because we won't gain any speed up.
- if (m_object->isTree())
- return [super accessibilityIndexOfChild:child];
-
- const AccessibilityObject::AccessibilityChildrenVector& children = m_object->children();
-
- if (children.isEmpty())
- return [[self renderWidgetChildren] indexOfObject:child];
-
- unsigned count = children.size();
- for (unsigned k = 0; k < count; ++k) {
- WebAccessibilityObjectWrapper* wrapper = children[k]->wrapper();
- if (wrapper == child || (children[k]->isAttachment() && [wrapper attachmentView] == child))
- return k;
- }
-
- return NSNotFound;
-}
-
-- (NSUInteger)accessibilityArrayAttributeCount:(NSString *)attribute
-{
- if (![self updateObjectBackingStore])
- return 0;
-
- if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
- // Tree items object returns a different set of children than those that are in children()
- // because an AXOutline (the mac role is becomes) has some odd stipulations.
- if (m_object->isTree() || m_object->isTreeItem())
- return [[self accessibilityAttributeValue:NSAccessibilityChildrenAttribute] count];
-
- const AccessibilityObject::AccessibilityChildrenVector& children = m_object->children();
- if (children.isEmpty())
- return [[self renderWidgetChildren] count];
-
- return children.size();
- }
-
- return [super accessibilityArrayAttributeCount:attribute];
-}
-
-- (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount
-{
- if (![self updateObjectBackingStore])
- return nil;
-
- if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
- if (m_object->children().isEmpty()) {
- NSArray *children = [self renderWidgetChildren];
- if (!children)
- return nil;
-
- NSUInteger childCount = [children count];
- if (index >= childCount)
- return nil;
-
- NSUInteger arrayLength = min(childCount - index, maxCount);
- return [children subarrayWithRange:NSMakeRange(index, arrayLength)];
- } else if (m_object->isTree()) {
- // Tree objects return their rows as their children. We can use the original method in this case.
- return [super accessibilityArrayAttributeValues:attribute index:index maxCount:maxCount];
- }
-
- const AccessibilityObject::AccessibilityChildrenVector& children = m_object->children();
- unsigned childCount = children.size();
- if (index >= childCount)
- return nil;
-
- unsigned available = min(childCount - index, maxCount);
-
- NSMutableArray *subarray = [NSMutableArray arrayWithCapacity:available];
- for (unsigned added = 0; added < available; ++index, ++added) {
- WebAccessibilityObjectWrapper* wrapper = children[index]->wrapper();
- if (wrapper) {
- // The attachment view should be returned, otherwise AX palindrome errors occur.
- if (children[index]->isAttachment() && [wrapper attachmentView])
- [subarray addObject:[wrapper attachmentView]];
- else
- [subarray addObject:wrapper];
- }
- }
-
- return subarray;
- }
-
- return [super accessibilityArrayAttributeValues:attribute index:index maxCount:maxCount];
-}
-
-@end
-
-#endif // HAVE(ACCESSIBILITY)
diff --git a/Source/WebCore/accessibility/win/AXObjectCacheWin.cpp b/Source/WebCore/accessibility/win/AXObjectCacheWin.cpp
deleted file mode 100644
index 54fe4a76a..000000000
--- a/Source/WebCore/accessibility/win/AXObjectCacheWin.cpp
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Copyright (C) 2008, 2009, 2010, 2013 Apple Inc. All Rights Reserved.
- * Copyright (C) 2012 Serotek Corporation. All Rights Reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-#include "config.h"
-#include "AXObjectCache.h"
-
-#include "AccessibilityObject.h"
-#include "Chrome.h"
-#include "ChromeClient.h"
-#include "Document.h"
-#include "Page.h"
-#include "RenderObject.h"
-
-// Provided by IAccessibleEventID.idl
-#define IA2_EVENT_DOCUMENT_LOAD_COMPLETE 261
-
-using namespace std;
-
-namespace WebCore {
-
-void AXObjectCache::detachWrapper(AccessibilityObject* obj)
-{
- // On Windows, AccessibilityObjects are created when get_accChildCount is
- // called, but they are not wrapped until get_accChild is called, so this
- // object may not have a wrapper.
- if (AccessibilityObjectWrapper* wrapper = obj->wrapper())
- wrapper->detach();
-}
-
-void AXObjectCache::attachWrapper(AccessibilityObject*)
-{
- // On Windows, AccessibilityObjects are wrapped when the accessibility
- // software requests them via get_accChild.
-}
-
-void AXObjectCache::handleScrolledToAnchor(const Node* anchorNode)
-{
- // The anchor node may not be accessible. Post the notification for the
- // first accessible object.
- postPlatformNotification(AccessibilityObject::firstAccessibleObjectFromNode(anchorNode), AXScrolledToAnchor);
-}
-
-void AXObjectCache::postPlatformNotification(AccessibilityObject* obj, AXNotification notification)
-{
- if (!obj)
- return;
-
- Document* document = obj->document();
- if (!document)
- return;
-
- Page* page = document->page();
- if (!page || !page->chrome().platformPageClient())
- return;
-
- DWORD msaaEvent;
- switch (notification) {
- case AXCheckedStateChanged:
- msaaEvent = EVENT_OBJECT_STATECHANGE;
- break;
-
- case AXFocusedUIElementChanged:
- case AXActiveDescendantChanged:
- msaaEvent = EVENT_OBJECT_FOCUS;
- break;
-
- case AXScrolledToAnchor:
- msaaEvent = EVENT_SYSTEM_SCROLLINGSTART;
- break;
-
- case AXLayoutComplete:
- msaaEvent = EVENT_OBJECT_REORDER;
- break;
-
- case AXLoadComplete:
- msaaEvent = IA2_EVENT_DOCUMENT_LOAD_COMPLETE;
- break;
-
- case AXValueChanged:
- case AXMenuListValueChanged:
- msaaEvent = EVENT_OBJECT_VALUECHANGE;
- break;
-
- case AXMenuListItemSelected:
- msaaEvent = EVENT_OBJECT_SELECTION;
- break;
-
- default:
- return;
- }
-
- // Windows will end up calling get_accChild() on the root accessible
- // object for the WebView, passing the child ID that we specify below. We
- // negate the AXID so we know that the caller is passing the ID of an
- // element, not the index of a child element.
-
- ASSERT(obj->axObjectID() >= 1);
- ASSERT(obj->axObjectID() <= numeric_limits<LONG>::max());
-
- NotifyWinEvent(msaaEvent, page->chrome().platformPageClient(), OBJID_CLIENT, -static_cast<LONG>(obj->axObjectID()));
-}
-
-void AXObjectCache::nodeTextChangePlatformNotification(AccessibilityObject*, AXTextChange, unsigned, const String&)
-{
-}
-
-void AXObjectCache::frameLoadingEventPlatformNotification(AccessibilityObject* obj, AXLoadingEvent notification)
-{
- if (!obj)
- return;
-
- Document* document = obj->document();
- if (!document)
- return;
-
- Page* page = document->page();
- if (!page)
- return;
-
- if (notification == AXLoadingStarted)
- page->chrome().client()->AXStartFrameLoad();
- else if (notification == AXLoadingFinished)
- page->chrome().client()->AXFinishFrameLoad();
-}
-
-AXID AXObjectCache::platformGenerateAXID() const
-{
- static AXID lastUsedID = 0;
-
- // Generate a new ID. Windows accessibility relies on a positive AXID,
- // ranging from 1 to LONG_MAX.
- AXID objID = lastUsedID;
- do {
- ++objID;
- objID %= std::numeric_limits<LONG>::max();
- } while (objID == 0 || HashTraits<AXID>::isDeletedValue(objID) || m_idsInUse.contains(objID));
-
- ASSERT(objID >= 1 && objID <= std::numeric_limits<LONG>::max());
-
- lastUsedID = objID;
-
- return objID;
-}
-
-void AXObjectCache::handleFocusedUIElementChanged(Node*, Node* newFocusedNode)
-{
- if (!newFocusedNode)
- return;
-
- Page* page = newFocusedNode->document()->page();
- if (!page || !page->chrome().platformPageClient())
- return;
-
- AccessibilityObject* focusedObject = focusedUIElementForPage(page);
- if (!focusedObject)
- return;
-
- ASSERT(!focusedObject->accessibilityIsIgnored());
-
- postPlatformNotification(focusedObject, AXFocusedUIElementChanged);
-}
-
-} // namespace WebCore
diff --git a/Source/WebCore/accessibility/win/AccessibilityObjectWin.cpp b/Source/WebCore/accessibility/win/AccessibilityObjectWin.cpp
deleted file mode 100644
index 44122ef27..000000000
--- a/Source/WebCore/accessibility/win/AccessibilityObjectWin.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2008 Apple Inc. All Rights Reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "config.h"
-#include "AccessibilityObject.h"
-
-#if HAVE(ACCESSIBILITY)
-
-namespace WebCore {
-
-bool AccessibilityObject::accessibilityIgnoreAttachment() const
-{
- return false;
-}
-
-AccessibilityObjectInclusion AccessibilityObject::accessibilityPlatformIncludesObject() const
-{
- if (isMenuListPopup() || isMenuListOption())
- return IncludeObject;
-
- return DefaultBehavior;
-}
-
-} // namespace WebCore
-
-#endif // HAVE(ACCESSIBILITY)
diff --git a/Source/WebCore/accessibility/win/AccessibilityObjectWrapperWin.cpp b/Source/WebCore/accessibility/win/AccessibilityObjectWrapperWin.cpp
deleted file mode 100644
index ba3d53ba4..000000000
--- a/Source/WebCore/accessibility/win/AccessibilityObjectWrapperWin.cpp
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2013 Apple Inc. All Rights Reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "config.h"
-#include "AccessibilityObjectWrapperWin.h"
-
-#if HAVE(ACCESSIBILITY)
-
-#include "AXObjectCache.h"
-#include "AccessibilityObject.h"
-#include "BString.h"
-#include "HTMLNames.h"
-#include "QualifiedName.h"
-
-namespace WebCore {
-
-void AccessibilityObjectWrapper::accessibilityAttributeValue(const AtomicString& attributeName, VARIANT* result)
-{
- // FIXME: This should be fleshed out to match the Mac version
-
- // Not a real concept on Windows, but used heavily in WebKit accessibility testing.
- if (attributeName == "AXTitleUIElementAttribute") {
- if (!m_object->exposesTitleUIElement())
- return;
-
- AccessibilityObject* obj = m_object->titleUIElement();
- if (obj) {
- ASSERT(V_VT(result) == VT_EMPTY);
- V_VT(result) = VT_UNKNOWN;
- AccessibilityObjectWrapper* wrapper = obj->wrapper();
- V_UNKNOWN(result) = wrapper;
- if (wrapper)
- wrapper->AddRef();
- }
- return;
- }
-
- // Used by DRT to find an accessible node by its element id.
- if (attributeName == "AXDRTElementIdAttribute") {
- ASSERT(V_VT(result) == VT_EMPTY);
-
- V_VT(result) = VT_BSTR;
- V_BSTR(result) = WebCore::BString(m_object->getAttribute(WebCore::HTMLNames::idAttr)).release();
- return;
- }
-}
-
-
-} // namespace WebCore
-
-#endif // HAVE(ACCESSIBILITY)
diff --git a/Source/WebCore/accessibility/win/AccessibilityObjectWrapperWin.h b/Source/WebCore/accessibility/win/AccessibilityObjectWrapperWin.h
deleted file mode 100644
index f32159117..000000000
--- a/Source/WebCore/accessibility/win/AccessibilityObjectWrapperWin.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2008 Apple Inc. All Rights Reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-#ifndef AccessibilityObjectWrapperWin_h
-#define AccessibilityObjectWrapperWin_h
-
-#include <wtf/text/AtomicString.h>
-
-namespace WebCore {
-
- class AccessibilityObject;
-
- class AccessibilityObjectWrapper : public IUnknown {
- public:
- // IUnknown
- virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) = 0;
- virtual ULONG STDMETHODCALLTYPE AddRef(void) = 0;
- virtual ULONG STDMETHODCALLTYPE Release(void) = 0;
-
- virtual void detach() = 0;
- bool attached() const { return m_object; }
- AccessibilityObject* accessibilityObject() const { return m_object; }
-
- void accessibilityAttributeValue(const AtomicString&, VARIANT*);
-
- protected:
- AccessibilityObjectWrapper(AccessibilityObject* obj) : m_object(obj) { }
- AccessibilityObjectWrapper() : m_object(0) { }
-
- AccessibilityObject* m_object;
- };
-
-} // namespace WebCore
-
-#endif // AccessibilityObjectWrapperWin_h