LibWebView: Load content blocker lists from settings
Add a content_blocking.list_paths advanced setting backed by a JSON array. This lets content blocker list paths avoid splitting one string. The about:settings editor presents array values as one path per line. It persists them as arrays. Load configured paths before command-line paths so temporary lists can still be appended for a single browser session.
This commit is contained in:
parent
a8a78100b3
commit
5fef343164
6 changed files with 101 additions and 12 deletions
|
|
@ -117,7 +117,8 @@
|
|||
input[type="search"],
|
||||
input[type="text"],
|
||||
input[type="url"],
|
||||
select {
|
||||
select,
|
||||
textarea {
|
||||
background-color: var(--input-background-color);
|
||||
|
||||
width: 100%;
|
||||
|
|
@ -319,10 +320,16 @@
|
|||
}
|
||||
|
||||
.config-control input[type="text"],
|
||||
.config-control input[type="number"] {
|
||||
.config-control input[type="number"],
|
||||
.config-control textarea {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.config-control textarea {
|
||||
min-width: 280px;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.config-row {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
|
|
|
|||
|
|
@ -24,6 +24,22 @@ function createControl(variable) {
|
|||
return control;
|
||||
}
|
||||
|
||||
if (variable.type === "array" && variable.elementType === "string") {
|
||||
const input = document.createElement("textarea");
|
||||
input.rows = 3;
|
||||
input.value = Array.isArray(variable.value) ? variable.value.join("\n") : "";
|
||||
input.addEventListener("change", () => {
|
||||
const value = input.value
|
||||
.split(/\r?\n/)
|
||||
.map(entry => entry.trim())
|
||||
.filter(entry => entry.length !== 0);
|
||||
|
||||
sendConfigVariableValue(variable, value);
|
||||
});
|
||||
control.append(input);
|
||||
return control;
|
||||
}
|
||||
|
||||
const input = document.createElement("input");
|
||||
input.value = variable.value ?? "";
|
||||
|
||||
|
|
|
|||
|
|
@ -333,8 +333,16 @@ ErrorOr<void> Application::initialize(Main::Arguments const& arguments)
|
|||
if (profile_process.has_value())
|
||||
profile_process_type = process_type_from_name(*profile_process);
|
||||
|
||||
auto configured_content_blocker_list_paths = m_settings.config_variable_as_string_array(ConfigVariableID::ContentBlockerListPaths);
|
||||
|
||||
Vector<ByteString> content_blocker_list_paths_as_byte_strings;
|
||||
TRY(content_blocker_list_paths_as_byte_strings.try_ensure_capacity(content_blocker_list_paths.size()));
|
||||
TRY(content_blocker_list_paths_as_byte_strings.try_ensure_capacity(configured_content_blocker_list_paths.size() + content_blocker_list_paths.size()));
|
||||
for (auto const& path : configured_content_blocker_list_paths) {
|
||||
if (path.is_empty())
|
||||
continue;
|
||||
|
||||
content_blocker_list_paths_as_byte_strings.unchecked_append(path.to_byte_string());
|
||||
}
|
||||
for (auto path : content_blocker_list_paths)
|
||||
content_blocker_list_paths_as_byte_strings.unchecked_append(path);
|
||||
|
||||
|
|
|
|||
|
|
@ -67,6 +67,15 @@ static Array<ConfigVariableDefinition, static_cast<size_t>(ConfigVariableID::Cou
|
|||
.title = "Show WebContent process ID in tab titles"sv,
|
||||
.description = "Append the active WebContent process ID to each tab title and tooltip."sv,
|
||||
.default_value = false,
|
||||
.array_element_type = {},
|
||||
},
|
||||
{
|
||||
.id = ConfigVariableID::ContentBlockerListPaths,
|
||||
.name = "content_blocking.list_paths"sv,
|
||||
.title = "Content blocker list paths"sv,
|
||||
.description = "Load content blocker lists from these filesystem paths on startup, in order."sv,
|
||||
.default_value = JsonArray {},
|
||||
.array_element_type = JsonValue::Type::String,
|
||||
},
|
||||
} };
|
||||
|
||||
|
|
@ -90,24 +99,49 @@ static ConfigVariableDefinition const& config_variable_definition(ConfigVariable
|
|||
return config_variable_definitions()[static_cast<size_t>(id)];
|
||||
}
|
||||
|
||||
static bool config_variable_value_matches_type(JsonValue const& default_value, JsonValue const& value)
|
||||
static bool json_value_matches_type(JsonValue const& value, JsonValue::Type type)
|
||||
{
|
||||
switch (default_value.type()) {
|
||||
switch (type) {
|
||||
case JsonValue::Type::Null:
|
||||
return value.is_null();
|
||||
case JsonValue::Type::Bool:
|
||||
return value.is_bool();
|
||||
case JsonValue::Type::Number:
|
||||
return value.is_number();
|
||||
case JsonValue::Type::String:
|
||||
return value.is_string();
|
||||
case JsonValue::Type::Null:
|
||||
case JsonValue::Type::Array:
|
||||
return value.is_array();
|
||||
case JsonValue::Type::Object:
|
||||
return value.type() == default_value.type();
|
||||
return value.is_object();
|
||||
}
|
||||
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
static bool json_array_contains_only_type(JsonArray const& array, JsonValue::Type type)
|
||||
{
|
||||
bool contains_only_type = true;
|
||||
|
||||
array.for_each([&](JsonValue const& value) {
|
||||
if (!json_value_matches_type(value, type))
|
||||
contains_only_type = false;
|
||||
});
|
||||
|
||||
return contains_only_type;
|
||||
}
|
||||
|
||||
static bool config_variable_value_is_valid(ConfigVariableDefinition const& variable, JsonValue const& value)
|
||||
{
|
||||
if (!json_value_matches_type(value, variable.default_value.type()))
|
||||
return false;
|
||||
|
||||
if (variable.default_value.is_array() && variable.array_element_type.has_value())
|
||||
return json_array_contains_only_type(value.as_array(), *variable.array_element_type);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Settings Settings::create(Badge<Application>)
|
||||
{
|
||||
// FIXME: Move this to a generic "Ladybird config directory" helper.
|
||||
|
|
@ -200,7 +234,7 @@ Settings Settings::create(Badge<Application>)
|
|||
if (auto config_variables = settings_json.value().get_object(CONFIG_VARIABLES_KEY); config_variables.has_value()) {
|
||||
for (auto const& variable : config_variable_definitions()) {
|
||||
if (auto value = config_variables->get(variable.name); value.has_value()) {
|
||||
if (config_variable_value_matches_type(variable.default_value, *value))
|
||||
if (config_variable_value_is_valid(variable, *value))
|
||||
settings.m_config_variables[static_cast<size_t>(variable.id)] = *value;
|
||||
}
|
||||
}
|
||||
|
|
@ -651,10 +685,29 @@ bool Settings::config_variable_as_bool(ConfigVariableID id) const
|
|||
return *value;
|
||||
}
|
||||
|
||||
Vector<String> Settings::config_variable_as_string_array(ConfigVariableID id) const
|
||||
{
|
||||
auto const& variable = config_variable_definition(id);
|
||||
VERIFY(variable.default_value.is_array());
|
||||
|
||||
auto const& value = config_variable(id);
|
||||
VERIFY(value.is_array());
|
||||
|
||||
Vector<String> values;
|
||||
values.ensure_capacity(value.as_array().size());
|
||||
|
||||
value.as_array().for_each([&](JsonValue const& entry) {
|
||||
if (entry.is_string())
|
||||
values.append(entry.as_string());
|
||||
});
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
void Settings::set_config_variable(ConfigVariableID id, JsonValue value)
|
||||
{
|
||||
auto const& variable = config_variable_definition(id);
|
||||
if (!config_variable_value_matches_type(variable.default_value, value))
|
||||
if (!config_variable_value_is_valid(variable, value))
|
||||
return;
|
||||
|
||||
if (m_config_variables[static_cast<size_t>(id)].equals(value))
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ enum class GlobalPrivacyControl {
|
|||
|
||||
enum class ConfigVariableID : u8 {
|
||||
ShowWebContentProcessIDInTabTitle,
|
||||
ContentBlockerListPaths,
|
||||
Count,
|
||||
};
|
||||
|
||||
|
|
@ -54,6 +55,7 @@ struct ConfigVariableDefinition {
|
|||
StringView title;
|
||||
StringView description;
|
||||
JsonValue default_value;
|
||||
Optional<JsonValue::Type> array_element_type;
|
||||
};
|
||||
|
||||
WEBVIEW_API ReadonlySpan<ConfigVariableDefinition const> config_variable_definitions();
|
||||
|
|
@ -134,6 +136,7 @@ public:
|
|||
|
||||
JsonValue const& config_variable(ConfigVariableID) const;
|
||||
bool config_variable_as_bool(ConfigVariableID) const;
|
||||
Vector<String> config_variable_as_string_array(ConfigVariableID) const;
|
||||
void set_config_variable(ConfigVariableID, JsonValue);
|
||||
void set_config_variable(StringView name, JsonValue const&);
|
||||
|
||||
|
|
|
|||
|
|
@ -12,9 +12,9 @@
|
|||
|
||||
namespace WebView {
|
||||
|
||||
static StringView config_variable_type_to_string(JsonValue const& value)
|
||||
static StringView config_variable_type_to_string(JsonValue::Type type)
|
||||
{
|
||||
switch (value.type()) {
|
||||
switch (type) {
|
||||
case JsonValue::Type::Null:
|
||||
return "null"sv;
|
||||
case JsonValue::Type::Bool:
|
||||
|
|
@ -114,7 +114,9 @@ void SettingsUI::load_current_settings()
|
|||
variable_object.set("name"sv, variable.name);
|
||||
variable_object.set("title"sv, variable.title);
|
||||
variable_object.set("description"sv, variable.description);
|
||||
variable_object.set("type"sv, config_variable_type_to_string(variable.default_value));
|
||||
variable_object.set("type"sv, config_variable_type_to_string(variable.default_value.type()));
|
||||
if (variable.array_element_type.has_value())
|
||||
variable_object.set("elementType"sv, config_variable_type_to_string(*variable.array_element_type));
|
||||
variable_object.set("defaultValue"sv, variable.default_value);
|
||||
variable_object.set("value"sv, WebView::Application::settings().config_variable(variable.id));
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue