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 these prefixes:
46+ * <ul>
47+ * <li>"goog:" - Google
48+ * <li>"moz:" - Mozilla
49+ * <li>"ms:" - Microsoft
50+ * <li>"safari:" - Apple
51+ * <li>"se:" - Selenium
52+ * </ul>
53+ * <li>Extension capabilities with these suffixes:
54+ * <ul>
55+ * <li>"Options"
56+ * <li>"options"
57+ * <li>"loggingPrefs"
58+ * <li>"debuggerAddress"
59+ * </ul>
60+ * </ul>
4461 */
4562public class DefaultSlotMatcher implements SlotMatcher , Serializable {
4663
@@ -55,6 +72,13 @@ public class DefaultSlotMatcher implements SlotMatcher, Serializable {
5572 public static final List <String > MANDATORY_CAPABILITIES =
5673 Arrays .asList ("platformName" , "browserName" , "browserVersion" );
5774
75+ /*
76+ List of extension capability suffixes we never should try to match, they should be
77+ matched in the Node or in the browser driver.
78+ */
79+ private static final List <String > EXTENSION_CAPABILITY_SUFFIXES =
80+ Arrays .asList ("Options" , "options" , "loggingPrefs" , "debuggerAddress" );
81+
5882 @ Override
5983 public boolean matches (Capabilities stereotype , Capabilities capabilities ) {
6084
@@ -80,13 +104,14 @@ public boolean matches(Capabilities stereotype, Capabilities capabilities) {
80104
81105 // At the end, a simple browser, browserVersion and platformName match
82106 boolean browserNameMatch =
83- (capabilities .getBrowserName () == null || capabilities .getBrowserName ().isEmpty ())
107+ capabilities .getBrowserName () == null
108+ || capabilities .getBrowserName ().isEmpty ()
84109 || Objects .equals (stereotype .getBrowserName (), capabilities .getBrowserName ())
85110 || matchConditionToRemoveCapability (capabilities );
86111 boolean browserVersionMatch =
87- ( capabilities .getBrowserVersion () == null
88- || capabilities .getBrowserVersion ().isEmpty ()
89- || Objects .equals (capabilities .getBrowserVersion (), "stable" ) )
112+ capabilities .getBrowserVersion () == null
113+ || capabilities .getBrowserVersion ().isEmpty ()
114+ || Objects .equals (capabilities .getBrowserVersion (), "stable" )
90115 || browserVersionMatch (stereotype .getBrowserVersion (), capabilities .getBrowserVersion ())
91116 || matchConditionToRemoveCapability (capabilities );
92117 boolean platformNameMatch =
@@ -110,21 +135,17 @@ private Boolean initialMatch(Capabilities stereotype, Capabilities capabilities)
110135 name ->
111136 MANDATORY_CAPABILITIES .stream ()
112137 .noneMatch (mandatory -> mandatory .equalsIgnoreCase (name )))
113- .map (
138+ .filter (name -> capabilities .getCapability (name ) != null )
139+ .allMatch (
114140 name -> {
115- if (capabilities .getCapability (name ) instanceof String ) {
116- return stereotype
117- .getCapability (name )
118- .toString ()
119- .equalsIgnoreCase (capabilities .getCapability (name ).toString ());
120- } else {
121- return capabilities .getCapability (name ) == null
122- || Objects .equals (
123- stereotype .getCapability (name ), capabilities .getCapability (name ));
124- }
125- })
126- .reduce (Boolean ::logicalAnd )
127- .orElse (true );
141+ if (stereotype .getCapability (name ) instanceof String
142+ && capabilities .getCapability (name ) instanceof String ) {
143+ return ((String ) stereotype .getCapability (name ))
144+ .equalsIgnoreCase ((String ) capabilities .getCapability (name ));
145+ }
146+ return Objects .equals (
147+ stereotype .getCapability (name ), capabilities .getCapability (name ));
148+ });
128149 }
129150
130151 private Boolean managedDownloadsEnabled (Capabilities stereotype , Capabilities capabilities ) {
@@ -148,13 +169,11 @@ private Boolean platformVersionMatch(Capabilities stereotype, Capabilities capab
148169 */
149170 return capabilities .getCapabilityNames ().stream ()
150171 .filter (name -> name .contains ("platformVersion" ))
151- .map (
172+ .allMatch (
152173 platformVersionCapName ->
153174 Objects .equals (
154175 stereotype .getCapability (platformVersionCapName ),
155- capabilities .getCapability (platformVersionCapName )))
156- .reduce (Boolean ::logicalAnd )
157- .orElse (true );
176+ capabilities .getCapability (platformVersionCapName )));
158177 }
159178
160179 private Boolean extensionCapabilitiesMatch (Capabilities stereotype , Capabilities capabilities ) {
@@ -166,25 +185,26 @@ private Boolean extensionCapabilitiesMatch(Capabilities stereotype, Capabilities
166185 by the remote endpoint.
167186 */
168187 return stereotype .getCapabilityNames ().stream ()
188+ // examine only extension capabilities
169189 .filter (name -> name .contains (":" ))
170- .filter (name -> !name .toLowerCase ().contains ("options" ))
171- .filter (name -> capabilities .asMap ().containsKey (name ))
190+ // ignore special extension capability prefixes
172191 .filter (name -> EXTENSION_CAPABILITIES_PREFIXES .stream ().noneMatch (name ::contains ))
173- .map (
192+ // ignore special extension capability suffixes
193+ .filter (name -> EXTENSION_CAPABILITY_SUFFIXES .stream ().noneMatch (name ::endsWith ))
194+ // ignore capabilities not specified in the request
195+ .filter (name -> capabilities .getCapability (name ) != null )
196+ .allMatch (
174197 name -> {
175- if (capabilities .getCapability (name ) instanceof String ) {
176- return stereotype
177- .getCapability (name )
178- .toString ()
179- .equalsIgnoreCase (capabilities .getCapability (name ).toString ());
180- } else {
181- return capabilities .getCapability (name ) == null
182- || Objects .equals (
183- stereotype .getCapability (name ), capabilities .getCapability (name ));
184- }
185- })
186- .reduce (Boolean ::logicalAnd )
187- .orElse (true );
198+ // evaluate capabilities with String values
199+ if (stereotype .getCapability (name ) instanceof String
200+ && capabilities .getCapability (name ) instanceof String ) {
201+ return ((String ) stereotype .getCapability (name ))
202+ .equalsIgnoreCase ((String ) capabilities .getCapability (name ));
203+ }
204+ // evaluate capabilities with non-String values
205+ return Objects .equals (
206+ stereotype .getCapability (name ), capabilities .getCapability (name ));
207+ });
188208 }
189209
190210 public static Boolean matchConditionToRemoveCapability (Capabilities capabilities ) {
0 commit comments