Skip to content

Commit

Permalink
Merged in wilcofiers/axe-core/blackboard-patch (pull request #20)
Browse files Browse the repository at this point in the history
Blackboard color contrast patch
  • Loading branch information
WilcoFiers committed Mar 3, 2016
2 parents aad84b5 + 535d265 commit 6e0e149
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 64 deletions.
6 changes: 3 additions & 3 deletions lib/checks/color/color-contrast.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
var useScroll = !(options || {}).noScroll;
var noScroll = !!(options || {}).noScroll;
var bgNodes = [],
bgColor = commons.color.getBackgroundColor(node, bgNodes, useScroll),
fgColor = commons.color.getForegroundColor(node, useScroll);
bgColor = commons.color.getBackgroundColor(node, bgNodes, noScroll),
fgColor = commons.color.getForegroundColor(node, noScroll);

//We don't know, so we'll pass it provisionally
if (fgColor === null || bgColor === null) {
Expand Down
97 changes: 46 additions & 51 deletions lib/commons/color/get-background-color.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,36 @@
/*global dom, color */
/* jshint maxstatements: 32, maxcomplexity: 15 */
/* jshint maxstatements: 34, maxcomplexity: 15 */
//TODO dsturley: too complex, needs refactor!!

var graphicNodeNames = [
'IMG', 'CANVAS', 'OBJECT', 'IFRAME', 'VIDEO', 'SVG'
];

/**
* Returns the non-alpha-blended background color of a node, null if it's an image
* @param {Element} node
* @return {Color}
*/
function getBackgroundForSingleNode(node) {
var bgColor,
nodeStyle = window.getComputedStyle(node);
var bgColor, bgColorString, opacity;
var nodeStyle = window.getComputedStyle(node);
var nodeName = node.nodeName.toUpperCase();

if (nodeStyle.getPropertyValue('background-image') !== 'none') {
if (graphicNodeNames.indexOf(nodeName) !== -1 ||
nodeStyle.getPropertyValue('background-image') !== 'none') {
return null;
}

var bgColorString = nodeStyle.getPropertyValue('background-color');
bgColorString = nodeStyle.getPropertyValue('background-color');
//Firefox exposes unspecified background as 'transparent' rather than rgba(0,0,0,0)
if (bgColorString === 'transparent') {
bgColor = new color.Color(0, 0, 0, 0);
} else {
bgColor = new color.Color();
bgColor.parseRgbString(bgColorString);
}
var opacity = nodeStyle.getPropertyValue('opacity');

opacity = nodeStyle.getPropertyValue('opacity');
bgColor.alpha = bgColor.alpha * opacity;

return bgColor;
Expand Down Expand Up @@ -51,39 +58,12 @@ dom.isOpaque = function(node) {
* @return {Array} array of elements
*/
var getVisualParents = function(node, rect) {
var visualParents,
thisIndex,
parents = [],
fallbackToVisual = false,
currentNode = node,
nodeStyle = window.getComputedStyle(currentNode),
posVal, topVal, bottomVal, leftVal, rightVal;

while (currentNode !== null && (!dom.isOpaque(currentNode) || parseInt(nodeStyle.getPropertyValue('height'), 10) === 0)) {
// If the element is positioned, we can't rely on DOM order to find visual parents
posVal = nodeStyle.getPropertyValue('position');
topVal = nodeStyle.getPropertyValue('top');
bottomVal = nodeStyle.getPropertyValue('bottom');
leftVal = nodeStyle.getPropertyValue('left');
rightVal = nodeStyle.getPropertyValue('right');
if ((posVal !== 'static' && posVal !== 'relative') ||
(posVal === 'relative' &&
(leftVal !== 'auto' ||
rightVal !== 'auto' ||
topVal !== 'auto' ||
bottomVal !== 'auto'))) {
fallbackToVisual = true;
}
currentNode = currentNode.parentElement;
if (currentNode !== null) {
nodeStyle = window.getComputedStyle(currentNode);
if (parseInt(nodeStyle.getPropertyValue('height'), 10) !== 0) {
parents.push(currentNode);
}
}
}
var visualParents, thisIndex, posVal;
var parents = [];
var currentNode = node;
var nodeStyle = window.getComputedStyle(currentNode);

if (fallbackToVisual && dom.supportsElementsFromPoint(document)) {
if (dom.supportsElementsFromPoint(document)) {
visualParents = dom.elementsFromPoint(document,
Math.ceil(rect.left + 1),
Math.ceil(rect.top + 1));
Expand All @@ -92,10 +72,24 @@ var getVisualParents = function(node, rect) {
// if the element is not present; then something is obscuring it thus making calculation impossible
if (thisIndex === -1) {
return null;

} else if (visualParents && (thisIndex < visualParents.length - 1)) {
return visualParents.slice(thisIndex + 1);
}
}

if (visualParents && (thisIndex < visualParents.length - 1)) {
parents = visualParents.slice(thisIndex + 1);
while (currentNode !== null) {
// If the element is positioned, we can't rely on DOM order to find visual parents
posVal = nodeStyle.getPropertyValue('position');
if (posVal !== 'static') {
return null;
}
currentNode = currentNode.parentElement;
if (currentNode !== null) {
nodeStyle = window.getComputedStyle(currentNode);
if (parseInt(nodeStyle.getPropertyValue('height'), 10) !== 0) {
parents.push(currentNode);
}
}
}

Expand All @@ -108,32 +102,33 @@ var getVisualParents = function(node, rect) {
* there is no opaque ancestor element visually containing it, or because background images are used.
* @param {Element} node
* @param {Array} bgNodes array to which all encountered nodes should be appended
* @param {Boolean} useScroll
* @param {Boolean} noScroll (default false)
* @return {Color}
*/
//TODO dsturley; why is this passing `bgNodes`?
color.getBackgroundColor = function(node, bgNodes, useScroll) {
color.getBackgroundColor = function(node, bgNodes, noScroll) {
var parent, parentColor;

var bgColor = getBackgroundForSingleNode(node);

if (bgNodes && (bgColor === null || bgColor.alpha !== 0)) {
bgNodes.push(node);
}
if (bgColor === null || bgColor.alpha === 1) {
return bgColor;
}

if(useScroll) {
if(noScroll !== true) {
node.scrollIntoView();
}

var rect = node.getBoundingClientRect(),
currentNode = node,
colorStack = [{
var rect = node.getBoundingClientRect();
var currentNode = node;
var colorStack = [{
color: bgColor,
node: node
}],
parents = getVisualParents(currentNode, rect);
}];
var parents = getVisualParents(currentNode, rect);

if (!parents) {
return null;
}
Expand All @@ -143,11 +138,11 @@ color.getBackgroundColor = function(node, bgNodes, useScroll) {

if (!parent && currentNode.tagName !== 'HTML') {
return null;
}

//Assume white if top level is not specified
if (!parent && currentNode.tagName === 'HTML') {
} else if (!parent && currentNode.tagName === 'HTML') {
parentColor = new color.Color(255, 255, 255, 1);

} else {

if (!dom.visuallyContains(node, parent)) {
Expand Down
6 changes: 3 additions & 3 deletions lib/commons/color/get-foreground-color.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
* Returns the flattened foreground color of an element, or null if it can't be determined because
* of transparency
* @param {Element} node
* @param {Boolean} useScroll
* @param {Boolean} noScroll (default false)
* @return {Color}
*/
color.getForegroundColor = function (node, useScroll) {
color.getForegroundColor = function (node, noScroll) {
var nodeStyle = window.getComputedStyle(node);

var fgColor = new color.Color();
Expand All @@ -16,7 +16,7 @@ color.getForegroundColor = function (node, useScroll) {
fgColor.alpha = fgColor.alpha * opacity;
if (fgColor.alpha === 1) { return fgColor; }

var bgColor = color.getBackgroundColor(node, [], useScroll);
var bgColor = color.getBackgroundColor(node, [], noScroll);
if (bgColor === null) { return null; }

return color.flattenColors(fgColor, bgColor);
Expand Down
2 changes: 1 addition & 1 deletion lib/intro.stub
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*! aXe v<%= pkg.version %>
* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.organization %>
* Copyright (c) <%= grunt.template.today("yyyy") %> Deque Systems, Inc.
*
* Your use of 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
Expand Down
50 changes: 44 additions & 6 deletions test/commons/color/get-background-color.js
Original file line number Diff line number Diff line change
Expand Up @@ -217,15 +217,17 @@ describe('color.getBackgroundColor', function () {
});

it('should ignore 0-height elements', function () {
fixture.innerHTML = '<div id="parent" style="height: 40px; width: 30px; ' +
fixture.innerHTML =
'<div id="parent" style="height: 40px; width: 30px; ' +
'background-color: white; position: relative; z-index: 5">' +
'<div float="left" style="height: 0px; background-color: black">' +
'<div id="target" style="height: 20px; width: 25px; z-index: 25;">' +
' <div float="left" style="height: 0px; background-color: black">' +
' <div id="target" style="height: 20px; width: 25px; z-index: 25;">' +
'</div></div></div>';
var target = fixture.querySelector('#target');
var parent = fixture.querySelector('#parent');
var bgNodes = [];
var actual = commons.color.getBackgroundColor(target, bgNodes);

var expected = new commons.color.Color(255, 255, 255, 1);
assert.closeTo(actual.red, expected.red, 0.5);
assert.closeTo(actual.green, expected.green, 0.5);
Expand All @@ -245,7 +247,7 @@ describe('color.getBackgroundColor', function () {
var shifted = fixture.querySelector('#shifted');
var parent = fixture.querySelector('#parent');
var bgNodes = [];
var actual = commons.color.getBackgroundColor(target, bgNodes, true);
var actual = commons.color.getBackgroundColor(target, bgNodes, false);
var expected = new commons.color.Color(0, 0, 0, 1);
if (commons.dom.supportsElementsFromPoint(document)) {
assert.deepEqual(bgNodes, [shifted]);
Expand All @@ -259,6 +261,42 @@ describe('color.getBackgroundColor', function () {
assert.closeTo(actual.alpha, expected.alpha, 0.1);
});

it('should return null when encountering background images during visual traversal', function () {
fixture.innerHTML =
'<div id="parent" style="height: 40px; width: 30px; ' +
' background-color: white; position: relative; z-index: 5"> ' +
' <div id="target" style="position: relative; top: 1px; height: 20px;' +
' width: 25px; z-index: 25; background:rgba(0,125,0,0.5);"></div> ' +
' <div id="shifted" style="position: absolute; top: -30px; height: 40px; ' +
' background-image: url(foobar.png);'+
' width: 35px; z-index: 15;">' +
' </div>'+
'</div>';

var target = fixture.querySelector('#target');
var bgNodes = [];
var outcome = commons.color.getBackgroundColor(target, bgNodes, false);
assert.isNull(outcome);
});

it('should return null when encountering image nodes during visual traversal', function () {
fixture.innerHTML =
'<div id="parent" style="height: 40px; width: 30px; ' +
' background-color: white; position: relative; z-index: 5"> ' +
' <div id="shifted" style="position: absolute; top: -10px; height: 40px; ' +
' width: 35px; z-index: 15;">' +
' <img src="some-img.png" width="35" height="40">' +
' </div>'+
' <div id="target" style="position: relative; top: 1px; height: 20px;' +
' width: 25px; z-index: 25; background:rgba(0,125,0,0.5);"></div> ' +
'</div>';

var target = fixture.querySelector('#target');
var bgNodes = [];
var outcome = commons.color.getBackgroundColor(target, bgNodes, false);
assert.isNull(outcome);
});

it('does not change the scroll when scroll is disabled', function() {
fixture.innerHTML = '<div id="parent" style="height: 40px; width: 30px; ' +
'background-color: white; position: relative; z-index: 5">' +
Expand All @@ -268,7 +306,7 @@ describe('color.getBackgroundColor', function () {
var bgNodes = [];
window.scroll(0, 0);

commons.color.getBackgroundColor(targetEl, bgNodes, false);
commons.color.getBackgroundColor(targetEl, bgNodes, true);

assert.equal(window.pageYOffset, 0);
});
Expand All @@ -282,7 +320,7 @@ describe('color.getBackgroundColor', function () {
var bgNodes = [];
window.scroll(0, 0);

commons.color.getBackgroundColor(targetEl, bgNodes, true);
commons.color.getBackgroundColor(targetEl, bgNodes, false);

assert.notEqual(window.pageYOffset, 0);
});
Expand Down

0 comments on commit 6e0e149

Please sign in to comment.