Enable resizable text areas.
[surf.git] / surf.c
diff --git a/surf.c b/surf.c
index dfcd787..bcb3a7b 100644 (file)
--- a/surf.c
+++ b/surf.c
@@ -29,7 +29,7 @@
 char *argv0;
 
 #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 COOKIEJAR_TYPE          (cookiejar_get_type ())
 #define COOKIEJAR(obj)          (G_TYPE_CHECK_INSTANCE_CAST ((obj), COOKIEJAR_TYPE, CookieJar))
 
@@ -47,7 +47,7 @@ typedef struct Client {
        WebKitWebView *view;
        WebKitWebInspector *inspector;
        char *title, *linkhover;
-       const char *uri, *needle;
+       const char *needle;
        gint progress;
        struct Client *next;
        gboolean zoomed, fullscreen, isinspecting, sslfailed;
@@ -78,8 +78,10 @@ static GdkNativeWindow embed = 0;
 static gboolean showxid = FALSE;
 static char winid[64];
 static gboolean usingproxy = 0;
-static char togglestat[7];
+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,
@@ -91,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,
@@ -142,8 +148,9 @@ static void navigate(Client *c, const Arg *arg);
 static Client *newclient(void);
 static void newwindow(Client *c, const Arg *arg, gboolean noembed);
 static void pasteuri(GtkClipboard *clipboard, const char *text, gpointer d);
-static void populatepopup(WebKitWebView *web, GtkMenu *menu, Client *c);
-static void popupactivate(GtkMenuItem *menu, Client *);
+static gboolean contextmenu(WebKitWebView *view, GtkWidget *menu,
+               WebKitHitTestResult *target, gboolean keyboard, Client *c);
+static void menuactivate(GtkMenuItem *item, Client *c);
 static void print(Client *c, const Arg *arg);
 static GdkFilterReturn processx(GdkXEvent *xevent, GdkEvent *event,
                gpointer d);
@@ -158,9 +165,9 @@ static void sigchld(int unused);
 static void source(Client *c, const Arg *arg);
 static void spawn(Client *c, const Arg *arg);
 static void stop(Client *c, const Arg *arg);
-static void titlechange(WebKitWebView *v, WebKitWebFrame *frame,
-               const char *title, Client *c);
+static void titlechange(WebKitWebView *view, GParamSpec *pspec, Client *c);
 static void toggle(Client *c, const Arg *arg);
+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);
@@ -241,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;
@@ -294,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
@@ -309,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;
@@ -600,8 +640,8 @@ 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;
@@ -616,7 +656,7 @@ loadstatuschange(WebKitWebView *view, GParamSpec *pspec, Client *c) {
 
 static void
 loaduri(Client *c, const Arg *arg) {
-       char *u, *rp;
+       char *u = NULL, *rp;
        const char *uri = (char *)arg->v;
        Arg a = { .b = FALSE };
        struct stat st;
@@ -634,16 +674,18 @@ 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);
                updatetitle(c);
        }
+       g_free(u);
 }
 
 static void
@@ -665,6 +707,9 @@ newclient(void) {
        if(!(c = calloc(1, sizeof(Client))))
                die("Cannot malloc!\n");
 
+       c->title = NULL;
+       c->progress = 100;
+
        /* Window */
        if(embed) {
                c->win = gtk_plug_new(embed);
@@ -704,7 +749,7 @@ newclient(void) {
        c->view = WEBKIT_WEB_VIEW(webkit_web_view_new());
 
        g_signal_connect(G_OBJECT(c->view),
-                       "title-changed",
+                       "notify::title",
                        G_CALLBACK(titlechange), c);
        g_signal_connect(G_OBJECT(c->view),
                        "hovering-over-link",
@@ -737,8 +782,8 @@ newclient(void) {
                        "button-release-event",
                        G_CALLBACK(buttonrelease), c);
        g_signal_connect(G_OBJECT(c->view),
-                       "populate-popup",
-                       G_CALLBACK(populatepopup), c);
+                       "context-menu",
+                       G_CALLBACK(contextmenu), c);
        g_signal_connect(G_OBJECT(c->view),
                        "resource-request-starting",
                        G_CALLBACK(beforerequest), c);
@@ -803,6 +848,8 @@ newclient(void) {
                        kioskmode ^ 1, NULL);
        g_object_set(G_OBJECT(settings), "default-font-size",
                        defaultfontsize, NULL);
+       g_object_set(G_OBJECT(settings), "resizable-text-areas",
+                       1, NULL);
 
        /*
         * While stupid, CSS specifies that a pixel represents 1/96 of an inch.
@@ -818,6 +865,9 @@ newclient(void) {
                        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(
@@ -845,7 +895,6 @@ newclient(void) {
        if(hidebackground)
                webkit_web_view_set_transparent(c->view, TRUE);
 
-       c->title = NULL;
        c->next = clients;
        clients = c;
 
@@ -865,11 +914,13 @@ newclient(void) {
 static void
 newwindow(Client *c, const Arg *arg, gboolean noembed) {
        guint i = 0;
-       const char *cmd[14], *uri;
+       const char *cmd[16], *uri;
        const Arg a = { .v = (void *)cmd };
        char tmp[64];
 
        cmd[i++] = argv0;
+       cmd[i++] = "-a";
+       cmd[i++] = cookiepolicies;
        if(!enablescrollbars)
                cmd[i++] = "-b";
        if(embed && !noembed) {
@@ -897,19 +948,21 @@ newwindow(Client *c, const Arg *arg, gboolean noembed) {
        spawn(NULL, &a);
 }
 
-static void
-populatepopup(WebKitWebView *web, GtkMenu *menu, Client *c) {
-       GList *items = gtk_container_get_children(GTK_CONTAINER(menu));
+static gboolean
+contextmenu(WebKitWebView *view, GtkWidget *menu, WebKitHitTestResult *target,
+               gboolean keyboard, Client *c) {
+       GList *items = gtk_container_get_children(GTK_CONTAINER(GTK_MENU(menu)));
 
        for(GList *l = items; l; l = l->next) {
-               g_signal_connect(l->data, "activate", G_CALLBACK(popupactivate), c);
+               g_signal_connect(l->data, "activate", G_CALLBACK(menuactivate), c);
        }
 
        g_list_free(items);
+       return FALSE;
 }
 
 static void
-popupactivate(GtkMenuItem *menu, Client *c) {
+menuactivate(GtkMenuItem *item, Client *c) {
        /*
         * context-menu-action-2000     open link
         * context-menu-action-1        open link in window
@@ -925,7 +978,7 @@ popupactivate(GtkMenuItem *menu, Client *c) {
        const char *name;
        GtkClipboard *prisel;
 
-       a = gtk_activatable_get_related_action(GTK_ACTIVATABLE(menu));
+       a = gtk_activatable_get_related_action(GTK_ACTIVATABLE(item));
        if(a == NULL)
                return;
 
@@ -1006,7 +1059,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) *
@@ -1038,6 +1091,7 @@ setup(void) {
        char *new_proxy;
        SoupURI *puri;
        SoupSession *s;
+       GError *error = NULL;
 
        /* clean up any zombies immediately */
        sigchld(0);
@@ -1060,11 +1114,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 */
@@ -1122,9 +1182,12 @@ stop(Client *c, const Arg *arg) {
 }
 
 static void
-titlechange(WebKitWebView *v, WebKitWebFrame *f, const char *t, Client *c) {
-       c->title = copystr(&c->title, t);
-       updatetitle(c);
+titlechange(WebKitWebView *view, GParamSpec *pspec, Client *c) {
+       const gchar *t = webkit_web_view_get_title(view);
+       if (t) {
+               c->title = copystr(&c->title, t);
+               updatetitle(c);
+       }
 }
 
 static void
@@ -1141,6 +1204,37 @@ toggle(Client *c, const Arg *arg) {
        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;
@@ -1159,15 +1253,6 @@ twitch(Client *c, const Arg *arg) {
        gtk_adjustment_set_value(a, v);
 }
 
-static void
-togglegeolocation(Client *c, const Arg *arg) {
-       Arg a = { .b = FALSE };
-
-       allowgeolocation ^= 1;
-
-       reload(c, &a);
-}
-
 static void
 togglescrollbars(Client *c, const Arg *arg) {
        GtkPolicyType vspolicy;
@@ -1205,27 +1290,30 @@ 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[1] = allowgeolocation? 'G': 'g';
+       togglestat[p++] = allowgeolocation? 'G': 'g';
 
        g_object_get(G_OBJECT(settings), "auto-load-images", &value, NULL);
-       togglestat[2] = value? 'I': 'i';
+       togglestat[p++] = value? 'I': 'i';
 
        g_object_get(G_OBJECT(settings), "enable-scripts", &value, NULL);
-       togglestat[3] = value? 'S': 's';
+       togglestat[p++] = value? 'S': 's';
 
        g_object_get(G_OBJECT(settings), "enable-plugins", &value, NULL);
-       togglestat[4] = value? 'V': 'v';
+       togglestat[p++] = value? 'V': 'v';
 
        g_object_get(G_OBJECT(settings), "user-stylesheet-uri", &uri, NULL);
-       togglestat[5] = uri[0] ? 'M': 'm';
+       togglestat[p++] = uri[0] ? 'M': 'm';
 
-       togglestat[6] = '\0';
+       togglestat[p] = '\0';
 }
 
 static void
@@ -1256,16 +1344,18 @@ updatetitle(Client *c) {
                                        pagestat, c->linkhover);
                } else if(c->progress != 100) {
                        t = g_strdup_printf("[%i%%] %s:%s | %s", c->progress,
-                                       togglestat, pagestat, c->title);
+                                       togglestat, pagestat,
+                                       (c->title == NULL)? "" : c->title);
                } else {
                        t = g_strdup_printf("%s:%s | %s", togglestat, pagestat,
-                                       c->title);
+                                       (c->title == NULL)? "" : c->title);
                }
 
                gtk_window_set_title(GTK_WINDOW(c->win), t);
                g_free(t);
        } else {
-               gtk_window_set_title(GTK_WINDOW(c->win), c->title);
+               gtk_window_set_title(GTK_WINDOW(c->win),
+                               (c->title == NULL)? "" : c->title);
        }
 }
 
@@ -1278,8 +1368,10 @@ updatewinid(Client *c) {
 static void
 usage(void) {
        die("usage: %s [-bBfFgGiIkKnNpPsSvx]"
+               " [-a cookiepolicies ] "
                " [-c cookiefile] [-e xid] [-r scriptfile]"
-               " [-t stylefile] [-u useragent] [uri]\n", basename(argv0));
+               " [-t stylefile] [-u useragent] [-z zoomlevel]"
+               " [uri]\n", basename(argv0));
 }
 
 static void
@@ -1307,11 +1399,15 @@ zoom(Client *c, const Arg *arg) {
 int
 main(int argc, char *argv[]) {
        Arg arg;
+       Client *c;
 
        memset(&arg, 0, sizeof(arg));
 
        /* command line args */
        ARGBEGIN {
+       case 'a':
+               cookiepolicies = EARGF(usage());
+               break;
        case 'b':
                enablescrollbars = 0;
                break;
@@ -1376,11 +1472,14 @@ main(int argc, char *argv[]) {
                useragent = EARGF(usage());
                break;
        case 'v':
-               die("surf-"VERSION", ©2009-2012 surf engineers, "
+               die("surf-"VERSION", ©2009-2014 surf engineers, "
                                "see LICENSE for details\n");
        case 'x':
                showxid = TRUE;
                break;
+       case 'z':
+               zoomlevel = strtof(EARGF(usage()), NULL);
+               break;
        default:
                usage();
        } ARGEND;
@@ -1388,9 +1487,12 @@ main(int argc, char *argv[]) {
                arg.v = argv[0];
 
        setup();
-       newclient();
-       if(arg.v)
+       c = newclient();
+       if(arg.v) {
                loaduri(clients, &arg);
+       } else {
+               updatetitle(c);
+       }
 
        gtk_main();
        cleanup();