RootAccessible.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735
  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 "RootAccessible.h"
  6. #include "mozilla/ArrayUtils.h"
  7. #define CreateEvent CreateEventA
  8. #include "nsIDOMDocument.h"
  9. #include "Accessible-inl.h"
  10. #include "DocAccessible-inl.h"
  11. #include "nsAccessibilityService.h"
  12. #include "nsAccUtils.h"
  13. #include "nsCoreUtils.h"
  14. #include "nsEventShell.h"
  15. #include "Relation.h"
  16. #include "Role.h"
  17. #include "States.h"
  18. #ifdef MOZ_XUL
  19. #include "XULTreeAccessible.h"
  20. #endif
  21. #include "mozilla/dom/Element.h"
  22. #include "nsIDocShellTreeItem.h"
  23. #include "nsIDocShellTreeOwner.h"
  24. #include "mozilla/a11y/DocAccessibleParent.h"
  25. #include "mozilla/dom/Event.h"
  26. #include "mozilla/dom/EventTarget.h"
  27. #include "mozilla/dom/TabParent.h"
  28. #include "nsIDOMCustomEvent.h"
  29. #include "nsIDOMXULMultSelectCntrlEl.h"
  30. #include "nsIDocument.h"
  31. #include "nsIInterfaceRequestorUtils.h"
  32. #include "nsIPropertyBag2.h"
  33. #include "nsIServiceManager.h"
  34. #include "nsPIDOMWindow.h"
  35. #include "nsIWebBrowserChrome.h"
  36. #include "nsReadableUtils.h"
  37. #include "nsFocusManager.h"
  38. #include "nsGlobalWindow.h"
  39. #ifdef MOZ_XUL
  40. #include "nsIXULDocument.h"
  41. #include "nsIXULWindow.h"
  42. #endif
  43. using namespace mozilla;
  44. using namespace mozilla::a11y;
  45. using namespace mozilla::dom;
  46. ////////////////////////////////////////////////////////////////////////////////
  47. // nsISupports
  48. NS_IMPL_ISUPPORTS_INHERITED0(RootAccessible, DocAccessible)
  49. ////////////////////////////////////////////////////////////////////////////////
  50. // Constructor/destructor
  51. RootAccessible::
  52. RootAccessible(nsIDocument* aDocument, nsIPresShell* aPresShell) :
  53. DocAccessibleWrap(aDocument, aPresShell)
  54. {
  55. mType = eRootType;
  56. }
  57. RootAccessible::~RootAccessible()
  58. {
  59. }
  60. ////////////////////////////////////////////////////////////////////////////////
  61. // Accessible
  62. ENameValueFlag
  63. RootAccessible::Name(nsString& aName)
  64. {
  65. aName.Truncate();
  66. if (ARIARoleMap()) {
  67. Accessible::Name(aName);
  68. if (!aName.IsEmpty())
  69. return eNameOK;
  70. }
  71. mDocumentNode->GetTitle(aName);
  72. return eNameOK;
  73. }
  74. role
  75. RootAccessible::NativeRole()
  76. {
  77. // If it's a <dialog> or <wizard>, use roles::DIALOG instead
  78. dom::Element* rootElm = mDocumentNode->GetRootElement();
  79. if (rootElm && rootElm->IsAnyOfXULElements(nsGkAtoms::dialog,
  80. nsGkAtoms::wizard))
  81. return roles::DIALOG;
  82. return DocAccessibleWrap::NativeRole();
  83. }
  84. // RootAccessible protected member
  85. #ifdef MOZ_XUL
  86. uint32_t
  87. RootAccessible::GetChromeFlags()
  88. {
  89. // Return the flag set for the top level window as defined
  90. // by nsIWebBrowserChrome::CHROME_WINDOW_[FLAGNAME]
  91. // Not simple: nsIXULWindow is not just a QI from nsIDOMWindow
  92. nsCOMPtr<nsIDocShell> docShell = nsCoreUtils::GetDocShellFor(mDocumentNode);
  93. NS_ENSURE_TRUE(docShell, 0);
  94. nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
  95. docShell->GetTreeOwner(getter_AddRefs(treeOwner));
  96. NS_ENSURE_TRUE(treeOwner, 0);
  97. nsCOMPtr<nsIXULWindow> xulWin(do_GetInterface(treeOwner));
  98. if (!xulWin) {
  99. return 0;
  100. }
  101. uint32_t chromeFlags;
  102. xulWin->GetChromeFlags(&chromeFlags);
  103. return chromeFlags;
  104. }
  105. #endif
  106. uint64_t
  107. RootAccessible::NativeState()
  108. {
  109. uint64_t state = DocAccessibleWrap::NativeState();
  110. if (state & states::DEFUNCT)
  111. return state;
  112. #ifdef MOZ_XUL
  113. uint32_t chromeFlags = GetChromeFlags();
  114. if (chromeFlags & nsIWebBrowserChrome::CHROME_WINDOW_RESIZE)
  115. state |= states::SIZEABLE;
  116. // If it has a titlebar it's movable
  117. // XXX unless it's minimized or maximized, but not sure
  118. // how to detect that
  119. if (chromeFlags & nsIWebBrowserChrome::CHROME_TITLEBAR)
  120. state |= states::MOVEABLE;
  121. if (chromeFlags & nsIWebBrowserChrome::CHROME_MODAL)
  122. state |= states::MODAL;
  123. #endif
  124. nsFocusManager* fm = nsFocusManager::GetFocusManager();
  125. if (fm && fm->GetActiveWindow() == mDocumentNode->GetWindow())
  126. state |= states::ACTIVE;
  127. return state;
  128. }
  129. const char* const kEventTypes[] = {
  130. #ifdef DEBUG_DRAGDROPSTART
  131. // Capture mouse over events and fire fake DRAGDROPSTART event to simplify
  132. // debugging a11y objects with event viewers.
  133. "mouseover",
  134. #endif
  135. // Fired when list or tree selection changes.
  136. "select",
  137. // Fired when value changes immediately, wether or not focused changed.
  138. "ValueChange",
  139. "AlertActive",
  140. "TreeRowCountChanged",
  141. "TreeInvalidated",
  142. // add ourself as a OpenStateChange listener (custom event fired in tree.xml)
  143. "OpenStateChange",
  144. // add ourself as a CheckboxStateChange listener (custom event fired in HTMLInputElement.cpp)
  145. "CheckboxStateChange",
  146. // add ourself as a RadioStateChange Listener ( custom event fired in in HTMLInputElement.cpp & radio.xml)
  147. "RadioStateChange",
  148. "popupshown",
  149. "popuphiding",
  150. "DOMMenuInactive",
  151. "DOMMenuItemActive",
  152. "DOMMenuItemInactive",
  153. "DOMMenuBarActive",
  154. "DOMMenuBarInactive"
  155. };
  156. nsresult
  157. RootAccessible::AddEventListeners()
  158. {
  159. // EventTarget interface allows to register event listeners to
  160. // receive untrusted events (synthetic events generated by untrusted code).
  161. // For example, XBL bindings implementations for elements that are hosted in
  162. // non chrome document fire untrusted events.
  163. nsCOMPtr<EventTarget> nstarget = mDocumentNode;
  164. if (nstarget) {
  165. for (const char* const* e = kEventTypes,
  166. * const* e_end = ArrayEnd(kEventTypes);
  167. e < e_end; ++e) {
  168. nsresult rv = nstarget->AddEventListener(NS_ConvertASCIItoUTF16(*e),
  169. this, true, true, 2);
  170. NS_ENSURE_SUCCESS(rv, rv);
  171. }
  172. }
  173. return DocAccessible::AddEventListeners();
  174. }
  175. nsresult
  176. RootAccessible::RemoveEventListeners()
  177. {
  178. nsCOMPtr<EventTarget> target = mDocumentNode;
  179. if (target) {
  180. for (const char* const* e = kEventTypes,
  181. * const* e_end = ArrayEnd(kEventTypes);
  182. e < e_end; ++e) {
  183. nsresult rv = target->RemoveEventListener(NS_ConvertASCIItoUTF16(*e), this, true);
  184. NS_ENSURE_SUCCESS(rv, rv);
  185. }
  186. }
  187. // Do this before removing clearing caret accessible, so that it can use
  188. // shutdown the caret accessible's selection listener
  189. DocAccessible::RemoveEventListeners();
  190. return NS_OK;
  191. }
  192. ////////////////////////////////////////////////////////////////////////////////
  193. // public
  194. void
  195. RootAccessible::DocumentActivated(DocAccessible* aDocument)
  196. {
  197. }
  198. ////////////////////////////////////////////////////////////////////////////////
  199. // nsIDOMEventListener
  200. NS_IMETHODIMP
  201. RootAccessible::HandleEvent(nsIDOMEvent* aDOMEvent)
  202. {
  203. MOZ_ASSERT(aDOMEvent);
  204. Event* event = aDOMEvent->InternalDOMEvent();
  205. nsCOMPtr<nsINode> origTargetNode = do_QueryInterface(event->GetOriginalTarget());
  206. if (!origTargetNode)
  207. return NS_OK;
  208. #ifdef A11Y_LOG
  209. if (logging::IsEnabled(logging::eDOMEvents)) {
  210. nsAutoString eventType;
  211. aDOMEvent->GetType(eventType);
  212. logging::DOMEvent("handled", origTargetNode, eventType);
  213. }
  214. #endif
  215. DocAccessible* document =
  216. GetAccService()->GetDocAccessible(origTargetNode->OwnerDoc());
  217. if (document) {
  218. // Root accessible exists longer than any of its descendant documents so
  219. // that we are guaranteed notification is processed before root accessible
  220. // is destroyed.
  221. document->HandleNotification<RootAccessible, nsIDOMEvent>
  222. (this, &RootAccessible::ProcessDOMEvent, aDOMEvent);
  223. }
  224. return NS_OK;
  225. }
  226. // RootAccessible protected
  227. void
  228. RootAccessible::ProcessDOMEvent(nsIDOMEvent* aDOMEvent)
  229. {
  230. MOZ_ASSERT(aDOMEvent);
  231. Event* event = aDOMEvent->InternalDOMEvent();
  232. nsCOMPtr<nsINode> origTargetNode = do_QueryInterface(event->GetOriginalTarget());
  233. nsAutoString eventType;
  234. aDOMEvent->GetType(eventType);
  235. #ifdef A11Y_LOG
  236. if (logging::IsEnabled(logging::eDOMEvents))
  237. logging::DOMEvent("processed", origTargetNode, eventType);
  238. #endif
  239. if (eventType.EqualsLiteral("popuphiding")) {
  240. HandlePopupHidingEvent(origTargetNode);
  241. return;
  242. }
  243. DocAccessible* targetDocument = GetAccService()->
  244. GetDocAccessible(origTargetNode->OwnerDoc());
  245. NS_ASSERTION(targetDocument, "No document while accessible is in document?!");
  246. Accessible* accessible =
  247. targetDocument->GetAccessibleOrContainer(origTargetNode);
  248. if (!accessible)
  249. return;
  250. #ifdef MOZ_XUL
  251. XULTreeAccessible* treeAcc = accessible->AsXULTree();
  252. if (treeAcc) {
  253. if (eventType.EqualsLiteral("TreeRowCountChanged")) {
  254. HandleTreeRowCountChangedEvent(aDOMEvent, treeAcc);
  255. return;
  256. }
  257. if (eventType.EqualsLiteral("TreeInvalidated")) {
  258. HandleTreeInvalidatedEvent(aDOMEvent, treeAcc);
  259. return;
  260. }
  261. }
  262. #endif
  263. if (eventType.EqualsLiteral("RadioStateChange")) {
  264. uint64_t state = accessible->State();
  265. bool isEnabled = (state & (states::CHECKED | states::SELECTED)) != 0;
  266. if (accessible->NeedsDOMUIEvent()) {
  267. RefPtr<AccEvent> accEvent =
  268. new AccStateChangeEvent(accessible, states::CHECKED, isEnabled);
  269. nsEventShell::FireEvent(accEvent);
  270. }
  271. if (isEnabled) {
  272. FocusMgr()->ActiveItemChanged(accessible);
  273. #ifdef A11Y_LOG
  274. if (logging::IsEnabled(logging::eFocus))
  275. logging::ActiveItemChangeCausedBy("RadioStateChange", accessible);
  276. #endif
  277. }
  278. return;
  279. }
  280. if (eventType.EqualsLiteral("CheckboxStateChange")) {
  281. if (accessible->NeedsDOMUIEvent()) {
  282. uint64_t state = accessible->State();
  283. bool isEnabled = !!(state & states::CHECKED);
  284. RefPtr<AccEvent> accEvent =
  285. new AccStateChangeEvent(accessible, states::CHECKED, isEnabled);
  286. nsEventShell::FireEvent(accEvent);
  287. }
  288. return;
  289. }
  290. Accessible* treeItemAcc = nullptr;
  291. #ifdef MOZ_XUL
  292. // If it's a tree element, need the currently selected item.
  293. if (treeAcc) {
  294. treeItemAcc = accessible->CurrentItem();
  295. if (treeItemAcc)
  296. accessible = treeItemAcc;
  297. }
  298. if (treeItemAcc && eventType.EqualsLiteral("OpenStateChange")) {
  299. uint64_t state = accessible->State();
  300. bool isEnabled = (state & states::EXPANDED) != 0;
  301. RefPtr<AccEvent> accEvent =
  302. new AccStateChangeEvent(accessible, states::EXPANDED, isEnabled);
  303. nsEventShell::FireEvent(accEvent);
  304. return;
  305. }
  306. nsINode* targetNode = accessible->GetNode();
  307. if (treeItemAcc && eventType.EqualsLiteral("select")) {
  308. // XXX: We shouldn't be based on DOM select event which doesn't provide us
  309. // any context info. We should integrate into nsTreeSelection instead.
  310. // If multiselect tree, we should fire selectionadd or selection removed
  311. if (FocusMgr()->HasDOMFocus(targetNode)) {
  312. nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSel =
  313. do_QueryInterface(targetNode);
  314. nsAutoString selType;
  315. multiSel->GetSelType(selType);
  316. if (selType.IsEmpty() || !selType.EqualsLiteral("single")) {
  317. // XXX: We need to fire EVENT_SELECTION_ADD and EVENT_SELECTION_REMOVE
  318. // for each tree item. Perhaps each tree item will need to cache its
  319. // selection state and fire an event after a DOM "select" event when
  320. // that state changes. XULTreeAccessible::UpdateTreeSelection();
  321. nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_SELECTION_WITHIN,
  322. accessible);
  323. return;
  324. }
  325. RefPtr<AccSelChangeEvent> selChangeEvent =
  326. new AccSelChangeEvent(treeAcc, treeItemAcc,
  327. AccSelChangeEvent::eSelectionAdd);
  328. nsEventShell::FireEvent(selChangeEvent);
  329. return;
  330. }
  331. }
  332. else
  333. #endif
  334. if (eventType.EqualsLiteral("AlertActive")) {
  335. nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_ALERT, accessible);
  336. }
  337. else if (eventType.EqualsLiteral("popupshown")) {
  338. HandlePopupShownEvent(accessible);
  339. }
  340. else if (eventType.EqualsLiteral("DOMMenuInactive")) {
  341. if (accessible->Role() == roles::MENUPOPUP) {
  342. nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_END,
  343. accessible);
  344. }
  345. }
  346. else if (eventType.EqualsLiteral("DOMMenuItemActive")) {
  347. FocusMgr()->ActiveItemChanged(accessible);
  348. #ifdef A11Y_LOG
  349. if (logging::IsEnabled(logging::eFocus))
  350. logging::ActiveItemChangeCausedBy("DOMMenuItemActive", accessible);
  351. #endif
  352. }
  353. else if (eventType.EqualsLiteral("DOMMenuItemInactive")) {
  354. // Process DOMMenuItemInactive event for autocomplete only because this is
  355. // unique widget that may acquire focus from autocomplete popup while popup
  356. // stays open and has no active item. In case of XUL tree autocomplete
  357. // popup this event is fired for tree accessible.
  358. Accessible* widget =
  359. accessible->IsWidget() ? accessible : accessible->ContainerWidget();
  360. if (widget && widget->IsAutoCompletePopup()) {
  361. FocusMgr()->ActiveItemChanged(nullptr);
  362. #ifdef A11Y_LOG
  363. if (logging::IsEnabled(logging::eFocus))
  364. logging::ActiveItemChangeCausedBy("DOMMenuItemInactive", accessible);
  365. #endif
  366. }
  367. }
  368. else if (eventType.EqualsLiteral("DOMMenuBarActive")) { // Always from user input
  369. nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENU_START,
  370. accessible, eFromUserInput);
  371. // Notify of active item change when menubar gets active and if it has
  372. // current item. This is a case of mouseover (set current menuitem) and
  373. // mouse click (activate the menubar). If menubar doesn't have current item
  374. // (can be a case of menubar activation from keyboard) then ignore this
  375. // notification because later we'll receive DOMMenuItemActive event after
  376. // current menuitem is set.
  377. Accessible* activeItem = accessible->CurrentItem();
  378. if (activeItem) {
  379. FocusMgr()->ActiveItemChanged(activeItem);
  380. #ifdef A11Y_LOG
  381. if (logging::IsEnabled(logging::eFocus))
  382. logging::ActiveItemChangeCausedBy("DOMMenuBarActive", accessible);
  383. #endif
  384. }
  385. }
  386. else if (eventType.EqualsLiteral("DOMMenuBarInactive")) { // Always from user input
  387. nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENU_END,
  388. accessible, eFromUserInput);
  389. FocusMgr()->ActiveItemChanged(nullptr);
  390. #ifdef A11Y_LOG
  391. if (logging::IsEnabled(logging::eFocus))
  392. logging::ActiveItemChangeCausedBy("DOMMenuBarInactive", accessible);
  393. #endif
  394. }
  395. else if (accessible->NeedsDOMUIEvent() &&
  396. eventType.EqualsLiteral("ValueChange")) {
  397. uint32_t event = accessible->HasNumericValue()
  398. ? nsIAccessibleEvent::EVENT_VALUE_CHANGE
  399. : nsIAccessibleEvent::EVENT_TEXT_VALUE_CHANGE;
  400. targetDocument->FireDelayedEvent(event, accessible);
  401. }
  402. #ifdef DEBUG_DRAGDROPSTART
  403. else if (eventType.EqualsLiteral("mouseover")) {
  404. nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_DRAGDROP_START,
  405. accessible);
  406. }
  407. #endif
  408. }
  409. ////////////////////////////////////////////////////////////////////////////////
  410. // Accessible
  411. void
  412. RootAccessible::Shutdown()
  413. {
  414. // Called manually or by Accessible::LastRelease()
  415. if (!PresShell())
  416. return; // Already shutdown
  417. DocAccessibleWrap::Shutdown();
  418. }
  419. Relation
  420. RootAccessible::RelationByType(RelationType aType)
  421. {
  422. if (!mDocumentNode || aType != RelationType::EMBEDS)
  423. return DocAccessibleWrap::RelationByType(aType);
  424. if (nsPIDOMWindowOuter* rootWindow = mDocumentNode->GetWindow()) {
  425. nsCOMPtr<nsPIDOMWindowOuter> contentWindow = nsGlobalWindow::Cast(rootWindow)->GetContent();
  426. if (contentWindow) {
  427. nsCOMPtr<nsIDocument> contentDocumentNode = contentWindow->GetDoc();
  428. if (contentDocumentNode) {
  429. DocAccessible* contentDocument =
  430. GetAccService()->GetDocAccessible(contentDocumentNode);
  431. if (contentDocument)
  432. return Relation(contentDocument);
  433. }
  434. }
  435. }
  436. return Relation();
  437. }
  438. ////////////////////////////////////////////////////////////////////////////////
  439. // Protected members
  440. void
  441. RootAccessible::HandlePopupShownEvent(Accessible* aAccessible)
  442. {
  443. roles::Role role = aAccessible->Role();
  444. if (role == roles::MENUPOPUP) {
  445. // Don't fire menupopup events for combobox and autocomplete lists.
  446. nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_START,
  447. aAccessible);
  448. return;
  449. }
  450. if (role == roles::TOOLTIP) {
  451. // There is a single <xul:tooltip> node which Mozilla moves around.
  452. // The accessible for it stays the same no matter where it moves.
  453. // AT's expect to get an EVENT_SHOW for the tooltip.
  454. // In event callback the tooltip's accessible will be ready.
  455. nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_SHOW, aAccessible);
  456. return;
  457. }
  458. if (role == roles::COMBOBOX_LIST) {
  459. // Fire expanded state change event for comboboxes and autocompeletes.
  460. Accessible* combobox = aAccessible->Parent();
  461. if (!combobox)
  462. return;
  463. roles::Role comboboxRole = combobox->Role();
  464. if (comboboxRole == roles::COMBOBOX ||
  465. comboboxRole == roles::AUTOCOMPLETE) {
  466. RefPtr<AccEvent> event =
  467. new AccStateChangeEvent(combobox, states::EXPANDED, true);
  468. if (event)
  469. nsEventShell::FireEvent(event);
  470. }
  471. }
  472. }
  473. void
  474. RootAccessible::HandlePopupHidingEvent(nsINode* aPopupNode)
  475. {
  476. // Get popup accessible. There are cases when popup element isn't accessible
  477. // but an underlying widget is and behaves like popup, an example is
  478. // autocomplete popups.
  479. DocAccessible* document = nsAccUtils::GetDocAccessibleFor(aPopupNode);
  480. if (!document)
  481. return;
  482. Accessible* popup = document->GetAccessible(aPopupNode);
  483. if (!popup) {
  484. Accessible* popupContainer = document->GetContainerAccessible(aPopupNode);
  485. if (!popupContainer)
  486. return;
  487. uint32_t childCount = popupContainer->ChildCount();
  488. for (uint32_t idx = 0; idx < childCount; idx++) {
  489. Accessible* child = popupContainer->GetChildAt(idx);
  490. if (child->IsAutoCompletePopup()) {
  491. popup = child;
  492. break;
  493. }
  494. }
  495. // No popup no events. Focus is managed by DOM. This is a case for
  496. // menupopups of menus on Linux since there are no accessible for popups.
  497. if (!popup)
  498. return;
  499. }
  500. // In case of autocompletes and comboboxes fire state change event for
  501. // expanded state. Note, HTML form autocomplete isn't a subject of state
  502. // change event because they aren't autocompletes strictly speaking.
  503. // When popup closes (except nested popups and menus) then fire focus event to
  504. // where it was. The focus event is expected even if popup didn't take a focus.
  505. static const uint32_t kNotifyOfFocus = 1;
  506. static const uint32_t kNotifyOfState = 2;
  507. uint32_t notifyOf = 0;
  508. // HTML select is target of popuphidding event. Otherwise get container
  509. // widget. No container widget means this is either tooltip or menupopup.
  510. // No events in the former case.
  511. Accessible* widget = nullptr;
  512. if (popup->IsCombobox()) {
  513. widget = popup;
  514. } else {
  515. widget = popup->ContainerWidget();
  516. if (!widget) {
  517. if (!popup->IsMenuPopup())
  518. return;
  519. widget = popup;
  520. }
  521. }
  522. if (popup->IsAutoCompletePopup()) {
  523. // No focus event for autocomplete because it's managed by
  524. // DOMMenuItemInactive events.
  525. if (widget->IsAutoComplete())
  526. notifyOf = kNotifyOfState;
  527. } else if (widget->IsCombobox()) {
  528. // Fire focus for active combobox, otherwise the focus is managed by DOM
  529. // focus notifications. Always fire state change event.
  530. if (widget->IsActiveWidget())
  531. notifyOf = kNotifyOfFocus;
  532. notifyOf |= kNotifyOfState;
  533. } else if (widget->IsMenuButton()) {
  534. // Can be a part of autocomplete.
  535. Accessible* compositeWidget = widget->ContainerWidget();
  536. if (compositeWidget && compositeWidget->IsAutoComplete()) {
  537. widget = compositeWidget;
  538. notifyOf = kNotifyOfState;
  539. }
  540. // Autocomplete (like searchbar) can be inactive when popup hiddens
  541. notifyOf |= kNotifyOfFocus;
  542. } else if (widget == popup) {
  543. // Top level context menus and alerts.
  544. // Ignore submenus and menubar. When submenu is closed then sumbenu
  545. // container menuitem takes a focus via DOMMenuItemActive notification.
  546. // For menubars processing we listen DOMMenubarActive/Inactive
  547. // notifications.
  548. notifyOf = kNotifyOfFocus;
  549. }
  550. // Restore focus to where it was.
  551. if (notifyOf & kNotifyOfFocus) {
  552. FocusMgr()->ActiveItemChanged(nullptr);
  553. #ifdef A11Y_LOG
  554. if (logging::IsEnabled(logging::eFocus))
  555. logging::ActiveItemChangeCausedBy("popuphiding", popup);
  556. #endif
  557. }
  558. // Fire expanded state change event.
  559. if (notifyOf & kNotifyOfState) {
  560. RefPtr<AccEvent> event =
  561. new AccStateChangeEvent(widget, states::EXPANDED, false);
  562. document->FireDelayedEvent(event);
  563. }
  564. }
  565. #ifdef MOZ_XUL
  566. void
  567. RootAccessible::HandleTreeRowCountChangedEvent(nsIDOMEvent* aEvent,
  568. XULTreeAccessible* aAccessible)
  569. {
  570. nsCOMPtr<nsIDOMCustomEvent> customEvent(do_QueryInterface(aEvent));
  571. if (!customEvent)
  572. return;
  573. nsCOMPtr<nsIVariant> detailVariant;
  574. customEvent->GetDetail(getter_AddRefs(detailVariant));
  575. if (!detailVariant)
  576. return;
  577. nsCOMPtr<nsISupports> supports;
  578. detailVariant->GetAsISupports(getter_AddRefs(supports));
  579. nsCOMPtr<nsIPropertyBag2> propBag(do_QueryInterface(supports));
  580. if (!propBag)
  581. return;
  582. nsresult rv;
  583. int32_t index, count;
  584. rv = propBag->GetPropertyAsInt32(NS_LITERAL_STRING("index"), &index);
  585. if (NS_FAILED(rv))
  586. return;
  587. rv = propBag->GetPropertyAsInt32(NS_LITERAL_STRING("count"), &count);
  588. if (NS_FAILED(rv))
  589. return;
  590. aAccessible->InvalidateCache(index, count);
  591. }
  592. void
  593. RootAccessible::HandleTreeInvalidatedEvent(nsIDOMEvent* aEvent,
  594. XULTreeAccessible* aAccessible)
  595. {
  596. nsCOMPtr<nsIDOMCustomEvent> customEvent(do_QueryInterface(aEvent));
  597. if (!customEvent)
  598. return;
  599. nsCOMPtr<nsIVariant> detailVariant;
  600. customEvent->GetDetail(getter_AddRefs(detailVariant));
  601. if (!detailVariant)
  602. return;
  603. nsCOMPtr<nsISupports> supports;
  604. detailVariant->GetAsISupports(getter_AddRefs(supports));
  605. nsCOMPtr<nsIPropertyBag2> propBag(do_QueryInterface(supports));
  606. if (!propBag)
  607. return;
  608. int32_t startRow = 0, endRow = -1, startCol = 0, endCol = -1;
  609. propBag->GetPropertyAsInt32(NS_LITERAL_STRING("startrow"),
  610. &startRow);
  611. propBag->GetPropertyAsInt32(NS_LITERAL_STRING("endrow"),
  612. &endRow);
  613. propBag->GetPropertyAsInt32(NS_LITERAL_STRING("startcolumn"),
  614. &startCol);
  615. propBag->GetPropertyAsInt32(NS_LITERAL_STRING("endcolumn"),
  616. &endCol);
  617. aAccessible->TreeViewInvalidated(startRow, endRow, startCol, endCol);
  618. }
  619. #endif
  620. ProxyAccessible*
  621. RootAccessible::GetPrimaryRemoteTopLevelContentDoc() const
  622. {
  623. nsCOMPtr<nsIDocShellTreeOwner> owner;
  624. mDocumentNode->GetDocShell()->GetTreeOwner(getter_AddRefs(owner));
  625. NS_ENSURE_TRUE(owner, nullptr);
  626. nsCOMPtr<nsITabParent> tabParent;
  627. owner->GetPrimaryTabParent(getter_AddRefs(tabParent));
  628. if (!tabParent) {
  629. return nullptr;
  630. }
  631. auto tab = static_cast<dom::TabParent*>(tabParent.get());
  632. return tab->GetTopLevelDocAccessible();
  633. }