tMerge remote-tracking branch 'upstream/surf-webkit2' into master - surf - [fork] 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 00b428177753db5915102ff834801b641c660c11
 (DIR) parent e5f5686d373bff6b9eceec06f2d8e57b729972a6
 (HTM) Author: Anders Damsgaard <anders@adamsgaard.dk>
       Date:   Mon,  2 Nov 2020 08:23:48 +0100
       
       Merge remote-tracking branch 'upstream/surf-webkit2' into master
       
       Diffstat:
         M Makefile                            |      46 ++++++++++++++++----------------
         D common.c                            |      15 ---------------
         M common.h                            |       2 --
         M config.def.h                        |       6 ++++++
         M config.mk                           |       7 ++++---
         D libsurf-webext.c                    |     128 -------------------------------
         M surf.c                              |     111 ++++++++++++++++++++-----------
         A webext-surf.c                       |     106 ++++++++++++++++++++++++++++++
       
       8 files changed, 210 insertions(+), 211 deletions(-)
       ---
 (DIR) diff --git a/Makefile b/Makefile
       t@@ -5,13 +5,12 @@
        include config.mk
        
        SRC = surf.c
       -CSRC = common.c
       -WEBEXTSRC = libsurf-webext.c
       +WSRC = webext-surf.c
        OBJ = $(SRC:.c=.o)
       -COBJ = $(CSRC:.c=.o)
       -WEBEXTOBJ = $(WEBEXTSRC:.c=.o)
       +WOBJ = $(WSRC:.c=.o)
       +WLIB = $(WSRC:.c=.so)
        
       -all: options libsurf-webext.so surf
       +all: options surf $(WLIB)
        
        options:
                @echo surf build options:
       t@@ -23,26 +22,23 @@ options:
        .c.o:
                $(CC) $(SURFCFLAGS) $(CFLAGS) -c $<
        
       +.o.so:
       +        $(CC) -shared -Wl,-soname,$@ $(LDFLAGS) -o $@ $< $(WEBEXTLIBS)
       +
        config.h:
                cp config.def.h $@
        
       -$(OBJ): config.h common.h config.mk
       -$(COBJ): config.h common.h config.mk
       -$(WEBEXTOBJ): config.h common.h config.mk
       -
       -$(WEBEXTOBJ): $(WEBEXTSRC)
       -        $(CC) $(WEBEXTCFLAGS) $(CFLAGS) -c $(WEBEXTSRC)
       +$(OBJ) $(WOBJ): config.h common.h config.mk
        
       -libsurf-webext.so: $(WEBEXTOBJ) $(COBJ)
       -        $(CC) -shared -Wl,-soname,$@ $(LDFLAGS) -o $@ \
       -            $(WEBEXTOBJ) $(COBJ) $(WEBEXTLIBS)
       +surf: $(OBJ)
       +        $(CC) $(SURFLDFLAGS) $(LDFLAGS) -o $@ $(OBJ) $(LIBS)
        
       -surf: $(OBJ) $(COBJ)
       -        $(CC) $(SURFLDFLAGS) $(LDFLAGS) -o $@ $(OBJ) $(COBJ) $(LIBS)
       +$(WOBJ):
       +        $(CC) $(WEBEXTCFLAGS) $(CFLAGS) -c $(@:.o=.c)
        
        clean:
       -        rm -f surf $(OBJ) $(COBJ)
       -        rm -f libsurf-webext.so $(WEBEXTOBJ)
       +        rm -f surf $(OBJ)
       +        rm -f $(WLIB) $(WOBJ)
        
        distclean: clean
                rm -f config.h surf-$(VERSION).tar.gz
       t@@ -51,7 +47,7 @@ dist: distclean
                mkdir -p surf-$(VERSION)
                cp -R LICENSE Makefile config.mk config.def.h README \
                    surf-open.sh arg.h TODO.md surf.png \
       -            surf.1 $(SRC) $(WEBEXTSRC) surf-$(VERSION)
       +            surf.1 $(SRC) $(CSRC) $(WSRC) surf-$(VERSION)
                tar -cf surf-$(VERSION).tar surf-$(VERSION)
                gzip surf-$(VERSION).tar
                rm -rf surf-$(VERSION)
       t@@ -61,8 +57,10 @@ install: all
                cp -f surf $(DESTDIR)$(PREFIX)/bin
                chmod 755 $(DESTDIR)$(PREFIX)/bin/surf
                mkdir -p $(DESTDIR)$(LIBDIR)
       -        cp -f libsurf-webext.so $(DESTDIR)$(LIBDIR)
       -        chmod 644 $(DESTDIR)$(LIBDIR)/libsurf-webext.so
       +        cp -f $(WLIB) $(DESTDIR)$(LIBDIR)
       +        for wlib in $(WLIB); do \
       +            chmod 644 $(DESTDIR)$(LIBDIR)/$$wlib; \
       +        done
                mkdir -p $(DESTDIR)$(MANPREFIX)/man1
                sed "s/VERSION/$(VERSION)/g" < surf.1 > $(DESTDIR)$(MANPREFIX)/man1/surf.1
                chmod 644 $(DESTDIR)$(MANPREFIX)/man1/surf.1
       t@@ -70,8 +68,10 @@ install: all
        uninstall:
                rm -f $(DESTDIR)$(PREFIX)/bin/surf
                rm -f $(DESTDIR)$(MANPREFIX)/man1/surf.1
       -        rm -f $(DESTDIR)$(LIBDIR)/libsurf-webext.so
       +        for wlib in $(WLIB); do \
       +            rm -f $(DESTDIR)$(LIBDIR)/$$wlib; \
       +        done
                - rmdir $(DESTDIR)$(LIBDIR)
        
        .SUFFIXES: .so .o .c
       -.PHONY: all options clean-dist clean dist install uninstall
       +.PHONY: all options distclean clean dist install uninstall
 (DIR) diff --git a/common.c b/common.c
       t@@ -1,15 +0,0 @@
       -#include <stdarg.h>
       -#include <stdio.h>
       -#include <stdlib.h>
       -
       -void
       -die(const char *errstr, ...)
       -{
       -        va_list ap;
       -
       -        va_start(ap, errstr);
       -        vfprintf(stderr, errstr, ap);
       -        va_end(ap);
       -        exit(1);
       -}
       -
 (DIR) diff --git a/common.h b/common.h
       t@@ -1,3 +1 @@
        #define MSGBUFSZ 8
       -
       -void die(char *, ...);
 (DIR) diff --git a/config.def.h b/config.def.h
       t@@ -6,6 +6,11 @@ static char *styledir       = "~/.surf/styles/";
        static char *certdir        = "~/.surf/certificates/";
        static char *cachedir       = "~/.surf/cache/";
        static char *cookiefile     = "~/.surf/cookies.txt";
       +static char **plugindirs    = (char*[]){
       +        "~/.surf/plugins/",
       +        LIBPREFIX "/mozilla/plugins/",
       +        NULL
       +};
        
        /* Webkit default features */
        /* Highest priority value will be used.
       t@@ -24,6 +29,7 @@ static Parameter defconfig[ParameterLast] = {
                [DefaultCharset]      =       { { .v = "UTF-8" }, },
                [DiskCache]           =       { { .i = 1 },     },
                [DNSPrefetch]         =       { { .i = 0 },     },
       +        [Ephemeral]           =       { { .i = 0 },     },
                [FileURLsCrossAccess] =       { { .i = 0 },     },
                [FontSize]            =       { { .i = 12 },    },
                [FrameFlattening]     =       { { .i = 0 },     },
 (DIR) diff --git a/config.mk b/config.mk
       t@@ -22,9 +22,10 @@ INCS = $(X11INC) $(GTKINC)
        LIBS = $(X11LIB) $(GTKLIB) -lgthread-2.0
        
        # flags
       -CPPFLAGS = -DVERSION=\"$(VERSION)\" -DWEBEXTDIR=\"$(LIBDIR)\" \
       -           -D_DEFAULT_SOURCE -DGCR_API_SUBJECT_TO_CHANGE
       -SURFCFLAGS = $(INCS) $(CPPFLAGS) -fPIC
       +CPPFLAGS = -DVERSION=\"$(VERSION)\" -DGCR_API_SUBJECT_TO_CHANGE \
       +           -DLIBPREFIX=\"$(LIBPREFIX)\" -DWEBEXTDIR=\"$(LIBDIR)\" \
       +           -D_DEFAULT_SOURCE
       +SURFCFLAGS = -fPIC $(INCS) $(CPPFLAGS)
        WEBEXTCFLAGS = -fPIC $(WEBEXTINC)
        
        # compiler
 (DIR) diff --git a/libsurf-webext.c b/libsurf-webext.c
       t@@ -1,128 +0,0 @@
       -#include <sys/stat.h>
       -#include <fcntl.h>
       -#include <limits.h>
       -#include <stdlib.h>
       -
       -#include <gio/gio.h>
       -#include <webkit2/webkit-web-extension.h>
       -#include <webkitdom/webkitdom.h>
       -#include <webkitdom/WebKitDOMDOMWindowUnstable.h>
       -
       -#include "common.h"
       -
       -#define LENGTH(x)   (sizeof(x) / sizeof(x[0]))
       -
       -typedef struct Page {
       -        guint64 id;
       -        WebKitWebPage *webpage;
       -        struct Page *next;
       -} Page;
       -
       -static int pipein, pipeout;
       -static Page *pages;
       -
       -Page *
       -newpage(WebKitWebPage *page)
       -{
       -        Page *p;
       -
       -        if (!(p = calloc(1, sizeof(Page))))
       -                die("Cannot malloc!\n");
       -
       -        p->next = pages;
       -        pages = p;
       -
       -        p->id = webkit_web_page_get_id(page);
       -        p->webpage = page;
       -
       -        return p;
       -}
       -
       -static void
       -msgsurf(Page *p, const char *s)
       -{
       -        static char msg[MSGBUFSZ];
       -        size_t sln = strlen(s);
       -        int ret;
       -
       -        if ((ret = snprintf(msg, sizeof(msg), "%c%c%s",
       -                            2 + sln, p ? p->id : 0, s))
       -            >= sizeof(msg)) {
       -                fprintf(stderr, "webext: message too long: %d\n", ret);
       -                return;
       -        }
       -
       -        if (pipeout && write(pipeout, msg, sizeof(msg)) < 0)
       -                fprintf(stderr, "webext: error sending: %.*s\n", ret-2, msg+2);
       -}
       -
       -static gboolean
       -readpipe(GIOChannel *s, GIOCondition c, gpointer unused)
       -{
       -        static char msg[MSGBUFSZ], msgsz;
       -        WebKitDOMDOMWindow *view;
       -        GError *gerr = NULL;
       -        glong wh, ww;
       -        Page *p;
       -
       -        if (g_io_channel_read_chars(s, msg, LENGTH(msg), NULL, &gerr) !=
       -            G_IO_STATUS_NORMAL) {
       -                fprintf(stderr, "webext: error reading pipe: %s\n",
       -                        gerr->message);
       -                g_error_free(gerr);
       -                return TRUE;
       -        }
       -        if ((msgsz = msg[0]) < 3) {
       -                fprintf(stderr, "webext: message too short: %d\n", msgsz);
       -                return TRUE;
       -        }
       -
       -        for (p = pages; p; p = p->next) {
       -                if (p->id == msg[1])
       -                        break;
       -        }
       -        if (!p || !(view = webkit_dom_document_get_default_view(
       -                    webkit_web_page_get_dom_document(p->webpage))))
       -                return TRUE;
       -
       -        switch (msg[2]) {
       -        case 'h':
       -                if (msgsz != 4)
       -                        return TRUE;
       -                ww = webkit_dom_dom_window_get_inner_width(view);
       -                webkit_dom_dom_window_scroll_by(view,
       -                                                (ww / 100) * msg[3], 0);
       -                break;
       -        case 'v':
       -                if (msgsz != 4)
       -                        return TRUE;
       -                wh = webkit_dom_dom_window_get_inner_height(view);
       -                webkit_dom_dom_window_scroll_by(view,
       -                                                0, (wh / 100) * msg[3]);
       -                break;
       -        }
       -
       -        return TRUE;
       -}
       -
       -static void
       -webpagecreated(WebKitWebExtension *e, WebKitWebPage *wp, gpointer unused)
       -{
       -        Page *p = newpage(wp);
       -}
       -
       -G_MODULE_EXPORT void
       -webkit_web_extension_initialize_with_user_data(WebKitWebExtension *e, GVariant *gv)
       -{
       -        GIOChannel *gchanpipe;
       -
       -        g_signal_connect(e, "page-created", G_CALLBACK(webpagecreated), NULL);
       -
       -        g_variant_get(gv, "(ii)", &pipein, &pipeout);
       -        msgsurf(NULL, "i");
       -
       -        gchanpipe = g_io_channel_unix_new(pipein);
       -        g_io_channel_set_encoding(gchanpipe, NULL, NULL);
       -        g_io_channel_set_close_on_unref(gchanpipe, TRUE);
       -        g_io_add_watch(gchanpipe, G_IO_IN, readpipe, NULL);
       -}
 (DIR) diff --git a/surf.c b/surf.c
       t@@ -3,9 +3,11 @@
         * To understand surf, start reading main().
         */
        #include <sys/file.h>
       +#include <sys/socket.h>
        #include <sys/types.h>
        #include <sys/wait.h>
        #include <glib.h>
       +#include <inttypes.h>
        #include <libgen.h>
        #include <limits.h>
        #include <pwd.h>
       t@@ -62,6 +64,7 @@ typedef enum {
                DiskCache,
                DefaultCharset,
                DNSPrefetch,
       +        Ephemeral,
                FileURLsCrossAccess,
                FontSize,
                FrameFlattening,
       t@@ -109,7 +112,7 @@ typedef struct Client {
                GTlsCertificate *cert, *failedcert;
                GTlsCertificateFlags tlserr;
                Window xid;
       -        unsigned long pageid;
       +        guint64 pageid;
                int progress, fullscreen, https, insecure, errorpage;
                const char *title, *overtitle, *targeturi;
                const char *needle;
       t@@ -145,6 +148,7 @@ typedef struct {
        } SiteSpecific;
        
        /* Surf */
       +static void die(const char *errstr, ...);
        static void usage(void);
        static void setup(void);
        static void sigchld(int unused);
       t@@ -189,7 +193,7 @@ static gboolean buttonreleased(GtkWidget *w, GdkEvent *e, Client *c);
        static GdkFilterReturn processx(GdkXEvent *xevent, GdkEvent *event,
                                        gpointer d);
        static gboolean winevent(GtkWidget *w, GdkEvent *e, Client *c);
       -static gboolean readpipe(GIOChannel *s, GIOCondition ioc, gpointer unused);
       +static gboolean readsock(GIOChannel *s, GIOCondition ioc, gpointer unused);
        static void showview(WebKitWebView *v, Client *c);
        static GtkWidget *createwindow(Client *c);
        static gboolean loadfailedtls(WebKitWebView *v, gchar *uri,
       t@@ -255,7 +259,7 @@ static char *stylefile;
        static const char *useragent;
        static Parameter *curconfig;
        static int modparams[ParameterLast];
       -static int pipein[2], pipeout[2];
       +static int spair[2];
        char *argv0;
        
        static ParamName loadtransient[] = {
       t@@ -306,6 +310,17 @@ static ParamName loadfinished[] = {
        #include "config.h"
        
        void
       +die(const char *errstr, ...)
       +{
       +       va_list ap;
       +
       +       va_start(ap, errstr);
       +       vfprintf(stderr, errstr, ap);
       +       va_end(ap);
       +       exit(1);
       +}
       +
       +void
        usage(void)
        {
                die("usage: surf [-bBdDfFgGiIkKmMnNpPsStTvwxX]\n"
       t@@ -342,18 +357,24 @@ setup(void)
                /* dirs and files */
                cookiefile = buildfile(cookiefile);
                scriptfile = buildfile(scriptfile);
       -        cachedir   = buildpath(cachedir);
                certdir    = buildpath(certdir);
       +        if (curconfig[Ephemeral].val.i)
       +                cachedir = NULL;
       +        else
       +                cachedir   = buildpath(cachedir);
        
                gdkkb = gdk_seat_get_keyboard(gdk_display_get_default_seat(gdpy));
        
       -        if (pipe(pipeout) < 0 || pipe(pipein) < 0) {
       -                fputs("Unable to create pipes\n", stderr);
       +        if (socketpair(AF_UNIX, SOCK_DGRAM, 0, spair) < 0) {
       +                fputs("Unable to create sockets\n", stderr);
       +                spair[0] = spair[1] = -1;
                } else {
       -                gchanin = g_io_channel_unix_new(pipein[0]);
       +                gchanin = g_io_channel_unix_new(spair[0]);
                        g_io_channel_set_encoding(gchanin, NULL, NULL);
       +                g_io_channel_set_flags(gchanin, g_io_channel_get_flags(gchanin)
       +                                       | G_IO_FLAG_NONBLOCK, NULL);
                        g_io_channel_set_close_on_unref(gchanin, TRUE);
       -                g_io_add_watch(gchanin, G_IO_IN, readpipe, NULL);
       +                g_io_add_watch(gchanin, G_IO_IN, readsock, NULL);
                }
        
        
       t@@ -1042,8 +1063,8 @@ spawn(Client *c, const Arg *a)
                if (fork() == 0) {
                        if (dpy)
                                close(ConnectionNumber(dpy));
       -                close(pipein[0]);
       -                close(pipeout[1]);
       +                close(spair[0]);
       +                close(spair[1]);
                        setsid();
                        execvp(((char **)a->v)[0], (char **)a->v);
                        fprintf(stderr, "%s: execvp %s", argv0, ((char **)a->v)[0]);
       t@@ -1077,8 +1098,8 @@ cleanup(void)
                while (clients)
                        destroyclient(clients);
        
       -        close(pipein[0]);
       -        close(pipeout[1]);
       +        close(spair[0]);
       +        close(spair[1]);
                g_free(cookiefile);
                g_free(scriptfile);
                g_free(stylefile);
       t@@ -1133,11 +1154,16 @@ newview(Client *c, WebKitWebView *rv)
        
                        contentmanager = webkit_user_content_manager_new();
        
       -                context = webkit_web_context_new_with_website_data_manager(
       -                          webkit_website_data_manager_new(
       -                          "base-cache-directory", cachedir,
       -                          "base-data-directory", cachedir,
       -                          NULL));
       +                if (curconfig[Ephemeral].val.i) {
       +                        context = webkit_web_context_new_ephemeral();
       +                } else {
       +                        context = webkit_web_context_new_with_website_data_manager(
       +                                  webkit_website_data_manager_new(
       +                                  "base-cache-directory", cachedir,
       +                                  "base-data-directory", cachedir,
       +                                  NULL));
       +                }
       +
        
                        cookiemanager = webkit_web_context_get_cookie_manager(context);
        
       t@@ -1153,10 +1179,15 @@ newview(Client *c, WebKitWebView *rv)
                        webkit_web_context_set_cache_model(context,
                            curconfig[DiskCache].val.i ? WEBKIT_CACHE_MODEL_WEB_BROWSER :
                            WEBKIT_CACHE_MODEL_DOCUMENT_VIEWER);
       +                /* plugins directories */
       +                for (; *plugindirs; ++plugindirs)
       +                        webkit_web_context_set_additional_plugins_directory(
       +                            context, *plugindirs);
        
                        /* Currently only works with text file to be compatible with curl */
       -                webkit_cookie_manager_set_persistent_storage(cookiemanager,
       -                    cookiefile, WEBKIT_COOKIE_PERSISTENT_STORAGE_TEXT);
       +                if (!curconfig[Ephemeral].val.i)
       +                        webkit_cookie_manager_set_persistent_storage(cookiemanager,
       +                            cookiefile, WEBKIT_COOKIE_PERSISTENT_STORAGE_TEXT);
                        /* cookie policy */
                        webkit_cookie_manager_set_accept_policy(cookiemanager,
                            cookiepolicy_get());
       t@@ -1211,30 +1242,26 @@ newview(Client *c, WebKitWebView *rv)
        }
        
        static gboolean
       -readpipe(GIOChannel *s, GIOCondition ioc, gpointer unused)
       +readsock(GIOChannel *s, GIOCondition ioc, gpointer unused)
        {
       -        static char msg[MSGBUFSZ], msgsz;
       +        static char msg[MSGBUFSZ];
                GError *gerr = NULL;
       +        gsize msgsz;
        
       -        if (g_io_channel_read_chars(s, msg, sizeof(msg), NULL, &gerr) !=
       +        if (g_io_channel_read_chars(s, msg, sizeof(msg), &msgsz, &gerr) !=
                    G_IO_STATUS_NORMAL) {
       -                fprintf(stderr, "surf: error reading pipe: %s\n",
       -                        gerr->message);
       -                g_error_free(gerr);
       +                if (gerr) {
       +                        fprintf(stderr, "surf: error reading socket: %s\n",
       +                                gerr->message);
       +                        g_error_free(gerr);
       +                }
                        return TRUE;
                }
       -        if ((msgsz = msg[0]) < 3) {
       +        if (msgsz < 2) {
                        fprintf(stderr, "surf: message too short: %d\n", msgsz);
                        return TRUE;
                }
        
       -        switch (msg[2]) {
       -        case 'i':
       -                close(pipein[1]);
       -                close(pipeout[0]);
       -                break;
       -        }
       -
                return TRUE;
        }
        
       t@@ -1243,10 +1270,10 @@ initwebextensions(WebKitWebContext *wc, Client *c)
        {
                GVariant *gv;
        
       -        if (!pipeout[0] || !pipein[1])
       +        if (spair[1] < 0)
                        return;
        
       -        gv = g_variant_new("(ii)", pipeout[0], pipein[1]);
       +        gv = g_variant_new("i", spair[1]);
        
                webkit_web_context_set_web_extensions_initialization_user_data(wc, gv);
                webkit_web_context_set_web_extensions_directory(wc, WEBEXTDIR);
       t@@ -1425,7 +1452,7 @@ createwindow(Client *c)
                        gtk_window_set_wmclass(GTK_WINDOW(w), wmstr, "Surf");
                        g_free(wmstr);
        
       -                wmstr = g_strdup_printf("%s[%lu]", "Surf", c->pageid);
       +                wmstr = g_strdup_printf("%s[%"PRIu64"]", "Surf", c->pageid);
                        gtk_window_set_role(GTK_WINDOW(w), wmstr);
                        g_free(wmstr);
        
       t@@ -1518,6 +1545,7 @@ loadchanged(WebKitWebView *v, WebKitLoadEvent e, Client *c)
                        break;
                case WEBKIT_LOAD_COMMITTED:
                        setatom(c, AtomUri, uri);
       +                c->title = uri;
                        seturiparameters(c, uri, loadcommitted);
                        c->https = webkit_web_view_get_tls_info(c->view, &c->cert,
                                                                &c->tlserr);
       t@@ -1853,15 +1881,18 @@ msgext(Client *c, char type, const Arg *a)
                static char msg[MSGBUFSZ];
                int ret;
        
       -        if ((ret = snprintf(msg, sizeof(msg), "%c%c%c%c",
       -                            4, c->pageid, type, a->i))
       +        if (spair[0] < 0)
       +                return;
       +
       +        if ((ret = snprintf(msg, sizeof(msg), "%c%c%c", c->pageid, type, a->i))
                    >= sizeof(msg)) {
                        fprintf(stderr, "surf: message too long: %d\n", ret);
                        return;
                }
        
       -        if (pipeout[1] && write(pipeout[1], msg, sizeof(msg)) < 0)
       -                fprintf(stderr, "surf: error sending: %.*s\n", ret-2, msg+2);
       +        if (send(spair[0], msg, ret, 0) != ret)
       +                fprintf(stderr, "surf: error sending: %u%c%d (%d)\n",
       +                        c->pageid, type, a->i, ret);
        }
        
        void
 (DIR) diff --git a/webext-surf.c b/webext-surf.c
       t@@ -0,0 +1,106 @@
       +#include <sys/socket.h>
       +#include <sys/stat.h>
       +#include <fcntl.h>
       +#include <inttypes.h>
       +#include <limits.h>
       +#include <stdio.h>
       +#include <stdlib.h>
       +
       +#include <gio/gio.h>
       +#include <webkit2/webkit-web-extension.h>
       +#include <webkitdom/webkitdom.h>
       +#include <webkitdom/WebKitDOMDOMWindowUnstable.h>
       +
       +#include "common.h"
       +
       +#define LENGTH(x)   (sizeof(x) / sizeof(x[0]))
       +
       +static WebKitWebExtension *webext;
       +static int sock;
       +
       +static void
       +msgsurf(guint64 pageid, const char *s)
       +{
       +        static char msg[MSGBUFSZ];
       +        size_t sln = strlen(s);
       +        int ret;
       +
       +        if ((ret = snprintf(msg, sizeof(msg), "%c%s", pageid, s))
       +            >= sizeof(msg)) {
       +                fprintf(stderr, "webext: msg: message too long: %d\n", ret);
       +                return;
       +        }
       +
       +        if (send(sock, msg, ret, 0) < 0)
       +                fprintf(stderr, "webext: error sending: %s\n", msg+1);
       +}
       +
       +static gboolean
       +readsock(GIOChannel *s, GIOCondition c, gpointer unused)
       +{
       +        static char js[48], msg[MSGBUFSZ];
       +        WebKitWebPage *page;
       +        JSCContext *jsc;
       +        GError *gerr = NULL;
       +        gsize msgsz;
       +
       +        if (g_io_channel_read_chars(s, msg, sizeof(msg), &msgsz, &gerr) !=
       +            G_IO_STATUS_NORMAL) {
       +                if (gerr) {
       +                        fprintf(stderr, "webext: error reading socket: %s\n",
       +                                gerr->message);
       +                        g_error_free(gerr);
       +                }
       +                return TRUE;
       +        }
       +
       +        if (msgsz < 2) {
       +                fprintf(stderr, "webext: readsock: message too short: %d\n",
       +                        msgsz);
       +                return TRUE;
       +        }
       +
       +        if (!(page = webkit_web_extension_get_page(webext, msg[0])))
       +                return TRUE;
       +
       +        jsc = webkit_frame_get_js_context(webkit_web_page_get_main_frame(page));
       +
       +        switch (msg[1]) {
       +        case 'h':
       +                if (msgsz != 3)
       +                        return TRUE;
       +                snprintf(js, sizeof(js),
       +                         "window.scrollBy(window.innerWidth/100*%d,0);",
       +                         msg[2]);
       +                jsc_context_evaluate(jsc, js, -1);
       +                break;
       +        case 'v':
       +                if (msgsz != 3)
       +                        return TRUE;
       +                snprintf(js, sizeof(js),
       +                         "window.scrollBy(0,window.innerHeight/100*%d);",
       +                         msg[2]);
       +                jsc_context_evaluate(jsc, js, -1);
       +                break;
       +        }
       +
       +        return TRUE;
       +}
       +
       +G_MODULE_EXPORT void
       +webkit_web_extension_initialize_with_user_data(WebKitWebExtension *e,
       +                                               const GVariant *gv)
       +{
       +        GIOChannel *gchansock;
       +
       +        webext = e;
       +
       +        g_variant_get(gv, "i", &sock);
       +
       +        gchansock = g_io_channel_unix_new(sock);
       +        g_io_channel_set_encoding(gchansock, NULL, NULL);
       +        g_io_channel_set_flags(gchansock, g_io_channel_get_flags(gchansock)
       +                               | G_IO_FLAG_NONBLOCK, NULL);
       +        g_io_channel_set_close_on_unref(gchansock, TRUE);
       +        g_io_add_watch(gchansock, G_IO_IN, readsock, NULL);
       +}