adji

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

commit: 41835171d3e1928248a5c41bfd8fee516ce81227
parent: ab23be019c4193254ca28365693a33c6604f42fa
author: Chris Noxz <chris@noxz.tech>
date:   Sat, 28 Dec 2024 15:35:12 +0100
new year commit
Mbrowser.c1479+++++++-------------
Mbrowser.h701++++++----
Mconfig.h85+-
Mconfig.mk2+-
Mwe_redirect.c3+
5 files changed, 989 insertions(+), 1281 deletions(-)
diff --git a/browser.c b/browser.c
@@ -21,8 +21,9 @@
 
 
 void
-about_scheme_request(WebKitURISchemeRequest    *request,
-                     gpointer                   data)
+about_scheme_request(
+	WebKitURISchemeRequest                     *request,
+	gpointer                                    data)
 {
 	(void) data;
 
@@ -32,26 +33,33 @@ about_scheme_request(WebKitURISchemeRequest    *request,
 	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);
+	p = request_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\">Nonconfigurable 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>",
+		    "<tr><td>Gtk version</td><td>%u.%u.%u</td></tr>"
+		    "<tr><td>WebKit version</td><td>%u.%u.%u</td></tr>"
+		    "<tr>"
+		    "<th align=\"left\">Configurable Name</th>"
+		    "<th align=\"left\">Value</th>"
+		    "</tr>",
 		    VERSION,
+		    gtk_get_major_version(),
+		    gtk_get_minor_version(),
+		    gtk_get_micro_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)
+		for (int i = 0; i < CFN_LAST; i++) {
+			if (!cfg[i].e)
 				continue;
 			g_string_append_printf(m, "<tr><td>%s</td><td>", cfg[i].e);
 			switch (cfg[i].t) {
@@ -77,23 +85,10 @@ about_scheme_request(WebKitURISchemeRequest    *request,
 		}
 		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,
+		    BER_INVALID_ABOUT_PATH,
 		    "Invalid 'about:%s' page", p)));
 		g_error_free(e);
 		return;
@@ -106,196 +101,222 @@ about_scheme_request(WebKitURISchemeRequest    *request,
 	g_string_free(m, TRUE);
 }
 
-struct Client*
-client_create(const gchar                      *uri,
-              WebKitWebView                    *related_wv,
-              gboolean                          show,
-              gboolean                          focus_tab)
+void
+attach_to_host(
+	GtkWidget                                  *self)
+{
+	guchar                 *d = NULL;           /* data holder */
+	gint                    l,                  /* data length */
+	                        f;                  /* data format */
+	GdkAtom                 t;                  /* data atom type */
+	GtkWidget               *w;                 /* top widget (window?) */
+
+	if (GTK_IS_PLUG(w = gtk_widget_get_toplevel(self)) || !GTK_IS_WINDOW(w))
+		return;
+
+	if (gdk_property_get(get_widget_win(GTK_WIDGET(w)),
+	                     gdk_atom_intern(CFG_S(CFN_TAB_HOST), FALSE), GDK_NONE,
+	                     0, G_MAXLONG, FALSE, &t, &f, &l, &d)
+	                     && t == gdk_atom_intern("WINDOW", FALSE)
+	                     && f == 32
+	                     && l == sizeof(Window))
+		gl.embed = (long)*((Window*)d);
+	else
+		gl.embed = 0;
+	g_free(d);
+}
+
+void
+attach_to_window(
+	GtkWidget                                  *self)
+{
+	GtkWidget *vbx;
+	GtkWidget *wv;
+
+	gl.embed = 0;
+
+	if (!gl.initialized)
+		return;
+
+	gl.win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+
+	if (!(vbx = gtk_bin_get_child(GTK_BIN(self))))
+		return;
+
+	if (!(wv = gtk_container_get_children(GTK_CONTAINER(vbx))->data))
+		return;
+
+	/* window settings */
+	gtk_window_set_default_size(GTK_WINDOW(gl.win), 800, 600);
+	gtk_window_set_title(GTK_WINDOW(gl.win), __NAME__);
+
+	/* connect signal callbacks */
+	CB(gl.win,              "destroy",          cb_quit);
+	CB(gl.win,              "map-event",        cb_map);
+
+	g_signal_handlers_disconnect_by_data(G_OBJECT(self), NULL);
+
+	g_object_ref(vbx);
+	gtk_container_remove(GTK_CONTAINER(self), vbx);
+	gtk_container_add(GTK_CONTAINER(gl.win), vbx);
+	g_object_unref(vbx);
+
+	gtk_widget_destroy(self);
+	gtk_widget_show_all(gl.win);
+	gtk_window_set_focus(GTK_WINDOW(gl.win), wv);
+}
+
+void
+fork_url(
+	const gchar                                *uri)
+{
+	char e[64];
+
+	if (gl.embed > 0 && snprintf(e, sizeof(e), "%lu", gl.embed) > 0)
+		execvp(gl.arg0, (char *[]){ gl.arg0, "-E", e, (char*)uri, NULL });
+	else
+		execvp(gl.arg0, (char *[]){ gl.arg0, (char*)uri, NULL });
+}
+
+void
+initialize(
+	const gchar                                *uri)
 {
-	struct Client          *c;                  /* client to be */
 	gchar                  *u = NULL;           /* translated uri */
-	GtkWidget              *e,                  /* event box */
-	                       *t;                  /* tab box */
 	int                     i;
 
 	/* communicate uri through ipc if constructed */
-	if (uri != NULL && gl.ipc == IPC_CLIENT) {
-		if ((u = resolve_uri(uri)) == NULL || ipc_send(u) <= 0
-		    || ipc_send("\n") <= 0)
-			fprintf(stderr, __NAME__": Could not send message '%s'\n", u);
+	if (gl.initialized && uri) {
+		if ((u = resolve_uri(uri)) && fork() == 0)
+			fork_url(u);
 		g_free(u);
-		return NULL; /* the request was handled elsewhere */
+		return; /* the request was handled elsewhere */
 	}
 
+	if (gl.initialized)
+		return;
+
 	/* do not create a new client if existing uri was resolved to NULL, as it
 	 * was handled as an XDG-OPEN event. Still process uri == NULL for "create"
 	 * callback signals, such as "Open Link in New Window". */
-	if (uri != NULL && (u = resolve_uri(uri)) == NULL)
-		return NULL;
-
-	if (!(c = calloc(1, sizeof(struct Client))))
-		die(__NAME__ ": fatal: calloc failed\n");
+	if (uri && !(u = resolve_uri(uri)))
+		return;
 
-	if (related_wv == NULL)
-		c->wv = webkit_web_view_new();
-	else
-		c->wv = webkit_web_view_new_with_related_view(related_wv);
+	gl.wv = webkit_web_view_new();
 
 	/* connect signal callbacks */
-	CB(c->wv, "button-release-event",           cb_wv_hid, c);
-	CB(c->wv, "close",                          cb_wv_close, c);
-	CB(c->wv, "context-menu",                   cb_context_menu, c);
-	CB(c->wv, "create",                         cb_wv_create, NULL);
-	CB(c->wv, "decide-policy",                  cb_wv_decide_policy, NULL);
-	CB(c->wv, "key-press-event",                cb_wv_hid, c);
-	CB(c->wv, "load-changed",                   cb_wv_load_changed, c);
-	CB(c->wv, "load-failed-with-tls-errors",    cb_wv_tls_load_failed, c);
-	CB(c->wv, "mouse-target-changed",           cb_wv_hover, c);
-	CB(c->wv, "notify::estimated-load-progress",cb_wv_load_progress_changed, c);
-	CB(c->wv, "notify::favicon",                cb_favicon_changed, c);
-	CB(c->wv, "notify::title",                  cb_title_changed, c);
-	CB(c->wv, "notify::uri",                    cb_uri_changed, c);
-	CB(c->wv, "scroll-event",                   cb_wv_hid, c);
-	CB(c->wv, "web-process-crashed",            cb_wv_crashed, c);
+	CB(gl.wv, "button-release-event",           cb_wv_hid);
+	CB(gl.wv, "close",                          cb_wv_close);
+	CB(gl.wv, "context-menu",                   cb_context_menu);
+	CB(gl.wv, "create",                         cb_wv_create);
+	CB(gl.wv, "decide-policy",                  cb_wv_decide_policy);
+	CB(gl.wv, "key-press-event",                cb_wv_hid);
+	CB(gl.wv, "load-changed",                   cb_wv_load_changed);
+	CB(gl.wv, "load-failed-with-tls-errors",    cb_wv_tls_load_failed);
+	CB(gl.wv, "mouse-target-changed",           cb_wv_hover);
+	CB(gl.wv, "notify::estimated-load-progress",cb_wv_load_progress_changed);
+	CB(gl.wv, "notify::favicon",                cb_favicon_changed);
+	CB(gl.wv, "notify::title",                  cb_title_changed);
+	CB(gl.wv, "notify::uri",                    cb_uri_changed);
+	CB(gl.wv, "scroll-event",                   cb_wv_hid);
+	CB(gl.wv, "web-process-crashed",            cb_wv_crashed);
+	CB(gl.wv, "enter-fullscreen",               cb_wv_fullscreen_enter);
+	CB(gl.wv, "leave-fullscreen",               cb_wv_fullscreen_leave);
 
 	/* load config into webkit settings */
-	c->settings = webkit_settings_new();
-	for (i = 0; i < LastConfig; i++)
-		if (cfg[i].s != NULL)
+	gl.settings = webkit_settings_new();
+	for (i = 0; i < CFN_LAST; i++)
+		if (cfg[i].s)
 			g_object_set(
-			    G_OBJECT(c->settings),
+			    G_OBJECT(gl.settings),
 			    cfg[i].s,
 			    cfg[i].i ? (Arg)!(cfg[i].v.b) : (cfg[i].v),
 			    NULL
 			);
-	webkit_web_view_set_settings(WEBKIT_WEB_VIEW(c->wv), c->settings);
+	webkit_web_view_set_settings(WEBKIT_WEB_VIEW(gl.wv), gl.settings);
 
-	if (CFG_L(AcceptedLanguages) != NULL)
+	if (CFG_L(CFN_ACCEPTED_LANGUAGES))
 		webkit_web_context_set_preferred_languages(
-		    webkit_web_view_get_context(WEBKIT_WEB_VIEW(c->wv)),
-		    CFG_L(AcceptedLanguages)
+		    webkit_web_view_get_context(WEBKIT_WEB_VIEW(gl.wv)),
+		    CFG_L(CFN_ACCEPTED_LANGUAGES)
 		);
-	if (CFG_S(CookieFile) != NULL)
+	if (CFG_S(CFN_COOKIE_FILE))
 		webkit_cookie_manager_set_persistent_storage(
 		    webkit_web_context_get_cookie_manager(
-		        webkit_web_view_get_context(WEBKIT_WEB_VIEW(c->wv))
+		        webkit_web_view_get_context(WEBKIT_WEB_VIEW(gl.wv))
 		    ),
-		    CFG_S(CookieFile),
+		    CFG_S(CFN_COOKIE_FILE),
 		    WEBKIT_COOKIE_PERSISTENT_STORAGE_TEXT
 		);
-	if (CFG_S(ProxyUri) != NULL)
+	if (CFG_S(CFN_PROXY_URI))
 		webkit_website_data_manager_set_network_proxy_settings(
-		    webkit_web_view_get_website_data_manager(WEBKIT_WEB_VIEW(c->wv)),
+		    webkit_web_view_get_website_data_manager(WEBKIT_WEB_VIEW(gl.wv)),
 		    WEBKIT_NETWORK_PROXY_MODE_CUSTOM,
 		    webkit_network_proxy_settings_new(
-		        CFG_S(ProxyUri), CFG_L(ProxyIgnore)
+		        CFG_S(CFN_PROXY_URI), CFG_L(CFN_PROXY_IGNORE)
 		    )
 		);
 	webkit_web_view_set_zoom_level(
-	    WEBKIT_WEB_VIEW(c->wv), CFG_F(ZoomLevel)
+	    WEBKIT_WEB_VIEW(gl.wv), CFG_F(CFN_ZOOM_LEVEL)
 	);
 
 	/* create entry */
-	c->entry = gtk_entry_new();
-	CB(c->entry, "key-press-event",             cb_entry_hid, c);
-	CB(c->entry, "icon-release",                cb_entry_icon_hid, c);
+	gl.entry = gtk_entry_new();
+	CB(gl.entry, "key-press-event",             cb_entry_hid);
+	CB(gl.entry, "icon-release",                cb_entry_icon_hid);
 	gtk_entry_set_icon_from_icon_name(
-	    GTK_ENTRY(c->entry), GTK_ENTRY_ICON_SECONDARY, CFG_B(DisableJavaScript)
-	    ? ICON_JS_OFF : ICON_JS_ON
+	    GTK_ENTRY(gl.entry), GTK_ENTRY_ICON_SECONDARY,
+	    CFG_B(CFN_DISABLE_JAVASCRIPT) ? ICON_JS_OFF : ICON_JS_ON
 	);
 
 	/* create vertical box to store the web view and the entry */
-	c->vbx = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
-	gtk_box_pack_start(GTK_BOX(c->vbx), c->wv, TRUE, TRUE, 0);
-	gtk_box_pack_start(GTK_BOX(c->vbx), c->entry, FALSE, FALSE, 0);
-	gtk_container_set_focus_child(GTK_CONTAINER(c->vbx), c->wv);
-
-	/* create tab containing an icon and a label */
-	c->tab_icon = gtk_image_new_from_icon_name(ICON_GLOBE, GTK_ICON_SIZE_MENU);
-	c->tab_label = gtk_label_new(__NAME__);
-	gtk_label_set_ellipsize(GTK_LABEL(c->tab_label), PANGO_ELLIPSIZE_END);
-	gtk_widget_set_halign(GTK_WIDGET(c->tab_label), GTK_ALIGN_START);
-	gtk_widget_set_hexpand(GTK_WIDGET(c->tab_label), TRUE);
-	gtk_widget_set_has_tooltip(GTK_WIDGET(c->tab_label), TRUE);
-	/* pack icon and label in a horizontal box */
-	t = gtk_box_new(
-	    GTK_ORIENTATION_HORIZONTAL, 5 * gtk_widget_get_scale_factor(mw.win)
-	);
-	gtk_box_pack_start(GTK_BOX(t), c->tab_icon, FALSE, FALSE, 0);
-	gtk_box_pack_start(
-	    GTK_BOX(t),
-	    c->tab_label,
-	    TRUE,
-	    TRUE,
-	    3 * gtk_widget_get_scale_factor(mw.win)
-	);
-	/* back the tab inside an event box to enable scroll and button events */
-	e = gtk_event_box_new();
-	gtk_container_add(GTK_CONTAINER(e), t);
-	gtk_widget_add_events(e, GDK_SCROLL_MASK);
-	CB(e, "button-release-event",               cb_tab_hid, c);
-	CB(e, "scroll-event",                       cb_tab_hid, c);
-	/* store a (non client) reference to the label for later use */
-	g_object_set_data(G_OBJECT(e), __NAME__"-tab-label", c->tab_label);
-	/* show tab */
-	gtk_widget_show_all(e);
-
-	/* append everything reorderable to the notebook after current page */
-	gtk_notebook_insert_page(
-	    GTK_NOTEBOOK(mw.nb),
-	    c->vbx,
-	    e,
-	    gtk_notebook_get_current_page(GTK_NOTEBOOK(mw.nb)) + 1
-	);
-	gtk_notebook_set_tab_reorderable(GTK_NOTEBOOK(mw.nb), c->vbx, TRUE);
+	gl.vbx = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
+	gtk_box_pack_start(GTK_BOX(gl.vbx), gl.wv, TRUE, TRUE, 0);
+	gtk_box_pack_start(GTK_BOX(gl.vbx), gl.entry, FALSE, FALSE, 0);
+	gtk_container_set_focus_child(GTK_CONTAINER(gl.vbx), gl.wv);
+
+	/* pack everything in a vertical box */
+	gtk_container_add(GTK_CONTAINER(gl.win), gl.vbx);
 
 	/* manage which tab should be focused */
-	c->focus_new_tab = focus_tab;
-	if (show)
-		show_web_view(c);
-	else
-		CB(c->wv, "ready-to-show",              cb_wv_show, c);
+	gtk_widget_show_all(gl.win);
+	gtk_window_set_focus(GTK_WINDOW(gl.win), gl.wv);
 
 	/* finally load the uri */
-	if (u != NULL)
-		webkit_web_view_load_uri(WEBKIT_WEB_VIEW(c->wv), u);
+	if (u)
+		webkit_web_view_load_uri(WEBKIT_WEB_VIEW(gl.wv), u);
 	g_free(u);
 
-	gl.clients++;
-	return c;
+	load_user_styles(WEBKIT_WEB_VIEW(gl.wv));
+	load_user_scripts(WEBKIT_WEB_VIEW(gl.wv));
+
+	if (uri && !strcmp(CFG_S(CFN_HOME_URI), uri))
+		gtk_widget_grab_focus(gl.entry);
+
+	gl.initialized = TRUE;
 }
 
 void
-client_destroy(struct Client *c)
+terminate(void)
 {
-	int                     i;
-
 	/* disconnect all handlers */
-	g_signal_handlers_disconnect_by_data(G_OBJECT(c->wv), c);
-	g_signal_handlers_disconnect_by_data(G_OBJECT(c->wv), NULL);
-
-	if ((i = gtk_notebook_page_num(GTK_NOTEBOOK(mw.nb), c->vbx)) != -1)
-		gtk_notebook_remove_page(GTK_NOTEBOOK(mw.nb), i);
-	else
-		fprintf(stderr, __NAME__": Page does not exist in notebook\n");
+	g_signal_handlers_disconnect_by_data(G_OBJECT(gl.wv), NULL);
 
-	free(c);
-
-	if (--gl.clients == 0)
-		quit();
+	gl.initialized = FALSE;
+	quit();
 }
 
 gboolean
-command(struct Client                          *c,
-        const gchar                            *t)
+command(
+	const gchar                                *t)
 {
 	if (t[0] == '/') {          /* in-page search */
 		g_free(gl.search_text);
 		gl.search_text = g_strdup(t + 1);
-		search(c, IPS_INIT);
+		search(IPS_INIT);
 		return TRUE;
 	} else if (t[0] == 'q') {   /* quit (vim-like) */
-		quit();
+		terminate();
 		return TRUE;
 	}
 
@@ -303,9 +324,9 @@ command(struct Client                          *c,
 }
 
 void
-create_context_menu(struct Client              *c,
-                    WebKitContextMenu          *context_menu,
-                    WebKitHitTestResult        *hit_test_result)
+create_context_menu(
+	WebKitContextMenu                          *context_menu,
+	WebKitHitTestResult                        *hit_test_result)
 {
 	guint                   x;                  /* hit test result context */
 
@@ -316,20 +337,16 @@ create_context_menu(struct Client              *c,
 	);
 
 	/* if document is the only context */
-	if (x == WEBKIT_HIT_TEST_RESULT_CONTEXT_DOCUMENT) {
-		create_context_menu_item(
-		    c,
+	if (x == WEBKIT_HIT_TEST_RESULT_CONTEXT_DOCUMENT)
+		return create_context_menu_item(
 		    context_menu,
 		    "open-external",
 		    "Open Page Externally",
 		    cb_open_external
-		);
-		return; /* no need to check further */
-	}
+		); /* no need to check further */
 
 	if (x & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK)
 		create_context_menu_item(
-		    c,
 		    context_menu,
 		    "open-external",
 		    "Open Link Externally",
@@ -337,12 +354,11 @@ create_context_menu(struct Client              *c,
 		);
 
 	/* requires javascript for DOM access from here on */
-	if (!webkit_settings_get_enable_javascript(c->settings))
+	if (!webkit_settings_get_enable_javascript(gl.settings))
 		return;
 
 	if (x & WEBKIT_HIT_TEST_RESULT_CONTEXT_SELECTION)
 		create_context_menu_item(
-		    c,
 		    context_menu,
 		    "selection-search",
 		    "Search Selection",
@@ -351,15 +367,15 @@ create_context_menu(struct Client              *c,
 }
 
 void
-create_context_menu_item(struct Client          *c,
-                         WebKitContextMenu      *context_menu,
-                         const gchar            *name,
-                         const gchar            *label,
-                         void                   *action)
+create_context_menu_item(
+	WebKitContextMenu                          *context_menu,
+	const gchar                                *name,
+	const gchar                                *label,
+	void                                       *action)
 {
 	GAction                *a = (GAction*)g_simple_action_new(name, NULL);
 
-	CB(a, "activate", action, c);
+	CB(a, "activate", action);
 	webkit_context_menu_prepend(
 	    context_menu,
 	    webkit_context_menu_item_new_from_gaction(a, label, NULL)
@@ -368,44 +384,30 @@ create_context_menu_item(struct Client          *c,
 }
 
 void
-die(const char *msg)
+download_blob(
+	WebKitDownload                             *d)
 {
-	fprintf(stderr, msg);
-	exit(EXIT_FAILURE);
+	WebKitURIResponse *r = webkit_download_get_response(d);
+	const gchar *u = webkit_uri_response_get_uri(r);
+
+	if (strncmp(u, "blob:", 5) && strncmp(u, "data:", 5))
+		return;
+
+	webkit_download_set_destination(d, CFG_S(CFN_BLOB_FILE));
 }
 
-int
-get_memory(int                                 *cr, /* current real memory */
-           int                                 *pr, /* peak real memory */
-           int                                 *cv, /* current virtual memory */
-           int                                 *pv) /* peak virtual memory */
+void
+download_response(
+	WebKitURIResponse                          *r)
 {
-	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);
+	xdg_open("adjidl:", webkit_uri_response_get_uri(r), TRUE);
 }
 
 gchar*
-get_uri(GtkWidget* wv)
+get_uri(
+	GtkWidget                                  *wv)
 {
-	const gchar *u;
+	const gchar            *u;
 
 	if (!(u = webkit_web_view_get_uri(WEBKIT_WEB_VIEW(wv))))
 		return NULL;
@@ -417,219 +419,145 @@ get_uri(GtkWidget* wv)
 }
 
 gboolean
-ipc_request(GIOChannel                         *src,
-            GIOCondition                        condition,
-            gpointer                            data)
+handle_fullscreen(
+	gboolean                                    fullscreen)
 {
-	gchar                  *u = NULL;           /* uri received */
+	GdkDisplay             *d = gdk_display_get_default();
+	GdkWindow              *p = GTK_IS_PLUG(gl.win)
+	                          ? get_socket_win(gl.win)
+	                          : to_gdk_win(d, gl.embed);
 
-	(void)condition;        /* not used */
-	(void)data;             /* not used */
+	if (fullscreen) /* hide or show widgets depending on fullscreen */
+		gtk_widget_hide(gl.entry);
+	else
+		gtk_widget_show_all(gl.entry);
 
-	g_io_channel_read_line(src, &u, NULL, NULL, NULL);
-	if (u) {
-		g_strstrip(u);
-		client_create(u, NULL, TRUE, TRUE);
-		g_free(u);
-	}
-	return TRUE;
-}
+	if (!p) /* if not embeded we are done! */
+		return TRUE;
 
-ssize_t
-ipc_send(char *data)
-{
-	size_t                  s = 0,              /* data amount sent */
-	                        l = strlen(data);   /* data length */
-	ssize_t                 w;                  /* data written */
-
-	while (s < l) { /* write until everything is sent */
-		if ((w = write(gl.ipc_pipe_fd, data + s, l - s)) == -1) {
-			if (errno == EINTR)
-				continue;       /* EINTR: so retry */
-			perror(__NAME__": Error writing to fifo");
-			return w;
-		} else if (w == 0) {
-			return 0;
-		}
-		s += w;
-	}
+	gtk_widget_grab_focus(gl.wv);
 
-	return s;
+	if (fullscreen) /* tell parent to go fullscreen */
+		gdk_window_fullscreen(p);
+	else
+		gdk_window_unfullscreen(p);
+	return TRUE;
 }
 
-void
-ipc_setup(void)
+gboolean
+key_common(
+	GdkEventKey                                *event)
 {
-	gchar                  *f = NULL,           /* fifo file name */
-	                       *p = NULL;           /* fifo file path */
-
-	/* only setup for ipc clients */
-	if (gl.ipc != IPC_CLIENT)
-		return;
+	gchar                  *u = NULL;
+	gboolean                f = is_fullscreen();
 
-	/* create fifo file path and allow for several ipc setups by naming them */
-	f = g_strdup_printf("%s_%s.fifo", __NAME__, CFG_S(FifoName));
-	p = g_build_filename(g_get_user_runtime_dir(), f, NULL);
-
-	/* fail hard if fifo cannot be created */
-	if (!g_file_test(p, G_FILE_TEST_EXISTS) && mkfifo(p, 0600) == -1)
-		die(__NAME__ ": fatal: mkfifo failed\n");
-
-	/* if no one is listening create the ipc host instance */
-	if ((gl.ipc_pipe_fd = open(p, O_WRONLY | O_NONBLOCK)) == -1) {
-		g_io_add_watch(
-		    g_io_channel_new_file(p, "r+", NULL),
-		    G_IO_IN,
-		    (GIOFunc)ipc_request,
-		    NULL
-		);
-		gl.ipc = IPC_HOST;  /* convert client to host */
+	/* key presses not using the alt key */
+	switch (event->keyval) {
+	case GDK_KEY_F11: /* toggle window fullscreen state */
+		if (f) {
+			gtk_window_unfullscreen(GTK_WINDOW(gl.win));
+			handle_fullscreen(!f);
+		} else {
+			gtk_window_fullscreen(GTK_WINDOW(gl.win));
+			handle_fullscreen(f);
+		}
+		return TRUE;
 	}
 
-	g_free(f);
-	g_free(p);
-}
+	if (event->state & GDK_CONTROL_MASK) /* ctrl key commands */
+		switch (event->keyval) {
+		case GDK_KEY_h:
+		case GDK_KEY_j:
+		case GDK_KEY_k:
+		case GDK_KEY_l: /* in-page movement by emulating arrow keys... */
+		case GDK_KEY_g:
+		case GDK_KEY_G: /* ... as well as home and end */
+			event->keyval /* translate key press accordingly */
+			    = event->keyval == GDK_KEY_h ? GDK_KEY_Left
+			    : event->keyval == GDK_KEY_j ? GDK_KEY_Down
+			    : event->keyval == GDK_KEY_k ? GDK_KEY_Up
+			    : event->keyval == GDK_KEY_l ? GDK_KEY_Right
+			    : event->keyval == GDK_KEY_g ? GDK_KEY_Home
+			    : /*                        */ GDK_KEY_End;
+			event->state = 0;
+			gtk_propagate_event(GTK_WIDGET(gl.wv), (GdkEvent*)event);
+			return TRUE;
+		}
 
-gboolean
-key_common(struct Client                       *c,
-           GdkEventKey                         *event)
-{
-	int                     i,
-	                        m;
-	gchar                  *u = NULL;
+	if (f) /* do not handle key events in fullscreen */
+		return FALSE;
 
 	/* only handle key presses using the alt key */
 	if (!(event->state & GDK_MOD1_MASK))
 		return FALSE;
 
-	/* key presses using ctrl+alt keys */
-	if (event->state & GDK_CONTROL_MASK) {
-		switch (event->keyval) {
-		case GDK_KEY_J:
-		case GDK_KEY_K: /* move page/tab backwards or forwards in stack */
-			i = gtk_notebook_page_num(GTK_NOTEBOOK(mw.nb), c->vbx);
-			m = gtk_notebook_get_n_pages(GTK_NOTEBOOK(mw.nb)) - 1;
-			i += event->keyval == GDK_KEY_J
-			    ? i == 0 ? 0 : -1
-			    : i == m ? m : 1;
-			gtk_notebook_reorder_child(GTK_NOTEBOOK(mw.nb), c->vbx, i);
-			return TRUE;
-		case GDK_KEY_j: /* go to previous page/tab */
-			gtk_notebook_prev_page(GTK_NOTEBOOK(mw.nb));
-			return TRUE;
-		case GDK_KEY_k: /* go to next page/tab */
-			gtk_notebook_next_page(GTK_NOTEBOOK(mw.nb));
-			return TRUE;
-		}
-	}
-
 	switch (event->keyval) {
 	case GDK_KEY_q: /* destroy client and close tab */
-		client_destroy(c);
+		terminate();
 		return TRUE;
 	case GDK_KEY_w: /* go to home page */
-		if ((u = resolve_uri(CFG_S(HomeUri))) != NULL)
-			webkit_web_view_load_uri(WEBKIT_WEB_VIEW(c->wv), u);
+		if ((u = resolve_uri(CFG_S(CFN_HOME_URI))))
+			webkit_web_view_load_uri(WEBKIT_WEB_VIEW(gl.wv), u);
 		g_free(u);
 		return TRUE;
-	case GDK_KEY_t: /* create new client/tab */
-		if ((u = resolve_uri(CFG_S(HomeUri))) != NULL) {
-			c = client_create(u, NULL, TRUE, TRUE);
-			g_free(u);
-			gtk_widget_grab_focus(c->entry);
-		}
-		return TRUE;
 	case GDK_KEY_r: /* reload, while bypassing cache */
-		webkit_web_view_reload_bypass_cache(WEBKIT_WEB_VIEW(c->wv));
+		webkit_web_view_reload_bypass_cache(WEBKIT_WEB_VIEW(gl.wv));
 		return TRUE;
 	case GDK_KEY_i:
-		toggle_inspector(c);
+		toggle_inspector();
 		return TRUE;
 	case GDK_KEY_o: /* focus entry */
-		gtk_widget_grab_focus(c->entry);
+		gtk_widget_grab_focus(gl.entry);
 		return TRUE;
 	case GDK_KEY_n: /* search forward */
-		search(c, IPS_FORWARD);
+		search(IPS_FORWARD);
 		return TRUE;
 	case GDK_KEY_N: /* search backward */
-		search(c, IPS_BACKWARD);
+		search(IPS_BACKWARD);
 		return TRUE;
 	case GDK_KEY_slash: /* initiate in-page search */
-		gtk_widget_grab_focus(c->entry);
-		gtk_entry_set_text(GTK_ENTRY(c->entry), ":/");
-		gtk_editable_set_position(GTK_EDITABLE(c->entry), -1);
-		return TRUE;
-	case GDK_KEY_b: /* toggle tab visibility */
-		gtk_notebook_set_show_tabs(
-		    GTK_NOTEBOOK(mw.nb),
-		    !gtk_notebook_get_show_tabs(GTK_NOTEBOOK(mw.nb))
-		);
-		return TRUE;
-	case GDK_KEY_1:
-	case GDK_KEY_2:
-	case GDK_KEY_3:
-	case GDK_KEY_4:
-	case GDK_KEY_5:
-	case GDK_KEY_6:
-	case GDK_KEY_7:
-	case GDK_KEY_8:
-	case GDK_KEY_9: /* go to nth tab */
-		gtk_notebook_set_current_page(GTK_NOTEBOOK(mw.nb), event->keyval-0x31);
+		gtk_widget_grab_focus(gl.entry);
+		gtk_entry_set_text(GTK_ENTRY(gl.entry), ":/");
+		gtk_editable_set_position(GTK_EDITABLE(gl.entry), -1);
 		return TRUE;
 	case GDK_KEY_0:
 	case GDK_KEY_minus:
 	case GDK_KEY_equal: /* set zoom level */
 		webkit_web_view_set_zoom_level(
-		    WEBKIT_WEB_VIEW(c->wv),
+		    WEBKIT_WEB_VIEW(gl.wv),
 		    event->keyval == GDK_KEY_0
-		    ? CFG_F(ZoomLevel)
-		    : webkit_web_view_get_zoom_level(WEBKIT_WEB_VIEW(c->wv))
+		    ? CFG_F(CFN_ZOOM_LEVEL)
+		    : webkit_web_view_get_zoom_level(WEBKIT_WEB_VIEW(gl.wv))
 		    + ((event->keyval == GDK_KEY_minus) ? -0.1 : 0.1)
 		);
 		return TRUE;
 	case GDK_KEY_H: /* go back in history */
-		webkit_web_view_go_back(WEBKIT_WEB_VIEW(c->wv));
+		webkit_web_view_go_back(WEBKIT_WEB_VIEW(gl.wv));
 		return TRUE;
 	case GDK_KEY_L: /* go forward in history */
-		webkit_web_view_go_forward(WEBKIT_WEB_VIEW(c->wv));
+		webkit_web_view_go_forward(WEBKIT_WEB_VIEW(gl.wv));
 		return TRUE;
 	case GDK_KEY_s: /* toggle javascript (on or off) */
-		set_javascript_policy(c, JSP_TOGGLE);
+		set_javascript_policy(JSP_TOGGLE);
 		return TRUE;
 	case GDK_KEY_c: /* toggle tls error policy (ignore or fail) */
-		toggle_tls_error_policy(c);
-		return TRUE;
-	case GDK_KEY_h:
-	case GDK_KEY_j:
-	case GDK_KEY_k:
-	case GDK_KEY_l: /* in-page movement by emulating arrow keys... */
-	case GDK_KEY_g:
-	case GDK_KEY_G: /* ... as well as home and end */
-		event->keyval /* translate key press accordingly */
-		    = event->keyval == GDK_KEY_h ? GDK_KEY_Left
-		    : event->keyval == GDK_KEY_j ? GDK_KEY_Down
-		    : event->keyval == GDK_KEY_k ? GDK_KEY_Up
-		    : event->keyval == GDK_KEY_l ? GDK_KEY_Right
-		    : event->keyval == GDK_KEY_g ? GDK_KEY_Home
-		    : /*                        */ GDK_KEY_End;
-		event->state = 0;
-		gtk_propagate_event(GTK_WIDGET(c->wv), (GdkEvent*)event);
+		toggle_tls_error_policy();
 		return TRUE;
 	case GDK_KEY_p: /* open print dialog */
 		webkit_print_operation_run_dialog(
-		    webkit_print_operation_new(WEBKIT_WEB_VIEW(c->wv)),
-		    GTK_WINDOW(mw.win)
+		    webkit_print_operation_new(WEBKIT_WEB_VIEW(gl.wv)),
+		    GTK_WINDOW(gl.win)
 		);
 		return TRUE;
 	}
 
 	/* check external handler keys */
-	if (cfg[ExternalHandlerKeys].v.b &&
+	if (cfg[CFN_EXTERNAL_HANDLER_KEYS].v.b &&
 	    event->keyval < 256 &&
 	    g_strv_contains(
-	        CFG_L(ExternalHandlerKeys), (gchar[]){(gchar)event->keyval, 0}
+	        CFG_L(CFN_EXTERNAL_HANDLER_KEYS), (gchar[]){(gchar)event->keyval, 0}
 	    )) {
-		open_external(c, (gchar)event->keyval);
+		open_external((gchar)event->keyval);
 		return TRUE;
 	}
 
@@ -637,8 +565,8 @@ key_common(struct Client                       *c,
 }
 
 gboolean
-key_entry(struct Client                        *c,
-          GdkEventKey                          *event)
+key_entry(
+	GdkEventKey                                *event)
 {
 	const gchar            *t;
 	gchar                  *u = NULL,
@@ -651,47 +579,46 @@ key_entry(struct Client                        *c,
 		switch (event->keyval) { /* movements inside entry */
 		case GDK_KEY_l:
 		case GDK_KEY_h:
-			p = gtk_editable_get_position(GTK_EDITABLE(c->entry));
-			m = gtk_entry_get_text_length(GTK_ENTRY(c->entry));
+			p = gtk_editable_get_position(GTK_EDITABLE(gl.entry));
+			m = gtk_entry_get_text_length(GTK_ENTRY(gl.entry));
 			p += event->keyval == GDK_KEY_l
 				? m > p ? 1 : 0     /* move right */
 				: 0 < p ? -1 : 0;   /* move left */
-			gtk_editable_set_position(GTK_EDITABLE(c->entry), p);
+			gtk_editable_set_position(GTK_EDITABLE(gl.entry), p);
 			return TRUE;
 		}
 	}
 
 	/* handle common key presses */
-	if (key_common(c, event))
+	if (key_common(event))
 		return TRUE;
 
 	/* handle any key press (not just using the alt key) */
 	switch (event->keyval) {
 	case GDK_KEY_KP_Enter:
 	case GDK_KEY_Return:
-		gtk_widget_grab_focus(c->wv);
-		if ((t = gtk_entry_get_text(GTK_ENTRY(c->entry))) == NULL)
+		gtk_widget_grab_focus(gl.wv);
+		if (!(t = gtk_entry_get_text(GTK_ENTRY(gl.entry))))
 			return TRUE;
 		if (t[0] != ':') { /* if not a command */
 			/* store current uri before loading new uri */
-			l = get_uri(c->wv);
-			if ((u = resolve_uri(t)) != NULL)
-				webkit_web_view_load_uri(WEBKIT_WEB_VIEW(c->wv), u);
+			l = get_uri(gl.wv);
+			if ((u = resolve_uri(t)))
+				webkit_web_view_load_uri(WEBKIT_WEB_VIEW(gl.wv), u);
 			g_free(u);
 			/* fix: notify::uri won't be raised if current uri is unchanged */
-			if (!strcmp(l, (u = get_uri(c->wv))))
-				uri_changed(c);
-			g_free(u);
-			g_free(l);
+			if (!strcmp(l, (u = get_uri(gl.wv))))
+				uri_changed();
+			g_free_all((gpointer[]){u, l, NULL});
 			return TRUE;
-		} else if (command(c, t + 1)) { /* if command */
+		} else if (command(t + 1)) { /* if command */
 			return TRUE;
 		} /* FALL THROUGH */
 	case GDK_KEY_Escape:
-		gtk_widget_grab_focus(c->wv);
-		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);
+		gtk_widget_grab_focus(gl.wv);
+		u = get_uri(gl.wv);
+		gtk_entry_set_text(GTK_ENTRY(gl.entry), (!u ? __NAME__ : u));
+		gtk_editable_set_position(GTK_EDITABLE(gl.entry), -1);
 		g_free(u);
 		return TRUE;
 	}
@@ -700,69 +627,35 @@ key_entry(struct Client                        *c,
 }
 
 gboolean
-key_tab(struct Client                          *c,
-        GdkEvent                               *event)
-{
-	GdkScrollDirection      d;
-
-	if (event->type == GDK_BUTTON_RELEASE) {
-		if (((GdkEventButton *)event)->button == 2) { /* scroll wheel click */
-			client_destroy(c);
-			return TRUE;
-		}
-	} else if (event->type == GDK_SCROLL) { /* scroll wheel selection */
-		gdk_event_get_scroll_direction(event, &d);
-		if (d == GDK_SCROLL_UP)
-			gtk_notebook_next_page(GTK_NOTEBOOK(mw.nb));
-		else if (d == GDK_SCROLL_DOWN)
-			gtk_notebook_prev_page(GTK_NOTEBOOK(mw.nb));
-		return TRUE;
-	}
-
-	/* regain lost focus while clicking a tab (not a perfect solution)
-	 * note: this won't work if the cursor is dragged while a button is
-	 *   pressed. One pseudo solution for this would be to subscribe to
-	 *   event-after (specifically the GDK_ENTER_NOTIFY event type) on tab hid
-	 *   events, however this only works if the selected tab is hovered while a
-	 *   button is released and would also cause the corresponding web view for
-	 *   any hovering tab to grab focus (effectively stealing focus from the
-	 *   current web view or the entry when not desirable). */
-	gtk_widget_grab_focus(c->wv);
-	gtk_editable_set_position(GTK_EDITABLE(c->entry), -1);
-
-	return FALSE;
-}
-
-gboolean
-key_web_view(struct Client                     *c,
-             GdkEvent                          *event)
+key_web_view(
+	GdkEvent                                   *event)
 {
 	gdouble                 dx,                 /* scroll x-delta */
 	                        dy;                 /* scroll y-delta */
 
 	/* handle common key presses */
-	if (event->type == GDK_KEY_PRESS && key_common(c, (GdkEventKey *)event))
+	if (event->type == GDK_KEY_PRESS && key_common((GdkEventKey *)event))
 		return TRUE;
 
 	/* escape key: stop web rendering */
 	if (event->type == GDK_KEY_PRESS
 	    && ((GdkEventKey *)event)->keyval == GDK_KEY_Escape) {
-		webkit_web_view_stop_loading(WEBKIT_WEB_VIEW(c->wv));
-		gtk_entry_set_progress_fraction(GTK_ENTRY(c->entry), 0);
+		webkit_web_view_stop_loading(WEBKIT_WEB_VIEW(gl.wv));
+		gtk_entry_set_progress_fraction(GTK_ENTRY(gl.entry), 0);
 	/* mouse button events */
 	} else if (event->type == GDK_BUTTON_RELEASE) {
 		switch (((GdkEventButton *)event)->button) {
-		case 2:  /* mouse middle button: open in new tab */
-			if (c->hover_uri != NULL) {
-				client_create(c->hover_uri, NULL, TRUE, FALSE);
+		case 2:  /* mouse middle button: create new client */
+			if (gl.hover_uri) {
+				initialize(gl.hover_uri);
 				return TRUE;
 			}
 			break;
 		case 8:  /* mouse button back: go back in history */
-			webkit_web_view_go_back(WEBKIT_WEB_VIEW(c->wv));
+			webkit_web_view_go_back(WEBKIT_WEB_VIEW(gl.wv));
 			return TRUE;
 		case 9:  /* mouse button forward: go forward in history */
-			webkit_web_view_go_forward(WEBKIT_WEB_VIEW(c->wv));
+			webkit_web_view_go_forward(WEBKIT_WEB_VIEW(gl.wv));
 			return TRUE;
 		}
 	/* scroll using alt pressed: zoom in or out */
@@ -770,10 +663,10 @@ key_web_view(struct Client                     *c,
 	           && (((GdkEventScroll *)event)->state & GDK_MOD1_MASK)) {
 		gdk_event_get_scroll_deltas(event, &dx, &dy);
 		webkit_web_view_set_zoom_level(
-		    WEBKIT_WEB_VIEW(c->wv),
+		    WEBKIT_WEB_VIEW(gl.wv),
 		    dx != 0
-		    ? CFG_F(ZoomLevel)
-		    : webkit_web_view_get_zoom_level(WEBKIT_WEB_VIEW(c->wv)) + -dy * 0.1
+		    ? CFG_F(CFN_ZOOM_LEVEL)
+		    : webkit_web_view_get_zoom_level(WEBKIT_WEB_VIEW(gl.wv)) + -dy * 0.1
 		);
 		return TRUE;
 	}
@@ -782,35 +675,35 @@ key_web_view(struct Client                     *c,
 }
 
 void
-load_changed(struct Client                     *c,
-             WebKitLoadEvent                    event_type)
+load_changed(
+	WebKitLoadEvent                             event_type)
 {
 	switch (event_type) {
 	/* when page load starts, reset everything */
 	case WEBKIT_LOAD_STARTED:
-		c->https = FALSE;
-		if (c->error_page)
-			c->error_page = FALSE;
+		gl.https = FALSE;
+		if (gl.error_page)
+			gl.error_page = FALSE;
 		else
-			g_clear_object(&c->failed_crt);
+			g_clear_object(&gl.failed_crt);
 		break;
 	/* when page load is redirected, continue as usual */
 	case WEBKIT_LOAD_REDIRECTED:
 		break;
 	/* when page load is committed, get https and tls state */
 	case WEBKIT_LOAD_COMMITTED:
-		c->https = webkit_web_view_get_tls_info(
-		    WEBKIT_WEB_VIEW(c->wv), &c->crt, &c->tls_error
+		gl.https = webkit_web_view_get_tls_info(
+		    WEBKIT_WEB_VIEW(gl.wv), &gl.crt, &gl.tls_error
 		);
 		break;
 	/* when page load is finished, run all user scripts */
 	case WEBKIT_LOAD_FINISHED:
-		load_user_styles(WEBKIT_WEB_VIEW(c->wv));
-		run_user_scripts(WEBKIT_WEB_VIEW(c->wv));
+		load_user_styles(WEBKIT_WEB_VIEW(gl.wv));
+		load_user_scripts(WEBKIT_WEB_VIEW(gl.wv));
 		break;
 	}
 
-	update_title(c);
+	update_title();
 }
 
 void
@@ -820,15 +713,13 @@ load_configuration(void)
 	int                     i;
 
 	/* set global defaults */
-	gl.clients              = 0;                /* client counter */
-	gl.ipc                  = IPC_CLIENT;       /* ipc type */
-	gl.ipc_pipe_fd          = 0;                /* ipc pipe file descriptor */
 	gl.search_text          = NULL;             /* in page search phrase */
 	gl.state_lock           = TRUE;             /* prevents state save */
+	gl.initialized          = FALSE;            /* is client initialized? */
 
 	/* load default configuration */
-	for (i = 0; i < LastConfig; i++) {
-		if (cfg[i].e && (e = g_getenv(cfg[i].e)) != NULL) {
+	for (i = 0; i < CFN_LAST; i++) {
+		if (cfg[i].e && (e = g_getenv(cfg[i].e))) {
 			switch (cfg[i].t) {
 			case CFG_INT:       cfg[i].v.i = atoi(e); break;
 			case CFG_FLOAT:     cfg[i].v.f = atof(e); break;
@@ -841,74 +732,7 @@ load_configuration(void)
 }
 
 void
-load_state(void)
-{
-	char                    u[URI_MAX];         /* line/uri holder */
-	FILE                   *fp = NULL;          /* file pointer */
-	int                     c,                  /* character holder */
-	                        x,                  /* state indicators */
-	                        idx = 0;            /* current page index */
-	unsigned                i = 0u;
-
-	/* only load states for ipc host */
-	if (CFG_S(StateFile) == NULL || gl.ipc != IPC_HOST)
-		return;
-
-	if (!(fp = fopen(CFG_S(StateFile), "r"))) {
-		/* fail if file exists as it cannot be accessed, else remove state lock
-		 * to see if saving the state will create it (ie. the parent exists) */
-		if (access(CFG_S(StateFile), F_OK) == 0)
-			perror(__NAME__": Error opening state file");
-		else
-			gl.state_lock = FALSE;
-		return;
-	}
-
-	do {
-		c = fgetc(fp);
-
-		if (i == URI_MAX && !(c == '\n' || c == EOF)) { /* uri is too long */
-			i = 0u;
-			while ((c = fgetc(fp)) != '\n' && c != EOF);
-			if (c == EOF)
-				break;
-		}
-
-		if (c != '\n' && c != EOF) {
-			u[i++] = (char)c;
-			continue;   /* character is added to string, not more to do */
-		}
-
-		if (i == 0)
-			continue;   /* skip empty lines */
-
-		u[i] = '\0';
-		i = 0u;
-
-		if (sscanf(u, "%d:%s", &x, u) != 2)
-			continue;   /* incorrect format, so skip it */
-
-		set_javascript_policy(
-		    client_create(u, NULL, TRUE, TRUE),
-		    (x & STATE_JAVASCRIPT) != 0
-		);
-		idx = x & STATE_CURRENT ? gl.clients - 1 : idx;
-
-	} while (c != EOF);
-
-	fclose(fp);
-
-	/* select current tab if any */
-	if (gl.clients > 0)
-		gtk_notebook_set_current_page(GTK_NOTEBOOK(mw.nb), idx);
-
-	/* enable state save */
-	gl.state_lock = FALSE;
-
-}
-
-void
-load_stdin()
+load_stdin(void)
 {
 	int                     c;                  /* character holder */
 	GString                *s = g_string_new(NULL);
@@ -917,52 +741,77 @@ load_stdin()
 	while ((c = getc(stdin)) != EOF)
 		g_string_append_c(s, (gchar)c);
 
-	/* ignore content of stdin if instance is not independent, as there is no
-	 * uri to send over IPC anyway */
-	if (gl.ipc != IPC_NONE)
-		fprintf(stderr, __NAME__": stdin detected, use with -I instead\n");
-	else
-		/* load the html content, which won't survive a reload */
-		webkit_web_view_load_html(
-		    WEBKIT_WEB_VIEW(client_create(NULL, NULL, TRUE, TRUE)->wv),
-		    s->str,
-		    NULL
-		);
+	/* load the html content, which won't survive a reload */
+	initialize(NULL);
+	webkit_web_view_load_html(WEBKIT_WEB_VIEW(gl.wv), s->str, NULL);
 	g_string_free(s, TRUE);
 }
 
 void
-load_user_styles(WebKitWebView *web_view)
+load_user_scripts(
+	WebKitWebView                              *web_view)
 {
 	gchar                  *c = NULL,           /* file content */
 	                       *p = NULL,           /* path (file) */
 	                       *b = NULL;           /* base directory (scripts) */
 	const gchar            *e = NULL;           /* directory file entry */
-	GDir                   *s = NULL;           /* user style directory */
+	GDir                   *s = NULL;           /* user script directory */
 
-	b = g_build_filename(g_get_user_config_dir(), __NAME__, CFG_S(UcDir), NULL);
-	if ((s = g_dir_open(b, 0, NULL)) == NULL) {
-		g_free(b);
-		return;
+	webkit_user_content_manager_remove_all_scripts(
+	    webkit_web_view_get_user_content_manager(web_view)
+	);
+
+	IUS(web_view, SCRIPT_ALL_FRAMES, WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES);
+	IUS(web_view, SCRIPT_MAIN_FRAME, WEBKIT_USER_CONTENT_INJECT_TOP_FRAME);
+
+	b = g_build_filename(CFG_DIR, __NAME__, CFG_S(CFN_USER_SCRIPT_DIR), NULL);
+	if (!(s = g_dir_open(b, 0, NULL)))
+		return g_free(b);
+
+	while ((e = g_dir_read_name(s))) {
+		if (g_str_has_suffix((p = g_build_filename(b, e, NULL)), ".js")
+		    && g_file_get_contents(p, &c, NULL, NULL)) {
+			IUS(web_view, c, WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES);
+			g_free(c);
+		}
+		g_free(p);
 	}
 
+	g_dir_close(s);
+	g_free(b);
+}
+
+void
+load_user_styles(
+	WebKitWebView                              *web_view)
+{
+	gchar                  *c = NULL,           /* file content */
+	                       *p = NULL,           /* path (file) */
+	                       *b = NULL;           /* base directory (scripts) */
+	const gchar            *e = NULL,           /* directory file entry */
+	                       *u = NULL;           /* uri holder */
+	GDir                   *s = NULL;           /* user style directory */
+	gchar                 **m = NULL;           /* match container */
+
+	b = g_build_filename(CFG_DIR, __NAME__, CFG_S(CFN_USER_CSS_DIR), NULL);
+	if (!(s = g_dir_open(b, 0, NULL)))
+		return g_free(b);
+
+	if (!(u = webkit_web_view_get_uri(WEBKIT_WEB_VIEW(web_view))))
+		return; /* end load if no uri exists */
+
 	webkit_user_content_manager_remove_all_style_sheets(
 	    webkit_web_view_get_user_content_manager(web_view)
 	);
 
-	while ((e = g_dir_read_name(s)) != NULL) {
+	while ((e = g_dir_read_name(s))) {
 		if (g_str_has_suffix((p = g_build_filename(b, e, NULL)), ".css")
 		    && g_file_get_contents(p, &c, NULL, NULL)) {
-			webkit_user_content_manager_add_style_sheet(
-			    webkit_web_view_get_user_content_manager(web_view),
-			    webkit_user_style_sheet_new(
-			        c,
-			        WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES,
-			        WEBKIT_USER_STYLE_LEVEL_USER,
-			        NULL,
-			        NULL
-			    )
-			);
+			/* reject uri specific css that do not match */
+			m = g_regex_split_simple("/\\* URL -- (.*) \\*/", c, 0, 0);
+			if (!m[1] || (m[1] != NULL && !strncmp(m[1], u, strlen(m[1]))))
+				IUSS(web_view, c, WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES);
+			g_free(m);
 			g_free(c);
 		}
 		g_free(p);
@@ -975,152 +824,52 @@ load_user_styles(WebKitWebView *web_view)
 void
 main_window_setup(void)
 {
-	GtkWidget              *v;                  /* vertical box */
-
 	/* define window elements */
-	mw.win                  = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-	mw.nb                   = gtk_notebook_new();
-	mw.dbx                  = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
-	v                       = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
+	gl.win                  = gl.embed > 0
+	                        ? gtk_plug_new(gl.embed)
+	                        : gtk_window_new(GTK_WINDOW_TOPLEVEL);
 
 	/* window settings */
-	gtk_window_set_default_size(GTK_WINDOW(mw.win), 800, 600);
-	gtk_window_set_title(GTK_WINDOW(mw.win), __NAME__);
-
-	/* notebook settings */
-	gtk_notebook_set_scrollable(GTK_NOTEBOOK(mw.nb), TRUE);
-	gtk_notebook_set_tab_pos(GTK_NOTEBOOK(mw.nb), GTK_POS_TOP);
-
-	/* prevent notebook (including tabs) from stealing focus */
-	gtk_widget_set_can_focus(mw.nb, FALSE);
-
-	/* pack everything in a vertical box */
-	gtk_box_pack_start(GTK_BOX(v), mw.nb, TRUE, TRUE, 0);
-	gtk_box_pack_start(GTK_BOX(v), mw.dbx, FALSE, FALSE, 0);
-	gtk_container_add(GTK_CONTAINER(mw.win), v);
+	gtk_window_set_default_size(GTK_WINDOW(gl.win), 800, 600);
+	gtk_window_set_title(GTK_WINDOW(gl.win), __NAME__);
 
 	/* connect signal callbacks */
-	CB(mw.win,              "destroy",          cb_quit, NULL);
-	CB(mw.nb,               "switch-page",      cb_notebook_switch_page, NULL);
-	CBA(mw.nb,              "switch-page",      cb_notebook_modified, NULL);
-	CB(mw.nb,               "page-added",       cb_notebook_modified, NULL);
-	CB(mw.nb,               "page-removed",     cb_notebook_modified, NULL);
-	CB(mw.nb,               "page-reordered",   cb_notebook_modified, NULL);
+	CB(gl.win,              "destroy",          cb_quit);
+
+	if (gl.embed)
+		CB(gl.win,          "unmap-event",      cb_unmap);
+	CB(gl.win,              "map-event",        cb_map);
 }
 
 void
-open_external(struct Client *c, const gchar s)
+open_external(
+	const gchar                                 s)
 {
 	char                    b[URI_MAX];         /* command line buffer */
 	gchar                  *p = NULL;           /* external handler path */
 	p = g_build_filename(
-	    g_get_user_config_dir(), __NAME__, CFG_S(ExternalHandlerFile), NULL
+	    CFG_DIR, __NAME__, CFG_S(CFN_EXTERNAL_HANDLER_FILE), NULL
 	);
 	if (s)
-		sprintf(b, "%s %c:%s &", p, s, gtk_entry_get_text(GTK_ENTRY(c->entry)));
+		sprintf(b, "%s %c:%s &", p, s, gtk_entry_get_text(GTK_ENTRY(gl.entry)));
 	else
-		sprintf(b, "%s %s &", p, gtk_entry_get_text(GTK_ENTRY(c->entry)));
+		sprintf(b, "%s %s &", p, gtk_entry_get_text(GTK_ENTRY(gl.entry)));
 	g_free(p);
 
 	system(b);
 }
 
-void
-prepare_download(WebKitDownload                *d,
-                 gchar                         *suggested_filename)
-{
-	gchar                  *s = g_strdup(suggested_filename),
-	                       *p = NULL,           /* proposed path */
-	                       *f = NULL,           /* final path */
-	                       *u = NULL;           /* uri */
-	GtkWidget              *b;                  /* button */
-	int                     i;
-	static gboolean         c = FALSE;          /* condition, TRUE=finished */
-
-	/* make suggested filename safe, by replacing directory separators */
-	for (i = 0; s[i]; i++)
-		if (s[i] == G_DIR_SEPARATOR)
-			s[i] = '_';
-
-	p = g_build_filename(CFG_S(DownloadDirectory), s, NULL);
-	f = g_strdup(p);
-
-	/* check for a free suffix */
-	for (i = 1; g_file_test(f, G_FILE_TEST_EXISTS) && i < SUFFIX_MAX; i++) {
-		g_free(f);
-		f = rebuild_filename(p, i);
-	}
-
-	if (i < SUFFIX_MAX) {
-		u = g_filename_to_uri(f, NULL, NULL);
-		webkit_download_set_destination(d, u);
-
-		b = gtk_button_new_with_label(s);
-		gtk_button_set_always_show_image(GTK_BUTTON(b), TRUE);
-		gtk_button_set_image(
-		    GTK_BUTTON(b),
-		    gtk_image_new_from_icon_name(ICON_DOWNLOAD, GTK_ICON_SIZE_BUTTON)
-		);
-		gtk_widget_set_margin_end(b, 1);
-		gtk_box_pack_start(GTK_BOX(mw.dbx), b, FALSE, FALSE, 0);
-		gtk_box_reorder_child(GTK_BOX(mw.dbx), b, 0);
-		gtk_widget_show_all(mw.dbx);
-
-		g_object_set_data(G_OBJECT(b), __NAME__"-finished", (gpointer)&c);
-
-		CB(d, "notify::estimated-progress", cb_download_changed_progress, b);
-		CB(d, "finished", cb_download_finished, b);
-		g_object_ref(d);
-		CB(b, "button-press-event", cb_download_press, d);
-	} else { /* could not find a free suffix under the SUFFIX_MAX limit */
-		fprintf(stderr, __NAME__": Limit reached for filename suffix\n");
-		webkit_download_cancel(d);
-	}
-
-	g_free(p);
-	g_free(s);
-	g_free(f);
-	g_free(u);
-}
-
 void
 quit(void)
 {
-	save_state();
 	gtk_main_quit();
 }
 
-gchar *
-rebuild_filename(
-	gchar                                      *p,
-	int                                         n)
-{
-	int i, l = strlen(p);
-	gchar *f;
-
-	for (i = l; i >= 0; i--)
-		if (p[i] == '.' || (p[i] == '/' && (i = l) >= 0))
-			break;
-
-	if (i == -1)
-		die(__NAME__ ": fatal: invalid download path\n");
-
-	if (i < l) {
-		p[i] = '\0';
-		f = g_strdup_printf("%s.%d.%s", p, n, &p[i + 1]);
-		p[i] = '.';
-	} else {
-		f = g_strdup_printf("%s.%d", p, n);
-	}
-
-	return f;
-}
-
 void
-render_tls_error(struct Client                 *c,
-                 gchar                         *uri,
-                 GTlsCertificate               *crt,
-                 GTlsCertificateFlags           cert_flags)
+render_tls_error(
+	gchar                                      *uri,
+	GTlsCertificate                            *crt,
+	GTlsCertificateFlags                        crt_flags)
 {
 	GString                *m = NULL;           /* message (error) */
 	gchar                  *h = NULL,           /* html code */
@@ -1133,24 +882,24 @@ render_tls_error(struct Client                 *c,
 	                       *ad = NULL;          /* not after date */
 
 	m                       = g_string_new(NULL);
-	c->failed_crt           = g_object_ref(crt);
-	c->tls_error            = cert_flags;
-	c->error_page           = TRUE;
+	gl.failed_crt           = g_object_ref(crt);
+	gl.tls_error            = crt_flags;
+	gl.error_page           = TRUE;
 
 	/* translate all flags to messages */
-	if (c->tls_error & G_TLS_CERTIFICATE_UNKNOWN_CA)
+	if (gl.tls_error & G_TLS_CERTIFICATE_UNKNOWN_CA)
 		g_string_append(m, MSG_TLS_CERTIFICATE_UNKNOWN_CA);
-	if (c->tls_error & G_TLS_CERTIFICATE_BAD_IDENTITY)
+	if (gl.tls_error & G_TLS_CERTIFICATE_BAD_IDENTITY)
 		g_string_append(m, MSG_TLS_CERTIFICATE_BAD_IDENTITY);
-	if (c->tls_error & G_TLS_CERTIFICATE_NOT_ACTIVATED)
+	if (gl.tls_error & G_TLS_CERTIFICATE_NOT_ACTIVATED)
 		g_string_append(m, MSG_TLS_CERTIFICATE_NOT_ACTIVATED);
-	if (c->tls_error & G_TLS_CERTIFICATE_EXPIRED)
+	if (gl.tls_error & G_TLS_CERTIFICATE_EXPIRED)
 		g_string_append(m, MSG_TLS_CERTIFICATE_EXPIRED);
-	if (c->tls_error & G_TLS_CERTIFICATE_REVOKED)
+	if (gl.tls_error & G_TLS_CERTIFICATE_REVOKED)
 		g_string_append(m, MSG_TLS_CERTIFICATE_REVOKED);
-	if (c->tls_error & G_TLS_CERTIFICATE_INSECURE)
+	if (gl.tls_error & G_TLS_CERTIFICATE_INSECURE)
 		g_string_append(m, MSG_TLS_CERTIFICATE_INSECURE);
-	if (c->tls_error & G_TLS_CERTIFICATE_GENERIC_ERROR)
+	if (gl.tls_error & G_TLS_CERTIFICATE_GENERIC_ERROR)
 		g_string_append(m, MSG_TLS_CERTIFICATE_GENERIC_ERROR);
 
 	/* construct html code and load it */
@@ -1162,118 +911,44 @@ render_tls_error(struct Client                 *c,
 	b = g_date_time_format_iso8601(bd);
 	a = g_date_time_format_iso8601(ad);
 	h = g_strdup_printf(TLS_MSG_FORMAT, uri, m->str, s, i, b, a, p);
-	webkit_web_view_load_alternate_html(WEBKIT_WEB_VIEW(c->wv), h, uri, NULL);
+	webkit_web_view_load_alternate_html(WEBKIT_WEB_VIEW(gl.wv), h, uri, NULL);
 
 	g_string_free(m, TRUE);
-	g_free(h);
-	g_free(s);
-	g_free(i);
-	g_free(b);
-	g_free(a);
-	g_free(p);
+	g_free_all((gpointer[]){h, s, i, b, a, p, NULL});
 	g_date_time_unref(ad);
 	g_date_time_unref(bd);
 }
 
 void
-run_user_scripts(WebKitWebView *web_view)
+save_history(
+	const gchar                                *t)
 {
-	gchar                  *c = NULL,           /* file content */
-	                       *p = NULL,           /* path (file) */
-	                       *b = NULL;           /* base directory (scripts) */
-	const gchar            *e = NULL;           /* directory file entry */
-	GDir                   *s = NULL;           /* user script directory */
-
-	b = g_build_filename(g_get_user_config_dir(), __NAME__, CFG_S(UsDir), NULL);
-	if ((s = g_dir_open(b, 0, NULL)) == NULL) {
-		g_free(b);
-		return;
-	}
-
-	while ((e = g_dir_read_name(s)) != NULL) {
-		if (g_str_has_suffix((p = g_build_filename(b, e, NULL)), ".js")
-		    && g_file_get_contents(p, &c, NULL, NULL)) {
-			webkit_web_view_evaluate_javascript(
-			    web_view, c, -1, NULL, NULL, NULL, NULL, NULL
-			);
-			g_free(c);
-		}
-		g_free(p);
-	}
-
-	g_dir_close(s);
-	g_free(b);
-}
-
-void
-save_history(const gchar *t)
-{
-	FILE                   *fp;
-
-	if (CFG_S(HistoryFile) == NULL)
-		return;
-
-	if ((fp = fopen(CFG_S(HistoryFile), "a")) != NULL) {
-		fprintf(fp, "%s\n", t);
-		fclose(fp);
-	} else {
-		perror(__NAME__": Error opening history file");
-	}
-}
-
-void
-save_state(void)
-{
-	GList                  *c;                  /* child (notebook page) */
-	int                     x,                  /* state indicators */
-	                        i,
-	                        l;                  /* length (number of tabs) */
 	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)
+	if (!CFG_S(CFN_HISTORY_FILE))
 		return;
 
-	if ((fp = fopen(CFG_S(StateFile), "w")) == NULL) {
-		perror(__NAME__": Error opening state file");
+	if (!strcmp(t, ABOUT_BLANK))
 		return;
-	}
 
-	/* save uri from each tab into state file */
-	for (i = 0, l = gtk_notebook_get_n_pages(GTK_NOTEBOOK(mw.nb)); i < l; i++) {
-		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"))
-			continue;
-		wv = WEBKIT_WEB_VIEW(c->data);
-		s = webkit_web_view_get_settings(wv);
-		x = 0;
-		if (gtk_notebook_get_current_page(GTK_NOTEBOOK(mw.nb)) == i)
-			x |= STATE_CURRENT;
-		if (webkit_settings_get_enable_javascript_markup(s))
-			x |= STATE_JAVASCRIPT;
-		fprintf(fp, "%d:%s\n", x, (u = get_uri((GtkWidget*)wv)));
-		g_free(u);
-	}
+	if (!(fp = fopen(CFG_S(CFN_HISTORY_FILE), "a")))
+		return perror(__NAME__": Error opening history file");
 
+	fprintf(fp, "%s\n", t);
 	fclose(fp);
 }
 
 void
-search(struct Client                           *c,
-       enum inpage_search_type                  type)
+search(
+	enum inpage_search_type                     type)
 {
-	if (gl.search_text == NULL)
+	if (!gl.search_text)
 		return;
 
 	switch (type) {
 	case IPS_INIT:
 		webkit_find_controller_search(
-		    webkit_web_view_get_find_controller(WEBKIT_WEB_VIEW(c->wv)),
+		    webkit_web_view_get_find_controller(WEBKIT_WEB_VIEW(gl.wv)),
 		    gl.search_text,
 		    WEBKIT_FIND_OPTIONS_CASE_INSENSITIVE
 		    | WEBKIT_FIND_OPTIONS_WRAP_AROUND,
@@ -1282,53 +957,55 @@ search(struct Client                           *c,
 		break;
 	case IPS_FORWARD:
 		webkit_find_controller_search_next(
-		    webkit_web_view_get_find_controller(WEBKIT_WEB_VIEW(c->wv))
+		    webkit_web_view_get_find_controller(WEBKIT_WEB_VIEW(gl.wv))
 		);
 		break;
 	case IPS_BACKWARD:
 		webkit_find_controller_search_previous(
-		    webkit_web_view_get_find_controller(WEBKIT_WEB_VIEW(c->wv))
+		    webkit_web_view_get_find_controller(WEBKIT_WEB_VIEW(gl.wv))
 		);
 		break;
 	}
 }
 
 void
-selection_search(struct Client *c)
+selection_search(void)
 {
 	webkit_web_view_evaluate_javascript(
-	    WEBKIT_WEB_VIEW(c->wv),
-	    "window.getSelection().toString();",
+	    WEBKIT_WEB_VIEW(gl.wv),
+	    gl.first_search ? SCRIPT_SEARCH : SCRIPT_SEARCH_EXTRA,
 	    -1,
 	    NULL,
 	    NULL,
 	    NULL,
 	    cb_selection_search_finished,
-	    c
+	    NULL
 	);
 }
 
 void
-selection_search_finished(struct Client        *c,
-                          GAsyncResult         *result)
+selection_search_finished(
+	GAsyncResult                               *result)
 {
 	JSCValue               *v = NULL;
 	gchar                  *s = NULL,
 	                       *u = NULL;
 
-	if ((v = webkit_web_view_evaluate_javascript_finish(
-	    WEBKIT_WEB_VIEW(c->wv), result, NULL
-	    )) == NULL)
+	if (!(v = webkit_web_view_evaluate_javascript_finish(
+	    WEBKIT_WEB_VIEW(gl.wv), result, NULL)))
 		return;
 
 	if (jsc_value_is_string(v)
 	    && strlen(s = jsc_value_to_string(v))
 	    && !jsc_context_get_exception(jsc_value_get_context(v))
-	    && (u = resolve_uri(s)) != NULL)
-		client_create(u, NULL, TRUE, FALSE);
+	    && (u = resolve_uri(s)))
+		initialize(u);
+	else if (gl.first_search) {
+		gl.first_search = FALSE;
+		selection_search();
+	}
 
-	g_free(u);
-	g_free(s);
+	g_free_all((gpointer[]){u, s, NULL});
 }
 
 void
@@ -1337,13 +1014,9 @@ set_default_web_context(void)
 	gchar                  *p = NULL;           /* web extensions path */
 	WebKitWebContext       *c;                  /* web context */
 
-	/* no context needed for clients */
-	if (gl.ipc == IPC_CLIENT)
-		return;
-
 	c = webkit_web_context_get_default();
 
-	p = g_build_filename(g_get_user_config_dir(), __NAME__, CFG_S(WeDir), NULL);
+	p = g_build_filename(CFG_DIR, __NAME__, CFG_S(CFN_WEB_EXTENSION_DIR), NULL);
 #if GTK_CHECK_VERSION(3, 98, 0) /* seems to be fixed in 3.98.0, we'll see */
 	webkit_web_context_set_sandbox_enabled(c, TRUE);
 	webkit_web_context_add_path_to_sandbox(c, p, TRUE);
@@ -1351,7 +1024,7 @@ set_default_web_context(void)
 	webkit_web_context_set_web_extensions_directory(c, p);
 #endif
 
-	CB(c, "download-started", cb_download_start, NULL);
+	CB(c, "download-started", cb_download_start);
 	webkit_web_context_set_favicon_database_directory(c, NULL);
 	webkit_web_context_register_uri_scheme(
 	    c, ABOUT_SCHEME, (WebKitURISchemeRequestCallback)about_scheme_request,
@@ -1362,88 +1035,55 @@ set_default_web_context(void)
 }
 
 void
-set_hover_uri(struct Client                    *c,
-              WebKitHitTestResult              *hit_test_result)
+set_hover_uri(
+	WebKitHitTestResult                        *hit_test_result)
 {
 	const char             *t;                  /* entry text holder */
 	gchar                  *u = NULL;           /* uri text holder */
 
-	g_free(c->hover_uri);
+	g_free(gl.hover_uri);
 
 	/* only display hovered links */
 	if (webkit_hit_test_result_context_is_link(hit_test_result)) {
 		t = webkit_hit_test_result_get_link_uri(hit_test_result);
-		c->hover_uri = g_strdup(t);
+		gl.hover_uri = g_strdup(t);
 	} else {
-		u = get_uri(c->wv);
-		c->hover_uri = NULL;
+		u = get_uri(gl.wv);
+		gl.hover_uri = NULL;
 	}
 
-	if (!gtk_widget_is_focus(c->entry))
-		gtk_entry_set_text(GTK_ENTRY(c->entry), u ? u : t);
+	if (!gtk_widget_is_focus(gl.entry))
+		gtk_entry_set_text(GTK_ENTRY(gl.entry), u ? u : t);
 
 	g_free(u);
 }
 
 void
-set_javascript_policy(struct Client            *c,
-                      enum javascript_policy    policy)
+set_javascript_policy(
+	enum javascript_policy                      policy)
 {
 	webkit_settings_set_enable_javascript_markup(
-	    c->settings,
+	    gl.settings,
 	    policy == JSP_TOGGLE
-	        ? !webkit_settings_get_enable_javascript_markup(c->settings)
+	        ? !webkit_settings_get_enable_javascript_markup(gl.settings)
 	        : policy
 	);
 	gtk_entry_set_icon_from_icon_name(
-	    GTK_ENTRY(c->entry),
+	    GTK_ENTRY(gl.entry),
 	    GTK_ENTRY_ICON_SECONDARY,
-	    webkit_settings_get_enable_javascript_markup(c->settings)
+	    webkit_settings_get_enable_javascript_markup(gl.settings)
 	        ? ICON_JS_ON
 	        : ICON_JS_OFF
 	);
-	webkit_web_view_reload_bypass_cache(WEBKIT_WEB_VIEW(c->wv));
-	save_state();
+	webkit_web_view_reload_bypass_cache(WEBKIT_WEB_VIEW(gl.wv));
 }
 
 void
-set_window_title(gint i)
-{
-	GtkWidget              *c;                  /* notebook child/page */
-
-	if ((c = gtk_notebook_get_nth_page(GTK_NOTEBOOK(mw.nb), i)) == NULL)
-		return;
-
-	gtk_window_set_title(GTK_WINDOW(mw.win),
-	    gtk_label_get_text(
-	        GTK_LABEL((GtkWidget *)g_object_get_data(
-	            G_OBJECT(gtk_notebook_get_tab_label(GTK_NOTEBOOK(mw.nb), c)),
-	            __NAME__"-tab-label"
-	        ))
-	    )
-	);
-}
-
-void
-show_web_view(struct Client *c)
-{
-	gint                    i;
-
-	gtk_widget_show_all(mw.win);
-
-	if (c->focus_new_tab) {
-		if ((i = gtk_notebook_page_num(GTK_NOTEBOOK(mw.nb), c->vbx)) != -1)
-			gtk_notebook_set_current_page(GTK_NOTEBOOK(mw.nb), i);
-		gtk_widget_grab_focus(c->wv);
-	}
-}
-
-void
-toggle_inspector(struct Client *c)
+toggle_inspector(void)
 {
 	WebKitWebInspector     *i;
 
-	i = webkit_web_view_get_inspector(WEBKIT_WEB_VIEW(c->wv));
+	i = webkit_web_view_get_inspector(WEBKIT_WEB_VIEW(gl.wv));
 
 	/* assumes that the inspector has not been detached by the user */
 	if (webkit_web_inspector_is_attached(i))
@@ -1453,19 +1093,20 @@ toggle_inspector(struct Client *c)
 }
 
 void
-toggle_tls_error_policy(struct Client *c)
+toggle_tls_error_policy(void)
 {
 	webkit_website_data_manager_set_tls_errors_policy(
-	    webkit_web_view_get_website_data_manager(WEBKIT_WEB_VIEW(c->wv)),
+	    webkit_web_view_get_website_data_manager(WEBKIT_WEB_VIEW(gl.wv)),
 	    !webkit_website_data_manager_get_tls_errors_policy(
-	        webkit_web_view_get_website_data_manager(WEBKIT_WEB_VIEW(c->wv))
+	        webkit_web_view_get_website_data_manager(WEBKIT_WEB_VIEW(gl.wv))
 	    )
 	);
-	webkit_web_view_reload_bypass_cache(WEBKIT_WEB_VIEW(c->wv));
+	webkit_web_view_reload_bypass_cache(WEBKIT_WEB_VIEW(gl.wv));
 }
 
 gchar *
-resolve_uri(const gchar *t)
+resolve_uri(
+	const gchar                                *t)
 {
 	gchar                  *u = NULL,           /* uri to return */
 	                       *l = NULL,           /* temporary string */
@@ -1479,18 +1120,20 @@ resolve_uri(const gchar *t)
 		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"))
+	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 */
-	else 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(CFN_URI_SCHEMES), 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))
-		xdg_open(s, t);
+	else if (s && CFG_L(CFN_XDG_SCHEMES)
+	         && g_strv_contains(CFG_L(CFN_XDG_SCHEMES), s))
+		xdg_open(s, t, FALSE);
 	/* if path is local, use the file scheme, else try to see if the string is
 	 * expandable and is a local path */
-	else if ((l = realpath(t, NULL)) != NULL || ((r = wordexp(t, &x, 0)) == 0
-	          && (l = resolve_uri_words(x.we_wordc, x.we_wordv)) != NULL))
+	else if ((l = realpath(t, NULL)) || ((r = wordexp(t, &x, 0)) == 0
+	          && (l = resolve_uri_words(x.we_wordc, x.we_wordv))))
 		u = g_strdup_printf("file://%s", l);
 	/* else, check if the text can be interpreted as a valid https uri; it's
 	 * not enough to check if uri is valid - check for period and no spaces */
@@ -1500,19 +1143,20 @@ resolve_uri(const gchar *t)
 	/* fallback to web search, using a specified search engine */
 	else
 		u = g_strdup_printf(
-		    CFG_S(SearchEngineUriFormat),
+		    CFG_S(CFN_SEARCH_ENGINE_URI_FORMAT),
 		    (e = g_uri_escape_string(t, NULL, FALSE))
 		);
 
 	if (r == 0 || r == WRDE_NOSPACE) /* free on success (r == 0) and NOSPACE */
 		wordfree(&x);
-	g_free(e);
-	g_free(l);
+	g_free_all((gpointer[]){e, l, NULL});
 	return u; /* return a pointer that the caller is responsible for freeing */
 }
 
 char *
-resolve_uri_words(int c, char **w)
+resolve_uri_words(
+	int                                         c,
+	char                                      **w)
 {
 	gchar                  *d = NULL,           /* uri decoded string */
 	                       *p = NULL,           /* path to return */
@@ -1534,200 +1178,151 @@ resolve_uri_words(int c, char **w)
 
 	p = realpath((d = g_uri_unescape_string(s, NULL)), NULL);
 
-	g_free(d);
-	g_free(s);
+	g_free_all((gpointer[]){d, s, NULL});
 	return p; /* return a pointer that the caller is responsible for freeing */
 }
 
 void
-update_download_button(WebKitDownload          *d,
-                       GtkButton               *btn,
-                       gboolean                 done)
-{
-	gdouble                 p,                  /* percent completed */
-	                        l;                  /* response length (bytes) */
-	gchar                  *t = NULL,           /* text holder*/
-	                       *f = NULL,           /* file name */
-	                       *b = NULL;           /* base name */
-
-	if ((f = g_filename_from_uri(webkit_download_get_destination(d), NULL, NULL)
-	    ) == NULL)
-		return; /* should never happen, but just in case... */
-
-	p = webkit_download_get_estimated_progress(d);
-	p = (p > 1 ? 1 : p < 0 ? 0 : p) * 100;
-	l = webkit_uri_response_get_content_length(webkit_download_get_response(d));
-	b = g_path_get_basename(f);
-
-	gtk_button_set_label(btn, !done /* set label based on progress */
-		? (t = g_strdup_printf(" %s (%.0f%% of %.1f MB)", b, p, l / 1e6))
-		: (t = g_strdup_printf(" %s", b))
-	);
-
-	g_free(t);
-	g_free(f);
-	g_free(b);
-}
-
-void
-update_favicon(struct Client *c)
+update_favicon(void)
 {
-	cairo_surface_t        *f;  /* favicon */
-	GdkPixbuf              *b,  /* pix buffer */
-	                       *s;  /* pix buffer (scaled) */
+	cairo_surface_t        *f;                  /* favicon */
+	GdkPixbuf              *b,                  /* pix buffer */
+	                       *s;                  /* pix buffer (scaled) */
+	int                     d;                  /* scaled dimension */
 
 	/* set fallback favicon */
-	gtk_image_set_from_icon_name(
-	    GTK_IMAGE(c->tab_icon), ICON_GLOBE, GTK_ICON_SIZE_SMALL_TOOLBAR
-	);
+	gtk_window_set_icon_name(GTK_WINDOW(gl.win), ICON_GLOBE);
 
-	if ((f = webkit_web_view_get_favicon(WEBKIT_WEB_VIEW(c->wv))) == NULL)
+	if (!(f = webkit_web_view_get_favicon(WEBKIT_WEB_VIEW(gl.wv))))
 		return;
 
-	if ((b = gdk_pixbuf_get_from_surface(f, 0, 0,
+	if (!(b = gdk_pixbuf_get_from_surface(f, 0, 0,
 	    cairo_image_surface_get_width(f),
-	    cairo_image_surface_get_height(f))) == NULL)
+	    cairo_image_surface_get_height(f))))
 		return;
 
-	s = gdk_pixbuf_scale_simple(b,
-	    16 * gtk_widget_get_scale_factor(c->tab_icon),
-	    16 * gtk_widget_get_scale_factor(c->tab_icon),
-	    GDK_INTERP_BILINEAR
-	);
-	gtk_image_set_from_pixbuf(GTK_IMAGE(c->tab_icon), s);
+	/* setting the window icon enables other programs to retrieve the icon */
+	d = 16 * gtk_widget_get_scale_factor(gl.win);
+	s = gdk_pixbuf_scale_simple(b, d, d, GDK_INTERP_BILINEAR);
+	gtk_window_set_icon(GTK_WINDOW(gl.win), s);
 
 	g_object_unref(s);
 	g_object_unref(b);
 }
 
 void
-update_load_progress(struct Client *c)
+update_load_progress(void)
 {
 	gdouble                 p;
 
-	p = webkit_web_view_get_estimated_load_progress(WEBKIT_WEB_VIEW(c->wv));
-	gtk_entry_set_progress_fraction(GTK_ENTRY(c->entry), (p == 1 ? 0 : p));
+	p = webkit_web_view_get_estimated_load_progress(WEBKIT_WEB_VIEW(gl.wv));
+	gtk_entry_set_progress_fraction(GTK_ENTRY(gl.entry), (p == 1 ? 0 : p));
 }
 
 void
-update_title(struct Client *c)
+update_title(void)
 {
 	const gchar            *t;
-	gchar                  *m = NULL,
-	                       *u;
+	gchar                  *u;
 
-	u = get_uri(c->wv);
-	t = webkit_web_view_get_title(WEBKIT_WEB_VIEW(c->wv));
+	u = get_uri(gl.wv);
+	t = webkit_web_view_get_title(WEBKIT_WEB_VIEW(gl.wv));
 
 	/* title priority: title, url, __NAME__ */
-	t = t == NULL || t[0] == 0 ? (u == NULL || u[0] == 0 ? __NAME__ : u) : t;
-
-	/* set label markup and certificate icon based on tls status */
-	if (c->failed_crt
-	    || (c->https && c->tls_error != G_TLS_CERTIFICATE_NO_FLAGS)) {
-		m = g_markup_printf_escaped(CFG_S(BadTlsTabFormat), t);
-		gtk_entry_set_icon_from_icon_name(
-		    GTK_ENTRY(c->entry), GTK_ENTRY_ICON_PRIMARY, ICON_BAD_TLS
-		);
-	} else {
-		m = g_markup_printf_escaped(CFG_S(NormalTabFormat), t);
-		if (c->https)
-			gtk_entry_set_icon_from_icon_name(
-			    GTK_ENTRY(c->entry), GTK_ENTRY_ICON_PRIMARY, ICON_TLS
-			);
-		else
-			gtk_entry_set_icon_from_icon_name(
-			    GTK_ENTRY(c->entry), GTK_ENTRY_ICON_PRIMARY, NULL
-			);
-	}
+	t = !t || t[0] == 0 ? (!u || u[0] == 0 ? __NAME__ : u) : t;
 
-	gtk_label_set_markup(GTK_LABEL(c->tab_label), m);
-	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);
+	gtk_entry_set_icon_from_icon_name(
+	    GTK_ENTRY(gl.entry),
+	    GTK_ENTRY_ICON_PRIMARY,
+	      gl.failed_crt ? ICON_BAD_TLS
+	    : gl.https && gl.tls_error != G_TLS_CERTIFICATE_NO_FLAGS ? ICON_BAD_TLS
+	    : gl.https ? ICON_TLS
+	    : NULL
+	);
+
+	gtk_window_set_title(GTK_WINDOW(gl.win), t);
 }
 
 void
-uri_changed(struct Client *c)
+uri_changed(void)
 {
 	gchar                  *t = NULL;
 
 	/* make sure to not overwrite the "WEB PROCESS CRASHED" message */
-	if ((t = get_uri(c->wv)) != NULL && strlen(t) > 0) {
-		gtk_entry_set_text(GTK_ENTRY(c->entry), t);
+	if ((t = get_uri(gl.wv)) && strlen(t) > 0) {
+		gtk_entry_set_text(GTK_ENTRY(gl.entry), t);
 		save_history(t);
-		save_state();
 	}
 
 	g_free(t);
 }
 
 void
-web_view_crashed(struct Client *c)
+web_view_crashed(void)
 {
 	gchar                  *t = NULL,
 	                       *u = NULL;
 
 	gtk_entry_set_text(
-	    GTK_ENTRY(c->entry),
-	    (t = g_strdup_printf("WEB PROCESS CRASHED: %s", (u = get_uri(c->wv))))
+	    GTK_ENTRY(gl.entry),
+	    (t = g_strdup_printf("WEB PROCESS CRASHED: %s", (u = get_uri(gl.wv))))
 	);
-	g_free(t);
-	g_free(u);
+	g_free_all((gpointer[]){t, u, NULL});
 }
 
 void
-xdg_open(const gchar                           *s,
-         const gchar                           *t)
+xdg_open(
+	const gchar                                *s,
+	const gchar                                *t,
+	gboolean                                    keep)
 {
 	char                    b[URI_MAX];         /* command line buffer */
 
-	/* make sure to send the scheme the way it was matched */
-	sprintf(b, "%s %s%s &", XDG_OPEN, s, t+strlen(s));
-	system(b);
+	if (fork() == 0) {
+		/* if not keep, make sure to send the scheme the way it was matched */
+		sprintf(b, "%s%s", s, keep ? t : t+strlen(s));
+		execvp(XDG_OPEN, (char *[]){ XDG_OPEN, b, NULL });
+		fprintf(stderr, "child process (execvp) failed: %s %s\n", XDG_OPEN, b);
+		exit(EXIT_FAILURE);
+	}
 }
 
 int
-main(int argc, char **argv)
+main(
+	int                                         argc,
+	char                                      **argv)
 {
 	int                     opt,
 	                        i;
 
+	sigchld(0);
+
+	gl.arg0 = argv[0];
 	gtk_init(&argc, &argv);
 
 	/* load default configuration before reading command-line arguments */
 	load_configuration();
 
-	while ((opt = getopt(argc, argv, "I")) != -1)
+	while ((opt = getopt(argc, argv, "E:")) != -1)
 		switch (opt) {
-		case 'I':
-			gl.ipc = IPC_NONE;
-			break;
-		default:
-			die("Usage: " __NAME__ " [-I [-]] [URI ...] [FILE ...]\n");
+		case 'E': to_win(&gl.embed, optarg); break;
+		default:  die("Usage: " __NAME__ " [-E WINID] [URI ...] [FILE ...]\n");
 		}
 
-	ipc_setup();
 	set_default_web_context();
 	main_window_setup();
-	load_state();
 
 	/* load a default home uri if no clients and no arguments exist  */
-	if (optind >= argc && gl.clients == 0)
-		client_create(CFG_S(HomeUri), NULL, TRUE, TRUE);
+	if (optind >= argc)
+		initialize(CFG_S(CFN_HOME_URI));
 	/* load stdin if first argument is '-' */
 	if (optind < argc && !strcmp(argv[optind], "-"))
 		load_stdin();
 	/* load remaining command line arguments as uris into new clients */
 	else if (optind < argc)
-		for (i = optind; i < argc; i++)
-			client_create(argv[i], NULL, TRUE, TRUE);
+		for (i = optind; i < argc; initialize(argv[i++]));
 
-	/* don't load gtk for ipc clients, they have done their bit, and make sure
-	 * there are at least one client to view */
-	if (gl.ipc != IPC_CLIENT && gl.clients > 0)
-		gtk_main();
+	gtk_main();
 
 	exit(EXIT_SUCCESS);
 }
diff --git a/browser.h b/browser.h
@@ -24,8 +24,10 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <signal.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <sys/wait.h>
 
 #include <JavaScriptCore/JavaScript.h>
 #include <gdk/gdkkeysyms.h>
@@ -44,19 +46,16 @@
 #define ICON_BAD_TLS        "channel-insecure-symbolic"
 #define ICON_JS_ON          "emblem-ok-symbolic"
 #define ICON_JS_OFF         "action-unavailable-symbolic"
-#define CB(C, E, F, D)                                                         \
+#define CB(C, E, F)                                                            \
         {                                                                      \
-            g_signal_connect(G_OBJECT(C), (E), G_CALLBACK(F), (D));            \
-        }
-#define CBA(C, E, F, D)                                                        \
-        {                                                                      \
-            g_signal_connect_after(G_OBJECT(C), (E), G_CALLBACK(F), (D));      \
+            g_signal_connect(G_OBJECT(C), (E), G_CALLBACK(F), NULL);           \
         }
 #define CFG_B(X)            cfg[(X)].v.b
 #define CFG_F(X)            cfg[(X)].v.f
 #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 CFG_DIR             g_get_user_config_dir()
 
 #define TLS_MSG_FORMAT      "<h2>Could not validate TLS for: %s</h2><pre>%s"   \
                             "</pre><pre>s:%s\ni:%s\nv:NotBefore:%s; NotAfter:" \
@@ -88,6 +87,55 @@
 #define STATE_JAVASCRIPT    1 << 1
 #define BROWSER_ERROR       (browser_error_quark())
 #define ABOUT_SCHEME        __NAME__"-about"
+#define ABOUT_BLANK         "about:blank"
+#define SCRIPT_MAIN_FRAME   "window.onmessage = (e) => {"                      \
+                            "  window.adji_selection = e.data;"                \
+                            "};"
+#define SCRIPT_ALL_FRAMES   "window.onmessage = (e) => {"                      \
+                            "  if (e.data == 'selection') {"                   \
+                            "    let r = window.getSelection().toString();"    \
+                            "    e.source.postMessage(r, e.origin);"           \
+                            "  }"                                              \
+                            "};"
+#define SCRIPT_SEARCH_EXTRA "cw = document.activeElement.contentWindow;"       \
+                            "if (cw) {"                                        \
+                            "  cw.postMessage('selection','*');"               \
+                            "} else {"                                         \
+                            "  let r = window.getSelection().toString();"      \
+                            "  window.adji_selection = r;"                     \
+                            "} window.adji_selection;"
+#define SCRIPT_SEARCH       "window.adji_selection = '';"SCRIPT_SEARCH_EXTRA
+#define IUS(W, S, C)        /* Inject User Script */                           \
+        {                                                                      \
+            webkit_user_content_manager_add_script(                            \
+                webkit_web_view_get_user_content_manager((W)),                 \
+                    webkit_user_script_new(                                    \
+                        (S),                                                   \
+                        (C),                                                   \
+                        WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_START,           \
+                        NULL,                                                  \
+                        NULL));                                                \
+        }
+#define IUSS(W, S, C)       /* Inject User Style Sheet */                      \
+        {                                                                      \
+            webkit_user_content_manager_add_style_sheet(                       \
+                webkit_web_view_get_user_content_manager((W)),                 \
+                    webkit_user_style_sheet_new(                               \
+                        (S),                                                   \
+                        (C),                                                   \
+                        WEBKIT_USER_STYLE_LEVEL_USER,                          \
+                        NULL,                                                  \
+                        NULL));                                                \
+        }
+
+/*** Name translations ***/
+/*      name                original name                                   */
+#define request_path(R)     webkit_uri_scheme_request_get_path((R))
+#define get_socket_win(W)   gtk_plug_get_socket_window(GTK_PLUG((W)))
+#define get_widget_win(W)   gtk_widget_get_window((W))
+#define get_win_state(W)    gdk_window_get_state(get_widget_win(W))
+#define is_fullscreen()     get_win_state(gl.win) & GDK_WINDOW_STATE_FULLSCREEN
+#define to_gdk_win(D, W)    gdk_x11_window_foreign_new_for_display((D), (W))
 
 enum javascript_policy {
 	JSP_DISABLE             = 0,
@@ -101,97 +149,69 @@ enum inpage_search_type {
 	IPS_BACKWARD
 };
 
-enum ipc_type {
-	IPC_NONE,
-	IPC_HOST,
-	IPC_CLIENT
-};
-
 enum config_type {
 	CFG_INT,
 	CFG_FLOAT,
 	CFG_BOOL,
 	CFG_STRING,
-	CFG_LIST,
+	CFG_LIST
 };
 
+/** Enumerators for configurables  */
 enum config_name {
-	AcceptedLanguages,
-	BadTlsTabFormat,
-	ConsoleToStdout,
-	CookieFile,
-	DnsPrefetching,
-	DefaultCharset,
-	DefaultFont,
-	DefaultFontSize,
-	DeveloperExtras,
-	DisableAutoLoadImages,
-	DisableJavaScript,
-	DownloadDirectory,
-	EncryptedMedia,
-	ExternalHandlerFile,
-	ExternalHandlerKeys,
-	FifoName,
-	HistoryFile,
-	HomeUri,
-	HyperlinkAuditing,
-	JsAccessClipboard,
-	JsOpenWindows,
-	MonospaceFont,
-	NormalTabFormat,
-	ProxyIgnore,
-	ProxyUri,
-	SansSerifFont,
-	SearchEngineUriFormat,
-	SerifFont,
-	SmoothScrolling,
-	StateFile,
-	UcDir,      /* user (cascading) style sheets directory */
-	UriSchemes,
-	UsDir,      /* user scripts directory */
-	UserAgent,
-	WeDir,      /* web extension directory */
-	WebRtc,
-	XdgSchemes,
-	ZoomLevel,
+	CFN_ACCEPTED_LANGUAGES,
+	CFN_BLOB_FILE,
+	CFN_CONSOLE_TO_STDOUT,
+	CFN_COOKIE_FILE,
+	CFN_DNS_PREFETCHING,
+	CFN_DEFAULT_CHARSET,
+	CFN_DEFAULT_FONT,
+	CFN_DEFAULT_FONT_SIZE,
+	CFN_DEVELOPER_EXTRAS,
+	CFN_DISABLE_AUTOLOAD_IMAGES,
+	CFN_DISABLE_JAVASCRIPT,
+	CFN_ENCRYPTED_MEDIA,
+	CFN_EXTERNAL_HANDLER_FILE,
+	CFN_EXTERNAL_HANDLER_KEYS,
+	CFN_HISTORY_FILE,
+	CFN_HOME_URI,
+	CFN_HYPERLINK_AUDITING,
+	CFN_JS_ACCESS_CLIPBOARD,
+	CFN_JS_OPEN_WINDOWS,
+	CFN_MONOSPACE_FONT,
+	CFN_NORMAL_TAB_FORMAT,
+	CFN_PROXY_IGNORE,
+	CFN_PROXY_URI,
+	CFN_SANS_SERIF_FONT,
+	CFN_SEARCH_ENGINE_URI_FORMAT,
+	CFN_SERIF_FONT,
+	CFN_SMOOTH_SCROLLING,
+	CFN_STATE_FILE,
+	CFN_TAB_HOST,
+	CFN_USER_CSS_DIR,
+	CFN_URI_SCHEMES,
+	CFN_USER_SCRIPT_DIR,
+	CFN_USER_AGENT,
+	CFN_WEB_EXTENSION_DIR,
+	CFN_WEB_RTC,
+	CFN_XDG_SCHEMES,
+	CFN_ZOOM_LEVEL,
 	/* must be last to represent number of config items */
-	LastConfig
+	CFN_LAST
 };
 
 enum browser_error {
-	BROWSER_ERROR_INVALID_ABOUT_PATH
+	BER_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 */
-	gboolean                b;              /* union boolean */
-	gchar                  *s;              /* union string */
-	gchar                 **l;              /* union string list/array */
+	gint                    i;
+	gdouble                 f;
+	gboolean                b;
+	gchar                  *s;
+	gchar                 **l;
 } Arg;
 
-struct Client
-{
-	GTlsCertificate        *crt;            /* last certificate */
-	GtkWidget              *entry;          /* uri/command/entry */
-	gboolean                error_page;     /* flag if error page */
-	GTlsCertificate        *failed_crt;     /* last failed certificate */
-	gboolean                focus_new_tab;  /* flag for focusing tab */
-	gchar                  *hover_uri;      /* uri last hovered */
-	gboolean                https;          /* flag if https site */
-	WebKitSettings         *settings;       /* reference to webkit settings */
-	GtkWidget              *tab_icon;       /* holder for favicon in tab */
-	GtkWidget              *tab_label;      /* holder for title in tab */
-	GTlsCertificateFlags    tls_error;      /* last tls error */
-	GtkWidget              *vbx;            /* vertical box (view + entry) */
-	GtkWidget              *wv;             /* web view */
-};
-
 typedef struct {
 	const char             *e;
 	const char             *s;
@@ -201,192 +221,236 @@ typedef struct {
 } Config;
 
 struct Global {
-	int                     clients;
+	GTlsCertificate        *crt;
+	gboolean                error_page;
+	gboolean                first_search;
+	gchar                  *hover_uri;
+	GTlsCertificate        *failed_crt;
 	gboolean                state_lock;
-	enum ipc_type           ipc;
-	int                     ipc_pipe_fd;
+	gboolean                initialized;
+	gboolean                https;
+	Window                  embed;
+	GtkWidget              *win;
+	GtkWidget              *vbx;
+	GtkWidget              *wv;
+	GtkWidget              *entry;
+	gchar                  *arg0;
+	WebKitSettings         *settings;
 	gchar                  *search_text;
+	GTlsCertificateFlags    tls_error;
 } gl;
 
-struct MainWindow
+
+/*** "Special" functions ***/
+static GQuark
+browser_error_quark()
 {
-	GtkWidget              *dbx;
-	GtkWidget              *nb;
-	GtkWidget              *win;
-} 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 *);
-static gboolean command(struct Client *, const gchar*);
-static void create_context_menu(struct Client *, WebKitContextMenu *,
-                                WebKitHitTestResult *);
-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);
-static gboolean key_common(struct Client *, GdkEventKey *);
-static gboolean key_entry(struct Client *, GdkEventKey *);
-static gboolean key_tab(struct Client *, GdkEvent *);
-static gboolean key_web_view(struct Client *, GdkEvent *);
-static void load_changed(struct Client *, WebKitLoadEvent);
+	return g_quark_from_string(__NAME__"-quark");
+}
+
+
+/*** Function prototypes ***/
+static void about_scheme_request(
+	WebKitURISchemeRequest                     *request,
+	gpointer                                    data
+);
+
+static void attach_to_host(
+	GtkWidget                                  *self
+);
+
+static void attach_to_window(
+	GtkWidget                                  *self
+);
+
+static void initialize(
+	const gchar                                *uri
+);
+
+static void terminate(void);
+
+static gboolean command(
+	const gchar                                *t
+);
+
+static void create_context_menu(
+	WebKitContextMenu                          *context_menu,
+	WebKitHitTestResult                        *hit_test_result
+);
+
+static void create_context_menu_item(
+	WebKitContextMenu                          *context_menu,
+	const gchar                                *name,
+	const gchar                                *label,
+	void                                       *action
+);
+
+static void download_blob(
+	WebKitDownload                             *d
+);
+
+static void download_response(
+	WebKitURIResponse                          *r
+);
+
+static gchar *get_uri(
+	GtkWidget                                  *wv
+);
+
+static gboolean handle_fullscreen(
+	gboolean                                    fullscreen
+);
+
+static gboolean key_common(
+	GdkEventKey                                *event
+);
+
+static gboolean key_entry(
+	GdkEventKey                                *event
+);
+
+static gboolean key_web_view(
+	GdkEvent                                   *event
+);
+
+static void load_changed(
+	WebKitLoadEvent                             event_type
+);
+
 static void load_configuration(void);
-static void load_state(void);
+
 static void load_stdin(void);
-static void load_user_styles(WebKitWebView *);
+
+static void load_user_scripts(
+	WebKitWebView                              *web_view
+);
+
+static void load_user_styles(
+	WebKitWebView                              *web_view
+);
+
 static void main_window_setup(void);
-static void open_external(struct Client *, const gchar);
-static void prepare_download(WebKitDownload *, gchar *);
+
+static void open_external(
+	const gchar                                 s
+);
+
 static void quit(void);
-static gchar *rebuild_filename(gchar *, int);
-static void render_tls_error(struct Client *, gchar *, GTlsCertificate *,
-                             GTlsCertificateFlags);
-static void run_user_scripts(WebKitWebView *);
-static void save_history(const gchar *);
-static void save_state(void);
-static void search(struct Client *, enum inpage_search_type);
-static void selection_search(struct Client *);
-static void selection_search_finished(struct Client *, GAsyncResult *);
+
+static void render_tls_error(
+	gchar                                      *uri,
+	GTlsCertificate                            *crt,
+	GTlsCertificateFlags                        crt_flags
+);
+
+static void save_history(
+	const gchar                                *t
+);
+
+static void search(
+	enum inpage_search_type                     type
+);
+
+static void selection_search(void);
+
+static void selection_search_finished(
+	GAsyncResult                               *result
+);
+
 static void set_default_web_context(void);
-static void set_hover_uri(struct Client *, WebKitHitTestResult *);
-static void set_javascript_policy(struct Client *, enum javascript_policy);
-static void set_window_title(gint);
-static void show_web_view(struct Client *);
-static void toggle_inspector(struct Client *);
-static void toggle_tls_error_policy(struct Client *);
-static gchar *resolve_uri(const gchar *);
-static char *resolve_uri_words(int, char **);
-static void update_download_button(WebKitDownload *, GtkButton *, gboolean);
-static void update_favicon(struct Client *);
-static void update_load_progress(struct Client *);
-static void update_title(struct Client *);
-static void uri_changed(struct Client *);
-static void web_view_crashed(struct Client *);
-static void xdg_open(const gchar *, const gchar *);
-
-/* callback functions */
-static gboolean cb_context_menu(WebKitWebView *, WebKitContextMenu *,
-                                GdkEvent *, WebKitHitTestResult *, gpointer);
-static void cb_download_changed_progress(GObject *, GParamSpec *, gpointer);
-static void cb_download_finished(GObject *, gpointer);
-static gboolean cb_download_prepare(WebKitDownload *, gchar *, gpointer);
-static void cb_download_press(GtkWidget *, GdkEventButton *, gpointer);
-static void cb_download_start(WebKitWebView *, WebKitDownload *, gpointer);
-static void cb_favicon_changed(GObject *, GParamSpec *, gpointer);
-static gboolean cb_entry_hid(GtkWidget *, GdkEvent *, gpointer);
-static void cb_entry_icon_hid(GtkEntry *, GtkEntryIconPosition, GdkEvent *,
-                              gpointer);
-static void cb_notebook_modified(GtkNotebook *, GtkWidget *, guint, gpointer);
-static void cb_notebook_switch_page(GtkNotebook *, GtkWidget *, guint,
-                                    gpointer);
-static gboolean cb_open_external(GSimpleAction *, GVariant *, gpointer);
-static gboolean cb_selection_search(GSimpleAction *, GVariant *, gpointer);
-static void cb_selection_search_finished(GObject *, GAsyncResult *, gpointer);
-static void cb_quit(GObject *, gpointer);
-static gboolean cb_tab_hid(GtkWidget *, GdkEvent *, gpointer);
-static void cb_title_changed(GObject *, GParamSpec *, gpointer);
-static void cb_uri_changed(GObject *, GParamSpec *, gpointer);
-static void cb_wv_close(GtkWidget *, gpointer);
-static gboolean cb_wv_crashed(WebKitWebView *, gpointer);
-static WebKitWebView *cb_wv_create(WebKitWebView *, WebKitNavigationAction *,
-                                   gpointer);
-static gboolean cb_wv_decide_policy(WebKitWebView *, WebKitPolicyDecision *,
-                                    WebKitPolicyDecisionType, gpointer);
-static gboolean cb_wv_hid(GtkWidget *, GdkEvent *, gpointer);
-static void cb_wv_hover(WebKitWebView *, WebKitHitTestResult *, guint,
-                        gpointer);
-static void cb_wv_load_changed(GObject *, GParamSpec *, gpointer);
-static void cb_wv_load_progress_changed(GObject *, GParamSpec *, gpointer);
-static void cb_wv_show(WebKitWebView *, gpointer);
-static gboolean cb_wv_tls_load_failed(GObject *, gchar *, GTlsCertificate *,
-                                      GTlsCertificateFlags, gpointer);
 
+static void set_hover_uri(
+	WebKitHitTestResult                        *hit_test_result
+);
 
-gboolean
-cb_context_menu(WebKitWebView                  *web_view,
-                WebKitContextMenu              *context_menu,
-                GdkEvent                       *event,
-                WebKitHitTestResult            *hit_test_result,
-                gpointer                        data)
-{
-	(void)web_view;
-	(void)event;
+static void set_javascript_policy(
+	enum javascript_policy                      policy
+);
 
-	create_context_menu((struct Client *)data, context_menu, hit_test_result);
-	return FALSE;
-}
+static void toggle_inspector(void);
+
+static void toggle_tls_error_policy(void);
 
+static gchar *resolve_uri(
+	const gchar                                *t
+);
+
+static char *resolve_uri_words(
+	int                                         c,
+	char                                      **w
+);
+
+static void update_favicon(void);
+
+static void update_load_progress(void);
+
+static void update_title(void);
+
+static void uri_changed(void);
+
+static void web_view_crashed(void);
+
+static void xdg_open(
+	const gchar                                *s,
+	const gchar                                *t,
+	gboolean                                    keep
+);
+
+
+/*** Utility functions ***/
 void
-cb_download_changed_progress(GObject           *obj,
-                             GParamSpec        *pspec,
-                             gpointer           data)
+die(
+	const char                                 *msg)
 {
-	(void)pspec;
-
-	update_download_button(WEBKIT_DOWNLOAD(obj), GTK_BUTTON(data), FALSE);
+	fprintf(stderr, msg);
+	exit(EXIT_FAILURE);
 }
 
 void
-cb_download_finished(GObject                   *obj,
-                     gpointer                   data)
+g_free_all(
+	gpointer                                   *p)
 {
-	if (!GTK_IS_BUTTON(data))
-		return;
-
-	(*((gboolean*)g_object_get_data(data, __NAME__"-finished"))) = TRUE;
-
-	update_download_button(WEBKIT_DOWNLOAD(obj), GTK_BUTTON(data), TRUE);
-	gtk_button_set_image(
-	    GTK_BUTTON(data),
-	    gtk_image_new_from_icon_name(ICON_FINISHED, GTK_ICON_SIZE_BUTTON)
-	);
+	for (; *p; g_free(*(p++)));
 }
 
-gboolean
-cb_download_prepare(WebKitDownload             *download,
-                    gchar                      *suggested_filename,
-                    gpointer                    data)
+void
+to_win(
+	Window                                     *i,
+	const char                                 *s)
 {
-	(void)data;
+	char *e;
 
-	prepare_download(download, suggested_filename);
-	return FALSE;
+	*i = strtol(s, &e, 0);
+	if (e == s || *e != 0)
+		*i = -1;
 }
 
 void
-cb_download_press(GtkWidget                   *btn,
-                  GdkEventButton              *event,
-                  gpointer                     data)
+sigchld(
+	int                                        s)
 {
-	switch (event->button) {
-	case 3: /* right click: */
-	case 2: /* middle click: cancel and/or remove */
-		if (!(*((gboolean*)g_object_get_data(G_OBJECT(btn), __NAME__"-finished"))))
-			webkit_download_cancel(WEBKIT_DOWNLOAD(data));
-		g_object_unref(WEBKIT_DOWNLOAD(data));
-		gtk_widget_destroy(btn);
-		break;
-	case 1: /* left click: open downloaded file */
-		if ((*((gboolean*)g_object_get_data(G_OBJECT(btn), __NAME__"-finished"))))
-			xdg_open("", webkit_download_get_destination(WEBKIT_DOWNLOAD(data)));
-		break;
-	}
+	(void) s;
+
+	if (signal(SIGCHLD, sigchld) == SIG_ERR)
+		die("Failed to initialize SIGCHLD handler");
+	while (waitpid(-1, NULL, WNOHANG) > 0);
 }
 
-void
-cb_download_start(WebKitWebView                *web_view,
-                  WebKitDownload               *download,
-                  gpointer                      data)
+
+/*** Callback function wrappers ***/
+gboolean
+cb_context_menu(WebKitWebView                  *web_view,
+                WebKitContextMenu              *context_menu,
+                GdkEvent                       *event,
+                WebKitHitTestResult            *hit_test_result,
+                gpointer                        data)
 {
+	(void)data;
+	(void)event;
 	(void)web_view;
 
-	CB(download, "decide-destination", cb_download_prepare, data);
+	create_context_menu(context_menu, hit_test_result);
+	return FALSE;
 }
 
 void
@@ -396,8 +460,9 @@ cb_favicon_changed(GObject                     *obj,
 {
 	(void)obj;
 	(void)pspec;
+	(void)data;
 
-	update_favicon((struct Client *)data);
+	update_favicon();
 }
 
 gboolean
@@ -406,10 +471,11 @@ cb_entry_hid(GtkWidget                         *widget,
              gpointer                           data)
 {
 	(void)widget;
+	(void)data;
 
 	/* only handle key presses */
 	if (event->type == GDK_KEY_PRESS)
-		return key_entry((struct Client *)data, (GdkEventKey*)event);
+		return key_entry((GdkEventKey*)event);
 
 	return FALSE;
 
@@ -423,38 +489,39 @@ cb_entry_icon_hid(GtkEntry                     *entry,
 {
 	(void)entry;
 	(void)event;
+	(void)data;
 
 	if (icon_pos == GTK_ENTRY_ICON_PRIMARY)
-		toggle_tls_error_policy((struct Client *)data);
+		toggle_tls_error_policy();
 	else if (icon_pos == GTK_ENTRY_ICON_SECONDARY)
-		set_javascript_policy((struct Client *)data, JSP_TOGGLE);
+		set_javascript_policy(JSP_TOGGLE);
 }
 
 void
-cb_notebook_modified(GtkNotebook               *notebook,
-                     GtkWidget                 *child,
-                     guint                      idx,
-                     gpointer                   data)
+cb_download_prepare(WebKitDownload             *download,
+                   GParamSpec                  *param,
+                   gpointer                     data)
 {
-	(void)notebook;
-	(void)child;
-	(void)idx;
+	(void)param;
 	(void)data;
 
-	save_state();
+	/* check if blob  */
+	download_blob(download);
+
+	/* forward to external download handler */
+	download_response(webkit_download_get_response(download));
+	webkit_download_cancel(download);
 }
 
 void
-cb_notebook_switch_page(GtkNotebook            *notebook,
-                        GtkWidget              *child,
-                        guint                   idx,
-                        gpointer                data)
+cb_download_start(WebKitWebContext             *context,
+                  WebKitDownload               *download,
+                  gpointer                      data)
 {
-	(void)notebook;
-	(void)child;
+	(void)context;
 	(void)data;
 
-	set_window_title(idx);
+	CB(download, "decide-destination", cb_download_prepare);
 }
 
 gboolean
@@ -464,8 +531,9 @@ cb_open_external(GSimpleAction                 *action,
 {
 	(void)action;
 	(void)parameter;
+	(void)data;
 
-	open_external((struct Client *)data, 0);
+	open_external(0);
 	return FALSE;
 }
 
@@ -486,8 +554,10 @@ cb_selection_search(GSimpleAction              *action,
 {
 	(void)action;
 	(void)parameter;
+	(void)data;
 
-	selection_search((struct Client *)data);
+	gl.first_search = TRUE;
+	selection_search();
 	return FALSE;
 }
 
@@ -496,21 +566,10 @@ cb_selection_search_finished(GObject           *object,
                              GAsyncResult      *result,
                              gpointer           data)
 {
-	struct Client *c = (struct Client *)data;
-
+	(void)data;
 	(void)object;
 
-	selection_search_finished(c, result);
-}
-
-gboolean
-cb_tab_hid(GtkWidget                           *widget,
-           GdkEvent                            *event,
-           gpointer                             data)
-{
-	(void)widget;
-
-	return key_tab((struct Client *)data, event);
+	selection_search_finished(result);
 }
 
 void
@@ -520,8 +579,32 @@ cb_title_changed(GObject                       *obj,
 {
 	(void)obj;
 	(void)pspec;
+	(void)data;
+
+	update_title();
+}
+
+void
+cb_map(GObject                                 *obj,
+      GdkEvent                                  event,
+      gpointer                                  data)
+{
+	(void)event;
+	(void)data;
 
-	update_title((struct Client *)data);
+	attach_to_host((GtkWidget *)obj);
+}
+
+
+void
+cb_unmap(GObject                               *obj,
+        GdkEvent                                event,
+        gpointer                                data)
+{
+	(void)event;
+	(void)data;
+
+	attach_to_window((GtkWidget *)obj);
 }
 
 void
@@ -531,8 +614,9 @@ cb_uri_changed(GObject                         *obj,
 {
 	(void)obj;
 	(void)pspec;
+	(void)data;
 
-	uri_changed((struct Client *)data);
+	uri_changed();
 }
 
 void
@@ -540,8 +624,9 @@ cb_wv_close(GtkWidget                          *widget,
             gpointer                            data)
 {
 	(void)widget;
+	(void)data;
 
-	client_destroy((struct Client *)data);
+	terminate();
 }
 
 gboolean
@@ -549,8 +634,9 @@ cb_wv_crashed(WebKitWebView                    *web_view,
               gpointer                          data)
 {
 	(void)web_view;
+	(void)data;
 
-	web_view_crashed((struct Client *)data);
+	web_view_crashed();
 	return TRUE;
 }
 
@@ -559,15 +645,14 @@ cb_wv_create(WebKitWebView                     *web_view,
              WebKitNavigationAction            *navigation_action,
              gpointer                           data)
 {
-	struct Client *c;
-
-	(void)navigation_action;
+	(void)web_view;
 	(void)data;
 
-	if ((c = client_create(NULL, web_view, FALSE, FALSE)) == NULL)
-		return NULL;
+	initialize(
+	    webkit_uri_request_get_uri(
+	        webkit_navigation_action_get_request(navigation_action)));
 
-	return WEBKIT_WEB_VIEW(c->wv);
+	return NULL;
 }
 
 gboolean
@@ -580,35 +665,60 @@ cb_wv_decide_policy(WebKitWebView              *web_view,
 	(void)data;
 
 	/* only handle policy decisions */
-	if (type != WEBKIT_POLICY_DECISION_TYPE_RESPONSE)
+	if (type != WEBKIT_POLICY_DECISION_TYPE_RESPONSE) {
 		return FALSE;
 	/* check if: 'HTTP/1.1 204 No Content'
 	 * this should be ignored, but isn't
 	 * see: https://bugs.webkit.org/show_bug.cgi?id=60206
 	 * */
-	else if (webkit_uri_response_get_status_code(
+	} else if (webkit_uri_response_get_status_code(
 	            webkit_response_policy_decision_get_response(
 	                WEBKIT_RESPONSE_POLICY_DECISION(decision))) == 204
-	        )
+	        ) {
 		webkit_policy_decision_ignore(decision);
 	/* continue as normal */
-	else if (!webkit_response_policy_decision_is_mime_type_supported(
+	} else if (webkit_response_policy_decision_is_mime_type_supported(
 	            WEBKIT_RESPONSE_POLICY_DECISION(decision))
-	        )
-		webkit_policy_decision_download(decision);
-	else
+	        ) {
 		webkit_policy_decision_use(decision);
+	} else {
+		webkit_policy_decision_ignore(decision);
+		download_response(
+		    webkit_response_policy_decision_get_response(
+		        WEBKIT_RESPONSE_POLICY_DECISION(decision)));
+	}
 	return TRUE;
 }
 
+gboolean
+cb_wv_fullscreen_enter(WebKitWebView           *web_view,
+                       gpointer                 data)
+{
+	(void)web_view;
+	(void)data;
+
+	return !handle_fullscreen(TRUE);
+}
+
+gboolean
+cb_wv_fullscreen_leave(WebKitWebView           *web_view,
+                       gpointer                 data)
+{
+	(void)web_view;
+	(void)data;
+
+	return !handle_fullscreen(FALSE);
+}
+
 gboolean
 cb_wv_hid(GtkWidget                            *widget,
           GdkEvent                             *event,
           gpointer                              data)
 {
 	(void)widget;
+	(void)data;
 
-	return key_web_view((struct Client *)data, event);
+	return key_web_view(event);
 }
 
 void
@@ -619,8 +729,9 @@ cb_wv_hover(WebKitWebView                      *web_view,
 {
 	(void)web_view;
 	(void)modifiers;
+	(void)data;
 
-	set_hover_uri((struct Client *)data, hit_test_result);
+	set_hover_uri(hit_test_result);
 }
 
 void
@@ -629,8 +740,9 @@ cb_wv_load_changed(GObject                     *obj,
                    gpointer                     data)
 {
 	(void)obj;
+	(void)data;
 
-	load_changed((struct Client *)data, (WebKitLoadEvent)pspec);
+	load_changed((WebKitLoadEvent)pspec);
 }
 
 void
@@ -640,17 +752,9 @@ cb_wv_load_progress_changed(GObject            *obj,
 {
 	(void)obj;
 	(void)pspec;
+	(void)data;
 
-	update_load_progress((struct Client *)data);
-}
-
-void
-cb_wv_show(WebKitWebView                       *web_view,
-           gpointer                             data)
-{
-	(void)web_view;
-
-	show_web_view((struct Client *)data);
+	update_load_progress();
 }
 
 gboolean
@@ -661,8 +765,9 @@ cb_wv_tls_load_failed(GObject                  *obj,
                       gpointer                  data)
 {
 	(void)obj;
+	(void)data;
 
-	render_tls_error((struct Client *)data, uri, crt, err);
+	render_tls_error(uri, crt, err);
 	return TRUE;
 }
 
diff --git a/config.h b/config.h
@@ -1,4 +1,7 @@
 /**
+ * @file
+ * @brief Configuration file
+ *
  * Copyright (C) 2023 Chris Noxz
  * Author(s): Chris Noxz <chris@noxz.tech>
  *
@@ -19,46 +22,48 @@
 #ifndef CONFIG_H
 #define CONFIG_H
 
-static Config cfg[LastConfig] = {
-	/* config name              enviornment variable name                       webkit web view setting name                config type invert       default value */
-	[AcceptedLanguages]     = { __NAME_UPPERCASE__"_ACCEPTED_LANGUAGES",        NULL,                                       CFG_LIST,   FALSE, {.l = NULL }},
-	[BadTlsTabFormat]       = { NULL,                                           NULL,                                       CFG_STRING, FALSE, {.s = "<span foreground=\"yellow\" style=\"italic\">%s</span>" }},
-	[ConsoleToStdout]       = { __NAME_UPPERCASE__"_ENABLE_CONSOLE_TO_STDOUT",  "enable-write-console-messages-to-stdout",  CFG_BOOL,   FALSE, {.b = FALSE }},
-	[CookieFile]            = { __NAME_UPPERCASE__"_COOKIE_FILE",               NULL,                                       CFG_STRING, FALSE, {.s = NULL }},
-	[DnsPrefetching]        = { NULL,                                           "enable-dns-prefetching",                   CFG_BOOL,   FALSE, {.b = FALSE }},
-	[DefaultCharset]        = { __NAME_UPPERCASE__"_CHARSET",                   "default-charset",                          CFG_STRING, FALSE, {.s = "UTF-8" }},
-	[DefaultFontSize]       = { __NAME_UPPERCASE__"_DEFAULT_FONT_SIZE",         "default-font-size",                        CFG_INT,    FALSE, {.i = 12 }},
-	[DefaultFont]           = { __NAME_UPPERCASE__"_DEFAULT_FONT",              "default-font-family",                      CFG_STRING, FALSE, {.s = "monospace" }},
-	[DeveloperExtras]       = { NULL,                                           "enable-developer-extras",                  CFG_BOOL,   FALSE, {.b = TRUE }},
-	[DisableAutoLoadImages] = { __NAME_UPPERCASE__"_DISABLE_AUTO_LOAD_IMAGES",  "auto-load-images",                         CFG_BOOL,   TRUE,  {.b = FALSE }},
-	[DisableJavaScript]     = { __NAME_UPPERCASE__"_DISABLE_JAVASCRIPT",        "enable-javascript-markup",                 CFG_BOOL,   TRUE,  {.b = FALSE }},
-	[DownloadDirectory]     = { __NAME_UPPERCASE__"_DOWNLOAD_DIR",              NULL,                                       CFG_STRING, FALSE, {.s = "/var/tmp" }},
-	[EncryptedMedia]        = { NULL,                                           "enable-encrypted-media",                   CFG_BOOL,   FALSE, {.b = FALSE }},
-	[ExternalHandlerFile]   = { NULL,                                           NULL,                                       CFG_STRING, FALSE, {.s = "exthandler" }},
-	[ExternalHandlerKeys]   = { __NAME_UPPERCASE__"_EXTERNAL_HANDLER_KEYS",     NULL,                                       CFG_LIST,   FALSE, {.l = NULL }},
-	[FifoName]              = { __NAME_UPPERCASE__"_FIFO_NAME",                 NULL,                                       CFG_STRING, FALSE, {.s = "default" }},
-	[HistoryFile]           = { __NAME_UPPERCASE__"_HISTORY_FILE",              NULL,                                       CFG_STRING, FALSE, {.s = NULL }},
-	[HomeUri]               = { __NAME_UPPERCASE__"_HOME_URI",                  NULL,                                       CFG_STRING, FALSE, {.s = "about:blank" }},
-	[HyperlinkAuditing]     = { NULL,                                           "enable-hyperlink-auditing",                CFG_BOOL,   FALSE, {.b = FALSE }},
-	[JsAccessClipboard]     = { NULL,                                           "javascript-can-access-clipboard",          CFG_BOOL,   FALSE, {.b = FALSE }},
-	[JsOpenWindows]         = { NULL,                                           "javascript-can-access-clipboard",          CFG_BOOL,   FALSE, {.b = FALSE }},
-	[MonospaceFont]         = { __NAME_UPPERCASE__"_MONOSPACE_FONT",            "monospace-font-family",                    CFG_STRING, FALSE, {.s = "monospace" }},
-	[NormalTabFormat]       = { NULL,                                           NULL,                                       CFG_STRING, FALSE, {.s = "<span>%s</span>" }},
-	[ProxyIgnore]           = { __NAME_UPPERCASE__"_PROXY_IGNORE",              NULL,                                       CFG_LIST,   FALSE, {.l = NULL }},
-	[ProxyUri]              = { __NAME_UPPERCASE__"_PROXY_URI",                 NULL,                                       CFG_STRING, FALSE, {.s = NULL }},
-	[SansSerifFont]         = { __NAME_UPPERCASE__"_SANS_SERIF_FONT",           "sans-serif-font-family",                   CFG_STRING, FALSE, {.s = "sans-serif" }},
-	[SearchEngineUriFormat] = { __NAME_UPPERCASE__"_SE_URI_FORMAT",             NULL,                                       CFG_STRING, FALSE, {.s = "https://ddg.gg?q=%s" }},
-	[SerifFont]             = { __NAME_UPPERCASE__"_SERIF_FONT",                "serif-font-family",                        CFG_STRING, FALSE, {.s = "serif" }},
-	[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", __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" }},
-	[WebRtc]                = { NULL,                                           "enable-webrtc",                            CFG_BOOL,   FALSE, {.b = FALSE }},
-	[XdgSchemes]            = { __NAME_UPPERCASE__"_XDG_SCHEMES",               NULL,                                       CFG_LIST,   FALSE, {.l = NULL }},
-	[ZoomLevel]             = { __NAME_UPPERCASE__"_ZOOM_LEVEL",                NULL,                                       CFG_FLOAT,  FALSE, {.f = 1.0 }},
+/**
+ * @brief Contains dynamically loaded configuration
+ */
+static Config cfg[CFN_LAST] = {
+	/* config name                      enviornment variable name                       webkit web view setting name                config type invert       default value */
+	[CFN_ACCEPTED_LANGUAGES]        = { __NAME_UPPERCASE__"_ACCEPTED_LANGUAGES",        NULL,                                       CFG_LIST,   FALSE, {.l = NULL }},
+	[CFN_BLOB_FILE]                 = { __NAME_UPPERCASE__"_BLOB_FILE",                 NULL,                                       CFG_STRING, FALSE, {.s = "/tmp/." __NAME__ "_blob" }},
+	[CFN_CONSOLE_TO_STDOUT]         = { __NAME_UPPERCASE__"_ENABLE_CONSOLE_TO_STDOUT",  "enable-write-console-messages-to-stdout",  CFG_BOOL,   FALSE, {.b = FALSE }},
+	[CFN_COOKIE_FILE]               = { __NAME_UPPERCASE__"_COOKIE_FILE",               NULL,                                       CFG_STRING, FALSE, {.s = NULL }},
+	[CFN_DNS_PREFETCHING]           = { NULL,                                           "enable-dns-prefetching",                   CFG_BOOL,   FALSE, {.b = FALSE }},
+	[CFN_DEFAULT_CHARSET]           = { __NAME_UPPERCASE__"_CHARSET",                   "default-charset",                          CFG_STRING, FALSE, {.s = "UTF-8" }},
+	[CFN_DEFAULT_FONT]              = { __NAME_UPPERCASE__"_DEFAULT_FONT",              "default-font-family",                      CFG_STRING, FALSE, {.s = "monospace" }},
+	[CFN_DEFAULT_FONT_SIZE]         = { __NAME_UPPERCASE__"_DEFAULT_FONT_SIZE",         "default-font-size",                        CFG_INT,    FALSE, {.i = 12 }},
+	[CFN_DEVELOPER_EXTRAS]          = { NULL,                                           "enable-developer-extras",                  CFG_BOOL,   FALSE, {.b = TRUE }},
+	[CFN_DISABLE_AUTOLOAD_IMAGES]   = { __NAME_UPPERCASE__"_DISABLE_AUTO_LOAD_IMAGES",  "auto-load-images",                         CFG_BOOL,   TRUE,  {.b = FALSE }},
+	[CFN_DISABLE_JAVASCRIPT]        = { __NAME_UPPERCASE__"_DISABLE_JAVASCRIPT",        "enable-javascript-markup",                 CFG_BOOL,   TRUE,  {.b = FALSE }},
+	[CFN_ENCRYPTED_MEDIA]           = { NULL,                                           "enable-encrypted-media",                   CFG_BOOL,   FALSE, {.b = FALSE }},
+	[CFN_EXTERNAL_HANDLER_FILE]     = { NULL,                                           NULL,                                       CFG_STRING, FALSE, {.s = "exthandler" }},
+	[CFN_EXTERNAL_HANDLER_KEYS]     = { __NAME_UPPERCASE__"_EXTERNAL_HANDLER_KEYS",     NULL,                                       CFG_LIST,   FALSE, {.l = NULL }},
+	[CFN_HISTORY_FILE]              = { __NAME_UPPERCASE__"_HISTORY_FILE",              NULL,                                       CFG_STRING, FALSE, {.s = NULL }},
+	[CFN_HOME_URI]                  = { __NAME_UPPERCASE__"_HOME_URI",                  NULL,                                       CFG_STRING, FALSE, {.s = "about:blank" }},
+	[CFN_HYPERLINK_AUDITING]        = { NULL,                                           "enable-hyperlink-auditing",                CFG_BOOL,   FALSE, {.b = FALSE }},
+	[CFN_JS_ACCESS_CLIPBOARD]       = { NULL,                                           "javascript-can-access-clipboard",          CFG_BOOL,   FALSE, {.b = FALSE }},
+	[CFN_JS_OPEN_WINDOWS]           = { NULL,                                           "javascript-can-open-windows-automatically",CFG_BOOL,   FALSE, {.b = FALSE }},
+	[CFN_MONOSPACE_FONT]            = { __NAME_UPPERCASE__"_MONOSPACE_FONT",            "monospace-font-family",                    CFG_STRING, FALSE, {.s = "monospace" }},
+	[CFN_NORMAL_TAB_FORMAT]         = { NULL,                                           NULL,                                       CFG_STRING, FALSE, {.s = "<span>%s</span>" }},
+	[CFN_PROXY_IGNORE]              = { __NAME_UPPERCASE__"_PROXY_IGNORE",              NULL,                                       CFG_LIST,   FALSE, {.l = NULL }},
+	[CFN_PROXY_URI]                 = { __NAME_UPPERCASE__"_PROXY_URI",                 NULL,                                       CFG_STRING, FALSE, {.s = NULL }},
+	[CFN_SANS_SERIF_FONT]           = { __NAME_UPPERCASE__"_SANS_SERIF_FONT",           "sans-serif-font-family",                   CFG_STRING, FALSE, {.s = "sans-serif" }},
+	[CFN_SEARCH_ENGINE_URI_FORMAT]  = { __NAME_UPPERCASE__"_SE_URI_FORMAT",             NULL,                                       CFG_STRING, FALSE, {.s = "https://ddg.gg?q=%s" }},
+	[CFN_SERIF_FONT]                = { __NAME_UPPERCASE__"_SERIF_FONT",                "serif-font-family",                        CFG_STRING, FALSE, {.s = "serif" }},
+	[CFN_SMOOTH_SCROLLING]          = { __NAME_UPPERCASE__"_ENABLE_SMOOTH_SCROLLING",   "enable-smooth-scrolling",                  CFG_BOOL,   FALSE, {.b = FALSE }},
+	[CFN_STATE_FILE]                = { __NAME_UPPERCASE__"_STATE_FILE",                NULL,                                       CFG_STRING, FALSE, {.s = NULL }},
+	[CFN_TAB_HOST]                  = { __NAME_UPPERCASE__"_TAB_HOST",                  NULL,                                       CFG_STRING, FALSE, {.s = "_TABBED_HOST" }},
+	[CFN_USER_CSS_DIR]              = { NULL,                                           NULL,                                       CFG_STRING, FALSE, {.s = "styles" }},
+	[CFN_URI_SCHEMES]               = { NULL,                                           NULL,                                       CFG_LIST,   FALSE, {.l = (gchar*[]){ "http", "https", "file", "about", __NAME__"-about", "data", "blob", "webkit", NULL } }},
+	[CFN_USER_SCRIPT_DIR]           = { NULL,                                           NULL,                                       CFG_STRING, FALSE, {.s = "scripts" }},
+	[CFN_USER_AGENT]                = { __NAME_UPPERCASE__"_USER_AGENT",                "user-agent",                               CFG_STRING, FALSE, {.s = NULL }},
+	[CFN_WEB_EXTENSION_DIR]         = { NULL,                                           NULL,                                       CFG_STRING, FALSE, {.s = "web_extensions" }},
+	[CFN_WEB_RTC]                   = { NULL,                                           "enable-webrtc",                            CFG_BOOL,   FALSE, {.b = FALSE }},
+	[CFN_XDG_SCHEMES]               = { __NAME_UPPERCASE__"_XDG_SCHEMES",               NULL,                                       CFG_LIST,   FALSE, {.l = NULL }},
+	[CFN_ZOOM_LEVEL]                = { __NAME_UPPERCASE__"_ZOOM_LEVEL",                NULL,                                       CFG_FLOAT,  FALSE, {.f = 1.0 }},
 };
 
 #endif /* !CONFIG_H */
diff --git a/config.mk b/config.mk
@@ -6,7 +6,7 @@ __NAME__            = adji
 __NAME_UPPERCASE__  = ADJI
 
 # paths
-PREFIX              = /usr/local
+PREFIX              = ${HOME}/.local
 MANPREFIX           = ${PREFIX}/share/man
 
 # flags
diff --git a/we_redirect.c b/we_redirect.c
@@ -108,6 +108,9 @@ cb_web_page_send_request(WebKitWebPage *web_page, WebKitURIRequest *request,
 
 	i = webkit_uri_request_get_uri(request);
 
+	if (verbose)
+		fprintf(stderr, "  [req]: %s\n", i);
+
 	/* go through every rule and apply accordingly */
 	while (p && d) {
 		/* find matching rule for incoming uri */