blob: b98f6090a9a56a639893ae35c76f56c276200e7a [file] [log] [blame]
/*
* Copyright (C) 2008 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "config.h"
#import "AccessibilityObject.h"
#import "AccessibilityLabel.h"
#import "AccessibilityList.h"
#import "ElementAncestorIterator.h"
#import "HTMLFieldSetElement.h"
#import "HTMLInputElement.h"
#import "LocalizedStrings.h"
#import "RenderObject.h"
#import "Settings.h"
#if ENABLE(ACCESSIBILITY) && PLATFORM(MAC)
#import "WebAccessibilityObjectWrapperMac.h"
#import "Widget.h"
namespace WebCore {
void AccessibilityObject::detachFromParent()
{
if (isAttachment())
overrideAttachmentParent(nullptr);
}
void AccessibilityObject::overrideAttachmentParent(AXCoreObject* parent)
{
if (!isAttachment())
return;
id parentWrapper = nil;
if (parent) {
if (parent->accessibilityIsIgnored())
parent = parent->parentObjectUnignored();
parentWrapper = parent->wrapper();
}
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
[[wrapper() attachmentView] accessibilitySetOverrideValue:parentWrapper forAttribute:NSAccessibilityParentAttribute];
ALLOW_DEPRECATED_DECLARATIONS_END
}
// On iOS, we don't have to return the value in the title. We can return the actual title, given the API.
bool AccessibilityObject::fileUploadButtonReturnsValueInTitle() const
{
return true;
}
bool AccessibilityObject::accessibilityIgnoreAttachment() const
{
// FrameView attachments are now handled by AccessibilityScrollView,
// so if this is the attachment, it should be ignored.
Widget* widget = nullptr;
if (isAttachment() && (widget = widgetForAttachmentView()) && widget->isFrameView())
return true;
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
if ([wrapper() attachmentView])
return [[wrapper() attachmentView] accessibilityIsIgnored];
ALLOW_DEPRECATED_DECLARATIONS_END
// Attachments are ignored by default (unless we determine that we should expose them).
return true;
}
AccessibilityObjectInclusion AccessibilityObject::accessibilityPlatformIncludesObject() const
{
if (isMenuListPopup() || isMenuListOption())
return AccessibilityObjectInclusion::IgnoreObject;
if (roleValue() == AccessibilityRole::Caption && ariaRoleAttribute() == AccessibilityRole::Unknown)
return AccessibilityObjectInclusion::IgnoreObject;
if (roleValue() == AccessibilityRole::Mark)
return AccessibilityObjectInclusion::IncludeObject;
// Never expose an unknown object on the Mac. Clients of the AX API will not know what to do with it.
// Special case is when the unknown object is actually an attachment.
if (roleValue() == AccessibilityRole::Unknown && !isAttachment())
return AccessibilityObjectInclusion::IgnoreObject;
if (roleValue() == AccessibilityRole::Inline && !isStyleFormatGroup())
return AccessibilityObjectInclusion::IgnoreObject;
if (RenderObject* renderer = this->renderer()) {
// The legend element is ignored if it lives inside of a fieldset element that uses it to generate alternative text.
if (renderer->isLegend()) {
Element* element = this->element();
if (element && ancestorsOfType<HTMLFieldSetElement>(*element).first())
return AccessibilityObjectInclusion::IgnoreObject;
}
}
return AccessibilityObjectInclusion::DefaultBehavior;
}
bool AccessibilityObject::caretBrowsingEnabled() const
{
Frame* frame = this->frame();
return frame && frame->settings().caretBrowsingEnabled();
}
void AccessibilityObject::setCaretBrowsingEnabled(bool on)
{
Frame* frame = this->frame();
if (!frame)
return;
frame->settings().setCaretBrowsingEnabled(on);
}
String AccessibilityObject::rolePlatformString() const
{
AccessibilityRole role = roleValue();
// If it is a label with just static text or an anonymous math operator, remap role to StaticText.
// The mfenced element creates anonymous RenderMathMLOperators with no RenderText
// descendants. These anonymous renderers are the only accessible objects
// containing the operator.
if ((role == AccessibilityRole::Label && is<AccessibilityLabel>(*this) && downcast<AccessibilityLabel>(*this).containsOnlyStaticText())
|| isAnonymousMathOperator())
role = AccessibilityRole::StaticText;
else if (role == AccessibilityRole::Canvas && canvasHasFallbackContent())
role = AccessibilityRole::Group;
return Accessibility::roleToPlatformString(role);
}
String AccessibilityObject::rolePlatformDescription() const
{
AccessibilityRole role = roleValue();
NSString *axRole = rolePlatformString();
if ([axRole isEqualToString:NSAccessibilityGroupRole]) {
if (isOutput())
return AXOutputText();
String ariaLandmarkRoleDescription = this->ariaLandmarkRoleDescription();
if (!ariaLandmarkRoleDescription.isEmpty())
return ariaLandmarkRoleDescription;
switch (role) {
case AccessibilityRole::Audio:
return localizedMediaControlElementString("AudioElement");
case AccessibilityRole::Definition:
return AXDefinitionText();
case AccessibilityRole::DescriptionListTerm:
case AccessibilityRole::Term:
return AXDescriptionListTermText();
case AccessibilityRole::DescriptionListDetail:
return AXDescriptionListDetailText();
case AccessibilityRole::Details:
return AXDetailsText();
case AccessibilityRole::Feed:
return AXFeedText();
case AccessibilityRole::Footer:
return AXFooterRoleDescriptionText();
case AccessibilityRole::Mark:
return AXMarkText();
case AccessibilityRole::Video:
return localizedMediaControlElementString("VideoElement");
case AccessibilityRole::GraphicsDocument:
return AXARIAContentGroupText(@"ARIADocument");
default:
return String();
}
}
if ([axRole isEqualToString:@"AXWebArea"])
return AXWebAreaText();
if ([axRole isEqualToString:@"AXLink"])
return AXLinkText();
if ([axRole isEqualToString:@"AXListMarker"])
return AXListMarkerText();
if ([axRole isEqualToString:@"AXImageMap"])
return AXImageMapText();
if ([axRole isEqualToString:@"AXHeading"])
return AXHeadingText();
if ([axRole isEqualToString:NSAccessibilityTextFieldRole]) {
auto* node = this->node();
if (is<HTMLInputElement>(node)) {
auto& input = downcast<HTMLInputElement>(*node);
if (input.isEmailField())
return AXEmailFieldText();
if (input.isTelephoneField())
return AXTelephoneFieldText();
if (input.isURLField())
return AXURLFieldText();
if (input.isNumberField())
return AXNumberFieldText();
// These input types are not enabled on mac yet, we check the type attribute for now.
auto& type = input.attributeWithoutSynchronization(HTMLNames::typeAttr);
if (equalLettersIgnoringASCIICase(type, "date"))
return AXDateFieldText();
if (equalLettersIgnoringASCIICase(type, "time"))
return AXTimeFieldText();
if (equalLettersIgnoringASCIICase(type, "week"))
return AXWeekFieldText();
if (equalLettersIgnoringASCIICase(type, "month"))
return AXMonthFieldText();
if (equalLettersIgnoringASCIICase(type, "datetime-local"))
return AXDateTimeFieldText();
}
}
if (isFileUploadButton())
return AXFileUploadButtonText();
// Only returning for DL (not UL or OL) because description changed with HTML5 from 'definition list' to
// superset 'description list' and does not return the same values in AX API on some OS versions.
if (is<AccessibilityList>(*this)) {
if (downcast<AccessibilityList>(*this).isDescriptionList())
return AXDescriptionListText();
}
if (role == AccessibilityRole::HorizontalRule)
return AXHorizontalRuleDescriptionText();
// AppKit also returns AXTab for the role description for a tab item.
if (isTabItem())
return NSAccessibilityRoleDescription(@"AXTab", nil);
if (isSummary())
return AXSummaryText();
return String();
}
namespace Accessibility {
PlatformRoleMap createPlatformRoleMap()
{
struct RoleEntry {
AccessibilityRole value;
NSString *string;
};
static const RoleEntry roles[] = {
{ AccessibilityRole::Unknown, NSAccessibilityUnknownRole },
{ AccessibilityRole::Button, NSAccessibilityButtonRole },
{ AccessibilityRole::RadioButton, NSAccessibilityRadioButtonRole },
{ AccessibilityRole::CheckBox, NSAccessibilityCheckBoxRole },
{ AccessibilityRole::Slider, NSAccessibilitySliderRole },
{ AccessibilityRole::TabGroup, NSAccessibilityTabGroupRole },
{ AccessibilityRole::TextField, NSAccessibilityTextFieldRole },
{ AccessibilityRole::StaticText, NSAccessibilityStaticTextRole },
{ AccessibilityRole::TextArea, NSAccessibilityTextAreaRole },
{ AccessibilityRole::ScrollArea, NSAccessibilityScrollAreaRole },
{ AccessibilityRole::PopUpButton, NSAccessibilityPopUpButtonRole },
{ AccessibilityRole::MenuButton, NSAccessibilityMenuButtonRole },
{ AccessibilityRole::Table, NSAccessibilityTableRole },
{ AccessibilityRole::Application, NSAccessibilityApplicationRole },
{ AccessibilityRole::Group, NSAccessibilityGroupRole },
{ AccessibilityRole::TextGroup, NSAccessibilityGroupRole },
{ AccessibilityRole::RadioGroup, NSAccessibilityRadioGroupRole },
{ AccessibilityRole::List, NSAccessibilityListRole },
{ AccessibilityRole::Directory, NSAccessibilityListRole },
{ AccessibilityRole::ScrollBar, NSAccessibilityScrollBarRole },
{ AccessibilityRole::ValueIndicator, NSAccessibilityValueIndicatorRole },
{ AccessibilityRole::Image, NSAccessibilityImageRole },
{ AccessibilityRole::MenuBar, NSAccessibilityMenuBarRole },
{ AccessibilityRole::Menu, NSAccessibilityMenuRole },
{ AccessibilityRole::MenuItem, NSAccessibilityMenuItemRole },
{ AccessibilityRole::MenuItemCheckbox, NSAccessibilityMenuItemRole },
{ AccessibilityRole::MenuItemRadio, NSAccessibilityMenuItemRole },
{ AccessibilityRole::Column, NSAccessibilityColumnRole },
{ AccessibilityRole::Row, NSAccessibilityRowRole },
{ AccessibilityRole::Toolbar, NSAccessibilityToolbarRole },
{ AccessibilityRole::BusyIndicator, NSAccessibilityBusyIndicatorRole },
{ AccessibilityRole::ProgressIndicator, NSAccessibilityProgressIndicatorRole },
{ AccessibilityRole::Meter, NSAccessibilityLevelIndicatorRole },
{ AccessibilityRole::Window, NSAccessibilityWindowRole },
{ AccessibilityRole::Drawer, NSAccessibilityDrawerRole },
{ AccessibilityRole::SystemWide, NSAccessibilitySystemWideRole },
{ AccessibilityRole::Outline, NSAccessibilityOutlineRole },
{ AccessibilityRole::Incrementor, NSAccessibilityIncrementorRole },
{ AccessibilityRole::Browser, NSAccessibilityBrowserRole },
{ AccessibilityRole::ComboBox, NSAccessibilityComboBoxRole },
{ AccessibilityRole::SplitGroup, NSAccessibilitySplitGroupRole },
{ AccessibilityRole::Splitter, NSAccessibilitySplitterRole },
{ AccessibilityRole::ColorWell, NSAccessibilityColorWellRole },
{ AccessibilityRole::GrowArea, NSAccessibilityGrowAreaRole },
{ AccessibilityRole::Sheet, NSAccessibilitySheetRole },
{ AccessibilityRole::HelpTag, NSAccessibilityHelpTagRole },
{ AccessibilityRole::Matte, NSAccessibilityMatteRole },
{ AccessibilityRole::Ruler, NSAccessibilityRulerRole },
{ AccessibilityRole::RulerMarker, NSAccessibilityRulerMarkerRole },
{ AccessibilityRole::Link, NSAccessibilityLinkRole },
{ AccessibilityRole::DisclosureTriangle, NSAccessibilityDisclosureTriangleRole },
{ AccessibilityRole::Grid, NSAccessibilityTableRole },
{ AccessibilityRole::TreeGrid, NSAccessibilityTableRole },
{ AccessibilityRole::WebCoreLink, NSAccessibilityLinkRole },
{ AccessibilityRole::ImageMapLink, NSAccessibilityLinkRole },
{ AccessibilityRole::ImageMap, @"AXImageMap" },
{ AccessibilityRole::ListMarker, @"AXListMarker" },
{ AccessibilityRole::WebArea, @"AXWebArea" },
{ AccessibilityRole::Heading, @"AXHeading" },
{ AccessibilityRole::ListBox, NSAccessibilityListRole },
{ AccessibilityRole::ListBoxOption, NSAccessibilityStaticTextRole },
{ AccessibilityRole::Cell, NSAccessibilityCellRole },
{ AccessibilityRole::GridCell, NSAccessibilityCellRole },
{ AccessibilityRole::TableHeaderContainer, NSAccessibilityGroupRole },
{ AccessibilityRole::ColumnHeader, NSAccessibilityCellRole },
{ AccessibilityRole::RowHeader, NSAccessibilityCellRole },
{ AccessibilityRole::Definition, NSAccessibilityGroupRole },
{ AccessibilityRole::DescriptionListDetail, NSAccessibilityGroupRole },
{ AccessibilityRole::DescriptionListTerm, NSAccessibilityGroupRole },
{ AccessibilityRole::Term, NSAccessibilityGroupRole },
{ AccessibilityRole::DescriptionList, NSAccessibilityListRole },
{ AccessibilityRole::SliderThumb, NSAccessibilityValueIndicatorRole },
{ AccessibilityRole::WebApplication, NSAccessibilityGroupRole },
{ AccessibilityRole::LandmarkBanner, NSAccessibilityGroupRole },
{ AccessibilityRole::LandmarkComplementary, NSAccessibilityGroupRole },
{ AccessibilityRole::LandmarkDocRegion, NSAccessibilityGroupRole },
{ AccessibilityRole::LandmarkContentInfo, NSAccessibilityGroupRole },
{ AccessibilityRole::LandmarkMain, NSAccessibilityGroupRole },
{ AccessibilityRole::LandmarkNavigation, NSAccessibilityGroupRole },
{ AccessibilityRole::LandmarkRegion, NSAccessibilityGroupRole },
{ AccessibilityRole::LandmarkSearch, NSAccessibilityGroupRole },
{ AccessibilityRole::ApplicationAlert, NSAccessibilityGroupRole },
{ AccessibilityRole::ApplicationAlertDialog, NSAccessibilityGroupRole },
{ AccessibilityRole::ApplicationDialog, NSAccessibilityGroupRole },
{ AccessibilityRole::ApplicationGroup, NSAccessibilityGroupRole },
{ AccessibilityRole::ApplicationTextGroup, NSAccessibilityGroupRole },
{ AccessibilityRole::ApplicationLog, NSAccessibilityGroupRole },
{ AccessibilityRole::ApplicationMarquee, NSAccessibilityGroupRole },
{ AccessibilityRole::ApplicationStatus, NSAccessibilityGroupRole },
{ AccessibilityRole::ApplicationTimer, NSAccessibilityGroupRole },
{ AccessibilityRole::Document, NSAccessibilityGroupRole },
{ AccessibilityRole::DocumentArticle, NSAccessibilityGroupRole },
{ AccessibilityRole::DocumentMath, NSAccessibilityGroupRole },
{ AccessibilityRole::DocumentNote, NSAccessibilityGroupRole },
{ AccessibilityRole::UserInterfaceTooltip, NSAccessibilityGroupRole },
{ AccessibilityRole::Tab, NSAccessibilityRadioButtonRole },
{ AccessibilityRole::TabList, NSAccessibilityTabGroupRole },
{ AccessibilityRole::TabPanel, NSAccessibilityGroupRole },
{ AccessibilityRole::Tree, NSAccessibilityOutlineRole },
{ AccessibilityRole::TreeItem, NSAccessibilityRowRole },
{ AccessibilityRole::ListItem, NSAccessibilityGroupRole },
{ AccessibilityRole::Paragraph, NSAccessibilityGroupRole },
{ AccessibilityRole::Label, NSAccessibilityGroupRole },
{ AccessibilityRole::Div, NSAccessibilityGroupRole },
{ AccessibilityRole::Form, NSAccessibilityGroupRole },
{ AccessibilityRole::SpinButton, NSAccessibilityIncrementorRole },
{ AccessibilityRole::Footer, NSAccessibilityGroupRole },
{ AccessibilityRole::ToggleButton, NSAccessibilityCheckBoxRole },
{ AccessibilityRole::Canvas, NSAccessibilityImageRole },
{ AccessibilityRole::SVGRoot, NSAccessibilityGroupRole },
{ AccessibilityRole::Legend, NSAccessibilityGroupRole },
{ AccessibilityRole::MathElement, NSAccessibilityGroupRole },
{ AccessibilityRole::Audio, NSAccessibilityGroupRole },
{ AccessibilityRole::Video, NSAccessibilityGroupRole },
{ AccessibilityRole::HorizontalRule, NSAccessibilitySplitterRole },
{ AccessibilityRole::Blockquote, NSAccessibilityGroupRole },
{ AccessibilityRole::Switch, NSAccessibilityCheckBoxRole },
{ AccessibilityRole::SearchField, NSAccessibilityTextFieldRole },
{ AccessibilityRole::Pre, NSAccessibilityGroupRole },
{ AccessibilityRole::RubyBase, NSAccessibilityGroupRole },
{ AccessibilityRole::RubyBlock, NSAccessibilityGroupRole },
{ AccessibilityRole::RubyInline, NSAccessibilityGroupRole },
{ AccessibilityRole::RubyRun, NSAccessibilityGroupRole },
{ AccessibilityRole::RubyText, NSAccessibilityGroupRole },
{ AccessibilityRole::Details, NSAccessibilityGroupRole },
{ AccessibilityRole::Summary, NSAccessibilityButtonRole },
{ AccessibilityRole::SVGTextPath, NSAccessibilityGroupRole },
{ AccessibilityRole::SVGText, NSAccessibilityGroupRole },
{ AccessibilityRole::SVGTSpan, NSAccessibilityGroupRole },
{ AccessibilityRole::Inline, NSAccessibilityGroupRole },
{ AccessibilityRole::Mark, NSAccessibilityGroupRole },
{ AccessibilityRole::Time, NSAccessibilityGroupRole },
{ AccessibilityRole::Feed, NSAccessibilityGroupRole },
{ AccessibilityRole::Figure, NSAccessibilityGroupRole },
{ AccessibilityRole::Footnote, NSAccessibilityGroupRole },
{ AccessibilityRole::GraphicsDocument, NSAccessibilityGroupRole },
{ AccessibilityRole::GraphicsObject, NSAccessibilityGroupRole },
{ AccessibilityRole::GraphicsSymbol, NSAccessibilityImageRole },
{ AccessibilityRole::Caption, NSAccessibilityGroupRole },
{ AccessibilityRole::Deletion, NSAccessibilityGroupRole },
{ AccessibilityRole::Insertion, NSAccessibilityGroupRole },
{ AccessibilityRole::Subscript, NSAccessibilityGroupRole },
{ AccessibilityRole::Superscript, NSAccessibilityGroupRole },
};
PlatformRoleMap roleMap;
for (auto& role : roles)
roleMap.add(static_cast<unsigned>(role.value), role.string);
return roleMap;
}
} // namespace Accessibility
} // WebCore
#endif // ENABLE(ACCESSIBILITY) && PLATFORM(MAC)