XULFormControlAccessible.cpp 16 KB

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