badwolf.c 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820
  1. // BadWolf: Minimalist and privacy-oriented WebKitGTK+ browser
  2. // Copyright © 2019-2020 Badwolf Authors <https://hacktivis.me/projects/badwolf>
  3. // SPDX-License-Identifier: BSD-3-Clause
  4. #include "badwolf.h"
  5. #include "config.h"
  6. #include "keybindings.h"
  7. #include "uri.h"
  8. #include <glib/gi18n.h> /* _() and other internationalization/localization helpers */
  9. #include <libsoup/soup.h> /* soup* */
  10. #include <locale.h> /* LC_* */
  11. #include <stdio.h> /* perror(), fprintf() */
  12. #include <stdlib.h> /* malloc() */
  13. const gchar *homepage = "https://hacktivis.me/projects/badwolf";
  14. const gchar *version = VERSION;
  15. gchar *web_extensions_directory;
  16. static gboolean WebViewCb_close(WebKitWebView *webView, gpointer user_data);
  17. static gboolean WebViewCb_web_process_terminated(WebKitWebView *webView,
  18. WebKitWebProcessTerminationReason reason,
  19. gpointer user_data);
  20. static gboolean
  21. WebViewCb_notify__uri(WebKitWebView *webView, GParamSpec *pspec, gpointer user_data);
  22. static gboolean
  23. WebViewCb_notify__title(WebKitWebView *webView, GParamSpec *pspec, gpointer user_data);
  24. static gboolean
  25. WebViewCb_notify__is__playing__audio(WebKitWebView *webView, GParamSpec *pspec, gpointer user_data);
  26. static gboolean WebViewCb_notify__estimated_load_progress(WebKitWebView *webView,
  27. GParamSpec *pspec,
  28. gpointer user_data);
  29. static gboolean WebViewCb_mouse_target_changed(WebKitWebView *webView,
  30. WebKitHitTestResult *hit,
  31. guint modifiers,
  32. gpointer user_data);
  33. static WebKitWebView *WebViewCb_create(WebKitWebView *related_web_view,
  34. WebKitNavigationAction *navigation_action,
  35. gpointer user_data);
  36. static gboolean WebViewCb_permission_request(WebKitWebView *web_view,
  37. WebKitPermissionRequest *request,
  38. gpointer user_data);
  39. static gboolean WebViewCb_decide_policy(WebKitWebView *web_view,
  40. WebKitPolicyDecision *decision,
  41. WebKitPolicyDecisionType decision_type,
  42. gpointer user_data);
  43. static void web_contextCb_download_started(WebKitWebContext *web_context,
  44. WebKitDownload *download,
  45. gpointer user_data);
  46. static gboolean downloadCb_decide_destination(WebKitDownload *download,
  47. gchar *suggested_filename,
  48. gpointer user_data);
  49. static gboolean locationCb_activate(GtkEntry *location, gpointer user_data);
  50. static gboolean javascriptCb_toggled(GtkButton *javascript, gpointer user_data);
  51. static gboolean SearchEntryCb_next__match(GtkSearchEntry *search, gpointer user_data);
  52. static gboolean SearchEntryCb_previous__match(GtkSearchEntry *search, gpointer user_data);
  53. static gboolean SearchEntryCb_search__changed(GtkSearchEntry *search, gpointer user_data);
  54. static gboolean SearchEntryCb_stop__search(GtkSearchEntry *search, gpointer user_data);
  55. static void new_tabCb_clicked(GtkButton *new_tab, gpointer user_data);
  56. static void closeCb_clicked(GtkButton *close, gpointer user_data);
  57. static void
  58. notebookCb_switch__page(GtkNotebook *notebook, GtkWidget *page, guint page_num, gpointer user_data);
  59. static gboolean
  60. WebViewCb_close(WebKitWebView *webView, gpointer user_data)
  61. {
  62. (void)webView;
  63. struct Client *browser = (struct Client *)user_data;
  64. GtkNotebook *notebook = GTK_NOTEBOOK(browser->window->notebook);
  65. gtk_notebook_remove_page(notebook,
  66. badwolf_get_tab_position(GTK_CONTAINER(notebook), browser->box));
  67. gtk_widget_destroy(browser->box);
  68. free(browser);
  69. return TRUE;
  70. }
  71. static gboolean
  72. WebViewCb_web_process_terminated(WebKitWebView *webView,
  73. WebKitWebProcessTerminationReason reason,
  74. gpointer user_data)
  75. {
  76. (void)webView;
  77. struct Client *browser = (struct Client *)user_data;
  78. switch(reason)
  79. {
  80. case WEBKIT_WEB_PROCESS_CRASHED:
  81. fprintf(stderr, "%s", _("the web process crashed.\n"));
  82. webView_tab_label_change(browser, _("Crashed"));
  83. break;
  84. case WEBKIT_WEB_PROCESS_EXCEEDED_MEMORY_LIMIT:
  85. fprintf(stderr, "%s", _("the web process exceeded the memory limit.\n"));
  86. webView_tab_label_change(browser, _("Out of Memory"));
  87. break;
  88. default:
  89. fprintf(stderr, "%s", _("the web process terminated for an unknown reason.\n"));
  90. webView_tab_label_change(browser, _("Unknown Crash"));
  91. }
  92. return FALSE;
  93. }
  94. static gboolean
  95. WebViewCb_notify__uri(WebKitWebView *webView, GParamSpec *pspec, gpointer user_data)
  96. {
  97. (void)webView;
  98. (void)pspec;
  99. const gchar *location_uri;
  100. struct Client *browser = (struct Client *)user_data;
  101. location_uri = webkit_web_view_get_uri(browser->webView);
  102. gtk_entry_set_text(GTK_ENTRY(browser->location), location_uri);
  103. if(webkit_uri_for_display(location_uri) != location_uri)
  104. gtk_widget_set_tooltip_text(browser->location, webkit_uri_for_display(location_uri));
  105. else
  106. gtk_widget_set_has_tooltip(browser->location, false);
  107. return TRUE;
  108. }
  109. GtkWidget *
  110. badwolf_new_tab_box(const gchar *title, struct Client *browser)
  111. {
  112. (void)browser;
  113. GtkWidget *tab_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
  114. GtkWidget *close =
  115. gtk_button_new_from_icon_name("window-close-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR);
  116. GtkWidget *label = gtk_label_new(title);
  117. GtkWidget *playing =
  118. gtk_image_new_from_icon_name("audio-volume-high-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR);
  119. #ifdef BADWOLF_TAB_BOX_WIDTH
  120. gtk_widget_set_size_request(label, BADWOLF_TAB_BOX_WIDTH, -1);
  121. #endif
  122. #ifdef BADWOLF_TAB_LABEL_CHARWIDTH
  123. gtk_label_set_width_chars(GTK_LABEL(label), BADWOLF_TAB_LABEL_CHARWIDTH);
  124. #endif
  125. gtk_widget_set_hexpand(tab_box, BADWOLF_TAB_HEXPAND);
  126. gtk_label_set_ellipsize(GTK_LABEL(label), BADWOLF_TAB_LABEL_ELLIPSIZE);
  127. gtk_label_set_single_line_mode(GTK_LABEL(label), TRUE);
  128. gtk_box_pack_start(GTK_BOX(tab_box), playing, FALSE, FALSE, 0);
  129. gtk_box_pack_start(GTK_BOX(tab_box), label, TRUE, TRUE, 0);
  130. gtk_box_pack_start(GTK_BOX(tab_box), close, FALSE, FALSE, 0);
  131. gtk_button_set_relief(GTK_BUTTON(close), GTK_RELIEF_NONE);
  132. g_signal_connect(close, "clicked", G_CALLBACK(closeCb_clicked), browser);
  133. gtk_widget_set_tooltip_text(tab_box, title);
  134. gtk_widget_show_all(tab_box);
  135. gtk_widget_set_visible(playing, webkit_web_view_is_playing_audio(browser->webView));
  136. return tab_box;
  137. }
  138. static gboolean
  139. WebViewCb_notify__title(WebKitWebView *webView, GParamSpec *pspec, gpointer user_data)
  140. {
  141. (void)webView;
  142. (void)pspec;
  143. struct Client *browser = (struct Client *)user_data;
  144. webView_tab_label_change(browser, NULL);
  145. return TRUE;
  146. }
  147. static gboolean
  148. WebViewCb_notify__is__playing__audio(WebKitWebView *webView, GParamSpec *pspec, gpointer user_data)
  149. {
  150. (void)webView;
  151. (void)pspec;
  152. struct Client *browser = (struct Client *)user_data;
  153. webView_tab_label_change(browser, NULL);
  154. return TRUE;
  155. }
  156. void
  157. webView_tab_label_change(struct Client *browser, const gchar *title)
  158. {
  159. GtkWidget *notebook = browser->window->notebook;
  160. #define title_IS_EMPTY (title == NULL) || (title == (const gchar *)"")
  161. if(title_IS_EMPTY) title = webkit_web_view_get_title(browser->webView);
  162. if(title_IS_EMPTY) title = webkit_web_view_get_uri(browser->webView);
  163. if(title_IS_EMPTY) title = "BadWolf";
  164. gtk_notebook_set_tab_label(
  165. GTK_NOTEBOOK(notebook), browser->box, badwolf_new_tab_box(title, browser));
  166. gtk_notebook_set_menu_label_text(GTK_NOTEBOOK(notebook), browser->box, title);
  167. // Set the window title if the title change was on the current tab
  168. if(badwolf_get_tab_position(GTK_CONTAINER(notebook), browser->box) ==
  169. gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook)))
  170. gtk_window_set_title(GTK_WINDOW(browser->window->main_window), title);
  171. }
  172. static gboolean
  173. WebViewCb_notify__estimated_load_progress(WebKitWebView *webView,
  174. GParamSpec *pspec,
  175. gpointer user_data)
  176. {
  177. (void)webView;
  178. (void)pspec;
  179. struct Client *browser = (struct Client *)user_data;
  180. gdouble progress;
  181. progress = webkit_web_view_get_estimated_load_progress(browser->webView);
  182. if(progress >= 1) progress = 0;
  183. gtk_entry_set_progress_fraction(GTK_ENTRY(browser->location), progress);
  184. return TRUE;
  185. }
  186. static gboolean
  187. WebViewCb_mouse_target_changed(WebKitWebView *webView,
  188. WebKitHitTestResult *hit,
  189. guint modifiers,
  190. gpointer user_data)
  191. {
  192. (void)webView;
  193. (void)modifiers;
  194. struct Client *browser = (struct Client *)user_data;
  195. if(webkit_hit_test_result_context_is_link(hit))
  196. {
  197. const gchar *link_uri = webkit_hit_test_result_get_link_uri(hit);
  198. gtk_label_set_text(GTK_LABEL(browser->statuslabel), webkit_uri_for_display(link_uri));
  199. }
  200. else
  201. gtk_label_set_text(GTK_LABEL(browser->statuslabel), NULL);
  202. return FALSE;
  203. }
  204. static gboolean
  205. WebViewCb_scroll_event(GtkWidget *widget, GdkEvent *event, gpointer data)
  206. {
  207. (void)widget;
  208. struct Client *browser = (struct Client *)data;
  209. gdouble delta_x, delta_y;
  210. gdouble zoom;
  211. if(((GdkEventScroll *)event)->state & GDK_CONTROL_MASK)
  212. {
  213. gdk_event_get_scroll_deltas(event, &delta_x, &delta_y);
  214. zoom = webkit_web_view_get_zoom_level(WEBKIT_WEB_VIEW(browser->webView));
  215. zoom -= delta_y * 0.1;
  216. webkit_web_view_set_zoom_level(WEBKIT_WEB_VIEW(browser->webView), zoom);
  217. return TRUE;
  218. }
  219. return FALSE;
  220. }
  221. static WebKitWebView *
  222. WebViewCb_create(WebKitWebView *related_web_view,
  223. WebKitNavigationAction *navigation_action,
  224. gpointer user_data)
  225. {
  226. (void)navigation_action;
  227. struct Window *window = (struct Window *)user_data;
  228. struct Client *browser = new_browser(window, NULL, related_web_view);
  229. if(badwolf_new_tab(GTK_NOTEBOOK(window->notebook), browser) < 0)
  230. return NULL;
  231. else
  232. return browser->webView;
  233. }
  234. static gboolean
  235. WebViewCb_permission_request(WebKitWebView *web_view,
  236. WebKitPermissionRequest *request,
  237. gpointer user_data)
  238. {
  239. (void)web_view;
  240. (void)user_data;
  241. webkit_permission_request_deny(request);
  242. return TRUE; /* Stop other handlers */
  243. }
  244. static gboolean
  245. WebViewCb_decide_policy(WebKitWebView *web_view,
  246. WebKitPolicyDecision *decision,
  247. WebKitPolicyDecisionType decision_type,
  248. gpointer user_data)
  249. {
  250. WebKitResponsePolicyDecision *r;
  251. (void)web_view;
  252. (void)user_data;
  253. switch(decision_type)
  254. {
  255. case WEBKIT_POLICY_DECISION_TYPE_RESPONSE:
  256. r = WEBKIT_RESPONSE_POLICY_DECISION(decision);
  257. if(!webkit_response_policy_decision_is_mime_type_supported(r))
  258. webkit_policy_decision_download(decision);
  259. else
  260. webkit_policy_decision_use(decision);
  261. break;
  262. default:
  263. /* Use whatever default there is. */
  264. return FALSE;
  265. }
  266. return TRUE;
  267. }
  268. static char *
  269. detail_tls_certificate_flags(GTlsCertificateFlags tls_errors)
  270. {
  271. GString *errors = g_string_new(NULL);
  272. g_string_append_printf(errors,
  273. _("Couldn't verify the TLS certificate to ensure a better security of the "
  274. "connection. You might want to verify your machine and network.\n\n"));
  275. if(tls_errors & G_TLS_CERTIFICATE_UNKNOWN_CA)
  276. g_string_append_printf(errors, _("Error: The X509 Certificate Authority is unknown.\n"));
  277. if(tls_errors & G_TLS_CERTIFICATE_BAD_IDENTITY)
  278. g_string_append(errors, _("Error: The given identity doesn't match the expected one.\n"));
  279. if(tls_errors & G_TLS_CERTIFICATE_NOT_ACTIVATED)
  280. g_string_append(errors,
  281. _("Error: The certificate isn't valid yet. Check your system's clock.\n"));
  282. if(tls_errors & G_TLS_CERTIFICATE_EXPIRED)
  283. g_string_append(errors, _("Error: The certificate has expired. Check your system's clock.\n"));
  284. if(tls_errors & G_TLS_CERTIFICATE_REVOKED)
  285. g_string_append(errors, _("Error: The certificate has been revoked.\n"));
  286. if(tls_errors & G_TLS_CERTIFICATE_INSECURE)
  287. g_string_append(errors, _("Error: The certificate is considered to be insecure.\n"));
  288. if(tls_errors & G_TLS_CERTIFICATE_GENERIC_ERROR)
  289. g_string_append(errors, _("Error: Some unknown error occurred validating the certificate.\n"));
  290. return g_string_free(errors, FALSE);
  291. }
  292. static gboolean
  293. WebViewCb_load_failed_with_tls_errors(WebKitWebView *web_view,
  294. gchar *failing_text,
  295. GTlsCertificate *certificate,
  296. GTlsCertificateFlags errors,
  297. gpointer user_data)
  298. {
  299. (void)web_view;
  300. (void)certificate;
  301. (void)errors;
  302. struct Client *browser = (struct Client *)user_data;
  303. gchar *error_details = detail_tls_certificate_flags(errors);
  304. gint dialog_response;
  305. SoupURI *failing_uri = soup_uri_new(failing_text);
  306. GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(browser->window->main_window),
  307. GTK_DIALOG_MODAL & GTK_DIALOG_DESTROY_WITH_PARENT,
  308. GTK_MESSAGE_ERROR,
  309. GTK_BUTTONS_NONE,
  310. _("TLS Error for %s."),
  311. failing_text);
  312. gtk_dialog_add_buttons(
  313. GTK_DIALOG(dialog), _("Temporarly Add Exception"), 1, _("Continue"), 0, NULL);
  314. gtk_dialog_set_default_response(GTK_DIALOG(dialog), 0);
  315. gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), "%s\n", error_details);
  316. dialog_response = gtk_dialog_run(GTK_DIALOG(dialog));
  317. if(dialog_response == 1)
  318. {
  319. webkit_web_context_allow_tls_certificate_for_host(
  320. webkit_web_view_get_context(browser->webView), certificate, failing_uri->host);
  321. webkit_web_view_reload(browser->webView);
  322. }
  323. soup_uri_free(failing_uri);
  324. g_free(error_details);
  325. gtk_widget_destroy(dialog);
  326. return FALSE; /* propagate the event further */
  327. }
  328. static void
  329. web_contextCb_download_started(WebKitWebContext *web_context,
  330. WebKitDownload *download,
  331. gpointer user_data)
  332. {
  333. (void)web_context;
  334. g_signal_connect(G_OBJECT(download),
  335. "decide-destination",
  336. G_CALLBACK(downloadCb_decide_destination),
  337. user_data);
  338. }
  339. static gboolean
  340. downloadCb_decide_destination(WebKitDownload *download,
  341. gchar *suggested_filename,
  342. gpointer user_data)
  343. {
  344. struct Client *browser = (struct Client *)user_data;
  345. gint chooser_response;
  346. GtkWindow *parent_window = GTK_WINDOW(browser->window->main_window);
  347. GtkFileChooserNative *file_dialog =
  348. gtk_file_chooser_native_new(NULL, parent_window, GTK_FILE_CHOOSER_ACTION_SAVE, NULL, NULL);
  349. GtkFileChooser *file_chooser = GTK_FILE_CHOOSER(file_dialog);
  350. gtk_file_chooser_set_current_name(file_chooser, suggested_filename);
  351. gtk_file_chooser_set_do_overwrite_confirmation(file_chooser, TRUE);
  352. webkit_download_set_allow_overwrite(download, TRUE);
  353. chooser_response = gtk_native_dialog_run(GTK_NATIVE_DIALOG(file_dialog));
  354. if(chooser_response == GTK_RESPONSE_ACCEPT)
  355. webkit_download_set_destination(download, gtk_file_chooser_get_uri(file_chooser));
  356. else
  357. webkit_download_cancel(download);
  358. g_object_unref(file_dialog);
  359. return FALSE; /* Let it propagate */
  360. }
  361. static gboolean
  362. locationCb_activate(GtkEntry *location, gpointer user_data)
  363. {
  364. struct Client *browser = (struct Client *)user_data;
  365. webkit_web_view_load_uri(browser->webView,
  366. badwolf_ensure_uri_scheme(gtk_entry_get_text(location), TRUE));
  367. return TRUE;
  368. }
  369. static gboolean
  370. javascriptCb_toggled(GtkButton *javascript, gpointer user_data)
  371. {
  372. struct Client *browser = (struct Client *)user_data;
  373. WebKitSettings *settings = webkit_web_view_get_settings(browser->webView);
  374. webkit_settings_set_enable_javascript_markup(
  375. settings, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(javascript)));
  376. webkit_web_view_set_settings(browser->webView, settings);
  377. return TRUE;
  378. }
  379. static gboolean
  380. SearchEntryCb_next__match(GtkSearchEntry *search, gpointer user_data)
  381. {
  382. (void)search;
  383. struct Client *browser = (struct Client *)user_data;
  384. WebKitFindController *findController = webkit_web_view_get_find_controller(browser->webView);
  385. webkit_find_controller_search_next(findController);
  386. return TRUE;
  387. }
  388. static gboolean
  389. SearchEntryCb_previous__match(GtkSearchEntry *search, gpointer user_data)
  390. {
  391. (void)search;
  392. struct Client *browser = (struct Client *)user_data;
  393. WebKitFindController *findController = webkit_web_view_get_find_controller(browser->webView);
  394. webkit_find_controller_search_previous(findController);
  395. return TRUE;
  396. }
  397. static gboolean
  398. SearchEntryCb_search__changed(GtkSearchEntry *search, gpointer user_data)
  399. {
  400. struct Client *browser = (struct Client *)user_data;
  401. WebKitFindController *findController = webkit_web_view_get_find_controller(browser->webView);
  402. const gchar *search_text = gtk_entry_get_text(GTK_ENTRY(search));
  403. webkit_find_controller_search(findController, search_text, 0, 0);
  404. return TRUE;
  405. }
  406. static gboolean
  407. SearchEntryCb_stop__search(GtkSearchEntry *search, gpointer user_data)
  408. {
  409. (void)search;
  410. struct Client *browser = (struct Client *)user_data;
  411. WebKitFindController *findController = webkit_web_view_get_find_controller(browser->webView);
  412. webkit_find_controller_search_finish(findController);
  413. return TRUE;
  414. }
  415. struct Client *
  416. new_browser(struct Window *window, const gchar *target_url, WebKitWebView *related_web_view)
  417. {
  418. struct Client *browser = malloc(sizeof(struct Client));
  419. target_url = badwolf_ensure_uri_scheme(target_url, (related_web_view == NULL));
  420. char *badwolf_l10n = NULL;
  421. if(browser == NULL) return NULL;
  422. browser->window = window;
  423. browser->box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
  424. browser->toolbar = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
  425. browser->location = gtk_entry_new();
  426. browser->javascript = gtk_check_button_new();
  427. browser->statusbar = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
  428. browser->statuslabel = gtk_label_new(NULL);
  429. browser->search = gtk_search_entry_new();
  430. WebKitWebContext *web_context = webkit_web_context_new_ephemeral();
  431. webkit_web_context_set_sandbox_enabled(web_context, TRUE);
  432. webkit_web_context_set_process_model(web_context,
  433. WEBKIT_PROCESS_MODEL_MULTIPLE_SECONDARY_PROCESSES);
  434. webkit_web_context_set_web_extensions_directory(web_context, web_extensions_directory);
  435. badwolf_l10n = getenv("BADWOLF_L10N");
  436. if(badwolf_l10n != NULL)
  437. {
  438. gchar **languages = g_strsplit(badwolf_l10n, ":", -1);
  439. webkit_web_context_set_spell_checking_languages(web_context, (const gchar *const *)languages);
  440. g_strfreev(languages);
  441. webkit_web_context_set_spell_checking_enabled(web_context, TRUE);
  442. }
  443. WebKitSettings *settings = webkit_settings_new_with_settings(BADWOLF_WEBKIT_SETTINGS);
  444. gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(browser->javascript),
  445. webkit_settings_get_enable_javascript_markup(settings));
  446. browser->webView = WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW,
  447. "web-context",
  448. web_context,
  449. "related-view",
  450. related_web_view,
  451. "settings",
  452. settings,
  453. NULL));
  454. gtk_widget_set_tooltip_text(browser->javascript, _("Toggle javascript"));
  455. gtk_box_pack_start(GTK_BOX(browser->toolbar),
  456. GTK_WIDGET(browser->javascript),
  457. FALSE,
  458. FALSE,
  459. BADWOLF_TOOLBAR_PADDING);
  460. gtk_box_pack_start(GTK_BOX(browser->toolbar),
  461. GTK_WIDGET(browser->location),
  462. TRUE,
  463. TRUE,
  464. BADWOLF_TOOLBAR_PADDING);
  465. gtk_box_pack_start(
  466. GTK_BOX(browser->box), GTK_WIDGET(browser->toolbar), FALSE, FALSE, BADWOLF_BOX_PADDING);
  467. gtk_box_pack_start(
  468. GTK_BOX(browser->box), GTK_WIDGET(browser->webView), TRUE, TRUE, BADWOLF_BOX_PADDING);
  469. gtk_box_pack_start(
  470. GTK_BOX(browser->box), GTK_WIDGET(browser->statusbar), FALSE, FALSE, BADWOLF_BOX_PADDING);
  471. gtk_box_pack_start(GTK_BOX(browser->statusbar),
  472. GTK_WIDGET(browser->search),
  473. FALSE,
  474. FALSE,
  475. BADWOLF_STATUSBAR_PADDING);
  476. gtk_box_pack_start(GTK_BOX(browser->statusbar),
  477. GTK_WIDGET(browser->statuslabel),
  478. FALSE,
  479. FALSE,
  480. BADWOLF_STATUSBAR_PADDING);
  481. gtk_widget_set_halign(browser->statusbar, GTK_ALIGN_START);
  482. gtk_label_set_single_line_mode(GTK_LABEL(browser->statuslabel), TRUE);
  483. gtk_label_set_ellipsize(GTK_LABEL(browser->statuslabel), BADWOLF_STATUSLABEL_ELLIPSIZE);
  484. gtk_entry_set_text(GTK_ENTRY(browser->location), target_url);
  485. gtk_entry_set_has_frame(GTK_ENTRY(browser->location), FALSE);
  486. gtk_entry_set_input_purpose(GTK_ENTRY(browser->location), GTK_INPUT_PURPOSE_URL);
  487. gtk_entry_set_placeholder_text(GTK_ENTRY(browser->search), _("search in current page"));
  488. gtk_entry_set_has_frame(GTK_ENTRY(browser->search), FALSE);
  489. /* signals for location entry widget */
  490. g_signal_connect(browser->location, "activate", G_CALLBACK(locationCb_activate), browser);
  491. /* signals for javacript toggle widget */
  492. g_signal_connect(browser->javascript, "toggled", G_CALLBACK(javascriptCb_toggled), browser);
  493. /* signals for WebView widget */
  494. g_signal_connect(browser->webView,
  495. "web-process-terminated",
  496. G_CALLBACK(WebViewCb_web_process_terminated),
  497. browser);
  498. g_signal_connect(browser->webView, "notify::uri", G_CALLBACK(WebViewCb_notify__uri), browser);
  499. g_signal_connect(browser->webView, "notify::title", G_CALLBACK(WebViewCb_notify__title), browser);
  500. g_signal_connect(browser->webView,
  501. "notify::is-playing-audio",
  502. G_CALLBACK(WebViewCb_notify__is__playing__audio),
  503. browser);
  504. g_signal_connect(browser->webView,
  505. "mouse-target-changed",
  506. G_CALLBACK(WebViewCb_mouse_target_changed),
  507. browser);
  508. g_signal_connect(browser->webView,
  509. "notify::estimated-load-progress",
  510. G_CALLBACK(WebViewCb_notify__estimated_load_progress),
  511. browser);
  512. g_signal_connect(browser->webView, "create", G_CALLBACK(WebViewCb_create), window);
  513. g_signal_connect(browser->webView, "close", G_CALLBACK(WebViewCb_close), browser);
  514. g_signal_connect(
  515. browser->webView, "key-press-event", G_CALLBACK(WebViewCb_key_press_event), browser);
  516. g_signal_connect(browser->webView, "scroll-event", G_CALLBACK(WebViewCb_scroll_event), browser);
  517. g_signal_connect(
  518. browser->webView, "permission-request", G_CALLBACK(WebViewCb_permission_request), NULL);
  519. g_signal_connect(browser->webView, "decide-policy", G_CALLBACK(WebViewCb_decide_policy), NULL);
  520. g_signal_connect(browser->webView,
  521. "load-failed-with-tls-errors",
  522. G_CALLBACK(WebViewCb_load_failed_with_tls_errors),
  523. browser);
  524. /* signals for WebView's WebContext */
  525. g_signal_connect(G_OBJECT(web_context),
  526. "download-started",
  527. G_CALLBACK(web_contextCb_download_started),
  528. browser);
  529. /* signals for search widget */
  530. g_signal_connect(browser->search, "next-match", G_CALLBACK(SearchEntryCb_next__match), browser);
  531. g_signal_connect(
  532. browser->search, "previous-match", G_CALLBACK(SearchEntryCb_previous__match), browser);
  533. g_signal_connect(
  534. browser->search, "search-changed", G_CALLBACK(SearchEntryCb_search__changed), browser);
  535. g_signal_connect(browser->search, "stop-search", G_CALLBACK(SearchEntryCb_stop__search), browser);
  536. /* signals for box container */
  537. g_signal_connect(browser->box, "key-press-event", G_CALLBACK(boxCb_key_press_event), browser);
  538. if(related_web_view == NULL) webkit_web_view_load_uri(browser->webView, target_url);
  539. return browser;
  540. }
  541. /* badwolf_new_tab: Inserts struct Client *browser in GtkNotebook *notebook
  542. *
  543. * returns:
  544. * 0 : Ran successfully
  545. * -1 : Failed to insert a page for browser->box
  546. * -2 : browser is NULL
  547. */
  548. int
  549. badwolf_new_tab(GtkNotebook *notebook, struct Client *browser)
  550. {
  551. gint current_page = gtk_notebook_get_current_page(notebook);
  552. gchar *title = _("New tab");
  553. if(browser == NULL) return -2;
  554. gtk_widget_show_all(browser->box);
  555. if(gtk_notebook_insert_page(notebook, browser->box, NULL, (current_page + 1)) == -1) return -1;
  556. gtk_notebook_set_tab_reorderable(notebook, browser->box, TRUE);
  557. gtk_notebook_set_tab_label(notebook, browser->box, badwolf_new_tab_box(title, browser));
  558. gtk_notebook_set_menu_label_text(GTK_NOTEBOOK(notebook), browser->box, title);
  559. gtk_widget_queue_draw(GTK_WIDGET(notebook));
  560. return 0;
  561. }
  562. static void
  563. new_tabCb_clicked(GtkButton *new_tab, gpointer user_data)
  564. {
  565. (void)new_tab;
  566. struct Window *window = (struct Window *)user_data;
  567. struct Client *browser = new_browser(window, NULL, NULL);
  568. badwolf_new_tab(GTK_NOTEBOOK(window->notebook), browser);
  569. }
  570. static void
  571. closeCb_clicked(GtkButton *close, gpointer user_data)
  572. {
  573. (void)close;
  574. struct Client *browser = (struct Client *)user_data;
  575. webkit_web_view_try_close(browser->webView);
  576. }
  577. static void
  578. notebookCb_switch__page(GtkNotebook *notebook, GtkWidget *page, guint page_num, gpointer user_data)
  579. {
  580. (void)page_num;
  581. struct Window *window = (struct Window *)user_data;
  582. GtkWidget *label = gtk_notebook_get_tab_label(notebook, page);
  583. // TODO: Maybe find a better way to store the title
  584. gtk_window_set_title(GTK_WINDOW(window->main_window), gtk_widget_get_tooltip_text(label));
  585. }
  586. gint
  587. badwolf_get_tab_position(GtkContainer *notebook, GtkWidget *child)
  588. {
  589. GValue position = G_VALUE_INIT;
  590. g_value_init(&position, G_TYPE_INT);
  591. gtk_container_child_get_property(notebook, child, "position", &position);
  592. return g_value_get_int(&position);
  593. }
  594. int
  595. main(int argc, char *argv[])
  596. {
  597. struct Window *window = &(struct Window){NULL};
  598. setlocale(LC_ALL, "");
  599. bindtextdomain(PACKAGE, DATADIR "/locale");
  600. bind_textdomain_codeset(PACKAGE, "UTF-8");
  601. textdomain(PACKAGE);
  602. gtk_init(&argc, &argv);
  603. #ifdef BADWOLF_PREFER_DARK_THEME
  604. g_object_set(gtk_settings_get_default(),
  605. "gtk-application-prefer-dark-theme",
  606. BADWOLF_PREFER_DARK_THEME,
  607. NULL);
  608. #endif
  609. fprintf(stderr, _("Running Badwolf version: %s\n"), version);
  610. fprintf(stderr,
  611. _("Buildtime WebKit version: %d.%d.%d\n"),
  612. WEBKIT_MAJOR_VERSION,
  613. WEBKIT_MINOR_VERSION,
  614. WEBKIT_MICRO_VERSION);
  615. fprintf(stderr,
  616. _("Runtime WebKit version: %d.%d.%d\n"),
  617. webkit_get_major_version(),
  618. webkit_get_minor_version(),
  619. webkit_get_micro_version());
  620. web_extensions_directory =
  621. g_build_filename(g_get_user_data_dir(), "badwolf", "webkit-web-extension", NULL);
  622. fprintf(stderr, _("webkit-web-extension directory set to: %s\n"), web_extensions_directory);
  623. window->main_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  624. window->notebook = gtk_notebook_new();
  625. window->new_tab = gtk_button_new_from_icon_name("tab-new-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR);
  626. gtk_window_set_default_size(
  627. GTK_WINDOW(window->main_window), BADWOLF_DEFAULT_WIDTH, BADWOLF_DEFAULT_HEIGHT);
  628. gtk_window_set_role(GTK_WINDOW(window->main_window), "browser");
  629. gtk_widget_set_tooltip_text(window->new_tab, _("Open new tab"));
  630. gtk_notebook_set_action_widget(GTK_NOTEBOOK(window->notebook), window->new_tab, GTK_PACK_END);
  631. gtk_notebook_set_scrollable(GTK_NOTEBOOK(window->notebook), TRUE);
  632. gtk_notebook_set_tab_pos(GTK_NOTEBOOK(window->notebook), BADWOLF_TAB_POSITION);
  633. gtk_notebook_popup_enable(GTK_NOTEBOOK(window->notebook));
  634. gtk_container_add(GTK_CONTAINER(window->main_window), window->notebook);
  635. if(argc == 1)
  636. badwolf_new_tab(GTK_NOTEBOOK(window->notebook), new_browser(window, NULL, NULL));
  637. else
  638. for(int i = 1; i < argc; ++i)
  639. badwolf_new_tab(GTK_NOTEBOOK(window->notebook), new_browser(window, argv[i], NULL));
  640. g_signal_connect(
  641. window->main_window, "key-press-event", G_CALLBACK(main_windowCb_key_press_event), window);
  642. g_signal_connect(window->main_window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
  643. g_signal_connect(window->new_tab, "clicked", G_CALLBACK(new_tabCb_clicked), window);
  644. g_signal_connect(window->notebook, "switch-page", G_CALLBACK(notebookCb_switch__page), window);
  645. gtk_widget_show(window->new_tab);
  646. gtk_widget_show_all(window->main_window);
  647. gtk_main();
  648. #if 0
  649. /* TRANSLATOR Ignore this entry. Done for forcing Unicode in xgettext. */
  650. _("ø");
  651. #endif
  652. return 0;
  653. }