diff --git a/java/src/org/openqa/selenium/chrome/ChromeDriverService.java b/java/src/org/openqa/selenium/chrome/ChromeDriverService.java index 921ad928ea5fc..fa2bee0167044 100644 --- a/java/src/org/openqa/selenium/chrome/ChromeDriverService.java +++ b/java/src/org/openqa/selenium/chrome/ChromeDriverService.java @@ -18,7 +18,6 @@ package org.openqa.selenium.chrome; import static java.util.Collections.unmodifiableList; -import static java.util.Collections.unmodifiableMap; import static org.openqa.selenium.remote.Browser.CHROME; import com.google.auto.service.AutoService; @@ -27,7 +26,6 @@ import java.io.OutputStream; import java.time.Duration; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; @@ -110,12 +108,7 @@ public ChromeDriverService( @Nullable List args, @Nullable Map environment) throws IOException { - super( - executable, - port, - timeout, - unmodifiableList(new ArrayList<>(args)), - unmodifiableMap(new HashMap<>(environment))); + super(executable, port, timeout, args, environment); } public String getDriverName() { diff --git a/java/src/org/openqa/selenium/edge/EdgeDriverInfo.java b/java/src/org/openqa/selenium/edge/EdgeDriverInfo.java index 2435c1fae3dbb..23070cee03f44 100644 --- a/java/src/org/openqa/selenium/edge/EdgeDriverInfo.java +++ b/java/src/org/openqa/selenium/edge/EdgeDriverInfo.java @@ -21,7 +21,6 @@ import com.google.auto.service.AutoService; import java.util.Optional; -import java.util.logging.Logger; import org.openqa.selenium.Capabilities; import org.openqa.selenium.ImmutableCapabilities; import org.openqa.selenium.SessionNotCreatedException; @@ -34,8 +33,6 @@ @AutoService(WebDriverInfo.class) public class EdgeDriverInfo extends ChromiumDriverInfo { - private static final Logger LOG = Logger.getLogger(EdgeDriverInfo.class.getName()); - @Override public String getDisplayName() { return "Edge"; diff --git a/java/src/org/openqa/selenium/edge/EdgeDriverService.java b/java/src/org/openqa/selenium/edge/EdgeDriverService.java index 6f1ae54147861..2ba4459ebb519 100644 --- a/java/src/org/openqa/selenium/edge/EdgeDriverService.java +++ b/java/src/org/openqa/selenium/edge/EdgeDriverService.java @@ -104,7 +104,7 @@ public EdgeDriverService( @Nullable List args, @Nullable Map environment) throws IOException { - super(executable, port, timeout, List.copyOf(args), Map.copyOf(environment)); + super(executable, port, timeout, args, environment); } public String getDriverName() { diff --git a/java/src/org/openqa/selenium/edge/package-info.java b/java/src/org/openqa/selenium/edge/package-info.java new file mode 100644 index 0000000000000..0ecb0d05d4dd3 --- /dev/null +++ b/java/src/org/openqa/selenium/edge/package-info.java @@ -0,0 +1,50 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +/** + * Mechanisms to configure and run selenium via the command line. There are two key classes {@link + * org.openqa.selenium.cli.CliCommand} and {@link org.openqa.selenium.grid.config.HasRoles}. + * Ultimately, these are used to build a {@link org.openqa.selenium.grid.config.Config} instance, + * for which there are strongly-typed role-specific classes that use a {@code Config}, such as + * {@link org.openqa.selenium.grid.node.docker.DockerOptions}. + * + *

Assuming your {@code CliCommand} extends {@link org.openqa.selenium.grid.TemplateGridCommand}, + * the process for building the set of flags to use is: + * + *

    + *
  1. The default flags are added (these are {@link org.openqa.selenium.grid.server.HelpFlags} + * and {@link org.openqa.selenium.grid.config.ConfigFlags} + *
  2. {@link java.util.ServiceLoader} is used to find all implementations of {@link + * org.openqa.selenium.grid.config.HasRoles} where {@link + * org.openqa.selenium.grid.config.HasRoles#getRoles()} is contained within {@link + * org.openqa.selenium.cli.CliCommand#getConfigurableRoles()}. + *
  3. Finally all flags returned by {@link + * org.openqa.selenium.grid.TemplateGridCommand#getFlagObjects()} are added. + *
+ * + *

The flags are then used by JCommander to parse the command arguments. Once that's done, the + * raw flags are converted to a {@link org.openqa.selenium.grid.config.Config} by combining all of + * the flag objects with system properties and environment variables. This implies that each flag + * object has annotated each field with {@link org.openqa.selenium.grid.config.ConfigValue}. + * + *

Ultimately, this means that flag objects have all (most?) fields annotated with JCommander's + * {@link com.beust.jcommander.Parameter} annotation as well as {@code ConfigValue}. + */ +@NullMarked +package org.openqa.selenium.edge; + +import org.jspecify.annotations.NullMarked; diff --git a/java/src/org/openqa/selenium/federatedcredentialmanagement/FederatedCredentialManagementAccount.java b/java/src/org/openqa/selenium/federatedcredentialmanagement/FederatedCredentialManagementAccount.java index ff9248a2eb7f5..d14f0c855ee35 100644 --- a/java/src/org/openqa/selenium/federatedcredentialmanagement/FederatedCredentialManagementAccount.java +++ b/java/src/org/openqa/selenium/federatedcredentialmanagement/FederatedCredentialManagementAccount.java @@ -18,7 +18,6 @@ package org.openqa.selenium.federatedcredentialmanagement; import java.util.Map; -import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; /** @@ -29,7 +28,6 @@ * @see * https://w3c-fedid.github.io/FedCM/#webdriver-accountlist */ -@NullMarked public class FederatedCredentialManagementAccount { private final @Nullable String accountId; private final @Nullable String email; @@ -57,16 +55,16 @@ public class FederatedCredentialManagementAccount { public static final String LOGIN_STATE_SIGNIN = "SignIn"; public static final String LOGIN_STATE_SIGNUP = "SignUp"; - public FederatedCredentialManagementAccount(Map dict) { - accountId = (String) dict.getOrDefault("accountId", null); - email = (String) dict.getOrDefault("email", null); - name = (String) dict.getOrDefault("name", null); - givenName = (String) dict.getOrDefault("givenName", null); - pictureUrl = (String) dict.getOrDefault("pictureUrl", null); - idpConfigUrl = (String) dict.getOrDefault("idpConfigUrl", null); - loginState = (String) dict.getOrDefault("loginState", null); - termsOfServiceUrl = (String) dict.getOrDefault("termsOfServiceUrl", null); - privacyPolicyUrl = (String) dict.getOrDefault("privacyPolicyUrl", null); + public FederatedCredentialManagementAccount(Map dict) { + accountId = dict.getOrDefault("accountId", null); + email = dict.getOrDefault("email", null); + name = dict.getOrDefault("name", null); + givenName = dict.getOrDefault("givenName", null); + pictureUrl = dict.getOrDefault("pictureUrl", null); + idpConfigUrl = dict.getOrDefault("idpConfigUrl", null); + loginState = dict.getOrDefault("loginState", null); + termsOfServiceUrl = dict.getOrDefault("termsOfServiceUrl", null); + privacyPolicyUrl = dict.getOrDefault("privacyPolicyUrl", null); } public @Nullable String getAccountid() { diff --git a/java/src/org/openqa/selenium/federatedcredentialmanagement/FederatedCredentialManagementDialog.java b/java/src/org/openqa/selenium/federatedcredentialmanagement/FederatedCredentialManagementDialog.java index 3c12d92e0c6c0..4882141cc0177 100644 --- a/java/src/org/openqa/selenium/federatedcredentialmanagement/FederatedCredentialManagementDialog.java +++ b/java/src/org/openqa/selenium/federatedcredentialmanagement/FederatedCredentialManagementDialog.java @@ -18,7 +18,6 @@ package org.openqa.selenium.federatedcredentialmanagement; import java.util.List; -import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; /** @@ -26,7 +25,6 @@ * * @see https://w3c-fedid.github.io/FedCM/ */ -@NullMarked public interface FederatedCredentialManagementDialog { String DIALOG_TYPE_ACCOUNT_LIST = "AccountChooser"; diff --git a/java/src/org/openqa/selenium/federatedcredentialmanagement/HasFederatedCredentialManagement.java b/java/src/org/openqa/selenium/federatedcredentialmanagement/HasFederatedCredentialManagement.java index 501a52d6e05e7..2026f66617758 100644 --- a/java/src/org/openqa/selenium/federatedcredentialmanagement/HasFederatedCredentialManagement.java +++ b/java/src/org/openqa/selenium/federatedcredentialmanagement/HasFederatedCredentialManagement.java @@ -17,13 +17,11 @@ package org.openqa.selenium.federatedcredentialmanagement; -import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.openqa.selenium.Beta; /** Used by classes to indicate that they can interact with FedCM dialogs. */ @Beta -@NullMarked public interface HasFederatedCredentialManagement { /** * Disables the promise rejection delay. diff --git a/java/src/org/openqa/selenium/federatedcredentialmanagement/package-info.java b/java/src/org/openqa/selenium/federatedcredentialmanagement/package-info.java new file mode 100644 index 0000000000000..359e361a886f5 --- /dev/null +++ b/java/src/org/openqa/selenium/federatedcredentialmanagement/package-info.java @@ -0,0 +1,50 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +/** + * Mechanisms to configure and run selenium via the command line. There are two key classes {@link + * org.openqa.selenium.cli.CliCommand} and {@link org.openqa.selenium.grid.config.HasRoles}. + * Ultimately, these are used to build a {@link org.openqa.selenium.grid.config.Config} instance, + * for which there are strongly-typed role-specific classes that use a {@code Config}, such as + * {@link org.openqa.selenium.grid.node.docker.DockerOptions}. + * + *

Assuming your {@code CliCommand} extends {@link org.openqa.selenium.grid.TemplateGridCommand}, + * the process for building the set of flags to use is: + * + *

    + *
  1. The default flags are added (these are {@link org.openqa.selenium.grid.server.HelpFlags} + * and {@link org.openqa.selenium.grid.config.ConfigFlags} + *
  2. {@link java.util.ServiceLoader} is used to find all implementations of {@link + * org.openqa.selenium.grid.config.HasRoles} where {@link + * org.openqa.selenium.grid.config.HasRoles#getRoles()} is contained within {@link + * org.openqa.selenium.cli.CliCommand#getConfigurableRoles()}. + *
  3. Finally all flags returned by {@link + * org.openqa.selenium.grid.TemplateGridCommand#getFlagObjects()} are added. + *
+ * + *

The flags are then used by JCommander to parse the command arguments. Once that's done, the + * raw flags are converted to a {@link org.openqa.selenium.grid.config.Config} by combining all of + * the flag objects with system properties and environment variables. This implies that each flag + * object has annotated each field with {@link org.openqa.selenium.grid.config.ConfigValue}. + * + *

Ultimately, this means that flag objects have all (most?) fields annotated with JCommander's + * {@link com.beust.jcommander.Parameter} annotation as well as {@code ConfigValue}. + */ +@NullMarked +package org.openqa.selenium.federatedcredentialmanagement; + +import org.jspecify.annotations.NullMarked; diff --git a/java/src/org/openqa/selenium/firefox/AddHasContext.java b/java/src/org/openqa/selenium/firefox/AddHasContext.java index 787892239fc20..4369ab6cda1d2 100644 --- a/java/src/org/openqa/selenium/firefox/AddHasContext.java +++ b/java/src/org/openqa/selenium/firefox/AddHasContext.java @@ -69,8 +69,7 @@ public void setContext(FirefoxCommandContext context) { @Override public FirefoxCommandContext getContext() { - String context = (String) executeMethod.execute(GET_CONTEXT, null); - + String context = executeMethod.executeRequired(GET_CONTEXT, null); 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 d1b21880bbdb6..671199bd2e494 100644 --- a/java/src/org/openqa/selenium/firefox/AddHasExtensions.java +++ b/java/src/org/openqa/selenium/firefox/AddHasExtensions.java @@ -95,9 +95,8 @@ public String installExtension(Path path, Boolean temporary) { throw new InvalidArgumentException(path + " is an invalid path", e); } - return (String) - executeMethod.execute( - INSTALL_EXTENSION, Map.of("addon", encoded, "temporary", temporary)); + return executeMethod.executeRequired( + INSTALL_EXTENSION, Map.of("addon", encoded, "temporary", temporary)); } private byte[] zipDirectory(Path path) throws IOException { @@ -105,7 +104,7 @@ private byte[] zipDirectory(Path path) throws IOException { try (ZipOutputStream zos = new ZipOutputStream(baos)) { Files.walkFileTree( path, - new SimpleFileVisitor() { + new SimpleFileVisitor<>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { diff --git a/java/src/org/openqa/selenium/firefox/FileExtension.java b/java/src/org/openqa/selenium/firefox/FileExtension.java index 7f7253c22df85..db9e755f00527 100644 --- a/java/src/org/openqa/selenium/firefox/FileExtension.java +++ b/java/src/org/openqa/selenium/firefox/FileExtension.java @@ -18,6 +18,7 @@ package org.openqa.selenium.firefox; import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.Objects.requireNonNull; import static org.openqa.selenium.json.Json.MAP_TYPE; import java.io.BufferedInputStream; @@ -131,7 +132,7 @@ private String readIdFromManifestJson(File root) { JsonInput json = new Json().newInput(reader)) { String addOnId = null; - Map manifestObject = json.read(MAP_TYPE); + Map manifestObject = json.readNonNull(MAP_TYPE); if (manifestObject.get("applications") instanceof Map) { Map applicationObj = (Map) manifestObject.get("applications"); if (applicationObj.get("gecko") instanceof Map) { @@ -143,10 +144,14 @@ private String readIdFromManifestJson(File root) { } if (addOnId == null || addOnId.isEmpty()) { - addOnId = - ((String) manifestObject.get("name")).replaceAll(" ", "") - + "@" - + manifestObject.get("version"); + String name = + (String) + requireNonNull( + manifestObject.get("name"), + () -> + "Manifest should contain either 'applications' or 'name', but received: " + + manifestObject); + addOnId = name.replaceAll(" ", "") + "@" + manifestObject.get("version"); } return addOnId; @@ -158,9 +163,8 @@ private String readIdFromManifestJson(File root) { } private String readIdFromInstallRdf(File root) { + File installRdf = new File(root, "install.rdf"); try { - File installRdf = new File(root, "install.rdf"); - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); factory.setNamespaceAware(true); @@ -214,7 +218,7 @@ public Iterator getPrefixes(String uri) { } return id; } catch (Exception e) { - throw new WebDriverException(e); + throw new WebDriverException("Failed to read id from " + installRdf.getAbsolutePath(), e); } } } diff --git a/java/src/org/openqa/selenium/firefox/FirefoxCommandContext.java b/java/src/org/openqa/selenium/firefox/FirefoxCommandContext.java index 7c683788938a7..07e5208d0c978 100644 --- a/java/src/org/openqa/selenium/firefox/FirefoxCommandContext.java +++ b/java/src/org/openqa/selenium/firefox/FirefoxCommandContext.java @@ -32,17 +32,15 @@ public enum FirefoxCommandContext { @Override public String toString() { - return String.valueOf(text); + return text; } public static FirefoxCommandContext fromString(String text) { - if (text != null) { - for (FirefoxCommandContext b : FirefoxCommandContext.values()) { - if (text.equalsIgnoreCase(b.text)) { - return b; - } + for (FirefoxCommandContext b : FirefoxCommandContext.values()) { + if (text.equalsIgnoreCase(b.text)) { + return b; } } - return null; + throw new IllegalArgumentException("Unknown Firefox context: " + text); } } diff --git a/java/src/org/openqa/selenium/firefox/FirefoxDriver.java b/java/src/org/openqa/selenium/firefox/FirefoxDriver.java index a84f836f374b8..2546a8556e971 100644 --- a/java/src/org/openqa/selenium/firefox/FirefoxDriver.java +++ b/java/src/org/openqa/selenium/firefox/FirefoxDriver.java @@ -27,7 +27,6 @@ import java.util.logging.Logger; import java.util.stream.Collectors; import java.util.stream.Stream; -import org.jspecify.annotations.NonNull; import org.openqa.selenium.Beta; import org.openqa.selenium.Capabilities; import org.openqa.selenium.ImmutableCapabilities; @@ -169,6 +168,8 @@ public static RemoteWebDriverBuilder builder() { /** Check capabilities and proxy if it is set */ private static Capabilities checkCapabilitiesAndProxy(Capabilities capabilities) { + // TODO I think we can remove this null check + //noinspection ConstantValue if (capabilities == null) { return new ImmutableCapabilities(); } @@ -184,7 +185,6 @@ private static Capabilities checkCapabilitiesAndProxy(Capabilities capabilities) return caps; } - @NonNull @Override public Capabilities getCapabilities() { return capabilities; diff --git a/java/src/org/openqa/selenium/firefox/FirefoxDriverLogLevel.java b/java/src/org/openqa/selenium/firefox/FirefoxDriverLogLevel.java index b46e762214b84..e7366fc74f9d2 100644 --- a/java/src/org/openqa/selenium/firefox/FirefoxDriverLogLevel.java +++ b/java/src/org/openqa/selenium/firefox/FirefoxDriverLogLevel.java @@ -21,6 +21,7 @@ import java.util.Locale; import java.util.Map; import java.util.logging.Level; +import org.jspecify.annotations.Nullable; /** * Log levels defined by GeckoDriver @@ -51,7 +52,8 @@ public String toString() { return super.toString().toLowerCase(Locale.ENGLISH); } - public static FirefoxDriverLogLevel fromString(String text) { + @Nullable + public static FirefoxDriverLogLevel fromString(@Nullable String text) { if (text != null) { for (FirefoxDriverLogLevel b : FirefoxDriverLogLevel.values()) { if (text.equalsIgnoreCase(b.toString())) { @@ -70,6 +72,7 @@ Map toJson() { return Collections.singletonMap("level", toString()); } + @Nullable static FirefoxDriverLogLevel fromJson(Map json) { return fromString(json.get("level")); } diff --git a/java/src/org/openqa/selenium/firefox/FirefoxDriverService.java b/java/src/org/openqa/selenium/firefox/FirefoxDriverService.java index 65e9ed83be01d..69f7626fe8244 100644 --- a/java/src/org/openqa/selenium/firefox/FirefoxDriverService.java +++ b/java/src/org/openqa/selenium/firefox/FirefoxDriverService.java @@ -35,7 +35,7 @@ public abstract class FirefoxDriverService extends DriverService { * @param environment The environment for the launched server. * @throws IOException If an I/O error occurs. */ - public FirefoxDriverService( + protected FirefoxDriverService( @Nullable File executable, int port, @Nullable Duration timeout, diff --git a/java/src/org/openqa/selenium/firefox/FirefoxOptions.java b/java/src/org/openqa/selenium/firefox/FirefoxOptions.java index 6950661874cc6..641d6253fd34a 100644 --- a/java/src/org/openqa/selenium/firefox/FirefoxOptions.java +++ b/java/src/org/openqa/selenium/firefox/FirefoxOptions.java @@ -227,11 +227,11 @@ public FirefoxOptions addPreference(String key, Object value) { return setFirefoxOption(Keys.PREFS, Collections.unmodifiableMap(newPrefs)); } - Map prefs() { + @Nullable Map prefs() { return getOption(Keys.PREFS); } - String profile() { + @Nullable String profile() { return getOption(Keys.PROFILE); } @@ -293,6 +293,7 @@ protected Set getExtraCapabilityNames() { return Collections.unmodifiableSet(names); } + @Nullable @Override protected Object getExtraCapability(String capabilityName) { Require.nonNull("Capability name", capabilityName); @@ -440,6 +441,7 @@ private enum Keys { @Override public void amend(Map sourceOptions, Map toAmend) {} + @Nullable @Override public Object mirror(Map first, Map second) { return null; @@ -465,6 +467,7 @@ public void amend(Map sourceOptions, Map toAmend toAmend.put(key(), Collections.unmodifiableList(new ArrayList<>(newArgs))); } + @Nullable @Override public Object mirror(Map first, Map second) { Object rawFirst = first.getOrDefault(key(), new ArrayList<>()); @@ -494,6 +497,7 @@ public void amend(Map sourceOptions, Map toAmend } } + @Nullable @Override public Object mirror(Map first, Map second) { Object value = second.get(key()); @@ -527,6 +531,7 @@ public void amend(Map sourceOptions, Map toAmend toAmend.put(key(), Collections.unmodifiableMap(collected)); } + @Nullable @Override public Object mirror(Map first, Map second) { Object rawFirst = first.getOrDefault(key(), new TreeMap<>()); @@ -559,6 +564,7 @@ public void amend(Map sourceOptions, Map toAmend toAmend.put(key(), o); } + @Nullable @Override public Object mirror(Map first, Map second) { Object value = second.get(key()); @@ -590,6 +596,7 @@ public void amend(Map sourceOptions, Map toAmend toAmend.put(key(), Collections.unmodifiableMap(collected)); } + @Nullable @Override public Object mirror(Map first, Map second) { Object rawFirst = first.getOrDefault(key(), new TreeMap<>()); @@ -627,6 +634,7 @@ public void amend(Map sourceOptions, Map toAmend toAmend.put(key(), o); } + @Nullable @Override public Object mirror(Map first, Map second) { Object value = second.get(key()); @@ -657,6 +665,7 @@ public String key() { public abstract void amend(Map sourceOptions, Map toAmend); + @Nullable public abstract Object mirror(Map first, Map second); } } diff --git a/java/src/org/openqa/selenium/firefox/FirefoxProfile.java b/java/src/org/openqa/selenium/firefox/FirefoxProfile.java index f39b5e1fa33c9..023bf8571cf40 100644 --- a/java/src/org/openqa/selenium/firefox/FirefoxProfile.java +++ b/java/src/org/openqa/selenium/firefox/FirefoxProfile.java @@ -26,6 +26,7 @@ import java.nio.charset.Charset; import java.util.HashMap; import java.util.Map; +import org.jspecify.annotations.Nullable; import org.openqa.selenium.WebDriverException; import org.openqa.selenium.io.FileHandler; import org.openqa.selenium.io.TemporaryFilesystem; @@ -38,7 +39,7 @@ public class FirefoxProfile { private static final String ASSUME_UNTRUSTED_ISSUER_PREF = "webdriver_assume_untrusted_issuer"; private final Preferences additionalPrefs; private final Map extensions = new HashMap<>(); - private final File model; + private @Nullable final File model; private boolean loadNoFocusLib; private boolean acceptUntrustedCerts; private boolean untrustedCertIssuer; @@ -54,7 +55,7 @@ public FirefoxProfile() { * * @param profileDir The profile directory to use as a model. */ - public FirefoxProfile(File profileDir) { + public FirefoxProfile(@Nullable File profileDir) { additionalPrefs = new Preferences(); model = profileDir; verifyModel(model); @@ -124,7 +125,7 @@ public boolean getBooleanPreference(String key, boolean defaultValue) { return defaultValue; } - private void verifyModel(File model) { + private void verifyModel(@Nullable File model) { if (model == null) { return; } @@ -292,8 +293,10 @@ public FirefoxProfile setAssumeUntrustedCertificateIssuer(boolean untrustedIssue return this; } - public void clean(File profileDir) { - TemporaryFilesystem.getDefaultTmpFS().deleteTempDir(profileDir); + public void clean(@Nullable File profileDir) { + if (profileDir != null) { + TemporaryFilesystem.getDefaultTmpFS().deleteTempDir(profileDir); + } } String toJson() throws IOException { @@ -336,7 +339,7 @@ public File layoutOnDisk() { } } - protected void copyModel(File sourceDir, File profileDir) throws IOException { + protected void copyModel(@Nullable File sourceDir, File profileDir) throws IOException { if (sourceDir == null || !sourceDir.exists()) { return; } diff --git a/java/src/org/openqa/selenium/firefox/GeckoDriverInfo.java b/java/src/org/openqa/selenium/firefox/GeckoDriverInfo.java index dfb371978a18b..da4503b97ea01 100644 --- a/java/src/org/openqa/selenium/firefox/GeckoDriverInfo.java +++ b/java/src/org/openqa/selenium/firefox/GeckoDriverInfo.java @@ -22,7 +22,6 @@ import com.google.auto.service.AutoService; import java.util.Optional; -import java.util.logging.Logger; import org.openqa.selenium.Capabilities; import org.openqa.selenium.ImmutableCapabilities; import org.openqa.selenium.SessionNotCreatedException; @@ -33,8 +32,6 @@ @AutoService(WebDriverInfo.class) public class GeckoDriverInfo implements WebDriverInfo { - private static final Logger LOG = Logger.getLogger(GeckoDriverInfo.class.getName()); - @Override public String getDisplayName() { return "Firefox"; diff --git a/java/src/org/openqa/selenium/firefox/Preferences.java b/java/src/org/openqa/selenium/firefox/Preferences.java index 51f272b323962..59b182573804e 100644 --- a/java/src/org/openqa/selenium/firefox/Preferences.java +++ b/java/src/org/openqa/selenium/firefox/Preferences.java @@ -32,6 +32,7 @@ import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.jspecify.annotations.Nullable; import org.openqa.selenium.WebDriverException; import org.openqa.selenium.json.Json; @@ -183,7 +184,7 @@ private Object preferenceAsValue(String toConvert) { } } - Object getPreference(String key) { + @Nullable Object getPreference(String key) { return allPrefs.get(key); } diff --git a/java/src/org/openqa/selenium/firefox/ProfilesIni.java b/java/src/org/openqa/selenium/firefox/ProfilesIni.java index 8e22caa5107e1..535e12921ea20 100644 --- a/java/src/org/openqa/selenium/firefox/ProfilesIni.java +++ b/java/src/org/openqa/selenium/firefox/ProfilesIni.java @@ -28,6 +28,7 @@ import java.text.MessageFormat; import java.util.HashMap; import java.util.Map; +import org.jspecify.annotations.Nullable; import org.openqa.selenium.Platform; import org.openqa.selenium.WebDriverException; import org.openqa.selenium.io.FileHandler; @@ -41,7 +42,7 @@ public ProfilesIni() { profiles = readProfiles(appData); } - protected Map readProfiles(File appData) { + protected Map readProfiles(@Nullable File appData) { Map toReturn = new HashMap<>(); File profilesIni = new File(appData, "profiles.ini"); @@ -95,13 +96,16 @@ protected Map readProfiles(File appData) { return toReturn; } - protected File newProfile(String name, File appData, String path, boolean isRelative) { + @Nullable + protected File newProfile( + @Nullable String name, @Nullable File appData, @Nullable String path, boolean isRelative) { if (name != null && path != null) { return isRelative ? new File(appData, path) : new File(path); } return null; } + @Nullable public FirefoxProfile getProfile(String profileName) { File profileDir = profiles.get(profileName); if (profileDir == null) { @@ -127,6 +131,7 @@ public FirefoxProfile getProfile(String profileName) { return new FirefoxProfile(tempDir); } + @Nullable protected File locateAppDataDirectory(Platform os) { File appData; if (os.is(WINDOWS)) { diff --git a/java/src/org/openqa/selenium/firefox/package-info.java b/java/src/org/openqa/selenium/firefox/package-info.java new file mode 100644 index 0000000000000..45dd7c8f0a7dc --- /dev/null +++ b/java/src/org/openqa/selenium/firefox/package-info.java @@ -0,0 +1,21 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +@NullMarked +package org.openqa.selenium.firefox; + +import org.jspecify.annotations.NullMarked; diff --git a/java/src/org/openqa/selenium/ie/ElementScrollBehavior.java b/java/src/org/openqa/selenium/ie/ElementScrollBehavior.java index c816170757213..c97cc1de12d20 100644 --- a/java/src/org/openqa/selenium/ie/ElementScrollBehavior.java +++ b/java/src/org/openqa/selenium/ie/ElementScrollBehavior.java @@ -17,6 +17,8 @@ package org.openqa.selenium.ie; +import org.jspecify.annotations.Nullable; + public enum ElementScrollBehavior { TOP(0), BOTTOM(1), @@ -33,6 +35,7 @@ public String toString() { return String.valueOf(value); } + @Nullable public static ElementScrollBehavior fromString(String text) { for (ElementScrollBehavior b : ElementScrollBehavior.values()) { if (text.equalsIgnoreCase(b.toString())) { diff --git a/java/src/org/openqa/selenium/ie/InternetExplorerDriverInfo.java b/java/src/org/openqa/selenium/ie/InternetExplorerDriverInfo.java index 4ac3ae75f856e..7713ba8ff91ff 100644 --- a/java/src/org/openqa/selenium/ie/InternetExplorerDriverInfo.java +++ b/java/src/org/openqa/selenium/ie/InternetExplorerDriverInfo.java @@ -22,7 +22,6 @@ import com.google.auto.service.AutoService; import java.util.Optional; -import java.util.logging.Logger; import org.openqa.selenium.Capabilities; import org.openqa.selenium.ImmutableCapabilities; import org.openqa.selenium.Platform; @@ -34,8 +33,6 @@ @AutoService(WebDriverInfo.class) public class InternetExplorerDriverInfo implements WebDriverInfo { - private static final Logger LOG = Logger.getLogger(InternetExplorerDriverInfo.class.getName()); - @Override public String getDisplayName() { return "Internet Explorer"; diff --git a/java/src/org/openqa/selenium/ie/InternetExplorerDriverService.java b/java/src/org/openqa/selenium/ie/InternetExplorerDriverService.java index 3cb43892c6e85..f02cfa5dd43aa 100644 --- a/java/src/org/openqa/selenium/ie/InternetExplorerDriverService.java +++ b/java/src/org/openqa/selenium/ie/InternetExplorerDriverService.java @@ -18,7 +18,6 @@ package org.openqa.selenium.ie; import static java.util.Collections.unmodifiableList; -import static java.util.Collections.unmodifiableMap; import static org.openqa.selenium.remote.Browser.IE; import com.google.auto.service.AutoService; @@ -26,7 +25,6 @@ import java.io.IOException; import java.time.Duration; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; @@ -85,12 +83,7 @@ public InternetExplorerDriverService( @Nullable List args, @Nullable Map environment) throws IOException { - super( - executable, - port, - timeout, - unmodifiableList(new ArrayList<>(args)), - unmodifiableMap(new HashMap<>(environment))); + super(executable, port, timeout, args, environment); } public String getDriverName() { diff --git a/java/src/org/openqa/selenium/ie/InternetExplorerOptions.java b/java/src/org/openqa/selenium/ie/InternetExplorerOptions.java index 84ceef9965d60..6f1c5ccc1c381 100644 --- a/java/src/org/openqa/selenium/ie/InternetExplorerOptions.java +++ b/java/src/org/openqa/selenium/ie/InternetExplorerOptions.java @@ -42,6 +42,7 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import java.util.stream.Stream; +import org.jspecify.annotations.Nullable; import org.openqa.selenium.Capabilities; import org.openqa.selenium.internal.Require; import org.openqa.selenium.remote.AbstractDriverOptions; @@ -276,6 +277,7 @@ protected Set getExtraCapabilityNames() { return Collections.emptySet(); } + @Nullable @Override protected Object getExtraCapability(String capabilityName) { Require.nonNull("Capability name", capabilityName); diff --git a/java/src/org/openqa/selenium/ie/package-info.java b/java/src/org/openqa/selenium/ie/package-info.java new file mode 100644 index 0000000000000..e2d33d56dc710 --- /dev/null +++ b/java/src/org/openqa/selenium/ie/package-info.java @@ -0,0 +1,50 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +/** + * Mechanisms to configure and run selenium via the command line. There are two key classes {@link + * org.openqa.selenium.cli.CliCommand} and {@link org.openqa.selenium.grid.config.HasRoles}. + * Ultimately, these are used to build a {@link org.openqa.selenium.grid.config.Config} instance, + * for which there are strongly-typed role-specific classes that use a {@code Config}, such as + * {@link org.openqa.selenium.grid.node.docker.DockerOptions}. + * + *

Assuming your {@code CliCommand} extends {@link org.openqa.selenium.grid.TemplateGridCommand}, + * the process for building the set of flags to use is: + * + *

    + *
  1. The default flags are added (these are {@link org.openqa.selenium.grid.server.HelpFlags} + * and {@link org.openqa.selenium.grid.config.ConfigFlags} + *
  2. {@link java.util.ServiceLoader} is used to find all implementations of {@link + * org.openqa.selenium.grid.config.HasRoles} where {@link + * org.openqa.selenium.grid.config.HasRoles#getRoles()} is contained within {@link + * org.openqa.selenium.cli.CliCommand#getConfigurableRoles()}. + *
  3. Finally all flags returned by {@link + * org.openqa.selenium.grid.TemplateGridCommand#getFlagObjects()} are added. + *
+ * + *

The flags are then used by JCommander to parse the command arguments. Once that's done, the + * raw flags are converted to a {@link org.openqa.selenium.grid.config.Config} by combining all of + * the flag objects with system properties and environment variables. This implies that each flag + * object has annotated each field with {@link org.openqa.selenium.grid.config.ConfigValue}. + * + *

Ultimately, this means that flag objects have all (most?) fields annotated with JCommander's + * {@link com.beust.jcommander.Parameter} annotation as well as {@code ConfigValue}. + */ +@NullMarked +package org.openqa.selenium.ie; + +import org.jspecify.annotations.NullMarked; diff --git a/java/src/org/openqa/selenium/print/PageMargin.java b/java/src/org/openqa/selenium/print/PageMargin.java index 6fe27e6063e5a..6c18bf3588a7d 100644 --- a/java/src/org/openqa/selenium/print/PageMargin.java +++ b/java/src/org/openqa/selenium/print/PageMargin.java @@ -18,9 +18,7 @@ package org.openqa.selenium.print; import java.util.Map; -import org.jspecify.annotations.NullMarked; -@NullMarked public class PageMargin { private final double top; private final double bottom; diff --git a/java/src/org/openqa/selenium/print/PageSize.java b/java/src/org/openqa/selenium/print/PageSize.java index a673fda24ec45..ff8f3ab2489b1 100644 --- a/java/src/org/openqa/selenium/print/PageSize.java +++ b/java/src/org/openqa/selenium/print/PageSize.java @@ -19,9 +19,8 @@ import java.util.HashMap; import java.util.Map; -import org.jspecify.annotations.NullMarked; +import org.openqa.selenium.internal.Require; -@NullMarked public class PageSize { private final double height; private final double width; @@ -52,9 +51,7 @@ public double getWidth() { } public static PageSize setPageSize(PageSize pageSize) { - if (pageSize == null) { - throw new IllegalArgumentException("Page size cannot be null"); - } + Require.nonNull("Page size", pageSize); return new PageSize(pageSize.getHeight(), pageSize.getWidth()); } diff --git a/java/src/org/openqa/selenium/print/PrintOptions.java b/java/src/org/openqa/selenium/print/PrintOptions.java index 1e1b7a86da3d5..019e051979f89 100644 --- a/java/src/org/openqa/selenium/print/PrintOptions.java +++ b/java/src/org/openqa/selenium/print/PrintOptions.java @@ -20,11 +20,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.openqa.selenium.internal.Require; -@NullMarked public class PrintOptions { public enum Orientation { diff --git a/java/src/org/openqa/selenium/print/package-info.java b/java/src/org/openqa/selenium/print/package-info.java new file mode 100644 index 0000000000000..48ef4b6fe9f52 --- /dev/null +++ b/java/src/org/openqa/selenium/print/package-info.java @@ -0,0 +1,50 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +/** + * Mechanisms to configure and run selenium via the command line. There are two key classes {@link + * org.openqa.selenium.cli.CliCommand} and {@link org.openqa.selenium.grid.config.HasRoles}. + * Ultimately, these are used to build a {@link org.openqa.selenium.grid.config.Config} instance, + * for which there are strongly-typed role-specific classes that use a {@code Config}, such as + * {@link org.openqa.selenium.grid.node.docker.DockerOptions}. + * + *

Assuming your {@code CliCommand} extends {@link org.openqa.selenium.grid.TemplateGridCommand}, + * the process for building the set of flags to use is: + * + *

    + *
  1. The default flags are added (these are {@link org.openqa.selenium.grid.server.HelpFlags} + * and {@link org.openqa.selenium.grid.config.ConfigFlags} + *
  2. {@link java.util.ServiceLoader} is used to find all implementations of {@link + * org.openqa.selenium.grid.config.HasRoles} where {@link + * org.openqa.selenium.grid.config.HasRoles#getRoles()} is contained within {@link + * org.openqa.selenium.cli.CliCommand#getConfigurableRoles()}. + *
  3. Finally all flags returned by {@link + * org.openqa.selenium.grid.TemplateGridCommand#getFlagObjects()} are added. + *
+ * + *

The flags are then used by JCommander to parse the command arguments. Once that's done, the + * raw flags are converted to a {@link org.openqa.selenium.grid.config.Config} by combining all of + * the flag objects with system properties and environment variables. This implies that each flag + * object has annotated each field with {@link org.openqa.selenium.grid.config.ConfigValue}. + * + *

Ultimately, this means that flag objects have all (most?) fields annotated with JCommander's + * {@link com.beust.jcommander.Parameter} annotation as well as {@code ConfigValue}. + */ +@NullMarked +package org.openqa.selenium.print; + +import org.jspecify.annotations.NullMarked; diff --git a/java/src/org/openqa/selenium/remote/service/DriverService.java b/java/src/org/openqa/selenium/remote/service/DriverService.java index 11aacd3677441..3276498d3a315 100644 --- a/java/src/org/openqa/selenium/remote/service/DriverService.java +++ b/java/src/org/openqa/selenium/remote/service/DriverService.java @@ -17,6 +17,7 @@ package org.openqa.selenium.remote.service; +import static java.util.Collections.emptyList; import static java.util.Collections.emptyMap; import static java.util.concurrent.TimeUnit.SECONDS; import static org.openqa.selenium.concurrent.ExecutorServices.shutdownGracefully; @@ -117,8 +118,8 @@ protected DriverService( this.executable = executable.getCanonicalPath(); } this.timeout = timeout; - this.args = args; - this.environment = environment; + this.args = args == null ? emptyList() : List.copyOf(args); + this.environment = environment == null ? emptyMap() : Map.copyOf(environment); this.url = getUrl(port); } diff --git a/java/src/org/openqa/selenium/safari/AddHasPermissions.java b/java/src/org/openqa/selenium/safari/AddHasPermissions.java index 96e49bdb54084..6aa4572387a89 100644 --- a/java/src/org/openqa/selenium/safari/AddHasPermissions.java +++ b/java/src/org/openqa/selenium/safari/AddHasPermissions.java @@ -67,21 +67,15 @@ public void setPermissions(String permission, boolean value) { @Override public Map getPermissions() { - Object resultObject = executeMethod.execute(GET_PERMISSIONS, null); + Map resultMap = executeMethod.executeRequired(GET_PERMISSIONS, null); - if (resultObject instanceof Map) { - Map resultMap = (Map) resultObject; - Map permissionMap = new HashMap<>(); - for (Map.Entry entry : resultMap.entrySet()) { - if (entry.getKey() instanceof String && entry.getValue() instanceof Boolean) { - permissionMap.put((String) entry.getKey(), (Boolean) entry.getValue()); - } + Map permissionMap = new HashMap<>(); + for (Map.Entry entry : resultMap.entrySet()) { + if (entry.getKey() instanceof String && entry.getValue() instanceof Boolean) { + permissionMap.put((String) entry.getKey(), (Boolean) entry.getValue()); } - return permissionMap; - } else { - throw new IllegalStateException( - "Unexpected result type: " + resultObject.getClass().getName()); } + return permissionMap; } }; } diff --git a/java/src/org/openqa/selenium/safari/SafariDriverInfo.java b/java/src/org/openqa/selenium/safari/SafariDriverInfo.java index 3f9967db274d4..bfd408716a3fb 100644 --- a/java/src/org/openqa/selenium/safari/SafariDriverInfo.java +++ b/java/src/org/openqa/selenium/safari/SafariDriverInfo.java @@ -22,7 +22,6 @@ import com.google.auto.service.AutoService; import java.util.Optional; -import java.util.logging.Logger; import org.openqa.selenium.Capabilities; import org.openqa.selenium.ImmutableCapabilities; import org.openqa.selenium.Platform; @@ -35,8 +34,6 @@ @AutoService(WebDriverInfo.class) public class SafariDriverInfo implements WebDriverInfo { - private static final Logger LOG = Logger.getLogger(SafariDriverInfo.class.getName()); - @Override public String getDisplayName() { return "Safari"; diff --git a/java/src/org/openqa/selenium/safari/SafariDriverService.java b/java/src/org/openqa/selenium/safari/SafariDriverService.java index 19c6e29480cc4..52d3d7bc00f96 100644 --- a/java/src/org/openqa/selenium/safari/SafariDriverService.java +++ b/java/src/org/openqa/selenium/safari/SafariDriverService.java @@ -68,7 +68,7 @@ public SafariDriverService( @Nullable List args, @Nullable Map environment) throws IOException { - super(executable, port, timeout, List.copyOf(args), Map.copyOf(environment)); + super(executable, port, timeout, args, environment); } public String getDriverName() { diff --git a/java/src/org/openqa/selenium/safari/SafariOptions.java b/java/src/org/openqa/selenium/safari/SafariOptions.java index b9bda31f9259b..ab390e6b99c25 100644 --- a/java/src/org/openqa/selenium/safari/SafariOptions.java +++ b/java/src/org/openqa/selenium/safari/SafariOptions.java @@ -23,6 +23,7 @@ import java.util.Collections; import java.util.Set; +import org.jspecify.annotations.Nullable; import org.openqa.selenium.Capabilities; import org.openqa.selenium.WebDriverException; import org.openqa.selenium.internal.Require; @@ -105,7 +106,7 @@ public SafariOptions setAutomaticInspection(boolean automaticInspection) { } public boolean getAutomaticProfiling() { - return Boolean.TRUE.equals(is(Option.AUTOMATIC_PROFILING)); + return is(Option.AUTOMATIC_PROFILING); } /** @@ -144,6 +145,7 @@ protected Set getExtraCapabilityNames() { return Collections.emptySet(); } + @Nullable @Override protected Object getExtraCapability(String capabilityName) { return null; diff --git a/java/src/org/openqa/selenium/safari/SafariTechPreviewDriverService.java b/java/src/org/openqa/selenium/safari/SafariTechPreviewDriverService.java index 01f15fc063df2..d6d03d8f172b9 100644 --- a/java/src/org/openqa/selenium/safari/SafariTechPreviewDriverService.java +++ b/java/src/org/openqa/selenium/safari/SafariTechPreviewDriverService.java @@ -29,6 +29,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import org.jspecify.annotations.Nullable; import org.openqa.selenium.Capabilities; import org.openqa.selenium.WebDriverException; import org.openqa.selenium.net.PortProber; @@ -66,7 +67,7 @@ public SafariTechPreviewDriverService( List args, Map environment) throws IOException { - super(executable, port, timeout, List.copyOf(args), Map.copyOf(environment)); + super(executable, port, timeout, args, environment); } public String getDriverName() { @@ -114,7 +115,7 @@ public static class Builder extends DriverService.Builder< SafariTechPreviewDriverService, SafariTechPreviewDriverService.Builder> { - private Boolean diagnose; + @Nullable private Boolean diagnose; @Override public int score(Capabilities capabilities) { diff --git a/java/src/org/openqa/selenium/safari/package-info.java b/java/src/org/openqa/selenium/safari/package-info.java new file mode 100644 index 0000000000000..6684283ee6752 --- /dev/null +++ b/java/src/org/openqa/selenium/safari/package-info.java @@ -0,0 +1,50 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +/** + * Mechanisms to configure and run selenium via the command line. There are two key classes {@link + * org.openqa.selenium.cli.CliCommand} and {@link org.openqa.selenium.grid.config.HasRoles}. + * Ultimately, these are used to build a {@link org.openqa.selenium.grid.config.Config} instance, + * for which there are strongly-typed role-specific classes that use a {@code Config}, such as + * {@link org.openqa.selenium.grid.node.docker.DockerOptions}. + * + *

Assuming your {@code CliCommand} extends {@link org.openqa.selenium.grid.TemplateGridCommand}, + * the process for building the set of flags to use is: + * + *

    + *
  1. The default flags are added (these are {@link org.openqa.selenium.grid.server.HelpFlags} + * and {@link org.openqa.selenium.grid.config.ConfigFlags} + *
  2. {@link java.util.ServiceLoader} is used to find all implementations of {@link + * org.openqa.selenium.grid.config.HasRoles} where {@link + * org.openqa.selenium.grid.config.HasRoles#getRoles()} is contained within {@link + * org.openqa.selenium.cli.CliCommand#getConfigurableRoles()}. + *
  3. Finally all flags returned by {@link + * org.openqa.selenium.grid.TemplateGridCommand#getFlagObjects()} are added. + *
+ * + *

The flags are then used by JCommander to parse the command arguments. Once that's done, the + * raw flags are converted to a {@link org.openqa.selenium.grid.config.Config} by combining all of + * the flag objects with system properties and environment variables. This implies that each flag + * object has annotated each field with {@link org.openqa.selenium.grid.config.ConfigValue}. + * + *

Ultimately, this means that flag objects have all (most?) fields annotated with JCommander's + * {@link com.beust.jcommander.Parameter} annotation as well as {@code ConfigValue}. + */ +@NullMarked +package org.openqa.selenium.safari; + +import org.jspecify.annotations.NullMarked;