diff options
| Commit | 12cbd061058a5e5a23380fce66169d34af3de746 (patch) | |
|---|---|---|
| Author | 2022-07-19 14:18:16 +0300 | |
| Committer | 2022-07-19 14:18:16 +0300 | |
| Files | 7cabb04515ad4ed21f6f190922ff48c5ad9a23b5 /goodwolf.c | |
| goodwolf-12cbd061058a5e5a23380fce66169d34af3de746.tar goodwolf-12cbd061058a5e5a23380fce66169d34af3de746.tar.gz goodwolf-12cbd061058a5e5a23380fce66169d34af3de746.zip | ||
| Parent | ccbc83e827e11c7deb2959a6752452691c4165a1 (diff) | |
code: Renamed codebase to goodwolf
1 files changed, 1329 insertions, 0 deletions
| -rw-r--r-- | goodwolf.c | 1329 |
diff --git a/goodwolf.c b/goodwolf.c new file mode 100644 index 0000000..cb0b1b2 --- /dev/null +++ b/goodwolf.c @@ -0,0 +1,1329 @@ +// BadWolf: Minimalist and privacy-oriented WebKitGTK+ browser +// Copyright © 2019-2021 Badwolf Authors <https://hacktivis.me/projects/badwolf> +// SPDX-License-Identifier: BSD-3-Clause + +#include "goodwolf.h" + +#include "bookmarks.h" +#include "config.h" +#include "downloads.h" +#include "fmt.h" +#include "keybindings.h" +#include "uri.h" + +#include <assert.h> +#include <glib/gi18n.h> /* _() and other internationalization/localization helpers */ +#include <libsoup/soup.h> /* soup* */ +#include <locale.h> /* LC_* */ +#include <stdio.h> /* perror(), fprintf(), snprintf() */ +#include <stdlib.h> /* malloc() */ +#include <unistd.h> /* access() */ + +const gchar *homepage = "https://hacktivis.me/projects/goodwolf"; +const gchar *version = VERSION; + +static gchar *web_extensions_directory; +static uint64_t context_id_counter = 0; +GtkTreeModel *bookmarks_completion_model; + +static gboolean tabs_no_label = false; +static gboolean tabs_constant = false; +static gboolean tabs_context_label = false; +static gboolean tray_enabled = false; +static gboolean alt_useragent = false; +static int custom_favicon_size = 30; +static gboolean persistent_storage = false; +static gboolean separated_storage = false; + +static gboolean WebViewCb_close(WebKitWebView *webView, gpointer user_data); +static gboolean WebViewCb_web_process_terminated(WebKitWebView *webView, + WebKitWebProcessTerminationReason reason, + gpointer user_data); +static gboolean +WebViewCb_notify__uri(WebKitWebView *webView, GParamSpec *pspec, gpointer user_data); +static gboolean +WebViewCb_notify__title(WebKitWebView *webView, GParamSpec *pspec, gpointer user_data); +static gboolean +WebViewCb_notify__favicon(WebKitWebView *webView, GParamSpec *pspec, gpointer user_data); +static gboolean +WebViewCb_notify__is__playing__audio(WebKitWebView *webView, GParamSpec *pspec, gpointer user_data); +static gboolean WebViewCb_notify__estimated_load_progress(WebKitWebView *webView, + GParamSpec *pspec, + gpointer user_data); +static gboolean WebViewCb_mouse_target_changed(WebKitWebView *webView, + WebKitHitTestResult *hit, + guint modifiers, + gpointer user_data); +static WebKitWebView *WebViewCb_create(WebKitWebView *related_web_view, + WebKitNavigationAction *navigation_action, + gpointer user_data); +static gboolean WebViewCb_permission_request(WebKitWebView *web_view, + WebKitPermissionRequest *request, + gpointer user_data); +static gboolean WebViewCb_decide_policy(WebKitWebView *web_view, + WebKitPolicyDecision *decision, + WebKitPolicyDecisionType decision_type, + gpointer user_data); +static void +WebViewCb_load_changed(WebKitWebView *webView, WebKitLoadEvent load_event, gpointer user_data); +static void web_contextCb_download_started(WebKitWebContext *web_context, + WebKitDownload *download, + gpointer user_data); +static gboolean locationCb_activate(GtkEntry *location, gpointer user_data); +static gboolean javascriptCb_toggled(GtkButton *javascript, gpointer user_data); +static gboolean auto_load_imagesCb_toggled(GtkButton *auto_load_images, gpointer user_data); +static void backCb_clicked(GtkButton *back, gpointer user_data); +static void forwardCb_clicked(GtkButton *forward, gpointer user_data); +static void printCb_clicked(GtkButton *forward, gpointer user_data); +static gboolean SearchEntryCb_next__match(GtkSearchEntry *search, gpointer user_data); +static gboolean SearchEntryCb_previous__match(GtkSearchEntry *search, gpointer user_data); +static gboolean SearchEntryCb_search__changed(GtkSearchEntry *search, gpointer user_data); +static gboolean SearchEntryCb_stop__search(GtkSearchEntry *search, gpointer user_data); +static void new_tabCb_clicked(GtkButton *new_tab, gpointer user_data); +static void closeCb_clicked(GtkButton *close, gpointer user_data); +static void +notebookCb_switch__page(GtkNotebook *notebook, GtkWidget *page, guint page_num, gpointer user_data); +void content_managerCb_ready(GObject *store, GAsyncResult *result, gpointer user_data); + +static gboolean +WebViewCb_close(WebKitWebView *webView, gpointer user_data) +{ + (void)webView; + struct Client *browser = (struct Client *)user_data; + + gtk_widget_destroy(browser->box); + + free(browser); + + return TRUE; +} + +static gboolean +WebViewCb_web_process_terminated(WebKitWebView *webView, + WebKitWebProcessTerminationReason reason, + gpointer user_data) +{ + (void)webView; + struct Client *browser = (struct Client *)user_data; + + switch(reason) + { + case WEBKIT_WEB_PROCESS_CRASHED: + fprintf(stderr, "%s", _("the web process crashed.\n")); + webView_tab_label_change(browser, _("Crashed")); + break; + case WEBKIT_WEB_PROCESS_EXCEEDED_MEMORY_LIMIT: + fprintf(stderr, "%s", _("the web process exceeded the memory limit.\n")); + webView_tab_label_change(browser, _("Out of Memory")); + break; + default: + fprintf(stderr, "%s", _("the web process terminated for an unknown reason.\n")); + webView_tab_label_change(browser, _("Unknown Crash")); + } + + 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); + + if(webkit_uri_for_display(location_uri) != location_uri) + gtk_widget_set_tooltip_text(browser->location, webkit_uri_for_display(location_uri)); + else + gtk_widget_set_has_tooltip(browser->location, false); + + return TRUE; +} + +cairo_surface_t* scale_favicon(cairo_surface_t *f) +{ + if (f == NULL) return NULL; + gint pixel_size = custom_favicon_size; + if (pixel_size == 0) { + gtk_icon_size_lookup(GTK_ICON_SIZE_SMALL_TOOLBAR, NULL, &pixel_size); + if (pixel_size == 0) return NULL; + } + + cairo_surface_t *result = cairo_surface_create_similar(f, + cairo_surface_get_content(f), + pixel_size, + pixel_size); + cairo_t *cr = cairo_create(result); + cairo_scale(cr, + (double)pixel_size/cairo_image_surface_get_width(f), + (double)pixel_size/cairo_image_surface_get_height(f)); + cairo_set_source_surface(cr, f, 0, 0); + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + cairo_paint(cr); + cairo_destroy(cr); + return result; +} + +GtkWidget * +goodwolf_new_tab_box(const gchar *title, struct Client *browser) +{ + (void)browser; + /* flawfinder: ignore. bound checks are done */ + char context_id_str[GOODWOLF_CTX_SIZ] = {0, 0, 0, 0, 0, 0, 0}; + + fmt_context_id(browser->context_id, context_id_str, 1); + + GtkWidget *tab_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); + gtk_widget_set_name(tab_box, "browser__tabbox"); + + GtkWidget *close = + gtk_button_new_from_icon_name("window-close-symbolic", GTK_ICON_SIZE_LARGE_TOOLBAR); + gtk_widget_set_name(close, "browser__tabbox__close"); + GtkWidget *context_label; + GtkWidget *context_label_event_box; + if (tabs_context_label) { + context_label = gtk_label_new(context_id_str); + gtk_widget_set_name(context_label, "browser__tabbox__context_label"); + context_label_event_box = gtk_event_box_new(); + gtk_container_add(GTK_CONTAINER(context_label_event_box), context_label); + } + GtkWidget *favicon = + gtk_image_new_from_surface(scale_favicon(webkit_web_view_get_favicon(browser->webView))); + gtk_widget_set_name(favicon, "browser__tabbox__favicon"); + GtkWidget *favicon_event_box = gtk_event_box_new(); + gtk_container_add(GTK_CONTAINER(favicon_event_box), favicon); + GtkWidget *label = gtk_label_new(title); + gtk_widget_set_name(label, "browser__tabbox__label"); + GtkWidget *label_event_box = gtk_event_box_new(); + gtk_container_add(GTK_CONTAINER(label_event_box), label); + GtkWidget *playing = + gtk_image_new_from_icon_name("audio-volume-high-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR); + gtk_widget_set_name(playing, "browser__tabbox__playing"); + GtkWidget *playing_event_box = gtk_event_box_new(); + gtk_container_add(GTK_CONTAINER(playing_event_box), playing); + +#ifdef GOODWOLF_TAB_BOX_WIDTH + gtk_widget_set_size_request(label, GOODWOLF_TAB_BOX_WIDTH, -1); +#endif +#ifdef GOODWOLF_TAB_LABEL_CHARWIDTH + gtk_label_set_width_chars(GTK_LABEL(label), GOODWOLF_TAB_LABEL_CHARWIDTH); +#endif + gtk_widget_set_hexpand(tab_box, GOODWOLF_TAB_HEXPAND); + + gtk_label_set_ellipsize(GTK_LABEL(label), GOODWOLF_TAB_LABEL_ELLIPSIZE); + gtk_label_set_single_line_mode(GTK_LABEL(label), TRUE); + + if (tabs_context_label) { + gtk_label_set_single_line_mode(GTK_LABEL(context_label), TRUE); + gtk_box_pack_start(GTK_BOX(tab_box), context_label_event_box, TRUE, TRUE, 0); + } + + gtk_box_pack_start(GTK_BOX(tab_box), favicon_event_box, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(tab_box), playing_event_box, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(tab_box), label_event_box, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(tab_box), close, FALSE, FALSE, 0); + + gtk_button_set_relief(GTK_BUTTON(close), GTK_RELIEF_NONE); + + g_signal_connect(close, "clicked", G_CALLBACK(closeCb_clicked), browser); + + gtk_widget_set_tooltip_text(tab_box, title); + + gtk_widget_show_all(tab_box); + gtk_widget_set_visible(playing, webkit_web_view_is_playing_audio(browser->webView)); + gtk_widget_set_visible(label, !tabs_no_label); + gtk_widget_set_visible(close, !tabs_constant); + gtk_widget_set_events(label, GDK_BUTTON_RELEASE_MASK); + g_signal_connect( + tab_box, "button-release-event", G_CALLBACK(tab_boxCb_button_release_event), browser); + + return tab_box; +} + +static gboolean +WebViewCb_notify__title(WebKitWebView *webView, GParamSpec *pspec, gpointer user_data) +{ + (void)webView; + (void)pspec; + struct Client *browser = (struct Client *)user_data; + + webView_tab_label_change(browser, NULL); + + return TRUE; +} + +static gboolean +WebViewCb_notify__is__playing__audio(WebKitWebView *webView, GParamSpec *pspec, gpointer user_data) +{ + (void)webView; + (void)pspec; + struct Client *browser = (struct Client *)user_data; + + webView_tab_label_change(browser, NULL); + + return TRUE; +} + +static gboolean +WebViewCb_notify__favicon(WebKitWebView *webView, GParamSpec *pspec, gpointer user_data) +{ + (void)webView; + (void)pspec; + struct Client *browser = (struct Client *)user_data; + + webView_tab_label_change(browser, NULL); + + return TRUE; +} + +void +webView_tab_label_change(struct Client *browser, const gchar *title) +{ + GtkWidget *notebook = browser->window->notebook; + +#define title_IS_EMPTY (title == NULL) || strnlen(title, 2) == 0 + + if(title_IS_EMPTY) title = webkit_web_view_get_title(browser->webView); + if(title_IS_EMPTY) title = webkit_web_view_get_uri(browser->webView); + if(title_IS_EMPTY) title = "BadWolf"; + + gtk_notebook_set_tab_label( + GTK_NOTEBOOK(notebook), browser->box, goodwolf_new_tab_box(title, browser)); + gtk_notebook_set_menu_label_text(GTK_NOTEBOOK(notebook), browser->box, title); + + // Set the window title if the title change was on the current tab + if(gtk_notebook_page_num(GTK_NOTEBOOK(notebook), browser->box) == + gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook))) + gtk_window_set_title(GTK_WINDOW(browser->window->main_window), title); +} + +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; + + if(webkit_hit_test_result_context_is_link(hit)) + { + const gchar *link_uri = webkit_hit_test_result_get_link_uri(hit); + + gtk_label_set_text(GTK_LABEL(browser->statuslabel), webkit_uri_for_display(link_uri)); + } + else + gtk_label_set_text(GTK_LABEL(browser->statuslabel), NULL); + + return FALSE; +} + +static gboolean +WebViewCb_scroll_event(GtkWidget *widget, GdkEvent *event, gpointer data) +{ + (void)widget; + struct Client *browser = (struct Client *)data; + gdouble delta_x, delta_y; + gdouble zoom; + + if(((GdkEventScroll *)event)->state & GDK_CONTROL_MASK) + { + gdk_event_get_scroll_deltas(event, &delta_x, &delta_y); + zoom = webkit_web_view_get_zoom_level(WEBKIT_WEB_VIEW(browser->webView)); + zoom -= delta_y * 0.1; + webkit_web_view_set_zoom_level(WEBKIT_WEB_VIEW(browser->webView), zoom); + return TRUE; + } + + return FALSE; +} + +static WebKitWebView * +WebViewCb_create(WebKitWebView *related_web_view, + WebKitNavigationAction *navigation_action, + gpointer user_data) +{ + (void)navigation_action; + struct Client *old_browser = (struct Client *)user_data; + struct Client *browser = NULL; + + // shouldn't be needed but better be safe + old_browser->webView = related_web_view; + + browser = new_browser(old_browser->window, NULL, old_browser); + + if(goodwolf_new_tab(GTK_NOTEBOOK(old_browser->window->notebook), browser, FALSE) < 0) + return NULL; + else + return browser->webView; +} + +static gboolean +WebViewCb_permission_request(WebKitWebView *web_view, + WebKitPermissionRequest *request, + gpointer user_data) +{ + (void)web_view; + (void)user_data; + + webkit_permission_request_deny(request); + + return TRUE; /* Stop other handlers */ +} + +static gboolean +WebViewCb_decide_policy(WebKitWebView *web_view, + WebKitPolicyDecision *decision, + WebKitPolicyDecisionType decision_type, + gpointer user_data) +{ + WebKitResponsePolicyDecision *r; + (void)web_view; + (void)user_data; + + switch(decision_type) + { + case WEBKIT_POLICY_DECISION_TYPE_RESPONSE: + r = WEBKIT_RESPONSE_POLICY_DECISION(decision); + if(!webkit_response_policy_decision_is_mime_type_supported(r)) + webkit_policy_decision_download(decision); + else + webkit_policy_decision_use(decision); + break; + default: + /* Use whatever default there is. */ + return FALSE; + } + + return TRUE; +} + +static void +WebViewCb_load_changed(WebKitWebView *webView, WebKitLoadEvent load_event, gpointer user_data) +{ + (void)webView; + (void)load_event; + struct Client *browser = (struct Client *)user_data; + + gtk_widget_set_sensitive(browser->back, webkit_web_view_can_go_back(browser->webView)); + gtk_widget_set_sensitive(browser->forward, webkit_web_view_can_go_forward(browser->webView)); +} + +static char * +detail_tls_certificate_flags(GTlsCertificateFlags tls_errors) +{ + GString *errors = g_string_new(NULL); + + g_string_append_printf(errors, + _("Couldn't verify the TLS certificate to ensure a better security of the " + "connection. You might want to verify your machine and network.\n\n")); + + if(tls_errors & G_TLS_CERTIFICATE_UNKNOWN_CA) + g_string_append_printf(errors, _("Error: The X509 Certificate Authority is unknown.\n")); + + if(tls_errors & G_TLS_CERTIFICATE_BAD_IDENTITY) + g_string_append(errors, _("Error: The given identity doesn't match the expected one.\n")); + + if(tls_errors & G_TLS_CERTIFICATE_NOT_ACTIVATED) + g_string_append(errors, + _("Error: The certificate isn't valid yet. Check your system's clock.\n")); + + if(tls_errors & G_TLS_CERTIFICATE_EXPIRED) + g_string_append(errors, _("Error: The certificate has expired. Check your system's clock.\n")); + + if(tls_errors & G_TLS_CERTIFICATE_REVOKED) + g_string_append(errors, _("Error: The certificate has been revoked.\n")); + + if(tls_errors & G_TLS_CERTIFICATE_INSECURE) + g_string_append(errors, _("Error: The certificate is considered to be insecure.\n")); + + if(tls_errors & G_TLS_CERTIFICATE_GENERIC_ERROR) + g_string_append(errors, _("Error: Some unknown error occurred validating the certificate.\n")); + + return g_string_free(errors, FALSE); +} + +static gboolean +WebViewCb_load_failed_with_tls_errors(WebKitWebView *web_view, + gchar *failing_text, + GTlsCertificate *certificate, + GTlsCertificateFlags errors, + gpointer user_data) +{ + (void)web_view; + (void)certificate; + (void)errors; + struct Client *browser = (struct Client *)user_data; + gchar *error_details = detail_tls_certificate_flags(errors); + gint dialog_response; + +#ifndef USE_LIBSOUP2 + GUri *failing_uri = g_uri_parse(failing_text, G_URI_FLAGS_NONE, NULL); +#else + SoupURI *failing_uri = soup_uri_new(failing_text); +#endif + + GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(browser->window->main_window), + GTK_DIALOG_MODAL & GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_NONE, + _("TLS Error for %s."), + failing_text); + gtk_dialog_add_buttons( + GTK_DIALOG(dialog), _("Temporarily Add Exception"), 1, _("Continue"), 0, NULL); + if(!failing_uri) + { + gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog), 1, FALSE); + } + gtk_dialog_set_default_response(GTK_DIALOG(dialog), 0); + gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), "%s\n", error_details); + + dialog_response = gtk_dialog_run(GTK_DIALOG(dialog)); + + if(dialog_response == 1) + { +#ifndef USE_LIBSOUP2 + webkit_web_context_allow_tls_certificate_for_host( + webkit_web_view_get_context(browser->webView), certificate, g_uri_get_host(failing_uri)); +#else + webkit_web_context_allow_tls_certificate_for_host( + webkit_web_view_get_context(browser->webView), certificate, failing_uri->host); +#endif + webkit_web_view_reload(browser->webView); + } + +#ifndef USE_LIBSOUP2 + /* Calling g_free(failing_uri) ought to be the correct way but this causes a segfault. + * + * - documentation describes it as something which should be free + * - implementation seems to make it a pointer that should be freed + * - epiphany doesn't seems to free/unref it but gnome code seems to frequently have memleaks + * + * Decided to at least continue to try with using g_uri_unref(failing_uri) instead. + * Related fediverse post: <https://queer.hacktivis.me/objects/cec7c4e8-6a58-4358-85bf-b66f9bb21a98> + */ + g_uri_unref(failing_uri); +#else + soup_uri_free(failing_uri); +#endif + g_free(error_details); + gtk_widget_destroy(dialog); + + return FALSE; /* propagate the event further */ +} + +static void +web_contextCb_download_started(WebKitWebContext *web_context, + WebKitDownload *webkit_download, + gpointer user_data) +{ + (void)web_context; + struct Client *browser = (struct Client *)user_data; + struct Download *download = malloc(sizeof(struct Download)); + + assert(webkit_download); + + if(download != NULL) + { + download->window = browser->window; + + download_new_entry(webkit_download, download); + + g_signal_connect( + G_OBJECT(webkit_download), "received-data", G_CALLBACK(downloadCb_received_data), download); + g_signal_connect(G_OBJECT(webkit_download), + "created-destination", + G_CALLBACK(downloadCb_created_destination), + download); + g_signal_connect(G_OBJECT(webkit_download), "failed", G_CALLBACK(downloadCb_failed), download); + g_signal_connect( + G_OBJECT(webkit_download), "finished", G_CALLBACK(downloadCb_finished), download); + } + + g_signal_connect(G_OBJECT(webkit_download), + "decide-destination", + G_CALLBACK(downloadCb_decide_destination), + user_data); +} + +static gboolean +locationCb_activate(GtkEntry *location, gpointer user_data) +{ + struct Client *browser = (struct Client *)user_data; + + webkit_web_view_load_uri(browser->webView, + goodwolf_ensure_uri_scheme(gtk_entry_get_text(location), TRUE)); + + return TRUE; +} + +static gboolean +javascriptCb_toggled(GtkButton *javascript, gpointer user_data) +{ + struct Client *browser = (struct Client *)user_data; + + WebKitSettings *settings = webkit_web_view_get_settings(browser->webView); + + webkit_settings_set_enable_javascript_markup( + settings, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(javascript))); + + webkit_web_view_set_settings(browser->webView, settings); + + return TRUE; +} + +static gboolean +auto_load_imagesCb_toggled(GtkButton *auto_load_images, gpointer user_data) +{ + struct Client *browser = (struct Client *)user_data; + + WebKitSettings *settings = webkit_web_view_get_settings(browser->webView); + + webkit_settings_set_auto_load_images( + settings, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(auto_load_images))); + + webkit_web_view_set_settings(browser->webView, settings); + + return TRUE; +} + +static void +backCb_clicked(GtkButton *back, gpointer user_data) +{ + (void)back; + struct Client *browser = (struct Client *)user_data; + + webkit_web_view_go_back(browser->webView); +} + +static void +forwardCb_clicked(GtkButton *forward, gpointer user_data) +{ + (void)forward; + struct Client *browser = (struct Client *)user_data; + + webkit_web_view_go_forward(browser->webView); +} + +static void +printCb_clicked(GtkButton *print, gpointer user_data) +{ + (void)print; + struct Client *browser = (struct Client *)user_data; + + WebKitPrintOperation *print_operation = webkit_print_operation_new(browser->webView); + + webkit_print_operation_run_dialog(print_operation, GTK_WINDOW(browser->window->main_window)); +} + +static gboolean +SearchEntryCb_next__match(GtkSearchEntry *search, gpointer user_data) +{ + (void)search; + struct Client *browser = (struct Client *)user_data; + WebKitFindController *findController = webkit_web_view_get_find_controller(browser->webView); + + webkit_find_controller_search_next(findController); + + return TRUE; +} + +static gboolean +SearchEntryCb_previous__match(GtkSearchEntry *search, gpointer user_data) +{ + (void)search; + struct Client *browser = (struct Client *)user_data; + WebKitFindController *findController = webkit_web_view_get_find_controller(browser->webView); + + webkit_find_controller_search_previous(findController); + + return TRUE; +} + +static gboolean +SearchEntryCb_search__changed(GtkSearchEntry *search, gpointer user_data) +{ + struct Client *browser = (struct Client *)user_data; + WebKitFindController *findController = webkit_web_view_get_find_controller(browser->webView); + const gchar *search_text = gtk_entry_get_text(GTK_ENTRY(search)); + + webkit_find_controller_search(findController, search_text, 0, 0); + + return TRUE; +} + +static gboolean +SearchEntryCb_stop__search(GtkSearchEntry *search, gpointer user_data) +{ + (void)search; + struct Client *browser = (struct Client *)user_data; + WebKitFindController *findController = webkit_web_view_get_find_controller(browser->webView); + + webkit_find_controller_search_finish(findController); + + return TRUE; +} + +static gboolean +widgetCb_drop_button3_event(GtkWidget *widget, GdkEvent *event, gpointer user_data) +{ + (void)widget; + (void)user_data; + + // Button3 being right-click on right-handed mode, left-click on left-handed mode + return ((GdkEventButton *)event)->button == 3; +} + +struct Client * +new_browser(struct Window *window, const gchar *target_url, struct Client *old_browser) +{ + struct Client *browser = malloc(sizeof(struct Client)); + target_url = goodwolf_ensure_uri_scheme(target_url, (old_browser == NULL)); + char *goodwolf_l10n = NULL; + + WebKitWebContext *web_context; + + if(browser == NULL) return NULL; + + browser->window = window; + browser->context_id = old_browser == NULL ? context_id_counter++ : old_browser->context_id; + browser->box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); + gtk_widget_set_name(browser->box, "browser__box"); + + browser->toolbar = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); + gtk_widget_set_name(browser->toolbar, "browser__toolbar"); + + browser->back = + gtk_button_new_from_icon_name("go-previous-symbolic", GTK_ICON_SIZE_LARGE_TOOLBAR); + gtk_widget_set_name(browser->back, "browser__back"); + + browser->forward = gtk_button_new_from_icon_name("go-next-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR); + gtk_widget_set_name(browser->forward, "browser__forward"); + + GtkWidget *toolbar_separator = gtk_separator_new(GTK_ORIENTATION_VERTICAL); + + browser->javascript = gtk_toggle_button_new_with_mnemonic(_("_JS")); + gtk_widget_set_name(browser->javascript, "browser__javascript"); + gtk_widget_set_tooltip_text(browser->javascript, _("Toggle javascript")); + gtk_button_set_relief(GTK_BUTTON(browser->javascript), GTK_RELIEF_NONE); + + browser->auto_load_images = gtk_toggle_button_new_with_mnemonic(_("_IMG")); + gtk_widget_set_name(browser->auto_load_images, "browser__load_images"); + gtk_widget_set_tooltip_text(browser->auto_load_images, _("Toggle loading images automatically")); + gtk_button_set_relief(GTK_BUTTON(browser->auto_load_images), GTK_RELIEF_NONE); + + browser->location = gtk_entry_new(); + gtk_widget_set_name(browser->location, "browser__location"); + + GtkWidget *print = + gtk_button_new_from_icon_name("document-print-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR); + gtk_widget_set_name(browser->back, "browser__print"); + + browser->statusbar = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); + gtk_widget_set_name(browser->statusbar, "browser__statusbar"); + browser->search = gtk_search_entry_new(); + gtk_widget_set_name(browser->search, "browser__search"); + browser->statuslabel = gtk_label_new(NULL); + gtk_widget_set_name(browser->statuslabel, "browser__statuslabel"); + + if(old_browser == NULL) + { + WebKitWebsiteDataManager *website_data_manager = + (persistent_storage) ? webkit_website_data_manager_new(NULL) + : webkit_website_data_manager_new_ephemeral(); + webkit_website_data_manager_set_itp_enabled(website_data_manager, TRUE); + + web_context = webkit_web_context_new_with_website_data_manager(website_data_manager); + g_object_unref(website_data_manager); + webkit_web_context_set_sandbox_enabled(web_context, TRUE); + webkit_web_context_set_process_model(web_context, + WEBKIT_PROCESS_MODEL_MULTIPLE_SECONDARY_PROCESSES); + webkit_web_context_set_web_extensions_directory(web_context, web_extensions_directory); + webkit_web_context_set_favicon_database_directory(web_context, NULL); + if (persistent_storage) { + char context_id_str[GOODWOLF_CTX_SIZ] = {0, 0, 0, 0, 0, 0, 0}; + if (separated_storage) { + fmt_context_id(browser->context_id, context_id_str, 0); + g_mkdir_with_parents( + g_build_filename(g_get_user_config_dir(), "goodwolf", context_id_str, NULL), + 0755); + } + + webkit_cookie_manager_set_persistent_storage( + webkit_website_data_manager_get_cookie_manager(website_data_manager), + g_build_filename(g_get_user_config_dir(), "goodwolf", context_id_str, "cookies.txt", NULL), + WEBKIT_COOKIE_PERSISTENT_STORAGE_TEXT); + webkit_cookie_manager_set_accept_policy( + webkit_website_data_manager_get_cookie_manager(website_data_manager), + WEBKIT_COOKIE_POLICY_ACCEPT_ALWAYS); + } + + g_signal_connect(G_OBJECT(web_context), + "download-started", + G_CALLBACK(web_contextCb_download_started), + browser); + } + else + { + web_context = webkit_web_view_get_context(old_browser->webView); + } + + /* flawfinder: ignore. Consider that g_strsplit is safe enough */ + goodwolf_l10n = getenv("GOODWOLF_L10N"); + + if(goodwolf_l10n != NULL) + { + gchar **languages = g_strsplit(goodwolf_l10n, ":", -1); + webkit_web_context_set_spell_checking_languages(web_context, (const gchar *const *)languages); + g_strfreev(languages); + + webkit_web_context_set_spell_checking_enabled(web_context, TRUE); + } + + WebKitSettings *settings = webkit_settings_new_with_settings(GOODWOLF_WEBKIT_SETTINGS); + if (alt_useragent == TRUE) + webkit_settings_set_user_agent(settings, "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:74.0) Gecko/20100101 Firefox/74.0"); + + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(browser->javascript), + webkit_settings_get_enable_javascript_markup(settings)); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(browser->auto_load_images), + webkit_settings_get_auto_load_images(settings)); + + browser->webView = WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW, + "web-context", + web_context, + "related-view", + old_browser == NULL ? NULL : old_browser->webView, + "settings", + settings, + "user-content-manager", + window->content_manager, + NULL)); + gtk_widget_set_name(GTK_WIDGET(browser->webView), "browser__webView"); + if(old_browser == NULL) + { + g_object_unref(web_context); + } + g_object_unref(settings); + + gtk_box_pack_start( + GTK_BOX(browser->toolbar), GTK_WIDGET(browser->back), FALSE, FALSE, GOODWOLF_TOOLBAR_PADDING); + gtk_box_pack_start(GTK_BOX(browser->toolbar), + GTK_WIDGET(browser->forward), + FALSE, + FALSE, + GOODWOLF_TOOLBAR_PADDING); + gtk_box_pack_start(GTK_BOX(browser->toolbar), + toolbar_separator, + FALSE, + FALSE, + GOODWOLF_TOOLBAR_SEPARATOR_PADDING); + gtk_box_pack_start(GTK_BOX(browser->toolbar), + GTK_WIDGET(browser->javascript), + FALSE, + FALSE, + GOODWOLF_TOOLBAR_PADDING); + gtk_box_pack_start(GTK_BOX(browser->toolbar), + GTK_WIDGET(browser->auto_load_images), + FALSE, + FALSE, + GOODWOLF_TOOLBAR_PADDING); + gtk_box_pack_start(GTK_BOX(browser->toolbar), + GTK_WIDGET(browser->location), + TRUE, + TRUE, + GOODWOLF_TOOLBAR_PADDING); + gtk_box_pack_start(GTK_BOX(browser->toolbar), print, FALSE, FALSE, GOODWOLF_TOOLBAR_PADDING); + + gtk_container_set_focus_child(GTK_CONTAINER(browser->box), browser->toolbar); + gtk_container_set_focus_child(GTK_CONTAINER(browser->toolbar), browser->location); + + gtk_box_pack_start( + GTK_BOX(browser->box), GTK_WIDGET(browser->toolbar), FALSE, FALSE, GOODWOLF_BOX_PADDING); + gtk_box_pack_start( + GTK_BOX(browser->box), GTK_WIDGET(browser->webView), TRUE, TRUE, GOODWOLF_BOX_PADDING); + + gtk_box_pack_start( + GTK_BOX(browser->box), GTK_WIDGET(browser->statusbar), FALSE, FALSE, GOODWOLF_BOX_PADDING); + + gtk_box_pack_start(GTK_BOX(browser->statusbar), + GTK_WIDGET(browser->search), + FALSE, + FALSE, + GOODWOLF_STATUSBAR_PADDING); + gtk_box_pack_start(GTK_BOX(browser->statusbar), + GTK_WIDGET(browser->statuslabel), + FALSE, + FALSE, + GOODWOLF_STATUSBAR_PADDING); + + gtk_widget_set_halign(browser->statusbar, GTK_ALIGN_START); + + gtk_label_set_single_line_mode(GTK_LABEL(browser->statuslabel), TRUE); + gtk_label_set_ellipsize(GTK_LABEL(browser->statuslabel), GOODWOLF_STATUSLABEL_ELLIPSIZE); + + if(bookmarks_completion_model != NULL) + { + GtkEntryCompletion *location_completion = gtk_entry_completion_new(); + GtkTreeModel *location_completion_model = bookmarks_completion_model; + + bookmarks_completion_setup(location_completion, location_completion_model); + gtk_entry_set_completion(GTK_ENTRY(browser->location), location_completion); + } + + gtk_entry_set_text(GTK_ENTRY(browser->location), target_url); + gtk_entry_set_input_purpose(GTK_ENTRY(browser->location), GTK_INPUT_PURPOSE_URL); + + gtk_entry_set_placeholder_text(GTK_ENTRY(browser->search), _("search in current page")); + + /* signals for back/forward buttons */ + g_signal_connect(browser->back, "clicked", G_CALLBACK(backCb_clicked), browser); + g_signal_connect(browser->forward, "clicked", G_CALLBACK(forwardCb_clicked), browser); + /* prevents GtkNotebook from spawning it's context-menu */ + g_signal_connect( + browser->back, "button-press-event", G_CALLBACK(widgetCb_drop_button3_event), NULL); + g_signal_connect( + browser->back, "button-release-event", G_CALLBACK(widgetCb_drop_button3_event), NULL); + g_signal_connect( + browser->forward, "button-press-event", G_CALLBACK(widgetCb_drop_button3_event), NULL); + g_signal_connect( + browser->forward, "button-release-event", G_CALLBACK(widgetCb_drop_button3_event), NULL); + + /* signals for javacript toggle widget */ + g_signal_connect(browser->javascript, "toggled", G_CALLBACK(javascriptCb_toggled), browser); + /* prevents GtkNotebook from spawning it's context-menu */ + g_signal_connect( + browser->javascript, "button-press-event", G_CALLBACK(widgetCb_drop_button3_event), NULL); + g_signal_connect( + browser->javascript, "button-release-event", G_CALLBACK(widgetCb_drop_button3_event), NULL); + + /* signals for auto_load_images toggle widget */ + g_signal_connect( + browser->auto_load_images, "toggled", G_CALLBACK(auto_load_imagesCb_toggled), browser); + /* prevents GtkNotebook from spawning it's context-menu */ + g_signal_connect(browser->auto_load_images, + "button-press-event", + G_CALLBACK(widgetCb_drop_button3_event), + NULL); + g_signal_connect(browser->auto_load_images, + "button-release-event", + G_CALLBACK(widgetCb_drop_button3_event), + NULL); + + /* signals for location entry widget */ + g_signal_connect(browser->location, "activate", G_CALLBACK(locationCb_activate), browser); + + /* signals for print button */ + g_signal_connect(print, "clicked", G_CALLBACK(printCb_clicked), browser); + /* prevents GtkNotebook from spawning it's context-menu */ + g_signal_connect(print, "button-press-event", G_CALLBACK(widgetCb_drop_button3_event), NULL); + g_signal_connect(print, "button-release-event", G_CALLBACK(widgetCb_drop_button3_event), NULL); + + /* signals for WebView widget */ + g_signal_connect(browser->webView, + "web-process-terminated", + G_CALLBACK(WebViewCb_web_process_terminated), + browser); + 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, + "notify::favicon", + G_CALLBACK(WebViewCb_notify__favicon), + browser); + g_signal_connect(browser->webView, + "notify::is-playing-audio", + G_CALLBACK(WebViewCb_notify__is__playing__audio), + 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); + g_signal_connect(browser->webView, "create", G_CALLBACK(WebViewCb_create), browser); + g_signal_connect(browser->webView, "close", G_CALLBACK(WebViewCb_close), browser); + + g_signal_connect( + browser->webView, "key-press-event", G_CALLBACK(WebViewCb_key_press_event), browser); + g_signal_connect(browser->webView, "scroll-event", G_CALLBACK(WebViewCb_scroll_event), browser); + g_signal_connect( + browser->webView, "permission-request", G_CALLBACK(WebViewCb_permission_request), NULL); + g_signal_connect(browser->webView, "decide-policy", G_CALLBACK(WebViewCb_decide_policy), NULL); + g_signal_connect(browser->webView, + "load-failed-with-tls-errors", + G_CALLBACK(WebViewCb_load_failed_with_tls_errors), + browser); + g_signal_connect(browser->webView, "load-changed", G_CALLBACK(WebViewCb_load_changed), browser); + + /* signals for search widget */ + g_signal_connect(browser->search, "next-match", G_CALLBACK(SearchEntryCb_next__match), browser); + g_signal_connect( + browser->search, "previous-match", G_CALLBACK(SearchEntryCb_previous__match), browser); + g_signal_connect( + browser->search, "search-changed", G_CALLBACK(SearchEntryCb_search__changed), browser); + g_signal_connect(browser->search, "stop-search", G_CALLBACK(SearchEntryCb_stop__search), browser); + + /* signals for box container */ + g_signal_connect(browser->box, "key-press-event", G_CALLBACK(boxCb_key_press_event), browser); + + if(old_browser == NULL) webkit_web_view_load_uri(browser->webView, target_url); + + return browser; +} + +/* goodwolf_new_tab: Inserts struct Client *browser in GtkNotebook *notebook + * and optionally switches selected tab to it. + * + * returns: + * 0 : Ran successfully + * -1 : Failed to insert a page for browser->box + * -2 : browser is NULL + */ +int +goodwolf_new_tab(GtkNotebook *notebook, struct Client *browser, bool auto_switch) +{ + gint current_page = gtk_notebook_get_current_page(notebook); + gchar *title = _("New tab"); + + if(browser == NULL) return -2; + + gtk_widget_show_all(browser->box); + + if(gtk_notebook_insert_page(notebook, browser->box, NULL, (current_page + 1)) == -1) return -1; + + gtk_notebook_set_tab_reorderable(notebook, browser->box, TRUE); + gtk_notebook_set_tab_label(notebook, browser->box, goodwolf_new_tab_box(title, browser)); + gtk_notebook_set_menu_label_text(GTK_NOTEBOOK(notebook), browser->box, title); + + gtk_widget_queue_draw(GTK_WIDGET(notebook)); + + if(auto_switch) + { + gtk_notebook_set_current_page(notebook, gtk_notebook_page_num(notebook, browser->box)); + } + + return 0; +} + +static void +new_tabCb_clicked(GtkButton *new_tab, gpointer user_data) +{ + (void)new_tab; + struct Window *window = (struct Window *)user_data; + struct Client *browser = new_browser(window, NULL, NULL); + + goodwolf_new_tab(GTK_NOTEBOOK(window->notebook), browser, TRUE); +} + +static void +closeCb_clicked(GtkButton *close, gpointer user_data) +{ + (void)close; + struct Client *browser = (struct Client *)user_data; + + webkit_web_view_try_close(browser->webView); +} + +static void +notebookCb_switch__page(GtkNotebook *notebook, GtkWidget *page, guint page_num, gpointer user_data) +{ + (void)page_num; + struct Window *window = (struct Window *)user_data; + GtkWidget *label = gtk_notebook_get_tab_label(notebook, page); + + // TODO: Maybe find a better way to store the title + gtk_window_set_title(GTK_WINDOW(window->main_window), gtk_widget_get_tooltip_text(label)); +} + +void +content_managerCb_ready(GObject *store, GAsyncResult *result, gpointer user_data) +{ + (void)store; + struct Window *window = (struct Window *)user_data; + GError *err = NULL; + + WebKitUserContentFilter *filter = + webkit_user_content_filter_store_load_finish(window->content_store, result, &err); + + if(filter == NULL) + { + if(err == NULL) + { + fprintf(stderr, _("goodwolf: failed to load content-filter, err: [%d] %s\n"), -1, "unknown"); + } + else + { + fprintf(stderr, + _("goodwolf: failed to load content-filter, err: [%d] %s\n"), + err->code, + err->message); + } + } + else + { + fprintf(stderr, _("goodwolf: content-filter loaded, adding to content-manager…\n")); + webkit_user_content_manager_add_filter(window->content_manager, filter); + } +} + +static void +storeCb_finish(WebKitUserContentFilterStore *store, GAsyncResult *result, gpointer user_data) +{ + (void)store; + struct Window *window = (struct Window *)user_data; + GError *err = NULL; + + WebKitUserContentFilter *filter = + webkit_user_content_filter_store_save_finish(window->content_store, result, &err); + + if(filter == NULL) + { + if(err == NULL) + { + fprintf(stderr, + _("goodwolf: failed to compile content-filters.json, err: [%d] %s\n"), + -1, + "unknown"); + } + else + { + fprintf(stderr, + _("goodwolf: failed to compile content-filters.json, err: [%d] %s\n"), + err->code, + err->message); + } + } + else + { + webkit_user_content_filter_store_load( + window->content_store, "a", NULL, content_managerCb_ready, window); + } +} + +void +tray_icon_on_click(GtkStatusIcon *status_icon, gpointer main_window_ptr) +{ + GtkWidget *main_window = GTK_WIDGET(main_window_ptr); + if (gtk_widget_is_visible(main_window)) + gtk_widget_hide(main_window); + else + gtk_widget_show(main_window); +} + +static GtkStatusIcon *create_tray_icon(const struct Window *window) { + GtkStatusIcon *tray_icon; + + tray_icon = gtk_status_icon_new(); + g_signal_connect(G_OBJECT(tray_icon), "activate", G_CALLBACK(tray_icon_on_click), window->main_window); + gtk_status_icon_set_from_icon_name(tray_icon, "goodwolf"); + gtk_status_icon_set_tooltip_text(tray_icon, "Badwolf"); + gtk_status_icon_set_visible(tray_icon, TRUE); + + return tray_icon; +} + +int +main(int argc, char *argv[]) +{ + if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'h') { + printf("\ +goodwolf [OPTIONS] [URLs]\n\n\ +Options:\n\ +-s DD Specifies a custom favicon size, where DD is a two digit number\n\ +-nl Tabs won't have a label (page title)\n\ +-cl Tabs will have a context label (text that identifies every tab)\n\ +-c You won't be able to add or remove any tabs, other than those specified\n\ + as parameters\n\ +-t Adds a tray icon. Clicking it toggles visibility of the browser window.\n\ +-u All tabs will use an alternative UserAgent\n\ +-p Persistent storage, meaning non-session cookies will be stored on drive\n\ +-g Separated storage, meaning persistent storage will be done separately for\n\ + each tab context. Doesn't do anything if the -p option isn't selected.\n" + ); + return 0; + } + + struct Window *window = &(struct Window){NULL, NULL, NULL, NULL, NULL, NULL}; + GApplication *application; + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, DATADIR "/locale"); + bind_textdomain_codeset(PACKAGE, "UTF-8"); + textdomain(PACKAGE); + + application = g_application_new("me.hacktivis.goodwolf", + G_APPLICATION_HANDLES_COMMAND_LINE | + G_APPLICATION_SEND_ENVIRONMENT | G_APPLICATION_NON_UNIQUE); + g_application_register(application, NULL, NULL); + //g_application_activate(application); + + gtk_init(&argc, &argv); + + fprintf(stderr, _("Running Badwolf version: %s\n"), version); + fprintf(stderr, + _("Buildtime WebKit version: %d.%d.%d\n"), + WEBKIT_MAJOR_VERSION, + WEBKIT_MINOR_VERSION, + WEBKIT_MICRO_VERSION); + fprintf(stderr, + _("Runtime WebKit version: %d.%d.%d\n"), + webkit_get_major_version(), + webkit_get_minor_version(), + webkit_get_micro_version()); + + web_extensions_directory = + g_build_filename(g_get_user_data_dir(), "goodwolf", "webkit-web-extension", NULL); + fprintf(stderr, _("webkit-web-extension directory set to: %s\n"), web_extensions_directory); + + bookmarks_completion_model = bookmarks_completion_init(); + g_object_ref(bookmarks_completion_model); + + window->main_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + window->notebook = gtk_notebook_new(); + window->new_tab = gtk_button_new_from_icon_name("tab-new-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR); + window->downloads_tab = goodwolf_downloads_tab_new(); + window->content_manager = webkit_user_content_manager_new(); + + gchar *contentFilterPath = + g_build_filename(g_get_user_config_dir(), g_get_prgname(), "content-filters.json", NULL); + GFile *contentFilterFile = g_file_new_for_path(contentFilterPath); + fprintf(stderr, _("content-filters file set to: %s\n"), contentFilterPath); + + gchar *filtersPath = g_build_filename(g_get_user_cache_dir(), g_get_prgname(), "filters", NULL); + window->content_store = webkit_user_content_filter_store_new(filtersPath); + + webkit_user_content_filter_store_save_from_file(window->content_store, + "a", + contentFilterFile, + NULL, + (GAsyncReadyCallback)storeCb_finish, + window); + + gtk_window_set_default_size( + GTK_WINDOW(window->main_window), GOODWOLF_DEFAULT_WIDTH, GOODWOLF_DEFAULT_HEIGHT); + gtk_window_set_role(GTK_WINDOW(window->main_window), "browser"); + gtk_window_set_icon_name(GTK_WINDOW(window->main_window), "goodwolf"); + + gchar *provider_path_app = g_build_filename(DATADIR, "interface.css", NULL); + /* flawfinder: ignore, just a presence check */ + if(access(provider_path_app, R_OK) == 0) + { + GtkCssProvider *css_provider_app = gtk_css_provider_new(); + gtk_css_provider_load_from_path(css_provider_app, provider_path_app, NULL); + gtk_style_context_add_provider_for_screen( + gtk_widget_get_screen(GTK_WIDGET(window->main_window)), + GTK_STYLE_PROVIDER(css_provider_app), + GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + } + g_free(provider_path_app); + + gchar *provider_path_user = + g_build_filename(g_get_user_data_dir(), "goodwolf", "interface.css", NULL); + /* flawfinder: ignore, just a presence check */ + if(access(provider_path_user, R_OK) == 0) + { + GtkCssProvider *css_provider_user = gtk_css_provider_new(); + gtk_css_provider_load_from_path(css_provider_user, provider_path_user, NULL); + gtk_style_context_add_provider_for_screen( + gtk_widget_get_screen(GTK_WIDGET(window->main_window)), + GTK_STYLE_PROVIDER(css_provider_user), + GTK_STYLE_PROVIDER_PRIORITY_USER); + } + g_free(provider_path_user); + + gtk_widget_set_tooltip_text(window->new_tab, _("Open new tab")); + + gtk_notebook_set_action_widget(GTK_NOTEBOOK(window->notebook), window->new_tab, GTK_PACK_END); + gtk_notebook_set_scrollable(GTK_NOTEBOOK(window->notebook), TRUE); + gtk_notebook_set_tab_pos(GTK_NOTEBOOK(window->notebook), GOODWOLF_TAB_POSITION); + gtk_notebook_popup_enable(GTK_NOTEBOOK(window->notebook)); + + gtk_container_add(GTK_CONTAINER(window->main_window), window->notebook); + gtk_widget_queue_draw(window->notebook); + + goodwolf_downloads_tab_attach(window); + + g_signal_connect( + window->main_window, "key-press-event", G_CALLBACK(main_windowCb_key_press_event), window); + + g_signal_connect(window->main_window, "destroy", G_CALLBACK(gtk_main_quit), NULL); + g_signal_connect(window->new_tab, "clicked", G_CALLBACK(new_tabCb_clicked), window); + g_signal_connect(window->notebook, "switch-page", G_CALLBACK(notebookCb_switch__page), window); + + while (argc > 1 && argv[1][0] == '-') { + if (argv[1][1] == 'n' && argv[1][2] == 'l') + tabs_no_label = TRUE; + else if (argv[1][1] == 'c' && argv[1][2] == 'l') + tabs_context_label = TRUE; + else if (argv[1][1] == 'c') + tabs_constant = TRUE; + else if (argv[1][1] == 't') + tray_enabled = TRUE; + else if (argv[1][1] == 'u') + alt_useragent = TRUE; + else if (argv[1][1] == 'p') + persistent_storage = TRUE; + else if (argv[1][1] == 'g') + separated_storage = TRUE; + else if (argv[1][1] == 's') { + argv++; + argc--; + custom_favicon_size = (argv[1][0] - '0') * 10 + argv[1][1] - '0'; + } + + argv++; + argc--; + } + + if (!tabs_constant) + gtk_widget_show(window->new_tab); + gtk_widget_show_all(window->main_window); + + if(argc == 1) + goodwolf_new_tab(GTK_NOTEBOOK(window->notebook), new_browser(window, NULL, NULL), FALSE); + else + for(int i = 1; i < argc; ++i) + goodwolf_new_tab(GTK_NOTEBOOK(window->notebook), new_browser(window, argv[i], NULL), TRUE); + + gtk_notebook_set_current_page(GTK_NOTEBOOK(window->notebook), 1); + + if (tray_enabled) + create_tray_icon(window); + + gtk_main(); + + g_object_unref(bookmarks_completion_model); + +#if 0 + /* TRANSLATOR Ignore this entry. Done for forcing Unicode in xgettext. */ + _("ø"); +#endif + + return 0; +} |