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