summaryrefslogtreecommitdiff
path: root/Source/WebCore/accessibility
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/accessibility')
-rw-r--r--Source/WebCore/accessibility/AXObjectCache.cpp1956
-rw-r--r--Source/WebCore/accessibility/AXObjectCache.h309
-rw-r--r--Source/WebCore/accessibility/AXTextStateChangeIntent.h94
-rw-r--r--Source/WebCore/accessibility/AccessibilityARIAGrid.cpp68
-rw-r--r--Source/WebCore/accessibility/AccessibilityARIAGrid.h27
-rw-r--r--Source/WebCore/accessibility/AccessibilityARIAGridCell.cpp101
-rw-r--r--Source/WebCore/accessibility/AccessibilityARIAGridCell.h26
-rw-r--r--Source/WebCore/accessibility/AccessibilityARIAGridRow.cpp32
-rw-r--r--Source/WebCore/accessibility/AccessibilityARIAGridRow.h25
-rw-r--r--Source/WebCore/accessibility/AccessibilityAllInOne.cpp60
-rw-r--r--Source/WebCore/accessibility/AccessibilityAttachment.cpp110
-rw-r--r--Source/WebCore/accessibility/AccessibilityAttachment.h59
-rw-r--r--Source/WebCore/accessibility/AccessibilityImageMapLink.cpp16
-rw-r--r--Source/WebCore/accessibility/AccessibilityImageMapLink.h63
-rw-r--r--Source/WebCore/accessibility/AccessibilityLabel.cpp109
-rw-r--r--Source/WebCore/accessibility/AccessibilityLabel.h57
-rw-r--r--Source/WebCore/accessibility/AccessibilityList.cpp110
-rw-r--r--Source/WebCore/accessibility/AccessibilityList.h26
-rw-r--r--Source/WebCore/accessibility/AccessibilityListBox.cpp42
-rw-r--r--Source/WebCore/accessibility/AccessibilityListBox.h35
-rw-r--r--Source/WebCore/accessibility/AccessibilityListBoxOption.cpp55
-rw-r--r--Source/WebCore/accessibility/AccessibilityListBoxOption.h51
-rw-r--r--Source/WebCore/accessibility/AccessibilityMathMLElement.cpp456
-rw-r--r--Source/WebCore/accessibility/AccessibilityMathMLElement.h115
-rw-r--r--Source/WebCore/accessibility/AccessibilityMediaControls.cpp71
-rw-r--r--Source/WebCore/accessibility/AccessibilityMediaControls.h69
-rw-r--r--Source/WebCore/accessibility/AccessibilityMenuList.cpp42
-rw-r--r--Source/WebCore/accessibility/AccessibilityMenuList.h28
-rw-r--r--Source/WebCore/accessibility/AccessibilityMenuListOption.cpp12
-rw-r--r--Source/WebCore/accessibility/AccessibilityMenuListOption.h40
-rw-r--r--Source/WebCore/accessibility/AccessibilityMenuListPopup.cpp17
-rw-r--r--Source/WebCore/accessibility/AccessibilityMenuListPopup.h33
-rw-r--r--Source/WebCore/accessibility/AccessibilityMockObject.cpp2
-rw-r--r--Source/WebCore/accessibility/AccessibilityMockObject.h22
-rw-r--r--Source/WebCore/accessibility/AccessibilityNodeObject.cpp856
-rw-r--r--Source/WebCore/accessibility/AccessibilityNodeObject.h231
-rw-r--r--Source/WebCore/accessibility/AccessibilityObject.cpp1530
-rw-r--r--Source/WebCore/accessibility/AccessibilityObject.h365
-rw-r--r--Source/WebCore/accessibility/AccessibilityProgressIndicator.cpp104
-rw-r--r--Source/WebCore/accessibility/AccessibilityProgressIndicator.h37
-rw-r--r--Source/WebCore/accessibility/AccessibilityRenderObject.cpp2195
-rw-r--r--Source/WebCore/accessibility/AccessibilityRenderObject.h343
-rw-r--r--Source/WebCore/accessibility/AccessibilitySVGElement.cpp314
-rw-r--r--Source/WebCore/accessibility/AccessibilitySVGElement.h62
-rw-r--r--Source/WebCore/accessibility/AccessibilitySVGRoot.cpp12
-rw-r--r--Source/WebCore/accessibility/AccessibilitySVGRoot.h29
-rw-r--r--Source/WebCore/accessibility/AccessibilityScrollView.cpp108
-rw-r--r--Source/WebCore/accessibility/AccessibilityScrollView.h62
-rw-r--r--Source/WebCore/accessibility/AccessibilityScrollbar.cpp20
-rw-r--r--Source/WebCore/accessibility/AccessibilityScrollbar.h33
-rw-r--r--Source/WebCore/accessibility/AccessibilitySlider.cpp31
-rw-r--r--Source/WebCore/accessibility/AccessibilitySlider.h51
-rw-r--r--Source/WebCore/accessibility/AccessibilitySpinButton.cpp44
-rw-r--r--Source/WebCore/accessibility/AccessibilitySpinButton.h41
-rw-r--r--Source/WebCore/accessibility/AccessibilityTable.cpp351
-rw-r--r--Source/WebCore/accessibility/AccessibilityTable.h53
-rw-r--r--Source/WebCore/accessibility/AccessibilityTableCell.cpp280
-rw-r--r--Source/WebCore/accessibility/AccessibilityTableCell.h48
-rw-r--r--Source/WebCore/accessibility/AccessibilityTableColumn.cpp137
-rw-r--r--Source/WebCore/accessibility/AccessibilityTableColumn.h35
-rw-r--r--Source/WebCore/accessibility/AccessibilityTableHeaderContainer.cpp16
-rw-r--r--Source/WebCore/accessibility/AccessibilityTableHeaderContainer.h26
-rw-r--r--Source/WebCore/accessibility/AccessibilityTableRow.cpp83
-rw-r--r--Source/WebCore/accessibility/AccessibilityTableRow.h36
-rw-r--r--Source/WebCore/accessibility/AccessibilityTree.cpp113
-rw-r--r--Source/WebCore/accessibility/AccessibilityTree.h (renamed from Source/WebCore/accessibility/AccessibilitySearchFieldButtons.h)34
-rw-r--r--Source/WebCore/accessibility/AccessibilityTreeItem.cpp (renamed from Source/WebCore/accessibility/AccessibilitySearchFieldButtons.cpp)59
-rw-r--r--Source/WebCore/accessibility/AccessibilityTreeItem.h50
-rw-r--r--Source/WebCore/accessibility/atk/AXObjectCacheAtk.cpp60
-rw-r--r--Source/WebCore/accessibility/atk/AccessibilityObjectAtk.cpp118
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleHyperlink.cpp6
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleHyperlink.h7
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceAction.h7
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceComponent.cpp2
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceComponent.h7
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceDocument.cpp2
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceDocument.h6
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceEditableText.h6
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceHyperlinkImpl.h6
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceHypertext.cpp7
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceHypertext.h7
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceImage.cpp4
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceImage.h6
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceSelection.cpp90
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceSelection.h6
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceTable.cpp57
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceTable.h6
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceTableCell.cpp160
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceTableCell.h32
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceText.cpp72
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceText.h6
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceValue.cpp105
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceValue.h6
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleUtil.cpp13
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleUtil.h8
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleWrapperAtk.cpp659
-rw-r--r--Source/WebCore/accessibility/atk/WebKitAccessibleWrapperAtk.h8
97 files changed, 9510 insertions, 4096 deletions
diff --git a/Source/WebCore/accessibility/AXObjectCache.cpp b/Source/WebCore/accessibility/AXObjectCache.cpp
index fec39c7fb..0ceedd7d4 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.
*
@@ -35,20 +35,23 @@
#include "AccessibilityARIAGrid.h"
#include "AccessibilityARIAGridCell.h"
#include "AccessibilityARIAGridRow.h"
+#include "AccessibilityAttachment.h"
#include "AccessibilityImageMapLink.h"
+#include "AccessibilityLabel.h"
#include "AccessibilityList.h"
#include "AccessibilityListBox.h"
#include "AccessibilityListBoxOption.h"
+#include "AccessibilityMathMLElement.h"
#include "AccessibilityMediaControls.h"
#include "AccessibilityMenuList.h"
#include "AccessibilityMenuListOption.h"
#include "AccessibilityMenuListPopup.h"
#include "AccessibilityProgressIndicator.h"
#include "AccessibilityRenderObject.h"
+#include "AccessibilitySVGElement.h"
#include "AccessibilitySVGRoot.h"
#include "AccessibilityScrollView.h"
#include "AccessibilityScrollbar.h"
-#include "AccessibilitySearchFieldButtons.h"
#include "AccessibilitySlider.h"
#include "AccessibilitySpinButton.h"
#include "AccessibilityTable.h"
@@ -56,37 +59,61 @@
#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"
#include "HTMLMeterElement.h"
#include "HTMLNames.h"
+#include "InlineElementBox.h"
+#include "MathMLElement.h"
#include "Page.h"
+#include "RenderAttachment.h"
+#include "RenderLineBreak.h"
#include "RenderListBox.h"
+#include "RenderMathMLOperator.h"
#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 "SVGElement.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"
#endif
+#if COMPILER(MSVC)
+// See https://msdn.microsoft.com/en-us/library/1wea5zwe.aspx
+#pragma warning(disable: 4701)
+#endif
+
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;
+static double AccessibilityFocusAriaModalNodeNotificationInterval = 0.050;
+
AccessibilityObjectInclusion AXComputedObjectAttributeCache::getIgnored(AXID id) const
{
HashMap<AXID, CachedAXObjectAttributes>::const_iterator it = m_idMapping.find(id);
@@ -104,7 +131,34 @@ void AXComputedObjectAttributeCache::setIgnored(AXID id, AccessibilityObjectIncl
m_idMapping.set(id, attributes);
}
}
-
+
+AccessibilityReplacedText::AccessibilityReplacedText(const VisibleSelection& selection)
+{
+ if (AXObjectCache::accessibilityEnabled()) {
+ m_replacedRange.startIndex.value = indexForVisiblePosition(selection.start(), m_replacedRange.startIndex.scope);
+ if (selection.isRange()) {
+ m_replacedText = AccessibilityObject::stringForVisiblePositionRange(selection);
+ m_replacedRange.endIndex.value = indexForVisiblePosition(selection.end(), m_replacedRange.endIndex.scope);
+ } else
+ m_replacedRange.endIndex = m_replacedRange.startIndex;
+ }
+}
+
+void AccessibilityReplacedText::postTextStateChangeNotification(AXObjectCache* cache, AXTextEditType type, const String& text, const VisibleSelection& selection)
+{
+ if (!cache)
+ return;
+ if (!AXObjectCache::accessibilityEnabled())
+ return;
+
+ VisiblePosition position = selection.start();
+ auto* node = highestEditableRoot(position.deepEquivalent(), HasEditableAXRole);
+ if (m_replacedText.length())
+ cache->postTextReplacementNotification(node, AXTextEditTypeDelete, m_replacedText, type, text, position);
+ else
+ cache->postTextStateChangeNotification(node, type, text, position);
+}
+
bool AXObjectCache::gAccessibilityEnabled = false;
bool AXObjectCache::gAccessibilityEnhancedUserInterfaceEnabled = false;
@@ -118,15 +172,33 @@ 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_notificationPostTimer(*this, &AXObjectCache::notificationPostTimerFired)
+ , m_passwordNotificationPostTimer(*this, &AXObjectCache::passwordNotificationPostTimerFired)
+ , m_liveRegionChangedPostTimer(*this, &AXObjectCache::liveRegionChangedNotificationPostTimerFired)
+ , m_focusAriaModalNodeTimer(*this, &AXObjectCache::focusAriaModalNodeTimerFired)
+ , m_currentAriaModalNode(nullptr)
{
+ findAriaModalNodes();
}
AXObjectCache::~AXObjectCache()
{
m_notificationPostTimer.stop();
+ m_liveRegionChangedPostTimer.stop();
+ m_focusAriaModalNodeTimer.stop();
for (const auto& object : m_objects.values()) {
detachWrapper(object.get(), CacheDestroyed);
@@ -135,46 +207,131 @@ AXObjectCache::~AXObjectCache()
}
}
+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->attributeWithoutSynchronization(aria_modalAttr), "true"))
+ continue;
+
+ m_ariaModalNodesSet.add(element);
+ }
+
+ // Set the current valid aria-modal node if possible.
+ updateCurrentAriaModalNode();
+}
+
+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)
{
// 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);
if (!axRenderImage)
- return 0;
+ return nullptr;
for (const auto& child : axRenderImage->children()) {
- if (!child->isImageMapLink())
+ if (!is<AccessibilityImageMapLink>(*child))
continue;
- if (toAccessibilityImageMapLink(child.get())->areaElement() == areaElement)
+ 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();
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())
@@ -191,12 +348,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);
}
@@ -204,12 +361,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);
}
@@ -217,7 +374,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));
@@ -230,14 +387,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);
}
@@ -246,13 +403,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;
- return equalIgnoringCase(toElement(node)->fastGetAttribute(roleAttr), role);
+ auto& roleValue = downcast<Element>(*node).attributeWithoutSynchronization(roleAttr);
+ if (role.isNull())
+ return roleValue.isEmpty();
+ if (roleValue.isEmpty())
+ return false;
+
+ 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();
@@ -264,62 +427,80 @@ 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 (node && is<HTMLLabelElement>(node) && nodeHasRole(node, nullAtom))
+ return AccessibilityLabel::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
-
- // Search field buttons
- if (node && node->isElementNode() && toElement(node)->isSearchFieldCancelButtonElement())
- return AccessibilitySearchFieldCancelButton::create(renderer);
- 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<SVGElement>(node))
+ return AccessibilitySVGElement::create(renderer);
+
+#if ENABLE(MATHML)
+ // The mfenced element creates anonymous RenderMathMLOperators which should be treated
+ // as MathML elements and assigned the MathElementRole so that platform logic regarding
+ // inclusion and role mapping is not bypassed.
+ bool isAnonymousOperator = renderer->isAnonymous() && is<RenderMathMLOperator>(*renderer);
+ if (isAnonymousOperator || is<MathMLElement>(node))
+ return AccessibilityMathMLElement::create(renderer, isAnonymousOperator);
+#endif
+
+ 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));
+ if (is<RenderProgress>(cssBox))
+ return AccessibilityProgressIndicator::create(&downcast<RenderProgress>(cssBox));
+
+#if ENABLE(ATTACHMENT_ELEMENT)
+ if (is<RenderAttachment>(cssBox))
+ return AccessibilityAttachment::create(&downcast<RenderAttachment>(cssBox));
#endif
#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);
}
@@ -327,16 +508,16 @@ 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(toScrollView(widget));
- else if (widget->isScrollbar())
- newObj = AccessibilityScrollbar::create(toScrollbar(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));
@@ -344,7 +525,7 @@ AccessibilityObject* AXObjectCache::getOrCreate(Widget* widget)
// Catch the case if an (unsupported) widget type is used. Only FrameView and ScrollBar are supported now.
ASSERT(newObj);
if (!newObj)
- return 0;
+ return nullptr;
getAXID(newObj.get());
@@ -358,7 +539,7 @@ AccessibilityObject* AXObjectCache::getOrCreate(Widget* widget)
AccessibilityObject* AXObjectCache::getOrCreate(Node* node)
{
if (!node)
- return 0;
+ return nullptr;
if (AccessibilityObject* obj = get(node))
return obj;
@@ -367,20 +548,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);
@@ -405,7 +591,7 @@ AccessibilityObject* AXObjectCache::getOrCreate(Node* node)
AccessibilityObject* AXObjectCache::getOrCreate(RenderObject* renderer)
{
if (!renderer)
- return 0;
+ return nullptr;
if (AccessibilityObject* obj = get(renderer))
return obj;
@@ -433,7 +619,7 @@ AccessibilityObject* AXObjectCache::getOrCreate(RenderObject* renderer)
AccessibilityObject* AXObjectCache::rootObject()
{
if (!gAccessibilityEnabled)
- return 0;
+ return nullptr;
return getOrCreate(m_document.view());
}
@@ -441,16 +627,16 @@ AccessibilityObject* AXObjectCache::rootObject()
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) {
@@ -482,13 +668,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();
@@ -525,6 +711,8 @@ void AXObjectCache::remove(RenderObject* renderer)
AXID axID = m_renderObjectMapping.get(renderer);
remove(axID);
m_renderObjectMapping.remove(renderer);
+ if (is<RenderBlock>(*renderer))
+ m_deferredIsIgnoredChangeList.remove(downcast<RenderBlock>(renderer));
}
void AXObjectCache::remove(Node* node)
@@ -539,6 +727,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;
@@ -556,7 +750,7 @@ void AXObjectCache::remove(Widget* view)
}
-#if !PLATFORM(WIN) || OS(WINCE)
+#if !PLATFORM(WIN)
AXID AXObjectCache::platformGenerateAXID() const
{
static AXID lastUsedID = 0;
@@ -641,8 +835,30 @@ void AXObjectCache::handleMenuOpened(Node* node)
postNotification(getOrCreate(node), &document(), AXMenuOpened);
}
-void AXObjectCache::childrenChanged(Node* node)
+void AXObjectCache::handleLiveRegionCreated(Node* node)
+{
+ if (!is<Element>(node) || !node->renderer())
+ return;
+
+ Element* element = downcast<Element>(node);
+ String liveRegionStatus = element->attributeWithoutSynchronization(aria_liveAttr);
+ if (liveRegionStatus.isEmpty()) {
+ const AtomicString& ariaRole = element->attributeWithoutSynchronization(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));
}
@@ -650,8 +866,11 @@ void AXObjectCache::childrenChanged(RenderObject* renderer, RenderObject* newChi
{
if (!renderer)
return;
- if (newChild)
+
+ if (newChild) {
handleMenuOpened(newChild->node());
+ handleLiveRegionCreated(newChild->node());
+ }
childrenChanged(get(renderer));
}
@@ -664,15 +883,14 @@ void AXObjectCache::childrenChanged(AccessibilityObject* obj)
obj->childrenChanged();
}
-void AXObjectCache::notificationPostTimerFired(Timer<AXObjectCache>&)
+void AXObjectCache::notificationPostTimerFired()
{
Ref<Document> protectorForCacheOwner(m_document);
m_notificationPostTimer.stop();
- // In DRT, posting notifications has a tendency to immediately queue up other notifications, which can lead to unexpected behavior
+ // 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 = m_notificationsToPost;
- m_notificationsToPost.clear();
+ auto notifications = WTFMove(m_notificationsToPost);
for (const auto& note : notifications) {
AccessibilityObject* obj = note.first.get();
@@ -685,21 +903,42 @@ void AXObjectCache::notificationPostTimerFired(Timer<AXObjectCache>&)
#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 = toAccessibilityRenderObject(obj);
- RenderObject* renderer = renderObj->renderer();
- if (renderer)
+ 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;
+ }
+
postPlatformNotification(obj, notification);
if (notification == AXChildrenChanged && obj->parentObjectIfExists() && obj->lastKnownIsIgnoredValue() != obj->accessibilityIsIgnored())
childrenChanged(obj->parentObject());
}
}
+
+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, PostTarget postTarget, PostType postType)
{
@@ -777,7 +1016,7 @@ void AXObjectCache::handleMenuItemSelected(Node* node)
if (!nodeHasRole(node, "menuitem") && !nodeHasRole(node, "menuitemradio") && !nodeHasRole(node, "menuitemcheckbox"))
return;
- if (!toElement(node)->focused() && !equalIgnoringCase(toElement(node)->fastGetAttribute(aria_selectedAttr), "true"))
+ if (!downcast<Element>(*node).focused() && !equalLettersIgnoringASCIICase(downcast<Element>(*node).attributeWithoutSynchronization(aria_selectedAttr), "true"))
return;
postNotification(getOrCreate(node), &document(), AXMenuListItemSelected);
@@ -808,16 +1047,273 @@ void AXObjectCache::selectedChildrenChanged(RenderObject* renderer)
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;
+ if (type == AXTextEditTypeUnknown)
+ return;
+
+ 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;
+ if (deletionType != AXTextEditTypeDelete)
+ return;
+ if (!(insertionType == AXTextEditTypeInsert || insertionType == AXTextEditTypeTyping || insertionType == AXTextEditTypeDictation || insertionType == AXTextEditTypePaste))
+ return;
+
+ 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)
@@ -834,6 +1330,67 @@ 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();
+}
+
+static AccessibilityObject* firstFocusableChild(AccessibilityObject* obj)
+{
+ if (!obj)
+ return nullptr;
+
+ for (auto* child = obj->firstChild(); child; child = child->nextSibling()) {
+ if (child->canSetFocusAttribute())
+ return child;
+ if (AccessibilityObject* focusable = firstFocusableChild(child))
+ return focusable;
+ }
+ return nullptr;
+}
+
+void AXObjectCache::focusAriaModalNode()
+{
+ if (m_focusAriaModalNodeTimer.isActive())
+ m_focusAriaModalNodeTimer.stop();
+
+ m_focusAriaModalNodeTimer.startOneShot(AccessibilityFocusAriaModalNodeNotificationInterval);
+}
+
+void AXObjectCache::focusAriaModalNodeTimerFired()
+{
+ if (!m_currentAriaModalNode)
+ return;
+
+ // Don't set focus if we are already focusing onto some element within
+ // the dialog.
+ if (m_currentAriaModalNode->contains(document().focusedElement()))
+ return;
+
+ if (AccessibilityObject* currentAriaModalNodeObject = getOrCreate(m_currentAriaModalNode)) {
+ if (AccessibilityObject* focusable = firstFocusableChild(currentAriaModalNodeObject))
+ focusable->setFocused(true);
+ }
+}
+
void AXObjectCache::handleScrollbarUpdate(ScrollView* view)
{
if (!view)
@@ -874,7 +1431,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-"))
@@ -895,17 +1452,44 @@ 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);
+ else if (attrName == aria_modalAttr)
+ handleAriaModalChange(element);
else
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).attributeWithoutSynchronization(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();
+ }
+ if (m_currentAriaModalNode)
+ focusAriaModalNode();
+
+ 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);
}
@@ -944,11 +1528,555 @@ VisiblePosition AXObjectCache::visiblePositionForTextMarkerData(TextMarkerData&
AXObjectCache* cache = renderer->document().axObjectCache();
if (!cache->isIDinUse(textMarkerData.axID))
return VisiblePosition();
+
+ return visiblePos;
+}
+
+CharacterOffset AXObjectCache::characterOffsetForTextMarkerData(TextMarkerData& textMarkerData)
+{
+ if (!isNodeInUse(textMarkerData.node))
+ return CharacterOffset();
+
+ if (textMarkerData.ignored)
+ return CharacterOffset();
+
+ CharacterOffset result = CharacterOffset(textMarkerData.node, textMarkerData.characterStartIndex, textMarkerData.characterOffset);
+ // When we are at a line wrap and the VisiblePosition is upstream, it means the text marker is at the end of the previous line.
+ // We use the previous CharacterOffset so that it will match the Range.
+ if (textMarkerData.affinity == UPSTREAM)
+ return previousCharacterOffset(result, false);
+ return result;
+}
+
+CharacterOffset AXObjectCache::traverseToOffsetInRange(RefPtr<Range>range, int offset, TraverseOption option, bool stayWithinRange)
+{
+ if (!range)
+ return CharacterOffset();
+
+ bool toNodeEnd = option & TraverseOptionToNodeEnd;
+ bool validateOffset = option & TraverseOptionValidateOffset;
+
+ int offsetInCharacter = 0;
+ int cumulativeOffset = 0;
+ int remaining = 0;
+ int lastLength = 0;
+ Node* currentNode = nullptr;
+ bool finished = false;
+ int lastStartOffset = 0;
+
+ TextIterator iterator(range.get(), TextIteratorEntersTextControls);
+
+ // 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()))
+ cumulativeOffset++;
+ lastLength = cumulativeOffset;
+
+ // 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, option);
+ 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)) {
+ cumulativeOffset++;
+ 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->isShadowRoot()) {
+ // Since we are entering text controls, we should set the currentNode
+ // to be the shadow host when there's no content.
+ currentNode = currentNode->shadowHost();
+ continue;
+ } else if (currentNode != previousNode) {
+ // We should set the start offset and length for the current node in case this is the last iteration.
+ lastStartOffset = 1;
+ lastLength = 0;
+ continue;
+ }
+ }
+ }
+ cumulativeOffset += currentLength;
+ }
+
+ if (currentNode == previousNode) {
+ lastLength += currentLength;
+ lastStartOffset = iterator.range()->endOffset() - lastLength;
+ }
+ else {
+ lastLength = currentLength;
+ lastStartOffset = hasReplacedNodeOrBR ? 0 : iterator.range()->startOffset();
+ }
+
+ // Break early if we have advanced enough characters.
+ bool offsetLimitReached = validateOffset ? cumulativeOffset + lastStartOffset >= offset : cumulativeOffset >= offset;
+ if (!toNodeEnd && offsetLimitReached) {
+ offsetInCharacter = validateOffset ? std::max(offset - lastStartOffset, 0) : offset - (cumulativeOffset - lastLength);
+ finished = true;
+ break;
+ }
+ previousNode = currentNode;
+ }
+
+ if (!finished) {
+ offsetInCharacter = lastLength;
+ if (!toNodeEnd)
+ remaining = offset - cumulativeOffset;
+ }
+
+ // Sometimes when we are getting the end CharacterOffset of a line range, the TextIterator will emit an extra space at the end
+ // and make the character count greater than the Range's end offset.
+ if (toNodeEnd && currentNode->isTextNode() && currentNode == &range->endContainer() && static_cast<int>(range->endOffset()) < lastStartOffset + offsetInCharacter)
+ offsetInCharacter = range->endOffset() - lastStartOffset;
+
+ 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;
+ auto range = Range::create(*document);
+ if (AccessibilityObject::replacedNodeNeedsCharacter(node)) {
+ // For replaced nodes without children, the node itself is included in the range.
+ if (range->selectNode(*node).hasException())
+ return nullptr;
+ } else {
+ if (range->selectNodeContents(*node).hasException())
+ return nullptr;
+ }
+ return WTFMove(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->hasChildNodes())
+ node1 = node1->traverseToChildAt(characterOffset1.offset);
+ if (!node2->offsetInCharacters() && !isReplacedNodeOrBR(node2) && node2->hasChildNodes())
+ node2 = node2->traverseToChildAt(characterOffset2.offset);
+
+ if (!node1 || !node2)
+ return false;
+
+ RefPtr<Range> range1 = AXObjectCache::rangeForNodeContents(node1);
+ RefPtr<Range> range2 = AXObjectCache::rangeForNodeContents(node2);
+
+ if (!range2)
+ return true;
+ if (!range1)
+ return false;
+ auto result = range1->compareBoundaryPoints(Range::START_TO_START, *range2);
+ if (result.hasException())
+ return true;
+ return result.releaseReturnValue() <= 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 bool setRangeStartOrEndWithCharacterOffset(Range& range, const CharacterOffset& characterOffset, bool isStart)
+{
+ if (characterOffset.isNull())
+ return false;
+
+ int offset = characterOffset.startIndex + characterOffset.offset;
+ Node* node = characterOffset.node;
+ ASSERT(node);
+
+ bool replacedNodeOrBR = isReplacedNodeOrBR(node);
+ // For the non text node that has no children, we should create the range with its parent, otherwise the range would be collapsed.
+ // Example: <div contenteditable="true"></div>, we want the range to include the div element.
+ bool noChildren = !replacedNodeOrBR && !node->isTextNode() && !node->hasChildNodes();
+ int characterCount = noChildren ? (isStart ? 0 : 1) : characterOffset.offset;
- if (deepPos.deprecatedNode() != textMarkerData.node || deepPos.deprecatedEditingOffset() != textMarkerData.offset)
+ if (replacedNodeOrBR || noChildren)
+ node = resetNodeAndOffsetForReplacedNode(node, offset, characterCount);
+
+ if (!node)
+ return false;
+
+ if (isStart) {
+ if (range.setStart(*node, offset).hasException())
+ return false;
+ } else {
+ if (range.setEnd(*node, offset).hasException())
+ return false;
+ }
+
+ return true;
+}
+
+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;
+
+ auto result = Range::create(m_document);
+ if (!setRangeStartOrEndWithCharacterOffset(result, startCharacterOffset, true))
+ return nullptr;
+ if (!setRangeStartOrEndWithCharacterOffset(result, endCharacterOffset, false))
+ return nullptr;
+ return WTFMove(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();
+
+ // When getting the end CharacterOffset at node boundary, we don't want to collapse to the previous node.
+ if (!isStart && !range->endOffset())
+ return characterOffsetForNodeAndOffset(range->endContainer(), 0, TraverseOptionIncludeStart);
+
+ // If it's end text marker, we want to go to the end of the range, and stay within the range.
+ bool stayWithinRange = !isStart;
+
+ Node& endNode = range->endContainer();
+ if (endNode.offsetInCharacters() && !isStart)
+ return traverseToOffsetInRange(rangeForNodeContents(&endNode), range->endOffset(), TraverseOptionValidateOffset);
+
+ Ref<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()) {
+ CharacterOffset nodeStartOffset = traverseToOffsetInRange(rangeForNodeContents(&node), range->startOffset(), TraverseOptionValidateOffset);
+ if (isStart)
+ return nodeStartOffset;
+ copyRange = Range::create(range->ownerDocument(), &range->startContainer(), 0, &range->endContainer(), range->endOffset());
+ offset += nodeStartOffset.offset;
+ }
+
+ return traverseToOffsetInRange(WTFMove(copyRange), offset, isStart ? TraverseOptionDefault : TraverseOptionToNodeEnd, 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, option);
+ while (!characterOffset.isNull() && characterOffset.remaining() && !toNodeEnd) {
+ domNode = nextNode(domNode);
+ if (!domNode)
+ return CharacterOffset();
+ range = rangeForNodeContents(domNode);
+ characterOffset = traverseToOffsetInRange(range, characterOffset.remaining(), option);
+ }
+
+ return characterOffset;
+}
+
+void AXObjectCache::textMarkerDataForCharacterOffset(TextMarkerData& textMarkerData, const CharacterOffset& characterOffset)
+{
+ memset(&textMarkerData, 0, sizeof(TextMarkerData));
+ setTextMarkerDataWithCharacterOffset(textMarkerData, characterOffset);
+}
+
+bool AXObjectCache::shouldSkipBoundary(const CharacterOffset& previous, const CharacterOffset& next)
+{
+ // Match the behavior of VisiblePosition, we should skip the node boundary when there's no visual space or new line character.
+ if (previous.isNull() || next.isNull())
+ return false;
+
+ if (previous.node == next.node)
+ return false;
+
+ if (next.startIndex > 0 || next.offset > 0)
+ return false;
+
+ CharacterOffset newLine = startCharacterOffsetOfLine(next);
+ if (next.isEqual(newLine))
+ return false;
+
+ return true;
+}
+
+void AXObjectCache::textMarkerDataForNextCharacterOffset(TextMarkerData& textMarkerData, const CharacterOffset& characterOffset)
+{
+ CharacterOffset next = characterOffset;
+ CharacterOffset previous = characterOffset;
+ bool shouldContinue;
+ do {
+ shouldContinue = false;
+ next = nextCharacterOffset(next, false);
+ if (shouldSkipBoundary(previous, next))
+ next = nextCharacterOffset(next, false);
+ textMarkerDataForCharacterOffset(textMarkerData, next);
+
+ // We should skip next CharactetOffset if it's visually the same.
+ if (!lengthForRange(rangeForUnorderedCharacterOffsets(previous, next).get()))
+ shouldContinue = true;
+ previous = next;
+ } while (textMarkerData.ignored || shouldContinue);
+}
+
+void AXObjectCache::textMarkerDataForPreviousCharacterOffset(TextMarkerData& textMarkerData, const CharacterOffset& characterOffset)
+{
+ CharacterOffset previous = characterOffset;
+ CharacterOffset next = characterOffset;
+ bool shouldContinue;
+ do {
+ shouldContinue = false;
+ previous = previousCharacterOffset(previous, false);
+ textMarkerDataForCharacterOffset(textMarkerData, previous);
+
+ // We should skip previous CharactetOffset if it's visually the same.
+ if (!lengthForRange(rangeForUnorderedCharacterOffsets(previous, next).get()))
+ shouldContinue = true;
+ next = previous;
+ } while (textMarkerData.ignored || shouldContinue);
+}
+
+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();
- return visiblePos;
+ // Create a collapsed range and use that to form a VisiblePosition, so that the case with
+ // composed characters will be covered.
+ auto range = rangeForUnorderedCharacterOffsets(characterOffset, characterOffset);
+ return range ? VisiblePosition(range->startPosition()) : VisiblePosition();
+}
+
+CharacterOffset AXObjectCache::characterOffsetFromVisiblePosition(const VisiblePosition& visiblePos)
+{
+ if (visiblePos.isNull())
+ return CharacterOffset();
+
+ Position deepPos = visiblePos.deepEquivalent();
+ Node* domNode = deepPos.deprecatedNode();
+ ASSERT(domNode);
+
+ if (domNode->offsetInCharacters())
+ return traverseToOffsetInRange(rangeForNodeContents(domNode), deepPos.deprecatedEditingOffset(), TraverseOptionValidateOffset);
+
+ 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 visiblePositionRange = obj->visiblePositionRange();
+ VisiblePosition visiblePosition = visiblePositionRange.start;
+ int characterOffset = 0;
+ Position currentPosition = visiblePosition.deepEquivalent();
+
+ VisiblePosition previousVisiblePos;
+ while (!currentPosition.isNull() && !deepPos.equals(currentPosition)) {
+ previousVisiblePos = visiblePosition;
+ visiblePosition = obj->nextVisiblePosition(visiblePosition);
+ currentPosition = visiblePosition.deepEquivalent();
+ Position previousPosition = previousVisiblePos.deepEquivalent();
+ // Sometimes nextVisiblePosition will give the same VisiblePostion,
+ // we break here to avoid infinite loop.
+ if (currentPosition.equals(previousPosition))
+ break;
+ characterOffset++;
+
+ // When VisiblePostion moves to next node, it will count the leading line break as
+ // 1 offset, which we shouldn't include in CharacterOffset.
+ if (currentPosition.deprecatedNode() != previousPosition.deprecatedNode()) {
+ if (visiblePosition.characterBefore() == '\n')
+ characterOffset--;
+ } else {
+ // Sometimes VisiblePosition will move multiple characters, like emoji.
+ if (currentPosition.deprecatedNode()->offsetInCharacters())
+ characterOffset += currentPosition.offsetInContainerNode() - previousPosition.offsetInContainerNode() - 1;
+ }
+ }
+
+ // Sometimes when the node is a replaced node and is ignored in accessibility, we get a wrong CharacterOffset from it.
+ CharacterOffset result = traverseToOffsetInRange(rangeForNodeContents(obj->node()), characterOffset);
+ if (result.remainingOffset > 0 && !result.isNull() && isRendererReplacedElement(result.node->renderer()))
+ result.offset += result.remainingOffset;
+ return result;
+}
+
+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)
@@ -966,10 +2094,15 @@ 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;
+
+ // If the visible position has an anchor type referring to a node other than the anchored node, we should
+ // set the text marker data with CharacterOffset so that the offset will correspond to the node.
+ CharacterOffset characterOffset = characterOffsetFromVisiblePosition(visiblePos);
+ if (deepPos.anchorType() == Position::PositionIsAfterAnchor || deepPos.anchorType() == Position::PositionIsAfterChildren) {
+ textMarkerDataForCharacterOffset(textMarkerData, characterOffset);
+ return;
}
// find or create an accessibility object for this node
@@ -979,15 +2112,535 @@ void AXObjectCache::textMarkerDataForVisiblePosition(TextMarkerData& textMarkerD
textMarkerData.axID = obj.get()->axObjectID();
textMarkerData.node = domNode;
textMarkerData.offset = deepPos.deprecatedEditingOffset();
- textMarkerData.affinity = visiblePos.affinity();
+ textMarkerData.affinity = visiblePos.affinity();
+
+ textMarkerData.characterOffset = characterOffset.offset;
+ textMarkerData.characterStartIndex = characterOffset.startIndex;
cache->setNodeInUse(domNode);
}
+CharacterOffset AXObjectCache::nextCharacterOffset(const CharacterOffset& characterOffset, bool ignoreNextNodeStart)
+{
+ if (characterOffset.isNull())
+ return CharacterOffset();
+
+ // We don't always move one 'character' at a time since there might be composed characters.
+ int nextOffset = Position::uncheckedNextOffset(characterOffset.node, characterOffset.offset);
+ CharacterOffset next = characterOffsetForNodeAndOffset(*characterOffset.node, nextOffset);
+
+ // To be consistent with VisiblePosition, we should consider the case that current node end to next node start counts 1 offset.
+ if (!ignoreNextNodeStart && !next.isNull() && !isReplacedNodeOrBR(next.node) && next.node != characterOffset.node) {
+ int length = TextIterator::rangeLength(rangeForUnorderedCharacterOffsets(characterOffset, next).get());
+ if (length > nextOffset - characterOffset.offset)
+ next = characterOffsetForNodeAndOffset(*next.node, 0, TraverseOptionIncludeStart);
+ }
+
+ return next;
+}
+
+CharacterOffset AXObjectCache::previousCharacterOffset(const CharacterOffset& characterOffset, bool ignorePreviousNodeEnd)
+{
+ if (characterOffset.isNull())
+ return CharacterOffset();
+
+ // To be consistent with VisiblePosition, we should consider the case that current node start to previous node end counts 1 offset.
+ if (!ignorePreviousNodeEnd && !characterOffset.offset)
+ return characterOffsetForNodeAndOffset(*characterOffset.node, 0);
+
+ // We don't always move one 'character' a time since there might be composed characters.
+ int previousOffset = Position::uncheckedPreviousOffset(characterOffset.node, characterOffset.offset);
+ return characterOffsetForNodeAndOffset(*characterOffset.node, previousOffset, 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;
+
+ // We should consider the node boundary that splits words. Otherwise VoiceOver won't see it as space.
+ c = nextCharacterOffset(characterOffset, false);
+ if (shouldSkipBoundary(characterOffset, c))
+ c = nextCharacterOffset(c, false);
+ 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);
+ if (c.isNull())
+ return characterOffset;
+ } else {
+ CharacterOffset endOfParagraph = endCharacterOffsetOfParagraph(characterOffset);
+ if (characterOffset.isEqual(endOfParagraph))
+ return characterOffset;
+ }
+
+ return nextBoundary(c, endWordBoundary);
+}
+
+CharacterOffset AXObjectCache::previousWordStartCharacterOffset(const CharacterOffset& characterOffset)
+{
+ if (characterOffset.isNull())
+ return CharacterOffset();
+
+ CharacterOffset previousOffset = previousCharacterOffset(characterOffset);
+ 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, RightWordIfOnBoundary);
+ 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 { };
+
+ Node* boundary = parentEditingBoundary(characterOffset.node);
+ if (!boundary)
+ return { };
+
+ RefPtr<Range> searchRange = rangeForNodeContents(boundary);
+ if (!searchRange)
+ return { };
+
+ Vector<UChar, 1024> string;
+ unsigned prefixLength = 0;
+
+ if (requiresContextForWordBoundary(characterAfter(characterOffset))) {
+ auto backwardsScanRange = boundary->document().createRange();
+ if (!setRangeStartOrEndWithCharacterOffset(backwardsScanRange, characterOffset, false))
+ return { };
+ prefixLength = prefixLengthForRange(backwardsScanRange, string);
+ }
+
+ if (!setRangeStartOrEndWithCharacterOffset(*searchRange, characterOffset, true))
+ return { };
+ CharacterOffset end = startOrEndCharacterOffsetForRange(searchRange, false);
+
+ TextIterator it(searchRange.get(), TextIteratorEmitsObjectReplacementCharacters);
+ unsigned next = forwardSearchForBoundaryWithTextIterator(it, string, prefixLength, searchFunction);
+
+ if (it.atEnd() && next == string.size())
+ return end;
+
+ // We should consider the node boundary that splits words.
+ if (searchFunction == endWordBoundary && next - prefixLength == 1)
+ return nextCharacterOffset(characterOffset, false);
+
+ // 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;
+
+ if (requiresContextForWordBoundary(characterBefore(characterOffset))) {
+ auto forwardsScanRange = boundary->document().createRange();
+ if (forwardsScanRange->setEndAfter(*boundary).hasException())
+ return { };
+ if (!setRangeStartOrEndWithCharacterOffset(forwardsScanRange, characterOffset, true))
+ return { };
+ suffixLength = suffixLengthForRange(forwardsScanRange, string);
+ }
+
+ if (!setRangeStartOrEndWithCharacterOffset(*searchRange, characterOffset, false))
+ return { };
+ CharacterOffset start = startOrEndCharacterOffsetForRange(searchRange, true);
+
+ 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();
+
+ // SimplifiedBackwardsTextIterator ignores replaced elements.
+ if (AccessibilityObject::replacedNodeNeedsCharacter(characterOffset.node))
+ return characterOffsetForNodeAndOffset(*characterOffset.node, 0);
+ Node* nextSibling = node.nextSibling();
+ if (&node != characterOffset.node && AccessibilityObject::replacedNodeNeedsCharacter(nextSibling))
+ return startOrEndCharacterOffsetForRange(rangeForNodeContents(nextSibling), false);
+
+ 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.isTextNode())
+ return traverseToOffsetInRange(rangeForNodeContents(&node), next, TraverseOptionValidateOffset);
+ 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() - suffixLength - 1] == '\n'))
+ characterCount = 0;
+ return characterOffsetForNodeAndOffset(*characterOffset.node, characterCount, TraverseOptionIncludeStart);
+}
+
+CharacterOffset AXObjectCache::startCharacterOffsetOfParagraph(const CharacterOffset& characterOffset, EditingBoundaryCrossingRule boundaryCrossingRule)
+{
+ if (characterOffset.isNull())
+ return CharacterOffset();
+
+ auto* startNode = characterOffset.node;
+
+ if (isRenderedAsNonInlineTableImageOrHR(startNode))
+ return startOrEndCharacterOffsetForRange(rangeForNodeContents(startNode), true);
+
+ auto* startBlock = enclosingBlock(startNode);
+ int offset = characterOffset.startIndex + characterOffset.offset;
+ auto* highestRoot = highestEditableRoot(firstPositionInOrBeforeNode(startNode));
+ Position::AnchorType type = Position::PositionIsOffsetInAnchor;
+
+ auto* 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;
+ Node* highestRoot = highestEditableRoot(firstPositionInOrBeforeNode(startNode));
+ 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);
+
+ // We should skip the preceding BR node.
+ if (characterOffsetNodeIsBR(previous) && !characterOffsetNodeIsBR(characterOffset))
+ previous = previousCharacterOffset(previous);
+
+ 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);
+
+ // We should skip the preceding BR node.
+ if (characterOffsetNodeIsBR(previous) && !characterOffsetNodeIsBR(characterOffset))
+ previous = previousCharacterOffset(previous);
+
+ return startCharacterOffsetOfSentence(previous);
+}
+
+LayoutRect AXObjectCache::localCaretRectForCharacterOffset(RenderObject*& renderer, const CharacterOffset& characterOffset)
+{
+ if (characterOffset.isNull()) {
+ renderer = nullptr;
+ return IntRect();
+ }
+
+ Node* node = characterOffset.node;
+
+ renderer = node->renderer();
+ if (!renderer)
+ return LayoutRect();
+
+ InlineBox* inlineBox = nullptr;
+ int caretOffset;
+ // Use a collapsed range to get the position.
+ RefPtr<Range> range = rangeForUnorderedCharacterOffsets(characterOffset, characterOffset);
+ if (!range)
+ return IntRect();
+
+ Position startPosition = range->startPosition();
+ startPosition.getInlineBoxAndOffset(DOWNSTREAM, inlineBox, caretOffset);
+
+ if (inlineBox)
+ renderer = &inlineBox->renderer();
+
+ if (is<RenderLineBreak>(renderer) && downcast<RenderLineBreak>(renderer)->inlineBoxWrapper() != inlineBox)
+ return IntRect();
+
+ return renderer->localCaretRect(inlineBox, caretOffset);
+}
+
+IntRect AXObjectCache::absoluteCaretBoundsForCharacterOffset(const CharacterOffset& characterOffset)
+{
+ RenderBlock* caretPainter = nullptr;
+
+ // First compute a rect local to the renderer at the selection start.
+ RenderObject* renderer = nullptr;
+ LayoutRect localRect = localCaretRectForCharacterOffset(renderer, characterOffset);
+
+ localRect = localCaretRectInRendererForRect(localRect, characterOffset.node, renderer, caretPainter);
+ return absoluteBoundsForLocalCaretRect(caretPainter, localRect);
+}
+
+CharacterOffset AXObjectCache::characterOffsetForPoint(const IntPoint &point, AccessibilityObject* obj)
+{
+ if (!obj)
+ return CharacterOffset();
+
+ VisiblePosition vp = obj->visiblePositionForPoint(point);
+ RefPtr<Range> range = makeRange(vp, vp);
+ return startOrEndCharacterOffsetForRange(range, true);
+}
+
+CharacterOffset AXObjectCache::characterOffsetForPoint(const IntPoint &point)
+{
+ RefPtr<Range> caretRange = m_document.caretRangeFromPoint(LayoutPoint(point));
+ return startOrEndCharacterOffsetForRange(caretRange, true);
+}
+
+CharacterOffset AXObjectCache::characterOffsetForBounds(const IntRect& rect, bool first)
+{
+ if (rect.isEmpty())
+ return CharacterOffset();
+
+ IntPoint corner = first ? rect.minXMinYCorner() : rect.maxXMaxYCorner();
+ CharacterOffset characterOffset = characterOffsetForPoint(corner);
+
+ if (rect.contains(absoluteCaretBoundsForCharacterOffset(characterOffset).center()))
+ return characterOffset;
+
+ // If the initial position is located outside the bounds adjust it incrementally as needed.
+ CharacterOffset nextCharOffset = nextCharacterOffset(characterOffset, false);
+ CharacterOffset previousCharOffset = previousCharacterOffset(characterOffset, false);
+ while (!nextCharOffset.isNull() || !previousCharOffset.isNull()) {
+ if (rect.contains(absoluteCaretBoundsForCharacterOffset(nextCharOffset).center()))
+ return nextCharOffset;
+ if (rect.contains(absoluteCaretBoundsForCharacterOffset(previousCharOffset).center()))
+ return previousCharOffset;
+
+ nextCharOffset = nextCharacterOffset(nextCharOffset, false);
+ previousCharOffset = previousCharacterOffset(previousCharOffset, false);
+ }
+
+ return CharacterOffset();
+}
+
+// FIXME: Remove VisiblePosition code after implementing this using CharacterOffset.
+CharacterOffset AXObjectCache::endCharacterOffsetOfLine(const CharacterOffset& characterOffset)
+{
+ if (characterOffset.isNull())
+ return CharacterOffset();
+
+ VisiblePosition vp = visiblePositionFromCharacterOffset(characterOffset);
+ VisiblePosition endLine = endOfLine(vp);
+
+ return characterOffsetFromVisiblePosition(endLine);
+}
+
+CharacterOffset AXObjectCache::startCharacterOffsetOfLine(const CharacterOffset& characterOffset)
+{
+ if (characterOffset.isNull())
+ return CharacterOffset();
+
+ VisiblePosition vp = visiblePositionFromCharacterOffset(characterOffset);
+ VisiblePosition startLine = startOfLine(vp);
+
+ return characterOffsetFromVisiblePosition(startLine);
+}
+
+CharacterOffset AXObjectCache::characterOffsetForIndex(int index, const AccessibilityObject* obj)
+{
+ if (!obj)
+ return CharacterOffset();
+
+ RefPtr<Range> range = obj->elementRange();
+ CharacterOffset start = startOrEndCharacterOffsetForRange(range, true);
+ CharacterOffset end = startOrEndCharacterOffsetForRange(range, false);
+ CharacterOffset result = start;
+ for (int i = 0; i < index; i++) {
+ result = nextCharacterOffset(result, false);
+ if (result.isEqual(end))
+ break;
+ }
+ return result;
+}
+
+int AXObjectCache::indexForCharacterOffset(const CharacterOffset& characterOffset, AccessibilityObject* obj)
+{
+ // Create a collapsed range so that we can get the VisiblePosition from it.
+ RefPtr<Range> range = rangeForUnorderedCharacterOffsets(characterOffset, characterOffset);
+ if (!range)
+ return 0;
+ VisiblePosition vp = range->startPosition();
+ return obj->indexForVisiblePosition(vp);
+}
+
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))
@@ -999,20 +2652,18 @@ const Element* AXObjectCache::rootAXEditableElement(const Node* node)
void AXObjectCache::clearTextMarkerNodesInUse(Document* document)
{
- HashSet<Node*>::iterator it = m_textMarkerNodes.begin();
- HashSet<Node*>::iterator end = m_textMarkerNodes.end();
-
- // Check each node to see if it's inside the document being deleted.
+ 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 (; it != end; ++it) {
- if (&(*it)->document() == document)
- nodesToDelete.add(*it);
+ for (const auto& node : m_textMarkerNodes) {
+ if (!node->isConnected() || &(node)->document() == document)
+ nodesToDelete.add(node);
}
- it = nodesToDelete.begin();
- end = nodesToDelete.end();
- for (; it != end; ++it)
- m_textMarkerNodes.remove(*it);
+ for (const auto& node : nodesToDelete)
+ m_textMarkerNodes.remove(node);
}
bool AXObjectCache::nodeIsTextControl(const Node* node)
@@ -1024,24 +2675,59 @@ bool AXObjectCache::nodeIsTextControl(const Node* node)
return axObject && axObject->isTextControl();
}
+void AXObjectCache::performDeferredIsIgnoredChange()
+{
+ for (auto* renderer : m_deferredIsIgnoredChangeList)
+ recomputeIsIgnored(renderer);
+ m_deferredIsIgnoredChangeList.clear();
+}
+
+void AXObjectCache::recomputeDeferredIsIgnored(RenderBlock& renderer)
+{
+ if (renderer.beingDestroyed())
+ return;
+ m_deferredIsIgnoredChangeList.add(&renderer);
+}
+
bool isNodeAriaVisible(Node* node)
{
if (!node)
return false;
-
- // To determine if a node is ARIA visible, we need to check the parent hierarchy to see if anyone specifies
- // aria-hidden explicitly.
+
+ // 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 (testNode->isElementNode()) {
- const AtomicString& ariaHiddenValue = toElement(testNode)->fastGetAttribute(aria_hiddenAttr);
- if (equalIgnoringCase(ariaHiddenValue, "false"))
- return true;
- if (equalIgnoringCase(ariaHiddenValue, "true"))
+ if (is<Element>(*testNode)) {
+ const AtomicString& ariaHiddenValue = downcast<Element>(*testNode).attributeWithoutSynchronization(aria_hiddenAttr);
+ if (equalLettersIgnoringASCIICase(ariaHiddenValue, "true"))
+ return false;
+
+ bool ariaHiddenFalse = equalLettersIgnoringASCIICase(ariaHiddenValue, "false");
+ if (!testNode->renderer() && !ariaHiddenFalse)
return false;
+ if (!ariaHiddenFalsePresent && ariaHiddenFalse)
+ ariaHiddenFalsePresent = true;
+ // We should break early when it gets to a rendered object.
+ if (testNode->renderer())
+ break;
}
}
- return 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)
@@ -1056,6 +2742,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 cf5fd5795..4f2bc6d9c 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
@@ -23,15 +23,18 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AXObjectCache_h
-#define AXObjectCache_h
+#pragma once
+#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 {
@@ -40,6 +43,7 @@ class Document;
class HTMLAreaElement;
class Node;
class Page;
+class RenderBlock;
class RenderObject;
class ScrollView;
class VisiblePosition;
@@ -49,9 +53,35 @@ 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:
AccessibilityObjectInclusion getIgnored(AXID) const;
@@ -67,6 +97,32 @@ private:
HashMap<AXID, CachedAXObjectAttributes> m_idMapping;
};
+struct VisiblePositionIndex {
+ int value = -1;
+ RefPtr<ContainerNode> scope;
+};
+
+struct VisiblePositionIndexRange {
+ VisiblePositionIndex startIndex;
+ VisiblePositionIndex endIndex;
+ bool isNull() const { return startIndex.value == -1 || endIndex.value == -1; }
+};
+
+class AccessibilityReplacedText {
+public:
+ AccessibilityReplacedText() { }
+ AccessibilityReplacedText(const VisibleSelection&);
+ void postTextStateChangeNotification(AXObjectCache*, AXTextEditType, const String&, const VisibleSelection&);
+ const VisiblePositionIndexRange& replacedRange() { return m_replacedRange; }
+protected:
+ String m_replacedText;
+ VisiblePositionIndexRange m_replacedRange;
+};
+
+#if !PLATFORM(COCOA)
+enum AXTextChange { AXTextInserted, AXTextDeleted, AXTextAttributesChanged };
+#endif
+
enum PostTarget { TargetElement, TargetObservableParent };
enum PostType { PostSynchronously, PostAsynchronously };
@@ -77,12 +133,12 @@ public:
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,7 +160,7 @@ public:
void detachWrapper(AccessibilityObject*, AccessibilityDetachmentType);
void attachWrapper(AccessibilityObject*);
- void childrenChanged(Node*);
+ void childrenChanged(Node*, Node* newChild = nullptr);
void childrenChanged(RenderObject*, RenderObject* newChild = nullptr);
void childrenChanged(AccessibilityObject*);
void checkedStateChanged(Node*);
@@ -122,16 +178,19 @@ 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();
- static void disableAccessibility();
+ 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; }
@@ -146,7 +205,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*);
@@ -155,7 +213,49 @@ 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&);
+ // Use ignoreNextNodeStart/ignorePreviousNodeEnd to determine the behavior when we are at node boundary.
+ CharacterOffset nextCharacterOffset(const CharacterOffset&, bool ignoreNextNodeStart = true);
+ CharacterOffset previousCharacterOffset(const CharacterOffset&, bool ignorePreviousNodeEnd = true);
+ void startOrEndTextMarkerDataForRange(TextMarkerData&, RefPtr<Range>, bool);
+ CharacterOffset startOrEndCharacterOffsetForRange(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&);
+
+ // Bounds
+ CharacterOffset characterOffsetForPoint(const IntPoint&, AccessibilityObject*);
+ IntRect absoluteCaretBoundsForCharacterOffset(const CharacterOffset&);
+ CharacterOffset characterOffsetForBounds(const IntRect&, bool);
+
+ // Lines
+ CharacterOffset endCharacterOffsetOfLine(const CharacterOffset&);
+ CharacterOffset startCharacterOffsetOfLine(const CharacterOffset&);
+
+ // Index
+ CharacterOffset characterOffsetForIndex(int, const AccessibilityObject*);
+ int indexForCharacterOffset(const CharacterOffset&, AccessibilityObject*);
enum AXNotification {
AXActiveDescendantChanged,
@@ -165,10 +265,12 @@ public:
AXFocusedUIElementChanged,
AXLayoutComplete,
AXLoadComplete,
+ AXNewDocumentLoadComplete,
AXSelectedChildrenChanged,
AXSelectedTextChanged,
AXValueChanged,
AXScrolledToAnchor,
+ AXLiveRegionCreated,
AXLiveRegionChanged,
AXMenuListItemSelected,
AXMenuListValueChanged,
@@ -177,6 +279,7 @@ public:
AXRowCountChanged,
AXRowCollapsed,
AXRowExpanded,
+ AXExpandedChanged,
AXInvalidStatusChanged,
AXTextChanged,
AXAriaAttributeChanged,
@@ -187,12 +290,19 @@ public:
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 nodeTextChangeNotification(Node*, AXTextChange, unsigned offset, const String&);
+ void setTextSelectionIntent(const AXTextStateChangeIntent&);
+ void setIsSynchronizingSelection(bool);
+
+ 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*);
+ void focusAriaModalNode();
enum AXLoadingEvent {
AXLoadingStarted,
@@ -211,12 +321,26 @@ public:
AXComputedObjectAttributeCache* computedObjectAttributeCache() { return m_computedObjectAttributeCache.get(); }
Document& document() const { return m_document; }
-
+
+#if PLATFORM(MAC)
+ static void setShouldRepostNotificationsForTests(bool value);
+#endif
+ void recomputeDeferredIsIgnored(RenderBlock& renderer);
+ void performDeferredIsIgnoredChange();
+
protected:
void postPlatformNotification(AccessibilityObject*, AXNotification);
void platformHandleFocusedUIElementChanged(Node* oldFocusedNode, Node* newFocusedNode);
- void nodeTextChangePlatformNotification(AccessibilityObject*, AXTextChange, unsigned offset, const String&);
+#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*);
@@ -225,8 +349,57 @@ 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, TraverseOptionValidateOffset = 1 << 3 };
+ Node* nextNode(Node*) const;
+ Node* previousNode(Node*) const;
+ CharacterOffset traverseToOffsetInRange(RefPtr<Range>, int, TraverseOption = TraverseOptionDefault, 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 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&);
+ CharacterOffset characterOffsetForPoint(const IntPoint&);
+ LayoutRect localCaretRectForCharacterOffset(RenderObject*&, const CharacterOffset&);
+ bool shouldSkipBoundary(const CharacterOffset&, const CharacterOffset&);
private:
+ AccessibilityObject* rootWebArea();
+
+ static AccessibilityObject* focusedImageMapUIElement(HTMLAreaElement*);
+
+ AXID getAXID(AccessibilityObject*);
+
+ void notificationPostTimerFired();
+
+ void liveRegionChangedNotificationPostTimerFired();
+
+ void focusAriaModalNodeTimerFired();
+
+ 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;
@@ -234,20 +407,28 @@ private:
HashMap<Node*, AXID> m_nodeObjectMapping;
HashSet<Node*> m_textMarkerNodes;
std::unique_ptr<AXComputedObjectAttributeCache> m_computedObjectAttributeCache;
- static bool gAccessibilityEnabled;
- static bool gAccessibilityEnhancedUserInterfaceEnabled;
-
+ WEBCORE_EXPORT static bool gAccessibilityEnabled;
+ WEBCORE_EXPORT static bool gAccessibilityEnhancedUserInterfaceEnabled;
+
HashSet<AXID> m_idsInUse;
-
- Timer<AXObjectCache> m_notificationPostTimer;
+
+ Timer m_notificationPostTimer;
Vector<std::pair<RefPtr<AccessibilityObject>, AXNotification>> m_notificationsToPost;
- void notificationPostTimerFired(Timer<AXObjectCache>&);
- void handleMenuOpened(Node*);
- void handleMenuItemSelected(Node*);
+
+ Timer m_passwordNotificationPostTimer;
+
+ ListHashSet<RefPtr<AccessibilityObject>> m_passwordNotificationsToPost;
- static AccessibilityObject* focusedImageMapUIElement(HTMLAreaElement*);
+ Timer m_liveRegionChangedPostTimer;
+ ListHashSet<RefPtr<AccessibilityObject>> m_liveRegionObjectsSet;
- AXID getAXID(AccessibilityObject*);
+ Timer m_focusAriaModalNodeTimer;
+ Node* m_currentAriaModalNode;
+ ListHashSet<Node*> m_ariaModalNodesSet;
+
+ AXTextStateChangeIntent m_textSelectionIntent;
+ bool m_isSynchronizingSelection { false };
+ ListHashSet<RenderBlock*> m_deferredIsIgnoredChangeList;
};
class AXAttributeCacheEnabler
@@ -255,40 +436,44 @@ 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*);
#if !HAVE(ACCESSIBILITY)
inline AccessibilityObjectInclusion AXComputedObjectAttributeCache::getIgnored(AXID) const { return DefaultBehavior; }
+inline AccessibilityReplacedText::AccessibilityReplacedText(const VisibleSelection&) { }
+inline void AccessibilityReplacedText::postTextStateChangeNotification(AXObjectCache*, AXTextEditType, const String&, const VisibleSelection&) { }
inline void AXComputedObjectAttributeCache::setIgnored(AXID, AccessibilityObjectInclusion) { }
-inline AXObjectCache::AXObjectCache(Document& document) : m_document(document), m_notificationPostTimer(this, (Timer<AXObjectCache>::TimerFiredFunction) nullptr) { }
+inline AXObjectCache::AXObjectCache(Document& document) : m_document(document), m_notificationPostTimer(*this, &AXObjectCache::notificationPostTimerFired), m_passwordNotificationPostTimer(*this, &AXObjectCache::passwordNotificationPostTimerFired), m_liveRegionChangedPostTimer(*this, &AXObjectCache::liveRegionChangedNotificationPostTimerFired), m_focusAriaModalNodeTimer(*this, &AXObjectCache::focusAriaModalNodeTimerFired) { }
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*, RenderObject*) { }
-inline void AXObjectCache::childrenChanged(Node*) { }
+inline void AXObjectCache::childrenChanged(Node*, Node*) { }
inline void AXObjectCache::childrenChanged(AccessibilityObject*) { }
inline void AXObjectCache::textChanged(RenderObject*) { }
inline void AXObjectCache::textChanged(Node*) { }
@@ -300,25 +485,51 @@ inline void AXObjectCache::frameLoadingEventPlatformNotification(AccessibilityOb
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::recomputeDeferredIsIgnored(RenderBlock&) { }
+inline void AXObjectCache::performDeferredIsIgnoredChange() { }
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::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 void AXObjectCache::focusAriaModalNode() { }
+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 IntRect AXObjectCache::absoluteCaretBoundsForCharacterOffset(const CharacterOffset&) { return IntRect(); }
+inline CharacterOffset AXObjectCache::characterOffsetForIndex(int, const AccessibilityObject*) { return CharacterOffset(); }
+inline CharacterOffset AXObjectCache::startOrEndCharacterOffsetForRange(RefPtr<Range>, bool) { return CharacterOffset(); }
+inline CharacterOffset AXObjectCache::endCharacterOffsetOfLine(const CharacterOffset&) { 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
+
+} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AXTextStateChangeIntent.h b/Source/WebCore/accessibility/AXTextStateChangeIntent.h
new file mode 100644
index 000000000..1feccce2e
--- /dev/null
+++ b/Source/WebCore/accessibility/AXTextStateChangeIntent.h
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+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)
+ { }
+};
+
+} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AccessibilityARIAGrid.cpp b/Source/WebCore/accessibility/AccessibilityARIAGrid.cpp
index acb837070..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,11 +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"
+#include "RenderTableSection.h"
namespace WebCore {
@@ -47,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 = toAccessibilityTableRow(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;
}
@@ -85,9 +87,9 @@ 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.
+ // 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
@@ -98,7 +100,7 @@ void AccessibilityARIAGrid::addChildren()
{
ASSERT(!m_haveChildren);
- if (!isAccessibilityTable()) {
+ if (!isExposableThroughAccessibility()) {
AccessibilityRenderObject::addChildren();
return;
}
@@ -109,20 +111,36 @@ void AccessibilityARIAGrid::addChildren()
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 = toAccessibilityTableColumn(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 2a4fb9b26..33232d66a 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.
*
@@ -26,8 +26,7 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AccessibilityARIAGrid_h
-#define AccessibilityARIAGrid_h
+#pragma once
#include "AccessibilityTable.h"
#include <wtf/Forward.h>
@@ -37,28 +36,24 @@ 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 override { return true; }
- virtual void addChildren() override;
+ void addChildren() override;
private:
+ explicit AccessibilityARIAGrid(RenderObject*);
+
// ARIA treegrids and grids support selected rows.
- virtual bool supportsSelectedRows() override { return true; }
- virtual bool isMultiSelectable() const override { return true; }
- virtual bool isTableExposableThroughAccessibility() const override { return true; }
+ bool supportsSelectedRows() override { return true; }
+ bool isMultiSelectable() const override { return true; }
+ bool computeIsTableExposableThroughAccessibility() const override { return true; }
+ bool isAriaTable() const override { return true; }
void addRowDescendant(AccessibilityObject*, HashSet<AccessibilityObject*>& appendedRows, unsigned& columnCount);
bool addTableCellChild(AccessibilityObject*, HashSet<AccessibilityObject*>& appendedRows, unsigned& columnCount);
};
} // namespace WebCore
-
-#endif // AccessibilityARIAGrid_h
diff --git a/Source/WebCore/accessibility/AccessibilityARIAGridCell.cpp b/Source/WebCore/accessibility/AccessibilityARIAGridCell.cpp
index 506e7f25e..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,9 +32,12 @@
#include "AccessibilityObject.h"
#include "AccessibilityTable.h"
#include "AccessibilityTableRow.h"
+#include "HTMLNames.h"
namespace WebCore {
+using namespace HTMLNames;
+
AccessibilityARIAGridCell::AccessibilityARIAGridCell(RenderObject* renderer)
: AccessibilityTableCell(renderer)
{
@@ -44,43 +47,37 @@ AccessibilityARIAGridCell::~AccessibilityARIAGridCell()
{
}
-PassRefPtr<AccessibilityARIAGridCell> AccessibilityARIAGridCell::create(RenderObject* renderer)
+Ref<AccessibilityARIAGridCell> AccessibilityARIAGridCell::create(RenderObject* renderer)
{
- return adoptRef(new AccessibilityARIAGridCell(renderer));
+ return adoptRef(*new AccessibilityARIAGridCell(renderer));
}
AccessibilityTable* AccessibilityARIAGridCell::parentTable() const
{
- AccessibilityObject* parent = parentObjectUnignored();
- if (!parent)
- return nullptr;
-
- if (parent->isAccessibilityTable())
- return toAccessibilityTable(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 nullptr;
-
- return toAccessibilityTable(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(std::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 = toAccessibilityTableRow(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 = toAccessibilityTable(parent)->columnCount();
+ unsigned columnCount = downcast<AccessibilityTable>(*parent).columnCount();
if (!columnCount)
return;
@@ -94,17 +91,51 @@ void AccessibilityARIAGridCell::rowIndexRange(std::pair<unsigned, unsigned>& row
}
}
- // 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);
+}
+
+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)
+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;
const AccessibilityChildrenVector& siblings = parent->children();
@@ -116,8 +147,20 @@ void AccessibilityARIAGridCell::columnIndexRange(std::pair<unsigned, unsigned>&
}
}
- // 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 1df3e3f02..ef226007a 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.
*
@@ -26,30 +26,28 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AccessibilityARIAGridCell_h
-#define AccessibilityARIAGridCell_h
+#pragma once
#include "AccessibilityTableCell.h"
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(std::pair<unsigned, unsigned>& rowRange) override;
+ void rowIndexRange(std::pair<unsigned, unsigned>& rowRange) const override;
// fills in the start location and column span of cell
- virtual void columnIndexRange(std::pair<unsigned, unsigned>& columnRange) override;
+ void columnIndexRange(std::pair<unsigned, unsigned>& columnRange) const override;
-protected:
- virtual AccessibilityTable* parentTable() const override;
+private:
+ explicit AccessibilityARIAGridCell(RenderObject*);
+
+ AccessibilityTable* parentTable() const override;
+ AccessibilityObject* parentRowGroup() const;
+ unsigned ariaRowSpanWithRowIndex(unsigned index) const;
};
} // namespace WebCore
-
-#endif // AccessibilityARIAGridCell_h
diff --git a/Source/WebCore/accessibility/AccessibilityARIAGridRow.cpp b/Source/WebCore/accessibility/AccessibilityARIAGridRow.cpp
index f85b62719..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.
*
@@ -44,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
@@ -63,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.
@@ -73,7 +73,7 @@ void AccessibilityARIAGridRow::disclosedRows(AccessibilityChildrenVector& disclo
return;
unsigned level = hierarchicalLevel();
- auto& allRows = toAccessibilityTable(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();
@@ -90,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();
- auto& allRows = toAccessibilityTable(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();
@@ -114,14 +114,22 @@ AccessibilityObject* AccessibilityARIAGridRow::disclosedByRow() const
return nullptr;
}
+AccessibilityObject* AccessibilityARIAGridRow::parentObjectUnignored() const
+{
+ return parentTable();
+}
+
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 toAccessibilityTable(parent);
+ if (is<AccessibilityTable>(*parent)) {
+ AccessibilityTable& tableParent = downcast<AccessibilityTable>(*parent);
+ if (tableParent.isExposableThroughAccessibility() && tableParent.isAriaTable())
+ return &tableParent;
+ }
}
return nullptr;
diff --git a/Source/WebCore/accessibility/AccessibilityARIAGridRow.h b/Source/WebCore/accessibility/AccessibilityARIAGridRow.h
index c97db29b4..7ba74f4dd 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.
*
@@ -26,8 +26,7 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AccessibilityARIAGridRow_h
-#define AccessibilityARIAGridRow_h
+#pragma once
#include "AccessibilityTableRow.h"
@@ -35,26 +34,24 @@ namespace WebCore {
class AccessibilityTable;
-class AccessibilityARIAGridRow : public AccessibilityTableRow {
-
-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() override;
+ AccessibilityObject* headerObject() override;
private:
- virtual bool isARIATreeGridRow() const override;
- virtual AccessibilityTable* parentTable() const override;
-};
+ explicit AccessibilityARIAGridRow(RenderObject*);
-ACCESSIBILITY_OBJECT_TYPE_CASTS(AccessibilityARIAGridRow, isARIATreeGridRow())
+ bool isARIATreeGridRow() const override;
+ AccessibilityTable* parentTable() const override;
+ AccessibilityObject* parentObjectUnignored() const override;
+};
} // namespace WebCore
-#endif // AccessibilityARIAGridRow_h
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilityARIAGridRow, isARIATreeGridRow())
diff --git a/Source/WebCore/accessibility/AccessibilityAllInOne.cpp b/Source/WebCore/accessibility/AccessibilityAllInOne.cpp
new file mode 100644
index 000000000..2cbe4505c
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityAllInOne.cpp
@@ -0,0 +1,60 @@
+/*
+ * 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 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.
+ */
+
+// This all-in-one cpp file cuts down on template bloat to allow us to build our Windows release build.
+
+#include "AXObjectCache.cpp"
+#include "AccessibilityARIAGrid.cpp"
+#include "AccessibilityARIAGridCell.cpp"
+#include "AccessibilityARIAGridRow.cpp"
+#include "AccessibilityAttachment.cpp"
+#include "AccessibilityImageMapLink.cpp"
+#include "AccessibilityLabel.cpp"
+#include "AccessibilityList.cpp"
+#include "AccessibilityListBox.cpp"
+#include "AccessibilityListBoxOption.cpp"
+#include "AccessibilityMathMLElement.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"
+#include "AccessibilityRenderObject.cpp"
+#include "AccessibilitySVGElement.cpp"
+#include "AccessibilitySVGRoot.cpp"
+#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/AccessibilityAttachment.cpp b/Source/WebCore/accessibility/AccessibilityAttachment.cpp
new file mode 100644
index 000000000..a7d3c37ea
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityAttachment.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2016 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * OWNER 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 "AccessibilityAttachment.h"
+
+#include "HTMLAttachmentElement.h"
+#include "HTMLNames.h"
+#include "LocalizedStrings.h"
+#include "RenderAttachment.h"
+
+#if ENABLE(ATTACHMENT_ELEMENT)
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+AccessibilityAttachment::AccessibilityAttachment(RenderAttachment* renderer)
+ : AccessibilityRenderObject(renderer)
+{
+}
+
+Ref<AccessibilityAttachment> AccessibilityAttachment::create(RenderAttachment* renderer)
+{
+ return adoptRef(*new AccessibilityAttachment(renderer));
+}
+
+bool AccessibilityAttachment::hasProgress(float* progress) const
+{
+ auto& progressString = getAttribute(progressAttr);
+ bool validProgress;
+ float result = std::max<float>(std::min<float>(progressString.toFloat(&validProgress), 1), 0);
+ if (progress)
+ *progress = result;
+ return validProgress;
+}
+
+float AccessibilityAttachment::valueForRange() const
+{
+ float progress = 0;
+ hasProgress(&progress);
+ return progress;
+}
+
+HTMLAttachmentElement* AccessibilityAttachment::attachmentElement() const
+{
+ ASSERT(is<HTMLAttachmentElement>(node()));
+ if (!is<HTMLAttachmentElement>(node()))
+ return nullptr;
+
+ return downcast<HTMLAttachmentElement>(node());
+}
+
+String AccessibilityAttachment::roleDescription() const
+{
+ return AXAttachmentRoleText();
+}
+
+bool AccessibilityAttachment::computeAccessibilityIsIgnored() const
+{
+ return false;
+}
+
+void AccessibilityAttachment::accessibilityText(Vector<AccessibilityText>& textOrder)
+{
+ HTMLAttachmentElement* attachmentElement = this->attachmentElement();
+ if (!attachmentElement)
+ return;
+
+ auto title = attachmentElement->attachmentTitle();
+ auto& subtitle = getAttribute(subtitleAttr);
+ auto& action = getAttribute(actionAttr);
+
+ if (action.length())
+ textOrder.append(AccessibilityText(action, ActionText));
+
+ if (title.length())
+ textOrder.append(AccessibilityText(title, TitleText));
+
+ if (subtitle.length())
+ textOrder.append(AccessibilityText(subtitle, SubtitleText));
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(ATTACHMENT_ELEMENT)
+
diff --git a/Source/WebCore/accessibility/AccessibilityAttachment.h b/Source/WebCore/accessibility/AccessibilityAttachment.h
new file mode 100644
index 000000000..2b657737c
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityAttachment.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * OWNER 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.
+ */
+
+#pragma once
+
+#if ENABLE(ATTACHMENT_ELEMENT)
+
+#include "AccessibilityRenderObject.h"
+
+namespace WebCore {
+
+class HTMLAttachmentElement;
+class RenderAttachment;
+
+class AccessibilityAttachment final : public AccessibilityRenderObject {
+public:
+ static Ref<AccessibilityAttachment> create(RenderAttachment*);
+ HTMLAttachmentElement* attachmentElement() const;
+ bool hasProgress(float* progress = nullptr) const;
+
+private:
+ AccessibilityRole roleValue() const override { return ButtonRole; }
+ bool isAttachmentElement() const override { return true; }
+
+ String roleDescription() const override;
+ float valueForRange() const override;
+ bool computeAccessibilityIsIgnored() const override;
+ void accessibilityText(Vector<AccessibilityText>&) override;
+ explicit AccessibilityAttachment(RenderAttachment*);
+};
+
+} // namespace WebCore
+
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilityAttachment, isAttachmentElement())
+
+#endif // ENABLE(ATTACHMENT_ELEMENT)
diff --git a/Source/WebCore/accessibility/AccessibilityImageMapLink.cpp b/Source/WebCore/accessibility/AccessibilityImageMapLink.cpp
index 5f3f2fab0..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,7 +60,7 @@ 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());
}
@@ -140,8 +140,8 @@ RenderElement* AccessibilityImageMapLink::imageMapLinkRenderer() const
return nullptr;
RenderElement* renderer = nullptr;
- if (m_parent && m_parent->isAccessibilityRenderObject())
- renderer = toRenderElement(toAccessibilityRenderObject(m_parent)->renderer());
+ if (is<AccessibilityRenderObject>(m_parent))
+ renderer = downcast<RenderElement>(downcast<AccessibilityRenderObject>(*m_parent).renderer());
else
renderer = m_mapElement->renderer();
diff --git a/Source/WebCore/accessibility/AccessibilityImageMapLink.h b/Source/WebCore/accessibility/AccessibilityImageMapLink.h
index d28ff6bfb..d74e30cff 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.
*
@@ -26,8 +26,7 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AccessibilityImageMapLink_h
-#define AccessibilityImageMapLink_h
+#pragma once
#include "AccessibilityMockObject.h"
#include "HTMLAreaElement.h"
@@ -35,12 +34,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,40 +45,39 @@ public:
void setHTMLMapElement(HTMLMapElement* element) { m_mapElement = element; }
HTMLMapElement* mapElement() const { return m_mapElement.get(); }
- virtual Node* node() const override { return m_areaElement.get(); }
+ Node* node() const override { return m_areaElement.get(); }
- virtual AccessibilityRole roleValue() const override;
- virtual bool isEnabled() const override { return true; }
+ AccessibilityRole roleValue() const override;
+ bool isEnabled() const override { return true; }
- 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;
+ Element* anchorElement() const override;
+ Element* actionElement() const override;
+ URL url() const override;
+ bool isLink() const override { return true; }
+ bool isLinked() const override { return true; }
+ String title() const override;
+ String accessibilityDescription() const override;
+ AccessibilityObject* parentObject() const override;
- virtual String stringValueForMSAA() const override;
- virtual String nameForMSAA() const override;
+ String stringValueForMSAA() const override;
+ String nameForMSAA() const override;
- virtual LayoutRect elementRect() const override;
-
-private:
- RefPtr<HTMLAreaElement> m_areaElement;
- RefPtr<HTMLMapElement> m_mapElement;
+ LayoutRect elementRect() const override;
- virtual void detachFromParent() override;
+private:
+ AccessibilityImageMapLink();
- virtual Path elementPath() const override;
+ void detachFromParent() override;
+ 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; }
-};
+ void accessibilityText(Vector<AccessibilityText>&) override;
+ bool isImageMapLink() const override { return true; }
+ bool supportsPath() const override { return true; }
-ACCESSIBILITY_OBJECT_TYPE_CASTS(AccessibilityImageMapLink, isImageMapLink())
+ RefPtr<HTMLAreaElement> m_areaElement;
+ RefPtr<HTMLMapElement> m_mapElement;
+};
} // namespace WebCore
-#endif // AccessibilityImageMapLink_h
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilityImageMapLink, isImageMapLink())
diff --git a/Source/WebCore/accessibility/AccessibilityLabel.cpp b/Source/WebCore/accessibility/AccessibilityLabel.cpp
new file mode 100644
index 000000000..c2d503884
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityLabel.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2016 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 "AccessibilityLabel.h"
+
+#include "AXObjectCache.h"
+#include "HTMLNames.h"
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+AccessibilityLabel::AccessibilityLabel(RenderObject* renderer)
+ : AccessibilityRenderObject(renderer)
+{
+}
+
+AccessibilityLabel::~AccessibilityLabel()
+{
+}
+
+Ref<AccessibilityLabel> AccessibilityLabel::create(RenderObject* renderer)
+{
+ return adoptRef(*new AccessibilityLabel(renderer));
+}
+
+bool AccessibilityLabel::computeAccessibilityIsIgnored() const
+{
+ return accessibilityIsIgnoredByDefault();
+}
+
+String AccessibilityLabel::stringValue() const
+{
+ if (containsOnlyStaticText())
+ return textUnderElement();
+ return AccessibilityNodeObject::stringValue();
+}
+
+static bool childrenContainOnlyStaticText(const AccessibilityObject::AccessibilityChildrenVector& children)
+{
+ if (!children.size())
+ return false;
+ for (const auto& child : children) {
+ if (child->roleValue() == StaticTextRole)
+ continue;
+ if (child->roleValue() == GroupRole) {
+ if (!childrenContainOnlyStaticText(child->children()))
+ return false;
+ } else
+ return false;
+ }
+ return true;
+}
+
+bool AccessibilityLabel::containsOnlyStaticText() const
+{
+ if (m_containsOnlyStaticTextDirty)
+ return childrenContainOnlyStaticText(m_children);
+ return m_containsOnlyStaticText;
+}
+
+void AccessibilityLabel::updateChildrenIfNecessary()
+{
+ AccessibilityRenderObject::updateChildrenIfNecessary();
+ if (m_containsOnlyStaticTextDirty)
+ m_containsOnlyStaticText = childrenContainOnlyStaticText(m_children);
+ m_containsOnlyStaticTextDirty = false;
+}
+
+void AccessibilityLabel::clearChildren()
+{
+ AccessibilityRenderObject::clearChildren();
+ m_containsOnlyStaticText = false;
+ m_containsOnlyStaticTextDirty = false;
+}
+
+void AccessibilityLabel::insertChild(AccessibilityObject* object, unsigned index)
+{
+ AccessibilityRenderObject::insertChild(object, index);
+ m_containsOnlyStaticTextDirty = true;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AccessibilityLabel.h b/Source/WebCore/accessibility/AccessibilityLabel.h
new file mode 100644
index 000000000..ed75f66c4
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityLabel.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#pragma once
+
+#include "AccessibilityRenderObject.h"
+
+namespace WebCore {
+
+class AccessibilityLabel final : public AccessibilityRenderObject {
+public:
+ static Ref<AccessibilityLabel> create(RenderObject*);
+ virtual ~AccessibilityLabel();
+
+ bool containsOnlyStaticText() const;
+
+private:
+ explicit AccessibilityLabel(RenderObject*);
+ bool computeAccessibilityIsIgnored() const final;
+ AccessibilityRole roleValue() const final { return LabelRole; }
+ bool isLabel() const final { return true; }
+ String stringValue() const final;
+ void updateChildrenIfNecessary() final;
+ void clearChildren() final;
+ void insertChild(AccessibilityObject*, unsigned) final;
+ bool m_containsOnlyStaticTextDirty : 1;
+ bool m_containsOnlyStaticText : 1;
+};
+
+} // namespace WebCore
+
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilityLabel, isLabel())
diff --git a/Source/WebCore/accessibility/AccessibilityList.cpp b/Source/WebCore/accessibility/AccessibilityList.cpp
index 69aa71f1e..da130a2b4 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,7 +30,10 @@
#include "AccessibilityList.h"
#include "AXObjectCache.h"
+#include "HTMLElement.h"
#include "HTMLNames.h"
+#include "PseudoElement.h"
+#include "RenderListItem.h"
#include "RenderObject.h"
namespace WebCore {
@@ -46,9 +49,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
@@ -91,15 +94,108 @@ bool AccessibilityList::isDescriptionList() const
return false;
Node* node = m_renderer->node();
- return node && node->hasTagName(dlTag);
+ return node && node->hasTagName(dlTag);
}
-AccessibilityRole AccessibilityList::roleValue() const
+bool AccessibilityList::childHasPseudoVisibleListItemMarkers(RenderObject* listItem)
{
- if (isDescriptionList())
+ // 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)
+ 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;
+}
- return ListRole;
+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 5e844ff0c..908979f4a 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.
*
@@ -26,33 +26,31 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AccessibilityList_h
-#define AccessibilityList_h
+#pragma once
#include "AccessibilityRenderObject.h"
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 override { return true; }
bool isUnorderedList() const;
bool isOrderedList() const;
bool isDescriptionList() const;
- virtual AccessibilityRole roleValue() const override final;
+ AccessibilityRole roleValue() const override;
+
private:
- virtual bool computeAccessibilityIsIgnored() const override;
+ explicit AccessibilityList(RenderObject*);
+ bool isList() const override { return true; }
+ bool computeAccessibilityIsIgnored() const override;
+ AccessibilityRole determineAccessibilityRole() override;
+ bool childHasPseudoVisibleListItemMarkers(RenderObject*);
};
-ACCESSIBILITY_OBJECT_TYPE_CASTS(AccessibilityList, isList())
-
} // namespace WebCore
-#endif // AccessibilityList_h
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilityList, isList())
diff --git a/Source/WebCore/accessibility/AccessibilityListBox.cpp b/Source/WebCore/accessibility/AccessibilityListBox.cpp
index f9bb388ef..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.
*
@@ -49,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
@@ -61,7 +61,7 @@ bool AccessibilityListBox::canSetSelectedChildrenAttribute() const
if (!selectNode)
return false;
- return !toHTMLSelectElement(selectNode)->isDisabledFormControl();
+ return !downcast<HTMLSelectElement>(*selectNode).isDisabledFormControl();
}
void AccessibilityListBox::addChildren()
@@ -72,7 +72,7 @@ void AccessibilityListBox::addChildren()
m_haveChildren = true;
- for (const auto& listItem : toHTMLSelectElement(selectNode)->listItems()) {
+ 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(listItem);
@@ -92,16 +92,16 @@ void AccessibilityListBox::setSelectedChildren(const AccessibilityChildrenVector
// disable any selected options
for (const auto& child : m_children) {
- AccessibilityListBoxOption* listBoxOption = toAccessibilityListBoxOption(child.get());
- if (listBoxOption->isSelected())
- listBoxOption->setSelected(false);
+ auto& listBoxOption = downcast<AccessibilityListBoxOption>(*child);
+ if (listBoxOption.isSelected())
+ listBoxOption.setSelected(false);
}
for (const auto& obj : children) {
if (obj->roleValue() != ListBoxOptionRole)
continue;
- toAccessibilityListBoxOption(obj.get())->setSelected(true);
+ downcast<AccessibilityListBoxOption>(*obj).setSelected(true);
}
}
@@ -113,7 +113,7 @@ void AccessibilityListBox::selectedChildren(AccessibilityChildrenVector& result)
addChildren();
for (const auto& child : m_children) {
- if (toAccessibilityListBoxOption(child.get())->isSelected())
+ if (downcast<AccessibilityListBoxOption>(*child).isSelected())
result.append(child.get());
}
}
@@ -127,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]);
}
}
@@ -136,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);
- toAccessibilityListBoxOption(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
@@ -149,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 2f56bf60d..ab91d9376 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.
*
@@ -26,39 +26,34 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AccessibilityListBox_h
-#define AccessibilityListBox_h
+#pragma once
#include "AccessibilityRenderObject.h"
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 override { return true; }
-
- virtual bool canSetSelectedChildrenAttribute() const override;
+ bool canSetSelectedChildrenAttribute() const override;
void setSelectedChildren(const AccessibilityChildrenVector&);
- virtual AccessibilityRole roleValue() const override { return ListBoxRole; }
+ AccessibilityRole roleValue() const override { return ListBoxRole; }
- virtual void selectedChildren(AccessibilityChildrenVector&) override;
- virtual void visibleChildren(AccessibilityChildrenVector&) override;
+ void selectedChildren(AccessibilityChildrenVector&) override;
+ void visibleChildren(AccessibilityChildrenVector&) override;
- virtual void addChildren() override;
+ void addChildren() override;
-private:
+private:
+ explicit AccessibilityListBox(RenderObject*);
+
+ bool isNativeListBox() const override { return true; }
AccessibilityObject* listBoxOptionAccessibilityObject(HTMLElement*) const;
- virtual AccessibilityObject* elementAccessibilityHitTest(const IntPoint&) const override;
+ AccessibilityObject* elementAccessibilityHitTest(const IntPoint&) const override;
};
-
-ACCESSIBILITY_OBJECT_TYPE_CASTS(AccessibilityListBox, isListBox())
} // namespace WebCore
-#endif // AccessibilityListBox_h
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilityListBox, isNativeListBox())
diff --git a/Source/WebCore/accessibility/AccessibilityListBoxOption.cpp b/Source/WebCore/accessibility/AccessibilityListBoxOption.cpp
index e403e6c3d..6a63894ef 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.
*
@@ -46,7 +46,7 @@ namespace WebCore {
using namespace HTMLNames;
AccessibilityListBoxOption::AccessibilityListBoxOption()
- : m_optionElement(0)
+ : m_optionElement(nullptr)
{
}
@@ -54,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->hasAttributeWithoutSynchronization(disabledAttr))
return false;
return true;
@@ -78,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
@@ -106,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();
int index = listBoxOptionIndex();
if (index != -1)
- rect = toRenderListBox(listBoxRenderer)->itemBoundingBoxRect(parentRect.location(), index);
+ rect = downcast<RenderListBox>(*listBoxRenderer).itemBoundingBoxRect(parentRect.location(), index);
return rect;
}
@@ -131,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())
@@ -156,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();
}
@@ -174,7 +165,7 @@ AccessibilityObject* AccessibilityListBoxOption::parentObject() const
{
HTMLSelectElement* parentNode = listBoxOptionParentNode();
if (!parentNode)
- return 0;
+ return nullptr;
return m_optionElement->document().axObjectCache()->getOrCreate(parentNode);
}
@@ -200,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
diff --git a/Source/WebCore/accessibility/AccessibilityListBoxOption.h b/Source/WebCore/accessibility/AccessibilityListBoxOption.h
index ad1aecfaa..9d3ecd2e5 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.
*
@@ -26,8 +26,7 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AccessibilityListBoxOption_h
-#define AccessibilityListBoxOption_h
+#pragma once
#include "AccessibilityObject.h"
#include "HTMLElement.h"
@@ -37,46 +36,42 @@ namespace WebCore {
class AccessibilityListBox;
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 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;
+ AccessibilityRole roleValue() const override { return ListBoxOptionRole; }
+ bool isSelected() const override;
+ bool isEnabled() const override;
+ bool isSelectedOptionActive() const override;
+ String stringValue() const override;
+ Element* actionElement() const override;
+ Node* node() const override { return m_optionElement; }
+ void setSelected(bool) override;
+ bool canSetSelectedAttribute() const override;
- virtual LayoutRect elementRect() const override;
- virtual AccessibilityObject* parentObject() const override;
- virtual bool isListBoxOption() const override final { return true; }
+ LayoutRect elementRect() const override;
+ AccessibilityObject* parentObject() const override;
private:
- HTMLElement* m_optionElement;
-
- virtual bool canHaveChildren() const override { return false; }
+ AccessibilityListBoxOption();
+
+ bool isListBoxOption() const override { return true; }
+ bool canHaveChildren() const override { return false; }
HTMLSelectElement* listBoxOptionParentNode() const;
int listBoxOptionIndex() const;
IntRect listBoxOptionRect() const;
AccessibilityObject* listBoxOptionAccessibilityObject(HTMLElement*) const;
- virtual bool computeAccessibilityIsIgnored() const override;
-};
+ bool computeAccessibilityIsIgnored() const override;
-ACCESSIBILITY_OBJECT_TYPE_CASTS(AccessibilityListBoxOption, isListBoxOption())
+ HTMLElement* m_optionElement;
+};
} // namespace WebCore
-#endif // AccessibilityListBoxOption_h
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilityListBoxOption, isListBoxOption())
diff --git a/Source/WebCore/accessibility/AccessibilityMathMLElement.cpp b/Source/WebCore/accessibility/AccessibilityMathMLElement.cpp
new file mode 100644
index 000000000..e31beafae
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityMathMLElement.cpp
@@ -0,0 +1,456 @@
+/*
+ * Copyright (C) 2016 Igalia, S.L.
+ * 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * OWNER 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"
+
+#if ENABLE(MATHML)
+#include "AccessibilityMathMLElement.h"
+
+#include "AXObjectCache.h"
+#include "MathMLNames.h"
+
+namespace WebCore {
+
+AccessibilityMathMLElement::AccessibilityMathMLElement(RenderObject* renderer, bool isAnonymousOperator)
+ : AccessibilityRenderObject(renderer)
+ , m_isAnonymousOperator(isAnonymousOperator)
+{
+}
+
+AccessibilityMathMLElement::~AccessibilityMathMLElement()
+{
+}
+
+Ref<AccessibilityMathMLElement> AccessibilityMathMLElement::create(RenderObject* renderer, bool isAnonymousOperator)
+{
+ return adoptRef(*new AccessibilityMathMLElement(renderer, isAnonymousOperator));
+}
+
+AccessibilityRole AccessibilityMathMLElement::determineAccessibilityRole()
+{
+ if (!m_renderer)
+ return UnknownRole;
+
+ if ((m_ariaRole = determineAriaRoleAttribute()) != UnknownRole)
+ return m_ariaRole;
+
+ Node* node = m_renderer->node();
+ if (node && node->hasTagName(MathMLNames::mathTag))
+ return DocumentMathRole;
+
+ // It's not clear which role a platform should choose for a math element.
+ // Declaring a math element role should give flexibility to platforms to choose.
+ return MathElementRole;
+}
+
+String AccessibilityMathMLElement::textUnderElement(AccessibilityTextUnderElementMode mode) const
+{
+ if (m_isAnonymousOperator) {
+ UChar operatorChar = downcast<RenderMathMLOperator>(*m_renderer).textContent();
+ return operatorChar ? String(&operatorChar, 1) : String();
+ }
+
+ return AccessibilityRenderObject::textUnderElement(mode);
+}
+
+String AccessibilityMathMLElement::stringValue() const
+{
+ if (m_isAnonymousOperator)
+ return textUnderElement();
+
+ return AccessibilityRenderObject::stringValue();
+}
+
+bool AccessibilityMathMLElement::isIgnoredElementWithinMathTree() const
+{
+ if (m_isAnonymousOperator)
+ return false;
+
+ // Only math elements that we explicitly recognize should be included
+ // We don't want things like <mstyle> to appear in the tree.
+ if (isMathFraction() || isMathFenced() || isMathSubscriptSuperscript() || isMathRow()
+ || isMathUnderOver() || isMathRoot() || isMathText() || isMathNumber()
+ || isMathOperator() || isMathFenceOperator() || isMathSeparatorOperator()
+ || isMathIdentifier() || isMathTable() || isMathTableRow() || isMathTableCell() || isMathMultiscript())
+ return false;
+
+ return true;
+}
+
+bool AccessibilityMathMLElement::isMathFraction() const
+{
+ return m_renderer && m_renderer->isRenderMathMLFraction();
+}
+
+bool AccessibilityMathMLElement::isMathFenced() const
+{
+ return m_renderer && m_renderer->isRenderMathMLFenced();
+}
+
+bool AccessibilityMathMLElement::isMathSubscriptSuperscript() const
+{
+ return m_renderer && m_renderer->isRenderMathMLScripts() && !isMathMultiscript();
+}
+
+bool AccessibilityMathMLElement::isMathRow() const
+{
+ return m_renderer && m_renderer->isRenderMathMLRow() && !isMathRoot();
+}
+
+bool AccessibilityMathMLElement::isMathUnderOver() const
+{
+ return m_renderer && m_renderer->isRenderMathMLUnderOver();
+}
+
+bool AccessibilityMathMLElement::isMathSquareRoot() const
+{
+ return m_renderer && m_renderer->isRenderMathMLSquareRoot();
+}
+
+bool AccessibilityMathMLElement::isMathToken() const
+{
+ return m_renderer && m_renderer->isRenderMathMLToken();
+}
+
+bool AccessibilityMathMLElement::isMathRoot() const
+{
+ return m_renderer && m_renderer->isRenderMathMLRoot();
+}
+
+bool AccessibilityMathMLElement::isMathOperator() const
+{
+ return m_renderer && m_renderer->isRenderMathMLOperator();
+}
+
+bool AccessibilityMathMLElement::isAnonymousMathOperator() const
+{
+ return m_isAnonymousOperator;
+}
+
+bool AccessibilityMathMLElement::isMathFenceOperator() const
+{
+ if (!is<RenderMathMLOperator>(m_renderer))
+ return false;
+
+ return downcast<RenderMathMLOperator>(*m_renderer).hasOperatorFlag(MathMLOperatorDictionary::Fence);
+}
+
+bool AccessibilityMathMLElement::isMathSeparatorOperator() const
+{
+ if (!is<RenderMathMLOperator>(m_renderer))
+ return false;
+
+ return downcast<RenderMathMLOperator>(*m_renderer).hasOperatorFlag(MathMLOperatorDictionary::Separator);
+}
+
+bool AccessibilityMathMLElement::isMathText() const
+{
+ return node() && (node()->hasTagName(MathMLNames::mtextTag) || hasTagName(MathMLNames::msTag));
+}
+
+bool AccessibilityMathMLElement::isMathNumber() const
+{
+ return node() && node()->hasTagName(MathMLNames::mnTag);
+}
+
+bool AccessibilityMathMLElement::isMathIdentifier() const
+{
+ return node() && node()->hasTagName(MathMLNames::miTag);
+}
+
+bool AccessibilityMathMLElement::isMathMultiscript() const
+{
+ return node() && node()->hasTagName(MathMLNames::mmultiscriptsTag);
+}
+
+bool AccessibilityMathMLElement::isMathTable() const
+{
+ return node() && node()->hasTagName(MathMLNames::mtableTag);
+}
+
+bool AccessibilityMathMLElement::isMathTableRow() const
+{
+ return node() && (node()->hasTagName(MathMLNames::mtrTag) || hasTagName(MathMLNames::mlabeledtrTag));
+}
+
+bool AccessibilityMathMLElement::isMathTableCell() const
+{
+ return node() && node()->hasTagName(MathMLNames::mtdTag);
+}
+
+bool AccessibilityMathMLElement::isMathScriptObject(AccessibilityMathScriptObjectType type) const
+{
+ AccessibilityObject* parent = parentObjectUnignored();
+ if (!parent)
+ return false;
+
+ return type == Subscript ? this == parent->mathSubscriptObject() : this == parent->mathSuperscriptObject();
+}
+
+bool AccessibilityMathMLElement::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;
+}
+
+AccessibilityObject* AccessibilityMathMLElement::mathRadicandObject()
+{
+ if (!isMathRoot())
+ return nullptr;
+
+ // For MathSquareRoot, we actually return the first child of the base.
+ // See also https://webkit.org/b/146452
+ const auto& children = this->children();
+ if (children.size() < 1)
+ return nullptr;
+
+ return children[0].get();
+}
+
+AccessibilityObject* AccessibilityMathMLElement::mathRootIndexObject()
+{
+ if (!isMathRoot() || isMathSquareRoot())
+ return nullptr;
+
+ const auto& children = this->children();
+ if (children.size() < 2)
+ return nullptr;
+
+ return children[1].get();
+}
+
+AccessibilityObject* AccessibilityMathMLElement::mathNumeratorObject()
+{
+ if (!isMathFraction())
+ return nullptr;
+
+ const auto& children = this->children();
+ if (children.size() != 2)
+ return nullptr;
+
+ return children[0].get();
+}
+
+AccessibilityObject* AccessibilityMathMLElement::mathDenominatorObject()
+{
+ if (!isMathFraction())
+ return nullptr;
+
+ const auto& children = this->children();
+ if (children.size() != 2)
+ return nullptr;
+
+ return children[1].get();
+}
+
+AccessibilityObject* AccessibilityMathMLElement::mathUnderObject()
+{
+ if (!isMathUnderOver() || !node())
+ return nullptr;
+
+ const auto& children = this->children();
+ if (children.size() < 2)
+ return nullptr;
+
+ if (node()->hasTagName(MathMLNames::munderTag) || node()->hasTagName(MathMLNames::munderoverTag))
+ return children[1].get();
+
+ return nullptr;
+}
+
+AccessibilityObject* AccessibilityMathMLElement::mathOverObject()
+{
+ if (!isMathUnderOver() || !node())
+ return nullptr;
+
+ const auto& children = this->children();
+ if (children.size() < 2)
+ return nullptr;
+
+ if (node()->hasTagName(MathMLNames::moverTag))
+ return children[1].get();
+ if (node()->hasTagName(MathMLNames::munderoverTag))
+ return children[2].get();
+
+ return nullptr;
+}
+
+AccessibilityObject* AccessibilityMathMLElement::mathBaseObject()
+{
+ if (!isMathSubscriptSuperscript() && !isMathUnderOver() && !isMathMultiscript())
+ return nullptr;
+
+ const auto& children = this->children();
+ // The base object in question is always the first child.
+ if (children.size() > 0)
+ return children[0].get();
+
+ return nullptr;
+}
+
+AccessibilityObject* AccessibilityMathMLElement::mathSubscriptObject()
+{
+ if (!isMathSubscriptSuperscript() || !node())
+ return nullptr;
+
+ const auto& children = this->children();
+ if (children.size() < 2)
+ return nullptr;
+
+ if (node()->hasTagName(MathMLNames::msubTag) || node()->hasTagName(MathMLNames::msubsupTag))
+ return children[1].get();
+
+ return nullptr;
+}
+
+AccessibilityObject* AccessibilityMathMLElement::mathSuperscriptObject()
+{
+ if (!isMathSubscriptSuperscript() || !node())
+ return nullptr;
+
+ const auto& children = this->children();
+ unsigned count = children.size();
+
+ if (count >= 2 && node()->hasTagName(MathMLNames::msupTag))
+ return children[1].get();
+
+ if (count >= 3 && node()->hasTagName(MathMLNames::msubsupTag))
+ return children[2].get();
+
+ return nullptr;
+}
+
+String AccessibilityMathMLElement::mathFencedOpenString() const
+{
+ if (!isMathFenced())
+ return String();
+
+ return getAttribute(MathMLNames::openAttr);
+}
+
+String AccessibilityMathMLElement::mathFencedCloseString() const
+{
+ if (!isMathFenced())
+ return String();
+
+ return getAttribute(MathMLNames::closeAttr);
+}
+
+void AccessibilityMathMLElement::mathPrescripts(AccessibilityMathMultiscriptPairs& prescripts)
+{
+ if (!isMathMultiscript() || !node())
+ return;
+
+ bool foundPrescript = false;
+ std::pair<AccessibilityObject*, AccessibilityObject*> prescriptPair;
+ for (Node* child = node()->firstChild(); child; child = child->nextSibling()) {
+ if (foundPrescript) {
+ AccessibilityObject* axChild = axObjectCache()->getOrCreate(child);
+ if (axChild && axChild->isMathElement()) {
+ if (!prescriptPair.first)
+ prescriptPair.first = axChild;
+ else {
+ prescriptPair.second = axChild;
+ prescripts.append(prescriptPair);
+ prescriptPair.first = nullptr;
+ prescriptPair.second = nullptr;
+ }
+ }
+ } else if (child->hasTagName(MathMLNames::mprescriptsTag))
+ foundPrescript = true;
+ }
+
+ // Handle the odd number of pre scripts case.
+ if (prescriptPair.first)
+ prescripts.append(prescriptPair);
+}
+
+void AccessibilityMathMLElement::mathPostscripts(AccessibilityMathMultiscriptPairs& postscripts)
+{
+ if (!isMathMultiscript() || !node())
+ return;
+
+ // In Multiscripts, the post-script elements start after the first element (which is the base)
+ // and continue until a <mprescripts> tag is found
+ std::pair<AccessibilityObject*, AccessibilityObject*> postscriptPair;
+ bool foundBaseElement = false;
+ for (Node* child = node()->firstChild(); child; child = child->nextSibling()) {
+ if (child->hasTagName(MathMLNames::mprescriptsTag))
+ break;
+
+ AccessibilityObject* axChild = axObjectCache()->getOrCreate(child);
+ if (axChild && axChild->isMathElement()) {
+ if (!foundBaseElement)
+ foundBaseElement = true;
+ else if (!postscriptPair.first)
+ postscriptPair.first = axChild;
+ else {
+ postscriptPair.second = axChild;
+ postscripts.append(postscriptPair);
+ postscriptPair.first = nullptr;
+ postscriptPair.second = nullptr;
+ }
+ }
+ }
+
+ // Handle the odd number of post scripts case.
+ if (postscriptPair.first)
+ postscripts.append(postscriptPair);
+}
+
+int AccessibilityMathMLElement::mathLineThickness() const
+{
+ if (!is<RenderMathMLFraction>(m_renderer))
+ return -1;
+
+ return downcast<RenderMathMLFraction>(*m_renderer).relativeLineThickness();
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(MATHML)
diff --git a/Source/WebCore/accessibility/AccessibilityMathMLElement.h b/Source/WebCore/accessibility/AccessibilityMathMLElement.h
new file mode 100644
index 000000000..580b859cb
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityMathMLElement.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2016 Igalia, S.L.
+ * 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * OWNER 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.
+ */
+
+#pragma once
+
+#if ENABLE(MATHML)
+
+#include "AccessibilityRenderObject.h"
+#include "RenderMathMLBlock.h"
+#include "RenderMathMLFraction.h"
+#include "RenderMathMLMath.h"
+#include "RenderMathMLOperator.h"
+#include "RenderMathMLRoot.h"
+
+namespace WebCore {
+
+class AccessibilityMathMLElement : public AccessibilityRenderObject {
+
+public:
+ static Ref<AccessibilityMathMLElement> create(RenderObject*, bool isAnonymousOperator);
+ virtual ~AccessibilityMathMLElement();
+
+protected:
+ explicit AccessibilityMathMLElement(RenderObject*, bool isAnonymousOperator);
+
+private:
+ AccessibilityRole determineAccessibilityRole() final;
+ String textUnderElement(AccessibilityTextUnderElementMode = AccessibilityTextUnderElementMode()) const override;
+ String stringValue() const override;
+ bool isIgnoredElementWithinMathTree() const final;
+
+ bool isMathElement() const final { return true; }
+
+ bool isMathFraction() const override;
+ bool isMathFenced() const override;
+ bool isMathSubscriptSuperscript() const override;
+ bool isMathRow() const override;
+ bool isMathUnderOver() const override;
+ bool isMathRoot() const override;
+ bool isMathSquareRoot() const override;
+ bool isMathText() const override;
+ bool isMathNumber() const override;
+ bool isMathOperator() const override;
+ bool isMathFenceOperator() const override;
+ bool isMathSeparatorOperator() const override;
+ bool isMathIdentifier() const override;
+ bool isMathTable() const override;
+ bool isMathTableRow() const override;
+ bool isMathTableCell() const override;
+ bool isMathMultiscript() const override;
+ bool isMathToken() const override;
+ bool isMathScriptObject(AccessibilityMathScriptObjectType) const override;
+ bool isMathMultiscriptObject(AccessibilityMathMultiscriptObjectType) const override;
+
+ // Generic components.
+ AccessibilityObject* mathBaseObject() override;
+
+ // Root components.
+ AccessibilityObject* mathRadicandObject() override;
+ AccessibilityObject* mathRootIndexObject() override;
+
+ // Fraction components.
+ AccessibilityObject* mathNumeratorObject() override;
+ AccessibilityObject* mathDenominatorObject() override;
+
+ // Under over components.
+ AccessibilityObject* mathUnderObject() override;
+ AccessibilityObject* mathOverObject() override;
+
+ // Subscript/superscript components.
+ AccessibilityObject* mathSubscriptObject() override;
+ AccessibilityObject* mathSuperscriptObject() override;
+
+ // Fenced components.
+ String mathFencedOpenString() const override;
+ String mathFencedCloseString() const override;
+ int mathLineThickness() const override;
+ bool isAnonymousMathOperator() const override;
+
+ // Multiscripts components.
+ void mathPrescripts(AccessibilityMathMultiscriptPairs&) override;
+ void mathPostscripts(AccessibilityMathMultiscriptPairs&) override;
+
+ bool m_isAnonymousOperator;
+};
+
+} // namespace WebCore
+
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilityMathMLElement, isMathElement())
+
+#endif // ENABLE(MATHML)
diff --git a/Source/WebCore/accessibility/AccessibilityMediaControls.cpp b/Source/WebCore/accessibility/AccessibilityMediaControls.cpp
index 92fe22104..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);
@@ -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
@@ -242,10 +243,10 @@ bool AccessibilityMediaControlsContainer::controllingVideoElement() const
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;
@@ -265,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);
}
@@ -295,9 +296,9 @@ 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
@@ -313,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 c729609a0..07ff9cb33 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.
*
@@ -26,9 +26,7 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-
-#ifndef AccessibilityMediaControls_h
-#define AccessibilityMediaControls_h
+#pragma once
#if ENABLE(VIDEO)
@@ -38,84 +36,77 @@
namespace WebCore {
class AccessibilityMediaControl : public AccessibilityRenderObject {
-
public:
- static PassRefPtr<AccessibilityObject> create(RenderObject*);
+ static Ref<AccessibilityObject> create(RenderObject*);
virtual ~AccessibilityMediaControl() { }
- virtual AccessibilityRole roleValue() const override;
+ AccessibilityRole roleValue() const override;
- virtual String title() const override;
- virtual String accessibilityDescription() const override;
- virtual String helpText() const override;
+ String title() const override;
+ String accessibilityDescription() const override;
+ String helpText() const override;
protected:
explicit AccessibilityMediaControl(RenderObject*);
MediaControlElementType controlType() const;
- String controlTypeName() const;
- virtual bool computeAccessibilityIsIgnored() const override;
+ const String& controlTypeName() const;
+ bool computeAccessibilityIsIgnored() const override;
private:
- virtual void accessibilityText(Vector<AccessibilityText>&) override;
+ 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 override { return true; }
-
- virtual String helpText() const override;
- virtual String valueDescription() const override;
+ String helpText() const override;
+ String valueDescription() const override;
const AtomicString& getAttribute(const QualifiedName& attribute) const;
private:
explicit AccessibilityMediaTimeline(RenderObject*);
-};
+ 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 override { return ToolbarRole; }
+ AccessibilityRole roleValue() const override { return ToolbarRole; }
- virtual String helpText() const override;
- virtual String accessibilityDescription() const override;
+ String helpText() const override;
+ String accessibilityDescription() const override;
private:
explicit AccessibilityMediaControlsContainer(RenderObject*);
bool controllingVideoElement() const;
- const String elementTypeName() const;
- virtual bool computeAccessibilityIsIgnored() const override;
+ const String& elementTypeName() const;
+ 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 override { return ApplicationTimerRole; }
+ AccessibilityRole roleValue() const override { return ApplicationTimerRole; }
- virtual String stringValue() const override;
- virtual String accessibilityDescription() const override;
+ String stringValue() const override;
+ String accessibilityDescription() const override;
private:
explicit AccessibilityMediaTimeDisplay(RenderObject*);
- virtual bool isMediaControlLabel() const override { return true; }
- virtual bool computeAccessibilityIsIgnored() const override;
+ bool isMediaControlLabel() const override { return true; }
+ bool computeAccessibilityIsIgnored() const override;
};
-
} // namespace WebCore
#endif // ENABLE(VIDEO)
-
-#endif // AccessibilityMediaControls_h
diff --git a/Source/WebCore/accessibility/AccessibilityMenuList.cpp b/Source/WebCore/accessibility/AccessibilityMenuList.cpp
index bc5364c20..c0e5cf00e 100644
--- a/Source/WebCore/accessibility/AccessibilityMenuList.cpp
+++ b/Source/WebCore/accessibility/AccessibilityMenuList.cpp
@@ -37,12 +37,12 @@ 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);
@@ -58,20 +58,24 @@ bool AccessibilityMenuList::press() const
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;
- toAccessibilityMockObject(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();
@@ -100,7 +104,7 @@ bool AccessibilityMenuList::canSetFocusAttribute() const
if (!node())
return false;
- return !toElement(node())->isDisabledFormControl();
+ return !downcast<Element>(*node()).isDisabledFormControl();
}
void AccessibilityMenuList::didUpdateActiveOption(int optionIndex)
@@ -111,15 +115,21 @@ void AccessibilityMenuList::didUpdateActiveOption(int optionIndex)
const auto& childObjects = children();
if (!childObjects.isEmpty()) {
ASSERT(childObjects.size() == 1);
- ASSERT(childObjects[0]->isMenuListPopup());
-
- if (childObjects[0]->isMenuListPopup()) {
- if (AccessibilityMenuListPopup* popup = toAccessibilityMenuListPopup(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, TargetElement, 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 01d5885d3..8f1123d34 100644
--- a/Source/WebCore/accessibility/AccessibilityMenuList.h
+++ b/Source/WebCore/accessibility/AccessibilityMenuList.h
@@ -23,40 +23,34 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AccessibilityMenuList_h
-#define AccessibilityMenuList_h
+#pragma once
#include "AccessibilityRenderObject.h"
namespace WebCore {
-class AccessibilityMenuList;
-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 override;
- virtual bool press() const override;
+ bool isCollapsed() const override;
+ bool press() override;
void didUpdateActiveOption(int optionIndex);
private:
explicit AccessibilityMenuList(RenderMenuList*);
- virtual bool isMenuList() const override { return true; }
- virtual AccessibilityRole roleValue() const override { return PopUpButtonRole; }
- virtual bool canSetFocusAttribute() const override;
+ bool isMenuList() const override { return true; }
+ AccessibilityRole roleValue() const override { return PopUpButtonRole; }
+ bool canSetFocusAttribute() const override;
- virtual void addChildren() override;
- virtual void childrenChanged() override;
+ void addChildren() override;
+ void childrenChanged() override;
};
-ACCESSIBILITY_OBJECT_TYPE_CASTS(AccessibilityMenuList, isMenuList())
-
} // namespace WebCore
-#endif // AccessibilityMenuList_h
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilityMenuList, isMenuList())
diff --git a/Source/WebCore/accessibility/AccessibilityMenuListOption.cpp b/Source/WebCore/accessibility/AccessibilityMenuListOption.cpp
index 296fc71bc..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
@@ -118,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 6bee58b18..4111e07b9 100644
--- a/Source/WebCore/accessibility/AccessibilityMenuListOption.h
+++ b/Source/WebCore/accessibility/AccessibilityMenuListOption.h
@@ -23,47 +23,43 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AccessibilityMenuListOption_h
-#define AccessibilityMenuListOption_h
+#pragma once
#include "AccessibilityMockObject.h"
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 override { return true; }
+ bool isMenuListOption() const override { return true; }
- virtual AccessibilityRole roleValue() const override { return MenuListOptionRole; }
- virtual bool canHaveChildren() const override { return false; }
+ AccessibilityRole roleValue() const override { return MenuListOptionRole; }
+ bool canHaveChildren() const override { return false; }
- 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;
+ Element* actionElement() const override;
+ bool isEnabled() const override;
+ bool isVisible() const override;
+ bool isOffScreen() const override;
+ bool isSelected() const override;
+ String nameForMSAA() const override;
+ void setSelected(bool) override;
+ bool canSetSelectedAttribute() const override;
+ LayoutRect elementRect() const override;
+ String stringValue() const override;
+ bool computeAccessibilityIsIgnored() const override;
RefPtr<HTMLElement> m_element;
};
-ACCESSIBILITY_OBJECT_TYPE_CASTS(AccessibilityMenuListOption, isMenuListOption())
-
} // namespace WebCore
-#endif // AccessibilityMenuListOption_h
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilityMenuListOption, isMenuListOption())
diff --git a/Source/WebCore/accessibility/AccessibilityMenuListPopup.cpp b/Source/WebCore/accessibility/AccessibilityMenuListPopup.cpp
index 8890752a9..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) || !element->inRenderedDocument())
- 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 = toAccessibilityMenuListOption(object);
- option->setElement(element);
-
- return option;
+ return &option;
}
-bool AccessibilityMenuListPopup::press() const
+bool AccessibilityMenuListPopup::press()
{
if (!m_parent)
return false;
@@ -102,7 +99,7 @@ void AccessibilityMenuListPopup::addChildren()
m_haveChildren = true;
- for (const auto& listItem : toHTMLSelectElement(selectNode)->listItems()) {
+ for (const auto& listItem : downcast<HTMLSelectElement>(*selectNode).listItems()) {
AccessibilityMenuListOption* option = menuListOptionAccessibilityObject(listItem);
if (option) {
option->setParent(this);
diff --git a/Source/WebCore/accessibility/AccessibilityMenuListPopup.h b/Source/WebCore/accessibility/AccessibilityMenuListPopup.h
index 9e20933e3..c1987cd3a 100644
--- a/Source/WebCore/accessibility/AccessibilityMenuListPopup.h
+++ b/Source/WebCore/accessibility/AccessibilityMenuListPopup.h
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AccessibilityMenuListPopup_h
-#define AccessibilityMenuListPopup_h
+#pragma once
#include "AccessibilityMockObject.h"
@@ -33,37 +32,33 @@ namespace WebCore {
class AccessibilityMenuList;
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 override;
- virtual bool isOffScreen() const override;
+ bool isEnabled() const override;
+ bool isOffScreen() const override;
void didUpdateActiveOption(int optionIndex);
-
private:
AccessibilityMenuListPopup();
- virtual bool isMenuListPopup() const override { return true; }
+ bool isMenuListPopup() const override { return true; }
- virtual LayoutRect elementRect() const override { return LayoutRect(); }
- virtual AccessibilityRole roleValue() const override { return MenuListPopupRole; }
+ LayoutRect elementRect() const override { return LayoutRect(); }
+ AccessibilityRole roleValue() const override { return MenuListPopupRole; }
- virtual bool isVisible() const override;
- virtual bool press() const override;
- virtual void addChildren() override;
- virtual void childrenChanged() override;
- virtual bool computeAccessibilityIsIgnored() const override;
+ bool isVisible() const override;
+ bool press() override;
+ void addChildren() override;
+ void childrenChanged() override;
+ bool computeAccessibilityIsIgnored() const override;
AccessibilityMenuListOption* menuListOptionAccessibilityObject(HTMLElement*) const;
};
-ACCESSIBILITY_OBJECT_TYPE_CASTS(AccessibilityMenuListPopup, isMenuListPopup())
-
} // namespace WebCore
-#endif // AccessibilityMenuListPopup_h
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilityMenuListPopup, isMenuListPopup())
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 985a21982..668f889d4 100644
--- a/Source/WebCore/accessibility/AccessibilityMockObject.h
+++ b/Source/WebCore/accessibility/AccessibilityMockObject.h
@@ -23,8 +23,7 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AccessibilityMockObject_h
-#define AccessibilityMockObject_h
+#pragma once
#include "AccessibilityObject.h"
@@ -37,24 +36,23 @@ protected:
public:
virtual ~AccessibilityMockObject();
- virtual AccessibilityObject* parentObject() const override { return m_parent; }
- virtual void setParent(AccessibilityObject* parent) { m_parent = parent; };
- virtual bool isEnabled() const override { return true; }
+ AccessibilityObject* parentObject() const override { return m_parent; }
+ virtual void setParent(AccessibilityObject* parent) { m_parent = parent; }
+ 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 = 0; }
+ void detachFromParent() override { m_parent = nullptr; }
private:
- virtual bool isMockObject() const override { return true; }
+ bool isMockObject() const final { return true; }
+ bool isDetachedFromParent() override { return !m_parent; }
- virtual bool computeAccessibilityIsIgnored() const override;
-};
+ bool computeAccessibilityIsIgnored() const override;
+};
-ACCESSIBILITY_OBJECT_TYPE_CASTS(AccessibilityMockObject, isMockObject())
-
} // namespace WebCore
-#endif // AccessibilityMockObject_h
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilityMockObject, isMockObject())
diff --git a/Source/WebCore/accessibility/AccessibilityNodeObject.cpp b/Source/WebCore/accessibility/AccessibilityNodeObject.cpp
index 8969dba6f..4bce0b09c 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,6 +31,7 @@
#include "AXObjectCache.h"
#include "AccessibilityImageMapLink.h"
+#include "AccessibilityList.h"
#include "AccessibilityListBox.h"
#include "AccessibilitySpinButton.h"
#include "AccessibilityTable.h"
@@ -41,40 +42,31 @@
#include "FrameLoader.h"
#include "FrameSelection.h"
#include "FrameView.h"
-#include "HTMLAreaElement.h"
+#include "HTMLCanvasElement.h"
+#include "HTMLDetailsElement.h"
#include "HTMLFieldSetElement.h"
#include "HTMLFormElement.h"
-#include "HTMLFrameElementBase.h"
#include "HTMLImageElement.h"
#include "HTMLInputElement.h"
#include "HTMLLabelElement.h"
#include "HTMLLegendElement.h"
-#include "HTMLMapElement.h"
#include "HTMLNames.h"
-#include "HTMLOptGroupElement.h"
-#include "HTMLOptionElement.h"
-#include "HTMLOptionsCollection.h"
#include "HTMLParserIdioms.h"
-#include "HTMLPlugInImageElement.h"
#include "HTMLSelectElement.h"
#include "HTMLTextAreaElement.h"
#include "HTMLTextFormControlElement.h"
-#include "HitTestRequest.h"
-#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 "Text.h"
#include "TextControlInnerElements.h"
-#include "TextIterator.h"
#include "UserGestureIndicator.h"
#include "VisibleUnits.h"
#include "Widget.h"
@@ -87,7 +79,7 @@ namespace WebCore {
using namespace HTMLNames;
-static String accessibleNameForNode(Node*);
+static String accessibleNameForNode(Node* node, Node* labelledbyNode = nullptr);
AccessibilityNodeObject::AccessibilityNodeObject(Node* node)
: AccessibilityObject()
@@ -115,16 +107,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(AccessibilityDetachmentType detachmentType, AXObjectCache* cache)
{
// AccessibilityObject calls clearChildren.
AccessibilityObject::detach(detachmentType, cache);
- m_node = 0;
+ m_node = nullptr;
}
void AccessibilityNodeObject::childrenChanged()
@@ -149,11 +141,14 @@ 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())
- cache->postNotification(parent, parent->document(), AXObjectCache::AXLiveRegionChanged);
+ cache->postLiveRegionChangeNotification(parent);
// If this element is an ARIA text control, notify the AT of changes.
- if (parent->isARIATextControl() && !parent->isNativeTextControl() && !parent->node()->hasEditableStyle())
+ if (parent->isNonNativeTextControl())
cache->postNotification(parent, parent->document(), AXObjectCache::AXValueChanged);
}
}
@@ -171,12 +166,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);
}
@@ -184,11 +179,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);
}
@@ -196,11 +191,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);
}
@@ -208,11 +203,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);
}
@@ -225,20 +220,23 @@ 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
{
return boundingBoxRect();
}
-
+
LayoutRect AccessibilityNodeObject::boundingBoxRect() const
{
// AccessibilityNodeObjects have no mechanism yet to return a size or position.
@@ -267,7 +265,7 @@ void AccessibilityNodeObject::setNode(Node* node)
Document* AccessibilityNodeObject::document() const
{
if (!node())
- return 0;
+ return nullptr;
return &node()->document();
}
@@ -276,52 +274,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;
@@ -397,11 +395,8 @@ bool AccessibilityNodeObject::canHaveChildren() const
case ListBoxOptionRole:
case ScrollBarRole:
case ProgressIndicatorRole:
+ case SwitchRole:
return false;
- case LegendRole:
- if (Element* element = this->element())
- return !ancestorsOfType<HTMLFieldSetElement>(*element).first();
- FALLTHROUGH;
default:
return true;
}
@@ -426,20 +421,28 @@ bool AccessibilityNodeObject::computeAccessibilityIsIgnored() const
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;
- Element& canvasElement = toElement(*node);
+ 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.
@@ -451,23 +454,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;
@@ -479,12 +477,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,
@@ -496,7 +495,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;
@@ -509,15 +508,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;
@@ -530,29 +529,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();
+}
- return inputElement->isPasswordField();
+AccessibilityObject* AccessibilityNodeObject::passwordFieldOrContainingPasswordField()
+{
+ Node* node = this->node();
+ if (!node)
+ return nullptr;
+
+ 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;
@@ -613,14 +624,11 @@ bool AccessibilityNodeObject::isMenuItem() const
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
@@ -628,30 +636,25 @@ bool AccessibilityNodeObject::isEnabled() const
// 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 (equalIgnoringCase(disabledStatus, "true"))
+ if (equalLettersIgnoringASCIICase(disabledStatus, "true"))
return false;
- if (equalIgnoringCase(disabledStatus, "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();
+ return equalLettersIgnoringASCIICase(getAttribute(indeterminateAttr), "true");
}
bool AccessibilityNodeObject::isPressed() const
@@ -663,16 +666,13 @@ bool AccessibilityNodeObject::isPressed() const
if (!node)
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"))
- return true;
- return false;
- }
+ // If this is an toggle button, check the aria-pressed attribute rather than node()->active()
+ if (isToggleButton())
+ return equalLettersIgnoringASCIICase(getAttribute(aria_pressedAttr), "true");
- if (!node->isElementNode())
+ if (!is<Element>(*node))
return false;
- return toElement(node)->active();
+ return downcast<Element>(*node).active();
}
bool AccessibilityNodeObject::isChecked() const
@@ -682,9 +682,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;
@@ -694,13 +693,14 @@ bool AccessibilityNodeObject::isChecked() const
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;
@@ -709,53 +709,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->hasEditableStyle();
+ return node() && node()->hasTagName(selectTag) && downcast<HTMLSelectElement>(*node()).multiple();
}
bool AccessibilityNodeObject::isRequired() const
{
// Explicit aria-required values should trump native required attributes.
const AtomicString& requiredValue = getAttribute(aria_requiredAttr);
- if (equalIgnoringCase(requiredValue, "true"))
+ if (equalLettersIgnoringASCIICase(requiredValue, "true"))
return true;
- if (equalIgnoringCase(requiredValue, "false"))
+ if (equalLettersIgnoringASCIICase(requiredValue, "false"))
return false;
Node* n = this->node();
- if (n && (n->isElementNode() && toElement(n)->isFormControlElement()))
- return toHTMLFormControlElement(n)->isRequired();
+ if (is<HTMLFormControlElement>(n))
+ return downcast<HTMLFormControlElement>(*n).isRequired();
return false;
}
@@ -766,9 +745,11 @@ bool AccessibilityNodeObject::supportsRequiredAttribute() const
case ButtonRole:
return isFileUploadButton();
case CellRole:
+ case ColumnHeaderRole:
case CheckBoxRole:
case ComboBoxRole:
case GridRole:
+ case GridCellRole:
case IncrementorRole:
case ListBoxRole:
case PopUpButtonRole:
@@ -818,6 +799,11 @@ int AccessibilityNodeObject::headingLevel() const
if (node->hasTagName(h6Tag))
return 6;
+ // The implicit value of aria-level is 2 for the heading role.
+ // https://www.w3.org/TR/wai-aria-1.1/#heading
+ if (ariaRoleAttribute() == HeadingRole)
+ return 2;
+
return 0;
}
@@ -831,44 +817,62 @@ 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())
return 0.0f;
- return getAttribute(aria_valuenowAttr).toFloat();
+ // In ARIA 1.1, the implicit value for aria-valuenow on a spin button is 0.
+ // For other roles, it is half way between aria-valuemin and aria-valuemax.
+ auto value = getAttribute(aria_valuenowAttr);
+ if (!value.isEmpty())
+ return value.toFloat();
+
+ return isSpinButton() ? 0 : (minValueForRange() + maxValueForRange()) / 2;
}
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())
return 0.0f;
- return getAttribute(aria_valuemaxAttr).toFloat();
+ auto value = getAttribute(aria_valuemaxAttr);
+ if (!value.isEmpty())
+ return value.toFloat();
+
+ // In ARIA 1.1, the implicit value for aria-valuemax on a spin button
+ // is that there is no maximum value. For other roles, it is 100.
+ return isSpinButton() ? std::numeric_limits<float>::max() : 100.0f;
}
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())
return 0.0f;
- return getAttribute(aria_valueminAttr).toFloat();
+ auto value = getAttribute(aria_valueminAttr);
+ if (!value.isEmpty())
+ return value.toFloat();
+
+ // In ARIA 1.1, the implicit value for aria-valuemin on a spin button
+ // is that there is no minimum value. For other roles, it is 0.
+ return isSpinButton() ? -std::numeric_limits<float>::max() : 0.0f;
}
float AccessibilityNodeObject::stepValueForRange() const
@@ -892,8 +896,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
@@ -928,12 +931,13 @@ AccessibilityObject* AccessibilityNodeObject::selectedTabItem()
if (!isTabList())
return nullptr;
+ // FIXME: Is this valid? ARIA tab items support aria-selected; not aria-checked.
// Find the child tab item that is selected (ie. the intValue == 1).
AccessibilityObject::AccessibilityChildrenVector tabs;
tabChildren(tabs);
for (const auto& child : children()) {
- if (child->isTabItem() && child->isChecked())
+ if (child->isTabItem() && (child->isChecked() || child->isSelected()))
return child.get();
}
return nullptr;
@@ -942,7 +946,7 @@ AccessibilityObject* AccessibilityNodeObject::selectedTabItem()
AccessibilityButtonState AccessibilityNodeObject::checkboxOrRadioValue() const
{
if (isNativeCheckboxOrRadio())
- return isChecked() ? ButtonStateOn : ButtonStateOff;
+ return isIndeterminate() ? ButtonStateMixed : isChecked() ? ButtonStateOn : ButtonStateOff;
return AccessibilityObject::checkboxOrRadioValue();
}
@@ -951,25 +955,25 @@ 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 (isHTMLInputElement(node)) {
- HTMLInputElement* input = toHTMLInputElement(node);
- if (!input->isDisabledFormControl() && (input->isRadioButton() || input->isCheckbox() || input->isTextButton() || input->isFileUpload() || input->isImageButton()))
+ 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;
@@ -980,7 +984,7 @@ static bool isNodeActionElement(Node* node)
static Element* nativeActionElement(Node* start)
{
if (!start)
- return 0;
+ 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).
@@ -988,25 +992,25 @@ static Element* nativeActionElement(Node* start)
for (Node* child = start->firstChild(); child; child = child->nextSibling()) {
if (isNodeActionElement(child))
- return toElement(child);
+ return downcast<Element>(child);
if (Element* subChild = nativeActionElement(child))
return subChild;
}
- return 0;
+ return nullptr;
}
Element* AccessibilityNodeObject::actionElement() const
{
Node* node = this->node();
if (!node)
- return 0;
+ return nullptr;
if (isNodeActionElement(node))
- return toElement(node);
+ return downcast<Element>(node);
if (AccessibilityObject::isARIAInput(ariaRoleAttribute()))
- return toElement(node);
+ return downcast<Element>(node);
switch (roleValue()) {
case ButtonRole:
@@ -1020,7 +1024,7 @@ Element* AccessibilityNodeObject::actionElement() const
// Check if the author is hiding the real control element inside the ARIA element.
if (Element* nativeElement = nativeActionElement(node))
return nativeElement;
- return toElement(node);
+ return downcast<Element>(node);
default:
break;
}
@@ -1031,25 +1035,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
// FIXME: Do the continuation search like anchorElement does
- for (auto& element : elementLineage(node->isElementNode() ? toElement(node) : node->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;
}
- return 0;
+ return nullptr;
}
bool AccessibilityNodeObject::isDescendantOfBarrenParent() const
@@ -1075,13 +1079,13 @@ void AccessibilityNodeObject::alterSliderValue(bool increase)
void AccessibilityNodeObject::increment()
{
- UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture);
+ UserGestureIndicator gestureIndicator(ProcessingUserGesture, document());
alterSliderValue(true);
}
void AccessibilityNodeObject::decrement()
{
- UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture);
+ UserGestureIndicator gestureIndicator(ProcessingUserGesture, document());
alterSliderValue(false);
}
@@ -1154,8 +1158,8 @@ 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()) {
@@ -1179,15 +1183,16 @@ String AccessibilityNodeObject::ariaAccessibilityDescription() const
return String();
}
-static Element* siblingWithAriaRole(String role, Node* node)
+static Element* siblingWithAriaRole(Node* node, const char* role)
{
+ // 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 nullptr;
for (auto& sibling : childrenOfType<Element>(*parent)) {
- const AtomicString& siblingAriaRole = sibling.fastGetAttribute(roleAttr);
- if (equalIgnoringCase(siblingAriaRole, role))
+ // FIXME: Should skip sibling that is the same as the node.
+ if (equalIgnoringASCIICase(sibling.attributeWithoutSynchronization(roleAttr), role))
return &sibling;
}
@@ -1199,39 +1204,89 @@ Element* AccessibilityNodeObject::menuElementForMenuButton() const
if (ariaRoleAttribute() != MenuButtonRole)
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;
+}
+
+AccessibilityObject* AccessibilityNodeObject::captionForFigure() const
+{
+ if (!isFigure())
+ return nullptr;
+
+ AXObjectCache* cache = axObjectCache();
+ if (!cache)
+ return nullptr;
+
+ Node* node = this->node();
+ for (Node* child = node->firstChild(); child; child = child->nextSibling()) {
+ if (child->hasTagName(figcaptionTag))
+ return cache->getOrCreate(child);
+ }
+ return nullptr;
}
bool AccessibilityNodeObject::usesAltTagForTextComputation() const
{
return isImage() || isInputImage() || isNativeImage() || isCanvas() || (node() && node()->hasTagName(imgTag));
}
+
+bool AccessibilityNodeObject::isLabelable() const
+{
+ Node* node = this->node();
+ if (!node)
+ return false;
+
+ return is<HTMLInputElement>(*node) || AccessibilityObject::isARIAInput(ariaRoleAttribute()) || isControl() || isProgressIndicator() || isMeter();
+}
+
+String AccessibilityNodeObject::textForLabelElement(Element* element) const
+{
+ String result = String();
+ if (!is<HTMLLabelElement>(*element))
+ return result;
+
+ HTMLLabelElement* label = downcast<HTMLLabelElement>(element);
+ // Check to see if there's aria-labelledby attribute on the label element.
+ if (AccessibilityObject* labelObject = axObjectCache()->getOrCreate(label))
+ result = labelObject->ariaLabeledByAttribute();
+
+ // Then check for aria-label attribute.
+ if (result.isEmpty())
+ result = label->attributeWithoutSynchronization(aria_labelAttr);
+
+ return !result.isEmpty() ? result : label->innerText();
+}
void AccessibilityNodeObject::titleElementText(Vector<AccessibilityText>& textOrder) const
{
@@ -1239,15 +1294,14 @@ void AccessibilityNodeObject::titleElementText(Vector<AccessibilityText>& textOr
if (!node)
return;
- bool isInputTag = isHTMLInputElement(node);
- if (isInputTag || AccessibilityObject::isARIAInput(ariaRoleAttribute()) || isControl()) {
- HTMLLabelElement* label = labelForElement(toElement(node));
- if (label) {
+ if (isLabelable()) {
+ if (HTMLLabelElement* label = labelForElement(downcast<Element>(node))) {
AccessibilityObject* labelObject = axObjectCache()->getOrCreate(label);
- String innerText = label->innerText();
+ String innerText = textForLabelElement(label);
+
// Only use the <label> text if there's no ARIA override.
if (!innerText.isEmpty() && !ariaAccessibilityDescription())
- textOrder.append(AccessibilityText(innerText, LabelByElementText, labelObject));
+ textOrder.append(AccessibilityText(innerText, isMeter() ? AlternativeText : LabelByElementText, labelObject));
return;
}
}
@@ -1273,8 +1327,8 @@ void AccessibilityNodeObject::alternativeText(Vector<AccessibilityText>& textOrd
textOrder.append(AccessibilityText(ariaLabel, AlternativeText));
if (usesAltTagForTextComputation()) {
- if (renderer() && renderer()->isRenderImage()) {
- String renderAltText = toRenderImage(renderer())->altText();
+ 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)) {
@@ -1294,17 +1348,22 @@ void AccessibilityNodeObject::alternativeText(Vector<AccessibilityText>& textOrd
return;
// The fieldset element derives its alternative text from the first associated legend element if one is available.
- if (isHTMLFieldSetElement(node)) {
- AccessibilityObject* object = axObjectCache()->getOrCreate(toHTMLFieldSetElement(node)->legend());
+ if (is<HTMLFieldSetElement>(*node)) {
+ AccessibilityObject* object = axObjectCache()->getOrCreate(downcast<HTMLFieldSetElement>(*node).legend());
if (object && !object->isHidden())
textOrder.append(AccessibilityText(accessibleNameForNode(object->node()), AlternativeText));
}
-#if ENABLE(SVG)
- // SVG elements all can have a <svg:title> element inside which should act as the descriptive text.
- if (node->isSVGElement())
- textOrder.append(AccessibilityText(toSVGElement(node)->title(), AlternativeText));
-#endif
+ // The figure element derives its alternative text from the first associated figcaption element if one is available.
+ if (isFigure()) {
+ AccessibilityObject* captionForFigure = this->captionForFigure();
+ if (captionForFigure && !captionForFigure->isHidden())
+ textOrder.append(AccessibilityText(accessibleNameForNode(captionForFigure->node()), AlternativeText));
+ }
+
+ // Tree items missing a label are labeled by all child elements.
+ if (isTreeItem() && ariaLabel.isEmpty() && ariaLabeledByAttribute().isEmpty())
+ textOrder.append(AccessibilityText(accessibleNameForNode(node), AlternativeText));
#if ENABLE(MATHML)
if (node->isMathMLElement())
@@ -1318,11 +1377,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;
}
}
@@ -1344,7 +1403,7 @@ void AccessibilityNodeObject::visibleText(Vector<AccessibilityText>& textOrder)
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(MAC)
+#if !PLATFORM(COCOA)
case ListItemRole:
#endif
case MenuButtonRole:
@@ -1352,8 +1411,8 @@ void AccessibilityNodeObject::visibleText(Vector<AccessibilityText>& textOrder)
case MenuItemCheckboxRole:
case MenuItemRadioRole:
case RadioButtonRole:
+ case SwitchRole:
case TabRole:
- case ProgressIndicatorRole:
useTextUnderElement = true;
break;
default:
@@ -1365,6 +1424,9 @@ void AccessibilityNodeObject::visibleText(Vector<AccessibilityText>& textOrder)
if (isHeading() || isLink())
useTextUnderElement = true;
+ if (isOutput())
+ useTextUnderElement = true;
+
if (useTextUnderElement) {
AccessibilityTextUnderElementMode mode;
@@ -1387,27 +1449,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));
}
}
@@ -1436,7 +1494,7 @@ void AccessibilityNodeObject::ariaLabeledByText(Vector<AccessibilityText>& textO
axElements.append(axElement);
}
- textOrder.append(AccessibilityText(ariaLabeledBy, AlternativeText, axElements));
+ textOrder.append(AccessibilityText(ariaLabeledBy, AlternativeText, WTFMove(axElements)));
}
}
@@ -1458,30 +1516,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->attributeWithoutSynchronization(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->attributeWithoutSynchronization(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();
}
@@ -1503,15 +1557,9 @@ String AccessibilityNodeObject::accessibilityDescription() const
if (!alt.isNull())
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())
- return toSVGElement(m_node)->title();
-#endif
#if ENABLE(MATHML)
- if (m_node && m_node->isMathMLElement())
+ if (is<MathMLElement>(m_node))
return getAttribute(MathMLNames::alttextAttr);
#endif
@@ -1540,21 +1588,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)
@@ -1568,10 +1617,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.attributeWithoutSynchronization(aria_levelAttr);
if (!ariaLevel.isEmpty())
return ariaLevel.toInt();
@@ -1583,7 +1632,7 @@ unsigned AccessibilityNodeObject::hierarchicalLevel() const
// We measure tree hierarchy by the number of groups that the item is within.
unsigned level = 1;
for (AccessibilityObject* parent = parentObject(); parent; parent = parent->parentObject()) {
- AccessibilityRole parentRole = parent->roleValue();
+ AccessibilityRole parentRole = parent->ariaRoleAttribute();
if (parentRole == GroupRole)
level++;
else if (parentRole == TreeRole)
@@ -1593,9 +1642,18 @@ unsigned AccessibilityNodeObject::hierarchicalLevel() const
return level;
}
+void AccessibilityNodeObject::setIsExpanded(bool expand)
+{
+ if (is<HTMLDetailsElement>(node())) {
+ auto& details = downcast<HTMLDetailsElement>(*node());
+ if (expand != details.isOpen())
+ details.toggleOpen();
+ }
+}
+
// 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.childrenInclusion == AccessibilityTextUnderElementMode::TextUnderElementModeIncludeAllChildren)
@@ -1618,7 +1676,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.
@@ -1631,13 +1694,19 @@ static bool shouldUseAccessiblityObjectInnerText(AccessibilityObject* obj, Acces
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, String& childText)
+static bool shouldAddSpaceBeforeAppendingNextElement(StringBuilder& builder, const String& childText)
{
if (!builder.length() || !childText.length())
return false;
@@ -1645,41 +1714,46 @@ static bool shouldAddSpaceBeforeAppendingNextElement(StringBuilder& builder, Str
// 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();
-
- // The render tree should be stable before going ahead. Otherwise, further uses of the
- // TextIterator will force a layout update, potentially altering the accessibility tree
- // and leading to crashes in the loop that computes the result text from the children.
- ASSERT(!document()->renderView()->layoutState());
- ASSERT(!document()->childNeedsStyleRecalc());
+ 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 (shouldAddSpaceBeforeAppendingNextElement(builder, textOrder[0].text))
- builder.append(' ');
- builder.append(textOrder[0].text);
+ appendNameToStringBuilder(builder, textOrder[0].text);
continue;
}
}
-
+
String childText = child->textUnderElement(mode);
- if (childText.length()) {
- if (shouldAddSpaceBeforeAppendingNextElement(builder, childText))
- builder.append(' ');
- builder.append(childText);
- }
+ if (childText.length())
+ appendNameToStringBuilder(builder, childText);
}
return builder.toString().stripWhiteSpace().simplifyWhiteSpace(isHTMLSpaceButNotLineBreak);
@@ -1691,18 +1765,18 @@ 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 (isLabelable()) {
+ 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();
+ return textForLabelElement(label);
}
// If this node isn't rendered, there's no inner text we can extract from a select element.
@@ -1725,6 +1799,7 @@ String AccessibilityNodeObject::title() const
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>.
@@ -1759,13 +1834,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
@@ -1785,16 +1860,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);
+ const AtomicString& overriddenDescription = listItems[selectedIndex]->attributeWithoutSynchronization(aria_labelAttr);
if (!overriddenDescription.isNull())
return overriddenDescription;
}
- if (!selectElement->multiple())
- return selectElement->value();
+ if (!selectElement.multiple())
+ return selectElement.value();
return String();
}
@@ -1814,73 +1889,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->isHTMLElement())
+ ASSERT(node);
+ if (!is<Element>(node))
return String();
- HTMLElement* element = toHTMLElement(node);
-
- const AtomicString& ariaLabel = element->fastGetAttribute(aria_labelAttr);
+ Element& element = downcast<Element>(*node);
+ const AtomicString& ariaLabel = element.attributeWithoutSynchronization(aria_labelAttr);
if (!ariaLabel.isEmpty())
return ariaLabel;
- const AtomicString& alt = element->fastGetAttribute(altAttr);
+ const AtomicString& alt = element.attributeWithoutSynchronization(altAttr);
if (!alt.isEmpty())
return alt;
-
- if (isHTMLInputElement(node))
- return toHTMLInputElement(node)->value();
-
+
// 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);
- String text;
- if (axObject)
- text = axObject->textUnderElement();
- else if (node->isElementNode())
- text = toElement(node)->innerText();
+ 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 (!text.isEmpty())
return text;
- const AtomicString& title = element->fastGetAttribute(titleAttr);
+ const AtomicString& title = element.attributeWithoutSynchronization(titleAttr);
if (!title.isEmpty())
return title;
return String();
}
+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 builder.toString();
+}
+
String AccessibilityNodeObject::accessibilityDescriptionForElements(Vector<Element*> &elements) const
{
StringBuilder builder;
unsigned size = elements.size();
- for (unsigned i = 0; i < size; ++i) {
- if (i)
- builder.append(' ');
-
- builder.append(accessibleNameForNode(elements[i]));
- }
+ for (unsigned i = 0; i < size; ++i)
+ appendNameToStringBuilder(builder, accessibleNameForNode(elements[i], node()));
return builder.toString();
}
@@ -1931,18 +2036,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)
+ // 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
@@ -2008,22 +2161,12 @@ AccessibilityRole AccessibilityNodeObject::remapAriaRoleDueToParent(Accessibilit
return role;
}
-// 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 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:
@@ -2032,6 +2175,9 @@ bool AccessibilityNodeObject::canSetSelectedAttribute() const
case TreeGridRole:
case TreeItemRole:
case TreeRole:
+ case MenuItemCheckboxRole:
+ case MenuItemRadioRole:
+ case MenuItemRole:
return isEnabled();
default:
return false;
diff --git a/Source/WebCore/accessibility/AccessibilityNodeObject.h b/Source/WebCore/accessibility/AccessibilityNodeObject.h
index 28fad801b..37d6eb01f 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.
*
@@ -26,8 +26,7 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AccessibilityNodeObject_h
-#define AccessibilityNodeObject_h
+#pragma once
#include "AccessibilityObject.h"
#include "LayoutRect.h"
@@ -37,123 +36,114 @@ namespace WebCore {
class AXObjectCache;
class Element;
-class Frame;
-class FrameView;
-class HitTestResult;
-class HTMLAnchorElement;
-class HTMLAreaElement;
-class HTMLElement;
class HTMLLabelElement;
-class HTMLMapElement;
-class HTMLSelectElement;
-class IntPoint;
-class IntSize;
class Node;
-class RenderListBox;
-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() override;
-
- virtual bool isAccessibilityNodeObject() const override { return true; }
-
- virtual bool canvasHasFallbackContent() const override;
-
- virtual bool isAnchor() 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;
+ void init() override;
+
+ bool canvasHasFallbackContent() const override;
+
+ bool isControl() const override;
+ bool isFieldset() const override;
+ bool isGroup() const override;
+ bool isHeading() const override;
+ bool isHovered() const override;
+ bool isImage() const override;
+ bool isImageButton() const override;
+ bool isInputImage() const override;
+ bool isLink() const override;
+ bool isMenu() const override;
+ bool isMenuBar() const override;
+ bool isMenuButton() const override;
+ bool isMenuItem() const override;
+ bool isMenuRelated() const override;
+ bool isMultiSelectable() const override;
virtual bool isNativeCheckboxOrRadio() const;
- virtual bool isNativeImage() const override;
- virtual bool isNativeTextControl() const override;
- virtual bool isPasswordField() const 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 isReadOnly() const override;
- virtual bool isRequired() const override;
- virtual bool supportsRequiredAttribute() const override;
-
- virtual bool canSetSelectedAttribute() const override;
+ bool isNativeImage() const override;
+ bool isNativeTextControl() const override;
+ bool isPasswordField() const override;
+ AccessibilityObject* passwordFieldOrContainingPasswordField() override;
+ bool isProgressIndicator() const override;
+ bool isSearchField() const override;
+ bool isSlider() const override;
+
+ bool isChecked() const override;
+ bool isEnabled() const override;
+ bool isIndeterminate() const override;
+ bool isPressed() const override;
+ bool isRequired() const override;
+ bool supportsRequiredAttribute() const override;
+
+ bool canSetSelectedAttribute() const override;
void setNode(Node*);
- virtual Node* node() const override { return m_node; }
- virtual Document* document() const override;
-
- virtual bool canSetFocusAttribute() const override;
- virtual int headingLevel() 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 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 Element* actionElement() const override;
- Element* mouseButtonListener() const;
- virtual Element* anchorElement() const override;
+ Node* node() const override { return m_node; }
+ Document* document() const override;
+
+ bool canSetFocusAttribute() const override;
+ int headingLevel() const override;
+
+ bool canSetValueAttribute() const override;
+
+ String valueDescription() const override;
+ float valueForRange() const override;
+ float maxValueForRange() const override;
+ float minValueForRange() const override;
+ float stepValueForRange() const override;
+
+ AccessibilityObject* selectedRadioButton() override;
+ AccessibilityObject* selectedTabItem() override;
+ AccessibilityButtonState checkboxOrRadioValue() const override;
+
+ unsigned hierarchicalLevel() const override;
+ String textUnderElement(AccessibilityTextUnderElementMode = AccessibilityTextUnderElementMode()) const override;
+ String accessibilityDescriptionForChildren() const;
+ String accessibilityDescription() const override;
+ String helpText() const override;
+ String title() const override;
+ String text() const override;
+ String stringValue() const override;
+ void colorValue(int& r, int& g, int& b) const override;
+ String ariaLabeledByAttribute() const override;
+ bool hasAttributesRequiredForInclusion() const final;
+ void setIsExpanded(bool) override;
+
+ Element* actionElement() const override;
+ Element* mouseButtonListener(MouseButtonListenerResultFilter = ExcludeBodyElement) const;
+ Element* anchorElement() const override;
AccessibilityObject* menuForMenuButton() const;
virtual void changeValueByPercent(float percentChange);
- 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;
+ AccessibilityObject* firstChild() const override;
+ AccessibilityObject* lastChild() const override;
+ AccessibilityObject* previousSibling() const override;
+ AccessibilityObject* nextSibling() const override;
+ AccessibilityObject* parentObject() const override;
+ AccessibilityObject* parentObjectIfExists() const override;
- virtual void detach(AccessibilityDetachmentType, AXObjectCache*) override;
- virtual void childrenChanged() override;
- virtual void updateAccessibilityRole() override;
+ void detach(AccessibilityDetachmentType, AXObjectCache*) override;
+ void childrenChanged() override;
+ void updateAccessibilityRole() override;
- virtual void increment() override;
- virtual void decrement() override;
+ void increment() override;
+ void decrement() override;
- virtual LayoutRect elementRect() const override;
+ LayoutRect elementRect() const override;
protected:
+ explicit AccessibilityNodeObject(Node*);
+
AccessibilityRole m_ariaRole;
bool m_childrenDirty;
mutable AccessibilityRole m_roleForMSAA;
@@ -161,50 +151,51 @@ protected:
bool m_initialized;
#endif
- virtual bool isDetached() const override { return !m_node; }
+ bool isDetached() const override { return !m_node; }
virtual AccessibilityRole determineAccessibilityRole();
- virtual void addChildren() override;
- virtual void addChild(AccessibilityObject*) override;
- virtual void insertChild(AccessibilityObject*, unsigned index) override;
+ void addChildren() override;
+ void addChild(AccessibilityObject*) override;
+ void insertChild(AccessibilityObject*, unsigned index) override;
- virtual bool canHaveChildren() const override;
- virtual AccessibilityRole ariaRoleAttribute() const override;
- AccessibilityRole determineAriaRoleAttribute() const;
+ bool canHaveChildren() const override;
+ AccessibilityRole ariaRoleAttribute() const override;
+ virtual AccessibilityRole determineAriaRoleAttribute() const;
AccessibilityRole remapAriaRoleDueToParent(AccessibilityRole) const;
- bool hasContentEditableAttributeSet() const;
- virtual bool isDescendantOfBarrenParent() const override;
+ 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.
bool isGenericFocusableElement() const;
+ bool isLabelable() const;
HTMLLabelElement* labelForElement(Element*) const;
+ String textForLabelElement(Element*) const;
String ariaAccessibilityDescription() const;
void ariaLabeledByElements(Vector<Element*>& elements) const;
String accessibilityDescriptionForElements(Vector<Element*> &elements) const;
- virtual LayoutRect boundingBoxRect() const override;
- virtual String ariaDescribedByAttribute() const override;
+ LayoutRect boundingBoxRect() const override;
+ String ariaDescribedByAttribute() const override;
Element* menuElementForMenuButton() const;
Element* menuItemElementForMenu() const;
AccessibilityObject* menuButtonForMenu() const;
+ AccessibilityObject* captionForFigure() const;
private:
- Node* m_node;
-
- virtual void accessibilityText(Vector<AccessibilityText>&) override;
+ bool isAccessibilityNodeObject() const final { return true; }
+ 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 override;
+ bool computeAccessibilityIsIgnored() const override;
bool usesAltTagForTextComputation() const;
-};
-ACCESSIBILITY_OBJECT_TYPE_CASTS(AccessibilityNodeObject, isAccessibilityNodeObject())
+ Node* m_node;
+};
} // namespace WebCore
-#endif // AccessibilityNodeObject_h
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilityNodeObject, isAccessibilityNodeObject())
diff --git a/Source/WebCore/accessibility/AccessibilityObject.cpp b/Source/WebCore/accessibility/AccessibilityObject.cpp
index 2294167e3..0f6319e37 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,26 +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"
@@ -63,6 +71,7 @@
#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>
@@ -77,8 +86,8 @@ AccessibilityObject::AccessibilityObject()
, m_haveChildren(false)
, m_role(UnknownRole)
, m_lastKnownIsIgnoredValue(DefaultBehavior)
-#if PLATFORM(GTK) || (PLATFORM(EFL) && HAVE(ACCESSIBILITY))
- , m_wrapper(0)
+#if PLATFORM(GTK)
+ , m_wrapper(nullptr)
#endif
{
}
@@ -99,7 +108,7 @@ void AccessibilityObject::detach(AccessibilityDetachmentType detachmentType, AXO
clearChildren();
#if HAVE(ACCESSIBILITY)
- setWrapper(0);
+ setWrapper(nullptr);
#endif
}
@@ -204,6 +213,9 @@ bool AccessibilityObject::isAccessibilityObjectSearchMatchAtIndex(AccessibilityO
case MisspelledWordSearchKey:
return axObject->hasMisspelling();
+ case OutlineSearchKey:
+ return axObject->isTree();
+
case PlainTextSearchKey:
return axObject->hasPlainText();
@@ -223,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();
@@ -280,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 LandmarkRegionRole:
+ case FormRole:
+ case GridRole:
+ case GroupRole:
+ case ImageRole:
+ case ListRole:
+ case ListBoxRole:
+ 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:
+ case WebApplicationRole:
+ 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> protectedThis(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;
@@ -299,19 +389,24 @@ 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
{
AccessibilityRole role = roleValue();
- return role == LandmarkApplicationRole
- || role == LandmarkBannerRole
+ return role == LandmarkBannerRole
|| role == LandmarkComplementaryRole
|| role == LandmarkContentInfoRole
|| role == LandmarkMainRole
|| role == LandmarkNavigationRole
+ || role == LandmarkRegionRole
|| role == LandmarkSearchRole;
}
@@ -330,13 +425,11 @@ bool AccessibilityObject::hasMisspelling() const
if (!textChecker)
return false;
- const UChar* chars = stringValue().deprecatedCharacters();
- int charsLength = stringValue().length();
bool isMisspelled = false;
if (unifiedTextCheckerEnabled(frame)) {
Vector<TextCheckingResult> results;
- checkTextOfParagraph(textChecker, chars, charsLength, TextCheckingTypeSpelling, results);
+ checkTextOfParagraph(*textChecker, stringValue(), TextCheckingTypeSpelling, results, frame->selection().selection());
if (!results.isEmpty())
isMisspelled = true;
return isMisspelled;
@@ -344,16 +437,16 @@ 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;
return isMisspelled;
}
-int AccessibilityObject::blockquoteLevel() const
+unsigned AccessibilityObject::blockquoteLevel() const
{
- int level = 0;
+ unsigned level = 0;
for (Node* elementNode = node(); elementNode; elementNode = elementNode->parentNode()) {
if (elementNode->hasTagName(blockquoteTag))
++level;
@@ -364,29 +457,53 @@ int AccessibilityObject::blockquoteLevel() const
AccessibilityObject* AccessibilityObject::parentObjectUnignored() const
{
- AccessibilityObject* parent;
- for (parent = parentObject(); parent && parent->accessibilityIsIgnored(); parent = parent->parentObject()) {
+ return const_cast<AccessibilityObject*>(AccessibilityObject::matchedParent(*this, false, [] (const AccessibilityObject& object) {
+ return !object.accessibilityIsIgnored();
+ }));
+}
+
+AccessibilityObject* AccessibilityObject::previousSiblingUnignored(int limit) const
+{
+ AccessibilityObject* previous;
+ ASSERT(limit >= 0);
+ for (previous = previousSibling(); previous && previous->accessibilityIsIgnored(); previous = previous->previousSibling()) {
+ limit--;
+ if (limit <= 0)
+ break;
}
-
- return parent;
+ return previous;
+}
+
+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;
}
AccessibilityObject* AccessibilityObject::firstAccessibleObjectFromNode(const Node* node)
{
if (!node)
- return 0;
+ 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());
}
@@ -394,19 +511,26 @@ AccessibilityObject* AccessibilityObject::firstAccessibleObjectFromNode(const No
return accessibleObject;
}
+bool AccessibilityObject::isDescendantOfRole(AccessibilityRole role) const
+{
+ return AccessibilityObject::matchedParent(*this, false, [&role] (const AccessibilityObject& object) {
+ return object.roleValue() == role;
+ }) != nullptr;
+}
+
static void appendAccessibilityObject(AccessibilityObject* object, AccessibilityObject::AccessibilityChildrenVector& results)
{
// 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->hasLivingRenderTree())
+ Document* document = downcast<FrameView>(*widget).frame().document();
+ if (!document || !document->hasLivingRenderTree())
return;
- object = object->axObjectCache()->getOrCreate(doc);
+ object = object->axObjectCache()->getOrCreate(document);
}
if (object)
@@ -417,7 +541,7 @@ static void appendChildrenToArray(AccessibilityObject* object, bool isForward, A
{
// 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.
- const auto& searchChildren = object->isAccessibilityTable() ? toAccessibilityTable(object)->rows() : object->children();
+ const auto& searchChildren = is<AccessibilityTable>(*object) && downcast<AccessibilityTable>(*object).isExposableThroughAccessibility() ? downcast<AccessibilityTable>(*object).rows() : object->children();
size_t childrenSize = searchChildren.size();
@@ -463,7 +587,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.
@@ -475,21 +600,23 @@ 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();
}
// The outer loop steps up the parent chain each time (unignored is important here because otherwise elements would be searched twice)
- for (AccessibilityObject* stopSearchElement = parentObjectUnignored(); startObject != stopSearchElement; startObject = startObject->parentObjectUnignored()) {
+ for (AccessibilityObject* stopSearchElement = parentObjectUnignored(); startObject && startObject != stopSearchElement; startObject = startObject->parentObjectUnignored()) {
// 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()) {
@@ -499,20 +626,194 @@ 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 && (referenceRange->endPosition().isNull() || ((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 && (referenceRange->startPosition().isNull() || ((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).releaseReturnValue();
+ 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.
@@ -532,7 +833,7 @@ bool AccessibilityObject::hasAttributesRequiredForInclusion() const
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)
@@ -576,34 +877,89 @@ 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);
+ 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();
- UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture);
- actionElem->accessKeyAction(true);
+ // 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;
+
+ ASSERT(pressElement);
+ // Prefer the hit test element, if it is inside the target element.
+ if (hitTestElement && hitTestElement->isDescendantOf(*pressElement))
+ pressElement = hitTestElement;
+
+ UserGestureIndicator gestureIndicator(ProcessingUserGesture, document);
+
+ bool dispatchedTouchEvent = false;
+#if PLATFORM(IOS)
+ if (hasTouchEventListener())
+ dispatchedTouchEvent = dispatchTouchEvent();
+#endif
+ if (!dispatchedTouchEvent)
+ pressElement->accessKeyAction(true);
+
return true;
}
+
+bool AccessibilityObject::dispatchTouchEvent()
+{
+ bool handled = false;
+#if ENABLE(IOS_TOUCH_EVENTS)
+ MainFrame* frame = mainFrame();
+ if (!frame)
+ return false;
+
+ handled = 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
{
@@ -794,7 +1150,7 @@ static VisiblePosition startOfStyleRange(const VisiblePosition& visiblePos)
{
RenderObject* renderer = visiblePos.deepEquivalent().deprecatedNode()->renderer();
RenderObject* startRenderer = renderer;
- RenderStyle* style = &renderer->style();
+ auto* style = &renderer->style();
// traverse backward by renderer to look for style change
for (RenderObject* r = renderer->previousInPreOrder(); r; r = r->previousInPreOrder()) {
@@ -857,7 +1213,28 @@ VisiblePositionRange AccessibilityObject::visiblePositionRangeForRange(const Pla
return VisiblePositionRange(startPosition, endPosition);
}
-static bool replacedNodeNeedsCharacter(Node* replacedNode)
+RefPtr<Range> AccessibilityObject::rangeForPlainTextRange(const PlainTextRange& range) const
+{
+ unsigned textLength = getLengthForTextRange();
+ if (range.start + range.length > textLength)
+ return nullptr;
+
+ if (AXObjectCache* cache = axObjectCache()) {
+ CharacterOffset start = cache->characterOffsetForIndex(range.start, this);
+ CharacterOffset end = cache->characterOffsetForIndex(range.start + range.length, this);
+ return cache->rangeForUnorderedCharacterOffsets(start, end);
+ }
+ return nullptr;
+}
+
+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
@@ -877,30 +1254,73 @@ 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
+String AccessibilityObject::listMarkerTextForNodeAndPosition(Node* node, const VisiblePosition& visiblePositionStart)
{
// If the range does not contain the start of the line, the list marker text should not be included.
if (!isStartOfLine(visiblePositionStart))
return String();
+ // We should speak the list marker only for the first line.
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();
+ if (!inSameLine(visiblePositionStart, firstPositionInNode(&listItem->element())))
+ return String();
+
+ return listMarkerTextForNode(node);
}
+
+String AccessibilityObject::stringForRange(RefPtr<Range> range) const
+{
+ if (!range)
+ return String();
+
+ TextIterator it(range.get());
+ if (it.atEnd())
+ return String();
-String AccessibilityObject::stringForVisiblePositionRange(const VisiblePositionRange& visiblePositionRange) const
+ 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.
+ // Don't add list marker text for new line character.
+ if (it.text().length() != 1 || !isSpaceOrNewline(it.text()[0]))
+ builder.append(listMarkerTextForNodeAndPosition(it.node(), VisiblePosition(range->startPosition())));
+ 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)
{
if (visiblePositionRange.isNull())
return String();
@@ -909,21 +1329,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);
}
}
@@ -941,17 +1356,16 @@ 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;
}
}
@@ -1141,15 +1555,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);
}
+
+// 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->attributeWithoutSynchronization(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
{
@@ -1224,18 +1689,32 @@ unsigned AccessibilityObject::doAXLineForIndex(unsigned index)
void AccessibilityObject::updateBackingStore()
{
// Updating the layout may delete this object.
+ RefPtr<AccessibilityObject> protectedThis(this);
+
if (Document* document = this->document()) {
- if (!document->view()->isInLayout())
+ if (!document->view()->isInRenderTreeLayout())
document->updateLayoutIgnorePendingStylesheets();
}
+
+ updateChildrenIfNecessary();
}
#endif
-
+
+ScrollView* AccessibilityObject::scrollViewAncestor() const
+{
+ if (const AccessibilityObject* scrollParent = AccessibilityObject::matchedParent(*this, true, [] (const AccessibilityObject& object) {
+ return is<AccessibilityScrollView>(object);
+ }))
+ return downcast<AccessibilityScrollView>(*scrollParent).scrollView();
+
+ return nullptr;
+}
+
Document* AccessibilityObject::document() const
{
FrameView* frameView = documentFrameView();
if (!frameView)
- return 0;
+ return nullptr;
return frameView->frame().document();
}
@@ -1244,7 +1723,7 @@ Page* AccessibilityObject::page() const
{
Document* document = this->document();
if (!document)
- return 0;
+ return nullptr;
return document->page();
}
@@ -1255,7 +1734,7 @@ FrameView* AccessibilityObject::documentFrameView() const
object = object->parentObject();
if (!object)
- return 0;
+ return nullptr;
return object->documentFrameView();
}
@@ -1272,8 +1751,11 @@ 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()
@@ -1290,16 +1772,16 @@ AccessibilityObject* AccessibilityObject::anchorElementForNode(Node* node)
{
RenderObject* obj = node->renderer();
if (!obj)
- return 0;
+ return nullptr;
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);
}
@@ -1307,16 +1789,27 @@ AccessibilityObject* AccessibilityObject::anchorElementForNode(Node* node)
AccessibilityObject* AccessibilityObject::headingElementForNode(Node* node)
{
if (!node)
- return 0;
+ return nullptr;
RenderObject* renderObject = node->renderer();
if (!renderObject)
- return 0;
+ return nullptr;
AccessibilityObject* axObject = renderObject->document().axObjectCache()->getOrCreate(renderObject);
- for (; axObject && axObject->roleValue() != HeadingRole; axObject = axObject->parentObject()) { }
- return axObject;
+ return const_cast<AccessibilityObject*>(AccessibilityObject::matchedParent(*axObject, true, [] (const AccessibilityObject& object) {
+ return object.roleValue() == HeadingRole;
+ }));
+}
+
+const AccessibilityObject* AccessibilityObject::matchedParent(const AccessibilityObject& object, bool includeSelf, const std::function<bool(const AccessibilityObject&)>& matches)
+{
+ const AccessibilityObject* parent = includeSelf ? &object : object.parentObject();
+ for (; parent; parent = parent->parentObject()) {
+ if (matches(*parent))
+ return parent;
+ }
+ return nullptr;
}
void AccessibilityObject::ariaTreeRows(AccessibilityChildrenVector& result)
@@ -1354,21 +1847,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()));
+ 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:
@@ -1380,6 +1890,7 @@ const String& AccessibilityObject::actionVerb() const
case RadioButtonRole:
return radioButtonAction;
case CheckBoxRole:
+ case SwitchRole:
return isChecked() ? checkedCheckBoxAction : uncheckedCheckBoxAction;
case LinkRole:
case WebCoreLinkRole:
@@ -1401,52 +1912,127 @@ const String& AccessibilityObject::actionVerb() const
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
{
- DEFINE_STATIC_LOCAL(const AtomicString, invalidStatusFalse, ("false", AtomicString::ConstructFromLiteral));
- DEFINE_STATIC_LOCAL(const AtomicString, invalidStatusTrue, ("true", AtomicString::ConstructFromLiteral));
-
- // aria-invalid can return false (default), grammer, spelling, or true.
- const AtomicString& ariaInvalid = getAttribute(aria_invalidAttr);
+ 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', empty or not present, it should return false.
- if (ariaInvalid.isEmpty() || equalIgnoringCase(ariaInvalid, invalidStatusFalse))
- return invalidStatusFalse;
+ if (ariaInvalid.isEmpty()) {
+ // We should expose invalid status for input types.
+ Node* node = this->node();
+ if (node && is<HTMLInputElement>(*node)) {
+ HTMLInputElement& input = downcast<HTMLInputElement>(*node);
+ if (input.hasBadInput() || input.typeMismatch())
+ return trueValue;
+ }
+ return falseValue;
+ }
- // Only 'true', 'grammar' and 'spelling' are values recognised by the WAI-ARIA
- // specification. Any other non empty string should be treated as 'true'.
- if (equalIgnoringCase(ariaInvalid, "spelling") || equalIgnoringCase(ariaInvalid, "grammar"))
- return ariaInvalid;
- return invalidStatusTrue;
+ // If "false", "undefined" [sic, string value], empty, or missing, return "false".
+ if (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;
}
-bool AccessibilityObject::hasAttribute(const QualifiedName& attribute) const
+AccessibilityARIACurrentState AccessibilityObject::ariaCurrentState() const
+{
+ // aria-current can return false (default), true, page, step, location, date or time.
+ String currentStateValue = stripLeadingAndTrailingHTMLSpaces(getAttribute(aria_currentAttr));
+
+ // If "false", empty, or missing, return false state.
+ if (currentStateValue.isEmpty() || currentStateValue == "false")
+ return ARIACurrentFalse;
+
+ 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;
+
+ // Any value not included in the list of allowed values should be treated as "true".
+ return ARIACurrentTrue;
+}
+
+bool AccessibilityObject::isAriaModalDescendant(Node* ariaModalNode) const
+{
+ 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
{
- Node* elementNode = node();
- if (!elementNode)
+ // 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).hasAttributeWithoutSynchronization(attribute);
+}
- Element* element = toElement(elementNode);
- return element->fastGetAttribute(attribute);
+const AtomicString& AccessibilityObject::getAttribute(const QualifiedName& attribute) const
+{
+ if (Element* element = this->element())
+ return element->attributeWithoutSynchronization(attribute);
+ return nullAtom;
}
// Lacking concrete evidence of orientation, horizontal means width > height. vertical is height > width;
@@ -1458,20 +2044,17 @@ 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
{
if (!axObject || !axObject->hasChildren())
return false;
-
- for (const AccessibilityObject* parent = parentObject(); parent; parent = parent->parentObject()) {
- if (parent == axObject)
- return true;
- }
- return false;
+
+ return AccessibilityObject::matchedParent(*this, false, [axObject] (const AccessibilityObject& object) {
+ return &object == axObject;
+ }) != nullptr;
}
bool AccessibilityObject::isAncestorOfObject(const AccessibilityObject* axObject) const
@@ -1488,22 +2071,30 @@ 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 },
- { "application", LandmarkApplicationRole },
+ { "application", WebApplicationRole },
{ "article", DocumentArticleRole },
{ "banner", LandmarkBannerRole },
{ "button", ButtonRole },
@@ -1512,12 +2103,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 },
@@ -1536,6 +2130,7 @@ static ARIARoleMap* createARIARoleMap()
{ "menuitem", MenuItemRole },
{ "menuitemcheckbox", MenuItemCheckboxRole },
{ "menuitemradio", MenuItemRadioRole },
+ { "none", PresentationalRole },
{ "note", DocumentNoteRole },
{ "navigation", LandmarkNavigationRole },
{ "option", ListBoxOptionRole },
@@ -1543,14 +2138,17 @@ static ARIARoleMap* createARIARoleMap()
{ "progressbar", ProgressIndicatorRole },
{ "radio", RadioButtonRole },
{ "radiogroup", RadioGroupRole },
- { "region", DocumentRegionRole },
+ { "region", LandmarkRegionRole },
{ "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 },
@@ -1563,26 +2161,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;
}
@@ -1590,6 +2199,18 @@ 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;
+ if (role == PopUpButtonRole || role == ToggleButtonRole)
+ role = ButtonRole;
+
+ return reverseAriaRoleMap().get(role);
+}
+
bool AccessibilityObject::hasHighlighting() const
{
for (Node* node = this->node(); node; node = node->parentNode()) {
@@ -1600,12 +2221,89 @@ bool AccessibilityObject::hasHighlighting() const
return false;
}
+String 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, emptyString(), 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 (node && node->isElementNode())
- return toElement(node);
- return 0;
+ if (is<Element>(node))
+ return downcast<Element>(node);
+ return nullptr;
+}
+
+bool AccessibilityObject::isValueAutofillAvailable() const
+{
+ if (!isNativeTextControl())
+ return false;
+
+ Node* node = this->node();
+ if (!is<HTMLInputElement>(node))
+ return false;
+
+ return downcast<HTMLInputElement>(*node).autoFillButtonType() != AutoFillButtonType::None;
+}
+
+AutoFillButtonType AccessibilityObject::valueAutofillButtonType() const
+{
+ if (!isValueAutofillAvailable())
+ return AutoFillButtonType::None;
+
+ return downcast<HTMLInputElement>(*this->node()).autoFillButtonType();
+}
+
+bool AccessibilityObject::isValueAutofilled() const
+{
+ if (!isNativeTextControl())
+ return false;
+
+ Node* node = this->node();
+ if (!is<HTMLInputElement>(node))
+ return false;
+
+ return downcast<HTMLInputElement>(*node).isAutoFilled();
}
const AtomicString& AccessibilityObject::placeholderValue() const
@@ -1614,6 +2312,10 @@ const AtomicString& AccessibilityObject::placeholderValue() const
if (!placeholder.isEmpty())
return placeholder;
+ const AtomicString& ariaPlaceholder = getAttribute(aria_placeholderAttr);
+ if (!ariaPlaceholder.isEmpty())
+ return ariaPlaceholder;
+
return nullAtom;
}
@@ -1622,12 +2324,9 @@ bool AccessibilityObject::isInsideARIALiveRegion() const
if (supportsARIALiveRegion())
return true;
- for (AccessibilityObject* axParent = parentObject(); axParent; axParent = axParent->parentObject()) {
- if (axParent->supportsARIALiveRegion())
- return true;
- }
-
- return false;
+ return AccessibilityObject::matchedParent(*this, false, [] (const AccessibilityObject& object) {
+ return object.supportsARIALiveRegion();
+ }) != nullptr;
}
bool AccessibilityObject::supportsARIAAttributes() const
@@ -1636,13 +2335,14 @@ bool AccessibilityObject::supportsARIAAttributes() const
return supportsARIALiveRegion()
|| supportsARIADragging()
|| supportsARIADropping()
- || supportsARIAFlowTo()
|| supportsARIAOwns()
|| hasAttribute(aria_atomicAttr)
|| hasAttribute(aria_busyAttr)
|| hasAttribute(aria_controlsAttr)
+ || hasAttribute(aria_currentAttr)
|| hasAttribute(aria_describedbyAttr)
|| hasAttribute(aria_disabledAttr)
+ || hasAttribute(aria_flowtoAttr)
|| hasAttribute(aria_haspopupAttr)
|| hasAttribute(aria_invalidAttr)
|| hasAttribute(aria_labelAttr)
@@ -1650,10 +2350,14 @@ bool AccessibilityObject::supportsARIAAttributes() const
|| 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
@@ -1662,8 +2366,10 @@ 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.
@@ -1672,7 +2378,7 @@ AccessibilityObject* AccessibilityObject::elementAccessibilityHitTest(const IntP
return child->elementAccessibilityHitTest(point);
}
- return const_cast<AccessibilityObject*>(this);
+ return const_cast<AccessibilityObject*>(this);
}
AXObjectCache* AccessibilityObject::axObjectCache() const
@@ -1680,18 +2386,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);
}
@@ -1699,11 +2405,11 @@ 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 (equalIgnoringCase(sortAttribute, "other"))
+ if (equalLettersIgnoringASCIICase(sortAttribute, "other"))
return SortDirectionOther;
return SortDirectionNone;
@@ -1714,7 +2420,8 @@ bool AccessibilityObject::supportsRangeValue() const
return isProgressIndicator()
|| isSlider()
|| isScrollbar()
- || isSpinButton();
+ || isSpinButton()
+ || isAttachmentElement();
}
bool AccessibilityObject::supportsARIASetSize() const
@@ -1745,50 +2452,100 @@ String AccessibilityObject::identifierAttribute() const
void AccessibilityObject::classList(Vector<String>& classList) const
{
Node* node = this->node();
- if (!node || !node->isElementNode())
+ if (!is<Element>(node))
return;
- Element* element = toElement(node);
- DOMTokenList* list = element->classList();
- if (!list)
- return;
- unsigned length = list->length();
+ 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());
+ 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::supportsARIAExpanded() const
+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);
- return equalIgnoringCase(expanded, "true") || equalIgnoringCase(expanded, "false");
+ 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();
+
+ // Summary element should use its details parent's expanded status.
+ if (isSummary()) {
+ if (const AccessibilityObject* parent = AccessibilityObject::matchedParent(*this, false, [] (const AccessibilityObject& object) {
+ return object.roleValue() == DetailsRole;
+ }))
+ return parent->isExpanded();
+ }
+
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")) {
- // ARIA says that radio and menuitemradio elements must NOT expose button state 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)
+ if (ariaRole == RadioButtonRole || ariaRole == MenuItemRadioRole || ariaRole == SwitchRole)
return ButtonStateOff;
return ButtonStateMixed;
}
+ if (isIndeterminate())
+ return ButtonStateMixed;
+
return ButtonStateOff;
}
@@ -1797,71 +2554,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
@@ -1886,8 +2661,8 @@ bool AccessibilityObject::isOnscreen() const
const AccessibilityObject* outer = objects[i];
const AccessibilityObject* inner = objects[i - 1];
// FIXME: unclear if we need LegacyIOSDocumentVisibleRect.
- const IntRect outerRect = i < levels ? pixelSnappedIntRect(outer->boundingBoxRect()) : outer->getScrollableAreaIfScrollable()->visibleContentRect(ScrollableArea::LegacyIOSDocumentVisibleRect);
- const IntRect innerRect = pixelSnappedIntRect(inner->isAccessibilityScrollView() ? inner->parentObject()->boundingBoxRect() : inner->boundingBoxRect());
+ 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;
@@ -1900,7 +2675,7 @@ bool AccessibilityObject::isOnscreen() const
void AccessibilityObject::scrollToMakeVisible() const
{
- IntRect objectRect = pixelSnappedIntRect(boundingBoxRect());
+ IntRect objectRect = snappedIntRect(boundingBoxRect());
objectRect.setLocation(IntPoint());
scrollToMakeVisibleWithSubFocus(objectRect);
}
@@ -1910,7 +2685,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)
@@ -1921,6 +2696,11 @@ void AccessibilityObject::scrollToMakeVisibleWithSubFocus(const IntRect& subfocu
// 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(),
@@ -1934,9 +2714,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
@@ -2001,6 +2788,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()
{
@@ -2019,7 +2902,8 @@ void AccessibilityObject::notifyIfIgnoredValueChanged()
{
bool isIgnored = accessibilityIsIgnored();
if (lastKnownIsIgnoredValue() != isIgnored) {
- axObjectCache()->childrenChanged(parentObject());
+ if (AXObjectCache* cache = axObjectCache())
+ cache->childrenChanged(parentObject());
setLastKnownIsIgnoredValue(isIgnored);
}
}
@@ -2033,7 +2917,7 @@ TextIteratorBehavior AccessibilityObject::textIteratorBehaviorForTextRange() con
{
TextIteratorBehavior behavior = TextIteratorIgnoresStyleVisibility;
-#if PLATFORM(GTK) || PLATFORM(EFL)
+#if PLATFORM(GTK)
// We need to emit replaced elements for GTK, and present
// them with the 'object replacement character' (0xFFFC).
behavior = static_cast<TextIteratorBehavior>(behavior | TextIteratorEmitsObjectReplacementCharacters);
@@ -2072,11 +2956,9 @@ bool AccessibilityObject::accessibilityIsIgnoredByDefault() const
// http://www.w3.org/TR/wai-aria/terms#def_hidden
bool AccessibilityObject::isARIAHidden() const
{
- for (const AccessibilityObject* object = this; object; object = object->parentObject()) {
- if (equalIgnoringCase(object->getAttribute(aria_hiddenAttr), "true"))
- return true;
- }
- return false;
+ return AccessibilityObject::matchedParent(*this, true, [] (const AccessibilityObject& object) {
+ return equalLettersIgnoringASCIICase(object.getAttribute(aria_hiddenAttr), "true");
+ }) != nullptr;
}
// DOM component of hidden definition.
@@ -2096,6 +2978,9 @@ AccessibilityObjectInclusion AccessibilityObject::defaultObjectInclusion() const
if (isARIAHidden())
return IgnoreObject;
+ if (ignoredFromARIAModalPresence())
+ return IgnoreObject;
+
if (isPresentationalChildOfAriaRole())
return IgnoreObject;
@@ -2104,7 +2989,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) {
@@ -2119,7 +3008,8 @@ 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;
@@ -2141,10 +3031,146 @@ void AccessibilityObject::elementsFromAttribute(Vector<Element*>& elements, cons
Vector<String> idVector;
idList.split(' ', idVector);
- for (auto idName : 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
+
+AccessibilityObject* AccessibilityObject::focusableAncestor()
+{
+ return const_cast<AccessibilityObject*>(AccessibilityObject::matchedParent(*this, true, [] (const AccessibilityObject& object) {
+ return object.canSetFocusAttribute();
+ }));
+}
+
+AccessibilityObject* AccessibilityObject::editableAncestor()
+{
+ return const_cast<AccessibilityObject*>(AccessibilityObject::matchedParent(*this, true, [] (const AccessibilityObject& object) {
+ return object.isTextControl();
+ }));
+}
+
+AccessibilityObject* AccessibilityObject::highestEditableAncestor()
+{
+ AccessibilityObject* editableAncestor = this->editableAncestor();
+ AccessibilityObject* previousEditableAncestor = nullptr;
+ while (editableAncestor) {
+ if (editableAncestor == previousEditableAncestor) {
+ if (AccessibilityObject* parent = editableAncestor->parentObject()) {
+ editableAncestor = parent->editableAncestor();
+ continue;
+ }
+ break;
+ }
+ previousEditableAncestor = editableAncestor;
+ editableAncestor = editableAncestor->editableAncestor();
+ }
+ return previousEditableAncestor;
+}
+
+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)
+ || node->hasTagName(supTag) || node->hasTagName(subTag);
+}
+
+bool AccessibilityObject::isSubscriptStyleGroup() const
+{
+ Node* node = this->node();
+ return node && node->hasTagName(subTag);
+}
+
+bool AccessibilityObject::isSuperscriptStyleGroup() const
+{
+ Node* node = this->node();
+ return node && node->hasTagName(supTag);
+}
+
+bool AccessibilityObject::isFigure() const
+{
+ Node* node = this->node();
+ return node && node->hasTagName(figureTag);
+}
+
+bool AccessibilityObject::isOutput() const
+{
+ Node* node = this->node();
+ return node && node->hasTagName(outputTag);
+}
+
+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();
+}
+
+void AccessibilityObject::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);
+ }
+}
+
+void AccessibilityObject::ariaControlsElements(AccessibilityChildrenVector& ariaControls) const
+{
+ ariaElementsFromAttribute(ariaControls, aria_controlsAttr);
+}
+
+void AccessibilityObject::ariaDescribedByElements(AccessibilityChildrenVector& ariaDescribedBy) const
+{
+ ariaElementsFromAttribute(ariaDescribedBy, aria_describedbyAttr);
+}
+
+void AccessibilityObject::ariaFlowToElements(AccessibilityChildrenVector& flowTo) const
+{
+ ariaElementsFromAttribute(flowTo, aria_flowtoAttr);
+}
+
+void AccessibilityObject::ariaLabelledByElements(AccessibilityChildrenVector& ariaLabelledBy) const
+{
+ ariaElementsFromAttribute(ariaLabelledBy, aria_labelledbyAttr);
+ if (!ariaLabelledBy.size())
+ ariaElementsFromAttribute(ariaLabelledBy, aria_labeledbyAttr);
+}
+
+void AccessibilityObject::ariaOwnsElements(AccessibilityChildrenVector& axObjects) const
+{
+ ariaElementsFromAttribute(axObjects, aria_ownsAttr);
+}
+
} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AccessibilityObject.h b/Source/WebCore/accessibility/AccessibilityObject.h
index 390ae2f9e..63ea8c638 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.
*
@@ -27,27 +27,28 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AccessibilityObject_h
-#define AccessibilityObject_h
+#pragma once
#include "FloatQuad.h"
+#include "HTMLTextFormControlElement.h"
#include "LayoutRect.h"
#include "Path.h"
-#include "TextIterator.h"
+#include "Range.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;
@@ -62,7 +63,7 @@ OBJC_CLASS WebAccessibilityObjectWrapper;
typedef WebAccessibilityObjectWrapper AccessibilityObjectWrapper;
-#elif PLATFORM(GTK) || (PLATFORM(EFL) && HAVE(ACCESSIBILITY))
+#elif PLATFORM(GTK)
typedef struct _AtkObject AtkObject;
typedef struct _AtkObject AccessibilityObjectWrapper;
#else
@@ -76,17 +77,14 @@ class AXObjectCache;
class Element;
class Frame;
class FrameView;
-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;
@@ -102,10 +100,12 @@ enum AccessibilityRole {
ApplicationStatusRole,
ApplicationTimerRole,
AudioRole,
+ BlockquoteRole,
BrowserRole,
BusyIndicatorRole,
ButtonRole,
CanvasRole,
+ CaptionRole,
CellRole,
CheckBoxRole,
ColorWellRole,
@@ -116,6 +116,7 @@ enum AccessibilityRole {
DescriptionListRole,
DescriptionListTermRole,
DescriptionListDetailRole,
+ DetailsRole,
DirectoryRole,
DisclosureTriangleRole,
DivRole,
@@ -123,29 +124,30 @@ enum AccessibilityRole {
DocumentArticleRole,
DocumentMathRole,
DocumentNoteRole,
- DocumentRegionRole,
DrawerRole,
EditableTextRole,
FooterRole,
FormRole,
GridRole,
+ GridCellRole,
GroupRole,
GrowAreaRole,
HeadingRole,
HelpTagRole,
HorizontalRuleRole,
IgnoredRole,
+ InlineRole,
ImageRole,
ImageMapRole,
ImageMapLinkRole,
IncrementorRole,
LabelRole,
- LandmarkApplicationRole,
LandmarkBannerRole,
LandmarkComplementaryRole,
LandmarkContentInfoRole,
LandmarkMainRole,
LandmarkNavigationRole,
+ LandmarkRegionRole,
LandmarkSearchRole,
LegendRole,
LinkRole,
@@ -154,6 +156,7 @@ enum AccessibilityRole {
ListBoxOptionRole,
ListItemRole,
ListMarkerRole,
+ MarkRole,
MathElementRole,
MatteRole,
MenuRole,
@@ -167,17 +170,24 @@ enum AccessibilityRole {
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,
@@ -186,8 +196,13 @@ enum AccessibilityRole {
SplitGroupRole,
SplitterRole,
StaticTextRole,
+ SummaryRole,
+ SwitchRole,
SystemWideRole,
SVGRootRole,
+ SVGTextRole,
+ SVGTSpanRole,
+ SVGTextPathRole,
TabGroupRole,
TabListRole,
TabPanelRole,
@@ -205,6 +220,7 @@ enum AccessibilityRole {
UserInterfaceTooltipRole,
ValueIndicatorRole,
VideoRole,
+ WebApplicationRole,
WebAreaRole,
WebCoreLinkRole,
WindowRole,
@@ -219,6 +235,9 @@ enum AccessibilityTextSource {
TitleTagText,
PlaceholderText,
LabelByElementText,
+ TitleText,
+ SubtitleText,
+ ActionText,
};
struct AccessibilityText {
@@ -231,10 +250,10 @@ struct AccessibilityText {
, 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)
@@ -249,20 +268,24 @@ 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)
- : childrenInclusion(c)
- , includeFocusableContent(i)
- { }
+ AccessibilityTextUnderElementMode(ChildrenInclusion c = TextUnderElementModeSkipIgnoredChildren, bool i = false, Node* ignored = nullptr)
+ : childrenInclusion(c)
+ , includeFocusableContent(i)
+ , ignoredChildNode(ignored)
+ { }
};
enum AccessibilityOrientation {
AccessibilityOrientationVertical,
AccessibilityOrientationHorizontal,
+ AccessibilityOrientationUndefined,
};
enum AccessibilityObjectInclusion {
@@ -317,6 +340,7 @@ enum AccessibilitySearchKey {
ListSearchKey,
LiveRegionSearchKey,
MisspelledWordSearchKey,
+ OutlineSearchKey,
PlainTextSearchKey,
RadioGroupSearchKey,
SameTypeSearchKey,
@@ -342,13 +366,15 @@ struct AccessibilitySearchCriteria {
String searchText;
unsigned resultsLimit;
bool visibleOnly;
+ bool immediateDescendantsOnly;
- AccessibilitySearchCriteria(AccessibilityObject* startObject, AccessibilitySearchDirection searchDirection, String searchText, unsigned resultsLimit, bool visibleOnly)
- : startObject(startObject)
- , searchDirection(searchDirection)
- , searchText(searchText)
- , resultsLimit(resultsLimit)
- , visibleOnly(visibleOnly)
+ 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)
{ }
};
@@ -366,6 +392,11 @@ struct VisiblePositionRange {
, end(e)
{ }
+ VisiblePositionRange(const VisibleSelection& selection)
+ : start(selection.start())
+ , end(selection.end())
+ { }
+
bool isNull() const { return start.isNull() || end.isNull(); }
};
@@ -387,6 +418,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();
@@ -412,25 +475,29 @@ public:
virtual bool isAccessibilityScrollbar() const { return false; }
virtual bool isAccessibilityScrollView() const { return false; }
virtual bool isAccessibilitySVGRoot() const { return false; }
+ virtual bool isAccessibilitySVGElement() const { return false; }
bool accessibilityObjectContainsText(String *) const;
-
- virtual bool isAnchor() const { return false; }
- virtual bool isAttachment() const { return false; }
+
+ virtual bool isAttachmentElement() 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 isNativeListBox() const { return false; }
+ bool isListBox() const { return roleValue() == ListBoxRole; }
virtual bool isListBoxOption() const { return false; }
+ virtual bool isAttachment() const { return false; }
virtual bool isMediaTimeline() const { return false; }
virtual bool isMenuRelated() const { return false; }
virtual bool isMenu() const { return false; }
@@ -444,9 +511,9 @@ public:
virtual bool isSliderThumb() const { return false; }
virtual bool isInputSlider() const { return false; }
virtual bool isControl() const { return false; }
+ virtual bool isLabel() 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; }
@@ -463,8 +530,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; }
@@ -483,7 +553,15 @@ public:
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;
+ bool isSubscriptStyleGroup() const;
+ bool isSuperscriptStyleGroup() const;
+ bool isFigure() const;
+ bool isSummary() const { return roleValue() == SummaryRole; }
+ bool isOutput() const;
+
virtual bool isChecked() const { return false; }
virtual bool isEnabled() const { return false; }
virtual bool isSelected() const { return false; }
@@ -494,7 +572,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; }
@@ -519,6 +596,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; }
@@ -527,14 +606,14 @@ public:
virtual bool canSetSelectedChildrenAttribute() const { return false; }
virtual bool canSetExpandedAttribute() const { return false; }
- Element* element() const;
- 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;
- int blockquoteLevel() const;
+ unsigned blockquoteLevel() const;
virtual int headingLevel() const { return 0; }
virtual int tableLevel() const { return 0; }
virtual AccessibilityButtonState checkboxOrRadioValue() const;
@@ -543,28 +622,38 @@ 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);
static bool isARIAInput(AccessibilityRole);
+
virtual bool supportsARIAOwns() const { return false; }
- 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 { }
+ void ariaControlsElements(AccessibilityChildrenVector&) const;
+ void ariaDescribedByElements(AccessibilityChildrenVector&) const;
+ void ariaFlowToElements(AccessibilityChildrenVector&) const;
+ void ariaLabelledByElements(AccessibilityChildrenVector&) const;
+ void ariaOwnsElements(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;
+ virtual String 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;
@@ -579,38 +668,50 @@ 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; }
+
+ bool isDescendantOfRole(AccessibilityRole) const;
+
+ // Text selection
+ RefPtr<Range> rangeOfStringClosestToRangeInDirection(Range*, AccessibilitySearchDirection, Vector<String>&) const;
+ RefPtr<Range> selectionRange() const;
+ String selectText(AccessibilitySelectTextCriteria*);
- virtual AccessibilityObject* observableObject() const { return 0; }
+ 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;
@@ -628,13 +729,17 @@ public:
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;
@@ -642,14 +747,13 @@ public:
static AccessibilityObject* anchorElementForNode(Node*);
static AccessibilityObject* headingElementForNode(Node*);
- virtual Element* anchorElement() const { return 0; }
- virtual Element* actionElement() const { return 0; }
+ 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(); }
@@ -665,13 +769,15 @@ public:
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* 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; }
@@ -685,8 +791,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() { }
@@ -699,23 +805,27 @@ public:
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 bool canHaveSelectedChildren() const { 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;
@@ -725,10 +835,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;
@@ -738,9 +852,14 @@ public:
VisiblePositionRange paragraphForPosition(const VisiblePosition&) const;
VisiblePositionRange styleRangeForPosition(const VisiblePosition&) const;
VisiblePositionRange visiblePositionRangeForRange(const PlainTextRange&) const;
+ VisiblePositionRange lineRangeForPosition(const VisiblePosition&) const;
+
+ RefPtr<Range> rangeForPlainTextRange(const PlainTextRange&) const;
- String stringForVisiblePositionRange(const VisiblePositionRange&) const;
+ static String stringForVisiblePositionRange(const VisiblePositionRange&);
+ String stringForRange(RefPtr<Range>) const;
virtual IntRect boundsForVisiblePositionRange(const VisiblePositionRange&) const { return IntRect(); }
+ virtual IntRect boundsForRange(const RefPtr<Range>) const { return IntRect(); }
int lengthForVisiblePositionRange(const VisiblePositionRange&) const;
virtual void setSelectedVisiblePositionRange(const VisiblePositionRange&) const { }
@@ -774,10 +893,13 @@ public:
virtual String doAXStringForRange(const PlainTextRange&) const { return String(); }
virtual IntRect doAXBoundsForRange(const PlainTextRange&) const { return IntRect(); }
- String listMarkerTextForNodeAndPosition(Node*, const VisiblePosition&) const;
+ virtual IntRect doAXBoundsForRangeUsingCharacterOffset(const PlainTextRange&) const { return IntRect(); }
+ static String listMarkerTextForNodeAndPosition(Node*, const VisiblePosition&);
unsigned doAXLineForIndex(unsigned);
+ String computedRoleString() const;
+
virtual String stringValueForMSAA() const { return String(); }
virtual String stringRoleForMSAA() const { return String(); }
virtual String nameForMSAA() const { return String(); }
@@ -785,7 +907,10 @@ public:
virtual AccessibilityRole roleValueForMSAA() const { return roleValue(); }
virtual String passwordFieldValue() const { return String(); }
-
+ bool isValueAutofilled() const;
+ bool isValueAutofillAvailable() const;
+ AutoFillButtonType valueAutofillButtonType() 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.
@@ -796,10 +921,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; }
+ virtual bool isBusy() 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;
@@ -812,7 +944,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);
@@ -838,28 +976,32 @@ 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(); }
virtual String mathFencedCloseString() const { return String(); }
virtual int mathLineThickness() const { return 0; }
+ virtual bool isAnonymousMathOperator() const { return false; }
// Multiscripts components.
typedef Vector<std::pair<AccessibilityObject*, AccessibilityObject*>> AccessibilityMathMultiscriptPairs;
@@ -872,7 +1014,7 @@ public:
bool isHidden() const { return isARIAHidden() || isDOMHidden(); }
#if HAVE(ACCESSIBILITY)
-#if PLATFORM(GTK) || PLATFORM(EFL)
+#if PLATFORM(GTK)
AccessibilityObjectWrapper* wrapper() const;
void setWrapper(AccessibilityObjectWrapper*);
#else
@@ -884,7 +1026,7 @@ public:
#endif
#endif
-#if PLATFORM(MAC)
+#if PLATFORM(COCOA)
void overrideAttachmentParent(AccessibilityObject* parent);
#else
void overrideAttachmentParent(AccessibilityObject*) { }
@@ -902,12 +1044,30 @@ public:
#if PLATFORM(IOS)
int accessibilityPasswordFieldLength();
+ bool hasTouchEventListener() const;
+ bool isInputTypePopupButton() const;
#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
+
+ AccessibilityObject* focusableAncestor();
+ AccessibilityObject* editableAncestor();
+ AccessibilityObject* highestEditableAncestor();
+
+ static const AccessibilityObject* matchedParent(const AccessibilityObject&, bool includeSelf, const std::function<bool(const AccessibilityObject&)>&);
+
protected:
AXID m_id;
AccessibilityChildrenVector m_children;
@@ -918,17 +1078,22 @@ 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 isOnscreen() const;
-
-#if (PLATFORM(GTK) || PLATFORM(EFL)) && HAVE(ACCESSIBILITY)
+ bool dispatchTouchEvent();
+
+ void ariaElementsFromAttribute(AccessibilityChildrenVector&, const QualifiedName&) const;
+
+#if PLATFORM(GTK) && HAVE(ACCESSIBILITY)
bool allowsTextRanges() const;
unsigned getLengthForTextRange() const;
#else
@@ -936,11 +1101,11 @@ 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;
-#elif PLATFORM(GTK) || (PLATFORM(EFL) && HAVE(ACCESSIBILITY))
+#elif PLATFORM(GTK)
AtkObject* m_wrapper;
#endif
};
@@ -952,9 +1117,9 @@ inline int AccessibilityObject::lineForPosition(const VisiblePosition&) const {
inline void AccessibilityObject::updateBackingStore() { }
#endif
-#define ACCESSIBILITY_OBJECT_TYPE_CASTS(ToValueTypeName, predicate) \
- TYPE_CASTS_BASE(ToValueTypeName, AccessibilityObject, object, object->predicate, object.predicate)
-
} // namespace WebCore
-#endif // AccessibilityObject_h
+#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()
diff --git a/Source/WebCore/accessibility/AccessibilityProgressIndicator.cpp b/Source/WebCore/accessibility/AccessibilityProgressIndicator.cpp
index 1dac10988..c14177cfc 100644
--- a/Source/WebCore/accessibility/AccessibilityProgressIndicator.cpp
+++ b/Source/WebCore/accessibility/AccessibilityProgressIndicator.cpp
@@ -21,11 +21,12 @@
#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"
#include "HTMLProgressElement.h"
+#include "LocalizedStrings.h"
#include "RenderMeter.h"
#include "RenderObject.h"
#include "RenderProgress.h"
@@ -34,17 +35,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 +51,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 +62,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 +119,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 +139,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 +152,62 @@ 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();
+}
+
+String AccessibilityProgressIndicator::gaugeRegionValueDescription() const
+{
+#if PLATFORM(COCOA)
+ if (!m_renderer || !m_renderer->isMeter())
+ return String();
+
+ // Only expose this when the author has explicitly specified the following attributes.
+ if (!hasAttribute(lowAttr) && !hasAttribute(highAttr) && !hasAttribute(optimumAttr))
+ return String();
+
+ if (HTMLMeterElement* element = meterElement()) {
+ switch (element->gaugeRegion()) {
+ case HTMLMeterElement::GaugeRegionOptimum:
+ return AXMeterGaugeRegionOptimumText();
+ case HTMLMeterElement::GaugeRegionSuboptimal:
+ return AXMeterGaugeRegionSuboptimalText();
+ case HTMLMeterElement::GaugeRegionEvenLessGood:
+ return AXMeterGaugeRegionLessGoodText();
+ default:
+ break;
+ }
+ }
+#endif
+ return String();
}
#endif
-} // namespace WebCore
+Element* AccessibilityProgressIndicator::element() const
+{
+ if (m_renderer->isProgress())
+ return progressElement();
+
+#if ENABLE(METER_ELEMENT)
+ if (m_renderer->isMeter())
+ return meterElement();
+#endif
-#endif // ENABLE(PROGRESS_ELEMENT) || ENABLE(METER_ELEMENT)
+ return AccessibilityObject::element();
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AccessibilityProgressIndicator.h b/Source/WebCore/accessibility/AccessibilityProgressIndicator.h
index 1edfe8a0b..3f5cfe054 100644
--- a/Source/WebCore/accessibility/AccessibilityProgressIndicator.h
+++ b/Source/WebCore/accessibility/AccessibilityProgressIndicator.h
@@ -18,10 +18,8 @@
*
*/
-#ifndef AccessibilityProgressIndicator_h
-#define AccessibilityProgressIndicator_h
+#pragma once
-#if ENABLE(PROGRESS_ELEMENT) || ENABLE(METER_ELEMENT)
#include "AccessibilityRenderObject.h"
namespace WebCore {
@@ -31,44 +29,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*);
+ String gaugeRegionValueDescription() const;
#endif
+ Element* element() const override;
private:
- virtual AccessibilityRole roleValue() const override { return ProgressIndicatorRole; }
+ AccessibilityRole roleValue() const override { return ProgressIndicatorRole; }
- virtual bool isProgressIndicator() const override { return true; }
+ bool isProgressIndicator() const override { return true; }
- virtual float valueForRange() const override;
- virtual float maxValueForRange() const override;
- virtual float minValueForRange() const override;
+ String valueDescription() const override;
+ float valueForRange() const override;
+ float maxValueForRange() const override;
+ 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 override;
+ bool computeAccessibilityIsIgnored() const override;
};
-
} // namespace WebCore
-#endif // ENABLE(PROGRESS_ELEMENT) || ENABLE(METER_ELEMENT)
-
-#endif // AccessibilityProgressIndicator_h
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilityProgressIndicator, isProgressIndicator())
diff --git a/Source/WebCore/accessibility/AccessibilityRenderObject.cpp b/Source/WebCore/accessibility/AccessibilityRenderObject.cpp
index 80e59420e..3b7edcaa9 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.
*
@@ -36,32 +36,33 @@
#include "AccessibilitySpinButton.h"
#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 "HTMLDetailsElement.h"
#include "HTMLFormElement.h"
#include "HTMLFrameElementBase.h"
#include "HTMLImageElement.h"
#include "HTMLInputElement.h"
#include "HTMLLabelElement.h"
#include "HTMLMapElement.h"
+#include "HTMLMeterElement.h"
#include "HTMLNames.h"
-#include "HTMLOptGroupElement.h"
#include "HTMLOptionElement.h"
#include "HTMLOptionsCollection.h"
#include "HTMLSelectElement.h"
+#include "HTMLSummaryElement.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 "Page.h"
#include "ProgressTracker.h"
@@ -75,13 +76,13 @@
#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 "RenderMenuList.h"
#include "RenderSVGRoot.h"
#include "RenderSVGShape.h"
+#include "RenderTableCell.h"
#include "RenderText.h"
#include "RenderTextControl.h"
#include "RenderTextControlSingleLine.h"
@@ -92,15 +93,14 @@
#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>
namespace WebCore {
@@ -126,9 +126,9 @@ 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(AccessibilityDetachmentType detachmentType, AXObjectCache* cache)
@@ -141,14 +141,14 @@ void AccessibilityRenderObject::detach(AccessibilityDetachmentType detachmentTyp
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)
@@ -157,16 +157,9 @@ 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(RenderInline& renderer)
@@ -174,47 +167,51 @@ static inline RenderObject* firstChildInContinuation(RenderInline& renderer)
auto continuation = renderer.continuation();
while (continuation) {
- if (continuation->isRenderBlock())
+ if (is<RenderBlock>(*continuation))
return continuation;
if (RenderObject* child = continuation->firstChild())
return child;
- continuation = toRenderInline(continuation)->continuation();
+ continuation = downcast<RenderInline>(*continuation).continuation();
}
return nullptr;
}
-static inline RenderObject* firstChildConsideringContinuation(RenderObject* renderer)
+static inline RenderObject* firstChildConsideringContinuation(RenderObject& renderer)
{
- RenderObject* firstChild = renderer->firstChildSlow();
+ RenderObject* firstChild = renderer.firstChildSlow();
+ // We don't want to include the end of a continuation as the firstChild of the
+ // anonymous parent, because everything has already been linked up via continuation.
+ // CSS first-letter selector is an example of this case.
+ if (renderer.isAnonymous() && firstChild && firstChild->isInlineElementContinuation())
+ firstChild = nullptr;
+
if (!firstChild && isInlineWithContinuation(renderer))
- firstChild = firstChildInContinuation(toRenderInline(*renderer));
+ firstChild = firstChildInContinuation(downcast<RenderInline>(renderer));
return firstChild;
}
-static inline RenderObject* lastChildConsideringContinuation(RenderObject* renderer)
+static inline RenderObject* lastChildConsideringContinuation(RenderObject& renderer)
{
- RenderObject* lastChild = renderer->lastChildSlow();
- 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->lastChildSlow())
- 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;
@@ -223,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).
@@ -240,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();
@@ -250,104 +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()->isMathMLElement())
- return nullptr;
-#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()->element()->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 (!cur->isRenderInline() && !cur->isRenderBlock())
- return renderer;
+ if (!is<RenderInline>(renderer) && !is<RenderBlock>(renderer))
+ 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)
{
- RenderObject* child = renderer->firstChildSlow();
+ 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->firstChildSlow())->parent();
- while (firstChildIsInlineContinuation(firstParent))
- firstParent = startOfContinuations(firstParent->firstChildSlow())->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();
}
@@ -357,40 +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)
{
- RenderObject* child = renderer->lastChildSlow();
- return child && isInlineWithContinuation(child);
+ 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)) {
- RenderElement* lastParent = endOfContinuations(toRenderBlock(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();
}
@@ -400,54 +385,61 @@ 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())) {
- auto continuation = toRenderInline(m_renderer->parent())->continuation();
+ // Case 5.1: After case 4, (the element was inline w/ continuation but had no sibling), then check it's parent.
+ if (!nextSibling && 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);
+
+ // After case 4, there are chances that nextSibling has the same node as the current renderer,
+ // which might lead to adding the same child repeatedly.
+ if (nextSibling && nextSibling->node() == m_renderer->node()) {
+ if (AccessibilityObject* nextObj = axObjectCache()->getOrCreate(nextSibling))
+ return nextObj->nextSibling();
+ }
}
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;
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.
RenderInline* startOfConts = nullptr;
- RenderObject* firstChild = 0;
- if (m_renderer->isRenderBlock() && (startOfConts = startOfContinuations(m_renderer)))
+ 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.
@@ -455,7 +447,7 @@ 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;
@@ -476,17 +468,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->view().frameView());
+ 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());
@@ -498,15 +494,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->view().frameView());
+ if (isWebArea())
+ return cache->getOrCreate(&m_renderer->view().frameView());
- return 0;
+ return nullptr;
}
bool AccessibilityRenderObject::isAttachment() const
@@ -522,34 +522,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()) {
- if (HTMLElement* body = m_renderer->document().body()) {
- if (body->hasEditableStyle())
- return false;
- }
-
- return !m_renderer->document().hasEditableStyle();
- }
-
- return AccessibilityNodeObject::isReadOnly();
-}
bool AccessibilityRenderObject::isOffScreen() const
{
- ASSERT(m_renderer);
- IntRect contentRect = pixelSnappedIntRect(m_renderer->absoluteClippedOverflowRect());
+ if (!m_renderer)
+ return true;
+
+ IntRect contentRect = snappedIntRect(m_renderer->absoluteClippedOverflowRect());
// FIXME: unclear if we need LegacyIOSDocumentVisibleRect.
IntRect viewRect = m_renderer->view().frameView().visibleContentRect(ScrollableArea::LegacyIOSDocumentVisibleRect);
viewRect.intersect(contentRect);
@@ -559,33 +545,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
@@ -602,21 +589,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)
@@ -632,28 +620,25 @@ 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");
-#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()) {
- if (ancestorsOfType<RenderMathMLOperator>(*m_renderer).first())
- return toRenderText(*m_renderer).text();
- }
-#endif
+ bool isRenderText = is<RenderText>(*m_renderer);
+
+ 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.childrenInclusion == AccessibilityTextUnderElementMode::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.
- Document* nodeDocument = 0;
+ Document* nodeDocument = nullptr;
RefPtr<Range> textRange;
if (Node* node = m_renderer->node()) {
nodeDocument = &node->document();
@@ -683,34 +668,65 @@ String AccessibilityRenderObject::textUnderElement(AccessibilityTextUnderElement
// catch stale WebCoreAXObject (see <rdar://problem/3960196>)
if (frame->document() != nodeDocument)
return String();
+
+ // 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()) {
-
+ 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 = toRenderTextFragment(renderTextObject)->altText();
+ const String& altText = renderTextFragment.altText();
if (!altText.isEmpty())
return altText;
- return String(static_cast<RenderTextFragment*>(m_renderer)->contentString());
+ 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
{
if (!m_renderer)
- return 0;
+ return nullptr;
if (m_renderer->isRenderView())
return &m_renderer->document();
return m_renderer->node();
@@ -733,25 +749,25 @@ 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);
+ const AtomicString& overriddenDescription = listItems[selectedIndex]->attributeWithoutSynchronization(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())
return String();
@@ -759,8 +775,13 @@ String AccessibilityRenderObject::stringValue() const
if (isTextControl())
return text();
- if (m_renderer->isFileUploadControl())
- return toRenderFileUploadControl(m_renderer)->fileTextValue();
+#if PLATFORM(IOS)
+ if (isInputTypePopupButton())
+ return textUnderElement();
+#endif
+
+ 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;
@@ -772,19 +793,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
@@ -814,27 +835,25 @@ 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())
- quads = toRenderText(obj)->absoluteQuadsClippedToEllipsis();
- 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())
+ if (isWebArea())
result.setSize(obj->view().frameView().contentsSize());
return result;
@@ -845,7 +864,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();
@@ -865,19 +884,13 @@ 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.
if (auto svgRoot = ancestorsOfType<RenderSVGRoot>(*m_renderer).first()) {
@@ -887,7 +900,6 @@ Path AccessibilityRenderObject::elementPath() const
return path;
}
-#endif
return Path();
}
@@ -899,13 +911,13 @@ 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)
+#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));
@@ -914,27 +926,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);
- URL 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
URL documentURL = m_renderer->document().url();
if (!equalIgnoringFragmentIdentifier(documentURL, linkURL))
- return 0;
+ return nullptr;
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);
@@ -948,36 +957,33 @@ ESpeak AccessibilityRenderObject::speakProperty() const
return m_renderer->style().speak();
}
-void AccessibilityRenderObject::addRadioButtonGroupMembers(AccessibilityChildrenVector& linkedUIElements) const
+void AccessibilityRenderObject::addRadioButtonGroupChildren(AccessibilityObject* parent, AccessibilityChildrenVector& linkedUIElements) const
{
- if (!m_renderer || roleValue() != RadioButtonRole)
- return;
+ for (const auto& child : parent->children()) {
+ if (child->roleValue() == RadioButtonRole)
+ linkedUIElements.append(child);
+ else
+ addRadioButtonGroupChildren(child.get(), linkedUIElements);
+ }
+}
- Node* node = m_renderer->node();
- if (!node || !isHTMLInputElement(node))
+void AccessibilityRenderObject::addRadioButtonGroupMembers(AccessibilityChildrenVector& linkedUIElements) const
+{
+ if (roleValue() != RadioButtonRole)
return;
- HTMLInputElement* input = toHTMLInputElement(node);
- // if there's a form, then this is easy
- if (input->form()) {
- Vector<Ref<Element>> formElements;
- input->form()->getNamedElements(input->name(), formElements);
-
- for (auto& associateElement : formElements) {
- if (AccessibilityObject* object = axObjectCache()->getOrCreate(&associateElement.get()))
- linkedUIElements.append(object);
- }
+ Node* node = this->node();
+ if (is<HTMLInputElement>(node)) {
+ HTMLInputElement& input = downcast<HTMLInputElement>(*node);
+ for (auto& radioSibling : input.radioButtonGroup()) {
+ if (AccessibilityObject* object = axObjectCache()->getOrCreate(radioSibling))
+ 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);
- }
- }
+ // If we didn't find any radio button siblings with the traditional naming, lets search for a radio group role and find its children.
+ for (AccessibilityObject* parent = parentObject(); parent; parent = parent->parentObject()) {
+ if (parent->roleValue() == RadioGroupRole)
+ addRadioButtonGroupChildren(parent, linkedUIElements);
}
}
}
@@ -988,7 +994,7 @@ void AccessibilityRenderObject::linkedUIElements(AccessibilityChildrenVector& li
{
ariaFlowToElements(linkedUIElements);
- if (isAnchor()) {
+ if (isLink()) {
AccessibilityObject* linkedAXElement = internalLinkElement();
if (linkedAXElement)
linkedUIElements.append(linkedAXElement);
@@ -1010,42 +1016,6 @@ bool AccessibilityRenderObject::ariaHasPopup() const
return elementAttributeValue(aria_haspopupAttr);
}
-bool AccessibilityRenderObject::supportsARIAFlowTo() const
-{
- return !getAttribute(aria_flowtoAttr).isEmpty();
-}
-
-void AccessibilityRenderObject::ariaFlowToElements(AccessibilityChildrenVector& flowTo) const
-{
- Vector<Element*> elements;
- elementsFromAttribute(elements, aria_flowtoAttr);
-
- AXObjectCache* cache = axObjectCache();
- for (const auto& element : elements) {
- AccessibilityObject* flowToElement = cache->getOrCreate(element);
- if (flowToElement)
- flowTo.append(flowToElement);
- }
-
-}
-
-bool AccessibilityRenderObject::supportsARIADescribedBy() const
-{
- return !getAttribute(aria_describedbyAttr).isEmpty();
-}
-
-void AccessibilityRenderObject::ariaDescribedByElements(AccessibilityChildrenVector& ariaDescribedBy) const
-{
- Vector<Element*> elements;
- elementsFromAttribute(elements, aria_describedbyAttr);
-
- AXObjectCache* cache = axObjectCache();
- for (const auto& element : elements) {
- if (AccessibilityObject* describedByElement = cache->getOrCreate(element))
- ariaDescribedBy.append(describedByElement);
- }
-}
-
bool AccessibilityRenderObject::supportsARIADropping() const
{
const AtomicString& dropEffect = getAttribute(aria_dropeffectAttr);
@@ -1055,7 +1025,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()
@@ -1078,7 +1048,7 @@ void AccessibilityRenderObject::determineARIADropEffects(Vector<String>& effects
bool AccessibilityRenderObject::exposesTitleUIElement() const
{
- if (!isControl())
+ if (!isControl() && !isFigure())
return false;
// If this control is ignored (because it's invisible),
@@ -1090,26 +1060,42 @@ bool AccessibilityRenderObject::exposesTitleUIElement() const
if (hasTextAlternative())
return false;
+ // When <label> element has aria-label or aria-labelledby on it, we shouldn't expose it as the
+ // titleUIElement, otherwise its inner text will be announced by a screenreader.
+ if (isLabelable()) {
+ if (HTMLLabelElement* label = labelForElement(downcast<Element>(node()))) {
+ if (!label->attributeWithoutSynchronization(aria_labelAttr).isEmpty())
+ return false;
+ if (AccessibilityObject* labelObject = axObjectCache()->getOrCreate(label)) {
+ if (!labelObject->ariaLabeledByAttribute().isEmpty())
+ return false;
+ }
+ }
+ }
+
return true;
}
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));
+
+ if (isFigure())
+ return captionForFigure();
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
@@ -1117,7 +1103,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;
@@ -1128,7 +1117,7 @@ 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;
@@ -1156,7 +1145,7 @@ AccessibilityObjectInclusion AccessibilityRenderObject::defaultObjectInclusion()
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;
@@ -1171,6 +1160,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.
@@ -1199,7 +1191,7 @@ bool AccessibilityRenderObject::computeAccessibilityIsIgnored() const
return accessibilityIgnoreAttachment();
// ignore popup menu items because AppKit does
- if (ancestorsOfType<RenderMenuList>(*m_renderer).first())
+ if (m_renderer && ancestorsOfType<RenderMenuList>(*m_renderer).first())
return true;
// find out if this element is inside of a label element.
@@ -1207,16 +1199,20 @@ bool AccessibilityRenderObject::computeAccessibilityIsIgnored() const
AccessibilityObject* controlObject = correspondingControlForLabelElement();
if (controlObject && !controlObject->exposesTitleUIElement() && controlObject->isCheckboxOrRadio())
return true;
+
+ // https://webkit.org/b/161276 Getting the controlObject might cause the m_renderer to be nullptr.
+ if (!m_renderer)
+ return true;
if (m_renderer->isBR())
return true;
- if (m_renderer->isText()) {
+ 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->isMenuItem() || parent->ariaRoleAttribute() == MenuButtonRole))
return true;
- auto& renderText = toRenderText(*m_renderer);
+ auto& renderText = downcast<RenderText>(*m_renderer);
if (!renderText.hasRenderedText())
return true;
@@ -1226,9 +1222,13 @@ bool AccessibilityRenderObject::computeAccessibilityIsIgnored() const
return true;
}
+ // Walking up the parent chain might reset the m_renderer.
+ if (!m_renderer)
+ return true;
+
// The alt attribute may be set on a text fragment through CSS, which should be honored.
- if (renderText.isTextFragment()) {
- AccessibilityObjectInclusion altTextInclusion = objectInclusionFromAltText(toRenderTextFragment(&renderText)->altText());
+ if (is<RenderTextFragment>(renderText)) {
+ AccessibilityObjectInclusion altTextInclusion = objectInclusionFromAltText(downcast<RenderTextFragment>(renderText).altText());
if (altTextInclusion == IgnoreObject)
return true;
if (altTextInclusion == IncludeObject)
@@ -1245,16 +1245,39 @@ bool AccessibilityRenderObject::computeAccessibilityIsIgnored() const
if (isLink())
return false;
+ if (isLandmark())
+ return false;
+
// all controls are accessible
if (isControl())
return false;
- if (ariaRoleAttribute() != UnknownRole)
+ if (isFigure())
return false;
+ switch (roleValue()) {
+ case AudioRole:
+ case DescriptionListTermRole:
+ case DescriptionListDetailRole:
+ case DetailsRole:
+ case DocumentArticleRole:
+ case LandmarkRegionRole:
+ 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.
@@ -1263,14 +1286,6 @@ bool AccessibilityRenderObject::computeAccessibilityIsIgnored() const
if (hasContentEditableAttributeSet())
return false;
- switch (roleValue()) {
- case AudioRole:
- case ListItemRole:
- case VideoRole:
- return false;
- default:
- break;
- }
// if this element has aria attributes on it, it should not be ignored.
if (supportsARIAAttributes())
@@ -1285,8 +1300,8 @@ bool AccessibilityRenderObject::computeAccessibilityIsIgnored() const
return false;
#endif
- if (m_renderer->isRenderBlockFlow() && m_renderer->childrenInline() && !canSetFocusAttribute())
- return !toRenderBlockFlow(m_renderer)->hasLines() && !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()) {
@@ -1298,9 +1313,9 @@ bool AccessibilityRenderObject::computeAccessibilityIsIgnored() const
// 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 = m_renderer && m_renderer->isRenderImage();
+ bool isRenderImage = is<RenderImage>(m_renderer);
if (isRenderImage)
- altTextInclusion = objectInclusionFromAltText(toRenderImage(m_renderer)->altText());
+ altTextInclusion = objectInclusionFromAltText(downcast<RenderImage>(*m_renderer).altText());
else
altTextInclusion = objectInclusionFromAltText(getAttribute(altAttr).string());
@@ -1315,13 +1330,13 @@ bool AccessibilityRenderObject::computeAccessibilityIsIgnored() const
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(toRenderElement(m_renderer), image->view().zoomFactor());
+ if (image.cachedImage()) {
+ LayoutSize imageSize = image.cachedImage()->imageSizeForRenderer(&image, image.view().zoomFactor());
return imageSize.height() <= 1 || imageSize.width() <= 1;
}
}
@@ -1332,17 +1347,31 @@ 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;
+#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())
@@ -1366,7 +1395,14 @@ bool AccessibilityRenderObject::computeAccessibilityIsIgnored() const
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;
}
@@ -1384,18 +1420,14 @@ double AccessibilityRenderObject::estimatedLoadingProgress() const
if (isLoaded())
return 1.0;
- Page* page = m_renderer->document().page();
- if (!page)
- return 0;
-
- return page->progress().estimatedProgress();
+ return m_renderer->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
@@ -1416,20 +1448,27 @@ int AccessibilityRenderObject::textLength() const
return text().length();
}
-PlainTextRange AccessibilityRenderObject::ariaSelectedTextRange() const
+PlainTextRange AccessibilityRenderObject::documentBasedSelectedTextRange() const
{
Node* node = m_renderer->node();
if (!node)
return PlainTextRange();
-
+
VisibleSelection visibleSelection = selection();
RefPtr<Range> currentSelectionRange = visibleSelection.toNormalizedRange();
- if (!currentSelectionRange || !currentSelectionRange->intersectsNode(node, IGNORE_EXCEPTION))
+ if (!currentSelectionRange)
return PlainTextRange();
-
+ // FIXME: The reason this does the correct thing when the selection is in the
+ // shadow tree of an input element is that we get an exception below, and we
+ // choose to interpret all exceptions as "does not intersect". Seems likely
+ // that does not handle all cases correctly.
+ auto intersectsResult = currentSelectionRange->intersectsNode(*node);
+ if (!intersectsResult.hasException() && !intersectsResult.releaseReturnValue())
+ return PlainTextRange();
+
int start = indexForVisiblePosition(visibleSelection.start());
int end = indexForVisiblePosition(visibleSelection.end());
-
+
return PlainTextRange(start, end - start);
}
@@ -1441,24 +1480,19 @@ String AccessibilityRenderObject::selectedText() const
return String(); // need to return something distinct from empty string
if (isNativeTextControl()) {
- HTMLTextFormControlElement& textControl = toRenderTextControl(m_renderer)->textFormControlElement();
+ 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
@@ -1474,57 +1508,82 @@ PlainTextRange AccessibilityRenderObject::selectedTextRange() const
return PlainTextRange();
AccessibilityRole ariaRole = ariaRoleAttribute();
- if (isNativeTextControl() && ariaRole == UnknownRole) {
- HTMLTextFormControlElement& textControl = toRenderTextControl(m_renderer)->textFormControlElement();
+ // 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)
{
+ setTextSelectionIntent(axObjectCache(), range.length ? AXTextStateChangeTypeSelectionExtend : AXTextStateChangeTypeSelectionMove);
+
if (isNativeTextControl()) {
- HTMLTextFormControlElement& textControl = toRenderTextControl(m_renderer)->textFormControlElement();
+ HTMLTextFormControlElement& textControl = downcast<RenderTextControl>(*m_renderer).textFormControlElement();
textControl.setSelectionRange(range.start, range.start + range.length);
- return;
+ } else {
+ ASSERT(node());
+ VisiblePosition start = visiblePositionForIndexUsingCharacterIterator(*node(), range.start);
+ VisiblePosition end = visiblePositionForIndexUsingCharacterIterator(*node(), range.start + range.length);
+ m_renderer->frame().selection().setSelection(VisibleSelection(start, end), FrameSelection::defaultSetSelectionOptions(UserTriggered));
}
-
- Node* node = m_renderer->node();
- m_renderer->frame().selection().setSelection(VisibleSelection(Position(node, range.start, Position::PositionIsOffsetInAnchor),
- Position(node, range.start + range.length, Position::PositionIsOffsetInAnchor), DOWNSTREAM));
+
+ clearTextSelectionIntent(axObjectCache());
}
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();
- 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 URL();
}
bool AccessibilityRenderObject::isUnvisited() const
{
+ if (!m_renderer)
+ return true;
+
// FIXME: Is it a privacy violation to expose unvisited information to accessibility APIs?
return m_renderer->style().isLink() && m_renderer->style().insideLink() == InsideUnvisitedLink;
}
bool AccessibilityRenderObject::isVisited() const
{
+ if (!m_renderer)
+ return false;
+
// FIXME: Is it a privacy violation to expose visited information to accessibility APIs?
return m_renderer->style().isLink() && m_renderer->style().insideLink() == InsideVisitedLink;
}
@@ -1535,11 +1594,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
@@ -1547,7 +1605,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
@@ -1555,17 +1613,19 @@ 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())
return true;
+ // Menu items are considered selectable by assistive technologies
+ if (isMenuItem())
+ return isFocused() || parentObjectUnignored()->activeDescendant() == this;
+
return false;
}
@@ -1588,8 +1648,12 @@ bool AccessibilityRenderObject::isTabItemSelected() const
Vector<Element*> elements;
elementsFromAttribute(elements, aria_controlsAttr);
+ AXObjectCache* cache = axObjectCache();
+ if (!cache)
+ return false;
+
for (const auto& element : elements) {
- AccessibilityObject* tabPanel = axObjectCache()->getOrCreate(element);
+ AccessibilityObject* tabPanel = cache->getOrCreate(element);
// A tab item should only control tab panels.
if (!tabPanel || tabPanel->roleValue() != TabPanelRole)
@@ -1635,25 +1699,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> protectedThis(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();
@@ -1667,35 +1741,19 @@ void AccessibilityRenderObject::setSelectedRows(AccessibilityChildrenVector& sel
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);
- }
-}
-
-void AccessibilityRenderObject::ariaOwnsElements(AccessibilityChildrenVector& axObjects) const
-{
- Vector<Element*> elements;
- elementsFromAttribute(elements, aria_ownsAttr);
-
- for (const auto& element : elements) {
- RenderObject* render = element->renderer();
- AccessibilityObject* obj = axObjectCache()->getOrCreate(render);
- if (obj)
- axObjects.append(obj);
- }
+ if (renderer.isTextField() && is<HTMLInputElement>(element))
+ downcast<HTMLInputElement>(element).setValue(string);
+ else if (renderer.isTextArea() && is<HTMLTextAreaElement>(element))
+ downcast<HTMLTextAreaElement>(element).setValue(string);
}
bool AccessibilityRenderObject::supportsARIAOwns() const
@@ -1711,7 +1769,7 @@ RenderView* AccessibilityRenderObject::topRenderer() const
{
Document* topDoc = topDocument();
if (!topDoc)
- return 0;
+ return nullptr;
return topDoc->renderView();
}
@@ -1719,51 +1777,53 @@ RenderView* AccessibilityRenderObject::topRenderer() const
Document* AccessibilityRenderObject::document() const
{
if (!m_renderer)
- return 0;
+ 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);
+ 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 = toAccessibilityImageMapLink(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);
}
}
}
@@ -1772,7 +1832,7 @@ void AccessibilityRenderObject::getDocumentLinks(AccessibilityChildrenVector& re
FrameView* AccessibilityRenderObject::documentFrameView() const
{
if (!m_renderer)
- return 0;
+ return nullptr;
// this is the RenderObject's Document's Frame's FrameView
return &m_renderer->view().frameView();
@@ -1781,8 +1841,8 @@ FrameView* AccessibilityRenderObject::documentFrameView() const
Widget* AccessibilityRenderObject::widgetForAttachmentView() const
{
if (!isAttachment())
- return 0;
- return toRenderWidget(m_renderer)->widget();
+ return nullptr;
+ return downcast<RenderWidget>(*m_renderer).widget();
}
// This function is like a cross-platform version of - (WebCoreTextMarkerRange*)textMarkerRange. It returns
@@ -1821,7 +1881,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->view().positionForPoint(IntPoint());
+ VisiblePosition visiblePos = m_renderer->view().positionForPoint(IntPoint(), nullptr);
VisiblePosition savedVisiblePos;
while (--lineCount) {
savedVisiblePos = visiblePos;
@@ -1847,22 +1907,22 @@ VisiblePosition AccessibilityRenderObject::visiblePositionForIndex(int index) co
return VisiblePosition();
if (isNativeTextControl())
- return toRenderTextControl(m_renderer)->textFormControlElement().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();
- return visiblePositionForIndexUsingCharacterIterator(node, index);
+ return visiblePositionForIndexUsingCharacterIterator(*node, index);
}
-int AccessibilityRenderObject::indexForVisiblePosition(const VisiblePosition& pos) const
+int AccessibilityRenderObject::indexForVisiblePosition(const VisiblePosition& position) const
{
if (isNativeTextControl())
- return toRenderTextControl(m_renderer)->textFormControlElement().indexForVisiblePosition(pos);
+ return downcast<RenderTextControl>(*m_renderer).textFormControlElement().indexForVisiblePosition(position);
if (!isTextControl())
return 0;
@@ -1871,7 +1931,7 @@ int AccessibilityRenderObject::indexForVisiblePosition(const VisiblePosition& po
if (!node)
return 0;
- Position indexPosition = pos.deepEquivalent();
+ Position indexPosition = position.deepEquivalent();
if (indexPosition.isNull() || highestEditableRoot(indexPosition, HasEditableAXRole) != node)
return 0;
@@ -1883,13 +1943,13 @@ int AccessibilityRenderObject::indexForVisiblePosition(const VisiblePosition& po
bool forSelectionPreservation = false;
#endif
- return WebCore::indexForVisiblePosition(node, pos, forSelectionPreservation);
+ return WebCore::indexForVisiblePosition(*node, position, 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();
@@ -1911,11 +1971,32 @@ 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::boundsForRects(LayoutRect& rect1, LayoutRect& rect2, RefPtr<Range> dataRange) const
+{
+ LayoutRect ourRect = rect1;
+ ourRect.unite(rect2);
+
+ // if the rectangle spans lines and contains multiple text chars, use the range's bounding box intead
+ if (rect1.maxY() != rect2.maxY()) {
+ LayoutRect boundingBox = dataRange->absoluteBoundingBox();
+ String rangeString = plainText(dataRange.get());
+ if (rangeString.length() > 1 && !boundingBox.isEmpty())
+ ourRect = boundingBox;
+ }
+
+#if PLATFORM(MAC)
+ return m_renderer->view().frameView().contentsToScreen(snappedIntRect(ourRect));
+#else
+ return snappedIntRect(ourRect);
+#endif
}
IntRect AccessibilityRenderObject::boundsForVisiblePositionRange(const VisiblePositionRange& visiblePositionRange) const
@@ -1941,23 +2022,56 @@ IntRect AccessibilityRenderObject::boundsForVisiblePositionRange(const VisiblePo
}
}
- LayoutRect ourrect = rect1;
- ourrect.unite(rect2);
+ RefPtr<Range> dataRange = makeRange(range.start, range.end);
+ return boundsForRects(rect1, rect2, dataRange);
+}
+
+IntRect AccessibilityRenderObject::boundsForRange(const RefPtr<Range> range) const
+{
+ if (!range)
+ return IntRect();
- // 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();
- String rangeString = plainText(dataRange.get());
- if (rangeString.length() > 1 && !boundingBox.isEmpty())
- ourrect = boundingBox;
+ AXObjectCache* cache = this->axObjectCache();
+ if (!cache)
+ return IntRect();
+
+ CharacterOffset start = cache->startOrEndCharacterOffsetForRange(range, true);
+ CharacterOffset end = cache->startOrEndCharacterOffsetForRange(range, false);
+
+ LayoutRect rect1 = cache->absoluteCaretBoundsForCharacterOffset(start);
+ LayoutRect rect2 = cache->absoluteCaretBoundsForCharacterOffset(end);
+
+ // readjust for position at the edge of a line. This is to exclude line rect that doesn't need to be accounted in the range bounds.
+ if (rect2.y() != rect1.y()) {
+ CharacterOffset endOfFirstLine = cache->endCharacterOffsetOfLine(start);
+ if (start.isEqual(endOfFirstLine)) {
+ start = cache->nextCharacterOffset(start, false);
+ rect1 = cache->absoluteCaretBoundsForCharacterOffset(start);
+ }
+ if (end.isEqual(endOfFirstLine)) {
+ end = cache->previousCharacterOffset(end, false);
+ rect2 = cache->absoluteCaretBoundsForCharacterOffset(end);
+ }
}
-#if PLATFORM(MAC) && !PLATFORM(IOS)
- return m_renderer->view().frameView().contentsToScreen(pixelSnappedIntRect(ourrect));
-#else
- return pixelSnappedIntRect(ourrect);
-#endif
+ return boundsForRects(rect1, rect2, range);
+}
+
+bool AccessibilityRenderObject::isVisiblePositionRangeInDifferentDocument(const VisiblePositionRange& range) const
+{
+ if (range.start.isNull() || range.end.isNull())
+ return false;
+
+ VisibleSelection newSelection = VisibleSelection(range.start, range.end);
+ if (Document* newSelectionDocument = newSelection.base().document()) {
+ if (RefPtr<Frame> newSelectionFrame = newSelectionDocument->frame()) {
+ Frame* frame = this->frame();
+ if (!frame || (newSelectionFrame != frame && newSelectionDocument != frame->document()))
+ return true;
+ }
+ }
+
+ return false;
}
void AccessibilityRenderObject::setSelectedVisiblePositionRange(const VisiblePositionRange& range) const
@@ -1965,12 +2079,24 @@ void AccessibilityRenderObject::setSelectedVisiblePositionRange(const VisiblePos
if (range.start.isNull() || range.end.isNull())
return;
+ // In WebKit1, when the top web area sets the selection to be an input element in an iframe, the caret will disappear.
+ // FrameSelection::setSelectionWithoutUpdatingAppearance is setting the selection on the new frame in this case, and causing this behavior.
+ if (isWebArea() && parentObject() && parentObject()->isAttachment()) {
+ if (isVisiblePositionRangeInDifferentDocument(range))
+ 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)
+ 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());
}
}
@@ -1984,17 +2110,17 @@ VisiblePosition AccessibilityRenderObject::visiblePositionForPoint(const IntPoin
if (!renderView)
return VisiblePosition();
-#if PLATFORM(MAC)
+#if PLATFORM(COCOA)
FrameView* frameView = &renderView->frameView();
#endif
- Node* innerNode = 0;
+ Node* innerNode = nullptr;
// locate the node containing the point
LayoutPoint pointResult;
while (1) {
LayoutPoint ourpoint;
-#if PLATFORM(MAC) && !PLATFORM(IOS)
+#if PLATFORM(MAC)
ourpoint = frameView->screenToContents(point);
#else
ourpoint = point;
@@ -2014,21 +2140,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())
+ Widget* widget = downcast<RenderWidget>(*renderer).widget();
+ if (!is<FrameView>(widget))
break;
- Frame& frame = toFrameView(widget)->frame();
+ Frame& frame = downcast<FrameView>(*widget).frame();
renderView = frame.document()->renderView();
-#if PLATFORM(MAC)
- frameView = toFrameView(widget);
+#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
@@ -2147,34 +2273,41 @@ IntRect AccessibilityRenderObject::doAXBoundsForRange(const PlainTextRange& rang
return IntRect();
}
+IntRect AccessibilityRenderObject::doAXBoundsForRangeUsingCharacterOffset(const PlainTextRange& range) const
+{
+ if (allowsTextRanges())
+ return boundsForRange(rangeForPlainTextRange(range));
+ return IntRect();
+}
+
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;
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();
+ AccessibilityObject* remote = remoteSVGRootElement(Create);
if (!remote)
- return 0;
+ return nullptr;
IntSize offset = point - roundedIntPoint(boundingBoxRect().location());
return remote->accessibilityHitTest(IntPoint(offset));
@@ -2188,29 +2321,41 @@ AccessibilityObject* AccessibilityRenderObject::elementAccessibilityHitTest(cons
return AccessibilityObject::elementAccessibilityHitTest(point);
}
+static bool shouldUseShadowHostForHitTesting(Node* shadowHost)
+{
+ // We need to allow automation of mouse events on video tags.
+ return shadowHost && !shadowHost->hasTagName(videoTag);
+}
+
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;
- Node* node = hitTestResult.innerNode()->deprecatedShadowAncestorNode();
+ Node* node = hitTestResult.innerNode();
+ if (!node)
+ return nullptr;
+ Node* shadowAncestorNode = node->shadowHost();
+ if (shouldUseShadowHostForHitTesting(shadowAncestorNode))
+ node = shadowAncestorNode;
+ 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);
result->updateChildrenIfNecessary();
@@ -2271,29 +2416,35 @@ 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);
if (!target)
- return 0;
+ return nullptr;
+
+ 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;
+ }
- 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;
+ return nullptr;
}
void AccessibilityRenderObject::handleAriaExpandedChanged()
{
+ // This object might be deleted under the call to the parentObject() method.
+ auto protectedThis = makeRef(*this);
+
// Find if a parent of this object should handle aria-expanded changes.
AccessibilityObject* containerParent = this->parentObject();
while (containerParent) {
@@ -2318,23 +2469,29 @@ void AccessibilityRenderObject::handleAriaExpandedChanged()
}
// Post that the row count changed.
+ AXObjectCache* cache = axObjectCache();
+ if (!cache)
+ return;
+
if (containerParent)
- axObjectCache()->postNotification(containerParent, document(), AXObjectCache::AXRowCountChanged);
+ 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);
+ 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;
if (!renderer()->frame().selection().isFocusedAndActive() || renderer()->document().focusedElement() != element)
return;
- if (toAccessibilityRenderObject(activeDescendant()) && shouldNotifyActiveDescendant())
+ if (activeDescendant() && shouldNotifyActiveDescendant())
renderer()->document().axObjectCache()->postNotification(m_renderer, AXObjectCache::AXActiveDescendantChanged);
}
@@ -2342,15 +2499,15 @@ AccessibilityObject* AccessibilityRenderObject::correspondingControlForLabelElem
{
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);
}
@@ -2358,36 +2515,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;
@@ -2397,41 +2556,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).hasAttributeWithoutSynchronization(usemapAttr))
+ return ImageMapRole;
if ((cssBox && cssBox->isListItem()) || (node && node->hasTagName(liTag)))
return ListItemRole;
if (m_renderer->isListMarker())
@@ -2443,7 +2622,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;
@@ -2453,35 +2632,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;
@@ -2491,23 +2674,11 @@ 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 ENABLE(MATHML)
- if (node && node->hasTagName(MathMLNames::mathTag))
- return DocumentMathRole;
-#endif
- // It's not clear which role a platform should choose for a math element.
- // Declaring a math element role should give flexibility to platforms to choose.
- if (isMathElement())
- return MathElementRole;
+
+ if (isStyleFormatGroup())
+ return is<RenderInline>(*m_renderer) ? InlineRole : GroupRole;
if (node && node->hasTagName(ddTag))
return DescriptionListDetailRole;
@@ -2518,19 +2689,27 @@ AccessibilityRole AccessibilityRenderObject::determineAccessibilityRole()
if (node && node->hasTagName(dlTag))
return DescriptionListRole;
- if (node && (node->hasTagName(rpTag) || node->hasTagName(rtTag)))
- return AnnotationRole;
-
+ // 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)
- // 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;
+ return DivRole;
+#else
+ return GroupRole;
#endif
// Table sections should be ignored.
@@ -2543,7 +2722,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))
@@ -2552,7 +2731,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))
@@ -2567,16 +2746,41 @@ AccessibilityRole AccessibilityRenderObject::determineAccessibilityRole()
if (node && node->hasTagName(asideTag))
return LandmarkComplementaryRole;
+ // The default role attribute value for the section element, region, became a landmark in ARIA 1.1.
+ // The HTML AAM spec says it is "strongly recommended" that ATs only convey and provide navigation
+ // for section elements which have names.
if (node && node->hasTagName(sectionTag))
- return DocumentRegionRole;
+ return hasAttribute(aria_labelAttr) || hasAttribute(aria_labelledbyAttr) ? LandmarkRegionRole : GroupRole;
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(markTag))
+ return MarkRole;
+
+ if (node && node->hasTagName(preTag))
+ return PreRole;
+
+ if (is<HTMLDetailsElement>(node))
+ return DetailsRole;
+ if (is<HTMLSummaryElement>(node))
+ return SummaryRole;
+
+ // http://rawgit.com/w3c/aria/master/html-aam/html-aam.html
+ // Output elements should be mapped to status role.
+ if (isOutput())
+ return ApplicationStatusRole;
+
#if ENABLE(VIDEO)
- if (node && isHTMLVideoElement(node))
+ if (is<HTMLVideoElement>(node))
return VideoRole;
- if (node && isHTMLAudioElement(node))
+ if (is<HTMLAudioElement>(node))
return AudioRole;
#endif
@@ -2590,25 +2794,55 @@ AccessibilityRole AccessibilityRenderObject::determineAccessibilityRole()
return LandmarkBannerRole;
if (node && node->hasTagName(footerTag) && !isDescendantOfElementType(articleTag) && !isDescendantOfElementType(sectionTag))
return FooterRole;
-
- if (m_renderer->isRenderBlockFlow())
- return GroupRole;
+
+ // menu tags with toolbar type should have Toolbar role.
+ if (node && node->hasTagName(menuTag) && equalLettersIgnoringASCIICase(getAttribute(typeAttr), "toolbar"))
+ return ToolbarRole;
// 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)
+ // 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 +2855,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 +2884,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 = toAccessibilityRenderObject(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,37 +2927,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 (isMeter())
- return false;
-
- 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
@@ -2729,15 +2945,18 @@ 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);
+ cache->postLiveRegionChangeNotification(parent);
- if (parent->isARIATextControl() && !parent->isNativeTextControl() && !parent->node()->hasEditableStyle())
+ if (parent->isNonNativeTextControl())
cache->postNotification(renderParent, AXObjectCache::AXValueChanged);
}
}
@@ -2751,10 +2970,10 @@ 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;
@@ -2762,14 +2981,14 @@ void AccessibilityRenderObject::addImageMapChildren()
// add an <area> element for this child if it has a link
if (!area.isLink())
continue;
- AccessibilityImageMapLink* areaObject = toAccessibilityImageMapLink(axObjectCache()->getOrCreate(ImageMapLinkRole));
- areaObject->setHTMLAreaElement(&area);
- areaObject->setHTMLMapElement(map);
- areaObject->setParent(this);
- if (!areaObject->accessibilityIsIgnored())
- m_children.append(areaObject);
+ 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());
+ axObjectCache()->remove(areaObject.axObjectID());
}
}
@@ -2784,56 +3003,59 @@ 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);
+ if (HTMLElement* autoFillElement = input.autoFillButtonElement()) {
+ if (AccessibilityObject* axAutoFill = axObjectCache()->getOrCreate(autoFillElement))
+ m_children.append(axAutoFill);
+ }
+
+ HTMLElement* spinButtonElement = input.innerSpinButtonElement();
+ if (!is<SpinButtonElement>(spinButtonElement))
return;
- AccessibilitySpinButton* axSpinButton = toAccessibilitySpinButton(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
{
- return remoteSVGRootElement();
+ return remoteSVGRootElement(Create);
}
void AccessibilityRenderObject::detachRemoteSVGRoot()
{
- if (AccessibilitySVGRoot* root = remoteSVGRootElement())
- root->setParent(0);
+ if (AccessibilitySVGRoot* root = remoteSVGRootElement(Retrieve))
+ root->setParent(nullptr);
}
-AccessibilitySVGRoot* AccessibilityRenderObject::remoteSVGRootElement() const
+AccessibilitySVGRoot* AccessibilityRenderObject::remoteSVGRootElement(CreationChoice createIfNecessary) const
{
-#if ENABLE(SVG)
- if (!m_renderer || !m_renderer->isRenderImage())
+ if (!is<RenderImage>(m_renderer))
return nullptr;
- CachedImage* cachedImage = toRenderImage(m_renderer)->cachedImage();
+ CachedImage* cachedImage = downcast<RenderImage>(*m_renderer).cachedImage();
if (!cachedImage)
return nullptr;
Image* image = cachedImage->image();
- if (!image || !image->isSVGImage())
+ 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 nullptr;
Frame& frame = frameView->frame();
- Document* doc = frame.document();
- if (!doc || !doc->isSVGDocument())
+ Document* document = frame.document();
+ if (!is<SVGDocument>(document))
return nullptr;
- SVGSVGElement* rootElement = toSVGDocument(doc)->rootElement();
+ SVGSVGElement* rootElement = SVGDocument::rootElement(*document);
if (!rootElement)
return nullptr;
RenderObject* rendererRoot = rootElement->renderer();
@@ -2843,23 +3065,20 @@ AccessibilitySVGRoot* AccessibilityRenderObject::remoteSVGRootElement() const
AXObjectCache* cache = frame.document()->axObjectCache();
if (!cache)
return nullptr;
- AccessibilityObject* rootSVGObject = cache->getOrCreate(rendererRoot);
+ AccessibilityObject* rootSVGObject = createIfNecessary == Create ? cache->getOrCreate(rendererRoot) : cache->get(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())
+ ASSERT(!createIfNecessary || rootSVGObject);
+ if (!is<AccessibilitySVGRoot>(rootSVGObject))
return nullptr;
- return toAccessibilitySVGRoot(rootSVGObject);
-#else
- return nullptr;
-#endif
+ return downcast<AccessibilitySVGRoot>(rootSVGObject);
}
void AccessibilityRenderObject::addRemoteSVGChildren()
{
- AccessibilitySVGRoot* root = remoteSVGRootElement();
+ AccessibilitySVGRoot* root = remoteSVGRootElement(Create);
if (!root)
return;
@@ -2901,7 +3120,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
@@ -2949,7 +3168,7 @@ void AccessibilityRenderObject::addHiddenChildren()
if (children.size())
childObject = children.last().get();
else
- childObject = 0;
+ childObject = nullptr;
}
if (childObject)
@@ -2969,6 +3188,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,
@@ -2990,9 +3227,11 @@ void AccessibilityRenderObject::addChildren()
addCanvasChildren();
addRemoteSVGChildren();
-#if PLATFORM(MAC)
+#if PLATFORM(COCOA)
updateAttachmentViewParents();
#endif
+
+ updateRoleAfterChildrenCreation();
}
bool AccessibilityRenderObject::canHaveChildren() const
@@ -3003,36 +3242,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".
@@ -3044,23 +3266,49 @@ 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
+bool AccessibilityRenderObject::isBusy() const
{
return elementAttributeValue(aria_busyAttr);
}
+
+bool AccessibilityRenderObject::canHaveSelectedChildren() const
+{
+ switch (roleValue()) {
+ // These roles are containers whose children support aria-selected:
+ case GridRole:
+ case ListBoxRole:
+ case TabListRole:
+ case TreeRole:
+ case TreeGridRole:
+ // These roles are containers whose children are treated as selected by assistive
+ // technologies. We can get the "selected" item via aria-activedescendant or the
+ // focused element.
+ case MenuRole:
+ case MenuBarRole:
+ return true;
+ default:
+ return false;
+ }
+}
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();
@@ -3072,12 +3320,24 @@ void AccessibilityRenderObject::ariaSelectedRows(AccessibilityChildrenVector& re
return;
}
- for (const auto& row : allRows) {
- if (row->isSelected()) {
- result.append(row);
- if (!isMulti)
- break;
+ // Get all the rows.
+ auto rowsIteration = [&](auto& 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());
}
}
@@ -3099,12 +3359,36 @@ void AccessibilityRenderObject::selectedChildren(AccessibilityChildrenVector& re
{
ASSERT(result.isEmpty());
- // only listboxes should be asked for their selected children.
- AccessibilityRole role = roleValue();
- if (role == ListBoxRole) // native list boxes would be AccessibilityListBoxes, so only check for aria list boxes
+ if (!canHaveSelectedChildren())
+ return;
+
+ switch (roleValue()) {
+ case ListBoxRole:
+ // native list boxes would be AccessibilityListBoxes, so only check for aria list boxes
ariaListboxSelectedChildren(result);
- else if (role == TreeRole || role == TreeGridRole || role == TableRole)
+ return;
+ case GridRole:
+ case TreeRole:
ariaSelectedRows(result);
+ return;
+ case TabListRole:
+ if (AccessibilityObject* selectedTab = selectedTabItem())
+ result.append(selectedTab);
+ return;
+ case MenuRole:
+ case MenuBarRole:
+ if (AccessibilityObject* descendant = activeDescendant()) {
+ result.append(descendant);
+ return;
+ }
+ if (AccessibilityObject* focusedElement = focusedUIElement()) {
+ result.append(focusedElement);
+ return;
+ }
+ return;
+ default:
+ ASSERT_NOT_REACHED();
+ }
}
void AccessibilityRenderObject::ariaListboxVisibleChildren(AccessibilityChildrenVector& result)
@@ -3144,12 +3428,12 @@ 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()));
+ 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:
@@ -3179,15 +3463,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)
@@ -3204,8 +3488,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();
@@ -3217,10 +3501,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
@@ -3293,12 +3577,16 @@ String AccessibilityRenderObject::nameForMSAA() const
static bool shouldReturnTagNameAsRoleForMSAA(const Element& element)
{
- // See "document structure",
- // https://wiki.mozilla.org/Accessibility/AT-Windows-API
- // FIXME: Add the other tag names that should be returned as the role.
- return element.hasTagName(h1Tag) || element.hasTagName(h2Tag)
+ return element.hasTagName(abbrTag) || element.hasTagName(acronymTag)
+ || element.hasTagName(blockquoteTag) || element.hasTagName(ddTag)
+ || element.hasTagName(dlTag) || element.hasTagName(dtTag)
+ || element.hasTagName(formTag) || element.hasTagName(frameTag)
+ || element.hasTagName(h1Tag) || element.hasTagName(h2Tag)
|| element.hasTagName(h3Tag) || element.hasTagName(h4Tag)
- || element.hasTagName(h5Tag) || element.hasTagName(h6Tag);
+ || element.hasTagName(h5Tag) || element.hasTagName(h6Tag)
+ || element.hasTagName(iframeTag) || element.hasTagName(qTag)
+ || element.hasTagName(tbodyTag) || element.hasTagName(tfootTag)
+ || element.hasTagName(theadTag);
}
String AccessibilityRenderObject::stringRoleForMSAA() const
@@ -3307,14 +3595,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
@@ -3352,10 +3640,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;
@@ -3380,397 +3668,52 @@ String AccessibilityRenderObject::passwordFieldValue() const
// Look for the RenderText object in the RenderObject tree for this input field.
RenderObject* renderer = node()->renderer();
- while (renderer && !renderer->isText())
- renderer = toRenderElement(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 toRenderText(renderer)->textWithoutConvertingBackslashToYenSymbol();
+ 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)
- return false;
-
- return node->isMathMLElement();
-}
-
-bool AccessibilityRenderObject::isMathFraction() const
-{
- return m_renderer && m_renderer->isRenderMathMLFraction();
-}
-
-bool AccessibilityRenderObject::isMathFenced() const
-{
- return m_renderer && m_renderer->isRenderMathMLFenced();
-}
-
-bool AccessibilityRenderObject::isMathSubscriptSuperscript() const
-{
- return m_renderer && m_renderer->isRenderMathMLScripts() && !isMathMultiscript();
-}
-
-bool AccessibilityRenderObject::isMathRow() const
-{
- return m_renderer && m_renderer->isRenderMathMLRow();
-}
-
-bool AccessibilityRenderObject::isMathUnderOver() const
-{
- return m_renderer && m_renderer->isRenderMathMLUnderOver();
-}
-
-bool AccessibilityRenderObject::isMathSquareRoot() const
-{
- return m_renderer && m_renderer->isRenderMathMLSquareRoot();
-}
-
-bool AccessibilityRenderObject::isMathRoot() const
-{
- return m_renderer && m_renderer->isRenderMathMLRoot();
-}
-
-bool AccessibilityRenderObject::isMathOperator() const
-{
- if (!m_renderer || !m_renderer->isRenderMathMLOperator())
- 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.
- return isMathElement() && node()->hasTagName(MathMLNames::moTag);
-}
-
-bool AccessibilityRenderObject::isMathFenceOperator() const
-{
- if (!m_renderer || !m_renderer->isRenderMathMLOperator())
- return false;
-
- return toRenderMathMLOperator(*m_renderer).operatorType() == RenderMathMLOperator::Fence;
-}
-
-bool AccessibilityRenderObject::isMathSeparatorOperator() const
-{
- if (!m_renderer || !m_renderer->isRenderMathMLOperator())
- return false;
-
- return toRenderMathMLOperator(*m_renderer).operatorType() == RenderMathMLOperator::Separator;
-}
-
-bool AccessibilityRenderObject::isMathText() const
-{
- return node() && node()->hasTagName(MathMLNames::mtextTag);
-}
-
-bool AccessibilityRenderObject::isMathNumber() const
-{
- return node() && node()->hasTagName(MathMLNames::mnTag);
-}
-
-bool AccessibilityRenderObject::isMathIdentifier() const
-{
- return node() && node()->hasTagName(MathMLNames::miTag);
-}
-
-bool AccessibilityRenderObject::isMathMultiscript() const
-{
- return node() && node()->hasTagName(MathMLNames::mmultiscriptsTag);
-}
-
-bool AccessibilityRenderObject::isMathTable() const
-{
- return node() && node()->hasTagName(MathMLNames::mtableTag);
-}
-
-bool AccessibilityRenderObject::isMathTableRow() const
-{
- return node() && node()->hasTagName(MathMLNames::mtrTag);
-}
-
-bool AccessibilityRenderObject::isMathTableCell() const
-{
- return node() && node()->hasTagName(MathMLNames::mtdTag);
-}
-
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.
- if (m_renderer->isAnonymous()) {
- for (AccessibilityObject* parent = parentObject(); parent; parent = parent->parentObject()) {
- if (parent->isMathElement())
- return true;
- }
- }
-
- // Only math elements that we explicitly recognize should be included
- // We don't want things like <mstyle> to appear in the tree.
- if (isMathElement()) {
- if (isMathFraction() || isMathFenced() || isMathSubscriptSuperscript() || isMathRow()
- || isMathUnderOver() || isMathRoot() || isMathText() || isMathNumber()
- || isMathOperator() || isMathFenceOperator() || isMathSeparatorOperator()
- || isMathIdentifier() || isMathTable() || isMathTableRow() || isMathTableCell() || isMathMultiscript())
- return false;
- return true;
- }
-
- return false;
-}
-
-AccessibilityObject* AccessibilityRenderObject::mathRadicandObject()
-{
- if (!isMathRoot())
- return 0;
-
- const auto& 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();
+ // We ignore anonymous boxes inserted into RenderMathMLBlocks to honor CSS rules.
+ // See https://www.w3.org/TR/css3-box/#block-level0
+ return m_renderer && m_renderer->isAnonymous() && m_renderer->parent() && is<RenderMathMLBlock>(m_renderer->parent());
}
-
-AccessibilityObject* AccessibilityRenderObject::mathRootIndexObject()
-{
- if (!isMathRoot())
- return 0;
-
- const auto& 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();
-}
-
-AccessibilityObject* AccessibilityRenderObject::mathNumeratorObject()
-{
- if (!isMathFraction())
- return 0;
-
- const auto& children = this->children();
- if (children.size() != 2)
- return 0;
-
- return children[0].get();
-}
-
-AccessibilityObject* AccessibilityRenderObject::mathDenominatorObject()
-{
- if (!isMathFraction())
- return 0;
-
- const auto& children = this->children();
- if (children.size() != 2)
- return 0;
-
- return children[1].get();
-}
-
-AccessibilityObject* AccessibilityRenderObject::mathUnderObject()
-{
- if (!isMathUnderOver() || !node())
- return 0;
-
- const auto& children = this->children();
- if (children.size() < 2)
- return 0;
-
- if (node()->hasTagName(MathMLNames::munderTag) || node()->hasTagName(MathMLNames::munderoverTag))
- return children[1].get();
-
- return 0;
-}
-
-AccessibilityObject* AccessibilityRenderObject::mathOverObject()
-{
- if (!isMathUnderOver() || !node())
- return 0;
-
- const auto& children = this->children();
- if (children.size() < 2)
- return 0;
-
- if (node()->hasTagName(MathMLNames::moverTag))
- return children[1].get();
- if (node()->hasTagName(MathMLNames::munderoverTag))
- return children[2].get();
-
- return 0;
-}
-
-AccessibilityObject* AccessibilityRenderObject::mathBaseObject()
-{
- if (!isMathSubscriptSuperscript() && !isMathUnderOver() && !isMathMultiscript())
- return 0;
-
- 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;
-}
-
-AccessibilityObject* AccessibilityRenderObject::mathSubscriptObject()
-{
- if (!isMathSubscriptSuperscript() || !node())
- return 0;
-
- const auto& children = this->children();
- if (children.size() < 2)
- return 0;
-
- if (node()->hasTagName(MathMLNames::msubTag) || node()->hasTagName(MathMLNames::msubsupTag))
- return children[1].get();
-
- return 0;
-}
-
-AccessibilityObject* AccessibilityRenderObject::mathSuperscriptObject()
-{
- if (!isMathSubscriptSuperscript() || !node())
- return 0;
-
- const auto& children = this->children();
- unsigned count = children.size();
-
- if (count >= 2 && node()->hasTagName(MathMLNames::msupTag))
- return children[1].get();
-
- if (count >= 3 && node()->hasTagName(MathMLNames::msubsupTag))
- return children[2].get();
-
- return 0;
-}
-
-String AccessibilityRenderObject::mathFencedOpenString() const
-{
- if (!isMathFenced())
- return String();
-
- return getAttribute(MathMLNames::openAttr);
-}
-
-String AccessibilityRenderObject::mathFencedCloseString() const
-{
- if (!isMathFenced())
- return String();
-
- return getAttribute(MathMLNames::closeAttr);
-}
-
-void AccessibilityRenderObject::mathPrescripts(AccessibilityMathMultiscriptPairs& prescripts)
-{
- if (!isMathMultiscript() || !node())
- return;
-
- bool foundPrescript = false;
- std::pair<AccessibilityObject*, AccessibilityObject*> prescriptPair;
- for (Node* child = node()->firstChild(); child; child = child->nextSibling()) {
- if (foundPrescript) {
- AccessibilityObject* axChild = axObjectCache()->getOrCreate(child);
- if (axChild && axChild->isMathElement()) {
- if (!prescriptPair.first)
- prescriptPair.first = axChild;
- else {
- prescriptPair.second = axChild;
- prescripts.append(prescriptPair);
- prescriptPair.first = 0;
- prescriptPair.second = 0;
- }
- }
- } else if (child->hasTagName(MathMLNames::mprescriptsTag))
- foundPrescript = true;
- }
-
- // Handle the odd number of pre scripts case.
- if (prescriptPair.first)
- prescripts.append(prescriptPair);
-}
-
-void AccessibilityRenderObject::mathPostscripts(AccessibilityMathMultiscriptPairs& postscripts)
-{
- if (!isMathMultiscript() || !node())
- return;
-
- // In Multiscripts, the post-script elements start after the first element (which is the base)
- // and continue until a <mprescripts> tag is found
- std::pair<AccessibilityObject*, AccessibilityObject*> postscriptPair;
- bool foundBaseElement = false;
- for (Node* child = node()->firstChild(); child; child = child->nextSibling()) {
- if (child->hasTagName(MathMLNames::mprescriptsTag))
- break;
-
- AccessibilityObject* axChild = axObjectCache()->getOrCreate(child);
- if (axChild && axChild->isMathElement()) {
- if (!foundBaseElement)
- foundBaseElement = true;
- else if (!postscriptPair.first)
- postscriptPair.first = axChild;
- else {
- postscriptPair.second = axChild;
- postscripts.append(postscriptPair);
- postscriptPair.first = 0;
- postscriptPair.second = 0;
- }
- }
- }
-
- // Handle the odd number of post scripts case.
- if (postscriptPair.first)
- postscripts.append(postscriptPair);
-}
-
-int AccessibilityRenderObject::mathLineThickness() const
-{
- if (!isMathFraction())
- return -1;
-
- return toRenderMathMLFraction(m_renderer)->lineThickness();
-}
-
#endif
} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AccessibilityRenderObject.h b/Source/WebCore/accessibility/AccessibilityRenderObject.h
index 3902fb8c6..cccb39ebe 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.
*
@@ -26,8 +26,7 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AccessibilityRenderObject_h
-#define AccessibilityRenderObject_h
+#pragma once
#include "AccessibilityNodeObject.h"
#include "LayoutRect.h"
@@ -38,222 +37,219 @@ namespace WebCore {
class AccessibilitySVGRoot;
class AXObjectCache;
class Element;
-class Frame;
class FrameView;
-class HitTestResult;
-class HTMLAnchorElement;
class HTMLAreaElement;
class HTMLElement;
class HTMLLabelElement;
class HTMLMapElement;
-class HTMLSelectElement;
class IntPoint;
class IntSize;
class Node;
-class RenderListBox;
class RenderTextControl;
class RenderView;
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 override { return true; }
- virtual void init() override;
+ void init() override;
- virtual bool isAttachment() const override;
- virtual bool isFileUploadButton() const override;
+ bool isAttachment() const override;
+ bool isFileUploadButton() const override;
- virtual bool isSelected() const override;
- virtual bool isFocused() const override;
- virtual bool isLoaded() const override;
- virtual bool isOffScreen() const override;
- virtual bool isReadOnly() 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;
+ bool isSelected() const override;
+ bool isFocused() const override;
+ bool isLoaded() const override;
+ bool isOffScreen() const override;
+ bool isUnvisited() const override;
+ bool isVisited() const override;
+ bool isLinked() const override;
+ bool hasBoldFont() const override;
+ bool hasItalicFont() const override;
+ bool hasPlainText() const override;
+ bool hasSameFont(RenderObject*) const override;
+ bool hasSameFontColor(RenderObject*) const override;
+ bool hasSameStyle(RenderObject*) const override;
+ bool hasUnderline() const override;
- virtual bool canSetTextRangeAttributes() const override;
- virtual bool canSetValueAttribute() const override;
- virtual bool canSetExpandedAttribute() const override;
+ bool canSetTextRangeAttributes() const override;
+ bool canSetExpandedAttribute() const override;
- virtual void setAccessibleName(const AtomicString&) override;
+ void setAccessibleName(const AtomicString&) override;
// Provides common logic used by all elements when determining isIgnored.
- virtual AccessibilityObjectInclusion defaultObjectInclusion() const override;
+ AccessibilityObjectInclusion defaultObjectInclusion() const override;
- virtual int layoutCount() const override;
- virtual double estimatedLoadingProgress() const override;
+ int layoutCount() const override;
+ double estimatedLoadingProgress() const override;
- 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;
+ AccessibilityObject* firstChild() const override;
+ AccessibilityObject* lastChild() const override;
+ AccessibilityObject* previousSibling() const override;
+ AccessibilityObject* nextSibling() const override;
+ AccessibilityObject* parentObject() const override;
+ AccessibilityObject* parentObjectIfExists() const override;
+ AccessibilityObject* observableObject() const override;
+ void linkedUIElements(AccessibilityChildrenVector&) const override;
+ bool exposesTitleUIElement() const override;
+ AccessibilityObject* titleUIElement() const override;
+ AccessibilityObject* correspondingControlForLabelElement() const override;
+ AccessibilityObject* correspondingLabelForControlElement() const override;
- virtual void ariaOwnsElements(AccessibilityChildrenVector&) const override;
- virtual bool supportsARIAOwns() const override;
- virtual bool isPresentationalChildOfAriaRole() const override;
- virtual bool ariaRoleHasPresentationalChildren() const override;
+ bool supportsARIAOwns() const override;
+ bool isPresentationalChildOfAriaRole() const override;
+ bool ariaRoleHasPresentationalChildren() const override;
// Should be called on the root accessibility object to kick off a hit test.
- virtual AccessibilityObject* accessibilityHitTest(const IntPoint&) const override;
+ AccessibilityObject* accessibilityHitTest(const IntPoint&) const override;
- virtual Element* anchorElement() const override;
+ Element* anchorElement() const override;
- virtual LayoutRect boundingBoxRect() const override;
- virtual LayoutRect elementRect() const override;
- virtual IntPoint clickPoint() override;
+ LayoutRect boundingBoxRect() const override;
+ LayoutRect elementRect() const override;
+ IntPoint clickPoint() override;
void setRenderer(RenderObject*);
- virtual RenderObject* renderer() const override { return m_renderer; }
+ RenderObject* renderer() const override { return m_renderer; }
RenderBoxModelObject* renderBoxModelObject() const;
- virtual Node* node() const override;
+ Node* node() const override;
- virtual Document* document() const override;
+ Document* document() const override;
RenderView* topRenderer() const;
RenderTextControl* textControl() const;
HTMLLabelElement* labelElementContainer() 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;
+ URL url() const override;
+ PlainTextRange selectedTextRange() const override;
+ VisibleSelection selection() const override;
+ String stringValue() const override;
+ String helpText() const override;
+ String textUnderElement(AccessibilityTextUnderElementMode = AccessibilityTextUnderElementMode()) const override;
+ String text() const override;
+ int textLength() const override;
+ String selectedText() const override;
+ const AtomicString& accessKey() const override;
virtual const String& actionVerb() const;
- virtual Widget* widget() const override;
- virtual Widget* widgetForAttachmentView() const override;
+ Widget* widget() const override;
+ Widget* widgetForAttachmentView() const override;
virtual void getDocumentLinks(AccessibilityChildrenVector&);
- virtual FrameView* documentFrameView() const override;
+ FrameView* documentFrameView() const override;
- virtual void clearChildren() override;
- virtual void updateChildrenIfNecessary() override;
+ void clearChildren() override;
+ void updateChildrenIfNecessary() override;
- 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;
+ void setFocused(bool) override;
+ void setSelectedTextRange(const PlainTextRange&) override;
+ void setValue(const String&) override;
+ void setSelectedRows(AccessibilityChildrenVector&) override;
+ AccessibilityOrientation orientation() const override;
- 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;
+ void detach(AccessibilityDetachmentType, AXObjectCache*) override;
+ void textChanged() override;
+ void addChildren() override;
+ bool canHaveChildren() const override;
+ bool canHaveSelectedChildren() const override;
+ void selectedChildren(AccessibilityChildrenVector&) override;
+ void visibleChildren(AccessibilityChildrenVector&) override;
+ void tabChildren(AccessibilityChildrenVector&) override;
+ bool shouldFocusActiveDescendant() const override;
bool shouldNotifyActiveDescendant() const;
- virtual AccessibilityObject* activeDescendant() const override;
- virtual void handleActiveDescendantChanged() override;
- virtual void handleAriaExpandedChanged() override;
+ AccessibilityObject* activeDescendant() const override;
+ void handleActiveDescendantChanged() override;
+ void handleAriaExpandedChanged() override;
- 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 ariaHasPopup() const override;
+ VisiblePositionRange visiblePositionRange() const override;
+ VisiblePositionRange visiblePositionRangeForLine(unsigned) const override;
+ IntRect boundsForVisiblePositionRange(const VisiblePositionRange&) const override;
+ IntRect boundsForRange(const RefPtr<Range>) const override;
+ IntRect boundsForRects(LayoutRect&, LayoutRect&, RefPtr<Range>) const;
+ void setSelectedVisiblePositionRange(const VisiblePositionRange&) const override;
+ bool isVisiblePositionRangeInDifferentDocument(const VisiblePositionRange&) const;
+ bool ariaHasPopup() const override;
- virtual bool supportsARIADropping() const override;
- virtual bool supportsARIADragging() const override;
- virtual bool isARIAGrabbed() override;
- virtual void determineARIADropEffects(Vector<String>&) override;
+ bool supportsARIADropping() const override;
+ bool supportsARIADragging() const override;
+ bool isARIAGrabbed() override;
+ void determineARIADropEffects(Vector<String>&) override;
- virtual VisiblePosition visiblePositionForPoint(const IntPoint&) const override;
- virtual VisiblePosition visiblePositionForIndex(unsigned indexValue, bool lastIndexOK) const override;
- virtual int index(const VisiblePosition&) const override;
+ VisiblePosition visiblePositionForPoint(const IntPoint&) const override;
+ VisiblePosition visiblePositionForIndex(unsigned indexValue, bool lastIndexOK) const override;
+ int index(const VisiblePosition&) const override;
- virtual VisiblePosition visiblePositionForIndex(int) const override;
- virtual int indexForVisiblePosition(const VisiblePosition&) const override;
+ VisiblePosition visiblePositionForIndex(int) const override;
+ int indexForVisiblePosition(const VisiblePosition&) const override;
- virtual void lineBreaks(Vector<int>&) const override;
- virtual PlainTextRange doAXRangeForLine(unsigned) const override;
- virtual PlainTextRange doAXRangeForIndex(unsigned) const override;
+ void lineBreaks(Vector<int>&) const override;
+ PlainTextRange doAXRangeForLine(unsigned) const override;
+ PlainTextRange doAXRangeForIndex(unsigned) const override;
- virtual String doAXStringForRange(const PlainTextRange&) const override;
- virtual IntRect doAXBoundsForRange(const PlainTextRange&) const override;
+ String doAXStringForRange(const PlainTextRange&) const override;
+ IntRect doAXBoundsForRange(const PlainTextRange&) const override;
+ IntRect doAXBoundsForRangeUsingCharacterOffset(const PlainTextRange&) const override;
- 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;
+ String stringValueForMSAA() const override;
+ String stringRoleForMSAA() const override;
+ String nameForMSAA() const override;
+ String descriptionForMSAA() const override;
+ AccessibilityRole roleValueForMSAA() const override;
- virtual String passwordFieldValue() const override;
+ String passwordFieldValue() const override;
protected:
- RenderObject* m_renderer;
-
+ explicit AccessibilityRenderObject(RenderObject*);
void setRenderObject(RenderObject* renderer) { m_renderer = renderer; }
bool needsToUpdateChildren() const { return m_childrenDirty; }
- virtual ScrollableArea* getScrollableAreaIfScrollable() const override;
- virtual void scrollTo(const IntPoint&) const override;
+ ScrollableArea* getScrollableAreaIfScrollable() const override;
+ void scrollTo(const IntPoint&) const override;
- virtual bool isDetached() const override { return !m_renderer; }
+ bool isDetached() const override { return !m_renderer; }
- virtual AccessibilityRole determineAccessibilityRole() override;
- virtual bool computeAccessibilityIsIgnored() const override;
+ AccessibilityRole determineAccessibilityRole() override;
+ bool computeAccessibilityIsIgnored() const override;
+
+#if ENABLE(MATHML)
+ virtual bool isIgnoredElementWithinMathTree() const;
+#endif
+
+ RenderObject* m_renderer;
private:
+ bool isAccessibilityRenderObject() const 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() override { m_childrenDirty = true; }
- virtual Path elementPath() const override;
+ void setNeedsToUpdateChildren() override { m_childrenDirty = true; }
+ Path elementPath() const override;
bool isTabItemSelected() const;
LayoutRect checkboxOrRadioRect() const;
void addRadioButtonGroupMembers(AccessibilityChildrenVector& linkedUIElements) const;
+ void addRadioButtonGroupChildren(AccessibilityObject*, AccessibilityChildrenVector&) const;
AccessibilityObject* internalLinkElement() const;
AccessibilityObject* accessibilityImageMapHitTest(HTMLAreaElement*, const IntPoint&) const;
AccessibilityObject* accessibilityParentForImageMap(HTMLMapElement*) const;
- virtual AccessibilityObject* elementAccessibilityHitTest(const IntPoint&) const override;
+ AccessibilityObject* elementAccessibilityHitTest(const IntPoint&) const override;
- bool renderObjectIsObservable(RenderObject*) const;
+ bool renderObjectIsObservable(RenderObject&) const;
RenderObject* renderParentObject() const;
bool isDescendantOfElementType(const QualifiedName& tagName) const;
bool isSVGImage() const;
void detachRemoteSVGRoot();
- AccessibilitySVGRoot* remoteSVGRootElement() const;
+ enum CreationChoice { Create, Retrieve };
+ AccessibilitySVGRoot* remoteSVGRootElement(CreationChoice createIfNecessary) const;
AccessibilityObject* remoteSVGElementHitTest(const IntPoint&) const;
void offsetBoundingBoxForRemoteSVGElement(LayoutRect&) const;
- virtual bool supportsPath() const override;
+ bool supportsPath() const override;
void addHiddenChildren();
void addTextFieldChildren();
@@ -261,79 +257,30 @@ private:
void addCanvasChildren();
void addAttachmentChildren();
void addRemoteSVGChildren();
-#if PLATFORM(MAC)
+#if PLATFORM(COCOA)
void updateAttachmentViewParents();
#endif
-
+ String expandedTextValue() const override;
+ bool supportsExpandedTextValue() const override;
+ void updateRoleAfterChildrenCreation();
+
void ariaSelectedRows(AccessibilityChildrenVector&);
bool elementAttributeValue(const QualifiedName&) const;
void setElementAttributeValue(const QualifiedName&, bool);
- virtual ESpeak speakProperty() const override;
-
- virtual const AtomicString& ariaLiveRegionStatus() const override;
- virtual const AtomicString& ariaLiveRegionRelevant() const override;
- virtual bool ariaLiveRegionAtomic() const override;
- virtual bool ariaLiveRegionBusy() const override;
-
- bool inheritsPresentationalRole() const;
-
-#if ENABLE(MATHML)
- // All math elements return true for isMathElement().
- 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;
-
- // Generic components.
- virtual AccessibilityObject* mathBaseObject() override;
+ ESpeak speakProperty() const override;
- // Root components.
- virtual AccessibilityObject* mathRadicandObject() override;
- virtual AccessibilityObject* mathRootIndexObject() override;
-
- // Fraction components.
- virtual AccessibilityObject* mathNumeratorObject() override;
- virtual AccessibilityObject* mathDenominatorObject() override;
+ const String ariaLiveRegionStatus() const override;
+ const AtomicString& ariaLiveRegionRelevant() const override;
+ bool ariaLiveRegionAtomic() const override;
+ bool isBusy() const override;
- // Under over components.
- virtual AccessibilityObject* mathUnderObject() override;
- virtual AccessibilityObject* mathOverObject() override;
-
- // Subscript/superscript components.
- virtual AccessibilityObject* mathSubscriptObject() override;
- virtual AccessibilityObject* mathSuperscriptObject() override;
-
- // Fenced components.
- virtual String mathFencedOpenString() const override;
- virtual String mathFencedCloseString() const override;
- virtual int mathLineThickness() const override;
+ bool inheritsPresentationalRole() const override;
- // Multiscripts components.
- virtual void mathPrescripts(AccessibilityMathMultiscriptPairs&) override;
- virtual void mathPostscripts(AccessibilityMathMultiscriptPairs&) override;
-
- bool isIgnoredElementWithinMathTree() const;
-#endif
+ bool shouldGetTextFromNode(AccessibilityTextUnderElementMode) const;
};
-ACCESSIBILITY_OBJECT_TYPE_CASTS(AccessibilityRenderObject, isAccessibilityRenderObject())
-
} // namespace WebCore
-#endif // AccessibilityRenderObject_h
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilityRenderObject, isAccessibilityRenderObject())
diff --git a/Source/WebCore/accessibility/AccessibilitySVGElement.cpp b/Source/WebCore/accessibility/AccessibilitySVGElement.cpp
new file mode 100644
index 000000000..24c2a79d7
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilitySVGElement.cpp
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2016 Igalia, S.L.
+ * 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * OWNER 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 "AccessibilitySVGElement.h"
+
+#include "AXObjectCache.h"
+#include "ElementIterator.h"
+#include "HTMLNames.h"
+#include "Language.h"
+#include "RenderIterator.h"
+#include "RenderText.h"
+#include "SVGAElement.h"
+#include "SVGDescElement.h"
+#include "SVGGElement.h"
+#include "SVGTitleElement.h"
+#include "SVGUseElement.h"
+#include "XLinkNames.h"
+
+namespace WebCore {
+
+AccessibilitySVGElement::AccessibilitySVGElement(RenderObject* renderer)
+ : AccessibilityRenderObject(renderer)
+{
+}
+
+AccessibilitySVGElement::~AccessibilitySVGElement()
+{
+}
+
+Ref<AccessibilitySVGElement> AccessibilitySVGElement::create(RenderObject* renderer)
+{
+ return adoptRef(*new AccessibilitySVGElement(renderer));
+}
+
+AccessibilityObject* AccessibilitySVGElement::targetForUseElement() const
+{
+ if (!is<SVGUseElement>(element()))
+ return nullptr;
+
+ SVGUseElement& use = downcast<SVGUseElement>(*element());
+ String href = use.href();
+ if (href.isEmpty())
+ href = getAttribute(HTMLNames::hrefAttr);
+
+ Element* target = SVGURIReference::targetElementFromIRIString(href, use.document());
+ if (target)
+ return axObjectCache()->getOrCreate(target);
+
+ return nullptr;
+}
+
+template <typename ChildrenType>
+Element* AccessibilitySVGElement::childElementWithMatchingLanguage(ChildrenType& children) const
+{
+ String languageCode = language();
+ if (languageCode.isEmpty())
+ languageCode = defaultLanguage();
+
+ // The best match for a group of child SVG2 'title' or 'desc' elements may be the one
+ // which lacks a 'lang' attribute value. However, indexOfBestMatchingLanguageInList()
+ // currently bases its decision on non-empty strings. Furthermore, we cannot count on
+ // that child element having a given position. So we'll look for such an element while
+ // building the language list and save it as our fallback.
+
+ Element* fallback = nullptr;
+ Vector<String> childLanguageCodes;
+ Vector<Element*> elements;
+ for (auto& child : children) {
+ String lang = child.attributeWithoutSynchronization(SVGNames::langAttr);
+ childLanguageCodes.append(lang);
+ elements.append(&child);
+
+ // The current draft of the SVG2 spec states if there are multiple equally-valid
+ // matches, the first match should be used.
+ if (lang.isEmpty() && !fallback)
+ fallback = &child;
+ }
+
+ bool exactMatch;
+ size_t index = indexOfBestMatchingLanguageInList(languageCode, childLanguageCodes, exactMatch);
+ if (index < childLanguageCodes.size())
+ return elements[index];
+
+ return fallback;
+}
+
+void AccessibilitySVGElement::accessibilityText(Vector<AccessibilityText>& textOrder)
+{
+ String description = accessibilityDescription();
+ if (!description.isEmpty())
+ textOrder.append(AccessibilityText(description, AlternativeText));
+
+ String helptext = helpText();
+ if (!helptext.isEmpty())
+ textOrder.append(AccessibilityText(helptext, HelpText));
+}
+
+String AccessibilitySVGElement::accessibilityDescription() const
+{
+ // According to the SVG Accessibility API Mappings spec, the order of priority is:
+ // 1. aria-labelledby
+ // 2. aria-label
+ // 3. a direct child title element (selected according to language)
+ // 4. xlink:title attribute
+ // 5. for a use element, the accessible name calculated for the re-used content
+ // 6. for text container elements, the text content
+
+ String ariaDescription = ariaAccessibilityDescription();
+ if (!ariaDescription.isEmpty())
+ return ariaDescription;
+
+ auto titleElements = childrenOfType<SVGTitleElement>(*element());
+ if (auto titleChild = childElementWithMatchingLanguage(titleElements))
+ return titleChild->textContent();
+
+ if (is<SVGAElement>(element())) {
+ String xlinkTitle = element()->attributeWithoutSynchronization(XLinkNames::titleAttr);
+ if (!xlinkTitle.isEmpty())
+ return xlinkTitle;
+ }
+
+ if (m_renderer->isSVGText()) {
+ AccessibilityTextUnderElementMode mode;
+ String text = textUnderElement(mode);
+ if (!text.isEmpty())
+ return text;
+ }
+
+ if (is<SVGUseElement>(element())) {
+ if (AccessibilityObject* target = targetForUseElement())
+ return target->accessibilityDescription();
+ }
+
+ // FIXME: This is here to not break the svg-image.html test. But 'alt' is not
+ // listed as a supported attribute of the 'image' element in the SVG spec:
+ // https://www.w3.org/TR/SVG/struct.html#ImageElement
+ if (m_renderer->isSVGImage()) {
+ const AtomicString& alt = getAttribute(HTMLNames::altAttr);
+ if (!alt.isNull())
+ return alt;
+ }
+
+ return String();
+}
+
+String AccessibilitySVGElement::helpText() const
+{
+ // According to the SVG Accessibility API Mappings spec, the order of priority is:
+ // 1. aria-describedby
+ // 2. a direct child desc element
+ // 3. for a use element, the accessible description calculated for the re-used content
+ // 4. for text container elements, the text content, if not used for the name
+ // 5. a direct child title element that provides a tooltip, if not used for the name
+
+ String describedBy = ariaDescribedByAttribute();
+ if (!describedBy.isEmpty())
+ return describedBy;
+
+ auto descriptionElements = childrenOfType<SVGDescElement>(*element());
+ if (auto descriptionChild = childElementWithMatchingLanguage(descriptionElements))
+ return descriptionChild->textContent();
+
+ if (is<SVGUseElement>(element())) {
+ AccessibilityObject* target = targetForUseElement();
+ if (target)
+ return target->helpText();
+ }
+
+ String description = accessibilityDescription();
+
+ if (m_renderer->isSVGText()) {
+ AccessibilityTextUnderElementMode mode;
+ String text = textUnderElement(mode);
+ if (!text.isEmpty() && text != description)
+ return text;
+ }
+
+ auto titleElements = childrenOfType<SVGTitleElement>(*element());
+ if (auto titleChild = childElementWithMatchingLanguage(titleElements)) {
+ if (titleChild->textContent() != description)
+ return titleChild->textContent();
+ }
+
+ return String();
+}
+
+bool AccessibilitySVGElement::computeAccessibilityIsIgnored() const
+{
+ // According to the SVG Accessibility API Mappings spec, items should be excluded if:
+ // * They would be excluded according to the Core Accessibility API Mappings.
+ // * They are neither perceivable nor interactive.
+ // * Their first mappable role is presentational, unless they have a global ARIA
+ // attribute (covered by Core AAM) or at least one 'title' or 'desc' child element.
+ // * They have an ancestor with Children Presentational: True (covered by Core AAM)
+
+ AccessibilityObjectInclusion decision = defaultObjectInclusion();
+ if (decision == IgnoreObject)
+ return true;
+
+ if (m_renderer->isSVGHiddenContainer())
+ return true;
+
+ // The SVG AAM states objects with at least one 'title' or 'desc' element MUST be included.
+ // At this time, the presence of a matching 'lang' attribute is not mentioned in the spec.
+ for (const auto& child : childrenOfType<SVGElement>(*element())) {
+ if ((is<SVGTitleElement>(child) || is<SVGDescElement>(child)))
+ return false;
+ }
+
+ if (roleValue() == PresentationalRole || inheritsPresentationalRole())
+ return true;
+
+ if (ariaRoleAttribute() != UnknownRole)
+ return false;
+
+ // The SVG AAM states text elements should also be included, if they have content.
+ if (m_renderer->isSVGText() || m_renderer->isSVGTextPath()) {
+ for (auto& child : childrenOfType<RenderText>(downcast<RenderElement>(*m_renderer))) {
+ if (!child.isAllCollapsibleWhitespace())
+ return false;
+ }
+ }
+
+ // SVG shapes should not be included unless there's a concrete reason for inclusion.
+ // https://rawgit.com/w3c/aria/master/svg-aam/svg-aam.html#exclude_elements
+ if (m_renderer->isSVGShape())
+ return !(hasAttributesRequiredForInclusion() || canSetFocusAttribute() || element()->hasEventListeners());
+
+ return AccessibilityRenderObject::computeAccessibilityIsIgnored();
+}
+
+bool AccessibilitySVGElement::inheritsPresentationalRole() const
+{
+ if (canSetFocusAttribute())
+ return false;
+
+ AccessibilityRole role = roleValue();
+ if (role != SVGTextPathRole && role != SVGTSpanRole)
+ return false;
+
+ for (AccessibilityObject* parent = parentObject(); parent; parent = parent->parentObject()) {
+ if (is<AccessibilityRenderObject>(*parent) && parent->element()->hasTagName(SVGNames::textTag))
+ return parent->roleValue() == PresentationalRole;
+ }
+
+ return false;
+}
+
+AccessibilityRole AccessibilitySVGElement::determineAriaRoleAttribute() const
+{
+ AccessibilityRole role = AccessibilityRenderObject::determineAriaRoleAttribute();
+ if (role != PresentationalRole)
+ return role;
+
+ // The presence of a 'title' or 'desc' child element trumps PresentationalRole.
+ // https://lists.w3.org/Archives/Public/public-svg-a11y/2016Apr/0016.html
+ // At this time, the presence of a matching 'lang' attribute is not mentioned.
+ for (const auto& child : childrenOfType<SVGElement>(*element())) {
+ if ((is<SVGTitleElement>(child) || is<SVGDescElement>(child)))
+ return UnknownRole;
+ }
+
+ return role;
+}
+
+AccessibilityRole AccessibilitySVGElement::determineAccessibilityRole()
+{
+ if ((m_ariaRole = determineAriaRoleAttribute()) != UnknownRole)
+ return m_ariaRole;
+
+ Element* svgElement = element();
+
+ if (m_renderer->isSVGShape() || m_renderer->isSVGPath() || m_renderer->isSVGImage() || is<SVGUseElement>(svgElement))
+ return ImageRole;
+ if (m_renderer->isSVGForeignObject() || is<SVGGElement>(svgElement))
+ return GroupRole;
+ if (m_renderer->isSVGText())
+ return SVGTextRole;
+ if (m_renderer->isSVGTextPath())
+ return SVGTextPathRole;
+ if (m_renderer->isSVGTSpan())
+ return SVGTSpanRole;
+ if (is<SVGAElement>(svgElement))
+ return WebCoreLinkRole;
+
+ return AccessibilityRenderObject::determineAccessibilityRole();
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AccessibilitySVGElement.h b/Source/WebCore/accessibility/AccessibilitySVGElement.h
new file mode 100644
index 000000000..0d1d28ff1
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilitySVGElement.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 Igalia, S.L.
+ * 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * OWNER 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.
+ */
+
+#pragma once
+
+#include "AccessibilityRenderObject.h"
+
+namespace WebCore {
+
+class AccessibilitySVGElement : public AccessibilityRenderObject {
+
+public:
+ static Ref<AccessibilitySVGElement> create(RenderObject*);
+ virtual ~AccessibilitySVGElement();
+
+ String accessibilityDescription() const final;
+ String helpText() const final;
+
+protected:
+ explicit AccessibilitySVGElement(RenderObject*);
+
+private:
+ void accessibilityText(Vector<AccessibilityText>&) final;
+ AccessibilityRole determineAccessibilityRole() final;
+ AccessibilityRole determineAriaRoleAttribute() const final;
+ bool inheritsPresentationalRole() const final;
+ bool isAccessibilitySVGElement() const final { return true; }
+ bool computeAccessibilityIsIgnored() const final;
+
+ AccessibilityObject* targetForUseElement() const;
+
+ template <typename ChildrenType>
+ Element* childElementWithMatchingLanguage(ChildrenType&) const;
+};
+
+} // namespace WebCore
+
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilitySVGElement, isAccessibilitySVGElement())
diff --git a/Source/WebCore/accessibility/AccessibilitySVGRoot.cpp b/Source/WebCore/accessibility/AccessibilitySVGRoot.cpp
index 08a2d589d..3dab320cb 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.
*
@@ -34,8 +34,8 @@
namespace WebCore {
AccessibilitySVGRoot::AccessibilitySVGRoot(RenderObject* renderer)
- : AccessibilityRenderObject(renderer)
- , m_parent(0)
+ : AccessibilitySVGElement(renderer)
+ , 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
@@ -55,7 +55,7 @@ AccessibilityObject* AccessibilitySVGRoot::parentObject() const
if (m_parent)
return m_parent;
- return AccessibilityRenderObject::parentObject();
+ return AccessibilitySVGElement::parentObject();
}
diff --git a/Source/WebCore/accessibility/AccessibilitySVGRoot.h b/Source/WebCore/accessibility/AccessibilitySVGRoot.h
index f4d092420..7511c81c7 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.
*
@@ -26,32 +26,29 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AccessibilitySVGRoot_h
-#define AccessibilitySVGRoot_h
+#pragma once
-#include "AccessibilityRenderObject.h"
+#include "AccessibilitySVGElement.h"
namespace WebCore {
-class AccessibilitySVGRoot : public AccessibilityRenderObject {
-
-protected:
- explicit AccessibilitySVGRoot(RenderObject*);
+class AccessibilitySVGRoot final : public AccessibilitySVGElement {
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 override;
- virtual bool isAccessibilitySVGRoot() const override { return true; }
-};
+ AccessibilityObject* parentObject() const override;
+ bool isAccessibilitySVGRoot() const override { return true; }
-ACCESSIBILITY_OBJECT_TYPE_CASTS(AccessibilitySVGRoot, isAccessibilitySVGRoot())
+ AccessibilityObject* m_parent;
+ AccessibilityRole roleValue() const override { return GroupRole; }
+};
} // namespace WebCore
-#endif // AccessibilitySVGRoot_h
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilitySVGRoot, isAccessibilitySVGRoot())
diff --git a/Source/WebCore/accessibility/AccessibilityScrollView.cpp b/Source/WebCore/accessibility/AccessibilityScrollView.cpp
index f158506b3..ed5aa5844 100644
--- a/Source/WebCore/accessibility/AccessibilityScrollView.cpp
+++ b/Source/WebCore/accessibility/AccessibilityScrollView.cpp
@@ -51,12 +51,12 @@ AccessibilityScrollView::~AccessibilityScrollView()
void AccessibilityScrollView::detach(AccessibilityDetachmentType detachmentType, AXObjectCache* cache)
{
AccessibilityObject::detach(detachmentType, cache);
- m_scrollView = 0;
+ 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
@@ -106,13 +108,11 @@ void AccessibilityScrollView::setFocused(bool focused)
void AccessibilityScrollView::updateChildrenIfNecessary()
{
- if (m_childrenDirty)
- clearChildren();
-
- if (!m_haveChildren)
- addChildren();
-
- updateScrollbars();
+ // 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()
@@ -124,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;
}
}
@@ -147,19 +147,23 @@ void AccessibilityScrollView::removeChildScrollbar(AccessibilityObject* scrollba
AccessibilityScrollbar* AccessibilityScrollView::addChildScrollbar(Scrollbar* scrollbar)
{
if (!scrollbar)
- return 0;
+ return nullptr;
- AccessibilityScrollbar* scrollBarObject = toAccessibilityScrollbar(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
@@ -185,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->hasLivingRenderTree())
- 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();
@@ -214,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 56be1a07e..0d76f664a 100644
--- a/Source/WebCore/accessibility/AccessibilityScrollView.h
+++ b/Source/WebCore/accessibility/AccessibilityScrollView.h
@@ -23,8 +23,7 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AccessibilityScrollView_h
-#define AccessibilityScrollView_h
+#pragma once
#include "AccessibilityObject.h"
@@ -34,47 +33,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 override { return ScrollAreaRole; }
+ static Ref<AccessibilityScrollView> create(ScrollView*);
+ AccessibilityRole roleValue() const override { return ScrollAreaRole; }
ScrollView* scrollView() const { return m_scrollView; }
virtual ~AccessibilityScrollView();
- virtual void detach(AccessibilityDetachmentType, AXObjectCache*) override;
+ void detach(AccessibilityDetachmentType, AXObjectCache*) override;
+
+ AccessibilityObject* webAreaObject() const;
-protected:
- virtual ScrollableArea* getScrollableAreaIfScrollable() const override;
- virtual void scrollTo(const IntPoint&) const override;
-
private:
explicit AccessibilityScrollView(ScrollView*);
- virtual bool computeAccessibilityIsIgnored() const override;
- virtual bool isAccessibilityScrollView() const override { return true; }
- virtual bool isEnabled() const override { return true; }
+ ScrollableArea* getScrollableAreaIfScrollable() const override;
+ void scrollTo(const IntPoint&) const override;
+ bool computeAccessibilityIsIgnored() const override;
+ bool isAccessibilityScrollView() const override { return true; }
+ bool isEnabled() const override { return true; }
- virtual bool isAttachment() const override;
- virtual Widget* widgetForAttachmentView() const override;
+ bool isAttachment() const override;
+ Widget* widgetForAttachmentView() const override;
- 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; }
+ AccessibilityObject* scrollBar(AccessibilityOrientation) override;
+ void addChildren() override;
+ void clearChildren() override;
+ AccessibilityObject* accessibilityHitTest(const IntPoint&) const override;
+ void updateChildrenIfNecessary() override;
+ void setNeedsToUpdateChildren() override { m_childrenDirty = true; }
void updateScrollbars();
- virtual void setFocused(bool) override;
- virtual bool canSetFocusAttribute() const override;
- virtual bool isFocused() const override;
+ void setFocused(bool) override;
+ bool canSetFocusAttribute() const override;
+ bool isFocused() const override;
- virtual FrameView* documentFrameView() const override;
- virtual LayoutRect elementRect() const override;
- virtual AccessibilityObject* parentObject() const override;
- virtual AccessibilityObject* parentObjectIfExists() const override;
+ FrameView* documentFrameView() const override;
+ LayoutRect elementRect() const override;
+ AccessibilityObject* parentObject() const override;
+ AccessibilityObject* parentObjectIfExists() const override;
- AccessibilityObject* webAreaObject() const;
- virtual AccessibilityObject* firstChild() const override { return webAreaObject(); }
+ AccessibilityObject* firstChild() const override { return webAreaObject(); }
AccessibilityScrollbar* addChildScrollbar(Scrollbar*);
void removeChildScrollbar(AccessibilityObject*);
@@ -83,9 +81,7 @@ private:
RefPtr<AccessibilityObject> m_verticalScrollbar;
bool m_childrenDirty;
};
-
-ACCESSIBILITY_OBJECT_TYPE_CASTS(AccessibilityScrollView, isAccessibilityScrollView())
} // namespace WebCore
-#endif // AccessibilityScrollView_h
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilityScrollView, isAccessibilityScrollView())
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 c086a4162..3cc431a0d 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.
*
@@ -26,8 +26,7 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AccessibilityScrollbar_h
-#define AccessibilityScrollbar_h
+#pragma once
#include "AccessibilityMockObject.h"
@@ -35,35 +34,33 @@ 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 override { return true; }
- virtual bool canSetNumericValue() const override { return true; }
+ bool canSetValueAttribute() const override { return true; }
+ bool canSetNumericValue() const override { return true; }
- virtual bool isAccessibilityScrollbar() const override { return true; }
- virtual LayoutRect elementRect() const override;
+ bool isAccessibilityScrollbar() const override { return true; }
+ LayoutRect elementRect() const override;
- virtual AccessibilityRole roleValue() const override { return ScrollBarRole; }
- virtual AccessibilityOrientation orientation() const override;
- virtual Document* document() const override;
- virtual bool isEnabled() const override;
+ AccessibilityRole roleValue() const override { return ScrollBarRole; }
+ AccessibilityOrientation orientation() const override;
+ Document* document() const override;
+ bool isEnabled() const override;
// Assumes float [0..1]
- virtual void setValue(float) override;
- virtual float valueForRange() const override;
+ void setValue(float) override;
+ float valueForRange() const override;
RefPtr<Scrollbar> m_scrollbar;
};
-ACCESSIBILITY_OBJECT_TYPE_CASTS(AccessibilityScrollbar, isAccessibilityScrollbar())
-
} // namespace WebCore
-#endif // AccessibilityScrollbar_h
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilityScrollbar, isAccessibilityScrollbar())
diff --git a/Source/WebCore/accessibility/AccessibilitySlider.cpp b/Source/WebCore/accessibility/AccessibilitySlider.cpp
index 6450bfdec..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
@@ -84,15 +84,15 @@ void AccessibilitySlider::addChildren()
AXObjectCache* cache = m_renderer->document().axObjectCache();
- AccessibilitySliderThumb* thumb = toAccessibilitySliderThumb(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
@@ -133,15 +133,12 @@ void AccessibilitySlider::setValue(const String& value)
if (input->value() == value)
return;
- input->setValue(value);
-
- // Fire change event manually, as RenderSlider::setValueForPosition does.
- input->dispatchFormControlChangeEvent();
+ input->setValue(value, DispatchChangeEvent);
}
HTMLInputElement* AccessibilitySlider::inputElement() const
{
- return toHTMLInputElement(m_renderer->node());
+ return downcast<HTMLInputElement>(m_renderer->node());
}
@@ -149,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
@@ -162,7 +159,9 @@ LayoutRect AccessibilitySliderThumb::elementRect() const
RenderObject* sliderRenderer = m_parent->renderer();
if (!sliderRenderer || !sliderRenderer->isSlider())
return LayoutRect();
- return toHTMLInputElement(sliderRenderer->node())->sliderThumbElement()->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 d9529903d..4b20f7939 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.
*
@@ -26,8 +26,7 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AccessibilitySlider_h
-#define AccessibilitySlider_h
+#pragma once
#include "AccessibilityMockObject.h"
#include "AccessibilityRenderObject.h"
@@ -37,9 +36,8 @@ namespace WebCore {
class HTMLInputElement;
class AccessibilitySlider : public AccessibilityRenderObject {
-
public:
- static PassRefPtr<AccessibilitySlider> create(RenderObject*);
+ static Ref<AccessibilitySlider> create(RenderObject*);
virtual ~AccessibilitySlider() { }
protected:
@@ -47,45 +45,40 @@ protected:
private:
HTMLInputElement* inputElement() const;
- virtual AccessibilityObject* elementAccessibilityHitTest(const IntPoint&) const override;
+ AccessibilityObject* elementAccessibilityHitTest(const IntPoint&) const override;
- virtual AccessibilityRole roleValue() const override { return SliderRole; }
- virtual bool isSlider() const override { return true; }
- virtual bool isInputSlider() const override { return true; }
- virtual bool isControl() const override { return true; }
+ AccessibilityRole roleValue() const override { return SliderRole; }
+ bool isSlider() const final { return true; }
+ bool isInputSlider() const override { return true; }
+ bool isControl() const override { return true; }
- virtual void addChildren() override;
+ void addChildren() override;
- virtual bool canSetValueAttribute() const override { return true; }
+ bool canSetValueAttribute() const override { return true; }
const AtomicString& getAttribute(const QualifiedName& attribute) 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;
+ void setValue(const String&) override;
+ float valueForRange() const override;
+ float maxValueForRange() const override;
+ float minValueForRange() const override;
+ 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 bool isSliderThumb() const override final { return true; }
-
- virtual AccessibilityRole roleValue() const override { return SliderThumbRole; }
-
- virtual LayoutRect elementRect() const override;
+ AccessibilityRole roleValue() const override { return SliderThumbRole; }
+ LayoutRect elementRect() const override;
private:
AccessibilitySliderThumb();
- virtual bool computeAccessibilityIsIgnored() const override;
+ bool isSliderThumb() const override { return true; }
+ bool computeAccessibilityIsIgnored() const override;
};
-ACCESSIBILITY_OBJECT_TYPE_CASTS(AccessibilitySliderThumb, isSliderThumb())
-
} // namespace WebCore
-#endif // AccessibilitySlider_h
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilitySliderThumb, isSliderThumb())
diff --git a/Source/WebCore/accessibility/AccessibilitySpinButton.cpp b/Source/WebCore/accessibility/AccessibilitySpinButton.cpp
index 955079483..11be8a916 100644
--- a/Source/WebCore/accessibility/AccessibilitySpinButton.cpp
+++ b/Source/WebCore/accessibility/AccessibilitySpinButton.cpp
@@ -31,13 +31,13 @@
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)
{
}
@@ -49,6 +49,8 @@ AccessibilityObject* AccessibilitySpinButton::incrementButton()
{
if (!m_haveChildren)
addChildren();
+ if (!m_haveChildren)
+ return nullptr;
ASSERT(m_children.size() == 2);
@@ -59,6 +61,8 @@ AccessibilityObject* AccessibilitySpinButton::decrementButton()
{
if (!m_haveChildren)
addChildren();
+ if (!m_haveChildren)
+ return nullptr;
ASSERT(m_children.size() == 2);
@@ -80,17 +84,21 @@ LayoutRect AccessibilitySpinButton::elementRect() const
void AccessibilitySpinButton::addChildren()
{
+ AXObjectCache* cache = axObjectCache();
+ if (!cache)
+ return;
+
m_haveChildren = true;
- AccessibilitySpinButtonPart* incrementor = toAccessibilitySpinButtonPart(axObjectCache()->getOrCreate(SpinButtonPartRole));
- incrementor->setIsIncrementor(true);
- incrementor->setParent(this);
- m_children.append(incrementor);
+ auto& incrementor = downcast<AccessibilitySpinButtonPart>(*cache->getOrCreate(SpinButtonPartRole));
+ incrementor.setIsIncrementor(true);
+ incrementor.setParent(this);
+ m_children.append(&incrementor);
- AccessibilitySpinButtonPart* decrementor = toAccessibilitySpinButtonPart(axObjectCache()->getOrCreate(SpinButtonPartRole));
- decrementor->setIsIncrementor(false);
- decrementor->setParent(this);
- m_children.append(decrementor);
+ auto& decrementor = downcast<AccessibilitySpinButtonPart>(*cache->getOrCreate(SpinButtonPartRole));
+ decrementor.setIsIncrementor(false);
+ decrementor.setParent(this);
+ m_children.append(&decrementor);
}
void AccessibilitySpinButton::step(int amount)
@@ -109,9 +117,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 +138,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 c3956954f..bc043be85 100644
--- a/Source/WebCore/accessibility/AccessibilitySpinButton.h
+++ b/Source/WebCore/accessibility/AccessibilitySpinButton.h
@@ -23,18 +23,16 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AccessibilitySpinButton_h
-#define AccessibilitySpinButton_h
+#pragma once
#include "AccessibilityMockObject.h"
-
#include "SpinButtonElement.h"
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 +45,18 @@ public:
private:
AccessibilitySpinButton();
- 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;
+ AccessibilityRole roleValue() const override { return SpinButtonRole; }
+ bool isSpinButton() const override { return true; }
+ bool isNativeSpinButton() const override { return true; }
+ void addChildren() override;
+ 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,17 +64,16 @@ public:
private:
AccessibilitySpinButtonPart();
- bool m_isIncrementor : 1;
- virtual bool press() const override;
- virtual AccessibilityRole roleValue() const override { return ButtonRole; }
- virtual bool isSpinButtonPart() const override { return true; }
- virtual LayoutRect elementRect() const override;
-};
+ bool press() override;
+ AccessibilityRole roleValue() const override { return ButtonRole; }
+ bool isSpinButtonPart() const override { return true; }
+ LayoutRect elementRect() const override;
-ACCESSIBILITY_OBJECT_TYPE_CASTS(AccessibilitySpinButton, isNativeSpinButton())
-ACCESSIBILITY_OBJECT_TYPE_CASTS(AccessibilitySpinButtonPart, isSpinButtonPart())
+ unsigned m_isIncrementor : 1;
+};
-} // namespace WebCore
+} // namespace WebCore
-#endif // AccessibilitySpinButton_h
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilitySpinButton, isNativeSpinButton())
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilitySpinButtonPart, isSpinButtonPart())
diff --git a/Source/WebCore/accessibility/AccessibilityTable.cpp b/Source/WebCore/accessibility/AccessibilityTable.cpp
index d67783672..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.
*
@@ -44,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)
{
}
@@ -62,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
@@ -82,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)
@@ -105,34 +127,38 @@ bool AccessibilityTable::isDataTable() const
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);
- if (!table->element() || !isHTMLTableElement(table->element()))
- return false;
-
- // if there is a caption element, summary, THEAD, or TFOOT section, it's most certainly a data table
- HTMLTableElement* tableElement = toHTMLTableElement(table->element());
- 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<Element>(*tableElement)) {
- 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,7 +174,7 @@ bool AccessibilityTable::isDataTable() const
return true;
// Store the background color of the table to check against cell's background colors.
- const RenderStyle& tableStyle = table->style();
+ const RenderStyle& tableStyle = table.style();
Color tableBGColor = tableStyle.visitedDependentColor(CSSPropertyBackgroundColor);
// check enough of the cells to find if the table matches our criteria
@@ -183,22 +209,22 @@ bool AccessibilityTable::isDataTable() const
if (cell->width() < 1 || cell->height() < 1)
continue;
- validCellCount++;
+ ++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++;
+ ++headersInFirstColumnCount;
// In this case, the developer explicitly assigned a "data" table attribute.
- if (cellElement->hasTagName(tdTag) || cellElement->hasTagName(thTag)) {
- HTMLTableCellElement* tableCellElement = toHTMLTableCellElement(cellElement);
- if (!tableCellElement->headers().isEmpty() || !tableCellElement->abbr().isEmpty()
- || !tableCellElement->axis().isEmpty() || !tableCellElement->scope().isEmpty())
+ 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();
@@ -210,25 +236,25 @@ bool AccessibilityTable::isDataTable() const
// 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
+ 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)
@@ -236,13 +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;
const RenderStyle& rowRenderStyle = renderRow->style();
Color rowColor = rowRenderStyle.visitedDependentColor(CSSPropertyBackgroundColor);
alternatingRowColors[alternatingRowColorCount] = rowColor;
- alternatingRowColorCount++;
+ ++alternatingRowColorCount;
}
}
@@ -287,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
@@ -302,12 +328,6 @@ bool AccessibilityTable::isTableExposableThroughAccessibility() const
if (hasARIARole())
return false;
- // Gtk+ ATs expect all tables to be exposed as tables.
-#if PLATFORM(GTK) || PLATFORM(EFL)
- Element* tableNode = toRenderTable(m_renderer)->element();
- return tableNode && isHTMLTableElement(tableNode);
-#endif
-
return isDataTable();
}
@@ -319,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;
}
@@ -333,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 = toAccessibilityTableRow(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) || PLATFORM(EFL)
- 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 = std::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 = toAccessibilityTableColumn(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()
@@ -402,10 +474,10 @@ 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();
}
@@ -430,8 +502,11 @@ void AccessibilityTable::columnHeaders(AccessibilityChildrenVector& headers)
updateChildrenIfNecessary();
- for (const auto& column : m_columns) {
- if (AccessibilityObject* header = toAccessibilityTableColumn(column.get())->headerObject())
+ // 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);
}
}
@@ -443,8 +518,11 @@ void AccessibilityTable::rowHeaders(AccessibilityChildrenVector& headers)
updateChildrenIfNecessary();
- for (const auto& row : m_rows) {
- if (AccessibilityObject* header = toAccessibilityTableRow(row.get())->headerObject())
+ // 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);
}
}
@@ -491,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;
}
@@ -502,7 +580,7 @@ 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) {
@@ -513,29 +591,33 @@ AccessibilityTableCell* AccessibilityTable::cellForColumnAndRow(unsigned column,
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;
std::pair<unsigned, unsigned> columnRange;
std::pair<unsigned, unsigned> rowRange;
- AccessibilityTableCell* tableCellChild = toAccessibilityTableCell(child);
- tableCellChild->columnIndexRange(columnRange);
- tableCellChild->rowIndexRange(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;
}
@@ -548,7 +630,7 @@ bool AccessibilityTable::computeAccessibilityIsIgnored() const
if (decision == IgnoreObject)
return true;
- if (!isAccessibilityTable())
+ if (!isExposableThroughAccessibility())
return AccessibilityRenderObject::computeAccessibilityIsIgnored();
return false;
@@ -563,7 +645,7 @@ void AccessibilityTable::titleElementText(Vector<AccessibilityText>& textOrder)
String AccessibilityTable::title() const
{
- if (!isAccessibilityTable())
+ if (!isExposableThroughAccessibility())
return AccessibilityRenderObject::title();
String title;
@@ -572,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();
}
@@ -585,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 3b447d41a..8511b8ecd 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.
*
@@ -26,8 +26,7 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AccessibilityTable_h
-#define AccessibilityTable_h
+#pragma once
#include "AccessibilityRenderObject.h"
#include <wtf/Forward.h>
@@ -35,23 +34,21 @@
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() override;
+ void init() final;
- virtual AccessibilityRole roleValue() const override;
+ AccessibilityRole roleValue() const final;
virtual bool isAriaTable() const { return false; }
- virtual void addChildren() override;
- virtual void clearChildren() override;
+ void addChildren() override;
+ void clearChildren() final;
const AccessibilityChildrenVector& columns();
const AccessibilityChildrenVector& rows();
@@ -59,9 +56,9 @@ public:
virtual bool supportsSelectedRows() { return false; }
unsigned columnCount();
unsigned rowCount();
- virtual int tableLevel() const override;
+ int tableLevel() const final;
- virtual String title() const override;
+ String title() const final;
// all the cells in the table
void cells(AccessibilityChildrenVector&);
@@ -74,32 +71,38 @@ public:
// 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;
+ bool isTable() const 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 isTableExposableThroughAccessibility() const;
- virtual bool computeAccessibilityIsIgnored() const override;
+ bool isDataTable() const final;
+ bool computeAccessibilityIsIgnored() const final;
private:
- virtual void titleElementText(Vector<AccessibilityText>&) const override;
+ virtual bool computeIsTableExposableThroughAccessibility() const;
+ void titleElementText(Vector<AccessibilityText>&) const final;
+ HTMLTableElement* tableElement() const;
+ void addChildrenFromSection(RenderTableSection*, unsigned& maxColumnCount);
+ void addTableCellChild(AccessibilityObject*, HashSet<AccessibilityObject*>& appendedRows, unsigned& columnCount);
};
-ACCESSIBILITY_OBJECT_TYPE_CASTS(AccessibilityTable, isTable())
-
} // namespace WebCore
-#endif // AccessibilityTable_h
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilityTable, isTable())
diff --git a/Source/WebCore/accessibility/AccessibilityTableCell.cpp b/Source/WebCore/accessibility/AccessibilityTableCell.cpp
index 876a9c2ca..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.
*
@@ -32,6 +32,8 @@
#include "AXObjectCache.h"
#include "AccessibilityTable.h"
#include "AccessibilityTableRow.h"
+#include "ElementIterator.h"
+#include "HTMLElement.h"
#include "HTMLNames.h"
#include "RenderObject.h"
#include "RenderTableCell.h"
@@ -42,6 +44,7 @@ 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,6 +65,12 @@ 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();
@@ -70,39 +79,67 @@ bool AccessibilityTableCell::computeAccessibilityIsIgnored() 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 toAccessibilityTable(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;
}
@@ -111,6 +148,62 @@ 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();
@@ -142,12 +235,28 @@ bool AccessibilityTableCell::isTableCellInSameColGroup(AccessibilityTableCell* t
return false;
}
+String AccessibilityTableCell::expandedTextValue() const
+{
+ 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);
@@ -156,16 +265,16 @@ void AccessibilityTableCell::columnHeaders(AccessibilityChildrenVector& headers)
for (unsigned row = 0; row < rowRange.first; row++) {
AccessibilityTableCell* tableCell = parent->cellForColumnAndRow(colRange.first, row);
- if (tableCell == this || headers.contains(tableCell))
+ if (!tableCell || tableCell == this || headers.contains(tableCell))
continue;
std::pair<unsigned, unsigned> childRowRange;
tableCell->rowIndexRange(childRowRange);
const AtomicString& scope = tableCell->getAttribute(scopeAttr);
- if (scope == "col" || tableCell->isTableHeaderCell())
+ if (scope == "colgroup" && isTableCellInSameColGroup(tableCell))
headers.append(tableCell);
- else if (scope == "colgroup" && isTableCellInSameColGroup(tableCell))
+ else if (tableCell->isColumnHeaderCell())
headers.append(tableCell);
}
}
@@ -184,52 +293,45 @@ void AccessibilityTableCell::rowHeaders(AccessibilityChildrenVector& headers)
for (unsigned column = 0; column < colRange.first; column++) {
AccessibilityTableCell* tableCell = parent->cellForColumnAndRow(column, rowRange.first);
- if (tableCell == this || headers.contains(tableCell))
+ if (!tableCell || tableCell == this || headers.contains(tableCell))
continue;
const AtomicString& scope = tableCell->getAttribute(scopeAttr);
- if (scope == "row")
+ if (scope == "rowgroup" && isTableCellInSameRowGroup(tableCell))
headers.append(tableCell);
- else if (scope == "rowgroup" && isTableCellInSameRowGroup(tableCell))
+ else if (tableCell->isRowHeaderCell())
headers.append(tableCell);
}
}
-
-void AccessibilityTableCell::rowIndexRange(std::pair<unsigned, unsigned>& rowRange)
+
+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 (!m_renderer || !m_renderer->isTableCell())
+ if (!is<RenderTableCell>(m_renderer))
return;
- RenderTableCell* renderCell = toRenderTableCell(m_renderer);
- rowRange.first = renderCell->rowIndex();
- rowRange.second = renderCell->rowSpan();
+ RenderTableCell& renderCell = downcast<RenderTableCell>(*m_renderer);
+ rowRange.second = renderCell.rowSpan();
- // 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)
- return;
-
- RenderTableSection* tableSection = table->topSection();
- unsigned rowOffset = 0;
- while (tableSection) {
- if (tableSection == section)
- break;
- rowOffset += tableSection->numRows();
- tableSection = table->sectionBelow(tableSection, SkipEmptySections);
- }
-
- rowRange.first += rowOffset;
+ if (AccessibilityTableRow* parentRow = this->parentRow())
+ rowRange.first = parentRow->rowIndex();
}
-void AccessibilityTableCell::columnIndexRange(std::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
@@ -237,36 +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;
if (!headerCell->element() || !headerCell->element()->hasTagName(thTag))
- return 0;
+ 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 b45d238be..78be9e254 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.
*
@@ -26,51 +26,61 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AccessibilityTableCell_h
-#define AccessibilityTableCell_h
+#pragma once
#include "AccessibilityRenderObject.h"
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 override;
+ bool isTableCell() const final;
bool isTableHeaderCell() const;
+ bool isColumnHeaderCell() const;
+ bool isRowHeaderCell() const;
// fills in the start location and row span of cell
- virtual void rowIndexRange(std::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(std::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:
+ explicit AccessibilityTableCell(RenderObject*);
+
+ AccessibilityTableRow* parentRow() const;
virtual AccessibilityTable* parentTable() const;
+ AccessibilityRole determineAccessibilityRole() final;
+
int m_rowIndex;
- virtual AccessibilityRole determineAccessibilityRole() override;
+ 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 override;
- virtual bool exposesTitleUIElement() const override { return true; }
- virtual bool computeAccessibilityIsIgnored() const override;
-
+ AccessibilityObject* titleUIElement() const final;
+ bool exposesTitleUIElement() const final { return true; }
+ bool computeAccessibilityIsIgnored() const final;
+ String expandedTextValue() const final;
+ bool supportsExpandedTextValue() const final;
+
bool isTableCellInSameRowGroup(AccessibilityTableCell*);
bool isTableCellInSameColGroup(AccessibilityTableCell*);
};
-ACCESSIBILITY_OBJECT_TYPE_CASTS(AccessibilityTableCell, isTableCell())
-
} // namespace WebCore
-#endif // AccessibilityTableCell_h
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilityTableCell, isTableCell())
diff --git a/Source/WebCore/accessibility/AccessibilityTableColumn.cpp b/Source/WebCore/accessibility/AccessibilityTableColumn.cpp
index 6791c3320..1669a6fc0 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,6 +31,8 @@
#include "AXObjectCache.h"
#include "AccessibilityTableCell.h"
+#include "HTMLCollection.h"
+#include "HTMLElement.h"
#include "HTMLNames.h"
#include "RenderTable.h"
#include "RenderTableCell.h"
@@ -48,9 +50,9 @@ AccessibilityTableColumn::~AccessibilityTableColumn()
{
}
-PassRefPtr<AccessibilityTableColumn> AccessibilityTableColumn::create()
+Ref<AccessibilityTableColumn> AccessibilityTableColumn::create()
{
- return adoptRef(new AccessibilityTableColumn());
+ return adoptRef(*new AccessibilityTableColumn());
}
void AccessibilityTableColumn::setParent(AccessibilityObject* parent)
@@ -62,88 +64,115 @@ 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()) {
+ if (parentTable.isAriaTable()) {
for (const auto& cell : children()) {
if (cell->ariaRoleAttribute() == ColumnHeaderRole)
return cell.get();
}
- return 0;
+ return nullptr;
}
- if (!renderer->isTable())
- return 0;
+ if (!is<RenderTable>(*renderer))
+ return nullptr;
- RenderTable* table = toRenderTable(renderer);
-
- AccessibilityObject* headerObject = 0;
-
- // 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;
+
+ Node* testCellNode = testCell->element();
+ // If the RenderTableCell doesn't have an element because its anonymous,
+ // try to see if we can find the original cell element to check if it has a <th> tag.
+ if (!testCellNode && testCell->isAnonymous()) {
+ if (Element* parentElement = testCell->parent() ? testCell->parent()->element() : nullptr) {
+ if (parentElement->hasTagName(trTag) && testCol < static_cast<int>(parentElement->countChildNodes()))
+ testCellNode = parentElement->traverseToChildAt(testCol);
+ }
+ }
+
+ if (!testCellNode)
+ continue;
+
+ // If th is required, but we found an element that doesn't have a th tag, we can stop looking.
+ if (thTagRequired && !testCellNode->hasTagName(thTag))
+ break;
+
+ cell = testCell;
break;
-
- if (!testCell->element())
- continue;
-
- if (thTagRequired && !testCell->element()->hasTagName(thTag))
- continue;
-
- cell = testCell;
+ }
}
if (!cell)
- return 0;
+ return nullptr;
- return axObjectCache()->getOrCreate(cell);
+ auto* cellObject = axObjectCache()->getOrCreate(cell);
+ if (!cellObject || cellObject->accessibilityIsIgnored())
+ return nullptr;
+
+ return cellObject;
}
bool AccessibilityTableColumn::computeAccessibilityIsIgnored() const
@@ -151,7 +180,7 @@ bool AccessibilityTableColumn::computeAccessibilityIsIgnored() const
if (!m_parent)
return true;
-#if PLATFORM(IOS) || PLATFORM(GTK) || PLATFORM(EFL)
+#if PLATFORM(IOS) || PLATFORM(GTK)
return true;
#endif
@@ -163,14 +192,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;
@@ -179,7 +211,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 65ed020a2..6b27f907c 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.
*
@@ -26,8 +26,7 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AccessibilityTableColumn_h
-#define AccessibilityTableColumn_h
+#pragma once
#include "AccessibilityMockObject.h"
#include "AccessibilityTable.h"
@@ -37,37 +36,33 @@ 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 override { return ColumnRole; }
- virtual bool isTableColumn() const override { return true; }
+ AccessibilityRole roleValue() const override { return ColumnRole; }
void setColumnIndex(int columnIndex) { m_columnIndex = columnIndex; }
int columnIndex() const { return m_columnIndex; }
- virtual void addChildren() override;
- virtual void setParent(AccessibilityObject*) override;
+ void addChildren() override;
+ void setParent(AccessibilityObject*) override;
- virtual LayoutRect elementRect() const override;
+ LayoutRect elementRect() const override;
-private:
- unsigned m_columnIndex;
- LayoutRect m_columnRect;
+private:
+ AccessibilityTableColumn();
AccessibilityObject* headerObjectForSection(RenderTableSection*, bool thTagRequired);
- virtual bool computeAccessibilityIsIgnored() const override;
-};
+ bool computeAccessibilityIsIgnored() const override;
+ bool isTableColumn() const override { return true; }
-ACCESSIBILITY_OBJECT_TYPE_CASTS(AccessibilityTableColumn, isTableColumn())
+ unsigned m_columnIndex;
+};
} // namespace WebCore
-#endif // AccessibilityTableColumn_h
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilityTableColumn, isTableColumn())
diff --git a/Source/WebCore/accessibility/AccessibilityTableHeaderContainer.cpp b/Source/WebCore/accessibility/AccessibilityTableHeaderContainer.cpp
index 52a080020..3907e12a4 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.
*
@@ -42,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
@@ -58,7 +58,7 @@ bool AccessibilityTableHeaderContainer::computeAccessibilityIsIgnored() const
if (!m_parent)
return true;
-#if PLATFORM(IOS) || PLATFORM(GTK) || PLATFORM(EFL)
+#if PLATFORM(IOS) || PLATFORM(GTK)
return true;
#endif
@@ -70,10 +70,14 @@ 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;
- toAccessibilityTable(m_parent)->columnHeaders(m_children);
+ parentTable.columnHeaders(m_children);
for (const auto& child : m_children)
m_headerRect.unite(child->elementRect());
diff --git a/Source/WebCore/accessibility/AccessibilityTableHeaderContainer.h b/Source/WebCore/accessibility/AccessibilityTableHeaderContainer.h
index 99890d3cd..16da83919 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.
*
@@ -26,8 +26,7 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AccessibilityTableHeaderContainer_h
-#define AccessibilityTableHeaderContainer_h
+#pragma once
#include "AccessibilityMockObject.h"
#include "AccessibilityTable.h"
@@ -35,26 +34,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 override { return TableHeaderContainerRole; }
+ AccessibilityRole roleValue() const override { return TableHeaderContainerRole; }
- virtual void addChildren() override;
+ void addChildren() override;
- virtual LayoutRect elementRect() const override;
+ LayoutRect elementRect() const override;
private:
- LayoutRect m_headerRect;
+ AccessibilityTableHeaderContainer();
- virtual bool computeAccessibilityIsIgnored() const override;
+ bool computeAccessibilityIsIgnored() const override;
+
+ LayoutRect m_headerRect;
};
} // namespace WebCore
-
-#endif // AccessibilityTableHeaderContainer_h
diff --git a/Source/WebCore/accessibility/AccessibilityTableRow.cpp b/Source/WebCore/accessibility/AccessibilityTableRow.cpp
index ab4d0d03b..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.
*
@@ -51,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()
@@ -61,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;
}
@@ -73,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
@@ -104,38 +98,81 @@ 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() ? toAccessibilityTable(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;
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 = toAccessibilityTableCell(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 3eddfea1f..ed6de91cd 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.
*
@@ -26,8 +26,7 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AccessibilityTableRow_h
-#define AccessibilityTableRow_h
+#pragma once
#include "AccessibilityRenderObject.h"
@@ -36,14 +35,9 @@ 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 override;
// retrieves the "row" header (a th tag in the rightmost column)
virtual AccessibilityObject* headerObject();
@@ -56,18 +50,24 @@ public:
// in the row, but their col/row spans overlap into it
void appendChild(AccessibilityObject*);
+ void addChildren() override;
+
+ int ariaColumnIndex() const;
+ int ariaRowIndex() const;
+
protected:
- virtual AccessibilityRole determineAccessibilityRole() override;
+ explicit AccessibilityTableRow(RenderObject*);
+
+ AccessibilityRole determineAccessibilityRole() final;
private:
- int m_rowIndex;
-
- virtual AccessibilityObject* observableObject() const override;
- virtual bool computeAccessibilityIsIgnored() const override;
-};
+ bool isTableRow() const final;
+ AccessibilityObject* observableObject() const final;
+ bool computeAccessibilityIsIgnored() const final;
-ACCESSIBILITY_OBJECT_TYPE_CASTS(AccessibilityTableRow, isTableRow())
+ int m_rowIndex;
+};
-} // namespace WebCore
+} // namespace WebCore
-#endif // AccessibilityTableRow_h
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilityTableRow, isTableRow())
diff --git a/Source/WebCore/accessibility/AccessibilityTree.cpp b/Source/WebCore/accessibility/AccessibilityTree.cpp
new file mode 100644
index 000000000..07f313658
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityTree.cpp
@@ -0,0 +1,113 @@
+/*
+ * 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::nodeHasTreeItemChild(Node& node) const
+{
+ for (auto* child = node.firstChild(); child; child = child->nextSibling()) {
+ if (nodeHasRole(child, "treeitem"))
+ return true;
+ }
+ return false;
+}
+
+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, "presentation")) {
+ if (!nodeHasTreeItemChild(*child))
+ return false;
+ 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/AccessibilitySearchFieldButtons.h b/Source/WebCore/accessibility/AccessibilityTree.h
index a00e70391..35c7f14f7 100644
--- a/Source/WebCore/accessibility/AccessibilitySearchFieldButtons.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,6 +10,9 @@
* 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
@@ -23,29 +26,26 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AccessibilitySearchFieldButtons_h
-#define AccessibilitySearchFieldButtons_h
+
+#pragma once
#include "AccessibilityRenderObject.h"
namespace WebCore {
-
-class AccessibilitySearchFieldCancelButton final : public AccessibilityRenderObject {
-public:
- static PassRefPtr<AccessibilitySearchFieldCancelButton> create(RenderObject*);
- virtual String accessibilityDescription() const override;
- virtual void accessibilityText(Vector<AccessibilityText>&) override;
- virtual bool press() const override;
- virtual AccessibilityRole roleValue() const override { return ButtonRole; }
-
-protected:
- virtual bool computeAccessibilityIsIgnored() const override;
+class AccessibilityTree final : public AccessibilityRenderObject {
+public:
+ static Ref<AccessibilityTree> create(RenderObject*);
+ virtual ~AccessibilityTree();
private:
- explicit AccessibilitySearchFieldCancelButton(RenderObject*);
+ explicit AccessibilityTree(RenderObject*);
+ bool computeAccessibilityIsIgnored() const override;
+ AccessibilityRole determineAccessibilityRole() override;
+ bool isTreeValid() const;
+ bool nodeHasTreeItemChild(Node&) const;
};
-
+
} // namespace WebCore
-#endif // AccessibilitySearchFieldButtons_h
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilityTree, isTree())
diff --git a/Source/WebCore/accessibility/AccessibilitySearchFieldButtons.cpp b/Source/WebCore/accessibility/AccessibilityTreeItem.cpp
index c7c949c2d..cd36c38bd 100644
--- a/Source/WebCore/accessibility/AccessibilitySearchFieldButtons.cpp
+++ b/Source/WebCore/accessibility/AccessibilityTreeItem.cpp
@@ -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,6 +10,9 @@
* 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
@@ -24,53 +27,39 @@
*/
#include "config.h"
-#include "AccessibilitySearchFieldButtons.h"
+#include "AccessibilityTreeItem.h"
-#include "LocalizedStrings.h"
-#include "RenderObject.h"
+#include "AXObjectCache.h"
+#include "HTMLNames.h"
namespace WebCore {
-
-PassRefPtr<AccessibilitySearchFieldCancelButton> AccessibilitySearchFieldCancelButton::create(RenderObject* renderer)
-{
- return adoptRef(new AccessibilitySearchFieldCancelButton(renderer));
-}
-
-AccessibilitySearchFieldCancelButton::AccessibilitySearchFieldCancelButton(RenderObject* renderer)
+
+using namespace HTMLNames;
+
+AccessibilityTreeItem::AccessibilityTreeItem(RenderObject* renderer)
: AccessibilityRenderObject(renderer)
{
}
-
-String AccessibilitySearchFieldCancelButton::accessibilityDescription() const
+
+AccessibilityTreeItem::~AccessibilityTreeItem()
{
-#if PLATFORM(IOS)
- return String();
-#else
- return AXSearchFieldCancelButtonText();
-#endif
}
-
-void AccessibilitySearchFieldCancelButton::accessibilityText(Vector<AccessibilityText>& textOrder)
+
+Ref<AccessibilityTreeItem> AccessibilityTreeItem::create(RenderObject* renderer)
{
- textOrder.append(AccessibilityText(accessibilityDescription(), AlternativeText));
+ return adoptRef(*new AccessibilityTreeItem(renderer));
}
-bool AccessibilitySearchFieldCancelButton::press() const
+AccessibilityRole AccessibilityTreeItem::determineAccessibilityRole()
{
- Node* node = this->node();
- if (!node || !node->isElementNode())
- return false;
- Element* element = toElement(node);
- // The default event handler on SearchFieldCancelButtonElement requires hover.
- element->setHovered(true);
- element->accessKeyAction(true);
- return true;
-}
+ // 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;
-bool AccessibilitySearchFieldCancelButton::computeAccessibilityIsIgnored() const
-{
- return accessibilityIsIgnoredByDefault();
+ 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..94038589a
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityTreeItem.h
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "AccessibilityRenderObject.h"
+
+namespace WebCore {
+
+class AccessibilityTreeItem final : public AccessibilityRenderObject {
+public:
+ static Ref<AccessibilityTreeItem> create(RenderObject*);
+ virtual ~AccessibilityTreeItem();
+
+ bool shouldIgnoreAttributeRole() const override { return !m_isTreeItemValid; }
+
+private:
+ explicit AccessibilityTreeItem(RenderObject*);
+ AccessibilityRole determineAccessibilityRole() override;
+ bool m_isTreeItemValid;
+};
+
+} // namespace WebCore
+
+SPECIALIZE_TYPE_TRAITS_ACCESSIBILITY(AccessibilityTreeItem, isTreeItem())
diff --git a/Source/WebCore/accessibility/atk/AXObjectCacheAtk.cpp b/Source/WebCore/accessibility/atk/AXObjectCacheAtk.cpp
index 753f761e4..85e0c64d5 100644
--- a/Source/WebCore/accessibility/atk/AXObjectCacheAtk.cpp
+++ b/Source/WebCore/accessibility/atk/AXObjectCacheAtk.cpp
@@ -30,7 +30,8 @@
#include "Range.h"
#include "TextIterator.h"
#include "WebKitAccessibleWrapperAtk.h"
-#include <wtf/gobject/GRefPtr.h>
+#include <wtf/NeverDestroyed.h>
+#include <wtf/glib/GRefPtr.h>
#include <wtf/text/CString.h>
namespace WebCore {
@@ -75,7 +76,19 @@ void AXObjectCache::attachWrapper(AccessibilityObject* obj)
if (!document || document->childNeedsStyleRecalc())
return;
- // Don't emit the signal for objects that we already know won't be exposed directly.
+ // 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;
@@ -119,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()))
@@ -128,21 +141,19 @@ static void notifyChildrenSelectionChange(AccessibilityObject* object)
// Only support HTML select elements so far (ARIA selectors not supported).
Node* node = object->node();
- if (!node || !isHTMLSelectElement(node))
+ 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;
}
@@ -154,11 +165,11 @@ 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) {
@@ -179,8 +190,8 @@ static void notifyChildrenSelectionChange(AccessibilityObject* object)
}
// Update pointers to the previously involved objects.
- oldListObject = listObject;
- oldFocusedObject = item;
+ oldListObject.get() = listObject;
+ oldFocusedObject.get() = item;
}
void AXObjectCache::postPlatformNotification(AccessibilityObject* coreObject, AXNotification notification)
@@ -191,7 +202,7 @@ void AXObjectCache::postPlatformNotification(AccessibilityObject* coreObject, AX
switch (notification) {
case AXCheckedStateChanged:
- if (!coreObject->isCheckboxOrRadio())
+ if (!coreObject->isCheckboxOrRadio() && !coreObject->isSwitch())
return;
atk_object_notify_state_change(axObject, ATK_STATE_CHECKED, coreObject->isChecked());
break;
@@ -212,7 +223,13 @@ void AXObjectCache::postPlatformNotification(AccessibilityObject* coreObject, AX
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
g_signal_emit_by_name(ATK_OBJECT(axObject), "property-change::accessible-value", &propertyValues, NULL);
}
@@ -232,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;
@@ -251,12 +268,15 @@ void AXObjectCache::nodeTextChangePlatformNotification(AccessibilityObject* obje
// 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;
diff --git a/Source/WebCore/accessibility/atk/AccessibilityObjectAtk.cpp b/Source/WebCore/accessibility/atk/AccessibilityObjectAtk.cpp
index 51e1b787a..238248430 100644
--- a/Source/WebCore/accessibility/atk/AccessibilityObjectAtk.cpp
+++ b/Source/WebCore/accessibility/atk/AccessibilityObjectAtk.cpp
@@ -21,9 +21,13 @@
#include "config.h"
#include "AccessibilityObject.h"
-#include "HTMLNames.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,10 +45,11 @@ AccessibilityObjectInclusion AccessibilityObject::accessibilityPlatformIncludesO
if (!parent)
return DefaultBehavior;
- AccessibilityRole role = roleValue();
- if (role == HorizontalRuleRole)
- return IncludeObject;
+ // If the author has provided a role, platform-specific inclusion likely doesn't apply.
+ if (ariaRoleAttribute() != UnknownRole)
+ return DefaultBehavior;
+ AccessibilityRole role = roleValue();
// We expose the slider as a whole but not its value indicator.
if (role == SliderThumbRole)
return IgnoreObject;
@@ -59,12 +64,13 @@ 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.
+ // However, WebCore also maps ARIA's "text" role to the StaticTextRole.
if (role == StaticTextRole)
- return IgnoreObject;
+ return ariaRoleAttribute() != UnknownRole ? DefaultBehavior : IgnoreObject;
// Include all list items, regardless they have or not inline children
if (role == ListItemRole)
@@ -79,52 +85,72 @@ AccessibilityObjectInclusion AccessibilityObject::accessibilityPlatformIncludesO
if (role == UnknownRole)
return IgnoreObject;
+ 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;
+ }
+
+ 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.
- Node* node = renderObject->node();
- if (node && node->hasTagName(HTMLNames::spanTag) && !canSetFocusAttribute() && !hasAttributesRequiredForInclusion())
+ if (is<HTMLSpanElement>(node) && !canSetFocusAttribute() && !hasAttributesRequiredForInclusion() && !supportsARIAAttributes())
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 (!renderObject->firstChildSlow())
- return DefaultBehavior;
-
- if (!parent->renderer() || parent->renderer()->isAnonymousBlock())
- return DefaultBehavior;
-
- for (RenderObject* r = renderObject->firstChildSlow(); r; r = r->nextSibling()) {
- if (r->isAnonymousBlock())
- return IncludeObject;
- }
- }
-
- // 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 (renderObject && renderObject->isAnonymousBlock() && !parent->renderer()->isBody()
- && parent->ariaRoleAttribute() == UnknownRole)
+ // 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;
@@ -157,7 +183,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
@@ -169,9 +195,9 @@ 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())
- textLength = toRenderText(*renderer).textLength();
+ 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.
diff --git a/Source/WebCore/accessibility/atk/WebKitAccessibleHyperlink.cpp b/Source/WebCore/accessibility/atk/WebKitAccessibleHyperlink.cpp
index 0e4a0c8ed..9df03efa9 100644
--- a/Source/WebCore/accessibility/atk/WebKitAccessibleHyperlink.cpp
+++ b/Source/WebCore/accessibility/atk/WebKitAccessibleHyperlink.cpp
@@ -231,10 +231,10 @@ 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);
+ auto& marker = downcast<RenderListMarker>(*renderer);
return baseLength + marker.text().length() + marker.suffix().length();
}
@@ -385,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/WebKitAccessibleHyperlink.h b/Source/WebCore/accessibility/atk/WebKitAccessibleHyperlink.h
index 59ca73cea..0533156e7 100644
--- a/Source/WebCore/accessibility/atk/WebKitAccessibleHyperlink.h
+++ b/Source/WebCore/accessibility/atk/WebKitAccessibleHyperlink.h
@@ -17,8 +17,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef WebKitAccessibleHyperlink_h
-#define WebKitAccessibleHyperlink_h
+#pragma once
#if HAVE(ACCESSIBILITY)
@@ -60,6 +59,4 @@ WebCore::AccessibilityObject* webkitAccessibleHyperlinkGetAccessibilityObject(We
G_END_DECLS
-#endif // WebKitAccessibleHyperlink_h
-
-#endif
+#endif // HAVE(ACCESSIBILITY)
diff --git a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceAction.h b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceAction.h
index a0de63fd7..5cd75287b 100644
--- a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceAction.h
+++ b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceAction.h
@@ -19,8 +19,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef WebKitAccessibleInterfaceAction_h
-#define WebKitAccessibleInterfaceAction_h
+#pragma once
#if HAVE(ACCESSIBILITY)
@@ -28,6 +27,4 @@
void webkitAccessibleActionInterfaceInit(AtkActionIface*);
-#endif
-
-#endif // WebKitAccessibleInterfaceAction_h
+#endif // HAVE(ACCESSIBILITY)
diff --git a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceComponent.cpp b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceComponent.cpp
index b93d8c39d..e86f23098 100644
--- a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceComponent.cpp
+++ b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceComponent.cpp
@@ -85,7 +85,7 @@ static void webkitAccessibleComponentGetExtents(AtkComponent* component, gint* x
g_return_if_fail(ATK_IS_COMPONENT(component));
returnIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(component));
- IntRect rect = pixelSnappedIntRect(core(component)->elementRect());
+ IntRect rect = snappedIntRect(core(component)->elementRect());
contentsRelativeToAtkCoordinateType(core(component), coordType, rect, x, y, width, height);
}
diff --git a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceComponent.h b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceComponent.h
index 4ccfd97a4..209d51e7e 100644
--- a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceComponent.h
+++ b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceComponent.h
@@ -19,8 +19,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef WebKitAccessibleInterfaceComponent_h
-#define WebKitAccessibleInterfaceComponent_h
+#pragma once
#if HAVE(ACCESSIBILITY)
@@ -28,6 +27,4 @@
void webkitAccessibleComponentInterfaceInit(AtkComponentIface*);
-#endif
-
-#endif // WebKitAccessibleInterfaceComponent_h
+#endif // HAVE(ACCESSIBILITY)
diff --git a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceDocument.cpp b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceDocument.cpp
index 5a52f8130..6752d12d3 100644
--- a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceDocument.cpp
+++ b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceDocument.cpp
@@ -89,7 +89,7 @@ static AtkAttributeSet* webkitAccessibleDocumentGetAttributes(AtkDocument* docum
g_return_val_if_fail(ATK_IS_DOCUMENT(document), 0);
returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(document), 0);
- AtkAttributeSet* attributeSet = 0;
+ AtkAttributeSet* attributeSet = nullptr;
const gchar* attributes[] = { "DocType", "Encoding", "URI" };
for (unsigned i = 0; i < G_N_ELEMENTS(attributes); i++) {
diff --git a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceDocument.h b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceDocument.h
index 5ee946962..6fab32ada 100644
--- a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceDocument.h
+++ b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceDocument.h
@@ -19,8 +19,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef WebKitAccessibleInterfaceDocument_h
-#define WebKitAccessibleInterfaceDocument_h
+#pragma once
#if HAVE(ACCESSIBILITY)
@@ -28,5 +27,4 @@
void webkitAccessibleDocumentInterfaceInit(AtkDocumentIface*);
-#endif
-#endif // WebKitAccessibleInterfaceDocument_h
+#endif // HAVE(ACCESSIBILITY)
diff --git a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceEditableText.h b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceEditableText.h
index da7669130..6414e2d88 100644
--- a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceEditableText.h
+++ b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceEditableText.h
@@ -19,8 +19,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef WebKitAccessibleInterfaceEditableText_h
-#define WebKitAccessibleInterfaceEditableText_h
+#pragma once
#if HAVE(ACCESSIBILITY)
@@ -28,5 +27,4 @@
void webkitAccessibleEditableTextInterfaceInit(AtkEditableTextIface*);
-#endif
-#endif // WebKitAccessibleInterfaceEditableText_h
+#endif // HAVE(ACCESSIBILITY)
diff --git a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceHyperlinkImpl.h b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceHyperlinkImpl.h
index 56a8c67ac..c9eebac5b 100644
--- a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceHyperlinkImpl.h
+++ b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceHyperlinkImpl.h
@@ -17,8 +17,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef WebKitAccessibleInterfaceHyperlinkImpl_h
-#define WebKitAccessibleInterfaceHyperlinkImpl_h
+#pragma once
#if HAVE(ACCESSIBILITY)
@@ -26,5 +25,4 @@
void webkitAccessibleHyperlinkImplInterfaceInit(AtkHyperlinkImplIface*);
-#endif
-#endif // WebKitAccessibleInterfaceHyperlinkImpl_h
+#endif // HAVE(ACCESSIBILITY)
diff --git a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceHypertext.cpp b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceHypertext.cpp
index 75c045c01..db60218df 100644
--- a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceHypertext.cpp
+++ b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceHypertext.cpp
@@ -70,12 +70,9 @@ static gint webkitAccessibleHypertextGetNLinks(AtkHypertext* hypertext)
returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(hypertext), 0);
const AccessibilityObject::AccessibilityChildrenVector& children = core(hypertext)->children();
- if (!children.size())
- return 0;
-
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))
diff --git a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceHypertext.h b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceHypertext.h
index 7a3a0892a..da163af23 100644
--- a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceHypertext.h
+++ b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceHypertext.h
@@ -17,8 +17,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef WebKitAccessibleInterfaceHypertext_h
-#define WebKitAccessibleInterfaceHypertext_h
+#pragma once
#if HAVE(ACCESSIBILITY)
@@ -26,6 +25,4 @@
void webkitAccessibleHypertextInterfaceInit(AtkHypertextIface*);
-#endif
-
-#endif // WebKitAccessibleInterfaceHypertext_h
+#endif // HAVE(ACCESSIBILITY)
diff --git a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceImage.cpp b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceImage.cpp
index bab75995a..843969b42 100644
--- a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceImage.cpp
+++ b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceImage.cpp
@@ -54,7 +54,7 @@ static void webkitAccessibleImageGetImagePosition(AtkImage* image, gint* x, gint
g_return_if_fail(ATK_IMAGE(image));
returnIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(image));
- IntRect rect = pixelSnappedIntRect(core(image)->elementRect());
+ IntRect rect = snappedIntRect(core(image)->elementRect());
contentsRelativeToAtkCoordinateType(core(image), coordType, rect, x, y);
}
@@ -71,7 +71,7 @@ static void webkitAccessibleImageGetImageSize(AtkImage* image, gint* width, gint
g_return_if_fail(ATK_IMAGE(image));
returnIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(image));
- IntSize size = core(image)->pixelSnappedSize();
+ IntSize size = snappedIntRect(core(image)->elementRect()).size();
if (width)
*width = size.width();
diff --git a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceImage.h b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceImage.h
index 45be65541..4f31640c9 100644
--- a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceImage.h
+++ b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceImage.h
@@ -19,8 +19,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef WebKitAccessibleInterfaceImage_h
-#define WebKitAccessibleInterfaceImage_h
+#pragma once
#if HAVE(ACCESSIBILITY)
@@ -28,5 +27,4 @@
void webkitAccessibleImageInterfaceInit(AtkImageIface*);
-#endif
-#endif // WebKitAccessibleInterfaceImage_h
+#endif // HAVE(ACCESSIBILITY)
diff --git a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceSelection.cpp b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceSelection.cpp
index cb7a3ccff..ec7f8caa5 100644
--- a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceSelection.cpp
+++ b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceSelection.cpp
@@ -45,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));
}
@@ -56,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())
@@ -67,11 +67,11 @@ static AccessibilityObject* listObjectForSelection(AtkSelection* selection)
// of items with role MenuListOptionRole.
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;
}
@@ -80,50 +80,37 @@ 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;
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)
{
- // i is the ith selection as opposed to the ith child.
-
AccessibilityObject* coreSelection = core(selection);
if (!coreSelection || !coreSelection->isAccessibilityRenderObject() || index < 0)
- return 0;
-
- AccessibilityObject::AccessibilityChildrenVector selectedItems;
- if (coreSelection->isListBox())
- coreSelection->selectedChildren(selectedItems);
- else if (coreSelection->isMenuList()) {
- RenderObject* renderer = coreSelection->renderer();
- if (!renderer)
- return 0;
-
- HTMLSelectElement* selectNode = toHTMLSelectElement(renderer->node());
- int selectedIndex = selectNode->selectedIndex();
- const Vector<HTMLElement*> listItems = selectNode->listItems();
-
- if (selectedIndex < 0 || selectedIndex >= static_cast<int>(listItems.size()))
- return 0;
-
- return optionFromList(selection, selectedIndex);
- }
-
- if (index < static_cast<gint>(selectedItems.size()))
- return selectedItems.at(index).get();
+ return nullptr;
+
+ // This method provides the functionality expected by atk_selection_ref_selection().
+ // According to the ATK documentation for this method, the index is "a gint specifying
+ // the index in the selection set. (e.g. the ith selection as opposed to the ith child)."
+ // There is different API, namely atk_object_ref_accessible_child(), when the ith child
+ // from the set of all children is sought.
+ AccessibilityObject::AccessibilityChildrenVector options;
+ coreSelection->selectedChildren(options);
+ if (index < static_cast<gint>(options.size()))
+ return options.at(index).get();
- return 0;
+ return nullptr;
}
static gboolean webkitAccessibleSelectionAddSelection(AtkSelection* selection, gint index)
@@ -154,20 +141,20 @@ static gboolean webkitAccessibleSelectionClearSelection(AtkSelection* selection)
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 = toAccessibilityListBox(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), 0);
- returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(selection), 0);
+ g_return_val_if_fail(ATK_SELECTION(selection), nullptr);
+ returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(selection), nullptr);
AccessibilityObject* option = optionFromSelection(selection, index);
if (option) {
@@ -176,7 +163,7 @@ static AtkObject* webkitAccessibleSelectionRefSelection(AtkSelection* selection,
return child;
}
- return 0;
+ return nullptr;
}
static gint webkitAccessibleSelectionGetSelectionCount(AtkSelection* selection)
@@ -188,22 +175,18 @@ static gint webkitAccessibleSelectionGetSelectionCount(AtkSelection* selection)
if (!coreSelection || !coreSelection->isAccessibilityRenderObject())
return 0;
- if (coreSelection->isListBox()) {
- AccessibilityObject::AccessibilityChildrenVector selectedItems;
- coreSelection->selectedChildren(selectedItems);
- return static_cast<gint>(selectedItems.size());
- }
-
if (coreSelection->isMenuList()) {
RenderObject* renderer = coreSelection->renderer();
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;
+ AccessibilityObject::AccessibilityChildrenVector selectedItems;
+ coreSelection->selectedChildren(selectedItems);
+ return static_cast<gint>(selectedItems.size());
}
static gboolean webkitAccessibleSelectionIsChildSelected(AtkSelection* selection, gint index)
@@ -231,7 +214,6 @@ static gboolean webkitAccessibleSelectionRemoveSelection(AtkSelection* selection
if (!coreSelection)
return FALSE;
- // TODO: This is only getting called if i == 0. What is preventing the rest?
AccessibilityObject* option = optionFromSelection(selection, index);
if (option && (coreSelection->isListBox() || coreSelection->isMenuList())) {
option->setSelected(false);
@@ -250,12 +232,12 @@ static gboolean webkitAccessibleSelectionSelectAllSelection(AtkSelection* select
if (!coreSelection || !coreSelection->isMultiSelectable())
return FALSE;
- if (coreSelection->isListBox()) {
+ if (is<AccessibilityListBox>(*coreSelection)) {
const AccessibilityObject::AccessibilityChildrenVector& children = coreSelection->children();
- AccessibilityListBox* listBox = toAccessibilityListBox(coreSelection);
- listBox->setSelectedChildren(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/WebKitAccessibleInterfaceSelection.h b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceSelection.h
index 6500029d0..e0f50aa82 100644
--- a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceSelection.h
+++ b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceSelection.h
@@ -19,8 +19,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef WebKitAccessibleInterfaceSelection_h
-#define WebKitAccessibleInterfaceSelection_h
+#pragma once
#if HAVE(ACCESSIBILITY)
@@ -28,5 +27,4 @@
void webkitAccessibleSelectionInterfaceInit(AtkSelectionIface*);
-#endif
-#endif // WebKitAccessibleInterfaceSelection_h
+#endif // HAVE(ACCESSIBILITY)
diff --git a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceTable.cpp b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceTable.cpp
index f6ed19c0a..a03443afc 100644
--- a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceTable.cpp
+++ b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceTable.cpp
@@ -37,7 +37,6 @@
#include "AccessibilityObject.h"
#include "AccessibilityTable.h"
#include "AccessibilityTableCell.h"
-#include "HTMLSelectElement.h"
#include "HTMLTableCaptionElement.h"
#include "HTMLTableElement.h"
#include "RenderElement.h"
@@ -50,7 +49,7 @@ using namespace WebCore;
static AccessibilityObject* core(AtkTable* table)
{
if (!WEBKIT_IS_ACCESSIBLE(table))
- return 0;
+ return nullptr;
return webkitAccessibleGetAccessibilityObject(WEBKIT_ACCESSIBLE(table));
}
@@ -58,9 +57,9 @@ static AccessibilityObject* core(AtkTable* table)
static AccessibilityTableCell* cell(AtkTable* table, guint row, guint column)
{
AccessibilityObject* accTable = core(table);
- if (accTable->isAccessibilityRenderObject())
- return toAccessibilityTable(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)
@@ -79,15 +78,13 @@ 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;
- toAccessibilityTable(accTable)->cells(allCells);
- if (0 <= index && static_cast<unsigned>(index) < allCells.size()) {
- AccessibilityObject* accCell = allCells.at(index).get();
- return toAccessibilityTableCell(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)
@@ -114,7 +111,7 @@ static gint webkitAccessibleTableGetIndexAt(AtkTable* table, gint row, gint colu
returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), -1);
AccessibilityTableCell* axCell = cell(table, row, column);
- AccessibilityTable* axTable = toAccessibilityTable(core(table));
+ AccessibilityTable* axTable = downcast<AccessibilityTable>(core(table));
return cellIndex(axCell, axTable);
}
@@ -152,8 +149,8 @@ static gint webkitAccessibleTableGetNColumns(AtkTable* table)
returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), 0);
AccessibilityObject* accTable = core(table);
- if (accTable->isAccessibilityRenderObject())
- return toAccessibilityTable(accTable)->columnCount();
+ if (is<AccessibilityTable>(*accTable))
+ return downcast<AccessibilityTable>(*accTable).columnCount();
return 0;
}
@@ -163,8 +160,8 @@ static gint webkitAccessibleTableGetNRows(AtkTable* table)
returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), 0);
AccessibilityObject* accTable = core(table);
- if (accTable->isAccessibilityRenderObject())
- return toAccessibilityTable(accTable)->rowCount();
+ if (is<AccessibilityTable>(*accTable))
+ return downcast<AccessibilityTable>(*accTable).rowCount();
return 0;
}
@@ -202,18 +199,18 @@ static AtkObject* webkitAccessibleTableGetColumnHeader(AtkTable* table, gint col
returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), 0);
AccessibilityObject* accTable = core(table);
- if (accTable->isAccessibilityRenderObject()) {
+ if (is<AccessibilityTable>(*accTable)) {
AccessibilityObject::AccessibilityChildrenVector columnHeaders;
- toAccessibilityTable(accTable)->columnHeaders(columnHeaders);
+ downcast<AccessibilityTable>(*accTable).columnHeaders(columnHeaders);
for (const auto& columnHeader : columnHeaders) {
std::pair<unsigned, unsigned> columnRange;
- toAccessibilityTableCell(columnHeader.get())->columnIndexRange(columnRange);
+ downcast<AccessibilityTableCell>(*columnHeader).columnIndexRange(columnRange);
if (columnRange.first <= static_cast<unsigned>(column) && static_cast<unsigned>(column) < columnRange.first + columnRange.second)
return columnHeader->wrapper();
}
}
- return 0;
+ return nullptr;
}
static AtkObject* webkitAccessibleTableGetRowHeader(AtkTable* table, gint row)
@@ -222,35 +219,35 @@ static AtkObject* webkitAccessibleTableGetRowHeader(AtkTable* table, gint row)
returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), 0);
AccessibilityObject* accTable = core(table);
- if (accTable->isAccessibilityRenderObject()) {
+ if (is<AccessibilityTable>(*accTable)) {
AccessibilityObject::AccessibilityChildrenVector rowHeaders;
- toAccessibilityTable(accTable)->rowHeaders(rowHeaders);
+ downcast<AccessibilityTable>(*accTable).rowHeaders(rowHeaders);
for (const auto& rowHeader : rowHeaders) {
std::pair<unsigned, unsigned> rowRange;
- toAccessibilityTableCell(rowHeader.get())->rowIndexRange(rowRange);
+ downcast<AccessibilityTableCell>(*rowHeader).rowIndexRange(rowRange);
if (rowRange.first <= static_cast<unsigned>(row) && static_cast<unsigned>(row) < rowRange.first + rowRange.second)
return rowHeader->wrapper();
}
}
- return 0;
+ return nullptr;
}
static AtkObject* webkitAccessibleTableGetCaption(AtkTable* table)
{
- g_return_val_if_fail(ATK_TABLE(table), 0);
- returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), 0);
+ 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()->element())->wrapper();
}
}
- return 0;
+ return nullptr;
}
static const gchar* webkitAccessibleTableGetColumnDescription(AtkTable* table, gint column)
diff --git a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceTable.h b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceTable.h
index 5bddf39c2..eacc4dfea 100644
--- a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceTable.h
+++ b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceTable.h
@@ -19,8 +19,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef WebKitAccessibleInterfaceTable_h
-#define WebKitAccessibleInterfaceTable_h
+#pragma once
#if HAVE(ACCESSIBILITY)
@@ -28,5 +27,4 @@
void webkitAccessibleTableInterfaceInit(AtkTableIface*);
-#endif
-#endif // WebKitAccessibleInterfaceTable_h
+#endif // HAVE(ACCESSIBILITY)
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..d2f58c6fb
--- /dev/null
+++ b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceTableCell.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2008 Nuanti Ltd.
+ * Copyright (C) 2009 Jan Alonzo
+ * Copyright (C) 2009, 2010, 2011, 2012 Igalia S.L.
+ *
+ * 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.
+ */
+
+#pragma once
+
+#if HAVE(ACCESSIBILITY)
+
+#include <atk/atk.h>
+
+#if ATK_CHECK_VERSION(2,11,90)
+void webkitAccessibleTableCellInterfaceInit(AtkTableCellIface*);
+#endif
+
+#endif // HAVE(ACCESSIBILITY)
diff --git a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceText.cpp b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceText.cpp
index 8422d7045..5edcc98f3 100644
--- a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceText.cpp
+++ b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceText.cpp
@@ -36,7 +36,7 @@
#include "AccessibilityObject.h"
#include "Document.h"
-#include "Font.h"
+#include "FontCascade.h"
#include "FrameView.h"
#include "HTMLParserIdioms.h"
#include "HostWindow.h"
@@ -51,7 +51,7 @@
#include "WebKitAccessibleUtil.h"
#include "WebKitAccessibleWrapperAtk.h"
#include "htmlediting.h"
-#include <wtf/gobject/GUniquePtr.h>
+#include <wtf/glib/GUniquePtr.h>
#include <wtf/text/CString.h>
using namespace WebCore;
@@ -84,9 +84,9 @@ static AtkAttributeSet* getAttributeSetForAccessibilityObject(const Accessibilit
return 0;
RenderObject* renderer = object->renderer();
- RenderStyle* style = &renderer->style();
+ auto* style = &renderer->style();
- AtkAttributeSet* result = 0;
+ 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());
@@ -125,19 +125,19 @@ static AtkAttributeSet* getAttributeSetForAccessibilityObject(const Accessibilit
}
if (!style->textIndent().isUndefined()) {
- int indentation = valueForLength(style->textIndent(), object->size().width(), &renderer->view());
+ 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;
@@ -192,19 +192,19 @@ 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().string();
+ String invalidStatus = object->invalidStatus();
if (invalidStatus != "false") {
// Register the custom attribute for 'aria-invalid' if not done yet.
if (atkTextAttributeInvalid == ATK_TEXT_ATTR_INVALID)
@@ -231,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);
@@ -270,8 +270,8 @@ 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);
+ if (is<RenderListMarker>(renderer)) {
+ auto& marker = downcast<RenderListMarker>(*renderer);
return marker.text().length() + marker.suffix().length();
}
@@ -318,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) {
@@ -363,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;
}
@@ -495,8 +495,8 @@ static gchar* webkitAccessibleTextGetText(AtkText* text, gint startOffset, gint
int actualEndOffset = endOffset == -1 ? ret.length() : endOffset;
if (coreObject->roleValue() == ListItemRole) {
RenderObject* objRenderer = coreObject->renderer();
- if (objRenderer && objRenderer->isListItem()) {
- String markerText = toRenderListItem(objRenderer)->markerTextWithSuffix();
+ if (is<RenderListItem>(objRenderer)) {
+ String markerText = downcast<RenderListItem>(*objRenderer).markerTextWithSuffix();
ret = objRenderer->style().direction() == LTR ? markerText + ret : ret + markerText;
if (endOffset == -1)
actualEndOffset = ret.length() + markerText.length();
@@ -601,7 +601,7 @@ static VisibleSelection wordAtPositionForAtkBoundary(const AccessibilityObject*
// 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()))
+ if (isStartOfWord(position) && deprecatedIsEditingWhitespace(position.characterBefore()))
startPosition = position;
else
startPosition = previousWordPosition(position);
@@ -729,7 +729,7 @@ static bool isWhiteSpaceBetweenSentences(const VisiblePosition& position)
if (position.isNull())
return false;
- if (!isWhitespace(position.characterAfter()))
+ if (!deprecatedIsEditingWhitespace(position.characterAfter()))
return false;
VisiblePosition startOfWhiteSpace = startOfWord(position, RightWordIfOnBoundary);
@@ -764,12 +764,12 @@ static VisibleSelection sentenceAtPositionForAtkBoundary(const AccessibilityObje
// 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()))
+ if (deprecatedIsEditingWhitespace(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()))
+ if (deprecatedIsEditingWhitespace(endPosition.characterBefore()))
endPosition = startOfWord(endPosition, LeftWordIfOnBoundary);
// Finally, do some additional adjustments that might be needed if
@@ -1055,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;
@@ -1233,29 +1233,9 @@ static gboolean webkitAccessibleTextRemoveSelection(AtkText* text, gint selectio
static gboolean webkitAccessibleTextSetCaretOffset(AtkText* text, gint offset)
{
- g_return_val_if_fail(ATK_TEXT(text), FALSE);
- returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), FALSE);
-
- AccessibilityObject* coreObject = core(text);
- if (!coreObject->isAccessibilityRenderObject())
- return FALSE;
-
- // We need to adjust the offsets for the list item marker.
- int offsetAdjustment = offsetAdjustmentForListItem(coreObject);
- if (offsetAdjustment) {
- if (offset < offsetAdjustment)
- return FALSE;
-
- offset = atkOffsetToWebCoreOffset(text, offset);
- }
-
- PlainTextRange textRange(offset, 0);
- VisiblePositionRange range = coreObject->visiblePositionRangeForRange(textRange);
- if (range.isNull())
- return FALSE;
-
- coreObject->setSelectedVisiblePositionRange(range);
- return TRUE;
+ // 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 ATK_CHECK_VERSION(2, 10, 0)
diff --git a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceText.h b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceText.h
index d7c7376e4..f9f9de071 100644
--- a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceText.h
+++ b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceText.h
@@ -19,8 +19,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef WebKitAccessibleInterfaceText_h
-#define WebKitAccessibleInterfaceText_h
+#pragma once
#if HAVE(ACCESSIBILITY)
@@ -28,5 +27,4 @@
void webkitAccessibleTextInterfaceInit(AtkTextIface*);
-#endif
-#endif // WebKitAccessibleInterfaceText_h
+#endif // HAVE(ACCESSIBILITY)
diff --git a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceValue.cpp b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceValue.cpp
index 19c6cf003..ac582fa66 100644
--- a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceValue.cpp
+++ b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceValue.cpp
@@ -26,6 +26,7 @@
#include "HTMLNames.h"
#include "WebKitAccessibleUtil.h"
#include "WebKitAccessibleWrapperAtk.h"
+#include <wtf/text/CString.h>
using namespace WebCore;
@@ -37,6 +38,83 @@ 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));
@@ -92,16 +170,7 @@ static gboolean webkitAccessibleValueSetCurrentValue(AtkValue* value, const GVal
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)
@@ -113,19 +182,17 @@ static void webkitAccessibleValueGetMinimumIncrement(AtkValue* value, GValue* gV
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. Implicit value of step should be one or larger.
- float step = (coreObject->maxValueForRange() - coreObject->minValueForRange()) * 0.05;
- g_value_set_float(gValue, step < 1 ? 1 : step);
+ 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/WebKitAccessibleInterfaceValue.h b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceValue.h
index 6e53551ec..f1b49ebeb 100644
--- a/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceValue.h
+++ b/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceValue.h
@@ -17,8 +17,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef WebKitAccessibleInterfaceValue_h
-#define WebKitAccessibleInterfaceValue_h
+#pragma once
#if HAVE(ACCESSIBILITY)
@@ -26,5 +25,4 @@
void webkitAccessibleValueInterfaceInit(AtkValueIface*);
-#endif
-#endif // WebKitAccessibleInterfaceValue_h
+#endif // HAVE(ACCESSIBILITY)
diff --git a/Source/WebCore/accessibility/atk/WebKitAccessibleUtil.cpp b/Source/WebCore/accessibility/atk/WebKitAccessibleUtil.cpp
index 58147fa8c..001f9d06f 100644
--- a/Source/WebCore/accessibility/atk/WebKitAccessibleUtil.cpp
+++ b/Source/WebCore/accessibility/atk/WebKitAccessibleUtil.cpp
@@ -159,11 +159,14 @@ bool selectionBelongsToObject(AccessibilityObject* coreObject, VisibleSelection&
// AND that the selection is not just "touching" one of the
// boundaries for the selected node. We want to check whether the
// node is actually inside the region, at least partially.
- 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)));
+ auto& node = *coreObject->node();
+ auto* lastDescendant = node.lastDescendant();
+ unsigned lastOffset = lastOffsetInNode(lastDescendant);
+ auto intersectsResult = range->intersectsNode(node);
+ return !intersectsResult.hasException()
+ && intersectsResult.releaseReturnValue()
+ && (&range->endContainer() != &node || range->endOffset())
+ && (&range->startContainer() != lastDescendant || range->startOffset() != lastOffset);
}
#endif
diff --git a/Source/WebCore/accessibility/atk/WebKitAccessibleUtil.h b/Source/WebCore/accessibility/atk/WebKitAccessibleUtil.h
index d0312dcd1..a946001f4 100644
--- a/Source/WebCore/accessibility/atk/WebKitAccessibleUtil.h
+++ b/Source/WebCore/accessibility/atk/WebKitAccessibleUtil.h
@@ -19,8 +19,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef WebKitAccessibleUtil_h
-#define WebKitAccessibleUtil_h
+#pragma once
#if HAVE(ACCESSIBILITY)
@@ -61,7 +60,7 @@ class VisibleSelection;
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*);
@@ -69,5 +68,4 @@ String accessibilityDescription(WebCore::AccessibilityObject*);
bool selectionBelongsToObject(WebCore::AccessibilityObject*, WebCore::VisibleSelection&);
-#endif
-#endif // WebKitAccessibleUtil_h
+#endif // HAVE(ACCESSIBILITY)
diff --git a/Source/WebCore/accessibility/atk/WebKitAccessibleWrapperAtk.cpp b/Source/WebCore/accessibility/atk/WebKitAccessibleWrapperAtk.cpp
index 79eb46d35..86cee4e25 100644
--- a/Source/WebCore/accessibility/atk/WebKitAccessibleWrapperAtk.cpp
+++ b/Source/WebCore/accessibility/atk/WebKitAccessibleWrapperAtk.cpp
@@ -37,13 +37,17 @@
#include "AXObjectCache.h"
#include "AccessibilityList.h"
#include "AccessibilityListBoxOption.h"
+#include "AccessibilityTable.h"
#include "Document.h"
#include "Frame.h"
#include "FrameView.h"
#include "HTMLNames.h"
#include "HTMLTableElement.h"
#include "HostWindow.h"
+#include "RenderAncestorIterator.h"
+#include "RenderFieldset.h"
#include "RenderObject.h"
+#include "SVGElement.h"
#include "Settings.h"
#include "TextIterator.h"
#include "VisibleUnits.h"
@@ -57,6 +61,7 @@
#include "WebKitAccessibleInterfaceImage.h"
#include "WebKitAccessibleInterfaceSelection.h"
#include "WebKitAccessibleInterfaceTable.h"
+#include "WebKitAccessibleInterfaceTableCell.h"
#include "WebKitAccessibleInterfaceText.h"
#include "WebKitAccessibleInterfaceValue.h"
#include "WebKitAccessibleUtil.h"
@@ -64,10 +69,6 @@
#include <glib/gprintf.h>
#include <wtf/text/CString.h>
-#if PLATFORM(GTK)
-#include <gtk/gtk.h>
-#endif
-
using namespace WebCore;
struct _WebKitAccessiblePrivate {
@@ -93,7 +94,7 @@ struct _WebKitAccessiblePrivate {
static AccessibilityObject* fallbackObject()
{
- static AccessibilityObject* object = AccessibilityListBoxOption::create().leakRef();
+ static AccessibilityObject* object = &AccessibilityListBoxOption::create().leakRef();
return object;
}
@@ -110,58 +111,24 @@ static const gchar* webkitAccessibleGetName(AtkObject* object)
g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0);
returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), 0);
- AccessibilityObject* coreObject = core(object);
-
- if (!coreObject->isAccessibilityRenderObject())
- return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName, coreObject->stringValue());
+ Vector<AccessibilityText> textOrder;
+ core(object)->accessibilityText(textOrder);
- if (coreObject->isFieldset()) {
- AccessibilityObject* label = coreObject->titleUIElement();
- if (label) {
- AtkObject* atkObject = label->wrapper();
- if (ATK_IS_TEXT(atkObject))
- return atk_text_get_text(ATK_TEXT(atkObject), 0, -1);
- }
- }
-
- if (coreObject->isControl()) {
- AccessibilityObject* label = coreObject->correspondingLabelForControlElement();
- if (label) {
- AtkObject* atkObject = label->wrapper();
- if (ATK_IS_TEXT(atkObject))
- return atk_text_get_text(ATK_TEXT(atkObject), 0, -1);
- }
-
- // Try text under the node.
- String textUnder = coreObject->textUnderElement();
- if (textUnder.length())
- return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName, textUnder);
- }
-
- if (coreObject->isImage() || coreObject->isInputImage()) {
- Node* node = coreObject->node();
- if (node && node->isHTMLElement()) {
- // Get the attribute rather than altText String so as not to fall back on title.
- String alt = toHTMLElement(node)->getAttribute(HTMLNames::altAttr);
- if (!alt.isEmpty())
- return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName, alt);
- }
- }
+ for (const auto& text : textOrder) {
+ // FIXME: This check is here because AccessibilityNodeObject::titleElementText()
+ // appends an empty String for the LabelByElementText source when there is a
+ // titleUIElement(). Removing this check makes some fieldsets lose their name.
+ if (text.text.isEmpty())
+ continue;
- // Fallback for the webArea object: just return the document's title.
- if (coreObject->isWebArea()) {
- Document* document = coreObject->document();
- if (document)
- return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName, document->title());
+ // WebCore Accessibility should provide us with the text alternative computation
+ // in the order defined by that spec. So take the first thing that our platform
+ // does not expose via the AtkObject description.
+ if (text.textSource != HelpText && text.textSource != SummaryText)
+ return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName, text.text);
}
- // Nothing worked so far, try with the AccessibilityObject's
- // title() before going ahead with stringValue().
- String axTitle = accessibilityTitle(coreObject);
- if (!axTitle.isEmpty())
- return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName, axTitle);
-
- return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName, coreObject->stringValue());
+ return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName, "");
}
static const gchar* webkitAccessibleGetDescription(AtkObject* object)
@@ -169,27 +136,26 @@ static const gchar* webkitAccessibleGetDescription(AtkObject* object)
g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0);
returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), 0);
- AccessibilityObject* coreObject = core(object);
- Node* node = 0;
- if (coreObject->isAccessibilityRenderObject())
- node = coreObject->node();
- if (!node || !node->isHTMLElement() || coreObject->ariaRoleAttribute() != UnknownRole || coreObject->isImage())
- return cacheAndReturnAtkProperty(object, AtkCachedAccessibleDescription, accessibilityDescription(coreObject));
-
- // atk_table_get_summary returns an AtkObject. We have no summary object, so expose summary here.
- if (coreObject->roleValue() == TableRole) {
- String summary = toHTMLTableElement(node)->summary();
- if (!summary.isEmpty())
- return cacheAndReturnAtkProperty(object, AtkCachedAccessibleDescription, summary);
- }
+ Vector<AccessibilityText> textOrder;
+ core(object)->accessibilityText(textOrder);
+
+ bool nameTextAvailable = false;
+ for (const auto& text : textOrder) {
+ // WebCore Accessibility should provide us with the text alternative computation
+ // in the order defined by that spec. So take the first thing that our platform
+ // does not expose via the AtkObject name.
+ if (text.textSource == HelpText || text.textSource == SummaryText)
+ return cacheAndReturnAtkProperty(object, AtkCachedAccessibleDescription, text.text);
- // The title attribute should be reliably available as the object's descripton.
- // We do not want to fall back on other attributes in its absence. See bug 25524.
- String title = toHTMLElement(node)->title();
- if (!title.isEmpty())
- return cacheAndReturnAtkProperty(object, AtkCachedAccessibleDescription, title);
+ // If there is no other text alternative, the title tag contents will have been
+ // used for the AtkObject name. We don't want to duplicate it here.
+ if (text.textSource == TitleTagText && nameTextAvailable)
+ return cacheAndReturnAtkProperty(object, AtkCachedAccessibleDescription, text.text);
- return cacheAndReturnAtkProperty(object, AtkCachedAccessibleDescription, accessibilityDescription(coreObject));
+ nameTextAvailable = true;
+ }
+
+ return cacheAndReturnAtkProperty(object, AtkCachedAccessibleDescription, "");
}
static void removeAtkRelationByType(AtkRelationSet* relationSet, AtkRelationType relationType)
@@ -206,57 +172,56 @@ static void removeAtkRelationByType(AtkRelationSet* relationSet, AtkRelationType
static void setAtkRelationSetFromCoreObject(AccessibilityObject* coreObject, AtkRelationSet* relationSet)
{
- if (coreObject->isFieldset()) {
- AccessibilityObject* label = coreObject->titleUIElement();
- if (label) {
- removeAtkRelationByType(relationSet, ATK_RELATION_LABELLED_BY);
- atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABELLED_BY, label->wrapper());
- }
- return;
- }
-
- if (coreObject->roleValue() == LegendRole) {
- for (AccessibilityObject* parent = coreObject->parentObjectUnignored(); parent; parent = parent->parentObjectUnignored()) {
- if (parent->isFieldset()) {
- atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABEL_FOR, parent->wrapper());
- break;
- }
- }
- return;
- }
+ // FIXME: We're not implementing all the relation types, most notably the inverse/reciprocal
+ // types. Filed as bug 155494.
+ // Elements with aria-labelledby should have the labelled-by relation as per the ARIA AAM spec.
+ // Controls with a label element and fieldsets with a legend element should also use this relation
+ // as per the HTML AAM spec. The reciprocal label-for relation should also be used.
+ removeAtkRelationByType(relationSet, ATK_RELATION_LABELLED_BY);
if (coreObject->isControl()) {
- AccessibilityObject* label = coreObject->correspondingLabelForControlElement();
- if (label) {
- removeAtkRelationByType(relationSet, ATK_RELATION_LABELLED_BY);
+ if (AccessibilityObject* label = coreObject->correspondingLabelForControlElement())
atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABELLED_BY, label->wrapper());
+ } else if (coreObject->isFieldset()) {
+ if (AccessibilityObject* label = coreObject->titleUIElement())
+ atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABELLED_BY, label->wrapper());
+ } else if (coreObject->roleValue() == LegendRole) {
+ if (RenderFieldset* renderFieldset = ancestorsOfType<RenderFieldset>(*coreObject->renderer()).first()) {
+ AccessibilityObject* fieldset = coreObject->axObjectCache()->getOrCreate(renderFieldset);
+ atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABEL_FOR, fieldset->wrapper());
}
+ } else if (AccessibilityObject* control = coreObject->correspondingControlForLabelElement()) {
+ atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABEL_FOR, control->wrapper());
} else {
- AccessibilityObject* control = coreObject->correspondingControlForLabelElement();
- if (control)
- atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABEL_FOR, control->wrapper());
+ AccessibilityObject::AccessibilityChildrenVector ariaLabelledByElements;
+ coreObject->ariaLabelledByElements(ariaLabelledByElements);
+ for (const auto& accessibilityObject : ariaLabelledByElements)
+ atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABELLED_BY, accessibilityObject->wrapper());
}
- // Check whether object supports aria-flowto
- if (coreObject->supportsARIAFlowTo()) {
- removeAtkRelationByType(relationSet, ATK_RELATION_FLOWS_TO);
- AccessibilityObject::AccessibilityChildrenVector ariaFlowToElements;
- coreObject->ariaFlowToElements(ariaFlowToElements);
- for (const auto& accessibilityObject : ariaFlowToElements)
- atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_FLOWS_TO, accessibilityObject->wrapper());
- }
-
- // Check whether object supports aria-describedby. It provides an additional information for the user.
- if (coreObject->supportsARIADescribedBy()) {
- removeAtkRelationByType(relationSet, ATK_RELATION_DESCRIBED_BY);
- AccessibilityObject::AccessibilityChildrenVector ariaDescribedByElements;
- coreObject->ariaDescribedByElements(ariaDescribedByElements);
- for (const auto& accessibilityObject : ariaDescribedByElements)
- atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_DESCRIBED_BY, accessibilityObject->wrapper());
- }
+ // Elements with aria-flowto should have the flows-to relation as per the ARIA AAM spec.
+ removeAtkRelationByType(relationSet, ATK_RELATION_FLOWS_TO);
+ AccessibilityObject::AccessibilityChildrenVector ariaFlowToElements;
+ coreObject->ariaFlowToElements(ariaFlowToElements);
+ for (const auto& accessibilityObject : ariaFlowToElements)
+ atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_FLOWS_TO, accessibilityObject->wrapper());
+
+ // Elements with aria-describedby should have the described-by relation as per the ARIA AAM spec.
+ removeAtkRelationByType(relationSet, ATK_RELATION_DESCRIBED_BY);
+ AccessibilityObject::AccessibilityChildrenVector ariaDescribedByElements;
+ coreObject->ariaDescribedByElements(ariaDescribedByElements);
+ for (const auto& accessibilityObject : ariaDescribedByElements)
+ atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_DESCRIBED_BY, accessibilityObject->wrapper());
+
+ // Elements with aria-controls should have the controller-for relation as per the ARIA AAM spec.
+ removeAtkRelationByType(relationSet, ATK_RELATION_CONTROLLER_FOR);
+ AccessibilityObject::AccessibilityChildrenVector ariaControls;
+ coreObject->ariaControlsElements(ariaControls);
+ for (const auto& accessibilityObject : ariaControls)
+ atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_CONTROLLER_FOR, accessibilityObject->wrapper());
}
-static gpointer webkitAccessibleParentClass = 0;
+static gpointer webkitAccessibleParentClass = nullptr;
static bool isRootObject(AccessibilityObject* coreObject)
{
@@ -284,18 +249,6 @@ static AtkObject* atkParentOfRootObject(AtkObject* object)
Document* document = coreObject->document();
if (!document)
return 0;
-
-#if PLATFORM(GTK)
- HostWindow* hostWindow = document->view()->hostWindow();
- if (hostWindow) {
- PlatformPageClient scrollView = hostWindow->platformPageClient();
- if (scrollView) {
- GtkWidget* scrollViewParent = gtk_widget_get_parent(scrollView);
- if (scrollViewParent)
- return gtk_widget_get_accessible(scrollViewParent);
- }
- }
-#endif // PLATFORM(GTK)
}
if (!coreParent)
@@ -327,31 +280,9 @@ static AtkObject* webkitAccessibleGetParent(AtkObject* object)
if (!coreParent)
return 0;
- // We don't expose table rows to Assistive technologies, but we
- // need to have them anyway in the hierarchy from WebCore to
- // properly perform coordinates calculations when requested.
- if (coreParent->isTableRow() && coreObject->isTableCell())
- coreParent = coreParent->parentObjectUnignored();
-
return coreParent->wrapper();
}
-static gint getNChildrenForTable(AccessibilityObject* coreObject)
-{
- const AccessibilityObject::AccessibilityChildrenVector& tableChildren = coreObject->children();
- size_t cellsCount = 0;
-
- // Look for the actual index of the cell inside the table.
- for (const auto& tableChild : tableChildren) {
- if (tableChild->isTableRow())
- cellsCount += tableChild->children().size();
- else
- cellsCount++;
- }
-
- return cellsCount;
-}
-
static gint webkitAccessibleGetNChildren(AtkObject* object)
{
g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0);
@@ -359,38 +290,9 @@ static gint webkitAccessibleGetNChildren(AtkObject* object)
AccessibilityObject* coreObject = core(object);
- // Tables should be treated in a different way because rows should
- // be bypassed when exposing the accessible hierarchy.
- if (coreObject->isAccessibilityTable())
- return getNChildrenForTable(coreObject);
-
return coreObject->children().size();
}
-static AccessibilityObject* getChildForTable(AccessibilityObject* coreObject, gint index)
-{
- const AccessibilityObject::AccessibilityChildrenVector& tableChildren = coreObject->children();
- size_t cellsCount = 0;
-
- // Look for the actual index of the cell inside the table.
- size_t current = static_cast<size_t>(index);
- for (const auto& tableChild : tableChildren) {
- if (tableChild->isTableRow()) {
- const AccessibilityObject::AccessibilityChildrenVector& rowChildren = tableChild->children();
- size_t rowChildrenCount = rowChildren.size();
- if (current < cellsCount + rowChildrenCount)
- return rowChildren.at(current - cellsCount).get();
- cellsCount += rowChildrenCount;
- } else if (cellsCount == current)
- return tableChild.get();
- else
- cellsCount++;
- }
-
- // Shouldn't reach if the child was found.
- return 0;
-}
-
static AtkObject* webkitAccessibleRefChild(AtkObject* object, gint index)
{
g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0);
@@ -400,18 +302,12 @@ static AtkObject* webkitAccessibleRefChild(AtkObject* object, gint index)
return 0;
AccessibilityObject* coreObject = core(object);
- AccessibilityObject* coreChild = 0;
-
- // Tables are special cases because rows should be bypassed, but
- // still taking their cells into account.
- if (coreObject->isAccessibilityTable())
- coreChild = getChildForTable(coreObject, index);
- else {
- const AccessibilityObject::AccessibilityChildrenVector& children = coreObject->children();
- if (static_cast<unsigned>(index) >= children.size())
- return 0;
- coreChild = children.at(index).get();
- }
+ AccessibilityObject* coreChild = nullptr;
+
+ const AccessibilityObject::AccessibilityChildrenVector& children = coreObject->children();
+ if (static_cast<size_t>(index) >= children.size())
+ return 0;
+ coreChild = children.at(index).get();
if (!coreChild)
return 0;
@@ -423,40 +319,6 @@ static AtkObject* webkitAccessibleRefChild(AtkObject* object, gint index)
return child;
}
-static gint getIndexInParentForCellInRow(AccessibilityObject* coreObject)
-{
- AccessibilityObject* parent = coreObject->parentObjectUnignored();
- if (!parent)
- return -1;
-
- AccessibilityObject* grandParent = parent->parentObjectUnignored();
- if (!grandParent)
- return -1;
-
- const AccessibilityObject::AccessibilityChildrenVector& rows = grandParent->children();
- size_t previousCellsCount = 0;
-
- // Look for the actual index of the cell inside the table.
- for (const auto& row : rows) {
- if (!row->isTableRow())
- continue;
-
- const AccessibilityObject::AccessibilityChildrenVector& cells = row->children();
- size_t cellsCount = cells.size();
-
- if (row == parent) {
- for (unsigned j = 0; j < cellsCount; ++j) {
- if (cells[j] == coreObject)
- return previousCellsCount + j;
- }
- }
-
- previousCellsCount += cellsCount;
- }
-
- return -1;
-}
-
static gint webkitAccessibleGetIndexInParent(AtkObject* object)
{
g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), -1);
@@ -480,11 +342,6 @@ static gint webkitAccessibleGetIndexInParent(AtkObject* object)
}
}
- // Need to calculate the index of the cell in the table, as
- // rows won't be exposed to assistive technologies.
- if (parent && parent->isTableRow() && coreObject->isTableCell())
- return getIndexInParentForCellInRow(coreObject);
-
if (!parent)
return -1;
@@ -497,11 +354,9 @@ static AtkAttributeSet* webkitAccessibleGetAttributes(AtkObject* object)
g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0);
returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), 0);
- AtkAttributeSet* attributeSet = 0;
+ AtkAttributeSet* attributeSet = nullptr;
#if PLATFORM(GTK)
attributeSet = addToAtkAttributeSet(attributeSet, "toolkit", "WebKitGtk");
-#elif PLATFORM(EFL)
- attributeSet = addToAtkAttributeSet(attributeSet, "toolkit", "WebKitEfl");
#endif
AccessibilityObject* coreObject = core(object);
@@ -511,9 +366,12 @@ static AtkAttributeSet* webkitAccessibleGetAttributes(AtkObject* object)
// Hack needed for WebKit2 tests because obtaining an element by its ID
// cannot be done from the UIProcess. Assistive technologies have no need
// for this information.
- Node* node = coreObject->node();
- if (node && node->isElementNode()) {
- String id = toElement(node)->getIdAttribute().string();
+ Element* element = coreObject->element() ? coreObject->element() : coreObject->actionElement();
+ if (element) {
+ String tagName = element->tagName();
+ if (!tagName.isEmpty())
+ attributeSet = addToAtkAttributeSet(attributeSet, "tag", tagName.convertToASCIILowercase().utf8().data());
+ String id = element->getIdAttribute().string();
if (!id.isEmpty())
attributeSet = addToAtkAttributeSet(attributeSet, "html-id", id.utf8().data());
}
@@ -524,9 +382,16 @@ static AtkAttributeSet* webkitAccessibleGetAttributes(AtkObject* object)
attributeSet = addToAtkAttributeSet(attributeSet, "level", value.utf8().data());
}
+ if (coreObject->roleValue() == MathElementRole) {
+ if (coreObject->isMathMultiscriptObject(PreSuperscript) || coreObject->isMathMultiscriptObject(PreSubscript))
+ attributeSet = addToAtkAttributeSet(attributeSet, "multiscript-type", "pre");
+ else if (coreObject->isMathMultiscriptObject(PostSuperscript) || coreObject->isMathMultiscriptObject(PostSubscript))
+ attributeSet = addToAtkAttributeSet(attributeSet, "multiscript-type", "post");
+ }
+
// Set the 'layout-guess' attribute to help Assistive
// Technologies know when an exposed table is not data table.
- if (coreObject->isAccessibilityTable() && !coreObject->isDataTable())
+ if (is<AccessibilityTable>(*coreObject) && downcast<AccessibilityTable>(*coreObject).isExposableThroughAccessibility() && !coreObject->isDataTable())
attributeSet = addToAtkAttributeSet(attributeSet, "layout-guess", "true");
String placeholder = coreObject->placeholderValue();
@@ -543,32 +408,38 @@ static AtkAttributeSet* webkitAccessibleGetAttributes(AtkObject* object)
attributeSet = addToAtkAttributeSet(attributeSet, "sort", sortAttribute.string().utf8().data());
}
- // Landmarks will be exposed with xml-roles object attributes, with the exception
- // of LandmarkApplicationRole, which will be exposed with ATK_ROLE_EMBEDDED.
- AccessibilityRole role = coreObject->roleValue();
- switch (role) {
- case LandmarkBannerRole:
- attributeSet = addToAtkAttributeSet(attributeSet, "xml-roles", "banner");
- break;
- case LandmarkComplementaryRole:
- attributeSet = addToAtkAttributeSet(attributeSet, "xml-roles", "complementary");
- break;
- case LandmarkContentInfoRole:
- attributeSet = addToAtkAttributeSet(attributeSet, "xml-roles", "contentinfo");
- break;
- case LandmarkMainRole:
- attributeSet = addToAtkAttributeSet(attributeSet, "xml-roles", "main");
- break;
- case LandmarkNavigationRole:
- attributeSet = addToAtkAttributeSet(attributeSet, "xml-roles", "navigation");
- break;
- case LandmarkSearchRole:
- attributeSet = addToAtkAttributeSet(attributeSet, "xml-roles", "search");
- break;
- default:
- break;
+ if (coreObject->supportsARIAPosInSet())
+ attributeSet = addToAtkAttributeSet(attributeSet, "posinset", String::number(coreObject->ariaPosInSet()).utf8().data());
+
+ if (coreObject->supportsARIASetSize())
+ attributeSet = addToAtkAttributeSet(attributeSet, "setsize", String::number(coreObject->ariaSetSize()).utf8().data());
+
+ String isReadOnly = coreObject->ariaReadOnlyValue();
+ if (!isReadOnly.isEmpty())
+ attributeSet = addToAtkAttributeSet(attributeSet, "readonly", isReadOnly.utf8().data());
+
+ String valueDescription = coreObject->valueDescription();
+ if (!valueDescription.isEmpty())
+ attributeSet = addToAtkAttributeSet(attributeSet, "valuetext", valueDescription.utf8().data());
+
+ // According to the W3C Core Accessibility API Mappings 1.1, section 5.4.1 General Rules:
+ // "User agents must expose the WAI-ARIA role string if the API supports a mechanism to do so."
+ // In the case of ATK, the mechanism to do so is an object attribute pair (xml-roles:"string").
+ // The computedRoleString is primarily for testing, and not limited to elements with ARIA roles.
+ // Because the computedRoleString currently contains the ARIA role string, we'll use it for
+ // both purposes, as the "computed-role" object attribute for all elements which have a value
+ // and also via the "xml-roles" attribute for elements with ARIA, as well as for landmarks.
+ String roleString = coreObject->computedRoleString();
+ if (!roleString.isEmpty()) {
+ if (coreObject->ariaRoleAttribute() != UnknownRole || coreObject->isLandmark())
+ attributeSet = addToAtkAttributeSet(attributeSet, "xml-roles", roleString.utf8().data());
+ attributeSet = addToAtkAttributeSet(attributeSet, "computed-role", roleString.utf8().data());
}
+ String roleDescription = coreObject->roleDescription();
+ if (!roleDescription.isEmpty())
+ attributeSet = addToAtkAttributeSet(attributeSet, "roledescription", roleDescription.utf8().data());
+
return attributeSet;
}
@@ -586,10 +457,17 @@ static AtkRole atkRole(AccessibilityObject* coreObject)
case UnknownRole:
return ATK_ROLE_UNKNOWN;
case AudioRole:
+#if ATK_CHECK_VERSION(2, 11, 3)
+ return ATK_ROLE_AUDIO;
+#endif
case VideoRole:
+#if ATK_CHECK_VERSION(2, 11, 3)
+ return ATK_ROLE_VIDEO;
+#endif
return ATK_ROLE_EMBEDDED;
case ButtonRole:
return ATK_ROLE_PUSH_BUTTON;
+ case SwitchRole:
case ToggleButtonRole:
return ATK_ROLE_TOGGLE_BUTTON;
case RadioButtonRole:
@@ -603,9 +481,14 @@ static AtkRole atkRole(AccessibilityObject* coreObject)
return ATK_ROLE_PAGE_TAB_LIST;
case TextFieldRole:
case TextAreaRole:
+ case SearchFieldRole:
return ATK_ROLE_ENTRY;
case StaticTextRole:
+#if ATK_CHECK_VERSION(2, 15, 2)
+ return ATK_ROLE_STATIC;
+#else
return ATK_ROLE_TEXT;
+#endif
case OutlineRole:
case TreeRole:
return ATK_ROLE_TREE;
@@ -627,15 +510,13 @@ static AtkRole atkRole(AccessibilityObject* coreObject)
// return ATK_ROLE_TABLE_COLUMN_HEADER; // Is this right?
return ATK_ROLE_UNKNOWN; // Matches Mozilla
case RowRole:
- // return ATK_ROLE_TABLE_ROW_HEADER; // Is this right?
- return ATK_ROLE_LIST_ITEM; // Matches Mozilla
+ return ATK_ROLE_TABLE_ROW;
case ToolbarRole:
return ATK_ROLE_TOOL_BAR;
case BusyIndicatorRole:
return ATK_ROLE_PROGRESS_BAR; // Is this right?
case ProgressIndicatorRole:
- // return ATK_ROLE_SPIN_BUTTON; // Some confusion about this role in AccessibilityRenderObject.cpp
- return ATK_ROLE_PROGRESS_BAR;
+ return coreObject->isMeter() ? ATK_ROLE_LEVEL_BAR : ATK_ROLE_PROGRESS_BAR;
case WindowRole:
return ATK_ROLE_WINDOW;
case PopUpButtonRole:
@@ -646,31 +527,42 @@ static AtkRole atkRole(AccessibilityObject* coreObject)
case SplitterRole:
return ATK_ROLE_SEPARATOR;
case ColorWellRole:
- return ATK_ROLE_COLOR_CHOOSER;
+#if PLATFORM(GTK)
+ // ATK_ROLE_COLOR_CHOOSER is defined as a dialog (i.e. it's what appears when you push the button).
+ return ATK_ROLE_PUSH_BUTTON;
+#endif
case ListRole:
return ATK_ROLE_LIST;
case ScrollBarRole:
return ATK_ROLE_SCROLL_BAR;
case ScrollAreaRole:
return ATK_ROLE_SCROLL_PANE;
- case GridRole: // Is this right?
+ case GridRole:
case TableRole:
return ATK_ROLE_TABLE;
case ApplicationRole:
return ATK_ROLE_APPLICATION;
- case GroupRole:
case RadioGroupRole:
+ case SVGRootRole:
case TabPanelRole:
return ATK_ROLE_PANEL;
- case RowHeaderRole: // Row headers are cells after all.
- case ColumnHeaderRole: // Column headers are cells after all.
+ case GroupRole:
+ return coreObject->isStyleFormatGroup() ? ATK_ROLE_SECTION : ATK_ROLE_PANEL;
+ case RowHeaderRole:
+ return ATK_ROLE_ROW_HEADER;
+ case ColumnHeaderRole:
+ return ATK_ROLE_COLUMN_HEADER;
+ case CaptionRole:
+ return ATK_ROLE_CAPTION;
case CellRole:
- return ATK_ROLE_TABLE_CELL;
+ case GridCellRole:
+ return coreObject->inheritsPresentationalRole() ? ATK_ROLE_SECTION : ATK_ROLE_TABLE_CELL;
case LinkRole:
case WebCoreLinkRole:
case ImageMapLinkRole:
return ATK_ROLE_LINK;
case ImageMapRole:
+ return ATK_ROLE_IMAGE_MAP;
case ImageRole:
return ATK_ROLE_IMAGE;
case ListMarkerRole:
@@ -686,17 +578,27 @@ static AtkRole atkRole(AccessibilityObject* coreObject)
case HeadingRole:
return ATK_ROLE_HEADING;
case ListBoxRole:
- return ATK_ROLE_LIST;
+ // https://rawgit.com/w3c/aria/master/core-aam/core-aam.html#role-map-listbox
+ return coreObject->isDescendantOfRole(ComboBoxRole) ? ATK_ROLE_MENU : ATK_ROLE_LIST_BOX;
case ListItemRole:
+ return coreObject->inheritsPresentationalRole() ? ATK_ROLE_SECTION : ATK_ROLE_LIST_ITEM;
case ListBoxOptionRole:
- return ATK_ROLE_LIST_ITEM;
+ return coreObject->isDescendantOfRole(ComboBoxRole) ? ATK_ROLE_MENU_ITEM : ATK_ROLE_LIST_ITEM;
case ParagraphRole:
return ATK_ROLE_PARAGRAPH;
case LabelRole:
case LegendRole:
return ATK_ROLE_LABEL;
+ case BlockquoteRole:
+#if ATK_CHECK_VERSION(2, 11, 3)
+ return ATK_ROLE_BLOCK_QUOTE;
+#endif
case DivRole:
+ case PreRole:
+ case SVGTextRole:
return ATK_ROLE_SECTION;
+ case FooterRole:
+ return ATK_ROLE_FOOTER;
case FormRole:
return ATK_ROLE_FORM;
case CanvasRole:
@@ -711,7 +613,7 @@ static AtkRole atkRole(AccessibilityObject* coreObject)
return ATK_ROLE_TOOL_TIP;
case WebAreaRole:
return ATK_ROLE_DOCUMENT_WEB;
- case LandmarkApplicationRole:
+ case WebApplicationRole:
return ATK_ROLE_EMBEDDED;
#if ATK_CHECK_VERSION(2, 11, 3)
case ApplicationLogRole:
@@ -724,11 +626,40 @@ static AtkRole atkRole(AccessibilityObject* coreObject)
return ATK_ROLE_DEFINITION;
case DocumentMathRole:
return ATK_ROLE_MATH;
+ case MathElementRole:
+ if (coreObject->isMathRow())
+ return ATK_ROLE_PANEL;
+ if (coreObject->isMathTable())
+ return ATK_ROLE_TABLE;
+ if (coreObject->isMathTableRow())
+ return ATK_ROLE_TABLE_ROW;
+ if (coreObject->isMathTableCell())
+ return ATK_ROLE_TABLE_CELL;
+ if (coreObject->isMathSubscriptSuperscript() || coreObject->isMathMultiscript())
+ return ATK_ROLE_SECTION;
+#if ATK_CHECK_VERSION(2, 15, 4)
+ if (coreObject->isMathFraction())
+ return ATK_ROLE_MATH_FRACTION;
+ if (coreObject->isMathSquareRoot() || coreObject->isMathRoot())
+ return ATK_ROLE_MATH_ROOT;
+ if (coreObject->isMathScriptObject(Subscript)
+ || coreObject->isMathMultiscriptObject(PreSubscript) || coreObject->isMathMultiscriptObject(PostSubscript))
+ return ATK_ROLE_SUBSCRIPT;
+ if (coreObject->isMathScriptObject(Superscript)
+ || coreObject->isMathMultiscriptObject(PreSuperscript) || coreObject->isMathMultiscriptObject(PostSuperscript))
+ return ATK_ROLE_SUPERSCRIPT;
+#endif
+#if ATK_CHECK_VERSION(2, 15, 2)
+ if (coreObject->isMathToken())
+ return ATK_ROLE_STATIC;
+#endif
+ return ATK_ROLE_UNKNOWN;
case LandmarkBannerRole:
case LandmarkComplementaryRole:
case LandmarkContentInfoRole:
case LandmarkMainRole:
case LandmarkNavigationRole:
+ case LandmarkRegionRole:
case LandmarkSearchRole:
return ATK_ROLE_LANDMARK;
#endif
@@ -740,6 +671,19 @@ static AtkRole atkRole(AccessibilityObject* coreObject)
case DescriptionListDetailRole:
return ATK_ROLE_DESCRIPTION_VALUE;
#endif
+ case InlineRole:
+#if ATK_CHECK_VERSION(2, 15, 4)
+ if (coreObject->isSubscriptStyleGroup())
+ return ATK_ROLE_SUBSCRIPT;
+ if (coreObject->isSuperscriptStyleGroup())
+ return ATK_ROLE_SUPERSCRIPT;
+#endif
+#if ATK_CHECK_VERSION(2, 15, 2)
+ return ATK_ROLE_STATIC;
+ case SVGTextPathRole:
+ case SVGTSpanRole:
+ return ATK_ROLE_STATIC;
+#endif
default:
return ATK_ROLE_UNKNOWN;
}
@@ -803,16 +747,18 @@ static void setAtkStateSetFromCoreObject(AccessibilityObject* coreObject, AtkSta
if (isListBoxOption && coreObject->isSelectedOptionActive())
atk_state_set_add_state(stateSet, ATK_STATE_ACTIVE);
+ if (coreObject->isBusy())
+ atk_state_set_add_state(stateSet, ATK_STATE_BUSY);
+
+#if ATK_CHECK_VERSION(2,11,2)
+ if (coreObject->supportsChecked() && coreObject->canSetValueAttribute())
+ atk_state_set_add_state(stateSet, ATK_STATE_CHECKABLE);
+#endif
+
if (coreObject->isChecked())
atk_state_set_add_state(stateSet, ATK_STATE_CHECKED);
- // FIXME: isReadOnly does not seem to do the right thing for
- // controls, so check explicitly for them. In addition, because
- // isReadOnly is false for listBoxOptions, we need to add one
- // more check so that we do not present them as being "editable".
- if ((!coreObject->isReadOnly()
- || (coreObject->isControl() && coreObject->canSetValueAttribute()))
- && !isListBoxOption)
+ if ((coreObject->isTextControl() || coreObject->isNonNativeTextControl()) && coreObject->canSetValueAttribute())
atk_state_set_add_state(stateSet, ATK_STATE_EDITABLE);
// FIXME: Put both ENABLED and SENSITIVE together here for now
@@ -857,6 +803,11 @@ static void setAtkStateSetFromCoreObject(AccessibilityObject* coreObject, AtkSta
if (coreObject->isPressed())
atk_state_set_add_state(stateSet, ATK_STATE_PRESSED);
+#if ATK_CHECK_VERSION(2,15,3)
+ if (!coreObject->canSetValueAttribute() && (coreObject->supportsARIAReadOnly()))
+ atk_state_set_add_state(stateSet, ATK_STATE_READ_ONLY);
+#endif
+
if (coreObject->isRequired())
atk_state_set_add_state(stateSet, ATK_STATE_REQUIRED);
@@ -890,10 +841,10 @@ static void setAtkStateSetFromCoreObject(AccessibilityObject* coreObject, AtkSta
}
// Mutually exclusive, so we group these two
- if (coreObject->roleValue() == TextFieldRole)
- atk_state_set_add_state(stateSet, ATK_STATE_SINGLE_LINE);
- else if (coreObject->roleValue() == TextAreaRole)
+ if (coreObject->roleValue() == TextAreaRole || coreObject->ariaIsMultiline())
atk_state_set_add_state(stateSet, ATK_STATE_MULTI_LINE);
+ else if (coreObject->roleValue() == TextFieldRole || coreObject->roleValue() == SearchFieldRole)
+ atk_state_set_add_state(stateSet, ATK_STATE_SINGLE_LINE);
// TODO: ATK_STATE_SENSITIVE
@@ -965,7 +916,7 @@ static const gchar* webkitAccessibleGetObjectLocale(AtkObject* object)
return cacheAndReturnAtkProperty(object, AtkCachedDocumentLocale, language);
} else if (ATK_IS_TEXT(object)) {
- const gchar* locale = 0;
+ const gchar* locale = nullptr;
AtkAttributeSet* textAttributes = atk_text_get_default_attributes(ATK_TEXT(object));
for (AtkAttributeSet* attributes = textAttributes; attributes; attributes = attributes->next) {
@@ -1046,6 +997,9 @@ static const GInterfaceInfo AtkInterfacesInitFunctions[] = {
{reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleComponentInterfaceInit), 0, 0},
{reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleImageInterfaceInit), 0, 0},
{reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleTableInterfaceInit), 0, 0},
+#if ATK_CHECK_VERSION(2,11,90)
+ {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleTableCellInterfaceInit), 0, 0},
+#endif
{reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleHypertextInterfaceInit), 0, 0},
{reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleHyperlinkImplInterfaceInit), 0, 0},
{reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleDocumentInterfaceInit), 0, 0},
@@ -1053,43 +1007,50 @@ static const GInterfaceInfo AtkInterfacesInitFunctions[] = {
};
enum WAIType {
- WAI_ACTION,
- WAI_SELECTION,
- WAI_EDITABLE_TEXT,
- WAI_TEXT,
- WAI_COMPONENT,
- WAI_IMAGE,
- WAI_TABLE,
- WAI_HYPERTEXT,
- WAI_HYPERLINK,
- WAI_DOCUMENT,
- WAI_VALUE,
+ WAIAction,
+ WAISelection,
+ WAIEditableText,
+ WAIText,
+ WAIComponent,
+ WAIImage,
+ WAITable,
+#if ATK_CHECK_VERSION(2,11,90)
+ WAITableCell,
+#endif
+ WAIHypertext,
+ WAIHyperlink,
+ WAIDocument,
+ WAIValue,
};
static GType GetAtkInterfaceTypeFromWAIType(WAIType type)
{
switch (type) {
- case WAI_ACTION:
+ case WAIAction:
return ATK_TYPE_ACTION;
- case WAI_SELECTION:
+ case WAISelection:
return ATK_TYPE_SELECTION;
- case WAI_EDITABLE_TEXT:
+ case WAIEditableText:
return ATK_TYPE_EDITABLE_TEXT;
- case WAI_TEXT:
+ case WAIText:
return ATK_TYPE_TEXT;
- case WAI_COMPONENT:
+ case WAIComponent:
return ATK_TYPE_COMPONENT;
- case WAI_IMAGE:
+ case WAIImage:
return ATK_TYPE_IMAGE;
- case WAI_TABLE:
+ case WAITable:
return ATK_TYPE_TABLE;
- case WAI_HYPERTEXT:
+#if ATK_CHECK_VERSION(2,11,90)
+ case WAITableCell:
+ return ATK_TYPE_TABLE_CELL;
+#endif
+ case WAIHypertext:
return ATK_TYPE_HYPERTEXT;
- case WAI_HYPERLINK:
+ case WAIHyperlink:
return ATK_TYPE_HYPERLINK_IMPL;
- case WAI_DOCUMENT:
+ case WAIDocument:
return ATK_TYPE_DOCUMENT;
- case WAI_VALUE:
+ case WAIValue:
return ATK_TYPE_VALUE;
}
@@ -1099,7 +1060,8 @@ static GType GetAtkInterfaceTypeFromWAIType(WAIType type)
static bool roleIsTextType(AccessibilityRole role)
{
return role == ParagraphRole || role == HeadingRole || role == DivRole || role == CellRole
- || role == LinkRole || role == WebCoreLinkRole || role == ListItemRole;
+ || role == LinkRole || role == WebCoreLinkRole || role == ListItemRole || role == PreRole
+ || role == GridCellRole;
}
static guint16 getInterfaceMaskFromObject(AccessibilityObject* coreObject)
@@ -1107,7 +1069,7 @@ static guint16 getInterfaceMaskFromObject(AccessibilityObject* coreObject)
guint16 interfaceMask = 0;
// Component interface is always supported
- interfaceMask |= 1 << WAI_COMPONENT;
+ interfaceMask |= 1 << WAIComponent;
AccessibilityRole role = coreObject->roleValue();
@@ -1117,68 +1079,71 @@ static guint16 getInterfaceMaskFromObject(AccessibilityObject* coreObject)
// object, and only supports having one action per object), it is
// better just to implement this interface for every instance of
// the WebKitAccessible class and let WebCore decide what to do.
- interfaceMask |= 1 << WAI_ACTION;
+ interfaceMask |= 1 << WAIAction;
// Selection
- if (coreObject->isListBox() || coreObject->isMenuList())
- interfaceMask |= 1 << WAI_SELECTION;
+ if (coreObject->canHaveSelectedChildren() || coreObject->isMenuList())
+ interfaceMask |= 1 << WAISelection;
// Get renderer if available.
- RenderObject* renderer = 0;
+ RenderObject* renderer = nullptr;
if (coreObject->isAccessibilityRenderObject())
renderer = coreObject->renderer();
// Hyperlink (links and embedded objects).
if (coreObject->isLink() || (renderer && renderer->isReplaced()))
- interfaceMask |= 1 << WAI_HYPERLINK;
+ interfaceMask |= 1 << WAIHyperlink;
- // Text & Editable Text
+ // Text, Editable Text & Hypertext
if (role == StaticTextRole || coreObject->isMenuListOption())
- interfaceMask |= 1 << WAI_TEXT;
- else {
- if (coreObject->isTextControl()) {
- interfaceMask |= 1 << WAI_TEXT;
- if (!coreObject->isReadOnly())
- interfaceMask |= 1 << WAI_EDITABLE_TEXT;
- } else {
- if (role != TableRole) {
- interfaceMask |= 1 << WAI_HYPERTEXT;
- if ((renderer && renderer->childrenInline()) || roleIsTextType(role))
- interfaceMask |= 1 << WAI_TEXT;
- }
+ interfaceMask |= 1 << WAIText;
+ else if (coreObject->isTextControl() || coreObject->isNonNativeTextControl()) {
+ interfaceMask |= 1 << WAIText;
+ if (coreObject->canSetValueAttribute())
+ interfaceMask |= 1 << WAIEditableText;
+ } else if (!coreObject->isWebArea()) {
+ if (role != TableRole) {
+ interfaceMask |= 1 << WAIHypertext;
+ if ((renderer && renderer->childrenInline()) || roleIsTextType(role) || coreObject->isMathToken())
+ interfaceMask |= 1 << WAIText;
+ }
- // Add the TEXT interface for list items whose
- // first accessible child has a text renderer
- if (role == ListItemRole) {
- const AccessibilityObject::AccessibilityChildrenVector& children = coreObject->children();
- if (children.size()) {
- AccessibilityObject* axRenderChild = children.at(0).get();
- interfaceMask |= getInterfaceMaskFromObject(axRenderChild);
- }
+ // Add the TEXT interface for list items whose
+ // first accessible child has a text renderer
+ if (role == ListItemRole) {
+ const AccessibilityObject::AccessibilityChildrenVector& children = coreObject->children();
+ if (children.size()) {
+ AccessibilityObject* axRenderChild = children.at(0).get();
+ interfaceMask |= getInterfaceMaskFromObject(axRenderChild);
}
}
}
// Image
if (coreObject->isImage())
- interfaceMask |= 1 << WAI_IMAGE;
+ interfaceMask |= 1 << WAIImage;
// Table
- if (role == TableRole)
- interfaceMask |= 1 << WAI_TABLE;
+ if (role == TableRole || role == GridRole)
+ interfaceMask |= 1 << WAITable;
+
+#if ATK_CHECK_VERSION(2,11,90)
+ if (role == CellRole || role == ColumnHeaderRole || role == RowHeaderRole)
+ interfaceMask |= 1 << WAITableCell;
+#endif
// Document
if (role == WebAreaRole)
- interfaceMask |= 1 << WAI_DOCUMENT;
+ interfaceMask |= 1 << WAIDocument;
// Value
if (role == SliderRole || role == SpinButtonRole || role == ScrollBarRole || role == ProgressIndicatorRole)
- interfaceMask |= 1 << WAI_VALUE;
+ interfaceMask |= 1 << WAIValue;
#if ENABLE(INPUT_TYPE_COLOR)
// Color type.
if (role == ColorWellRole)
- interfaceMask |= 1 << WAI_TEXT;
+ interfaceMask |= 1 << WAIText;
#endif
return interfaceMask;
@@ -1261,18 +1226,6 @@ bool webkitAccessibleIsDetached(WebKitAccessible* accessible)
return accessible->m_object == fallbackObject();
}
-AtkObject* webkitAccessibleGetFocusedElement(WebKitAccessible* accessible)
-{
- if (!accessible->m_object)
- return 0;
-
- RefPtr<AccessibilityObject> focusedObj = accessible->m_object->focusedUIElement();
- if (!focusedObj)
- return 0;
-
- return focusedObj->wrapper();
-}
-
AccessibilityObject* objectFocusedAndCaretOffsetUnignored(AccessibilityObject* referenceObject, int& offset)
{
// Indication that something bogus has transpired.
@@ -1314,7 +1267,7 @@ AccessibilityObject* objectFocusedAndCaretOffsetUnignored(AccessibilityObject* r
if (referenceObject->isDescendantOfObject(firstUnignoredParent))
referenceObject = firstUnignoredParent;
- Node* startNode = 0;
+ Node* startNode = nullptr;
if (firstUnignoredParent != referenceObject || firstUnignoredParent->isTextControl()) {
// We need to use the first child's node of the reference
// object as the start point to calculate the caret offset
@@ -1356,7 +1309,7 @@ AccessibilityObject* objectFocusedAndCaretOffsetUnignored(AccessibilityObject* r
const char* cacheAndReturnAtkProperty(AtkObject* object, AtkCachedProperty property, String value)
{
WebKitAccessiblePrivate* priv = WEBKIT_ACCESSIBLE(object)->priv;
- CString* propertyPtr = 0;
+ CString* propertyPtr = nullptr;
switch (property) {
case AtkCachedAccessibleName:
diff --git a/Source/WebCore/accessibility/atk/WebKitAccessibleWrapperAtk.h b/Source/WebCore/accessibility/atk/WebKitAccessibleWrapperAtk.h
index 0d473019d..7dfebfb7f 100644
--- a/Source/WebCore/accessibility/atk/WebKitAccessibleWrapperAtk.h
+++ b/Source/WebCore/accessibility/atk/WebKitAccessibleWrapperAtk.h
@@ -20,8 +20,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef WebKitAccessibleWrapperAtk_h
-#define WebKitAccessibleWrapperAtk_h
+#pragma once
#if HAVE(ACCESSIBILITY)
@@ -79,13 +78,10 @@ void webkitAccessibleDetach(WebKitAccessible*);
bool webkitAccessibleIsDetached(WebKitAccessible*);
-AtkObject* webkitAccessibleGetFocusedElement(WebKitAccessible*);
-
WebCore::AccessibilityObject* objectFocusedAndCaretOffsetUnignored(WebCore::AccessibilityObject*, int& offset);
const char* cacheAndReturnAtkProperty(AtkObject*, AtkCachedProperty, String value);
G_END_DECLS
-#endif
-#endif // WebKitAccessibleWrapperAtk_h
+#endif // HAVE(ACCESSIBILITY)