From 311d2b35ef4dda0be797064cdf6b1a63dff68ecd Mon Sep 17 00:00:00 2001 From: Karen Holland Date: Mon, 17 Jul 2023 16:43:20 +0000 Subject: [PATCH] MDL-78785 tool_brickfield: Processing rgb & rgba color contrasts Including rgb(), rgba() processing for colour contrast checking. Including review requests :void and @covers for phpunit testing. --- .../brickfield_accessibility_color_test.php | 40 +++++-- .../common/checks/css_text_has_contrast.php | 19 +-- .../checks/css_text_has_contrast_test.php | 111 ++++++++++++++++++ 3 files changed, 152 insertions(+), 18 deletions(-) diff --git a/admin/tool/brickfield/classes/local/htmlchecker/common/brickfield_accessibility_color_test.php b/admin/tool/brickfield/classes/local/htmlchecker/common/brickfield_accessibility_color_test.php index a392d1106e427..f93e43e2abc86 100644 --- a/admin/tool/brickfield/classes/local/htmlchecker/common/brickfield_accessibility_color_test.php +++ b/admin/tool/brickfield/classes/local/htmlchecker/common/brickfield_accessibility_color_test.php @@ -245,7 +245,6 @@ public function luminosity(string $r, string $r2, string $g, string $g2, string return $luminosity; } - /** * Returns the decimal equivalents for a HEX color. Returns null if it cannot be determined. * @param string $color The hex color value @@ -268,13 +267,21 @@ public function get_rgb(string $color): ?array { */ public function convert_color(string $color): string { $color = trim($color); - if (strpos($color, ' ') !== false) { - $colors = explode(' ', $color); - foreach ($colors as $backgroundpart) { - if (substr(trim($backgroundpart), 0, 1) == '#' || - in_array(trim($backgroundpart), array_keys($this->colornames)) || - strtolower(substr(trim($backgroundpart), 0, 3)) == 'rgb') { - $color = $backgroundpart; + // Search for color in rgb format first, as this can potentially contain a space. + if (strpos($color, 'rgb') !== false) { + $colors = explode('rgb', $color, 2); // Getting 2 only in array. + // Getting end point of rgb value, i.e. the end bracket. + $endpos = strpos($colors[1], ')'); + $color = 'rgb' . substr($colors[1], 0, ($endpos + 1)); // Recompiling rgb value. + } else { + // Splitting multi-value css background value. + if (strpos($color, ' ') !== false) { + $colors = explode(' ', $color); + foreach ($colors as $backgroundpart) { + if (substr(trim($backgroundpart), 0, 1) == '#' || + in_array(trim($backgroundpart), array_keys($this->colornames))) { + $color = $backgroundpart; + } } } } @@ -296,9 +303,20 @@ public function convert_color(string $color): string { } // RGB values. if (strtolower(substr($color, 0, 3)) == 'rgb') { - $colors = explode(',', trim(str_replace('rgb(', '', $color), '()')); - if (count($colors) != 3) { - return false; + if (strpos($color, 'rgba') !== false) { + $tmpbg = $this->get_rgb($this->defaultbackground); + $colors = explode(',', trim(str_replace('rgba(', '', $color), '()')); + if (count($colors) != 4) { + return false; + } + $colors[0] = round(((1 - $colors[3]) * $tmpbg['r']) + ($colors[3] * $colors[0])); + $colors[1] = round((1 - $colors[3]) * $tmpbg['g']) + ($colors[3] * $colors[1]); + $colors[2] = round((1 - $colors[3]) * $tmpbg['b']) + ($colors[3] * $colors[2]); + } else { + $colors = explode(',', trim(str_replace('rgb(', '', $color), '()')); + if (count($colors) != 3) { + return false; + } } $r = intval($colors[0]); $g = intval($colors[1]); diff --git a/admin/tool/brickfield/classes/local/htmlchecker/common/checks/css_text_has_contrast.php b/admin/tool/brickfield/classes/local/htmlchecker/common/checks/css_text_has_contrast.php index 76be8c8ca4f72..daacac098d742 100644 --- a/admin/tool/brickfield/classes/local/htmlchecker/common/checks/css_text_has_contrast.php +++ b/admin/tool/brickfield/classes/local/htmlchecker/common/checks/css_text_has_contrast.php @@ -62,9 +62,18 @@ public function check(): void { foreach ($entries as $element) { $style = $this->css->get_style($element); - if (isset($style['background-color']) || isset($style['color'])) { + if (isset($style['background-color']) || isset($style['color']) || isset($style['background'])) { if (!isset($style['background-color'])) { - $style['background-color'] = $this->defaultbackground; + if (isset($style['background'])) { + // Parsing background-color from CSS background shortcut string. + $style['background-color'] = '#' . $this->convert_color($style['background']); + // If value is empty after hash, then use defaultbackground. + if ($style['background-color'] == '#') { + $style['background-color'] = $this->defaultbackground; + } + } else { + $style['background-color'] = $this->defaultbackground; + } } if (!isset($style['color'])) { @@ -82,11 +91,7 @@ public function check(): void { $style['color'] = '#' . $this->convert_color($style['color']); $style['background-color'] = '#' . $this->convert_color($background); - if (substr($background, 0, 3) == "rgb") { - $background = '#' . $this->convert_color($background); - } - - $luminosity = $this->get_luminosity($style['color'], $background); + $luminosity = $this->get_luminosity($style['color'], $style['background-color']); $fontsize = 0; $bold = false; $italic = false; diff --git a/admin/tool/brickfield/tests/local/htmlchecker/common/checks/css_text_has_contrast_test.php b/admin/tool/brickfield/tests/local/htmlchecker/common/checks/css_text_has_contrast_test.php index 1654094e4b9e0..4f739f0b6f62e 100644 --- a/admin/tool/brickfield/tests/local/htmlchecker/common/checks/css_text_has_contrast_test.php +++ b/admin/tool/brickfield/tests/local/htmlchecker/common/checks/css_text_has_contrast_test.php @@ -30,6 +30,7 @@ /** * Class test_css_text_has_contrast_test + * @covers \tool_brickfield\local\htmlchecker\brickfield_accessibility */ class css_text_has_contrast_test extends all_checks { /** @var string The check type. */ @@ -347,4 +348,114 @@ public function test_check_for_largerbold_pass(): void { $results = $this->get_checker_results($this->largerboldpass); $this->assertEmpty($results); } + + /** + * Test for rgb colors with insufficient contrast. + */ + public function test_bad_rgbcolor(): void { + $html = '

+ This is not contrasty enough.

'; + $results = $this->get_checker_results($html); + $this->assertTrue($results[0]->element->tagName == 'p'); + } + + /** + * Test for rgb colors with sufficient contrast. + */ + public function test_good_rgbcolor(): void { + $html = '

+ This is contrasty enough.

'; + $results = $this->get_checker_results($html); + $this->assertEmpty($results); + } + + /** + * Test for named colors with insufficient contrast. + */ + public function test_bad_namedcolor2(): void { + $html = '

+ This is not contrasty enough.

'; + $results = $this->get_checker_results($html); + $this->assertTrue($results[0]->element->tagName == 'p'); + } + + /** + * Test for named colors with sufficient contrast. + */ + public function test_good_namedcolor2(): void { + $html = '

+ This is contrasty enough.

'; + $results = $this->get_checker_results($html); + $this->assertEmpty($results); + } + + /** + * Test for background value with insufficient contrast. + */ + public function test_bad_backgroundcss(): void { + $html = '

+ This is not contrasty enough.

'; + $results = $this->get_checker_results($html); + $this->assertTrue($results[0]->element->tagName == 'p'); + } + + /** + * Test for background value with sufficient contrast. + */ + public function test_good_backgroundcss(): void { + $html = '

+ This is contrasty enough.

'; + $results = $this->get_checker_results($html); + $this->assertEmpty($results); + } + + /** + * Test for background value with rgb with insufficient contrast. + */ + public function test_bad_backgroundcssrgb(): void { + $html = '

+ This is not contrasty enough.

'; + $results = $this->get_checker_results($html); + $this->assertTrue($results[0]->element->tagName == 'p'); + } + + /** + * Test for background value with rgb with sufficient contrast. + */ + public function test_good_backgroundcssrgb(): void { + $html = '

+ This is contrasty enough.

'; + $results = $this->get_checker_results($html); + $this->assertEmpty($results); + } + + /** + * Test for text with insufficient contrast of 4.3. + */ + public function test_bad_contrastrounding(): void { + $html = '

+ This is not contrasty enough.

'; + $results = $this->get_checker_results($html); + $this->assertTrue($results[0]->element->tagName == 'p'); + } + + /** + * Test for background value with rgba with insufficient contrast. + */ + public function test_bad_backgroundcssrgba(): void { + $html = '

+ This is not contrasty enough.

'; + $results = $this->get_checker_results($html); + $this->assertTrue($results[0]->element->tagName == 'p'); + } + + /** + * Test for background value with rgba with sufficient contrast. + */ + public function test_good_backgroundcssrgba(): void { + $html = '

+ This is contrasty enough.

'; + $results = $this->get_checker_results($html); + $this->assertEmpty($results); + } }