All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.mike10004.seleniumhelp.ChromeWebDriverFactory Maven / Gradle / Ivy

There is a newer version: 0.58
Show newest version
package com.github.mike10004.seleniumhelp;

import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeDriverService;
import org.openqa.selenium.chrome.ChromeOptions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nullable;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Supplier;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

public class ChromeWebDriverFactory extends CapableWebDriverFactory {

    private static final Logger log = LoggerFactory.getLogger(ChromeWebDriverFactory.class);

    private final CookiePreparer cookiePreparer;
    private final ImmutableList driverServiceBuilderConfigurators;

    @SuppressWarnings("unused")
    public ChromeWebDriverFactory() {
        this(builder());
    }

    protected ChromeWebDriverFactory(Builder builder) {
        super(builder);
        driverServiceBuilderConfigurators = ImmutableList.copyOf(builder.driverServiceBuilderConfigurators);
        cookiePreparer = builder.cookiePreparer;
    }

    public static Builder builder() {
        return new Builder();
    }

    @Override
    public WebdrivingSession startWebdriving(WebdrivingConfig config) throws IOException {
        ServicedSession session = createWebDriverMaybeWithProxy(config);
        return session;
    }

    private ServicedSession createWebDriverMaybeWithProxy(WebdrivingConfig config) throws IOException {
        ChromeOptions chromeOptions = new ChromeOptions();
        configureProxy(chromeOptions, config);
        cookiePreparer.supplementOptions(chromeOptions);
        ChromeDriverService.Builder serviceBuilder = createDriverServiceBuilder();
        serviceBuilder.withEnvironment(environmentSupplier.get());
        driverServiceBuilderConfigurators.forEach(configurator -> configurator.configure(serviceBuilder));
        modifyOptions(chromeOptions);
        ChromeDriverService service = serviceBuilder.build();
        final ChromeDriver driver;
        try {
            driver = new ChromeDriver(service, chromeOptions);
        } catch (WebDriverException e) { // on failure to start
            if (service.isRunning()) {
                log.warn("failed to construct ChromeDriver, but driver service is still running; trying to stop");
                try {
                    service.stop();
                } catch (RuntimeException e2) {
                    log.error("failed to stop driver service", e2);
                }
            }
            throw e;
        }
        cookiePreparer.prepareCookies(driver);
        return new ServicedSession(driver, service);
    }

    protected ChromeDriverService.Builder createDriverServiceBuilder() {
        return new ChromeDriverService.Builder().usingAnyFreePort();
    }

    @SuppressWarnings("unchecked")
    static List getArguments(ChromeOptions options) {
        Map json = options.toJson();
        Map googOptions = (Map) json.get("goog:chromeOptions");
        checkArgument(googOptions != null, "args not present in %s", json);
        return (List) googOptions.get("args");
    }

    /**
     * Parameterizes a capabilities instance according to a config instance. The certificate and key source
     * is not used because Chrome in webdriver mode appears to be fine with untrusted certificates,
     * which is good because we don't know how to install custom certificates.
     * @param options the options instance
     * @param config the config instance
     */
    protected void configureProxy(ChromeOptions options, WebdrivingConfig config) {
        @Nullable WebdrivingProxyDefinition proxySpecification = config.getProxySpecification();
        @Nullable org.openqa.selenium.Proxy seleniumProxy = null;
        if (proxySpecification != null) {
            seleniumProxy = maybeModifyBypassList(proxySpecification.createWebdrivingProxy());
        }
        options.setProxy(seleniumProxy);
    }

    /**
     * Adjusts the list of bypass patterns if necessary.
     *
     * Special attention is required when the bypass list does not include localhost (or any loopback address),
     * as noted here: https://bugs.chromium.org/p/chromium/issues/detail?id=899126#c18
     *
     * If no bypasses are specified -- meaning the proxy should not be bypassed even for localhost --
     * then we have to explicitly say so by actually want to specify is {@code <-loopback>}.
     * @return an adjusted proxy instance
     */
    @Nullable
    private org.openqa.selenium.Proxy maybeModifyBypassList(@Nullable org.openqa.selenium.Proxy proxy) {
        if (proxy == null) {
            return null;
        }
        if (Strings.isNullOrEmpty(proxy.getNoProxy())) {
            proxy.setNoProxy("<-loopback>");
        }
        return proxy;
    }

    /**
     * Interface for service classes that prepare cookies in a Chrome webdriver.
     */
    public interface CookiePreparer {
        /**
         * Parameterizes a given options instance with whatever is necessary to
         * prepare cookies in a Chrome browser instance. This is invoked prior to
         * instantiation of the webdriver.
         * @param options the options to parameterize
         * @throws IOException if I/O errors occur
         */
        void supplementOptions(ChromeOptions options) throws IOException;

        /**
         * Performs steps necessary to install cookies in a webdriver instance.
         * This is invoked after instantiation of the given driver but before
         * clients have access to it, that is, before it is returned by the
         * factory.
         * @param driver the webdriver
         * @throws WebDriverException should something go awry
         */
        void prepareCookies(ChromeDriver driver) throws WebDriverException;
    }

    private static final CookiePreparer cookielessPreparer = new CookiePreparer() {
        @Override
        public void supplementOptions(ChromeOptions options) {
            // no op
        }

        @Override
        public void prepareCookies(ChromeDriver driver) {
            // no op
        }
    };

    public static CookiePreparer makeCookieImplanter(Path scratchDir, Supplier> cookiesSupplier) {
        return new ChromeCookiePreparer(scratchDir, cookiesSupplier);
    }

    public interface DriverServiceBuilderConfigurator {
        void configure(ChromeDriverService.Builder builder);
    }

    @SuppressWarnings("unused")
    public static final class Builder extends CapableWebDriverFactoryBuilder {

        private CookiePreparer cookiePreparer = cookielessPreparer;
        private List driverServiceBuilderConfigurators = new ArrayList<>();

        private Builder() {
        }

        public Builder driverServiceBuilderConfigurator(DriverServiceBuilderConfigurator configurator) {
            driverServiceBuilderConfigurators.add(configurator);
            return this;
        }

        @Deprecated
        public Builder headless() {
            return headless(true);
        }

        /**
         * @deprecated use {@code configure(o -> o.setHeadless(true))} instead
         */
        @Deprecated
        public Builder headless(boolean headless) {
            return configure(o -> o.setHeadless(headless));
        }

        @Deprecated // for transition only
        public Builder chromeOptions(Consumer modifier) {
            return configure(modifier);
        }

        public Builder cookiePreparer(CookiePreparer val) {
            cookiePreparer = checkNotNull(val);
            return this;
        }

        public ChromeWebDriverFactory build() {
            return new ChromeWebDriverFactory(this);
        }

    }

}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy