зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1624909: Create and maintain radio siblings array for position information. r=eeejay
Differential Revision: https://phabricator.services.mozilla.com/D72751
This commit is contained in:
Родитель
d389f72e6d
Коммит
7a58efe041
|
@ -533,3 +533,21 @@ Accessible* Pivot::AtPoint(int32_t aX, int32_t aY, PivotRule& aRule) {
|
||||||
|
|
||||||
return match;
|
return match;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Role Rule
|
||||||
|
|
||||||
|
PivotRoleRule::PivotRoleRule(mozilla::a11y::role aRole) : mRole(aRole) {}
|
||||||
|
|
||||||
|
uint16_t PivotRoleRule::Match(Accessible* aAccessible) {
|
||||||
|
uint16_t result = nsIAccessibleTraversalRule::FILTER_IGNORE;
|
||||||
|
|
||||||
|
if (nsAccUtils::MustPrune(aAccessible)) {
|
||||||
|
result |= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aAccessible->Role() == mRole) {
|
||||||
|
result |= nsIAccessibleTraversalRule::FILTER_MATCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
|
@ -83,6 +83,19 @@ class Pivot final {
|
||||||
Accessible* mRoot;
|
Accessible* mRoot;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This rule matches accessibles on a given role.
|
||||||
|
*/
|
||||||
|
class PivotRoleRule final : public PivotRule {
|
||||||
|
public:
|
||||||
|
PivotRoleRule(mozilla::a11y::role aRole);
|
||||||
|
|
||||||
|
virtual uint16_t Match(Accessible* aAccessible) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
mozilla::a11y::role mRole;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace a11y
|
} // namespace a11y
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "DocAccessibleChild.h"
|
#include "DocAccessibleChild.h"
|
||||||
#include "EventTree.h"
|
#include "EventTree.h"
|
||||||
#include "GeckoProfiler.h"
|
#include "GeckoProfiler.h"
|
||||||
|
#include "Pivot.h"
|
||||||
#include "Relation.h"
|
#include "Relation.h"
|
||||||
#include "Role.h"
|
#include "Role.h"
|
||||||
#include "RootAccessible.h"
|
#include "RootAccessible.h"
|
||||||
|
@ -1682,8 +1683,39 @@ Relation Accessible::RelationByType(RelationType aType) const {
|
||||||
return Relation(
|
return Relation(
|
||||||
new RelatedAccIterator(Document(), mContent, nsGkAtoms::aria_flowto));
|
new RelatedAccIterator(Document(), mContent, nsGkAtoms::aria_flowto));
|
||||||
|
|
||||||
case RelationType::MEMBER_OF:
|
case RelationType::MEMBER_OF: {
|
||||||
|
if (Role() == roles::RADIOBUTTON) {
|
||||||
|
/* If we see a radio button role here, we're dealing with an aria
|
||||||
|
* radio button (because input=radio buttons are
|
||||||
|
* HTMLRadioButtonAccessibles) */
|
||||||
|
Relation rel = Relation();
|
||||||
|
Accessible* currParent = Parent();
|
||||||
|
while (currParent && currParent->Role() != roles::RADIO_GROUP) {
|
||||||
|
currParent = currParent->Parent();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currParent->Role() == roles::RADIO_GROUP) {
|
||||||
|
/* If we found a radiogroup parent, search for all
|
||||||
|
* roles::RADIOBUTTON children and add them to our relation.
|
||||||
|
* This search will include the radio button this method
|
||||||
|
* was called from, which is expected. */
|
||||||
|
Pivot p = Pivot(currParent);
|
||||||
|
PivotRoleRule rule(roles::RADIOBUTTON);
|
||||||
|
Accessible* match = currParent;
|
||||||
|
while ((match = p.Next(match, rule))) {
|
||||||
|
rel.AppendTarget(match);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* By webkit's standard, aria radio buttons do not get grouped
|
||||||
|
* if they lack a group parent, so we return an empty
|
||||||
|
* relation here if the above check fails. */
|
||||||
|
|
||||||
|
return rel;
|
||||||
|
}
|
||||||
|
|
||||||
return Relation(mDoc, GetAtomicRegion());
|
return Relation(mDoc, GetAtomicRegion());
|
||||||
|
}
|
||||||
|
|
||||||
case RelationType::SUBWINDOW_OF:
|
case RelationType::SUBWINDOW_OF:
|
||||||
case RelationType::EMBEDS:
|
case RelationType::EMBEDS:
|
||||||
|
|
|
@ -70,6 +70,12 @@ uint64_t HTMLRadioButtonAccessible::NativeState() const {
|
||||||
|
|
||||||
void HTMLRadioButtonAccessible::GetPositionAndSizeInternal(int32_t* aPosInSet,
|
void HTMLRadioButtonAccessible::GetPositionAndSizeInternal(int32_t* aPosInSet,
|
||||||
int32_t* aSetSize) {
|
int32_t* aSetSize) {
|
||||||
|
Unused << ComputeGroupAttributes(aPosInSet, aSetSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
Relation HTMLRadioButtonAccessible::ComputeGroupAttributes(
|
||||||
|
int32_t* aPosInSet, int32_t* aSetSize) const {
|
||||||
|
Relation rel = Relation();
|
||||||
int32_t namespaceId = mContent->NodeInfo()->NamespaceID();
|
int32_t namespaceId = mContent->NodeInfo()->NamespaceID();
|
||||||
nsAutoString tagName;
|
nsAutoString tagName;
|
||||||
mContent->NodeInfo()->GetName(tagName);
|
mContent->NodeInfo()->GetName(tagName);
|
||||||
|
@ -87,7 +93,7 @@ void HTMLRadioButtonAccessible::GetPositionAndSizeInternal(int32_t* aPosInSet,
|
||||||
inputElms = NS_GetContentList(formElm, namespaceId, tagName);
|
inputElms = NS_GetContentList(formElm, namespaceId, tagName);
|
||||||
else
|
else
|
||||||
inputElms = NS_GetContentList(mContent->OwnerDoc(), namespaceId, tagName);
|
inputElms = NS_GetContentList(mContent->OwnerDoc(), namespaceId, tagName);
|
||||||
NS_ENSURE_TRUE_VOID(inputElms);
|
NS_ENSURE_TRUE(inputElms, rel);
|
||||||
|
|
||||||
uint32_t inputCount = inputElms->Length(false);
|
uint32_t inputCount = inputElms->Length(false);
|
||||||
|
|
||||||
|
@ -103,12 +109,23 @@ void HTMLRadioButtonAccessible::GetPositionAndSizeInternal(int32_t* aPosInSet,
|
||||||
name, eCaseMatters) &&
|
name, eCaseMatters) &&
|
||||||
mDoc->HasAccessible(inputElm)) {
|
mDoc->HasAccessible(inputElm)) {
|
||||||
count++;
|
count++;
|
||||||
|
rel.AppendTarget(mDoc->GetAccessible(inputElm));
|
||||||
if (inputElm == mContent) indexOf = count;
|
if (inputElm == mContent) indexOf = count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*aPosInSet = indexOf;
|
*aPosInSet = indexOf;
|
||||||
*aSetSize = count;
|
*aSetSize = count;
|
||||||
|
return rel;
|
||||||
|
}
|
||||||
|
|
||||||
|
Relation HTMLRadioButtonAccessible::RelationByType(RelationType aType) const {
|
||||||
|
if (aType == RelationType::MEMBER_OF) {
|
||||||
|
int32_t unusedPos, unusedSetSize;
|
||||||
|
return ComputeGroupAttributes(&unusedPos, &unusedSetSize);
|
||||||
|
} else {
|
||||||
|
return Accessible::RelationByType(aType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "FormControlAccessible.h"
|
#include "FormControlAccessible.h"
|
||||||
#include "HyperTextAccessibleWrap.h"
|
#include "HyperTextAccessibleWrap.h"
|
||||||
#include "nsAccUtils.h"
|
#include "nsAccUtils.h"
|
||||||
|
#include "Relation.h"
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
class TextEditor;
|
class TextEditor;
|
||||||
|
@ -30,6 +31,10 @@ class HTMLRadioButtonAccessible : public RadioButtonAccessible {
|
||||||
virtual uint64_t NativeState() const override;
|
virtual uint64_t NativeState() const override;
|
||||||
virtual void GetPositionAndSizeInternal(int32_t* aPosInSet,
|
virtual void GetPositionAndSizeInternal(int32_t* aPosInSet,
|
||||||
int32_t* aSetSize) override;
|
int32_t* aSetSize) override;
|
||||||
|
virtual Relation RelationByType(RelationType aType) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Relation ComputeGroupAttributes(int32_t* aPosInSet, int32_t* aSetSize) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -182,9 +182,11 @@ Class a11y::GetTypeFromRole(roles::Role aRole) {
|
||||||
|
|
||||||
case roles::CHECKBUTTON:
|
case roles::CHECKBUTTON:
|
||||||
case roles::TOGGLE_BUTTON:
|
case roles::TOGGLE_BUTTON:
|
||||||
case roles::RADIOBUTTON:
|
|
||||||
return [mozCheckboxAccessible class];
|
return [mozCheckboxAccessible class];
|
||||||
|
|
||||||
|
case roles::RADIOBUTTON:
|
||||||
|
return [mozRadioButtonAccessible class];
|
||||||
|
|
||||||
case roles::SPINBUTTON:
|
case roles::SPINBUTTON:
|
||||||
case roles::SLIDER:
|
case roles::SLIDER:
|
||||||
return [mozIncrementableAccessible class];
|
return [mozIncrementableAccessible class];
|
||||||
|
|
|
@ -21,6 +21,12 @@
|
||||||
- (int)isChecked;
|
- (int)isChecked;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
// Accessible for a radio button
|
||||||
|
@interface mozRadioButtonAccessible : mozCheckboxAccessible
|
||||||
|
- (id)accessibilityAttributeValue:(NSString*)attribute;
|
||||||
|
- (NSUInteger)accessibilityArrayAttributeCount:(NSString*)attribute;
|
||||||
|
@end
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Accessible for a PANE
|
* Accessible for a PANE
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "Accessible-inl.h"
|
#include "Accessible-inl.h"
|
||||||
#include "DocAccessible.h"
|
#include "DocAccessible.h"
|
||||||
#include "XULTabAccessible.h"
|
#include "XULTabAccessible.h"
|
||||||
|
#include "HTMLFormControlAccessible.h"
|
||||||
|
|
||||||
#include "nsDeckFrame.h"
|
#include "nsDeckFrame.h"
|
||||||
#include "nsObjCExceptions.h"
|
#include "nsObjCExceptions.h"
|
||||||
|
@ -126,7 +127,6 @@ enum CheckboxValue {
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)stateChanged:(uint64_t)state isEnabled:(BOOL)enabled {
|
- (void)stateChanged:(uint64_t)state isEnabled:(BOOL)enabled {
|
||||||
|
|
||||||
[super stateChanged:state isEnabled:enabled];
|
[super stateChanged:state isEnabled:enabled];
|
||||||
|
|
||||||
if (state == states::EXPANDED) {
|
if (state == states::EXPANDED) {
|
||||||
|
@ -155,6 +155,36 @@ enum CheckboxValue {
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@implementation mozRadioButtonAccessible
|
||||||
|
|
||||||
|
- (id)accessibilityAttributeValue:(NSString*)attribute {
|
||||||
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
||||||
|
|
||||||
|
if ([attribute isEqualToString:NSAccessibilityLinkedUIElementsAttribute]) {
|
||||||
|
if (AccessibleWrap* accWrap = [self getGeckoAccessible]) {
|
||||||
|
NSMutableArray* radioSiblings = [NSMutableArray new];
|
||||||
|
HTMLRadioButtonAccessible* radioAcc = (HTMLRadioButtonAccessible*)accWrap;
|
||||||
|
Relation rel = radioAcc->RelationByType(RelationType::MEMBER_OF);
|
||||||
|
Accessible* tempAcc;
|
||||||
|
while ((tempAcc = rel.Next())) {
|
||||||
|
[radioSiblings addObject:GetNativeFromGeckoAccessible(tempAcc)];
|
||||||
|
}
|
||||||
|
return radioSiblings;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ProxyAccessible* proxy = [self getProxyAccessible]) {
|
||||||
|
nsTArray<ProxyAccessible*> accs = proxy->RelationByType(RelationType::MEMBER_OF);
|
||||||
|
return ConvertToNSArray(accs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [super accessibilityAttributeValue:attribute];
|
||||||
|
|
||||||
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
@implementation mozCheckboxAccessible
|
@implementation mozCheckboxAccessible
|
||||||
|
|
||||||
- (int)isChecked {
|
- (int)isChecked {
|
||||||
|
|
|
@ -17,6 +17,7 @@ support-files =
|
||||||
[browser_roles_elements.js]
|
[browser_roles_elements.js]
|
||||||
[browser_table.js]
|
[browser_table.js]
|
||||||
[browser_selectables.js]
|
[browser_selectables.js]
|
||||||
|
[browser_radio_position.js]
|
||||||
[browser_toggle_radio_check.js]
|
[browser_toggle_radio_check.js]
|
||||||
[browser_link.js]
|
[browser_link.js]
|
||||||
[browser_aria_haspopup.js]
|
[browser_aria_haspopup.js]
|
||||||
|
|
|
@ -0,0 +1,321 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
/* import-globals-from ../../mochitest/role.js */
|
||||||
|
/* import-globals-from ../../mochitest/states.js */
|
||||||
|
loadScripts(
|
||||||
|
{ name: "role.js", dir: MOCHITESTS_DIR },
|
||||||
|
{ name: "states.js", dir: MOCHITESTS_DIR }
|
||||||
|
);
|
||||||
|
|
||||||
|
function getChildRoles(parent) {
|
||||||
|
return parent
|
||||||
|
.getAttributeValue("AXChildren")
|
||||||
|
.map(c => c.getAttributeValue("AXRole"));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLinkedTitles(element) {
|
||||||
|
return element
|
||||||
|
.getAttributeValue("AXLinkedUIElements")
|
||||||
|
.map(c => c.getAttributeValue("AXTitle"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test radio group
|
||||||
|
*/
|
||||||
|
addAccessibleTask(
|
||||||
|
`<div role="radiogroup" id="radioGroup">
|
||||||
|
<div role="radio"
|
||||||
|
id="radioGroupItem1">
|
||||||
|
Regular crust
|
||||||
|
</div>
|
||||||
|
<div role="radio"
|
||||||
|
id="radioGroupItem2">
|
||||||
|
Deep dish
|
||||||
|
</div>
|
||||||
|
<div role="radio"
|
||||||
|
id="radioGroupItem3">
|
||||||
|
Thin crust
|
||||||
|
</div>
|
||||||
|
</div>`,
|
||||||
|
async (browser, accDoc) => {
|
||||||
|
let item1 = getNativeInterface(accDoc, "radioGroupItem1");
|
||||||
|
let item2 = getNativeInterface(accDoc, "radioGroupItem2");
|
||||||
|
let item3 = getNativeInterface(accDoc, "radioGroupItem3");
|
||||||
|
let titleList = ["Regular crust", "Deep dish", "Thin crust"];
|
||||||
|
|
||||||
|
Assert.deepEqual(
|
||||||
|
titleList,
|
||||||
|
[item1, item2, item3].map(c => c.getAttributeValue("AXTitle")),
|
||||||
|
"Title list matches"
|
||||||
|
);
|
||||||
|
|
||||||
|
let linkedElems = item1.getAttributeValue("AXLinkedUIElements");
|
||||||
|
is(linkedElems.length, 3, "Item 1 has three linked UI elems");
|
||||||
|
Assert.deepEqual(
|
||||||
|
getLinkedTitles(item1),
|
||||||
|
titleList,
|
||||||
|
"Item one has correctly ordered linked elements"
|
||||||
|
);
|
||||||
|
|
||||||
|
linkedElems = item2.getAttributeValue("AXLinkedUIElements");
|
||||||
|
is(linkedElems.length, 3, "Item 2 has three linked UI elems");
|
||||||
|
Assert.deepEqual(
|
||||||
|
getLinkedTitles(item2),
|
||||||
|
titleList,
|
||||||
|
"Item two has correctly ordered linked elements"
|
||||||
|
);
|
||||||
|
|
||||||
|
linkedElems = item3.getAttributeValue("AXLinkedUIElements");
|
||||||
|
is(linkedElems.length, 3, "Item 3 has three linked UI elems");
|
||||||
|
Assert.deepEqual(
|
||||||
|
getLinkedTitles(item3),
|
||||||
|
titleList,
|
||||||
|
"Item three has correctly ordered linked elements"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test dynamic add to a radio group
|
||||||
|
*/
|
||||||
|
addAccessibleTask(
|
||||||
|
`<div role="radiogroup" id="radioGroup">
|
||||||
|
<div role="radio"
|
||||||
|
id="radioGroupItem1">
|
||||||
|
Option One
|
||||||
|
</div>
|
||||||
|
</div>`,
|
||||||
|
async (browser, accDoc) => {
|
||||||
|
let item1 = getNativeInterface(accDoc, "radioGroupItem1");
|
||||||
|
let linkedElems = item1.getAttributeValue("AXLinkedUIElements");
|
||||||
|
|
||||||
|
is(linkedElems.length, 1, "Item 1 has one linked UI elem");
|
||||||
|
is(
|
||||||
|
linkedElems[0].getAttributeValue("AXTitle"),
|
||||||
|
item1.getAttributeValue("AXTitle"),
|
||||||
|
"Item 1 is first element"
|
||||||
|
);
|
||||||
|
|
||||||
|
let reorder = waitForEvent(EVENT_REORDER, "radioGroup");
|
||||||
|
await SpecialPowers.spawn(browser, [], () => {
|
||||||
|
let d = content.document.createElement("div");
|
||||||
|
d.setAttribute("role", "radio");
|
||||||
|
content.document.getElementById("radioGroup").appendChild(d);
|
||||||
|
});
|
||||||
|
await reorder;
|
||||||
|
|
||||||
|
let radioGroup = getNativeInterface(accDoc, "radioGroup");
|
||||||
|
let groupMembers = radioGroup.getAttributeValue("AXChildren");
|
||||||
|
is(groupMembers.length, 2, "Radio group has two members");
|
||||||
|
let item2 = groupMembers[1];
|
||||||
|
item1 = getNativeInterface(accDoc, "radioGroupItem1");
|
||||||
|
let titleList = ["Option One", ""];
|
||||||
|
|
||||||
|
Assert.deepEqual(
|
||||||
|
titleList,
|
||||||
|
[item1, item2].map(c => c.getAttributeValue("AXTitle")),
|
||||||
|
"Title list matches"
|
||||||
|
);
|
||||||
|
|
||||||
|
linkedElems = item1.getAttributeValue("AXLinkedUIElements");
|
||||||
|
is(linkedElems.length, 2, "Item 1 has two linked UI elems");
|
||||||
|
Assert.deepEqual(
|
||||||
|
getLinkedTitles(item1),
|
||||||
|
titleList,
|
||||||
|
"Item one has correctly ordered linked elements"
|
||||||
|
);
|
||||||
|
|
||||||
|
linkedElems = item2.getAttributeValue("AXLinkedUIElements");
|
||||||
|
is(linkedElems.length, 2, "Item 2 has two linked UI elems");
|
||||||
|
Assert.deepEqual(
|
||||||
|
getLinkedTitles(item2),
|
||||||
|
titleList,
|
||||||
|
"Item two has correctly ordered linked elements"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test input[type=radio] for single group
|
||||||
|
*/
|
||||||
|
addAccessibleTask(
|
||||||
|
`<input type="radio" id="cat" name="animal"><label for="cat">Cat</label>
|
||||||
|
<input type="radio" id="dog" name="animal"><label for="dog">Dog</label>
|
||||||
|
<input type="radio" id="catdog" name="animal"><label for="catdog">CatDog</label>`,
|
||||||
|
async (browser, accDoc) => {
|
||||||
|
let cat = getNativeInterface(accDoc, "cat");
|
||||||
|
let dog = getNativeInterface(accDoc, "dog");
|
||||||
|
let catdog = getNativeInterface(accDoc, "catdog");
|
||||||
|
let titleList = ["Cat", "Dog", "CatDog"];
|
||||||
|
|
||||||
|
Assert.deepEqual(
|
||||||
|
titleList,
|
||||||
|
[cat, dog, catdog].map(x => x.getAttributeValue("AXTitle")),
|
||||||
|
"Title list matches"
|
||||||
|
);
|
||||||
|
|
||||||
|
let linkedElems = cat.getAttributeValue("AXLinkedUIElements");
|
||||||
|
is(linkedElems.length, 3, "Cat has three linked UI elems");
|
||||||
|
Assert.deepEqual(
|
||||||
|
getLinkedTitles(cat),
|
||||||
|
titleList,
|
||||||
|
"Cat has correctly ordered linked elements"
|
||||||
|
);
|
||||||
|
|
||||||
|
linkedElems = dog.getAttributeValue("AXLinkedUIElements");
|
||||||
|
is(linkedElems.length, 3, "Dog has three linked UI elems");
|
||||||
|
Assert.deepEqual(
|
||||||
|
getLinkedTitles(dog),
|
||||||
|
titleList,
|
||||||
|
"Dog has correctly ordered linked elements"
|
||||||
|
);
|
||||||
|
|
||||||
|
linkedElems = catdog.getAttributeValue("AXLinkedUIElements");
|
||||||
|
is(linkedElems.length, 3, "Catdog has three linked UI elems");
|
||||||
|
Assert.deepEqual(
|
||||||
|
getLinkedTitles(catdog),
|
||||||
|
titleList,
|
||||||
|
"catdog has correctly ordered linked elements"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test input[type=radio] for different groups
|
||||||
|
*/
|
||||||
|
addAccessibleTask(
|
||||||
|
`<input type="radio" id="cat" name="one"><label for="cat">Cat</label>
|
||||||
|
<input type="radio" id="dog" name="two"><label for="dog">Dog</label>
|
||||||
|
<input type="radio" id="catdog"><label for="catdog">CatDog</label>`,
|
||||||
|
async (browser, accDoc) => {
|
||||||
|
let cat = getNativeInterface(accDoc, "cat");
|
||||||
|
let dog = getNativeInterface(accDoc, "dog");
|
||||||
|
let catdog = getNativeInterface(accDoc, "catdog");
|
||||||
|
|
||||||
|
let linkedElems = cat.getAttributeValue("AXLinkedUIElements");
|
||||||
|
is(linkedElems.length, 1, "Cat has one linked UI elem");
|
||||||
|
is(
|
||||||
|
linkedElems[0].getAttributeValue("AXTitle"),
|
||||||
|
cat.getAttributeValue("AXTitle"),
|
||||||
|
"Cat is only element"
|
||||||
|
);
|
||||||
|
|
||||||
|
linkedElems = dog.getAttributeValue("AXLinkedUIElements");
|
||||||
|
is(linkedElems.length, 1, "Dog has one linked UI elem");
|
||||||
|
is(
|
||||||
|
linkedElems[0].getAttributeValue("AXTitle"),
|
||||||
|
dog.getAttributeValue("AXTitle"),
|
||||||
|
"Dog is only element"
|
||||||
|
);
|
||||||
|
|
||||||
|
linkedElems = catdog.getAttributeValue("AXLinkedUIElements");
|
||||||
|
is(linkedElems.length, 0, "Catdog has no linked UI elem");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test input[type=radio] for single group across DOM
|
||||||
|
*/
|
||||||
|
addAccessibleTask(
|
||||||
|
`<input type="radio" id="cat" name="animal"><label for="cat">Cat</label>
|
||||||
|
<div>
|
||||||
|
<span>
|
||||||
|
<input type="radio" id="dog" name="animal"><label for="dog">Dog</label>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input type="radio" id="catdog" name="animal"><label for="catdog">CatDog</label>
|
||||||
|
</div>`,
|
||||||
|
async (browser, accDoc) => {
|
||||||
|
let cat = getNativeInterface(accDoc, "cat");
|
||||||
|
let dog = getNativeInterface(accDoc, "dog");
|
||||||
|
let catdog = getNativeInterface(accDoc, "catdog");
|
||||||
|
let titleList = ["Cat", "Dog", "CatDog"];
|
||||||
|
|
||||||
|
Assert.deepEqual(
|
||||||
|
titleList,
|
||||||
|
[cat, dog, catdog].map(x => x.getAttributeValue("AXTitle")),
|
||||||
|
"Title list matches"
|
||||||
|
);
|
||||||
|
|
||||||
|
let linkedElems = cat.getAttributeValue("AXLinkedUIElements");
|
||||||
|
is(linkedElems.length, 3, "Cat has three linked UI elems");
|
||||||
|
Assert.deepEqual(
|
||||||
|
getLinkedTitles(cat),
|
||||||
|
titleList,
|
||||||
|
"cat has correctly ordered linked elements"
|
||||||
|
);
|
||||||
|
|
||||||
|
linkedElems = dog.getAttributeValue("AXLinkedUIElements");
|
||||||
|
is(linkedElems.length, 3, "Dog has three linked UI elems");
|
||||||
|
Assert.deepEqual(
|
||||||
|
getLinkedTitles(dog),
|
||||||
|
titleList,
|
||||||
|
"dog has correctly ordered linked elements"
|
||||||
|
);
|
||||||
|
|
||||||
|
linkedElems = catdog.getAttributeValue("AXLinkedUIElements");
|
||||||
|
is(linkedElems.length, 3, "Catdog has three linked UI elems");
|
||||||
|
Assert.deepEqual(
|
||||||
|
getLinkedTitles(catdog),
|
||||||
|
titleList,
|
||||||
|
"catdog has correctly ordered linked elements"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test dynamic add of input[type=radio] in a single group
|
||||||
|
*/
|
||||||
|
addAccessibleTask(
|
||||||
|
`<div id="container"><input type="radio" id="cat" name="animal"></div>`,
|
||||||
|
async (browser, accDoc) => {
|
||||||
|
let cat = getNativeInterface(accDoc, "cat");
|
||||||
|
let container = getNativeInterface(accDoc, "container");
|
||||||
|
|
||||||
|
let containerChildren = container.getAttributeValue("AXChildren");
|
||||||
|
is(containerChildren.length, 1, "container has one button");
|
||||||
|
is(
|
||||||
|
containerChildren[0].getAttributeValue("AXRole"),
|
||||||
|
"AXRadioButton",
|
||||||
|
"Container child is radio button"
|
||||||
|
);
|
||||||
|
|
||||||
|
let linkedElems = cat.getAttributeValue("AXLinkedUIElements");
|
||||||
|
is(linkedElems.length, 1, "Cat has 1 linked UI elem");
|
||||||
|
is(
|
||||||
|
linkedElems[0].getAttributeValue("AXTitle"),
|
||||||
|
cat.getAttributeValue("AXTitle"),
|
||||||
|
"Cat is first element"
|
||||||
|
);
|
||||||
|
let reorder = waitForEvent(EVENT_REORDER, "container");
|
||||||
|
await SpecialPowers.spawn(browser, [], () => {
|
||||||
|
let input = content.document.createElement("input");
|
||||||
|
input.setAttribute("type", "radio");
|
||||||
|
input.setAttribute("name", "animal");
|
||||||
|
content.document.getElementById("container").appendChild(input);
|
||||||
|
});
|
||||||
|
await reorder;
|
||||||
|
|
||||||
|
container = getNativeInterface(accDoc, "container");
|
||||||
|
containerChildren = container.getAttributeValue("AXChildren");
|
||||||
|
|
||||||
|
is(containerChildren.length, 2, "container has two children");
|
||||||
|
|
||||||
|
Assert.deepEqual(
|
||||||
|
getChildRoles(container),
|
||||||
|
["AXRadioButton", "AXRadioButton"],
|
||||||
|
"Both children are radio buttons"
|
||||||
|
);
|
||||||
|
|
||||||
|
linkedElems = containerChildren[0].getAttributeValue("AXLinkedUIElements");
|
||||||
|
is(linkedElems.length, 2, "Cat has 2 linked elements");
|
||||||
|
|
||||||
|
linkedElems = containerChildren[1].getAttributeValue("AXLinkedUIElements");
|
||||||
|
is(linkedElems.length, 2, "New button has 2 linked elements");
|
||||||
|
}
|
||||||
|
);
|
Загрузка…
Ссылка в новой задаче