org.openqa.selenium.firefox.FirefoxOptions Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of selenium-firefox-driver Show documentation
Show all versions of selenium-firefox-driver Show documentation
Selenium automates browsers. That's it! What you do with that power is entirely up to you.
// 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.
package org.openqa.selenium.firefox;
import static java.util.Objects.requireNonNull;
import static org.openqa.selenium.firefox.FirefoxDriver.BINARY;
import static org.openqa.selenium.firefox.FirefoxDriver.MARIONETTE;
import static org.openqa.selenium.firefox.FirefoxDriver.PROFILE;
import static org.openqa.selenium.remote.CapabilityType.ACCEPT_INSECURE_CERTS;
import static org.openqa.selenium.remote.CapabilityType.PAGE_LOAD_STRATEGY;
import static org.openqa.selenium.remote.CapabilityType.UNHANDLED_PROMPT_BEHAVIOUR;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedMap;
import org.openqa.selenium.Capabilities;
import org.openqa.selenium.MutableCapabilities;
import org.openqa.selenium.PageLoadStrategy;
import org.openqa.selenium.Proxy;
import org.openqa.selenium.UnexpectedAlertBehaviour;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.remote.BrowserType;
import org.openqa.selenium.remote.CapabilityType;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.TreeMap;
import java.util.logging.Level;
/**
* Manage firefox specific settings in a way that geckodriver can understand.
*
* An example of usage:
*
* FirefoxOptions options = new FirefoxOptions()
* .addPreference("browser.startup.page", 1)
* .addPreference("browser.startup.homepage", "https://www.google.co.uk");
* WebDriver driver = new FirefoxDriver(options);
*
*/
public class FirefoxOptions extends MutableCapabilities {
public final static String FIREFOX_OPTIONS = "moz:firefoxOptions";
private List args = new ArrayList<>();
private Map booleanPrefs = new TreeMap<>();
private Map intPrefs = new TreeMap<>();
private Map stringPrefs = new TreeMap<>();
private FirefoxDriverLogLevel logLevel;
private Binary binary;
private boolean legacy;
private FirefoxProfile profile;
public FirefoxOptions() {
// Read system properties and use those if they are set, allowing users to override them later
// should they want to.
String binary = System.getProperty(FirefoxDriver.SystemProperty.BROWSER_BINARY);
if (binary != null) {
setBinary(binary);
}
String profileName = System.getProperty(FirefoxDriver.SystemProperty.BROWSER_PROFILE);
if (profileName != null) {
FirefoxProfile profile = new ProfilesIni().getProfile(profileName);
if (profile == null) {
throw new WebDriverException(String.format(
"Firefox profile '%s' named in system property '%s' not found",
profileName, FirefoxDriver.SystemProperty.BROWSER_PROFILE));
}
setProfile(profile);
}
String forceMarionette = System.getProperty(FirefoxDriver.SystemProperty.DRIVER_USE_MARIONETTE);
if (forceMarionette != null) {
setLegacy(!Boolean.getBoolean(FirefoxDriver.SystemProperty.DRIVER_USE_MARIONETTE));
}
setCapability(CapabilityType.BROWSER_NAME, BrowserType.FIREFOX);
setAcceptInsecureCerts(true);
}
public FirefoxOptions(Capabilities source) {
// We need to initialize all our own fields before calling.
super();
source.asMap().forEach((key, value)-> {
if (value != null) {
setCapability(key, value);
}
});
// If `source` has options, we need to mirror those into this instance. This may be either a
// Map (if we're constructing from a serialized instance) or another FirefoxOptions. *sigh*
Object raw = source.getCapability(FIREFOX_OPTIONS);
if (raw == null) {
return;
}
if (raw instanceof FirefoxOptions) {
FirefoxOptions that = (FirefoxOptions) raw;
addArguments(that.args);
that.booleanPrefs.forEach(this::addPreference);
that.intPrefs.forEach(this::addPreference);
that.stringPrefs.forEach(this::addPreference);
setLegacy(that.legacy);
if (that.logLevel != null) { setLogLevel(that.logLevel); }
if (that.binary != null) { setCapability(BINARY, that.binary.asCapability()); }
if (that.profile != null) { setProfile(that.profile); }
} else if (raw instanceof Map) {
Map that = (Map) raw;
if (that.containsKey("args")) {
Object value = that.get("args");
if (value instanceof String) {
addArguments((String) that.get("args"));
} else if (value instanceof List) {
addArguments((List) that.get("args"));
} else {
// last resort
addArguments(that.get("args").toString());
}
}
if (that.containsKey("prefs")) {
Map prefs = (Map) that.get("prefs");
prefs.forEach((k, v) -> {
if (v instanceof String) {
stringPrefs.put(k, (String) v);
} else if (v instanceof Number) {
intPrefs.put(k, ((Number) v).intValue());
} else if (v instanceof Boolean) {
booleanPrefs.put(k, (Boolean) v);
}
});
}
if (that.containsKey("binary")) { setBinary((String) that.get("binary")); }
if (that.containsKey("log")) {
Map logStruct = (Map) that.get("log");
Object rawLevel = logStruct.get("level");
if (rawLevel instanceof String) {
setLogLevel(FirefoxDriverLogLevel.fromString((String) rawLevel));
} else if (rawLevel instanceof FirefoxDriverLogLevel) {
setLogLevel((FirefoxDriverLogLevel) rawLevel);
}
}
if (that.containsKey("profile")) {
Object value = that.get("profile");
if (value instanceof String) {
try {
setProfile(FirefoxProfile.fromJson((String) value));
} catch (IOException e) {
throw new WebDriverException(e);
}
} else if (value instanceof FirefoxProfile) {
setProfile((FirefoxProfile) value);
} else {
throw new WebDriverException(
"In FirefoxOptions, don't know how to convert profile: " + that);
}
}
}
}
public FirefoxOptions setLegacy(boolean legacy) {
setCapability(MARIONETTE, !legacy);
return this;
}
public boolean isLegacy() {
return legacy;
}
public FirefoxOptions setBinary(FirefoxBinary binary) {
setCapability(BINARY, binary);
return this;
}
public FirefoxOptions setBinary(Path path) {
setCapability(BINARY, path);
return this;
}
public FirefoxOptions setBinary(String path) {
setCapability(BINARY, path);
return this;
}
/**
* Constructs a {@link FirefoxBinary} and returns that to be used, and because of this is only
* useful when actually starting firefox.
*/
public FirefoxBinary getBinary() {
return getBinaryOrNull().orElseGet(FirefoxBinary::new);
}
public Optional getBinaryOrNull() {
return Optional.ofNullable(binary).map(Binary::asBinary);
}
public FirefoxOptions setProfile(FirefoxProfile profile) {
setCapability(FirefoxDriver.PROFILE, profile);
return this;
}
public FirefoxProfile getProfile() {
return profile;
}
public FirefoxOptions addArguments(String... arguments) {
addArguments(ImmutableList.copyOf(arguments));
return this;
}
public FirefoxOptions addArguments(List arguments) {
args.addAll(arguments);
return this;
}
public FirefoxOptions addPreference(String key, boolean value) {
booleanPrefs.put(requireNonNull(key), value);
return this;
}
public FirefoxOptions addPreference(String key, int value) {
intPrefs.put(requireNonNull(key), value);
return this;
}
public FirefoxOptions addPreference(String key, String value) {
stringPrefs.put(requireNonNull(key), value);
return this;
}
/**
* @deprecated Use {@link #setLogLevel(FirefoxDriverLogLevel)}
*/
@Deprecated
public FirefoxOptions setLogLevel(Level logLevel) {
setLogLevel(FirefoxDriverLogLevel.fromLevel(logLevel));
return this;
}
public FirefoxOptions setLogLevel(FirefoxDriverLogLevel logLevel) {
this.logLevel = Objects.requireNonNull(logLevel, "Log level must be set");
return this;
}
public FirefoxOptions setPageLoadStrategy(PageLoadStrategy strategy) {
setCapability(
PAGE_LOAD_STRATEGY,
Objects.requireNonNull(strategy, "Page load strategy must be set"));
return this;
}
public FirefoxOptions setUnhandledPromptBehaviour(UnexpectedAlertBehaviour behaviour) {
setCapability(
UNHANDLED_PROMPT_BEHAVIOUR,
Objects.requireNonNull(behaviour, "Unhandled prompt behavior must be set"));
return this;
}
public FirefoxOptions setAcceptInsecureCerts(boolean acceptInsecureCerts) {
setCapability(ACCEPT_INSECURE_CERTS, acceptInsecureCerts);
return this;
}
public FirefoxOptions setHeadless(boolean headless) {
args.remove("-headless");
if (headless) {
args.add("-headless");
}
return this;
}
public FirefoxOptions setProxy(Proxy proxy) {
setCapability(CapabilityType.PROXY, proxy);
return this;
}
@Override
public void setCapability(String key, Object value) {
switch (key) {
case BINARY:
binary = new Binary(requireNonNull(value, "Binary value cannot be null"));
value = binary.asCapability();
break;
case MARIONETTE:
if (value instanceof Boolean) {
legacy = !(Boolean) value;
}
break;
case PROFILE:
if (value instanceof FirefoxProfile) {
profile = (FirefoxProfile) value;
} else if (value instanceof String) {
try {
profile = FirefoxProfile.fromJson((String) value);
} catch (IOException e) {
throw new WebDriverException(e);
}
value = profile;
} else {
throw new WebDriverException("Unexpected value for profile: " + value);
}
break;
default:
// Do nothing
}
super.setCapability(key, value);
}
@Override
public Map asMap() {
TreeMap toReturn = new TreeMap<>(super.asMap());
ImmutableSortedMap.Builder w3cOptions = ImmutableSortedMap.naturalOrder();
w3cOptions.put("args", args);
if (binary != null) {
w3cOptions.put("binary", binary.asPath());
}
if (logLevel != null) {
w3cOptions.put("log", ImmutableMap.of("level", logLevel));
}
if (profile != null) {
for (Map.Entry pref : booleanPrefs.entrySet()) {
profile.setPreference(pref.getKey(), pref.getValue());
}
for (Map.Entry pref : intPrefs.entrySet()) {
profile.setPreference(pref.getKey(), pref.getValue());
}
for (Map.Entry pref : stringPrefs.entrySet()) {
profile.setPreference(pref.getKey(), pref.getValue());
}
try {
w3cOptions.put("profile", profile.toJson());
} catch (IOException e) {
throw new WebDriverException(e);
}
} else {
ImmutableMap.Builder allPrefs = ImmutableMap.builder();
allPrefs.putAll(booleanPrefs);
allPrefs.putAll(intPrefs);
allPrefs.putAll(stringPrefs);
w3cOptions.put("prefs", allPrefs.build());
}
toReturn.put(FIREFOX_OPTIONS, w3cOptions.build());
return toReturn;
}
@Override
public FirefoxOptions merge(Capabilities capabilities) {
super.merge(capabilities);
return this;
}
@Override
protected int amendHashCode() {
return Objects.hash(
args,
booleanPrefs,
intPrefs,
stringPrefs,
logLevel,
binary,
legacy,
profile);
}
private class Binary {
private String path;
private FirefoxBinary binary;
public Binary(Object value) {
if (value instanceof FirefoxBinary) {
this.binary = (FirefoxBinary) value;
binary.amendOptions(FirefoxOptions.this);
return;
}
if (value instanceof Path || value instanceof String) {
this.path = value.toString().replace('\\', '/');
return;
}
throw new IllegalArgumentException("Unrecognised type for binary: " + value);
}
FirefoxBinary asBinary() {
return binary == null ? new FirefoxBinary(new File(path)) : binary;
}
Object asCapability() {
return binary == null ? path : binary;
}
String asPath() {
return binary == null ? path : binary.getPath();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Binary)) {
return false;
}
Binary that = (Binary) o;
return Objects.equals(this.path, that.path) &&
Objects.equals(this.binary, that.binary);
}
@Override
public int hashCode() {
return Objects.hash(path, binary);
}
}
}