Communicate with webextension via a pipe

This commit is contained in:
Quentin Rameau 2015-12-07 15:50:00 +01:00
parent 1bd6d20102
commit 7ea0c2f7f8
4 changed files with 213 additions and 47 deletions

View File

@ -29,7 +29,7 @@ options:
@$(LIBTOOL) --mode compile --tag CC $(CC) $(LIBCFLAGS) -c $< @$(LIBTOOL) --mode compile --tag CC $(CC) $(LIBCFLAGS) -c $<
$(OBJ): config.h config.mk $(OBJ): config.h config.mk
$(LIBOBJ): config.mk $(LIBOBJ): config.h config.mk
config.h: config.h:
@echo creating $@ from config.def.h @echo creating $@ from config.def.h

View File

@ -143,15 +143,13 @@ static Key keys[] = {
{ MODKEY, GDK_KEY_l, navigate, { .i = +1 } }, { MODKEY, GDK_KEY_l, navigate, { .i = +1 } },
{ MODKEY, GDK_KEY_h, navigate, { .i = -1 } }, { MODKEY, GDK_KEY_h, navigate, { .i = -1 } },
/* Currently we have to use scrolling steps that WebKit2GTK+ gives us /* vertical and horizontal scrolling, in viewport percentage */
* d: step down, u: step up, r: step right, l:step left { MODKEY, GDK_KEY_j, scrollv, { .i = +10 } },
* D: page down, U: page up */ { MODKEY, GDK_KEY_k, scrollv, { .i = -10 } },
{ MODKEY, GDK_KEY_j, scroll, { .i = 'd' } }, { MODKEY, GDK_KEY_b, scrollv, { .i = +50 } },
{ MODKEY, GDK_KEY_k, scroll, { .i = 'u' } }, { MODKEY, GDK_KEY_space, scrollv, { .i = -50 } },
{ MODKEY, GDK_KEY_b, scroll, { .i = 'U' } }, { MODKEY, GDK_KEY_i, scrollh, { .i = +10 } },
{ MODKEY, GDK_KEY_space, scroll, { .i = 'D' } }, { MODKEY, GDK_KEY_u, scrollh, { .i = -10 } },
{ MODKEY, GDK_KEY_i, scroll, { .i = 'r' } },
{ MODKEY, GDK_KEY_u, scroll, { .i = 'l' } },
{ MODKEY|GDK_SHIFT_MASK, GDK_KEY_j, zoom, { .i = -1 } }, { MODKEY|GDK_SHIFT_MASK, GDK_KEY_j, zoom, { .i = -1 } },

View File

@ -1,7 +1,131 @@
#include <webkit2/webkit-web-extension.h> #include <sys/stat.h>
#include <fcntl.h>
#include <limits.h>
#include <stdlib.h>
G_MODULE_EXPORT void #include <gio/gio.h>
webkit_web_extension_initialize(WebKitWebExtension *e) #include <webkit2/webkit-web-extension.h>
#include <webkitdom/webkitdom.h>
#include <webkitdom/WebKitDOMDOMWindowUnstable.h>
#define LENGTH(x) (sizeof(x) / sizeof(x[0]))
#define MSGBUFSZ 32
typedef struct Page {
guint64 id;
WebKitWebPage *webpage;
WebKitDOMDOMWindow *view;
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)
{
char msg[MSGBUFSZ];
int ret;
msg[0] = p ? p->id : 0;
ret = snprintf(&msg[1], sizeof(msg) - 1, "%s", s);
if (ret >= sizeof(msg)) {
fprintf(stderr, "webext: message too long: %d\n", ret);
return; return;
} }
if (pipeout) {
if (write(pipeout, msg, sizeof(msg)) < 0)
fprintf(stderr, "webext: error sending: %s\n", msg);
}
}
static gboolean
readpipe(GIOChannel *s, GIOCondition c, gpointer unused)
{
char msg[MSGBUFSZ];
gsize msgsz;
GError *gerr = NULL;
glong wh, ww;
Page *p;
if (g_io_channel_read_chars(s, msg, LENGTH(msg), &msgsz, &gerr) !=
G_IO_STATUS_NORMAL) {
fprintf(stderr, "webext: error reading pipe: %s\n",
gerr->message);
g_error_free(gerr);
return TRUE;
}
msg[msgsz] = '\0';
for (p = pages; p; p = p->next) {
if (p->id == msg[0])
break;
}
if (!p || !p->view)
return TRUE;
switch (msg[1]) {
case 'h':
ww = webkit_dom_dom_window_get_inner_width(p->view);
webkit_dom_dom_window_scroll_by(p->view,
(ww / 100) * msg[2], 0);
break;
case 'v':
wh = webkit_dom_dom_window_get_inner_height(p->view);
webkit_dom_dom_window_scroll_by(p->view,
0, (wh / 100) * msg[2]);
break;
}
return TRUE;
}
static void
documentloaded(WebKitWebPage *wp, Page *p)
{
p->view = webkit_dom_document_get_default_view(
webkit_web_page_get_dom_document(wp));
}
static void
webpagecreated(WebKitWebExtension *e, WebKitWebPage *wp, gpointer unused)
{
Page *p = newpage(wp);
g_signal_connect(wp, "document-loaded", G_CALLBACK(documentloaded), p);
}
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);
}

114
surf.c
View File

@ -11,7 +11,6 @@
#include <pwd.h> #include <pwd.h>
#include <regex.h> #include <regex.h>
#include <signal.h> #include <signal.h>
#include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -28,11 +27,13 @@
#include <webkit2/webkit2.h> #include <webkit2/webkit2.h>
#include <X11/X.h> #include <X11/X.h>
#include <X11/Xatom.h> #include <X11/Xatom.h>
#include <glib.h>
#include "arg.h" #include "arg.h"
#define LENGTH(x) (sizeof(x) / sizeof(x[0])) #define LENGTH(x) (sizeof(x) / sizeof(x[0]))
#define CLEANMASK(mask) (mask & (MODKEY|GDK_SHIFT_MASK)) #define CLEANMASK(mask) (mask & (MODKEY|GDK_SHIFT_MASK))
#define MSGBUFSZ 32
enum { AtomFind, AtomGo, AtomUri, AtomLast }; enum { AtomFind, AtomGo, AtomUri, AtomLast };
@ -104,6 +105,7 @@ typedef struct Client {
GTlsCertificate *cert, *failedcert; GTlsCertificate *cert, *failedcert;
GTlsCertificateFlags tlserr; GTlsCertificateFlags tlserr;
Window xid; Window xid;
unsigned long pageid;
int progress, fullscreen, https, insecure, errorpage; int progress, fullscreen, https, insecure, errorpage;
const char *title, *overtitle, *targeturi; const char *title, *overtitle, *targeturi;
const char *needle; const char *needle;
@ -171,6 +173,7 @@ static void updatewinid(Client *c);
static void handleplumb(Client *c, const char *uri); static void handleplumb(Client *c, const char *uri);
static void newwindow(Client *c, const Arg *a, int noembed); static void newwindow(Client *c, const Arg *a, int noembed);
static void spawn(Client *c, const Arg *a); static void spawn(Client *c, const Arg *a);
static void msgext(Client *c, char type, const Arg *a);
static void destroyclient(Client *c); static void destroyclient(Client *c);
static void cleanup(void); static void cleanup(void);
@ -183,6 +186,7 @@ static gboolean buttonreleased(GtkWidget *w, GdkEvent *e, Client *c);
static GdkFilterReturn processx(GdkXEvent *xevent, GdkEvent *event, static GdkFilterReturn processx(GdkXEvent *xevent, GdkEvent *event,
gpointer d); gpointer d);
static gboolean winevent(GtkWidget *w, GdkEvent *e, Client *c); static gboolean winevent(GtkWidget *w, GdkEvent *e, Client *c);
static gboolean readpipe(GIOChannel *s, GIOCondition ioc, gpointer unused);
static void showview(WebKitWebView *v, Client *c); static void showview(WebKitWebView *v, Client *c);
static GtkWidget *createwindow(Client *c); static GtkWidget *createwindow(Client *c);
static gboolean loadfailedtls(WebKitWebView *v, gchar *uri, static gboolean loadfailedtls(WebKitWebView *v, gchar *uri,
@ -219,7 +223,8 @@ static void print(Client *c, const Arg *a);
static void showcert(Client *c, const Arg *a); static void showcert(Client *c, const Arg *a);
static void clipboard(Client *c, const Arg *a); static void clipboard(Client *c, const Arg *a);
static void zoom(Client *c, const Arg *a); static void zoom(Client *c, const Arg *a);
static void scroll(Client *c, const Arg *a); static void scrollv(Client *c, const Arg *a);
static void scrollh(Client *c, const Arg *a);
static void navigate(Client *c, const Arg *a); static void navigate(Client *c, const Arg *a);
static void stop(Client *c, const Arg *a); static void stop(Client *c, const Arg *a);
static void toggle(Client *c, const Arg *a); static void toggle(Client *c, const Arg *a);
@ -247,6 +252,7 @@ static char *stylefile;
static const char *useragent; static const char *useragent;
static Parameter *curconfig; static Parameter *curconfig;
static int modparams[ParameterLast]; static int modparams[ParameterLast];
static int pipein[2], pipeout[2];
char *argv0; char *argv0;
static ParamName loadtransient[] = { static ParamName loadtransient[] = {
@ -318,6 +324,7 @@ die(const char *errstr, ...)
void void
setup(void) setup(void)
{ {
GIOChannel *gchanin;
GdkDisplay *gdpy; GdkDisplay *gdpy;
int i, j; int i, j;
@ -348,6 +355,16 @@ setup(void)
gdkkb = gdk_seat_get_keyboard(gdk_display_get_default_seat(gdpy)); 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);
} else {
gchanin = g_io_channel_unix_new(pipein[0]);
g_io_channel_set_encoding(gchanin, NULL, NULL);
g_io_channel_set_close_on_unref(gchanin, TRUE);
g_io_add_watch(gchanin, G_IO_IN, readpipe, NULL);
}
for (i = 0; i < LENGTH(certs); ++i) { for (i = 0; i < LENGTH(certs); ++i) {
if (!regcomp(&(certs[i].re), certs[i].regex, REG_EXTENDED)) { if (!regcomp(&(certs[i].re), certs[i].regex, REG_EXTENDED)) {
certs[i].file = g_strconcat(certdir, "/", certs[i].file, certs[i].file = g_strconcat(certdir, "/", certs[i].file,
@ -1033,6 +1050,8 @@ spawn(Client *c, const Arg *a)
if (fork() == 0) { if (fork() == 0) {
if (dpy) if (dpy)
close(ConnectionNumber(dpy)); close(ConnectionNumber(dpy));
close(pipein[0]);
close(pipeout[1]);
setsid(); setsid();
execvp(((char **)a->v)[0], (char **)a->v); execvp(((char **)a->v)[0], (char **)a->v);
fprintf(stderr, "%s: execvp %s", argv0, ((char **)a->v)[0]); fprintf(stderr, "%s: execvp %s", argv0, ((char **)a->v)[0]);
@ -1065,6 +1084,9 @@ cleanup(void)
{ {
while (clients) while (clients)
destroyclient(clients); destroyclient(clients);
close(pipein[0]);
close(pipeout[1]);
g_free(cookiefile); g_free(cookiefile);
g_free(scriptfile); g_free(scriptfile);
g_free(stylefile); g_free(stylefile);
@ -1077,14 +1099,13 @@ newview(Client *c, WebKitWebView *rv)
{ {
WebKitWebView *v; WebKitWebView *v;
WebKitSettings *settings; WebKitSettings *settings;
WebKitUserContentManager *contentmanager;
WebKitWebContext *context; WebKitWebContext *context;
WebKitCookieManager *cookiemanager; WebKitCookieManager *cookiemanager;
WebKitUserContentManager *contentmanager;
/* Webview */ /* Webview */
if (rv) { if (rv) {
v = WEBKIT_WEB_VIEW( v = WEBKIT_WEB_VIEW(webkit_web_view_new_with_related_view(rv));
webkit_web_view_new_with_related_view(rv));
} else { } else {
settings = webkit_settings_new_with_settings( settings = webkit_settings_new_with_settings(
"allow-file-access-from-file-urls", curconfig[FileURLsCrossAccess].val.i, "allow-file-access-from-file-urls", curconfig[FileURLsCrossAccess].val.i,
@ -1197,9 +1218,43 @@ newview(Client *c, WebKitWebView *rv)
return v; return v;
} }
static gboolean
readpipe(GIOChannel *s, GIOCondition ioc, gpointer unused)
{
char msg[MSGBUFSZ];
gsize msgsz;
GError *gerr = NULL;
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);
return TRUE;
}
msg[msgsz] = '\0';
switch (msg[1]) {
case 'i':
close(pipein[1]);
close(pipeout[0]);
break;
}
return TRUE;
}
void void
initwebextensions(WebKitWebContext *wc, Client *c) initwebextensions(WebKitWebContext *wc, Client *c)
{ {
GVariant *gv;
if (!pipeout[0] || !pipein[1])
return;
gv = g_variant_new("(ii)", pipeout[0], pipein[1]);
webkit_web_context_set_web_extensions_initialization_user_data(wc, gv);
webkit_web_context_set_web_extensions_directory(wc, WEBEXTDIR); webkit_web_context_set_web_extensions_directory(wc, WEBEXTDIR);
} }
@ -1326,6 +1381,7 @@ showview(WebKitWebView *v, Client *c)
c->finder = webkit_web_view_get_find_controller(c->view); c->finder = webkit_web_view_get_find_controller(c->view);
c->inspector = webkit_web_view_get_inspector(c->view); c->inspector = webkit_web_view_get_inspector(c->view);
c->pageid = webkit_web_view_get_page_id(c->view);
c->win = createwindow(c); c->win = createwindow(c);
gtk_container_add(GTK_CONTAINER(c->win), GTK_WIDGET(c->view)); gtk_container_add(GTK_CONTAINER(c->win), GTK_WIDGET(c->view));
@ -1375,8 +1431,7 @@ createwindow(Client *c)
gtk_window_set_wmclass(GTK_WINDOW(w), wmstr, "Surf"); gtk_window_set_wmclass(GTK_WINDOW(w), wmstr, "Surf");
g_free(wmstr); g_free(wmstr);
wmstr = g_strdup_printf("%s[%lu]", "Surf", wmstr = g_strdup_printf("%s[%lu]", "Surf", c->pageid);
webkit_web_view_get_page_id(c->view));
gtk_window_set_role(GTK_WINDOW(w), wmstr); gtk_window_set_role(GTK_WINDOW(w), wmstr);
g_free(wmstr); g_free(wmstr);
@ -1797,38 +1852,27 @@ zoom(Client *c, const Arg *a)
curconfig[ZoomLevel].val.f = webkit_web_view_get_zoom_level(c->view); curconfig[ZoomLevel].val.f = webkit_web_view_get_zoom_level(c->view);
} }
void static void
scroll(Client *c, const Arg *a) msgext(Client *c, char type, const Arg *a)
{ {
GdkEvent *ev = gdk_event_new(GDK_KEY_PRESS); char msg[MSGBUFSZ] = { c->pageid, type, a->i, '\0' };
gdk_event_set_device(ev, gdkkb); if (pipeout[1]) {
ev->key.window = gtk_widget_get_window(GTK_WIDGET(c->win)); if (write(pipeout[1], msg, sizeof(msg)) < 0)
ev->key.state = GDK_CONTROL_MASK; fprintf(stderr, "surf: error sending: %s\n", msg);
ev->key.time = GDK_CURRENT_TIME; }
switch (a->i) {
case 'd':
ev->key.keyval = GDK_KEY_Down;
break;
case 'D':
ev->key.keyval = GDK_KEY_Page_Down;
break;
case 'l':
ev->key.keyval = GDK_KEY_Left;
break;
case 'r':
ev->key.keyval = GDK_KEY_Right;
break;
case 'U':
ev->key.keyval = GDK_KEY_Page_Up;
break;
case 'u':
ev->key.keyval = GDK_KEY_Up;
break;
} }
gdk_event_put(ev); void
scrollv(Client *c, const Arg *a)
{
msgext(c, 'v', a);
}
void
scrollh(Client *c, const Arg *a)
{
msgext(c, 'h', a);
} }
void void