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