adji

Adji's a Decisive and Joyful Internet browser
git clone https://noxz.tech/git/adji.git
adji

browser.h
1/**
2 * Copyright (C) 2023 Chris Noxz
3 * Author(s): Chris Noxz <chris@noxz.tech>
4 *
5 * This program is free software: you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation, either version 3 of the License, or (at your option)
8 * any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License along with
16 * this program. If not, see <https://www.gnu.org/licenses/>.
17 */
18
19#ifndef BROWSER_H
20#define BROWSER_H
21
22#include <fcntl.h>
23#include <limits.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <sys/stat.h>
28#include <sys/types.h>
29
30#include <JavaScriptCore/JavaScript.h>
31#include <gdk/gdkkeysyms.h>
32#include <gio/gio.h>
33#include <gtk/gtk.h>
34#include <gtk/gtkx.h>
35#include <webkit2/webkit2.h>
36#include <wordexp.h>
37
38#define URI_MAX             2000
39#define SUFFIX_MAX          100
40#define ICON_DOWNLOAD       "network-receive-symbolic"
41#define ICON_FINISHED       "emblem-default-symbolic"
42#define ICON_GLOBE          "globe-symbolic"
43#define ICON_TLS            "channel-secure-symbolic"
44#define ICON_BAD_TLS        "channel-insecure-symbolic"
45#define ICON_JS_ON          "emblem-ok-symbolic"
46#define ICON_JS_OFF         "action-unavailable-symbolic"
47#define CB(C, E, F, D)                                                         \
48        {                                                                      \
49            g_signal_connect(G_OBJECT(C), (E), G_CALLBACK(F), (D));            \
50        }
51#define CBA(C, E, F, D)                                                        \
52        {                                                                      \
53            g_signal_connect_after(G_OBJECT(C), (E), G_CALLBACK(F), (D));      \
54        }
55#define CFG_B(X)            cfg[(X)].v.b
56#define CFG_F(X)            cfg[(X)].v.f
57#define CFG_I(X)            cfg[(X)].v.i
58#define CFG_L(X)            (const gchar* const*)cfg[(X)].v.l
59#define CFG_S(X)            cfg[(X)].v.s
60
61#define TLS_MSG_FORMAT      "<h2>Could not validate TLS for: %s</h2><pre>%s"   \
62                            "</pre><pre>s:%s\ni:%s\nv:NotBefore:%s; NotAfter:" \
63                            "%s</pre><pre>%s</pre>"
64#define MSG_TLS_CERTIFICATE_UNKNOWN_CA                                         \
65                            ">  The signing certificate authority is not known"\
66                            ".<br>"
67#define MSG_TLS_CERTIFICATE_BAD_IDENTITY                                       \
68                            ">  The certificate does not match the expected"   \
69                            "identity of the site that it was retrieved from." \
70                            "<br>"
71#define MSG_TLS_CERTIFICATE_NOT_ACTIVATED                                      \
72                            ">  The certificate's activation time is still in" \
73                            " the future.<br>"
74#define MSG_TLS_CERTIFICATE_EXPIRED                                            \
75                            ">  The certificate has expired.<br>"
76#define MSG_TLS_CERTIFICATE_REVOKED                                            \
77                            ">  The certificate has been revoked according to" \
78                            " the GTlsConnection's certificate revocation list"\
79                            ".<br>"
80#define MSG_TLS_CERTIFICATE_INSECURE                                           \
81                            ">  The certificate's algorithm is considered "    \
82                            "insecure.<br>"
83#define MSG_TLS_CERTIFICATE_GENERIC_ERROR                                      \
84                            ">  Some error occurred validating the certificate"\
85                            ".<br>"
86#define XDG_OPEN            "xdg-open"
87#define STATE_CURRENT       1 << 0
88#define STATE_JAVASCRIPT    1 << 1
89#define BROWSER_ERROR       (browser_error_quark())
90#define ABOUT_SCHEME        __NAME__"-about"
91
92enum javascript_policy {
93	JSP_DISABLE             = 0,
94	JSP_ENABLE              = 1,
95	JSP_TOGGLE              = 2
96};
97
98enum inpage_search_type {
99	IPS_INIT,
100	IPS_FORWARD,
101	IPS_BACKWARD
102};
103
104enum ipc_type {
105	IPC_NONE,
106	IPC_HOST,
107	IPC_CLIENT
108};
109
110enum config_type {
111	CFG_INT,
112	CFG_FLOAT,
113	CFG_BOOL,
114	CFG_STRING,
115	CFG_LIST,
116};
117
118enum config_name {
119	AcceptedLanguages,
120	BadTlsTabFormat,
121	ConsoleToStdout,
122	CookieFile,
123	DnsPrefetching,
124	DefaultCharset,
125	DefaultFont,
126	DefaultFontSize,
127	DeveloperExtras,
128	DisableAutoLoadImages,
129	DisableJavaScript,
130	DownloadDirectory,
131	EncryptedMedia,
132	ExternalHandlerFile,
133	ExternalHandlerKeys,
134	FifoName,
135	HistoryFile,
136	HomeUri,
137	HyperlinkAuditing,
138	JsAccessClipboard,
139	JsOpenWindows,
140	MonospaceFont,
141	NormalTabFormat,
142	ProxyIgnore,
143	ProxyUri,
144	SansSerifFont,
145	SearchEngineUriFormat,
146	SerifFont,
147	SmoothScrolling,
148	StateFile,
149	UcDir,      /* user (cascading) style sheets directory */
150	UriSchemes,
151	UsDir,      /* user scripts directory */
152	UserAgent,
153	WeDir,      /* web extension directory */
154	WebRtc,
155	XdgSchemes,
156	ZoomLevel,
157	/* must be last to represent number of config items */
158	LastConfig
159};
160
161enum browser_error {
162	BROWSER_ERROR_INVALID_ABOUT_PATH
163};
164
165static GQuark browser_error_quark()
166{
167	return g_quark_from_string(__NAME__"-quark");
168}
169
170typedef union {
171	gint                    i;              /* union integer */
172	gdouble                 f;              /* union float/double */
173	gboolean                b;              /* union boolean */
174	gchar                  *s;              /* union string */
175	gchar                 **l;              /* union string list/array */
176} Arg;
177
178struct Client
179{
180	GTlsCertificate        *crt;            /* last certificate */
181	GtkWidget              *entry;          /* uri/command/entry */
182	gboolean                error_page;     /* flag if error page */
183	GTlsCertificate        *failed_crt;     /* last failed certificate */
184	gboolean                focus_new_tab;  /* flag for focusing tab */
185	gchar                  *hover_uri;      /* uri last hovered */
186	gboolean                https;          /* flag if https site */
187	WebKitSettings         *settings;       /* reference to webkit settings */
188	GtkWidget              *tab_icon;       /* holder for favicon in tab */
189	GtkWidget              *tab_label;      /* holder for title in tab */
190	GTlsCertificateFlags    tls_error;      /* last tls error */
191	GtkWidget              *vbx;            /* vertical box (view + entry) */
192	GtkWidget              *wv;             /* web view */
193};
194
195typedef struct {
196	const char             *e;
197	const char             *s;
198	enum config_type        t;
199	gboolean                i;
200	Arg                     v;
201} Config;
202
203struct Global {
204	int                     clients;
205	gboolean                state_lock;
206	enum ipc_type           ipc;
207	int                     ipc_pipe_fd;
208	gchar                  *search_text;
209} gl;
210
211struct MainWindow
212{
213	GtkWidget              *dbx;
214	GtkWidget              *nb;
215	GtkWidget              *win;
216} mw;
217
218/* "normal" functions */
219static void about_scheme_request(WebKitURISchemeRequest *, gpointer);
220static struct Client *client_create(const gchar *, WebKitWebView *, gboolean,
221                                    gboolean);
222static void client_destroy(struct Client *);
223static gboolean command(struct Client *, const gchar*);
224static void create_context_menu(struct Client *, WebKitContextMenu *,
225                                WebKitHitTestResult *);
226static void create_context_menu_item(struct Client *, WebKitContextMenu *,
227                                     const gchar *, const gchar *, void *);
228static void die(const char *);
229static int get_memory(int *, int *, int *, int *);
230static gchar *get_uri(GtkWidget*);
231static gboolean ipc_request(GIOChannel *, GIOCondition, gpointer);
232static ssize_t ipc_send(char *);
233static void ipc_setup(void);
234static gboolean key_common(struct Client *, GdkEventKey *);
235static gboolean key_entry(struct Client *, GdkEventKey *);
236static gboolean key_tab(struct Client *, GdkEvent *);
237static gboolean key_web_view(struct Client *, GdkEvent *);
238static void load_changed(struct Client *, WebKitLoadEvent);
239static void load_configuration(void);
240static void load_state(void);
241static void load_stdin(void);
242static void load_user_styles(WebKitWebView *);
243static void main_window_setup(void);
244static void open_external(struct Client *, const gchar);
245static void prepare_download(WebKitDownload *, gchar *);
246static void quit(void);
247static gchar *rebuild_filename(gchar *, int);
248static void render_tls_error(struct Client *, gchar *, GTlsCertificate *,
249                             GTlsCertificateFlags);
250static void run_user_scripts(WebKitWebView *);
251static void save_history(const gchar *);
252static void save_state(void);
253static void search(struct Client *, enum inpage_search_type);
254static void selection_search(struct Client *);
255static void selection_search_finished(struct Client *, GAsyncResult *);
256static void set_default_web_context(void);
257static void set_hover_uri(struct Client *, WebKitHitTestResult *);
258static void set_javascript_policy(struct Client *, enum javascript_policy);
259static void set_window_title(gint);
260static void show_web_view(struct Client *);
261static void toggle_inspector(struct Client *);
262static void toggle_tls_error_policy(struct Client *);
263static gchar *resolve_uri(const gchar *);
264static char *resolve_uri_words(int, char **);
265static void update_download_button(WebKitDownload *, GtkButton *, gboolean);
266static void update_favicon(struct Client *);
267static void update_load_progress(struct Client *);
268static void update_title(struct Client *);
269static void uri_changed(struct Client *);
270static void web_view_crashed(struct Client *);
271static void xdg_open(const gchar *, const gchar *);
272
273/* callback functions */
274static gboolean cb_context_menu(WebKitWebView *, WebKitContextMenu *,
275                                GdkEvent *, WebKitHitTestResult *, gpointer);
276static void cb_download_changed_progress(GObject *, GParamSpec *, gpointer);
277static void cb_download_finished(GObject *, gpointer);
278static gboolean cb_download_prepare(WebKitDownload *, gchar *, gpointer);
279static void cb_download_press(GtkWidget *, GdkEventButton *, gpointer);
280static void cb_download_start(WebKitWebView *, WebKitDownload *, gpointer);
281static void cb_favicon_changed(GObject *, GParamSpec *, gpointer);
282static gboolean cb_entry_hid(GtkWidget *, GdkEvent *, gpointer);
283static void cb_entry_icon_hid(GtkEntry *, GtkEntryIconPosition, GdkEvent *,
284                              gpointer);
285static void cb_notebook_modified(GtkNotebook *, GtkWidget *, guint, gpointer);
286static void cb_notebook_switch_page(GtkNotebook *, GtkWidget *, guint,
287                                    gpointer);
288static gboolean cb_open_external(GSimpleAction *, GVariant *, gpointer);
289static gboolean cb_selection_search(GSimpleAction *, GVariant *, gpointer);
290static void cb_selection_search_finished(GObject *, GAsyncResult *, gpointer);
291static void cb_quit(GObject *, gpointer);
292static gboolean cb_tab_hid(GtkWidget *, GdkEvent *, gpointer);
293static void cb_title_changed(GObject *, GParamSpec *, gpointer);
294static void cb_uri_changed(GObject *, GParamSpec *, gpointer);
295static void cb_wv_close(GtkWidget *, gpointer);
296static gboolean cb_wv_crashed(WebKitWebView *, gpointer);
297static WebKitWebView *cb_wv_create(WebKitWebView *, WebKitNavigationAction *,
298                                   gpointer);
299static gboolean cb_wv_decide_policy(WebKitWebView *, WebKitPolicyDecision *,
300                                    WebKitPolicyDecisionType, gpointer);
301static gboolean cb_wv_hid(GtkWidget *, GdkEvent *, gpointer);
302static void cb_wv_hover(WebKitWebView *, WebKitHitTestResult *, guint,
303                        gpointer);
304static void cb_wv_load_changed(GObject *, GParamSpec *, gpointer);
305static void cb_wv_load_progress_changed(GObject *, GParamSpec *, gpointer);
306static void cb_wv_show(WebKitWebView *, gpointer);
307static gboolean cb_wv_tls_load_failed(GObject *, gchar *, GTlsCertificate *,
308                                      GTlsCertificateFlags, gpointer);
309
310
311gboolean
312cb_context_menu(WebKitWebView                  *web_view,
313                WebKitContextMenu              *context_menu,
314                GdkEvent                       *event,
315                WebKitHitTestResult            *hit_test_result,
316                gpointer                        data)
317{
318	(void)web_view;
319	(void)event;
320
321	create_context_menu((struct Client *)data, context_menu, hit_test_result);
322	return FALSE;
323}
324
325void
326cb_download_changed_progress(GObject           *obj,
327                             GParamSpec        *pspec,
328                             gpointer           data)
329{
330	(void)pspec;
331
332	update_download_button(WEBKIT_DOWNLOAD(obj), GTK_BUTTON(data), FALSE);
333}
334
335void
336cb_download_finished(GObject                   *obj,
337                     gpointer                   data)
338{
339	if (!GTK_IS_BUTTON(data))
340		return;
341
342	(*((gboolean*)g_object_get_data(data, __NAME__"-finished"))) = TRUE;
343
344	update_download_button(WEBKIT_DOWNLOAD(obj), GTK_BUTTON(data), TRUE);
345	gtk_button_set_image(
346	    GTK_BUTTON(data),
347	    gtk_image_new_from_icon_name(ICON_FINISHED, GTK_ICON_SIZE_BUTTON)
348	);
349}
350
351gboolean
352cb_download_prepare(WebKitDownload             *download,
353                    gchar                      *suggested_filename,
354                    gpointer                    data)
355{
356	(void)data;
357
358	prepare_download(download, suggested_filename);
359	return FALSE;
360}
361
362void
363cb_download_press(GtkWidget                   *btn,
364                  GdkEventButton              *event,
365                  gpointer                     data)
366{
367	switch (event->button) {
368	case 3: /* right click: */
369	case 2: /* middle click: cancel and/or remove */
370		if (!(*((gboolean*)g_object_get_data(G_OBJECT(btn), __NAME__"-finished"))))
371			webkit_download_cancel(WEBKIT_DOWNLOAD(data));
372		g_object_unref(WEBKIT_DOWNLOAD(data));
373		gtk_widget_destroy(btn);
374		break;
375	case 1: /* left click: open downloaded file */
376		if ((*((gboolean*)g_object_get_data(G_OBJECT(btn), __NAME__"-finished"))))
377			xdg_open("", webkit_download_get_destination(WEBKIT_DOWNLOAD(data)));
378		break;
379	}
380}
381
382void
383cb_download_start(WebKitWebView                *web_view,
384                  WebKitDownload               *download,
385                  gpointer                      data)
386{
387	(void)web_view;
388
389	CB(download, "decide-destination", cb_download_prepare, data);
390}
391
392void
393cb_favicon_changed(GObject                     *obj,
394                   GParamSpec                  *pspec,
395                   gpointer                     data)
396{
397	(void)obj;
398	(void)pspec;
399
400	update_favicon((struct Client *)data);
401}
402
403gboolean
404cb_entry_hid(GtkWidget                         *widget,
405             GdkEvent                          *event,
406             gpointer                           data)
407{
408	(void)widget;
409
410	/* only handle key presses */
411	if (event->type == GDK_KEY_PRESS)
412		return key_entry((struct Client *)data, (GdkEventKey*)event);
413
414	return FALSE;
415
416}
417
418void
419cb_entry_icon_hid(GtkEntry                     *entry,
420                  GtkEntryIconPosition          icon_pos,
421                  GdkEvent                     *event,
422                  gpointer                      data)
423{
424	(void)entry;
425	(void)event;
426
427	if (icon_pos == GTK_ENTRY_ICON_PRIMARY)
428		toggle_tls_error_policy((struct Client *)data);
429	else if (icon_pos == GTK_ENTRY_ICON_SECONDARY)
430		set_javascript_policy((struct Client *)data, JSP_TOGGLE);
431}
432
433void
434cb_notebook_modified(GtkNotebook               *notebook,
435                     GtkWidget                 *child,
436                     guint                      idx,
437                     gpointer                   data)
438{
439	(void)notebook;
440	(void)child;
441	(void)idx;
442	(void)data;
443
444	save_state();
445}
446
447void
448cb_notebook_switch_page(GtkNotebook            *notebook,
449                        GtkWidget              *child,
450                        guint                   idx,
451                        gpointer                data)
452{
453	(void)notebook;
454	(void)child;
455	(void)data;
456
457	set_window_title(idx);
458}
459
460gboolean
461cb_open_external(GSimpleAction                 *action,
462                 GVariant                      *parameter,
463                 gpointer                       data)
464{
465	(void)action;
466	(void)parameter;
467
468	open_external((struct Client *)data, 0);
469	return FALSE;
470}
471
472void
473cb_quit(GObject                                *obj,
474        gpointer                                data)
475{
476	(void)obj;
477	(void)data;
478
479	quit();
480}
481
482gboolean
483cb_selection_search(GSimpleAction              *action,
484                    GVariant                   *parameter,
485                    gpointer                    data)
486{
487	(void)action;
488	(void)parameter;
489
490	selection_search((struct Client *)data);
491	return FALSE;
492}
493
494void
495cb_selection_search_finished(GObject           *object,
496                             GAsyncResult      *result,
497                             gpointer           data)
498{
499	struct Client *c = (struct Client *)data;
500
501	(void)object;
502
503	selection_search_finished(c, result);
504}
505
506gboolean
507cb_tab_hid(GtkWidget                           *widget,
508           GdkEvent                            *event,
509           gpointer                             data)
510{
511	(void)widget;
512
513	return key_tab((struct Client *)data, event);
514}
515
516void
517cb_title_changed(GObject                       *obj,
518                 GParamSpec                    *pspec,
519                 gpointer                       data)
520{
521	(void)obj;
522	(void)pspec;
523
524	update_title((struct Client *)data);
525}
526
527void
528cb_uri_changed(GObject                         *obj,
529               GParamSpec                      *pspec,
530               gpointer                         data)
531{
532	(void)obj;
533	(void)pspec;
534
535	uri_changed((struct Client *)data);
536}
537
538void
539cb_wv_close(GtkWidget                          *widget,
540            gpointer                            data)
541{
542	(void)widget;
543
544	client_destroy((struct Client *)data);
545}
546
547gboolean
548cb_wv_crashed(WebKitWebView                    *web_view,
549              gpointer                          data)
550{
551	(void)web_view;
552
553	web_view_crashed((struct Client *)data);
554	return TRUE;
555}
556
557WebKitWebView *
558cb_wv_create(WebKitWebView                     *web_view,
559             WebKitNavigationAction            *navigation_action,
560             gpointer                           data)
561{
562	struct Client *c;
563
564	(void)navigation_action;
565	(void)data;
566
567	if ((c = client_create(NULL, web_view, FALSE, FALSE)) == NULL)
568		return NULL;
569
570	return WEBKIT_WEB_VIEW(c->wv);
571}
572
573gboolean
574cb_wv_decide_policy(WebKitWebView              *web_view,
575                    WebKitPolicyDecision       *decision,
576                    WebKitPolicyDecisionType    type,
577                    gpointer                    data)
578{
579	(void)web_view;
580	(void)data;
581
582	/* only handle policy decisions */
583	if (type != WEBKIT_POLICY_DECISION_TYPE_RESPONSE)
584		return FALSE;
585	/* check if: 'HTTP/1.1 204 No Content'
586	 * this should be ignored, but isn't
587	 * see: https://bugs.webkit.org/show_bug.cgi?id=60206
588	 * */
589	else if (webkit_uri_response_get_status_code(
590	            webkit_response_policy_decision_get_response(
591	                WEBKIT_RESPONSE_POLICY_DECISION(decision))) == 204
592	        )
593		webkit_policy_decision_ignore(decision);
594	/* continue as normal */
595	else if (!webkit_response_policy_decision_is_mime_type_supported(
596	            WEBKIT_RESPONSE_POLICY_DECISION(decision))
597	        )
598		webkit_policy_decision_download(decision);
599	else
600		webkit_policy_decision_use(decision);
601	return TRUE;
602}
603
604gboolean
605cb_wv_hid(GtkWidget                            *widget,
606          GdkEvent                             *event,
607          gpointer                              data)
608{
609	(void)widget;
610
611	return key_web_view((struct Client *)data, event);
612}
613
614void
615cb_wv_hover(WebKitWebView                      *web_view,
616            WebKitHitTestResult                *hit_test_result,
617            guint                               modifiers,
618            gpointer                            data)
619{
620	(void)web_view;
621	(void)modifiers;
622
623	set_hover_uri((struct Client *)data, hit_test_result);
624}
625
626void
627cb_wv_load_changed(GObject                     *obj,
628                   GParamSpec                  *pspec,
629                   gpointer                     data)
630{
631	(void)obj;
632
633	load_changed((struct Client *)data, (WebKitLoadEvent)pspec);
634}
635
636void
637cb_wv_load_progress_changed(GObject            *obj,
638                            GParamSpec         *pspec,
639                            gpointer            data)
640{
641	(void)obj;
642	(void)pspec;
643
644	update_load_progress((struct Client *)data);
645}
646
647void
648cb_wv_show(WebKitWebView                       *web_view,
649           gpointer                             data)
650{
651	(void)web_view;
652
653	show_web_view((struct Client *)data);
654}
655
656gboolean
657cb_wv_tls_load_failed(GObject                  *obj,
658                      gchar                    *uri,
659                      GTlsCertificate          *crt,
660                      GTlsCertificateFlags      err,
661                      gpointer                  data)
662{
663	(void)obj;
664
665	render_tls_error((struct Client *)data, uri, crt, err);
666	return TRUE;
667}
668
669#endif /* !BROWSER_H */