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 #ifndef __nsAccessibilityService_h__
7 #define __nsAccessibilityService_h__
8
9 #include "mozilla/a11y/DocManager.h"
10 #include "mozilla/a11y/FocusManager.h"
11 #include "mozilla/a11y/Platform.h"
12 #include "mozilla/a11y/Role.h"
13 #include "mozilla/a11y/SelectionManager.h"
14 #include "mozilla/Preferences.h"
15
16 #include "nsIContent.h"
17 #include "nsIObserver.h"
18 #include "nsIAccessibleEvent.h"
19 #include "nsIEventListenerService.h"
20 #include "nsXULAppAPI.h"
21 #include "xpcAccessibilityService.h"
22
23 class nsImageFrame;
24 class nsIArray;
25 class nsITreeView;
26
27 namespace mozilla {
28
29 class PresShell;
30
31 namespace dom {
32 class DOMStringList;
33 class Element;
34 } // namespace dom
35
36 namespace a11y {
37
38 class AccAttributes;
39 class ApplicationAccessible;
40 class xpcAccessibleApplication;
41
42 /**
43 * Return focus manager.
44 */
45 FocusManager* FocusMgr();
46
47 /**
48 * Return selection manager.
49 */
50 SelectionManager* SelectionMgr();
51
52 /**
53 * Returns the application accessible.
54 */
55 ApplicationAccessible* ApplicationAcc();
56 xpcAccessibleApplication* XPCApplicationAcc();
57
58 typedef LocalAccessible*(New_Accessible)(mozilla::dom::Element* aElement,
59 LocalAccessible* aContext);
60
61 // These fields are not `nsStaticAtom* const` because MSVC doesn't like it.
62 struct MarkupAttrInfo {
63 nsStaticAtom* name;
64 nsStaticAtom* value;
65
66 nsStaticAtom* DOMAttrName;
67 nsStaticAtom* DOMAttrValue;
68 };
69
70 struct MarkupMapInfo {
71 const nsStaticAtom* const tag;
72 New_Accessible* new_func;
73 a11y::role role;
74 MarkupAttrInfo attrs[4];
75 };
76
77 struct XULMarkupMapInfo {
78 const nsStaticAtom* const tag;
79 New_Accessible* new_func;
80 };
81
82 /**
83 * PREF_ACCESSIBILITY_FORCE_DISABLED preference change callback.
84 */
85 void PrefChanged(const char* aPref, void* aClosure);
86
87 /**
88 * Read and normalize PREF_ACCESSIBILITY_FORCE_DISABLED preference.
89 */
90 EPlatformDisabledState ReadPlatformDisabledState();
91
92 } // namespace a11y
93 } // namespace mozilla
94
95 class nsAccessibilityService final : public mozilla::a11y::DocManager,
96 public mozilla::a11y::FocusManager,
97 public mozilla::a11y::SelectionManager,
98 public nsIListenerChangeListener,
99 public nsIObserver {
100 public:
101 typedef mozilla::a11y::LocalAccessible LocalAccessible;
102 typedef mozilla::a11y::DocAccessible DocAccessible;
103
104 // nsIListenerChangeListener
105 NS_IMETHOD ListenersChanged(nsIArray* aEventChanges) override;
106
107 protected:
108 ~nsAccessibilityService();
109
110 public:
111 NS_DECL_ISUPPORTS_INHERITED
112 NS_DECL_NSIOBSERVER
113
114 LocalAccessible* GetRootDocumentAccessible(mozilla::PresShell* aPresShell,
115 bool aCanCreate);
116
117 /**
118 * Adds/remove ATK root accessible for gtk+ native window to/from children
119 * of the application accessible.
120 */
121 LocalAccessible* AddNativeRootAccessible(void* aAtkAccessible);
122 void RemoveNativeRootAccessible(LocalAccessible* aRootAccessible);
123
124 bool HasAccessible(nsINode* aDOMNode);
125
126 /**
127 * Get a string equivalent for an accessible role value.
128 */
129 void GetStringRole(uint32_t aRole, nsAString& aString);
130
131 /**
132 * Get a string equivalent for an accessible state/extra state.
133 */
134 already_AddRefed<mozilla::dom::DOMStringList> GetStringStates(
135 uint64_t aStates) const;
136 void GetStringStates(uint32_t aState, uint32_t aExtraState,
137 nsISupports** aStringStates);
138
139 /**
140 * Get a string equivalent for an accessible event value.
141 */
142 void GetStringEventType(uint32_t aEventType, nsAString& aString);
143
144 /**
145 * Get a string equivalent for an accessible event value.
146 */
147 void GetStringEventType(uint32_t aEventType, nsACString& aString);
148
149 /**
150 * Get a string equivalent for an accessible relation type.
151 */
152 void GetStringRelationType(uint32_t aRelationType, nsAString& aString);
153
154 // nsAccesibilityService
155 /**
156 * Notification used to update the accessible tree when deck panel is
157 * switched.
158 */
159 void DeckPanelSwitched(mozilla::PresShell* aPresShell, nsIContent* aDeckNode,
160 nsIFrame* aPrevBoxFrame, nsIFrame* aCurrentBoxFrame);
161
162 /**
163 * Notification used to update the accessible tree when new content is
164 * inserted.
165 */
166 void ContentRangeInserted(mozilla::PresShell* aPresShell,
167 nsIContent* aStartChild, nsIContent* aEndChild);
168
169 /**
170 * Triggers a re-evaluation of the a11y tree of aContent after the next
171 * refresh. This is important because whether we create accessibles may
172 * depend on the frame tree / style.
173 */
174 void ScheduleAccessibilitySubtreeUpdate(mozilla::PresShell* aPresShell,
175 nsIContent* aStartChild);
176
177 /**
178 * Notification used to update the accessible tree when content is removed.
179 */
180 void ContentRemoved(mozilla::PresShell* aPresShell, nsIContent* aChild);
181
182 /**
183 * Notification used to invalidate the isLayoutTable cache.
184 */
185 void TableLayoutGuessMaybeChanged(mozilla::PresShell* aPresShell,
186 nsIContent* aContent);
187
188 /**
189 * Notifies when a combobox <option> text or label changes.
190 */
191 void ComboboxOptionMaybeChanged(mozilla::PresShell*,
192 nsIContent* aMutatingNode);
193
194 void UpdateText(mozilla::PresShell* aPresShell, nsIContent* aContent);
195
196 /**
197 * Update XUL:tree accessible tree when treeview is changed.
198 */
199 void TreeViewChanged(mozilla::PresShell* aPresShell, nsIContent* aContent,
200 nsITreeView* aView);
201
202 /**
203 * Notify of input@type="element" value change.
204 */
205 void RangeValueChanged(mozilla::PresShell* aPresShell, nsIContent* aContent);
206
207 /**
208 * Update the image map.
209 */
210 void UpdateImageMap(nsImageFrame* aImageFrame);
211
212 /**
213 * Update the label accessible tree when rendered @value is changed.
214 */
215 void UpdateLabelValue(mozilla::PresShell* aPresShell, nsIContent* aLabelElm,
216 const nsString& aNewValue);
217
218 /**
219 * Notify accessibility that anchor jump has been accomplished to the given
220 * target. Used by layout.
221 */
222 void NotifyOfAnchorJumpTo(nsIContent* aTarget);
223
224 /**
225 * Notify that presshell is activated.
226 */
227 void PresShellActivated(mozilla::PresShell* aPresShell);
228
229 /**
230 * Recreate an accessible for the given content node in the presshell.
231 */
232 void RecreateAccessible(mozilla::PresShell* aPresShell, nsIContent* aContent);
233
234 void FireAccessibleEvent(uint32_t aEvent, LocalAccessible* aTarget);
235
236 void NotifyOfPossibleBoundsChange(mozilla::PresShell* aPresShell,
237 nsIContent* aContent);
238
239 void NotifyOfComputedStyleChange(mozilla::PresShell* aPresShell,
240 nsIContent* aContent);
241
242 void NotifyOfResolutionChange(mozilla::PresShell* aPresShell,
243 float aResolution);
244
245 // nsAccessibiltiyService
246
247 /**
248 * Return true if accessibility service has been shutdown.
249 */
IsShutdown()250 static bool IsShutdown() { return gConsumers == 0; };
251
252 /**
253 * Creates an accessible for the given DOM node.
254 *
255 * @param aNode [in] the given node
256 * @param aContext [in] context the accessible is created in
257 * @param aIsSubtreeHidden [out, optional] indicates whether the node's
258 * frame and its subtree is hidden
259 */
260 LocalAccessible* CreateAccessible(nsINode* aNode, LocalAccessible* aContext,
261 bool* aIsSubtreeHidden = nullptr);
262
MarkupRole(const nsIContent * aContent)263 mozilla::a11y::role MarkupRole(const nsIContent* aContent) const {
264 const mozilla::a11y::MarkupMapInfo* markupMap =
265 GetMarkupMapInfoForNode(aContent);
266 return markupMap ? markupMap->role : mozilla::a11y::roles::NOTHING;
267 }
268
269 /**
270 * Return the associated value for a given attribute if
271 * it appears in the MarkupMap. Otherwise, it returns null.
272 */
MarkupAttribute(const nsIContent * aContent,nsStaticAtom * aAtom)273 nsStaticAtom* MarkupAttribute(const nsIContent* aContent,
274 nsStaticAtom* aAtom) const {
275 const mozilla::a11y::MarkupMapInfo* markupMap =
276 GetMarkupMapInfoForNode(aContent);
277 if (markupMap) {
278 for (size_t i = 0; i < mozilla::ArrayLength(markupMap->attrs); i++) {
279 const mozilla::a11y::MarkupAttrInfo* info = markupMap->attrs + i;
280 if (info->name == aAtom) {
281 return info->value;
282 }
283 }
284 }
285 return nullptr;
286 }
287
288 /**
289 * Set the object attribute defined by markup for the given element.
290 */
291 void MarkupAttributes(const nsIContent* aContent,
292 mozilla::a11y::AccAttributes* aAttributes) const;
293
294 /**
295 * A list of possible accessibility service consumers. Accessibility service
296 * can only be shut down when there are no remaining consumers.
297 *
298 * eXPCOM - accessibility service is used by XPCOM.
299 *
300 * eMainProcess - accessibility service was started by main process in the
301 * content process.
302 *
303 * ePlatformAPI - accessibility service is used by the platform api in the
304 * main process.
305 */
306 enum ServiceConsumer {
307 eXPCOM = 1 << 0,
308 eMainProcess = 1 << 1,
309 ePlatformAPI = 1 << 2,
310 };
311
312 private:
313 // nsAccessibilityService creation is controlled by friend
314 // GetOrCreateAccService, keep constructors private.
315 nsAccessibilityService();
316 nsAccessibilityService(const nsAccessibilityService&);
317 nsAccessibilityService& operator=(const nsAccessibilityService&);
318
319 private:
320 /**
321 * Initialize accessibility service.
322 */
323 bool Init();
324
325 /**
326 * Shutdowns accessibility service.
327 */
328 void Shutdown();
329
330 /**
331 * Create an accessible whose type depends on the given frame.
332 */
333 already_AddRefed<LocalAccessible> CreateAccessibleByFrameType(
334 nsIFrame* aFrame, nsIContent* aContent, LocalAccessible* aContext);
335
336 /**
337 * Notify observers about change of the accessibility service's consumers.
338 */
339 void NotifyOfConsumersChange();
340
341 /**
342 * Get a JSON string representing the accessibility service consumers.
343 */
344 void GetConsumers(nsAString& aString);
345
346 /**
347 * Set accessibility service consumers.
348 */
349 void SetConsumers(uint32_t aConsumers, bool aNotify = true);
350
351 /**
352 * Unset accessibility service consumers.
353 */
354 void UnsetConsumers(uint32_t aConsumers);
355
356 /**
357 * Reference for accessibility service instance.
358 */
359 static nsAccessibilityService* gAccessibilityService;
360
361 /**
362 * Reference for application accessible instance.
363 */
364 static mozilla::a11y::ApplicationAccessible* gApplicationAccessible;
365 static mozilla::a11y::xpcAccessibleApplication* gXPCApplicationAccessible;
366
367 /**
368 * Contains a set of accessibility service consumers.
369 */
370 static uint32_t gConsumers;
371
372 using MarkupMap = nsTHashMap<nsPtrHashKey<const nsAtom>,
373 const mozilla::a11y::MarkupMapInfo*>;
374 MarkupMap mHTMLMarkupMap;
375 MarkupMap mMathMLMarkupMap;
376
GetMarkupMapInfoForNode(const nsIContent * aContent)377 const mozilla::a11y::MarkupMapInfo* GetMarkupMapInfoForNode(
378 const nsIContent* aContent) const {
379 if (aContent->IsHTMLElement()) {
380 return mHTMLMarkupMap.Get(aContent->NodeInfo()->NameAtom());
381 }
382 if (aContent->IsMathMLElement()) {
383 return mMathMLMarkupMap.Get(aContent->NodeInfo()->NameAtom());
384 }
385 // This function can be called by MarkupAttribute, etc. which might in turn
386 // be called on a XUL, SVG, etc. element. For example, this can happen
387 // with nsAccUtils::SetLiveContainerAttributes.
388 return nullptr;
389 }
390
391 nsTHashMap<nsPtrHashKey<const nsAtom>, const mozilla::a11y::XULMarkupMapInfo*>
392 mXULMarkupMap;
393
394 friend nsAccessibilityService* GetAccService();
395 friend nsAccessibilityService* GetOrCreateAccService(uint32_t);
396 friend void MaybeShutdownAccService(uint32_t);
397 friend void mozilla::a11y::PrefChanged(const char*, void*);
398 friend mozilla::a11y::FocusManager* mozilla::a11y::FocusMgr();
399 friend mozilla::a11y::SelectionManager* mozilla::a11y::SelectionMgr();
400 friend mozilla::a11y::ApplicationAccessible* mozilla::a11y::ApplicationAcc();
401 friend mozilla::a11y::xpcAccessibleApplication*
402 mozilla::a11y::XPCApplicationAcc();
403 friend class xpcAccessibilityService;
404 };
405
406 /**
407 * Return the accessibility service instance. (Handy global function)
408 */
GetAccService()409 inline nsAccessibilityService* GetAccService() {
410 return nsAccessibilityService::gAccessibilityService;
411 }
412
413 /**
414 * Return accessibility service instance; creating one if necessary.
415 */
416 nsAccessibilityService* GetOrCreateAccService(
417 uint32_t aNewConsumer = nsAccessibilityService::ePlatformAPI);
418
419 /**
420 * Shutdown accessibility service if needed.
421 */
422 void MaybeShutdownAccService(uint32_t aFormerConsumer);
423
424 /**
425 * Return true if we're in a content process and not B2G.
426 */
IPCAccessibilityActive()427 inline bool IPCAccessibilityActive() { return XRE_IsContentProcess(); }
428
429 /**
430 * Map nsIAccessibleEvents constants to strings. Used by
431 * nsAccessibilityService::GetStringEventType() method.
432 */
433 static const char kEventTypeNames[][40] = {
434 "unknown", //
435 "show", // EVENT_SHOW
436 "hide", // EVENT_HIDE
437 "reorder", // EVENT_REORDER
438 "active decendent change", // EVENT_ACTIVE_DECENDENT_CHANGED
439 "focus", // EVENT_FOCUS
440 "state change", // EVENT_STATE_CHANGE
441 "location change", // EVENT_LOCATION_CHANGE
442 "name changed", // EVENT_NAME_CHANGE
443 "description change", // EVENT_DESCRIPTION_CHANGE
444 "value change", // EVENT_VALUE_CHANGE
445 "help change", // EVENT_HELP_CHANGE
446 "default action change", // EVENT_DEFACTION_CHANGE
447 "action change", // EVENT_ACTION_CHANGE
448 "accelerator change", // EVENT_ACCELERATOR_CHANGE
449 "selection", // EVENT_SELECTION
450 "selection add", // EVENT_SELECTION_ADD
451 "selection remove", // EVENT_SELECTION_REMOVE
452 "selection within", // EVENT_SELECTION_WITHIN
453 "alert", // EVENT_ALERT
454 "foreground", // EVENT_FOREGROUND
455 "menu start", // EVENT_MENU_START
456 "menu end", // EVENT_MENU_END
457 "menupopup start", // EVENT_MENUPOPUP_START
458 "menupopup end", // EVENT_MENUPOPUP_END
459 "capture start", // EVENT_CAPTURE_START
460 "capture end", // EVENT_CAPTURE_END
461 "movesize start", // EVENT_MOVESIZE_START
462 "movesize end", // EVENT_MOVESIZE_END
463 "contexthelp start", // EVENT_CONTEXTHELP_START
464 "contexthelp end", // EVENT_CONTEXTHELP_END
465 "dragdrop start", // EVENT_DRAGDROP_START
466 "dragdrop end", // EVENT_DRAGDROP_END
467 "dialog start", // EVENT_DIALOG_START
468 "dialog end", // EVENT_DIALOG_END
469 "scrolling start", // EVENT_SCROLLING_START
470 "scrolling end", // EVENT_SCROLLING_END
471 "minimize start", // EVENT_MINIMIZE_START
472 "minimize end", // EVENT_MINIMIZE_END
473 "document load complete", // EVENT_DOCUMENT_LOAD_COMPLETE
474 "document reload", // EVENT_DOCUMENT_RELOAD
475 "document load stopped", // EVENT_DOCUMENT_LOAD_STOPPED
476 "document attributes changed", // EVENT_DOCUMENT_ATTRIBUTES_CHANGED
477 "document content changed", // EVENT_DOCUMENT_CONTENT_CHANGED
478 "property changed", // EVENT_PROPERTY_CHANGED
479 "page changed", // EVENT_PAGE_CHANGED
480 "text attribute changed", // EVENT_TEXT_ATTRIBUTE_CHANGED
481 "text caret moved", // EVENT_TEXT_CARET_MOVED
482 "text changed", // EVENT_TEXT_CHANGED
483 "text inserted", // EVENT_TEXT_INSERTED
484 "text removed", // EVENT_TEXT_REMOVED
485 "text updated", // EVENT_TEXT_UPDATED
486 "text selection changed", // EVENT_TEXT_SELECTION_CHANGED
487 "visible data changed", // EVENT_VISIBLE_DATA_CHANGED
488 "text column changed", // EVENT_TEXT_COLUMN_CHANGED
489 "section changed", // EVENT_SECTION_CHANGED
490 "table caption changed", // EVENT_TABLE_CAPTION_CHANGED
491 "table model changed", // EVENT_TABLE_MODEL_CHANGED
492 "table summary changed", // EVENT_TABLE_SUMMARY_CHANGED
493 "table row description changed", // EVENT_TABLE_ROW_DESCRIPTION_CHANGED
494 "table row header changed", // EVENT_TABLE_ROW_HEADER_CHANGED
495 "table row insert", // EVENT_TABLE_ROW_INSERT
496 "table row delete", // EVENT_TABLE_ROW_DELETE
497 "table row reorder", // EVENT_TABLE_ROW_REORDER
498 "table column description changed", // EVENT_TABLE_COLUMN_DESCRIPTION_CHANGED
499 "table column header changed", // EVENT_TABLE_COLUMN_HEADER_CHANGED
500 "table column insert", // EVENT_TABLE_COLUMN_INSERT
501 "table column delete", // EVENT_TABLE_COLUMN_DELETE
502 "table column reorder", // EVENT_TABLE_COLUMN_REORDER
503 "window activate", // EVENT_WINDOW_ACTIVATE
504 "window create", // EVENT_WINDOW_CREATE
505 "window deactivate", // EVENT_WINDOW_DEACTIVATE
506 "window destroy", // EVENT_WINDOW_DESTROY
507 "window maximize", // EVENT_WINDOW_MAXIMIZE
508 "window minimize", // EVENT_WINDOW_MINIMIZE
509 "window resize", // EVENT_WINDOW_RESIZE
510 "window restore", // EVENT_WINDOW_RESTORE
511 "hyperlink end index changed", // EVENT_HYPERLINK_END_INDEX_CHANGED
512 "hyperlink number of anchors changed", // EVENT_HYPERLINK_NUMBER_OF_ANCHORS_CHANGED
513 "hyperlink selected link changed", // EVENT_HYPERLINK_SELECTED_LINK_CHANGED
514 "hypertext link activated", // EVENT_HYPERTEXT_LINK_ACTIVATED
515 "hypertext link selected", // EVENT_HYPERTEXT_LINK_SELECTED
516 "hyperlink start index changed", // EVENT_HYPERLINK_START_INDEX_CHANGED
517 "hypertext changed", // EVENT_HYPERTEXT_CHANGED
518 "hypertext links count changed", // EVENT_HYPERTEXT_NLINKS_CHANGED
519 "object attribute changed", // EVENT_OBJECT_ATTRIBUTE_CHANGED
520 "virtual cursor changed", // EVENT_VIRTUALCURSOR_CHANGED
521 "text value change", // EVENT_TEXT_VALUE_CHANGE
522 "scrolling", // EVENT_SCROLLING
523 "announcement", // EVENT_ANNOUNCEMENT
524 "live region added", // EVENT_LIVE_REGION_ADDED
525 "live region removed", // EVENT_LIVE_REGION_REMOVED
526 "table styling changed", // EVENT_TABLE_STYLING_CHANGED
527 "inner reorder", // EVENT_INNER_REORDER
528 };
529
530 #endif
531