badwolf.c 44 KB

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