tor-browser

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

XULFormControlAccessible.cpp (15792B)


      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 "XULFormControlAccessible.h"
      7 
      8 #include "LocalAccessible-inl.h"
      9 #include "HTMLFormControlAccessible.h"
     10 #include "nsAccUtils.h"
     11 #include "DocAccessible.h"
     12 #include "Relation.h"
     13 #include "mozilla/a11y/Role.h"
     14 #include "States.h"
     15 #include "TreeWalker.h"
     16 #include "XULMenuAccessible.h"
     17 
     18 #include "nsIDOMXULButtonElement.h"
     19 #include "nsIDOMXULMenuListElement.h"
     20 #include "nsIDOMXULRadioGroupElement.h"
     21 #include "nsIDOMXULSelectCntrlItemEl.h"
     22 #include "nsIFrame.h"
     23 #include "nsMenuPopupFrame.h"
     24 #include "nsNameSpaceManager.h"
     25 #include "mozilla/dom/Element.h"
     26 
     27 using namespace mozilla::a11y;
     28 
     29 ////////////////////////////////////////////////////////////////////////////////
     30 // XULButtonAccessible
     31 ////////////////////////////////////////////////////////////////////////////////
     32 
     33 XULButtonAccessible::XULButtonAccessible(nsIContent* aContent,
     34                                         DocAccessible* aDoc)
     35    : AccessibleWrap(aContent, aDoc) {
     36  if (ContainsMenu()) {
     37    mGenericTypes |= eMenuButton;
     38  } else {
     39    mGenericTypes |= eButton;
     40  }
     41 }
     42 
     43 XULButtonAccessible::~XULButtonAccessible() {}
     44 
     45 ////////////////////////////////////////////////////////////////////////////////
     46 // XULButtonAccessible: nsISupports
     47 
     48 ////////////////////////////////////////////////////////////////////////////////
     49 // XULButtonAccessible: nsIAccessible
     50 
     51 bool XULButtonAccessible::HasPrimaryAction() const { return true; }
     52 
     53 void XULButtonAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) {
     54  if (aIndex == eAction_Click) aName.AssignLiteral("press");
     55 }
     56 
     57 ////////////////////////////////////////////////////////////////////////////////
     58 // XULButtonAccessible: LocalAccessible
     59 
     60 role XULButtonAccessible::NativeRole() const {
     61  // Buttons can be checked; they simply appear pressed in rather than checked.
     62  // In this case, we must expose them as toggle buttons.
     63  nsCOMPtr<nsIDOMXULButtonElement> xulButtonElement = Elm()->AsXULButton();
     64  if (xulButtonElement) {
     65    nsAutoString type;
     66    xulButtonElement->GetType(type);
     67    if (type.EqualsLiteral("checkbox") || type.EqualsLiteral("radio")) {
     68      return roles::TOGGLE_BUTTON;
     69    }
     70  }
     71  return roles::PUSHBUTTON;
     72 }
     73 
     74 uint64_t XULButtonAccessible::NativeState() const {
     75  // Possible states: focused, focusable, unavailable(disabled).
     76 
     77  // get focus and disable status from base class
     78  uint64_t state = LocalAccessible::NativeState();
     79 
     80  nsCOMPtr<nsIDOMXULButtonElement> xulButtonElement = Elm()->AsXULButton();
     81  if (xulButtonElement) {
     82    // Some buttons can have their checked state set without being of type
     83    // checkbox or radio. Expose the pressed state unconditionally.
     84    bool checked = false;
     85    xulButtonElement->GetChecked(&checked);
     86    if (checked) {
     87      state |= states::PRESSED;
     88    }
     89  }
     90 
     91  if (ContainsMenu()) state |= states::HASPOPUP;
     92 
     93  if (mContent->AsElement()->HasAttr(nsGkAtoms::_default)) {
     94    state |= states::DEFAULT;
     95  }
     96 
     97  return state;
     98 }
     99 
    100 bool XULButtonAccessible::AttributeChangesState(nsAtom* aAttribute) {
    101  if (aAttribute == nsGkAtoms::checked) {
    102    return true;
    103  }
    104  return AccessibleWrap::AttributeChangesState(aAttribute);
    105 }
    106 
    107 void XULButtonAccessible::DOMAttributeChanged(int32_t aNameSpaceID,
    108                                              nsAtom* aAttribute,
    109                                              AttrModType aModType,
    110                                              const nsAttrValue* aOldValue,
    111                                              uint64_t aOldState) {
    112  AccessibleWrap::DOMAttributeChanged(aNameSpaceID, aAttribute, aModType,
    113                                      aOldValue, aOldState);
    114  if (aAttribute == nsGkAtoms::label) {
    115    mDoc->FireDelayedEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, this);
    116  }
    117 }
    118 
    119 ////////////////////////////////////////////////////////////////////////////////
    120 // XULButtonAccessible: Widgets
    121 
    122 bool XULButtonAccessible::IsWidget() const { return true; }
    123 
    124 bool XULButtonAccessible::IsActiveWidget() const {
    125  return FocusMgr()->HasDOMFocus(mContent);
    126 }
    127 
    128 bool XULButtonAccessible::AreItemsOperable() const {
    129  if (IsMenuButton()) {
    130    LocalAccessible* menuPopup = mChildren.SafeElementAt(0, nullptr);
    131    if (menuPopup) {
    132      nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(menuPopup->GetFrame());
    133      return menuPopupFrame->IsOpen();
    134    }
    135  }
    136  return false;  // no items
    137 }
    138 
    139 bool XULButtonAccessible::IsAcceptableChild(nsIContent* aEl) const {
    140  // In general XUL buttons should not have accessible children. However:
    141  return
    142      //   menu buttons can have popup accessibles (@type="menu" or
    143      //   columnpicker).
    144      aEl->IsXULElement(nsGkAtoms::menupopup) ||
    145      // A XUL button can be labelled by a direct child text node, so we need to
    146      // allow that as a child so it will be picked up when computing name from
    147      // subtree.
    148      (aEl->IsText() && aEl->GetParent() == mContent);
    149 }
    150 
    151 ////////////////////////////////////////////////////////////////////////////////
    152 // XULButtonAccessible protected
    153 
    154 bool XULButtonAccessible::ContainsMenu() const {
    155  return mContent->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
    156                                            nsGkAtoms::menu, eCaseMatters);
    157 }
    158 
    159 ////////////////////////////////////////////////////////////////////////////////
    160 // XULDropmarkerAccessible
    161 ////////////////////////////////////////////////////////////////////////////////
    162 
    163 XULDropmarkerAccessible::XULDropmarkerAccessible(nsIContent* aContent,
    164                                                 DocAccessible* aDoc)
    165    : LeafAccessible(aContent, aDoc) {}
    166 
    167 bool XULDropmarkerAccessible::HasPrimaryAction() const { return true; }
    168 
    169 bool XULDropmarkerAccessible::DropmarkerOpen(bool aToggleOpen) const {
    170  bool isOpen = false;
    171 
    172  nsIContent* parent = mContent->GetFlattenedTreeParent();
    173 
    174  while (parent) {
    175    nsCOMPtr<nsIDOMXULButtonElement> parentButtonElement =
    176        parent->AsElement()->AsXULButton();
    177    if (parentButtonElement) {
    178      parentButtonElement->GetOpen(&isOpen);
    179      if (aToggleOpen) parentButtonElement->SetOpen(!isOpen);
    180      return isOpen;
    181    }
    182 
    183    nsCOMPtr<nsIDOMXULMenuListElement> parentMenuListElement =
    184        parent->AsElement()->AsXULMenuList();
    185    if (parentMenuListElement) {
    186      parentMenuListElement->GetOpen(&isOpen);
    187      if (aToggleOpen) parentMenuListElement->SetOpen(!isOpen);
    188      return isOpen;
    189    }
    190    parent = parent->GetFlattenedTreeParent();
    191  }
    192 
    193  return isOpen;
    194 }
    195 
    196 void XULDropmarkerAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) {
    197  aName.Truncate();
    198  if (aIndex == eAction_Click) {
    199    if (DropmarkerOpen(false)) {
    200      aName.AssignLiteral("close");
    201    } else {
    202      aName.AssignLiteral("open");
    203    }
    204  }
    205 }
    206 
    207 bool XULDropmarkerAccessible::DoAction(uint8_t index) const {
    208  if (index == eAction_Click) {
    209    DropmarkerOpen(true);  // Reverse the open attribute
    210    return true;
    211  }
    212  return false;
    213 }
    214 
    215 role XULDropmarkerAccessible::NativeRole() const { return roles::PUSHBUTTON; }
    216 
    217 uint64_t XULDropmarkerAccessible::NativeState() const {
    218  return DropmarkerOpen(false) ? states::PRESSED : 0;
    219 }
    220 
    221 ////////////////////////////////////////////////////////////////////////////////
    222 // XULGroupboxAccessible
    223 ////////////////////////////////////////////////////////////////////////////////
    224 
    225 XULGroupboxAccessible::XULGroupboxAccessible(nsIContent* aContent,
    226                                             DocAccessible* aDoc)
    227    : AccessibleWrap(aContent, aDoc) {}
    228 
    229 role XULGroupboxAccessible::NativeRole() const { return roles::GROUPING; }
    230 
    231 ENameValueFlag XULGroupboxAccessible::NativeName(nsString& aName) const {
    232  // XXX: we use the first related accessible only.
    233  LocalAccessible* label =
    234      RelationByType(RelationType::LABELLED_BY).LocalNext();
    235  if (label) {
    236    label->Name(aName);
    237    return eNameFromRelations;
    238  }
    239 
    240  return eNameOK;
    241 }
    242 
    243 Relation XULGroupboxAccessible::RelationByType(RelationType aType) const {
    244  Relation rel = AccessibleWrap::RelationByType(aType);
    245 
    246  // The label for xul:groupbox is generated from the first xul:label
    247  if (aType == RelationType::LABELLED_BY && ChildCount() > 0) {
    248    LocalAccessible* childAcc = LocalChildAt(0);
    249    if (childAcc->Role() == roles::LABEL &&
    250        childAcc->GetContent()->IsXULElement(nsGkAtoms::label)) {
    251      rel.AppendTarget(childAcc);
    252    }
    253  }
    254 
    255  return rel;
    256 }
    257 
    258 ////////////////////////////////////////////////////////////////////////////////
    259 // XULRadioButtonAccessible
    260 ////////////////////////////////////////////////////////////////////////////////
    261 
    262 XULRadioButtonAccessible::XULRadioButtonAccessible(nsIContent* aContent,
    263                                                   DocAccessible* aDoc)
    264    : RadioButtonAccessible(aContent, aDoc) {}
    265 
    266 uint64_t XULRadioButtonAccessible::NativeState() const {
    267  uint64_t state = LeafAccessible::NativeState();
    268  state |= states::CHECKABLE;
    269 
    270  nsCOMPtr<nsIDOMXULSelectControlItemElement> radioButton =
    271      Elm()->AsXULSelectControlItem();
    272  if (radioButton) {
    273    bool selected = false;  // Radio buttons can be selected
    274    radioButton->GetSelected(&selected);
    275    if (selected) {
    276      state |= states::CHECKED;
    277    }
    278  }
    279 
    280  return state;
    281 }
    282 
    283 uint64_t XULRadioButtonAccessible::NativeInteractiveState() const {
    284  return NativelyUnavailable() ? states::UNAVAILABLE : states::FOCUSABLE;
    285 }
    286 
    287 ////////////////////////////////////////////////////////////////////////////////
    288 // XULRadioButtonAccessible: Widgets
    289 
    290 LocalAccessible* XULRadioButtonAccessible::ContainerWidget() const {
    291  return mParent;
    292 }
    293 
    294 ////////////////////////////////////////////////////////////////////////////////
    295 // XULRadioGroupAccessible
    296 ////////////////////////////////////////////////////////////////////////////////
    297 
    298 /**
    299 * XUL Radio Group
    300 *   The Radio Group proxies for the Radio Buttons themselves. The Group gets
    301 *   focus whereas the Buttons do not. So we only have an accessible object for
    302 *   this for the purpose of getting the proper RadioButton. Need this here to
    303 *   avoid circular reference problems when navigating the accessible tree and
    304 *   for getting to the radiobuttons.
    305 */
    306 
    307 XULRadioGroupAccessible::XULRadioGroupAccessible(nsIContent* aContent,
    308                                                 DocAccessible* aDoc)
    309    : XULSelectControlAccessible(aContent, aDoc) {}
    310 
    311 role XULRadioGroupAccessible::NativeRole() const { return roles::RADIO_GROUP; }
    312 
    313 uint64_t XULRadioGroupAccessible::NativeInteractiveState() const {
    314  // The radio group is not focusable. Sometimes the focus controller will
    315  // report that it is focused. That means that the actual selected radio button
    316  // should be considered focused.
    317  return NativelyUnavailable() ? states::UNAVAILABLE : 0;
    318 }
    319 
    320 ////////////////////////////////////////////////////////////////////////////////
    321 // XULRadioGroupAccessible: Widgets
    322 
    323 bool XULRadioGroupAccessible::IsWidget() const { return true; }
    324 
    325 bool XULRadioGroupAccessible::IsActiveWidget() const {
    326  return FocusMgr()->HasDOMFocus(mContent);
    327 }
    328 
    329 bool XULRadioGroupAccessible::AreItemsOperable() const { return true; }
    330 
    331 LocalAccessible* XULRadioGroupAccessible::CurrentItem() const {
    332  if (!mSelectControl) {
    333    return nullptr;
    334  }
    335 
    336  RefPtr<dom::Element> currentItemElm;
    337  nsCOMPtr<nsIDOMXULRadioGroupElement> group =
    338      mSelectControl->AsXULRadioGroup();
    339  if (group) {
    340    group->GetFocusedItem(getter_AddRefs(currentItemElm));
    341  }
    342 
    343  if (currentItemElm) {
    344    DocAccessible* document = Document();
    345    if (document) {
    346      return document->GetAccessible(currentItemElm);
    347    }
    348  }
    349 
    350  return nullptr;
    351 }
    352 
    353 void XULRadioGroupAccessible::SetCurrentItem(const LocalAccessible* aItem) {
    354  if (!mSelectControl) {
    355    return;
    356  }
    357 
    358  nsCOMPtr<dom::Element> itemElm = aItem->Elm();
    359  nsCOMPtr<nsIDOMXULRadioGroupElement> group =
    360      mSelectControl->AsXULRadioGroup();
    361  if (group) {
    362    group->SetFocusedItem(itemElm);
    363  }
    364 }
    365 
    366 ////////////////////////////////////////////////////////////////////////////////
    367 // XULStatusBarAccessible
    368 ////////////////////////////////////////////////////////////////////////////////
    369 
    370 XULStatusBarAccessible::XULStatusBarAccessible(nsIContent* aContent,
    371                                               DocAccessible* aDoc)
    372    : AccessibleWrap(aContent, aDoc) {}
    373 
    374 role XULStatusBarAccessible::NativeRole() const { return roles::STATUSBAR; }
    375 
    376 ////////////////////////////////////////////////////////////////////////////////
    377 // XULToolbarButtonAccessible
    378 ////////////////////////////////////////////////////////////////////////////////
    379 
    380 XULToolbarButtonAccessible::XULToolbarButtonAccessible(nsIContent* aContent,
    381                                                       DocAccessible* aDoc)
    382    : XULButtonAccessible(aContent, aDoc) {}
    383 
    384 void XULToolbarButtonAccessible::GetPositionAndSetSize(int32_t* aPosInSet,
    385                                                       int32_t* aSetSize) {
    386  int32_t setSize = 0;
    387  int32_t posInSet = 0;
    388 
    389  LocalAccessible* parent = LocalParent();
    390  if (!parent) return;
    391 
    392  uint32_t childCount = parent->ChildCount();
    393  for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
    394    LocalAccessible* child = parent->LocalChildAt(childIdx);
    395    if (IsSeparator(child)) {  // end of a group of buttons
    396      if (posInSet) break;     // we've found our group, so we're done
    397 
    398      setSize = 0;  // not our group, so start a new group
    399 
    400    } else {
    401      setSize++;  // another button in the group
    402 
    403      if (child == this) posInSet = setSize;  // we've found our button
    404    }
    405  }
    406 
    407  *aPosInSet = posInSet;
    408  *aSetSize = setSize;
    409 }
    410 
    411 bool XULToolbarButtonAccessible::IsSeparator(LocalAccessible* aAccessible) {
    412  nsIContent* content = aAccessible->GetContent();
    413  return content && content->IsAnyOfXULElements(nsGkAtoms::toolbarseparator,
    414                                                nsGkAtoms::toolbarspacer,
    415                                                nsGkAtoms::toolbarspring);
    416 }
    417 
    418 ////////////////////////////////////////////////////////////////////////////////
    419 // XULToolbarButtonAccessible: Widgets
    420 
    421 bool XULToolbarButtonAccessible::IsAcceptableChild(nsIContent* aEl) const {
    422  return XULButtonAccessible::IsAcceptableChild(aEl) ||
    423         // In addition to the children allowed by buttons, toolbarbuttons can
    424         // have labels as children, but only if the label attribute is not
    425         // present.
    426         (aEl->IsXULElement(nsGkAtoms::label) &&
    427          !mContent->AsElement()->HasAttr(nsGkAtoms::label));
    428 }
    429 
    430 ////////////////////////////////////////////////////////////////////////////////
    431 // XULToolbarAccessible
    432 ////////////////////////////////////////////////////////////////////////////////
    433 
    434 XULToolbarAccessible::XULToolbarAccessible(nsIContent* aContent,
    435                                           DocAccessible* aDoc)
    436    : AccessibleWrap(aContent, aDoc) {}
    437 
    438 role XULToolbarAccessible::NativeRole() const { return roles::TOOLBAR; }
    439 
    440 ENameValueFlag XULToolbarAccessible::NativeName(nsString& aName) const {
    441  if (mContent->AsElement()->GetAttr(nsGkAtoms::toolbarname, aName)) {
    442    aName.CompressWhitespace();
    443  }
    444 
    445  return eNameOK;
    446 }
    447 
    448 ////////////////////////////////////////////////////////////////////////////////
    449 // XULToolbarAccessible
    450 ////////////////////////////////////////////////////////////////////////////////
    451 
    452 XULToolbarSeparatorAccessible::XULToolbarSeparatorAccessible(
    453    nsIContent* aContent, DocAccessible* aDoc)
    454    : LeafAccessible(aContent, aDoc) {}
    455 
    456 role XULToolbarSeparatorAccessible::NativeRole() const {
    457  return roles::SEPARATOR;
    458 }
    459 
    460 uint64_t XULToolbarSeparatorAccessible::NativeState() const { return 0; }