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