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

Java tutorial

Introduction

Here is the source code for org.openqa.selenium.firefox.Preferences.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 com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Maps;
import com.google.common.io.CharStreams;
import com.google.common.io.LineReader;

import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.io.IOUtils;
import org.openqa.selenium.remote.JsonToBeanConverter;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

class Preferences {

    /**
     * The maximum amount of time scripts should be permitted to run. The user may increase this
     * timeout, but may not set it below the default value.
     */
    private static final String MAX_SCRIPT_RUN_TIME_KEY = "dom.max_script_run_time";
    private static final int DEFAULT_MAX_SCRIPT_RUN_TIME = 30;

    /**
     * This pattern is used to parse preferences in user.js. It is intended to match all preference
     * lines in the format generated by Firefox; it won't necessarily match all possible lines that
     * Firefox will parse.
     *
     * e.g. if you have a line with extra spaces after the end-of-line semicolon, this pattern will
     * not match that line because Firefox never generates lines like that.
     */
    private static final Pattern PREFERENCE_PATTERN = Pattern.compile("user_pref\\(\"([^\"]+)\", (\"?.+?\"?)\\);");

    private Map<String, Object> immutablePrefs = Maps.newHashMap();
    private Map<String, Object> allPrefs = Maps.newHashMap();

    public Preferences(Reader defaults) {
        readDefaultPreferences(defaults);
    }

    public Preferences(Reader defaults, File userPrefs) {
        readDefaultPreferences(defaults);
        FileReader reader = null;
        try {
            reader = new FileReader(userPrefs);
            readPreferences(reader);
        } catch (IOException e) {
            throw new WebDriverException(e);
        } finally {
            IOUtils.closeQuietly(reader);
        }
    }

    public Preferences(Reader defaults, Reader reader) {
        readDefaultPreferences(defaults);
        try {
            readPreferences(reader);
        } catch (IOException e) {
            throw new WebDriverException(e);
        } finally {
            IOUtils.closeQuietly(reader);
        }
    }

    private void readDefaultPreferences(Reader defaultsReader) {
        try {
            String rawJson = CharStreams.toString(defaultsReader);
            Map<String, Object> map = new JsonToBeanConverter().convert(Map.class, rawJson);

            Map<String, Object> frozen = (Map<String, Object>) map.get("frozen");
            for (Map.Entry<String, Object> entry : frozen.entrySet()) {
                String key = entry.getKey();
                Object value = entry.getValue();
                if (value instanceof Long) {
                    value = new Integer(((Long) value).intValue());
                }
                setPreference(key, value);
                immutablePrefs.put(key, value);
            }

            Map<String, Object> mutable = (Map<String, Object>) map.get("mutable");
            for (Map.Entry<String, Object> entry : mutable.entrySet()) {
                Object value = entry.getValue();
                if (value instanceof Long) {
                    value = new Integer(((Long) value).intValue());
                }
                setPreference(entry.getKey(), value);
            }
        } catch (IOException e) {
            throw new WebDriverException(e);
        }
    }

    private void setPreference(String key, Object value) {
        if (value instanceof String) {
            setPreference(key, (String) value);
        } else if (value instanceof Boolean) {
            setPreference(key, ((Boolean) value).booleanValue());
        } else {
            setPreference(key, ((Number) value).intValue());
        }
    }

    private void readPreferences(Reader reader) throws IOException {
        LineReader allLines = new LineReader(reader);
        String line = allLines.readLine();
        while (line != null) {
            Matcher matcher = PREFERENCE_PATTERN.matcher(line);
            if (matcher.matches()) {
                allPrefs.put(matcher.group(1), preferenceAsValue(matcher.group(2)));
            }
            line = allLines.readLine();
        }
    }

    public void setPreference(String key, String value) {
        checkPreference(key, value);
        if (isStringified(value)) {
            throw new IllegalArgumentException(
                    String.format("Preference values must be plain strings: %s: %s", key, value));
        }
        allPrefs.put(key, value);
    }

    public void setPreference(String key, boolean value) {
        checkPreference(key, value);
        allPrefs.put(key, value);
    }

    public void setPreference(String key, int value) {
        checkPreference(key, value);
        allPrefs.put(key, value);
    }

    public void addTo(Preferences prefs) {
        // TODO(simon): Stop being lazy
        prefs.allPrefs.putAll(allPrefs);
    }

    public void addTo(FirefoxProfile profile) {
        profile.getAdditionalPreferences().allPrefs.putAll(allPrefs);
    }

    public void writeTo(Writer writer) throws IOException {
        for (Map.Entry<String, Object> pref : allPrefs.entrySet()) {
            writer.append("user_pref(\"").append(pref.getKey()).append("\", ");
            writer.append(valueAsPreference(pref.getValue()));
            writer.append(");\n");
        }
    }

    private String valueAsPreference(Object value) {
        if (value instanceof String) {
            return "\"" + escapeValueAsPreference((String) value) + "\"";
        } else {
            return escapeValueAsPreference(String.valueOf(value));
        }

    }

    private String escapeValueAsPreference(String value) {
        return value.replaceAll("\\\\", "\\\\\\\\").replaceAll("\"", "\\\\\"");
    }

    private Object preferenceAsValue(String toConvert) {
        if (toConvert.startsWith("\"") && toConvert.endsWith("\"")) {
            return toConvert.substring(1, toConvert.length() - 1).replaceAll("\\\\\\\\", "\\\\");
        }

        if ("false".equals(toConvert) || "true".equals(toConvert)) {
            return Boolean.parseBoolean(toConvert);
        }

        try {
            return Integer.parseInt(toConvert);
        } catch (NumberFormatException e) {
            throw new WebDriverException(e);
        }
    }

    @VisibleForTesting
    protected Object getPreference(String key) {
        return allPrefs.get(key);
    }

    private boolean isStringified(String value) {
        // Assume we a string is stringified (i.e. wrapped in " ") when
        // the first character == " and the last character == "
        return value.startsWith("\"") && value.endsWith("\"");
    }

    public void putAll(Map<String, Object> frozenPreferences) {
        allPrefs.putAll(frozenPreferences);
    }

    private void checkPreference(String key, Object value) {
        checkNotNull(value);
        checkArgument(
                !immutablePrefs.containsKey(key)
                        || (immutablePrefs.containsKey(key) && value.equals(immutablePrefs.get(key))),
                "Preference %s may not be overridden: frozen value=%s, requested value=%s", key,
                immutablePrefs.get(key), value);
        if (MAX_SCRIPT_RUN_TIME_KEY.equals(key)) {
            int n;
            if (value instanceof String) {
                n = Integer.parseInt((String) value);
            } else if (value instanceof Integer) {
                n = (Integer) value;
            } else {
                throw new IllegalArgumentException(String.format("%s value must be a number: %s",
                        MAX_SCRIPT_RUN_TIME_KEY, value.getClass().getName()));
            }
            checkArgument(n == 0 || n >= DEFAULT_MAX_SCRIPT_RUN_TIME, "%s must be == 0 || >= %s",
                    MAX_SCRIPT_RUN_TIME_KEY, DEFAULT_MAX_SCRIPT_RUN_TIME);
        }
    }

}