diff --git a/lib/commons/dom/get-overflow-hidden-ancestors.js b/lib/commons/dom/get-overflow-hidden-ancestors.js index 8ace4290..b68432d3 100644 --- a/lib/commons/dom/get-overflow-hidden-ancestors.js +++ b/lib/commons/dom/get-overflow-hidden-ancestors.js @@ -27,10 +27,14 @@ const getOverflowHiddenAncestors = memoize( ) { ancestors.push(vNode); } - } else { - if (overflow === 'hidden' || overflow.includes('clip')) { - ancestors.push(vNode); - } + } else if ( + cache.get('ruleId') && + cache.get('ruleId') === 'reflow-4x-zoom-scroll' && + overflow.includes('hidden') + ) { + ancestors.push(vNode); + } else if (overflow === 'hidden' || overflow.includes('clip')) { + ancestors.push(vNode); } return ancestors.concat(getOverflowHiddenAncestors(vNode.parent)); diff --git a/lib/commons/dom/get-visible-child-text-rects.js b/lib/commons/dom/get-visible-child-text-rects.js index 2cd01773..7a949442 100644 --- a/lib/commons/dom/get-visible-child-text-rects.js +++ b/lib/commons/dom/get-visible-child-text-rects.js @@ -1,4 +1,4 @@ -import { getNodeFromTree, memoize } from '../../core/utils'; +import { getNodeFromTree } from '../../core/utils'; import { sanitize } from '../text'; import { getIntersectionRect, getRectCenter, isPointInRect } from '../math'; import getOverflowHiddenAncestors from './get-overflow-hidden-ancestors'; @@ -11,53 +11,70 @@ import cache from '../../core/base/cache'; * @instance * @param {Element} node */ -const getVisibleChildTextRects = memoize( - function getVisibleChildTextRectsMemoized(node) { - const vNode = getNodeFromTree(node); - const nodeRect = vNode.boundingClientRect; - const clientRects = []; - const overflowHiddenNodes = getOverflowHiddenAncestors(vNode); +const getVisibleChildTextRects = (node, options = {}) => { + const { + checkTextRectOutsideNodeBoundingRect = false, + includeOutsideBounds = true, + includeOverflowHidden = false, + checkNoVisibleRectsIdentified = false + } = options; + const vNode = getNodeFromTree(node); + const nodeRect = vNode.boundingClientRect; + const clientRects = []; + const overflowHiddenNodes = getOverflowHiddenAncestors(vNode); - node.childNodes.forEach(textNode => { - if (textNode.nodeType !== 3 || sanitize(textNode.nodeValue) === '') { - return; - } - - const contentRects = getContentRects(textNode); - if (isOutsideNodeBounds(contentRects, nodeRect) && !cache.get('ruleId')) { - return; - } - - clientRects.push(...filterHiddenRects(contentRects, overflowHiddenNodes)); - }); + node.childNodes.forEach(textNode => { + if (textNode.nodeType !== 3 || sanitize(textNode.nodeValue) === '') { + return; + } - // a11y-engine-domforge change + const contentRects = getContentRects(textNode); if ( - clientRects.length <= 0 && - cache.get('ruleId') && - cache.get('ruleId') === 'resize-2x-zoom' + includeOutsideBounds && + isOutsideNodeBounds( + contentRects, + nodeRect, + checkTextRectOutsideNodeBoundingRect + ) && + (!cache.get('ruleId') || cache.get('ruleId') === 'reflow-4x-zoom-scroll') ) { - return []; + return; } - /** - * if all text rects are larger than the bounds of the node, - * or goes outside of the bounds of the node, we need to use - * the nodes bounding rect so we stay within the bounds of the - * element. - * - * @see https://github.com/dequelabs/axe-core/issues/2178 - * @see https://github.com/dequelabs/axe-core/issues/2483 - * @see https://github.com/dequelabs/axe-core/issues/2681 - * - * also need to resize the nodeRect to fit within the bounds of any overflow: hidden ancestors. - * - * @see https://github.com/dequelabs/axe-core/issues/4253 - */ - return clientRects.length - ? clientRects - : filterHiddenRects([nodeRect], overflowHiddenNodes); + + clientRects.push( + ...filterHiddenRects( + contentRects, + includeOverflowHidden ? [] : overflowHiddenNodes + ) + ); + }); + + // a11y-engine-domforge change + if ( + clientRects.length <= 0 && + ((cache.get('ruleId') && cache.get('ruleId') === 'resize-2x-zoom') || + checkNoVisibleRectsIdentified) + ) { + return []; } -); + /** + * if all text rects are larger than the bounds of the node, + * or goes outside of the bounds of the node, we need to use + * the nodes bounding rect so we stay within the bounds of the + * element. + * + * @see https://github.com/dequelabs/axe-core/issues/2178 + * @see https://github.com/dequelabs/axe-core/issues/2483 + * @see https://github.com/dequelabs/axe-core/issues/2681 + * + * also need to resize the nodeRect to fit within the bounds of any overflow: hidden ancestors. + * + * @see https://github.com/dequelabs/axe-core/issues/4253 + */ + return clientRects.length + ? clientRects + : filterHiddenRects([nodeRect], overflowHiddenNodes); +}; export default getVisibleChildTextRects; function getContentRects(node) { @@ -72,10 +89,20 @@ function getContentRects(node) { * when determining the rect stack we will also use the midpoint * of the text rect to determine out of bounds */ -function isOutsideNodeBounds(rects, nodeRect) { +function isOutsideNodeBounds( + rects, + nodeRect, + checkTextRectOutsideNodeBoundingRect = false +) { return rects.some(rect => { const centerPoint = getRectCenter(rect); - return !isPointInRect(centerPoint, nodeRect); + if (checkTextRectOutsideNodeBoundingRect) { + return ( + !isPointInRect(centerPoint, nodeRect) || rect.right > nodeRect.right + ); + } else { + return !isPointInRect(centerPoint, nodeRect); + } }); } diff --git a/lib/rules/autocomplete-a11y-matches.js b/lib/rules/autocomplete-a11y-matches.js index e788e278..29e258ca 100644 --- a/lib/rules/autocomplete-a11y-matches.js +++ b/lib/rules/autocomplete-a11y-matches.js @@ -92,6 +92,14 @@ function quantityField(node) { }); } +function isReadOnly(node) { + return node.hasAttribute('readonly'); +} + +function isCombobox(node) { + return node.getAttribute('role') === 'combobox'; +} + function autocompleteA11yMatches(node, virtualNode) { const a11yEngineFlag = true; /* the flag is used to tell autocomplete matcher that it is being called @@ -100,7 +108,9 @@ function autocompleteA11yMatches(node, virtualNode) { return ( autocompleteMatches(node, virtualNode, a11yEngineFlag) && !nodeIsASearchFunctionality(node) && - !quantityField(node) + !quantityField(node) && + !isReadOnly(node) && + !isCombobox(node) ); }