no bug - Correct some typos in the comments. a=typo-fix
[gecko.git] / accessible / mac / RotorRules.mm
blob07f8479161ff0a31158798bccfa0dc13d2a59bcd
1 /* clang-format off */
2 /* -*- Mode: Objective-C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
3 /* clang-format on */
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #import "RotorRules.h"
10 #include "nsCocoaUtils.h"
11 #include "DocAccessibleParent.h"
12 #include "nsIAccessiblePivot.h"
13 #include "nsAccUtils.h"
15 #include "nsAccessibilityService.h"
17 using namespace mozilla;
18 using namespace mozilla::a11y;
20 // Generic Rotor Rule
22 RotorRule::RotorRule(Accessible* aDirectDescendantsFrom,
23                      const nsString& aSearchText)
24     : mDirectDescendantsFrom(aDirectDescendantsFrom),
25       mSearchText(aSearchText) {}
27 RotorRule::RotorRule(const nsString& aSearchText)
28     : mDirectDescendantsFrom(nullptr), mSearchText(aSearchText) {}
30 uint16_t RotorRule::Match(Accessible* aAcc) {
31   uint16_t result = nsIAccessibleTraversalRule::FILTER_IGNORE;
33   if (nsAccUtils::MustPrune(aAcc)) {
34     result |= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
35   }
37   if (mDirectDescendantsFrom && (aAcc != mDirectDescendantsFrom)) {
38     result |= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
39   }
41   if ([GetNativeFromGeckoAccessible(aAcc) isAccessibilityElement]) {
42     result |= nsIAccessibleTraversalRule::FILTER_MATCH;
43   }
45   if ((result & nsIAccessibleTraversalRule::FILTER_MATCH) &&
46       !mSearchText.IsEmpty()) {
47     // If we have a non-empty search text, there are some roles
48     // we can safely ignore.
49     switch (aAcc->Role()) {
50       case roles::LANDMARK:
51       case roles::COMBOBOX:
52       case roles::LISTITEM:
53       case roles::COMBOBOX_LIST:
54       case roles::MENUBAR:
55       case roles::MENUPOPUP:
56       case roles::DOCUMENT:
57       case roles::APPLICATION:
58         // XXX: These roles either have AXTitle/AXDescription overridden as
59         // empty, or should never be returned in search text results. This
60         // should be better mapped somewhere.
61         result &= ~nsIAccessibleTraversalRule::FILTER_MATCH;
62         break;
63       default:
64         nsAutoString name;
65         aAcc->Name(name);
66         if (!CaseInsensitiveFindInReadable(mSearchText, name)) {
67           result &= ~nsIAccessibleTraversalRule::FILTER_MATCH;
68         }
69         break;
70     }
71   }
73   return result;
76 // Rotor Role Rule
78 RotorRoleRule::RotorRoleRule(role aRole, Accessible* aDirectDescendantsFrom,
79                              const nsString& aSearchText)
80     : RotorRule(aDirectDescendantsFrom, aSearchText), mRole(aRole){};
82 RotorRoleRule::RotorRoleRule(role aRole, const nsString& aSearchText)
83     : RotorRule(aSearchText), mRole(aRole){};
85 uint16_t RotorRoleRule::Match(Accessible* aAcc) {
86   uint16_t result = RotorRule::Match(aAcc);
88   // if a match was found in the base-class's Match function,
89   // it is valid to consider that match again here. if it is
90   // not of the desired role, we flip the match bit to "unmatch"
91   // otherwise, the match persists.
92   if ((result & nsIAccessibleTraversalRule::FILTER_MATCH) &&
93       aAcc->Role() != mRole) {
94     result &= ~nsIAccessibleTraversalRule::FILTER_MATCH;
95   }
97   return result;
100 // Rotor Mac Role Rule
102 RotorMacRoleRule::RotorMacRoleRule(NSString* aMacRole,
103                                    Accessible* aDirectDescendantsFrom,
104                                    const nsString& aSearchText)
105     : RotorRule(aDirectDescendantsFrom, aSearchText), mMacRole(aMacRole) {
106   [mMacRole retain];
109 RotorMacRoleRule::RotorMacRoleRule(NSString* aMacRole,
110                                    const nsString& aSearchText)
111     : RotorRule(aSearchText), mMacRole(aMacRole) {
112   [mMacRole retain];
115 RotorMacRoleRule::~RotorMacRoleRule() { [mMacRole release]; }
117 uint16_t RotorMacRoleRule::Match(Accessible* aAcc) {
118   uint16_t result = RotorRule::Match(aAcc);
120   // if a match was found in the base-class's Match function,
121   // it is valid to consider that match again here. if it is
122   // not of the desired role, we flip the match bit to "unmatch"
123   // otherwise, the match persists.
124   if ((result & nsIAccessibleTraversalRule::FILTER_MATCH)) {
125     mozAccessible* nativeMatch = GetNativeFromGeckoAccessible(aAcc);
126     if (![[nativeMatch moxRole] isEqualToString:mMacRole]) {
127       result &= ~nsIAccessibleTraversalRule::FILTER_MATCH;
128     }
129   }
131   return result;
134 // Rotor Control Rule
136 RotorControlRule::RotorControlRule(Accessible* aDirectDescendantsFrom,
137                                    const nsString& aSearchText)
138     : RotorRule(aDirectDescendantsFrom, aSearchText){};
140 RotorControlRule::RotorControlRule(const nsString& aSearchText)
141     : RotorRule(aSearchText){};
143 uint16_t RotorControlRule::Match(Accessible* aAcc) {
144   uint16_t result = RotorRule::Match(aAcc);
146   // if a match was found in the base-class's Match function,
147   // it is valid to consider that match again here. if it is
148   // not of the desired role, we flip the match bit to "unmatch"
149   // otherwise, the match persists.
150   if ((result & nsIAccessibleTraversalRule::FILTER_MATCH)) {
151     switch (aAcc->Role()) {
152       case roles::PUSHBUTTON:
153       case roles::SPINBUTTON:
154       case roles::DETAILS:
155       case roles::CHECKBUTTON:
156       case roles::LISTBOX:
157       case roles::COMBOBOX:
158       case roles::EDITCOMBOBOX:
159       case roles::RADIOBUTTON:
160       case roles::RADIO_GROUP:
161       case roles::PAGETAB:
162       case roles::SLIDER:
163       case roles::SWITCH:
164       case roles::ENTRY:
165       case roles::OUTLINE:
166       case roles::PASSWORD_TEXT:
167       case roles::BUTTONMENU:
168         return result;
170       case roles::DATE_EDITOR:
171       case roles::TIME_EDITOR:
172         result |= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
173         return result;
175       case roles::GROUPING: {
176         // Groupings are sometimes used (like radio groups) to denote
177         // sets of controls. If that's the case, we want to surface
178         // them. We also want to surface grouped time and date controls.
179         for (unsigned int i = 0; i < aAcc->ChildCount(); i++) {
180           Accessible* currChild = aAcc->ChildAt(i);
181           if (currChild->Role() == roles::CHECKBUTTON ||
182               currChild->Role() == roles::SWITCH ||
183               currChild->Role() == roles::SPINBUTTON ||
184               currChild->Role() == roles::RADIOBUTTON) {
185             return result;
186           }
187         }
189         // if we iterated through the groups children and didn't
190         // find a control with one of the roles above, we should
191         // ignore this grouping
192         result &= ~nsIAccessibleTraversalRule::FILTER_MATCH;
193         return result;
194       }
196       default:
197         // if we did not match on any above role, we should
198         // ignore this accessible.
199         result &= ~nsIAccessibleTraversalRule::FILTER_MATCH;
200     }
201   }
203   return result;
206 // Rotor TextEntry Rule
208 RotorTextEntryRule::RotorTextEntryRule(Accessible* aDirectDescendantsFrom,
209                                        const nsString& aSearchText)
210     : RotorRule(aDirectDescendantsFrom, aSearchText){};
212 RotorTextEntryRule::RotorTextEntryRule(const nsString& aSearchText)
213     : RotorRule(aSearchText){};
215 uint16_t RotorTextEntryRule::Match(Accessible* aAcc) {
216   uint16_t result = RotorRule::Match(aAcc);
218   // if a match was found in the base-class's Match function,
219   // it is valid to consider that match again here. if it is
220   // not of the desired role, we flip the match bit to "unmatch"
221   // otherwise, the match persists.
222   if ((result & nsIAccessibleTraversalRule::FILTER_MATCH)) {
223     if (aAcc->Role() != roles::PASSWORD_TEXT && aAcc->Role() != roles::ENTRY) {
224       result &= ~nsIAccessibleTraversalRule::FILTER_MATCH;
225     }
226   }
228   return result;
231 // Rotor Link Rule
233 RotorLinkRule::RotorLinkRule(Accessible* aDirectDescendantsFrom,
234                              const nsString& aSearchText)
235     : RotorRule(aDirectDescendantsFrom, aSearchText){};
237 RotorLinkRule::RotorLinkRule(const nsString& aSearchText)
238     : RotorRule(aSearchText){};
240 uint16_t RotorLinkRule::Match(Accessible* aAcc) {
241   uint16_t result = RotorRule::Match(aAcc);
243   // if a match was found in the base-class's Match function,
244   // it is valid to consider that match again here. if it is
245   // not of the desired role, we flip the match bit to "unmatch"
246   // otherwise, the match persists.
247   if ((result & nsIAccessibleTraversalRule::FILTER_MATCH)) {
248     mozAccessible* nativeMatch = GetNativeFromGeckoAccessible(aAcc);
249     if (![[nativeMatch moxRole] isEqualToString:@"AXLink"]) {
250       result &= ~nsIAccessibleTraversalRule::FILTER_MATCH;
251     }
252   }
254   return result;
257 RotorVisitedLinkRule::RotorVisitedLinkRule(const nsString& aSearchText)
258     : RotorLinkRule(aSearchText) {}
260 RotorVisitedLinkRule::RotorVisitedLinkRule(Accessible* aDirectDescendantsFrom,
261                                            const nsString& aSearchText)
262     : RotorLinkRule(aDirectDescendantsFrom, aSearchText) {}
264 uint16_t RotorVisitedLinkRule::Match(Accessible* aAcc) {
265   uint16_t result = RotorLinkRule::Match(aAcc);
267   if (result & nsIAccessibleTraversalRule::FILTER_MATCH) {
268     mozAccessible* nativeMatch = GetNativeFromGeckoAccessible(aAcc);
269     if (![[nativeMatch moxVisited] boolValue]) {
270       result &= ~nsIAccessibleTraversalRule::FILTER_MATCH;
271     }
272   }
274   return result;
277 RotorUnvisitedLinkRule::RotorUnvisitedLinkRule(const nsString& aSearchText)
278     : RotorLinkRule(aSearchText) {}
280 RotorUnvisitedLinkRule::RotorUnvisitedLinkRule(
281     Accessible* aDirectDescendantsFrom, const nsString& aSearchText)
282     : RotorLinkRule(aDirectDescendantsFrom, aSearchText) {}
284 uint16_t RotorUnvisitedLinkRule::Match(Accessible* aAcc) {
285   uint16_t result = RotorLinkRule::Match(aAcc);
287   if (result & nsIAccessibleTraversalRule::FILTER_MATCH) {
288     mozAccessible* nativeMatch = GetNativeFromGeckoAccessible(aAcc);
289     if ([[nativeMatch moxVisited] boolValue]) {
290       result &= ~nsIAccessibleTraversalRule::FILTER_MATCH;
291     }
292   }
294   return result;
297 // Match Not Rule
299 RotorNotMacRoleRule::RotorNotMacRoleRule(NSString* aMacRole,
300                                          Accessible* aDirectDescendantsFrom,
301                                          const nsString& aSearchText)
302     : RotorMacRoleRule(aMacRole, aDirectDescendantsFrom, aSearchText) {}
304 RotorNotMacRoleRule::RotorNotMacRoleRule(NSString* aMacRole,
305                                          const nsString& aSearchText)
306     : RotorMacRoleRule(aMacRole, aSearchText) {}
308 uint16_t RotorNotMacRoleRule::Match(Accessible* aAcc) {
309   uint16_t result = RotorRule::Match(aAcc);
311   // if a match was found in the base-class's Match function,
312   // it is valid to consider that match again here. if it is
313   // not different from the desired role, we flip the
314   // match bit to "unmatch" otherwise, the match persists.
315   if ((result & nsIAccessibleTraversalRule::FILTER_MATCH)) {
316     mozAccessible* nativeMatch = GetNativeFromGeckoAccessible(aAcc);
317     if ([[nativeMatch moxRole] isEqualToString:mMacRole]) {
318       result &= ~nsIAccessibleTraversalRule::FILTER_MATCH;
319     }
320   }
321   return result;
324 // Rotor Static Text Rule
326 RotorStaticTextRule::RotorStaticTextRule(Accessible* aDirectDescendantsFrom,
327                                          const nsString& aSearchText)
328     : RotorRule(aDirectDescendantsFrom, aSearchText){};
330 RotorStaticTextRule::RotorStaticTextRule(const nsString& aSearchText)
331     : RotorRule(aSearchText){};
333 uint16_t RotorStaticTextRule::Match(Accessible* aAcc) {
334   uint16_t result = RotorRule::Match(aAcc);
336   // if a match was found in the base-class's Match function,
337   // it is valid to consider that match again here. if it is
338   // not of the desired role, we flip the match bit to "unmatch"
339   // otherwise, the match persists.
340   if ((result & nsIAccessibleTraversalRule::FILTER_MATCH)) {
341     mozAccessible* nativeMatch = GetNativeFromGeckoAccessible(aAcc);
342     if (![[nativeMatch moxRole] isEqualToString:@"AXStaticText"]) {
343       result &= ~nsIAccessibleTraversalRule::FILTER_MATCH;
344     }
345   }
347   return result;
350 // Rotor Heading Level Rule
352 RotorHeadingLevelRule::RotorHeadingLevelRule(int32_t aLevel,
353                                              Accessible* aDirectDescendantsFrom,
354                                              const nsString& aSearchText)
355     : RotorRoleRule(roles::HEADING, aDirectDescendantsFrom, aSearchText),
356       mLevel(aLevel){};
358 RotorHeadingLevelRule::RotorHeadingLevelRule(int32_t aLevel,
359                                              const nsString& aSearchText)
360     : RotorRoleRule(roles::HEADING, aSearchText), mLevel(aLevel){};
362 uint16_t RotorHeadingLevelRule::Match(Accessible* aAcc) {
363   uint16_t result = RotorRoleRule::Match(aAcc);
365   // if a match was found in the base-class's Match function,
366   // it is valid to consider that match again here. if it is
367   // not of the desired heading level, we flip the match bit to
368   // "unmatch" otherwise, the match persists.
369   if ((result & nsIAccessibleTraversalRule::FILTER_MATCH)) {
370     int32_t currLevel = aAcc->GroupPosition().level;
372     if (currLevel != mLevel) {
373       result &= ~nsIAccessibleTraversalRule::FILTER_MATCH;
374     }
375   }
377   return result;
380 uint16_t RotorLiveRegionRule::Match(Accessible* aAcc) {
381   uint16_t result = RotorRule::Match(aAcc);
383   if ((result & nsIAccessibleTraversalRule::FILTER_MATCH)) {
384     mozAccessible* nativeMatch = GetNativeFromGeckoAccessible(aAcc);
385     if (![nativeMatch moxIsLiveRegion]) {
386       result &= ~nsIAccessibleTraversalRule::FILTER_MATCH;
387     }
388   }
389   return result;