From 5e8e34c111a1224ca0931bbb1e46e81b95d169f7 Mon Sep 17 00:00:00 2001 From: Andrei Solntsev Date: Fri, 6 Mar 2026 09:13:57 +0200 Subject: [PATCH 1/3] make the signature change in `ExecuteMethod` backward compatible ... to avoid too many changes e.g. in Appium project (its has own implementation `AppiumExecutionMethod`). In PR #17152, I changed signature of `ExecuteMethod.execute()` - and later realized that it breaks backward compatibility in Appium. --- .../openqa/selenium/chromium/AddHasCasting.java | 3 +-- .../org/openqa/selenium/remote/ExecuteMethod.java | 14 ++++++++++++-- .../openqa/selenium/remote/FedCmDialogImpl.java | 2 +- .../openqa/selenium/remote/LocalExecuteMethod.java | 2 +- .../selenium/remote/RemoteExecuteMethod.java | 5 ++--- 5 files changed, 17 insertions(+), 9 deletions(-) diff --git a/java/src/org/openqa/selenium/chromium/AddHasCasting.java b/java/src/org/openqa/selenium/chromium/AddHasCasting.java index b52d55e50f0dc..28afef620a5cf 100644 --- a/java/src/org/openqa/selenium/chromium/AddHasCasting.java +++ b/java/src/org/openqa/selenium/chromium/AddHasCasting.java @@ -18,7 +18,6 @@ package org.openqa.selenium.chromium; import static java.util.Collections.emptyList; -import static java.util.Objects.requireNonNullElse; import java.util.List; import java.util.Map; @@ -56,7 +55,7 @@ public HasCasting getImplementation(Capabilities capabilities, ExecuteMethod exe return new HasCasting() { @Override public List> getCastSinks() { - return requireNonNullElse(executeMethod.execute(GET_CAST_SINKS, null), emptyList()); + return executeMethod.execute(GET_CAST_SINKS, null, emptyList()); } @Override diff --git a/java/src/org/openqa/selenium/remote/ExecuteMethod.java b/java/src/org/openqa/selenium/remote/ExecuteMethod.java index e348e5daf4604..c5fdab47148a1 100644 --- a/java/src/org/openqa/selenium/remote/ExecuteMethod.java +++ b/java/src/org/openqa/selenium/remote/ExecuteMethod.java @@ -18,6 +18,7 @@ package org.openqa.selenium.remote; import static java.util.Objects.requireNonNull; +import static java.util.Objects.requireNonNullElse; import java.util.Map; import org.jspecify.annotations.NullMarked; @@ -37,9 +38,18 @@ public interface ExecuteMethod { * @param parameters The parameters to execute that command with * @return The result of {@link Response#getValue()}. */ - @Nullable T execute(String commandName, @Nullable Map parameters); + @Nullable Object execute(String commandName, @Nullable Map parameters); + + default T execute(String commandName, @Nullable Map parameters, T defaultValue) { + return requireNonNullElse(executeOptional(commandName, parameters), defaultValue); + } + + @SuppressWarnings("unchecked") + default @Nullable T executeOptional(String commandName, @Nullable Map parameters) { + return (T) execute(commandName, parameters); + } default T executeRequired(String commandName, @Nullable Map parameters) { - return requireNonNull(execute(commandName, parameters)); + return requireNonNull(executeOptional(commandName, parameters)); } } diff --git a/java/src/org/openqa/selenium/remote/FedCmDialogImpl.java b/java/src/org/openqa/selenium/remote/FedCmDialogImpl.java index ed50cfab338fe..7e0488153380e 100644 --- a/java/src/org/openqa/selenium/remote/FedCmDialogImpl.java +++ b/java/src/org/openqa/selenium/remote/FedCmDialogImpl.java @@ -46,7 +46,7 @@ public void selectAccount(int index) { @Nullable @Override public String getDialogType() { - return executeMethod.execute(DriverCommand.GET_FEDCM_DIALOG_TYPE, null); + return executeMethod.executeOptional(DriverCommand.GET_FEDCM_DIALOG_TYPE, null); } @Override diff --git a/java/src/org/openqa/selenium/remote/LocalExecuteMethod.java b/java/src/org/openqa/selenium/remote/LocalExecuteMethod.java index 0d2b4e4cb5c36..84a6093dc8928 100644 --- a/java/src/org/openqa/selenium/remote/LocalExecuteMethod.java +++ b/java/src/org/openqa/selenium/remote/LocalExecuteMethod.java @@ -26,7 +26,7 @@ class LocalExecuteMethod implements ExecuteMethod { @Nullable @Override - public T execute(String commandName, @Nullable Map parameters) { + public Object execute(String commandName, @Nullable Map parameters) { throw new WebDriverException("Cannot execute remote command: " + commandName); } } diff --git a/java/src/org/openqa/selenium/remote/RemoteExecuteMethod.java b/java/src/org/openqa/selenium/remote/RemoteExecuteMethod.java index 8f28193c5718e..9be7a20bfd2be 100644 --- a/java/src/org/openqa/selenium/remote/RemoteExecuteMethod.java +++ b/java/src/org/openqa/selenium/remote/RemoteExecuteMethod.java @@ -31,9 +31,8 @@ public RemoteExecuteMethod(RemoteWebDriver driver) { this.driver = Require.nonNull("Remote WebDriver", driver); } - @SuppressWarnings("unchecked") @Override - public @Nullable T execute(String commandName, @Nullable Map parameters) { + public @Nullable Object execute(String commandName, @Nullable Map parameters) { Response response; if (parameters == null || parameters.isEmpty()) { @@ -42,7 +41,7 @@ public RemoteExecuteMethod(RemoteWebDriver driver) { response = driver.execute(commandName, parameters); } - return (T) response.getValue(); + return response.getValue(); } @Override From 20ce091b6d73676dcb4de053fcefa4618c1873d0 Mon Sep 17 00:00:00 2001 From: Andrei Solntsev Date: Fri, 6 Mar 2026 09:50:26 +0200 Subject: [PATCH 2/3] rename the additional helper methods `ExecuteMethod` --- .../selenium/chromium/AddHasCasting.java | 2 +- .../openqa/selenium/chromium/AddHasCdp.java | 3 +-- .../chromium/AddHasNetworkConditions.java | 3 +-- .../selenium/firefox/AddHasContext.java | 2 +- .../selenium/firefox/AddHasExtensions.java | 2 +- .../openqa/selenium/remote/ExecuteMethod.java | 24 +++++++++++++++---- .../selenium/remote/FedCmDialogImpl.java | 9 ++++--- .../openqa/selenium/remote/RemoteLogs.java | 3 +-- .../selenium/safari/AddHasPermissions.java | 2 +- .../selenium/remote/RemoteLogsTest.java | 2 +- 10 files changed, 31 insertions(+), 21 deletions(-) diff --git a/java/src/org/openqa/selenium/chromium/AddHasCasting.java b/java/src/org/openqa/selenium/chromium/AddHasCasting.java index 28afef620a5cf..7cb789ecf9b8a 100644 --- a/java/src/org/openqa/selenium/chromium/AddHasCasting.java +++ b/java/src/org/openqa/selenium/chromium/AddHasCasting.java @@ -81,7 +81,7 @@ public void startTabMirroring(String deviceName) { @Override public String getCastIssueMessage() { - return executeMethod.executeRequired(GET_CAST_ISSUE_MESSAGE, null).toString(); + return executeMethod.execute(GET_CAST_ISSUE_MESSAGE).toString(); } @Override diff --git a/java/src/org/openqa/selenium/chromium/AddHasCdp.java b/java/src/org/openqa/selenium/chromium/AddHasCdp.java index e35998388ffb1..08e8225a1ec1b 100644 --- a/java/src/org/openqa/selenium/chromium/AddHasCdp.java +++ b/java/src/org/openqa/selenium/chromium/AddHasCdp.java @@ -51,8 +51,7 @@ public HasCdp getImplementation(Capabilities capabilities, ExecuteMethod execute Require.nonNull("Command name", commandName); Require.nonNull("Parameters", parameters); - return executeMethod.executeRequired( - EXECUTE_CDP, Map.of("cmd", commandName, "params", parameters)); + return executeMethod.executeAs(EXECUTE_CDP, Map.of("cmd", commandName, "params", parameters)); }; } } diff --git a/java/src/org/openqa/selenium/chromium/AddHasNetworkConditions.java b/java/src/org/openqa/selenium/chromium/AddHasNetworkConditions.java index 3a045c37d8170..d7774d040297e 100644 --- a/java/src/org/openqa/selenium/chromium/AddHasNetworkConditions.java +++ b/java/src/org/openqa/selenium/chromium/AddHasNetworkConditions.java @@ -75,8 +75,7 @@ public HasNetworkConditions getImplementation( return new HasNetworkConditions() { @Override public ChromiumNetworkConditions getNetworkConditions() { - @SuppressWarnings("unchecked") - Map result = executeMethod.executeRequired(GET_NETWORK_CONDITIONS, null); + Map result = executeMethod.execute(GET_NETWORK_CONDITIONS); return new ChromiumNetworkConditions() .setOffline((Boolean) result.getOrDefault(OFFLINE, false)) .setLatency(Duration.ofMillis((Long) result.getOrDefault(LATENCY, 0))) diff --git a/java/src/org/openqa/selenium/firefox/AddHasContext.java b/java/src/org/openqa/selenium/firefox/AddHasContext.java index 4369ab6cda1d2..bddb8de9e0058 100644 --- a/java/src/org/openqa/selenium/firefox/AddHasContext.java +++ b/java/src/org/openqa/selenium/firefox/AddHasContext.java @@ -69,7 +69,7 @@ public void setContext(FirefoxCommandContext context) { @Override public FirefoxCommandContext getContext() { - String context = executeMethod.executeRequired(GET_CONTEXT, null); + String context = executeMethod.execute(GET_CONTEXT); return FirefoxCommandContext.fromString(context); } }; diff --git a/java/src/org/openqa/selenium/firefox/AddHasExtensions.java b/java/src/org/openqa/selenium/firefox/AddHasExtensions.java index 671199bd2e494..32726c6375bb6 100644 --- a/java/src/org/openqa/selenium/firefox/AddHasExtensions.java +++ b/java/src/org/openqa/selenium/firefox/AddHasExtensions.java @@ -95,7 +95,7 @@ public String installExtension(Path path, Boolean temporary) { throw new InvalidArgumentException(path + " is an invalid path", e); } - return executeMethod.executeRequired( + return executeMethod.executeAs( INSTALL_EXTENSION, Map.of("addon", encoded, "temporary", temporary)); } diff --git a/java/src/org/openqa/selenium/remote/ExecuteMethod.java b/java/src/org/openqa/selenium/remote/ExecuteMethod.java index c5fdab47148a1..015984aaab521 100644 --- a/java/src/org/openqa/selenium/remote/ExecuteMethod.java +++ b/java/src/org/openqa/selenium/remote/ExecuteMethod.java @@ -40,16 +40,30 @@ public interface ExecuteMethod { */ @Nullable Object execute(String commandName, @Nullable Map parameters); + /** + * Execute the given command and return the default value if the command return null. + * @return non-nullable value of type T. + */ + @SuppressWarnings("unchecked") default T execute(String commandName, @Nullable Map parameters, T defaultValue) { - return requireNonNullElse(executeOptional(commandName, parameters), defaultValue); + return (T) requireNonNullElse(execute(commandName, parameters), defaultValue); } + /** + * Execute the given command and cast the returned value to T. + * @return non-nullable value of type T. + */ @SuppressWarnings("unchecked") - default @Nullable T executeOptional(String commandName, @Nullable Map parameters) { - return (T) execute(commandName, parameters); + default T executeAs(String commandName, @Nullable Map parameters) { + return (T) requireNonNull(execute(commandName, parameters)); } - default T executeRequired(String commandName, @Nullable Map parameters) { - return requireNonNull(executeOptional(commandName, parameters)); + /** + * Execute the given command without parameters and cast the returned value to T. + * @return non-nullable value of type T. + */ + @SuppressWarnings("unchecked") + default T execute(String commandName) { + return (T) requireNonNull(execute(commandName, null)); } } diff --git a/java/src/org/openqa/selenium/remote/FedCmDialogImpl.java b/java/src/org/openqa/selenium/remote/FedCmDialogImpl.java index 7e0488153380e..19d0ee4295986 100644 --- a/java/src/org/openqa/selenium/remote/FedCmDialogImpl.java +++ b/java/src/org/openqa/selenium/remote/FedCmDialogImpl.java @@ -46,7 +46,7 @@ public void selectAccount(int index) { @Nullable @Override public String getDialogType() { - return executeMethod.executeOptional(DriverCommand.GET_FEDCM_DIALOG_TYPE, null); + return (String) executeMethod.execute(DriverCommand.GET_FEDCM_DIALOG_TYPE, null); } @Override @@ -58,21 +58,20 @@ public void clickDialog() { @Nullable @Override public String getTitle() { - Map result = executeMethod.executeRequired(DriverCommand.GET_FEDCM_TITLE, null); + Map result = executeMethod.execute(DriverCommand.GET_FEDCM_TITLE); return result.get("title"); } @Nullable @Override public String getSubtitle() { - Map result = executeMethod.executeRequired(DriverCommand.GET_FEDCM_TITLE, null); + Map result = executeMethod.execute(DriverCommand.GET_FEDCM_TITLE); return result.get("subtitle"); } @Override public List getAccounts() { - List> accounts = - executeMethod.executeRequired(DriverCommand.GET_ACCOUNTS, null); + List> accounts = executeMethod.execute(DriverCommand.GET_ACCOUNTS); return accounts.stream() .map(map -> new FederatedCredentialManagementAccount(map)) diff --git a/java/src/org/openqa/selenium/remote/RemoteLogs.java b/java/src/org/openqa/selenium/remote/RemoteLogs.java index 7d0f230d6b324..267558e892e14 100644 --- a/java/src/org/openqa/selenium/remote/RemoteLogs.java +++ b/java/src/org/openqa/selenium/remote/RemoteLogs.java @@ -149,8 +149,7 @@ private Set getAvailableLocalLogs() { @Override public Set getAvailableLogTypes() { - List rawList = - executeMethod.executeRequired(DriverCommand.GET_AVAILABLE_LOG_TYPES, null); + List rawList = executeMethod.execute(DriverCommand.GET_AVAILABLE_LOG_TYPES); Set builder = new LinkedHashSet<>(); builder.addAll(rawList); builder.addAll(getAvailableLocalLogs()); diff --git a/java/src/org/openqa/selenium/safari/AddHasPermissions.java b/java/src/org/openqa/selenium/safari/AddHasPermissions.java index 6aa4572387a89..f9edf3496eeea 100644 --- a/java/src/org/openqa/selenium/safari/AddHasPermissions.java +++ b/java/src/org/openqa/selenium/safari/AddHasPermissions.java @@ -67,7 +67,7 @@ public void setPermissions(String permission, boolean value) { @Override public Map getPermissions() { - Map resultMap = executeMethod.executeRequired(GET_PERMISSIONS, null); + Map resultMap = executeMethod.execute(GET_PERMISSIONS); Map permissionMap = new HashMap<>(); for (Map.Entry entry : resultMap.entrySet()) { diff --git a/java/test/org/openqa/selenium/remote/RemoteLogsTest.java b/java/test/org/openqa/selenium/remote/RemoteLogsTest.java index e9044760a0e93..39631bbabd5c0 100644 --- a/java/test/org/openqa/selenium/remote/RemoteLogsTest.java +++ b/java/test/org/openqa/selenium/remote/RemoteLogsTest.java @@ -133,7 +133,7 @@ void throwsOnBogusRemoteLogsResponse() { @Test void canGetAvailableLogTypes() { List remoteAvailableLogTypes = List.of(LogType.PROFILER, LogType.SERVER); - when(executeMethod.executeRequired(DriverCommand.GET_AVAILABLE_LOG_TYPES, null)) + when(executeMethod.execute(DriverCommand.GET_AVAILABLE_LOG_TYPES)) .thenReturn(remoteAvailableLogTypes); Set localAvailableLogTypes = Set.of(LogType.PROFILER, LogType.CLIENT); From 15643922a71084738213c15b79632866759db432 Mon Sep 17 00:00:00 2001 From: Andrei Solntsev Date: Fri, 6 Mar 2026 09:52:29 +0200 Subject: [PATCH 3/3] rename the additional helper methods `ExecuteMethod` --- java/src/org/openqa/selenium/remote/ExecuteMethod.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/java/src/org/openqa/selenium/remote/ExecuteMethod.java b/java/src/org/openqa/selenium/remote/ExecuteMethod.java index 015984aaab521..b65238c0d4262 100644 --- a/java/src/org/openqa/selenium/remote/ExecuteMethod.java +++ b/java/src/org/openqa/selenium/remote/ExecuteMethod.java @@ -42,6 +42,7 @@ public interface ExecuteMethod { /** * Execute the given command and return the default value if the command return null. + * * @return non-nullable value of type T. */ @SuppressWarnings("unchecked") @@ -51,6 +52,7 @@ default T execute(String commandName, @Nullable Map parameters, T /** * Execute the given command and cast the returned value to T. + * * @return non-nullable value of type T. */ @SuppressWarnings("unchecked") @@ -60,6 +62,7 @@ default T executeAs(String commandName, @Nullable Map parameters) /** * Execute the given command without parameters and cast the returned value to T. + * * @return non-nullable value of type T. */ @SuppressWarnings("unchecked")