org.openqa.selenium.remote.RemoteWebDriver.java Source code

Java tutorial

Introduction

Here is the source code for org.openqa.selenium.remote.RemoteWebDriver.java

Source

// 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 static java.util.concurrent.TimeUnit.SECONDS;
import static org.openqa.selenium.remote.CapabilityType.LOGGING_PREFS;
import static org.openqa.selenium.remote.CapabilityType.PLATFORM;
import static org.openqa.selenium.remote.CapabilityType.PLATFORM_NAME;
import static org.openqa.selenium.remote.CapabilityType.SUPPORTS_JAVASCRIPT;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;

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.ImmutableCapabilities;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.MutableCapabilities;
import org.openqa.selenium.NoSuchElementException;
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.WindowType;
import org.openqa.selenium.interactions.HasInputDevices;
import org.openqa.selenium.interactions.Interactive;
import org.openqa.selenium.interactions.Keyboard;
import org.openqa.selenium.interactions.Mouse;
import org.openqa.selenium.interactions.Sequence;
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.WebElementToJsonConverter;
import org.openqa.selenium.virtualauthenticator.Credential;
import org.openqa.selenium.virtualauthenticator.HasVirtualAuthenticator;
import org.openqa.selenium.virtualauthenticator.VirtualAuthenticator;
import org.openqa.selenium.virtualauthenticator.VirtualAuthenticatorOptions;

import java.net.URL;
import java.util.Base64;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Augmentable
public class RemoteWebDriver implements WebDriver, JavascriptExecutor, FindsById, FindsByClassName, FindsByLinkText,
        FindsByName, FindsByCssSelector, FindsByTagName, FindsByXPath, HasInputDevices, HasCapabilities,
        Interactive, TakesScreenshot, HasVirtualAuthenticator {

    // 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 ImmutableCapabilities());
    }

    public RemoteWebDriver(Capabilities capabilities) {
        this(new HttpCommandExecutor(null), capabilities);
    }

    public RemoteWebDriver(CommandExecutor executor, Capabilities capabilities) {
        this.executor = executor;

        init(capabilities);

        if (executor instanceof NeedsLocalLogs) {
            ((NeedsLocalLogs) executor).setLocalLogs(localLogs);
        }

        try {
            startSession(capabilities);
        } catch (RuntimeException e) {
            try {
                quit();
            } catch (Exception ignored) {
                // Ignore the clean-up exception. We'll propagate the original failure.
            }

            throw e;
        }
    }

    public RemoteWebDriver(URL remoteAddress, Capabilities capabilities) {
        this(new HttpCommandExecutor(remoteAddress), capabilities);
    }

    @Beta
    public static RemoteWebDriverBuilder builder() {
        return new RemoteWebDriverBuilder();
    }

    private void init(Capabilities capabilities) {
        capabilities = capabilities == null ? new ImmutableCapabilities() : capabilities;

        logger.addHandler(LoggingHandler.getInstance());

        converter = new JsonToWebElementConverter(this);
        executeMethod = new RemoteExecuteMethod(this);
        keyboard = new RemoteKeyboard(executeMethod);
        mouse = new RemoteMouse(executeMethod);

        ImmutableSet.Builder<String> builder = new ImmutableSet.Builder<>();

        boolean isProfilingEnabled = capabilities.is(CapabilityType.ENABLE_PROFILING_CAPABILITY);
        if (isProfilingEnabled) {
            builder.add(LogType.PROFILER);
        }

        LoggingPreferences mergedLoggingPrefs = new LoggingPreferences();
        mergedLoggingPrefs.addPreferences((LoggingPreferences) capabilities.getCapability(LOGGING_PREFS));

        if (!mergedLoggingPrefs.getEnabledLogTypes().contains(LogType.CLIENT)
                || mergedLoggingPrefs.getLevel(LogType.CLIENT) != Level.OFF) {
            builder.add(LogType.CLIENT);
        }

        Set<String> 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 capabilities) {
        Response response = execute(DriverCommand.NEW_SESSION(capabilities));

        Map<String, Object> rawCapabilities = (Map<String, Object>) response.getValue();
        MutableCapabilities returnedCapabilities = new MutableCapabilities();
        for (Map.Entry<String, Object> entry : rawCapabilities.entrySet()) {
            // Handle the platform later
            if (PLATFORM.equals(entry.getKey()) || PLATFORM_NAME.equals(entry.getKey())) {
                continue;
            }
            returnedCapabilities.setCapability(entry.getKey(), entry.getValue());
        }
        String platformString = (String) rawCapabilities.getOrDefault(PLATFORM, rawCapabilities.get(PLATFORM_NAME));
        Platform platform;
        try {
            if (platformString == null || "".equals(platformString)) {
                platform = Platform.ANY;
            } else {
                platform = Platform.fromString(platformString);
            }
        } catch (WebDriverException 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.setCapability(PLATFORM, platform);
        returnedCapabilities.setCapability(PLATFORM_NAME, platform);

        if (rawCapabilities.containsKey(SUPPORTS_JAVASCRIPT)) {
            Object raw = rawCapabilities.get(SUPPORTS_JAVASCRIPT);
            if (raw instanceof String) {
                returnedCapabilities.setCapability(SUPPORTS_JAVASCRIPT, Boolean.parseBoolean((String) raw));
            } else if (raw instanceof Boolean) {
                returnedCapabilities.setCapability(SUPPORTS_JAVASCRIPT, ((Boolean) raw).booleanValue());
            }
        } else {
            returnedCapabilities.setCapability(SUPPORTS_JAVASCRIPT, true);
        }

        this.capabilities = returnedCapabilities;
        sessionId = new SessionId(response.getSessionId());
    }

    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;
    }

    @Override
    public Capabilities getCapabilities() {
        return capabilities;
    }

    @Override
    public void get(String url) {
        execute(DriverCommand.GET(url));
    }

    @Override
    public String getTitle() {
        Response response = execute(DriverCommand.GET_TITLE);
        Object value = response.getValue();
        return value == null ? "" : value.toString();
    }

    @Override
    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();
    }

    @Override
    public <X> X getScreenshotAs(OutputType<X> 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"));
        }
    }

    @Override
    public List<WebElement> findElements(By by) {
        return by.findElements(this);
    }

    @Override
    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(by, using));
        Object value = response.getValue();
        if (value == null) { // see https://github.com/SeleniumHQ/selenium/issues/5809
            throw new NoSuchElementException(String.format("Cannot locate an element using %s=%s", by, using));
        }
        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 remoteElement = (RemoteWebElement) element;
            remoteElement.setFoundBy(context, by, using);
            remoteElement.setFileDetector(getFileDetector());
        }
    }

    @SuppressWarnings("unchecked")
    protected List<WebElement> 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(by, using));
        Object value = response.getValue();
        if (value == null) { // see https://github.com/SeleniumHQ/selenium/issues/4555
            return Collections.emptyList();
        }
        List<WebElement> allElements;
        try {
            allElements = (List<WebElement>) value;
        } catch (ClassCastException ex) {
            throw new WebDriverException("Returned value cannot be converted to List<WebElement>: " + value, ex);
        }
        for (WebElement element : allElements) {
            setFoundBy(this, element, by, using);
        }
        return allElements;
    }

    @Override
    public WebElement findElementById(String using) {
        return findElement("id", using);
    }

    @Override
    public List<WebElement> findElementsById(String using) {
        return findElements("id", using);
    }

    @Override
    public WebElement findElementByLinkText(String using) {
        return findElement("link text", using);
    }

    @Override
    public List<WebElement> findElementsByLinkText(String using) {
        return findElements("link text", using);
    }

    @Override
    public WebElement findElementByPartialLinkText(String using) {
        return findElement("partial link text", using);
    }

    @Override
    public List<WebElement> findElementsByPartialLinkText(String using) {
        return findElements("partial link text", using);
    }

    @Override
    public WebElement findElementByTagName(String using) {
        return findElement("tag name", using);
    }

    @Override
    public List<WebElement> findElementsByTagName(String using) {
        return findElements("tag name", using);
    }

    @Override
    public WebElement findElementByName(String using) {
        return findElement("name", using);
    }

    @Override
    public List<WebElement> findElementsByName(String using) {
        return findElements("name", using);
    }

    @Override
    public WebElement findElementByClassName(String using) {
        return findElement("class name", using);
    }

    @Override
    public List<WebElement> findElementsByClassName(String using) {
        return findElements("class name", using);
    }

    @Override
    public WebElement findElementByCssSelector(String using) {
        return findElement("css selector", using);
    }

    @Override
    public List<WebElement> findElementsByCssSelector(String using) {
        return findElements("css selector", using);
    }

    @Override
    public WebElement findElementByXPath(String using) {
        return findElement("xpath", using);
    }

    @Override
    public List<WebElement> findElementsByXPath(String using) {
        return findElements("xpath", using);
    }

    // Misc

    @Override
    public String getPageSource() {
        return (String) execute(DriverCommand.GET_PAGE_SOURCE).getValue();
    }

    @Override
    public void close() {
        execute(DriverCommand.CLOSE);
    }

    @Override
    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;
        }
    }

    @Override
    @SuppressWarnings({ "unchecked" })
    public Set<String> getWindowHandles() {
        Response response = execute(DriverCommand.GET_WINDOW_HANDLES);
        Object value = response.getValue();
        try {
            List<String> returnedValues = (List<String>) value;
            return new LinkedHashSet<>(returnedValues);
        } catch (ClassCastException ex) {
            throw new WebDriverException("Returned value cannot be converted to List<String>: " + value, ex);
        }
    }

    @Override
    public String getWindowHandle() {
        return String.valueOf(execute(DriverCommand.GET_CURRENT_WINDOW_HANDLE).getValue());
    }

    @Override
    public Object executeScript(String script, Object... args) {
        if (!isJavascriptEnabled()) {
            throw new UnsupportedOperationException(
                    "You must be using an underlying instance of WebDriver that supports executing javascript");
        }

        // Escape the quote marks
        script = script.replaceAll("\"", "\\\"");

        List<Object> convertedArgs = Stream.of(args).map(new WebElementToJsonConverter())
                .collect(Collectors.toList());

        return execute(DriverCommand.EXECUTE_SCRIPT(script, convertedArgs)).getValue();
    }

    @Override
    public Object executeAsyncScript(String script, Object... args) {
        if (!isJavascriptEnabled()) {
            throw new UnsupportedOperationException("You must be using an underlying instance of "
                    + "WebDriver that supports executing javascript");
        }

        // Escape the quote marks
        script = script.replaceAll("\"", "\\\"");

        List<Object> convertedArgs = Stream.of(args).map(new WebElementToJsonConverter())
                .collect(Collectors.toList());

        return execute(DriverCommand.EXECUTE_ASYNC_SCRIPT(script, convertedArgs)).getValue();
    }

    private boolean isJavascriptEnabled() {
        return capabilities.is(SUPPORTS_JAVASCRIPT);
    }

    @Override
    public TargetLocator switchTo() {
        return new RemoteTargetLocator();
    }

    @Override
    public Navigation navigate() {
        return new RemoteNavigation();
    }

    @Override
    public Options manage() {
        return new RemoteWebDriverOptions();
    }

    protected void setElementConverter(JsonToWebElementConverter converter) {
        this.converter = Objects.requireNonNull(converter, "Element converter must not be null");
    }

    protected JsonToWebElementConverter getElementConverter() {
        return converter;
    }

    /**
     * Sets the RemoteWebDriver's client log level.
     *
     * @param level The log level to use.
     */
    public void setLogLevel(Level level) {
        this.level = level;
    }

    Response execute(CommandPayload payload) {
        Command command = new Command(sessionId, payload);
        Response response;

        long start = System.currentTimeMillis();
        String currentName = Thread.currentThread().getName();
        Thread.currentThread()
                .setName(String.format("Forwarding %s on session %s to remote", command.getName(), sessionId));
        try {
            log(sessionId, command.getName(), command, When.BEFORE);
            response = executor.execute(command);
            log(sessionId, command.getName(), command, When.AFTER);

            if (response == null) {
                return null;
            }

            // Unwrap the response value by converting any JSON objects of the form
            // {"ELEMENT": id} to RemoteWebElements.
            Object value = getElementConverter().apply(response.getValue());
            response.setValue(value);
        } catch (WebDriverException e) {
            throw e;
        } catch (Exception e) {
            log(sessionId, command.getName(), command, When.EXCEPTION);
            String errorMessage = "Error communicating with the remote browser. " + "It may have died.";
            if (command.getName().equals(DriverCommand.NEW_SESSION)) {
                errorMessage = "Could not start a new session. Possible causes are "
                        + "invalid address of the remote server or browser start-up failure.";
            }
            UnreachableBrowserException ube = new UnreachableBrowserException(errorMessage, e);
            if (getSessionId() != null) {
                ube.addInfo(WebDriverException.SESSION_ID, getSessionId().toString());
            }
            if (getCapabilities() != null) {
                ube.addInfo("Capabilities", getCapabilities().toString());
            }
            throw ube;
        } finally {
            Thread.currentThread().setName(currentName);
        }

        try {
            errorHandler.throwIfResponseFailed(response, System.currentTimeMillis() - start);
        } catch (WebDriverException ex) {
            if (command.getParameters() != null && command.getParameters().containsKey("using")
                    && command.getParameters().containsKey("value")) {
                ex.addInfo("*** Element info", String.format("{Using=%s, value=%s}",
                        command.getParameters().get("using"), command.getParameters().get("value")));
            }
            ex.addInfo(WebDriverException.DRIVER_INFO, this.getClass().getName());
            if (getSessionId() != null) {
                ex.addInfo(WebDriverException.SESSION_ID, getSessionId().toString());
            }
            if (getCapabilities() != null) {
                ex.addInfo("Capabilities", getCapabilities().toString());
            }
            throw ex;
        }
        return response;
    }

    protected Response execute(String driverCommand, Map<String, ?> parameters) {
        return execute(new CommandPayload(driverCommand, parameters));
    }

    protected Response execute(String command) {
        return execute(command, ImmutableMap.of());
    }

    protected ExecuteMethod getExecuteMethod() {
        return executeMethod;
    }

    @Override
    public void perform(Collection<Sequence> actions) {
        execute(DriverCommand.ACTIONS(actions));
    }

    @Override
    public void resetInputState() {
        execute(DriverCommand.CLEAR_ACTIONS_STATE);
    }

    @Override
    public Keyboard getKeyboard() {
        return keyboard;
    }

    @Override
    public Mouse getMouse() {
        return mouse;
    }

    @Override
    public VirtualAuthenticator addVirtualAuthenticator(VirtualAuthenticatorOptions options) {
        String authenticatorId = (String) execute(DriverCommand.ADD_VIRTUAL_AUTHENTICATOR, options.toMap())
                .getValue();
        return new RemoteVirtualAuthenticator(authenticatorId);
    }

    @Override
    public void removeVirtualAuthenticator(VirtualAuthenticator authenticator) {
        execute(DriverCommand.REMOVE_VIRTUAL_AUTHENTICATOR,
                ImmutableMap.of("authenticatorId", authenticator.getId()));
    }

    /**
     * Override this to be notified at key points in the execution of a command.
     *
     * @param sessionId   the session id.
     * @param commandName the command that is being executed.
     * @param toLog       any data that might be interesting.
     * @param when        verb tense of "Execute" to prefix message
     */
    protected void log(SessionId sessionId, String commandName, Object toLog, When when) {
        if (!logger.isLoggable(level)) {
            return;
        }
        String text = String.valueOf(toLog);
        if (commandName.equals(DriverCommand.EXECUTE_SCRIPT)
                || commandName.equals(DriverCommand.EXECUTE_ASYNC_SCRIPT)) {
            if (text.length() > 100 && Boolean.getBoolean("webdriver.remote.shorten_log_messages")) {
                text = text.substring(0, 100) + "...";
            }
        }
        switch (when) {
        case BEFORE:
            logger.log(level, "Executing: " + commandName + " " + text);
            break;
        case AFTER:
            logger.log(level, "Executed: " + text);
            break;
        case EXCEPTION:
            logger.log(level, "Exception: " + text);
            break;
        default:
            logger.log(level, text);
            break;
        }
    }

    public FileDetector getFileDetector() {
        return fileDetector;
    }

    protected class RemoteWebDriverOptions implements Options {

        @Override
        @Beta
        public Logs logs() {
            return remoteLogs;
        }

        @Override
        public void addCookie(Cookie cookie) {
            cookie.validate();
            execute(DriverCommand.ADD_COOKIE(cookie));
        }

        @Override
        public void deleteCookieNamed(String name) {
            execute(DriverCommand.DELETE_COOKIE(name));
        }

        @Override
        public void deleteCookie(Cookie cookie) {
            deleteCookieNamed(cookie.getName());
        }

        @Override
        public void deleteAllCookies() {
            execute(DriverCommand.DELETE_ALL_COOKIES);
        }

        @Override
        @SuppressWarnings({ "unchecked" })
        public Set<Cookie> getCookies() {
            Object returned = execute(DriverCommand.GET_ALL_COOKIES).getValue();

            Set<Cookie> toReturn = new HashSet<>();

            if (!(returned instanceof Collection)) {
                return toReturn;
            }

            ((Collection<?>) returned).stream().map(o -> (Map<String, Object>) o).map(rawCookie -> {
                Cookie.Builder builder = new Cookie.Builder((String) rawCookie.get("name"),
                        (String) rawCookie.get("value")).path((String) rawCookie.get("path"))
                                .domain((String) rawCookie.get("domain"))
                                .isSecure(rawCookie.containsKey("secure") && (Boolean) rawCookie.get("secure"))
                                .isHttpOnly(
                                        rawCookie.containsKey("httpOnly") && (Boolean) rawCookie.get("httpOnly"));

                Number expiryNum = (Number) rawCookie.get("expiry");
                builder.expiresOn(expiryNum == null ? null : new Date(SECONDS.toMillis(expiryNum.longValue())));
                return builder.build();
            }).forEach(toReturn::add);

            return toReturn;
        }

        @Override
        public Cookie getCookieNamed(String name) {
            Set<Cookie> allCookies = getCookies();
            for (Cookie cookie : allCookies) {
                if (cookie.getName().equals(name)) {
                    return cookie;
                }
            }
            return null;
        }

        @Override
        public Timeouts timeouts() {
            return new RemoteTimeouts();
        }

        @Override
        public ImeHandler ime() {
            return new RemoteInputMethodManager();
        }

        @Override
        @Beta
        public Window window() {
            return new RemoteWindow();
        }

        protected class RemoteInputMethodManager implements WebDriver.ImeHandler {

            @Override
            @SuppressWarnings("unchecked")
            public List<String> getAvailableEngines() {
                Response response = execute(DriverCommand.IME_GET_AVAILABLE_ENGINES);
                return (List<String>) response.getValue();
            }

            @Override
            public String getActiveEngine() {
                Response response = execute(DriverCommand.IME_GET_ACTIVE_ENGINE);
                return (String) response.getValue();
            }

            @Override
            public boolean isActivated() {
                Response response = execute(DriverCommand.IME_IS_ACTIVATED);
                return (Boolean) response.getValue();
            }

            @Override
            public void deactivate() {
                execute(DriverCommand.IME_DEACTIVATE);
            }

            @Override
            public void activateEngine(String engine) {
                execute(DriverCommand.IME_ACTIVATE_ENGINE(engine));
            }
        } // RemoteInputMethodManager class

        protected class RemoteTimeouts implements Timeouts {

            @Override
            public Timeouts implicitlyWait(long time, TimeUnit unit) {
                execute(DriverCommand.SET_IMPLICIT_WAIT_TIMEOUT(time, unit));
                return this;
            }

            @Override
            public Timeouts setScriptTimeout(long time, TimeUnit unit) {
                execute(DriverCommand.SET_SCRIPT_TIMEOUT(time, unit));
                return this;
            }

            @Override
            public Timeouts pageLoadTimeout(long time, TimeUnit unit) {
                execute(DriverCommand.SET_PAGE_LOAD_TIMEOUT(time, unit));
                return this;
            }
        } // timeouts class.

        @Beta
        protected class RemoteWindow implements Window {

            @Override
            public void setSize(Dimension targetSize) {
                execute(DriverCommand.SET_CURRENT_WINDOW_SIZE(targetSize));
            }

            @Override
            public void setPosition(Point targetPosition) {
                execute(DriverCommand.SET_CURRENT_WINDOW_POSITION(targetPosition));
            }

            @Override
            @SuppressWarnings({ "unchecked" })
            public Dimension getSize() {
                Response response = execute(DriverCommand.GET_CURRENT_WINDOW_SIZE);

                Map<String, Object> rawSize = (Map<String, Object>) response.getValue();

                int width = ((Number) rawSize.get("width")).intValue();
                int height = ((Number) rawSize.get("height")).intValue();

                return new Dimension(width, height);
            }

            Map<String, Object> rawPoint;

            @Override
            @SuppressWarnings("unchecked")
            public Point getPosition() {
                Response response = execute(DriverCommand.GET_CURRENT_WINDOW_POSITION());
                rawPoint = (Map<String, Object>) response.getValue();

                int x = ((Number) rawPoint.get("x")).intValue();
                int y = ((Number) rawPoint.get("y")).intValue();

                return new Point(x, y);
            }

            @Override
            public void maximize() {
                execute(DriverCommand.MAXIMIZE_CURRENT_WINDOW);
            }

            @Override
            public void fullscreen() {
                execute(DriverCommand.FULLSCREEN_CURRENT_WINDOW);
            }
        }
    }

    private class RemoteNavigation implements Navigation {

        @Override
        public void back() {
            execute(DriverCommand.GO_BACK);
        }

        @Override
        public void forward() {
            execute(DriverCommand.GO_FORWARD);
        }

        @Override
        public void to(String url) {
            get(url);
        }

        @Override
        public void to(URL url) {
            get(String.valueOf(url));
        }

        @Override
        public void refresh() {
            execute(DriverCommand.REFRESH);
        }
    }

    protected class RemoteTargetLocator implements TargetLocator {

        @Override
        public WebDriver frame(int frameIndex) {
            execute(DriverCommand.SWITCH_TO_FRAME(frameIndex));
            return RemoteWebDriver.this;
        }

        @Override
        public WebDriver frame(String frameName) {
            String name = frameName.replaceAll("(['\"\\\\#.:;,!?+<>=~*^$|%&@`{}\\-/\\[\\]\\(\\)])", "\\\\$1");
            List<WebElement> frameElements = RemoteWebDriver.this
                    .findElements(By.cssSelector("frame[name='" + name + "'],iframe[name='" + name + "']"));
            if (frameElements.size() == 0) {
                frameElements = RemoteWebDriver.this
                        .findElements(By.cssSelector("frame#" + name + ",iframe#" + name));
            }
            if (frameElements.size() == 0) {
                throw new NoSuchFrameException("No frame element found by name or id " + frameName);
            }
            return frame(frameElements.get(0));
        }

        @Override
        public WebDriver frame(WebElement frameElement) {
            Object elementAsJson = new WebElementToJsonConverter().apply(frameElement);
            execute(DriverCommand.SWITCH_TO_FRAME(elementAsJson));
            return RemoteWebDriver.this;
        }

        @Override
        public WebDriver parentFrame() {
            execute(DriverCommand.SWITCH_TO_PARENT_FRAME);
            return RemoteWebDriver.this;
        }

        @Override
        public WebDriver window(String windowHandleOrName) {
            try {
                execute(DriverCommand.SWITCH_TO_WINDOW(windowHandleOrName));
                return RemoteWebDriver.this;
            } catch (NoSuchWindowException nsw) {
                // simulate search by name
                String original = getWindowHandle();
                for (String handle : getWindowHandles()) {
                    switchTo().window(handle);
                    if (windowHandleOrName.equals(executeScript("return window.name"))) {
                        return RemoteWebDriver.this; // found by name
                    }
                }
                switchTo().window(original);
                throw nsw;
            }
        }

        @Override
        public WebDriver newWindow(WindowType typeHint) {
            String original = getWindowHandle();
            try {
                Response response = execute(DriverCommand.SWITCH_TO_NEW_WINDOW(typeHint));
                String newWindowHandle = ((Map<String, Object>) response.getValue()).get("handle").toString();
                switchTo().window(newWindowHandle);
                return RemoteWebDriver.this;
            } catch (WebDriverException ex) {
                switchTo().window(original);
                throw ex;
            }
        }

        @Override
        public WebDriver defaultContent() {
            execute(DriverCommand.SWITCH_TO_FRAME(null));
            return RemoteWebDriver.this;
        }

        @Override
        public WebElement activeElement() {
            Response response = execute(DriverCommand.GET_ACTIVE_ELEMENT);
            return (WebElement) response.getValue();
        }

        @Override
        public Alert alert() {
            execute(DriverCommand.GET_ALERT_TEXT);
            return new RemoteAlert();
        }
    }

    private class RemoteAlert implements Alert {

        public RemoteAlert() {
        }

        @Override
        public void dismiss() {
            execute(DriverCommand.DISMISS_ALERT);
        }

        @Override
        public void accept() {
            execute(DriverCommand.ACCEPT_ALERT);
        }

        @Override
        public String getText() {
            return (String) execute(DriverCommand.GET_ALERT_TEXT).getValue();
        }

        /**
         * @param keysToSend character sequence to send to the alert
         *
         * @throws IllegalArgumentException if keysToSend is null
         */
        @Override
        public void sendKeys(String keysToSend) {
            if (keysToSend == null) {
                throw new IllegalArgumentException("Keys to send should be a not null CharSequence");
            }
            execute(DriverCommand.SET_ALERT_VALUE(keysToSend));
        }
    }

    private class RemoteVirtualAuthenticator implements VirtualAuthenticator {
        private final String id;

        public RemoteVirtualAuthenticator(final String id) {
            this.id = Objects.requireNonNull(id);
        }

        @Override
        public String getId() {
            return id;
        }

        @Override
        public void addCredential(Credential credential) {
            execute(DriverCommand.ADD_CREDENTIAL, new ImmutableMap.Builder<String, Object>()
                    .putAll(credential.toMap()).put("authenticatorId", id).build());
        }

        @Override
        public List<Credential> getCredentials() {
            List<Map<String, Object>> response = (List<Map<String, Object>>) execute(DriverCommand.GET_CREDENTIALS,
                    ImmutableMap.of("authenticatorId", id)).getValue();
            return response.stream().map(Credential::fromMap).collect(Collectors.toList());
        }

        @Override
        public void removeCredential(byte[] credentialId) {
            removeCredential(Base64.getUrlEncoder().encodeToString(credentialId));
        }

        @Override
        public void removeCredential(String credentialId) {
            execute(DriverCommand.REMOVE_CREDENTIAL,
                    ImmutableMap.of("authenticatorId", id, "credentialId", credentialId)).getValue();
        }

        @Override
        public void removeAllCredentials() {
            execute(DriverCommand.REMOVE_ALL_CREDENTIALS, ImmutableMap.of("authenticatorId", id));
        }

        @Override
        public void setUserVerified(boolean verified) {
            execute(DriverCommand.SET_USER_VERIFIED,
                    ImmutableMap.of("authenticatorId", id, "isUserVerified", verified));
        }
    }

    public enum When {
        BEFORE, AFTER, EXCEPTION
    }

    @Override
    public String toString() {
        Capabilities caps = getCapabilities();
        if (caps == null) {
            return super.toString();
        }

        // w3c name first
        Object platform = caps.getCapability(PLATFORM_NAME);
        if (!(platform instanceof String)) {
            platform = caps.getCapability(PLATFORM);
        }
        if (platform == null) {
            platform = "unknown";
        }

        return String.format("%s: %s on %s (%s)", getClass().getSimpleName(), caps.getBrowserName(), platform,
                getSessionId());
    }
}