mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-30 00:01:50 +00:00
Bug 249998. Support DHTML accessibility plan, including roles, states and events for state changes. r=pkw, sr=jst
This commit is contained in:
parent
ea7d10745f
commit
63a278237e
@ -52,7 +52,7 @@
|
||||
*
|
||||
* @status UNDER_REVIEW
|
||||
*/
|
||||
[scriptable, uuid(E2F395E9-3795-4494-9D0E-C5D50CD196C0)]
|
||||
[scriptable, uuid(14502afc-9513-4514-aab9-e4e7d3167ec7)]
|
||||
interface nsIAccessible : nsISupports
|
||||
{
|
||||
/**
|
||||
@ -98,8 +98,9 @@ interface nsIAccessible : nsISupports
|
||||
|
||||
/**
|
||||
* Accessible value -- a number or a secondary text equivalent for this node
|
||||
* Widgets that use xhtml2:role can force a value using the valuenow attribute
|
||||
*/
|
||||
readonly attribute AString value;
|
||||
readonly attribute AString finalValue;
|
||||
|
||||
/**
|
||||
* Accessible description -- long text associated with this node
|
||||
@ -120,16 +121,27 @@ interface nsIAccessible : nsISupports
|
||||
readonly attribute AString keyBinding;
|
||||
|
||||
/**
|
||||
* Enumerated accessible role. The values depend on platform because of variations.
|
||||
* Natural enumerated accessible role for the associated element.
|
||||
* The values depend on platform because of variations.
|
||||
* See the ROLE_* constants defined later in this file.
|
||||
* This does not take into account xhtml2:role as the finalRole does.
|
||||
*/
|
||||
readonly attribute unsigned long role;
|
||||
|
||||
/**
|
||||
* Enumerated accessible role. The values depend on platform because of variations.
|
||||
* See the ROLE_* constants defined later in this file.
|
||||
* Widgets can use xhtml2:role to force the final role
|
||||
*/
|
||||
readonly attribute unsigned long finalRole;
|
||||
|
||||
/**
|
||||
* Accessible states -- bit field which describes boolean properties of node.
|
||||
* See the STATE_* constants defined later in this file.
|
||||
* Widgets that use xhtml2:role can force the state using the attributes
|
||||
* disabled, etc.
|
||||
*/
|
||||
readonly attribute unsigned long state;
|
||||
readonly attribute unsigned long finalState;
|
||||
|
||||
/**
|
||||
* Extended accessible states -- second bit field describing node
|
||||
|
@ -41,10 +41,11 @@
|
||||
interface nsIAccessNode;
|
||||
interface nsIDOMNode;
|
||||
|
||||
[scriptable, uuid(F0809603-74E8-4284-8366-B293A5D0B758)]
|
||||
[uuid(F0809603-74E8-4284-8366-B293A5D0B758)]
|
||||
interface nsPIAccessibleDocument : nsISupports
|
||||
{
|
||||
[noscript] void invalidateCacheSubtree(in nsIDOMNode aStartNode);
|
||||
[noscript] void cacheAccessNode(in voidPtr aUniqueID, in nsIAccessNode aAccessNode);
|
||||
[noscript] void destroy();
|
||||
void invalidateCacheSubtree(in nsIDOMNode aStartNode);
|
||||
void cacheAccessNode(in voidPtr aUniqueID, in nsIAccessNode aAccessNode);
|
||||
void destroy();
|
||||
void flushPendingEvents();
|
||||
};
|
||||
|
@ -769,7 +769,7 @@ getRoleCB(AtkObject *aAtkObj)
|
||||
NS_REINTERPRET_CAST(MaiAtkObject*, aAtkObj)->accWrap;
|
||||
|
||||
PRUint32 accRole;
|
||||
nsresult rv = accWrap->GetRole(&accRole);
|
||||
nsresult rv = accWrap->GetFinalRole(&accRole);
|
||||
NS_ENSURE_SUCCESS(rv, ATK_ROLE_INVALID);
|
||||
|
||||
//the cross-platform Accessible object returns the same value for
|
||||
@ -880,7 +880,7 @@ refStateSetCB(AtkObject *aAtkObj)
|
||||
NS_REINTERPRET_CAST(MaiAtkObject*, aAtkObj)->accWrap;
|
||||
|
||||
PRUint32 accState = 0;
|
||||
nsresult rv = accWrap->GetState(&accState);
|
||||
nsresult rv = accWrap->GetFinalState(&accState);
|
||||
NS_ENSURE_SUCCESS(rv, state_set);
|
||||
|
||||
rv = accWrap->GetExtState(&accState);
|
||||
|
@ -64,6 +64,7 @@ ACCESSIBILITY_ATOM(textFrame, "TextFrame")
|
||||
// Alphabetical list of tag names
|
||||
ACCESSIBILITY_ATOM(a, "a")
|
||||
ACCESSIBILITY_ATOM(blockquote, "blockquote")
|
||||
ACCESSIBILITY_ATOM(form, "form")
|
||||
ACCESSIBILITY_ATOM(h1, "h1")
|
||||
ACCESSIBILITY_ATOM(h2, "h2")
|
||||
ACCESSIBILITY_ATOM(h3, "h3")
|
||||
@ -82,7 +83,9 @@ ACCESSIBILITY_ATOM(ul, "ul")
|
||||
ACCESSIBILITY_ATOM(accesskey, "accesskey")
|
||||
ACCESSIBILITY_ATOM(control, "control")
|
||||
ACCESSIBILITY_ATOM(_for, "for")
|
||||
ACCESSIBILITY_ATOM(form, "form")
|
||||
ACCESSIBILITY_ATOM(id, "id")
|
||||
ACCESSIBILITY_ATOM(tabindex, "tabindex")
|
||||
|
||||
// DHTML accessibility attributes
|
||||
ACCESSIBILITY_ATOM(valuenow, "valuenow") // For DHTML widget values
|
||||
ACCESSIBILITY_ATOM(role, "role")
|
||||
|
@ -50,6 +50,7 @@
|
||||
#include "nsIWidget.h"
|
||||
#include "nsIDOMDocumentView.h"
|
||||
#include "nsIDOMAbstractView.h"
|
||||
#include "nsIDOM3Node.h"
|
||||
#include "nsIDOMWindowInternal.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsIDOMElement.h"
|
||||
@ -100,7 +101,7 @@
|
||||
NS_IMPL_ISUPPORTS_INHERITED2(nsAccessible, nsAccessNode, nsIAccessible, nsPIAccessible)
|
||||
|
||||
nsAccessible::nsAccessible(nsIDOMNode* aNode, nsIWeakReference* aShell): nsAccessNodeWrap(aNode, aShell),
|
||||
mParent(nsnull), mFirstChild(nsnull), mNextSibling(nsnull)
|
||||
mParent(nsnull), mFirstChild(nsnull), mNextSibling(nsnull), mRoleMapEntry(nsnull)
|
||||
{
|
||||
#ifdef NS_DEBUG_X
|
||||
{
|
||||
@ -222,6 +223,41 @@ NS_IMETHODIMP nsAccessible::SetNextSibling(nsIAccessible *aNextSibling)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsAccessible::Init()
|
||||
{
|
||||
nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
|
||||
nsAutoString roleString;
|
||||
if (content &&
|
||||
NS_CONTENT_ATTR_HAS_VALUE == content->GetAttr(kNameSpaceID_XHTML2_Unofficial,
|
||||
nsAccessibilityAtoms::role,
|
||||
roleString)) {
|
||||
// QI to nsIDOM3Node causes some overhead. Unfortunately we need to do this each
|
||||
// time there is a role attribute, because the prefixe to namespace mappings
|
||||
// can change within any subtree via the xmlns attribute
|
||||
nsCOMPtr<nsIDOM3Node> dom3Node(do_QueryInterface(content));
|
||||
if (dom3Node) {
|
||||
nsAutoString prefix;
|
||||
NS_NAMED_LITERAL_STRING(kRolesWAI_Namespace, "http://www.w3.org/wai/pf/GUIRoleTaxonomy#");
|
||||
dom3Node->LookupPrefix(kRolesWAI_Namespace, prefix);
|
||||
prefix += ':';
|
||||
if (StringBeginsWith(roleString, prefix)) {
|
||||
roleString.Cut(0, prefix.Length());
|
||||
nsCString utf8Role = NS_ConvertUCS2toUTF8(roleString); // For easy comparison
|
||||
for (PRUint32 index = 0; gWAIRoleMap[index].roleString; index ++) {
|
||||
if (utf8Role.Equals(gWAIRoleMap[index].roleString)) {
|
||||
break; // The dynamic role attribute maps to an entry in our table
|
||||
}
|
||||
}
|
||||
// Always use some entry if there is a role string
|
||||
// If no match, we use the last entry which maps to ROLE_NOTHING
|
||||
mRoleMapEntry = &gWAIRoleMap[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nsAccessNodeWrap::Init();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsAccessible::Shutdown()
|
||||
{
|
||||
mNextSibling = nsnull;
|
||||
@ -613,7 +649,7 @@ NS_IMETHODIMP nsAccessible::GetChildAtPoint(PRInt32 tx, PRInt32 ty, nsIAccessibl
|
||||
while (child) {
|
||||
child->GetBounds(&x, &y, &w, &h);
|
||||
if (tx >= x && tx < x + w && ty >= y && ty < y + h) {
|
||||
child->GetState(&state);
|
||||
child->GetFinalState(&state);
|
||||
if ((state & (STATE_OFFSCREEN|STATE_INVISIBLE)) == 0) { // Don't walk into offscreen items
|
||||
NS_ADDREF(*aAccessible = child);
|
||||
return NS_OK;
|
||||
@ -893,7 +929,7 @@ NS_IMETHODIMP nsAccessible::TakeFocus()
|
||||
if (!content) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
content->SetFocus(nsCOMPtr<nsPresContext>(GetPresContext()));
|
||||
content->SetFocus(GetPresContext());
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
@ -1255,6 +1291,118 @@ NS_IMETHODIMP nsAccessible::FireToolkitEvent(PRUint32 aEvent, nsIAccessible *aTa
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsRoleMapEntry nsAccessible::gWAIRoleMap[] =
|
||||
{
|
||||
// This list of WAI-defined roles are currently hardcoded.
|
||||
// Eventually we will most likely be loading an RDF resource that contains this information
|
||||
// Using RDF will also allow for role extensibility.
|
||||
// XXX Should we store attribute names in this table as atoms instead of strings?
|
||||
// Definition of nsRoleMapEntry and nsStateMapEntry contains comments explaining this table.
|
||||
{"button", ROLE_PUSHBUTTON, 0, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
|
||||
{"checkbox", ROLE_CHECKBUTTON, 0, {"checked", "true", STATE_CHECKED}, {"readonly", 0, STATE_READONLY}, {0, 0, 0}},
|
||||
{"checkbox-tristate", ROLE_CHECKBUTTON, 0, {"checked", "true", STATE_CHECKED}, {"checked", "mixed", STATE_MIXED}, {"readonly", 0, STATE_READONLY}},
|
||||
{"icon", ROLE_ICON, 0, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
|
||||
{"menu", ROLE_MENUPOPUP, 0, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
|
||||
{"menubar", ROLE_MENUBAR, 0, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
|
||||
{"menuitem", ROLE_MENUITEM, 0, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
|
||||
{"menuitem-checkbox", ROLE_MENUITEM, 0, {"checked", "true", STATE_CHECKED}, {0, 0, 0}, {0, 0, 0}},
|
||||
{"progress-meter", ROLE_PROGRESSBAR, STATE_READONLY, {"valuenow", "unknown", STATE_MIXED}, {0, 0, 0}, {0, 0, 0}},
|
||||
{"grid", ROLE_TABLE, 0, {"readonly", 0, STATE_READONLY}, {"multiselect", 0, STATE_EXTSELECTABLE | STATE_MULTISELECTABLE}, {0, 0, 0}},
|
||||
{"gridcell", ROLE_CELL, STATE_SELECTABLE, {"selected", 0, STATE_SELECTED}, {0, 0, 0}, {0, 0, 0}},
|
||||
{"option", ROLE_LISTITEM, 0, {"selected", 0, STATE_SELECTED}, {0, 0, 0}, {0, 0, 0}},
|
||||
{"secret-text", ROLE_PASSWORD_TEXT, STATE_PROTECTED, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, // XXX Use ext state STATE_SINGLE_LINE
|
||||
{"select", ROLE_LIST, 0, {"readonly", 0, STATE_READONLY}, {"multiselect", 0, STATE_EXTSELECTABLE | STATE_MULTISELECTABLE}, {0, 0, 0}},
|
||||
{"slider", ROLE_SLIDER, 0, {"readonly", 0, STATE_READONLY}, {0, 0, 0}, {0, 0, 0}},
|
||||
{"submit", ROLE_PUSHBUTTON, STATE_DEFAULT, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
|
||||
{"textarea", ROLE_TEXT, 0, {"readonly", 0, STATE_READONLY}, {0, 0, 0}, {0, 0, 0}}, // XXX Use ext state STATE_MULTI_LINE
|
||||
{"textfield", ROLE_TEXT, 0, {"readonly", 0, STATE_READONLY}, {0, 0, 0}, {0, 0, 0}}, // XXX Use ext state STATE_SINGLE_LINE
|
||||
{"toolbar-icon", ROLE_PUSHBUTTON, 0, {"checked", "true", STATE_PRESSED}, {0, 0, 0}, {0, 0, 0}},
|
||||
{"tree", ROLE_OUTLINE, 0, {"readonly", 0, STATE_READONLY}, {"multiselect", 0, STATE_EXTSELECTABLE | STATE_MULTISELECTABLE}, {0, 0, 0}},
|
||||
{"treeitem", ROLE_OUTLINEITEM, 0, {"selected", 0, STATE_SELECTED}, {0, 0, 0}, {0, 0, 0}},
|
||||
{nsnull, ROLE_NOTHING, 0, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
|
||||
};
|
||||
|
||||
// XHTML 2 roles
|
||||
// These don't need a mapping - they are exposed either through DOM or via MSAA role string
|
||||
// {"banner", ROLE_NOTHING, 0, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
|
||||
// {"contentinfo", ROLE_NOTHING, 0, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
|
||||
// {"main", ROLE_NOTHING, 0, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
|
||||
// {"navigation", ROLE_NOTHING, 0, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
|
||||
// {"note", ROLE_NOTHING, 0, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
|
||||
// {"search", ROLE_NOTHING, 0, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
|
||||
// {"secondary", ROLE_NOTHING, 0, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
|
||||
// {"seealso", ROLE_NOTHING, 0, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
|
||||
|
||||
nsStateMapEntry nsAccessible::gDisabledStateMap = {"disabled", 0, STATE_UNAVAILABLE };
|
||||
// Possibly split into 2 kinds of roles -- those that hold data and those that don't
|
||||
// These states only apply to items that can hold data
|
||||
//nsStateMapEntry nsAccessible::gInvalidStateMap = {"invalid", 0, STATE_INVALID }; // XXX wait until extended states fix lands
|
||||
//nsStateMapEntry nsAccessible::gRequiredStateMap = {"required", 0, STATE_REQUIRED }; // XXX no MSAA or ATK mapping
|
||||
|
||||
|
||||
NS_IMETHODIMP nsAccessible::GetFinalRole(PRUint32 *aRole)
|
||||
{
|
||||
if (mRoleMapEntry) {
|
||||
*aRole = mRoleMapEntry->role;
|
||||
if (*aRole != ROLE_NOTHING) {
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
return GetRole(aRole);
|
||||
}
|
||||
|
||||
PRUint32 nsAccessible::MappedAttrState(nsIContent *aContent, nsStateMapEntry *aStateMapEntry)
|
||||
{
|
||||
if (!aStateMapEntry->attributeName) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
nsAutoString attribValue;
|
||||
nsCOMPtr<nsIAtom> attribAtom = do_GetAtom(aStateMapEntry->attributeName); // XXX put atoms directly in entry
|
||||
if (NS_CONTENT_ATTR_HAS_VALUE == aContent->GetAttr(kNameSpaceID_StatesWAI_Unofficial,
|
||||
attribAtom,
|
||||
attribValue) &&
|
||||
(!aStateMapEntry->attributeValue ||
|
||||
NS_ConvertUCS2toUTF8(attribValue).Equals(aStateMapEntry->attributeValue))) {
|
||||
return aStateMapEntry->state;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsAccessible::GetFinalState(PRUint32 *aState)
|
||||
{
|
||||
nsresult rv = GetState(aState);
|
||||
if (NS_FAILED(rv) || !mRoleMapEntry) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
|
||||
if (content) {
|
||||
*aState |= mRoleMapEntry->state |
|
||||
MappedAttrState(content, &mRoleMapEntry->attributeMap1) |
|
||||
MappedAttrState(content, &mRoleMapEntry->attributeMap2) |
|
||||
MappedAttrState(content, &mRoleMapEntry->attributeMap3) |
|
||||
MappedAttrState(content, &gDisabledStateMap); // Anything can be disabled/unavailable
|
||||
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsAccessible::GetFinalValue(nsAString& aValue)
|
||||
{
|
||||
if (mRoleMapEntry) {
|
||||
nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
|
||||
if (content &&
|
||||
NS_CONTENT_ATTR_HAS_VALUE == content->GetAttr(kNameSpaceID_StatesWAI_Unofficial,
|
||||
nsAccessibilityAtoms::valuenow,
|
||||
aValue)) {
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
return GetValue(aValue);
|
||||
}
|
||||
|
||||
// Not implemented by this class
|
||||
|
||||
/* DOMString getValue (); */
|
||||
|
@ -56,6 +56,28 @@ class nsIAtom;
|
||||
// When mNextSibling is set to this, it indicates there ar eno more siblings
|
||||
#define DEAD_END_ACCESSIBLE NS_STATIC_CAST(nsIAccessible*, (void*)1)
|
||||
|
||||
struct nsStateMapEntry
|
||||
{
|
||||
const char* attributeName; // magic value of nsnull means last entry in map
|
||||
const char* attributeValue; // magic value of nsnull means any value
|
||||
PRUint32 state; // OR state with this
|
||||
};
|
||||
|
||||
struct nsRoleMapEntry
|
||||
{
|
||||
const char *roleString; // such as "button"
|
||||
PRUint32 role; // use this role
|
||||
PRUint32 state; // always OR state with this
|
||||
// For this role with a DOM attribute/value match definined in
|
||||
// nsStateMapEntry.attributeName && .attributeValue, OR accessible state with
|
||||
// nsStateMapEntry.state
|
||||
// Currently you can have up to 3 DOM attributes with accessible state mappings.
|
||||
// A variable sized array would not allow use of C++'s struct initialization feature.
|
||||
nsStateMapEntry attributeMap1;
|
||||
nsStateMapEntry attributeMap2;
|
||||
nsStateMapEntry attributeMap3;
|
||||
};
|
||||
|
||||
class nsAccessible : public nsAccessNodeWrap,
|
||||
public nsIAccessible,
|
||||
public nsPIAccessible
|
||||
@ -74,8 +96,13 @@ public:
|
||||
NS_DECL_NSPIACCESSIBLE
|
||||
|
||||
// nsIAccessNode
|
||||
NS_IMETHOD Init();
|
||||
NS_IMETHOD Shutdown();
|
||||
|
||||
// Support GetFinalState(), GetFinalValue()
|
||||
NS_IMETHOD GetState(PRUint32 *aState);
|
||||
NS_IMETHOD GetValue(nsAString & aValue);
|
||||
|
||||
#ifdef MOZ_ACCESSIBILITY_ATK
|
||||
static nsresult GetParentBlockNode(nsIPresShell *aPresShell, nsIDOMNode *aCurrentNode, nsIDOMNode **aBlockNode);
|
||||
static nsIFrame* GetParentBlockFrame(nsIFrame *aFrame);
|
||||
@ -86,6 +113,7 @@ public:
|
||||
static PRBool IsCorrectFrameType(nsIFrame* aFrame, nsIAtom* aAtom);
|
||||
|
||||
protected:
|
||||
PRUint32 MappedAttrState(nsIContent *aContent, nsStateMapEntry *aStateMapEntry);
|
||||
virtual nsIFrame* GetBoundsFrame();
|
||||
virtual void GetBoundsRect(nsRect& aRect, nsIFrame** aRelativeFrame);
|
||||
PRBool IsPartiallyVisible(PRBool *aIsOffscreen);
|
||||
@ -111,7 +139,12 @@ protected:
|
||||
// Data Members
|
||||
nsCOMPtr<nsIAccessible> mParent;
|
||||
nsIAccessible *mFirstChild, *mNextSibling;
|
||||
nsRoleMapEntry *mRoleMapEntry; // Non-null indicates author-supplied role; possibly state & value as well
|
||||
|
||||
static nsRoleMapEntry gWAIRoleMap[];
|
||||
static nsStateMapEntry gDisabledStateMap;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -189,12 +189,16 @@ NS_IMETHODIMP nsLinkableAccessible::GetState(PRUint32 *aState)
|
||||
GetParent(getter_AddRefs(parentAccessible));
|
||||
if (parentAccessible) {
|
||||
PRUint32 orState = 0;
|
||||
parentAccessible->GetState(&orState);
|
||||
parentAccessible->GetFinalState(&orState);
|
||||
*aState |= orState;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!mLinkContent->IsFocusable()) {
|
||||
*aState &= ~STATE_FOCUSABLE; // Links must have href or tabindex
|
||||
}
|
||||
else {
|
||||
nsCOMPtr<nsIAccessibleDocument> docAccessible(GetDocAccessible());
|
||||
if (docAccessible) {
|
||||
PRBool isEditable;
|
||||
@ -203,6 +207,7 @@ NS_IMETHODIMP nsLinkableAccessible::GetState(PRUint32 *aState)
|
||||
*aState &= ~(STATE_FOCUSED | STATE_FOCUSABLE); // Links not focusable in editor
|
||||
}
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -338,26 +343,7 @@ NS_IMETHODIMP nsGenericAccessible::TakeFocus()
|
||||
|
||||
NS_IMETHODIMP nsGenericAccessible::GetRole(PRUint32 *aRole)
|
||||
{
|
||||
// XXX todo: use DHTML role attribs to fill in accessible role
|
||||
nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
|
||||
if (!content) {
|
||||
return NS_ERROR_FAILURE; // Node already shut down
|
||||
}
|
||||
|
||||
*aRole = ROLE_NOTHING;
|
||||
|
||||
nsAutoString role;
|
||||
if (content->GetAttr(kNameSpaceID_XHTML2_Unofficial, nsAccessibilityAtoms::role, role) != NS_CONTENT_ATTR_HAS_VALUE) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (role.EqualsLiteral("button")) {
|
||||
*aRole = ROLE_PUSHBUTTON;
|
||||
}
|
||||
else if (role.EqualsLiteral("checkbox")) {
|
||||
*aRole = ROLE_CHECKBUTTON;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -39,10 +39,12 @@
|
||||
#include "nsDocAccessible.h"
|
||||
#include "nsAccessibleEventData.h"
|
||||
#include "nsIAccessibilityService.h"
|
||||
#include "nsArray.h"
|
||||
#include "nsICommandManager.h"
|
||||
#include "nsIDocShell.h"
|
||||
#include "nsIDocShellTreeItem.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIDOMAttr.h"
|
||||
#include "nsIDOMCharacterData.h"
|
||||
#include "nsIDOMDocument.h"
|
||||
#include "nsIDOMEventTarget.h"
|
||||
@ -78,7 +80,6 @@
|
||||
//-----------------------------------------------------
|
||||
nsDocAccessible::nsDocAccessible(nsIDOMNode *aDOMNode, nsIWeakReference* aShell):
|
||||
nsBlockAccessible(aDOMNode, aShell), mWnd(nsnull),
|
||||
mScrollWatchTimer(nsnull), mDocLoadTimer(nsnull),
|
||||
mWebProgress(nsnull), mEditor(nsnull),
|
||||
mBusy(eBusyStateUninitialized),
|
||||
mScrollPositionChangedTicks(0), mIsNewDocument(PR_FALSE)
|
||||
@ -403,6 +404,12 @@ NS_IMETHODIMP nsDocAccessible::Shutdown()
|
||||
mDocLoadTimer->Cancel();
|
||||
mDocLoadTimer = nsnull;
|
||||
}
|
||||
if (mFireEventTimer) {
|
||||
mFireEventTimer->Cancel();
|
||||
mFireEventTimer = nsnull;
|
||||
}
|
||||
mEventsToFire.Clear();
|
||||
|
||||
mWebProgress = nsnull;
|
||||
|
||||
ClearCache(mAccessNodeCache);
|
||||
@ -550,12 +557,13 @@ nsresult nsDocAccessible::AddEventListeners()
|
||||
rv = target->AddEventListener(NS_LITERAL_STRING("DOMNodeRemoved"),
|
||||
this, PR_TRUE);
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to register listener");
|
||||
rv = target->AddEventListener(NS_LITERAL_STRING("DOMNodeInsertedIntoDocument"),
|
||||
this, PR_TRUE);
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to register listener");
|
||||
rv = target->AddEventListener(NS_LITERAL_STRING("DOMNodeRemovedFromDocument"),
|
||||
this, PR_TRUE);
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to register listener");
|
||||
// These aren't implemented yet, and we're not using them
|
||||
// rv = target->AddEventListener(NS_LITERAL_STRING("DOMNodeInsertedIntoDocument"),
|
||||
// this, PR_TRUE);
|
||||
// NS_ASSERTION(NS_SUCCEEDED(rv), "failed to register listener");
|
||||
// rv = target->AddEventListener(NS_LITERAL_STRING("DOMNodeRemovedFromDocument"),
|
||||
// this, PR_TRUE);
|
||||
// NS_ASSERTION(NS_SUCCEEDED(rv), "failed to register listener");
|
||||
|
||||
return rv;
|
||||
}
|
||||
@ -579,8 +587,8 @@ nsresult nsDocAccessible::RemoveEventListeners()
|
||||
target->RemoveEventListener(NS_LITERAL_STRING("DOMSubtreeModified"), this, PR_TRUE);
|
||||
target->RemoveEventListener(NS_LITERAL_STRING("DOMNodeInserted"), this, PR_TRUE);
|
||||
target->RemoveEventListener(NS_LITERAL_STRING("DOMNodeRemoved"), this, PR_TRUE);
|
||||
target->RemoveEventListener(NS_LITERAL_STRING("DOMNodeInsertedIntoDocument"), this, PR_TRUE);
|
||||
target->RemoveEventListener(NS_LITERAL_STRING("DOMNodeRemovedFromDocument"), this, PR_TRUE);
|
||||
// target->RemoveEventListener(NS_LITERAL_STRING("DOMNodeInsertedIntoDocument"), this, PR_TRUE);
|
||||
// target->RemoveEventListener(NS_LITERAL_STRING("DOMNodeRemovedFromDocument"), this, PR_TRUE);
|
||||
|
||||
if (mScrollWatchTimer) {
|
||||
mScrollWatchTimer->Cancel();
|
||||
@ -856,11 +864,130 @@ NS_IMETHODIMP nsDocAccessible::SubtreeModified(nsIDOMEvent* aEvent)
|
||||
NS_IMETHODIMP nsDocAccessible::AttrModified(nsIDOMEvent* aMutationEvent)
|
||||
{
|
||||
// XXX todo
|
||||
// We will probably need to handle special cases here
|
||||
// We still need to handle special HTML cases here
|
||||
// For example, if an <img>'s usemap attribute is modified
|
||||
// Otherwise it may just be a state change, for example an object changing
|
||||
// its visibility.
|
||||
// its visibility
|
||||
|
||||
nsCOMPtr<nsIPresShell> shell = GetPresShell();
|
||||
if (!shell) {
|
||||
return NS_OK; // Document has been shut down
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMMutationEvent> mutationEvent(do_QueryInterface(aMutationEvent));
|
||||
NS_ASSERTION(mutationEvent, "Not a mutation event!");
|
||||
nsCOMPtr<nsIDOMEventTarget> domEventTarget;
|
||||
mutationEvent->GetTarget(getter_AddRefs(domEventTarget));
|
||||
nsCOMPtr<nsIDOMNode> targetNode(do_QueryInterface(domEventTarget));
|
||||
NS_ASSERTION(targetNode, "No node for attr modified");
|
||||
if (!targetNode) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIAccessibilityService> accService =
|
||||
do_GetService("@mozilla.org/accessibilityService;1");
|
||||
NS_ASSERTION(accService, "How can we be here if no accessibility service?");
|
||||
|
||||
nsCOMPtr<nsIAccessible> changedAccessible;
|
||||
accService->GetAccessibleInShell(targetNode, shell,
|
||||
getter_AddRefs(changedAccessible));
|
||||
if (!changedAccessible) {
|
||||
return NS_OK; // The attribute change did not occur on a node exposed for accessibility
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMNode> attributeNode;
|
||||
mutationEvent->GetRelatedNode(getter_AddRefs(attributeNode));
|
||||
nsCOMPtr<nsIDOMAttr> domAttr(do_QueryInterface(attributeNode));
|
||||
NS_ASSERTION(domAttr, "No related attribute node for DOMAttrModified event");
|
||||
|
||||
nsAutoString attrName;
|
||||
mutationEvent->GetAttrName(attrName);
|
||||
|
||||
PRUint32 eventType = 0;
|
||||
if (attrName.EqualsLiteral("checked")) {
|
||||
nsCOMPtr<nsIContent> targetContent(do_QueryInterface(targetNode));
|
||||
if (targetContent->IsContentOfType(nsIContent::eXUL)) {
|
||||
// XXX Should we remove XUL's CheckboxStateChanged event and just utilize this instead?
|
||||
return NS_OK; // XUL utilizes CheckboxStateChanged event for this. Don't fire double event
|
||||
}
|
||||
eventType = nsIAccessibleEvent::EVENT_STATE_CHANGE;
|
||||
}
|
||||
else if (attrName.EqualsLiteral("readonly") ||
|
||||
attrName.EqualsLiteral("disabled") || attrName.EqualsLiteral("required") ||
|
||||
attrName.EqualsLiteral("invalid")) {
|
||||
eventType = nsIAccessibleEvent::EVENT_STATE_CHANGE;
|
||||
}
|
||||
else if (attrName.EqualsLiteral("valuenow")) {
|
||||
eventType = nsIAccessibleEvent::EVENT_VALUE_CHANGE;
|
||||
}
|
||||
else if (attrName.EqualsLiteral("selected")) {
|
||||
nsCOMPtr<nsIContent> targetContent(do_QueryInterface(targetNode));
|
||||
if (targetContent->IsContentOfType(nsIContent::eXUL)) {
|
||||
return NS_OK; // XUL fires special events for selection
|
||||
}
|
||||
// XXX Do we need to differentiate between different kinds of selection events
|
||||
// such as selection, selection add, selection remove?
|
||||
nsAutoString attrValue;
|
||||
mutationEvent->GetNewValue(attrValue);
|
||||
if (!attrValue.IsEmpty() && !attrValue.EqualsLiteral("false")) {
|
||||
eventType = nsIAccessibleEvent::EVENT_SELECTION;
|
||||
}
|
||||
}
|
||||
|
||||
// Fire after short timer, because we need to wait for
|
||||
// DOM attribute to actually change. Otherwise, assistive technology
|
||||
// will retrieve the wrong state/value/selection info.
|
||||
if (eventType) {
|
||||
PRBool isTimerStarted = PR_TRUE;
|
||||
if (mEventsToFire.Count() == 0) {
|
||||
if (!mFireEventTimer) {
|
||||
// Do not yet have a timer going for firing another event.
|
||||
mFireEventTimer = do_CreateInstance("@mozilla.org/timer;1");
|
||||
NS_ENSURE_TRUE(mFireEventTimer, NS_ERROR_OUT_OF_MEMORY);
|
||||
}
|
||||
isTimerStarted = PR_FALSE;
|
||||
}
|
||||
// XXX Add related data for ATK support.
|
||||
// For example, state change event should provide what state has changed,
|
||||
// as well as the old and new value.
|
||||
nsCOMPtr<nsIAccessibleEvent> event =
|
||||
new nsAccessibleEventData(eventType, changedAccessible, this, nsnull);
|
||||
NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
|
||||
mEventsToFire.AppendObject(event);
|
||||
if (!isTimerStarted) {
|
||||
// This is be the first delayed event in queue, start timer
|
||||
// so that event gets fired via FlushEventsCallback
|
||||
mFireEventTimer->InitWithFuncCallback(FlushEventsCallback,
|
||||
NS_STATIC_CAST(nsPIAccessibleDocument*, this),
|
||||
0, nsITimer::TYPE_ONE_SHOT);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsDocAccessible::FlushPendingEvents()
|
||||
{
|
||||
PRUint32 length = mEventsToFire.Count();
|
||||
NS_ASSERTION(length, "How did we get here without events to fire?");
|
||||
for (PRUint32 index = 0; index < length; index ++) {
|
||||
nsIAccessibleEvent *accessibleEvent = mEventsToFire[index];
|
||||
NS_ASSERTION(accessibleEvent, "Array item is not an accessible event");
|
||||
nsCOMPtr<nsIAccessible> accessible;
|
||||
accessibleEvent->GetAccessible(getter_AddRefs(accessible));
|
||||
PRUint32 eventType;
|
||||
accessibleEvent->GetEventType(&eventType);
|
||||
FireToolkitEvent(eventType, accessible, nsnull);
|
||||
}
|
||||
mEventsToFire.Clear(); // Clear out array
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void nsDocAccessible::FlushEventsCallback(nsITimer *aTimer, void *aClosure)
|
||||
{
|
||||
nsPIAccessibleDocument *accessibleDoc = NS_STATIC_CAST(nsPIAccessibleDocument*, aClosure);
|
||||
NS_ASSERTION(accessibleDoc, "How did we get here without an accessible document?");
|
||||
accessibleDoc->FlushPendingEvents();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsDocAccessible::NodeRemovedFromDocument(nsIDOMEvent* aMutationEvent)
|
||||
|
@ -42,6 +42,8 @@
|
||||
#include "nsBaseWidgetAccessible.h"
|
||||
#include "nsIAccessibleDocument.h"
|
||||
#include "nsPIAccessibleDocument.h"
|
||||
#include "nsIAccessibleEvent.h"
|
||||
#include "nsIArray.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIDOMMutationListener.h"
|
||||
#include "nsIEditor.h"
|
||||
@ -96,6 +98,7 @@ class nsDocAccessible : public nsBlockAccessible,
|
||||
NS_DECL_NSIWEBPROGRESSLISTENER
|
||||
|
||||
NS_IMETHOD FireToolkitEvent(PRUint32 aEvent, nsIAccessible* aAccessible, void* aData);
|
||||
static void FlushEventsCallback(nsITimer *aTimer, void *aClosure);
|
||||
|
||||
// nsIAccessNode
|
||||
NS_IMETHOD Shutdown();
|
||||
@ -124,11 +127,13 @@ class nsDocAccessible : public nsBlockAccessible,
|
||||
nsCOMPtr<nsIDocument> mDocument;
|
||||
nsCOMPtr<nsITimer> mScrollWatchTimer;
|
||||
nsCOMPtr<nsITimer> mDocLoadTimer;
|
||||
nsCOMPtr<nsITimer> mFireEventTimer;
|
||||
nsCOMPtr<nsIWebProgress> mWebProgress;
|
||||
nsCOMPtr<nsIEditor> mEditor; // Editor, if there is one
|
||||
EBusyState mBusy;
|
||||
PRUint16 mScrollPositionChangedTicks; // Used for tracking scroll events
|
||||
PRPackedBool mIsNewDocument;
|
||||
nsCOMArray<nsIAccessibleEvent> mEventsToFire;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -518,7 +518,7 @@ nsIFrame* nsHTMLSelectOptionAccessible::GetBoundsFrame()
|
||||
if (NS_SUCCEEDED(accService->GetAccessibleFor(selectNode,
|
||||
getter_AddRefs(selAcc)))) {
|
||||
PRUint32 state;
|
||||
selAcc->GetState(&state);
|
||||
selAcc->GetFinalState(&state);
|
||||
if (state & STATE_COLLAPSED) {
|
||||
nsCOMPtr<nsIPresShell> presShell(GetPresShell());
|
||||
if (!presShell) {
|
||||
@ -879,7 +879,7 @@ NS_IMETHODIMP nsHTMLComboboxAccessible::GetValue(nsAString& aValue)
|
||||
nsCOMPtr<nsIAccessible> textFieldAccessible;
|
||||
nsresult rv = GetFirstChild(getter_AddRefs(textFieldAccessible));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return textFieldAccessible->GetValue(aValue);
|
||||
return textFieldAccessible->GetFinalValue(aValue);
|
||||
}
|
||||
|
||||
/** ----- nsHTMLComboboxTextFieldAccessible ----- */
|
||||
|
@ -238,7 +238,7 @@ STDMETHODIMP nsAccessibleWrap::get_accValue(
|
||||
GetXPAccessibleFor(varChild, getter_AddRefs(xpAccessible));
|
||||
if (xpAccessible) {
|
||||
nsAutoString value;
|
||||
if (NS_FAILED(xpAccessible->GetValue(value)))
|
||||
if (NS_FAILED(xpAccessible->GetFinalValue(value)))
|
||||
return S_FALSE;
|
||||
|
||||
*pszValue = ::SysAllocString(value.get());
|
||||
@ -279,7 +279,7 @@ STDMETHODIMP nsAccessibleWrap::get_accRole(
|
||||
return E_FAIL;
|
||||
|
||||
PRUint32 role = 0;
|
||||
if (NS_FAILED(xpAccessible->GetRole(&role)))
|
||||
if (NS_FAILED(xpAccessible->GetFinalRole(&role)))
|
||||
return E_FAIL;
|
||||
|
||||
// -- Try enumerated role
|
||||
@ -336,7 +336,7 @@ STDMETHODIMP nsAccessibleWrap::get_accState(
|
||||
return E_FAIL;
|
||||
|
||||
PRUint32 state;
|
||||
if (NS_FAILED(xpAccessible->GetState(&state)))
|
||||
if (NS_FAILED(xpAccessible->GetFinalState(&state)))
|
||||
return E_FAIL;
|
||||
|
||||
pvarState->lVal = state;
|
||||
|
@ -94,7 +94,7 @@ NS_IMETHODIMP nsXULMenuitemAccessible::GetState(PRUint32 *_retval)
|
||||
PRUint32 parentState = 0;
|
||||
nsCOMPtr<nsIAccessible> parentAccessible;
|
||||
GetParent(getter_AddRefs(parentAccessible));
|
||||
parentAccessible->GetState(&parentState);
|
||||
parentAccessible->GetFinalState(&parentState);
|
||||
*_retval &= ~STATE_OFFSCREEN; // clear the old OFFSCREEN bit
|
||||
*_retval |= (parentState & STATE_OFFSCREEN); // or it with the parent's offscreen bit
|
||||
|
||||
@ -228,7 +228,7 @@ NS_IMETHODIMP nsXULMenuitemAccessible::DoAction(PRUint8 index)
|
||||
nsCOMPtr<nsIAccessible> buttonAccessible;
|
||||
parentAccessible->GetPreviousSibling(getter_AddRefs(buttonAccessible));
|
||||
PRUint32 state;
|
||||
buttonAccessible->GetState(&state);
|
||||
buttonAccessible->GetFinalState(&state);
|
||||
if (state & STATE_PRESSED)
|
||||
buttonAccessible->DoAction(eAction_Click);
|
||||
}
|
||||
|
@ -61,7 +61,9 @@ static const PRInt32 kNameSpaceID_None = 0;
|
||||
#define kNameSpaceID_SVG 10
|
||||
#define kNameSpaceID_XMLEvents 11
|
||||
#define kNameSpaceID_XHTML2_Unofficial 12
|
||||
#define kNameSpaceID_LastBuiltin 12 // last 'built-in' namespace
|
||||
#define kNameSpaceID_RolesWAI_Unofficial 13
|
||||
#define kNameSpaceID_StatesWAI_Unofficial 14
|
||||
#define kNameSpaceID_LastBuiltin 14 // last 'built-in' namespace
|
||||
|
||||
#define NS_NAMESPACEMANAGER_CONTRACTID "@mozilla.org/content/namespacemanager;1"
|
||||
|
||||
|
@ -64,6 +64,8 @@ static NS_DEFINE_CID(kXTFServiceCID, NS_XTFSERVICE_CID);
|
||||
#define kSVGNameSpaceURI "http://www.w3.org/2000/svg"
|
||||
#define kXMLEventsNameSpaceURI "http://www.w3.org/2001/xml-events"
|
||||
#define kXHTML2UnofficialNameSpaceURI "http://www.w3.org/TR/xhtml2" // Will eventually change
|
||||
#define kRolesWAIUnofficialNameSpaceURI "http://www.w3.org/wai/pf/GUIRoleTaxonomy#" // Will eventually change
|
||||
#define kStatesWAIUnofficialNameSpaceURI "http://www.w3.org/wai/pf/GUIStateTaxonomy#" // Will eventually change
|
||||
|
||||
//-----------------------------------------------------------
|
||||
// Name Space
|
||||
@ -311,6 +313,8 @@ nsresult NameSpaceManagerImpl::Init()
|
||||
AddNameSpace(NS_LITERAL_STRING(kSVGNameSpaceURI), kNameSpaceID_SVG);
|
||||
AddNameSpace(NS_LITERAL_STRING(kXMLEventsNameSpaceURI), kNameSpaceID_XMLEvents);
|
||||
AddNameSpace(NS_LITERAL_STRING(kXHTML2UnofficialNameSpaceURI), kNameSpaceID_XHTML2_Unofficial);
|
||||
AddNameSpace(NS_LITERAL_STRING(kRolesWAIUnofficialNameSpaceURI), kNameSpaceID_RolesWAI_Unofficial);
|
||||
AddNameSpace(NS_LITERAL_STRING(kStatesWAIUnofficialNameSpaceURI), kNameSpaceID_StatesWAI_Unofficial);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user