adji

Adji's a Decisive and Joyful Internet browser
git clone https://noxz.tech/git/adji.git
Log | Files | Tags | LICENSE

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 void render_tls_error(struct Client *, gchar *, GTlsCertificate *,
248                             GTlsCertificateFlags);
249static void run_user_scripts(WebKitWebView *);
250static void save_history(const gchar *);
251static void save_state(void);
252static void search(struct Client *, enum inpage_search_type);
253static void selection_search(struct Client *);
254static void selection_search_finished(struct Client *, GAsyncResult *);
255static void set_default_web_context(void);
256static void set_hover_uri(struct Client *, WebKitHitTestResult *);
257static void set_javascript_policy(struct Client *, enum javascript_policy);
258static void set_window_title(gint);
259static void show_web_view(struct Client *);
260static void toggle_inspector(struct Client *);
261static void toggle_tls_error_policy(struct Client *);
262static gchar *resolve_uri(const gchar *);
263static char *resolve_uri_words(int, char **);
264static void update_download_button(WebKitDownload *, GtkButton *, gboolean);
265static void update_favicon(struct Client *);
266static void update_load_progress(struct Client *);
267static void update_title(struct Client *);
268static void uri_changed(struct Client *);
269static void web_view_crashed(struct Client *);
270static void xdg_open(const gchar *, const gchar *);
271
272/* callback functions */
273static gboolean cb_context_menu(WebKitWebView *, WebKitContextMenu *,
274                                GdkEvent *, WebKitHitTestResult *, gpointer);
275static void cb_download_changed_progress(GObject *, GParamSpec *, gpointer);
276static void cb_download_finished(GObject *, gpointer);
277static gboolean cb_download_prepare(WebKitDownload *, gchar *, gpointer);
278static void cb_download_press(GtkWidget *, GdkEventButton *, gpointer);
279static void cb_download_start(WebKitWebView *, WebKitDownload *, gpointer);
280static void cb_favicon_changed(GObject *, GParamSpec *, gpointer);
281static gboolean cb_entry_hid(GtkWidget *, GdkEvent *, gpointer);
282static void cb_entry_icon_hid(GtkEntry *, GtkEntryIconPosition, GdkEvent *,
283                              gpointer);
284static void cb_notebook_modified(GtkNotebook *, GtkWidget *, guint, gpointer);
285static void cb_notebook_switch_page(GtkNotebook *, GtkWidget *, guint,
286                                    gpointer);
287static gboolean cb_open_external(GSimpleAction *, GVariant *, gpointer);
288static gboolean cb_selection_search(GSimpleAction *, GVariant *, gpointer);
289static void cb_selection_search_finished(GObject *, GAsyncResult *, gpointer);
290static void cb_quit(GObject *, gpointer);
291static gboolean cb_tab_hid(GtkWidget *, GdkEvent *, gpointer);
292static void cb_title_changed(GObject *, GParamSpec *, gpointer);
293static void cb_uri_changed(GObject *, GParamSpec *, gpointer);
294static void cb_wv_close(GtkWidget *, gpointer);
295static gboolean cb_wv_crashed(WebKitWebView *, gpointer);
296static WebKitWebView *cb_wv_create(WebKitWebView *, WebKitNavigationAction *,
297                                   gpointer);
298static gboolean cb_wv_decide_policy(WebKitWebView *, WebKitPolicyDecision *,
299                                    WebKitPolicyDecisionType, gpointer);
300static gboolean cb_wv_hid(GtkWidget *, GdkEvent *, gpointer);
301static void cb_wv_hover(WebKitWebView *, WebKitHitTestResult *, guint,
302                        gpointer);
303static void cb_wv_load_changed(GObject *, GParamSpec *, gpointer);
304static void cb_wv_load_progress_changed(GObject *, GParamSpec *, gpointer);
305static void cb_wv_show(WebKitWebView *, gpointer);
306static gboolean cb_wv_tls_load_failed(GObject *, gchar *, GTlsCertificate *,
307                                      GTlsCertificateFlags, gpointer);
308
309
310gboolean
311cb_context_menu(WebKitWebView                  *web_view,
312                WebKitContextMenu              *context_menu,
313                GdkEvent                       *event,
314                WebKitHitTestResult            *hit_test_result,
315                gpointer                        data)
316{
317	(void)web_view;
318	(void)event;
319
320	create_context_menu((struct Client *)data, context_menu, hit_test_result);
321	return FALSE;
322}
323
324void
325cb_download_changed_progress(GObject           *obj,
326                             GParamSpec        *pspec,
327                             gpointer           data)
328{
329	(void)pspec;
330
331	update_download_button(WEBKIT_DOWNLOAD(obj), GTK_BUTTON(data), FALSE);
332}
333
334void
335cb_download_finished(GObject                   *obj,
336                     gpointer                   data)
337{
338	if (!GTK_IS_BUTTON(data))
339		return;
340
341	(*((gboolean*)g_object_get_data(data, __NAME__"-finished"))) = TRUE;
342
343	update_download_button(WEBKIT_DOWNLOAD(obj), GTK_BUTTON(data), TRUE);
344	gtk_button_set_image(
345	    GTK_BUTTON(data),
346	    gtk_image_new_from_icon_name(ICON_FINISHED, GTK_ICON_SIZE_BUTTON)
347	);
348}
349
350gboolean
351cb_download_prepare(WebKitDownload             *download,
352                    gchar                      *suggested_filename,
353                    gpointer                    data)
354{
355	(void)data;
356
357	prepare_download(download, suggested_filename);
358	return FALSE;
359}
360
361void
362cb_download_press(GtkWidget                   *btn,
363                  GdkEventButton              *event,
364                  gpointer                     data)
365{
366	switch (event->button) {
367	case 3: /* right click: */
368	case 2: /* middle click: cancel and/or remove */
369		if (!(*((gboolean*)g_object_get_data(G_OBJECT(btn), __NAME__"-finished"))))
370			webkit_download_cancel(WEBKIT_DOWNLOAD(data));
371		g_object_unref(WEBKIT_DOWNLOAD(data));
372		gtk_widget_destroy(btn);
373		break;
374	case 1: /* left click: open downloaded file */
375		if ((*((gboolean*)g_object_get_data(G_OBJECT(btn), __NAME__"-finished"))))
376			xdg_open("", webkit_download_get_destination(WEBKIT_DOWNLOAD(data)));
377		break;
378	}
379}
380
381void
382cb_download_start(WebKitWebView                *web_view,
383                  WebKitDownload               *download,
384                  gpointer                      data)
385{
386	(void)web_view;
387
388	CB(download, "decide-destination", cb_download_prepare, data);
389}
390
391void
392cb_favicon_changed(GObject                     *obj,
393                   GParamSpec                  *pspec,
394                   gpointer                     data)
395{
396	(void)obj;
397	(void)pspec;
398
399	update_favicon((struct Client *)data);
400}
401
402gboolean
403cb_entry_hid(GtkWidget                         *widget,
404             GdkEvent                          *event,
405             gpointer                           data)
406{
407	(void)widget;
408
409	/* only handle key presses */
410	if (event->type == GDK_KEY_PRESS)
411		return key_entry((struct Client *)data, (GdkEventKey*)event);
412
413	return FALSE;
414
415}
416
417void
418cb_entry_icon_hid(GtkEntry                     *entry,
419                  GtkEntryIconPosition          icon_pos,
420                  GdkEvent                     *event,
421                  gpointer                      data)
422{
423	(void)entry;
424	(void)event;
425
426	if (icon_pos == GTK_ENTRY_ICON_PRIMARY)
427		toggle_tls_error_policy((struct Client *)data);
428	else if (icon_pos == GTK_ENTRY_ICON_SECONDARY)
429		set_javascript_policy((struct Client *)data, JSP_TOGGLE);
430}
431
432void
433cb_notebook_modified(GtkNotebook               *notebook,
434                     GtkWidget                 *child,
435                     guint                      idx,
436                     gpointer                   data)
437{
438	(void)notebook;
439	(void)child;
440	(void)idx;
441	(void)data;
442
443	save_state();
444}
445
446void
447cb_notebook_switch_page(GtkNotebook            *notebook,
448                        GtkWidget              *child,
449                        guint                   idx,
450                        gpointer                data)
451{
452	(void)notebook;
453	(void)child;
454	(void)data;
455
456	set_window_title(idx);
457}
458
459gboolean
460cb_open_external(GSimpleAction                 *action,
461                 GVariant                      *parameter,
462                 gpointer                       data)
463{
464	(void)action;
465	(void)parameter;
466
467	open_external((struct Client *)data, 0);
468	return FALSE;
469}
470
471void
472cb_quit(GObject                                *obj,
473        gpointer                                data)
474{
475	(void)obj;
476	(void)data;
477
478	quit();
479}
480
481gboolean
482cb_selection_search(GSimpleAction              *action,
483                    GVariant                   *parameter,
484                    gpointer                    data)
485{
486	(void)action;
487	(void)parameter;
488
489	selection_search((struct Client *)data);
490	return FALSE;
491}
492
493void
494cb_selection_search_finished(GObject           *object,
495                             GAsyncResult      *result,
496                             gpointer           data)
497{
498	struct Client *c = (struct Client *)data;
499
500	(void)object;
501
502	selection_search_finished(c, result);
503}
504
505gboolean
506cb_tab_hid(GtkWidget                           *widget,
507           GdkEvent                            *event,
508           gpointer                             data)
509{
510	(void)widget;
511
512	return key_tab((struct Client *)data, event);
513}
514
515void
516cb_title_changed(GObject                       *obj,
517                 GParamSpec                    *pspec,
518                 gpointer                       data)
519{
520	(void)obj;
521	(void)pspec;
522
523	update_title((struct Client *)data);
524}
525
526void
527cb_uri_changed(GObject                         *obj,
528               GParamSpec                      *pspec,
529               gpointer                         data)
530{
531	(void)obj;
532	(void)pspec;
533
534	uri_changed((struct Client *)data);
535}
536
537void
538cb_wv_close(GtkWidget                          *widget,
539            gpointer                            data)
540{
541	(void)widget;
542
543	client_destroy((struct Client *)data);
544}
545
546gboolean
547cb_wv_crashed(WebKitWebView                    *web_view,
548              gpointer                          data)
549{
550	(void)web_view;
551
552	web_view_crashed((struct Client *)data);
553	return TRUE;
554}
555
556WebKitWebView *
557cb_wv_create(WebKitWebView                     *web_view,
558             WebKitNavigationAction            *navigation_action,
559             gpointer                           data)
560{
561	struct Client *c;
562
563	(void)navigation_action;
564	(void)data;
565
566	if ((c = client_create(NULL, web_view, FALSE, FALSE)) == NULL)
567		return NULL;
568
569	return WEBKIT_WEB_VIEW(c->wv);
570}
571
572gboolean
573cb_wv_decide_policy(WebKitWebView              *web_view,
574                    WebKitPolicyDecision       *decision,
575                    WebKitPolicyDecisionType    type,
576                    gpointer                    data)
577{
578	(void)web_view;
579	(void)data;
580
581	/* only handle policy decisions */
582	if (type != WEBKIT_POLICY_DECISION_TYPE_RESPONSE)
583		return FALSE;
584	/* check if: 'HTTP/1.1 204 No Content'
585	 * this should be ignored, but isn't
586	 * see: https://bugs.webkit.org/show_bug.cgi?id=60206
587	 * */
588	else if (webkit_uri_response_get_status_code(
589	            webkit_response_policy_decision_get_response(
590	                WEBKIT_RESPONSE_POLICY_DECISION(decision))) == 204
591	        )
592		webkit_policy_decision_ignore(decision);
593	/* continue as normal */
594	else if (!webkit_response_policy_decision_is_mime_type_supported(
595	            WEBKIT_RESPONSE_POLICY_DECISION(decision))
596	        )
597		webkit_policy_decision_download(decision);
598	else
599		webkit_policy_decision_use(decision);
600	return TRUE;
601}
602
603gboolean
604cb_wv_hid(GtkWidget                            *widget,
605          GdkEvent                             *event,
606          gpointer                              data)
607{
608	(void)widget;
609
610	return key_web_view((struct Client *)data, event);
611}
612
613void
614cb_wv_hover(WebKitWebView                      *web_view,
615            WebKitHitTestResult                *hit_test_result,
616            guint                               modifiers,
617            gpointer                            data)
618{
619	(void)web_view;
620	(void)modifiers;
621
622	set_hover_uri((struct Client *)data, hit_test_result);
623}
624
625void
626cb_wv_load_changed(GObject                     *obj,
627                   GParamSpec                  *pspec,
628                   gpointer                     data)
629{
630	(void)obj;
631
632	load_changed((struct Client *)data, (WebKitLoadEvent)pspec);
633}
634
635void
636cb_wv_load_progress_changed(GObject            *obj,
637                            GParamSpec         *pspec,
638                            gpointer            data)
639{
640	(void)obj;
641	(void)pspec;
642
643	update_load_progress((struct Client *)data);
644}
645
646void
647cb_wv_show(WebKitWebView                       *web_view,
648           gpointer                             data)
649{
650	(void)web_view;
651
652	show_web_view((struct Client *)data);
653}
654
655gboolean
656cb_wv_tls_load_failed(GObject                  *obj,
657                      gchar                    *uri,
658                      GTlsCertificate          *crt,
659                      GTlsCertificateFlags      err,
660                      gpointer                  data)
661{
662	(void)obj;
663
664	render_tls_error((struct Client *)data, uri, crt, err);
665	return TRUE;
666}
667
668#endif /* !BROWSER_H */