2 /* -*- Mode: Objective-C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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
25 - (NSString*)moxRole {
26 return NSAccessibilityGroupRole;
29 - (NSString*)moxRoleDescription {
30 if ([[self moxSubrole] isEqualToString:@"AXLandmarkApplication"]) {
31 return utils::LocalizedString(u"application"_ns);
34 return NSAccessibilityRoleDescription(NSAccessibilityGroupRole, nil);
37 - (id<mozAccessible>)moxParent {
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
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];
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 {
80 numberWithDouble:[[self moxUIElementsForSearchPredicate:searchPredicate]
84 - (BOOL)disableChild:(id)child {
94 MOZ_ASSERT((mParent == nil) == mIsExpired);
96 return [super isExpired];
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.
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);
118 if ([self isExpired]) {
123 if (mGeckoAccessible->IsLocal()) {
124 MOZ_ASSERT(mGeckoAccessible->AsLocal()->IsDoc());
125 DocAccessible* acc = mGeckoAccessible->AsLocal()->AsDoc();
128 RemoteAccessible* proxy = mGeckoAccessible->AsRemote();
136 return [NSURL URLWithString:nsCocoaUtils::ToNSString(url)];
139 - (NSNumber*)moxLoaded {
140 if ([self isExpired]) {
143 // We are loaded if we aren't busy or stale
144 return @([self stateWithMask:(states::BUSY & states::STALE)] == 0);
148 - (NSNumber*)moxLoadingProgress {
149 if ([self isExpired]) {
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
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
166 // if we are not busy and not stale, we are loaded
170 - (NSArray*)moxLinkUIElements {
171 NSDictionary* searchPredicate = @{
172 @"AXSearchKey" : @"AXLinkSearchKey",
173 @"AXImmediateDescendantsOnly" : @NO,
174 @"AXResultsLimit" : @(-1),
175 @"AXDirection" : @"AXDirectionNext",
178 return [self moxUIElementsForSearchPredicate:searchPredicate];
181 - (void)handleAccessibleEvent:(uint32_t)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"];
195 // otherwise the doc belongs to an iframe (IsTopLevelInContentProcess)
196 // and we fire AXLayoutComplete instead
197 [self moxPostNotification:@"AXLayoutComplete"];
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] ];
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.
225 if (selector == @selector(moxElementBusy)) {
226 // Don't confuse aria-busy with a document's busy state.
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];
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
254 mRootGroup = [[MOXRootGroup alloc] initWithParent:self];
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];