chromium/android_webview/java/src/org/chromium/android_webview/common/FlagOverrideHelper.java

// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.android_webview.common;

import android.text.TextUtils;

import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;

import org.chromium.base.CommandLine;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Helper class to apply multiple features/flags to the global CommandLine singleton. This class and
 * its static methods assume the CommandLine has already been initialized.
 */
public class FlagOverrideHelper {
    private Map<String, Flag> mFlagMap = new HashMap<>();

    public FlagOverrideHelper(Flag[] flagList) {
        for (Flag flag : flagList) {
            mFlagMap.put(flag.getName(), flag);
        }
    }

    @VisibleForTesting
    public static List<String> getCommaDelimitedSwitchValue(String name) {
        return CommandLine.getInstance().hasSwitch(name)
                ? Arrays.asList(CommandLine.getInstance().getSwitchValue(name).split(","))
                : new ArrayList<>();
    }

    @VisibleForTesting
    public static void setCommaDelimitedSwitchValue(String name, @NonNull List<String> value) {
        if (value.isEmpty()) {
            CommandLine.getInstance().removeSwitch(name);
        } else {
            CommandLine.getInstance().appendSwitchWithValue(name, TextUtils.join(",", value));
        }
    }

    /**
     * Apply the flag overrides specified in {@code overrides} to the {@link CommandLine} singleton.
     * The flag names in {@code overrides} must have been already declared in the {@code flagList}
     * passed to this instance's constructor.
     *
     * @param overrides a Map of flag names to override state (enabled or disabled).
     */
    public void applyFlagOverrides(Map<String, Boolean> overrides) {
        Set<String> enabledFeatures = new HashSet<>();
        Set<String> disabledFeatures = new HashSet<>();
        enabledFeatures.addAll(getCommaDelimitedSwitchValue("enable-features"));
        disabledFeatures.addAll(getCommaDelimitedSwitchValue("disable-features"));

        for (Map.Entry<String, Boolean> entry : overrides.entrySet()) {
            Flag flag = getFlagForName(entry.getKey());
            boolean enabled = entry.getValue();
            if (flag.isBaseFeature()) {
                if (enabled) {
                    enabledFeatures.add(flag.getName());
                    disabledFeatures.remove(flag.getName());
                } else {
                    enabledFeatures.remove(flag.getName());
                    disabledFeatures.add(flag.getName());
                }
            } else {
                if (enabled && flag.getEnabledStateValue() != null) {
                    CommandLine.getInstance()
                            .appendSwitchWithValue(flag.getName(), flag.getEnabledStateValue());
                } else if (enabled) {
                    CommandLine.getInstance().appendSwitch(flag.getName());
                } else {
                    CommandLine.getInstance().removeSwitch(flag.getName());
                }
            }
        }

        setCommaDelimitedSwitchValue("enable-features", new ArrayList<>(enabledFeatures));
        setCommaDelimitedSwitchValue("disable-features", new ArrayList<>(disabledFeatures));
    }

    /**
     * Fetches a {@link Flag} corresponding to {@code name}, based on the Flag array passed to the
     * constructor.
     *
     * @param name the name of the {@link Flag} to look up.
     * @return the desired {@link Flag}.
     * @throws RuntimeException if this cannot find {@code name} in the list.
     */
    public Flag getFlagForName(@NonNull String name) {
        if (mFlagMap.containsKey(name)) {
            return mFlagMap.get(name);
        }
        // This should not be reached.
        throw new RuntimeException("Unable to find flag '" + name + "' in the list.");
    }
}