adji

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

commit: 762f699595ad4d340d89434d0e2260e7329b7a2b
parent: f365a16b73bc181f503278e2e0523891187d5394
author: Chris Noxz <chris@noxz.tech>
date:   Sat, 20 Jan 2024 20:20:00 +0100
add about:(config|memory) feature
Mbrowser.c191+++++++++++++++++---
Mbrowser.h15+-
Mconfig.h2+-
3 files changed, 185 insertions(+), 23 deletions(-)
diff --git a/browser.c b/browser.c
@@ -20,6 +20,93 @@
 #include "config.h"
 
 
+void
+about_scheme_request(WebKitURISchemeRequest    *request,
+                     gpointer                   data)
+{
+	(void) data;
+
+	GError                 *e;                  /* error holder */
+	GInputStream           *s;                  /* stream to send */
+	GString                *m;                  /* message of stream */
+	const gchar            *p;                  /* path holder eg. 'config' */
+	gchar                 **w;                  /* temporary word holder */
+	gsize                   sl;                 /* size of stream */
+	int                     st[4] = {0};        /* memory stats */
+
+	p = webkit_uri_scheme_request_get_path(request);
+
+	/* about:config */
+	if (!strcmp(p, "config")) {
+		g_string_append_printf((m = g_string_new(NULL)), "<html><body>"
+		    "<table style=\"width:100%%;\">"
+		    "<tr>"
+		    "<th align=\"left\">Configurable Name</th>"
+		    "<th align=\"left\">Value</th>"
+		    "</tr>"
+		    "<tr><td>"__NAME__" version</td><td>%s</td></tr>"
+		    "<tr><td>WebKit version</td><td>%u.%u.%u</td></tr>",
+		    VERSION,
+		    webkit_get_major_version(),
+		    webkit_get_minor_version(),
+		    webkit_get_micro_version());
+		for (int i = 0; i < LastConfig; i++) {
+			if (cfg[i].e == NULL)
+				continue;
+			g_string_append_printf(m, "<tr><td>%s</td><td>", cfg[i].e);
+			switch (cfg[i].t) {
+			case CFG_STRING:
+				g_string_append_printf(m, "%s", cfg[i].v.s);
+				break;
+			case CFG_INT:
+				g_string_append_printf(m, "%d", cfg[i].v.i);
+				break;
+			case CFG_FLOAT:
+				g_string_append_printf(m, "%f", cfg[i].v.f);
+				break;
+			case CFG_BOOL:
+				g_string_append_printf(m, "%s", cfg[i].v.b ? "TRUE" : "FALSE");
+				break;
+			case CFG_LIST:
+				for (w = cfg[i].v.l; w && *w; w++)
+					g_string_append_printf(m, "%s%s", w == cfg[i].v.l
+					                       ? "" : "; ", *w);
+				break;
+			}
+			g_string_append_printf(m, "</td></tr>");
+		}
+		g_string_append_printf(m, "</table>");
+		g_string_append_printf(m, "</body></html>");
+	/* about:memory (only exists if memory can be read) */
+	} else if (!strcmp(p, "memory")
+	           && get_memory(&(st[0]), &(st[1]), &(st[2]), &(st[3]))) {
+		g_string_append_printf((m = g_string_new(NULL)), "<html><body>"
+		    "<table style=\"width:100%%;\">"
+		    "<tr><td>current real memory</td><td>%'d kB</tr>"
+		    "<tr><td>peak real memory</td><td>%'d kB</tr>"
+		    "<tr><td>current virt memory</td><td>%'d kB</tr>"
+		    "<tr><td>peak virt memory</td><td>%'d kB</tr>"
+		    "</table>"
+		    "</body></html>",
+		    st[0], st[1], st[2], st[3]
+		);
+	} else {
+		webkit_uri_scheme_request_finish_error(request, (e = g_error_new(
+		    BROWSER_ERROR,
+		    BROWSER_ERROR_INVALID_ABOUT_PATH,
+		    "Invalid 'about:%s' page", p)));
+		g_error_free(e);
+		return;
+	}
+
+	sl = strlen(m->str);
+	s = g_memory_input_stream_new_from_data(m->str, sl, NULL);
+	webkit_uri_scheme_request_finish(request, s, sl, "text/html");
+	g_object_unref(s);
+	g_string_free(m, TRUE);
+}
+
+
 struct Client*
 client_create(const gchar                      *uri,
               WebKitWebView                    *related_wv,
@@ -288,6 +375,48 @@ die(const char *msg)
 	exit(EXIT_FAILURE);
 }
 
+int
+get_memory(int                                 *cr, /* current real memory */
+           int                                 *pr, /* peak real memory */
+           int                                 *cv, /* current virtual memory */
+           int                                 *pv) /* peak virtual memory */
+{
+	char                    b[256] = {0};
+	FILE                   *f;
+
+	*cr = *pr = *cv = *pv = -1;
+
+	if (!(f = fopen("/proc/self/status", "r")))
+		return 0;
+
+	while (fscanf(f, " %255s", b) == 1)
+		if (!strcmp(b, "VmRSS:"))
+			fscanf(f, " %d", cr);
+		else if (!strcmp(b, "VmHWM:"))
+			fscanf(f, " %d", pr);
+		else if (!strcmp(b, "VmSize:"))
+			fscanf(f, " %d", cv);
+		else if (!strcmp(b, "VmPeak:"))
+			fscanf(f, " %d", pv);
+	fclose(f);
+
+	return (*cr != -1 && *pr != -1 && *cv != -1 && *pv != -1);
+}
+
+gchar*
+get_uri(GtkWidget* wv)
+{
+	const gchar *u;
+
+	if (!(u = webkit_web_view_get_uri(WEBKIT_WEB_VIEW(wv))))
+		return NULL;
+
+	if (!strncmp(u, ABOUT_SCHEME":", strlen(ABOUT_SCHEME":")))
+		return g_strconcat("about:", u + strlen(ABOUT_SCHEME":"), NULL);
+
+	return g_strdup(u);
+}
+
 gboolean
 ipc_request(GIOChannel                         *src,
             GIOCondition                        condition,
@@ -512,9 +641,9 @@ gboolean
 key_entry(struct Client                        *c,
           GdkEventKey                          *event)
 {
-	const gchar            *t,
-	                       *l;
-	gchar                  *u = NULL;
+	const gchar            *t;
+	gchar                  *u = NULL,
+	                       *l = NULL;
 	int                     p,
 	                        m;
 
@@ -546,22 +675,25 @@ key_entry(struct Client                        *c,
 			return TRUE;
 		if (t[0] != ':') { /* if not a command */
 			/* store current uri before loading new uri */
-			l = WV_GET_URI(c->wv);
+			l = get_uri(c->wv);
 			if ((u = resolve_uri(t)) != NULL)
 				webkit_web_view_load_uri(WEBKIT_WEB_VIEW(c->wv), u);
 			g_free(u);
 			/* fix: notify::uri won't be raised if current uri is unchanged */
-			if (g_strcmp0(l, WV_GET_URI(c->wv)) == 0)
+			if (!strcmp(l, (u = get_uri(c->wv))))
 				uri_changed(c);
+			g_free(u);
+			g_free(l);
 			return TRUE;
 		} else if (command(c, t + 1)) { /* if command */
 			return TRUE;
 		} /* FALL THROUGH */
 	case GDK_KEY_Escape:
 		gtk_widget_grab_focus(c->wv);
-		t = WV_GET_URI(c->wv);
-		gtk_entry_set_text(GTK_ENTRY(c->entry), (t == NULL ? __NAME__ : t));
+		u = get_uri(c->wv);
+		gtk_entry_set_text(GTK_ENTRY(c->entry), (u == NULL ? __NAME__ : u));
 		gtk_editable_set_position(GTK_EDITABLE(c->entry), -1);
+		g_free(u);
 		return TRUE;
 	}
 	return FALSE;
@@ -1053,6 +1185,7 @@ save_state(void)
 	FILE                   *fp;
 	WebKitWebView          *wv;
 	WebKitSettings         *s;
+	gchar                  *u = NULL;
 
 	/* only save state for ipc host, when state isn't locked for loading */
 	if (CFG_S(StateFile) == NULL || gl.ipc != IPC_HOST || gl.state_lock)
@@ -1068,7 +1201,7 @@ save_state(void)
 		c = gtk_container_get_children(
 		    GTK_CONTAINER(gtk_notebook_get_nth_page(GTK_NOTEBOOK(mw.nb), i))
 		);
-		if (strcmp(gtk_widget_get_name((c->data)), "WebKitWebView") != 0)
+		if (strcmp(gtk_widget_get_name((c->data)), "WebKitWebView"))
 			continue;
 		wv = WEBKIT_WEB_VIEW(c->data);
 		s = webkit_web_view_get_settings(wv);
@@ -1077,7 +1210,8 @@ save_state(void)
 			x |= STATE_CURRENT;
 		if (webkit_settings_get_enable_javascript_markup(s))
 			x |= STATE_JAVASCRIPT;
-		fprintf(fp, "%d:%s\n", x, WV_GET_URI(wv));
+		fprintf(fp, "%d:%s\n", x, (u = get_uri((GtkWidget*)wv)));
+		g_free(u);
 	}
 
 	fclose(fp);
@@ -1173,6 +1307,10 @@ set_default_web_context(void)
 
 	CB(c, "download-started", cb_download_start, NULL);
 	webkit_web_context_set_favicon_database_directory(c, NULL);
+	webkit_web_context_register_uri_scheme(
+	    c, ABOUT_SCHEME, (WebKitURISchemeRequestCallback)about_scheme_request,
+	    NULL, NULL
+	);
 
 	g_free(p);
 }
@@ -1182,6 +1320,7 @@ set_hover_uri(struct Client                    *c,
               WebKitHitTestResult              *hit_test_result)
 {
 	const char             *t;                  /* entry text holder */
+	gchar                  *u = NULL;           /* uri text holder */
 
 	g_free(c->hover_uri);
 
@@ -1190,12 +1329,14 @@ set_hover_uri(struct Client                    *c,
 		t = webkit_hit_test_result_get_link_uri(hit_test_result);
 		c->hover_uri = g_strdup(t);
 	} else {
-		t = WV_GET_URI(c->wv);
+		u = get_uri(c->wv);
 		c->hover_uri = NULL;
 	}
 
 	if (!gtk_widget_is_focus(c->entry))
-		gtk_entry_set_text(GTK_ENTRY(c->entry), t);
+		gtk_entry_set_text(GTK_ENTRY(c->entry), u ? u : t);
+
+	g_free(u);
 }
 
 void
@@ -1288,11 +1429,14 @@ resolve_uri(const gchar *t)
 	int                     r = -1;             /* result holder for wordexp */
 
 	/* cannot expand string in file scheme, so try without scheme */
-	if (strncmp("file://", t, 6) == 0 && (t[7] == '~' || t[7] == '$'))
+	if (!strncmp("file://", t, 7) && (t[7] == '~' || t[7] == '$'))
 		return resolve_uri(t + 7);
 
+	/* use internal about page so that about: prefix is ignored by WebKit */
+	if (!strncmp(t, "about:", 6) && strcmp(t, "about:blank"))
+		u = g_strdup_printf(ABOUT_SCHEME":%s", t + 6);
 	/* check if valid scheme, and if so just create a copy of the text */
-	if ((s = g_uri_peek_scheme(t)) && g_strv_contains(CFG_L(UriSchemes), s))
+	else if ((s = g_uri_peek_scheme(t)) && g_strv_contains(CFG_L(UriSchemes), s))
 		u = g_strdup(t);
 	/* if no match, then test xdg schemes (schemes that are redirected) */
 	else if (s && CFG_L(XdgSchemes) && g_strv_contains(CFG_L(XdgSchemes), s))
@@ -1422,11 +1566,11 @@ update_load_progress(struct Client *c)
 void
 update_title(struct Client *c)
 {
-	const gchar            *t,
+	const gchar            *t;
+	gchar                  *m = NULL,
 	                       *u;
-	gchar                  *m = NULL;
 
-	u = WV_GET_URI(c->wv);
+	u = get_uri(c->wv);
 	t = webkit_web_view_get_title(WEBKIT_WEB_VIEW(c->wv));
 
 	/* title priority: title, url, __NAME__ */
@@ -1455,31 +1599,36 @@ update_title(struct Client *c)
 	gtk_widget_set_tooltip_text(c->tab_label, t);
 	set_window_title(gtk_notebook_get_current_page(GTK_NOTEBOOK(mw.nb)));
 	g_free(m);
+	g_free(u);
 }
 
 void
 uri_changed(struct Client *c)
 {
-	const gchar            *t;
+	gchar                  *t = NULL;
 
 	/* make sure to not overwrite the "WEB PROCESS CRASHED" message */
-	if ((t = WV_GET_URI(c->wv)) != NULL && strlen(t) > 0) {
+	if ((t = get_uri(c->wv)) != NULL && strlen(t) > 0) {
 		gtk_entry_set_text(GTK_ENTRY(c->entry), t);
 		save_history(t);
 		save_state();
 	}
+
+	g_free(t);
 }
 
 void
 web_view_crashed(struct Client *c)
 {
-	gchar                  *t = NULL;
+	gchar                  *t = NULL,
+	                       *u = NULL;
 
 	gtk_entry_set_text(
 	    GTK_ENTRY(c->entry),
-	    (t = g_strdup_printf("WEB PROCESS CRASHED: %s", WV_GET_URI(c->wv)))
+	    (t = g_strdup_printf("WEB PROCESS CRASHED: %s", (u = get_uri(c->wv))))
 	);
 	g_free(t);
+	g_free(u);
 }
 
 void
@@ -1522,7 +1671,7 @@ main(int argc, char **argv)
 	if (optind >= argc && gl.clients == 0)
 		client_create(CFG_S(HomeUri), NULL, TRUE, TRUE);
 	/* load stdin if first argument is '-' */
-	if (optind < argc && strcmp(argv[optind], "-") == 0)
+	if (optind < argc && !strcmp(argv[optind], "-"))
 		load_stdin();
 	/* load remaining command line arguments as uris into new clients */
 	else if (optind < argc)
diff --git a/browser.h b/browser.h
@@ -57,7 +57,6 @@
 #define CFG_I(X)            cfg[(X)].v.i
 #define CFG_L(X)            (const gchar* const*)cfg[(X)].v.l
 #define CFG_S(X)            cfg[(X)].v.s
-#define WV_GET_URI(X)       webkit_web_view_get_uri(WEBKIT_WEB_VIEW((X)))
 
 #define TLS_MSG_FORMAT      "<h2>Could not validate TLS for: %s</h2><pre>%s"   \
                             "</pre><pre>s:%s\ni:%s\nv:NotBefore:%s; NotAfter:" \
@@ -87,6 +86,8 @@
 #define XDG_OPEN            "xdg-open"
 #define STATE_CURRENT       1 << 0
 #define STATE_JAVASCRIPT    1 << 1
+#define BROWSER_ERROR       (browser_error_quark())
+#define ABOUT_SCHEME        __NAME__"-about"
 
 enum javascript_policy {
 	JSP_DISABLE             = 0,
@@ -157,6 +158,15 @@ enum config_name {
 	LastConfig
 };
 
+enum browser_error {
+	BROWSER_ERROR_INVALID_ABOUT_PATH
+};
+
+static GQuark browser_error_quark()
+{
+	return g_quark_from_string(__NAME__"-quark");
+}
+
 typedef union {
 	gint                    i;              /* union integer */
 	gdouble                 f;              /* union float/double */
@@ -206,6 +216,7 @@ struct MainWindow
 } mw;
 
 /* "normal" functions */
+static void about_scheme_request(WebKitURISchemeRequest *, gpointer);
 static struct Client *client_create(const gchar *, WebKitWebView *, gboolean,
                                     gboolean);
 static void client_destroy(struct Client *);
@@ -215,6 +226,8 @@ static void create_context_menu(struct Client *, WebKitContextMenu *,
 static void create_context_menu_item(struct Client *, WebKitContextMenu *,
                                      const gchar *, const gchar *, void *);
 static void die(const char *);
+static int get_memory(int *, int *, int *, int *);
+static gchar *get_uri(GtkWidget*);
 static gboolean ipc_request(GIOChannel *, GIOCondition, gpointer);
 static ssize_t ipc_send(char *);
 static void ipc_setup(void);
diff --git a/config.h b/config.h
@@ -52,7 +52,7 @@ static Config cfg[LastConfig] = {
 	[SmoothScrolling]       = { __NAME_UPPERCASE__"_ENABLE_SMOOTH_SCROLLING",   "enable-smooth-scrolling",                  CFG_BOOL,   FALSE, {.b = FALSE }},
 	[StateFile]             = { __NAME_UPPERCASE__"_STATE_FILE",                NULL,                                       CFG_STRING, FALSE, {.s = NULL }},
 	[UcDir]                 = { NULL,                                           NULL,                                       CFG_STRING, FALSE, {.s = "styles" }},
-	[UriSchemes]            = { NULL,                                           NULL,                                       CFG_LIST,   FALSE, {.l = (gchar*[]){ "http", "https", "file", "about", "data", "webkit", NULL } }},
+	[UriSchemes]            = { NULL,                                           NULL,                                       CFG_LIST,   FALSE, {.l = (gchar*[]){ "http", "https", "file", "about", __NAME__"-about", "data", "webkit", NULL } }},
 	[UsDir]                 = { NULL,                                           NULL,                                       CFG_STRING, FALSE, {.s = "scripts" }},
 	[UserAgent]             = { __NAME_UPPERCASE__"_USER_AGENT",                "user-agent",                               CFG_STRING, FALSE, {.s = NULL }},
 	[WeDir]                 = { NULL,                                           NULL,                                       CFG_STRING, FALSE, {.s = "web_extensions" }},