Make deep hit-testing work. This makes VoiceOver able to speak whatever you mouse over, both in XUL and web page content (if that pref is set in the accessibility settings)

r=surkov, b=352329
This commit is contained in:
hwaara%gmail.com 2006-12-01 10:53:52 +00:00
Родитель c52e7c43eb
Коммит 935b7af395
6 изменённых файлов: 106 добавлений и 63 удалений

Просмотреть файл

@ -122,6 +122,9 @@
#ifdef DEBUG
- (void)printHierarchy;
- (void)printHierarchyWithLevel:(unsigned)numSpaces;
- (void)sanityCheckChildren;
- (void)sanityCheckChildren:(NSArray *)theChildren;
#endif
// ---- NSAccessibility methods ---- //

Просмотреть файл

@ -49,6 +49,8 @@
#include "nsIAccessibleText.h"
#include "nsIAccessibleEditableText.h"
#include "nsRootAccessible.h"
// 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
@ -65,32 +67,50 @@ ConvertCocoaToGeckoPoint(NSPoint &aInPoint, nsPoint &aOutPoint)
aOutPoint.MoveTo ((nscoord)aInPoint.x, (nscoord)(mainScreenHeight - aInPoint.y));
}
// returns an object if it is not ignored. if it's ignored, will return
// the first unignored ancestor.
static inline id
ObjectOrUnignoredAncestor(id anObject)
// 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)
{
if ([anObject accessibilityIsIgnored])
return NSAccessibilityUnignoredAncestor(anObject);
if ([anObject hasRepresentedView])
return [anObject representedView];
return anObject;
}
// 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)
{
// 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;
}
static inline mozAccessible*
NativeFromGeckoAccessible(nsIAccessible *anAccessible)
GetNativeFromGeckoAccessible(nsIAccessible *anAccessible)
{
mozAccessible *native = nil;
anAccessible->GetNativeInterface ((void**)&native);
return [native hasRepresentedView] ? [native representedView] : native;
return native;
}
#pragma mark -
@interface mozAccessible (Private)
#ifdef DEBUG
- (void)sanityCheckChildren:(NSArray*)theChildren;
#endif
@end
#pragma mark -
@implementation mozAccessible
@ -233,20 +253,26 @@ NativeFromGeckoAccessible(nsIAccessible *anAccessible)
// the point we're given is guaranteed to be bottom-left screen coordinates.
nsPoint geckoPoint;
ConvertCocoaToGeckoPoint (point, geckoPoint);
// start iterating as deep down as we can on this point, with the current accessible as the root.
// as soon as GetChildAtPoint() returns null, or can't descend further (without getting the same accessible again)
// we stop.
nsCOMPtr<nsIAccessible> deepestFoundChild, newChild(mGeckoAccessible);
do {
deepestFoundChild = newChild;
deepestFoundChild->GetChildAtPoint((PRInt32)geckoPoint.x, (PRInt32)geckoPoint.y, getter_AddRefs(newChild));
} while (newChild && newChild.get() != deepestFoundChild.get());
// see if we can find an accessible at that point.
nsCOMPtr<nsIAccessible> foundChild;
mGeckoAccessible->GetChildAtPoint ((PRInt32)geckoPoint.x, (PRInt32)geckoPoint.y, getter_AddRefs (foundChild));
if (foundChild) {
// if we found something, return its native accessible.
mozAccessible *nativeChild = NativeFromGeckoAccessible(foundChild);
// if we found something, return its native accessible.
if (deepestFoundChild) {
mozAccessible *nativeChild = GetNativeFromGeckoAccessible(deepestFoundChild);
if (nativeChild)
return ObjectOrUnignoredAncestor (nativeChild);
return GetClosestInterestingAccessible(nativeChild);
}
// if we didn't find anything, return ourself (or the first unignored ancestor).
return ObjectOrUnignoredAncestor ([self hasRepresentedView] ? [self representedView] : self);
return GetClosestInterestingAccessible(self);
}
- (NSArray*)accessibilityActionNames
@ -272,13 +298,13 @@ NativeFromGeckoAccessible(nsIAccessible *anAccessible)
mGeckoAccessible->GetFocusedChild (getter_AddRefs (focusedGeckoChild));
if (focusedGeckoChild) {
mozAccessible *focusedChild = NativeFromGeckoAccessible(focusedGeckoChild);
mozAccessible *focusedChild = GetNativeFromGeckoAccessible(focusedGeckoChild);
if (focusedChild)
return ObjectOrUnignoredAncestor(focusedChild);
return GetClosestInterestingAccessible(focusedChild);
}
// return ourself if we can't get a native focused child.
return ObjectOrUnignoredAncestor([self hasRepresentedView] ? [self representedView] : self);
return GetClosestInterestingAccessible(self);
}
#pragma mark -
@ -287,26 +313,21 @@ NativeFromGeckoAccessible(nsIAccessible *anAccessible)
{
nsCOMPtr<nsIAccessible> accessibleParent(mGeckoAccessible->GetUnignoredParent());
if (accessibleParent) {
id nativeParent = NativeFromGeckoAccessible(accessibleParent);
if (nativeParent) {
nativeParent = ObjectOrUnignoredAncestor(nativeParent);
NSAssert(![nativeParent accessibilityIsIgnored], @"returned parent should not be ignored!");
// if the object doesn't have a representedView method, we might have the NSWindow or any kind of
// parent object far up in the hierarchy.
if ([nativeParent respondsToSelector:@selector(hasRepresentedView)] &&
[nativeParent hasRepresentedView])
return [nativeParent representedView];
return nativeParent;
}
id nativeParent = GetNativeFromGeckoAccessible(accessibleParent);
if (nativeParent)
return GetClosestInterestingAccessible(nativeParent);
}
#ifdef DEBUG
NSLog (@"!!! we can't find a parent for %@", self);
#endif
// we didn't find a parent
return nil;
// 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.
nsRefPtr<nsRootAccessible> root(mGeckoAccessible->GetRootAccessible());
id nativeParent = GetNativeFromGeckoAccessible(NS_STATIC_CAST(nsIAccessible*, root));
NSAssert1 (nativeParent, @"!!! we can't find a parent for %@", self);
return GetClosestInterestingAccessible(nativeParent);
}
- (BOOL)hasRepresentedView
@ -330,9 +351,8 @@ NativeFromGeckoAccessible(nsIAccessible *anAccessible)
{
if (mChildren)
return mChildren;
if (!mChildren)
mChildren = [[NSMutableArray alloc] init];
mChildren = [[NSMutableArray alloc] init];
// get the array of children.
nsTArray<nsRefPtr<nsAccessibleWrap> > childrenArray;
@ -345,9 +365,9 @@ NativeFromGeckoAccessible(nsIAccessible *anAccessible)
for (; index < totalCount; index++) {
nsAccessibleWrap *curAccessible = childrenArray.ElementAt(index);
if (curAccessible) {
mozAccessible *curNative = NativeFromGeckoAccessible(curAccessible);
mozAccessible *curNative = GetNativeFromGeckoAccessible(curAccessible);
if (curNative)
[mChildren addObject:curNative];
[mChildren addObject:GetObjectOrRepresentedView(curNative)];
}
}
@ -471,7 +491,7 @@ NativeFromGeckoAccessible(nsIAccessible *anAccessible)
NSLog (@"%@ received focus!", self);
#endif
NSAssert1(![self accessibilityIsIgnored], @"trying to set focus to ignored element! (%@)", self);
NSAccessibilityPostNotification([self hasRepresentedView] ? [self representedView] : self,
NSAccessibilityPostNotification(GetObjectOrRepresentedView(self),
NSAccessibilityFocusedUIElementChangedNotification);
}
@ -511,15 +531,25 @@ NativeFromGeckoAccessible(nsIAccessible *anAccessible)
// will check that our children actually reference us as their
// parent.
- (void)sanityCheckChildren
- (void)sanityCheckChildren:(NSArray *)children
{
NSAssert(![self accessibilityIsIgnored], @"can't sanity check children of an ignored accessible!");
NSEnumerator *iter = [[self children] objectEnumerator];
NSEnumerator *iter = [children objectEnumerator];
mozAccessible *curObj = nil;
while ((curObj = [iter nextObject]))
NSAssert2([curObj parent] == self,
@"!!! %@ not returning %@ as AXParent, even though it is a AXChild of it!", curObj, self);
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);
}
}
- (void)sanityCheckChildren
{
[self sanityCheckChildren:[self children]];
}
- (void)printHierarchy

Просмотреть файл

@ -70,4 +70,8 @@ struct AccessibleWrapper {
mozAccessible* getNativeObject () {
return object;
}
PRBool isIgnored () {
return (PRBool)[object accessibilityIsIgnored];
}
};

Просмотреть файл

@ -80,7 +80,7 @@ static id <mozAccessible, mozView> getNativeViewFromRootAccessible (nsAccessible
mParallelView = getNativeViewFromRootAccessible (mGeckoAccessible);
NSAssert(mParallelView, @" can't return root accessible's native parallel view.");
NSAssert(mParallelView, @"can't return root accessible's native parallel view.");
return mParallelView;
}

Просмотреть файл

@ -89,11 +89,9 @@ class nsAccessibleWrap : public nsAccessible
role == ROLE_AUTOCOMPLETE);
}
// if we for some reason have no native accessible, we should be skipped over (and traversed)
// when fetching all unignored children, etc. when counting unignored children, we will not be counted.
PRBool IsIgnored() {
return (mNativeWrapper == nsnull);
}
// ignored means that the accessible might still have children, but is not displayed
// to the user. it also has no native accessible object represented for it.
PRBool IsIgnored();
PRInt32 GetUnignoredChildCount(PRBool aDeepCount);

Просмотреть файл

@ -188,6 +188,14 @@ nsAccessibleWrap::GetUnignoredChildCount(PRBool aDeepCount)
return childCount;
}
// if we for some reason have no native accessible, we should be skipped over (and traversed)
// when fetching all unignored children, etc. when counting unignored children, we will not be counted.
PRBool
nsAccessibleWrap::IsIgnored()
{
return (mNativeWrapper == nsnull) || mNativeWrapper->isIgnored();
}
void
nsAccessibleWrap::GetUnignoredChildren(nsTArray<nsRefPtr<nsAccessibleWrap> > &aChildrenArray)
{