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