X-Git-Url: https://git.danieliu.xyz/?a=blobdiff_plain;f=surf.c;h=906d1452ae6fa84c6cf597f5bc347753d39ed903;hb=780cca964ca0cf622746ad6e8cae8beb8047d1df;hp=682c1e299c38043f9381b31841dd98c83a5d5725;hpb=0415175e10880585c765613af0bd43e02de87116;p=surf.git diff --git a/surf.c b/surf.c index 682c1e2..906d145 100644 --- a/surf.c +++ b/surf.c @@ -2,6 +2,7 @@ * * To understand surf, start reading main(). */ + #include #include #include @@ -42,22 +43,16 @@ union Arg { }; typedef struct Client { - GtkWidget *win, *scroll, *vbox, *indicator; + GtkWidget *win, *scroll, *vbox, *pane; WebKitWebView *view; WebKitWebInspector *inspector; char *title, *linkhover; - const char *uri, *needle; + const char *needle; gint progress; struct Client *next; - gboolean zoomed, fullscreen, isinspector, sslfailed; + gboolean zoomed, fullscreen, isinspecting, sslfailed; } Client; -typedef struct { - char *label; - void (*func)(Client *c, const Arg *arg); - const Arg arg; -} Item; - typedef struct { guint mod; guint keyval; @@ -83,8 +78,12 @@ static GdkNativeWindow embed = 0; static gboolean showxid = FALSE; static char winid[64]; static gboolean usingproxy = 0; -static char togglestat[5]; +static char togglestat[8]; +static char pagestat[3]; +static GTlsDatabase *tlsdb; +static int policysel = 0; +static void addaccelgroup(Client *c); static void beforerequest(WebKitWebView *w, WebKitWebFrame *f, WebKitWebResource *r, WebKitNetworkRequest *req, WebKitNetworkResponse *resp, gpointer d); @@ -94,12 +93,16 @@ static gboolean buttonrelease(WebKitWebView *web, GdkEventButton *e, static void cleanup(void); static void clipboard(Client *c, const Arg *arg); +/* Cookiejar implementation */ static void cookiejar_changed(SoupCookieJar *self, SoupCookie *old_cookie, SoupCookie *new_cookie); static void cookiejar_finalize(GObject *self); -static SoupCookieJar *cookiejar_new(const char *filename, gboolean read_only); +static SoupCookieJarAcceptPolicy cookiepolicy_get(void); +static SoupCookieJar *cookiejar_new(const char *filename, gboolean read_only, + SoupCookieJarAcceptPolicy policy); static void cookiejar_set_property(GObject *self, guint prop_id, const GValue *value, GParamSpec *pspec); +static char cookiepolicy_set(const SoupCookieJarAcceptPolicy p); static char *copystr(char **str, const char *src); static WebKitWebView *createwindow(WebKitWebView *v, WebKitWebFrame *f, @@ -110,16 +113,19 @@ static gboolean decidedownload(WebKitWebView *v, WebKitWebFrame *f, static gboolean decidewindow(WebKitWebView *v, WebKitWebFrame *f, WebKitNetworkRequest *r, WebKitWebNavigationAction *n, WebKitWebPolicyDecision *p, Client *c); +static gboolean deletion_interface(WebKitWebView *view, + WebKitDOMHTMLElement *arg1, Client *c); static void destroyclient(Client *c); static void destroywin(GtkWidget* w, Client *c); static void die(const char *errstr, ...); -static void drawindicator(Client *c); static void eval(Client *c, const Arg *arg); -static gboolean exposeindicator(GtkWidget *w, GdkEventExpose *e, Client *c); static void find(Client *c, const Arg *arg); static void fullscreen(Client *c, const Arg *arg); +static void geopolicyrequested(WebKitWebView *v, WebKitWebFrame *f, + WebKitGeolocationPolicyDecision *d, Client *c); static const char *getatom(Client *c, int a); static void gettogglestat(Client *c); +static void getpagestat(Client *c); static char *geturi(Client *c); static gboolean initdownload(WebKitWebView *v, WebKitDownload *o, Client *c); @@ -130,7 +136,9 @@ static gboolean inspector_show(WebKitWebInspector *i, Client *c); static gboolean inspector_close(WebKitWebInspector *i, Client *c); static void inspector_finished(WebKitWebInspector *i, Client *c); -static gboolean keypress(GtkWidget *w, GdkEventKey *ev, Client *c); +static gboolean keypress(GtkAccelGroup *group, + GObject *obj, guint key, GdkModifierType mods, + Client *c); static void linkhover(WebKitWebView *v, const char* t, const char* l, Client *c); static void loadstatuschange(WebKitWebView *view, GParamSpec *pspec, @@ -159,7 +167,11 @@ static void stop(Client *c, const Arg *arg); static void titlechange(WebKitWebView *v, WebKitWebFrame *frame, const char *title, Client *c); static void toggle(Client *c, const Arg *arg); -static void update(Client *c); +static void togglecookiepolicy(Client *c, const Arg *arg); +static void togglegeolocation(Client *c, const Arg *arg); +static void togglescrollbars(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 windowobjectcleared(GtkWidget *w, WebKitWebFrame *frame, @@ -169,11 +181,26 @@ static void zoom(Client *c, const Arg *arg); /* configuration, allows nested code to access above variables */ #include "config.h" +static void +addaccelgroup(Client *c) { + 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); +} + static void beforerequest(WebKitWebView *w, WebKitWebFrame *f, WebKitWebResource *r, WebKitNetworkRequest *req, WebKitNetworkResponse *resp, gpointer d) { const gchar *uri = webkit_network_request_get_uri(req); + if(g_str_has_suffix(uri, "/favicon.ico")) webkit_network_request_set_uri(req, "about:blank"); } @@ -221,7 +248,8 @@ buttonrelease(WebKitWebView *web, GdkEventButton *e, GList *gl) { g_object_get(result, "context", &context, NULL); if(context & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK) { - if(e->button == 2) { + if(e->button == 2 || + (e->button == 1 && CLEANMASK(e->state) == CLEANMASK(MODKEY))) { g_object_get(result, "link-uri", &arg.v, NULL); newwindow(NULL, &arg, e->state & GDK_CONTROL_MASK); return true; @@ -274,10 +302,12 @@ cookiejar_init(CookieJar *self) { } static SoupCookieJar * -cookiejar_new(const char *filename, gboolean read_only) { +cookiejar_new(const char *filename, gboolean read_only, + SoupCookieJarAcceptPolicy policy) { return g_object_new(COOKIEJAR_TYPE, SOUP_COOKIE_JAR_TEXT_FILENAME, filename, - SOUP_COOKIE_JAR_READ_ONLY, read_only, NULL); + SOUP_COOKIE_JAR_READ_ONLY, read_only, + SOUP_COOKIE_JAR_ACCEPT_POLICY, policy, NULL); } static void @@ -289,6 +319,36 @@ cookiejar_set_property(GObject *self, guint prop_id, const GValue *value, flock(COOKIEJAR(self)->lock, LOCK_UN); } +static SoupCookieJarAcceptPolicy +cookiepolicy_get(void) { + switch(cookiepolicies[policysel]) { + case 'a': + return SOUP_COOKIE_JAR_ACCEPT_NEVER; + case '@': + return SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY; + case 'A': + default: + break; + } + + return SOUP_COOKIE_JAR_ACCEPT_ALWAYS; +} + +static char +cookiepolicy_set(const SoupCookieJarAcceptPolicy ep) { + switch(ep) { + case SOUP_COOKIE_JAR_ACCEPT_NEVER: + return 'a'; + case SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY: + return '@'; + case SOUP_COOKIE_JAR_ACCEPT_ALWAYS: + default: + break; + } + + return 'A'; +} + static void evalscript(JSContextRef js, char *script, char* scriptname) { JSStringRef jsscript, jsscriptname; @@ -296,7 +356,8 @@ evalscript(JSContextRef js, char *script, char* scriptname) { jsscript = JSStringCreateWithUTF8CString(script); jsscriptname = JSStringCreateWithUTF8CString(scriptname); - JSEvaluateScript(js, jsscript, JSContextGetGlobalObject(js), jsscriptname, 0, &exception); + JSEvaluateScript(js, jsscript, JSContextGetGlobalObject(js), + jsscriptname, 0, &exception); JSStringRelease(jsscript); JSStringRelease(jsscriptname); } @@ -307,7 +368,8 @@ runscript(WebKitWebFrame *frame) { GError *error; if(g_file_get_contents(scriptfile, &script, NULL, &error)) { - evalscript(webkit_web_frame_get_global_context(frame), script, scriptfile); + evalscript(webkit_web_frame_get_global_context(frame), + script, scriptfile); } } @@ -315,10 +377,15 @@ static void clipboard(Client *c, const Arg *arg) { gboolean paste = *(gboolean *)arg; - if(paste) - gtk_clipboard_request_text(gtk_clipboard_get(GDK_SELECTION_PRIMARY), pasteuri, c); - else - gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_PRIMARY), c->linkhover ? c->linkhover : geturi(c), -1); + if(paste) { + gtk_clipboard_request_text( + gtk_clipboard_get(GDK_SELECTION_PRIMARY), + pasteuri, c); + } else { + gtk_clipboard_set_text( + gtk_clipboard_get(GDK_SELECTION_PRIMARY), + c->linkhover ? c->linkhover : geturi(c), -1); + } } static char * @@ -365,12 +432,17 @@ decidewindow(WebKitWebView *view, WebKitWebFrame *f, WebKitNetworkRequest *r, return FALSE; } +static gboolean +deletion_interface(WebKitWebView *view, + WebKitDOMHTMLElement *arg1, Client *c) { + return FALSE; +} + static void destroyclient(Client *c) { Client *p; webkit_web_view_stop_loading(c->view); - gtk_widget_destroy(c->indicator); gtk_widget_destroy(GTK_WIDGET(c->view)); gtk_widget_destroy(c->scroll); gtk_widget_destroy(c->vbox); @@ -402,51 +474,6 @@ die(const char *errstr, ...) { exit(EXIT_FAILURE); } -static void -drawindicator(Client *c) { - gint width; - const char *uri; - char *colorname; - GtkWidget *w; - GdkGC *gc; - GdkColor fg; - - uri = geturi(c); - w = c->indicator; - width = c->progress * w->allocation.width / 100; - gc = gdk_gc_new(w->window); - if(strstr(uri, "https://") == uri) { - if(usingproxy) { - colorname = c->sslfailed? progress_proxy_untrust : - progress_proxy_trust; - } else { - colorname = c->sslfailed? progress_untrust : - progress_trust; - } - } else { - if(usingproxy) { - colorname = progress_proxy; - } else { - colorname = progress; - } - } - - gdk_color_parse(colorname, &fg); - gdk_gc_set_rgb_fg_color(gc, &fg); - gdk_draw_rectangle(w->window, - w->style->bg_gc[GTK_WIDGET_STATE(w)], - TRUE, 0, 0, w->allocation.width, w->allocation.height); - gdk_draw_rectangle(w->window, gc, TRUE, 0, 0, width, - w->allocation.height); - g_object_unref(gc); -} - -static gboolean -exposeindicator(GtkWidget *w, GdkEventExpose *e, Client *c) { - drawindicator(c); - return TRUE; -} - static void find(Client *c, const Arg *arg) { const char *s; @@ -466,6 +493,16 @@ fullscreen(Client *c, const Arg *arg) { c->fullscreen = !c->fullscreen; } +static void +geopolicyrequested(WebKitWebView *v, WebKitWebFrame *f, + WebKitGeolocationPolicyDecision *d, Client *c) { + if(allowgeolocation) { + webkit_geolocation_policy_allow(d); + } else { + webkit_geolocation_policy_deny(d); + } +} + static const char * getatom(Client *c, int a) { static char buf[BUFSIZ]; @@ -477,11 +514,13 @@ getatom(Client *c, int a) { XGetWindowProperty(dpy, GDK_WINDOW_XID(GTK_WIDGET(c->win)->window), atoms[a], 0L, BUFSIZ, False, XA_STRING, &adummy, &idummy, &ldummy, &ldummy, &p); - if(p) + if(p) { strncpy(buf, (char *)p, LENGTH(buf)-1); - else + } else { buf[0] = '\0'; + } XFree(p); + return buf; } @@ -506,28 +545,45 @@ initdownload(WebKitWebView *view, WebKitDownload *o, Client *c) { static void inspector(Client *c, const Arg *arg) { - if(c->isinspector) - return; - webkit_web_inspector_show(c->inspector); + if(c->isinspecting) { + webkit_web_inspector_close(c->inspector); + } else { + webkit_web_inspector_show(c->inspector); + } } static WebKitWebView * inspector_new(WebKitWebInspector *i, WebKitWebView *v, Client *c) { - Client *n = newclient(); - n->isinspector = true; - - return n->view; + return WEBKIT_WEB_VIEW(webkit_web_view_new()); } static gboolean inspector_show(WebKitWebInspector *i, Client *c) { - gtk_widget_show(GTK_WIDGET(webkit_web_inspector_get_web_view(i))); + WebKitWebView *w; + + if(c->isinspecting) + return false; + + w = webkit_web_inspector_get_web_view(i); + gtk_paned_pack2(GTK_PANED(c->pane), GTK_WIDGET(w), TRUE, TRUE); + gtk_widget_show(GTK_WIDGET(w)); + c->isinspecting = true; + return true; } static gboolean inspector_close(WebKitWebInspector *i, Client *c) { - gtk_widget_hide(GTK_WIDGET(webkit_web_inspector_get_web_view(i))); + GtkWidget *w; + + if(!c->isinspecting) + return false; + + w = GTK_WIDGET(webkit_web_inspector_get_web_view(i)); + gtk_widget_hide(w); + gtk_widget_destroy(w); + c->isinspecting = false; + return true; } @@ -537,14 +593,17 @@ inspector_finished(WebKitWebInspector *i, Client *c) { } static gboolean -keypress(GtkWidget* w, GdkEventKey *ev, Client *c) { +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(gdk_keyval_to_lower(ev->keyval) == keys[i].keyval - && CLEANMASK(ev->state) == keys[i].mod + if(key == keys[i].keyval + && mods == keys[i].mod && keys[i].func) { keys[i].func(c, &(keys[i].arg)); processed = TRUE; @@ -562,7 +621,7 @@ linkhover(WebKitWebView *v, const char* t, const char* l, Client *c) { free(c->linkhover); c->linkhover = NULL; } - update(c); + updatetitle(c); } static void @@ -581,14 +640,14 @@ loadstatuschange(WebKitWebView *view, GParamSpec *pspec, Client *c) { src = webkit_web_frame_get_data_source(frame); request = webkit_web_data_source_get_request(src); msg = webkit_network_request_get_message(request); - c->sslfailed = soup_message_get_flags(msg) - ^ SOUP_MESSAGE_CERTIFICATE_TRUSTED; + c->sslfailed = !(soup_message_get_flags(msg) + & SOUP_MESSAGE_CERTIFICATE_TRUSTED); } setatom(c, AtomUri, uri); break; case WEBKIT_LOAD_FINISHED: c->progress = 100; - update(c); + updatetitle(c); break; default: break; @@ -600,12 +659,13 @@ loaduri(Client *c, const Arg *arg) { char *u, *rp; const char *uri = (char *)arg->v; Arg a = { .b = FALSE }; + struct stat st; if(strcmp(uri, "") == 0) return; /* In case it's a file path. */ - if(uri[0] == '/') { + if(stat(uri, &st) == 0) { rp = realpath(uri, NULL); u = g_strdup_printf("file://%s", rp); free(rp); @@ -614,15 +674,17 @@ loaduri(Client *c, const Arg *arg) { : g_strdup_printf("http://%s", uri); } + setatom(c, AtomUri, uri); + /* prevents endless loop */ - if(c->uri && strcmp(u, c->uri) == 0) { + if(strcmp(u, geturi(c)) == 0) { reload(c, &a); } else { webkit_web_view_load_uri(c->view, u); c->progress = 0; c->title = copystr(&c->title, u); g_free(u); - update(c); + updatetitle(c); } } @@ -638,6 +700,8 @@ newclient(void) { WebKitWebSettings *settings; WebKitWebFrame *frame; GdkGeometry hints = { 1, 1 }; + GdkScreen *screen; + gdouble dpi; char *uri, *ua; if(!(c = calloc(1, sizeof(Client)))) @@ -667,26 +731,29 @@ newclient(void) { g_signal_connect(G_OBJECT(c->win), "destroy", G_CALLBACK(destroywin), c); - g_signal_connect(G_OBJECT(c->win), - "key-press-event", - G_CALLBACK(keypress), c); + + if(!kioskmode) + addaccelgroup(c); + + /* Pane */ + c->pane = gtk_vpaned_new(); /* VBox */ c->vbox = gtk_vbox_new(FALSE, 0); - - /* Scrolled Window */ - c->scroll = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(c->scroll), - GTK_POLICY_NEVER, GTK_POLICY_NEVER); + gtk_paned_pack1(GTK_PANED(c->pane), c->vbox, TRUE, TRUE); /* Webview */ c->view = WEBKIT_WEB_VIEW(webkit_web_view_new()); + g_signal_connect(G_OBJECT(c->view), "title-changed", G_CALLBACK(titlechange), c); g_signal_connect(G_OBJECT(c->view), "hovering-over-link", G_CALLBACK(linkhover), c); + g_signal_connect(G_OBJECT(c->view), + "geolocation-policy-decision-requested", + G_CALLBACK(geopolicyrequested), c); g_signal_connect(G_OBJECT(c->view), "create-web-view", G_CALLBACK(createwindow), c); @@ -717,25 +784,35 @@ newclient(void) { g_signal_connect(G_OBJECT(c->view), "resource-request-starting", G_CALLBACK(beforerequest), c); + g_signal_connect(G_OBJECT(c->view), + "should-show-delete-interface-for-element", + G_CALLBACK(deletion_interface), c); - /* Indicator */ - c->indicator = gtk_drawing_area_new(); - gtk_widget_set_size_request(c->indicator, 0, indicator_thickness); - g_signal_connect (G_OBJECT (c->indicator), "expose_event", - G_CALLBACK (exposeindicator), c); + /* Scrolled Window */ + c->scroll = gtk_scrolled_window_new(NULL, NULL); + + frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(c->view)); + g_signal_connect(G_OBJECT(frame), "scrollbars-policy-changed", + G_CALLBACK(gtk_true), NULL); + + if(!enablescrollbars) { + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(c->scroll), + GTK_POLICY_NEVER, GTK_POLICY_NEVER); + } else { + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(c->scroll), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + } /* Arranging */ gtk_container_add(GTK_CONTAINER(c->scroll), GTK_WIDGET(c->view)); - gtk_container_add(GTK_CONTAINER(c->win), c->vbox); + gtk_container_add(GTK_CONTAINER(c->win), c->pane); gtk_container_add(GTK_CONTAINER(c->vbox), c->scroll); - gtk_container_add(GTK_CONTAINER(c->vbox), c->indicator); /* Setup */ - gtk_box_set_child_packing(GTK_BOX(c->vbox), c->indicator, FALSE, - FALSE, 0, GTK_PACK_START); gtk_box_set_child_packing(GTK_BOX(c->vbox), c->scroll, TRUE, TRUE, 0, GTK_PACK_START); gtk_widget_grab_focus(GTK_WIDGET(c->view)); + gtk_widget_show(c->pane); gtk_widget_show(c->vbox); gtk_widget_show(c->scroll); gtk_widget_show(GTK_WIDGET(c->view)); @@ -746,8 +823,8 @@ newclient(void) { gdk_window_add_filter(GTK_WIDGET(c->win)->window, processx, c); webkit_web_view_set_full_content_zoom(c->view, TRUE); - frame = webkit_web_view_get_main_frame(c->view); runscript(frame); + settings = webkit_web_view_get_settings(c->view); if(!(ua = getenv("SURF_USERAGENT"))) ua = useragent; @@ -764,6 +841,28 @@ newclient(void) { enablespatialbrowsing, NULL); g_object_set(G_OBJECT(settings), "enable-developer-extras", enableinspector, NULL); + g_object_set(G_OBJECT(settings), "enable-default-context-menu", + kioskmode ^ 1, NULL); + g_object_set(G_OBJECT(settings), "default-font-size", + defaultfontsize, NULL); + + /* + * While stupid, CSS specifies that a pixel represents 1/96 of an inch. + * This ensures websites are not unusably small with a high DPI screen. + * It is equivalent to firefox's "layout.css.devPixelsPerPx" setting. + */ + if(zoomto96dpi) { + screen = gdk_window_get_screen(GTK_WIDGET(c->win)->window); + dpi = gdk_screen_get_resolution(screen); + if(dpi != -1) { + g_object_set(G_OBJECT(settings), "enforce-96-dpi", true, + NULL); + webkit_web_view_set_zoom_level(c->view, dpi/96); + } + } + /* This might conflict with _zoomto96dpi_. */ + if(zoomlevel != 1.0) + webkit_web_view_set_zoom_level(c->view, zoomlevel); if(enableinspector) { c->inspector = WEBKIT_WEB_INSPECTOR( @@ -776,7 +875,12 @@ newclient(void) { G_CALLBACK(inspector_close), c); g_signal_connect(G_OBJECT(c->inspector), "finished", G_CALLBACK(inspector_finished), c); - c->isinspector = false; + c->isinspecting = false; + } + + if(runinfullscreen) { + c->fullscreen = 0; + fullscreen(c, NULL); } g_free(uri); @@ -789,6 +893,7 @@ newclient(void) { c->title = NULL; c->next = clients; clients = c; + if(showxid) { gdk_display_sync(gtk_widget_get_display(c->win)); printf("%u\n", @@ -798,30 +903,37 @@ newclient(void) { die("Error closing stdout"); } } + return c; } static void newwindow(Client *c, const Arg *arg, gboolean noembed) { guint i = 0; - const char *cmd[10], *uri; + const char *cmd[14], *uri; const Arg a = { .v = (void *)cmd }; char tmp[64]; cmd[i++] = argv0; + if(!enablescrollbars) + cmd[i++] = "-b"; if(embed && !noembed) { cmd[i++] = "-e"; snprintf(tmp, LENGTH(tmp), "%u\n", (int)embed); cmd[i++] = tmp; } - if(!enablescripts) - cmd[i++] = "-s"; - if(!enableplugins) - cmd[i++] = "-p"; if(!loadimages) cmd[i++] = "-i"; + if(kioskmode) + cmd[i++] = "-k"; + if(!enableplugins) + cmd[i++] = "-p"; + if(!enablescripts) + cmd[i++] = "-s"; if(showxid) cmd[i++] = "-x"; + cmd[i++] = "-c"; + cmd[i++] = cookiefile; cmd[i++] = "--"; uri = arg->v ? (char *)arg->v : c->linkhover; if(uri) @@ -893,11 +1005,12 @@ processx(GdkXEvent *e, GdkEvent *event, gpointer d) { if(ev->atom == atoms[AtomFind]) { arg.b = TRUE; find(c, &arg); + return GDK_FILTER_REMOVE; - } - else if(ev->atom == atoms[AtomGo]) { + } else if(ev->atom == atoms[AtomGo]) { arg.v = getatom(c, AtomGo); loaduri(c, &arg); + return GDK_FILTER_REMOVE; } } @@ -908,16 +1021,17 @@ processx(GdkXEvent *e, GdkEvent *event, gpointer d) { static void progresschange(WebKitWebView *view, GParamSpec *pspec, Client *c) { c->progress = webkit_web_view_get_progress(c->view) * 100; - update(c); + updatetitle(c); } static void reload(Client *c, const Arg *arg) { gboolean nocache = *(gboolean *)arg; - if(nocache) + if(nocache) { webkit_web_view_reload_bypass_cache(c->view); - else + } else { webkit_web_view_reload(c->view); + } } static void @@ -937,7 +1051,7 @@ scroll(GtkAdjustment *a, const Arg *arg) { gdouble v; v = gtk_adjustment_get_value(a); - switch (arg->i){ + switch(arg->i) { case +10000: case -10000: v += gtk_adjustment_get_page_increment(a) * @@ -969,12 +1083,11 @@ setup(void) { char *new_proxy; SoupURI *puri; SoupSession *s; + GError *error = NULL; /* clean up any zombies immediately */ sigchld(0); gtk_init(NULL, NULL); - if (!g_thread_supported()) - g_thread_init(NULL); dpy = GDK_DISPLAY(); @@ -993,11 +1106,17 @@ setup(void) { /* cookie jar */ soup_session_add_feature(s, - SOUP_SESSION_FEATURE(cookiejar_new(cookiefile, - FALSE))); + SOUP_SESSION_FEATURE(cookiejar_new(cookiefile, FALSE, + cookiepolicy_get()))); /* ssl */ - g_object_set(G_OBJECT(s), "ssl-ca-file", cafile, NULL); + tlsdb = g_tls_file_database_new(cafile, &error); + + if(error) { + g_warning("Error loading SSL database %s: %s", cafile, error->message); + g_error_free(error); + } + g_object_set(G_OBJECT(s), "tls-database", tlsdb, NULL); g_object_set(G_OBJECT(s), "ssl-strict", strictssl, NULL); /* proxy */ @@ -1057,7 +1176,7 @@ stop(Client *c, const Arg *arg) { static void titlechange(WebKitWebView *v, WebKitWebFrame *f, const char *t, Client *c) { c->title = copystr(&c->title, t); - update(c); + updatetitle(c); } static void @@ -1071,51 +1190,160 @@ toggle(Client *c, const Arg *arg) { g_object_get(G_OBJECT(settings), name, &value, NULL); g_object_set(G_OBJECT(settings), name, !value, NULL); - reload(c,&a); + reload(c, &a); +} + +static void +togglecookiepolicy(Client *c, const Arg *arg) { + SoupCookieJar *jar; + SoupCookieJarAcceptPolicy policy; + + jar = SOUP_COOKIE_JAR( + soup_session_get_feature( + webkit_get_default_session(), + SOUP_TYPE_COOKIE_JAR)); + g_object_get(G_OBJECT(jar), "accept-policy", &policy, NULL); + + policysel++; + if(policysel >= strlen(cookiepolicies)) + policysel = 0; + + g_object_set(G_OBJECT(jar), "accept-policy", + cookiepolicy_get(), NULL); + + updatetitle(c); + /* Do not reload. */ +} + +static void +togglegeolocation(Client *c, const Arg *arg) { + Arg a = { .b = FALSE }; + + allowgeolocation ^= 1; + + reload(c, &a); +} + +static void +twitch(Client *c, const Arg *arg) { + GtkAdjustment *a; + gdouble v; + + a = gtk_scrolled_window_get_vadjustment( + GTK_SCROLLED_WINDOW(c->scroll)); + + v = gtk_adjustment_get_value(a); + + v += arg->i; + + v = MAX(v, 0.0); + v = MIN(v, gtk_adjustment_get_upper(a) - + gtk_adjustment_get_page_size(a)); + gtk_adjustment_set_value(a, v); +} + +static void +togglescrollbars(Client *c, const Arg *arg) { + GtkPolicyType vspolicy; + Arg a; + + gtk_scrolled_window_get_policy(GTK_SCROLLED_WINDOW(c->scroll), NULL, &vspolicy); + + if(vspolicy == GTK_POLICY_AUTOMATIC) { + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(c->scroll), + GTK_POLICY_NEVER, GTK_POLICY_NEVER); + } else { + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(c->scroll), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + a.i = +1; + twitch(c, &a); + a.i = -1; + twitch(c, &a); + } +} + +static void +togglestyle(Client *c, const Arg *arg) { + WebKitWebSettings *settings; + char *uri; + + settings = webkit_web_view_get_settings(c->view); + g_object_get(G_OBJECT(settings), "user-stylesheet-uri", &uri, NULL); + uri = uri[0] ? g_strdup("") : g_strconcat("file://", stylefile, NULL); + g_object_set(G_OBJECT(settings), "user-stylesheet-uri", uri, NULL); + + updatetitle(c); } static void gettogglestat(Client *c){ gboolean value; + char *uri; + int p = 0; WebKitWebSettings *settings = webkit_web_view_get_settings(c->view); + togglestat[p++] = cookiepolicy_set(cookiepolicy_get()); + g_object_get(G_OBJECT(settings), "enable-caret-browsing", &value, NULL); - togglestat[0] = value? 'C': 'c'; + togglestat[p++] = value? 'C': 'c'; + + togglestat[p++] = allowgeolocation? 'G': 'g'; g_object_get(G_OBJECT(settings), "auto-load-images", &value, NULL); - togglestat[1] = value? 'I': 'i'; + togglestat[p++] = value? 'I': 'i'; g_object_get(G_OBJECT(settings), "enable-scripts", &value, NULL); - togglestat[2] = value? 'S': 's'; + togglestat[p++] = value? 'S': 's'; g_object_get(G_OBJECT(settings), "enable-plugins", &value, NULL); - togglestat[3] = value? 'V': 'v'; + togglestat[p++] = value? 'V': 'v'; + + g_object_get(G_OBJECT(settings), "user-stylesheet-uri", &uri, NULL); + togglestat[p++] = uri[0] ? 'M': 'm'; - togglestat[4] = '\0'; + togglestat[p] = '\0'; } +static void +getpagestat(Client *c) { + const char *uri = geturi(c); + + if(strstr(uri, "https://") == uri) { + pagestat[0] = c->sslfailed ? 'U' : 'T'; + } else { + pagestat[0] = '-'; + } + + pagestat[1] = usingproxy ? 'P' : '-'; + pagestat[2] = '\0'; + +} static void -update(Client *c) { +updatetitle(Client *c) { char *t; - gettogglestat(c); + if(showindicators) { + gettogglestat(c); + getpagestat(c); - if(c->linkhover) { - t = g_strdup_printf("%s| %s", togglestat, c->linkhover); - } else if(c->progress != 100) { - drawindicator(c); - gtk_widget_show(c->indicator); - t = g_strdup_printf("[%i%%] %s| %s", c->progress, togglestat, - c->title); + if(c->linkhover) { + t = g_strdup_printf("%s:%s | %s", togglestat, + pagestat, c->linkhover); + } else if(c->progress != 100) { + t = g_strdup_printf("[%i%%] %s:%s | %s", c->progress, + togglestat, pagestat, c->title); + } else { + t = g_strdup_printf("%s:%s | %s", togglestat, pagestat, + c->title); + } + + gtk_window_set_title(GTK_WINDOW(c->win), t); + g_free(t); } else { - gtk_widget_hide_all(c->indicator); - t = g_strdup_printf("%s| %s", togglestat, c->title); + gtk_window_set_title(GTK_WINDOW(c->win), c->title); } - - gtk_window_set_title(GTK_WINDOW(c->win), t); - g_free(t); } static void @@ -1126,8 +1354,11 @@ updatewinid(Client *c) { static void usage(void) { - die("usage: %s [-inpsvx] [-c cookiefile] [-e xid] [-r scriptfile]" - " [-t stylefile] [-u useragent] [uri]\n", basename(argv0)); + die("usage: %s [-bBfFgGiIkKnNpPsSvx]" + " [-a cookiepolicies ] " + " [-c cookiefile] [-e xid] [-r scriptfile]" + " [-t stylefile] [-u useragent] [-z zoomlevel]" + " [uri]\n", basename(argv0)); } static void @@ -1160,27 +1391,63 @@ main(int argc, char *argv[]) { /* command line args */ ARGBEGIN { + case 'b': + enablescrollbars = 0; + break; + case 'B': + enablescrollbars = 1; + break; case 'c': cookiefile = EARGF(usage()); break; case 'e': embed = strtol(EARGF(usage()), NULL, 0); break; + case 'f': + runinfullscreen = 1; + break; + case 'F': + runinfullscreen = 0; + break; + case 'g': + allowgeolocation = 0; + break; + case 'G': + allowgeolocation = 1; + break; case 'i': loadimages = 0; break; + case 'I': + loadimages = 1; + break; + case 'k': + kioskmode = 0; + break; + case 'K': + kioskmode = 1; + break; case 'n': enableinspector = 0; break; + case 'N': + enableinspector = 1; + break; case 'p': enableplugins = 0; break; + case 'P': + enableplugins = 1; + break; case 'r': scriptfile = EARGF(usage()); break; case 's': enablescripts = 0; break; + case 'S': + enablescripts = 1; + break; case 't': stylefile = EARGF(usage()); break; @@ -1193,6 +1460,9 @@ main(int argc, char *argv[]) { case 'x': showxid = TRUE; break; + case 'z': + zoomlevel = strtof(EARGF(usage()), NULL); + break; default: usage(); } ARGEND;