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