mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
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
This commit is contained in:
parent
a9f284053f
commit
ed9e9ba487
@ -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)
|
||||
|
||||
|
@ -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<nsIDOMNode> domNode(do_QueryInterface(aContent));
|
||||
nsCOMPtr<nsIAccessible> accessible;
|
||||
if (domNode == mDOMNode) {
|
||||
accessible = this;
|
||||
if (!aFromValue) {
|
||||
// prevent recursive call GetName()
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
else {
|
||||
nsCOMPtr<nsIAccessibilityService> 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<nsIPresShell> shell = GetPresShell();
|
||||
if (!shell) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsIContent *parentContent = aContent->GetParent();
|
||||
nsCOMPtr<nsIContent> 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<nsIDOMXULLabeledControlElement> 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<nsIAtom> 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<nsIPresShell> 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<nsIDOMXULSelectControlElement> selectControlEl(do_QueryInterface(aContent));
|
||||
nsCOMPtr<nsIAtom> 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<nsIPresShell> shell = GetPresShell();
|
||||
NS_ENSURE_TRUE(shell, NS_ERROR_FAILURE);
|
||||
|
||||
PRUint32 index;
|
||||
for (index = 0; index < numChildren; index++) {
|
||||
nsCOMPtr<nsIContent> 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<nsIContent> content = nsCoreUtils::GetRoleContent(mDOMNode);
|
||||
if (!content)
|
||||
return NS_OK;
|
||||
|
||||
nsCOMPtr<nsIArray> 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<nsIContent> 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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 <surkov.alexander@gmail.com> (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
|
||||
};
|
@ -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 <surkov.alexander@gmail.com> (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
|
508
accessible/src/base/nsTextEquivUtils.cpp
Normal file
508
accessible/src/base/nsTextEquivUtils.cpp
Normal file
@ -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 <surkov.alexander@gmail.com> (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<nsIAccessNode> accessNode(do_QueryInterface(aAccessible));
|
||||
|
||||
nsCOMPtr<nsIDOMNode> DOMNode;
|
||||
accessNode->GetDOMNode(getter_AddRefs(DOMNode));
|
||||
nsCOMPtr<nsIContent> 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<nsIAccessNode> accessNode(do_QueryInterface(aAccessible));
|
||||
|
||||
nsCOMPtr<nsIDOMNode> DOMNode;
|
||||
accessNode->GetDOMNode(getter_AddRefs(DOMNode));
|
||||
|
||||
nsCOMPtr<nsIContent> content = nsCoreUtils::GetRoleContent(DOMNode);
|
||||
if (!content)
|
||||
return NS_OK;
|
||||
|
||||
nsCOMPtr<nsIArray> 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<nsIContent> 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<nsIDOMNode> DOMNode(do_QueryInterface(aContent));
|
||||
nsCOMPtr<nsIPresShell> 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<nsIAccessible> 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<nsIDOMNode> DOMNode(do_QueryInterface(aContent));
|
||||
|
||||
PRBool isHTMLBlock = PR_FALSE;
|
||||
nsCOMPtr<nsIPresShell> 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<nsIAccessible> nsTextEquivUtils::gInitiatorAcc;
|
||||
|
||||
nsresult
|
||||
nsTextEquivUtils::AppendFromAccessibleChildren(nsIAccessible *aAccessible,
|
||||
nsAString *aString)
|
||||
{
|
||||
nsCOMPtr<nsIAccessible> 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<nsIAccessNode> accessNode(do_QueryInterface(aAccessible));
|
||||
|
||||
nsCOMPtr<nsIDOMNode> DOMNode;
|
||||
accessNode->GetDOMNode(getter_AddRefs(DOMNode));
|
||||
nsCOMPtr<nsIContent> 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<nsIAccessible> nextSibling;
|
||||
aAccessible->GetNextSibling(getter_AddRefs(nextSibling));
|
||||
if (nextSibling) {
|
||||
nsCOMPtr<nsIAccessible> parent;
|
||||
aAccessible->GetParent(getter_AddRefs(parent));
|
||||
if (parent) {
|
||||
nsCOMPtr<nsIAccessible> 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<nsIContent> 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<nsIDOMXULLabeledControlElement> 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
|
||||
};
|
168
accessible/src/base/nsTextEquivUtils.h
Normal file
168
accessible/src/base/nsTextEquivUtils.h
Normal file
@ -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 <surkov.alexander@gmail.com> (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<nsIAccessible> gInitiatorAcc;
|
||||
};
|
||||
|
||||
#endif
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -967,7 +967,8 @@ NS_IMETHODIMP nsHTMLTableAccessible::GetDescription(nsAString& aDescription)
|
||||
captionAccessNode->GetDOMNode(getter_AddRefs(captionNode));
|
||||
nsCOMPtr<nsIContent> captionContent = do_QueryInterface(captionNode);
|
||||
if (captionContent) {
|
||||
AppendFlatStringFromSubtree(captionContent, &aDescription);
|
||||
nsTextEquivUtils::
|
||||
AppendTextEquivFromContent(this, captionContent, &aDescription);
|
||||
}
|
||||
}
|
||||
#ifdef SHOW_LAYOUT_HEURISTIC
|
||||
|
@ -169,22 +169,9 @@ nsTextAccessible(aDomNode, aShell)
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHTMLLabelAccessible::GetNameInternal(nsAString& aReturn)
|
||||
{
|
||||
nsresult rv = NS_ERROR_FAILURE;
|
||||
nsCOMPtr<nsIContent> 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)
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -165,12 +165,15 @@ nsXULLinkAccessible::GetValue(nsAString& aValue)
|
||||
nsresult
|
||||
nsXULLinkAccessible::GetNameInternal(nsAString& aName)
|
||||
{
|
||||
if (IsDefunct())
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
nsCOMPtr<nsIContent> 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
|
||||
|
@ -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 \
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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;
|
||||
|
@ -119,6 +119,7 @@
|
||||
<rule fromsubtree="true"/>
|
||||
<rule attr="title" type="string"/>
|
||||
</ruleset>
|
||||
|
||||
</ruledfn>
|
||||
|
||||
<rulesample>
|
||||
@ -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">
|
||||
<html:p>This is a paragraph</html:p>
|
||||
<html:a href="#">This is a link</html:a>
|
||||
<html:ul>
|
||||
<html:li>This is a list</html:li>
|
||||
<html:li>Listitem1</html:li>
|
||||
<html:li>Listitem2</html:li>
|
||||
</html:ul>
|
||||
</html:td>
|
||||
</html:tr>
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,7 +1,7 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>nsIAccessible::name calculation for HTML buttons</title>
|
||||
<title>nsIAccessible::name calculation for elements</title>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
|
||||
<script type="application/javascript"
|
||||
@ -10,19 +10,16 @@
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/a11y/accessible/common.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/a11y/accessible/events.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/a11y/accessible/nsIAccessible_name.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
function doTest()
|
||||
{
|
||||
testNames();
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
// gA11yEventDumpID = "eventdump";
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(doTest);
|
||||
addA11yLoadEvent(testNames);
|
||||
</script>
|
||||
|
||||
</head>
|
||||
@ -31,7 +28,7 @@
|
||||
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=459635"
|
||||
title="nsIAccessible::name calculation for HTML buttons">
|
||||
title="nsIAccessible::name calculation for elements">
|
||||
Mozilla Bug 459635
|
||||
</a>
|
||||
<p id="display"></p>
|
||||
@ -39,6 +36,6 @@
|
||||
<pre id="test">
|
||||
</pre>
|
||||
|
||||
|
||||
<div id="eventdump"></div>
|
||||
</body>
|
||||
</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</button>
|
||||
<br/>
|
||||
|
||||
<!-- name from named accessible -->
|
||||
<input id="labelledby_namedacc" type="checkbox"
|
||||
aria-label="Data" />
|
||||
<input id="input_labelledby_namedacc"
|
||||
aria-labelledby="labelledby_namedacc" />
|
||||
|
||||
<!-- the name from subtree, mixed content -->
|
||||
<span id="labelledby_mixed">no<span>more text</span></span>
|
||||
<button id="btn_labelledby_mixed"
|
||||
@ -286,16 +307,39 @@
|
||||
</label>
|
||||
</form>
|
||||
|
||||
<!-- A label with a nested control in the middle -->
|
||||
<!-- a label with a nested control in the start, middle and end -->
|
||||
<form>
|
||||
<label id="comboinstart"><select id="combo3">
|
||||
<option>One</option>
|
||||
<option>Two</option>
|
||||
</select>
|
||||
day(s).
|
||||
</label>
|
||||
|
||||
<label id="comboinmiddle">
|
||||
Subscribe to
|
||||
<select id="combo3" name="occupation">
|
||||
<select id="combo4" name="occupation">
|
||||
<option>ATOM</option>
|
||||
<option>RSS</option>
|
||||
</select>
|
||||
feed.
|
||||
</label>
|
||||
|
||||
<label id="comboinmiddle2" for="checkbox">Play the
|
||||
<select id="combo5">
|
||||
<option>Haliluya</option>
|
||||
<option>Hurra</option>
|
||||
</select>
|
||||
sound when new mail arrives
|
||||
</label>
|
||||
<input id="checkbox" type="checkbox" />
|
||||
|
||||
<label id="comboinend">
|
||||
This day was
|
||||
<select id="combo6" name="occupation">
|
||||
<option>sunny</option>
|
||||
<option>rainy</option>
|
||||
</select></label>
|
||||
</form>
|
||||
|
||||
</body>
|
||||
|
@ -40,6 +40,10 @@
|
||||
// of elements. Gets the name from text nodes of those elements.
|
||||
testName("btn_labelledby_texts", "text1 text2");
|
||||
|
||||
// Trick cases. Self and recursive referencing.
|
||||
testName("rememberHistoryDays", "Remember 3 days");
|
||||
testName("historyDays", "Remember 3 days");
|
||||
testName("rememberAfter", null); // XUL labels doesn't allow name from subtree
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Name from subtree (single relation labelled_by).
|
||||
@ -101,7 +105,7 @@
|
||||
// Get the name from anonymous label element for anonymous textbox
|
||||
// (@anonid is used).
|
||||
var ID = "box_label_anon1";
|
||||
var box1Acc = testName(ID, "");
|
||||
var box1Acc = testName(ID, null);
|
||||
if (box1Acc) {
|
||||
var textboxAcc = box1Acc.firstChild;
|
||||
is(textboxAcc.name, "Label",
|
||||
@ -111,7 +115,7 @@
|
||||
// Get the name from anonymous label element for anonymous textbox
|
||||
// (@anonid is used). Nested bindings.
|
||||
ID = "box_label_anon2";
|
||||
var box2Acc = testName(ID, "");
|
||||
var box2Acc = testName(ID, null);
|
||||
if (box2Acc) {
|
||||
var textboxAcc = box2Acc.firstChild;
|
||||
is(textboxAcc.name, "Label",
|
||||
@ -195,6 +199,14 @@
|
||||
<button id="btn_labelledby_texts"
|
||||
aria-labelledby="labelledby_text1 labelledby_text2"/>
|
||||
|
||||
<!-- trick aria-labelledby -->
|
||||
<checkbox id="rememberHistoryDays"
|
||||
label="Remember "
|
||||
aria-labelledby="rememberHistoryDays historyDays rememberAfter"/>
|
||||
<textbox id="historyDays" type="number" size="3" value="3"
|
||||
aria-labelledby="rememberHistoryDays historyDays rememberAfter"/>
|
||||
<label id="rememberAfter">days</label>
|
||||
|
||||
<!-- the name from subtree, mixed content -->
|
||||
<description id="labelledby_mixed">
|
||||
no<description>more text</description>
|
||||
|
@ -47,8 +47,8 @@
|
||||
names = [
|
||||
"Search in: Newsticker", // label
|
||||
"Search in:", // text leaf
|
||||
"Search in: Newsticker", // combobox
|
||||
"Search in:", // combobox_list
|
||||
"Search in:", // combobox
|
||||
"Search in: Newsticker", // combobox_list
|
||||
"Newsticker", // option 1
|
||||
"Entire site" // Option 2
|
||||
];
|
||||
|
Loading…
Reference in New Issue
Block a user