3939 * Then the {@code stereotype} must contain the same values.
4040 * </ul>
4141 *
42- * <p>One thing to note is that extension capabilities are not considered when matching slots, since
43- * the matching of these is implementation-specific to each driver.
42+ * <p>Note that extension capabilities are considered for slot matching, with the following exceptions:
43+ *
44+ * <ul>
45+ * <li>Extension capabilities with prefix "se:"
46+ * <li>Extension capabilities with these suffixes:
47+ * <ul>
48+ * <li>"options"
49+ * <li>"Options"
50+ * <li>"loggingPrefs"
51+ * <li>"debuggerAddress"
52+ * </ul>
53+ * </ul>
4454 */
4555public class DefaultSlotMatcher implements SlotMatcher , Serializable {
4656
4757 /*
48- List of prefixed extension capabilities we never should try to match, they should be
58+ List of extension capability suffixes we never should try to match, they should be
4959 matched in the Node or in the browser driver.
5060 */
51- private static final List <String > EXTENSION_CAPABILITIES_PREFIXES =
52- Arrays .asList ("goog: " , "moz: " , "ms: " , "se: " );
61+ private static final List <String > EXTENSION_CAPABILITY_SUFFIXES =
62+ Arrays .asList ("Options " , "options " , "loggingPrefs " , "debuggerAddress " );
5363
5464 @ Override
5565 public boolean matches (Capabilities stereotype , Capabilities capabilities ) {
@@ -76,14 +86,14 @@ public boolean matches(Capabilities stereotype, Capabilities capabilities) {
7686
7787 // At the end, a simple browser, browserVersion and platformName match
7888 boolean browserNameMatch =
79- (capabilities .getBrowserName () == null || capabilities .getBrowserName ().isEmpty ())
89+ capabilities .getBrowserName () == null
90+ || capabilities .getBrowserName ().isEmpty ()
8091 || Objects .equals (stereotype .getBrowserName (), capabilities .getBrowserName ());
8192 boolean browserVersionMatch =
82- (capabilities .getBrowserVersion () == null
83- || capabilities .getBrowserVersion ().isEmpty ()
84- || Objects .equals (capabilities .getBrowserVersion (), "stable" ))
85- || browserVersionMatch (
86- stereotype .getBrowserVersion (), capabilities .getBrowserVersion ());
93+ capabilities .getBrowserVersion () == null
94+ || capabilities .getBrowserVersion ().isEmpty ()
95+ || Objects .equals (capabilities .getBrowserVersion (), "stable" )
96+ || browserVersionMatch (stereotype .getBrowserVersion (), capabilities .getBrowserVersion ());
8797 boolean platformNameMatch =
8898 capabilities .getPlatformName () == null
8999 || Objects .equals (stereotype .getPlatformName (), capabilities .getPlatformName ())
@@ -102,21 +112,17 @@ private Boolean initialMatch(Capabilities stereotype, Capabilities capabilities)
102112 .filter (name -> !name .contains (":" ))
103113 // Platform matching is special, we do it later
104114 .filter (name -> !"platformName" .equalsIgnoreCase (name ))
105- .map (
115+ .filter (name -> capabilities .getCapability (name ) != null )
116+ .allMatch (
106117 name -> {
107- if (capabilities .getCapability (name ) instanceof String ) {
108- return stereotype
109- .getCapability (name )
110- .toString ()
111- .equalsIgnoreCase (capabilities .getCapability (name ).toString ());
112- } else {
113- return capabilities .getCapability (name ) == null
114- || Objects .equals (
115- stereotype .getCapability (name ), capabilities .getCapability (name ));
118+ if (stereotype .getCapability (name ) instanceof String
119+ && capabilities .getCapability (name ) instanceof String ) {
120+ return ((String ) stereotype .getCapability (name ))
121+ .equalsIgnoreCase ((String ) capabilities .getCapability (name ));
116122 }
117- })
118- . reduce ( Boolean :: logicalAnd )
119- . orElse ( true );
123+ return Objects . equals (
124+ stereotype . getCapability ( name ), capabilities . getCapability ( name ));
125+ } );
120126 }
121127
122128 private Boolean managedDownloadsEnabled (Capabilities stereotype , Capabilities capabilities ) {
@@ -140,39 +146,39 @@ private Boolean platformVersionMatch(Capabilities stereotype, Capabilities capab
140146 */
141147 return capabilities .getCapabilityNames ().stream ()
142148 .filter (name -> name .contains ("platformVersion" ))
143- .map (
149+ .allMatch (
144150 platformVersionCapName ->
145151 Objects .equals (
146152 stereotype .getCapability (platformVersionCapName ),
147- capabilities .getCapability (platformVersionCapName )))
148- .reduce (Boolean ::logicalAnd )
149- .orElse (true );
153+ capabilities .getCapability (platformVersionCapName )));
150154 }
151155
152156 private Boolean extensionCapabilitiesMatch (Capabilities stereotype , Capabilities capabilities ) {
153157 /*
154- We match extension capabilities when they are not prefixed with any of the
155- EXTENSION_CAPABILITIES_PREFIXES items. Also, we match them only when the capabilities
156- of the new session request contains that specific extension capability .
158+ We match extension capabilities in new session requests whose names do not have the prefix "se:" or
159+ one of the reserved suffixes ("options", "Options", "loggingPrefs", or "debuggerAddress"). These are
160+ forwarded to the matched node for use in configuration, but are not considered for node matching .
157161 */
158162 return stereotype .getCapabilityNames ().stream ()
163+ // examine only extension capabilities
159164 .filter (name -> name .contains (":" ))
160- .filter (name -> capabilities .asMap ().containsKey (name ))
161- .filter (name -> EXTENSION_CAPABILITIES_PREFIXES .stream ().noneMatch (name ::contains ))
162- .map (
165+ // ignore Selenium extension capabilities
166+ .filter (name -> !name .startsWith ("se:" ))
167+ // ignore special extension capability suffixes
168+ .filter (name -> EXTENSION_CAPABILITY_SUFFIXES .stream ().noneMatch (name ::endsWith ))
169+ // ignore capabilities not specified in the request
170+ .filter (name -> capabilities .getCapability (name ) != null )
171+ .allMatch (
163172 name -> {
164- if (capabilities .getCapability (name ) instanceof String ) {
165- return stereotype
166- .getCapability (name )
167- .toString ()
168- .equalsIgnoreCase (capabilities .getCapability (name ).toString ());
169- } else {
170- return capabilities .getCapability (name ) == null
171- || Objects .equals (
172- stereotype .getCapability (name ), capabilities .getCapability (name ));
173+ // evaluate capabilities with String values
174+ if (stereotype .getCapability (name ) instanceof String
175+ && capabilities .getCapability (name ) instanceof String ) {
176+ return ((String ) stereotype .getCapability (name ))
177+ .equalsIgnoreCase ((String ) capabilities .getCapability (name ));
173178 }
174- })
175- .reduce (Boolean ::logicalAnd )
176- .orElse (true );
179+ // evaluate capabilities with Number or Boolean values
180+ return Objects .equals (
181+ stereotype .getCapability (name ), capabilities .getCapability (name ));
182+ });
177183 }
178184}
0 commit comments