Bug 1546555 - include WCAG score as part of contrast check to only calculate once. Rename ColorContrastScores constant to SCORES to be used by all checks. r=nchevobbe
authorYura Zenevich <yura.zenevich@gmail.com>
Sat, 27 Apr 2019 13:33:27 +0000
changeset 530471 70a7b7db7b84452a83026949fccda6d10ca57869
parent 530470 660c16b89380269752e7e67ed717a4cf5dca6e5f
child 530476 b0d41799ceb31e39ea857f18f4a65e8944818c6a
child 530477 9191d987a9e12d16651aef4978cb7b59d6dc9bb8
push id11265
push userffxbld-merge
push dateMon, 13 May 2019 10:53:39 +0000
treeherdermozilla-beta@77e0fe8dbdd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnchevobbe
bugs1546555
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1546555 - include WCAG score as part of contrast check to only calculate once. Rename ColorContrastScores constant to SCORES to be used by all checks. r=nchevobbe Differential Revision: https://phabricator.services.mozilla.com/D28753
devtools/client/accessibility/components/AuditFilter.js
devtools/client/accessibility/components/ColorContrastAccessibility.js
devtools/client/accessibility/components/ContrastBadge.js
devtools/client/accessibility/test/browser/browser_accessibility_sidebar_checks.js
devtools/client/accessibility/test/jest/components/audit-filter.test.js
devtools/client/accessibility/test/jest/components/badges.test.js
devtools/client/accessibility/test/jest/components/contrast-badge.test.js
devtools/client/accessibility/test/mochitest/test_accessible_contrast.html
devtools/server/actors/accessibility/contrast.js
devtools/server/actors/accessibility/walker.js
devtools/server/actors/highlighters/utils/accessibility.js
devtools/server/tests/browser/browser_accessibility_node_audit.js
devtools/shared/constants.js
--- a/devtools/client/accessibility/components/AuditFilter.js
+++ b/devtools/client/accessibility/components/AuditFilter.js
@@ -3,29 +3,22 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const React = require("devtools/client/shared/vendor/react");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 
-const { getContrastRatioScore } = require("./ColorContrastAccessibility");
 const { isFiltered } = require("../utils/audit");
 const { FILTERS } = require("../constants");
-const { accessibility: { AUDIT_TYPE, ColorContrastScores } } =
-  require("devtools/shared/constants");
+const { accessibility: { AUDIT_TYPE, SCORES } } = require("devtools/shared/constants");
 
-function validateContrast({ error, value, min, isLargeText }) {
-  if (error) {
-    return false;
-  }
-
-  const score = getContrastRatioScore(value || min, isLargeText);
-  return score === ColorContrastScores.FAIL;
+function validateContrast({ error, score }) {
+  return !error && score === SCORES.FAIL;
 }
 
 const AUDIT_TYPE_TO_FILTER = {
   [AUDIT_TYPE.CONTRAST]: {
      filterKey: FILTERS.CONTRAST,
      validator: validateContrast,
   },
 };
--- a/devtools/client/accessibility/components/ColorContrastAccessibility.js
+++ b/devtools/client/accessibility/components/ColorContrastAccessibility.js
@@ -6,43 +6,42 @@
 
 const { Component, createFactory } = require("devtools/client/shared/vendor/react");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const { div, span, h3 } = require("devtools/client/shared/vendor/react-dom-factories");
 const LearnMoreLink = createFactory(require("./LearnMoreLink"));
 
 const { A11Y_CONTRAST_LEARN_MORE_LINK } = require("../constants");
 const { L10N } = require("../utils/l10n");
-const { accessibility: { ColorContrastScores } } = require("devtools/shared/constants");
 
 /**
  * Component that renders a colour contrast value along with a swatch preview of what the
  * text and background colours are.
  */
 class ContrastValueClass extends Component {
   static get propTypes() {
     return {
       backgroundColor: PropTypes.array.isRequired,
       color: PropTypes.array.isRequired,
-      isLargeText: PropTypes.bool.isRequired,
       value: PropTypes.number.isRequired,
+      score: PropTypes.string,
     };
   }
 
   render() {
     const {
       backgroundColor,
       color,
-      isLargeText,
       value,
+      score,
     } = this.props;
 
     const className = [
       "accessibility-contrast-value",
-      getContrastRatioScore(value, isLargeText),
+      score,
     ].join(" ");
 
     return (
       span({
         className,
         role: "presentation",
         style: {
           "--accessibility-contrast-color": `rgba(${color})`,
@@ -66,27 +65,30 @@ class ColorContrastAccessibilityClass ex
       isLargeText: PropTypes.bool.isRequired,
       color: PropTypes.array.isRequired,
       value: PropTypes.number,
       min: PropTypes.number,
       max: PropTypes.number,
       backgroundColor: PropTypes.array,
       backgroundColorMin: PropTypes.array,
       backgroundColorMax: PropTypes.array,
+      score: PropTypes.string,
+      scoreMin: PropTypes.string,
+      scoreMax: PropTypes.string,
     };
   }
 
   render() {
     const {
       error,
       isLargeText,
       color,
-      value, backgroundColor,
-      min, backgroundColorMin,
-      max, backgroundColorMax,
+      value, backgroundColor, score,
+      min, backgroundColorMin, scoreMin,
+      max, backgroundColorMax, scoreMax,
     } = this.props;
 
     const children = [];
 
     if (error) {
       children.push(span({
         className: "accessibility-color-contrast-error",
         role: "presentation",
@@ -94,27 +96,27 @@ class ColorContrastAccessibilityClass ex
 
       return (div({
         role: "presentation",
         className: "accessibility-color-contrast",
       }, ...children));
     }
 
     if (value) {
-      children.push(ContrastValue({ isLargeText, color, backgroundColor, value }));
+      children.push(ContrastValue({ score, color, backgroundColor, value }));
     } else {
       children.push(
         ContrastValue(
-          { isLargeText, color, backgroundColor: backgroundColorMin, value: min }),
+          { score: scoreMin, color, backgroundColor: backgroundColorMin, value: min }),
         div({
           role: "presentation",
           className: "accessibility-color-contrast-separator",
         }),
         ContrastValue(
-          { isLargeText, color, backgroundColor: backgroundColorMax, value: max }),
+          { score: scoreMax, color, backgroundColor: backgroundColorMax, value: max }),
       );
     }
 
     if (isLargeText) {
       children.push(
         span({
           className: "accessibility-color-contrast-large-text",
           role: "presentation",
@@ -135,25 +137,22 @@ class ColorContrastAccessibilityClass ex
   }
 }
 
 const ColorContrastAccessibility = createFactory(ColorContrastAccessibilityClass);
 
 class ContrastAnnotationClass extends Component {
   static get propTypes() {
     return {
-      isLargeText: PropTypes.bool.isRequired,
-      value: PropTypes.number,
-      min: PropTypes.number,
+      score: PropTypes.string,
     };
   }
 
   render() {
-    const { isLargeText, min, value } = this.props;
-    const score = getContrastRatioScore(value || min, isLargeText);
+    const { score } = this.props;
 
     return (
       LearnMoreLink(
         {
           className: "accessibility-color-contrast-annotation",
           href: A11Y_CONTRAST_LEARN_MORE_LINK,
           learnMoreStringKey: "accessibility.learnMore",
           l10n: L10N,
@@ -186,36 +185,12 @@ class ColorContrastCheck extends Compone
         }, L10N.getStr("accessibility.contrast.header")),
         ColorContrastAccessibility(this.props),
         !error && ContrastAnnotation(this.props)
       )
     );
   }
 }
 
-/**
- * Get contrast ratio score.
- * ratio.
- * @param  {Number} value
- *         Value of the contrast ratio for a given accessible object.
- * @param  {Boolean} isLargeText
- *         True if the accessible object contains large text.
- * @return {String}
- *         Represents the appropriate contrast ratio score.
- */
-function getContrastRatioScore(value, isLargeText) {
-  const levels = isLargeText ? { AA: 3, AAA: 4.5 } : { AA: 4.5, AAA: 7 };
-
-  let score = ColorContrastScores.FAIL;
-  if (value >= levels.AAA) {
-    score = ColorContrastScores.AAA;
-  } else if (value >= levels.AA) {
-    score = ColorContrastScores.AA;
-  }
-
-  return score;
-}
-
 module.exports = {
   ColorContrastAccessibility: ColorContrastAccessibilityClass,
   ColorContrastCheck,
-  getContrastRatioScore,
 };
--- a/devtools/client/accessibility/components/ContrastBadge.js
+++ b/devtools/client/accessibility/components/ContrastBadge.js
@@ -4,48 +4,44 @@
 "use strict";
 
 // React
 const { Component, createFactory } = require("devtools/client/shared/vendor/react");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 
 const { L10N } = require("../utils/l10n");
 
-const { getContrastRatioScore } = require("./ColorContrastAccessibility");
-const { accessibility: { ColorContrastScores } } = require("devtools/shared/constants");
+const { accessibility: { SCORES } } = require("devtools/shared/constants");
 
 loader.lazyGetter(this, "Badge", () => createFactory(require("./Badge")));
 
 const { FILTERS } = require("../constants");
 
 /**
 * Component for rendering a badge for contrast accessibliity check
 * failures association with a given accessibility object in the accessibility
 * tree.
 */
 
 class ContrastBadge extends Component {
   static get propTypes() {
     return {
       error: PropTypes.string,
-      isLargeText: PropTypes.bool.isRequired,
-      value: PropTypes.number,
-      min: PropTypes.number,
+      score: PropTypes.string,
       walker: PropTypes.object.isRequired,
     };
   }
 
   render() {
-    const { error, value, min, isLargeText, walker } = this.props;
+    const { error, score, walker } = this.props;
     if (error) {
       return null;
     }
 
-    const score = getContrastRatioScore(value || min, isLargeText);
-    if (score !== ColorContrastScores.FAIL) {
+    if (score !== SCORES.FAIL) {
       return null;
     }
 
     return Badge({
       label: L10N.getStr("accessibility.badge.contrast"),
       tooltip: L10N.getStr("accessibility.badge.contrast.tooltip"),
       filterKey: FILTERS.CONTRAST,
       walker,
--- a/devtools/client/accessibility/test/browser/browser_accessibility_sidebar_checks.js
+++ b/devtools/client/accessibility/test/browser/browser_accessibility_sidebar_checks.js
@@ -38,32 +38,34 @@ const tests = [{
   },
   expected: {
     audit: {
       "CONTRAST": {
         "value": 4.00,
         "color": [255, 0, 0, 1],
         "backgroundColor": [255, 255, 255, 1],
         "isLargeText": false,
+        "score": "fail",
       },
     },
   },
 }, {
   desc: "Check accessible representing text node in blue.",
   setup: async ({ doc }) => {
     await toggleRow(doc, 3);
     await selectRow(doc, 4);
   },
   expected: {
     audit: {
       "CONTRAST": {
         "value": 8.59,
         "color": [0, 0, 255, 1],
         "backgroundColor": [255, 255, 255, 1],
         "isLargeText": false,
+        "score": "AAA",
       },
     },
   },
 }];
 
 /**
  * Test that checks the Accessibility panel sidebar.
  */
--- a/devtools/client/accessibility/test/jest/components/audit-filter.test.js
+++ b/devtools/client/accessibility/test/jest/components/audit-filter.test.js
@@ -57,16 +57,17 @@ describe("AuditController component:", (
 
     const wrapper = mount(Provider({store}, AuditFilter({
       checks: {
         "CONTRAST": {
           "value": 5.11,
           "color": [255, 0, 0, 1],
           "backgroundColor": [255, 255, 255, 1],
           "isLargeText": false,
+          "score": "AA",
         },
       },
     }, span())));
     expect(wrapper.html()).toMatchSnapshot();
     expect(wrapper.isEmptyRender()).toBe(true);
   });
 
   it("audit filter filtered contrast checks fail", () => {
@@ -74,16 +75,17 @@ describe("AuditController component:", (
       preloadedState: { audit: { filters: { [FILTERS.CONTRAST]: true }}},
     });
 
     const CONTRAST = {
       "value": 3.1,
       "color": [255, 0, 0, 1],
       "backgroundColor": [255, 255, 255, 1],
       "isLargeText": false,
+      "score": "fail",
     };
 
     const wrapper = mount(Provider({store}, AuditFilter({
       checks: { CONTRAST },
     }, span())));
     expect(wrapper.html()).toMatchSnapshot();
     const filter = wrapper.find(AuditFilterClass);
     expect(filter.children().length).toBe(1);
@@ -97,16 +99,17 @@ describe("AuditController component:", (
 
     const CONTRAST = {
       "min": 1.19,
       "max": 1.39,
       "color": [128, 128, 128, 1],
       "backgroundColorMin": [219, 106, 116, 1],
       "backgroundColorMax": [156, 145, 211, 1],
       "isLargeText": false,
+      "score": "fail",
     };
 
     const wrapper = mount(Provider({store}, AuditFilter({
       checks: { CONTRAST },
     }, span())));
     expect(wrapper.html()).toMatchSnapshot();
     const filter = wrapper.find(AuditFilterClass);
     expect(filter.children().length).toBe(1);
--- a/devtools/client/accessibility/test/jest/components/badges.test.js
+++ b/devtools/client/accessibility/test/jest/components/badges.test.js
@@ -46,29 +46,31 @@ describe("Badges component:", () => {
   it("contrast ratio success render", () => {
     const wrapper = mount(Provider({ store }, Badges({
       checks: {
         "CONTRAST": {
           "value": 5.11,
           "color": [255, 0, 0, 1],
           "backgroundColor": [255, 255, 255, 1],
           "isLargeText": false,
+          "score": "AA",
         },
       },
     })));
     expect(wrapper.html()).toMatchSnapshot();
     expect(wrapper.isEmptyRender()).toBe(true);
   });
 
   it("contrast ratio fail render", () => {
     const CONTRAST = {
       "value": 3.1,
       "color": [255, 0, 0, 1],
       "backgroundColor": [255, 255, 255, 1],
       "isLargeText": false,
+      "score": "fail",
     };
     const wrapper = mount(Provider({ store }, Badges({ checks: { CONTRAST }})));
 
     expect(wrapper.html()).toMatchSnapshot();
     expect(wrapper.find(Badge).length).toBe(1);
     expect(wrapper.find(ContrastBadge).length).toBe(1);
     expect(wrapper.find(ContrastBadge).first().props()).toMatchObject(CONTRAST);
   });
@@ -76,16 +78,17 @@ describe("Badges component:", () => {
   it("contrast ratio fail range render", () => {
     const CONTRAST = {
       "min": 1.19,
       "max": 1.39,
       "color": [128, 128, 128, 1],
       "backgroundColorMin": [219, 106, 116, 1],
       "backgroundColorMax": [156, 145, 211, 1],
       "isLargeText": false,
+      "score": "fail",
     };
     const wrapper = mount(Provider({ store }, Badges({ checks: { CONTRAST }})));
 
     expect(wrapper.html()).toMatchSnapshot();
     expect(wrapper.find(Badge).length).toBe(1);
     expect(wrapper.find(ContrastBadge).length).toBe(1);
     expect(wrapper.find(ContrastBadge).first().props()).toMatchObject(CONTRAST);
   });
--- a/devtools/client/accessibility/test/jest/components/contrast-badge.test.js
+++ b/devtools/client/accessibility/test/jest/components/contrast-badge.test.js
@@ -24,44 +24,48 @@ describe("ContrastBadge component:", () 
     expect(wrapper.html()).toMatchSnapshot();
     expect(wrapper.isEmptyRender()).toBe(true);
   });
 
   it("success render", () => {
     const wrapper = shallow(ContrastBadge({
       value: 5.11,
       isLargeText: false,
+      score: "AA",
     }));
     expect(wrapper.html()).toMatchSnapshot();
     expect(wrapper.isEmptyRender()).toBe(true);
   });
 
   it("success range render", () => {
     const wrapper = shallow(ContrastBadge({
       min: 5.11,
       max: 6.25,
       isLargeText: false,
+      score: "AA",
     }));
     expect(wrapper.html()).toMatchSnapshot();
     expect(wrapper.isEmptyRender()).toBe(true);
   });
 
   it("success large text render", () => {
     const wrapper = shallow(ContrastBadge({
       value: 3.77,
       isLargeText: true,
+      score: "AA",
     }));
     expect(wrapper.html()).toMatchSnapshot();
     expect(wrapper.isEmptyRender()).toBe(true);
   });
 
   it("fail render", () => {
     const wrapper = mount(Provider({ store }, ContrastBadge({
       value: 3.77,
       isLargeText: false,
+      score: "fail",
     })));
 
     expect(wrapper.html()).toMatchSnapshot();
     expect(wrapper.children().length).toBe(1);
     const contrastBadge = wrapper.find(ContrastBadgeClass);
     const badge = contrastBadge.childAt(0);
     expect(badge.type()).toBe(Badge);
     expect(badge.props()).toMatchObject({
--- a/devtools/client/accessibility/test/mochitest/test_accessible_contrast.html
+++ b/devtools/client/accessibility/test/mochitest/test_accessible_contrast.html
@@ -36,36 +36,41 @@ window.onload = async function() {
     );
 
     matchSnapshot("ColorContrastAccessibility basic render.",
       React.createElement(ColorContrastCheck, {
         "value": 4.00,
         "color": [255, 0, 0, 1],
         "backgroundColor": [255, 255, 255, 1],
         "isLargeText": false,
+        "score": "fail",
       })
     );
 
     matchSnapshot("ColorContrastAccessibility range render.",
       React.createElement(ColorContrastCheck, {
         "min": 1.19,
         "max": 1.39,
         "color": [128, 128, 128, 1],
         "backgroundColorMin": [219, 106, 116, 1],
         "backgroundColorMax": [156, 145, 211, 1],
         "isLargeText": false,
+        "score": "fail",
+        "scoreMin": "fail",
+        "scoreMax": "fail",
       })
     );
 
     matchSnapshot("ColorContrastAccessibility large text render.",
       React.createElement(ColorContrastCheck, {
         "value": 4.00,
         "color": [255, 0, 0, 1],
         "backgroundColor": [255, 255, 255, 1],
         "isLargeText": true,
+        "score": "AA",
       })
     );
   } catch (e) {
     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
   } finally {
     SimpleTest.finish();
   }
 };
--- a/devtools/server/actors/accessibility/contrast.js
+++ b/devtools/server/actors/accessibility/contrast.js
@@ -6,16 +6,17 @@
 
 loader.lazyRequireGetter(this, "colorUtils", "devtools/shared/css/color", true);
 loader.lazyRequireGetter(this, "CssLogic", "devtools/server/actors/inspector/css-logic", true);
 loader.lazyRequireGetter(this, "getBounds", "devtools/server/actors/highlighters/utils/accessibility", true);
 loader.lazyRequireGetter(this, "getCurrentZoom", "devtools/shared/layout/utils", true);
 loader.lazyRequireGetter(this, "addPseudoClassLock", "devtools/server/actors/highlighters/utils/markup", true);
 loader.lazyRequireGetter(this, "removePseudoClassLock", "devtools/server/actors/highlighters/utils/markup", true);
 loader.lazyRequireGetter(this, "DevToolsWorker", "devtools/shared/worker/worker", true);
+loader.lazyRequireGetter(this, "accessibility", "devtools/shared/constants", true);
 
 const WORKER_URL = "resource://devtools/server/actors/accessibility/worker.js";
 const HIGHLIGHTED_PSEUDO_CLASS = ":-moz-devtools-highlighted";
 // CSS pixel value (constant) that corresponds to 14 point text size which defines large
 // text when font text is bold (font weight is greater than or equal to 600).
 const BOLD_LARGE_TEXT_MIN_PIXELS = 18.66;
 // CSS pixel value (constant) that corresponds to 18 point text size which defines large
 // text for normal text (e.g. not bold).
@@ -105,16 +106,39 @@ function getImageCtx(win, bounds, zoom, 
   if (node) {
     removePseudoClassLock(node, HIGHLIGHTED_PSEUDO_CLASS);
   }
 
   return ctx;
 }
 
 /**
+ * Get contrast ratio score based on WCAG criteria.
+ * @param  {Number} ratio
+ *         Value of the contrast ratio for a given accessible object.
+ * @param  {Boolean} isLargeText
+ *         True if the accessible object contains large text.
+ * @return {String}
+ *         Value that represents calculated contrast ratio score.
+ */
+function getContrastRatioScore(ratio, isLargeText) {
+  const { SCORES: { FAIL, AA, AAA } } = accessibility;
+  const levels = isLargeText ? { AA: 3, AAA: 4.5 } : { AA: 4.5, AAA: 7 };
+
+  let score = FAIL;
+  if (ratio >= levels.AAA) {
+    score = AAA;
+  } else if (ratio >= levels.AA) {
+    score = AA;
+  }
+
+  return score;
+}
+
+/**
  * Calculates the contrast ratio of the referenced DOM node.
  *
  * @param  {DOMNode} node
  *         The node for which we want to calculate the contrast ratio.
  * @param  {Object}  options
  *         - bounds   {Object}
  *                    Bounds for the accessible object.
  *         - win      {Object}
@@ -172,36 +196,43 @@ async function getContrastRatioFor(node,
 
   if (!rgba) {
     return {
       error: true,
     };
   }
 
   if (rgba.value) {
+    const value = colorUtils.calculateContrastRatio(rgba.value, color);
     return {
-      value: colorUtils.calculateContrastRatio(rgba.value, color),
+      value,
       color,
       backgroundColor: rgba.value,
       isLargeText,
+      score: getContrastRatioScore(value, isLargeText),
     };
   }
 
   let min = colorUtils.calculateContrastRatio(rgba.min, color);
   let max = colorUtils.calculateContrastRatio(rgba.max, color);
 
   // Flip minimum and maximum contrast ratios if necessary.
   if (min > max) {
     [min, max] = [max, min];
     [rgba.min, rgba.max] = [rgba.max, rgba.min];
   }
 
+  const score = getContrastRatioScore(min, isLargeText);
+
   return {
     min,
     max,
     color,
     backgroundColorMin: rgba.min,
     backgroundColorMax: rgba.max,
     isLargeText,
+    score,
+    scoreMin: score,
+    scoreMax: getContrastRatioScore(max, isLargeText),
   };
 }
 
 exports.getContrastRatioFor = getContrastRatioFor;
--- a/devtools/server/actors/accessibility/walker.js
+++ b/devtools/server/actors/accessibility/walker.js
@@ -17,16 +17,17 @@ loader.lazyRequireGetter(this, "getCurre
 loader.lazyRequireGetter(this, "InspectorUtils", "InspectorUtils");
 loader.lazyRequireGetter(this, "isDefunct", "devtools/server/actors/utils/accessibility", true);
 loader.lazyRequireGetter(this, "isTypeRegistered", "devtools/server/actors/highlighters", true);
 loader.lazyRequireGetter(this, "isWindowIncluded", "devtools/shared/layout/utils", true);
 loader.lazyRequireGetter(this, "isXUL", "devtools/server/actors/highlighters/utils/markup", true);
 loader.lazyRequireGetter(this, "loadSheet", "devtools/shared/layout/utils", true);
 loader.lazyRequireGetter(this, "register", "devtools/server/actors/highlighters", true);
 loader.lazyRequireGetter(this, "removeSheet", "devtools/shared/layout/utils", true);
+loader.lazyRequireGetter(this, "accessibility", "devtools/shared/constants", true);
 
 const kStateHover = 0x00000004; // NS_EVENT_STATE_HOVER
 
 const HIGHLIGHTER_STYLES_SHEET = `data:text/css;charset=utf-8,
 * {
   transition: none !important;
 }
 
@@ -400,17 +401,20 @@ const AccessibleWalkerActor = ActorClass
   async audit() {
     const doc = await this.getDocument();
     const report = new Map();
     getAudit(doc, report);
     await Promise.all(report.values());
 
     const ancestries = [];
     for (const [acc, audit] of report.entries()) {
-      if (audit && Object.values(audit).filter(check => check != null).length > 0) {
+      // Filter out audits that have no failing checks.
+      if (audit &&
+          Object.values(audit).some(check => check != null && !check.error &&
+            check.score === accessibility.SCORES.FAIL)) {
         ancestries.push(this.getAncestry(acc));
       }
     }
 
     return Promise.all(ancestries);
   },
 
   onHighlighterEvent: function(data) {
--- a/devtools/server/actors/highlighters/utils/accessibility.js
+++ b/devtools/server/actors/highlighters/utils/accessibility.js
@@ -4,17 +4,17 @@
 
 "use strict";
 
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 const { getCurrentZoom, getViewportDimensions } = require("devtools/shared/layout/utils");
 const { moveInfobar, createNode } = require("./markup");
 const { truncateString } = require("devtools/shared/inspector/utils");
 
-const { accessibility: { ColorContrastScores } } = require("devtools/shared/constants");
+const { accessibility: { SCORES } } = require("devtools/shared/constants");
 
 const STRINGS_URI = "devtools/shared/locales/accessibility.properties";
 loader.lazyRequireGetter(this, "LocalizationHelper", "devtools/shared/l10n", true);
 DevToolsUtils.defineLazyGetter(this, "L10N", () => new LocalizationHelper(STRINGS_URI));
 
 const { accessibility: { AUDIT_TYPE } } = require("devtools/shared/constants");
 
 // Max string length for truncating accessible name values.
@@ -522,21 +522,20 @@ class ContrastRatio extends AuditReport 
       attributes: {
         "class": "contrast-ratio",
         "id": "contrast-ratio-max",
       },
       prefix: this.prefix,
     });
   }
 
-  _fillAndStyleContrastValue(el, { value, isLargeText, color, backgroundColor }) {
+  _fillAndStyleContrastValue(el, { value, className, color, backgroundColor }) {
     value = value.toFixed(2);
-    const style = getContrastRatioScoreStyle(value, isLargeText);
     this.setTextContent(el, value);
-    el.classList.add(style);
+    el.classList.add(className);
     el.setAttribute("style",
       `--accessibility-highlighter-contrast-ratio-color: rgba(${color});` +
       `--accessibility-highlighter-contrast-ratio-bg: rgba(${backgroundColor});`);
     el.removeAttribute("hidden");
   }
 
   /**
    * Update contrast ratio score infobar markup.
@@ -546,17 +545,17 @@ class ContrastRatio extends AuditReport 
    *         True if the contrast ratio markup was updated correctly and infobar audit
    *         block should be visible.
    */
   update({ [AUDIT_TYPE.CONTRAST]: contrastRatio }) {
     const els = {};
     for (const key of ["label", "min", "max", "error", "separator"]) {
       const el = els[key] = this.getElement(`contrast-ratio-${key}`);
       if (["min", "max"].includes(key)) {
-        Object.values(ColorContrastScores).forEach(
+        Object.values(SCORES).forEach(
           className => el.classList.remove(className));
         this.setTextContent(el, "");
       }
 
       el.setAttribute("hidden", true);
       el.removeAttribute("style");
     }
 
@@ -569,28 +568,30 @@ class ContrastRatio extends AuditReport 
       L10N.getStr(`accessibility.contrast.ratio.label${isLargeText ? ".large" : ""}`));
     els.label.removeAttribute("hidden");
     if (error) {
       els.error.removeAttribute("hidden");
       return true;
     }
 
     if (contrastRatio.value) {
-      const { value, color, backgroundColor } = contrastRatio;
+      const { value, color, score, backgroundColor } = contrastRatio;
       this._fillAndStyleContrastValue(els.min,
-        { value, isLargeText, color, backgroundColor });
+        { value, className: score, color, backgroundColor });
       return true;
     }
 
-    const { min, max, color, backgroundColorMin, backgroundColorMax } = contrastRatio;
+    const {
+      min, max, color, backgroundColorMin, backgroundColorMax, scoreMin, scoreMax,
+    } = contrastRatio;
     this._fillAndStyleContrastValue(els.min,
-      { value: min, isLargeText, color, backgroundColor: backgroundColorMin });
+      { value: min, className: scoreMin, color, backgroundColor: backgroundColorMin });
     els.separator.removeAttribute("hidden");
     this._fillAndStyleContrastValue(els.max,
-      { value: max, isLargeText, color, backgroundColor: backgroundColorMax });
+      { value: max, className: scoreMax, color, backgroundColor: backgroundColorMax });
 
     return true;
   }
 }
 
 /**
  * A helper function that calculate accessible object bounds and positioning to
  * be used for highlighting.
@@ -641,35 +642,12 @@ function getBounds(win, { x, y, w, h, zo
   bottom *= zoomFactor;
 
   const width = right - left;
   const height = bottom - top;
 
   return { left, right, top, bottom, width, height };
 }
 
-/**
- * Get contrast ratio score styling to be applied on the element that renders the contrast
- * ratio.
- * @param  {Number} ratio
- *         Value of the contrast ratio for a given accessible object.
- * @param  {Boolean} isLargeText
- *         True if the accessible object contains large text.
- * @return {String}
- *         CSS class that represents the appropriate contrast ratio score styling.
- */
-function getContrastRatioScoreStyle(ratio, isLargeText) {
-  const levels = isLargeText ? { AA: 3, AAA: 4.5 } : { AA: 4.5, AAA: 7 };
-
-  let style = ColorContrastScores.FAIL;
-  if (ratio >= levels.AAA) {
-    style = ColorContrastScores.AAA;
-  } else if (ratio >= levels.AA) {
-    style = ColorContrastScores.AA;
-  }
-
-  return style;
-}
-
 exports.MAX_STRING_LENGTH = MAX_STRING_LENGTH;
 exports.getBounds = getBounds;
 exports.Infobar = Infobar;
 exports.XULWindowInfobar = XULWindowInfobar;
--- a/devtools/server/tests/browser/browser_accessibility_node_audit.js
+++ b/devtools/server/tests/browser/browser_accessibility_node_audit.js
@@ -32,16 +32,17 @@ add_task(async function() {
 
   const headerNode = await walker.querySelector(walker.rootNode, "#h1");
   await checkAudit(a11yWalker, headerNode, {
     "CONTRAST": {
       "value": 21,
       "color": [0, 0, 0, 1],
       "backgroundColor": [255, 255, 255, 1],
       "isLargeText": true,
+      score: "AAA",
     },
   });
 
   const paragraphNode = await walker.querySelector(walker.rootNode, "#p");
   await checkAudit(a11yWalker, paragraphNode, {
     "CONTRAST": null,
   });
 
--- a/devtools/shared/constants.js
+++ b/devtools/shared/constants.js
@@ -12,14 +12,14 @@
 
 exports.accessibility = {
   // List of audit types.
   AUDIT_TYPE: {
     CONTRAST: "CONTRAST",
   },
   // Constants associated with WCAG guidelines score system:
   // failing -> AA -> AAA;
-  ColorContrastScores: {
+  SCORES: {
     FAIL: "fail",
     AA: "AA",
     AAA: "AAA",
   },
 };