tor-browser

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

XULComboboxAccessible.cpp (6722B)


      1 /* -*- Mode: C++; tab-width: 4; 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 "XULComboboxAccessible.h"
      7 
      8 #include "LocalAccessible-inl.h"
      9 #include "nsAccessibilityService.h"
     10 #include "DocAccessible.h"
     11 #include "nsCoreUtils.h"
     12 #include "nsFocusManager.h"
     13 
     14 #include "mozilla/a11y/DocAccessibleParent.h"
     15 #include "mozilla/a11y/Role.h"
     16 #include "States.h"
     17 
     18 #include "mozilla/dom/Element.h"
     19 #include "nsIDOMXULMenuListElement.h"
     20 
     21 using namespace mozilla::a11y;
     22 
     23 ////////////////////////////////////////////////////////////////////////////////
     24 // XULComboboxAccessible
     25 ////////////////////////////////////////////////////////////////////////////////
     26 
     27 XULComboboxAccessible::XULComboboxAccessible(nsIContent* aContent,
     28                                             DocAccessible* aDoc)
     29    : AccessibleWrap(aContent, aDoc) {
     30  mGenericTypes |= eCombobox;
     31 }
     32 
     33 role XULComboboxAccessible::NativeRole() const { return roles::COMBOBOX; }
     34 
     35 uint64_t XULComboboxAccessible::NativeState() const {
     36  // As a nsComboboxAccessible we can have the following states:
     37  //     STATE_FOCUSED
     38  //     STATE_FOCUSABLE
     39  //     STATE_HASPOPUP
     40  //     STATE_EXPANDED
     41  //     STATE_COLLAPSED
     42 
     43  // Get focus status from base class
     44  uint64_t state = LocalAccessible::NativeState();
     45 
     46  nsCOMPtr<nsIDOMXULMenuListElement> menuList = Elm()->AsXULMenuList();
     47  if (menuList) {
     48    bool isOpen = false;
     49    menuList->GetOpen(&isOpen);
     50    if (isOpen) {
     51      state |= states::EXPANDED;
     52    }
     53  }
     54 
     55  return state | states::HASPOPUP | states::EXPANDABLE;
     56 }
     57 
     58 bool XULComboboxAccessible::IsAcceptableChild(nsIContent* aContent) const {
     59  return AccessibleWrap::IsAcceptableChild(aContent) && !aContent->IsText();
     60 }
     61 
     62 EDescriptionValueFlag XULComboboxAccessible::Description(
     63    nsString& aDescription) const {
     64  aDescription.Truncate();
     65  // Use description of currently focused option
     66  nsCOMPtr<nsIDOMXULMenuListElement> menuListElm = Elm()->AsXULMenuList();
     67  if (!menuListElm) return eDescriptionOK;
     68 
     69  nsCOMPtr<dom::Element> focusedOptionItem;
     70  menuListElm->GetSelectedItem(getter_AddRefs(focusedOptionItem));
     71  if (focusedOptionItem && mDoc) {
     72    LocalAccessible* focusedOptionAcc = mDoc->GetAccessible(focusedOptionItem);
     73    if (focusedOptionAcc) {
     74      return focusedOptionAcc->Description(aDescription);
     75    }
     76  }
     77 
     78  return eDescriptionOK;
     79 }
     80 
     81 void XULComboboxAccessible::Value(nsString& aValue) const {
     82  aValue.Truncate();
     83 
     84  // The value is the option or text shown entered in the combobox.
     85  nsCOMPtr<nsIDOMXULMenuListElement> menuList = Elm()->AsXULMenuList();
     86  if (menuList) menuList->GetLabel(aValue);
     87 }
     88 
     89 bool XULComboboxAccessible::HasPrimaryAction() const { return true; }
     90 
     91 bool XULComboboxAccessible::DoAction(uint8_t aIndex) const {
     92  if (aIndex != XULComboboxAccessible::eAction_Click) return false;
     93 
     94  // Programmaticaly toggle the combo box.
     95  nsCOMPtr<nsIDOMXULMenuListElement> menuList = Elm()->AsXULMenuList();
     96  if (!menuList) return false;
     97 
     98  bool isDroppedDown = false;
     99  menuList->GetOpen(&isDroppedDown);
    100  menuList->SetOpen(!isDroppedDown);
    101  return true;
    102 }
    103 
    104 void XULComboboxAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) {
    105  aName.Truncate();
    106  if (aIndex != XULComboboxAccessible::eAction_Click) return;
    107 
    108  nsCOMPtr<nsIDOMXULMenuListElement> menuList = Elm()->AsXULMenuList();
    109  if (!menuList) return;
    110 
    111  bool isDroppedDown = false;
    112  menuList->GetOpen(&isDroppedDown);
    113  if (isDroppedDown) {
    114    aName.AssignLiteral("close");
    115  } else {
    116    aName.AssignLiteral("open");
    117  }
    118 }
    119 
    120 ////////////////////////////////////////////////////////////////////////////////
    121 // Widgets
    122 
    123 bool XULComboboxAccessible::IsActiveWidget() const {
    124  if (mContent->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::editable,
    125                                         nsGkAtoms::_true, eIgnoreCase)) {
    126    int32_t childCount = mChildren.Length();
    127    for (int32_t idx = 0; idx < childCount; idx++) {
    128      LocalAccessible* child = mChildren[idx];
    129      if (child->Role() == roles::ENTRY) {
    130        return FocusMgr()->HasDOMFocus(child->GetContent());
    131      }
    132    }
    133    return false;
    134  }
    135 
    136  return FocusMgr()->HasDOMFocus(mContent);
    137 }
    138 
    139 bool XULComboboxAccessible::AreItemsOperable() const {
    140  nsCOMPtr<nsIDOMXULMenuListElement> menuListElm = Elm()->AsXULMenuList();
    141  if (menuListElm) {
    142    bool isOpen = false;
    143    menuListElm->GetOpen(&isOpen);
    144    return isOpen;
    145  }
    146 
    147  return false;
    148 }
    149 
    150 ////////////////////////////////////////////////////////////////////////////////
    151 // XULContentSelectDropdownAccessible
    152 ////////////////////////////////////////////////////////////////////////////////
    153 
    154 Accessible* XULContentSelectDropdownAccessible::Parent() const {
    155  // We render the expanded dropdown for <select>s in the parent process
    156  // as a child of the application accessible. This confuses some
    157  // ATs which expect the select to _always_ parent the dropdown (in
    158  // both expanded and collapsed states).
    159  // To rectify this, we spoof the <select> as the parent of the
    160  // expanded dropdown here. Note that we do not spoof the child relationship.
    161 
    162  // First, try to find the select that spawned this dropdown.
    163  // The select that was activated does not get states::EXPANDED, but
    164  // it should still have focus.
    165  Accessible* focusedAcc = nullptr;
    166  if (auto* focusedNode = FocusMgr()->FocusedDOMNode()) {
    167    // If we get a node here, we're in a non-remote browser.
    168    DocAccessible* doc =
    169        GetAccService()->GetDocAccessible(focusedNode->OwnerDoc());
    170    focusedAcc = doc->GetAccessible(focusedNode);
    171  } else {
    172    nsFocusManager* focusManagerDOM = nsFocusManager::GetFocusManager();
    173    dom::BrowsingContext* focusedContext =
    174        focusManagerDOM->GetFocusedBrowsingContextInChrome();
    175 
    176    DocAccessibleParent* focusedDoc =
    177        DocAccessibleParent::GetFrom(focusedContext);
    178    if (NS_WARN_IF(!focusedDoc)) {
    179      // We can fail to get a document here if a user is
    180      // performing a drag-and-drop selection with mouse. See
    181      // `browser/base/content/tests/browser_selectpopup_large.js`
    182      return LocalParent();
    183    }
    184    MOZ_ASSERT(focusedDoc->IsDoc(), "Got non-document?");
    185    focusedAcc = focusedDoc->AsDoc()->GetFocusedAcc();
    186  }
    187 
    188  if (!NS_WARN_IF(focusedAcc && focusedAcc->IsHTMLCombobox())) {
    189    // We can sometimes get a document here if the select that
    190    // this dropdown should anchor to loses focus. This can happen when
    191    // calling AXPressed on macOS. Call into the regular parent
    192    // function instead.
    193    return LocalParent();
    194  }
    195 
    196  return focusedAcc;
    197 }