surf.c (53185B)
1 /* See LICENSE file for copyright and license details. 2 * 3 * To understand surf, start reading main(). 4 */ 5 #include <sys/file.h> 6 #include <sys/socket.h> 7 #include <sys/types.h> 8 #include <sys/wait.h> 9 #include <glib.h> 10 #include <inttypes.h> 11 #include <libgen.h> 12 #include <limits.h> 13 #include <pwd.h> 14 #include <regex.h> 15 #include <signal.h> 16 #include <stdio.h> 17 #include <stdlib.h> 18 #include <string.h> 19 #include <unistd.h> 20 21 #include <gdk/gdk.h> 22 #include <gdk/gdkkeysyms.h> 23 #include <gdk/gdkx.h> 24 #include <glib/gstdio.h> 25 #include <gtk/gtk.h> 26 #include <gtk/gtkx.h> 27 #include <gcr/gcr.h> 28 #include <JavaScriptCore/JavaScript.h> 29 #include <webkit2/webkit2.h> 30 #include <X11/X.h> 31 #include <X11/Xatom.h> 32 #include <glib.h> 33 34 #include "arg.h" 35 #include "common.h" 36 37 #define LENGTH(x) (sizeof(x) / sizeof(x[0])) 38 #define CLEANMASK(mask) (mask & (MODKEY|GDK_SHIFT_MASK)) 39 40 enum { AtomFind, AtomGo, AtomUri, AtomLast }; 41 42 enum { 43 OnDoc = WEBKIT_HIT_TEST_RESULT_CONTEXT_DOCUMENT, 44 OnLink = WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK, 45 OnImg = WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE, 46 OnMedia = WEBKIT_HIT_TEST_RESULT_CONTEXT_MEDIA, 47 OnEdit = WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE, 48 OnBar = WEBKIT_HIT_TEST_RESULT_CONTEXT_SCROLLBAR, 49 OnSel = WEBKIT_HIT_TEST_RESULT_CONTEXT_SELECTION, 50 OnAny = OnDoc | OnLink | OnImg | OnMedia | OnEdit | OnBar | OnSel, 51 }; 52 53 typedef enum { 54 AccessMicrophone, 55 AccessWebcam, 56 CaretBrowsing, 57 Certificate, 58 CookiePolicies, 59 DiskCache, 60 DefaultCharset, 61 DNSPrefetch, 62 Ephemeral, 63 FileURLsCrossAccess, 64 FontSize, 65 FrameFlattening, 66 Geolocation, 67 HideBackground, 68 Inspector, 69 Java, 70 JavaScript, 71 KioskMode, 72 LoadImages, 73 MediaManualPlay, 74 PreferredLanguages, 75 RunInFullscreen, 76 ScrollBars, 77 ShowIndicators, 78 SiteQuirks, 79 SmoothScrolling, 80 SpellChecking, 81 SpellLanguages, 82 StrictTLS, 83 Style, 84 WebGL, 85 ZoomLevel, 86 ParameterLast 87 } ParamName; 88 89 typedef union { 90 int i; 91 float f; 92 const void *v; 93 } Arg; 94 95 typedef struct { 96 Arg val; 97 int prio; 98 } Parameter; 99 100 typedef struct Client { 101 GtkWidget *win; 102 WebKitWebView *view; 103 WebKitWebInspector *inspector; 104 WebKitFindController *finder; 105 WebKitHitTestResult *mousepos; 106 GTlsCertificate *cert, *failedcert; 107 GTlsCertificateFlags tlserr; 108 Window xid; 109 guint64 pageid; 110 int progress, fullscreen, https, insecure, errorpage; 111 const char *title, *overtitle, *targeturi; 112 const char *needle; 113 struct Client *next; 114 } Client; 115 116 typedef struct { 117 guint mod; 118 guint keyval; 119 void (*func)(Client *c, const Arg *a); 120 const Arg arg; 121 } Key; 122 123 typedef struct { 124 unsigned int target; 125 unsigned int mask; 126 guint button; 127 void (*func)(Client *c, const Arg *a, WebKitHitTestResult *h); 128 const Arg arg; 129 unsigned int stopevent; 130 } Button; 131 132 typedef struct { 133 const char *uri; 134 Parameter config[ParameterLast]; 135 regex_t re; 136 } UriParameters; 137 138 typedef struct { 139 char *regex; 140 char *file; 141 regex_t re; 142 } SiteSpecific; 143 144 /* Surf */ 145 static void die(const char *errstr, ...); 146 static void usage(void); 147 static void setup(void); 148 static void sigchld(int unused); 149 static void sighup(int unused); 150 static char *buildfile(const char *path); 151 static char *buildpath(const char *path); 152 static char *untildepath(const char *path); 153 static const char *getuserhomedir(const char *user); 154 static const char *getcurrentuserhomedir(void); 155 static Client *newclient(Client *c); 156 static void loaduri(Client *c, const Arg *a); 157 static const char *geturi(Client *c); 158 static void setatom(Client *c, int a, const char *v); 159 static const char *getatom(Client *c, int a); 160 static void updatetitle(Client *c); 161 static void gettogglestats(Client *c); 162 static void getpagestats(Client *c); 163 static WebKitCookieAcceptPolicy cookiepolicy_get(void); 164 static char cookiepolicy_set(const WebKitCookieAcceptPolicy p); 165 static void seturiparameters(Client *c, const char *uri, ParamName *params); 166 static void setparameter(Client *c, int refresh, ParamName p, const Arg *a); 167 static const char *getcert(const char *uri); 168 static void setcert(Client *c, const char *file); 169 static const char *getstyle(const char *uri); 170 static void setstyle(Client *c, const char *file); 171 static void runscript(Client *c); 172 static void evalscript(Client *c, const char *jsstr, ...); 173 static void updatewinid(Client *c); 174 static void handleplumb(Client *c, const char *uri); 175 static void newwindow(Client *c, const Arg *a, int noembed); 176 static void spawn(Client *c, const Arg *a); 177 static void msgext(Client *c, char type, const Arg *a); 178 static void destroyclient(Client *c); 179 static void cleanup(void); 180 181 /* GTK/WebKit */ 182 static WebKitWebView *newview(Client *c, WebKitWebView *rv); 183 static void initwebextensions(WebKitWebContext *wc, Client *c); 184 static GtkWidget *createview(WebKitWebView *v, WebKitNavigationAction *a, 185 Client *c); 186 static gboolean buttonreleased(GtkWidget *w, GdkEvent *e, Client *c); 187 static GdkFilterReturn processx(GdkXEvent *xevent, GdkEvent *event, 188 gpointer d); 189 static gboolean winevent(GtkWidget *w, GdkEvent *e, Client *c); 190 static gboolean readsock(GIOChannel *s, GIOCondition ioc, gpointer unused); 191 static void showview(WebKitWebView *v, Client *c); 192 static GtkWidget *createwindow(Client *c); 193 static gboolean loadfailedtls(WebKitWebView *v, gchar *uri, 194 GTlsCertificate *cert, 195 GTlsCertificateFlags err, Client *c); 196 static void loadchanged(WebKitWebView *v, WebKitLoadEvent e, Client *c); 197 static void progresschanged(WebKitWebView *v, GParamSpec *ps, Client *c); 198 static void titlechanged(WebKitWebView *view, GParamSpec *ps, Client *c); 199 static void mousetargetchanged(WebKitWebView *v, WebKitHitTestResult *h, 200 guint modifiers, Client *c); 201 static gboolean permissionrequested(WebKitWebView *v, 202 WebKitPermissionRequest *r, Client *c); 203 static gboolean decidepolicy(WebKitWebView *v, WebKitPolicyDecision *d, 204 WebKitPolicyDecisionType dt, Client *c); 205 static void decidenavigation(WebKitPolicyDecision *d, Client *c); 206 static void decidenewwindow(WebKitPolicyDecision *d, Client *c); 207 static void decideresource(WebKitPolicyDecision *d, Client *c); 208 static void insecurecontent(WebKitWebView *v, WebKitInsecureContentEvent e, 209 Client *c); 210 static void downloadstarted(WebKitWebContext *wc, WebKitDownload *d, 211 Client *c); 212 static void responsereceived(WebKitDownload *d, GParamSpec *ps, Client *c); 213 static void download(Client *c, WebKitURIResponse *r); 214 static void webprocessterminated(WebKitWebView *v, 215 WebKitWebProcessTerminationReason r, 216 Client *c); 217 static void closeview(WebKitWebView *v, Client *c); 218 static void destroywin(GtkWidget* w, Client *c); 219 220 /* Hotkeys */ 221 static void pasteuri(GtkClipboard *clipboard, const char *text, gpointer d); 222 static void reload(Client *c, const Arg *a); 223 static void print(Client *c, const Arg *a); 224 static void showcert(Client *c, const Arg *a); 225 static void clipboard(Client *c, const Arg *a); 226 static void zoom(Client *c, const Arg *a); 227 static void scrollv(Client *c, const Arg *a); 228 static void scrollh(Client *c, const Arg *a); 229 static void navigate(Client *c, const Arg *a); 230 static void stop(Client *c, const Arg *a); 231 static void toggle(Client *c, const Arg *a); 232 static void togglefullscreen(Client *c, const Arg *a); 233 static void togglecookiepolicy(Client *c, const Arg *a); 234 static void toggleinspector(Client *c, const Arg *a); 235 static void find(Client *c, const Arg *a); 236 237 /* Buttons */ 238 static void clicknavigate(Client *c, const Arg *a, WebKitHitTestResult *h); 239 static void clicknewwindow(Client *c, const Arg *a, WebKitHitTestResult *h); 240 static void clickexternplayer(Client *c, const Arg *a, WebKitHitTestResult *h); 241 242 static char winid[64]; 243 static char togglestats[12]; 244 static char pagestats[2]; 245 static Atom atoms[AtomLast]; 246 static Window embed; 247 static int showxid; 248 static int cookiepolicy; 249 static Display *dpy; 250 static Client *clients; 251 static GdkDevice *gdkkb; 252 static char *stylefile; 253 static const char *useragent; 254 static Parameter *curconfig; 255 static int modparams[ParameterLast]; 256 static int spair[2]; 257 char *argv0; 258 259 static ParamName loadtransient[] = { 260 Certificate, 261 CookiePolicies, 262 DiskCache, 263 DNSPrefetch, 264 FileURLsCrossAccess, 265 JavaScript, 266 LoadImages, 267 PreferredLanguages, 268 ShowIndicators, 269 StrictTLS, 270 ParameterLast 271 }; 272 273 static ParamName loadcommitted[] = { 274 // AccessMicrophone, 275 // AccessWebcam, 276 CaretBrowsing, 277 DefaultCharset, 278 FontSize, 279 FrameFlattening, 280 Geolocation, 281 HideBackground, 282 Inspector, 283 Java, 284 // KioskMode, 285 MediaManualPlay, 286 RunInFullscreen, 287 ScrollBars, 288 SiteQuirks, 289 SmoothScrolling, 290 SpellChecking, 291 SpellLanguages, 292 Style, 293 ZoomLevel, 294 ParameterLast 295 }; 296 297 static ParamName loadfinished[] = { 298 ParameterLast 299 }; 300 301 /* configuration, allows nested code to access above variables */ 302 #include "config.h" 303 304 void 305 die(const char *errstr, ...) 306 { 307 va_list ap; 308 309 va_start(ap, errstr); 310 vfprintf(stderr, errstr, ap); 311 va_end(ap); 312 exit(1); 313 } 314 315 void 316 usage(void) 317 { 318 die("usage: surf [-bBdDfFgGiIkKmMnNpPsStTvwxX]\n" 319 "[-a cookiepolicies ] [-c cookiefile] [-C stylefile] [-e xid]\n" 320 "[-r scriptfile] [-u useragent] [-z zoomlevel] [uri]\n"); 321 } 322 323 void 324 setup(void) 325 { 326 GIOChannel *gchanin; 327 GdkDisplay *gdpy; 328 int i, j; 329 330 /* clean up any zombies immediately */ 331 sigchld(0); 332 if (signal(SIGHUP, sighup) == SIG_ERR) 333 die("Can't install SIGHUP handler"); 334 335 if (!(dpy = XOpenDisplay(NULL))) 336 die("Can't open default display"); 337 338 /* atoms */ 339 atoms[AtomFind] = XInternAtom(dpy, "_SURF_FIND", False); 340 atoms[AtomGo] = XInternAtom(dpy, "_SURF_GO", False); 341 atoms[AtomUri] = XInternAtom(dpy, "_SURF_URI", False); 342 343 gtk_init(NULL, NULL); 344 345 gdpy = gdk_display_get_default(); 346 347 curconfig = defconfig; 348 349 /* dirs and files */ 350 cookiefile = buildfile(cookiefile); 351 scriptfile = buildfile(scriptfile); 352 certdir = buildpath(certdir); 353 if (curconfig[Ephemeral].val.i) 354 cachedir = NULL; 355 else 356 cachedir = buildpath(cachedir); 357 358 gdkkb = gdk_seat_get_keyboard(gdk_display_get_default_seat(gdpy)); 359 360 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, spair) < 0) { 361 fputs("Unable to create sockets\n", stderr); 362 spair[0] = spair[1] = -1; 363 } else { 364 gchanin = g_io_channel_unix_new(spair[0]); 365 g_io_channel_set_encoding(gchanin, NULL, NULL); 366 g_io_channel_set_flags(gchanin, g_io_channel_get_flags(gchanin) 367 | G_IO_FLAG_NONBLOCK, NULL); 368 g_io_channel_set_close_on_unref(gchanin, TRUE); 369 g_io_add_watch(gchanin, G_IO_IN, readsock, NULL); 370 } 371 372 373 for (i = 0; i < LENGTH(certs); ++i) { 374 if (!regcomp(&(certs[i].re), certs[i].regex, REG_EXTENDED)) { 375 certs[i].file = g_strconcat(certdir, "/", certs[i].file, 376 NULL); 377 } else { 378 fprintf(stderr, "Could not compile regex: %s\n", 379 certs[i].regex); 380 certs[i].regex = NULL; 381 } 382 } 383 384 if (!stylefile) { 385 styledir = buildpath(styledir); 386 for (i = 0; i < LENGTH(styles); ++i) { 387 if (!regcomp(&(styles[i].re), styles[i].regex, 388 REG_EXTENDED)) { 389 styles[i].file = g_strconcat(styledir, "/", 390 styles[i].file, NULL); 391 } else { 392 fprintf(stderr, "Could not compile regex: %s\n", 393 styles[i].regex); 394 styles[i].regex = NULL; 395 } 396 } 397 g_free(styledir); 398 } else { 399 stylefile = buildfile(stylefile); 400 } 401 402 for (i = 0; i < LENGTH(uriparams); ++i) { 403 if (regcomp(&(uriparams[i].re), uriparams[i].uri, 404 REG_EXTENDED)) { 405 fprintf(stderr, "Could not compile regex: %s\n", 406 uriparams[i].uri); 407 uriparams[i].uri = NULL; 408 continue; 409 } 410 411 /* copy default parameters with higher priority */ 412 for (j = 0; j < ParameterLast; ++j) { 413 if (defconfig[j].prio >= uriparams[i].config[j].prio) 414 uriparams[i].config[j] = defconfig[j]; 415 } 416 } 417 } 418 419 void 420 sigchld(int unused) 421 { 422 if (signal(SIGCHLD, sigchld) == SIG_ERR) 423 die("Can't install SIGCHLD handler"); 424 while (waitpid(-1, NULL, WNOHANG) > 0) 425 ; 426 } 427 428 void 429 sighup(int unused) 430 { 431 Arg a = { .i = 0 }; 432 Client *c; 433 434 for (c = clients; c; c = c->next) 435 reload(c, &a); 436 } 437 438 char * 439 buildfile(const char *path) 440 { 441 char *dname, *bname, *bpath, *fpath; 442 FILE *f; 443 444 dname = g_path_get_dirname(path); 445 bname = g_path_get_basename(path); 446 447 bpath = buildpath(dname); 448 g_free(dname); 449 450 fpath = g_build_filename(bpath, bname, NULL); 451 g_free(bpath); 452 g_free(bname); 453 454 if (!(f = fopen(fpath, "a"))) 455 die("Could not open file: %s\n", fpath); 456 457 g_chmod(fpath, 0600); /* always */ 458 fclose(f); 459 460 return fpath; 461 } 462 463 static const char* 464 getuserhomedir(const char *user) 465 { 466 struct passwd *pw = getpwnam(user); 467 468 if (!pw) 469 die("Can't get user %s login information.\n", user); 470 471 return pw->pw_dir; 472 } 473 474 static const char* 475 getcurrentuserhomedir(void) 476 { 477 const char *homedir; 478 const char *user; 479 struct passwd *pw; 480 481 homedir = getenv("HOME"); 482 if (homedir) 483 return homedir; 484 485 user = getenv("USER"); 486 if (user) 487 return getuserhomedir(user); 488 489 pw = getpwuid(getuid()); 490 if (!pw) 491 die("Can't get current user home directory\n"); 492 493 return pw->pw_dir; 494 } 495 496 char * 497 buildpath(const char *path) 498 { 499 char *apath, *fpath; 500 501 if (path[0] == '~') 502 apath = untildepath(path); 503 else 504 apath = g_strdup(path); 505 506 /* creating directory */ 507 if (g_mkdir_with_parents(apath, 0700) < 0) 508 die("Could not access directory: %s\n", apath); 509 510 fpath = realpath(apath, NULL); 511 g_free(apath); 512 513 return fpath; 514 } 515 516 char * 517 untildepath(const char *path) 518 { 519 char *apath, *name, *p; 520 const char *homedir; 521 522 if (path[1] == '/' || path[1] == '\0') { 523 p = (char *)&path[1]; 524 homedir = getcurrentuserhomedir(); 525 } else { 526 if ((p = strchr(path, '/'))) 527 name = g_strndup(&path[1], p - (path + 1)); 528 else 529 name = g_strdup(&path[1]); 530 531 homedir = getuserhomedir(name); 532 g_free(name); 533 } 534 apath = g_build_filename(homedir, p, NULL); 535 return apath; 536 } 537 538 Client * 539 newclient(Client *rc) 540 { 541 Client *c; 542 543 if (!(c = calloc(1, sizeof(Client)))) 544 die("Cannot malloc!\n"); 545 546 c->next = clients; 547 clients = c; 548 549 c->progress = 100; 550 c->view = newview(c, rc ? rc->view : NULL); 551 552 return c; 553 } 554 555 void 556 loaduri(Client *c, const Arg *a) 557 { 558 struct stat st; 559 char *url, *path, *apath; 560 const char *uri = a->v; 561 562 if (g_strcmp0(uri, "") == 0) 563 return; 564 565 if (g_str_has_prefix(uri, "http://") || 566 g_str_has_prefix(uri, "https://") || 567 g_str_has_prefix(uri, "file://") || 568 g_str_has_prefix(uri, "about:")) { 569 url = g_strdup(uri); 570 } else { 571 if (uri[0] == '~') 572 apath = untildepath(uri); 573 else 574 apath = (char *)uri; 575 if (!stat(apath, &st) && (path = realpath(apath, NULL))) { 576 url = g_strdup_printf("file://%s", path); 577 free(path); 578 } else { 579 url = g_strdup_printf("http://%s", uri); 580 } 581 if (apath != uri) 582 free(apath); 583 } 584 585 setatom(c, AtomUri, url); 586 587 if (strcmp(url, geturi(c)) == 0) { 588 reload(c, a); 589 } else { 590 webkit_web_view_load_uri(c->view, url); 591 updatetitle(c); 592 } 593 594 g_free(url); 595 } 596 597 const char * 598 geturi(Client *c) 599 { 600 const char *uri; 601 602 if (!(uri = webkit_web_view_get_uri(c->view))) 603 uri = "about:blank"; 604 return uri; 605 } 606 607 void 608 setatom(Client *c, int a, const char *v) 609 { 610 XChangeProperty(dpy, c->xid, 611 atoms[a], XA_STRING, 8, PropModeReplace, 612 (unsigned char *)v, strlen(v) + 1); 613 XSync(dpy, False); 614 } 615 616 const char * 617 getatom(Client *c, int a) 618 { 619 static char buf[BUFSIZ]; 620 Atom adummy; 621 int idummy; 622 unsigned long ldummy; 623 unsigned char *p = NULL; 624 625 XSync(dpy, False); 626 XGetWindowProperty(dpy, c->xid, atoms[a], 0L, BUFSIZ, False, XA_STRING, 627 &adummy, &idummy, &ldummy, &ldummy, &p); 628 if (p) 629 strncpy(buf, (char *)p, LENGTH(buf) - 1); 630 else 631 buf[0] = '\0'; 632 XFree(p); 633 634 return buf; 635 } 636 637 void 638 updatetitle(Client *c) 639 { 640 char *title; 641 const char *name = c->overtitle ? c->overtitle : 642 c->title ? c->title : ""; 643 644 if (curconfig[ShowIndicators].val.i) { 645 gettogglestats(c); 646 getpagestats(c); 647 648 if (c->progress != 100) 649 title = g_strdup_printf("[%i%%] %s:%s | %s", 650 c->progress, togglestats, pagestats, name); 651 else 652 title = g_strdup_printf("%s:%s | %s", 653 togglestats, pagestats, name); 654 655 gtk_window_set_title(GTK_WINDOW(c->win), title); 656 g_free(title); 657 } else { 658 gtk_window_set_title(GTK_WINDOW(c->win), name); 659 } 660 } 661 662 void 663 gettogglestats(Client *c) 664 { 665 togglestats[0] = cookiepolicy_set(cookiepolicy_get()); 666 togglestats[1] = curconfig[CaretBrowsing].val.i ? 'C' : 'c'; 667 togglestats[2] = curconfig[Geolocation].val.i ? 'G' : 'g'; 668 togglestats[3] = curconfig[DiskCache].val.i ? 'D' : 'd'; 669 togglestats[4] = curconfig[LoadImages].val.i ? 'I' : 'i'; 670 togglestats[5] = curconfig[JavaScript].val.i ? 'S' : 's'; 671 togglestats[7] = curconfig[Style].val.i ? 'M' : 'm'; 672 togglestats[8] = curconfig[FrameFlattening].val.i ? 'F' : 'f'; 673 togglestats[9] = curconfig[Certificate].val.i ? 'X' : 'x'; 674 togglestats[10] = curconfig[StrictTLS].val.i ? 'T' : 't'; 675 togglestats[11] = '\0'; 676 } 677 678 void 679 getpagestats(Client *c) 680 { 681 if (c->https) 682 pagestats[0] = (c->tlserr || c->insecure) ? 'U' : 'T'; 683 else 684 pagestats[0] = '-'; 685 pagestats[1] = '\0'; 686 } 687 688 WebKitCookieAcceptPolicy 689 cookiepolicy_get(void) 690 { 691 switch (((char *)curconfig[CookiePolicies].val.v)[cookiepolicy]) { 692 case 'a': 693 return WEBKIT_COOKIE_POLICY_ACCEPT_NEVER; 694 case '@': 695 return WEBKIT_COOKIE_POLICY_ACCEPT_NO_THIRD_PARTY; 696 default: /* fallthrough */ 697 case 'A': 698 return WEBKIT_COOKIE_POLICY_ACCEPT_ALWAYS; 699 } 700 } 701 702 char 703 cookiepolicy_set(const WebKitCookieAcceptPolicy p) 704 { 705 switch (p) { 706 case WEBKIT_COOKIE_POLICY_ACCEPT_NEVER: 707 return 'a'; 708 case WEBKIT_COOKIE_POLICY_ACCEPT_NO_THIRD_PARTY: 709 return '@'; 710 default: /* fallthrough */ 711 case WEBKIT_COOKIE_POLICY_ACCEPT_ALWAYS: 712 return 'A'; 713 } 714 } 715 716 void 717 seturiparameters(Client *c, const char *uri, ParamName *params) 718 { 719 Parameter *config, *uriconfig = NULL; 720 int i, p; 721 722 for (i = 0; i < LENGTH(uriparams); ++i) { 723 if (uriparams[i].uri && 724 !regexec(&(uriparams[i].re), uri, 0, NULL, 0)) { 725 uriconfig = uriparams[i].config; 726 break; 727 } 728 } 729 730 curconfig = uriconfig ? uriconfig : defconfig; 731 732 for (i = 0; (p = params[i]) != ParameterLast; ++i) { 733 switch(p) { 734 default: /* FALLTHROUGH */ 735 if (!(defconfig[p].prio < curconfig[p].prio || 736 defconfig[p].prio < modparams[p])) 737 continue; 738 case Certificate: 739 case CookiePolicies: 740 case Style: 741 setparameter(c, 0, p, &curconfig[p].val); 742 } 743 } 744 } 745 746 void 747 setparameter(Client *c, int refresh, ParamName p, const Arg *a) 748 { 749 GdkRGBA bgcolor = { 0 }; 750 WebKitSettings *s = webkit_web_view_get_settings(c->view); 751 752 modparams[p] = curconfig[p].prio; 753 754 switch (p) { 755 case AccessMicrophone: 756 return; /* do nothing */ 757 case AccessWebcam: 758 return; /* do nothing */ 759 case CaretBrowsing: 760 webkit_settings_set_enable_caret_browsing(s, a->i); 761 refresh = 0; 762 break; 763 case Certificate: 764 if (a->i) 765 setcert(c, geturi(c)); 766 return; /* do not update */ 767 case CookiePolicies: 768 webkit_cookie_manager_set_accept_policy( 769 webkit_web_context_get_cookie_manager( 770 webkit_web_view_get_context(c->view)), 771 cookiepolicy_get()); 772 refresh = 0; 773 break; 774 case DiskCache: 775 webkit_web_context_set_cache_model( 776 webkit_web_view_get_context(c->view), a->i ? 777 WEBKIT_CACHE_MODEL_WEB_BROWSER : 778 WEBKIT_CACHE_MODEL_DOCUMENT_VIEWER); 779 return; /* do not update */ 780 case DefaultCharset: 781 webkit_settings_set_default_charset(s, a->v); 782 return; /* do not update */ 783 case DNSPrefetch: 784 webkit_settings_set_enable_dns_prefetching(s, a->i); 785 return; /* do not update */ 786 case FileURLsCrossAccess: 787 webkit_settings_set_allow_file_access_from_file_urls(s, a->i); 788 webkit_settings_set_allow_universal_access_from_file_urls(s, a->i); 789 return; /* do not update */ 790 case FontSize: 791 webkit_settings_set_default_font_size(s, a->i); 792 return; /* do not update */ 793 case FrameFlattening: 794 webkit_settings_set_enable_frame_flattening(s, a->i); 795 break; 796 case Geolocation: 797 refresh = 0; 798 break; 799 case HideBackground: 800 if (a->i) 801 webkit_web_view_set_background_color(c->view, &bgcolor); 802 return; /* do not update */ 803 case Inspector: 804 webkit_settings_set_enable_developer_extras(s, a->i); 805 return; /* do not update */ 806 case Java: 807 webkit_settings_set_enable_java(s, a->i); 808 return; /* do not update */ 809 case JavaScript: 810 webkit_settings_set_enable_javascript(s, a->i); 811 break; 812 case KioskMode: 813 return; /* do nothing */ 814 case LoadImages: 815 webkit_settings_set_auto_load_images(s, a->i); 816 break; 817 case MediaManualPlay: 818 webkit_settings_set_media_playback_requires_user_gesture(s, a->i); 819 break; 820 case PreferredLanguages: 821 return; /* do nothing */ 822 case RunInFullscreen: 823 return; /* do nothing */ 824 case ScrollBars: 825 /* Disabled until we write some WebKitWebExtension for 826 * manipulating the DOM directly. 827 enablescrollbars = !enablescrollbars; 828 evalscript(c, "document.documentElement.style.overflow = '%s'", 829 enablescrollbars ? "auto" : "hidden"); 830 */ 831 return; /* do not update */ 832 case ShowIndicators: 833 break; 834 case SmoothScrolling: 835 webkit_settings_set_enable_smooth_scrolling(s, a->i); 836 return; /* do not update */ 837 case SiteQuirks: 838 webkit_settings_set_enable_site_specific_quirks(s, a->i); 839 break; 840 case SpellChecking: 841 webkit_web_context_set_spell_checking_enabled( 842 webkit_web_view_get_context(c->view), a->i); 843 return; /* do not update */ 844 case SpellLanguages: 845 return; /* do nothing */ 846 case StrictTLS: 847 webkit_web_context_set_tls_errors_policy( 848 webkit_web_view_get_context(c->view), a->i ? 849 WEBKIT_TLS_ERRORS_POLICY_FAIL : 850 WEBKIT_TLS_ERRORS_POLICY_IGNORE); 851 break; 852 case Style: 853 webkit_user_content_manager_remove_all_style_sheets( 854 webkit_web_view_get_user_content_manager(c->view)); 855 if (a->i) 856 setstyle(c, getstyle(geturi(c))); 857 refresh = 0; 858 break; 859 case WebGL: 860 webkit_settings_set_enable_webgl(s, a->i); 861 break; 862 case ZoomLevel: 863 webkit_web_view_set_zoom_level(c->view, a->f); 864 return; /* do not update */ 865 default: 866 return; /* do nothing */ 867 } 868 869 updatetitle(c); 870 if (refresh) 871 reload(c, a); 872 } 873 874 const char * 875 getcert(const char *uri) 876 { 877 int i; 878 879 for (i = 0; i < LENGTH(certs); ++i) { 880 if (certs[i].regex && 881 !regexec(&(certs[i].re), uri, 0, NULL, 0)) 882 return certs[i].file; 883 } 884 885 return NULL; 886 } 887 888 void 889 setcert(Client *c, const char *uri) 890 { 891 const char *file = getcert(uri); 892 char *host; 893 GTlsCertificate *cert; 894 895 if (!file) 896 return; 897 898 if (!(cert = g_tls_certificate_new_from_file(file, NULL))) { 899 fprintf(stderr, "Could not read certificate file: %s\n", file); 900 return; 901 } 902 903 if ((uri = strstr(uri, "https://"))) { 904 uri += sizeof("https://") - 1; 905 host = g_strndup(uri, strchr(uri, '/') - uri); 906 webkit_web_context_allow_tls_certificate_for_host( 907 webkit_web_view_get_context(c->view), cert, host); 908 g_free(host); 909 } 910 911 g_object_unref(cert); 912 913 } 914 915 const char * 916 getstyle(const char *uri) 917 { 918 int i; 919 920 if (stylefile) 921 return stylefile; 922 923 for (i = 0; i < LENGTH(styles); ++i) { 924 if (styles[i].regex && 925 !regexec(&(styles[i].re), uri, 0, NULL, 0)) 926 return styles[i].file; 927 } 928 929 return ""; 930 } 931 932 void 933 setstyle(Client *c, const char *file) 934 { 935 gchar *style; 936 937 if (!g_file_get_contents(file, &style, NULL, NULL)) { 938 fprintf(stderr, "Could not read style file: %s\n", file); 939 return; 940 } 941 942 webkit_user_content_manager_add_style_sheet( 943 webkit_web_view_get_user_content_manager(c->view), 944 webkit_user_style_sheet_new(style, 945 WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES, 946 WEBKIT_USER_STYLE_LEVEL_USER, 947 NULL, NULL)); 948 949 g_free(style); 950 } 951 952 void 953 runscript(Client *c) 954 { 955 gchar *script; 956 gsize l; 957 958 if (g_file_get_contents(scriptfile, &script, &l, NULL) && l) 959 evalscript(c, "%s", script); 960 g_free(script); 961 } 962 963 void 964 evalscript(Client *c, const char *jsstr, ...) 965 { 966 va_list ap; 967 gchar *script; 968 969 va_start(ap, jsstr); 970 script = g_strdup_vprintf(jsstr, ap); 971 va_end(ap); 972 973 webkit_web_view_run_javascript(c->view, script, NULL, NULL, NULL); 974 g_free(script); 975 } 976 977 void 978 updatewinid(Client *c) 979 { 980 snprintf(winid, LENGTH(winid), "%lu", c->xid); 981 } 982 983 void 984 handleplumb(Client *c, const char *uri) 985 { 986 Arg a = (Arg)PLUMB(uri); 987 spawn(c, &a); 988 } 989 990 void 991 newwindow(Client *c, const Arg *a, int noembed) 992 { 993 int i = 0; 994 char tmp[64]; 995 const char *cmd[29], *uri; 996 const Arg arg = { .v = cmd }; 997 998 cmd[i++] = argv0; 999 cmd[i++] = "-a"; 1000 cmd[i++] = curconfig[CookiePolicies].val.v; 1001 cmd[i++] = curconfig[ScrollBars].val.i ? "-B" : "-b"; 1002 if (cookiefile && g_strcmp0(cookiefile, "")) { 1003 cmd[i++] = "-c"; 1004 cmd[i++] = cookiefile; 1005 } 1006 if (stylefile && g_strcmp0(stylefile, "")) { 1007 cmd[i++] = "-C"; 1008 cmd[i++] = stylefile; 1009 } 1010 cmd[i++] = curconfig[DiskCache].val.i ? "-D" : "-d"; 1011 if (embed && !noembed) { 1012 cmd[i++] = "-e"; 1013 snprintf(tmp, LENGTH(tmp), "%lu", embed); 1014 cmd[i++] = tmp; 1015 } 1016 cmd[i++] = curconfig[RunInFullscreen].val.i ? "-F" : "-f" ; 1017 cmd[i++] = curconfig[Geolocation].val.i ? "-G" : "-g" ; 1018 cmd[i++] = curconfig[LoadImages].val.i ? "-I" : "-i" ; 1019 cmd[i++] = curconfig[KioskMode].val.i ? "-K" : "-k" ; 1020 cmd[i++] = curconfig[Style].val.i ? "-M" : "-m" ; 1021 cmd[i++] = curconfig[Inspector].val.i ? "-N" : "-n" ; 1022 if (scriptfile && g_strcmp0(scriptfile, "")) { 1023 cmd[i++] = "-r"; 1024 cmd[i++] = scriptfile; 1025 } 1026 cmd[i++] = curconfig[JavaScript].val.i ? "-S" : "-s"; 1027 cmd[i++] = curconfig[StrictTLS].val.i ? "-T" : "-t"; 1028 if (fulluseragent && g_strcmp0(fulluseragent, "")) { 1029 cmd[i++] = "-u"; 1030 cmd[i++] = fulluseragent; 1031 } 1032 if (showxid) 1033 cmd[i++] = "-w"; 1034 cmd[i++] = curconfig[Certificate].val.i ? "-X" : "-x" ; 1035 /* do not keep zoom level */ 1036 cmd[i++] = "--"; 1037 if ((uri = a->v)) 1038 cmd[i++] = uri; 1039 cmd[i] = NULL; 1040 1041 spawn(c, &arg); 1042 } 1043 1044 void 1045 spawn(Client *c, const Arg *a) 1046 { 1047 if (fork() == 0) { 1048 if (dpy) 1049 close(ConnectionNumber(dpy)); 1050 close(spair[0]); 1051 close(spair[1]); 1052 setsid(); 1053 execvp(((char **)a->v)[0], (char **)a->v); 1054 fprintf(stderr, "%s: execvp %s", argv0, ((char **)a->v)[0]); 1055 perror(" failed"); 1056 exit(1); 1057 } 1058 } 1059 1060 void 1061 destroyclient(Client *c) 1062 { 1063 Client *p; 1064 1065 webkit_web_view_stop_loading(c->view); 1066 /* Not needed, has already been called 1067 gtk_widget_destroy(c->win); 1068 */ 1069 1070 for (p = clients; p && p->next != c; p = p->next) 1071 ; 1072 if (p) 1073 p->next = c->next; 1074 else 1075 clients = c->next; 1076 free(c); 1077 } 1078 1079 void 1080 cleanup(void) 1081 { 1082 while (clients) 1083 destroyclient(clients); 1084 1085 close(spair[0]); 1086 close(spair[1]); 1087 g_free(cookiefile); 1088 g_free(scriptfile); 1089 g_free(stylefile); 1090 g_free(cachedir); 1091 XCloseDisplay(dpy); 1092 } 1093 1094 WebKitWebView * 1095 newview(Client *c, WebKitWebView *rv) 1096 { 1097 WebKitWebView *v; 1098 WebKitSettings *settings; 1099 WebKitWebContext *context; 1100 WebKitCookieManager *cookiemanager; 1101 WebKitUserContentManager *contentmanager; 1102 1103 /* Webview */ 1104 if (rv) { 1105 v = WEBKIT_WEB_VIEW(webkit_web_view_new_with_related_view(rv)); 1106 } else { 1107 settings = webkit_settings_new_with_settings( 1108 "allow-file-access-from-file-urls", curconfig[FileURLsCrossAccess].val.i, 1109 "allow-universal-access-from-file-urls", curconfig[FileURLsCrossAccess].val.i, 1110 "auto-load-images", curconfig[LoadImages].val.i, 1111 "default-charset", curconfig[DefaultCharset].val.v, 1112 "default-font-size", curconfig[FontSize].val.i, 1113 "enable-caret-browsing", curconfig[CaretBrowsing].val.i, 1114 "enable-developer-extras", curconfig[Inspector].val.i, 1115 "enable-dns-prefetching", curconfig[DNSPrefetch].val.i, 1116 "enable-frame-flattening", curconfig[FrameFlattening].val.i, 1117 "enable-html5-database", curconfig[DiskCache].val.i, 1118 "enable-html5-local-storage", curconfig[DiskCache].val.i, 1119 "enable-java", curconfig[Java].val.i, 1120 "enable-javascript", curconfig[JavaScript].val.i, 1121 "enable-site-specific-quirks", curconfig[SiteQuirks].val.i, 1122 "enable-smooth-scrolling", curconfig[SmoothScrolling].val.i, 1123 "enable-webgl", curconfig[WebGL].val.i, 1124 "media-playback-requires-user-gesture", curconfig[MediaManualPlay].val.i, 1125 NULL); 1126 /* For more interesting settings, have a look at 1127 * http://webkitgtk.org/reference/webkit2gtk/stable/WebKitSettings.html */ 1128 1129 if (strcmp(fulluseragent, "")) { 1130 webkit_settings_set_user_agent(settings, fulluseragent); 1131 } else if (surfuseragent) { 1132 webkit_settings_set_user_agent_with_application_details( 1133 settings, "Surf", VERSION); 1134 } 1135 useragent = webkit_settings_get_user_agent(settings); 1136 1137 contentmanager = webkit_user_content_manager_new(); 1138 1139 if (curconfig[Ephemeral].val.i) { 1140 context = webkit_web_context_new_ephemeral(); 1141 } else { 1142 context = webkit_web_context_new_with_website_data_manager( 1143 webkit_website_data_manager_new( 1144 "base-cache-directory", cachedir, 1145 "base-data-directory", cachedir, 1146 NULL)); 1147 } 1148 1149 1150 cookiemanager = webkit_web_context_get_cookie_manager(context); 1151 1152 /* rendering process model, can be a shared unique one 1153 * or one for each view */ 1154 webkit_web_context_set_process_model(context, 1155 WEBKIT_PROCESS_MODEL_MULTIPLE_SECONDARY_PROCESSES); 1156 /* TLS */ 1157 webkit_web_context_set_tls_errors_policy(context, 1158 curconfig[StrictTLS].val.i ? WEBKIT_TLS_ERRORS_POLICY_FAIL : 1159 WEBKIT_TLS_ERRORS_POLICY_IGNORE); 1160 /* disk cache */ 1161 webkit_web_context_set_cache_model(context, 1162 curconfig[DiskCache].val.i ? WEBKIT_CACHE_MODEL_WEB_BROWSER : 1163 WEBKIT_CACHE_MODEL_DOCUMENT_VIEWER); 1164 1165 /* Currently only works with text file to be compatible with curl */ 1166 if (!curconfig[Ephemeral].val.i) 1167 webkit_cookie_manager_set_persistent_storage(cookiemanager, 1168 cookiefile, WEBKIT_COOKIE_PERSISTENT_STORAGE_TEXT); 1169 /* cookie policy */ 1170 webkit_cookie_manager_set_accept_policy(cookiemanager, 1171 cookiepolicy_get()); 1172 /* languages */ 1173 webkit_web_context_set_preferred_languages(context, 1174 curconfig[PreferredLanguages].val.v); 1175 webkit_web_context_set_spell_checking_languages(context, 1176 curconfig[SpellLanguages].val.v); 1177 webkit_web_context_set_spell_checking_enabled(context, 1178 curconfig[SpellChecking].val.i); 1179 1180 g_signal_connect(G_OBJECT(context), "download-started", 1181 G_CALLBACK(downloadstarted), c); 1182 g_signal_connect(G_OBJECT(context), "initialize-web-extensions", 1183 G_CALLBACK(initwebextensions), c); 1184 1185 v = g_object_new(WEBKIT_TYPE_WEB_VIEW, 1186 "settings", settings, 1187 "user-content-manager", contentmanager, 1188 "web-context", context, 1189 NULL); 1190 } 1191 1192 g_signal_connect(G_OBJECT(v), "notify::estimated-load-progress", 1193 G_CALLBACK(progresschanged), c); 1194 g_signal_connect(G_OBJECT(v), "notify::title", 1195 G_CALLBACK(titlechanged), c); 1196 g_signal_connect(G_OBJECT(v), "button-release-event", 1197 G_CALLBACK(buttonreleased), c); 1198 g_signal_connect(G_OBJECT(v), "close", 1199 G_CALLBACK(closeview), c); 1200 g_signal_connect(G_OBJECT(v), "create", 1201 G_CALLBACK(createview), c); 1202 g_signal_connect(G_OBJECT(v), "decide-policy", 1203 G_CALLBACK(decidepolicy), c); 1204 g_signal_connect(G_OBJECT(v), "insecure-content-detected", 1205 G_CALLBACK(insecurecontent), c); 1206 g_signal_connect(G_OBJECT(v), "load-failed-with-tls-errors", 1207 G_CALLBACK(loadfailedtls), c); 1208 g_signal_connect(G_OBJECT(v), "load-changed", 1209 G_CALLBACK(loadchanged), c); 1210 g_signal_connect(G_OBJECT(v), "mouse-target-changed", 1211 G_CALLBACK(mousetargetchanged), c); 1212 g_signal_connect(G_OBJECT(v), "permission-request", 1213 G_CALLBACK(permissionrequested), c); 1214 g_signal_connect(G_OBJECT(v), "ready-to-show", 1215 G_CALLBACK(showview), c); 1216 g_signal_connect(G_OBJECT(v), "web-process-terminated", 1217 G_CALLBACK(webprocessterminated), c); 1218 1219 return v; 1220 } 1221 1222 static gboolean 1223 readsock(GIOChannel *s, GIOCondition ioc, gpointer unused) 1224 { 1225 static char msg[MSGBUFSZ]; 1226 GError *gerr = NULL; 1227 gsize msgsz; 1228 1229 if (g_io_channel_read_chars(s, msg, sizeof(msg), &msgsz, &gerr) != 1230 G_IO_STATUS_NORMAL) { 1231 if (gerr) { 1232 fprintf(stderr, "surf: error reading socket: %s\n", 1233 gerr->message); 1234 g_error_free(gerr); 1235 } 1236 return TRUE; 1237 } 1238 if (msgsz < 2) { 1239 fprintf(stderr, "surf: message too short: %d\n", msgsz); 1240 return TRUE; 1241 } 1242 1243 return TRUE; 1244 } 1245 1246 void 1247 initwebextensions(WebKitWebContext *wc, Client *c) 1248 { 1249 GVariant *gv; 1250 1251 if (spair[1] < 0) 1252 return; 1253 1254 gv = g_variant_new("i", spair[1]); 1255 1256 webkit_web_context_set_web_extensions_initialization_user_data(wc, gv); 1257 webkit_web_context_set_web_extensions_directory(wc, WEBEXTDIR); 1258 } 1259 1260 GtkWidget * 1261 createview(WebKitWebView *v, WebKitNavigationAction *a, Client *c) 1262 { 1263 Client *n; 1264 1265 switch (webkit_navigation_action_get_navigation_type(a)) { 1266 case WEBKIT_NAVIGATION_TYPE_OTHER: /* fallthrough */ 1267 /* 1268 * popup windows of type “other” are almost always triggered 1269 * by user gesture, so inverse the logic here 1270 */ 1271 /* instead of this, compare destination uri to mouse-over uri for validating window */ 1272 if (webkit_navigation_action_is_user_gesture(a)) 1273 return NULL; 1274 case WEBKIT_NAVIGATION_TYPE_LINK_CLICKED: /* fallthrough */ 1275 case WEBKIT_NAVIGATION_TYPE_FORM_SUBMITTED: /* fallthrough */ 1276 case WEBKIT_NAVIGATION_TYPE_BACK_FORWARD: /* fallthrough */ 1277 case WEBKIT_NAVIGATION_TYPE_RELOAD: /* fallthrough */ 1278 case WEBKIT_NAVIGATION_TYPE_FORM_RESUBMITTED: 1279 n = newclient(c); 1280 break; 1281 default: 1282 return NULL; 1283 } 1284 1285 return GTK_WIDGET(n->view); 1286 } 1287 1288 gboolean 1289 buttonreleased(GtkWidget *w, GdkEvent *e, Client *c) 1290 { 1291 WebKitHitTestResultContext element; 1292 int i; 1293 1294 element = webkit_hit_test_result_get_context(c->mousepos); 1295 1296 for (i = 0; i < LENGTH(buttons); ++i) { 1297 if (element & buttons[i].target && 1298 e->button.button == buttons[i].button && 1299 CLEANMASK(e->button.state) == CLEANMASK(buttons[i].mask) && 1300 buttons[i].func) { 1301 buttons[i].func(c, &buttons[i].arg, c->mousepos); 1302 return buttons[i].stopevent; 1303 } 1304 } 1305 1306 return FALSE; 1307 } 1308 1309 GdkFilterReturn 1310 processx(GdkXEvent *e, GdkEvent *event, gpointer d) 1311 { 1312 Client *c = (Client *)d; 1313 XPropertyEvent *ev; 1314 Arg a; 1315 1316 if (((XEvent *)e)->type == PropertyNotify) { 1317 ev = &((XEvent *)e)->xproperty; 1318 if (ev->state == PropertyNewValue) { 1319 if (ev->atom == atoms[AtomFind]) { 1320 find(c, NULL); 1321 1322 return GDK_FILTER_REMOVE; 1323 } else if (ev->atom == atoms[AtomGo]) { 1324 a.v = getatom(c, AtomGo); 1325 loaduri(c, &a); 1326 1327 return GDK_FILTER_REMOVE; 1328 } 1329 } 1330 } 1331 return GDK_FILTER_CONTINUE; 1332 } 1333 1334 gboolean 1335 winevent(GtkWidget *w, GdkEvent *e, Client *c) 1336 { 1337 int i; 1338 1339 switch (e->type) { 1340 case GDK_ENTER_NOTIFY: 1341 c->overtitle = c->targeturi; 1342 updatetitle(c); 1343 break; 1344 case GDK_KEY_PRESS: 1345 if (!curconfig[KioskMode].val.i) { 1346 for (i = 0; i < LENGTH(keys); ++i) { 1347 if (gdk_keyval_to_lower(e->key.keyval) == 1348 keys[i].keyval && 1349 CLEANMASK(e->key.state) == keys[i].mod && 1350 keys[i].func) { 1351 updatewinid(c); 1352 keys[i].func(c, &(keys[i].arg)); 1353 return TRUE; 1354 } 1355 } 1356 } 1357 case GDK_LEAVE_NOTIFY: 1358 c->overtitle = NULL; 1359 updatetitle(c); 1360 break; 1361 case GDK_WINDOW_STATE: 1362 if (e->window_state.changed_mask == 1363 GDK_WINDOW_STATE_FULLSCREEN) 1364 c->fullscreen = e->window_state.new_window_state & 1365 GDK_WINDOW_STATE_FULLSCREEN; 1366 break; 1367 default: 1368 break; 1369 } 1370 1371 return FALSE; 1372 } 1373 1374 void 1375 showview(WebKitWebView *v, Client *c) 1376 { 1377 GdkRGBA bgcolor = { 0 }; 1378 GdkWindow *gwin; 1379 1380 c->finder = webkit_web_view_get_find_controller(c->view); 1381 c->inspector = webkit_web_view_get_inspector(c->view); 1382 1383 c->pageid = webkit_web_view_get_page_id(c->view); 1384 c->win = createwindow(c); 1385 1386 gtk_container_add(GTK_CONTAINER(c->win), GTK_WIDGET(c->view)); 1387 gtk_widget_show_all(c->win); 1388 gtk_widget_grab_focus(GTK_WIDGET(c->view)); 1389 1390 gwin = gtk_widget_get_window(GTK_WIDGET(c->win)); 1391 c->xid = gdk_x11_window_get_xid(gwin); 1392 updatewinid(c); 1393 if (showxid) { 1394 gdk_display_sync(gtk_widget_get_display(c->win)); 1395 puts(winid); 1396 fflush(stdout); 1397 } 1398 1399 if (curconfig[HideBackground].val.i) 1400 webkit_web_view_set_background_color(c->view, &bgcolor); 1401 1402 if (!curconfig[KioskMode].val.i) { 1403 gdk_window_set_events(gwin, GDK_ALL_EVENTS_MASK); 1404 gdk_window_add_filter(gwin, processx, c); 1405 } 1406 1407 if (curconfig[RunInFullscreen].val.i) 1408 togglefullscreen(c, NULL); 1409 1410 if (curconfig[ZoomLevel].val.f != 1.0) 1411 webkit_web_view_set_zoom_level(c->view, 1412 curconfig[ZoomLevel].val.f); 1413 1414 setatom(c, AtomFind, ""); 1415 setatom(c, AtomUri, "about:blank"); 1416 } 1417 1418 GtkWidget * 1419 createwindow(Client *c) 1420 { 1421 char *wmstr; 1422 GtkWidget *w; 1423 1424 if (embed) { 1425 w = gtk_plug_new(embed); 1426 } else { 1427 w = gtk_window_new(GTK_WINDOW_TOPLEVEL); 1428 1429 wmstr = g_path_get_basename(argv0); 1430 gtk_window_set_wmclass(GTK_WINDOW(w), wmstr, "Surf"); 1431 g_free(wmstr); 1432 1433 wmstr = g_strdup_printf("%s[%"PRIu64"]", "Surf", c->pageid); 1434 gtk_window_set_role(GTK_WINDOW(w), wmstr); 1435 g_free(wmstr); 1436 1437 gtk_window_set_default_size(GTK_WINDOW(w), winsize[0], winsize[1]); 1438 } 1439 1440 g_signal_connect(G_OBJECT(w), "destroy", 1441 G_CALLBACK(destroywin), c); 1442 g_signal_connect(G_OBJECT(w), "enter-notify-event", 1443 G_CALLBACK(winevent), c); 1444 g_signal_connect(G_OBJECT(w), "key-press-event", 1445 G_CALLBACK(winevent), c); 1446 g_signal_connect(G_OBJECT(w), "leave-notify-event", 1447 G_CALLBACK(winevent), c); 1448 g_signal_connect(G_OBJECT(w), "window-state-event", 1449 G_CALLBACK(winevent), c); 1450 1451 return w; 1452 } 1453 1454 gboolean 1455 loadfailedtls(WebKitWebView *v, gchar *uri, GTlsCertificate *cert, 1456 GTlsCertificateFlags err, Client *c) 1457 { 1458 GString *errmsg = g_string_new(NULL); 1459 gchar *html, *pem; 1460 1461 c->failedcert = g_object_ref(cert); 1462 c->tlserr = err; 1463 c->errorpage = 1; 1464 1465 if (err & G_TLS_CERTIFICATE_UNKNOWN_CA) 1466 g_string_append(errmsg, 1467 "The signing certificate authority is not known.<br>"); 1468 if (err & G_TLS_CERTIFICATE_BAD_IDENTITY) 1469 g_string_append(errmsg, 1470 "The certificate does not match the expected identity " 1471 "of the site that it was retrieved from.<br>"); 1472 if (err & G_TLS_CERTIFICATE_NOT_ACTIVATED) 1473 g_string_append(errmsg, 1474 "The certificate's activation time " 1475 "is still in the future.<br>"); 1476 if (err & G_TLS_CERTIFICATE_EXPIRED) 1477 g_string_append(errmsg, "The certificate has expired.<br>"); 1478 if (err & G_TLS_CERTIFICATE_REVOKED) 1479 g_string_append(errmsg, 1480 "The certificate has been revoked according to " 1481 "the GTlsConnection's certificate revocation list.<br>"); 1482 if (err & G_TLS_CERTIFICATE_INSECURE) 1483 g_string_append(errmsg, 1484 "The certificate's algorithm is considered insecure.<br>"); 1485 if (err & G_TLS_CERTIFICATE_GENERIC_ERROR) 1486 g_string_append(errmsg, 1487 "Some error occurred validating the certificate.<br>"); 1488 1489 g_object_get(cert, "certificate-pem", &pem, NULL); 1490 html = g_strdup_printf("<p>Could not validate TLS for “%s”<br>%s</p>" 1491 "<p>You can inspect the following certificate " 1492 "with Ctrl-t (default keybinding).</p>" 1493 "<p><pre>%s</pre></p>", uri, errmsg->str, pem); 1494 g_free(pem); 1495 g_string_free(errmsg, TRUE); 1496 1497 webkit_web_view_load_alternate_html(c->view, html, uri, NULL); 1498 g_free(html); 1499 1500 return TRUE; 1501 } 1502 1503 void 1504 loadchanged(WebKitWebView *v, WebKitLoadEvent e, Client *c) 1505 { 1506 const char *uri = geturi(c); 1507 1508 switch (e) { 1509 case WEBKIT_LOAD_STARTED: 1510 setatom(c, AtomUri, uri); 1511 c->title = uri; 1512 c->https = c->insecure = 0; 1513 seturiparameters(c, uri, loadtransient); 1514 if (c->errorpage) 1515 c->errorpage = 0; 1516 else 1517 g_clear_object(&c->failedcert); 1518 break; 1519 case WEBKIT_LOAD_REDIRECTED: 1520 setatom(c, AtomUri, uri); 1521 c->title = uri; 1522 seturiparameters(c, uri, loadtransient); 1523 break; 1524 case WEBKIT_LOAD_COMMITTED: 1525 setatom(c, AtomUri, uri); 1526 c->title = uri; 1527 seturiparameters(c, uri, loadcommitted); 1528 c->https = webkit_web_view_get_tls_info(c->view, &c->cert, 1529 &c->tlserr); 1530 break; 1531 case WEBKIT_LOAD_FINISHED: 1532 seturiparameters(c, uri, loadfinished); 1533 /* Disabled until we write some WebKitWebExtension for 1534 * manipulating the DOM directly. 1535 evalscript(c, "document.documentElement.style.overflow = '%s'", 1536 enablescrollbars ? "auto" : "hidden"); 1537 */ 1538 runscript(c); 1539 break; 1540 } 1541 updatetitle(c); 1542 } 1543 1544 void 1545 progresschanged(WebKitWebView *v, GParamSpec *ps, Client *c) 1546 { 1547 c->progress = webkit_web_view_get_estimated_load_progress(c->view) * 1548 100; 1549 updatetitle(c); 1550 } 1551 1552 void 1553 titlechanged(WebKitWebView *view, GParamSpec *ps, Client *c) 1554 { 1555 c->title = webkit_web_view_get_title(c->view); 1556 updatetitle(c); 1557 } 1558 1559 void 1560 mousetargetchanged(WebKitWebView *v, WebKitHitTestResult *h, guint modifiers, 1561 Client *c) 1562 { 1563 WebKitHitTestResultContext hc = webkit_hit_test_result_get_context(h); 1564 1565 /* Keep the hit test to know where is the pointer on the next click */ 1566 c->mousepos = h; 1567 1568 if (hc & OnLink) 1569 c->targeturi = webkit_hit_test_result_get_link_uri(h); 1570 else if (hc & OnImg) 1571 c->targeturi = webkit_hit_test_result_get_image_uri(h); 1572 else if (hc & OnMedia) 1573 c->targeturi = webkit_hit_test_result_get_media_uri(h); 1574 else 1575 c->targeturi = NULL; 1576 1577 c->overtitle = c->targeturi; 1578 updatetitle(c); 1579 } 1580 1581 gboolean 1582 permissionrequested(WebKitWebView *v, WebKitPermissionRequest *r, Client *c) 1583 { 1584 ParamName param = ParameterLast; 1585 1586 if (WEBKIT_IS_GEOLOCATION_PERMISSION_REQUEST(r)) { 1587 param = Geolocation; 1588 } else if (WEBKIT_IS_USER_MEDIA_PERMISSION_REQUEST(r)) { 1589 if (webkit_user_media_permission_is_for_audio_device( 1590 WEBKIT_USER_MEDIA_PERMISSION_REQUEST(r))) 1591 param = AccessMicrophone; 1592 else if (webkit_user_media_permission_is_for_video_device( 1593 WEBKIT_USER_MEDIA_PERMISSION_REQUEST(r))) 1594 param = AccessWebcam; 1595 } else { 1596 return FALSE; 1597 } 1598 1599 if (curconfig[param].val.i) 1600 webkit_permission_request_allow(r); 1601 else 1602 webkit_permission_request_deny(r); 1603 1604 return TRUE; 1605 } 1606 1607 gboolean 1608 decidepolicy(WebKitWebView *v, WebKitPolicyDecision *d, 1609 WebKitPolicyDecisionType dt, Client *c) 1610 { 1611 switch (dt) { 1612 case WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION: 1613 decidenavigation(d, c); 1614 break; 1615 case WEBKIT_POLICY_DECISION_TYPE_NEW_WINDOW_ACTION: 1616 decidenewwindow(d, c); 1617 break; 1618 case WEBKIT_POLICY_DECISION_TYPE_RESPONSE: 1619 decideresource(d, c); 1620 break; 1621 default: 1622 webkit_policy_decision_ignore(d); 1623 break; 1624 } 1625 return TRUE; 1626 } 1627 1628 void 1629 decidenavigation(WebKitPolicyDecision *d, Client *c) 1630 { 1631 WebKitNavigationAction *a = 1632 webkit_navigation_policy_decision_get_navigation_action( 1633 WEBKIT_NAVIGATION_POLICY_DECISION(d)); 1634 1635 switch (webkit_navigation_action_get_navigation_type(a)) { 1636 case WEBKIT_NAVIGATION_TYPE_LINK_CLICKED: /* fallthrough */ 1637 case WEBKIT_NAVIGATION_TYPE_FORM_SUBMITTED: /* fallthrough */ 1638 case WEBKIT_NAVIGATION_TYPE_BACK_FORWARD: /* fallthrough */ 1639 case WEBKIT_NAVIGATION_TYPE_RELOAD: /* fallthrough */ 1640 case WEBKIT_NAVIGATION_TYPE_FORM_RESUBMITTED: /* fallthrough */ 1641 case WEBKIT_NAVIGATION_TYPE_OTHER: /* fallthrough */ 1642 default: 1643 /* Do not navigate to links with a "_blank" target (popup) */ 1644 if (webkit_navigation_policy_decision_get_frame_name( 1645 WEBKIT_NAVIGATION_POLICY_DECISION(d))) { 1646 webkit_policy_decision_ignore(d); 1647 } else { 1648 /* Filter out navigation to different domain ? */ 1649 /* get action→urirequest, copy and load in new window+view 1650 * on Ctrl+Click ? */ 1651 webkit_policy_decision_use(d); 1652 } 1653 break; 1654 } 1655 } 1656 1657 void 1658 decidenewwindow(WebKitPolicyDecision *d, Client *c) 1659 { 1660 Arg arg; 1661 WebKitNavigationAction *a = 1662 webkit_navigation_policy_decision_get_navigation_action( 1663 WEBKIT_NAVIGATION_POLICY_DECISION(d)); 1664 1665 1666 switch (webkit_navigation_action_get_navigation_type(a)) { 1667 case WEBKIT_NAVIGATION_TYPE_LINK_CLICKED: /* fallthrough */ 1668 case WEBKIT_NAVIGATION_TYPE_FORM_SUBMITTED: /* fallthrough */ 1669 case WEBKIT_NAVIGATION_TYPE_BACK_FORWARD: /* fallthrough */ 1670 case WEBKIT_NAVIGATION_TYPE_RELOAD: /* fallthrough */ 1671 case WEBKIT_NAVIGATION_TYPE_FORM_RESUBMITTED: 1672 /* Filter domains here */ 1673 /* If the value of “mouse-button” is not 0, then the navigation was triggered by a mouse event. 1674 * test for link clicked but no button ? */ 1675 arg.v = webkit_uri_request_get_uri( 1676 webkit_navigation_action_get_request(a)); 1677 newwindow(c, &arg, 0); 1678 break; 1679 case WEBKIT_NAVIGATION_TYPE_OTHER: /* fallthrough */ 1680 default: 1681 break; 1682 } 1683 1684 webkit_policy_decision_ignore(d); 1685 } 1686 1687 void 1688 decideresource(WebKitPolicyDecision *d, Client *c) 1689 { 1690 int i, isascii = 1; 1691 WebKitResponsePolicyDecision *r = WEBKIT_RESPONSE_POLICY_DECISION(d); 1692 WebKitURIResponse *res = 1693 webkit_response_policy_decision_get_response(r); 1694 const gchar *uri = webkit_uri_response_get_uri(res); 1695 1696 if (g_str_has_suffix(uri, "/favicon.ico")) { 1697 webkit_policy_decision_ignore(d); 1698 return; 1699 } 1700 1701 if (!g_str_has_prefix(uri, "http://") 1702 && !g_str_has_prefix(uri, "https://") 1703 && !g_str_has_prefix(uri, "about:") 1704 && !g_str_has_prefix(uri, "file://") 1705 && !g_str_has_prefix(uri, "data:") 1706 && !g_str_has_prefix(uri, "blob:") 1707 && strlen(uri) > 0) { 1708 for (i = 0; i < strlen(uri); i++) { 1709 if (!g_ascii_isprint(uri[i])) { 1710 isascii = 0; 1711 break; 1712 } 1713 } 1714 if (isascii) { 1715 handleplumb(c, uri); 1716 webkit_policy_decision_ignore(d); 1717 return; 1718 } 1719 } 1720 1721 if (webkit_response_policy_decision_is_mime_type_supported(r)) { 1722 webkit_policy_decision_use(d); 1723 } else { 1724 webkit_policy_decision_ignore(d); 1725 download(c, res); 1726 } 1727 } 1728 1729 void 1730 insecurecontent(WebKitWebView *v, WebKitInsecureContentEvent e, Client *c) 1731 { 1732 c->insecure = 1; 1733 } 1734 1735 void 1736 downloadstarted(WebKitWebContext *wc, WebKitDownload *d, Client *c) 1737 { 1738 g_signal_connect(G_OBJECT(d), "notify::response", 1739 G_CALLBACK(responsereceived), c); 1740 } 1741 1742 void 1743 responsereceived(WebKitDownload *d, GParamSpec *ps, Client *c) 1744 { 1745 download(c, webkit_download_get_response(d)); 1746 webkit_download_cancel(d); 1747 } 1748 1749 void 1750 download(Client *c, WebKitURIResponse *r) 1751 { 1752 Arg a = (Arg)DOWNLOAD(webkit_uri_response_get_uri(r), geturi(c)); 1753 spawn(c, &a); 1754 } 1755 1756 void 1757 webprocessterminated(WebKitWebView *v, WebKitWebProcessTerminationReason r, 1758 Client *c) 1759 { 1760 fprintf(stderr, "web process terminated: %s\n", 1761 r == WEBKIT_WEB_PROCESS_CRASHED ? "crashed" : "no memory"); 1762 closeview(v, c); 1763 } 1764 1765 void 1766 closeview(WebKitWebView *v, Client *c) 1767 { 1768 gtk_widget_destroy(c->win); 1769 } 1770 1771 void 1772 destroywin(GtkWidget* w, Client *c) 1773 { 1774 destroyclient(c); 1775 if (!clients) 1776 gtk_main_quit(); 1777 } 1778 1779 void 1780 pasteuri(GtkClipboard *clipboard, const char *text, gpointer d) 1781 { 1782 Arg a = {.v = text }; 1783 if (text) 1784 loaduri((Client *) d, &a); 1785 } 1786 1787 void 1788 reload(Client *c, const Arg *a) 1789 { 1790 if (a->i) 1791 webkit_web_view_reload_bypass_cache(c->view); 1792 else 1793 webkit_web_view_reload(c->view); 1794 } 1795 1796 void 1797 print(Client *c, const Arg *a) 1798 { 1799 webkit_print_operation_run_dialog(webkit_print_operation_new(c->view), 1800 GTK_WINDOW(c->win)); 1801 } 1802 1803 void 1804 showcert(Client *c, const Arg *a) 1805 { 1806 GTlsCertificate *cert = c->failedcert ? c->failedcert : c->cert; 1807 GcrCertificate *gcrt; 1808 GByteArray *crt; 1809 GtkWidget *win; 1810 GcrCertificateWidget *wcert; 1811 1812 if (!cert) 1813 return; 1814 1815 g_object_get(cert, "certificate", &crt, NULL); 1816 gcrt = gcr_simple_certificate_new(crt->data, crt->len); 1817 g_byte_array_unref(crt); 1818 1819 win = gtk_window_new(GTK_WINDOW_TOPLEVEL); 1820 wcert = gcr_certificate_widget_new(gcrt); 1821 g_object_unref(gcrt); 1822 1823 gtk_container_add(GTK_CONTAINER(win), GTK_WIDGET(wcert)); 1824 gtk_widget_show_all(win); 1825 } 1826 1827 void 1828 clipboard(Client *c, const Arg *a) 1829 { 1830 if (a->i) { /* load clipboard uri */ 1831 gtk_clipboard_request_text(gtk_clipboard_get( 1832 GDK_SELECTION_PRIMARY), 1833 pasteuri, c); 1834 } else { /* copy uri */ 1835 gtk_clipboard_set_text(gtk_clipboard_get( 1836 GDK_SELECTION_PRIMARY), c->targeturi 1837 ? c->targeturi : geturi(c), -1); 1838 } 1839 } 1840 1841 void 1842 zoom(Client *c, const Arg *a) 1843 { 1844 if (a->i > 0) 1845 webkit_web_view_set_zoom_level(c->view, 1846 curconfig[ZoomLevel].val.f + 0.1); 1847 else if (a->i < 0) 1848 webkit_web_view_set_zoom_level(c->view, 1849 curconfig[ZoomLevel].val.f - 0.1); 1850 else 1851 webkit_web_view_set_zoom_level(c->view, 1.0); 1852 1853 curconfig[ZoomLevel].val.f = webkit_web_view_get_zoom_level(c->view); 1854 } 1855 1856 static void 1857 msgext(Client *c, char type, const Arg *a) 1858 { 1859 static char msg[MSGBUFSZ]; 1860 int ret; 1861 1862 if (spair[0] < 0) 1863 return; 1864 1865 if ((ret = snprintf(msg, sizeof(msg), "%c%c%c", c->pageid, type, a->i)) 1866 >= sizeof(msg)) { 1867 fprintf(stderr, "surf: message too long: %d\n", ret); 1868 return; 1869 } 1870 1871 if (send(spair[0], msg, ret, 0) != ret) 1872 fprintf(stderr, "surf: error sending: %u%c%d (%d)\n", 1873 c->pageid, type, a->i, ret); 1874 } 1875 1876 void 1877 scrollv(Client *c, const Arg *a) 1878 { 1879 msgext(c, 'v', a); 1880 } 1881 1882 void 1883 scrollh(Client *c, const Arg *a) 1884 { 1885 msgext(c, 'h', a); 1886 } 1887 1888 void 1889 navigate(Client *c, const Arg *a) 1890 { 1891 if (a->i < 0) 1892 webkit_web_view_go_back(c->view); 1893 else if (a->i > 0) 1894 webkit_web_view_go_forward(c->view); 1895 } 1896 1897 void 1898 stop(Client *c, const Arg *a) 1899 { 1900 webkit_web_view_stop_loading(c->view); 1901 } 1902 1903 void 1904 toggle(Client *c, const Arg *a) 1905 { 1906 curconfig[a->i].val.i ^= 1; 1907 setparameter(c, 1, (ParamName)a->i, &curconfig[a->i].val); 1908 } 1909 1910 void 1911 togglefullscreen(Client *c, const Arg *a) 1912 { 1913 /* toggling value is handled in winevent() */ 1914 if (c->fullscreen) 1915 gtk_window_unfullscreen(GTK_WINDOW(c->win)); 1916 else 1917 gtk_window_fullscreen(GTK_WINDOW(c->win)); 1918 } 1919 1920 void 1921 togglecookiepolicy(Client *c, const Arg *a) 1922 { 1923 ++cookiepolicy; 1924 cookiepolicy %= strlen(curconfig[CookiePolicies].val.v); 1925 1926 setparameter(c, 0, CookiePolicies, NULL); 1927 } 1928 1929 void 1930 toggleinspector(Client *c, const Arg *a) 1931 { 1932 if (webkit_web_inspector_is_attached(c->inspector)) 1933 webkit_web_inspector_close(c->inspector); 1934 else if (curconfig[Inspector].val.i) 1935 webkit_web_inspector_show(c->inspector); 1936 } 1937 1938 void 1939 find(Client *c, const Arg *a) 1940 { 1941 const char *s, *f; 1942 1943 if (a && a->i) { 1944 if (a->i > 0) 1945 webkit_find_controller_search_next(c->finder); 1946 else 1947 webkit_find_controller_search_previous(c->finder); 1948 } else { 1949 s = getatom(c, AtomFind); 1950 f = webkit_find_controller_get_search_text(c->finder); 1951 1952 if (g_strcmp0(f, s) == 0) /* reset search */ 1953 webkit_find_controller_search(c->finder, "", findopts, 1954 G_MAXUINT); 1955 1956 webkit_find_controller_search(c->finder, s, findopts, 1957 G_MAXUINT); 1958 1959 if (strcmp(s, "") == 0) 1960 webkit_find_controller_search_finish(c->finder); 1961 } 1962 } 1963 1964 void 1965 clicknavigate(Client *c, const Arg *a, WebKitHitTestResult *h) 1966 { 1967 navigate(c, a); 1968 } 1969 1970 void 1971 clicknewwindow(Client *c, const Arg *a, WebKitHitTestResult *h) 1972 { 1973 Arg arg; 1974 1975 arg.v = webkit_hit_test_result_get_link_uri(h); 1976 newwindow(c, &arg, a->i); 1977 } 1978 1979 void 1980 clickexternplayer(Client *c, const Arg *a, WebKitHitTestResult *h) 1981 { 1982 Arg arg; 1983 1984 arg = (Arg)VIDEOPLAY(webkit_hit_test_result_get_media_uri(h)); 1985 spawn(c, &arg); 1986 } 1987 1988 int 1989 main(int argc, char *argv[]) 1990 { 1991 Arg arg; 1992 Client *c; 1993 1994 memset(&arg, 0, sizeof(arg)); 1995 1996 /* command line args */ 1997 ARGBEGIN { 1998 case 'a': 1999 defconfig[CookiePolicies].val.v = EARGF(usage()); 2000 defconfig[CookiePolicies].prio = 2; 2001 break; 2002 case 'b': 2003 defconfig[ScrollBars].val.i = 0; 2004 defconfig[ScrollBars].prio = 2; 2005 break; 2006 case 'B': 2007 defconfig[ScrollBars].val.i = 1; 2008 defconfig[ScrollBars].prio = 2; 2009 break; 2010 case 'c': 2011 cookiefile = EARGF(usage()); 2012 break; 2013 case 'C': 2014 stylefile = EARGF(usage()); 2015 break; 2016 case 'd': 2017 defconfig[DiskCache].val.i = 0; 2018 defconfig[DiskCache].prio = 2; 2019 break; 2020 case 'D': 2021 defconfig[DiskCache].val.i = 1; 2022 defconfig[DiskCache].prio = 2; 2023 break; 2024 case 'e': 2025 embed = strtol(EARGF(usage()), NULL, 0); 2026 break; 2027 case 'f': 2028 defconfig[RunInFullscreen].val.i = 0; 2029 defconfig[RunInFullscreen].prio = 2; 2030 break; 2031 case 'F': 2032 defconfig[RunInFullscreen].val.i = 1; 2033 defconfig[RunInFullscreen].prio = 2; 2034 break; 2035 case 'g': 2036 defconfig[Geolocation].val.i = 0; 2037 defconfig[Geolocation].prio = 2; 2038 break; 2039 case 'G': 2040 defconfig[Geolocation].val.i = 1; 2041 defconfig[Geolocation].prio = 2; 2042 break; 2043 case 'i': 2044 defconfig[LoadImages].val.i = 0; 2045 defconfig[LoadImages].prio = 2; 2046 break; 2047 case 'I': 2048 defconfig[LoadImages].val.i = 1; 2049 defconfig[LoadImages].prio = 2; 2050 break; 2051 case 'k': 2052 defconfig[KioskMode].val.i = 0; 2053 defconfig[KioskMode].prio = 2; 2054 break; 2055 case 'K': 2056 defconfig[KioskMode].val.i = 1; 2057 defconfig[KioskMode].prio = 2; 2058 break; 2059 case 'm': 2060 defconfig[Style].val.i = 0; 2061 defconfig[Style].prio = 2; 2062 break; 2063 case 'M': 2064 defconfig[Style].val.i = 1; 2065 defconfig[Style].prio = 2; 2066 break; 2067 case 'n': 2068 defconfig[Inspector].val.i = 0; 2069 defconfig[Inspector].prio = 2; 2070 break; 2071 case 'N': 2072 defconfig[Inspector].val.i = 1; 2073 defconfig[Inspector].prio = 2; 2074 break; 2075 case 'r': 2076 scriptfile = EARGF(usage()); 2077 break; 2078 case 's': 2079 defconfig[JavaScript].val.i = 0; 2080 defconfig[JavaScript].prio = 2; 2081 break; 2082 case 'S': 2083 defconfig[JavaScript].val.i = 1; 2084 defconfig[JavaScript].prio = 2; 2085 break; 2086 case 't': 2087 defconfig[StrictTLS].val.i = 0; 2088 defconfig[StrictTLS].prio = 2; 2089 break; 2090 case 'T': 2091 defconfig[StrictTLS].val.i = 1; 2092 defconfig[StrictTLS].prio = 2; 2093 break; 2094 case 'u': 2095 fulluseragent = EARGF(usage()); 2096 break; 2097 case 'v': 2098 die("surf-"VERSION", see LICENSE for © details\n"); 2099 case 'w': 2100 showxid = 1; 2101 break; 2102 case 'x': 2103 defconfig[Certificate].val.i = 0; 2104 defconfig[Certificate].prio = 2; 2105 break; 2106 case 'X': 2107 defconfig[Certificate].val.i = 1; 2108 defconfig[Certificate].prio = 2; 2109 break; 2110 case 'z': 2111 defconfig[ZoomLevel].val.f = strtof(EARGF(usage()), NULL); 2112 defconfig[ZoomLevel].prio = 2; 2113 break; 2114 default: 2115 usage(); 2116 } ARGEND; 2117 if (argc > 0) 2118 arg.v = argv[0]; 2119 else 2120 arg.v = "about:blank"; 2121 2122 setup(); 2123 c = newclient(NULL); 2124 showview(NULL, c); 2125 2126 loaduri(c, &arg); 2127 updatetitle(c); 2128 2129 gtk_main(); 2130 cleanup(); 2131 2132 return 0; 2133 }