org.openqa.selenium.remote.RemoteWebDriver Maven / Gradle / Ivy
// 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.remote;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.openqa.selenium.Alert;
import org.openqa.selenium.Beta;
import org.openqa.selenium.By;
import org.openqa.selenium.Capabilities;
import org.openqa.selenium.Cookie;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.HasCapabilities;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.NoSuchFrameException;
import org.openqa.selenium.NoSuchWindowException;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.Platform;
import org.openqa.selenium.Point;
import org.openqa.selenium.SearchContext;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.HasInputDevices;
import org.openqa.selenium.interactions.Keyboard;
import org.openqa.selenium.interactions.Mouse;
import org.openqa.selenium.internal.FindsByClassName;
import org.openqa.selenium.internal.FindsByCssSelector;
import org.openqa.selenium.internal.FindsById;
import org.openqa.selenium.internal.FindsByLinkText;
import org.openqa.selenium.internal.FindsByName;
import org.openqa.selenium.internal.FindsByTagName;
import org.openqa.selenium.internal.FindsByXPath;
import org.openqa.selenium.logging.LocalLogs;
import org.openqa.selenium.logging.LogType;
import org.openqa.selenium.logging.LoggingHandler;
import org.openqa.selenium.logging.LoggingPreferences;
import org.openqa.selenium.logging.Logs;
import org.openqa.selenium.logging.NeedsLocalLogs;
import org.openqa.selenium.remote.internal.JsonToWebElementConverter;
import org.openqa.selenium.remote.internal.WebElementToJsonConverter;
import org.openqa.selenium.security.Credentials;
import org.openqa.selenium.security.UserAndPassword;
import java.net.URL;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
@Augmentable
public class RemoteWebDriver implements WebDriver, JavascriptExecutor,
FindsById, FindsByClassName, FindsByLinkText, FindsByName,
FindsByCssSelector, FindsByTagName, FindsByXPath,
HasInputDevices, HasCapabilities, TakesScreenshot {
// TODO(dawagner): This static logger should be unified with the per-instance localLogs
private static final Logger logger = Logger.getLogger(RemoteWebDriver.class.getName());
private Level level = Level.FINE;
private ErrorHandler errorHandler = new ErrorHandler();
private CommandExecutor executor;
private Capabilities capabilities;
private SessionId sessionId;
private FileDetector fileDetector = new UselessFileDetector();
private ExecuteMethod executeMethod;
private JsonToWebElementConverter converter;
private RemoteKeyboard keyboard;
private RemoteMouse mouse;
private Logs remoteLogs;
private LocalLogs localLogs;
// For cglib
protected RemoteWebDriver() {
init(new DesiredCapabilities(), null);
}
public RemoteWebDriver(CommandExecutor executor, Capabilities desiredCapabilities,
Capabilities requiredCapabilities) {
this.executor = executor;
init(desiredCapabilities, requiredCapabilities);
if (executor instanceof NeedsLocalLogs) {
((NeedsLocalLogs)executor).setLocalLogs(localLogs);
}
try {
startClient(desiredCapabilities, requiredCapabilities);
} catch (RuntimeException e) {
try {
stopClient(desiredCapabilities, requiredCapabilities);
} catch (Exception ignored) {
// Ignore the clean-up exception. We'll propagate the original failure.
}
throw e;
}
try {
startSession(desiredCapabilities, requiredCapabilities);
} catch (RuntimeException e) {
try {
quit();
} catch (Exception ignored) {
// Ignore the clean-up exception. We'll propagate the original failure.
}
throw e;
}
}
public RemoteWebDriver(CommandExecutor executor, Capabilities desiredCapabilities) {
this(executor, desiredCapabilities, null);
}
public RemoteWebDriver(Capabilities desiredCapabilities) {
this((URL) null, desiredCapabilities);
}
public RemoteWebDriver(URL remoteAddress, Capabilities desiredCapabilities,
Capabilities requiredCapabilities) {
this(new HttpCommandExecutor(remoteAddress), desiredCapabilities,
requiredCapabilities);
}
public RemoteWebDriver(URL remoteAddress, Capabilities desiredCapabilities) {
this(new HttpCommandExecutor(remoteAddress), desiredCapabilities, null);
}
private void init(Capabilities desiredCapabilities, Capabilities requiredCapabilities) {
logger.addHandler(LoggingHandler.getInstance());
converter = new JsonToWebElementConverter(this);
executeMethod = new RemoteExecuteMethod(this);
keyboard = new RemoteKeyboard(executeMethod);
mouse = new RemoteMouse(executeMethod);
ImmutableSet.Builder builder = new ImmutableSet.Builder<>();
boolean isProfilingEnabled = desiredCapabilities != null &&
desiredCapabilities.is(CapabilityType.ENABLE_PROFILING_CAPABILITY);
if (requiredCapabilities != null && requiredCapabilities.getCapability(
CapabilityType.ENABLE_PROFILING_CAPABILITY) != null) {
isProfilingEnabled = requiredCapabilities.is(CapabilityType.ENABLE_PROFILING_CAPABILITY);
}
if (isProfilingEnabled) {
builder.add(LogType.PROFILER);
}
LoggingPreferences mergedLoggingPrefs = new LoggingPreferences();
if (desiredCapabilities != null) {
mergedLoggingPrefs.addPreferences((LoggingPreferences)desiredCapabilities.getCapability(
CapabilityType.LOGGING_PREFS));
}
if (requiredCapabilities != null) {
mergedLoggingPrefs.addPreferences((LoggingPreferences)requiredCapabilities.getCapability(
CapabilityType.LOGGING_PREFS));
}
if ((mergedLoggingPrefs.getEnabledLogTypes().contains(LogType.CLIENT) &&
mergedLoggingPrefs.getLevel(LogType.CLIENT) != Level.OFF) ||
!mergedLoggingPrefs.getEnabledLogTypes().contains(LogType.CLIENT)) {
builder.add(LogType.CLIENT);
}
Set logTypesToInclude = builder.build();
LocalLogs performanceLogger = LocalLogs.getStoringLoggerInstance(logTypesToInclude);
LocalLogs clientLogs = LocalLogs.getHandlerBasedLoggerInstance(LoggingHandler.getInstance(),
logTypesToInclude);
localLogs = LocalLogs.getCombinedLogsHolder(clientLogs, performanceLogger);
remoteLogs = new RemoteLogs(executeMethod, localLogs);
}
/**
* Set the file detector to be used when sending keyboard input. By default, this is set to a file
* detector that does nothing.
*
* @param detector The detector to use. Must not be null.
* @see FileDetector
* @see LocalFileDetector
* @see UselessFileDetector
*/
public void setFileDetector(FileDetector detector) {
if (detector == null) {
throw new WebDriverException("You may not set a file detector that is null");
}
fileDetector = detector;
}
public SessionId getSessionId() {
return sessionId;
}
protected void setSessionId(String opaqueKey) {
sessionId = new SessionId(opaqueKey);
}
protected void startSession(Capabilities desiredCapabilities) {
startSession(desiredCapabilities, null);
}
@SuppressWarnings({"unchecked"})
protected void startSession(Capabilities desiredCapabilities,
Capabilities requiredCapabilities) {
ImmutableMap.Builder paramBuilder =
new ImmutableMap.Builder<>();
paramBuilder.put("desiredCapabilities", desiredCapabilities);
if (requiredCapabilities != null) {
paramBuilder.put("requiredCapabilities", requiredCapabilities);
}
Map parameters = paramBuilder.build();
Response response = execute(DriverCommand.NEW_SESSION, parameters);
Map rawCapabilities = (Map) response.getValue();
DesiredCapabilities returnedCapabilities = new DesiredCapabilities();
for (Map.Entry entry : rawCapabilities.entrySet()) {
// Handle the platform later
if (CapabilityType.PLATFORM.equals(entry.getKey())) {
continue;
}
returnedCapabilities.setCapability(entry.getKey(), entry.getValue());
}
String platformString = (String) rawCapabilities.get(CapabilityType.PLATFORM);
Platform platform;
try {
if (platformString == null || "".equals(platformString)) {
platform = Platform.ANY;
} else {
platform = Platform.valueOf(platformString);
}
} catch (IllegalArgumentException e) {
// The server probably responded with a name matching the os.name
// system property. Try to recover and parse this.
platform = Platform.extractFromSysProperty(platformString);
}
returnedCapabilities.setPlatform(platform);
capabilities = returnedCapabilities;
sessionId = new SessionId(response.getSessionId());
}
/**
* Method called before {@link #startSession(Capabilities) starting a new session}. The default
* implementation is a no-op, but subtypes should override this method to define custom behavior.
*/
protected void startClient() {
}
/**
* Method called before {@link #startSession(Capabilities) starting a new session}. The default
* implementation is a no-op, but subtypes should override this method to define custom behavior.
*/
protected void startClient(Capabilities desiredCapabilities, Capabilities requiredCapabilities) {
startClient();
}
/**
* Method called after executing a {@link #quit()} command. The default implementation is a no-op,
* but subtypes should override this method to define custom behavior.
*/
protected void stopClient() {
}
/**
* Method called after executing a {@link #quit()} command. The default implementation is a no-op,
* but subtypes should override this method to define custom behavior.
*/
protected void stopClient(Capabilities desiredCapabilities, Capabilities requiredCapabilities) {
stopClient();
}
public ErrorHandler getErrorHandler() {
return errorHandler;
}
public void setErrorHandler(ErrorHandler handler) {
this.errorHandler = handler;
}
public CommandExecutor getCommandExecutor() {
return executor;
}
protected void setCommandExecutor(CommandExecutor executor) {
this.executor = executor;
}
public Capabilities getCapabilities() {
return capabilities;
}
public void get(String url) {
execute(DriverCommand.GET, ImmutableMap.of("url", url));
}
public String getTitle() {
Response response = execute(DriverCommand.GET_TITLE);
Object value = response.getValue();
return value == null ? "" : value.toString();
}
public String getCurrentUrl() {
Response response = execute(DriverCommand.GET_CURRENT_URL);
if (response == null || response.getValue() == null) {
throw new WebDriverException("Remote browser did not respond to getCurrentUrl");
}
return response.getValue().toString();
}
public X getScreenshotAs(OutputType outputType) throws WebDriverException {
Response response = execute(DriverCommand.SCREENSHOT);
Object result = response.getValue();
if (result instanceof String) {
String base64EncodedPng = (String) result;
return outputType.convertFromBase64Png(base64EncodedPng);
} else if (result instanceof byte[]) {
String base64EncodedPng = new String((byte[]) result);
return outputType.convertFromBase64Png(base64EncodedPng);
} else {
throw new RuntimeException(String.format("Unexpected result for %s command: %s",
DriverCommand.SCREENSHOT,
result == null ? "null" : result.getClass().getName() + " instance"));
}
}
public List findElements(By by) {
return by.findElements(this);
}
public WebElement findElement(By by) {
return by.findElement(this);
}
protected WebElement findElement(String by, String using) {
if (using == null) {
throw new IllegalArgumentException("Cannot find elements when the selector is null.");
}
Response response = execute(DriverCommand.FIND_ELEMENT,
ImmutableMap.of("using", by, "value", using));
Object value = response.getValue();
WebElement element;
try {
element = (WebElement) value;
} catch (ClassCastException ex) {
throw new WebDriverException("Returned value cannot be converted to WebElement: " + value, ex);
}
setFoundBy(this, element, by, using);
return element;
}
protected void setFoundBy(SearchContext context, WebElement element, String by, String using) {
if (element instanceof RemoteWebElement) {
((RemoteWebElement) element).setFoundBy(context, by, using);
}
}
@SuppressWarnings("unchecked")
protected List findElements(String by, String using) {
if (using == null) {
throw new IllegalArgumentException("Cannot find elements when the selector is null.");
}
Response response = execute(DriverCommand.FIND_ELEMENTS,
ImmutableMap.of("using", by, "value", using));
Object value = response.getValue();
List allElements;
try {
allElements = (List) value;
} catch (ClassCastException ex) {
throw new WebDriverException("Returned value cannot be converted to List: " + value, ex);
}
for (WebElement element : allElements) {
setFoundBy(this, element, by, using);
}
return allElements;
}
static String cssEscape(String using) {
using = using.replaceAll("(['\"\\\\#.:;,!?+<>=~*^$|%&@`{}\\-\\/\\[\\]\\(\\)])", "\\\\$1");
if (using.length() > 0 && Character.isDigit(using.charAt(0))) {
using = "\\" + Integer.toString(30 + Integer.parseInt(using.substring(0,1))) + " " + using.substring(1);
}
return using;
}
public WebElement findElementById(String using) {
return findElement("id", using);
}
public List findElementsById(String using) {
return findElements("id", using);
}
public WebElement findElementByLinkText(String using) {
return findElement("link text", using);
}
public List findElementsByLinkText(String using) {
return findElements("link text", using);
}
public WebElement findElementByPartialLinkText(String using) {
return findElement("partial link text", using);
}
public List findElementsByPartialLinkText(String using) {
return findElements("partial link text", using);
}
public WebElement findElementByTagName(String using) {
return findElement("tag name", using);
}
public List findElementsByTagName(String using) {
return findElements("tag name", using);
}
public WebElement findElementByName(String using) {
return findElement("name", using);
}
public List findElementsByName(String using) {
return findElements("name", using);
}
public WebElement findElementByClassName(String using) {
return findElement("class name", using);
}
public List findElementsByClassName(String using) {
return findElements("class name", using);
}
public WebElement findElementByCssSelector(String using) {
return findElement("css selector", using);
}
public List findElementsByCssSelector(String using) {
return findElements("css selector", using);
}
public WebElement findElementByXPath(String using) {
return findElement("xpath", using);
}
public List findElementsByXPath(String using) {
return findElements("xpath", using);
}
// Misc
public String getPageSource() {
return (String) execute(DriverCommand.GET_PAGE_SOURCE).getValue();
}
public void close() {
execute(DriverCommand.CLOSE);
}
public void quit() {
// no-op if session id is null. We're only going to make ourselves unhappy
if (sessionId == null) {
return;
}
try {
execute(DriverCommand.QUIT);
} finally {
sessionId = null;
stopClient();
}
}
@SuppressWarnings({"unchecked"})
public Set getWindowHandles() {
Response response = execute(DriverCommand.GET_WINDOW_HANDLES);
Object value = response.getValue();
try {
List returnedValues = (List) value;
return new LinkedHashSet<>(returnedValues);
} catch (ClassCastException ex) {
throw new WebDriverException(
"Returned value cannot be converted to List: " + value, ex);
}
}
public String getWindowHandle() {
return String.valueOf(execute(DriverCommand.GET_CURRENT_WINDOW_HANDLE).getValue());
}
public Object executeScript(String script, Object... args) {
if (!capabilities.isJavascriptEnabled()) {
throw new UnsupportedOperationException(
"You must be using an underlying instance of WebDriver that supports executing javascript");
}
// Escape the quote marks
script = script.replaceAll("\"", "\\\"");
Iterable
© 2015 - 2025 Weber Informatics LLC | Privacy Policy