org.openqa.selenium.firefox.FirefoxDriver.java Source code

Java tutorial

Introduction

Here is the source code for org.openqa.selenium.firefox.FirefoxDriver.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.firefox;

import static java.util.Collections.singletonMap;
import static org.openqa.selenium.remote.CapabilityType.PROXY;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

import org.openqa.selenium.Capabilities;
import org.openqa.selenium.ImmutableCapabilities;
import org.openqa.selenium.MutableCapabilities;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.Proxy;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.html5.LocalStorage;
import org.openqa.selenium.html5.SessionStorage;
import org.openqa.selenium.html5.WebStorage;
import org.openqa.selenium.remote.CommandInfo;
import org.openqa.selenium.remote.FileDetector;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.openqa.selenium.remote.Response;
import org.openqa.selenium.remote.html5.RemoteWebStorage;
import org.openqa.selenium.remote.http.HttpMethod;
import org.openqa.selenium.remote.service.DriverCommandExecutor;
import org.openqa.selenium.remote.service.DriverService;

import java.nio.file.Path;
import java.util.Objects;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.stream.StreamSupport;

/**
 * An implementation of the {#link WebDriver} interface that drives Firefox.
 * <p>
 * The best way to construct a {@code FirefoxDriver} with various options is to make use of the
 * {@link FirefoxOptions}, like so:
 *
 * <pre>
 * FirefoxOptions options = new FirefoxOptions()
 *     .addPreference("browser.startup.page", 1)
 *     .addPreference("browser.startup.homepage", "https://www.google.co.uk")
 *     .setAcceptInsecureCerts(true)
 *     .setHeadless(true);
 * WebDriver driver = new FirefoxDriver(options);
 * </pre>
 */
public class FirefoxDriver extends RemoteWebDriver implements WebStorage, HasExtensions {

    public static final class SystemProperty {

        /**
         * System property that defines the location of the Firefox executable file.
         */
        public static final String BROWSER_BINARY = "webdriver.firefox.bin";

        /**
         * System property that defines the location of the file where Firefox log should be stored.
         */
        public static final String BROWSER_LOGFILE = "webdriver.firefox.logfile";

        /**
         * System property that defines the additional library path (Linux only).
         */
        public static final String BROWSER_LIBRARY_PATH = "webdriver.firefox.library.path";

        /**
         * System property that defines the profile that should be used as a template.
         * When the driver starts, it will make a copy of the profile it is using,
         * rather than using that profile directly.
         */
        public static final String BROWSER_PROFILE = "webdriver.firefox.profile";

        /**
         * System property that defines the location of the webdriver.xpi browser extension to install
         * in the browser. If not set, the prebuilt extension bundled with this class will be used.
         */
        public static final String DRIVER_XPI_PROPERTY = "webdriver.firefox.driver";

        /**
         * Boolean system property that instructs FirefoxDriver to use Marionette backend,
         * overrides any capabilities specified by the user
         */
        public static final String DRIVER_USE_MARIONETTE = "webdriver.firefox.marionette";
    }

    public static final String BINARY = "firefox_binary";
    public static final String PROFILE = "firefox_profile";
    public static final String MARIONETTE = "marionette";

    private static class ExtraCommands {
        static String INSTALL_EXTENSION = "installExtension";
        static String UNINSTALL_EXTENSION = "uninstallExtension";
        static String FULL_PAGE_SCREENSHOT = "fullPageScreenshot";
    }

    private static final ImmutableMap<String, CommandInfo> EXTRA_COMMANDS = ImmutableMap.of(
            ExtraCommands.INSTALL_EXTENSION,
            new CommandInfo("/session/:sessionId/moz/addon/install", HttpMethod.POST),
            ExtraCommands.UNINSTALL_EXTENSION,
            new CommandInfo("/session/:sessionId/moz/addon/uninstall", HttpMethod.POST),
            ExtraCommands.FULL_PAGE_SCREENSHOT,
            new CommandInfo("/session/:sessionId/moz/screenshot/full", HttpMethod.GET));

    private static class FirefoxDriverCommandExecutor extends DriverCommandExecutor {
        public FirefoxDriverCommandExecutor(DriverService service) {
            super(service, EXTRA_COMMANDS);
        }
    }

    protected FirefoxBinary binary;
    private RemoteWebStorage webStorage;

    public FirefoxDriver() {
        this(new FirefoxOptions());
    }

    /**
     * @deprecated Use {@link #FirefoxDriver(FirefoxOptions)}.
     */
    @Deprecated
    public FirefoxDriver(Capabilities desiredCapabilities) {
        this(new FirefoxOptions(Objects.requireNonNull(desiredCapabilities, "No capabilities seen")));
    }

    /**
     * @deprecated Use {@link #FirefoxDriver(FirefoxDriverService, FirefoxOptions)}.
     */
    @Deprecated
    public FirefoxDriver(FirefoxDriverService service, Capabilities desiredCapabilities) {
        this(Objects.requireNonNull(service, "No driver service provided"),
                new FirefoxOptions(desiredCapabilities));
    }

    public FirefoxDriver(FirefoxOptions options) {
        this(toExecutor(options), options);
    }

    public FirefoxDriver(FirefoxDriverService service) {
        this(service, new FirefoxOptions());
    }

    public FirefoxDriver(FirefoxDriverService service, FirefoxOptions options) {
        this(new FirefoxDriverCommandExecutor(service), options);
    }

    private FirefoxDriver(FirefoxDriverCommandExecutor executor, FirefoxOptions options) {
        super(executor, dropCapabilities(options));
        webStorage = new RemoteWebStorage(getExecuteMethod());
    }

    private static FirefoxDriverCommandExecutor toExecutor(FirefoxOptions options) {
        Objects.requireNonNull(options, "No options to construct executor from");

        String sysProperty = System.getProperty(SystemProperty.DRIVER_USE_MARIONETTE);
        boolean isLegacy = (sysProperty != null && !Boolean.parseBoolean(sysProperty)) || options.isLegacy();

        FirefoxDriverService.Builder<?, ?> builder = StreamSupport
                .stream(ServiceLoader.load(DriverService.Builder.class).spliterator(), false)
                .filter(b -> b instanceof FirefoxDriverService.Builder)
                .map(FirefoxDriverService.Builder.class::cast).filter(b -> b.isLegacy() == isLegacy).findFirst()
                .orElseThrow(WebDriverException::new);

        return new FirefoxDriverCommandExecutor(builder.withOptions(options).build());
    }

    @Override
    public void setFileDetector(FileDetector detector) {
        throw new WebDriverException("Setting the file detector only works on remote webdriver instances obtained "
                + "via RemoteWebDriver");
    }

    @Override
    public LocalStorage getLocalStorage() {
        return webStorage.getLocalStorage();
    }

    @Override
    public SessionStorage getSessionStorage() {
        return webStorage.getSessionStorage();
    }

    private static boolean isLegacy(Capabilities desiredCapabilities) {
        Boolean forceMarionette = forceMarionetteFromSystemProperty();
        if (forceMarionette != null) {
            return !forceMarionette;
        }
        Object marionette = desiredCapabilities.getCapability(MARIONETTE);
        return marionette instanceof Boolean && !(Boolean) marionette;
    }

    @Override
    public String installExtension(Path path) {
        return (String) execute(ExtraCommands.INSTALL_EXTENSION,
                ImmutableMap.of("path", path.toAbsolutePath().toString(), "temporary", false)).getValue();
    }

    @Override
    public void uninstallExtension(String extensionId) {
        execute(ExtraCommands.UNINSTALL_EXTENSION, singletonMap("id", extensionId));
    }

    /**
     * Capture the full page screenshot and store it in the specified location.
     *
     * @param <X> Return type for getFullPageScreenshotAs.
     * @param outputType target type, @see OutputType
     * @return Object in which is stored information about the screenshot.
     * @throws WebDriverException on failure.
     */
    public <X> X getFullPageScreenshotAs(OutputType<X> outputType) throws WebDriverException {
        Response response = execute(ExtraCommands.FULL_PAGE_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", ExtraCommands.FULL_PAGE_SCREENSHOT,
                            result == null ? "null" : result.getClass().getName() + " instance"));
        }
    }

    private static Boolean forceMarionetteFromSystemProperty() {
        String useMarionette = System.getProperty(SystemProperty.DRIVER_USE_MARIONETTE);
        if (useMarionette == null) {
            return null;
        }
        return Boolean.valueOf(useMarionette);
    }

    /**
     * Drops capabilities that we shouldn't send over the wire.
     *
     * Used for capabilities which aren't BeanToJson-convertable, and are only used by the local
     * launcher.
     */
    private static Capabilities dropCapabilities(Capabilities capabilities) {
        if (capabilities == null) {
            return new ImmutableCapabilities();
        }

        MutableCapabilities caps;

        if (isLegacy(capabilities)) {
            final Set<String> toRemove = Sets.newHashSet(BINARY, PROFILE);
            caps = new MutableCapabilities(Maps.filterKeys(capabilities.asMap(), key -> !toRemove.contains(key)));
        } else {
            caps = new MutableCapabilities(capabilities);
        }

        // Ensure that the proxy is in a state fit to be sent to the extension
        Proxy proxy = Proxy.extractFrom(capabilities);
        if (proxy != null) {
            caps.setCapability(PROXY, proxy);
        }

        return caps;
    }
}