Bug 395909. Should not require namespaces for ARIA role usage in text/html. r=surkov, a=dsicore

This commit is contained in:
aaronleventhal@moonset.net 2007-09-18 14:43:19 -07:00
parent 3347682055
commit 558c745412
7 changed files with 107 additions and 60 deletions

View File

@ -55,7 +55,7 @@ interface nsIAccessibilityService : nsIAccessibleRetrieval
nsIAccessible createHyperTextAccessible(in nsISupports aFrame);
nsIAccessible createHTMLBRAccessible(in nsISupports aFrame);
nsIAccessible createHTMLButtonAccessible(in nsISupports aFrame);
nsIAccessible createHTMLAccessibleByMarkup(in nsIFrame aFrame, in nsIWeakReference aWeakShell, in nsIDOMNode aDOMNode, in AString aRole);
nsIAccessible createHTMLAccessibleByMarkup(in nsIFrame aFrame, in nsIWeakReference aWeakShell, in nsIDOMNode aDOMNode);
nsIAccessible createHTMLLIAccessible(in nsISupports aFrame, in nsISupports aBulletFrame, in AString aBulletText);
nsIAccessible createHTMLCheckboxAccessible(in nsISupports aFrame);
nsIAccessible createHTMLComboboxAccessible(in nsIDOMNode aNode, in nsIWeakReference aPresShell);

View File

@ -47,12 +47,14 @@
#include "nsIDocShellTreeItem.h"
#include "nsIDocument.h"
#include "nsIDocumentViewer.h"
#include "nsIDOM3Node.h"
#include "nsIDOMCSSStyleDeclaration.h"
#include "nsIDOMCSSPrimitiveValue.h"
#include "nsIDOMDocument.h"
#include "nsIDOMElement.h"
#include "nsIDOMHTMLDocument.h"
#include "nsIDOMHTMLElement.h"
#include "nsIDOMNSDocument.h"
#include "nsIDOMNSHTMLElement.h"
#include "nsIDOMViewCSS.h"
#include "nsIDOMWindow.h"
@ -890,3 +892,71 @@ nsAccessNode::GetLanguage(nsAString& aLanguage)
return NS_OK;
}
PRBool
nsAccessNode::GetARIARole(nsIContent *aContent, nsString& aRole)
{
nsAutoString prefix;
PRBool strictPrefixChecking = PR_TRUE;
aRole.Truncate();
if (aContent->IsNodeOfType(nsINode::eHTML)) { // HTML node
// Allow non-namespaced role attribute in HTML
aContent->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::role, aRole);
// Find non-namespaced role attribute on HTML node
nsCOMPtr<nsIDOMNSDocument> doc(do_QueryInterface(aContent->GetDocument()));
if (doc) {
// In text/html we are hardcoded to allow the exact prefix "wairole:" to
// always indicate that we are using the WAI roles.
// This allows ARIA to be used within text/html where namespaces cannot be defined.
// We also now relax the prefix checking, which means no prefix is required to use WAI Roles
nsAutoString mimeType;
doc->GetContentType(mimeType);
if (mimeType.EqualsLiteral("text/html")) {
prefix = NS_LITERAL_STRING("wairole:");
strictPrefixChecking = PR_FALSE;
}
}
}
// Try namespaced-role attribute (xhtml or xhtml2 namespace) -- allowed in any kind of content
if (aRole.IsEmpty() && !aContent->GetAttr(kNameSpaceID_XHTML, nsAccessibilityAtoms::role, aRole) &&
!aContent->GetAttr(kNameSpaceID_XHTML2_Unofficial, nsAccessibilityAtoms::role, aRole)) {
return PR_FALSE;
}
PRBool hasPrefix = (aRole.Find(":") >= 0);
if (!hasPrefix) {
// * No prefix* -- not a QName
// Just return entire string as long as prefix is not currently required
if (strictPrefixChecking) {
// Prefix was required and we didn't have one
aRole.Truncate();
return PR_FALSE;
}
return PR_TRUE;
}
// * Has prefix * -- is a QName (role="prefix:rolename")
if (strictPrefixChecking) { // Not text/html, we need to actually find the WAIRole prefix
// QI to nsIDOM3Node causes some overhead. Unfortunately we need to do this each
// time there is a prefixed role attribute, because the prefix to namespace mappings
// can change within any subtree via the xmlns attribute
nsCOMPtr<nsIDOM3Node> dom3Node(do_QueryInterface(aContent));
if (dom3Node) {
// Look up exact prefix name for WAI Roles
NS_NAMED_LITERAL_STRING(kWAIRoles_Namespace, "http://www.w3.org/2005/01/wai-rdf/GUIRoleTaxonomy#");
dom3Node->LookupPrefix(kWAIRoles_Namespace, prefix);
prefix += ':';
}
}
PRUint32 length = prefix.Length();
if (length > 1 && StringBeginsWith(aRole, prefix)) {
// Is a QName (role="prefix:rolename"), and prefix matches WAI Role prefix
// Trim the WAI Role prefix off
aRole.Cut(0, length);
}
return PR_TRUE;
}

View File

@ -71,6 +71,16 @@ class nsApplicationAccessibleWrap;
typedef nsInterfaceHashtable<nsVoidPtrHashKey, nsIAccessNode>
nsAccessNodeHashtable;
/**
* Does the current content have this ARIA role?
* Implemented as a compiler macro so that length can be computed at compile time.
* @param aContent Node to get role string from
* @param aRoleName Role string to compare with -- literal const char*
* @return PR_TRUE if there is a match
*/
#define ARIARoleEquals(aContent, aRoleName) \
nsAccessNode::ARIARoleEqualsImpl(aContent, aRoleName, NS_ARRAY_LENGTH(aRoleName) - 1)
class nsAccessNode: public nsIAccessNode, public nsPIAccessNode
{
public: // construction, destruction
@ -114,14 +124,16 @@ class nsAccessNode: public nsIAccessNode, public nsPIAccessNode
aContent->HasAttr(kNameSpaceID_XHTML2_Unofficial, nsAccessibilityAtoms::role);
}
// Return PR_TRUE if there is a role attribute, and fill it into aRole
static PRBool GetRoleAttribute(nsIContent *aContent, nsAString& aRole)
{
aRole.Truncate();
return (aContent->IsNodeOfType(nsINode::eHTML) && aContent->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::role, aRole)) ||
aContent->GetAttr(kNameSpaceID_XHTML, nsAccessibilityAtoms::role, aRole) ||
aContent->GetAttr(kNameSpaceID_XHTML2_Unofficial, nsAccessibilityAtoms::role, aRole);
}
/**
* Provide the role string if there is one
* @param aContent Node to get role string from
* @param aRole String to fill role into
* @return PR_TRUE if there is a role attribute, and fill it into aRole
*/
static PRBool GetARIARole(nsIContent *aContent, nsString& aRole);
static PRBool ARIARoleEqualsImpl(nsIContent* aContent, const char* aRoleName, PRUint32 aLen)
{ nsAutoString role; return GetARIARole(aContent, role) && role.EqualsASCII(aRoleName, aLen); }
static void GetComputedStyleDeclaration(const nsAString& aPseudoElt,
nsIDOMElement *aElement,

View File

@ -434,7 +434,6 @@ nsresult
nsAccessibilityService::CreateHTMLAccessibleByMarkup(nsIFrame *aFrame,
nsIWeakReference *aWeakShell,
nsIDOMNode *aNode,
const nsAString& aRole,
nsIAccessible **aAccessible)
{
// This method assumes we're in an HTML namespace.
@ -1337,9 +1336,7 @@ NS_IMETHODIMP nsAccessibilityService::GetAccessible(nsIDOMNode *aNode,
}
nsAutoString role;
if (nsAccessNode::GetRoleAttribute(content, role) &&
StringEndsWith(role, NS_LITERAL_STRING(":presentation")) &&
!content->IsFocusable()) {
if (nsAccessNode::GetARIARole(content, role) && role.EqualsLiteral("presentation") && !content->IsFocusable()) {
// Only create accessible for role=":presentation" if it is focusable --
// in that case we need an accessible in case it gets focused, we
// don't want focus ever to be 'lost'
@ -1366,7 +1363,7 @@ NS_IMETHODIMP nsAccessibilityService::GetAccessible(nsIDOMNode *aNode,
} else if (!newAcc) { // HTML accessibles
// Prefer to use markup (mostly tag name, perhaps attributes) to
// decide if and what kind of accessible to create.
CreateHTMLAccessibleByMarkup(frame, aWeakShell, aNode, role, getter_AddRefs(newAcc));
CreateHTMLAccessibleByMarkup(frame, aWeakShell, aNode, getter_AddRefs(newAcc));
PRBool tryFrame = (newAcc == nsnull);
if (!content->IsFocusable()) {

View File

@ -45,7 +45,6 @@
#include "nsIAccessibleHyperText.h"
#include "nsAccessibleTreeWalker.h"
#include "nsIDOM3Node.h"
#include "nsIDOMElement.h"
#include "nsIDOMDocument.h"
#include "nsIDOMDocumentXBL.h"
@ -53,7 +52,6 @@
#include "nsIDOMHTMLDocument.h"
#include "nsIDOMHTMLFormElement.h"
#include "nsIDOMNodeFilter.h"
#include "nsIDOMNSDocument.h"
#include "nsIDOMNSHTMLElement.h"
#include "nsIDOMTreeWalker.h"
#include "nsIDOMXULButtonElement.h"
@ -475,45 +473,18 @@ NS_IMETHODIMP nsAccessible::Init()
{
nsIContent *content = GetRoleContent(mDOMNode);
nsAutoString roleString;
if (content && GetRoleAttribute(content, roleString)) {
// QI to nsIDOM3Node causes some overhead. Unfortunately we need to do this each
// time there is a role attribute, because the prefixe to namespace mappings
// can change within any subtree via the xmlns attribute
nsCOMPtr<nsIDOM3Node> dom3Node(do_QueryInterface(content));
if (dom3Node) {
nsAutoString prefix;
NS_NAMED_LITERAL_STRING(kWAIRoles_Namespace, "http://www.w3.org/2005/01/wai-rdf/GUIRoleTaxonomy#");
dom3Node->LookupPrefix(kWAIRoles_Namespace, prefix);
if (prefix.IsEmpty()) {
// In HTML we are hardcoded to allow the exact prefix "wairole:" to
// always indicate that we are using the WAI roles. This allows DHTML accessibility
// to be used within HTML
nsCOMPtr<nsIDOMNSDocument> doc(do_QueryInterface(content->GetDocument()));
if (doc) {
nsAutoString mimeType;
doc->GetContentType(mimeType);
if (mimeType.EqualsLiteral("text/html")) {
prefix = NS_LITERAL_STRING("wairole");
}
}
}
prefix += ':';
PRUint32 length = prefix.Length();
if (length > 1 && StringBeginsWith(roleString, prefix)) {
roleString.Cut(0, length);
nsCString utf8Role = NS_ConvertUTF16toUTF8(roleString); // For easy comparison
ToLowerCase(utf8Role);
PRUint32 index;
for (index = 0; nsARIAMap::gWAIRoleMap[index].roleString; index ++) {
if (utf8Role.Equals(nsARIAMap::gWAIRoleMap[index].roleString)) {
break; // The dynamic role attribute maps to an entry in our table
}
}
// Always use some entry if there is a role string
// If no match, we use the last entry which maps to ROLE_NOTHING
mRoleMapEntry = &nsARIAMap::gWAIRoleMap[index];
if (content && GetARIARole(content, roleString)) {
nsCString utf8Role = NS_ConvertUTF16toUTF8(roleString); // For easy comparison
ToLowerCase(utf8Role);
PRUint32 index;
for (index = 0; nsARIAMap::gWAIRoleMap[index].roleString; index ++) {
if (utf8Role.Equals(nsARIAMap::gWAIRoleMap[index].roleString)) {
break; // The dynamic role attribute maps to an entry in our table
}
}
// Always use some entry if there is a role string
// If no match, we use the last entry which maps to ROLE_NOTHING
mRoleMapEntry = &nsARIAMap::gWAIRoleMap[index];
}
return nsAccessNodeWrap::Init();
@ -2067,7 +2038,7 @@ nsAccessible::GetAttributes(nsIPersistentProperties **aAttributes)
// XXX In the future we may need to expose the dynamic content role inheritance chain
// through this attribute
nsAutoString xmlRole;
if (GetRoleAttribute(content, xmlRole)) {
if (GetARIARole(content, xmlRole)) {
attributes->SetStringProperty(NS_LITERAL_CSTRING("xml-roles"), xmlRole, oldValueUnused);
}

View File

@ -1807,9 +1807,7 @@ NS_IMETHODIMP nsDocAccessible::InvalidateCacheSubtree(nsIContent *aChild,
eCoalesceFromSameSubtree, isAsynch);
// Check to see change occured in an ARIA menu, and fire an EVENT_MENUPOPUP_START if it did
nsAutoString role;
if (GetRoleAttribute(aChild, role) &&
StringEndsWith(role, NS_LITERAL_STRING(":menu"), nsCaseInsensitiveStringComparator())) {
if (ARIARoleEquals(aChild, "menu")) {
FireDelayedToolkitEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_START,
childNode, nsnull, eAllowDupes, isAsynch);
}
@ -1817,8 +1815,7 @@ NS_IMETHODIMP nsDocAccessible::InvalidateCacheSubtree(nsIContent *aChild,
// Check to see if change occured inside an alert, and fire an EVENT_ALERT if it did
nsIContent *ancestor = aChild;
while (ancestor) {
if (GetRoleAttribute(ancestor, role) &&
StringEndsWith(role, NS_LITERAL_STRING(":alert"), nsCaseInsensitiveStringComparator())) {
if (ARIARoleEquals(ancestor, "alert")) {
nsCOMPtr<nsIDOMNode> alertNode(do_QueryInterface(ancestor));
FireDelayedToolkitEvent(nsIAccessibleEvent::EVENT_ALERT, alertNode, nsnull,
eRemoveDupes, isAsynch);

View File

@ -466,7 +466,7 @@ STDMETHODIMP nsAccessibleWrap::get_accRole(
if (content->IsNodeOfType(nsINode::eELEMENT)) {
nsAutoString roleString;
if (msaaRole != ROLE_SYSTEM_CLIENT && !GetRoleAttribute(content, roleString)) {
if (msaaRole != ROLE_SYSTEM_CLIENT && !GetARIARole(content, roleString)) {
nsINodeInfo *nodeInfo = content->NodeInfo();
nodeInfo->GetName(roleString);
nsAutoString nameSpaceURI;