badwolf.c 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207
  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. badwolf_new_tab(GTK_NOTEBOOK(window->notebook), browser, FALSE);
  304. webkit_web_view_load_uri(browser->webView, target_url);
  305. webkit_policy_decision_ignore(decision);
  306. }
  307. else
  308. {
  309. /* Use whatever default there is. */
  310. return FALSE;
  311. }
  312. break;
  313. case WEBKIT_POLICY_DECISION_TYPE_RESPONSE:
  314. r = WEBKIT_RESPONSE_POLICY_DECISION(decision);
  315. if(!webkit_response_policy_decision_is_mime_type_supported(r))
  316. webkit_policy_decision_download(decision);
  317. else
  318. webkit_policy_decision_use(decision);
  319. break;
  320. default:
  321. /* Use whatever default there is. */
  322. return FALSE;
  323. }
  324. return TRUE;
  325. }
  326. static void
  327. WebViewCb_load_changed(WebKitWebView *UNUSED(webView),
  328. WebKitLoadEvent UNUSED(load_event),
  329. gpointer user_data)
  330. {
  331. struct Client *browser = (struct Client *)user_data;
  332. gtk_widget_set_sensitive(browser->back, webkit_web_view_can_go_back(browser->webView));
  333. gtk_widget_set_sensitive(browser->forward, webkit_web_view_can_go_forward(browser->webView));
  334. }
  335. static char *
  336. detail_tls_certificate_flags(GTlsCertificateFlags tls_errors)
  337. {
  338. GString *errors = g_string_new(NULL);
  339. g_string_append_printf(errors,
  340. _("Couldn't verify the TLS certificate to ensure a better security of the "
  341. "connection. You might want to verify your machine and network.\n\n"));
  342. if(tls_errors & G_TLS_CERTIFICATE_UNKNOWN_CA)
  343. g_string_append_printf(errors, _("Error: The X509 Certificate Authority is unknown.\n"));
  344. if(tls_errors & G_TLS_CERTIFICATE_BAD_IDENTITY)
  345. g_string_append(errors, _("Error: The given identity doesn't match the expected one.\n"));
  346. if(tls_errors & G_TLS_CERTIFICATE_NOT_ACTIVATED)
  347. g_string_append(errors,
  348. _("Error: The certificate isn't valid yet. Check your system's clock.\n"));
  349. if(tls_errors & G_TLS_CERTIFICATE_EXPIRED)
  350. g_string_append(errors, _("Error: The certificate has expired. Check your system's clock.\n"));
  351. if(tls_errors & G_TLS_CERTIFICATE_REVOKED)
  352. g_string_append(errors, _("Error: The certificate has been revoked.\n"));
  353. if(tls_errors & G_TLS_CERTIFICATE_INSECURE)
  354. g_string_append(errors, _("Error: The certificate is considered to be insecure.\n"));
  355. if(tls_errors & G_TLS_CERTIFICATE_GENERIC_ERROR)
  356. g_string_append(errors, _("Error: Some unknown error occurred validating the certificate.\n"));
  357. return g_string_free(errors, FALSE);
  358. }
  359. static gboolean
  360. WebViewCb_load_failed_with_tls_errors(WebKitWebView *UNUSED(web_view),
  361. gchar *failing_text,
  362. GTlsCertificate *certificate,
  363. GTlsCertificateFlags errors,
  364. gpointer user_data)
  365. {
  366. struct Client *browser = (struct Client *)user_data;
  367. gchar *error_details = detail_tls_certificate_flags(errors);
  368. gint dialog_response;
  369. #ifndef USE_LIBSOUP2
  370. GUri *failing_uri = g_uri_parse(failing_text, G_URI_FLAGS_NONE, NULL);
  371. #else
  372. SoupURI *failing_uri = soup_uri_new(failing_text);
  373. #endif
  374. GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(window->main_window),
  375. GTK_DIALOG_MODAL & GTK_DIALOG_DESTROY_WITH_PARENT,
  376. GTK_MESSAGE_ERROR,
  377. GTK_BUTTONS_NONE,
  378. _("TLS Error for %s."),
  379. failing_text);
  380. gtk_dialog_add_buttons(
  381. GTK_DIALOG(dialog), _("Temporarily Add Exception"), 1, _("Continue"), 0, NULL);
  382. if(!failing_uri)
  383. {
  384. gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog), 1, FALSE);
  385. }
  386. gtk_dialog_set_default_response(GTK_DIALOG(dialog), 0);
  387. gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), "%s\n", error_details);
  388. dialog_response = gtk_dialog_run(GTK_DIALOG(dialog));
  389. if(dialog_response == 1)
  390. {
  391. #ifndef USE_LIBSOUP2
  392. webkit_web_context_allow_tls_certificate_for_host(
  393. webkit_web_view_get_context(browser->webView), certificate, g_uri_get_host(failing_uri));
  394. #else
  395. webkit_web_context_allow_tls_certificate_for_host(
  396. webkit_web_view_get_context(browser->webView), certificate, failing_uri->host);
  397. #endif
  398. webkit_web_view_reload(browser->webView);
  399. }
  400. #ifndef USE_LIBSOUP2
  401. /* Calling g_free(failing_uri) ought to be the correct way but this causes a segfault.
  402. *
  403. * - documentation describes it as something which should be free
  404. * - implementation seems to make it a pointer that should be freed
  405. * - epiphany doesn't seems to free/unref it but gnome code seems to frequently have memleaks
  406. *
  407. * Decided to at least continue to try with using g_uri_unref(failing_uri) instead.
  408. * Related fediverse post: <https://queer.hacktivis.me/objects/cec7c4e8-6a58-4358-85bf-b66f9bb21a98>
  409. */
  410. g_uri_unref(failing_uri);
  411. #else
  412. soup_uri_free(failing_uri);
  413. #endif
  414. g_free(error_details);
  415. gtk_widget_destroy(dialog);
  416. return FALSE; /* propagate the event further */
  417. }
  418. static void
  419. web_contextCb_download_started(WebKitWebContext *UNUSED(web_context),
  420. WebKitDownload *webkit_download,
  421. gpointer user_data)
  422. {
  423. struct Download *download = malloc(sizeof(struct Download));
  424. assert(webkit_download);
  425. if(download != NULL)
  426. {
  427. download_new_entry(webkit_download, download);
  428. g_signal_connect(
  429. G_OBJECT(webkit_download), "received-data", G_CALLBACK(downloadCb_received_data), download);
  430. g_signal_connect(G_OBJECT(webkit_download),
  431. "created-destination",
  432. G_CALLBACK(downloadCb_created_destination),
  433. download);
  434. g_signal_connect(G_OBJECT(webkit_download), "failed", G_CALLBACK(downloadCb_failed), download);
  435. g_signal_connect(
  436. G_OBJECT(webkit_download), "finished", G_CALLBACK(downloadCb_finished), download);
  437. }
  438. g_signal_connect(G_OBJECT(webkit_download),
  439. "decide-destination",
  440. G_CALLBACK(downloadCb_decide_destination),
  441. user_data);
  442. }
  443. static gboolean
  444. locationCb_activate(GtkEntry *location, gpointer user_data)
  445. {
  446. struct Client *browser = (struct Client *)user_data;
  447. webkit_web_view_load_uri(browser->webView,
  448. badwolf_ensure_uri_scheme(gtk_entry_get_text(location), TRUE));
  449. return TRUE;
  450. }
  451. static gboolean
  452. javascriptCb_toggled(GtkButton *javascript, gpointer user_data)
  453. {
  454. struct Client *browser = (struct Client *)user_data;
  455. WebKitSettings *settings = webkit_web_view_get_settings(browser->webView);
  456. webkit_settings_set_enable_javascript_markup(
  457. settings, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(javascript)));
  458. webkit_web_view_set_settings(browser->webView, settings);
  459. return TRUE;
  460. }
  461. static gboolean
  462. auto_load_imagesCb_toggled(GtkButton *auto_load_images, gpointer user_data)
  463. {
  464. struct Client *browser = (struct Client *)user_data;
  465. WebKitSettings *settings = webkit_web_view_get_settings(browser->webView);
  466. webkit_settings_set_auto_load_images(
  467. settings, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(auto_load_images)));
  468. webkit_web_view_set_settings(browser->webView, settings);
  469. return TRUE;
  470. }
  471. static void
  472. backCb_clicked(GtkButton *UNUSED(back), gpointer user_data)
  473. {
  474. struct Client *browser = (struct Client *)user_data;
  475. webkit_web_view_go_back(browser->webView);
  476. }
  477. static void
  478. forwardCb_clicked(GtkButton *UNUSED(forward), gpointer user_data)
  479. {
  480. struct Client *browser = (struct Client *)user_data;
  481. webkit_web_view_go_forward(browser->webView);
  482. }
  483. static void
  484. printCb_clicked(GtkButton *UNUSED(print), gpointer user_data)
  485. {
  486. struct Client *browser = (struct Client *)user_data;
  487. WebKitPrintOperation *print_operation = webkit_print_operation_new(browser->webView);
  488. webkit_print_operation_run_dialog(print_operation, GTK_WINDOW(window->main_window));
  489. }
  490. static gboolean
  491. SearchEntryCb_next__match(GtkSearchEntry *UNUSED(search), gpointer user_data)
  492. {
  493. struct Client *browser = (struct Client *)user_data;
  494. WebKitFindController *findController = webkit_web_view_get_find_controller(browser->webView);
  495. webkit_find_controller_search_next(findController);
  496. return TRUE;
  497. }
  498. static gboolean
  499. SearchEntryCb_previous__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_previous(findController);
  504. return TRUE;
  505. }
  506. static gboolean
  507. SearchEntryCb_search__changed(GtkSearchEntry *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. const gchar *search_text = gtk_entry_get_text(GTK_ENTRY(search));
  512. webkit_find_controller_search(findController, search_text, 0, 0);
  513. return TRUE;
  514. }
  515. static gboolean
  516. SearchEntryCb_stop__search(GtkSearchEntry *UNUSED(search), gpointer user_data)
  517. {
  518. struct Client *browser = (struct Client *)user_data;
  519. WebKitFindController *findController = webkit_web_view_get_find_controller(browser->webView);
  520. webkit_find_controller_search_finish(findController);
  521. return TRUE;
  522. }
  523. static gboolean
  524. widgetCb_drop_button3_event(GtkWidget *UNUSED(widget), GdkEvent *event, gpointer UNUSED(user_data))
  525. {
  526. // Button3 being right-click on right-handed mode, left-click on left-handed mode
  527. return ((GdkEventButton *)event)->button == 3;
  528. }
  529. struct Client *
  530. new_browser(const gchar *target_url, struct Client *old_browser)
  531. {
  532. struct Client *browser = malloc(sizeof(struct Client));
  533. target_url = badwolf_ensure_uri_scheme(target_url, (old_browser == NULL));
  534. char *badwolf_l10n = NULL;
  535. WebKitWebContext *web_context = NULL;
  536. if(browser == NULL) return NULL;
  537. assert(window != NULL);
  538. browser->context_id = old_browser == NULL ? context_id_counter++ : old_browser->context_id;
  539. browser->box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
  540. gtk_widget_set_name(browser->box, "browser__box");
  541. browser->toolbar = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
  542. gtk_widget_set_name(browser->toolbar, "browser__toolbar");
  543. browser->back =
  544. gtk_button_new_from_icon_name("go-previous-symbolic", GTK_ICON_SIZE_LARGE_TOOLBAR);
  545. gtk_widget_set_name(browser->back, "browser__back");
  546. browser->forward = gtk_button_new_from_icon_name("go-next-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR);
  547. gtk_widget_set_name(browser->forward, "browser__forward");
  548. GtkWidget *toolbar_separator = gtk_separator_new(GTK_ORIENTATION_VERTICAL);
  549. browser->javascript = gtk_toggle_button_new_with_mnemonic(_("_JS"));
  550. gtk_widget_set_name(browser->javascript, "browser__javascript");
  551. gtk_widget_set_tooltip_text(browser->javascript, _("Toggle javascript"));
  552. gtk_button_set_relief(GTK_BUTTON(browser->javascript), GTK_RELIEF_NONE);
  553. browser->auto_load_images = gtk_toggle_button_new_with_mnemonic(_("_IMG"));
  554. gtk_widget_set_name(browser->auto_load_images, "browser__load_images");
  555. gtk_widget_set_tooltip_text(browser->auto_load_images, _("Toggle loading images automatically"));
  556. gtk_button_set_relief(GTK_BUTTON(browser->auto_load_images), GTK_RELIEF_NONE);
  557. browser->location = gtk_entry_new();
  558. gtk_widget_set_name(browser->location, "browser__location");
  559. GtkWidget *print =
  560. gtk_button_new_from_icon_name("document-print-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR);
  561. gtk_widget_set_name(browser->back, "browser__print");
  562. browser->statusbar = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
  563. gtk_widget_set_name(browser->statusbar, "browser__statusbar");
  564. browser->search = gtk_search_entry_new();
  565. gtk_widget_set_name(browser->search, "browser__search");
  566. browser->statuslabel = gtk_label_new(NULL);
  567. gtk_widget_set_name(browser->statuslabel, "browser__statuslabel");
  568. if(old_browser == NULL)
  569. {
  570. WebKitWebsiteDataManager *website_data_manager = webkit_website_data_manager_new_ephemeral();
  571. webkit_website_data_manager_set_itp_enabled(website_data_manager, TRUE);
  572. web_context = webkit_web_context_new_with_website_data_manager(website_data_manager);
  573. g_object_unref(website_data_manager);
  574. webkit_web_context_set_sandbox_enabled(web_context, TRUE);
  575. webkit_web_context_set_web_extensions_directory(web_context, web_extensions_directory);
  576. g_signal_connect(G_OBJECT(web_context),
  577. "download-started",
  578. G_CALLBACK(web_contextCb_download_started),
  579. browser);
  580. /* flawfinder: ignore. Consider that g_strsplit is safe enough */
  581. badwolf_l10n = getenv("BADWOLF_L10N");
  582. if(badwolf_l10n != NULL)
  583. {
  584. gchar **languages = g_strsplit(badwolf_l10n, ":", -1);
  585. webkit_web_context_set_spell_checking_languages(web_context, (const gchar *const *)languages);
  586. g_strfreev(languages);
  587. webkit_web_context_set_spell_checking_enabled(web_context, TRUE);
  588. }
  589. }
  590. WebKitSettings *settings = webkit_settings_new_with_settings(BADWOLF_WEBKIT_SETTINGS);
  591. if(opt_S) webkit_settings_set_enable_javascript_markup(settings, true);
  592. if(opt_i) webkit_settings_set_auto_load_images(settings, false);
  593. gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(browser->javascript),
  594. webkit_settings_get_enable_javascript_markup(settings));
  595. gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(browser->auto_load_images),
  596. webkit_settings_get_auto_load_images(settings));
  597. browser->webView = WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW,
  598. "web-context",
  599. web_context,
  600. "related-view",
  601. old_browser == NULL ? NULL : old_browser->webView,
  602. "settings",
  603. settings,
  604. "user-content-manager",
  605. window->content_manager,
  606. NULL));
  607. gtk_widget_set_name(GTK_WIDGET(browser->webView), "browser__webView");
  608. if(old_browser == NULL)
  609. {
  610. g_object_unref(web_context);
  611. }
  612. g_object_unref(settings);
  613. gtk_box_pack_start(
  614. GTK_BOX(browser->toolbar), GTK_WIDGET(browser->back), FALSE, FALSE, BADWOLF_TOOLBAR_PADDING);
  615. gtk_box_pack_start(GTK_BOX(browser->toolbar),
  616. GTK_WIDGET(browser->forward),
  617. FALSE,
  618. FALSE,
  619. BADWOLF_TOOLBAR_PADDING);
  620. gtk_box_pack_start(GTK_BOX(browser->toolbar),
  621. toolbar_separator,
  622. FALSE,
  623. FALSE,
  624. BADWOLF_TOOLBAR_SEPARATOR_PADDING);
  625. gtk_box_pack_start(GTK_BOX(browser->toolbar),
  626. GTK_WIDGET(browser->javascript),
  627. FALSE,
  628. FALSE,
  629. BADWOLF_TOOLBAR_PADDING);
  630. gtk_box_pack_start(GTK_BOX(browser->toolbar),
  631. GTK_WIDGET(browser->auto_load_images),
  632. FALSE,
  633. FALSE,
  634. BADWOLF_TOOLBAR_PADDING);
  635. gtk_box_pack_start(GTK_BOX(browser->toolbar),
  636. GTK_WIDGET(browser->location),
  637. TRUE,
  638. TRUE,
  639. BADWOLF_TOOLBAR_PADDING);
  640. gtk_box_pack_start(GTK_BOX(browser->toolbar), print, FALSE, FALSE, BADWOLF_TOOLBAR_PADDING);
  641. gtk_container_set_focus_child(GTK_CONTAINER(browser->box), browser->toolbar);
  642. gtk_container_set_focus_child(GTK_CONTAINER(browser->toolbar), browser->location);
  643. gtk_box_pack_start(
  644. GTK_BOX(browser->box), GTK_WIDGET(browser->toolbar), FALSE, FALSE, BADWOLF_BOX_PADDING);
  645. gtk_box_pack_start(
  646. GTK_BOX(browser->box), GTK_WIDGET(browser->webView), TRUE, TRUE, BADWOLF_BOX_PADDING);
  647. gtk_box_pack_start(
  648. GTK_BOX(browser->box), GTK_WIDGET(browser->statusbar), FALSE, FALSE, BADWOLF_BOX_PADDING);
  649. gtk_box_pack_start(GTK_BOX(browser->statusbar),
  650. GTK_WIDGET(browser->search),
  651. FALSE,
  652. FALSE,
  653. BADWOLF_STATUSBAR_PADDING);
  654. gtk_box_pack_start(GTK_BOX(browser->statusbar),
  655. GTK_WIDGET(browser->statuslabel),
  656. FALSE,
  657. FALSE,
  658. BADWOLF_STATUSBAR_PADDING);
  659. gtk_widget_set_halign(browser->statusbar, GTK_ALIGN_START);
  660. gtk_label_set_single_line_mode(GTK_LABEL(browser->statuslabel), TRUE);
  661. gtk_label_set_ellipsize(GTK_LABEL(browser->statuslabel), BADWOLF_STATUSLABEL_ELLIPSIZE);
  662. if(bookmarks_completion_model != NULL)
  663. {
  664. GtkEntryCompletion *location_completion = gtk_entry_completion_new();
  665. GtkTreeModel *location_completion_model = bookmarks_completion_model;
  666. bookmarks_completion_setup(location_completion, location_completion_model);
  667. gtk_entry_set_completion(GTK_ENTRY(browser->location), location_completion);
  668. }
  669. gtk_entry_set_text(GTK_ENTRY(browser->location), target_url);
  670. gtk_entry_set_input_purpose(GTK_ENTRY(browser->location), GTK_INPUT_PURPOSE_URL);
  671. gtk_entry_set_placeholder_text(GTK_ENTRY(browser->search), _("search in current page"));
  672. /* signals for back/forward buttons */
  673. g_signal_connect(browser->back, "clicked", G_CALLBACK(backCb_clicked), browser);
  674. g_signal_connect(browser->forward, "clicked", G_CALLBACK(forwardCb_clicked), browser);
  675. /* prevents GtkNotebook from spawning it's context-menu */
  676. g_signal_connect(
  677. browser->back, "button-press-event", G_CALLBACK(widgetCb_drop_button3_event), NULL);
  678. g_signal_connect(
  679. browser->back, "button-release-event", G_CALLBACK(widgetCb_drop_button3_event), NULL);
  680. g_signal_connect(
  681. browser->forward, "button-press-event", G_CALLBACK(widgetCb_drop_button3_event), NULL);
  682. g_signal_connect(
  683. browser->forward, "button-release-event", G_CALLBACK(widgetCb_drop_button3_event), NULL);
  684. /* signals for javacript toggle widget */
  685. g_signal_connect(browser->javascript, "toggled", G_CALLBACK(javascriptCb_toggled), browser);
  686. /* prevents GtkNotebook from spawning it's context-menu */
  687. g_signal_connect(
  688. browser->javascript, "button-press-event", G_CALLBACK(widgetCb_drop_button3_event), NULL);
  689. g_signal_connect(
  690. browser->javascript, "button-release-event", G_CALLBACK(widgetCb_drop_button3_event), NULL);
  691. /* signals for auto_load_images toggle widget */
  692. g_signal_connect(
  693. browser->auto_load_images, "toggled", G_CALLBACK(auto_load_imagesCb_toggled), browser);
  694. /* prevents GtkNotebook from spawning it's context-menu */
  695. g_signal_connect(browser->auto_load_images,
  696. "button-press-event",
  697. G_CALLBACK(widgetCb_drop_button3_event),
  698. NULL);
  699. g_signal_connect(browser->auto_load_images,
  700. "button-release-event",
  701. G_CALLBACK(widgetCb_drop_button3_event),
  702. NULL);
  703. /* signals for location entry widget */
  704. g_signal_connect(browser->location, "activate", G_CALLBACK(locationCb_activate), browser);
  705. /* signals for print button */
  706. g_signal_connect(print, "clicked", G_CALLBACK(printCb_clicked), browser);
  707. /* prevents GtkNotebook from spawning it's context-menu */
  708. g_signal_connect(print, "button-press-event", G_CALLBACK(widgetCb_drop_button3_event), NULL);
  709. g_signal_connect(print, "button-release-event", G_CALLBACK(widgetCb_drop_button3_event), NULL);
  710. /* signals for WebView widget */
  711. g_signal_connect(browser->webView,
  712. "web-process-terminated",
  713. G_CALLBACK(WebViewCb_web_process_terminated),
  714. browser);
  715. g_signal_connect(browser->webView, "notify::uri", G_CALLBACK(WebViewCb_notify__uri), browser);
  716. g_signal_connect(browser->webView, "notify::title", G_CALLBACK(WebViewCb_notify__title), browser);
  717. g_signal_connect(browser->webView,
  718. "notify::is-playing-audio",
  719. G_CALLBACK(WebViewCb_notify__is__playing__audio),
  720. browser);
  721. g_signal_connect(browser->webView,
  722. "mouse-target-changed",
  723. G_CALLBACK(WebViewCb_mouse_target_changed),
  724. browser);
  725. g_signal_connect(browser->webView,
  726. "notify::estimated-load-progress",
  727. G_CALLBACK(WebViewCb_notify__estimated_load_progress),
  728. browser);
  729. g_signal_connect(browser->webView, "create", G_CALLBACK(WebViewCb_create), browser);
  730. g_signal_connect(browser->webView, "close", G_CALLBACK(WebViewCb_close), browser);
  731. g_signal_connect(
  732. browser->webView, "key-press-event", G_CALLBACK(WebViewCb_key_press_event), browser);
  733. g_signal_connect(browser->webView, "scroll-event", G_CALLBACK(WebViewCb_scroll_event), browser);
  734. g_signal_connect(
  735. browser->webView, "permission-request", G_CALLBACK(WebViewCb_permission_request), NULL);
  736. g_signal_connect(browser->webView, "decide-policy", G_CALLBACK(WebViewCb_decide_policy), browser);
  737. g_signal_connect(browser->webView,
  738. "load-failed-with-tls-errors",
  739. G_CALLBACK(WebViewCb_load_failed_with_tls_errors),
  740. browser);
  741. g_signal_connect(browser->webView, "load-changed", G_CALLBACK(WebViewCb_load_changed), browser);
  742. /* signals for search widget */
  743. g_signal_connect(browser->search, "next-match", G_CALLBACK(SearchEntryCb_next__match), browser);
  744. g_signal_connect(
  745. browser->search, "previous-match", G_CALLBACK(SearchEntryCb_previous__match), browser);
  746. g_signal_connect(
  747. browser->search, "search-changed", G_CALLBACK(SearchEntryCb_search__changed), browser);
  748. g_signal_connect(browser->search, "stop-search", G_CALLBACK(SearchEntryCb_stop__search), browser);
  749. /* signals for box container */
  750. g_signal_connect(browser->box, "key-press-event", G_CALLBACK(boxCb_key_press_event), browser);
  751. if(old_browser == NULL) webkit_web_view_load_uri(browser->webView, target_url);
  752. return browser;
  753. }
  754. /* badwolf_new_tab: Inserts struct Client *browser in GtkNotebook *notebook
  755. * and optionally switches selected tab to it.
  756. *
  757. * returns:
  758. * 0 : Ran successfully
  759. * -1 : Failed to insert a page for browser->box
  760. * -2 : browser is NULL
  761. */
  762. int
  763. badwolf_new_tab(GtkNotebook *notebook, struct Client *browser, bool auto_switch)
  764. {
  765. gint current_page = gtk_notebook_get_current_page(notebook);
  766. gchar *title = _("New tab");
  767. if(browser == NULL) return -2;
  768. gtk_widget_show_all(browser->box);
  769. if(gtk_notebook_insert_page(notebook, browser->box, NULL, (current_page + 1)) == -1) return -1;
  770. gtk_notebook_set_tab_reorderable(notebook, browser->box, TRUE);
  771. gtk_notebook_set_tab_label(notebook, browser->box, badwolf_new_tab_box(title, browser));
  772. gtk_notebook_set_menu_label_text(GTK_NOTEBOOK(notebook), browser->box, title);
  773. gtk_widget_queue_draw(GTK_WIDGET(notebook));
  774. if(auto_switch)
  775. {
  776. gtk_notebook_set_current_page(notebook, gtk_notebook_page_num(notebook, browser->box));
  777. }
  778. return 0;
  779. }
  780. static void
  781. new_tabCb_clicked(GtkButton *UNUSED(new_tab), gpointer UNUSED(user_data))
  782. {
  783. struct Client *browser = new_browser(NULL, NULL);
  784. badwolf_new_tab(GTK_NOTEBOOK(window->notebook), browser, TRUE);
  785. }
  786. static void
  787. closeCb_clicked(GtkButton *UNUSED(close), gpointer user_data)
  788. {
  789. struct Client *browser = (struct Client *)user_data;
  790. webkit_web_view_try_close(browser->webView);
  791. }
  792. static void
  793. notebookCb_switch__page(GtkNotebook *notebook,
  794. GtkWidget *page,
  795. guint UNUSED(page_num),
  796. gpointer UNUSED(user_data))
  797. {
  798. GtkWidget *label = gtk_notebook_get_tab_label(notebook, page);
  799. // TODO: Maybe find a better way to store the title
  800. gtk_window_set_title(GTK_WINDOW(window->main_window), gtk_widget_get_tooltip_text(label));
  801. }
  802. void
  803. content_managerCb_ready(GObject *UNUSED(store), GAsyncResult *result, gpointer UNUSED(user_data))
  804. {
  805. GError *err = NULL;
  806. WebKitUserContentFilter *filter =
  807. webkit_user_content_filter_store_load_finish(window->content_store, result, &err);
  808. if(filter == NULL)
  809. {
  810. if(err == NULL)
  811. {
  812. fprintf(stderr, _("badwolf: failed to load content-filter, err: [%d] %s\n"), -1, "unknown");
  813. }
  814. else
  815. {
  816. fprintf(stderr,
  817. _("badwolf: failed to load content-filter, err: [%d] %s\n"),
  818. err->code,
  819. err->message);
  820. }
  821. }
  822. else
  823. {
  824. fprintf(stderr, _("badwolf: content-filter loaded, adding to content-manager…\n"));
  825. webkit_user_content_manager_add_filter(window->content_manager, filter);
  826. }
  827. }
  828. static void
  829. storeCb_finish(WebKitUserContentFilterStore *UNUSED(store),
  830. GAsyncResult *result,
  831. gpointer UNUSED(user_data))
  832. {
  833. GError *err = NULL;
  834. WebKitUserContentFilter *filter =
  835. webkit_user_content_filter_store_save_finish(window->content_store, result, &err);
  836. if(filter == NULL)
  837. {
  838. if(err == NULL)
  839. {
  840. fprintf(stderr,
  841. _("badwolf: failed to compile content-filters.json, err: [%d] %s\n"),
  842. -1,
  843. "unknown");
  844. }
  845. else
  846. {
  847. fprintf(stderr,
  848. _("badwolf: failed to compile content-filters.json, err: [%d] %s\n"),
  849. err->code,
  850. err->message);
  851. }
  852. }
  853. else
  854. {
  855. webkit_user_content_filter_store_load(
  856. window->content_store, "a", NULL, content_managerCb_ready, window);
  857. }
  858. }
  859. int
  860. main(int argc, char *argv[])
  861. {
  862. GApplication *application;
  863. setlocale(LC_ALL, "");
  864. bindtextdomain(PACKAGE, DATADIR "/locale");
  865. bind_textdomain_codeset(PACKAGE, "UTF-8");
  866. textdomain(PACKAGE);
  867. application = g_application_new("me.hacktivis.badwolf",
  868. G_APPLICATION_HANDLES_COMMAND_LINE |
  869. G_APPLICATION_SEND_ENVIRONMENT | G_APPLICATION_NON_UNIQUE);
  870. g_application_register(application, NULL, NULL);
  871. //g_application_activate(application);
  872. gtk_init(&argc, &argv);
  873. int c = EOF;
  874. while((c = getopt(argc, argv, "iS")) != EOF)
  875. {
  876. switch(c)
  877. {
  878. case 'i':
  879. opt_i = true;
  880. break;
  881. case 'S':
  882. opt_S = true;
  883. break;
  884. case '?':
  885. fprintf(stderr, _("badwolf: Unrecognized option: '-%c'\n"), optopt);
  886. return 1;
  887. }
  888. }
  889. argc -= optind;
  890. argv += optind;
  891. fprintf(stderr, _("Running Badwolf version: %s\n"), version);
  892. fprintf(stderr,
  893. _("Buildtime WebKit version: %d.%d.%d\n"),
  894. WEBKIT_MAJOR_VERSION,
  895. WEBKIT_MINOR_VERSION,
  896. WEBKIT_MICRO_VERSION);
  897. fprintf(stderr,
  898. _("Runtime WebKit version: %d.%d.%d\n"),
  899. webkit_get_major_version(),
  900. webkit_get_minor_version(),
  901. webkit_get_micro_version());
  902. web_extensions_directory =
  903. g_build_filename(g_get_user_data_dir(), "badwolf", "webkit-web-extension", NULL);
  904. fprintf(stderr, _("webkit-web-extension directory set to: %s\n"), web_extensions_directory);
  905. bookmarks_completion_model = bookmarks_completion_init();
  906. g_object_ref(bookmarks_completion_model);
  907. gchar *filtersPath = g_build_filename(g_get_user_cache_dir(), g_get_prgname(), "filters", NULL);
  908. window = &(struct Window){
  909. .main_window = gtk_window_new(GTK_WINDOW_TOPLEVEL),
  910. .notebook = gtk_notebook_new(),
  911. .new_tab = gtk_button_new_from_icon_name("tab-new-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR),
  912. .downloads_tab = badwolf_downloads_tab_new(),
  913. .content_manager = webkit_user_content_manager_new(),
  914. .content_store = webkit_user_content_filter_store_new(filtersPath),
  915. };
  916. load_userscripts(window->content_manager);
  917. assert(window != NULL);
  918. gchar *contentFilterPath =
  919. g_build_filename(g_get_user_config_dir(), g_get_prgname(), "content-filters.json", NULL);
  920. GFile *contentFilterFile = g_file_new_for_path(contentFilterPath);
  921. fprintf(stderr, _("content-filters file set to: %s\n"), contentFilterPath);
  922. webkit_user_content_filter_store_save_from_file(window->content_store,
  923. "a",
  924. contentFilterFile,
  925. NULL,
  926. (GAsyncReadyCallback)storeCb_finish,
  927. window);
  928. gtk_window_set_default_size(
  929. GTK_WINDOW(window->main_window), BADWOLF_DEFAULT_WIDTH, BADWOLF_DEFAULT_HEIGHT);
  930. gtk_window_set_role(GTK_WINDOW(window->main_window), "browser");
  931. gtk_window_set_icon_name(GTK_WINDOW(window->main_window), "badwolf");
  932. gchar *provider_path_app = g_build_filename(DATADIR, "interface.css", NULL);
  933. /* flawfinder: ignore, just a presence check */
  934. if(access(provider_path_app, R_OK) == 0)
  935. {
  936. GtkCssProvider *css_provider_app = gtk_css_provider_new();
  937. gtk_css_provider_load_from_path(css_provider_app, provider_path_app, NULL);
  938. gtk_style_context_add_provider_for_screen(
  939. gtk_widget_get_screen(GTK_WIDGET(window->main_window)),
  940. GTK_STYLE_PROVIDER(css_provider_app),
  941. GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
  942. }
  943. g_free(provider_path_app);
  944. gchar *provider_path_user =
  945. g_build_filename(g_get_user_data_dir(), "badwolf", "interface.css", NULL);
  946. /* flawfinder: ignore, just a presence check */
  947. if(access(provider_path_user, R_OK) == 0)
  948. {
  949. GtkCssProvider *css_provider_user = gtk_css_provider_new();
  950. gtk_css_provider_load_from_path(css_provider_user, provider_path_user, NULL);
  951. gtk_style_context_add_provider_for_screen(
  952. gtk_widget_get_screen(GTK_WIDGET(window->main_window)),
  953. GTK_STYLE_PROVIDER(css_provider_user),
  954. GTK_STYLE_PROVIDER_PRIORITY_USER);
  955. }
  956. g_free(provider_path_user);
  957. gtk_widget_set_tooltip_text(window->new_tab, _("Open new tab"));
  958. gtk_notebook_set_action_widget(GTK_NOTEBOOK(window->notebook), window->new_tab, GTK_PACK_END);
  959. gtk_notebook_set_scrollable(GTK_NOTEBOOK(window->notebook), TRUE);
  960. gtk_notebook_set_tab_pos(GTK_NOTEBOOK(window->notebook), BADWOLF_TAB_POSITION);
  961. gtk_notebook_popup_enable(GTK_NOTEBOOK(window->notebook));
  962. gtk_container_add(GTK_CONTAINER(window->main_window), window->notebook);
  963. gtk_widget_queue_draw(window->notebook);
  964. badwolf_downloads_tab_attach();
  965. g_signal_connect(
  966. window->main_window, "key-press-event", G_CALLBACK(main_windowCb_key_press_event), NULL);
  967. g_signal_connect(window->main_window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
  968. g_signal_connect(window->new_tab, "clicked", G_CALLBACK(new_tabCb_clicked), NULL);
  969. g_signal_connect(window->notebook, "switch-page", G_CALLBACK(notebookCb_switch__page), NULL);
  970. gtk_widget_show(window->new_tab);
  971. gtk_widget_show_all(window->main_window);
  972. if(argc == 0)
  973. badwolf_new_tab(GTK_NOTEBOOK(window->notebook), new_browser(NULL, NULL), FALSE);
  974. else
  975. for(int i = 0; i < argc; ++i)
  976. badwolf_new_tab(GTK_NOTEBOOK(window->notebook), new_browser(argv[i], NULL), FALSE);
  977. gtk_notebook_set_current_page(GTK_NOTEBOOK(window->notebook), 1);
  978. gtk_main();
  979. g_object_unref(bookmarks_completion_model);
  980. #if 0
  981. /* TRANSLATOR Ignore this entry. Done for forcing Unicode in xgettext. */
  982. _("ø");
  983. #endif
  984. return 0;
  985. }