Bug 1219299 - rework aria-owns implementation, r=yzen
This commit is contained in:
@@ -324,8 +324,14 @@ IDRefsIterator::GetElem(const nsDependentSubstring& aID)
|
|||||||
Accessible*
|
Accessible*
|
||||||
IDRefsIterator::Next()
|
IDRefsIterator::Next()
|
||||||
{
|
{
|
||||||
nsIContent* nextElm = NextElem();
|
nsIContent* nextEl = nullptr;
|
||||||
return nextElm ? mDoc->GetAccessible(nextElm) : nullptr;
|
while ((nextEl = NextElem())) {
|
||||||
|
Accessible* acc = mDoc->GetAccessible(nextEl);
|
||||||
|
if (acc) {
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -295,12 +295,16 @@ EventQueue::CoalesceReorderEvents(AccEvent* aTailEvent)
|
|||||||
AccReorderEvent* thisReorder = downcast_accEvent(thisEvent);
|
AccReorderEvent* thisReorder = downcast_accEvent(thisEvent);
|
||||||
AccReorderEvent* tailReorder = downcast_accEvent(aTailEvent);
|
AccReorderEvent* tailReorder = downcast_accEvent(aTailEvent);
|
||||||
uint32_t eventType = thisReorder->IsShowHideEventTarget(tailParent);
|
uint32_t eventType = thisReorder->IsShowHideEventTarget(tailParent);
|
||||||
if (eventType == nsIAccessibleEvent::EVENT_SHOW)
|
if (eventType == nsIAccessibleEvent::EVENT_SHOW) {
|
||||||
tailReorder->DoNotEmitAll();
|
tailReorder->DoNotEmitAll();
|
||||||
else if (eventType == nsIAccessibleEvent::EVENT_HIDE)
|
}
|
||||||
|
else if (eventType == nsIAccessibleEvent::EVENT_HIDE) {
|
||||||
NS_ERROR("Accessible tree was modified after it was removed! Huh?");
|
NS_ERROR("Accessible tree was modified after it was removed! Huh?");
|
||||||
else
|
}
|
||||||
|
else {
|
||||||
aTailEvent->mEventRule = AccEvent::eDoNotEmit;
|
aTailEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||||
|
mEvents[index].swap(mEvents[count - 1]);
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(NotificationController)
|
|||||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHangingChildDocuments)
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHangingChildDocuments)
|
||||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContentInsertions)
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContentInsertions)
|
||||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvents)
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvents)
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRelocations)
|
||||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||||
|
|
||||||
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(NotificationController, AddRef)
|
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(NotificationController, AddRef)
|
||||||
@@ -86,6 +87,7 @@ NotificationController::Shutdown()
|
|||||||
mContentInsertions.Clear();
|
mContentInsertions.Clear();
|
||||||
mNotifications.Clear();
|
mNotifications.Clear();
|
||||||
mEvents.Clear();
|
mEvents.Clear();
|
||||||
|
mRelocations.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -351,6 +353,16 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime)
|
|||||||
// modification are done.
|
// modification are done.
|
||||||
mDocument->ProcessInvalidationList();
|
mDocument->ProcessInvalidationList();
|
||||||
|
|
||||||
|
// We cannot rely on DOM tree to keep aria-owns relations updated. Make
|
||||||
|
// a validation to remove dead links.
|
||||||
|
mDocument->ValidateARIAOwned();
|
||||||
|
|
||||||
|
// Process relocation list.
|
||||||
|
for (uint32_t idx = 0; idx < mRelocations.Length(); idx++) {
|
||||||
|
mDocument->DoARIAOwnsRelocation(mRelocations[idx]);
|
||||||
|
}
|
||||||
|
mRelocations.Clear();
|
||||||
|
|
||||||
// If a generic notification occurs after this point then we may be allowed to
|
// If a generic notification occurs after this point then we may be allowed to
|
||||||
// process it synchronously. However we do not want to reenter if fireing
|
// process it synchronously. However we do not want to reenter if fireing
|
||||||
// events causes script to run.
|
// events causes script to run.
|
||||||
|
|||||||
@@ -133,6 +133,16 @@ public:
|
|||||||
nsIContent* aStartChildNode,
|
nsIContent* aStartChildNode,
|
||||||
nsIContent* aEndChildNode);
|
nsIContent* aEndChildNode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pend an accessible subtree relocation.
|
||||||
|
*/
|
||||||
|
void ScheduleRelocation(Accessible* aOwner)
|
||||||
|
{
|
||||||
|
if (!mRelocations.Contains(aOwner) && mRelocations.AppendElement(aOwner)) {
|
||||||
|
ScheduleProcessing();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start to observe refresh to make notifications and events processing after
|
* Start to observe refresh to make notifications and events processing after
|
||||||
* layout.
|
* layout.
|
||||||
@@ -303,6 +313,11 @@ private:
|
|||||||
* use SwapElements() on it.
|
* use SwapElements() on it.
|
||||||
*/
|
*/
|
||||||
nsTArray<RefPtr<Notification> > mNotifications;
|
nsTArray<RefPtr<Notification> > mNotifications;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds all scheduled relocations.
|
||||||
|
*/
|
||||||
|
nsTArray<RefPtr<Accessible> > mRelocations;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace a11y
|
} // namespace a11y
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ TreeWalker::Next(ChildrenIterator* aIter, Accessible** aAccesible,
|
|||||||
// Ignore the accessible and its subtree if it was repositioned by means of
|
// Ignore the accessible and its subtree if it was repositioned by means of
|
||||||
// aria-owns.
|
// aria-owns.
|
||||||
if (accessible) {
|
if (accessible) {
|
||||||
if (accessible->IsRepositioned()) {
|
if (accessible->IsRelocated()) {
|
||||||
*aSkipSubtree = true;
|
*aSkipSubtree = true;
|
||||||
} else {
|
} else {
|
||||||
*aAccesible = accessible;
|
*aAccesible = accessible;
|
||||||
|
|||||||
@@ -910,13 +910,13 @@ public:
|
|||||||
* Get/set repositioned bit indicating that the accessible was moved in
|
* Get/set repositioned bit indicating that the accessible was moved in
|
||||||
* the accessible tree, i.e. the accessible tree structure differs from DOM.
|
* the accessible tree, i.e. the accessible tree structure differs from DOM.
|
||||||
*/
|
*/
|
||||||
bool IsRepositioned() const { return mStateFlags & eRepositioned; }
|
bool IsRelocated() const { return mStateFlags & eRelocated; }
|
||||||
void SetRepositioned(bool aRepositioned)
|
void SetRelocated(bool aRelocated)
|
||||||
{
|
{
|
||||||
if (aRepositioned)
|
if (aRelocated)
|
||||||
mStateFlags |= eRepositioned;
|
mStateFlags |= eRelocated;
|
||||||
else
|
else
|
||||||
mStateFlags &= ~eRepositioned;
|
mStateFlags &= ~eRelocated;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1009,9 +1009,9 @@ protected:
|
|||||||
eSubtreeMutating = 1 << 6, // subtree is being mutated
|
eSubtreeMutating = 1 << 6, // subtree is being mutated
|
||||||
eIgnoreDOMUIEvent = 1 << 7, // don't process DOM UI events for a11y events
|
eIgnoreDOMUIEvent = 1 << 7, // don't process DOM UI events for a11y events
|
||||||
eSurvivingInUpdate = 1 << 8, // parent drops children to recollect them
|
eSurvivingInUpdate = 1 << 8, // parent drops children to recollect them
|
||||||
eRepositioned = 1 << 9, // accessible was moved in tree
|
eRelocated = 1 << 9, // accessible was moved in tree
|
||||||
|
|
||||||
eLastStateFlag = eRepositioned
|
eLastStateFlag = eRelocated
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -130,9 +130,13 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DocAccessible, Accessible)
|
|||||||
}
|
}
|
||||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAccessibleCache)
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAccessibleCache)
|
||||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnchorJumpElm)
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnchorJumpElm)
|
||||||
for (uint32_t i = 0; i < tmp->mARIAOwnsInvalidationList.Length(); ++i) {
|
for (auto it = tmp->mARIAOwnsHash.ConstIter(); !it.Done(); it.Next()) {
|
||||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mARIAOwnsInvalidationList[i].mOwner)
|
nsTArray<RefPtr<Accessible> >* ar = it.UserData();
|
||||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mARIAOwnsInvalidationList[i].mChild)
|
for (uint32_t i = 0; i < ar->Length(); i++) {
|
||||||
|
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
|
||||||
|
"mARIAOwnsHash entry item");
|
||||||
|
cb.NoteXPCOMChild(ar->ElementAt(i));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||||
|
|
||||||
@@ -144,10 +148,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DocAccessible, Accessible)
|
|||||||
tmp->mNodeToAccessibleMap.Clear();
|
tmp->mNodeToAccessibleMap.Clear();
|
||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mAccessibleCache)
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mAccessibleCache)
|
||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnchorJumpElm)
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnchorJumpElm)
|
||||||
for (uint32_t i = 0; i < tmp->mARIAOwnsInvalidationList.Length(); ++i) {
|
tmp->mARIAOwnsHash.Clear();
|
||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mARIAOwnsInvalidationList[i].mOwner)
|
|
||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mARIAOwnsInvalidationList[i].mChild)
|
|
||||||
}
|
|
||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||||
|
|
||||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DocAccessible)
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DocAccessible)
|
||||||
@@ -731,6 +732,10 @@ DocAccessible::AttributeWillChange(nsIDocument* aDocument,
|
|||||||
if (aModType != nsIDOMMutationEvent::ADDITION)
|
if (aModType != nsIDOMMutationEvent::ADDITION)
|
||||||
RemoveDependentIDsFor(accessible, aAttribute);
|
RemoveDependentIDsFor(accessible, aAttribute);
|
||||||
|
|
||||||
|
if (aAttribute == nsGkAtoms::id) {
|
||||||
|
RelocateARIAOwnedIfNeeded(aElement);
|
||||||
|
}
|
||||||
|
|
||||||
// Store the ARIA attribute old value so that it can be used after
|
// Store the ARIA attribute old value so that it can be used after
|
||||||
// attribute change. Note, we assume there's no nested ARIA attribute
|
// attribute change. Note, we assume there's no nested ARIA attribute
|
||||||
// changes. If this happens then we should end up with keeping a stack of
|
// changes. If this happens then we should end up with keeping a stack of
|
||||||
@@ -906,6 +911,10 @@ DocAccessible::AttributeChangedImpl(Accessible* aAccessible,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (aAttribute == nsGkAtoms::id) {
|
||||||
|
RelocateARIAOwnedIfNeeded(elm);
|
||||||
|
}
|
||||||
|
|
||||||
// ARIA or XUL selection
|
// ARIA or XUL selection
|
||||||
if ((aAccessible->GetContent()->IsXULElement() &&
|
if ((aAccessible->GetContent()->IsXULElement() &&
|
||||||
aAttribute == nsGkAtoms::selected) ||
|
aAttribute == nsGkAtoms::selected) ||
|
||||||
@@ -1040,6 +1049,10 @@ DocAccessible::ARIAAttributeChanged(Accessible* aAccessible, nsIAtom* aAttribute
|
|||||||
FireDelayedEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE, aAccessible);
|
FireDelayedEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE, aAccessible);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (aAttribute == nsGkAtoms::aria_owns) {
|
||||||
|
mNotificationController->ScheduleRelocation(aAccessible);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -1271,6 +1284,15 @@ DocAccessible::BindToDocument(Accessible* aAccessible,
|
|||||||
aAccessible->SetRoleMapEntry(aRoleMapEntry);
|
aAccessible->SetRoleMapEntry(aRoleMapEntry);
|
||||||
|
|
||||||
AddDependentIDsFor(aAccessible);
|
AddDependentIDsFor(aAccessible);
|
||||||
|
|
||||||
|
if (aAccessible->HasOwnContent()) {
|
||||||
|
nsIContent* el = aAccessible->GetContent();
|
||||||
|
if (el->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_owns)) {
|
||||||
|
mNotificationController->ScheduleRelocation(aAccessible);
|
||||||
|
}
|
||||||
|
|
||||||
|
RelocateARIAOwnedIfNeeded(el);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -1365,67 +1387,6 @@ DocAccessible::ProcessInvalidationList()
|
|||||||
}
|
}
|
||||||
|
|
||||||
mInvalidationList.Clear();
|
mInvalidationList.Clear();
|
||||||
|
|
||||||
// Alter the tree according to aria-owns (seize the trees).
|
|
||||||
for (uint32_t idx = 0; idx < mARIAOwnsInvalidationList.Length(); idx++) {
|
|
||||||
Accessible* owner = mARIAOwnsInvalidationList[idx].mOwner;
|
|
||||||
if (!owner->IsInDocument()) { // eventually died before we've got here
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Accessible* child = GetAccessible(mARIAOwnsInvalidationList[idx].mChild);
|
|
||||||
if (!child || !child->IsInDocument()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Accessible* oldParent = child->Parent();
|
|
||||||
if (!oldParent) {
|
|
||||||
NS_ERROR("The accessible is in document but doesn't have a parent");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
int32_t idxInParent = child->IndexInParent();
|
|
||||||
|
|
||||||
// XXX: update context flags
|
|
||||||
{
|
|
||||||
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(oldParent);
|
|
||||||
RefPtr<AccMutationEvent> hideEvent =
|
|
||||||
new AccHideEvent(child, child->GetContent(), false);
|
|
||||||
FireDelayedEvent(hideEvent);
|
|
||||||
reorderEvent->AddSubMutationEvent(hideEvent);
|
|
||||||
|
|
||||||
AutoTreeMutation mut(oldParent);
|
|
||||||
oldParent->RemoveChild(child);
|
|
||||||
|
|
||||||
MaybeNotifyOfValueChange(oldParent);
|
|
||||||
FireDelayedEvent(reorderEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isReinserted = false;
|
|
||||||
{
|
|
||||||
AutoTreeMutation mut(owner);
|
|
||||||
isReinserted = owner->AppendChild(child);
|
|
||||||
}
|
|
||||||
|
|
||||||
Accessible* newParent = owner;
|
|
||||||
if (!isReinserted) {
|
|
||||||
AutoTreeMutation mut(oldParent);
|
|
||||||
oldParent->InsertChildAt(idxInParent, child);
|
|
||||||
newParent = oldParent;
|
|
||||||
}
|
|
||||||
|
|
||||||
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(newParent);
|
|
||||||
RefPtr<AccMutationEvent> showEvent =
|
|
||||||
new AccShowEvent(child, child->GetContent());
|
|
||||||
FireDelayedEvent(showEvent);
|
|
||||||
reorderEvent->AddSubMutationEvent(showEvent);
|
|
||||||
|
|
||||||
MaybeNotifyOfValueChange(newParent);
|
|
||||||
FireDelayedEvent(reorderEvent);
|
|
||||||
|
|
||||||
child->SetRepositioned(isReinserted);
|
|
||||||
}
|
|
||||||
|
|
||||||
mARIAOwnsInvalidationList.Clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Accessible*
|
Accessible*
|
||||||
@@ -1635,31 +1596,6 @@ DocAccessible::AddDependentIDsFor(Accessible* aRelProvider, nsIAtom* aRelAttr)
|
|||||||
if (!HasAccessible(dependentContent)) {
|
if (!HasAccessible(dependentContent)) {
|
||||||
mInvalidationList.AppendElement(dependentContent);
|
mInvalidationList.AppendElement(dependentContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update ARIA owns cache.
|
|
||||||
if (relAttr == nsGkAtoms::aria_owns) {
|
|
||||||
// ARIA owns cannot refer to itself or a parent. Ignore
|
|
||||||
// the element if so.
|
|
||||||
nsIContent* parentEl = relProviderEl;
|
|
||||||
while (parentEl && parentEl != dependentContent) {
|
|
||||||
parentEl = parentEl->GetParent();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!parentEl) {
|
|
||||||
// ARIA owns element cannot refer to an element in parents chain
|
|
||||||
// of other ARIA owns element (including that ARIA owns element)
|
|
||||||
// if it's inside of a dependent element subtree of that
|
|
||||||
// ARIA owns element. Applied recursively.
|
|
||||||
if (!IsInARIAOwnsLoop(relProviderEl, dependentContent)) {
|
|
||||||
nsTArray<nsIContent*>* list =
|
|
||||||
mARIAOwnsHash.LookupOrAdd(aRelProvider);
|
|
||||||
list->AppendElement(dependentContent);
|
|
||||||
|
|
||||||
mARIAOwnsInvalidationList.AppendElement(
|
|
||||||
ARIAOwnsPair(aRelProvider, dependentContent));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1709,59 +1645,6 @@ DocAccessible::RemoveDependentIDsFor(Accessible* aRelProvider,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// aria-owns has gone, put the children back.
|
|
||||||
if (relAttr == nsGkAtoms::aria_owns) {
|
|
||||||
nsTArray<nsIContent*>* children = mARIAOwnsHash.Get(aRelProvider);
|
|
||||||
if (children) {
|
|
||||||
nsTArray<Accessible*> containers;
|
|
||||||
|
|
||||||
// Remove ARIA owned elements from where they belonged.
|
|
||||||
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(aRelProvider);
|
|
||||||
{
|
|
||||||
AutoTreeMutation mut(aRelProvider);
|
|
||||||
for (uint32_t idx = 0; idx < children->Length(); idx++) {
|
|
||||||
nsIContent* childEl = children->ElementAt(idx);
|
|
||||||
Accessible* child = GetAccessible(childEl);
|
|
||||||
if (child && child->IsRepositioned()) {
|
|
||||||
{
|
|
||||||
RefPtr<AccMutationEvent> hideEvent =
|
|
||||||
new AccHideEvent(child, childEl, false);
|
|
||||||
FireDelayedEvent(hideEvent);
|
|
||||||
reorderEvent->AddSubMutationEvent(hideEvent);
|
|
||||||
|
|
||||||
aRelProvider->RemoveChild(child);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collect DOM-order containers to update their trees.
|
|
||||||
child->SetRepositioned(false);
|
|
||||||
Accessible* container = GetContainerAccessible(childEl);
|
|
||||||
if (!containers.Contains(container)) {
|
|
||||||
containers.AppendElement(container);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mARIAOwnsHash.Remove(aRelProvider);
|
|
||||||
for (uint32_t idx = 0; idx < mARIAOwnsInvalidationList.Length();) {
|
|
||||||
if (mARIAOwnsInvalidationList[idx].mOwner == aRelProvider) {
|
|
||||||
mARIAOwnsInvalidationList.RemoveElementAt(idx);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
idx++;
|
|
||||||
}
|
|
||||||
|
|
||||||
MaybeNotifyOfValueChange(aRelProvider);
|
|
||||||
FireDelayedEvent(reorderEvent);
|
|
||||||
|
|
||||||
// Reinserted previously ARIA owned elements into the tree
|
|
||||||
// (restore a DOM-like order).
|
|
||||||
for (uint32_t idx = 0; idx < containers.Length(); idx++) {
|
|
||||||
UpdateTreeOnInsertion(containers[idx]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the relation attribute is given then we don't have anything else to
|
// If the relation attribute is given then we don't have anything else to
|
||||||
// check.
|
// check.
|
||||||
if (aRelAttr)
|
if (aRelAttr)
|
||||||
@@ -1769,45 +1652,6 @@ DocAccessible::RemoveDependentIDsFor(Accessible* aRelProvider,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
|
||||||
DocAccessible::IsInARIAOwnsLoop(nsIContent* aOwnerEl, nsIContent* aDependentEl)
|
|
||||||
{
|
|
||||||
// ARIA owns element cannot refer to an element in parents chain of other ARIA
|
|
||||||
// owns element (including that ARIA owns element) if it's inside of
|
|
||||||
// a dependent element subtree of that ARIA owns element.
|
|
||||||
for (auto it = mARIAOwnsHash.Iter(); !it.Done(); it.Next()) {
|
|
||||||
Accessible* otherOwner = it.Key();
|
|
||||||
nsIContent* parentEl = otherOwner->GetContent();
|
|
||||||
while (parentEl && parentEl != aDependentEl) {
|
|
||||||
parentEl = parentEl->GetParent();
|
|
||||||
}
|
|
||||||
|
|
||||||
// The dependent element of this ARIA owns element contains some other ARIA
|
|
||||||
// owns element, make sure this ARIA owns element is not in a subtree of
|
|
||||||
// a dependent element of that other ARIA owns element. If not then
|
|
||||||
// continue a check recursively.
|
|
||||||
if (parentEl) {
|
|
||||||
nsTArray<nsIContent*>* childEls = it.UserData();
|
|
||||||
for (uint32_t idx = 0; idx < childEls->Length(); idx++) {
|
|
||||||
nsIContent* childEl = childEls->ElementAt(idx);
|
|
||||||
nsIContent* parentEl = aOwnerEl;
|
|
||||||
while (parentEl && parentEl != childEl) {
|
|
||||||
parentEl = parentEl->GetParent();
|
|
||||||
}
|
|
||||||
if (parentEl) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsInARIAOwnsLoop(aOwnerEl, childEl)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
DocAccessible::UpdateAccessibleOnAttrChange(dom::Element* aElement,
|
DocAccessible::UpdateAccessibleOnAttrChange(dom::Element* aElement,
|
||||||
nsIAtom* aAttribute)
|
nsIAtom* aAttribute)
|
||||||
@@ -2023,11 +1867,6 @@ DocAccessible::UpdateTreeOnRemoval(Accessible* aContainer, nsIContent* aChildNod
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We may not have an integral DOM tree to remove all aria-owns relations
|
|
||||||
// from the tree. Validate all relations after timeout to workaround that.
|
|
||||||
mNotificationController->ScheduleNotification<DocAccessible>
|
|
||||||
(this, &DocAccessible::ValidateARIAOwned);
|
|
||||||
|
|
||||||
// Content insertion/removal is not cause of accessible tree change.
|
// Content insertion/removal is not cause of accessible tree change.
|
||||||
if (updateFlags == eNoAccessible)
|
if (updateFlags == eNoAccessible)
|
||||||
return;
|
return;
|
||||||
@@ -2111,22 +1950,243 @@ DocAccessible::UpdateTreeInternal(Accessible* aChild, bool aIsInsert,
|
|||||||
return updateFlags;
|
return updateFlags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
DocAccessible::RelocateARIAOwnedIfNeeded(nsIContent* aElement)
|
||||||
|
{
|
||||||
|
if (!aElement->HasID())
|
||||||
|
return;
|
||||||
|
|
||||||
|
AttrRelProviderArray* list =
|
||||||
|
mDependentIDsHash.Get(nsDependentAtomString(aElement->GetID()));
|
||||||
|
if (list) {
|
||||||
|
for (uint32_t idx = 0; idx < list->Length(); idx++) {
|
||||||
|
if (list->ElementAt(idx)->mRelAttr == nsGkAtoms::aria_owns) {
|
||||||
|
Accessible* owner = GetAccessible(list->ElementAt(idx)->mContent);
|
||||||
|
if (owner) {
|
||||||
|
mNotificationController->ScheduleRelocation(owner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
DocAccessible::ValidateARIAOwned()
|
DocAccessible::ValidateARIAOwned()
|
||||||
{
|
{
|
||||||
for (auto it = mARIAOwnsHash.Iter(); !it.Done(); it.Next()) {
|
for (auto it = mARIAOwnsHash.Iter(); !it.Done(); it.Next()) {
|
||||||
nsTArray<nsIContent*>* childEls = it.UserData();
|
Accessible* owner = it.Key();
|
||||||
for (uint32_t idx = 0; idx < childEls->Length(); idx++) {
|
nsTArray<RefPtr<Accessible> >* children = it.UserData();
|
||||||
nsIContent* childEl = childEls->ElementAt(idx);
|
|
||||||
Accessible* child = GetAccessible(childEl);
|
// Owner is about to die, put children back if applicable.
|
||||||
if (child && child->IsInDocument() && !child->GetFrame()) {
|
if (!owner->IsInDocument()) {
|
||||||
if (!child->Parent()) {
|
PutChildrenBack(children, 0);
|
||||||
NS_ERROR("An element in the document doesn't have a parent?");
|
it.Remove();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
UpdateTreeOnRemoval(child->Parent(), childEl);
|
|
||||||
|
for (uint32_t idx = 0; idx < children->Length(); idx++) {
|
||||||
|
Accessible* child = children->ElementAt(idx);
|
||||||
|
if (!child->IsInDocument()) {
|
||||||
|
children->RemoveElementAt(idx);
|
||||||
|
idx--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_ASSERTION(child->Parent(), "No parent for ARIA owned?");
|
||||||
|
|
||||||
|
// If DOM node doesn't have a frame anymore then shutdown its accessible.
|
||||||
|
if (child->Parent() && !child->GetFrame()) {
|
||||||
|
UpdateTreeOnRemoval(child->Parent(), child->GetContent());
|
||||||
|
children->RemoveElementAt(idx);
|
||||||
|
idx--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_ASSERTION(child->Parent() == owner,
|
||||||
|
"Illigally stolen ARIA owned child!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (children->Length() == 0) {
|
||||||
|
it.Remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
DocAccessible::DoARIAOwnsRelocation(Accessible* aOwner)
|
||||||
|
{
|
||||||
|
nsTArray<RefPtr<Accessible> >* children = mARIAOwnsHash.LookupOrAdd(aOwner);
|
||||||
|
|
||||||
|
IDRefsIterator iter(this, aOwner->Elm(), nsGkAtoms::aria_owns);
|
||||||
|
Accessible* child = nullptr;
|
||||||
|
|
||||||
|
uint32_t arrayIdx = 0, insertIdx = aOwner->ChildCount() - children->Length();
|
||||||
|
while ((child = iter.Next())) {
|
||||||
|
// Same child on same position, no change.
|
||||||
|
if (child->Parent() == aOwner &&
|
||||||
|
child->IndexInParent() == static_cast<int32_t>(insertIdx)) {
|
||||||
|
NS_ASSERTION(child == children->ElementAt(arrayIdx), "Not in sync!");
|
||||||
|
insertIdx++; arrayIdx++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_ASSERTION(children->SafeElementAt(arrayIdx) != child, "Already in place!");
|
||||||
|
|
||||||
|
nsTArray<RefPtr<Accessible> >::index_type idx = children->IndexOf(child);
|
||||||
|
if (idx < arrayIdx) {
|
||||||
|
continue; // ignore second entry of same ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// A new child is found, check for loops.
|
||||||
|
if (child->Parent() != aOwner) {
|
||||||
|
Accessible* parent = aOwner;
|
||||||
|
while (parent && parent != child && !parent->IsDoc()) {
|
||||||
|
parent = parent->Parent();
|
||||||
|
}
|
||||||
|
// A referred child cannot be a parent of the owner.
|
||||||
|
if (parent == child) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (child->Parent() == aOwner) {
|
||||||
|
MoveChild(child, insertIdx - 1);
|
||||||
|
children->InsertElementAt(arrayIdx, child);
|
||||||
|
arrayIdx++;
|
||||||
|
|
||||||
|
} else if (SeizeChild(aOwner, child, insertIdx)) {
|
||||||
|
children->InsertElementAt(arrayIdx, child);
|
||||||
|
insertIdx++; arrayIdx++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put back children that are not seized anymore.
|
||||||
|
PutChildrenBack(children, arrayIdx);
|
||||||
|
if (children->Length() == 0) {
|
||||||
|
mARIAOwnsHash.Remove(aOwner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
DocAccessible::SeizeChild(Accessible* aNewParent, Accessible* aChild,
|
||||||
|
int32_t aIdxInParent)
|
||||||
|
{
|
||||||
|
Accessible* oldParent = aChild->Parent();
|
||||||
|
NS_PRECONDITION(oldParent, "No parent?");
|
||||||
|
|
||||||
|
int32_t oldIdxInParent = aChild->IndexInParent();
|
||||||
|
|
||||||
|
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(oldParent);
|
||||||
|
RefPtr<AccMutationEvent> hideEvent =
|
||||||
|
new AccHideEvent(aChild, aChild->GetContent(), false);
|
||||||
|
reorderEvent->AddSubMutationEvent(hideEvent);
|
||||||
|
|
||||||
|
{
|
||||||
|
AutoTreeMutation mut(oldParent);
|
||||||
|
oldParent->RemoveChild(aChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isReinserted = false;
|
||||||
|
{
|
||||||
|
AutoTreeMutation mut(aNewParent);
|
||||||
|
isReinserted = aNewParent->InsertChildAt(aIdxInParent, aChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isReinserted) {
|
||||||
|
AutoTreeMutation mut(oldParent);
|
||||||
|
oldParent->InsertChildAt(oldIdxInParent, aChild);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The child may be stolen from other ARIA owns element.
|
||||||
|
if (aChild->IsRelocated()) {
|
||||||
|
nsTArray<RefPtr<Accessible> >* children = mARIAOwnsHash.Get(oldParent);
|
||||||
|
children->RemoveElement(aChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
FireDelayedEvent(hideEvent);
|
||||||
|
MaybeNotifyOfValueChange(oldParent);
|
||||||
|
FireDelayedEvent(reorderEvent);
|
||||||
|
|
||||||
|
reorderEvent = new AccReorderEvent(aNewParent);
|
||||||
|
RefPtr<AccMutationEvent> showEvent =
|
||||||
|
new AccShowEvent(aChild, aChild->GetContent());
|
||||||
|
reorderEvent->AddSubMutationEvent(showEvent);
|
||||||
|
|
||||||
|
FireDelayedEvent(showEvent);
|
||||||
|
MaybeNotifyOfValueChange(aNewParent);
|
||||||
|
FireDelayedEvent(reorderEvent);
|
||||||
|
|
||||||
|
aChild->SetRelocated(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
DocAccessible::MoveChild(Accessible* aChild, int32_t aIdxInParent)
|
||||||
|
{
|
||||||
|
NS_PRECONDITION(aChild->Parent(), "No parent?");
|
||||||
|
|
||||||
|
Accessible* parent = aChild->Parent();
|
||||||
|
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(parent);
|
||||||
|
RefPtr<AccMutationEvent> hideEvent =
|
||||||
|
new AccHideEvent(aChild, aChild->GetContent(), false);
|
||||||
|
reorderEvent->AddSubMutationEvent(hideEvent);
|
||||||
|
|
||||||
|
AutoTreeMutation mut(parent);
|
||||||
|
parent->RemoveChild(aChild);
|
||||||
|
|
||||||
|
parent->InsertChildAt(aIdxInParent, aChild);
|
||||||
|
aChild->SetRelocated(true);
|
||||||
|
|
||||||
|
FireDelayedEvent(hideEvent);
|
||||||
|
|
||||||
|
RefPtr<AccMutationEvent> showEvent =
|
||||||
|
new AccShowEvent(aChild, aChild->GetContent());
|
||||||
|
reorderEvent->AddSubMutationEvent(showEvent);
|
||||||
|
FireDelayedEvent(showEvent);
|
||||||
|
|
||||||
|
MaybeNotifyOfValueChange(parent);
|
||||||
|
FireDelayedEvent(reorderEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
DocAccessible::PutChildrenBack(nsTArray<RefPtr<Accessible> >* aChildren,
|
||||||
|
uint32_t aStartIdx)
|
||||||
|
{
|
||||||
|
nsTArray<Accessible*> containers;
|
||||||
|
for (auto idx = aStartIdx; idx < aChildren->Length(); idx++) {
|
||||||
|
Accessible* child = aChildren->ElementAt(idx);
|
||||||
|
|
||||||
|
// If the child is in the tree then remove it from the owner.
|
||||||
|
if (child->IsInDocument()) {
|
||||||
|
Accessible* owner = child->Parent();
|
||||||
|
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(owner);
|
||||||
|
RefPtr<AccMutationEvent> hideEvent =
|
||||||
|
new AccHideEvent(child, child->GetContent(), false);
|
||||||
|
reorderEvent->AddSubMutationEvent(hideEvent);
|
||||||
|
|
||||||
|
{
|
||||||
|
AutoTreeMutation mut(owner);
|
||||||
|
owner->RemoveChild(child);
|
||||||
|
child->SetRelocated(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
FireDelayedEvent(hideEvent);
|
||||||
|
MaybeNotifyOfValueChange(owner);
|
||||||
|
FireDelayedEvent(reorderEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
Accessible* container = GetContainerAccessible(child->GetContent());
|
||||||
|
if (container &&
|
||||||
|
containers.IndexOf(container) == nsTArray<Accessible*>::NoIndex) {
|
||||||
|
containers.AppendElement(container);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// And put it back where it belongs to.
|
||||||
|
aChildren->RemoveElementsAt(aStartIdx, aChildren->Length() - aStartIdx);
|
||||||
|
for (uint32_t idx = 0; idx < containers.Length(); idx++) {
|
||||||
|
UpdateTreeOnInsertion(containers[idx]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -286,13 +286,9 @@ public:
|
|||||||
*/
|
*/
|
||||||
Accessible* ARIAOwnedAt(Accessible* aParent, uint32_t aIndex) const
|
Accessible* ARIAOwnedAt(Accessible* aParent, uint32_t aIndex) const
|
||||||
{
|
{
|
||||||
nsTArray<nsIContent*>* childrenEl = mARIAOwnsHash.Get(aParent);
|
nsTArray<RefPtr<Accessible> >* children = mARIAOwnsHash.Get(aParent);
|
||||||
if (childrenEl) {
|
if (children) {
|
||||||
nsIContent* childEl = childrenEl->SafeElementAt(aIndex);
|
return children->SafeElementAt(aIndex);
|
||||||
Accessible* child = GetAccessible(childEl);
|
|
||||||
if (child && child->IsRepositioned()) {
|
|
||||||
return child;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@@ -436,12 +432,6 @@ protected:
|
|||||||
void RemoveDependentIDsFor(Accessible* aRelProvider,
|
void RemoveDependentIDsFor(Accessible* aRelProvider,
|
||||||
nsIAtom* aRelAttr = nullptr);
|
nsIAtom* aRelAttr = nullptr);
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true if given ARIA owner element and its referred content make
|
|
||||||
* the loop closed.
|
|
||||||
*/
|
|
||||||
bool IsInARIAOwnsLoop(nsIContent* aOwnerEl, nsIContent* aDependentEl);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update or recreate an accessible depending on a changed attribute.
|
* Update or recreate an accessible depending on a changed attribute.
|
||||||
*
|
*
|
||||||
@@ -513,11 +503,38 @@ protected:
|
|||||||
uint32_t UpdateTreeInternal(Accessible* aChild, bool aIsInsert,
|
uint32_t UpdateTreeInternal(Accessible* aChild, bool aIsInsert,
|
||||||
AccReorderEvent* aReorderEvent);
|
AccReorderEvent* aReorderEvent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedule ARIA owned element relocation if needed.
|
||||||
|
*/
|
||||||
|
void RelocateARIAOwnedIfNeeded(nsIContent* aEl);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates all aria-owns connections and updates the tree accordingly.
|
* Validates all aria-owns connections and updates the tree accordingly.
|
||||||
*/
|
*/
|
||||||
void ValidateARIAOwned();
|
void ValidateARIAOwned();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Steals or puts back accessible subtrees.
|
||||||
|
*/
|
||||||
|
void DoARIAOwnsRelocation(Accessible* aOwner);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves the child from old parent under new one.
|
||||||
|
*/
|
||||||
|
bool SeizeChild(Accessible* aNewParent, Accessible* aChild,
|
||||||
|
int32_t aIdxInParent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move the child under same parent.
|
||||||
|
*/
|
||||||
|
void MoveChild(Accessible* aChild, int32_t aIdxInParent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves children back under their original parents.
|
||||||
|
*/
|
||||||
|
void PutChildrenBack(nsTArray<RefPtr<Accessible> >* aChildren,
|
||||||
|
uint32_t aStartIdx);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create accessible tree.
|
* Create accessible tree.
|
||||||
*
|
*
|
||||||
@@ -649,14 +666,12 @@ protected:
|
|||||||
AttrRelProvider& operator =(const AttrRelProvider&);
|
AttrRelProvider& operator =(const AttrRelProvider&);
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef nsTArray<nsAutoPtr<AttrRelProvider> > AttrRelProviderArray;
|
|
||||||
typedef nsClassHashtable<nsStringHashKey, AttrRelProviderArray>
|
|
||||||
DependentIDsHashtable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The cache of IDs pointed by relation attributes.
|
* The cache of IDs pointed by relation attributes.
|
||||||
*/
|
*/
|
||||||
DependentIDsHashtable mDependentIDsHash;
|
typedef nsTArray<nsAutoPtr<AttrRelProvider> > AttrRelProviderArray;
|
||||||
|
nsClassHashtable<nsStringHashKey, AttrRelProviderArray>
|
||||||
|
mDependentIDsHash;
|
||||||
|
|
||||||
friend class RelatedAccIterator;
|
friend class RelatedAccIterator;
|
||||||
|
|
||||||
@@ -669,24 +684,11 @@ protected:
|
|||||||
nsTArray<nsIContent*> mInvalidationList;
|
nsTArray<nsIContent*> mInvalidationList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds a list of aria-owns relations.
|
* Holds a list of aria-owns relocations.
|
||||||
*/
|
*/
|
||||||
nsClassHashtable<nsPtrHashKey<Accessible>, nsTArray<nsIContent*> >
|
nsClassHashtable<nsPtrHashKey<Accessible>, nsTArray<RefPtr<Accessible> > >
|
||||||
mARIAOwnsHash;
|
mARIAOwnsHash;
|
||||||
|
|
||||||
struct ARIAOwnsPair {
|
|
||||||
ARIAOwnsPair(Accessible* aOwner, nsIContent* aChild) :
|
|
||||||
mOwner(aOwner), mChild(aChild) { }
|
|
||||||
ARIAOwnsPair(const ARIAOwnsPair& aPair) :
|
|
||||||
mOwner(aPair.mOwner), mChild(aPair.mChild) { }
|
|
||||||
ARIAOwnsPair& operator =(const ARIAOwnsPair& aPair)
|
|
||||||
{ mOwner = aPair.mOwner; mChild = aPair.mChild; return *this; }
|
|
||||||
|
|
||||||
RefPtr<Accessible> mOwner;
|
|
||||||
nsCOMPtr<nsIContent> mChild;
|
|
||||||
};
|
|
||||||
nsTArray<ARIAOwnsPair> mARIAOwnsInvalidationList;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to process notification from core and accessible events.
|
* Used to process notification from core and accessible events.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -51,9 +51,11 @@
|
|||||||
{
|
{
|
||||||
this.menuNode = getNode(aMenuID);
|
this.menuNode = getNode(aMenuID);
|
||||||
|
|
||||||
|
// Because of aria-owns processing we may have menupopup start fired before
|
||||||
|
// related show event.
|
||||||
this.eventSeq = [
|
this.eventSeq = [
|
||||||
new invokerChecker(EVENT_SHOW, this.menuNode),
|
new invokerChecker(EVENT_SHOW, this.menuNode),
|
||||||
new invokerChecker(EVENT_MENUPOPUP_START, this.menuNode),
|
new asyncInvokerChecker(EVENT_MENUPOPUP_START, this.menuNode),
|
||||||
new invokerChecker(EVENT_REORDER, getNode(aParentMenuID))
|
new invokerChecker(EVENT_REORDER, getNode(aParentMenuID))
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
{
|
{
|
||||||
var tree =
|
var tree =
|
||||||
{ SECTION: [ // t1_1
|
{ SECTION: [ // t1_1
|
||||||
{ SECTION: [ // t1_2
|
{ HEADING: [ // t1_2
|
||||||
// no kids, no loop
|
// no kids, no loop
|
||||||
] }
|
] }
|
||||||
] };
|
] };
|
||||||
@@ -36,8 +36,8 @@
|
|||||||
|
|
||||||
tree =
|
tree =
|
||||||
{ SECTION: [ // t2_1
|
{ SECTION: [ // t2_1
|
||||||
{ SECTION: [ // t2_2
|
{ GROUPING: [ // t2_2
|
||||||
{ SECTION: [ // t2_3
|
{ HEADING: [ // t2_3
|
||||||
// no kids, no loop
|
// no kids, no loop
|
||||||
] }
|
] }
|
||||||
] }
|
] }
|
||||||
@@ -46,9 +46,9 @@
|
|||||||
|
|
||||||
tree =
|
tree =
|
||||||
{ SECTION: [ // t3_3
|
{ SECTION: [ // t3_3
|
||||||
{ SECTION: [ // t3_1
|
{ GROUPING: [ // t3_1
|
||||||
{ SECTION: [ // t3_2
|
{ NOTE: [ // t3_2
|
||||||
{ SECTION: [ // DOM child of t3_2
|
{ HEADING: [ // DOM child of t3_2
|
||||||
// no kids, no loop
|
// no kids, no loop
|
||||||
] }
|
] }
|
||||||
] }
|
] }
|
||||||
@@ -58,7 +58,7 @@
|
|||||||
|
|
||||||
tree =
|
tree =
|
||||||
{ SECTION: [ // t4_1
|
{ SECTION: [ // t4_1
|
||||||
{ SECTION: [ // DOM child of t4_1
|
{ GROUPING: [ // DOM child of t4_1, aria-owns ignored
|
||||||
// no kids, no loop
|
// no kids, no loop
|
||||||
] }
|
] }
|
||||||
] };
|
] };
|
||||||
@@ -66,11 +66,11 @@
|
|||||||
|
|
||||||
tree =
|
tree =
|
||||||
{ SECTION: [ // t5_1
|
{ SECTION: [ // t5_1
|
||||||
{ SECTION: [ // DOM child of t5_1
|
{ GROUPING: [ // DOM child of t5_1
|
||||||
{ SECTION: [ // t5_2
|
{ NOTE: [ // t5_2
|
||||||
{ SECTION: [ // DOM child of t5_2
|
{ HEADING: [ // DOM child of t5_2
|
||||||
{ SECTION: [ // t5_3
|
{ FORM: [ // t5_3
|
||||||
{ SECTION: [ // DOM child of t5_3
|
{ TOOLTIP: [ // DOM child of t5_3
|
||||||
// no kids, no loop
|
// no kids, no loop
|
||||||
]}
|
]}
|
||||||
]}
|
]}
|
||||||
@@ -80,6 +80,14 @@
|
|||||||
] };
|
] };
|
||||||
testAccessibleTree("t5_1", tree);
|
testAccessibleTree("t5_1", tree);
|
||||||
|
|
||||||
|
tree =
|
||||||
|
{ SECTION: [ // t6_1
|
||||||
|
{ RADIOBUTTON: [ ] },
|
||||||
|
{ CHECKBUTTON: [ ] }, // t6_3, rearranged by aria-owns
|
||||||
|
{ PUSHBUTTON: [ ] }, // t6_2, rearranged by aria-owns
|
||||||
|
] };
|
||||||
|
testAccessibleTree("t6_1", tree);
|
||||||
|
|
||||||
SimpleTest.finish();
|
SimpleTest.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,24 +104,37 @@
|
|||||||
<pre id="test">
|
<pre id="test">
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
<!-- simple loop -->
|
||||||
<div id="t1_1" aria-owns="t1_2"></div>
|
<div id="t1_1" aria-owns="t1_2"></div>
|
||||||
<div id="t1_2" aria-owns="t1_1"></div>
|
<div id="t1_2" aria-owns="t1_1" role="heading"></div>
|
||||||
|
|
||||||
<div id="t2_2" aria-owns="t2_3"></div>
|
<!-- loop -->
|
||||||
|
<div id="t2_2" aria-owns="t2_3" role="group"></div>
|
||||||
<div id="t2_1" aria-owns="t2_2"></div>
|
<div id="t2_1" aria-owns="t2_2"></div>
|
||||||
<div id="t2_3" aria-owns="t2_1"></div>
|
<div id="t2_3" aria-owns="t2_1" role="heading"></div>
|
||||||
|
|
||||||
<div id="t3_1" aria-owns="t3_2"></div>
|
<!-- loop #2 -->
|
||||||
<div id="t3_2">
|
<div id="t3_1" aria-owns="t3_2" role="group"></div>
|
||||||
<div aria-owns="t3_3"></div>
|
<div id="t3_2" role="note">
|
||||||
|
<div aria-owns="t3_3" role="heading"></div>
|
||||||
</div>
|
</div>
|
||||||
<div id="t3_3" aria-owns="t3_1"></div>
|
<div id="t3_3" aria-owns="t3_1"></div>
|
||||||
|
|
||||||
<div id="t4_1"><div aria-owns="t4_1"></div></div>
|
<!-- self loop -->
|
||||||
|
<div id="t4_1"><div aria-owns="t4_1" role="group"></div></div>
|
||||||
|
|
||||||
|
<!-- natural and aria-owns hierarchy -->
|
||||||
|
<div id="t5_1"><div aria-owns="t5_2" role="group"></div></div>
|
||||||
|
<div id="t5_2" role="note"><div aria-owns="t5_3" role="heading"></div></div>
|
||||||
|
<div id="t5_3" role="form"><div aria-owns="t5_1" role="tooltip"></div></div>
|
||||||
|
|
||||||
|
<!-- rearrange children -->
|
||||||
|
<div id="t6_1" aria-owns="t6_3 t6_2">
|
||||||
|
<div id="t6_2" role="button"></div>
|
||||||
|
<div id="t6_3" role="checkbox"></div>
|
||||||
|
<div role="radio"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="t5_1"><div aria-owns="t5_2"></div>
|
|
||||||
<div id="t5_2"><div aria-owns="t5_3"></div></div>
|
|
||||||
<div id="t5_3"><div aria-owns="t5_1"></div></div>
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -26,11 +26,11 @@
|
|||||||
function removeARIAOwns()
|
function removeARIAOwns()
|
||||||
{
|
{
|
||||||
this.eventSeq = [
|
this.eventSeq = [
|
||||||
new invokerChecker(EVENT_HIDE, getNode("t2_checkbox")),
|
new invokerChecker(EVENT_HIDE, getNode("t1_checkbox")),
|
||||||
new invokerChecker(EVENT_HIDE, getNode("t2_button")),
|
new invokerChecker(EVENT_HIDE, getNode("t1_button")),
|
||||||
new invokerChecker(EVENT_SHOW, getNode("t2_button")),
|
new invokerChecker(EVENT_SHOW, getNode("t1_button")),
|
||||||
new invokerChecker(EVENT_SHOW, getNode("t2_checkbox")),
|
new invokerChecker(EVENT_SHOW, getNode("t1_checkbox")),
|
||||||
new invokerChecker(EVENT_REORDER, getNode("container2"))
|
new invokerChecker(EVENT_REORDER, getNode("t1_container"))
|
||||||
];
|
];
|
||||||
|
|
||||||
this.invoke = function removeARIAOwns_invoke()
|
this.invoke = function removeARIAOwns_invoke()
|
||||||
@@ -43,9 +43,9 @@
|
|||||||
] },
|
] },
|
||||||
{ PUSHBUTTON: [ ] }
|
{ PUSHBUTTON: [ ] }
|
||||||
] };
|
] };
|
||||||
testAccessibleTree("container2", tree);
|
testAccessibleTree("t1_container", tree);
|
||||||
|
|
||||||
getNode("container2").removeAttribute("aria-owns");
|
getNode("t1_container").removeAttribute("aria-owns");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.finalCheck = function removeARIAOwns_finalCheck()
|
this.finalCheck = function removeARIAOwns_finalCheck()
|
||||||
@@ -58,7 +58,7 @@
|
|||||||
{ SECTION: [] }
|
{ SECTION: [] }
|
||||||
] }
|
] }
|
||||||
] };
|
] };
|
||||||
testAccessibleTree("container2", tree);
|
testAccessibleTree("t1_container", tree);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.getID = function removeARIAOwns_getID()
|
this.getID = function removeARIAOwns_getID()
|
||||||
@@ -70,16 +70,17 @@
|
|||||||
function setARIAOwns()
|
function setARIAOwns()
|
||||||
{
|
{
|
||||||
this.eventSeq = [
|
this.eventSeq = [
|
||||||
new invokerChecker(EVENT_HIDE, getNode("t2_button")),
|
new invokerChecker(EVENT_HIDE, getNode("t1_button")),
|
||||||
new invokerChecker(EVENT_SHOW, getNode("t2_button")),
|
new invokerChecker(EVENT_SHOW, getNode("t1_button")),
|
||||||
new invokerChecker(EVENT_HIDE, getNode("t2_subdiv")),
|
new invokerChecker(EVENT_HIDE, getNode("t1_subdiv")),
|
||||||
new invokerChecker(EVENT_SHOW, getNode("t2_subdiv")),
|
new invokerChecker(EVENT_SHOW, getNode("t1_subdiv")),
|
||||||
new invokerChecker(EVENT_REORDER, getNode("container2"))
|
new invokerChecker(EVENT_REORDER, getNode("t1_container"))
|
||||||
];
|
];
|
||||||
|
|
||||||
this.invoke = function setARIAOwns_invoke()
|
this.invoke = function setARIAOwns_invoke()
|
||||||
{
|
{
|
||||||
getNode("container2").setAttribute("aria-owns", "t2_button t2_subdiv");
|
getNode("t1_container").
|
||||||
|
setAttribute("aria-owns", "t1_button t1_subdiv");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.finalCheck = function setARIAOwns_finalCheck()
|
this.finalCheck = function setARIAOwns_finalCheck()
|
||||||
@@ -88,11 +89,11 @@
|
|||||||
// the children.
|
// the children.
|
||||||
var tree =
|
var tree =
|
||||||
{ SECTION: [
|
{ SECTION: [
|
||||||
{ CHECKBUTTON: [ ] }, // div
|
{ CHECKBUTTON: [ ] }, // checkbox
|
||||||
{ PUSHBUTTON: [ ] }, // button
|
{ PUSHBUTTON: [ ] }, // button, rearranged by ARIA own
|
||||||
{ SECTION: [ ] } // subdiv
|
{ SECTION: [ ] } // subdiv from the subtree, ARIA owned
|
||||||
] };
|
] };
|
||||||
testAccessibleTree("container2", tree);
|
testAccessibleTree("t1_container", tree);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.getID = function setARIAOwns_getID()
|
this.getID = function setARIAOwns_getID()
|
||||||
@@ -101,19 +102,53 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addIdToARIAOwns()
|
||||||
|
{
|
||||||
|
this.eventSeq = [
|
||||||
|
new invokerChecker(EVENT_HIDE, getNode("t1_group")),
|
||||||
|
new invokerChecker(EVENT_SHOW, getNode("t1_group")),
|
||||||
|
new invokerChecker(EVENT_REORDER, document)
|
||||||
|
];
|
||||||
|
|
||||||
|
this.invoke = function addIdToARIAOwns_invoke()
|
||||||
|
{
|
||||||
|
getNode("t1_container").
|
||||||
|
setAttribute("aria-owns", "t1_button t1_subdiv t1_group");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.finalCheck = function addIdToARIAOwns_finalCheck()
|
||||||
|
{
|
||||||
|
// children are swapped again, button and subdiv are appended to
|
||||||
|
// the children.
|
||||||
|
var tree =
|
||||||
|
{ SECTION: [
|
||||||
|
{ CHECKBUTTON: [ ] }, // t1_checkbox
|
||||||
|
{ PUSHBUTTON: [ ] }, // button, t1_button
|
||||||
|
{ SECTION: [ ] }, // subdiv from the subtree, t1_subdiv
|
||||||
|
{ GROUPING: [ ] } // group from outside, t1_group
|
||||||
|
] };
|
||||||
|
testAccessibleTree("t1_container", tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getID = function addIdToARIAOwns_getID()
|
||||||
|
{
|
||||||
|
return "Add id to @aria-owns attribute value";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function appendEl()
|
function appendEl()
|
||||||
{
|
{
|
||||||
this.eventSeq = [
|
this.eventSeq = [
|
||||||
new invokerChecker(EVENT_SHOW, getNode, "child3"),
|
new invokerChecker(EVENT_SHOW, getNode, "t1_child3"),
|
||||||
new invokerChecker(EVENT_REORDER, getNode("container2"))
|
new invokerChecker(EVENT_REORDER, getNode("t1_container"))
|
||||||
];
|
];
|
||||||
|
|
||||||
this.invoke = function appendEl_invoke()
|
this.invoke = function appendEl_invoke()
|
||||||
{
|
{
|
||||||
var div = document.createElement("div");
|
var div = document.createElement("div");
|
||||||
div.setAttribute("id", "child3");
|
div.setAttribute("id", "t1_child3");
|
||||||
div.setAttribute("role", "radio")
|
div.setAttribute("role", "radio")
|
||||||
getNode("container2").appendChild(div);
|
getNode("t1_container").appendChild(div);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.finalCheck = function appendEl_finalCheck()
|
this.finalCheck = function appendEl_finalCheck()
|
||||||
@@ -124,10 +159,11 @@
|
|||||||
{ SECTION: [
|
{ SECTION: [
|
||||||
{ CHECKBUTTON: [ ] },
|
{ CHECKBUTTON: [ ] },
|
||||||
{ RADIOBUTTON: [ ] },
|
{ RADIOBUTTON: [ ] },
|
||||||
{ PUSHBUTTON: [ ] }, // ARIA owned
|
{ PUSHBUTTON: [ ] }, // ARIA owned, t1_button
|
||||||
{ SECTION: [ ] } // ARIA owned
|
{ SECTION: [ ] }, // ARIA owned, t1_subdiv
|
||||||
|
{ GROUPING: [ ] } // ARIA owned, t1_group
|
||||||
] };
|
] };
|
||||||
testAccessibleTree("container2", tree);
|
testAccessibleTree("t1_container", tree);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.getID = function appendEl_getID()
|
this.getID = function appendEl_getID()
|
||||||
@@ -139,15 +175,15 @@
|
|||||||
function removeEl()
|
function removeEl()
|
||||||
{
|
{
|
||||||
this.eventSeq = [
|
this.eventSeq = [
|
||||||
new invokerChecker(EVENT_HIDE, getNode, "t2_checkbox"),
|
new invokerChecker(EVENT_HIDE, getNode, "t1_checkbox"),
|
||||||
new invokerChecker(EVENT_SHOW, getNode, "t2_checkbox"),
|
new invokerChecker(EVENT_SHOW, getNode, "t1_checkbox"),
|
||||||
new invokerChecker(EVENT_REORDER, getNode("container2"))
|
new invokerChecker(EVENT_REORDER, getNode("t1_container"))
|
||||||
];
|
];
|
||||||
|
|
||||||
this.invoke = function removeEl_invoke()
|
this.invoke = function removeEl_invoke()
|
||||||
{
|
{
|
||||||
// remove a container of t2_subdiv
|
// remove a container of t1_subdiv
|
||||||
getNode("t2_span").parentNode.removeChild(getNode("t2_span"));
|
getNode("t1_span").parentNode.removeChild(getNode("t1_span"));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.finalCheck = function removeEl_finalCheck()
|
this.finalCheck = function removeEl_finalCheck()
|
||||||
@@ -157,14 +193,214 @@
|
|||||||
{ SECTION: [
|
{ SECTION: [
|
||||||
{ CHECKBUTTON: [ ] },
|
{ CHECKBUTTON: [ ] },
|
||||||
{ RADIOBUTTON: [ ] },
|
{ RADIOBUTTON: [ ] },
|
||||||
{ PUSHBUTTON: [ ] } // ARIA owned
|
{ PUSHBUTTON: [ ] }, // ARIA owned, t1_button
|
||||||
|
{ GROUPING: [ ] } // ARIA owned, t1_group
|
||||||
] };
|
] };
|
||||||
testAccessibleTree("container2", tree);
|
testAccessibleTree("t1_container", tree);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.getID = function removeEl_getID()
|
this.getID = function removeEl_getID()
|
||||||
{
|
{
|
||||||
return "Remove a container of ARIA ownded element";
|
return "Remove a container of ARIA owned element";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeId()
|
||||||
|
{
|
||||||
|
this.eventSeq = [
|
||||||
|
new invokerChecker(EVENT_HIDE, getNode("t1_group")),
|
||||||
|
new invokerChecker(EVENT_SHOW, getNode("t1_group")),
|
||||||
|
new invokerChecker(EVENT_REORDER, document)
|
||||||
|
];
|
||||||
|
|
||||||
|
this.invoke = function removeId_invoke()
|
||||||
|
{
|
||||||
|
getNode("t1_group").removeAttribute("id");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.finalCheck = function removeId_finalCheck()
|
||||||
|
{
|
||||||
|
var tree =
|
||||||
|
{ SECTION: [
|
||||||
|
{ CHECKBUTTON: [ ] },
|
||||||
|
{ RADIOBUTTON: [ ] },
|
||||||
|
{ PUSHBUTTON: [ ] } // ARIA owned, t1_button
|
||||||
|
] };
|
||||||
|
testAccessibleTree("t1_container", tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getID = function removeId_getID()
|
||||||
|
{
|
||||||
|
return "Remove ID from ARIA owned element";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setId()
|
||||||
|
{
|
||||||
|
this.eventSeq = [
|
||||||
|
new invokerChecker(EVENT_HIDE, getNode("t1_grouptmp")),
|
||||||
|
new invokerChecker(EVENT_SHOW, getNode("t1_grouptmp")),
|
||||||
|
new invokerChecker(EVENT_REORDER, document)
|
||||||
|
];
|
||||||
|
|
||||||
|
this.invoke = function setId_invoke()
|
||||||
|
{
|
||||||
|
getNode("t1_grouptmp").setAttribute("id", "t1_group");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.finalCheck = function setId_finalCheck()
|
||||||
|
{
|
||||||
|
var tree =
|
||||||
|
{ SECTION: [
|
||||||
|
{ CHECKBUTTON: [ ] },
|
||||||
|
{ RADIOBUTTON: [ ] },
|
||||||
|
{ PUSHBUTTON: [ ] }, // ARIA owned, t1_button
|
||||||
|
{ GROUPING: [ ] } // ARIA owned, t1_group, previously t1_grouptmp
|
||||||
|
] };
|
||||||
|
testAccessibleTree("t1_container", tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getID = function setId_getID()
|
||||||
|
{
|
||||||
|
return "Set ID that is referred by ARIA owns";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an accessible DOM element containing an element referred by
|
||||||
|
* ARIA owns.
|
||||||
|
*/
|
||||||
|
function removeA11eteiner()
|
||||||
|
{
|
||||||
|
this.eventSeq = [
|
||||||
|
new invokerChecker(EVENT_REORDER, getNode("t2_container1"))
|
||||||
|
];
|
||||||
|
|
||||||
|
this.invoke = function removeA11eteiner_invoke()
|
||||||
|
{
|
||||||
|
var tree =
|
||||||
|
{ SECTION: [
|
||||||
|
{ CHECKBUTTON: [ ] } // ARIA owned, 't2_owned'
|
||||||
|
] };
|
||||||
|
testAccessibleTree("t2_container1", tree);
|
||||||
|
|
||||||
|
getNode("t2_container2").removeChild(getNode("t2_container3"));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.finalCheck = function removeA11eteiner_finalCheck()
|
||||||
|
{
|
||||||
|
var tree =
|
||||||
|
{ SECTION: [
|
||||||
|
] };
|
||||||
|
testAccessibleTree("t2_container1", tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getID = function removeA11eteiner_getID()
|
||||||
|
{
|
||||||
|
return "Remove an accessible DOM element containing an element referred by ARIA owns";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Steal an element from other ARIA owns element. This use case guarantees
|
||||||
|
* that result of setAttribute/removeAttribute doesn't depend on their order.
|
||||||
|
*/
|
||||||
|
function stealFromOtherARIAOwns()
|
||||||
|
{
|
||||||
|
this.eventSeq = [
|
||||||
|
new invokerChecker(EVENT_REORDER, getNode("t3_container2"))
|
||||||
|
];
|
||||||
|
|
||||||
|
this.invoke = function stealFromOtherARIAOwns_invoke()
|
||||||
|
{
|
||||||
|
getNode("t3_container2").setAttribute("aria-owns", "t3_child");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.finalCheck = function stealFromOtherARIAOwns_finalCheck()
|
||||||
|
{
|
||||||
|
var tree =
|
||||||
|
{ SECTION: [
|
||||||
|
] };
|
||||||
|
testAccessibleTree("t3_container1", tree);
|
||||||
|
|
||||||
|
tree =
|
||||||
|
{ SECTION: [
|
||||||
|
{ CHECKBUTTON: [
|
||||||
|
] }
|
||||||
|
] };
|
||||||
|
testAccessibleTree("t3_container2", tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getID = function stealFromOtherARIAOwns_getID()
|
||||||
|
{
|
||||||
|
return "Steal an element from other ARIA owns element";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function appendElToRecacheChildren()
|
||||||
|
{
|
||||||
|
this.eventSeq = [
|
||||||
|
new invokerChecker(EVENT_REORDER, getNode("t3_container2"))
|
||||||
|
];
|
||||||
|
|
||||||
|
this.invoke = function appendElToRecacheChildren_invoke()
|
||||||
|
{
|
||||||
|
var div = document.createElement("div");
|
||||||
|
div.setAttribute("role", "radio")
|
||||||
|
getNode("t3_container2").appendChild(div);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.finalCheck = function appendElToRecacheChildren_finalCheck()
|
||||||
|
{
|
||||||
|
var tree =
|
||||||
|
{ SECTION: [
|
||||||
|
] };
|
||||||
|
testAccessibleTree("t3_container1", tree);
|
||||||
|
|
||||||
|
tree =
|
||||||
|
{ SECTION: [
|
||||||
|
{ RADIOBUTTON: [ ] },
|
||||||
|
{ CHECKBUTTON: [ ] } // ARIA owned
|
||||||
|
] };
|
||||||
|
testAccessibleTree("t3_container2", tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getID = function appendElToRecacheChildren_getID()
|
||||||
|
{
|
||||||
|
return "Append a child under @aria-owns element to trigger children recache";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showHiddenElement()
|
||||||
|
{
|
||||||
|
this.eventSeq = [
|
||||||
|
new invokerChecker(EVENT_REORDER, getNode("t4_container1"))
|
||||||
|
];
|
||||||
|
|
||||||
|
this.invoke = function showHiddenElement_invoke()
|
||||||
|
{
|
||||||
|
var tree =
|
||||||
|
{ SECTION: [
|
||||||
|
{ RADIOBUTTON: [] }
|
||||||
|
] };
|
||||||
|
testAccessibleTree("t4_container1", tree);
|
||||||
|
|
||||||
|
getNode("t4_child1").style.display = "block";
|
||||||
|
}
|
||||||
|
|
||||||
|
this.finalCheck = function showHiddenElement_finalCheck()
|
||||||
|
{
|
||||||
|
var tree =
|
||||||
|
{ SECTION: [
|
||||||
|
{ CHECKBUTTON: [] },
|
||||||
|
{ RADIOBUTTON: [] }
|
||||||
|
] };
|
||||||
|
testAccessibleTree("t4_container1", tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getID = function showHiddenElement_getID()
|
||||||
|
{
|
||||||
|
return "Show hidden ARIA owns referred element";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,10 +417,24 @@
|
|||||||
{
|
{
|
||||||
gQueue = new eventQueue();
|
gQueue = new eventQueue();
|
||||||
|
|
||||||
|
// test1
|
||||||
gQueue.push(new removeARIAOwns());
|
gQueue.push(new removeARIAOwns());
|
||||||
gQueue.push(new setARIAOwns());
|
gQueue.push(new setARIAOwns());
|
||||||
|
gQueue.push(new addIdToARIAOwns());
|
||||||
gQueue.push(new appendEl());
|
gQueue.push(new appendEl());
|
||||||
gQueue.push(new removeEl());
|
gQueue.push(new removeEl());
|
||||||
|
gQueue.push(new removeId());
|
||||||
|
gQueue.push(new setId());
|
||||||
|
|
||||||
|
// test2
|
||||||
|
gQueue.push(new removeA11eteiner());
|
||||||
|
|
||||||
|
// test3
|
||||||
|
gQueue.push(new stealFromOtherARIAOwns());
|
||||||
|
gQueue.push(new appendElToRecacheChildren());
|
||||||
|
|
||||||
|
// test4
|
||||||
|
gQueue.push(new showHiddenElement());
|
||||||
|
|
||||||
gQueue.invoke(); // SimpleTest.finish() will be called in the end
|
gQueue.invoke(); // SimpleTest.finish() will be called in the end
|
||||||
}
|
}
|
||||||
@@ -202,15 +452,31 @@
|
|||||||
<pre id="test">
|
<pre id="test">
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
<div id="container2" aria-owns="t2_checkbox t2_button">
|
<div id="t1_container" aria-owns="t1_checkbox t1_button">
|
||||||
<div role="button" id="t2_button"></div>
|
<div role="button" id="t1_button"></div>
|
||||||
<div role="checkbox" id="t2_checkbox">
|
<div role="checkbox" id="t1_checkbox">
|
||||||
<span id="t2_span">
|
<span id="t1_span">
|
||||||
<div id="t2_subdiv"></div>
|
<div id="t1_subdiv"></div>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="t1_group" role="group"></div>
|
||||||
|
<div id="t1_grouptmp" role="group"></div>
|
||||||
|
|
||||||
|
<div id="t2_container1" aria-owns="t2_owned"></div>
|
||||||
|
<div id="t2_container2">
|
||||||
|
<div id="t2_container3"><div id="t2_owned" role="checkbox"></div></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="t3_container1" aria-owns="t3_child"></div>
|
||||||
|
<div id="t3_child" role="checkbox"></div>
|
||||||
|
<div id="t3_container2"></div>
|
||||||
|
|
||||||
|
<div id="t4_container1" aria-owns="t4_child1 t4_child2"></div>
|
||||||
|
<div id="t4_container2">
|
||||||
|
<div id="t4_child1" style="display:none" role="checkbox"></div>
|
||||||
|
<div id="t4_child2" role="radio"></div>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -22,9 +22,9 @@
|
|||||||
|
|
||||||
this.eventSeq = [
|
this.eventSeq = [
|
||||||
new invokerChecker(EVENT_HIDE, getNode("child")),
|
new invokerChecker(EVENT_HIDE, getNode("child")),
|
||||||
new invokerChecker(EVENT_REORDER, this.containerNode),
|
|
||||||
new invokerChecker(EVENT_HIDE, getNode("childDoc")),
|
new invokerChecker(EVENT_HIDE, getNode("childDoc")),
|
||||||
new invokerChecker(EVENT_SHOW, "newChildDoc")
|
new invokerChecker(EVENT_SHOW, "newChildDoc"),
|
||||||
|
new invokerChecker(EVENT_REORDER, this.containerNode)
|
||||||
];
|
];
|
||||||
|
|
||||||
this.invoke = function runTest_invoke()
|
this.invoke = function runTest_invoke()
|
||||||
|
|||||||
Reference in New Issue
Block a user