Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/devtools/server/actors/accessibility/audit/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 14 kB image not shown  

Quelle  keyboard.js   Sprache: JAVA

 
/* 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";

loader.lazyRequireGetter(
  this,
  "CssLogic",
  "resource://devtools/server/actors/inspector/css-logic.js",
  true
);
loader.lazyRequireGetter(
  this,
  "getMatchingCSSRules",
  "resource://devtools/shared/inspector/css-logic.js",
  true
);
loader.lazyRequireGetter(
  this,
  "nodeConstants",
  "resource://devtools/shared/dom-node-constants.js"
);
loader.lazyRequireGetter(
  this,
  ["isDefunct""getAriaRoles"],
  "resource://devtools/server/actors/utils/accessibility.js",
  true
);

const {
  accessibility: {
    AUDIT_TYPE: { KEYBOARD },
    ISSUE_TYPE: {
      [KEYBOARD]: {
        FOCUSABLE_NO_SEMANTICS,
        FOCUSABLE_POSITIVE_TABINDEX,
        INTERACTIVE_NO_ACTION,
        INTERACTIVE_NOT_FOCUSABLE,
        MOUSE_INTERACTIVE_ONLY,
        NO_FOCUS_VISIBLE,
      },
    },
    SCORES: { FAIL, WARNING },
  },
} = require("resource://devtools/shared/constants.js");

// Accessible action for showing long description.
const CLICK_ACTION = "click";

/**
 * 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 KEYBOARD_FOCUSABLE_ROLES = new Set([
  Ci.nsIAccessibleRole.ROLE_BUTTONMENU,
  Ci.nsIAccessibleRole.ROLE_CHECKBUTTON,
  Ci.nsIAccessibleRole.ROLE_COMBOBOX,
  Ci.nsIAccessibleRole.ROLE_EDITCOMBOBOX,
  Ci.nsIAccessibleRole.ROLE_ENTRY,
  Ci.nsIAccessibleRole.ROLE_LINK,
  Ci.nsIAccessibleRole.ROLE_LISTBOX,
  Ci.nsIAccessibleRole.ROLE_PASSWORD_TEXT,
  Ci.nsIAccessibleRole.ROLE_PUSHBUTTON,
  Ci.nsIAccessibleRole.ROLE_RADIOBUTTON,
  Ci.nsIAccessibleRole.ROLE_SLIDER,
  Ci.nsIAccessibleRole.ROLE_SPINBUTTON,
  Ci.nsIAccessibleRole.ROLE_SUMMARY,
  Ci.nsIAccessibleRole.ROLE_SWITCH,
  Ci.nsIAccessibleRole.ROLE_TOGGLE_BUTTON,
]);

const INTERACTIVE_ROLES = new Set([
  ...KEYBOARD_FOCUSABLE_ROLES,
  Ci.nsIAccessibleRole.ROLE_CHECK_MENU_ITEM,
  Ci.nsIAccessibleRole.ROLE_CHECK_RICH_OPTION,
  Ci.nsIAccessibleRole.ROLE_COMBOBOX_OPTION,
  Ci.nsIAccessibleRole.ROLE_MENUITEM,
  Ci.nsIAccessibleRole.ROLE_OPTION,
  Ci.nsIAccessibleRole.ROLE_OUTLINE,
  Ci.nsIAccessibleRole.ROLE_OUTLINEITEM,
  Ci.nsIAccessibleRole.ROLE_PAGETAB,
  Ci.nsIAccessibleRole.ROLE_PARENT_MENUITEM,
  Ci.nsIAccessibleRole.ROLE_RADIO_MENU_ITEM,
  Ci.nsIAccessibleRole.ROLE_RICH_OPTION,
]);

const INTERACTIVE_IF_FOCUSABLE_ROLES = new Set([
  // If article is focusable, we can assume it is inside a feed.
  Ci.nsIAccessibleRole.ROLE_ARTICLE,
  // Column header can be focusable.
  Ci.nsIAccessibleRole.ROLE_COLUMNHEADER,
  Ci.nsIAccessibleRole.ROLE_GRID_CELL,
  Ci.nsIAccessibleRole.ROLE_MENUBAR,
  Ci.nsIAccessibleRole.ROLE_MENUPOPUP,
  Ci.nsIAccessibleRole.ROLE_PAGETABLIST,
  // Row header can be focusable.
  Ci.nsIAccessibleRole.ROLE_ROWHEADER,
  Ci.nsIAccessibleRole.ROLE_SCROLLBAR,
  Ci.nsIAccessibleRole.ROLE_SEPARATOR,
  Ci.nsIAccessibleRole.ROLE_TOOLBAR,
]);

/**
 * 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 accessible is focusable with the keyboard.
 *
 * @param   {nsIAccessible} accessible
 *          Accessible for which to determine if it is keyboard focusable.
 *
 * @returns {Boolean}
 *          True if focusable with the keyboard.
 */

function isKeyboardFocusable(accessible) {
  const state = {};
  accessible.getState(state, {});
  // State will be focusable even if the tabindex is negative.
  return (
    state.value & Ci.nsIAccessibleStates.STATE_FOCUSABLE &&
    // Platform accessibility will still report STATE_FOCUSABLE even with the
    // tabindex="-1" so we need to check that it is >= 0 to be considered
    // keyboard focusable.
    accessible.DOMNode.tabIndex > -1
  );
}

/**
 * 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 = getMatchingCSSRules(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 = getMatchingCSSRules(currentNode);
  const properties = new Set();
  for (const rule of tempRules) {
    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.
  if (!isKeyboardFocusable(accessible)) {
    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 (!KEYBOARD_FOCUSABLE_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;
  }

  if (isKeyboardFocusable(accessible)) {
    return null;
  }

  const ariaRoles = getAriaRoles(accessible);
  if (
    ariaRoles &&
    (ariaRoles.includes("combobox") || ariaRoles.includes("listbox"))
  ) {
    // Do not force ARIA combobox or listbox to be focusable.
    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) ||
    // Visible listboxes will have focusable state when inside comboboxes.
    accessible.role === Ci.nsIAccessibleRole.ROLE_COMBOBOX_LIST
  ) {
    return null;
  }

  if (isKeyboardFocusable(accessible)) {
    if (INTERACTIVE_IF_FOCUSABLE_ROLES.has(accessible.role)) {
      return null;
    }

    // ROLE_TABLE is used for grids too which are considered interactive.
    if (accessible.role === Ci.nsIAccessibleRole.ROLE_TABLE) {
      const ariaRoles = getAriaRoles(accessible);
      if (ariaRoles && ariaRoles.includes("grid")) {
        return null;
      }
    }

    return { score: WARNING, issue: FOCUSABLE_NO_SEMANTICS };
  }

  const state = {};
  accessible.getState(state, {});
  if (
    // Ignore text leafs.
    accessible.role === Ci.nsIAccessibleRole.ROLE_TEXT_LEAF ||
    // Ignore accessibles with no accessible actions.
    accessible.actionCount === 0 ||
    // Ignore labels that have a label for relation with their target because
    // they are clickable.
    (accessible.role === Ci.nsIAccessibleRole.ROLE_LABEL &&
      accessible.getRelationByType(Ci.nsIAccessibleRelation.RELATION_LABEL_FOR)
        .targetsCount > 0) ||
    // Ignore images that are inside an anchor (have linked state).
    (accessible.role === Ci.nsIAccessibleRole.ROLE_GRAPHIC &&
      state.value & Ci.nsIAccessibleStates.STATE_LINKED)
  ) {
    return null;
  }

  // Ignore anything but a click action in the list of actions.
  for (let i = 0; i < accessible.actionCount; i++) {
    if (accessible.getActionName(i) === CLICK_ACTION) {
      return { score: FAIL, issue: MOUSE_INTERACTIVE_ONLY };
    }
  }

  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;
  }

  if (!isKeyboardFocusable(accessible)) {
    return null;
  }

  if (DOMNode.tabIndex > 0) {
    return { score: WARNING, issue: FOCUSABLE_POSITIVE_TABINDEX };
  }

  return null;
}

function auditKeyboard(accessible) {
  if (isDefunct(accessible)) {
    return null;
  }
  // 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;

Messung V0.5
C=96 H=100 G=97

¤ Dauer der Verarbeitung: 0.12 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.