mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 573469 - part2, cache IDs pointed by relation attributes, r=marcoz, davidb, sr=neil, a=blockingFinal+
This commit is contained in:
parent
c5e8166d7a
commit
7871888c61
@ -37,10 +37,12 @@
|
||||
|
||||
#include "AccIterator.h"
|
||||
|
||||
#include "nsAccessibilityService.h"
|
||||
#include "nsAccessible.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsAccIterator
|
||||
// AccIterator
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AccIterator::AccIterator(nsAccessible *aAccessible,
|
||||
filters::FilterFuncPtr aFilterFunc,
|
||||
@ -93,3 +95,45 @@ AccIterator::IteratorState::IteratorState(nsAccessible *aParent,
|
||||
mParent(aParent), mIndex(0), mParentState(mParentState)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// RelatedAccIterator
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
RelatedAccIterator::
|
||||
RelatedAccIterator(nsDocAccessible* aDocument, nsIContent* aDependentContent,
|
||||
nsIAtom* aRelAttr) :
|
||||
mRelAttr(aRelAttr), mProviders(nsnull), mBindingParent(nsnull), mIndex(0)
|
||||
{
|
||||
mBindingParent = aDependentContent->GetBindingParent();
|
||||
nsIAtom* IDAttr = mBindingParent ?
|
||||
nsAccessibilityAtoms::anonid : aDependentContent->GetIDAttributeName();
|
||||
|
||||
nsAutoString id;
|
||||
if (aDependentContent->GetAttr(kNameSpaceID_None, IDAttr, id))
|
||||
mProviders = aDocument->mDependentIDsHash.Get(id);
|
||||
}
|
||||
|
||||
nsAccessible*
|
||||
RelatedAccIterator::Next()
|
||||
{
|
||||
if (!mProviders)
|
||||
return nsnull;
|
||||
|
||||
while (mIndex < mProviders->Length()) {
|
||||
nsDocAccessible::AttrRelProvider* provider = (*mProviders)[mIndex++];
|
||||
|
||||
// Return related accessible for the given attribute and if the provider
|
||||
// content is in the same binding in the case of XBL usage.
|
||||
if (provider->mRelAttr == mRelAttr &&
|
||||
(!mBindingParent ||
|
||||
mBindingParent == provider->mContent->GetBindingParent())) {
|
||||
nsAccessible* related = GetAccService()->GetAccessible(provider->mContent);
|
||||
if (related)
|
||||
return related;
|
||||
}
|
||||
}
|
||||
|
||||
return nsnull;
|
||||
}
|
||||
|
@ -40,6 +40,7 @@
|
||||
|
||||
#include "filters.h"
|
||||
#include "nscore.h"
|
||||
#include "nsDocAccessible.h"
|
||||
|
||||
/**
|
||||
* Allows to iterate through accessible children or subtree complying with
|
||||
@ -93,4 +94,41 @@ private:
|
||||
IteratorState *mState;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Allows to traverse through related accessibles that are pointing to the given
|
||||
* dependent accessible by relation attribute.
|
||||
*/
|
||||
class RelatedAccIterator
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param aDocument [in] the document accessible the related
|
||||
* & accessibles belong to.
|
||||
* @param aDependentContent [in] the content of dependent accessible that
|
||||
* relations were requested for
|
||||
* @param aRelAttr [in] relation attribute that relations are
|
||||
* pointed by
|
||||
*/
|
||||
RelatedAccIterator(nsDocAccessible* aDocument, nsIContent* aDependentContent,
|
||||
nsIAtom* aRelAttr);
|
||||
|
||||
/**
|
||||
* Return next related accessible for the given dependent accessible.
|
||||
*/
|
||||
nsAccessible* Next();
|
||||
|
||||
private:
|
||||
RelatedAccIterator();
|
||||
RelatedAccIterator(const RelatedAccIterator&);
|
||||
RelatedAccIterator& operator = (const RelatedAccIterator&);
|
||||
|
||||
nsIAtom* mRelAttr;
|
||||
nsDocAccessible::AttrRelProviderArray* mProviders;
|
||||
nsIContent* mBindingParent;
|
||||
PRUint32 mIndex;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -2037,25 +2037,28 @@ nsAccessible::GetRelationByType(PRUint32 aRelationType,
|
||||
|
||||
// Relationships are defined on the same content node that the role would be
|
||||
// defined on.
|
||||
nsresult rv;
|
||||
nsresult rv = NS_OK_NO_RELATION_TARGET;
|
||||
switch (aRelationType)
|
||||
{
|
||||
case nsIAccessibleRelation::RELATION_LABEL_FOR:
|
||||
{
|
||||
RelatedAccIterator iter(GetDocAccessible(), mContent,
|
||||
nsAccessibilityAtoms::aria_labelledby);
|
||||
|
||||
nsAccessible* related = nsnull;
|
||||
while ((related = iter.Next())) {
|
||||
rv = nsRelUtils::AddTarget(aRelationType, aRelation, related);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
if (mContent->Tag() == nsAccessibilityAtoms::label) {
|
||||
nsIAtom *IDAttr = mContent->IsHTML() ?
|
||||
nsAccessibilityAtoms::_for : nsAccessibilityAtoms::control;
|
||||
rv = nsRelUtils::
|
||||
AddTargetFromIDRefAttr(aRelationType, aRelation, mContent, IDAttr);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (rv != NS_OK_NO_RELATION_TARGET)
|
||||
return NS_OK; // XXX bug 381599, avoid performance problems
|
||||
}
|
||||
|
||||
return nsRelUtils::
|
||||
AddTargetFromNeighbour(aRelationType, aRelation, mContent,
|
||||
nsAccessibilityAtoms::aria_labelledby);
|
||||
return rv;
|
||||
}
|
||||
|
||||
case nsIAccessibleRelation::RELATION_LABELLED_BY:
|
||||
@ -2091,13 +2094,14 @@ nsAccessible::GetRelationByType(PRUint32 aRelationType,
|
||||
|
||||
case nsIAccessibleRelation::RELATION_DESCRIPTION_FOR:
|
||||
{
|
||||
rv = nsRelUtils::
|
||||
AddTargetFromNeighbour(aRelationType, aRelation, mContent,
|
||||
nsAccessibilityAtoms::aria_describedby);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
RelatedAccIterator iter(GetDocAccessible(), mContent,
|
||||
nsAccessibilityAtoms::aria_describedby);
|
||||
|
||||
if (rv != NS_OK_NO_RELATION_TARGET)
|
||||
return NS_OK; // XXX bug 381599, avoid performance problems
|
||||
nsAccessible* related = nsnull;
|
||||
while ((related = iter.Next())) {
|
||||
rv = nsRelUtils::AddTarget(aRelationType, aRelation, related);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
if (mContent->Tag() == nsAccessibilityAtoms::description &&
|
||||
mContent->IsXUL()) {
|
||||
@ -2109,18 +2113,23 @@ nsAccessible::GetRelationByType(PRUint32 aRelationType,
|
||||
nsAccessibilityAtoms::control);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
return rv;
|
||||
}
|
||||
|
||||
case nsIAccessibleRelation::RELATION_NODE_CHILD_OF:
|
||||
{
|
||||
rv = nsRelUtils::
|
||||
AddTargetFromNeighbour(aRelationType, aRelation, mContent,
|
||||
nsAccessibilityAtoms::aria_owns);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
RelatedAccIterator iter(GetDocAccessible(), mContent,
|
||||
nsAccessibilityAtoms::aria_owns);
|
||||
|
||||
nsAccessible* related = nsnull;
|
||||
while ((related = iter.Next())) {
|
||||
rv = nsRelUtils::AddTarget(aRelationType, aRelation, related);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// Got relation from aria-owns, don't calculate it from native markup.
|
||||
if (rv != NS_OK_NO_RELATION_TARGET)
|
||||
return NS_OK; // XXX bug 381599, avoid performance problems
|
||||
return NS_OK;
|
||||
|
||||
// This is an ARIA tree or treegrid that doesn't use owns, so we need to
|
||||
// get the parent the hard way.
|
||||
@ -2153,14 +2162,20 @@ nsAccessible::GetRelationByType(PRUint32 aRelationType,
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
return rv;
|
||||
}
|
||||
|
||||
case nsIAccessibleRelation::RELATION_CONTROLLED_BY:
|
||||
{
|
||||
return nsRelUtils::
|
||||
AddTargetFromNeighbour(aRelationType, aRelation, mContent,
|
||||
nsAccessibilityAtoms::aria_controls);
|
||||
RelatedAccIterator iter(GetDocAccessible(), mContent,
|
||||
nsAccessibilityAtoms::aria_controls);
|
||||
|
||||
nsAccessible* related = nsnull;
|
||||
while ((related = iter.Next())) {
|
||||
rv = nsRelUtils::AddTarget(aRelationType, aRelation, related);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
case nsIAccessibleRelation::RELATION_CONTROLLER_FOR:
|
||||
@ -2188,9 +2203,15 @@ nsAccessible::GetRelationByType(PRUint32 aRelationType,
|
||||
|
||||
case nsIAccessibleRelation::RELATION_FLOWS_FROM:
|
||||
{
|
||||
return nsRelUtils::
|
||||
AddTargetFromNeighbour(aRelationType, aRelation, mContent,
|
||||
nsAccessibilityAtoms::aria_flowto);
|
||||
RelatedAccIterator iter(GetDocAccessible(), mContent,
|
||||
nsAccessibilityAtoms::aria_flowto);
|
||||
|
||||
nsAccessible* related = nsnull;
|
||||
while ((related = iter.Next())) {
|
||||
rv = nsRelUtils::AddTarget(aRelationType, aRelation, related);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
case nsIAccessibleRelation::RELATION_DEFAULT_BUTTON:
|
||||
|
@ -51,7 +51,6 @@
|
||||
#include "nsStringGlue.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsRefPtrHashtable.h"
|
||||
#include "nsDataHashtable.h"
|
||||
|
||||
class AccGroupInfo;
|
||||
class EmbeddedObjCollector;
|
||||
@ -67,8 +66,6 @@ class nsIView;
|
||||
|
||||
typedef nsRefPtrHashtable<nsVoidPtrHashKey, nsAccessible>
|
||||
nsAccessibleHashtable;
|
||||
typedef nsDataHashtable<nsPtrHashKey<const nsINode>, nsAccessible*>
|
||||
NodeToAccessibleMap;
|
||||
|
||||
// see nsAccessible::GetAttrValue
|
||||
#define NS_OK_NO_ARIA_VALUE \
|
||||
|
@ -85,6 +85,16 @@ namespace dom = mozilla::dom;
|
||||
|
||||
PRUint32 nsDocAccessible::gLastFocusedAccessiblesState = 0;
|
||||
|
||||
static nsIAtom** kRelationAttrs[] =
|
||||
{
|
||||
&nsAccessibilityAtoms::aria_labelledby,
|
||||
&nsAccessibilityAtoms::aria_describedby,
|
||||
&nsAccessibilityAtoms::aria_owns,
|
||||
&nsAccessibilityAtoms::aria_controls,
|
||||
&nsAccessibilityAtoms::aria_flowto
|
||||
};
|
||||
|
||||
static const PRUint32 kRelationAttrsLen = NS_ARRAY_LENGTH(kRelationAttrs);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor/desctructor
|
||||
@ -95,6 +105,7 @@ nsDocAccessible::
|
||||
nsHyperTextAccessibleWrap(aRootContent, aShell),
|
||||
mDocument(aDocument), mScrollPositionChangedTicks(0), mIsLoaded(PR_FALSE)
|
||||
{
|
||||
mDependentIDsHash.Init();
|
||||
// XXX aaronl should we use an algorithm for the initial cache size?
|
||||
mAccessibleCache.Init(kDefaultCacheSize);
|
||||
mNodeToAccessibleMap.Init(kDefaultCacheSize);
|
||||
@ -134,6 +145,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDocAccessible, nsAccessible)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mEventQueue)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSTARRAY(mChildDocuments)
|
||||
tmp->mDependentIDsHash.Clear();
|
||||
tmp->mNodeToAccessibleMap.Clear();
|
||||
ClearCache(tmp->mAccessibleCache);
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
@ -664,6 +676,7 @@ nsDocAccessible::Shutdown()
|
||||
|
||||
mWeakShell = nsnull; // Avoid reentrancy
|
||||
|
||||
mDependentIDsHash.Clear();
|
||||
mNodeToAccessibleMap.Clear();
|
||||
ClearCache(mAccessibleCache);
|
||||
|
||||
@ -929,10 +942,19 @@ nsDocAccessible::AttributeWillChange(nsIDocument *aDocument,
|
||||
PRInt32 aNameSpaceID,
|
||||
nsIAtom* aAttribute, PRInt32 aModType)
|
||||
{
|
||||
// XXX TODO: bugs 381599 467143 472142 472143
|
||||
// XXX TODO: bugs 381599 (partially fixed by 573469), 467143, 472142, 472143.
|
||||
// Here we will want to cache whatever state we are potentially interested in,
|
||||
// such as the existence of aria-pressed for button (so we know if we need to
|
||||
// newly expose it as a toggle button) etc.
|
||||
|
||||
// Update dependent IDs cache.
|
||||
if (aModType == nsIDOMMutationEvent::MODIFICATION ||
|
||||
aModType == nsIDOMMutationEvent::REMOVAL) {
|
||||
nsAccessible* accessible =
|
||||
GetAccService()->GetAccessibleInWeakShell(aElement, mWeakShell);
|
||||
if (accessible)
|
||||
RemoveDependentIDsFor(accessible, aAttribute);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -943,6 +965,16 @@ nsDocAccessible::AttributeChanged(nsIDocument *aDocument,
|
||||
{
|
||||
AttributeChangedImpl(aElement, aNameSpaceID, aAttribute);
|
||||
|
||||
// Update dependent IDs cache.
|
||||
if (aModType == nsIDOMMutationEvent::MODIFICATION ||
|
||||
aModType == nsIDOMMutationEvent::ADDITION) {
|
||||
nsAccessible* accessible =
|
||||
GetAccService()->GetAccessibleInWeakShell(aElement, mWeakShell);
|
||||
|
||||
if (accessible)
|
||||
AddDependentIDsFor(accessible, aAttribute);
|
||||
}
|
||||
|
||||
// If it was the focused node, cache the new state
|
||||
if (aElement == gLastFocusedNode) {
|
||||
nsAccessible *focusedAccessible = GetAccService()->GetAccessible(aElement);
|
||||
@ -1362,6 +1394,7 @@ nsDocAccessible::BindToDocument(nsAccessible* aAccessible,
|
||||
}
|
||||
|
||||
aAccessible->SetRoleMapEntry(aRoleMapEntry);
|
||||
AddDependentIDsFor(aAccessible);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1373,6 +1406,8 @@ nsDocAccessible::UnbindFromDocument(nsAccessible* aAccessible)
|
||||
mNodeToAccessibleMap.Get(aAccessible->GetNode()) == aAccessible)
|
||||
mNodeToAccessibleMap.Remove(aAccessible->GetNode());
|
||||
|
||||
RemoveDependentIDsFor(aAccessible);
|
||||
|
||||
#ifdef DEBUG
|
||||
NS_ASSERTION(mAccessibleCache.GetWeak(aAccessible->UniqueID()),
|
||||
"Unbinding the unbound accessible!");
|
||||
@ -1557,6 +1592,84 @@ nsDocAccessible::RecreateAccessible(nsINode* aNode)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Protected members
|
||||
|
||||
void
|
||||
nsDocAccessible::AddDependentIDsFor(nsAccessible* aRelProvider,
|
||||
nsIAtom* aRelAttr)
|
||||
{
|
||||
for (PRUint32 idx = 0; idx < kRelationAttrsLen; idx++) {
|
||||
nsIAtom* relAttr = *kRelationAttrs[idx];
|
||||
if (aRelAttr && aRelAttr != relAttr)
|
||||
continue;
|
||||
|
||||
IDRefsIterator iter(aRelProvider->GetContent(), relAttr);
|
||||
while (true) {
|
||||
const nsDependentSubstring id = iter.NextID();
|
||||
if (id.IsEmpty())
|
||||
break;
|
||||
|
||||
AttrRelProviderArray* providers = mDependentIDsHash.Get(id);
|
||||
if (!providers) {
|
||||
providers = new AttrRelProviderArray();
|
||||
if (providers) {
|
||||
if (!mDependentIDsHash.Put(id, providers)) {
|
||||
delete providers;
|
||||
providers = nsnull;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (providers) {
|
||||
AttrRelProvider* provider =
|
||||
new AttrRelProvider(relAttr, aRelProvider->GetContent());
|
||||
if (provider)
|
||||
providers->AppendElement(provider);
|
||||
}
|
||||
}
|
||||
|
||||
// If the relation attribute is given then we don't have anything else to
|
||||
// check.
|
||||
if (aRelAttr)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDocAccessible::RemoveDependentIDsFor(nsAccessible* aRelProvider,
|
||||
nsIAtom* aRelAttr)
|
||||
{
|
||||
for (PRUint32 idx = 0; idx < kRelationAttrsLen; idx++) {
|
||||
nsIAtom* relAttr = *kRelationAttrs[idx];
|
||||
if (aRelAttr && aRelAttr != *kRelationAttrs[idx])
|
||||
continue;
|
||||
|
||||
IDRefsIterator iter(aRelProvider->GetContent(), relAttr);
|
||||
while (true) {
|
||||
const nsDependentSubstring id = iter.NextID();
|
||||
if (id.IsEmpty())
|
||||
break;
|
||||
|
||||
AttrRelProviderArray* providers = mDependentIDsHash.Get(id);
|
||||
if (providers) {
|
||||
for (PRUint32 jdx = 0; jdx < providers->Length(); ) {
|
||||
AttrRelProvider* provider = (*providers)[jdx];
|
||||
if (provider->mRelAttr == relAttr &&
|
||||
provider->mContent == aRelProvider->GetContent())
|
||||
providers->RemoveElement(provider);
|
||||
else
|
||||
jdx++;
|
||||
}
|
||||
if (providers->Length() == 0)
|
||||
mDependentIDsHash.Remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
// If the relation attribute is given then we don't have anything else to
|
||||
// check.
|
||||
if (aRelAttr)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDocAccessible::FireValueChangeForTextFields(nsAccessible *aAccessible)
|
||||
{
|
||||
|
@ -44,6 +44,8 @@
|
||||
#include "nsHyperTextAccessibleWrap.h"
|
||||
#include "nsEventShell.h"
|
||||
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIDocumentObserver.h"
|
||||
#include "nsIEditor.h"
|
||||
@ -267,6 +269,28 @@ protected:
|
||||
mChildDocuments.RemoveElement(aChildDocument);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add dependent IDs pointed by accessible element by relation attribute to
|
||||
* cache. If the relation attribute is missed then all relation attributes
|
||||
* are checked.
|
||||
*
|
||||
* @param aRelProvider [in] accessible that element has relation attribute
|
||||
* @param aRelAttr [in, optional] relation attribute
|
||||
*/
|
||||
void AddDependentIDsFor(nsAccessible* aRelProvider,
|
||||
nsIAtom* aRelAttr = nsnull);
|
||||
|
||||
/**
|
||||
* Remove dependent IDs pointed by accessible element by relation attribute
|
||||
* from cache. If the relation attribute is absent then all relation
|
||||
* attributes are checked.
|
||||
*
|
||||
* @param aRelProvider [in] accessible that element has relation attribute
|
||||
* @param aRelAttr [in, optional] relation attribute
|
||||
*/
|
||||
void RemoveDependentIDsFor(nsAccessible* aRelProvider,
|
||||
nsIAtom* aRelAttr = nsnull);
|
||||
|
||||
static void ScrollTimerCallback(nsITimer *aTimer, void *aClosure);
|
||||
|
||||
/**
|
||||
@ -299,14 +323,6 @@ protected:
|
||||
CharacterDataChangeInfo* aInfo,
|
||||
PRBool aIsInserted);
|
||||
|
||||
/**
|
||||
* Used to define should the event be fired on a delay.
|
||||
*/
|
||||
enum EEventFiringType {
|
||||
eNormalEvent,
|
||||
eDelayedEvent
|
||||
};
|
||||
|
||||
/**
|
||||
* Fire a value change event for the the given accessible if it is a text
|
||||
* field (has a ROLE_ENTRY).
|
||||
@ -347,7 +363,8 @@ protected:
|
||||
* Cache of accessibles within this document accessible.
|
||||
*/
|
||||
nsAccessibleHashtable mAccessibleCache;
|
||||
NodeToAccessibleMap mNodeToAccessibleMap;
|
||||
nsDataHashtable<nsPtrHashKey<const nsINode>, nsAccessible*>
|
||||
mNodeToAccessibleMap;
|
||||
|
||||
nsCOMPtr<nsIDocument> mDocument;
|
||||
nsCOMPtr<nsITimer> mScrollWatchTimer;
|
||||
@ -366,9 +383,35 @@ protected:
|
||||
static nsIAtom *gLastFocusedFrameType;
|
||||
|
||||
nsTArray<nsRefPtr<nsDocAccessible> > mChildDocuments;
|
||||
|
||||
/**
|
||||
* A storage class for pairing content with one of its relation attributes.
|
||||
*/
|
||||
class AttrRelProvider
|
||||
{
|
||||
public:
|
||||
AttrRelProvider(nsIAtom* aRelAttr, nsIContent* aContent) :
|
||||
mRelAttr(aRelAttr), mContent(aContent) { }
|
||||
|
||||
nsIAtom* mRelAttr;
|
||||
nsIContent* mContent;
|
||||
|
||||
private:
|
||||
AttrRelProvider();
|
||||
AttrRelProvider(const AttrRelProvider&);
|
||||
AttrRelProvider& operator =(const AttrRelProvider&);
|
||||
};
|
||||
|
||||
/**
|
||||
* The cache of IDs pointed by relation attributes.
|
||||
*/
|
||||
typedef nsTArray<nsAutoPtr<AttrRelProvider> > AttrRelProviderArray;
|
||||
nsClassHashtable<nsStringHashKey, AttrRelProviderArray> mDependentIDsHash;
|
||||
|
||||
friend class RelatedAccIterator;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsDocAccessible,
|
||||
NS_DOCACCESSIBLE_IMPL_CID)
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
@ -50,6 +50,7 @@ _TEST_FILES =\
|
||||
test_general.xul \
|
||||
test_tabbrowser.xul \
|
||||
test_tree.xul \
|
||||
test_update.html \
|
||||
$(NULL)
|
||||
|
||||
libs:: $(_TEST_FILES)
|
||||
|
@ -65,6 +65,9 @@
|
||||
testRelation(iframeDocAcc, RELATION_NODE_CHILD_OF, iframeAcc);
|
||||
|
||||
// aria-controls
|
||||
getAccessible("tab");
|
||||
todo(false,
|
||||
"Getting an accessible tab, otherwise relations for tabpanel aren't cached. Bug 606924 will fix that.");
|
||||
testRelation("tabpanel", RELATION_CONTROLLED_BY, "tab");
|
||||
testRelation("tab", RELATION_CONTROLLER_FOR, "tabpanel");
|
||||
|
||||
|
@ -66,6 +66,9 @@
|
||||
testRelation(iframeDocAcc, RELATION_NODE_CHILD_OF, iframeAcc);
|
||||
|
||||
// aria-controls
|
||||
getAccessible("tab");
|
||||
todo(false,
|
||||
"Getting an accessible tab, otherwise relations for tabpanel aren't cached. Bug 606924 will fix that.");
|
||||
testRelation("tabpanel", RELATION_CONTROLLED_BY, "tab");
|
||||
testRelation("tab", RELATION_CONTROLLER_FOR, "tabpanel");
|
||||
|
||||
|
162
accessible/tests/mochitest/relations/test_update.html
Normal file
162
accessible/tests/mochitest/relations/test_update.html
Normal file
@ -0,0 +1,162 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Test updating of accessible relations</title>
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/MochiKit/packed.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="../common.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../relations.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../role.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../events.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
function testRelated(aRelAttr, aHostRelation, aDependentRelation)
|
||||
{
|
||||
// no attribute
|
||||
testRelation("dependent1", aDependentRelation, null);
|
||||
testRelation("dependent2", aDependentRelation, null);
|
||||
if (aHostRelation)
|
||||
testRelation("host", aHostRelation, null);
|
||||
|
||||
// set attribute
|
||||
getNode("host").setAttribute(aRelAttr, "dependent1");
|
||||
testRelation("dependent1", aDependentRelation, "host");
|
||||
testRelation("dependent2", aDependentRelation, null);
|
||||
if (aHostRelation)
|
||||
testRelation("host", aHostRelation, "dependent1");
|
||||
|
||||
// change attribute
|
||||
getNode("host").setAttribute(aRelAttr, "dependent2");
|
||||
testRelation("dependent1", aDependentRelation, null);
|
||||
testRelation("dependent2", aDependentRelation, "host");
|
||||
if (aHostRelation)
|
||||
testRelation("host", aHostRelation, "dependent2");
|
||||
|
||||
// remove attribute
|
||||
getNode("host").removeAttribute(aRelAttr);
|
||||
testRelation("dependent1", aDependentRelation, null);
|
||||
testRelation("dependent2", aDependentRelation, null);
|
||||
if (aHostRelation)
|
||||
testRelation("host", aHostRelation, null);
|
||||
}
|
||||
|
||||
function insertRelated(aHostRelAttr, aDependentID, aInsertHostFirst,
|
||||
aHostRelation, aDependentRelation)
|
||||
{
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_REORDER, document)
|
||||
];
|
||||
|
||||
this.invoke = function insertRelated_invoke()
|
||||
{
|
||||
this.hostNode = document.createElement("div");
|
||||
this.hostNode.setAttribute(aHostRelAttr, aDependentID);
|
||||
|
||||
this.dependentNode = document.createElement("div");
|
||||
this.dependentNode.setAttribute("id", aDependentID);
|
||||
|
||||
if (aInsertHostFirst) {
|
||||
document.body.appendChild(this.hostNode);
|
||||
document.body.appendChild(this.dependentNode);
|
||||
} else {
|
||||
document.body.appendChild(this.dependentNode);
|
||||
document.body.appendChild(this.hostNode);
|
||||
}
|
||||
}
|
||||
|
||||
this.finalCheck = function insertRelated_finalCheck()
|
||||
{
|
||||
testRelation(this.dependentNode, aDependentRelation, this.hostNode);
|
||||
if (aHostRelation)
|
||||
testRelation(this.hostNode, aHostRelation, this.dependentNode);
|
||||
}
|
||||
|
||||
this.getID = function insertRelated_getID()
|
||||
{
|
||||
return "Insert " + aHostRelAttr + "='" + aDependentID + "' node" +
|
||||
(aInsertHostFirst ? " before" : "after") + " dependent node";
|
||||
}
|
||||
}
|
||||
|
||||
var gQueue = null;
|
||||
|
||||
function doTest()
|
||||
{
|
||||
// Relation updates on ARIA attribute changes.
|
||||
testRelated("aria-labelledby", RELATION_LABELLED_BY, RELATION_LABEL_FOR);
|
||||
testRelated("aria-describedby",
|
||||
RELATION_DESCRIBED_BY, RELATION_DESCRIPTION_FOR);
|
||||
testRelated("aria-owns", null, RELATION_NODE_CHILD_OF);
|
||||
testRelated("aria-controls",
|
||||
RELATION_CONTROLLER_FOR, RELATION_CONTROLLED_BY);
|
||||
testRelated("aria-flowto", RELATION_FLOWS_TO, RELATION_FLOWS_FROM);
|
||||
|
||||
// Insert related accessibles into tree.
|
||||
gQueue = new eventQueue();
|
||||
gQueue.push(new insertRelated("aria-labelledby", "dependent3", true,
|
||||
RELATION_LABELLED_BY, RELATION_LABEL_FOR));
|
||||
gQueue.push(new insertRelated("aria-labelledby", "dependent4", false,
|
||||
RELATION_LABELLED_BY, RELATION_LABEL_FOR));
|
||||
|
||||
gQueue.push(new insertRelated("aria-describedby", "dependent5", true,
|
||||
RELATION_DESCRIBED_BY,
|
||||
RELATION_DESCRIPTION_FOR));
|
||||
gQueue.push(new insertRelated("aria-describedby", "dependent6", false,
|
||||
RELATION_DESCRIBED_BY,
|
||||
RELATION_DESCRIPTION_FOR));
|
||||
|
||||
gQueue.push(new insertRelated("aria-owns", "dependent7", true,
|
||||
null, RELATION_NODE_CHILD_OF));
|
||||
gQueue.push(new insertRelated("aria-owns", "dependent8", false,
|
||||
null, RELATION_NODE_CHILD_OF));
|
||||
|
||||
gQueue.push(new insertRelated("aria-controls", "dependent9", true,
|
||||
RELATION_CONTROLLER_FOR,
|
||||
RELATION_CONTROLLED_BY));
|
||||
gQueue.push(new insertRelated("aria-controls", "dependent10", false,
|
||||
RELATION_CONTROLLER_FOR,
|
||||
RELATION_CONTROLLED_BY));
|
||||
|
||||
gQueue.push(new insertRelated("aria-flowto", "dependent11", true,
|
||||
RELATION_FLOWS_TO, RELATION_FLOWS_FROM));
|
||||
gQueue.push(new insertRelated("aria-flowto", "dependent12", false,
|
||||
RELATION_FLOWS_TO, RELATION_FLOWS_FROM));
|
||||
|
||||
gQueue.invoke(); // will call SimpleTest.finish()
|
||||
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addA11yLoadEvent(doTest);
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=573469"
|
||||
title="Cache relations defined by ARIA attributes">
|
||||
Mozilla Bug 573469
|
||||
</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
|
||||
<div id="dependent1">label</div>
|
||||
<div id="dependent2">label2</div>
|
||||
<div role="checkbox" id="host"></div>
|
||||
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user