tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

accessibility.js (6159B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 "use strict";
      6 
      7 loader.lazyRequireGetter(
      8  this,
      9  "colorUtils",
     10  "resource://devtools/shared/css/color.js",
     11  true
     12 );
     13 const {
     14  accessibility: {
     15    SCORES: { FAIL, AA, AAA },
     16  },
     17 } = require("resource://devtools/shared/constants.js");
     18 
     19 /**
     20 * Mapping of text size to contrast ratio score levels
     21 */
     22 const LEVELS = {
     23  LARGE_TEXT: { AA: 3, AAA: 4.5 },
     24  REGULAR_TEXT: { AA: 4.5, AAA: 7 },
     25 };
     26 
     27 /**
     28 * Mapping of large text size to CSS pixel value
     29 */
     30 const LARGE_TEXT = {
     31  // CSS pixel value (constant) that corresponds to 14 point text size which defines large
     32  // text when font text is bold (font weight is greater than or equal to 600).
     33  BOLD_LARGE_TEXT_MIN_PIXELS: 18.66,
     34  // CSS pixel value (constant) that corresponds to 18 point text size which defines large
     35  // text for normal text (e.g. not bold).
     36  LARGE_TEXT_MIN_PIXELS: 24,
     37 };
     38 
     39 /**
     40 * Get contrast ratio score based on WCAG criteria.
     41 *
     42 * @param  {number} ratio
     43 *         Value of the contrast ratio for a given accessible object.
     44 * @param  {boolean} isLargeText
     45 *         True if the accessible object contains large text.
     46 * @return {string}
     47 *         Value that represents calculated contrast ratio score.
     48 */
     49 function getContrastRatioScore(ratio, isLargeText) {
     50  const levels = isLargeText ? LEVELS.LARGE_TEXT : LEVELS.REGULAR_TEXT;
     51 
     52  let score = FAIL;
     53  if (ratio >= levels.AAA) {
     54    score = AAA;
     55  } else if (ratio >= levels.AA) {
     56    score = AA;
     57  }
     58 
     59  return score;
     60 }
     61 
     62 /**
     63 * Get calculated text style properties from a node's computed style, if possible.
     64 *
     65 * @param  {object} computedStyle
     66 *         Computed style using which text styling information is to be calculated.
     67 *         - fontSize   {String}
     68 *                      Font size of the text
     69 *         - fontWeight {String}
     70 *                      Font weight of the text
     71 *         - color      {String}
     72 *                      Rgb color of the text
     73 *         - opacity    {String} Optional
     74 *                      Opacity of the text
     75 * @return {object}
     76 *         Color and text size information for a given DOM node.
     77 */
     78 function getTextProperties(computedStyle) {
     79  const { color, fontSize, fontWeight } = computedStyle;
     80  let { r, g, b, a } = InspectorUtils.colorToRGBA(color);
     81 
     82  // If the element has opacity in addition to background alpha value, take it
     83  // into account. TODO: this does not handle opacity set on ancestor elements
     84  // (see bug https://bugzilla.mozilla.org/show_bug.cgi?id=1544721).
     85  const opacity = computedStyle.opacity
     86    ? parseFloat(computedStyle.opacity)
     87    : null;
     88  if (opacity) {
     89    a = opacity * a;
     90  }
     91 
     92  const textRgbaColor = new colorUtils.CssColor(
     93    `rgba(${r}, ${g}, ${b}, ${a})`,
     94    true
     95  );
     96  // TODO: For cases where text color is transparent, it likely comes from the color of
     97  // the background that is underneath it (commonly from background-clip: text
     98  // property). With some additional investigation it might be possible to calculate the
     99  // color contrast where the color of the background is used as text color and the
    100  // color of the ancestor's background is used as its background.
    101  if (textRgbaColor.isTransparent()) {
    102    return null;
    103  }
    104 
    105  const isBoldText = parseInt(fontWeight, 10) >= 600;
    106  const size = parseFloat(fontSize);
    107  const isLargeText =
    108    size >=
    109    (isBoldText
    110      ? LARGE_TEXT.BOLD_LARGE_TEXT_MIN_PIXELS
    111      : LARGE_TEXT.LARGE_TEXT_MIN_PIXELS);
    112 
    113  return {
    114    color: [r, g, b, a],
    115    isLargeText,
    116    isBoldText,
    117    size,
    118    opacity,
    119  };
    120 }
    121 
    122 /**
    123 * Calculates contrast ratio or range of contrast ratios of the referenced DOM node
    124 * against the given background color data. If background is multi-colored, return a
    125 * range, otherwise a single contrast ratio.
    126 *
    127 * @param  {object} backgroundColorData
    128 *         Object with one or more of the following properties:
    129 *         - value              {Array}
    130 *                              rgba array for single color background
    131 *         - min                {Array}
    132 *                              min luminance rgba array for multi color background
    133 *         - max                {Array}
    134 *                              max luminance rgba array for multi color background
    135 * @param  {object}  textData
    136 *         - color              {Array}
    137 *                              rgba array for text of referenced DOM node
    138 *         - isLargeText        {Boolean}
    139 *                              True if text of referenced DOM node is large
    140 * @return {object}
    141 *         An object that may contain one or more of the following fields: error,
    142 *         isLargeText, value, min, max values for contrast.
    143 */
    144 function getContrastRatioAgainstBackground(
    145  backgroundColorData,
    146  { color, isLargeText }
    147 ) {
    148  if (backgroundColorData.value) {
    149    const value = colorUtils.calculateContrastRatio(
    150      backgroundColorData.value,
    151      color
    152    );
    153    return {
    154      value,
    155      color,
    156      backgroundColor: backgroundColorData.value,
    157      isLargeText,
    158      score: getContrastRatioScore(value, isLargeText),
    159    };
    160  }
    161 
    162  let { min: backgroundColorMin, max: backgroundColorMax } =
    163    backgroundColorData;
    164  let min = colorUtils.calculateContrastRatio(backgroundColorMin, color);
    165  let max = colorUtils.calculateContrastRatio(backgroundColorMax, color);
    166 
    167  // Flip minimum and maximum contrast ratios if necessary.
    168  if (min > max) {
    169    [min, max] = [max, min];
    170    [backgroundColorMin, backgroundColorMax] = [
    171      backgroundColorMax,
    172      backgroundColorMin,
    173    ];
    174  }
    175 
    176  const score = getContrastRatioScore(min, isLargeText);
    177 
    178  return {
    179    min,
    180    max,
    181    color,
    182    backgroundColorMin,
    183    backgroundColorMax,
    184    isLargeText,
    185    score,
    186    scoreMin: score,
    187    scoreMax: getContrastRatioScore(max, isLargeText),
    188  };
    189 }
    190 
    191 exports.getContrastRatioScore = getContrastRatioScore;
    192 exports.getTextProperties = getTextProperties;
    193 exports.getContrastRatioAgainstBackground = getContrastRatioAgainstBackground;
    194 exports.LARGE_TEXT = LARGE_TEXT;