// BadWolf: Minimalist and privacy-oriented WebKitGTK+ browser // Copyright © 2019 Haelwenn (lanodan) Monnier // SPDX-License-Identifier: BSD-3-Clause #include /* fprintf() */ #include #include struct Client { GtkWidget *main_window; GtkWidget *box; GtkWidget *toolbar; GtkWidget *javascript; GtkWidget *location; GtkWidget *statusbar; WebKitWebView *webView; }; static gboolean WebViewCb_close(WebKitWebView *webView, GtkWidget *window) { (void)webView; gtk_widget_destroy(window); return TRUE; } static gboolean WebViewCb_web_process_terminated(WebKitWebView *webView, WebKitWebProcessTerminationReason reason) { (void)webView; char *str_reason; switch(reason) { case WEBKIT_WEB_PROCESS_CRASHED: str_reason = "the web process crashed."; break; case WEBKIT_WEB_PROCESS_EXCEEDED_MEMORY_LIMIT: str_reason = "the web process exceeded the memory limit."; break; default: str_reason = "the web process terminated for an unknown reason."; } fprintf(stderr, "BadWolf [signal: web-process-terminated]: %s\n", str_reason); return FALSE; } static gboolean WebViewCb_notify__uri(WebKitWebView *webView, GParamSpec *pspec, gpointer user_data) { (void)webView; (void)pspec; const gchar *location_uri; struct Client *browser = (struct Client *)user_data; location_uri = webkit_web_view_get_uri(browser->webView); gtk_entry_set_text(GTK_ENTRY(browser->location), location_uri); return TRUE; } static gboolean WebViewCb_notify__title(WebKitWebView *webView, GParamSpec *pspec, gpointer user_data) { (void)webView; (void)pspec; const gchar *title; struct Client *browser = (struct Client *)user_data; title = webkit_web_view_get_title(browser->webView); if(title == NULL) title = webkit_web_view_get_uri(browser->webView); if(title == NULL) title = "BadWolf"; gtk_window_set_title(GTK_WINDOW(browser->main_window), title); return TRUE; } static gboolean WebViewCb_notify__estimated_load_progress(WebKitWebView *webView, GParamSpec *pspec, gpointer user_data) { (void)webView; (void)pspec; struct Client *browser = (struct Client *)user_data; gdouble progress; progress = webkit_web_view_get_estimated_load_progress(browser->webView); if(progress == 1) { progress = 0; } gtk_entry_set_progress_fraction(GTK_ENTRY(browser->location), progress); return TRUE; } static gboolean WebViewCb_mouse_target_changed(WebKitWebView *webView, WebKitHitTestResult *hit, guint modifiers, gpointer user_data) { (void)webView; (void)modifiers; struct Client *browser = (struct Client *)user_data; const gchar *link_uri = webkit_hit_test_result_get_link_uri(hit); gtk_label_set_text(GTK_LABEL(browser->statusbar), link_uri); return TRUE; } static gboolean locationCb_activate(GtkEntry *location, gpointer user_data) { const char *target_url; struct Client *browser = (struct Client *)user_data; target_url = gtk_entry_get_text(location); if(target_url != NULL) webkit_web_view_load_uri(browser->webView, target_url); return TRUE; } static gboolean javascriptCb_toggled(GtkButton *javascript, gpointer user_data) { (void)javascript; struct Client *browser = (struct Client *)user_data; WebKitSettings *settings = webkit_web_view_get_settings(browser->webView); gboolean javascript_enabled = webkit_settings_get_enable_javascript(settings); webkit_settings_set_enable_javascript(settings, !javascript_enabled); webkit_web_view_set_settings(browser->webView, settings); return TRUE; } int main(int argc, char *argv[]) { struct Client *browser = malloc(sizeof(struct Client)); char *target_url = "about:blank"; gtk_init(&argc, &argv); if(argv[1]) target_url = argv[1]; browser->main_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); browser->box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); browser->toolbar = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); browser->location = gtk_entry_new(); browser->javascript = gtk_check_button_new(); browser->statusbar = gtk_label_new(NULL); browser->webView = WEBKIT_WEB_VIEW(webkit_web_view_new()); gtk_container_add(GTK_CONTAINER(browser->main_window), browser->box); gtk_box_pack_start(GTK_BOX(browser->toolbar), GTK_WIDGET(browser->javascript), FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(browser->toolbar), GTK_WIDGET(browser->location), TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(browser->box), GTK_WIDGET(browser->toolbar), FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(browser->box), GTK_WIDGET(browser->webView), TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(browser->box), GTK_WIDGET(browser->statusbar), FALSE, FALSE, 0); gtk_widget_set_halign(browser->statusbar, GTK_ALIGN_START); gtk_label_set_single_line_mode(GTK_LABEL(browser->statusbar), TRUE); gtk_entry_set_text(GTK_ENTRY(browser->location), target_url); gtk_entry_set_has_frame(GTK_ENTRY(browser->location), FALSE); gtk_entry_set_input_purpose(GTK_ENTRY(browser->location), GTK_INPUT_PURPOSE_URL); g_signal_connect(browser->main_window, "destroy", G_CALLBACK(gtk_main_quit), NULL); g_signal_connect(browser->location, "activate", G_CALLBACK(locationCb_activate), browser); g_signal_connect(browser->javascript, "toggled", G_CALLBACK(javascriptCb_toggled), browser); g_signal_connect(browser->webView, "close", G_CALLBACK(WebViewCb_close), browser->main_window); g_signal_connect(browser->webView, "web-process-terminated", G_CALLBACK(WebViewCb_web_process_terminated), NULL); g_signal_connect(browser->webView, "notify::uri", G_CALLBACK(WebViewCb_notify__uri), browser); g_signal_connect(browser->webView, "notify::title", G_CALLBACK(WebViewCb_notify__title), browser); g_signal_connect(browser->webView, "mouse-target-changed", G_CALLBACK(WebViewCb_mouse_target_changed), browser); g_signal_connect(browser->webView, "notify::estimated-load-progress", G_CALLBACK(WebViewCb_notify__estimated_load_progress), browser); WebKitSettings *settings = webkit_web_view_get_settings(browser->webView); webkit_settings_set_default_charset(settings, "utf-8"); webkit_settings_set_enable_caret_browsing(settings, TRUE); webkit_settings_set_enable_developer_extras(settings, TRUE); webkit_settings_set_enable_java(settings, FALSE); /* dangerous */ webkit_settings_set_enable_javascript(settings, FALSE); /* sane default 😏 */ webkit_settings_set_enable_plugins(settings, FALSE); /* dangerous */ webkit_settings_set_javascript_can_access_clipboard(settings, FALSE); /* dangerous */ webkit_settings_set_javascript_can_open_windows_automatically(settings, FALSE); /* dangerous */ webkit_settings_set_media_playback_requires_user_gesture(settings, TRUE); /* typically breaks notification sounds */ webkit_settings_set_minimum_font_size(settings, 9); /* text needs to be readable; screw broken pages */ webkit_web_view_set_settings(browser->webView, settings); webkit_web_view_load_uri(browser->webView, target_url); gtk_widget_grab_focus(GTK_WIDGET(browser->webView)); gtk_widget_show_all(browser->main_window); gtk_main(); return 0; }