Skip to content

Commit 7f66ee8

Browse files
WilcoFiersmarcysutton
authored andcommitted
fix: Ignore shadowRoots on elements that don't allow them
1 parent 9402a7c commit 7f66ee8

File tree

3 files changed

+62
-2
lines changed

3 files changed

+62
-2
lines changed

lib/checks/.DS_Store

8 KB
Binary file not shown.

lib/core/utils/flattened-tree.js

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,20 @@ function getSlotChildren(node) {
5151
return retVal;
5252
}
5353

54+
const possibleShadowRoots = ['article', 'aside', 'blockquote',
55+
'body', 'div', 'footer', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
56+
'header', 'main', 'nav', 'p', 'section', 'span'];
57+
axe.utils.isShadowRoot = function isShadowRoot (node) {
58+
const nodeName = node.nodeName.toLowerCase();
59+
if (node.shadowRoot) {
60+
if (/^[a-z][a-z0-9_.-]*-[a-z0-9_.-]*$/.test(nodeName) ||
61+
possibleShadowRoots.includes(nodeName)) {
62+
return true;
63+
}
64+
}
65+
return false;
66+
};
67+
5468
/**
5569
* Recursvely returns an array of the virtual DOM nodes at this level
5670
* excluding comment nodes and the shadow DOM nodes <content> and <slot>
@@ -75,8 +89,8 @@ axe.utils.getFlattenedTree = function (node, shadowId) {
7589
node = node.documentElement;
7690
}
7791
nodeName = node.nodeName.toLowerCase();
78-
// for some reason Chrome's marquee element has an open shadow DOM
79-
if (node.shadowRoot && nodeName !== 'marquee') {
92+
93+
if (axe.utils.isShadowRoot(node)) {
8094
// generate an ID for this shadow root and overwrite the current
8195
// closure shadowId with this value so that it cascades down the tree
8296
retVal = virtualDOMfromNode(node, shadowId);

test/core/utils/flattened-tree.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,32 @@ function shadowIdAssertions () {
6666

6767
}
6868

69+
describe('isShadowRoot', function () {
70+
'use strict';
71+
var isShadowRoot = axe.utils.isShadowRoot;
72+
73+
it('returns false if the node has no shadowRoot', function () {
74+
assert.isFalse(isShadowRoot({ nodeName: 'DIV', shadowRoot: undefined }));
75+
});
76+
it('returns true if the native element allows shadow DOM', function () {
77+
assert.isTrue(isShadowRoot({ nodeName: 'DIV', shadowRoot: {} }));
78+
assert.isTrue(isShadowRoot({ nodeName: 'H1', shadowRoot: {} }));
79+
assert.isTrue(isShadowRoot({ nodeName: 'ASIDE', shadowRoot: {} }));
80+
});
81+
it('returns true if a custom element with shadowRoot', function () {
82+
assert.isTrue(isShadowRoot({ nodeName: 'X-BUTTON', shadowRoot: {} }));
83+
assert.isTrue(isShadowRoot({ nodeName: 'T1000-SCHWARZENEGGER', shadowRoot: {} }));
84+
});
85+
it('returns true if an invalid custom element with shadowRoot', function () {
86+
assert.isFalse(isShadowRoot({ nodeName: '0-BUZZ', shadowRoot: {} }));
87+
assert.isFalse(isShadowRoot({ nodeName: '--ELM--', shadowRoot: {} }));
88+
});
89+
it('returns false if the native element does not allow shadow DOM', function () {
90+
assert.isFalse(isShadowRoot({ nodeName: 'IFRAME', shadowRoot: {} }));
91+
assert.isFalse(isShadowRoot({ nodeName: 'STRONG', shadowRoot: {} }));
92+
});
93+
});
94+
6995
if (shadowSupport.v0) {
7096
describe('flattened-tree shadow DOM v0', function () {
7197
'use strict';
@@ -154,6 +180,26 @@ if (shadowSupport.v1) {
154180
assert.isTrue(virtualDOM[0].children[2].children[1].children[0].children[0].actualNode.textContent === 'fallback content');
155181
assert.isTrue(virtualDOM[0].children[2].children[1].children[0].children[1].actualNode.nodeName === 'LI');
156182
});
183+
it('calls isShadowRoot to identify a shadow root', function () {
184+
var isShadowRoot = axe.utils.isShadowRoot;
185+
fixture.innerHTML = '<div></div>';
186+
var div = fixture.querySelector('div');
187+
var shadowRoot = div.attachShadow({ mode: 'open' });
188+
shadowRoot.innerHTML = '<h1>Just a man in the back</h1>';
189+
190+
// Test without isShqdowRoot overwritten
191+
assert.equal(axe.utils.getFlattenedTree(div)[0].children.length, 1);
192+
193+
var called = false;
194+
axe.utils.isShadowRoot = function () {
195+
called = true;
196+
return false;
197+
};
198+
// Test with isShadowRoot overwritten
199+
assert.equal(axe.utils.getFlattenedTree(div)[0].children.length, 0);
200+
assert.isTrue(called);
201+
axe.utils.isShadowRoot = isShadowRoot;
202+
});
157203
});
158204
describe('flattened-tree shadow DOM v1: boxed slots', function () {
159205
'use strict';

0 commit comments

Comments
 (0)