Bug 637578 - Expose explict-name object attribute, r=tbsaunde

This commit is contained in:
Alexander Surkov 2012-10-17 15:38:16 +09:00
parent aee349a1a3
commit 02b3cceee5
16 changed files with 90 additions and 62 deletions

View File

@ -32,7 +32,7 @@ nsTextEquivUtils::GetNameFromSubtree(Accessible* aAccessible,
return NS_OK; return NS_OK;
gInitiatorAcc = aAccessible; gInitiatorAcc = aAccessible;
if (GetRoleRule(aAccessible->Role()) == eFromSubtree) { if (IsNameFromSubtreeAllowed(aAccessible)) {
//XXX: is it necessary to care the accessible is not a document? //XXX: is it necessary to care the accessible is not a document?
if (aAccessible->IsContent()) { if (aAccessible->IsContent()) {
nsAutoString name; nsAutoString name;

View File

@ -89,6 +89,14 @@ public:
static nsresult AppendTextEquivFromTextContent(nsIContent *aContent, static nsresult AppendTextEquivFromTextContent(nsIContent *aContent,
nsAString *aString); nsAString *aString);
/**
* Return true if the given accessible allows name from subtree.
*/
static bool IsNameFromSubtreeAllowed(Accessible* aAccessible)
{
return GetRoleRule(aAccessible->Role()) == eFromSubtree;
}
private: private:
/** /**
* Iterates accessible children and calculates text equivalent from each * Iterates accessible children and calculates text equivalent from each

View File

@ -275,7 +275,7 @@ Accessible::Name(nsString& aName)
ENameValueFlag nameFlag = NativeName(aName); ENameValueFlag nameFlag = NativeName(aName);
if (!aName.IsEmpty()) if (!aName.IsEmpty())
return eNameOK; return nameFlag;
// In the end get the name from tooltip. // In the end get the name from tooltip.
if (mContent->IsHTML()) { if (mContent->IsHTML()) {
@ -288,14 +288,12 @@ Accessible::Name(nsString& aName)
aName.CompressWhitespace(); aName.CompressWhitespace();
return eNameFromTooltip; return eNameFromTooltip;
} }
} else {
return eNameOK;
} }
if (nameFlag != eNoNameOnPurpose) if (nameFlag != eNoNameOnPurpose)
aName.SetIsVoid(true); aName.SetIsVoid(true);
return eNameOK; return nameFlag;
} }
NS_IMETHODIMP NS_IMETHODIMP
@ -1055,7 +1053,7 @@ Accessible::TakeFocus()
return NS_OK; return NS_OK;
} }
void ENameValueFlag
Accessible::GetHTMLName(nsString& aLabel) Accessible::GetHTMLName(nsString& aLabel)
{ {
Accessible* labelAcc = nullptr; Accessible* labelAcc = nullptr;
@ -1066,8 +1064,11 @@ Accessible::GetHTMLName(nsString& aLabel)
aLabel.CompressWhitespace(); aLabel.CompressWhitespace();
} }
if (aLabel.IsEmpty()) if (!aLabel.IsEmpty())
nsTextEquivUtils::GetNameFromSubtree(this, aLabel); return eNameOK;
nsTextEquivUtils::GetNameFromSubtree(this, aLabel);
return aLabel.IsEmpty() ? eNameOK : eNameFromSubtree;
} }
/** /**
@ -1082,7 +1083,7 @@ Accessible::GetHTMLName(nsString& aLabel)
* the control that uses the control="controlID" syntax will use * the control that uses the control="controlID" syntax will use
* the child label for its Name. * the child label for its Name.
*/ */
void ENameValueFlag
Accessible::GetXULName(nsString& aName) Accessible::GetXULName(nsString& aName)
{ {
// CASE #1 (via label attribute) -- great majority of the cases // CASE #1 (via label attribute) -- great majority of the cases
@ -1125,10 +1126,9 @@ Accessible::GetXULName(nsString& aName)
} }
} }
// XXX If CompressWhiteSpace worked on nsAString we could avoid a copy
aName.CompressWhitespace(); aName.CompressWhitespace();
if (!aName.IsEmpty()) if (!aName.IsEmpty())
return; return eNameOK;
// Can get text from title of <toolbaritem> if we're a child of a <toolbaritem> // Can get text from title of <toolbaritem> if we're a child of a <toolbaritem>
nsIContent *bindingParent = mContent->GetBindingParent(); nsIContent *bindingParent = mContent->GetBindingParent();
@ -1138,12 +1138,13 @@ Accessible::GetXULName(nsString& aName)
if (parent->Tag() == nsGkAtoms::toolbaritem && if (parent->Tag() == nsGkAtoms::toolbaritem &&
parent->GetAttr(kNameSpaceID_None, nsGkAtoms::title, aName)) { parent->GetAttr(kNameSpaceID_None, nsGkAtoms::title, aName)) {
aName.CompressWhitespace(); aName.CompressWhitespace();
return; return eNameOK;
} }
parent = parent->GetParent(); parent = parent->GetParent();
} }
nsTextEquivUtils::GetNameFromSubtree(this, aName); nsTextEquivUtils::GetNameFromSubtree(this, aName);
return aName.IsEmpty() ? eNameOK : eNameFromSubtree;
} }
nsresult nsresult
@ -1229,6 +1230,13 @@ Accessible::GetAttributes(nsIPersistentProperties **aAttributes)
if (State() & states::CHECKABLE) if (State() & states::CHECKABLE)
nsAccUtils::SetAccAttr(attributes, nsGkAtoms::checkable, NS_LITERAL_STRING("true")); nsAccUtils::SetAccAttr(attributes, nsGkAtoms::checkable, NS_LITERAL_STRING("true"));
// Expose 'explicit-name' attribute.
if (!nsTextEquivUtils::IsNameFromSubtreeAllowed(this) ||
Name(oldValueUnused) != eNameFromSubtree) {
attributes->SetStringProperty(NS_LITERAL_CSTRING("explicit-name"),
NS_LITERAL_STRING("true"), oldValueUnused);
}
// Group attributes (level/setsize/posinset) // Group attributes (level/setsize/posinset)
GroupPos groupPos = GroupPosition(); GroupPos groupPos = GroupPosition();
nsAccUtils::SetAccGroupAttrs(attributes, groupPos.level, nsAccUtils::SetAccGroupAttrs(attributes, groupPos.level,
@ -2420,23 +2428,18 @@ Accessible::Shutdown()
// Accessible protected // Accessible protected
void void
Accessible::ARIAName(nsAString& aName) Accessible::ARIAName(nsString& aName)
{ {
nsAutoString label;
// aria-labelledby now takes precedence over aria-label // aria-labelledby now takes precedence over aria-label
nsresult rv = nsTextEquivUtils:: nsresult rv = nsTextEquivUtils::
GetTextEquivFromIDRefs(this, nsGkAtoms::aria_labelledby, label); GetTextEquivFromIDRefs(this, nsGkAtoms::aria_labelledby, aName);
if (NS_SUCCEEDED(rv)) { if (NS_SUCCEEDED(rv)) {
label.CompressWhitespace(); aName.CompressWhitespace();
aName = label;
} }
if (label.IsEmpty() && if (aName.IsEmpty() &&
mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_label, mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_label, aName)) {
label)) { aName.CompressWhitespace();
label.CompressWhitespace();
aName = label;
} }
} }
@ -2445,9 +2448,10 @@ ENameValueFlag
Accessible::NativeName(nsString& aName) Accessible::NativeName(nsString& aName)
{ {
if (mContent->IsHTML()) if (mContent->IsHTML())
GetHTMLName(aName); return GetHTMLName(aName);
else if (mContent->IsXUL())
GetXULName(aName); if (mContent->IsXUL())
return GetXULName(aName);
return eNameOK; return eNameOK;
} }

View File

@ -58,6 +58,11 @@ enum ENameValueFlag {
*/ */
eNoNameOnPurpose, eNoNameOnPurpose,
/**
* Name was computed from the subtree.
*/
eNameFromSubtree,
/** /**
* Tooltip was used as a name. * Tooltip was used as a name.
*/ */
@ -805,13 +810,13 @@ protected:
/** /**
* Returns the accessible name specified by ARIA. * Returns the accessible name specified by ARIA.
*/ */
void ARIAName(nsAString& aName); void ARIAName(nsString& aName);
/** /**
* Compute the name of HTML/XUL node. * Compute the name of HTML/XUL node.
*/ */
void GetHTMLName(nsString& aName); mozilla::a11y::ENameValueFlag GetHTMLName(nsString& aName);
void GetXULName(nsString& aName); mozilla::a11y::ENameValueFlag GetXULName(nsString& aName);
// helper method to verify frames // helper method to verify frames
static nsresult GetFullKeyName(const nsAString& aModifierName, const nsAString& aKeyName, nsAString& aStringOut); static nsresult GetFullKeyName(const nsAString& aModifierName, const nsAString& aKeyName, nsAString& aStringOut);

View File

@ -1953,12 +1953,14 @@ HyperTextAccessible::ScrollSubstringToPoint(int32_t aStartIndex,
ENameValueFlag ENameValueFlag
HyperTextAccessible::NativeName(nsString& aName) HyperTextAccessible::NativeName(nsString& aName)
{ {
AccessibleWrap::NativeName(aName); ENameValueFlag nameFlag = AccessibleWrap::NativeName(aName);
if (!aName.IsEmpty())
return nameFlag;
// Get name from title attribute for HTML abbr and acronym elements making it // Get name from title attribute for HTML abbr and acronym elements making it
// a valid name from markup. Otherwise their name isn't picked up by recursive // a valid name from markup. Otherwise their name isn't picked up by recursive
// name computation algorithm. See NS_OK_NAME_FROM_TOOLTIP. // name computation algorithm. See NS_OK_NAME_FROM_TOOLTIP.
if (aName.IsEmpty() && IsAbbreviation() && if (IsAbbreviation() &&
mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::title, aName)) mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::title, aName))
aName.CompressWhitespace(); aName.CompressWhitespace();

View File

@ -77,16 +77,15 @@ ImageAccessible::NativeName(nsString& aName)
if (!aName.IsEmpty()) if (!aName.IsEmpty())
return eNameOK; return eNameOK;
Accessible::NativeName(aName); ENameValueFlag nameFlag = Accessible::NativeName(aName);
if (aName.IsEmpty() && hasAltAttrib) { if (!aName.IsEmpty())
// No accessible name but empty 'alt' attribute is present. If further name return nameFlag;
// computation algorithm doesn't provide non empty name then it means
// an empty 'alt' attribute was used to indicate a decorative image (see
// nsIAccessible::name attribute for details).
return eNoNameOnPurpose;
}
return eNameOK; // No accessible name but empty 'alt' attribute is present. If further name
// computation algorithm doesn't provide non empty name then it means
// an empty 'alt' attribute was used to indicate a decorative image (see
// Accessible::Name() method for details).
return hasAltAttrib ? eNoNameOnPurpose : eNameOK;
} }
role role

View File

@ -58,7 +58,7 @@ ENameValueFlag
HTMLLabelAccessible::NativeName(nsString& aName) HTMLLabelAccessible::NativeName(nsString& aName)
{ {
nsTextEquivUtils::GetNameFromSubtree(this, aName); nsTextEquivUtils::GetNameFromSubtree(this, aName);
return eNameOK; return aName.IsEmpty() ? eNameOK : eNameFromSubtree;
} }
role role

View File

@ -259,13 +259,13 @@ HTMLButtonAccessible::NativeRole()
ENameValueFlag ENameValueFlag
HTMLButtonAccessible::NativeName(nsString& aName) HTMLButtonAccessible::NativeName(nsString& aName)
{ {
Accessible::NativeName(aName); ENameValueFlag nameFlag = Accessible::NativeName(aName);
if (!aName.IsEmpty() || mContent->Tag() != nsGkAtoms::input) if (!aName.IsEmpty() || mContent->Tag() != nsGkAtoms::input)
return eNameOK; return nameFlag;
// No name from HTML or ARIA // Note: No need to check @value attribute since it results in anonymous text
if (!mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::value, aName) && // node. The name is calculated from subtree in this case.
!mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::alt, aName)) { if (!mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::alt, aName)) {
// Use the button's (default) label if nothing else works // Use the button's (default) label if nothing else works
nsIFrame* frame = GetFrame(); nsIFrame* frame = GetFrame();
if (frame) { if (frame) {
@ -323,9 +323,9 @@ HTMLTextFieldAccessible::NativeRole()
ENameValueFlag ENameValueFlag
HTMLTextFieldAccessible::NativeName(nsString& aName) HTMLTextFieldAccessible::NativeName(nsString& aName)
{ {
Accessible::NativeName(aName); ENameValueFlag nameFlag = Accessible::NativeName(aName);
if (!aName.IsEmpty()) if (!aName.IsEmpty())
return eNameOK; return nameFlag;
if (mContent->GetBindingParent()) { if (mContent->GetBindingParent()) {
// XXX: bug 459640 // XXX: bug 459640
@ -613,9 +613,9 @@ HTMLGroupboxAccessible::GetLegend()
ENameValueFlag ENameValueFlag
HTMLGroupboxAccessible::NativeName(nsString& aName) HTMLGroupboxAccessible::NativeName(nsString& aName)
{ {
Accessible::NativeName(aName); ENameValueFlag nameFlag = Accessible::NativeName(aName);
if (!aName.IsEmpty()) if (!aName.IsEmpty())
return eNameOK; return nameFlag;
nsIContent* legendContent = GetLegend(); nsIContent* legendContent = GetLegend();
if (legendContent) if (legendContent)
@ -696,9 +696,9 @@ HTMLFigureAccessible::NativeRole()
ENameValueFlag ENameValueFlag
HTMLFigureAccessible::NativeName(nsString& aName) HTMLFigureAccessible::NativeName(nsString& aName)
{ {
HyperTextAccessibleWrap::NativeName(aName); ENameValueFlag nameFlag = HyperTextAccessibleWrap::NativeName(aName);
if (!aName.IsEmpty()) if (!aName.IsEmpty())
return eNameOK; return nameFlag;
nsIContent* captionContent = Caption(); nsIContent* captionContent = Caption();
if (captionContent) if (captionContent)

View File

@ -163,9 +163,9 @@ HTMLAreaAccessible::
ENameValueFlag ENameValueFlag
HTMLAreaAccessible::NativeName(nsString& aName) HTMLAreaAccessible::NativeName(nsString& aName)
{ {
Accessible::NativeName(aName); ENameValueFlag nameFlag = Accessible::NativeName(aName);
if (!aName.IsEmpty()) if (!aName.IsEmpty())
return eNameOK; return nameFlag;
if (!mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::alt, aName)) if (!mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::alt, aName))
GetValue(aName); GetValue(aName);

View File

@ -200,6 +200,7 @@ HTMLSelectOptionAccessible::NativeName(nsString& aName)
if (text && text->IsNodeOfType(nsINode::eTEXT)) { if (text && text->IsNodeOfType(nsINode::eTEXT)) {
nsTextEquivUtils::AppendTextEquivFromTextContent(text, &aName); nsTextEquivUtils::AppendTextEquivFromTextContent(text, &aName);
aName.CompressWhitespace(); aName.CompressWhitespace();
return aName.IsEmpty() ? eNameOK : eNameFromSubtree;
} }
return eNameOK; return eNameOK;

View File

@ -388,9 +388,9 @@ HTMLTableAccessible::NativeState()
ENameValueFlag ENameValueFlag
HTMLTableAccessible::NativeName(nsString& aName) HTMLTableAccessible::NativeName(nsString& aName)
{ {
Accessible::NativeName(aName); ENameValueFlag nameFlag = Accessible::NativeName(aName);
if (!aName.IsEmpty()) if (!aName.IsEmpty())
return eNameOK; return nameFlag;
// Use table caption as a name. // Use table caption as a name.
Accessible* caption = Caption(); Accessible* caption = Caption();

View File

@ -125,10 +125,11 @@ ENameValueFlag
XULLinkAccessible::NativeName(nsString& aName) XULLinkAccessible::NativeName(nsString& aName)
{ {
mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::value, aName); mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::value, aName);
if (aName.IsEmpty()) if (!aName.IsEmpty())
nsTextEquivUtils::GetNameFromSubtree(this, aName); return eNameOK;
return eNameOK; nsTextEquivUtils::GetNameFromSubtree(this, aName);
return aName.IsEmpty() ? eNameOK : eNameFromSubtree;
} }
role role

View File

@ -634,8 +634,7 @@ XULListitemAccessible::NativeName(nsString& aName)
} }
} }
GetXULName(aName); return GetXULName(aName);
return eNameOK;
} }
role role

View File

@ -188,6 +188,11 @@ function testNameForAttrRule(aElm, aRule)
var msg = "Attribute '" + attr + "' test. "; var msg = "Attribute '" + attr + "' test. ";
testName(aElm, name, msg); testName(aElm, name, msg);
if (aRule.getAttribute("explict-name") != "false")
testAttrs(aElm, {"explicit-name" : "true"}, true);
else
testAbsentAttrs(aElm, {"explicit-name" : "true"});
aElm.removeAttribute(attr); aElm.removeAttribute(attr);
gTestIterator.iterateNext(); gTestIterator.iterateNext();
@ -235,6 +240,7 @@ function testNameForElmRule(aElm, aRule)
var msg = "Element '" + tagname + "' test."; var msg = "Element '" + tagname + "' test.";
testName(aElm, labelElm.getAttribute("a11yname"), msg); testName(aElm, labelElm.getAttribute("a11yname"), msg);
testAttrs(aElm, {"explicit-name" : "true"}, true);
var parentNode = labelElm.parentNode; var parentNode = labelElm.parentNode;
@ -252,6 +258,7 @@ function testNameForSubtreeRule(aElm, aRule)
{ {
var msg = "From subtree test."; var msg = "From subtree test.";
testName(aElm, aElm.getAttribute("a11yname"), msg); testName(aElm, aElm.getAttribute("a11yname"), msg);
testAbsentAttrs(aElm, {"explicit-name" : "true"});
if (gDumpToConsole) { if (gDumpToConsole) {
dump("\nProcessed from subtree rule. Wait for reorder event on " + dump("\nProcessed from subtree rule. Wait for reorder event on " +

View File

@ -125,7 +125,7 @@
<!-- specific --> <!-- specific -->
<ruleset id="htmlinputbutton"> <ruleset id="htmlinputbutton">
<ruleset ref="htmlelm_start"/> <ruleset ref="htmlelm_start"/>
<rule attr="value" type="string"/> <rule attr="value" type="string" explict-name="false"/>
<rule attr="alt" type="string"/> <rule attr="alt" type="string"/>
<rule attr="src" type="string"/> <rule attr="src" type="string"/>
<rule attr="data" type="string"/> <rule attr="data" type="string"/>

View File

@ -13,6 +13,8 @@
src="../events.js"></script> src="../events.js"></script>
<script type="application/javascript" <script type="application/javascript"
src="../name.js"></script> src="../name.js"></script>
<script type="application/javascript"
src="../attributes.js"></script>
<script type="application/javascript" <script type="application/javascript"
src="markup.js"></script> src="markup.js"></script>