Bug 368191. Implement ARIA activedescendant property. r=david.bolter
git-svn-id: svn://10.0.0.236/trunk@221715 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
@@ -64,6 +64,7 @@
|
||||
#include "nsIStringBundle.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsRootAccessible.h"
|
||||
#include "nsIFocusController.h"
|
||||
|
||||
/* For documentation of the accessibility architecture,
|
||||
* see http://lxr.mozilla.org/seamonkey/source/accessible/accessible-docs.html
|
||||
@@ -664,4 +665,37 @@ void nsAccessNode::ClearCache(nsInterfaceHashtable<nsVoidHashKey, nsIAccessNode>
|
||||
aCache.Enumerate(ClearCacheEntry, nsnull);
|
||||
}
|
||||
|
||||
already_AddRefed<nsIDOMNode> nsAccessNode::GetCurrentFocus()
|
||||
{
|
||||
nsCOMPtr<nsIPresShell> shell = GetPresShellFor(mDOMNode);
|
||||
NS_ENSURE_TRUE(shell, nsnull);
|
||||
nsCOMPtr<nsIDocument> doc = shell->GetDocument();
|
||||
NS_ENSURE_TRUE(doc, nsnull);
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> privateDOMWindow(do_QueryInterface(doc->GetWindow()));
|
||||
if (!privateDOMWindow) {
|
||||
return nsnull;
|
||||
}
|
||||
nsIFocusController *focusController = privateDOMWindow->GetRootFocusController();
|
||||
if (!focusController) {
|
||||
return nsnull;
|
||||
}
|
||||
nsCOMPtr<nsIDOMElement> focusedElement;
|
||||
focusController->GetFocusedElement(getter_AddRefs(focusedElement));
|
||||
if (!focusedElement) {
|
||||
return nsnull;
|
||||
}
|
||||
nsIDOMNode *focusedNode;
|
||||
focusedElement->QueryInterface(NS_GET_IID(nsIDOMNode), (void**)&focusedNode);
|
||||
if (!focusedNode) {
|
||||
// Document itself may have focus
|
||||
nsCOMPtr<nsIDOMWindowInternal> focusedWinInternal;
|
||||
focusController->GetFocusedWindow(getter_AddRefs(focusedWinInternal));
|
||||
if (focusedWinInternal) {
|
||||
nsCOMPtr<nsIDOMDocument> focusedDOMDocument;
|
||||
focusedWinInternal->GetDocument(getter_AddRefs(focusedDOMDocument));
|
||||
focusedDOMDocument->QueryInterface(NS_GET_IID(nsIDOMNode), (void**)&focusedNode);
|
||||
}
|
||||
}
|
||||
return focusedNode;
|
||||
}
|
||||
|
||||
@@ -149,6 +149,7 @@ class nsAccessNode: public nsIAccessNode, public nsPIAccessNode
|
||||
|
||||
static nsIDOMNode *gLastFocusedNode;
|
||||
static nsIAccessibilityService* GetAccService();
|
||||
already_AddRefed<nsIDOMNode> GetCurrentFocus();
|
||||
|
||||
protected:
|
||||
nsresult MakeAccessNode(nsIDOMNode *aNode, nsIAccessNode **aAccessNode);
|
||||
|
||||
@@ -164,6 +164,7 @@ ACCESSIBILITY_ATOM(type, "type")
|
||||
ACCESSIBILITY_ATOM(value, "value")
|
||||
|
||||
// ARIA (DHTML accessibility) attributes
|
||||
ACCESSIBILITY_ATOM(activedescendant, "activedescendant")
|
||||
ACCESSIBILITY_ATOM(checked, "checked")
|
||||
ACCESSIBILITY_ATOM(droppable, "droppable")
|
||||
ACCESSIBILITY_ATOM(expanded, "expanded")
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsDocAccessible.h"
|
||||
#include "nsRootAccessible.h"
|
||||
#include "nsAccessibilityAtoms.h"
|
||||
#include "nsAccessibleEventData.h"
|
||||
#include "nsIAccessibilityService.h"
|
||||
@@ -945,20 +945,35 @@ nsDocAccessible::AttributeChanged(nsIDocument *aDocument, nsIContent* aContent,
|
||||
}
|
||||
}
|
||||
else if (aNameSpaceID == kNameSpaceID_WAIProperties) {
|
||||
// DHTML accessibility attributes
|
||||
if (!HasRoleAttribute(aContent)) {
|
||||
// We don't care about DHTML state changes unless there is
|
||||
// a DHTML role set for the element
|
||||
// ARIA attributes
|
||||
if (aAttribute == nsAccessibilityAtoms::disabled ||
|
||||
aAttribute == nsAccessibilityAtoms::required ||
|
||||
aAttribute == nsAccessibilityAtoms::invalid) {
|
||||
// Universal boolean properties that don't require a role
|
||||
eventType = nsIAccessibleEvent::EVENT_STATE_CHANGE;
|
||||
}
|
||||
else if (aAttribute == nsAccessibilityAtoms::activedescendant) {
|
||||
// The activedescendant universal property redirects accessible focus events
|
||||
// to the element with the id that activedescendant points to
|
||||
nsCOMPtr<nsIDOMNode> currentFocus = GetCurrentFocus();
|
||||
if (SameCOMIdentity(currentFocus, aContent)) {
|
||||
nsRefPtr<nsRootAccessible> rootAcc = GetRootAccessible();
|
||||
if (rootAcc) {
|
||||
rootAcc->FireAccessibleFocusEvent(nsnull, currentFocus, nsnull, PR_TRUE);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (!HasRoleAttribute(aContent)) {
|
||||
// We don't care about these other ARIA attribute changes unless there is
|
||||
// an ARIA role set for the element
|
||||
// XXX we should check the role map to see if the changed
|
||||
// property is relevant for that particular role
|
||||
return;
|
||||
}
|
||||
if (aAttribute == nsAccessibilityAtoms::checked ||
|
||||
aAttribute == nsAccessibilityAtoms::expanded) {
|
||||
eventType = nsIAccessibleEvent::EVENT_STATE_CHANGE;
|
||||
}
|
||||
else if (aAttribute == nsAccessibilityAtoms::readonly ||
|
||||
aAttribute == nsAccessibilityAtoms::disabled ||
|
||||
aAttribute == nsAccessibilityAtoms::required ||
|
||||
aAttribute == nsAccessibilityAtoms::invalid) {
|
||||
aAttribute == nsAccessibilityAtoms::expanded ||
|
||||
aAttribute == nsAccessibilityAtoms::readonly) {
|
||||
eventType = nsIAccessibleEvent::EVENT_STATE_CHANGE;
|
||||
}
|
||||
else if (aAttribute == nsAccessibilityAtoms::valuenow) {
|
||||
|
||||
@@ -443,8 +443,6 @@ void nsRootAccessible::FireAccessibleFocusEvent(nsIAccessible *aAccessible,
|
||||
nsIDOMEvent *aFocusEvent,
|
||||
PRBool aForceEvent)
|
||||
{
|
||||
NS_ASSERTION(aAccessible, "Attempted to fire focus event for no accessible");
|
||||
|
||||
if (mCaretAccessible) {
|
||||
nsCOMPtr<nsIDOMNSEvent> nsevent(do_QueryInterface(aFocusEvent));
|
||||
if (nsevent) {
|
||||
@@ -465,23 +463,51 @@ void nsRootAccessible::FireAccessibleFocusEvent(nsIAccessible *aAccessible,
|
||||
}
|
||||
}
|
||||
|
||||
// Check for aaa:activedescendant, which changes which element has focus
|
||||
nsCOMPtr<nsIDOMNode> finalFocusNode = aNode;
|
||||
nsCOMPtr<nsIAccessible> finalFocusAccessible = aAccessible;
|
||||
nsCOMPtr<nsIContent> finalFocusContent = do_QueryInterface(aNode);
|
||||
if (finalFocusContent) {
|
||||
nsAutoString id;
|
||||
if (finalFocusContent->GetAttr(kNameSpaceID_WAIProperties, nsAccessibilityAtoms::activedescendant, id)) {
|
||||
nsCOMPtr<nsIDOMDocument> domDoc;
|
||||
aNode->GetOwnerDocument(getter_AddRefs(domDoc));
|
||||
if (!domDoc) {
|
||||
return;
|
||||
}
|
||||
nsCOMPtr<nsIDOMElement> relatedEl;
|
||||
domDoc->GetElementById(id, getter_AddRefs(relatedEl));
|
||||
finalFocusContent = do_QueryInterface(relatedEl);
|
||||
if (!finalFocusContent) {
|
||||
return;
|
||||
}
|
||||
GetAccService()->GetAccessibleFor(finalFocusNode, getter_AddRefs(finalFocusAccessible));
|
||||
// XXX Deal with case where finalFocusNode is not in parent chain of original true focus (aNode).
|
||||
// We need direction from ARIA spec -- should we just return because it's not valid?
|
||||
// Or should we put focus on the original container node?
|
||||
}
|
||||
}
|
||||
|
||||
// Fire focus only if it changes, but always fire focus events when aForceEvent == PR_TRUE
|
||||
if (gLastFocusedNode == aNode && !aForceEvent) {
|
||||
if (gLastFocusedNode == finalFocusNode && !aForceEvent) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIAccessible> privateAccessible =
|
||||
do_QueryInterface(aAccessible);
|
||||
do_QueryInterface(finalFocusAccessible);
|
||||
NS_ASSERTION(privateAccessible , "No nsPIAccessible for nsIAccessible");
|
||||
if (!privateAccessible) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Use focus events on DHTML menuitems to indicate when to fire menustart and menuend
|
||||
// Special dynamic content handling
|
||||
PRUint32 role = ROLE_NOTHING;
|
||||
aAccessible->GetFinalRole(&role);
|
||||
finalFocusAccessible->GetFinalRole(&role);
|
||||
if (role == ROLE_MENUITEM) {
|
||||
if (!mIsInDHTMLMenu) { // Entering menus
|
||||
PRUint32 naturalRole; // The natural role is the role that this type of element normally has
|
||||
aAccessible->GetRole(&naturalRole);
|
||||
finalFocusAccessible->GetRole(&naturalRole);
|
||||
if (role != naturalRole) { // Must be a DHTML menuitem
|
||||
FireToolkitEvent(nsIAccessibleEvent::EVENT_MENUSTART, this, nsnull);
|
||||
mIsInDHTMLMenu = ROLE_MENUITEM;
|
||||
@@ -494,41 +520,16 @@ void nsRootAccessible::FireAccessibleFocusEvent(nsIAccessible *aAccessible,
|
||||
}
|
||||
|
||||
NS_IF_RELEASE(gLastFocusedNode);
|
||||
gLastFocusedNode = aNode;
|
||||
gLastFocusedNode = finalFocusNode;
|
||||
NS_IF_ADDREF(gLastFocusedNode);
|
||||
|
||||
privateAccessible->FireToolkitEvent(nsIAccessibleEvent::EVENT_FOCUS,
|
||||
aAccessible, nsnull);
|
||||
finalFocusAccessible, nsnull);
|
||||
}
|
||||
|
||||
void nsRootAccessible::FireCurrentFocusEvent()
|
||||
{
|
||||
nsCOMPtr<nsIDOMWindow> domWin;
|
||||
GetWindow(getter_AddRefs(domWin));
|
||||
nsCOMPtr<nsPIDOMWindow> privateDOMWindow(do_QueryInterface(domWin));
|
||||
if (!privateDOMWindow) {
|
||||
return;
|
||||
}
|
||||
nsIFocusController *focusController = privateDOMWindow->GetRootFocusController();
|
||||
if (!focusController) {
|
||||
return;
|
||||
}
|
||||
nsCOMPtr<nsIDOMElement> focusedElement;
|
||||
focusController->GetFocusedElement(getter_AddRefs(focusedElement));
|
||||
nsCOMPtr<nsIDOMNode> focusedNode(do_QueryInterface(focusedElement));
|
||||
if (!focusedNode) {
|
||||
// Document itself may have focus
|
||||
nsCOMPtr<nsIDOMWindowInternal> focusedWinInternal;
|
||||
focusController->GetFocusedWindow(getter_AddRefs(focusedWinInternal));
|
||||
if (focusedWinInternal) {
|
||||
nsCOMPtr<nsIDOMDocument> focusedDOMDocument;
|
||||
focusedWinInternal->GetDocument(getter_AddRefs(focusedDOMDocument));
|
||||
focusedNode = do_QueryInterface(focusedDOMDocument);
|
||||
}
|
||||
if (!focusedNode) {
|
||||
return; // Could not get a focused document either
|
||||
}
|
||||
}
|
||||
nsCOMPtr<nsIDOMNode> focusedNode = GetCurrentFocus();
|
||||
|
||||
// Simulate a focus event so that we can reuse code that fires focus for container children like treeitems
|
||||
nsCOMPtr<nsIDOMDocumentEvent> docEvent = do_QueryInterface(mDocument);
|
||||
|
||||
@@ -91,6 +91,11 @@ class nsRootAccessible : public nsDocAccessibleWrap,
|
||||
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(NS_ROOTACCESSIBLE_IMPL_CID)
|
||||
|
||||
void FireAccessibleFocusEvent(nsIAccessible *focusAccessible,
|
||||
nsIDOMNode *focusNode,
|
||||
nsIDOMEvent *aFocusEvent,
|
||||
PRBool aForceEvent = PR_FALSE);
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsITimer> mFireFocusTimer;
|
||||
static void FireFocusCallback(nsITimer *aTimer, void *aClosure);
|
||||
@@ -102,10 +107,6 @@ class nsRootAccessible : public nsDocAccessibleWrap,
|
||||
nsIDOMNode* aTargetNode);
|
||||
static void GetTargetNode(nsIDOMEvent *aEvent, nsIDOMNode **aTargetNode);
|
||||
void TryFireEarlyLoadEvent(nsIDOMNode *aDocNode);
|
||||
void FireAccessibleFocusEvent(nsIAccessible *focusAccessible,
|
||||
nsIDOMNode *focusNode,
|
||||
nsIDOMEvent *aFocusEvent,
|
||||
PRBool aForceEvent = PR_FALSE);
|
||||
void FireCurrentFocusEvent();
|
||||
void GetChromeEventHandler(nsIDOMEventTarget **aChromeTarget);
|
||||
#ifdef MOZ_XUL
|
||||
|
||||
Reference in New Issue
Block a user