From 1a504b3d2db98ca370e3f87020f504fd6a7019cd Mon Sep 17 00:00:00 2001 From: Pooja0504 Date: Wed, 4 Jun 2025 15:13:21 +0530 Subject: [PATCH 1/4] Updated get-visible-child-text-rects --- .../dom/get-visible-child-text-rects.js | 115 +++++++----------- 1 file changed, 41 insertions(+), 74 deletions(-) diff --git a/lib/commons/dom/get-visible-child-text-rects.js b/lib/commons/dom/get-visible-child-text-rects.js index 7b56fc09..7ffa5217 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 } from '../../core/utils'; +import { getNodeFromTree, memoize } from '../../core/utils'; import { sanitize } from '../text'; import { getIntersectionRect, getRectCenter, isPointInRect } from '../math'; import getOverflowHiddenAncestors from './get-overflow-hidden-ancestors'; @@ -11,72 +11,49 @@ import cache from '../../core/base/cache'; * @instance * @param {Element} node */ -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); +const getVisibleChildTextRects = memoize( + function getVisibleChildTextRectsMemoized(node) { + 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; - } + node.childNodes.forEach(textNode => { + if (textNode.nodeType !== 3 || sanitize(textNode.nodeValue) === '') { + return; + } - const contentRects = getContentRects(textNode); - if ( - includeOutsideBounds && - isOutsideNodeBounds( - contentRects, - nodeRect, - checkTextRectOutsideNodeBoundingRect - ) && - (!cache.get('ruleId') || - cache.get('ruleId') === 'reflow-4x-zoom-scroll' || - cache.get('ruleId') === 'color-contrast') - ) { - return; - } + const contentRects = getContentRects(textNode); + if (isOutsideNodeBounds(contentRects, nodeRect) && !cache.get('ruleId')) { + return; + } - clientRects.push( - ...filterHiddenRects( - contentRects, - includeOverflowHidden ? [] : overflowHiddenNodes - ) - ); - }); + clientRects.push(...filterHiddenRects(contentRects, overflowHiddenNodes)); + }); - // a11y-engine-domforge change - if ( - clientRects.length <= 0 && - ((cache.get('ruleId') && cache.get('ruleId') === 'resize-2x-zoom') || - checkNoVisibleRectsIdentified) - ) { - return []; + // a11y-engine-domforge change + if (clientRects.length <= 0) { + 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); } - /** - * 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) { @@ -91,20 +68,10 @@ 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, - checkTextRectOutsideNodeBoundingRect = false -) { +function isOutsideNodeBounds(rects, nodeRect) { return rects.some(rect => { const centerPoint = getRectCenter(rect); - if (checkTextRectOutsideNodeBoundingRect) { - return ( - !isPointInRect(centerPoint, nodeRect) || rect.right > nodeRect.right - ); - } else { - return !isPointInRect(centerPoint, nodeRect); - } + return !isPointInRect(centerPoint, nodeRect); }); } From fd25ae9265c982f99c08cd835c4a372b55fa09c9 Mon Sep 17 00:00:00 2001 From: Pooja0504 Date: Wed, 4 Jun 2025 15:26:45 +0530 Subject: [PATCH 2/4] AXE-1598 : no-visible-label rule --- .../get-visible-child-text-rects-memoized.js | 104 ++++++++++++++++ .../dom/get-visible-child-text-rects.js | 115 +++++++++++------- 2 files changed, 178 insertions(+), 41 deletions(-) create mode 100644 lib/commons/dom/get-visible-child-text-rects-memoized.js diff --git a/lib/commons/dom/get-visible-child-text-rects-memoized.js b/lib/commons/dom/get-visible-child-text-rects-memoized.js new file mode 100644 index 00000000..acb4213c --- /dev/null +++ b/lib/commons/dom/get-visible-child-text-rects-memoized.js @@ -0,0 +1,104 @@ +import { getNodeFromTree, memoize } from '../../core/utils'; +import { sanitize } from '../text'; +import { getIntersectionRect, getRectCenter, isPointInRect } from '../math'; +import getOverflowHiddenAncestors from './get-overflow-hidden-ancestors'; +import cache from '../../core/base/cache'; + +/** + * Get the visible text client rects of a node. + * @method getVisibleChildTextRect + * @memberof axe.commons.dom + * @instance + * @param {Element} node + */ +const getVisibleChildTextRect = memoize( + function getVisibleChildTextRectMemoized(node) { + 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)); + }); + + // a11y-engine-domforge change + if (clientRects.length <= 0) { + 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 getVisibleChildTextRect; + +function getContentRects(node) { + const range = document.createRange(); + range.selectNodeContents(node); + return Array.from(range.getClientRects()); +} + +/** + * Check to see if the text rect size is outside the of the + * nodes bounding rect. Since we use the midpoint of the element + * when determining the rect stack we will also use the midpoint + * of the text rect to determine out of bounds + */ +function isOutsideNodeBounds(rects, nodeRect) { + return rects.some(rect => { + const centerPoint = getRectCenter(rect); + return !isPointInRect(centerPoint, nodeRect); + }); +} + +/** + * Filter out 0 width and height rects (newline characters) and + * any rects that are outside the bounds of overflow hidden + * ancestors + */ +function filterHiddenRects(contentRects, overflowHiddenNodes) { + const visibleRects = []; + contentRects.forEach(contentRect => { + // ie11 has newline characters return 0.00998, so we'll say if the + // line is < 1 it shouldn't be counted + if (contentRect.width < 1 || contentRect.height < 1) { + return; + } + + // update the rect size to fit inside the bounds of all overflow + // hidden ancestors + const visibleRect = overflowHiddenNodes.reduce((rect, overflowNode) => { + return rect && getIntersectionRect(rect, overflowNode.boundingClientRect); + }, contentRect); + + if (visibleRect) { + visibleRects.push(visibleRect); + } + }); + + return visibleRects; +} diff --git a/lib/commons/dom/get-visible-child-text-rects.js b/lib/commons/dom/get-visible-child-text-rects.js index 7ffa5217..7b56fc09 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,49 +11,72 @@ 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; - } + node.childNodes.forEach(textNode => { + if (textNode.nodeType !== 3 || sanitize(textNode.nodeValue) === '') { + return; + } - const contentRects = getContentRects(textNode); - if (isOutsideNodeBounds(contentRects, nodeRect) && !cache.get('ruleId')) { - return; - } + const contentRects = getContentRects(textNode); + if ( + includeOutsideBounds && + isOutsideNodeBounds( + contentRects, + nodeRect, + checkTextRectOutsideNodeBoundingRect + ) && + (!cache.get('ruleId') || + cache.get('ruleId') === 'reflow-4x-zoom-scroll' || + cache.get('ruleId') === 'color-contrast') + ) { + return; + } - clientRects.push(...filterHiddenRects(contentRects, overflowHiddenNodes)); - }); + clientRects.push( + ...filterHiddenRects( + contentRects, + includeOverflowHidden ? [] : overflowHiddenNodes + ) + ); + }); - // a11y-engine-domforge change - if (clientRects.length <= 0) { - 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); + // 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) { @@ -68,10 +91,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); + } }); } From 7f9a1001b9dab824a441a22f11dfb06accca2855 Mon Sep 17 00:00:00 2001 From: Pooja0504 Date: Wed, 4 Jun 2025 16:26:27 +0530 Subject: [PATCH 3/4] Updated code --- ...ild-text-rects-memoized.js => get-visible-child-text-rect.js} | 0 lib/commons/dom/index.js | 1 + 2 files changed, 1 insertion(+) rename lib/commons/dom/{get-visible-child-text-rects-memoized.js => get-visible-child-text-rect.js} (100%) diff --git a/lib/commons/dom/get-visible-child-text-rects-memoized.js b/lib/commons/dom/get-visible-child-text-rect.js similarity index 100% rename from lib/commons/dom/get-visible-child-text-rects-memoized.js rename to lib/commons/dom/get-visible-child-text-rect.js diff --git a/lib/commons/dom/index.js b/lib/commons/dom/index.js index 57ae0756..00036f4b 100644 --- a/lib/commons/dom/index.js +++ b/lib/commons/dom/index.js @@ -22,6 +22,7 @@ export { default as getTargetSize } from './get-target-size'; export { default as getTextElementStack } from './get-text-element-stack'; export { default as getViewportSize } from './get-viewport-size'; export { default as getVisibleChildTextRects } from './get-visible-child-text-rects'; +export { default as getVisibleChildTextRect } from './get-visible-child-text-rect'; export { default as hasContentVirtual } from './has-content-virtual'; export { default as hasContent } from './has-content'; export { default as hasLangText } from './has-lang-text'; From 4562db3e3550793d9f562008f4cc012e55611edf Mon Sep 17 00:00:00 2001 From: Pooja0504 Date: Fri, 20 Jun 2025 13:04:14 +0530 Subject: [PATCH 4/4] Autocomplete-valid stable rule --- lib/rules/autocomplete-valid.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/rules/autocomplete-valid.json b/lib/rules/autocomplete-valid.json index 8d71b20f..9afefdcd 100644 --- a/lib/rules/autocomplete-valid.json +++ b/lib/rules/autocomplete-valid.json @@ -9,8 +9,7 @@ "EN-301-549", "EN-9.1.3.5", "ACT", - "a11y-engine", - "a11y-engine-experimental" + "a11y-engine" ], "actIds": ["73f2c2"], "metadata": {