mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-27 06:43:32 +00:00
71fd3eb9cb
This revision implements the ARIA rowgroup role in Gecko. Previously, Gecko was using roles::GROUPING for multiple types of groups and disambiguating them in various places. This revision unwinds that while maintaining present functionality. Differential Revision: https://phabricator.services.mozilla.com/D205671
450 lines
14 KiB
C
450 lines
14 KiB
C
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim:expandtab:shiftwidth=2:tabstop=2:
|
|
*/
|
|
/* 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/. */
|
|
|
|
MARKUPMAP(
|
|
a,
|
|
[](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* {
|
|
// An anchor element without an href attribute and without a click
|
|
// listener should be a generic.
|
|
if (!aElement->HasAttr(nsGkAtoms::href) &&
|
|
!nsCoreUtils::HasClickListener(aElement)) {
|
|
return new HyperTextAccessible(aElement, aContext->Document());
|
|
}
|
|
// Only some roles truly enjoy life as HTMLLinkAccessibles, for
|
|
// details see closed bug 494807.
|
|
const nsRoleMapEntry* roleMapEntry = aria::GetRoleMap(aElement);
|
|
if (roleMapEntry && roleMapEntry->role != roles::NOTHING &&
|
|
roleMapEntry->role != roles::LINK) {
|
|
return new HyperTextAccessible(aElement, aContext->Document());
|
|
}
|
|
|
|
return new HTMLLinkAccessible(aElement, aContext->Document());
|
|
},
|
|
0)
|
|
|
|
MARKUPMAP(abbr, New_HyperText, 0)
|
|
|
|
MARKUPMAP(acronym, New_HyperText, 0)
|
|
|
|
MARKUPMAP(address, New_HyperText, roles::GROUPING)
|
|
|
|
MARKUPMAP(article, New_HyperText, roles::ARTICLE, Attr(xmlroles, article))
|
|
|
|
MARKUPMAP(
|
|
aside,
|
|
[](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* {
|
|
return new HTMLAsideAccessible(aElement, aContext->Document());
|
|
},
|
|
0)
|
|
|
|
MARKUPMAP(blockquote, New_HyperText, roles::BLOCKQUOTE)
|
|
|
|
MARKUPMAP(
|
|
button,
|
|
[](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* {
|
|
return new HTMLButtonAccessible(aElement, aContext->Document());
|
|
},
|
|
0)
|
|
|
|
MARKUPMAP(
|
|
caption,
|
|
[](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* {
|
|
if (aContext->IsTable()) {
|
|
dom::HTMLTableElement* tableEl =
|
|
dom::HTMLTableElement::FromNode(aContext->GetContent());
|
|
if (tableEl && tableEl == aElement->GetParent() &&
|
|
tableEl->GetCaption() == aElement) {
|
|
return new HTMLCaptionAccessible(aElement, aContext->Document());
|
|
}
|
|
}
|
|
return nullptr;
|
|
},
|
|
0)
|
|
|
|
MARKUPMAP(code, New_HyperText, roles::CODE)
|
|
|
|
MARKUPMAP(dd, New_HTMLDtOrDd<HyperTextAccessible>, roles::DEFINITION)
|
|
|
|
MARKUPMAP(del, New_HyperText, roles::CONTENT_DELETION)
|
|
|
|
MARKUPMAP(details, New_HyperText, roles::DETAILS)
|
|
|
|
MARKUPMAP(dfn, New_HyperText, roles::TERM)
|
|
|
|
MARKUPMAP(dialog, New_HyperText, roles::DIALOG)
|
|
|
|
MARKUPMAP(
|
|
div,
|
|
[](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* {
|
|
// Never create an accessible if we're part of an anonymous
|
|
// subtree.
|
|
if (aElement->IsInNativeAnonymousSubtree()) {
|
|
return nullptr;
|
|
}
|
|
// Always create an accessible if the div has an id.
|
|
if (aElement->HasAttr(nsGkAtoms::id)) {
|
|
return new HyperTextAccessible(aElement, aContext->Document());
|
|
}
|
|
// Never create an accessible if the div is not display:block; or
|
|
// display:inline-block or the like.
|
|
nsIFrame* f = aElement->GetPrimaryFrame();
|
|
if (!f || !f->IsBlockFrameOrSubclass()) {
|
|
return nullptr;
|
|
}
|
|
// Check for various conditions to determine if this is a block
|
|
// break and needs to be rendered.
|
|
// If its previous sibling is an inline element, we probably want
|
|
// to break, so render.
|
|
// FIXME: This looks extremely incorrect in presence of shadow DOM,
|
|
// display: contents, and what not.
|
|
nsIContent* prevSibling = aElement->GetPreviousSibling();
|
|
if (prevSibling) {
|
|
nsIFrame* prevSiblingFrame = prevSibling->GetPrimaryFrame();
|
|
if (prevSiblingFrame && prevSiblingFrame->IsInlineOutside()) {
|
|
return new HyperTextAccessible(aElement, aContext->Document());
|
|
}
|
|
}
|
|
// Now, check the children.
|
|
nsIContent* firstChild = aElement->GetFirstChild();
|
|
if (firstChild) {
|
|
nsIFrame* firstChildFrame = firstChild->GetPrimaryFrame();
|
|
if (!firstChildFrame) {
|
|
// The first child is invisible, but this might be due to an
|
|
// invisible text node. Try the next.
|
|
firstChild = firstChild->GetNextSibling();
|
|
if (!firstChild) {
|
|
// If there's no next sibling, there's only one child, so there's
|
|
// nothing more we can do.
|
|
return nullptr;
|
|
}
|
|
firstChildFrame = firstChild->GetPrimaryFrame();
|
|
}
|
|
// Check to see if first child has an inline frame.
|
|
if (firstChildFrame && firstChildFrame->IsInlineOutside()) {
|
|
return new HyperTextAccessible(aElement, aContext->Document());
|
|
}
|
|
nsIContent* lastChild = aElement->GetLastChild();
|
|
MOZ_ASSERT(lastChild);
|
|
if (lastChild != firstChild) {
|
|
nsIFrame* lastChildFrame = lastChild->GetPrimaryFrame();
|
|
if (!lastChildFrame) {
|
|
// The last child is invisible, but this might be due to an
|
|
// invisible text node. Try the next.
|
|
lastChild = lastChild->GetPreviousSibling();
|
|
MOZ_ASSERT(lastChild);
|
|
if (lastChild == firstChild) {
|
|
return nullptr;
|
|
}
|
|
lastChildFrame = lastChild->GetPrimaryFrame();
|
|
}
|
|
// Check to see if last child has an inline frame.
|
|
if (lastChildFrame && lastChildFrame->IsInlineOutside()) {
|
|
return new HyperTextAccessible(aElement, aContext->Document());
|
|
}
|
|
}
|
|
}
|
|
return nullptr;
|
|
},
|
|
roles::SECTION)
|
|
|
|
MARKUPMAP(
|
|
dl,
|
|
[](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* {
|
|
return new HTMLListAccessible(aElement, aContext->Document());
|
|
},
|
|
roles::DEFINITION_LIST)
|
|
|
|
MARKUPMAP(dt, New_HTMLDtOrDd<HTMLLIAccessible>, roles::TERM)
|
|
|
|
MARKUPMAP(em, New_HyperText, roles::EMPHASIS)
|
|
|
|
MARKUPMAP(
|
|
figcaption,
|
|
[](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* {
|
|
return new HTMLFigcaptionAccessible(aElement, aContext->Document());
|
|
},
|
|
roles::CAPTION)
|
|
|
|
MARKUPMAP(
|
|
figure,
|
|
[](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* {
|
|
return new HTMLFigureAccessible(aElement, aContext->Document());
|
|
},
|
|
roles::FIGURE, Attr(xmlroles, figure))
|
|
|
|
MARKUPMAP(
|
|
fieldset,
|
|
[](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* {
|
|
return new HTMLGroupboxAccessible(aElement, aContext->Document());
|
|
},
|
|
0)
|
|
|
|
MARKUPMAP(
|
|
form,
|
|
[](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* {
|
|
return new HTMLFormAccessible(aElement, aContext->Document());
|
|
},
|
|
0)
|
|
|
|
MARKUPMAP(
|
|
footer,
|
|
[](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* {
|
|
return new HTMLHeaderOrFooterAccessible(aElement, aContext->Document());
|
|
},
|
|
0)
|
|
|
|
MARKUPMAP(
|
|
header,
|
|
[](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* {
|
|
return new HTMLHeaderOrFooterAccessible(aElement, aContext->Document());
|
|
},
|
|
0)
|
|
|
|
MARKUPMAP(h1, New_HyperText, roles::HEADING)
|
|
|
|
MARKUPMAP(h2, New_HyperText, roles::HEADING)
|
|
|
|
MARKUPMAP(h3, New_HyperText, roles::HEADING)
|
|
|
|
MARKUPMAP(h4, New_HyperText, roles::HEADING)
|
|
|
|
MARKUPMAP(h5, New_HyperText, roles::HEADING)
|
|
|
|
MARKUPMAP(h6, New_HyperText, roles::HEADING)
|
|
|
|
MARKUPMAP(hgroup, New_HyperText, roles::GROUPING)
|
|
|
|
MARKUPMAP(
|
|
hr,
|
|
[](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* {
|
|
return new HTMLHRAccessible(aElement, aContext->Document());
|
|
},
|
|
0)
|
|
|
|
MARKUPMAP(
|
|
input,
|
|
[](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* {
|
|
// TODO(emilio): This would be faster if it used
|
|
// HTMLInputElement's already-parsed representation.
|
|
if (aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
|
|
nsGkAtoms::checkbox, eIgnoreCase)) {
|
|
return new CheckboxAccessible(aElement, aContext->Document());
|
|
}
|
|
if (aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
|
|
nsGkAtoms::image, eIgnoreCase)) {
|
|
return new HTMLButtonAccessible(aElement, aContext->Document());
|
|
}
|
|
if (aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
|
|
nsGkAtoms::radio, eIgnoreCase)) {
|
|
return new HTMLRadioButtonAccessible(aElement, aContext->Document());
|
|
}
|
|
if (aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
|
|
nsGkAtoms::time, eIgnoreCase)) {
|
|
return new HTMLDateTimeAccessible<roles::TIME_EDITOR>(
|
|
aElement, aContext->Document());
|
|
}
|
|
if (aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
|
|
nsGkAtoms::date, eIgnoreCase) ||
|
|
aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
|
|
nsGkAtoms::datetime_local, eIgnoreCase)) {
|
|
return new HTMLDateTimeAccessible<roles::DATE_EDITOR>(
|
|
aElement, aContext->Document());
|
|
}
|
|
return nullptr;
|
|
},
|
|
0)
|
|
|
|
MARKUPMAP(ins, New_HyperText, roles::CONTENT_INSERTION)
|
|
|
|
MARKUPMAP(
|
|
label,
|
|
[](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* {
|
|
return new HTMLLabelAccessible(aElement, aContext->Document());
|
|
},
|
|
roles::LABEL)
|
|
|
|
MARKUPMAP(
|
|
legend,
|
|
[](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* {
|
|
return new HTMLLegendAccessible(aElement, aContext->Document());
|
|
},
|
|
roles::LABEL)
|
|
|
|
MARKUPMAP(
|
|
li,
|
|
[](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* {
|
|
// If list item is a child of accessible list then create an
|
|
// accessible for it unconditionally by tag name. nsBlockFrame
|
|
// creates the list item accessible for other elements styled as
|
|
// list items.
|
|
if (aContext->IsList() &&
|
|
aContext->GetContent() == aElement->GetParent()) {
|
|
return new HTMLLIAccessible(aElement, aContext->Document());
|
|
}
|
|
|
|
return nullptr;
|
|
},
|
|
0)
|
|
|
|
MARKUPMAP(main, New_HyperText, roles::LANDMARK)
|
|
|
|
MARKUPMAP(map, nullptr, roles::TEXT_CONTAINER)
|
|
|
|
MARKUPMAP(mark, New_HyperText, roles::MARK, Attr(xmlroles, mark))
|
|
|
|
MARKUPMAP(
|
|
menu,
|
|
[](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* {
|
|
return new HTMLListAccessible(aElement, aContext->Document());
|
|
},
|
|
roles::LIST)
|
|
|
|
MARKUPMAP(nav, New_HyperText, roles::LANDMARK)
|
|
|
|
MARKUPMAP(
|
|
ol,
|
|
[](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* {
|
|
return new HTMLListAccessible(aElement, aContext->Document());
|
|
},
|
|
roles::LIST)
|
|
|
|
MARKUPMAP(
|
|
option,
|
|
[](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* {
|
|
return new HTMLSelectOptionAccessible(aElement, aContext->Document());
|
|
},
|
|
0)
|
|
|
|
MARKUPMAP(
|
|
optgroup,
|
|
[](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* {
|
|
return new HTMLSelectOptGroupAccessible(aElement, aContext->Document());
|
|
},
|
|
0)
|
|
|
|
MARKUPMAP(
|
|
output,
|
|
[](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* {
|
|
return new HTMLOutputAccessible(aElement, aContext->Document());
|
|
},
|
|
roles::STATUSBAR, Attr(aria_live, polite))
|
|
|
|
MARKUPMAP(p, nullptr, roles::PARAGRAPH)
|
|
|
|
MARKUPMAP(
|
|
progress,
|
|
[](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* {
|
|
return new HTMLProgressAccessible(aElement, aContext->Document());
|
|
},
|
|
0)
|
|
|
|
MARKUPMAP(q, New_HyperText, 0)
|
|
|
|
MARKUPMAP(s, New_HyperText, roles::CONTENT_DELETION)
|
|
|
|
MARKUPMAP(
|
|
section,
|
|
[](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* {
|
|
return new HTMLSectionAccessible(aElement, aContext->Document());
|
|
},
|
|
0)
|
|
|
|
MARKUPMAP(strong, New_HyperText, roles::STRONG)
|
|
|
|
MARKUPMAP(sub, New_HyperText, roles::SUBSCRIPT)
|
|
|
|
MARKUPMAP(
|
|
summary,
|
|
[](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* {
|
|
return new HTMLSummaryAccessible(aElement, aContext->Document());
|
|
},
|
|
roles::SUMMARY)
|
|
|
|
MARKUPMAP(sup, New_HyperText, roles::SUPERSCRIPT)
|
|
|
|
MARKUPMAP(
|
|
table,
|
|
[](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* {
|
|
return new HTMLTableAccessible(aElement, aContext->Document());
|
|
},
|
|
roles::TABLE)
|
|
|
|
MARKUPMAP(time, New_HyperText, roles::TIME, Attr(xmlroles, time),
|
|
AttrFromDOM(datetime, datetime))
|
|
|
|
MARKUPMAP(tbody, nullptr, roles::ROWGROUP)
|
|
|
|
MARKUPMAP(
|
|
td,
|
|
[](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* {
|
|
if (!aContext->IsHTMLTableRow()) {
|
|
return nullptr;
|
|
}
|
|
if (aElement->HasAttr(nsGkAtoms::scope)) {
|
|
return new HTMLTableHeaderCellAccessible(aElement,
|
|
aContext->Document());
|
|
}
|
|
return new HTMLTableCellAccessible(aElement, aContext->Document());
|
|
},
|
|
0)
|
|
|
|
MARKUPMAP(tfoot, nullptr, roles::ROWGROUP)
|
|
|
|
MARKUPMAP(
|
|
th,
|
|
[](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* {
|
|
if (!aContext->IsHTMLTableRow()) {
|
|
return nullptr;
|
|
}
|
|
return new HTMLTableHeaderCellAccessible(aElement, aContext->Document());
|
|
},
|
|
0)
|
|
|
|
MARKUPMAP(thead, nullptr, roles::ROWGROUP)
|
|
|
|
MARKUPMAP(
|
|
tr,
|
|
[](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* {
|
|
if (aContext->IsTableRow()) {
|
|
// A <tr> within a row isn't valid.
|
|
return nullptr;
|
|
}
|
|
const nsRoleMapEntry* roleMapEntry = aria::GetRoleMap(aElement);
|
|
if (roleMapEntry && roleMapEntry->role != roles::NOTHING &&
|
|
roleMapEntry->role != roles::ROW) {
|
|
// There is a valid ARIA role which isn't "row". Don't treat this as an
|
|
// HTML table row.
|
|
return nullptr;
|
|
}
|
|
// Check if this <tr> is within a table. We check the grandparent because
|
|
// it might be inside a rowgroup. We don't specifically check for an HTML
|
|
// table because there are cases where there is a <tr> inside a
|
|
// <div role="table"> such as Monorail.
|
|
if (aContext->IsTable() ||
|
|
(aContext->LocalParent() && aContext->LocalParent()->IsTable())) {
|
|
return new HTMLTableRowAccessible(aElement, aContext->Document());
|
|
}
|
|
return nullptr;
|
|
},
|
|
roles::ROW)
|
|
|
|
MARKUPMAP(
|
|
ul,
|
|
[](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* {
|
|
return new HTMLListAccessible(aElement, aContext->Document());
|
|
},
|
|
roles::LIST)
|
|
|
|
MARKUPMAP(
|
|
meter,
|
|
[](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* {
|
|
return new HTMLMeterAccessible(aElement, aContext->Document());
|
|
},
|
|
roles::METER)
|
|
|
|
MARKUPMAP(search, New_HyperText, roles::LANDMARK)
|