2007-03-22 10:30:00 -07:00
|
|
|
/* -*- Mode: Objective-C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
* the License. You may obtain a copy of the License at
|
|
|
|
* http://www.mozilla.org/MPL/
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
* License.
|
|
|
|
*
|
|
|
|
* The Original Code is mozilla.org code.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is
|
|
|
|
* Mozilla Foundation.
|
|
|
|
* Portions created by the Initial Developer are Copyright (C) 2006
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
|
|
|
* Original Author: Håkan Waara <hwaara@gmail.com>
|
|
|
|
*
|
|
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
|
|
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
|
|
|
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
* the provisions above, a recipient may use your version of this file under
|
|
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
*
|
|
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
|
|
|
|
#import "mozAccessible.h"
|
|
|
|
|
|
|
|
// to get the mozView formal protocol, that all gecko's ChildViews implement.
|
|
|
|
#import "mozView.h"
|
|
|
|
#import "nsRoleMap.h"
|
|
|
|
|
|
|
|
#include "nsRect.h"
|
|
|
|
#include "nsCoord.h"
|
2008-02-22 12:13:17 -08:00
|
|
|
#include "nsObjCExceptions.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
#include "nsIAccessible.h"
|
2011-12-08 13:19:29 -08:00
|
|
|
#include "nsIAccessibleRelation.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
#include "nsIAccessibleText.h"
|
|
|
|
#include "nsIAccessibleEditableText.h"
|
2011-12-08 13:19:29 -08:00
|
|
|
#include "Relation.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
#include "nsRootAccessible.h"
|
|
|
|
|
2011-07-27 05:43:01 -07:00
|
|
|
using namespace mozilla::a11y;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// These constants are only defined in OS X SDK 10.4, so we define them in order
|
|
|
|
// to be able to use for earlier OS versions.
|
|
|
|
const NSString *kInstanceDescriptionAttribute = @"AXDescription"; // NSAccessibilityDescriptionAttribute
|
|
|
|
const NSString *kTopLevelUIElementAttribute = @"AXTopLevelUIElement"; // NSAccessibilityTopLevelUIElementAttribute
|
|
|
|
const NSString *kTextLinkSubrole = @"AXTextLink"; // NSAccessibilitySecureTextFieldSubrole
|
|
|
|
const NSString *kURLAttribute = @"AXURL";
|
|
|
|
|
|
|
|
// converts a screen-global point in the cocoa coordinate system (with origo in the bottom-left corner
|
|
|
|
// of the screen), into a top-left screen point, that gecko can use.
|
|
|
|
static inline void
|
|
|
|
ConvertCocoaToGeckoPoint(NSPoint &aInPoint, nsPoint &aOutPoint)
|
|
|
|
{
|
|
|
|
float mainScreenHeight = [(NSView*)[[NSScreen screens] objectAtIndex:0] frame].size.height;
|
|
|
|
aOutPoint.MoveTo ((nscoord)aInPoint.x, (nscoord)(mainScreenHeight - aInPoint.y));
|
|
|
|
}
|
|
|
|
|
|
|
|
// all mozAccessibles are either abstract objects (that correspond to XUL widgets, HTML frames, etc) or are
|
|
|
|
// attached to a certain view; for example a document view. when we hand an object off to an AT, we always want
|
|
|
|
// to give it the represented view, in the latter case.
|
|
|
|
static inline id <mozAccessible>
|
|
|
|
GetObjectOrRepresentedView(id <mozAccessible> anObject)
|
|
|
|
{
|
2008-02-22 12:13:17 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if ([anObject hasRepresentedView])
|
|
|
|
return [anObject representedView];
|
|
|
|
return anObject;
|
2008-02-22 12:13:17 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// returns the passed in object if it is not ignored. if it's ignored, will return
|
|
|
|
// the first unignored ancestor.
|
|
|
|
static inline id
|
|
|
|
GetClosestInterestingAccessible(id anObject)
|
|
|
|
{
|
2008-02-22 12:13:17 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// this object is not ignored, so let's return it.
|
|
|
|
if (![anObject accessibilityIsIgnored])
|
|
|
|
return GetObjectOrRepresentedView(anObject);
|
|
|
|
|
|
|
|
// find the closest ancestor that is not ignored.
|
|
|
|
id unignoredObject = anObject;
|
|
|
|
while ((unignoredObject = [unignoredObject accessibilityAttributeValue:NSAccessibilityParentAttribute])) {
|
|
|
|
if (![unignoredObject accessibilityIsIgnored])
|
|
|
|
// object is not ignored, so let's stop the search.
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if it's a mozAccessible, we need to take care to maybe return the view we
|
|
|
|
// represent, to the AT.
|
|
|
|
if ([unignoredObject respondsToSelector:@selector(hasRepresentedView)])
|
|
|
|
return GetObjectOrRepresentedView(unignoredObject);
|
|
|
|
|
|
|
|
return unignoredObject;
|
2008-02-22 12:13:17 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline mozAccessible*
|
|
|
|
GetNativeFromGeckoAccessible(nsIAccessible *anAccessible)
|
|
|
|
{
|
2008-02-22 12:13:17 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSNULL;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
mozAccessible *native = nil;
|
|
|
|
anAccessible->GetNativeInterface ((void**)&native);
|
|
|
|
return native;
|
2008-02-22 12:13:17 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NSNULL;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
|
|
|
|
@implementation mozAccessible
|
|
|
|
|
|
|
|
- (id)initWithAccessible:(nsAccessibleWrap*)geckoAccessible
|
|
|
|
{
|
2008-02-22 12:13:17 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if ((self = [super init])) {
|
|
|
|
mGeckoAccessible = geckoAccessible;
|
|
|
|
mIsExpired = NO;
|
2010-09-05 20:33:29 -07:00
|
|
|
mRole = geckoAccessible->Role();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// Check for OS X "role skew"; the role constants in nsIAccessible.idl need to match the ones
|
|
|
|
// in nsRoleMap.h.
|
|
|
|
NS_ASSERTION([AXRoles[nsIAccessibleRole::ROLE_LAST_ENTRY] isEqualToString:@"ROLE_LAST_ENTRY"], "Role skew in the role map!");
|
|
|
|
}
|
|
|
|
|
|
|
|
return self;
|
2008-02-22 12:13:17 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)dealloc
|
|
|
|
{
|
2008-02-22 12:13:17 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
[mChildren release];
|
|
|
|
[super dealloc];
|
2008-02-22 12:13:17 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
|
|
|
|
- (BOOL)accessibilityIsIgnored
|
|
|
|
{
|
2008-02-22 12:13:17 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// unknown (either unimplemented, or irrelevant) elements are marked as ignored
|
|
|
|
// as well as expired elements.
|
|
|
|
return mIsExpired || [[self role] isEqualToString:NSAccessibilityUnknownRole];
|
2008-02-22 12:13:17 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSArray*)accessibilityAttributeNames
|
|
|
|
{
|
2008-02-22 12:13:17 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// if we're expired, we don't support any attributes.
|
|
|
|
if (mIsExpired)
|
|
|
|
return [NSArray array];
|
|
|
|
|
|
|
|
static NSArray *generalAttributes = nil;
|
|
|
|
|
|
|
|
if (!generalAttributes) {
|
|
|
|
// standard attributes that are shared and supported by all generic elements.
|
|
|
|
generalAttributes = [[NSArray alloc] initWithObjects: NSAccessibilityChildrenAttribute,
|
|
|
|
NSAccessibilityParentAttribute,
|
|
|
|
NSAccessibilityRoleAttribute,
|
|
|
|
NSAccessibilityTitleAttribute,
|
|
|
|
NSAccessibilityValueAttribute,
|
|
|
|
NSAccessibilitySubroleAttribute,
|
|
|
|
NSAccessibilityRoleDescriptionAttribute,
|
|
|
|
NSAccessibilityPositionAttribute,
|
|
|
|
NSAccessibilityEnabledAttribute,
|
|
|
|
NSAccessibilitySizeAttribute,
|
|
|
|
NSAccessibilityWindowAttribute,
|
|
|
|
NSAccessibilityFocusedAttribute,
|
|
|
|
NSAccessibilityHelpAttribute,
|
|
|
|
NSAccessibilityTitleUIElementAttribute,
|
|
|
|
kTopLevelUIElementAttribute,
|
|
|
|
kInstanceDescriptionAttribute,
|
|
|
|
nil];
|
|
|
|
}
|
|
|
|
|
|
|
|
return generalAttributes;
|
2008-02-22 12:13:17 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
- (id)accessibilityAttributeValue:(NSString*)attribute
|
|
|
|
{
|
2008-02-22 12:13:17 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (mIsExpired)
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
if ([attribute isEqualToString:NSAccessibilityChildrenAttribute])
|
|
|
|
return [self children];
|
|
|
|
if ([attribute isEqualToString:NSAccessibilityParentAttribute])
|
|
|
|
return [self parent];
|
|
|
|
|
|
|
|
#ifdef DEBUG_hakan
|
|
|
|
NSLog (@"(%@ responding to attr %@)", self, attribute);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if ([attribute isEqualToString:NSAccessibilityRoleAttribute])
|
|
|
|
return [self role];
|
|
|
|
if ([attribute isEqualToString:NSAccessibilityPositionAttribute])
|
|
|
|
return [self position];
|
|
|
|
if ([attribute isEqualToString:NSAccessibilitySubroleAttribute])
|
|
|
|
return [self subrole];
|
|
|
|
if ([attribute isEqualToString:NSAccessibilityEnabledAttribute])
|
|
|
|
return [NSNumber numberWithBool:[self isEnabled]];
|
|
|
|
if ([attribute isEqualToString:NSAccessibilityValueAttribute])
|
|
|
|
return [self value];
|
|
|
|
if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute])
|
|
|
|
return NSAccessibilityRoleDescription([self role], nil);
|
2011-05-17 07:00:30 -07:00
|
|
|
if ([attribute isEqualToString: (NSString*) kInstanceDescriptionAttribute])
|
2007-03-22 10:30:00 -07:00
|
|
|
return [self customDescription];
|
|
|
|
if ([attribute isEqualToString:NSAccessibilityFocusedAttribute])
|
|
|
|
return [NSNumber numberWithBool:[self isFocused]];
|
|
|
|
if ([attribute isEqualToString:NSAccessibilitySizeAttribute])
|
|
|
|
return [self size];
|
|
|
|
if ([attribute isEqualToString:NSAccessibilityWindowAttribute])
|
|
|
|
return [self window];
|
2011-05-17 07:00:30 -07:00
|
|
|
if ([attribute isEqualToString: (NSString*) kTopLevelUIElementAttribute])
|
2007-03-22 10:30:00 -07:00
|
|
|
return [self window];
|
2011-12-08 13:19:29 -08:00
|
|
|
if ([attribute isEqualToString:NSAccessibilityTitleAttribute])
|
2007-03-22 10:30:00 -07:00
|
|
|
return [self title];
|
2011-12-08 13:19:29 -08:00
|
|
|
if ([attribute isEqualToString:NSAccessibilityTitleUIElementAttribute]) {
|
|
|
|
Relation rel = mGeckoAccessible->RelationByType(nsIAccessibleRelation::RELATION_LABELLED_BY);
|
|
|
|
nsAccessible* tempAcc = rel.Next();
|
|
|
|
return tempAcc ? GetNativeFromGeckoAccessible(tempAcc) : nil;
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
if ([attribute isEqualToString:NSAccessibilityHelpAttribute])
|
|
|
|
return [self help];
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
NSLog (@"!!! %@ can't respond to attribute %@", self, attribute);
|
|
|
|
#endif
|
2011-12-08 13:19:29 -08:00
|
|
|
return nil;
|
2008-02-22 12:13:17 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute
|
|
|
|
{
|
2008-02-22 12:13:17 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if ([attribute isEqualToString:NSAccessibilityFocusedAttribute])
|
|
|
|
return [self canBeFocused];
|
|
|
|
|
|
|
|
return NO;
|
2008-02-22 12:13:17 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute
|
|
|
|
{
|
2008-02-22 12:13:17 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
#ifdef DEBUG_hakan
|
|
|
|
NSLog (@"[%@] %@='%@'", self, attribute, value);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// we only support focusing elements so far.
|
|
|
|
if ([attribute isEqualToString:NSAccessibilityFocusedAttribute] && [value boolValue])
|
|
|
|
[self focus];
|
2008-02-22 12:13:17 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
- (id)accessibilityHitTest:(NSPoint)point
|
|
|
|
{
|
|
|
|
if (mIsExpired)
|
|
|
|
return nil;
|
2008-09-17 06:11:39 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// Convert from cocoa's coordinate system to gecko's. According to the docs
|
|
|
|
// the point we're given is guaranteed to be bottom-left screen coordinates.
|
|
|
|
nsPoint geckoPoint;
|
|
|
|
ConvertCocoaToGeckoPoint (point, geckoPoint);
|
|
|
|
|
2008-09-17 06:11:39 -07:00
|
|
|
nsCOMPtr<nsIAccessible> deepestFoundChild;
|
|
|
|
mGeckoAccessible->GetDeepestChildAtPoint((PRInt32)geckoPoint.x,
|
|
|
|
(PRInt32)geckoPoint.y,
|
|
|
|
getter_AddRefs(deepestFoundChild));
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// if we found something, return its native accessible.
|
|
|
|
if (deepestFoundChild) {
|
|
|
|
mozAccessible *nativeChild = GetNativeFromGeckoAccessible(deepestFoundChild);
|
|
|
|
if (nativeChild)
|
|
|
|
return GetClosestInterestingAccessible(nativeChild);
|
|
|
|
}
|
|
|
|
|
|
|
|
// if we didn't find anything, return ourself (or the first unignored ancestor).
|
|
|
|
return GetClosestInterestingAccessible(self);
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSArray*)accessibilityActionNames
|
|
|
|
{
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString*)accessibilityActionDescription:(NSString*)action
|
|
|
|
{
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)accessibilityPerformAction:(NSString*)action
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id)accessibilityFocusedUIElement
|
|
|
|
{
|
|
|
|
if (mIsExpired)
|
|
|
|
return nil;
|
|
|
|
|
2011-07-15 15:58:49 -07:00
|
|
|
nsAccessible* focusedGeckoChild = mGeckoAccessible->FocusedChild();
|
2007-03-22 10:30:00 -07:00
|
|
|
if (focusedGeckoChild) {
|
|
|
|
mozAccessible *focusedChild = GetNativeFromGeckoAccessible(focusedGeckoChild);
|
|
|
|
if (focusedChild)
|
|
|
|
return GetClosestInterestingAccessible(focusedChild);
|
|
|
|
}
|
|
|
|
|
|
|
|
// return ourself if we can't get a native focused child.
|
|
|
|
return GetClosestInterestingAccessible(self);
|
|
|
|
}
|
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
|
|
|
|
- (id <mozAccessible>)parent
|
|
|
|
{
|
2008-02-22 12:13:17 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
nsCOMPtr<nsIAccessible> accessibleParent(mGeckoAccessible->GetUnignoredParent());
|
|
|
|
if (accessibleParent) {
|
|
|
|
id nativeParent = GetNativeFromGeckoAccessible(accessibleParent);
|
|
|
|
if (nativeParent)
|
|
|
|
return GetClosestInterestingAccessible(nativeParent);
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetUnignoredParent() returns null when there is no unignored accessible all the way up to
|
|
|
|
// the root accessible. so we'll have to return whatever native accessible is above our root accessible
|
|
|
|
// (which might be the owning NSWindow in the application, for example).
|
|
|
|
//
|
|
|
|
// get the native root accessible, and tell it to return its first parent unignored accessible.
|
2011-03-01 21:16:28 -08:00
|
|
|
nsRootAccessible* root = mGeckoAccessible->RootAccessible();
|
2007-07-08 00:08:04 -07:00
|
|
|
id nativeParent = GetNativeFromGeckoAccessible(static_cast<nsIAccessible*>(root));
|
2007-03-22 10:30:00 -07:00
|
|
|
NSAssert1 (nativeParent, @"!!! we can't find a parent for %@", self);
|
|
|
|
|
|
|
|
return GetClosestInterestingAccessible(nativeParent);
|
2008-02-22 12:13:17 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)hasRepresentedView
|
|
|
|
{
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id)representedView
|
|
|
|
{
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)isRoot
|
|
|
|
{
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
// gets our native children lazily.
|
|
|
|
// returns nil when there are no children.
|
|
|
|
- (NSArray*)children
|
|
|
|
{
|
2008-02-22 12:13:17 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
|
|
|
|
2011-12-05 12:33:30 -08:00
|
|
|
if (mChildren || !mGeckoAccessible->AreChildrenCached())
|
2007-03-22 10:30:00 -07:00
|
|
|
return mChildren;
|
2011-12-05 12:33:30 -08:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
mChildren = [[NSMutableArray alloc] init];
|
|
|
|
|
|
|
|
// get the array of children.
|
|
|
|
nsTArray<nsRefPtr<nsAccessibleWrap> > childrenArray;
|
|
|
|
mGeckoAccessible->GetUnignoredChildren(childrenArray);
|
|
|
|
|
|
|
|
// now iterate through the children array, and get each native accessible.
|
|
|
|
int totalCount = childrenArray.Length();
|
|
|
|
int index = 0;
|
|
|
|
|
|
|
|
for (; index < totalCount; index++) {
|
|
|
|
nsAccessibleWrap *curAccessible = childrenArray.ElementAt(index);
|
|
|
|
if (curAccessible) {
|
|
|
|
mozAccessible *curNative = GetNativeFromGeckoAccessible(curAccessible);
|
|
|
|
if (curNative)
|
|
|
|
[mChildren addObject:GetObjectOrRepresentedView(curNative)];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DEBUG_hakan
|
|
|
|
// make sure we're not returning any ignored accessibles.
|
|
|
|
NSEnumerator *e = [mChildren objectEnumerator];
|
|
|
|
mozAccessible *m = nil;
|
|
|
|
while ((m = [e nextObject])) {
|
|
|
|
NSAssert1(![m accessibilityIsIgnored], @"we should never return an ignored accessible! (%@)", m);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return mChildren;
|
2008-02-22 12:13:17 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSValue*)position
|
|
|
|
{
|
2008-02-22 12:13:17 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
PRInt32 x, y, width, height;
|
|
|
|
mGeckoAccessible->GetBounds (&x, &y, &width, &height);
|
|
|
|
NSPoint p = NSMakePoint (x, y);
|
|
|
|
|
|
|
|
// The coords we get from Gecko are top-left screen coordinates.
|
|
|
|
// Cocoa wants us to return bottom-left screen coordinates.
|
|
|
|
// This involves two steps:
|
|
|
|
// 1. Put the rect in the bottom-left coord space
|
|
|
|
// 2. Subtract the height of the rect's Y-coordinate, to make the
|
|
|
|
// the rect's origin (0, 0) be in the bottom-left corner.
|
|
|
|
|
|
|
|
float mainScreenHeight = [[[NSScreen screens] objectAtIndex:0] frame].size.height;
|
|
|
|
p.y = mainScreenHeight - p.y - height;
|
|
|
|
|
|
|
|
return [NSValue valueWithPoint:p];
|
2008-02-22 12:13:17 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSValue*)size
|
|
|
|
{
|
2008-02-22 12:13:17 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
PRInt32 x, y, width, height;
|
|
|
|
mGeckoAccessible->GetBounds (&x, &y, &width, &height);
|
|
|
|
return [NSValue valueWithSize:NSMakeSize (width, height)];
|
2008-02-22 12:13:17 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString*)role
|
|
|
|
{
|
|
|
|
#ifdef DEBUG_A11Y
|
2008-10-17 03:10:43 -07:00
|
|
|
NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(mGeckoAccessible),
|
|
|
|
"Does not support nsIAccessibleText when it should");
|
2007-03-22 10:30:00 -07:00
|
|
|
#endif
|
2011-05-17 07:00:30 -07:00
|
|
|
return (NSString*) AXRoles[mRole];
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString*)subrole
|
|
|
|
{
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString*)title
|
|
|
|
{
|
2008-02-22 12:13:17 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
nsAutoString title;
|
|
|
|
mGeckoAccessible->GetName (title);
|
|
|
|
return title.IsEmpty() ? nil : [NSString stringWithCharacters:title.BeginReading() length:title.Length()];
|
2008-02-22 12:13:17 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
- (id)value
|
|
|
|
{
|
2008-02-22 12:13:17 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
nsAutoString value;
|
|
|
|
mGeckoAccessible->GetValue (value);
|
|
|
|
return value.IsEmpty() ? nil : [NSString stringWithCharacters:value.BeginReading() length:value.Length()];
|
2008-02-22 12:13:17 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)valueDidChange
|
|
|
|
{
|
2008-02-22 12:13:17 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
#ifdef DEBUG_hakan
|
|
|
|
NSLog(@"%@'s value changed!", self);
|
|
|
|
#endif
|
|
|
|
// sending out a notification is expensive, so we don't do it other than for really important objects,
|
|
|
|
// like mozTextAccessible.
|
2008-02-22 12:13:17 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString*)customDescription
|
|
|
|
{
|
2008-02-22 12:13:17 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
|
|
|
|
2011-04-23 06:14:05 -07:00
|
|
|
if (mGeckoAccessible->IsDefunct())
|
|
|
|
return nil;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
nsAutoString desc;
|
2011-04-23 06:14:05 -07:00
|
|
|
mGeckoAccessible->Description(desc);
|
2007-03-22 10:30:00 -07:00
|
|
|
return desc.IsEmpty() ? nil : [NSString stringWithCharacters:desc.BeginReading() length:desc.Length()];
|
2008-02-22 12:13:17 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString*)help
|
|
|
|
{
|
2008-02-22 12:13:17 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
nsAutoString helpText;
|
|
|
|
mGeckoAccessible->GetHelp (helpText);
|
|
|
|
return helpText.IsEmpty() ? nil : [NSString stringWithCharacters:helpText.BeginReading() length:helpText.Length()];
|
2008-02-22 12:13:17 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// objc-style description (from NSObject); not to be confused with the accessible description above.
|
|
|
|
- (NSString*)description
|
|
|
|
{
|
2008-02-22 12:13:17 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
return [NSString stringWithFormat:@"(%p) %@", self, [self role]];
|
2008-02-22 12:13:17 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)isFocused
|
|
|
|
{
|
2011-04-09 16:38:06 -07:00
|
|
|
return (mGeckoAccessible->State() & states::FOCUSED) != 0;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)canBeFocused
|
|
|
|
{
|
2011-04-09 16:38:06 -07:00
|
|
|
return mGeckoAccessible->State() & states::FOCUSABLE;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)focus
|
|
|
|
{
|
|
|
|
nsresult rv = mGeckoAccessible->TakeFocus();
|
|
|
|
return NS_SUCCEEDED(rv);
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)isEnabled
|
|
|
|
{
|
2011-04-09 16:38:06 -07:00
|
|
|
return (mGeckoAccessible->State() & states::UNAVAILABLE) == 0;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// The root accessible calls this when the focused node was
|
|
|
|
// changed to us.
|
|
|
|
- (void)didReceiveFocus
|
|
|
|
{
|
2008-02-22 12:13:17 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
#ifdef DEBUG_hakan
|
|
|
|
NSLog (@"%@ received focus!", self);
|
|
|
|
#endif
|
|
|
|
NSAssert1(![self accessibilityIsIgnored], @"trying to set focus to ignored element! (%@)", self);
|
|
|
|
NSAccessibilityPostNotification(GetObjectOrRepresentedView(self),
|
|
|
|
NSAccessibilityFocusedUIElementChangedNotification);
|
2008-02-22 12:13:17 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSWindow*)window
|
|
|
|
{
|
2008-02-22 12:13:17 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
|
|
|
|
2007-07-08 00:08:04 -07:00
|
|
|
nsAccessibleWrap *accWrap = static_cast<nsAccessibleWrap*>(mGeckoAccessible);
|
2010-09-16 20:23:17 -07:00
|
|
|
|
|
|
|
// Get a pointer to the native window (NSWindow) we reside in.
|
2007-03-22 10:30:00 -07:00
|
|
|
NSWindow *nativeWindow = nil;
|
2010-09-16 20:23:17 -07:00
|
|
|
nsDocAccessible* docAcc = accWrap->GetDocAccessible();
|
|
|
|
if (docAcc)
|
|
|
|
nativeWindow = static_cast<NSWindow*>(docAcc->GetNativeWindow());
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
NSAssert1(nativeWindow, @"Could not get native window for %@", self);
|
|
|
|
return nativeWindow;
|
2008-02-22 12:13:17 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)invalidateChildren
|
|
|
|
{
|
2008-02-22 12:13:17 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// make room for new children
|
|
|
|
[mChildren release];
|
|
|
|
mChildren = nil;
|
2008-02-22 12:13:17 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)expire
|
|
|
|
{
|
2008-02-22 12:13:17 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
[self invalidateChildren];
|
|
|
|
mIsExpired = YES;
|
2008-02-22 12:13:17 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)isExpired
|
|
|
|
{
|
|
|
|
return mIsExpired;
|
|
|
|
}
|
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
#pragma mark Debug methods
|
|
|
|
#pragma mark -
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
|
|
|
|
// will check that our children actually reference us as their
|
|
|
|
// parent.
|
|
|
|
- (void)sanityCheckChildren:(NSArray *)children
|
|
|
|
{
|
2008-02-22 12:13:17 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
NSAssert(![self accessibilityIsIgnored], @"can't sanity check children of an ignored accessible!");
|
|
|
|
NSEnumerator *iter = [children objectEnumerator];
|
|
|
|
mozAccessible *curObj = nil;
|
|
|
|
|
|
|
|
NSLog(@"sanity checking %@", self);
|
|
|
|
|
|
|
|
while ((curObj = [iter nextObject])) {
|
|
|
|
id realSelf = GetObjectOrRepresentedView(self);
|
|
|
|
NSLog(@"checking %@", realSelf);
|
|
|
|
NSAssert2([curObj parent] == realSelf,
|
|
|
|
@"!!! %@ not returning %@ as AXParent, even though it is a AXChild of it!", curObj, realSelf);
|
|
|
|
}
|
2008-02-22 12:13:17 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)sanityCheckChildren
|
|
|
|
{
|
2008-02-22 12:13:17 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
[self sanityCheckChildren:[self children]];
|
2008-02-22 12:13:17 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)printHierarchy
|
|
|
|
{
|
2008-02-22 12:13:17 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
[self printHierarchyWithLevel:0];
|
2008-02-22 12:13:17 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)printHierarchyWithLevel:(unsigned)level
|
|
|
|
{
|
2008-02-22 12:13:17 -08:00
|
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
NSAssert(![self isExpired], @"!!! trying to print hierarchy of expired object!");
|
|
|
|
|
|
|
|
// print this node
|
|
|
|
NSMutableString *indent = [NSMutableString stringWithCapacity:level];
|
|
|
|
unsigned i=0;
|
|
|
|
for (;i<level;i++)
|
|
|
|
[indent appendString:@" "];
|
|
|
|
|
|
|
|
NSLog (@"%@(#%i) %@", indent, level, self);
|
|
|
|
|
|
|
|
// use |children| method to make sure our children are lazily fetched first.
|
|
|
|
NSArray *children = [self children];
|
|
|
|
if (!children)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (![self accessibilityIsIgnored])
|
|
|
|
[self sanityCheckChildren];
|
|
|
|
|
|
|
|
NSEnumerator *iter = [children objectEnumerator];
|
|
|
|
mozAccessible *object = nil;
|
|
|
|
|
|
|
|
while (iter && (object = [iter nextObject]))
|
|
|
|
// print every child node's subtree, increasing the indenting
|
|
|
|
// by two for every level.
|
|
|
|
[object printHierarchyWithLevel:(level+1)];
|
2008-02-22 12:13:17 -08:00
|
|
|
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* DEBUG */
|
|
|
|
|
|
|
|
@end
|