From 0e48d4c3b485e4e6c00be956adc53ccddff087d8 Mon Sep 17 00:00:00 2001 From: Maliha Islam Date: Sat, 13 Jul 2019 18:36:41 +0000 Subject: [PATCH] Bug 1478156 - Move shared code to be used by color contrast feature, r=yzen,gl Differential Revision: https://phabricator.services.mozilla.com/D32490 --HG-- extra : moz-landing-system : lando --- .../client/accessibility/accessibility.css | 64 ++-------- devtools/client/accessibility/index.html | 2 + devtools/client/jar.mn | 1 + .../themes/accessibility-color-contrast.css | 52 ++++++++ .../actors/accessibility/audit/contrast.js | 104 +++------------- devtools/shared/accessibility.js | 117 ++++++++++++++++++ devtools/shared/moz.build | 1 + 7 files changed, 197 insertions(+), 144 deletions(-) create mode 100644 devtools/client/themes/accessibility-color-contrast.css create mode 100644 devtools/shared/accessibility.js diff --git a/devtools/client/accessibility/accessibility.css b/devtools/client/accessibility/accessibility.css index 6aa3436a574c..72ded362f156 100644 --- a/devtools/client/accessibility/accessibility.css +++ b/devtools/client/accessibility/accessibility.css @@ -20,12 +20,6 @@ --accessibility-link-color: var(--blue-60); --accessibility-link-color-active: var(--blue-70); --accessibility-body-background-a90: rgba(255, 255, 255, 0.9); - --badge-active-background-color: var(--blue-50); - --badge-active-border-color: #FFFFFFB3; - --badge-interactive-background-color: var(--grey-20); - --accessible-label-background-color: white; - --accessible-label-border-color: #CACAD1; - --accessible-label-color: var(--grey-60); /* Similarly to webconsole, add more padding before the toolbar group. */ --separator-inline-margin: 5px; --accessibility-code-background: var(--grey-20); @@ -37,12 +31,6 @@ --accessibility-link-color: var(--theme-highlight-blue); --accessibility-link-color-active: var(--blue-40); --accessibility-body-background-a90: rgba(42, 42, 46, 0.9); - --badge-active-background-color: var(--blue-60); - --badge-active-border-color: #FFF6; - --badge-interactive-background-color: var(--grey-70); - --accessible-label-background-color: var(--grey-80); - --accessible-label-border-color: var(--grey-50); - --accessible-label-color: var(--grey-40); --accessibility-code-background: var(--grey-70); } @@ -382,9 +370,9 @@ body { border-radius: 3px; padding: 0px 3px; margin-inline-start: 5px; - color: var(--accessible-label-color); - background-color: var(--accessible-label-background-color); - border: 1px solid var(--accessible-label-border-color); + color: var(--badge-color); + background-color: var(--badge-background-color); + border: 1px solid var(--badge-border-color); } .badge.audit-badge::before { @@ -427,7 +415,7 @@ body { } .treeTable:not(:focus):not(:focus-within) .treeRow.selected .badges .badge { - color: var(--accessible-label-color); + color: var(--badge-color); } .badge.toggle-button.checked { @@ -580,9 +568,9 @@ body { } .accessible .tree .objectBox-accessible .accessible-role { - background-color: var(--accessible-label-background-color); - color: var(--accessible-label-color); - border: 1px solid var(--accessible-label-border-color); + background-color: var(--badge-background-color); + color: var(--badge-color); + border: 1px solid var(--badge-border-color); border-radius: 3px; padding: 0px 2px; margin-inline-start: 5px; @@ -700,10 +688,6 @@ body { line-height: 20px; } -.accessibility-color-contrast { - align-items: baseline; -} - .accessibility-check-header { margin: 0; font-weight: bold; @@ -715,7 +699,7 @@ body { display: inline; margin: 0; white-space: normal; - color: var(--accessible-label-color); + color: var(--badge-color); } .accessibility-check-annotation .link { @@ -740,16 +724,6 @@ body { text-decoration: underline; } -.accessibility-color-contrast-large-text { - background-color: var(--accessible-label-background-color); - color: var(--accessible-label-color); - outline: 1px solid var(--accessible-label-border-color); - -moz-outline-radius: 3px; - padding: 0px 2px; - margin-inline-start: 6px; - line-height: initial; -} - .accessibility-color-contrast .accessibility-contrast-value:not(:empty) { margin-block-end: 4px; } @@ -772,28 +746,6 @@ body { margin-inline-start: 4px; } -.accessibility-color-contrast .accessibility-contrast-value:not(:empty):after { - margin-inline-start: 4px; -} - -.accessibility-color-contrast .accessibility-contrast-value:not(:empty).AA:after, -.accessibility-color-contrast .accessibility-contrast-value:not(:empty).AAA:after { - color: var(--theme-highlight-green); -} - -.accessibility-color-contrast .accessibility-contrast-value:not(:empty).fail:after { - color: #E57180; - content: "⚠️"; -} - -.accessibility-color-contrast .accessibility-contrast-value:not(:empty).AA:after { - content: "AA\2713"; -} - -.accessibility-color-contrast .accessibility-contrast-value:not(:empty).AAA:after { - content: "AAA\2713"; -} - .accessibility-color-contrast .accessibility-color-contrast-label:after { content: ":"; } diff --git a/devtools/client/accessibility/index.html b/devtools/client/accessibility/index.html index 1248b76a9e49..76993446b70e 100644 --- a/devtools/client/accessibility/index.html +++ b/devtools/client/accessibility/index.html @@ -7,6 +7,8 @@ + + diff --git a/devtools/client/jar.mn b/devtools/client/jar.mn index c20cf1cbabd5..732f06d73b4e 100644 --- a/devtools/client/jar.mn +++ b/devtools/client/jar.mn @@ -165,6 +165,7 @@ devtools.jar: skin/images/dock-side-right.svg (themes/images/dock-side-right.svg) skin/images/dock-undock.svg (themes/images/dock-undock.svg) skin/floating-scrollbars-responsive-design.css (themes/floating-scrollbars-responsive-design.css) + skin/accessibility-color-contrast.css (themes/accessibility-color-contrast.css) skin/badge.css (themes/badge.css) skin/inspector.css (themes/inspector.css) skin/images/profiler-stopwatch.svg (themes/images/profiler-stopwatch.svg) diff --git a/devtools/client/themes/accessibility-color-contrast.css b/devtools/client/themes/accessibility-color-contrast.css new file mode 100644 index 000000000000..8fec1000f307 --- /dev/null +++ b/devtools/client/themes/accessibility-color-contrast.css @@ -0,0 +1,52 @@ +/* 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/. */ + + /* Classes used to style the color contrast section in the Accessibility + * Checks panel and color picker tooltip across the Inspector panel. + * + * The section consists of: + * - contrast ratio value (numeric + score badge (AA/AAA/fail)): + * Only shows up if contrast ratio can be calculated. + * - large text indicator badge: + * Only shows up if the selected text node contains large text. + */ +.accessibility-color-contrast { + position: relative; + display: flex; + cursor: default; + height: inherit; + align-items: baseline; +} + +.accessibility-color-contrast .accessibility-contrast-value:not(:empty):after { + margin-inline-start: 4px; +} + +.accessibility-color-contrast .accessibility-contrast-value:not(:empty).AA:after, +.accessibility-color-contrast .accessibility-contrast-value:not(:empty).AAA:after { + color: var(--theme-highlight-green); +} + +.accessibility-color-contrast .accessibility-contrast-value:not(:empty).fail:after { + color: #E57180; + content: "⚠️"; +} + +.accessibility-color-contrast .accessibility-contrast-value:not(:empty).AA:after { + content: "AA\2713"; +} + +.accessibility-color-contrast .accessibility-contrast-value:not(:empty).AAA:after { + content: "AAA\2713"; +} + +.accessibility-color-contrast-large-text { + background-color: var(--badge-background-color); + color: var(--badge-color); + outline: 1px solid var(--badge-border-color); + -moz-outline-radius: 3px; + padding: 0px 2px; + margin-inline-start: 6px; + line-height: initial; +} diff --git a/devtools/server/actors/accessibility/audit/contrast.js b/devtools/server/actors/accessibility/audit/contrast.js index b9091c753f76..234f715f1e79 100644 --- a/devtools/server/actors/accessibility/audit/contrast.js +++ b/devtools/server/actors/accessibility/audit/contrast.js @@ -37,14 +37,20 @@ loader.lazyRequireGetter( ); loader.lazyRequireGetter( this, - "DevToolsWorker", - "devtools/shared/worker/worker", + "getContrastRatioScore", + "devtools/shared/accessibility", true ); loader.lazyRequireGetter( this, - "accessibility", - "devtools/shared/constants", + "getTextProperties", + "devtools/shared/accessibility", + true +); +loader.lazyRequireGetter( + this, + "DevToolsWorker", + "devtools/shared/worker/worker", true ); loader.lazyRequireGetter( @@ -55,67 +61,12 @@ loader.lazyRequireGetter( 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). -const LARGE_TEXT_MIN_PIXELS = 24; +const { + LARGE_TEXT: { BOLD_LARGE_TEXT_MIN_PIXELS, LARGE_TEXT_MIN_PIXELS }, +} = require("devtools/shared/accessibility"); loader.lazyGetter(this, "worker", () => new DevToolsWorker(WORKER_URL)); -/** - * Get text style properties for a given node, if possible. - * @param {DOMNode} node - * DOM node for which text styling information is to be calculated. - * @return {Object} - * Color and text size information for a given DOM node. - */ -function getTextProperties(node) { - const computedStyles = CssLogic.getComputedStyle(node); - if (!computedStyles) { - return null; - } - - const { - color, - "font-size": fontSize, - "font-weight": fontWeight, - } = computedStyles; - const opacity = parseFloat(computedStyles.opacity); - - let { r, g, b, a } = colorUtils.colorToRGBA(color, true); - // If the element has opacity in addition to background alpha value, take it - // into account. TODO: this does not handle opacity set on ancestor elements - // (see bug https://bugzilla.mozilla.org/show_bug.cgi?id=1544721). - a = opacity * a; - const textRgbaColor = new colorUtils.CssColor( - `rgba(${r}, ${g}, ${b}, ${a})`, - true - ); - // TODO: For cases where text color is transparent, it likely comes from the color of - // the background that is underneath it (commonly from background-clip: text - // property). With some additional investigation it might be possible to calculate the - // color contrast where the color of the background is used as text color and the - // color of the ancestor's background is used as its background. - if (textRgbaColor.isTransparent()) { - return null; - } - - const isBoldText = parseInt(fontWeight, 10) >= 600; - const size = parseFloat(fontSize); - const isLargeText = - size >= (isBoldText ? BOLD_LARGE_TEXT_MIN_PIXELS : LARGE_TEXT_MIN_PIXELS); - - return { - color: [r, g, b, a], - isLargeText, - isBoldText, - size, - opacity, - }; -} - /** * Get canvas rendering context for the current target window bound by the bounds of the * accessible objects. @@ -167,31 +118,6 @@ function getImageCtx(win, bounds, zoom, scale, node) { 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. * @@ -208,7 +134,9 @@ function getContrastRatioScore(ratio, isLargeText) { * isLargeText, value, min, max values for contrast. */ async function getContrastRatioFor(node, options = {}) { - const props = getTextProperties(node); + const computedStyle = CssLogic.getComputedStyle(node); + const props = computedStyle ? getTextProperties(computedStyle) : null; + if (!props) { return { error: true, diff --git a/devtools/shared/accessibility.js b/devtools/shared/accessibility.js new file mode 100644 index 000000000000..46d37740314d --- /dev/null +++ b/devtools/shared/accessibility.js @@ -0,0 +1,117 @@ +/* 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, "colorUtils", "devtools/shared/css/color", true); +const { + accessibility: { + SCORES: { FAIL, AA, AAA }, + }, +} = require("devtools/shared/constants"); + +/** + * Mapping of text size to contrast ratio score levels + */ +const LEVELS = { + LARGE_TEXT: { AA: 3, AAA: 4.5 }, + REGULAR_TEXT: { AA: 4.5, AAA: 7 }, +}; + +/** + * Mapping of large text size to CSS pixel value + */ +const LARGE_TEXT = { + // 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). + 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). + LARGE_TEXT_MIN_PIXELS: 24, +}; + +/** + * 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 levels = isLargeText ? LEVELS.LARGE_TEXT : LEVELS.REGULAR_TEXT; + + let score = FAIL; + if (ratio >= levels.AAA) { + score = AAA; + } else if (ratio >= levels.AA) { + score = AA; + } + + return score; +} + +/** + * Get calculated text style properties from a node's computed style, if possible. + * @param {Object} computedStyle + * Computed style using which text styling information is to be calculated. + * - fontSize {String} + * Font size of the text + * - fontWeight {String} + * Font weight of the text + * - color {String} + * Rgb color of the text + * - opacity {String} Optional + * Opacity of the text + * @return {Object} + * Color and text size information for a given DOM node. + */ +function getTextProperties(computedStyle) { + const { color, fontSize, fontWeight } = computedStyle; + let { r, g, b, a } = colorUtils.colorToRGBA(color, true); + + // If the element has opacity in addition to background alpha value, take it + // into account. TODO: this does not handle opacity set on ancestor elements + // (see bug https://bugzilla.mozilla.org/show_bug.cgi?id=1544721). + const opacity = computedStyle.opacity + ? parseFloat(computedStyle.opacity) + : null; + if (opacity) { + a = opacity * a; + } + + const textRgbaColor = new colorUtils.CssColor( + `rgba(${r}, ${g}, ${b}, ${a})`, + true + ); + // TODO: For cases where text color is transparent, it likely comes from the color of + // the background that is underneath it (commonly from background-clip: text + // property). With some additional investigation it might be possible to calculate the + // color contrast where the color of the background is used as text color and the + // color of the ancestor's background is used as its background. + if (textRgbaColor.isTransparent()) { + return null; + } + + const isBoldText = parseInt(fontWeight, 10) >= 600; + const size = parseFloat(fontSize); + const isLargeText = + size >= + (isBoldText + ? LARGE_TEXT.BOLD_LARGE_TEXT_MIN_PIXELS + : LARGE_TEXT.LARGE_TEXT_MIN_PIXELS); + + return { + color: [r, g, b, a], + isLargeText, + isBoldText, + size, + opacity, + }; +} + +exports.getContrastRatioScore = getContrastRatioScore; +exports.getTextProperties = getTextProperties; +exports.LARGE_TEXT = LARGE_TEXT; diff --git a/devtools/shared/moz.build b/devtools/shared/moz.build index 7505dccdfdd0..b2897a845b8d 100644 --- a/devtools/shared/moz.build +++ b/devtools/shared/moz.build @@ -43,6 +43,7 @@ XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini'] JAR_MANIFESTS += ['jar.mn'] DevToolsModules( + 'accessibility.js', 'async-storage.js', 'async-utils.js', 'base-loader.js',