AX: Defer attribute computation until needed.
[WebKit-https.git] / Source / WebCore / accessibility / AccessibilityNodeObject.cpp
index b28f646..b072efd 100644 (file)
@@ -35,6 +35,7 @@
 #include "AccessibilityListBox.h"
 #include "AccessibilitySpinButton.h"
 #include "AccessibilityTable.h"
+#include "AccessibleNode.h"
 #include "Editing.h"
 #include "ElementIterator.h"
 #include "EventNames.h"
@@ -83,11 +84,6 @@ static String accessibleNameForNode(Node* node, Node* labelledbyNode = nullptr);
 
 AccessibilityNodeObject::AccessibilityNodeObject(Node* node)
     : AccessibilityObject()
-    , m_ariaRole(UnknownRole)
-    , m_roleForMSAA(UnknownRole)
-#ifndef NDEBUG
-    , m_initialized(false)
-#endif
     , m_node(node)
 {
 }
@@ -149,7 +145,7 @@ void AccessibilityNodeObject::childrenChanged()
         // 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())
+        if (parent->supportsLiveRegion())
             cache->postLiveRegionChangeNotification(parent);
         
         // If this element is an ARIA text control, notify the AT of changes.
@@ -282,57 +278,57 @@ Document* AccessibilityNodeObject::document() const
 AccessibilityRole AccessibilityNodeObject::determineAccessibilityRole()
 {
     if (!node())
-        return UnknownRole;
+        return AccessibilityRole::Unknown;
 
-    if ((m_ariaRole = determineAriaRoleAttribute()) != UnknownRole)
+    if ((m_ariaRole = determineAriaRoleAttribute()) != AccessibilityRole::Unknown)
         return m_ariaRole;
     
     if (node()->isLink())
-        return WebCoreLinkRole;
+        return AccessibilityRole::WebCoreLink;
     if (node()->isTextNode())
-        return StaticTextRole;
+        return AccessibilityRole::StaticText;
     if (node()->hasTagName(buttonTag))
         return buttonRoleType();
     if (is<HTMLInputElement>(*node())) {
         HTMLInputElement& input = downcast<HTMLInputElement>(*node());
         if (input.isCheckbox())
-            return CheckBoxRole;
+            return AccessibilityRole::CheckBox;
         if (input.isRadioButton())
-            return RadioButtonRole;
+            return AccessibilityRole::RadioButton;
         if (input.isTextButton())
             return buttonRoleType();
         if (input.isRangeControl())
-            return SliderRole;
+            return AccessibilityRole::Slider;
         if (input.isInputTypeHidden())
-            return IgnoredRole;
+            return AccessibilityRole::Ignored;
         if (input.isSearchField())
-            return SearchFieldRole;
+            return AccessibilityRole::SearchField;
 #if ENABLE(INPUT_TYPE_COLOR)
         if (input.isColorControl())
-            return ColorWellRole;
+            return AccessibilityRole::ColorWell;
 #endif
-        return TextFieldRole;
+        return AccessibilityRole::TextField;
     }
     if (node()->hasTagName(selectTag)) {
         HTMLSelectElement& selectElement = downcast<HTMLSelectElement>(*node());
-        return selectElement.multiple() ? ListBoxRole : PopUpButtonRole;
+        return selectElement.multiple() ? AccessibilityRole::ListBox : AccessibilityRole::PopUpButton;
     }
     if (is<HTMLTextAreaElement>(*node()))
-        return TextAreaRole;
+        return AccessibilityRole::TextArea;
     if (headingLevel())
-        return HeadingRole;
+        return AccessibilityRole::Heading;
     if (node()->hasTagName(blockquoteTag))
-        return BlockquoteRole;
+        return AccessibilityRole::Blockquote;
     if (node()->hasTagName(divTag))
-        return DivRole;
+        return AccessibilityRole::Div;
     if (node()->hasTagName(pTag))
-        return ParagraphRole;
+        return AccessibilityRole::Paragraph;
     if (is<HTMLLabelElement>(*node()))
-        return LabelRole;
+        return AccessibilityRole::Label;
     if (is<Element>(*node()) && downcast<Element>(*node()).isFocusable())
-        return GroupRole;
+        return AccessibilityRole::Group;
     
-    return UnknownRole;
+    return AccessibilityRole::Unknown;
 }
 
 void AccessibilityNodeObject::addChildren()
@@ -370,18 +366,26 @@ bool AccessibilityNodeObject::canHaveChildren() const
     
     // Elements that should not have children
     switch (roleValue()) {
-    case ImageRole:
-    case ButtonRole:
-    case PopUpButtonRole:
-    case CheckBoxRole:
-    case RadioButtonRole:
-    case TabRole:
-    case ToggleButtonRole:
-    case StaticTextRole:
-    case ListBoxOptionRole:
-    case ScrollBarRole:
-    case ProgressIndicatorRole:
-    case SwitchRole:
+    case AccessibilityRole::Image:
+    case AccessibilityRole::Button:
+    case AccessibilityRole::PopUpButton:
+    case AccessibilityRole::CheckBox:
+    case AccessibilityRole::RadioButton:
+    case AccessibilityRole::Tab:
+    case AccessibilityRole::ToggleButton:
+    case AccessibilityRole::StaticText:
+    case AccessibilityRole::ListBoxOption:
+    case AccessibilityRole::ScrollBar:
+    case AccessibilityRole::ProgressIndicator:
+    case AccessibilityRole::Switch:
+    case AccessibilityRole::MenuItemCheckbox:
+    case AccessibilityRole::MenuItemRadio:
+    case AccessibilityRole::Splitter:
+        return false;
+    case AccessibilityRole::DocumentMath:
+#if ENABLE(MATHML)
+        return node()->isMathMLElement();
+#endif
         return false;
     default:
         return true;
@@ -409,18 +413,18 @@ bool AccessibilityNodeObject::computeAccessibilityIsIgnored() const
     }
 
     AccessibilityObjectInclusion decision = defaultObjectInclusion();
-    if (decision == IncludeObject)
+    if (decision == AccessibilityObjectInclusion::IncludeObject)
         return false;
-    if (decision == IgnoreObject)
+    if (decision == AccessibilityObjectInclusion::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)
+    if (roleValue() == AccessibilityRole::Ignored)
         return true;
     
-    return m_role == UnknownRole;
+    return m_role == AccessibilityRole::Unknown;
 }
 
 bool AccessibilityNodeObject::canvasHasFallbackContent() const
@@ -463,7 +467,7 @@ bool AccessibilityNodeObject::isSearchField() const
     if (!node)
         return false;
 
-    if (roleValue() == SearchFieldRole)
+    if (roleValue() == AccessibilityRole::SearchField)
         return true;
 
     if (!is<HTMLInputElement>(*node))
@@ -477,12 +481,12 @@ bool AccessibilityNodeObject::isSearchField() const
 
     // Check the node name of the input type, sometimes it's "search".
     const AtomicString& nameAttribute = getAttribute(nameAttr);
-    if (nameAttribute.contains("search", false))
+    if (nameAttribute.containsIgnoringASCIICase("search"))
         return true;
 
     // Check the form action and the name, which will sometimes be "search".
     auto* form = inputElement.form();
-    if (form && (form->name().contains("search", false) || form->action().contains("search", false)))
+    if (form && (form->name().containsIgnoringASCIICase("search") || form->action().containsIgnoringASCIICase("search")))
         return true;
 
     return false;
@@ -510,7 +514,7 @@ bool AccessibilityNodeObject::isNativeImage() const
 
 bool AccessibilityNodeObject::isImage() const
 {
-    return roleValue() == ImageRole;
+    return roleValue() == AccessibilityRole::Image;
 }
 
 bool AccessibilityNodeObject::isPasswordField() const
@@ -519,7 +523,7 @@ bool AccessibilityNodeObject::isPasswordField() const
     if (!is<HTMLInputElement>(node))
         return false;
 
-    if (ariaRoleAttribute() != UnknownRole)
+    if (ariaRoleAttribute() != AccessibilityRole::Unknown)
         return false;
 
     return downcast<HTMLInputElement>(*node).isPasswordField();
@@ -547,7 +551,7 @@ AccessibilityObject* AccessibilityNodeObject::passwordFieldOrContainingPasswordF
 bool AccessibilityNodeObject::isInputImage() const
 {
     Node* node = this->node();
-    if (is<HTMLInputElement>(node) && roleValue() == ButtonRole) {
+    if (is<HTMLInputElement>(node) && roleValue() == AccessibilityRole::Button) {
         HTMLInputElement& input = downcast<HTMLInputElement>(*node);
         return input.isImageButton();
     }
@@ -557,23 +561,23 @@ bool AccessibilityNodeObject::isInputImage() const
 
 bool AccessibilityNodeObject::isProgressIndicator() const
 {
-    return roleValue() == ProgressIndicatorRole;
+    return roleValue() == AccessibilityRole::ProgressIndicator;
 }
 
 bool AccessibilityNodeObject::isSlider() const
 {
-    return roleValue() == SliderRole;
+    return roleValue() == AccessibilityRole::Slider;
 }
 
 bool AccessibilityNodeObject::isMenuRelated() const
 {
     switch (roleValue()) {
-    case MenuRole:
-    case MenuBarRole:
-    case MenuButtonRole:
-    case MenuItemRole:
-    case MenuItemCheckboxRole:
-    case MenuItemRadioRole:
+    case AccessibilityRole::Menu:
+    case AccessibilityRole::MenuBar:
+    case AccessibilityRole::MenuButton:
+    case AccessibilityRole::MenuItem:
+    case AccessibilityRole::MenuItemCheckbox:
+    case AccessibilityRole::MenuItemRadio:
         return true;
     default:
         return false;
@@ -582,25 +586,25 @@ bool AccessibilityNodeObject::isMenuRelated() const
 
 bool AccessibilityNodeObject::isMenu() const
 {
-    return roleValue() == MenuRole;
+    return roleValue() == AccessibilityRole::Menu;
 }
 
 bool AccessibilityNodeObject::isMenuBar() const
 {
-    return roleValue() == MenuBarRole;
+    return roleValue() == AccessibilityRole::MenuBar;
 }
 
 bool AccessibilityNodeObject::isMenuButton() const
 {
-    return roleValue() == MenuButtonRole;
+    return roleValue() == AccessibilityRole::MenuButton;
 }
 
 bool AccessibilityNodeObject::isMenuItem() const
 {
     switch (roleValue()) {
-    case MenuItemRole:
-    case MenuItemRadioRole:
-    case MenuItemCheckboxRole:
+    case AccessibilityRole::MenuItem:
+    case AccessibilityRole::MenuItemRadio:
+    case AccessibilityRole::MenuItemCheckbox:
         return true;
     default:
         return false;
@@ -621,14 +625,14 @@ 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 (equalLettersIgnoringASCIICase(disabledStatus, "true"))
-            return false;
-        if (equalLettersIgnoringASCIICase(disabledStatus, "false"))
+        if (auto disabled = object->boolValueForProperty(AXPropertyName::Disabled)) {
+            if (disabled.value())
+                return false;
             break;
+        }
     }
     
-    if (roleValue() == HorizontalRuleRole)
+    if (roleValue() == AccessibilityRole::HorizontalRule)
         return false;
     
     Node* node = this->node();
@@ -654,7 +658,7 @@ bool AccessibilityNodeObject::isPressed() const
 
     // If this is an toggle button, check the aria-pressed attribute rather than node()->active()
     if (isToggleButton())
-        return equalLettersIgnoringASCIICase(getAttribute(aria_pressedAttr), "true");
+        return equalLettersIgnoringASCIICase(stringValueForProperty(AXPropertyName::Pressed), "true");
 
     if (!is<Element>(*node))
         return false;
@@ -674,19 +678,19 @@ bool AccessibilityNodeObject::isChecked() const
     // Else, if this is an ARIA checkbox or radio, respect the aria-checked attribute
     bool validRole = false;
     switch (ariaRoleAttribute()) {
-    case RadioButtonRole:
-    case CheckBoxRole:
-    case MenuItemRole:
-    case MenuItemCheckboxRole:
-    case MenuItemRadioRole:
-    case SwitchRole:
+    case AccessibilityRole::RadioButton:
+    case AccessibilityRole::CheckBox:
+    case AccessibilityRole::MenuItem:
+    case AccessibilityRole::MenuItemCheckbox:
+    case AccessibilityRole::MenuItemRadio:
+    case AccessibilityRole::Switch:
         validRole = true;
         break;
     default:
         break;
     }
     
-    if (validRole && equalLettersIgnoringASCIICase(getAttribute(aria_checkedAttr), "true"))
+    if (validRole && equalLettersIgnoringASCIICase(stringValueForProperty(AXPropertyName::Checked), "true"))
         return true;
 
     return false;
@@ -700,11 +704,8 @@ bool AccessibilityNodeObject::isHovered() const
 
 bool AccessibilityNodeObject::isMultiSelectable() const
 {
-    const AtomicString& ariaMultiSelectable = getAttribute(aria_multiselectableAttr);
-    if (equalLettersIgnoringASCIICase(ariaMultiSelectable, "true"))
-        return true;
-    if (equalLettersIgnoringASCIICase(ariaMultiSelectable, "false"))
-        return false;
+    if (auto multiSelectable = boolValueForProperty(AXPropertyName::Multiselectable))
+        return multiSelectable.value();
     
     return node() && node()->hasTagName(selectTag) && downcast<HTMLSelectElement>(*node()).multiple();
 }
@@ -712,11 +713,8 @@ bool AccessibilityNodeObject::isMultiSelectable() const
 bool AccessibilityNodeObject::isRequired() const
 {
     // Explicit aria-required values should trump native required attributes.
-    const AtomicString& requiredValue = getAttribute(aria_requiredAttr);
-    if (equalLettersIgnoringASCIICase(requiredValue, "true"))
-        return true;
-    if (equalLettersIgnoringASCIICase(requiredValue, "false"))
-        return false;
+    if (auto axRequired = boolValueForProperty(AXPropertyName::Required))
+        return axRequired.value();
 
     Node* n = this->node();
     if (is<HTMLFormControlElement>(n))
@@ -728,26 +726,26 @@ bool AccessibilityNodeObject::isRequired() const
 bool AccessibilityNodeObject::supportsRequiredAttribute() const
 {
     switch (roleValue()) {
-    case ButtonRole:
+    case AccessibilityRole::Button:
         return isFileUploadButton();
-    case CellRole:
-    case ColumnHeaderRole:
-    case CheckBoxRole:
-    case ComboBoxRole:
-    case GridRole:
-    case GridCellRole:
-    case IncrementorRole:
-    case ListBoxRole:
-    case PopUpButtonRole:
-    case RadioButtonRole:
-    case RadioGroupRole:
-    case RowHeaderRole:
-    case SliderRole:
-    case SpinButtonRole:
-    case TableHeaderContainerRole:
-    case TextAreaRole:
-    case TextFieldRole:
-    case ToggleButtonRole:
+    case AccessibilityRole::Cell:
+    case AccessibilityRole::ColumnHeader:
+    case AccessibilityRole::CheckBox:
+    case AccessibilityRole::ComboBox:
+    case AccessibilityRole::Grid:
+    case AccessibilityRole::GridCell:
+    case AccessibilityRole::Incrementor:
+    case AccessibilityRole::ListBox:
+    case AccessibilityRole::PopUpButton:
+    case AccessibilityRole::RadioButton:
+    case AccessibilityRole::RadioGroup:
+    case AccessibilityRole::RowHeader:
+    case AccessibilityRole::Slider:
+    case AccessibilityRole::SpinButton:
+    case AccessibilityRole::TableHeaderContainer:
+    case AccessibilityRole::TextArea:
+    case AccessibilityRole::TextField:
+    case AccessibilityRole::ToggleButton:
         return true;
     default:
         return false;
@@ -762,9 +760,9 @@ int AccessibilityNodeObject::headingLevel() const
         return false;
 
     if (isHeading()) {
-        int ariaLevel = getAttribute(aria_levelAttr).toInt();
-        if (ariaLevel > 0)
-            return ariaLevel;
+        int level = unsignedValueForProperty(AXPropertyName::Level);
+        if (level > 0)
+            return level;
     }
 
     if (node->hasTagName(h1Tag))
@@ -787,7 +785,7 @@ int AccessibilityNodeObject::headingLevel() const
 
     // 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)
+    if (ariaRoleAttribute() == AccessibilityRole::Heading)
         return 2;
 
     return 0;
@@ -798,7 +796,7 @@ String AccessibilityNodeObject::valueDescription() const
     if (!isRangeControl())
         return String();
 
-    return getAttribute(aria_valuetextAttr).string();
+    return stringValueForProperty(AXPropertyName::ValueText);
 }
 
 float AccessibilityNodeObject::valueForRange() const
@@ -814,9 +812,8 @@ float AccessibilityNodeObject::valueForRange() const
 
     // 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();
+    if (hasProperty(AXPropertyName::ValueNow))
+        return doubleValueForProperty(AXPropertyName::ValueNow);
 
     return isSpinButton() ? 0 : (minValueForRange() + maxValueForRange()) / 2;
 }
@@ -832,9 +829,8 @@ float AccessibilityNodeObject::maxValueForRange() const
     if (!isRangeControl())
         return 0.0f;
 
-    auto& value = getAttribute(aria_valuemaxAttr);
-    if (!value.isEmpty())
-        return value.toFloat();
+    if (hasProperty(AXPropertyName::ValueMax))
+        return doubleValueForProperty(AXPropertyName::ValueMax);
 
     // 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.
@@ -852,9 +848,8 @@ float AccessibilityNodeObject::minValueForRange() const
     if (!isRangeControl())
         return 0.0f;
 
-    auto& value = getAttribute(aria_valueminAttr);
-    if (!value.isEmpty())
-        return value.toFloat();
+    if (hasProperty(AXPropertyName::ValueMin))
+        return doubleValueForProperty(AXPropertyName::ValueMin);
 
     // 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.
@@ -868,12 +863,12 @@ float AccessibilityNodeObject::stepValueForRange() const
 
 bool AccessibilityNodeObject::isHeading() const
 {
-    return roleValue() == HeadingRole;
+    return roleValue() == AccessibilityRole::Heading;
 }
 
 bool AccessibilityNodeObject::isLink() const
 {
-    return roleValue() == WebCoreLinkRole;
+    return roleValue() == AccessibilityRole::WebCoreLink;
 }
 
 bool AccessibilityNodeObject::isControl() const
@@ -897,7 +892,7 @@ bool AccessibilityNodeObject::isFieldset() const
 bool AccessibilityNodeObject::isGroup() const
 {
     AccessibilityRole role = roleValue();
-    return role == GroupRole || role == TextGroupRole || role == ApplicationGroupRole || role == ApplicationTextGroupRole;
+    return role == AccessibilityRole::Group || role == AccessibilityRole::TextGroup || role == AccessibilityRole::ApplicationGroup || role == AccessibilityRole::ApplicationTextGroup;
 }
 
 AccessibilityObject* AccessibilityNodeObject::selectedRadioButton()
@@ -907,7 +902,7 @@ AccessibilityObject* AccessibilityNodeObject::selectedRadioButton()
 
     // Find the child radio button that is selected (ie. the intValue == 1).
     for (const auto& child : children()) {
-        if (child->roleValue() == RadioButtonRole && child->checkboxOrRadioValue() == ButtonStateOn)
+        if (child->roleValue() == AccessibilityRole::RadioButton && child->checkboxOrRadioValue() == AccessibilityButtonState::On)
             return child.get();
     }
     return nullptr;
@@ -933,7 +928,7 @@ AccessibilityObject* AccessibilityNodeObject::selectedTabItem()
 AccessibilityButtonState AccessibilityNodeObject::checkboxOrRadioValue() const
 {
     if (isNativeCheckboxOrRadio())
-        return isIndeterminate() ? ButtonStateMixed : isChecked() ? ButtonStateOn : ButtonStateOff;
+        return isIndeterminate() ? AccessibilityButtonState::Mixed : isChecked() ? AccessibilityButtonState::On : AccessibilityButtonState::Off;
 
     return AccessibilityObject::checkboxOrRadioValue();
 }
@@ -1000,14 +995,14 @@ Element* AccessibilityNodeObject::actionElement() const
         return downcast<Element>(node);
 
     switch (roleValue()) {
-    case ButtonRole:
-    case PopUpButtonRole:
-    case ToggleButtonRole:
-    case TabRole:
-    case MenuItemRole:
-    case MenuItemCheckboxRole:
-    case MenuItemRadioRole:
-    case ListItemRole:
+    case AccessibilityRole::Button:
+    case AccessibilityRole::PopUpButton:
+    case AccessibilityRole::ToggleButton:
+    case AccessibilityRole::Tab:
+    case AccessibilityRole::MenuItem:
+    case AccessibilityRole::MenuItemCheckbox:
+    case AccessibilityRole::MenuItemRadio:
+    case AccessibilityRole::ListItem:
         // Check if the author is hiding the real control element inside the ARIA element.
         if (Element* nativeElement = nativeActionElement(node))
             return nativeElement;
@@ -1058,7 +1053,7 @@ bool AccessibilityNodeObject::isDescendantOfBarrenParent() const
 
 void AccessibilityNodeObject::alterSliderValue(bool increase)
 {
-    if (roleValue() != SliderRole)
+    if (roleValue() != AccessibilityRole::Slider)
         return;
 
     if (!getAttribute(stepAttr).isEmpty())
@@ -1117,11 +1112,11 @@ bool AccessibilityNodeObject::isGenericFocusableElement() const
         return false;
     
     AccessibilityRole role = roleValue();
-    if (role == VideoRole || role == AudioRole)
+    if (role == AccessibilityRole::Video || role == AccessibilityRole::Audio)
         return false;
 
     // If it has an aria role, it's not generic.
-    if (m_ariaRole != UnknownRole)
+    if (m_ariaRole != AccessibilityRole::Unknown)
         return false;
 
     // If the content editable attribute is set on this element, that's the reason
@@ -1133,14 +1128,14 @@ bool AccessibilityNodeObject::isGenericFocusableElement() const
 
     // The web area and body element are both focusable, but existing logic handles these
     // cases already, so we don't need to include them here.
-    if (role == WebAreaRole)
+    if (role == AccessibilityRole::WebArea)
         return false;
     if (node() && node()->hasTagName(bodyTag))
         return false;
 
     // An SVG root is focusable by default, but it's probably not interactive, so don't
     // include it. It can still be made accessible by giving it an ARIA role.
-    if (role == SVGRootRole)
+    if (role == AccessibilityRole::SVGRoot)
         return false;
 
     return true;
@@ -1166,7 +1161,7 @@ String AccessibilityNodeObject::ariaAccessibilityDescription() const
     if (!ariaLabeledBy.isEmpty())
         return ariaLabeledBy;
 
-    const AtomicString& ariaLabel = getAttribute(aria_labelAttr);
+    const AtomicString& ariaLabel = stringValueForProperty(AXPropertyName::Label);
     if (!ariaLabel.isEmpty())
         return ariaLabel;
 
@@ -1182,7 +1177,7 @@ static Element* siblingWithAriaRole(Node* node, const char* role)
 
     for (auto& sibling : childrenOfType<Element>(*parent)) {
         // FIXME: Should skip sibling that is the same as the node.
-        if (equalIgnoringASCIICase(sibling.attributeWithoutSynchronization(roleAttr), role))
+        if (equalIgnoringASCIICase(AccessibleNode::effectiveStringValueForElement(sibling, AXPropertyName::Role), role))
             return &sibling;
     }
 
@@ -1191,7 +1186,7 @@ static Element* siblingWithAriaRole(Node* node, const char* role)
 
 Element* AccessibilityNodeObject::menuElementForMenuButton() const
 {
-    if (ariaRoleAttribute() != MenuButtonRole)
+    if (ariaRoleAttribute() != AccessibilityRole::MenuButton)
         return nullptr;
 
     return siblingWithAriaRole(node(), "menu");
@@ -1206,7 +1201,7 @@ AccessibilityObject* AccessibilityNodeObject::menuForMenuButton() const
 
 Element* AccessibilityNodeObject::menuItemElementForMenu() const
 {
-    if (ariaRoleAttribute() != MenuRole)
+    if (ariaRoleAttribute() != AccessibilityRole::Menu)
         return nullptr;
     
     return siblingWithAriaRole(node(), "menuitem");
@@ -1273,7 +1268,7 @@ String AccessibilityNodeObject::textForLabelElement(Element* element) const
     
     // Then check for aria-label attribute.
     if (result.isEmpty())
-        result = label->attributeWithoutSynchronization(aria_labelAttr);
+        result = AccessibleNode::effectiveStringValueForElement(*label, AXPropertyName::Label);
     
     return !result.isEmpty() ? result : label->innerText();
 }
@@ -1291,14 +1286,14 @@ void AccessibilityNodeObject::titleElementText(Vector<AccessibilityText>& textOr
             
             // Only use the <label> text if there's no ARIA override.
             if (!innerText.isEmpty() && !ariaAccessibilityDescription())
-                textOrder.append(AccessibilityText(innerText, isMeter() ? AlternativeText : LabelByElementText, labelObject));
+                textOrder.append(AccessibilityText(innerText, isMeter() ? AccessibilityTextSource::Alternative : AccessibilityTextSource::LabelByElement, labelObject));
             return;
         }
     }
     
     AccessibilityObject* titleUIElement = this->titleUIElement();
     if (titleUIElement)
-        textOrder.append(AccessibilityText(String(), LabelByElementText, titleUIElement));
+        textOrder.append(AccessibilityText(String(), AccessibilityTextSource::LabelByElement, titleUIElement));
 }
 
 void AccessibilityNodeObject::alternativeText(Vector<AccessibilityText>& textOrder) const
@@ -1306,15 +1301,15 @@ void AccessibilityNodeObject::alternativeText(Vector<AccessibilityText>& textOrd
     if (isWebArea()) {
         String webAreaText = alternativeTextForWebArea();
         if (!webAreaText.isEmpty())
-            textOrder.append(AccessibilityText(webAreaText, AlternativeText));
+            textOrder.append(AccessibilityText(webAreaText, AccessibilityTextSource::Alternative));
         return;
     }
     
     ariaLabeledByText(textOrder);
     
-    const AtomicString& ariaLabel = getAttribute(aria_labelAttr);
+    const AtomicString& ariaLabel = stringValueForProperty(AXPropertyName::Label);
     if (!ariaLabel.isEmpty())
-        textOrder.append(AccessibilityText(ariaLabel, AlternativeText));
+        textOrder.append(AccessibilityText(ariaLabel, AccessibilityTextSource::Alternative));
     
     if (usesAltTagForTextComputation()) {
         if (is<RenderImage>(renderer())) {
@@ -1322,7 +1317,7 @@ void AccessibilityNodeObject::alternativeText(Vector<AccessibilityText>& textOrd
 
             // RenderImage will return title as a fallback from altText, but we don't want title here because we consider that in helpText.
             if (!renderAltText.isEmpty() && renderAltText != getAttribute(titleAttr)) {
-                textOrder.append(AccessibilityText(renderAltText, AlternativeText));
+                textOrder.append(AccessibilityText(renderAltText, AccessibilityTextSource::Alternative));
                 return;
             }
         }
@@ -1330,7 +1325,7 @@ void AccessibilityNodeObject::alternativeText(Vector<AccessibilityText>& textOrd
         // Otherwise, it should fallback to other methods, like the title attribute.
         const AtomicString& alt = getAttribute(altAttr);
         if (!alt.isEmpty())
-            textOrder.append(AccessibilityText(alt, AlternativeText));
+            textOrder.append(AccessibilityText(alt, AccessibilityTextSource::Alternative));
     }
     
     Node* node = this->node();
@@ -1341,23 +1336,23 @@ void AccessibilityNodeObject::alternativeText(Vector<AccessibilityText>& textOrd
     if (is<HTMLFieldSetElement>(*node)) {
         AccessibilityObject* object = axObjectCache()->getOrCreate(downcast<HTMLFieldSetElement>(*node).legend());
         if (object && !object->isHidden())
-            textOrder.append(AccessibilityText(accessibleNameForNode(object->node()), AlternativeText));
+            textOrder.append(AccessibilityText(accessibleNameForNode(object->node()), AccessibilityTextSource::Alternative));
     }
     
     // The figure element derives its alternative text from the first associated figcaption element if one is available.
     if (isFigureElement()) {
         AccessibilityObject* captionForFigure = this->captionForFigure();
         if (captionForFigure && !captionForFigure->isHidden())
-            textOrder.append(AccessibilityText(accessibleNameForNode(captionForFigure->node()), AlternativeText));
+            textOrder.append(AccessibilityText(accessibleNameForNode(captionForFigure->node()), AccessibilityTextSource::Alternative));
     }
     
     // Tree items missing a label are labeled by all child elements.
     if (isTreeItem() && ariaLabel.isEmpty() && ariaLabeledByAttribute().isEmpty())
-        textOrder.append(AccessibilityText(accessibleNameForNode(node), AlternativeText));
+        textOrder.append(AccessibilityText(accessibleNameForNode(node), AccessibilityTextSource::Alternative));
     
 #if ENABLE(MATHML)
     if (node->isMathMLElement())
-        textOrder.append(AccessibilityText(getAttribute(MathMLNames::alttextAttr), AlternativeText));
+        textOrder.append(AccessibilityText(getAttribute(MathMLNames::alttextAttr), AccessibilityTextSource::Alternative));
 #endif
 }
 
@@ -1371,7 +1366,7 @@ void AccessibilityNodeObject::visibleText(Vector<AccessibilityText>& textOrder)
     if (isInputTag) {
         HTMLInputElement& input = downcast<HTMLInputElement>(*node);
         if (input.isTextButton()) {
-            textOrder.append(AccessibilityText(input.valueWithDefault(), VisibleText));
+            textOrder.append(AccessibilityText(input.valueWithDefault(), AccessibilityTextSource::Visible));
             return;
         }
     }
@@ -1383,26 +1378,26 @@ void AccessibilityNodeObject::visibleText(Vector<AccessibilityText>& textOrder)
     bool useTextUnderElement = false;
     
     switch (roleValue()) {
-    case PopUpButtonRole:
+    case AccessibilityRole::PopUpButton:
         // Native popup buttons should not use their button children's text as a title. That value is retrieved through stringValue().
         if (node->hasTagName(selectTag))
             break;
         FALLTHROUGH;
-    case ButtonRole:
-    case ToggleButtonRole:
-    case CheckBoxRole:
-    case ListBoxOptionRole:
+    case AccessibilityRole::Button:
+    case AccessibilityRole::ToggleButton:
+    case AccessibilityRole::CheckBox:
+    case AccessibilityRole::ListBoxOption:
     // MacOS does not expect native <li> elements to expose label information, it only expects leaf node elements to do that.
 #if !PLATFORM(COCOA)
-    case ListItemRole:
+    case AccessibilityRole::ListItem:
 #endif
-    case MenuButtonRole:
-    case MenuItemRole:
-    case MenuItemCheckboxRole:
-    case MenuItemRadioRole:
-    case RadioButtonRole:
-    case SwitchRole:
-    case TabRole:
+    case AccessibilityRole::MenuButton:
+    case AccessibilityRole::MenuItem:
+    case AccessibilityRole::MenuItemCheckbox:
+    case AccessibilityRole::MenuItemRadio:
+    case AccessibilityRole::RadioButton:
+    case AccessibilityRole::Switch:
+    case AccessibilityRole::Tab:
         useTextUnderElement = true;
         break;
     default:
@@ -1426,7 +1421,7 @@ void AccessibilityNodeObject::visibleText(Vector<AccessibilityText>& textOrder)
 
         String text = textUnderElement(mode);
         if (!text.isEmpty())
-            textOrder.append(AccessibilityText(text, ChildrenText));
+            textOrder.append(AccessibilityText(text, AccessibilityTextSource::Children));
     }
 }
 
@@ -1434,28 +1429,28 @@ void AccessibilityNodeObject::helpText(Vector<AccessibilityText>& textOrder) con
 {
     const AtomicString& ariaHelp = getAttribute(aria_helpAttr);
     if (!ariaHelp.isEmpty())
-        textOrder.append(AccessibilityText(ariaHelp, HelpText));
+        textOrder.append(AccessibilityText(ariaHelp, AccessibilityTextSource::Help));
     
     String describedBy = ariaDescribedByAttribute();
     if (!describedBy.isEmpty())
-        textOrder.append(AccessibilityText(describedBy, SummaryText));
+        textOrder.append(AccessibilityText(describedBy, AccessibilityTextSource::Summary));
 
     // Summary attribute used as help text on tables.
     const AtomicString& summary = getAttribute(summaryAttr);
     if (!summary.isEmpty())
-        textOrder.append(AccessibilityText(summary, SummaryText));
+        textOrder.append(AccessibilityText(summary, AccessibilityTextSource::Summary));
 
     // 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.
+    // type to AccessibilityTextSource::Help.
     const AtomicString& title = getAttribute(titleAttr);
     if (!title.isEmpty()) {
-        if (!isMeter())
-            textOrder.append(AccessibilityText(title, TitleTagText));
+        if (!isMeter() && !roleIgnoresTitle())
+            textOrder.append(AccessibilityText(title, AccessibilityTextSource::TitleTag));
         else
-            textOrder.append(AccessibilityText(title, HelpText));
+            textOrder.append(AccessibilityText(title, AccessibilityTextSource::Help));
     }
 }
 
@@ -1468,7 +1463,7 @@ void AccessibilityNodeObject::accessibilityText(Vector<AccessibilityText>& textO
     
     String placeholder = placeholderValue();
     if (!placeholder.isEmpty())
-        textOrder.append(AccessibilityText(placeholder, PlaceholderText));
+        textOrder.append(AccessibilityText(placeholder, AccessibilityTextSource::Placeholder));
 }
     
 void AccessibilityNodeObject::ariaLabeledByText(Vector<AccessibilityText>& textOrder) const
@@ -1479,12 +1474,10 @@ void AccessibilityNodeObject::ariaLabeledByText(Vector<AccessibilityText>& textO
         ariaLabeledByElements(elements);
         
         Vector<RefPtr<AccessibilityObject>> axElements;
-        for (const auto& element : elements) {
-            RefPtr<AccessibilityObject> axElement = axObjectCache()->getOrCreate(element);
-            axElements.append(axElement);
-        }
+        for (const auto& element : elements)
+            axElements.append(axObjectCache()->getOrCreate(element));
         
-        textOrder.append(AccessibilityText(ariaLabeledBy, AlternativeText, WTFMove(axElements)));
+        textOrder.append(AccessibilityText(ariaLabeledBy, AccessibilityTextSource::Alternative, WTFMove(axElements)));
     }
 }
     
@@ -1506,7 +1499,7 @@ 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->attributeWithoutSynchronization(aria_labelAttr);
+        const AtomicString& ariaLabel = AccessibleNode::effectiveStringValueForElement(*documentElement, AXPropertyName::Label);
         if (!ariaLabel.isEmpty())
             return ariaLabel;
     }
@@ -1533,7 +1526,7 @@ String AccessibilityNodeObject::alternativeTextForWebArea() const
 String AccessibilityNodeObject::accessibilityDescription() const
 {
     // Static text should not have a description, it should only have a stringValue.
-    if (roleValue() == StaticTextRole)
+    if (roleValue() == AccessibilityRole::StaticText)
         return String();
 
     String ariaDescription = ariaAccessibilityDescription();
@@ -1557,12 +1550,29 @@ String AccessibilityNodeObject::accessibilityDescription() const
     // Both are used to generate what a screen reader speaks.                                                           
     // If this point is reached (i.e. there's no accessibilityDescription) and there's no title(), we should fallback to using the title attribute.
     // The title attribute is normally used as help text (because it is a tooltip), but if there is nothing else available, this should be used (according to ARIA).
-    if (title().isEmpty())
+    // https://bugs.webkit.org/show_bug.cgi?id=170475: An exception is when the element is semantically unimportant. In those cases, title text should remain as help text.
+    if (title().isEmpty() && !roleIgnoresTitle())
         return getAttribute(titleAttr);
 
     return String();
 }
 
+// Returns whether the role was not intended to play a semantically meaningful part of the
+// accessibility hierarchy. This applies to generic groups like <div>'s with no role value set.
+bool AccessibilityNodeObject::roleIgnoresTitle() const
+{
+    if (ariaRoleAttribute() != AccessibilityRole::Unknown)
+        return false;
+
+    switch (roleValue()) {
+    case AccessibilityRole::Div:
+    case AccessibilityRole::Unknown:
+        return true;
+    default:
+        return false;
+    }
+}
+
 String AccessibilityNodeObject::helpText() const
 {
     Node* node = this->node();
@@ -1594,7 +1604,7 @@ String AccessibilityNodeObject::helpText() const
         // 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.
         if (AccessibilityObject* axObj = axObjectCache()->getOrCreate(ancestor)) {
-            if (!axObj->isGroup() && axObj->roleValue() != UnknownRole)
+            if (!axObj->isGroup() && axObj->roleValue() != AccessibilityRole::Unknown)
                 break;
         }
     }
@@ -1607,13 +1617,11 @@ unsigned AccessibilityNodeObject::hierarchicalLevel() const
     Node* node = this->node();
     if (!is<Element>(node))
         return 0;
-    Element& element = downcast<Element>(*node);
-    const AtomicString& ariaLevel = element.attributeWithoutSynchronization(aria_levelAttr);
-    if (!ariaLevel.isEmpty())
-        return ariaLevel.toInt();
+    if (hasProperty(AXPropertyName::Level))
+        return unsignedValueForProperty(AXPropertyName::Level);
     
     // Only tree item will calculate its level through the DOM currently.
-    if (roleValue() != TreeItemRole)
+    if (roleValue() != AccessibilityRole::TreeItem)
         return 0;
     
     // Hierarchy leveling starts at 1, to match the aria-level spec.
@@ -1621,9 +1629,9 @@ unsigned AccessibilityNodeObject::hierarchicalLevel() const
     unsigned level = 1;
     for (AccessibilityObject* parent = parentObject(); parent; parent = parent->parentObject()) {
         AccessibilityRole parentRole = parent->ariaRoleAttribute();
-        if (parentRole == ApplicationGroupRole)
+        if (parentRole == AccessibilityRole::ApplicationGroup)
             level++;
-        else if (parentRole == TreeRole)
+        else if (parentRole == AccessibilityRole::Tree)
             break;
     }
     
@@ -1669,7 +1677,7 @@ static bool shouldUseAccessibilityObjectInnerText(AccessibilityObject* obj, Acce
         && !obj->accessibleNameDerivesFromContent())
         return false;
     
-    if (equalLettersIgnoringASCIICase(obj->getAttribute(aria_hiddenAttr), "true"))
+    if (obj->boolValueForProperty(AXPropertyName::Hidden).value())
         return false;
     
     // If something doesn't expose any children, then we can always take the inner text content.
@@ -1780,26 +1788,26 @@ String AccessibilityNodeObject::title() const
         return String();
 
     switch (roleValue()) {
-    case PopUpButtonRole:
+    case AccessibilityRole::PopUpButton:
         // Native popup buttons should not use their button children's text as a title. That value is retrieved through stringValue().
         if (node->hasTagName(selectTag))
             return String();
         FALLTHROUGH;
-    case ButtonRole:
-    case ToggleButtonRole:
-    case CheckBoxRole:
-    case ListBoxOptionRole:
-    case ListItemRole:
-    case MenuButtonRole:
-    case MenuItemRole:
-    case MenuItemCheckboxRole:
-    case MenuItemRadioRole:
-    case RadioButtonRole:
-    case SwitchRole:
-    case TabRole:
+    case AccessibilityRole::Button:
+    case AccessibilityRole::ToggleButton:
+    case AccessibilityRole::CheckBox:
+    case AccessibilityRole::ListBoxOption:
+    case AccessibilityRole::ListItem:
+    case AccessibilityRole::MenuButton:
+    case AccessibilityRole::MenuItem:
+    case AccessibilityRole::MenuItemCheckbox:
+    case AccessibilityRole::MenuItemRadio:
+    case AccessibilityRole::RadioButton:
+    case AccessibilityRole::Switch:
+    case AccessibilityRole::Tab:
         return textUnderElement();
     // SVGRoots should not use the text under itself as a title. That could include the text of objects like <text>.
-    case SVGRootRole:
+    case AccessibilityRole::SVGRoot:
         return String();
     default:
         break;
@@ -1816,7 +1824,7 @@ String AccessibilityNodeObject::title() const
 String AccessibilityNodeObject::text() const
 {
     // If this is a user defined static text, use the accessible name computation.                                      
-    if (ariaRoleAttribute() == StaticTextRole) {
+    if (isARIAStaticText()) {
         Vector<AccessibilityText> textOrder;
         alternativeText(textOrder);
         if (textOrder.size() > 0 && textOrder[0].text.length())
@@ -1845,7 +1853,7 @@ String AccessibilityNodeObject::stringValue() const
     if (!node)
         return String();
 
-    if (ariaRoleAttribute() == StaticTextRole) {
+    if (isARIAStaticText()) {
         String staticText = text();
         if (!staticText.length())
             staticText = textUnderElement();
@@ -1860,7 +1868,7 @@ String AccessibilityNodeObject::stringValue() const
         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]->attributeWithoutSynchronization(aria_labelAttr);
+            const AtomicString& overriddenDescription = AccessibleNode::effectiveStringValueForElement(*listItems[selectedIndex], AXPropertyName::Label);
             if (!overriddenDescription.isNull())
                 return overriddenDescription;
         }
@@ -1892,12 +1900,7 @@ void AccessibilityNodeObject::colorValue(int& r, int& g, int& b) const
     if (!is<HTMLInputElement>(node()))
         return;
 
-    auto& input = downcast<HTMLInputElement>(*node());
-    if (!input.isColorControl())
-        return;
-
-    // HTMLInputElement::value always returns a string parseable by Color().
-    Color color(input.value());
+    auto color = downcast<HTMLInputElement>(*node()).valueAsColor();
     r = color.red();
     g = color.green();
     b = color.blue();
@@ -1913,7 +1916,7 @@ static String accessibleNameForNode(Node* node, Node* labelledbyNode)
         return String();
     
     Element& element = downcast<Element>(*node);
-    const AtomicString& ariaLabel = element.attributeWithoutSynchronization(aria_labelAttr);
+    const AtomicString& ariaLabel = AccessibleNode::effectiveStringValueForElement(element, AXPropertyName::Label);
     if (!ariaLabel.isEmpty())
         return ariaLabel;
     
@@ -2014,7 +2017,9 @@ bool AccessibilityNodeObject::hasAttributesRequiredForInclusion() const
     if (AccessibilityObject::hasAttributesRequiredForInclusion())
         return true;
 
-    if (!ariaAccessibilityDescription().isEmpty())
+    // Avoid calculating the actual description here, which is expensive.
+    // This means there might be more accessible elements in the tree if the labelledBy points to invalid elements, but that shouldn't cause any real problems.
+    if (getAttribute(aria_labelledbyAttr).length() || getAttribute(aria_labeledbyAttr).length() || getAttribute(aria_labelAttr).length())
         return true;
 
     return false;
@@ -2058,7 +2063,7 @@ bool AccessibilityNodeObject::canSetValueAttribute() const
             return !input.isReadOnly();
     }
 
-    String readOnly = ariaReadOnlyValue();
+    String readOnly = readOnlyValue();
     if (!readOnly.isEmpty())
         return readOnly == "true" ? false : true;
 
@@ -2074,8 +2079,13 @@ bool AccessibilityNodeObject::canSetValueAttribute() const
 #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())
+    if (supportsReadOnly())
         return true;
+
+    if (isRadioButton()) {
+        auto radioGroup = radioGroupAncestor();
+        return radioGroup ? radioGroup->readOnlyValue() != "true" : true;
+    }
 #endif
 
     if (isWebArea()) {
@@ -2096,39 +2106,39 @@ bool AccessibilityNodeObject::canSetValueAttribute() const
 
 AccessibilityRole AccessibilityNodeObject::determineAriaRoleAttribute() const
 {
-    const AtomicString& ariaRole = getAttribute(roleAttr);
+    const AtomicString& ariaRole = stringValueForProperty(AXPropertyName::Role);
     if (ariaRole.isNull() || ariaRole.isEmpty())
-        return UnknownRole;
+        return AccessibilityRole::Unknown;
     
     AccessibilityRole role = ariaRoleToWebCoreRole(ariaRole);
 
     // ARIA states if an item can get focus, it should not be presentational.
-    if (role == PresentationalRole && canSetFocusAttribute())
-        return UnknownRole;
+    if (role == AccessibilityRole::Presentational && canSetFocusAttribute())
+        return AccessibilityRole::Unknown;
 
-    if (role == ButtonRole)
+    if (role == AccessibilityRole::Button)
         role = buttonRoleType();
 
-    if (role == TextAreaRole && !ariaIsMultiline())
-        role = TextFieldRole;
+    if (role == AccessibilityRole::TextArea && !ariaIsMultiline())
+        role = AccessibilityRole::TextField;
 
     role = remapAriaRoleDueToParent(role);
     
     // Presentational roles are invalidated by the presence of ARIA attributes.
-    if (role == PresentationalRole && supportsARIAAttributes())
-        role = UnknownRole;
+    if (role == AccessibilityRole::Presentational && supportsARIAAttributes())
+        role = AccessibilityRole::Unknown;
     
     // The ARIA spec states, "Authors must give each element with role region a brief label that
     // describes the purpose of the content in the region." The Core AAM states, "Special case:
     // if the region does not have an accessible name, do not expose the element as a landmark.
     // Use the native host language role of the element instead."
-    if (role == LandmarkRegionRole && !hasAttribute(aria_labelAttr) && !hasAttribute(aria_labelledbyAttr))
-        role = UnknownRole;
+    if (role == AccessibilityRole::LandmarkRegion && !hasProperty(AXPropertyName::Label) && !hasAttribute(aria_labelledbyAttr))
+        role = AccessibilityRole::Unknown;
 
-    if (role)
+    if (static_cast<int>(role))
         return role;
 
-    return UnknownRole;
+    return AccessibilityRole::Unknown;
 }
 
 AccessibilityRole AccessibilityNodeObject::ariaRoleAttribute() const
@@ -2143,21 +2153,21 @@ AccessibilityRole AccessibilityNodeObject::remapAriaRoleDueToParent(Accessibilit
     // While inside the call stack of creating an element, we need to avoid accessibilityIsIgnored().
     // https://bugs.webkit.org/show_bug.cgi?id=65174
 
-    if (role != ListBoxOptionRole && role != MenuItemRole)
+    if (role != AccessibilityRole::ListBoxOption && role != AccessibilityRole::MenuItem)
         return role;
     
     for (AccessibilityObject* parent = parentObject(); parent && !parent->accessibilityIsIgnored(); parent = parent->parentObject()) {
         AccessibilityRole parentAriaRole = parent->ariaRoleAttribute();
 
         // Selects and listboxes both have options as child roles, but they map to different roles within WebCore.
-        if (role == ListBoxOptionRole && parentAriaRole == MenuRole)
-            return MenuItemRole;
+        if (role == AccessibilityRole::ListBoxOption && parentAriaRole == AccessibilityRole::Menu)
+            return AccessibilityRole::MenuItem;
         // An aria "menuitem" may map to MenuButton or MenuItem depending on its parent.
-        if (role == MenuItemRole && parentAriaRole == ApplicationGroupRole)
-            return MenuButtonRole;
+        if (role == AccessibilityRole::MenuItem && parentAriaRole == AccessibilityRole::ApplicationGroup)
+            return AccessibilityRole::MenuButton;
         
         // If the parent had a different role, then we don't need to continue searching up the chain.
-        if (parentAriaRole)
+        if (parentAriaRole != AccessibilityRole::Unknown)
             break;
     }
     
@@ -2168,19 +2178,19 @@ bool AccessibilityNodeObject::canSetSelectedAttribute() const
 {
     // Elements that can be selected
     switch (roleValue()) {
-    case CellRole:
-    case GridCellRole:
-    case RadioButtonRole:
-    case RowHeaderRole:
-    case RowRole:
-    case TabListRole:
-    case TabRole:
-    case TreeGridRole:
-    case TreeItemRole:
-    case TreeRole:
-    case MenuItemCheckboxRole:
-    case MenuItemRadioRole:
-    case MenuItemRole:
+    case AccessibilityRole::Cell:
+    case AccessibilityRole::GridCell:
+    case AccessibilityRole::RadioButton:
+    case AccessibilityRole::RowHeader:
+    case AccessibilityRole::Row:
+    case AccessibilityRole::TabList:
+    case AccessibilityRole::Tab:
+    case AccessibilityRole::TreeGrid:
+    case AccessibilityRole::TreeItem:
+    case AccessibilityRole::Tree:
+    case AccessibilityRole::MenuItemCheckbox:
+    case AccessibilityRole::MenuItemRadio:
+    case AccessibilityRole::MenuItem:
         return isEnabled();
     default:
         return false;