Another copy of my dotfiles. Because I don't completely trust GitHub.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2286 lines
56 KiB

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