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