AccessibilityObject.cpp [plain text]
#include "config.h"
#include "AccessibilityObject.h"
#include "AXObjectCache.h"
#include "AccessibilityRenderObject.h"
#include "FloatRect.h"
#include "FocusController.h"
#include "Frame.h"
#include "FrameLoader.h"
#include "FrameSelection.h"
#include "HTMLNames.h"
#include "LocalizedStrings.h"
#include "NodeList.h"
#include "NotImplemented.h"
#include "Page.h"
#include "RenderImage.h"
#include "RenderListItem.h"
#include "RenderListMarker.h"
#include "RenderMenuList.h"
#include "RenderTextControl.h"
#include "RenderTheme.h"
#include "RenderView.h"
#include "RenderWidget.h"
#include "TextIterator.h"
#include "htmlediting.h"
#include "visible_units.h"
#include <wtf/StdLibExtras.h>
#include <wtf/text/StringBuilder.h>
#include <wtf/text/WTFString.h>
#include <wtf/unicode/CharacterNames.h>
using namespace std;
namespace WebCore {
using namespace HTMLNames;
AccessibilityObject::AccessibilityObject()
: m_id(0)
, m_haveChildren(false)
, m_role(UnknownRole)
#if PLATFORM(GTK)
, m_wrapper(0)
#endif
{
}
AccessibilityObject::~AccessibilityObject()
{
ASSERT(isDetached());
}
void AccessibilityObject::detach()
{
#if HAVE(ACCESSIBILITY)
setWrapper(0);
#endif
}
AccessibilityObject* AccessibilityObject::parentObjectUnignored() const
{
AccessibilityObject* parent;
for (parent = parentObject(); parent && parent->accessibilityIsIgnored(); parent = parent->parentObject()) {
}
return parent;
}
AccessibilityObject* AccessibilityObject::firstAccessibleObjectFromNode(const Node* node)
{
ASSERT(AXObjectCache::accessibilityEnabled());
if (!node)
return 0;
Document* document = node->document();
if (!document)
return 0;
AXObjectCache* cache = document->axObjectCache();
AccessibilityObject* accessibleObject = cache->getOrCreate(node->renderer());
while (accessibleObject && accessibleObject->accessibilityIsIgnored()) {
node = node->traverseNextNode();
while (node && !node->renderer())
node = node->traverseNextSibling();
if (!node)
return 0;
accessibleObject = cache->getOrCreate(node->renderer());
}
return accessibleObject;
}
bool AccessibilityObject::isARIAInput(AccessibilityRole ariaRole)
{
return ariaRole == RadioButtonRole || ariaRole == CheckBoxRole || ariaRole == TextFieldRole;
}
bool AccessibilityObject::isARIAControl(AccessibilityRole ariaRole)
{
return isARIAInput(ariaRole) || ariaRole == TextAreaRole || ariaRole == ButtonRole
|| ariaRole == ComboBoxRole || ariaRole == SliderRole;
}
IntPoint AccessibilityObject::clickPoint() const
{
IntRect rect = elementRect();
return IntPoint(rect.x() + rect.width() / 2, rect.y() + rect.height() / 2);
}
bool AccessibilityObject::press() const
{
Element* actionElem = actionElement();
if (!actionElem)
return false;
if (Frame* f = actionElem->document()->frame())
f->loader()->resetMultipleFormSubmissionProtection();
actionElem->accessKeyAction(true);
return true;
}
String AccessibilityObject::language() const
{
const AtomicString& lang = getAttribute(langAttr);
if (!lang.isEmpty())
return lang;
AccessibilityObject* parent = parentObject();
if (!parent) {
Document* doc = document();
if (doc)
return doc->contentLanguage();
return nullAtom;
}
return parent->language();
}
VisiblePositionRange AccessibilityObject::visiblePositionRangeForUnorderedPositions(const VisiblePosition& visiblePos1, const VisiblePosition& visiblePos2) const
{
if (visiblePos1.isNull() || visiblePos2.isNull())
return VisiblePositionRange();
VisiblePosition startPos;
VisiblePosition endPos;
bool alreadyInOrder;
if (visiblePos1 == visiblePos2 && visiblePos2.affinity() == UPSTREAM)
alreadyInOrder = false;
else
alreadyInOrder = VisibleSelection(visiblePos1, visiblePos2).isBaseFirst();
if (alreadyInOrder) {
startPos = visiblePos1;
endPos = visiblePos2;
} else {
startPos = visiblePos2;
endPos = visiblePos1;
}
return VisiblePositionRange(startPos, endPos);
}
VisiblePositionRange AccessibilityObject::positionOfLeftWord(const VisiblePosition& visiblePos) const
{
VisiblePosition startPosition = startOfWord(visiblePos, LeftWordIfOnBoundary);
VisiblePosition endPosition = endOfWord(startPosition);
return VisiblePositionRange(startPosition, endPosition);
}
VisiblePositionRange AccessibilityObject::positionOfRightWord(const VisiblePosition& visiblePos) const
{
VisiblePosition startPosition = startOfWord(visiblePos, RightWordIfOnBoundary);
VisiblePosition endPosition = endOfWord(startPosition);
return VisiblePositionRange(startPosition, endPosition);
}
static VisiblePosition updateAXLineStartForVisiblePosition(const VisiblePosition& visiblePosition)
{
VisiblePosition tempPosition;
VisiblePosition startPosition = visiblePosition;
Position p;
RenderObject* renderer;
while (true) {
tempPosition = startPosition.previous();
if (tempPosition.isNull())
break;
p = tempPosition.deepEquivalent();
if (!p.deprecatedNode())
break;
renderer = p.deprecatedNode()->renderer();
if (!renderer || (renderer->isRenderBlock() && !p.deprecatedEditingOffset()))
break;
InlineBox* box;
int ignoredCaretOffset;
p.getInlineBoxAndOffset(tempPosition.affinity(), box, ignoredCaretOffset);
if (box)
break;
startPosition = tempPosition;
}
return startPosition;
}
VisiblePositionRange AccessibilityObject::leftLineVisiblePositionRange(const VisiblePosition& visiblePos) const
{
if (visiblePos.isNull())
return VisiblePositionRange();
VisiblePosition prevVisiblePos = visiblePos.previous();
if (prevVisiblePos.isNull())
return VisiblePositionRange();
VisiblePosition startPosition = startOfLine(prevVisiblePos);
if (startPosition.isNull()) {
while (startPosition.isNull() && prevVisiblePos.isNotNull()) {
prevVisiblePos = prevVisiblePos.previous();
startPosition = startOfLine(prevVisiblePos);
}
} else
startPosition = updateAXLineStartForVisiblePosition(startPosition);
VisiblePosition endPosition = endOfLine(prevVisiblePos);
return VisiblePositionRange(startPosition, endPosition);
}
VisiblePositionRange AccessibilityObject::rightLineVisiblePositionRange(const VisiblePosition& visiblePos) const
{
if (visiblePos.isNull())
return VisiblePositionRange();
VisiblePosition nextVisiblePos = visiblePos.next();
if (nextVisiblePos.isNull())
return VisiblePositionRange();
VisiblePosition startPosition = startOfLine(nextVisiblePos);
if (startPosition.isNull()) {
startPosition = visiblePos;
nextVisiblePos = nextVisiblePos.next();
} else
startPosition = updateAXLineStartForVisiblePosition(startPosition);
VisiblePosition endPosition = endOfLine(nextVisiblePos);
while (endPosition.isNull() && nextVisiblePos.isNotNull()) {
nextVisiblePos = nextVisiblePos.next();
endPosition = endOfLine(nextVisiblePos);
}
return VisiblePositionRange(startPosition, endPosition);
}
VisiblePositionRange AccessibilityObject::sentenceForPosition(const VisiblePosition& visiblePos) const
{
VisiblePosition startPosition = startOfSentence(visiblePos);
VisiblePosition endPosition = endOfSentence(startPosition);
return VisiblePositionRange(startPosition, endPosition);
}
VisiblePositionRange AccessibilityObject::paragraphForPosition(const VisiblePosition& visiblePos) const
{
VisiblePosition startPosition = startOfParagraph(visiblePos);
VisiblePosition endPosition = endOfParagraph(startPosition);
return VisiblePositionRange(startPosition, endPosition);
}
static VisiblePosition startOfStyleRange(const VisiblePosition visiblePos)
{
RenderObject* renderer = visiblePos.deepEquivalent().deprecatedNode()->renderer();
RenderObject* startRenderer = renderer;
RenderStyle* style = renderer->style();
for (RenderObject* r = renderer->previousInPreOrder(); r; r = r->previousInPreOrder()) {
if (r->firstChild())
continue;
if (r->style() != style)
break;
startRenderer = r;
}
return firstPositionInOrBeforeNode(startRenderer->node());
}
static VisiblePosition endOfStyleRange(const VisiblePosition& visiblePos)
{
RenderObject* renderer = visiblePos.deepEquivalent().deprecatedNode()->renderer();
RenderObject* endRenderer = renderer;
RenderStyle* style = renderer->style();
for (RenderObject* r = renderer->nextInPreOrder(); r; r = r->nextInPreOrder()) {
if (r->firstChild())
continue;
if (r->style() != style)
break;
endRenderer = r;
}
return lastPositionInOrAfterNode(endRenderer->node());
}
VisiblePositionRange AccessibilityObject::styleRangeForPosition(const VisiblePosition& visiblePos) const
{
if (visiblePos.isNull())
return VisiblePositionRange();
return VisiblePositionRange(startOfStyleRange(visiblePos), endOfStyleRange(visiblePos));
}
VisiblePositionRange AccessibilityObject::visiblePositionRangeForRange(const PlainTextRange& range) const
{
unsigned textLength = getLengthForTextRange();
if (range.start + range.length > textLength)
return VisiblePositionRange();
VisiblePosition startPosition = visiblePositionForIndex(range.start);
startPosition.setAffinity(DOWNSTREAM);
VisiblePosition endPosition = visiblePositionForIndex(range.start + range.length);
return VisiblePositionRange(startPosition, endPosition);
}
static bool replacedNodeNeedsCharacter(Node* replacedNode)
{
if (!replacedNode || !replacedNode->renderer() || !replacedNode->renderer()->isReplaced() || replacedNode->isTextNode())
return false;
AccessibilityObject* object = replacedNode->renderer()->document()->axObjectCache()->getOrCreate(replacedNode->renderer());
if (object->accessibilityIsIgnored())
return false;
return true;
}
static RenderListItem* renderListItemContainerForNode(Node* node)
{
for (; node; node = node->parentNode()) {
RenderBoxModelObject* renderer = node->renderBoxModelObject();
if (renderer && renderer->isListItem())
return toRenderListItem(renderer);
}
return 0;
}
String AccessibilityObject::listMarkerTextForNodeAndPosition(Node* node, const VisiblePosition& visiblePositionStart) const
{
if (!isStartOfLine(visiblePositionStart))
return String();
RenderListItem* listItem = renderListItemContainerForNode(node);
if (!listItem)
return String();
const String& markerText = listItem->markerText();
if (markerText.isEmpty())
return String();
return markerText + ". ";
}
String AccessibilityObject::stringForVisiblePositionRange(const VisiblePositionRange& visiblePositionRange) const
{
if (visiblePositionRange.isNull())
return String();
StringBuilder builder;
RefPtr<Range> range = makeRange(visiblePositionRange.start, visiblePositionRange.end);
for (TextIterator it(range.get()); !it.atEnd(); it.advance()) {
if (it.length()) {
String listMarkerText = listMarkerTextForNodeAndPosition(it.node(), visiblePositionRange.start);
if (!listMarkerText.isEmpty())
builder.append(listMarkerText);
builder.append(it.characters(), it.length());
} else {
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)))
builder.append(objectReplacementCharacter);
}
}
return builder.toString();
}
int AccessibilityObject::lengthForVisiblePositionRange(const VisiblePositionRange& visiblePositionRange) const
{
if (visiblePositionRange.isNull())
return -1;
int length = 0;
RefPtr<Range> range = makeRange(visiblePositionRange.start, visiblePositionRange.end);
for (TextIterator it(range.get()); !it.atEnd(); it.advance()) {
if (it.length())
length += it.length();
else {
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)))
length++;
}
}
return length;
}
VisiblePosition AccessibilityObject::nextWordEnd(const VisiblePosition& visiblePos) const
{
if (visiblePos.isNull())
return VisiblePosition();
VisiblePosition nextVisiblePos = visiblePos.next();
if (nextVisiblePos.isNull())
return VisiblePosition();
return endOfWord(nextVisiblePos, LeftWordIfOnBoundary);
}
VisiblePosition AccessibilityObject::previousWordStart(const VisiblePosition& visiblePos) const
{
if (visiblePos.isNull())
return VisiblePosition();
VisiblePosition prevVisiblePos = visiblePos.previous();
if (prevVisiblePos.isNull())
return VisiblePosition();
return startOfWord(prevVisiblePos, RightWordIfOnBoundary);
}
VisiblePosition AccessibilityObject::nextLineEndPosition(const VisiblePosition& visiblePos) const
{
if (visiblePos.isNull())
return VisiblePosition();
VisiblePosition nextVisiblePos = visiblePos.next();
if (nextVisiblePos.isNull())
return VisiblePosition();
VisiblePosition endPosition = endOfLine(nextVisiblePos);
while (endPosition.isNull() && nextVisiblePos.isNotNull()) {
nextVisiblePos = nextVisiblePos.next();
endPosition = endOfLine(nextVisiblePos);
}
return endPosition;
}
VisiblePosition AccessibilityObject::previousLineStartPosition(const VisiblePosition& visiblePos) const
{
if (visiblePos.isNull())
return VisiblePosition();
VisiblePosition prevVisiblePos = visiblePos.previous();
if (prevVisiblePos.isNull())
return VisiblePosition();
VisiblePosition startPosition = startOfLine(prevVisiblePos);
if (startPosition.isNull()) {
while (startPosition.isNull() && prevVisiblePos.isNotNull()) {
prevVisiblePos = prevVisiblePos.previous();
startPosition = startOfLine(prevVisiblePos);
}
} else
startPosition = updateAXLineStartForVisiblePosition(startPosition);
return startPosition;
}
VisiblePosition AccessibilityObject::nextSentenceEndPosition(const VisiblePosition& visiblePos) const
{
if (visiblePos.isNull())
return VisiblePosition();
VisiblePosition nextVisiblePos = visiblePos.next();
if (nextVisiblePos.isNull())
return VisiblePosition();
VisiblePosition endPosition;
String lineString = plainText(makeRange(startOfLine(nextVisiblePos), endOfLine(nextVisiblePos)).get());
if (lineString.isEmpty())
endPosition = nextVisiblePos;
else
endPosition = endOfSentence(nextVisiblePos);
return endPosition;
}
VisiblePosition AccessibilityObject::previousSentenceStartPosition(const VisiblePosition& visiblePos) const
{
if (visiblePos.isNull())
return VisiblePosition();
VisiblePosition previousVisiblePos = visiblePos.previous();
if (previousVisiblePos.isNull())
return VisiblePosition();
VisiblePosition startPosition;
String lineString = plainText(makeRange(startOfLine(previousVisiblePos), endOfLine(previousVisiblePos)).get());
if (lineString.isEmpty())
startPosition = previousVisiblePos;
else
startPosition = startOfSentence(previousVisiblePos);
return startPosition;
}
VisiblePosition AccessibilityObject::nextParagraphEndPosition(const VisiblePosition& visiblePos) const
{
if (visiblePos.isNull())
return VisiblePosition();
VisiblePosition nextPos = visiblePos.next();
if (nextPos.isNull())
return VisiblePosition();
return endOfParagraph(nextPos);
}
VisiblePosition AccessibilityObject::previousParagraphStartPosition(const VisiblePosition& visiblePos) const
{
if (visiblePos.isNull())
return VisiblePosition();
VisiblePosition previousPos = visiblePos.previous();
if (previousPos.isNull())
return VisiblePosition();
return startOfParagraph(previousPos);
}
AccessibilityObject* AccessibilityObject::accessibilityObjectForPosition(const VisiblePosition& visiblePos) const
{
if (visiblePos.isNull())
return 0;
RenderObject* obj = visiblePos.deepEquivalent().deprecatedNode()->renderer();
if (!obj)
return 0;
return obj->document()->axObjectCache()->getOrCreate(obj);
}
int AccessibilityObject::lineForPosition(const VisiblePosition& visiblePos) const
{
if (visiblePos.isNull())
return 0;
unsigned lineCount = 0;
VisiblePosition currentVisiblePos = visiblePos;
VisiblePosition savedVisiblePos;
while (currentVisiblePos.isNotNull() && !(inSameLine(currentVisiblePos, savedVisiblePos))) {
++lineCount;
savedVisiblePos = currentVisiblePos;
VisiblePosition prevVisiblePos = previousLinePosition(currentVisiblePos, 0);
currentVisiblePos = prevVisiblePos;
}
return lineCount - 1;
}
PlainTextRange AccessibilityObject::plainTextRangeForVisiblePositionRange(const VisiblePositionRange& positionRange) const
{
int index1 = index(positionRange.start);
int index2 = index(positionRange.end);
if (index1 < 0 || index2 < 0 || index1 > index2)
return PlainTextRange();
return PlainTextRange(index1, index2 - index1);
}
PlainTextRange AccessibilityObject::doAXRangeForPosition(const IntPoint& point) const
{
int i = index(visiblePositionForPoint(point));
if (i < 0)
return PlainTextRange();
return PlainTextRange(i, 1);
}
PlainTextRange AccessibilityObject::doAXStyleRangeForIndex(unsigned index) const
{
VisiblePositionRange range = styleRangeForPosition(visiblePositionForIndex(index, false));
return plainTextRangeForVisiblePositionRange(range);
}
unsigned AccessibilityObject::doAXLineForIndex(unsigned index)
{
return lineForPosition(visiblePositionForIndex(index, false));
}
void AccessibilityObject::updateBackingStore()
{
if (Document* document = this->document())
document->updateLayoutIgnorePendingStylesheets();
}
Document* AccessibilityObject::document() const
{
FrameView* frameView = documentFrameView();
if (!frameView)
return 0;
return frameView->frame()->document();
}
FrameView* AccessibilityObject::documentFrameView() const
{
const AccessibilityObject* object = this;
while (object && !object->isAccessibilityRenderObject())
object = object->parentObject();
if (!object)
return 0;
return object->documentFrameView();
}
void AccessibilityObject::updateChildrenIfNecessary()
{
if (!hasChildren())
addChildren();
}
void AccessibilityObject::clearChildren()
{
size_t length = m_children.size();
for (size_t i = 0; i < length; i++)
m_children[i]->detachFromParent();
m_children.clear();
m_haveChildren = false;
}
AccessibilityObject* AccessibilityObject::anchorElementForNode(Node* node)
{
RenderObject* obj = node->renderer();
if (!obj)
return 0;
RefPtr<AccessibilityObject> axObj = obj->document()->axObjectCache()->getOrCreate(obj);
Element* anchor = axObj->anchorElement();
if (!anchor)
return 0;
RenderObject* anchorRenderer = anchor->renderer();
if (!anchorRenderer)
return 0;
return anchorRenderer->document()->axObjectCache()->getOrCreate(anchorRenderer);
}
void AccessibilityObject::ariaTreeRows(AccessibilityChildrenVector& result)
{
AccessibilityChildrenVector axChildren = children();
unsigned count = axChildren.size();
for (unsigned k = 0; k < count; ++k) {
AccessibilityObject* obj = axChildren[k].get();
if (obj->roleValue() == TreeItemRole)
result.append(obj);
obj->ariaTreeRows(result);
}
}
void AccessibilityObject::ariaTreeItemContent(AccessibilityChildrenVector& result)
{
AccessibilityChildrenVector axChildren = children();
unsigned count = axChildren.size();
for (unsigned k = 0; k < count; ++k) {
AccessibilityObject* obj = axChildren[k].get();
AccessibilityRole role = obj->roleValue();
if (role == TreeItemRole || role == GroupRole)
continue;
result.append(obj);
}
}
void AccessibilityObject::ariaTreeItemDisclosedRows(AccessibilityChildrenVector& result)
{
AccessibilityChildrenVector axChildren = children();
unsigned count = axChildren.size();
for (unsigned k = 0; k < count; ++k) {
AccessibilityObject* obj = axChildren[k].get();
if (obj->roleValue() == TreeItemRole)
result.append(obj);
else
obj->ariaTreeRows(result);
}
}
const String& AccessibilityObject::actionVerb() const
{
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, noAction, ());
switch (roleValue()) {
case ButtonRole:
return buttonAction;
case TextFieldRole:
case TextAreaRole:
return textFieldAction;
case RadioButtonRole:
return radioButtonAction;
case CheckBoxRole:
return isChecked() ? checkedCheckBoxAction : uncheckedCheckBoxAction;
case LinkRole:
case WebCoreLinkRole:
return linkAction;
case PopUpButtonRole:
return menuListAction;
case MenuListPopupRole:
return menuListPopupAction;
default:
return noAction;
}
}
bool AccessibilityObject::ariaIsMultiline() const
{
return equalIgnoringCase(getAttribute(aria_multilineAttr), "true");
}
const AtomicString& AccessibilityObject::invalidStatus() const
{
DEFINE_STATIC_LOCAL(const AtomicString, invalidStatusFalse, ("false"));
const AtomicString& ariaInvalid = getAttribute(aria_invalidAttr);
if (ariaInvalid.isEmpty())
return invalidStatusFalse;
return ariaInvalid;
}
const AtomicString& AccessibilityObject::getAttribute(const QualifiedName& attribute) const
{
Node* elementNode = node();
if (!elementNode)
return nullAtom;
if (!elementNode->isElementNode())
return nullAtom;
Element* element = static_cast<Element*>(elementNode);
return element->fastGetAttribute(attribute);
}
AccessibilityOrientation AccessibilityObject::orientation() const
{
IntRect bounds = elementRect();
if (bounds.size().width() > bounds.size().height())
return AccessibilityOrientationHorizontal;
if (bounds.size().height() > bounds.size().width())
return AccessibilityOrientationVertical;
return AccessibilityOrientationHorizontal;
}
typedef HashMap<String, AccessibilityRole, CaseFoldingHash> ARIARoleMap;
struct RoleEntry {
String ariaRole;
AccessibilityRole webcoreRole;
};
static ARIARoleMap* createARIARoleMap()
{
const RoleEntry roles[] = {
{ "alert", ApplicationAlertRole },
{ "alertdialog", ApplicationAlertDialogRole },
{ "application", LandmarkApplicationRole },
{ "article", DocumentArticleRole },
{ "banner", LandmarkBannerRole },
{ "button", ButtonRole },
{ "checkbox", CheckBoxRole },
{ "complementary", LandmarkComplementaryRole },
{ "contentinfo", LandmarkContentInfoRole },
{ "dialog", ApplicationDialogRole },
{ "directory", DirectoryRole },
{ "grid", TableRole },
{ "gridcell", CellRole },
{ "columnheader", ColumnHeaderRole },
{ "combobox", ComboBoxRole },
{ "definition", DefinitionListDefinitionRole },
{ "document", DocumentRole },
{ "rowheader", RowHeaderRole },
{ "group", GroupRole },
{ "heading", HeadingRole },
{ "img", ImageRole },
{ "link", WebCoreLinkRole },
{ "list", ListRole },
{ "listitem", ListItemRole },
{ "listbox", ListBoxRole },
{ "log", ApplicationLogRole },
{ "main", LandmarkMainRole },
{ "marquee", ApplicationMarqueeRole },
{ "math", DocumentMathRole },
{ "menu", MenuRole },
{ "menubar", MenuBarRole },
{ "menuitemcheckbox", MenuItemRole },
{ "menuitemradio", MenuItemRole },
{ "note", DocumentNoteRole },
{ "navigation", LandmarkNavigationRole },
{ "option", ListBoxOptionRole },
{ "presentation", PresentationalRole },
{ "progressbar", ProgressIndicatorRole },
{ "radio", RadioButtonRole },
{ "radiogroup", RadioGroupRole },
{ "region", DocumentRegionRole },
{ "row", RowRole },
{ "range", SliderRole },
{ "scrollbar", ScrollBarRole },
{ "search", LandmarkSearchRole },
{ "separator", SplitterRole },
{ "slider", SliderRole },
{ "spinbutton", ProgressIndicatorRole },
{ "status", ApplicationStatusRole },
{ "tab", TabRole },
{ "tablist", TabListRole },
{ "tabpanel", TabPanelRole },
{ "text", StaticTextRole },
{ "textbox", TextAreaRole },
{ "timer", ApplicationTimerRole },
{ "toolbar", ToolbarRole },
{ "tooltip", UserInterfaceTooltipRole },
{ "tree", TreeRole },
{ "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;
}
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);
if (role)
return role;
}
return role;
}
const AtomicString& AccessibilityObject::placeholderValue() const
{
const AtomicString& placeholder = getAttribute(placeholderAttr);
if (!placeholder.isEmpty())
return placeholder;
return nullAtom;
}
bool AccessibilityObject::isInsideARIALiveRegion() const
{
if (supportsARIALiveRegion())
return true;
for (AccessibilityObject* axParent = parentObject(); axParent; axParent = axParent->parentObject()) {
if (axParent->supportsARIALiveRegion())
return true;
}
return false;
}
bool AccessibilityObject::supportsARIAAttributes() const
{
return supportsARIALiveRegion() || supportsARIADragging() || supportsARIADropping() || supportsARIAFlowTo() || supportsARIAOwns();
}
bool AccessibilityObject::supportsARIALiveRegion() const
{
const AtomicString& liveRegion = ariaLiveRegionStatus();
return equalIgnoringCase(liveRegion, "polite") || equalIgnoringCase(liveRegion, "assertive");
}
AccessibilityObject* AccessibilityObject::elementAccessibilityHitTest(const IntPoint& point) const
{
if (isAttachment()) {
Widget* widget = widgetForAttachmentView();
if (widget && widget->isFrameView())
return axObjectCache()->getOrCreate(widget)->accessibilityHitTest(IntPoint(point - widget->frameRect().location()));
}
return const_cast<AccessibilityObject*>(this);
}
AXObjectCache* AccessibilityObject::axObjectCache() const
{
Document* doc = document();
if (doc)
return doc->axObjectCache();
return 0;
}
AccessibilityObject* AccessibilityObject::focusedUIElement() const
{
Document* doc = document();
if (!doc)
return 0;
Page* page = doc->page();
if (!page)
return 0;
return AXObjectCache::focusedUIElementForPage(page);
}
AccessibilitySortDirection AccessibilityObject::sortDirection() const
{
const AtomicString& sortAttribute = getAttribute(aria_sortAttr);
if (equalIgnoringCase(sortAttribute, "ascending"))
return SortDirectionAscending;
if (equalIgnoringCase(sortAttribute, "descending"))
return SortDirectionDescending;
return SortDirectionNone;
}
bool AccessibilityObject::supportsARIAExpanded() const
{
return !getAttribute(aria_expandedAttr).isEmpty();
}
bool AccessibilityObject::isExpanded() const
{
if (equalIgnoringCase(getAttribute(aria_expandedAttr), "true"))
return true;
return false;
}
AccessibilityButtonState AccessibilityObject::checkboxOrRadioValue() const
{
const AtomicString& result = getAttribute(aria_checkedAttr);
if (equalIgnoringCase(result, "true"))
return ButtonStateOn;
if (equalIgnoringCase(result, "mixed"))
return ButtonStateMixed;
return ButtonStateOff;
}
}