Bug 1874684 - Part 37: Fix unified compilation. r=allstarschh
[gecko.git] / devtools / shared / accessibility.js
blobdb6983d033cc83bb25c89669adc8c1476e1cb798
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/. */
5 "use strict";
7 loader.lazyRequireGetter(
8   this,
9   "colorUtils",
10   "resource://devtools/shared/css/color.js",
11   true
13 const {
14   accessibility: {
15     SCORES: { FAIL, AA, AAA },
16   },
17 } = require("resource://devtools/shared/constants.js");
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 },
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,
39 /**
40  * Get contrast ratio score based on WCAG criteria.
41  * @param  {Number} ratio
42  *         Value of the contrast ratio for a given accessible object.
43  * @param  {Boolean} isLargeText
44  *         True if the accessible object contains large text.
45  * @return {String}
46  *         Value that represents calculated contrast ratio score.
47  */
48 function getContrastRatioScore(ratio, isLargeText) {
49   const levels = isLargeText ? LEVELS.LARGE_TEXT : LEVELS.REGULAR_TEXT;
51   let score = FAIL;
52   if (ratio >= levels.AAA) {
53     score = AAA;
54   } else if (ratio >= levels.AA) {
55     score = AA;
56   }
58   return score;
61 /**
62  * Get calculated text style properties from a node's computed style, if possible.
63  * @param  {Object} computedStyle
64  *         Computed style using which text styling information is to be calculated.
65  *         - fontSize   {String}
66  *                      Font size of the text
67  *         - fontWeight {String}
68  *                      Font weight of the text
69  *         - color      {String}
70  *                      Rgb color of the text
71  *         - opacity    {String} Optional
72  *                      Opacity of the text
73  * @return {Object}
74  *         Color and text size information for a given DOM node.
75  */
76 function getTextProperties(computedStyle) {
77   const { color, fontSize, fontWeight } = computedStyle;
78   let { r, g, b, a } = InspectorUtils.colorToRGBA(color);
80   // If the element has opacity in addition to background alpha value, take it
81   // into account. TODO: this does not handle opacity set on ancestor elements
82   // (see bug https://bugzilla.mozilla.org/show_bug.cgi?id=1544721).
83   const opacity = computedStyle.opacity
84     ? parseFloat(computedStyle.opacity)
85     : null;
86   if (opacity) {
87     a = opacity * a;
88   }
90   const textRgbaColor = new colorUtils.CssColor(
91     `rgba(${r}, ${g}, ${b}, ${a})`,
92     true
93   );
94   // TODO: For cases where text color is transparent, it likely comes from the color of
95   // the background that is underneath it (commonly from background-clip: text
96   // property). With some additional investigation it might be possible to calculate the
97   // color contrast where the color of the background is used as text color and the
98   // color of the ancestor's background is used as its background.
99   if (textRgbaColor.isTransparent()) {
100     return null;
101   }
103   const isBoldText = parseInt(fontWeight, 10) >= 600;
104   const size = parseFloat(fontSize);
105   const isLargeText =
106     size >=
107     (isBoldText
108       ? LARGE_TEXT.BOLD_LARGE_TEXT_MIN_PIXELS
109       : LARGE_TEXT.LARGE_TEXT_MIN_PIXELS);
111   return {
112     color: [r, g, b, a],
113     isLargeText,
114     isBoldText,
115     size,
116     opacity,
117   };
121  * Calculates contrast ratio or range of contrast ratios of the referenced DOM node
122  * against the given background color data. If background is multi-colored, return a
123  * range, otherwise a single contrast ratio.
125  * @param  {Object} backgroundColorData
126  *         Object with one or more of the following properties:
127  *         - value              {Array}
128  *                              rgba array for single color background
129  *         - min                {Array}
130  *                              min luminance rgba array for multi color background
131  *         - max                {Array}
132  *                              max luminance rgba array for multi color background
133  * @param  {Object}  textData
134  *         - color              {Array}
135  *                              rgba array for text of referenced DOM node
136  *         - isLargeText        {Boolean}
137  *                              True if text of referenced DOM node is large
138  * @return {Object}
139  *         An object that may contain one or more of the following fields: error,
140  *         isLargeText, value, min, max values for contrast.
141  */
142 function getContrastRatioAgainstBackground(
143   backgroundColorData,
144   { color, isLargeText }
145 ) {
146   if (backgroundColorData.value) {
147     const value = colorUtils.calculateContrastRatio(
148       backgroundColorData.value,
149       color
150     );
151     return {
152       value,
153       color,
154       backgroundColor: backgroundColorData.value,
155       isLargeText,
156       score: getContrastRatioScore(value, isLargeText),
157     };
158   }
160   let { min: backgroundColorMin, max: backgroundColorMax } =
161     backgroundColorData;
162   let min = colorUtils.calculateContrastRatio(backgroundColorMin, color);
163   let max = colorUtils.calculateContrastRatio(backgroundColorMax, color);
165   // Flip minimum and maximum contrast ratios if necessary.
166   if (min > max) {
167     [min, max] = [max, min];
168     [backgroundColorMin, backgroundColorMax] = [
169       backgroundColorMax,
170       backgroundColorMin,
171     ];
172   }
174   const score = getContrastRatioScore(min, isLargeText);
176   return {
177     min,
178     max,
179     color,
180     backgroundColorMin,
181     backgroundColorMax,
182     isLargeText,
183     score,
184     scoreMin: score,
185     scoreMax: getContrastRatioScore(max, isLargeText),
186   };
189 exports.getContrastRatioScore = getContrastRatioScore;
190 exports.getTextProperties = getTextProperties;
191 exports.getContrastRatioAgainstBackground = getContrastRatioAgainstBackground;
192 exports.LARGE_TEXT = LARGE_TEXT;