Skip to content

Commit

Permalink
[GLib] Support for loading websettings from a config file
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=273043

Reviewed by Michael Catanzaro, Carlos Garcia Campos and Adrian Perez de Castro.

New WebKitSettings API allowing to apply settings declared in a .ini style file. Based on a similar
feature implemented in Cog by Adrian Perez de Castro <aperez@igalia.com>.

* Source/WTF/wtf/glib/GUniquePtr.h:
* Source/WebKit/UIProcess/API/glib/WebKitSettings.cpp:
(webkit_settings_apply_from_key_file):
* Source/WebKit/UIProcess/API/glib/WebKitSettings.h.in:
* Tools/MiniBrowser/gtk/main.c:
(main):
* Tools/TestWebKitAPI/Tests/WebKitGLib/TestWebKitSettings.cpp:
(testWebKitSettingsApplyFromConfigFile):
(beforeAll):

Canonical link: https://commits.webkit.org/278688@main
  • Loading branch information
philn committed May 13, 2024
1 parent 8f767ad commit 4d653b6
Show file tree
Hide file tree
Showing 4 changed files with 213 additions and 0 deletions.
121 changes: 121 additions & 0 deletions Source/WebKit/UIProcess/API/glib/WebKitSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4131,3 +4131,124 @@ WebKitFeatureList* webkit_settings_get_development_features(void)
{
return webkitFeatureListCreate(WebPreferences::internalDebugFeatures());
}

/**
* webkit_settings_apply_from_key_file:
* @settings: a #WebKitSettings
* @key_file: a #GKeyFile
* @group_name: Name of the group to read from @key_file
* @error: return location for error or %NULL to ignore
*
* Reads the contents of the given @group_name from the given @key_file and apply the value of
* each key/value to the corresponding property on the @settings.
*
* Value types have to match with the corresponding setting property type and the group keys have to
* match existing setting property names. If those conditions are not met, the function will return
* %FALSE.
*
* Supported value types are strings (unquoted), booleans (0, 1, true, false) and unsigned integers.
*
* Returns: %TRUE if the settings were correctly applied or %FALSE on error.
*
* Since: 2.46
*/
gboolean webkit_settings_apply_from_key_file(WebKitSettings* settings, GKeyFile* keyFile, const gchar* groupName, GError** error)
{
g_return_val_if_fail(WEBKIT_IS_SETTINGS(settings), FALSE);
g_return_val_if_fail(keyFile, FALSE);
g_return_val_if_fail(groupName, FALSE);
g_return_val_if_fail(!error || !*error, FALSE);

if (!g_key_file_has_group(keyFile, groupName)) {
g_set_error(error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE, "The key file has no %s group", groupName);
return FALSE;
}

auto klass = G_OBJECT_GET_CLASS(settings);
unsigned totalProperties = 0;
GUniquePtr<GParamSpec*> properties(g_object_class_list_properties(klass, &totalProperties));

GRefPtr<GPtrArray> propertyNames = adoptGRef(g_ptr_array_sized_new(totalProperties));
GRefPtr<GArray> values = adoptGRef(g_array_sized_new(FALSE, FALSE, sizeof(GValue), totalProperties));
g_array_set_clear_func(values.get(), reinterpret_cast<GDestroyNotify>(g_value_unset));

for (unsigned i = 0; i < totalProperties; i++) {
GUniqueOutPtr<GError> lookupError;
const char* name = properties.get()[i]->name;
if (!g_key_file_has_key(keyFile, groupName, name, &lookupError.outPtr())) {
if (lookupError) {
g_propagate_error(error, lookupError.release());
return FALSE;
}
continue; // Setting missing in GKeyFile, skip it.
}

GValue value = G_VALUE_INIT;
bool isValueSet = false;
switch (G_PARAM_SPEC_VALUE_TYPE(properties.get()[i])) {
case G_TYPE_BOOLEAN: {
bool boolValue = g_key_file_get_boolean(keyFile, groupName, name, &lookupError.outPtr());
if (!boolValue && lookupError) {
g_propagate_error(error, lookupError.release());
return FALSE;
}
g_value_init(&value, G_TYPE_BOOLEAN);
g_value_set_boolean(&value, boolValue);
isValueSet = true;
break;
}
case G_TYPE_UINT: {
guint64 uintValue = g_key_file_get_uint64(keyFile, groupName, name, &lookupError.outPtr());
if (!uintValue && lookupError) {
g_propagate_error(error, lookupError.release());
return FALSE;
}
if (uintValue > G_MAXUINT) {
g_set_error(error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE, "Value for '%s' exceeds maximum integer size", name);
return FALSE;
}
g_value_init(&value, G_TYPE_UINT);
g_value_set_uint(&value, static_cast<guint>(uintValue));
isValueSet = true;
break;
}
case G_TYPE_STRING: {
GUniquePtr<char> stringValue(g_key_file_get_string(keyFile, groupName, name, &lookupError.outPtr()));
if (!stringValue) {
g_assert(lookupError);
g_propagate_error(error, lookupError.release());
return FALSE;
}
g_value_init(&value, G_TYPE_STRING);
g_value_take_string(&value, stringValue.release());
isValueSet = true;
break;
}
default:
break;
}
if (isValueSet) {
g_ptr_array_add(propertyNames.get(), const_cast<char*>(name));
g_array_append_val(values.get(), value);
}
}

size_t length;
GUniqueOutPtr<GError> getKeysError;
GUniquePtr<char*> allKeys(g_key_file_get_keys(keyFile, groupName, &length, &getKeysError.outPtr()));
if (UNLIKELY(getKeysError)) {
g_propagate_error(error, getKeysError.release());
return FALSE;
}

for (unsigned i = 0; i < length; i++) {
auto key = allKeys.get()[i];
if (!g_ptr_array_find_with_equal_func(propertyNames.get(), static_cast<gconstpointer>(key), g_str_equal, nullptr)) {
g_set_error(error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE, "The %s group contains an invalid setting: %s", groupName, key);
return FALSE;
}
}

g_object_setv(G_OBJECT(settings), propertyNames->len, const_cast<const char**>(reinterpret_cast<char**>(propertyNames->pdata)), reinterpret_cast<GValue*>(values->data));
return TRUE;
}
6 changes: 6 additions & 0 deletions Source/WebKit/UIProcess/API/glib/WebKitSettings.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,12 @@ webkit_settings_get_experimental_features (void);
WEBKIT_API WebKitFeatureList *
webkit_settings_get_development_features (void);

WEBKIT_API gboolean
webkit_settings_apply_from_key_file (WebKitSettings *settings,
GKeyFile *key_file,
const gchar *group_name,
GError **error);

G_END_DECLS

#endif /* WebKitSettings_h */
22 changes: 22 additions & 0 deletions Tools/MiniBrowser/gtk/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ static gboolean enableITP;
static gboolean exitAfterLoad;
static gboolean webProcessCrashed;
static gboolean printVersion;
static char *configFile;

#if !GTK_CHECK_VERSION(3, 98, 0)
static gboolean enableSandbox;
Expand Down Expand Up @@ -179,6 +180,7 @@ static const GOptionEntry commandLineOptions[] =
{ "exit-after-load", 0, 0, G_OPTION_ARG_NONE, &exitAfterLoad, "Quit the browser after the load finishes", NULL },
{ "time-zone", 't', 0, G_OPTION_ARG_STRING, &timeZone, "Set time zone", "TIMEZONE" },
{ "version", 'v', 0, G_OPTION_ARG_NONE, &printVersion, "Print the WebKitGTK version", NULL },
{ "config", 'C', 0, G_OPTION_ARG_FILENAME, &configFile, "Path to a configuration file", "PATH" },
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &uriArguments, 0, "[URL…]" },
{ 0, 0, 0, 0, 0, 0, 0 }
};
Expand Down Expand Up @@ -997,6 +999,7 @@ int main(int argc, char *argv[])
webkit_settings_set_enable_developer_extras(webkitSettings, TRUE);
webkit_settings_set_enable_webgl(webkitSettings, TRUE);
webkit_settings_set_enable_media_stream(webkitSettings, TRUE);

if (!addSettingsGroupToContext(context, webkitSettings))
g_clear_object(&webkitSettings);

Expand All @@ -1011,6 +1014,25 @@ int main(int argc, char *argv[])
}
g_option_context_free(context);

if (configFile) {
g_autoptr(GFile) file = g_file_new_for_commandline_arg(configFile);
g_autofree char* configFilePath = g_file_get_path(file);

if (!g_file_query_exists(file, NULL)) {
g_printerr("%s: File does not exist: %s\n", g_get_prgname(), configFilePath);
return EXIT_FAILURE;
}

g_autoptr(GError) error = NULL;
g_autoptr(GKeyFile) keyFile = g_key_file_new();
if (!g_key_file_load_from_file(keyFile, configFilePath, G_KEY_FILE_NONE, &error) || !webkit_settings_apply_from_key_file(webkitSettings, keyFile, "websettings", &error)) {
g_printerr("%s: Cannot load configuration file: %s\n", g_get_prgname(), error->message);
return EXIT_FAILURE;
}

g_clear_pointer(&configFile, g_free);
}

if (printVersion) {
g_print("WebKitGTK %u.%u.%u",
webkit_get_major_version(),
Expand Down
64 changes: 64 additions & 0 deletions Tools/TestWebKitAPI/Tests/WebKitGLib/TestWebKitSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include <WebCore/SoupVersioning.h>
#include <wtf/HashSet.h>
#include <wtf/glib/GRefPtr.h>
#include <wtf/text/StringConcatenateNumbers.h>

static WebKitTestServer* gServer;

Expand Down Expand Up @@ -458,6 +459,68 @@ void testWebKitFeatures(Test* test, gconstpointer)
#endif
}

void testWebKitSettingsApplyFromConfigFile(Test* test, gconstpointer)
{
GRefPtr<WebKitSettings> settings = adoptGRef(webkit_settings_new());
GUniquePtr<GKeyFile> key_file(g_key_file_new());
const char* key_file_contents = "[websettings]\n" \
"user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36\n" \
"enable-webaudio = 0\n" \
"enable-webrtc = true\n";

const char* invalidGroup = "[foo]\nbar = 42\n";
const char* unknownSetting = "[websettings]\n" \
"iamnotasetting = yes\n" \
"enable-webaudio = 0\n";
const char* invalidSettingType = "[websettings]\n" \
"enable-webaudio = ishouldnotbeastring\n";
auto bigIntNotSupported = makeString("[websettings]\nminimum-font-size = "_s, std::numeric_limits<uint64_t>::max(), '\n');
GUniqueOutPtr<GError> error;

// Loading settings from a file not containing a websettings group should raise an error.
g_key_file_load_from_data(key_file.get(), invalidGroup, strlen(invalidGroup), G_KEY_FILE_NONE, &error.outPtr());
g_assert_no_error(error.get());
g_assert_false(webkit_settings_apply_from_key_file(settings.get(), key_file.get(), "websettings", &error.outPtr()));
g_assert_error(error.get(), G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE);
g_assert_true(webkit_settings_get_enable_webaudio(settings.get()));

// Check default values of settings, before applying key_file settings.
g_assert_true(webkit_settings_get_enable_webaudio(settings.get()));
g_assert_false(webkit_settings_get_enable_webrtc(settings.get()));
CString defaultUserAgent = webkit_settings_get_user_agent(settings.get());

// Loading settings from a file that contains an unknown setting should raise an error.
g_key_file_load_from_data(key_file.get(), unknownSetting, strlen(unknownSetting), G_KEY_FILE_NONE, &error.outPtr());
g_assert_no_error(error.get());
g_assert_false(webkit_settings_apply_from_key_file(settings.get(), key_file.get(), "websettings", &error.outPtr()));
g_assert_error(error.get(), G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE);

// Mismatching a setting value type should raise an error.
g_key_file_load_from_data(key_file.get(), invalidSettingType, strlen(invalidSettingType), G_KEY_FILE_NONE, &error.outPtr());
g_assert_no_error(error.get());
g_assert_false(webkit_settings_apply_from_key_file(settings.get(), key_file.get(), "websettings", &error.outPtr()));
g_assert_error(error.get(), G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE);

// Overflowing uint settings should raise an error.
g_key_file_load_from_data(key_file.get(), bigIntNotSupported.utf8().data(), bigIntNotSupported.length(), G_KEY_FILE_NONE, &error.outPtr());
g_assert_no_error(error.get());
g_assert_false(webkit_settings_apply_from_key_file(settings.get(), key_file.get(), "websettings", &error.outPtr()));
g_assert_error(error.get(), G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE);

g_assert_true(g_key_file_load_from_data(key_file.get(), key_file_contents, strlen(key_file_contents), G_KEY_FILE_NONE, &error.outPtr()));
g_assert_no_error(error.get());

g_assert_true(webkit_settings_apply_from_key_file(settings.get(), key_file.get(), "websettings", &error.outPtr()));
g_assert_no_error(error.get());

// Check settings after apply key_file settings.
g_assert_false(webkit_settings_get_enable_webaudio(settings.get()));
g_assert_true(webkit_settings_get_enable_webrtc(settings.get()));

CString newUserAgent = webkit_settings_get_user_agent(settings.get());
g_assert_cmpstr(newUserAgent.data(), !=, defaultUserAgent.data());
}

#if PLATFORM(GTK)
static CString convertWebViewMainResourceDataToCString(WebViewTest* test)
{
Expand Down Expand Up @@ -566,6 +629,7 @@ void beforeAll()
Test::add("WebKitSettings", "webkit-settings", testWebKitSettings);
Test::add("WebKitSettings", "new-with-settings", testWebKitSettingsNewWithSettings);
Test::add("WebKitSettings", "features", testWebKitFeatures);
Test::add("WebKitSettings", "config-file", testWebKitSettingsApplyFromConfigFile);
#if PLATFORM(GTK)
WebViewTest::add("WebKitSettings", "user-agent", testWebKitSettingsUserAgent);
#endif
Expand Down

0 comments on commit 4d653b6

Please sign in to comment.