Welcome to the new Parasoft forums! We hope you will enjoy the site and try out some of the new features, like sharing an idea you may have for one of our products or following a category.

Using Selenium with Firefox Snap (Ubuntu)

benken_parasoft
benken_parasoft Posts: 1,290 ✭✭✭

Ubuntu now provides Firefox as a Snap package. The WebDriver for Firefox (geckodriver) is not compatible out-of-box. From the release notes:

Using geckodriver to launch Firefox inside a sandbox -- for example a Firefox distribution using Snap or Flatpak -- can fail with a "Profile not found" error if the sandbox restricts Firefox's ability to access the system temporary directory. geckodriver uses the temporary directory to store Firefox profiles created during the run.

They provide a work around:

This issue can now be worked around by using the --profile-root command line option or setting the TMPDIR environment variable to a location that both Firefox and geckodriver have read/write access

How do you pass --profile-root to geckodriver from Selenium? It isn't trivial but here's one way to do it:

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxOptions;
import org.openqa.selenium.firefox.GeckoDriverService;

public final class DriverUtil {

    private DriverUtil() {
    }

    public static FirefoxDriver getFirefoxDriver() {
        FirefoxOptions options = new FirefoxOptions();
        String osName = System.getProperty("os.name");
        String profileRoot = osName.contains("Linux") && new File("/snap/firefox").exists()
                ? createProfileRootInUserHome()
                : null;
        return profileRoot != null
                ? new FirefoxDriver(createGeckoDriverService(profileRoot), options)
                : new FirefoxDriver(options);
    }

    private static String createProfileRootInUserHome() {
        String userHome = System.getProperty("user.home");
        File profileRoot = new File(userHome, "snap/firefox/common/.firefox-profile-root");
        if (!profileRoot.exists()) {
            if (!profileRoot.mkdirs()) {
                return null;
            }
        }
        return profileRoot.getAbsolutePath();
    }

    private static GeckoDriverService createGeckoDriverService(String tempProfileDir) {
        return new GeckoDriverService.Builder() {
            @Override
            protected List<String> createArgs() {
                List<String> args = new ArrayList<>(super.createArgs());
                args.add(String.format("--profile-root=%s", tempProfileDir));
                return args;
            }
        }.build();
    }
}

The above example uses "~/snap/firefox/common/.firefox-profile-root" as the profile root but you can choose some other directory under "~/snap/firefox/common/" as desired.

Comments

  • benken_parasoft
    benken_parasoft Posts: 1,290 ✭✭✭
    edited September 2023

    Another workaround from Running Firefox in a container-based package:

    Use a geckodriver that runs in the same container filesystem as the Firefox package. For example on Ubuntu /snap/bin/geckodriver will work with the default Firefox.

    However, making this work involves overriding File.getCanonicalPath() as described here:
    https://github.com/SeleniumHQ/selenium/issues/7788

    Example:

        public static FirefoxDriver getFirefoxDriver() {
            FirefoxOptions options = new FirefoxOptions();
            GeckoDriverService service = null;
            File snapGeckoDriver = new File("/snap/bin/geckodriver") {
                // https://github.com/SeleniumHQ/selenium/issues/7788
                @Override
                public String getCanonicalPath() throws IOException {
                    return getAbsolutePath();
                }
            };
            if (snapGeckoDriver.exists()) {
                service = new GeckoDriverService.Builder().usingDriverExecutable(snapGeckoDriver).build();
            }
            return service != null ? new FirefoxDriver(service, options) : new FirefoxDriver(options);
        }