/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 | | } |