зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1816346 part 2: If we force an Accessible to be created due to transform/fixed/sticky despite it being presentational, make it a generic Accessible. r=nlapre
Otherwise, semantics are exposed (e.g. for a table), which completely defeats the author's intent that this be treated as presentational. Differential Revision: https://phabricator.services.mozilla.com/D170165
This commit is contained in:
Родитель
75092d8fb0
Коммит
f826326f5f
|
@ -112,20 +112,6 @@ static bool MustBeAccessible(nsIContent* aContent, DocAccessible* aDocument) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the frame has been transformed, and the content has any children, we
|
|
||||||
// should create an Accessible so that we can account for the transform when
|
|
||||||
// calculating the Accessible's bounds using the parent process cache.
|
|
||||||
// Ditto for content which is position: fixed or sticky.
|
|
||||||
// However, don't do this for XUL widgets, as this breaks XUL a11y code
|
|
||||||
// expectations in some cases. XUL widgets are only used in the parent
|
|
||||||
// process and can't be cached anyway.
|
|
||||||
if (aContent->HasChildren() && !aContent->IsXULElement() &&
|
|
||||||
(frame->IsTransformed() || frame->IsStickyPositioned() ||
|
|
||||||
(frame->StyleDisplay()->mPosition == StylePositionProperty::Fixed &&
|
|
||||||
nsLayoutUtils::IsReallyFixedPos(frame)))) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (aContent->IsElement()) {
|
if (aContent->IsElement()) {
|
||||||
uint32_t attrCount = aContent->AsElement()->GetAttrCount();
|
uint32_t attrCount = aContent->AsElement()->GetAttrCount();
|
||||||
for (uint32_t attrIdx = 0; attrIdx < attrCount; attrIdx++) {
|
for (uint32_t attrIdx = 0; attrIdx < attrCount; attrIdx++) {
|
||||||
|
@ -162,6 +148,34 @@ static bool MustBeAccessible(nsIContent* aContent, DocAccessible* aDocument) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if the element must be a generic Accessible, even if it has been
|
||||||
|
* marked presentational with role="presentation", etc. MustBeAccessible causes
|
||||||
|
* an Accessible to be created as if it weren't marked presentational at all;
|
||||||
|
* e.g. <table role="presentation" tabindex="0"> will expose roles::TABLE and
|
||||||
|
* support TableAccessibleBase. In contrast, this function causes a generic
|
||||||
|
* Accessible to be created; e.g. <table role="presentation" style="position:
|
||||||
|
* fixed;"> will expose roles::TEXT_CONTAINER and will not support
|
||||||
|
* TableAccessibleBase. This is necessary in certain cases for the
|
||||||
|
* RemoteAccessible cache.
|
||||||
|
*/
|
||||||
|
static bool MustBeGenericAccessible(nsIContent* aContent,
|
||||||
|
DocAccessible* aDocument) {
|
||||||
|
nsIFrame* frame = aContent->GetPrimaryFrame();
|
||||||
|
MOZ_ASSERT(frame);
|
||||||
|
// If the frame has been transformed, and the content has any children, we
|
||||||
|
// should create an Accessible so that we can account for the transform when
|
||||||
|
// calculating the Accessible's bounds using the parent process cache.
|
||||||
|
// Ditto for content which is position: fixed or sticky.
|
||||||
|
// However, don't do this for XUL widgets, as this breaks XUL a11y code
|
||||||
|
// expectations in some cases. XUL widgets are only used in the parent
|
||||||
|
// process and can't be cached anyway.
|
||||||
|
return aContent->HasChildren() && !aContent->IsXULElement() &&
|
||||||
|
(frame->IsTransformed() || frame->IsStickyPositioned() ||
|
||||||
|
(frame->StyleDisplay()->mPosition == StylePositionProperty::Fixed &&
|
||||||
|
nsLayoutUtils::IsReallyFixedPos(frame)));
|
||||||
|
}
|
||||||
|
|
||||||
bool nsAccessibilityService::ShouldCreateImgAccessible(
|
bool nsAccessibilityService::ShouldCreateImgAccessible(
|
||||||
mozilla::dom::Element* aElement, DocAccessible* aDocument) {
|
mozilla::dom::Element* aElement, DocAccessible* aDocument) {
|
||||||
// The element must have a layout frame for us to proceed. If there is no
|
// The element must have a layout frame for us to proceed. If there is no
|
||||||
|
@ -1110,14 +1124,23 @@ LocalAccessible* nsAccessibilityService::CreateAccessible(
|
||||||
|
|
||||||
const nsRoleMapEntry* roleMapEntry = aria::GetRoleMap(content->AsElement());
|
const nsRoleMapEntry* roleMapEntry = aria::GetRoleMap(content->AsElement());
|
||||||
|
|
||||||
// If the element is focusable or global ARIA attribute is applied to it or
|
|
||||||
// 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 (roleMapEntry && (roleMapEntry->Is(nsGkAtoms::presentation) ||
|
||||||
roleMapEntry->Is(nsGkAtoms::none))) {
|
roleMapEntry->Is(nsGkAtoms::none))) {
|
||||||
if (!MustBeAccessible(content, document)) return nullptr;
|
if (MustBeAccessible(content, document)) {
|
||||||
|
// If the element is focusable, a global ARIA attribute is applied to it
|
||||||
roleMapEntry = nullptr;
|
// or it is referenced by an ARIA relationship, then treat
|
||||||
|
// role="presentation" on the element as if the role is not there.
|
||||||
|
roleMapEntry = nullptr;
|
||||||
|
} else if (MustBeGenericAccessible(content, document)) {
|
||||||
|
// Clear roleMapEntry so that we use the generic role specified below.
|
||||||
|
// Otherwise, we'd expose roles::NOTHING as specified for presentation in
|
||||||
|
// ARIAMap.
|
||||||
|
roleMapEntry = nullptr;
|
||||||
|
newAcc = new EnumRoleHyperTextAccessible<roles::TEXT_CONTAINER>(content,
|
||||||
|
document);
|
||||||
|
} else {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!newAcc && content->IsHTMLElement()) { // HTML accessibles
|
if (!newAcc && content->IsHTMLElement()) { // HTML accessibles
|
||||||
|
@ -1304,6 +1327,9 @@ LocalAccessible* nsAccessibilityService::CreateAccessible(
|
||||||
// Interesting generic non-HTML container
|
// Interesting generic non-HTML container
|
||||||
newAcc = new AccessibleWrap(content, document);
|
newAcc = new AccessibleWrap(content, document);
|
||||||
}
|
}
|
||||||
|
} else if (!newAcc && MustBeGenericAccessible(content, document)) {
|
||||||
|
newAcc = new EnumRoleHyperTextAccessible<roles::TEXT_CONTAINER>(content,
|
||||||
|
document);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newAcc) {
|
if (newAcc) {
|
||||||
|
|
|
@ -131,7 +131,7 @@ addAccessibleTask(
|
||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
async function(browser, docAcc) {
|
async function(browser, docAcc) {
|
||||||
const tree = { SECTION: [{ PARAGRAPH: [{ TEXT_LEAF: [] }] }] };
|
const tree = { TEXT_CONTAINER: [{ PARAGRAPH: [{ TEXT_LEAF: [] }] }] };
|
||||||
|
|
||||||
const divWithTransform = findAccessibleChildByID(docAcc, "container")
|
const divWithTransform = findAccessibleChildByID(docAcc, "container")
|
||||||
.firstChild;
|
.firstChild;
|
||||||
|
@ -159,7 +159,7 @@ addAccessibleTask(
|
||||||
`,
|
`,
|
||||||
async function(browser, docAcc) {
|
async function(browser, docAcc) {
|
||||||
let divToTransform = findAccessibleChildByID(docAcc, "div-to-transform");
|
let divToTransform = findAccessibleChildByID(docAcc, "div-to-transform");
|
||||||
ok(!divToTransform, "There should not be a section accessible.");
|
ok(!divToTransform, "There should not be a div accessible.");
|
||||||
|
|
||||||
// Translate the div.
|
// Translate the div.
|
||||||
await invokeContentTask(browser, [], () => {
|
await invokeContentTask(browser, [], () => {
|
||||||
|
@ -171,7 +171,7 @@ addAccessibleTask(
|
||||||
// Verify that the SECTION accessible appeared after we gave it a transform.
|
// Verify that the SECTION accessible appeared after we gave it a transform.
|
||||||
divToTransform = findAccessibleChildByID(docAcc, "div-to-transform");
|
divToTransform = findAccessibleChildByID(docAcc, "div-to-transform");
|
||||||
const tree = {
|
const tree = {
|
||||||
SECTION: [{ PARAGRAPH: [{ TEXT_LEAF: [] }] }],
|
TEXT_CONTAINER: [{ PARAGRAPH: [{ TEXT_LEAF: [] }] }],
|
||||||
};
|
};
|
||||||
testAccessibleTree(divToTransform, tree);
|
testAccessibleTree(divToTransform, tree);
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,11 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
loadScripts({ name: "layout.js", dir: MOCHITESTS_DIR });
|
/* import-globals-from ../../mochitest/role.js */
|
||||||
|
loadScripts(
|
||||||
|
{ name: "layout.js", dir: MOCHITESTS_DIR },
|
||||||
|
{ name: "role.js", dir: MOCHITESTS_DIR }
|
||||||
|
);
|
||||||
requestLongerTimeout(2);
|
requestLongerTimeout(2);
|
||||||
|
|
||||||
const appUnitsPerDevPixel = 60;
|
const appUnitsPerDevPixel = 60;
|
||||||
|
@ -320,13 +324,17 @@ addAccessibleTask(
|
||||||
*/
|
*/
|
||||||
addAccessibleTask(
|
addAccessibleTask(
|
||||||
`
|
`
|
||||||
<div id="fixed" role="presentation" style="position: fixed;">fixed</div>
|
<table id="fixed" role="presentation" style="position: fixed;">
|
||||||
|
<tr><th>fixed</th></tr>
|
||||||
|
</table>
|
||||||
<div id="mutate" role="presentation">mutate</div>
|
<div id="mutate" role="presentation">mutate</div>
|
||||||
<hr style="height: 200vh;">
|
<hr style="height: 200vh;">
|
||||||
<p>bottom</p>
|
<p>bottom</p>
|
||||||
`,
|
`,
|
||||||
async function(browser, docAcc) {
|
async function(browser, docAcc) {
|
||||||
ok(findAccessibleChildByID(docAcc, "fixed"), "fixed is accessible");
|
const fixed = findAccessibleChildByID(docAcc, "fixed");
|
||||||
|
ok(fixed, "fixed is accessible");
|
||||||
|
isnot(fixed.role, ROLE_TABLE, "fixed doesn't have ROLE_TABLE");
|
||||||
ok(!findAccessibleChildByID(docAcc, "mutate"), "mutate inaccessible");
|
ok(!findAccessibleChildByID(docAcc, "mutate"), "mutate inaccessible");
|
||||||
info("Setting position: fixed on mutate");
|
info("Setting position: fixed on mutate");
|
||||||
let shown = waitForEvent(EVENT_SHOW, "mutate");
|
let shown = waitForEvent(EVENT_SHOW, "mutate");
|
||||||
|
|
Загрузка…
Ссылка в новой задаче