From 3c1576fac109961260e4cb6bce0293a1e63b8e49 Mon Sep 17 00:00:00 2001 From: Alexander Surkov Date: Thu, 19 Feb 2009 14:56:19 +0800 Subject: [PATCH 01/26] Bug 463645 - container-live object attribute should reflect role as well, r=davidb, marcoz --- accessible/src/base/nsARIAMap.cpp | 75 +++++++++++++++++++ accessible/src/base/nsARIAMap.h | 11 +++ accessible/src/base/nsAccUtils.cpp | 37 ++++++++- accessible/src/base/nsAccUtils.h | 6 ++ accessible/src/base/nsAccessibilityAtomList.h | 1 + accessible/src/base/nsAccessible.cpp | 18 ++++- .../tests/mochitest/test_objectattrs.html | 28 ++++++- 7 files changed, 165 insertions(+), 11 deletions(-) diff --git a/accessible/src/base/nsARIAMap.cpp b/accessible/src/base/nsARIAMap.cpp index 4b4bacedf06a..ea6ced1f7898 100644 --- a/accessible/src/base/nsARIAMap.cpp +++ b/accessible/src/base/nsARIAMap.cpp @@ -65,6 +65,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = nsIAccessibleRole::ROLE_ALERT, eNoValue, eNoAction, + eNoLiveAttr, kNoReqStates, kEndEntry }, @@ -73,6 +74,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = nsIAccessibleRole::ROLE_DIALOG, eNoValue, eNoAction, + eNoLiveAttr, kNoReqStates, kEndEntry }, @@ -81,6 +83,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = nsIAccessibleRole::ROLE_APPLICATION, eNoValue, eNoAction, + eNoLiveAttr, kNoReqStates, kEndEntry }, @@ -97,6 +100,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = nsIAccessibleRole::ROLE_PUSHBUTTON, eNoValue, eClickAction, + eNoLiveAttr, kNoReqStates, {&nsAccessibilityAtoms::aria_pressed, kBoolState, nsIAccessibleStates::STATE_PRESSED}, {&nsAccessibilityAtoms::aria_pressed, "mixed", nsIAccessibleStates::STATE_MIXED}, @@ -107,6 +111,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = nsIAccessibleRole::ROLE_CHECKBUTTON, eNoValue, eCheckUncheckAction, + eNoLiveAttr, nsIAccessibleStates::STATE_CHECKABLE, {&nsAccessibilityAtoms::aria_checked, kBoolState, nsIAccessibleStates::STATE_CHECKED}, {&nsAccessibilityAtoms::aria_checked, "mixed", nsIAccessibleStates::STATE_MIXED}, @@ -118,6 +123,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = nsIAccessibleRole::ROLE_COLUMNHEADER, eNoValue, eNoAction, + eNoLiveAttr, kNoReqStates, {&nsAccessibilityAtoms::aria_selected, kBoolState, nsIAccessibleStates::STATE_SELECTED | nsIAccessibleStates::STATE_SELECTABLE}, {&nsAccessibilityAtoms::aria_selected, "false", nsIAccessibleStates::STATE_SELECTABLE}, @@ -129,6 +135,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = nsIAccessibleRole::ROLE_COMBOBOX, eHasValueMinMax, eOpenCloseAction, + eNoLiveAttr, nsIAccessibleStates::STATE_COLLAPSED | nsIAccessibleStates::STATE_HASPOPUP, // Manually map EXT_STATE_SUPPORTS_AUTOCOMPLETION aria-autocomplete {&nsAccessibilityAtoms::aria_readonly, kBoolState, nsIAccessibleStates::STATE_READONLY}, @@ -139,6 +146,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = nsIAccessibleRole::ROLE_DIALOG, eNoValue, eNoAction, + eNoLiveAttr, kNoReqStates, kEndEntry }, @@ -155,6 +163,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = nsIAccessibleRole::ROLE_TABLE, eNoValue, eNoAction, + eNoLiveAttr, nsIAccessibleStates::STATE_FOCUSABLE, {&nsAccessibilityAtoms::aria_multiselectable, kBoolState, nsIAccessibleStates::STATE_MULTISELECTABLE | nsIAccessibleStates::STATE_EXTSELECTABLE}, {&nsAccessibilityAtoms::aria_readonly, kBoolState, nsIAccessibleStates::STATE_READONLY}, @@ -165,6 +174,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = nsIAccessibleRole::ROLE_GRID_CELL, eNoValue, eNoAction, + eNoLiveAttr, kNoReqStates, {&nsAccessibilityAtoms::aria_selected, kBoolState, nsIAccessibleStates::STATE_SELECTED | nsIAccessibleStates::STATE_SELECTABLE}, {&nsAccessibilityAtoms::aria_selected, "false", nsIAccessibleStates::STATE_SELECTABLE}, @@ -176,6 +186,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = nsIAccessibleRole::ROLE_GROUPING, eNoValue, eNoAction, + eNoLiveAttr, kNoReqStates, kEndEntry }, @@ -184,6 +195,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = nsIAccessibleRole::ROLE_HEADING, eNoValue, eNoAction, + eNoLiveAttr, kNoReqStates, kEndEntry }, @@ -192,6 +204,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = nsIAccessibleRole::ROLE_GRAPHIC, eNoValue, eNoAction, + eNoLiveAttr, kNoReqStates, kEndEntry }, @@ -200,6 +213,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = nsIAccessibleRole::ROLE_LABEL, eNoValue, eNoAction, + eNoLiveAttr, kNoReqStates, kEndEntry }, @@ -208,6 +222,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = nsIAccessibleRole::ROLE_LINK, eNoValue, eJumpAction, + eNoLiveAttr, nsIAccessibleStates::STATE_LINKED, kEndEntry }, @@ -216,6 +231,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = nsIAccessibleRole::ROLE_LIST, eNoValue, eNoAction, + eNoLiveAttr, nsIAccessibleStates::STATE_READONLY, {&nsAccessibilityAtoms::aria_multiselectable, kBoolState, nsIAccessibleStates::STATE_MULTISELECTABLE | nsIAccessibleStates::STATE_EXTSELECTABLE}, kEndEntry @@ -225,6 +241,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = nsIAccessibleRole::ROLE_LISTBOX, eNoValue, eNoAction, + eNoLiveAttr, kNoReqStates, {&nsAccessibilityAtoms::aria_readonly, kBoolState, nsIAccessibleStates::STATE_READONLY}, {&nsAccessibilityAtoms::aria_multiselectable, kBoolState, nsIAccessibleStates::STATE_MULTISELECTABLE | nsIAccessibleStates::STATE_EXTSELECTABLE}, @@ -235,6 +252,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = nsIAccessibleRole::ROLE_LISTITEM, eNoValue, eNoAction, // XXX: should depend on state, parent accessible + eNoLiveAttr, nsIAccessibleStates::STATE_READONLY, {&nsAccessibilityAtoms::aria_selected, kBoolState, nsIAccessibleStates::STATE_SELECTED | nsIAccessibleStates::STATE_SELECTABLE}, {&nsAccessibilityAtoms::aria_selected, "false", nsIAccessibleStates::STATE_SELECTABLE}, @@ -243,11 +261,30 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = {&nsAccessibilityAtoms::aria_checked, "false", nsIAccessibleStates::STATE_CHECKABLE}, kEndEntry }, + { + "log", + nsIAccessibleRole::ROLE_NOTHING, + eNoValue, + eNoAction, + ePoliteLiveAttr, + kNoReqStates, + kEndEntry + }, + { + "marquee", + nsIAccessibleRole::ROLE_NOTHING, + eNoValue, + eNoAction, + eOffLiveAttr, + kNoReqStates, + kEndEntry + }, { "math", nsIAccessibleRole::ROLE_FLAT_EQUATION, eNoValue, eNoAction, + eNoLiveAttr, kNoReqStates, kEndEntry }, @@ -257,6 +294,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = eNoValue, eNoAction, // XXX: technically accessibles of menupopup role haven't // any action, but menu can be open or close. + eNoLiveAttr, kNoReqStates, kEndEntry }, @@ -265,6 +303,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = nsIAccessibleRole::ROLE_MENUBAR, eNoValue, eNoAction, + eNoLiveAttr, kNoReqStates, kEndEntry }, @@ -273,6 +312,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = nsIAccessibleRole::ROLE_MENUITEM, eNoValue, eClickAction, + eNoLiveAttr, kNoReqStates, {&nsAccessibilityAtoms::aria_checked, kBoolState, nsIAccessibleStates::STATE_CHECKED | nsIAccessibleStates::STATE_CHECKABLE}, {&nsAccessibilityAtoms::aria_checked, "mixed", nsIAccessibleStates::STATE_MIXED | nsIAccessibleStates::STATE_CHECKABLE}, @@ -284,6 +324,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = nsIAccessibleRole::ROLE_CHECK_MENU_ITEM, eNoValue, eClickAction, + eNoLiveAttr, nsIAccessibleStates::STATE_CHECKABLE, {&nsAccessibilityAtoms::aria_checked, kBoolState, nsIAccessibleStates::STATE_CHECKED }, {&nsAccessibilityAtoms::aria_checked, "mixed", nsIAccessibleStates::STATE_MIXED}, @@ -294,6 +335,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = nsIAccessibleRole::ROLE_RADIO_MENU_ITEM, eNoValue, eClickAction, + eNoLiveAttr, nsIAccessibleStates::STATE_CHECKABLE, {&nsAccessibilityAtoms::aria_checked, kBoolState, nsIAccessibleStates::STATE_CHECKED }, kEndEntry @@ -303,6 +345,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = nsIAccessibleRole::ROLE_OPTION, eNoValue, eSelectAction, + eNoLiveAttr, kNoReqStates, {&nsAccessibilityAtoms::aria_selected, kBoolState, nsIAccessibleStates::STATE_SELECTED | nsIAccessibleStates::STATE_SELECTABLE}, {&nsAccessibilityAtoms::aria_selected, "false", nsIAccessibleStates::STATE_SELECTABLE}, @@ -316,6 +359,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = nsIAccessibleRole::ROLE_NOTHING, eNoValue, eNoAction, + eNoLiveAttr, kNoReqStates, kEndEntry }, @@ -324,6 +368,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = nsIAccessibleRole::ROLE_PROGRESSBAR, eHasValueMinMax, eNoAction, + eNoLiveAttr, nsIAccessibleStates::STATE_READONLY, kEndEntry }, @@ -332,6 +377,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = nsIAccessibleRole::ROLE_RADIOBUTTON, eNoValue, eSelectAction, + eNoLiveAttr, kNoReqStates, {&nsAccessibilityAtoms::aria_checked, kBoolState, nsIAccessibleStates::STATE_CHECKED}, kEndEntry @@ -341,6 +387,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = nsIAccessibleRole::ROLE_GROUPING, eNoValue, eNoAction, + eNoLiveAttr, kNoReqStates, kEndEntry }, @@ -349,6 +396,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = nsIAccessibleRole::ROLE_PANE, eNoValue, eNoAction, + eNoLiveAttr, kNoReqStates, kEndEntry }, @@ -357,6 +405,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = nsIAccessibleRole::ROLE_ROW, eNoValue, eNoAction, + eNoLiveAttr, kNoReqStates, {&nsAccessibilityAtoms::aria_selected, kBoolState, nsIAccessibleStates::STATE_SELECTED | nsIAccessibleStates::STATE_SELECTABLE}, {&nsAccessibilityAtoms::aria_selected, "false", nsIAccessibleStates::STATE_SELECTABLE}, @@ -367,6 +416,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = nsIAccessibleRole::ROLE_ROWHEADER, eNoValue, eNoAction, + eNoLiveAttr, kNoReqStates, {&nsAccessibilityAtoms::aria_selected, kBoolState, nsIAccessibleStates::STATE_SELECTED | nsIAccessibleStates::STATE_SELECTABLE}, {&nsAccessibilityAtoms::aria_selected, "false", nsIAccessibleStates::STATE_SELECTABLE}, @@ -378,6 +428,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = nsIAccessibleRole::ROLE_SECTION, eNoValue, eNoAction, + eNoLiveAttr, kNoReqStates, kEndEntry }, @@ -386,6 +437,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = nsIAccessibleRole::ROLE_SEPARATOR, eNoValue, eNoAction, + eNoLiveAttr, kNoReqStates, kEndEntry }, @@ -394,6 +446,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = nsIAccessibleRole::ROLE_SLIDER, eHasValueMinMax, eNoAction, + eNoLiveAttr, kNoReqStates, {&nsAccessibilityAtoms::aria_readonly, kBoolState, nsIAccessibleStates::STATE_READONLY}, kEndEntry @@ -403,6 +456,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = nsIAccessibleRole::ROLE_SPINBUTTON, eHasValueMinMax, eNoAction, + eNoLiveAttr, kNoReqStates, {&nsAccessibilityAtoms::aria_readonly, kBoolState, nsIAccessibleStates::STATE_READONLY}, kEndEntry @@ -412,6 +466,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = nsIAccessibleRole::ROLE_STATUSBAR, eNoValue, eNoAction, + eNoLiveAttr, kNoReqStates, kEndEntry }, @@ -420,6 +475,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = nsIAccessibleRole::ROLE_PAGETAB, eNoValue, eSwitchAction, + eNoLiveAttr, kNoReqStates, kEndEntry }, @@ -428,6 +484,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = nsIAccessibleRole::ROLE_PAGETABLIST, eNoValue, eNoAction, + ePoliteLiveAttr, kNoReqStates, kEndEntry }, @@ -436,6 +493,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = nsIAccessibleRole::ROLE_PROPERTYPAGE, eNoValue, eNoAction, + eNoLiveAttr, kNoReqStates, kEndEntry }, @@ -444,6 +502,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = nsIAccessibleRole::ROLE_ENTRY, eNoValue, eActivateAction, + eNoLiveAttr, kNoReqStates, // Manually map EXT_STATE_SINGLE_LINE and EXT_STATE_MULTI_LINE FROM aria-multiline // Manually map EXT_STATE_SUPPORTS_AUTOCOMPLETION aria-autocomplete @@ -452,11 +511,21 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = {&nsAccessibilityAtoms::aria_readonly, kBoolState, nsIAccessibleStates::STATE_READONLY}, kEndEntry }, + { + "timer", + nsIAccessibleRole::ROLE_NOTHING, + eNoValue, + eNoAction, + eOffLiveAttr, + kNoReqStates, + kEndEntry + }, { "toolbar", nsIAccessibleRole::ROLE_TOOLBAR, eNoValue, eNoAction, + eNoLiveAttr, kNoReqStates, kEndEntry }, @@ -465,6 +534,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = nsIAccessibleRole::ROLE_TOOLTIP, eNoValue, eNoAction, + eNoLiveAttr, kNoReqStates, kEndEntry }, @@ -473,6 +543,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = nsIAccessibleRole::ROLE_OUTLINE, eNoValue, eNoAction, + eNoLiveAttr, kNoReqStates, {&nsAccessibilityAtoms::aria_readonly, kBoolState, nsIAccessibleStates::STATE_READONLY}, {&nsAccessibilityAtoms::aria_multiselectable, kBoolState, nsIAccessibleStates::STATE_MULTISELECTABLE | nsIAccessibleStates::STATE_EXTSELECTABLE}, @@ -483,6 +554,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = nsIAccessibleRole::ROLE_TREE_TABLE, eNoValue, eNoAction, + eNoLiveAttr, kNoReqStates, {&nsAccessibilityAtoms::aria_readonly, kBoolState, nsIAccessibleStates::STATE_READONLY}, {&nsAccessibilityAtoms::aria_multiselectable, kBoolState, nsIAccessibleStates::STATE_MULTISELECTABLE | nsIAccessibleStates::STATE_EXTSELECTABLE}, @@ -494,6 +566,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = eNoValue, eActivateAction, // XXX: should expose second 'expand/collapse' action based // on states + eNoLiveAttr, kNoReqStates, {&nsAccessibilityAtoms::aria_selected, kBoolState, nsIAccessibleStates::STATE_SELECTED | nsIAccessibleStates::STATE_SELECTABLE}, {&nsAccessibilityAtoms::aria_selected, "false", nsIAccessibleStates::STATE_SELECTABLE}, @@ -510,6 +583,7 @@ nsRoleMapEntry nsARIAMap::gLandmarkRoleMap = { nsIAccessibleRole::ROLE_NOTHING, eNoValue, eNoAction, + eNoLiveAttr, kNoReqStates, kEndEntry }; @@ -519,6 +593,7 @@ nsRoleMapEntry nsARIAMap::gEmptyRoleMap = { nsIAccessibleRole::ROLE_NOTHING, eNoValue, eNoAction, + eNoLiveAttr, kNoReqStates, kEndEntry }; diff --git a/accessible/src/base/nsARIAMap.h b/accessible/src/base/nsARIAMap.h index 2e9fada0ca80..18eed91f62ba 100644 --- a/accessible/src/base/nsARIAMap.h +++ b/accessible/src/base/nsARIAMap.h @@ -63,6 +63,13 @@ enum EActionRule eSwitchAction }; +enum ELiveAttrRule +{ + eNoLiveAttr, + eOffLiveAttr, + ePoliteLiveAttr +}; + // ARIA attribute characteristic masks, grow as needed /** @@ -115,6 +122,10 @@ struct nsRoleMapEntry // Action mapping rule, how to expose nsIAccessible action EActionRule actionRule; + // 'live' and 'container-live' object attributes mapping rule: how to expose + // these object attributes if ARIA 'live' attribute is missed. + ELiveAttrRule liveAttRule; + // Automatic state mapping rule: always include in nsIAccessibleStates PRUint32 state; // or kNoReqStates if no nsIAccessibleStates are automatic for this role. diff --git a/accessible/src/base/nsAccUtils.cpp b/accessible/src/base/nsAccUtils.cpp index a87388e94fcc..2119145767e4 100644 --- a/accessible/src/base/nsAccUtils.cpp +++ b/accessible/src/base/nsAccUtils.cpp @@ -272,21 +272,38 @@ nsAccUtils::SetLiveContainerAttributes(nsIPersistentProperties *aAttributes, nsAutoString atomic, live, relevant, busy; nsIContent *ancestor = aStartContent; while (ancestor) { + + // container-relevant attribute if (relevant.IsEmpty() && nsAccUtils::HasDefinedARIAToken(ancestor, nsAccessibilityAtoms::aria_relevant) && ancestor->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_relevant, relevant)) SetAccAttr(aAttributes, nsAccessibilityAtoms::containerRelevant, relevant); - if (live.IsEmpty() && - nsAccUtils::HasDefinedARIAToken(ancestor, nsAccessibilityAtoms::aria_live) && - ancestor->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_live, live)) - SetAccAttr(aAttributes, nsAccessibilityAtoms::containerLive, live); + // container-live attribute + if (live.IsEmpty()) { + if (nsAccUtils::HasDefinedARIAToken(ancestor, + nsAccessibilityAtoms::aria_live)) { + ancestor->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_live, + live); + SetAccAttr(aAttributes, nsAccessibilityAtoms::containerLive, live); + } else { + nsCOMPtr node(do_QueryInterface(ancestor)); + nsRoleMapEntry *role = GetRoleMapEntry(node); + if (role) { + nsAutoString live; + GetLiveAttrValue(role->liveAttRule, live); + SetAccAttr(aAttributes, nsAccessibilityAtoms::containerLive, live); + } + } + } + // container-atomic attribute if (atomic.IsEmpty() && nsAccUtils::HasDefinedARIAToken(ancestor, nsAccessibilityAtoms::aria_atomic) && ancestor->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_atomic, atomic)) SetAccAttr(aAttributes, nsAccessibilityAtoms::containerAtomic, atomic); + // container-busy attribute if (busy.IsEmpty() && nsAccUtils::HasDefinedARIAToken(ancestor, nsAccessibilityAtoms::aria_busy) && ancestor->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_busy, busy)) @@ -669,6 +686,18 @@ nsAccUtils::GetAttributeCharacteristics(nsIAtom* aAtom) return 0; } +void +nsAccUtils::GetLiveAttrValue(PRUint32 aRule, nsAString& aValue) +{ + switch (aRule) { + case eOffLiveAttr: + aValue = NS_LITERAL_STRING("off"); + break; + case ePoliteLiveAttr: + aValue = NS_LITERAL_STRING("polite"); + break; + } +} #ifdef DEBUG_A11Y diff --git a/accessible/src/base/nsAccUtils.h b/accessible/src/base/nsAccUtils.h index 820ac2d62e14..522891092c24 100644 --- a/accessible/src/base/nsAccUtils.h +++ b/accessible/src/base/nsAccUtils.h @@ -270,6 +270,12 @@ public: */ static PRUint8 GetAttributeCharacteristics(nsIAtom* aAtom); + /** + * Return the 'live' or 'container-live' object attribute value from the given + * ELiveAttrRule constant. + */ + static void GetLiveAttrValue(PRUint32 aRule, nsAString& aValue); + /** * Query nsAccessNode from the given nsIAccessible. */ diff --git a/accessible/src/base/nsAccessibilityAtomList.h b/accessible/src/base/nsAccessibilityAtomList.h index 1f10c5655e60..8ef576e755db 100644 --- a/accessible/src/base/nsAccessibilityAtomList.h +++ b/accessible/src/base/nsAccessibilityAtomList.h @@ -256,6 +256,7 @@ ACCESSIBILITY_ATOM(containerBusy, "container-busy") ACCESSIBILITY_ATOM(containerLive, "container-live") ACCESSIBILITY_ATOM(containerRelevant, "container-relevant") ACCESSIBILITY_ATOM(level, "level") +ACCESSIBILITY_ATOM(live, "live") ACCESSIBILITY_ATOM(lineNumber, "line-number") ACCESSIBILITY_ATOM(posinset, "posinset") ACCESSIBILITY_ATOM(setsize, "setsize") diff --git a/accessible/src/base/nsAccessible.cpp b/accessible/src/base/nsAccessible.cpp index cc26ddd496b1..d39d86390559 100644 --- a/accessible/src/base/nsAccessible.cpp +++ b/accessible/src/base/nsAccessible.cpp @@ -1925,8 +1925,9 @@ NS_IMETHODIMP nsAccessible::GetFinalRole(PRUint32 *aRole) // gLandmarkRoleMap: can use role of accessible class impl // gEmptyRoleMap and all others: cannot use role of accessible class impl - if (mRoleMapEntry != &nsARIAMap::gLandmarkRoleMap) { - // We can now expose ROLE_NOTHING when there is a role map entry, which + if (mRoleMapEntry->role != nsIAccessibleRole::ROLE_NOTHING) { + // We can now expose ROLE_NOTHING when there is a role map entry or used + // role is nothing, which // will cause ATK to use ROLE_UNKNOWN and MSAA to use a BSTR role with // the ARIA role or element's tag. In either case the AT can also use // the object attributes tag and xml-roles to find out more. @@ -2012,7 +2013,7 @@ nsAccessible::GetAttributes(nsIPersistentProperties **aAttributes) NS_ENSURE_SUCCESS(rv, rv); } - // Expose all ARIA attributes + // Expose object attributes from ARIA attributes. PRUint32 numAttrs = content->GetAttrCount(); for (PRUint32 count = 0; count < numAttrs; count ++) { const nsAttrName *attr = content->GetAttrNameAt(count); @@ -2035,6 +2036,17 @@ nsAccessible::GetAttributes(nsIPersistentProperties **aAttributes) } } + // If there is no aria-live attribute then expose default value of 'live' + // object attribute used for ARIA role of this accessible. + if (mRoleMapEntry) { + nsAutoString live; + nsAccUtils::GetAccAttr(attributes, nsAccessibilityAtoms::live, live); + if (live.IsEmpty()) { + nsAccUtils::GetLiveAttrValue(mRoleMapEntry->liveAttRule, live); + nsAccUtils::SetAccAttr(attributes, nsAccessibilityAtoms::live, live); + } + } + return NS_OK; } diff --git a/accessible/tests/mochitest/test_objectattrs.html b/accessible/tests/mochitest/test_objectattrs.html index 8d2da9b9c5d1..8c849f5cef94 100644 --- a/accessible/tests/mochitest/test_objectattrs.html +++ b/accessible/tests/mochitest/test_objectattrs.html @@ -23,10 +23,24 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=475006 testAttrs("atomic", {"atomic" : "true"}, true); testAttrs("autocomplete", {"autocomplete" : "true"}, true); // bug 477876 testAttrs("checked", {"checkable" : "true"}, true); - testAttrs("dropeffect", {"dropeffect" : "copy"}, true); + testAttrs("dropeffect", {"dropeffect" : "copy"}, true); + + // live object attribute testAttrs("live", {"live" : "polite"}, true); - testAttrs("me", {"container-live" : "polite"}, true); - + testAttrs("live2", {"live" : "polite"}, true); + testAttrs("log", {"live" : "polite"}, true); + testAttrs("marquee", {"live" : "off"}, true); + testAttrs("status", {"live" : "polite"}, true); + testAttrs("timer", {"live" : "off"}, true); + + // container-live object attribute + testAttrs("liveChild", {"container-live" : "polite"}, true); + testAttrs("live2Child", {"container-live" : "polite"}, true); + testAttrs("logChild", {"container-live" : "polite"}, true); + testAttrs("marqueeChild", {"container-live" : "off"}, true); + testAttrs("statusChild", {"container-live" : "polite"}, true); + testAttrs("timerChild", {"container-live" : "off"}, true); + SimpleTest.finish(); } @@ -50,7 +64,13 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=475006
-
excuse
me
+ +
excuse
me
+
excuse
me
+
excuse
me
+
excuse
me
+
excuse
me
+
excuse
me
From 4881aa328694ac5eb17466cdcc9369db5c204d18 Mon Sep 17 00:00:00 2001 From: Alexander Surkov Date: Thu, 19 Feb 2009 15:06:14 +0800 Subject: [PATCH 02/26] Bug 455886 - AccessibleNameFromSubtree(): don't recurse into subtrees for roles that don't use name from subtree, r=davidb, marcoz --HG-- rename : accessible/src/base/nsNameUtils.cpp => accessible/src/base/nsTextEquivUtils.cpp rename : accessible/src/base/nsNameUtils.h => accessible/src/base/nsTextEquivUtils.h --- accessible/src/base/Makefile.in | 2 +- accessible/src/base/nsAccessible.cpp | 321 +---------- accessible/src/base/nsAccessible.h | 27 +- accessible/src/base/nsDocAccessible.cpp | 4 +- accessible/src/base/nsNameUtils.cpp | 169 ------ accessible/src/base/nsNameUtils.h | 71 --- accessible/src/base/nsTextEquivUtils.cpp | 508 ++++++++++++++++++ accessible/src/base/nsTextEquivUtils.h | 168 ++++++ .../src/html/nsHTMLFormControlAccessible.cpp | 3 +- .../src/html/nsHTMLSelectAccessible.cpp | 3 +- accessible/src/html/nsHTMLTableAccessible.cpp | 3 +- accessible/src/html/nsHTMLTextAccessible.cpp | 19 +- accessible/src/xforms/nsXFormsAccessible.cpp | 4 +- .../xforms/nsXFormsFormControlsAccessible.cpp | 4 +- accessible/src/xul/nsXULMenuAccessible.h | 2 - accessible/src/xul/nsXULTextAccessible.cpp | 5 +- accessible/tests/mochitest/Makefile.in | 2 +- accessible/tests/mochitest/common.js | 24 + accessible/tests/mochitest/events.js | 41 ++ accessible/tests/mochitest/namerules.xml | 6 +- .../tests/mochitest/nsIAccessible_name.js | 201 ++++--- .../tests/mochitest/test_name_markup.html | 17 +- .../mochitest/test_nsIAccessible_name.html | 52 +- .../mochitest/test_nsIAccessible_name.xul | 16 +- .../mochitest/test_nsIAccessible_selects.html | 4 +- 25 files changed, 999 insertions(+), 677 deletions(-) delete mode 100644 accessible/src/base/nsNameUtils.cpp delete mode 100644 accessible/src/base/nsNameUtils.h create mode 100644 accessible/src/base/nsTextEquivUtils.cpp create mode 100644 accessible/src/base/nsTextEquivUtils.h diff --git a/accessible/src/base/Makefile.in b/accessible/src/base/Makefile.in index 63cb959ede41..47d49afb19e7 100644 --- a/accessible/src/base/Makefile.in +++ b/accessible/src/base/Makefile.in @@ -81,7 +81,6 @@ CPPSRCS = \ nsAccessibilityAtoms.cpp \ nsCoreUtils.cpp \ nsAccUtils.cpp \ - nsNameUtils.cpp \ nsRelUtils.cpp \ nsAccessibilityService.cpp \ nsAccessible.cpp \ @@ -93,6 +92,7 @@ CPPSRCS = \ nsApplicationAccessible.cpp \ nsCaretAccessible.cpp \ nsTextAccessible.cpp \ + nsTextEquivUtils.cpp \ nsTextAttrs.cpp \ $(NULL) diff --git a/accessible/src/base/nsAccessible.cpp b/accessible/src/base/nsAccessible.cpp index d39d86390559..c4bfaf3f9608 100644 --- a/accessible/src/base/nsAccessible.cpp +++ b/accessible/src/base/nsAccessible.cpp @@ -40,7 +40,6 @@ #include "nsAccessible.h" #include "nsAccessibleRelation.h" #include "nsHyperTextAccessibleWrap.h" -#include "nsNameUtils.h" #include "nsIAccessibleDocument.h" #include "nsIAccessibleHyperText.h" @@ -329,7 +328,9 @@ NS_IMETHODIMP nsAccessible::GetDescription(nsAString& aDescription) } if (!content->IsNodeOfType(nsINode::eTEXT)) { nsAutoString description; - nsresult rv = GetTextFromRelationID(nsAccessibilityAtoms::aria_describedby, description); + nsresult rv = nsTextEquivUtils:: + GetTextEquivFromIDRefs(this, nsAccessibilityAtoms::aria_describedby, + description); if (NS_FAILED(rv)) { PRBool isXUL = content->IsNodeOfType(nsINode::eXUL); if (isXUL) { @@ -341,7 +342,8 @@ NS_IMETHODIMP nsAccessible::GetDescription(nsAString& aDescription) if (descriptionContent) { // We have a description content node - AppendFlatStringFromSubtree(descriptionContent, &description); + nsTextEquivUtils:: + AppendTextEquivFromContent(this, descriptionContent, &description); } } if (description.IsEmpty()) { @@ -1456,284 +1458,6 @@ nsAccessible::TakeFocus() return NS_OK; } -nsresult nsAccessible::AppendStringWithSpaces(nsAString *aFlatString, const nsAString& textEquivalent) -{ - // Insert spaces to insure that words from controls aren't jammed together - if (!textEquivalent.IsEmpty()) { - if (!aFlatString->IsEmpty()) - aFlatString->Append(PRUnichar(' ')); - aFlatString->Append(textEquivalent); - aFlatString->Append(PRUnichar(' ')); - } - return NS_OK; -} - -nsresult nsAccessible::AppendNameFromAccessibleFor(nsIContent *aContent, - nsAString *aFlatString, - PRBool aFromValue) -{ - nsAutoString textEquivalent, value; - - nsCOMPtr domNode(do_QueryInterface(aContent)); - nsCOMPtr accessible; - if (domNode == mDOMNode) { - accessible = this; - if (!aFromValue) { - // prevent recursive call GetName() - return NS_OK; - } - } - else { - nsCOMPtr accService = - do_GetService("@mozilla.org/accessibilityService;1"); - NS_ENSURE_TRUE(accService, NS_ERROR_FAILURE); - accService->GetAccessibleInWeakShell(domNode, mWeakShell, getter_AddRefs(accessible)); - } - if (accessible) { - if (aFromValue) { - accessible->GetValue(textEquivalent); - } - else { - accessible->GetName(textEquivalent); - } - } - - textEquivalent.CompressWhitespace(); - return AppendStringWithSpaces(aFlatString, textEquivalent); -} - -/* - * AppendFlatStringFromContentNode and AppendFlatStringFromSubtree - * - * This method will glean useful text, in whatever form it exists, from any content node given to it. - * It is used by any decendant of nsAccessible that needs to get text from a single node, as - * well as by nsAccessible::AppendFlatStringFromSubtree, which gleans and concatenates text from any node and - * that node's decendants. - */ - -nsresult nsAccessible::AppendFlatStringFromContentNode(nsIContent *aContent, nsAString *aFlatString) -{ - if (aContent->IsNodeOfType(nsINode::eTEXT)) { - // If it's a text node, append the text - PRBool isHTMLBlock = PR_FALSE; - nsCOMPtr shell = GetPresShell(); - if (!shell) { - return NS_ERROR_FAILURE; - } - - nsIContent *parentContent = aContent->GetParent(); - nsCOMPtr appendedSubtreeStart(do_QueryInterface(mDOMNode)); - if (parentContent && parentContent != appendedSubtreeStart) { - nsIFrame *frame = shell->GetPrimaryFrameFor(parentContent); - if (frame) { - // If this text is inside a block level frame (as opposed to span level), we need to add spaces around that - // block's text, so we don't get words jammed together in final name - // Extra spaces will be trimmed out later - const nsStyleDisplay* display = frame->GetStyleDisplay(); - if (display->IsBlockOutside() || - display->mDisplay == NS_STYLE_DISPLAY_TABLE_CELL) { - isHTMLBlock = PR_TRUE; - if (!aFlatString->IsEmpty()) { - aFlatString->Append(PRUnichar(' ')); - } - } - } - } - if (aContent->TextLength() > 0) { - nsIFrame *frame = shell->GetPrimaryFrameFor(aContent); - if (frame) { - nsresult rv = frame->GetRenderedText(aFlatString); - NS_ENSURE_SUCCESS(rv, rv); - } else { - //if aContent is an object that is display: none, we have no a frame - aContent->AppendTextTo(*aFlatString); - } - if (isHTMLBlock && !aFlatString->IsEmpty()) { - aFlatString->Append(PRUnichar(' ')); - } - } - return NS_OK; - } - - nsAutoString textEquivalent; - if (!aContent->IsNodeOfType(nsINode::eHTML)) { - if (aContent->IsNodeOfType(nsINode::eXUL)) { - nsCOMPtr labeledEl(do_QueryInterface(aContent)); - if (labeledEl) { - labeledEl->GetLabel(textEquivalent); - } - else { - if (aContent->NodeInfo()->Equals(nsAccessibilityAtoms::label, kNameSpaceID_XUL)) { - aContent->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::value, textEquivalent); - } - if (textEquivalent.IsEmpty()) { - aContent->GetAttr(kNameSpaceID_None, - nsAccessibilityAtoms::tooltiptext, textEquivalent); - } - } - AppendNameFromAccessibleFor(aContent, &textEquivalent, PR_TRUE /* use value */); - - return AppendStringWithSpaces(aFlatString, textEquivalent); - } - return NS_OK; // Not HTML and not XUL -- we don't handle it yet - } - - nsCOMPtr tag = aContent->Tag(); - if (tag == nsAccessibilityAtoms::img) { - return AppendNameFromAccessibleFor(aContent, aFlatString); - } - - if (tag == nsAccessibilityAtoms::input) { - static nsIContent::AttrValuesArray strings[] = - {&nsAccessibilityAtoms::button, &nsAccessibilityAtoms::submit, - &nsAccessibilityAtoms::reset, &nsAccessibilityAtoms::image, nsnull}; - if (aContent->FindAttrValueIn(kNameSpaceID_None, nsAccessibilityAtoms::type, - strings, eIgnoreCase) >= 0) { - return AppendNameFromAccessibleFor(aContent, aFlatString); - } - } - - if (tag == nsAccessibilityAtoms::object && !aContent->GetChildCount()) { - // If object has no alternative content children, try title - aContent->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::title, textEquivalent); - } - else if (tag == nsAccessibilityAtoms::br) { - // If it's a line break, insert a space so that words aren't jammed together - aFlatString->AppendLiteral("\r\n"); - return NS_OK; - } - else if (tag != nsAccessibilityAtoms::a && tag != nsAccessibilityAtoms::area) { - AppendNameFromAccessibleFor(aContent, aFlatString, PR_TRUE /* use value */); - } - - textEquivalent.CompressWhitespace(); - return AppendStringWithSpaces(aFlatString, textEquivalent); -} - - -nsresult nsAccessible::AppendFlatStringFromSubtree(nsIContent *aContent, nsAString *aFlatString) -{ - static PRBool isAlreadyHere; // Prevent recursion which can cause infinite loops - if (isAlreadyHere) { - return NS_OK; - } - - isAlreadyHere = PR_TRUE; - - nsCOMPtr shell = GetPresShell(); - NS_ENSURE_TRUE(shell, NS_ERROR_FAILURE); - - nsIFrame *frame = shell->GetPrimaryFrameFor(aContent); - PRBool isHidden = (!frame || !frame->GetStyleVisibility()->IsVisible()); - nsresult rv = AppendFlatStringFromSubtreeRecurse(aContent, aFlatString, - isHidden); - - isAlreadyHere = PR_FALSE; - - if (NS_SUCCEEDED(rv) && !aFlatString->IsEmpty()) { - nsAString::const_iterator start, end; - aFlatString->BeginReading(start); - aFlatString->EndReading(end); - - PRInt32 spacesToTruncate = 0; - while (-- end != start && *end == ' ') - ++ spacesToTruncate; - - if (spacesToTruncate > 0) - aFlatString->Truncate(aFlatString->Length() - spacesToTruncate); - } - - return rv; -} - -nsresult -nsAccessible::AppendFlatStringFromSubtreeRecurse(nsIContent *aContent, - nsAString *aFlatString, - PRBool aIsRootHidden) -{ - // Depth first search for all text nodes that are decendants of content node. - // Append all the text into one flat string - PRUint32 numChildren = 0; - nsCOMPtr selectControlEl(do_QueryInterface(aContent)); - nsCOMPtr tag = aContent->Tag(); - - if (!selectControlEl && - tag != nsAccessibilityAtoms::textarea && - tag != nsAccessibilityAtoms::select) { - // Don't walk children of elements with options, just get label directly. - // Don't traverse the children of a textarea, we want the value, not the - // static text node. - // Don't traverse the children of a select element, we only want the - // current value. - numChildren = aContent->GetChildCount(); - } - - if (numChildren == 0) { - // There are no children or they are irrelvant: get the text from the current node - AppendFlatStringFromContentNode(aContent, aFlatString); - return NS_OK; - } - - // There are relevant children: use them to get the text. - nsCOMPtr shell = GetPresShell(); - NS_ENSURE_TRUE(shell, NS_ERROR_FAILURE); - - PRUint32 index; - for (index = 0; index < numChildren; index++) { - nsCOMPtr childContent = aContent->GetChildAt(index); - - // Walk into hidden subtree if the the root parent is also hidden. This - // happens when the author explictly uses a hidden label or description. - if (!aIsRootHidden) { - nsIFrame *childFrame = shell->GetPrimaryFrameFor(childContent); - if (!childFrame || !childFrame->GetStyleVisibility()->IsVisible()) - continue; - } - - AppendFlatStringFromSubtreeRecurse(childContent, aFlatString, - aIsRootHidden); - } - - return NS_OK; -} - -nsresult nsAccessible::GetTextFromRelationID(nsIAtom *aIDProperty, nsString &aName) -{ - // Get DHTML name from content subtree pointed to by ID attribute - aName.Truncate(); - NS_ASSERTION(mDOMNode, "Called from shutdown accessible"); - - nsCOMPtr content = nsCoreUtils::GetRoleContent(mDOMNode); - if (!content) - return NS_OK; - - nsCOMPtr refElms; - nsCoreUtils::GetElementsByIDRefsAttr(content, aIDProperty, - getter_AddRefs(refElms)); - - if (!refElms) - return NS_OK; - - PRUint32 count = 0; - nsresult rv = refElms->GetLength(&count); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr refContent; - for (PRUint32 idx = 0; idx < count; idx++) { - refContent = do_QueryElementAt(refElms, idx, &rv); - NS_ENSURE_SUCCESS(rv, rv); - - if (!aName.IsEmpty()) - aName += ' '; // Need whitespace between multiple labels or descriptions - - rv = AppendFlatStringFromSubtree(refContent, &aName); - NS_ENSURE_SUCCESS(rv, rv); - } - - aName.CompressWhitespace(); - return NS_OK; -} - nsresult nsAccessible::GetHTMLName(nsAString& aLabel) { @@ -1746,7 +1470,8 @@ nsAccessible::GetHTMLName(nsAString& aLabel) nsIContent *labelContent = nsCoreUtils::GetHTMLLabelContent(content); if (labelContent) { nsAutoString label; - nsresult rv = AppendFlatStringFromSubtree(labelContent, &label); + nsresult rv = + nsTextEquivUtils::AppendTextEquivFromContent(this, labelContent, &label); NS_ENSURE_SUCCESS(rv, rv); label.CompressWhitespace(); @@ -1756,20 +1481,7 @@ nsAccessible::GetHTMLName(nsAString& aLabel) } } - PRUint32 role = nsAccUtils::Role(this); - PRUint32 canAggregateName = - nsNameUtils::gRoleToNameRulesMap[role] & eFromSubtree; - - if (canAggregateName) { - // Don't use AppendFlatStringFromSubtree for container widgets like menulist - nsresult rv = AppendFlatStringFromSubtree(content, &aLabel); - NS_ENSURE_SUCCESS(rv, rv); - - if (!aLabel.IsEmpty()) - return NS_OK; - } - - return NS_OK; + return nsTextEquivUtils::GetNameFromSubtree(this, aLabel); } /** @@ -1829,7 +1541,7 @@ nsAccessible::GetXULName(nsAString& aLabel) if (xulLabel && NS_SUCCEEDED(xulLabel->GetValue(label)) && label.IsEmpty()) { // If no value attribute, a non-empty label must contain // children that define its text -- possibly using HTML - AppendFlatStringFromSubtree(labelContent, &label); + nsTextEquivUtils::AppendTextEquivFromContent(this, labelContent, &label); } } @@ -1854,12 +1566,7 @@ nsAccessible::GetXULName(nsAString& aLabel) parent = parent->GetParent(); } - PRUint32 role = nsAccUtils::Role(this); - PRUint32 canAggregateName = - nsNameUtils::gRoleToNameRulesMap[role] & eFromSubtree; - - return canAggregateName ? - AppendFlatStringFromSubtree(content, &aLabel) : NS_OK; + return nsTextEquivUtils::GetNameFromSubtree(this, aLabel); } NS_IMETHODIMP @@ -3390,14 +3097,18 @@ nsAccessible::GetARIAName(nsAString& aName) // First check for label override via aria-label property nsAutoString label; if (content->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_label, label)) { + label.CompressWhitespace(); aName = label; return NS_OK; } // Second check for label override via aria-labelledby relationship - nsresult rv = GetTextFromRelationID(nsAccessibilityAtoms::aria_labelledby, label); - if (NS_SUCCEEDED(rv)) + nsresult rv = nsTextEquivUtils:: + GetTextEquivFromIDRefs(this, nsAccessibilityAtoms::aria_labelledby, label); + if (NS_SUCCEEDED(rv)) { + label.CompressWhitespace(); aName = label; + } return rv; } diff --git a/accessible/src/base/nsAccessible.h b/accessible/src/base/nsAccessible.h index 31bedfc3d438..76fbb93015e2 100644 --- a/accessible/src/base/nsAccessible.h +++ b/accessible/src/base/nsAccessible.h @@ -41,6 +41,10 @@ #include "nsAccessNodeWrap.h" +#include "nsARIAMap.h" +#include "nsRelUtils.h" +#include "nsTextEquivUtils.h" + #include "nsIAccessible.h" #include "nsPIAccessible.h" #include "nsIAccessibleHyperLink.h" @@ -50,15 +54,12 @@ #include "nsIAccessibleStates.h" #include "nsIAccessibleEvent.h" -#include "nsRelUtils.h" - #include "nsIDOMNodeList.h" #include "nsINameSpaceManager.h" #include "nsWeakReference.h" #include "nsString.h" #include "nsTArray.h" #include "nsIDOMDOMStringList.h" -#include "nsARIAMap.h" struct nsRect; class nsIContent; @@ -181,18 +182,6 @@ protected: virtual void GetBoundsRect(nsRect& aRect, nsIFrame** aRelativeFrame); PRBool IsVisible(PRBool *aIsOffscreen); - // Relation helpers - - /** - * For a given ARIA relation, such as labelledby or describedby, get the collated text - * for the subtree that's pointed to. - * - * @param aIDProperty The ARIA relationship property to get the text for - * @param aName Where to put the text - * @return error or success code - */ - nsresult GetTextFromRelationID(nsIAtom *aIDProperty, nsString &aName); - ////////////////////////////////////////////////////////////////////////////// // Name helpers. @@ -206,14 +195,6 @@ protected: */ nsresult GetXULName(nsAString& aName); - // For accessibles that are not lists of choices, the name of the subtree should be the - // sum of names in the subtree - nsresult AppendFlatStringFromSubtree(nsIContent *aContent, nsAString *aFlatString); - nsresult AppendNameFromAccessibleFor(nsIContent *aContent, nsAString *aFlatString, - PRBool aFromValue = PR_FALSE); - nsresult AppendFlatStringFromContentNode(nsIContent *aContent, nsAString *aFlatString); - nsresult AppendStringWithSpaces(nsAString *aFlatString, const nsAString& textEquivalent); - // helper method to verify frames static nsresult GetFullKeyName(const nsAString& aModifierName, const nsAString& aKeyName, nsAString& aStringOut); static nsresult GetTranslatedString(const nsAString& aKey, nsAString& aStringOut); diff --git a/accessible/src/base/nsDocAccessible.cpp b/accessible/src/base/nsDocAccessible.cpp index ed14022e1f1e..df0267f45d07 100644 --- a/accessible/src/base/nsDocAccessible.cpp +++ b/accessible/src/base/nsDocAccessible.cpp @@ -263,7 +263,9 @@ nsDocAccessible::GetDescription(nsAString& aDescription) if (aDescription.IsEmpty()) { nsAutoString description; - GetTextFromRelationID(nsAccessibilityAtoms::aria_describedby, description); + nsTextEquivUtils:: + GetTextEquivFromIDRefs(this, nsAccessibilityAtoms::aria_describedby, + description); aDescription = description; } diff --git a/accessible/src/base/nsNameUtils.cpp b/accessible/src/base/nsNameUtils.cpp deleted file mode 100644 index 5193040e07a0..000000000000 --- a/accessible/src/base/nsNameUtils.cpp +++ /dev/null @@ -1,169 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:expandtab:shiftwidth=2:tabstop=2: - */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is mozilla.org code. - * - * The Initial Developer of the Original Code is Mozilla Foundation - * Portions created by the Initial Developer are Copyright (C) 2008 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Alexander Surkov (original author) - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "nsNameUtils.h" - -//////////////////////////////////////////////////////////////////////////////// -// Name rules to role map. - -PRUint32 nsNameUtils::gRoleToNameRulesMap[] = -{ - eNoRule, // ROLE_NOTHING - eNoRule, // ROLE_TITLEBAR - eNoRule, // ROLE_MENUBAR - eNoRule, // ROLE_SCROLLBAR - eNoRule, // ROLE_GRIP - eNoRule, // ROLE_SOUND - eNoRule, // ROLE_CURSOR - eNoRule, // ROLE_CARET - eNoRule, // ROLE_ALERT - eNoRule, // ROLE_WINDOW - eNoRule, // ROLE_INTERNAL_FRAME - eNoRule, // ROLE_MENUPOPUP - eFromSubtree, // ROLE_MENUITEM - eFromSubtree, // ROLE_TOOLTIP - eNoRule, // ROLE_APPLICATION - eNoRule, // ROLE_DOCUMENT - eNoRule, // ROLE_PANE - eNoRule, // ROLE_CHART - eNoRule, // ROLE_DIALOG - eNoRule, // ROLE_BORDER - eNoRule, // ROLE_GROUPING - eNoRule, // ROLE_SEPARATOR - eNoRule, // ROLE_TOOLBAR - eNoRule, // ROLE_STATUSBAR - eNoRule, // ROLE_TABLE - eFromSubtree, // ROLE_COLUMNHEADER - eFromSubtree, // ROLE_ROWHEADER - eFromSubtree, // ROLE_COLUMN - eFromSubtree, // ROLE_ROW - eNoRule, // ROLE_CELL - eFromSubtree, // ROLE_LINK - eFromSubtree, // ROLE_HELPBALLOON - eNoRule, // ROLE_CHARACTER - eNoRule, // ROLE_LIST - eFromSubtree, // ROLE_LISTITEM - eNoRule, // ROLE_OUTLINE - eFromSubtree, // ROLE_OUTLINEITEM - eFromSubtree, // ROLE_PAGETAB - eNoRule, // ROLE_PROPERTYPAGE - eNoRule, // ROLE_INDICATOR - eNoRule, // ROLE_GRAPHIC - eNoRule, // ROLE_STATICTEXT - eNoRule, // ROLE_TEXT_LEAF - eFromSubtree, // ROLE_PUSHBUTTON - eFromSubtree, // ROLE_CHECKBUTTON - eFromSubtree, // ROLE_RADIOBUTTON - eNoRule, // ROLE_COMBOBOX - eNoRule, // ROLE_DROPLIST - eNoRule, // ROLE_PROGRESSBAR - eNoRule, // ROLE_DIAL - eNoRule, // ROLE_HOTKEYFIELD - eNoRule, // ROLE_SLIDER - eNoRule, // ROLE_SPINBUTTON - eNoRule, // ROLE_DIAGRAM - eNoRule, // ROLE_ANIMATION - eNoRule, // ROLE_EQUATION - eFromSubtree, // ROLE_BUTTONDROPDOWN - eFromSubtree, // ROLE_BUTTONMENU - eFromSubtree, // ROLE_BUTTONDROPDOWNGRID - eNoRule, // ROLE_WHITESPACE - eNoRule, // ROLE_PAGETABLIST - eNoRule, // ROLE_CLOCK - eNoRule, // ROLE_SPLITBUTTON - eNoRule, // ROLE_IPADDRESS - eNoRule, // ROLE_ACCEL_LABEL - eNoRule, // ROLE_ARROW - eNoRule, // ROLE_CANVAS - eFromSubtree, // ROLE_CHECK_MENU_ITEM - eNoRule, // ROLE_COLOR_CHOOSER - eNoRule, // ROLE_DATE_EDITOR - eNoRule, // ROLE_DESKTOP_ICON - eNoRule, // ROLE_DESKTOP_FRAME - eNoRule, // ROLE_DIRECTORY_PANE - eNoRule, // ROLE_FILE_CHOOSER - eNoRule, // ROLE_FONT_CHOOSER - eNoRule, // ROLE_CHROME_WINDOW - eNoRule, // ROLE_GLASS_PANE - eNoRule, // ROLE_HTML_CONTAINER - eNoRule, // ROLE_ICON - eNoRule, // ROLE_LABEL - eNoRule, // ROLE_LAYERED_PANE - eNoRule, // ROLE_OPTION_PANE - eNoRule, // ROLE_PASSWORD_TEXT - eNoRule, // ROLE_POPUP_MENU - eFromSubtree, // ROLE_RADIO_MENU_ITEM - eNoRule, // ROLE_ROOT_PANE - eNoRule, // ROLE_SCROLL_PANE - eNoRule, // ROLE_SPLIT_PANE - eFromSubtree, // ROLE_TABLE_COLUMN_HEADER - eFromSubtree, // ROLE_TABLE_ROW_HEADER - eFromSubtree, // ROLE_TEAR_OFF_MENU_ITEM - eNoRule, // ROLE_TERMINAL - eNoRule, // ROLE_TEXT_CONTAINER - eFromSubtree, // ROLE_TOGGLE_BUTTON - eNoRule, // ROLE_TREE_TABLE - eNoRule, // ROLE_VIEWPORT - eNoRule, // ROLE_HEADER - eNoRule, // ROLE_FOOTER - eNoRule, // ROLE_PARAGRAPH - eNoRule, // ROLE_RULER - eNoRule, // ROLE_AUTOCOMPLETE - eNoRule, // ROLE_EDITBAR - eNoRule, // ROLE_ENTRY - eNoRule, // ROLE_CAPTION - eNoRule, // ROLE_DOCUMENT_FRAME - eNoRule, // ROLE_HEADING - eNoRule, // ROLE_PAGE - eNoRule, // ROLE_SECTION - eNoRule, // ROLE_REDUNDANT_OBJECT - eNoRule, // ROLE_FORM - eNoRule, // ROLE_IME - eNoRule, // ROLE_APP_ROOT - eFromSubtree, // ROLE_PARENT_MENUITEM - eNoRule, // ROLE_CALENDAR - eNoRule, // ROLE_COMBOBOX_LIST - eFromSubtree, // ROLE_COMBOBOX_OPTION - eNoRule, // ROLE_IMAGE_MAP - eFromSubtree, // ROLE_OPTION - eFromSubtree, // ROLE_RICH_OPTION - eNoRule, // ROLE_LISTBOX - eNoRule, // ROLE_FLAT_EQUATION - eFromSubtree // ROLE_GRID_CELL -}; diff --git a/accessible/src/base/nsNameUtils.h b/accessible/src/base/nsNameUtils.h deleted file mode 100644 index 7ae2c3e96d9e..000000000000 --- a/accessible/src/base/nsNameUtils.h +++ /dev/null @@ -1,71 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:expandtab:shiftwidth=2:tabstop=2: - */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is mozilla.org code. - * - * The Initial Developer of the Original Code is Mozilla Foundation - * Portions created by the Initial Developer are Copyright (C) 2008 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Alexander Surkov (original author) - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef _nsNameUtils_H_ -#define _nsNameUtils_H_ - -#include "prtypes.h" - -/** - * Name from subtree calculation rules. - */ -enum ENameFromSubtreeRule -{ - // do not walk into subtree to compute the name - eNoRule = 0x00, - - // compute the name from the subtree - eFromSubtree = 0x01 -}; - -/** - * The class provides utils methods to compute the accessible name and - * description. - */ -class nsNameUtils -{ -public: - - /** - * Map array from roles to name rules (bit state of ENameFromSubtreeRule). - */ - static PRUint32 gRoleToNameRulesMap[]; -}; - -#endif diff --git a/accessible/src/base/nsTextEquivUtils.cpp b/accessible/src/base/nsTextEquivUtils.cpp new file mode 100644 index 000000000000..6807efbbbc6b --- /dev/null +++ b/accessible/src/base/nsTextEquivUtils.cpp @@ -0,0 +1,508 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:expandtab:shiftwidth=2:tabstop=2: + */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Mozilla Foundation + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Alexander Surkov (original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsTextEquivUtils.h" + +#include "nsAccessible.h" + +#include "nsIDOMXULLabeledControlEl.h" + +#include "nsArrayUtils.h" + +#define NS_OK_NO_NAME_CLAUSE_HANDLED \ +NS_ERROR_GENERATE_SUCCESS(NS_ERROR_MODULE_GENERAL, 0x24) + +//////////////////////////////////////////////////////////////////////////////// +// nsTextEquivUtils. Public. + +nsresult +nsTextEquivUtils::GetNameFromSubtree(nsIAccessible *aAccessible, + nsAString& aName) +{ + aName.Truncate(); + + if (gInitiatorAcc) + return NS_OK; + + gInitiatorAcc = aAccessible; + + PRUint32 role = nsAccUtils::Role(aAccessible); + PRUint32 nameRule = gRoleToNameRulesMap[role]; + + if (nameRule == eFromSubtree) { + nsCOMPtr accessNode(do_QueryInterface(aAccessible)); + + nsCOMPtr DOMNode; + accessNode->GetDOMNode(getter_AddRefs(DOMNode)); + nsCOMPtr content(do_QueryInterface(DOMNode)); + if (content) { + nsAutoString name; + AppendFromAccessibleChildren(aAccessible, &name); + name.CompressWhitespace(); + aName = name; + } + } + + gInitiatorAcc = nsnull; + + return NS_OK; +} + +nsresult +nsTextEquivUtils::GetTextEquivFromIDRefs(nsIAccessible *aAccessible, + nsIAtom *aIDRefsAttr, + nsAString& aTextEquiv) +{ + aTextEquiv.Truncate(); + + nsCOMPtr accessNode(do_QueryInterface(aAccessible)); + + nsCOMPtr DOMNode; + accessNode->GetDOMNode(getter_AddRefs(DOMNode)); + + nsCOMPtr content = nsCoreUtils::GetRoleContent(DOMNode); + if (!content) + return NS_OK; + + nsCOMPtr refElms; + nsCoreUtils::GetElementsByIDRefsAttr(content, aIDRefsAttr, + getter_AddRefs(refElms)); + + if (!refElms) + return NS_OK; + + PRUint32 count = 0; + nsresult rv = refElms->GetLength(&count); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr refContent; + for (PRUint32 idx = 0; idx < count; idx++) { + refContent = do_QueryElementAt(refElms, idx, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + if (!aTextEquiv.IsEmpty()) + aTextEquiv += ' '; + + rv = AppendTextEquivFromContent(aAccessible, refContent, &aTextEquiv); + NS_ENSURE_SUCCESS(rv, rv); + } + + return NS_OK; +} + +nsresult +nsTextEquivUtils::AppendTextEquivFromContent(nsIAccessible *aInitiatorAcc, + nsIContent *aContent, + nsAString *aString) +{ + // Prevent recursion which can cause infinite loops. + if (gInitiatorAcc) + return NS_OK; + + gInitiatorAcc = aInitiatorAcc; + + nsCOMPtr DOMNode(do_QueryInterface(aContent)); + nsCOMPtr shell = nsCoreUtils::GetPresShellFor(DOMNode); + if (!shell) { + NS_ASSERTION(PR_TRUE, "There is no presshell!"); + gInitiatorAcc = nsnull; + return NS_ERROR_UNEXPECTED; + } + + // If the given content is not visible or isn't accessible then go down + // through the DOM subtree otherwise go down through accessible subtree and + // calculate the flat string. + nsIFrame *frame = shell->GetPrimaryFrameFor(aContent); + PRBool isVisible = frame && frame->GetStyleVisibility()->IsVisible(); + + nsresult rv; + PRBool goThroughDOMSubtree = PR_TRUE; + + if (isVisible) { + nsCOMPtr accessible; + rv = nsAccessNode::GetAccService()-> + GetAccessibleInShell(DOMNode, shell, getter_AddRefs(accessible)); + if (NS_SUCCEEDED(rv) && accessible) { + rv = AppendFromAccessible(accessible, aString); + goThroughDOMSubtree = PR_FALSE; + } + } + + if (goThroughDOMSubtree) + rv = AppendFromDOMNode(aContent, aString); + + gInitiatorAcc = nsnull; + return rv; +} + +nsresult +nsTextEquivUtils::AppendTextEquivFromTextContent(nsIContent *aContent, + nsAString *aString) +{ + if (aContent->IsNodeOfType(nsINode::eTEXT)) { + + nsCOMPtr DOMNode(do_QueryInterface(aContent)); + + PRBool isHTMLBlock = PR_FALSE; + nsCOMPtr shell = nsCoreUtils::GetPresShellFor(DOMNode); + NS_ENSURE_STATE(shell); + + nsIContent *parentContent = aContent->GetParent(); + if (parentContent) { + nsIFrame *frame = shell->GetPrimaryFrameFor(parentContent); + if (frame) { + // If this text is inside a block level frame (as opposed to span + // level), we need to add spaces around that block's text, so we don't + // get words jammed together in final name. + const nsStyleDisplay* display = frame->GetStyleDisplay(); + if (display->IsBlockOutside() || + display->mDisplay == NS_STYLE_DISPLAY_TABLE_CELL) { + isHTMLBlock = PR_TRUE; + if (!aString->IsEmpty()) { + aString->Append(PRUnichar(' ')); + } + } + } + } + + if (aContent->TextLength() > 0) { + nsIFrame *frame = shell->GetPrimaryFrameFor(aContent); + if (frame) { + nsresult rv = frame->GetRenderedText(aString); + NS_ENSURE_SUCCESS(rv, rv); + } else { + // If aContent is an object that is display: none, we have no a frame. + aContent->AppendTextTo(*aString); + } + if (isHTMLBlock && !aString->IsEmpty()) { + aString->Append(PRUnichar(' ')); + } + } + + return NS_OK; + } + + if (aContent->IsNodeOfType(nsINode::eHTML) && + aContent->NodeInfo()->Equals(nsAccessibilityAtoms::br)) { + aString->AppendLiteral("\r\n"); + return NS_OK; + } + + return NS_OK_NO_NAME_CLAUSE_HANDLED; +} + +//////////////////////////////////////////////////////////////////////////////// +// nsTextEquivUtils. Private. + +nsCOMPtr nsTextEquivUtils::gInitiatorAcc; + +nsresult +nsTextEquivUtils::AppendFromAccessibleChildren(nsIAccessible *aAccessible, + nsAString *aString) +{ + nsCOMPtr accChild, accNextChild; + aAccessible->GetFirstChild(getter_AddRefs(accChild)); + + while (accChild) { + nsresult rv = AppendFromAccessible(accChild, aString); + NS_ENSURE_SUCCESS(rv, rv); + + accChild->GetNextSibling(getter_AddRefs(accNextChild)); + accChild.swap(accNextChild); + } + + return NS_OK; +} + +nsresult +nsTextEquivUtils::AppendFromAccessible(nsIAccessible *aAccessible, + nsAString *aString) +{ + nsCOMPtr accessNode(do_QueryInterface(aAccessible)); + + nsCOMPtr DOMNode; + accessNode->GetDOMNode(getter_AddRefs(DOMNode)); + nsCOMPtr content(do_QueryInterface(DOMNode)); + NS_ASSERTION(content, "There is no content!"); + + if (content) { + nsresult rv = AppendTextEquivFromTextContent(content, aString); + if (rv != NS_OK_NO_NAME_CLAUSE_HANDLED) + return rv; + } + + nsAutoString text; + nsresult rv = aAccessible->GetName(text); + NS_ENSURE_SUCCESS(rv, rv); + + AppendString(aString, text); + + PRUint32 role = nsAccUtils::Role(aAccessible); + PRUint32 nameRule = gRoleToNameRulesMap[role]; + + if (nameRule == eFromValue) { + // Implementation of step f) of text equivalent computation. If the given + // accessible is not root accessible (the accessible the text equivalent is + // computed for in the end) then append accessible value. Otherwise append + // value if and only if the given accessible is in the middle of its parent. + + if (aAccessible != gInitiatorAcc) { + rv = aAccessible->GetValue(text); + NS_ENSURE_SUCCESS(rv, rv); + + AppendString(aString, text); + } else { + nsCOMPtr nextSibling; + aAccessible->GetNextSibling(getter_AddRefs(nextSibling)); + if (nextSibling) { + nsCOMPtr parent; + aAccessible->GetParent(getter_AddRefs(parent)); + if (parent) { + nsCOMPtr firstChild; + parent->GetFirstChild(getter_AddRefs(firstChild)); + if (firstChild && firstChild != aAccessible) { + rv = aAccessible->GetValue(text); + NS_ENSURE_SUCCESS(rv, rv); + + AppendString(aString, text); + } + } + } + } + } + + // Implementation of g) step of text equivalent computation guide. Go down + // into subtree if accessible allows "text equivalent from subtree rule" or + // it's not root and not control. + if (text.IsEmpty() && (nameRule & eFromSubtreeIfRec)) + return AppendFromAccessibleChildren(aAccessible, aString); + + return NS_OK; +} + +nsresult +nsTextEquivUtils::AppendFromDOMChildren(nsIContent *aContent, + nsAString *aString) +{ + PRUint32 childCount = aContent->GetChildCount(); + for (PRUint32 childIdx = 0; childIdx < childCount; childIdx++) { + nsCOMPtr childContent = aContent->GetChildAt(childIdx); + + nsresult rv = AppendFromDOMNode(childContent, aString); + NS_ENSURE_SUCCESS(rv, rv); + } + + return NS_OK; +} + +nsresult +nsTextEquivUtils::AppendFromDOMNode(nsIContent *aContent, nsAString *aString) +{ + nsresult rv = AppendTextEquivFromTextContent(aContent, aString); + NS_ENSURE_SUCCESS(rv, rv); + + if (rv != NS_OK_NO_NAME_CLAUSE_HANDLED) + return NS_OK; + + if (aContent->IsNodeOfType(nsINode::eXUL)) { + nsAutoString textEquivalent; + nsCOMPtr labeledEl = + do_QueryInterface(aContent); + + if (labeledEl) { + labeledEl->GetLabel(textEquivalent); + } else { + if (aContent->NodeInfo()->Equals(nsAccessibilityAtoms::label, + kNameSpaceID_XUL)) + aContent->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::value, + textEquivalent); + + if (textEquivalent.IsEmpty()) + aContent->GetAttr(kNameSpaceID_None, + nsAccessibilityAtoms::tooltiptext, textEquivalent); + } + + AppendString(aString, textEquivalent); + } + + return AppendFromDOMChildren(aContent, aString); +} + +void +nsTextEquivUtils::AppendString(nsAString *aString, + const nsAString& aTextEquivalent) +{ + // Insert spaces to insure that words from controls aren't jammed together. + if (aTextEquivalent.IsEmpty()) + return; + + if (!aString->IsEmpty()) + aString->Append(PRUnichar(' ')); + + aString->Append(aTextEquivalent); +} + +//////////////////////////////////////////////////////////////////////////////// +// Name rules to role map. + +PRUint32 nsTextEquivUtils::gRoleToNameRulesMap[] = +{ + eNoRule, // ROLE_NOTHING + eNoRule, // ROLE_TITLEBAR + eNoRule, // ROLE_MENUBAR + eNoRule, // ROLE_SCROLLBAR + eNoRule, // ROLE_GRIP + eNoRule, // ROLE_SOUND + eNoRule, // ROLE_CURSOR + eNoRule, // ROLE_CARET + eNoRule, // ROLE_ALERT + eNoRule, // ROLE_WINDOW + eNoRule, // ROLE_INTERNAL_FRAME + eNoRule, // ROLE_MENUPOPUP + eFromSubtree, // ROLE_MENUITEM + eFromSubtree, // ROLE_TOOLTIP + eNoRule, // ROLE_APPLICATION + eNoRule, // ROLE_DOCUMENT + eNoRule, // ROLE_PANE + eNoRule, // ROLE_CHART + eNoRule, // ROLE_DIALOG + eNoRule, // ROLE_BORDER + eNoRule, // ROLE_GROUPING + eNoRule, // ROLE_SEPARATOR + eNoRule, // ROLE_TOOLBAR + eNoRule, // ROLE_STATUSBAR + eNoRule, // ROLE_TABLE + eFromSubtree, // ROLE_COLUMNHEADER + eFromSubtree, // ROLE_ROWHEADER + eFromSubtree, // ROLE_COLUMN + eFromSubtree, // ROLE_ROW + eFromSubtreeIfRec, // ROLE_CELL + eFromSubtree, // ROLE_LINK + eFromSubtree, // ROLE_HELPBALLOON + eNoRule, // ROLE_CHARACTER + eFromSubtreeIfRec, // ROLE_LIST + eFromSubtree, // ROLE_LISTITEM + eNoRule, // ROLE_OUTLINE + eFromSubtree, // ROLE_OUTLINEITEM + eFromSubtree, // ROLE_PAGETAB + eNoRule, // ROLE_PROPERTYPAGE + eNoRule, // ROLE_INDICATOR + eNoRule, // ROLE_GRAPHIC + eNoRule, // ROLE_STATICTEXT + eNoRule, // ROLE_TEXT_LEAF + eFromSubtree, // ROLE_PUSHBUTTON + eFromSubtree, // ROLE_CHECKBUTTON + eFromSubtree, // ROLE_RADIOBUTTON + eFromValue, // ROLE_COMBOBOX + eNoRule, // ROLE_DROPLIST + eFromValue, // ROLE_PROGRESSBAR + eNoRule, // ROLE_DIAL + eNoRule, // ROLE_HOTKEYFIELD + eNoRule, // ROLE_SLIDER + eNoRule, // ROLE_SPINBUTTON + eNoRule, // ROLE_DIAGRAM + eNoRule, // ROLE_ANIMATION + eNoRule, // ROLE_EQUATION + eFromSubtree, // ROLE_BUTTONDROPDOWN + eFromSubtree, // ROLE_BUTTONMENU + eFromSubtree, // ROLE_BUTTONDROPDOWNGRID + eNoRule, // ROLE_WHITESPACE + eNoRule, // ROLE_PAGETABLIST + eNoRule, // ROLE_CLOCK + eNoRule, // ROLE_SPLITBUTTON + eNoRule, // ROLE_IPADDRESS + eNoRule, // ROLE_ACCEL_LABEL + eNoRule, // ROLE_ARROW + eNoRule, // ROLE_CANVAS + eFromSubtree, // ROLE_CHECK_MENU_ITEM + eNoRule, // ROLE_COLOR_CHOOSER + eNoRule, // ROLE_DATE_EDITOR + eNoRule, // ROLE_DESKTOP_ICON + eNoRule, // ROLE_DESKTOP_FRAME + eNoRule, // ROLE_DIRECTORY_PANE + eNoRule, // ROLE_FILE_CHOOSER + eNoRule, // ROLE_FONT_CHOOSER + eNoRule, // ROLE_CHROME_WINDOW + eNoRule, // ROLE_GLASS_PANE + eFromSubtreeIfRec, // ROLE_HTML_CONTAINER + eNoRule, // ROLE_ICON + eFromSubtree, // ROLE_LABEL + eNoRule, // ROLE_LAYERED_PANE + eNoRule, // ROLE_OPTION_PANE + eNoRule, // ROLE_PASSWORD_TEXT + eNoRule, // ROLE_POPUP_MENU + eFromSubtree, // ROLE_RADIO_MENU_ITEM + eNoRule, // ROLE_ROOT_PANE + eNoRule, // ROLE_SCROLL_PANE + eNoRule, // ROLE_SPLIT_PANE + eFromSubtree, // ROLE_TABLE_COLUMN_HEADER + eFromSubtree, // ROLE_TABLE_ROW_HEADER + eFromSubtree, // ROLE_TEAR_OFF_MENU_ITEM + eNoRule, // ROLE_TERMINAL + eFromSubtreeIfRec, // ROLE_TEXT_CONTAINER + eFromSubtree, // ROLE_TOGGLE_BUTTON + eNoRule, // ROLE_TREE_TABLE + eNoRule, // ROLE_VIEWPORT + eNoRule, // ROLE_HEADER + eNoRule, // ROLE_FOOTER + eFromSubtreeIfRec, // ROLE_PARAGRAPH + eNoRule, // ROLE_RULER + eNoRule, // ROLE_AUTOCOMPLETE + eNoRule, // ROLE_EDITBAR + eFromValue, // ROLE_ENTRY + eNoRule, // ROLE_CAPTION + eNoRule, // ROLE_DOCUMENT_FRAME + eNoRule, // ROLE_HEADING + eNoRule, // ROLE_PAGE + eFromSubtreeIfRec, // ROLE_SECTION + eNoRule, // ROLE_REDUNDANT_OBJECT + eNoRule, // ROLE_FORM + eNoRule, // ROLE_IME + eNoRule, // ROLE_APP_ROOT + eFromSubtree, // ROLE_PARENT_MENUITEM + eNoRule, // ROLE_CALENDAR + eNoRule, // ROLE_COMBOBOX_LIST + eFromSubtree, // ROLE_COMBOBOX_OPTION + eNoRule, // ROLE_IMAGE_MAP + eFromSubtree, // ROLE_OPTION + eFromSubtree, // ROLE_RICH_OPTION + eNoRule, // ROLE_LISTBOX + eNoRule, // ROLE_FLAT_EQUATION + eFromSubtree // ROLE_GRID_CELL +}; diff --git a/accessible/src/base/nsTextEquivUtils.h b/accessible/src/base/nsTextEquivUtils.h new file mode 100644 index 000000000000..31ee83231e93 --- /dev/null +++ b/accessible/src/base/nsTextEquivUtils.h @@ -0,0 +1,168 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:expandtab:shiftwidth=2:tabstop=2: + */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Mozilla Foundation + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Alexander Surkov (original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _nsTextEquivUtils_H_ +#define _nsTextEquivUtils_H_ + +#include "nsIAccessible.h" + +#include "nsIContent.h" +#include "nsIStringBundle.h" + +/** + * Text equivalent computation rules (see nsTextEquivUtils::gRoleToNameRulesMap) + */ +enum ETextEquivRule +{ + // No rule. + eNoRule = 0x00, + + // Walk into subtree only if the currently navigated accessible is not root + // accessible (i.e. if the accessible is part of text equivalent computation). + eFromSubtreeIfRec = 0x01, + + // Text equivalent computation from subtree is allowed. + eFromSubtree = 0x03, + + // The accessible allows to append its value to text equivalent. + // XXX: This is temporary solution. Once we move accessible value of links + // and linkable accessibles to MSAA part we can remove this. + eFromValue = 0x04 +}; + +/** + * The class provides utils methods to compute the accessible name and + * description. + */ +class nsTextEquivUtils +{ +public: + + /** + * Calculates the name from accessible subtree if allowed. + * + * @param aAccessible [in] the given accessible + * @param aName [out] accessible name + */ + static nsresult GetNameFromSubtree(nsIAccessible *aAccessible, + nsAString& aName); + + /** + * Calculates text equivalent for the given accessible from its IDRefs + * attribute (like aria-labelledby or aria-describedby). + * + * @param aAccessible [in] the accessible text equivalent is computed for + * @param aIDRefsAttr [in] IDRefs attribute on DOM node of the accessible + * @param aTextEquiv [out] result text equivalent + */ + static nsresult GetTextEquivFromIDRefs(nsIAccessible *aAccessible, + nsIAtom *aIDRefsAttr, + nsAString& aTextEquiv); + + /** + * Calculates the text equivalent from the given content and its subtree if + * allowed and appends it to the given string. + * + * @param aInitiatorAcc [in] the accessible text equivalent is computed for + * in the end (root accessible of text equivalent + * calculation recursion) + * @param aContent [in] the given content the text equivalent is + * computed from + * @param aString [in, out] the string + */ + static nsresult AppendTextEquivFromContent(nsIAccessible *aInitiatorAcc, + nsIContent *aContent, + nsAString *aString); + + /** + * Calculates the text equivalent from the given text content (may be text + * node or html:br) and appends it to the given string. + * + * @param aContent [in] the text content + * @param aString [in, out] the string + */ + static nsresult AppendTextEquivFromTextContent(nsIContent *aContent, + nsAString *aString); + +private: + /** + * Iterates accessible children and calculates text equivalent from each + * child. + */ + static nsresult AppendFromAccessibleChildren(nsIAccessible *aAccessible, + nsAString *aString); + + /** + * Calculates text equivalent from the given accessible and its subtree if + * allowed. + */ + static nsresult AppendFromAccessible(nsIAccessible *aAccessible, + nsAString *aString); + + /** + * Iterates DOM children and calculates text equivalent from each child node. + */ + static nsresult AppendFromDOMChildren(nsIContent *aContent, + nsAString *aString); + + /** + * Calculates text equivalent from the given DOM node and its subtree if + * allowed. + */ + static nsresult AppendFromDOMNode(nsIContent *aContent, nsAString *aString); + + /** + * Concatenates strings and appends space between them. + */ + static void AppendString(nsAString *aString, const nsAString& aTextEquivalent); + + /** + * Map array from roles to name rules (constants of ETextEquivRule). + */ + static PRUint32 gRoleToNameRulesMap[]; + + /** + * The accessible for which we are computing a text equivalent. It is useful + * for bailing out during recursive text computation, or for special cases + * like step f. of the ARIA implementation guide. + */ + static nsCOMPtr gInitiatorAcc; +}; + +#endif diff --git a/accessible/src/html/nsHTMLFormControlAccessible.cpp b/accessible/src/html/nsHTMLFormControlAccessible.cpp index e7a82b362c88..f47195c5b025 100644 --- a/accessible/src/html/nsHTMLFormControlAccessible.cpp +++ b/accessible/src/html/nsHTMLFormControlAccessible.cpp @@ -627,7 +627,8 @@ nsHTMLGroupboxAccessible::GetNameInternal(nsAString& aName) nsIContent *legendContent = GetLegend(); if (legendContent) { - return AppendFlatStringFromSubtree(legendContent, &aName); + return nsTextEquivUtils:: + AppendTextEquivFromContent(this, legendContent, &aName); } return NS_OK; diff --git a/accessible/src/html/nsHTMLSelectAccessible.cpp b/accessible/src/html/nsHTMLSelectAccessible.cpp index a3829edad487..bd8791a369de 100644 --- a/accessible/src/html/nsHTMLSelectAccessible.cpp +++ b/accessible/src/html/nsHTMLSelectAccessible.cpp @@ -526,7 +526,8 @@ nsHTMLSelectOptionAccessible::GetNameInternal(nsAString& aName) if (text->IsNodeOfType(nsINode::eTEXT)) { nsAutoString txtValue; - nsresult rv = AppendFlatStringFromContentNode(text, &txtValue); + nsresult rv = nsTextEquivUtils:: + AppendTextEquivFromTextContent(text, &txtValue); NS_ENSURE_SUCCESS(rv, rv); // Temp var (txtValue) needed until CompressWhitespace built for nsAString diff --git a/accessible/src/html/nsHTMLTableAccessible.cpp b/accessible/src/html/nsHTMLTableAccessible.cpp index c3bddde9f9ad..a3dad3800bf1 100644 --- a/accessible/src/html/nsHTMLTableAccessible.cpp +++ b/accessible/src/html/nsHTMLTableAccessible.cpp @@ -967,7 +967,8 @@ NS_IMETHODIMP nsHTMLTableAccessible::GetDescription(nsAString& aDescription) captionAccessNode->GetDOMNode(getter_AddRefs(captionNode)); nsCOMPtr captionContent = do_QueryInterface(captionNode); if (captionContent) { - AppendFlatStringFromSubtree(captionContent, &aDescription); + nsTextEquivUtils:: + AppendTextEquivFromContent(this, captionContent, &aDescription); } } #ifdef SHOW_LAYOUT_HEURISTIC diff --git a/accessible/src/html/nsHTMLTextAccessible.cpp b/accessible/src/html/nsHTMLTextAccessible.cpp index 69b815e373de..8811104daae6 100644 --- a/accessible/src/html/nsHTMLTextAccessible.cpp +++ b/accessible/src/html/nsHTMLTextAccessible.cpp @@ -169,22 +169,9 @@ nsTextAccessible(aDomNode, aShell) } nsresult -nsHTMLLabelAccessible::GetNameInternal(nsAString& aReturn) -{ - nsresult rv = NS_ERROR_FAILURE; - nsCOMPtr content(do_QueryInterface(mDOMNode)); - - nsAutoString name; - if (content) - rv = AppendFlatStringFromSubtree(content, &name); - - if (NS_SUCCEEDED(rv)) { - // Temp var needed until CompressWhitespace built for nsAString - name.CompressWhitespace(); - aReturn = name; - } - - return rv; +nsHTMLLabelAccessible::GetNameInternal(nsAString& aName) +{ + return nsTextEquivUtils::GetNameFromSubtree(this, aName); } NS_IMETHODIMP nsHTMLLabelAccessible::GetRole(PRUint32 *aRole) diff --git a/accessible/src/xforms/nsXFormsAccessible.cpp b/accessible/src/xforms/nsXFormsAccessible.cpp index 2feb87241a84..b9fea24315a6 100644 --- a/accessible/src/xforms/nsXFormsAccessible.cpp +++ b/accessible/src/xforms/nsXFormsAccessible.cpp @@ -234,7 +234,9 @@ NS_IMETHODIMP nsXFormsAccessible::GetDescription(nsAString& aDescription) { nsAutoString description; - nsresult rv = GetTextFromRelationID(nsAccessibilityAtoms::aria_describedby, description); + nsresult rv = nsTextEquivUtils:: + GetTextEquivFromIDRefs(this, nsAccessibilityAtoms::aria_describedby, + description); if (NS_SUCCEEDED(rv) && !description.IsEmpty()) { aDescription = description; diff --git a/accessible/src/xforms/nsXFormsFormControlsAccessible.cpp b/accessible/src/xforms/nsXFormsFormControlsAccessible.cpp index 86b2fc4824fb..d4f27a48e8ef 100644 --- a/accessible/src/xforms/nsXFormsFormControlsAccessible.cpp +++ b/accessible/src/xforms/nsXFormsFormControlsAccessible.cpp @@ -66,7 +66,9 @@ NS_IMETHODIMP nsXFormsLabelAccessible::GetDescription(nsAString& aDescription) { nsAutoString description; - nsresult rv = GetTextFromRelationID(nsAccessibilityAtoms::aria_describedby, description); + nsresult rv = nsTextEquivUtils:: + GetTextEquivFromIDRefs(this, nsAccessibilityAtoms::aria_describedby, + description); aDescription = description; return rv; } diff --git a/accessible/src/xul/nsXULMenuAccessible.h b/accessible/src/xul/nsXULMenuAccessible.h index 7d5b5b206dc5..bfc2e344240c 100644 --- a/accessible/src/xul/nsXULMenuAccessible.h +++ b/accessible/src/xul/nsXULMenuAccessible.h @@ -61,8 +61,6 @@ public: protected: nsresult ChangeSelection(PRInt32 aIndex, PRUint8 aMethod, PRBool *aSelState); - nsresult AppendFlatStringFromSubtree(nsIContent *aContent, nsAString *aFlatString) - { return NS_OK; } // Overrides base impl in nsAccessible // nsIDOMXULMultiSelectControlElement inherits from this, so we'll always have // one of these if the widget is valid and not defunct diff --git a/accessible/src/xul/nsXULTextAccessible.cpp b/accessible/src/xul/nsXULTextAccessible.cpp index 61ab312112b9..897b2d944b0c 100644 --- a/accessible/src/xul/nsXULTextAccessible.cpp +++ b/accessible/src/xul/nsXULTextAccessible.cpp @@ -165,12 +165,15 @@ nsXULLinkAccessible::GetValue(nsAString& aValue) nsresult nsXULLinkAccessible::GetNameInternal(nsAString& aName) { + if (IsDefunct()) + return NS_ERROR_FAILURE; + nsCOMPtr content(do_QueryInterface(mDOMNode)); content->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::value, aName); if (!aName.IsEmpty()) return NS_OK; - return AppendFlatStringFromSubtree(content, &aName); + return nsTextEquivUtils::GetNameFromSubtree(this, aName); } NS_IMETHODIMP diff --git a/accessible/tests/mochitest/Makefile.in b/accessible/tests/mochitest/Makefile.in index d6ab72f0094f..4fe916993d5c 100644 --- a/accessible/tests/mochitest/Makefile.in +++ b/accessible/tests/mochitest/Makefile.in @@ -81,7 +81,7 @@ _TEST_FILES =\ test_nsIAccessible_name.html \ test_nsIAccessible_name_button.html \ test_nsIAccessible_name_link.html \ - $(warning test_nsIAccessible_name.xul temporarily disabled) \ + test_nsIAccessible_name.xul \ test_nsIAccessible_selects.html \ test_nsIAccessible_focus.html \ test_nsIAccessibleDocument.html \ diff --git a/accessible/tests/mochitest/common.js b/accessible/tests/mochitest/common.js index 223db9bfd684..e84c1c8c3911 100644 --- a/accessible/tests/mochitest/common.js +++ b/accessible/tests/mochitest/common.js @@ -178,6 +178,9 @@ function getNode(aNodeOrID) */ function getAccessible(aAccOrElmOrID, aInterfaces, aElmObj, aDoNotFailIfNoAcc) { + if (!aAccOrElmOrID) + return; + var elm = null; if (aAccOrElmOrID instanceof nsIAccessible) { @@ -246,6 +249,27 @@ function isAccessible(aAccOrElmOrID) return getAccessible(aAccOrElmOrID, null, null, true) ? true : false; } +/** + * Run through accessible tree of the given identifier so that we ensure + * accessible tree is created. + */ +function ensureAccessibleTree(aAccOrElmOrID) +{ + var acc = getAccessible(aAccOrElmOrID); + if (!acc) + return; + + var child = acc.firstChild; + while (child) { + ensureAccessibleTree(child); + try { + child = child.nextSibling; + } catch (e) { + child = null; + } + } +} + /** * Convert role to human readable string. */ diff --git a/accessible/tests/mochitest/events.js b/accessible/tests/mochitest/events.js index fd9001fea9fd..40e4f8f98686 100644 --- a/accessible/tests/mochitest/events.js +++ b/accessible/tests/mochitest/events.js @@ -1,3 +1,9 @@ +//////////////////////////////////////////////////////////////////////////////// +// Constants + +const EVENT_REORDER = nsIAccessibleEvent.EVENT_REORDER; +const EVENT_DOM_DESTROY = nsIAccessibleEvent.EVENT_DOM_DESTROY; + //////////////////////////////////////////////////////////////////////////////// // General @@ -6,6 +12,38 @@ */ var gA11yEventDumpID = ""; +/** + * Executes the function when requested event is handled. + * + * @param aEventType [in] event type + * @param aTarget [in] event target + * @param aFunc [in] function to call when event is handled + * @param aContext [in, optional] object in which context the function is + * called + * @param aArg1 [in, optional] argument passed into the function + * @param aArg2 [in, optional] argument passed into the function + */ +function waitForEvent(aEventType, aTarget, aFunc, aContext, aArg1, aArg2) +{ + var handler = { + handleEvent: function handleEvent(aEvent) { + if (!aTarget || aTarget == aEvent.DOMNode) { + unregisterA11yEventListener(aEventType, this); + + window.setTimeout( + function () + { + aFunc.call(aContext, aArg1, aArg2); + }, + 0 + ); + } + } + }; + + registerA11yEventListener(aEventType, handler); +} + /** * Register accessibility event listener. * @@ -386,6 +424,9 @@ function removeA11yEventListener(aEventType, aEventHandler) function dumpInfoToDOM(aInfo) { + if (!gA11yEventDumpID) + return; + var dumpElm = document.getElementById(gA11yEventDumpID); var div = document.createElement("div"); div.textContent = aInfo; diff --git a/accessible/tests/mochitest/namerules.xml b/accessible/tests/mochitest/namerules.xml index 4cf80328402e..a0a11a821e26 100644 --- a/accessible/tests/mochitest/namerules.xml +++ b/accessible/tests/mochitest/namerules.xml @@ -119,6 +119,7 @@ + @@ -193,12 +194,13 @@ role="gridcell" aria-label="test1" aria-labelledby="l1 l2" - a11yname=" This is a paragraph This is a link This is a list" + a11yname="This is a paragraph This is a link • Listitem1 • Listitem2" title="This is a paragraph This is a link This is a list"> This is a paragraph This is a link - This is a list + Listitem1 + Listitem2 diff --git a/accessible/tests/mochitest/nsIAccessible_name.js b/accessible/tests/mochitest/nsIAccessible_name.js index fb7e505f21ad..ba5ad487c313 100644 --- a/accessible/tests/mochitest/nsIAccessible_name.js +++ b/accessible/tests/mochitest/nsIAccessible_name.js @@ -3,9 +3,8 @@ function testName(aAccOrElmOrID, aName, aMsg) var msg = aMsg ? aMsg : ""; var acc = getAccessible(aAccOrElmOrID); - if (!acc) { - ok(false, msg + "No accessible for " + aAccOrElmOrID + "!"); - } + if (!acc) + return; try { is(acc.name, aName, msg + "Wrong name of the accessible for " + aAccOrElmOrID); @@ -36,13 +35,67 @@ function testNames() gRuleDoc = request.responseXML; var markupElms = evaluateXPath(gRuleDoc, "//rules/rulesample/markup"); - for (var idx = 0; idx < markupElms.length; idx++) - testNamesForMarkup(markupElms[idx]); + gTestIterator.iterateMarkups(markupElms); } //////////////////////////////////////////////////////////////////////////////// // Private section. +/** + * Helper class to interate through name tests. + */ +var gTestIterator = +{ + iterateMarkups: function gTestIterator_iterateMarkups(aMarkupElms) + { + this.markupElms = aMarkupElms; + + this.iterateNext(); + }, + + iterateRules: function gTestIterator_iterateRules(aElm, aContainer, aRuleElms) + { + this.ruleElms = aRuleElms; + this.elm = aElm; + this.container = aContainer; + + this.iterateNext(); + }, + + iterateNext: function gTestIterator_iterateNext() + { + if (this.markupIdx == -1) { + this.markupIdx++; + testNamesForMarkup(this.markupElms[this.markupIdx]); + return; + } + + this.ruleIdx++; + if (this.ruleIdx == this.ruleElms.length) { + this.markupIdx++; + if (this.markupIdx == this.markupElms.length) { + SimpleTest.finish(); + return; + } + + document.body.removeChild(this.container); + + this.ruleIdx = -1; + testNamesForMarkup(this.markupElms[this.markupIdx]); + return; + } + + testNameForRule(this.elm, this.ruleElms[this.ruleIdx]); + }, + + markupElms: null, + markupIdx: -1, + ruleElms: null, + ruleIdx: -1, + elm: null, + container: null +}; + /** * Process every 'markup' element and test names for it. Used by testNames * function. @@ -50,7 +103,7 @@ function testNames() function testNamesForMarkup(aMarkupElm) { var div = document.createElement("div"); - div.setAttribute("test", "test"); + div.setAttribute("id", "test"); var child = aMarkupElm.firstChild; while (child) { @@ -58,90 +111,114 @@ function testNamesForMarkup(aMarkupElm) div.appendChild(newChild); child = child.nextSibling; } + + waitForEvent(EVENT_REORDER, document, testNamesForMarkupRules, + null, aMarkupElm, div); + document.body.appendChild(div); +} + +function testNamesForMarkupRules(aMarkupElm, aContainer) +{ + ensureAccessibleTree(aContainer); var serializer = new XMLSerializer(); - var expr = "//html/body/div[@test='test']/" + aMarkupElm.getAttribute("ref"); + var expr = "//html/body/div[@id='test']/" + aMarkupElm.getAttribute("ref"); var elms = evaluateXPath(document, expr, htmlDocResolver); var ruleId = aMarkupElm.getAttribute("ruleset"); var ruleElms = getRuleElmsByRulesetId(ruleId); - for (var idx = 0; idx < ruleElms.length; idx++) - testNameForRule(elms[0], ruleElms[idx]); - - document.body.removeChild(div); + gTestIterator.iterateRules(elms[0], aContainer, ruleElms); } /** * Test name for current rule and current 'markup' element. Used by * testNamesForMarkup function. */ -function testNameForRule(aElm, aRule) +function testNameForRule(aElm, aRuleElm) { + if (aRuleElm.hasAttribute("attr")) + testNameForAttrRule(aElm, aRuleElm); + else if (aRuleElm.hasAttribute("elm") && aRuleElm.hasAttribute("elmattr")) + testNameForElmRule(aElm, aRuleElm); + else if (aRuleElm.getAttribute("fromsubtree") == "true") + testNameForSubtreeRule(aElm, aRuleElm); +} + +function testNameForAttrRule(aElm, aRule) +{ + var name = ""; + var attr = aRule.getAttribute("attr"); - if (attr) { - var name = ""; - var attrValue = aElm.getAttribute(attr); - - var type = aRule.getAttribute("type"); - if (type == "string") { - name = attrValue; + var attrValue = aElm.getAttribute(attr); - } else if (type == "ref") { - var ids = attrValue.split(/\s+/);///\,\s*/); - for (var idx = 0; idx < ids.length; idx++) { - var labelElm = getNode(ids[idx]); - if (name != "") - name += " "; + var type = aRule.getAttribute("type"); + if (type == "string") { + name = attrValue; - name += labelElm.getAttribute("a11yname"); - } + } else if (type == "ref") { + var ids = attrValue.split(/\s+/); + for (var idx = 0; idx < ids.length; idx++) { + var labelElm = getNode(ids[idx]); + if (name != "") + name += " "; + + name += labelElm.getAttribute("a11yname"); } - - var msg = "Attribute '" + attr + "' test. "; - testName(aElm, name, msg); - aElm.removeAttribute(attr); - return; } + var msg = "Attribute '" + attr + "' test. "; + testName(aElm, name, msg); + aElm.removeAttribute(attr); + + gTestIterator.iterateNext(); +} + +function testNameForElmRule(aElm, aRule) +{ var elm = aRule.getAttribute("elm"); var elmattr = aRule.getAttribute("elmattr"); - if (elm && elmattr) { - var filter = { - acceptNode: function filter_acceptNode(aNode) - { - if (aNode.localName == this.mLocalName && - aNode.getAttribute(this.mAttrName) == this.mAttrValue) - return NodeFilter.FILTER_ACCEPT; - return NodeFilter.FILTER_SKIP; - }, + var filter = { + acceptNode: function filter_acceptNode(aNode) + { + if (aNode.localName == this.mLocalName && + aNode.getAttribute(this.mAttrName) == this.mAttrValue) + return NodeFilter.FILTER_ACCEPT; - mLocalName: elm, - mAttrName: elmattr, - mAttrValue: aElm.getAttribute("id") - }; + return NodeFilter.FILTER_SKIP; + }, - var treeWalker = document.createTreeWalker(document.body, - NodeFilter.SHOW_ELEMENT, - filter, false); - var labelElm = treeWalker.nextNode(); - var msg = "Element '" + elm + "' test."; - testName(aElm, labelElm.getAttribute("a11yname"), msg); - labelElm.parentNode.removeChild(labelElm); - return; - } + mLocalName: elm, + mAttrName: elmattr, + mAttrValue: aElm.getAttribute("id") + }; - var fromSubtree = aRule.getAttribute("fromsubtree"); - if (fromSubtree == "true") { - var msg = "From subtree test."; - testName(aElm, aElm.getAttribute("a11yname"), msg); - while (aElm.firstChild) - aElm.removeChild(aElm.firstChild); - return; - } + var treeWalker = document.createTreeWalker(document.body, + NodeFilter.SHOW_ELEMENT, + filter, false); + var labelElm = treeWalker.nextNode(); + var msg = "Element '" + elm + "' test."; + testName(aElm, labelElm.getAttribute("a11yname"), msg); + + var parentNode = labelElm.parentNode; + waitForEvent(EVENT_REORDER, parentNode, + gTestIterator.iterateNext, gTestIterator); + + parentNode.removeChild(labelElm); +} + +function testNameForSubtreeRule(aElm, aRule) +{ + var msg = "From subtree test."; + testName(aElm, aElm.getAttribute("a11yname"), msg); + + waitForEvent(EVENT_REORDER, aElm, gTestIterator.iterateNext, gTestIterator); + + while (aElm.firstChild) + aElm.removeChild(aElm.firstChild); } /** diff --git a/accessible/tests/mochitest/test_name_markup.html b/accessible/tests/mochitest/test_name_markup.html index 34cb74a25846..abe5fd780108 100644 --- a/accessible/tests/mochitest/test_name_markup.html +++ b/accessible/tests/mochitest/test_name_markup.html @@ -1,7 +1,7 @@ - nsIAccessible::name calculation for HTML buttons + nsIAccessible::name calculation for elements + @@ -31,7 +28,7 @@ + title="nsIAccessible::name calculation for elements"> Mozilla Bug 459635

@@ -39,6 +36,6 @@
   
- +
diff --git a/accessible/tests/mochitest/test_nsIAccessible_name.html b/accessible/tests/mochitest/test_nsIAccessible_name.html index ec354aef7b9b..bbcdb187ab8d 100644 --- a/accessible/tests/mochitest/test_nsIAccessible_name.html +++ b/accessible/tests/mochitest/test_nsIAccessible_name.html @@ -35,6 +35,10 @@ // of elements. Gets the name from text nodes of those elements. testName("btn_labelledby_texts", "text1 text2"); + ////////////////////////////////////////////////////////////////////////// + // Name from named accessible + + testName("input_labelledby_namedacc", "Data"); ////////////////////////////////////////////////////////////////////////// // Name from subtree (single relation labelled_by). @@ -129,10 +133,21 @@ testName("textareawithchild", "Story: Bar"); ///////////////////////////////////////////////////////////////////////// - // label with nested combobox + // label with nested combobox (test for 'f' item of name computation guide) + + testName("comboinstart", "One day(s)."); + testName("combo3", "day(s)."); testName("comboinmiddle", "Subscribe to ATOM feed."); - + testName("combo4", "Subscribe to ATOM feed."); + + testName("comboinmiddle2", "Play the Haliluya sound when new mail arrives"); + testName("combo5", "Play the Haliluya sound when new mail arrives"); + testName("checkbox", "Play the Haliluya sound when new mail arrives"); + + testName("comboinend", "This day was sunny"); + testName("combo6", "This day was"); + SimpleTest.finish(); } @@ -175,6 +190,12 @@ aria-labelledby="labelledby_text1 labelledby_text2">2
+ + + + nomore text