[🐛 Bug]: Displayed property of WebElement outputs incorrectly when using ShadowRoot
What happened?
When evaluating the Displayed property of the WebElement. Displayed returns False, but should return True as the element is displayed.
To use the below PowerShell code sample, save the script to a directory that also contains the WebDriver.dll and desired browserdriver.exe
---PowerShell code sample did not display correctly, so I attached as .txt file (Also included in the "How can we reproduce the issue" section--- Example.txt
Execution of the Example outputs:
Notice Displayed is False; yet on the page, the element is definitely displayed.
I have investigated and the issue seems to come from the use of ShadowRoot on the page and is-Displayed.js not able to correctly evaluate it.
The input element that we are checking the Displayed property on hosts a shadow-root
When is-displayed.js evaluates the element, it begins evaluating each element up the tree to the root element. If it were the normal DOM, it would reach the #document element and see this is nodeType 9 and return true if all elements are visible up the tree. In this case, when is-displayed.js begins going up the tree it reaches the "dnx-testfield" which is seen as the shadowRoot. In the case of this page, there is no assignedSlot for the shadowRoot, so null is returned and since null is returned, False is returned for displayed.
This can be seen in the below code; however, I only have the minified .js to work with, so it's a little hard to read.
function c(d) {
if (W(d) && "none" == Y(d, "display"))
return !1;
var e;
if ((e = d.parentNode) && e.shadowRoot && void 0 !== d.assignedSlot)
e = d.assignedSlot ? d.assignedSlot.parentNode : null;
else if (d.getDestinationInsertionPoints) {
var f = d.getDestinationInsertionPoints();
0 < f.length && (e = f[f.length - 1])
}
if (Fc && e instanceof ShadowRoot) {
if (e.host.shadowRoot !== e)
return !1;
e = e.host
}
return !e || 9 != e.nodeType && 11 != e.nodeType ? e && W(e, "DETAILS") && !e.open && !W(d, "SUMMARY") ? !1 : !!e && c(e) : !0
}
In the page when a parent element with a populated shadowRoot property is reached, it goes in the this block of code:
if ((e = d.parentNode) && e.shadowRoot && void 0 !== d.assignedSlot)
e = d.assignedSlot ? d.assignedSlot.parentNode : null;
In the page, the parent element with shadowRoot is dnx.textfield with label "Email"
When this is reached e is set to null because d.assignedSlot (the assignedSlot property value of the current element) is null
Then since e is null and e (the parent element is not type 9 or 11), then it does not return True as it should since the shadowRoot has been reached.
return !e || 9 != e.nodeType && 11 != e.nodeType ? e && W(e, "DETAILS") && !e.open && !W(d, "SUMMARY") ? !1 : !!e && c(e) : !0
It may not be the right solution, but I was able to address this by adding a conditional to the return that if the parent element matched the parent elements shadowroot property, then return the parent element instead of null when there is no assignedSlot.
if ((e = d.parentNode) && e.shadowRoot && void 0 !== d.assignedSlot)
e = d.assignedSlot ? d.assignedSlot.parentNode : d.parentNode == d.parentNode.shadowRoot ? d.parentNode : null;
else if (d.getDestinationInsertionPoints) {
var f = d.getDestinationInsertionPoints();
0 < f.length && (e = f[f.length - 1])
}
How can we reproduce the issue?
Function Get-Browser {
Param (
[Parameter(Mandatory=$true)]
[ValidateSet("Edge","Chrome")]
[string]$type
)
Set-EnvironmentPath
Add-Type -Path "$($PSScriptRoot)\WebDriver.dll"
Switch ($type) {
"edge" {
$Driver = New-Object OpenQA.Selenium.Edge.EdgeDriver
}
"chrome" {
$Driver = New-Object OpenQA.Selenium.Chrome.ChromeDriver
}
}
$Driver
}
Relevant log output
Unfortunately I was unable to find a good example of enabling logging with PowerShell, and the logging namespace did not show under OpenQA.Selenium.Internal, so I was not able to include this.
Operating System
Windows 10
Selenium version
PowerShell 5.1.19041.1682
What are the browser(s) and version(s) where you see this issue?
Chrome 121.0.6167.185 and Edge 121.0.2277.128 (both 64-bit)
What are the browser driver(s) and version(s) where you see this issue?
ChromeDriver 120.0.6099.109 (3419140ab665596f21b385ce136419fde0924272-refs/branch-heads/6099@{#1483}) Microsoft Edge WebDriver 120.0.2210.121 (6086ec26a02a23b6723b794ef7b81d37e71506e3)
Are you using Selenium Grid?
No
@SitrucHtims, thank you for creating this issue. We will troubleshoot it as soon as we can.
Info for maintainers
Triage this issue by using labels.
If information is missing, add a helpful comment and then I-issue-template label.
If the issue is a question, add the I-question label.
If the issue is valid but there is no time to troubleshoot it, consider adding the help wanted label.
If the issue requires changes or fixes from an external project (e.g., ChromeDriver, GeckoDriver, MSEdgeDriver, W3C),
add the applicable G-* label, and it will provide the correct link and auto-close the
issue.
After troubleshooting the issue, please add the R-awaiting answer label.
Thank you!
Sorry looks like the PowerShell script in the "How can we reproduce the issue?" section is cut off, but the full script is attached as a .txt file in the "What happened?" section
Does this happen in Firefox, Safari, and Edge as well?
I know it happens with Chrome and Edge, I can check for FF, but don't have a system to check Safari.
Can you check? This would hint if it is a ChromeDriver issue or a WebDriver issue.
I can confirm the behavior happens for Chrome, Edge, and FireFox. I am unsure about Safari as I do not have a system to test with.
It could actually be that the flow is never intended to reach this line of execution and getting there unintentionally.
if ((e = d.parentNode) && e.shadowRoot && void 0 !== d.assignedSlot)
e = d.assignedSlot ? d.assignedSlot.parentNode : null;
else if (d.getDestinationInsertionPoints) {
var f = d.getDestinationInsertionPoints();
0 < f.length && (e = f[f.length - 1])
}
void 0 !== d.assignedSlot appears to be checking to see if d.assignedSlot has a value or not, but it's using !== which also checks the data type, and null is not equal to undefined from a data type perspective.
IE:
undefined !== null
true
undefined != null
false
Since d.assignedSlot is null, maybe it is intended to skip this and the solution would simply be to change the condition:
from:
void 0 !== d.assignedSlot
to:
void 0 != d.assignedSlot
This issue is looking for contributors.
Please comment below or reach out to us through our IRC/Slack/Matrix channels if you are interested.