AccessibilitySVGElement.cpp [plain text]
#include "config.h"
#include "AccessibilitySVGElement.h"
#include "AXObjectCache.h"
#include "ElementIterator.h"
#include "HTMLNames.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"
#include <wtf/Language.h>
namespace WebCore {
AccessibilitySVGElement::AccessibilitySVGElement(RenderObject* renderer)
: AccessibilityRenderObject(renderer)
{
}
AccessibilitySVGElement::~AccessibilitySVGElement() = default;
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();
Element* fallback = nullptr;
Vector<String> childLanguageCodes;
Vector<Element*> elements;
for (auto& child : children) {
auto& lang = child.attributeWithoutSynchronization(SVGNames::langAttr);
childLanguageCodes.append(lang);
elements.append(&child);
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, AccessibilityTextSource::Alternative));
String helptext = helpText();
if (!helptext.isEmpty())
textOrder.append(AccessibilityText(helptext, AccessibilityTextSource::Help));
}
String AccessibilitySVGElement::accessibilityDescription() const
{
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())) {
auto& 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();
}
if (m_renderer->isSVGImage()) {
const AtomicString& alt = getAttribute(HTMLNames::altAttr);
if (!alt.isNull())
return alt;
}
return String();
}
String AccessibilitySVGElement::helpText() const
{
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
{
AccessibilityObjectInclusion decision = defaultObjectInclusion();
if (decision == AccessibilityObjectInclusion::IgnoreObject)
return true;
if (m_renderer->isSVGHiddenContainer())
return true;
for (const auto& child : childrenOfType<SVGElement>(*element())) {
if ((is<SVGTitleElement>(child) || is<SVGDescElement>(child)))
return false;
}
if (roleValue() == AccessibilityRole::Presentational || inheritsPresentationalRole())
return true;
if (ariaRoleAttribute() != AccessibilityRole::Unknown)
return false;
if (m_renderer->isSVGText() || m_renderer->isSVGTextPath()) {
for (auto& child : childrenOfType<RenderText>(downcast<RenderElement>(*m_renderer))) {
if (!child.isAllCollapsibleWhitespace())
return false;
}
}
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 != AccessibilityRole::SVGTextPath && role != AccessibilityRole::SVGTSpan)
return false;
for (AccessibilityObject* parent = parentObject(); parent; parent = parent->parentObject()) {
if (is<AccessibilityRenderObject>(*parent) && parent->element()->hasTagName(SVGNames::textTag))
return parent->roleValue() == AccessibilityRole::Presentational;
}
return false;
}
AccessibilityRole AccessibilitySVGElement::determineAriaRoleAttribute() const
{
AccessibilityRole role = AccessibilityRenderObject::determineAriaRoleAttribute();
if (role != AccessibilityRole::Presentational)
return role;
for (const auto& child : childrenOfType<SVGElement>(*element())) {
if ((is<SVGTitleElement>(child) || is<SVGDescElement>(child)))
return AccessibilityRole::Unknown;
}
return role;
}
AccessibilityRole AccessibilitySVGElement::determineAccessibilityRole()
{
if ((m_ariaRole = determineAriaRoleAttribute()) != AccessibilityRole::Unknown)
return m_ariaRole;
Element* svgElement = element();
if (m_renderer->isSVGShape() || m_renderer->isSVGPath() || m_renderer->isSVGImage() || is<SVGUseElement>(svgElement))
return AccessibilityRole::Image;
if (m_renderer->isSVGForeignObject() || is<SVGGElement>(svgElement))
return AccessibilityRole::Group;
if (m_renderer->isSVGText())
return AccessibilityRole::SVGText;
if (m_renderer->isSVGTextPath())
return AccessibilityRole::SVGTextPath;
if (m_renderer->isSVGTSpan())
return AccessibilityRole::SVGTSpan;
if (is<SVGAElement>(svgElement))
return AccessibilityRole::WebCoreLink;
return AccessibilityRenderObject::determineAccessibilityRole();
}
}