Bug 249998. Support DHTML accessibility plan, including roles, states and events for state changes. r=pkw, sr=jst

This commit is contained in:
aaronleventhal%moonset.net 2005-01-28 02:35:26 +00:00
parent ea7d10745f
commit 63a278237e
14 changed files with 382 additions and 61 deletions

View File

@ -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

View File

@ -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();
};

View File

@ -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);

View File

@ -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")

View File

@ -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 (); */

View File

@ -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

View File

@ -189,18 +189,23 @@ NS_IMETHODIMP nsLinkableAccessible::GetState(PRUint32 *aState)
GetParent(getter_AddRefs(parentAccessible));
if (parentAccessible) {
PRUint32 orState = 0;
parentAccessible->GetState(&orState);
parentAccessible->GetFinalState(&orState);
*aState |= orState;
}
}
}
nsCOMPtr<nsIAccessibleDocument> docAccessible(GetDocAccessible());
if (docAccessible) {
PRBool isEditable;
docAccessible->GetIsEditable(&isEditable);
if (isEditable) {
*aState &= ~(STATE_FOCUSED | STATE_FOCUSABLE); // Links not focusable in editor
if (!mLinkContent->IsFocusable()) {
*aState &= ~STATE_FOCUSABLE; // Links must have href or tabindex
}
else {
nsCOMPtr<nsIAccessibleDocument> docAccessible(GetDocAccessible());
if (docAccessible) {
PRBool isEditable;
docAccessible->GetIsEditable(&isEditable);
if (isEditable) {
*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;
}

View File

@ -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,13 +864,132 @@ 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)
{
// Not implemented yet, see bug 74220

View File

@ -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

View File

@ -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 ----- */

View File

@ -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;

View File

@ -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);
}

View File

@ -60,8 +60,10 @@ static const PRInt32 kNameSpaceID_None = 0;
#define kNameSpaceID_XUL 9
#define kNameSpaceID_SVG 10
#define kNameSpaceID_XMLEvents 11
#define kNameSpaceID_XHTML2_Unofficial 12
#define kNameSpaceID_LastBuiltin 12 // last 'built-in' namespace
#define kNameSpaceID_XHTML2_Unofficial 12
#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"

View File

@ -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
@ -278,7 +280,7 @@ NameSpaceImpl::CreateChildNameSpace(nsIAtom* aPrefix, const nsAString& aURI,
return NS_ERROR_OUT_OF_MEMORY;
}
NS_ADDREF(*aChildNameSpace);
NS_ADDREF(*aChildNameSpace);
return NS_OK;
}
@ -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;
}