Skip to content

Commit c0021fe

Browse files
committed
Fix: Convert By.className to css selector for Firefox inside Shadow DOM
1 parent 6c607a8 commit c0021fe

File tree

2 files changed

+51
-8
lines changed

2 files changed

+51
-8
lines changed

java/src/org/openqa/selenium/remote/ShadowRoot.java

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.openqa.selenium.WebElement;
3232
import org.openqa.selenium.WrapsDriver;
3333
import org.openqa.selenium.internal.Require;
34+
import org.openqa.selenium.remote.shadow.FirefoxClassNameWorkaround;
3435

3536
// Note: we want people to code against the SearchContext API, so we keep this class package private
3637
class ShadowRoot implements SearchContext, WrapsDriver {
@@ -44,18 +45,20 @@ class ShadowRoot implements SearchContext, WrapsDriver {
4445

4546
@Override
4647
public List<WebElement> findElements(By by) {
48+
By resolved = FirefoxClassNameWorkaround.resolveForShadow(by, this);
4749
return parent.findElements(
4850
this,
4951
(using, value) -> FIND_ELEMENTS_FROM_SHADOW_ROOT(id, using, String.valueOf(value)),
50-
by);
52+
resolved);
5153
}
5254

5355
@Override
5456
public WebElement findElement(By by) {
57+
By resolved = FirefoxClassNameWorkaround.resolveForShadow(by, this);
5558
return parent.findElement(
5659
this,
5760
(using, value) -> FIND_ELEMENT_FROM_SHADOW_ROOT(id, using, String.valueOf(value)),
58-
by);
61+
resolved);
5962
}
6063

6164
@Override
@@ -73,12 +76,8 @@ private Map<String, Object> toJson() {
7376

7477
@Override
7578
public boolean equals(Object o) {
76-
if (this == o) {
77-
return true;
78-
}
79-
if (o == null || getClass() != o.getClass()) {
80-
return false;
81-
}
79+
if (this == o) return true;
80+
if (o == null || getClass() != o.getClass()) return false;
8281
ShadowRoot that = (ShadowRoot) o;
8382
return Objects.equals(parent, that.parent) && Objects.equals(id, that.id);
8483
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package org.openqa.selenium.remote.shadow;
2+
3+
import org.openqa.selenium.By;
4+
import org.openqa.selenium.InvalidSelectorException;
5+
import org.openqa.selenium.SearchContext;
6+
import org.openqa.selenium.WebDriver;
7+
import org.openqa.selenium.WrapsDriver;
8+
import org.openqa.selenium.remote.HasCapabilities;
9+
import org.openqa.selenium.Capabilities;
10+
11+
final class FirefoxClassNameWorkaround {
12+
13+
static boolean shouldUseCssSelector(By by, SearchContext context) {
14+
if (!(by instanceof By.ByClassName)) return false;
15+
if (!(context instanceof WrapsDriver)) return false;
16+
17+
try {
18+
WebDriver driver = ((WrapsDriver) context).getWrappedDriver();
19+
Capabilities caps = ((HasCapabilities) driver).getCapabilities();
20+
return "firefox".equalsIgnoreCase(caps.getBrowserName());
21+
} catch (Exception ignored) {
22+
return false;
23+
}
24+
}
25+
26+
static By convertToCss(By by) {
27+
String className = extractClassName(by);
28+
if (className.contains(" ")) {
29+
throw new InvalidSelectorException(
30+
"Compound class names not supported in Firefox Shadow DOM. Use single class name or By.cssSelector instead."
31+
);
32+
}
33+
return By.cssSelector("." + className);
34+
}
35+
36+
private static String extractClassName(By by) {
37+
String raw = by.toString().replace("By.className:", "").trim();
38+
return raw.replaceAll("\\s+", "");
39+
}
40+
41+
public static By resolveForShadow(By by, SearchContext context) {
42+
return shouldUseCssSelector(by, context) ? convertToCss(by) : by;
43+
}
44+
}

0 commit comments

Comments
 (0)