mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-19 16:25:38 +00:00
Bug 1564968 - adding keyboard audit type serverside support. r=nchevobbe
Differential Revision: https://phabricator.services.mozilla.com/D43442 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
830e72a287
commit
07581454e5
@ -62,11 +62,10 @@ function audit(state = getInitialState(), action) {
|
||||
[filter]: isToggledToActive,
|
||||
};
|
||||
|
||||
if (
|
||||
isToggledToActive &&
|
||||
!filters[FILTERS.ALL] &&
|
||||
Object.values(AUDIT_TYPE).every(filterKey => filters[filterKey])
|
||||
) {
|
||||
const allAuditTypesActive = Object.values(AUDIT_TYPE)
|
||||
.filter(filterKey => filters.hasOwnProperty(filterKey))
|
||||
.every(filterKey => filters[filterKey]);
|
||||
if (isToggledToActive && !filters[FILTERS.ALL] && allAuditTypesActive) {
|
||||
filters[FILTERS.ALL] = true;
|
||||
} else if (!isToggledToActive && filters[FILTERS.ALL]) {
|
||||
filters[FILTERS.ALL] = false;
|
||||
|
@ -17,6 +17,12 @@ loader.lazyRequireGetter(
|
||||
"devtools/server/actors/accessibility/audit/contrast",
|
||||
true
|
||||
);
|
||||
loader.lazyRequireGetter(
|
||||
this,
|
||||
"auditKeyboard",
|
||||
"devtools/server/actors/accessibility/audit/keyboard",
|
||||
true
|
||||
);
|
||||
loader.lazyRequireGetter(
|
||||
this,
|
||||
"auditTextLabel",
|
||||
@ -486,6 +492,9 @@ const AccessibleActor = ActorClassWithSpec(accessibleSpec, {
|
||||
switch (type) {
|
||||
case AUDIT_TYPE.CONTRAST:
|
||||
return this._getContrastRatio();
|
||||
case AUDIT_TYPE.KEYBOARD:
|
||||
// Determine if keyboard accessibility is lacking where it is necessary.
|
||||
return auditKeyboard(this.rawAccessible);
|
||||
case AUDIT_TYPE.TEXT_LABEL:
|
||||
// Determine if text alternative is missing for an accessible where it
|
||||
// is necessary.
|
||||
|
416
devtools/server/actors/accessibility/audit/keyboard.js
Normal file
416
devtools/server/actors/accessibility/audit/keyboard.js
Normal file
@ -0,0 +1,416 @@
|
||||
/* 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";
|
||||
|
||||
const { Ci, Cu } = require("chrome");
|
||||
loader.lazyRequireGetter(
|
||||
this,
|
||||
"CssLogic",
|
||||
"devtools/server/actors/inspector/css-logic",
|
||||
true
|
||||
);
|
||||
loader.lazyRequireGetter(
|
||||
this,
|
||||
"getCSSStyleRules",
|
||||
"devtools/shared/inspector/css-logic",
|
||||
true
|
||||
);
|
||||
loader.lazyRequireGetter(this, "InspectorUtils", "InspectorUtils");
|
||||
loader.lazyRequireGetter(
|
||||
this,
|
||||
"nodeConstants",
|
||||
"devtools/shared/dom-node-constants"
|
||||
);
|
||||
|
||||
const {
|
||||
accessibility: {
|
||||
AUDIT_TYPE: { KEYBOARD },
|
||||
ISSUE_TYPE: {
|
||||
[KEYBOARD]: {
|
||||
FOCUSABLE_NO_SEMANTICS,
|
||||
FOCUSABLE_POSITIVE_TABINDEX,
|
||||
INTERACTIVE_NO_ACTION,
|
||||
INTERACTIVE_NOT_FOCUSABLE,
|
||||
NO_FOCUS_VISIBLE,
|
||||
},
|
||||
},
|
||||
SCORES: { FAIL, WARNING },
|
||||
},
|
||||
} = require("devtools/shared/constants");
|
||||
|
||||
// Specified by the author CSS rule type.
|
||||
const STYLE_RULE = 1;
|
||||
|
||||
/**
|
||||
* Focus specific pseudo classes that the keyboard audit simulates to determine
|
||||
* focus styling.
|
||||
*/
|
||||
const FOCUS_PSEUDO_CLASS = ":focus";
|
||||
const MOZ_FOCUSRING_PSEUDO_CLASS = ":-moz-focusring";
|
||||
|
||||
const INTERACTIVE_ROLES = new Set([
|
||||
Ci.nsIAccessibleRole.ROLE_BUTTONMENU,
|
||||
Ci.nsIAccessibleRole.ROLE_CHECKBUTTON,
|
||||
Ci.nsIAccessibleRole.ROLE_CHECK_MENU_ITEM,
|
||||
Ci.nsIAccessibleRole.ROLE_CHECK_RICH_OPTION,
|
||||
Ci.nsIAccessibleRole.ROLE_COMBOBOX,
|
||||
Ci.nsIAccessibleRole.ROLE_COMBOBOX_OPTION,
|
||||
Ci.nsIAccessibleRole.ROLE_EDITCOMBOBOX,
|
||||
Ci.nsIAccessibleRole.ROLE_ENTRY,
|
||||
Ci.nsIAccessibleRole.ROLE_LINK,
|
||||
Ci.nsIAccessibleRole.ROLE_LISTBOX,
|
||||
Ci.nsIAccessibleRole.ROLE_MENUITEM,
|
||||
Ci.nsIAccessibleRole.ROLE_OPTION,
|
||||
Ci.nsIAccessibleRole.ROLE_PAGETAB,
|
||||
Ci.nsIAccessibleRole.ROLE_PASSWORD_TEXT,
|
||||
Ci.nsIAccessibleRole.ROLE_PUSHBUTTON,
|
||||
Ci.nsIAccessibleRole.ROLE_RADIOBUTTON,
|
||||
Ci.nsIAccessibleRole.ROLE_RADIO_MENU_ITEM,
|
||||
Ci.nsIAccessibleRole.ROLE_RICH_OPTION,
|
||||
Ci.nsIAccessibleRole.ROLE_SLIDER,
|
||||
Ci.nsIAccessibleRole.ROLE_SPINBUTTON,
|
||||
Ci.nsIAccessibleRole.ROLE_SWITCH,
|
||||
Ci.nsIAccessibleRole.ROLE_TOGGLE_BUTTON,
|
||||
]);
|
||||
|
||||
/**
|
||||
* Determine if a node is dead or is not an element node.
|
||||
*
|
||||
* @param {DOMNode} node
|
||||
* Node to be tested for validity.
|
||||
*
|
||||
* @returns {Boolean}
|
||||
* True if the node is either dead or is not an element node.
|
||||
*/
|
||||
function isInvalidNode(node) {
|
||||
return (
|
||||
!node ||
|
||||
Cu.isDeadWrapper(node) ||
|
||||
node.nodeType !== nodeConstants.ELEMENT_NODE ||
|
||||
!node.ownerGlobal
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a current node has focus specific styling by applying a
|
||||
* focus-related pseudo class (such as :focus or :-moz-focusring) to a focusable
|
||||
* node.
|
||||
*
|
||||
* @param {DOMNode} focusableNode
|
||||
* Node to apply focus-related pseudo class to.
|
||||
* @param {DOMNode} currentNode
|
||||
* Node to be checked for having focus specific styling.
|
||||
* @param {String} pseudoClass
|
||||
* A focus related pseudo-class to be simulated for style comparison.
|
||||
*
|
||||
* @returns {Boolean}
|
||||
* True if the currentNode has focus specific styling.
|
||||
*/
|
||||
function hasStylesForFocusRelatedPseudoClass(
|
||||
focusableNode,
|
||||
currentNode,
|
||||
pseudoClass
|
||||
) {
|
||||
const defaultRules = getCSSStyleRules(currentNode);
|
||||
|
||||
InspectorUtils.addPseudoClassLock(focusableNode, pseudoClass);
|
||||
|
||||
// Determine a set of properties that are specific to CSS rules that are only
|
||||
// present when a focus related pseudo-class is locked in.
|
||||
const tempRules = getCSSStyleRules(currentNode);
|
||||
const properties = new Set();
|
||||
for (const rule of tempRules) {
|
||||
if (rule.type !== STYLE_RULE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!defaultRules.includes(rule)) {
|
||||
for (let index = 0; index < rule.style.length; index++) {
|
||||
properties.add(rule.style.item(index));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If there are no focus specific CSS rules or properties, currentNode does
|
||||
// node have any focus specific styling, we are done.
|
||||
if (properties.size === 0) {
|
||||
InspectorUtils.removePseudoClassLock(focusableNode, pseudoClass);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Determine values for properties that are focus specific.
|
||||
const tempStyle = CssLogic.getComputedStyle(currentNode);
|
||||
const focusStyle = {};
|
||||
for (const name of properties.values()) {
|
||||
focusStyle[name] = tempStyle.getPropertyValue(name);
|
||||
}
|
||||
|
||||
InspectorUtils.removePseudoClassLock(focusableNode, pseudoClass);
|
||||
|
||||
// If values for focus specific properties are different from default style
|
||||
// values, assume we have focus spefic styles for the currentNode.
|
||||
const defaultStyle = CssLogic.getComputedStyle(currentNode);
|
||||
for (const name of properties.values()) {
|
||||
if (defaultStyle.getPropertyValue(name) !== focusStyle[name]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an element node (currentNode) has distinct focus styling. This
|
||||
* function also takes into account a case when focus styling is applied to a
|
||||
* descendant too.
|
||||
*
|
||||
* @param {DOMNode} focusableNode
|
||||
* Node to apply focus-related pseudo class to.
|
||||
* @param {DOMNode} currentNode
|
||||
* Node to be checked for having focus specific styling.
|
||||
*
|
||||
* @returns {Boolean}
|
||||
* True if the node or its descendant has distinct focus styling.
|
||||
*/
|
||||
function hasFocusStyling(focusableNode, currentNode) {
|
||||
if (isInvalidNode(currentNode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if an element node has distinct :-moz-focusring styling.
|
||||
const hasStylesForMozFocusring = hasStylesForFocusRelatedPseudoClass(
|
||||
focusableNode,
|
||||
currentNode,
|
||||
MOZ_FOCUSRING_PSEUDO_CLASS
|
||||
);
|
||||
if (hasStylesForMozFocusring) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if an element node has distinct :focus styling.
|
||||
const hasStylesForFocus = hasStylesForFocusRelatedPseudoClass(
|
||||
focusableNode,
|
||||
currentNode,
|
||||
FOCUS_PSEUDO_CLASS
|
||||
);
|
||||
if (hasStylesForFocus) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If no element specific focus styles where found, check if its element
|
||||
// children have them.
|
||||
for (
|
||||
let child = currentNode.firstElementChild;
|
||||
child;
|
||||
child = currentNode.nextnextElementSibling
|
||||
) {
|
||||
if (hasFocusStyling(focusableNode, child)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* A rule that determines if a focusable accessible object has appropriate focus
|
||||
* styling.
|
||||
*
|
||||
* @param {nsIAccessible} accessible
|
||||
* Accessible to be checked for being focusable and having focus
|
||||
* styling.
|
||||
*
|
||||
* @return {null|Object}
|
||||
* Null if accessible has keyboard focus styling, audit report object
|
||||
* otherwise.
|
||||
*/
|
||||
function focusStyleRule(accessible) {
|
||||
const { DOMNode } = accessible;
|
||||
if (isInvalidNode(DOMNode)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Ignore non-focusable elements.
|
||||
const state = {};
|
||||
accessible.getState(state, {});
|
||||
if (!(state.value & Ci.nsIAccessibleStates.STATE_FOCUSABLE)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (hasFocusStyling(DOMNode, DOMNode)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// If no browser or author focus styling was found, check if the node is a
|
||||
// widget that is themed by platform native theme.
|
||||
if (InspectorUtils.isElementThemed(DOMNode)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return { score: WARNING, issue: NO_FOCUS_VISIBLE };
|
||||
}
|
||||
|
||||
/**
|
||||
* A rule that determines if an interactive accessible has any associated
|
||||
* accessible actions with it. If the element is interactive but and has no
|
||||
* actions, assistive technology users will not be able to interact with it.
|
||||
*
|
||||
* @param {nsIAccessible} accessible
|
||||
* Accessible to be checked for being interactive and having accessible
|
||||
* actions.
|
||||
*
|
||||
* @return {null|Object}
|
||||
* Null if accessible is not interactive or if it is and it has
|
||||
* accessible action associated with it, audit report object otherwise.
|
||||
*/
|
||||
function interactiveRule(accessible) {
|
||||
if (!INTERACTIVE_ROLES.has(accessible.role)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (accessible.actionCount > 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return { score: FAIL, issue: INTERACTIVE_NO_ACTION };
|
||||
}
|
||||
|
||||
/**
|
||||
* A rule that determines if an interactive accessible is also focusable when
|
||||
* not disabled.
|
||||
*
|
||||
* @param {nsIAccessible} accessible
|
||||
* Accessible to be checked for being interactive and being focusable
|
||||
* when enabled.
|
||||
*
|
||||
* @return {null|Object}
|
||||
* Null if accessible is not interactive or if it is and it is focusable
|
||||
* when enabled, audit report object otherwise.
|
||||
*/
|
||||
function focusableRule(accessible) {
|
||||
if (!INTERACTIVE_ROLES.has(accessible.role)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const state = {};
|
||||
accessible.getState(state, {});
|
||||
// We only expect in interactive accessible object to be focusable if it is
|
||||
// not disabled.
|
||||
if (state.value & Ci.nsIAccessibleStates.STATE_UNAVAILABLE) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// State will be focusable even if the tabindex is negative.
|
||||
if (
|
||||
state.value & Ci.nsIAccessibleStates.STATE_FOCUSABLE &&
|
||||
accessible.DOMNode.tabIndex > -1
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return { score: FAIL, issue: INTERACTIVE_NOT_FOCUSABLE };
|
||||
}
|
||||
|
||||
/**
|
||||
* A rule that determines if a focusable accessible has an associated
|
||||
* interactive role.
|
||||
*
|
||||
* @param {nsIAccessible} accessible
|
||||
* Accessible to be checked for having an interactive role if it is
|
||||
* focusable.
|
||||
*
|
||||
* @return {null|Object}
|
||||
* Null if accessible is not interactive or if it is and it has an
|
||||
* interactive role, audit report object otherwise.
|
||||
*/
|
||||
function semanticsRule(accessible) {
|
||||
if (INTERACTIVE_ROLES.has(accessible.role)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const state = {};
|
||||
accessible.getState(state, {});
|
||||
if (state.value & Ci.nsIAccessibleStates.STATE_FOCUSABLE) {
|
||||
return { score: WARNING, issue: FOCUSABLE_NO_SEMANTICS };
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* A rule that determines if an element associated with a focusable accessible
|
||||
* has a positive tabindex.
|
||||
*
|
||||
* @param {nsIAccessible} accessible
|
||||
* Accessible to be checked for having an element with positive tabindex
|
||||
* attribute.
|
||||
*
|
||||
* @return {null|Object}
|
||||
* Null if accessible is not focusable or if it is and its element's
|
||||
* tabindex attribute is less than 1, audit report object otherwise.
|
||||
*/
|
||||
function tabIndexRule(accessible) {
|
||||
const { DOMNode } = accessible;
|
||||
if (isInvalidNode(DOMNode)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const state = {};
|
||||
accessible.getState(state, {});
|
||||
if (!(state.value & Ci.nsIAccessibleStates.STATE_FOCUSABLE)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (DOMNode.tabIndex > 0) {
|
||||
return { score: WARNING, issue: FOCUSABLE_POSITIVE_TABINDEX };
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function auditKeyboard(accessible) {
|
||||
// Do not test anything on accessible objects for documents or frames.
|
||||
if (
|
||||
accessible.role === Ci.nsIAccessibleRole.ROLE_DOCUMENT ||
|
||||
accessible.role === Ci.nsIAccessibleRole.ROLE_INTERNAL_FRAME
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check if interactive accessible can be used by the assistive
|
||||
// technology.
|
||||
let issue = interactiveRule(accessible);
|
||||
if (issue) {
|
||||
return issue;
|
||||
}
|
||||
|
||||
// Check if interactive accessible is also focusable when enabled.
|
||||
issue = focusableRule(accessible);
|
||||
if (issue) {
|
||||
return issue;
|
||||
}
|
||||
|
||||
// Check if accessible object has an element with a positive tabindex.
|
||||
issue = tabIndexRule(accessible);
|
||||
if (issue) {
|
||||
return issue;
|
||||
}
|
||||
|
||||
// Check if a focusable accessible has interactive semantics.
|
||||
issue = semanticsRule(accessible);
|
||||
if (issue) {
|
||||
return issue;
|
||||
}
|
||||
|
||||
// Check if focusable accessible has associated focus styling.
|
||||
issue = focusStyleRule(accessible);
|
||||
if (issue) {
|
||||
return issue;
|
||||
}
|
||||
|
||||
return issue;
|
||||
}
|
||||
|
||||
module.exports.auditKeyboard = auditKeyboard;
|
@ -4,6 +4,7 @@
|
||||
|
||||
DevToolsModules(
|
||||
'contrast.js',
|
||||
'keyboard.js',
|
||||
'text-label.js',
|
||||
)
|
||||
|
||||
|
@ -12,6 +12,7 @@ support-files =
|
||||
application-manifest-warnings.html
|
||||
doc_accessibility_audit.html
|
||||
doc_accessibility_infobar.html
|
||||
doc_accessibility_keyboard_audit.html
|
||||
doc_accessibility_text_label_audit_frame.html
|
||||
doc_accessibility_text_label_audit.html
|
||||
doc_accessibility.html
|
||||
@ -49,6 +50,10 @@ support-files =
|
||||
[browser_accessibility_highlighter_infobar.js]
|
||||
skip-if = (os == 'win' && processor == 'aarch64') # bug 1533184
|
||||
[browser_accessibility_infobar_show.js]
|
||||
[browser_accessibility_keyboard_audit.js]
|
||||
skip-if =
|
||||
(os == 'win' && processor == 'aarch64') || # bug 1533184
|
||||
fission # Fails intermittently under Fission.
|
||||
[browser_accessibility_infobar_audit_text_label.js]
|
||||
[browser_accessibility_node.js]
|
||||
skip-if = (os == 'win' && processor == 'aarch64') # bug 1533184
|
||||
|
@ -0,0 +1,111 @@
|
||||
/* 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";
|
||||
|
||||
/**
|
||||
* Checks functionality around text label audit for the AccessibleActor.
|
||||
*/
|
||||
|
||||
const {
|
||||
accessibility: {
|
||||
AUDIT_TYPE: { KEYBOARD },
|
||||
SCORES: { FAIL, WARNING },
|
||||
ISSUE_TYPE: {
|
||||
[KEYBOARD]: {
|
||||
FOCUSABLE_NO_SEMANTICS,
|
||||
FOCUSABLE_POSITIVE_TABINDEX,
|
||||
INTERACTIVE_NO_ACTION,
|
||||
INTERACTIVE_NOT_FOCUSABLE,
|
||||
NO_FOCUS_VISIBLE,
|
||||
},
|
||||
},
|
||||
},
|
||||
} = require("devtools/shared/constants");
|
||||
|
||||
add_task(async function() {
|
||||
const { target, walker, accessibility } = await initAccessibilityFrontForUrl(
|
||||
`${MAIN_DOMAIN}doc_accessibility_keyboard_audit.html`
|
||||
);
|
||||
|
||||
const a11yWalker = await accessibility.getWalker();
|
||||
await accessibility.enable();
|
||||
|
||||
const tests = [
|
||||
[
|
||||
"Focusable element (styled button) with no semantics.",
|
||||
"#button-1",
|
||||
{ score: WARNING, issue: FOCUSABLE_NO_SEMANTICS },
|
||||
],
|
||||
["Element (styled button) with no semantics.", "#button-2", null],
|
||||
[
|
||||
"Container element for out of order focusable element.",
|
||||
"#input-container",
|
||||
null,
|
||||
],
|
||||
[
|
||||
"Interactive element with focus out of order (-1).",
|
||||
"#input-1",
|
||||
{
|
||||
score: FAIL,
|
||||
issue: INTERACTIVE_NOT_FOCUSABLE,
|
||||
},
|
||||
],
|
||||
[
|
||||
"Interactive element with focus out of order (-1) when disabled.",
|
||||
"#input-2",
|
||||
null,
|
||||
],
|
||||
["Interactive element when disabled.", "#input-3", null],
|
||||
["Focusable interactive element.", "#input-4", null],
|
||||
[
|
||||
"Interactive accesible (link with no attributes) with no accessible actions.",
|
||||
"#link-1",
|
||||
{
|
||||
score: FAIL,
|
||||
issue: INTERACTIVE_NO_ACTION,
|
||||
},
|
||||
],
|
||||
["Interactive accessible (link with valid hred).", "#link-2", null],
|
||||
["Interactive accessible with no tabindex.", "#button-3", null],
|
||||
[
|
||||
"Interactive accessible with -1 tabindex.",
|
||||
"#button-4",
|
||||
{
|
||||
score: FAIL,
|
||||
issue: INTERACTIVE_NOT_FOCUSABLE,
|
||||
},
|
||||
],
|
||||
["Interactive accessible with 0 tabindex.", "#button-5", null],
|
||||
[
|
||||
"Interactive accessible with 1 tabindex.",
|
||||
"#button-6",
|
||||
{ score: WARNING, issue: FOCUSABLE_POSITIVE_TABINDEX },
|
||||
],
|
||||
[
|
||||
"Focusable ARIA button with no focus styling.",
|
||||
"#focusable-1",
|
||||
{ score: WARNING, issue: NO_FOCUS_VISIBLE },
|
||||
],
|
||||
["Focusable ARIA button with focus styling.", "#focusable-2", null],
|
||||
["Focusable ARIA button with browser styling.", "#focusable-3", null],
|
||||
];
|
||||
|
||||
for (const [description, selector, expected] of tests) {
|
||||
info(description);
|
||||
const node = await walker.querySelector(walker.rootNode, selector);
|
||||
const front = await a11yWalker.getAccessibleFor(node);
|
||||
const audit = await front.audit({ types: [KEYBOARD] });
|
||||
Assert.deepEqual(
|
||||
audit[KEYBOARD],
|
||||
expected,
|
||||
`Audit result for ${selector} is correct.`
|
||||
);
|
||||
}
|
||||
|
||||
await accessibility.disable();
|
||||
await waitForA11yShutdown();
|
||||
await target.destroy();
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
@ -21,6 +21,7 @@ add_task(async function() {
|
||||
childCount: 2,
|
||||
checks: {
|
||||
[AUDIT_TYPE.CONTRAST]: null,
|
||||
[AUDIT_TYPE.KEYBOARD]: null,
|
||||
[AUDIT_TYPE.TEXT_LABEL]: {
|
||||
score: SCORES.FAIL,
|
||||
issue: ISSUE_TYPE.DOCUMENT_NO_TITLE,
|
||||
@ -35,6 +36,7 @@ add_task(async function() {
|
||||
childCount: 1,
|
||||
checks: {
|
||||
[AUDIT_TYPE.CONTRAST]: null,
|
||||
[AUDIT_TYPE.KEYBOARD]: null,
|
||||
[AUDIT_TYPE.TEXT_LABEL]: null,
|
||||
},
|
||||
},
|
||||
@ -52,6 +54,7 @@ add_task(async function() {
|
||||
isLargeText: false,
|
||||
score: "fail",
|
||||
},
|
||||
[AUDIT_TYPE.KEYBOARD]: null,
|
||||
[AUDIT_TYPE.TEXT_LABEL]: null,
|
||||
},
|
||||
},
|
||||
@ -61,6 +64,7 @@ add_task(async function() {
|
||||
childCount: 1,
|
||||
checks: {
|
||||
[AUDIT_TYPE.CONTRAST]: null,
|
||||
[AUDIT_TYPE.KEYBOARD]: null,
|
||||
[AUDIT_TYPE.TEXT_LABEL]: null,
|
||||
},
|
||||
},
|
||||
@ -76,6 +80,7 @@ add_task(async function() {
|
||||
isLargeText: false,
|
||||
score: "fail",
|
||||
},
|
||||
[AUDIT_TYPE.KEYBOARD]: null,
|
||||
[AUDIT_TYPE.TEXT_LABEL]: null,
|
||||
},
|
||||
},
|
||||
|
@ -0,0 +1,33 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
#focusable-1 {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#focusable-2:focus {
|
||||
outline: none;
|
||||
border: 1px solid black;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="button-1" class="Button" tabindex="0">I should really be a button</div>
|
||||
<div id="button-2" class="Button">I should really be a button</div>
|
||||
<div id="input-container"><input id="input-1" type="text" tabindex="-1" /></div>
|
||||
<input id="input-2" type="text" tabindex="-1" disabled />
|
||||
<input id="input-3" type="text" disabled />
|
||||
<input id="input-4" type="text" />
|
||||
<a id="link-1">Though a link, I'm not interactive.</a>
|
||||
<a id="link-2" href="example.com">I'm a proper link.</a>
|
||||
<button id="button-3">Button with no tabindex</button>
|
||||
<button id="button-4" tabindex="-1">Button with -1 tabindex</button>
|
||||
<button id="button-5" tabindex="0">Button with 0 tabindex</button>
|
||||
<button id="button-6" tabindex="1">Button with 1 tabindex</button>
|
||||
<div id="focusable-1" role="button" tabindex="0">Focusable with no focus style.</div>
|
||||
<div id="focusable-2" role="button" tabindex="0">Focusable with focus style.</div>
|
||||
<div id="focusable-3" role="button" tabindex="0">Focusable with focus style.</div>
|
||||
</body>
|
||||
</html>
|
@ -13,11 +13,24 @@
|
||||
// List of audit types.
|
||||
const AUDIT_TYPE = {
|
||||
CONTRAST: "CONTRAST",
|
||||
KEYBOARD: "KEYBOARD",
|
||||
TEXT_LABEL: "TEXT_LABEL",
|
||||
};
|
||||
|
||||
// Types of issues grouped by audit types.
|
||||
const ISSUE_TYPE = {
|
||||
[AUDIT_TYPE.KEYBOARD]: {
|
||||
// Focusable accessible objects have no semantics.
|
||||
FOCUSABLE_NO_SEMANTICS: "FOCUSABLE_NO_SEMANTICS",
|
||||
// Tab index greater than 0 is provided.
|
||||
FOCUSABLE_POSITIVE_TABINDEX: "FOCUSABLE_POSITIVE_TABINDEX",
|
||||
// Interactive accesible objects do not have an associated action.
|
||||
INTERACTIVE_NO_ACTION: "INTERACTIVE_NO_ACTION",
|
||||
// Interative accessible objcets are not focusable.
|
||||
INTERACTIVE_NOT_FOCUSABLE: "INTERACTIVE_NOT_FOCUSABLE",
|
||||
// Focusable accessible objects have no focus styling.
|
||||
NO_FOCUS_VISIBLE: "NO_FOCUS_VISIBLE",
|
||||
},
|
||||
[AUDIT_TYPE.TEXT_LABEL]: {
|
||||
// <AREA> name is provided via "alt" attribute.
|
||||
AREA_NO_NAME_FROM_ALT: "AREA_NO_NAME_FROM_ALT",
|
||||
|
Loading…
Reference in New Issue
Block a user