tStyle change in functions ordering - surf - customized build of surf, the suckless webkit browser
 (HTM) git clone git://src.adamsgaard.dk/surf
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 432f3c6b53cf47db3141a3e9b8b5aec2152f9aae
 (DIR) parent 7e9a85a5cb31a2ddefd06498eff33df724bdc6cf
 (HTM) Author: Quentin Rameau <quinq@fifth.space>
       Date:   Sat, 21 Nov 2015 19:14:08 +0100
       
       Style change in functions ordering
       
       Try to group and order functions in a logical manner.
       Same thing for config keybindings.
       
       Diffstat:
         M config.def.h                        |      40 ++++++++++++++++---------------
         M surf.c                              |    1738 +++++++++++++++----------------
       
       2 files changed, 889 insertions(+), 889 deletions(-)
       ---
 (DIR) diff --git a/config.def.h b/config.def.h
       t@@ -84,18 +84,15 @@ static SiteStyle styles[] = {
         */
        static Key keys[] = {
                /* modifier                 keyval          function    arg */
       -        { MODKEY|GDK_SHIFT_MASK, GDK_KEY_r,      reload,     { .b = TRUE } },
       -        { MODKEY,                GDK_KEY_r,      reload,     { .b = FALSE } },
       -        { MODKEY|GDK_SHIFT_MASK, GDK_KEY_p,      print,      { 0 } },
       +        { MODKEY,                GDK_KEY_g,      spawn,      SETPROP("_SURF_URI", "_SURF_GO") },
       +        { MODKEY,                GDK_KEY_f,      spawn,      SETPROP("_SURF_FIND", "_SURF_FIND") },
       +        { MODKEY,                GDK_KEY_slash,  spawn,      SETPROP("_SURF_FIND", "_SURF_FIND") },
        
       -        { MODKEY,                GDK_KEY_p,      clipboard,  { .b = TRUE } },
       -        { MODKEY,                GDK_KEY_y,      clipboard,  { .b = FALSE } },
       +        { 0,                     GDK_KEY_Escape, stop,       { 0 } },
       +        { MODKEY,                GDK_KEY_c,      stop,       { 0 } },
        
       -        { MODKEY|GDK_SHIFT_MASK, GDK_KEY_j,      zoom,       { .i = -1 } },
       -        { MODKEY|GDK_SHIFT_MASK, GDK_KEY_k,      zoom,       { .i = +1 } },
       -        { MODKEY|GDK_SHIFT_MASK, GDK_KEY_q,      zoom,       { .i = 0  } },
       -        { MODKEY,                GDK_KEY_minus,  zoom,       { .i = -1 } },
       -        { MODKEY,                GDK_KEY_plus,   zoom,       { .i = +1 } },
       +        { MODKEY|GDK_SHIFT_MASK, GDK_KEY_r,      reload,     { .b = TRUE } },
       +        { MODKEY,                GDK_KEY_r,      reload,     { .b = FALSE } },
        
                { MODKEY,                GDK_KEY_l,      navigate,   { .i = +1 } },
                { MODKEY,                GDK_KEY_h,      navigate,   { .i = -1 } },
       t@@ -108,17 +105,25 @@ static Key keys[] = {
                { MODKEY,                GDK_KEY_i,      scroll_h,   { .i = +10 } },
                { MODKEY,                GDK_KEY_u,      scroll_h,   { .i = -10 } },
        
       -        { 0,                     GDK_KEY_F11,    togglefullscreen, { 0 } },
       -        { 0,                     GDK_KEY_Escape, stop,       { 0 } },
       -        { MODKEY|GDK_SHIFT_MASK, GDK_KEY_o,      toggleinspector, { 0 } },
       +        { MODKEY|GDK_SHIFT_MASK, GDK_KEY_j,      zoom,       { .i = -1 } },
       +        { MODKEY|GDK_SHIFT_MASK, GDK_KEY_k,      zoom,       { .i = +1 } },
       +        { MODKEY|GDK_SHIFT_MASK, GDK_KEY_q,      zoom,       { .i = 0  } },
       +        { MODKEY,                GDK_KEY_minus,  zoom,       { .i = -1 } },
       +        { MODKEY,                GDK_KEY_plus,   zoom,       { .i = +1 } },
        
       -        { MODKEY,                GDK_KEY_g,      spawn,      SETPROP("_SURF_URI", "_SURF_GO") },
       -        { MODKEY,                GDK_KEY_f,      spawn,      SETPROP("_SURF_FIND", "_SURF_FIND") },
       -        { MODKEY,                GDK_KEY_slash,  spawn,      SETPROP("_SURF_FIND", "_SURF_FIND") },
       +        { MODKEY,                GDK_KEY_p,      clipboard,  { .b = TRUE } },
       +        { MODKEY,                GDK_KEY_y,      clipboard,  { .b = FALSE } },
        
                { MODKEY,                GDK_KEY_n,      find,       { .i = +1 } },
                { MODKEY|GDK_SHIFT_MASK, GDK_KEY_n,      find,       { .i = -1 } },
        
       +        { MODKEY|GDK_SHIFT_MASK, GDK_KEY_p,      print,      { 0 } },
       +
       +        { MODKEY|GDK_SHIFT_MASK, GDK_KEY_a,      togglecookiepolicy, { 0 } },
       +        { 0,                     GDK_KEY_F11,    togglefullscreen, { 0 } },
       +        { MODKEY|GDK_SHIFT_MASK, GDK_KEY_o,      toggleinspector, { 0 } },
       +        { MODKEY|GDK_SHIFT_MASK, GDK_KEY_m,      togglestyle, { 0 } },
       +
                { MODKEY|GDK_SHIFT_MASK, GDK_KEY_c,      toggle,     { .i = CaretBrowsing } },
                { MODKEY|GDK_SHIFT_MASK, GDK_KEY_f,      toggle,     { .i = FrameFlattening } },
                { MODKEY|GDK_SHIFT_MASK, GDK_KEY_g,      toggle,     { .i = Geolocation } },
       t@@ -126,9 +131,6 @@ static Key keys[] = {
                { MODKEY|GDK_SHIFT_MASK, GDK_KEY_i,      toggle,     { .i = LoadImages } },
                { MODKEY|GDK_SHIFT_MASK, GDK_KEY_v,      toggle,     { .i = Plugins } },
                { MODKEY|GDK_SHIFT_MASK, GDK_KEY_b,      toggle,     { .i = ScrollBars } },
       -
       -        { MODKEY|GDK_SHIFT_MASK, GDK_KEY_a,      togglecookiepolicy, { 0 } },
       -        { MODKEY|GDK_SHIFT_MASK, GDK_KEY_m,      togglestyle, { 0 } },
        };
        
        /* button definitions */
 (DIR) diff --git a/surf.c b/surf.c
       t@@ -29,14 +29,22 @@
        
        #include "arg.h"
        
       -char *argv0;
       -
        #define LENGTH(x)               (sizeof(x) / sizeof(x[0]))
        #define CLEANMASK(mask)         (mask & (MODKEY|GDK_SHIFT_MASK))
        
        enum { AtomFind, AtomGo, AtomUri, AtomLast };
        
        enum {
       +        CaretBrowsing,
       +        FrameFlattening,
       +        Geolocation,
       +        JavaScript,
       +        LoadImages,
       +        Plugins,
       +        ScrollBars,
       +};
       +
       +enum {
                OnDoc   = WEBKIT_HIT_TEST_RESULT_CONTEXT_DOCUMENT,
                OnLink  = WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK,
                OnImg   = WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE,
       t@@ -47,42 +55,31 @@ enum {
                OnAny   = OnDoc | OnLink | OnImg | OnMedia | OnEdit | OnBar | OnSel,
        };
        
       -enum {
       -        CaretBrowsing,
       -        FrameFlattening,
       -        Geolocation,
       -        JavaScript,
       -        LoadImages,
       -        Plugins,
       -        ScrollBars,
       -};
       -
       -typedef union Arg Arg;
       -union Arg {
       +typedef union {
                gboolean b;
                gint i;
                const void *v;
       -};
       +} Arg;
        
        typedef struct Client {
                GtkWidget *win;
       -        Window xid;
                WebKitWebView *view;
                WebKitWebInspector *inspector;
                WebKitFindController *finder;
                WebKitHitTestResult *mousepos;
                GTlsCertificateFlags tlsflags;
       +        Window xid;
       +        gint progress;
       +        gboolean fullscreen;
                const char *title, *targeturi;
                const char *needle;
       -        gint progress;
                struct Client *next;
       -        gboolean fullscreen;
        } Client;
        
        typedef struct {
                guint mod;
                guint keyval;
       -        void (*func)(Client *c, const Arg *arg);
       +        void (*func)(Client *c, const Arg *a);
                const Arg arg;
        } Key;
        
       t@@ -101,114 +98,168 @@ typedef struct {
                regex_t re;
        } SiteStyle;
        
       -static Display *dpy;
       -static Atom atoms[AtomLast];
       -static Client *clients = NULL;
       -static Window embed = 0;
       -static gboolean showxid = FALSE;
       -static char winid[64];
       -static char togglestats[10];
       -static char pagestats[2];
       -static int cookiepolicy;
       -static char *stylefile = NULL;
       -static const char *useragent;
       -
       -static void addaccelgroup(Client *c);
       +/* Surf */
       +static void usage(void);
       +static void die(const char *errstr, ...);
       +static void setup(void);
       +static void sigchld(int unused);
        static char *buildfile(const char *path);
        static char *buildpath(const char *path);
       -static gboolean buttonreleased(GtkWidget *w, GdkEventKey *e, Client *c);
       -static void cleanup(void);
       -static void clipboard(Client *c, const Arg *a);
       -
       +static Client *newclient(Client *c);
       +static void addaccelgroup(Client *c);
       +static void loaduri(Client *c, const Arg *a);
       +static char *geturi(Client *c);
       +static void setatom(Client *c, int a, const char *v);
       +static const char *getatom(Client *c, int a);
       +static void updatetitle(Client *c);
       +static void gettogglestats(Client *c);
       +static void getpagestats(Client *c);
        static WebKitCookieAcceptPolicy cookiepolicy_get(void);
        static char cookiepolicy_set(const WebKitCookieAcceptPolicy p);
       +static const gchar *getstyle(const char *uri);
       +static void setstyle(Client *c, const char *stylefile);
       +static void runscript(Client *c);
       +static void evalscript(Client *c, const char *jsstr, ...);
       +static void updatewinid(Client *c);
       +static void handleplumb(Client *c, const gchar *uri);
       +static void newwindow(Client *c, const Arg *arg, gboolean noembed);
       +static void spawn(Client *c, const Arg *a);
       +static void destroyclient(Client *c);
       +static void cleanup(void);
        
       +/* GTK/WebKit */
       +static WebKitWebView *newview(Client *c, WebKitWebView *rv);
        static GtkWidget *createview(WebKitWebView *v, WebKitNavigationAction *a,
                        Client *c);
       +static gboolean buttonreleased(GtkWidget *w, GdkEventKey *e, Client *c);
       +static gboolean keypress(GtkAccelGroup *group, GObject *obj, guint key,
       +                         GdkModifierType mods, Client *c);
       +static GdkFilterReturn processx(GdkXEvent *xevent, GdkEvent *event,
       +                                gpointer d);
       +static gboolean winevent(GtkWidget *w, GdkEvent *e, Client *c);
       +static void showview(WebKitWebView *v, Client *c);
       +static GtkWidget *createwindow(Client *c);
       +static void loadchanged(WebKitWebView *v, WebKitLoadEvent e, Client *c);
       +static void progresschanged(WebKitWebView *v, GParamSpec *ps, Client *c);
       +static void titlechanged(WebKitWebView *view, GParamSpec *ps, Client *c);
       +static void mousetargetchanged(WebKitWebView *v, WebKitHitTestResult *h,
       +                guint modifiers, Client *c);
       +static gboolean permissionrequested(WebKitWebView *v,
       +                WebKitPermissionRequest *r, Client *c);
        static gboolean decidepolicy(WebKitWebView *v, WebKitPolicyDecision *d,
            WebKitPolicyDecisionType dt, Client *c);
        static void decidenavigation(WebKitPolicyDecision *d, Client *c);
        static void decidenewwindow(WebKitPolicyDecision *d, Client *c);
        static void decideresource(WebKitPolicyDecision *d, Client *c);
       -static void closeview(WebKitWebView *v, Client *c);
       -static void destroyclient(Client *c);
       -static void destroywin(GtkWidget* w, Client *c);
       -static void die(const char *errstr, ...);
       -static void evalscript(Client *c, const char *jsstr, ...);
       -static void runscript(Client *c);
       -static void find(Client *c, const Arg *a);
       -static void togglefullscreen(Client *c, const Arg *a);
       -static gboolean permissionrequested(WebKitWebView *v,
       -                WebKitPermissionRequest *r, Client *c);
       -static const char *getatom(Client *c, int a);
       -static void gettogglestats(Client *c);
       -static void getpagestats(Client *c);
       -static char *geturi(Client *c);
       -static const gchar *getstyle(const char *uri);
       -static void setstyle(Client *c, const char *stylefile);
       -
       -static void handleplumb(Client *c, const gchar *uri);
       -
        static void downloadstarted(WebKitWebContext *wc, WebKitDownload *d,
                        Client *c);
        static void responsereceived(WebKitDownload *d, GParamSpec *ps, Client *c);
        static void download(Client *c, WebKitURIResponse *r);
       +static void closeview(WebKitWebView *v, Client *c);
       +static void destroywin(GtkWidget* w, Client *c);
        
       -static void toggleinspector(Client *c, const Arg *a);
       -
       -static gboolean keypress(GtkAccelGroup *group, GObject *obj, guint key,
       -                         GdkModifierType mods, Client *c);
       -static void mousetargetchanged(WebKitWebView *v, WebKitHitTestResult *h,
       -                guint modifiers, Client *c);
       -static void loadchanged(WebKitWebView *v, WebKitLoadEvent e, Client *c);
       -static void loaduri(Client *c, const Arg *a);
       -static void navigate(Client *c, const Arg *a);
       -static void clicknavigate(Client *c, const Arg *a, WebKitHitTestResult *h);
       -static Client *newclient(Client *c);
       -static WebKitWebView *newview(Client *c, WebKitWebView *rv);
       -static void showview(WebKitWebView *v, Client *c);
       -static void newwindow(Client *c, const Arg *arg, gboolean noembed);
       -static GtkWidget *createwindow(Client *c);
       +/* Hotkeys */
        static void pasteuri(GtkClipboard *clipboard, const char *text, gpointer d);
       -static void print(Client *c, const Arg *a);
       -static GdkFilterReturn processx(GdkXEvent *xevent, GdkEvent *event,
       -                                gpointer d);
       -static gboolean winevent(GtkWidget *w, GdkEvent *e, Client *c);
       -static void progresschanged(WebKitWebView *v, GParamSpec *ps, Client *c);
       -static void clicknewwindow(Client *c, const Arg *a, WebKitHitTestResult *h);
        static void reload(Client *c, const Arg *arg);
       -static void scroll_h(Client *c, const Arg *a);
       +static void print(Client *c, const Arg *a);
       +static void clipboard(Client *c, const Arg *a);
       +static void zoom(Client *c, const Arg *a);
        static void scroll_v(Client *c, const Arg *a);
       -static void setatom(Client *c, int a, const char *v);
       -static void setup(void);
       -static void sigchld(int unused);
       -static void spawn(Client *c, const Arg *arg);
       +static void scroll_h(Client *c, const Arg *a);
       +static void navigate(Client *c, const Arg *a);
        static void stop(Client *c, const Arg *arg);
       -static void titlechanged(WebKitWebView *view, GParamSpec *ps, Client *c);
        static void toggle(Client *c, const Arg *a);
       +static void togglefullscreen(Client *c, const Arg *a);
        static void togglecookiepolicy(Client *c, const Arg *arg);
        static void togglestyle(Client *c, const Arg *arg);
       -static void updatetitle(Client *c);
       -static void updatewinid(Client *c);
       -static void usage(void);
       -static void zoom(Client *c, const Arg *a);
       +static void toggleinspector(Client *c, const Arg *a);
       +static void find(Client *c, const Arg *a);
       +
       +/* Buttons */
       +static void clicknavigate(Client *c, const Arg *a, WebKitHitTestResult *h);
       +static void clicknewwindow(Client *c, const Arg *a, WebKitHitTestResult *h);
       +
       +static char winid[64];
       +static char togglestats[10];
       +static char pagestats[2];
       +static Atom atoms[AtomLast];
       +static Window embed;
       +static gboolean showxid = FALSE;
       +static int cookiepolicy;
       +static Display *dpy;
       +static Client *clients;
       +static char *stylefile;
       +static const char *useragent;
       +char *argv0;
        
        /* configuration, allows nested code to access above variables */
        #include "config.h"
        
        void
       -addaccelgroup(Client *c)
       +usage(void)
       +{
       +        die("usage: %s [-bBdDfFgGiIkKmMnNpPsSvx] [-a cookiepolicies ] "
       +            "[-c cookiefile] [-e xid] [-r scriptfile] [-t stylefile] "
       +            "[-u useragent] [-z zoomlevel] [uri]\n", basename(argv0));
       +}
       +
       +void
       +die(const char *errstr, ...)
       +{
       +        va_list ap;
       +
       +        va_start(ap, errstr);
       +        vfprintf(stderr, errstr, ap);
       +        va_end(ap);
       +        exit(EXIT_FAILURE);
       +}
       +
       +void
       +setup(void)
        {
                int i;
       -        GtkAccelGroup *group = gtk_accel_group_new();
       -        GClosure *closure;
        
       -        for (i = 0; i < LENGTH(keys); i++) {
       -                closure = g_cclosure_new(G_CALLBACK(keypress), c, NULL);
       -                gtk_accel_group_connect(group, keys[i].keyval, keys[i].mod, 0,
       -                                        closure);
       +        /* clean up any zombies immediately */
       +        sigchld(0);
       +        gtk_init(NULL, NULL);
       +
       +        dpy = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
       +
       +        /* atoms */
       +        atoms[AtomFind] = XInternAtom(dpy, "_SURF_FIND", False);
       +        atoms[AtomGo] = XInternAtom(dpy, "_SURF_GO", False);
       +        atoms[AtomUri] = XInternAtom(dpy, "_SURF_URI", False);
       +
       +        /* dirs and files */
       +        cookiefile = buildfile(cookiefile);
       +        scriptfile = buildfile(scriptfile);
       +        cachedir   = buildpath(cachedir);
       +
       +        if (stylefile == NULL) {
       +                styledir = buildpath(styledir);
       +                for (i = 0; i < LENGTH(styles); i++) {
       +                        if (regcomp(&(styles[i].re), styles[i].regex,
       +                            REG_EXTENDED)) {
       +                                fprintf(stderr,
       +                                        "Could not compile regex: %s\n",
       +                                        styles[i].regex);
       +                                styles[i].regex = NULL;
       +                        }
       +                        styles[i].style = g_strconcat(styledir, "/",
       +                            styles[i].style, NULL);
       +                }
       +                g_free(styledir);
       +        } else {
       +                stylefile = buildfile(stylefile);
                }
       -        gtk_window_add_accel_group(GTK_WINDOW(c->win), group);
       +}
       +
       +void
       +sigchld(int unused)
       +{
       +        if (signal(SIGCHLD, sigchld) == SIG_ERR)
       +                die("Can't install SIGCHLD handler");
       +        while (0 < waitpid(-1, NULL, WNOHANG));
        }
        
        char *
       t@@ -273,37 +324,161 @@ buildpath(const char *path)
                return fpath;
        }
        
       -gboolean
       -buttonreleased(GtkWidget *w, GdkEventKey *e, Client *c)
       +Client *
       +newclient(Client *rc)
        {
       -        WebKitHitTestResultContext element;
       -        GdkEventButton *eb = (GdkEventButton*)e;
       -        int i;
       +        Client *c;
        
       -        element = webkit_hit_test_result_get_context(c->mousepos);
       +        if (!(c = calloc(1, sizeof(Client))))
       +                die("Cannot malloc!\n");
        
       -        for (i = 0; i < LENGTH(buttons); ++i) {
       -                if (element & buttons[i].target &&
       -                    eb->button == buttons[i].button &&
       -                    CLEANMASK(eb->state) == CLEANMASK(buttons[i].mask) &&
       -                    buttons[i].func) {
       -                        buttons[i].func(c, &buttons[i].arg, c->mousepos);
       -                        return buttons[i].stopevent;
       -                }
       -        }
       +        c->title = NULL;
       +        c->progress = 100;
        
       -        return FALSE;
       +        c->next = clients;
       +        clients = c;
       +
       +        c->view = newview(c, rc ? rc->view : NULL);
       +        c->tlsflags = G_TLS_CERTIFICATE_VALIDATE_ALL + 1;
       +
       +        return c;
        }
        
        void
       -cleanup(void)
       +addaccelgroup(Client *c)
        {
       -        while (clients)
       -                destroyclient(clients);
       -        g_free(cookiefile);
       -        g_free(scriptfile);
       -        g_free(stylefile);
       -        g_free(cachedir);
       +        int i;
       +        GtkAccelGroup *group = gtk_accel_group_new();
       +        GClosure *closure;
       +
       +        for (i = 0; i < LENGTH(keys); i++) {
       +                closure = g_cclosure_new(G_CALLBACK(keypress), c, NULL);
       +                gtk_accel_group_connect(group, keys[i].keyval, keys[i].mod, 0,
       +                                        closure);
       +        }
       +        gtk_window_add_accel_group(GTK_WINDOW(c->win), group);
       +}
       +
       +void
       +loaduri(Client *c, const Arg *a)
       +{
       +        struct stat st;
       +        char *url, *path;
       +        const char *uri = (char *)a->v;
       +
       +        if (g_strcmp0(uri, "") == 0)
       +                return;
       +
       +        if (g_strrstr(uri, "://") || g_str_has_prefix(uri, "about:")) {
       +                url = g_strdup(uri);
       +        } else if (!stat(uri, &st) && (path = realpath(uri, NULL))) {
       +                url = g_strdup_printf("file://%s", path);
       +                free(path);
       +        } else {
       +                url = g_strdup_printf("http://%s", uri);
       +        }
       +
       +        setatom(c, AtomUri, url);
       +
       +        if (strcmp(url, geturi(c)) == 0) {
       +                reload(c, a);
       +        } else {
       +                webkit_web_view_load_uri(c->view, url);
       +                c->title = geturi(c);
       +                updatetitle(c);
       +        }
       +
       +        g_free(url);
       +}
       +
       +char *
       +geturi(Client *c)
       +{
       +        char *uri;
       +
       +        if (!(uri = (char *)webkit_web_view_get_uri(c->view)))
       +                uri = "about:blank";
       +        return uri;
       +}
       +
       +void
       +setatom(Client *c, int a, const char *v)
       +{
       +        XSync(dpy, False);
       +        XChangeProperty(dpy, c->xid,
       +                        atoms[a], XA_STRING, 8, PropModeReplace,
       +                        (unsigned char *)v, strlen(v) + 1);
       +}
       +
       +const char *
       +getatom(Client *c, int a)
       +{
       +        static char buf[BUFSIZ];
       +        Atom adummy;
       +        int idummy;
       +        unsigned long ldummy;
       +        unsigned char *p = NULL;
       +
       +        XGetWindowProperty(dpy, c->xid,
       +                           atoms[a], 0L, BUFSIZ, False, XA_STRING,
       +                           &adummy, &idummy, &ldummy, &ldummy, &p);
       +        if (p)
       +                strncpy(buf, (char *)p, LENGTH(buf)-1);
       +        else
       +                buf[0] = '\0';
       +        XFree(p);
       +
       +        return buf;
       +}
       +
       +void
       +updatetitle(Client *c)
       +{
       +        char *title;
       +
       +        if (showindicators) {
       +                gettogglestats(c);
       +                getpagestats(c);
       +
       +                if (c->progress != 100) {
       +                        title = g_strdup_printf("[%i%%] %s:%s | %s",
       +                            c->progress, togglestats, pagestats,
       +                            c->targeturi ? c->targeturi : c->title);
       +                } else {
       +                        title = g_strdup_printf("%s:%s | %s",
       +                            togglestats, pagestats,
       +                            c->targeturi ? c->targeturi : c->title);
       +                }
       +
       +                gtk_window_set_title(GTK_WINDOW(c->win), title);
       +                g_free(title);
       +        } else {
       +                gtk_window_set_title(GTK_WINDOW(c->win), c->title ?
       +                    c->title : "");
       +        }
       +}
       +
       +void
       +gettogglestats(Client *c)
       +{
       +        togglestats[0] = cookiepolicy_set(cookiepolicy_get());
       +        togglestats[1] = enablecaretbrowsing ? 'C' : 'c';
       +        togglestats[2] = allowgeolocation ? 'G' : 'g';
       +        togglestats[3] = enablecache ? 'D' : 'd';
       +        togglestats[4] = loadimages ? 'I' : 'i';
       +        togglestats[5] = enablescripts ? 'S': 's';
       +        togglestats[6] = enableplugins ? 'V' : 'v';
       +        togglestats[7] = enablestyle ? 'M' : 'm';
       +        togglestats[8] = enableframeflattening ? 'F' : 'f';
       +        togglestats[9] = '\0';
       +}
       +
       +void
       +getpagestats(Client *c)
       +{
       +        pagestats[0] = c->tlsflags > G_TLS_CERTIFICATE_VALIDATE_ALL ? '-' :
       +            c->tlsflags > 0 ? 'U' : 'T';
       +        pagestats[1] = '\0';
        }
        
        WebKitCookieAcceptPolicy
       t@@ -338,18 +513,41 @@ cookiepolicy_set(const WebKitCookieAcceptPolicy ep)
                return 'A';
        }
        
       +const gchar *
       +getstyle(const char *uri)
       +{
       +        int i;
       +
       +        if (stylefile != NULL)
       +                return stylefile;
       +
       +        for (i = 0; i < LENGTH(styles); i++) {
       +                if (styles[i].regex && !regexec(&(styles[i].re), uri, 0,
       +                    NULL, 0))
       +                        return styles[i].style;
       +        }
       +
       +        return "";
       +}
       +
        void
       -evalscript(Client *c, const char *jsstr, ...)
       +setstyle(Client *c, const char *stylefile)
        {
       -        va_list ap;
       -        gchar *script;
       +        gchar *style;
        
       -        va_start(ap, jsstr);
       -        script = g_strdup_vprintf(jsstr, ap);
       -        va_end(ap);
       +        if (!g_file_get_contents(stylefile, &style, NULL, NULL)) {
       +                fprintf(stderr, "Could not read style file: %s\n", stylefile);
       +                return;
       +        }
        
       -        webkit_web_view_run_javascript(c->view, script, NULL, NULL, NULL);
       -        g_free(script);
       +        webkit_user_content_manager_add_style_sheet(
       +            webkit_web_view_get_user_content_manager(c->view),
       +            webkit_user_style_sheet_new(style,
       +            WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES,
       +            WEBKIT_USER_STYLE_LEVEL_USER,
       +            NULL, NULL));
       +
       +        g_free(style);
        }
        
        void
       t@@ -364,517 +562,129 @@ runscript(Client *c)
        }
        
        void
       -clipboard(Client *c, const Arg *a)
       -{
       -        if (a->b) { /* load clipboard uri */
       -                gtk_clipboard_request_text(gtk_clipboard_get(
       -                                           GDK_SELECTION_PRIMARY),
       -                                           pasteuri, c);
       -        } else { /* copy uri */
       -                gtk_clipboard_set_text(gtk_clipboard_get(
       -                                       GDK_SELECTION_PRIMARY), c->targeturi
       -                                       ? c->targeturi : geturi(c), -1);
       -        }
       -}
       -
       -GtkWidget *
       -createview(WebKitWebView *v, WebKitNavigationAction *a, Client *c)
       +evalscript(Client *c, const char *jsstr, ...)
        {
       -        Client *n;
       +        va_list ap;
       +        gchar *script;
        
       -        switch (webkit_navigation_action_get_navigation_type(a)) {
       -        case WEBKIT_NAVIGATION_TYPE_OTHER: /* fallthrough */
       -                /*
       -                 * popup windows of type “other” are almost always triggered
       -                 * by user gesture, so inverse the logic here
       -                 */
       -/* instead of this, compare destination uri to mouse-over uri for validating window */
       -                if (webkit_navigation_action_is_user_gesture(a)) {
       -                        return NULL;
       -                        break;
       -                }
       -        case WEBKIT_NAVIGATION_TYPE_LINK_CLICKED: /* fallthrough */
       -        case WEBKIT_NAVIGATION_TYPE_FORM_SUBMITTED: /* fallthrough */
       -        case WEBKIT_NAVIGATION_TYPE_BACK_FORWARD: /* fallthrough */
       -        case WEBKIT_NAVIGATION_TYPE_RELOAD: /* fallthrough */
       -        case WEBKIT_NAVIGATION_TYPE_FORM_RESUBMITTED:
       -                n = newclient(c);
       -                break;
       -        default:
       -                return NULL;
       -                break;
       -        }
       +        va_start(ap, jsstr);
       +        script = g_strdup_vprintf(jsstr, ap);
       +        va_end(ap);
        
       -        return GTK_WIDGET(n->view);
       +        webkit_web_view_run_javascript(c->view, script, NULL, NULL, NULL);
       +        g_free(script);
        }
        
       -gboolean
       -decidepolicy(WebKitWebView *v, WebKitPolicyDecision *d,
       -    WebKitPolicyDecisionType dt, Client *c)
       +void
       +updatewinid(Client *c)
        {
       -        switch (dt) {
       -        case WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION:
       -                decidenavigation(d, c);
       -                break;
       -        case WEBKIT_POLICY_DECISION_TYPE_NEW_WINDOW_ACTION:
       -                decidenewwindow(d, c);
       -                break;
       -        case WEBKIT_POLICY_DECISION_TYPE_RESPONSE:
       -                decideresource(d, c);
       -                break;
       -        default:
       -                webkit_policy_decision_ignore(d);
       -                break;
       -        }
       -        return TRUE;
       +        snprintf(winid, LENGTH(winid), "%lu", c->xid);
        }
        
        void
       -decidenavigation(WebKitPolicyDecision *d, Client *c)
       +handleplumb(Client *c, const gchar *uri)
        {
       -        WebKitNavigationAction *a;
       +        Arg arg;
        
       -        a = webkit_navigation_policy_decision_get_navigation_action(
       -            WEBKIT_NAVIGATION_POLICY_DECISION(d));
       -
       -        switch (webkit_navigation_action_get_navigation_type(a)) {
       -        case WEBKIT_NAVIGATION_TYPE_LINK_CLICKED: /* fallthrough */
       -        case WEBKIT_NAVIGATION_TYPE_FORM_SUBMITTED: /* fallthrough */
       -        case WEBKIT_NAVIGATION_TYPE_BACK_FORWARD: /* fallthrough */
       -        case WEBKIT_NAVIGATION_TYPE_RELOAD: /* fallthrough */
       -        case WEBKIT_NAVIGATION_TYPE_FORM_RESUBMITTED:
       -        case WEBKIT_NAVIGATION_TYPE_OTHER: /* fallthrough */
       -        default:
       -                /* Do not navigate to links with a "_blank" target (popup) */
       -                if (webkit_navigation_policy_decision_get_frame_name(
       -                    WEBKIT_NAVIGATION_POLICY_DECISION(d))) {
       -                        webkit_policy_decision_ignore(d);
       -                } else {
       -                        /* Filter out navigation to different domain ? */
       -                        /* get action→urirequest, copy and load in new window+view
       -                         * on Ctrl+Click ? */
       -                        webkit_policy_decision_use(d);
       -                }
       -                break;
       -        }
       -}
       -
       -void
       -decidenewwindow(WebKitPolicyDecision *d, Client *c)
       -{
       -        WebKitNavigationAction *a;
       -        Arg arg;
       -
       -        a = webkit_navigation_policy_decision_get_navigation_action(
       -            WEBKIT_NAVIGATION_POLICY_DECISION(d));
       -
       -        switch (webkit_navigation_action_get_navigation_type(a)) {
       -        case WEBKIT_NAVIGATION_TYPE_LINK_CLICKED: /* fallthrough */
       -        case WEBKIT_NAVIGATION_TYPE_FORM_SUBMITTED: /* fallthrough */
       -        case WEBKIT_NAVIGATION_TYPE_BACK_FORWARD: /* fallthrough */
       -        case WEBKIT_NAVIGATION_TYPE_RELOAD: /* fallthrough */
       -        case WEBKIT_NAVIGATION_TYPE_FORM_RESUBMITTED:
       -                /* Filter domains here */
       -/* If the value of “mouse-button” is not 0, then the navigation was triggered by a mouse event.
       - * test for link clicked but no button ? */
       -                arg.v = webkit_uri_request_get_uri(
       -                    webkit_navigation_action_get_request(a));
       -                newwindow(c, &arg, 0);
       -                break;
       -        case WEBKIT_NAVIGATION_TYPE_OTHER: /* fallthrough */
       -        default:
       -                break;
       -        }
       -
       -        webkit_policy_decision_ignore(d);
       -}
       -
       -void
       -decideresource(WebKitPolicyDecision *d, Client *c)
       -{
       -        const gchar *uri;
       -        int i, isascii = 1;
       -        WebKitResponsePolicyDecision *r = WEBKIT_RESPONSE_POLICY_DECISION(d);
       -        WebKitURIResponse *res;
       -
       -        res = webkit_response_policy_decision_get_response(r);
       -        uri = webkit_uri_response_get_uri(res);
       -
       -        if (g_str_has_suffix(uri, "/favicon.ico"))
       -                webkit_uri_request_set_uri(
       -                    webkit_response_policy_decision_get_request(r),
       -                    "about:blank");
       -
       -        if (!g_str_has_prefix(uri, "http://")
       -            && !g_str_has_prefix(uri, "https://")
       -            && !g_str_has_prefix(uri, "about:")
       -            && !g_str_has_prefix(uri, "file://")
       -            && !g_str_has_prefix(uri, "data:")
       -            && !g_str_has_prefix(uri, "blob:")
       -            && strlen(uri) > 0) {
       -                for (i = 0; i < strlen(uri); i++) {
       -                        if (!g_ascii_isprint(uri[i])) {
       -                                isascii = 0;
       -                                break;
       -                        }
       -                }
       -                if (isascii) {
       -                        handleplumb(c, uri);
       -                        webkit_policy_decision_ignore(d);
       -                }
       -        }
       -
       -        if (webkit_response_policy_decision_is_mime_type_supported(r)) {
       -                webkit_policy_decision_use(d);
       -        } else {
       -                webkit_policy_decision_ignore(d);
       -                download(c, res);
       -        }
       -}
       -
       -void
       -destroyclient(Client *c)
       -{
       -        Client *p;
       -
       -        webkit_web_view_stop_loading(c->view);
       -        /* Not needed, has already been called
       -        gtk_widget_destroy(c->win);
       -         */
       -
       -        for (p = clients; p && p->next != c; p = p->next)
       -                ;
       -        if (p)
       -                p->next = c->next;
       -        else
       -                clients = c->next;
       -        free(c);
       -}
       -
       -void
       -closeview(WebKitWebView *v, Client *c)
       -{
       -        gtk_widget_destroy(c->win);
       -}
       -
       -void
       -destroywin(GtkWidget* w, Client *c)
       -{
       -        destroyclient(c);
       -        if (clients == NULL)
       -                gtk_main_quit();
       -}
       -
       -void
       -die(const char *errstr, ...)
       -{
       -        va_list ap;
       -
       -        va_start(ap, errstr);
       -        vfprintf(stderr, errstr, ap);
       -        va_end(ap);
       -        exit(EXIT_FAILURE);
       -}
       -
       -void
       -find(Client *c, const Arg *a)
       -{
       -        const char *s, *f;
       -
       -        if (a && a->i) {
       -                if (a->i > 0)
       -                        webkit_find_controller_search_next(c->finder);
       -                else
       -                        webkit_find_controller_search_previous(c->finder);
       -        } else {
       -                s = getatom(c, AtomFind);
       -                f = webkit_find_controller_get_search_text(c->finder);
       -
       -                if (g_strcmp0(f, s) == 0) /* reset search */
       -                        webkit_find_controller_search(c->finder, "", findopts, G_MAXUINT);
       -
       -                webkit_find_controller_search(c->finder, s, findopts, G_MAXUINT);
       -
       -                if (strcmp(s, "") == 0)
       -                        webkit_find_controller_search_finish(c->finder);
       -        }
       -}
       -
       -void
       -togglefullscreen(Client *c, const Arg *a)
       -{
       -        /* toggling value is handled in winevent() */
       -        if (c->fullscreen)
       -                gtk_window_unfullscreen(GTK_WINDOW(c->win));
       -        else
       -                gtk_window_fullscreen(GTK_WINDOW(c->win));
       -}
       -
       -gboolean
       -permissionrequested(WebKitWebView *v, WebKitPermissionRequest *r, Client *c)
       -{
       -        if (WEBKIT_IS_GEOLOCATION_PERMISSION_REQUEST(r)) {
       -                if (allowgeolocation)
       -                        webkit_permission_request_allow(r);
       -                else
       -                        webkit_permission_request_deny(r);
       -                return TRUE;
       -        }
       -
       -        return FALSE;
       -}
       -
       -const char *
       -getatom(Client *c, int a)
       -{
       -        static char buf[BUFSIZ];
       -        Atom adummy;
       -        int idummy;
       -        unsigned long ldummy;
       -        unsigned char *p = NULL;
       -
       -        XGetWindowProperty(dpy, c->xid,
       -                           atoms[a], 0L, BUFSIZ, False, XA_STRING,
       -                           &adummy, &idummy, &ldummy, &ldummy, &p);
       -        if (p)
       -                strncpy(buf, (char *)p, LENGTH(buf)-1);
       -        else
       -                buf[0] = '\0';
       -        XFree(p);
       -
       -        return buf;
       -}
       -
       -char *
       -geturi(Client *c)
       -{
       -        char *uri;
       -
       -        if (!(uri = (char *)webkit_web_view_get_uri(c->view)))
       -                uri = "about:blank";
       -        return uri;
       -}
       -
       -const gchar *
       -getstyle(const char *uri)
       -{
       -        int i;
       -
       -        if (stylefile != NULL)
       -                return stylefile;
       -
       -        for (i = 0; i < LENGTH(styles); i++) {
       -                if (styles[i].regex && !regexec(&(styles[i].re), uri, 0,
       -                    NULL, 0))
       -                        return styles[i].style;
       -        }
       -
       -        return "";
       -}
       +        arg = (Arg)PLUMB(uri);
       +        spawn(c, &arg);
       +}
        
        void
       -setstyle(Client *c, const char *stylefile)
       -{
       -        gchar *style;
       -
       -        if (!g_file_get_contents(stylefile, &style, NULL, NULL)) {
       -                fprintf(stderr, "Could not read style file: %s\n", stylefile);
       -                return;
       -        }
       -
       -        webkit_user_content_manager_add_style_sheet(
       -            webkit_web_view_get_user_content_manager(c->view),
       -            webkit_user_style_sheet_new(style,
       -            WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES,
       -            WEBKIT_USER_STYLE_LEVEL_USER,
       -            NULL, NULL));
       -
       -        g_free(style);
       -}
       -
       -void
       -handleplumb(Client *c, const gchar *uri)
       -{
       -        Arg arg;
       -
       -        arg = (Arg)PLUMB(uri);
       -        spawn(c, &arg);
       -}
       -
       -void
       -downloadstarted(WebKitWebContext *wc, WebKitDownload *d, Client *c)
       -{
       -        g_signal_connect(G_OBJECT(d), "notify::response",
       -            G_CALLBACK(responsereceived), c);
       -}
       -
       -void
       -responsereceived(WebKitDownload *d, GParamSpec *ps, Client *c)
       -{
       -        download(c, webkit_download_get_response(d));
       -        webkit_download_cancel(d);
       -}
       -
       -void
       -download(Client *c, WebKitURIResponse *r)
       +newwindow(Client *c, const Arg *a, int noembed)
        {
       -        Arg a;
       -
       -        a = (Arg)DOWNLOAD(webkit_uri_response_get_uri(r), geturi(c));
       -        spawn(c, &a);
       -}
       +        int i = 0;
       +        char tmp[64];
       +        const char *cmd[26], *uri;
       +        const Arg arg = { .v = cmd };
        
       -void
       -toggleinspector(Client *c, const Arg *a)
       -{
       -        if (enableinspector) {
       -                if (webkit_web_inspector_is_attached(c->inspector))
       -                        webkit_web_inspector_close(c->inspector);
       -                else
       -                        webkit_web_inspector_show(c->inspector);
       +        cmd[i++] = argv0;
       +        cmd[i++] = "-a";
       +        cmd[i++] = cookiepolicies;
       +        cmd[i++] = enablescrollbars ? "-B" : "-b";
       +        if (cookiefile && g_strcmp0(cookiefile, "")) {
       +                cmd[i++] = "-c";
       +                cmd[i++] = cookiefile;
                }
       -}
       -
       -gboolean
       -keypress(GtkAccelGroup *group, GObject *obj, guint key, GdkModifierType mods,
       -         Client *c)
       -{
       -        guint i;
       -        gboolean processed = FALSE;
       -
       -        mods = CLEANMASK(mods);
       -        key = gdk_keyval_to_lower(key);
       -        updatewinid(c);
       -        for (i = 0; i < LENGTH(keys); i++) {
       -                if (key == keys[i].keyval
       -                    && mods == keys[i].mod
       -                    && keys[i].func) {
       -                        keys[i].func(c, &(keys[i].arg));
       -                        processed = TRUE;
       -                }
       +        cmd[i++] = enablecache ? "-D" : "-d";
       +        if (embed && !noembed) {
       +                cmd[i++] = "-e";
       +                snprintf(tmp, LENGTH(tmp), "%lu", embed);
       +                cmd[i++] = tmp;
                }
       -
       -        return processed;
       -}
       -
       -void
       -mousetargetchanged(WebKitWebView *v, WebKitHitTestResult *h, guint modifiers,
       -    Client *c)
       -{
       -        WebKitHitTestResultContext hc;
       -
       -        /* Keep the hit test to know where is the pointer on the next click */
       -        c->mousepos = h;
       -
       -        hc = webkit_hit_test_result_get_context(h);
       -
       -        if (hc & OnLink)
       -                c->targeturi = webkit_hit_test_result_get_link_uri(h);
       -        else if (hc & OnImg)
       -                c->targeturi = webkit_hit_test_result_get_image_uri(h);
       -        else if (hc & OnMedia)
       -                c->targeturi = webkit_hit_test_result_get_media_uri(h);
       -        else
       -                c->targeturi = NULL;
       -        updatetitle(c);
       -}
       -
       -void
       -loadchanged(WebKitWebView *v, WebKitLoadEvent e, Client *c)
       -{
       -        switch (e) {
       -        case WEBKIT_LOAD_STARTED:
       -                c->tlsflags = G_TLS_CERTIFICATE_VALIDATE_ALL + 1;
       -                break;
       -        case WEBKIT_LOAD_REDIRECTED:
       -                setatom(c, AtomUri, geturi(c));
       -                break;
       -        case WEBKIT_LOAD_COMMITTED:
       -                if (!webkit_web_view_get_tls_info(c->view, NULL, &(c->tlsflags)))
       -                        c->tlsflags = G_TLS_CERTIFICATE_VALIDATE_ALL + 1;
       -
       -                setatom(c, AtomUri, geturi(c));
       -
       -                if (enablestyle)
       -                        setstyle(c, getstyle(geturi(c)));
       -                break;
       -        case WEBKIT_LOAD_FINISHED:
       -                /* Disabled until we write some WebKitWebExtension for
       -                 * manipulating the DOM directly.
       -                evalscript(c, "document.documentElement.style.overflow = '%s'",
       -                    enablescrollbars ? "auto" : "hidden");
       -                */
       -                runscript(c);
       -                break;
       +        cmd[i++] = runinfullscreen ? "-F" : "-f";
       +        cmd[i++] = allowgeolocation ? "-G" : "-g";
       +        cmd[i++] = loadimages ? "-I" : "-i";
       +        cmd[i++] = kioskmode ? "-K" : "-k";
       +        cmd[i++] = enablestyle ? "-M" : "-m";
       +        cmd[i++] = enableinspector ? "-N" : "-n";
       +        cmd[i++] = enableplugins ? "-P" : "-p";
       +        if (scriptfile && g_strcmp0(scriptfile, "")) {
       +                cmd[i++] = "-r";
       +                cmd[i++] = scriptfile;
                }
       -        updatetitle(c);
       -}
       -
       -void
       -loaduri(Client *c, const Arg *a)
       -{
       -        struct stat st;
       -        char *url, *path;
       -        const char *uri = (char *)a->v;
       -
       -        if (g_strcmp0(uri, "") == 0)
       -                return;
       -
       -        if (g_strrstr(uri, "://") || g_str_has_prefix(uri, "about:")) {
       -                url = g_strdup(uri);
       -        } else if (!stat(uri, &st) && (path = realpath(uri, NULL))) {
       -                url = g_strdup_printf("file://%s", path);
       -                free(path);
       -        } else {
       -                url = g_strdup_printf("http://%s", uri);
       +        cmd[i++] = enablescripts ? "-S" : "-s";
       +        if (stylefile && g_strcmp0(stylefile, "")) {
       +                cmd[i++] = "-t";
       +                cmd[i++] = stylefile;
                }
       -
       -        setatom(c, AtomUri, url);
       -
       -        if (strcmp(url, geturi(c)) == 0) {
       -                reload(c, a);
       -        } else {
       -                webkit_web_view_load_uri(c->view, url);
       -                c->title = geturi(c);
       -                updatetitle(c);
       +        if (fulluseragent && g_strcmp0(fulluseragent, "")) {
       +                cmd[i++] = "-u";
       +                cmd[i++] = fulluseragent;
                }
       -
       -        g_free(url);
       -}
       -
       -void
       -navigate(Client *c, const Arg *a)
       -{
       -        if (a->i < 0)
       -                webkit_web_view_go_back(c->view);
       -        else if (a->i > 0)
       -                webkit_web_view_go_forward(c->view);
       +        if (showxid)
       +                cmd[i++] = "-x";
       +        /* do not keep zoom level */
       +        cmd[i++] = "--";
       +        if ((uri = a->v))
       +                cmd[i++] = uri;
       +        cmd[i] = NULL;
       +
       +        spawn(c, &arg);
        }
        
        void
       -clicknavigate(Client *c, const Arg *a, WebKitHitTestResult *h)
       +spawn(Client *c, const Arg *arg)
        {
       -        navigate(c, a);
       +        if (fork() == 0) {
       +                if (dpy)
       +                        close(ConnectionNumber(dpy));
       +                setsid();
       +                execvp(((char **)arg->v)[0], (char **)arg->v);
       +                fprintf(stderr, "surf: execvp %s", ((char **)arg->v)[0]);
       +                perror(" failed");
       +                exit(0);
       +        }
        }
        
       -Client *
       -newclient(Client *rc)
       +void
       +destroyclient(Client *c)
        {
       -        Client *c;
       -
       -        if (!(c = calloc(1, sizeof(Client))))
       -                die("Cannot malloc!\n");
       -
       -        c->title = NULL;
       -        c->progress = 100;
       +        Client *p;
        
       -        c->next = clients;
       -        clients = c;
       +        webkit_web_view_stop_loading(c->view);
       +        /* Not needed, has already been called
       +        gtk_widget_destroy(c->win);
       +         */
        
       -        c->view = newview(c, rc ? rc->view : NULL);
       -        c->tlsflags = G_TLS_CERTIFICATE_VALIDATE_ALL + 1;
       +        for (p = clients; p && p->next != c; p = p->next)
       +                ;
       +        if (p)
       +                p->next = c->next;
       +        else
       +                clients = c->next;
       +        free(c);
       +}
        
       -        return c;
       +void
       +cleanup(void)
       +{
       +        while (clients)
       +                destroyclient(clients);
       +        g_free(cookiefile);
       +        g_free(scriptfile);
       +        g_free(stylefile);
       +        g_free(cachedir);
        }
        
        WebKitWebView *
       t@@ -983,109 +793,178 @@ newview(Client *c, WebKitWebView *rv)
                return v;
        }
        
       -void
       -showview(WebKitWebView *v, Client *c)
       +GtkWidget *
       +createview(WebKitWebView *v, WebKitNavigationAction *a, Client *c)
        {
       -        GdkGeometry hints = { 1, 1 };
       -        GdkRGBA bgcolor = { 0 };
       -        GdkWindow *gwin;
       +        Client *n;
        
       -        c->win = createwindow(c);
       +        switch (webkit_navigation_action_get_navigation_type(a)) {
       +        case WEBKIT_NAVIGATION_TYPE_OTHER: /* fallthrough */
       +                /*
       +                 * popup windows of type “other” are almost always triggered
       +                 * by user gesture, so inverse the logic here
       +                 */
       +/* instead of this, compare destination uri to mouse-over uri for validating window */
       +                if (webkit_navigation_action_is_user_gesture(a)) {
       +                        return NULL;
       +                        break;
       +                }
       +        case WEBKIT_NAVIGATION_TYPE_LINK_CLICKED: /* fallthrough */
       +        case WEBKIT_NAVIGATION_TYPE_FORM_SUBMITTED: /* fallthrough */
       +        case WEBKIT_NAVIGATION_TYPE_BACK_FORWARD: /* fallthrough */
       +        case WEBKIT_NAVIGATION_TYPE_RELOAD: /* fallthrough */
       +        case WEBKIT_NAVIGATION_TYPE_FORM_RESUBMITTED:
       +                n = newclient(c);
       +                break;
       +        default:
       +                return NULL;
       +                break;
       +        }
        
       -        if (enableinspector)
       -                c->inspector = webkit_web_view_get_inspector(c->view);
       +        return GTK_WIDGET(n->view);
       +}
        
       -        c->finder = webkit_web_view_get_find_controller(c->view);
       +gboolean
       +buttonreleased(GtkWidget *w, GdkEventKey *e, Client *c)
       +{
       +        WebKitHitTestResultContext element;
       +        GdkEventButton *eb = (GdkEventButton*)e;
       +        int i;
        
       -        if (!kioskmode)
       -                addaccelgroup(c);
       +        element = webkit_hit_test_result_get_context(c->mousepos);
        
       -        /* Arranging */
       -        gtk_container_add(GTK_CONTAINER(c->win), GTK_WIDGET(c->view));
       +        for (i = 0; i < LENGTH(buttons); ++i) {
       +                if (element & buttons[i].target &&
       +                    eb->button == buttons[i].button &&
       +                    CLEANMASK(eb->state) == CLEANMASK(buttons[i].mask) &&
       +                    buttons[i].func) {
       +                        buttons[i].func(c, &buttons[i].arg, c->mousepos);
       +                        return buttons[i].stopevent;
       +                }
       +        }
        
       -        /* Setup */
       -        gtk_widget_grab_focus(GTK_WIDGET(c->view));
       -        gtk_widget_show(GTK_WIDGET(c->view));
       -        gtk_widget_show(c->win);
       -        gwin = gtk_widget_get_window(GTK_WIDGET(c->win));
       -        c->xid = gdk_x11_window_get_xid(gwin);
       -        gtk_window_set_geometry_hints(GTK_WINDOW(c->win), NULL, &hints,
       -                                      GDK_HINT_MIN_SIZE);
       -        gdk_window_set_events(gwin, GDK_ALL_EVENTS_MASK);
       -        gdk_window_add_filter(gwin, processx, c);
       +        return FALSE;
       +}
        
       -        if (zoomlevel != 1.0)
       -                webkit_web_view_set_zoom_level(c->view, zoomlevel);
       +gboolean
       +keypress(GtkAccelGroup *group, GObject *obj, guint key, GdkModifierType mods,
       +         Client *c)
       +{
       +        guint i;
       +        gboolean processed = FALSE;
        
       -        if (runinfullscreen)
       -                togglefullscreen(c, NULL);
       +        mods = CLEANMASK(mods);
       +        key = gdk_keyval_to_lower(key);
       +        updatewinid(c);
       +        for (i = 0; i < LENGTH(keys); i++) {
       +                if (key == keys[i].keyval
       +                    && mods == keys[i].mod
       +                    && keys[i].func) {
       +                        keys[i].func(c, &(keys[i].arg));
       +                        processed = TRUE;
       +                }
       +        }
        
       -        setatom(c, AtomFind, "");
       -        setatom(c, AtomUri, "about:blank");
       -        if (hidebackground)
       -                webkit_web_view_set_background_color(c->view, &bgcolor);
       +        return processed;
       +}
        
       -        if (showxid) {
       -                gdk_display_sync(gtk_widget_get_display(c->win));
       -                printf("%lu\n", c->xid);
       -                fflush(NULL);
       -                if (fclose(stdout) != 0) {
       -                        die("Error closing stdout");
       -                }
       +GdkFilterReturn
       +processx(GdkXEvent *e, GdkEvent *event, gpointer d)
       +{
       +        Client *c = (Client *)d;
       +        XPropertyEvent *ev;
       +        Arg arg;
       +
       +        if (((XEvent *)e)->type == PropertyNotify) {
       +                ev = &((XEvent *)e)->xproperty;
       +                if (ev->state == PropertyNewValue) {
       +                        if (ev->atom == atoms[AtomFind]) {
       +                                find(c, NULL);
       +
       +                                return GDK_FILTER_REMOVE;
       +                        } else if (ev->atom == atoms[AtomGo]) {
       +                                arg.v = getatom(c, AtomGo);
       +                                loaduri(c, &arg);
       +
       +                                return GDK_FILTER_REMOVE;
       +                        }
       +                }
       +        }
       +        return GDK_FILTER_CONTINUE;
       +}
       +
       +gboolean
       +winevent(GtkWidget *w, GdkEvent *e, Client *c)
       +{
       +        switch (e->type) {
       +        case GDK_LEAVE_NOTIFY:
       +                c->targeturi = NULL;
       +                updatetitle(c);
       +                break;
       +        case GDK_WINDOW_STATE: /* fallthrough */
       +                if (e->window_state.changed_mask ==
       +                    GDK_WINDOW_STATE_FULLSCREEN) {
       +                        c->fullscreen = e->window_state.new_window_state &
       +                            GDK_WINDOW_STATE_FULLSCREEN;
       +                        break;
       +                }
       +        default:
       +                return FALSE;
                }
       +
       +        return TRUE;
        }
        
        void
       -newwindow(Client *c, const Arg *a, int noembed)
       +showview(WebKitWebView *v, Client *c)
        {
       -        int i = 0;
       -        char tmp[64];
       -        const char *cmd[26], *uri;
       -        const Arg arg = { .v = cmd };
       +        GdkGeometry hints = { 1, 1 };
       +        GdkRGBA bgcolor = { 0 };
       +        GdkWindow *gwin;
        
       -        cmd[i++] = argv0;
       -        cmd[i++] = "-a";
       -        cmd[i++] = cookiepolicies;
       -        cmd[i++] = enablescrollbars ? "-B" : "-b";
       -        if (cookiefile && g_strcmp0(cookiefile, "")) {
       -                cmd[i++] = "-c";
       -                cmd[i++] = cookiefile;
       -        }
       -        cmd[i++] = enablecache ? "-D" : "-d";
       -        if (embed && !noembed) {
       -                cmd[i++] = "-e";
       -                snprintf(tmp, LENGTH(tmp), "%lu", embed);
       -                cmd[i++] = tmp;
       -        }
       -        cmd[i++] = runinfullscreen ? "-F" : "-f";
       -        cmd[i++] = allowgeolocation ? "-G" : "-g";
       -        cmd[i++] = loadimages ? "-I" : "-i";
       -        cmd[i++] = kioskmode ? "-K" : "-k";
       -        cmd[i++] = enablestyle ? "-M" : "-m";
       -        cmd[i++] = enableinspector ? "-N" : "-n";
       -        cmd[i++] = enableplugins ? "-P" : "-p";
       -        if (scriptfile && g_strcmp0(scriptfile, "")) {
       -                cmd[i++] = "-r";
       -                cmd[i++] = scriptfile;
       -        }
       -        cmd[i++] = enablescripts ? "-S" : "-s";
       -        if (stylefile && g_strcmp0(stylefile, "")) {
       -                cmd[i++] = "-t";
       -                cmd[i++] = stylefile;
       -        }
       -        if (fulluseragent && g_strcmp0(fulluseragent, "")) {
       -                cmd[i++] = "-u";
       -                cmd[i++] = fulluseragent;
       -        }
       -        if (showxid)
       -                cmd[i++] = "-x";
       -        /* do not keep zoom level */
       -        cmd[i++] = "--";
       -        if ((uri = a->v))
       -                cmd[i++] = uri;
       -        cmd[i] = NULL;
       +        c->win = createwindow(c);
       +
       +        if (enableinspector)
       +                c->inspector = webkit_web_view_get_inspector(c->view);
       +
       +        c->finder = webkit_web_view_get_find_controller(c->view);
       +
       +        if (!kioskmode)
       +                addaccelgroup(c);
        
       -        spawn(c, &arg);
       +        /* Arranging */
       +        gtk_container_add(GTK_CONTAINER(c->win), GTK_WIDGET(c->view));
       +
       +        /* Setup */
       +        gtk_widget_grab_focus(GTK_WIDGET(c->view));
       +        gtk_widget_show(GTK_WIDGET(c->view));
       +        gtk_widget_show(c->win);
       +        gwin = gtk_widget_get_window(GTK_WIDGET(c->win));
       +        c->xid = gdk_x11_window_get_xid(gwin);
       +        gtk_window_set_geometry_hints(GTK_WINDOW(c->win), NULL, &hints,
       +                                      GDK_HINT_MIN_SIZE);
       +        gdk_window_set_events(gwin, GDK_ALL_EVENTS_MASK);
       +        gdk_window_add_filter(gwin, processx, c);
       +
       +        if (zoomlevel != 1.0)
       +                webkit_web_view_set_zoom_level(c->view, zoomlevel);
       +
       +        if (runinfullscreen)
       +                togglefullscreen(c, NULL);
       +
       +        setatom(c, AtomFind, "");
       +        setatom(c, AtomUri, "about:blank");
       +        if (hidebackground)
       +                webkit_web_view_set_background_color(c->view, &bgcolor);
       +
       +        if (showxid) {
       +                gdk_display_sync(gtk_widget_get_display(c->win));
       +                printf("%lu\n", c->xid);
       +                fflush(NULL);
       +                if (fclose(stdout) != 0) {
       +                        die("Error closing stdout");
       +                }
       +        }
        }
        
        GtkWidget *
       t@@ -1126,60 +1005,254 @@ createwindow(Client *c)
        }
        
        void
       -pasteuri(GtkClipboard *clipboard, const char *text, gpointer d)
       +loadchanged(WebKitWebView *v, WebKitLoadEvent e, Client *c)
        {
       -        Arg arg = {.v = text };
       -        if (text != NULL)
       -                loaduri((Client *) d, &arg);
       +        switch (e) {
       +        case WEBKIT_LOAD_STARTED:
       +                c->tlsflags = G_TLS_CERTIFICATE_VALIDATE_ALL + 1;
       +                break;
       +        case WEBKIT_LOAD_REDIRECTED:
       +                setatom(c, AtomUri, geturi(c));
       +                break;
       +        case WEBKIT_LOAD_COMMITTED:
       +                if (!webkit_web_view_get_tls_info(c->view, NULL, &(c->tlsflags)))
       +                        c->tlsflags = G_TLS_CERTIFICATE_VALIDATE_ALL + 1;
       +
       +                setatom(c, AtomUri, geturi(c));
       +
       +                if (enablestyle)
       +                        setstyle(c, getstyle(geturi(c)));
       +                break;
       +        case WEBKIT_LOAD_FINISHED:
       +                /* Disabled until we write some WebKitWebExtension for
       +                 * manipulating the DOM directly.
       +                evalscript(c, "document.documentElement.style.overflow = '%s'",
       +                    enablescrollbars ? "auto" : "hidden");
       +                */
       +                runscript(c);
       +                break;
       +        }
       +        updatetitle(c);
        }
        
        void
       -print(Client *c, const Arg *a)
       +progresschanged(WebKitWebView *v, GParamSpec *ps, Client *c)
        {
       -        webkit_print_operation_run_dialog(webkit_print_operation_new(c->view),
       -            GTK_WINDOW(c->win));
       +        c->progress = webkit_web_view_get_estimated_load_progress(c->view) *
       +            100;
       +        updatetitle(c);
        }
        
       -GdkFilterReturn
       -processx(GdkXEvent *e, GdkEvent *event, gpointer d)
       +void
       +titlechanged(WebKitWebView *view, GParamSpec *ps, Client *c)
        {
       -        Client *c = (Client *)d;
       -        XPropertyEvent *ev;
       +        c->title = webkit_web_view_get_title(c->view);
       +        updatetitle(c);
       +}
       +
       +void
       +mousetargetchanged(WebKitWebView *v, WebKitHitTestResult *h, guint modifiers,
       +    Client *c)
       +{
       +        WebKitHitTestResultContext hc;
       +
       +        /* Keep the hit test to know where is the pointer on the next click */
       +        c->mousepos = h;
       +
       +        hc = webkit_hit_test_result_get_context(h);
       +
       +        if (hc & OnLink)
       +                c->targeturi = webkit_hit_test_result_get_link_uri(h);
       +        else if (hc & OnImg)
       +                c->targeturi = webkit_hit_test_result_get_image_uri(h);
       +        else if (hc & OnMedia)
       +                c->targeturi = webkit_hit_test_result_get_media_uri(h);
       +        else
       +                c->targeturi = NULL;
       +        updatetitle(c);
       +}
       +
       +gboolean
       +permissionrequested(WebKitWebView *v, WebKitPermissionRequest *r, Client *c)
       +{
       +        if (WEBKIT_IS_GEOLOCATION_PERMISSION_REQUEST(r)) {
       +                if (allowgeolocation)
       +                        webkit_permission_request_allow(r);
       +                else
       +                        webkit_permission_request_deny(r);
       +                return TRUE;
       +        }
       +
       +        return FALSE;
       +}
       +
       +gboolean
       +decidepolicy(WebKitWebView *v, WebKitPolicyDecision *d,
       +    WebKitPolicyDecisionType dt, Client *c)
       +{
       +        switch (dt) {
       +        case WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION:
       +                decidenavigation(d, c);
       +                break;
       +        case WEBKIT_POLICY_DECISION_TYPE_NEW_WINDOW_ACTION:
       +                decidenewwindow(d, c);
       +                break;
       +        case WEBKIT_POLICY_DECISION_TYPE_RESPONSE:
       +                decideresource(d, c);
       +                break;
       +        default:
       +                webkit_policy_decision_ignore(d);
       +                break;
       +        }
       +        return TRUE;
       +}
       +
       +void
       +decidenavigation(WebKitPolicyDecision *d, Client *c)
       +{
       +        WebKitNavigationAction *a;
       +
       +        a = webkit_navigation_policy_decision_get_navigation_action(
       +            WEBKIT_NAVIGATION_POLICY_DECISION(d));
       +
       +        switch (webkit_navigation_action_get_navigation_type(a)) {
       +        case WEBKIT_NAVIGATION_TYPE_LINK_CLICKED: /* fallthrough */
       +        case WEBKIT_NAVIGATION_TYPE_FORM_SUBMITTED: /* fallthrough */
       +        case WEBKIT_NAVIGATION_TYPE_BACK_FORWARD: /* fallthrough */
       +        case WEBKIT_NAVIGATION_TYPE_RELOAD: /* fallthrough */
       +        case WEBKIT_NAVIGATION_TYPE_FORM_RESUBMITTED:
       +        case WEBKIT_NAVIGATION_TYPE_OTHER: /* fallthrough */
       +        default:
       +                /* Do not navigate to links with a "_blank" target (popup) */
       +                if (webkit_navigation_policy_decision_get_frame_name(
       +                    WEBKIT_NAVIGATION_POLICY_DECISION(d))) {
       +                        webkit_policy_decision_ignore(d);
       +                } else {
       +                        /* Filter out navigation to different domain ? */
       +                        /* get action→urirequest, copy and load in new window+view
       +                         * on Ctrl+Click ? */
       +                        webkit_policy_decision_use(d);
       +                }
       +                break;
       +        }
       +}
       +
       +void
       +decidenewwindow(WebKitPolicyDecision *d, Client *c)
       +{
       +        WebKitNavigationAction *a;
                Arg arg;
        
       -        if (((XEvent *)e)->type == PropertyNotify) {
       -                ev = &((XEvent *)e)->xproperty;
       -                if (ev->state == PropertyNewValue) {
       -                        if (ev->atom == atoms[AtomFind]) {
       -                                find(c, NULL);
       +        a = webkit_navigation_policy_decision_get_navigation_action(
       +            WEBKIT_NAVIGATION_POLICY_DECISION(d));
       +
       +        switch (webkit_navigation_action_get_navigation_type(a)) {
       +        case WEBKIT_NAVIGATION_TYPE_LINK_CLICKED: /* fallthrough */
       +        case WEBKIT_NAVIGATION_TYPE_FORM_SUBMITTED: /* fallthrough */
       +        case WEBKIT_NAVIGATION_TYPE_BACK_FORWARD: /* fallthrough */
       +        case WEBKIT_NAVIGATION_TYPE_RELOAD: /* fallthrough */
       +        case WEBKIT_NAVIGATION_TYPE_FORM_RESUBMITTED:
       +                /* Filter domains here */
       +/* If the value of “mouse-button” is not 0, then the navigation was triggered by a mouse event.
       + * test for link clicked but no button ? */
       +                arg.v = webkit_uri_request_get_uri(
       +                    webkit_navigation_action_get_request(a));
       +                newwindow(c, &arg, 0);
       +                break;
       +        case WEBKIT_NAVIGATION_TYPE_OTHER: /* fallthrough */
       +        default:
       +                break;
       +        }
       +
       +        webkit_policy_decision_ignore(d);
       +}
       +
       +void
       +decideresource(WebKitPolicyDecision *d, Client *c)
       +{
       +        const gchar *uri;
       +        int i, isascii = 1;
       +        WebKitResponsePolicyDecision *r = WEBKIT_RESPONSE_POLICY_DECISION(d);
       +        WebKitURIResponse *res;
       +
       +        res = webkit_response_policy_decision_get_response(r);
       +        uri = webkit_uri_response_get_uri(res);
       +
       +        if (g_str_has_suffix(uri, "/favicon.ico"))
       +                webkit_uri_request_set_uri(
       +                    webkit_response_policy_decision_get_request(r),
       +                    "about:blank");
       +
       +        if (!g_str_has_prefix(uri, "http://")
       +            && !g_str_has_prefix(uri, "https://")
       +            && !g_str_has_prefix(uri, "about:")
       +            && !g_str_has_prefix(uri, "file://")
       +            && !g_str_has_prefix(uri, "data:")
       +            && !g_str_has_prefix(uri, "blob:")
       +            && strlen(uri) > 0) {
       +                for (i = 0; i < strlen(uri); i++) {
       +                        if (!g_ascii_isprint(uri[i])) {
       +                                isascii = 0;
       +                                break;
       +                        }
       +                }
       +                if (isascii) {
       +                        handleplumb(c, uri);
       +                        webkit_policy_decision_ignore(d);
       +                }
       +        }
       +
       +        if (webkit_response_policy_decision_is_mime_type_supported(r)) {
       +                webkit_policy_decision_use(d);
       +        } else {
       +                webkit_policy_decision_ignore(d);
       +                download(c, res);
       +        }
       +}
       +
       +void
       +downloadstarted(WebKitWebContext *wc, WebKitDownload *d, Client *c)
       +{
       +        g_signal_connect(G_OBJECT(d), "notify::response",
       +            G_CALLBACK(responsereceived), c);
       +}
       +
       +void
       +responsereceived(WebKitDownload *d, GParamSpec *ps, Client *c)
       +{
       +        download(c, webkit_download_get_response(d));
       +        webkit_download_cancel(d);
       +}
        
       -                                return GDK_FILTER_REMOVE;
       -                        } else if (ev->atom == atoms[AtomGo]) {
       -                                arg.v = getatom(c, AtomGo);
       -                                loaduri(c, &arg);
       +void
       +download(Client *c, WebKitURIResponse *r)
       +{
       +        Arg a;
        
       -                                return GDK_FILTER_REMOVE;
       -                        }
       -                }
       -        }
       -        return GDK_FILTER_CONTINUE;
       +        a = (Arg)DOWNLOAD(webkit_uri_response_get_uri(r), geturi(c));
       +        spawn(c, &a);
        }
        
        void
       -progresschanged(WebKitWebView *v, GParamSpec *ps, Client *c)
       +closeview(WebKitWebView *v, Client *c)
        {
       -        c->progress = webkit_web_view_get_estimated_load_progress(c->view) *
       -            100;
       -        updatetitle(c);
       +        gtk_widget_destroy(c->win);
        }
        
        void
       -clicknewwindow(Client *c, const Arg *a, WebKitHitTestResult *h)
       +destroywin(GtkWidget* w, Client *c)
        {
       -        Arg arg;
       +        destroyclient(c);
       +        if (clients == NULL)
       +                gtk_main_quit();
       +}
        
       -        arg.v = webkit_hit_test_result_get_link_uri(h);
       -        newwindow(c, &arg, a->b);
       +void
       +pasteuri(GtkClipboard *clipboard, const char *text, gpointer d)
       +{
       +        Arg arg = {.v = text };
       +        if (text != NULL)
       +                loaduri((Client *) d, &arg);
        }
        
        void
       t@@ -1193,88 +1266,60 @@ reload(Client *c, const Arg *arg)
        }
        
        void
       -scroll_h(Client *c, const Arg *a)
       +print(Client *c, const Arg *a)
        {
       -        evalscript(c, "window.scrollBy(%d * (window.innerWidth / 100), 0)",
       -            a->i);
       +        webkit_print_operation_run_dialog(webkit_print_operation_new(c->view),
       +            GTK_WINDOW(c->win));
        }
        
        void
       -scroll_v(Client *c, const Arg *a)
       +clipboard(Client *c, const Arg *a)
        {
       -        evalscript(c, "window.scrollBy(0, %d * (window.innerHeight / 100))",
       -            a->i);
       +        if (a->b) { /* load clipboard uri */
       +                gtk_clipboard_request_text(gtk_clipboard_get(
       +                                           GDK_SELECTION_PRIMARY),
       +                                           pasteuri, c);
       +        } else { /* copy uri */
       +                gtk_clipboard_set_text(gtk_clipboard_get(
       +                                       GDK_SELECTION_PRIMARY), c->targeturi
       +                                       ? c->targeturi : geturi(c), -1);
       +        }
        }
        
        void
       -setatom(Client *c, int a, const char *v)
       +zoom(Client *c, const Arg *a)
        {
       -        XSync(dpy, False);
       -        XChangeProperty(dpy, c->xid,
       -                        atoms[a], XA_STRING, 8, PropModeReplace,
       -                        (unsigned char *)v, strlen(v) + 1);
       +        if (a->i > 0)
       +                webkit_web_view_set_zoom_level(c->view, zoomlevel + 0.1);
       +        else if (a->i < 0)
       +                webkit_web_view_set_zoom_level(c->view, zoomlevel - 0.1);
       +        else
       +                webkit_web_view_set_zoom_level(c->view, 1.0);
       +
       +        zoomlevel = webkit_web_view_get_zoom_level(c->view);
        }
        
        void
       -setup(void)
       +scroll_v(Client *c, const Arg *a)
        {
       -        int i;
       -
       -        /* clean up any zombies immediately */
       -        sigchld(0);
       -        gtk_init(NULL, NULL);
       -
       -        dpy = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
       -
       -        /* atoms */
       -        atoms[AtomFind] = XInternAtom(dpy, "_SURF_FIND", False);
       -        atoms[AtomGo] = XInternAtom(dpy, "_SURF_GO", False);
       -        atoms[AtomUri] = XInternAtom(dpy, "_SURF_URI", False);
       -
       -        /* dirs and files */
       -        cookiefile = buildfile(cookiefile);
       -        scriptfile = buildfile(scriptfile);
       -        cachedir   = buildpath(cachedir);
       -
       -        if (stylefile == NULL) {
       -                styledir = buildpath(styledir);
       -                for (i = 0; i < LENGTH(styles); i++) {
       -                        if (regcomp(&(styles[i].re), styles[i].regex,
       -                            REG_EXTENDED)) {
       -                                fprintf(stderr,
       -                                        "Could not compile regex: %s\n",
       -                                        styles[i].regex);
       -                                styles[i].regex = NULL;
       -                        }
       -                        styles[i].style = g_strconcat(styledir, "/",
       -                            styles[i].style, NULL);
       -                }
       -                g_free(styledir);
       -        } else {
       -                stylefile = buildfile(stylefile);
       -        }
       +        evalscript(c, "window.scrollBy(0, %d * (window.innerHeight / 100))",
       +            a->i);
        }
        
        void
       -sigchld(int unused)
       +scroll_h(Client *c, const Arg *a)
        {
       -        if (signal(SIGCHLD, sigchld) == SIG_ERR)
       -                die("Can't install SIGCHLD handler");
       -        while (0 < waitpid(-1, NULL, WNOHANG));
       +        evalscript(c, "window.scrollBy(%d * (window.innerWidth / 100), 0)",
       +            a->i);
        }
        
        void
       -spawn(Client *c, const Arg *arg)
       +navigate(Client *c, const Arg *a)
        {
       -        if (fork() == 0) {
       -                if (dpy)
       -                        close(ConnectionNumber(dpy));
       -                setsid();
       -                execvp(((char **)arg->v)[0], (char **)arg->v);
       -                fprintf(stderr, "surf: execvp %s", ((char **)arg->v)[0]);
       -                perror(" failed");
       -                exit(0);
       -        }
       +        if (a->i < 0)
       +                webkit_web_view_go_back(c->view);
       +        else if (a->i > 0)
       +                webkit_web_view_go_forward(c->view);
        }
        
        void
       t@@ -1284,35 +1329,6 @@ stop(Client *c, const Arg *arg)
        }
        
        void
       -titlechanged(WebKitWebView *view, GParamSpec *ps, Client *c)
       -{
       -        c->title = webkit_web_view_get_title(c->view);
       -        updatetitle(c);
       -}
       -
       -gboolean
       -winevent(GtkWidget *w, GdkEvent *e, Client *c)
       -{
       -        switch (e->type) {
       -        case GDK_LEAVE_NOTIFY:
       -                c->targeturi = NULL;
       -                updatetitle(c);
       -                break;
       -        case GDK_WINDOW_STATE: /* fallthrough */
       -                if (e->window_state.changed_mask ==
       -                    GDK_WINDOW_STATE_FULLSCREEN) {
       -                        c->fullscreen = e->window_state.new_window_state &
       -                            GDK_WINDOW_STATE_FULLSCREEN;
       -                        break;
       -                }
       -        default:
       -                return FALSE;
       -        }
       -
       -        return TRUE;
       -}
       -
       -void
        toggle(Client *c, const Arg *a)
        {
                WebKitSettings *s;
       t@@ -1363,6 +1379,16 @@ toggle(Client *c, const Arg *a)
        }
        
        void
       +togglefullscreen(Client *c, const Arg *a)
       +{
       +        /* toggling value is handled in winevent() */
       +        if (c->fullscreen)
       +                gtk_window_unfullscreen(GTK_WINDOW(c->win));
       +        else
       +                gtk_window_fullscreen(GTK_WINDOW(c->win));
       +}
       +
       +void
        togglecookiepolicy(Client *c, const Arg *arg)
        {
                ++cookiepolicy;
       t@@ -1387,80 +1413,53 @@ togglestyle(Client *c, const Arg *arg)
        }
        
        void
       -gettogglestats(Client *c)
       +toggleinspector(Client *c, const Arg *a)
        {
       -        togglestats[0] = cookiepolicy_set(cookiepolicy_get());
       -        togglestats[1] = enablecaretbrowsing ? 'C' : 'c';
       -        togglestats[2] = allowgeolocation ? 'G' : 'g';
       -        togglestats[3] = enablecache ? 'D' : 'd';
       -        togglestats[4] = loadimages ? 'I' : 'i';
       -        togglestats[5] = enablescripts ? 'S': 's';
       -        togglestats[6] = enableplugins ? 'V' : 'v';
       -        togglestats[7] = enablestyle ? 'M' : 'm';
       -        togglestats[8] = enableframeflattening ? 'F' : 'f';
       -        togglestats[9] = '\0';
       +        if (enableinspector) {
       +                if (webkit_web_inspector_is_attached(c->inspector))
       +                        webkit_web_inspector_close(c->inspector);
       +                else
       +                        webkit_web_inspector_show(c->inspector);
       +        }
        }
        
        void
       -getpagestats(Client *c)
       +find(Client *c, const Arg *a)
        {
       -        pagestats[0] = c->tlsflags > G_TLS_CERTIFICATE_VALIDATE_ALL ? '-' :
       -            c->tlsflags > 0 ? 'U' : 'T';
       -        pagestats[1] = '\0';
       -}
       +        const char *s, *f;
        
       -void
       -updatetitle(Client *c)
       -{
       -        char *title;
       +        if (a && a->i) {
       +                if (a->i > 0)
       +                        webkit_find_controller_search_next(c->finder);
       +                else
       +                        webkit_find_controller_search_previous(c->finder);
       +        } else {
       +                s = getatom(c, AtomFind);
       +                f = webkit_find_controller_get_search_text(c->finder);
        
       -        if (showindicators) {
       -                gettogglestats(c);
       -                getpagestats(c);
       +                if (g_strcmp0(f, s) == 0) /* reset search */
       +                        webkit_find_controller_search(c->finder, "", findopts, G_MAXUINT);
        
       -                if (c->progress != 100) {
       -                        title = g_strdup_printf("[%i%%] %s:%s | %s",
       -                            c->progress, togglestats, pagestats,
       -                            c->targeturi ? c->targeturi : c->title);
       -                } else {
       -                        title = g_strdup_printf("%s:%s | %s",
       -                            togglestats, pagestats,
       -                            c->targeturi ? c->targeturi : c->title);
       -                }
       +                webkit_find_controller_search(c->finder, s, findopts, G_MAXUINT);
        
       -                gtk_window_set_title(GTK_WINDOW(c->win), title);
       -                g_free(title);
       -        } else {
       -                gtk_window_set_title(GTK_WINDOW(c->win), c->title ?
       -                    c->title : "");
       +                if (strcmp(s, "") == 0)
       +                        webkit_find_controller_search_finish(c->finder);
                }
        }
        
        void
       -updatewinid(Client *c)
       -{
       -        snprintf(winid, LENGTH(winid), "%lu", c->xid);
       -}
       -
       -void
       -usage(void)
       +clicknavigate(Client *c, const Arg *a, WebKitHitTestResult *h)
        {
       -        die("usage: %s [-bBdDfFgGiIkKmMnNpPsSvx] [-a cookiepolicies ] "
       -            "[-c cookiefile] [-e xid] [-r scriptfile] [-t stylefile] "
       -            "[-u useragent] [-z zoomlevel] [uri]\n", basename(argv0));
       +        navigate(c, a);
        }
        
        void
       -zoom(Client *c, const Arg *a)
       +clicknewwindow(Client *c, const Arg *a, WebKitHitTestResult *h)
        {
       -        if (a->i > 0)
       -                webkit_web_view_set_zoom_level(c->view, zoomlevel + 0.1);
       -        else if (a->i < 0)
       -                webkit_web_view_set_zoom_level(c->view, zoomlevel - 0.1);
       -        else
       -                webkit_web_view_set_zoom_level(c->view, 1.0);
       +        Arg arg;
        
       -        zoomlevel = webkit_web_view_get_zoom_level(c->view);
       +        arg.v = webkit_hit_test_result_get_link_uri(h);
       +        newwindow(c, &arg, a->b);
        }
        
        int
       t@@ -1579,4 +1578,3 @@ main(int argc, char *argv[])
        
                return EXIT_SUCCESS;
        }
       -