tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

HTMLElementAccessibles.cpp (10016B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "HTMLElementAccessibles.h"
      7 
      8 #include "CacheConstants.h"
      9 #include "nsCoreUtils.h"
     10 #include "nsTextEquivUtils.h"
     11 #include "Relation.h"
     12 #include "mozilla/a11y/Role.h"
     13 #include "States.h"
     14 
     15 #include "mozilla/dom/HTMLLabelElement.h"
     16 #include "mozilla/dom/HTMLDetailsElement.h"
     17 #include "mozilla/dom/HTMLSummaryElement.h"
     18 
     19 using namespace mozilla::a11y;
     20 
     21 ////////////////////////////////////////////////////////////////////////////////
     22 // HTMLHRAccessible
     23 ////////////////////////////////////////////////////////////////////////////////
     24 
     25 role HTMLHRAccessible::NativeRole() const { return roles::SEPARATOR; }
     26 
     27 ////////////////////////////////////////////////////////////////////////////////
     28 // HTMLBRAccessible
     29 ////////////////////////////////////////////////////////////////////////////////
     30 
     31 role HTMLBRAccessible::NativeRole() const { return roles::WHITESPACE; }
     32 
     33 uint64_t HTMLBRAccessible::NativeState() const { return states::READONLY; }
     34 
     35 ENameValueFlag HTMLBRAccessible::NativeName(nsString& aName) const {
     36  aName = static_cast<char16_t>('\n');  // Newline char
     37  return eNameOK;
     38 }
     39 
     40 ////////////////////////////////////////////////////////////////////////////////
     41 // HTMLLabelAccessible
     42 ////////////////////////////////////////////////////////////////////////////////
     43 
     44 ENameValueFlag HTMLLabelAccessible::NativeName(nsString& aName) const {
     45  return eNameOK;
     46 }
     47 
     48 Relation HTMLLabelAccessible::RelationByType(RelationType aType) const {
     49  Relation rel = AccessibleWrap::RelationByType(aType);
     50  if (aType == RelationType::LABEL_FOR) {
     51    dom::HTMLLabelElement* label = dom::HTMLLabelElement::FromNode(mContent);
     52    rel.AppendTarget(mDoc, label->GetControl());
     53  }
     54 
     55  return rel;
     56 }
     57 
     58 void HTMLLabelAccessible::DOMAttributeChanged(int32_t aNameSpaceID,
     59                                              nsAtom* aAttribute,
     60                                              AttrModType aModType,
     61                                              const nsAttrValue* aOldValue,
     62                                              uint64_t aOldState) {
     63  HyperTextAccessible::DOMAttributeChanged(aNameSpaceID, aAttribute, aModType,
     64                                           aOldValue, aOldState);
     65 
     66  if (aAttribute == nsGkAtoms::_for) {
     67    mDoc->QueueCacheUpdate(this, CacheDomain::Relations | CacheDomain::Actions);
     68  }
     69 }
     70 
     71 bool HTMLLabelAccessible::HasPrimaryAction() const {
     72  return nsCoreUtils::IsLabelWithControl(mContent);
     73 }
     74 
     75 void HTMLLabelAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) {
     76  if (aIndex == 0) {
     77    if (HasPrimaryAction()) {
     78      aName.AssignLiteral("click");
     79    }
     80  }
     81 }
     82 
     83 ////////////////////////////////////////////////////////////////////////////////
     84 // nsHTMLOuputAccessible
     85 ////////////////////////////////////////////////////////////////////////////////
     86 
     87 Relation HTMLOutputAccessible::RelationByType(RelationType aType) const {
     88  Relation rel = AccessibleWrap::RelationByType(aType);
     89  if (aType == RelationType::CONTROLLED_BY) {
     90    rel.AppendIter(
     91        new AssociatedElementsIterator(mDoc, mContent, nsGkAtoms::_for));
     92  }
     93 
     94  return rel;
     95 }
     96 
     97 void HTMLOutputAccessible::DOMAttributeChanged(int32_t aNameSpaceID,
     98                                               nsAtom* aAttribute,
     99                                               AttrModType aModType,
    100                                               const nsAttrValue* aOldValue,
    101                                               uint64_t aOldState) {
    102  HyperTextAccessible::DOMAttributeChanged(aNameSpaceID, aAttribute, aModType,
    103                                           aOldValue, aOldState);
    104 
    105  if (aAttribute == nsGkAtoms::_for) {
    106    mDoc->QueueCacheUpdate(this, CacheDomain::Relations);
    107  }
    108 }
    109 
    110 ////////////////////////////////////////////////////////////////////////////////
    111 // HTMLSummaryAccessible
    112 ////////////////////////////////////////////////////////////////////////////////
    113 
    114 HTMLSummaryAccessible::HTMLSummaryAccessible(nsIContent* aContent,
    115                                             DocAccessible* aDoc)
    116    : HyperTextAccessible(aContent, aDoc) {
    117  mGenericTypes |= eButton;
    118 }
    119 
    120 bool HTMLSummaryAccessible::HasPrimaryAction() const { return true; }
    121 
    122 void HTMLSummaryAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) {
    123  if (aIndex != eAction_Click) {
    124    return;
    125  }
    126 
    127  dom::HTMLSummaryElement* summary =
    128      dom::HTMLSummaryElement::FromNode(mContent);
    129  if (!summary) {
    130    return;
    131  }
    132 
    133  dom::HTMLDetailsElement* details = summary->GetDetails();
    134  if (!details) {
    135    return;
    136  }
    137 
    138  if (details->Open()) {
    139    aName.AssignLiteral("collapse");
    140  } else {
    141    aName.AssignLiteral("expand");
    142  }
    143 }
    144 
    145 uint64_t HTMLSummaryAccessible::NativeState() const {
    146  uint64_t state = HyperTextAccessible::NativeState();
    147 
    148  dom::HTMLSummaryElement* summary =
    149      dom::HTMLSummaryElement::FromNode(mContent);
    150  if (!summary) {
    151    return state;
    152  }
    153 
    154  dom::HTMLDetailsElement* details = summary->GetDetails();
    155  if (!details) {
    156    return state;
    157  }
    158 
    159  state |= states::EXPANDABLE;
    160 
    161  if (details->Open()) {
    162    state |= states::EXPANDED;
    163  }
    164 
    165  return state;
    166 }
    167 
    168 HTMLSummaryAccessible* HTMLSummaryAccessible::FromDetails(
    169    LocalAccessible* details) {
    170  if (!dom::HTMLDetailsElement::FromNodeOrNull(details->GetContent())) {
    171    return nullptr;
    172  }
    173 
    174  HTMLSummaryAccessible* summaryAccessible = nullptr;
    175  for (uint32_t i = 0; i < details->ChildCount(); i++) {
    176    // Iterate through the children of our details accessible to locate main
    177    // summary. This iteration includes the anonymous summary if the details
    178    // element was not explicitly created with one.
    179    LocalAccessible* child = details->LocalChildAt(i);
    180    auto* summary =
    181        mozilla::dom::HTMLSummaryElement::FromNodeOrNull(child->GetContent());
    182    if (summary && summary->IsMainSummary()) {
    183      summaryAccessible = static_cast<HTMLSummaryAccessible*>(child);
    184      break;
    185    }
    186  }
    187 
    188  return summaryAccessible;
    189 }
    190 
    191 ////////////////////////////////////////////////////////////////////////////////
    192 // HTMLSummaryAccessible: Widgets
    193 
    194 bool HTMLSummaryAccessible::IsWidget() const { return true; }
    195 
    196 ////////////////////////////////////////////////////////////////////////////////
    197 // HTMLHeaderOrFooterAccessible
    198 ////////////////////////////////////////////////////////////////////////////////
    199 
    200 role HTMLHeaderOrFooterAccessible::NativeRole() const {
    201  // Only map header and footer if they are direct descendants of the body tag.
    202  // If other sectioning or sectioning root elements, they become sections.
    203  nsIContent* parent = mContent->GetParent();
    204  while (parent) {
    205    if (parent->IsAnyOfHTMLElements(
    206            nsGkAtoms::article, nsGkAtoms::aside, nsGkAtoms::nav,
    207            nsGkAtoms::section, nsGkAtoms::main, nsGkAtoms::blockquote,
    208            nsGkAtoms::details, nsGkAtoms::dialog, nsGkAtoms::fieldset,
    209            nsGkAtoms::figure, nsGkAtoms::td)) {
    210      break;
    211    }
    212    parent = parent->GetParent();
    213  }
    214 
    215  // No sectioning or sectioning root elements found.
    216  if (!parent) {
    217    return roles::LANDMARK;
    218  }
    219 
    220  return roles::SECTION;
    221 }
    222 
    223 ////////////////////////////////////////////////////////////////////////////////
    224 // HTMLAsideAccessible
    225 ////////////////////////////////////////////////////////////////////////////////
    226 
    227 role HTMLAsideAccessible::NativeRole() const {
    228  // Per the HTML-AAM spec, there are two cases for aside elements:
    229  //   1. scoped to body or main elements -> 'complementary' role
    230  //   2. scoped to sectioning content elements
    231  //       -> if the element has an accessible name, 'complementary' role
    232  //       -> otherwise, 'generic' role
    233  // To implement this, walk ancestors until we find a sectioning content
    234  // element, or a body/main element, then take actions based on the rules
    235  // above.
    236  nsIContent* parent = mContent->GetParent();
    237  while (parent) {
    238    if (parent->IsAnyOfHTMLElements(nsGkAtoms::article, nsGkAtoms::aside,
    239                                    nsGkAtoms::nav, nsGkAtoms::section)) {
    240      return !NameIsEmpty() ? roles::LANDMARK : roles::SECTION;
    241    }
    242    if (parent->IsAnyOfHTMLElements(nsGkAtoms::main, nsGkAtoms::body)) {
    243      return roles::LANDMARK;
    244    }
    245    parent = parent->GetParent();
    246  }
    247 
    248  // Fall back to landmark, though we always expect to find a body element.
    249  return roles::LANDMARK;
    250 }
    251 
    252 ////////////////////////////////////////////////////////////////////////////////
    253 // HTMLSectionAccessible
    254 ////////////////////////////////////////////////////////////////////////////////
    255 
    256 role HTMLSectionAccessible::NativeRole() const {
    257  return NameIsEmpty() ? roles::SECTION : roles::REGION;
    258 }
    259 
    260 ////////////////////////////////////////////////////////////////////////////////
    261 // HTMLAbbreviationAccessible
    262 ////////////////////////////////////////////////////////////////////////////////
    263 
    264 ENameValueFlag HTMLAbbreviationAccessible::NativeName(nsString& aName) const {
    265  if (mContent->AsElement()->GetAttr(nsGkAtoms::title, aName)) {
    266    // "title" tag takes priority
    267    return eNameOK;
    268  }
    269 
    270  return HyperTextAccessible::NativeName(aName);
    271 }
    272 
    273 void HTMLAbbreviationAccessible::DOMAttributeChanged(
    274    int32_t aNameSpaceID, nsAtom* aAttribute, AttrModType aModType,
    275    const nsAttrValue* aOldValue, uint64_t aOldState) {
    276  if (aAttribute == nsGkAtoms::title) {
    277    nsAutoString name;
    278    ARIAName(name);
    279    if (name.IsEmpty()) {
    280      mDoc->FireDelayedEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, this);
    281      return;
    282    }
    283 
    284    if (!mContent->AsElement()->HasAttr(nsGkAtoms::aria_describedby)) {
    285      mDoc->FireDelayedEvent(nsIAccessibleEvent::EVENT_DESCRIPTION_CHANGE,
    286                             this);
    287    }
    288 
    289    return;
    290  }
    291 
    292  HyperTextAccessible::DOMAttributeChanged(aNameSpaceID, aAttribute, aModType,
    293                                           aOldValue, aOldState);
    294 }