diff --git a/lib/checks/color/color-contrast.js b/lib/checks/color/color-contrast.js index 448ca1a94b..c311bb577e 100644 --- a/lib/checks/color/color-contrast.js +++ b/lib/checks/color/color-contrast.js @@ -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) { diff --git a/lib/commons/color/get-background-color.js b/lib/commons/color/get-background-color.js index 929f212e26..c5ee6cce46 100644 --- a/lib/commons/color/get-background-color.js +++ b/lib/commons/color/get-background-color.js @@ -1,21 +1,27 @@ /*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); @@ -23,7 +29,8 @@ function getBackgroundForSingleNode(node) { bgColor = new color.Color(); bgColor.parseRgbString(bgColorString); } - var opacity = nodeStyle.getPropertyValue('opacity'); + + opacity = nodeStyle.getPropertyValue('opacity'); bgColor.alpha = bgColor.alpha * opacity; return bgColor; @@ -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)); @@ -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); + } } } @@ -108,14 +102,14 @@ 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); } @@ -123,17 +117,18 @@ color.getBackgroundColor = function(node, bgNodes, useScroll) { 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; } @@ -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)) { diff --git a/lib/commons/color/get-foreground-color.js b/lib/commons/color/get-foreground-color.js index 83db345c56..c8fc60a062 100644 --- a/lib/commons/color/get-foreground-color.js +++ b/lib/commons/color/get-foreground-color.js @@ -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(); @@ -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); diff --git a/lib/intro.stub b/lib/intro.stub index df30da614f..310066e452 100644 --- a/lib/intro.stub +++ b/lib/intro.stub @@ -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 diff --git a/test/commons/color/get-background-color.js b/test/commons/color/get-background-color.js index 8c39204f04..e3c162885e 100644 --- a/test/commons/color/get-background-color.js +++ b/test/commons/color/get-background-color.js @@ -217,15 +217,17 @@ describe('color.getBackgroundColor', function () { }); it('should ignore 0-height elements', function () { - fixture.innerHTML = '
' + - '
' + - '
' + + '
' + + '
' + '
'; 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); @@ -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]); @@ -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 = + '
' + + '
' + + '
' + + '
'+ + '
'; + + 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 = + '
' + + '
' + + ' ' + + '
'+ + '
' + + '
'; + + 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 = '
' + @@ -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); }); @@ -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); });