mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-21 09:49:14 +00:00
Bug 378540. GetTextAtOffset() broken for word boundaries. r=surkov, a=dsicore
This commit is contained in:
parent
bcd455df72
commit
a4c907ce3a
@ -66,12 +66,9 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
|
||||
{"application", nsIAccessibleRole::ROLE_APPLICATION, eNameLabelOrTitle, eNoValue, kNoReqStates, kEndEntry},
|
||||
{"button", nsIAccessibleRole::ROLE_PUSHBUTTON, eNameOkFromChildren, eNoValue, kNoReqStates,
|
||||
{"disabled", kBoolState, nsIAccessibleStates::STATE_UNAVAILABLE},
|
||||
{"pressed", kBoolState, nsIAccessibleStates::STATE_PRESSED}, kEndEntry},
|
||||
{"pressed", kBoolState, nsIAccessibleStates::STATE_PRESSED},
|
||||
{"pressed", "mixed", nsIAccessibleStates::STATE_MIXED}, kEndEntry},
|
||||
{"checkbox", nsIAccessibleRole::ROLE_CHECKBUTTON, eNameOkFromChildren, eNoValue, nsIAccessibleStates::STATE_CHECKABLE,
|
||||
{"disabled", kBoolState, nsIAccessibleStates::STATE_UNAVAILABLE},
|
||||
{"checked", kBoolState, nsIAccessibleStates::STATE_CHECKED},
|
||||
{"readonly", kBoolState, nsIAccessibleStates::STATE_READONLY}, kEndEntry},
|
||||
{"checkboxtristate", nsIAccessibleRole::ROLE_CHECKBUTTON, eNameOkFromChildren, eNoValue, nsIAccessibleStates::STATE_CHECKABLE,
|
||||
{"disabled", kBoolState, nsIAccessibleStates::STATE_UNAVAILABLE},
|
||||
{"checked", kBoolState, nsIAccessibleStates::STATE_CHECKED},
|
||||
{"checked", "mixed", nsIAccessibleStates::STATE_MIXED},
|
||||
@ -116,6 +113,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
|
||||
{"selected", kBoolState, nsIAccessibleStates::STATE_SELECTED | nsIAccessibleStates::STATE_SELECTABLE},
|
||||
{"selected", "false", nsIAccessibleStates::STATE_SELECTABLE},
|
||||
{"checked", kBoolState, nsIAccessibleStates::STATE_CHECKED | nsIAccessibleStates::STATE_CHECKABLE},
|
||||
{"checked", "mixed", nsIAccessibleStates::STATE_MIXED | nsIAccessibleStates::STATE_CHECKABLE},
|
||||
{"checked", "false", nsIAccessibleStates::STATE_CHECKABLE}, kEndEntry},
|
||||
{"menu", nsIAccessibleRole::ROLE_MENUPOPUP, eNameLabelOrTitle, eNoValue, kNoReqStates,
|
||||
{"disabled", kBoolState, nsIAccessibleStates::STATE_UNAVAILABLE}, kEndEntry},
|
||||
@ -124,11 +122,12 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
|
||||
{"menuitem", nsIAccessibleRole::ROLE_MENUITEM, eNameOkFromChildren, eNoValue, kNoReqStates,
|
||||
{"disabled", kBoolState, nsIAccessibleStates::STATE_UNAVAILABLE},
|
||||
{"checked", kBoolState, nsIAccessibleStates::STATE_CHECKED | nsIAccessibleStates::STATE_CHECKABLE},
|
||||
{"checked", "mixed", nsIAccessibleStates::STATE_MIXED},
|
||||
{"checked", "mixed", nsIAccessibleStates::STATE_MIXED | nsIAccessibleStates::STATE_CHECKABLE},
|
||||
{"checked", "false", nsIAccessibleStates::STATE_CHECKABLE}, kEndEntry},
|
||||
{"menuitemcheckbox", nsIAccessibleRole::ROLE_MENUITEM, eNameOkFromChildren, eNoValue, nsIAccessibleStates::STATE_CHECKABLE,
|
||||
{"disabled", kBoolState, nsIAccessibleStates::STATE_UNAVAILABLE},
|
||||
{"checked", kBoolState, nsIAccessibleStates::STATE_CHECKED }, kEndEntry},
|
||||
{"checked", kBoolState, nsIAccessibleStates::STATE_CHECKED },
|
||||
{"checked", "mixed", nsIAccessibleStates::STATE_MIXED}, kEndEntry},
|
||||
{"menuitemradio", nsIAccessibleRole::ROLE_MENUITEM, eNameOkFromChildren, eNoValue, nsIAccessibleStates::STATE_CHECKABLE,
|
||||
{"disabled", kBoolState, nsIAccessibleStates::STATE_UNAVAILABLE},
|
||||
{"checked", kBoolState, nsIAccessibleStates::STATE_CHECKED }, kEndEntry},
|
||||
@ -137,6 +136,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
|
||||
{"selected", kBoolState, nsIAccessibleStates::STATE_SELECTED | nsIAccessibleStates::STATE_SELECTABLE},
|
||||
{"selected", "false", nsIAccessibleStates::STATE_SELECTABLE},
|
||||
{"checked", kBoolState, nsIAccessibleStates::STATE_CHECKED | nsIAccessibleStates::STATE_CHECKABLE},
|
||||
{"checked", "mixed", nsIAccessibleStates::STATE_MIXED | nsIAccessibleStates::STATE_CHECKABLE},
|
||||
{"checked", "false", nsIAccessibleStates::STATE_CHECKABLE}, kEndEntry},
|
||||
{"progressbar", nsIAccessibleRole::ROLE_PROGRESSBAR, eNameLabelOrTitle, eHasValueMinMax, nsIAccessibleStates::STATE_READONLY,
|
||||
{"disabled", kBoolState, nsIAccessibleStates::STATE_UNAVAILABLE}, kEndEntry},
|
||||
@ -193,7 +193,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
|
||||
{"expanded", kBoolState, nsIAccessibleStates::STATE_EXPANDED},
|
||||
{"expanded", "false", nsIAccessibleStates::STATE_COLLAPSED},
|
||||
{"checked", kBoolState, nsIAccessibleStates::STATE_CHECKED | nsIAccessibleStates::STATE_CHECKABLE},
|
||||
{"checked", "mixed", nsIAccessibleStates::STATE_MIXED},
|
||||
{"checked", "mixed", nsIAccessibleStates::STATE_MIXED | nsIAccessibleStates::STATE_CHECKABLE},
|
||||
{"checked", "false", nsIAccessibleStates::STATE_CHECKABLE},},
|
||||
{nsnull, nsIAccessibleRole::ROLE_NOTHING, eNameLabelOrTitle, eNoValue, kNoReqStates, kEndEntry} // Last item
|
||||
};
|
||||
|
@ -164,7 +164,6 @@ ACCESSIBILITY_ATOM(editable, "editable")
|
||||
ACCESSIBILITY_ATOM(_for, "for")
|
||||
ACCESSIBILITY_ATOM(hidden, "hidden") // XUL tree columns
|
||||
ACCESSIBILITY_ATOM(href, "href")
|
||||
ACCESSIBILITY_ATOM(id, "id")
|
||||
ACCESSIBILITY_ATOM(increment, "increment") // XUL
|
||||
ACCESSIBILITY_ATOM(lang, "lang")
|
||||
ACCESSIBILITY_ATOM(maxpos, "maxpos") // XUL
|
||||
|
@ -1423,7 +1423,7 @@ NS_IMETHODIMP nsAccessibilityService::GetAccessible(nsIDOMNode *aNode,
|
||||
// We don't do this for <body>, <html>, <window>, <dialog> etc. which
|
||||
// correspond to the doc accessible and will be created in any case
|
||||
if (!newAcc && content->Tag() != nsAccessibilityAtoms::body && content->GetParent() &&
|
||||
(content->IsFocusable() ||
|
||||
(content->IsFocusable() || content->GetID() ||
|
||||
(isHTML && nsAccUtils::HasListener(content, NS_LITERAL_STRING("click"))) ||
|
||||
content->HasAttr(kNameSpaceID_WAIProperties, nsAccessibilityAtoms::describedby) ||
|
||||
content->HasAttr(kNameSpaceID_WAIProperties, nsAccessibilityAtoms::labelledby) ||
|
||||
|
@ -402,3 +402,9 @@ nsAccUtils::GetDocShellTreeItemFor(nsIDOMNode *aNode)
|
||||
return docShellTreeItem;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsAccUtils::GetID(nsIContent *aContent, nsAString& aID)
|
||||
{
|
||||
nsIAtom *idAttribute = aContent->GetIDAttributeName();
|
||||
return idAttribute ? aContent->GetAttr(kNameSpaceID_None, idAttribute, aID) : PR_FALSE;
|
||||
}
|
||||
|
@ -180,6 +180,14 @@ public:
|
||||
*/
|
||||
static already_AddRefed<nsIDocShellTreeItem>
|
||||
GetDocShellTreeItemFor(nsIDOMNode *aNode);
|
||||
|
||||
/**
|
||||
* Get the ID for an element, in some types of XML this may not be the ID attribute
|
||||
* @param aContent Node to get the ID for
|
||||
* @param aID Where to put ID string
|
||||
* @return PR_TRUE if there is an ID set for this node
|
||||
*/
|
||||
static PRBool GetID(nsIContent *aContent, nsAString& aID);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -1630,11 +1630,10 @@ nsIContent* nsAccessible::GetHTMLLabelContent(nsIContent *aForNode)
|
||||
// for="control_id" attribute. To save computing time, only
|
||||
// look for those inside of a form element
|
||||
nsAutoString forId;
|
||||
aForNode->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::id, forId);
|
||||
// Actually we'll be walking down the content this time, with a depth first search
|
||||
if (forId.IsEmpty()) {
|
||||
if (!nsAccUtils::GetID(aForNode, forId)) {
|
||||
break;
|
||||
}
|
||||
// Actually we'll be walking down the content this time, with a depth first search
|
||||
return FindDescendantPointingToID(&forId, walkUpContent,
|
||||
nsAccessibilityAtoms::_for);
|
||||
}
|
||||
@ -1701,10 +1700,8 @@ nsAccessible::FindNeighbourPointingToNode(nsIContent *aForNode,
|
||||
PRUint32 aAncestorLevelsToSearch)
|
||||
{
|
||||
nsCOMPtr<nsIContent> binding;
|
||||
|
||||
nsAutoString controlID;
|
||||
aForNode->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::id, controlID);
|
||||
if (controlID.IsEmpty()) {
|
||||
if (!nsAccUtils::GetID(aForNode, controlID)) {
|
||||
binding = aForNode->GetBindingParent();
|
||||
if (binding == aForNode)
|
||||
return nsnull;
|
||||
@ -2063,12 +2060,10 @@ nsAccessible::GetAttributes(nsIPersistentProperties **aAttributes)
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIContent> content = GetRoleContent(mDOMNode);
|
||||
if (content) {
|
||||
nsAutoString id;
|
||||
nsAutoString id;
|
||||
if (content && nsAccUtils::GetID(content, id)) {
|
||||
nsAutoString oldValueUnused;
|
||||
if (content->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::id, id)) {
|
||||
attributes->SetStringProperty(NS_LITERAL_CSTRING("id"), id, oldValueUnused);
|
||||
}
|
||||
attributes->SetStringProperty(NS_LITERAL_CSTRING("id"), id, oldValueUnused);
|
||||
// XXX In the future we may need to expose the dynamic content role inheritance chain
|
||||
// through this attribute
|
||||
nsAutoString xmlRole;
|
||||
|
@ -77,6 +77,8 @@
|
||||
// nsDocAccessible //
|
||||
//=============================//
|
||||
|
||||
PRUint32 nsDocAccessible::gLastFocusedAccessiblesState = 0;
|
||||
|
||||
//-----------------------------------------------------
|
||||
// construction
|
||||
//-----------------------------------------------------
|
||||
@ -918,6 +920,23 @@ void
|
||||
nsDocAccessible::AttributeChanged(nsIDocument *aDocument, nsIContent* aContent,
|
||||
PRInt32 aNameSpaceID, nsIAtom* aAttribute,
|
||||
PRInt32 aModType, PRUint32 aStateMask)
|
||||
{
|
||||
AttributeChangedImpl(aContent, aNameSpaceID, aAttribute);
|
||||
|
||||
// If it was the focused node, cache the new state
|
||||
nsCOMPtr<nsIDOMNode> targetNode = do_QueryInterface(aContent);
|
||||
if (targetNode == gLastFocusedNode) {
|
||||
nsCOMPtr<nsIAccessible> focusedAccessible;
|
||||
GetAccService()->GetAccessibleFor(targetNode, getter_AddRefs(focusedAccessible));
|
||||
if (focusedAccessible) {
|
||||
gLastFocusedAccessiblesState = State(focusedAccessible);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
nsDocAccessible::AttributeChangedImpl(nsIContent* aContent, PRInt32 aNameSpaceID, nsIAtom* aAttribute)
|
||||
{
|
||||
// Fire accessible event after short timer, because we need to wait for
|
||||
// DOM attribute & resulting layout to actually change. Otherwise,
|
||||
@ -1087,21 +1106,33 @@ nsDocAccessible::ARIAAttributeChanged(nsIContent* aContent, nsIAtom* aAttribute)
|
||||
return;
|
||||
}
|
||||
|
||||
if (aAttribute == nsAccessibilityAtoms::checked) {
|
||||
if (aAttribute == nsAccessibilityAtoms::checked ||
|
||||
aAttribute == nsAccessibilityAtoms::pressed) {
|
||||
const PRUint32 kState = (aAttribute == nsAccessibilityAtoms::checked) ?
|
||||
nsIAccessibleStates::STATE_CHECKED :
|
||||
nsIAccessibleStates::STATE_PRESSED;
|
||||
nsCOMPtr<nsIAccessibleStateChangeEvent> event =
|
||||
new nsAccStateChangeEvent(targetNode,
|
||||
nsIAccessibleStates::STATE_CHECKED,
|
||||
PR_FALSE);
|
||||
FireDelayedAccessibleEvent(event);
|
||||
return;
|
||||
}
|
||||
|
||||
if (aAttribute == nsAccessibilityAtoms::pressed) {
|
||||
nsCOMPtr<nsIAccessibleStateChangeEvent> event =
|
||||
new nsAccStateChangeEvent(targetNode,
|
||||
nsIAccessibleStates::STATE_PRESSED,
|
||||
PR_FALSE);
|
||||
new nsAccStateChangeEvent(targetNode, kState, PR_FALSE);
|
||||
FireDelayedAccessibleEvent(event);
|
||||
if (targetNode == gLastFocusedNode) {
|
||||
// State changes for MIXED state currently only supported for focused item, because
|
||||
// otherwise we would need access to the old attribute value in this listener.
|
||||
// This is because we don't know if the previous value of aaa:checked or aaa:pressed was "mixed"
|
||||
// without caching that info.
|
||||
nsCOMPtr<nsIAccessible> accessible;
|
||||
event->GetAccessible(getter_AddRefs(accessible));
|
||||
if (accessible) {
|
||||
PRBool wasMixed = (gLastFocusedAccessiblesState & nsIAccessibleStates::STATE_MIXED) != 0;
|
||||
PRBool isMixed = (State(accessible) & nsIAccessibleStates::STATE_MIXED) != 0;
|
||||
if (wasMixed != isMixed) {
|
||||
nsCOMPtr<nsIAccessibleStateChangeEvent> event =
|
||||
new nsAccStateChangeEvent(targetNode,
|
||||
nsIAccessibleStates::STATE_MIXED,
|
||||
PR_FALSE, isMixed);
|
||||
FireDelayedAccessibleEvent(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -149,7 +149,16 @@ class nsDocAccessible : public nsHyperTextAccessibleWrap,
|
||||
static void ScrollTimerCallback(nsITimer *aTimer, void *aClosure);
|
||||
|
||||
/**
|
||||
* Fires accessible events when ARIA attribute is chaned.
|
||||
* Fires accessible events when attribute is changed.
|
||||
*
|
||||
* @param aContent - node that attribute is changed for
|
||||
* @param aNameSpaceID - namespace of changed attribute
|
||||
* @param aAttribute - changed attribute
|
||||
*/
|
||||
void AttributeChangedImpl(nsIContent* aContent, PRInt32 aNameSpaceID, nsIAtom* aAttribute);
|
||||
|
||||
/**
|
||||
* Fires accessible events when ARIA attribute is changed.
|
||||
*
|
||||
* @param aContent - node that attribute is changed for
|
||||
* @param aAttribute - changed attribute
|
||||
@ -195,6 +204,7 @@ class nsDocAccessible : public nsHyperTextAccessibleWrap,
|
||||
protected:
|
||||
PRBool mIsAnchor;
|
||||
PRBool mIsAnchorJumped;
|
||||
static PRUint32 gLastFocusedAccessiblesState;
|
||||
|
||||
private:
|
||||
static void DocLoadCallback(nsITimer *aTimer, void *aClosure);
|
||||
|
@ -481,6 +481,7 @@ PRBool nsRootAccessible::FireAccessibleFocusEvent(nsIAccessible *aAccessible,
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
gLastFocusedAccessiblesState = State(finalFocusAccessible);
|
||||
PRUint32 role = Role(finalFocusAccessible);
|
||||
if (role == nsIAccessibleRole::ROLE_MENUITEM) {
|
||||
if (!mCurrentARIAMenubar) { // Entering menus
|
||||
|
@ -866,6 +866,12 @@ nsresult nsHyperTextAccessible::GetTextHelper(EGetTextType aType, nsAccessibleTe
|
||||
amount, eDirNext, needsStart);
|
||||
NS_ENSURE_TRUE(endOffset >= 0, NS_ERROR_FAILURE);
|
||||
if (finalEndOffset == aOffset) {
|
||||
if (aType == eGetAt && amount == eSelectWord) {
|
||||
// Fix word error for the first character in word: PeekOffset() will return the previous word when
|
||||
// aOffset points to the first character of the word, but accessibility APIs want the current word
|
||||
// that the first character is in
|
||||
return GetTextHelper(eGetAfter, aBoundaryType, aOffset, aStartOffset, aEndOffset, aText);
|
||||
}
|
||||
// This happens sometimes when current character at finalStartOffset
|
||||
// is an embedded object character representing another hypertext, that
|
||||
// the AT really needs to dig into separately
|
||||
@ -873,13 +879,6 @@ nsresult nsHyperTextAccessible::GetTextHelper(EGetTextType aType, nsAccessibleTe
|
||||
}
|
||||
}
|
||||
|
||||
// Fix word error for the first character in word: PeekOffset() will return the previous word when
|
||||
// aOffset points to the first character of the word, but accessibility APIs want the current word
|
||||
// that the first character is in
|
||||
if (aType == eGetAt && amount == eSelectWord && aOffset == endOffset) {
|
||||
return GetTextHelper(eGetAfter, aBoundaryType, aOffset, aStartOffset, aEndOffset, aText);
|
||||
}
|
||||
|
||||
*aStartOffset = finalStartOffset;
|
||||
*aEndOffset = finalEndOffset;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user