Bug 1735252 [wpt PR 31197] - Regenerate WPT certificates, a=testonly
[gecko.git] / accessible / mac / MOXWebAreaAccessible.mm
bloba5d8e8c3057beb460bf02f790893d40c88078fe6
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 "MOXWebAreaAccessible.h"
10 #import "MOXSearchInfo.h"
12 #include "nsCocoaUtils.h"
13 #include "DocAccessibleParent.h"
15 using namespace mozilla::a11y;
17 @implementation MOXRootGroup
19 - (id)initWithParent:(MOXWebAreaAccessible*)parent {
20   // The parent is always a MOXWebAreaAccessible
21   mParent = parent;
22   return [super init];
25 - (NSString*)moxRole {
26   return NSAccessibilityGroupRole;
29 - (NSString*)moxRoleDescription {
30   if ([[self moxSubrole] isEqualToString:@"AXLandmarkApplication"]) {
31     return utils::LocalizedString(u"application"_ns);
32   }
34   return NSAccessibilityRoleDescription(NSAccessibilityGroupRole, nil);
37 - (id<mozAccessible>)moxParent {
38   return mParent;
41 - (NSArray*)moxChildren {
42   // Reparent the children of the web area here.
43   return [mParent rootGroupChildren];
46 - (NSString*)moxIdentifier {
47   // This is mostly for testing purposes to assert that this is the generated
48   // root group.
49   return @"root-group";
52 - (NSString*)moxSubrole {
53   // Steal the subrole internally mapped to the web area.
54   return [mParent moxSubrole];
57 - (id)moxHitTest:(NSPoint)point {
58   return [mParent moxHitTest:point];
61 - (NSValue*)moxPosition {
62   return [mParent moxPosition];
65 - (NSValue*)moxSize {
66   return [mParent moxSize];
69 - (NSArray*)moxUIElementsForSearchPredicate:(NSDictionary*)searchPredicate {
70   MOXSearchInfo* search =
71       [[[MOXSearchInfo alloc] initWithParameters:searchPredicate
72                                          andRoot:self] autorelease];
74   return [search performSearch];
77 - (NSNumber*)moxUIElementCountForSearchPredicate:
78     (NSDictionary*)searchPredicate {
79   return [NSNumber
80       numberWithDouble:[[self moxUIElementsForSearchPredicate:searchPredicate]
81                            count]];
84 - (BOOL)disableChild:(id)child {
85   return NO;
88 - (void)expire {
89   mParent = nil;
90   [super expire];
93 - (BOOL)isExpired {
94   MOZ_ASSERT((mParent == nil) == mIsExpired);
96   return [super isExpired];
99 @end
101 @implementation MOXWebAreaAccessible
103 - (NSString*)moxRole {
104   // The OS role is AXWebArea regardless of the gecko role
105   // (APPLICATION or DOCUMENT).
106   // If the web area has a role of APPLICATION, its root group will
107   // reflect that in a subrole/description.
108   return @"AXWebArea";
111 - (NSString*)moxRoleDescription {
112   // The role description is "HTML Content" regardless of the gecko role
113   // (APPLICATION or DOCUMENT)
114   return utils::LocalizedString(u"htmlContent"_ns);
117 - (NSURL*)moxURL {
118   if ([self isExpired]) {
119     return nil;
120   }
122   nsAutoString url;
123   if (mGeckoAccessible->IsLocal()) {
124     MOZ_ASSERT(mGeckoAccessible->AsLocal()->IsDoc());
125     DocAccessible* acc = mGeckoAccessible->AsLocal()->AsDoc();
126     acc->URL(url);
127   } else {
128     RemoteAccessible* proxy = mGeckoAccessible->AsRemote();
129     proxy->URL(url);
130   }
132   if (url.IsEmpty()) {
133     return nil;
134   }
136   return [NSURL URLWithString:nsCocoaUtils::ToNSString(url)];
139 - (NSNumber*)moxLoaded {
140   if ([self isExpired]) {
141     return nil;
142   }
143   // We are loaded if we aren't busy or stale
144   return @([self stateWithMask:(states::BUSY & states::STALE)] == 0);
147 // overrides
148 - (NSNumber*)moxLoadingProgress {
149   if ([self isExpired]) {
150     return nil;
151   }
153   if ([self stateWithMask:states::STALE] != 0) {
154     // We expose stale state until the document is ready (DOM is loaded and tree
155     // is constructed) so we indicate load hasn't started while this state is
156     // present.
157     return @0.0;
158   }
160   if ([self stateWithMask:states::BUSY] != 0) {
161     // We expose state busy until the document and all its subdocuments are
162     // completely loaded, so we indicate partial loading here
163     return @0.5;
164   }
166   // if we are not busy and not stale, we are loaded
167   return @1.0;
170 - (NSArray*)moxLinkUIElements {
171   NSDictionary* searchPredicate = @{
172     @"AXSearchKey" : @"AXLinkSearchKey",
173     @"AXImmediateDescendantsOnly" : @NO,
174     @"AXResultsLimit" : @(-1),
175     @"AXDirection" : @"AXDirectionNext",
176   };
178   return [self moxUIElementsForSearchPredicate:searchPredicate];
181 - (void)handleAccessibleEvent:(uint32_t)eventType {
182   switch (eventType) {
183     case nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE:
184       [self moxPostNotification:
185                 NSAccessibilityFocusedUIElementChangedNotification];
186       if ((mGeckoAccessible->IsRemote() &&
187            mGeckoAccessible->AsRemote()->IsDoc() &&
188            mGeckoAccessible->AsRemote()->AsDoc()->IsTopLevel()) ||
189           (mGeckoAccessible->IsLocal() &&
190            !mGeckoAccessible->AsLocal()->IsRoot() &&
191            mGeckoAccessible->AsLocal()->AsDoc()->ParentDocument()->IsRoot())) {
192         // we fire an AXLoadComplete event on top-level documents only
193         [self moxPostNotification:@"AXLoadComplete"];
194       } else {
195         // otherwise the doc belongs to an iframe (IsTopLevelInContentProcess)
196         // and we fire AXLayoutComplete instead
197         [self moxPostNotification:@"AXLayoutComplete"];
198       }
199       break;
200   }
202   [super handleAccessibleEvent:eventType];
205 - (NSArray*)rootGroupChildren {
206   // This method is meant to expose the doc's children to the root group.
207   return [super moxChildren];
210 - (NSArray*)moxUnignoredChildren {
211   if (id rootGroup = [self rootGroup]) {
212     return @[ [self rootGroup] ];
213   }
215   // There is no root group, expose the children here directly.
216   return [super moxUnignoredChildren];
219 - (BOOL)moxBlockSelector:(SEL)selector {
220   if (selector == @selector(moxSubrole)) {
221     // Never expose a subrole for a web area.
222     return YES;
223   }
225   if (selector == @selector(moxElementBusy)) {
226     // Don't confuse aria-busy with a document's busy state.
227     return YES;
228   }
230   return [super moxBlockSelector:selector];
233 - (void)moxPostNotification:(NSString*)notification {
234   if (![notification isEqualToString:@"AXElementBusyChanged"]) {
235     // Suppress AXElementBusyChanged since it uses gecko's BUSY state
236     // to tell VoiceOver about aria-busy changes. We use that state
237     // differently in documents.
238     [super moxPostNotification:notification];
239   }
242 - (id)rootGroup {
243   NSArray* children = [super moxUnignoredChildren];
244   if (mRole != roles::APPLICATION && [children count] == 1 &&
245       [[[children firstObject] moxUnignoredChildren] count] != 0) {
246     // We only need a root group if our document:
247     // (1) has multiple children, or
248     // (2) a one child that is a leaf, or
249     // (3) has a role of APPLICATION
250     return nil;
251   }
253   if (!mRootGroup) {
254     mRootGroup = [[MOXRootGroup alloc] initWithParent:self];
255   }
257   return mRootGroup;
260 - (void)expire {
261   [mRootGroup expire];
262   [super expire];
265 - (void)dealloc {
266   // This object can only be dealoced after the gecko accessible wrapper
267   // reference is released, and that happens after expire is called.
268   MOZ_ASSERT([self isExpired]);
269   [mRootGroup release];
271   [super dealloc];
274 @end