Coverage Report

Created: 2018-09-25 14:53

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