surf.c - surf - Surf web browser.
 (HTM) git clone git://r-36.net/surf
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       surf.c (51025B)
       ---
            1 /* See LICENSE file for copyright and license details.
            2  *
            3  * To understand surf, start reading main().
            4  */
            5 #include <signal.h>
            6 #include <X11/X.h>
            7 #include <X11/Xatom.h>
            8 #include <gtk/gtk.h>
            9 #include <gdk/gdkx.h>
           10 #include <gdk/gdk.h>
           11 #include <gdk/gdkkeysyms.h>
           12 #include <string.h>
           13 #include <sys/types.h>
           14 #include <sys/wait.h>
           15 #include <fcntl.h>
           16 #include <unistd.h>
           17 #include <limits.h>
           18 #include <stdlib.h>
           19 #include <stdio.h>
           20 #include <webkit/webkit.h>
           21 #include <JavaScriptCore/JavaScript.h>
           22 #include <sys/file.h>
           23 #include <libgen.h>
           24 #include <stdarg.h>
           25 #include <regex.h>
           26 #include <pwd.h>
           27 #include <glib.h>
           28 #include <glib/gstdio.h>
           29 
           30 #include "arg.h"
           31 
           32 char *argv0;
           33 
           34 #define LENGTH(x)               (sizeof(x) / sizeof(x[0]))
           35 #define CLEANMASK(mask)         (mask & (MODKEY|GDK_SHIFT_MASK))
           36 #define COOKIEJAR_TYPE          (cookiejar_get_type ())
           37 #define COOKIEJAR(obj)          (G_TYPE_CHECK_INSTANCE_CAST ((obj), COOKIEJAR_TYPE, CookieJar))
           38 
           39 enum { AtomFind, AtomGo, AtomUri, AtomUA, AtomLast };
           40 enum {
           41         ClkDoc   = WEBKIT_HIT_TEST_RESULT_CONTEXT_DOCUMENT,
           42         ClkLink  = WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK,
           43         ClkImg   = WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE,
           44         ClkMedia = WEBKIT_HIT_TEST_RESULT_CONTEXT_MEDIA,
           45         ClkSel   = WEBKIT_HIT_TEST_RESULT_CONTEXT_SELECTION,
           46         ClkEdit  = WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE,
           47         ClkAny   = ClkDoc | ClkLink | ClkImg | ClkMedia | ClkSel | ClkEdit,
           48 };
           49 
           50 typedef union Arg Arg;
           51 union Arg {
           52         gboolean b;
           53         gint i;
           54         const void *v;
           55 };
           56 
           57 typedef struct Client {
           58         GtkWidget *win, *scroll, *vbox, *pane;
           59         WebKitWebView *view;
           60         WebKitWebInspector *inspector;
           61         char *title, *linkhover;
           62         const char *needle;
           63         gint progress;
           64         struct Client *next;
           65         gboolean zoomed, fullscreen, isinspecting, sslfailed;
           66 } Client;
           67 
           68 typedef struct {
           69         char *key;
           70         char *value;
           71 } HttpHeader;
           72 
           73 typedef struct {
           74         guint mod;
           75         guint keyval;
           76         void (*func)(Client *c, const Arg *arg);
           77         const Arg arg;
           78 } Key;
           79 
           80 typedef struct {
           81         unsigned int click;
           82         unsigned int mask;
           83         guint button;
           84         void (*func)(Client *c, const Arg *arg);
           85         const Arg arg;
           86 } Button;
           87 
           88 typedef struct {
           89         SoupCookieJarText parent_instance;
           90         int lock;
           91 } CookieJar;
           92 
           93 typedef struct {
           94         SoupCookieJarTextClass parent_class;
           95 } CookieJarClass;
           96 
           97 G_DEFINE_TYPE(CookieJar, cookiejar, SOUP_TYPE_COOKIE_JAR_TEXT)
           98 
           99 typedef struct {
          100         char *regex;
          101         char *style;
          102         regex_t re;
          103 } SiteStyle;
          104 
          105 static Display *dpy;
          106 static Atom atoms[AtomLast];
          107 static Client *clients = NULL;
          108 static GdkNativeWindow embed = 0;
          109 static gboolean showxid = FALSE;
          110 static char winid[64];
          111 static gboolean usingproxy = 0;
          112 static char togglestat[12];
          113 static char pagestat[3];
          114 static GTlsDatabase *tlsdb;
          115 static int policysel = 0;
          116 static char *stylefile = NULL;
          117 static SoupCache *diskcache = NULL;
          118 
          119 static void addaccelgroup(Client *c);
          120 static void beforerequest(WebKitWebView *w, WebKitWebFrame *f,
          121                           WebKitWebResource *r, WebKitNetworkRequest *req,
          122                           WebKitNetworkResponse *resp, Client *c);
          123 static const char *getuserhomedir(const char *user);
          124 static const char *getcurrentuserhomedir(void);
          125 static char *buildfile(const char *path);
          126 static char *buildpath(const char *path);
          127 static gboolean buttonrelease(WebKitWebView *web, GdkEventButton *e, Client *c);
          128 static void cleanup(void);
          129 static void clipboard(Client *c, const Arg *arg);
          130 
          131 /* Cookiejar implementation */
          132 static void cookiejar_changed(SoupCookieJar *self, SoupCookie *old_cookie,
          133                               SoupCookie *new_cookie);
          134 static void cookiejar_finalize(GObject *self);
          135 static SoupCookieJarAcceptPolicy cookiepolicy_get(void);
          136 static SoupCookieJar *cookiejar_new(const char *filename, gboolean read_only,
          137                                     SoupCookieJarAcceptPolicy policy);
          138 static void cookiejar_set_property(GObject *self, guint prop_id,
          139     const GValue *value, GParamSpec *pspec);
          140 static char cookiepolicy_set(const SoupCookieJarAcceptPolicy p);
          141 
          142 static char *copystr(char **str, const char *src);
          143 static WebKitWebView *createwindow(WebKitWebView *v, WebKitWebFrame *f,
          144                                    Client *c);
          145 static gboolean decidedownload(WebKitWebView *v, WebKitWebFrame *f,
          146                                WebKitNetworkRequest *r, gchar *m,
          147                                WebKitWebPolicyDecision *p, Client *c);
          148 static gboolean decidewindow(WebKitWebView *v, WebKitWebFrame *f,
          149                              WebKitNetworkRequest *r, WebKitWebNavigationAction
          150                              *n, WebKitWebPolicyDecision *p, Client *c);
          151 static gboolean deletion_interface(WebKitWebView *view,
          152                                    WebKitDOMHTMLElement *arg1, Client *c);
          153 static void destroyclient(Client *c);
          154 static void destroywin(GtkWidget* w, Client *c);
          155 static void die(const char *errstr, ...);
          156 static void eval(Client *c, const Arg *arg);
          157 static void find(Client *c, const Arg *arg);
          158 static void fullscreen(Client *c, const Arg *arg);
          159 static void geopolicyrequested(WebKitWebView *v, WebKitWebFrame *f,
          160                                WebKitGeolocationPolicyDecision *d, Client *c);
          161 static const char *getatom(Client *c, int a);
          162 static void gettogglestat(Client *c);
          163 static void getpagestat(Client *c);
          164 static char *geturi(Client *c);
          165 static const gchar *getstyle(const char *uri);
          166 static void setstyle(Client *c, const char *style);
          167 
          168 static void handleplumb(Client *c, WebKitWebView *w, const gchar *uri);
          169 
          170 static gboolean initdownload(WebKitWebView *v, WebKitDownload *o, Client *c);
          171 
          172 static void inspector(Client *c, const Arg *arg);
          173 static WebKitWebView *inspector_new(WebKitWebInspector *i, WebKitWebView *v,
          174                                     Client *c);
          175 static gboolean inspector_show(WebKitWebInspector *i, Client *c);
          176 static gboolean inspector_close(WebKitWebInspector *i, Client *c);
          177 static void inspector_finished(WebKitWebInspector *i, Client *c);
          178 
          179 static gboolean keypress(GtkAccelGroup *group, GObject *obj, guint key,
          180                          GdkModifierType mods, Client *c);
          181 static void linkhover(WebKitWebView *v, const char* t, const char* l,
          182                       Client *c);
          183 static void loadstatuschange(WebKitWebView *view, GParamSpec *pspec,
          184                              Client *c);
          185 static void loaduri(Client *c, const Arg *arg);
          186 static void navigate(Client *c, const Arg *arg);
          187 static Client *newclient(void);
          188 static void newwindow(Client *c, const Arg *arg, gboolean noembed);
          189 static void pasteuri(GtkClipboard *clipboard, const char *text, gpointer d);
          190 static gboolean contextmenu(WebKitWebView *view, GtkWidget *menu,
          191                             WebKitHitTestResult *target, gboolean keyboard,
          192                             Client *c);
          193 static void menuactivate(GtkMenuItem *item, Client *c);
          194 static void print(Client *c, const Arg *arg);
          195 static GdkFilterReturn processx(GdkXEvent *xevent, GdkEvent *event,
          196                                 gpointer d);
          197 static void progresschange(WebKitWebView *view, GParamSpec *pspec, Client *c);
          198 static void linkopen(Client *c, const Arg *arg);
          199 static void linkopenembed(Client *c, const Arg *arg);
          200 static void reload(Client *c, const Arg *arg);
          201 static void scroll_h(Client *c, const Arg *arg);
          202 static void scroll_v(Client *c, const Arg *arg);
          203 static void scroll(GtkAdjustment *a, const Arg *arg);
          204 static void setatom(Client *c, int a, const char *v);
          205 static void setup(void);
          206 static void setup_proxy(void);
          207 static void sigchld(int unused);
          208 static void sighup(int unused);
          209 static void source(Client *c, const Arg *arg);
          210 static void spawn(Client *c, const Arg *arg);
          211 static void stop(Client *c, const Arg *arg);
          212 static void titlechange(WebKitWebView *view, GParamSpec *pspec, Client *c);
          213 static void titlechangeleave(void *a, void *b, Client *c);
          214 static void toggle(Client *c, const Arg *arg);
          215 static void togglehelper(Client *c, const Arg *arg, int reload);
          216 static void togglecookiepolicy(Client *c, const Arg *arg);
          217 static void toggleinsecurecontent(Client *c, const Arg *arg);
          218 static void togglegeolocation(Client *c, const Arg *arg);
          219 static void toggleproxy(Client *c, const Arg *arg);
          220 static void togglescrollbars(Client *c, const Arg *arg);
          221 static void togglesoup(Client *c, const Arg *arg);
          222 static void togglestyle(Client *c, const Arg *arg);
          223 static void updatetitle(Client *c);
          224 static void updatewinid(Client *c);
          225 static void usage(void);
          226 static void windowobjectcleared(GtkWidget *w, WebKitWebFrame *frame,
          227                                 JSContextRef js, JSObjectRef win, Client *c);
          228 static void zoom(Client *c, const Arg *arg);
          229 
          230 /* configuration, allows nested code to access above variables */
          231 #include "config.h"
          232 
          233 void
          234 addaccelgroup(Client *c)
          235 {
          236         int i;
          237         GtkAccelGroup *group = gtk_accel_group_new();
          238         GClosure *closure;
          239 
          240         for (i = 0; i < LENGTH(keys); i++) {
          241                 closure = g_cclosure_new(G_CALLBACK(keypress), c, NULL);
          242                 gtk_accel_group_connect(group, keys[i].keyval, keys[i].mod, 0,
          243                                         closure);
          244         }
          245         gtk_window_add_accel_group(GTK_WINDOW(c->win), group);
          246 }
          247 
          248 void
          249 beforerequest(WebKitWebView *w, WebKitWebFrame *f, WebKitWebResource *r,
          250               WebKitNetworkRequest *req, WebKitNetworkResponse *resp,
          251               Client *c)
          252 {
          253         SoupMessage *msg;
          254         SoupMessageHeaders *hdrs;
          255         const gchar *uri = webkit_network_request_get_uri(req);
          256         int i, isascii = 1;
          257 
          258         if (g_str_has_suffix(uri, "/favicon.ico"))
          259                 webkit_network_request_set_uri(req, "about:blank");
          260 
          261         if (!g_str_has_prefix(uri, "http://")
          262                         && !g_str_has_prefix(uri, "https://")
          263                         && !g_str_has_prefix(uri, "about:")
          264                         && !g_str_has_prefix(uri, "file://")
          265                         && !g_str_has_prefix(uri, "data:")
          266                         && !g_str_has_prefix(uri, "blob:")
          267                         && strlen(uri) > 0) {
          268                 for (i = 0; i < strlen(uri); i++) {
          269                         if (!g_ascii_isprint(uri[i])) {
          270                                 isascii = 0;
          271                                 break;
          272                         }
          273                 }
          274                 if (isascii)
          275                         handleplumb(c, w, uri);
          276 
          277                 return;
          278         }
          279         if (g_str_has_prefix(uri, "http://")
          280                         || g_str_has_prefix(uri, "https://")) {
          281                 msg = webkit_network_request_get_message(req);
          282                 g_object_get(G_OBJECT(msg), "request-headers", &hdrs,
          283                                 NULL);
          284                 if (hdrs != NULL) {
          285                         for (i = 0; i < LENGTH(customheaders); i++) {
          286                                 soup_message_headers_replace(hdrs,
          287                                                 customheaders[i].key,
          288                                                 customheaders[i].value);
          289                         }
          290                 }
          291         }
          292 }
          293 
          294 char *
          295 buildfile(const char *path)
          296 {
          297         char *dname, *bname, *bpath, *fpath;
          298         FILE *f;
          299 
          300         dname = g_path_get_dirname(path);
          301         bname = g_path_get_basename(path);
          302 
          303         bpath = buildpath(dname);
          304         g_free(dname);
          305 
          306         fpath = g_build_filename(bpath, bname, NULL);
          307         g_free(bpath);
          308         g_free(bname);
          309 
          310         if (!(f = fopen(fpath, "a")))
          311                 die("Could not open file: %s\n", fpath);
          312 
          313         g_chmod(fpath, 0600); /* always */
          314         fclose(f);
          315 
          316         return fpath;
          317 }
          318 
          319 static const char*
          320 getuserhomedir(const char *user)
          321 {
          322         struct passwd *pw = getpwnam(user);
          323 
          324         if (!pw)
          325                 die("Can't get user %s login information.\n", user);
          326 
          327         return pw->pw_dir;
          328 }
          329 
          330 static const char*
          331 getcurrentuserhomedir(void)
          332 {
          333         const char *homedir;
          334         const char *user;
          335         struct passwd *pw;
          336 
          337         homedir = getenv("HOME");
          338         if (homedir)
          339                 return homedir;
          340 
          341         user = getenv("USER");
          342         if (user)
          343                 return getuserhomedir(user);
          344 
          345         pw = getpwuid(getuid());
          346         if (!pw)
          347                 die("Can't get current user home directory\n");
          348 
          349         return pw->pw_dir;
          350 }
          351 
          352 char *
          353 buildpath(const char *path)
          354 {
          355         char *apath, *name, *p, *fpath;
          356         const char *homedir;
          357 
          358         if (path[0] == '~') {
          359                 if (path[1] == '/' || path[1] == '\0') {
          360                         p = (char *)&path[1];
          361                         homedir = getcurrentuserhomedir();
          362                 } else {
          363                         if ((p = strchr(path, '/'))) {
          364                                 name = g_strndup(&path[1], --p - path);
          365                         } else {
          366                                 name = g_strdup(&path[1]);
          367                         }
          368 
          369                         homedir = getuserhomedir(name);
          370                         g_free(name);
          371                 }
          372                 apath = g_build_filename(homedir, p, NULL);
          373         } else {
          374                 apath = g_strdup(path);
          375         }
          376 
          377         /* creating directory */
          378         if (g_mkdir_with_parents(apath, 0700) < 0)
          379                 die("Could not access directory: %s\n", apath);
          380 
          381         fpath = realpath(apath, NULL);
          382         g_free(apath);
          383 
          384         return fpath;
          385 }
          386 
          387 gboolean
          388 buttonrelease(WebKitWebView *web, GdkEventButton *e, Client *c)
          389 {
          390         WebKitHitTestResultContext context;
          391         WebKitHitTestResult *result;
          392         Arg arg;
          393         unsigned int i;
          394 
          395         result = webkit_web_view_get_hit_test_result(web, e);
          396         g_object_get(result, "context", &context, NULL);
          397         g_object_get(result, "link-uri", &arg.v, NULL);
          398         for (i = 0; i < LENGTH(buttons); i++) {
          399                 if (context & buttons[i].click
          400                     && e->button == buttons[i].button
          401                     && CLEANMASK(e->state) == CLEANMASK(buttons[i].mask)
          402                     && buttons[i].func) {
          403                         buttons[i].func(c, buttons[i].click == ClkLink
          404                                         && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg);
          405                         return true;
          406                 }
          407         }
          408         return false;
          409 }
          410 
          411 void
          412 cleanup(void)
          413 {
          414         if (diskcache) {
          415                 soup_cache_flush(diskcache);
          416                 soup_cache_dump(diskcache);
          417         }
          418         while (clients)
          419                 destroyclient(clients);
          420         g_free(cookiefile);
          421         g_free(scriptfile);
          422         g_free(stylefile);
          423 }
          424 
          425 void
          426 cookiejar_changed(SoupCookieJar *self, SoupCookie *old_cookie,
          427                   SoupCookie *new_cookie)
          428 {
          429         flock(COOKIEJAR(self)->lock, LOCK_EX);
          430         if (new_cookie && !new_cookie->expires && sessiontime) {
          431                 soup_cookie_set_expires(new_cookie,
          432                                         soup_date_new_from_now(sessiontime));
          433         }
          434         SOUP_COOKIE_JAR_CLASS(cookiejar_parent_class)->changed(self,
          435                                                                old_cookie,
          436                                                                new_cookie);
          437         flock(COOKIEJAR(self)->lock, LOCK_UN);
          438 }
          439 
          440 void
          441 cookiejar_class_init(CookieJarClass *klass)
          442 {
          443         SOUP_COOKIE_JAR_CLASS(klass)->changed = cookiejar_changed;
          444         G_OBJECT_CLASS(klass)->get_property =
          445             G_OBJECT_CLASS(cookiejar_parent_class)->get_property;
          446         G_OBJECT_CLASS(klass)->set_property = cookiejar_set_property;
          447         G_OBJECT_CLASS(klass)->finalize = cookiejar_finalize;
          448         g_object_class_override_property(G_OBJECT_CLASS(klass), 1, "filename");
          449 }
          450 
          451 void
          452 cookiejar_finalize(GObject *self)
          453 {
          454         close(COOKIEJAR(self)->lock);
          455         G_OBJECT_CLASS(cookiejar_parent_class)->finalize(self);
          456 }
          457 
          458 void
          459 cookiejar_init(CookieJar *self)
          460 {
          461         self->lock = open(cookiefile, 0);
          462 }
          463 
          464 SoupCookieJar *
          465 cookiejar_new(const char *filename, gboolean read_only,
          466               SoupCookieJarAcceptPolicy policy)
          467 {
          468         return g_object_new(COOKIEJAR_TYPE,
          469                             SOUP_COOKIE_JAR_TEXT_FILENAME, filename,
          470                             SOUP_COOKIE_JAR_READ_ONLY, read_only,
          471                             SOUP_COOKIE_JAR_ACCEPT_POLICY, policy, NULL);
          472 }
          473 
          474 void
          475 cookiejar_set_property(GObject *self, guint prop_id, const GValue *value,
          476                        GParamSpec *pspec)
          477 {
          478         flock(COOKIEJAR(self)->lock, LOCK_SH);
          479         G_OBJECT_CLASS(cookiejar_parent_class)->set_property(self, prop_id,
          480                                                              value, pspec);
          481         flock(COOKIEJAR(self)->lock, LOCK_UN);
          482 }
          483 
          484 SoupCookieJarAcceptPolicy
          485 cookiepolicy_get(void)
          486 {
          487         switch (cookiepolicies[policysel]) {
          488         case 'a':
          489                 return SOUP_COOKIE_JAR_ACCEPT_NEVER;
          490         case '@':
          491                 return SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY;
          492         case 'A':
          493         default:
          494                 break;
          495         }
          496 
          497         return SOUP_COOKIE_JAR_ACCEPT_ALWAYS;
          498 }
          499 
          500 char
          501 cookiepolicy_set(const SoupCookieJarAcceptPolicy ep)
          502 {
          503         switch (ep) {
          504         case SOUP_COOKIE_JAR_ACCEPT_NEVER:
          505                 return 'a';
          506         case SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY:
          507                 return '@';
          508         case SOUP_COOKIE_JAR_ACCEPT_ALWAYS:
          509         default:
          510                 break;
          511         }
          512 
          513         return 'A';
          514 }
          515 
          516 void
          517 evalscript(JSContextRef js, char *script, char *scriptname)
          518 {
          519         JSStringRef jsscript, jsscriptname;
          520         JSValueRef exception = NULL;
          521 
          522         jsscript = JSStringCreateWithUTF8CString(script);
          523         jsscriptname = JSStringCreateWithUTF8CString(scriptname);
          524         JSEvaluateScript(js, jsscript, JSContextGetGlobalObject(js),
          525                          jsscriptname, 0, &exception);
          526         JSStringRelease(jsscript);
          527         JSStringRelease(jsscriptname);
          528 }
          529 
          530 void
          531 runscript(WebKitWebFrame *frame)
          532 {
          533         char *script;
          534         GError *error;
          535 
          536         if (g_file_get_contents(scriptfile, &script, NULL, &error)) {
          537                 evalscript(webkit_web_frame_get_global_context(frame), script,
          538                            scriptfile);
          539         }
          540 }
          541 
          542 void
          543 clipboard(Client *c, const Arg *arg)
          544 {
          545         gboolean paste = *(gboolean *)arg;
          546 
          547         if (paste) {
          548                 gtk_clipboard_request_text(gtk_clipboard_get(
          549                                            GDK_SELECTION_PRIMARY),
          550                                            pasteuri, c);
          551         } else {
          552                 gtk_clipboard_set_text(gtk_clipboard_get(
          553                                        GDK_SELECTION_PRIMARY), c->linkhover
          554                                        ? c->linkhover : geturi(c), -1);
          555         }
          556 }
          557 
          558 char *
          559 copystr(char **str, const char *src)
          560 {
          561         char *tmp;
          562         tmp = g_strdup(src);
          563 
          564         if (str && *str) {
          565                 g_free(*str);
          566                 *str = tmp;
          567         }
          568         return tmp;
          569 }
          570 
          571 WebKitWebView *
          572 createwindow(WebKitWebView  *v, WebKitWebFrame *f, Client *c)
          573 {
          574         Client *n = newclient();
          575         return n->view;
          576 }
          577 
          578 gboolean
          579 decidedownload(WebKitWebView *v, WebKitWebFrame *f, WebKitNetworkRequest *r,
          580                gchar *m,  WebKitWebPolicyDecision *p, Client *c)
          581 {
          582         if (!webkit_web_view_can_show_mime_type(v, m)) {
          583                 webkit_web_policy_decision_download(p);
          584                 return TRUE;
          585         }
          586         return FALSE;
          587 }
          588 
          589 gboolean
          590 decidewindow(WebKitWebView *view, WebKitWebFrame *f, WebKitNetworkRequest *r,
          591              WebKitWebNavigationAction *n, WebKitWebPolicyDecision *p,
          592              Client *c)
          593 {
          594         Arg arg;
          595 
          596         if (webkit_web_navigation_action_get_reason(n)
          597             == WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED) {
          598                 webkit_web_policy_decision_ignore(p);
          599                 arg.v = (void *)webkit_network_request_get_uri(r);
          600                 newwindow(NULL, &arg, 0);
          601                 return TRUE;
          602         }
          603         return FALSE;
          604 }
          605 
          606 gboolean
          607 deletion_interface(WebKitWebView *view, WebKitDOMHTMLElement *arg1, Client *c)
          608 {
          609         return FALSE;
          610 }
          611 
          612 void
          613 destroyclient(Client *c)
          614 {
          615         Client *p;
          616 
          617         webkit_web_view_stop_loading(c->view);
          618         gtk_widget_destroy(GTK_WIDGET(c->view));
          619         gtk_widget_destroy(c->scroll);
          620         gtk_widget_destroy(c->vbox);
          621         gtk_widget_destroy(c->win);
          622 
          623         for (p = clients; p && p->next != c; p = p->next)
          624                 ;
          625         if (p)
          626                 p->next = c->next;
          627         else
          628                 clients = c->next;
          629         free(c);
          630         if (clients == NULL)
          631                 gtk_main_quit();
          632 }
          633 
          634 void
          635 destroywin(GtkWidget* w, Client *c)
          636 {
          637         destroyclient(c);
          638 }
          639 
          640 void
          641 die(const char *errstr, ...)
          642 {
          643         va_list ap;
          644 
          645         va_start(ap, errstr);
          646         vfprintf(stderr, errstr, ap);
          647         va_end(ap);
          648         exit(EXIT_FAILURE);
          649 }
          650 
          651 void
          652 find(Client *c, const Arg *arg)
          653 {
          654         const char *s;
          655 
          656         s = getatom(c, AtomFind);
          657         gboolean forward = *(gboolean *)arg;
          658         webkit_web_view_search_text(c->view, s, FALSE, forward, TRUE);
          659 }
          660 
          661 void
          662 fullscreen(Client *c, const Arg *arg)
          663 {
          664         if (c->fullscreen)
          665                 gtk_window_unfullscreen(GTK_WINDOW(c->win));
          666         else
          667                 gtk_window_fullscreen(GTK_WINDOW(c->win));
          668         c->fullscreen = !c->fullscreen;
          669 }
          670 
          671 void
          672 geopolicyrequested(WebKitWebView *v, WebKitWebFrame *f,
          673                    WebKitGeolocationPolicyDecision *d, Client *c)
          674 {
          675         if (allowgeolocation)
          676                 webkit_geolocation_policy_allow(d);
          677         else
          678                 webkit_geolocation_policy_deny(d);
          679 }
          680 
          681 const char *
          682 getatom(Client *c, int a)
          683 {
          684         static char buf[BUFSIZ];
          685         Atom adummy;
          686         int idummy;
          687         unsigned long ldummy;
          688         unsigned char *p = NULL;
          689 
          690         XGetWindowProperty(dpy, GDK_WINDOW_XID(GTK_WIDGET(c->win)->window),
          691                            atoms[a], 0L, BUFSIZ, False, AnyPropertyType,
          692                            &adummy, &idummy, &ldummy, &ldummy, &p);
          693         if (p)
          694                 strncpy(buf, (char *)p, LENGTH(buf)-1);
          695         else
          696                 buf[0] = '\0';
          697         XFree(p);
          698 
          699         return buf;
          700 }
          701 
          702 char *
          703 geturi(Client *c)
          704 {
          705         char *uri;
          706 
          707         if (!(uri = (char *)webkit_web_view_get_uri(c->view)))
          708                 uri = "about:blank";
          709         return uri;
          710 }
          711 
          712 const gchar *
          713 getstyle(const char *uri)
          714 {
          715         int i;
          716 
          717         if (stylefile != NULL)
          718                 return stylefile;
          719 
          720         for (i = 0; i < LENGTH(styles); i++) {
          721                 if (styles[i].regex && !regexec(&(styles[i].re), uri, 0,
          722                     NULL, 0))
          723                         return styles[i].style;
          724         }
          725 
          726         return "";
          727 }
          728 
          729 void
          730 setstyle(Client *c, const char *style)
          731 {
          732         WebKitWebSettings *settings = webkit_web_view_get_settings(c->view);
          733 
          734         g_object_set(G_OBJECT(settings), "user-stylesheet-uri", style, NULL);
          735 }
          736 
          737 void
          738 handleplumb(Client *c, WebKitWebView *w, const gchar *uri)
          739 {
          740         Arg arg;
          741 
          742         webkit_web_view_stop_loading(w);
          743         arg = (Arg)PLUMB((char *)uri);
          744         spawn(c, &arg);
          745 }
          746 
          747 gboolean
          748 initdownload(WebKitWebView *view, WebKitDownload *o, Client *c)
          749 {
          750         Arg arg;
          751 
          752         updatewinid(c);
          753         arg = (Arg)DOWNLOAD((char *)webkit_download_get_uri(o), geturi(c));
          754         spawn(c, &arg);
          755         return FALSE;
          756 }
          757 
          758 void
          759 inspector(Client *c, const Arg *arg)
          760 {
          761         if (enableinspector) {
          762                 if (c->isinspecting)
          763                         webkit_web_inspector_close(c->inspector);
          764                 else
          765                         webkit_web_inspector_show(c->inspector);
          766         }
          767 }
          768 
          769 WebKitWebView *
          770 inspector_new(WebKitWebInspector *i, WebKitWebView *v, Client *c)
          771 {
          772         return WEBKIT_WEB_VIEW(webkit_web_view_new());
          773 }
          774 
          775 gboolean
          776 inspector_show(WebKitWebInspector *i, Client *c)
          777 {
          778         WebKitWebView *w;
          779 
          780         if (c->isinspecting)
          781                 return false;
          782 
          783         w = webkit_web_inspector_get_web_view(i);
          784         gtk_paned_pack2(GTK_PANED(c->pane), GTK_WIDGET(w), TRUE, TRUE);
          785         gtk_widget_show(GTK_WIDGET(w));
          786         c->isinspecting = true;
          787 
          788         return true;
          789 }
          790 
          791 gboolean
          792 inspector_close(WebKitWebInspector *i, Client *c)
          793 {
          794         GtkWidget *w;
          795 
          796         if (!c->isinspecting)
          797                 return false;
          798 
          799         w = GTK_WIDGET(webkit_web_inspector_get_web_view(i));
          800         gtk_widget_hide(w);
          801         gtk_widget_destroy(w);
          802         c->isinspecting = false;
          803 
          804         return true;
          805 }
          806 
          807 void
          808 inspector_finished(WebKitWebInspector *i, Client *c)
          809 {
          810         g_free(c->inspector);
          811 }
          812 
          813 gboolean
          814 keypress(GtkAccelGroup *group, GObject *obj, guint key, GdkModifierType mods,
          815          Client *c)
          816 {
          817         guint i;
          818         gboolean processed = FALSE;
          819 
          820         mods = CLEANMASK(mods);
          821         key = gdk_keyval_to_lower(key);
          822         updatewinid(c);
          823         for (i = 0; i < LENGTH(keys); i++) {
          824                 if (key == keys[i].keyval
          825                     && mods == keys[i].mod
          826                     && keys[i].func) {
          827                         keys[i].func(c, &(keys[i].arg));
          828                         processed = TRUE;
          829                 }
          830         }
          831 
          832         return processed;
          833 }
          834 
          835 void
          836 linkhover(WebKitWebView *v, const char* t, const char* l, Client *c)
          837 {
          838         if (l) {
          839                 c->linkhover = copystr(&c->linkhover, l);
          840         } else if (c->linkhover) {
          841                 free(c->linkhover);
          842                 c->linkhover = NULL;
          843         }
          844         updatetitle(c);
          845 }
          846 
          847 void
          848 loadstatuschange(WebKitWebView *view, GParamSpec *pspec, Client *c)
          849 {
          850         WebKitWebFrame *frame;
          851         WebKitWebDataSource *src;
          852         WebKitNetworkRequest *request;
          853         SoupMessage *msg;
          854         char *uri;
          855 
          856         switch (webkit_web_view_get_load_status (c->view)) {
          857         case WEBKIT_LOAD_COMMITTED:
          858                 uri = geturi(c);
          859                 if (strstr(uri, "https://") == uri) {
          860                         frame = webkit_web_view_get_main_frame(c->view);
          861                         src = webkit_web_frame_get_data_source(frame);
          862                         request = webkit_web_data_source_get_request(src);
          863                         msg = webkit_network_request_get_message(request);
          864                         c->sslfailed = !(soup_message_get_flags(msg)
          865                                          & SOUP_MESSAGE_CERTIFICATE_TRUSTED);
          866                 }
          867                 setatom(c, AtomUri, uri);
          868                 c->title = copystr(&c->title, uri);
          869 
          870                 if (enablestyle)
          871                         setstyle(c, getstyle(uri));
          872                 break;
          873         case WEBKIT_LOAD_FINISHED:
          874                 c->progress = 100;
          875                 updatetitle(c);
          876                 if (diskcache) {
          877                         soup_cache_flush(diskcache);
          878                         soup_cache_dump(diskcache);
          879                 }
          880                 break;
          881         default:
          882                 break;
          883         }
          884 }
          885 
          886 void
          887 loaduri(Client *c, const Arg *arg)
          888 {
          889         char *u = NULL, *rp;
          890         const char *uri = (char *)arg->v;
          891         Arg a = { .b = FALSE };
          892         struct stat st;
          893 
          894         if (strcmp(uri, "") == 0)
          895                 return;
          896 
          897         /* In case it's a file path. */
          898         if (stat(uri, &st) == 0) {
          899                 rp = realpath(uri, NULL);
          900                 u = g_strdup_printf("file://%s", rp);
          901                 free(rp);
          902         } else {
          903                 u = g_strrstr(uri, "://") || g_str_has_prefix(uri, "about:") ? g_strdup(uri)
          904                     : g_strdup_printf("http://%s", uri);
          905         }
          906 
          907         setatom(c, AtomUri, uri);
          908 
          909 
          910         /* prevents endless loop */
          911         if (strcmp(u, geturi(c)) == 0) {
          912                 reload(c, &a);
          913         } else {
          914                 webkit_web_view_load_uri(c->view, u);
          915                 c->progress = 0;
          916                 c->title = copystr(&c->title, u);
          917                 updatetitle(c);
          918         }
          919         g_free(u);
          920 }
          921 
          922 void
          923 navigate(Client *c, const Arg *arg)
          924 {
          925         int steps = *(int *)arg;
          926         webkit_web_view_go_back_or_forward(c->view, steps);
          927 }
          928 
          929 Client *
          930 newclient(void)
          931 {
          932         Client *c;
          933         WebKitWebSettings *settings;
          934         WebKitWebFrame *frame;
          935         GdkGeometry hints = { 1, 1 };
          936         GdkScreen *screen;
          937         gdouble dpi;
          938         char *ua;
          939 
          940         if (!(c = calloc(1, sizeof(Client))))
          941                 die("Cannot malloc!\n");
          942 
          943         c->title = NULL;
          944         c->progress = 100;
          945 
          946         /* Window */
          947         if (embed) {
          948                 c->win = gtk_plug_new(embed);
          949         } else {
          950                 c->win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
          951 
          952                 /* TA:  20091214:  Despite what the GNOME docs say, the ICCCM
          953                  * is always correct, so we should still call this function.
          954                  * But when doing so, we *must* differentiate between a
          955                  * WM_CLASS and a resource on the window.  By convention, the
          956                  * window class (WM_CLASS) is capped, while the resource is in
          957                  * lowercase.   Both these values come as a pair.
          958                  */
          959                 gtk_window_set_wmclass(GTK_WINDOW(c->win), "surf", "Surf");
          960 
          961                 /* TA:  20091214:  And set the role here as well -- so that
          962                  * sessions can pick this up.
          963                  */
          964                 gtk_window_set_role(GTK_WINDOW(c->win), "Surf");
          965         }
          966         gtk_window_set_default_size(GTK_WINDOW(c->win), 800, 600);
          967         g_signal_connect(G_OBJECT(c->win),
          968                          "destroy",
          969                          G_CALLBACK(destroywin), c);
          970         g_signal_connect(G_OBJECT(c->win),
          971                          "leave_notify_event",
          972                          G_CALLBACK(titlechangeleave), c);
          973 
          974         if (!kioskmode)
          975                 addaccelgroup(c);
          976 
          977         /* Pane */
          978         c->pane = gtk_vpaned_new();
          979 
          980         /* VBox */
          981         c->vbox = gtk_vbox_new(FALSE, 0);
          982         gtk_paned_pack1(GTK_PANED(c->pane), c->vbox, TRUE, TRUE);
          983 
          984         /* Webview */
          985         c->view = WEBKIT_WEB_VIEW(webkit_web_view_new());
          986 
          987         g_signal_connect(G_OBJECT(c->view),
          988                          "notify::title",
          989                          G_CALLBACK(titlechange), c);
          990         g_signal_connect(G_OBJECT(c->view),
          991                          "hovering-over-link",
          992                          G_CALLBACK(linkhover), c);
          993         g_signal_connect(G_OBJECT(c->view),
          994                          "geolocation-policy-decision-requested",
          995                          G_CALLBACK(geopolicyrequested), c);
          996         g_signal_connect(G_OBJECT(c->view),
          997                          "create-web-view",
          998                          G_CALLBACK(createwindow), c);
          999         g_signal_connect(G_OBJECT(c->view),
         1000                          "new-window-policy-decision-requested",
         1001                          G_CALLBACK(decidewindow), c);
         1002         g_signal_connect(G_OBJECT(c->view),
         1003                          "mime-type-policy-decision-requested",
         1004                          G_CALLBACK(decidedownload), c);
         1005         g_signal_connect(G_OBJECT(c->view),
         1006                          "window-object-cleared",
         1007                          G_CALLBACK(windowobjectcleared), c);
         1008         g_signal_connect(G_OBJECT(c->view),
         1009                          "notify::load-status",
         1010                          G_CALLBACK(loadstatuschange), c);
         1011         g_signal_connect(G_OBJECT(c->view),
         1012                          "notify::progress",
         1013                          G_CALLBACK(progresschange), c);
         1014         g_signal_connect(G_OBJECT(c->view),
         1015                          "download-requested",
         1016                          G_CALLBACK(initdownload), c);
         1017         g_signal_connect(G_OBJECT(c->view),
         1018                          "button-release-event",
         1019                          G_CALLBACK(buttonrelease), c);
         1020         g_signal_connect(G_OBJECT(c->view),
         1021                          "context-menu",
         1022                          G_CALLBACK(contextmenu), c);
         1023         g_signal_connect(G_OBJECT(c->view),
         1024                          "resource-request-starting",
         1025                          G_CALLBACK(beforerequest), c);
         1026         g_signal_connect(G_OBJECT(c->view),
         1027                          "should-show-delete-interface-for-element",
         1028                          G_CALLBACK(deletion_interface), c);
         1029 
         1030         /* Scrolled Window */
         1031         c->scroll = gtk_scrolled_window_new(NULL, NULL);
         1032 
         1033         frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(c->view));
         1034         g_signal_connect(G_OBJECT(frame), "scrollbars-policy-changed",
         1035                          G_CALLBACK(gtk_true), NULL);
         1036 
         1037         if (!enablescrollbars) {
         1038                 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(c->scroll),
         1039                                                GTK_POLICY_NEVER,
         1040                                                GTK_POLICY_NEVER);
         1041         } else {
         1042                 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(c->scroll),
         1043                                                GTK_POLICY_AUTOMATIC,
         1044                                                GTK_POLICY_AUTOMATIC);
         1045         }
         1046 
         1047         /* Arranging */
         1048         gtk_container_add(GTK_CONTAINER(c->scroll), GTK_WIDGET(c->view));
         1049         gtk_container_add(GTK_CONTAINER(c->win), c->pane);
         1050         gtk_container_add(GTK_CONTAINER(c->vbox), c->scroll);
         1051 
         1052         /* Setup */
         1053         gtk_box_set_child_packing(GTK_BOX(c->vbox), c->scroll, TRUE, TRUE, 0,
         1054                                   GTK_PACK_START);
         1055         gtk_widget_grab_focus(GTK_WIDGET(c->view));
         1056         gtk_widget_show(c->pane);
         1057         gtk_widget_show(c->vbox);
         1058         gtk_widget_show(c->scroll);
         1059         gtk_widget_show(GTK_WIDGET(c->view));
         1060         gtk_widget_show(c->win);
         1061         gtk_window_set_geometry_hints(GTK_WINDOW(c->win), NULL, &hints,
         1062                                       GDK_HINT_MIN_SIZE);
         1063         gdk_window_set_events(GTK_WIDGET(c->win)->window, GDK_ALL_EVENTS_MASK);
         1064         gdk_window_add_filter(GTK_WIDGET(c->win)->window, processx, c);
         1065         webkit_web_view_set_full_content_zoom(c->view, TRUE);
         1066 
         1067         runscript(frame);
         1068 
         1069         settings = webkit_web_view_get_settings(c->view);
         1070         g_object_set(G_OBJECT(settings), "html5-local-storage-database-path",
         1071                         dbfolder, NULL);
         1072         g_object_set(G_OBJECT(settings),
         1073                         "enable-offline-web-application-cache",
         1074                         offlineappcache, NULL);
         1075         g_object_set(G_OBJECT(settings),
         1076                         "enable-page-cache", enablepagecache, NULL);
         1077         g_object_set(G_OBJECT(settings),
         1078                         "enable-private-browsing", privatebrowsing, NULL);
         1079         g_object_set(G_OBJECT(settings), "enable-dns-prefetching",
         1080                         dnsprefetching, NULL);
         1081 
         1082         if (!(ua = getenv("SURF_USERAGENT")))
         1083                 ua = useragent;
         1084         g_object_set(G_OBJECT(settings), "user-agent", ua, NULL);
         1085         setatom(c, AtomUA, ua);
         1086 
         1087         g_object_set(G_OBJECT(settings),
         1088                      "auto-load-images", loadimages, NULL);
         1089         g_object_set(G_OBJECT(settings),
         1090                      "enable-plugins", enableplugins, NULL);
         1091         g_object_set(G_OBJECT(settings),
         1092                      "enable-scripts", enablescripts, NULL);
         1093         g_object_set(G_OBJECT(settings),
         1094                      "enable-spatial-navigation", enablespatialbrowsing, NULL);
         1095         g_object_set(G_OBJECT(settings),
         1096                      "enable-spell-checking", enablespellchecking, NULL);
         1097         g_object_set(G_OBJECT(settings),
         1098                      "media-playback-allows-inline", inlineplayback, NULL);
         1099         g_object_set(G_OBJECT(settings),
         1100                      "media-playback-requires-user-gesture", inlinegestures, NULL);
         1101         g_object_set(G_OBJECT(settings),
         1102                      "enable-webaudio", enablewebaudio, NULL);
         1103         g_object_set(G_OBJECT(settings),
         1104                      "enable-webgl", enablewebgl, NULL);
         1105         g_object_set(G_OBJECT(settings),
         1106                      "enable-developer-extras", enableinspector, NULL);
         1107         g_object_set(G_OBJECT(settings),
         1108                      "enable-default-context-menu", kioskmode ^ 1, NULL);
         1109         g_object_set(G_OBJECT(settings),
         1110                      "default-font-size", defaultfontsize, NULL);
         1111         g_object_set(G_OBJECT(settings),
         1112                      "default-monospace-font-size", defaultmonofontsize, NULL);
         1113         g_object_set(G_OBJECT(settings),
         1114                      "resizable-text-areas", 1, NULL);
         1115         g_object_set(G_OBJECT(settings),
         1116                      "default-encoding", defaultencoding, NULL);
         1117         g_object_set(G_OBJECT(settings),
         1118                      "enable-accelerated-compositing", accelrendering, NULL);
         1119         g_object_set(G_OBJECT(settings),
         1120                      "enable-display-of-insecure-content", insecureresources,
         1121                      NULL);
         1122         g_object_set(G_OBJECT(settings),
         1123                      "enable-running-of-insecure-content", insecureresources,
         1124                      NULL);
         1125         g_object_set(G_OBJECT(settings),
         1126                      "enable-html5-database", enablehtml5db, NULL);
         1127         g_object_set(G_OBJECT(settings),
         1128                      "enable-html5-local-storage", enablehtml5local, NULL);
         1129         g_object_set(G_OBJECT(settings),
         1130                      "enable-java-applet", enablejava, NULL);
         1131         g_object_set(G_OBJECT(settings),
         1132                      "enable-media-stream", enablemediastream, NULL);
         1133         g_object_set(G_OBJECT(settings),
         1134                      "enable-mediasource", enablemediasource, NULL);
         1135         if (enablestyle)
         1136                 setstyle(c, getstyle("about:blank"));
         1137 
         1138         /*
         1139          * While stupid, CSS specifies that a pixel represents 1/96 of an inch.
         1140          * This ensures websites are not unusably small with a high DPI screen.
         1141          * It is equivalent to firefox's "layout.css.devPixelsPerPx" setting.
         1142          */
         1143         if (zoomto96dpi) {
         1144                 screen = gdk_window_get_screen(GTK_WIDGET(c->win)->window);
         1145                 dpi = gdk_screen_get_resolution(screen);
         1146                 if (dpi != -1) {
         1147                         g_object_set(G_OBJECT(settings),
         1148                                      "enforce-96-dpi", true, NULL);
         1149                         webkit_web_view_set_zoom_level(c->view, dpi/96);
         1150                 }
         1151         }
         1152         /* This might conflict with _zoomto96dpi_. */
         1153         if (zoomlevel != 1.0)
         1154                 webkit_web_view_set_zoom_level(c->view, zoomlevel);
         1155 
         1156         if (enableinspector) {
         1157                 c->inspector = webkit_web_view_get_inspector(c->view);
         1158                 g_signal_connect(G_OBJECT(c->inspector), "inspect-web-view",
         1159                                  G_CALLBACK(inspector_new), c);
         1160                 g_signal_connect(G_OBJECT(c->inspector), "show-window",
         1161                                  G_CALLBACK(inspector_show), c);
         1162                 g_signal_connect(G_OBJECT(c->inspector), "close-window",
         1163                                  G_CALLBACK(inspector_close), c);
         1164                 g_signal_connect(G_OBJECT(c->inspector), "finished",
         1165                                  G_CALLBACK(inspector_finished), c);
         1166                 c->isinspecting = false;
         1167         }
         1168 
         1169         if (runinfullscreen)
         1170                 fullscreen(c, NULL);
         1171 
         1172         setatom(c, AtomFind, "");
         1173         setatom(c, AtomUri, "about:blank");
         1174         if (hidebackground)
         1175                 webkit_web_view_set_transparent(c->view, TRUE);
         1176 
         1177         c->next = clients;
         1178         clients = c;
         1179 
         1180         if (showxid) {
         1181                 gdk_display_sync(gtk_widget_get_display(c->win));
         1182                 printf("%u\n",
         1183                        (guint)GDK_WINDOW_XID(GTK_WIDGET(c->win)->window));
         1184                 fflush(NULL);
         1185                 if (fclose(stdout) != 0)
         1186                         die("Error closing stdout");
         1187         }
         1188 
         1189         return c;
         1190 }
         1191 
         1192 void
         1193 newwindow(Client *c, const Arg *arg, gboolean noembed)
         1194 {
         1195         guint i = 0;
         1196         const char *cmd[30], *uri;
         1197         const Arg a = { .v = (void *)cmd };
         1198         char tmp[64], ztmp[6];
         1199 
         1200         cmd[i++] = argv0;
         1201 
         1202         if (cookiepolicies != NULL) {
         1203                 cmd[i++] = "-a";
         1204                 cmd[i++] = cookiepolicies;
         1205         }
         1206 
         1207         if (enablescrollbars)
         1208                 cmd[i++] = "-B";
         1209         else
         1210                 cmd[i++] = "-b";
         1211 
         1212         if (cookiefile != NULL) {
         1213                 cmd[i++] = "-c";
         1214                 cmd[i++] = cookiefile;
         1215         }
         1216 
         1217         if (enablediskcache)
         1218                 cmd[i++] = "-D";
         1219         else
         1220                 cmd[i++] = "-d";
         1221 
         1222         if (embed && !noembed) {
         1223                 cmd[i++] = "-e";
         1224                 snprintf(tmp, LENGTH(tmp), "%u", (int)embed);
         1225                 cmd[i++] = tmp;
         1226         }
         1227 
         1228         if (runinfullscreen)
         1229                 cmd[i++] = "-F";
         1230         else
         1231                 cmd[i++] = "-f";
         1232 
         1233         if (allowgeolocation)
         1234                 cmd[i++] = "-G";
         1235         else
         1236                 cmd[i++] = "-g";
         1237 
         1238         if (loadimages)
         1239                 cmd[i++] = "-I";
         1240         else
         1241                 cmd[i++] = "-i";
         1242 
         1243         if (kioskmode)
         1244                 cmd[i++] = "-K";
         1245         else
         1246                 cmd[i++] = "-k";
         1247 
         1248         if (insecureresources)
         1249                 cmd[i++] = "-L";
         1250         else
         1251                 cmd[i++] = "-l";
         1252 
         1253         if (enablestyle)
         1254                 cmd[i++] = "-M";
         1255         else
         1256                 cmd[i++] = "-m";
         1257 
         1258         if (enableinspector)
         1259                 cmd[i++] = "-N";
         1260         else
         1261                 cmd[i++] = "-n";
         1262 
         1263         if (enableplugins)
         1264                 cmd[i++] = "-P";
         1265         else
         1266                 cmd[i++] = "-p";
         1267 
         1268         if (scriptfile != NULL) {
         1269                 cmd[i++] = "-r";
         1270                 cmd[i++] = scriptfile;
         1271         }
         1272 
         1273         if (enablescripts)
         1274                 cmd[i++] = "-S";
         1275         else
         1276                 cmd[i++] = "-s";
         1277 
         1278         if (strictssl)
         1279                 cmd[i++] = "-T";
         1280         else
         1281                 cmd[i++] = "-t";
         1282 
         1283         if (useragent != NULL) {
         1284                 cmd[i++] = "-u";
         1285                 cmd[i++] = useragent;
         1286         }
         1287 
         1288         if (privatebrowsing)
         1289                 cmd[i++] = "-W";
         1290         else
         1291                 cmd[i++] = "-w";
         1292 
         1293 
         1294         if (showxid)
         1295                 cmd[i++] = "-x";
         1296 
         1297         if (zoomlevel != 1.0) {
         1298                 cmd[i++] = "-z";
         1299                 snprintf(ztmp, LENGTH(ztmp), "%.1f", zoomlevel);
         1300                 cmd[i++] = ztmp;
         1301         }
         1302 
         1303         cmd[i++] = "--";
         1304         uri = arg->v? (char *)arg->v : c->linkhover;
         1305         if (uri)
         1306                 cmd[i++] = uri;
         1307         cmd[i++] = NULL;
         1308         spawn(NULL, &a);
         1309 }
         1310 
         1311 gboolean
         1312 contextmenu(WebKitWebView *view, GtkWidget *menu, WebKitHitTestResult *target,
         1313             gboolean keyboard, Client *c)
         1314 {
         1315         GList *items = gtk_container_get_children(GTK_CONTAINER(GTK_MENU(menu)));
         1316 
         1317         for (GList *l = items; l; l = l->next)
         1318                 g_signal_connect(l->data, "activate", G_CALLBACK(menuactivate), c);
         1319 
         1320         g_list_free(items);
         1321         return FALSE;
         1322 }
         1323 
         1324 void
         1325 menuactivate(GtkMenuItem *item, Client *c)
         1326 {
         1327         /*
         1328          * context-menu-action-2000 open link
         1329          * context-menu-action-1    open link in window
         1330          * context-menu-action-2    download linked file
         1331          * context-menu-action-3    copy link location
         1332          * context-menu-action-7    copy image address
         1333          * context-menu-action-13   reload
         1334          * context-menu-action-10   back
         1335          * context-menu-action-11   forward
         1336          * context-menu-action-12   stop
         1337          */
         1338 
         1339         GtkAction *a = NULL;
         1340         const char *name, *uri;
         1341         GtkClipboard *prisel, *clpbrd;
         1342 
         1343         a = gtk_activatable_get_related_action(GTK_ACTIVATABLE(item));
         1344         if (a == NULL)
         1345                 return;
         1346 
         1347         name = gtk_action_get_name(a);
         1348         if (!g_strcmp0(name, "context-menu-action-3")) {
         1349                 prisel = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
         1350                 gtk_clipboard_set_text(prisel, c->linkhover, -1);
         1351         } else if (!g_strcmp0(name, "context-menu-action-7")) {
         1352                 prisel = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
         1353                 clpbrd = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
         1354                 uri = gtk_clipboard_wait_for_text(clpbrd);
         1355                 if (uri)
         1356                         gtk_clipboard_set_text(prisel, uri, -1);
         1357         }
         1358 }
         1359 
         1360 void
         1361 pasteuri(GtkClipboard *clipboard, const char *text, gpointer d)
         1362 {
         1363         Arg arg = {.v = text };
         1364         if (text != NULL)
         1365                 loaduri((Client *) d, &arg);
         1366 }
         1367 
         1368 void
         1369 print(Client *c, const Arg *arg)
         1370 {
         1371         webkit_web_frame_print(webkit_web_view_get_main_frame(c->view));
         1372 }
         1373 
         1374 GdkFilterReturn
         1375 processx(GdkXEvent *e, GdkEvent *event, gpointer d)
         1376 {
         1377         Client *c = (Client *)d;
         1378         WebKitWebSettings *settings;
         1379         XPropertyEvent *ev;
         1380         Arg arg;
         1381 
         1382         if (((XEvent *)e)->type == PropertyNotify) {
         1383                 ev = &((XEvent *)e)->xproperty;
         1384                 if (ev->state == PropertyNewValue) {
         1385                         if (ev->atom == atoms[AtomFind]) {
         1386                                 arg.b = TRUE;
         1387                                 find(c, &arg);
         1388                                 return GDK_FILTER_REMOVE;
         1389                         } else if (ev->atom == atoms[AtomGo]) {
         1390                                 arg.v = getatom(c, AtomGo);
         1391                                 loaduri(c, &arg);
         1392                                 return GDK_FILTER_REMOVE;
         1393                         } else if (ev->atom == atoms[AtomUA]) {
         1394                                 settings = webkit_web_view_get_settings(c->view);
         1395                                 g_object_set(G_OBJECT(settings), "user-agent",
         1396                                                 getatom(c, AtomUA), NULL);
         1397                                 return GDK_FILTER_REMOVE;
         1398                         }
         1399                 }
         1400         }
         1401         return GDK_FILTER_CONTINUE;
         1402 }
         1403 
         1404 void
         1405 progresschange(WebKitWebView *view, GParamSpec *pspec, Client *c)
         1406 {
         1407         c->progress = webkit_web_view_get_progress(c->view) * 100;
         1408         updatetitle(c);
         1409 }
         1410 
         1411 void
         1412 linkopen(Client *c, const Arg *arg)
         1413 {
         1414         newwindow(NULL, arg, 1);
         1415 }
         1416 
         1417 void
         1418 linkopenembed(Client *c, const Arg *arg)
         1419 {
         1420         newwindow(NULL, arg, 0);
         1421 }
         1422 
         1423 void
         1424 reload(Client *c, const Arg *arg)
         1425 {
         1426         gboolean nocache = *(gboolean *)arg;
         1427         if (nocache)
         1428                 webkit_web_view_reload_bypass_cache(c->view);
         1429         else
         1430                 webkit_web_view_reload(c->view);
         1431 }
         1432 
         1433 void
         1434 scroll_h(Client *c, const Arg *arg)
         1435 {
         1436         scroll(gtk_scrolled_window_get_hadjustment(
         1437                GTK_SCROLLED_WINDOW(c->scroll)), arg);
         1438 }
         1439 
         1440 void
         1441 scroll_v(Client *c, const Arg *arg)
         1442 {
         1443         scroll(gtk_scrolled_window_get_vadjustment(
         1444                GTK_SCROLLED_WINDOW(c->scroll)), arg);
         1445 }
         1446 
         1447 void
         1448 scroll(GtkAdjustment *a, const Arg *arg)
         1449 {
         1450         gdouble v;
         1451 
         1452         v = gtk_adjustment_get_value(a);
         1453         switch (arg->i) {
         1454         case +10000:
         1455         case -10000:
         1456                 v += gtk_adjustment_get_page_increment(a) * (arg->i / 10000);
         1457                 break;
         1458         case +20000:
         1459         case -20000:
         1460         default:
         1461                 v += gtk_adjustment_get_step_increment(a) * arg->i;
         1462         }
         1463 
         1464         v = MAX(v, 0.0);
         1465         v = MIN(v, gtk_adjustment_get_upper(a) -
         1466                 gtk_adjustment_get_page_size(a));
         1467         gtk_adjustment_set_value(a, v);
         1468 }
         1469 
         1470 void
         1471 setatom(Client *c, int a, const char *v)
         1472 {
         1473         XSync(dpy, False);
         1474         XChangeProperty(dpy, GDK_WINDOW_XID(GTK_WIDGET(c->win)->window),
         1475                         atoms[a], XA_STRING, 8, PropModeReplace,
         1476                         (unsigned char *)v, strlen(v) + 1);
         1477         XSync(dpy, False);
         1478 }
         1479 
         1480 void
         1481 setup(void)
         1482 {
         1483         int i;
         1484         char *styledirfile, *stylepath;
         1485         SoupSession *s;
         1486         GError *error = NULL;
         1487 
         1488         /* clean up any zombies immediately */
         1489         sigchld(0);
         1490         if (signal(SIGHUP, sighup) == SIG_ERR)
         1491                 die("Can't install SIGHUP handler");
         1492         gtk_init(NULL, NULL);
         1493 
         1494         dpy = GDK_DISPLAY();
         1495 
         1496         /* atoms */
         1497         atoms[AtomFind] = XInternAtom(dpy, "_SURF_FIND", False);
         1498         atoms[AtomGo] = XInternAtom(dpy, "_SURF_GO", False);
         1499         atoms[AtomUri] = XInternAtom(dpy, "_SURF_URI", False);
         1500         atoms[AtomUA] = XInternAtom(dpy, "_SURF_UA", False);
         1501 
         1502         /* dirs and files */
         1503         cookiefile = buildfile(cookiefile);
         1504         scriptfile = buildfile(scriptfile);
         1505         cachefolder = buildpath(cachefolder);
         1506         dbfolder = buildpath(dbfolder);
         1507         if (stylefile == NULL) {
         1508                 styledir = buildpath(styledir);
         1509                 for (i = 0; i < LENGTH(styles); i++) {
         1510                         if (regcomp(&(styles[i].re), styles[i].regex,
         1511                             REG_EXTENDED)) {
         1512                                 fprintf(stderr,
         1513                                         "Could not compile regex: %s\n",
         1514                                         styles[i].regex);
         1515                                 styles[i].regex = NULL;
         1516                         }
         1517                         styledirfile    = g_strconcat(styledir, "/",
         1518                                                       styles[i].style, NULL);
         1519                         stylepath       = buildfile(styledirfile);
         1520                         styles[i].style = g_strconcat("file://", stylepath,
         1521                                                       NULL);
         1522                         g_free(styledirfile);
         1523                         g_free(stylepath);
         1524                 }
         1525                 g_free(styledir);
         1526         } else {
         1527                 stylepath = buildfile(stylefile);
         1528                 stylefile = g_strconcat("file://", stylepath, NULL);
         1529                 g_free(stylepath);
         1530         }
         1531 
         1532         /* request handler */
         1533         s = webkit_get_default_session();
         1534 
         1535         /* cookie jar */
         1536         soup_session_add_feature(s,
         1537                                  SOUP_SESSION_FEATURE(cookiejar_new(cookiefile,
         1538                                  FALSE, cookiepolicy_get())));
         1539 
         1540         /* disk cache */
         1541         if (enablediskcache) {
         1542                 diskcache = soup_cache_new(cachefolder,
         1543                                            SOUP_CACHE_SINGLE_USER);
         1544                 soup_cache_set_max_size(diskcache, diskcachebytes);
         1545                 soup_cache_load(diskcache);
         1546                 soup_session_add_feature(s, SOUP_SESSION_FEATURE(diskcache));
         1547         }
         1548 
         1549         /* ssl */
         1550         tlsdb = g_tls_file_database_new(cafile, &error);
         1551         if (error) {
         1552                 g_warning("Error loading SSL database %s: %s", cafile,
         1553                           error->message);
         1554                 g_error_free(error);
         1555         }
         1556         g_object_set(G_OBJECT(s), "tls-database", tlsdb, NULL);
         1557         g_object_set(G_OBJECT(s), "ssl-strict", strictssl, NULL);
         1558 
         1559         setup_proxy();
         1560 }
         1561 
         1562 void
         1563 setup_proxy(void)
         1564 {
         1565         char *proxy, *new_proxy, *no_proxy, **new_no_proxy;
         1566         GProxyResolver *pr;
         1567         SoupSession *s;
         1568 
         1569         /* request handler */
         1570         s = webkit_get_default_session();
         1571 
         1572         /* proxy */
         1573         if ((proxy = getenv("http_proxy")) && strcmp(proxy, "")) {
         1574                 new_proxy = g_strrstr(proxy, "http://")
         1575                             || g_strrstr(proxy, "https://")
         1576                             || g_strrstr(proxy, "socks://")
         1577                             || g_strrstr(proxy, "socks4://")
         1578                             || g_strrstr(proxy, "socks4a://")
         1579                             || g_strrstr(proxy, "socks5://")
         1580                             ? g_strdup(proxy)
         1581                             : g_strdup_printf("http://%s", proxy);
         1582                 new_no_proxy = ((no_proxy = getenv("no_proxy")) && strcmp(no_proxy, ""))
         1583                                ? g_strsplit(no_proxy, ",", -1) : NULL;
         1584                 pr = g_simple_proxy_resolver_new(new_proxy, new_no_proxy);
         1585                 g_object_set(G_OBJECT(s), "proxy-resolver", pr, NULL);
         1586                 g_free(new_proxy);
         1587                 g_strfreev(new_no_proxy);
         1588                 usingproxy = 1;
         1589         } else {
         1590                 usingproxy = 0;
         1591         }
         1592 }
         1593 
         1594 void
         1595 sigchld(int unused)
         1596 {
         1597         if (signal(SIGCHLD, sigchld) == SIG_ERR)
         1598                 die("Can't install SIGCHLD handler");
         1599         while (0 < waitpid(-1, NULL, WNOHANG));
         1600 }
         1601 
         1602 void
         1603 sighup(int unused)
         1604 {
         1605         Arg a = { .b = FALSE };
         1606         Client *c;
         1607 
         1608         for (c = clients; c; c = c->next)
         1609                 reload(c, &a);
         1610 }
         1611 
         1612 void
         1613 source(Client *c, const Arg *arg)
         1614 {
         1615         Arg a = { .b = FALSE };
         1616         gboolean s;
         1617 
         1618         s = webkit_web_view_get_view_source_mode(c->view);
         1619         webkit_web_view_set_view_source_mode(c->view, !s);
         1620         reload(c, &a);
         1621 }
         1622 
         1623 void
         1624 spawn(Client *c, const Arg *arg)
         1625 {
         1626         if (fork() == 0) {
         1627                 if (dpy)
         1628                         close(ConnectionNumber(dpy));
         1629                 setsid();
         1630                 execvp(((char **)arg->v)[0], (char **)arg->v);
         1631                 fprintf(stderr, "surf: execvp %s", ((char **)arg->v)[0]);
         1632                 perror(" failed");
         1633                 exit(0);
         1634         }
         1635 }
         1636 
         1637 void
         1638 eval(Client *c, const Arg *arg)
         1639 {
         1640         WebKitWebFrame *frame = webkit_web_view_get_main_frame(c->view);
         1641         evalscript(webkit_web_frame_get_global_context(frame),
         1642                    ((char **)arg->v)[0], "");
         1643 }
         1644 
         1645 void
         1646 stop(Client *c, const Arg *arg)
         1647 {
         1648         webkit_web_view_stop_loading(c->view);
         1649 }
         1650 
         1651 void
         1652 titlechange(WebKitWebView *view, GParamSpec *pspec, Client *c)
         1653 {
         1654         const gchar *t = webkit_web_view_get_title(view);
         1655 
         1656         if (t) {
         1657                 c->title = copystr(&c->title, t);
         1658                 updatetitle(c);
         1659         }
         1660 }
         1661 
         1662 void
         1663 titlechangeleave(void *a, void *b, Client *c)
         1664 {
         1665         c->linkhover = NULL;
         1666         updatetitle(c);
         1667 }
         1668 
         1669 void
         1670 togglehelper(Client *c, const Arg *arg, int doreload)
         1671 {
         1672         WebKitWebSettings *settings;
         1673         char *name = (char *)arg->v;
         1674         gboolean value;
         1675         Arg a = { .b = FALSE };
         1676 
         1677         settings = webkit_web_view_get_settings(c->view);
         1678         g_object_get(G_OBJECT(settings), name, &value, NULL);
         1679         g_object_set(G_OBJECT(settings), name, !value, NULL);
         1680 
         1681         if (doreload)
         1682                 reload(c, &a);
         1683 }
         1684 
         1685 void
         1686 toggle(Client *c, const Arg *arg)
         1687 {
         1688         togglehelper(c, arg, 1);
         1689 }
         1690 
         1691 void
         1692 togglecookiepolicy(Client *c, const Arg *arg)
         1693 {
         1694         SoupCookieJar *jar;
         1695         SoupCookieJarAcceptPolicy policy;
         1696 
         1697         jar = SOUP_COOKIE_JAR(soup_session_get_feature(
         1698                               webkit_get_default_session(),
         1699                               SOUP_TYPE_COOKIE_JAR));
         1700         g_object_get(G_OBJECT(jar), "accept-policy", &policy, NULL);
         1701 
         1702         policysel++;
         1703         if (policysel >= strlen(cookiepolicies))
         1704                 policysel = 0;
         1705 
         1706         g_object_set(G_OBJECT(jar), "accept-policy", cookiepolicy_get(), NULL);
         1707 
         1708         updatetitle(c);
         1709         /* Do not reload. */
         1710 }
         1711 
         1712 void
         1713 toggleinsecurecontent(Client *c, const Arg *arg)
         1714 {
         1715         Arg a;
         1716 
         1717         a.v = "enable-running-of-insecure-content";
         1718         togglehelper(c, &a, 0);
         1719         a.v = "enable-display-of-insecure-content";
         1720         togglehelper(c, &a, 1);
         1721 }
         1722 
         1723 void
         1724 togglegeolocation(Client *c, const Arg *arg)
         1725 {
         1726         Arg a = { .b = FALSE };
         1727 
         1728         allowgeolocation ^= 1;
         1729         reload(c, &a);
         1730 }
         1731 
         1732 void
         1733 togglesoup(Client *c, const Arg *arg)
         1734 {
         1735         SoupSession *s;
         1736         char *name = (char *)arg->v;
         1737         gboolean value;
         1738         Arg a = { .b = FALSE };
         1739 
         1740         /* request handler */
         1741         s = webkit_get_default_session();
         1742         g_object_get(G_OBJECT(s), name, &value, NULL);
         1743         g_object_set(G_OBJECT(s), name, !value, NULL);
         1744 
         1745         reload(c, &a);
         1746 }
         1747 
         1748 void
         1749 twitch(Client *c, const Arg *arg)
         1750 {
         1751         GtkAdjustment *a;
         1752         gdouble v;
         1753 
         1754         a = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(
         1755                                                 c->scroll));
         1756 
         1757         v = gtk_adjustment_get_value(a);
         1758 
         1759         v += arg->i;
         1760 
         1761         v = MAX(v, 0.0);
         1762         v = MIN(v, gtk_adjustment_get_upper(a) -
         1763                 gtk_adjustment_get_page_size(a));
         1764         gtk_adjustment_set_value(a, v);
         1765 }
         1766 
         1767 void
         1768 toggleproxy(Client *c, const Arg *arg)
         1769 {
         1770         SoupSession *s;
         1771         GProxyResolver *pr;
         1772 
         1773         /* request handler */
         1774         s = webkit_get_default_session();
         1775 
         1776         if (usingproxy) {
         1777                 pr = NULL;
         1778                 g_object_get(G_OBJECT(s), "proxy-resolver", &pr, NULL);
         1779                 if (pr != NULL)
         1780                         g_object_unref(pr);
         1781 
         1782                 g_object_set(G_OBJECT(s), "proxy-resolver", NULL, NULL);
         1783                 usingproxy = 0;
         1784         } else {
         1785                 setup_proxy();
         1786         }
         1787 
         1788         updatetitle(c);
         1789         /* Do not reload. */
         1790 }
         1791 
         1792 void
         1793 togglescrollbars(Client *c, const Arg *arg)
         1794 {
         1795         GtkPolicyType vspolicy;
         1796         Arg a;
         1797 
         1798         gtk_scrolled_window_get_policy(GTK_SCROLLED_WINDOW(c->scroll), NULL,
         1799                                        &vspolicy);
         1800 
         1801         if (vspolicy == GTK_POLICY_AUTOMATIC) {
         1802                 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(c->scroll),
         1803                                                GTK_POLICY_NEVER,
         1804                                                GTK_POLICY_NEVER);
         1805         } else {
         1806                 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(c->scroll),
         1807                                                GTK_POLICY_AUTOMATIC,
         1808                                                GTK_POLICY_AUTOMATIC);
         1809                 a.i = +1;
         1810                 twitch(c, &a);
         1811                 a.i = -1;
         1812                 twitch(c, &a);
         1813         }
         1814 }
         1815 
         1816 void
         1817 togglestyle(Client *c, const Arg *arg)
         1818 {
         1819         enablestyle = !enablestyle;
         1820         setstyle(c, enablestyle ? getstyle(geturi(c)) : "");
         1821         updatetitle(c);
         1822 }
         1823 
         1824 void
         1825 gettogglestat(Client *c)
         1826 {
         1827         gboolean value;
         1828         int p = 0;
         1829         WebKitWebSettings *settings = webkit_web_view_get_settings(c->view);
         1830         SoupSession *s = webkit_get_default_session();
         1831 
         1832         togglestat[p++] = cookiepolicy_set(cookiepolicy_get());
         1833 
         1834         g_object_get(G_OBJECT(settings), "enable-caret-browsing", &value,
         1835                      NULL);
         1836         togglestat[p++] = value? 'C': 'c';
         1837 
         1838         togglestat[p++] = enablediskcache? 'D': 'd';
         1839 
         1840         togglestat[p++] = allowgeolocation? 'G': 'g';
         1841 
         1842         g_object_get(G_OBJECT(settings), "auto-load-images", &value, NULL);
         1843         togglestat[p++] = value? 'I': 'i';
         1844 
         1845         g_object_get(G_OBJECT(settings),
         1846                         "enable-display-of-insecure-content", &value, NULL);
         1847         togglestat[p++] = value? 'L': 'l';
         1848 
         1849         togglestat[p++] = enablestyle ? 'M': 'm';
         1850 
         1851         g_object_get(G_OBJECT(settings), "enable-scripts", &value, NULL);
         1852         togglestat[p++] = value? 'S': 's';
         1853 
         1854         g_object_get(G_OBJECT(s), "ssl-strict", &value, NULL);
         1855         togglestat[p++] = value? 'T': 't';
         1856 
         1857         g_object_get(G_OBJECT(settings), "enable-plugins", &value, NULL);
         1858         togglestat[p++] = value? 'V': 'v';
         1859 
         1860         g_object_get(G_OBJECT(settings), "enable-private-browsing", &value,
         1861                         NULL);
         1862         togglestat[p++] = value? 'W': 'w';
         1863 
         1864         togglestat[p] = '\0';
         1865 }
         1866 
         1867 void
         1868 getpagestat(Client *c)
         1869 {
         1870         const char *uri = geturi(c);
         1871 
         1872         if (strstr(uri, "https://") == uri)
         1873                 pagestat[0] = c->sslfailed ? 'U' : 'T';
         1874         else
         1875                 pagestat[0] = '-';
         1876 
         1877         pagestat[1] = usingproxy ? 'P' : '-';
         1878         pagestat[2] = '\0';
         1879 }
         1880 
         1881 void
         1882 updatetitle(Client *c)
         1883 {
         1884         char *t;
         1885 
         1886         if (showindicators) {
         1887                 gettogglestat(c);
         1888                 getpagestat(c);
         1889 
         1890                 if (c->linkhover) {
         1891                         t = g_strdup_printf("%s:%s | %s", togglestat, pagestat,
         1892                                             c->linkhover);
         1893                 } else if (c->progress != 100) {
         1894                         t = g_strdup_printf("[%i%%] %s:%s | %s", c->progress,
         1895                                             togglestat, pagestat,
         1896                                             c->title == NULL ? "" : c->title);
         1897                 } else {
         1898                         t = g_strdup_printf("%s:%s | %s", togglestat, pagestat,
         1899                                             c->title == NULL ? "" : c->title);
         1900                 }
         1901 
         1902                 gtk_window_set_title(GTK_WINDOW(c->win), t);
         1903                 g_free(t);
         1904         } else {
         1905                 gtk_window_set_title(GTK_WINDOW(c->win), (c->title == NULL) ?
         1906                                      "" : c->title);
         1907         }
         1908 }
         1909 
         1910 void
         1911 updatewinid(Client *c)
         1912 {
         1913         snprintf(winid, LENGTH(winid), "%u",
         1914                  (int)GDK_WINDOW_XID(GTK_WIDGET(c->win)->window));
         1915 }
         1916 
         1917 void
         1918 usage(void)
         1919 {
         1920         die("usage: %s [-bBdDfFgGiIkKlLmMnNpPsStTvwWx] [-a cookiepolicies ] "
         1921             "[-c cookiefile] [-e xid] [-r scriptfile] [-y stylefile] "
         1922             "[-u useragent] [-z zoomlevel] [uri]\n", basename(argv0));
         1923 }
         1924 
         1925 void
         1926 windowobjectcleared(GtkWidget *w, WebKitWebFrame *frame, JSContextRef js,
         1927                     JSObjectRef win, Client *c)
         1928 {
         1929         runscript(frame);
         1930 }
         1931 
         1932 void
         1933 zoom(Client *c, const Arg *arg)
         1934 {
         1935         c->zoomed = TRUE;
         1936         if (arg->i < 0) {
         1937                 /* zoom out */
         1938                 webkit_web_view_zoom_out(c->view);
         1939         } else if (arg->i > 0) {
         1940                 /* zoom in */
         1941                 webkit_web_view_zoom_in(c->view);
         1942         } else {
         1943                 /* reset */
         1944                 c->zoomed = FALSE;
         1945                 webkit_web_view_set_zoom_level(c->view, 1.0);
         1946         }
         1947 }
         1948 
         1949 int
         1950 main(int argc, char *argv[])
         1951 {
         1952         Arg arg;
         1953         Client *c;
         1954 
         1955         memset(&arg, 0, sizeof(arg));
         1956 
         1957         /* command line args */
         1958         ARGBEGIN {
         1959         case 'a':
         1960                 cookiepolicies = EARGF(usage());
         1961                 break;
         1962         case 'b':
         1963                 enablescrollbars = 0;
         1964                 break;
         1965         case 'B':
         1966                 enablescrollbars = 1;
         1967                 break;
         1968         case 'c':
         1969                 cookiefile = EARGF(usage());
         1970                 break;
         1971         case 'd':
         1972                 enablediskcache = 0;
         1973                 break;
         1974         case 'D':
         1975                 enablediskcache = 1;
         1976                 break;
         1977         case 'e':
         1978                 embed = strtol(EARGF(usage()), NULL, 0);
         1979                 break;
         1980         case 'f':
         1981                 runinfullscreen = 0;
         1982                 break;
         1983         case 'F':
         1984                 runinfullscreen = 1;
         1985                 break;
         1986         case 'g':
         1987                 allowgeolocation = 0;
         1988                 break;
         1989         case 'G':
         1990                 allowgeolocation = 1;
         1991                 break;
         1992         case 'i':
         1993                 loadimages = 0;
         1994                 break;
         1995         case 'I':
         1996                 loadimages = 1;
         1997                 break;
         1998         case 'k':
         1999                 kioskmode = 0;
         2000                 break;
         2001         case 'K':
         2002                 kioskmode = 1;
         2003                 break;
         2004         case 'L':
         2005                 insecureresources = 1;
         2006                 break;
         2007         case 'l':
         2008                 insecureresources = 0;
         2009                 break;
         2010         case 'm':
         2011                 enablestyle = 0;
         2012                 break;
         2013         case 'M':
         2014                 enablestyle = 1;
         2015                 break;
         2016         case 'n':
         2017                 enableinspector = 0;
         2018                 break;
         2019         case 'N':
         2020                 enableinspector = 1;
         2021                 break;
         2022         case 'p':
         2023                 enableplugins = 0;
         2024                 break;
         2025         case 'P':
         2026                 enableplugins = 1;
         2027                 break;
         2028         case 'r':
         2029                 scriptfile = EARGF(usage());
         2030                 break;
         2031         case 's':
         2032                 enablescripts = 0;
         2033                 break;
         2034         case 'S':
         2035                 enablescripts = 1;
         2036                 break;
         2037         case 't':
         2038                 strictssl = 0;
         2039                 break;
         2040         case 'T':
         2041                 strictssl = 1;
         2042                 break;
         2043         case 'y':
         2044                 stylefile = EARGF(usage());
         2045                 break;
         2046         case 'u':
         2047                 useragent = EARGF(usage());
         2048                 break;
         2049         case 'v':
         2050                 die("surf-"VERSION", ©2009-2016 surf engineers, "
         2051                     "see LICENSE for details\n");
         2052         case 'w':
         2053                 privatebrowsing = 0;
         2054                 break;
         2055         case 'W':
         2056                 privatebrowsing = 1;
         2057                 break;
         2058         case 'x':
         2059                 showxid = TRUE;
         2060                 break;
         2061         case 'z':
         2062                 zoomlevel = strtof(EARGF(usage()), NULL);
         2063                 break;
         2064         default:
         2065                 usage();
         2066         } ARGEND;
         2067         if (argc > 0)
         2068                 arg.v = argv[0];
         2069 
         2070         setup();
         2071         c = newclient();
         2072         if (arg.v)
         2073                 loaduri(clients, &arg);
         2074         else
         2075                 updatetitle(c);
         2076 
         2077         gtk_main();
         2078         cleanup();
         2079 
         2080         return EXIT_SUCCESS;
         2081 }
         2082