mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 21:31:04 +00:00
d381b4bca6
CLOSED TREE Backed out changeset d66c3f19a210 (bug 1235261) Backed out changeset 467d945426bb (bug 1235261) Backed out changeset 32b61df13142 (bug 1235261) Backed out changeset c50bb8ed4196 (bug 1235261) Backed out changeset 0ff0fa6fe81f (bug 1235261) Backed out changeset df70e89669da (bug 1235261) Backed out changeset 064969357fc9 (bug 1235261)
1296 lines
40 KiB
Plaintext
1296 lines
40 KiB
Plaintext
/* -*- Mode: Objective-C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#import "mozAccessible.h"
|
|
|
|
#import "MacUtils.h"
|
|
#import "mozView.h"
|
|
|
|
#include "Accessible-inl.h"
|
|
#include "nsAccUtils.h"
|
|
#include "nsIAccessibleRelation.h"
|
|
#include "nsIAccessibleEditableText.h"
|
|
#include "nsIPersistentProperties2.h"
|
|
#include "Relation.h"
|
|
#include "Role.h"
|
|
#include "RootAccessible.h"
|
|
#include "TableAccessible.h"
|
|
#include "TableCellAccessible.h"
|
|
#include "mozilla/a11y/PDocAccessible.h"
|
|
|
|
#include "mozilla/Services.h"
|
|
#include "nsRect.h"
|
|
#include "nsCocoaUtils.h"
|
|
#include "nsCoord.h"
|
|
#include "nsObjCExceptions.h"
|
|
#include "nsWhitespaceTokenizer.h"
|
|
#include <prdtoa.h>
|
|
|
|
using namespace mozilla;
|
|
using namespace mozilla::a11y;
|
|
|
|
#define NSAccessibilityMathRootRadicandAttribute @"AXMathRootRadicand"
|
|
#define NSAccessibilityMathRootIndexAttribute @"AXMathRootIndex"
|
|
#define NSAccessibilityMathFractionNumeratorAttribute @"AXMathFractionNumerator"
|
|
#define NSAccessibilityMathFractionDenominatorAttribute @"AXMathFractionDenominator"
|
|
#define NSAccessibilityMathBaseAttribute @"AXMathBase"
|
|
#define NSAccessibilityMathSubscriptAttribute @"AXMathSubscript"
|
|
#define NSAccessibilityMathSuperscriptAttribute @"AXMathSuperscript"
|
|
#define NSAccessibilityMathUnderAttribute @"AXMathUnder"
|
|
#define NSAccessibilityMathOverAttribute @"AXMathOver"
|
|
#define NSAccessibilityMathLineThicknessAttribute @"AXMathLineThickness"
|
|
// XXX WebKit also defines the following attributes.
|
|
// See bugs 1176970 and 1176983.
|
|
// - NSAccessibilityMathFencedOpenAttribute @"AXMathFencedOpen"
|
|
// - NSAccessibilityMathFencedCloseAttribute @"AXMathFencedClose"
|
|
// - NSAccessibilityMathPrescriptsAttribute @"AXMathPrescripts"
|
|
// - NSAccessibilityMathPostscriptsAttribute @"AXMathPostscripts"
|
|
|
|
// 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)
|
|
{
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
|
|
|
// 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;
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
|
}
|
|
|
|
ProxyAccessible*
|
|
a11y::GetProxyUnignoredParent(const ProxyAccessible* aProxy)
|
|
{
|
|
ProxyAccessible* parent = aProxy->Parent();
|
|
while (parent && IsProxyIgnored(aProxy))
|
|
parent = parent->Parent();
|
|
|
|
return parent;
|
|
}
|
|
|
|
void
|
|
a11y::GetProxyUnignoredChildren(const ProxyAccessible* aProxy,
|
|
nsTArray<ProxyAccessible*>* aChildrenArray)
|
|
{
|
|
if (aProxy->MustPruneChildren())
|
|
return;
|
|
|
|
uint32_t childCount = aProxy->ChildrenCount();
|
|
for (size_t childIdx = 0; childIdx < childCount; childIdx++) {
|
|
ProxyAccessible* childProxy = aProxy->ChildAt(childIdx);
|
|
|
|
// If element is ignored, then add its children as substitutes.
|
|
if (IsProxyIgnored(childProxy)) {
|
|
GetProxyUnignoredChildren(aProxy, aChildrenArray);
|
|
continue;
|
|
}
|
|
|
|
aChildrenArray->AppendElement(childProxy);
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
a11y::IsProxyIgnored(const ProxyAccessible* aProxy)
|
|
{
|
|
mozAccessible* nativeObject = GetNativeFromProxy(aProxy);
|
|
if (!nativeObject)
|
|
return true;
|
|
|
|
return [nativeObject accessibilityIsIgnored];
|
|
}
|
|
|
|
// convert an array of Gecko accessibles to an NSArray of native accessibles
|
|
static inline NSMutableArray*
|
|
ConvertToNSArray(nsTArray<Accessible*>& aArray)
|
|
{
|
|
NSMutableArray* nativeArray = [[NSMutableArray alloc] init];
|
|
|
|
// iterate through the list, and get each native accessible.
|
|
size_t totalCount = aArray.Length();
|
|
for (size_t i = 0; i < totalCount; i++) {
|
|
Accessible* curAccessible = aArray.ElementAt(i);
|
|
mozAccessible* curNative = GetNativeFromGeckoAccessible(curAccessible);
|
|
if (curNative)
|
|
[nativeArray addObject:GetObjectOrRepresentedView(curNative)];
|
|
}
|
|
|
|
return nativeArray;
|
|
}
|
|
|
|
// convert an array of Gecko proxy accessibles to an NSArray of native accessibles
|
|
static inline NSMutableArray*
|
|
ConvertToNSArray(nsTArray<ProxyAccessible*>& aArray)
|
|
{
|
|
NSMutableArray* nativeArray = [[NSMutableArray alloc] init];
|
|
|
|
// iterate through the list, and get each native accessible.
|
|
size_t totalCount = aArray.Length();
|
|
for (size_t i = 0; i < totalCount; i++) {
|
|
ProxyAccessible* curAccessible = aArray.ElementAt(i);
|
|
mozAccessible* curNative = GetNativeFromProxy(curAccessible);
|
|
if (curNative)
|
|
[nativeArray addObject:GetObjectOrRepresentedView(curNative)];
|
|
}
|
|
|
|
return nativeArray;
|
|
}
|
|
|
|
#pragma mark -
|
|
|
|
@implementation mozAccessible
|
|
|
|
- (id)initWithAccessible:(uintptr_t)aGeckoAccessible
|
|
{
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
|
|
|
if ((self = [super init])) {
|
|
mGeckoAccessible = aGeckoAccessible;
|
|
if (aGeckoAccessible & IS_PROXY)
|
|
mRole = [self getProxyAccessible]->Role();
|
|
else
|
|
mRole = [self getGeckoAccessible]->Role();
|
|
}
|
|
|
|
return self;
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
|
}
|
|
|
|
- (void)dealloc
|
|
{
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
[mChildren release];
|
|
[super dealloc];
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
|
}
|
|
|
|
- (mozilla::a11y::AccessibleWrap*)getGeckoAccessible
|
|
{
|
|
// Check if mGeckoAccessible points at a proxy
|
|
if (mGeckoAccessible & IS_PROXY)
|
|
return nil;
|
|
|
|
return reinterpret_cast<AccessibleWrap*>(mGeckoAccessible);
|
|
}
|
|
|
|
- (mozilla::a11y::ProxyAccessible*)getProxyAccessible
|
|
{
|
|
// Check if mGeckoAccessible points at a proxy
|
|
if (!(mGeckoAccessible & IS_PROXY))
|
|
return nil;
|
|
|
|
return reinterpret_cast<ProxyAccessible*>(mGeckoAccessible & ~IS_PROXY);
|
|
}
|
|
|
|
#pragma mark -
|
|
|
|
- (BOOL)accessibilityIsIgnored
|
|
{
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
|
|
|
|
// unknown (either unimplemented, or irrelevant) elements are marked as ignored
|
|
// as well as expired elements.
|
|
|
|
bool noRole = [[self role] isEqualToString:NSAccessibilityUnknownRole];
|
|
if (AccessibleWrap* accWrap = [self getGeckoAccessible])
|
|
return (noRole && !(accWrap->InteractiveState() & states::FOCUSABLE));
|
|
|
|
if (ProxyAccessible* proxy = [self getProxyAccessible])
|
|
return (noRole && !(proxy->State() & states::FOCUSABLE));
|
|
|
|
return true;
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
|
|
}
|
|
|
|
- (NSArray*)additionalAccessibilityAttributeNames
|
|
{
|
|
NSMutableArray* additional = [NSMutableArray array];
|
|
switch (mRole) {
|
|
case roles::MATHML_ROOT:
|
|
[additional addObject:NSAccessibilityMathRootIndexAttribute];
|
|
[additional addObject:NSAccessibilityMathRootRadicandAttribute];
|
|
break;
|
|
case roles::MATHML_SQUARE_ROOT:
|
|
[additional addObject:NSAccessibilityMathRootRadicandAttribute];
|
|
break;
|
|
case roles::MATHML_FRACTION:
|
|
[additional addObject:NSAccessibilityMathFractionNumeratorAttribute];
|
|
[additional addObject:NSAccessibilityMathFractionDenominatorAttribute];
|
|
[additional addObject:NSAccessibilityMathLineThicknessAttribute];
|
|
break;
|
|
case roles::MATHML_SUB:
|
|
case roles::MATHML_SUP:
|
|
case roles::MATHML_SUB_SUP:
|
|
[additional addObject:NSAccessibilityMathBaseAttribute];
|
|
[additional addObject:NSAccessibilityMathSubscriptAttribute];
|
|
[additional addObject:NSAccessibilityMathSuperscriptAttribute];
|
|
break;
|
|
case roles::MATHML_UNDER:
|
|
case roles::MATHML_OVER:
|
|
case roles::MATHML_UNDER_OVER:
|
|
[additional addObject:NSAccessibilityMathBaseAttribute];
|
|
[additional addObject:NSAccessibilityMathUnderAttribute];
|
|
[additional addObject:NSAccessibilityMathOverAttribute];
|
|
break;
|
|
// XXX bug 1176983
|
|
// roles::MATHML_MULTISCRIPTS should also have the following attributes:
|
|
// - NSAccessibilityMathPrescriptsAttribute
|
|
// - NSAccessibilityMathPostscriptsAttribute
|
|
// XXX bug 1176970
|
|
// roles::MATHML_FENCED should also have the following attributes:
|
|
// - NSAccessibilityMathFencedOpenAttribute
|
|
// - NSAccessibilityMathFencedCloseAttribute
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return additional;
|
|
}
|
|
|
|
- (NSArray*)accessibilityAttributeNames
|
|
{
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
|
|
|
// if we're expired, we don't support any attributes.
|
|
AccessibleWrap* accWrap = [self getGeckoAccessible];
|
|
ProxyAccessible* proxy = [self getProxyAccessible];
|
|
if (!accWrap && !proxy)
|
|
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,
|
|
NSAccessibilityTopLevelUIElementAttribute,
|
|
#if DEBUG
|
|
@"AXMozDescription",
|
|
#endif
|
|
nil];
|
|
}
|
|
|
|
NSArray* objectAttributes = generalAttributes;
|
|
|
|
NSArray* additionalAttributes = [self additionalAccessibilityAttributeNames];
|
|
if ([additionalAttributes count])
|
|
objectAttributes = [objectAttributes arrayByAddingObjectsFromArray:additionalAttributes];
|
|
|
|
return objectAttributes;
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
|
}
|
|
|
|
- (id)childAt:(uint32_t)i
|
|
{
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
|
|
|
if (AccessibleWrap* accWrap = [self getGeckoAccessible]) {
|
|
Accessible* child = accWrap->GetChildAt(i);
|
|
return child ? GetNativeFromGeckoAccessible(child) : nil;
|
|
} else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
|
|
ProxyAccessible* child = proxy->ChildAt(i);
|
|
return child ? GetNativeFromProxy(child) : nil;
|
|
}
|
|
|
|
return nil;
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
|
}
|
|
|
|
- (id)accessibilityAttributeValue:(NSString*)attribute
|
|
{
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
|
|
|
AccessibleWrap* accWrap = [self getGeckoAccessible];
|
|
ProxyAccessible* proxy = [self getProxyAccessible];
|
|
if (!accWrap && !proxy)
|
|
return nil;
|
|
|
|
#if DEBUG
|
|
if ([attribute isEqualToString:@"AXMozDescription"])
|
|
return [NSString stringWithFormat:@"role = %u native = %@", mRole, [self class]];
|
|
#endif
|
|
|
|
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 [self roleDescription];
|
|
if ([attribute isEqualToString:NSAccessibilityFocusedAttribute])
|
|
return [NSNumber numberWithBool:[self isFocused]];
|
|
if ([attribute isEqualToString:NSAccessibilitySizeAttribute])
|
|
return [self size];
|
|
if ([attribute isEqualToString:NSAccessibilityWindowAttribute])
|
|
return [self window];
|
|
if ([attribute isEqualToString:NSAccessibilityTopLevelUIElementAttribute])
|
|
return [self window];
|
|
if ([attribute isEqualToString:NSAccessibilityTitleAttribute])
|
|
return [self title];
|
|
if ([attribute isEqualToString:NSAccessibilityTitleUIElementAttribute]) {
|
|
if (accWrap) {
|
|
Relation rel = accWrap->RelationByType(RelationType::LABELLED_BY);
|
|
Accessible* tempAcc = rel.Next();
|
|
return tempAcc ? GetNativeFromGeckoAccessible(tempAcc) : nil;
|
|
}
|
|
nsTArray<ProxyAccessible*> rel = proxy->RelationByType(RelationType::LABELLED_BY);
|
|
ProxyAccessible* tempProxy = rel.SafeElementAt(0);
|
|
return tempProxy ? GetNativeFromProxy(tempProxy) : nil;
|
|
}
|
|
if ([attribute isEqualToString:NSAccessibilityHelpAttribute])
|
|
return [self help];
|
|
|
|
switch (mRole) {
|
|
case roles::MATHML_ROOT:
|
|
if ([attribute isEqualToString:NSAccessibilityMathRootRadicandAttribute])
|
|
return [self childAt:0];
|
|
if ([attribute isEqualToString:NSAccessibilityMathRootIndexAttribute])
|
|
return [self childAt:1];
|
|
break;
|
|
case roles::MATHML_SQUARE_ROOT:
|
|
if ([attribute isEqualToString:NSAccessibilityMathRootRadicandAttribute])
|
|
return [self childAt:0];
|
|
break;
|
|
case roles::MATHML_FRACTION:
|
|
if ([attribute isEqualToString:NSAccessibilityMathFractionNumeratorAttribute])
|
|
return [self childAt:0];
|
|
if ([attribute isEqualToString:NSAccessibilityMathFractionDenominatorAttribute])
|
|
return [self childAt:1];
|
|
if ([attribute isEqualToString:NSAccessibilityMathLineThicknessAttribute]) {
|
|
// WebKit sets line thickness to some logical value parsed in the
|
|
// renderer object of the <mfrac> element. It's not clear whether the
|
|
// exact value is relevant to assistive technologies. From a semantic
|
|
// point of view, the only important point is to distinguish between
|
|
// <mfrac> elements that have a fraction bar and those that do not.
|
|
// Per the MathML 3 spec, the latter happens iff the linethickness
|
|
// attribute is of the form [zero-float][optional-unit]. In that case we
|
|
// set line thickness to zero and in the other cases we set it to one.
|
|
nsAutoString thickness;
|
|
if (accWrap) {
|
|
nsCOMPtr<nsIPersistentProperties> attributes = accWrap->Attributes();
|
|
nsAccUtils::GetAccAttr(attributes, nsGkAtoms::linethickness_, thickness);
|
|
} else {
|
|
nsAutoTArray<Attribute, 10> attrs;
|
|
proxy->Attributes(&attrs);
|
|
for (size_t i = 0 ; i < attrs.Length() ; i++) {
|
|
if (attrs.ElementAt(i).Name() == "thickness") {
|
|
thickness = attrs.ElementAt(i).Value();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
double value = 1.0;
|
|
if (!thickness.IsEmpty())
|
|
value = PR_strtod(NS_LossyConvertUTF16toASCII(thickness).get(),
|
|
nullptr);
|
|
return [NSNumber numberWithInteger:(value ? 1 : 0)];
|
|
}
|
|
break;
|
|
case roles::MATHML_SUB:
|
|
if ([attribute isEqualToString:NSAccessibilityMathBaseAttribute])
|
|
return [self childAt:0];
|
|
if ([attribute isEqualToString:NSAccessibilityMathSubscriptAttribute])
|
|
return [self childAt:1];
|
|
#ifdef DEBUG
|
|
if ([attribute isEqualToString:NSAccessibilityMathSuperscriptAttribute])
|
|
return nil;
|
|
#endif
|
|
break;
|
|
case roles::MATHML_SUP:
|
|
if ([attribute isEqualToString:NSAccessibilityMathBaseAttribute])
|
|
return [self childAt:0];
|
|
#ifdef DEBUG
|
|
if ([attribute isEqualToString:NSAccessibilityMathSubscriptAttribute])
|
|
return nil;
|
|
#endif
|
|
if ([attribute isEqualToString:NSAccessibilityMathSuperscriptAttribute])
|
|
return [self childAt:1];
|
|
break;
|
|
case roles::MATHML_SUB_SUP:
|
|
if ([attribute isEqualToString:NSAccessibilityMathBaseAttribute])
|
|
return [self childAt:0];
|
|
if ([attribute isEqualToString:NSAccessibilityMathSubscriptAttribute])
|
|
return [self childAt:1];
|
|
if ([attribute isEqualToString:NSAccessibilityMathSuperscriptAttribute])
|
|
return [self childAt:2];
|
|
break;
|
|
case roles::MATHML_UNDER:
|
|
if ([attribute isEqualToString:NSAccessibilityMathBaseAttribute])
|
|
return [self childAt:0];
|
|
if ([attribute isEqualToString:NSAccessibilityMathUnderAttribute])
|
|
return [self childAt:1];
|
|
#ifdef DEBUG
|
|
if ([attribute isEqualToString:NSAccessibilityMathOverAttribute])
|
|
return nil;
|
|
#endif
|
|
break;
|
|
case roles::MATHML_OVER:
|
|
if ([attribute isEqualToString:NSAccessibilityMathBaseAttribute])
|
|
return [self childAt:0];
|
|
#ifdef DEBUG
|
|
if ([attribute isEqualToString:NSAccessibilityMathUnderAttribute])
|
|
return nil;
|
|
#endif
|
|
if ([attribute isEqualToString:NSAccessibilityMathOverAttribute])
|
|
return [self childAt:1];
|
|
break;
|
|
case roles::MATHML_UNDER_OVER:
|
|
if ([attribute isEqualToString:NSAccessibilityMathBaseAttribute])
|
|
return [self childAt:0];
|
|
if ([attribute isEqualToString:NSAccessibilityMathUnderAttribute])
|
|
return [self childAt:1];
|
|
if ([attribute isEqualToString:NSAccessibilityMathOverAttribute])
|
|
return [self childAt:2];
|
|
break;
|
|
// XXX bug 1176983
|
|
// roles::MATHML_MULTISCRIPTS should also have the following attributes:
|
|
// - NSAccessibilityMathPrescriptsAttribute
|
|
// - NSAccessibilityMathPostscriptsAttribute
|
|
// XXX bug 1176970
|
|
// roles::MATHML_FENCED should also have the following attributes:
|
|
// - NSAccessibilityMathFencedOpenAttribute
|
|
// - NSAccessibilityMathFencedCloseAttribute
|
|
default:
|
|
break;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
NSLog (@"!!! %@ can't respond to attribute %@", self, attribute);
|
|
#endif
|
|
return nil;
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
|
}
|
|
|
|
- (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute
|
|
{
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
|
|
|
|
if ([attribute isEqualToString:NSAccessibilityFocusedAttribute])
|
|
return [self canBeFocused];
|
|
|
|
return NO;
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
|
|
}
|
|
|
|
- (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute
|
|
{
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
#ifdef DEBUG_hakan
|
|
NSLog (@"[%@] %@='%@'", self, attribute, value);
|
|
#endif
|
|
|
|
// we only support focusing elements so far.
|
|
if ([attribute isEqualToString:NSAccessibilityFocusedAttribute] && [value boolValue])
|
|
[self focus];
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
|
}
|
|
|
|
- (id)accessibilityHitTest:(NSPoint)point
|
|
{
|
|
AccessibleWrap* accWrap = [self getGeckoAccessible];
|
|
ProxyAccessible* proxy = [self getProxyAccessible];
|
|
if (!accWrap && !proxy)
|
|
return nil;
|
|
|
|
// Convert the given screen-global point in the cocoa coordinate system (with
|
|
// origin in the bottom-left corner of the screen) into point in the Gecko
|
|
// coordinate system (with origin in a top-left screen point).
|
|
NSScreen* mainView = [[NSScreen screens] objectAtIndex:0];
|
|
NSPoint tmpPoint = NSMakePoint(point.x,
|
|
[mainView frame].size.height - point.y);
|
|
LayoutDeviceIntPoint geckoPoint = nsCocoaUtils::
|
|
CocoaPointsToDevPixels(tmpPoint, nsCocoaUtils::GetBackingScaleFactor(mainView));
|
|
|
|
mozAccessible* nativeChild = nil;
|
|
if (accWrap) {
|
|
Accessible* child = accWrap->ChildAtPoint(geckoPoint.x, geckoPoint.y,
|
|
Accessible::eDeepestChild);
|
|
if (child)
|
|
nativeChild = GetNativeFromGeckoAccessible(child);
|
|
} else if (proxy) {
|
|
ProxyAccessible* child = proxy->ChildAtPoint(geckoPoint.x, geckoPoint.y,
|
|
Accessible::eDeepestChild);
|
|
if (child)
|
|
nativeChild = GetNativeFromProxy(child);
|
|
}
|
|
|
|
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
|
|
{
|
|
// by default we return whatever the MacOS API know about.
|
|
// if you have custom actions, override.
|
|
return NSAccessibilityActionDescription(action);
|
|
}
|
|
|
|
- (void)accessibilityPerformAction:(NSString*)action
|
|
{
|
|
}
|
|
|
|
- (id)accessibilityFocusedUIElement
|
|
{
|
|
AccessibleWrap* accWrap = [self getGeckoAccessible];
|
|
ProxyAccessible* proxy = [self getProxyAccessible];
|
|
if (!accWrap && !proxy)
|
|
return nil;
|
|
|
|
mozAccessible* focusedChild = nil;
|
|
if (accWrap) {
|
|
Accessible* focusedGeckoChild = accWrap->FocusedChild();
|
|
if (focusedGeckoChild)
|
|
focusedChild = GetNativeFromGeckoAccessible(focusedGeckoChild);
|
|
} else if (proxy) {
|
|
ProxyAccessible* focusedGeckoChild = proxy->FocusedChild();
|
|
if (focusedGeckoChild)
|
|
focusedChild = GetNativeFromProxy(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
|
|
{
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
|
|
|
id nativeParent = nil;
|
|
if (AccessibleWrap* accWrap = [self getGeckoAccessible]) {
|
|
Accessible* accessibleParent = accWrap->GetUnignoredParent();
|
|
if (accessibleParent)
|
|
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.
|
|
nativeParent = GetNativeFromGeckoAccessible(accWrap->RootAccessible());
|
|
} else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
|
|
// Go up the chain to find a parent that is not ignored.
|
|
ProxyAccessible* accessibleParent = GetProxyUnignoredParent(proxy);
|
|
if (accessibleParent)
|
|
nativeParent = GetNativeFromProxy(accessibleParent);
|
|
if (nativeParent)
|
|
return GetClosestInterestingAccessible(nativeParent);
|
|
|
|
Accessible* outerDoc = proxy->OuterDocOfRemoteBrowser();
|
|
nativeParent = outerDoc ?
|
|
GetNativeFromGeckoAccessible(outerDoc->RootAccessible()) : nil;
|
|
} else {
|
|
return nil;
|
|
}
|
|
|
|
NSAssert1 (nativeParent, @"!!! we can't find a parent for %@", self);
|
|
|
|
return GetClosestInterestingAccessible(nativeParent);
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
|
}
|
|
|
|
- (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
|
|
{
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
|
|
|
AccessibleWrap* accWrap = [self getGeckoAccessible];
|
|
if (mChildren || (accWrap && !accWrap->AreChildrenCached()))
|
|
return mChildren;
|
|
|
|
// get the array of children.
|
|
if (accWrap) {
|
|
nsAutoTArray<Accessible*, 10> childrenArray;
|
|
accWrap->GetUnignoredChildren(&childrenArray);
|
|
mChildren = ConvertToNSArray(childrenArray);
|
|
} else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
|
|
nsAutoTArray<ProxyAccessible*, 10> childrenArray;
|
|
GetProxyUnignoredChildren(proxy, &childrenArray);
|
|
mChildren = ConvertToNSArray(childrenArray);
|
|
}
|
|
|
|
#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;
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
|
}
|
|
|
|
- (NSValue*)position
|
|
{
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
|
|
|
nsIntRect rect;
|
|
if (AccessibleWrap* accWrap = [self getGeckoAccessible])
|
|
rect = accWrap->Bounds();
|
|
else if (ProxyAccessible* proxy = [self getProxyAccessible])
|
|
rect = proxy->Bounds();
|
|
else
|
|
return nil;
|
|
|
|
NSScreen* mainView = [[NSScreen screens] objectAtIndex:0];
|
|
CGFloat scaleFactor = nsCocoaUtils::GetBackingScaleFactor(mainView);
|
|
NSPoint p = NSMakePoint(static_cast<CGFloat>(rect.x) / scaleFactor,
|
|
[mainView frame].size.height - static_cast<CGFloat>(rect.y + rect.height) / scaleFactor);
|
|
|
|
return [NSValue valueWithPoint:p];
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
|
}
|
|
|
|
- (NSValue*)size
|
|
{
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
|
|
|
nsIntRect rect;
|
|
if (AccessibleWrap* accWrap = [self getGeckoAccessible])
|
|
rect = accWrap->Bounds();
|
|
else if (ProxyAccessible* proxy = [self getProxyAccessible])
|
|
rect = proxy->Bounds();
|
|
else
|
|
return nil;
|
|
|
|
CGFloat scaleFactor =
|
|
nsCocoaUtils::GetBackingScaleFactor([[NSScreen screens] objectAtIndex:0]);
|
|
return [NSValue valueWithSize:NSMakeSize(static_cast<CGFloat>(rect.width) / scaleFactor,
|
|
static_cast<CGFloat>(rect.height) / scaleFactor)];
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
|
}
|
|
|
|
- (NSString*)role
|
|
{
|
|
AccessibleWrap* accWrap = [self getGeckoAccessible];
|
|
if (accWrap) {
|
|
#ifdef DEBUG_A11Y
|
|
NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(accWrap),
|
|
"Does not support Text when it should");
|
|
#endif
|
|
} else if (![self getProxyAccessible]) {
|
|
return nil;
|
|
}
|
|
|
|
#define ROLE(geckoRole, stringRole, atkRole, macRole, msaaRole, ia2Role, nameRule) \
|
|
case roles::geckoRole: \
|
|
return macRole;
|
|
|
|
switch (mRole) {
|
|
#include "RoleMap.h"
|
|
default:
|
|
NS_NOTREACHED("Unknown role.");
|
|
return NSAccessibilityUnknownRole;
|
|
}
|
|
|
|
#undef ROLE
|
|
}
|
|
|
|
- (NSString*)subrole
|
|
{
|
|
AccessibleWrap* accWrap = [self getGeckoAccessible];
|
|
ProxyAccessible* proxy = [self getProxyAccessible];
|
|
|
|
// Deal with landmarks first
|
|
nsIAtom* landmark = nullptr;
|
|
if (accWrap)
|
|
landmark = accWrap->LandmarkRole();
|
|
else if (proxy)
|
|
landmark = proxy->LandmarkRole();
|
|
|
|
if (landmark) {
|
|
if (landmark == nsGkAtoms::application)
|
|
return @"AXLandmarkApplication";
|
|
if (landmark == nsGkAtoms::banner)
|
|
return @"AXLandmarkBanner";
|
|
if (landmark == nsGkAtoms::complementary)
|
|
return @"AXLandmarkComplementary";
|
|
if (landmark == nsGkAtoms::contentinfo)
|
|
return @"AXLandmarkContentInfo";
|
|
if (landmark == nsGkAtoms::form)
|
|
return @"AXLandmarkForm";
|
|
if (landmark == nsGkAtoms::main)
|
|
return @"AXLandmarkMain";
|
|
if (landmark == nsGkAtoms::navigation)
|
|
return @"AXLandmarkNavigation";
|
|
if (landmark == nsGkAtoms::search)
|
|
return @"AXLandmarkSearch";
|
|
if (landmark == nsGkAtoms::searchbox)
|
|
return @"AXSearchField";
|
|
}
|
|
|
|
// Now, deal with widget roles
|
|
nsIAtom* roleAtom = nullptr;
|
|
if (accWrap && accWrap->HasARIARole()) {
|
|
nsRoleMapEntry* roleMap = accWrap->ARIARoleMap();
|
|
roleAtom = *roleMap->roleAtom;
|
|
}
|
|
if (proxy)
|
|
roleAtom = proxy->ARIARoleAtom();
|
|
|
|
if (roleAtom) {
|
|
if (roleAtom == nsGkAtoms::alert)
|
|
return @"AXApplicationAlert";
|
|
if (roleAtom == nsGkAtoms::alertdialog)
|
|
return @"AXApplicationAlertDialog";
|
|
if (roleAtom == nsGkAtoms::article)
|
|
return @"AXDocumentArticle";
|
|
if (roleAtom == nsGkAtoms::dialog)
|
|
return @"AXApplicationDialog";
|
|
if (roleAtom == nsGkAtoms::document)
|
|
return @"AXDocument";
|
|
if (roleAtom == nsGkAtoms::log_)
|
|
return @"AXApplicationLog";
|
|
if (roleAtom == nsGkAtoms::marquee)
|
|
return @"AXApplicationMarquee";
|
|
if (roleAtom == nsGkAtoms::math)
|
|
return @"AXDocumentMath";
|
|
if (roleAtom == nsGkAtoms::note_)
|
|
return @"AXDocumentNote";
|
|
if (roleAtom == nsGkAtoms::region)
|
|
return @"AXDocumentRegion";
|
|
if (roleAtom == nsGkAtoms::status)
|
|
return @"AXApplicationStatus";
|
|
if (roleAtom == nsGkAtoms::tabpanel)
|
|
return @"AXTabPanel";
|
|
if (roleAtom == nsGkAtoms::timer)
|
|
return @"AXApplicationTimer";
|
|
if (roleAtom == nsGkAtoms::tooltip)
|
|
return @"AXUserInterfaceTooltip";
|
|
}
|
|
|
|
switch (mRole) {
|
|
case roles::LIST:
|
|
return @"AXContentList"; // 10.6+ NSAccessibilityContentListSubrole;
|
|
|
|
case roles::ENTRY:
|
|
if ((accWrap && accWrap->IsSearchbox()) ||
|
|
(proxy && proxy->IsSearchbox()))
|
|
return @"AXSearchField";
|
|
break;
|
|
|
|
case roles::DEFINITION_LIST:
|
|
return @"AXDefinitionList"; // 10.6+ NSAccessibilityDefinitionListSubrole;
|
|
|
|
case roles::TERM:
|
|
return @"AXTerm";
|
|
|
|
case roles::DEFINITION:
|
|
return @"AXDefinition";
|
|
|
|
case roles::MATHML_MATH:
|
|
return @"AXDocumentMath";
|
|
|
|
case roles::MATHML_FRACTION:
|
|
return @"AXMathFraction";
|
|
|
|
case roles::MATHML_FENCED:
|
|
// XXX bug 1176970
|
|
// This should be AXMathFence, but doing so without implementing the
|
|
// whole fence interface seems to make VoiceOver crash, so we present it
|
|
// as a row for now.
|
|
return @"AXMathRow";
|
|
|
|
case roles::MATHML_SUB:
|
|
case roles::MATHML_SUP:
|
|
case roles::MATHML_SUB_SUP:
|
|
return @"AXMathSubscriptSuperscript";
|
|
|
|
case roles::MATHML_ROW:
|
|
case roles::MATHML_STYLE:
|
|
case roles::MATHML_ERROR:
|
|
return @"AXMathRow";
|
|
|
|
case roles::MATHML_UNDER:
|
|
case roles::MATHML_OVER:
|
|
case roles::MATHML_UNDER_OVER:
|
|
return @"AXMathUnderOver";
|
|
|
|
case roles::MATHML_SQUARE_ROOT:
|
|
return @"AXMathSquareRoot";
|
|
|
|
case roles::MATHML_ROOT:
|
|
return @"AXMathRoot";
|
|
|
|
case roles::MATHML_TEXT:
|
|
return @"AXMathText";
|
|
|
|
case roles::MATHML_NUMBER:
|
|
return @"AXMathNumber";
|
|
|
|
case roles::MATHML_IDENTIFIER:
|
|
return @"AXMathIdentifier";
|
|
|
|
case roles::MATHML_TABLE:
|
|
return @"AXMathTable";
|
|
|
|
case roles::MATHML_TABLE_ROW:
|
|
return @"AXMathTableRow";
|
|
|
|
case roles::MATHML_CELL:
|
|
return @"AXMathTableCell";
|
|
|
|
// XXX: NSAccessibility also uses subroles AXMathSeparatorOperator and
|
|
// AXMathFenceOperator. We should use the NS_MATHML_OPERATOR_FENCE and
|
|
// NS_MATHML_OPERATOR_SEPARATOR bits of nsOperatorFlags, but currently they
|
|
// are only available from the MathML layout code. Hence we just fallback
|
|
// to subrole AXMathOperator for now.
|
|
// XXX bug 1175747 WebKit also creates anonymous operators for <mfenced>
|
|
// which have subroles AXMathSeparatorOperator and AXMathFenceOperator.
|
|
case roles::MATHML_OPERATOR:
|
|
return @"AXMathOperator";
|
|
|
|
case roles::MATHML_MULTISCRIPTS:
|
|
return @"AXMathMultiscript";
|
|
|
|
case roles::SWITCH:
|
|
return @"AXSwitch";
|
|
|
|
case roles::ALERT:
|
|
return @"AXApplicationAlert";
|
|
|
|
case roles::SEPARATOR:
|
|
return @"AXContentSeparator";
|
|
|
|
case roles::PROPERTYPAGE:
|
|
return @"AXTabPanel";
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return nil;
|
|
}
|
|
|
|
struct RoleDescrMap
|
|
{
|
|
NSString* role;
|
|
const nsString description;
|
|
};
|
|
|
|
static const RoleDescrMap sRoleDescrMap[] = {
|
|
{ @"AXApplicationAlert", NS_LITERAL_STRING("alert") },
|
|
{ @"AXApplicationAlertDialog", NS_LITERAL_STRING("alertDialog") },
|
|
{ @"AXApplicationLog", NS_LITERAL_STRING("log") },
|
|
{ @"AXApplicationMarquee", NS_LITERAL_STRING("marquee") },
|
|
{ @"AXApplicationStatus", NS_LITERAL_STRING("status") },
|
|
{ @"AXApplicationTimer", NS_LITERAL_STRING("timer") },
|
|
{ @"AXContentSeparator", NS_LITERAL_STRING("separator") },
|
|
{ @"AXDefinition", NS_LITERAL_STRING("definition") },
|
|
{ @"AXDocument", NS_LITERAL_STRING("document") },
|
|
{ @"AXDocumentArticle", NS_LITERAL_STRING("article") },
|
|
{ @"AXDocumentMath", NS_LITERAL_STRING("math") },
|
|
{ @"AXDocumentNote", NS_LITERAL_STRING("note") },
|
|
{ @"AXDocumentRegion", NS_LITERAL_STRING("region") },
|
|
{ @"AXLandmarkApplication", NS_LITERAL_STRING("application") },
|
|
{ @"AXLandmarkBanner", NS_LITERAL_STRING("banner") },
|
|
{ @"AXLandmarkComplementary", NS_LITERAL_STRING("complementary") },
|
|
{ @"AXLandmarkContentInfo", NS_LITERAL_STRING("content") },
|
|
{ @"AXLandmarkMain", NS_LITERAL_STRING("main") },
|
|
{ @"AXLandmarkNavigation", NS_LITERAL_STRING("navigation") },
|
|
{ @"AXLandmarkSearch", NS_LITERAL_STRING("search") },
|
|
{ @"AXSearchField", NS_LITERAL_STRING("searchTextField") },
|
|
{ @"AXTabPanel", NS_LITERAL_STRING("tabPanel") },
|
|
{ @"AXTerm", NS_LITERAL_STRING("term") },
|
|
{ @"AXUserInterfaceTooltip", NS_LITERAL_STRING("tooltip") }
|
|
};
|
|
|
|
struct RoleDescrComparator
|
|
{
|
|
const NSString* mRole;
|
|
explicit RoleDescrComparator(const NSString* aRole) : mRole(aRole) {}
|
|
int operator()(const RoleDescrMap& aEntry) const {
|
|
return [mRole compare:aEntry.role];
|
|
}
|
|
};
|
|
|
|
- (NSString*)roleDescription
|
|
{
|
|
if (mRole == roles::DOCUMENT)
|
|
return utils::LocalizedString(NS_LITERAL_STRING("htmlContent"));
|
|
|
|
NSString* subrole = [self subrole];
|
|
|
|
if (subrole) {
|
|
size_t idx = 0;
|
|
if (BinarySearchIf(sRoleDescrMap, 0, ArrayLength(sRoleDescrMap),
|
|
RoleDescrComparator(subrole), &idx)) {
|
|
return utils::LocalizedString(sRoleDescrMap[idx].description);
|
|
}
|
|
}
|
|
|
|
return NSAccessibilityRoleDescription([self role], subrole);
|
|
}
|
|
|
|
- (NSString*)title
|
|
{
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
|
|
|
nsAutoString title;
|
|
if (AccessibleWrap* accWrap = [self getGeckoAccessible])
|
|
accWrap->Name(title);
|
|
else if (ProxyAccessible* proxy = [self getProxyAccessible])
|
|
proxy->Name(title);
|
|
|
|
return nsCocoaUtils::ToNSString(title);
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
|
}
|
|
|
|
- (id)value
|
|
{
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
|
|
|
nsAutoString value;
|
|
if (AccessibleWrap* accWrap = [self getGeckoAccessible])
|
|
accWrap->Value(value);
|
|
else if (ProxyAccessible* proxy = [self getProxyAccessible])
|
|
proxy->Value(value);
|
|
|
|
return nsCocoaUtils::ToNSString(value);
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
|
}
|
|
|
|
- (void)valueDidChange
|
|
{
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
#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.
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
|
}
|
|
|
|
- (void)selectedTextDidChange
|
|
{
|
|
// Do nothing. mozTextAccessible will.
|
|
}
|
|
|
|
- (void)documentLoadComplete
|
|
{
|
|
id realSelf = GetObjectOrRepresentedView(self);
|
|
NSAccessibilityPostNotification(realSelf, NSAccessibilityFocusedUIElementChangedNotification);
|
|
NSAccessibilityPostNotification(realSelf, @"AXLoadComplete");
|
|
NSAccessibilityPostNotification(realSelf, @"AXLayoutComplete");
|
|
}
|
|
|
|
- (NSString*)help
|
|
{
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
|
|
|
// What needs to go here is actually the accDescription of an item.
|
|
// The MSAA acc_help method has nothing to do with this one.
|
|
nsAutoString helpText;
|
|
if (AccessibleWrap* accWrap = [self getGeckoAccessible])
|
|
accWrap->Description(helpText);
|
|
else if (ProxyAccessible* proxy = [self getProxyAccessible])
|
|
proxy->Description(helpText);
|
|
|
|
return nsCocoaUtils::ToNSString(helpText);
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
|
}
|
|
|
|
// objc-style description (from NSObject); not to be confused with the accessible description above.
|
|
- (NSString*)description
|
|
{
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
|
|
|
return [NSString stringWithFormat:@"(%p) %@", self, [self role]];
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
|
}
|
|
|
|
- (BOOL)isFocused
|
|
{
|
|
return FocusMgr()->IsFocused([self getGeckoAccessible]);
|
|
}
|
|
|
|
- (BOOL)canBeFocused
|
|
{
|
|
if (AccessibleWrap* accWrap = [self getGeckoAccessible])
|
|
return accWrap->InteractiveState() & states::FOCUSABLE;
|
|
|
|
if (ProxyAccessible* proxy = [self getProxyAccessible])
|
|
return proxy->State() & states::FOCUSABLE;
|
|
|
|
return false;
|
|
}
|
|
|
|
- (BOOL)focus
|
|
{
|
|
if (AccessibleWrap* accWrap = [self getGeckoAccessible])
|
|
accWrap->TakeFocus();
|
|
else if (ProxyAccessible* proxy = [self getProxyAccessible])
|
|
proxy->TakeFocus();
|
|
else
|
|
return NO;
|
|
|
|
return YES;
|
|
}
|
|
|
|
- (BOOL)isEnabled
|
|
{
|
|
if (AccessibleWrap* accWrap = [self getGeckoAccessible])
|
|
return ((accWrap->InteractiveState() & states::UNAVAILABLE) == 0);
|
|
|
|
if (ProxyAccessible* proxy = [self getProxyAccessible])
|
|
return ((proxy->State() & states::UNAVAILABLE) == 0);
|
|
|
|
return false;
|
|
}
|
|
|
|
// The root accessible calls this when the focused node was
|
|
// changed to us.
|
|
- (void)didReceiveFocus
|
|
{
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
#ifdef DEBUG_hakan
|
|
NSLog (@"%@ received focus!", self);
|
|
#endif
|
|
NSAssert1(![self accessibilityIsIgnored], @"trying to set focus to ignored element! (%@)", self);
|
|
NSAccessibilityPostNotification(GetObjectOrRepresentedView(self),
|
|
NSAccessibilityFocusedUIElementChangedNotification);
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
|
}
|
|
|
|
- (NSWindow*)window
|
|
{
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
|
|
|
// Get a pointer to the native window (NSWindow) we reside in.
|
|
NSWindow *nativeWindow = nil;
|
|
DocAccessible* docAcc = nullptr;
|
|
if (AccessibleWrap* accWrap = [self getGeckoAccessible]) {
|
|
docAcc = accWrap->Document();
|
|
} else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
|
|
Accessible* outerDoc = proxy->OuterDocOfRemoteBrowser();
|
|
if (outerDoc)
|
|
docAcc = outerDoc->Document();
|
|
}
|
|
|
|
if (docAcc)
|
|
nativeWindow = static_cast<NSWindow*>(docAcc->GetNativeWindow());
|
|
|
|
NSAssert1(nativeWindow, @"Could not get native window for %@", self);
|
|
return nativeWindow;
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
|
}
|
|
|
|
- (void)invalidateChildren
|
|
{
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
// make room for new children
|
|
[mChildren release];
|
|
mChildren = nil;
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
|
}
|
|
|
|
- (void)appendChild:(Accessible*)aAccessible
|
|
{
|
|
// if mChildren is nil, then we don't even need to bother
|
|
if (!mChildren)
|
|
return;
|
|
|
|
mozAccessible *curNative = GetNativeFromGeckoAccessible(aAccessible);
|
|
if (curNative)
|
|
[mChildren addObject:GetObjectOrRepresentedView(curNative)];
|
|
}
|
|
|
|
- (void)expire
|
|
{
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
[self invalidateChildren];
|
|
|
|
mGeckoAccessible = 0;
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
|
}
|
|
|
|
- (BOOL)isExpired
|
|
{
|
|
return ![self getGeckoAccessible] && ![self getProxyAccessible];
|
|
}
|
|
|
|
#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
|
|
{
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
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);
|
|
}
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
|
}
|
|
|
|
- (void)sanityCheckChildren
|
|
{
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
[self sanityCheckChildren:[self children]];
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
|
}
|
|
|
|
- (void)printHierarchy
|
|
{
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
[self printHierarchyWithLevel:0];
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
|
}
|
|
|
|
- (void)printHierarchyWithLevel:(unsigned)level
|
|
{
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
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)];
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
|
}
|
|
|
|
#endif /* DEBUG */
|
|
|
|
@end
|