diff --git a/accessible/src/base/nsAccessible.cpp b/accessible/src/base/nsAccessible.cpp index 27b1c777d30b..016579e201c1 100644 --- a/accessible/src/base/nsAccessible.cpp +++ b/accessible/src/base/nsAccessible.cpp @@ -626,11 +626,19 @@ NS_IMETHODIMP nsAccessible::GetState(PRUint32 *aState) if (content->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::disabled)) { *aState |= STATE_UNAVAILABLE; } - else if ((!mRoleMapEntry && content->IsContentOfType(nsIContent::eELEMENT)) || - content->IsFocusable()) { - // Default state for element accessible is focusable unless role manually set - // Subclasses of nsAccessible will clear focusable state if necessary - *aState |= STATE_FOCUSABLE; + else if (content->IsContentOfType(nsIContent::eELEMENT)) { + if (!mRoleMapEntry) { + // Default state for element accessible is focusable unless role is manually set + // Subclasses of nsAccessible will clear focusable state if necessary + *aState |= STATE_FOCUSABLE; + } + else { + nsIFrame *frame = GetFrame(); + if (frame && frame->IsFocusable()) { + *aState |= STATE_FOCUSABLE; + } + } + if (gLastFocusedNode == mDOMNode) { *aState |= STATE_FOCUSED; } @@ -1358,14 +1366,15 @@ nsRoleMapEntry nsAccessible::gWAIRoleMap[] = // Definition of nsRoleMapEntry and nsStateMapEntry contains comments explaining this table. {"alert", ROLE_ALERT, eAggregateSubtree, 0, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, {"button", ROLE_PUSHBUTTON, eAggregateSubtree, 0, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, - {"checkbox", ROLE_CHECKBUTTON, eAggregateSubtree, 0, {"checked", "true", STATE_CHECKED}, {"readonly", 0, STATE_READONLY}, {0, 0, 0}}, - {"checkbox-tristate", ROLE_CHECKBUTTON, eAggregateSubtree, 0, {"checked", "true", STATE_CHECKED}, {"checked", "mixed", STATE_MIXED}, {"readonly", 0, STATE_READONLY}}, + {"checkbox", ROLE_CHECKBUTTON, eAggregateSubtree, 0, {"checked", 0, STATE_CHECKED}, {"readonly", 0, STATE_READONLY}, {0, 0, 0}}, + {"checkbox-tristate", ROLE_CHECKBUTTON, eAggregateSubtree, 0, {"checked", 0, STATE_CHECKED}, {"checked", "mixed", STATE_MIXED}, {"readonly", 0, STATE_READONLY}}, {"columnheader", ROLE_COLUMNHEADER, eAggregateSubtree, STATE_SELECTABLE, {"selected", 0, STATE_SELECTED}, {"readonly", 0, STATE_READONLY}, {0, 0, 0}}, {"icon", ROLE_ICON, eAggregateSubtree, 0, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, + {"list", ROLE_LIST, eTitleOnly, 0, {"readonly", 0, STATE_READONLY}, {"multiselect", 0, STATE_EXTSELECTABLE | STATE_MULTISELECTABLE}, {0, 0, 0}}, + {"listitem", ROLE_LISTITEM, eAggregateSubtree, STATE_SELECTABLE, {"checked", 0, STATE_CHECKED}, {0, 0, 0}, {0, 0, 0}}, {"menu", ROLE_MENUPOPUP, eTitleOnly, 0, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, {"menubar", ROLE_MENUBAR, eTitleOnly, 0, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, - {"menuitem", ROLE_MENUITEM, eAggregateSubtree, 0, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, - {"menuitem-checkbox", ROLE_MENUITEM, eAggregateSubtree, 0, {"checked", "true", STATE_CHECKED}, {0, 0, 0}, {0, 0, 0}}, + {"menuitem", ROLE_MENUITEM, eAggregateSubtree, 0, {"checked", 0, STATE_CHECKED}, {0, 0, 0}, {0, 0, 0}}, {"grid", ROLE_TABLE, eTitleOnly, STATE_FOCUSABLE, {"readonly", 0, STATE_READONLY}, {"multiselect", 0, STATE_EXTSELECTABLE | STATE_MULTISELECTABLE}, {0, 0, 0}}, {"gridcell", ROLE_CELL, eAggregateSubtree, STATE_SELECTABLE, {"selected", 0, STATE_SELECTED}, {"readonly", 0, STATE_READONLY}, {0, 0, 0}}, {"option", ROLE_LISTITEM, eAggregateSubtree, STATE_SELECTABLE, {"selected", 0, STATE_SELECTED}, {0, 0, 0}, {0, 0, 0}}, @@ -1378,7 +1387,7 @@ nsRoleMapEntry nsAccessible::gWAIRoleMap[] = {"submit", ROLE_PUSHBUTTON, eAggregateSubtree, STATE_DEFAULT, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, {"textarea", ROLE_TEXT, eTitleOnly, 0, {"readonly", 0, STATE_READONLY}, {0, 0, 0}, {0, 0, 0}}, // XXX Use ext state STATE_MULTI_LINE {"textfield", ROLE_TEXT, eTitleOnly, 0, {"readonly", 0, STATE_READONLY}, {0, 0, 0}, {0, 0, 0}}, // XXX Use ext state STATE_SINGLE_LINE - {"toolbar-icon", ROLE_PUSHBUTTON, eAggregateSubtree, 0, {"checked", "true", STATE_PRESSED}, {0, 0, 0}, {0, 0, 0}}, + {"toolbar-icon", ROLE_PUSHBUTTON, eAggregateSubtree, 0, {"checked", 0, STATE_PRESSED}, {0, 0, 0}, {0, 0, 0}}, {"tree", ROLE_OUTLINE, eTitleOnly, 0, {"readonly", 0, STATE_READONLY}, {"multiselect", 0, STATE_EXTSELECTABLE | STATE_MULTISELECTABLE}, {0, 0, 0}}, {"treeitem", ROLE_OUTLINEITEM, eAggregateSubtree, STATE_SELECTABLE, {"selected", 0, STATE_SELECTED}, {0, 0, 0}, {0, 0, 0}}, {nsnull, ROLE_NOTHING, eTitleOnly, 0, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, @@ -1404,6 +1413,9 @@ nsStateMapEntry nsAccessible::gDisabledStateMap = {"disabled", 0, STATE_UNAVAILA NS_IMETHODIMP nsAccessible::GetFinalRole(PRUint32 *aRole) { + if (!mDOMNode) { + return NS_ERROR_FAILURE; // Node already shut down + } if (mRoleMapEntry) { *aRole = mRoleMapEntry->role; if (*aRole != ROLE_NOTHING) { @@ -1413,48 +1425,83 @@ NS_IMETHODIMP nsAccessible::GetFinalRole(PRUint32 *aRole) return GetRole(aRole); } -PRUint32 nsAccessible::MappedAttrState(nsIContent *aContent, nsStateMapEntry *aStateMapEntry) +PRUint32 nsAccessible::MappedAttrState(nsIContent *aContent, PRUint32 aStartState, + nsStateMapEntry *aStateMapEntry) { if (!aStateMapEntry->attributeName) { - return 0; + return aStartState; } nsAutoString attribValue; nsCOMPtr 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; + attribValue)) { + if (!aStateMapEntry->attributeValue) { + // No attribute value map specified in state map entry indicates state cleared + if (attribValue.EqualsLiteral("false")) { + return aStartState & ~aStateMapEntry->state; + } + return aStartState | aStateMapEntry->state; + } + if (NS_ConvertUTF16toUTF8(attribValue).Equals(aStateMapEntry->attributeValue)) { + return aStartState | aStateMapEntry->state; + } } - return 0; + return aStartState; } NS_IMETHODIMP nsAccessible::GetFinalState(PRUint32 *aState) { + if (!mDOMNode) { + return NS_ERROR_FAILURE; // Node already shut down + } nsresult rv = GetState(aState); if (NS_FAILED(rv) || !mRoleMapEntry) { return rv; } - *aState &= ~STATE_READONLY; // Once DHTML role is used, we're only readonly if DHTML readonly used + PRUint32 finalState = *aState; + finalState &= ~STATE_READONLY; // Once DHTML role is used, we're only readonly if DHTML readonly used + + if (gLastFocusedNode == mDOMNode && (mRoleMapEntry->state & STATE_SELECTABLE)) { + // If we're focused and selectable and not inside a multiselect, + // then we're also selected + nsCOMPtr container = this; + PRUint32 containerState = 0, containerRole; + while (0 == (containerState & STATE_MULTISELECTABLE)) { + nsCOMPtr current; + current.swap(container); + current->GetParent(getter_AddRefs(container)); + if (!container || (NS_SUCCEEDED(container->GetFinalRole(&containerRole)) && + containerRole == ROLE_PANE)) { + finalState |= STATE_SELECTED; + break; + } + container->GetFinalState(&containerState); + } + } nsCOMPtr 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 - + finalState |= mRoleMapEntry->state; + finalState = MappedAttrState(content, finalState, &mRoleMapEntry->attributeMap1); + finalState = MappedAttrState(content, finalState, &mRoleMapEntry->attributeMap2); + finalState = MappedAttrState(content, finalState, &mRoleMapEntry->attributeMap3); + // Anything can be disabled/unavailable + finalState = MappedAttrState(content, finalState, &gDisabledStateMap); } + + *aState = finalState; return rv; } NS_IMETHODIMP nsAccessible::GetFinalValue(nsAString& aValue) { + if (!mDOMNode) { + return NS_ERROR_FAILURE; // Node already shut down + } if (mRoleMapEntry) { nsCOMPtr content(do_QueryInterface(mDOMNode)); if (content && diff --git a/accessible/src/base/nsAccessible.h b/accessible/src/base/nsAccessible.h index 133facf9b5e3..4a036e1e3727 100644 --- a/accessible/src/base/nsAccessible.h +++ b/accessible/src/base/nsAccessible.h @@ -119,7 +119,7 @@ public: static PRBool IsCorrectFrameType(nsIFrame* aFrame, nsIAtom* aAtom); protected: - PRUint32 MappedAttrState(nsIContent *aContent, nsStateMapEntry *aStateMapEntry); + PRUint32 MappedAttrState(nsIContent *aContent, PRUint32 aStartState,nsStateMapEntry *aStateMapEntry); virtual nsIFrame* GetBoundsFrame(); virtual void GetBoundsRect(nsRect& aRect, nsIFrame** aRelativeFrame); PRBool IsPartiallyVisible(PRBool *aIsOffscreen); diff --git a/accessible/src/base/nsRootAccessible.cpp b/accessible/src/base/nsRootAccessible.cpp index fd63c0b9bf5c..37cc780b1254 100644 --- a/accessible/src/base/nsRootAccessible.cpp +++ b/accessible/src/base/nsRootAccessible.cpp @@ -345,17 +345,10 @@ void nsRootAccessible::FireAccessibleFocusEvent(nsIAccessible *aAccessible, nsID return; } - // Special DHTML handling - PRBool isHTML; // If it is HTML we will check extra DHTML accesibility logic - nsCOMPtr content(do_QueryInterface(aNode)); - if (content) { - isHTML = content->IsContentOfType(nsIContent::eHTML); - } - else { - nsCOMPtr htmlDoc(do_QueryInterface(aNode)); - isHTML = (htmlDoc != nsnull); - } - if (isHTML) { + // Special dynamic content handling + PRUint32 naturalRole; // The natural role is the role that this type of element normally has + aAccessible->GetRole(&naturalRole); + if (role != naturalRole) { // xhtml2:role is being used to override element's natural role FireDHTMLFocusRelatedEvents(aAccessible, role); }