Index: third_party/WebKit/Source/modules/accessibility/AXObject.cpp |
diff --git a/third_party/WebKit/Source/modules/accessibility/AXObject.cpp b/third_party/WebKit/Source/modules/accessibility/AXObject.cpp |
deleted file mode 100644 |
index 2cae448cea5924e6a4d2b86e6dc2643618255bf7..0000000000000000000000000000000000000000 |
--- a/third_party/WebKit/Source/modules/accessibility/AXObject.cpp |
+++ /dev/null |
@@ -1,1871 +0,0 @@ |
-/* |
- * Copyright (C) 2008, 2009, 2011 Apple Inc. All rights reserved. |
- * |
- * Redistribution and use in source and binary forms, with or without |
- * modification, are permitted provided that the following conditions |
- * are met: |
- * |
- * 1. Redistributions of source code must retain the above copyright |
- * notice, this list of conditions and the following disclaimer. |
- * 2. Redistributions in binary form must reproduce the above copyright |
- * notice, this list of conditions and the following disclaimer in the |
- * documentation and/or other materials provided with the distribution. |
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of |
- * its contributors may be used to endorse or promote products derived |
- * from this software without specific prior written permission. |
- * |
- * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY |
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
- * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
- */ |
- |
-#include "modules/accessibility/AXObject.h" |
- |
-#include "SkMatrix44.h" |
-#include "core/InputTypeNames.h" |
-#include "core/css/resolver/StyleResolver.h" |
-#include "core/dom/AccessibleNode.h" |
-#include "core/dom/DocumentUserGestureToken.h" |
-#include "core/editing/EditingUtilities.h" |
-#include "core/editing/VisibleUnits.h" |
-#include "core/frame/FrameView.h" |
-#include "core/frame/LocalFrame.h" |
-#include "core/frame/Settings.h" |
-#include "core/html/HTMLDialogElement.h" |
-#include "core/html/HTMLFrameOwnerElement.h" |
-#include "core/html/HTMLInputElement.h" |
-#include "core/html/parser/HTMLParserIdioms.h" |
-#include "core/layout/LayoutBoxModelObject.h" |
-#include "modules/accessibility/AXObjectCacheImpl.h" |
-#include "platform/UserGestureIndicator.h" |
-#include "platform/text/PlatformLocale.h" |
-#include "platform/wtf/HashSet.h" |
-#include "platform/wtf/StdLibExtras.h" |
-#include "platform/wtf/text/WTFString.h" |
- |
-using blink::WebLocalizedString; |
- |
-namespace blink { |
- |
-using namespace HTMLNames; |
- |
-namespace { |
-typedef HashMap<String, AccessibilityRole, CaseFoldingHash> ARIARoleMap; |
-typedef HashSet<String, CaseFoldingHash> ARIAWidgetSet; |
- |
-struct RoleEntry { |
- const char* aria_role; |
- AccessibilityRole webcore_role; |
-}; |
- |
-const RoleEntry kRoles[] = {{"alert", kAlertRole}, |
- {"alertdialog", kAlertDialogRole}, |
- {"application", kApplicationRole}, |
- {"article", kArticleRole}, |
- {"banner", kBannerRole}, |
- {"button", kButtonRole}, |
- {"cell", kCellRole}, |
- {"checkbox", kCheckBoxRole}, |
- {"columnheader", kColumnHeaderRole}, |
- {"combobox", kComboBoxRole}, |
- {"complementary", kComplementaryRole}, |
- {"contentinfo", kContentInfoRole}, |
- {"definition", kDefinitionRole}, |
- {"dialog", kDialogRole}, |
- {"directory", kDirectoryRole}, |
- {"document", kDocumentRole}, |
- {"feed", kFeedRole}, |
- {"figure", kFigureRole}, |
- {"form", kFormRole}, |
- {"grid", kGridRole}, |
- {"gridcell", kCellRole}, |
- {"group", kGroupRole}, |
- {"heading", kHeadingRole}, |
- {"img", kImageRole}, |
- {"link", kLinkRole}, |
- {"list", kListRole}, |
- {"listbox", kListBoxRole}, |
- {"listitem", kListItemRole}, |
- {"log", kLogRole}, |
- {"main", kMainRole}, |
- {"marquee", kMarqueeRole}, |
- {"math", kMathRole}, |
- {"menu", kMenuRole}, |
- {"menubar", kMenuBarRole}, |
- {"menuitem", kMenuItemRole}, |
- {"menuitemcheckbox", kMenuItemCheckBoxRole}, |
- {"menuitemradio", kMenuItemRadioRole}, |
- {"navigation", kNavigationRole}, |
- {"none", kNoneRole}, |
- {"note", kNoteRole}, |
- {"option", kListBoxOptionRole}, |
- {"presentation", kPresentationalRole}, |
- {"progressbar", kProgressIndicatorRole}, |
- {"radio", kRadioButtonRole}, |
- {"radiogroup", kRadioGroupRole}, |
- {"region", kRegionRole}, |
- {"row", kRowRole}, |
- {"rowheader", kRowHeaderRole}, |
- {"scrollbar", kScrollBarRole}, |
- {"search", kSearchRole}, |
- {"searchbox", kSearchBoxRole}, |
- {"separator", kSplitterRole}, |
- {"slider", kSliderRole}, |
- {"spinbutton", kSpinButtonRole}, |
- {"status", kStatusRole}, |
- {"switch", kSwitchRole}, |
- {"tab", kTabRole}, |
- {"table", kTableRole}, |
- {"tablist", kTabListRole}, |
- {"tabpanel", kTabPanelRole}, |
- {"term", kTermRole}, |
- {"text", kStaticTextRole}, |
- {"textbox", kTextFieldRole}, |
- {"timer", kTimerRole}, |
- {"toolbar", kToolbarRole}, |
- {"tooltip", kUserInterfaceTooltipRole}, |
- {"tree", kTreeRole}, |
- {"treegrid", kTreeGridRole}, |
- {"treeitem", kTreeItemRole}}; |
- |
-struct InternalRoleEntry { |
- AccessibilityRole webcore_role; |
- const char* internal_role_name; |
-}; |
- |
-const InternalRoleEntry kInternalRoles[] = { |
- {kUnknownRole, "Unknown"}, |
- {kAbbrRole, "Abbr"}, |
- {kAlertDialogRole, "AlertDialog"}, |
- {kAlertRole, "Alert"}, |
- {kAnchorRole, "Anchor"}, |
- {kAnnotationRole, "Annotation"}, |
- {kApplicationRole, "Application"}, |
- {kArticleRole, "Article"}, |
- {kAudioRole, "Audio"}, |
- {kBannerRole, "Banner"}, |
- {kBlockquoteRole, "Blockquote"}, |
- // TODO(nektar): Delete busy_indicator role. It's used nowhere. |
- {kBusyIndicatorRole, "BusyIndicator"}, |
- {kButtonRole, "Button"}, |
- {kCanvasRole, "Canvas"}, |
- {kCaptionRole, "Caption"}, |
- {kCellRole, "Cell"}, |
- {kCheckBoxRole, "CheckBox"}, |
- {kColorWellRole, "ColorWell"}, |
- {kColumnHeaderRole, "ColumnHeader"}, |
- {kColumnRole, "Column"}, |
- {kComboBoxRole, "ComboBox"}, |
- {kComplementaryRole, "Complementary"}, |
- {kContentInfoRole, "ContentInfo"}, |
- {kDateRole, "Date"}, |
- {kDateTimeRole, "DateTime"}, |
- {kDefinitionRole, "Definition"}, |
- {kDescriptionListDetailRole, "DescriptionListDetail"}, |
- {kDescriptionListRole, "DescriptionList"}, |
- {kDescriptionListTermRole, "DescriptionListTerm"}, |
- {kDetailsRole, "Details"}, |
- {kDialogRole, "Dialog"}, |
- {kDirectoryRole, "Directory"}, |
- {kDisclosureTriangleRole, "DisclosureTriangle"}, |
- {kDivRole, "Div"}, |
- {kDocumentRole, "Document"}, |
- {kEmbeddedObjectRole, "EmbeddedObject"}, |
- {kFeedRole, "feed"}, |
- {kFigcaptionRole, "Figcaption"}, |
- {kFigureRole, "Figure"}, |
- {kFooterRole, "Footer"}, |
- {kFormRole, "Form"}, |
- {kGridRole, "Grid"}, |
- {kGroupRole, "Group"}, |
- {kHeadingRole, "Heading"}, |
- {kIframePresentationalRole, "IframePresentational"}, |
- {kIframeRole, "Iframe"}, |
- {kIgnoredRole, "Ignored"}, |
- {kImageMapLinkRole, "ImageMapLink"}, |
- {kImageMapRole, "ImageMap"}, |
- {kImageRole, "Image"}, |
- {kInlineTextBoxRole, "InlineTextBox"}, |
- {kInputTimeRole, "InputTime"}, |
- {kLabelRole, "Label"}, |
- {kLegendRole, "Legend"}, |
- {kLinkRole, "Link"}, |
- {kLineBreakRole, "LineBreak"}, |
- {kListBoxOptionRole, "ListBoxOption"}, |
- {kListBoxRole, "ListBox"}, |
- {kListItemRole, "ListItem"}, |
- {kListMarkerRole, "ListMarker"}, |
- {kListRole, "List"}, |
- {kLogRole, "Log"}, |
- {kMainRole, "Main"}, |
- {kMarkRole, "Mark"}, |
- {kMarqueeRole, "Marquee"}, |
- {kMathRole, "Math"}, |
- {kMenuBarRole, "MenuBar"}, |
- {kMenuButtonRole, "MenuButton"}, |
- {kMenuItemRole, "MenuItem"}, |
- {kMenuItemCheckBoxRole, "MenuItemCheckBox"}, |
- {kMenuItemRadioRole, "MenuItemRadio"}, |
- {kMenuListOptionRole, "MenuListOption"}, |
- {kMenuListPopupRole, "MenuListPopup"}, |
- {kMenuRole, "Menu"}, |
- {kMeterRole, "Meter"}, |
- {kNavigationRole, "Navigation"}, |
- {kNoneRole, "None"}, |
- {kNoteRole, "Note"}, |
- {kOutlineRole, "Outline"}, |
- {kParagraphRole, "Paragraph"}, |
- {kPopUpButtonRole, "PopUpButton"}, |
- {kPreRole, "Pre"}, |
- {kPresentationalRole, "Presentational"}, |
- {kProgressIndicatorRole, "ProgressIndicator"}, |
- {kRadioButtonRole, "RadioButton"}, |
- {kRadioGroupRole, "RadioGroup"}, |
- {kRegionRole, "Region"}, |
- {kRootWebAreaRole, "RootWebArea"}, |
- {kRowHeaderRole, "RowHeader"}, |
- {kRowRole, "Row"}, |
- {kRubyRole, "Ruby"}, |
- {kRulerRole, "Ruler"}, |
- {kSVGRootRole, "SVGRoot"}, |
- {kScrollAreaRole, "ScrollArea"}, |
- {kScrollBarRole, "ScrollBar"}, |
- {kSeamlessWebAreaRole, "SeamlessWebArea"}, |
- {kSearchRole, "Search"}, |
- {kSearchBoxRole, "SearchBox"}, |
- {kSliderRole, "Slider"}, |
- {kSliderThumbRole, "SliderThumb"}, |
- {kSpinButtonPartRole, "SpinButtonPart"}, |
- {kSpinButtonRole, "SpinButton"}, |
- {kSplitterRole, "Splitter"}, |
- {kStaticTextRole, "StaticText"}, |
- {kStatusRole, "Status"}, |
- {kSwitchRole, "Switch"}, |
- {kTabGroupRole, "TabGroup"}, |
- {kTabListRole, "TabList"}, |
- {kTabPanelRole, "TabPanel"}, |
- {kTabRole, "Tab"}, |
- {kTableHeaderContainerRole, "TableHeaderContainer"}, |
- {kTableRole, "Table"}, |
- {kTermRole, "Term"}, |
- {kTextFieldRole, "TextField"}, |
- {kTimeRole, "Time"}, |
- {kTimerRole, "Timer"}, |
- {kToggleButtonRole, "ToggleButton"}, |
- {kToolbarRole, "Toolbar"}, |
- {kTreeGridRole, "TreeGrid"}, |
- {kTreeItemRole, "TreeItem"}, |
- {kTreeRole, "Tree"}, |
- {kUserInterfaceTooltipRole, "UserInterfaceTooltip"}, |
- {kVideoRole, "Video"}, |
- {kWebAreaRole, "WebArea"}, |
- {kWindowRole, "Window"}}; |
- |
-static_assert(WTF_ARRAY_LENGTH(kInternalRoles) == kNumRoles, |
- "Not all internal roles have an entry in internalRoles array"); |
- |
-// Roles which we need to map in the other direction |
-const RoleEntry kReverseRoles[] = { |
- {"button", kToggleButtonRole}, {"combobox", kPopUpButtonRole}, |
- {"contentinfo", kFooterRole}, {"menuitem", kMenuButtonRole}, |
- {"menuitem", kMenuListOptionRole}, {"progressbar", kMeterRole}, |
- {"textbox", kTextFieldRole}}; |
- |
-static ARIARoleMap* CreateARIARoleMap() { |
- ARIARoleMap* role_map = new ARIARoleMap; |
- |
- for (size_t i = 0; i < WTF_ARRAY_LENGTH(kRoles); ++i) |
- role_map->Set(String(kRoles[i].aria_role), kRoles[i].webcore_role); |
- return role_map; |
-} |
- |
-static Vector<AtomicString>* CreateRoleNameVector() { |
- Vector<AtomicString>* role_name_vector = new Vector<AtomicString>(kNumRoles); |
- for (int i = 0; i < kNumRoles; i++) |
- (*role_name_vector)[i] = g_null_atom; |
- |
- for (size_t i = 0; i < WTF_ARRAY_LENGTH(kRoles); ++i) |
- (*role_name_vector)[kRoles[i].webcore_role] = |
- AtomicString(kRoles[i].aria_role); |
- |
- for (size_t i = 0; i < WTF_ARRAY_LENGTH(kReverseRoles); ++i) |
- (*role_name_vector)[kReverseRoles[i].webcore_role] = |
- AtomicString(kReverseRoles[i].aria_role); |
- |
- return role_name_vector; |
-} |
- |
-static Vector<AtomicString>* CreateInternalRoleNameVector() { |
- Vector<AtomicString>* internal_role_name_vector = |
- new Vector<AtomicString>(kNumRoles); |
- for (size_t i = 0; i < WTF_ARRAY_LENGTH(kInternalRoles); i++) |
- (*internal_role_name_vector)[kInternalRoles[i].webcore_role] = |
- AtomicString(kInternalRoles[i].internal_role_name); |
- |
- return internal_role_name_vector; |
-} |
- |
-const char* g_aria_widgets[] = { |
- // From http://www.w3.org/TR/wai-aria/roles#widget_roles |
- "alert", "alertdialog", "button", "checkbox", "dialog", "gridcell", "link", |
- "log", "marquee", "menuitem", "menuitemcheckbox", "menuitemradio", "option", |
- "progressbar", "radio", "scrollbar", "slider", "spinbutton", "status", |
- "tab", "tabpanel", "textbox", "timer", "tooltip", "treeitem", |
- // Composite user interface widgets. |
- // This list is also from the w3.org site referenced above. |
- "combobox", "grid", "listbox", "menu", "menubar", "radiogroup", "tablist", |
- "tree", "treegrid"}; |
- |
-static ARIAWidgetSet* CreateARIARoleWidgetSet() { |
- ARIAWidgetSet* widget_set = new HashSet<String, CaseFoldingHash>(); |
- for (size_t i = 0; i < WTF_ARRAY_LENGTH(g_aria_widgets); ++i) |
- widget_set->insert(String(g_aria_widgets[i])); |
- return widget_set; |
-} |
- |
-const char* g_aria_interactive_widget_attributes[] = { |
- // These attributes implicitly indicate the given widget is interactive. |
- // From http://www.w3.org/TR/wai-aria/states_and_properties#attrs_widgets |
- "aria-activedescendant", "aria-checked", "aria-controls", |
- "aria-disabled", // If it's disabled, it can be made interactive. |
- "aria-expanded", "aria-haspopup", "aria-multiselectable", |
- "aria-pressed", "aria-required", "aria-selected"}; |
- |
-HTMLDialogElement* GetActiveDialogElement(Node* node) { |
- return node->GetDocument().ActiveModalDialog(); |
-} |
- |
-} // namespace |
- |
-unsigned AXObject::number_of_live_ax_objects_ = 0; |
- |
-AXObject::AXObject(AXObjectCacheImpl& ax_object_cache) |
- : id_(0), |
- have_children_(false), |
- role_(kUnknownRole), |
- last_known_is_ignored_value_(kDefaultBehavior), |
- explicit_container_id_(0), |
- parent_(nullptr), |
- last_modification_count_(-1), |
- cached_is_ignored_(false), |
- cached_is_inert_or_aria_hidden_(false), |
- cached_is_descendant_of_leaf_node_(false), |
- cached_is_descendant_of_disabled_node_(false), |
- cached_has_inherited_presentational_role_(false), |
- cached_is_presentational_child_(false), |
- cached_ancestor_exposes_active_descendant_(false), |
- cached_live_region_root_(nullptr), |
- ax_object_cache_(&ax_object_cache) { |
- ++number_of_live_ax_objects_; |
-} |
- |
-AXObject::~AXObject() { |
- DCHECK(IsDetached()); |
- --number_of_live_ax_objects_; |
-} |
- |
-void AXObject::Detach() { |
- // Clear any children and call detachFromParent on them so that |
- // no children are left with dangling pointers to their parent. |
- ClearChildren(); |
- |
- ax_object_cache_ = nullptr; |
-} |
- |
-bool AXObject::IsDetached() const { |
- return !ax_object_cache_; |
-} |
- |
-const AtomicString& AXObject::GetAOMPropertyOrARIAAttribute( |
- AOMStringProperty property) const { |
- Node* node = this->GetNode(); |
- if (!node || !node->IsElementNode()) |
- return g_null_atom; |
- |
- return AccessibleNode::GetPropertyOrARIAAttribute(ToElement(node), property); |
-} |
- |
-bool AXObject::IsARIATextControl() const { |
- return AriaRoleAttribute() == kTextFieldRole || |
- AriaRoleAttribute() == kSearchBoxRole || |
- AriaRoleAttribute() == kComboBoxRole; |
-} |
- |
-bool AXObject::IsButton() const { |
- AccessibilityRole role = RoleValue(); |
- |
- return role == kButtonRole || role == kPopUpButtonRole || |
- role == kToggleButtonRole; |
-} |
- |
-bool AXObject::IsCheckable() const { |
- switch (RoleValue()) { |
- case kCheckBoxRole: |
- case kMenuItemCheckBoxRole: |
- case kMenuItemRadioRole: |
- case kRadioButtonRole: |
- case kSwitchRole: |
- return true; |
- default: |
- return false; |
- } |
-} |
- |
-// Why this is here instead of AXNodeObject: |
-// Because an AXMenuListOption (<option>) can |
-// have an ARIA role of menuitemcheckbox/menuitemradio |
-// yet does not inherit from AXNodeObject |
-AccessibilityButtonState AXObject::CheckedState() const { |
- if (!IsCheckable()) |
- return kButtonStateOff; |
- |
- const AtomicString& checkedAttribute = |
- GetAOMPropertyOrARIAAttribute(AOMStringProperty::kChecked); |
- if (checkedAttribute) { |
- if (EqualIgnoringASCIICase(checkedAttribute, "true")) |
- return kButtonStateOn; |
- |
- if (EqualIgnoringASCIICase(checkedAttribute, "mixed")) { |
- // Only checkboxes and radios should support the mixed state. |
- const AccessibilityRole role = RoleValue(); |
- if (role == kCheckBoxRole || role == kMenuItemCheckBoxRole || |
- role == kRadioButtonRole || role == kMenuItemRadioRole) |
- return kButtonStateMixed; |
- } |
- |
- return kButtonStateOff; |
- } |
- |
- const Node* node = this->GetNode(); |
- if (!node) |
- return kButtonStateOff; |
- |
- if (IsNativeInputInMixedState(node)) |
- return kButtonStateMixed; |
- |
- if (isHTMLInputElement(*node) && |
- toHTMLInputElement(*node).ShouldAppearChecked()) { |
- return kButtonStateOn; |
- } |
- |
- return kButtonStateOff; |
-} |
- |
-bool AXObject::IsNativeInputInMixedState(const Node* node) { |
- if (!isHTMLInputElement(node)) |
- return false; |
- |
- const HTMLInputElement* input = toHTMLInputElement(node); |
- const auto inputType = input->type(); |
- if (inputType != InputTypeNames::checkbox && |
- inputType != InputTypeNames::radio) |
- return false; |
- return input->ShouldAppearIndeterminate(); |
-} |
- |
-bool AXObject::IsLandmarkRelated() const { |
- switch (RoleValue()) { |
- case kApplicationRole: |
- case kArticleRole: |
- case kBannerRole: |
- case kComplementaryRole: |
- case kContentInfoRole: |
- case kFooterRole: |
- case kFormRole: |
- case kMainRole: |
- case kNavigationRole: |
- case kRegionRole: |
- case kSearchRole: |
- return true; |
- default: |
- return false; |
- } |
-} |
- |
-bool AXObject::IsMenuRelated() const { |
- switch (RoleValue()) { |
- case kMenuRole: |
- case kMenuBarRole: |
- case kMenuButtonRole: |
- case kMenuItemRole: |
- case kMenuItemCheckBoxRole: |
- case kMenuItemRadioRole: |
- return true; |
- default: |
- return false; |
- } |
-} |
- |
-bool AXObject::IsPasswordFieldAndShouldHideValue() const { |
- Settings* settings = GetDocument()->GetSettings(); |
- if (!settings || settings->GetAccessibilityPasswordValuesEnabled()) |
- return false; |
- |
- return IsPasswordField(); |
-} |
- |
-bool AXObject::IsClickable() const { |
- switch (RoleValue()) { |
- case kButtonRole: |
- case kCheckBoxRole: |
- case kColorWellRole: |
- case kComboBoxRole: |
- case kImageMapLinkRole: |
- case kLinkRole: |
- case kListBoxOptionRole: |
- case kMenuButtonRole: |
- case kPopUpButtonRole: |
- case kRadioButtonRole: |
- case kSpinButtonRole: |
- case kTabRole: |
- case kTextFieldRole: |
- case kToggleButtonRole: |
- return true; |
- default: |
- return false; |
- } |
-} |
- |
-bool AXObject::AccessibilityIsIgnored() const { |
- UpdateCachedAttributeValuesIfNeeded(); |
- return cached_is_ignored_; |
-} |
- |
-void AXObject::UpdateCachedAttributeValuesIfNeeded() const { |
- if (IsDetached()) |
- return; |
- |
- AXObjectCacheImpl& cache = AxObjectCache(); |
- |
- if (cache.ModificationCount() == last_modification_count_) |
- return; |
- |
- last_modification_count_ = cache.ModificationCount(); |
- cached_background_color_ = ComputeBackgroundColor(); |
- cached_is_inert_or_aria_hidden_ = ComputeIsInertOrAriaHidden(); |
- cached_is_descendant_of_leaf_node_ = (LeafNodeAncestor() != 0); |
- cached_is_descendant_of_disabled_node_ = (DisabledAncestor() != 0); |
- cached_has_inherited_presentational_role_ = |
- (InheritsPresentationalRoleFrom() != 0); |
- cached_is_presentational_child_ = |
- (AncestorForWhichThisIsAPresentationalChild() != 0); |
- cached_is_ignored_ = ComputeAccessibilityIsIgnored(); |
- cached_live_region_root_ = |
- IsLiveRegion() |
- ? const_cast<AXObject*>(this) |
- : (ParentObjectIfExists() ? ParentObjectIfExists()->LiveRegionRoot() |
- : 0); |
- cached_ancestor_exposes_active_descendant_ = |
- ComputeAncestorExposesActiveDescendant(); |
-} |
- |
-bool AXObject::AccessibilityIsIgnoredByDefault( |
- IgnoredReasons* ignored_reasons) const { |
- return DefaultObjectInclusion(ignored_reasons) == kIgnoreObject; |
-} |
- |
-AXObjectInclusion AXObject::AccessibilityPlatformIncludesObject() const { |
- if (IsMenuListPopup() || IsMenuListOption()) |
- return kIncludeObject; |
- |
- return kDefaultBehavior; |
-} |
- |
-AXObjectInclusion AXObject::DefaultObjectInclusion( |
- IgnoredReasons* ignored_reasons) const { |
- if (IsInertOrAriaHidden()) { |
- if (ignored_reasons) |
- ComputeIsInertOrAriaHidden(ignored_reasons); |
- return kIgnoreObject; |
- } |
- |
- if (IsPresentationalChild()) { |
- if (ignored_reasons) { |
- AXObject* ancestor = AncestorForWhichThisIsAPresentationalChild(); |
- ignored_reasons->push_back( |
- IgnoredReason(kAXAncestorDisallowsChild, ancestor)); |
- } |
- return kIgnoreObject; |
- } |
- |
- return AccessibilityPlatformIncludesObject(); |
-} |
- |
-bool AXObject::IsInertOrAriaHidden() const { |
- UpdateCachedAttributeValuesIfNeeded(); |
- return cached_is_inert_or_aria_hidden_; |
-} |
- |
-bool AXObject::ComputeIsInertOrAriaHidden( |
- IgnoredReasons* ignored_reasons) const { |
- if (GetNode()) { |
- if (GetNode()->IsInert()) { |
- if (ignored_reasons) { |
- HTMLDialogElement* dialog = GetActiveDialogElement(GetNode()); |
- if (dialog) { |
- AXObject* dialog_object = AxObjectCache().GetOrCreate(dialog); |
- if (dialog_object) |
- ignored_reasons->push_back( |
- IgnoredReason(kAXActiveModalDialog, dialog_object)); |
- else |
- ignored_reasons->push_back(IgnoredReason(kAXInert)); |
- } else { |
- // TODO(aboxhall): handle inert attribute if it eventuates |
- ignored_reasons->push_back(IgnoredReason(kAXInert)); |
- } |
- } |
- return true; |
- } |
- } else { |
- AXObject* parent = ParentObject(); |
- if (parent && parent->IsInertOrAriaHidden()) { |
- if (ignored_reasons) |
- parent->ComputeIsInertOrAriaHidden(ignored_reasons); |
- return true; |
- } |
- } |
- |
- const AXObject* hidden_root = AriaHiddenRoot(); |
- if (hidden_root) { |
- if (ignored_reasons) { |
- if (hidden_root == this) |
- ignored_reasons->push_back(IgnoredReason(kAXAriaHidden)); |
- else |
- ignored_reasons->push_back( |
- IgnoredReason(kAXAriaHiddenRoot, hidden_root)); |
- } |
- return true; |
- } |
- |
- return false; |
-} |
- |
-bool AXObject::IsDescendantOfLeafNode() const { |
- UpdateCachedAttributeValuesIfNeeded(); |
- return cached_is_descendant_of_leaf_node_; |
-} |
- |
-AXObject* AXObject::LeafNodeAncestor() const { |
- if (AXObject* parent = ParentObject()) { |
- if (!parent->CanHaveChildren()) |
- return parent; |
- |
- return parent->LeafNodeAncestor(); |
- } |
- |
- return 0; |
-} |
- |
-const AXObject* AXObject::AriaHiddenRoot() const { |
- for (const AXObject* object = this; object; object = object->ParentObject()) { |
- if (EqualIgnoringASCIICase(object->GetAttribute(aria_hiddenAttr), "true")) |
- return object; |
- } |
- |
- return 0; |
-} |
- |
-bool AXObject::IsDescendantOfDisabledNode() const { |
- UpdateCachedAttributeValuesIfNeeded(); |
- return cached_is_descendant_of_disabled_node_; |
-} |
- |
-const AXObject* AXObject::DisabledAncestor() const { |
- const AtomicString& disabled = GetAttribute(aria_disabledAttr); |
- if (EqualIgnoringASCIICase(disabled, "true")) |
- return this; |
- if (EqualIgnoringASCIICase(disabled, "false")) |
- return 0; |
- |
- if (AXObject* parent = ParentObject()) |
- return parent->DisabledAncestor(); |
- |
- return 0; |
-} |
- |
-bool AXObject::LastKnownIsIgnoredValue() { |
- if (last_known_is_ignored_value_ == kDefaultBehavior) |
- last_known_is_ignored_value_ = |
- AccessibilityIsIgnored() ? kIgnoreObject : kIncludeObject; |
- |
- return last_known_is_ignored_value_ == kIgnoreObject; |
-} |
- |
-void AXObject::SetLastKnownIsIgnoredValue(bool is_ignored) { |
- last_known_is_ignored_value_ = is_ignored ? kIgnoreObject : kIncludeObject; |
-} |
- |
-bool AXObject::HasInheritedPresentationalRole() const { |
- UpdateCachedAttributeValuesIfNeeded(); |
- return cached_has_inherited_presentational_role_; |
-} |
- |
-bool AXObject::IsPresentationalChild() const { |
- UpdateCachedAttributeValuesIfNeeded(); |
- return cached_is_presentational_child_; |
-} |
- |
-bool AXObject::AncestorExposesActiveDescendant() const { |
- UpdateCachedAttributeValuesIfNeeded(); |
- return cached_ancestor_exposes_active_descendant_; |
-} |
- |
-bool AXObject::ComputeAncestorExposesActiveDescendant() const { |
- const AXObject* parent = ParentObjectUnignored(); |
- if (!parent) |
- return false; |
- |
- if (parent->SupportsActiveDescendant() && |
- !parent->GetAttribute(aria_activedescendantAttr).IsEmpty()) { |
- return true; |
- } |
- |
- return parent->AncestorExposesActiveDescendant(); |
-} |
- |
-// Simplify whitespace, but preserve a single leading and trailing whitespace |
-// character if it's present. |
-// static |
-String AXObject::CollapseWhitespace(const String& str) { |
- StringBuilder result; |
- if (!str.IsEmpty() && IsHTMLSpace<UChar>(str[0])) |
- result.Append(' '); |
- result.Append(str.SimplifyWhiteSpace(IsHTMLSpace<UChar>)); |
- if (!str.IsEmpty() && IsHTMLSpace<UChar>(str[str.length() - 1])) |
- result.Append(' '); |
- return result.ToString(); |
-} |
- |
-String AXObject::ComputedName() const { |
- AXNameFrom name_from; |
- AXObject::AXObjectVector name_objects; |
- return GetName(name_from, &name_objects); |
-} |
- |
-String AXObject::GetName(AXNameFrom& name_from, |
- AXObject::AXObjectVector* name_objects) const { |
- HeapHashSet<Member<const AXObject>> visited; |
- AXRelatedObjectVector related_objects; |
- String text = TextAlternative(false, false, visited, name_from, |
- &related_objects, nullptr); |
- |
- AccessibilityRole role = RoleValue(); |
- if (!GetNode() || (!isHTMLBRElement(GetNode()) && role != kStaticTextRole && |
- role != kInlineTextBoxRole)) |
- text = CollapseWhitespace(text); |
- |
- if (name_objects) { |
- name_objects->clear(); |
- for (size_t i = 0; i < related_objects.size(); i++) |
- name_objects->push_back(related_objects[i]->object); |
- } |
- |
- return text; |
-} |
- |
-String AXObject::GetName(NameSources* name_sources) const { |
- AXObjectSet visited; |
- AXNameFrom tmp_name_from; |
- AXRelatedObjectVector tmp_related_objects; |
- String text = TextAlternative(false, false, visited, tmp_name_from, |
- &tmp_related_objects, name_sources); |
- text = text.SimplifyWhiteSpace(IsHTMLSpace<UChar>); |
- return text; |
-} |
- |
-String AXObject::RecursiveTextAlternative(const AXObject& ax_obj, |
- bool in_aria_labelled_by_traversal, |
- AXObjectSet& visited) { |
- if (visited.Contains(&ax_obj) && !in_aria_labelled_by_traversal) |
- return String(); |
- |
- AXNameFrom tmp_name_from; |
- return ax_obj.TextAlternative(true, in_aria_labelled_by_traversal, visited, |
- tmp_name_from, nullptr, nullptr); |
-} |
- |
-bool AXObject::IsHiddenForTextAlternativeCalculation() const { |
- if (EqualIgnoringASCIICase(GetAttribute(aria_hiddenAttr), "false")) |
- return false; |
- |
- if (GetLayoutObject()) |
- return GetLayoutObject()->Style()->Visibility() != EVisibility::kVisible; |
- |
- // This is an obscure corner case: if a node has no LayoutObject, that means |
- // it's not rendered, but we still may be exploring it as part of a text |
- // alternative calculation, for example if it was explicitly referenced by |
- // aria-labelledby. So we need to explicitly call the style resolver to check |
- // whether it's invisible or display:none, rather than relying on the style |
- // cached in the LayoutObject. |
- Document* document = GetDocument(); |
- if (!document || !document->GetFrame()) |
- return false; |
- if (Node* node = GetNode()) { |
- if (node->isConnected() && node->IsElementNode()) { |
- RefPtr<ComputedStyle> style = |
- document->EnsureStyleResolver().StyleForElement(ToElement(node)); |
- return style->Display() == EDisplay::kNone || |
- style->Visibility() != EVisibility::kVisible; |
- } |
- } |
- return false; |
-} |
- |
-String AXObject::AriaTextAlternative(bool recursive, |
- bool in_aria_labelled_by_traversal, |
- AXObjectSet& visited, |
- AXNameFrom& name_from, |
- AXRelatedObjectVector* related_objects, |
- NameSources* name_sources, |
- bool* found_text_alternative) const { |
- String text_alternative; |
- bool already_visited = visited.Contains(this); |
- visited.insert(this); |
- |
- // Step 2A from: http://www.w3.org/TR/accname-aam-1.1 |
- // If you change this logic, update AXNodeObject::nameFromLabelElement, too. |
- if (!in_aria_labelled_by_traversal && |
- IsHiddenForTextAlternativeCalculation()) { |
- *found_text_alternative = true; |
- return String(); |
- } |
- |
- // Step 2B from: http://www.w3.org/TR/accname-aam-1.1 |
- // If you change this logic, update AXNodeObject::nameFromLabelElement, too. |
- if (!in_aria_labelled_by_traversal && !already_visited) { |
- const QualifiedName& attr = |
- HasAttribute(aria_labeledbyAttr) && !HasAttribute(aria_labelledbyAttr) |
- ? aria_labeledbyAttr |
- : aria_labelledbyAttr; |
- name_from = kAXNameFromRelatedElement; |
- if (name_sources) { |
- name_sources->push_back(NameSource(*found_text_alternative, attr)); |
- name_sources->back().type = name_from; |
- } |
- |
- const AtomicString& aria_labelledby = GetAttribute(attr); |
- if (!aria_labelledby.IsNull()) { |
- if (name_sources) |
- name_sources->back().attribute_value = aria_labelledby; |
- |
- // Operate on a copy of |visited| so that if |nameSources| is not null, |
- // the set of visited objects is preserved unmodified for future |
- // calculations. |
- AXObjectSet visited_copy = visited; |
- text_alternative = TextFromAriaLabelledby(visited_copy, related_objects); |
- if (!text_alternative.IsNull()) { |
- if (name_sources) { |
- NameSource& source = name_sources->back(); |
- source.type = name_from; |
- source.related_objects = *related_objects; |
- source.text = text_alternative; |
- *found_text_alternative = true; |
- } else { |
- *found_text_alternative = true; |
- return text_alternative; |
- } |
- } else if (name_sources) { |
- name_sources->back().invalid = true; |
- } |
- } |
- } |
- |
- // Step 2C from: http://www.w3.org/TR/accname-aam-1.1 |
- // If you change this logic, update AXNodeObject::nameFromLabelElement, too. |
- name_from = kAXNameFromAttribute; |
- if (name_sources) { |
- name_sources->push_back( |
- NameSource(*found_text_alternative, aria_labelAttr)); |
- name_sources->back().type = name_from; |
- } |
- const AtomicString& aria_label = |
- GetAOMPropertyOrARIAAttribute(AOMStringProperty::kLabel); |
- if (!aria_label.IsEmpty()) { |
- text_alternative = aria_label; |
- |
- if (name_sources) { |
- NameSource& source = name_sources->back(); |
- source.text = text_alternative; |
- source.attribute_value = aria_label; |
- *found_text_alternative = true; |
- } else { |
- *found_text_alternative = true; |
- return text_alternative; |
- } |
- } |
- |
- return text_alternative; |
-} |
- |
-String AXObject::TextFromElements( |
- bool in_aria_labelledby_traversal, |
- AXObjectSet& visited, |
- HeapVector<Member<Element>>& elements, |
- AXRelatedObjectVector* related_objects) const { |
- StringBuilder accumulated_text; |
- bool found_valid_element = false; |
- AXRelatedObjectVector local_related_objects; |
- |
- for (const auto& element : elements) { |
- AXObject* ax_element = AxObjectCache().GetOrCreate(element); |
- if (ax_element) { |
- found_valid_element = true; |
- |
- String result = RecursiveTextAlternative( |
- *ax_element, in_aria_labelledby_traversal, visited); |
- local_related_objects.push_back( |
- new NameSourceRelatedObject(ax_element, result)); |
- if (!result.IsEmpty()) { |
- if (!accumulated_text.IsEmpty()) |
- accumulated_text.Append(' '); |
- accumulated_text.Append(result); |
- } |
- } |
- } |
- if (!found_valid_element) |
- return String(); |
- if (related_objects) |
- *related_objects = local_related_objects; |
- return accumulated_text.ToString(); |
-} |
- |
-void AXObject::TokenVectorFromAttribute(Vector<String>& tokens, |
- const QualifiedName& attribute) const { |
- Node* node = this->GetNode(); |
- if (!node || !node->IsElementNode()) |
- return; |
- |
- String attribute_value = GetAttribute(attribute).GetString(); |
- if (attribute_value.IsEmpty()) |
- return; |
- |
- attribute_value.SimplifyWhiteSpace(); |
- attribute_value.Split(' ', tokens); |
-} |
- |
-void AXObject::ElementsFromAttribute(HeapVector<Member<Element>>& elements, |
- const QualifiedName& attribute) const { |
- Vector<String> ids; |
- TokenVectorFromAttribute(ids, attribute); |
- if (ids.IsEmpty()) |
- return; |
- |
- TreeScope& scope = GetNode()->GetTreeScope(); |
- for (const auto& id : ids) { |
- if (Element* id_element = scope.getElementById(AtomicString(id))) |
- elements.push_back(id_element); |
- } |
-} |
- |
-void AXObject::AriaLabelledbyElementVector( |
- HeapVector<Member<Element>>& elements) const { |
- // Try both spellings, but prefer aria-labelledby, which is the official spec. |
- ElementsFromAttribute(elements, aria_labelledbyAttr); |
- if (!elements.size()) |
- ElementsFromAttribute(elements, aria_labeledbyAttr); |
-} |
- |
-String AXObject::TextFromAriaLabelledby( |
- AXObjectSet& visited, |
- AXRelatedObjectVector* related_objects) const { |
- HeapVector<Member<Element>> elements; |
- AriaLabelledbyElementVector(elements); |
- return TextFromElements(true, visited, elements, related_objects); |
-} |
- |
-String AXObject::TextFromAriaDescribedby( |
- AXRelatedObjectVector* related_objects) const { |
- AXObjectSet visited; |
- HeapVector<Member<Element>> elements; |
- ElementsFromAttribute(elements, aria_describedbyAttr); |
- return TextFromElements(true, visited, elements, related_objects); |
-} |
- |
-RGBA32 AXObject::BackgroundColor() const { |
- UpdateCachedAttributeValuesIfNeeded(); |
- return cached_background_color_; |
-} |
- |
-AccessibilityOrientation AXObject::Orientation() const { |
- // In ARIA 1.1, the default value for aria-orientation changed from |
- // horizontal to undefined. |
- return kAccessibilityOrientationUndefined; |
-} |
- |
-AXSupportedAction AXObject::Action() const { |
- if (!ActionElement()) |
- return AXSupportedAction::kNone; |
- |
- switch (RoleValue()) { |
- case kButtonRole: |
- case kToggleButtonRole: |
- return AXSupportedAction::kPress; |
- case kTextFieldRole: |
- return AXSupportedAction::kActivate; |
- case kRadioButtonRole: |
- return AXSupportedAction::kSelect; |
- case kCheckBoxRole: |
- case kSwitchRole: |
- return CheckedState() == kButtonStateOff ? AXSupportedAction::kCheck |
- : AXSupportedAction::kUncheck; |
- case kLinkRole: |
- return AXSupportedAction::kJump; |
- case kPopUpButtonRole: |
- return AXSupportedAction::kOpen; |
- default: |
- return AXSupportedAction::kClick; |
- } |
-} |
- |
-bool AXObject::IsMultiline() const { |
- Node* node = this->GetNode(); |
- if (!node) |
- return false; |
- |
- if (isHTMLTextAreaElement(*node)) |
- return true; |
- |
- if (HasEditableStyle(*node)) |
- return true; |
- |
- if (!IsNativeTextControl() && !IsNonNativeTextControl()) |
- return false; |
- |
- return EqualIgnoringASCIICase(GetAttribute(aria_multilineAttr), "true"); |
-} |
- |
-bool AXObject::AriaPressedIsPresent() const { |
- return !GetAttribute(aria_pressedAttr).IsEmpty(); |
-} |
- |
-bool AXObject::SupportsActiveDescendant() const { |
- // According to the ARIA Spec, all ARIA composite widgets, ARIA text boxes |
- // and ARIA groups should be able to expose an active descendant. |
- // Implicitly, <input> and <textarea> elements should also have this ability. |
- switch (AriaRoleAttribute()) { |
- case kComboBoxRole: |
- case kGridRole: |
- case kGroupRole: |
- case kListBoxRole: |
- case kMenuRole: |
- case kMenuBarRole: |
- case kRadioGroupRole: |
- case kRowRole: |
- case kSearchBoxRole: |
- case kTabListRole: |
- case kTextFieldRole: |
- case kToolbarRole: |
- case kTreeRole: |
- case kTreeGridRole: |
- return true; |
- default: |
- return false; |
- } |
-} |
- |
-bool AXObject::SupportsARIAAttributes() const { |
- return IsLiveRegion() || SupportsARIADragging() || SupportsARIADropping() || |
- SupportsARIAFlowTo() || SupportsARIAOwns() || |
- HasAttribute(aria_labelAttr); |
-} |
- |
-bool AXObject::SupportsRangeValue() const { |
- return IsProgressIndicator() || IsMeter() || IsSlider() || IsScrollbar() || |
- IsSpinButton(); |
-} |
- |
-bool AXObject::SupportsSetSizeAndPosInSet() const { |
- AXObject* parent = ParentObject(); |
- if (!parent) |
- return false; |
- |
- int role = RoleValue(); |
- int parent_role = parent->RoleValue(); |
- |
- if ((role == kListBoxOptionRole && parent_role == kListBoxRole) || |
- (role == kListItemRole && parent_role == kListRole) || |
- (role == kMenuItemRole && parent_role == kMenuRole) || |
- (role == kRadioButtonRole) || |
- (role == kTabRole && parent_role == kTabListRole) || |
- (role == kTreeItemRole && parent_role == kTreeRole) || |
- (role == kTreeItemRole && parent_role == kGroupRole)) { |
- return true; |
- } |
- |
- return false; |
-} |
- |
-int AXObject::IndexInParent() const { |
- if (!ParentObject()) |
- return 0; |
- |
- const auto& siblings = ParentObject()->Children(); |
- int child_count = siblings.size(); |
- |
- for (int index = 0; index < child_count; ++index) { |
- if (siblings[index].Get() == this) { |
- return index; |
- } |
- } |
- return 0; |
-} |
- |
-bool AXObject::IsLiveRegion() const { |
- const AtomicString& live_region = LiveRegionStatus(); |
- return EqualIgnoringASCIICase(live_region, "polite") || |
- EqualIgnoringASCIICase(live_region, "assertive"); |
-} |
- |
-AXObject* AXObject::LiveRegionRoot() const { |
- UpdateCachedAttributeValuesIfNeeded(); |
- return cached_live_region_root_; |
-} |
- |
-const AtomicString& AXObject::ContainerLiveRegionStatus() const { |
- UpdateCachedAttributeValuesIfNeeded(); |
- return cached_live_region_root_ ? cached_live_region_root_->LiveRegionStatus() |
- : g_null_atom; |
-} |
- |
-const AtomicString& AXObject::ContainerLiveRegionRelevant() const { |
- UpdateCachedAttributeValuesIfNeeded(); |
- return cached_live_region_root_ |
- ? cached_live_region_root_->LiveRegionRelevant() |
- : g_null_atom; |
-} |
- |
-bool AXObject::ContainerLiveRegionAtomic() const { |
- UpdateCachedAttributeValuesIfNeeded(); |
- return cached_live_region_root_ && |
- cached_live_region_root_->LiveRegionAtomic(); |
-} |
- |
-bool AXObject::ContainerLiveRegionBusy() const { |
- UpdateCachedAttributeValuesIfNeeded(); |
- return cached_live_region_root_ && cached_live_region_root_->LiveRegionBusy(); |
-} |
- |
-AXObject* AXObject::ElementAccessibilityHitTest(const IntPoint& point) const { |
- // Check if there are any mock elements that need to be handled. |
- for (const auto& child : children_) { |
- if (child->IsMockObject() && |
- child->GetBoundsInFrameCoordinates().Contains(point)) |
- return child->ElementAccessibilityHitTest(point); |
- } |
- |
- return const_cast<AXObject*>(this); |
-} |
- |
-const AXObject::AXObjectVector& AXObject::Children() { |
- UpdateChildrenIfNecessary(); |
- |
- return children_; |
-} |
- |
-AXObject* AXObject::ParentObject() const { |
- if (IsDetached()) |
- return 0; |
- |
- if (parent_) |
- return parent_; |
- |
- if (AxObjectCache().IsAriaOwned(this)) |
- return AxObjectCache().GetAriaOwnedParent(this); |
- |
- return ComputeParent(); |
-} |
- |
-AXObject* AXObject::ParentObjectIfExists() const { |
- if (IsDetached()) |
- return 0; |
- |
- if (parent_) |
- return parent_; |
- |
- return ComputeParentIfExists(); |
-} |
- |
-AXObject* AXObject::ParentObjectUnignored() const { |
- AXObject* parent; |
- for (parent = ParentObject(); parent && parent->AccessibilityIsIgnored(); |
- parent = parent->ParentObject()) { |
- } |
- |
- return parent; |
-} |
- |
-void AXObject::UpdateChildrenIfNecessary() { |
- if (!HasChildren()) |
- AddChildren(); |
-} |
- |
-void AXObject::ClearChildren() { |
- // Detach all weak pointers from objects to their parents. |
- for (const auto& child : children_) |
- child->DetachFromParent(); |
- |
- children_.clear(); |
- have_children_ = false; |
-} |
- |
-Document* AXObject::GetDocument() const { |
- FrameView* frame_view = DocumentFrameView(); |
- if (!frame_view) |
- return 0; |
- |
- return frame_view->GetFrame().GetDocument(); |
-} |
- |
-FrameView* AXObject::DocumentFrameView() const { |
- const AXObject* object = this; |
- while (object && !object->IsAXLayoutObject()) |
- object = object->ParentObject(); |
- |
- if (!object) |
- return 0; |
- |
- return object->DocumentFrameView(); |
-} |
- |
-String AXObject::Language() const { |
- const AtomicString& lang = GetAttribute(langAttr); |
- if (!lang.IsEmpty()) |
- return lang; |
- |
- AXObject* parent = ParentObject(); |
- |
- // As a last resort, fall back to the content language specified in the meta |
- // tag. |
- if (!parent) { |
- Document* doc = GetDocument(); |
- if (doc) |
- return doc->ContentLanguage(); |
- return g_null_atom; |
- } |
- |
- return parent->Language(); |
-} |
- |
-bool AXObject::HasAttribute(const QualifiedName& attribute) const { |
- Node* element_node = GetNode(); |
- if (!element_node) |
- return false; |
- |
- if (!element_node->IsElementNode()) |
- return false; |
- |
- Element* element = ToElement(element_node); |
- return element->FastHasAttribute(attribute); |
-} |
- |
-const AtomicString& AXObject::GetAttribute( |
- const QualifiedName& attribute) const { |
- Node* element_node = GetNode(); |
- if (!element_node) |
- return g_null_atom; |
- |
- if (!element_node->IsElementNode()) |
- return g_null_atom; |
- |
- Element* element = ToElement(element_node); |
- return element->FastGetAttribute(attribute); |
-} |
- |
-// |
-// Scrollable containers. |
-// |
- |
-bool AXObject::IsScrollableContainer() const { |
- return !!GetScrollableAreaIfScrollable(); |
-} |
- |
-IntPoint AXObject::GetScrollOffset() const { |
- ScrollableArea* area = GetScrollableAreaIfScrollable(); |
- if (!area) |
- return IntPoint(); |
- |
- return IntPoint(area->ScrollOffsetInt().Width(), |
- area->ScrollOffsetInt().Height()); |
-} |
- |
-IntPoint AXObject::MinimumScrollOffset() const { |
- ScrollableArea* area = GetScrollableAreaIfScrollable(); |
- if (!area) |
- return IntPoint(); |
- |
- return IntPoint(area->MinimumScrollOffsetInt().Width(), |
- area->MinimumScrollOffsetInt().Height()); |
-} |
- |
-IntPoint AXObject::MaximumScrollOffset() const { |
- ScrollableArea* area = GetScrollableAreaIfScrollable(); |
- if (!area) |
- return IntPoint(); |
- |
- return IntPoint(area->MaximumScrollOffsetInt().Width(), |
- area->MaximumScrollOffsetInt().Height()); |
-} |
- |
-void AXObject::SetScrollOffset(const IntPoint& offset) const { |
- ScrollableArea* area = GetScrollableAreaIfScrollable(); |
- if (!area) |
- return; |
- |
- // TODO(bokan): This should potentially be a UserScroll. |
- area->SetScrollOffset(ScrollOffset(offset.X(), offset.Y()), |
- kProgrammaticScroll); |
-} |
- |
-void AXObject::GetRelativeBounds(AXObject** out_container, |
- FloatRect& out_bounds_in_container, |
- SkMatrix44& out_container_transform) const { |
- *out_container = nullptr; |
- out_bounds_in_container = FloatRect(); |
- out_container_transform.setIdentity(); |
- |
- // First check if it has explicit bounds, for example if this element is tied |
- // to a canvas path. When explicit coordinates are provided, the ID of the |
- // explicit container element that the coordinates are relative to must be |
- // provided too. |
- if (!explicit_element_rect_.IsEmpty()) { |
- *out_container = AxObjectCache().ObjectFromAXID(explicit_container_id_); |
- if (*out_container) { |
- out_bounds_in_container = FloatRect(explicit_element_rect_); |
- return; |
- } |
- } |
- |
- LayoutObject* layout_object = LayoutObjectForRelativeBounds(); |
- if (!layout_object) |
- return; |
- |
- if (IsWebArea()) { |
- if (layout_object->GetFrame()->View()) |
- out_bounds_in_container.SetSize( |
- FloatSize(layout_object->GetFrame()->View()->ContentsSize())); |
- return; |
- } |
- |
- // First compute the container. The container must be an ancestor in the |
- // accessibility tree, and its LayoutObject must be an ancestor in the layout |
- // tree. Get the first such ancestor that's either scrollable or has a paint |
- // layer. |
- AXObject* container = ParentObjectUnignored(); |
- LayoutObject* container_layout_object = nullptr; |
- while (container) { |
- container_layout_object = container->GetLayoutObject(); |
- if (container_layout_object && |
- container_layout_object->IsBoxModelObject() && |
- layout_object->IsDescendantOf(container_layout_object)) { |
- if (container->IsScrollableContainer() || |
- container_layout_object->HasLayer()) |
- break; |
- } |
- |
- container = container->ParentObjectUnignored(); |
- } |
- |
- if (!container) |
- return; |
- *out_container = container; |
- out_bounds_in_container = |
- layout_object->LocalBoundingBoxRectForAccessibility(); |
- |
- // If the container has a scroll offset, subtract that out because we want our |
- // bounds to be relative to the *unscrolled* position of the container object. |
- ScrollableArea* scrollable_area = container->GetScrollableAreaIfScrollable(); |
- if (scrollable_area && !container->IsWebArea()) { |
- ScrollOffset scroll_offset = scrollable_area->GetScrollOffset(); |
- out_bounds_in_container.Move(scroll_offset); |
- } |
- |
- // Compute the transform between the container's coordinate space and this |
- // object. If the transform is just a simple translation, apply that to the |
- // bounding box, but if it's a non-trivial transformation like a rotation, |
- // scaling, etc. then return the full matrix instead. |
- TransformationMatrix transform = layout_object->LocalToAncestorTransform( |
- ToLayoutBoxModelObject(container_layout_object)); |
- if (transform.IsIdentityOr2DTranslation()) { |
- out_bounds_in_container.Move(transform.To2DTranslation()); |
- } else { |
- out_container_transform = TransformationMatrix::ToSkMatrix44(transform); |
- } |
-} |
- |
-LayoutRect AXObject::GetBoundsInFrameCoordinates() const { |
- AXObject* container = nullptr; |
- FloatRect bounds; |
- SkMatrix44 transform; |
- GetRelativeBounds(&container, bounds, transform); |
- FloatRect computed_bounds(0, 0, bounds.Width(), bounds.Height()); |
- while (container && container != this) { |
- computed_bounds.Move(bounds.X(), bounds.Y()); |
- if (!container->IsWebArea()) { |
- computed_bounds.Move(-container->GetScrollOffset().X(), |
- -container->GetScrollOffset().Y()); |
- } |
- if (!transform.isIdentity()) { |
- TransformationMatrix transformation_matrix(transform); |
- transformation_matrix.MapRect(computed_bounds); |
- } |
- container->GetRelativeBounds(&container, bounds, transform); |
- } |
- return LayoutRect(computed_bounds); |
-} |
- |
-// |
-// Modify or take an action on an object. |
-// |
- |
-bool AXObject::Press() { |
- Document* document = GetDocument(); |
- if (!document) |
- return false; |
- |
- UserGestureIndicator gesture_indicator(DocumentUserGestureToken::Create( |
- document, UserGestureToken::kNewGesture)); |
- Element* action_elem = ActionElement(); |
- if (action_elem) { |
- action_elem->AccessKeyAction(true); |
- return true; |
- } |
- |
- if (CanSetFocusAttribute()) { |
- SetFocused(true); |
- return true; |
- } |
- |
- return false; |
-} |
- |
-void AXObject::ScrollToMakeVisible() const { |
- IntRect object_rect = PixelSnappedIntRect(GetBoundsInFrameCoordinates()); |
- object_rect.SetLocation(IntPoint()); |
- ScrollToMakeVisibleWithSubFocus(object_rect); |
-} |
- |
-// This is a 1-dimensional scroll offset helper function that's applied |
-// separately in the horizontal and vertical directions, because the |
-// logic is the same. The goal is to compute the best scroll offset |
-// in order to make an object visible within a viewport. |
-// |
-// If the object is already fully visible, returns the same scroll |
-// offset. |
-// |
-// In case the whole object cannot fit, you can specify a |
-// subfocus - a smaller region within the object that should |
-// be prioritized. If the whole object can fit, the subfocus is |
-// ignored. |
-// |
-// If possible, the object and subfocus are centered within the |
-// viewport. |
-// |
-// Example 1: the object is already visible, so nothing happens. |
-// +----------Viewport---------+ |
-// +---Object---+ |
-// +--SubFocus--+ |
-// |
-// Example 2: the object is not fully visible, so it's centered |
-// within the viewport. |
-// Before: |
-// +----------Viewport---------+ |
-// +---Object---+ |
-// +--SubFocus--+ |
-// |
-// After: |
-// +----------Viewport---------+ |
-// +---Object---+ |
-// +--SubFocus--+ |
-// |
-// Example 3: the object is larger than the viewport, so the |
-// viewport moves to show as much of the object as possible, |
-// while also trying to center the subfocus. |
-// Before: |
-// +----------Viewport---------+ |
-// +---------------Object--------------+ |
-// +-SubFocus-+ |
-// |
-// After: |
-// +----------Viewport---------+ |
-// +---------------Object--------------+ |
-// +-SubFocus-+ |
-// |
-// When constraints cannot be fully satisfied, the min |
-// (left/top) position takes precedence over the max (right/bottom). |
-// |
-// Note that the return value represents the ideal new scroll offset. |
-// This may be out of range - the calling function should clip this |
-// to the available range. |
-static int ComputeBestScrollOffset(int current_scroll_offset, |
- int subfocus_min, |
- int subfocus_max, |
- int object_min, |
- int object_max, |
- int viewport_min, |
- int viewport_max) { |
- int viewport_size = viewport_max - viewport_min; |
- |
- // If the object size is larger than the viewport size, consider |
- // only a portion that's as large as the viewport, centering on |
- // the subfocus as much as possible. |
- if (object_max - object_min > viewport_size) { |
- // Since it's impossible to fit the whole object in the |
- // viewport, exit now if the subfocus is already within the viewport. |
- if (subfocus_min - current_scroll_offset >= viewport_min && |
- subfocus_max - current_scroll_offset <= viewport_max) |
- return current_scroll_offset; |
- |
- // Subfocus must be within focus. |
- subfocus_min = std::max(subfocus_min, object_min); |
- subfocus_max = std::min(subfocus_max, object_max); |
- |
- // Subfocus must be no larger than the viewport size; favor top/left. |
- if (subfocus_max - subfocus_min > viewport_size) |
- subfocus_max = subfocus_min + viewport_size; |
- |
- // Compute the size of an object centered on the subfocus, the size of the |
- // viewport. |
- int centered_object_min = (subfocus_min + subfocus_max - viewport_size) / 2; |
- int centered_object_max = centered_object_min + viewport_size; |
- |
- object_min = std::max(object_min, centered_object_min); |
- object_max = std::min(object_max, centered_object_max); |
- } |
- |
- // Exit now if the focus is already within the viewport. |
- if (object_min - current_scroll_offset >= viewport_min && |
- object_max - current_scroll_offset <= viewport_max) |
- return current_scroll_offset; |
- |
- // Center the object in the viewport. |
- return (object_min + object_max - viewport_min - viewport_max) / 2; |
-} |
- |
-void AXObject::ScrollToMakeVisibleWithSubFocus(const IntRect& subfocus) const { |
- // Search up the parent chain until we find the first one that's scrollable. |
- const AXObject* scroll_parent = ParentObject() ? ParentObject() : this; |
- ScrollableArea* scrollable_area = 0; |
- while (scroll_parent) { |
- scrollable_area = scroll_parent->GetScrollableAreaIfScrollable(); |
- if (scrollable_area) |
- break; |
- scroll_parent = scroll_parent->ParentObject(); |
- } |
- if (!scroll_parent || !scrollable_area) |
- return; |
- |
- IntRect object_rect = PixelSnappedIntRect(GetBoundsInFrameCoordinates()); |
- IntSize scroll_offset = scrollable_area->ScrollOffsetInt(); |
- IntRect scroll_visible_rect = scrollable_area->VisibleContentRect(); |
- |
- // Convert the object rect into local coordinates. |
- if (!scroll_parent->IsWebArea()) { |
- object_rect.MoveBy(IntPoint(scroll_offset)); |
- object_rect.MoveBy( |
- -PixelSnappedIntRect(scroll_parent->GetBoundsInFrameCoordinates()) |
- .Location()); |
- } |
- |
- int desired_x = ComputeBestScrollOffset( |
- scroll_offset.Width(), object_rect.X() + subfocus.X(), |
- object_rect.X() + subfocus.MaxX(), object_rect.X(), object_rect.MaxX(), 0, |
- scroll_visible_rect.Width()); |
- int desired_y = ComputeBestScrollOffset( |
- scroll_offset.Height(), object_rect.Y() + subfocus.Y(), |
- object_rect.Y() + subfocus.MaxY(), object_rect.Y(), object_rect.MaxY(), 0, |
- scroll_visible_rect.Height()); |
- |
- scroll_parent->SetScrollOffset(IntPoint(desired_x, desired_y)); |
- |
- // Convert the subfocus into the coordinates of the scroll parent. |
- IntRect new_subfocus = subfocus; |
- IntRect new_element_rect = PixelSnappedIntRect(GetBoundsInFrameCoordinates()); |
- IntRect scroll_parent_rect = |
- PixelSnappedIntRect(scroll_parent->GetBoundsInFrameCoordinates()); |
- new_subfocus.Move(new_element_rect.X(), new_element_rect.Y()); |
- new_subfocus.Move(-scroll_parent_rect.X(), -scroll_parent_rect.Y()); |
- |
- if (scroll_parent->ParentObject()) { |
- // Recursively make sure the scroll parent itself is visible. |
- scroll_parent->ScrollToMakeVisibleWithSubFocus(new_subfocus); |
- } else { |
- // To minimize the number of notifications, only fire one on the topmost |
- // object that has been scrolled. |
- AxObjectCache().PostNotification(const_cast<AXObject*>(this), |
- AXObjectCacheImpl::kAXLocationChanged); |
- } |
-} |
- |
-void AXObject::ScrollToGlobalPoint(const IntPoint& global_point) const { |
- // Search up the parent chain and create a vector of all scrollable parent |
- // objects and ending with this object itself. |
- HeapVector<Member<const AXObject>> objects; |
- AXObject* parent_object; |
- for (parent_object = this->ParentObject(); parent_object; |
- parent_object = parent_object->ParentObject()) { |
- if (parent_object->GetScrollableAreaIfScrollable()) |
- objects.push_front(parent_object); |
- } |
- objects.push_back(this); |
- |
- // Start with the outermost scrollable (the main window) and try to scroll the |
- // next innermost object to the given point. |
- int offset_x = 0, offset_y = 0; |
- IntPoint point = global_point; |
- size_t levels = objects.size() - 1; |
- for (size_t i = 0; i < levels; i++) { |
- const AXObject* outer = objects[i]; |
- const AXObject* inner = objects[i + 1]; |
- ScrollableArea* scrollable_area = outer->GetScrollableAreaIfScrollable(); |
- |
- IntRect inner_rect = |
- inner->IsWebArea() |
- ? PixelSnappedIntRect( |
- inner->ParentObject()->GetBoundsInFrameCoordinates()) |
- : PixelSnappedIntRect(inner->GetBoundsInFrameCoordinates()); |
- IntRect object_rect = inner_rect; |
- IntSize scroll_offset = scrollable_area->ScrollOffsetInt(); |
- |
- // Convert the object rect into local coordinates. |
- object_rect.Move(offset_x, offset_y); |
- if (!outer->IsWebArea()) |
- object_rect.Move(scroll_offset.Width(), scroll_offset.Height()); |
- |
- int desired_x = ComputeBestScrollOffset( |
- 0, object_rect.X(), object_rect.MaxX(), object_rect.X(), |
- object_rect.MaxX(), point.X(), point.X()); |
- int desired_y = ComputeBestScrollOffset( |
- 0, object_rect.Y(), object_rect.MaxY(), object_rect.Y(), |
- object_rect.MaxY(), point.Y(), point.Y()); |
- outer->SetScrollOffset(IntPoint(desired_x, desired_y)); |
- |
- if (outer->IsWebArea() && !inner->IsWebArea()) { |
- // If outer object we just scrolled is a web area (frame) but the inner |
- // object is not, keep track of the coordinate transformation to apply to |
- // future nested calculations. |
- scroll_offset = scrollable_area->ScrollOffsetInt(); |
- offset_x -= (scroll_offset.Width() + point.X()); |
- offset_y -= (scroll_offset.Height() + point.Y()); |
- point.Move(scroll_offset.Width() - inner_rect.Width(), |
- scroll_offset.Height() - inner_rect.Y()); |
- } else if (inner->IsWebArea()) { |
- // Otherwise, if the inner object is a web area, reset the coordinate |
- // transformation. |
- offset_x = 0; |
- offset_y = 0; |
- } |
- } |
- |
- // To minimize the number of notifications, only fire one on the topmost |
- // object that has been scrolled. |
- DCHECK(objects[0]); |
- // TODO(nektar): Switch to postNotification(objects[0] and remove |getNode|. |
- AxObjectCache().PostNotification(objects[0]->GetNode(), |
- AXObjectCacheImpl::kAXLocationChanged); |
-} |
- |
-void AXObject::SetSequentialFocusNavigationStartingPoint() { |
- // Call it on the nearest ancestor that overrides this with a specific |
- // implementation. |
- if (ParentObject()) |
- ParentObject()->SetSequentialFocusNavigationStartingPoint(); |
-} |
- |
-void AXObject::NotifyIfIgnoredValueChanged() { |
- bool is_ignored = AccessibilityIsIgnored(); |
- if (LastKnownIsIgnoredValue() != is_ignored) { |
- AxObjectCache().ChildrenChanged(ParentObject()); |
- SetLastKnownIsIgnoredValue(is_ignored); |
- } |
-} |
- |
-void AXObject::SelectionChanged() { |
- if (AXObject* parent = ParentObjectIfExists()) |
- parent->SelectionChanged(); |
-} |
- |
-int AXObject::LineForPosition(const VisiblePosition& position) const { |
- if (position.IsNull() || !GetNode()) |
- return -1; |
- |
- // If the position is not in the same editable region as this AX object, |
- // return -1. |
- Node* container_node = position.DeepEquivalent().ComputeContainerNode(); |
- if (!container_node->IsShadowIncludingInclusiveAncestorOf(GetNode()) && |
- !GetNode()->IsShadowIncludingInclusiveAncestorOf(container_node)) |
- return -1; |
- |
- int line_count = -1; |
- VisiblePosition current_position = position; |
- VisiblePosition previous_position; |
- |
- // Move up until we get to the top. |
- // FIXME: This only takes us to the top of the rootEditableElement, not the |
- // top of the top document. |
- do { |
- previous_position = current_position; |
- current_position = PreviousLinePosition(current_position, LayoutUnit(), |
- kHasEditableAXRole); |
- ++line_count; |
- } while (current_position.IsNotNull() && |
- !InSameLine(current_position, previous_position)); |
- |
- return line_count; |
-} |
- |
-bool AXObject::IsARIAControl(AccessibilityRole aria_role) { |
- return IsARIAInput(aria_role) || aria_role == kButtonRole || |
- aria_role == kComboBoxRole || aria_role == kSliderRole; |
-} |
- |
-bool AXObject::IsARIAInput(AccessibilityRole aria_role) { |
- return aria_role == kRadioButtonRole || aria_role == kCheckBoxRole || |
- aria_role == kTextFieldRole || aria_role == kSwitchRole || |
- aria_role == kSearchBoxRole; |
-} |
- |
-AccessibilityRole AXObject::AriaRoleToWebCoreRole(const String& value) { |
- DCHECK(!value.IsEmpty()); |
- |
- static const ARIARoleMap* role_map = CreateARIARoleMap(); |
- |
- Vector<String> role_vector; |
- value.Split(' ', role_vector); |
- AccessibilityRole role = kUnknownRole; |
- for (const auto& child : role_vector) { |
- role = role_map->at(child); |
- if (role) |
- return role; |
- } |
- |
- return role; |
-} |
- |
-bool AXObject::IsInsideFocusableElementOrARIAWidget(const Node& node) { |
- const Node* cur_node = &node; |
- do { |
- if (cur_node->IsElementNode()) { |
- const Element* element = ToElement(cur_node); |
- if (element->IsFocusable()) |
- return true; |
- String role = element->getAttribute("role"); |
- if (!role.IsEmpty() && AXObject::IncludesARIAWidgetRole(role)) |
- return true; |
- if (HasInteractiveARIAAttribute(*element)) |
- return true; |
- } |
- cur_node = cur_node->parentNode(); |
- } while (cur_node && !isHTMLBodyElement(node)); |
- return false; |
-} |
- |
-bool AXObject::HasInteractiveARIAAttribute(const Element& element) { |
- for (size_t i = 0; i < WTF_ARRAY_LENGTH(g_aria_interactive_widget_attributes); |
- ++i) { |
- const char* attribute = g_aria_interactive_widget_attributes[i]; |
- if (element.hasAttribute(attribute)) { |
- return true; |
- } |
- } |
- return false; |
-} |
- |
-bool AXObject::IncludesARIAWidgetRole(const String& role) { |
- static const HashSet<String, CaseFoldingHash>* role_set = |
- CreateARIARoleWidgetSet(); |
- |
- Vector<String> role_vector; |
- role.Split(' ', role_vector); |
- for (const auto& child : role_vector) { |
- if (role_set->Contains(child)) |
- return true; |
- } |
- return false; |
-} |
- |
-bool AXObject::NameFromContents() const { |
- // ARIA 1.1, section 5.2.7.5. |
- switch (RoleValue()) { |
- case kAnchorRole: |
- case kButtonRole: |
- case kCellRole: |
- case kCheckBoxRole: |
- case kColumnHeaderRole: |
- case kDirectoryRole: |
- case kDisclosureTriangleRole: |
- case kHeadingRole: |
- case kLineBreakRole: |
- case kLinkRole: |
- case kListBoxOptionRole: |
- case kListItemRole: |
- case kMenuItemRole: |
- case kMenuItemCheckBoxRole: |
- case kMenuItemRadioRole: |
- case kMenuListOptionRole: |
- case kPopUpButtonRole: |
- case kRadioButtonRole: |
- case kRowHeaderRole: |
- case kStaticTextRole: |
- case kStatusRole: |
- case kSwitchRole: |
- case kTabRole: |
- case kToggleButtonRole: |
- case kTreeItemRole: |
- case kUserInterfaceTooltipRole: |
- return true; |
- case kRowRole: { |
- // Spec says we should always expose the name on rows, |
- // but for performance reasons we only do it |
- // if the row might receive focus |
- if (AncestorExposesActiveDescendant()) { |
- return true; |
- } |
- const Node* node = this->GetNode(); |
- return node && node->IsElementNode() && ToElement(node)->IsFocusable(); |
- } |
- default: |
- return false; |
- } |
-} |
- |
-AccessibilityRole AXObject::ButtonRoleType() const { |
- // If aria-pressed is present, then it should be exposed as a toggle button. |
- // http://www.w3.org/TR/wai-aria/states_and_properties#aria-pressed |
- if (AriaPressedIsPresent()) |
- return kToggleButtonRole; |
- if (AriaHasPopup()) |
- return kPopUpButtonRole; |
- // We don't contemplate RadioButtonRole, as it depends on the input |
- // type. |
- |
- return kButtonRole; |
-} |
- |
-const AtomicString& AXObject::RoleName(AccessibilityRole role) { |
- static const Vector<AtomicString>* role_name_vector = CreateRoleNameVector(); |
- |
- return role_name_vector->at(role); |
-} |
- |
-const AtomicString& AXObject::InternalRoleName(AccessibilityRole role) { |
- static const Vector<AtomicString>* internal_role_name_vector = |
- CreateInternalRoleNameVector(); |
- |
- return internal_role_name_vector->at(role); |
-} |
- |
-DEFINE_TRACE(AXObject) { |
- visitor->Trace(children_); |
- visitor->Trace(parent_); |
- visitor->Trace(cached_live_region_root_); |
- visitor->Trace(ax_object_cache_); |
-} |
- |
-} // namespace blink |