diff --git a/mozilla/accessible/src/base/nsAccessNode.cpp b/mozilla/accessible/src/base/nsAccessNode.cpp index 42d81c69a8d..c409a4639a3 100755 --- a/mozilla/accessible/src/base/nsAccessNode.cpp +++ b/mozilla/accessible/src/base/nsAccessNode.cpp @@ -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 aCache.Enumerate(ClearCacheEntry, nsnull); } +already_AddRefed nsAccessNode::GetCurrentFocus() +{ + nsCOMPtr shell = GetPresShellFor(mDOMNode); + NS_ENSURE_TRUE(shell, nsnull); + nsCOMPtr doc = shell->GetDocument(); + NS_ENSURE_TRUE(doc, nsnull); + nsCOMPtr privateDOMWindow(do_QueryInterface(doc->GetWindow())); + if (!privateDOMWindow) { + return nsnull; + } + nsIFocusController *focusController = privateDOMWindow->GetRootFocusController(); + if (!focusController) { + return nsnull; + } + nsCOMPtr 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 focusedWinInternal; + focusController->GetFocusedWindow(getter_AddRefs(focusedWinInternal)); + if (focusedWinInternal) { + nsCOMPtr focusedDOMDocument; + focusedWinInternal->GetDocument(getter_AddRefs(focusedDOMDocument)); + focusedDOMDocument->QueryInterface(NS_GET_IID(nsIDOMNode), (void**)&focusedNode); + } + } + return focusedNode; +} diff --git a/mozilla/accessible/src/base/nsAccessNode.h b/mozilla/accessible/src/base/nsAccessNode.h index 3a373a7f35a..a672277dd8c 100755 --- a/mozilla/accessible/src/base/nsAccessNode.h +++ b/mozilla/accessible/src/base/nsAccessNode.h @@ -149,6 +149,7 @@ class nsAccessNode: public nsIAccessNode, public nsPIAccessNode static nsIDOMNode *gLastFocusedNode; static nsIAccessibilityService* GetAccService(); + already_AddRefed GetCurrentFocus(); protected: nsresult MakeAccessNode(nsIDOMNode *aNode, nsIAccessNode **aAccessNode); diff --git a/mozilla/accessible/src/base/nsAccessibilityAtomList.h b/mozilla/accessible/src/base/nsAccessibilityAtomList.h index ceaa717f70c..2368be096ca 100755 --- a/mozilla/accessible/src/base/nsAccessibilityAtomList.h +++ b/mozilla/accessible/src/base/nsAccessibilityAtomList.h @@ -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") diff --git a/mozilla/accessible/src/base/nsDocAccessible.cpp b/mozilla/accessible/src/base/nsDocAccessible.cpp index f53dd2c730c..7300b7fa124 100644 --- a/mozilla/accessible/src/base/nsDocAccessible.cpp +++ b/mozilla/accessible/src/base/nsDocAccessible.cpp @@ -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 currentFocus = GetCurrentFocus(); + if (SameCOMIdentity(currentFocus, aContent)) { + nsRefPtr 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) { diff --git a/mozilla/accessible/src/base/nsRootAccessible.cpp b/mozilla/accessible/src/base/nsRootAccessible.cpp index 28179b44868..c86628b675b 100644 --- a/mozilla/accessible/src/base/nsRootAccessible.cpp +++ b/mozilla/accessible/src/base/nsRootAccessible.cpp @@ -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 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 finalFocusNode = aNode; + nsCOMPtr finalFocusAccessible = aAccessible; + nsCOMPtr finalFocusContent = do_QueryInterface(aNode); + if (finalFocusContent) { + nsAutoString id; + if (finalFocusContent->GetAttr(kNameSpaceID_WAIProperties, nsAccessibilityAtoms::activedescendant, id)) { + nsCOMPtr domDoc; + aNode->GetOwnerDocument(getter_AddRefs(domDoc)); + if (!domDoc) { + return; + } + nsCOMPtr 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 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 domWin; - GetWindow(getter_AddRefs(domWin)); - nsCOMPtr privateDOMWindow(do_QueryInterface(domWin)); - if (!privateDOMWindow) { - return; - } - nsIFocusController *focusController = privateDOMWindow->GetRootFocusController(); - if (!focusController) { - return; - } - nsCOMPtr focusedElement; - focusController->GetFocusedElement(getter_AddRefs(focusedElement)); - nsCOMPtr focusedNode(do_QueryInterface(focusedElement)); - if (!focusedNode) { - // Document itself may have focus - nsCOMPtr focusedWinInternal; - focusController->GetFocusedWindow(getter_AddRefs(focusedWinInternal)); - if (focusedWinInternal) { - nsCOMPtr focusedDOMDocument; - focusedWinInternal->GetDocument(getter_AddRefs(focusedDOMDocument)); - focusedNode = do_QueryInterface(focusedDOMDocument); - } - if (!focusedNode) { - return; // Could not get a focused document either - } - } + nsCOMPtr focusedNode = GetCurrentFocus(); // Simulate a focus event so that we can reuse code that fires focus for container children like treeitems nsCOMPtr docEvent = do_QueryInterface(mDocument); diff --git a/mozilla/accessible/src/base/nsRootAccessible.h b/mozilla/accessible/src/base/nsRootAccessible.h index 50986a1c8ed..c6ac1406f70 100644 --- a/mozilla/accessible/src/base/nsRootAccessible.h +++ b/mozilla/accessible/src/base/nsRootAccessible.h @@ -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 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