mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 804461, part2 - use context to build table trees, r=tbsaunde
This commit is contained in:
parent
5501032117
commit
d0638a4613
@ -87,7 +87,8 @@ TreeWalker::NextChildInternal(bool aNoWalkUp)
|
||||
|
||||
bool isSubtreeHidden = false;
|
||||
Accessible* accessible = mWalkCache ? mDoc->GetAccessible(childNode) :
|
||||
GetAccService()->GetOrCreateAccessible(childNode, mDoc, &isSubtreeHidden);
|
||||
GetAccService()->GetOrCreateAccessible(childNode, mContext,
|
||||
&isSubtreeHidden);
|
||||
|
||||
if (accessible)
|
||||
return accessible;
|
||||
|
@ -110,7 +110,7 @@ static nsRoleMapEntry sWAIRoleMaps[] =
|
||||
eNoValue,
|
||||
eSortAction,
|
||||
eNoLiveAttr,
|
||||
kGenericAccType,
|
||||
Accessible::eTableCellAccessible,
|
||||
kNoReqStates,
|
||||
eARIASelectable,
|
||||
eARIAReadonly
|
||||
@ -175,7 +175,7 @@ static nsRoleMapEntry sWAIRoleMaps[] =
|
||||
eNoValue,
|
||||
eNoAction,
|
||||
eNoLiveAttr,
|
||||
Accessible::eSelectAccessible,
|
||||
Accessible::eSelectAccessible | Accessible::eTableAccessible,
|
||||
states::FOCUSABLE,
|
||||
eARIAMultiSelectable,
|
||||
eARIAReadonly
|
||||
@ -187,7 +187,7 @@ static nsRoleMapEntry sWAIRoleMaps[] =
|
||||
eNoValue,
|
||||
eNoAction,
|
||||
eNoLiveAttr,
|
||||
kGenericAccType,
|
||||
Accessible::eTableCellAccessible,
|
||||
kNoReqStates,
|
||||
eARIASelectable,
|
||||
eARIAReadonly
|
||||
@ -429,7 +429,7 @@ static nsRoleMapEntry sWAIRoleMaps[] =
|
||||
eNoValue,
|
||||
eNoAction,
|
||||
eNoLiveAttr,
|
||||
kGenericAccType,
|
||||
Accessible::eTableRowAccessible,
|
||||
kNoReqStates,
|
||||
eARIASelectable
|
||||
},
|
||||
@ -450,7 +450,7 @@ static nsRoleMapEntry sWAIRoleMaps[] =
|
||||
eNoValue,
|
||||
eSortAction,
|
||||
eNoLiveAttr,
|
||||
kGenericAccType,
|
||||
Accessible::eTableCellAccessible,
|
||||
kNoReqStates,
|
||||
eARIASelectable,
|
||||
eARIAReadonly
|
||||
@ -603,7 +603,7 @@ static nsRoleMapEntry sWAIRoleMaps[] =
|
||||
eNoValue,
|
||||
eNoAction,
|
||||
eNoLiveAttr,
|
||||
Accessible::eSelectAccessible,
|
||||
Accessible::eSelectAccessible | Accessible::eTableAccessible,
|
||||
kNoReqStates,
|
||||
eARIAReadonly,
|
||||
eARIAMultiSelectable
|
||||
|
@ -208,7 +208,7 @@ nsAccessibilityService::GetRootDocumentAccessible(nsIPresShell* aPresShell,
|
||||
already_AddRefed<Accessible>
|
||||
nsAccessibilityService::CreateHTMLObjectFrameAccessible(nsObjectFrame* aFrame,
|
||||
nsIContent* aContent,
|
||||
DocAccessible* aDoc)
|
||||
Accessible* aContext)
|
||||
{
|
||||
// We can have several cases here:
|
||||
// 1) a text or html embedded document where the contentDocument variable in
|
||||
@ -227,7 +227,8 @@ nsAccessibilityService::CreateHTMLObjectFrameAccessible(nsObjectFrame* aFrame,
|
||||
nsCOMPtr<nsIDOMDocument> domDoc;
|
||||
obj->GetContentDocument(getter_AddRefs(domDoc));
|
||||
if (domDoc) {
|
||||
Accessible* newAcc = new OuterDocAccessible(aContent, aDoc);
|
||||
Accessible* newAcc =
|
||||
new OuterDocAccessible(aContent, aContext->Document());
|
||||
NS_ADDREF(newAcc);
|
||||
return newAcc;
|
||||
}
|
||||
@ -244,7 +245,8 @@ nsAccessibilityService::CreateHTMLObjectFrameAccessible(nsObjectFrame* aFrame,
|
||||
aFrame->GetPluginPort(&pluginPort);
|
||||
|
||||
Accessible* accessible =
|
||||
new HTMLWin32ObjectOwnerAccessible(aContent, aDoc, pluginPort);
|
||||
new HTMLWin32ObjectOwnerAccessible(aContent, aContext->Document(),
|
||||
pluginPort);
|
||||
NS_ADDREF(accessible);
|
||||
return accessible;
|
||||
|
||||
@ -257,7 +259,7 @@ nsAccessibilityService::CreateHTMLObjectFrameAccessible(nsObjectFrame* aFrame,
|
||||
NPPVpluginNativeAccessibleAtkPlugId, &plugId);
|
||||
if (NS_SUCCEEDED(rv) && !plugId.IsEmpty()) {
|
||||
AtkSocketAccessible* socketAccessible =
|
||||
new AtkSocketAccessible(aContent, aDoc, plugId);
|
||||
new AtkSocketAccessible(aContent, aContext->Document(), plugId);
|
||||
|
||||
NS_ADDREF(socketAccessible);
|
||||
return socketAccessible;
|
||||
@ -269,8 +271,8 @@ nsAccessibilityService::CreateHTMLObjectFrameAccessible(nsObjectFrame* aFrame,
|
||||
// 3) for images and imagemaps, or anything else with a child frame
|
||||
// we have the object frame, get the image frame
|
||||
nsIFrame* childFrame = aFrame->GetFirstPrincipalChild();
|
||||
return childFrame ? CreateAccessibleByFrameType(childFrame, aContent, aDoc) :
|
||||
nullptr;
|
||||
return childFrame ?
|
||||
CreateAccessibleByFrameType(childFrame, aContent, aContext) : nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
@ -678,17 +680,21 @@ nsAccessibilityService::IsLogged(const nsAString& aModule, bool* aIsLogged)
|
||||
|
||||
Accessible*
|
||||
nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode,
|
||||
DocAccessible* aDoc,
|
||||
Accessible* aContext,
|
||||
bool* aIsSubtreeHidden)
|
||||
{
|
||||
if (!aDoc || !aNode || gIsShutdown)
|
||||
return nullptr;
|
||||
NS_PRECONDITION(aContext && aNode && !gIsShutdown,
|
||||
"Maybe let'd do a crash? Oh, yes, baby!");
|
||||
|
||||
if (aIsSubtreeHidden)
|
||||
*aIsSubtreeHidden = false;
|
||||
|
||||
DocAccessible* document = aContext->Document();
|
||||
|
||||
// Check to see if we already have an accessible for this node in the cache.
|
||||
Accessible* cachedAccessible = aDoc->GetAccessible(aNode);
|
||||
// XXX: we don't have context check here. It doesn't really necessary until
|
||||
// we have in-law children adoption.
|
||||
Accessible* cachedAccessible = document->GetAccessible(aNode);
|
||||
if (cachedAccessible)
|
||||
return cachedAccessible;
|
||||
|
||||
@ -707,7 +713,7 @@ nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (aNode->OwnerDoc() != aDoc->DocumentNode()) {
|
||||
if (aNode->OwnerDoc() != document->DocumentNode()) {
|
||||
NS_ERROR("Creating accessible for wrong document");
|
||||
return nullptr;
|
||||
}
|
||||
@ -763,8 +769,8 @@ nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
newAcc = CreateAccessibleByFrameType(frame, content, aDoc);
|
||||
if (aDoc->BindToDocument(newAcc, nullptr)) {
|
||||
newAcc = CreateAccessibleByFrameType(frame, content, aContext);
|
||||
if (document->BindToDocument(newAcc, nullptr)) {
|
||||
newAcc->AsTextLeaf()->SetText(text);
|
||||
return newAcc;
|
||||
}
|
||||
@ -790,8 +796,8 @@ nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
newAcc = new HyperTextAccessibleWrap(content, aDoc);
|
||||
if (aDoc->BindToDocument(newAcc, aria::GetRoleMap(aNode)))
|
||||
newAcc = new HyperTextAccessibleWrap(content, document);
|
||||
if (document->BindToDocument(newAcc, aria::GetRoleMap(aNode)))
|
||||
return newAcc;
|
||||
return nullptr;
|
||||
}
|
||||
@ -802,115 +808,59 @@ nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode,
|
||||
// it is referenced by ARIA relationship then treat role="presentation" on
|
||||
// the element as the role is not there.
|
||||
if (roleMapEntry && roleMapEntry->Is(nsGkAtoms::presentation)) {
|
||||
if (!MustBeAccessible(content, aDoc))
|
||||
if (!MustBeAccessible(content, document))
|
||||
return nullptr;
|
||||
|
||||
roleMapEntry = nullptr;
|
||||
}
|
||||
|
||||
if (!newAcc && isHTML) { // HTML accessibles
|
||||
nsIAtom* frameType = frame->GetType();
|
||||
|
||||
bool partOfHTMLTable =
|
||||
frameType == nsGkAtoms::tableCaptionFrame ||
|
||||
frameType == nsGkAtoms::tableCellFrame ||
|
||||
frameType == nsGkAtoms::tableRowGroupFrame ||
|
||||
frameType == nsGkAtoms::tableRowFrame;
|
||||
bool legalPartOfHTMLTable = partOfHTMLTable;
|
||||
|
||||
if (partOfHTMLTable) {
|
||||
// Table-related frames don't get table-related roles
|
||||
// unless they are inside a table, but they may still get generic
|
||||
// accessibles
|
||||
nsIContent *tableContent = content;
|
||||
while ((tableContent = tableContent->GetParent()) != nullptr) {
|
||||
nsIFrame *tableFrame = tableContent->GetPrimaryFrame();
|
||||
if (!tableFrame)
|
||||
continue;
|
||||
|
||||
if (tableFrame->GetType() == nsGkAtoms::tableOuterFrame) {
|
||||
Accessible* tableAccessible = aDoc->GetAccessible(tableContent);
|
||||
|
||||
if (tableAccessible) {
|
||||
if (!roleMapEntry) {
|
||||
roles::Role role = tableAccessible->Role();
|
||||
// No ARIA role and not in table: override role. For example,
|
||||
// <table role="label"><td>content</td></table>
|
||||
if (role != roles::TABLE && role != roles::TREE_TABLE)
|
||||
roleMapEntry = &nsARIAMap::gEmptyRoleMap;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
nsRoleMapEntry* tableRoleMapEntry = aria::GetRoleMap(tableContent);
|
||||
NS_ASSERTION(tableRoleMapEntry && tableRoleMapEntry->Is(nsGkAtoms::presentation),
|
||||
"No accessible for parent table and it didn't have role of presentation");
|
||||
#endif
|
||||
|
||||
if (!roleMapEntry && !MustBeAccessible(content, aDoc)) {
|
||||
// Table-related descendants of presentation table are also
|
||||
// presentation if they aren't focusable and have not explicit ARIA
|
||||
// role (don't create accessibles for them unless they need to fire
|
||||
// focus events).
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// otherwise create ARIA based accessible.
|
||||
legalPartOfHTMLTable = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (tableContent->Tag() == nsGkAtoms::table) {
|
||||
// Stop before we are fooled by any additional table ancestors
|
||||
// This table cell frameis part of a separate ancestor table.
|
||||
legalPartOfHTMLTable = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!tableContent)
|
||||
legalPartOfHTMLTable = false;
|
||||
}
|
||||
|
||||
if (roleMapEntry) {
|
||||
// Create ARIA grid/treegrid accessibles if node is not a child or legal
|
||||
// child of HTML table and is not a HTML table.
|
||||
if ((!partOfHTMLTable || !legalPartOfHTMLTable) &&
|
||||
frameType != nsGkAtoms::tableOuterFrame) {
|
||||
|
||||
if (roleMapEntry->role == roles::TABLE ||
|
||||
roleMapEntry->role == roles::TREE_TABLE) {
|
||||
newAcc = new ARIAGridAccessibleWrap(content, aDoc);
|
||||
|
||||
} else if (roleMapEntry->role == roles::GRID_CELL ||
|
||||
roleMapEntry->role == roles::ROWHEADER ||
|
||||
roleMapEntry->role == roles::COLUMNHEADER) {
|
||||
newAcc = new ARIAGridCellAccessibleWrap(content, aDoc);
|
||||
// Create pure ARIA grid/treegrid related accessibles if they weren't used
|
||||
// on accessible HTML table elements.
|
||||
if ((roleMapEntry->accTypes & Accessible::eTableCellAccessible)) {
|
||||
if (aContext->IsOfType(Accessible::eTableRowAccessible) &&
|
||||
(frame->AccessibleType() != eHTMLTableCellAccessible ||
|
||||
aContext->GetContent() != content->GetParent())) {
|
||||
newAcc = new ARIAGridCellAccessibleWrap(content, document);
|
||||
}
|
||||
|
||||
} else if ((roleMapEntry->accTypes & Accessible::eTableAccessible) &&
|
||||
frame->AccessibleType() != eHTMLTableAccessible) {
|
||||
newAcc = new ARIAGridAccessibleWrap(content, document);
|
||||
}
|
||||
}
|
||||
|
||||
if (!newAcc) {
|
||||
// Prefer to use markup (mostly tag name, perhaps attributes) to
|
||||
// decide if and what kind of accessible to create.
|
||||
// The method creates accessibles for table related content too therefore
|
||||
// we do not call it if accessibles for table related content are
|
||||
// prevented above.
|
||||
newAcc = CreateHTMLAccessibleByMarkup(frame, content, aDoc,
|
||||
legalPartOfHTMLTable);
|
||||
// Prefer to use markup (mostly tag name, perhaps attributes) to decide if
|
||||
// and what kind of accessible to create.
|
||||
newAcc = CreateHTMLAccessibleByMarkup(frame, content, aContext);
|
||||
|
||||
// Try using frame to do it.
|
||||
if (!newAcc && (!partOfHTMLTable || legalPartOfHTMLTable))
|
||||
newAcc = CreateAccessibleByFrameType(frame, content, aDoc);
|
||||
if (!newAcc)
|
||||
newAcc = CreateAccessibleByFrameType(frame, content, aContext);
|
||||
|
||||
// If table has strong ARIA role then all table descendants shouldn't
|
||||
// expose their native roles.
|
||||
if (!roleMapEntry && newAcc) {
|
||||
if (frame->AccessibleType() == eHTMLTableRowAccessible) {
|
||||
nsRoleMapEntry* contextRoleMap = aContext->ARIARoleMap();
|
||||
if (contextRoleMap &&
|
||||
!(contextRoleMap->accTypes & Accessible::eTableAccessible))
|
||||
roleMapEntry = &nsARIAMap::gEmptyRoleMap;
|
||||
|
||||
} else if (frame->AccessibleType() == eHTMLTableCellAccessible &&
|
||||
aContext->ARIARoleMap() == &nsARIAMap::gEmptyRoleMap) {
|
||||
roleMapEntry = &nsARIAMap::gEmptyRoleMap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!newAcc) {
|
||||
// Elements may implement nsIAccessibleProvider via XBL. This allows them to
|
||||
// say what kind of accessible to create.
|
||||
newAcc = CreateAccessibleByType(content, aDoc);
|
||||
newAcc = CreateAccessibleByType(content, document);
|
||||
}
|
||||
|
||||
if (!newAcc) {
|
||||
@ -918,37 +868,37 @@ nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode,
|
||||
// on HTML elements
|
||||
nsIAtom* tag = content->Tag();
|
||||
if ((tag == nsGkAtoms::deck) || (tag == nsGkAtoms::tabpanels)) {
|
||||
newAcc = new XULDeckAccessible(content, aDoc);
|
||||
newAcc = new XULDeckAccessible(content, document);
|
||||
} else if (content->IsSVG(nsGkAtoms::svg)) {
|
||||
newAcc = new EnumRoleAccessible(content, aDoc, roles::DIAGRAM);
|
||||
newAcc = new EnumRoleAccessible(content, document, roles::DIAGRAM);
|
||||
} else if (content->IsMathML(nsGkAtoms::math)) {
|
||||
newAcc = new EnumRoleAccessible(content, aDoc, roles::EQUATION);
|
||||
newAcc = new EnumRoleAccessible(content, document, roles::EQUATION);
|
||||
}
|
||||
}
|
||||
|
||||
if (!newAcc)
|
||||
newAcc = CreateAccessibleForDeckChild(frame, content, aDoc);
|
||||
newAcc = CreateAccessibleForDeckChild(frame, content, document);
|
||||
|
||||
// If no accessible, see if we need to create a generic accessible because
|
||||
// of some property that makes this object interesting
|
||||
// We don't do this for <body>, <html>, <window>, <dialog> etc. which
|
||||
// correspond to the doc accessible and will be created in any case
|
||||
if (!newAcc && content->Tag() != nsGkAtoms::body && content->GetParent() &&
|
||||
(roleMapEntry || MustBeAccessible(content, aDoc) ||
|
||||
(roleMapEntry || MustBeAccessible(content, document) ||
|
||||
(isHTML && nsCoreUtils::HasClickListener(content)))) {
|
||||
// This content is focusable or has an interesting dynamic content accessibility property.
|
||||
// If it's interesting we need it in the accessibility hierarchy so that events or
|
||||
// other accessibles can point to it, or so that it can hold a state, etc.
|
||||
if (isHTML) {
|
||||
// Interesting HTML container which may have selectable text and/or embedded objects
|
||||
newAcc = new HyperTextAccessibleWrap(content, aDoc);
|
||||
newAcc = new HyperTextAccessibleWrap(content, document);
|
||||
} else { // XUL, SVG, MathML etc.
|
||||
// Interesting generic non-HTML container
|
||||
newAcc = new AccessibleWrap(content, aDoc);
|
||||
newAcc = new AccessibleWrap(content, document);
|
||||
}
|
||||
}
|
||||
|
||||
return aDoc->BindToDocument(newAcc, roleMapEntry) ? newAcc : nullptr;
|
||||
return document->BindToDocument(newAcc, roleMapEntry) ? newAcc : nullptr;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@ -1238,13 +1188,14 @@ nsAccessibilityService::CreateAccessibleByType(nsIContent* aContent,
|
||||
already_AddRefed<Accessible>
|
||||
nsAccessibilityService::CreateHTMLAccessibleByMarkup(nsIFrame* aFrame,
|
||||
nsIContent* aContent,
|
||||
DocAccessible* aDoc,
|
||||
bool aIsLegalPartOfHTMLTable)
|
||||
Accessible* aContext)
|
||||
{
|
||||
if (aIsLegalPartOfHTMLTable) {
|
||||
if (nsCoreUtils::IsHTMLTableHeader(aContent)) {
|
||||
Accessible* accessible =
|
||||
new HTMLTableHeaderCellAccessibleWrap(aContent, aDoc);
|
||||
DocAccessible* document = aContext->Document();
|
||||
if (aContext->IsOfType(Accessible::eTableRowAccessible)) {
|
||||
if (nsCoreUtils::IsHTMLTableHeader(aContent) &&
|
||||
aContext->GetContent() == aContent->GetParent()) {
|
||||
Accessible* accessible = new HTMLTableHeaderCellAccessibleWrap(aContent,
|
||||
document);
|
||||
NS_ADDREF(accessible);
|
||||
return accessible;
|
||||
}
|
||||
@ -1255,38 +1206,39 @@ nsAccessibilityService::CreateHTMLAccessibleByMarkup(nsIFrame* aFrame,
|
||||
// This method assumes we're in an HTML namespace.
|
||||
nsIAtom* tag = aContent->Tag();
|
||||
if (tag == nsGkAtoms::figcaption) {
|
||||
Accessible* accessible = new HTMLFigcaptionAccessible(aContent, aDoc);
|
||||
Accessible* accessible = new HTMLFigcaptionAccessible(aContent, document);
|
||||
NS_ADDREF(accessible);
|
||||
return accessible;
|
||||
}
|
||||
|
||||
if (tag == nsGkAtoms::figure) {
|
||||
Accessible* accessible = new HTMLFigureAccessible(aContent, aDoc);
|
||||
Accessible* accessible = new HTMLFigureAccessible(aContent, document);
|
||||
NS_ADDREF(accessible);
|
||||
return accessible;
|
||||
}
|
||||
|
||||
if (tag == nsGkAtoms::legend) {
|
||||
Accessible* accessible = new HTMLLegendAccessible(aContent, aDoc);
|
||||
Accessible* accessible = new HTMLLegendAccessible(aContent, document);
|
||||
NS_ADDREF(accessible);
|
||||
return accessible;
|
||||
}
|
||||
|
||||
if (tag == nsGkAtoms::option) {
|
||||
Accessible* accessible = new HTMLSelectOptionAccessible(aContent, aDoc);
|
||||
Accessible* accessible = new HTMLSelectOptionAccessible(aContent, document);
|
||||
NS_ADDREF(accessible);
|
||||
return accessible;
|
||||
}
|
||||
|
||||
if (tag == nsGkAtoms::optgroup) {
|
||||
Accessible* accessible = new HTMLSelectOptGroupAccessible(aContent, aDoc);
|
||||
Accessible* accessible =
|
||||
new HTMLSelectOptGroupAccessible(aContent, document);
|
||||
NS_ADDREF(accessible);
|
||||
return accessible;
|
||||
}
|
||||
|
||||
if (tag == nsGkAtoms::ul || tag == nsGkAtoms::ol ||
|
||||
tag == nsGkAtoms::dl) {
|
||||
Accessible* accessible = new HTMLListAccessible(aContent, aDoc);
|
||||
Accessible* accessible = new HTMLListAccessible(aContent, document);
|
||||
NS_ADDREF(accessible);
|
||||
return accessible;
|
||||
}
|
||||
@ -1297,12 +1249,12 @@ nsAccessibilityService::CreateHTMLAccessibleByMarkup(nsIFrame* aFrame,
|
||||
nsRoleMapEntry* roleMapEntry = aria::GetRoleMap(aContent);
|
||||
if (roleMapEntry && roleMapEntry->role != roles::NOTHING &&
|
||||
roleMapEntry->role != roles::LINK) {
|
||||
Accessible* accessible = new HyperTextAccessibleWrap(aContent, aDoc);
|
||||
Accessible* accessible = new HyperTextAccessibleWrap(aContent, document);
|
||||
NS_ADDREF(accessible);
|
||||
return accessible;
|
||||
}
|
||||
|
||||
Accessible* accessible = new HTMLLinkAccessible(aContent, aDoc);
|
||||
Accessible* accessible = new HTMLLinkAccessible(aContent, document);
|
||||
NS_ADDREF(accessible);
|
||||
return accessible;
|
||||
}
|
||||
@ -1310,7 +1262,7 @@ nsAccessibilityService::CreateHTMLAccessibleByMarkup(nsIFrame* aFrame,
|
||||
if (tag == nsGkAtoms::dt || tag == nsGkAtoms::li) {
|
||||
// Create list item accessible unconditionally by tag name. nsBlockFrame
|
||||
// creates the list item accessible for other elements styled as list items.
|
||||
Accessible* accessible = new HTMLLIAccessible(aContent, aDoc);
|
||||
Accessible* accessible = new HTMLLIAccessible(aContent, document);
|
||||
NS_ADDREF(accessible);
|
||||
return accessible;
|
||||
}
|
||||
@ -1327,20 +1279,20 @@ nsAccessibilityService::CreateHTMLAccessibleByMarkup(nsIFrame* aFrame,
|
||||
tag == nsGkAtoms::h5 ||
|
||||
tag == nsGkAtoms::h6 ||
|
||||
tag == nsGkAtoms::q) {
|
||||
Accessible* accessible = new HyperTextAccessibleWrap(aContent, aDoc);
|
||||
Accessible* accessible = new HyperTextAccessibleWrap(aContent, document);
|
||||
NS_ADDREF(accessible);
|
||||
return accessible;
|
||||
}
|
||||
|
||||
if (tag == nsGkAtoms::output) {
|
||||
Accessible* accessible = new HTMLOutputAccessible(aContent, aDoc);
|
||||
Accessible* accessible = new HTMLOutputAccessible(aContent, document);
|
||||
NS_ADDREF(accessible);
|
||||
return accessible;
|
||||
}
|
||||
|
||||
if (tag == nsGkAtoms::progress) {
|
||||
Accessible* accessible =
|
||||
new HTMLProgressMeterAccessible(aContent, aDoc);
|
||||
new HTMLProgressMeterAccessible(aContent, document);
|
||||
NS_ADDREF(accessible);
|
||||
return accessible;
|
||||
}
|
||||
@ -1351,86 +1303,109 @@ nsAccessibilityService::CreateHTMLAccessibleByMarkup(nsIFrame* aFrame,
|
||||
already_AddRefed<Accessible>
|
||||
nsAccessibilityService::CreateAccessibleByFrameType(nsIFrame* aFrame,
|
||||
nsIContent* aContent,
|
||||
DocAccessible* aDoc)
|
||||
Accessible* aContext)
|
||||
{
|
||||
DocAccessible* document = aContext->Document();
|
||||
|
||||
nsRefPtr<Accessible> newAcc;
|
||||
switch (aFrame->AccessibleType()) {
|
||||
case eNoAccessible:
|
||||
return nullptr;
|
||||
case eHTMLBRAccessible:
|
||||
newAcc = new HTMLBRAccessible(aContent, aDoc);
|
||||
newAcc = new HTMLBRAccessible(aContent, document);
|
||||
break;
|
||||
case eHTMLButtonAccessible:
|
||||
newAcc = new HTMLButtonAccessible(aContent, aDoc);
|
||||
newAcc = new HTMLButtonAccessible(aContent, document);
|
||||
break;
|
||||
case eHTMLCanvasAccessible:
|
||||
newAcc = new HTMLCanvasAccessible(aContent, aDoc);
|
||||
newAcc = new HTMLCanvasAccessible(aContent, document);
|
||||
break;
|
||||
case eHTMLCaptionAccessible:
|
||||
newAcc = new HTMLCaptionAccessible(aContent, aDoc);
|
||||
if (aContext->IsOfType(Accessible::eTableAccessible) &&
|
||||
aContext->GetContent() == aContent->GetParent()) {
|
||||
newAcc = new HTMLCaptionAccessible(aContent, document);
|
||||
}
|
||||
break;
|
||||
case eHTMLCheckboxAccessible:
|
||||
newAcc = new HTMLCheckboxAccessible(aContent, aDoc);
|
||||
newAcc = new HTMLCheckboxAccessible(aContent, document);
|
||||
break;
|
||||
case eHTMLComboboxAccessible:
|
||||
newAcc = new HTMLComboboxAccessible(aContent, aDoc);
|
||||
newAcc = new HTMLComboboxAccessible(aContent, document);
|
||||
break;
|
||||
case eHTMLFileInputAccessible:
|
||||
newAcc = new HTMLFileInputAccessible(aContent, aDoc);
|
||||
newAcc = new HTMLFileInputAccessible(aContent, document);
|
||||
break;
|
||||
case eHTMLGroupboxAccessible:
|
||||
newAcc = new HTMLGroupboxAccessible(aContent, aDoc);
|
||||
newAcc = new HTMLGroupboxAccessible(aContent, document);
|
||||
break;
|
||||
case eHTMLHRAccessible:
|
||||
newAcc = new HTMLHRAccessible(aContent, aDoc);
|
||||
newAcc = new HTMLHRAccessible(aContent, document);
|
||||
break;
|
||||
case eHTMLImageMapAccessible:
|
||||
newAcc = new HTMLImageMapAccessible(aContent, aDoc);
|
||||
newAcc = new HTMLImageMapAccessible(aContent, document);
|
||||
break;
|
||||
case eHTMLLabelAccessible:
|
||||
newAcc = new HTMLLabelAccessible(aContent, aDoc);
|
||||
newAcc = new HTMLLabelAccessible(aContent, document);
|
||||
break;
|
||||
case eHTMLLiAccessible:
|
||||
newAcc = new HTMLLIAccessible(aContent, aDoc);
|
||||
newAcc = new HTMLLIAccessible(aContent, document);
|
||||
break;
|
||||
case eHTMLSelectListAccessible:
|
||||
newAcc = new HTMLSelectListAccessible(aContent, aDoc);
|
||||
newAcc = new HTMLSelectListAccessible(aContent, document);
|
||||
break;
|
||||
case eHTMLMediaAccessible:
|
||||
newAcc = new EnumRoleAccessible(aContent, aDoc, roles::GROUPING);
|
||||
newAcc = new EnumRoleAccessible(aContent, document, roles::GROUPING);
|
||||
break;
|
||||
case eHTMLObjectFrameAccessible: {
|
||||
nsObjectFrame* objectFrame = do_QueryFrame(aFrame);
|
||||
newAcc = CreateHTMLObjectFrameAccessible(objectFrame, aContent, aDoc);
|
||||
newAcc = CreateHTMLObjectFrameAccessible(objectFrame, aContent, aContext);
|
||||
break;
|
||||
}
|
||||
|
||||
case eHTMLRadioButtonAccessible:
|
||||
newAcc = new HTMLRadioButtonAccessible(aContent, aDoc);
|
||||
newAcc = new HTMLRadioButtonAccessible(aContent, document);
|
||||
break;
|
||||
case eHTMLTableAccessible:
|
||||
newAcc = new HTMLTableAccessibleWrap(aContent, aDoc);
|
||||
newAcc = new HTMLTableAccessibleWrap(aContent, document);
|
||||
break;
|
||||
case eHTMLTableCellAccessible:
|
||||
newAcc = new HTMLTableCellAccessibleWrap(aContent, aDoc);
|
||||
// Accessible HTML table cell must be a child of accessible HTML table row.
|
||||
if (aContext->IsOfType(Accessible::eHTMLTableRowAccessible))
|
||||
newAcc = new HTMLTableCellAccessibleWrap(aContent, document);
|
||||
break;
|
||||
case eHTMLTableRowAccessible:
|
||||
newAcc = new EnumRoleAccessible(aContent, aDoc, roles::ROW);
|
||||
|
||||
case eHTMLTableRowAccessible: {
|
||||
// Accessible HTML table row must be a child of tbody/tfoot/thead of
|
||||
// accessible HTML table or must be a child of accessible of HTML table.
|
||||
if (aContext->IsOfType(Accessible::eTableAccessible)) {
|
||||
nsIContent* parentContent = aContent->GetParent();
|
||||
nsIFrame* parentFrame = parentContent->GetPrimaryFrame();
|
||||
if (parentFrame->GetType() == nsGkAtoms::tableRowGroupFrame) {
|
||||
parentContent = parentContent->GetParent();
|
||||
parentFrame = parentContent->GetPrimaryFrame();
|
||||
}
|
||||
|
||||
if (parentFrame->GetType() == nsGkAtoms::tableOuterFrame &&
|
||||
aContext->GetContent() == parentContent) {
|
||||
newAcc = new HTMLTableRowAccessible(aContent, document);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case eHTMLTextFieldAccessible:
|
||||
newAcc = new HTMLTextFieldAccessible(aContent, aDoc);
|
||||
newAcc = new HTMLTextFieldAccessible(aContent, document);
|
||||
break;
|
||||
case eHyperTextAccessible:
|
||||
newAcc = new HyperTextAccessibleWrap(aContent, aDoc);
|
||||
newAcc = new HyperTextAccessibleWrap(aContent, document);
|
||||
break;
|
||||
case eImageAccessible:
|
||||
newAcc = new ImageAccessibleWrap(aContent, aDoc);
|
||||
newAcc = new ImageAccessibleWrap(aContent, document);
|
||||
break;
|
||||
case eOuterDocAccessible:
|
||||
newAcc = new OuterDocAccessible(aContent, aDoc);
|
||||
newAcc = new OuterDocAccessible(aContent, document);
|
||||
break;
|
||||
case eTextLeafAccessible:
|
||||
newAcc = new TextLeafAccessibleWrap(aContent, aDoc);
|
||||
newAcc = new TextLeafAccessibleWrap(aContent, document);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -83,7 +83,7 @@ public:
|
||||
bool aCanCreate);
|
||||
already_AddRefed<Accessible>
|
||||
CreateHTMLObjectFrameAccessible(nsObjectFrame* aFrame, nsIContent* aContent,
|
||||
DocAccessible* aDoc);
|
||||
Accessible* aContext);
|
||||
|
||||
/**
|
||||
* Adds/remove ATK root accessible for gtk+ native window to/from children
|
||||
@ -155,11 +155,11 @@ public:
|
||||
* one.
|
||||
*
|
||||
* @param aNode [in] the given node
|
||||
* @param aDoc [in] the doc accessible of the node
|
||||
* @param aContext [in] context the accessible is created in
|
||||
* @param aIsSubtreeHidden [out, optional] indicates whether the node's
|
||||
* frame and its subtree is hidden
|
||||
*/
|
||||
Accessible* GetOrCreateAccessible(nsINode* aNode, DocAccessible* aDoc,
|
||||
Accessible* GetOrCreateAccessible(nsINode* aNode, Accessible* aContext,
|
||||
bool* aIsSubtreeHidden = nullptr);
|
||||
|
||||
private:
|
||||
@ -192,15 +192,14 @@ private:
|
||||
*/
|
||||
already_AddRefed<Accessible>
|
||||
CreateHTMLAccessibleByMarkup(nsIFrame* aFrame, nsIContent* aContent,
|
||||
DocAccessible* aDoc,
|
||||
bool aIsLegalPartOfHTMLTable);
|
||||
Accessible* aContext);
|
||||
|
||||
/**
|
||||
* Create an accessible whose type depends on the given frame.
|
||||
*/
|
||||
already_AddRefed<Accessible>
|
||||
CreateAccessibleByFrameType(nsIFrame* aFrame, nsIContent* aContent,
|
||||
DocAccessible* aDoc);
|
||||
Accessible* aContext);
|
||||
|
||||
/**
|
||||
* Create accessible if parent is a deck frame.
|
||||
|
@ -172,8 +172,12 @@ public:
|
||||
/**
|
||||
* Return true if ARIA role is specified on the element.
|
||||
*/
|
||||
bool HasARIARole() const
|
||||
{ return mRoleMapEntry; }
|
||||
bool HasARIARole() const { return mRoleMapEntry; }
|
||||
|
||||
/**
|
||||
* Retrun ARIA role map if any.
|
||||
*/
|
||||
nsRoleMapEntry* ARIARoleMap() const { return mRoleMapEntry; }
|
||||
|
||||
/**
|
||||
* Return accessible role specified by ARIA (see constants in
|
||||
@ -692,6 +696,11 @@ public:
|
||||
*/
|
||||
bool HasOwnContent() const { return mContent && !(mFlags & eSharedNode); }
|
||||
|
||||
/**
|
||||
* Return true if accessible is of given type.
|
||||
*/
|
||||
bool IsOfType(uint32_t aType) const { return mFlags & aType; }
|
||||
|
||||
/**
|
||||
* Return true if the accessible has a numeric value.
|
||||
*/
|
||||
@ -778,17 +787,21 @@ public: // XXX: a small hack to make these visible for nsARIAMap
|
||||
eHyperTextAccessible = 1 << 12,
|
||||
eHTMLFileInputAccessible = 1 << 13,
|
||||
eHTMLListItemAccessible = 1 << 14,
|
||||
eImageAccessible = 1 << 15,
|
||||
eImageMapAccessible = 1 << 16,
|
||||
eListControlAccessible = 1 << 17,
|
||||
eMenuButtonAccessible = 1 << 18,
|
||||
eMenuPopupAccessible = 1 << 19,
|
||||
eProgressAccessible = 1 << 20,
|
||||
eRootAccessible = 1 << 21,
|
||||
eSelectAccessible = 1 << 22,
|
||||
eTextLeafAccessible = 1 << 23,
|
||||
eXULDeckAccessible = 1 << 24,
|
||||
eXULTreeAccessible = 1 << 25
|
||||
eHTMLTableRowAccessible = 1 << 15,
|
||||
eImageAccessible = 1 << 16,
|
||||
eImageMapAccessible = 1 << 17,
|
||||
eListControlAccessible = 1 << 18,
|
||||
eMenuButtonAccessible = 1 << 19,
|
||||
eMenuPopupAccessible = 1 << 20,
|
||||
eProgressAccessible = 1 << 21,
|
||||
eRootAccessible = 1 << 22,
|
||||
eSelectAccessible = 1 << 23,
|
||||
eTableAccessible = 1 << 24,
|
||||
eTableCellAccessible = 1 << 25,
|
||||
eTableRowAccessible = 1 << 26,
|
||||
eTextLeafAccessible = 1 << 27,
|
||||
eXULDeckAccessible = 1 << 28,
|
||||
eXULTreeAccessible = 1 << 29
|
||||
};
|
||||
|
||||
protected:
|
||||
|
@ -145,7 +145,7 @@ HTMLSelectListAccessible::CacheOptSiblings(nsIContent* aParentContent)
|
||||
|
||||
// Get an accessible for option or optgroup and cache it.
|
||||
nsRefPtr<Accessible> accessible =
|
||||
GetAccService()->GetOrCreateAccessible(childContent, mDoc);
|
||||
GetAccService()->GetOrCreateAccessible(childContent, this);
|
||||
if (accessible)
|
||||
AppendChild(accessible);
|
||||
|
||||
|
@ -321,6 +321,19 @@ HTMLTableHeaderCellAccessible::NativeRole()
|
||||
return roles::COLUMNHEADER;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// HTMLTableRowAccessible
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED0(HTMLTableRowAccessible, Accessible)
|
||||
|
||||
role
|
||||
HTMLTableRowAccessible::NativeRole()
|
||||
{
|
||||
return roles::ROW;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// HTMLTableAccessible
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@ -329,6 +342,7 @@ HTMLTableAccessible::
|
||||
HTMLTableAccessible(nsIContent* aContent, DocAccessible* aDoc) :
|
||||
AccessibleWrap(aContent, aDoc), xpcAccessibleTable(this)
|
||||
{
|
||||
mFlags |= eTableAccessible;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -85,6 +85,24 @@ public:
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* HTML table row accessible (html:tr).
|
||||
*/
|
||||
class HTMLTableRowAccessible : public AccessibleWrap
|
||||
{
|
||||
public:
|
||||
HTMLTableRowAccessible(nsIContent* aContent, DocAccessible* aDoc) :
|
||||
AccessibleWrap(aContent, aDoc)
|
||||
{ mFlags |= eTableRowAccessible | eHTMLTableRowAccessible; }
|
||||
virtual ~HTMLTableRowAccessible() { }
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
// Accessible
|
||||
virtual a11y::role NativeRole();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* HTML table accessible (html:table).
|
||||
*/
|
||||
|
@ -28,6 +28,7 @@ XULTreeGridAccessible::
|
||||
XULTreeGridAccessible(nsIContent* aContent, DocAccessible* aDoc) :
|
||||
XULTreeAccessible(aContent, aDoc), xpcAccessibleTable(this)
|
||||
{
|
||||
mFlags |= eTableAccessible;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@ -279,6 +280,8 @@ XULTreeGridRowAccessible::
|
||||
nsITreeView* aTreeView, int32_t aRow) :
|
||||
XULTreeItemAccessibleBase(aContent, aDoc, aTreeAcc, aTree, aTreeView, aRow)
|
||||
{
|
||||
mFlags |= eTableRowAccessible;
|
||||
|
||||
mAccessibleCache.Init(kDefaultTreeCacheSize);
|
||||
}
|
||||
|
||||
|
@ -369,6 +369,9 @@ function testAccessibleTree(aAccOrElmOrID, aAccTree)
|
||||
testStates(acc, statesObj.states, statesObj.extraStates,
|
||||
statesObj.absentStates, statesObj.absentExtraStates);
|
||||
|
||||
} else if (prop == "tagName") {
|
||||
is(accTree[prop], acc.DOMNode.tagName, msg);
|
||||
|
||||
} else if (prop != "children") {
|
||||
is(acc[prop], accTree[prop], msg);
|
||||
}
|
||||
|
@ -115,7 +115,8 @@
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// test gEmptyRoleMap
|
||||
testRole("cell", ROLE_NOTHING);
|
||||
testRole("buttontable_row", ROLE_NOTHING);
|
||||
testRole("buttontable_cell", ROLE_NOTHING);
|
||||
|
||||
// abstract roles
|
||||
var abstract_roles = ["composite", "landmark", "structure", "widget",
|
||||
@ -275,8 +276,8 @@
|
||||
|
||||
<!-- test gEmptyRoleMap -->
|
||||
<table role="button">
|
||||
<tr>
|
||||
<td id="cell">cell</td>
|
||||
<tr id="buttontable_row">
|
||||
<td id="buttontable_cell">cell</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
@ -32,6 +32,124 @@
|
||||
|
||||
testAccessibleTree("grid", accTree);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// crazy grids (mad mix of ARIA and HTML tables)
|
||||
|
||||
accTree = {
|
||||
role: ROLE_TABLE,
|
||||
children: [
|
||||
{ // div@role="row"
|
||||
role: ROLE_ROW,
|
||||
tagName: "DIV",
|
||||
children: [
|
||||
{ // caption text leaf
|
||||
role: ROLE_TEXT_LEAF,
|
||||
name: "caption",
|
||||
children: [ ]
|
||||
},
|
||||
{ // th text leaf
|
||||
role: ROLE_TEXT_LEAF,
|
||||
name: "header1",
|
||||
children: [ ]
|
||||
},
|
||||
{ // td@role="columnheader"
|
||||
role: ROLE_COLUMNHEADER,
|
||||
name: "header2",
|
||||
children: [ { TEXT_LEAF: [ ] } ]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
testAccessibleTree("crazy_grid1", accTree);
|
||||
|
||||
accTree = {
|
||||
role: ROLE_TABLE,
|
||||
children: [
|
||||
{ // tr@role="row"
|
||||
role: ROLE_ROW,
|
||||
tagName: "TR",
|
||||
children: [
|
||||
{ // td text leaf
|
||||
role: ROLE_TEXT_LEAF,
|
||||
name: "cell1",
|
||||
children: [ ]
|
||||
},
|
||||
{ // td@role="gridcell"
|
||||
role: ROLE_GRID_CELL,
|
||||
name: "cell2",
|
||||
children: [ { TEXT_LEAF: [ ] } ]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
testAccessibleTree("crazy_grid2", accTree);
|
||||
|
||||
accTree = {
|
||||
role: ROLE_TABLE,
|
||||
children: [
|
||||
{ // div@role="row"
|
||||
role: ROLE_ROW,
|
||||
children: [
|
||||
{ // div@role="gridcell"
|
||||
role: ROLE_GRID_CELL,
|
||||
children: [
|
||||
{ // text leaf from presentational table
|
||||
role: ROLE_TEXT_LEAF,
|
||||
name: "cell3",
|
||||
children: [ ]
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
testAccessibleTree("crazy_grid3", accTree);
|
||||
|
||||
accTree = {
|
||||
role: ROLE_TABLE,
|
||||
children: [
|
||||
{ // div@role="row"
|
||||
role: ROLE_ROW,
|
||||
children: [
|
||||
{ // div@role="gridcell"
|
||||
role: ROLE_GRID_CELL,
|
||||
children: [
|
||||
{ // table
|
||||
role: ROLE_TABLE,
|
||||
children: [
|
||||
{ // tr
|
||||
role: ROLE_ROW,
|
||||
children: [
|
||||
{ // td
|
||||
role: ROLE_CELL,
|
||||
children: [
|
||||
{ // caption text leaf of presentational table
|
||||
role: ROLE_TEXT_LEAF,
|
||||
name: "caption",
|
||||
children: [ ]
|
||||
},
|
||||
{ // td text leaf of presentational table
|
||||
role: ROLE_TEXT_LEAF,
|
||||
name: "cell4",
|
||||
children: [ ]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
testAccessibleTree("crazy_grid4", accTree);
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
@ -59,5 +177,55 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="crazy_grid1" role="grid">
|
||||
<div role="row">
|
||||
<table role="presentation">
|
||||
<caption>caption</caption>
|
||||
<tr>
|
||||
<th>header1</th>
|
||||
<td role="columnheader">header2</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="crazy_grid2" role="grid">
|
||||
<table role="presentation">
|
||||
<tr role="row">
|
||||
<td id="ct_cell1">cell1</td>
|
||||
<td role="gridcell">cell2</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="crazy_grid3" role="grid">
|
||||
<div role="row">
|
||||
<div role="gridcell">
|
||||
<table role="presentation">
|
||||
<tr>
|
||||
<td>cell3</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="crazy_grid4" role="grid">
|
||||
<div role="row">
|
||||
<div role="gridcell">
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<table role="presentation">
|
||||
<caption>caption</caption>
|
||||
<tr><td>cell4</td></tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -27,7 +27,6 @@
|
||||
|
||||
var tree =
|
||||
{ PUSHBUTTON: [ // table
|
||||
{ NOTHING: [ // tbody
|
||||
{ NOTHING: [ // tr
|
||||
{ NOTHING: [ // th
|
||||
{ TEXT_LEAF: [ ] }
|
||||
@ -35,8 +34,7 @@
|
||||
{ NOTHING: [ // td
|
||||
{ TEXT_LEAF: [ ] }
|
||||
] }
|
||||
] },
|
||||
] },
|
||||
] }
|
||||
] };
|
||||
testAccessibleTree("button_table", tree);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user