logo

badwolf

minimalist and privacy-oriented web browser based on WebKitGTK git clone https://hacktivis.me/git/badwolf.git

badwolf.c (45286B)


  1. // BadWolf: Minimalist and privacy-oriented WebKitGTK+ browser
  2. // SPDX-FileCopyrightText: 2019-2024 Badwolf Authors <https://hacktivis.me/projects/badwolf>
  3. // SPDX-License-Identifier: BSD-3-Clause
  4. #include "badwolf.h"
  5. #include "bookmarks.h"
  6. #include "config.h"
  7. #include "downloads.h"
  8. #include "fmt.h"
  9. #include "keybindings.h"
  10. #include "uri.h"
  11. #include "userscripts.h"
  12. #include <assert.h>
  13. #include <glib/gi18n.h> /* _() and other internationalization/localization helpers */
  14. #include <libsoup/soup.h> /* soup* */
  15. #include <locale.h> /* LC_* */
  16. #include <stdio.h> /* perror(), fprintf(), snprintf() */
  17. #include <stdlib.h> /* malloc() */
  18. #include <unistd.h> /* access(), getopt() */
  19. const gchar *homepage = "https://hacktivis.me/projects/badwolf";
  20. const gchar *version = VERSION;
  21. struct Window *window = NULL;
  22. static gchar *web_extensions_directory;
  23. static uint64_t context_id_counter = 0;
  24. GtkTreeModel *bookmarks_completion_model;
  25. bool opt_S = false, opt_i = false;
  26. static gboolean WebViewCb_close(WebKitWebView *webView, gpointer user_data);
  27. static gboolean WebViewCb_web_process_terminated(WebKitWebView *webView,
  28. WebKitWebProcessTerminationReason reason,
  29. gpointer user_data);
  30. static gboolean
  31. WebViewCb_notify__uri(WebKitWebView *webView, GParamSpec *pspec, gpointer user_data);
  32. static gboolean
  33. WebViewCb_notify__title(WebKitWebView *webView, GParamSpec *pspec, gpointer user_data);
  34. static gboolean
  35. WebViewCb_notify__is__playing__audio(WebKitWebView *webView, GParamSpec *pspec, gpointer user_data);
  36. static gboolean WebViewCb_notify__estimated_load_progress(WebKitWebView *webView,
  37. GParamSpec *pspec,
  38. gpointer user_data);
  39. static gboolean WebViewCb_mouse_target_changed(WebKitWebView *webView,
  40. WebKitHitTestResult *hit,
  41. guint modifiers,
  42. gpointer user_data);
  43. static WebKitWebView *WebViewCb_create(WebKitWebView *related_web_view,
  44. WebKitNavigationAction *navigation_action,
  45. gpointer user_data);
  46. static gboolean WebViewCb_permission_request(WebKitWebView *web_view,
  47. WebKitPermissionRequest *request,
  48. gpointer user_data);
  49. static gboolean WebViewCb_decide_policy(WebKitWebView *web_view,
  50. WebKitPolicyDecision *decision,
  51. WebKitPolicyDecisionType decision_type,
  52. gpointer user_data);
  53. static void
  54. WebViewCb_load_changed(WebKitWebView *webView, WebKitLoadEvent load_event, gpointer user_data);
  55. static void web_contextCb_download_started(WebKitWebContext *web_context,
  56. WebKitDownload *download,
  57. gpointer user_data);
  58. static gboolean locationCb_activate(GtkEntry *location, gpointer user_data);
  59. static gboolean javascriptCb_toggled(GtkButton *javascript, gpointer user_data);
  60. static gboolean auto_load_imagesCb_toggled(GtkButton *auto_load_images, gpointer user_data);
  61. static void backCb_clicked(GtkButton *back, gpointer user_data);
  62. static void forwardCb_clicked(GtkButton *forward, gpointer user_data);
  63. static void printCb_clicked(GtkButton *forward, gpointer user_data);
  64. static gboolean SearchEntryCb_next__match(GtkSearchEntry *search, gpointer user_data);
  65. static gboolean SearchEntryCb_previous__match(GtkSearchEntry *search, gpointer user_data);
  66. static gboolean SearchEntryCb_search__changed(GtkSearchEntry *search, gpointer user_data);
  67. static gboolean SearchEntryCb_stop__search(GtkSearchEntry *search, gpointer user_data);
  68. static void new_tabCb_clicked(GtkButton *new_tab, gpointer user_data);
  69. static void closeCb_clicked(GtkButton *close, gpointer user_data);
  70. static void
  71. notebookCb_switch__page(GtkNotebook *notebook, GtkWidget *page, guint page_num, gpointer user_data);
  72. void content_managerCb_ready(GObject *store, GAsyncResult *result, gpointer user_data);
  73. static gboolean
  74. WebViewCb_close(WebKitWebView *UNUSED(webView), gpointer user_data)
  75. {
  76. struct Client *browser = (struct Client *)user_data;
  77. gtk_widget_destroy(browser->box);
  78. free(browser);
  79. return TRUE;
  80. }
  81. static gboolean
  82. WebViewCb_web_process_terminated(WebKitWebView *UNUSED(webView),
  83. WebKitWebProcessTerminationReason reason,
  84. gpointer user_data)
  85. {
  86. struct Client *browser = (struct Client *)user_data;
  87. switch(reason)
  88. {
  89. case WEBKIT_WEB_PROCESS_CRASHED:
  90. fprintf(stderr, "%s", _("the web process crashed.\n"));
  91. webView_tab_label_change(browser, _("Crashed"));
  92. break;
  93. case WEBKIT_WEB_PROCESS_EXCEEDED_MEMORY_LIMIT:
  94. fprintf(stderr, "%s", _("the web process exceeded the memory limit.\n"));
  95. webView_tab_label_change(browser, _("Out of Memory"));
  96. break;
  97. case WEBKIT_WEB_PROCESS_TERMINATED_BY_API:
  98. fprintf(stderr, "%s", _("the web process was user terminated.\n"));
  99. webView_tab_label_change(browser, _("User terminated"));
  100. break;
  101. default:
  102. fprintf(stderr, "%s", _("the web process terminated for an unknown reason.\n"));
  103. webView_tab_label_change(browser, _("Unknown Crash"));
  104. }
  105. return FALSE;
  106. }
  107. static gboolean
  108. WebViewCb_notify__uri(WebKitWebView *UNUSED(webView), GParamSpec *UNUSED(pspec), gpointer user_data)
  109. {
  110. const gchar *location_uri;
  111. struct Client *browser = (struct Client *)user_data;
  112. location_uri = webkit_web_view_get_uri(browser->webView);
  113. printf("location_uri: <%s>\n", location_uri);
  114. // Don't set if location_uri is NULL / empty, latter happens on WebProcess termination
  115. if(location_uri == NULL || location_uri[0] == '\0')
  116. {
  117. return TRUE;
  118. }
  119. gtk_entry_set_text(GTK_ENTRY(browser->location), location_uri);
  120. if(webkit_uri_for_display(location_uri) != location_uri)
  121. gtk_widget_set_tooltip_text(browser->location, webkit_uri_for_display(location_uri));
  122. else
  123. gtk_widget_set_has_tooltip(browser->location, false);
  124. return TRUE;
  125. }
  126. GtkWidget *
  127. badwolf_new_tab_box(const gchar *title, struct Client *browser)
  128. {
  129. /* flawfinder: ignore. bound checks are done */
  130. char context_id_str[BADWOLF_CTX_SIZ] = {0, 0, 0, 0, 0, 0, 0};
  131. fmt_context_id(browser->context_id, context_id_str);
  132. GtkWidget *tab_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
  133. gtk_widget_set_name(tab_box, "browser__tabbox");
  134. GtkWidget *close =
  135. gtk_button_new_from_icon_name("window-close-symbolic", GTK_ICON_SIZE_LARGE_TOOLBAR);
  136. gtk_widget_set_name(close, "browser__tabbox__close");
  137. GtkWidget *label = gtk_label_new(title);
  138. gtk_widget_set_name(label, "browser__tabbox__label");
  139. GtkWidget *label_event_box = gtk_event_box_new();
  140. gtk_container_add(GTK_CONTAINER(label_event_box), label);
  141. GtkWidget *context_label = gtk_label_new(context_id_str);
  142. gtk_widget_set_name(context_label, "browser__tabbox__context_label");
  143. GtkWidget *context_label_event_box = gtk_event_box_new();
  144. gtk_container_add(GTK_CONTAINER(context_label_event_box), context_label);
  145. GtkWidget *playing =
  146. gtk_image_new_from_icon_name("audio-volume-high-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR);
  147. gtk_widget_set_name(playing, "browser__tabbox__playing");
  148. GtkWidget *playing_event_box = gtk_event_box_new();
  149. gtk_container_add(GTK_CONTAINER(playing_event_box), playing);
  150. #ifdef BADWOLF_TAB_BOX_WIDTH
  151. gtk_widget_set_size_request(label, BADWOLF_TAB_BOX_WIDTH, -1);
  152. #endif
  153. #ifdef BADWOLF_TAB_LABEL_CHARWIDTH
  154. gtk_label_set_width_chars(GTK_LABEL(label), BADWOLF_TAB_LABEL_CHARWIDTH);
  155. #endif
  156. gtk_widget_set_hexpand(tab_box, BADWOLF_TAB_HEXPAND);
  157. gtk_label_set_ellipsize(GTK_LABEL(label), BADWOLF_TAB_LABEL_ELLIPSIZE);
  158. gtk_label_set_single_line_mode(GTK_LABEL(label), TRUE);
  159. gtk_label_set_single_line_mode(GTK_LABEL(context_label), TRUE);
  160. gtk_box_pack_start(GTK_BOX(tab_box), context_label_event_box, TRUE, TRUE, 0);
  161. gtk_box_pack_start(GTK_BOX(tab_box), playing_event_box, FALSE, FALSE, 0);
  162. gtk_box_pack_start(GTK_BOX(tab_box), label_event_box, TRUE, TRUE, 0);
  163. gtk_box_pack_start(GTK_BOX(tab_box), close, FALSE, FALSE, 0);
  164. gtk_button_set_relief(GTK_BUTTON(close), GTK_RELIEF_NONE);
  165. g_signal_connect(close, "clicked", G_CALLBACK(closeCb_clicked), browser);
  166. gtk_widget_set_tooltip_text(tab_box, title);
  167. gtk_widget_show_all(tab_box);
  168. gtk_widget_set_visible(playing, webkit_web_view_is_playing_audio(browser->webView));
  169. gtk_widget_set_events(label, GDK_BUTTON_RELEASE_MASK);
  170. g_signal_connect(
  171. tab_box, "button-release-event", G_CALLBACK(tab_boxCb_button_release_event), browser);
  172. return tab_box;
  173. }
  174. static gboolean
  175. WebViewCb_notify__title(WebKitWebView *UNUSED(webView),
  176. GParamSpec *UNUSED(pspec),
  177. gpointer user_data)
  178. {
  179. struct Client *browser = (struct Client *)user_data;
  180. // Don't set if title is NULL / empty, latter happens on WebProcess crash
  181. const char *title = webkit_web_view_get_title(browser->webView);
  182. printf("title: <%s>\n", title);
  183. if(title == NULL || title[0] == '\0')
  184. {
  185. return TRUE;
  186. }
  187. webView_tab_label_change(browser, NULL);
  188. return TRUE;
  189. }
  190. static gboolean
  191. WebViewCb_notify__is__playing__audio(WebKitWebView *UNUSED(webView),
  192. GParamSpec *UNUSED(pspec),
  193. gpointer user_data)
  194. {
  195. struct Client *browser = (struct Client *)user_data;
  196. webView_tab_label_change(browser, NULL);
  197. return TRUE;
  198. }
  199. void
  200. webView_tab_label_change(struct Client *browser, const gchar *title)
  201. {
  202. GtkWidget *notebook = window->notebook;
  203. #define title_IS_EMPTY title == NULL || title[0] == '\0'
  204. if(title_IS_EMPTY) title = webkit_web_view_get_title(browser->webView);
  205. if(title_IS_EMPTY) title = webkit_web_view_get_uri(browser->webView);
  206. if(title_IS_EMPTY) title = _("Empty Title");
  207. gtk_notebook_set_tab_label(
  208. GTK_NOTEBOOK(notebook), browser->box, badwolf_new_tab_box(title, browser));
  209. gtk_notebook_set_menu_label_text(GTK_NOTEBOOK(notebook), browser->box, title);
  210. // Set the window title if the title change was on the current tab
  211. if(gtk_notebook_page_num(GTK_NOTEBOOK(notebook), browser->box) ==
  212. gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook)))
  213. gtk_window_set_title(GTK_WINDOW(window->main_window), title);
  214. }
  215. static gboolean
  216. WebViewCb_notify__estimated_load_progress(WebKitWebView *UNUSED(webView),
  217. GParamSpec *UNUSED(pspec),
  218. gpointer user_data)
  219. {
  220. struct Client *browser = (struct Client *)user_data;
  221. gdouble progress;
  222. progress = webkit_web_view_get_estimated_load_progress(browser->webView);
  223. if(progress >= 1) progress = 0;
  224. gtk_entry_set_progress_fraction(GTK_ENTRY(browser->location), progress);
  225. return TRUE;
  226. }
  227. static gboolean
  228. WebViewCb_mouse_target_changed(WebKitWebView *UNUSED(webView),
  229. WebKitHitTestResult *hit,
  230. guint UNUSED(modifiers),
  231. gpointer user_data)
  232. {
  233. struct Client *browser = (struct Client *)user_data;
  234. if(webkit_hit_test_result_context_is_link(hit))
  235. {
  236. const gchar *link_uri = webkit_hit_test_result_get_link_uri(hit);
  237. gtk_label_set_text(GTK_LABEL(browser->statuslabel), webkit_uri_for_display(link_uri));
  238. }
  239. else
  240. gtk_label_set_text(GTK_LABEL(browser->statuslabel), NULL);
  241. return FALSE;
  242. }
  243. static gboolean
  244. WebViewCb_scroll_event(GtkWidget *UNUSED(widget), GdkEvent *event, gpointer data)
  245. {
  246. struct Client *browser = (struct Client *)data;
  247. gdouble delta_x, delta_y;
  248. gdouble zoom;
  249. if(((GdkEventScroll *)event)->state & GDK_CONTROL_MASK)
  250. {
  251. gdk_event_get_scroll_deltas(event, &delta_x, &delta_y);
  252. zoom = webkit_web_view_get_zoom_level(WEBKIT_WEB_VIEW(browser->webView));
  253. zoom -= delta_y * 0.1;
  254. webkit_web_view_set_zoom_level(WEBKIT_WEB_VIEW(browser->webView), zoom);
  255. return TRUE;
  256. }
  257. return FALSE;
  258. }
  259. static WebKitWebView *
  260. WebViewCb_create(WebKitWebView *related_web_view,
  261. WebKitNavigationAction *UNUSED(navigation_action),
  262. gpointer user_data)
  263. {
  264. struct Client *old_browser = (struct Client *)user_data;
  265. struct Client *browser = NULL;
  266. // shouldn't be needed but better be safe
  267. old_browser->webView = related_web_view;
  268. browser = new_browser(NULL, old_browser);
  269. if(badwolf_new_tab(GTK_NOTEBOOK(window->notebook), browser, FALSE) < 0)
  270. return NULL;
  271. else
  272. return browser->webView;
  273. }
  274. static gboolean
  275. WebViewCb_permission_request(WebKitWebView *UNUSED(web_view),
  276. WebKitPermissionRequest *request,
  277. gpointer UNUSED(user_data))
  278. {
  279. webkit_permission_request_deny(request);
  280. return TRUE; /* Stop other handlers */
  281. }
  282. static gboolean
  283. WebViewCb_decide_policy(WebKitWebView *UNUSED(web_view),
  284. WebKitPolicyDecision *decision,
  285. WebKitPolicyDecisionType decision_type,
  286. gpointer user_data)
  287. {
  288. struct Client *old_browser = (struct Client *)user_data;
  289. WebKitResponsePolicyDecision *r;
  290. WebKitNavigationPolicyDecision *n;
  291. WebKitNavigationAction *navigation_action;
  292. switch(decision_type)
  293. {
  294. case WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION:
  295. n = WEBKIT_NAVIGATION_POLICY_DECISION(decision);
  296. navigation_action = webkit_navigation_policy_decision_get_navigation_action(n);
  297. if(GDK_CONTROL_MASK == webkit_navigation_action_get_modifiers(navigation_action) ||
  298. 2 == webkit_navigation_action_get_mouse_button(navigation_action))
  299. {
  300. WebKitURIRequest *uri = webkit_navigation_action_get_request(navigation_action);
  301. const gchar *target_url = webkit_uri_request_get_uri(uri);
  302. struct Client *browser = new_browser(target_url, old_browser);
  303. int ret = badwolf_new_tab(GTK_NOTEBOOK(window->notebook), browser, FALSE);
  304. if(ret < 0)
  305. {
  306. fprintf(stderr,
  307. "badwolf: Error while opening <%s> in a new tab (badwolf_new_tab returned %d)\n",
  308. target_url,
  309. ret);
  310. return FALSE;
  311. }
  312. webkit_web_view_load_uri(browser->webView, target_url);
  313. webkit_policy_decision_ignore(decision);
  314. }
  315. else
  316. {
  317. /* Use whatever default there is. */
  318. return FALSE;
  319. }
  320. break;
  321. case WEBKIT_POLICY_DECISION_TYPE_RESPONSE:
  322. r = WEBKIT_RESPONSE_POLICY_DECISION(decision);
  323. if(!webkit_response_policy_decision_is_mime_type_supported(r))
  324. webkit_policy_decision_download(decision);
  325. else
  326. webkit_policy_decision_use(decision);
  327. break;
  328. default:
  329. /* Use whatever default there is. */
  330. return FALSE;
  331. }
  332. return TRUE;
  333. }
  334. static void
  335. WebViewCb_load_changed(WebKitWebView *UNUSED(webView),
  336. WebKitLoadEvent UNUSED(load_event),
  337. gpointer user_data)
  338. {
  339. struct Client *browser = (struct Client *)user_data;
  340. gtk_widget_set_sensitive(browser->back, webkit_web_view_can_go_back(browser->webView));
  341. gtk_widget_set_sensitive(browser->forward, webkit_web_view_can_go_forward(browser->webView));
  342. }
  343. static char *
  344. detail_tls_certificate_flags(GTlsCertificateFlags tls_errors)
  345. {
  346. GString *errors = g_string_new(NULL);
  347. g_string_append_printf(errors,
  348. _("Couldn't verify the TLS certificate to ensure a better security of the "
  349. "connection. You might want to verify your machine and network.\n\n"));
  350. if(tls_errors & G_TLS_CERTIFICATE_UNKNOWN_CA)
  351. g_string_append_printf(errors, _("Error: The X509 Certificate Authority is unknown.\n"));
  352. if(tls_errors & G_TLS_CERTIFICATE_BAD_IDENTITY)
  353. g_string_append(errors, _("Error: The given identity doesn't match the expected one.\n"));
  354. if(tls_errors & G_TLS_CERTIFICATE_NOT_ACTIVATED)
  355. g_string_append(errors,
  356. _("Error: The certificate isn't valid yet. Check your system's clock.\n"));
  357. if(tls_errors & G_TLS_CERTIFICATE_EXPIRED)
  358. g_string_append(errors, _("Error: The certificate has expired. Check your system's clock.\n"));
  359. if(tls_errors & G_TLS_CERTIFICATE_REVOKED)
  360. g_string_append(errors, _("Error: The certificate has been revoked.\n"));
  361. if(tls_errors & G_TLS_CERTIFICATE_INSECURE)
  362. g_string_append(errors, _("Error: The certificate is considered to be insecure.\n"));
  363. if(tls_errors & G_TLS_CERTIFICATE_GENERIC_ERROR)
  364. g_string_append(errors, _("Error: Some unknown error occurred validating the certificate.\n"));
  365. return g_string_free(errors, FALSE);
  366. }
  367. static gboolean
  368. WebViewCb_load_failed_with_tls_errors(WebKitWebView *UNUSED(web_view),
  369. gchar *failing_text,
  370. GTlsCertificate *certificate,
  371. GTlsCertificateFlags errors,
  372. gpointer user_data)
  373. {
  374. struct Client *browser = (struct Client *)user_data;
  375. gchar *error_details = detail_tls_certificate_flags(errors);
  376. gint dialog_response;
  377. #ifndef USE_LIBSOUP2
  378. GUri *failing_uri = g_uri_parse(failing_text, G_URI_FLAGS_NONE, NULL);
  379. #else
  380. SoupURI *failing_uri = soup_uri_new(failing_text);
  381. #endif
  382. GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(window->main_window),
  383. GTK_DIALOG_MODAL & GTK_DIALOG_DESTROY_WITH_PARENT,
  384. GTK_MESSAGE_ERROR,
  385. GTK_BUTTONS_NONE,
  386. _("TLS Error for %s."),
  387. failing_text);
  388. gtk_dialog_add_buttons(
  389. GTK_DIALOG(dialog), _("Temporarily Add Exception"), 1, _("Continue"), 0, NULL);
  390. if(!failing_uri)
  391. {
  392. gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog), 1, FALSE);
  393. }
  394. gtk_dialog_set_default_response(GTK_DIALOG(dialog), 0);
  395. gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), "%s\n", error_details);
  396. dialog_response = gtk_dialog_run(GTK_DIALOG(dialog));
  397. if(dialog_response == 1)
  398. {
  399. #ifndef USE_LIBSOUP2
  400. webkit_web_context_allow_tls_certificate_for_host(
  401. webkit_web_view_get_context(browser->webView), certificate, g_uri_get_host(failing_uri));
  402. #else
  403. webkit_web_context_allow_tls_certificate_for_host(
  404. webkit_web_view_get_context(browser->webView), certificate, failing_uri->host);
  405. #endif
  406. webkit_web_view_reload(browser->webView);
  407. }
  408. #ifndef USE_LIBSOUP2
  409. /* Calling g_free(failing_uri) ought to be the correct way but this causes a segfault.
  410. *
  411. * - documentation describes it as something which should be free
  412. * - implementation seems to make it a pointer that should be freed
  413. * - epiphany doesn't seems to free/unref it but gnome code seems to frequently have memleaks
  414. *
  415. * Decided to at least continue to try with using g_uri_unref(failing_uri) instead.
  416. * Related fediverse post: <https://queer.hacktivis.me/objects/cec7c4e8-6a58-4358-85bf-b66f9bb21a98>
  417. */
  418. g_uri_unref(failing_uri);
  419. #else
  420. soup_uri_free(failing_uri);
  421. #endif
  422. g_free(error_details);
  423. gtk_widget_destroy(dialog);
  424. return FALSE; /* propagate the event further */
  425. }
  426. static void
  427. web_contextCb_download_started(WebKitWebContext *UNUSED(web_context),
  428. WebKitDownload *webkit_download,
  429. gpointer user_data)
  430. {
  431. struct Download *download = malloc(sizeof(struct Download));
  432. assert(webkit_download);
  433. if(download != NULL)
  434. {
  435. download_new_entry(webkit_download, download);
  436. g_signal_connect(
  437. G_OBJECT(webkit_download), "received-data", G_CALLBACK(downloadCb_received_data), download);
  438. g_signal_connect(G_OBJECT(webkit_download),
  439. "created-destination",
  440. G_CALLBACK(downloadCb_created_destination),
  441. download);
  442. g_signal_connect(G_OBJECT(webkit_download), "failed", G_CALLBACK(downloadCb_failed), download);
  443. g_signal_connect(
  444. G_OBJECT(webkit_download), "finished", G_CALLBACK(downloadCb_finished), download);
  445. }
  446. g_signal_connect(G_OBJECT(webkit_download),
  447. "decide-destination",
  448. G_CALLBACK(downloadCb_decide_destination),
  449. user_data);
  450. }
  451. static gboolean
  452. locationCb_activate(GtkEntry *location, gpointer user_data)
  453. {
  454. struct Client *browser = (struct Client *)user_data;
  455. webkit_web_view_load_uri(browser->webView,
  456. badwolf_ensure_uri_scheme(gtk_entry_get_text(location), TRUE));
  457. return TRUE;
  458. }
  459. static gboolean
  460. javascriptCb_toggled(GtkButton *javascript, gpointer user_data)
  461. {
  462. struct Client *browser = (struct Client *)user_data;
  463. WebKitSettings *settings = webkit_web_view_get_settings(browser->webView);
  464. webkit_settings_set_enable_javascript_markup(
  465. settings, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(javascript)));
  466. webkit_web_view_set_settings(browser->webView, settings);
  467. return TRUE;
  468. }
  469. static gboolean
  470. auto_load_imagesCb_toggled(GtkButton *auto_load_images, gpointer user_data)
  471. {
  472. struct Client *browser = (struct Client *)user_data;
  473. WebKitSettings *settings = webkit_web_view_get_settings(browser->webView);
  474. webkit_settings_set_auto_load_images(
  475. settings, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(auto_load_images)));
  476. webkit_web_view_set_settings(browser->webView, settings);
  477. return TRUE;
  478. }
  479. static void
  480. backCb_clicked(GtkButton *UNUSED(back), gpointer user_data)
  481. {
  482. struct Client *browser = (struct Client *)user_data;
  483. webkit_web_view_go_back(browser->webView);
  484. }
  485. static void
  486. forwardCb_clicked(GtkButton *UNUSED(forward), gpointer user_data)
  487. {
  488. struct Client *browser = (struct Client *)user_data;
  489. webkit_web_view_go_forward(browser->webView);
  490. }
  491. static void
  492. printCb_clicked(GtkButton *UNUSED(print), gpointer user_data)
  493. {
  494. struct Client *browser = (struct Client *)user_data;
  495. WebKitPrintOperation *print_operation = webkit_print_operation_new(browser->webView);
  496. webkit_print_operation_run_dialog(print_operation, GTK_WINDOW(window->main_window));
  497. }
  498. static gboolean
  499. SearchEntryCb_next__match(GtkSearchEntry *UNUSED(search), gpointer user_data)
  500. {
  501. struct Client *browser = (struct Client *)user_data;
  502. WebKitFindController *findController = webkit_web_view_get_find_controller(browser->webView);
  503. webkit_find_controller_search_next(findController);
  504. return TRUE;
  505. }
  506. static gboolean
  507. SearchEntryCb_previous__match(GtkSearchEntry *UNUSED(search), gpointer user_data)
  508. {
  509. struct Client *browser = (struct Client *)user_data;
  510. WebKitFindController *findController = webkit_web_view_get_find_controller(browser->webView);
  511. webkit_find_controller_search_previous(findController);
  512. return TRUE;
  513. }
  514. static gboolean
  515. SearchEntryCb_search__changed(GtkSearchEntry *search, gpointer user_data)
  516. {
  517. struct Client *browser = (struct Client *)user_data;
  518. WebKitFindController *findController = webkit_web_view_get_find_controller(browser->webView);
  519. const gchar *search_text = gtk_entry_get_text(GTK_ENTRY(search));
  520. webkit_find_controller_search(findController, search_text, 0, 0);
  521. return TRUE;
  522. }
  523. static gboolean
  524. SearchEntryCb_stop__search(GtkSearchEntry *UNUSED(search), gpointer user_data)
  525. {
  526. struct Client *browser = (struct Client *)user_data;
  527. WebKitFindController *findController = webkit_web_view_get_find_controller(browser->webView);
  528. webkit_find_controller_search_finish(findController);
  529. return TRUE;
  530. }
  531. static gboolean
  532. widgetCb_drop_button3_event(GtkWidget *UNUSED(widget), GdkEvent *event, gpointer UNUSED(user_data))
  533. {
  534. // Button3 being right-click on right-handed mode, left-click on left-handed mode
  535. return ((GdkEventButton *)event)->button == 3;
  536. }
  537. struct Client *
  538. new_browser(const gchar *target_url, struct Client *old_browser)
  539. {
  540. struct Client *browser = malloc(sizeof(struct Client));
  541. target_url = badwolf_ensure_uri_scheme(target_url, (old_browser == NULL));
  542. char *badwolf_l10n = NULL;
  543. WebKitWebContext *web_context = NULL;
  544. if(browser == NULL) return NULL;
  545. assert(window != NULL);
  546. browser->context_id = old_browser == NULL ? context_id_counter++ : old_browser->context_id;
  547. browser->box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
  548. gtk_widget_set_name(browser->box, "browser__box");
  549. browser->toolbar = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
  550. gtk_widget_set_name(browser->toolbar, "browser__toolbar");
  551. browser->back =
  552. gtk_button_new_from_icon_name("go-previous-symbolic", GTK_ICON_SIZE_LARGE_TOOLBAR);
  553. gtk_widget_set_name(browser->back, "browser__back");
  554. browser->forward = gtk_button_new_from_icon_name("go-next-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR);
  555. gtk_widget_set_name(browser->forward, "browser__forward");
  556. GtkWidget *toolbar_separator = gtk_separator_new(GTK_ORIENTATION_VERTICAL);
  557. browser->javascript = gtk_toggle_button_new_with_mnemonic(_("_JS"));
  558. gtk_widget_set_name(browser->javascript, "browser__javascript");
  559. gtk_widget_set_tooltip_text(browser->javascript, _("Toggle javascript"));
  560. gtk_button_set_relief(GTK_BUTTON(browser->javascript), GTK_RELIEF_NONE);
  561. browser->auto_load_images = gtk_toggle_button_new_with_mnemonic(_("_IMG"));
  562. gtk_widget_set_name(browser->auto_load_images, "browser__load_images");
  563. gtk_widget_set_tooltip_text(browser->auto_load_images, _("Toggle loading images automatically"));
  564. gtk_button_set_relief(GTK_BUTTON(browser->auto_load_images), GTK_RELIEF_NONE);
  565. browser->location = gtk_entry_new();
  566. gtk_widget_set_name(browser->location, "browser__location");
  567. GtkWidget *print =
  568. gtk_button_new_from_icon_name("document-print-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR);
  569. gtk_widget_set_name(browser->back, "browser__print");
  570. browser->statusbar = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
  571. gtk_widget_set_name(browser->statusbar, "browser__statusbar");
  572. browser->search = gtk_search_entry_new();
  573. gtk_widget_set_name(browser->search, "browser__search");
  574. browser->statuslabel = gtk_label_new(NULL);
  575. gtk_widget_set_name(browser->statuslabel, "browser__statuslabel");
  576. if(old_browser == NULL)
  577. {
  578. WebKitWebsiteDataManager *website_data_manager = webkit_website_data_manager_new_ephemeral();
  579. webkit_website_data_manager_set_itp_enabled(website_data_manager, TRUE);
  580. web_context = webkit_web_context_new_with_website_data_manager(website_data_manager);
  581. g_object_unref(website_data_manager);
  582. webkit_web_context_set_sandbox_enabled(web_context, TRUE);
  583. webkit_web_context_set_web_extensions_directory(web_context, web_extensions_directory);
  584. g_signal_connect(G_OBJECT(web_context),
  585. "download-started",
  586. G_CALLBACK(web_contextCb_download_started),
  587. browser);
  588. /* flawfinder: ignore. Consider that g_strsplit is safe enough */
  589. badwolf_l10n = getenv("BADWOLF_L10N");
  590. if(badwolf_l10n != NULL)
  591. {
  592. gchar **languages = g_strsplit(badwolf_l10n, ":", -1);
  593. webkit_web_context_set_spell_checking_languages(web_context, (const gchar *const *)languages);
  594. g_strfreev(languages);
  595. webkit_web_context_set_spell_checking_enabled(web_context, TRUE);
  596. }
  597. webkit_web_context_register_uri_scheme(
  598. web_context, "man", man_uri_scheme_request_cb, NULL, NULL);
  599. }
  600. WebKitSettings *settings = webkit_settings_new_with_settings(BADWOLF_WEBKIT_SETTINGS);
  601. if(opt_S) webkit_settings_set_enable_javascript_markup(settings, true);
  602. if(opt_i) webkit_settings_set_auto_load_images(settings, false);
  603. gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(browser->javascript),
  604. webkit_settings_get_enable_javascript_markup(settings));
  605. gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(browser->auto_load_images),
  606. webkit_settings_get_auto_load_images(settings));
  607. browser->webView = WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW,
  608. "web-context",
  609. web_context,
  610. "related-view",
  611. old_browser == NULL ? NULL : old_browser->webView,
  612. "settings",
  613. settings,
  614. "user-content-manager",
  615. window->content_manager,
  616. NULL));
  617. gtk_widget_set_name(GTK_WIDGET(browser->webView), "browser__webView");
  618. if(old_browser == NULL)
  619. {
  620. g_object_unref(web_context);
  621. }
  622. g_object_unref(settings);
  623. gtk_box_pack_start(
  624. GTK_BOX(browser->toolbar), GTK_WIDGET(browser->back), FALSE, FALSE, BADWOLF_TOOLBAR_PADDING);
  625. gtk_box_pack_start(GTK_BOX(browser->toolbar),
  626. GTK_WIDGET(browser->forward),
  627. FALSE,
  628. FALSE,
  629. BADWOLF_TOOLBAR_PADDING);
  630. gtk_box_pack_start(GTK_BOX(browser->toolbar),
  631. toolbar_separator,
  632. FALSE,
  633. FALSE,
  634. BADWOLF_TOOLBAR_SEPARATOR_PADDING);
  635. gtk_box_pack_start(GTK_BOX(browser->toolbar),
  636. GTK_WIDGET(browser->javascript),
  637. FALSE,
  638. FALSE,
  639. BADWOLF_TOOLBAR_PADDING);
  640. gtk_box_pack_start(GTK_BOX(browser->toolbar),
  641. GTK_WIDGET(browser->auto_load_images),
  642. FALSE,
  643. FALSE,
  644. BADWOLF_TOOLBAR_PADDING);
  645. gtk_box_pack_start(GTK_BOX(browser->toolbar),
  646. GTK_WIDGET(browser->location),
  647. TRUE,
  648. TRUE,
  649. BADWOLF_TOOLBAR_PADDING);
  650. gtk_box_pack_start(GTK_BOX(browser->toolbar), print, FALSE, FALSE, BADWOLF_TOOLBAR_PADDING);
  651. gtk_container_set_focus_child(GTK_CONTAINER(browser->box), browser->toolbar);
  652. gtk_container_set_focus_child(GTK_CONTAINER(browser->toolbar), browser->location);
  653. gtk_box_pack_start(
  654. GTK_BOX(browser->box), GTK_WIDGET(browser->toolbar), FALSE, FALSE, BADWOLF_BOX_PADDING);
  655. gtk_box_pack_start(
  656. GTK_BOX(browser->box), GTK_WIDGET(browser->webView), TRUE, TRUE, BADWOLF_BOX_PADDING);
  657. gtk_box_pack_start(
  658. GTK_BOX(browser->box), GTK_WIDGET(browser->statusbar), FALSE, FALSE, BADWOLF_BOX_PADDING);
  659. gtk_box_pack_start(GTK_BOX(browser->statusbar),
  660. GTK_WIDGET(browser->search),
  661. FALSE,
  662. FALSE,
  663. BADWOLF_STATUSBAR_PADDING);
  664. gtk_box_pack_start(GTK_BOX(browser->statusbar),
  665. GTK_WIDGET(browser->statuslabel),
  666. FALSE,
  667. FALSE,
  668. BADWOLF_STATUSBAR_PADDING);
  669. gtk_widget_set_halign(browser->statusbar, GTK_ALIGN_START);
  670. gtk_label_set_single_line_mode(GTK_LABEL(browser->statuslabel), TRUE);
  671. gtk_label_set_ellipsize(GTK_LABEL(browser->statuslabel), BADWOLF_STATUSLABEL_ELLIPSIZE);
  672. if(bookmarks_completion_model != NULL)
  673. {
  674. GtkEntryCompletion *location_completion = gtk_entry_completion_new();
  675. GtkTreeModel *location_completion_model = bookmarks_completion_model;
  676. bookmarks_completion_setup(location_completion, location_completion_model);
  677. gtk_entry_set_completion(GTK_ENTRY(browser->location), location_completion);
  678. }
  679. gtk_entry_set_text(GTK_ENTRY(browser->location), target_url);
  680. gtk_entry_set_input_purpose(GTK_ENTRY(browser->location), GTK_INPUT_PURPOSE_URL);
  681. gtk_entry_set_placeholder_text(GTK_ENTRY(browser->search), _("search in current page"));
  682. /* signals for back/forward buttons */
  683. g_signal_connect(browser->back, "clicked", G_CALLBACK(backCb_clicked), browser);
  684. g_signal_connect(browser->forward, "clicked", G_CALLBACK(forwardCb_clicked), browser);
  685. /* prevents GtkNotebook from spawning it's context-menu */
  686. g_signal_connect(
  687. browser->back, "button-press-event", G_CALLBACK(widgetCb_drop_button3_event), NULL);
  688. g_signal_connect(
  689. browser->back, "button-release-event", G_CALLBACK(widgetCb_drop_button3_event), NULL);
  690. g_signal_connect(
  691. browser->forward, "button-press-event", G_CALLBACK(widgetCb_drop_button3_event), NULL);
  692. g_signal_connect(
  693. browser->forward, "button-release-event", G_CALLBACK(widgetCb_drop_button3_event), NULL);
  694. /* signals for javascript toggle widget */
  695. g_signal_connect(browser->javascript, "toggled", G_CALLBACK(javascriptCb_toggled), browser);
  696. /* prevents GtkNotebook from spawning it's context-menu */
  697. g_signal_connect(
  698. browser->javascript, "button-press-event", G_CALLBACK(widgetCb_drop_button3_event), NULL);
  699. g_signal_connect(
  700. browser->javascript, "button-release-event", G_CALLBACK(widgetCb_drop_button3_event), NULL);
  701. /* signals for auto_load_images toggle widget */
  702. g_signal_connect(
  703. browser->auto_load_images, "toggled", G_CALLBACK(auto_load_imagesCb_toggled), browser);
  704. /* prevents GtkNotebook from spawning it's context-menu */
  705. g_signal_connect(browser->auto_load_images,
  706. "button-press-event",
  707. G_CALLBACK(widgetCb_drop_button3_event),
  708. NULL);
  709. g_signal_connect(browser->auto_load_images,
  710. "button-release-event",
  711. G_CALLBACK(widgetCb_drop_button3_event),
  712. NULL);
  713. /* signals for location entry widget */
  714. g_signal_connect(browser->location, "activate", G_CALLBACK(locationCb_activate), browser);
  715. /* signals for print button */
  716. g_signal_connect(print, "clicked", G_CALLBACK(printCb_clicked), browser);
  717. /* prevents GtkNotebook from spawning it's context-menu */
  718. g_signal_connect(print, "button-press-event", G_CALLBACK(widgetCb_drop_button3_event), NULL);
  719. g_signal_connect(print, "button-release-event", G_CALLBACK(widgetCb_drop_button3_event), NULL);
  720. /* signals for WebView widget */
  721. g_signal_connect(browser->webView,
  722. "web-process-terminated",
  723. G_CALLBACK(WebViewCb_web_process_terminated),
  724. browser);
  725. g_signal_connect(browser->webView, "notify::uri", G_CALLBACK(WebViewCb_notify__uri), browser);
  726. g_signal_connect(browser->webView, "notify::title", G_CALLBACK(WebViewCb_notify__title), browser);
  727. g_signal_connect(browser->webView,
  728. "notify::is-playing-audio",
  729. G_CALLBACK(WebViewCb_notify__is__playing__audio),
  730. browser);
  731. g_signal_connect(browser->webView,
  732. "mouse-target-changed",
  733. G_CALLBACK(WebViewCb_mouse_target_changed),
  734. browser);
  735. g_signal_connect(browser->webView,
  736. "notify::estimated-load-progress",
  737. G_CALLBACK(WebViewCb_notify__estimated_load_progress),
  738. browser);
  739. g_signal_connect(browser->webView, "create", G_CALLBACK(WebViewCb_create), browser);
  740. g_signal_connect(browser->webView, "close", G_CALLBACK(WebViewCb_close), browser);
  741. g_signal_connect(
  742. browser->webView, "key-press-event", G_CALLBACK(WebViewCb_key_press_event), browser);
  743. g_signal_connect(browser->webView, "scroll-event", G_CALLBACK(WebViewCb_scroll_event), browser);
  744. g_signal_connect(
  745. browser->webView, "permission-request", G_CALLBACK(WebViewCb_permission_request), NULL);
  746. g_signal_connect(browser->webView, "decide-policy", G_CALLBACK(WebViewCb_decide_policy), browser);
  747. g_signal_connect(browser->webView,
  748. "load-failed-with-tls-errors",
  749. G_CALLBACK(WebViewCb_load_failed_with_tls_errors),
  750. browser);
  751. g_signal_connect(browser->webView, "load-changed", G_CALLBACK(WebViewCb_load_changed), browser);
  752. /* signals for search widget */
  753. g_signal_connect(browser->search, "next-match", G_CALLBACK(SearchEntryCb_next__match), browser);
  754. g_signal_connect(
  755. browser->search, "previous-match", G_CALLBACK(SearchEntryCb_previous__match), browser);
  756. g_signal_connect(
  757. browser->search, "search-changed", G_CALLBACK(SearchEntryCb_search__changed), browser);
  758. g_signal_connect(browser->search, "stop-search", G_CALLBACK(SearchEntryCb_stop__search), browser);
  759. /* signals for box container */
  760. g_signal_connect(browser->box, "key-press-event", G_CALLBACK(boxCb_key_press_event), browser);
  761. if(old_browser == NULL) webkit_web_view_load_uri(browser->webView, target_url);
  762. return browser;
  763. }
  764. /* badwolf_new_tab: Inserts struct Client *browser in GtkNotebook *notebook
  765. * and optionally switches selected tab to it.
  766. *
  767. * returns:
  768. * 0 : Ran successfully
  769. * -1 : Failed to insert a page for browser->box
  770. * -2 : browser is NULL
  771. */
  772. int
  773. badwolf_new_tab(GtkNotebook *notebook, struct Client *browser, bool auto_switch)
  774. {
  775. gint current_page = gtk_notebook_get_current_page(notebook);
  776. gchar *title = _("New tab");
  777. if(browser == NULL) return -2;
  778. gtk_widget_show_all(browser->box);
  779. if(gtk_notebook_insert_page(notebook, browser->box, NULL, (current_page + 1)) == -1) return -1;
  780. gtk_notebook_set_tab_reorderable(notebook, browser->box, TRUE);
  781. gtk_notebook_set_tab_label(notebook, browser->box, badwolf_new_tab_box(title, browser));
  782. gtk_notebook_set_menu_label_text(GTK_NOTEBOOK(notebook), browser->box, title);
  783. gtk_widget_queue_draw(GTK_WIDGET(notebook));
  784. if(auto_switch)
  785. {
  786. gtk_notebook_set_current_page(notebook, gtk_notebook_page_num(notebook, browser->box));
  787. }
  788. return 0;
  789. }
  790. static void
  791. new_tabCb_clicked(GtkButton *UNUSED(new_tab), gpointer UNUSED(user_data))
  792. {
  793. struct Client *browser = new_browser(NULL, NULL);
  794. badwolf_new_tab(GTK_NOTEBOOK(window->notebook), browser, TRUE);
  795. }
  796. static void
  797. closeCb_clicked(GtkButton *UNUSED(close), gpointer user_data)
  798. {
  799. struct Client *browser = (struct Client *)user_data;
  800. webkit_web_view_try_close(browser->webView);
  801. }
  802. static void
  803. notebookCb_switch__page(GtkNotebook *notebook,
  804. GtkWidget *page,
  805. guint UNUSED(page_num),
  806. gpointer UNUSED(user_data))
  807. {
  808. GtkWidget *label = gtk_notebook_get_tab_label(notebook, page);
  809. // TODO: Maybe find a better way to store the title
  810. gtk_window_set_title(GTK_WINDOW(window->main_window), gtk_widget_get_tooltip_text(label));
  811. }
  812. void
  813. content_managerCb_ready(GObject *UNUSED(store), GAsyncResult *result, gpointer UNUSED(user_data))
  814. {
  815. GError *err = NULL;
  816. WebKitUserContentFilter *filter =
  817. webkit_user_content_filter_store_load_finish(window->content_store, result, &err);
  818. if(filter == NULL)
  819. {
  820. if(err == NULL)
  821. {
  822. fprintf(stderr, _("badwolf: failed to load content-filter, err: [%d] %s\n"), -1, "unknown");
  823. }
  824. else
  825. {
  826. fprintf(stderr,
  827. _("badwolf: failed to load content-filter, err: [%d] %s\n"),
  828. err->code,
  829. err->message);
  830. }
  831. }
  832. else
  833. {
  834. fprintf(stderr, _("badwolf: content-filter loaded, adding to content-manager…\n"));
  835. webkit_user_content_manager_add_filter(window->content_manager, filter);
  836. }
  837. }
  838. static void
  839. storeCb_finish(WebKitUserContentFilterStore *UNUSED(store),
  840. GAsyncResult *result,
  841. gpointer UNUSED(user_data))
  842. {
  843. GError *err = NULL;
  844. WebKitUserContentFilter *filter =
  845. webkit_user_content_filter_store_save_finish(window->content_store, result, &err);
  846. if(filter == NULL)
  847. {
  848. if(err == NULL)
  849. {
  850. fprintf(stderr,
  851. _("badwolf: failed to compile content-filters.json, err: [%d] %s\n"),
  852. -1,
  853. "unknown");
  854. }
  855. else
  856. {
  857. fprintf(stderr,
  858. _("badwolf: failed to compile content-filters.json, err: [%d] %s\n"),
  859. err->code,
  860. err->message);
  861. }
  862. }
  863. else
  864. {
  865. webkit_user_content_filter_store_load(
  866. window->content_store, "a", NULL, content_managerCb_ready, window);
  867. }
  868. }
  869. int
  870. main(int argc, char *argv[])
  871. {
  872. GApplication *application;
  873. errno = 0;
  874. setlocale(LC_ALL, "");
  875. if(errno != 0)
  876. {
  877. fprintf(stderr, "badwolf: Warning: Failed to initialize locales: %s\n", strerror(errno));
  878. errno = 0;
  879. }
  880. bindtextdomain(PACKAGE, DATADIR "/locale");
  881. bind_textdomain_codeset(PACKAGE, "UTF-8");
  882. textdomain(PACKAGE);
  883. application = g_application_new("me.hacktivis.badwolf",
  884. G_APPLICATION_HANDLES_COMMAND_LINE |
  885. G_APPLICATION_SEND_ENVIRONMENT | G_APPLICATION_NON_UNIQUE);
  886. g_application_register(application, NULL, NULL);
  887. //g_application_activate(application);
  888. gtk_init(&argc, &argv);
  889. int c = EOF;
  890. while((c = getopt(argc, argv, "iS")) != EOF)
  891. {
  892. switch(c)
  893. {
  894. case 'i':
  895. opt_i = true;
  896. break;
  897. case 'S':
  898. opt_S = true;
  899. break;
  900. case '?':
  901. fprintf(stderr, _("badwolf: Unrecognized option: '-%c'\n"), optopt);
  902. return 1;
  903. }
  904. }
  905. argc -= optind;
  906. argv += optind;
  907. fprintf(stderr, _("Running Badwolf version: %s\n"), version);
  908. fprintf(stderr,
  909. _("Buildtime WebKit version: %d.%d.%d\n"),
  910. WEBKIT_MAJOR_VERSION,
  911. WEBKIT_MINOR_VERSION,
  912. WEBKIT_MICRO_VERSION);
  913. fprintf(stderr,
  914. _("Runtime WebKit version: %d.%d.%d\n"),
  915. webkit_get_major_version(),
  916. webkit_get_minor_version(),
  917. webkit_get_micro_version());
  918. web_extensions_directory =
  919. g_build_filename(g_get_user_data_dir(), "badwolf", "webkit-web-extension", NULL);
  920. fprintf(stderr, _("webkit-web-extension directory set to: %s\n"), web_extensions_directory);
  921. bookmarks_completion_model = bookmarks_completion_init();
  922. g_object_ref(bookmarks_completion_model);
  923. gchar *filtersPath = g_build_filename(g_get_user_cache_dir(), g_get_prgname(), "filters", NULL);
  924. window = &(struct Window){
  925. .main_window = gtk_window_new(GTK_WINDOW_TOPLEVEL),
  926. .notebook = gtk_notebook_new(),
  927. .new_tab = gtk_button_new_from_icon_name("tab-new-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR),
  928. .downloads_tab = badwolf_downloads_tab_new(),
  929. .content_manager = webkit_user_content_manager_new(),
  930. .content_store = webkit_user_content_filter_store_new(filtersPath),
  931. };
  932. load_userscripts(window->content_manager);
  933. assert(window != NULL);
  934. gchar *contentFilterPath =
  935. g_build_filename(g_get_user_config_dir(), g_get_prgname(), "content-filters.json", NULL);
  936. GFile *contentFilterFile = g_file_new_for_path(contentFilterPath);
  937. fprintf(stderr, _("content-filters file set to: %s\n"), contentFilterPath);
  938. webkit_user_content_filter_store_save_from_file(window->content_store,
  939. "a",
  940. contentFilterFile,
  941. NULL,
  942. (GAsyncReadyCallback)storeCb_finish,
  943. window);
  944. gtk_window_set_default_size(
  945. GTK_WINDOW(window->main_window), BADWOLF_DEFAULT_WIDTH, BADWOLF_DEFAULT_HEIGHT);
  946. gtk_window_set_role(GTK_WINDOW(window->main_window), "browser");
  947. gtk_window_set_icon_name(GTK_WINDOW(window->main_window), "badwolf");
  948. gchar *provider_path_app = g_build_filename(DATADIR, "interface.css", NULL);
  949. /* flawfinder: ignore, just a presence check */
  950. if(access(provider_path_app, R_OK) == 0)
  951. {
  952. GtkCssProvider *css_provider_app = gtk_css_provider_new();
  953. gtk_css_provider_load_from_path(css_provider_app, provider_path_app, NULL);
  954. gtk_style_context_add_provider_for_screen(
  955. gtk_widget_get_screen(GTK_WIDGET(window->main_window)),
  956. GTK_STYLE_PROVIDER(css_provider_app),
  957. GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
  958. }
  959. g_free(provider_path_app);
  960. gchar *provider_path_user =
  961. g_build_filename(g_get_user_data_dir(), "badwolf", "interface.css", NULL);
  962. /* flawfinder: ignore, just a presence check */
  963. if(access(provider_path_user, R_OK) == 0)
  964. {
  965. GtkCssProvider *css_provider_user = gtk_css_provider_new();
  966. gtk_css_provider_load_from_path(css_provider_user, provider_path_user, NULL);
  967. gtk_style_context_add_provider_for_screen(
  968. gtk_widget_get_screen(GTK_WIDGET(window->main_window)),
  969. GTK_STYLE_PROVIDER(css_provider_user),
  970. GTK_STYLE_PROVIDER_PRIORITY_USER);
  971. }
  972. g_free(provider_path_user);
  973. gtk_widget_set_tooltip_text(window->new_tab, _("Open new tab"));
  974. gtk_notebook_set_action_widget(GTK_NOTEBOOK(window->notebook), window->new_tab, GTK_PACK_END);
  975. gtk_notebook_set_scrollable(GTK_NOTEBOOK(window->notebook), TRUE);
  976. gtk_notebook_set_tab_pos(GTK_NOTEBOOK(window->notebook), BADWOLF_TAB_POSITION);
  977. gtk_notebook_popup_enable(GTK_NOTEBOOK(window->notebook));
  978. gtk_container_add(GTK_CONTAINER(window->main_window), window->notebook);
  979. gtk_widget_queue_draw(window->notebook);
  980. badwolf_downloads_tab_attach();
  981. g_signal_connect(
  982. window->main_window, "key-press-event", G_CALLBACK(main_windowCb_key_press_event), NULL);
  983. g_signal_connect(window->main_window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
  984. g_signal_connect(window->new_tab, "clicked", G_CALLBACK(new_tabCb_clicked), NULL);
  985. g_signal_connect(window->notebook, "switch-page", G_CALLBACK(notebookCb_switch__page), NULL);
  986. gtk_widget_show(window->new_tab);
  987. gtk_widget_show_all(window->main_window);
  988. if(argc == 0)
  989. badwolf_new_tab(GTK_NOTEBOOK(window->notebook), new_browser(NULL, NULL), FALSE);
  990. else
  991. for(int i = 0; i < argc; ++i)
  992. badwolf_new_tab(GTK_NOTEBOOK(window->notebook), new_browser(argv[i], NULL), FALSE);
  993. gtk_notebook_set_current_page(GTK_NOTEBOOK(window->notebook), 1);
  994. gtk_main();
  995. g_object_unref(bookmarks_completion_model);
  996. #if 0
  997. /* TRANSLATOR Ignore this entry. Done for forcing Unicode in xgettext. */
  998. _("ø");
  999. #endif
  1000. return 0;
  1001. }