michael@0: /* -*- Mode: Objective-C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #import "mozAccessible.h" michael@0: michael@0: #import "MacUtils.h" michael@0: #import "mozView.h" michael@0: michael@0: #include "Accessible-inl.h" michael@0: #include "nsAccUtils.h" michael@0: #include "nsIAccessibleRelation.h" michael@0: #include "nsIAccessibleText.h" michael@0: #include "nsIAccessibleEditableText.h" michael@0: #include "nsIPersistentProperties2.h" michael@0: #include "Relation.h" michael@0: #include "Role.h" michael@0: #include "RootAccessible.h" michael@0: michael@0: #include "mozilla/Services.h" michael@0: #include "nsRect.h" michael@0: #include "nsCocoaUtils.h" michael@0: #include "nsCoord.h" michael@0: #include "nsObjCExceptions.h" michael@0: #include "nsWhitespaceTokenizer.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::a11y; michael@0: michael@0: // returns the passed in object if it is not ignored. if it's ignored, will return michael@0: // the first unignored ancestor. michael@0: static inline id michael@0: GetClosestInterestingAccessible(id anObject) michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; michael@0: michael@0: // this object is not ignored, so let's return it. michael@0: if (![anObject accessibilityIsIgnored]) michael@0: return GetObjectOrRepresentedView(anObject); michael@0: michael@0: // find the closest ancestor that is not ignored. michael@0: id unignoredObject = anObject; michael@0: while ((unignoredObject = [unignoredObject accessibilityAttributeValue:NSAccessibilityParentAttribute])) { michael@0: if (![unignoredObject accessibilityIsIgnored]) michael@0: // object is not ignored, so let's stop the search. michael@0: break; michael@0: } michael@0: michael@0: // if it's a mozAccessible, we need to take care to maybe return the view we michael@0: // represent, to the AT. michael@0: if ([unignoredObject respondsToSelector:@selector(hasRepresentedView)]) michael@0: return GetObjectOrRepresentedView(unignoredObject); michael@0: michael@0: return unignoredObject; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_NIL; michael@0: } michael@0: michael@0: #pragma mark - michael@0: michael@0: @implementation mozAccessible michael@0: michael@0: - (id)initWithAccessible:(AccessibleWrap*)geckoAccessible michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; michael@0: michael@0: if ((self = [super init])) { michael@0: mGeckoAccessible = geckoAccessible; michael@0: mRole = geckoAccessible->Role(); michael@0: } michael@0: michael@0: return self; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_NIL; michael@0: } michael@0: michael@0: - (void)dealloc michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK; michael@0: michael@0: [mChildren release]; michael@0: [super dealloc]; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK; michael@0: } michael@0: michael@0: #pragma mark - michael@0: michael@0: - (BOOL)accessibilityIsIgnored michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; michael@0: michael@0: // unknown (either unimplemented, or irrelevant) elements are marked as ignored michael@0: // as well as expired elements. michael@0: return !mGeckoAccessible || ([[self role] isEqualToString:NSAccessibilityUnknownRole] && michael@0: !(mGeckoAccessible->InteractiveState() & states::FOCUSABLE)); michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO); michael@0: } michael@0: michael@0: - (NSArray*)accessibilityAttributeNames michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; michael@0: michael@0: // if we're expired, we don't support any attributes. michael@0: if (!mGeckoAccessible) michael@0: return [NSArray array]; michael@0: michael@0: static NSArray *generalAttributes = nil; michael@0: michael@0: if (!generalAttributes) { michael@0: // standard attributes that are shared and supported by all generic elements. michael@0: generalAttributes = [[NSArray alloc] initWithObjects: NSAccessibilityChildrenAttribute, michael@0: NSAccessibilityParentAttribute, michael@0: NSAccessibilityRoleAttribute, michael@0: NSAccessibilityTitleAttribute, michael@0: NSAccessibilityValueAttribute, michael@0: NSAccessibilitySubroleAttribute, michael@0: NSAccessibilityRoleDescriptionAttribute, michael@0: NSAccessibilityPositionAttribute, michael@0: NSAccessibilityEnabledAttribute, michael@0: NSAccessibilitySizeAttribute, michael@0: NSAccessibilityWindowAttribute, michael@0: NSAccessibilityFocusedAttribute, michael@0: NSAccessibilityHelpAttribute, michael@0: NSAccessibilityTitleUIElementAttribute, michael@0: NSAccessibilityTopLevelUIElementAttribute, michael@0: NSAccessibilityDescriptionAttribute, michael@0: #if DEBUG michael@0: @"AXMozDescription", michael@0: #endif michael@0: nil]; michael@0: } michael@0: michael@0: return generalAttributes; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_NIL; michael@0: } michael@0: michael@0: - (id)accessibilityAttributeValue:(NSString*)attribute michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; michael@0: michael@0: if (!mGeckoAccessible) michael@0: return nil; michael@0: michael@0: #if DEBUG michael@0: if ([attribute isEqualToString:@"AXMozDescription"]) michael@0: return [NSString stringWithFormat:@"role = %u native = %@", mRole, [self class]]; michael@0: #endif michael@0: michael@0: if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) michael@0: return [self children]; michael@0: if ([attribute isEqualToString:NSAccessibilityParentAttribute]) michael@0: return [self parent]; michael@0: michael@0: #ifdef DEBUG_hakan michael@0: NSLog (@"(%@ responding to attr %@)", self, attribute); michael@0: #endif michael@0: michael@0: if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) michael@0: return [self role]; michael@0: if ([attribute isEqualToString:NSAccessibilityPositionAttribute]) michael@0: return [self position]; michael@0: if ([attribute isEqualToString:NSAccessibilitySubroleAttribute]) michael@0: return [self subrole]; michael@0: if ([attribute isEqualToString:NSAccessibilityEnabledAttribute]) michael@0: return [NSNumber numberWithBool:[self isEnabled]]; michael@0: if ([attribute isEqualToString:NSAccessibilityValueAttribute]) michael@0: return [self value]; michael@0: if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute]) michael@0: return [self roleDescription]; michael@0: if ([attribute isEqualToString:NSAccessibilityDescriptionAttribute]) michael@0: return [self customDescription]; michael@0: if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) michael@0: return [NSNumber numberWithBool:[self isFocused]]; michael@0: if ([attribute isEqualToString:NSAccessibilitySizeAttribute]) michael@0: return [self size]; michael@0: if ([attribute isEqualToString:NSAccessibilityWindowAttribute]) michael@0: return [self window]; michael@0: if ([attribute isEqualToString:NSAccessibilityTopLevelUIElementAttribute]) michael@0: return [self window]; michael@0: if ([attribute isEqualToString:NSAccessibilityTitleAttribute]) michael@0: return [self title]; michael@0: if ([attribute isEqualToString:NSAccessibilityTitleUIElementAttribute]) { michael@0: Relation rel = mGeckoAccessible->RelationByType(RelationType::LABELLED_BY); michael@0: Accessible* tempAcc = rel.Next(); michael@0: return tempAcc ? GetNativeFromGeckoAccessible(tempAcc) : nil; michael@0: } michael@0: if ([attribute isEqualToString:NSAccessibilityHelpAttribute]) michael@0: return [self help]; michael@0: michael@0: #ifdef DEBUG michael@0: NSLog (@"!!! %@ can't respond to attribute %@", self, attribute); michael@0: #endif michael@0: return nil; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_NIL; michael@0: } michael@0: michael@0: - (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; michael@0: michael@0: if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) michael@0: return [self canBeFocused]; michael@0: michael@0: return NO; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO); michael@0: } michael@0: michael@0: - (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK; michael@0: michael@0: #ifdef DEBUG_hakan michael@0: NSLog (@"[%@] %@='%@'", self, attribute, value); michael@0: #endif michael@0: michael@0: // we only support focusing elements so far. michael@0: if ([attribute isEqualToString:NSAccessibilityFocusedAttribute] && [value boolValue]) michael@0: [self focus]; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK; michael@0: } michael@0: michael@0: - (id)accessibilityHitTest:(NSPoint)point michael@0: { michael@0: if (!mGeckoAccessible) michael@0: return nil; michael@0: michael@0: // Convert the given screen-global point in the cocoa coordinate system (with michael@0: // origin in the bottom-left corner of the screen) into point in the Gecko michael@0: // coordinate system (with origin in a top-left screen point). michael@0: NSScreen* mainView = [[NSScreen screens] objectAtIndex:0]; michael@0: NSPoint tmpPoint = NSMakePoint(point.x, michael@0: [mainView frame].size.height - point.y); michael@0: nsIntPoint geckoPoint = nsCocoaUtils:: michael@0: CocoaPointsToDevPixels(tmpPoint, nsCocoaUtils::GetBackingScaleFactor(mainView)); michael@0: michael@0: Accessible* child = mGeckoAccessible->ChildAtPoint(geckoPoint.x, geckoPoint.y, michael@0: Accessible::eDeepestChild); michael@0: michael@0: if (child) { michael@0: mozAccessible* nativeChild = GetNativeFromGeckoAccessible(child); michael@0: if (nativeChild) michael@0: return GetClosestInterestingAccessible(nativeChild); michael@0: } michael@0: michael@0: // if we didn't find anything, return ourself (or the first unignored ancestor). michael@0: return GetClosestInterestingAccessible(self); michael@0: } michael@0: michael@0: - (NSArray*)accessibilityActionNames michael@0: { michael@0: return nil; michael@0: } michael@0: michael@0: - (NSString*)accessibilityActionDescription:(NSString*)action michael@0: { michael@0: // by default we return whatever the MacOS API know about. michael@0: // if you have custom actions, override. michael@0: return NSAccessibilityActionDescription(action); michael@0: } michael@0: michael@0: - (void)accessibilityPerformAction:(NSString*)action michael@0: { michael@0: } michael@0: michael@0: - (id)accessibilityFocusedUIElement michael@0: { michael@0: if (!mGeckoAccessible) michael@0: return nil; michael@0: michael@0: Accessible* focusedGeckoChild = mGeckoAccessible->FocusedChild(); michael@0: if (focusedGeckoChild) { michael@0: mozAccessible *focusedChild = GetNativeFromGeckoAccessible(focusedGeckoChild); michael@0: if (focusedChild) michael@0: return GetClosestInterestingAccessible(focusedChild); michael@0: } michael@0: michael@0: // return ourself if we can't get a native focused child. michael@0: return GetClosestInterestingAccessible(self); michael@0: } michael@0: michael@0: #pragma mark - michael@0: michael@0: - (id )parent michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; michael@0: michael@0: Accessible* accessibleParent = mGeckoAccessible->GetUnignoredParent(); michael@0: if (accessibleParent) { michael@0: id nativeParent = GetNativeFromGeckoAccessible(accessibleParent); michael@0: if (nativeParent) michael@0: return GetClosestInterestingAccessible(nativeParent); michael@0: } michael@0: michael@0: // GetUnignoredParent() returns null when there is no unignored accessible all the way up to michael@0: // the root accessible. so we'll have to return whatever native accessible is above our root accessible michael@0: // (which might be the owning NSWindow in the application, for example). michael@0: // michael@0: // get the native root accessible, and tell it to return its first parent unignored accessible. michael@0: RootAccessible* root = mGeckoAccessible->RootAccessible(); michael@0: id nativeParent = GetNativeFromGeckoAccessible(static_cast(root)); michael@0: NSAssert1 (nativeParent, @"!!! we can't find a parent for %@", self); michael@0: michael@0: return GetClosestInterestingAccessible(nativeParent); michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_NIL; michael@0: } michael@0: michael@0: - (BOOL)hasRepresentedView michael@0: { michael@0: return NO; michael@0: } michael@0: michael@0: - (id)representedView michael@0: { michael@0: return nil; michael@0: } michael@0: michael@0: - (BOOL)isRoot michael@0: { michael@0: return NO; michael@0: } michael@0: michael@0: // gets our native children lazily. michael@0: // returns nil when there are no children. michael@0: - (NSArray*)children michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; michael@0: michael@0: if (mChildren || !mGeckoAccessible->AreChildrenCached()) michael@0: return mChildren; michael@0: michael@0: mChildren = [[NSMutableArray alloc] init]; michael@0: michael@0: // get the array of children. michael@0: nsAutoTArray childrenArray; michael@0: mGeckoAccessible->GetUnignoredChildren(&childrenArray); michael@0: michael@0: // now iterate through the children array, and get each native accessible. michael@0: uint32_t totalCount = childrenArray.Length(); michael@0: for (uint32_t idx = 0; idx < totalCount; idx++) { michael@0: Accessible* curAccessible = childrenArray.ElementAt(idx); michael@0: if (curAccessible) { michael@0: mozAccessible *curNative = GetNativeFromGeckoAccessible(curAccessible); michael@0: if (curNative) michael@0: [mChildren addObject:GetObjectOrRepresentedView(curNative)]; michael@0: } michael@0: } michael@0: michael@0: #ifdef DEBUG_hakan michael@0: // make sure we're not returning any ignored accessibles. michael@0: NSEnumerator *e = [mChildren objectEnumerator]; michael@0: mozAccessible *m = nil; michael@0: while ((m = [e nextObject])) { michael@0: NSAssert1(![m accessibilityIsIgnored], @"we should never return an ignored accessible! (%@)", m); michael@0: } michael@0: #endif michael@0: michael@0: return mChildren; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_NIL; michael@0: } michael@0: michael@0: - (NSValue*)position michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; michael@0: michael@0: if (!mGeckoAccessible) michael@0: return nil; michael@0: michael@0: int32_t x = 0, y = 0, width = 0, height = 0; michael@0: mGeckoAccessible->GetBounds(&x, &y, &width, &height); michael@0: michael@0: NSScreen* mainView = [[NSScreen screens] objectAtIndex:0]; michael@0: CGFloat scaleFactor = nsCocoaUtils::GetBackingScaleFactor(mainView); michael@0: NSPoint p = NSMakePoint(static_cast(x) / scaleFactor, michael@0: [mainView frame].size.height - static_cast(y + height) / scaleFactor); michael@0: michael@0: return [NSValue valueWithPoint:p]; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_NIL; michael@0: } michael@0: michael@0: - (NSValue*)size michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; michael@0: michael@0: if (!mGeckoAccessible) michael@0: return nil; michael@0: michael@0: int32_t x = 0, y = 0, width = 0, height = 0; michael@0: mGeckoAccessible->GetBounds (&x, &y, &width, &height); michael@0: CGFloat scaleFactor = michael@0: nsCocoaUtils::GetBackingScaleFactor([[NSScreen screens] objectAtIndex:0]); michael@0: return [NSValue valueWithSize:NSMakeSize(static_cast(width) / scaleFactor, michael@0: static_cast(height) / scaleFactor)]; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_NIL; michael@0: } michael@0: michael@0: - (NSString*)role michael@0: { michael@0: if (!mGeckoAccessible) michael@0: return nil; michael@0: michael@0: #ifdef DEBUG_A11Y michael@0: NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(mGeckoAccessible), michael@0: "Does not support nsIAccessibleText when it should"); michael@0: #endif michael@0: michael@0: #define ROLE(geckoRole, stringRole, atkRole, macRole, msaaRole, ia2Role, nameRule) \ michael@0: case roles::geckoRole: \ michael@0: return macRole; michael@0: michael@0: switch (mRole) { michael@0: #include "RoleMap.h" michael@0: default: michael@0: NS_NOTREACHED("Unknown role."); michael@0: return NSAccessibilityUnknownRole; michael@0: } michael@0: michael@0: #undef ROLE michael@0: } michael@0: michael@0: - (NSString*)subrole michael@0: { michael@0: if (!mGeckoAccessible) michael@0: return nil; michael@0: michael@0: // XXX maybe we should cache the subrole. michael@0: nsAutoString xmlRoles; michael@0: michael@0: // XXX we don't need all the attributes (see bug 771113) michael@0: nsCOMPtr attributes = mGeckoAccessible->Attributes(); michael@0: if (attributes) michael@0: nsAccUtils::GetAccAttr(attributes, nsGkAtoms::xmlroles, xmlRoles); michael@0: michael@0: nsWhitespaceTokenizer tokenizer(xmlRoles); michael@0: michael@0: while (tokenizer.hasMoreTokens()) { michael@0: const nsDependentSubstring token(tokenizer.nextToken()); michael@0: michael@0: if (token.EqualsLiteral("banner")) michael@0: return @"AXLandmarkBanner"; michael@0: michael@0: if (token.EqualsLiteral("complementary")) michael@0: return @"AXLandmarkComplementary"; michael@0: michael@0: if (token.EqualsLiteral("contentinfo")) michael@0: return @"AXLandmarkContentInfo"; michael@0: michael@0: if (token.EqualsLiteral("main")) michael@0: return @"AXLandmarkMain"; michael@0: michael@0: if (token.EqualsLiteral("navigation")) michael@0: return @"AXLandmarkNavigation"; michael@0: michael@0: if (token.EqualsLiteral("search")) michael@0: return @"AXLandmarkSearch"; michael@0: } michael@0: michael@0: switch (mRole) { michael@0: case roles::LIST: michael@0: return @"AXContentList"; // 10.6+ NSAccessibilityContentListSubrole; michael@0: michael@0: case roles::DEFINITION_LIST: michael@0: return @"AXDefinitionList"; // 10.6+ NSAccessibilityDefinitionListSubrole; michael@0: michael@0: case roles::TERM: michael@0: return @"AXTerm"; michael@0: michael@0: case roles::DEFINITION: michael@0: return @"AXDefinition"; michael@0: michael@0: default: michael@0: break; michael@0: } michael@0: michael@0: return nil; michael@0: } michael@0: michael@0: - (NSString*)roleDescription michael@0: { michael@0: if (mRole == roles::DOCUMENT) michael@0: return utils::LocalizedString(NS_LITERAL_STRING("htmlContent")); michael@0: michael@0: NSString* subrole = [self subrole]; michael@0: michael@0: if ((mRole == roles::LISTITEM) && [subrole isEqualToString:@"AXTerm"]) michael@0: return utils::LocalizedString(NS_LITERAL_STRING("term")); michael@0: if ((mRole == roles::PARAGRAPH) && [subrole isEqualToString:@"AXDefinition"]) michael@0: return utils::LocalizedString(NS_LITERAL_STRING("definition")); michael@0: michael@0: NSString* role = [self role]; michael@0: michael@0: // the WAI-ARIA Landmarks michael@0: if ([role isEqualToString:NSAccessibilityGroupRole]) { michael@0: if ([subrole isEqualToString:@"AXLandmarkBanner"]) michael@0: return utils::LocalizedString(NS_LITERAL_STRING("banner")); michael@0: if ([subrole isEqualToString:@"AXLandmarkComplementary"]) michael@0: return utils::LocalizedString(NS_LITERAL_STRING("complementary")); michael@0: if ([subrole isEqualToString:@"AXLandmarkContentInfo"]) michael@0: return utils::LocalizedString(NS_LITERAL_STRING("content")); michael@0: if ([subrole isEqualToString:@"AXLandmarkMain"]) michael@0: return utils::LocalizedString(NS_LITERAL_STRING("main")); michael@0: if ([subrole isEqualToString:@"AXLandmarkNavigation"]) michael@0: return utils::LocalizedString(NS_LITERAL_STRING("navigation")); michael@0: if ([subrole isEqualToString:@"AXLandmarkSearch"]) michael@0: return utils::LocalizedString(NS_LITERAL_STRING("search")); michael@0: } michael@0: michael@0: return NSAccessibilityRoleDescription(role, subrole); michael@0: } michael@0: michael@0: - (NSString*)title michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; michael@0: michael@0: nsAutoString title; michael@0: mGeckoAccessible->Name(title); michael@0: return nsCocoaUtils::ToNSString(title); michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_NIL; michael@0: } michael@0: michael@0: - (id)value michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; michael@0: michael@0: nsAutoString value; michael@0: mGeckoAccessible->GetValue (value); michael@0: return value.IsEmpty() ? nil : [NSString stringWithCharacters:reinterpret_cast(value.BeginReading()) michael@0: length:value.Length()]; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_NIL; michael@0: } michael@0: michael@0: - (void)valueDidChange michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK; michael@0: michael@0: #ifdef DEBUG_hakan michael@0: NSLog(@"%@'s value changed!", self); michael@0: #endif michael@0: // sending out a notification is expensive, so we don't do it other than for really important objects, michael@0: // like mozTextAccessible. michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK; michael@0: } michael@0: michael@0: - (void)selectedTextDidChange michael@0: { michael@0: // Do nothing. mozTextAccessible will. michael@0: } michael@0: michael@0: - (NSString*)customDescription michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; michael@0: michael@0: if (mGeckoAccessible->IsDefunct()) michael@0: return nil; michael@0: michael@0: nsAutoString desc; michael@0: mGeckoAccessible->Description(desc); michael@0: michael@0: return nsCocoaUtils::ToNSString(desc); michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_NIL; michael@0: } michael@0: michael@0: - (NSString*)help michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; michael@0: michael@0: nsAutoString helpText; michael@0: mGeckoAccessible->GetHelp (helpText); michael@0: return helpText.IsEmpty() ? nil : [NSString stringWithCharacters:reinterpret_cast(helpText.BeginReading()) michael@0: length:helpText.Length()]; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_NIL; michael@0: } michael@0: michael@0: // objc-style description (from NSObject); not to be confused with the accessible description above. michael@0: - (NSString*)description michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; michael@0: michael@0: return [NSString stringWithFormat:@"(%p) %@", self, [self role]]; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_NIL; michael@0: } michael@0: michael@0: - (BOOL)isFocused michael@0: { michael@0: return FocusMgr()->IsFocused(mGeckoAccessible); michael@0: } michael@0: michael@0: - (BOOL)canBeFocused michael@0: { michael@0: return mGeckoAccessible && (mGeckoAccessible->InteractiveState() & states::FOCUSABLE); michael@0: } michael@0: michael@0: - (BOOL)focus michael@0: { michael@0: if (!mGeckoAccessible) michael@0: return NO; michael@0: michael@0: nsresult rv = mGeckoAccessible->TakeFocus(); michael@0: return NS_SUCCEEDED(rv); michael@0: } michael@0: michael@0: - (BOOL)isEnabled michael@0: { michael@0: return mGeckoAccessible && ((mGeckoAccessible->InteractiveState() & states::UNAVAILABLE) == 0); michael@0: } michael@0: michael@0: // The root accessible calls this when the focused node was michael@0: // changed to us. michael@0: - (void)didReceiveFocus michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK; michael@0: michael@0: #ifdef DEBUG_hakan michael@0: NSLog (@"%@ received focus!", self); michael@0: #endif michael@0: NSAssert1(![self accessibilityIsIgnored], @"trying to set focus to ignored element! (%@)", self); michael@0: NSAccessibilityPostNotification(GetObjectOrRepresentedView(self), michael@0: NSAccessibilityFocusedUIElementChangedNotification); michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK; michael@0: } michael@0: michael@0: - (NSWindow*)window michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; michael@0: michael@0: AccessibleWrap* accWrap = static_cast(mGeckoAccessible); michael@0: michael@0: // Get a pointer to the native window (NSWindow) we reside in. michael@0: NSWindow *nativeWindow = nil; michael@0: DocAccessible* docAcc = accWrap->Document(); michael@0: if (docAcc) michael@0: nativeWindow = static_cast(docAcc->GetNativeWindow()); michael@0: michael@0: NSAssert1(nativeWindow, @"Could not get native window for %@", self); michael@0: return nativeWindow; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_NIL; michael@0: } michael@0: michael@0: - (void)invalidateChildren michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK; michael@0: michael@0: // make room for new children michael@0: [mChildren release]; michael@0: mChildren = nil; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK; michael@0: } michael@0: michael@0: - (void)appendChild:(Accessible*)aAccessible michael@0: { michael@0: // if mChildren is nil, then we don't even need to bother michael@0: if (!mChildren) michael@0: return; michael@0: michael@0: mozAccessible *curNative = GetNativeFromGeckoAccessible(aAccessible); michael@0: if (curNative) michael@0: [mChildren addObject:GetObjectOrRepresentedView(curNative)]; michael@0: } michael@0: michael@0: - (void)expire michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK; michael@0: michael@0: [self invalidateChildren]; michael@0: michael@0: mGeckoAccessible = nullptr; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK; michael@0: } michael@0: michael@0: - (BOOL)isExpired michael@0: { michael@0: return !mGeckoAccessible; michael@0: } michael@0: michael@0: #pragma mark - michael@0: #pragma mark Debug methods michael@0: #pragma mark - michael@0: michael@0: #ifdef DEBUG michael@0: michael@0: // will check that our children actually reference us as their michael@0: // parent. michael@0: - (void)sanityCheckChildren:(NSArray *)children michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK; michael@0: michael@0: NSAssert(![self accessibilityIsIgnored], @"can't sanity check children of an ignored accessible!"); michael@0: NSEnumerator *iter = [children objectEnumerator]; michael@0: mozAccessible *curObj = nil; michael@0: michael@0: NSLog(@"sanity checking %@", self); michael@0: michael@0: while ((curObj = [iter nextObject])) { michael@0: id realSelf = GetObjectOrRepresentedView(self); michael@0: NSLog(@"checking %@", realSelf); michael@0: NSAssert2([curObj parent] == realSelf, michael@0: @"!!! %@ not returning %@ as AXParent, even though it is a AXChild of it!", curObj, realSelf); michael@0: } michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK; michael@0: } michael@0: michael@0: - (void)sanityCheckChildren michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK; michael@0: michael@0: [self sanityCheckChildren:[self children]]; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK; michael@0: } michael@0: michael@0: - (void)printHierarchy michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK; michael@0: michael@0: [self printHierarchyWithLevel:0]; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK; michael@0: } michael@0: michael@0: - (void)printHierarchyWithLevel:(unsigned)level michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK; michael@0: michael@0: NSAssert(![self isExpired], @"!!! trying to print hierarchy of expired object!"); michael@0: michael@0: // print this node michael@0: NSMutableString *indent = [NSMutableString stringWithCapacity:level]; michael@0: unsigned i=0; michael@0: for (;i