tor-browser

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

XULTabAccessible.cpp (8367B)


      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 "XULTabAccessible.h"
      7 
      8 #include "ARIAMap.h"
      9 #include "nsAccUtils.h"
     10 #include "Relation.h"
     11 #include "mozilla/a11y/Role.h"
     12 #include "States.h"
     13 
     14 // NOTE: alphabetically ordered
     15 #include "mozilla/dom/Document.h"
     16 #include "nsIDOMXULSelectCntrlItemEl.h"
     17 #include "nsIDOMXULRelatedElement.h"
     18 #include "nsXULElement.h"
     19 
     20 #include "mozilla/dom/BindingDeclarations.h"
     21 
     22 using namespace mozilla::a11y;
     23 
     24 ////////////////////////////////////////////////////////////////////////////////
     25 // XULTabAccessible
     26 ////////////////////////////////////////////////////////////////////////////////
     27 
     28 XULTabAccessible::XULTabAccessible(nsIContent* aContent, DocAccessible* aDoc)
     29    : HyperTextAccessible(aContent, aDoc) {}
     30 
     31 ////////////////////////////////////////////////////////////////////////////////
     32 // XULTabAccessible: LocalAccessible
     33 
     34 bool XULTabAccessible::HasPrimaryAction() const { return true; }
     35 
     36 void XULTabAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) {
     37  if (aIndex == eAction_Switch) aName.AssignLiteral("switch");
     38 }
     39 
     40 bool XULTabAccessible::DoAction(uint8_t index) const {
     41  if (index == eAction_Switch) {
     42    // XXXbz Could this just FromContent?
     43    RefPtr<nsXULElement> tab = nsXULElement::FromNodeOrNull(mContent);
     44    if (tab) {
     45      tab->Click(mozilla::dom::CallerType::System);
     46      return true;
     47    }
     48  }
     49  return false;
     50 }
     51 
     52 ////////////////////////////////////////////////////////////////////////////////
     53 // XULTabAccessible: LocalAccessible
     54 
     55 role XULTabAccessible::NativeRole() const { return roles::PAGETAB; }
     56 
     57 uint64_t XULTabAccessible::NativeState() const {
     58  // Possible states: focused, focusable, unavailable(disabled), offscreen.
     59 
     60  // get focus and disable status from base class
     61  uint64_t state = AccessibleWrap::NativeState();
     62 
     63  // Check whether the tab is selected and/or pinned
     64  nsCOMPtr<nsIDOMXULSelectControlItemElement> tab =
     65      Elm()->AsXULSelectControlItem();
     66  if (tab) {
     67    bool selected = false;
     68    if (NS_SUCCEEDED(tab->GetSelected(&selected)) && selected) {
     69      state |= states::SELECTED;
     70    }
     71 
     72    if (mContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::pinned)) {
     73      state |= states::PINNED;
     74    }
     75  }
     76 
     77  return state;
     78 }
     79 
     80 uint64_t XULTabAccessible::NativeInteractiveState() const {
     81  uint64_t state = LocalAccessible::NativeInteractiveState();
     82  // Bug 1951776: XUL tabs are implemented such that only the selected tab is
     83  // programmatically focusable. Unfortunately, Windows Voice Access requires
     84  // tabs to be focusable in order for them to be discovered by the "show
     85  // numbers" command. Therefore, always expose the focusable state for
     86  // accessibility. While this seems strange on the surface, it can be argued
     87  // that unselected tabs *can* receive focus at some point in the future (when
     88  // they're selected), even if they can't receive focus *now*. There is some
     89  // precedent for this: aria-activedescendant requires browsers to expose the
     90  // focusable state on all possible descendants even if they aren't the current
     91  // active descendant.
     92  return (state & states::UNAVAILABLE)
     93             ? state
     94             : state | states::SELECTABLE | states::FOCUSABLE;
     95 }
     96 
     97 Relation XULTabAccessible::RelationByType(RelationType aType) const {
     98  Relation rel = AccessibleWrap::RelationByType(aType);
     99  if (aType != RelationType::LABEL_FOR) return rel;
    100 
    101  // Expose 'LABEL_FOR' relation on tab accessible for tabpanel accessible.
    102  ErrorResult rv;
    103  nsIContent* parent = mContent->AsElement()->Closest("tabs"_ns, rv);
    104  if (!parent) return rel;
    105 
    106  nsCOMPtr<nsIDOMXULRelatedElement> tabsElm =
    107      parent->AsElement()->AsXULRelated();
    108  if (!tabsElm) return rel;
    109 
    110  RefPtr<mozilla::dom::Element> tabpanelElement;
    111  tabsElm->GetRelatedElement(GetNode(), getter_AddRefs(tabpanelElement));
    112  if (!tabpanelElement) return rel;
    113 
    114  rel.AppendTarget(mDoc, tabpanelElement);
    115  return rel;
    116 }
    117 
    118 void XULTabAccessible::ApplyARIAState(uint64_t* aState) const {
    119  HyperTextAccessible::ApplyARIAState(aState);
    120  // XUL tab has an implicit ARIA role of tab, so support aria-selected.
    121  // Don't use aria::MapToState because that will set the SELECTABLE state
    122  // even if the tab is disabled.
    123  if (nsAccUtils::IsARIASelected(this)) {
    124    *aState |= states::SELECTED;
    125  }
    126 }
    127 
    128 ////////////////////////////////////////////////////////////////////////////////
    129 // XULTabsAccessible
    130 ////////////////////////////////////////////////////////////////////////////////
    131 
    132 XULTabsAccessible::XULTabsAccessible(nsIContent* aContent, DocAccessible* aDoc)
    133    : XULSelectControlAccessible(aContent, aDoc) {}
    134 
    135 role XULTabsAccessible::NativeRole() const { return roles::PAGETABLIST; }
    136 
    137 bool XULTabsAccessible::HasPrimaryAction() const { return false; }
    138 
    139 void XULTabsAccessible::Value(nsString& aValue) const { aValue.Truncate(); }
    140 
    141 ENameValueFlag XULTabsAccessible::NativeName(nsString& aName) const {
    142  // no name
    143  return eNameOK;
    144 }
    145 
    146 void XULTabsAccessible::ApplyARIAState(uint64_t* aState) const {
    147  XULSelectControlAccessible::ApplyARIAState(aState);
    148  // XUL tabs has an implicit ARIA role of tablist, so support
    149  // aria-multiselectable.
    150  MOZ_ASSERT(Elm());
    151  aria::MapToState(aria::eARIAMultiSelectable, Elm(), aState);
    152 }
    153 
    154 // XUL tabs is a single selection control and doesn't allow ARIA selection.
    155 // However, if aria-multiselectable is used, it becomes a multiselectable
    156 // control, where both native and ARIA markup are used to set selection.
    157 // Therefore, if aria-multiselectable is set, use the base implementation of
    158 // the selection retrieval methods in order to support ARIA selection.
    159 // We don't bother overriding the selection setting methods because
    160 // current front-end code using XUL tabs doesn't support setting of
    161 //  aria-selected by the a11y engine and we still want to be able to set the
    162 // primary selected item according to XUL.
    163 
    164 void XULTabsAccessible::SelectedItems(nsTArray<Accessible*>* aItems) {
    165  if (nsAccUtils::IsARIAMultiSelectable(this)) {
    166    AccessibleWrap::SelectedItems(aItems);
    167  } else {
    168    XULSelectControlAccessible::SelectedItems(aItems);
    169  }
    170 }
    171 
    172 Accessible* XULTabsAccessible::GetSelectedItem(uint32_t aIndex) {
    173  if (nsAccUtils::IsARIAMultiSelectable(this)) {
    174    return AccessibleWrap::GetSelectedItem(aIndex);
    175  }
    176 
    177  return XULSelectControlAccessible::GetSelectedItem(aIndex);
    178 }
    179 
    180 uint32_t XULTabsAccessible::SelectedItemCount() {
    181  if (nsAccUtils::IsARIAMultiSelectable(this)) {
    182    return AccessibleWrap::SelectedItemCount();
    183  }
    184 
    185  return XULSelectControlAccessible::SelectedItemCount();
    186 }
    187 
    188 bool XULTabsAccessible::IsItemSelected(uint32_t aIndex) {
    189  if (nsAccUtils::IsARIAMultiSelectable(this)) {
    190    return AccessibleWrap::IsItemSelected(aIndex);
    191  }
    192 
    193  return XULSelectControlAccessible::IsItemSelected(aIndex);
    194 }
    195 
    196 ////////////////////////////////////////////////////////////////////////////////
    197 // XULTabpanelsAccessible
    198 ////////////////////////////////////////////////////////////////////////////////
    199 
    200 role XULTabpanelsAccessible::NativeRole() const { return roles::PANE; }
    201 
    202 ////////////////////////////////////////////////////////////////////////////////
    203 // XULTabpanelAccessible
    204 ////////////////////////////////////////////////////////////////////////////////
    205 
    206 XULTabpanelAccessible::XULTabpanelAccessible(nsIContent* aContent,
    207                                             DocAccessible* aDoc)
    208    : AccessibleWrap(aContent, aDoc) {}
    209 
    210 role XULTabpanelAccessible::NativeRole() const { return roles::PROPERTYPAGE; }
    211 
    212 Relation XULTabpanelAccessible::RelationByType(RelationType aType) const {
    213  Relation rel = AccessibleWrap::RelationByType(aType);
    214  if (aType != RelationType::LABELLED_BY) return rel;
    215 
    216  // Expose 'LABELLED_BY' relation on tabpanel accessible for tab accessible.
    217  if (!mContent->GetParent()) return rel;
    218 
    219  nsCOMPtr<nsIDOMXULRelatedElement> tabpanelsElm =
    220      mContent->GetParent()->AsElement()->AsXULRelated();
    221  if (!tabpanelsElm) return rel;
    222 
    223  RefPtr<mozilla::dom::Element> tabElement;
    224  tabpanelsElm->GetRelatedElement(GetNode(), getter_AddRefs(tabElement));
    225  if (!tabElement) return rel;
    226 
    227  rel.AppendTarget(mDoc, tabElement);
    228  return rel;
    229 }