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