Java tutorial
// 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 com.google.common.annotations.VisibleForTesting; import com.google.common.io.Resources; import org.openqa.selenium.Beta; import org.openqa.selenium.WebDriverException; import org.openqa.selenium.io.FileHandler; import org.openqa.selenium.io.TemporaryFilesystem; import org.openqa.selenium.io.Zip; import org.openqa.selenium.json.Json; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; import java.io.StringReader; import java.net.URL; import java.util.HashMap; import java.util.Map; public class FirefoxProfile { public static final String PORT_PREFERENCE = "webdriver_firefox_port"; public static final String ALLOWED_HOSTS_PREFERENCE = "webdriver_firefox_allowed_hosts"; private static final String defaultPrefs = "/org/openqa/selenium/firefox/webdriver_prefs.json"; private Preferences additionalPrefs; private Map<String, Extension> extensions = new HashMap<>(); private boolean loadNoFocusLib; private boolean acceptUntrustedCerts; private boolean untrustedCertIssuer; private File model; private static final String ACCEPT_UNTRUSTED_CERTS_PREF = "webdriver_accept_untrusted_certs"; private static final String ASSUME_UNTRUSTED_ISSUER_PREF = "webdriver_assume_untrusted_issuer"; public FirefoxProfile() { this(null); } /** * Constructs a firefox profile from an existing profile directory. * <p> * Users who need this functionality should consider using a named profile. * * @param profileDir The profile directory to use as a model. */ public FirefoxProfile(File profileDir) { this(null, profileDir); } @VisibleForTesting @Beta protected FirefoxProfile(Reader defaultsReader, File profileDir) { if (defaultsReader == null) { defaultsReader = onlyOverrideThisIfYouKnowWhatYouAreDoing(); } additionalPrefs = new Preferences(defaultsReader); model = profileDir; verifyModel(model); File prefsInModel = new File(model, "user.js"); if (prefsInModel.exists()) { StringReader reader = new StringReader("{\"frozen\": {}, \"mutable\": {}}"); Preferences existingPrefs = new Preferences(reader, prefsInModel); acceptUntrustedCerts = getBooleanPreference(existingPrefs, ACCEPT_UNTRUSTED_CERTS_PREF, true); untrustedCertIssuer = getBooleanPreference(existingPrefs, ASSUME_UNTRUSTED_ISSUER_PREF, true); existingPrefs.addTo(additionalPrefs); } else { acceptUntrustedCerts = true; untrustedCertIssuer = true; } // This is not entirely correct but this is not stored in the profile // so for now will always be set to false. loadNoFocusLib = false; try { defaultsReader.close(); } catch (IOException e) { throw new WebDriverException(e); } } /** * <strong>Internal method. This is liable to change at a moment's notice.</strong> * * @return InputStreamReader of the default firefox profile preferences */ @Beta protected Reader onlyOverrideThisIfYouKnowWhatYouAreDoing() { URL resource = Resources.getResource(FirefoxProfile.class, defaultPrefs); try { return new InputStreamReader(resource.openStream()); } catch (IOException e) { throw new WebDriverException(e); } } private boolean getBooleanPreference(Preferences prefs, String key, boolean defaultValue) { Object value = prefs.getPreference(key); if (value == null) { return defaultValue; } if (value instanceof Boolean) { return (Boolean) value; } throw new WebDriverException("Expected boolean value is not a boolean. It is: " + value); } public String getStringPreference(String key, String defaultValue) { Object preference = additionalPrefs.getPreference(key); if (preference instanceof String) { return (String) preference; } return defaultValue; } public int getIntegerPreference(String key, int defaultValue) { Object preference = additionalPrefs.getPreference(key); if (preference instanceof Integer) { return (Integer) preference; } return defaultValue; } public boolean getBooleanPreference(String key, boolean defaultValue) { Object preference = additionalPrefs.getPreference(key); if (preference instanceof Boolean) { return (Boolean) preference; } return defaultValue; } private void verifyModel(File model) { if (model == null) { return; } if (!model.exists()) { throw new UnableToCreateProfileException( "Given model profile directory does not exist: " + model.getPath()); } if (!model.isDirectory()) { throw new UnableToCreateProfileException( "Given model profile directory is not a directory: " + model.getAbsolutePath()); } } public boolean containsWebDriverExtension() { return extensions.containsKey("webdriver"); } public void addExtension(Class<?> loadResourcesUsing, String loadFrom) { // Is loadFrom a file? File file = new File(loadFrom); if (file.exists()) { addExtension(file); return; } addExtension(loadFrom, new ClasspathExtension(loadResourcesUsing, loadFrom)); } /** * Attempt to add an extension to install into this instance. * * @param extensionToInstall File pointing to the extension */ public void addExtension(File extensionToInstall) { addExtension(extensionToInstall.getName(), new FileExtension(extensionToInstall)); } public void addExtension(String key, Extension extension) { String name = deriveExtensionName(key); extensions.put(name, extension); } private String deriveExtensionName(String originalName) { String[] pieces = originalName.replace('\\', '/').split("/"); String name = pieces[pieces.length - 1]; name = name.replaceAll("\\..*?$", ""); return name; } public void setPreference(String key, Object value) { additionalPrefs.setPreference(key, value); } protected Preferences getAdditionalPreferences() { return additionalPrefs; } public void updateUserPrefs(File userPrefs) { Preferences prefs = new Preferences(onlyOverrideThisIfYouKnowWhatYouAreDoing()); // Allow users to override these settings prefs.setPreference("browser.startup.homepage", "about:blank"); // The user must be able to override this setting (to 1) in order to // to change homepage on Firefox 3.0 prefs.setPreference("browser.startup.page", 0); if (userPrefs.exists()) { prefs = new Preferences(onlyOverrideThisIfYouKnowWhatYouAreDoing(), userPrefs); if (!userPrefs.delete()) { throw new WebDriverException("Cannot delete existing user preferences"); } } additionalPrefs.addTo(prefs); // Should we accept untrusted certificates or not? prefs.setPreference(ACCEPT_UNTRUSTED_CERTS_PREF, acceptUntrustedCerts); prefs.setPreference(ASSUME_UNTRUSTED_ISSUER_PREF, untrustedCertIssuer); // If the user sets the home page, we should also start up there Object homePage = prefs.getPreference("browser.startup.homepage"); if (homePage instanceof String) { prefs.setPreference("startup.homepage_welcome_url", ""); } if (!"about:blank".equals(prefs.getPreference("browser.startup.homepage"))) { prefs.setPreference("browser.startup.page", 1); } try (FileWriter writer = new FileWriter(userPrefs)) { prefs.writeTo(writer); } catch (IOException e) { throw new WebDriverException(e); } } protected void deleteLockFiles(File profileDir) { File macAndLinuxLockFile = new File(profileDir, ".parentlock"); File windowsLockFile = new File(profileDir, "parent.lock"); macAndLinuxLockFile.delete(); windowsLockFile.delete(); } public void deleteExtensionsCacheIfItExists(File profileDir) { File cacheFile = new File(profileDir, "extensions.cache"); if (cacheFile.exists()) { cacheFile.delete(); } } /** * Returns whether the no focus library should be loaded for Firefox profiles launched on Linux, * even if native events are disabled. * * @return Whether the no focus library should always be loaded for Firefox on Linux. */ public boolean shouldLoadNoFocusLib() { return loadNoFocusLib; } /** * Sets whether the no focus library should always be loaded on Linux. * * @param loadNoFocusLib Whether to always load the no focus library. */ public void setAlwaysLoadNoFocusLib(boolean loadNoFocusLib) { this.loadNoFocusLib = loadNoFocusLib; } /** * Sets whether Firefox should accept SSL certificates which have expired, signed by an unknown * authority or are generally untrusted. This is set to true by default. * * @param acceptUntrustedSsl Whether untrusted SSL certificates should be accepted. */ public void setAcceptUntrustedCertificates(boolean acceptUntrustedSsl) { this.acceptUntrustedCerts = acceptUntrustedSsl; } /** * By default, when accepting untrusted SSL certificates, assume that these certificates will come * from an untrusted issuer or will be self signed. Due to limitation within Firefox, it is easy * to find out if the certificate has expired or does not match the host it was served for, but * hard to find out if the issuer of the certificate is untrusted. * <p> * By default, it is assumed that the certificates were not be issued from a trusted CA. * <p> * If you are receive an "untrusted site" prompt on Firefox when using a certificate that was * issued by valid issuer, but has expired or is being served served for a different host (e.g. * production certificate served in a testing environment) set this to false. * * @param untrustedIssuer whether to assume untrusted issuer or not. */ public void setAssumeUntrustedCertificateIssuer(boolean untrustedIssuer) { this.untrustedCertIssuer = untrustedIssuer; } public void clean(File profileDir) { TemporaryFilesystem.getDefaultTmpFS().deleteTempDir(profileDir); } public String toJson() throws IOException { File file = layoutOnDisk(); try { return Zip.zip(file); } finally { clean(file); } } public static FirefoxProfile fromJson(String json) throws IOException { // We used to just pass in the entire string without quotes. If we see that, we're good. // Otherwise, parse the json. if (json.trim().startsWith("\"")) { json = new Json().toType(json, String.class); } return new FirefoxProfile(Zip.unzipToTempDir(json, "webdriver", "duplicated")); } public void cleanTemporaryModel() { clean(model); } public void checkForChangesInFrozenPreferences() { additionalPrefs.checkForChangesInFrozenPreferences(); } /** * Call this to cause the current profile to be written to disk. The profile directory is * returned. Note that this profile directory is a temporary one and will be deleted when the JVM * exists (at the latest) * * This method should be called immediately before starting to use the profile and should only be * called once per instance of the {@link org.openqa.selenium.firefox.FirefoxDriver}. * * @return The directory containing the profile. */ public File layoutOnDisk() { try { File profileDir = TemporaryFilesystem.getDefaultTmpFS().createTempDir("anonymous", "webdriver-profile"); File userPrefs = new File(profileDir, "user.js"); copyModel(model, profileDir); installExtensions(profileDir); deleteLockFiles(profileDir); deleteExtensionsCacheIfItExists(profileDir); updateUserPrefs(userPrefs); return profileDir; } catch (IOException e) { throw new UnableToCreateProfileException(e); } } protected void copyModel(File sourceDir, File profileDir) throws IOException { if (sourceDir == null || !sourceDir.exists()) { return; } FileHandler.copy(sourceDir, profileDir); } protected void installExtensions(File parentDir) throws IOException { File extensionsDir = new File(parentDir, "extensions"); for (Extension extension : extensions.values()) { extension.writeTo(extensionsDir); } } }