Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/accessible/base/nsAccessibilityService.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 "nsAccessibilityService.h"
7
8
// NOTE: alphabetically ordered
9
#include "ApplicationAccessibleWrap.h"
10
#include "ARIAGridAccessibleWrap.h"
11
#include "ARIAMap.h"
12
#include "DocAccessible-inl.h"
13
#include "FocusManager.h"
14
#include "HTMLCanvasAccessible.h"
15
#include "HTMLElementAccessibles.h"
16
#include "HTMLImageMapAccessible.h"
17
#include "HTMLLinkAccessible.h"
18
#include "HTMLListAccessible.h"
19
#include "HTMLSelectAccessible.h"
20
#include "HTMLTableAccessibleWrap.h"
21
#include "HyperTextAccessibleWrap.h"
22
#include "RootAccessible.h"
23
#include "nsAccUtils.h"
24
#include "nsArrayUtils.h"
25
#include "nsAttrName.h"
26
#include "nsDOMTokenList.h"
27
#include "nsEventShell.h"
28
#include "nsIURI.h"
29
#include "nsTextFormatter.h"
30
#include "OuterDocAccessible.h"
31
#include "Role.h"
32
#ifdef MOZ_ACCESSIBILITY_ATK
33
#include "RootAccessibleWrap.h"
34
#endif
35
#include "States.h"
36
#include "Statistics.h"
37
#include "TextLeafAccessibleWrap.h"
38
#include "TreeWalker.h"
39
#include "xpcAccessibleApplication.h"
40
#include "xpcAccessibleDocument.h"
41
42
#ifdef MOZ_ACCESSIBILITY_ATK
43
#include "AtkSocketAccessible.h"
44
#endif
45
46
#ifdef XP_WIN
47
#include "mozilla/a11y/Compatibility.h"
48
#include "mozilla/dom/ContentChild.h"
49
#include "HTMLWin32ObjectAccessible.h"
50
#include "mozilla/StaticPtr.h"
51
#endif
52
53
#ifdef A11Y_LOG
54
#include "Logging.h"
55
#endif
56
57
#include "nsExceptionHandler.h"
58
#include "nsImageFrame.h"
59
#include "nsINamed.h"
60
#include "nsIObserverService.h"
61
#include "nsLayoutUtils.h"
62
#include "nsPluginFrame.h"
63
#include "SVGGeometryFrame.h"
64
#include "nsTreeBodyFrame.h"
65
#include "nsTreeColumns.h"
66
#include "nsTreeUtils.h"
67
#include "nsXBLPrototypeBinding.h"
68
#include "nsXBLBinding.h"
69
#include "mozilla/ArrayUtils.h"
70
#include "mozilla/dom/DOMStringList.h"
71
#include "mozilla/dom/EventTarget.h"
72
#include "mozilla/Preferences.h"
73
#include "mozilla/Services.h"
74
#include "nsDeckFrame.h"
75
76
#ifdef MOZ_XUL
77
#include "XULAlertAccessible.h"
78
#include "XULColorPickerAccessible.h"
79
#include "XULComboboxAccessible.h"
80
#include "XULElementAccessibles.h"
81
#include "XULFormControlAccessible.h"
82
#include "XULListboxAccessibleWrap.h"
83
#include "XULMenuAccessibleWrap.h"
84
#include "XULTabAccessible.h"
85
#include "XULTreeGridAccessibleWrap.h"
86
#endif
87
88
#if defined(XP_WIN) || defined(MOZ_ACCESSIBILITY_ATK)
89
#include "nsNPAPIPluginInstance.h"
90
#endif
91
92
using namespace mozilla;
93
using namespace mozilla::a11y;
94
using namespace mozilla::dom;
95
96
/**
97
 * Accessibility service force enable/disable preference.
98
 * Supported values:
99
 *   Accessibility is force enabled (accessibility should always be enabled): -1
100
 *   Accessibility is enabled (will be started upon a request, default value): 0
101
 *   Accessibility is force disabled (never enable accessibility):             1
102
 */
103
0
#define PREF_ACCESSIBILITY_FORCE_DISABLED "accessibility.force_disabled"
104
105
////////////////////////////////////////////////////////////////////////////////
106
// Statics
107
////////////////////////////////////////////////////////////////////////////////
108
109
/**
110
 * Return true if the element must be accessible.
111
 */
112
static bool
113
MustBeAccessible(nsIContent* aContent, DocAccessible* aDocument)
114
0
{
115
0
  if (aContent->GetPrimaryFrame()->IsFocusable())
116
0
    return true;
117
0
118
0
  if (aContent->IsElement()) {
119
0
    uint32_t attrCount = aContent->AsElement()->GetAttrCount();
120
0
    for (uint32_t attrIdx = 0; attrIdx < attrCount; attrIdx++) {
121
0
      const nsAttrName* attr = aContent->AsElement()->GetAttrNameAt(attrIdx);
122
0
      if (attr->NamespaceEquals(kNameSpaceID_None)) {
123
0
        nsAtom* attrAtom = attr->Atom();
124
0
        nsDependentAtomString attrStr(attrAtom);
125
0
        if (!StringBeginsWith(attrStr, NS_LITERAL_STRING("aria-")))
126
0
          continue; // not ARIA
127
0
128
0
        // A global state or a property and in case of token defined.
129
0
        uint8_t attrFlags = aria::AttrCharacteristicsFor(attrAtom);
130
0
        if ((attrFlags & ATTR_GLOBAL) && (!(attrFlags & ATTR_VALTOKEN) ||
131
0
             nsAccUtils::HasDefinedARIAToken(aContent, attrAtom))) {
132
0
          return true;
133
0
        }
134
0
      }
135
0
    }
136
0
  }
137
0
138
0
  // If the given ID is referred by relation attribute then create an accessible
139
0
  // for it.
140
0
  nsAutoString id;
141
0
  if (nsCoreUtils::GetID(aContent, id) && !id.IsEmpty())
142
0
    return aDocument->IsDependentID(id);
143
0
144
0
  return false;
145
0
}
146
147
/**
148
 * Used by XULMap.h to map both menupopup and popup elements
149
 */
150
#ifdef MOZ_XUL
151
Accessible*
152
CreateMenupopupAccessible(Element* aElement, Accessible* aContext)
153
0
{
154
0
#ifdef MOZ_ACCESSIBILITY_ATK
155
0
    // ATK considers this node to be redundant when within menubars, and it makes menu
156
0
    // navigation with assistive technologies more difficult
157
0
    // XXX In the future we will should this for consistency across the nsIAccessible
158
0
    // implementations on each platform for a consistent scripting environment, but
159
0
    // then strip out redundant accessibles in the AccessibleWrap class for each platform.
160
0
    nsIContent *parent = aElement->GetParent();
161
0
    if (parent && parent->IsXULElement(nsGkAtoms::menu))
162
0
      return nullptr;
163
0
#endif
164
0
165
0
    return new XULMenupopupAccessible(aElement, aContext->Document());
166
0
}
167
#endif
168
169
////////////////////////////////////////////////////////////////////////////////
170
// Accessible constructors
171
172
static Accessible*
173
New_HTMLLink(Element* aElement, Accessible* aContext)
174
0
{
175
0
  // Only some roles truly enjoy life as HTMLLinkAccessibles, for details
176
0
  // see closed bug 494807.
177
0
  const nsRoleMapEntry* roleMapEntry = aria::GetRoleMap(aElement);
178
0
  if (roleMapEntry && roleMapEntry->role != roles::NOTHING &&
179
0
      roleMapEntry->role != roles::LINK) {
180
0
    return new HyperTextAccessibleWrap(aElement, aContext->Document());
181
0
  }
182
0
183
0
  return new HTMLLinkAccessible(aElement, aContext->Document());
184
0
}
185
186
static Accessible* New_HyperText(Element* aElement, Accessible* aContext)
187
0
  { return new HyperTextAccessibleWrap(aElement, aContext->Document()); }
188
189
static Accessible* New_HTMLFigcaption(Element* aElement, Accessible* aContext)
190
0
  { return new HTMLFigcaptionAccessible(aElement, aContext->Document()); }
191
192
static Accessible* New_HTMLFigure(Element* aElement, Accessible* aContext)
193
0
  { return new HTMLFigureAccessible(aElement, aContext->Document()); }
194
195
static Accessible* New_HTMLHeaderOrFooter(Element* aElement, Accessible* aContext)
196
0
  { return new HTMLHeaderOrFooterAccessible(aElement, aContext->Document()); }
197
198
static Accessible* New_HTMLLegend(Element* aElement, Accessible* aContext)
199
0
  { return new HTMLLegendAccessible(aElement, aContext->Document()); }
200
201
static Accessible* New_HTMLOption(Element* aElement, Accessible* aContext)
202
0
  { return new HTMLSelectOptionAccessible(aElement, aContext->Document()); }
203
204
static Accessible* New_HTMLOptgroup(Element* aElement, Accessible* aContext)
205
0
  { return new HTMLSelectOptGroupAccessible(aElement, aContext->Document()); }
206
207
static Accessible* New_HTMLList(Element* aElement, Accessible* aContext)
208
0
  { return new HTMLListAccessible(aElement, aContext->Document()); }
209
210
static Accessible*
211
New_HTMLListitem(Element* aElement, Accessible* aContext)
212
0
{
213
0
  // If list item is a child of accessible list then create an accessible for
214
0
  // it unconditionally by tag name. nsBlockFrame creates the list item
215
0
  // accessible for other elements styled as list items.
216
0
  if (aContext->IsList() && aContext->GetContent() == aElement->GetParent())
217
0
    return new HTMLLIAccessible(aElement, aContext->Document());
218
0
219
0
  return nullptr;
220
0
}
221
222
template<typename AccClass>
223
static Accessible*
224
New_HTMLDtOrDd(Element* aElement, Accessible* aContext)
225
0
{
226
0
  nsIContent* parent = aContext->GetContent();
227
0
  if (parent->IsHTMLElement(nsGkAtoms::div)) {
228
0
    // It is conforming in HTML to use a div to group dt/dd elements.
229
0
    parent = parent->GetParent();
230
0
  }
231
0
232
0
  if (parent && parent->IsHTMLElement(nsGkAtoms::dl)) {
233
0
    return new AccClass(aElement, aContext->Document());
234
0
  }
235
0
236
0
  return nullptr;
237
0
}
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:mozilla::a11y::Accessible* New_HTMLDtOrDd<mozilla::a11y::HyperTextAccessible>(mozilla::dom::Element*, mozilla::a11y::Accessible*)
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:mozilla::a11y::Accessible* New_HTMLDtOrDd<mozilla::a11y::HTMLLIAccessible>(mozilla::dom::Element*, mozilla::a11y::Accessible*)
238
239
static Accessible* New_HTMLLabel(Element* aElement, Accessible* aContext)
240
0
  { return new HTMLLabelAccessible(aElement, aContext->Document()); }
241
242
static Accessible* New_HTMLInput(Element* aElement, Accessible* aContext)
243
0
{
244
0
  if (aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
245
0
                           nsGkAtoms::checkbox, eIgnoreCase)) {
246
0
    return new CheckboxAccessible(aElement, aContext->Document());
247
0
  }
248
0
  if (aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
249
0
                           nsGkAtoms::radio, eIgnoreCase)) {
250
0
    return new HTMLRadioButtonAccessible(aElement, aContext->Document());
251
0
  }
252
0
  if (aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
253
0
                           nsGkAtoms::time, eIgnoreCase)) {
254
0
    return new EnumRoleAccessible<roles::GROUPING>(aElement, aContext->Document());
255
0
  }
256
0
  if (aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
257
0
                           nsGkAtoms::date, eIgnoreCase)) {
258
0
    return new EnumRoleAccessible<roles::DATE_EDITOR>(aElement, aContext->Document());
259
0
  }
260
0
  return nullptr;
261
0
}
262
263
static Accessible* New_HTMLOutput(Element* aElement, Accessible* aContext)
264
0
  { return new HTMLOutputAccessible(aElement, aContext->Document()); }
265
266
static Accessible* New_HTMLProgress(Element* aElement, Accessible* aContext)
267
0
  { return new HTMLProgressMeterAccessible(aElement, aContext->Document()); }
268
269
static Accessible* New_HTMLSummary(Element* aElement, Accessible* aContext)
270
0
  { return new HTMLSummaryAccessible(aElement, aContext->Document()); }
271
272
static Accessible*
273
New_HTMLTableAccessible(Element* aElement, Accessible* aContext)
274
0
  { return new HTMLTableAccessible(aElement, aContext->Document()); }
275
276
static Accessible*
277
New_HTMLTableRowAccessible(Element* aElement, Accessible* aContext)
278
0
  { return new HTMLTableRowAccessible(aElement, aContext->Document()); }
279
280
static Accessible*
281
New_HTMLTableCellAccessible(Element* aElement, Accessible* aContext)
282
0
  { return new HTMLTableCellAccessible(aElement, aContext->Document()); }
283
284
/**
285
 * Cached value of the PREF_ACCESSIBILITY_FORCE_DISABLED preference.
286
 */
287
static int32_t sPlatformDisabledState = 0;
288
289
////////////////////////////////////////////////////////////////////////////////
290
// Markup maps array.
291
292
#define Attr(name, value) \
293
  { &nsGkAtoms::name, &nsGkAtoms::value }
294
295
#define AttrFromDOM(name, DOMAttrName) \
296
  { &nsGkAtoms::name, nullptr, &nsGkAtoms::DOMAttrName }
297
298
#define AttrFromDOMIf(name, DOMAttrName, DOMAttrValue) \
299
  { &nsGkAtoms::name, nullptr,  &nsGkAtoms::DOMAttrName, &nsGkAtoms::DOMAttrValue }
300
301
#define MARKUPMAP(atom, new_func, r, ... ) \
302
0
  { &nsGkAtoms::atom, new_func, static_cast<a11y::role>(r), { __VA_ARGS__ } },
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_0::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_1::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_2::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_3::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
303
304
static const HTMLMarkupMapInfo sHTMLMarkupMapList[] = {
305
  #include "MarkupMap.h"
306
};
307
308
#undef MARKUPMAP
309
310
#ifdef MOZ_XUL
311
#define XULMAP(atom, ...) \
312
0
  { &nsGkAtoms::atom, __VA_ARGS__ },
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_4::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_5::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_6::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_7::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_8::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_9::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_10::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_11::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_12::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_13::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_14::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_15::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_16::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_17::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_18::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_19::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_20::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_21::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_22::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_23::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_24::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_25::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_26::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_27::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_28::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_29::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_30::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_31::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_32::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_33::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_34::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_35::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_36::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_37::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_38::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_39::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_40::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_41::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_42::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_43::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_44::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_45::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
Unexecuted instantiation: Unified_cpp_accessible_base1.cpp:$_46::operator()(mozilla::dom::Element*, mozilla::a11y::Accessible*) const
313
314
#define XULMAP_TYPE(atom, new_type) \
315
XULMAP( \
316
  atom, \
317
  [](Element* aElement, Accessible* aContext) -> Accessible* { \
318
    return new new_type(aElement, aContext->Document()); \
319
  } \
320
)
321
322
static const XULMarkupMapInfo sXULMarkupMapList[] = {
323
  #include "XULMap.h"
324
};
325
326
#undef XULMAP_TYPE
327
#undef XULMAP
328
#endif
329
330
#undef Attr
331
#undef AttrFromDOM
332
#undef AttrFromDOMIf
333
334
////////////////////////////////////////////////////////////////////////////////
335
// nsAccessibilityService
336
////////////////////////////////////////////////////////////////////////////////
337
338
nsAccessibilityService *nsAccessibilityService::gAccessibilityService = nullptr;
339
ApplicationAccessible* nsAccessibilityService::gApplicationAccessible = nullptr;
340
xpcAccessibleApplication* nsAccessibilityService::gXPCApplicationAccessible = nullptr;
341
uint32_t nsAccessibilityService::gConsumers = 0;
342
343
nsAccessibilityService::nsAccessibilityService() :
344
  DocManager(), FocusManager(), mHTMLMarkupMap(ArrayLength(sHTMLMarkupMapList))
345
#ifdef MOZ_XUL
346
  , mXULMarkupMap(ArrayLength(sXULMarkupMapList))
347
#endif
348
0
{
349
0
}
350
351
nsAccessibilityService::~nsAccessibilityService()
352
0
{
353
0
  NS_ASSERTION(IsShutdown(), "Accessibility wasn't shutdown!");
354
0
  gAccessibilityService = nullptr;
355
0
}
356
357
////////////////////////////////////////////////////////////////////////////////
358
// nsIListenerChangeListener
359
360
NS_IMETHODIMP
361
nsAccessibilityService::ListenersChanged(nsIArray* aEventChanges)
362
0
{
363
0
  uint32_t targetCount;
364
0
  nsresult rv = aEventChanges->GetLength(&targetCount);
365
0
  NS_ENSURE_SUCCESS(rv, rv);
366
0
367
0
  for (uint32_t i = 0 ; i < targetCount ; i++) {
368
0
    nsCOMPtr<nsIEventListenerChange> change = do_QueryElementAt(aEventChanges, i);
369
0
370
0
    RefPtr<EventTarget> target;
371
0
    change->GetTarget(getter_AddRefs(target));
372
0
    nsCOMPtr<nsIContent> node(do_QueryInterface(target));
373
0
    if (!node || !node->IsHTMLElement()) {
374
0
      continue;
375
0
    }
376
0
377
0
    uint32_t changeCount;
378
0
    change->GetCountOfEventListenerChangesAffectingAccessibility(&changeCount);
379
0
    NS_ENSURE_SUCCESS(rv, rv);
380
0
381
0
    for (uint32_t i = 0 ; i < changeCount ; i++) {
382
0
      nsIDocument* ownerDoc = node->OwnerDoc();
383
0
      DocAccessible* document = GetExistingDocAccessible(ownerDoc);
384
0
385
0
      // Create an accessible for a inaccessible element having click event
386
0
      // handler.
387
0
      if (document && !document->HasAccessible(node) &&
388
0
          nsCoreUtils::HasClickListener(node)) {
389
0
        nsIContent* parentEl = node->GetFlattenedTreeParent();
390
0
        if (parentEl) {
391
0
          document->ContentInserted(parentEl, node, node->GetNextSibling());
392
0
        }
393
0
        break;
394
0
      }
395
0
    }
396
0
  }
397
0
  return NS_OK;
398
0
}
399
400
////////////////////////////////////////////////////////////////////////////////
401
// nsISupports
402
403
NS_IMPL_ISUPPORTS_INHERITED(nsAccessibilityService,
404
                            DocManager,
405
                            nsIObserver,
406
                            nsIListenerChangeListener,
407
                            nsISelectionListener) // from SelectionManager
408
409
////////////////////////////////////////////////////////////////////////////////
410
// nsIObserver
411
412
NS_IMETHODIMP
413
nsAccessibilityService::Observe(nsISupports *aSubject, const char *aTopic,
414
                         const char16_t *aData)
415
0
{
416
0
  if (!nsCRT::strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
417
0
    Shutdown();
418
0
  }
419
0
420
0
  return NS_OK;
421
0
}
422
423
void
424
nsAccessibilityService::NotifyOfAnchorJumpTo(nsIContent* aTargetNode)
425
0
{
426
0
  nsIDocument* documentNode = aTargetNode->GetUncomposedDoc();
427
0
  if (documentNode) {
428
0
    DocAccessible* document = GetDocAccessible(documentNode);
429
0
    if (document)
430
0
      document->SetAnchorJump(aTargetNode);
431
0
  }
432
0
}
433
434
void
435
nsAccessibilityService::FireAccessibleEvent(uint32_t aEvent,
436
                                            Accessible* aTarget)
437
0
{
438
0
  nsEventShell::FireEvent(aEvent, aTarget);
439
0
}
440
441
Accessible*
442
nsAccessibilityService::GetRootDocumentAccessible(nsIPresShell* aPresShell,
443
                                                  bool aCanCreate)
444
0
{
445
0
  nsIPresShell* ps = aPresShell;
446
0
  nsIDocument* documentNode = aPresShell->GetDocument();
447
0
  if (documentNode) {
448
0
    nsCOMPtr<nsIDocShellTreeItem> treeItem(documentNode->GetDocShell());
449
0
    if (treeItem) {
450
0
      nsCOMPtr<nsIDocShellTreeItem> rootTreeItem;
451
0
      treeItem->GetRootTreeItem(getter_AddRefs(rootTreeItem));
452
0
      if (treeItem != rootTreeItem) {
453
0
        nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(rootTreeItem));
454
0
        ps = docShell->GetPresShell();
455
0
      }
456
0
457
0
      return aCanCreate ? GetDocAccessible(ps) : ps->GetDocAccessible();
458
0
    }
459
0
  }
460
0
  return nullptr;
461
0
}
462
463
#ifdef XP_WIN
464
static StaticAutoPtr<nsTArray<nsCOMPtr<nsIContent> > > sPendingPlugins;
465
static StaticAutoPtr<nsTArray<nsCOMPtr<nsITimer> > > sPluginTimers;
466
467
class PluginTimerCallBack final : public nsITimerCallback
468
                                , public nsINamed
469
{
470
  ~PluginTimerCallBack() {}
471
472
public:
473
  explicit PluginTimerCallBack(nsIContent* aContent) : mContent(aContent) {}
474
475
  NS_DECL_ISUPPORTS
476
477
  NS_IMETHOD Notify(nsITimer* aTimer) final
478
  {
479
    if (!mContent->IsInUncomposedDoc())
480
      return NS_OK;
481
482
    nsIPresShell* ps = mContent->OwnerDoc()->GetShell();
483
    if (ps) {
484
      DocAccessible* doc = ps->GetDocAccessible();
485
      if (doc) {
486
        // Make sure that if we created an accessible for the plugin that wasn't
487
        // a plugin accessible we remove it before creating the right accessible.
488
        doc->RecreateAccessible(mContent);
489
        sPluginTimers->RemoveElement(aTimer);
490
        return NS_OK;
491
      }
492
    }
493
494
    // We couldn't get a doc accessible so presumably the document went away.
495
    // In this case don't leak our ref to the content or timer.
496
    sPendingPlugins->RemoveElement(mContent);
497
    sPluginTimers->RemoveElement(aTimer);
498
    return NS_OK;
499
  }
500
501
  NS_IMETHOD GetName(nsACString& aName) final
502
  {
503
    aName.AssignLiteral("PluginTimerCallBack");
504
    return NS_OK;
505
  }
506
507
private:
508
  nsCOMPtr<nsIContent> mContent;
509
};
510
511
NS_IMPL_ISUPPORTS(PluginTimerCallBack, nsITimerCallback, nsINamed)
512
#endif
513
514
already_AddRefed<Accessible>
515
nsAccessibilityService::CreatePluginAccessible(nsPluginFrame* aFrame,
516
                                               nsIContent* aContent,
517
                                               Accessible* aContext)
518
0
{
519
0
  // nsPluginFrame means a plugin, so we need to use the accessibility support
520
0
  // of the plugin.
521
0
  if (aFrame->GetRect().IsEmpty())
522
0
    return nullptr;
523
0
524
0
#if defined(XP_WIN) || defined(MOZ_ACCESSIBILITY_ATK)
525
0
  RefPtr<nsNPAPIPluginInstance> pluginInstance;
526
0
  if (NS_SUCCEEDED(aFrame->GetPluginInstance(getter_AddRefs(pluginInstance))) &&
527
0
      pluginInstance) {
528
#ifdef XP_WIN
529
    if (!sPendingPlugins->Contains(aContent) &&
530
        (Preferences::GetBool("accessibility.delay_plugins") ||
531
         Compatibility::IsJAWS() || Compatibility::IsWE())) {
532
      RefPtr<PluginTimerCallBack> cb = new PluginTimerCallBack(aContent);
533
      nsCOMPtr<nsITimer> timer;
534
      NS_NewTimerWithCallback(getter_AddRefs(timer),
535
                              cb, Preferences::GetUint("accessibility.delay_plugin_time"),
536
                              nsITimer::TYPE_ONE_SHOT);
537
      sPluginTimers->AppendElement(timer);
538
      sPendingPlugins->AppendElement(aContent);
539
      return nullptr;
540
    }
541
542
    // We need to remove aContent from the pending plugins here to avoid
543
    // reentrancy.  When the timer fires it calls
544
    // DocAccessible::ContentInserted() which does the work async.
545
    sPendingPlugins->RemoveElement(aContent);
546
547
    // Note: pluginPort will be null if windowless.
548
    HWND pluginPort = nullptr;
549
    aFrame->GetPluginPort(&pluginPort);
550
551
    RefPtr<Accessible> accessible =
552
      new HTMLWin32ObjectOwnerAccessible(aContent, aContext->Document(),
553
                                         pluginPort);
554
    return accessible.forget();
555
556
#elif MOZ_ACCESSIBILITY_ATK
557
0
    if (!AtkSocketAccessible::gCanEmbed)
558
0
      return nullptr;
559
0
560
0
    // Note this calls into the plugin, so crazy things may happen and aFrame
561
0
    // may go away.
562
0
    nsCString plugId;
563
0
    nsresult rv = pluginInstance->GetValueFromPlugin(
564
0
      NPPVpluginNativeAccessibleAtkPlugId, &plugId);
565
0
    if (NS_SUCCEEDED(rv) && !plugId.IsEmpty()) {
566
0
      RefPtr<AtkSocketAccessible> socketAccessible =
567
0
        new AtkSocketAccessible(aContent, aContext->Document(), plugId);
568
0
569
0
      return socketAccessible.forget();
570
0
    }
571
0
#endif
572
0
  }
573
0
#endif
574
0
575
0
  return nullptr;
576
0
}
577
578
void
579
nsAccessibilityService::DeckPanelSwitched(nsIPresShell* aPresShell,
580
                                          nsIContent* aDeckNode,
581
                                          nsIFrame* aPrevBoxFrame,
582
                                          nsIFrame* aCurrentBoxFrame)
583
0
{
584
0
  // Ignore tabpanels elements (a deck having an accessible) since their
585
0
  // children are accessible not depending on selected tab.
586
0
  DocAccessible* document = GetDocAccessible(aPresShell);
587
0
  if (!document || document->HasAccessible(aDeckNode))
588
0
    return;
589
0
590
0
  if (aPrevBoxFrame) {
591
0
    nsIContent* panelNode = aPrevBoxFrame->GetContent();
592
0
#ifdef A11Y_LOG
593
0
    if (logging::IsEnabled(logging::eTree)) {
594
0
      logging::MsgBegin("TREE", "deck panel unselected");
595
0
      logging::Node("container", panelNode);
596
0
      logging::Node("content", aDeckNode);
597
0
      logging::MsgEnd();
598
0
    }
599
0
#endif
600
0
601
0
    document->ContentRemoved(panelNode);
602
0
  }
603
0
604
0
  if (aCurrentBoxFrame) {
605
0
    nsIContent* panelNode = aCurrentBoxFrame->GetContent();
606
0
#ifdef A11Y_LOG
607
0
    if (logging::IsEnabled(logging::eTree)) {
608
0
      logging::MsgBegin("TREE", "deck panel selected");
609
0
      logging::Node("container", panelNode);
610
0
      logging::Node("content", aDeckNode);
611
0
      logging::MsgEnd();
612
0
    }
613
0
#endif
614
0
615
0
    document->ContentInserted(aDeckNode, panelNode, panelNode->GetNextSibling());
616
0
  }
617
0
}
618
619
void
620
nsAccessibilityService::ContentRangeInserted(nsIPresShell* aPresShell,
621
                                             nsIContent* aStartChild,
622
                                             nsIContent* aEndChild)
623
0
{
624
0
  DocAccessible* document = GetDocAccessible(aPresShell);
625
0
#ifdef A11Y_LOG
626
0
  if (logging::IsEnabled(logging::eTree)) {
627
0
    logging::MsgBegin("TREE", "content inserted; doc: %p", document);
628
0
    logging::Node("container", aStartChild->GetParent());
629
0
    for (nsIContent* child = aStartChild; child != aEndChild;
630
0
         child = child->GetNextSibling()) {
631
0
      logging::Node("content", child);
632
0
    }
633
0
    logging::MsgEnd();
634
0
    logging::Stack();
635
0
  }
636
0
#endif
637
0
638
0
  if (document) {
639
0
    document->ContentInserted(aStartChild->GetParent(), aStartChild, aEndChild);
640
0
  }
641
0
}
642
643
void
644
nsAccessibilityService::ContentRemoved(nsIPresShell* aPresShell,
645
                                       nsIContent* aChildNode)
646
0
{
647
0
  DocAccessible* document = GetDocAccessible(aPresShell);
648
0
#ifdef A11Y_LOG
649
0
  if (logging::IsEnabled(logging::eTree)) {
650
0
    logging::MsgBegin("TREE", "content removed; doc: %p", document);
651
0
    logging::Node("container node", aChildNode->GetFlattenedTreeParent());
652
0
    logging::Node("content node", aChildNode);
653
0
    logging::MsgEnd();
654
0
  }
655
0
#endif
656
0
657
0
  if (document) {
658
0
    document->ContentRemoved(aChildNode);
659
0
  }
660
0
661
0
#ifdef A11Y_LOG
662
0
  if (logging::IsEnabled(logging::eTree)) {
663
0
    logging::MsgEnd();
664
0
    logging::Stack();
665
0
  }
666
0
#endif
667
0
}
668
669
void
670
nsAccessibilityService::UpdateText(nsIPresShell* aPresShell,
671
                                   nsIContent* aContent)
672
0
{
673
0
  DocAccessible* document = GetDocAccessible(aPresShell);
674
0
  if (document)
675
0
    document->UpdateText(aContent);
676
0
}
677
678
void
679
nsAccessibilityService::TreeViewChanged(nsIPresShell* aPresShell,
680
                                        nsIContent* aContent,
681
                                        nsITreeView* aView)
682
0
{
683
0
  DocAccessible* document = GetDocAccessible(aPresShell);
684
0
  if (document) {
685
0
    Accessible* accessible = document->GetAccessible(aContent);
686
0
    if (accessible) {
687
0
      XULTreeAccessible* treeAcc = accessible->AsXULTree();
688
0
      if (treeAcc)
689
0
        treeAcc->TreeViewChanged(aView);
690
0
    }
691
0
  }
692
0
}
693
694
void
695
nsAccessibilityService::RangeValueChanged(nsIPresShell* aPresShell,
696
                                          nsIContent* aContent)
697
0
{
698
0
  DocAccessible* document = GetDocAccessible(aPresShell);
699
0
  if (document) {
700
0
    Accessible* accessible = document->GetAccessible(aContent);
701
0
    if (accessible) {
702
0
      document->FireDelayedEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE,
703
0
                                 accessible);
704
0
    }
705
0
  }
706
0
}
707
708
void
709
nsAccessibilityService::UpdateListBullet(nsIPresShell* aPresShell,
710
                                         nsIContent* aHTMLListItemContent,
711
                                         bool aHasBullet)
712
0
{
713
0
  DocAccessible* document = GetDocAccessible(aPresShell);
714
0
  if (document) {
715
0
    Accessible* accessible = document->GetAccessible(aHTMLListItemContent);
716
0
    if (accessible) {
717
0
      HTMLLIAccessible* listItem = accessible->AsHTMLListItem();
718
0
      if (listItem)
719
0
        listItem->UpdateBullet(aHasBullet);
720
0
    }
721
0
  }
722
0
}
723
724
void
725
nsAccessibilityService::UpdateImageMap(nsImageFrame* aImageFrame)
726
0
{
727
0
  nsIPresShell* presShell = aImageFrame->PresShell();
728
0
  DocAccessible* document = GetDocAccessible(presShell);
729
0
  if (document) {
730
0
    Accessible* accessible =
731
0
      document->GetAccessible(aImageFrame->GetContent());
732
0
    if (accessible) {
733
0
      HTMLImageMapAccessible* imageMap = accessible->AsImageMap();
734
0
      if (imageMap) {
735
0
        imageMap->UpdateChildAreas();
736
0
        return;
737
0
      }
738
0
739
0
      // If image map was initialized after we created an accessible (that'll
740
0
      // be an image accessible) then recreate it.
741
0
      RecreateAccessible(presShell, aImageFrame->GetContent());
742
0
    }
743
0
  }
744
0
}
745
746
void
747
nsAccessibilityService::UpdateLabelValue(nsIPresShell* aPresShell,
748
                                         nsIContent* aLabelElm,
749
                                         const nsString& aNewValue)
750
0
{
751
0
  DocAccessible* document = GetDocAccessible(aPresShell);
752
0
  if (document) {
753
0
    Accessible* accessible = document->GetAccessible(aLabelElm);
754
0
    if (accessible) {
755
0
      XULLabelAccessible* xulLabel = accessible->AsXULLabel();
756
0
      NS_ASSERTION(xulLabel,
757
0
                   "UpdateLabelValue was called for wrong accessible!");
758
0
      if (xulLabel)
759
0
        xulLabel->UpdateLabelValue(aNewValue);
760
0
    }
761
0
  }
762
0
}
763
764
void
765
nsAccessibilityService::PresShellActivated(nsIPresShell* aPresShell)
766
0
{
767
0
  DocAccessible* document = aPresShell->GetDocAccessible();
768
0
  if (document) {
769
0
    RootAccessible* rootDocument = document->RootAccessible();
770
0
    NS_ASSERTION(rootDocument, "Entirely broken tree: no root document!");
771
0
    if (rootDocument)
772
0
      rootDocument->DocumentActivated(document);
773
0
  }
774
0
}
775
776
void
777
nsAccessibilityService::RecreateAccessible(nsIPresShell* aPresShell,
778
                                           nsIContent* aContent)
779
0
{
780
0
  DocAccessible* document = GetDocAccessible(aPresShell);
781
0
  if (document)
782
0
    document->RecreateAccessible(aContent);
783
0
}
784
785
void
786
nsAccessibilityService::GetStringRole(uint32_t aRole, nsAString& aString)
787
0
{
788
0
#define ROLE(geckoRole, stringRole, atkRole, \
789
0
             macRole, msaaRole, ia2Role, androidClass, nameRule) \
790
0
  case roles::geckoRole: \
791
0
    aString.AssignLiteral(stringRole); \
792
0
    return;
793
0
794
0
  switch (aRole) {
795
0
#include "RoleMap.h"
796
0
    default:
797
0
      aString.AssignLiteral("unknown");
798
0
      return;
799
0
  }
800
0
801
0
#undef ROLE
802
0
}
803
804
void
805
nsAccessibilityService::GetStringStates(uint32_t aState, uint32_t aExtraState,
806
                                        nsISupports** aStringStates)
807
0
{
808
0
  RefPtr<DOMStringList> stringStates =
809
0
    GetStringStates(nsAccUtils::To64State(aState, aExtraState));
810
0
811
0
  // unknown state
812
0
  if (!stringStates->Length()) {
813
0
    stringStates->Add(NS_LITERAL_STRING("unknown"));
814
0
  }
815
0
816
0
  stringStates.forget(aStringStates);
817
0
}
818
819
already_AddRefed<DOMStringList>
820
nsAccessibilityService::GetStringStates(uint64_t aStates) const
821
0
{
822
0
  RefPtr<DOMStringList> stringStates = new DOMStringList();
823
0
824
0
  if (aStates & states::UNAVAILABLE) {
825
0
    stringStates->Add(NS_LITERAL_STRING("unavailable"));
826
0
  }
827
0
  if (aStates & states::SELECTED) {
828
0
    stringStates->Add(NS_LITERAL_STRING("selected"));
829
0
  }
830
0
  if (aStates & states::FOCUSED) {
831
0
    stringStates->Add(NS_LITERAL_STRING("focused"));
832
0
  }
833
0
  if (aStates & states::PRESSED) {
834
0
    stringStates->Add(NS_LITERAL_STRING("pressed"));
835
0
  }
836
0
  if (aStates & states::CHECKED) {
837
0
    stringStates->Add(NS_LITERAL_STRING("checked"));
838
0
  }
839
0
  if (aStates & states::MIXED) {
840
0
    stringStates->Add(NS_LITERAL_STRING("mixed"));
841
0
  }
842
0
  if (aStates & states::READONLY) {
843
0
    stringStates->Add(NS_LITERAL_STRING("readonly"));
844
0
  }
845
0
  if (aStates & states::HOTTRACKED) {
846
0
    stringStates->Add(NS_LITERAL_STRING("hottracked"));
847
0
  }
848
0
  if (aStates & states::DEFAULT) {
849
0
    stringStates->Add(NS_LITERAL_STRING("default"));
850
0
  }
851
0
  if (aStates & states::EXPANDED) {
852
0
    stringStates->Add(NS_LITERAL_STRING("expanded"));
853
0
  }
854
0
  if (aStates & states::COLLAPSED) {
855
0
    stringStates->Add(NS_LITERAL_STRING("collapsed"));
856
0
  }
857
0
  if (aStates & states::BUSY) {
858
0
    stringStates->Add(NS_LITERAL_STRING("busy"));
859
0
  }
860
0
  if (aStates & states::FLOATING) {
861
0
    stringStates->Add(NS_LITERAL_STRING("floating"));
862
0
  }
863
0
  if (aStates & states::ANIMATED) {
864
0
    stringStates->Add(NS_LITERAL_STRING("animated"));
865
0
  }
866
0
  if (aStates & states::INVISIBLE) {
867
0
    stringStates->Add(NS_LITERAL_STRING("invisible"));
868
0
  }
869
0
  if (aStates & states::OFFSCREEN) {
870
0
    stringStates->Add(NS_LITERAL_STRING("offscreen"));
871
0
  }
872
0
  if (aStates & states::SIZEABLE) {
873
0
    stringStates->Add(NS_LITERAL_STRING("sizeable"));
874
0
  }
875
0
  if (aStates & states::MOVEABLE) {
876
0
    stringStates->Add(NS_LITERAL_STRING("moveable"));
877
0
  }
878
0
  if (aStates & states::SELFVOICING) {
879
0
    stringStates->Add(NS_LITERAL_STRING("selfvoicing"));
880
0
  }
881
0
  if (aStates & states::FOCUSABLE) {
882
0
    stringStates->Add(NS_LITERAL_STRING("focusable"));
883
0
  }
884
0
  if (aStates & states::SELECTABLE) {
885
0
    stringStates->Add(NS_LITERAL_STRING("selectable"));
886
0
  }
887
0
  if (aStates & states::LINKED) {
888
0
    stringStates->Add(NS_LITERAL_STRING("linked"));
889
0
  }
890
0
  if (aStates & states::TRAVERSED) {
891
0
    stringStates->Add(NS_LITERAL_STRING("traversed"));
892
0
  }
893
0
  if (aStates & states::MULTISELECTABLE) {
894
0
    stringStates->Add(NS_LITERAL_STRING("multiselectable"));
895
0
  }
896
0
  if (aStates & states::EXTSELECTABLE) {
897
0
    stringStates->Add(NS_LITERAL_STRING("extselectable"));
898
0
  }
899
0
  if (aStates & states::PROTECTED) {
900
0
    stringStates->Add(NS_LITERAL_STRING("protected"));
901
0
  }
902
0
  if (aStates & states::HASPOPUP) {
903
0
    stringStates->Add(NS_LITERAL_STRING("haspopup"));
904
0
  }
905
0
  if (aStates & states::REQUIRED) {
906
0
    stringStates->Add(NS_LITERAL_STRING("required"));
907
0
  }
908
0
  if (aStates & states::ALERT) {
909
0
    stringStates->Add(NS_LITERAL_STRING("alert"));
910
0
  }
911
0
  if (aStates & states::INVALID) {
912
0
    stringStates->Add(NS_LITERAL_STRING("invalid"));
913
0
  }
914
0
  if (aStates & states::CHECKABLE) {
915
0
    stringStates->Add(NS_LITERAL_STRING("checkable"));
916
0
  }
917
0
  if (aStates & states::SUPPORTS_AUTOCOMPLETION) {
918
0
    stringStates->Add(NS_LITERAL_STRING("autocompletion"));
919
0
  }
920
0
  if (aStates & states::DEFUNCT) {
921
0
    stringStates->Add(NS_LITERAL_STRING("defunct"));
922
0
  }
923
0
  if (aStates & states::SELECTABLE_TEXT) {
924
0
    stringStates->Add(NS_LITERAL_STRING("selectable text"));
925
0
  }
926
0
  if (aStates & states::EDITABLE) {
927
0
    stringStates->Add(NS_LITERAL_STRING("editable"));
928
0
  }
929
0
  if (aStates & states::ACTIVE) {
930
0
    stringStates->Add(NS_LITERAL_STRING("active"));
931
0
  }
932
0
  if (aStates & states::MODAL) {
933
0
    stringStates->Add(NS_LITERAL_STRING("modal"));
934
0
  }
935
0
  if (aStates & states::MULTI_LINE) {
936
0
    stringStates->Add(NS_LITERAL_STRING("multi line"));
937
0
  }
938
0
  if (aStates & states::HORIZONTAL) {
939
0
    stringStates->Add(NS_LITERAL_STRING("horizontal"));
940
0
  }
941
0
  if (aStates & states::OPAQUE1) {
942
0
    stringStates->Add(NS_LITERAL_STRING("opaque"));
943
0
  }
944
0
  if (aStates & states::SINGLE_LINE) {
945
0
    stringStates->Add(NS_LITERAL_STRING("single line"));
946
0
  }
947
0
  if (aStates & states::TRANSIENT) {
948
0
    stringStates->Add(NS_LITERAL_STRING("transient"));
949
0
  }
950
0
  if (aStates & states::VERTICAL) {
951
0
    stringStates->Add(NS_LITERAL_STRING("vertical"));
952
0
  }
953
0
  if (aStates & states::STALE) {
954
0
    stringStates->Add(NS_LITERAL_STRING("stale"));
955
0
  }
956
0
  if (aStates & states::ENABLED) {
957
0
    stringStates->Add(NS_LITERAL_STRING("enabled"));
958
0
  }
959
0
  if (aStates & states::SENSITIVE) {
960
0
    stringStates->Add(NS_LITERAL_STRING("sensitive"));
961
0
  }
962
0
  if (aStates & states::EXPANDABLE) {
963
0
    stringStates->Add(NS_LITERAL_STRING("expandable"));
964
0
  }
965
0
966
0
  return stringStates.forget();
967
0
}
968
969
void
970
nsAccessibilityService::GetStringEventType(uint32_t aEventType,
971
                                           nsAString& aString)
972
0
{
973
0
  NS_ASSERTION(nsIAccessibleEvent::EVENT_LAST_ENTRY == ArrayLength(kEventTypeNames),
974
0
               "nsIAccessibleEvent constants are out of sync to kEventTypeNames");
975
0
976
0
  if (aEventType >= ArrayLength(kEventTypeNames)) {
977
0
    aString.AssignLiteral("unknown");
978
0
    return;
979
0
  }
980
0
981
0
  aString.AssignASCII(kEventTypeNames[aEventType]);
982
0
}
983
984
void
985
nsAccessibilityService::GetStringEventType(uint32_t aEventType,
986
                                           nsACString& aString)
987
0
{
988
0
  MOZ_ASSERT(nsIAccessibleEvent::EVENT_LAST_ENTRY == ArrayLength(kEventTypeNames),
989
0
             "nsIAccessibleEvent constants are out of sync to kEventTypeNames");
990
0
991
0
  if (aEventType >= ArrayLength(kEventTypeNames)) {
992
0
    aString.AssignLiteral("unknown");
993
0
    return;
994
0
  }
995
0
996
0
  aString = nsDependentCString(kEventTypeNames[aEventType]);
997
0
}
998
999
void
1000
nsAccessibilityService::GetStringRelationType(uint32_t aRelationType,
1001
                                              nsAString& aString)
1002
0
{
1003
0
  NS_ENSURE_TRUE_VOID(aRelationType <= static_cast<uint32_t>(RelationType::LAST));
1004
0
1005
0
#define RELATIONTYPE(geckoType, geckoTypeName, atkType, msaaType, ia2Type) \
1006
0
  case RelationType::geckoType: \
1007
0
    aString.AssignLiteral(geckoTypeName); \
1008
0
    return;
1009
0
1010
0
  RelationType relationType = static_cast<RelationType>(aRelationType);
1011
0
  switch (relationType) {
1012
0
#include "RelationTypeMap.h"
1013
0
    default:
1014
0
      aString.AssignLiteral("unknown");
1015
0
      return;
1016
0
  }
1017
0
1018
0
#undef RELATIONTYPE
1019
0
}
1020
1021
////////////////////////////////////////////////////////////////////////////////
1022
// nsAccessibilityService public
1023
1024
Accessible*
1025
nsAccessibilityService::CreateAccessible(nsINode* aNode,
1026
                                         Accessible* aContext,
1027
                                         bool* aIsSubtreeHidden)
1028
0
{
1029
0
  MOZ_ASSERT(aContext, "No context provided");
1030
0
  MOZ_ASSERT(aNode, "No node to create an accessible for");
1031
0
  MOZ_ASSERT(gConsumers, "No creation after shutdown");
1032
0
1033
0
  if (aIsSubtreeHidden)
1034
0
    *aIsSubtreeHidden = false;
1035
0
1036
0
  DocAccessible* document = aContext->Document();
1037
0
  MOZ_ASSERT(!document->GetAccessible(aNode),
1038
0
             "We already have an accessible for this node.");
1039
0
1040
0
  if (aNode->IsDocument()) {
1041
0
    // If it's document node then ask accessible document loader for
1042
0
    // document accessible, otherwise return null.
1043
0
    return GetDocAccessible(aNode->AsDocument());
1044
0
  }
1045
0
1046
0
  // We have a content node.
1047
0
  if (!aNode->GetComposedDoc()) {
1048
0
    NS_WARNING("Creating accessible for node with no document");
1049
0
    return nullptr;
1050
0
  }
1051
0
1052
0
  if (aNode->OwnerDoc() != document->DocumentNode()) {
1053
0
    NS_ERROR("Creating accessible for wrong document");
1054
0
    return nullptr;
1055
0
  }
1056
0
1057
0
  if (!aNode->IsContent())
1058
0
    return nullptr;
1059
0
1060
0
  nsIContent* content = aNode->AsContent();
1061
0
  if (aria::HasDefinedARIAHidden(content)) {
1062
0
    if (aIsSubtreeHidden) {
1063
0
      *aIsSubtreeHidden = true;
1064
0
    }
1065
0
    return nullptr;
1066
0
  }
1067
0
1068
0
  // Check frame and its visibility. Note, hidden frame allows visible
1069
0
  // elements in subtree.
1070
0
  nsIFrame* frame = content->GetPrimaryFrame();
1071
0
  if (!frame || !frame->StyleVisibility()->IsVisible()) {
1072
0
    // display:contents element doesn't have a frame, but retains the semantics.
1073
0
    // All its children are unaffected.
1074
0
    if (content->IsElement() && content->AsElement()->IsDisplayContents()) {
1075
0
      const HTMLMarkupMapInfo* markupMap =
1076
0
        mHTMLMarkupMap.Get(content->NodeInfo()->NameAtom());
1077
0
      if (markupMap && markupMap->new_func) {
1078
0
        RefPtr<Accessible> newAcc =
1079
0
          markupMap->new_func(content->AsElement(), aContext);
1080
0
        document->BindToDocument(newAcc, aria::GetRoleMap(content->AsElement()));
1081
0
        return newAcc;
1082
0
      }
1083
0
      return nullptr;
1084
0
    }
1085
0
1086
0
    if (aIsSubtreeHidden && !frame)
1087
0
      *aIsSubtreeHidden = true;
1088
0
1089
0
    return nullptr;
1090
0
  }
1091
0
1092
0
  if (frame->GetContent() != content) {
1093
0
    // Not the main content for this frame. This happens because <area>
1094
0
    // elements return the image frame as their primary frame. The main content
1095
0
    // for the image frame is the image content. If the frame is not an image
1096
0
    // frame or the node is not an area element then null is returned.
1097
0
    // This setup will change when bug 135040 is fixed. Make sure we don't
1098
0
    // create area accessible here. Hopefully assertion below will handle that.
1099
0
1100
#ifdef DEBUG
1101
  nsImageFrame* imageFrame = do_QueryFrame(frame);
1102
  NS_ASSERTION(imageFrame && content->IsHTMLElement(nsGkAtoms::area),
1103
               "Unknown case of not main content for the frame!");
1104
#endif
1105
    return nullptr;
1106
0
  }
1107
0
1108
#ifdef DEBUG
1109
  nsImageFrame* imageFrame = do_QueryFrame(frame);
1110
  NS_ASSERTION(!imageFrame || !content->IsHTMLElement(nsGkAtoms::area),
1111
               "Image map manages the area accessible creation!");
1112
#endif
1113
1114
0
  // Attempt to create an accessible based on what we know.
1115
0
  RefPtr<Accessible> newAcc;
1116
0
1117
0
  // Create accessible for visible text frames.
1118
0
  if (content->IsText()) {
1119
0
    nsIFrame::RenderedText text = frame->GetRenderedText(0,
1120
0
        UINT32_MAX, nsIFrame::TextOffsetType::OFFSETS_IN_CONTENT_TEXT,
1121
0
        nsIFrame::TrailingWhitespace::DONT_TRIM_TRAILING_WHITESPACE);
1122
0
    // Ignore not rendered text nodes and whitespace text nodes between table
1123
0
    // cells.
1124
0
    if (text.mString.IsEmpty() ||
1125
0
        (aContext->IsTableRow() && nsCoreUtils::IsWhitespaceString(text.mString))) {
1126
0
      if (aIsSubtreeHidden)
1127
0
        *aIsSubtreeHidden = true;
1128
0
1129
0
      return nullptr;
1130
0
    }
1131
0
1132
0
    newAcc = CreateAccessibleByFrameType(frame, content, aContext);
1133
0
    document->BindToDocument(newAcc, nullptr);
1134
0
    newAcc->AsTextLeaf()->SetText(text.mString);
1135
0
    return newAcc;
1136
0
  }
1137
0
1138
0
  if (content->IsHTMLElement(nsGkAtoms::map)) {
1139
0
    // Create hyper text accessible for HTML map if it is used to group links
1140
0
    // (see http://www.w3.org/TR/WCAG10-HTML-TECHS/#group-bypass). If the HTML
1141
0
    // map rect is empty then it is used for links grouping. Otherwise it should
1142
0
    // be used in conjunction with HTML image element and in this case we don't
1143
0
    // create any accessible for it and don't walk into it. The accessibles for
1144
0
    // HTML area (HTMLAreaAccessible) the map contains are attached as
1145
0
    // children of the appropriate accessible for HTML image
1146
0
    // (ImageAccessible).
1147
0
    if (nsLayoutUtils::GetAllInFlowRectsUnion(frame,
1148
0
                                              frame->GetParent()).IsEmpty()) {
1149
0
      if (aIsSubtreeHidden)
1150
0
        *aIsSubtreeHidden = true;
1151
0
1152
0
      return nullptr;
1153
0
    }
1154
0
1155
0
    newAcc = new HyperTextAccessibleWrap(content, document);
1156
0
    document->BindToDocument(newAcc, aria::GetRoleMap(content->AsElement()));
1157
0
    return newAcc;
1158
0
  }
1159
0
1160
0
  const nsRoleMapEntry* roleMapEntry = aria::GetRoleMap(content->AsElement());
1161
0
1162
0
  // If the element is focusable or global ARIA attribute is applied to it or
1163
0
  // it is referenced by ARIA relationship then treat role="presentation" on
1164
0
  // the element as the role is not there.
1165
0
  if (roleMapEntry &&
1166
0
      (roleMapEntry->Is(nsGkAtoms::presentation) ||
1167
0
       roleMapEntry->Is(nsGkAtoms::none))) {
1168
0
    if (!MustBeAccessible(content, document))
1169
0
      return nullptr;
1170
0
1171
0
    roleMapEntry = nullptr;
1172
0
  }
1173
0
1174
0
  if (!newAcc && content->IsHTMLElement()) {  // HTML accessibles
1175
0
    bool isARIATablePart = roleMapEntry &&
1176
0
      (roleMapEntry->accTypes & (eTableCell | eTableRow | eTable));
1177
0
1178
0
    if (!isARIATablePart ||
1179
0
        frame->AccessibleType() == eHTMLTableCellType ||
1180
0
        frame->AccessibleType() == eHTMLTableRowType ||
1181
0
        frame->AccessibleType() == eHTMLTableType) {
1182
0
      // Prefer to use markup to decide if and what kind of accessible to create,
1183
0
      const HTMLMarkupMapInfo* markupMap =
1184
0
        mHTMLMarkupMap.Get(content->NodeInfo()->NameAtom());
1185
0
      if (markupMap && markupMap->new_func)
1186
0
        newAcc = markupMap->new_func(content->AsElement(), aContext);
1187
0
1188
0
      if (!newAcc) // try by frame accessible type.
1189
0
        newAcc = CreateAccessibleByFrameType(frame, content, aContext);
1190
0
    }
1191
0
1192
0
    // In case of ARIA grid or table use table-specific classes if it's not
1193
0
    // native table based.
1194
0
    if (isARIATablePart && (!newAcc || newAcc->IsGenericHyperText())) {
1195
0
      if ((roleMapEntry->accTypes & eTableCell)) {
1196
0
        if (aContext->IsTableRow())
1197
0
          newAcc = new ARIAGridCellAccessibleWrap(content, document);
1198
0
1199
0
      } else if (roleMapEntry->IsOfType(eTableRow)) {
1200
0
        if (aContext->IsTable())
1201
0
          newAcc = new ARIARowAccessible(content, document);
1202
0
1203
0
      } else if (roleMapEntry->IsOfType(eTable)) {
1204
0
        newAcc = new ARIAGridAccessibleWrap(content, document);
1205
0
      }
1206
0
    }
1207
0
1208
0
    // If table has strong ARIA role then all table descendants shouldn't
1209
0
    // expose their native roles.
1210
0
    if (!roleMapEntry && newAcc && aContext->HasStrongARIARole()) {
1211
0
      if (frame->AccessibleType() == eHTMLTableRowType) {
1212
0
        const nsRoleMapEntry* contextRoleMap = aContext->ARIARoleMap();
1213
0
        if (!contextRoleMap->IsOfType(eTable))
1214
0
          roleMapEntry = &aria::gEmptyRoleMap;
1215
0
1216
0
      } else if (frame->AccessibleType() == eHTMLTableCellType &&
1217
0
                 aContext->ARIARoleMap() == &aria::gEmptyRoleMap) {
1218
0
        roleMapEntry = &aria::gEmptyRoleMap;
1219
0
1220
0
      } else if (content->IsAnyOfHTMLElements(nsGkAtoms::dt,
1221
0
                                              nsGkAtoms::li,
1222
0
                                              nsGkAtoms::dd) ||
1223
0
                 frame->AccessibleType() == eHTMLLiType) {
1224
0
        const nsRoleMapEntry* contextRoleMap = aContext->ARIARoleMap();
1225
0
        if (!contextRoleMap->IsOfType(eList))
1226
0
          roleMapEntry = &aria::gEmptyRoleMap;
1227
0
      }
1228
0
    }
1229
0
  }
1230
0
1231
0
  // Accessible XBL types and deck stuff are used in XUL only currently.
1232
0
  if (!newAcc && content->IsXULElement()) {
1233
0
    // No accessible for not selected deck panel and its children.
1234
0
    if (!aContext->IsXULTabpanels()) {
1235
0
      nsDeckFrame* deckFrame = do_QueryFrame(frame->GetParent());
1236
0
      if (deckFrame && deckFrame->GetSelectedBox() != frame) {
1237
0
        if (aIsSubtreeHidden)
1238
0
          *aIsSubtreeHidden = true;
1239
0
1240
0
        return nullptr;
1241
0
      }
1242
0
    }
1243
0
1244
0
#ifdef MOZ_XUL
1245
0
    // Prefer to use XUL to decide if and what kind of accessible to create.
1246
0
    const XULMarkupMapInfo* xulMap =
1247
0
      mXULMarkupMap.Get(content->NodeInfo()->NameAtom());
1248
0
    if (xulMap && xulMap->new_func) {
1249
0
      newAcc = xulMap->new_func(content->AsElement(), aContext);
1250
0
    }
1251
0
#endif
1252
0
1253
0
    // Any XUL box can be used as tabpanel, make sure we create a proper
1254
0
    // accessible for it.
1255
0
    if (!newAcc && aContext->IsXULTabpanels() &&
1256
0
        content->GetParent() == aContext->GetContent()) {
1257
0
      LayoutFrameType frameType = frame->Type();
1258
0
      if (frameType == LayoutFrameType::Box ||
1259
0
          frameType == LayoutFrameType::Scroll) {
1260
0
        newAcc = new XULTabpanelAccessible(content, document);
1261
0
      }
1262
0
    }
1263
0
  }
1264
0
1265
0
  if (!newAcc) {
1266
0
    if (content->IsSVGElement()) {
1267
0
      SVGGeometryFrame* geometryFrame = do_QueryFrame(frame);
1268
0
      if (geometryFrame) {
1269
0
        // A graphic elements: rect, circle, ellipse, line, path, polygon,
1270
0
        // polyline and image. A 'use' and 'text' graphic elements require
1271
0
        // special support.
1272
0
        newAcc = new EnumRoleAccessible<roles::GRAPHIC>(content, document);
1273
0
      } else if (content->IsSVGElement(nsGkAtoms::svg)) {
1274
0
        newAcc = new EnumRoleAccessible<roles::DIAGRAM>(content, document);
1275
0
      }
1276
0
1277
0
    } else if (content->IsMathMLElement()) {
1278
0
      const HTMLMarkupMapInfo* markupMap =
1279
0
        mHTMLMarkupMap.Get(content->NodeInfo()->NameAtom());
1280
0
      if (markupMap && markupMap->new_func)
1281
0
        newAcc = markupMap->new_func(content->AsElement(), aContext);
1282
0
1283
0
      // Fall back to text when encountering Content MathML.
1284
0
      if (!newAcc && !content->IsAnyOfMathMLElements(nsGkAtoms::annotation_,
1285
0
                                                     nsGkAtoms::annotation_xml_,
1286
0
                                                     nsGkAtoms::mpadded_,
1287
0
                                                     nsGkAtoms::mphantom_,
1288
0
                                                     nsGkAtoms::maligngroup_,
1289
0
                                                     nsGkAtoms::malignmark_,
1290
0
                                                     nsGkAtoms::mspace_,
1291
0
                                                     nsGkAtoms::semantics_)) {
1292
0
       newAcc = new HyperTextAccessible(content, document);
1293
0
      }
1294
0
    }
1295
0
  }
1296
0
1297
0
  // If no accessible, see if we need to create a generic accessible because
1298
0
  // of some property that makes this object interesting
1299
0
  // We don't do this for <body>, <html>, <window>, <dialog> etc. which
1300
0
  // correspond to the doc accessible and will be created in any case
1301
0
  if (!newAcc && !content->IsHTMLElement(nsGkAtoms::body) &&
1302
0
      content->GetParent() &&
1303
0
      (roleMapEntry || MustBeAccessible(content, document) ||
1304
0
       (content->IsHTMLElement() &&
1305
0
        nsCoreUtils::HasClickListener(content)))) {
1306
0
    // This content is focusable or has an interesting dynamic content accessibility property.
1307
0
    // If it's interesting we need it in the accessibility hierarchy so that events or
1308
0
    // other accessibles can point to it, or so that it can hold a state, etc.
1309
0
    if (content->IsHTMLElement()) {
1310
0
      // Interesting HTML container which may have selectable text and/or embedded objects
1311
0
      newAcc = new HyperTextAccessibleWrap(content, document);
1312
0
    } else {  // XUL, SVG, MathML etc.
1313
0
      // Interesting generic non-HTML container
1314
0
      newAcc = new AccessibleWrap(content, document);
1315
0
    }
1316
0
  }
1317
0
1318
0
  if (newAcc) {
1319
0
    document->BindToDocument(newAcc, roleMapEntry);
1320
0
  }
1321
0
  return newAcc;
1322
0
}
1323
1324
////////////////////////////////////////////////////////////////////////////////
1325
// nsAccessibilityService private
1326
1327
bool
1328
nsAccessibilityService::Init()
1329
0
{
1330
0
  // Initialize accessible document manager.
1331
0
  if (!DocManager::Init())
1332
0
    return false;
1333
0
1334
0
  // Add observers.
1335
0
  nsCOMPtr<nsIObserverService> observerService =
1336
0
    mozilla::services::GetObserverService();
1337
0
  if (!observerService)
1338
0
    return false;
1339
0
1340
0
  observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
1341
0
1342
#if defined(XP_WIN)
1343
  // This information needs to be initialized before the observer fires.
1344
  if (XRE_IsParentProcess()) {
1345
    Compatibility::Init();
1346
  }
1347
#endif // defined(XP_WIN)
1348
1349
0
  // Subscribe to EventListenerService.
1350
0
  nsCOMPtr<nsIEventListenerService> eventListenerService =
1351
0
    do_GetService("@mozilla.org/eventlistenerservice;1");
1352
0
  if (!eventListenerService)
1353
0
    return false;
1354
0
1355
0
  eventListenerService->AddListenerChangeListener(this);
1356
0
1357
0
  for (uint32_t i = 0; i < ArrayLength(sHTMLMarkupMapList); i++)
1358
0
    mHTMLMarkupMap.Put(*sHTMLMarkupMapList[i].tag, &sHTMLMarkupMapList[i]);
1359
0
1360
0
#ifdef MOZ_XUL
1361
0
  for (uint32_t i = 0; i < ArrayLength(sXULMarkupMapList); i++)
1362
0
    mXULMarkupMap.Put(*sXULMarkupMapList[i].tag, &sXULMarkupMapList[i]);
1363
0
#endif
1364
0
1365
0
#ifdef A11Y_LOG
1366
0
  logging::CheckEnv();
1367
0
#endif
1368
0
1369
0
  gAccessibilityService = this;
1370
0
  NS_ADDREF(gAccessibilityService); // will release in Shutdown()
1371
0
1372
0
  if (XRE_IsParentProcess()) {
1373
0
    gApplicationAccessible = new ApplicationAccessibleWrap();
1374
0
  } else {
1375
#if defined(XP_WIN)
1376
    dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
1377
    MOZ_ASSERT(contentChild);
1378
    // If we were instantiated by the chrome process, GetMsaaID() will return
1379
    // a non-zero value and we may safely continue with initialization.
1380
    if (!contentChild->GetMsaaID()) {
1381
      // Since we were not instantiated by chrome, we need to synchronously
1382
      // obtain a MSAA content process id.
1383
      contentChild->SendGetA11yContentId();
1384
    }
1385
1386
    gApplicationAccessible = new ApplicationAccessibleWrap();
1387
#else
1388
    gApplicationAccessible = new ApplicationAccessible();
1389
0
#endif // defined(XP_WIN)
1390
0
  }
1391
0
1392
0
  NS_ADDREF(gApplicationAccessible); // will release in Shutdown()
1393
0
  gApplicationAccessible->Init();
1394
0
1395
0
  CrashReporter::
1396
0
    AnnotateCrashReport(CrashReporter::Annotation::Accessibility,
1397
0
                        NS_LITERAL_CSTRING("Active"));
1398
0
1399
#ifdef XP_WIN
1400
  sPendingPlugins = new nsTArray<nsCOMPtr<nsIContent> >;
1401
  sPluginTimers = new nsTArray<nsCOMPtr<nsITimer> >;
1402
#endif
1403
1404
0
  // Now its safe to start platform accessibility.
1405
0
  if (XRE_IsParentProcess())
1406
0
    PlatformInit();
1407
0
1408
0
  statistics::A11yInitialized();
1409
0
1410
0
  static const char16_t kInitIndicator[] = { '1', 0 };
1411
0
  observerService->NotifyObservers(nullptr, "a11y-init-or-shutdown", kInitIndicator);
1412
0
1413
0
  return true;
1414
0
}
1415
1416
void
1417
nsAccessibilityService::Shutdown()
1418
0
{
1419
0
  // Application is going to be closed, shutdown accessibility and mark
1420
0
  // accessibility service as shutdown to prevent calls of its methods.
1421
0
  // Don't null accessibility service static member at this point to be safe
1422
0
  // if someone will try to operate with it.
1423
0
1424
0
  MOZ_ASSERT(gConsumers, "Accessibility was shutdown already");
1425
0
  UnsetConsumers(eXPCOM | eMainProcess | ePlatformAPI);
1426
0
1427
0
  // Remove observers.
1428
0
  nsCOMPtr<nsIObserverService> observerService =
1429
0
      mozilla::services::GetObserverService();
1430
0
  if (observerService) {
1431
0
    observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
1432
0
  }
1433
0
1434
0
  // Stop accessible document loader.
1435
0
  DocManager::Shutdown();
1436
0
1437
0
  SelectionManager::Shutdown();
1438
0
1439
#ifdef XP_WIN
1440
  sPendingPlugins = nullptr;
1441
1442
  uint32_t timerCount = sPluginTimers->Length();
1443
  for (uint32_t i = 0; i < timerCount; i++)
1444
    sPluginTimers->ElementAt(i)->Cancel();
1445
1446
  sPluginTimers = nullptr;
1447
#endif
1448
1449
0
  if (XRE_IsParentProcess())
1450
0
    PlatformShutdown();
1451
0
1452
0
  gApplicationAccessible->Shutdown();
1453
0
  NS_RELEASE(gApplicationAccessible);
1454
0
  gApplicationAccessible = nullptr;
1455
0
1456
0
  NS_IF_RELEASE(gXPCApplicationAccessible);
1457
0
  gXPCApplicationAccessible = nullptr;
1458
0
1459
0
  NS_RELEASE(gAccessibilityService);
1460
0
  gAccessibilityService = nullptr;
1461
0
1462
0
  if (observerService) {
1463
0
    static const char16_t kShutdownIndicator[] = { '0', 0 };
1464
0
    observerService->NotifyObservers(nullptr, "a11y-init-or-shutdown", kShutdownIndicator);
1465
0
  }
1466
0
}
1467
1468
already_AddRefed<Accessible>
1469
nsAccessibilityService::CreateAccessibleByFrameType(nsIFrame* aFrame,
1470
                                                    nsIContent* aContent,
1471
                                                    Accessible* aContext)
1472
0
{
1473
0
  DocAccessible* document = aContext->Document();
1474
0
1475
0
  RefPtr<Accessible> newAcc;
1476
0
  switch (aFrame->AccessibleType()) {
1477
0
    case eNoType:
1478
0
      return nullptr;
1479
0
    case eHTMLBRType:
1480
0
      newAcc = new HTMLBRAccessible(aContent, document);
1481
0
      break;
1482
0
    case eHTMLButtonType:
1483
0
      newAcc = new HTMLButtonAccessible(aContent, document);
1484
0
      break;
1485
0
    case eHTMLCanvasType:
1486
0
      newAcc = new HTMLCanvasAccessible(aContent, document);
1487
0
      break;
1488
0
    case eHTMLCaptionType:
1489
0
      if (aContext->IsTable() &&
1490
0
          aContext->GetContent() == aContent->GetParent()) {
1491
0
        newAcc = new HTMLCaptionAccessible(aContent, document);
1492
0
      }
1493
0
      break;
1494
0
    case eHTMLCheckboxType:
1495
0
      newAcc = new CheckboxAccessible(aContent, document);
1496
0
      break;
1497
0
    case eHTMLComboboxType:
1498
0
      newAcc = new HTMLComboboxAccessible(aContent, document);
1499
0
      break;
1500
0
    case eHTMLFileInputType:
1501
0
      newAcc = new HTMLFileInputAccessible(aContent, document);
1502
0
      break;
1503
0
    case eHTMLGroupboxType:
1504
0
      newAcc = new HTMLGroupboxAccessible(aContent, document);
1505
0
      break;
1506
0
    case eHTMLHRType:
1507
0
      newAcc = new HTMLHRAccessible(aContent, document);
1508
0
      break;
1509
0
    case eHTMLImageMapType:
1510
0
      newAcc = new HTMLImageMapAccessible(aContent, document);
1511
0
      break;
1512
0
    case eHTMLLiType:
1513
0
      if (aContext->IsList() &&
1514
0
          aContext->GetContent() == aContent->GetParent()) {
1515
0
        newAcc = new HTMLLIAccessible(aContent, document);
1516
0
      } else {
1517
0
        // Otherwise create a generic text accessible to avoid text jamming.
1518
0
        newAcc = new HyperTextAccessibleWrap(aContent, document);
1519
0
      }
1520
0
      break;
1521
0
    case eHTMLSelectListType:
1522
0
      newAcc = new HTMLSelectListAccessible(aContent, document);
1523
0
      break;
1524
0
    case eHTMLMediaType:
1525
0
      newAcc = new EnumRoleAccessible<roles::GROUPING>(aContent, document);
1526
0
      break;
1527
0
    case eHTMLRadioButtonType:
1528
0
      newAcc = new HTMLRadioButtonAccessible(aContent, document);
1529
0
      break;
1530
0
    case eHTMLRangeType:
1531
0
      newAcc = new HTMLRangeAccessible(aContent, document);
1532
0
      break;
1533
0
    case eHTMLSpinnerType:
1534
0
      newAcc = new HTMLSpinnerAccessible(aContent, document);
1535
0
      break;
1536
0
    case eHTMLTableType:
1537
0
      if (aContent->IsHTMLElement(nsGkAtoms::table))
1538
0
        newAcc = new HTMLTableAccessibleWrap(aContent, document);
1539
0
      else
1540
0
        newAcc = new HyperTextAccessibleWrap(aContent, document);
1541
0
      break;
1542
0
    case eHTMLTableCellType:
1543
0
      // Accessible HTML table cell should be a child of accessible HTML table
1544
0
      // or its row (CSS HTML tables are polite to the used markup at
1545
0
      // certain degree).
1546
0
      // Otherwise create a generic text accessible to avoid text jamming
1547
0
      // when reading by AT.
1548
0
      if (aContext->IsHTMLTableRow() || aContext->IsHTMLTable())
1549
0
        newAcc = new HTMLTableCellAccessibleWrap(aContent, document);
1550
0
      else
1551
0
        newAcc = new HyperTextAccessibleWrap(aContent, document);
1552
0
      break;
1553
0
1554
0
    case eHTMLTableRowType: {
1555
0
      // Accessible HTML table row may be a child of tbody/tfoot/thead of
1556
0
      // accessible HTML table or a direct child of accessible of HTML table.
1557
0
      Accessible* table = aContext->IsTable() ? aContext : nullptr;
1558
0
      if (!table && aContext->Parent() && aContext->Parent()->IsTable())
1559
0
        table = aContext->Parent();
1560
0
1561
0
      if (table) {
1562
0
        nsIContent* parentContent = aContent->GetParent();
1563
0
        nsIFrame* parentFrame = parentContent->GetPrimaryFrame();
1564
0
        if (!parentFrame->IsTableWrapperFrame()) {
1565
0
          parentContent = parentContent->GetParent();
1566
0
          parentFrame = parentContent->GetPrimaryFrame();
1567
0
        }
1568
0
1569
0
        if (parentFrame->IsTableWrapperFrame() &&
1570
0
            table->GetContent() == parentContent) {
1571
0
          newAcc = new HTMLTableRowAccessible(aContent, document);
1572
0
        }
1573
0
      }
1574
0
      break;
1575
0
    }
1576
0
    case eHTMLTextFieldType:
1577
0
      newAcc = new HTMLTextFieldAccessible(aContent, document);
1578
0
      break;
1579
0
    case eHyperTextType:
1580
0
      if (!aContent->IsAnyOfHTMLElements(nsGkAtoms::dt, nsGkAtoms::dd))
1581
0
        newAcc = new HyperTextAccessibleWrap(aContent, document);
1582
0
      break;
1583
0
1584
0
    case eImageType:
1585
0
      newAcc = new ImageAccessibleWrap(aContent, document);
1586
0
      break;
1587
0
    case eOuterDocType:
1588
0
      newAcc = new OuterDocAccessible(aContent, document);
1589
0
      break;
1590
0
    case ePluginType: {
1591
0
      nsPluginFrame* pluginFrame = do_QueryFrame(aFrame);
1592
0
      newAcc = CreatePluginAccessible(pluginFrame, aContent, aContext);
1593
0
      break;
1594
0
    }
1595
0
    case eTextLeafType:
1596
0
      newAcc = new TextLeafAccessibleWrap(aContent, document);
1597
0
      break;
1598
0
    default:
1599
0
      MOZ_ASSERT(false);
1600
0
      break;
1601
0
  }
1602
0
1603
0
  return newAcc.forget();
1604
0
}
1605
1606
void
1607
nsAccessibilityService::MarkupAttributes(const nsIContent* aContent,
1608
                                         nsIPersistentProperties* aAttributes) const
1609
0
{
1610
0
  const mozilla::a11y::HTMLMarkupMapInfo* markupMap =
1611
0
    mHTMLMarkupMap.Get(aContent->NodeInfo()->NameAtom());
1612
0
  if (!markupMap)
1613
0
    return;
1614
0
1615
0
  for (uint32_t i = 0; i < ArrayLength(markupMap->attrs); i++) {
1616
0
    const MarkupAttrInfo* info = markupMap->attrs + i;
1617
0
    if (!info->name)
1618
0
      break;
1619
0
1620
0
    if (info->DOMAttrName) {
1621
0
      if (info->DOMAttrValue) {
1622
0
        if (aContent->IsElement() &&
1623
0
            aContent->AsElement()->AttrValueIs(kNameSpaceID_None,
1624
0
                                               *info->DOMAttrName,
1625
0
                                               *info->DOMAttrValue,
1626
0
                                               eCaseMatters)) {
1627
0
          nsAccUtils::SetAccAttr(aAttributes, *info->name, *info->DOMAttrValue);
1628
0
        }
1629
0
        continue;
1630
0
      }
1631
0
1632
0
      nsAutoString value;
1633
0
1634
0
      if (aContent->IsElement()) {
1635
0
        aContent->AsElement()->GetAttr(kNameSpaceID_None, *info->DOMAttrName, value);
1636
0
      }
1637
0
1638
0
      if (!value.IsEmpty())
1639
0
        nsAccUtils::SetAccAttr(aAttributes, *info->name, value);
1640
0
1641
0
      continue;
1642
0
    }
1643
0
1644
0
    nsAccUtils::SetAccAttr(aAttributes, *info->name, *info->value);
1645
0
  }
1646
0
}
1647
1648
Accessible*
1649
nsAccessibilityService::AddNativeRootAccessible(void* aAtkAccessible)
1650
0
{
1651
0
#ifdef MOZ_ACCESSIBILITY_ATK
1652
0
  ApplicationAccessible* applicationAcc = ApplicationAcc();
1653
0
  if (!applicationAcc)
1654
0
    return nullptr;
1655
0
1656
0
  GtkWindowAccessible* nativeWnd =
1657
0
    new GtkWindowAccessible(static_cast<AtkObject*>(aAtkAccessible));
1658
0
1659
0
  if (applicationAcc->AppendChild(nativeWnd))
1660
0
    return nativeWnd;
1661
0
#endif
1662
0
1663
0
  return nullptr;
1664
0
}
1665
1666
void
1667
nsAccessibilityService::RemoveNativeRootAccessible(Accessible* aAccessible)
1668
0
{
1669
0
#ifdef MOZ_ACCESSIBILITY_ATK
1670
0
  ApplicationAccessible* applicationAcc = ApplicationAcc();
1671
0
1672
0
  if (applicationAcc)
1673
0
    applicationAcc->RemoveChild(aAccessible);
1674
0
#endif
1675
0
}
1676
1677
bool
1678
nsAccessibilityService::HasAccessible(nsINode* aDOMNode)
1679
0
{
1680
0
  if (!aDOMNode)
1681
0
    return false;
1682
0
1683
0
  DocAccessible* document = GetDocAccessible(aDOMNode->OwnerDoc());
1684
0
  if (!document)
1685
0
    return false;
1686
0
1687
0
  return document->HasAccessible(aDOMNode);
1688
0
}
1689
1690
////////////////////////////////////////////////////////////////////////////////
1691
// nsAccessibilityService private (DON'T put methods here)
1692
1693
void
1694
0
nsAccessibilityService::SetConsumers(uint32_t aConsumers, bool aNotify) {
1695
0
  if (gConsumers & aConsumers) {
1696
0
    return;
1697
0
  }
1698
0
1699
0
  gConsumers |= aConsumers;
1700
0
  if (aNotify) {
1701
0
    NotifyOfConsumersChange();
1702
0
  }
1703
0
}
1704
1705
void
1706
0
nsAccessibilityService::UnsetConsumers(uint32_t aConsumers) {
1707
0
  if (!(gConsumers & aConsumers)) {
1708
0
    return;
1709
0
  }
1710
0
1711
0
  gConsumers &= ~aConsumers;
1712
0
  NotifyOfConsumersChange();
1713
0
}
1714
1715
void
1716
nsAccessibilityService::GetConsumers(nsAString& aString)
1717
0
{
1718
0
  const char16_t* kJSONFmt =
1719
0
    u"{ \"XPCOM\": %s, \"MainProcess\": %s, \"PlatformAPI\": %s }";
1720
0
  nsString json;
1721
0
  nsTextFormatter::ssprintf(json, kJSONFmt,
1722
0
    gConsumers & eXPCOM ? "true" : "false",
1723
0
    gConsumers & eMainProcess ? "true" : "false",
1724
0
    gConsumers & ePlatformAPI ? "true" : "false");
1725
0
  aString.Assign(json);
1726
0
}
1727
1728
void
1729
nsAccessibilityService::NotifyOfConsumersChange()
1730
0
{
1731
0
  nsCOMPtr<nsIObserverService> observerService =
1732
0
    mozilla::services::GetObserverService();
1733
0
1734
0
  if (!observerService) {
1735
0
    return;
1736
0
  }
1737
0
1738
0
  nsAutoString consumers;
1739
0
  GetConsumers(consumers);
1740
0
  observerService->NotifyObservers(
1741
0
    nullptr, "a11y-consumers-changed", consumers.get());
1742
0
}
1743
1744
nsAccessibilityService*
1745
GetOrCreateAccService(uint32_t aNewConsumer)
1746
0
{
1747
0
  // Do not initialize accessibility if it is force disabled.
1748
0
  if (PlatformDisabledState() == ePlatformIsDisabled) {
1749
0
    return nullptr;
1750
0
  }
1751
0
1752
0
  if (!nsAccessibilityService::gAccessibilityService) {
1753
0
    RefPtr<nsAccessibilityService> service = new nsAccessibilityService();
1754
0
    if (!service->Init()) {
1755
0
      service->Shutdown();
1756
0
      return nullptr;
1757
0
    }
1758
0
  }
1759
0
1760
0
  MOZ_ASSERT(nsAccessibilityService::gAccessibilityService,
1761
0
             "Accessible service is not initialized.");
1762
0
  nsAccessibilityService::gAccessibilityService->SetConsumers(aNewConsumer);
1763
0
  return nsAccessibilityService::gAccessibilityService;
1764
0
}
1765
1766
void
1767
MaybeShutdownAccService(uint32_t aFormerConsumer)
1768
0
{
1769
0
  nsAccessibilityService* accService =
1770
0
    nsAccessibilityService::gAccessibilityService;
1771
0
1772
0
  if (!accService || nsAccessibilityService::IsShutdown()) {
1773
0
    return;
1774
0
  }
1775
0
1776
0
  // Still used by XPCOM
1777
0
  if (nsCoreUtils::AccEventObserversExist() ||
1778
0
      xpcAccessibilityService::IsInUse() ||
1779
0
      accService->HasXPCDocuments()) {
1780
0
    // In case the XPCOM flag was unset (possibly because of the shutdown
1781
0
    // timer in the xpcAccessibilityService) ensure it is still present. Note:
1782
0
    // this should be fixed when all the consumer logic is taken out as a
1783
0
    // separate class.
1784
0
    accService->SetConsumers(nsAccessibilityService::eXPCOM, false);
1785
0
1786
0
    if (aFormerConsumer != nsAccessibilityService::eXPCOM) {
1787
0
      // Only unset non-XPCOM consumers.
1788
0
      accService->UnsetConsumers(aFormerConsumer);
1789
0
    }
1790
0
    return;
1791
0
  }
1792
0
1793
0
  if (nsAccessibilityService::gConsumers & ~aFormerConsumer) {
1794
0
    accService->UnsetConsumers(aFormerConsumer);
1795
0
  } else {
1796
0
    accService->Shutdown(); // Will unset all nsAccessibilityService::gConsumers
1797
0
  }
1798
0
}
1799
1800
////////////////////////////////////////////////////////////////////////////////
1801
// Services
1802
////////////////////////////////////////////////////////////////////////////////
1803
1804
namespace mozilla {
1805
namespace a11y {
1806
1807
FocusManager*
1808
FocusMgr()
1809
0
{
1810
0
  return nsAccessibilityService::gAccessibilityService;
1811
0
}
1812
1813
SelectionManager*
1814
SelectionMgr()
1815
0
{
1816
0
  return nsAccessibilityService::gAccessibilityService;
1817
0
}
1818
1819
ApplicationAccessible*
1820
ApplicationAcc()
1821
0
{
1822
0
  return nsAccessibilityService::gApplicationAccessible;
1823
0
}
1824
1825
xpcAccessibleApplication*
1826
XPCApplicationAcc()
1827
0
{
1828
0
  if (!nsAccessibilityService::gXPCApplicationAccessible &&
1829
0
      nsAccessibilityService::gApplicationAccessible) {
1830
0
    nsAccessibilityService::gXPCApplicationAccessible =
1831
0
      new xpcAccessibleApplication(nsAccessibilityService::gApplicationAccessible);
1832
0
    NS_ADDREF(nsAccessibilityService::gXPCApplicationAccessible);
1833
0
  }
1834
0
1835
0
  return nsAccessibilityService::gXPCApplicationAccessible;
1836
0
}
1837
1838
EPlatformDisabledState
1839
PlatformDisabledState()
1840
0
{
1841
0
  static bool platformDisabledStateCached = false;
1842
0
  if (platformDisabledStateCached) {
1843
0
    return static_cast<EPlatformDisabledState>(sPlatformDisabledState);
1844
0
  }
1845
0
1846
0
  platformDisabledStateCached = true;
1847
0
  Preferences::RegisterCallback(PrefChanged, PREF_ACCESSIBILITY_FORCE_DISABLED);
1848
0
  return ReadPlatformDisabledState();
1849
0
}
1850
1851
EPlatformDisabledState
1852
ReadPlatformDisabledState()
1853
0
{
1854
0
  sPlatformDisabledState = Preferences::GetInt(PREF_ACCESSIBILITY_FORCE_DISABLED, 0);
1855
0
  if (sPlatformDisabledState < ePlatformIsForceEnabled) {
1856
0
    sPlatformDisabledState = ePlatformIsForceEnabled;
1857
0
  } else if (sPlatformDisabledState > ePlatformIsDisabled){
1858
0
    sPlatformDisabledState = ePlatformIsDisabled;
1859
0
  }
1860
0
1861
0
  return static_cast<EPlatformDisabledState>(sPlatformDisabledState);
1862
0
}
1863
1864
void
1865
PrefChanged(const char* aPref, void* aClosure)
1866
0
{
1867
0
  if (ReadPlatformDisabledState() == ePlatformIsDisabled) {
1868
0
    // Force shut down accessibility.
1869
0
    nsAccessibilityService* accService = nsAccessibilityService::gAccessibilityService;
1870
0
    if (accService && !nsAccessibilityService::IsShutdown()) {
1871
0
      accService->Shutdown();
1872
0
    }
1873
0
  }
1874
0
}
1875
1876
}
1877
}