accessible/src/xul/XULFormControlAccessible.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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/. */
     6 #include "XULFormControlAccessible.h"
     8 #include "Accessible-inl.h"
     9 #include "HTMLFormControlAccessible.h"
    10 #include "nsAccUtils.h"
    11 #include "nsCoreUtils.h"
    12 #include "DocAccessible.h"
    13 #include "nsIAccessibleRelation.h"
    14 #include "Relation.h"
    15 #include "Role.h"
    16 #include "States.h"
    17 #include "TreeWalker.h"
    18 #include "XULMenuAccessible.h"
    20 #include "nsIDOMNSEditableElement.h"
    21 #include "nsIDOMXULButtonElement.h"
    22 #include "nsIDOMXULCheckboxElement.h"
    23 #include "nsIDOMXULMenuListElement.h"
    24 #include "nsIDOMXULSelectCntrlItemEl.h"
    25 #include "nsIDOMXULTextboxElement.h"
    26 #include "nsIEditor.h"
    27 #include "nsIFrame.h"
    28 #include "nsITextControlFrame.h"
    29 #include "nsMenuPopupFrame.h"
    30 #include "nsNameSpaceManager.h"
    31 #include "mozilla/dom/Element.h"
    33 using namespace mozilla::a11y;
    35 ////////////////////////////////////////////////////////////////////////////////
    36 // XULButtonAccessible
    37 ////////////////////////////////////////////////////////////////////////////////
    39 XULButtonAccessible::
    40   XULButtonAccessible(nsIContent* aContent, DocAccessible* aDoc) :
    41   AccessibleWrap(aContent, aDoc)
    42 {
    43   if (ContainsMenu()) {
    44     mGenericTypes |= eMenuButton;
    45   } else {
    46     mGenericTypes |= eButton;
    47   }
    48 }
    50 ////////////////////////////////////////////////////////////////////////////////
    51 // XULButtonAccessible: nsISupports
    53 NS_IMPL_ISUPPORTS_INHERITED0(XULButtonAccessible, Accessible)
    55 ////////////////////////////////////////////////////////////////////////////////
    56 // XULButtonAccessible: nsIAccessible
    58 uint8_t
    59 XULButtonAccessible::ActionCount()
    60 {
    61   return 1;
    62 }
    64 NS_IMETHODIMP
    65 XULButtonAccessible::GetActionName(uint8_t aIndex, nsAString& aName)
    66 {
    67   if (aIndex == eAction_Click) {
    68     aName.AssignLiteral("press"); 
    69     return NS_OK;
    70   }
    71   return NS_ERROR_INVALID_ARG;
    72 }
    74 NS_IMETHODIMP
    75 XULButtonAccessible::DoAction(uint8_t aIndex)
    76 {
    77   if (aIndex != 0)
    78     return NS_ERROR_INVALID_ARG;
    80   DoCommand();
    81   return NS_OK;
    82 }
    84 ////////////////////////////////////////////////////////////////////////////////
    85 // XULButtonAccessible: Accessible
    87 role
    88 XULButtonAccessible::NativeRole()
    89 {
    90   return roles::PUSHBUTTON;
    91 }
    93 uint64_t
    94 XULButtonAccessible::NativeState()
    95 {
    96   // Possible states: focused, focusable, unavailable(disabled).
    98   // get focus and disable status from base class
    99   uint64_t state = Accessible::NativeState();
   101   // Buttons can be checked -- they simply appear pressed in rather than checked
   102   nsCOMPtr<nsIDOMXULButtonElement> xulButtonElement(do_QueryInterface(mContent));
   103   if (xulButtonElement) {
   104     nsAutoString type;
   105     xulButtonElement->GetType(type);
   106     if (type.EqualsLiteral("checkbox") || type.EqualsLiteral("radio")) {
   107       state |= states::CHECKABLE;
   108       bool checked = false;
   109       int32_t checkState = 0;
   110       xulButtonElement->GetChecked(&checked);
   111       if (checked) {
   112         state |= states::PRESSED;
   113         xulButtonElement->GetCheckState(&checkState);
   114         if (checkState == nsIDOMXULButtonElement::CHECKSTATE_MIXED) { 
   115           state |= states::MIXED;
   116         }
   117       }
   118     }
   119   }
   121   if (ContainsMenu())
   122     state |= states::HASPOPUP;
   124   if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::_default))
   125     state |= states::DEFAULT;
   127   return state;
   128 }
   130 ////////////////////////////////////////////////////////////////////////////////
   131 // XULButtonAccessible: Widgets
   133 bool
   134 XULButtonAccessible::IsWidget() const
   135 {
   136   return true;
   137 }
   139 bool
   140 XULButtonAccessible::IsActiveWidget() const
   141 {
   142   return FocusMgr()->HasDOMFocus(mContent);
   143 }
   145 bool
   146 XULButtonAccessible::AreItemsOperable() const
   147 {
   148   if (IsMenuButton()) {
   149     Accessible* menuPopup = mChildren.SafeElementAt(0, nullptr);
   150     if (menuPopup) {
   151       nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(menuPopup->GetFrame());
   152       return menuPopupFrame->IsOpen();
   153     }
   154   }
   155   return false; // no items
   156 }
   158 Accessible*
   159 XULButtonAccessible::ContainerWidget() const
   160 {
   161   if (IsMenuButton() && mParent && mParent->IsAutoComplete())
   162     return mParent;
   163   return nullptr;
   164 }
   166 bool
   167 XULButtonAccessible::IsAcceptableChild(Accessible* aPossibleChild) const
   168 {
   169   // In general XUL button has not accessible children. Nevertheless menu
   170   // buttons can have button (@type="menu-button") and popup accessibles
   171   // (@type="menu-button", @type="menu" or columnpicker.
   173   // XXX: no children until the button is menu button. Probably it's not
   174   // totally correct but in general AT wants to have leaf buttons.
   175   roles::Role role = aPossibleChild->Role();
   177   // Get an accessible for menupopup or panel elements.
   178   if (role == roles::MENUPOPUP)
   179     return true;
   181   // Button type="menu-button" contains a real button. Get an accessible
   182   // for it. Ignore dropmarker button which is placed as a last child.
   183   if (role != roles::PUSHBUTTON ||
   184       aPossibleChild->GetContent()->Tag() == nsGkAtoms::dropMarker)
   185     return false;
   187   return mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
   188                                nsGkAtoms::menuButton, eCaseMatters);
   189 }
   191 ////////////////////////////////////////////////////////////////////////////////
   192 // XULButtonAccessible protected
   194 bool
   195 XULButtonAccessible::ContainsMenu()
   196 {
   197   static nsIContent::AttrValuesArray strings[] =
   198     {&nsGkAtoms::menu, &nsGkAtoms::menuButton, nullptr};
   200   return mContent->FindAttrValueIn(kNameSpaceID_None,
   201                                    nsGkAtoms::type,
   202                                    strings, eCaseMatters) >= 0;
   203 }
   205 ////////////////////////////////////////////////////////////////////////////////
   206 // XULDropmarkerAccessible
   207 ////////////////////////////////////////////////////////////////////////////////
   209 XULDropmarkerAccessible::
   210   XULDropmarkerAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   211   LeafAccessible(aContent, aDoc)
   212 {
   213 }
   215 uint8_t
   216 XULDropmarkerAccessible::ActionCount()
   217 {
   218   return 1;
   219 }
   221 bool
   222 XULDropmarkerAccessible::DropmarkerOpen(bool aToggleOpen)
   223 {
   224   bool isOpen = false;
   226   nsCOMPtr<nsIDOMXULButtonElement> parentButtonElement =
   227     do_QueryInterface(mContent->GetFlattenedTreeParent());
   229   if (parentButtonElement) {
   230     parentButtonElement->GetOpen(&isOpen);
   231     if (aToggleOpen)
   232       parentButtonElement->SetOpen(!isOpen);
   233   }
   234   else {
   235     nsCOMPtr<nsIDOMXULMenuListElement> parentMenuListElement =
   236       do_QueryInterface(parentButtonElement);
   237     if (parentMenuListElement) {
   238       parentMenuListElement->GetOpen(&isOpen);
   239       if (aToggleOpen)
   240         parentMenuListElement->SetOpen(!isOpen);
   241     }
   242   }
   244   return isOpen;
   245 }
   247 /**
   248   * Return the name of our only action
   249   */
   250 NS_IMETHODIMP
   251 XULDropmarkerAccessible::GetActionName(uint8_t aIndex, nsAString& aName)
   252 {
   253   if (aIndex == eAction_Click) {
   254     if (DropmarkerOpen(false))
   255       aName.AssignLiteral("close");
   256     else
   257       aName.AssignLiteral("open");
   258     return NS_OK;
   259   }
   261   return NS_ERROR_INVALID_ARG;
   262 }
   264 /**
   265   * Tell the Dropmarker to do its action
   266   */
   267 NS_IMETHODIMP
   268 XULDropmarkerAccessible::DoAction(uint8_t index)
   269 {
   270   if (index == eAction_Click) {
   271     DropmarkerOpen(true); // Reverse the open attribute
   272     return NS_OK;
   273   }
   274   return NS_ERROR_INVALID_ARG;
   275 }
   277 role
   278 XULDropmarkerAccessible::NativeRole()
   279 {
   280   return roles::PUSHBUTTON;
   281 }
   283 uint64_t
   284 XULDropmarkerAccessible::NativeState()
   285 {
   286   return DropmarkerOpen(false) ? states::PRESSED : 0;
   287 }
   289 ////////////////////////////////////////////////////////////////////////////////
   290 // XULCheckboxAccessible
   291 ////////////////////////////////////////////////////////////////////////////////
   293 XULCheckboxAccessible::
   294   XULCheckboxAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   295   LeafAccessible(aContent, aDoc)
   296 {
   297 }
   299 role
   300 XULCheckboxAccessible::NativeRole()
   301 {
   302   return roles::CHECKBUTTON;
   303 }
   305 uint8_t
   306 XULCheckboxAccessible::ActionCount()
   307 {
   308   return 1;
   309 }
   311 /**
   312   * Return the name of our only action
   313   */
   314 NS_IMETHODIMP
   315 XULCheckboxAccessible::GetActionName(uint8_t aIndex, nsAString& aName)
   316 {
   317   if (aIndex == eAction_Click) {
   318     // check or uncheck
   320     if (NativeState() & states::CHECKED)
   321       aName.AssignLiteral("uncheck");
   322     else
   323       aName.AssignLiteral("check");
   325     return NS_OK;
   326   }
   327   return NS_ERROR_INVALID_ARG;
   328 }
   330 /**
   331   * Tell the checkbox to do its only action -- check( or uncheck) itself
   332   */
   333 NS_IMETHODIMP
   334 XULCheckboxAccessible::DoAction(uint8_t aIndex)
   335 {
   336   if (aIndex != eAction_Click)
   337     return NS_ERROR_INVALID_ARG;
   339   DoCommand();
   340   return NS_OK;
   341 }
   343 uint64_t
   344 XULCheckboxAccessible::NativeState()
   345 {
   346   // Possible states: focused, focusable, unavailable(disabled), checked
   347   // Get focus and disable status from base class
   348   uint64_t state = LeafAccessible::NativeState();
   350   state |= states::CHECKABLE;
   352   // Determine Checked state
   353   nsCOMPtr<nsIDOMXULCheckboxElement> xulCheckboxElement =
   354     do_QueryInterface(mContent);
   355   if (xulCheckboxElement) {
   356     bool checked = false;
   357     xulCheckboxElement->GetChecked(&checked);
   358     if (checked) {
   359       state |= states::CHECKED;
   360       int32_t checkState = 0;
   361       xulCheckboxElement->GetCheckState(&checkState);
   362       if (checkState == nsIDOMXULCheckboxElement::CHECKSTATE_MIXED)
   363         state |= states::MIXED;
   364     }
   365   }
   367   return state;
   368 }
   370 ////////////////////////////////////////////////////////////////////////////////
   371 // XULGroupboxAccessible
   372 ////////////////////////////////////////////////////////////////////////////////
   374 XULGroupboxAccessible::
   375   XULGroupboxAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   376   AccessibleWrap(aContent, aDoc)
   377 {
   378 }
   380 role
   381 XULGroupboxAccessible::NativeRole()
   382 {
   383   return roles::GROUPING;
   384 }
   386 ENameValueFlag
   387 XULGroupboxAccessible::NativeName(nsString& aName)
   388 {
   389   // XXX: we use the first related accessible only.
   390   Accessible* label =
   391     RelationByType(RelationType::LABELLED_BY).Next();
   392   if (label)
   393     return label->Name(aName);
   395   return eNameOK;
   396 }
   398 Relation
   399 XULGroupboxAccessible::RelationByType(RelationType aType)
   400 {
   401   Relation rel = AccessibleWrap::RelationByType(aType);
   402   if (aType != RelationType::LABELLED_BY)
   403     return rel;
   405   // The label for xul:groupbox is generated from xul:label that is
   406   // inside the anonymous content of the xul:caption.
   407   // The xul:label has an accessible object but the xul:caption does not
   408   uint32_t childCount = ChildCount();
   409   for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
   410     Accessible* childAcc = GetChildAt(childIdx);
   411     if (childAcc->Role() == roles::LABEL) {
   412       // Ensure that it's our label
   413       Relation reverseRel = childAcc->RelationByType(RelationType::LABEL_FOR);
   414       Accessible* testGroupbox = nullptr;
   415       while ((testGroupbox = reverseRel.Next()))
   416         if (testGroupbox == this) {
   417           // The <label> points back to this groupbox
   418           rel.AppendTarget(childAcc);
   419         }
   420     }
   421   }
   423   return rel;
   424 }
   426 ////////////////////////////////////////////////////////////////////////////////
   427 // XULRadioButtonAccessible
   428 ////////////////////////////////////////////////////////////////////////////////
   430 XULRadioButtonAccessible::
   431   XULRadioButtonAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   432   RadioButtonAccessible(aContent, aDoc)
   433 {
   434 }
   436 uint64_t
   437 XULRadioButtonAccessible::NativeState()
   438 {
   439   uint64_t state = LeafAccessible::NativeState();
   440   state |= states::CHECKABLE;
   442   nsCOMPtr<nsIDOMXULSelectControlItemElement> radioButton =
   443     do_QueryInterface(mContent);
   444   if (radioButton) {
   445     bool selected = false;   // Radio buttons can be selected
   446     radioButton->GetSelected(&selected);
   447     if (selected) {
   448       state |= states::CHECKED;
   449     }
   450   }
   452   return state;
   453 }
   455 uint64_t
   456 XULRadioButtonAccessible::NativeInteractiveState() const
   457 {
   458   return NativelyUnavailable() ? states::UNAVAILABLE : states::FOCUSABLE;
   459 }
   461 ////////////////////////////////////////////////////////////////////////////////
   462 // XULRadioButtonAccessible: Widgets
   464 Accessible*
   465 XULRadioButtonAccessible::ContainerWidget() const
   466 {
   467   return mParent;
   468 }
   471 ////////////////////////////////////////////////////////////////////////////////
   472 // XULRadioGroupAccessible
   473 ////////////////////////////////////////////////////////////////////////////////
   475 /**
   476   * XUL Radio Group
   477   *   The Radio Group proxies for the Radio Buttons themselves. The Group gets
   478   *   focus whereas the Buttons do not. So we only have an accessible object for
   479   *   this for the purpose of getting the proper RadioButton. Need this here to 
   480   *   avoid circular reference problems when navigating the accessible tree and
   481   *   for getting to the radiobuttons.
   482   */
   484 XULRadioGroupAccessible::
   485   XULRadioGroupAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   486   XULSelectControlAccessible(aContent, aDoc)
   487 { 
   488 }
   490 role
   491 XULRadioGroupAccessible::NativeRole()
   492 {
   493   return roles::GROUPING;
   494 }
   496 uint64_t
   497 XULRadioGroupAccessible::NativeInteractiveState() const
   498 {
   499   // The radio group is not focusable. Sometimes the focus controller will
   500   // report that it is focused. That means that the actual selected radio button
   501   // should be considered focused.
   502   return NativelyUnavailable() ? states::UNAVAILABLE : 0;
   503 }
   505 ////////////////////////////////////////////////////////////////////////////////
   506 // XULRadioGroupAccessible: Widgets
   508 bool
   509 XULRadioGroupAccessible::IsWidget() const
   510 {
   511   return true;
   512 }
   514 bool
   515 XULRadioGroupAccessible::IsActiveWidget() const
   516 {
   517   return FocusMgr()->HasDOMFocus(mContent);
   518 }
   520 bool
   521 XULRadioGroupAccessible::AreItemsOperable() const
   522 {
   523   return true;
   524 }
   527 ////////////////////////////////////////////////////////////////////////////////
   528 // XULStatusBarAccessible
   529 ////////////////////////////////////////////////////////////////////////////////
   531 XULStatusBarAccessible::
   532   XULStatusBarAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   533   AccessibleWrap(aContent, aDoc)
   534 {
   535 }
   537 role
   538 XULStatusBarAccessible::NativeRole()
   539 {
   540   return roles::STATUSBAR;
   541 }
   544 ////////////////////////////////////////////////////////////////////////////////
   545 // XULToolbarButtonAccessible
   546 ////////////////////////////////////////////////////////////////////////////////
   548 XULToolbarButtonAccessible::
   549   XULToolbarButtonAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   550   XULButtonAccessible(aContent, aDoc)
   551 {
   552 }
   554 void
   555 XULToolbarButtonAccessible::GetPositionAndSizeInternal(int32_t* aPosInSet,
   556                                                        int32_t* aSetSize)
   557 {
   558   int32_t setSize = 0;
   559   int32_t posInSet = 0;
   561   Accessible* parent = Parent();
   562   if (!parent)
   563     return;
   565   uint32_t childCount = parent->ChildCount();
   566   for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
   567     Accessible* child = parent->GetChildAt(childIdx);
   568     if (IsSeparator(child)) { // end of a group of buttons
   569       if (posInSet)
   570         break; // we've found our group, so we're done
   572       setSize = 0; // not our group, so start a new group
   574     } else {
   575       setSize++; // another button in the group
   577       if (child == this)
   578         posInSet = setSize; // we've found our button
   579     }
   580   }
   582   *aPosInSet = posInSet;
   583   *aSetSize = setSize;
   584 }
   586 bool
   587 XULToolbarButtonAccessible::IsSeparator(Accessible* aAccessible)
   588 {
   589   nsIContent* content = aAccessible->GetContent();
   590   return content && ((content->Tag() == nsGkAtoms::toolbarseparator) ||
   591                      (content->Tag() == nsGkAtoms::toolbarspacer) ||
   592                      (content->Tag() == nsGkAtoms::toolbarspring)); }
   595 ////////////////////////////////////////////////////////////////////////////////
   596 // XULToolbarAccessible
   597 ////////////////////////////////////////////////////////////////////////////////
   599 XULToolbarAccessible::
   600   XULToolbarAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   601   AccessibleWrap(aContent, aDoc)
   602 {
   603 }
   605 role
   606 XULToolbarAccessible::NativeRole()
   607 {
   608   return roles::TOOLBAR;
   609 }
   611 ENameValueFlag
   612 XULToolbarAccessible::NativeName(nsString& aName)
   613 {
   614   if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::toolbarname, aName))
   615     aName.CompressWhitespace();
   617   return eNameOK;
   618 }
   621 ////////////////////////////////////////////////////////////////////////////////
   622 // XULToolbarAccessible
   623 ////////////////////////////////////////////////////////////////////////////////
   625 XULToolbarSeparatorAccessible::
   626   XULToolbarSeparatorAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   627   LeafAccessible(aContent, aDoc)
   628 {
   629 }
   631 role
   632 XULToolbarSeparatorAccessible::NativeRole()
   633 {
   634   return roles::SEPARATOR;
   635 }
   637 uint64_t
   638 XULToolbarSeparatorAccessible::NativeState()
   639 {
   640   return 0;
   641 }

mercurial