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