Skip to content

Commit e1bcafc

Browse files
authored
fix(color-contrast): Prevent crash on large inline elments #1306 (#1341)
1 parent 05f37de commit e1bcafc

File tree

2 files changed

+52
-35
lines changed

2 files changed

+52
-35
lines changed

lib/commons/color/get-background-color.js

Lines changed: 37 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -231,33 +231,42 @@ color.getCoords = function(rect) {
231231
*/
232232
color.getRectStack = function(elm) {
233233
let boundingCoords = color.getCoords(elm.getBoundingClientRect());
234-
if (boundingCoords) {
235-
let boundingStack = dom.shadowElementsFromPoint(
236-
boundingCoords.x,
237-
boundingCoords.y
238-
);
239-
// allows inline elements spanning multiple lines to be evaluated
240-
let rects = Array.from(elm.getClientRects());
241-
if (rects && rects.length > 1) {
242-
let filteredArr = rects
243-
.filter(rect => {
244-
// exclude manual line breaks in Chrome/Safari
245-
return rect.width && rect.width > 0;
246-
})
247-
.map(rect => {
248-
let coords = color.getCoords(rect);
249-
if (coords) {
250-
return dom.shadowElementsFromPoint(coords.x, coords.y);
251-
}
252-
});
253-
// add bounding client rect stack for comparison later
254-
filteredArr.splice(0, 0, boundingStack);
255-
return filteredArr;
256-
} else {
257-
return [boundingStack];
258-
}
234+
if (!boundingCoords) {
235+
return null;
259236
}
260-
return null;
237+
238+
let boundingStack = dom.shadowElementsFromPoint(
239+
boundingCoords.x,
240+
boundingCoords.y
241+
);
242+
243+
let rects = Array.from(elm.getClientRects());
244+
// If the element does not have multiple rects, like for display:block, return a single stack
245+
if (!rects || rects.length <= 1) {
246+
return [boundingStack];
247+
}
248+
249+
// Handle inline elements spanning multiple lines to be evaluated
250+
let filteredArr = rects
251+
.filter(rect => {
252+
// exclude manual line breaks in Chrome/Safari
253+
return rect.width && rect.width > 0;
254+
})
255+
.map(rect => {
256+
let coords = color.getCoords(rect);
257+
if (coords) {
258+
return dom.shadowElementsFromPoint(coords.x, coords.y);
259+
}
260+
});
261+
262+
if (filteredArr.some(stack => stack === undefined)) {
263+
// Can be happen when one or more of the rects sits outside the viewport
264+
return null;
265+
}
266+
267+
// add bounding client rect stack for comparison later
268+
filteredArr.splice(0, 0, boundingStack);
269+
return filteredArr;
261270
};
262271
/**
263272
* Get filtered stack of block and inline elements, excluding line breaks
@@ -284,14 +293,6 @@ color.filteredRectStack = function(elm) {
284293
let rectA = rectStack[index - 1],
285294
rectB = rectStack[index];
286295

287-
// If either rect are undefined, we cannot complete the test. We exit
288-
// early to avoid unhelpful errors.
289-
// See https://github.com/dequelabs/axe-core/issues/1306.
290-
if (rectA === undefined || rectB === undefined) {
291-
isSame = false;
292-
return;
293-
}
294-
295296
// if elements in clientRects are the same
296297
// or the boundingClientRect contains the differing element, pass it
297298
isSame =
@@ -351,7 +352,8 @@ color.getBackgroundColor = function(elm, bgElms = [], noScroll = false) {
351352
if (noScroll !== true) {
352353
// Avoid scrolling overflow:hidden containers, by only aligning to top
353354
// when not doing so would move the center point above the viewport top.
354-
const alignToTop = elm.clientHeight - 2 >= window.innerHeight * 2;
355+
const clientHeight = elm.getBoundingClientRect().height;
356+
const alignToTop = clientHeight - 2 >= window.innerHeight * 2;
355357
elm.scrollIntoView(alignToTop);
356358
}
357359
let bgColors = [];

test/commons/color/get-background-color.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -710,6 +710,21 @@ describe('color.getBackgroundColor', function() {
710710
assert.closeTo(actual.alpha, 1, 0.1);
711711
});
712712

713+
it('does returns null for inline elements that do not fit the viewport', function() {
714+
var html = '';
715+
for (var i = 0; i < 1000; i++) {
716+
html += 'foo<br />';
717+
}
718+
fixture.innerHTML = '<em>' + html + '</em>';
719+
axe._tree = axe.utils.getFlattenedTree(fixture.firstChild);
720+
var outcome = axe.commons.color.getBackgroundColor(fixture.firstChild, []);
721+
assert.isNull(outcome);
722+
assert.equal(
723+
axe.commons.color.incompleteData.get('bgColor'),
724+
'outsideViewport'
725+
);
726+
});
727+
713728
it('should return the body bgColor when content does not overlap', function() {
714729
fixture.innerHTML =
715730
'<div style="height: 20px; width: 30px; background-color: red;">' +

0 commit comments

Comments
 (0)